summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.cmake.conf5
-rw-r--r--.qmake.conf7
-rw-r--r--CMakeLists.txt23
-rw-r--r--LICENSES/BSD-3-Clause.txt9
-rw-r--r--LICENSES/GFDL-1.3-no-invariants-only.txt (renamed from LICENSE.FDL)23
-rw-r--r--LICENSES/GPL-2.0-only.txt339
-rw-r--r--LICENSES/GPL-3.0-only.txt (renamed from LICENSE.GPL3-EXCEPT)30
-rw-r--r--LICENSES/LGPL-3.0-only.txt (renamed from LICENSE.LGPL3)0
-rw-r--r--LICENSES/LicenseRef-Qt-Commercial.txt8
-rw-r--r--LICENSES/Qt-GPL-exception-1.0.txt22
-rw-r--r--cmake/QtRepoSetup.cmake16
-rw-r--r--coin/axivion/ci_config_linux.json44
-rw-r--r--coin/module_config.yaml13
-rw-r--r--configure.cmake24
-rw-r--r--configure.json5
-rw-r--r--dependencies.yaml6
-rw-r--r--dist/changes-5.14.018
-rw-r--r--dist/changes-5.14.120
-rw-r--r--dist/changes-5.14.220
-rw-r--r--dist/changes-5.15.018
-rw-r--r--dist/changes-5.15.122
-rw-r--r--examples/CMakeLists.txt13
-rw-r--r--examples/examples.pro2
-rw-r--r--examples/scxml/CMakeLists.txt23
-rw-r--r--examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc16
-rw-r--r--examples/scxml/calculator-qml/Button.qml82
-rw-r--r--examples/scxml/calculator-qml/calculator-qml.cpp71
-rw-r--r--examples/scxml/calculator-qml/calculator-qml.pro13
-rw-r--r--examples/scxml/calculator-qml/calculator-qml.qrc6
-rw-r--r--examples/scxml/calculator-qml/doc/src/calculator-qml.qdoc77
-rw-r--r--examples/scxml/calculator-widgets/calculator-widgets.cpp66
-rw-r--r--examples/scxml/calculator-widgets/calculator-widgets.pro19
-rw-r--r--examples/scxml/calculator-widgets/doc/images/calculator.pngbin6272 -> 0 bytes
-rw-r--r--examples/scxml/calculator-widgets/doc/src/calculator.qdoc81
-rw-r--r--examples/scxml/calculator-widgets/mainwindow.cpp130
-rw-r--r--examples/scxml/calculator-widgets/mainwindow.h77
-rw-r--r--examples/scxml/calculator-widgets/mainwindow.ui177
-rw-r--r--examples/scxml/calculator/Button.qml35
-rw-r--r--examples/scxml/calculator/CMakeLists.txt52
-rw-r--r--examples/scxml/calculator/MainWindow.qml (renamed from examples/scxml/calculator-qml/calculator-qml.qml)67
-rw-r--r--examples/scxml/calculator/calculator-qml.h20
-rw-r--r--examples/scxml/calculator/calculator.cpp18
-rw-r--r--examples/scxml/calculator/calculator.pro26
-rw-r--r--examples/scxml/calculator/doc/images/calculator.png (renamed from examples/scxml/calculator-qml/doc/images/calculator-qml.png)bin6934 -> 6934 bytes
-rw-r--r--examples/scxml/calculator/doc/src/calculator.qdoc48
-rw-r--r--examples/scxml/calculator/qmldir5
-rw-r--r--examples/scxml/calculator/statemachine.scxml (renamed from examples/scxml/calculator-common/statemachine.scxml)0
-rw-r--r--examples/scxml/ftpclient/CMakeLists.txt47
-rw-r--r--examples/scxml/ftpclient/doc/src/ftpclient.qdoc59
-rw-r--r--examples/scxml/ftpclient/ftpcontrolchannel.cpp68
-rw-r--r--examples/scxml/ftpclient/ftpcontrolchannel.h60
-rw-r--r--examples/scxml/ftpclient/ftpdatachannel.cpp53
-rw-r--r--examples/scxml/ftpclient/ftpdatachannel.h62
-rw-r--r--examples/scxml/ftpclient/main.cpp60
-rw-r--r--examples/scxml/ftpclient/simpleftp.scxml51
-rw-r--r--examples/scxml/invoke-common/Button.qml82
-rw-r--r--examples/scxml/invoke-common/MainView.qml105
-rw-r--r--examples/scxml/invoke-common/SubView.qml73
-rw-r--r--examples/scxml/invoke-common/statemachine.scxml79
-rw-r--r--examples/scxml/invoke-dynamic/doc/images/invoke-dynamic.pngbin6580 -> 0 bytes
-rw-r--r--examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc67
-rw-r--r--examples/scxml/invoke-dynamic/invoke-dynamic.cpp65
-rw-r--r--examples/scxml/invoke-dynamic/invoke-dynamic.pro12
-rw-r--r--examples/scxml/invoke-dynamic/invoke-dynamic.qml60
-rw-r--r--examples/scxml/invoke-dynamic/invoke-dynamic.qrc9
-rw-r--r--examples/scxml/invoke-static/doc/src/invoke-static.qdoc79
-rw-r--r--examples/scxml/invoke-static/invoke-static.cpp69
-rw-r--r--examples/scxml/invoke-static/invoke-static.pro14
-rw-r--r--examples/scxml/invoke-static/invoke-static.qml57
-rw-r--r--examples/scxml/invoke-static/invoke-static.qrc8
-rw-r--r--examples/scxml/invoke/Button.qml37
-rw-r--r--examples/scxml/invoke/CMakeLists.txt53
-rw-r--r--examples/scxml/invoke/MainView.qml63
-rw-r--r--examples/scxml/invoke/SubView.qml31
-rw-r--r--examples/scxml/invoke/doc/images/invoke.png (renamed from examples/scxml/invoke-static/doc/images/invoke-static.png)bin7170 -> 7170 bytes
-rw-r--r--examples/scxml/invoke/doc/src/invoke.qdoc75
-rw-r--r--examples/scxml/invoke/invoke-qml.h20
-rw-r--r--examples/scxml/invoke/invoke.cpp17
-rw-r--r--examples/scxml/invoke/invoke.pro27
-rw-r--r--examples/scxml/invoke/qmldir5
-rw-r--r--examples/scxml/invoke/statemachine.scxml32
-rw-r--r--examples/scxml/mediaplayer-common/Mediaplayer.qml142
-rw-r--r--examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc17
-rw-r--r--examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc9
-rw-r--r--examples/scxml/mediaplayer-common/doc/src/mediaplayer-widgets-connecting-to-states.qdocinc16
-rw-r--r--examples/scxml/mediaplayer-common/mainwindow.cpp102
-rw-r--r--examples/scxml/mediaplayer-common/mainwindow.h83
-rw-r--r--examples/scxml/mediaplayer-common/mainwindow.ui31
-rw-r--r--examples/scxml/mediaplayer-common/mediaplayer.scxml92
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml83
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.cpp69
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro17
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.qml63
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.qrc6
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/thedatamodel.cpp64
-rw-r--r--examples/scxml/mediaplayer-qml-cppdatamodel/thedatamodel.h68
-rw-r--r--examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc62
-rw-r--r--examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.cpp66
-rw-r--r--examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro12
-rw-r--r--examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml53
-rw-r--r--examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qrc7
-rw-r--r--examples/scxml/mediaplayer-qml-static/doc/src/mediaplayer-qml-static.qdoc57
-rw-r--r--examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.cpp70
-rw-r--r--examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro14
-rw-r--r--examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.qml57
-rw-r--r--examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.qrc6
-rw-r--r--examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc65
-rw-r--r--examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp68
-rw-r--r--examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.pro21
-rw-r--r--examples/scxml/mediaplayer-widgets-dynamic/mediaplayer.qrc6
-rw-r--r--examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc59
-rw-r--r--examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp66
-rw-r--r--examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro20
-rw-r--r--examples/scxml/mediaplayer/CMakeLists.txt52
-rw-r--r--examples/scxml/mediaplayer/MainWindow.qml105
-rw-r--r--examples/scxml/mediaplayer/doc/images/mediaplayer.png (renamed from examples/scxml/mediaplayer-common/doc/images/mediaplayer.png)bin5951 -> 5951 bytes
-rw-r--r--examples/scxml/mediaplayer/doc/src/mediaplayer.qdoc (renamed from examples/scxml/mediaplayer-qml-cppdatamodel/doc/src/mediaplayer-qml-cppdatamodel.qdoc)44
-rw-r--r--examples/scxml/mediaplayer/main.cpp17
-rw-r--r--examples/scxml/mediaplayer/mediaplayer-qml.h20
-rw-r--r--examples/scxml/mediaplayer/mediaplayer.pro27
-rw-r--r--examples/scxml/mediaplayer/mediaplayer.scxml36
-rw-r--r--examples/scxml/mediaplayer/qmldir3
-rw-r--r--examples/scxml/mediaplayer/thedatamodel.cpp19
-rw-r--r--examples/scxml/mediaplayer/thedatamodel.h24
-rw-r--r--examples/scxml/pinball/doc/images/pinball-statechart-global.pngbin2925 -> 0 bytes
-rw-r--r--examples/scxml/pinball/doc/images/pinball-statechart-guicontrol.pngbin3618 -> 0 bytes
-rw-r--r--examples/scxml/pinball/doc/images/pinball-statechart-internalstate.pngbin2987 -> 0 bytes
-rw-r--r--examples/scxml/pinball/doc/images/pinball-statechart-logicalstate.pngbin7613 -> 0 bytes
-rw-r--r--examples/scxml/pinball/doc/images/pinball-statechart-modestate.pngbin3546 -> 0 bytes
-rw-r--r--examples/scxml/pinball/doc/images/pinball-statechart-onstate.pngbin7139 -> 0 bytes
-rw-r--r--examples/scxml/pinball/doc/images/pinball-statechart-workflow.pngbin4433 -> 0 bytes
-rw-r--r--examples/scxml/pinball/doc/images/pinball.pngbin44973 -> 0 bytes
-rw-r--r--examples/scxml/pinball/doc/src/pinball.qdoc502
-rw-r--r--examples/scxml/pinball/main.cpp66
-rw-r--r--examples/scxml/pinball/mainwindow.cpp133
-rw-r--r--examples/scxml/pinball/mainwindow.h78
-rw-r--r--examples/scxml/pinball/mainwindow.ui663
-rw-r--r--examples/scxml/pinball/pinball.pro19
-rw-r--r--examples/scxml/pinball/pinball.scxml353
-rw-r--r--examples/scxml/scxml.pro14
-rw-r--r--examples/scxml/sudoku/CMakeLists.txt57
-rw-r--r--examples/scxml/sudoku/doc/src/sudoku.qdoc45
-rw-r--r--examples/scxml/sudoku/main.cpp53
-rw-r--r--examples/scxml/sudoku/mainwindow.cpp109
-rw-r--r--examples/scxml/sudoku/mainwindow.h67
-rw-r--r--examples/scxml/sudoku/sudoku.js51
-rw-r--r--examples/scxml/sudoku/sudoku.scxml51
-rw-r--r--examples/scxml/trafficlight-common/Button.qml62
-rw-r--r--examples/scxml/trafficlight-common/Lights.ui.qml140
-rw-r--r--examples/scxml/trafficlight-common/TrafficLight.qml74
-rw-r--r--examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc25
-rw-r--r--examples/scxml/trafficlight-common/statemachine.scxml51
-rw-r--r--examples/scxml/trafficlight-common/trafficlight.cpp91
-rw-r--r--examples/scxml/trafficlight-common/trafficlight.h57
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/Button.qml17
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/CMakeLists.txt85
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/Lights.ui.qml80
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/MainView.qml29
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc49
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/qmldir6
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp72
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro20
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml60
-rw-r--r--examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc15
-rw-r--r--examples/scxml/trafficlight-qml-simple/CMakeLists.txt51
-rw-r--r--examples/scxml/trafficlight-qml-simple/Light.qml53
-rw-r--r--examples/scxml/trafficlight-qml-simple/MainView.qml62
-rw-r--r--examples/scxml/trafficlight-qml-simple/TrafficLight.qml110
-rw-r--r--examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc37
-rw-r--r--examples/scxml/trafficlight-qml-simple/qmldir5
-rw-r--r--examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.cpp68
-rw-r--r--examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro22
-rw-r--r--examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.qrc6
-rw-r--r--examples/scxml/trafficlight-qml-simple/trafficlight-qml.h20
-rw-r--r--examples/scxml/trafficlight-qml-static/Button.qml17
-rw-r--r--examples/scxml/trafficlight-qml-static/CMakeLists.txt87
-rw-r--r--examples/scxml/trafficlight-qml-static/Lights.ui.qml77
-rw-r--r--examples/scxml/trafficlight-qml-static/MainView.qml22
-rw-r--r--examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc37
-rw-r--r--examples/scxml/trafficlight-qml-static/qmldir6
-rw-r--r--examples/scxml/trafficlight-qml-static/trafficlight-qml-static.cpp67
-rw-r--r--examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro24
-rw-r--r--examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qml57
-rw-r--r--examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc14
-rw-r--r--examples/scxml/trafficlight-qml-static/trafficlight-qml.h20
-rw-r--r--examples/scxml/trafficlight-widgets-dynamic/CMakeLists.txt84
-rw-r--r--examples/scxml/trafficlight-widgets-dynamic/doc/src/trafficlight-widgets-dynamic.qdoc45
-rw-r--r--examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp63
-rw-r--r--examples/scxml/trafficlight-widgets-static/CMakeLists.txt84
-rw-r--r--examples/scxml/trafficlight-widgets-static/doc/src/trafficlight-widgets-static.qdoc31
-rw-r--r--examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.cpp53
-rw-r--r--examples/statemachine/CMakeLists.txt13
-rw-r--r--examples/statemachine/doc/images/pingpong-example.pngbin0 -> 7843 bytes
-rw-r--r--examples/statemachine/doc/images/trafficlight-example1.pngbin0 -> 3694 bytes
-rw-r--r--examples/statemachine/doc/images/trafficlight-example2.pngbin0 -> 7257 bytes
-rw-r--r--examples/statemachine/doc/src/moveblocks.qdoc192
-rw-r--r--examples/statemachine/doc/src/pingpong.qdoc68
-rw-r--r--examples/statemachine/doc/src/rogue.qdoc185
-rw-r--r--examples/statemachine/doc/src/trafficlight.qdoc57
-rw-r--r--examples/statemachine/moveblocks/CMakeLists.txt37
-rw-r--r--examples/statemachine/moveblocks/main.cpp267
-rw-r--r--examples/statemachine/moveblocks/moveblocks.pro7
-rw-r--r--examples/statemachine/pingpong/CMakeLists.txt39
-rw-r--r--examples/statemachine/pingpong/main.cpp104
-rw-r--r--examples/statemachine/pingpong/pingpong.pro8
-rw-r--r--examples/statemachine/rogue/CMakeLists.txt39
-rw-r--r--examples/statemachine/rogue/main.cpp17
-rw-r--r--examples/statemachine/rogue/movementtransition.h77
-rw-r--r--examples/statemachine/rogue/rogue.pro10
-rw-r--r--examples/statemachine/rogue/window.cpp201
-rw-r--r--examples/statemachine/rogue/window.h50
-rw-r--r--examples/statemachine/statemachine.pro11
-rw-r--r--examples/statemachine/trafficlight/CMakeLists.txt37
-rw-r--r--examples/statemachine/trafficlight/main.cpp152
-rw-r--r--examples/statemachine/trafficlight/trafficlight.pro7
-rw-r--r--licenseRule.json89
-rw-r--r--mkspecs/features/qscxmlc.prf2
-rw-r--r--qt_cmdline.cmake2
-rw-r--r--qtscxml.pro9
-rw-r--r--src/CMakeLists.txt11
-rw-r--r--src/imports/imports.pro3
-rw-r--r--src/imports/scxmlstatemachine/eventconnection.cpp143
-rw-r--r--src/imports/scxmlstatemachine/eventconnection_p.h96
-rw-r--r--src/imports/scxmlstatemachine/invokedservices.cpp133
-rw-r--r--src/imports/scxmlstatemachine/invokedservices_p.h92
-rw-r--r--src/imports/scxmlstatemachine/plugin.cpp88
-rw-r--r--src/imports/scxmlstatemachine/plugins.qmltypes140
-rw-r--r--src/imports/scxmlstatemachine/qmldir5
-rw-r--r--src/imports/scxmlstatemachine/scxmlstatemachine.pro23
-rw-r--r--src/imports/scxmlstatemachine/statemachineextended.cpp57
-rw-r--r--src/imports/scxmlstatemachine/statemachineextended_p.h76
-rw-r--r--src/imports/scxmlstatemachine/statemachineloader.cpp203
-rw-r--r--src/imports/scxmlstatemachine/statemachineloader_p.h102
-rw-r--r--src/plugins/CMakeLists.txt6
-rw-r--r--src/plugins/ecmascriptdatamodel/CMakeLists.txt27
-rw-r--r--src/plugins/ecmascriptdatamodel/ecmascriptdatamodelplugin.json3
-rw-r--r--src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodel.cpp (renamed from src/scxml/qscxmlecmascriptdatamodel.cpp)121
-rw-r--r--src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodel_p.h52
-rw-r--r--src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodelplugin.cpp15
-rw-r--r--src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodelplugin_p.h35
-rw-r--r--src/plugins/ecmascriptdatamodel/qscxmlecmascriptplatformproperties.cpp65
-rw-r--r--src/plugins/ecmascriptdatamodel/qscxmlecmascriptplatformproperties_p.h55
-rw-r--r--src/scxml/CMakeLists.txt57
-rw-r--r--src/scxml/Qt5ScxmlConfigExtras.cmake.in48
-rw-r--r--src/scxml/Qt5ScxmlMacros.cmake68
-rw-r--r--src/scxml/Qt6ScxmlMacros.cmake79
-rw-r--r--src/scxml/configure.cmake28
-rw-r--r--src/scxml/configure.json21
-rw-r--r--src/scxml/doc/external-resources.qdoc28
-rw-r--r--src/scxml/doc/qt6-changes.qdoc45
-rw-r--r--src/scxml/doc/qtscxml-cmake-macros.qdoc29
-rw-r--r--src/scxml/doc/qtscxml-examples.qdoc46
-rw-r--r--src/scxml/doc/qtscxml-index.qdoc60
-rw-r--r--src/scxml/doc/qtscxml-instantiating-state-machines.qdoc68
-rw-r--r--src/scxml/doc/qtscxml-module-cpp.qdoc45
-rw-r--r--src/scxml/doc/qtscxml-module-qml.qdoc36
-rw-r--r--src/scxml/doc/qtscxml-module-use.qdocinc30
-rw-r--r--src/scxml/doc/qtscxml-overview.qdoc33
-rw-r--r--src/scxml/doc/qtscxml-scxml-compliance.qdoc28
-rw-r--r--src/scxml/doc/qtscxml.qdocconf30
-rw-r--r--src/scxml/qscxmlcompiler.cpp197
-rw-r--r--src/scxml/qscxmlcompiler.h42
-rw-r--r--src/scxml/qscxmlcompiler_p.h95
-rw-r--r--src/scxml/qscxmlcppdatamodel.cpp48
-rw-r--r--src/scxml/qscxmlcppdatamodel.h40
-rw-r--r--src/scxml/qscxmlcppdatamodel_p.h40
-rw-r--r--src/scxml/qscxmldatamodel.cpp100
-rw-r--r--src/scxml/qscxmldatamodel.h51
-rw-r--r--src/scxml/qscxmldatamodel_p.h59
-rw-r--r--src/scxml/qscxmldatamodelplugin.cpp13
-rw-r--r--src/scxml/qscxmldatamodelplugin_p.h36
-rw-r--r--src/scxml/qscxmlecmascriptdatamodel.h80
-rw-r--r--src/scxml/qscxmlecmascriptplatformproperties.cpp101
-rw-r--r--src/scxml/qscxmlecmascriptplatformproperties_p.h93
-rw-r--r--src/scxml/qscxmlerror.cpp40
-rw-r--r--src/scxml/qscxmlerror.h40
-rw-r--r--src/scxml/qscxmlevent.cpp44
-rw-r--r--src/scxml/qscxmlevent.h40
-rw-r--r--src/scxml/qscxmlevent_p.h40
-rw-r--r--src/scxml/qscxmlexecutablecontent.cpp50
-rw-r--r--src/scxml/qscxmlexecutablecontent.h40
-rw-r--r--src/scxml/qscxmlexecutablecontent_p.h50
-rw-r--r--src/scxml/qscxmlglobals.h52
-rw-r--r--src/scxml/qscxmlglobals_p.h41
-rw-r--r--src/scxml/qscxmlinvokableservice.cpp68
-rw-r--r--src/scxml/qscxmlinvokableservice.h60
-rw-r--r--src/scxml/qscxmlinvokableservice_p.h56
-rw-r--r--src/scxml/qscxmlnulldatamodel.cpp42
-rw-r--r--src/scxml/qscxmlnulldatamodel.h40
-rw-r--r--src/scxml/qscxmlstatemachine.cpp342
-rw-r--r--src/scxml/qscxmlstatemachine.h200
-rw-r--r--src/scxml/qscxmlstatemachine_p.h114
-rw-r--r--src/scxml/qscxmlstatemachineinfo.cpp64
-rw-r--r--src/scxml/qscxmlstatemachineinfo_p.h59
-rw-r--r--src/scxml/qscxmltabledata.cpp110
-rw-r--r--src/scxml/qscxmltabledata.h42
-rw-r--r--src/scxml/qscxmltabledata_p.h61
-rw-r--r--src/scxml/qt_cmdline.cmake0
-rw-r--r--src/scxml/scxml.pro73
-rw-r--r--src/scxmlqml/CMakeLists.txt28
-rw-r--r--src/scxmlqml/eventconnection.cpp117
-rw-r--r--src/scxmlqml/eventconnection_p.h84
-rw-r--r--src/scxmlqml/invokedservices.cpp114
-rw-r--r--src/scxmlqml/invokedservices_p.h73
-rw-r--r--src/scxmlqml/qscxmlqmlglobals_p.h27
-rw-r--r--src/scxmlqml/statemachineextended.cpp21
-rw-r--r--src/scxmlqml/statemachineextended_p.h54
-rw-r--r--src/scxmlqml/statemachineloader.cpp198
-rw-r--r--src/scxmlqml/statemachineloader_p.h87
-rw-r--r--src/src.pro8
-rw-r--r--src/statemachine/CMakeLists.txt54
-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
-rw-r--r--src/statemachineqml/CMakeLists.txt31
-rw-r--r--src/statemachineqml/childrenprivate_p.h164
-rw-r--r--src/statemachineqml/finalstate.cpp68
-rw-r--r--src/statemachineqml/finalstate_p.h59
-rw-r--r--src/statemachineqml/qstatemachineqmlglobals_p.h27
-rw-r--r--src/statemachineqml/signaltransition.cpp346
-rw-r--r--src/statemachineqml/signaltransition_p.h101
-rw-r--r--src/statemachineqml/state.cpp221
-rw-r--r--src/statemachineqml/state_p.h63
-rw-r--r--src/statemachineqml/statemachine.cpp214
-rw-r--r--src/statemachineqml/statemachine_p.h78
-rw-r--r--src/statemachineqml/statemachineforeign_p.h61
-rw-r--r--src/statemachineqml/timeouttransition.cpp80
-rw-r--r--src/statemachineqml/timeouttransition_p.h53
-rw-r--r--sync.profile9
-rwxr-xr-xtests/3rdparty/gen-scion-tests.py26
-rw-r--r--tests/3rdparty/scion-tests/scxml-test-framework/test/w3c-ecma/test557.txt4
-rw-r--r--tests/CMakeLists.txt9
-rw-r--r--tests/auto/CMakeLists.txt22
-rw-r--r--tests/auto/auto.pro8
-rw-r--r--tests/auto/cmake/CMakeLists.txt48
-rw-r--r--tests/auto/cmake/cmake.pro6
-rw-r--r--tests/auto/cmake/test_qtscxml_module/CMakeLists.txt18
-rw-r--r--tests/auto/cmake/test_qtscxml_module/main.cpp40
-rw-r--r--tests/auto/compiled/CMakeLists.txt53
-rw-r--r--tests/auto/compiled/compiled.pro25
-rw-r--r--tests/auto/compiled/connection.scxml29
-rw-r--r--tests/auto/compiled/eventnames1.scxml29
-rw-r--r--tests/auto/compiled/eventnames2.scxml29
-rw-r--r--tests/auto/compiled/historyState.scxml57
-rw-r--r--tests/auto/compiled/ids1.scxml29
-rw-r--r--tests/auto/compiled/tst_compiled.cpp63
-rw-r--r--tests/auto/compiled/tst_compiled.qrc7
-rw-r--r--tests/auto/dynamicmetaobject/CMakeLists.txt33
-rw-r--r--tests/auto/dynamicmetaobject/dynamicmetaobject.pro13
-rw-r--r--tests/auto/dynamicmetaobject/mediaplayer.scxml51
-rw-r--r--tests/auto/dynamicmetaobject/test1.scxml29
-rw-r--r--tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp31
-rw-r--r--tests/auto/dynamicmetaobject/tst_dynamicmetaobject.qrc6
-rw-r--r--tests/auto/parser/CMakeLists.txt113
-rw-r--r--tests/auto/parser/data/badInitial.scxml.errors1
-rw-r--r--tests/auto/parser/data/commentInScript.scxml.errors1
-rw-r--r--tests/auto/parser/data/emptyScript.scxml12
-rw-r--r--tests/auto/parser/data/emptyScript.scxml.errors1
-rw-r--r--tests/auto/parser/data/eventnames.scxml29
-rw-r--r--tests/auto/parser/data/eventnames.scxml.errors10
-rw-r--r--tests/auto/parser/data/ids1.scxml29
-rw-r--r--tests/auto/parser/data/ids2.scxml29
-rw-r--r--tests/auto/parser/data/ids2.scxml.errors2
-rw-r--r--tests/auto/parser/data/namespaces1.scxml29
-rw-r--r--tests/auto/parser/data/scxml1.scxml29
-rw-r--r--tests/auto/parser/data/scxml1.scxml.errors4
-rw-r--r--tests/auto/parser/data/scxml2.scxml29
-rw-r--r--tests/auto/parser/data/scxml2.scxml.errors2
-rw-r--r--tests/auto/parser/data/test1.scxml29
-rw-r--r--tests/auto/parser/data/test1.scxml.errors2
-rw-r--r--tests/auto/parser/parser.pro13
-rw-r--r--tests/auto/parser/tst_parser.cpp39
-rw-r--r--tests/auto/parser/tst_parser.qrc84
-rw-r--r--tests/auto/qml/CMakeLists.txt10
-rw-r--r--tests/auto/qml/qqmlstatemachine/CMakeLists.txt42
-rw-r--r--tests/auto/qml/qqmlstatemachine/data/cppsignal.qml40
-rw-r--r--tests/auto/qml/qqmlstatemachine/data/signaltransition.qml30
-rw-r--r--tests/auto/qml/qqmlstatemachine/data/signaltransitionhelper.qml10
-rw-r--r--tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp168
-rw-r--r--tests/auto/qml/qqmlstatemachinemetatype/CMakeLists.txt37
-rw-r--r--tests/auto/qml/qqmlstatemachinemetatype/data/unregisterAttachedProperties.qml7
-rw-r--r--tests/auto/qml/qqmlstatemachinemetatype/tst_qqmlstatemachinemetatype.cpp38
-rw-r--r--tests/auto/qml/scxmlqmlcpp/CMakeLists.txt38
-rw-r--r--tests/auto/qml/scxmlqmlcpp/data/brokenstatemachine.scxml14
-rw-r--r--tests/auto/qml/scxmlqmlcpp/data/smlHelper.qml6
-rw-r--r--tests/auto/qml/scxmlqmlcpp/data/stateMachineLoader.qml8
-rw-r--r--tests/auto/qml/scxmlqmlcpp/data/statemachine.scxml64
-rw-r--r--tests/auto/qml/scxmlqmlcpp/data/submachineA.scxml4
-rw-r--r--tests/auto/qml/scxmlqmlcpp/data/submachineB.scxml4
-rw-r--r--tests/auto/qml/scxmlqmlcpp/data/topmachine.scxml19
-rw-r--r--tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp191
-rw-r--r--tests/auto/qmltest/CMakeLists.txt9
-rw-r--r--tests/auto/qmltest/scxml/CMakeLists.txt30
-rw-r--r--tests/auto/qmltest/scxml/statemachine.scxml20
-rw-r--r--tests/auto/qmltest/scxml/tst_dynamic.qml27
-rw-r--r--tests/auto/qmltest/scxml/tst_scxmlqml.cpp5
-rw-r--r--tests/auto/qmltest/statemachine/CMakeLists.txt18
-rw-r--r--tests/auto/qmltest/statemachine/tst_anonymousstate.qml16
-rw-r--r--tests/auto/qmltest/statemachine/tst_enumguard.qml60
-rw-r--r--tests/auto/qmltest/statemachine/tst_guardcondition.qml71
-rw-r--r--tests/auto/qmltest/statemachine/tst_historystate.qml64
-rw-r--r--tests/auto/qmltest/statemachine/tst_initialstate.qml27
-rw-r--r--tests/auto/qmltest/statemachine/tst_nestedinitialstates.qml35
-rw-r--r--tests/auto/qmltest/statemachine/tst_nestedstatemachine.qml41
-rw-r--r--tests/auto/qmltest/statemachine/tst_parallelmachine.qml56
-rw-r--r--tests/auto/qmltest/statemachine/tst_signaltransition.qml46
-rw-r--r--tests/auto/qmltest/statemachine/tst_statemachineqml.cpp5
-rw-r--r--tests/auto/qmltest/statemachine/tst_trafficlight.qml119
-rw-r--r--tests/auto/qmltest/statemachine/tst_triggeredArguments1.qml49
-rw-r--r--tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml49
-rw-r--r--tests/auto/qstatemachine/CMakeLists.txt8
-rw-r--r--tests/auto/qstatemachine/qstate/CMakeLists.txt14
-rw-r--r--tests/auto/qstatemachine/qstate/tst_qstate.cpp350
-rw-r--r--tests/auto/qstatemachine/qstatemachine/CMakeLists.txt24
-rw-r--r--tests/auto/qstatemachine/qstatemachine/tst_qstatemachine.cpp7054
-rw-r--r--tests/auto/scion/CMakeLists.txt149
-rw-r--r--tests/auto/scion/scion.pro103
-rw-r--r--tests/auto/scion/tst_scion.cpp118
-rw-r--r--tests/auto/scxmlcoutput/CMakeLists.txt9
-rw-r--r--tests/auto/scxmlcoutput/arguments/CMakeLists.txt19
-rw-r--r--tests/auto/scxmlcoutput/arguments/tst_scxmlcoutput.cpp25
-rw-r--r--tests/auto/scxmlcoutput/default/CMakeLists.txt19
-rw-r--r--tests/auto/scxmlcoutput/default/tst_scxmlcoutput.cpp25
-rw-r--r--tests/auto/scxmlcoutput/namespace/CMakeLists.txt19
-rw-r--r--tests/auto/scxmlcoutput/namespace/tst_scxmlcoutput.cpp25
-rw-r--r--tests/auto/scxmlcoutput/path/CMakeLists.txt24
-rw-r--r--tests/auto/scxmlcoutput/path/tst_scxmlcoutput.cpp25
-rw-r--r--tests/auto/scxmlcoutput/shared/ids1.scxml17
-rw-r--r--tests/auto/shared/bindableqmlutils.h116
-rw-r--r--tests/auto/shared/util.cpp78
-rw-r--r--tests/auto/shared/util.h75
-rw-r--r--tests/auto/statemachine/CMakeLists.txt53
-rw-r--r--tests/auto/statemachine/eventoccurred.scxml29
-rw-r--r--tests/auto/statemachine/ids1.scxml29
-rw-r--r--tests/auto/statemachine/invoke.scxml51
-rw-r--r--tests/auto/statemachine/stateDotDoneEvent.scxml29
-rw-r--r--tests/auto/statemachine/statemachine.pro13
-rw-r--r--tests/auto/statemachine/statenames.scxml29
-rw-r--r--tests/auto/statemachine/statenamesnested.scxml29
-rw-r--r--tests/auto/statemachine/submachineA.scxml4
-rw-r--r--tests/auto/statemachine/submachineB.scxml4
-rw-r--r--tests/auto/statemachine/topmachine.scxml19
-rw-r--r--tests/auto/statemachine/tst_statemachine.cpp211
-rw-r--r--tests/auto/statemachine/tst_statemachine.qrc13
-rw-r--r--tests/auto/statemachineinfo/CMakeLists.txt32
-rw-r--r--tests/auto/statemachineinfo/statemachine.scxml29
-rw-r--r--tests/auto/statemachineinfo/statemachineinfo.pro13
-rw-r--r--tests/auto/statemachineinfo/tst_statemachineinfo.cpp67
-rw-r--r--tests/auto/statemachineinfo/tst_statemachineinfo.qrc5
-rw-r--r--tests/manual/cppgen/CMakeLists.txt21
-rw-r--r--tests/manual/cppgen/cppgen.pro18
-rw-r--r--tests/manual/cppgen/tst_cppgen.cpp29
-rw-r--r--tests/manual/testCpp/CMakeLists.txt24
-rw-r--r--tests/manual/testCpp/genTestSxcml.py25
-rw-r--r--tests/manual/testCpp/testCpp.pro19
-rw-r--r--tests/manual/testCpp/testcpp.cpp29
-rw-r--r--tests/tests.pro4
-rw-r--r--tools/CMakeLists.txt7
-rw-r--r--tools/qscxmlc/CMakeLists.txt64
-rw-r--r--tools/qscxmlc/data.t12
-rw-r--r--tools/qscxmlc/doc/qscxmlc.qdoc90
-rw-r--r--tools/qscxmlc/generator.cpp1345
-rw-r--r--tools/qscxmlc/generator.h65
-rw-r--r--tools/qscxmlc/main.cpp29
-rw-r--r--tools/qscxmlc/moc.cpp2215
-rw-r--r--tools/qscxmlc/moc.h306
-rw-r--r--tools/qscxmlc/moc_patches/generator.cpp.patch286
-rw-r--r--tools/qscxmlc/moc_patches/generator.h.patch51
-rw-r--r--tools/qscxmlc/moc_patches/moc.cpp.patch29
-rw-r--r--tools/qscxmlc/moc_patches/moc.h.patch94
-rw-r--r--tools/qscxmlc/moc_patches/outputrevision.h.patch16
-rwxr-xr-xtools/qscxmlc/moc_patches/update_moc.sh94
-rw-r--r--tools/qscxmlc/outputrevision.h37
-rw-r--r--tools/qscxmlc/qscxmlc.cpp47
-rw-r--r--tools/qscxmlc/qscxmlc.h41
-rw-r--r--tools/qscxmlc/qscxmlc.pri32
-rw-r--r--tools/qscxmlc/qscxmlc.pro16
-rw-r--r--tools/qscxmlc/scxmlcppdumper.cpp94
-rw-r--r--tools/qscxmlc/scxmlcppdumper.h32
-rw-r--r--tools/qscxmlc/templates.qrc7
-rw-r--r--tools/qscxmlc/utils.h40
-rw-r--r--tools/tools.pro2
560 files changed, 32386 insertions, 13157 deletions
diff --git a/.cmake.conf b/.cmake.conf
new file mode 100644
index 0000000..dc1d7a9
--- /dev/null
+++ b/.cmake.conf
@@ -0,0 +1,5 @@
+set(QT_REPO_MODULE_VERSION "6.8.0")
+set(QT_REPO_MODULE_PRERELEASE_VERSION_SEGMENT "alpha1")
+set(QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_AS_CONST=1")
+list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_FOREACH=1")
+list(APPEND QT_EXTRA_INTERNAL_TARGET_DEFINES "QT_NO_CONTEXTLESS_CONNECT=1")
diff --git a/.qmake.conf b/.qmake.conf
deleted file mode 100644
index 45dbe7a..0000000
--- a/.qmake.conf
+++ /dev/null
@@ -1,7 +0,0 @@
-load(qt_build_config)
-CONFIG += qt_example_installs
-CONFIG += warning_clean
-
-DEFINES += QT_NO_FOREACH QT_NO_JAVA_STYLE_ITERATORS QT_NO_LINKED_LIST
-
-MODULE_VERSION = 6.0.0
diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644
index 0000000..9d16886
--- /dev/null
+++ b/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+
+include(.cmake.conf)
+project(QtScxml
+ VERSION "${QT_REPO_MODULE_VERSION}"
+ DESCRIPTION "Qt Scxml and StateMachine libraries"
+ HOMEPAGE_URL "https://qt.io/"
+ LANGUAGES CXX C
+)
+
+find_package(Qt6 ${PROJECT_VERSION} CONFIG REQUIRED COMPONENTS BuildInternals Core)
+find_package(Qt6 ${PROJECT_VERSION} CONFIG OPTIONAL_COMPONENTS Gui Qml Widgets OpenGL OpenGLWidgets Network QuickTest)
+qt_internal_project_setup()
+
+qt_internal_define_repo_target_set(qtscxml)
+qt_internal_define_repo_target_set(qtscxmlqml DEPENDS Scxml)
+qt_internal_define_repo_target_set(qtstatemachine)
+qt_internal_define_repo_target_set(qtstatemachineqml DEPENDS StateMachine)
+qt_internal_prepare_single_repo_target_set_build()
+qt_build_repo()
diff --git a/LICENSES/BSD-3-Clause.txt b/LICENSES/BSD-3-Clause.txt
new file mode 100644
index 0000000..b91bbd8
--- /dev/null
+++ b/LICENSES/BSD-3-Clause.txt
@@ -0,0 +1,9 @@
+Copyright (c) <year> <owner>.
+
+Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
+ 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/LICENSE.FDL b/LICENSES/GFDL-1.3-no-invariants-only.txt
index 938bb8d..857214d 100644
--- a/LICENSE.FDL
+++ b/LICENSES/GFDL-1.3-no-invariants-only.txt
@@ -1,9 +1,10 @@
+
GNU Free Documentation License
Version 1.3, 3 November 2008
Copyright (C) 2000, 2001, 2002, 2007, 2008 Free Software Foundation, Inc.
- <http://fsf.org/>
+ <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
@@ -376,7 +377,7 @@ The Free Software Foundation may publish new, revised versions of the
GNU Free Documentation License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in
detail to address new problems or concerns. See
-http://www.gnu.org/copyleft/.
+https://www.gnu.org/licenses/.
Each version of the License is given a distinguishing version number.
If the Document specifies that a particular numbered version of this
@@ -400,19 +401,19 @@ public wiki that anybody can edit is an example of such a server. A
"Massive Multiauthor Collaboration" (or "MMC") contained in the site
means any set of copyrightable works thus published on the MMC site.
-"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
-license published by Creative Commons Corporation, a not-for-profit
-corporation with a principal place of business in San Francisco,
-California, as well as future copyleft versions of that license
+"CC-BY-SA" means the Creative Commons Attribution-Share Alike 3.0
+license published by Creative Commons Corporation, a not-for-profit
+corporation with a principal place of business in San Francisco,
+California, as well as future copyleft versions of that license
published by that same organization.
-"Incorporate" means to publish or republish a Document, in whole or in
+"Incorporate" means to publish or republish a Document, in whole or in
part, as part of another Document.
-An MMC is "eligible for relicensing" if it is licensed under this
-License, and if all works that were first published under this License
-somewhere other than this MMC, and subsequently incorporated in whole or
-in part into the MMC, (1) had no cover texts or invariant sections, and
+An MMC is "eligible for relicensing" if it is licensed under this
+License, and if all works that were first published under this License
+somewhere other than this MMC, and subsequently incorporated in whole or
+in part into the MMC, (1) had no cover texts or invariant sections, and
(2) were thus incorporated prior to November 1, 2008.
The operator of an MMC Site may republish an MMC contained in the site
diff --git a/LICENSES/GPL-2.0-only.txt b/LICENSES/GPL-2.0-only.txt
new file mode 100644
index 0000000..d159169
--- /dev/null
+++ b/LICENSES/GPL-2.0-only.txt
@@ -0,0 +1,339 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along
+ with this program; if not, write to the Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/LICENSE.GPL3-EXCEPT b/LICENSES/GPL-3.0-only.txt
index b1cb1be..94a9ed0 100644
--- a/LICENSE.GPL3-EXCEPT
+++ b/LICENSES/GPL-3.0-only.txt
@@ -1,33 +1,3 @@
-This is the GNU General Public License version 3, annotated with The
-Qt Company GPL Exception 1.0:
-
--------------------------------------------------------------------------
-
-The Qt Company GPL Exception 1.0
-
-Exception 1:
-
-As a special exception you may create a larger work which contains the
-output of this application and distribute that work under terms of your
-choice, so long as the work is not otherwise derived from or based on
-this application and so long as the work does not in itself generate
-output that contains the output from this application in its original
-or modified form.
-
-Exception 2:
-
-As a special exception, you have permission to combine this application
-with Plugins licensed under the terms of your choice, to produce an
-executable, and to copy and distribute the resulting executable under
-the terms of your choice. However, the executable must be accompanied
-by a prominent notice offering all users of the executable the entire
-source code to this application, excluding the source code of the
-independent modules, but including any changes you have made to this
-application, under the terms of this license.
-
-
--------------------------------------------------------------------------
-
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
diff --git a/LICENSE.LGPL3 b/LICENSES/LGPL-3.0-only.txt
index 65c5ca8..65c5ca8 100644
--- a/LICENSE.LGPL3
+++ b/LICENSES/LGPL-3.0-only.txt
diff --git a/LICENSES/LicenseRef-Qt-Commercial.txt b/LICENSES/LicenseRef-Qt-Commercial.txt
new file mode 100644
index 0000000..825b1f3
--- /dev/null
+++ b/LICENSES/LicenseRef-Qt-Commercial.txt
@@ -0,0 +1,8 @@
+Licensees holding valid commercial Qt licenses may use this software in
+accordance with the the terms contained in a written agreement between
+you and The Qt Company. Alternatively, the terms and conditions that were
+accepted by the licensee when buying and/or downloading the
+software do apply.
+
+For the latest licensing terms and conditions, see https://www.qt.io/terms-conditions.
+For further information use the contact form at https://www.qt.io/contact-us.
diff --git a/LICENSES/Qt-GPL-exception-1.0.txt b/LICENSES/Qt-GPL-exception-1.0.txt
new file mode 100644
index 0000000..d0322bf
--- /dev/null
+++ b/LICENSES/Qt-GPL-exception-1.0.txt
@@ -0,0 +1,22 @@
+The Qt Company GPL Exception 1.0
+
+Exception 1:
+
+As a special exception you may create a larger work which contains the
+output of this application and distribute that work under terms of your
+choice, so long as the work is not otherwise derived from or based on
+this application and so long as the work does not in itself generate
+output that contains the output from this application in its original
+or modified form.
+
+Exception 2:
+
+As a special exception, you have permission to combine this application
+with Plugins licensed under the terms of your choice, to produce an
+executable, and to copy and distribute the resulting executable under
+the terms of your choice. However, the executable must be accompanied
+by a prominent notice offering all users of the executable the entire
+source code to this application, excluding the source code of the
+independent modules, but including any changes you have made to this
+application, under the terms of this license.
+
diff --git a/cmake/QtRepoSetup.cmake b/cmake/QtRepoSetup.cmake
new file mode 100644
index 0000000..c093733
--- /dev/null
+++ b/cmake/QtRepoSetup.cmake
@@ -0,0 +1,16 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+function(add_qt_statecharts target)
+ # Don't try to add statecharts when cross compiling, and the target is actually a host target
+ # (like a tool).
+ qt_is_imported_target("${target}" is_imported)
+ if(is_imported)
+ return()
+ endif()
+
+ cmake_parse_arguments(PARSE_ARGV 1 arg "" "" "FILES")
+ _qt_internal_validate_all_args_are_parsed(arg)
+
+ qt6_add_statecharts(${target} ${arg_FILES})
+endfunction()
diff --git a/coin/axivion/ci_config_linux.json b/coin/axivion/ci_config_linux.json
new file mode 100644
index 0000000..f806cff
--- /dev/null
+++ b/coin/axivion/ci_config_linux.json
@@ -0,0 +1,44 @@
+{
+ "Project": {
+ "BuildSystemIntegration": {
+ "child_order": [
+ "GCCSetup",
+ "CMake",
+ "LinkLibraries"
+ ]
+ },
+ "CMake": {
+ "_active": true,
+ "_copy_from": "CMakeIntegration",
+ "build_environment": {},
+ "build_options": "-j4",
+ "generate_options": "--fresh",
+ "generator": "Ninja"
+ },
+ "GCCSetup": {
+ "_active": true,
+ "_copy_from": "Command",
+ "build_command": "gccsetup --cc gcc --cxx g++ --config ../../../axivion/"
+ },
+ "LinkLibraries": {
+ "_active": true,
+ "_copy_from": "AxivionLinker",
+ "input_files": [
+ "build/lib/lib*.so*.ir",
+ "build/qml/*/lib*.so*.ir"
+ ],
+ "ir": "build/$(env:TESTED_MODULE_COIN).ir",
+ "plugin_files": [
+ "build/plugins/*/lib*.so*.ir"
+ ]
+ }
+ },
+ "_Format": "1.0",
+ "_Version": "7.6.2",
+ "_VersionNum": [
+ 7,
+ 6,
+ 2,
+ 12725
+ ]
+}
diff --git a/coin/module_config.yaml b/coin/module_config.yaml
new file mode 100644
index 0000000..aaf3323
--- /dev/null
+++ b/coin/module_config.yaml
@@ -0,0 +1,13 @@
+version: 2
+accept_configuration:
+ condition: property
+ property: features
+ not_contains_value: Disable
+
+instructions:
+ Build:
+ - !include "{{qt/qtbase}}/coin_module_build_template_v2.yaml"
+
+ Test:
+ - !include "{{qt/qtbase}}/coin_module_test_template_v3.yaml"
+ - !include "{{qt/qtbase}}/coin_module_test_docs.yaml"
diff --git a/configure.cmake b/configure.cmake
new file mode 100644
index 0000000..68f54ce
--- /dev/null
+++ b/configure.cmake
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+
+#### Inputs
+
+
+
+#### Libraries
+
+
+
+#### Tests
+
+
+
+#### Features
+
+
+qt_extra_definition("QT_VERSION_STR" "\"${PROJECT_VERSION}\"" PUBLIC)
+qt_extra_definition("QT_VERSION_MAJOR" ${PROJECT_VERSION_MAJOR} PUBLIC)
+qt_extra_definition("QT_VERSION_MINOR" ${PROJECT_VERSION_MINOR} PUBLIC)
+qt_extra_definition("QT_VERSION_PATCH" ${PROJECT_VERSION_PATCH} PUBLIC)
diff --git a/configure.json b/configure.json
deleted file mode 100644
index 9f9d32f..0000000
--- a/configure.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "subconfigs": [
- "src/scxml"
- ]
-}
diff --git a/dependencies.yaml b/dependencies.yaml
index 6f23caf..dc08c4a 100644
--- a/dependencies.yaml
+++ b/dependencies.yaml
@@ -1,7 +1,7 @@
dependencies:
../qtbase:
- ref: 35b5100302c9ff28a7ae286c6bca8de37683f270
+ ref: 0cd5eb895cc96126a495eb3d2d258be47eed193a
required: true
../qtdeclarative:
- ref: 02f9934f4dbb99932e3879d5a6a2ecfa96958f71
- required: true
+ ref: c63bb2bad5b4e741ed8a1e16d8f1f916c9baf61d
+ required: false
diff --git a/dist/changes-5.14.0 b/dist/changes-5.14.0
new file mode 100644
index 0000000..29a2c83
--- /dev/null
+++ b/dist/changes-5.14.0
@@ -0,0 +1,18 @@
+Qt 5.14 introduces many new features and improvements as well as bugfixes
+over the 5.13.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.14 series is binary compatible with the 5.13.x series.
+Applications compiled for 5.13 will continue to run with 5.14.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
diff --git a/dist/changes-5.14.1 b/dist/changes-5.14.1
new file mode 100644
index 0000000..d008638
--- /dev/null
+++ b/dist/changes-5.14.1
@@ -0,0 +1,20 @@
+Qt 5.14.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.14.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.14 series is binary compatible with the 5.13.x series.
+Applications compiled for 5.13 will continue to run with 5.14.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
diff --git a/dist/changes-5.14.2 b/dist/changes-5.14.2
new file mode 100644
index 0000000..68a0051
--- /dev/null
+++ b/dist/changes-5.14.2
@@ -0,0 +1,20 @@
+Qt 5.14.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.14.0 through 5.14.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.14 series is binary compatible with the 5.13.x series.
+Applications compiled for 5.13 will continue to run with 5.14.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
diff --git a/dist/changes-5.15.0 b/dist/changes-5.15.0
new file mode 100644
index 0000000..40076ce
--- /dev/null
+++ b/dist/changes-5.15.0
@@ -0,0 +1,18 @@
+Qt 5.15 introduces many new features and improvements as well as bugfixes
+over the 5.14.x series. For more details, refer to the online documentation
+included in this distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.15 series is binary compatible with the 5.14.x series.
+Applications compiled for 5.14 will continue to run with 5.15.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - This release contains only minor code improvements.
diff --git a/dist/changes-5.15.1 b/dist/changes-5.15.1
new file mode 100644
index 0000000..6c39df3
--- /dev/null
+++ b/dist/changes-5.15.1
@@ -0,0 +1,22 @@
+Qt 5.15.1 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.15.0.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.15 series is binary compatible with the 5.14.x series.
+Applications compiled for 5.14 will continue to run with 5.15.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+ - Fixed error checking when attempting to connect nonexistent states
+ - Fixed flaky autotests with a delayed event in scion.
+
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000..f765b17
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+qt_examples_build_begin(EXTERNAL_BUILD)
+
+if(TARGET Qt::Scxml)
+ add_subdirectory(scxml)
+endif()
+if(TARGET Qt::StateMachine)
+ add_subdirectory(statemachine)
+endif()
+
+qt_examples_build_end()
diff --git a/examples/examples.pro b/examples/examples.pro
index 2d6d823..c7fbeb0 100644
--- a/examples/examples.pro
+++ b/examples/examples.pro
@@ -1,4 +1,4 @@
TEMPLATE = subdirs
qtHaveModule(scxml): SUBDIRS += scxml
-
+qtHaveModule(statemachine): SUBDIRS += statemachine
diff --git a/examples/scxml/CMakeLists.txt b/examples/scxml/CMakeLists.txt
new file mode 100644
index 0000000..d9e2ef2
--- /dev/null
+++ b/examples/scxml/CMakeLists.txt
@@ -0,0 +1,23 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if (TARGET Qt::Network AND QT_FEATURE_scxml_ecmascriptdatamodel AND NOT ANDROID)
+ qt_internal_add_example(ftpclient)
+endif()
+
+if(TARGET Qt::Widgets)
+ qt_internal_add_example(trafficlight-widgets-static)
+ qt_internal_add_example(trafficlight-widgets-dynamic)
+ if(QT_FEATURE_scxml_ecmascriptdatamodel)
+ qt_internal_add_example(sudoku)
+ endif()
+endif()
+
+if(TARGET Qt::Qml AND TARGET Qt::Gui)
+ qt_internal_add_example(calculator)
+ qt_internal_add_example(trafficlight-qml-static)
+ qt_internal_add_example(trafficlight-qml-dynamic)
+ qt_internal_add_example(trafficlight-qml-simple)
+ qt_internal_add_example(mediaplayer)
+ qt_internal_add_example(invoke)
+endif()
diff --git a/examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc b/examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc
deleted file mode 100644
index 3e5982f..0000000
--- a/examples/scxml/calculator-common/doc/src/calculator-compiling.qdocinc
+++ /dev/null
@@ -1,16 +0,0 @@
- \section1 Compiling the State Machine
-
- We link against the Qt SCXML module by adding the following line to the
- \e .pro file:
-
- \quotefromfile calculator-widgets/calculator-widgets.pro
- \printuntil scxml
-
- We then specify the state machine to compile:
-
- \skipto STATECHARTS
- \printuntil statemachine.scxml
-
- The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
- \e statemachine.h and \e statemachine.cpp, and to add them to the \c HEADERS
- and \c SOURCES variables for compilation.
diff --git a/examples/scxml/calculator-qml/Button.qml b/examples/scxml/calculator-qml/Button.qml
deleted file mode 100644
index 84f4d0e..0000000
--- a/examples/scxml/calculator-qml/Button.qml
+++ /dev/null
@@ -1,82 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.5
-
-Rectangle {
- id: button
- signal clicked
- property alias text: text.text
- border.width: 1
- border.color: "white"
- property real textHeight: height - 2
- property real fontHeight: 0.3
- property bool pressed: mouse.pressed
- property real implicitMargin: (width - text.implicitWidth) / 2
-
- Text {
- id: text
- anchors.left: parent.left
- anchors.right: parent.right
- anchors.top: parent.top
- height: parent.textHeight
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- font.pixelSize: height * fontHeight
- color: "#1b1c1d"
- font.family: "Open Sans Regular"
- }
-
- MouseArea {
- id: mouse
- anchors.fill: parent
- onClicked: button.clicked()
- }
-}
diff --git a/examples/scxml/calculator-qml/calculator-qml.cpp b/examples/scxml/calculator-qml/calculator-qml.cpp
deleted file mode 100644
index 85b904e..0000000
--- a/examples/scxml/calculator-qml/calculator-qml.cpp
+++ /dev/null
@@ -1,71 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-#include "statemachine.h"
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
-
- qmlRegisterType<CalculatorStateMachine>("CalculatorStateMachine", 1, 0,
- "CalculatorStateMachine");
-
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/calculator-qml.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
-
diff --git a/examples/scxml/calculator-qml/calculator-qml.pro b/examples/scxml/calculator-qml/calculator-qml.pro
deleted file mode 100644
index c40242f..0000000
--- a/examples/scxml/calculator-qml/calculator-qml.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-QT += qml scxml
-
-CONFIG += c++11
-
-SOURCES += calculator-qml.cpp
-
-RESOURCES += calculator-qml.qrc
-
-STATECHARTS = ../calculator-common/statemachine.scxml
-
-# install
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/calculator-qml
-INSTALLS += target
diff --git a/examples/scxml/calculator-qml/calculator-qml.qrc b/examples/scxml/calculator-qml/calculator-qml.qrc
deleted file mode 100644
index 55bc81c..0000000
--- a/examples/scxml/calculator-qml/calculator-qml.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>calculator-qml.qml</file>
- <file>Button.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/calculator-qml/doc/src/calculator-qml.qdoc b/examples/scxml/calculator-qml/doc/src/calculator-qml.qdoc
deleted file mode 100644
index e58fcfd..0000000
--- a/examples/scxml/calculator-qml/doc/src/calculator-qml.qdoc
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example calculator-qml
- \title Qt SCXML Calculator QML Example
- \ingroup examples-qtscxml
-
- \image calculator-qml.png
-
- \brief A Qt Quick application that implements the Calculator example
- presented in the SCXML Specification.
-
- \e {Calculator} uses Qt SCXML to implement the
- \l{SCXML Specification - Calculator Example}{Calculator Example}
- presented in the SCXML Specification.
-
- The state machine is specified in the \e statemachine.scxml file and
- compiled into the \c CalculatorStateMachine class. The user interface is
- created using Qt Quick.
-
- \include examples-run.qdocinc
-
- \include calculator-compiling.qdocinc
-
- \section1 Instantiating the State Machine
-
- We make the generated \c CalculatorStateMachine class available to QML by
- registering it as a QML type in the \e calculator-qml.cpp file:
-
- \quotefromfile calculator-qml/calculator-qml.cpp
- \skipto statemachine.h
- \printuntil }
-
- To use the CalculatorStateMachine type in a QML file, we import it:
-
- \quotefromfile calculator-qml/calculator-qml.qml
- \skipto CalculatorStateMachine
- \printline 1.0
-
- We instantiate a CalculatorStateMachine and listen to the \c updateDisplay
- event. When it occurs, we change the text on the calculator display:
-
- \skipto CalculatorStateMachine {
- \printuntil /^ {4}\}/
-
- When users press the calculator buttons, the buttons submit events to the
- state machine:
-
- \skipto DIGIT.
- \skipto Button
- \printuntil }
-*/
diff --git a/examples/scxml/calculator-widgets/calculator-widgets.cpp b/examples/scxml/calculator-widgets/calculator-widgets.cpp
deleted file mode 100644
index b127427..0000000
--- a/examples/scxml/calculator-widgets/calculator-widgets.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "statemachine.h"
-#include "mainwindow.h"
-
-#include <QApplication>
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
-
- CalculatorStateMachine machine;
- MainWindow mainWindow(&machine);
-
- machine.start();
- mainWindow.show();
- return app.exec();
-}
diff --git a/examples/scxml/calculator-widgets/calculator-widgets.pro b/examples/scxml/calculator-widgets/calculator-widgets.pro
deleted file mode 100644
index 238980b..0000000
--- a/examples/scxml/calculator-widgets/calculator-widgets.pro
+++ /dev/null
@@ -1,19 +0,0 @@
-QT += widgets scxml
-
-CONFIG += c++11
-
-STATECHARTS = ../calculator-common/statemachine.scxml
-
-SOURCES += \
- calculator-widgets.cpp \
- mainwindow.cpp
-
-HEADERS += \
- mainwindow.h
-
-FORMS += \
- mainwindow.ui
-
-# install
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/calculator-widgets
-INSTALLS += target
diff --git a/examples/scxml/calculator-widgets/doc/images/calculator.png b/examples/scxml/calculator-widgets/doc/images/calculator.png
deleted file mode 100644
index 0834d0a..0000000
--- a/examples/scxml/calculator-widgets/doc/images/calculator.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/calculator-widgets/doc/src/calculator.qdoc b/examples/scxml/calculator-widgets/doc/src/calculator.qdoc
deleted file mode 100644
index 9463b53..0000000
--- a/examples/scxml/calculator-widgets/doc/src/calculator.qdoc
+++ /dev/null
@@ -1,81 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example calculator-widgets
- \title Qt SCXML Calculator Example
- \ingroup examples-qtscxml
-
- \image calculator.png
-
- \brief A widget-based application that implements the Calculator example
- presented in the SCXML Specification.
-
- \e {Calculator} uses Qt SCXML to implement the
- \l{SCXML Specification - Calculator Example}{Calculator Example}
- presented in the SCXML Specification.
-
- The state machine is specified in the \e statemachine.scxml file and
- compiled into the \c CalculatorStateMachine class. The user interface is
- created using Qt Widgets.
-
- \include examples-run.qdocinc
-
- \include calculator-compiling.qdocinc
-
- \section1 Instantiating the State Machine
-
- We instantiate the generated \c CalculatorStateMachine class in the
- \e calculator-widgets.cpp file, as follows:
-
- \quotefromfile calculator-widgets/calculator-widgets.cpp
- \skipto #include
- \printuntil }
-
- \section1 Connecting to Active Properties
-
- After instantiating the state machine, we can connect to the active
- properties of the states, as follows:
-
- \quotefromfile calculator-widgets/mainwindow.cpp
- \skipto connect
- \printuntil digit2
- \printuntil }
-
- The state machine can notify other code when events occur:
-
- \quotefromfile calculator-common/statemachine.scxml
- \skipto transition event="DISPLAY.UPDATE
- \printuntil </transition
-
- We connect to the \c updateDisplay event to display the data passed by
- the events:
-
- \quotefromfile calculator-widgets/mainwindow.cpp
- \skipto connectToEvent
- \printuntil });
-*/
diff --git a/examples/scxml/calculator-widgets/mainwindow.cpp b/examples/scxml/calculator-widgets/mainwindow.cpp
deleted file mode 100644
index b742b95..0000000
--- a/examples/scxml/calculator-widgets/mainwindow.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "mainwindow.h"
-#include "ui_mainwindow.h"
-
-#include <QScxmlStateMachine>
-#include <QStringListModel>
-
-QT_USE_NAMESPACE
-
-MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
- QWidget(parent),
- ui(new Ui::MainWindow),
- m_machine(machine)
-{
- ui->setupUi(this);
-
- connect(ui->digit0, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.0");
- });
- connect(ui->digit1, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.1");
- });
- connect(ui->digit2, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.2");
- });
- connect(ui->digit3, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.3");
- });
- connect(ui->digit4, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.4");
- });
- connect(ui->digit5, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.5");
- });
- connect(ui->digit6, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.6");
- });
- connect(ui->digit7, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.7");
- });
- connect(ui->digit8, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.8");
- });
- connect(ui->digit9, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("DIGIT.9");
- });
- connect(ui->point, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("POINT");
- });
- connect(ui->operPlus, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("OPER.PLUS");
- });
- connect(ui->operMinus, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("OPER.MINUS");
- });
- connect(ui->operStar, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("OPER.STAR");
- });
- connect(ui->operDiv, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("OPER.DIV");
- });
- connect(ui->equals, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("EQUALS");
- });
- connect(ui->c, &QAbstractButton::clicked, [this] {
- m_machine->submitEvent("C");
- });
-
- m_machine->connectToEvent(QLatin1String("updateDisplay"), this,
- [this](const QScxmlEvent &event) {
- const QString display = event.data().toMap()
- .value("display").toString();
- ui->display->setText(display);
- });
-}
-
-MainWindow::~MainWindow()
-{
- delete ui;
-}
-
diff --git a/examples/scxml/calculator-widgets/mainwindow.h b/examples/scxml/calculator-widgets/mainwindow.h
deleted file mode 100644
index fe34271..0000000
--- a/examples/scxml/calculator-widgets/mainwindow.h
+++ /dev/null
@@ -1,77 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef MAINWINDOW_H
-#define MAINWINDOW_H
-
-#include <QWidget>
-
-QT_BEGIN_NAMESPACE
-namespace Ui {
-class MainWindow;
-}
-class QScxmlStateMachine;
-QT_END_NAMESPACE
-
-
-class MainWindow : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit MainWindow(QScxmlStateMachine *machine, QWidget *parent = nullptr);
- ~MainWindow();
-
-private:
- QT_PREPEND_NAMESPACE(Ui::MainWindow) *ui;
- QScxmlStateMachine *m_machine;
-};
-
-#endif // MAINWINDOW_H
diff --git a/examples/scxml/calculator-widgets/mainwindow.ui b/examples/scxml/calculator-widgets/mainwindow.ui
deleted file mode 100644
index 2ee3864..0000000
--- a/examples/scxml/calculator-widgets/mainwindow.ui
+++ /dev/null
@@ -1,177 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QWidget" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>364</width>
- <height>182</height>
- </rect>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="0" column="0" colspan="2">
- <widget class="QLabel" name="display">
- <property name="frameShape">
- <enum>QFrame::StyledPanel</enum>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <widget class="QPushButton" name="digit7">
- <property name="text">
- <string>7</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QPushButton" name="point">
- <property name="text">
- <string>.</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QPushButton" name="digit5">
- <property name="text">
- <string>5</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QPushButton" name="digit9">
- <property name="text">
- <string>9</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QPushButton" name="digit8">
- <property name="text">
- <string>8</string>
- </property>
- </widget>
- </item>
- <item row="4" column="2">
- <widget class="QPushButton" name="c">
- <property name="text">
- <string>C</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QPushButton" name="digit4">
- <property name="text">
- <string>4</string>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QPushButton" name="operStar">
- <property name="text">
- <string>*</string>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QPushButton" name="digit3">
- <property name="text">
- <string>3</string>
- </property>
- </widget>
- </item>
- <item row="4" column="3">
- <widget class="QPushButton" name="equals">
- <property name="text">
- <string>=</string>
- </property>
- </widget>
- </item>
- <item row="1" column="3">
- <widget class="QPushButton" name="operMinus">
- <property name="text">
- <string>-</string>
- </property>
- </widget>
- </item>
- <item row="2" column="0">
- <widget class="QPushButton" name="digit1">
- <property name="text">
- <string>1</string>
- </property>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QPushButton" name="digit0">
- <property name="text">
- <string>0</string>
- </property>
- </widget>
- </item>
- <item row="3" column="3">
- <widget class="QPushButton" name="operDiv">
- <property name="text">
- <string>/</string>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <widget class="QPushButton" name="operPlus">
- <property name="text">
- <string>+</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QPushButton" name="digit6">
- <property name="text">
- <string>6</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QPushButton" name="digit2">
- <property name="text">
- <string>2</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="2" column="1">
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>10</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/examples/scxml/calculator/Button.qml b/examples/scxml/calculator/Button.qml
new file mode 100644
index 0000000..b2f0a79
--- /dev/null
+++ b/examples/scxml/calculator/Button.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+
+Rectangle {
+ id: button
+ signal clicked
+ property alias text: text.text
+ border.width: 1
+ border.color: "white"
+ property real textHeight: height - 2
+ property real fontHeight: 0.3
+ property bool pressed: mouse.pressed
+ property real implicitMargin: (width - text.implicitWidth) / 2
+
+ Text {
+ id: text
+ anchors.left: parent.left
+ anchors.right: parent.right
+ anchors.top: parent.top
+ height: parent.textHeight
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ font.pixelSize: height * button.fontHeight
+ color: "#1b1c1d"
+ font.family: "Open Sans Regular"
+ }
+
+ MouseArea {
+ id: mouse
+ anchors.fill: parent
+ onClicked: button.clicked()
+ }
+}
diff --git a/examples/scxml/calculator/CMakeLists.txt b/examples/scxml/calculator/CMakeLists.txt
new file mode 100644
index 0000000..a8844f7
--- /dev/null
+++ b/examples/scxml/calculator/CMakeLists.txt
@@ -0,0 +1,52 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(calculator LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/calculator")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Scxml)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(calculatorscxml
+ calculator.cpp
+)
+
+set_target_properties(calculatorscxml PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(calculatorscxml PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Scxml
+)
+
+# Statecharts:
+qt6_add_statecharts(calculatorscxml
+ statemachine.scxml
+)
+
+qt_add_qml_module(calculatorscxml
+ URI Calculator
+ VERSION 1.0
+ QML_FILES
+ MainWindow.qml
+ Button.qml
+ SOURCES
+ calculator-qml.h
+)
+
+install(TARGETS calculatorscxml
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/calculator-qml/calculator-qml.qml b/examples/scxml/calculator/MainWindow.qml
index c7d8236..61a349d 100644
--- a/examples/scxml/calculator-qml/calculator-qml.qml
+++ b/examples/scxml/calculator/MainWindow.qml
@@ -1,57 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import CalculatorStateMachine 1.0
-import QtQuick 2.5
-import QtQuick.Window 2.0
-import QtScxml 5.8
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Window
+import QtScxml
+import Calculator
Window {
id: window
@@ -64,7 +19,7 @@ Window {
running: true
EventConnection {
events: ["updateDisplay"]
- onOccurred: resultText.text = event.data.display
+ onOccurred: (event)=> resultText.text = event.data.display
}
}
@@ -111,6 +66,8 @@ Window {
id: operations
model: ["÷", "×", "+", "-"]
Button {
+ required property int index
+ required property string modelData
y: 0
x: index * width
width: parent.width / 4
@@ -134,6 +91,8 @@ Window {
id: digits
model: ["7", "8", "9", "4", "5", "6", "1", "2", "3", "0", ".", "C"]
Button {
+ required property int index
+ required property string modelData
x: (index % 3) * width
y: Math.floor(index / 3 + 1) * height
width: parent.width / 4
diff --git a/examples/scxml/calculator/calculator-qml.h b/examples/scxml/calculator/calculator-qml.h
new file mode 100644
index 0000000..92a574c
--- /dev/null
+++ b/examples/scxml/calculator/calculator-qml.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef CALCULATOR_QML
+#define CALCULATOR_QML
+
+#include "statemachine.h"
+
+#include <QtQml/qqml.h>
+#include <QtCore/qobject.h>
+
+struct CalculatorStateMachineRegistration
+{
+ Q_GADGET
+ QML_FOREIGN(CalculatorStateMachine)
+ QML_NAMED_ELEMENT(CalculatorStateMachine)
+ QML_ADDED_IN_VERSION(1, 0)
+};
+
+#endif // CALCULATOR_QML
diff --git a/examples/scxml/calculator/calculator.cpp b/examples/scxml/calculator/calculator.cpp
new file mode 100644
index 0000000..301b7c2
--- /dev/null
+++ b/examples/scxml/calculator/calculator.cpp
@@ -0,0 +1,18 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtGui/qguiapplication.h>
+#include <QtQml/qqmlapplicationengine.h>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
+ [](){ QCoreApplication::exit(EXIT_FAILURE); }, Qt::QueuedConnection);
+ engine.loadFromModule("Calculator", "MainWindow");
+
+ return app.exec();
+}
+
diff --git a/examples/scxml/calculator/calculator.pro b/examples/scxml/calculator/calculator.pro
new file mode 100644
index 0000000..ba7d343
--- /dev/null
+++ b/examples/scxml/calculator/calculator.pro
@@ -0,0 +1,26 @@
+QT += qml scxml
+
+CONFIG += c++11
+CONFIG += qmltypes
+
+SOURCES += calculator.cpp
+
+HEADERS += calculator-qml.h
+
+QML_IMPORT_NAME = Calculator
+QML_IMPORT_MAJOR_VERSION = 1
+
+qml_resources.files = \
+ qmldir \
+ MainWindow.qml \
+ Button.qml
+
+qml_resources.prefix = /qt/qml/Calculator
+
+RESOURCES += qml_resources
+
+STATECHARTS = statemachine.scxml
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/scxml/calculator
+INSTALLS += target
diff --git a/examples/scxml/calculator-qml/doc/images/calculator-qml.png b/examples/scxml/calculator/doc/images/calculator.png
index 021f56d..021f56d 100644
--- a/examples/scxml/calculator-qml/doc/images/calculator-qml.png
+++ b/examples/scxml/calculator/doc/images/calculator.png
Binary files differ
diff --git a/examples/scxml/calculator/doc/src/calculator.qdoc b/examples/scxml/calculator/doc/src/calculator.qdoc
new file mode 100644
index 0000000..5df6919
--- /dev/null
+++ b/examples/scxml/calculator/doc/src/calculator.qdoc
@@ -0,0 +1,48 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example calculator
+ \title SCXML Calculator
+ \examplecategory {Data Processing & I/O}
+ \ingroup examples-qtscxml
+ \meta tag {state machine}
+
+ \image calculator.png
+
+ \brief An application that implements the Calculator example
+ presented in the SCXML Specification.
+
+ \e {Calculator} uses Qt SCXML to implement the
+ \l{SCXML Specification - Calculator Example}{Calculator Example}
+ presented in the SCXML Specification.
+
+ The state machine is specified in the \e statemachine.scxml file and
+ compiled into the \c CalculatorStateMachine class. The user interface is
+ created using Qt Quick.
+
+ \include examples-run.qdocinc
+
+ \section1 Instantiating the State Machine
+
+ We make the generated \c CalculatorStateMachine class available to QML by
+ declaring it as a QML type in the \e calculator-qml.h file:
+
+ \quotefromfile calculator/calculator-qml.h
+ \skipto struct
+ \printuntil }
+
+ We instantiate a CalculatorStateMachine and listen to the \c updateDisplay
+ event. When it occurs, we change the text on the calculator display:
+
+ \quotefromfile calculator/MainWindow.qml
+ \skipto CalculatorStateMachine {
+ \printuntil /^ {4}\}/
+
+ When users press the calculator buttons, the buttons submit events to the
+ state machine:
+
+ \skipto DIGIT.
+ \skipto Button
+ \printuntil }
+*/
diff --git a/examples/scxml/calculator/qmldir b/examples/scxml/calculator/qmldir
new file mode 100644
index 0000000..1782b57
--- /dev/null
+++ b/examples/scxml/calculator/qmldir
@@ -0,0 +1,5 @@
+module Calculator
+prefer :/qt/qml/Calculator/
+MainWindow 1.0 MainWindow.qml
+Button 1.0 Button.qml
+
diff --git a/examples/scxml/calculator-common/statemachine.scxml b/examples/scxml/calculator/statemachine.scxml
index b1c6bb7..b1c6bb7 100644
--- a/examples/scxml/calculator-common/statemachine.scxml
+++ b/examples/scxml/calculator/statemachine.scxml
diff --git a/examples/scxml/ftpclient/CMakeLists.txt b/examples/scxml/ftpclient/CMakeLists.txt
new file mode 100644
index 0000000..adc9b7a
--- /dev/null
+++ b/examples/scxml/ftpclient/CMakeLists.txt
@@ -0,0 +1,47 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(ftpclient LANGUAGES CXX)
+
+if (ANDROID)
+ message(FATAL_ERROR "This project cannot be built on Android.")
+endif()
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/ftpclient")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Network Scxml)
+
+qt_standard_project_setup()
+
+qt_add_executable(ftpclient
+ ftpcontrolchannel.cpp ftpcontrolchannel.h
+ ftpdatachannel.cpp ftpdatachannel.h
+ main.cpp
+)
+
+set_target_properties(ftpclient PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(ftpclient PRIVATE
+ Qt6::Core
+ Qt6::Network
+ Qt6::Scxml
+)
+
+# Statecharts:
+qt6_add_statecharts(ftpclient
+ simpleftp.scxml
+)
+
+install(TARGETS ftpclient
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/ftpclient/doc/src/ftpclient.qdoc b/examples/scxml/ftpclient/doc/src/ftpclient.qdoc
index b69994d..9f3ce2a 100644
--- a/examples/scxml/ftpclient/doc/src/ftpclient.qdoc
+++ b/examples/scxml/ftpclient/doc/src/ftpclient.qdoc
@@ -1,34 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example ftpclient
- \title Qt SCXML FTP Client Example
+ \title SCXML FTP Client
\ingroup examples-qtscxml
+ \examplecategory {Networking}
\brief Implements a simple FTP client using a state machine.
@@ -67,19 +44,31 @@
\section1 Compiling the State Machine
We link against the Qt SCXML module by adding the following line to the
- \e .pro file:
+ project build files.
+ With qmake, we add the following to \e ftpclient.pro
\quotefromfile ftpclient/ftpclient.pro
- \printuntil scxml
+ \skipto QT
+ \printline scxml
We then specify the state machine to compile:
-
\skipto STATECHARTS
- \printuntil simpleftp.scxml
+ \printline scxml
+
+ With CMake, we add the following to \e CMakeLists.txt
+ \quotefromfile ftpclient/CMakeLists.txt
+ \skipto find_package
+ \printline Scxml
+ \skipto target_link_libraries
+ \printuntil )
+
+ We then specify the state machine to compile:
+ \skipto qt6_add_statecharts
+ \printuntil )
The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
- \e ftpclient.h and \e ftpclient.cpp, and to add them to the \c HEADERS
- and \c SOURCES variables for compilation.
+ \e simpleftp.h and \e simpleftp.cpp, and to add them appropriately to
+ the project as headers and sources.
\section1 Instantiating the State Machine
@@ -88,7 +77,7 @@
\quotefromfile ftpclient/main.cpp
\skipto #include
- \printuntil ftpdatachannel.h
+ \printuntil simpleftp
\dots
\skipto int main
\printuntil {
@@ -115,7 +104,7 @@
\printuntil }
We send commands to log into the FTP server as an anonymous user, to
- announce a port for the data connection, and to retrive a file:
+ announce a port for the data connection, and to retrieve a file:
\skipto QList
\printuntil });
diff --git a/examples/scxml/ftpclient/ftpcontrolchannel.cpp b/examples/scxml/ftpclient/ftpcontrolchannel.cpp
index 5f58105..d52a1e3 100644
--- a/examples/scxml/ftpclient/ftpcontrolchannel.cpp
+++ b/examples/scxml/ftpclient/ftpcontrolchannel.cpp
@@ -1,55 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "ftpcontrolchannel.h"
+#include <QtCore/qcoreapplication.h>
+
FtpControlChannel::FtpControlChannel(QObject *parent) : QObject(parent)
{
connect(&m_socket, &QIODevice::readyRead,
@@ -59,6 +14,8 @@ FtpControlChannel::FtpControlChannel(QObject *parent) : QObject(parent)
connect(&m_socket, &QAbstractSocket::connected, this, [this]() {
emit opened(m_socket.localAddress(), m_socket.localPort());
});
+ connect(&m_socket, &QAbstractSocket::errorOccurred,
+ this, &FtpControlChannel::error);
}
void FtpControlChannel::connectToServer(const QString &server)
@@ -85,12 +42,21 @@ void FtpControlChannel::onReadyRead()
int space = received.indexOf(' ');
if (space != -1) {
int code = received.mid(0, space).toInt();
- if (code == 0)
+ if (code == 0) {
+ qDebug() << "Info received: " << received.mid(space + 1);
emit info(received.mid(space + 1));
- else
+ } else {
+ qDebug() << "Reply received: " << received.mid(space + 1);
emit reply(code, received.mid(space + 1));
+ }
} else {
emit invalidReply(received);
}
}
}
+
+void FtpControlChannel::error(QAbstractSocket::SocketError error)
+{
+ qWarning() << "Socket error:" << error;
+ QCoreApplication::exit();
+}
diff --git a/examples/scxml/ftpclient/ftpcontrolchannel.h b/examples/scxml/ftpclient/ftpcontrolchannel.h
index 092f2a9..b577dd0 100644
--- a/examples/scxml/ftpclient/ftpcontrolchannel.h
+++ b/examples/scxml/ftpclient/ftpcontrolchannel.h
@@ -1,59 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef FTPCONTROLCHANNEL_H
#define FTPCONTROLCHANNEL_H
-#include <QHostAddress>
-#include <QObject>
-#include <QTcpSocket>
+#include <QtNetwork/qhostaddress.h>
+#include <QtNetwork/qtcpsocket.h>
+#include <QtCore/qobject.h>
class FtpControlChannel : public QObject
{
@@ -67,6 +20,9 @@ public:
// Send a command to the server
void command(const QByteArray &command, const QByteArray &params);
+public slots:
+ void error(QAbstractSocket::SocketError);
+
signals:
// Connection established. Local address and port are known.
diff --git a/examples/scxml/ftpclient/ftpdatachannel.cpp b/examples/scxml/ftpclient/ftpdatachannel.cpp
index f0a7aa4..83ef872 100644
--- a/examples/scxml/ftpclient/ftpdatachannel.cpp
+++ b/examples/scxml/ftpclient/ftpdatachannel.cpp
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "ftpdatachannel.h"
@@ -54,7 +7,7 @@ FtpDataChannel::FtpDataChannel(QObject *parent) : QObject(parent)
{
connect(&m_server, &QTcpServer::newConnection, this, [this]() {
m_socket.reset(m_server.nextPendingConnection());
- connect(m_socket.data(), &QTcpSocket::readyRead, [this]() {
+ connect(m_socket.get(), &QTcpSocket::readyRead, this, [this]() {
emit dataReceived(m_socket->readAll());
});
});
diff --git a/examples/scxml/ftpclient/ftpdatachannel.h b/examples/scxml/ftpclient/ftpdatachannel.h
index 2418196..da356a8 100644
--- a/examples/scxml/ftpclient/ftpdatachannel.h
+++ b/examples/scxml/ftpclient/ftpdatachannel.h
@@ -1,60 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef FTPDATACHANNEL_H
#define FTPDATACHANNEL_H
-#include <QObject>
-#include <QScopedPointer>
-#include <QTcpServer>
-#include <QTcpSocket>
+#include <QtCore/qobject.h>
+#include <QtNetwork/qtcpserver.h>
+#include <QtNetwork/qtcpsocket.h>
+
+#include <memory>
class FtpDataChannel : public QObject
{
@@ -86,7 +40,7 @@ signals:
private:
QTcpServer m_server;
- QScopedPointer<QTcpSocket> m_socket;
+ std::unique_ptr<QTcpSocket> m_socket;
};
#endif // FTPDATACHANNEL_H
diff --git a/examples/scxml/ftpclient/main.cpp b/examples/scxml/ftpclient/main.cpp
index b285077..9159c2b 100644
--- a/examples/scxml/ftpclient/main.cpp
+++ b/examples/scxml/ftpclient/main.cpp
@@ -1,58 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "ftpcontrolchannel.h"
#include "ftpdatachannel.h"
#include "simpleftp.h"
-#include <QCoreApplication>
+#include <QtCore/qcoreapplication.h>
#include <iostream>
@@ -64,7 +17,8 @@ struct Command {
int main(int argc, char *argv[])
{
if (argc != 3) {
- qDebug() << "Usage: ftpclient <server> <file>";
+ qWarning() << "Usage: ftpclient <server> <file>";
+ qWarning() << "For example: ftpclient ftp.gnu.org welcome.msg";
return 1;
}
@@ -79,7 +33,7 @@ int main(int argc, char *argv[])
// Print all data retrieved from the server on the console.
QObject::connect(&dataChannel, &FtpDataChannel::dataReceived,
[](const QByteArray &data) {
- std::cout << data.constData();
+ std::cout << data.constData() << std::flush;
});
// Translate server replies into state machine events.
@@ -123,7 +77,7 @@ int main(int argc, char *argv[])
// Connect to our own local FTP server
controlChannel.connectToServer(server);
- QObject::connect(&controlChannel, &FtpControlChannel::opened,
+ QObject::connect(&controlChannel, &FtpControlChannel::opened, &dataChannel,
[&](const QHostAddress &address, int) {
dataChannel.listen(address);
commands[1].args = dataChannel.portspec();
diff --git a/examples/scxml/ftpclient/simpleftp.scxml b/examples/scxml/ftpclient/simpleftp.scxml
index 7d57040..0e788bc 100644
--- a/examples/scxml/ftpclient/simpleftp.scxml
+++ b/examples/scxml/ftpclient/simpleftp.scxml
@@ -1,54 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="FtpClient"
datamodel="ecmascript">
diff --git a/examples/scxml/invoke-common/Button.qml b/examples/scxml/invoke-common/Button.qml
deleted file mode 100644
index 4e92e32..0000000
--- a/examples/scxml/invoke-common/Button.qml
+++ /dev/null
@@ -1,82 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.5
-
-Item {
- id: button
- signal clicked
- property string text: "hello"
- property bool enabled: true
- opacity: enabled ? 1.0 : 0.5
-
- Rectangle {
- x: 5
- y: 5
- width: parent.width - 10
- height: parent.height - 10
- radius: 5
- color: "lightsteelblue"
-
- Text {
- anchors.fill: parent
- color: "white"
- text: button.text
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
-
- MouseArea {
- id: mouse
- anchors.fill: parent
- onClicked: if (button.enabled) button.clicked()
- }
- }
-}
diff --git a/examples/scxml/invoke-common/MainView.qml b/examples/scxml/invoke-common/MainView.qml
deleted file mode 100644
index 8d8c3e3..0000000
--- a/examples/scxml/invoke-common/MainView.qml
+++ /dev/null
@@ -1,105 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.5
-import QtQuick.Window 2.2
-import QtScxml 5.8
-
-Window {
- id: window
- visible: true
- property StateMachine stateMachine
-
- color: "black"
- width: 400
- height: 300
-
- Item {
- width: parent.width / 2
- height: parent.height
-
- Button {
- id: nowhere
- text: "Go Nowhere"
- width: parent.width
- height: parent.height / 2
- onClicked: stateMachine.submitEvent("goNowhere")
- enabled: stateMachine.somewhere
- }
-
- Button {
- id: somewhere
- text: "Go Somewhere"
- width: parent.width
- height: parent.height / 2
- y: parent.height / 2
- onClicked: stateMachine.submitEvent("goSomewhere")
- enabled: stateMachine.nowhere
- }
- }
-
- Loader {
- source: "SubView.qml"
- active: stateMachine.somewhere
-
- x: parent.width / 2
- width: parent.width / 2
- height: parent.height
-
- InvokedServices {
- id: services
- stateMachine: window.stateMachine
- }
-
- property var anywhere: services.children.anywhere ? services.children.anywhere.stateMachine
- : undefined
- }
-}
-
diff --git a/examples/scxml/invoke-common/SubView.qml b/examples/scxml/invoke-common/SubView.qml
deleted file mode 100644
index 2c374d5..0000000
--- a/examples/scxml/invoke-common/SubView.qml
+++ /dev/null
@@ -1,73 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.5
-
-Item {
- // "anywhere" is a context property, so we always have to check if it's null
- Button {
- id: here
- enabled: anywhere ? anywhere.here : false
- text: "Go There"
- width: parent.width / 2
- height: parent.height
- onClicked: anywhere.submitEvent("goThere")
- }
-
- Button {
- id: there
- enabled: anywhere ? anywhere.there : false
- text: "Go Here"
- width: parent.width / 2
- height: parent.height
- x: width
- onClicked: anywhere.submitEvent("goHere")
- }
-}
diff --git a/examples/scxml/invoke-common/statemachine.scxml b/examples/scxml/invoke-common/statemachine.scxml
deleted file mode 100644
index ad04cb3..0000000
--- a/examples/scxml/invoke-common/statemachine.scxml
+++ /dev/null
@@ -1,79 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="Directions"
- initial="anyplace"
->
- <state id="anyplace">
- <transition event="goNowhere" target="nowhere"/>
- <transition event="goSomewhere" target="somewhere"/>
-
- <state id="nowhere"/>
- <state id="somewhere">
- <invoke type="http://www.w3.org/TR/scxml/">
- <content>
- <scxml name="anywhere" version="1.0">
- <state id="here">
- <transition event="goThere" target="there"/>
- </state>
- <state id="there">
- <transition event="goHere" target="here"/>
- </state>
- </scxml>
- </content>
- </invoke>
- </state>
- </state>
-</scxml>
diff --git a/examples/scxml/invoke-dynamic/doc/images/invoke-dynamic.png b/examples/scxml/invoke-dynamic/doc/images/invoke-dynamic.png
deleted file mode 100644
index 4efa912..0000000
--- a/examples/scxml/invoke-dynamic/doc/images/invoke-dynamic.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc b/examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc
deleted file mode 100644
index bff773a..0000000
--- a/examples/scxml/invoke-dynamic/doc/src/invoke-dynamic.qdoc
+++ /dev/null
@@ -1,67 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example invoke-dynamic
- \title Qt SCXML Invoke Example (Dynamic)
- \ingroup examples-qtscxml
-
- \brief Invokes a dynamically loaded nested state machine.
-
- \image invoke-dynamic.png
-
- \e{Invoke Example (Dynamic)} demonstrates how to use the \c <invoke> element
- with generated nested state-machines, where the SCXML file is dynamically
- loaded. The \c <invoke> element is used to create an instance of an external
- service.
-
- \include examples-run.qdocinc
-
- \section1 Invoking the State Machine
-
- In \e statemachine.scxml, we specify a state machine with the name
- \e Directions of type \e http://www.w3.org/TR/scxml/ to invoke:
-
- \quotefromfile invoke-common/statemachine.scxml
- \skipto scxml
- \printuntil
-
- \section1 Dynamically Loading the State Machine
-
- We link against the Qt SCXML module by adding the following line to the
- \e invoke-dynamic.pro file:
-
- \quotefromfile invoke-dynamic/invoke-dynamic.pro
- \skipto QT
- \printline scxml
-
- We dynamically create the state machine, as follows:
-
- \quotefromfile invoke-dynamic/invoke-dynamic.qml
- \skipto import
- \printuntil }
-*/
diff --git a/examples/scxml/invoke-dynamic/invoke-dynamic.cpp b/examples/scxml/invoke-dynamic/invoke-dynamic.cpp
deleted file mode 100644
index 4d48e16..0000000
--- a/examples/scxml/invoke-dynamic/invoke-dynamic.cpp
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
-
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/invoke-dynamic.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
diff --git a/examples/scxml/invoke-dynamic/invoke-dynamic.pro b/examples/scxml/invoke-dynamic/invoke-dynamic.pro
deleted file mode 100644
index e4f07da..0000000
--- a/examples/scxml/invoke-dynamic/invoke-dynamic.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-TEMPLATE = app
-
-QT += qml scxml
-CONFIG += c++11
-
-SOURCES += invoke-dynamic.cpp
-
-RESOURCES += invoke-dynamic.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/invoke-dynamic
-INSTALLS += target
-
diff --git a/examples/scxml/invoke-dynamic/invoke-dynamic.qml b/examples/scxml/invoke-dynamic/invoke-dynamic.qml
deleted file mode 100644
index 4ff85b4..0000000
--- a/examples/scxml/invoke-dynamic/invoke-dynamic.qml
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtScxml 5.8
-
-MainView {
- stateMachine: directions.stateMachine
-
- StateMachineLoader {
- id: directions
- source: "qrc:///statemachine.scxml"
- }
-}
diff --git a/examples/scxml/invoke-dynamic/invoke-dynamic.qrc b/examples/scxml/invoke-dynamic/invoke-dynamic.qrc
deleted file mode 100644
index ecf8bd1..0000000
--- a/examples/scxml/invoke-dynamic/invoke-dynamic.qrc
+++ /dev/null
@@ -1,9 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>invoke-dynamic.qml</file>
- <file alias="statemachine.scxml">../invoke-common/statemachine.scxml</file>
- <file alias="MainView.qml">../invoke-common/MainView.qml</file>
- <file alias="SubView.qml">../invoke-common/SubView.qml</file>
- <file alias="Button.qml">../invoke-common/Button.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/invoke-static/doc/src/invoke-static.qdoc b/examples/scxml/invoke-static/doc/src/invoke-static.qdoc
deleted file mode 100644
index 099b904..0000000
--- a/examples/scxml/invoke-static/doc/src/invoke-static.qdoc
+++ /dev/null
@@ -1,79 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example invoke-static
- \title Qt SCXML Invoke Example (Static)
- \ingroup examples-qtscxml
-
- \brief Invokes a compiled nested state machine.
-
- \image invoke-static.png
-
- \e{Invoke Example (Static)} demonstrates how to use the \c <invoke> element
- with generated nested state-machines, where the SCXML file is compiled to
- a C++ class. The \c <invoke> element is used to create an instance of an
- external service.
-
- \include examples-run.qdocinc
-
- \section1 Invoking the State Machine
-
- In \e statemachine.scxml, we specify a state machine with the name
- \e Directions of type \e http://www.w3.org/TR/scxml/ to invoke:
-
- \quotefromfile invoke-common/statemachine.scxml
- \skipto scxml
- \printuntil
-
- \section1 Compiling the State Machine
-
- We link against the Qt SCXML module by adding the following line to the
- \e invoke-static.pro file:
-
- \quotefromfile invoke-static/invoke-static.pro
- \skipto QT
- \printline scxml
-
- We then specify the state machine to compile:
-
- \skipto STATECHARTS
- \printline statemachine
-
- The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
- \e statemachine.h and \e statemachine.cpp, and to add them to the \c HEADERS
- and \c SOURCES variables for compilation.
-
- \section1 Instantiating the State Machine
-
- We instantiate the generated \c Directions class in the \e invoke-static.cpp
- file, as follows:
-
- \quotefromfile invoke-static/invoke-static.cpp
- \skipto statemachine.h
- \printuntil }
-*/
diff --git a/examples/scxml/invoke-static/invoke-static.cpp b/examples/scxml/invoke-static/invoke-static.cpp
deleted file mode 100644
index d53eb48..0000000
--- a/examples/scxml/invoke-static/invoke-static.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-#include "statemachine.h"
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
-
- qmlRegisterType<Directions>("Directions", 1, 0, "Directions");
-
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/invoke-static.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
diff --git a/examples/scxml/invoke-static/invoke-static.pro b/examples/scxml/invoke-static/invoke-static.pro
deleted file mode 100644
index e2e206c..0000000
--- a/examples/scxml/invoke-static/invoke-static.pro
+++ /dev/null
@@ -1,14 +0,0 @@
-TEMPLATE = app
-
-QT += qml scxml
-CONFIG += c++11
-
-SOURCES += invoke-static.cpp
-
-RESOURCES += invoke-static.qrc
-
-STATECHARTS = ../invoke-common/statemachine.scxml
-
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/invoke-static
-INSTALLS += target
-
diff --git a/examples/scxml/invoke-static/invoke-static.qml b/examples/scxml/invoke-static/invoke-static.qml
deleted file mode 100644
index dc59e1b..0000000
--- a/examples/scxml/invoke-static/invoke-static.qml
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import Directions 1.0
-
-MainView {
- stateMachine: Directions {
- running: true
- }
-}
diff --git a/examples/scxml/invoke-static/invoke-static.qrc b/examples/scxml/invoke-static/invoke-static.qrc
deleted file mode 100644
index cc5bcad..0000000
--- a/examples/scxml/invoke-static/invoke-static.qrc
+++ /dev/null
@@ -1,8 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>invoke-static.qml</file>
- <file alias="MainView.qml">../invoke-common/MainView.qml</file>
- <file alias="SubView.qml">../invoke-common/SubView.qml</file>
- <file alias="Button.qml">../invoke-common/Button.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/invoke/Button.qml b/examples/scxml/invoke/Button.qml
new file mode 100644
index 0000000..5be5665
--- /dev/null
+++ b/examples/scxml/invoke/Button.qml
@@ -0,0 +1,37 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+
+Item {
+ id: button
+ signal clicked
+ property string text: "hello"
+ property bool enabled: true
+ opacity: enabled ? 1.0 : 0.5
+
+ Rectangle {
+ x: 5
+ y: 5
+ width: parent.width - 10
+ height: parent.height - 10
+ radius: 5
+ color: "lightsteelblue"
+
+ Text {
+ anchors.fill: parent
+ color: "white"
+ text: button.text
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ id: mouse
+ anchors.fill: parent
+ onClicked: if (button.enabled) button.clicked()
+ }
+ }
+}
diff --git a/examples/scxml/invoke/CMakeLists.txt b/examples/scxml/invoke/CMakeLists.txt
new file mode 100644
index 0000000..d8c858a
--- /dev/null
+++ b/examples/scxml/invoke/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(invoke LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/invoke")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Scxml)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(invoke
+ invoke.cpp
+)
+
+set_target_properties(invoke PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(invoke PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Scxml
+)
+
+# Statecharts:
+qt6_add_statecharts(invoke
+ statemachine.scxml
+)
+
+qt_add_qml_module(invoke
+ URI InvokeExample
+ VERSION 1.0
+ QML_FILES
+ Button.qml
+ MainView.qml
+ SubView.qml
+ SOURCES
+ invoke-qml.h
+)
+
+install(TARGETS invoke
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/invoke/MainView.qml b/examples/scxml/invoke/MainView.qml
new file mode 100644
index 0000000..c7a4400
--- /dev/null
+++ b/examples/scxml/invoke/MainView.qml
@@ -0,0 +1,63 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Window
+import QtScxml
+import InvokeExample
+
+Window {
+ id: window
+ visible: true
+ color: "black"
+ width: 400
+ height: 300
+
+ DirectionsStateMachine {
+ id: stateMachine
+ running: true
+ }
+
+ Item {
+ width: parent.width / 2
+ height: parent.height
+
+ Button {
+ id: nowhere
+ text: "Go Nowhere"
+ width: parent.width
+ height: parent.height / 2
+ onClicked: stateMachine.submitEvent("goNowhere")
+ enabled: stateMachine.somewhere
+ }
+
+ Button {
+ id: somewhere
+ text: "Go Somewhere"
+ width: parent.width
+ height: parent.height / 2
+ y: parent.height / 2
+ onClicked: stateMachine.submitEvent("goSomewhere")
+ enabled: stateMachine.nowhere
+ }
+ }
+
+ Loader {
+ sourceComponent: SubView {
+ anywhere: services.children.anywhere ? services.children.anywhere.stateMachine : null
+ }
+ active: stateMachine.somewhere
+
+ x: parent.width / 2
+ width: parent.width / 2
+ height: parent.height
+
+ InvokedServices {
+ id: services
+ stateMachine: stateMachine
+ }
+ }
+}
+
diff --git a/examples/scxml/invoke/SubView.qml b/examples/scxml/invoke/SubView.qml
new file mode 100644
index 0000000..31dbf06
--- /dev/null
+++ b/examples/scxml/invoke/SubView.qml
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtScxml
+
+Item {
+ id: root
+ required property StateMachine anywhere
+
+ Button {
+ id: here
+ enabled: root.anywhere ? root.anywhere.here : false
+ text: "Go There"
+ width: parent.width / 2
+ height: parent.height
+ onClicked: root.anywhere.submitEvent("goThere")
+ }
+
+ Button {
+ id: there
+ enabled: root.anywhere ? root.anywhere.there : false
+ text: "Go Here"
+ width: parent.width / 2
+ height: parent.height
+ x: width
+ onClicked: root.anywhere.submitEvent("goHere")
+ }
+}
diff --git a/examples/scxml/invoke-static/doc/images/invoke-static.png b/examples/scxml/invoke/doc/images/invoke.png
index fa73ebe..fa73ebe 100644
--- a/examples/scxml/invoke-static/doc/images/invoke-static.png
+++ b/examples/scxml/invoke/doc/images/invoke.png
Binary files differ
diff --git a/examples/scxml/invoke/doc/src/invoke.qdoc b/examples/scxml/invoke/doc/src/invoke.qdoc
new file mode 100644
index 0000000..8e8d903
--- /dev/null
+++ b/examples/scxml/invoke/doc/src/invoke.qdoc
@@ -0,0 +1,75 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example invoke
+ \title SCXML Invoke
+ \examplecategory {Data Processing & I/O}
+ \ingroup examples-qtscxml
+
+ \brief Invokes a compiled nested state machine.
+
+ \image invoke.png
+
+ \e{Invoke} demonstrates how to use the \c <invoke> element
+ with generated nested state-machines, where the SCXML file is compiled to
+ a C++ class. The \c <invoke> element is used to create an instance of an
+ external service.
+
+ \include examples-run.qdocinc
+
+ \section1 Invoking the State Machine
+
+ In \e statemachine.scxml, we specify a state machine with the name
+ \e DirectionsStateMachine of type \e http://www.w3.org/TR/scxml/ to invoke:
+
+ \quotefromfile invoke/statemachine.scxml
+ \skipto scxml
+ \printuntil
+
+ \section1 Compiling the State Machine
+ We link against the Qt SCXML module by adding the following lines to the
+ example's build files.
+
+ \section2 \e invoke.pro when using qmake:
+
+ \quotefromfile invoke/invoke.pro
+ \skipto QT
+ \printline scxml
+
+ We then specify the state machine to compile:
+ \skipto STATECHARTS
+ \printline scxml
+
+ \section2 \e CMakeLists.txt when using cmake:
+ \quotefromfile invoke/CMakeLists.txt
+ \skipto find_package
+ \printline Scxml
+ \skipto target_link_libraries
+ \printuntil )
+
+ We then specify the state machine to compile:
+ \skipto qt6_add_statecharts
+ \printuntil )
+
+ The statechart directives \e STATECHARTS or \e qt6_add_statecharts invoke the Qt SCXML
+ Compiler, \c qscxmlc, which is run automatically to generate \e statemachine.h and
+ \e statemachine.cpp, which are then added appropriately as headers and sources for
+ compilation.
+
+ \section1 Declaring the state machine as QML element
+
+ The state machine is declared as a QML element as follows:
+ \quotefromfile invoke/invoke-qml.h
+ \skipto struct
+ \printuntil }
+
+ \section1 Instantiating the State Machine
+
+ We instantiate the generated \c DirectionsStateMachine element in the \e MainView.qml
+ file, as follows:
+
+ \quotefromfile invoke/MainView.qml
+ \skipto DirectionsStateMachine
+ \printuntil }
+*/
diff --git a/examples/scxml/invoke/invoke-qml.h b/examples/scxml/invoke/invoke-qml.h
new file mode 100644
index 0000000..e0735a8
--- /dev/null
+++ b/examples/scxml/invoke/invoke-qml.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef INVOKE_QML
+#define INVOKE_QML
+
+#include "statemachine.h"
+
+#include <QtQml/qqml.h>
+#include <QtCore/qobject.h>
+
+struct DirectionsStateMachineRegistration
+{
+ Q_GADGET
+ QML_FOREIGN(DirectionsStateMachine)
+ QML_NAMED_ELEMENT(DirectionsStateMachine)
+ QML_ADDED_IN_VERSION(1, 0)
+};
+
+#endif // INVOKE_QML
diff --git a/examples/scxml/invoke/invoke.cpp b/examples/scxml/invoke/invoke.cpp
new file mode 100644
index 0000000..6680396
--- /dev/null
+++ b/examples/scxml/invoke/invoke.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtGui/qguiapplication.h>
+#include <QtQml/qqmlapplicationengine.h>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
+ [](){ QCoreApplication::exit(EXIT_FAILURE); }, Qt::QueuedConnection);
+ engine.loadFromModule("InvokeExample", "MainView");
+
+ return app.exec();
+}
diff --git a/examples/scxml/invoke/invoke.pro b/examples/scxml/invoke/invoke.pro
new file mode 100644
index 0000000..d23a0af
--- /dev/null
+++ b/examples/scxml/invoke/invoke.pro
@@ -0,0 +1,27 @@
+TEMPLATE = app
+
+QT += qml scxml
+CONFIG += c++11
+CONFIG += qmltypes
+
+SOURCES += invoke.cpp
+HEADERS += invoke-qml.h
+
+QML_IMPORT_NAME = InvokeExample
+QML_IMPORT_MAJOR_VERSION = 1
+
+qml_resources.files = \
+ qmldir \
+ Button.qml \
+ MainView.qml \
+ SubView.qml
+
+qml_resources.prefix = /qt/qml/InvokeExample
+
+RESOURCES += qml_resources
+
+STATECHARTS = statemachine.scxml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/scxml/invoke
+INSTALLS += target
+
diff --git a/examples/scxml/invoke/qmldir b/examples/scxml/invoke/qmldir
new file mode 100644
index 0000000..6816682
--- /dev/null
+++ b/examples/scxml/invoke/qmldir
@@ -0,0 +1,5 @@
+module InvokeExample
+prefer :/qt/qml/InvokeExample/
+Button 1.0 Button.qml
+MainView 1.0 MainView.qml
+SubView 1.0 SubView.qml
diff --git a/examples/scxml/invoke/statemachine.scxml b/examples/scxml/invoke/statemachine.scxml
new file mode 100644
index 0000000..bd31dca
--- /dev/null
+++ b/examples/scxml/invoke/statemachine.scxml
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+-->
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+ name="DirectionsStateMachine"
+ initial="anyplace"
+>
+ <state id="anyplace">
+ <transition event="goNowhere" target="nowhere"/>
+ <transition event="goSomewhere" target="somewhere"/>
+
+ <state id="nowhere"/>
+ <state id="somewhere">
+ <invoke type="http://www.w3.org/TR/scxml/">
+ <content>
+ <scxml name="anywhere" version="1.0">
+ <state id="here">
+ <transition event="goThere" target="there"/>
+ </state>
+ <state id="there">
+ <transition event="goHere" target="here"/>
+ </state>
+ </scxml>
+ </content>
+ </invoke>
+ </state>
+ </state>
+</scxml>
diff --git a/examples/scxml/mediaplayer-common/Mediaplayer.qml b/examples/scxml/mediaplayer-common/Mediaplayer.qml
deleted file mode 100644
index 42a948a..0000000
--- a/examples/scxml/mediaplayer-common/Mediaplayer.qml
+++ /dev/null
@@ -1,142 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.5
-import QtQuick.Window 2.2
-import QtScxml 5.8
-
-Window {
- id: root
- property StateMachine stateMachine: scxmlLoader.stateMachine
- property alias source: scxmlLoader.source
-
- visible: true
- width: 750
- height: 350
- color: "white"
-
- ListView {
- id: theList
- width: parent.width / 2
- height: parent.height
- keyNavigationWraps: true
- highlightMoveDuration: 0
- focus: true
- model: ListModel {
- id: theModel
- ListElement { media: "Song 1" }
- ListElement { media: "Song 2" }
- ListElement { media: "Song 3" }
- }
- highlight: Rectangle { color: "lightsteelblue" }
- currentIndex: -1
- delegate: Rectangle {
- height: 40
- width: parent.width
- color: "transparent"
- MouseArea {
- anchors.fill: parent;
- onClicked: tap(index)
- }
- Text {
- id: txt
- anchors.fill: parent
- text: media
- verticalAlignment: Text.AlignVCenter
- }
- }
- }
-
- Text {
- id: theLog
- anchors.left: theList.right
- anchors.top: theText.bottom
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- }
-
- Text {
- id: theText
- anchors.left: theList.right
- anchors.right: parent.right;
- anchors.top: parent.top
- text: "Stopped"
- color: stateMachine.playing ? "green" : "red"
- }
-
- StateMachineLoader {
- id: scxmlLoader
- }
-
- EventConnection {
- stateMachine: root.stateMachine
- events: ["playbackStarted", "playbackStopped"]
- onOccurred: {
- var media = event.data.media;
- if (event.name === "playbackStarted") {
- theText.text = "Playing '" + media + "'";
- theLog.text = theLog.text + "\nplaybackStarted with data: "
- + JSON.stringify(event.data);
- } else if (event.name === "playbackStopped") {
- theText.text = "Stopped '" + media + "'";
- theLog.text = theLog.text + "\nplaybackStopped with data: "
- + JSON.stringify(event.data);
- }
- }
- }
-
- // Submit tap event to state machine.
- // "tap" toggles playing state of the current media.
- function tap(idx) {
- var media = theModel.get(idx).media;
- var data = { "media": media };
- stateMachine.submitEvent("tap", data);
- }
-}
diff --git a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc
deleted file mode 100644
index 711a32f..0000000
--- a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-compiling.qdocinc
+++ /dev/null
@@ -1,17 +0,0 @@
- \section1 Compiling the State Machine
-
- We link against the Qt SCXML module by adding the following line to the
- \e .pro file:
-
- \quotefromfile mediaplayer-widgets-static/mediaplayer-widgets-static.pro
- \skipto QT
- \printline scxml
-
- We then specify the state machine to compile:
-
- \skipto STATECHARTS
- \printline scxml
-
- The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
- \e statemachine.h and \e statemachine.cpp, and to add them to the \c HEADERS
- and \c SOURCES variables for compilation.
diff --git a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc
deleted file mode 100644
index 98b33c7..0000000
--- a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-ecmascript-data-model.qdocinc
+++ /dev/null
@@ -1,9 +0,0 @@
- \section1 Using the ECMAScript Data Model
-
- We specify the data model as a value of the \e datamodel attribute of the
- \c <scxml> element in \e mediaplayer-common/mediaplayer.scxml:
-
- \quotefromfile mediaplayer-common/mediaplayer.scxml
- \skipto scxml
- \printuntil </datamodel>
-
diff --git a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-widgets-connecting-to-states.qdocinc b/examples/scxml/mediaplayer-common/doc/src/mediaplayer-widgets-connecting-to-states.qdocinc
deleted file mode 100644
index 207f47d..0000000
--- a/examples/scxml/mediaplayer-common/doc/src/mediaplayer-widgets-connecting-to-states.qdocinc
+++ /dev/null
@@ -1,16 +0,0 @@
- \section1 Connecting to States
-
- The media player state machine will send out events when users tap a control
- and when playback starts or stops, as specified in the SCXML file:
-
- \quotefromfile mediaplayer-common/mediaplayer.scxml
- \skipto <state
- \printuntil !==
- \printuntil </state
-
- To be notified when a state machine sends out an event, we connect to the
- corresponding signals:
-
- \quotefromfile mediaplayer-common/mainwindow.cpp
- \skipto connectToEvent
- \printuntil playbackStopped
diff --git a/examples/scxml/mediaplayer-common/mainwindow.cpp b/examples/scxml/mediaplayer-common/mainwindow.cpp
deleted file mode 100644
index cedb072..0000000
--- a/examples/scxml/mediaplayer-common/mainwindow.cpp
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "mainwindow.h"
-#include "ui_mainwindow.h"
-
-#include <QScxmlStateMachine>
-#include <QStringListModel>
-
-QT_USE_NAMESPACE
-
-MainWindow::MainWindow(QScxmlStateMachine *stateMachine, QWidget *parent) :
- QWidget(parent),
- ui(new Ui::MainWindow)
-{
- ui->setupUi(this);
-
- auto model = new QStringListModel(QStringList() << QStringLiteral("song 1")
- << QStringLiteral("song 2")
- << QStringLiteral("song 3"),
- this);
- ui->mediaListView->setModel(model);
-
- connect(ui->mediaListView, &QAbstractItemView::clicked,
- [model, stateMachine](const QModelIndex &index) {
- QVariantMap data;
- data.insert(QStringLiteral("media"),
- model->data(index, Qt::EditRole).toString());
- stateMachine->submitEvent("tap", data);
- });
-
- stateMachine->connectToEvent("playbackStarted", this, &MainWindow::started);
- stateMachine->connectToEvent("playbackStopped", this, &MainWindow::stopped);
-}
-
-MainWindow::~MainWindow()
-{
- delete ui;
-}
-
-void MainWindow::started(const QScxmlEvent &event)
-{
- const QString media = event.data().toMap().value("media").toString();
- ui->logText->appendPlainText(QStringLiteral(
- "call on slot started with media '%1'").arg(media));
- ui->statusLabel->setText(QStringLiteral("Playing %1").arg(media));
-}
-
-void MainWindow::stopped(const QScxmlEvent &event)
-{
- const QString media = event.data().toMap().value("media").toString();
- ui->logText->appendPlainText(QStringLiteral(
- "call on slot stopped with media '%1'").arg(media));
- ui->statusLabel->setText(QStringLiteral("Stopped"));
-}
diff --git a/examples/scxml/mediaplayer-common/mainwindow.h b/examples/scxml/mediaplayer-common/mainwindow.h
deleted file mode 100644
index 59ae27a..0000000
--- a/examples/scxml/mediaplayer-common/mainwindow.h
+++ /dev/null
@@ -1,83 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef MAINWINDOW_H
-#define MAINWINDOW_H
-
-#include <QWidget>
-
-QT_BEGIN_NAMESPACE
-namespace Ui {
-class MainWindow;
-}
-
-class QScxmlEvent;
-class QScxmlStateMachine;
-
-QT_END_NAMESPACE
-
-class MainWindow : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit MainWindow(QScxmlStateMachine *stateMachine,
- QWidget *parent = nullptr);
- ~MainWindow();
-
-private slots:
- void started(const QScxmlEvent &event);
- void stopped(const QScxmlEvent &event);
-
-private:
- QT_PREPEND_NAMESPACE(Ui::MainWindow) *ui;
-};
-
-#endif // MAINWINDOW_H
diff --git a/examples/scxml/mediaplayer-common/mainwindow.ui b/examples/scxml/mediaplayer-common/mainwindow.ui
deleted file mode 100644
index 2e9e839..0000000
--- a/examples/scxml/mediaplayer-common/mainwindow.ui
+++ /dev/null
@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QWidget" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>750</width>
- <height>350</height>
- </rect>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0" rowspan="2">
- <widget class="QListView" name="mediaListView"/>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="statusLabel">
- <property name="text">
- <string>Stopped</string>
- </property>
- </widget>
- </item>
- <item row="1" column="1">
- <widget class="QPlainTextEdit" name="logText"/>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/examples/scxml/mediaplayer-common/mediaplayer.scxml b/examples/scxml/mediaplayer-common/mediaplayer.scxml
deleted file mode 100644
index ca68039..0000000
--- a/examples/scxml/mediaplayer-common/mediaplayer.scxml
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="MediaPlayerStateMachine"
- initial="stopped"
- datamodel="ecmascript"
->
- <datamodel>
- <data id="media"/>
- </datamodel>
-
- <script>
- function isValidMedia() {
- var m = _event.data.media
- return (m + "").length > 0
- }
- </script>
-
- <state id="stopped">
- <transition event="tap" cond="isValidMedia()" target="playing"/>
- </state>
-
- <state id="playing">
- <onentry>
- <assign location="media" expr="_event.data.media"/>
- <send event="playbackStarted">
- <param name="media" expr="media"/>
- </send>
- </onentry>
-
- <onexit>
- <send event="playbackStopped">
- <param name="media" expr="media"/>
- </send>
- </onexit>
-
- <transition event="tap" cond="!isValidMedia() || media === _event.data.media" target="stopped"/>
- <transition event="tap" cond="isValidMedia() &amp;&amp; media !== _event.data.media" target="playing"/>
- </state>
-</scxml>
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml
deleted file mode 100644
index 4ca810e..0000000
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml
+++ /dev/null
@@ -1,83 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<scxml
- xmlns="http://www.w3.org/2005/07/scxml"
- version="1.0"
- name="MediaPlayerStateMachine"
- initial="stopped"
- datamodel="cplusplus:TheDataModel:thedatamodel.h"
->
- <state id="stopped">
- <transition event="tap" cond="isValidMedia()" target="playing"/>
- </state>
-
- <state id="playing">
- <onentry>
- <script>
- media = eventData().value(QStringLiteral(&quot;media&quot;)).toString();
- </script>
- <send event="playbackStarted">
- <param name="media" expr="media"/>
- </send>
- </onentry>
-
- <onexit>
- <send event="playbackStopped">
- <param name="media" expr="media"/>
- </send>
- </onexit>
-
- <transition event="tap" cond="!isValidMedia() || media == eventData().value(QStringLiteral(&quot;media&quot;))" target="stopped"/>
- <transition event="tap" cond="isValidMedia() &amp;&amp; media != eventData().value(QStringLiteral(&quot;media&quot;))" target="playing"/>
- </state>
-</scxml>
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.cpp b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.cpp
deleted file mode 100644
index e0d97b1..0000000
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.cpp
+++ /dev/null
@@ -1,69 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-#include "mediaplayer-cppdatamodel.h"
-#include "thedatamodel.h"
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
-
- qmlRegisterType<TheDataModel>("MediaPlayerDataModel", 1, 0, "MediaPlayerDataModel");
- qmlRegisterType<MediaPlayerStateMachine>("MediaPlayerStateMachine", 1, 0, "MediaPlayerStateMachine");
-
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:///mediaplayer-qml-cppdatamodel.qml")));
-
- return app.exec();
-}
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro
deleted file mode 100644
index 2d21a37..0000000
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.pro
+++ /dev/null
@@ -1,17 +0,0 @@
-TEMPLATE = app
-
-QT += qml scxml
-CONFIG += c++11
-
-SOURCES += mediaplayer-qml-cppdatamodel.cpp \
- thedatamodel.cpp
-
-HEADERS += thedatamodel.h
-
-RESOURCES += mediaplayer-qml-cppdatamodel.qrc
-
-STATECHARTS = mediaplayer-cppdatamodel.scxml
-
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-qml-cppdatamodel
-INSTALLS += target
-
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.qml b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.qml
deleted file mode 100644
index b1aacb1..0000000
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.qml
+++ /dev/null
@@ -1,63 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import MediaPlayerStateMachine 1.0
-import MediaPlayerDataModel 1.0
-
-Mediaplayer {
- MediaPlayerDataModel {
- id: model
- }
-
- stateMachine: MediaPlayerStateMachine {
- onDataModelChanged: start()
- dataModel: model
- }
-}
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.qrc b/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.qrc
deleted file mode 100644
index 0de7eb7..0000000
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/mediaplayer-qml-cppdatamodel.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file alias="Mediaplayer.qml">../mediaplayer-common/Mediaplayer.qml</file>
- <file>mediaplayer-qml-cppdatamodel.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/thedatamodel.cpp b/examples/scxml/mediaplayer-qml-cppdatamodel/thedatamodel.cpp
deleted file mode 100644
index 0a3e200..0000000
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/thedatamodel.cpp
+++ /dev/null
@@ -1,64 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "thedatamodel.h"
-
-#include <QScxmlEvent>
-
-bool TheDataModel::isValidMedia() const
-{
- QString eventMedia = eventData().value(QStringLiteral("media")).toString();
- return eventMedia.size() > 0;
-}
-
-QVariantMap TheDataModel::eventData() const
-{
- return scxmlEvent().data().value<QVariantMap>();
-}
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/thedatamodel.h b/examples/scxml/mediaplayer-qml-cppdatamodel/thedatamodel.h
deleted file mode 100644
index e7a0e79..0000000
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/thedatamodel.h
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef THEDATAMODEL_H
-#define THEDATAMODEL_H
-
-#include "qscxmlcppdatamodel.h"
-
-class TheDataModel: public QScxmlCppDataModel
-{
- Q_OBJECT
- Q_SCXML_DATAMODEL
-
-private:
- bool isValidMedia() const;
- QVariantMap eventData() const;
-
- QString media;
-};
-
-#endif // THEDATAMODEL_H
diff --git a/examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc b/examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc
deleted file mode 100644
index 22d3ea2..0000000
--- a/examples/scxml/mediaplayer-qml-dynamic/doc/src/mediaplayer-qml-dynamic.qdoc
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example mediaplayer-qml-dynamic
- \title Qt SCXML Media Player QML Example (Dynamic)
- \ingroup examples-qtscxml
-
- \brief A Qt Quick application that sends data to and receives it from a
- dynamically loaded ECMAScript data model.
-
- \image mediaplayer.png
-
- \e {Media Player QML Example (Dynamic)} demonstrates how to access data from
- a dynamically loaded ECMAScript data model.
-
- The UI is created using Qt Quick.
-
- \include examples-run.qdocinc
-
- \include mediaplayer-ecmascript-data-model.qdocinc
-
- \section1 Dynamically Loading the State Machine
-
- We link against the Qt SCXML module by adding the following line to the
- example \e .pro file:
-
- \quotefromfile mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro
- \skipto QT
- \printline scxml
-
- We dynamically create the state machine in
- \e mediaplayer-common\Mediaplayer.qml:
-
- \quotefromfile mediaplayer-common/Mediaplayer.qml
- \skipto import QtScxml
- \printuntil scxmlLoader.source
-*/
diff --git a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.cpp b/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.cpp
deleted file mode 100644
index dce11c7..0000000
--- a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
-
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:///mediaplayer-qml-dynamic.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
-
diff --git a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro b/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro
deleted file mode 100644
index f032bba..0000000
--- a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.pro
+++ /dev/null
@@ -1,12 +0,0 @@
-TEMPLATE = app
-
-QT += qml scxml
-CONFIG += c++11
-
-SOURCES += mediaplayer-qml-dynamic.cpp
-
-RESOURCES += mediaplayer-qml-dynamic.qrc
-
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-qml-dynamic
-INSTALLS += target
-
diff --git a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml b/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml
deleted file mode 100644
index 864a76f..0000000
--- a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qml
+++ /dev/null
@@ -1,53 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-Mediaplayer {
- source: "qrc:///mediaplayer.scxml"
-}
diff --git a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qrc b/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qrc
deleted file mode 100644
index 0c7c40b..0000000
--- a/examples/scxml/mediaplayer-qml-dynamic/mediaplayer-qml-dynamic.qrc
+++ /dev/null
@@ -1,7 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file alias="Mediaplayer.qml">../mediaplayer-common/Mediaplayer.qml</file>
- <file>mediaplayer-qml-dynamic.qml</file>
- <file alias="mediaplayer.scxml">../mediaplayer-common/mediaplayer.scxml</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/mediaplayer-qml-static/doc/src/mediaplayer-qml-static.qdoc b/examples/scxml/mediaplayer-qml-static/doc/src/mediaplayer-qml-static.qdoc
deleted file mode 100644
index 4232ff1..0000000
--- a/examples/scxml/mediaplayer-qml-static/doc/src/mediaplayer-qml-static.qdoc
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example mediaplayer-qml-static
- \title Qt SCXML Media Player QML Example (Static)
- \ingroup examples-qtscxml
-
- \brief A Qt Quick application that sends data to and receives it from a
- compiled ECMAScript data model.
-
- \image mediaplayer.png
-
- \e {Media Player QML Example (Static)} demonstrates how to access data from
- an ECMAScript data model that is compiled into a C++ class.
-
- The UI is created using Qt Quick.
-
- \include examples-run.qdocinc
-
- \include mediaplayer-ecmascript-data-model.qdocinc
-
- \include mediaplayer-compiling.qdocinc
-
- \section1 Instantiating the State Machine
-
- We instantiate the generated \c MediaPlayerStateMachine class in
- \e mediaplayer-qml-static.cpp:
-
- \quotefromfile mediaplayer-qml-static/mediaplayer-qml-static.cpp
- \skipto mediaplayer.h
- \printuntil }
-*/
diff --git a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.cpp b/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.cpp
deleted file mode 100644
index 6dd263e..0000000
--- a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-#include "mediaplayer.h"
-
-int main(int argc, char *argv[])
-{
- QGuiApplication app(argc, argv);
-
- qmlRegisterType<MediaPlayerStateMachine>("MediaPlayerStateMachine", 1, 0, "MediaPlayerStateMachine");
-
- QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:///mediaplayer-qml-static.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
-
- return app.exec();
-}
-
diff --git a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro b/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro
deleted file mode 100644
index d8af563..0000000
--- a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.pro
+++ /dev/null
@@ -1,14 +0,0 @@
-TEMPLATE = app
-
-QT += qml scxml
-CONFIG += c++11
-
-SOURCES += mediaplayer-qml-static.cpp
-
-RESOURCES += mediaplayer-qml-static.qrc
-
-STATECHARTS = ../mediaplayer-common/mediaplayer.scxml
-
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-qml-static
-INSTALLS += target
-
diff --git a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.qml b/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.qml
deleted file mode 100644
index 170b938..0000000
--- a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.qml
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import MediaPlayerStateMachine 1.0
-
-Mediaplayer {
- stateMachine: MediaPlayerStateMachine {
- running: true
- }
-}
diff --git a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.qrc b/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.qrc
deleted file mode 100644
index e65d449..0000000
--- a/examples/scxml/mediaplayer-qml-static/mediaplayer-qml-static.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file alias="Mediaplayer.qml">../mediaplayer-common/Mediaplayer.qml</file>
- <file>mediaplayer-qml-static.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc b/examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc
deleted file mode 100644
index 59ec84f..0000000
--- a/examples/scxml/mediaplayer-widgets-dynamic/doc/src/mediaplayer-widgets-dynamic.qdoc
+++ /dev/null
@@ -1,65 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example mediaplayer-widgets-dynamic
- \title Qt SCXML Media Player Example (Dynamic)
- \ingroup examples-qtscxml
-
- \brief A widget-based application that sends data to and receives it from a
- dynamically loaded ECMAScript data model.
-
- \image mediaplayer.png
-
- \e {Media Player Example (Dynamic)} demonstrates how to access data from a
- dynamically loaded ECMAScript data model.
-
- The UI is created using Qt Widgets.
-
- \include examples-run.qdocinc
-
- \include mediaplayer-ecmascript-data-model.qdocinc
-
- \section1 Dynamically Loading the State Machine
-
- We link against the Qt SCXML module by adding the following line to the
- example \e .pro file:
-
- \quotefromfile mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.pro
- \skipto QT
- \printuntil scxml
-
- \quotefromfile mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp
-
- We dynamically create and instantiate the state machine in
- \e mediaplayer-wigdets-dynamic/mediaplayer-widgets-dynamic.cpp:
-
- \skipto mainwindow.h
- \printuntil /^\}/
-
- \include mediaplayer-widgets-connecting-to-states.qdocinc
-*/
diff --git a/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp b/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp
deleted file mode 100644
index edc62e7..0000000
--- a/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.cpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "../mediaplayer-common/mainwindow.h"
-
-#include <QApplication>
-#include <QScxmlStateMachine>
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
-
- auto machine = QScxmlStateMachine::fromFile(
- QStringLiteral(":mediaplayer.scxml"));
- MainWindow mainWindow(machine);
- machine->setParent(&mainWindow);
-
- machine->start();
- mainWindow.show();
- return app.exec();
-}
diff --git a/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.pro b/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.pro
deleted file mode 100644
index eb2949a..0000000
--- a/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer-widgets-dynamic.pro
+++ /dev/null
@@ -1,21 +0,0 @@
-QT += widgets scxml
-requires(qtConfig(listview))
-
-CONFIG += c++11
-
-SOURCES += \
- mediaplayer-widgets-dynamic.cpp \
- ../mediaplayer-common/mainwindow.cpp
-
-FORMS += \
- ../mediaplayer-common/mainwindow.ui
-
-HEADERS += \
- ../mediaplayer-common/mainwindow.h
-
-RESOURCES += \
- mediaplayer.qrc
-
-# install
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-widgets-dynamic
-INSTALLS += target
diff --git a/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer.qrc b/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer.qrc
deleted file mode 100644
index aecd468..0000000
--- a/examples/scxml/mediaplayer-widgets-dynamic/mediaplayer.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file alias="mediaplayer.scxml">../mediaplayer-common/mediaplayer.scxml</file>
- </qresource>
-</RCC>
-
diff --git a/examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc b/examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc
deleted file mode 100644
index dfa1230..0000000
--- a/examples/scxml/mediaplayer-widgets-static/doc/src/mediaplayer-widgets-static.qdoc
+++ /dev/null
@@ -1,59 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example mediaplayer-widgets-static
- \title Qt SCXML Media Player Example (Static)
- \ingroup examples-qtscxml
-
- \brief A widget-based application that sends data to and receives it from a
- compiled ECMAScript data model.
-
- \image mediaplayer.png
-
- \e {Media Player Example (Static)} demonstrates how to access data from an
- ECMAScript data model that is compiled into a C++ class.
-
- The UI is created using Qt Widgets.
-
- \include examples-run.qdocinc
-
- \include mediaplayer-ecmascript-data-model.qdocinc
-
- \include mediaplayer-compiling.qdocinc
-
- \section1 Instantiating the State Machine
-
- We instantiate the generated \c MediaPlayerStateMachine class in
- \e {mediaplayer-widgets-static.cpp}:
-
- \quotefromfile mediaplayer-widgets-static/mediaplayer-widgets-static.cpp
- \skipto mediaplayer.h
- \printuntil MainWindow mainWindow
-
- \include mediaplayer-widgets-connecting-to-states.qdocinc
-*/
diff --git a/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp b/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp
deleted file mode 100644
index 6a049d9..0000000
--- a/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "mediaplayer.h"
-#include "../mediaplayer-common/mainwindow.h"
-
-#include <QApplication>
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
-
- MediaPlayerStateMachine machine;
- MainWindow mainWindow(&machine);
-
- machine.start();
- mainWindow.show();
- return app.exec();
-}
diff --git a/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro b/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro
deleted file mode 100644
index 8ea0076..0000000
--- a/examples/scxml/mediaplayer-widgets-static/mediaplayer-widgets-static.pro
+++ /dev/null
@@ -1,20 +0,0 @@
-QT += widgets scxml
-requires(qtConfig(listview))
-
-CONFIG += c++11
-
-STATECHARTS = ../mediaplayer-common/mediaplayer.scxml
-
-SOURCES += \
- mediaplayer-widgets-static.cpp \
- ../mediaplayer-common/mainwindow.cpp
-
-FORMS += \
- ../mediaplayer-common/mainwindow.ui
-
-HEADERS += \
- ../mediaplayer-common/mainwindow.h
-
-# install
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer-widgets-static
-INSTALLS += target
diff --git a/examples/scxml/mediaplayer/CMakeLists.txt b/examples/scxml/mediaplayer/CMakeLists.txt
new file mode 100644
index 0000000..7870054
--- /dev/null
+++ b/examples/scxml/mediaplayer/CMakeLists.txt
@@ -0,0 +1,52 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(mediaplayer LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/mediaplayer")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Scxml)
+
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(mediaplayerscxml
+ main.cpp
+)
+
+set_target_properties(mediaplayerscxml PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(mediaplayerscxml PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Scxml
+)
+
+# Statecharts:
+qt6_add_statecharts(mediaplayerscxml
+ mediaplayer.scxml
+)
+
+qt_add_qml_module(mediaplayerscxml
+ URI Mediaplayer
+ VERSION 1.0
+ QML_FILES
+ MainWindow.qml
+ SOURCES
+ mediaplayer-qml.h
+ thedatamodel.h thedatamodel.cpp
+)
+
+install(TARGETS mediaplayerscxml
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/mediaplayer/MainWindow.qml b/examples/scxml/mediaplayer/MainWindow.qml
new file mode 100644
index 0000000..de4f710
--- /dev/null
+++ b/examples/scxml/mediaplayer/MainWindow.qml
@@ -0,0 +1,105 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Window
+import QtScxml
+import Mediaplayer
+
+Window {
+ id: root
+
+ MediaPlayerDataModel {
+ id: model
+ }
+
+ MediaPlayerStateMachine {
+ id: stateMachine
+ onDataModelChanged: stateMachine.start()
+ dataModel: model
+ }
+
+ visible: true
+ width: 750
+ height: 350
+ color: "white"
+
+ ListView {
+ id: theList
+ width: parent.width / 2
+ height: parent.height
+ keyNavigationWraps: true
+ highlightMoveDuration: 0
+ focus: true
+ model: ListModel {
+ id: theModel
+ ListElement { media: "Song 1" }
+ ListElement { media: "Song 2" }
+ ListElement { media: "Song 3" }
+ }
+ highlight: Rectangle { color: "lightsteelblue" }
+ currentIndex: -1
+ delegate: Rectangle {
+ id: delegateRect
+ required property string media
+ required property int index
+ height: 40
+ width: parent.width
+ color: "transparent"
+ MouseArea {
+ anchors.fill: parent;
+ onClicked: root.tap(delegateRect.index)
+ }
+ Text {
+ id: txt
+ anchors.fill: parent
+ text: delegateRect.media
+ verticalAlignment: Text.AlignVCenter
+ }
+ }
+ }
+
+ Text {
+ id: theLog
+ anchors.left: theList.right
+ anchors.top: theText.bottom
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ }
+
+ Text {
+ id: theText
+ anchors.left: theList.right
+ anchors.right: parent.right;
+ anchors.top: parent.top
+ text: "Stopped"
+ color: stateMachine.playing ? "green" : "red"
+ }
+
+ EventConnection {
+ stateMachine: stateMachine
+ events: ["playbackStarted", "playbackStopped"]
+ onOccurred: (event)=> {
+ var media = event.data.media;
+ if (event.name === "playbackStarted") {
+ theText.text = "Playing '" + media + "'";
+ theLog.text = theLog.text + "\nplaybackStarted with data: "
+ + JSON.stringify(event.data);
+ } else if (event.name === "playbackStopped") {
+ theText.text = "Stopped '" + media + "'";
+ theLog.text = theLog.text + "\nplaybackStopped with data: "
+ + JSON.stringify(event.data);
+ }
+ }
+ }
+
+ // Submit tap event to state machine.
+ // "tap" toggles playing state of the current media.
+ function tap(idx) {
+ var media = theModel.get(idx).media;
+ var data = { "media": media };
+ stateMachine.submitEvent("tap", data);
+ }
+}
diff --git a/examples/scxml/mediaplayer-common/doc/images/mediaplayer.png b/examples/scxml/mediaplayer/doc/images/mediaplayer.png
index b4c051e..b4c051e 100644
--- a/examples/scxml/mediaplayer-common/doc/images/mediaplayer.png
+++ b/examples/scxml/mediaplayer/doc/images/mediaplayer.png
Binary files differ
diff --git a/examples/scxml/mediaplayer-qml-cppdatamodel/doc/src/mediaplayer-qml-cppdatamodel.qdoc b/examples/scxml/mediaplayer/doc/src/mediaplayer.qdoc
index 421e72e..9862948 100644
--- a/examples/scxml/mediaplayer-qml-cppdatamodel/doc/src/mediaplayer-qml-cppdatamodel.qdoc
+++ b/examples/scxml/mediaplayer/doc/src/mediaplayer.qdoc
@@ -1,40 +1,18 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \example mediaplayer-qml-cppdatamodel
- \title Qt SCXML Media Player QML Example (C++ Data Model)
+ \example mediaplayer
+ \title SCXML Media Player
+ \examplecategory {Data Processing & I/O}
\ingroup examples-qtscxml
+ \meta tag {state machine}
\image mediaplayer.png
\brief Sends data to and receives it from a C++ data model.
- \e {Media Player QML Example (C++ Data Model)} demonstrates how to access
+ \e {Media Player} demonstrates how to access
data from a C++ data model. The data model enables writing C++ code for
\e expr attributes and \c <script> elements. The \e {data part} of the data
model is backed by a subclass of QScxmlCppDataModel, for which the Qt SCXML
@@ -49,7 +27,7 @@
We specify the data model as a value of the \e datamodel attribute of the
\c <scxml> element in the SCXML file:
- \quotefromfile mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml
+ \quotefromfile mediaplayer/mediaplayer.scxml
\skipto scxml
\printuntil datamodel
@@ -57,7 +35,7 @@
\c {cplusplus:<class-name>:<classdef-header>}. Therefore, we need a file
called \e thedatamodel.h that contains a subclass of QScxmlCppDataModel:
- \quotefromfile mediaplayer-qml-cppdatamodel/thedatamodel.h
+ \quotefromfile mediaplayer/thedatamodel.h
\skipto qscxmlcppdatamodel.h
\printuntil Q_SCXML_DATAMODEL
@@ -70,14 +48,14 @@
In the SCXML file, we specify C++ statements in the \c <script> element and
use the \e expr attribute to access the data model:
- \quotefromfile mediaplayer-qml-cppdatamodel/mediaplayer-cppdatamodel.scxml
+ \quotefromfile mediaplayer/mediaplayer.scxml
\skipto state
\printuntil </state>
\printuntil </state>
The Qt SCXML compiler generates the various \c evaluateTo methods and
converts the expressions and scripts into lambdas inside those methods in
- \e mediaplayer-cppdatamodel.cpp:
+ \e mediaplayer.cpp:
\code
bool TheDataModel::evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) {
diff --git a/examples/scxml/mediaplayer/main.cpp b/examples/scxml/mediaplayer/main.cpp
new file mode 100644
index 0000000..66d9a75
--- /dev/null
+++ b/examples/scxml/mediaplayer/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtGui/qguiapplication.h>
+#include <QtQml/qqmlapplicationengine.h>
+
+int main(int argc, char *argv[])
+{
+ QGuiApplication app(argc, argv);
+
+ QQmlApplicationEngine engine;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
+ [](){ QCoreApplication::exit(EXIT_FAILURE); }, Qt::QueuedConnection);
+ engine.loadFromModule("Mediaplayer", "MainWindow");
+
+ return app.exec();
+}
diff --git a/examples/scxml/mediaplayer/mediaplayer-qml.h b/examples/scxml/mediaplayer/mediaplayer-qml.h
new file mode 100644
index 0000000..9d036b7
--- /dev/null
+++ b/examples/scxml/mediaplayer/mediaplayer-qml.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MEDIAPLAYER_QML
+#define MEDIAPLAYER_QML
+
+#include "mediaplayer.h"
+
+#include <QtQml/qqml.h>
+#include <QtCore/qobject.h>
+
+struct MediaPlayerStateMachineRegistration
+{
+ Q_GADGET
+ QML_FOREIGN(MediaPlayerStateMachine)
+ QML_NAMED_ELEMENT(MediaPlayerStateMachine)
+ QML_ADDED_IN_VERSION(1, 0)
+};
+
+#endif // MEDIAPLAYER_QML
diff --git a/examples/scxml/mediaplayer/mediaplayer.pro b/examples/scxml/mediaplayer/mediaplayer.pro
new file mode 100644
index 0000000..5aaf236
--- /dev/null
+++ b/examples/scxml/mediaplayer/mediaplayer.pro
@@ -0,0 +1,27 @@
+TEMPLATE = app
+
+QT += qml scxml
+CONFIG += c++11
+CONFIG += qmltypes
+
+SOURCES += main.cpp \
+ thedatamodel.cpp
+
+HEADERS += thedatamodel.h \
+ mediaplayer-qml.h
+
+QML_IMPORT_NAME = Mediaplayer
+QML_IMPORT_MAJOR_VERSION = 1
+
+qml_resources.files = \
+ qmldir \
+ MainWindow.qml
+
+qml_resources.prefix = /qt/qml/Mediaplayer
+
+RESOURCES += qml_resources
+
+STATECHARTS = mediaplayer.scxml
+
+target.path = $$[QT_INSTALL_EXAMPLES]/scxml/mediaplayer
+INSTALLS += target
diff --git a/examples/scxml/mediaplayer/mediaplayer.scxml b/examples/scxml/mediaplayer/mediaplayer.scxml
new file mode 100644
index 0000000..402f9d9
--- /dev/null
+++ b/examples/scxml/mediaplayer/mediaplayer.scxml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+-->
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+ name="MediaPlayerStateMachine"
+ initial="stopped"
+ datamodel="cplusplus:TheDataModel:thedatamodel.h"
+>
+ <state id="stopped">
+ <transition event="tap" cond="isValidMedia()" target="playing"/>
+ </state>
+
+ <state id="playing">
+ <onentry>
+ <script>
+ media = eventData().value(QStringLiteral(&quot;media&quot;)).toString();
+ </script>
+ <send event="playbackStarted">
+ <param name="media" expr="media"/>
+ </send>
+ </onentry>
+
+ <onexit>
+ <send event="playbackStopped">
+ <param name="media" expr="media"/>
+ </send>
+ </onexit>
+
+ <transition event="tap" cond="!isValidMedia() || media == eventData().value(QStringLiteral(&quot;media&quot;))" target="stopped"/>
+ <transition event="tap" cond="isValidMedia() &amp;&amp; media != eventData().value(QStringLiteral(&quot;media&quot;))" target="playing"/>
+ </state>
+</scxml>
diff --git a/examples/scxml/mediaplayer/qmldir b/examples/scxml/mediaplayer/qmldir
new file mode 100644
index 0000000..1a35a2a
--- /dev/null
+++ b/examples/scxml/mediaplayer/qmldir
@@ -0,0 +1,3 @@
+module Mediaplayer
+prefer :/qt/qml/Mediaplayer/
+MainWindow 1.0 MainWindow.qml
diff --git a/examples/scxml/mediaplayer/thedatamodel.cpp b/examples/scxml/mediaplayer/thedatamodel.cpp
new file mode 100644
index 0000000..ffff638
--- /dev/null
+++ b/examples/scxml/mediaplayer/thedatamodel.cpp
@@ -0,0 +1,19 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "thedatamodel.h"
+
+#include <QtScxml/qscxmlevent.h>
+
+using namespace Qt::Literals::StringLiterals;
+
+bool TheDataModel::isValidMedia() const
+{
+ QString eventMedia = eventData().value(u"media"_s).toString();
+ return eventMedia.size() > 0;
+}
+
+QVariantMap TheDataModel::eventData() const
+{
+ return scxmlEvent().data().value<QVariantMap>();
+}
diff --git a/examples/scxml/mediaplayer/thedatamodel.h b/examples/scxml/mediaplayer/thedatamodel.h
new file mode 100644
index 0000000..936deee
--- /dev/null
+++ b/examples/scxml/mediaplayer/thedatamodel.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef THEDATAMODEL_H
+#define THEDATAMODEL_H
+
+#include "qscxmlcppdatamodel.h"
+#include <QtQml/qqml.h>
+
+class TheDataModel: public QScxmlCppDataModel
+{
+ Q_OBJECT
+ Q_SCXML_DATAMODEL
+ QML_NAMED_ELEMENT(MediaPlayerDataModel)
+ QML_ADDED_IN_VERSION(1, 0)
+
+private:
+ bool isValidMedia() const;
+ QVariantMap eventData() const;
+
+ QString media;
+};
+
+#endif // THEDATAMODEL_H
diff --git a/examples/scxml/pinball/doc/images/pinball-statechart-global.png b/examples/scxml/pinball/doc/images/pinball-statechart-global.png
deleted file mode 100644
index 630c678..0000000
--- a/examples/scxml/pinball/doc/images/pinball-statechart-global.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/pinball/doc/images/pinball-statechart-guicontrol.png b/examples/scxml/pinball/doc/images/pinball-statechart-guicontrol.png
deleted file mode 100644
index b79e343..0000000
--- a/examples/scxml/pinball/doc/images/pinball-statechart-guicontrol.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/pinball/doc/images/pinball-statechart-internalstate.png b/examples/scxml/pinball/doc/images/pinball-statechart-internalstate.png
deleted file mode 100644
index 3bd9d1b..0000000
--- a/examples/scxml/pinball/doc/images/pinball-statechart-internalstate.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/pinball/doc/images/pinball-statechart-logicalstate.png b/examples/scxml/pinball/doc/images/pinball-statechart-logicalstate.png
deleted file mode 100644
index 7a3395a..0000000
--- a/examples/scxml/pinball/doc/images/pinball-statechart-logicalstate.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/pinball/doc/images/pinball-statechart-modestate.png b/examples/scxml/pinball/doc/images/pinball-statechart-modestate.png
deleted file mode 100644
index e8b6df9..0000000
--- a/examples/scxml/pinball/doc/images/pinball-statechart-modestate.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/pinball/doc/images/pinball-statechart-onstate.png b/examples/scxml/pinball/doc/images/pinball-statechart-onstate.png
deleted file mode 100644
index db9459c..0000000
--- a/examples/scxml/pinball/doc/images/pinball-statechart-onstate.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/pinball/doc/images/pinball-statechart-workflow.png b/examples/scxml/pinball/doc/images/pinball-statechart-workflow.png
deleted file mode 100644
index dfcf3f2..0000000
--- a/examples/scxml/pinball/doc/images/pinball-statechart-workflow.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/pinball/doc/images/pinball.png b/examples/scxml/pinball/doc/images/pinball.png
deleted file mode 100644
index bbd390e..0000000
--- a/examples/scxml/pinball/doc/images/pinball.png
+++ /dev/null
Binary files differ
diff --git a/examples/scxml/pinball/doc/src/pinball.qdoc b/examples/scxml/pinball/doc/src/pinball.qdoc
deleted file mode 100644
index 5cf9f1d..0000000
--- a/examples/scxml/pinball/doc/src/pinball.qdoc
+++ /dev/null
@@ -1,502 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-/*!
- \example pinball
- \title Qt SCXML Pinball Example
- \ingroup examples-qtscxml
- \brief Encapsulates the internal logic of an application in an SCXML file.
-
- \e {Pinball} demonstrates a clear separation between the user interface,
- which may be easily replaced, and the internal logic encapsulated in an
- SCXML file, which could also be used with another user interface.
-
- \include examples-run.qdocinc
-
- \section1 Pinball Features
-
- \image pinball.png Screenshot of the Pinball example
-
- The Pinball example mimics a pinball game. The targets on the pinball table
- are substituted by GUI controls, mainly by push buttons. Display elements,
- including current score, highscore, and targets' lights, are substituted by
- labels. Usually, the state of the targets' lights changes very often during
- a game: the lights get turned on or off permanently or they blink at varying
- speed indicating a game (or a certain target) entered a temporary state. The
- state of each target light is presented as an enabled or a disabled label.
- There is no real ball, but clicking a target's button represents hitting a
- real pinball target with a ball.
-
- Our pinball contains the following features:
- \list
- \li Initially and when the game ends, the pinball table
- enters \c offState. In that state, all lights on the table
- blink slowly (at intervals of 1 second).
- \li After clicking the \uicontrol START button, the pinball table
- enters \c onState. All lights are turned off and the
- pinball table is ready to be played.
- \li When the table is in \c onState and the players
- click the \uicontrol {BALL OUT} button, the game ends
- and enters \c offState. If the players' score is
- higher than the current highscore, the highscore is updated.
- \li The goal is to collect the \uicontrol JACKPOT. In order to do that,
- the players must hit all five \uicontrol CRAZY letters twice.
- They have unlimited time for hitting them for the first time.
- However, after they have collected all the letters for the first time,
- they enter the \c hurryState and must collect
- them again within 5 seconds. If the time has passed and
- the letters were not collected again, the players must
- start collecting the letters from scratch.
- \li Scores:
- \list
- \li 1.000 per letter hit when not in \c hurryState.
- \li 10.000 per letter hit when in \c hurryState.
- \li 100.000 bonus for all 5 letters when not in \c hurryState.
- \li 1.000.000 bonus for all 5 letters when in \c hurryState
- (\uicontrol JACKPOT).
- \endlist
- \li When not in \c hurryState, the letters already hit should blink
- at intermediate speed (500ms). Letters not hit yet should stay off.
- \li When in \c hurryState, the letters already hit should
- stay on. Letters not hit yet should blink fast (200ms).
- In addition, the \uicontrol HURRY light should blink at the same speed.
- \li When the jackpot gets collected, the \uicontrol JACKPOT light should
- stay on.
- \endlist
-
- \section1 SCXML Part: Internal Logic Description
-
- The \e pinball.scxml file describes the internal logic implemented for the
- pinball game. In this example, we have chosen the ECMAScript data model:
-
- \quotefromfile pinball/pinball.scxml
- \skipto scxml
- \printuntil ecmascript
-
- The ECMAScript data model enables declaring variables with initial values
- that can be modified later. We declare the \c "highscore" and \c "score"
- variables with the initial values of 0:
-
- \printuntil </datamodel>
-
- We define a root parallel state \c "global", with two child states,
- \c guiControl and \c internalState, which are also parallel. Because the top
- \c global state is parallel, all of its direct children are active when it
- is active. In this example, the role of \c global is to collect the child
- states and make them both active at a time.
-
- \image pinball-statechart-global.png
-
- \section2 Maintaining Light State
-
- The \c guiControl element is responsible for maintaining the current
- state of each light control that is visible on the pinball table.
- Each light has a corresponding state.
-
- \image pinball-statechart-guicontrol.png
-
- For example, the light of the letter
- \uicontrol C corresponds to the \c cLight state. Each light state has two
- child states indicating whether the light is on or off:
-
- \printuntil target="cLightOn"
- \printuntil /^\ {12}<\//
-
- As mentioned before, the \c guiControl state is always active, and since
- it is of parallel type, all its direct children are always active too.
- Therefore, the \c cLight state is always active. However,
- only one of its children, \c cLightOn or \c cLightOff, is active at a time.
- The same applies to the other children of the \c guiControl state.
- In addition, we define transitions between on and off substates. For example,
- whenever the active state is \c cLightOn and a \c turnOffC event is received,
- we change the active substate of \c cLight to \c cLightOff.
- Whenever the active state is \c cLightOff and we receive a \c turnOnC event,
- we change the active substate of \c cLight to \c cLightOn.
-
- In our application, we use instances of QLabel class in C++
- to represent real lights on the table. When the light transitions
- into the \e on or \e off state, we enable or disable the particular label
- accordingly. The connection between the state machine and the GUI
- part of the application will be shown in the \l {cpp}{C++ code} later on. For now,
- it is enough to realize that changes to active states inside
- the state machine will serve as the external interface of the state machine
- that the other parts of the application (such as the GUI part) can listen to.
-
- All of the mentioned events that switch the state of a light
- will be generated by this state machine inside the \c internalState
- in reaction to running timers or external triggers.
-
- \section2 Maintaining Game State
-
- The \c internalState state consists of two main parts: \c logicalState and \c workflow.
-
- \image pinball-statechart-internalstate.png
-
- The \c logicalState state holds the definitions for the modes that the game is able
- to go into and for the logical states of collected targets. The \c workflow state
- implements a generator for light blinking and calculates most of the new states
- the machine should go into depending on incoming events and on currently active states.
- As mentioned already, \c internalState is always active, and since
- it is of a parallel type, \c logicalState and \c workflow are always active too.
-
- \section2 Maintaining Logical State of Buttons
-
- The \c logicalState state consist of two parts: \c letterState and \c modeState.
-
- \image pinball-statechart-logicalstate.png
-
- As previously mentioned, \c logicalState is always active, and since
- it is of parallel type, the \c letterState and \c modeState children are always
- active too. Now let us look at the first part, the \c letterState, which contains
- one parallel \c lettersState:
-
- \quotefromfile pinball/pinball.scxml
- \skipto letterState
- \printuntil lettersState
- \printuntil letter.R
- \dots 28
- \skipto /^\ {24}<\//
- \printuntil letter.A
- \dots 28
- \skipto /^\ {24}<\//
- \printuntil letter.Z
- \dots 28
- \skipto /^\ {24}<\//
- \printuntil letter.Y
- \dots 28
- \skipto /^\ {24}<\//
- \printuntil /^\ {16}<\//
-
- The \c lettersState state maintains the logical state of the buttons pretending to
- be targets that were clicked by the players. The letter state for the letter
- \uicontrol C holds whether the target for the letter \uicontrol C was hit,
- while the light state for the letter \uicontrol C holds whether the light
- for the target for the letter \uicontrol C should be currently on or off.
- In a real pinball game, these states are usually orthogonal,
- which means that if you have not hit a target yet, the target is blinking,
- indicating that it is currently worth hitting. This blinking
- means that the light state switches between on and off at short intervals,
- while the target state is continouosly off, because it has not been hit yet.
- The author of a pinball table can decide that
- after a target is hit (that is, after the target state switches to on)
- the target's light is continuously turned off or on or the intervals between
- lights blinking become shorter or longer.
-
- As mentioned before, \c letterState is always active, which means
- that its only child \c lettersState should always be active too. However,
- there is one exception: for a short while the \c lettersState may
- end up being \e {not active}. This happens when the transition for
- \c lettersState is being performed. This transition is triggered when
- the \c resetLetters event occurs, and it instructs the state machine
- to exit \c lettersState and all its descendant states and reenter
- \c lettersState and set up all its descendant states with their initial states.
- In short, the \c resetLetters event resets the \c lettersState and all its
- descendant states to the default configuration.
-
- The \c lettersState contains five direct substates that
- correspond to five different letters. The content for other letters' states
- than C is not shown here, but it is analogous to the content for C's state.
-
- The \c {letter.C} state contains two substates reflecting its off and on states:
- \c cLetterOff and \c cLetterOn. The \c {letter.C} state inside its parallel
- parent \c lettersState is always active (under the condition that
- \c lettersState is active, as described before). However,
- only one of its child states is active at a time: \c cLetterOff or \c cLetterOn.
- The initial substate of the \c {letter.C} state is \c cLetterOff meaning
- that whenever the \c {letter.C} state is being activated (which happens
- initially and after the \c resetLetters event) its active
- substate will be set to \c cLetterOff.
-
- The \c cLetterOff state defines a transition, which will be triggered by
- the \c {cLetterTriggered} event. This transition activates \c cLetterOn,
- the other child of \c {letter.C}, only when the machine is in \c onState
- (that is, when the pinball game is running).
- The \c {cLetterTriggered} event is expected to be an event posted into the state machine
- from outside of the state machine. This event should be generated when
- the ball hits the letter \uicontrol C target. In our example we mimic
- it by the clicking the letter \uicontrol C button.
-
- The \c cLetterOn state is defined as a final state, which means that
- whenever this state is activated the \c {done.state.letter.C} event
- will be automatically posted by the state machine. This event will be used
- later for updating the current score.
-
- Moreover, when all \c lettersState children reach their final state,
- the state machine will automatically post the \c {done.state.lettersState} event.
- This event will be used later, too, for updating the current score
- and for turning on or off the hurry state.
-
- \section2 Maintaining Game Modes
-
- The \c modeState state consists of two substates, \c offState and \c onState.
-
- \image pinball-statechart-modestate.png
-
- The \c offState state describes what should happen before the pinball game
- is started and when it is over,
- while \c onState represents the logic appropriate for the active game.
-
- \quotefromfile pinball/pinball.scxml
- \skipto offState
- \printuntil /^\ {20}<\//
-
- When the pinball application starts or a game ends, the machine goes into
- \c offState. Entering that state invokes some actions, which are
- enclosed inside an \c <onentry> element. First, we update the \c highScore
- variable in case the current \c highScore value is less than current \c score value.
- This is being checked inside the \c "cond" attribute of the \c <if> element
- (note that we need to escape the "<" character with "&lt;").
- Even in the \c off state, we want to show the last reached score,
- so we do not clear it here; we will do that when we enter the \c on state.
- Next, we raise two events: \c resetLetters to logically reset
- all letters that might have been hit during the last game and \c update
- to immediately activate the blinking and updating of all lights.
- When the machine is in \c offState, it is ready to transition into the
- \c onState if only the \c startTriggered event occurs, which is described
- by the <transition> element. This event is expected to be generated externally
- after clicking the \uicontrol START button on the pinball table.
-
- \skipto onState
- \printuntil /^\ {20}<\//
-
- \section2 Game On
-
- When the state machine enters \c onState, it first clears the current score
- variable. The \c onState state is of the parallel type and has two direct child states:
- \c hurryState and \c jackpotState. They are active as long as
- their parent, \c onState, is active. Both \c hurryState and \c jackpotState
- contain two substates that reflect their off and on states.
- Only one substate of \c hurryState and one substate of \c jackpotState
- can be active at a time. Initially, the off substates are active.
-
- \image pinball-statechart-onstate.png
-
- Whenever we enter \c hurryStateOff or \c hurryStateOn, we generate the same
- two events we generate when entering the \c onState state: \c resetLetters and
- \c update. In addition, when we enter the \c hurryStateOn state, we send a delayed
- event, \c goToHurryOff, with a delay of five seconds, marked with \c hurryId.
- This means that after five seconds we just
- switch the state back to \c hurryStateOff without granting the bonus points.
- In this way, we implement the five-second hurry feature of the pinball table.
- We also define transitions from \c hurryStateOff to \c hurryStateOn when the
- \c goToHurryOn event occurs and from \c hurryStateOn to \c hurryStateOff
- when the \c goToHurryOff event occurs. When we exit the \c hurryStateOn
- state, we cancel the possibly pending delayed event that was marked with
- \c hurryId. This is important in case the five secons have not elapsed yet,
- but players have collected all the five letters in the hurry state. We then
- collect the jackpot and want the pending timer to finish.
-
- The substates of \c jackpotState generate the request to update the state
- of lights. The \c jackpotStateOff state defines the transition to \c jackpotStateOn
- when the \c goForJackpot event occurs. The opposite transition is not
- needed, because when the jackpot gets collected, the corresponding light
- remains lit until the end of game. When a new game starts, the \c jackpotState
- is entered again which causes its initial active substate to be
- \c jackpotStateOff.
-
- In addition, the \c onState state defines one transition in reaction to the
- \c ballOutTriggered event which instructs the machine to go into the \c offState.
- The \c ballOutTriggered event is expected to be an event posted into the state machine
- from outside of the state machine. This event should be generated when
- the ball gets out of playing area of the table. In our example we mimic
- it by the clicking \uicontrol {BALL OUT} button. Posting the event from outside of state
- machine will be shown in the \l{cpp}{C++ code} later on.
-
- \section2 Generating Blinking Lights
-
- The \c workflow state is responsible for generating the blinking lights. The
- generator is defined in its \c lightImpulseGenerator substate. In addition,
- it is responsible for reacting to events that have been posted so far from
- the other parts of the state machine.
-
- \quotefromfile pinball/pinball.scxml
- \skipto workflow
- \printuntil done.state.letter.*
- \dots 20
- \skipto /^\ {16}<\//
- \printuntil done.state.lettersState
- \dots 20
- \skipto /^\ {16}<\//
- \printuntil updateLights
- \dots 20
- \skipto /^\ {16}<\//
- \printuntil updateLightsAccordingToLettersState
- \dots 20
- \skipto /^\ {16}<\//
- \printuntil turnOnLights
- \dots 20
- \skipto /^\ {16}<\//
- \printuntil turnOffLights
- \dots 20
- \skipto /^\ {16}<\//
- \printuntil /^\ {12}<\//
-
- The \c lightImpulseGenerator contains two child states:
- \c lightImpulseOn and \c lightImpulseOff, with only one active at a time.
-
- \image pinball-statechart-workflow.png
-
- Whenever the delayed \c lightImpulse event is being delivered, it immediately
- causes the transition from \c lightImpluseOn into \c lightImpulseOff or vice versa,
- depending on the state the machine was in. In effect, the \c lightImpulseGenerator
- toggles between its on and off state. These transitions are defined inside
- \c lightImpulseGenerator, so it means that during this toggling the machine
- also exits \c lightImpulseGenerator and reenters it immediately afterwards.
- Entering \c lightImpulseGenerator causes the generation of the \c update event.
- The \c update event triggers a targetless transition and posts two other
- events: \c scheduleNewImpulse and \c updateLights. The first one,
- \c scheduleNewImpulse, returns back to the \c lightImpulseGenerator, which
- posts a delayed \c lightImpulse event. After the delay,
- the \c lightImpulse event gets delivered back to \c lightImpulseGenerator,
- which causes it to toggle its substate again. In this way, the machine
- enters into a cycle. The current delay of the \c lightImpulse
- event depends on the state in which the machine was in the time of posting
- the delayed event. If a \c scheduleNewImpulse event occurs on demand, before
- the next delayed \c lightImpulse event gets delivered, we cancel any
- possible pending events.
-
- \quotefromfile pinball/pinball.scxml
- \skipto workflow
- \skipto done.state.letter.*
- \printuntil /^\ {16}<\//
- \printuntil /^\ {16}<\//
-
- Whenever we receive the event the name of which matches the
- \c {done.state.letter.*}, we update the current score.
- When the machine enters the final substate of the \c {letter.C},
- it emits the \c {done.state.letter.C} event. The same happens for
- all other letters we have previously defined. We capture the events for all
- letters, that is why we have used an asterisk
- after a dot in the event name.
- The transition above is targetless, since we just
- listen for matching events and update the internal data accordingly
- without changing any active state. The new score is being
- increased by 1.000 or 10.000 points, depending on whether we currently are
- in \c hurryStateOff or \c hurryStateOn.
- After the score is updated, we generate the \c updateLights event
- in order to immediately update the letters' lights accordingly.
- We do not generate the \c update event here, since we do not want to toggle
- the light impulse now, but just update the lights according to
- the current impulse state.
-
- We also intercept the \c {done.state.lettersState} event,
- which is being generated when all the letters have been hit.
- Depending on which state we are currently in, we grant the players either
- a small bonus of 100.000 or a big one of 1.000.000 (jackpot).
- In addition, we toggle the \c hurryState substate by
- sending the \c goToHurryOn or \c goToHurryOff event.
- When all letters have been collected while in \c hurryStateOn,
- we also raise the \c goForJackpot event which instructs
- the machine to activate the \c jackpotStateOn.
-
- \skipto updateLights
- \printuntil /^\ {16}<\//
- \printuntil updateLightsAccordingToLettersState
- \printuntil /^\ {16}<\//
- \printuntil turnOnLights
- \printuntil /^\ {16}<\//
- \printuntil turnOffLights
- \printuntil /^\ {16}<\//
-
- When we receive the \c updateLights event, we first want to send a
- \c updateScore event outside of the state machine. We pass
- the current values of the \c highScore and \c score variables to the event.
- This event is received by the C++ part.
-
- Next, depending on whether we are in \c jackpotStateOn or \c jackpotStateOff,
- we send the \c turnOnJackpot or the \c turnOffJackpot event,
- which instructs the \c guiControl state to transition to
- \c jackpotLightOn or \c jackpotLightOff, respectively.
-
- When the machine is in \e idle state, (that is, in the off state)
- or when the game is on, but no interaction occurs,
- the \c updateLights event is delivered periodically
- during the game, each time with the \c lightImpulseOn or
- \c lightImpulseOff state toggled. Depending on the
- current state of the light impulse and on the active state (\c offState,
- \c hurryStateOff or \c hurryStateOn), we turn on or off all the lights
- according to the description of the pinball table.
-
- \section1 GUI Part: User Interface Description
-
- The GUI part of the application consists of a \e mainwindow.ui
- file which describes the static user interface of the game.
-
- \target cpp
- \section1 C++ Part: Glue GUI with SCXML
-
- The C++ part of the application consists of a
- \c MainWindow class which glues the GUI part with the SCXML part.
- The class is declared in \e mainwindow.h.
-
- \quotefromfile pinball/mainwindow.h
- \skipto MainWindow
- \printuntil };
-
- The \c MainWindow class holds the pointer to the
- \c {QScxmlStateMachine *m_machine} which is the state machine
- class automatically generated by Qt out of SCMXL file
- and the pointer to the \c {Ui::MainWindow *m_ui} which
- describes the GUI part. It also declares two helper methods.
-
- \quotefromfile pinball/mainwindow.cpp
- \skipto #include
- \printuntil /\}$/
-
- The constructor of the \c MainWindow class
- instantiates the GUI part of the application
- and stores the pointer to the passed \c QScxmlStateMachine.
- It also initializes the GUI part and glues the
- GUI part to the state machine by connecting
- their communication interfaces together.
-
- The \c initAndConnect() method connects
- the state with the corresponding GUI widget by
- binding its activity with the enabling of the widget,
- so that whenever the state is active, its corresponding
- widget is enabled and whenever the state is inactive,
- the widget is disabled. We do that for all lights, targets,
- and description labels.
-
- We also intercept the \c updateScore event sent by the state machine,
- in order to update the score displays with the values
- passed with the event.
-
- The info about hitting any GUI target needs to be passed
- to the state machine and we do that by connecting
- all target buttons' \c clicked signals to the lambda expressions
- which submit the corresponding event into the state machine.
-
- \quotefromfile pinball/main.cpp
- \skipto #include
- \printuntil /\}$/
-
- In the \c main() function in the \e main.cpp file, we instantiate the
- \c app application object, \c Pinball state machine,
- and \c MainWindow GUI class. We initialize and start the state machine,
- show the main window, and execute the application.
-*/
diff --git a/examples/scxml/pinball/main.cpp b/examples/scxml/pinball/main.cpp
deleted file mode 100644
index 4bf058f..0000000
--- a/examples/scxml/pinball/main.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "mainwindow.h"
-#include "pinball.h"
-
-#include <QApplication>
-
-int main(int argc, char **argv)
-{
- QApplication app(argc, argv);
-
- Pinball machine;
- MainWindow mainWindow(&machine);
-
- machine.start();
- mainWindow.show();
- return app.exec();
-}
diff --git a/examples/scxml/pinball/mainwindow.cpp b/examples/scxml/pinball/mainwindow.cpp
deleted file mode 100644
index e146e4d..0000000
--- a/examples/scxml/pinball/mainwindow.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "mainwindow.h"
-#include "ui_mainwindow.h"
-
-#include <QScxmlStateMachine>
-#include <QStringListModel>
-
-QT_USE_NAMESPACE
-
-MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
- QWidget(parent),
- m_ui(new Ui::MainWindow),
- m_machine(machine)
-{
- m_ui->setupUi(this);
-
- // lights
- initAndConnect(QLatin1String("cLightOn"), m_ui->cLabel);
- initAndConnect(QLatin1String("rLightOn"), m_ui->rLabel);
- initAndConnect(QLatin1String("aLightOn"), m_ui->aLabel);
- initAndConnect(QLatin1String("zLightOn"), m_ui->zLabel);
- initAndConnect(QLatin1String("yLightOn"), m_ui->yLabel);
- initAndConnect(QLatin1String("hurryLightOn"), m_ui->hurryLabel);
- initAndConnect(QLatin1String("jackpotLightOn"), m_ui->jackpotLabel);
- initAndConnect(QLatin1String("gameOverLightOn"), m_ui->gameOverLabel);
-
- // help labels
- initAndConnect(QLatin1String("offState"), m_ui->offStateLabel);
- initAndConnect(QLatin1String("hurryStateOff"), m_ui->normalStateLabel);
- initAndConnect(QLatin1String("hurryStateOn"), m_ui->hurryStateLabel);
- initAndConnect(QLatin1String("jackpotStateOn"), m_ui->jackpotStateLabel);
-
- // context enablement
- initAndConnect(QLatin1String("offState"), m_ui->startButton);
- initAndConnect(QLatin1String("onState"), m_ui->cButton);
- initAndConnect(QLatin1String("onState"), m_ui->rButton);
- initAndConnect(QLatin1String("onState"), m_ui->aButton);
- initAndConnect(QLatin1String("onState"), m_ui->zButton);
- initAndConnect(QLatin1String("onState"), m_ui->yButton);
- initAndConnect(QLatin1String("onState"), m_ui->ballOutButton);
-
- // datamodel update
- m_machine->connectToEvent("updateScore", [this] (const QScxmlEvent &event) {
- const QVariant data = event.data();
- const QString highScore = data.toMap().value("highScore").toString();
- m_ui->highScoreLabel->setText(highScore);
- const QString score = data.toMap().value("score").toString();
- m_ui->scoreLabel->setText(score);
- });
-
- // gui interaction
- connect(m_ui->cButton, &QAbstractButton::clicked,
- [this] { m_machine->submitEvent("cLetterTriggered");
- });
- connect(m_ui->rButton, &QAbstractButton::clicked,
- [this] { m_machine->submitEvent("rLetterTriggered");
- });
- connect(m_ui->aButton, &QAbstractButton::clicked,
- [this] { m_machine->submitEvent("aLetterTriggered");
- });
- connect(m_ui->zButton, &QAbstractButton::clicked,
- [this] { m_machine->submitEvent("zLetterTriggered");
- });
- connect(m_ui->yButton, &QAbstractButton::clicked,
- [this] { m_machine->submitEvent("yLetterTriggered");
- });
- connect(m_ui->startButton, &QAbstractButton::clicked,
- [this] { m_machine->submitEvent("startTriggered");
- });
- connect(m_ui->ballOutButton, &QAbstractButton::clicked,
- [this] { m_machine->submitEvent("ballOutTriggered");
- });
-}
-
-MainWindow::~MainWindow()
-{
- delete m_ui;
-}
-
-void MainWindow::initAndConnect(const QString &state, QWidget *widget)
-{
- widget->setEnabled(m_machine->isActive(state));
- m_machine->connectToState(state, widget, &QWidget::setEnabled);
-}
diff --git a/examples/scxml/pinball/mainwindow.h b/examples/scxml/pinball/mainwindow.h
deleted file mode 100644
index daceb6e..0000000
--- a/examples/scxml/pinball/mainwindow.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef MAINWINDOW_H
-#define MAINWINDOW_H
-
-#include <QWidget>
-
-QT_BEGIN_NAMESPACE
-namespace Ui {
-class MainWindow;
-}
-class QScxmlStateMachine;
-QT_END_NAMESPACE
-
-
-class MainWindow : public QWidget
-{
- Q_OBJECT
-
-public:
- explicit MainWindow(QScxmlStateMachine *machine, QWidget *parent = nullptr);
- ~MainWindow();
-
-private:
- void initAndConnect(const QString &state, QWidget *widget);
- QT_PREPEND_NAMESPACE(Ui::MainWindow) *m_ui;
- QScxmlStateMachine *m_machine;
-};
-
-#endif // MAINWINDOW_H
diff --git a/examples/scxml/pinball/mainwindow.ui b/examples/scxml/pinball/mainwindow.ui
deleted file mode 100644
index 601c23e..0000000
--- a/examples/scxml/pinball/mainwindow.ui
+++ /dev/null
@@ -1,663 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>MainWindow</class>
- <widget class="QWidget" name="MainWindow">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>476</width>
- <height>458</height>
- </rect>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0" colspan="5">
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QLabel" name="highScoreTextLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>HIGH SCORE:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="highScoreLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>0</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="scoreTextLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>SCORE:</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QLabel" name="scoreLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>0</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="5" rowspan="7">
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>1</width>
- <height>98</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QToolButton" name="startButton">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Minimum">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>^
-^
-^
-
-S
-T
-A
-R
-T
-
-^
-^
-^</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0" colspan="5">
- <widget class="QLabel" name="gameOverLabel">
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>255</red>
- <green>0</green>
- <blue>4</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>255</red>
- <green>0</green>
- <blue>4</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>190</red>
- <green>190</green>
- <blue>190</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="font">
- <font>
- <pointsize>20</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>GAME OVER</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="2" column="0" colspan="5">
- <widget class="QLabel" name="jackpotLabel">
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>55</red>
- <green>200</green>
- <blue>191</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>55</red>
- <green>200</green>
- <blue>191</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>190</red>
- <green>190</green>
- <blue>190</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="font">
- <font>
- <pointsize>20</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>JACKPOT!</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="3" column="0" colspan="5">
- <widget class="QLabel" name="hurryLabel">
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>67</red>
- <green>24</green>
- <blue>97</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>67</red>
- <green>24</green>
- <blue>97</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>190</red>
- <green>190</green>
- <blue>190</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="font">
- <font>
- <pointsize>20</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>HURRY!</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="4" column="0">
- <widget class="QLabel" name="cLabel">
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>7</red>
- <green>0</green>
- <blue>222</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>7</red>
- <green>0</green>
- <blue>222</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>190</red>
- <green>190</green>
- <blue>190</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="font">
- <font>
- <pointsize>20</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>C</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QLabel" name="rLabel">
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>6</red>
- <green>208</green>
- <blue>36</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>6</red>
- <green>208</green>
- <blue>36</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>190</red>
- <green>190</green>
- <blue>190</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="font">
- <font>
- <pointsize>20</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>R</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="4" column="2">
- <widget class="QLabel" name="aLabel">
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>238</red>
- <green>17</green>
- <blue>1</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>238</red>
- <green>17</green>
- <blue>1</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>190</red>
- <green>190</green>
- <blue>190</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="font">
- <font>
- <pointsize>20</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>A</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="4" column="3">
- <widget class="QLabel" name="zLabel">
- <property name="enabled">
- <bool>true</bool>
- </property>
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>230</red>
- <green>10</green>
- <blue>230</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>230</red>
- <green>10</green>
- <blue>230</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>190</red>
- <green>190</green>
- <blue>190</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="font">
- <font>
- <pointsize>20</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>Z</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="4" column="4">
- <widget class="QLabel" name="yLabel">
- <property name="palette">
- <palette>
- <active>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>239</red>
- <green>224</green>
- <blue>9</blue>
- </color>
- </brush>
- </colorrole>
- </active>
- <inactive>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>239</red>
- <green>224</green>
- <blue>9</blue>
- </color>
- </brush>
- </colorrole>
- </inactive>
- <disabled>
- <colorrole role="WindowText">
- <brush brushstyle="SolidPattern">
- <color alpha="255">
- <red>190</red>
- <green>190</green>
- <blue>190</blue>
- </color>
- </brush>
- </colorrole>
- </disabled>
- </palette>
- </property>
- <property name="font">
- <font>
- <pointsize>20</pointsize>
- <weight>75</weight>
- <bold>true</bold>
- </font>
- </property>
- <property name="text">
- <string>Y</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="5" column="0">
- <widget class="QPushButton" name="cButton">
- <property name="text">
- <string>C</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QPushButton" name="rButton">
- <property name="text">
- <string>R</string>
- </property>
- </widget>
- </item>
- <item row="5" column="2">
- <widget class="QPushButton" name="aButton">
- <property name="text">
- <string>A</string>
- </property>
- </widget>
- </item>
- <item row="5" column="3">
- <widget class="QPushButton" name="zButton">
- <property name="text">
- <string>Z</string>
- </property>
- </widget>
- </item>
- <item row="5" column="4">
- <widget class="QPushButton" name="yButton">
- <property name="text">
- <string>Y</string>
- </property>
- </widget>
- </item>
- <item row="6" column="0" colspan="5">
- <widget class="QPushButton" name="ballOutButton">
- <property name="text">
- <string>BALL OUT</string>
- </property>
- </widget>
- </item>
- <item row="7" column="0" colspan="6">
- <widget class="QLabel" name="offStateLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Press START to release the ball.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="8" column="0" colspan="6">
- <widget class="QLabel" name="normalStateLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Try to hit all CRAZY letters. Watch out for ball and avoid BALL OUT since the game will end.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="9" column="0" colspan="6">
- <widget class="QLabel" name="hurryStateLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>Now you need to hurry. Quickly hit all CRAZY letters again to gather the extra JACKPOT bonus. You have only 5 seconds. Otherwise you will need to start from scratch.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- <item row="10" column="0" colspan="6">
- <widget class="QLabel" name="jackpotStateLabel">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Preferred" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="text">
- <string>You did that! You have collected JACKPOT! Congratulations. Now you can continue game until you BALL OUT.</string>
- </property>
- <property name="wordWrap">
- <bool>true</bool>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/examples/scxml/pinball/pinball.pro b/examples/scxml/pinball/pinball.pro
deleted file mode 100644
index 549010d..0000000
--- a/examples/scxml/pinball/pinball.pro
+++ /dev/null
@@ -1,19 +0,0 @@
-QT += widgets scxml
-
-CONFIG += c++11
-
-STATECHARTS = pinball.scxml
-
-SOURCES += \
- main.cpp \
- mainwindow.cpp
-
-FORMS += \
- mainwindow.ui
-
-HEADERS += \
- mainwindow.h
-
-# install
-target.path = $$[QT_INSTALL_EXAMPLES]/scxml/pinball
-INSTALLS += target
diff --git a/examples/scxml/pinball/pinball.scxml b/examples/scxml/pinball/pinball.scxml
deleted file mode 100644
index 022f202..0000000
--- a/examples/scxml/pinball/pinball.scxml
+++ /dev/null
@@ -1,353 +0,0 @@
-<?xml version="1.0" ?>
-<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
--->
-<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
- name="Pinball" datamodel="ecmascript">
- <datamodel>
- <data id="highScore" expr="0"/>
- <data id="score" expr="0"/>
- </datamodel>
- <parallel id="global">
- <parallel id="guiControl">
- <state id="cLight">
- <state id="cLightOn">
- <transition event="turnOffC" target="cLightOff"/>
- </state>
- <state id="cLightOff">
- <transition event="turnOnC" target="cLightOn"/>
- </state>
- </state>
- <state id="rLight">
- <state id="rLightOn">
- <transition event="turnOffR" target="rLightOff"/>
- </state>
- <state id="rLightOff">
- <transition event="turnOnR" target="rLightOn"/>
- </state>
- </state>
- <state id="aLight">
- <state id="aLightOn">
- <transition event="turnOffA" target="aLightOff"/>
- </state>
- <state id="aLightOff">
- <transition event="turnOnA" target="aLightOn"/>
- </state>
- </state>
- <state id="zLight">
- <state id="zLightOn">
- <transition event="turnOffZ" target="zLightOff"/>
- </state>
- <state id="zLightOff">
- <transition event="turnOnZ" target="zLightOn"/>
- </state>
- </state>
- <state id="yLight">
- <state id="yLightOn">
- <transition event="turnOffY" target="yLightOff"/>
- </state>
- <state id="yLightOff">
- <transition event="turnOnY" target="yLightOn"/>
- </state>
- </state>
- <state id="hurryLight">
- <state id="hurryLightOn">
- <transition event="turnOffHurry" target="hurryLightOff"/>
- </state>
- <state id="hurryLightOff">
- <transition event="turnOnHurry" target="hurryLightOn"/>
- </state>
- </state>
- <state id="jackpotLight">
- <state id="jackpotLightOn">
- <transition event="turnOffJackpot" target="jackpotLightOff"/>
- </state>
- <state id="jackpotLightOff">
- <transition event="turnOnJackpot" target="jackpotLightOn"/>
- </state>
- </state>
- <state id="gameOverLight">
- <state id="gameOverLightOn">
- <transition event="turnOffGameOver" target="gameOverLightOff"/>
- </state>
- <state id="gameOverLightOff">
- <transition event="turnOnGameOver" target="gameOverLightOn"/>
- </state>
- </state>
- </parallel>
-
- <parallel id="internalState">
- <parallel id="logicalState">
- <state id="letterState">
- <parallel id="lettersState">
- <state id="letter.C">
- <state id="cLetterOff">
- <transition event="cLetterTriggered" cond="In('onState')" target="cLetterOn"/>
- </state>
- <final id="cLetterOn"/>
- </state>
- <state id="letter.R">
- <state id="rLetterOff">
- <transition event="rLetterTriggered" cond="In('onState')" target="rLetterOn"/>
- </state>
- <final id="rLetterOn"/>
- </state>
- <state id="letter.A">
- <state id="aLetterOff">
- <transition event="aLetterTriggered" cond="In('onState')" target="aLetterOn"/>
- </state>
- <final id="aLetterOn"/>
- </state>
- <state id="letter.Z">
- <state id="zLetterOff">
- <transition event="zLetterTriggered" cond="In('onState')" target="zLetterOn"/>
- </state>
- <final id="zLetterOn"/>
- </state>
- <state id="letter.Y">
- <state id="yLetterOff">
- <transition event="yLetterTriggered" cond="In('onState')" target="yLetterOn"/>
- </state>
- <final id="yLetterOn"/>
- </state>
- <transition event="resetLetters" target="lettersState"/>
- </parallel>
- </state>
- <state id="modeState">
- <state id="offState">
- <onentry>
- <if cond="highScore &lt; score">
- <assign location="highScore" expr="score"/>
- </if>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="startTriggered" target="onState"/>
- </state>
- <parallel id="onState">
- <onentry>
- <assign location="score" expr="0"/>
- </onentry>
- <state id="hurryState">
- <state id="hurryStateOff">
- <onentry>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOn" target="hurryStateOn"/>
- </state>
- <state id="hurryStateOn">
- <onentry>
- <send event="goToHurryOff" id="hurryId" delay="5s"/>
- <raise event="resetLetters"/>
- <raise event="update"/>
- </onentry>
- <transition event="goToHurryOff" target="hurryStateOff"/>
- <onexit>
- <cancel sendid="hurryId"/>
- </onexit>
- </state>
- </state>
- <state id="jackpotState">
- <state id="jackpotStateOff">
- <onentry>
- <raise event="update"/>
- </onentry>
- <transition event="goForJackpot" target="jackpotStateOn"/>
- </state>
- <state id="jackpotStateOn">
- <onentry>
- <raise event="update"/>
- </onentry>
- </state>
- </state>
- <transition event="ballOutTriggered" target="offState"/>
- </parallel>
- </state>
- </parallel>
-
- <state id="workflow">
- <state id="lightImpulseGenerator">
- <state id="lightImpulseOn"/>
- <state id="lightImpulseOff"/>
-
- <onentry>
- <raise event="update"/>
- </onentry>
-
- <transition event="scheduleNewImpulse">
- <cancel sendid="lightId"/>
- <if cond="In('offState')">
- <send event="lightImpulse" id="lightId" delay="1s"/>
- <elseif cond="In('hurryStateOff')"/>
- <send event="lightImpulse" id="lightId" delay="500ms"/>
- <else/>
- <send event="lightImpulse" id="lightId" delay="200ms"/>
- </if>
- </transition>
-
- <transition event="update">
- <raise event="scheduleNewImpulse"/>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="lightImpulse" cond="In('lightImpulseOn')" target="lightImpulseOff"/>
- <transition event="lightImpulse" cond="In('lightImpulseOff')" target="lightImpulseOn"/>
- </state>
-
- <transition event="done.state.letter.*">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 1000"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 10000"/>
- </if>
- <raise event="updateLights"/>
- </transition>
-
- <transition event="done.state.lettersState">
- <if cond="In('hurryStateOff')">
- <assign location="score" expr="score + 100000"/>
- <raise event="goToHurryOn"/>
- <elseif cond="In('hurryStateOn')"/>
- <assign location="score" expr="score + 1000000"/>
- <raise event="goToHurryOff"/>
- <raise event="goForJackpot"/>
- </if>
- </transition>
-
- <transition event="updateLights">
- <send event="updateScore">
- <param name="highScore" expr="highScore"/>
- <param name="score" expr="score"/>
- </send>
- <if cond="In('jackpotStateOn')">
- <raise event="turnOnJackpot"/>
- <else/>
- <raise event="turnOffJackpot"/>
- </if>
-
- <if cond="In('lightImpulseOn')">
- <if cond="In('offState')">
- <raise event="turnOnLights"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOnJackpot"/>
- <raise event="turnOnGameOver"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="updateLightsAccordingToLettersState"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- <else/>
- <raise event="turnOnLights"/>
- <raise event="turnOnHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- <else/>
- <if cond="In('offState')">
- <raise event="turnOffLights"/>
- <raise event="turnOffHurry"/>
- <raise event="turnOffJackpot"/>
- <elseif cond="In('hurryStateOff')"/>
- <raise event="turnOffLights"/>
- <else/>
- <raise event="updateLightsAccordingToLettersState"/>
- </if>
- <raise event="turnOffHurry"/>
- <raise event="turnOffGameOver"/>
- </if>
- </transition>
-
- <transition event="updateLightsAccordingToLettersState">
- <if cond="In('cLetterOn')">
- <raise event="turnOnC"/>
- <else/>
- <raise event="turnOffC"/>
- </if>
- <if cond="In('rLetterOn')">
- <raise event="turnOnR"/>
- <else/>
- <raise event="turnOffR"/>
- </if>
- <if cond="In('aLetterOn')">
- <raise event="turnOnA"/>
- <else/>
- <raise event="turnOffA"/>
- </if>
- <if cond="In('zLetterOn')">
- <raise event="turnOnZ"/>
- <else/>
- <raise event="turnOffZ"/>
- </if>
- <if cond="In('yLetterOn')">
- <raise event="turnOnY"/>
- <else/>
- <raise event="turnOffY"/>
- </if>
- </transition>
-
- <transition event="turnOnLights">
- <raise event="turnOnC"/>
- <raise event="turnOnR"/>
- <raise event="turnOnA"/>
- <raise event="turnOnZ"/>
- <raise event="turnOnY"/>
- </transition>
-
- <transition event="turnOffLights">
- <raise event="turnOffC"/>
- <raise event="turnOffR"/>
- <raise event="turnOffA"/>
- <raise event="turnOffZ"/>
- <raise event="turnOffY"/>
- </transition>
- </state>
- </parallel>
- </parallel>
-</scxml>
diff --git a/examples/scxml/scxml.pro b/examples/scxml/scxml.pro
index 2f80c60..d467fb4 100644
--- a/examples/scxml/scxml.pro
+++ b/examples/scxml/scxml.pro
@@ -3,24 +3,16 @@ TEMPLATE = subdirs
qtHaveModule(widgets) {
SUBDIRS += trafficlight-widgets-static
SUBDIRS += trafficlight-widgets-dynamic
- SUBDIRS += mediaplayer-widgets-static
- SUBDIRS += mediaplayer-widgets-dynamic
- SUBDIRS += calculator-widgets
- SUBDIRS += pinball
SUBDIRS += sudoku
}
qtHaveModule(quick) {
- SUBDIRS += calculator-qml
+ SUBDIRS += calculator
SUBDIRS += trafficlight-qml-static
SUBDIRS += trafficlight-qml-dynamic
SUBDIRS += trafficlight-qml-simple
- SUBDIRS += mediaplayer-qml-static
- SUBDIRS += mediaplayer-qml-dynamic
-
- SUBDIRS += mediaplayer-qml-cppdatamodel
- SUBDIRS += invoke-static
- SUBDIRS += invoke-dynamic
+ SUBDIRS += mediaplayer
+ SUBDIRS += invoke
}
SUBDIRS += ftpclient
diff --git a/examples/scxml/sudoku/CMakeLists.txt b/examples/scxml/sudoku/CMakeLists.txt
new file mode 100644
index 0000000..d3e80dd
--- /dev/null
+++ b/examples/scxml/sudoku/CMakeLists.txt
@@ -0,0 +1,57 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(sudoku LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/sudoku")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Scxml Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(sudoku
+ main.cpp
+ mainwindow.cpp mainwindow.h
+)
+
+set_target_properties(sudoku PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(sudoku PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::Scxml
+ Qt::Widgets
+)
+
+# Resources:
+set(sudoku_resource_files
+ "data/nearly-solved-sudoku.data"
+ "data/sudoku.data"
+ "sudoku.js"
+)
+
+qt6_add_resources(sudoku "sudoku"
+ PREFIX
+ "/"
+ FILES
+ ${sudoku_resource_files}
+)
+
+# Statecharts:
+qt6_add_statecharts(sudoku
+ sudoku.scxml
+)
+
+install(TARGETS sudoku
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/sudoku/doc/src/sudoku.qdoc b/examples/scxml/sudoku/doc/src/sudoku.qdoc
index c3d8971..45a2967 100644
--- a/examples/scxml/sudoku/doc/src/sudoku.qdoc
+++ b/examples/scxml/sudoku/doc/src/sudoku.qdoc
@@ -1,37 +1,14 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example sudoku
- \title Qt SCXML Sudoku Example
+ \title SCXML Sudoku
+ \examplecategory {Data Processing & I/O}
\ingroup examples-qtscxml
- \brief Presents the use of SCXML in a sudoku game.
+ \meta tag {state machine}
- \e {Sudoku} demonstrates how an SCXML file may be used in a game.
+ \brief Demonstrates the use of separate javascript file with SCXML.
\include examples-run.qdocinc
@@ -106,10 +83,10 @@
remains unchanged.
\row
\li \c undoStack
- \li Holds the history of players' moves. It is a vector of
+ \li Holds the history of players' moves. It is a list of
the cells' coordinates that were touched last. Each
new modification during a game adds a pair of
- x and y coordinates to that vector.
+ x and y coordinates to that list.
\endtable
The variables above are shared with the script helper functions
@@ -257,12 +234,12 @@
grid is properly solved. Since we need to check
each row, each column, and each 3x3 square, we define
the \c isOK() helper function. This function takes
- the vector of numbers and returns \c true if the passed vector
+ the list of numbers and returns \c true if the passed list
contains unique numbers and no number equals zero, meaning
there is no empty cell. The main loop of the \c isSolved() is invoked
- nine times. In every iteration, we construct three vectors of numbers
+ nine times. In every iteration, we construct three lists of numbers
representing a row, a column, and a square of the grid and call \c isOK()
- for them. When all 27 vectors are OK, the grid is solved properly
+ for them. When all 27 lists are OK, the grid is solved properly
and we return \c true.
Coming back to our SCXML file, in case \c isSolved()
diff --git a/examples/scxml/sudoku/main.cpp b/examples/scxml/sudoku/main.cpp
index c65efa3..8cd790d 100644
--- a/examples/scxml/sudoku/main.cpp
+++ b/examples/scxml/sudoku/main.cpp
@@ -1,57 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h"
#include "sudoku.h"
-#include <QApplication>
+#include <QtWidgets/qapplication.h>
int main(int argc, char **argv)
{
diff --git a/examples/scxml/sudoku/mainwindow.cpp b/examples/scxml/sudoku/mainwindow.cpp
index 8b521ae..63a8f25 100644
--- a/examples/scxml/sudoku/mainwindow.cpp
+++ b/examples/scxml/sudoku/mainwindow.cpp
@@ -1,68 +1,21 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "mainwindow.h"
-#include <QComboBox>
-#include <QDir>
-#include <QFile>
-#include <QGridLayout>
-#include <QLabel>
-#include <QScxmlStateMachine>
-#include <QStringListModel>
-#include <QTextStream>
-#include <QToolButton>
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qstringlistmodel.h>
+#include <QtCore/qtextstream.h>
+#include <QtWidgets/qcombobox.h>
+#include <QtWidgets/qgridlayout.h>
+#include <QtWidgets/qlabel.h>
+#include <QtWidgets/qtoolbutton.h>
+#include <QtScxml/qscxmlstatemachine.h>
static int Size = 9;
-QT_USE_NAMESPACE
+using namespace Qt::Literals::StringLiterals;
static QVariantList emptyRow()
{
@@ -80,11 +33,11 @@ static QVariantMap readSudoku(const QString &fileName)
const QString data = str.readAll();
QVariantList initRowsVariant;
- const QStringList rows = data.split(QLatin1Char('\n'));
+ const QStringList rows = data.split('\n'_L1);
for (int i = 0; i < Size; i++) {
if (i < rows.count()) {
QVariantList initRowVariant;
- const QStringList row = rows.at(i).split(QLatin1Char(','));
+ const QStringList row = rows.at(i).split(','_L1);
for (int j = 0; j < Size; j++) {
const int val = j < row.count()
? row.at(j).toInt() % (Size + 1) : 0;
@@ -97,7 +50,7 @@ static QVariantMap readSudoku(const QString &fileName)
}
QVariantMap dataVariant;
- dataVariant.insert(QStringLiteral("initState"), initRowsVariant);
+ dataVariant.insert(u"initState"_s, initRowsVariant);
return dataVariant;
}
@@ -106,8 +59,8 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
QWidget(parent),
m_machine(machine)
{
- const QVector<QToolButton *> initVector(Size, nullptr);
- m_buttons = QVector<QVector<QToolButton *> >(Size, initVector);
+ const QList<QToolButton *> initVector(Size, nullptr);
+ m_buttons = QList<QList<QToolButton *>>(Size, initVector);
QGridLayout *layout = new QGridLayout(this);
@@ -118,10 +71,10 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
QSizePolicy::Expanding);
layout->addWidget(button, i + i / 3, j + j / 3);
m_buttons[i][j] = button;
- connect(button, &QToolButton::clicked, [this, i, j] () {
+ connect(button, &QToolButton::clicked, this, [this, i, j]() {
QVariantMap data;
- data.insert(QStringLiteral("x"), i);
- data.insert(QStringLiteral("y"), j);
+ data.insert(u"x"_s, i);
+ data.insert(u"y"_s, j);
m_machine->submitEvent("tap", data);
});
}
@@ -145,8 +98,7 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
m_startButton->setText(tr("Start"));
layout->addWidget(m_startButton, Size + 3, 0, 1, 3);
- connect(m_startButton, &QAbstractButton::clicked,
- [this] {
+ connect(m_startButton, &QAbstractButton::clicked, this, [this]() {
if (m_machine->isActive("playing"))
m_machine->submitEvent("stop");
else
@@ -164,15 +116,14 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
m_undoButton->setEnabled(false);
layout->addWidget(m_undoButton, Size + 3, 8, 1, 3);
- connect(m_undoButton, &QAbstractButton::clicked,
- [this] {
+ connect(m_undoButton, &QAbstractButton::clicked, this, [this]() {
m_machine->submitEvent("undo");
});
m_chooser = new QComboBox(this);
layout->addWidget(m_chooser, Size + 4, 0, 1, 11);
- QDir dataDir(QLatin1String(":/data"));
+ QDir dataDir(":/data"_L1);
QFileInfoList sudokuFiles = dataDir.entryInfoList(QStringList()
<< "*.data");
for (const QFileInfo &sudokuFile : sudokuFiles) {
@@ -180,8 +131,7 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
sudokuFile.absoluteFilePath());
}
- connect(m_chooser, QOverload<int>::of(&QComboBox::currentIndexChanged),
- [this] (int index) {
+ connect(m_chooser, &QComboBox::currentIndexChanged, this, [this](int index) {
const QString sudokuFile = m_chooser->itemData(index).toString();
const QVariantMap initValues = readSudoku(sudokuFile);
m_machine->submitEvent("setup", initValues);
@@ -203,14 +153,14 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
}
});
- m_machine->connectToState("solved", [this] (bool solved) {
+ m_machine->connectToState("solved", [this](bool solved) {
if (solved)
m_label->setText(tr("SOLVED !!!"));
else
m_label->setText(tr("unsolved"));
});
- m_machine->connectToEvent("updateGUI", [this] (const QScxmlEvent &event) {
+ m_machine->connectToEvent("updateGUI", [this](const QScxmlEvent &event) {
const QVariant data = event.data();
const QVariantList currentRows = data.toMap().value(
@@ -237,11 +187,4 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
}
}
});
-
- setLayout(layout);
}
-
-MainWindow::~MainWindow()
-{
-}
-
diff --git a/examples/scxml/sudoku/mainwindow.h b/examples/scxml/sudoku/mainwindow.h
index b37279e..b12a6dd 100644
--- a/examples/scxml/sudoku/mainwindow.h
+++ b/examples/scxml/sudoku/mainwindow.h
@@ -1,57 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
-#include <QWidget>
+#include <QtWidgets/qwidget.h>
QT_BEGIN_NAMESPACE
class QToolButton;
@@ -60,22 +13,20 @@ class QLabel;
class QComboBox;
QT_END_NAMESPACE
-
class MainWindow : public QWidget
{
Q_OBJECT
public:
explicit MainWindow(QScxmlStateMachine *machine, QWidget *parent = nullptr);
- ~MainWindow();
private:
- QScxmlStateMachine *m_machine;
- QVector<QVector<QToolButton *> > m_buttons;
- QToolButton *m_startButton;
- QToolButton *m_undoButton;
- QLabel *m_label;
- QComboBox *m_chooser;
+ QScxmlStateMachine *m_machine = nullptr;
+ QList<QList<QToolButton *>> m_buttons;
+ QToolButton *m_startButton = nullptr;
+ QToolButton *m_undoButton = nullptr;
+ QLabel *m_label = nullptr;
+ QComboBox *m_chooser = nullptr;
};
#endif // MAINWINDOW_H
diff --git a/examples/scxml/sudoku/sudoku.js b/examples/scxml/sudoku/sudoku.js
index 21a1ed8..98abe0a 100644
--- a/examples/scxml/sudoku/sudoku.js
+++ b/examples/scxml/sudoku/sudoku.js
@@ -1,52 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
function restart() {
for (var i = 0; i < initState.length; i++)
diff --git a/examples/scxml/sudoku/sudoku.scxml b/examples/scxml/sudoku/sudoku.scxml
index eb36283..0184c12 100644
--- a/examples/scxml/sudoku/sudoku.scxml
+++ b/examples/scxml/sudoku/sudoku.scxml
@@ -1,54 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="Sudoku" datamodel="ecmascript">
diff --git a/examples/scxml/trafficlight-common/Button.qml b/examples/scxml/trafficlight-common/Button.qml
deleted file mode 100644
index 24512ce..0000000
--- a/examples/scxml/trafficlight-common/Button.qml
+++ /dev/null
@@ -1,62 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.5
-
-Image {
- id: button
- signal clicked
-
- MouseArea {
- id: mouse
- anchors.fill: parent
- onClicked: button.clicked()
- }
-}
diff --git a/examples/scxml/trafficlight-common/Lights.ui.qml b/examples/scxml/trafficlight-common/Lights.ui.qml
deleted file mode 100644
index a385eae..0000000
--- a/examples/scxml/trafficlight-common/Lights.ui.qml
+++ /dev/null
@@ -1,140 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-
-import QtQuick 2.5
-import QtQuick.Window 2.2
-import TrafficLightStateMachine 1.0
-
-Image {
- id: lights
-
- property alias button: button
- property TrafficLightStateMachine stateMachine
-
- source: "background.png"
-
- Column {
- y: 40
- spacing: 27
- anchors.horizontalCenter: parent.horizontalCenter
-
- Image {
- id: redLight
- opacity: 0.2
- source: "red.png"
- }
-
- Image {
- id: yellowLight
- opacity: 0.2
- source: "yellow.png"
- }
-
- Image {
- id: greenLight
- opacity: 0.2
- source: "green.png"
- }
- }
-
- Button {
- id: button
-
- anchors.right: parent.right
- anchors.bottom: parent.bottom
- anchors.margins: 20
- source: "pause.png"
- }
-
- states: [
- State {
- name: "Red"
- when: stateMachine.red
-
- PropertyChanges {
- target: redLight
- opacity: 1
- }
- },
- State {
- name: "RedGoingGreen"
- when: stateMachine.redGoingGreen
-
- PropertyChanges {
- target: redLight
- opacity: 1
- }
-
- PropertyChanges {
- target: yellowLight
- opacity: 1
- }
- },
- State {
- name: "Yellow"
- when: stateMachine.yellow || stateMachine.blinking
-
- PropertyChanges {
- target: yellowLight
- opacity: 1
- }
- },
- State {
- name: "Green"
- when: stateMachine.green
-
- PropertyChanges {
- target: greenLight
- opacity: 1
- }
- }
- ]
-}
diff --git a/examples/scxml/trafficlight-common/TrafficLight.qml b/examples/scxml/trafficlight-common/TrafficLight.qml
deleted file mode 100644
index ec11ed5..0000000
--- a/examples/scxml/trafficlight-common/TrafficLight.qml
+++ /dev/null
@@ -1,74 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.5
-import QtQuick.Window 2.2
-import TrafficLightStateMachine 1.0
-
-Window {
- id: root
-
- property TrafficLightStateMachine stateMachine
-
- visible: true
- width: lights.width
- height: lights.height
- maximumWidth: lights.implicitWidth
- maximumHeight: lights.implicitHeight
-
- Lights {
- id: lights
-
- stateMachine: root.stateMachine
- button.source: stateMachine.working ? "pause.png" : "play.png"
-
- button.onClicked: stateMachine.submitEvent(stateMachine.working ? "smash" : "repair");
- }
-}
diff --git a/examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc b/examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc
index ac16203..1410dcb 100644
--- a/examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc
+++ b/examples/scxml/trafficlight-common/doc/src/trafficlight-compiling.qdocinc
@@ -1,17 +1,30 @@
\section1 Compiling the State Machine
- We link against the Qt SCXML module by adding the following line to the
- \e .pro file:
+ We link against the Qt SCXML module by adding the following lines to the
+ example's build files.
+
+ \section2 \e .pro when using qmake:
\quotefromfile trafficlight-qml-static/trafficlight-qml-static.pro
\skipto QT
\printline scxml
We then specify the state machine to compile:
-
\skipto STATECHARTS
\printline scxml
- The Qt SCXML Compiler, \c qscxmlc, is run automatically to generate
- \e statemachine.h and \e statemachine.cpp, and to add them to the \c HEADERS
- and \c SOURCES variables for compilation.
+ \section2 \e CMakeLists.txt when using cmake:
+ \quotefromfile trafficlight-qml-static/CMakeLists.txt
+ \skipto find_package
+ \printline Scxml
+ \skipto target_link_libraries
+ \printuntil )
+
+ We then specify the state machine to compile:
+ \skipto qt6_add_statecharts
+ \printuntil )
+
+ The statechart directives \e STATECHARTS or \e qt6_add_statecharts invoke the Qt SCXML
+ Compiler, \c qscxmlc, which is run automatically to generate \e statemachine.h and
+ \e statemachine.cpp, which are then added appropriately as headers and sources for
+ compilation.
diff --git a/examples/scxml/trafficlight-common/statemachine.scxml b/examples/scxml/trafficlight-common/statemachine.scxml
index 3cf023e..9afe664 100644
--- a/examples/scxml/trafficlight-common/statemachine.scxml
+++ b/examples/scxml/trafficlight-common/statemachine.scxml
@@ -1,54 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
diff --git a/examples/scxml/trafficlight-common/trafficlight.cpp b/examples/scxml/trafficlight-common/trafficlight.cpp
index 52be91d..e29f2b5 100644
--- a/examples/scxml/trafficlight-common/trafficlight.cpp
+++ b/examples/scxml/trafficlight-common/trafficlight.cpp
@@ -1,71 +1,26 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "trafficlight.h"
-#include <QPainter>
-#include <QVBoxLayout>
+#include <QtWidgets/qboxlayout.h>
+#include <QtGui/qpainter.h>
+
+using namespace Qt::Literals::StringLiterals;
class TrafficLightWidget : public QWidget
{
public:
TrafficLightWidget(QWidget *parent = nullptr)
- : QWidget(parent), m_background(QLatin1String(":/background.png"))
+ : QWidget(parent), m_background(":/background.png"_L1)
{
QVBoxLayout *vbox = new QVBoxLayout(this);
vbox->setContentsMargins(0, 40, 0, 80);
- m_red = new LightWidget(QLatin1String(":/red.png"));
+ m_red = new LightWidget(":/red.png"_L1);
vbox->addWidget(m_red, 0, Qt::AlignHCenter);
- m_yellow = new LightWidget(QLatin1String(":/yellow.png"));
+ m_yellow = new LightWidget(":/yellow.png"_L1);
vbox->addWidget(m_yellow, 0, Qt::AlignHCenter);
- m_green = new LightWidget(QLatin1String(":/green.png"));
+ m_green = new LightWidget(":/green.png"_L1);
vbox->addWidget(m_green, 0, Qt::AlignHCenter);
setLayout(vbox);
}
@@ -103,16 +58,11 @@ TrafficLight::TrafficLight(QScxmlStateMachine *machine, QWidget *parent)
TrafficLightWidget *widget = new TrafficLightWidget(this);
setFixedSize(widget->sizeHint());
- machine->connectToState(QStringLiteral("red"),
- widget->redLight(), &LightWidget::switchLight);
- machine->connectToState(QStringLiteral("redGoingGreen"),
- widget->redLight(), &LightWidget::switchLight);
- machine->connectToState(QStringLiteral("yellow"),
- widget->yellowLight(), &LightWidget::switchLight);
- machine->connectToState(QStringLiteral("blinking"),
- widget->yellowLight(), &LightWidget::switchLight);
- machine->connectToState(QStringLiteral("green"),
- widget->greenLight(), &LightWidget::switchLight);
+ machine->connectToState(u"red"_s, widget->redLight(), &LightWidget::switchLight);
+ machine->connectToState(u"redGoingGreen"_s, widget->redLight(), &LightWidget::switchLight);
+ machine->connectToState(u"yellow"_s, widget->yellowLight(), &LightWidget::switchLight);
+ machine->connectToState(u"blinking"_s, widget->yellowLight(), &LightWidget::switchLight);
+ machine->connectToState(u"green"_s, widget->greenLight(), &LightWidget::switchLight);
QAbstractButton *button = new ButtonWidget(this);
auto setButtonGeometry = [this, button](){
@@ -124,8 +74,7 @@ TrafficLight::TrafficLight(QScxmlStateMachine *machine, QWidget *parent)
connect(button, &QAbstractButton::toggled, this, setButtonGeometry);
setButtonGeometry();
- connect(button, &QAbstractButton::toggled,
- this, &TrafficLight::toggleWorking);
+ connect(button, &QAbstractButton::toggled, this, &TrafficLight::toggleWorking);
}
void TrafficLight::toggleWorking(bool pause)
@@ -150,7 +99,9 @@ void LightWidget::setOn(bool on)
}
void LightWidget::switchLight(bool onoff)
-{ setOn(onoff); }
+{
+ setOn(onoff);
+}
void LightWidget::paintEvent(QPaintEvent *)
{
@@ -167,8 +118,8 @@ QSize LightWidget::sizeHint() const
}
ButtonWidget::ButtonWidget(QWidget *parent) :
- QAbstractButton(parent), m_playIcon(QLatin1String(":/play.png")),
- m_pauseIcon(QLatin1String(":/pause.png"))
+ QAbstractButton(parent), m_playIcon(":/play.png"_L1),
+ m_pauseIcon(":/pause.png"_L1)
{
setCheckable(true);
}
diff --git a/examples/scxml/trafficlight-common/trafficlight.h b/examples/scxml/trafficlight-common/trafficlight.h
index 78e2c8d..97da67b 100644
--- a/examples/scxml/trafficlight-common/trafficlight.h
+++ b/examples/scxml/trafficlight-common/trafficlight.h
@@ -1,59 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#ifndef TRAFFICLIGHT_H
#define TRAFFICLIGHT_H
-#include <QAbstractButton>
-#include <QScxmlStateMachine>
-#include <QWidget>
+#include <QtWidgets/qwidget.h>
+#include <QtWidgets/qabstractbutton.h>
+#include <QtScxml/qscxmlstatemachine.h>
class TrafficLight : public QWidget
{
diff --git a/examples/scxml/trafficlight-qml-dynamic/Button.qml b/examples/scxml/trafficlight-qml-dynamic/Button.qml
new file mode 100644
index 0000000..1333193
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-dynamic/Button.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+
+Image {
+ id: button
+ signal clicked
+
+ MouseArea {
+ id: mouse
+ anchors.fill: parent
+ onClicked: button.clicked()
+ }
+}
diff --git a/examples/scxml/trafficlight-qml-dynamic/CMakeLists.txt b/examples/scxml/trafficlight-qml-dynamic/CMakeLists.txt
new file mode 100644
index 0000000..023edbf
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-dynamic/CMakeLists.txt
@@ -0,0 +1,85 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(trafficlight-qml-dynamic LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/trafficlight-qml-dynamic")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Scxml)
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(trafficlight-qml-dynamic
+ trafficlight-qml-dynamic.cpp
+)
+
+set_target_properties(trafficlight-qml-dynamic PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(trafficlight-qml-dynamic PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Scxml
+)
+
+set_source_files_properties("../trafficlight-common/statemachine.scxml"
+ PROPERTIES QT_RESOURCE_ALIAS "statemachine.scxml"
+)
+
+set_source_files_properties(../trafficlight-common/play.png
+ PROPERTIES QT_RESOURCE_ALIAS "play.png"
+)
+
+set_source_files_properties(../trafficlight-common/play.png
+ PROPERTIES QT_RESOURCE_ALIAS "play.png"
+)
+
+set_source_files_properties(../trafficlight-common/yellow.png
+ PROPERTIES QT_RESOURCE_ALIAS "yellow.png"
+)
+
+set_source_files_properties(../trafficlight-common/red.png
+ PROPERTIES QT_RESOURCE_ALIAS "red.png"
+)
+
+set_source_files_properties(../trafficlight-common/green.png
+ PROPERTIES QT_RESOURCE_ALIAS "green.png"
+)
+
+set_source_files_properties(../trafficlight-common/background.png
+ PROPERTIES QT_RESOURCE_ALIAS "background.png"
+)
+
+set_source_files_properties(../trafficlight-common/pause.png
+ PROPERTIES QT_RESOURCE_ALIAS "pause.png"
+)
+
+qt_add_qml_module(trafficlight-qml-dynamic
+ URI TrafficLightApplication
+ VERSION 1.0
+ QML_FILES
+ MainView.qml
+ Button.qml
+ Lights.ui.qml
+ RESOURCES
+ ../trafficlight-common/statemachine.scxml
+ ../trafficlight-common/play.png
+ ../trafficlight-common/yellow.png
+ ../trafficlight-common/red.png
+ ../trafficlight-common/green.png
+ ../trafficlight-common/background.png
+ ../trafficlight-common/pause.png
+)
+
+install(TARGETS trafficlight-qml-dynamic
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/trafficlight-qml-dynamic/Lights.ui.qml b/examples/scxml/trafficlight-qml-dynamic/Lights.ui.qml
new file mode 100644
index 0000000..ef683f9
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-dynamic/Lights.ui.qml
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtScxml
+
+Image {
+ id: lights
+
+ property alias button: button
+ required property StateMachine stateMachine
+
+ source: "background.png"
+
+ Column {
+ y: 40
+ spacing: 27
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ Image {
+ id: redLight
+ opacity: 0.2
+ source: "red.png"
+ }
+
+ Image {
+ id: yellowLight
+ opacity: 0.2
+ source: "yellow.png"
+ }
+
+ Image {
+ id: greenLight
+ opacity: 0.2
+ source: "green.png"
+ }
+ }
+
+ Button {
+ id: button
+
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ source: "pause.png"
+ }
+
+ states: [
+ // Suppress qmllint warning, dynamic statemachine properties not known at compile-time
+ // qmllint disable missing-property
+ State {
+ name: "Red"
+ when: lights.stateMachine.red
+
+ PropertyChanges { redLight.opacity: 1 }
+ },
+ State {
+ name: "RedGoingGreen"
+ when: lights.stateMachine.redGoingGreen
+
+ PropertyChanges { redLight.opacity: 1 }
+ PropertyChanges { yellowLight.opacity: 1 }
+ },
+ State {
+ name: "Yellow"
+ when: lights.stateMachine.yellow || lights.stateMachine.blinking
+
+ PropertyChanges { yellowLight.opacity: 1 }
+ },
+ State {
+ name: "Green"
+ when: lights.stateMachine.green
+
+ PropertyChanges { greenLight.opacity: 1 }
+ }
+ // qmllint enable missing-property
+ ]
+}
diff --git a/examples/scxml/trafficlight-qml-dynamic/MainView.qml b/examples/scxml/trafficlight-qml-dynamic/MainView.qml
new file mode 100644
index 0000000..2923568
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-dynamic/MainView.qml
@@ -0,0 +1,29 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtScxml
+import TrafficLightApplication
+
+Window {
+ width: lights.width
+ height: lights.height
+ visible: true
+
+ Lights {
+ id: lights
+ stateMachine: loader.stateMachine
+ // Suppress qmllint warning, dynamic statemachine properties not known at compile-time
+ // qmllint disable missing-property
+ button.source: stateMachine.working ? "pause.png" : "play.png"
+ button.onClicked: stateMachine.submitEvent(stateMachine.working ? "smash" : "repair");
+ // qmllint enable missing-property
+ }
+
+ StateMachineLoader {
+ id: loader
+ source: Qt.resolvedUrl("statemachine.scxml")
+ }
+}
diff --git a/examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc b/examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc
index 0316508..37bb634 100644
--- a/examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc
+++ b/examples/scxml/trafficlight-qml-dynamic/doc/src/trafficlight-qml-dynamic.qdoc
@@ -1,33 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example trafficlight-qml-dynamic
- \title Qt SCXML Traffic Light QML Example (Dynamic)
+ \title SCXML Traffic Light (Dynamic, QML)
+ \examplecategory {Data Processing & I/O}
\ingroup examples-qtscxml
\brief A Qt Quick application that uses a dynamically loaded state machine
@@ -35,7 +12,7 @@
\image trafficlight.png
- \e{Traffic Light QML Example (Dynamic)} demonstrates how to connect to the
+ \e{Traffic Light QML} demonstrates how to connect to the
active properties of a state in a dynamically loaded state machine.
The UI is created using Qt Quick.
@@ -44,16 +21,24 @@
\section1 Dynamically Loading the State Machine
- We link against the Qt SCXML module by adding the following line to the
- example \e .pro file:
+ We link against the Qt SCXML module by adding the following lines to the
+ example's build files.
+ To \e .pro when using qmake:
\quotefromfile trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro
\skipto QT
\printline scxml
+ To \e CMakeLists.txt when using cmake:
+ \quotefromfile trafficlight-qml-dynamic/CMakeLists.txt
+ \skipto find_package
+ \printline Scxml
+ \skipto target_link_libraries
+ \printuntil )
+
We dynamically create the state machine in the main QML file:
- \quotefromfile trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml
+ \quotefromfile trafficlight-qml-dynamic/MainView.qml
\skipto import QtScxml
\printuntil /^\}/
@@ -61,7 +46,7 @@
We connect to the states as follows:
- \quotefromfile trafficlight-common/Lights.ui.qml
+ \quotefromfile trafficlight-qml-dynamic/Lights.ui.qml
\skipto states
\printuntil ]
*/
diff --git a/examples/scxml/trafficlight-qml-dynamic/qmldir b/examples/scxml/trafficlight-qml-dynamic/qmldir
new file mode 100644
index 0000000..c375025
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-dynamic/qmldir
@@ -0,0 +1,6 @@
+module TrafficLightApplication
+prefer :/qt/qml/TrafficLightApplication/
+MainView 1.0 MainView.qml
+Button 1.0 Button.qml
+Lights 1.0 Lights.ui.qml
+
diff --git a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp
index a8bf00e..5b945b7 100644
--- a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp
+++ b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.cpp
@@ -1,75 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-#include <QScxmlStateMachine>
+#include <QtGui/qguiapplication.h>
+#include <QtQml/qqmlapplicationengine.h>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
- /* Register QScxmlStateMachine as TrafficLightStateMachine. This is required to have a type
- * for the state machine and allows full code completion in the static case, since we
- * share the QML code. */
- qmlRegisterUncreatableType<QScxmlStateMachine>("TrafficLightStateMachine",
- 1, 0,
- "TrafficLightStateMachine",
- QLatin1String("TrafficLightStateMachine is not creatable."));
-
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:///trafficlight-qml-dynamic.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
+ [](){ QCoreApplication::exit(EXIT_FAILURE); }, Qt::QueuedConnection);
+ engine.loadFromModule("TrafficLightApplication", "MainView");
return app.exec();
}
-
diff --git a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro
index c03a7ef..f562c79 100644
--- a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro
+++ b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.pro
@@ -4,7 +4,25 @@ QT += qml scxml
SOURCES += trafficlight-qml-dynamic.cpp
-RESOURCES += trafficlight-qml-dynamic.qrc
+QML_IMPORT_NAME = TrafficLightApplication
+QML_IMPORT_MAJOR_VERSION = 1
+
+qml_resources.files = \
+ qmldir \
+ MainView.qml \
+ Button.qml \
+ Lights.ui.qml \
+ ../trafficlight-common/statemachine.scxml \
+ ../trafficlight-common/play.png \
+ ../trafficlight-common/yellow.png \
+ ../trafficlight-common/red.png \
+ ../trafficlight-common/green.png \
+ ../trafficlight-common/background.png \
+ ../trafficlight-common/pause.png \
+
+qml_resources.prefix = /qt/qml/TrafficLightApplication
+
+RESOURCES += qml_resources
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/trafficlight-qml-dynamic
INSTALLS += target
diff --git a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml
deleted file mode 100644
index aef249a..0000000
--- a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qml
+++ /dev/null
@@ -1,60 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtScxml 5.8
-
-TrafficLight {
- StateMachineLoader {
- id: loader
- source: "qrc:///statemachine.scxml"
- }
-
- stateMachine: loader.stateMachine
-}
diff --git a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc b/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc
deleted file mode 100644
index 547935b..0000000
--- a/examples/scxml/trafficlight-qml-dynamic/trafficlight-qml-dynamic.qrc
+++ /dev/null
@@ -1,15 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file alias="TrafficLight.qml">../trafficlight-common/TrafficLight.qml</file>
- <file alias="Button.qml">../trafficlight-common/Button.qml</file>
- <file alias="Lights.ui.qml">../trafficlight-common/Lights.ui.qml</file>
- <file>trafficlight-qml-dynamic.qml</file>
- <file alias="statemachine.scxml">../trafficlight-common/statemachine.scxml</file>
- <file alias="green.png">../trafficlight-common/green.png</file>
- <file alias="yellow.png">../trafficlight-common/yellow.png</file>
- <file alias="red.png">../trafficlight-common/red.png</file>
- <file alias="pause.png">../trafficlight-common/pause.png</file>
- <file alias="play.png">../trafficlight-common/play.png</file>
- <file alias="background.png">../trafficlight-common/background.png</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/trafficlight-qml-simple/CMakeLists.txt b/examples/scxml/trafficlight-qml-simple/CMakeLists.txt
new file mode 100644
index 0000000..b3d2bfe
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-simple/CMakeLists.txt
@@ -0,0 +1,51 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(trafficlight-qml-simple LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/trafficlight-qml-simple")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Scxml)
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(trafficlight-qml-simple
+ trafficlight-qml-simple.cpp
+)
+
+set_target_properties(trafficlight-qml-simple PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(trafficlight-qml-simple PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::Qml
+ Qt::Scxml
+)
+
+# Statecharts:
+qt6_add_statecharts(trafficlight-qml-simple
+ ../trafficlight-common/statemachine.scxml
+)
+
+qt_add_qml_module(trafficlight-qml-simple
+ URI TrafficLightApplication
+ VERSION 1.0
+ QML_FILES
+ MainView.qml
+ Light.qml
+ SOURCES
+ trafficlight-qml.h
+)
+
+install(TARGETS trafficlight-qml-simple
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/trafficlight-qml-simple/Light.qml b/examples/scxml/trafficlight-qml-simple/Light.qml
index 8502d35..da6b30e 100644
--- a/examples/scxml/trafficlight-qml-simple/Light.qml
+++ b/examples/scxml/trafficlight-qml-simple/Light.qml
@@ -1,54 +1,7 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-import QtQuick 2.5
+import QtQuick
Item {
property color color
diff --git a/examples/scxml/trafficlight-qml-simple/MainView.qml b/examples/scxml/trafficlight-qml-simple/MainView.qml
new file mode 100644
index 0000000..3e66f19
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-simple/MainView.qml
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+import QtQuick
+import TrafficLightApplication
+
+Window {
+ id: root
+ visible: true
+ width: 100
+ height: 350
+
+ TrafficLightStateMachine {
+ id: stateMachine
+ running: true
+ }
+
+ Item {
+ id: lights
+ width: parent.width
+ height: 300
+
+ Light {
+ anchors.top: parent.top
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "red"
+ visible: stateMachine.red || stateMachine.redGoingGreen
+ }
+
+ Light {
+ anchors.centerIn: parent
+ color: "yellow"
+ visible: stateMachine.yellow || stateMachine.blinking
+ }
+
+ Light {
+ anchors.bottom: parent.bottom
+ anchors.horizontalCenter: parent.horizontalCenter
+ color: "green"
+ visible: stateMachine.green
+ }
+ }
+
+ Rectangle {
+ anchors.top: lights.bottom
+ anchors.bottom: parent.bottom
+ width: parent.width
+ border.color: "black"
+
+ Text {
+ anchors.fill: parent
+ text: stateMachine.working ? "Pause" : "Unpause"
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ }
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: stateMachine.submitEvent(stateMachine.working ? "smash" : "repair");
+ }
+ }
+}
diff --git a/examples/scxml/trafficlight-qml-simple/TrafficLight.qml b/examples/scxml/trafficlight-qml-simple/TrafficLight.qml
deleted file mode 100644
index d2f9948..0000000
--- a/examples/scxml/trafficlight-qml-simple/TrafficLight.qml
+++ /dev/null
@@ -1,110 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import QtQuick 2.5
-import QtQuick.Window 2.2
-import TrafficLightStateMachine 1.0
-
-Window {
- id: root
- visible: true
- width: 100
- height: 350
-
- TrafficLightStateMachine {
- id: stateMachine
- running: true
- }
-
- Item {
- id: lights
- width: parent.width
- height: 300
-
- Light {
- anchors.top: parent.top
- anchors.horizontalCenter: parent.horizontalCenter
- color: "red"
- visible: stateMachine.red || stateMachine.redGoingGreen
- }
-
- Light {
- anchors.centerIn: parent
- color: "yellow"
- visible: stateMachine.yellow || stateMachine.blinking
- }
-
- Light {
- anchors.bottom: parent.bottom
- anchors.horizontalCenter: parent.horizontalCenter
- color: "green"
- visible: stateMachine.green
- }
- }
-
- Rectangle {
- anchors.top: lights.bottom
- anchors.bottom: parent.bottom
- width: parent.width
- border.color: "black"
-
- Text {
- anchors.fill: parent
- text: stateMachine.working ? "Pause" : "Unpause"
- horizontalAlignment: Text.AlignHCenter
- verticalAlignment: Text.AlignVCenter
- }
-
- MouseArea {
- anchors.fill: parent
- onClicked: stateMachine.submitEvent(stateMachine.working ? "smash" : "repair");
- }
- }
-}
diff --git a/examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc b/examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc
index f66f5f7..d4bd8ef 100644
--- a/examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc
+++ b/examples/scxml/trafficlight-qml-simple/doc/src/trafficlight-qml-simple.qdoc
@@ -1,33 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example trafficlight-qml-simple
- \title Qt SCXML Traffic Light QML Example (Simple)
+ \title SCXML Traffic Light (Simple, QML)
+ \examplecategory {Data Processing & I/O}
\ingroup examples-qtscxml
\brief A Qt Quick application that uses a compiled state machine to
@@ -35,7 +12,7 @@
\image trafficlight.png
- \e{Traffic Light QML Example (Simple)} demonstrates how to connect to the
+ \e{Traffic Light} demonstrates how to connect to the
active properties of a state in a state machine that is compiled to a class.
The UI is created using Qt Quick.
@@ -48,7 +25,7 @@
We instantiate the state machine as follows:
- \quotefromfile trafficlight-qml-simple/TrafficLight.qml
+ \quotefromfile trafficlight-qml-simple/MainView.qml
\skipto TrafficLightStateMachine {
\printuntil }
@@ -57,7 +34,7 @@
We connect to the states as follows:
- \quotefromfile trafficlight-qml-simple/TrafficLight.qml
+ \quotefromfile trafficlight-qml-simple/MainView.qml
\skipto Light {
\printuntil /^ {4}\}/
*/
diff --git a/examples/scxml/trafficlight-qml-simple/qmldir b/examples/scxml/trafficlight-qml-simple/qmldir
new file mode 100644
index 0000000..179aefa
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-simple/qmldir
@@ -0,0 +1,5 @@
+module TrafficLightApplication
+prefer :/qt/qml/TrafficLightApplication/
+MainView 1.0 MainView.qml
+Light 1.0 Light.qml
+
diff --git a/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.cpp b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.cpp
index c9950c4..5b945b7 100644
--- a/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.cpp
+++ b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.cpp
@@ -1,71 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-#include "statemachine.h"
+#include <QtGui/qguiapplication.h>
+#include <QtQml/qqmlapplicationengine.h>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
- qmlRegisterType<TrafficLightStateMachine>("TrafficLightStateMachine", 1, 0,
- "TrafficLightStateMachine");
-
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/TrafficLight.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
+ [](){ QCoreApplication::exit(EXIT_FAILURE); }, Qt::QueuedConnection);
+ engine.loadFromModule("TrafficLightApplication", "MainView");
return app.exec();
}
-
diff --git a/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro
index 4de04ea..0424432 100644
--- a/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro
+++ b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.pro
@@ -2,13 +2,31 @@ TEMPLATE = app
QT += qml scxml
CONFIG += c++11
+CONFIG += qmltypes
SOURCES += trafficlight-qml-simple.cpp
-RESOURCES += trafficlight-qml-simple.qrc
+HEADERS += trafficlight-qml.h
STATECHARTS = ../trafficlight-common/statemachine.scxml
+QML_IMPORT_NAME = TrafficLightApplication
+QML_IMPORT_MAJOR_VERSION = 1
+
+qml_resources.files = \
+ qmldir \
+ MainView.qml \
+ Light.qml \
+ ../trafficlight-common/play.png \
+ ../trafficlight-common/yellow.png \
+ ../trafficlight-common/red.png \
+ ../trafficlight-common/green.png \
+ ../trafficlight-common/background.png \
+ ../trafficlight-common/pause.png \
+
+qml_resources.prefix = /qt/qml/TrafficLightApplication
+
+RESOURCES += qml_resources
+
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/trafficlight-qml-simple
INSTALLS += target
-
diff --git a/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.qrc b/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.qrc
deleted file mode 100644
index bb75dba..0000000
--- a/examples/scxml/trafficlight-qml-simple/trafficlight-qml-simple.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>TrafficLight.qml</file>
- <file>Light.qml</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/trafficlight-qml-simple/trafficlight-qml.h b/examples/scxml/trafficlight-qml-simple/trafficlight-qml.h
new file mode 100644
index 0000000..ac309bd
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-simple/trafficlight-qml.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TRAFFICLIGHT_QML
+#define TRAFFICLIGHT_QML
+
+#include "statemachine.h"
+
+#include <QtQml/qqml.h>
+#include <QtCore/qobject.h>
+
+struct TrafficLightStateMachineRegistration
+{
+ Q_GADGET
+ QML_FOREIGN(TrafficLightStateMachine)
+ QML_NAMED_ELEMENT(TrafficLightStateMachine)
+ QML_ADDED_IN_VERSION(1, 0)
+};
+
+#endif // TRAFFICLIGHT_QML
diff --git a/examples/scxml/trafficlight-qml-static/Button.qml b/examples/scxml/trafficlight-qml-static/Button.qml
new file mode 100644
index 0000000..1333193
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-static/Button.qml
@@ -0,0 +1,17 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+
+Image {
+ id: button
+ signal clicked
+
+ MouseArea {
+ id: mouse
+ anchors.fill: parent
+ onClicked: button.clicked()
+ }
+}
diff --git a/examples/scxml/trafficlight-qml-static/CMakeLists.txt b/examples/scxml/trafficlight-qml-static/CMakeLists.txt
new file mode 100644
index 0000000..676a482
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-static/CMakeLists.txt
@@ -0,0 +1,87 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(trafficlight-qml-static LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/trafficlight-qml-static")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Qml Scxml)
+qt_standard_project_setup(REQUIRES 6.5)
+
+qt_add_executable(trafficlight-qml-static
+ trafficlight-qml-static.cpp
+)
+
+set_target_properties(trafficlight-qml-static PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(trafficlight-qml-static PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Qml
+ Qt6::Scxml
+)
+
+# Statecharts:
+qt6_add_statecharts(trafficlight-qml-static
+ ../trafficlight-common/statemachine.scxml
+)
+
+set_source_files_properties(../trafficlight-common/play.png
+ PROPERTIES QT_RESOURCE_ALIAS "play.png"
+)
+
+set_source_files_properties(../trafficlight-common/play.png
+ PROPERTIES QT_RESOURCE_ALIAS "play.png"
+)
+
+set_source_files_properties(../trafficlight-common/yellow.png
+ PROPERTIES QT_RESOURCE_ALIAS "yellow.png"
+)
+
+set_source_files_properties(../trafficlight-common/red.png
+ PROPERTIES QT_RESOURCE_ALIAS "red.png"
+)
+
+set_source_files_properties(../trafficlight-common/green.png
+ PROPERTIES QT_RESOURCE_ALIAS "green.png"
+)
+
+set_source_files_properties(../trafficlight-common/background.png
+ PROPERTIES QT_RESOURCE_ALIAS "background.png"
+)
+
+set_source_files_properties(../trafficlight-common/pause.png
+ PROPERTIES QT_RESOURCE_ALIAS "pause.png"
+)
+
+qt_add_qml_module(trafficlight-qml-static
+ URI TrafficLightApplication
+ VERSION 1.0
+ QML_FILES
+ MainView.qml
+ Button.qml
+ Lights.ui.qml
+ SOURCES
+ trafficlight-qml.h
+ RESOURCES
+ ../trafficlight-common/play.png
+ ../trafficlight-common/yellow.png
+ ../trafficlight-common/red.png
+ ../trafficlight-common/green.png
+ ../trafficlight-common/background.png
+ ../trafficlight-common/pause.png
+)
+
+install(TARGETS trafficlight-qml-static
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/trafficlight-qml-static/Lights.ui.qml b/examples/scxml/trafficlight-qml-static/Lights.ui.qml
new file mode 100644
index 0000000..ace9cbc
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-static/Lights.ui.qml
@@ -0,0 +1,77 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import TrafficLightApplication
+
+Image {
+ id: lights
+
+ property alias button: button
+ property TrafficLightStateMachine stateMachine
+
+ source: "background.png"
+
+ Column {
+ y: 40
+ spacing: 27
+ anchors.horizontalCenter: parent.horizontalCenter
+
+ Image {
+ id: redLight
+ opacity: 0.2
+ source: "red.png"
+ }
+
+ Image {
+ id: yellowLight
+ opacity: 0.2
+ source: "yellow.png"
+ }
+
+ Image {
+ id: greenLight
+ opacity: 0.2
+ source: "green.png"
+ }
+ }
+
+ Button {
+ id: button
+
+ anchors.right: parent.right
+ anchors.bottom: parent.bottom
+ anchors.margins: 20
+ source: "pause.png"
+ }
+
+ states: [
+ State {
+ name: "Red"
+ when: lights.stateMachine.red
+
+ PropertyChanges { redLight.opacity: 1 }
+ },
+ State {
+ name: "RedGoingGreen"
+ when: lights.stateMachine.redGoingGreen
+
+ PropertyChanges { redLight.opacity: 1 }
+ PropertyChanges { yellowLight.opacity: 1 }
+ },
+ State {
+ name: "Yellow"
+ when: lights.stateMachine.yellow || lights.stateMachine.blinking
+
+ PropertyChanges { yellowLight.opacity: 1 }
+ },
+ State {
+ name: "Green"
+ when: lights.stateMachine.green
+
+ PropertyChanges { greenLight.opacity: 1 }
+ }
+ ]
+}
diff --git a/examples/scxml/trafficlight-qml-static/MainView.qml b/examples/scxml/trafficlight-qml-static/MainView.qml
new file mode 100644
index 0000000..d4dce03
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-static/MainView.qml
@@ -0,0 +1,22 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import TrafficLightApplication
+
+Window {
+ width: lights.width
+ height: lights.height
+ visible: true
+
+ Lights {
+ id: lights
+ stateMachine: TrafficLightStateMachine {
+ running: true
+ }
+ button.source: stateMachine.working ? "pause.png" : "play.png"
+ button.onClicked: stateMachine.submitEvent(stateMachine.working ? "smash" : "repair");
+ }
+}
diff --git a/examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc b/examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc
index 814f410..17a4fda 100644
--- a/examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc
+++ b/examples/scxml/trafficlight-qml-static/doc/src/trafficlight-qml-static.qdoc
@@ -1,33 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example trafficlight-qml-static
- \title Qt SCXML Traffic Light QML Example (Static)
+ \title SCXML Traffic Light (Static, QML)
+ \examplecategory {Data Processing & I/O}
\ingroup examples-qtscxml
\brief A Qt Quick application that uses a compiled state machine to
@@ -35,7 +12,7 @@
\image trafficlight.png
- \e{Traffic Light QML Example (Static)} demonstrates how to connect to the
+ \e{Traffic Light} demonstrates how to connect to the
active properties of a state in a state machine that is compiled to a class.
The UI is created using Qt Quick.
@@ -48,7 +25,7 @@
We instantiate the state machine as follows:
- \quotefromfile trafficlight-qml-static/trafficlight-qml-static.qml
+ \quotefromfile trafficlight-qml-static/MainView.qml
\skipto TrafficLight
\printuntil }
@@ -57,7 +34,7 @@
We connect to the states as follows:
- \quotefromfile trafficlight-common/Lights.ui.qml
+ \quotefromfile trafficlight-qml-static/Lights.ui.qml
\skipto states
\printuntil ]
*/
diff --git a/examples/scxml/trafficlight-qml-static/qmldir b/examples/scxml/trafficlight-qml-static/qmldir
new file mode 100644
index 0000000..c375025
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-static/qmldir
@@ -0,0 +1,6 @@
+module TrafficLightApplication
+prefer :/qt/qml/TrafficLightApplication/
+MainView 1.0 MainView.qml
+Button 1.0 Button.qml
+Lights 1.0 Lights.ui.qml
+
diff --git a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.cpp b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.cpp
index 30b5950..5b945b7 100644
--- a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.cpp
+++ b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.cpp
@@ -1,70 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
-#include <QGuiApplication>
-#include <QQmlApplicationEngine>
-#include <QQmlContext>
-
-#include "statemachine.h"
+#include <QtGui/qguiapplication.h>
+#include <QtQml/qqmlapplicationengine.h>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
- qmlRegisterType<TrafficLightStateMachine>("TrafficLightStateMachine", 1, 0, "TrafficLightStateMachine");
-
QQmlApplicationEngine engine;
- engine.load(QUrl(QStringLiteral("qrc:/trafficlight-qml-static.qml")));
- if (engine.rootObjects().isEmpty())
- return -1;
+ QObject::connect(&engine, &QQmlApplicationEngine::objectCreationFailed, &app,
+ [](){ QCoreApplication::exit(EXIT_FAILURE); }, Qt::QueuedConnection);
+ engine.loadFromModule("TrafficLightApplication", "MainView");
return app.exec();
}
-
diff --git a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro
index e5dd64c..2cc86aa 100644
--- a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro
+++ b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.pro
@@ -1,14 +1,34 @@
TEMPLATE = app
QT += qml scxml
+
CONFIG += c++11
+CONFIG += qmltypes
SOURCES += trafficlight-qml-static.cpp
-RESOURCES += trafficlight-qml-static.qrc
+HEADERS += trafficlight-qml.h
STATECHARTS = ../trafficlight-common/statemachine.scxml
+QML_IMPORT_NAME = TrafficLightApplication
+QML_IMPORT_MAJOR_VERSION = 1
+
+qml_resources.files = \
+ qmldir \
+ MainView.qml \
+ Button.qml \
+ Lights.ui.qml \
+ ../trafficlight-common/play.png \
+ ../trafficlight-common/yellow.png \
+ ../trafficlight-common/red.png \
+ ../trafficlight-common/green.png \
+ ../trafficlight-common/background.png \
+ ../trafficlight-common/pause.png \
+
+qml_resources.prefix = /qt/qml/TrafficLightApplication
+
+RESOURCES += qml_resources
+
target.path = $$[QT_INSTALL_EXAMPLES]/scxml/trafficlight-qml-static
INSTALLS += target
-
diff --git a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qml b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qml
deleted file mode 100644
index e0bd89d..0000000
--- a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qml
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-import TrafficLightStateMachine 1.0
-
-TrafficLight {
- stateMachine: TrafficLightStateMachine {
- running: true
- }
-}
diff --git a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc b/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc
deleted file mode 100644
index 890b4a7..0000000
--- a/examples/scxml/trafficlight-qml-static/trafficlight-qml-static.qrc
+++ /dev/null
@@ -1,14 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>trafficlight-qml-static.qml</file>
- <file alias="TrafficLight.qml">../trafficlight-common/TrafficLight.qml</file>
- <file alias="Button.qml">../trafficlight-common/Button.qml</file>
- <file alias="Lights.ui.qml">../trafficlight-common/Lights.ui.qml</file>
- <file alias="green.png">../trafficlight-common/green.png</file>
- <file alias="yellow.png">../trafficlight-common/yellow.png</file>
- <file alias="red.png">../trafficlight-common/red.png</file>
- <file alias="pause.png">../trafficlight-common/pause.png</file>
- <file alias="play.png">../trafficlight-common/play.png</file>
- <file alias="background.png">../trafficlight-common/background.png</file>
- </qresource>
-</RCC>
diff --git a/examples/scxml/trafficlight-qml-static/trafficlight-qml.h b/examples/scxml/trafficlight-qml-static/trafficlight-qml.h
new file mode 100644
index 0000000..ac309bd
--- /dev/null
+++ b/examples/scxml/trafficlight-qml-static/trafficlight-qml.h
@@ -0,0 +1,20 @@
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef TRAFFICLIGHT_QML
+#define TRAFFICLIGHT_QML
+
+#include "statemachine.h"
+
+#include <QtQml/qqml.h>
+#include <QtCore/qobject.h>
+
+struct TrafficLightStateMachineRegistration
+{
+ Q_GADGET
+ QML_FOREIGN(TrafficLightStateMachine)
+ QML_NAMED_ELEMENT(TrafficLightStateMachine)
+ QML_ADDED_IN_VERSION(1, 0)
+};
+
+#endif // TRAFFICLIGHT_QML
diff --git a/examples/scxml/trafficlight-widgets-dynamic/CMakeLists.txt b/examples/scxml/trafficlight-widgets-dynamic/CMakeLists.txt
new file mode 100644
index 0000000..022e2a3
--- /dev/null
+++ b/examples/scxml/trafficlight-widgets-dynamic/CMakeLists.txt
@@ -0,0 +1,84 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(trafficlight-widgets-dynamic LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/trafficlight-widgets-dynamic")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Scxml Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(trafficlight-widgets-dynamic
+ ../trafficlight-common/trafficlight.cpp ../trafficlight-common/trafficlight.h
+ trafficlight-widgets-dynamic.cpp
+)
+
+set_target_properties(trafficlight-widgets-dynamic PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(trafficlight-widgets-dynamic PRIVATE
+ Qt6::Core
+ Qt6::Gui
+ Qt6::Scxml
+ Qt6::Widgets
+)
+
+# Resources:
+set_source_files_properties("../trafficlight-common/background.png"
+ PROPERTIES QT_RESOURCE_ALIAS "background.png"
+)
+
+set_source_files_properties("../trafficlight-common/green.png"
+ PROPERTIES QT_RESOURCE_ALIAS "green.png"
+)
+
+set_source_files_properties("../trafficlight-common/pause.png"
+ PROPERTIES QT_RESOURCE_ALIAS "pause.png"
+)
+
+set_source_files_properties("../trafficlight-common/play.png"
+ PROPERTIES QT_RESOURCE_ALIAS "play.png"
+)
+
+set_source_files_properties("../trafficlight-common/red.png"
+ PROPERTIES QT_RESOURCE_ALIAS "red.png"
+)
+
+set_source_files_properties("../trafficlight-common/statemachine.scxml"
+ PROPERTIES QT_RESOURCE_ALIAS "statemachine.scxml"
+)
+
+set_source_files_properties("../trafficlight-common/yellow.png"
+ PROPERTIES QT_RESOURCE_ALIAS "yellow.png"
+)
+
+set(trafficlight-widgets-dynamic_resource_files
+ "../trafficlight-common/background.png"
+ "../trafficlight-common/green.png"
+ "../trafficlight-common/pause.png"
+ "../trafficlight-common/play.png"
+ "../trafficlight-common/red.png"
+ "../trafficlight-common/statemachine.scxml"
+ "../trafficlight-common/yellow.png"
+)
+
+qt6_add_resources(trafficlight-widgets-dynamic "trafficlight-widgets-dynamic"
+ PREFIX
+ "/"
+ FILES
+ ${trafficlight-widgets-dynamic_resource_files}
+)
+
+install(TARGETS trafficlight-widgets-dynamic
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/trafficlight-widgets-dynamic/doc/src/trafficlight-widgets-dynamic.qdoc b/examples/scxml/trafficlight-widgets-dynamic/doc/src/trafficlight-widgets-dynamic.qdoc
index de37ae0..45f9733 100644
--- a/examples/scxml/trafficlight-widgets-dynamic/doc/src/trafficlight-widgets-dynamic.qdoc
+++ b/examples/scxml/trafficlight-widgets-dynamic/doc/src/trafficlight-widgets-dynamic.qdoc
@@ -1,33 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example trafficlight-widgets-dynamic
- \title Qt SCXML Traffic Light Example (Dynamic)
+ \title SCXML Traffic Light (Dynamic, Widgets)
+ \examplecategory {Data Processing & I/O}
\ingroup examples-qtscxml
\brief A widget-based application that uses a dynamically loaded state
@@ -35,7 +12,7 @@
\image trafficlight.png
- \e{Traffic Light Example (Dynamic)} demonstrates how to connect to the
+ \e{Traffic Light} demonstrates how to connect to the
active properties of a state in a dynamically loaded state machine.
The UI is created using Qt Widgets.
@@ -44,13 +21,21 @@
\section1 Dynamically Loading the State Machine
- We link against the Qt SCXML module by adding the following line to the
- example \e .pro file:
+ We link against the Qt SCXML module by adding the following lines to the
+ example's build files.
+ To \e .pro when using qmake:
\quotefromfile trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.pro
\skipto QT
\printline scxml
+ To \e CMakeLists.txt when using cmake:
+ \quotefromfile trafficlight-widgets-dynamic/CMakeLists.txt
+ \skipto find_package
+ \printline Scxml
+ \skipto target_link_libraries
+ \printuntil )
+
We dynamically create the state machine in
\e trafficlight-widgets-dynamic.cpp:
diff --git a/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp b/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp
index 10f52dc..fbd02dd 100644
--- a/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp
+++ b/examples/scxml/trafficlight-widgets-dynamic/trafficlight-widgets-dynamic.cpp
@@ -1,71 +1,26 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "../trafficlight-common/trafficlight.h"
-#include <QApplication>
-#include <QTextStream>
+#include <QtWidgets/qapplication.h>
+#include <QtCore/qiodevice.h>
+#include <QtCore/qtextstream.h>
+
+using namespace Qt::Literals::StringLiterals;
int main(int argc, char **argv)
{
QApplication app(argc, argv);
- QScxmlStateMachine *machine = QScxmlStateMachine::fromFile(
- QStringLiteral(":statemachine.scxml"));
+ QScxmlStateMachine *machine = QScxmlStateMachine::fromFile(u":statemachine.scxml"_s);
+
if (!machine->parseErrors().isEmpty()) {
QTextStream errs(stderr, QIODevice::WriteOnly);
const auto errors = machine->parseErrors();
for (const QScxmlError &error : errors) {
errs << error.toString();
}
-
return -1;
}
diff --git a/examples/scxml/trafficlight-widgets-static/CMakeLists.txt b/examples/scxml/trafficlight-widgets-static/CMakeLists.txt
new file mode 100644
index 0000000..a133229
--- /dev/null
+++ b/examples/scxml/trafficlight-widgets-static/CMakeLists.txt
@@ -0,0 +1,84 @@
+# Copyright (C) 2023 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(trafficlight-widgets-static LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/scxml/trafficlight-widgets-static")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui Scxml Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(trafficlight-widgets-static
+ ../trafficlight-common/trafficlight.cpp ../trafficlight-common/trafficlight.h
+ trafficlight-widgets-static.cpp
+)
+
+set_target_properties(trafficlight-widgets-static PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(trafficlight-widgets-static PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::Scxml
+ Qt::Widgets
+)
+
+# Resources:
+set_source_files_properties("../trafficlight-common/background.png"
+ PROPERTIES QT_RESOURCE_ALIAS "background.png"
+)
+
+set_source_files_properties("../trafficlight-common/green.png"
+ PROPERTIES QT_RESOURCE_ALIAS "green.png"
+)
+
+set_source_files_properties("../trafficlight-common/pause.png"
+ PROPERTIES QT_RESOURCE_ALIAS "pause.png"
+)
+
+set_source_files_properties("../trafficlight-common/play.png"
+ PROPERTIES QT_RESOURCE_ALIAS "play.png"
+)
+
+set_source_files_properties("../trafficlight-common/red.png"
+ PROPERTIES QT_RESOURCE_ALIAS "red.png"
+)
+
+set_source_files_properties("../trafficlight-common/yellow.png"
+ PROPERTIES QT_RESOURCE_ALIAS "yellow.png"
+)
+
+set(trafficlight-widgets-static_resource_files
+ "../trafficlight-common/background.png"
+ "../trafficlight-common/green.png"
+ "../trafficlight-common/pause.png"
+ "../trafficlight-common/play.png"
+ "../trafficlight-common/red.png"
+ "../trafficlight-common/yellow.png"
+)
+
+qt6_add_resources(trafficlight-widgets-static "trafficlight-widgets-static"
+ PREFIX
+ "/"
+ FILES
+ ${trafficlight-widgets-static_resource_files}
+)
+
+# Statecharts:
+qt6_add_statecharts(trafficlight-widgets-static
+ ../trafficlight-common/statemachine.scxml
+)
+
+install(TARGETS trafficlight-widgets-static
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/scxml/trafficlight-widgets-static/doc/src/trafficlight-widgets-static.qdoc b/examples/scxml/trafficlight-widgets-static/doc/src/trafficlight-widgets-static.qdoc
index a807f22..a79da0f 100644
--- a/examples/scxml/trafficlight-widgets-static/doc/src/trafficlight-widgets-static.qdoc
+++ b/examples/scxml/trafficlight-widgets-static/doc/src/trafficlight-widgets-static.qdoc
@@ -1,33 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\example trafficlight-widgets-static
- \title Qt SCXML Traffic Light Example (Static)
+ \title SCXML Traffic Light (Static, Widgets)
+ \examplecategory {Data Processing & I/O}
\ingroup examples-qtscxml
\brief A widget-based application that uses a compiled state machine to
diff --git a/examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.cpp b/examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.cpp
index 67b5018..905a3c9 100644
--- a/examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.cpp
+++ b/examples/scxml/trafficlight-widgets-static/trafficlight-widgets-static.cpp
@@ -1,57 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2023 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
#include "statemachine.h"
#include "../trafficlight-common/trafficlight.h"
-#include <QApplication>
+#include <QtWidgets/qapplication.h>
int main(int argc, char **argv)
{
diff --git a/examples/statemachine/CMakeLists.txt b/examples/statemachine/CMakeLists.txt
new file mode 100644
index 0000000..1e5c452
--- /dev/null
+++ b/examples/statemachine/CMakeLists.txt
@@ -0,0 +1,13 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+if(NOT ANDROID)
+ qt_internal_add_example(pingpong)
+endif()
+if(TARGET Qt::Widgets)
+ qt_internal_add_example(rogue)
+ qt_internal_add_example(trafficlight)
+ if(QT_FEATURE_animation)
+ qt_internal_add_example(moveblocks)
+ endif()
+endif()
diff --git a/examples/statemachine/doc/images/pingpong-example.png b/examples/statemachine/doc/images/pingpong-example.png
new file mode 100644
index 0000000..af707e4
--- /dev/null
+++ b/examples/statemachine/doc/images/pingpong-example.png
Binary files differ
diff --git a/examples/statemachine/doc/images/trafficlight-example1.png b/examples/statemachine/doc/images/trafficlight-example1.png
new file mode 100644
index 0000000..ec8c7ff
--- /dev/null
+++ b/examples/statemachine/doc/images/trafficlight-example1.png
Binary files differ
diff --git a/examples/statemachine/doc/images/trafficlight-example2.png b/examples/statemachine/doc/images/trafficlight-example2.png
new file mode 100644
index 0000000..a12e4db
--- /dev/null
+++ b/examples/statemachine/doc/images/trafficlight-example2.png
Binary files differ
diff --git a/examples/statemachine/doc/src/moveblocks.qdoc b/examples/statemachine/doc/src/moveblocks.qdoc
new file mode 100644
index 0000000..d8645bb
--- /dev/null
+++ b/examples/statemachine/doc/src/moveblocks.qdoc
@@ -0,0 +1,192 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example moveblocks
+ \title Move Blocks
+ \ingroup examples-qtstatemachine
+ \examplecategory {Data Processing & I/O}
+
+ \brief The Move Blocks example shows how to animate items in a
+ QGraphicsScene using a QStateMachine with a custom transition.
+
+ \image moveblocks-example.png
+
+ The example animates the blue blocks that you can see in the image
+ above. The animation moves the blocks between four preset positions.
+
+ The example consists of the following classes:
+
+ \list
+ \li \c StateSwitcher inherits QState and can add
+ \c {StateSwitchTransition}s to other states.
+ When entered, it will randomly transition to one of these
+ states.
+ \li \c StateSwitchTransition is a custom transition that
+ triggers on \c{StateSwitchEvent}s.
+ \li \c StateSwitchEvent is a QEvent that triggers \c{StateSwitchTransition}s.
+ \li \c QGraphicsRectWidget is a QGraphicsWidget that simply
+ paints its background in a solid \l{Qt::}{blue} color.
+ \endlist
+
+ The blocks are instances of \c QGraphicsRectWidget and are
+ animated in a QGraphicsScene. We do this by building a state
+ graph, which we insert animations into. The graph is then executed
+ in a QStateMachine. All this is done in \c main().
+ Let's look at the \c main() function first.
+
+ \section1 The \c main() Function
+
+ After QApplication has been initialized, we set up the
+ QGraphicsScene with its \c{QGraphicsRectWidget}s.
+
+ \snippet moveblocks/main.cpp 1
+
+ After adding the scene to a QGraphicsView, it is time to build the
+ state graph. Let's first look at a statechart of what we are
+ trying to build.
+
+ \image move-blocks-chart.png
+
+ Note that the \c group has seven sub states, but we have only
+ included three of them in the diagram. The code that builds this
+ graph will be examined line-by-line, and will show how the graph
+ works. First off, we construct the \c group state:
+
+ \snippet moveblocks/main.cpp 2
+
+ The timer is used to add a delay between each time the blocks are
+ moved. The timer is started when \c group is entered. As we will
+ see later, \c group has a transition back to the \c StateSwitcher
+ when the timer times out. \c group is the initial state in the
+ machine, so an animation will be scheduled when the example is
+ started.
+
+ \snippet moveblocks/main.cpp 3
+ \dots
+ \snippet moveblocks/main.cpp 4
+
+ \c createGeometryState() returns a QState that will set the
+ geometry of our items upon entry. It also assigns \c group as the
+ parent of this state.
+
+ A QPropertyAnimation inserted into a transition will use the
+ values assigned to a QState (with QState::assignProperty()), i.e.,
+ the animation will interpolate between the current values of the
+ properties and the values in the target state. We add animated
+ transitions to the state graph later.
+
+ \snippet moveblocks/main.cpp 5
+
+ We move the items in parallel. Each item is added to \c
+ animationGroup, which is the animation that is inserted into the
+ transitions.
+
+ \snippet moveblocks/main.cpp 6
+
+ The sequential animation group, \c subGroup, helps us insert a
+ delay between the animation of each item.
+
+ \snippet moveblocks/main.cpp 7
+ \dots
+ \snippet moveblocks/main.cpp 8
+
+ A StateSwitchTransition is added to the state switcher
+ in \c StateSwitcher::addState(). We also add the animation in this
+ function. Since QPropertyAnimation uses the values from the
+ states, we can insert the same QPropertyAnimation instance in all
+ \c {StateSwitchTransition}s.
+
+ As mentioned previously, we add a transition to the state switcher
+ that triggers when the timer times out.
+
+ \snippet moveblocks/main.cpp 9
+
+ Finally, we can create the state machine, add our initial state,
+ and start execution of the state graph.
+
+ \section2 The \c createGeometryState() Function
+
+ In \c createGeometryState(), we set up the geometry for each
+ graphics item.
+
+ \snippet moveblocks/main.cpp 13
+
+ As mentioned before, QAbstractTransition will set up an animation
+ added with \l [CPP] {QAbstractTransition::}{addAnimation()} using
+ property values set with \l{QState::}{assignProperty()}.
+
+ \section1 The StateSwitcher Class
+
+ \c StateSwitcher has state switch transitions to each \l{QState}s
+ we created with \c createGeometryState(). Its job is to transition
+ to one of these states at random when it is entered.
+
+ All functions in \c StateSwitcher are inlined. We'll step through
+ its definition.
+
+ \snippet moveblocks/main.cpp 10
+
+ \c StateSwitcher is a state designed for a particular purpose and
+ will always be a top-level state. We use \c m_stateCount to keep
+ track of how many states we are managing, and \c m_lastIndex to
+ remember which state was the last state to which we transitioned.
+
+ \snippet moveblocks/main.cpp 11
+
+ We select the next state we are going to transition to, and post a
+ \c StateSwitchEvent, which we know will trigger the \c
+ StateSwitchTransition to the selected state.
+
+ \snippet moveblocks/main.cpp 12
+
+ This is where the magic happens. We assign a number to each state
+ added. This number is given to both a StateSwitchTransition and to
+ StateSwitchEvents. As we have seen, state switch events will
+ trigger a transition with the same number.
+
+ \section1 The StateSwitchTransition Class
+
+ \c StateSwitchTransition inherits QAbstractTransition and triggers
+ on \c{StateSwitchEvent}s. It contains only inline functions, so
+ let's take a look at its \l [CPP] {QAbstractTransition::}{eventTest()}
+ function, which is the only function that we define..
+
+ \snippet moveblocks/main.cpp 14
+
+ \c eventTest is called by QStateMachine when it checks whether a
+ transition should be triggered--a return value of true means that
+ it will. We simply check if our assigned number is equal to the
+ event's number (in which case we fire away).
+
+ \section1 The StateSwitchEvent Class
+
+ \c StateSwitchEvent inherits QEvent, and holds a number that has
+ been assigned to a state and state switch transition by
+ \c StateSwitcher. We have already seen how it is used to trigger
+ \c{StateSwitchTransition}s in \c StateSwitcher.
+
+ \snippet moveblocks/main.cpp 15
+
+ We only have inlined functions in this class, so a look at its
+ definition will do.
+
+ \section1 The QGraphicsRectWidget Class
+
+ QGraphicsRectWidget inherits QGraphicsWidget and simply paints its
+ \l{QWidget::}{rect()} blue. We inline \l{QWidget::}{paintEvent()},
+ which is the only function we define. Here is the
+ QGraphicsRectWidget class definition:
+
+ \snippet moveblocks/main.cpp 16
+
+ \section1 Moving On
+
+ The technique shown in this example works equally well for all
+ \l{QPropertyAnimation}s. As long as the value to be animated is a
+ Qt property, you can insert an animation of it into a state graph.
+
+ QState::addAnimation() takes a QAbstractAnimation, so any type
+ of animation can be inserted into the graph.
+*/
+
diff --git a/examples/statemachine/doc/src/pingpong.qdoc b/examples/statemachine/doc/src/pingpong.qdoc
new file mode 100644
index 0000000..71cfb2e
--- /dev/null
+++ b/examples/statemachine/doc/src/pingpong.qdoc
@@ -0,0 +1,68 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example pingpong
+ \title Ping Pong States
+ \ingroup examples-qtstatemachine
+ \examplecategory {Data Processing & I/O}
+
+ \brief The Ping Pong States example shows how to use parallel states together
+ with custom events and transitions in \l{Qt State Machine Overview}{Qt State Machine Framework}.
+
+ This example implements a statechart where two states communicate by
+ posting events to the state machine. The state chart looks as follows:
+
+ \image pingpong-example.png
+
+ The \c pinger and \c ponger states are parallel states, i.e. they are
+ entered simultaneously and will take transitions independently of
+ each other.
+
+ The \c pinger state will post the first \c ping event upon entry; the \c
+ ponger state will respond by posting a \c pong event; this will cause the
+ \c pinger state to post a new \c ping event; and so on.
+
+ \snippet pingpong/main.cpp 0
+
+ Two custom events are defined, \c PingEvent and \c PongEvent.
+
+ \snippet pingpong/main.cpp 1
+
+ The \c Pinger class defines a state that posts a \c PingEvent to the state
+ machine when the state is entered.
+
+ \snippet pingpong/main.cpp 2
+
+ The \c PingTransition class defines a transition that is triggered by
+ events of type \c PingEvent, and that posts a \c PongEvent (with a delay
+ of 500 milliseconds) to the state machine when the transition is
+ triggered.
+
+ \snippet pingpong/main.cpp 3
+
+ The \c PongTransition class defines a transition that is triggered by
+ events of type \c PongEvent, and that posts a \c PingEvent (with a delay
+ of 500 milliseconds) to the state machine when the transition is
+ triggered.
+
+ \snippet pingpong/main.cpp 4
+
+ The main() function begins by creating a state machine and a parallel
+ state group.
+
+ \snippet pingpong/main.cpp 5
+
+ Next, the \c pinger and \c ponger states are created, with the parallel
+ state group as their parent state. Note that the transitions are \e
+ targetless. When such a transition is triggered, the source state won't be
+ exited and re-entered; only the transition's onTransition() function will
+ be called, and the state machine's configuration will remain the same,
+ which is precisely what we want in this case.
+
+ \snippet pingpong/main.cpp 6
+
+ Finally, the group is added to the state machine, the machine is started,
+ and the application event loop is entered.
+
+ */
diff --git a/examples/statemachine/doc/src/rogue.qdoc b/examples/statemachine/doc/src/rogue.qdoc
new file mode 100644
index 0000000..be1a7e1
--- /dev/null
+++ b/examples/statemachine/doc/src/rogue.qdoc
@@ -0,0 +1,185 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example rogue
+ \title Rogue
+ \ingroup examples-qtstatemachine
+ \examplecategory {Data Processing & I/O}
+
+ \brief The Rogue example shows how to use the Qt state machine for event
+ handling.
+
+ \image rogue-example.png
+
+ This example implements a simple text based game. Do you see the
+ \c{@} in the screenshot? That's you, the rogue. The \c{#}
+ characters are walls, and the dots represent floor. In a real
+ game, other ASCII characters would represent all kinds of objects
+ and creatures, for instance, ancient dragons (\c{D}s) or food
+ rations (\c{%}s). But let's not get carried away. In this game,
+ the rogue is simply running around in an empty room.
+
+ The rogue is moved with the keypad (2, 4, 8, 6). That aside, we
+ have implemented a \c quit command that triggers if the player
+ types \c {q}. The player is then asked if he/she really wants to
+ quit.
+
+ Most games have commands that need more than one key press (we
+ think of consecutive presses, i.e., not of several keys being
+ pressed at the same time). In this game, only the \c quit command
+ falls under this category, but for the sake of argument, let's
+ imagine a fully-fledged game with a rich set of commands. If we
+ were to implement these by catching key events in
+ \l{QWidget::}{keyPressEvent()}, we would have to keep a lot of
+ class member variables to track the sequence of keys already typed
+ (or find some other way of deducing the current state of a
+ command). This can easily lead to spaghetti, which is--as we all
+ well know, I'm sure--unpleasant. With a state machine, on the
+ other hand, separate states can wait for a single key press, and
+ that makes our lives a lot simpler.
+
+ The example consists of two classes:
+
+ \list
+ \li \c Window draws the text display of the game and sets
+ up the state machine. The window also has a status bar
+ above the area in which the rouge moves.
+ \li \c MovementTransition is a transition that carries out
+ a single move of the rogue.
+ \endlist
+
+ Before we embark on a code walkthrough, it is necessary to take a
+ closer look at the design of the machine. Here is a state chart
+ that shows what we want to achieve:
+
+ \image rogue-statechart.png
+
+ The input state waits for a key press to start a new command.
+ When receiving a key it recognizes, it transitions to one of the
+ two commands of the game; though, as we will see, movement is
+ handled by the transition itself. The quit state waits for the
+ player to answer yes or no (by typing \c y or \c n) when asked
+ whether he/she really wants to quit the game.
+
+ The chart demonstrates how we use one state to wait for a single
+ key press. The press received may trigger one of the transitions
+ connected to the state.
+
+ \section1 Window Class Definition
+
+ The \c Window class is a widget that draws the text display of the
+ game. It also sets up the state machine, i.e., creates and
+ connects the states in the machine. It is the key events from this
+ widget that are used by the machine.
+
+ \snippet rogue/window.h 0
+
+ \c Direction specifies the direction in which the rogue is to
+ move. We use this in \c movePlayer(), which moves the rogue and
+ repaints the window. The game has a status line above the area in
+ which the rogue moves. The \c status property contains the text of
+ this line. We use a property because the QState class allows
+ setting any Qt \l{Qt's Property System}{property} when entered.
+ More on this later.
+
+ \snippet rogue/window.h 1
+
+ The \c map is an array with the characters that are currently
+ displayed. We set up the array in \c setupMap(), and update it
+ when the rogue is moved. \c pX and \c pY is the current position
+ of the rogue, initially set to (5, 5). \c WIDTH and \c HEIGHT are
+ constants specifying the dimensions of the map.
+
+ The \c paintEvent() function is left out of this walkthrough. We
+ also do not discuss other code that does not concern the state
+ machine (the \c setupMap(), \c status(), \c setStatus(), \c
+ movePlayer(), and \c sizeHint() functions). If you wish to take a
+ look at the code, click on the link for the \c window.cpp file at
+ the top of this page.
+
+ \section1 Window Class Implementation
+
+ Here is the constructor of \c Window:
+
+ \snippet rogue/window.cpp 0
+ \dots
+ \snippet rogue/window.cpp 1
+
+ Here we set up the map and statemachine. Let's proceed with the
+ \c buildMachine() function:
+
+ \snippet rogue/window.cpp 2
+
+ We enter \c inputState when the machine is started and from the \c
+ quitState if the user wants to continue playing. We then set the
+ status to a helpful reminder of how to play the game.
+
+ First, the \c Movement transition is added to the input state.
+ This will enable the rogue to be moved with the keypad. Notice
+ that we don't set a target state for the movement transition. This
+ will cause the transition to be triggered (and the
+ \l [CPP] {QAbstractTransition::}{onTransition()} function to be invoked),
+ but the machine will not leave the \c inputState. If we had set \c
+ inputState as the target state, we would first have left and then
+ entered the \c inputState again.
+
+ \snippet rogue/window.cpp 3
+
+ When we enter \c quitState, we update the status bar of the
+ window.
+
+ \c QKeyEventTransition is a utility class that removes the hassle
+ of implementing transitions for \l{QKeyEvent}s. We simply need to
+ specify the key on which the transition should trigger and the
+ target state of the transition.
+
+ \snippet rogue/window.cpp 4
+
+ The transition from \c inputState allows triggering the quit state
+ when the player types \c {q}.
+
+ \snippet rogue/window.cpp 5
+
+ The machine is set up, so it's time to start it.
+
+ \section1 The MovementTransition Class
+
+ \c MovementTransition is triggered when the player request the
+ rogue to be moved (by typing 2, 4, 6, or 8) when the machine is in
+ the \c inputState.
+
+ \snippet rogue/movementtransition.h 0
+
+ In the constructor, we tell QEventTransition to only send
+ \l{QEvent::}{KeyPress} events to the
+ \l [CPP] {QAbstractTransition::}{eventTest()} function:
+
+ \snippet rogue/movementtransition.h 1
+
+ The KeyPress events come wrapped in \l{QStateMachine::WrappedEvent}s. \c event
+ must be confirmed to be a wrapped event because Qt uses other
+ events internally. After that, it is simply a matter of checking
+ which key has been pressed.
+
+ Let's move on to the \c onTransition() function:
+
+ \snippet rogue/movementtransition.h 2
+
+ When \c onTransition() is invoked, we know that we have a
+ \l{QEvent::}{KeyPress} event with 2, 4, 6, or 8, and can ask \c
+ Window to move the player.
+
+ \section1 The Roguelike Tradition
+
+ You might have been wondering why the game features a rogue. Well,
+ these kinds of text based dungeon exploration games date back to a
+ game called, yes, "Rogue". Although outflanked by the technology
+ of modern 3D computer games, roguelikes have a solid community of
+ hard-core, devoted followers.
+
+ Playing these games can be surprisingly addictive (despite the
+ lack of graphics). Angband, the perhaps most well-known rougelike,
+ is found here: \l{http://rephial.org/}.
+*/
+
diff --git a/examples/statemachine/doc/src/trafficlight.qdoc b/examples/statemachine/doc/src/trafficlight.qdoc
new file mode 100644
index 0000000..cb5cfd1
--- /dev/null
+++ b/examples/statemachine/doc/src/trafficlight.qdoc
@@ -0,0 +1,57 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \example trafficlight
+ \title Traffic Light
+ \ingroup examples-qtstatemachine
+ \examplecategory {Data Processing & I/O}
+
+ \brief The Traffic Light example shows how to use \l{Qt State Machine Overview}
+ to implement the control flow of a traffic light.
+
+ \image trafficlight-example.png
+
+ In this example we write a TrafficLightWidget class. The traffic light has
+ three lights: Red, yellow and green. The traffic light transitions from
+ one light to another (red to yellow to green to yellow to red again) at
+ certain intervals.
+
+ \snippet trafficlight/main.cpp 0
+
+ The LightWidget class represents a single light of the traffic light. It
+ provides an \c on property and two slots, turnOn() and turnOff(), to turn
+ the light on and off, respectively. The widget paints itself in the color
+ that's passed to the constructor.
+
+ \snippet trafficlight/main.cpp 1
+
+ The TrafficLightWidget class represents the visual part of the traffic
+ light; it's a widget that contains three lights arranged vertically, and
+ provides accessor functions for these.
+
+ \snippet trafficlight/main.cpp 2
+
+ The createLightState() function creates a state that turns a light on when
+ the state is entered, and off when the state is exited. The state uses a
+ timer, and as we shall see the timeout is used to transition from one
+ LightState to another. Here is the statechart for the light state:
+
+ \image trafficlight-example1.png
+
+ \snippet trafficlight/main.cpp 3
+
+ The TrafficLight class combines the TrafficLightWidget with a state
+ machine. The state graph has four states: red-to-yellow, yellow-to-green,
+ green-to-yellow and yellow-to-red. The initial state is red-to-yellow;
+ when the state's timer times out, the state machine transitions to
+ yellow-to-green. The same process repeats through the other states.
+ This is what the statechart looks like:
+
+ \image trafficlight-example2.png
+
+ \snippet trafficlight/main.cpp 4
+
+ The main() function constructs a TrafficLight and shows it.
+
+*/
diff --git a/examples/statemachine/moveblocks/CMakeLists.txt b/examples/statemachine/moveblocks/CMakeLists.txt
new file mode 100644
index 0000000..1832dce
--- /dev/null
+++ b/examples/statemachine/moveblocks/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(moveblocks LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/animation/moveblocks")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui StateMachine Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(moveblocks
+ main.cpp
+)
+
+set_target_properties(moveblocks PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(moveblocks PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::StateMachine
+ Qt::Widgets
+)
+
+install(TARGETS moveblocks
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/statemachine/moveblocks/main.cpp b/examples/statemachine/moveblocks/main.cpp
new file mode 100644
index 0000000..294cda6
--- /dev/null
+++ b/examples/statemachine/moveblocks/main.cpp
@@ -0,0 +1,267 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore/QEvent>
+#include <QtCore/QParallelAnimationGroup>
+#include <QtCore/QPropertyAnimation>
+#include <QtCore/QRandomGenerator>
+#include <QtCore/QSequentialAnimationGroup>
+#include <QtCore/QTimer>
+#include <QtGui/QPainter>
+#include <QtStateMachine/QAbstractTransition>
+#include <QtStateMachine/QState>
+#include <QtStateMachine/QStateMachine>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QGraphicsView>
+#include <QtWidgets/QGraphicsWidget>
+
+//![15]
+class StateSwitchEvent: public QEvent
+{
+public:
+ explicit StateSwitchEvent(int rand) : QEvent(StateSwitchType), m_rand(rand) { }
+
+ static constexpr QEvent::Type StateSwitchType = QEvent::Type(QEvent::User + 256);
+
+ int rand() const { return m_rand; }
+
+private:
+ int m_rand;
+};
+//![15]
+
+//![16]
+class QGraphicsRectWidget : public QGraphicsWidget
+{
+public:
+ void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
+ {
+ painter->fillRect(rect(), Qt::blue);
+ }
+};
+//![16]
+
+class StateSwitchTransition: public QAbstractTransition
+{
+public:
+ explicit StateSwitchTransition(int rand) : QAbstractTransition(), m_rand(rand) { }
+
+protected:
+//![14]
+ bool eventTest(QEvent *event) override
+ {
+ return (event->type() == QEvent::Type(StateSwitchEvent::StateSwitchType))
+ && (static_cast<StateSwitchEvent *>(event)->rand() == m_rand);
+ }
+//![14]
+
+ void onTransition(QEvent *) override {}
+
+private:
+ int m_rand;
+};
+
+//![10]
+class StateSwitcher : public QState
+{
+ Q_OBJECT
+public:
+ explicit StateSwitcher(QStateMachine *machine) : QState(machine) { }
+//![10]
+
+//![11]
+ void onEntry(QEvent *) override
+ {
+ int n;
+ while ((n = QRandomGenerator::global()->bounded(m_stateCount) + 1) == m_lastIndex)
+ { }
+ m_lastIndex = n;
+ machine()->postEvent(new StateSwitchEvent(n));
+ }
+ void onExit(QEvent *) override {}
+//![11]
+
+//![12]
+ void addState(QState *state, QAbstractAnimation *animation) {
+ auto trans = new StateSwitchTransition(++m_stateCount);
+ trans->setTargetState(state);
+ addTransition(trans);
+ trans->addAnimation(animation);
+ }
+//![12]
+
+private:
+ int m_stateCount = 0;
+ int m_lastIndex = 0;
+};
+
+//![13]
+static QState *createGeometryState(QObject *w1, const QRect &rect1, QObject *w2, const QRect &rect2,
+ QObject *w3, const QRect &rect3, QObject *w4, const QRect &rect4,
+ QState *parent)
+{
+ auto result = new QState(parent);
+ result->assignProperty(w1, "geometry", rect1);
+ result->assignProperty(w2, "geometry", rect2);
+ result->assignProperty(w3, "geometry", rect3);
+ result->assignProperty(w4, "geometry", rect4);
+
+ return result;
+}
+//![13]
+
+
+class GraphicsView : public QGraphicsView
+{
+ Q_OBJECT
+public:
+ explicit GraphicsView(QGraphicsScene *scene, QWidget *parent = nullptr)
+ : QGraphicsView(scene, parent)
+ {
+ }
+
+protected:
+ void resizeEvent(QResizeEvent *event) override
+ {
+ fitInView(scene()->sceneRect());
+ QGraphicsView::resizeEvent(event);
+ }
+};
+
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+//![1]
+ auto button1 = new QGraphicsRectWidget;
+ auto button2 = new QGraphicsRectWidget;
+ auto button3 = new QGraphicsRectWidget;
+ auto button4 = new QGraphicsRectWidget;
+ button2->setZValue(1);
+ button3->setZValue(2);
+ button4->setZValue(3);
+ QGraphicsScene scene(0, 0, 300, 300);
+ scene.setBackgroundBrush(Qt::black);
+ scene.addItem(button1);
+ scene.addItem(button2);
+ scene.addItem(button3);
+ scene.addItem(button4);
+//![1]
+ GraphicsView window(&scene);
+ window.setFrameStyle(0);
+ window.setAlignment(Qt::AlignLeft | Qt::AlignTop);
+ window.setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+ window.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+//![2]
+ QStateMachine machine;
+
+ auto group = new QState;
+ group->setObjectName("group");
+ QTimer timer;
+ timer.setInterval(1250);
+ timer.setSingleShot(true);
+ QObject::connect(group, &QState::entered, &timer, QOverload<>::of(&QTimer::start));
+//![2]
+
+//![3]
+ auto state1 = createGeometryState(button1, QRect(100, 0, 50, 50),
+ button2, QRect(150, 0, 50, 50),
+ button3, QRect(200, 0, 50, 50),
+ button4, QRect(250, 0, 50, 50),
+ group);
+//![3]
+ auto state2 = createGeometryState(button1, QRect(250, 100, 50, 50),
+ button2, QRect(250, 150, 50, 50),
+ button3, QRect(250, 200, 50, 50),
+ button4, QRect(250, 250, 50, 50),
+ group);
+ auto state3 = createGeometryState(button1, QRect(150, 250, 50, 50),
+ button2, QRect(100, 250, 50, 50),
+ button3, QRect(50, 250, 50, 50),
+ button4, QRect(0, 250, 50, 50),
+ group);
+ auto state4 = createGeometryState(button1, QRect(0, 150, 50, 50),
+ button2, QRect(0, 100, 50, 50),
+ button3, QRect(0, 50, 50, 50),
+ button4, QRect(0, 0, 50, 50),
+ group);
+ auto state5 = createGeometryState(button1, QRect(100, 100, 50, 50),
+ button2, QRect(150, 100, 50, 50),
+ button3, QRect(100, 150, 50, 50),
+ button4, QRect(150, 150, 50, 50),
+ group);
+ auto state6 = createGeometryState(button1, QRect(50, 50, 50, 50),
+ button2, QRect(200, 50, 50, 50),
+ button3, QRect(50, 200, 50, 50),
+ button4, QRect(200, 200, 50, 50),
+ group);
+//![4]
+ auto state7 = createGeometryState(button1, QRect(0, 0, 50, 50),
+ button2, QRect(250, 0, 50, 50),
+ button3, QRect(0, 250, 50, 50),
+ button4, QRect(250, 250, 50, 50),
+ group);
+ group->setInitialState(state1);
+//![4]
+
+//![5]
+ QParallelAnimationGroup animationGroup;
+
+ auto anim = new QPropertyAnimation(button4, "geometry");
+ anim->setDuration(1000);
+ anim->setEasingCurve(QEasingCurve::OutElastic);
+ animationGroup.addAnimation(anim);
+//![5]
+
+//![6]
+ auto subGroup = new QSequentialAnimationGroup(&animationGroup);
+ subGroup->addPause(100);
+ anim = new QPropertyAnimation(button3, "geometry");
+ anim->setDuration(1000);
+ anim->setEasingCurve(QEasingCurve::OutElastic);
+ subGroup->addAnimation(anim);
+//![6]
+
+ subGroup = new QSequentialAnimationGroup(&animationGroup);
+ subGroup->addPause(150);
+ anim = new QPropertyAnimation(button2, "geometry");
+ anim->setDuration(1000);
+ anim->setEasingCurve(QEasingCurve::OutElastic);
+ subGroup->addAnimation(anim);
+
+ subGroup = new QSequentialAnimationGroup(&animationGroup);
+ subGroup->addPause(200);
+ anim = new QPropertyAnimation(button1, "geometry");
+ anim->setDuration(1000);
+ anim->setEasingCurve(QEasingCurve::OutElastic);
+ subGroup->addAnimation(anim);
+
+//![7]
+ auto stateSwitcher = new StateSwitcher(&machine);
+ stateSwitcher->setObjectName("stateSwitcher");
+ group->addTransition(&timer, &QTimer::timeout, stateSwitcher);
+ stateSwitcher->addState(state1, &animationGroup);
+ stateSwitcher->addState(state2, &animationGroup);
+//![7]
+ stateSwitcher->addState(state3, &animationGroup);
+ stateSwitcher->addState(state4, &animationGroup);
+ stateSwitcher->addState(state5, &animationGroup);
+ stateSwitcher->addState(state6, &animationGroup);
+//![8]
+ stateSwitcher->addState(state7, &animationGroup);
+//![8]
+
+//![9]
+ machine.addState(group);
+ machine.setInitialState(group);
+ machine.start();
+//![9]
+
+ window.resize(300, 300);
+ window.show();
+
+ return app.exec();
+}
+
+#include "main.moc"
diff --git a/examples/statemachine/moveblocks/moveblocks.pro b/examples/statemachine/moveblocks/moveblocks.pro
new file mode 100644
index 0000000..405a949
--- /dev/null
+++ b/examples/statemachine/moveblocks/moveblocks.pro
@@ -0,0 +1,7 @@
+QT += statemachine widgets
+
+SOURCES = main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/animation/moveblocks
+INSTALLS += target
diff --git a/examples/statemachine/pingpong/CMakeLists.txt b/examples/statemachine/pingpong/CMakeLists.txt
new file mode 100644
index 0000000..db1324e
--- /dev/null
+++ b/examples/statemachine/pingpong/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(pingpong LANGUAGES CXX)
+
+if (ANDROID)
+ message(FATAL_ERROR "This project cannot be built on Android.")
+endif()
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/statemachine/pingpong")
+
+find_package(Qt6 REQUIRED COMPONENTS Core StateMachine)
+
+qt_standard_project_setup()
+
+qt_add_executable(pingpong
+ main.cpp
+)
+
+set_target_properties(pingpong PROPERTIES
+ WIN32_EXECUTABLE FALSE
+ MACOSX_BUNDLE FALSE
+)
+
+target_link_libraries(pingpong PRIVATE
+ Qt::Core
+ Qt::StateMachine
+)
+
+install(TARGETS pingpong
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/statemachine/pingpong/main.cpp b/examples/statemachine/pingpong/main.cpp
new file mode 100644
index 0000000..a042e8b
--- /dev/null
+++ b/examples/statemachine/pingpong/main.cpp
@@ -0,0 +1,104 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+#include <QtCore/QEvent>
+#include <QtStateMachine/QAbstractTransition>
+#include <QtStateMachine/QState>
+#include <QtStateMachine/QStateMachine>
+
+static constexpr QEvent::Type PingEventType = QEvent::Type(QEvent::User + 2);
+static constexpr QEvent::Type PongEventType = QEvent::Type(QEvent::User + 3);
+
+//! [0]
+class PingEvent : public QEvent
+{
+public:
+ PingEvent() : QEvent(PingEventType) { }
+};
+
+class PongEvent : public QEvent
+{
+public:
+ PongEvent() : QEvent(PongEventType) { }
+};
+//! [0]
+
+//! [1]
+class Pinger : public QState
+{
+public:
+ explicit Pinger(QState *parent) : QState(parent) { }
+
+protected:
+ void onEntry(QEvent *) override
+ {
+ machine()->postEvent(new PingEvent);
+ qInfo() << "ping?";
+ }
+};
+//! [1]
+
+//! [3]
+class PongTransition : public QAbstractTransition
+{
+public:
+ PongTransition() {}
+
+protected:
+ bool eventTest(QEvent *e) override { return (e->type() == PingEventType); }
+
+ void onTransition(QEvent *) override
+ {
+ machine()->postDelayedEvent(new PingEvent, 500);
+ qInfo() << "ping?";
+ }
+};
+//! [3]
+
+//! [2]
+class PingTransition : public QAbstractTransition
+{
+public:
+ PingTransition() {}
+
+protected:
+ bool eventTest(QEvent *e) override { return e->type() == PingEventType; }
+
+ void onTransition(QEvent *) override
+ {
+ machine()->postDelayedEvent(new PongEvent, 500);
+ qInfo() << "pong!";
+ }
+};
+//! [2]
+
+//! [4]
+int main(int argc, char **argv)
+{
+ QCoreApplication app(argc, argv);
+
+ QStateMachine machine;
+ auto group = new QState(QState::ParallelStates);
+ group->setObjectName("group");
+//! [4]
+
+//! [5]
+ auto pinger = new Pinger(group);
+ pinger->setObjectName("pinger");
+ pinger->addTransition(new PongTransition);
+
+ auto ponger = new QState(group);
+ ponger->setObjectName("ponger");
+ ponger->addTransition(new PingTransition);
+//! [5]
+
+//! [6]
+ machine.addState(group);
+ machine.setInitialState(group);
+ machine.start();
+
+ return app.exec();
+}
+//! [6]
diff --git a/examples/statemachine/pingpong/pingpong.pro b/examples/statemachine/pingpong/pingpong.pro
new file mode 100644
index 0000000..5c569bb
--- /dev/null
+++ b/examples/statemachine/pingpong/pingpong.pro
@@ -0,0 +1,8 @@
+QT = statemachine core
+CONFIG += cmdline
+
+SOURCES = main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/statemachine/pingpong
+INSTALLS += target
diff --git a/examples/statemachine/rogue/CMakeLists.txt b/examples/statemachine/rogue/CMakeLists.txt
new file mode 100644
index 0000000..870a682
--- /dev/null
+++ b/examples/statemachine/rogue/CMakeLists.txt
@@ -0,0 +1,39 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(rogue LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/statemachine/rogue")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui StateMachine Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(rogue
+ main.cpp
+ movementtransition.h
+ window.cpp window.h
+)
+
+set_target_properties(rogue PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(rogue PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::StateMachine
+ Qt::Widgets
+)
+
+install(TARGETS rogue
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/statemachine/rogue/main.cpp b/examples/statemachine/rogue/main.cpp
new file mode 100644
index 0000000..a6e4012
--- /dev/null
+++ b/examples/statemachine/rogue/main.cpp
@@ -0,0 +1,17 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtWidgets/QApplication>
+
+#include "window.h"
+
+int main(int argv, char **args)
+{
+ QApplication app(argv, args);
+
+ Window window;
+ window.show();
+
+ return app.exec();
+}
+
diff --git a/examples/statemachine/rogue/movementtransition.h b/examples/statemachine/rogue/movementtransition.h
new file mode 100644
index 0000000..62b82c1
--- /dev/null
+++ b/examples/statemachine/rogue/movementtransition.h
@@ -0,0 +1,77 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef MOVEMENTTRANSITION_H
+#define MOVEMENTTRANSITION_H
+
+#include <QtGui/QKeyEvent>
+#include <QtStateMachine/QEventTransition>
+#include <QtStateMachine/QStateMachine>
+
+#include "window.h"
+
+//![0]
+class MovementTransition : public QEventTransition
+{
+ Q_OBJECT
+
+public:
+ explicit MovementTransition(Window *window)
+ : QEventTransition(window, QEvent::KeyPress), window(window)
+ {
+ }
+//![0]
+
+//![1]
+protected:
+ bool eventTest(QEvent *event) override {
+ if (event->type() == QEvent::StateMachineWrapped &&
+ static_cast<QStateMachine::WrappedEvent *>(event)->event()->type() == QEvent::KeyPress) {
+ auto wrappedEvent = static_cast<QStateMachine::WrappedEvent *>(event)->event();
+
+ auto keyEvent = static_cast<QKeyEvent *>(wrappedEvent);
+ int key = keyEvent->key();
+
+ return key == Qt::Key_2 || key == Qt::Key_8 || key == Qt::Key_6 ||
+ key == Qt::Key_4 || key == Qt::Key_Down || key == Qt::Key_Up ||
+ key == Qt::Key_Right || key == Qt::Key_Left;
+ }
+ return false;
+ }
+//![1]
+
+//![2]
+ void onTransition(QEvent *event) override {
+ auto keyEvent = static_cast<QKeyEvent *>(
+ static_cast<QStateMachine::WrappedEvent *>(event)->event());
+
+ int key = keyEvent->key();
+ switch (key) {
+ case Qt::Key_Left:
+ case Qt::Key_4:
+ window->movePlayer(Window::Left);
+ break;
+ case Qt::Key_Up:
+ case Qt::Key_8:
+ window->movePlayer(Window::Up);
+ break;
+ case Qt::Key_Right:
+ case Qt::Key_6:
+ window->movePlayer(Window::Right);
+ break;
+ case Qt::Key_Down:
+ case Qt::Key_2:
+ window->movePlayer(Window::Down);
+ break;
+ default:
+ ;
+ }
+ }
+//![2]
+
+private:
+ Window *window;
+};
+
+#endif
+
diff --git a/examples/statemachine/rogue/rogue.pro b/examples/statemachine/rogue/rogue.pro
new file mode 100644
index 0000000..7260d76
--- /dev/null
+++ b/examples/statemachine/rogue/rogue.pro
@@ -0,0 +1,10 @@
+QT += statemachine widgets
+
+HEADERS = window.h \
+ movementtransition.h
+SOURCES = main.cpp \
+ window.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/statemachine/rogue
+INSTALLS += target
diff --git a/examples/statemachine/rogue/window.cpp b/examples/statemachine/rogue/window.cpp
new file mode 100644
index 0000000..40761bd
--- /dev/null
+++ b/examples/statemachine/rogue/window.cpp
@@ -0,0 +1,201 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include "window.h"
+
+#include <QtCore/QRandomGenerator>
+#include <QtGui/QFont>
+#include <QtGui/QPainter>
+#include <QtStateMachine/QFinalState>
+#include <QtStateMachine/QKeyEventTransition>
+#include <QtWidgets/QApplication>
+
+#include <cmath>
+
+#include "movementtransition.h"
+
+//![0]
+Window::Window()
+{
+//![0]
+ QFont font("Monospace");
+ font.setStyleHint(QFont::TypeWriter);
+ font.setPointSize(12);
+ setFont(font);
+//![1]
+ setupMap();
+ buildMachine();
+}
+//![1]
+
+void Window::setStatus(const QString &status)
+{
+ myStatus = status;
+ repaint();
+}
+
+QString Window::status() const
+{
+ return myStatus;
+}
+
+void Window::paintEvent(QPaintEvent * /* event */)
+{
+ QFontMetrics metrics(font());
+ QPainter painter(this);
+ int fontHeight = metrics.height();
+ int fontWidth = metrics.horizontalAdvance('X');
+ int yPos = fontHeight;
+ int xPos;
+
+ painter.fillRect(rect(), Qt::black);
+ painter.setPen(Qt::white);
+
+ painter.drawText(QPoint(0, yPos), status());
+
+ for (int y = 0; y < HEIGHT; ++y) {
+ yPos += fontHeight;
+ xPos = 0;
+
+ for (int x = 0; x < WIDTH; ++x) {
+ if (y == pY && x == pX) {
+ xPos += fontWidth;
+ continue;
+ }
+
+ painter.setPen(Qt::white);
+
+ double x1 = static_cast<double>(pX);
+ double y1 = static_cast<double>(pY);
+ double x2 = static_cast<double>(x);
+ double y2 = static_cast<double>(y);
+
+ if (x2 < x1) {
+ x2 += 0.5;
+ } else if (x2 > x1) {
+ x2 -= 0.5;
+ }
+
+ if (y2 < y1) {
+ y2 += 0.5;
+ } else if (y2 > y1) {
+ y2 -= 0.5;
+ }
+
+ double dx = x2 - x1;
+ double dy = y2 - y1;
+
+ double length = std::hypot(dx, dy);
+
+ dx /= length;
+ dy /= length;
+
+ double xi = x1;
+ double yi = y1;
+
+ while (length > 0) {
+ int cx = static_cast<int>(xi + 0.5);
+ int cy = static_cast<int>(yi + 0.5);
+
+ if (x2 == cx && y2 == cy)
+ break;
+
+ if (!(x1 == cx && y1 == cy) && (map[cx][cy] == '#' || (length - 10) > 0)) {
+ painter.setPen(QColor(60, 60, 60));
+ break;
+ }
+
+ xi += dx;
+ yi += dy;
+ --length;
+ }
+
+ painter.drawText(QPoint(xPos, yPos), map[x][y]);
+ xPos += fontWidth;
+ }
+ }
+ painter.setPen(Qt::white);
+ painter.drawText(QPoint(pX * fontWidth, (pY + 2) * fontHeight), QChar('@'));
+}
+
+QSize Window::sizeHint() const
+{
+ QFontMetrics metrics(font());
+
+ return QSize(metrics.horizontalAdvance('X') * WIDTH, metrics.height() * (HEIGHT + 1));
+}
+
+//![2]
+void Window::buildMachine()
+{
+ machine = new QStateMachine;
+
+ auto inputState = new QState(machine);
+ inputState->assignProperty(this, "status", "Move the rogue with 2, 4, 6, and 8");
+
+ auto transition = new MovementTransition(this);
+ inputState->addTransition(transition);
+//![2]
+
+//![3]
+ auto quitState = new QState(machine);
+ quitState->assignProperty(this, "status", "Really quit(y/n)?");
+
+ auto yesTransition = new QKeyEventTransition(this, QEvent::KeyPress, Qt::Key_Y);
+ yesTransition->setTargetState(new QFinalState(machine));
+ quitState->addTransition(yesTransition);
+
+ auto noTransition = new QKeyEventTransition(this, QEvent::KeyPress, Qt::Key_N);
+ noTransition->setTargetState(inputState);
+ quitState->addTransition(noTransition);
+//![3]
+
+//![4]
+ auto quitTransition = new QKeyEventTransition(this, QEvent::KeyPress, Qt::Key_Q);
+ quitTransition->setTargetState(quitState);
+ inputState->addTransition(quitTransition);
+//![4]
+
+//![5]
+ machine->setInitialState(inputState);
+
+ connect(machine, &QStateMachine::finished, qApp, &QApplication::quit);
+
+ machine->start();
+}
+//![5]
+
+void Window::movePlayer(Direction direction)
+{
+ switch (direction) {
+ case Left:
+ if (map[pX - 1][pY] != '#')
+ --pX;
+ break;
+ case Right:
+ if (map[pX + 1][pY] != '#')
+ ++pX;
+ break;
+ case Up:
+ if (map[pX][pY - 1] != '#')
+ --pY;
+ break;
+ case Down:
+ if (map[pX][pY + 1] != '#')
+ ++pY;
+ break;
+ }
+ repaint();
+}
+
+void Window::setupMap()
+{
+ for (int x = 0; x < WIDTH; ++x)
+ for (int y = 0; y < HEIGHT; ++y) {
+ if (x == 0 || x == WIDTH - 1 || y == 0 || y == HEIGHT - 1 || QRandomGenerator::global()->bounded(40) == 0)
+ map[x][y] = '#';
+ else
+ map[x][y] = '.';
+ }
+}
+
diff --git a/examples/statemachine/rogue/window.h b/examples/statemachine/rogue/window.h
new file mode 100644
index 0000000..2db57ff
--- /dev/null
+++ b/examples/statemachine/rogue/window.h
@@ -0,0 +1,50 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#ifndef WINDOW_H
+#define WINDOW_H
+
+#include <QtWidgets/QWidget>
+
+QT_FORWARD_DECLARE_CLASS(QStateMachine);
+
+//![0]
+class Window : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(QString status READ status WRITE setStatus)
+
+public:
+ enum Direction { Up, Down, Left, Right };
+
+ Window();
+
+ void movePlayer(Direction direction);
+ void setStatus(const QString &status);
+ QString status() const;
+
+ QSize sizeHint() const override;
+
+protected:
+ void paintEvent(QPaintEvent *event) override;
+//![0]
+
+//![1]
+private:
+ void buildMachine();
+ void setupMap();
+
+ static constexpr int WIDTH = 35;
+ static constexpr int HEIGHT = 20;
+
+ QChar map[WIDTH][HEIGHT];
+ int pX = 5;
+ int pY = 5;
+
+ QStateMachine *machine;
+ QString myStatus;
+};
+//![1]
+
+#endif
+
diff --git a/examples/statemachine/statemachine.pro b/examples/statemachine/statemachine.pro
new file mode 100644
index 0000000..b635f8c
--- /dev/null
+++ b/examples/statemachine/statemachine.pro
@@ -0,0 +1,11 @@
+TEMPLATE = subdirs
+
+SUBDIRS += pingpong
+
+qtHaveModule(widgets) {
+ SUBDIRS += rogue \
+ trafficlight
+ qtConfig(animation) {
+ SUBDIRS += moveblocks
+ }
+}
diff --git a/examples/statemachine/trafficlight/CMakeLists.txt b/examples/statemachine/trafficlight/CMakeLists.txt
new file mode 100644
index 0000000..4338e8f
--- /dev/null
+++ b/examples/statemachine/trafficlight/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
+project(trafficlight LANGUAGES CXX)
+
+if(NOT DEFINED INSTALL_EXAMPLESDIR)
+ set(INSTALL_EXAMPLESDIR "examples")
+endif()
+
+set(INSTALL_EXAMPLEDIR "${INSTALL_EXAMPLESDIR}/widgets/statemachine/trafficlight")
+
+find_package(Qt6 REQUIRED COMPONENTS Core Gui StateMachine Widgets)
+
+qt_standard_project_setup()
+
+qt_add_executable(trafficlight
+ main.cpp
+)
+
+set_target_properties(trafficlight PROPERTIES
+ WIN32_EXECUTABLE TRUE
+ MACOSX_BUNDLE TRUE
+)
+
+target_link_libraries(trafficlight PRIVATE
+ Qt::Core
+ Qt::Gui
+ Qt::StateMachine
+ Qt::Widgets
+)
+
+install(TARGETS trafficlight
+ RUNTIME DESTINATION "${INSTALL_EXAMPLEDIR}"
+ BUNDLE DESTINATION "${INSTALL_EXAMPLEDIR}"
+ LIBRARY DESTINATION "${INSTALL_EXAMPLEDIR}"
+)
diff --git a/examples/statemachine/trafficlight/main.cpp b/examples/statemachine/trafficlight/main.cpp
new file mode 100644
index 0000000..5c85921
--- /dev/null
+++ b/examples/statemachine/trafficlight/main.cpp
@@ -0,0 +1,152 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause
+
+#include <QtCore/QTimer>
+#include <QtGui/QPainter>
+#include <QtStateMachine/QFinalState>
+#include <QtStateMachine/QStateMachine>
+#include <QtWidgets/QApplication>
+#include <QtWidgets/QVBoxLayout>
+#include <QtWidgets/QWidget>
+
+//! [0]
+class LightWidget : public QWidget
+{
+ Q_OBJECT
+ Q_PROPERTY(bool on READ isOn WRITE setOn)
+public:
+ explicit LightWidget(const QColor &color, QWidget *parent = nullptr)
+ : QWidget(parent), m_color(color)
+ {
+ }
+
+ bool isOn() const { return m_on; }
+
+ void setOn(bool on)
+ {
+ if (on == m_on)
+ return;
+ m_on = on;
+ update();
+ }
+
+public slots:
+ void turnOff() { setOn(false); }
+ void turnOn() { setOn(true); }
+
+protected:
+ void paintEvent(QPaintEvent *) override
+ {
+ if (!m_on)
+ return;
+ QPainter painter(this);
+ painter.setRenderHint(QPainter::Antialiasing);
+ painter.setBrush(m_color);
+ painter.drawEllipse(rect());
+ }
+
+private:
+ QColor m_color;
+ bool m_on = false;
+};
+//! [0]
+
+//! [1]
+class TrafficLightWidget : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit TrafficLightWidget(QWidget *parent = nullptr) : QWidget(parent)
+ {
+ auto vbox = new QVBoxLayout(this);
+ m_red = new LightWidget(Qt::red);
+ vbox->addWidget(m_red);
+ m_yellow = new LightWidget(Qt::yellow);
+ vbox->addWidget(m_yellow);
+ m_green = new LightWidget(Qt::green);
+ vbox->addWidget(m_green);
+ auto pal = palette();
+ pal.setColor(QPalette::Window, Qt::black);
+ setPalette(pal);
+ setAutoFillBackground(true);
+ }
+
+ LightWidget *redLight() const { return m_red; }
+ LightWidget *yellowLight() const { return m_yellow; }
+ LightWidget *greenLight() const { return m_green; }
+
+private:
+ LightWidget *m_red;
+ LightWidget *m_yellow;
+ LightWidget *m_green;
+};
+//! [1]
+
+//! [2]
+QState *createLightState(LightWidget *light, int duration, QState *parent = nullptr)
+{
+ auto lightState = new QState(parent);
+ auto timer = new QTimer(lightState);
+ timer->setInterval(duration);
+ timer->setSingleShot(true);
+ auto timing = new QState(lightState);
+ QObject::connect(timing, &QAbstractState::entered, light, &LightWidget::turnOn);
+ QObject::connect(timing, &QAbstractState::entered, timer, QOverload<>::of(&QTimer::start));
+ QObject::connect(timing, &QAbstractState::exited, light, &LightWidget::turnOff);
+ auto done = new QFinalState(lightState);
+ timing->addTransition(timer, &QTimer::timeout, done);
+ lightState->setInitialState(timing);
+ return lightState;
+}
+//! [2]
+
+//! [3]
+class TrafficLight : public QWidget
+{
+ Q_OBJECT
+public:
+ explicit TrafficLight(QWidget *parent = nullptr) : QWidget(parent)
+ {
+ auto vbox = new QVBoxLayout(this);
+ auto widget = new TrafficLightWidget;
+ vbox->addWidget(widget);
+ vbox->setContentsMargins(QMargins());
+
+ auto machine = new QStateMachine(this);
+ auto redGoingYellow = createLightState(widget->redLight(), 3000);
+ redGoingYellow->setObjectName("redGoingYellow");
+ auto yellowGoingGreen = createLightState(widget->yellowLight(), 1000);
+ yellowGoingGreen->setObjectName("yellowGoingGreen");
+ redGoingYellow->addTransition(redGoingYellow, &QState::finished, yellowGoingGreen);
+ auto greenGoingYellow = createLightState(widget->greenLight(), 3000);
+ greenGoingYellow->setObjectName("greenGoingYellow");
+ yellowGoingGreen->addTransition(yellowGoingGreen, &QState::finished, greenGoingYellow);
+ auto yellowGoingRed = createLightState(widget->yellowLight(), 1000);
+ yellowGoingRed->setObjectName("yellowGoingRed");
+ greenGoingYellow->addTransition(greenGoingYellow, &QState::finished, yellowGoingRed);
+ yellowGoingRed->addTransition(yellowGoingRed, &QState::finished, redGoingYellow);
+
+ machine->addState(redGoingYellow);
+ machine->addState(yellowGoingGreen);
+ machine->addState(greenGoingYellow);
+ machine->addState(yellowGoingRed);
+ machine->setInitialState(redGoingYellow);
+ machine->start();
+ }
+};
+//! [3]
+
+//! [4]
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+
+ TrafficLight widget;
+ widget.resize(110, 300);
+ widget.show();
+
+ return app.exec();
+}
+//! [4]
+
+#include "main.moc"
diff --git a/examples/statemachine/trafficlight/trafficlight.pro b/examples/statemachine/trafficlight/trafficlight.pro
new file mode 100644
index 0000000..1288f1f
--- /dev/null
+++ b/examples/statemachine/trafficlight/trafficlight.pro
@@ -0,0 +1,7 @@
+QT += statemachine widgets
+
+SOURCES = main.cpp
+
+# install
+target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/statemachine/trafficlight
+INSTALLS += target
diff --git a/licenseRule.json b/licenseRule.json
new file mode 100644
index 0000000..2f757f1
--- /dev/null
+++ b/licenseRule.json
@@ -0,0 +1,89 @@
+[
+ {
+ "comment" : ["file_pattern_ending: strings matched against the end of a file name.",
+ "location keys: regular expression matched against the beginning of",
+ "the file path (relative to the git submodule root).",
+ "spdx: list of SPDX-License-Expression's allowed in the matching files.",
+ "-------------------------------------------------------",
+ "Files with the following endings are Build System licensed,",
+ "unless they are examples",
+ "Files with other endings can also be build system files"
+ ],
+ "file_pattern_ending" : ["CMakeLists.txt", ".cmake", ".pro", ".pri", ".prf",
+ "configure", "configure.bat", "cmake.in", "plist.in", "CMakeLists.txt.in"],
+ "location" : {
+ "" : {
+ "comment" : "Default",
+ "file type" : "build system",
+ "spdx" : ["BSD-3-Clause"]
+ },
+ "(.*)(examples/|snippets/)" : {
+ "comment" : "Example takes precedence",
+ "file type" : "examples and snippets",
+ "spdx" : ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+ }
+ }
+ },
+ {
+ "comments" : ["Files with the following endings are Tool licensed,",
+ "unless they are examples.",
+ "Files with other endings can also be tool files."],
+ "file_pattern_ending" : [".sh", ".py", ".pl", ".bat", ".ps1"],
+ "location" :{
+ "" : {
+ "comment" : "Default",
+ "file type" : "tools and utils",
+ "spdx" : ["LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0"]
+ },
+ "(.*)(examples/|snippets/)" : {
+ "comment" : "Example takes precedence",
+ "file type" : "examples and snippets",
+ "spdx" : ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+ }
+ }
+ },
+ {
+ "comment" : "Files with the following endings are Documentation licensed.",
+ "file_pattern_ending" : [".qdoc", ".qdocinc" , ".qdocconf", ".txt", "README", "qt_attribution.json"],
+ "location" :{
+ "" : {
+ "comment" : "",
+ "file type" : "documentation",
+ "spdx" : ["LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only"]
+ }
+ }
+ },
+ {
+ "comment" : ["All other files",
+ "The licensing is defined only by the file location in the Qt module repository.",
+ "NO <file_pattern_ending> key for this case!",
+ "This needs to be the last entry of the file."],
+ "location" : {
+ "" : {
+ "comment" : "Default",
+ "file type" : "module and plugin",
+ "spdx" : ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only"]
+ },
+ "src/" : {
+ "comment" : "Default",
+ "file type" : "module and plugin",
+ "spdx" : ["LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only"]
+ },
+ "tools/" : {
+ "comment" : "Default",
+ "file type" : "tools and utils",
+ "spdx" : ["LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0"]
+ },
+ "tests/" : {
+ "comment" : "Default",
+ "file type" : "test",
+ "spdx" : ["LicenseRef-Qt-Commercial OR GPL-3.0-only"]
+ },
+ "(.*)(examples/|snippets/)" : {
+ "comment" : "Default",
+ "file type" : "examples and snippets",
+ "spdx" : ["LicenseRef-Qt-Commercial OR BSD-3-Clause"]
+ }
+ }
+ }
+]
diff --git a/mkspecs/features/qscxmlc.prf b/mkspecs/features/qscxmlc.prf
index ae1b3ba..a807937 100644
--- a/mkspecs/features/qscxmlc.prf
+++ b/mkspecs/features/qscxmlc.prf
@@ -1,4 +1,4 @@
-qtPrepareTool(QMAKE_QSCXMLC, qscxmlc)
+qtPrepareLibExecTool(QMAKE_QSCXMLC, qscxmlc)
isEmpty(QSCXMLC_DIR):QSCXMLC_DIR = .
diff --git a/qt_cmdline.cmake b/qt_cmdline.cmake
new file mode 100644
index 0000000..94d4e98
--- /dev/null
+++ b/qt_cmdline.cmake
@@ -0,0 +1,2 @@
+qt_commandline_subconfig(src/scxml)
+qt_commandline_subconfig(src/statemachine)
diff --git a/qtscxml.pro b/qtscxml.pro
deleted file mode 100644
index a2e3b51..0000000
--- a/qtscxml.pro
+++ /dev/null
@@ -1,9 +0,0 @@
-CONFIG += tests_need_tools examples_need_tools
-
-OTHER_FILES += \
- .qmake.conf \
- sync.profile \
- .gitignore \
- TODO.txt
-
-load(qt_parts)
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000..5fbfbd9
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,11 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+add_subdirectory(scxml)
+add_subdirectory(statemachine)
+if(TARGET Qt::Qml)
+ add_subdirectory(statemachineqml)
+ add_subdirectory(scxmlqml)
+endif()
+add_subdirectory(plugins)
diff --git a/src/imports/imports.pro b/src/imports/imports.pro
deleted file mode 100644
index 60a0bd2..0000000
--- a/src/imports/imports.pro
+++ /dev/null
@@ -1,3 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS = scxmlstatemachine
-
diff --git a/src/imports/scxmlstatemachine/eventconnection.cpp b/src/imports/scxmlstatemachine/eventconnection.cpp
deleted file mode 100644
index 2a073bd..0000000
--- a/src/imports/scxmlstatemachine/eventconnection.cpp
+++ /dev/null
@@ -1,143 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "eventconnection_p.h"
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \qmltype EventConnection
- \instantiates QScxmlEventConnection
- \inqmlmodule QtScxml
- \since QtScxml 5.8
-
- \brief Connects to events sent out by state machines.
-
- To receive a notification when a state machine sends out an event, a
- connection can be created to the corresponding signal.
-*/
-
-/*!
- \qmlproperty stringlist EventConnection::events
-
- The list of SCXML event specifiers that describe the events to listen for.
-
- Even though spaces are allowed in event specifications in SCXML documents,
- they are not allowed in this list. However, the list can contain multiple
- specifiers, to the same effect.
-*/
-
-/*!
- \qmlproperty ScxmlStateMachine EventConnection::stateMachine
-
- The state machine that sends out the event.
-*/
-
-/*!
- \qmlsignal EventConnection::occurred(event)
-
- This signal is emitted when the event \a event occurrs.
-
- The corresponding signal handler is \c onOccurred.
-
- \sa QScxmlEvent
-*/
-
-
-QScxmlEventConnection::QScxmlEventConnection(QObject *parent) :
- QObject(parent), m_stateMachine(nullptr)
-{
-}
-
-QStringList QScxmlEventConnection::events() const
-{
- return m_events;
-}
-
-void QScxmlEventConnection::setEvents(const QStringList &events)
-{
- if (events != m_events) {
- m_events = events;
- doConnect();
- emit eventsChanged();
- }
-}
-
-QScxmlStateMachine *QScxmlEventConnection::stateMachine() const
-{
- return m_stateMachine;
-}
-
-void QScxmlEventConnection::setStateMachine(QScxmlStateMachine *stateMachine)
-{
- if (stateMachine != m_stateMachine) {
- m_stateMachine = stateMachine;
- doConnect();
- emit stateMachineChanged();
- }
-}
-
-void QScxmlEventConnection::doConnect()
-{
- for (const QMetaObject::Connection &connection : qAsConst(m_connections))
- disconnect(connection);
- m_connections.clear();
- if (m_stateMachine) {
- for (const QString &event : qAsConst(m_events)) {
- m_connections.append(m_stateMachine->connectToEvent(event, this,
- &QScxmlEventConnection::occurred));
- }
-
- }
-
-}
-
-void QScxmlEventConnection::classBegin()
-{
-}
-
-void QScxmlEventConnection::componentComplete()
-{
- if (!m_stateMachine) {
- if ((m_stateMachine = qobject_cast<QScxmlStateMachine *>(parent())))
- doConnect();
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/imports/scxmlstatemachine/eventconnection_p.h b/src/imports/scxmlstatemachine/eventconnection_p.h
deleted file mode 100644
index 5fea645..0000000
--- a/src/imports/scxmlstatemachine/eventconnection_p.h
+++ /dev/null
@@ -1,96 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef EVENTCONNECTION_P_H
-#define EVENTCONNECTION_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 <QtScxml/qscxmlstatemachine.h>
-#include <QtCore/qobject.h>
-#include <QtQml/qqmlparserstatus.h>
-
-QT_BEGIN_NAMESPACE
-
-class QScxmlEventConnection : public QObject, public QQmlParserStatus
-{
- Q_OBJECT
- Q_PROPERTY(QStringList events READ events WRITE setEvents NOTIFY eventsChanged)
- Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine WRITE setStateMachine
- NOTIFY stateMachineChanged)
- Q_INTERFACES(QQmlParserStatus)
-
-public:
- QScxmlEventConnection(QObject *parent = nullptr);
-
- QStringList events() const;
- void setEvents(const QStringList &events);
-
- QScxmlStateMachine *stateMachine() const;
- void setStateMachine(QScxmlStateMachine *stateMachine);
-
-Q_SIGNALS:
- void eventsChanged();
- void stateMachineChanged();
-
- void occurred(const QScxmlEvent &event);
-
-private:
- QScxmlStateMachine *m_stateMachine;
- QStringList m_events;
-
- QList<QMetaObject::Connection> m_connections;
-
- void doConnect();
- void classBegin() override;
- void componentComplete() override;
-};
-
-QT_END_NAMESPACE
-
-#endif // EVENTCONNECTION_P_H
diff --git a/src/imports/scxmlstatemachine/invokedservices.cpp b/src/imports/scxmlstatemachine/invokedservices.cpp
deleted file mode 100644
index 6011557..0000000
--- a/src/imports/scxmlstatemachine/invokedservices.cpp
+++ /dev/null
@@ -1,133 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "invokedservices_p.h"
-#include <QtScxml/qscxmlinvokableservice.h>
-
-QT_BEGIN_NAMESPACE
-
-/*!
- \qmltype InvokedServices
- \instantiates QScxmlInvokedServices
- \inqmlmodule QtScxml
- \since QtScxml 5.8
-
- \brief Provices access to the services invoked by state machines.
-
- Makes the invoked services easily accessible by their names, without
- constantly iterating through QScxmlStateMachine::invokedServices.
-
- The services are called from state machines via the mechanism described in
- \l{SCXML Specification - 6.4 <invoke>}.
-*/
-
-QScxmlInvokedServices::QScxmlInvokedServices(QObject *parent) : QObject(parent)
-{
-}
-
-/*!
- \qmlproperty var InvokedServices::children
-
- The services invoked by the state machine.
-*/
-
-QVariantMap QScxmlInvokedServices::children()
-{
- QVariantMap ret;
- if (m_stateMachine) {
- const QVector<QScxmlInvokableService *> children = m_stateMachine->invokedServices();
- for (QScxmlInvokableService *service : children)
- ret.insertMulti(service->name(), QVariant::fromValue(service));
- }
- return ret;
-}
-
-void QScxmlInvokedServices::classBegin()
-{
-}
-
-/*!
- \qmlproperty ScxmlStateMachine InvokedServices::stateMachine
-
- The state machine that invoked the services.
-*/
-
-QScxmlStateMachine *QScxmlInvokedServices::stateMachine() const
-{
- return m_stateMachine;
-}
-
-void QScxmlInvokedServices::setStateMachine(QScxmlStateMachine *stateMachine)
-{
- if (stateMachine != m_stateMachine) {
- if (m_stateMachine) {
- disconnect(m_stateMachine, &QScxmlStateMachine::invokedServicesChanged,
- this, &QScxmlInvokedServices::childrenChanged);
- }
- m_stateMachine = stateMachine;
- connect(m_stateMachine, &QScxmlStateMachine::invokedServicesChanged,
- this, &QScxmlInvokedServices::childrenChanged);
- emit stateMachineChanged();
- emit childrenChanged();
- }
-}
-
-/*!
- \qmlproperty list<QtObject> InvokedServices::qmlChildren
-
- A list of additional QtObject types nested in this type.
-*/
-
-QQmlListProperty<QObject> QScxmlInvokedServices::qmlChildren()
-{
- return QQmlListProperty<QObject>(this, m_qmlChildren);
-}
-
-
-void QScxmlInvokedServices::componentComplete()
-{
- if (!m_stateMachine) {
- if ((m_stateMachine = qobject_cast<QScxmlStateMachine *>(parent()))) {
- connect(m_stateMachine, &QScxmlStateMachine::invokedServicesChanged,
- this, &QScxmlInvokedServices::childrenChanged);
- }
- }
-}
-
-QT_END_NAMESPACE
diff --git a/src/imports/scxmlstatemachine/invokedservices_p.h b/src/imports/scxmlstatemachine/invokedservices_p.h
deleted file mode 100644
index 5a6dac7..0000000
--- a/src/imports/scxmlstatemachine/invokedservices_p.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef INVOKEDSERVICES_P_H
-#define INVOKEDSERVICES_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 <QtQml/qqmlparserstatus.h>
-#include <QtQml/qqmllist.h>
-#include <QtScxml/qscxmlstatemachine.h>
-
-QT_BEGIN_NAMESPACE
-
-class QScxmlInvokedServices : public QObject, public QQmlParserStatus
-{
- Q_OBJECT
- Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine WRITE setStateMachine
- NOTIFY stateMachineChanged)
- Q_PROPERTY(QVariantMap children READ children NOTIFY childrenChanged)
- Q_PROPERTY(QQmlListProperty<QObject> qmlChildren READ qmlChildren)
- Q_INTERFACES(QQmlParserStatus)
- Q_CLASSINFO("DefaultProperty", "qmlChildren")
-public:
- QScxmlInvokedServices(QObject *parent = nullptr);
- QVariantMap children();
-
- QScxmlStateMachine *stateMachine() const;
- void setStateMachine(QScxmlStateMachine *stateMachine);
-
- QQmlListProperty<QObject> qmlChildren();
-
-Q_SIGNALS:
- void childrenChanged();
- void stateMachineChanged();
-
-private:
- void classBegin() override;
- void componentComplete() override;
-
- QScxmlStateMachine *m_stateMachine = nullptr;
- QList<QObject *> m_qmlChildren;
-};
-
-QT_END_NAMESPACE
-
-#endif // INVOKEDSERVICES_P_H
diff --git a/src/imports/scxmlstatemachine/plugin.cpp b/src/imports/scxmlstatemachine/plugin.cpp
deleted file mode 100644
index 1d6c8a1..0000000
--- a/src/imports/scxmlstatemachine/plugin.cpp
+++ /dev/null
@@ -1,88 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "statemachineloader_p.h"
-#include "eventconnection_p.h"
-#include "qscxmlevent.h"
-#include "statemachineextended_p.h"
-#include "invokedservices_p.h"
-
-#include <qqmlextensionplugin.h>
-#include <qqml.h>
-
-QT_BEGIN_NAMESPACE
-
-class QScxmlStateMachinePlugin : public QQmlExtensionPlugin
-{
- Q_OBJECT
- Q_PLUGIN_METADATA(IID QQmlExtensionInterface_iid)
-
-public:
- QScxmlStateMachinePlugin(QObject *parent = nullptr) : QQmlExtensionPlugin(parent) { }
- void registerTypes(const char *uri)
- {
- // @uri QtScxml
- Q_ASSERT(uri == QStringLiteral("QtScxml"));
-
- int major = 5;
- int minor = 8;
- // Do not rely on RegisterMethodArgumentMetaType meta-call to register the QScxmlEvent type.
- // This registration is required for the receiving end of the signal emission that carries
- // parameters of this type to be able to treat them correctly as a gadget. This is because the
- // receiving end of the signal is a generic method in the QML engine, at which point it's too late
- // to do a meta-type registration.
- static const int qScxmlEventMetaTypeId = qMetaTypeId<QScxmlEvent>();
- Q_UNUSED(qScxmlEventMetaTypeId)
- qmlRegisterType<QScxmlStateMachineLoader>(uri, major, minor, "StateMachineLoader");
- qmlRegisterType<QScxmlEventConnection>(uri, major, minor, "EventConnection");
- qmlRegisterType<QScxmlInvokedServices>(uri, major, minor, "InvokedServices");
- qmlRegisterExtendedUncreatableType<QScxmlStateMachine, QScxmlStateMachineExtended>(
- uri, major, minor, "StateMachine", "Only created through derived types");
-
- // The minor version used to be the current Qt 5 minor. For compatibility it is the last
- // Qt 5 release.
- qmlRegisterModule(uri, major, 15);
-
- qmlProtectModule(uri, 1);
- }
-};
-
-QT_END_NAMESPACE
-
-#include "plugin.moc"
diff --git a/src/imports/scxmlstatemachine/plugins.qmltypes b/src/imports/scxmlstatemachine/plugins.qmltypes
deleted file mode 100644
index 1b6c469..0000000
--- a/src/imports/scxmlstatemachine/plugins.qmltypes
+++ /dev/null
@@ -1,140 +0,0 @@
-import QtQuick.tooling 1.2
-
-// This file describes the plugin-supplied types contained in the library.
-// It is used for QML tooling purposes only.
-//
-// This file was auto-generated by:
-// 'qmlplugindump -nonrelocatable QtScxml 5.14'
-
-Module {
- dependencies: ["QtQuick 2.0"]
- Component {
- name: "QScxmlEventConnection"
- prototype: "QObject"
- exports: ["QtScxml/EventConnection 5.8"]
- exportMetaObjectRevisions: [0]
- Property { name: "events"; type: "QStringList" }
- Property { name: "stateMachine"; type: "QScxmlStateMachine"; isPointer: true }
- Signal {
- name: "occurred"
- Parameter { name: "event"; type: "QScxmlEvent" }
- }
- }
- Component {
- name: "QScxmlInvokedServices"
- defaultProperty: "qmlChildren"
- prototype: "QObject"
- exports: ["QtScxml/InvokedServices 5.8"]
- exportMetaObjectRevisions: [0]
- Property { name: "stateMachine"; type: "QScxmlStateMachine"; isPointer: true }
- Property { name: "children"; type: "QVariantMap"; isReadonly: true }
- Property { name: "qmlChildren"; type: "QObject"; isList: true; isReadonly: true }
- }
- Component {
- name: "QScxmlStateMachine"
- defaultProperty: "children"
- prototype: "QObject"
- exports: ["QtScxml/StateMachine 5.8"]
- isCreatable: false
- exportMetaObjectRevisions: [508]
- Property { name: "running"; type: "bool" }
- Property { name: "initialized"; type: "bool"; isReadonly: true }
- Property { name: "dataModel"; type: "QScxmlDataModel"; isPointer: true }
- Property { name: "initialValues"; type: "QVariantMap" }
- Property { name: "invokedServices"; type: "QVector<QScxmlInvokableService*>"; isReadonly: true }
- Property { name: "sessionId"; type: "string"; isReadonly: true }
- Property { name: "name"; type: "string"; isReadonly: true }
- Property { name: "invoked"; type: "bool"; isReadonly: true }
- Property { name: "parseErrors"; type: "QVector<QScxmlError>"; isReadonly: true }
- Property { name: "loader"; type: "QScxmlCompiler::Loader"; isPointer: true }
- Property { name: "tableData"; type: "QScxmlTableData"; isPointer: true }
- Signal {
- name: "runningChanged"
- Parameter { name: "running"; type: "bool" }
- }
- Signal {
- name: "invokedServicesChanged"
- Parameter { name: "invokedServices"; type: "QVector<QScxmlInvokableService*>" }
- }
- Signal {
- name: "log"
- Parameter { name: "label"; type: "string" }
- Parameter { name: "msg"; type: "string" }
- }
- Signal { name: "reachedStableState" }
- Signal { name: "finished" }
- Signal {
- name: "dataModelChanged"
- Parameter { name: "model"; type: "QScxmlDataModel"; isPointer: true }
- }
- Signal {
- name: "initialValuesChanged"
- Parameter { name: "initialValues"; type: "QVariantMap" }
- }
- Signal {
- name: "initializedChanged"
- Parameter { name: "initialized"; type: "bool" }
- }
- Signal {
- name: "loaderChanged"
- Parameter { name: "loader"; type: "QScxmlCompiler::Loader"; isPointer: true }
- }
- Signal {
- name: "tableDataChanged"
- Parameter { name: "tableData"; type: "QScxmlTableData"; isPointer: true }
- }
- Method { name: "start" }
- Method { name: "stop" }
- Method { name: "init"; type: "bool" }
- Method {
- name: "stateNames"
- type: "QStringList"
- Parameter { name: "compress"; type: "bool" }
- }
- Method { name: "stateNames"; type: "QStringList" }
- Method {
- name: "activeStateNames"
- type: "QStringList"
- Parameter { name: "compress"; type: "bool" }
- }
- Method { name: "activeStateNames"; type: "QStringList" }
- Method {
- name: "isActive"
- type: "bool"
- Parameter { name: "scxmlStateName"; type: "string" }
- }
- Method {
- name: "submitEvent"
- Parameter { name: "event"; type: "QScxmlEvent"; isPointer: true }
- }
- Method {
- name: "submitEvent"
- Parameter { name: "eventName"; type: "string" }
- }
- Method {
- name: "submitEvent"
- Parameter { name: "eventName"; type: "string" }
- Parameter { name: "data"; type: "QVariant" }
- }
- Method {
- name: "cancelDelayedEvent"
- Parameter { name: "sendId"; type: "string" }
- }
- Method {
- name: "isDispatchableTarget"
- type: "bool"
- Parameter { name: "target"; type: "string" }
- }
- Property { name: "children"; revision: 508; type: "QObject"; isList: true; isReadonly: true }
- }
- Component {
- name: "QScxmlStateMachineLoader"
- prototype: "QObject"
- exports: ["QtScxml/StateMachineLoader 5.8"]
- exportMetaObjectRevisions: [0]
- Property { name: "source"; type: "QUrl" }
- Property { name: "stateMachine"; type: "QScxmlStateMachine"; isReadonly: true; isPointer: true }
- Property { name: "initialValues"; type: "QVariantMap" }
- Property { name: "dataModel"; type: "QScxmlDataModel"; isPointer: true }
- }
-}
diff --git a/src/imports/scxmlstatemachine/qmldir b/src/imports/scxmlstatemachine/qmldir
deleted file mode 100644
index e836ddd..0000000
--- a/src/imports/scxmlstatemachine/qmldir
+++ /dev/null
@@ -1,5 +0,0 @@
-module QtScxml
-plugin declarative_scxml
-classname QScxmlStateMachinePlugin
-typeinfo plugins.qmltypes
-
diff --git a/src/imports/scxmlstatemachine/scxmlstatemachine.pro b/src/imports/scxmlstatemachine/scxmlstatemachine.pro
deleted file mode 100644
index 9bc7c72..0000000
--- a/src/imports/scxmlstatemachine/scxmlstatemachine.pro
+++ /dev/null
@@ -1,23 +0,0 @@
-TARGET = scxml
-TARGETPATH = QtScxml
-IMPORT_VERSION = 5.$$QT_MINOR_VERSION
-
-QT = scxml qml-private core-private
-
-SOURCES = \
- $$PWD/plugin.cpp \
- $$PWD/statemachineloader.cpp \
- $$PWD/eventconnection.cpp \
- $$PWD/statemachineextended.cpp \
- $$PWD/invokedservices.cpp
-
-HEADERS = \
- $$PWD/eventconnection_p.h \
- $$PWD/invokedservices_p.h \
- $$PWD/statemachineextended_p.h \
- $$PWD/statemachineloader_p.h
-
-
-load(qml_plugin)
-
-OTHER_FILES += plugins.qmltypes qmldir
diff --git a/src/imports/scxmlstatemachine/statemachineextended.cpp b/src/imports/scxmlstatemachine/statemachineextended.cpp
deleted file mode 100644
index 6b1b13b..0000000
--- a/src/imports/scxmlstatemachine/statemachineextended.cpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "statemachineextended_p.h"
-
-#include <QtScxml/qscxmlglobals.h>
-#include <QtScxml/qscxmlstatemachine.h>
-
-QT_BEGIN_NAMESPACE
-
-QScxmlStateMachineExtended::QScxmlStateMachineExtended(QObject *extendee) :
- QObject(extendee)
-{
-}
-
-QQmlListProperty<QObject> QScxmlStateMachineExtended::children()
-{
- return QQmlListProperty<QObject>(this, m_children);
-}
-
-QT_END_NAMESPACE
diff --git a/src/imports/scxmlstatemachine/statemachineextended_p.h b/src/imports/scxmlstatemachine/statemachineextended_p.h
deleted file mode 100644
index ab069b1..0000000
--- a/src/imports/scxmlstatemachine/statemachineextended_p.h
+++ /dev/null
@@ -1,76 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef STATEMACHINEEXTENDED_P_H
-#define STATEMACHINEEXTENDED_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 <QtScxml/qscxmlglobals.h>
-#include <QtCore/qobject.h>
-#include <QtQml/qqmllist.h>
-
-QT_BEGIN_NAMESPACE
-
-/* Allow State Machines created from QML to have children. */
-class QScxmlStateMachineExtended : public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QQmlListProperty<QObject> children READ children)
- Q_CLASSINFO("DefaultProperty", "children")
-public:
- QScxmlStateMachineExtended(QObject *extendee);
- QQmlListProperty<QObject> children();
-
-private:
- QObjectList m_children;
-};
-
-QT_END_NAMESPACE
-
-#endif // STATEMACHINEEXTENDED_P_H
diff --git a/src/imports/scxmlstatemachine/statemachineloader.cpp b/src/imports/scxmlstatemachine/statemachineloader.cpp
deleted file mode 100644
index 1d312a5..0000000
--- a/src/imports/scxmlstatemachine/statemachineloader.cpp
+++ /dev/null
@@ -1,203 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "statemachineloader_p.h"
-
-#include <QtScxml/qscxmlstatemachine.h>
-#include <qqmlcontext.h>
-#include <qqmlengine.h>
-#include <qqmlinfo.h>
-#include <qqmlfile.h>
-#include <qbuffer.h>
-
-/*!
- \qmltype StateMachineLoader
- \instantiates QScxmlStateMachineLoader
- \inqmlmodule QtScxml
-
- \brief Dynamically loads an SCXML document and instantiates the state machine.
-
- \since QtScxml 5.7
- */
-
-QScxmlStateMachineLoader::QScxmlStateMachineLoader(QObject *parent)
- : QObject(parent)
- , m_dataModel(nullptr)
- , m_implicitDataModel(nullptr)
- , m_stateMachine(nullptr)
-{
-}
-
-/*!
- \qmlproperty ScxmlStateMachine StateMachineLoader::stateMachine
-
- The state machine instance.
- */
-QT_PREPEND_NAMESPACE(QScxmlStateMachine) *QScxmlStateMachineLoader::stateMachine() const
-{
- return m_stateMachine;
-}
-
-/*!
- \qmlproperty url StateMachineLoader::source
-
- The URL of the SCXML document to load. Only synchronously accessible URLs
- are supported.
- */
-QUrl QScxmlStateMachineLoader::source()
-{
- return m_source;
-}
-
-void QScxmlStateMachineLoader::setSource(const QUrl &source)
-{
- if (!source.isValid())
- return;
-
- QUrl oldSource = m_source;
- if (m_stateMachine) {
- delete m_stateMachine;
- m_stateMachine = nullptr;
- m_implicitDataModel = nullptr;
- }
-
- if (parse(source)) {
- m_source = source;
- emit sourceChanged();
- } else {
- m_source.clear();
- if (!oldSource.isEmpty()) {
- emit sourceChanged();
- }
- }
-}
-
-QVariantMap QScxmlStateMachineLoader::initialValues() const
-{
- return m_initialValues;
-}
-
-void QScxmlStateMachineLoader::setInitialValues(const QVariantMap &initialValues)
-{
- if (initialValues != m_initialValues) {
- m_initialValues = initialValues;
- if (m_stateMachine)
- m_stateMachine->setInitialValues(initialValues);
- emit initialValuesChanged();
- }
-}
-
-QScxmlDataModel *QScxmlStateMachineLoader::dataModel() const
-{
- return m_dataModel;
-}
-
-void QScxmlStateMachineLoader::setDataModel(QScxmlDataModel *dataModel)
-{
- if (dataModel != m_dataModel) {
- m_dataModel = dataModel;
- if (m_stateMachine) {
- if (dataModel)
- m_stateMachine->setDataModel(dataModel);
- else
- m_stateMachine->setDataModel(m_implicitDataModel);
- }
- emit dataModelChanged();
- }
-}
-
-bool QScxmlStateMachineLoader::parse(const QUrl &source)
-{
- if (!QQmlFile::isSynchronous(source)) {
- qmlWarning(this) << QStringLiteral("Cannot open '%1' for reading: only synchronous access is supported.")
- .arg(source.url());
- return false;
- }
- QQmlFile scxmlFile(QQmlEngine::contextForObject(this)->engine(), source);
- if (scxmlFile.isError()) {
- // the synchronous case can only fail when the file is not found (or not readable).
- qmlWarning(this) << QStringLiteral("Cannot open '%1' for reading.").arg(source.url());
- return false;
- }
-
- QByteArray data(scxmlFile.dataByteArray());
- QBuffer buf(&data);
- if (!buf.open(QIODevice::ReadOnly)) {
- qmlWarning(this) << QStringLiteral("Cannot open input buffer for reading");
- return false;
- }
-
- QString fileName;
- if (source.isLocalFile()) {
- fileName = source.toLocalFile();
- } else if (source.scheme() == QStringLiteral("qrc")) {
- fileName = ":" + source.path();
- } else {
- qmlWarning(this) << QStringLiteral("%1 is neither a local nor a resource URL.")
- .arg(source.url())
- << QStringLiteral("Invoking services by relative path will not work.");
- }
-
- m_stateMachine = QScxmlStateMachine::fromData(&buf, fileName);
- m_stateMachine->setParent(this);
- m_implicitDataModel = m_stateMachine->dataModel();
-
- if (m_stateMachine->parseErrors().isEmpty()) {
- if (m_dataModel)
- m_stateMachine->setDataModel(m_dataModel);
- m_stateMachine->setInitialValues(m_initialValues);
- emit stateMachineChanged();
-
- // as this is deferred any pending property updates to m_dataModel and m_initialValues
- // should still occur before start().
- QMetaObject::invokeMethod(m_stateMachine, "start", Qt::QueuedConnection);
- return true;
- } else {
- qmlWarning(this) << QStringLiteral("Something went wrong while parsing '%1':")
- .arg(source.url())
- << Qt::endl;
- const auto errors = m_stateMachine->parseErrors();
- for (const QScxmlError &error : errors) {
- qmlWarning(this) << error.toString();
- }
-
- emit stateMachineChanged();
- return false;
- }
-}
diff --git a/src/imports/scxmlstatemachine/statemachineloader_p.h b/src/imports/scxmlstatemachine/statemachineloader_p.h
deleted file mode 100644
index 88ed445..0000000
--- a/src/imports/scxmlstatemachine/statemachineloader_p.h
+++ /dev/null
@@ -1,102 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef STATEMACHINELOADER_P_H
-#define STATEMACHINELOADER_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/qurl.h>
-#include <QtScxml/qscxmlstatemachine.h>
-#include <private/qqmlengine_p.h>
-
-QT_BEGIN_NAMESPACE
-
-class QScxmlStateMachineLoader: public QObject
-{
- Q_OBJECT
- Q_PROPERTY(QUrl source READ source WRITE setSource NOTIFY sourceChanged)
- Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine DESIGNABLE false NOTIFY stateMachineChanged)
- Q_PROPERTY(QVariantMap initialValues READ initialValues WRITE setInitialValues NOTIFY initialValuesChanged)
- Q_PROPERTY(QScxmlDataModel *dataModel READ dataModel WRITE setDataModel NOTIFY dataModelChanged)
-
-
-public:
- explicit QScxmlStateMachineLoader(QObject *parent = nullptr);
-
- QScxmlStateMachine *stateMachine() const;
-
- QUrl source();
- void setSource(const QUrl &source);
-
- QVariantMap initialValues() const;
- void setInitialValues(const QVariantMap &initialValues);
-
- QScxmlDataModel *dataModel() const;
- void setDataModel(QScxmlDataModel *dataModel);
-
-Q_SIGNALS:
- void sourceChanged();
- void initialValuesChanged();
- void stateMachineChanged();
- void dataModelChanged();
-
-private:
- bool parse(const QUrl &source);
-
-private:
- QUrl m_source;
- QVariantMap m_initialValues;
- QScxmlDataModel *m_dataModel;
- QScxmlDataModel *m_implicitDataModel;
- QScxmlStateMachine *m_stateMachine;
-};
-
-QT_END_NAMESPACE
-
-#endif // STATEMACHINELOADER_P_H
diff --git a/src/plugins/CMakeLists.txt b/src/plugins/CMakeLists.txt
new file mode 100644
index 0000000..71256c5
--- /dev/null
+++ b/src/plugins/CMakeLists.txt
@@ -0,0 +1,6 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(QT_FEATURE_scxml_ecmascriptdatamodel)
+ add_subdirectory(ecmascriptdatamodel)
+endif()
diff --git a/src/plugins/ecmascriptdatamodel/CMakeLists.txt b/src/plugins/ecmascriptdatamodel/CMakeLists.txt
new file mode 100644
index 0000000..7babe22
--- /dev/null
+++ b/src/plugins/ecmascriptdatamodel/CMakeLists.txt
@@ -0,0 +1,27 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## EcmaScript SCXML DataModel Plugin:
+#####################################################################
+
+qt_internal_include_in_repo_target_set(qtscxmlqml)
+
+qt_internal_add_plugin(QScxmlEcmaScriptDataModelPlugin
+ OUTPUT_NAME qscxmlecmascriptdatamodel
+ PLUGIN_TYPE scxmldatamodel
+ SOURCES
+ qscxmlecmascriptdatamodelplugin.cpp qscxmlecmascriptdatamodelplugin_p.h
+ qscxmlecmascriptdatamodel_p.h qscxmlecmascriptdatamodel.cpp
+ qscxmlecmascriptplatformproperties.cpp qscxmlecmascriptplatformproperties_p.h
+ LIBRARIES
+ Qt::Core
+ Qt::Scxml
+ Qt::Qml
+ INCLUDE_DIRECTORIES
+ $<TARGET_PROPERTY:Qt::QmlPrivate,INTERFACE_INCLUDE_DIRECTORIES>
+ $<TARGET_PROPERTY:Qt::Scxml,INTERFACE_INCLUDE_DIRECTORIES>
+ $<TARGET_PROPERTY:Qt::ScxmlPrivate,INTERFACE_INCLUDE_DIRECTORIES>
+)
+
+# OTHER_FILES = "ecmascriptdatamodel_plugin.json"
diff --git a/src/plugins/ecmascriptdatamodel/ecmascriptdatamodelplugin.json b/src/plugins/ecmascriptdatamodel/ecmascriptdatamodelplugin.json
new file mode 100644
index 0000000..09b76e2
--- /dev/null
+++ b/src/plugins/ecmascriptdatamodel/ecmascriptdatamodelplugin.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["ecmascriptdatamodel"]
+}
diff --git a/src/scxml/qscxmlecmascriptdatamodel.cpp b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodel.cpp
index da4fe84..031d6be 100644
--- a/src/scxml/qscxmlecmascriptdatamodel.cpp
+++ b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodel.cpp
@@ -1,48 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qscxmlglobals_p.h"
-#include "qscxmlecmascriptdatamodel.h"
+// 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 <QtScxml/private/qscxmlglobals_p.h>
+#include "qscxmlecmascriptdatamodel_p.h"
#include "qscxmlecmascriptplatformproperties_p.h"
-#include "qscxmlexecutablecontent_p.h"
-#include "qscxmlstatemachine_p.h"
-#include "qscxmldatamodel_p.h"
+#include <QtScxml/private/qscxmlexecutablecontent_p.h>
+#include <QtScxml/private/qscxmlstatemachine_p.h>
+#include <QtScxml/private/qscxmldatamodel_p.h>
#include <qjsengine.h>
#include <qjsondocument.h>
@@ -53,6 +17,8 @@
QT_BEGIN_NAMESPACE
+Q_LOGGING_CATEGORY(qscxmlEsLog, "qt.scxml.statemachine")
+
using namespace QScxmlExecutableContent;
typedef std::function<QString (bool *)> ToStringEvaluator;
@@ -121,7 +87,7 @@ public:
QJSEngine *engine = assertEngine();
dataModel = engine->globalObject();
- qCDebug(qscxmlLog) << m_stateMachine << "initializing the datamodel";
+ qCDebug(qscxmlEsLog) << m_stateMachine << "initializing the datamodel";
setupSystemVariables();
}
@@ -192,7 +158,7 @@ public:
return data;
}
- if (eventData == QVariant(QMetaType::VoidStar, 0)) {
+ if (eventData == QVariant(QMetaType(QMetaType::VoidStar), nullptr)) {
return QJSValue(QJSValue::NullValue);
}
@@ -268,17 +234,17 @@ public:
private: // Uses private API
static void setReadonlyProperty(QJSValue *object, const QString &name, const QJSValue &value)
{
- qCDebug(qscxmlLog) << "setting read-only property" << name;
+ qCDebug(qscxmlEsLog) << "setting read-only property" << name;
QV4::ExecutionEngine *engine = QJSValuePrivate::engine(object);
Q_ASSERT(engine);
QV4::Scope scope(engine);
- QV4::ScopedObject o(scope, QJSValuePrivate::getValue(object));
+ QV4::ScopedObject o(scope, QJSValuePrivate::asManagedType<QV4::Object>(object));
if (!o)
return;
if (!QJSValuePrivate::checkEngine(engine, value)) {
- qCWarning(qscxmlLog, "EcmaScriptDataModel::setReadonlyProperty(%s) failed: cannot set value created in a different engine", name.toUtf8().constData());
+ qCWarning(qscxmlEsLog, "EcmaScriptDataModel::setReadonlyProperty(%s) failed: cannot set value created in a different engine", name.toUtf8().constData());
return;
}
@@ -289,7 +255,7 @@ private: // Uses private API
return;
}
- QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
+ QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(engine, value));
o->defineReadonlyProperty(s, v);
if (engine->hasException)
engine->catchException();
@@ -310,7 +276,7 @@ private: // Uses private API
return SetPropertyFailedForAnotherReason;
QV4::Scope scope(engine);
- QV4::ScopedObject o(scope, QJSValuePrivate::getValue(object));
+ QV4::ScopedObject o(scope, QJSValuePrivate::asManagedType<QV4::Object>(object));
if (o == nullptr) {
return SetPropertyFailedForAnotherReason;
}
@@ -324,7 +290,7 @@ private: // Uses private API
QV4::PropertyAttributes attrs = o->getOwnProperty(s->toPropertyKey());
if (attrs.isWritable() || attrs.isEmpty()) {
- QV4::ScopedValue v(scope, QJSValuePrivate::convertedToValue(engine, value));
+ QV4::ScopedValue v(scope, QJSValuePrivate::convertToReturnedValue(engine, value));
o->insertMember(s, v);
if (engine->hasException) {
engine->catchException();
@@ -342,30 +308,24 @@ private:
QJSValue dataModel;
};
-/*!
- * \class QScxmlEcmaScriptDataModel
- * \brief The QScxmlEcmaScriptDataModel class is the ECMAScript data model for
+/*
+ * The QScxmlEcmaScriptDataModel class is the ECMAScript data model for
* a Qt SCXML state machine.
- * \since 5.7
- * \inmodule QtScxml
*
* This class implements the ECMAScript data model as described in
- * \l {SCXML Specification - B.2 The ECMAScript Data Model}. It can be
+ * "SCXML Specification - B.2 The ECMAScript Data Model". It can be
* subclassed to perform custom initialization.
*
- * \sa QScxmlStateMachine QScxmlDataModel
+ * See also QScxmlStateMachine QScxmlDataModel
*/
-/*!
- * Creates a new ECMAScript data model, with the parent object \a parent.
+/*
+ * Creates a new ECMAScript data model, with the parent object parent.
*/
QScxmlEcmaScriptDataModel::QScxmlEcmaScriptDataModel(QObject *parent)
: QScxmlDataModel(*(new QScxmlEcmaScriptDataModelPrivate), parent)
{}
-/*!
- \reimp
- */
bool QScxmlEcmaScriptDataModel::setup(const QVariantMap &initialDataValues)
{
Q_D(QScxmlEcmaScriptDataModel);
@@ -392,9 +352,6 @@ bool QScxmlEcmaScriptDataModel::setup(const QVariantMap &initialDataValues)
return ok;
}
-/*!
- \reimp
- */
QString QScxmlEcmaScriptDataModel::evaluateToString(QScxmlExecutableContent::EvaluatorId id,
bool *ok)
{
@@ -404,9 +361,6 @@ QString QScxmlEcmaScriptDataModel::evaluateToString(QScxmlExecutableContent::Eva
return d->evalStr(d->string(info.expr), d->string(info.context), ok);
}
-/*!
- \reimp
- */
bool QScxmlEcmaScriptDataModel::evaluateToBool(QScxmlExecutableContent::EvaluatorId id,
bool *ok)
{
@@ -416,9 +370,6 @@ bool QScxmlEcmaScriptDataModel::evaluateToBool(QScxmlExecutableContent::Evaluato
return d->evalBool(d->string(info.expr), d->string(info.context), ok);
}
-/*!
- \reimp
- */
QVariant QScxmlEcmaScriptDataModel::evaluateToVariant(QScxmlExecutableContent::EvaluatorId id,
bool *ok)
{
@@ -428,9 +379,6 @@ QVariant QScxmlEcmaScriptDataModel::evaluateToVariant(QScxmlExecutableContent::E
return d->evalJSValue(d->string(info.expr), d->string(info.context), ok).toVariant();
}
-/*!
- \reimp
- */
void QScxmlEcmaScriptDataModel::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id,
bool *ok)
{
@@ -440,9 +388,6 @@ void QScxmlEcmaScriptDataModel::evaluateToVoid(QScxmlExecutableContent::Evaluato
d->eval(d->string(info.expr), d->string(info.context), ok);
}
-/*!
- \reimp
- */
void QScxmlEcmaScriptDataModel::evaluateAssignment(QScxmlExecutableContent::EvaluatorId id,
bool *ok)
{
@@ -464,9 +409,6 @@ void QScxmlEcmaScriptDataModel::evaluateAssignment(QScxmlExecutableContent::Eval
}
}
-/*!
- \reimp
- */
void QScxmlEcmaScriptDataModel::evaluateInitialization(QScxmlExecutableContent::EvaluatorId id,
bool *ok)
{
@@ -481,9 +423,6 @@ void QScxmlEcmaScriptDataModel::evaluateInitialization(QScxmlExecutableContent::
evaluateAssignment(id, ok);
}
-/*!
- \reimp
- */
void QScxmlEcmaScriptDataModel::evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok,
ForeachLoopBody *body)
{
@@ -531,36 +470,24 @@ void QScxmlEcmaScriptDataModel::evaluateForeach(QScxmlExecutableContent::Evaluat
*ok = true;
}
-/*!
- * \reimp
- */
void QScxmlEcmaScriptDataModel::setScxmlEvent(const QScxmlEvent &event)
{
Q_D(QScxmlEcmaScriptDataModel);
d->assignEvent(event);
}
-/*!
- * \reimp
- */
QVariant QScxmlEcmaScriptDataModel::scxmlProperty(const QString &name) const
{
Q_D(const QScxmlEcmaScriptDataModel);
return d->property(name).toVariant();
}
-/*!
- * \reimp
- */
bool QScxmlEcmaScriptDataModel::hasScxmlProperty(const QString &name) const
{
Q_D(const QScxmlEcmaScriptDataModel);
return d->hasProperty(name);
}
-/*!
- * \reimp
- */
bool QScxmlEcmaScriptDataModel::setScxmlProperty(const QString &name, const QVariant &value,
const QString &context)
{
diff --git a/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodel_p.h b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodel_p.h
new file mode 100644
index 0000000..a449ae5
--- /dev/null
+++ b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodel_p.h
@@ -0,0 +1,52 @@
+// 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 QSCXMLECMASCRIPTDATAMODEL_P_H
+#define QSCXMLECMASCRIPTDATAMODEL_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 <QObject>
+#include <QtScxml/qscxmlglobals.h>
+#include <QtScxml/qscxmldatamodel.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class QScxmlEcmaScriptDataModelPrivate;
+class QScxmlEcmaScriptDataModel: public QScxmlDataModel
+{
+ Q_OBJECT
+ Q_DECLARE_PRIVATE(QScxmlEcmaScriptDataModel)
+public:
+ explicit QScxmlEcmaScriptDataModel(QObject *parent = nullptr);
+
+ Q_INVOKABLE bool setup(const QVariantMap &initialDataValues) override;
+
+ QString evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
+ bool evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
+ QVariant evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
+ void evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
+ void evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
+ void evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
+ void evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) override final;
+
+ void setScxmlEvent(const QScxmlEvent &event) override;
+
+ QVariant scxmlProperty(const QString &name) const override;
+ bool hasScxmlProperty(const QString &name) const override;
+ bool setScxmlProperty(const QString &name, const QVariant &value, const QString &context) override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSCXMLECMASCRIPTDATAMODEL_P_H
diff --git a/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodelplugin.cpp b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodelplugin.cpp
new file mode 100644
index 0000000..8f8ec2e
--- /dev/null
+++ b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodelplugin.cpp
@@ -0,0 +1,15 @@
+// Copyright (C) 2021 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 "QtScxml/qscxmldatamodel.h"
+#include "qscxmlecmascriptdatamodel_p.h"
+#include "qscxmlecmascriptdatamodelplugin_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QScxmlDataModel *QScxmlEcmaScriptDataModelPlugin::createScxmlDataModel() const
+{
+ return new QScxmlEcmaScriptDataModel;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodelplugin_p.h b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodelplugin_p.h
new file mode 100644
index 0000000..da169dd
--- /dev/null
+++ b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptdatamodelplugin_p.h
@@ -0,0 +1,35 @@
+// Copyright (C) 2021 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 QSCXMLECMASCRIPTDATAMODELPLUGIN_P_H
+#define QSCXMLECMASCRIPTDATAMODELPLUGIN_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 <QtScxml/private/qscxmldatamodelplugin_p.h>
+#include <QtCore/QObject>
+
+QT_BEGIN_NAMESPACE
+
+class QScxmlEcmaScriptDataModelPlugin : public QScxmlDataModelPlugin
+{
+ Q_OBJECT
+ Q_INTERFACES(QScxmlDataModelPlugin)
+ Q_PLUGIN_METADATA(IID QScxmlDataModelPluginInterface_iid FILE "ecmascriptdatamodelplugin.json")
+
+public:
+ QScxmlDataModel *createScxmlDataModel() const override;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSCXMLECMASCRIPTDATAMODELPLUGIN_P_H
diff --git a/src/plugins/ecmascriptdatamodel/qscxmlecmascriptplatformproperties.cpp b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptplatformproperties.cpp
new file mode 100644
index 0000000..03b4986
--- /dev/null
+++ b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptplatformproperties.cpp
@@ -0,0 +1,65 @@
+// 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 "qscxmlecmascriptplatformproperties_p.h"
+#include "qscxmlstatemachine.h"
+
+#include <qjsengine.h>
+
+QT_BEGIN_NAMESPACE
+class QScxmlPlatformProperties::Data
+{
+public:
+ Data()
+ : m_stateMachine(nullptr)
+ {}
+
+ QScxmlStateMachine *m_stateMachine;
+ QJSValue m_jsValue;
+};
+
+QScxmlPlatformProperties::QScxmlPlatformProperties(QObject *parent)
+ : QObject(parent)
+ , data(new Data)
+{}
+
+QScxmlPlatformProperties *QScxmlPlatformProperties::create(QJSEngine *engine, QScxmlStateMachine *stateMachine)
+{
+ QScxmlPlatformProperties *pp = new QScxmlPlatformProperties(engine);
+ pp->data->m_stateMachine = stateMachine;
+ pp->data->m_jsValue = engine->newQObject(pp);
+ return pp;
+}
+
+QScxmlPlatformProperties::~QScxmlPlatformProperties()
+{
+ delete data;
+}
+
+QJSEngine *QScxmlPlatformProperties::engine() const
+{
+ return qobject_cast<QJSEngine *>(parent());
+}
+
+QScxmlStateMachine *QScxmlPlatformProperties::stateMachine() const
+{
+ return data->m_stateMachine;
+}
+
+QJSValue QScxmlPlatformProperties::jsValue() const
+{
+ return data->m_jsValue;
+}
+
+/// _x.marks === "the spot"
+QString QScxmlPlatformProperties::marks() const
+{
+ return QStringLiteral("the spot");
+}
+
+bool QScxmlPlatformProperties::inState(const QString &stateName)
+{
+ return stateMachine()->isActive(stateName);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/ecmascriptdatamodel/qscxmlecmascriptplatformproperties_p.h b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptplatformproperties_p.h
new file mode 100644
index 0000000..f30f388
--- /dev/null
+++ b/src/plugins/ecmascriptdatamodel/qscxmlecmascriptplatformproperties_p.h
@@ -0,0 +1,55 @@
+// 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 QSCXMLECMASCRIPTPLATFORMPROPERTIES_P_H
+#define QSCXMLECMASCRIPTPLATFORMPROPERTIES_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 "qscxmlglobals.h"
+
+#include <QtCore/qobject.h>
+
+QT_FORWARD_DECLARE_CLASS(QJSEngine)
+QT_FORWARD_DECLARE_CLASS(QJSValue)
+
+QT_BEGIN_NAMESPACE
+
+class QScxmlStateMachine;
+class QScxmlPlatformProperties: public QObject
+{
+ Q_OBJECT
+
+ QScxmlPlatformProperties(QObject *parent);
+
+ Q_PROPERTY(QString marks READ marks CONSTANT)
+
+public:
+ static QScxmlPlatformProperties *create(QJSEngine *engine, QScxmlStateMachine *stateMachine);
+ ~QScxmlPlatformProperties();
+
+ QJSEngine *engine() const;
+ QScxmlStateMachine *stateMachine() const;
+ QJSValue jsValue() const;
+
+ QString marks() const;
+
+ Q_INVOKABLE bool inState(const QString &stateName);
+
+private:
+ class Data;
+ Data *data;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSCXMLECMASCRIPTPLATFORMPROPERTIES_P_H
diff --git a/src/scxml/CMakeLists.txt b/src/scxml/CMakeLists.txt
new file mode 100644
index 0000000..7aa8f20
--- /dev/null
+++ b/src/scxml/CMakeLists.txt
@@ -0,0 +1,57 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## Scxml Module:
+#####################################################################
+
+qt_internal_include_in_repo_target_set(qtscxml)
+
+qt_internal_add_module(Scxml
+ QMAKE_MODULE_CONFIG c++11 qscxmlc
+ PLUGIN_TYPES scxmldatamodel
+ SOURCES
+ qscxmlcompiler.cpp qscxmlcompiler.h qscxmlcompiler_p.h
+ qscxmlcppdatamodel.cpp qscxmlcppdatamodel.h qscxmlcppdatamodel_p.h
+ qscxmldatamodel.cpp qscxmldatamodel.h qscxmldatamodel_p.h
+ qscxmlerror.cpp qscxmlerror.h
+ qscxmlevent.cpp qscxmlevent.h qscxmlevent_p.h
+ qscxmlexecutablecontent.cpp qscxmlexecutablecontent.h qscxmlexecutablecontent_p.h
+ qscxmlglobals.h qscxmlglobals_p.h
+ qscxmlinvokableservice.cpp qscxmlinvokableservice.h qscxmlinvokableservice_p.h
+ qscxmlnulldatamodel.cpp qscxmlnulldatamodel.h
+ qscxmlstatemachine.cpp qscxmlstatemachine.h qscxmlstatemachine_p.h
+ qscxmlstatemachineinfo.cpp qscxmlstatemachineinfo_p.h
+ qscxmltabledata.cpp qscxmltabledata.h qscxmltabledata_p.h
+ qscxmldatamodelplugin_p.h qscxmldatamodelplugin.cpp
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ LIBRARIES
+ Qt::CorePrivate
+ PUBLIC_LIBRARIES
+ Qt::Core
+ PRIVATE_MODULE_INTERFACE
+ Qt::CorePrivate
+ GENERATE_CPP_EXPORTS
+)
+
+# Install the public qscxlmc.prf file that is used by the qmake
+set(scxml_mkspecs "${CMAKE_CURRENT_SOURCE_DIR}/../../mkspecs/features/qscxmlc.prf")
+set(mkspecs_install_dir "${INSTALL_MKSPECSDIR}")
+qt_path_join(mkspecs_install_dir "${QT_INSTALL_DIR}" "${mkspecs_install_dir}" "features")
+qt_copy_or_install(FILES "${scxml_mkspecs}" DESTINATION ${mkspecs_install_dir})
+
+#### Keys ignored in scope 3:.:.:scxml.pro:NOT force_independent AND ( NOT debug_and_release OR NOT build_all OR CONFIG(release,debug OR release) ):
+# QMAKE_EXTRA_COMPILERS = "prf2build"
+# prf2build.CONFIG = "no_link" "no_clean" "target_predeps"
+# prf2build.commands = "$$QMAKE_COPY" "${QMAKE_FILE_IN}" "${QMAKE_FILE_OUT}"
+# prf2build.input = "FEATURES"
+# prf2build.name = "COPY" "${QMAKE_FILE_IN}"
+# prf2build.output = "$$[QT_INSTALL_DATA/get]/mkspecs/features/${QMAKE_FILE_BASE}${QMAKE_FILE_EXT}"
+qt_internal_add_docs(Scxml
+ doc/qtscxml.qdocconf
+)
+
+include(Qt6ScxmlMacros.cmake)
diff --git a/src/scxml/Qt5ScxmlConfigExtras.cmake.in b/src/scxml/Qt5ScxmlConfigExtras.cmake.in
index edb320a..716c64e 100644
--- a/src/scxml/Qt5ScxmlConfigExtras.cmake.in
+++ b/src/scxml/Qt5ScxmlConfigExtras.cmake.in
@@ -1,40 +1,5 @@
-#
# Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
-# Contact: https://www.qt.io/licensing/
-#
-# This file is part of the QtScxml module of the Qt Toolkit.
-#
-# $QT_BEGIN_LICENSE:LGPL$
-# Commercial License Usage
-# Licensees holding valid commercial Qt licenses may use this file in
-# accordance with the commercial license agreement provided with the
-# Software or, alternatively, in accordance with the terms contained in
-# a written agreement between you and The Qt Company. For licensing terms
-# and conditions see https://www.qt.io/terms-conditions. For further
-# information use the contact form at https://www.qt.io/contact-us.
-#
-# GNU Lesser General Public License Usage
-# Alternatively, this file may be used under the terms of the GNU Lesser
-# General Public License version 3 as published by the Free Software
-# Foundation and appearing in the file LICENSE.LGPL3 included in the
-# packaging of this file. Please review the following information to
-# ensure the GNU Lesser General Public License version 3 requirements
-# will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-#
-# GNU General Public License Usage
-# Alternatively, this file may be used under the terms of the GNU
-# General Public License version 2.0 or (at your option) the GNU General
-# Public license version 3 or any later version approved by the KDE Free
-# Qt Foundation. The licenses are as published by the Free Software
-# Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-# included in the packaging of this file. Please review the following
-# information to ensure the GNU General Public License requirements will
-# be met: https://www.gnu.org/licenses/gpl-2.0.html and
-# https://www.gnu.org/licenses/gpl-3.0.html.
-#
-# $QT_END_LICENSE$
-
-if (NOT TARGET Qt5::qscxmlc)
+# SPDX-License-Identifier: BSD-3-Clause
add_executable(Qt5::qscxmlc IMPORTED)
!!IF isEmpty(CMAKE_BIN_DIR_IS_ABSOLUTE)
@@ -49,3 +14,14 @@ if (NOT TARGET Qt5::qscxmlc)
)
get_target_property(Qt5Scxml_QSCXMLC_EXECUTABLE Qt5::qscxmlc LOCATION)
endif()
+
+# Create versionless tool targets.
+foreach(__qt_tool qscxmlc)
+ if(NOT \"${QT_NO_CREATE_VERSIONLESS_TARGETS}\" AND NOT TARGET Qt::${__qt_tool}
+ AND TARGET Qt5::${__qt_tool})
+ add_executable(Qt::${__qt_tool} IMPORTED)
+ get_target_property(__qt_imported_location Qt5::${__qt_tool} IMPORTED_LOCATION)
+ set_target_properties(Qt::${__qt_tool}
+ PROPERTIES IMPORTED_LOCATION \"${__qt_imported_location}\")
+ endif()
+endforeach()
diff --git a/src/scxml/Qt5ScxmlMacros.cmake b/src/scxml/Qt5ScxmlMacros.cmake
deleted file mode 100644
index c4454ec..0000000
--- a/src/scxml/Qt5ScxmlMacros.cmake
+++ /dev/null
@@ -1,68 +0,0 @@
-#
-# Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
-# Contact: https://www.qt.io/licensing/
-#
-# This file is part of the QtScxml module of the Qt Toolkit.
-#
-# $QT_BEGIN_LICENSE:LGPL$
-# Commercial License Usage
-# Licensees holding valid commercial Qt licenses may use this file in
-# accordance with the commercial license agreement provided with the
-# Software or, alternatively, in accordance with the terms contained in
-# a written agreement between you and The Qt Company. For licensing terms
-# and conditions see https://www.qt.io/terms-conditions. For further
-# information use the contact form at https://www.qt.io/contact-us.
-#
-# GNU Lesser General Public License Usage
-# Alternatively, this file may be used under the terms of the GNU Lesser
-# General Public License version 3 as published by the Free Software
-# Foundation and appearing in the file LICENSE.LGPL3 included in the
-# packaging of this file. Please review the following information to
-# ensure the GNU Lesser General Public License version 3 requirements
-# will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-#
-# GNU General Public License Usage
-# Alternatively, this file may be used under the terms of the GNU
-# General Public License version 2.0 or (at your option) the GNU General
-# Public license version 3 or any later version approved by the KDE Free
-# Qt Foundation. The licenses are as published by the Free Software
-# Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-# included in the packaging of this file. Please review the following
-# information to ensure the GNU General Public License requirements will
-# be met: https://www.gnu.org/licenses/gpl-2.0.html and
-# https://www.gnu.org/licenses/gpl-3.0.html.
-#
-# $QT_END_LICENSE$
-
-if(NOT Qt5Scxml_QSCXMLC_EXECUTABLE)
- message(FATAL_ERROR "qscxmlc executable not found -- Check installation.")
-endif()
-
-# qt5_add_statecharts(outfiles inputfile ... )
-
-function(qt5_add_statecharts outfiles)
- set(options)
- set(oneValueArgs)
- set(multiValueArgs OPTIONS)
-
- cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
-
- set(scxml_files ${ARGS_UNPARSED_ARGUMENTS})
-
- foreach(it ${scxml_files})
- get_filename_component(outfilename ${it} NAME_WE)
- get_filename_component(infile ${it} ABSOLUTE)
- set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${outfilename})
- set(outfile_cpp ${CMAKE_CURRENT_BINARY_DIR}/${outfilename}.cpp)
- set(outfile_h ${CMAKE_CURRENT_BINARY_DIR}/${outfilename}.h)
-
- add_custom_command(OUTPUT ${outfile_cpp} ${outfile_h}
- COMMAND ${Qt5Scxml_QSCXMLC_EXECUTABLE}
- ARGS ${ARGS_OPTIONS} --output ${outfile} ${infile}
- MAIN_DEPENDENCY ${infile}
- VERBATIM)
- list(APPEND ${outfiles} ${outfile_cpp})
- endforeach()
- set_source_files_properties(${outfiles} PROPERTIES SKIP_AUTOMOC TRUE)
- set(${outfiles} ${${outfiles}} PARENT_SCOPE)
-endfunction()
diff --git a/src/scxml/Qt6ScxmlMacros.cmake b/src/scxml/Qt6ScxmlMacros.cmake
new file mode 100644
index 0000000..c99d845
--- /dev/null
+++ b/src/scxml/Qt6ScxmlMacros.cmake
@@ -0,0 +1,79 @@
+# Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+function(qt6_add_statecharts target_or_outfiles)
+ set(options)
+ set(oneValueArgs OUTPUT_DIR OUTPUT_DIRECTORY NAMESPACE)
+ set(multiValueArgs QSCXMLC_ARGUMENTS OPTIONS)
+
+ cmake_parse_arguments(ARGS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+
+ set(scxml_files ${ARGS_UNPARSED_ARGUMENTS})
+ set(outfiles)
+
+ if (ARGS_NAMESPACE)
+ set(namespace "--namespace" ${ARGS_NAMESPACE})
+ endif()
+
+ if (ARGS_OUTPUT_DIR)
+ message(AUTHOR_WARNING
+ "OUTPUT_DIR is deprecated. Please use OUTPUT_DIRECTORY instead.")
+ set(ARGS_OUTPUT_DIRECTORY ${ARGS_OUTPUT_DIR})
+ endif()
+
+ if (ARGS_QSCXMLC_ARGUMENTS)
+ message(AUTHOR_WARNING
+ "QSCXMLC_ARGUMENTS is deprecated. Please use OPTIONS instead.")
+ set(ARGS_OPTIONS ${ARGS_QSCXMLC_ARGUMENTS})
+ endif()
+
+ set(qscxmlcOutputDir ${CMAKE_CURRENT_BINARY_DIR})
+ if (ARGS_OUTPUT_DIRECTORY)
+ set(qscxmlcOutputDir ${ARGS_OUTPUT_DIRECTORY})
+ if (NOT EXISTS "${qscxmlcOutputDir}" OR NOT IS_DIRECTORY "${qscxmlcOutputDir}")
+ message(WARNING
+ "qt6_add_statecharts: output dir does not exist: \"" ${qscxmlcOutputDir} "\". "
+ "Statechart code generation may fail on some platforms." )
+ endif()
+ endif()
+
+ _qt_internal_get_tool_wrapper_script_path(tool_wrapper)
+ set(qscxmlc_bin "${tool_wrapper}" "$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qscxmlc>")
+
+ set(outfiles)
+ foreach(it ${scxml_files})
+ get_filename_component(outfilename ${it} NAME_WE)
+ get_filename_component(infile ${it} ABSOLUTE)
+ set(outfile ${qscxmlcOutputDir}/${outfilename})
+ set(outfile_cpp ${outfile}.cpp)
+ set(outfile_h ${outfile}.h)
+
+ add_custom_command(OUTPUT ${outfile_cpp} ${outfile_h}
+ COMMAND
+ ${qscxmlc_bin} ${namespace} ${ARGS_OPTIONS}
+ --output ${outfile} ${infile}
+ DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::qscxmlc
+ MAIN_DEPENDENCY ${infile}
+ VERBATIM)
+ set_source_files_properties(${outfile_cpp} ${outfile_h} PROPERTIES SKIP_AUTOGEN TRUE)
+ list(APPEND outfiles ${outfile_cpp})
+ endforeach()
+ if (TARGET ${target_or_outfiles})
+ target_include_directories(${target_or_outfiles} PRIVATE ${qscxmlcOutputDir})
+ target_sources(${target_or_outfiles} PRIVATE ${outfiles})
+ else()
+ set(${target_or_outfiles} ${outfiles} PARENT_SCOPE)
+ endif()
+endfunction()
+
+if(NOT QT_NO_CREATE_VERSIONLESS_FUNCTIONS)
+ function(qt_add_statecharts outfiles)
+ if(QT_DEFAULT_MAJOR_VERSION EQUAL 5)
+ qt5_add_statecharts("${outfiles}" ${ARGN})
+ elseif(QT_DEFAULT_MAJOR_VERSION EQUAL 6)
+ qt6_add_statecharts("${outfiles}" ${ARGN})
+ endif()
+ set("${outfiles}" "${${outfiles}}" PARENT_SCOPE)
+ endfunction()
+endif()
diff --git a/src/scxml/configure.cmake b/src/scxml/configure.cmake
new file mode 100644
index 0000000..55fc1d5
--- /dev/null
+++ b/src/scxml/configure.cmake
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+
+#### Inputs
+
+
+
+#### Libraries
+
+
+
+#### Tests
+
+
+
+#### Features
+
+qt_feature("scxml-ecmascriptdatamodel" PUBLIC
+ SECTION "SCXML"
+ LABEL "ECMAScript data model for QtScxml"
+ PURPOSE "Enables the usage of ecmascript data models in SCXML state machines."
+ CONDITION TARGET Qt::Qml # special case
+)
+qt_configure_add_summary_section(NAME "Qt Scxml")
+qt_configure_add_summary_entry(ARGS "scxml-ecmascriptdatamodel")
+qt_configure_end_summary_section() # end of "Qt Scxml" section
diff --git a/src/scxml/configure.json b/src/scxml/configure.json
deleted file mode 100644
index 288373d..0000000
--- a/src/scxml/configure.json
+++ /dev/null
@@ -1,21 +0,0 @@
-{
- "module": "scxml",
-
- "features": {
- "scxml-ecmascriptdatamodel": {
- "label": "ECMAScript data model for QtScxml",
- "purpose": "Enables the usage of ecmascript data models in SCXML state machines.",
- "section": "SCXML",
- "output": [ "publicFeature" ]
- }
- },
-
- "summary": [
- {
- "section": "Qt Scxml",
- "entries": [
- "scxml-ecmascriptdatamodel"
- ]
- }
- ]
-}
diff --git a/src/scxml/doc/external-resources.qdoc b/src/scxml/doc/external-resources.qdoc
index 39a1c3b..10650fa 100644
--- a/src/scxml/doc/external-resources.qdoc
+++ b/src/scxml/doc/external-resources.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\externalpage http://www.w3.org/TR/scxml/
\title SCXML Specification
diff --git a/src/scxml/doc/qt6-changes.qdoc b/src/scxml/doc/qt6-changes.qdoc
new file mode 100644
index 0000000..da14af4
--- /dev/null
+++ b/src/scxml/doc/qt6-changes.qdoc
@@ -0,0 +1,45 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+ \page qtscxml-changes-qt6.html
+ \title Changes to Qt SCXML
+ \ingroup changes-qt-5-to-6
+ \brief Migrate Qt SCXML 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 SCXML module, and provide
+ guidance to handle them.
+
+ \section1 Changes overview
+
+ The Qt SCXML module is largely source compatible with the Qt5
+ version and users of the library should be able to continue with no or
+ minor changes to their project.
+
+ \section1 API changes
+
+ \section2 QScxmlEcmaScriptDataModel API removal
+
+ The ecmascript datamodel, when enabled, introduces a dependency to the Qt QML library.
+ In Qt5 this depedendency is created at build time, whereas in Qt6 the dependency
+ is moved to runtime (internally a plugin). As a consequence the
+ QScxmlEcmaScriptDataModel class is no longer part of the public API.
+
+ \section1 Build system
+
+ As with Qt6 in general, the Qt SCXML 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/scxml/doc/qtscxml-cmake-macros.qdoc b/src/scxml/doc/qtscxml-cmake-macros.qdoc
new file mode 100644
index 0000000..1a182cb
--- /dev/null
+++ b/src/scxml/doc/qtscxml-cmake-macros.qdoc
@@ -0,0 +1,29 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
+
+/*!
+\page qtscxml-cmake-qt-add-statecharts.html
+\ingroup cmake-macros-qtscxml
+
+\title qt_add_statecharts
+\target qt6_add_statecharts
+
+\cmakecommandsince 6.1
+
+\section1 Description
+
+The \c qt6_add_statecharts macro instructs CMake to invoke the qscxmlc tool to
+read the provided .scxml files and produce C++ source and header files,
+that contain the classes that implement the state machines as defined in SCXML.
+
+\section1 Synopsis
+
+\badcode
+qt6_add_statecharts(<TARGET> file1.scxml [file2.scxml ...]
+ [OPTIONS ...])
+\endcode
+
+For further instructions, options and examples please refer to
+\l {Using the Qt SCXML Compiler (qscxmlc)}
+
+*/
diff --git a/src/scxml/doc/qtscxml-examples.qdoc b/src/scxml/doc/qtscxml-examples.qdoc
index cc0b6c4..e645bd6 100644
--- a/src/scxml/doc/qtscxml-examples.qdoc
+++ b/src/scxml/doc/qtscxml-examples.qdoc
@@ -1,49 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\group examples-qtscxml
\title Qt SCXML Examples
\brief Examples for the Qt SCXML module.
-\ingroup all-examples
The Qt SCXML example applications demonstrate the functionality provided by the
\l{Qt SCXML} module.
-There are multiple versions of the \e Invoke, \e {Media Player}, and
-\e {Traffic Light} example applications. Each application has some common files
-that are stored in a common folder, in addition to the files stored in the
-example version folder.
+There are multiple versions of the \e {Traffic Light} example application.
+The application has shared common files, and in addition specific files for
+each version of the application, under their respective folders. They demonstrate
+the different options for creating user interfaces (using \l {Qt Widgets}
+or \l {Qt Quick}) and for loading the SCXML dynamically versus first compiling
+it to a C++ class (the \e static versions).
-All versions of an example application have the same appearance and
-fuctionality. They demonstrate the different options for creating user
-interfaces (using \l {Qt Widgets} or \l {Qt Quick}) and for loading the SCXML
-dynamically versus first compiling it to a C++ class (the \e static versions).
-
-In addition, the Media Player example versions demonstrate how to access the C++
-and ECMAScript data models.
*/
diff --git a/src/scxml/doc/qtscxml-index.qdoc b/src/scxml/doc/qtscxml-index.qdoc
index b65a1c2..ca088e0 100644
--- a/src/scxml/doc/qtscxml-index.qdoc
+++ b/src/scxml/doc/qtscxml-index.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtscxml-index.html
@@ -39,24 +15,20 @@
\section1 Getting Started
- To include the definitions of the module's classes, use the following directive:
-
- \code
- #include <QScxmlStateMachine>
- \endcode
-
To import the QML types into your application, use the following import statement
in your .qml file:
- \qml \QtMinorVersion
- import QtScxml 5.\1
+ \qml
+ import QtScxml
\endqml
- To link against the module, add this line to your qmake .pro file:
+ To link against the module:
+
+ Using cmake:
+ \include qtscxml-module-use.qdocinc cmakebuild
- \code
- QT += scxml
- \endcode
+ Using qmake:
+ \include qtscxml-module-use.qdocinc qmakebuild
\section1 Articles and Guides
@@ -79,4 +51,16 @@
\li \l {Qt SCXML C++ Classes} {C++ Classes and Namespaces}
\li \l {Qt SCXML QML Types} {QML Types}
\endlist
+
+ \section1 Module Evolution
+ \l{Changes to Qt SCXML} 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 SCXML 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.
*/
diff --git a/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc b/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc
index 37cff10..51c51e2 100644
--- a/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc
+++ b/src/scxml/doc/qtscxml-instantiating-state-machines.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtscxml-instantiating-state-machines.html
@@ -43,7 +19,7 @@
Or, in QML:
\qml
- import QtScxml 5.8
+ import QtScxml
Item {
property StateMachine stateMachine: scxmlLoader.stateMachine
@@ -71,7 +47,12 @@
To use a compiled state machine in QML, you can register it as a QML type:
\code
- qmlRegisterType<MyStateMachine>("MyStateMachine", 1, 0, "MyStateMachine");
+ struct MyStateMachineRegistration {
+ Q_GADGET
+ QML_NAMED_ELEMENT(MyStateMachine)
+ QML_FOREIGN(MyStateMachine)
+ QML_ADDED_IN_VERSION(1, 0)
+ };
\endcode
Then you can instantiate it in QML, like this:
@@ -84,19 +65,25 @@
}
\endqml
- To compile a state machine, the following lines have to be added to a
- .pro file:
+ To compile a state machine, the following lines have to be added to the
+ project build file:
- \badcode
- QT += scxml
- STATECHARTS = MyStatemachine.scxml
- \endcode
+ When using cmake:
+
+ \include qtscxml-module-use.qdocinc cmakebuild
+ \include qtscxml-module-use.qdocinc cmakestatecharts
+
+ When using qmake:
+
+ \include qtscxml-module-use.qdocinc qmakebuild
+ \include qtscxml-module-use.qdocinc qmakestatecharts
This will tell qmake to run \e qscxmlc which generates MyStatemachine.h
- and MyStatemachine.cpp, and adds them to \l [QMake] HEADERS and
- \l [QMake] SOURCES variables. By default, the generated files are saved in
- the build directory. The \e QSCXMLC_DIR variable can be set to specify
- another directory. The \e QSCXMLC_NAMESPACE variable can be set to put the
+ and MyStatemachine.cpp, and adds them to appropriately to the project
+ headers and sources. By default, the generated files are saved in
+ the build directory. The qmake \e QSCXMLC_DIR or cmake \e OUTPUT_DIRECTORY
+ variable can be set to specify another directory. The qmake
+ \e QSCXMLC_NAMESPACE or cmake \e NAMESPACE variable can be set to put the
state machine code into a C++ namespace.
After instantiating a state machine, you can connect to any state's
@@ -107,6 +94,7 @@
\code
stateMachine->connectToState("red", [](bool active) {
qDebug() << (active ? "entered" : "exited") << "the red state";
+ });
\endcode
And in QML:
@@ -133,7 +121,7 @@
And in QML:
\qml
- import QtScxml 5.8
+ import QtScxml
EventConnection {
stateMachine: stateMachine
@@ -148,7 +136,7 @@
stateMachine->submitEvent("tap", QVariantMap({
{ "artist", "Fatboy Slim" },
{ "title", "The Rockafeller Skank" }
- });
+ }));
\endcode
This will generate a "tap" event with the map contents available in
diff --git a/src/scxml/doc/qtscxml-module-cpp.qdoc b/src/scxml/doc/qtscxml-module-cpp.qdoc
index 88002ca..2161f08 100644
--- a/src/scxml/doc/qtscxml-module-cpp.qdoc
+++ b/src/scxml/doc/qtscxml-module-cpp.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\module QtScxml
@@ -33,18 +9,15 @@
\ingroup modules
\ingroup technology-apis
\qtvariable scxml
+ \qtcmakepackage Scxml
- To include the definitions of the module's classes, use the following directive:
+ To use the module with cmake, use the \c{find_package()} command to locate the
+ needed module components in the \c{Qt6} package:
+ \include qtscxml-module-use.qdocinc cmakebuild
- \code
- #include <QScxmlStateMachine>
- \endcode
-
- To link against the module, add this line to your qmake .pro file:
-
- \code
- QT += scxml
- \endcode
+ To configure the module for building with qmake, add the module as a value
+ of the \c QT variable in the project's .pro file:
+ \include qtscxml-module-use.qdocinc qmakebuild
For more information, see \l{Instantiating State Machines}.
*/
diff --git a/src/scxml/doc/qtscxml-module-qml.qdoc b/src/scxml/doc/qtscxml-module-qml.qdoc
index 5fdae36..5bddabc 100644
--- a/src/scxml/doc/qtscxml-module-qml.qdoc
+++ b/src/scxml/doc/qtscxml-module-qml.qdoc
@@ -1,32 +1,8 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
- \qmlmodule QtScxml 5.\QtMinorVersion
+ \qmlmodule QtScxml 6.\QtMinorVersion
\title Qt SCXML QML Types
\ingroup qmlmodules
\brief Enables the use of SCXML state machines with QML.
@@ -34,9 +10,9 @@
To import the QML types into your application, use the following import statement
in your .qml file:
- \code \QtMinorVersion
- import QtScxml 5.\1
- \endcode
+ \qml
+ import QtScxml
+ \endqml
For more information, see \l{Instantiating State Machines}.
*/
diff --git a/src/scxml/doc/qtscxml-module-use.qdocinc b/src/scxml/doc/qtscxml-module-use.qdocinc
new file mode 100644
index 0000000..ba48db0
--- /dev/null
+++ b/src/scxml/doc/qtscxml-module-use.qdocinc
@@ -0,0 +1,30 @@
+// 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 Scxml)
+ target_link_libraries(mytarget PRIVATE Qt6::Scxml)
+ \endcode
+//! [cmakebuild]
+
+//! [qmakebuild]
+ \code
+ QT += scxml
+ \endcode
+//! [qmakebuild]
+
+//! [qmakestatecharts]
+ \code
+ STATECHARTS = MyStatemachine.scxml
+ \endcode
+//! [qmakestatecharts]
+
+//! [cmakestatecharts]
+ \code
+ qt6_add_statecharts(mytarget
+ MyStatemachine.scxml
+ )
+ \endcode
+//! [cmakestatecharts]
+
diff --git a/src/scxml/doc/qtscxml-overview.qdoc b/src/scxml/doc/qtscxml-overview.qdoc
index dfecb0a..dfbe0bf 100644
--- a/src/scxml/doc/qtscxml-overview.qdoc
+++ b/src/scxml/doc/qtscxml-overview.qdoc
@@ -1,34 +1,11 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtscxml-overview.html
\title Qt SCXML Overview
\brief Describes the Qt SCXML module.
+ \ingroup explanation
The Qt SCXML module provides classes for embedding state machines created
from State Chart XML (SCXML) files in Qt applications. The SCXML files
@@ -40,8 +17,8 @@
file. This enables creating a clear division between the application logic
and the user interface implementation by using Qt Quick or Qt Widgets.
- The Qt SCXML module differs from the \l {The State Machine Framework}
- {State Machine framework} in the Qt Core module in that Qt SCXML provides a
+ The Qt SCXML module differs from the \l {Qt State Machine Overview}
+ {State Machine framework} in the \l{Qt State Machine} module in that Qt SCXML provides a
\e {conforming processor} that can parse and process \e {conforming SCXML
documents}. In Qt SCXML, state machines are read from separate SCXML files
and integrated to Qt applications by instantiating the QScxmlStateMachine
diff --git a/src/scxml/doc/qtscxml-scxml-compliance.qdoc b/src/scxml/doc/qtscxml-scxml-compliance.qdoc
index ab1d730..6721c38 100644
--- a/src/scxml/doc/qtscxml-scxml-compliance.qdoc
+++ b/src/scxml/doc/qtscxml-scxml-compliance.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qtscxml-scxml-compliance.html
diff --git a/src/scxml/doc/qtscxml.qdocconf b/src/scxml/doc/qtscxml.qdocconf
index 6d90bd1..b8211e6 100644
--- a/src/scxml/doc/qtscxml.qdocconf
+++ b/src/scxml/doc/qtscxml.qdocconf
@@ -2,30 +2,27 @@ include($QT_INSTALL_DOCS/global/qt-module-defaults.qdocconf)
include($QT_INSTALL_DOCS/config/exampleurl-qtscxml.qdocconf)
project = QtScxml
-description = Qt Scxml Reference Documentation
+description = Qt SCXML Reference Documentation
version = $QT_VERSION
-# Install path for the examples. For Qt 5.6, the convention
-# is to use the repository name as the install location
-# under QT_INSTALL_EXAMPLES.
+# Install path for the examples
examplesinstallpath = scxml
exampledirs = ../../../examples/scxml
-imagedirs = ../../../examples/doc/images
examples.fileextensions += "*.scxml"
qhp.QtScxml.subprojects = classes qmltypes examples
qhp.QtScxml.subprojects.classes.title = C++ Classes
qhp.QtScxml.subprojects.classes.indexTitle = Qt SCXML C++ Classes
-qhp.QtScxml.subprojects.classes.selectors = class fake:headerfile
+qhp.QtScxml.subprojects.classes.selectors = class headerfile
qhp.QtScxml.subprojects.classes.sortPages = true
qhp.QtScxml.subprojects.qmltypes.title = QML Types
qhp.QtScxml.subprojects.qmltypes.indexTitle = Qt SCXML QML Types
-qhp.QtScxml.subprojects.qmltypes.selectors = qmlclass
+qhp.QtScxml.subprojects.qmltypes.selectors = qmltype
qhp.QtScxml.subprojects.qmltypes.sortPages = true
qhp.QtScxml.subprojects.examples.title = Examples
qhp.QtScxml.subprojects.examples.indexTitle = Qt SCXML Examples
-qhp.QtScxml.subprojects.examples.selectors = fake:example
+qhp.QtScxml.subprojects.examples.selectors = doc:example
qhp.projects = QtScxml
@@ -35,21 +32,24 @@ qhp.QtScxml.virtualFolder = qtscxml
qhp.QtScxml.indexTitle = Qt SCXML
qhp.QtScxml.indexRoot =
-depends += qtcore qtdoc qmake qtquick qtwidgets
+depends += qtcore qtdoc qmake qtquick qtwidgets qtstatemachine qtcmake
+
+headerdirs = .. \
+ ../../scxmlqml \
+ ../../plugins/ecmascriptdatamodel
-headerdirs = .. ../../imports/scxmlstatemachine
sourcedirs += .. \
- ../../imports/scxmlstatemachine \
+ ../../scxmlqml \
../../../tools/qscxmlc/doc \
- ../../../examples/scxml
+ ../../plugins/ecmascriptdatamodel
excludefiles += "../qscxmlexecutablecontent_p.h"
tagfile = qtscxml.tags
-manifestmeta.highlighted.names = "QtScxml/Qt SCXML Calculator QML Example" \
- "QtScxml/Qt SCXML Traffic Light QML Example (Dynamic)"
-
navigation.landingpage = "Qt SCXML"
navigation.cppclassespage = "Qt SCXML C++ Classes"
navigation.qmltypespage = "Qt SCXML QML Types"
+
+# Highlighted examples in Data Processing & IO category
+manifestmeta.highlighted.names = "QtScxml/SCXML Sudoku"
diff --git a/src/scxml/qscxmlcompiler.cpp b/src/scxml/qscxmlcompiler.cpp
index 36967d1..eaf7171 100644
--- a/src/scxml/qscxmlcompiler.cpp
+++ b/src/scxml/qscxmlcompiler.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmlcompiler_p.h"
#include "qscxmlexecutablecontent_p.h"
@@ -43,7 +7,7 @@
#include <qxmlstream.h>
#include <qloggingcategory.h>
#include <qfile.h>
-#include <qvector.h>
+#include <qlist.h>
#include <qstring.h>
#ifndef BUILD_QSCXMLC
@@ -56,6 +20,8 @@
#include <private/qmetaobjectbuilder_p.h>
#endif // BUILD_QSCXMLC
+#include <QtCore/qmap.h>
+
#include <functional>
namespace {
@@ -87,7 +53,7 @@ public:
doc->isVerified = true;
m_doc = doc;
- for (DocumentModel::AbstractState *state : qAsConst(doc->allStates)) {
+ for (DocumentModel::AbstractState *state : std::as_const(doc->allStates)) {
if (state->id.isEmpty()) {
continue;
#ifndef QT_NO_DEBUG
@@ -117,8 +83,8 @@ private:
scxml->initialTransition = createInitialTransition({firstChild});
}
} else {
- QVector<DocumentModel::AbstractState *> initialStates;
- for (const QString &initial : qAsConst(scxml->initial)) {
+ QList<DocumentModel::AbstractState *> initialStates;
+ for (const QString &initial : std::as_const(scxml->initial)) {
if (DocumentModel::AbstractState *s = m_stateById.value(initial))
initialStates.append(s);
else
@@ -155,8 +121,8 @@ private:
}
} else {
Q_ASSERT(state->type == DocumentModel::State::Normal);
- QVector<DocumentModel::AbstractState *> initialStates;
- for (const QString &initialState : qAsConst(state->initial)) {
+ QList<DocumentModel::AbstractState *> initialStates;
+ for (const QString &initialState : std::as_const(state->initial)) {
if (DocumentModel::AbstractState *s = m_stateById.value(initialState)) {
initialStates.append(s);
} else {
@@ -207,7 +173,7 @@ private:
if (int size = transition->targets.size())
transition->targetStates.reserve(size);
- for (const QString &target : qAsConst(transition->targets)) {
+ for (const QString &target : std::as_const(transition->targets)) {
if (DocumentModel::AbstractState *s = m_stateById.value(target)) {
if (transition->targetStates.contains(s)) {
error(transition->xmlLocation, QStringLiteral("duplicate target '%1'").arg(target));
@@ -218,7 +184,7 @@ private:
error(transition->xmlLocation, QStringLiteral("unknown state '%1' in target").arg(target));
}
}
- for (const QString &event : qAsConst(transition->events))
+ for (const QString &event : std::as_const(transition->events))
checkEvent(event, transition->xmlLocation, AllowWildCards);
m_parentNodes.append(transition);
@@ -233,7 +199,7 @@ private:
bool visit(DocumentModel::HistoryState *state) override
{
bool seenTransition = false;
- for (DocumentModel::StateOrTransition *sot : qAsConst(state->children)) {
+ for (DocumentModel::StateOrTransition *sot : std::as_const(state->children)) {
if (DocumentModel::State *s = sot->asState()) {
error(s->xmlLocation, QStringLiteral("history state cannot have substates"));
} else if (DocumentModel::Transition *t = sot->asTransition()) {
@@ -298,7 +264,7 @@ private:
if (!isLetter(c) && c != QLatin1Char('_'))
return false;
}
- for (int ei = id.length(); i != ei; ++i) {
+ for (int ei = id.size(); i != ei; ++i) {
const QChar c = id.at(i);
if (isLetter(c) || c.isDigit() || c == QLatin1Char('.') || c == QLatin1Char('-')
|| c == QLatin1Char('_') || isNameTail(c))
@@ -370,12 +336,12 @@ private:
if (part.isEmpty())
return false;
- if (wildCardMode == AllowWildCards && part.length() == 1
+ if (wildCardMode == AllowWildCards && part.size() == 1
&& part.at(0) == QLatin1Char('*')) {
continue;
}
- for (int i = 0, ei = part.length(); i != ei; ++i) {
+ for (int i = 0, ei = part.size(); i != ei; ++i) {
const QChar c = part.at(i);
if (!isLetter(c) && !c.isDigit() && c != QLatin1Char('-') && c != QLatin1Char('_')
&& c != QLatin1Char(':')) {
@@ -387,7 +353,7 @@ private:
return true;
}
- static const QVector<DocumentModel::StateOrTransition *> &allChildrenOfContainer(
+ static const QList<DocumentModel::StateOrTransition *> &allChildrenOfContainer(
DocumentModel::StateContainer *container)
{
if (auto state = container->asState())
@@ -402,8 +368,8 @@ private:
{
const auto &allChildren = allChildrenOfContainer(container);
- QVector<DocumentModel::AbstractState *> childStates;
- for (DocumentModel::StateOrTransition *child : qAsConst(allChildren)) {
+ QList<DocumentModel::AbstractState *> childStates;
+ for (DocumentModel::StateOrTransition *child : std::as_const(allChildren)) {
if (DocumentModel::State *s = child->asState())
return s;
else if (DocumentModel::HistoryState *h = child->asHistoryState())
@@ -412,13 +378,13 @@ private:
return nullptr;
}
- static QVector<DocumentModel::AbstractState *> allAbstractStates(
+ static QList<DocumentModel::AbstractState *> allAbstractStates(
DocumentModel::StateContainer *container)
{
const auto &allChildren = allChildrenOfContainer(container);
- QVector<DocumentModel::AbstractState *> childStates;
- for (DocumentModel::StateOrTransition *child : qAsConst(allChildren)) {
+ QList<DocumentModel::AbstractState *> childStates;
+ for (DocumentModel::StateOrTransition *child : std::as_const(allChildren)) {
if (DocumentModel::State *s = child->asState())
childStates.append(s);
else if (DocumentModel::HistoryState *h = child->asHistoryState())
@@ -428,7 +394,7 @@ private:
}
DocumentModel::Transition *createInitialTransition(
- const QVector<DocumentModel::AbstractState *> &states)
+ const QList<DocumentModel::AbstractState *> &states)
{
auto *newTransition = m_doc->newTransition(nullptr, DocumentModel::XmlLocation(-1, -1));
newTransition->type = DocumentModel::Transition::Synthetic;
@@ -460,7 +426,7 @@ private:
DocumentModel::ScxmlDocument *m_doc;
bool m_hasErrors;
QHash<QString, DocumentModel::AbstractState *> m_stateById;
- QVector<DocumentModel::Node *> m_parentNodes;
+ QList<DocumentModel::Node *> m_parentNodes;
};
#ifndef BUILD_QSCXMLC
@@ -469,8 +435,8 @@ class InvokeDynamicScxmlFactory: public QScxmlInvokableServiceFactory
Q_OBJECT
public:
InvokeDynamicScxmlFactory(const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- const QVector<QScxmlExecutableContent::ParameterInfo> &params)
+ const QList<QScxmlExecutableContent::StringId> &namelist,
+ const QList<QScxmlExecutableContent::ParameterInfo> &params)
: QScxmlInvokableServiceFactory(invokeInfo, namelist, params)
{}
@@ -485,9 +451,31 @@ private:
class DynamicStateMachinePrivate : public QScxmlStateMachinePrivate
{
+ struct DynamicMetaObject : public QAbstractDynamicMetaObject
+ {
+ QMetaObject *toDynamicMetaObject(QObject *) override
+ {
+ return this;
+ }
+
+ int metaCall(QObject *o, QMetaObject::Call c, int id, void **a) override
+ {
+ return o->qt_metacall(c, id, a);
+ }
+ };
+
public:
DynamicStateMachinePrivate() :
- QScxmlStateMachinePrivate(&QScxmlStateMachine::staticMetaObject) {}
+ QScxmlStateMachinePrivate(&QScxmlStateMachine::staticMetaObject)
+ {
+ metaObject = new DynamicMetaObject;
+ }
+
+ void setDynamicMetaObject(const QMetaObject *m) {
+ // Prevent the QML engine from creating a property cache for this thing.
+ static_cast<DynamicMetaObject *>(metaObject)->d = m->d;
+ m_metaObject = m;
+ }
};
class DynamicStateMachine: public QScxmlStateMachine, public QScxmlInternal::GeneratedTableData
@@ -544,7 +532,7 @@ private:
b.setClassName("DynamicStateMachine");
b.setSuperClass(&QScxmlStateMachine::staticMetaObject);
b.setStaticMetacallFunction(qt_static_metacall);
- d->m_metaObject = b.toMetaObject();
+ d->setDynamicMetaObject(b.toMetaObject());
}
void initDynamicParts(const MetaDataInfo &info)
@@ -553,7 +541,7 @@ private:
// Release the temporary QMetaObject.
Q_ASSERT(d->m_metaObject != &QScxmlStateMachine::staticMetaObject);
free(const_cast<QMetaObject *>(d->m_metaObject));
- d->m_metaObject = &QScxmlStateMachine::staticMetaObject;
+ d->setDynamicMetaObject(&QScxmlStateMachine::staticMetaObject);
// Build the real one.
QMetaObjectBuilder b;
@@ -579,7 +567,7 @@ private:
}
// And we're done
- d->m_metaObject = b.toMetaObject();
+ d->setDynamicMetaObject(b.toMetaObject());
}
public:
@@ -588,7 +576,7 @@ public:
Q_D(DynamicStateMachine);
if (d->m_metaObject != &QScxmlStateMachine::staticMetaObject) {
free(const_cast<QMetaObject *>(d->m_metaObject));
- d->m_metaObject = &QScxmlStateMachine::staticMetaObject;
+ d->setDynamicMetaObject(&QScxmlStateMachine::staticMetaObject);
}
}
@@ -602,8 +590,8 @@ public:
DataModelInfo dm;
auto factoryIdCreator = [stateMachine](
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- const QVector<QScxmlExecutableContent::ParameterInfo> &params,
+ const QList<QScxmlExecutableContent::StringId> &namelist,
+ const QList<QScxmlExecutableContent::ParameterInfo> &params,
const QSharedPointer<DocumentModel::ScxmlDocument> &content) -> int {
auto factory = new InvokeDynamicScxmlFactory(invokeInfo, namelist, params);
factory->setContent(content);
@@ -629,7 +617,7 @@ private:
}
private:
- QVector<QScxmlInvokableServiceFactory *> m_allFactoriesById;
+ QList<QScxmlInvokableServiceFactory *> m_allFactoriesById;
int m_propertyCount;
};
@@ -840,7 +828,7 @@ QScxmlStateMachine *QScxmlCompilerPrivate::instantiateStateMachine() const
void QScxmlCompilerPrivate::instantiateDataModel(QScxmlStateMachine *stateMachine) const
{
#ifdef BUILD_QSCXMLC
- Q_UNUSED(stateMachine)
+ Q_UNUSED(stateMachine);
#else
if (!m_errors.isEmpty()) {
qWarning() << "SCXML document has errors";
@@ -864,7 +852,7 @@ void QScxmlCompilerPrivate::instantiateDataModel(QScxmlStateMachine *stateMachin
/*!
* Returns the list of parse errors.
*/
-QVector<QScxmlError> QScxmlCompiler::errors() const
+QList<QScxmlError> QScxmlCompiler::errors() const
{
return d->errors();
}
@@ -1008,7 +996,7 @@ bool QScxmlCompilerPrivate::ParserState::isExecutableContent(ParserState::Kind k
return false;
}
-QScxmlCompilerPrivate::ParserState::Kind QScxmlCompilerPrivate::ParserState::nameToParserStateKind(const QStringRef &name)
+QScxmlCompilerPrivate::ParserState::Kind QScxmlCompilerPrivate::ParserState::nameToParserStateKind(QStringView name)
{
static QMap<QString, ParserState::Kind> nameToKind;
if (nameToKind.isEmpty()) {
@@ -1173,7 +1161,7 @@ void DocumentModel::Param::accept(DocumentModel::NodeVisitor *visitor)
void DocumentModel::DoneData::accept(DocumentModel::NodeVisitor *visitor)
{
if (visitor->visit(this)) {
- for (Param *param : qAsConst(params))
+ for (Param *param : std::as_const(params))
param->accept(visitor);
}
visitor->endVisit(this);
@@ -1246,7 +1234,7 @@ void DocumentModel::State::accept(DocumentModel::NodeVisitor *visitor)
visitor->visit(onExit);
if (doneData)
doneData->accept(visitor);
- for (Invoke *invoke : qAsConst(invokes))
+ for (Invoke *invoke : std::as_const(invokes))
invoke->accept(visitor);
}
visitor->endVisit(this);
@@ -1333,7 +1321,7 @@ bool QScxmlCompilerPrivate::verifyDocument()
this->addError(location, msg);
};
- if (ScxmlVerifier(handler).verify(m_doc.data()))
+ if (ScxmlVerifier(handler).verify(m_doc.get()))
return true;
else
return false;
@@ -1341,7 +1329,7 @@ bool QScxmlCompilerPrivate::verifyDocument()
DocumentModel::ScxmlDocument *QScxmlCompilerPrivate::scxmlDocument() const
{
- return m_doc && m_errors.isEmpty() ? m_doc.data() : nullptr;
+ return m_doc && m_errors.isEmpty() ? m_doc.get() : nullptr;
}
QString QScxmlCompilerPrivate::fileName() const
@@ -1372,7 +1360,7 @@ void QScxmlCompilerPrivate::parseSubDocument(DocumentModel::Invoke *parentInvoke
p.setFileName(fileName);
p.setLoader(loader());
p.d->readDocument();
- parentInvoke->content.reset(p.d->m_doc.take());
+ parentInvoke->content.reset(p.d->m_doc.release());
m_doc->allSubDocuments.append(parentInvoke->content.data());
m_errors.append(p.errors());
}
@@ -1386,7 +1374,7 @@ bool QScxmlCompilerPrivate::parseSubElement(DocumentModel::Invoke *parentInvoke,
p.setLoader(loader());
p.d->resetDocument();
bool ok = p.d->readElement();
- parentInvoke->content.reset(p.d->m_doc.take());
+ parentInvoke->content.reset(p.d->m_doc.release());
m_doc->allSubDocuments.append(parentInvoke->content.data());
m_errors.append(p.errors());
return ok;
@@ -1404,10 +1392,10 @@ bool QScxmlCompilerPrivate::preReadElementScxml()
const QXmlStreamAttributes attributes = m_reader->attributes();
if (attributes.hasAttribute(QStringLiteral("initial"))) {
const QString initial = attributes.value(QStringLiteral("initial")).toString();
- scxml->initial += initial.split(QChar::Space, QString::SkipEmptyParts);
+ scxml->initial += initial.split(QChar::Space, Qt::SkipEmptyParts);
}
- const QStringRef datamodel = attributes.value(QLatin1String("datamodel"));
+ const QStringView datamodel = attributes.value(QLatin1String("datamodel"));
if (datamodel.isEmpty() || datamodel == QLatin1String("null")) {
scxml->dataModel = DocumentModel::Scxml::NullDataModel;
} else if (datamodel == QLatin1String("ecmascript")) {
@@ -1421,7 +1409,7 @@ bool QScxmlCompilerPrivate::preReadElementScxml()
} else {
int lastColon = datamodel.lastIndexOf(QLatin1Char(':'));
if (lastColon == -1) {
- lastColon = datamodel.length();
+ lastColon = datamodel.size();
} else {
scxml->cppDataModelHeaderName = datamodel.mid(lastColon + 1).toString();
}
@@ -1431,7 +1419,7 @@ bool QScxmlCompilerPrivate::preReadElementScxml()
addError(QStringLiteral("Unsupported data model '%1' in scxml")
.arg(datamodel.toString()));
}
- const QStringRef binding = attributes.value(QLatin1String("binding"));
+ const QStringView binding = attributes.value(QLatin1String("binding"));
if (binding.isEmpty() || binding == QLatin1String("early")) {
scxml->binding = DocumentModel::Scxml::EarlyBinding;
} else if (binding == QLatin1String("late")) {
@@ -1441,7 +1429,7 @@ bool QScxmlCompilerPrivate::preReadElementScxml()
.arg(binding.toString()));
return false;
}
- const QStringRef name = attributes.value(QLatin1String("name"));
+ const QStringView name = attributes.value(QLatin1String("name"));
if (!name.isEmpty()) {
scxml->name = name.toString();
}
@@ -1460,7 +1448,7 @@ bool QScxmlCompilerPrivate::preReadElementState()
if (attributes.hasAttribute(QStringLiteral("initial"))) {
const QString initial = attributes.value(QStringLiteral("initial")).toString();
- newState->initial += initial.split(QChar::Space, QString::SkipEmptyParts);
+ newState->initial += initial.split(QChar::Space, Qt::SkipEmptyParts);
}
m_currentState = newState;
return true;
@@ -1525,11 +1513,11 @@ bool QScxmlCompilerPrivate::preReadElementTransition()
}
const QXmlStreamAttributes attributes = m_reader->attributes();
- transition->events = attributes.value(QLatin1String("event")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
- transition->targets = attributes.value(QLatin1String("target")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
+ transition->events = attributes.value(QLatin1String("event")).toString().split(QLatin1Char(' '), Qt::SkipEmptyParts);
+ transition->targets = attributes.value(QLatin1String("target")).toString().split(QLatin1Char(' '), Qt::SkipEmptyParts);
if (attributes.hasAttribute(QStringLiteral("cond")))
transition->condition.reset(new QString(attributes.value(QLatin1String("cond")).toString()));
- QStringRef type = attributes.value(QLatin1String("type"));
+ QStringView type = attributes.value(QLatin1String("type"));
if (type.isEmpty() || type == QLatin1String("external")) {
transition->type = DocumentModel::Transition::External;
} else if (type == QLatin1String("internal")) {
@@ -1565,7 +1553,7 @@ bool QScxmlCompilerPrivate::preReadElementHistory()
if (!maybeId(attributes, &newState->id))
return false;
- const QStringRef type = attributes.value(QLatin1String("type"));
+ const QStringView type = attributes.value(QLatin1String("type"));
if (type.isEmpty() || type == QLatin1String("shallow")) {
newState->type = DocumentModel::HistoryState::Shallow;
} else if (type == QLatin1String("deep")) {
@@ -1813,7 +1801,7 @@ bool QScxmlCompilerPrivate::preReadElementSend()
send->target = attributes.value(QLatin1String("target")).toString();
send->targetexpr = attributes.value(QLatin1String("targetexpr")).toString();
if (attributes.hasAttribute(QLatin1String("namelist")))
- send->namelist = attributes.value(QLatin1String("namelist")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
+ send->namelist = attributes.value(QLatin1String("namelist")).toString().split(QLatin1Char(' '), Qt::SkipEmptyParts);
current().instruction = send;
return true;
}
@@ -1845,16 +1833,16 @@ bool QScxmlCompilerPrivate::preReadElementInvoke()
invoke->idLocation = attributes.value(QLatin1String("idlocation")).toString();
invoke->type = attributes.value(QLatin1String("type")).toString();
invoke->typeexpr = attributes.value(QLatin1String("typeexpr")).toString();
- QStringRef autoforwardS = attributes.value(QLatin1String("autoforward"));
- if (QStringRef::compare(autoforwardS, QLatin1String("true"), Qt::CaseInsensitive) == 0
- || QStringRef::compare(autoforwardS, QLatin1String("yes"), Qt::CaseInsensitive) == 0
- || QStringRef::compare(autoforwardS, QLatin1String("t"), Qt::CaseInsensitive) == 0
- || QStringRef::compare(autoforwardS, QLatin1String("y"), Qt::CaseInsensitive) == 0
+ QStringView autoforwardS = attributes.value(QLatin1String("autoforward"));
+ if (autoforwardS.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0
+ || autoforwardS.compare(QLatin1String("yes"), Qt::CaseInsensitive) == 0
+ || autoforwardS.compare(QLatin1String("t"), Qt::CaseInsensitive) == 0
+ || autoforwardS.compare(QLatin1String("y"), Qt::CaseInsensitive) == 0
|| autoforwardS == QLatin1String("1"))
invoke->autoforward = true;
else
invoke->autoforward = false;
- invoke->namelist = attributes.value(QLatin1String("namelist")).toString().split(QLatin1Char(' '), QString::SkipEmptyParts);
+ invoke->namelist = attributes.value(QLatin1String("namelist")).toString().split(QLatin1Char(' '), Qt::SkipEmptyParts);
current().instruction = invoke;
return true;
}
@@ -2060,6 +2048,9 @@ bool QScxmlCompilerPrivate::postReadElementScript()
scriptI->content = QString::fromUtf8(data);
}
}
+ } else {
+ addError(scriptI->xmlLocation,
+ QStringLiteral("neither src nor any content has been given in the script tag"));
}
return flushInstruction();
}
@@ -2113,7 +2104,7 @@ bool QScxmlCompilerPrivate::readDocument()
for (bool finished = false; !finished && !m_reader->hasError();) {
switch (m_reader->readNext()) {
case QXmlStreamReader::StartElement : {
- const QStringRef newTag = m_reader->name();
+ const QStringView newTag = m_reader->name();
const ParserState::Kind newElementKind = ParserState::nameToParserStateKind(newTag);
auto ns = m_reader->namespaceUri();
@@ -2154,7 +2145,7 @@ bool QScxmlCompilerPrivate::readDocument()
bool QScxmlCompilerPrivate::readElement()
{
- const QStringRef currentTag = m_reader->name();
+ const QStringView currentTag = m_reader->name();
const QXmlStreamAttributes attributes = m_reader->attributes();
const ParserState::Kind elementKind = ParserState::nameToParserStateKind(currentTag);
@@ -2177,7 +2168,7 @@ bool QScxmlCompilerPrivate::readElement()
return parseSubElement(i, m_reader, m_fileName);
}
- if (elementKind != ParserState::Scxml && !m_stack.count()) {
+ if (elementKind != ParserState::Scxml && !m_stack.size()) {
addError(QStringLiteral("misplaced %1").arg(currentTag.toString()));
return false;
}
@@ -2219,7 +2210,7 @@ bool QScxmlCompilerPrivate::readElement()
for (bool finished = false; !finished && !m_reader->hasError();) {
switch (m_reader->readNext()) {
case QXmlStreamReader::StartElement : {
- const QStringRef newTag = m_reader->name();
+ const QStringView newTag = m_reader->name();
const ParserState::Kind newElementKind = ParserState::nameToParserStateKind(newTag);
auto ns = m_reader->namespaceUri();
@@ -2327,7 +2318,7 @@ QByteArray QScxmlCompilerPrivate::load(const QString &name, bool *ok)
return result;
}
-QVector<QScxmlError> QScxmlCompilerPrivate::errors() const
+QList<QScxmlError> QScxmlCompilerPrivate::errors() const
{
return m_errors;
}
@@ -2394,12 +2385,12 @@ QScxmlCompilerPrivate::ParserState &QScxmlCompilerPrivate::current()
QScxmlCompilerPrivate::ParserState &QScxmlCompilerPrivate::previous()
{
- return m_stack[m_stack.count() - 2];
+ return m_stack[m_stack.size() - 2];
}
bool QScxmlCompilerPrivate::hasPrevious() const
{
- return m_stack.count() > 1;
+ return m_stack.size() > 1;
}
bool QScxmlCompilerPrivate::checkAttributes(const QXmlStreamAttributes &attributes,
@@ -2416,7 +2407,7 @@ bool QScxmlCompilerPrivate::checkAttributes(const QXmlStreamAttributes &attribut
{
QStringList required = requiredNames;
for (const QXmlStreamAttribute &attribute : attributes) {
- const QStringRef ns = attribute.namespaceUri();
+ const QStringView ns = attribute.namespaceUri();
if (!ns.isEmpty() && ns != scxmlNamespace && ns != qtScxmlNamespace)
continue;
@@ -2451,7 +2442,7 @@ QByteArray QScxmlCompilerPrivate::DefaultLoader::load(const QString &name, const
const QUrl url(name);
if (!url.isLocalFile() && !url.isRelative())
errs << QStringLiteral("src attribute is not a local file (%1)").arg(name);
- QFileInfo fInfo = url.isLocalFile() ? url.toLocalFile() : name;
+ QFileInfo fInfo(url.isLocalFile() ? url.toLocalFile() : name);
#endif // BUILD_QSCXMLC
if (fInfo.isRelative())
fInfo = QFileInfo(QDir(baseDir).filePath(fInfo.filePath()));
diff --git a/src/scxml/qscxmlcompiler.h b/src/scxml/qscxmlcompiler.h
index 411dfde..3eecca5 100644
--- a/src/scxml/qscxmlcompiler.h
+++ b/src/scxml/qscxmlcompiler.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLCOMPILER_H
#define QSCXMLCOMPILER_H
@@ -72,7 +36,7 @@ public:
void setLoader(Loader *newLoader);
QScxmlStateMachine *compile();
- QVector<QScxmlError> errors() const;
+ QList<QScxmlError> errors() const;
private:
friend class QScxmlCompilerPrivate;
diff --git a/src/scxml/qscxmlcompiler_p.h b/src/scxml/qscxmlcompiler_p.h
index 4b270f8..498018c 100644
--- a/src/scxml/qscxmlcompiler_p.h
+++ b/src/scxml/qscxmlcompiler_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2021 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 QSCXMLCOMPILER_P_H
#define QSCXMLCOMPILER_P_H
@@ -60,6 +24,9 @@
#include <QtCore/qstringlist.h>
#include <QtCore/qstring.h>
#include <QtCore/qxmlstream.h>
+#include <QtCore/private/qglobal_p.h>
+
+#include <memory>
QT_BEGIN_NAMESPACE
@@ -129,7 +96,7 @@ struct DoneData: public Node
{
QString contents;
QString expr;
- QVector<Param *> params;
+ QList<Param *> params;
DoneData(const XmlLocation &xmlLocation): Node(xmlLocation) {}
void accept(NodeVisitor *visitor) override;
@@ -141,8 +108,8 @@ struct Instruction: public Node
virtual ~Instruction() {}
};
-typedef QVector<Instruction *> InstructionSequence;
-typedef QVector<InstructionSequence *> InstructionSequences;
+typedef QList<Instruction *> InstructionSequence;
+typedef QList<InstructionSequence *> InstructionSequences;
struct Send: public Instruction
{
@@ -157,7 +124,7 @@ struct Send: public Instruction
QString delay;
QString delayexpr;
QStringList namelist;
- QVector<Param *> params;
+ QList<Param *> params;
QString content;
QString contentexpr;
@@ -177,7 +144,7 @@ struct Invoke: public Instruction
QString idLocation;
QStringList namelist;
bool autoforward;
- QVector<Param *> params;
+ QList<Param *> params;
InstructionSequence finalize;
QSharedPointer<ScxmlDocument> content;
@@ -285,12 +252,12 @@ struct State: public AbstractState, public StateOrTransition
enum Type { Normal, Parallel, Final };
QStringList initial;
- QVector<DataElement *> dataElements;
- QVector<StateOrTransition *> children;
+ QList<DataElement *> dataElements;
+ QList<StateOrTransition *> children;
InstructionSequences onEntry;
InstructionSequences onExit;
DoneData *doneData;
- QVector<Invoke *> invokes;
+ QList<Invoke *> invokes;
Type type;
Transition *initialTransition; // when not set, it is filled during verification
@@ -322,7 +289,7 @@ struct Transition: public StateOrTransition
InstructionSequence instructionsOnTransition;
Type type;
- QVector<AbstractState *> targetStates; // when not set, it is filled during verification
+ QList<AbstractState *> targetStates; // when not set, it is filled during verification
Transition(const XmlLocation &xmlLocation)
: StateOrTransition(xmlLocation)
@@ -338,7 +305,7 @@ struct HistoryState: public AbstractState, public StateOrTransition
{
enum Type { Deep, Shallow };
Type type;
- QVector<StateOrTransition *> children;
+ QList<StateOrTransition *> children;
HistoryState(const XmlLocation &xmlLocation)
: StateOrTransition(xmlLocation)
@@ -376,8 +343,8 @@ struct Scxml: public StateContainer, public Node
QString cppDataModelClassName;
QString cppDataModelHeaderName;
BindingMethod binding;
- QVector<StateOrTransition *> children;
- QVector<DataElement *> dataElements;
+ QList<StateOrTransition *> children;
+ QList<DataElement *> dataElements;
QScopedPointer<Script> script;
InstructionSequence initialSetup;
@@ -405,11 +372,11 @@ struct ScxmlDocument
{
const QString fileName;
Scxml *root;
- QVector<AbstractState *> allStates;
- QVector<Transition *> allTransitions;
- QVector<Node *> allNodes;
- QVector<InstructionSequence *> allSequences;
- QVector<ScxmlDocument *> allSubDocuments; // weak pointers
+ QList<AbstractState *> allStates;
+ QList<Transition *> allTransitions;
+ QList<Node *> allNodes;
+ QList<InstructionSequence *> allSequences;
+ QList<ScxmlDocument *> allSubDocuments; // weak pointers
bool isVerified;
ScxmlDocument(const QString &fileName)
@@ -508,13 +475,13 @@ public:
void visit(InstructionSequence *sequence)
{
Q_ASSERT(sequence);
- for (Instruction *instruction : qAsConst(*sequence)) {
+ for (Instruction *instruction : std::as_const(*sequence)) {
Q_ASSERT(instruction);
instruction->accept(this);
}
}
- void visit(const QVector<DataElement *> &dataElements)
+ void visit(const QList<DataElement *> &dataElements)
{
for (DataElement *dataElement : dataElements) {
Q_ASSERT(dataElement);
@@ -522,7 +489,7 @@ public:
}
}
- void visit(const QVector<StateOrTransition *> &children)
+ void visit(const QList<StateOrTransition *> &children)
{
for (StateOrTransition *child : children) {
Q_ASSERT(child);
@@ -538,7 +505,7 @@ public:
}
}
- void visit(const QVector<Param *> &params)
+ void visit(const QList<Param *> &params)
{
for (Param *param : params) {
Q_ASSERT(param);
@@ -574,7 +541,7 @@ public:
const QString &fileName);
QByteArray load(const QString &name, bool *ok);
- QVector<QScxmlError> errors() const;
+ QList<QScxmlError> errors() const;
void addError(const QString &msg);
void addError(const DocumentModel::XmlLocation &location, const QString &msg);
@@ -694,7 +661,7 @@ private:
bool validChild(ParserState::Kind child) const;
static bool validChild(ParserState::Kind parent, ParserState::Kind child);
static bool isExecutableContent(ParserState::Kind kind);
- static Kind nameToParserStateKind(const QStringRef &name);
+ static Kind nameToParserStateKind(QStringView name);
static QStringList requiredAttributes(Kind kind);
static QStringList optionalAttributes(Kind kind);
};
@@ -719,14 +686,14 @@ private:
QString m_fileName;
QSet<QString> m_allIds;
- QScopedPointer<DocumentModel::ScxmlDocument> m_doc;
+ std::unique_ptr<DocumentModel::ScxmlDocument> m_doc;
DocumentModel::StateContainer *m_currentState;
DefaultLoader m_defaultLoader;
QScxmlCompiler::Loader *m_loader;
QXmlStreamReader *m_reader;
- QVector<ParserState> m_stack;
- QVector<QScxmlError> m_errors;
+ QList<ParserState> m_stack;
+ QList<QScxmlError> m_errors;
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlcppdatamodel.cpp b/src/scxml/qscxmlcppdatamodel.cpp
index ba18cc7..f357eae 100644
--- a/src/scxml/qscxmlcppdatamodel.cpp
+++ b/src/scxml/qscxmlcppdatamodel.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmlcppdatamodel_p.h"
#include "qscxmlstatemachine.h"
@@ -64,12 +28,12 @@ using namespace QScxmlExecutableContent;
The format of the \e datamodel attribute is: \c{cplusplus:<class-name>:<classdef-header>}.
So, for the example above, there should be a file \e thedatamodel.h containing a subclass of
QScxmlCppDataModel, containing at least the following:
- \code
+ \badcode
#include "qscxmlcppdatamodel.h"
class TheDataModel: public QScxmlCppDataModel
{
- Q_OBJECT
+ \Q_OBJECT
Q_SCXML_DATAMODEL
};
\endcode
@@ -123,7 +87,7 @@ void TheDataModel::evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool
statements, and in \e cond or \e expr attributes you can use any C++ expression that can be
converted to the respective bool or QVariant. And, as the \c this pointer is also captured, you
can call or access the data model (the \e media attribute in the example above). For the full
- example, see \l {Qt SCXML: Media Player QML Example (C++ Data Model)}.
+ example, see \l {SCXML Media Player}.
*/
/*!
@@ -138,6 +102,8 @@ QScxmlCppDataModel::QScxmlCppDataModel(QObject *parent)
* for data model variables specified by their keys, \a initialDataValues. These
* are the values specified by \c <param> tags in an \c <invoke> element.
*
+ * Returns \c true on success.
+ *
* \sa QScxmlStateMachine::init
*/
bool QScxmlCppDataModel::setup(const QVariantMap &initialDataValues)
diff --git a/src/scxml/qscxmlcppdatamodel.h b/src/scxml/qscxmlcppdatamodel.h
index 8f71229..0fd4ba4 100644
--- a/src/scxml/qscxmlcppdatamodel.h
+++ b/src/scxml/qscxmlcppdatamodel.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLCPPDATAMODEL_H
#define QSCXMLCPPDATAMODEL_H
diff --git a/src/scxml/qscxmlcppdatamodel_p.h b/src/scxml/qscxmlcppdatamodel_p.h
index b342e39..2168d02 100644
--- a/src/scxml/qscxmlcppdatamodel_p.h
+++ b/src/scxml/qscxmlcppdatamodel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLCPPDATAMODEL_P_H
#define QSCXMLCPPDATAMODEL_P_H
diff --git a/src/scxml/qscxmldatamodel.cpp b/src/scxml/qscxmldatamodel.cpp
index ab0ae7c..eaa15e8 100644
--- a/src/scxml/qscxmldatamodel.cpp
+++ b/src/scxml/qscxmldatamodel.cpp
@@ -1,51 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmldatamodel_p.h"
#include "qscxmlnulldatamodel.h"
-#if QT_CONFIG(scxml_ecmascriptdatamodel)
-#include "qscxmlecmascriptdatamodel.h"
-#endif
#include "qscxmlstatemachine_p.h"
+#include <QtCore/private/qfactoryloader_p.h>
+#include "qscxmldatamodelplugin_p.h"
+
QT_BEGIN_NAMESPACE
+Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
+ ("org.qt-project.qt.scxml.datamodel.plugin",
+ QStringLiteral("/scxmldatamodel")))
+
/*!
\class QScxmlDataModel::ForeachLoopBody
\brief The ForeachLoopBody class represents a function to be executed on
@@ -85,7 +53,7 @@ QScxmlDataModel::ForeachLoopBody::~ForeachLoopBody()
*
* One data model can only belong to one state machine.
*
- * \sa QScxmlStateMachine QScxmlCppDataModel QScxmlEcmaScriptDataModel QScxmlNullDataModel
+ * \sa QScxmlStateMachine QScxmlCppDataModel QScxmlNullDataModel
*/
/*!
@@ -127,11 +95,13 @@ void QScxmlDataModel::setStateMachine(QScxmlStateMachine *stateMachine)
{
Q_D(QScxmlDataModel);
- if (d->m_stateMachine == nullptr && stateMachine != nullptr) {
- d->m_stateMachine = stateMachine;
- if (stateMachine)
- stateMachine->setDataModel(this);
- emit stateMachineChanged(stateMachine);
+ if (d->m_stateMachine.valueBypassingBindings() == nullptr && stateMachine != nullptr) {
+ // the binding is removed only on the first valid set
+ // as the later attempts are ignored
+ d->m_stateMachine.removeBindingUnlessInWrapper();
+ d->m_stateMachine.setValueBypassingBindings(stateMachine);
+ stateMachine->setDataModel(this);
+ d->m_stateMachine.notify();
}
}
@@ -144,6 +114,37 @@ QScxmlStateMachine *QScxmlDataModel::stateMachine() const
return d->m_stateMachine;
}
+QBindable<QScxmlStateMachine*> QScxmlDataModel::bindableStateMachine()
+{
+ Q_D(QScxmlDataModel);
+ return &d->m_stateMachine;
+}
+
+/*!
+ * Creates a data model from a plugin specified by a \a pluginKey.
+ */
+QScxmlDataModel *QScxmlDataModel::createScxmlDataModel(const QString& pluginKey)
+{
+ QScxmlDataModel *model = nullptr;
+
+ int pluginIndex = loader()->indexOf(pluginKey);
+
+ if (QObject *object = loader()->instance(pluginIndex)) {
+ if (auto *plugin = qobject_cast<QScxmlDataModelPlugin *>(object)) {
+ model = plugin->createScxmlDataModel();
+ if (!model)
+ qWarning() << pluginKey << " data model was not instantiated, createScxmlDataModel() returned null.";
+
+ } else {
+ qWarning() << "plugin object for" << pluginKey << "is not a QScxmlDatModelPlugin.";
+ }
+ delete object;
+ } else {
+ qWarning() << pluginKey << " plugin not found." ;
+ }
+ return model;
+}
+
QScxmlDataModel *QScxmlDataModelPrivate::instantiateDataModel(DocumentModel::Scxml::DataModelType type)
{
QScxmlDataModel *dataModel = nullptr;
@@ -152,16 +153,13 @@ QScxmlDataModel *QScxmlDataModelPrivate::instantiateDataModel(DocumentModel::Scx
dataModel = new QScxmlNullDataModel;
break;
case DocumentModel::Scxml::JSDataModel:
-#if QT_CONFIG(scxml_ecmascriptdatamodel)
- dataModel = new QScxmlEcmaScriptDataModel;
-#endif
+ dataModel = QScxmlDataModel::createScxmlDataModel(QStringLiteral("ecmascriptdatamodel"));
break;
case DocumentModel::Scxml::CppDataModel:
break;
default:
Q_UNREACHABLE();
}
-
return dataModel;
}
diff --git a/src/scxml/qscxmldatamodel.h b/src/scxml/qscxmldatamodel.h
index 0c9e666..5c1811d 100644
--- a/src/scxml/qscxmldatamodel.h
+++ b/src/scxml/qscxmldatamodel.h
@@ -1,63 +1,29 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLDATAMODEL_H
#define QSCXMLDATAMODEL_H
#include <QtScxml/qscxmlexecutablecontent.h>
+#include <QtCore/qobject.h>
#include <QtCore/qvariant.h>
-#include <QtCore/qvector.h>
+
+Q_MOC_INCLUDE(qscxmlstatemachine.h)
QT_BEGIN_NAMESPACE
class QScxmlEvent;
class QScxmlStateMachine;
-class QScxmlTableData;
class QScxmlDataModelPrivate;
class Q_SCXML_EXPORT QScxmlDataModel : public QObject
{
Q_OBJECT
Q_DECLARE_PRIVATE(QScxmlDataModel)
- Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine WRITE setStateMachine NOTIFY stateMachineChanged)
+ Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine WRITE setStateMachine
+ NOTIFY stateMachineChanged BINDABLE bindableStateMachine)
public:
class Q_SCXML_EXPORT ForeachLoopBody
@@ -72,8 +38,11 @@ public:
public:
explicit QScxmlDataModel(QObject *parent = nullptr);
+ static QScxmlDataModel *createScxmlDataModel(const QString& pluginKey);
+
void setStateMachine(QScxmlStateMachine *stateMachine);
QScxmlStateMachine *stateMachine() const;
+ QBindable<QScxmlStateMachine*> bindableStateMachine();
Q_INVOKABLE virtual bool setup(const QVariantMap &initialDataValues) = 0;
diff --git a/src/scxml/qscxmldatamodel_p.h b/src/scxml/qscxmldatamodel_p.h
index b91c63c..2432959 100644
--- a/src/scxml/qscxmldatamodel_p.h
+++ b/src/scxml/qscxmldatamodel_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLDATAMODEL_P_H
#define QSCXMLDATAMODEL_P_H
@@ -54,18 +18,31 @@
#include "qscxmldatamodel.h"
#include "qscxmlcompiler_p.h"
#include <private/qobject_p.h>
+#include <private/qproperty_p.h>
QT_BEGIN_NAMESPACE
class QScxmlDataModelPrivate : public QObjectPrivate
{
+ Q_DECLARE_PUBLIC(QScxmlDataModel)
public:
- QScxmlDataModelPrivate() : m_stateMachine(nullptr) {}
+ QScxmlDataModelPrivate() = default;
static QScxmlDataModel *instantiateDataModel(DocumentModel::Scxml::DataModelType type);
-public:
- QScxmlStateMachine *m_stateMachine;
+ void setStateMachine(QScxmlStateMachine* stateMachine)
+ {
+ q_func()->setStateMachine(stateMachine);
+ }
+
+ void emitStateMachineChanged(QScxmlStateMachine* newValue)
+ {
+ emit q_func()->stateMachineChanged(newValue);
+ }
+
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QScxmlDataModelPrivate, QScxmlStateMachine*, m_stateMachine,
+ &QScxmlDataModelPrivate::setStateMachine,
+ &QScxmlDataModelPrivate::emitStateMachineChanged, nullptr)
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmldatamodelplugin.cpp b/src/scxml/qscxmldatamodelplugin.cpp
new file mode 100644
index 0000000..d60db70
--- /dev/null
+++ b/src/scxml/qscxmldatamodelplugin.cpp
@@ -0,0 +1,13 @@
+// Copyright (C) 2021 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 "qscxmldatamodelplugin_p.h"
+
+QT_BEGIN_NAMESPACE
+
+QScxmlDataModel *QScxmlDataModelPlugin::createScxmlDataModel() const
+{
+ return nullptr;
+}
+
+QT_END_NAMESPACE
diff --git a/src/scxml/qscxmldatamodelplugin_p.h b/src/scxml/qscxmldatamodelplugin_p.h
new file mode 100644
index 0000000..be00ea9
--- /dev/null
+++ b/src/scxml/qscxmldatamodelplugin_p.h
@@ -0,0 +1,36 @@
+// Copyright (C) 2021 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 QSCXMLDATAMODELPLUGIN_P_H
+#define QSCXMLDATAMODELPLUGIN_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 <QtScxml/private/qscxmlglobals_p.h>
+#include <QtScxml/qscxmldatamodel.h>
+#include <QtCore/qobject.h>
+
+QT_BEGIN_NAMESPACE
+
+#define QScxmlDataModelPluginInterface_iid "org.qt-project.qt.scxml.datamodel.plugin"
+
+class Q_SCXML_EXPORT QScxmlDataModelPlugin : public QObject
+{
+ Q_OBJECT
+public:
+ virtual QScxmlDataModel *createScxmlDataModel() const;
+};
+
+Q_DECLARE_INTERFACE(QScxmlDataModelPlugin, QScxmlDataModelPluginInterface_iid)
+QT_END_NAMESPACE
+
+#endif // QSCXMLDATAMODELPLUGIN_P_H
diff --git a/src/scxml/qscxmlecmascriptdatamodel.h b/src/scxml/qscxmlecmascriptdatamodel.h
deleted file mode 100644
index 16f29d4..0000000
--- a/src/scxml/qscxmlecmascriptdatamodel.h
+++ /dev/null
@@ -1,80 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSCXMLECMASCRIPTDATAMODEL_H
-#define QSCXMLECMASCRIPTDATAMODEL_H
-
-#include <QtScxml/qscxmlglobals.h>
-#include <QtScxml/qscxmldatamodel.h>
-
-QT_BEGIN_NAMESPACE
-
-// We cannot use QT_REQUIRE_CONFIG here, because the feature name contains a dash.
-#if QT_CONFIG(scxml_ecmascriptdatamodel)
-
-class QScxmlEcmaScriptDataModelPrivate;
-class Q_SCXML_EXPORT QScxmlEcmaScriptDataModel: public QScxmlDataModel
-{
- Q_OBJECT
- Q_DECLARE_PRIVATE(QScxmlEcmaScriptDataModel)
-public:
- explicit QScxmlEcmaScriptDataModel(QObject *parent = nullptr);
-
- Q_INVOKABLE bool setup(const QVariantMap &initialDataValues) override;
-
- QString evaluateToString(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
- bool evaluateToBool(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
- QVariant evaluateToVariant(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
- void evaluateToVoid(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
- void evaluateAssignment(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
- void evaluateInitialization(QScxmlExecutableContent::EvaluatorId id, bool *ok) override final;
- void evaluateForeach(QScxmlExecutableContent::EvaluatorId id, bool *ok, ForeachLoopBody *body) override final;
-
- void setScxmlEvent(const QScxmlEvent &event) override;
-
- QVariant scxmlProperty(const QString &name) const override;
- bool hasScxmlProperty(const QString &name) const override;
- bool setScxmlProperty(const QString &name, const QVariant &value, const QString &context) override;
-};
-
-#endif // QT_CONFIG(scxml_ecmascriptdatamodel)
-
-QT_END_NAMESPACE
-
-#endif // QSCXMLECMASCRIPTDATAMODEL_H
diff --git a/src/scxml/qscxmlecmascriptplatformproperties.cpp b/src/scxml/qscxmlecmascriptplatformproperties.cpp
deleted file mode 100644
index 95e3b4f..0000000
--- a/src/scxml/qscxmlecmascriptplatformproperties.cpp
+++ /dev/null
@@ -1,101 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#include "qscxmlecmascriptplatformproperties_p.h"
-#include "qscxmlstatemachine.h"
-
-#include <qjsengine.h>
-
-QT_BEGIN_NAMESPACE
-class QScxmlPlatformProperties::Data
-{
-public:
- Data()
- : m_stateMachine(nullptr)
- {}
-
- QScxmlStateMachine *m_stateMachine;
- QJSValue m_jsValue;
-};
-
-QScxmlPlatformProperties::QScxmlPlatformProperties(QObject *parent)
- : QObject(parent)
- , data(new Data)
-{}
-
-QScxmlPlatformProperties *QScxmlPlatformProperties::create(QJSEngine *engine, QScxmlStateMachine *stateMachine)
-{
- QScxmlPlatformProperties *pp = new QScxmlPlatformProperties(engine);
- pp->data->m_stateMachine = stateMachine;
- pp->data->m_jsValue = engine->newQObject(pp);
- return pp;
-}
-
-QScxmlPlatformProperties::~QScxmlPlatformProperties()
-{
- delete data;
-}
-
-QJSEngine *QScxmlPlatformProperties::engine() const
-{
- return qobject_cast<QJSEngine *>(parent());
-}
-
-QScxmlStateMachine *QScxmlPlatformProperties::stateMachine() const
-{
- return data->m_stateMachine;
-}
-
-QJSValue QScxmlPlatformProperties::jsValue() const
-{
- return data->m_jsValue;
-}
-
-/// _x.marks === "the spot"
-QString QScxmlPlatformProperties::marks() const
-{
- return QStringLiteral("the spot");
-}
-
-bool QScxmlPlatformProperties::inState(const QString &stateName)
-{
- return stateMachine()->isActive(stateName);
-}
-
-QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlecmascriptplatformproperties_p.h b/src/scxml/qscxmlecmascriptplatformproperties_p.h
deleted file mode 100644
index e7b8a09..0000000
--- a/src/scxml/qscxmlecmascriptplatformproperties_p.h
+++ /dev/null
@@ -1,93 +0,0 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
-
-#ifndef QSCXMLECMASCRIPTPLATFORMPROPERTIES_P_H
-#define QSCXMLECMASCRIPTPLATFORMPROPERTIES_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 "qscxmlglobals.h"
-
-#include <QtCore/qobject.h>
-
-QT_FORWARD_DECLARE_CLASS(QJSEngine)
-QT_FORWARD_DECLARE_CLASS(QJSValue)
-
-QT_REQUIRE_CONFIG(scxml_ecmascriptdatamodel);
-
-QT_BEGIN_NAMESPACE
-
-class QScxmlStateMachine;
-class QScxmlPlatformProperties: public QObject
-{
- Q_OBJECT
-
- QScxmlPlatformProperties(QObject *parent);
-
- Q_PROPERTY(QString marks READ marks CONSTANT)
-
-public:
- static QScxmlPlatformProperties *create(QJSEngine *engine, QScxmlStateMachine *stateMachine);
- ~QScxmlPlatformProperties();
-
- QJSEngine *engine() const;
- QScxmlStateMachine *stateMachine() const;
- QJSValue jsValue() const;
-
- QString marks() const;
-
- Q_INVOKABLE bool inState(const QString &stateName);
-
-private:
- class Data;
- Data *data;
-};
-
-QT_END_NAMESPACE
-
-#endif // QSCXMLECMASCRIPTPLATFORMPROPERTIES_P_H
diff --git a/src/scxml/qscxmlerror.cpp b/src/scxml/qscxmlerror.cpp
index 3033040..b5b541b 100644
--- a/src/scxml/qscxmlerror.cpp
+++ b/src/scxml/qscxmlerror.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmlerror.h"
diff --git a/src/scxml/qscxmlerror.h b/src/scxml/qscxmlerror.h
index 6e6e459..8b1201b 100644
--- a/src/scxml/qscxmlerror.h
+++ b/src/scxml/qscxmlerror.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLERROR_H
#define QSCXMLERROR_H
diff --git a/src/scxml/qscxmlevent.cpp b/src/scxml/qscxmlevent.cpp
index 2ec7566..3d4795f 100644
--- a/src/scxml/qscxmlevent.cpp
+++ b/src/scxml/qscxmlevent.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmlexecutablecontent_p.h"
#include "qscxmlevent_p.h"
@@ -71,7 +35,7 @@ QScxmlEvent *QScxmlEventBuilder::buildEvent()
}
if (!ok) {
// expr evaluation failure results in the data property of the event being set to null. See e.g. test528.
- data = QVariant(QMetaType::VoidStar, 0);
+ data = QVariant(QMetaType(QMetaType::VoidStar), nullptr);
}
} else {
QVariantMap keyValues;
@@ -86,7 +50,7 @@ QScxmlEvent *QScxmlEventBuilder::buildEvent()
} else {
// If the evaluation of the <param> tags fails, set _event.data to an empty string.
// See test343.
- data = QVariant(QMetaType::VoidStar, 0);
+ data = QVariant(QMetaType(QMetaType::VoidStar), nullptr);
}
}
diff --git a/src/scxml/qscxmlevent.h b/src/scxml/qscxmlevent.h
index e07da7a..566f0c3 100644
--- a/src/scxml/qscxmlevent.h
+++ b/src/scxml/qscxmlevent.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLEVENT_H
#define QSCXMLEVENT_H
diff --git a/src/scxml/qscxmlevent_p.h b/src/scxml/qscxmlevent_p.h
index cf4e26a..b177b30 100644
--- a/src/scxml/qscxmlevent_p.h
+++ b/src/scxml/qscxmlevent_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLEVENT_P_H
#define QSCXMLEVENT_P_H
diff --git a/src/scxml/qscxmlexecutablecontent.cpp b/src/scxml/qscxmlexecutablecontent.cpp
index 45079d7..eae7c4a 100644
--- a/src/scxml/qscxmlexecutablecontent.cpp
+++ b/src/scxml/qscxmlexecutablecontent.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2018 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2018 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 "qscxmlglobals_p.h"
#include "qscxmlexecutablecontent_p.h"
@@ -234,7 +198,7 @@ using namespace QScxmlExecutableContent;
#ifndef BUILD_QSCXMLC
-static int parseTime(const QString &t, bool *ok = nullptr)
+static int parseTime(QStringView t, bool *ok = nullptr)
{
if (t.isEmpty()) {
if (ok)
@@ -250,7 +214,7 @@ static int parseTime(const QString &t, bool *ok = nullptr)
++startPos;
}
int pos = startPos;
- for (int endPos = t.length(); pos < endPos; ++pos) {
+ for (int endPos = t.size(); pos < endPos; ++pos) {
auto c = t[pos];
if (c < QLatin1Char('0') || c > QLatin1Char('9'))
break;
@@ -259,11 +223,11 @@ static int parseTime(const QString &t, bool *ok = nullptr)
if (ok) *ok = false;
return -1;
}
- int value = t.midRef(startPos, pos - startPos).toInt(ok);
+ int value = t.mid(startPos, pos - startPos).toInt(ok);
if (ok && !*ok) return -1;
- if (t.length() == pos + 1 && t[pos] == QLatin1Char('s')) {
+ if (t.size() == pos + 1 && t[pos] == QLatin1Char('s')) {
value *= 1000;
- } else if (t.length() != pos + 2 || t[pos] != QLatin1Char('m') || t[pos + 1] != QLatin1Char('s')) {
+ } else if (t.size() != pos + 2 || t[pos] != QLatin1Char('m') || t[pos + 1] != QLatin1Char('s')) {
if (ok) *ok = false;
return -1;
}
diff --git a/src/scxml/qscxmlexecutablecontent.h b/src/scxml/qscxmlexecutablecontent.h
index 8d2ef13..9d456c2 100644
--- a/src/scxml/qscxmlexecutablecontent.h
+++ b/src/scxml/qscxmlexecutablecontent.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLEXECUTABLECONTENT_H
#define QSCXMLEXECUTABLECONTENT_H
diff --git a/src/scxml/qscxmlexecutablecontent_p.h b/src/scxml/qscxmlexecutablecontent_p.h
index 5980330..90f779f 100644
--- a/src/scxml/qscxmlexecutablecontent_p.h
+++ b/src/scxml/qscxmlexecutablecontent_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLEXECUTABLECONTENT_P_H
#define QSCXMLEXECUTABLECONTENT_P_H
@@ -404,11 +368,17 @@ struct StateTable {
return *(start + idx + 1);
}
- struct const_iterator: public std::iterator<std::forward_iterator_tag, int, ptrdiff_t,
- const int *, const int &>
+ struct const_iterator
{
const_iterator(const Array &a, int pos): a(a), pos(pos) {}
+ // std::iterator_traits
+ using value_type = int;
+ using pointer = const int*;
+ using reference = const int&;
+ using difference_type = std::ptrdiff_t;
+ using iterator_category = std::forward_iterator_tag;
+
const_iterator &operator++() {
if (pos < a.size()) ++pos;
return *this;
diff --git a/src/scxml/qscxmlglobals.h b/src/scxml/qscxmlglobals.h
index da45a2b..64169d2 100644
--- a/src/scxml/qscxmlglobals.h
+++ b/src/scxml/qscxmlglobals.h
@@ -1,59 +1,19 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLGLOBALS_H
#define QSCXMLGLOBALS_H
#include <QtCore/qglobal.h>
#include <QtScxml/qtscxml-config.h>
-QT_BEGIN_NAMESPACE
-
-#if defined(QT_STATIC) || defined(BUILD_QSCXMLC)
+#if defined(BUILD_QSCXMLC)
# define Q_SCXML_EXPORT
#else
-# ifdef QT_BUILD_SCXML_LIB
-# define Q_SCXML_EXPORT Q_DECL_EXPORT
-# else
-# define Q_SCXML_EXPORT Q_DECL_IMPORT
-# endif
+# include <QtScxml/qtscxmlexports.h>
#endif
+// Silence syncqt
+QT_BEGIN_NAMESPACE
QT_END_NAMESPACE
#endif // QSCXMLGLOBALS_H
diff --git a/src/scxml/qscxmlglobals_p.h b/src/scxml/qscxmlglobals_p.h
index fac74dc..ba82405 100644
--- a/src/scxml/qscxmlglobals_p.h
+++ b/src/scxml/qscxmlglobals_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLGLOBALS_P_H
#define QSCXMLGLOBALS_P_H
@@ -52,7 +16,6 @@
//
#include "qscxmlglobals.h"
-
#include <QtCore/qloggingcategory.h>
QT_BEGIN_NAMESPACE
diff --git a/src/scxml/qscxmlinvokableservice.cpp b/src/scxml/qscxmlinvokableservice.cpp
index 912be00..0665406 100644
--- a/src/scxml/qscxmlinvokableservice.cpp
+++ b/src/scxml/qscxmlinvokableservice.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmlglobals_p.h"
#include "qscxmlinvokableservice_p.h"
@@ -158,8 +122,8 @@ QScxmlInvokableServicePrivate::QScxmlInvokableServicePrivate(QScxmlStateMachine
QScxmlInvokableServiceFactoryPrivate::QScxmlInvokableServiceFactoryPrivate(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
+ const QList<QScxmlExecutableContent::StringId> &namelist,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters)
: invokeInfo(invokeInfo)
, names(namelist)
, parameters(parameters)
@@ -168,8 +132,8 @@ QScxmlInvokableServiceFactoryPrivate::QScxmlInvokableServiceFactoryPrivate(
QScxmlStaticScxmlServiceFactoryPrivate::QScxmlStaticScxmlServiceFactoryPrivate(
const QMetaObject *metaObject,
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &names,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
+ const QList<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters)
: QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters), metaObject(metaObject)
{
}
@@ -188,8 +152,8 @@ QScxmlStateMachine *QScxmlInvokableService::parentStateMachine() const
QScxmlInvokableServiceFactory::QScxmlInvokableServiceFactory(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &names,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent)
: QObject(*(new QScxmlInvokableServiceFactoryPrivate(invokeInfo, names, parameters)), parent)
{}
@@ -200,14 +164,14 @@ const QScxmlExecutableContent::InvokeInfo &QScxmlInvokableServiceFactory::invoke
return d->invokeInfo;
}
-const QVector<QScxmlExecutableContent::ParameterInfo> &
+const QList<QScxmlExecutableContent::ParameterInfo> &
QScxmlInvokableServiceFactory::parameters() const
{
Q_D(const QScxmlInvokableServiceFactory);
return d->parameters;
}
-const QVector<QScxmlExecutableContent::StringId> &QScxmlInvokableServiceFactory::names() const
+const QList<QScxmlExecutableContent::StringId> &QScxmlInvokableServiceFactory::names() const
{
Q_D(const QScxmlInvokableServiceFactory);
return d->names;
@@ -259,8 +223,8 @@ QString QScxmlInvokableServicePrivate::calculateId(
QVariantMap QScxmlInvokableServicePrivate::calculateData(
QScxmlStateMachine *parent,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
- const QVector<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &names,
bool *ok) const
{
Q_ASSERT(ok);
@@ -404,8 +368,8 @@ QScxmlStateMachine *QScxmlScxmlService::stateMachine() const
*/
QScxmlDynamicScxmlServiceFactory::QScxmlDynamicScxmlServiceFactory(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &names,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent)
: QScxmlInvokableServiceFactory(invokeInfo, names, parameters, parent)
{}
@@ -427,8 +391,8 @@ QScxmlInvokableService *QScxmlDynamicScxmlServiceFactory::invoke(
QScxmlStaticScxmlServiceFactory::QScxmlStaticScxmlServiceFactory(
const QMetaObject *metaObject,
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &nameList,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &nameList,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent)
: QScxmlInvokableServiceFactory(*(new QScxmlStaticScxmlServiceFactoryPrivate(
metaObject, invokeInfo, nameList, parameters)), parent)
diff --git a/src/scxml/qscxmlinvokableservice.h b/src/scxml/qscxmlinvokableservice.h
index 65ce4fc..fdf7dad 100644
--- a/src/scxml/qscxmlinvokableservice.h
+++ b/src/scxml/qscxmlinvokableservice.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLINVOKABLESERVICE_H
#define QSCXMLINVOKABLESERVICE_H
@@ -76,20 +40,20 @@ class Q_SCXML_EXPORT QScxmlInvokableServiceFactory : public QObject
Q_OBJECT
Q_DECLARE_PRIVATE(QScxmlInvokableServiceFactory)
Q_PROPERTY(QScxmlExecutableContent::InvokeInfo invokeInfo READ invokeInfo CONSTANT)
- Q_PROPERTY(QVector<QScxmlExecutableContent::ParameterInfo> parameters READ parameters CONSTANT)
- Q_PROPERTY(QVector<QScxmlExecutableContent::StringId> names READ names CONSTANT)
+ Q_PROPERTY(QList<QScxmlExecutableContent::ParameterInfo> parameters READ parameters CONSTANT)
+ Q_PROPERTY(QList<QScxmlExecutableContent::StringId> names READ names CONSTANT)
public:
QScxmlInvokableServiceFactory(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &names,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent = nullptr);
virtual QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) = 0;
const QScxmlExecutableContent::InvokeInfo &invokeInfo() const;
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters() const;
- const QVector<QScxmlExecutableContent::StringId> &names() const;
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters() const;
+ const QList<QScxmlExecutableContent::StringId> &names() const;
protected:
QScxmlInvokableServiceFactory(QScxmlInvokableServiceFactoryPrivate &dd, QObject *parent);
@@ -104,8 +68,8 @@ public:
QScxmlStaticScxmlServiceFactory(
const QMetaObject *metaObject,
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &nameList,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &nameList,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent = nullptr);
QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) override;
@@ -117,8 +81,8 @@ class Q_SCXML_EXPORT QScxmlDynamicScxmlServiceFactory: public QScxmlInvokableSer
public:
QScxmlDynamicScxmlServiceFactory(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &names,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
QObject *parent = nullptr);
QScxmlInvokableService *invoke(QScxmlStateMachine *parentStateMachine) override;
diff --git a/src/scxml/qscxmlinvokableservice_p.h b/src/scxml/qscxmlinvokableservice_p.h
index dce1d64..a334c5a 100644
--- a/src/scxml/qscxmlinvokableservice_p.h
+++ b/src/scxml/qscxmlinvokableservice_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLINVOKABLESERVICE_P_H
#define QSCXMLINVOKABLESERVICE_P_H
@@ -64,8 +28,8 @@ public:
QString calculateId(QScxmlStateMachine *parent,
const QScxmlExecutableContent::InvokeInfo &invokeInfo, bool *ok) const;
QVariantMap calculateData(QScxmlStateMachine *parent,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
- const QVector<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &names,
bool *ok) const;
QScxmlStateMachine *parentStateMachine;
@@ -76,12 +40,12 @@ class QScxmlInvokableServiceFactoryPrivate : public QObjectPrivate
public:
QScxmlInvokableServiceFactoryPrivate(
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &names,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters);
+ const QList<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters);
QScxmlExecutableContent::InvokeInfo invokeInfo;
- QVector<QScxmlExecutableContent::StringId> names;
- QVector<QScxmlExecutableContent::ParameterInfo> parameters;
+ QList<QScxmlExecutableContent::StringId> names;
+ QList<QScxmlExecutableContent::ParameterInfo> parameters;
};
class Q_SCXML_EXPORT QScxmlScxmlService: public QScxmlInvokableService
@@ -111,8 +75,8 @@ public:
QScxmlStaticScxmlServiceFactoryPrivate(
const QMetaObject *metaObject,
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &names,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters);
+ const QList<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters);
const QMetaObject *metaObject;
};
diff --git a/src/scxml/qscxmlnulldatamodel.cpp b/src/scxml/qscxmlnulldatamodel.cpp
index 4759d22..c62226f 100644
--- a/src/scxml/qscxmlnulldatamodel.cpp
+++ b/src/scxml/qscxmlnulldatamodel.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmlnulldatamodel.h"
#include "qscxmlevent.h"
@@ -99,7 +63,7 @@ public:
ResolvedEvaluatorInfo resolved;
if (expr.startsWith(QStringLiteral("In(")) && expr.endsWith(QLatin1Char(')'))) {
resolved.error = false;
- resolved.str = expr.mid(3, expr.length() - 4);
+ resolved.str = expr.mid(3, expr.size() - 4);
} else {
resolved.error = true;
resolved.str = QStringLiteral("%1 in %2").arg(expr, td->string(info.context));
diff --git a/src/scxml/qscxmlnulldatamodel.h b/src/scxml/qscxmlnulldatamodel.h
index b34428d..f999cf2 100644
--- a/src/scxml/qscxmlnulldatamodel.h
+++ b/src/scxml/qscxmlnulldatamodel.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLNULLDATAMODEL_H
#define QSCXMLNULLDATAMODEL_H
diff --git a/src/scxml/qscxmlstatemachine.cpp b/src/scxml/qscxmlstatemachine.cpp
index 23f8a71..04d3726 100644
--- a/src/scxml/qscxmlstatemachine.cpp
+++ b/src/scxml/qscxmlstatemachine.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmlstatemachine_p.h"
#include "qscxmlexecutablecontent_p.h"
@@ -94,16 +58,21 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
*/
/*!
- \fn template<typename PointerToMemberFunction> QMetaObject::Connection QScxmlStateMachine::connectToEvent(
- const QString &scxmlEventSpec,
- const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver,
- PointerToMemberFunction method,
- Qt::ConnectionType type)
+ \fn template<typename Functor> QMetaObject::Connection QScxmlStateMachine::connectToEvent(
+ const QString &scxmlEventSpec,
+ const QObject *context,
+ Functor &&functor,
+ Qt::ConnectionType type)
+ \fn template<typename Functor> QMetaObject::Connection QScxmlStateMachine::connectToEvent(
+ const QString &scxmlEventSpec,
+ Functor &&functor,
+ Qt::ConnectionType type)
Creates a connection of the given \a type from the event specified by
- \a scxmlEventSpec to \a method in the \a receiver object.
+ \a scxmlEventSpec to \a functor, which can be a functor or a member function of
+ the optional \a context object.
- The receiver's \a method must take a QScxmlEvent as a parameter.
+ The receiver's \a functor must take a QScxmlEvent as a parameter.
In contrast to event specifications in SCXML documents, spaces are not
allowed in the \a scxmlEventSpec here. In order to connect to multiple
@@ -114,93 +83,28 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
*/
/*!
- \fn template<typename Functor> typename QtPrivate::QEnableIf<!QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction && !std::is_same<const char*, Functor>::value, QMetaObject::Connection>::Type QScxmlStateMachine::connectToEvent(
- const QString &scxmlEventSpec,
- Functor functor,
- Qt::ConnectionType type)
-
- Creates a connection of the given \a type from the event specified by
- \a scxmlEventSpec to \a functor.
-
- The \a functor must take a QScxmlEvent as a parameter.
-
- In contrast to event specifications in SCXML documents, spaces are not
- allowed in the \a scxmlEventSpec here. In order to connect to multiple
- events with different prefixes, connectToEvent() has to be called multiple
- times.
-
- Returns a handle to the connection, which can be used later to disconnect.
-*/
-
-/*!
- \fn template<typename Functor> typename QtPrivate::QEnableIf<!QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction && !std::is_same<const char*, Functor>::value, QMetaObject::Connection>::Type QScxmlStateMachine::connectToEvent(
- const QString &scxmlEventSpec,
- const QObject *context,
- Functor functor,
- Qt::ConnectionType type)
-
- Creates a connection of the given \a type from the event specified by
- \a scxmlEventSpec to \a functor using \a context as context.
-
- The \a functor must take a QScxmlEvent as a parameter.
-
- In contrast to event specifications in SCXML documents, spaces are not
- allowed in the \a scxmlEventSpec here. In order to connect to multiple
- events with different prefixes, connectToEvent() has to be called multiple
- times.
-
- Returns a handle to the connection, which can be used later to disconnect.
-*/
-
-/*!
- \fn template<typename PointerToMemberFunction> QMetaObject::Connection QScxmlStateMachine::connectToState(
- const QString &scxmlStateName,
- const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver,
- PointerToMemberFunction method,
- Qt::ConnectionType type)
+ \fn template<typename Functor> QMetaObject::Connection QScxmlStateMachine::connectToState(
+ const QString &scxmlStateName,
+ const QObject *context,
+ Functor &&functor,
+ Qt::ConnectionType type)
+ \fn template<typename Functor> QMetaObject::Connection QScxmlStateMachine::connectToState(
+ const QString &scxmlStateName,
+ Functor &&functor,
+ Qt::ConnectionType type)
Creates a connection of the given \a type from the state specified by
- \a scxmlStateName to \a method in the \a receiver object.
+ \a scxmlStateName to \a functor, which can be a functor or a member function of
+ the optional \a context object.
- The receiver's \a method must take a boolean argument that indicates
+ The receiver's \a functor must take a boolean argument that indicates
whether the state connected became active or inactive.
Returns a handle to the connection, which can be used later to disconnect.
*/
/*!
- \fn template<typename Functor> typename QtPrivate::QEnableIf<!QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction && !std::is_same<const char*, Functor>::value, QMetaObject::Connection>::Type QScxmlStateMachine::connectToState(
- const QString &scxmlStateName,
- Functor functor,
- Qt::ConnectionType type)
-
- Creates a connection of the given \a type from the state specified by
- \a scxmlStateName to \a functor.
-
- The \a functor must take a boolean argument that indicates whether the
- state connected became active or inactive.
-
- Returns a handle to the connection, which can be used later to disconnect.
-*/
-
-/*!
- \fn template<typename Functor> typename QtPrivate::QEnableIf<!QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction && !std::is_same<const char*, Functor>::value, QMetaObject::Connection>::Type QScxmlStateMachine::connectToState(
- const QString &scxmlStateName,
- const QObject *context,
- Functor functor,
- Qt::ConnectionType type)
-
- Creates a connection of the given \a type from the state specified by
- \a scxmlStateName to \a functor using \a context as context.
-
- The \a functor must take a boolean argument that indicates whether the
- state connected became active or inactive.
-
- Returns a handle to the connection, which can be used later to disconnect.
-*/
-
-/*!
- \fn std::function<void(bool)> QScxmlStateMachine::onEntry(
+ \fn [onentry] std::function<void(bool)> QScxmlStateMachine::onEntry(
const QObject *receiver, const char *method)
Returns a functor that accepts a boolean argument and calls the given
@@ -215,7 +119,7 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
*/
/*!
- \fn std::function<void(bool)> QScxmlStateMachine::onExit(
+ \fn [onexit] std::function<void(bool)> QScxmlStateMachine::onExit(
const QObject *receiver, const char *method)
Returns a functor that accepts a boolean argument and calls the given
@@ -230,7 +134,7 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
*/
/*!
- \fn template<typename Functor> std::function<void(bool)> QScxmlStateMachine::onEntry(
+ \fn [onentry-functor] template<typename Functor> std::function<void(bool)> QScxmlStateMachine::onEntry(
Functor functor)
Returns a functor that accepts a boolean argument and calls the given
@@ -242,7 +146,7 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
*/
/*!
- \fn template<typename Functor> std::function<void(bool)> QScxmlStateMachine::onExit(Functor functor)
+ \fn [onexit-functor] template<typename Functor> std::function<void(bool)> QScxmlStateMachine::onExit(Functor functor)
Returns a functor that accepts a boolean argument and calls the given
\a functor if that argument is \c false. The given \a functor must not
@@ -253,7 +157,7 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
*/
/*!
- \fn template<typename PointerToMemberFunction> std::function<void(bool)> QScxmlStateMachine::onEntry(
+ \fn [onentry-template] template<typename PointerToMemberFunction> std::function<void(bool)> QScxmlStateMachine::onEntry(
const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver,
PointerToMemberFunction method)
@@ -267,7 +171,7 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
*/
/*!
- \fn template<typename PointerToMemberFunction> std::function<void(bool)> QScxmlStateMachine::onExit(
+ \fn [onexit-template] template<typename PointerToMemberFunction> std::function<void(bool)> QScxmlStateMachine::onExit(
const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver,
PointerToMemberFunction method)
@@ -429,12 +333,8 @@ QScxmlStateMachinePrivate::QScxmlStateMachinePrivate(const QMetaObject *metaObje
: QObjectPrivate()
, m_sessionId(QScxmlStateMachinePrivate::generateSessionId(QStringLiteral("session-")))
, m_isInvoked(false)
- , m_isInitialized(false)
, m_isProcessingEvents(false)
- , m_dataModel(nullptr)
- , m_loader(&m_defaultLoader)
, m_executionEngine(nullptr)
- , m_tableData(nullptr)
, m_parentStateMachine(nullptr)
, m_eventLoopHook(this)
, m_metaObject(metaObject)
@@ -442,6 +342,7 @@ QScxmlStateMachinePrivate::QScxmlStateMachinePrivate(const QMetaObject *metaObje
{
static int metaType = qRegisterMetaType<QScxmlStateMachine *>();
Q_UNUSED(metaType);
+ m_loader.setValueBypassingBindings(&m_defaultLoader);
}
QScxmlStateMachinePrivate::~QScxmlStateMachinePrivate()
@@ -502,13 +403,13 @@ QScxmlInvokableServiceFactory *QScxmlStateMachinePrivate::serviceFactory(int id)
Q_ASSERT(id <= m_stateTable->maxServiceId && id >= 0);
QScxmlInvokableServiceFactory *& factory = m_cachedFactories[size_t(id)];
if (factory == nullptr)
- factory = m_tableData->serviceFactory(id);
+ factory = m_tableData.value()->serviceFactory(id);
return factory;
}
bool QScxmlStateMachinePrivate::executeInitialSetup()
{
- return m_executionEngine->execute(m_tableData->initialSetup());
+ return m_executionEngine->execute(m_tableData.value()->initialSetup());
}
void QScxmlStateMachinePrivate::routeEvent(QScxmlEvent *event)
@@ -529,7 +430,7 @@ void QScxmlStateMachinePrivate::routeEvent(QScxmlEvent *event)
}
} else if (origin.startsWith(QStringLiteral("#_")) && origin != QStringLiteral("#_internal")) {
// route to children
- auto originId = origin.midRef(2);
+ auto originId = QStringView{origin}.mid(2);
for (const auto &invokedService : m_invokedServices) {
auto service = invokedService.service;
if (service == nullptr)
@@ -727,12 +628,12 @@ void QScxmlStateMachinePrivate::processEvents()
void QScxmlStateMachinePrivate::setEvent(QScxmlEvent *event)
{
Q_ASSERT(event);
- m_dataModel->setScxmlEvent(*event);
+ m_dataModel.value()->setScxmlEvent(*event);
}
void QScxmlStateMachinePrivate::resetEvent()
{
- m_dataModel->setScxmlEvent(QScxmlEvent());
+ m_dataModel.value()->setScxmlEvent(QScxmlEvent());
}
void QScxmlStateMachinePrivate::emitStateActive(int stateIndex, bool active)
@@ -747,6 +648,7 @@ void QScxmlStateMachinePrivate::emitStateActive(int stateIndex, bool active)
void QScxmlStateMachinePrivate::emitInvokedServicesChanged()
{
Q_Q(QScxmlStateMachine);
+ m_invokedServicesComputedProperty.notify();
emit q->invokedServicesChanged(q->invokedServices());
}
@@ -767,10 +669,14 @@ void QScxmlStateMachinePrivate::attach(QScxmlStateMachineInfo *info)
void QScxmlStateMachinePrivate::updateMetaCache()
{
+ // This function creates a mapping from state index/name to their signal indexes.
+ // The state index may differ from its signal index as we don't generate history
+ // and invalid states, effectively skipping them
m_stateIndexToSignalIndex.clear();
m_stateNameToSignalIndex.clear();
- if (!m_tableData)
+ const QScxmlTableData *tableData = m_tableData.valueBypassingBindings();
+ if (!tableData)
return;
if (!m_stateTable)
@@ -782,7 +688,7 @@ void QScxmlStateMachinePrivate::updateMetaCache()
const auto &s = m_stateTable->state(i);
if (!s.isHistoryState() && s.type != StateTable::State::Invalid) {
m_stateIndexToSignalIndex.insert(i, signalIndex);
- m_stateNameToSignalIndex.insert(m_tableData->string(s.name),
+ m_stateNameToSignalIndex.insert(tableData->string(s.name),
signalIndex + methodOffset);
++signalIndex;
@@ -794,7 +700,7 @@ QStringList QScxmlStateMachinePrivate::stateNames(const std::vector<int> &stateI
{
QStringList names;
for (int idx : stateIndexes)
- names.append(m_tableData->string(m_stateTable->state(idx).name));
+ names.append(m_tableData.value()->string(m_stateTable->state(idx).name));
return names;
}
@@ -852,7 +758,7 @@ bool QScxmlStateMachinePrivate::nameMatch(const StateTable::Array &patterns,
const QString eventName = event->name();
bool selected = false;
for (int eventSelectorIter = 0; eventSelectorIter < patterns.size(); ++eventSelectorIter) {
- QString eventStr = m_tableData->string(patterns[eventSelectorIter]);
+ QString eventStr = m_tableData.value()->string(patterns[eventSelectorIter]);
if (eventStr == QStringLiteral("*")) {
selected = true;
break;
@@ -913,7 +819,7 @@ void QScxmlStateMachinePrivate::selectTransitions(OrderedSet &enabledTransitions
enabled = true;
} else {
bool ok = false;
- enabled = m_dataModel->evaluateToBool(t.condition, &ok) && ok;
+ enabled = m_dataModel.value()->evaluateToBool(t.condition, &ok) && ok;
}
}
} else {
@@ -922,7 +828,7 @@ void QScxmlStateMachinePrivate::selectTransitions(OrderedSet &enabledTransitions
enabled = true;
} else {
bool ok = false;
- enabled = m_dataModel->evaluateToBool(t.condition, &ok) && ok;
+ enabled = m_dataModel.value()->evaluateToBool(t.condition, &ok) && ok;
}
}
}
@@ -1037,13 +943,13 @@ void QScxmlStateMachinePrivate::microstep(const OrderedSet &enabledTransitions)
const auto &transition = m_stateTable->transition(t);
QString from = QStringLiteral("(none)");
if (transition.source != StateTable::InvalidIndex)
- from = m_tableData->string(m_stateTable->state(transition.source).name);
+ from = m_tableData.value()->string(m_stateTable->state(transition.source).name);
QStringList to;
if (transition.targets == StateTable::InvalidIndex) {
to.append(QStringLiteral("(none)"));
} else {
for (int t : m_stateTable->array(transition.targets))
- to.append(m_tableData->string(m_stateTable->state(t).name));
+ to.append(m_tableData.value()->string(m_stateTable->state(t).name));
}
qCDebug(qscxmlLog) << q_func() << "\t" << t << ":" << from << "->"
<< to.join(QLatin1Char(','));
@@ -1073,7 +979,7 @@ void QScxmlStateMachinePrivate::exitStates(const OrderedSet &enabledTransitions)
for (int s : statesToExitSorted) {
for (int h : historyStates(s)) {
const auto &hState = m_stateTable->state(h);
- QVector<int> history;
+ QList<int> history;
for (int s0 : m_configuration) {
const auto &s0State = m_stateTable->state(s0);
@@ -1100,7 +1006,7 @@ void QScxmlStateMachinePrivate::exitStates(const OrderedSet &enabledTransitions)
if (m_infoSignalProxy) {
emit m_infoSignalProxy->statesExited(
- QVector<QScxmlStateMachineInfo::StateId>(statesToExitSorted.begin(),
+ QList<QScxmlStateMachineInfo::StateId>(statesToExitSorted.begin(),
statesToExitSorted.end()));
}
}
@@ -1132,7 +1038,7 @@ void QScxmlStateMachinePrivate::executeTransitionContent(const OrderedSet &enabl
if (m_infoSignalProxy) {
emit m_infoSignalProxy->transitionsTriggered(
- QVector<QScxmlStateMachineInfo::TransitionId>(enabledTransitions.list().begin(),
+ QList<QScxmlStateMachineInfo::TransitionId>(enabledTransitions.list().begin(),
enabledTransitions.list().end()));
}
}
@@ -1176,7 +1082,7 @@ void QScxmlStateMachinePrivate::enterStates(const OrderedSet &enabledTransitions
emit q->runningChanged(false);
} else {
const auto &parent = m_stateTable->state(state.parent);
- m_executionEngine->execute(state.doneData, m_tableData->string(parent.name));
+ m_executionEngine->execute(state.doneData, m_tableData.value()->string(parent.name));
if (parent.parent != StateTable::InvalidIndex) {
const auto &grandParent = m_stateTable->state(parent.parent);
if (grandParent.isParallel()) {
@@ -1184,7 +1090,7 @@ void QScxmlStateMachinePrivate::enterStates(const OrderedSet &enabledTransitions
auto e = new QScxmlEvent;
e->setEventType(QScxmlEvent::InternalEvent);
e->setName(QStringLiteral("done.state.")
- + m_tableData->string(grandParent.name));
+ + m_tableData.value()->string(grandParent.name));
q->submitEvent(e);
}
}
@@ -1196,7 +1102,7 @@ void QScxmlStateMachinePrivate::enterStates(const OrderedSet &enabledTransitions
emitStateActive(s, true);
if (m_infoSignalProxy) {
emit m_infoSignalProxy->statesEntered(
- QVector<QScxmlStateMachineInfo::StateId>(sortedStates.begin(),
+ QList<QScxmlStateMachineInfo::StateId>(sortedStates.begin(),
sortedStates.end()));
}
}
@@ -1247,7 +1153,15 @@ void QScxmlStateMachinePrivate::addDescendantStatesToEnter(
addAncestorStatesToEnter(s, state.parent, statesToEnter, statesForDefaultEntry,
defaultHistoryContent);
} else {
- const auto transitionIdx = m_stateTable->array(state.transitions)[0];
+ int transitionIdx = StateTable::InvalidIndex;
+ if (state.transitions == StateTable::InvalidIndex) {
+ int parentInitialTransition = m_stateTable->state(state.parent).initialTransition;
+ if (parentInitialTransition == StateTable::InvalidIndex)
+ return;
+ transitionIdx = parentInitialTransition;
+ } else {
+ transitionIdx = m_stateTable->array(state.transitions)[0];
+ }
const auto &defaultHistoryTransition = m_stateTable->transition(transitionIdx);
defaultHistoryContent->operator[](state.parent) =
defaultHistoryTransition.transitionInstructions;
@@ -1457,7 +1371,7 @@ void QScxmlStateMachinePrivate::getEffectiveTargetStates(OrderedSet *targets,
for (int historyState : historyValueIter.value()) {
targets->add(historyState);
}
- } else {
+ } else if (state.transitions != StateTable::InvalidIndex) {
getEffectiveTargetStates(targets, m_stateTable->array(state.transitions)[0]);
}
} else {
@@ -1507,10 +1421,10 @@ QScxmlStateMachine *QScxmlStateMachine::fromData(QIODevice *data, const QString
return compiler.compile();
}
-QVector<QScxmlError> QScxmlStateMachine::parseErrors() const
+QList<QScxmlError> QScxmlStateMachine::parseErrors() const
{
Q_D(const QScxmlStateMachine);
- return d->m_parserData ? d->m_parserData->m_errors : QVector<QScxmlError>();
+ return d->m_parserData ? d->m_parserData->m_errors : QList<QScxmlError>();
}
QScxmlStateMachine::QScxmlStateMachine(const QMetaObject *metaObject, QObject *parent)
@@ -1553,8 +1467,7 @@ QScxmlStateMachine::QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *p
Changing the data model when the state machine has been \c initialized is
not specified in the SCXML standard and leads to undefined behavior.
- \sa QScxmlDataModel, QScxmlNullDataModel, QScxmlEcmaScriptDataModel,
- QScxmlCppDataModel
+ \sa QScxmlDataModel, QScxmlNullDataModel, QScxmlCppDataModel
*/
/*!
@@ -1569,8 +1482,7 @@ QScxmlStateMachine::QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *p
Changing the data model when the state machine has been \l initialized is
not specified in the SCXML standard and leads to undefined behavior.
- \sa QScxmlDataModel, QScxmlNullDataModel, QScxmlEcmaScriptDataModel,
- QScxmlCppDataModel
+ \sa QScxmlDataModel, QScxmlNullDataModel, QScxmlCppDataModel
*/
/*!
@@ -1723,6 +1635,12 @@ bool QScxmlStateMachine::isInitialized() const
return d->m_isInitialized;
}
+QBindable<bool> QScxmlStateMachine::bindableInitialized() const
+{
+ Q_D(const QScxmlStateMachine);
+ return &d->m_isInitialized;
+}
+
/*!
* Sets the data model for this state machine to \a model. There is a 1:1
* relation between state machines and models. After setting the model once you
@@ -1733,10 +1651,13 @@ void QScxmlStateMachine::setDataModel(QScxmlDataModel *model)
{
Q_D(QScxmlStateMachine);
- if (d->m_dataModel == nullptr && model != nullptr) {
- d->m_dataModel = model;
- if (model)
- model->setStateMachine(this);
+ if (d->m_dataModel.valueBypassingBindings() == nullptr && model != nullptr) {
+ // the binding is removed only on the first valid set
+ // as the later attempts are ignored
+ d->m_dataModel.removeBindingUnlessInWrapper();
+ d->m_dataModel.setValueBypassingBindings(model);
+ model->setStateMachine(this);
+ d->m_dataModel.notify();
emit dataModelChanged(model);
}
}
@@ -1751,14 +1672,16 @@ QScxmlDataModel *QScxmlStateMachine::dataModel() const
return d->m_dataModel;
}
-void QScxmlStateMachine::setLoader(QScxmlCompiler::Loader *loader)
+QBindable<QScxmlDataModel*> QScxmlStateMachine::bindableDataModel()
{
Q_D(QScxmlStateMachine);
+ return &d->m_dataModel;
+}
- if (loader != d->m_loader) {
- d->m_loader = loader;
- emit loaderChanged(loader);
- }
+void QScxmlStateMachine::setLoader(QScxmlCompiler::Loader *loader)
+{
+ Q_D(QScxmlStateMachine);
+ d->m_loader.setValue(loader);
}
QScxmlCompiler::Loader *QScxmlStateMachine::loader() const
@@ -1768,6 +1691,12 @@ QScxmlCompiler::Loader *QScxmlStateMachine::loader() const
return d->m_loader;
}
+QBindable<QScxmlCompiler::Loader*> QScxmlStateMachine::bindableLoader()
+{
+ Q_D(QScxmlStateMachine);
+ return &d->m_loader;
+}
+
QScxmlTableData *QScxmlStateMachine::tableData() const
{
Q_D(const QScxmlStateMachine);
@@ -1779,14 +1708,18 @@ void QScxmlStateMachine::setTableData(QScxmlTableData *tableData)
{
Q_D(QScxmlStateMachine);
- if (d->m_tableData == tableData)
+ d->m_tableData.removeBindingUnlessInWrapper();
+ if (d->m_tableData.valueBypassingBindings() == tableData)
return;
- d->m_tableData = tableData;
+ d->m_tableData.setValueBypassingBindings(tableData);
if (tableData) {
d->m_stateTable = reinterpret_cast<const QScxmlExecutableContent::StateTable *>(
tableData->stateMachineTable());
- if (objectName().isEmpty()) {
+ // cannot use objectName() here, because it creates binding loop
+ const QString currentObjectName = d->extraData
+ ? d->extraData->objectName.valueBypassingBindings() : QString();
+ if (currentObjectName.isEmpty()) {
setObjectName(tableData->name());
}
if (d->m_stateTable->maxServiceId != QScxmlExecutableContent::StateTable::InvalidIndex) {
@@ -1806,9 +1739,16 @@ void QScxmlStateMachine::setTableData(QScxmlTableData *tableData)
d->updateMetaCache();
+ d->m_tableData.notify();
emit tableDataChanged(tableData);
}
+QBindable<QScxmlTableData*> QScxmlStateMachine::bindableTableData()
+{
+ Q_D(QScxmlStateMachine);
+ return &d->m_tableData;
+}
+
/*!
\qmlmethod ScxmlStateMachine::stateNames(bool compress)
@@ -1845,7 +1785,7 @@ QStringList QScxmlStateMachine::stateNames(bool compress) const
for (int i = 0; i < d->m_stateTable->stateCount; ++i) {
const auto &state = d->m_stateTable->state(i);
if (!compress || state.isAtomic())
- names.append(d->m_tableData->string(state.name));
+ names.append(d->m_tableData.value()->string(state.name));
}
return names;
}
@@ -1876,7 +1816,7 @@ QStringList QScxmlStateMachine::activeStateNames(bool compress) const
for (int stateIdx : d->m_configuration) {
const auto &state = d->m_stateTable->state(stateIdx);
if (state.isAtomic() || !compress)
- result.append(d->m_tableData->string(state.name));
+ result.append(d->m_tableData.value()->string(state.name));
}
return result;
}
@@ -1897,7 +1837,7 @@ bool QScxmlStateMachine::isActive(const QString &scxmlStateName) const
for (int stateIndex : d->m_configuration) {
const auto &state = d->m_stateTable->state(stateIndex);
- if (d->m_tableData->string(state.name) == scxmlStateName)
+ if (d->m_tableData.value()->string(state.name) == scxmlStateName)
return true;
}
@@ -1914,7 +1854,7 @@ QMetaObject::Connection QScxmlStateMachine::connectToStateImpl(const QString &sc
types = QtPrivate::ConnectionTypes<QtPrivate::List<bool> >::types();
Q_D(QScxmlStateMachine);
- const int signalIndex = d->m_stateNameToSignalIndex.value(scxmlStateName);
+ const int signalIndex = d->m_stateNameToSignalIndex.value(scxmlStateName, -1);
return signalIndex < 0 ? QMetaObject::Connection()
: QObjectPrivate::connectImpl(this, signalIndex, receiver, slot, slotObj,
type, types, d->m_metaObject);
@@ -2003,20 +1943,19 @@ bool QScxmlStateMachine::init()
{
Q_D(QScxmlStateMachine);
- if (d->m_isInitialized)
+ if (d->m_isInitialized.value())
return false;
if (!parseErrors().isEmpty())
return false;
- if (!dataModel() || !dataModel()->setup(d->m_initialValues))
+ if (!dataModel() || !dataModel()->setup(d->m_initialValues.value()))
return false;
if (!d->executeInitialSetup())
return false;
- d->m_isInitialized = true;
- emit initializedChanged(true);
+ d->m_isInitialized.setValue(true);
return true;
}
@@ -2054,10 +1993,13 @@ QVariantMap QScxmlStateMachine::initialValues()
void QScxmlStateMachine::setInitialValues(const QVariantMap &initialValues)
{
Q_D(QScxmlStateMachine);
- if (initialValues != d->m_initialValues) {
- d->m_initialValues = initialValues;
- emit initialValuesChanged(initialValues);
- }
+ d->m_initialValues.setValue(initialValues);
+}
+
+QBindable<QVariantMap> QScxmlStateMachine::bindableInitialValues()
+{
+ Q_D(QScxmlStateMachine);
+ return &d->m_initialValues;
}
QString QScxmlStateMachine::name() const
@@ -2206,7 +2148,7 @@ bool QScxmlStateMachine::isDispatchableTarget(const QString &target) const
return true; // that's the current state machine
if (target.startsWith(QStringLiteral("#_"))) {
- QStringRef targetId = target.midRef(2);
+ QStringView targetId = QStringView{target}.mid(2);
for (auto invokedService : d->m_invokedServices) {
if (invokedService.service && invokedService.service->id() == targetId)
return true;
@@ -2229,16 +2171,16 @@ bool QScxmlStateMachine::isDispatchableTarget(const QString &target) const
state machine (possibly recursively).
*/
-QVector<QScxmlInvokableService *> QScxmlStateMachine::invokedServices() const
+QList<QScxmlInvokableService *> QScxmlStateMachine::invokedServices() const
{
Q_D(const QScxmlStateMachine);
+ return d->m_invokedServicesComputedProperty;
+}
- QVector<QScxmlInvokableService *> result;
- for (int i = 0, ei = int(d->m_invokedServices.size()); i != ei; ++i) {
- if (auto service = d->m_invokedServices[size_t(i)].service)
- result.append(service);
- }
- return result;
+QBindable<QList<QScxmlInvokableService*>> QScxmlStateMachine::bindableInvokedServices()
+{
+ Q_D(QScxmlStateMachine);
+ return &d->m_invokedServicesComputedProperty;
}
/*!
@@ -2310,20 +2252,28 @@ QVector<QScxmlInvokableService *> QScxmlStateMachine::invokedServices() const
*/
/*!
- Starts this state machine. The machine will reset its configuration and
- transition to the initial state. When a final top-level state
+ Starts this state machine. When a final top-level state
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().
+ \note Calling start() after stop() will not result in a full reset of its
+ configuration yet, hence it is strongly advised not to do it.
+
+ \note Starting a finished machine yields a warning.
+
\sa runningChanged(), setRunning(), stop(), finished()
*/
void QScxmlStateMachine::start()
{
Q_D(QScxmlStateMachine);
+ if (d->isFinished()) {
+ qCWarning(qscxmlLog) << this << "Can't start finished machine";
+ }
+
if (!parseErrors().isEmpty())
return;
@@ -2366,7 +2316,11 @@ void QScxmlStateMachine::stop()
bool QScxmlStateMachine::isActive(int stateIndex) const
{
Q_D(const QScxmlStateMachine);
- return d->m_configuration.contains(stateIndex);
+ // Here we need to find the actual internal state index that corresponds with the
+ // index of the compiled metaobject (which is same as its mapped signal index).
+ // See updateMetaCache()
+ const int mappedStateIndex = d->m_stateIndexToSignalIndex.key(stateIndex, -1);
+ return d->m_configuration.contains(mappedStateIndex);
}
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachine.h b/src/scxml/qscxmlstatemachine.h
index a0a4e8b..a63a602 100644
--- a/src/scxml/qscxmlstatemachine.h
+++ b/src/scxml/qscxmlstatemachine.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLSTATEMACHINE_H
#define QSCXMLSTATEMACHINE_H
@@ -46,18 +10,21 @@
#include <QtScxml/qscxmlcompiler.h>
#include <QtScxml/qscxmlinvokableservice.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qpointer.h>
#include <QtCore/qstring.h>
-#include <QtCore/qvector.h>
#include <QtCore/qurl.h>
#include <QtCore/qvariant.h>
-#include <QtCore/qpointer.h>
#include <functional>
+Q_MOC_INCLUDE(qscxmltabledata.h)
+
QT_BEGIN_NAMESPACE
class QIODevice;
class QXmlStreamWriter;
class QTextStream;
+class QScxmlTableData;
class QScxmlStateMachinePrivate;
class Q_SCXML_EXPORT QScxmlStateMachine: public QObject
@@ -65,16 +32,22 @@ class Q_SCXML_EXPORT QScxmlStateMachine: public QObject
Q_DECLARE_PRIVATE(QScxmlStateMachine)
Q_OBJECT
Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged)
- Q_PROPERTY(bool initialized READ isInitialized NOTIFY initializedChanged)
- Q_PROPERTY(QScxmlDataModel *dataModel READ dataModel WRITE setDataModel NOTIFY dataModelChanged)
- Q_PROPERTY(QVariantMap initialValues READ initialValues WRITE setInitialValues NOTIFY initialValuesChanged)
- Q_PROPERTY(QVector<QScxmlInvokableService*> invokedServices READ invokedServices NOTIFY invokedServicesChanged)
+ Q_PROPERTY(bool initialized READ isInitialized
+ NOTIFY initializedChanged BINDABLE bindableInitialized)
+ Q_PROPERTY(QScxmlDataModel *dataModel READ dataModel WRITE setDataModel
+ NOTIFY dataModelChanged BINDABLE bindableDataModel)
+ Q_PROPERTY(QVariantMap initialValues READ initialValues WRITE setInitialValues
+ NOTIFY initialValuesChanged BINDABLE bindableInitialValues)
+ Q_PROPERTY(QList<QScxmlInvokableService*> invokedServices READ invokedServices
+ NOTIFY invokedServicesChanged BINDABLE bindableInvokedServices)
Q_PROPERTY(QString sessionId READ sessionId CONSTANT)
Q_PROPERTY(QString name READ name CONSTANT)
Q_PROPERTY(bool invoked READ isInvoked CONSTANT)
- Q_PROPERTY(QVector<QScxmlError> parseErrors READ parseErrors CONSTANT)
- Q_PROPERTY(QScxmlCompiler::Loader *loader READ loader WRITE setLoader NOTIFY loaderChanged)
- Q_PROPERTY(QScxmlTableData *tableData READ tableData WRITE setTableData NOTIFY tableDataChanged)
+ Q_PROPERTY(QList<QScxmlError> parseErrors READ parseErrors CONSTANT)
+ Q_PROPERTY(QScxmlCompiler::Loader *loader READ loader WRITE setLoader
+ NOTIFY loaderChanged BINDABLE bindableLoader)
+ Q_PROPERTY(QScxmlTableData *tableData READ tableData WRITE setTableData
+ NOTIFY tableDataChanged BINDABLE bindableTableData)
protected:
explicit QScxmlStateMachine(const QMetaObject *metaObject, QObject *parent = nullptr);
@@ -83,24 +56,28 @@ protected:
public:
static QScxmlStateMachine *fromFile(const QString &fileName);
static QScxmlStateMachine *fromData(QIODevice *data, const QString &fileName = QString());
- QVector<QScxmlError> parseErrors() const;
+ QList<QScxmlError> parseErrors() const;
QString sessionId() const;
bool isInvoked() const;
bool isInitialized() const;
+ QBindable<bool> bindableInitialized() const;
void setDataModel(QScxmlDataModel *model);
QScxmlDataModel *dataModel() const;
+ QBindable<QScxmlDataModel*> bindableDataModel();
void setLoader(QScxmlCompiler::Loader *loader);
QScxmlCompiler::Loader *loader() const;
+ QBindable<QScxmlCompiler::Loader*> bindableLoader();
bool isRunning() const;
void setRunning(bool running);
QVariantMap initialValues();
void setInitialValues(const QVariantMap &initialValues);
+ QBindable<QVariantMap> bindableInitialValues();
QString name() const;
Q_INVOKABLE QStringList stateNames(bool compress = true) const;
@@ -111,48 +88,46 @@ public:
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection);
- // connect state to a QObject slot
- template <typename PointerToMemberFunction>
- inline QMetaObject::Connection connectToState(
+ // connect state with context
+ template <typename Functor>
+#ifdef Q_QDOC
+ QMetaObject::Connection
+#else
+ inline std::enable_if_t<!std::is_same_v<const char*, Functor>, QMetaObject::Connection>
+#endif
+ connectToState(
const QString &scxmlStateName,
- const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver,
- PointerToMemberFunction method,
+#ifdef Q_QDOC
+ const QObject *context,
+#else
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *context,
+#endif
+ Functor &&functor,
Qt::ConnectionType type = Qt::AutoConnection)
{
- typedef QtPrivate::FunctionPointer<PointerToMemberFunction> SlotType;
+ using Prototype = void(*)(bool);
return connectToStateImpl(
- scxmlStateName, receiver, nullptr,
- new QtPrivate::QSlotObject<PointerToMemberFunction,
- typename SlotType::Arguments, void>(method),
+ scxmlStateName, context, nullptr,
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(functor)),
type);
}
- // connect state to a functor or function pointer (without context)
- template <typename Functor>
- inline typename QtPrivate::QEnableIf<
- !QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Functor>::value, QMetaObject::Connection>::Type
- connectToState(const QString &scxmlStateName, Functor functor,
- Qt::ConnectionType type = Qt::AutoConnection)
- {
- // Use this as context
- return connectToState(scxmlStateName, this, functor, type);
- }
-
- // connectToState to a functor or function pointer (with context)
+ // connect state without context
template <typename Functor>
- inline typename QtPrivate::QEnableIf<
- !QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Functor>::value, QMetaObject::Connection>::Type
- connectToState(const QString &scxmlStateName, const QObject *context, Functor functor,
- Qt::ConnectionType type = Qt::AutoConnection)
+#ifdef Q_QDOC
+ QMetaObject::Connection
+#else
+ inline std::enable_if_t<!std::is_same_v<const char*, Functor>, QMetaObject::Connection>
+#endif
+ connectToState(
+ const QString &scxmlStateName,
+ Functor &&functor,
+ Qt::ConnectionType type = Qt::AutoConnection)
{
- QtPrivate::QSlotObjectBase *slotObj = new QtPrivate::QFunctorSlotObject<Functor, 1,
- QtPrivate::List<bool>, void>(functor);
- return connectToStateImpl(scxmlStateName, context, reinterpret_cast<void **>(&functor),
- slotObj, type);
+ return connectToState(scxmlStateName, this, std::forward<Functor>(functor), type);
}
+ //! [onentry]
static std::function<void(bool)> onEntry(const QObject *receiver, const char *method)
{
const QPointer<QObject> receiverPointer(const_cast<QObject *>(receiver));
@@ -162,6 +137,7 @@ public:
};
}
+ //! [onexit]
static std::function<void(bool)> onExit(const QObject *receiver, const char *method)
{
const QPointer<QObject> receiverPointer(const_cast<QObject *>(receiver));
@@ -171,6 +147,7 @@ public:
};
}
+ //! [onentry-functor]
template<typename Functor>
static std::function<void(bool)> onEntry(Functor functor)
{
@@ -180,6 +157,7 @@ public:
};
}
+ //! [onexit-functor]
template<typename Functor>
static std::function<void(bool)> onExit(Functor functor)
{
@@ -189,6 +167,7 @@ public:
};
}
+ //! [onentry-template]
template<typename PointerToMemberFunction>
static std::function<void(bool)> onEntry(
const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver,
@@ -202,6 +181,7 @@ public:
};
}
+ //! [onexit-template]
template<typename PointerToMemberFunction>
static std::function<void(bool)> onExit(
const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver,
@@ -219,46 +199,42 @@ public:
const QObject *receiver, const char *method,
Qt::ConnectionType type = Qt::AutoConnection);
- // connect state to a QObject slot
- template <typename PointerToMemberFunction>
- inline QMetaObject::Connection connectToEvent(
+ // connect event with context
+ template <typename Functor>
+#ifdef Q_QDOC
+ QMetaObject::Connection
+#else
+ inline std::enable_if_t<!std::is_same_v<const char*, Functor>, QMetaObject::Connection>
+#endif
+ connectToEvent(
const QString &scxmlEventSpec,
- const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver,
- PointerToMemberFunction method,
+#ifdef Q_QDOC
+ const QObject *context,
+#else
+ const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *context,
+#endif
+ Functor &&functor,
Qt::ConnectionType type = Qt::AutoConnection)
{
- typedef QtPrivate::FunctionPointer<PointerToMemberFunction> SlotType;
+ using Prototype = void(*)(QScxmlEvent);
return connectToEventImpl(
- scxmlEventSpec, receiver, nullptr,
- new QtPrivate::QSlotObject<PointerToMemberFunction,
- typename SlotType::Arguments, void>(method),
+ scxmlEventSpec, context, nullptr,
+ QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(functor)),
type);
}
- // connect state to a functor or function pointer (without context)
+ // connect event without context
template <typename Functor>
- inline typename QtPrivate::QEnableIf<
- !QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Functor>::value, QMetaObject::Connection>::Type
- connectToEvent(const QString &scxmlEventSpec, Functor functor,
+#ifdef Q_QDOC
+ QMetaObject::Connection
+#else
+ inline std::enable_if_t<!std::is_same_v<const char*, Functor>, QMetaObject::Connection>
+#endif
+ connectToEvent(const QString &scxmlEventSpec, Functor &&functor,
Qt::ConnectionType type = Qt::AutoConnection)
{
// Use this as context
- return connectToEvent(scxmlEventSpec, this, functor, type);
- }
-
- // connectToEvent to a functor or function pointer (with context)
- template <typename Functor>
- inline typename QtPrivate::QEnableIf<
- !QtPrivate::FunctionPointer<Functor>::IsPointerToMemberFunction &&
- !std::is_same<const char*, Functor>::value, QMetaObject::Connection>::Type
- connectToEvent(const QString &scxmlEventSpec, const QObject *context, Functor functor,
- Qt::ConnectionType type = Qt::AutoConnection)
- {
- QtPrivate::QSlotObjectBase *slotObj = new QtPrivate::QFunctorSlotObject<Functor, 1,
- QtPrivate::List<QScxmlEvent>, void>(functor);
- return connectToEventImpl(scxmlEventSpec, context, reinterpret_cast<void **>(&functor),
- slotObj, type);
+ return connectToEvent(scxmlEventSpec, this, std::forward<Functor>(functor), type);
}
Q_INVOKABLE void submitEvent(QScxmlEvent *event);
@@ -268,14 +244,16 @@ public:
Q_INVOKABLE bool isDispatchableTarget(const QString &target) const;
- QVector<QScxmlInvokableService *> invokedServices() const;
+ QList<QScxmlInvokableService *> invokedServices() const;
+ QBindable<QList<QScxmlInvokableService*>> bindableInvokedServices();
QScxmlTableData *tableData() const;
void setTableData(QScxmlTableData *tableData);
+ QBindable<QScxmlTableData*> bindableTableData();
Q_SIGNALS:
void runningChanged(bool running);
- void invokedServicesChanged(const QVector<QScxmlInvokableService *> &invokedServices);
+ void invokedServicesChanged(const QList<QScxmlInvokableService *> &invokedServices);
void log(const QString &label, const QString &msg);
void reachedStableState();
void finished();
@@ -313,6 +291,6 @@ private:
QT_END_NAMESPACE
Q_DECLARE_METATYPE(QScxmlStateMachine *)
-Q_DECLARE_METATYPE(QVector<QScxmlInvokableService *>)
+Q_DECLARE_METATYPE(QList<QScxmlInvokableService *>)
#endif // QSCXMLSTATEMACHINE_H
diff --git a/src/scxml/qscxmlstatemachine_p.h b/src/scxml/qscxmlstatemachine_p.h
index 1c354fa..3d18ffc 100644
--- a/src/scxml/qscxmlstatemachine_p.h
+++ b/src/scxml/qscxmlstatemachine_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLSTATEMACHINE_P_H
#define QSCXMLSTATEMACHINE_P_H
@@ -56,7 +20,12 @@
#include <QtScxml/private/qscxmlstatemachineinfo_p.h>
#include <QtCore/private/qobject_p.h>
#include <QtCore/private/qmetaobject_p.h>
+#include <QtCore/private/qproperty_p.h>
+#include <QtCore/qhash.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qvariant.h>
#include <QtCore/qmetaobject.h>
+#include "qscxmlglobals_p.h"
QT_BEGIN_NAMESPACE
@@ -113,14 +82,14 @@ public:
{}
Q_SIGNALS:
- void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states);
- void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states);
- void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions);
+ void statesEntered(const QList<QScxmlStateMachineInfo::StateId> &states);
+ void statesExited(const QList<QScxmlStateMachineInfo::StateId> &states);
+ void transitionsTriggered(const QList<QScxmlStateMachineInfo::TransitionId> &transitions);
};
} // QScxmlInternal namespace
class QScxmlInvokableService;
-class QScxmlStateMachinePrivate: public QObjectPrivate
+class Q_SCXML_EXPORT QScxmlStateMachinePrivate: public QObjectPrivate
{
Q_DECLARE_PUBLIC(QScxmlStateMachine)
@@ -152,7 +121,7 @@ public: // types
{
public:
QScopedPointer<QScxmlDataModel> m_ownedDataModel;
- QVector<QScxmlError> m_errors;
+ QList<QScxmlError> m_errors;
};
// The OrderedSet is a set where it elements are in insertion order. See
@@ -216,7 +185,7 @@ public: // types
class Queue
{
- QVector<QScxmlEvent *> storage;
+ QList<QScxmlEvent *> storage;
public:
Queue()
@@ -328,14 +297,46 @@ private:
public: // types & data fields:
QString m_sessionId;
bool m_isInvoked;
- bool m_isInitialized;
+
+ void isInitializedChanged()
+ {
+ emit q_func()->initializedChanged(m_isInitialized.value());
+ }
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QScxmlStateMachinePrivate,
+ bool, m_isInitialized, false,
+ &QScxmlStateMachinePrivate::isInitializedChanged);
+
+ void initialValuesChanged()
+ {
+ emit q_func()->initialValuesChanged(m_initialValues.value());
+ }
+ Q_OBJECT_BINDABLE_PROPERTY(QScxmlStateMachinePrivate, QVariantMap, m_initialValues,
+ &QScxmlStateMachinePrivate::initialValuesChanged);
+
+ void loaderChanged()
+ {
+ emit q_func()->loaderChanged(m_loader.value());
+ }
+ Q_OBJECT_BINDABLE_PROPERTY(QScxmlStateMachinePrivate, QScxmlCompiler::Loader*, m_loader,
+ &QScxmlStateMachinePrivate::loaderChanged);
+
+ void setDataModel(QScxmlDataModel* loader)
+ {
+ q_func()->setDataModel(loader);
+ }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QScxmlStateMachinePrivate, QScxmlDataModel*, m_dataModel,
+ &QScxmlStateMachinePrivate::setDataModel, nullptr);
+
+ void setTableData(QScxmlTableData* tableData)
+ {
+ q_func()->setTableData(tableData);
+ }
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QScxmlStateMachinePrivate, QScxmlTableData*, m_tableData,
+ &QScxmlStateMachinePrivate::setTableData, nullptr);
+
bool m_isProcessingEvents;
- QVariantMap m_initialValues;
- QScxmlDataModel *m_dataModel;
QScxmlCompilerPrivate::DefaultLoader m_defaultLoader;
- QScxmlCompiler::Loader *m_loader;
QScxmlExecutionEngine *m_executionEngine;
- QScxmlTableData *m_tableData;
const StateTable *m_stateTable;
QScxmlStateMachine *m_parentStateMachine;
QScxmlInternal::EventLoopHook m_eventLoopHook;
@@ -346,7 +347,7 @@ public: // types & data fields:
private:
QScopedPointer<ParserData> m_parserData; // used when created by StateMachine::fromFile.
- typedef QHash<int, QVector<int>> HistoryValues;
+ typedef QHash<int, QList<int>> HistoryValues;
struct InvokedService {
int invokingState;
QScxmlInvokableService *service;
@@ -360,6 +361,18 @@ private:
Queue m_externalQueue;
QSet<int> m_statesToInvoke;
std::vector<InvokedService> m_invokedServices;
+ QList<QScxmlInvokableService*> invokedServicesActualCalculation() const
+ {
+ QList<QScxmlInvokableService *> result;
+ for (size_t i = 0, ei = m_invokedServices.size(); i != ei; ++i) {
+ if (auto service = m_invokedServices[i].service)
+ result.append(service);
+ }
+ return result;
+ }
+ Q_OBJECT_COMPUTED_PROPERTY(QScxmlStateMachinePrivate, QList<QScxmlInvokableService*>,
+ m_invokedServicesComputedProperty,
+ &QScxmlStateMachinePrivate::invokedServicesActualCalculation);
std::vector<bool> m_isFirstStateEntry;
std::vector<QScxmlInvokableServiceFactory *> m_cachedFactories;
enum { Invalid = 0, Starting, Running, Paused, Finished } m_runningState = Invalid;
@@ -378,6 +391,7 @@ private:
}
bool isPaused() const { return m_runningState == Paused; }
+ bool isFinished() const { return m_runningState == Finished; }
QScxmlInternal::StateMachineInfoProxy *m_infoSignalProxy;
diff --git a/src/scxml/qscxmlstatemachineinfo.cpp b/src/scxml/qscxmlstatemachineinfo.cpp
index 4fbbecf..b485f64 100644
--- a/src/scxml/qscxmlstatemachineinfo.cpp
+++ b/src/scxml/qscxmlstatemachineinfo.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmlstatemachineinfo_p.h"
#include "qscxmlstatemachine_p.h"
@@ -71,22 +35,22 @@ QScxmlStateMachine *QScxmlStateMachineInfo::stateMachine() const
return d->stateMachine();
}
-QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::allStates() const
+QList<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::allStates() const
{
Q_D(const QScxmlStateMachineInfo);
- QVector<QScxmlStateMachineInfo::StateId> all;
+ QList<QScxmlStateMachineInfo::StateId> all;
for (int i = 0, ei = d->stateTable()->stateCount; i < ei; ++i) {
all.append(i);
}
return all;
}
-QVector<QScxmlStateMachineInfo::TransitionId> QScxmlStateMachineInfo::allTransitions() const
+QList<QScxmlStateMachineInfo::TransitionId> QScxmlStateMachineInfo::allTransitions() const
{
Q_D(const QScxmlStateMachineInfo);
- QVector<QScxmlStateMachineInfo::TransitionId> all;
+ QList<QScxmlStateMachineInfo::TransitionId> all;
for (int i = 0, ei = d->stateTable()->transitionCount; i < ei; ++i) {
all.append(i);
}
@@ -136,7 +100,7 @@ QScxmlStateMachineInfo::StateType QScxmlStateMachineInfo::stateType(StateId stat
}
}
-QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::stateChildren(StateId stateId) const
+QList<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::stateChildren(StateId stateId) const
{
Q_D(const QScxmlStateMachineInfo);
@@ -146,7 +110,7 @@ QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::stateChildren(S
if (stateId >= 0 && stateId < d->stateTable()->stateCount)
childStates = d->stateTable()->state(stateId).childStates;
- QVector<QScxmlStateMachineInfo::StateId> all;
+ QList<QScxmlStateMachineInfo::StateId> all;
if (childStates == QScxmlExecutableContent::StateTable::InvalidIndex)
return all;
@@ -199,11 +163,11 @@ QScxmlStateMachineInfo::StateId QScxmlStateMachineInfo::transitionSource(Transit
return transition.source;
}
-QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::transitionTargets(TransitionId transitionId) const
+QList<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::transitionTargets(TransitionId transitionId) const
{
Q_D(const QScxmlStateMachineInfo);
- QVector<QScxmlStateMachineInfo::StateId> targets;
+ QList<QScxmlStateMachineInfo::StateId> targets;
if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
return targets;
@@ -218,11 +182,11 @@ QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::transitionTarge
return targets;
}
-QVector<QString> QScxmlStateMachineInfo::transitionEvents(TransitionId transitionId) const
+QList<QString> QScxmlStateMachineInfo::transitionEvents(TransitionId transitionId) const
{
Q_D(const QScxmlStateMachineInfo);
- QVector<QString> events;
+ QList<QString> events;
if (transitionId < 0 || transitionId >= d->stateTable()->transitionCount)
return events;
@@ -239,11 +203,11 @@ QVector<QString> QScxmlStateMachineInfo::transitionEvents(TransitionId transitio
return events;
}
-QVector<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::configuration() const
+QList<QScxmlStateMachineInfo::StateId> QScxmlStateMachineInfo::configuration() const
{
Q_D(const QScxmlStateMachineInfo);
const auto &list = d->stateMachinePrivate()->configuration().list();
- return QVector<StateId>(list.cbegin(), list.cend());
+ return QList<StateId>(list.cbegin(), list.cend());
}
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachineinfo_p.h b/src/scxml/qscxmlstatemachineinfo_p.h
index 4fa0ca0..8b746a5 100644
--- a/src/scxml/qscxmlstatemachineinfo_p.h
+++ b/src/scxml/qscxmlstatemachineinfo_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLSTATEMACHINEINFO_H
#define QSCXMLSTATEMACHINEINFO_H
@@ -53,6 +17,7 @@
#include <QtScxml/qscxmlglobals.h>
#include <QtCore/qobject.h>
+#include <QtCore/private/qglobal_p.h>
QT_BEGIN_NAMESPACE
@@ -91,23 +56,23 @@ public: // methods
QScxmlStateMachine *stateMachine() const;
- QVector<StateId> allStates() const;
- QVector<TransitionId> allTransitions() const;
+ QList<StateId> allStates() const;
+ QList<TransitionId> allTransitions() const;
QString stateName(int stateId) const;
StateId stateParent(StateId stateId) const;
StateType stateType(int stateId) const;
- QVector<StateId> stateChildren(StateId stateId) const;
+ QList<StateId> stateChildren(StateId stateId) const;
TransitionId initialTransition(StateId stateId) const;
TransitionType transitionType(TransitionId transitionId) const;
StateId transitionSource(TransitionId transitionId) const;
- QVector<StateId> transitionTargets(TransitionId transitionId) const;
- QVector<QString> transitionEvents(TransitionId transitionId) const;
- QVector<StateId> configuration() const;
+ QList<StateId> transitionTargets(TransitionId transitionId) const;
+ QList<QString> transitionEvents(TransitionId transitionId) const;
+ QList<StateId> configuration() const;
Q_SIGNALS:
- void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states);
- void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states);
- void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions);
+ void statesEntered(const QList<QScxmlStateMachineInfo::StateId> &states);
+ void statesExited(const QList<QScxmlStateMachineInfo::StateId> &states);
+ void transitionsTriggered(const QList<QScxmlStateMachineInfo::TransitionId> &transitions);
private:
Q_DECLARE_PRIVATE(QScxmlStateMachineInfo)
diff --git a/src/scxml/qscxmltabledata.cpp b/src/scxml/qscxmltabledata.cpp
index ab63696..81de319 100644
--- a/src/scxml/qscxmltabledata.cpp
+++ b/src/scxml/qscxmltabledata.cpp
@@ -1,46 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 "qscxmltabledata_p.h"
#include "qscxmlcompiler_p.h"
#include "qscxmlexecutablecontent_p.h"
+#include <QtCore/qmap.h>
+
QT_USE_NAMESPACE
/*!
@@ -146,13 +112,13 @@ public:
m_parents.reserve(32);
m_allTransitions.resize(doc->allTransitions.size());
m_docTransitionIndices.reserve(doc->allTransitions.size());
- for (auto *t : qAsConst(doc->allTransitions)) {
+ for (auto *t : std::as_const(doc->allTransitions)) {
m_docTransitionIndices.insert(t, m_docTransitionIndices.size());
}
m_docStatesIndices.reserve(doc->allStates.size());
m_transitionsForState.resize(doc->allStates.size());
m_allStates.resize(doc->allStates.size());
- for (DocumentModel::AbstractState *s : qAsConst(doc->allStates)) {
+ for (DocumentModel::AbstractState *s : std::as_const(doc->allStates)) {
m_docStatesIndices.insert(s, m_docStatesIndices.size());
}
@@ -183,7 +149,7 @@ public:
+ (m_allTransitions.size() * transitionSize)
+ m_arrays.size()
+ 1;
- QVector<qint32> data(dataSize, -1);
+ QList<qint32> data(dataSize, -1);
qint32 *ptr = data.data();
memcpy(ptr, &m_stateTable, sizeof(m_stateTable));
@@ -260,8 +226,8 @@ protected: // visitor
endSequence();
}
- QVector<DocumentModel::AbstractState *> childStates;
- for (DocumentModel::StateOrTransition *sot : qAsConst(node->children)) {
+ QList<DocumentModel::AbstractState *> childStates;
+ for (DocumentModel::StateOrTransition *sot : std::as_const(node->children)) {
if (DocumentModel::AbstractState *s = sot->asAbstractState()) {
childStates.append(s);
}
@@ -317,14 +283,14 @@ protected: // visitor
newState.entryInstructions = generate(state->onEntry);
newState.exitInstructions = generate(state->onExit);
if (!state->invokes.isEmpty()) {
- QVector<int> factoryIds;
- for (DocumentModel::Invoke *invoke : qAsConst(state->invokes)) {
+ QList<int> factoryIds;
+ for (DocumentModel::Invoke *invoke : std::as_const(state->invokes)) {
auto ctxt = createContext(QStringLiteral("invoke"));
- QVector<QScxmlExecutableContent::StringId> namelist;
- for (const QString &name : qAsConst(invoke->namelist))
+ QList<QScxmlExecutableContent::StringId> namelist;
+ for (const QString &name : std::as_const(invoke->namelist))
namelist += addString(name);
- QVector<QScxmlExecutableContent::ParameterInfo> params;
- for (DocumentModel::Param *param : qAsConst(invoke->params)) {
+ QList<QScxmlExecutableContent::ParameterInfo> params;
+ for (DocumentModel::Param *param : std::as_const(invoke->params)) {
QScxmlExecutableContent::ParameterInfo p;
p.name = addString(param->name);
p.expr = createEvaluatorVariant(QStringLiteral("param"), QStringLiteral("expr"),
@@ -361,8 +327,8 @@ protected: // visitor
visit(state->children);
- QVector<DocumentModel::AbstractState *> childStates;
- for (DocumentModel::StateOrTransition *sot : qAsConst(state->children)) {
+ QList<DocumentModel::AbstractState *> childStates;
+ for (DocumentModel::StateOrTransition *sot : std::as_const(state->children)) {
if (auto s = sot->asAbstractState()) {
childStates.append(s);
}
@@ -419,8 +385,8 @@ protected: // visitor
newTransition.targets = addStates(transition->targetStates);
- QVector<int> eventIds;
- for (const QString &event : qAsConst(transition->events))
+ QList<int> eventIds;
+ for (const QString &event : std::as_const(transition->events))
eventIds.push_back(addString(event));
newTransition.events = addArray(eventIds);
@@ -582,7 +548,7 @@ protected:
return addString(createContextString(instrName));
}
- void generate(const QVector<DocumentModel::DataElement *> &dataElements)
+ void generate(const QList<DocumentModel::DataElement *> &dataElements)
{
for (DocumentModel::DataElement *el : dataElements) {
auto ctxt = createContext(QStringLiteral("data"), QStringLiteral("expr"), el->expr);
@@ -605,7 +571,7 @@ protected:
return id;
}
- void generate(Array<ParameterInfo> *out, const QVector<DocumentModel::Param *> &in)
+ void generate(Array<ParameterInfo> *out, const QList<DocumentModel::Param *> &in)
{
out->count = in.size();
ParameterInfo *it = out->data();
@@ -742,7 +708,7 @@ protected:
return NoEvaluator;
}
- GeneratedTableData *tableData(const QVector<int> &stateMachineTable);
+ GeneratedTableData *tableData(const QList<int> &stateMachineTable);
StringId addString(const QString &str)
{ return str.isEmpty() ? NoString : m_stringTable.add(str); }
@@ -756,9 +722,9 @@ protected:
bool isCppDataModel() const
{ return m_isCppDataModel; }
- int addStates(const QVector<DocumentModel::AbstractState *> &states)
+ int addStates(const QList<DocumentModel::AbstractState *> &states)
{
- QVector<int> array;
+ QList<int> array;
for (auto *s : states) {
int si = m_docStatesIndices.value(s, -1);
Q_ASSERT(si != -1);
@@ -768,7 +734,7 @@ protected:
return addArray(array);
}
- int addArray(const QVector<int> &array)
+ int addArray(const QList<int> &array)
{
if (array.isEmpty())
return -1;
@@ -894,7 +860,7 @@ private:
class InstructionStorage {
public:
- InstructionStorage(QVector<qint32> &storage)
+ InstructionStorage(QList<qint32> &storage)
: m_instr(storage)
, m_info(nullptr)
{}
@@ -932,36 +898,36 @@ private:
}
private:
- QVector<qint32> &m_instr;
+ QList<qint32> &m_instr;
SequenceInfo *m_info;
};
- QVector<SequenceInfo> m_activeSequences;
+ QList<SequenceInfo> m_activeSequences;
GeneratedTableData::CreateFactoryId createFactoryId;
GeneratedTableData &m_tableData;
GeneratedTableData::DataModelInfo &m_dataModelInfo;
Table<QStringList, QString, StringId> m_stringTable;
InstructionStorage m_instructions;
- Table<QVector<EvaluatorInfo>, EvaluatorInfo, EvaluatorId> m_evaluators;
- Table<QVector<AssignmentInfo>, AssignmentInfo, EvaluatorId> m_assignments;
- Table<QVector<ForeachInfo>, ForeachInfo, EvaluatorId> m_foreaches;
- QVector<StringId> &m_dataIds;
+ Table<QList<EvaluatorInfo>, EvaluatorInfo, EvaluatorId> m_evaluators;
+ Table<QList<AssignmentInfo>, AssignmentInfo, EvaluatorId> m_assignments;
+ Table<QList<ForeachInfo>, ForeachInfo, EvaluatorId> m_foreaches;
+ QList<StringId> &m_dataIds;
bool m_isCppDataModel = false;
StateTable m_stateTable;
- QVector<int> m_parents;
- QVector<qint32> m_arrays;
+ QList<int> m_parents;
+ QList<qint32> m_arrays;
- QVector<StateTable::Transition> m_allTransitions;
+ QList<StateTable::Transition> m_allTransitions;
QHash<DocumentModel::Transition *, int> m_docTransitionIndices;
- QVector<StateTable::State> m_allStates;
+ QList<StateTable::State> m_allStates;
QHash<DocumentModel::AbstractState *, int> m_docStatesIndices;
- QVector<QVector<int>> m_transitionsForState;
+ QList<QList<int>> m_transitionsForState;
int m_currentTransition = StateTable::InvalidIndex;
bool m_bindLate = false;
- QVector<DocumentModel::DataElement *> m_dataElements;
+ QList<DocumentModel::DataElement *> m_dataElements;
Table<QStringList, QString, int> m_stateNames;
};
diff --git a/src/scxml/qscxmltabledata.h b/src/scxml/qscxmltabledata.h
index 5c0d345..ca56627 100644
--- a/src/scxml/qscxmltabledata.h
+++ b/src/scxml/qscxmltabledata.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLTABLEDATA_H
#define QSCXMLTABLEDATA_H
@@ -44,7 +8,7 @@
#include <QtCore/qstring.h>
#ifndef Q_QSCXMLC_OUTPUT_REVISION
-#define Q_QSCXMLC_OUTPUT_REVISION 1
+#define Q_QSCXMLC_OUTPUT_REVISION 2
#endif
QT_BEGIN_NAMESPACE
diff --git a/src/scxml/qscxmltabledata_p.h b/src/scxml/qscxmltabledata_p.h
index 47d8bac..6f147b7 100644
--- a/src/scxml/qscxmltabledata_p.h
+++ b/src/scxml/qscxmltabledata_p.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// 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 QSCXMLTABLEDATA_P_H
#define QSCXMLTABLEDATA_P_H
@@ -52,9 +16,10 @@
//
#include <QtScxml/qscxmltabledata.h>
-#include <QtCore/qsharedpointer.h>
#include <QtCore/qhash.h>
-#include <QtCore/qvector.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qsharedpointer.h>
+#include <QtCore/private/qglobal_p.h>
#include <functional>
@@ -72,8 +37,8 @@ class Q_SCXML_EXPORT GeneratedTableData: public QScxmlTableData
public:
typedef std::function<
int(const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- const QVector<QScxmlExecutableContent::ParameterInfo> &params,
+ const QList<QScxmlExecutableContent::StringId> &namelist,
+ const QList<QScxmlExecutableContent::ParameterInfo> &params,
QSharedPointer<DocumentModel::ScxmlDocument> content)
> CreateFactoryId;
@@ -110,13 +75,13 @@ public:
QScxmlInvokableServiceFactory *serviceFactory(int id) const override;
public:
- QVector<qint32> theStateMachineTable;
+ QList<qint32> theStateMachineTable;
QStringList theStrings;
- QVector<qint32> theInstructions;
- QVector<QScxmlExecutableContent::EvaluatorInfo> theEvaluators;
- QVector<QScxmlExecutableContent::AssignmentInfo> theAssignments;
- QVector<QScxmlExecutableContent::ForeachInfo> theForeaches;
- QVector<QScxmlExecutableContent::StringId> theDataNameIds;
+ QList<qint32> theInstructions;
+ QList<QScxmlExecutableContent::EvaluatorInfo> theEvaluators;
+ QList<QScxmlExecutableContent::AssignmentInfo> theAssignments;
+ QList<QScxmlExecutableContent::ForeachInfo> theForeaches;
+ QList<QScxmlExecutableContent::StringId> theDataNameIds;
QScxmlExecutableContent::ContainerId theInitialSetup;
int theName;
};
diff --git a/src/scxml/qt_cmdline.cmake b/src/scxml/qt_cmdline.cmake
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/src/scxml/qt_cmdline.cmake
diff --git a/src/scxml/scxml.pro b/src/scxml/scxml.pro
deleted file mode 100644
index fe55769..0000000
--- a/src/scxml/scxml.pro
+++ /dev/null
@@ -1,73 +0,0 @@
-TARGET = QtScxml
-QT = core-private
-MODULE_CONFIG += c++11 qscxmlc
-
-QMAKE_DOCS = $$PWD/doc/qtscxml.qdocconf
-
-CONFIG += $$MODULE_CONFIG
-DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
-
-HEADERS += \
- qscxmlcompiler.h \
- qscxmlcompiler_p.h \
- qscxmlstatemachine.h \
- qscxmlstatemachine_p.h \
- qscxmlglobals.h \
- qscxmlglobals_p.h \
- qscxmlnulldatamodel.h \
- qscxmlexecutablecontent.h \
- qscxmlexecutablecontent_p.h \
- qscxmlevent.h \
- qscxmlevent_p.h \
- qscxmldatamodel.h \
- qscxmldatamodel_p.h \
- qscxmlcppdatamodel_p.h \
- qscxmlcppdatamodel.h \
- qscxmlerror.h \
- qscxmlinvokableservice_p.h \
- qscxmlinvokableservice.h \
- qscxmltabledata.h \
- qscxmltabledata_p.h \
- qscxmlstatemachineinfo_p.h
-
-SOURCES += \
- qscxmlcompiler.cpp \
- qscxmlstatemachine.cpp \
- qscxmlnulldatamodel.cpp \
- qscxmlexecutablecontent.cpp \
- qscxmlevent.cpp \
- qscxmldatamodel.cpp \
- qscxmlcppdatamodel.cpp \
- qscxmlerror.cpp \
- qscxmlinvokableservice.cpp \
- qscxmltabledata.cpp \
- qscxmlstatemachineinfo.cpp
-
-qtConfig(scxml-ecmascriptdatamodel) {
- QT += qml-private
-
- HEADERS += \
- qscxmlecmascriptdatamodel.h \
- qscxmlecmascriptplatformproperties_p.h
-
- SOURCES += \
- qscxmlecmascriptdatamodel.cpp \
- qscxmlecmascriptplatformproperties.cpp
-}
-
-load(qt_module)
-
-FEATURES += ../../mkspecs/features/qscxmlc.prf
-features.files = $$FEATURES
-features.path = $$[QT_HOST_DATA]/mkspecs/features/
-INSTALLS += features
-
-!force_independent:if(!debug_and_release|!build_all|CONFIG(release, debug|release)) {
- # Copy qscxmlc.prf to the qtbase build directory (for non-installed developer builds)
- prf2build.input = FEATURES
- prf2build.output = $$[QT_INSTALL_DATA/get]/mkspecs/features/${QMAKE_FILE_BASE}${QMAKE_FILE_EXT}
- prf2build.commands = $$QMAKE_COPY ${QMAKE_FILE_IN} ${QMAKE_FILE_OUT}
- prf2build.name = COPY ${QMAKE_FILE_IN}
- prf2build.CONFIG = no_link no_clean target_predeps
- QMAKE_EXTRA_COMPILERS += prf2build
-}
diff --git a/src/scxmlqml/CMakeLists.txt b/src/scxmlqml/CMakeLists.txt
new file mode 100644
index 0000000..67afb9c
--- /dev/null
+++ b/src/scxmlqml/CMakeLists.txt
@@ -0,0 +1,28 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_include_in_repo_target_set(qtscxmlqml)
+
+qt_internal_add_qml_module(ScxmlQml
+ URI "QtScxml"
+ VERSION "${PROJECT_VERSION}"
+ PLUGIN_TARGET declarative_scxml
+ CLASS_NAME QScxmlStateMachinePlugin
+ SOURCES
+ eventconnection.cpp eventconnection_p.h
+ invokedservices.cpp invokedservices_p.h
+ statemachineextended.cpp statemachineextended_p.h
+ statemachineloader.cpp statemachineloader_p.h
+ qscxmlqmlglobals_p.h
+ DEFINES
+ QT_BUILD_SCXMLQML_LIB
+ DEPENDENCIES
+ QtQml
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Qml
+ Qt::Scxml
+ LIBRARIES
+ Qt::CorePrivate
+ GENERATE_CPP_EXPORTS
+)
diff --git a/src/scxmlqml/eventconnection.cpp b/src/scxmlqml/eventconnection.cpp
new file mode 100644
index 0000000..c7d65b0
--- /dev/null
+++ b/src/scxmlqml/eventconnection.cpp
@@ -0,0 +1,117 @@
+// 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 "eventconnection_p.h"
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype EventConnection
+//! \instantiates QScxmlEventConnection
+ \inqmlmodule QtScxml
+ \since QtScxml 5.8
+
+ \brief Connects to events sent out by state machines.
+
+ To receive a notification when a state machine sends out an event, a
+ connection can be created to the corresponding signal.
+*/
+
+/*!
+ \qmlproperty stringlist EventConnection::events
+
+ The list of SCXML event specifiers that describe the events to listen for.
+
+ Even though spaces are allowed in event specifications in SCXML documents,
+ they are not allowed in this list. However, the list can contain multiple
+ specifiers, to the same effect.
+*/
+
+/*!
+ \qmlproperty ScxmlStateMachine EventConnection::stateMachine
+
+ The state machine that sends out the event.
+*/
+
+/*!
+ \qmlsignal EventConnection::occurred(event)
+
+ This signal is emitted when the SCXML event \a event occurs.
+
+ \sa QScxmlEvent
+*/
+
+
+QScxmlEventConnection::QScxmlEventConnection(QObject *parent) :
+ QObject(parent)
+{
+}
+
+QStringList QScxmlEventConnection::events() const
+{
+ return m_events;
+}
+
+void QScxmlEventConnection::setEvents(const QStringList &events)
+{
+ m_events.removeBindingUnlessInWrapper();
+ if (events == m_events.valueBypassingBindings()) {
+ return;
+ }
+ m_events.setValueBypassingBindings(events);
+ doConnect();
+ m_events.notify();
+}
+
+QBindable<QStringList> QScxmlEventConnection::bindableEvents()
+{
+ return &m_events;
+}
+
+QScxmlStateMachine *QScxmlEventConnection::stateMachine() const
+{
+ return m_stateMachine;
+}
+
+void QScxmlEventConnection::setStateMachine(QScxmlStateMachine *stateMachine)
+{
+ m_stateMachine.removeBindingUnlessInWrapper();
+ if (stateMachine == m_stateMachine.valueBypassingBindings())
+ return;
+ m_stateMachine.setValueBypassingBindings(stateMachine);
+ doConnect();
+ m_stateMachine.notify();
+}
+
+QBindable<QScxmlStateMachine*> QScxmlEventConnection::bindableStateMachine()
+{
+ return &m_stateMachine;
+}
+
+void QScxmlEventConnection::doConnect()
+{
+ for (const QMetaObject::Connection &connection : std::as_const(m_connections))
+ disconnect(connection);
+ m_connections.clear();
+ const auto stateMachine = m_stateMachine.valueBypassingBindings();
+ if (stateMachine) {
+ const auto events = m_events.valueBypassingBindings();
+ for (const QString &event : events) {
+ m_connections.append(stateMachine->connectToEvent(event, this,
+ &QScxmlEventConnection::occurred));
+ }
+ }
+}
+
+void QScxmlEventConnection::classBegin()
+{
+}
+
+void QScxmlEventConnection::componentComplete()
+{
+ auto parentStateMachine = qobject_cast<QScxmlStateMachine *>(parent());
+ if (!m_stateMachine.value() && parentStateMachine)
+ setStateMachine(parentStateMachine);
+}
+
+QT_END_NAMESPACE
diff --git a/src/scxmlqml/eventconnection_p.h b/src/scxmlqml/eventconnection_p.h
new file mode 100644
index 0000000..1dd36d3
--- /dev/null
+++ b/src/scxmlqml/eventconnection_p.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 EVENTCONNECTION_P_H
+#define EVENTCONNECTION_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 "qscxmlqmlglobals_p.h"
+
+#include <QtScxml/qscxmlstatemachine.h>
+#include <QtScxml/qscxmlevent.h>
+#include <QtCore/qobject.h>
+#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qqml.h>
+#include "QtCore/qproperty.h"
+#include <private/qproperty_p.h>
+
+QT_BEGIN_NAMESPACE
+
+// QScxmlEvent is used as signal parameter, and defined in the cpp lib
+struct Q_SCXMLQML_EXPORT QScxmlEventForeign
+{
+ Q_GADGET
+ QML_ANONYMOUS
+ QML_FOREIGN(QScxmlEvent)
+ QML_ADDED_IN_VERSION(5,8)
+};
+
+class Q_SCXMLQML_EXPORT QScxmlEventConnection : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QStringList events READ events WRITE setEvents NOTIFY eventsChanged
+ BINDABLE bindableEvents)
+ Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine WRITE setStateMachine
+ NOTIFY stateMachineChanged BINDABLE bindableStateMachine)
+ Q_INTERFACES(QQmlParserStatus)
+ QML_NAMED_ELEMENT(EventConnection)
+ QML_ADDED_IN_VERSION(5,8)
+
+public:
+ QScxmlEventConnection(QObject *parent = nullptr);
+
+ QStringList events() const;
+ void setEvents(const QStringList &events);
+ QBindable<QStringList> bindableEvents();
+
+ QScxmlStateMachine *stateMachine() const;
+ void setStateMachine(QScxmlStateMachine *stateMachine);
+ QBindable<QScxmlStateMachine*> bindableStateMachine();
+
+Q_SIGNALS:
+ void eventsChanged();
+ void stateMachineChanged();
+
+ void occurred(const QScxmlEvent &event);
+
+private:
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QScxmlEventConnection, QScxmlStateMachine*, m_stateMachine,
+ &QScxmlEventConnection::setStateMachine,
+ &QScxmlEventConnection::stateMachineChanged, nullptr);
+ Q_OBJECT_COMPAT_PROPERTY(QScxmlEventConnection, QStringList, m_events,
+ &QScxmlEventConnection::setEvents,
+ &QScxmlEventConnection::eventsChanged);
+
+ QList<QMetaObject::Connection> m_connections;
+
+ void doConnect();
+ void classBegin() override;
+ void componentComplete() override;
+};
+
+QT_END_NAMESPACE
+
+#endif // EVENTCONNECTION_P_H
diff --git a/src/scxmlqml/invokedservices.cpp b/src/scxmlqml/invokedservices.cpp
new file mode 100644
index 0000000..27b939b
--- /dev/null
+++ b/src/scxmlqml/invokedservices.cpp
@@ -0,0 +1,114 @@
+// 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 "invokedservices_p.h"
+#include <QtScxml/qscxmlinvokableservice.h>
+
+QT_BEGIN_NAMESPACE
+
+/*!
+ \qmltype InvokedServices
+//! \instantiates QScxmlInvokedServices
+ \inqmlmodule QtScxml
+ \since QtScxml 5.8
+
+ \brief Provices access to the services invoked by state machines.
+
+ Makes the invoked services easily accessible by their names, without
+ constantly iterating through QScxmlStateMachine::invokedServices.
+
+ The services are called from state machines via the mechanism described in
+ \l{SCXML Specification - 6.4 <invoke>}.
+*/
+
+QScxmlInvokedServices::QScxmlInvokedServices(QObject *parent) : QObject(parent)
+{
+}
+
+/*!
+ \qmlproperty var InvokedServices::children
+
+ The services invoked by the state machine.
+*/
+
+QVariantMap QScxmlInvokedServices::children()
+{
+ return m_children.value();
+}
+
+QVariantMap QScxmlInvokedServices::childrenActualCalculation() const
+{
+ QVariantMap ret;
+ if (m_stateMachine.value()) {
+ const QList<QScxmlInvokableService *> children = m_stateMachine->invokedServices();
+ for (QScxmlInvokableService *service : children)
+ ret.insert(service->name(), QVariant::fromValue(service));
+ }
+ return ret;
+}
+
+QBindable<QVariantMap> QScxmlInvokedServices::bindableChildren()
+{
+ return &m_children;
+}
+
+void QScxmlInvokedServices::classBegin()
+{
+}
+
+/*!
+ \qmlproperty ScxmlStateMachine InvokedServices::stateMachine
+
+ The state machine that invoked the services.
+*/
+
+QScxmlStateMachine *QScxmlInvokedServices::stateMachine() const
+{
+ return m_stateMachine;
+}
+
+void QScxmlInvokedServices::setStateMachine(QScxmlStateMachine *stateMachine)
+{
+ m_stateMachine.removeBindingUnlessInWrapper();
+ if (stateMachine == m_stateMachine.valueBypassingBindings())
+ return;
+
+ QObject::disconnect(m_serviceConnection);
+ m_stateMachine.setValueBypassingBindings(stateMachine);
+
+ if (stateMachine) {
+ m_serviceConnection =
+ QObject::connect(stateMachine,
+ &QScxmlStateMachine::invokedServicesChanged, this, [this]() {
+ m_children.notify();
+ emit childrenChanged();
+ });
+ }
+ m_stateMachine.notify();
+ m_children.notify();
+ emit childrenChanged();
+}
+
+QBindable<QScxmlStateMachine*> QScxmlInvokedServices::bindableStateMachine()
+{
+ return &m_stateMachine;
+}
+
+/*!
+ \qmlproperty list<QtObject> InvokedServices::qmlChildren
+
+ A list of additional QtObject types nested in this type.
+*/
+
+QQmlListProperty<QObject> QScxmlInvokedServices::qmlChildren()
+{
+ return QQmlListProperty<QObject>(this, &m_qmlChildren);
+}
+
+void QScxmlInvokedServices::componentComplete()
+{
+ if (!m_stateMachine.value())
+ setStateMachine(qobject_cast<QScxmlStateMachine *>(parent()));
+}
+
+QT_END_NAMESPACE
diff --git a/src/scxmlqml/invokedservices_p.h b/src/scxmlqml/invokedservices_p.h
new file mode 100644
index 0000000..2b5a728
--- /dev/null
+++ b/src/scxmlqml/invokedservices_p.h
@@ -0,0 +1,73 @@
+// 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 INVOKEDSERVICES_P_H
+#define INVOKEDSERVICES_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 "qscxmlqmlglobals_p.h"
+
+#include <QtQml/qqmlparserstatus.h>
+#include <QtQml/qqmllist.h>
+#include <QtQml/qqml.h>
+#include <QtScxml/qscxmlstatemachine.h>
+#include <QtCore/qproperty.h>
+#include <private/qproperty_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_SCXMLQML_EXPORT QScxmlInvokedServices : public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine WRITE setStateMachine
+ NOTIFY stateMachineChanged BINDABLE bindableStateMachine)
+ Q_PROPERTY(QVariantMap children READ children NOTIFY childrenChanged BINDABLE bindableChildren)
+ Q_PROPERTY(QQmlListProperty<QObject> qmlChildren READ qmlChildren)
+ Q_INTERFACES(QQmlParserStatus)
+ Q_CLASSINFO("DefaultProperty", "qmlChildren")
+ QML_NAMED_ELEMENT(InvokedServices)
+ QML_ADDED_IN_VERSION(5,8)
+
+public:
+ QScxmlInvokedServices(QObject *parent = nullptr);
+
+ QVariantMap children();
+ QBindable<QVariantMap> bindableChildren();
+
+ QScxmlStateMachine *stateMachine() const;
+ void setStateMachine(QScxmlStateMachine *stateMachine);
+ QBindable<QScxmlStateMachine*> bindableStateMachine();
+
+ QQmlListProperty<QObject> qmlChildren();
+
+Q_SIGNALS:
+ void childrenChanged();
+ void stateMachineChanged();
+
+private:
+ void classBegin() override;
+ void componentComplete() override;
+ QVariantMap childrenActualCalculation() const;
+
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QScxmlInvokedServices, QScxmlStateMachine*, m_stateMachine,
+ &QScxmlInvokedServices::setStateMachine,
+ &QScxmlInvokedServices::stateMachineChanged, nullptr);
+ Q_OBJECT_COMPUTED_PROPERTY(QScxmlInvokedServices, QVariantMap,
+ m_children, &QScxmlInvokedServices::childrenActualCalculation);
+ QMetaObject::Connection m_serviceConnection;
+ QList<QObject *> m_qmlChildren;
+};
+
+QT_END_NAMESPACE
+
+#endif // INVOKEDSERVICES_P_H
diff --git a/src/scxmlqml/qscxmlqmlglobals_p.h b/src/scxmlqml/qscxmlqmlglobals_p.h
new file mode 100644
index 0000000..b2b07fe
--- /dev/null
+++ b/src/scxmlqml/qscxmlqmlglobals_p.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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 QSCXMLQMLGLOBALS_P_H
+#define QSCXMLQMLGLOBALS_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/qglobal.h>
+#include <QtScxmlQml/qtscxmlqmlexports.h>
+
+QT_BEGIN_NAMESPACE
+
+void Q_SCXMLQML_EXPORT qml_register_types_QtScxml();
+
+QT_END_NAMESPACE
+
+#endif // QSCXMLQMLGLOBALS_P_H
diff --git a/src/scxmlqml/statemachineextended.cpp b/src/scxmlqml/statemachineextended.cpp
new file mode 100644
index 0000000..7a7f990
--- /dev/null
+++ b/src/scxmlqml/statemachineextended.cpp
@@ -0,0 +1,21 @@
+// 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 "statemachineextended_p.h"
+
+#include <QtScxml/qscxmlglobals.h>
+#include <QtScxml/qscxmlstatemachine.h>
+
+QT_BEGIN_NAMESPACE
+
+QScxmlStateMachineExtended::QScxmlStateMachineExtended(QObject *extendee) :
+ QObject(extendee)
+{
+}
+
+QQmlListProperty<QObject> QScxmlStateMachineExtended::children()
+{
+ return QQmlListProperty<QObject>(this, &m_children);
+}
+
+QT_END_NAMESPACE
diff --git a/src/scxmlqml/statemachineextended_p.h b/src/scxmlqml/statemachineextended_p.h
new file mode 100644
index 0000000..4ca68f0
--- /dev/null
+++ b/src/scxmlqml/statemachineextended_p.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 STATEMACHINEEXTENDED_P_H
+#define STATEMACHINEEXTENDED_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 "qscxmlqmlglobals_p.h"
+
+#include <QtScxml/qscxmlstatemachine.h>
+#include <QtCore/qobject.h>
+#include <QtQml/qqmllist.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+/* Allow State Machines created from QML to have children. */
+class Q_SCXMLQML_EXPORT QScxmlStateMachineExtended : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<QObject> children READ children)
+ Q_CLASSINFO("DefaultProperty", "children")
+public:
+ QScxmlStateMachineExtended(QObject *extendee);
+ QQmlListProperty<QObject> children();
+
+private:
+ QObjectList m_children;
+};
+
+// The QScxmlStateMachine is defined in the scxml library
+struct Q_SCXMLQML_EXPORT QScxmlStateMachineForeign
+{
+ Q_GADGET
+ QML_UNCREATABLE("Only created through derived types")
+ QML_NAMED_ELEMENT(StateMachine)
+ QML_FOREIGN(QScxmlStateMachine)
+ QML_EXTENDED(QScxmlStateMachineExtended)
+ QML_ADDED_IN_VERSION(5,8)
+};
+
+QT_END_NAMESPACE
+
+#endif // STATEMACHINEEXTENDED_P_H
diff --git a/src/scxmlqml/statemachineloader.cpp b/src/scxmlqml/statemachineloader.cpp
new file mode 100644
index 0000000..33e3172
--- /dev/null
+++ b/src/scxmlqml/statemachineloader.cpp
@@ -0,0 +1,198 @@
+// 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 "statemachineloader_p.h"
+
+#include <QtScxml/qscxmlstatemachine.h>
+#include <qqmlcontext.h>
+#include <qqmlengine.h>
+#include <qqmlinfo.h>
+#include <qqmlfile.h>
+#include <qbuffer.h>
+
+/*!
+ \qmltype StateMachineLoader
+//! \instantiates QScxmlStateMachineLoader
+ \inqmlmodule QtScxml
+
+ \brief Dynamically loads an SCXML document and instantiates the state machine.
+
+ \since QtScxml 5.7
+ */
+
+QScxmlStateMachineLoader::QScxmlStateMachineLoader(QObject *parent)
+ : QObject(parent)
+ , m_implicitDataModel(nullptr)
+{
+}
+
+/*!
+ \qmlproperty ScxmlStateMachine StateMachineLoader::stateMachine
+
+ The state machine instance.
+ */
+QT_PREPEND_NAMESPACE(QScxmlStateMachine) *QScxmlStateMachineLoader::stateMachine() const
+{
+ return m_stateMachine;
+}
+
+void QScxmlStateMachineLoader::setStateMachine(QScxmlStateMachine* stateMachine)
+{
+ if (m_stateMachine.valueBypassingBindings() != stateMachine) {
+ delete m_stateMachine.valueBypassingBindings();
+ m_stateMachine.setValueBypassingBindings(stateMachine);
+ }
+}
+
+QBindable<QScxmlStateMachine*> QScxmlStateMachineLoader::bindableStateMachine()
+{
+ return &m_stateMachine;
+}
+
+/*!
+ \qmlproperty url StateMachineLoader::source
+
+ The URL of the SCXML document to load. Only synchronously accessible URLs
+ are supported.
+ */
+QUrl QScxmlStateMachineLoader::source()
+{
+ return m_source;
+}
+
+void QScxmlStateMachineLoader::setSource(const QUrl &source)
+{
+ if (!source.isValid())
+ return;
+
+ m_source.removeBindingUnlessInWrapper();
+
+ const QUrl oldSource = m_source.valueBypassingBindings();
+ const auto *oldStateMachine = m_stateMachine.valueBypassingBindings();
+ setStateMachine(nullptr);
+ m_implicitDataModel = nullptr;
+
+ if (parse(source))
+ m_source.setValueBypassingBindings(source);
+ else
+ m_source.setValueBypassingBindings(QUrl());
+
+ if (oldSource != m_source.valueBypassingBindings())
+ m_source.notify();
+
+ if (oldStateMachine != m_stateMachine.valueBypassingBindings())
+ m_stateMachine.notify();
+}
+
+QBindable<QUrl> QScxmlStateMachineLoader::bindableSource()
+{
+ return &m_source;
+}
+
+QVariantMap QScxmlStateMachineLoader::initialValues() const
+{
+ return m_initialValues;
+}
+
+void QScxmlStateMachineLoader::setInitialValues(const QVariantMap &initialValues)
+{
+ m_initialValues.removeBindingUnlessInWrapper();
+ if (initialValues == m_initialValues.valueBypassingBindings())
+ return;
+
+ m_initialValues.setValueBypassingBindings(initialValues);
+
+ const auto stateMachine = m_stateMachine.valueBypassingBindings();
+ if (stateMachine)
+ stateMachine->setInitialValues(initialValues);
+ m_initialValues.notify();
+}
+
+QBindable<QVariantMap> QScxmlStateMachineLoader::bindableInitialValues()
+{
+ return &m_initialValues;
+}
+
+QScxmlDataModel *QScxmlStateMachineLoader::dataModel() const
+{
+ return m_dataModel;
+}
+
+void QScxmlStateMachineLoader::setDataModel(QScxmlDataModel *dataModel)
+{
+ m_dataModel.removeBindingUnlessInWrapper();
+ if (dataModel == m_dataModel.valueBypassingBindings()) {
+ return;
+ }
+ m_dataModel.setValueBypassingBindings(dataModel);
+ const auto stateMachine = m_stateMachine.valueBypassingBindings();
+ if (stateMachine) {
+ if (dataModel)
+ stateMachine->setDataModel(dataModel);
+ else
+ stateMachine->setDataModel(m_implicitDataModel);
+ }
+ m_dataModel.notify();
+}
+
+QBindable<QScxmlDataModel*> QScxmlStateMachineLoader::bindableDataModel()
+{
+ return &m_dataModel;
+}
+
+bool QScxmlStateMachineLoader::parse(const QUrl &source)
+{
+ if (!QQmlFile::isSynchronous(source)) {
+ qmlWarning(this) << QStringLiteral("Cannot open '%1' for reading: only synchronous access is supported.")
+ .arg(source.url());
+ return false;
+ }
+ QQmlFile scxmlFile(QQmlEngine::contextForObject(this)->engine(), source);
+ if (scxmlFile.isError()) {
+ // the synchronous case can only fail when the file is not found (or not readable).
+ qmlWarning(this) << QStringLiteral("Cannot open '%1' for reading.").arg(source.url());
+ return false;
+ }
+
+ QByteArray data(scxmlFile.dataByteArray());
+ QBuffer buf(&data);
+ if (!buf.open(QIODevice::ReadOnly)) {
+ qmlWarning(this) << QStringLiteral("Cannot open input buffer for reading");
+ return false;
+ }
+
+ QString fileName;
+ if (source.isLocalFile()) {
+ fileName = source.toLocalFile();
+ } else if (source.scheme() == QStringLiteral("qrc")) {
+ fileName = QStringLiteral(":") + source.path();
+ } else {
+ qmlWarning(this) << QStringLiteral("%1 is neither a local nor a resource URL.")
+ .arg(source.url())
+ << QStringLiteral("Invoking services by relative path will not work.");
+ }
+
+ auto stateMachine = QScxmlStateMachine::fromData(&buf, fileName);
+ stateMachine->setParent(this);
+ m_implicitDataModel = stateMachine->dataModel();
+
+ if (stateMachine->parseErrors().isEmpty()) {
+ if (m_dataModel.value())
+ stateMachine->setDataModel(m_dataModel.value());
+ stateMachine->setInitialValues(m_initialValues.value());
+ setStateMachine(stateMachine);
+ // as this is deferred any pending property updates to m_dataModel and m_initialValues
+ // should still occur before start().
+ QMetaObject::invokeMethod(m_stateMachine.valueBypassingBindings(), "start", Qt::QueuedConnection);
+ return true;
+ } else {
+ qmlWarning(this) << QStringLiteral("Something went wrong while parsing '%1':")
+ .arg(source.url())
+ << Qt::endl;
+ const auto errors = stateMachine->parseErrors();
+ for (const QScxmlError &error : errors) {
+ qmlWarning(this) << error.toString();
+ }
+ return false;
+ }
+}
diff --git a/src/scxmlqml/statemachineloader_p.h b/src/scxmlqml/statemachineloader_p.h
new file mode 100644
index 0000000..46a228c
--- /dev/null
+++ b/src/scxmlqml/statemachineloader_p.h
@@ -0,0 +1,87 @@
+// 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 STATEMACHINELOADER_P_H
+#define STATEMACHINELOADER_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 "qscxmlqmlglobals_p.h"
+
+#include <QtCore/qurl.h>
+#include <QtScxml/qscxmlstatemachine.h>
+
+#include <QtCore/private/qproperty_p.h>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_SCXMLQML_EXPORT QScxmlStateMachineLoader: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QUrl source READ source WRITE setSource
+ NOTIFY sourceChanged BINDABLE bindableSource)
+ Q_PROPERTY(QScxmlStateMachine *stateMachine READ stateMachine DESIGNABLE false
+ NOTIFY stateMachineChanged BINDABLE bindableStateMachine)
+ Q_PROPERTY(QVariantMap initialValues READ initialValues WRITE setInitialValues
+ NOTIFY initialValuesChanged BINDABLE bindableInitialValues)
+ Q_PROPERTY(QScxmlDataModel *dataModel READ dataModel
+ WRITE setDataModel NOTIFY dataModelChanged BINDABLE bindableDataModel)
+ QML_NAMED_ELEMENT(StateMachineLoader)
+ QML_ADDED_IN_VERSION(5,8)
+
+public:
+ explicit QScxmlStateMachineLoader(QObject *parent = nullptr);
+
+ QScxmlStateMachine *stateMachine() const;
+ QBindable<QScxmlStateMachine*> bindableStateMachine();
+
+ QUrl source();
+ void setSource(const QUrl &source);
+ QBindable<QUrl> bindableSource();
+
+ QVariantMap initialValues() const;
+ void setInitialValues(const QVariantMap &initialValues);
+ QBindable<QVariantMap> bindableInitialValues();
+
+ QScxmlDataModel *dataModel() const;
+ void setDataModel(QScxmlDataModel *dataModel);
+ QBindable<QScxmlDataModel*> bindableDataModel();
+
+Q_SIGNALS:
+ void sourceChanged();
+ void initialValuesChanged();
+ void stateMachineChanged();
+ void dataModelChanged();
+
+private:
+ bool parse(const QUrl &source);
+ void setStateMachine(QScxmlStateMachine* stateMachine);
+
+private:
+ Q_OBJECT_COMPAT_PROPERTY(QScxmlStateMachineLoader, QUrl,
+ m_source, &QScxmlStateMachineLoader::setSource,
+ &QScxmlStateMachineLoader::sourceChanged);
+ Q_OBJECT_COMPAT_PROPERTY(QScxmlStateMachineLoader, QVariantMap,
+ m_initialValues, &QScxmlStateMachineLoader::setInitialValues,
+ &QScxmlStateMachineLoader::initialValuesChanged);
+ Q_OBJECT_COMPAT_PROPERTY_WITH_ARGS(QScxmlStateMachineLoader, QScxmlDataModel*,
+ m_dataModel, &QScxmlStateMachineLoader::setDataModel,
+ &QScxmlStateMachineLoader::dataModelChanged, nullptr);
+ Q_OBJECT_BINDABLE_PROPERTY_WITH_ARGS(QScxmlStateMachineLoader, QScxmlStateMachine*,
+ m_stateMachine, nullptr, &QScxmlStateMachineLoader::stateMachineChanged);
+ QScxmlDataModel *m_implicitDataModel;
+};
+
+QT_END_NAMESPACE
+
+#endif // STATEMACHINELOADER_P_H
diff --git a/src/src.pro b/src/src.pro
deleted file mode 100644
index e1073fb..0000000
--- a/src/src.pro
+++ /dev/null
@@ -1,8 +0,0 @@
-TEMPLATE = subdirs
-
-SUBDIRS += scxml
-
-qtHaveModule(qml) {
- SUBDIRS += imports
- imports.depends = scxml
-}
diff --git a/src/statemachine/CMakeLists.txt b/src/statemachine/CMakeLists.txt
new file mode 100644
index 0000000..6e1a716
--- /dev/null
+++ b/src/statemachine/CMakeLists.txt
@@ -0,0 +1,54 @@
+# 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
+ GENERATE_CPP_EXPORTS
+)
+
+## 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
diff --git a/src/statemachineqml/CMakeLists.txt b/src/statemachineqml/CMakeLists.txt
new file mode 100644
index 0000000..84fe127
--- /dev/null
+++ b/src/statemachineqml/CMakeLists.txt
@@ -0,0 +1,31 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+qt_internal_include_in_repo_target_set(qtstatemachineqml)
+
+qt_internal_add_qml_module(StateMachineQml
+ URI "QtQml.StateMachine"
+ VERSION "${PROJECT_VERSION}"
+ PLUGIN_TARGET qtqmlstatemachine
+ CLASS_NAME QtQmlStateMachinePlugin
+ SOURCES
+ qstatemachineqmlglobals_p.h
+ finalstate_p.h finalstate.cpp
+ signaltransition_p.h signaltransition.cpp
+ state_p.h state.cpp
+ statemachine_p.h statemachine.cpp
+ timeouttransition_p.h timeouttransition.cpp
+ childrenprivate_p.h
+ statemachineforeign_p.h
+ DEFINES
+ QT_BUILD_STATEMACHINEQML_LIB
+ DEPENDENCIES
+ QtQml
+ PUBLIC_LIBRARIES
+ Qt::Core
+ Qt::Qml
+ Qt::StateMachine
+ LIBRARIES
+ Qt::QmlPrivate
+ GENERATE_CPP_EXPORTS
+)
diff --git a/src/statemachineqml/childrenprivate_p.h b/src/statemachineqml/childrenprivate_p.h
new file mode 100644
index 0000000..c6de691
--- /dev/null
+++ b/src/statemachineqml/childrenprivate_p.h
@@ -0,0 +1,164 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLCHILDRENPRIVATE_H
+#define QQMLCHILDRENPRIVATE_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 <QAbstractState>
+#include <QAbstractTransition>
+#include <QStateMachine>
+#include <QQmlInfo>
+#include <QQmlListProperty>
+#include <private/qglobal_p.h>
+
+enum class ChildrenMode {
+ None = 0x0,
+ State = 0x1,
+ Transition = 0x2,
+ StateOrTransition = State | Transition
+};
+
+template<typename T>
+static T *parentObject(QQmlListProperty<QObject> *prop) { return static_cast<T *>(prop->object); }
+
+template<class T, ChildrenMode Mode>
+struct ParentHandler
+{
+ static bool unparentItem(QQmlListProperty<QObject> *prop, QObject *oldItem);
+ static bool parentItem(QQmlListProperty<QObject> *prop, QObject *item);
+};
+
+template<class T>
+struct ParentHandler<T, ChildrenMode::None>
+{
+ static bool unparentItem(QQmlListProperty<QObject> *, QObject *) { return true; }
+ static bool parentItem(QQmlListProperty<QObject> *, QObject *) { return true; }
+};
+
+template<class T>
+struct ParentHandler<T, ChildrenMode::State>
+{
+ static bool parentItem(QQmlListProperty<QObject> *prop, QObject *item)
+ {
+ if (QAbstractState *state = qobject_cast<QAbstractState *>(item)) {
+ state->setParent(parentObject<T>(prop));
+ return true;
+ }
+ return false;
+ }
+
+ static bool unparentItem(QQmlListProperty<QObject> *, QObject *oldItem)
+ {
+ if (QAbstractState *state = qobject_cast<QAbstractState *>(oldItem)) {
+ state->setParent(nullptr);
+ return true;
+ }
+ return false;
+ }
+};
+
+template<class T>
+struct ParentHandler<T, ChildrenMode::Transition>
+{
+ static bool parentItem(QQmlListProperty<QObject> *prop, QObject *item)
+ {
+ if (QAbstractTransition *trans = qobject_cast<QAbstractTransition *>(item)) {
+ parentObject<T>(prop)->addTransition(trans);
+ return true;
+ }
+ return false;
+ }
+
+ static bool unparentItem(QQmlListProperty<QObject> *prop, QObject *oldItem)
+ {
+ if (QAbstractTransition *trans = qobject_cast<QAbstractTransition *>(oldItem)) {
+ parentObject<T>(prop)->removeTransition(trans);
+ return true;
+ }
+ return false;
+ }
+};
+
+template<class T>
+struct ParentHandler<T, ChildrenMode::StateOrTransition>
+{
+ static bool parentItem(QQmlListProperty<QObject> *prop, QObject *oldItem)
+ {
+ return ParentHandler<T, ChildrenMode::State>::parentItem(prop, oldItem)
+ || ParentHandler<T, ChildrenMode::Transition>::parentItem(prop, oldItem);
+ }
+
+ static bool unparentItem(QQmlListProperty<QObject> *prop, QObject *oldItem)
+ {
+ return ParentHandler<T, ChildrenMode::State>::unparentItem(prop, oldItem)
+ || ParentHandler<T, ChildrenMode::Transition>::unparentItem(prop, oldItem);
+ }
+};
+
+template <class T, ChildrenMode Mode>
+class ChildrenPrivate
+{
+public:
+ static void append(QQmlListProperty<QObject> *prop, QObject *item)
+ {
+ Handler::parentItem(prop, item);
+ static_cast<Self *>(prop->data)->children.append(item);
+ parentObject<T>(prop)->childrenContentChanged();
+ }
+
+ static qsizetype count(QQmlListProperty<QObject> *prop)
+ {
+ return static_cast<Self *>(prop->data)->children.size();
+ }
+
+ static QObject *at(QQmlListProperty<QObject> *prop, qsizetype index)
+ {
+ return static_cast<Self *>(prop->data)->children.at(index);
+ }
+
+ static void clear(QQmlListProperty<QObject> *prop)
+ {
+ auto &children = static_cast<Self *>(prop->data)->children;
+ for (QObject *oldItem : std::as_const(children))
+ Handler::unparentItem(prop, oldItem);
+
+ children.clear();
+ parentObject<T>(prop)->childrenContentChanged();
+ }
+
+ static void replace(QQmlListProperty<QObject> *prop, qsizetype index, QObject *item)
+ {
+ auto &children = static_cast<Self *>(prop->data)->children;
+
+ Handler::unparentItem(prop, children.at(index));
+ Handler::parentItem(prop, item);
+
+ children.replace(index, item);
+ parentObject<T>(prop)->childrenContentChanged();
+ }
+
+ static void removeLast(QQmlListProperty<QObject> *prop)
+ {
+ Handler::unparentItem(prop, static_cast<Self *>(prop->data)->children.takeLast());
+ parentObject<T>(prop)->childrenContentChanged();
+ }
+
+private:
+ using Self = ChildrenPrivate<T, Mode>;
+ using Handler = ParentHandler<T, Mode>;
+
+ QList<QObject *> children;
+};
+
+#endif
diff --git a/src/statemachineqml/finalstate.cpp b/src/statemachineqml/finalstate.cpp
new file mode 100644
index 0000000..ffc8d88
--- /dev/null
+++ b/src/statemachineqml/finalstate.cpp
@@ -0,0 +1,68 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "finalstate_p.h"
+
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQmlInfo>
+
+FinalState::FinalState(QState *parent)
+ : QFinalState(parent)
+{
+}
+
+QQmlListProperty<QObject> FinalState::childrenActualCalculation() const
+{
+ // Mutating accesses to m_children only happen in the QML thread,
+ // so there are no thread-safety issues.
+ // The engine only creates non-const instances of the class anyway
+ return QQmlListProperty<QObject>(const_cast<FinalState*>(this), &m_children,
+ m_children.append, m_children.count, m_children.at,
+ m_children.clear, m_children.replace, m_children.removeLast);
+}
+
+QQmlListProperty<QObject> FinalState::children()
+{
+ return m_childrenComputedProperty;
+}
+
+void FinalState::childrenContentChanged()
+{
+ m_childrenComputedProperty.notify();
+ emit childrenChanged();
+}
+
+QBindable<QQmlListProperty<QObject>> FinalState::bindableChildren() const
+{
+ return &m_childrenComputedProperty;
+}
+
+/*!
+ \qmltype FinalState
+ \inqmlmodule QtQml.StateMachine
+ \inherits QAbstractState
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief Provides a final state.
+
+
+ A final state is used to communicate that (part of) a StateMachine has
+ finished its work. When a final top-level state is entered, the state
+ machine's \l{State::finished}{finished}() signal is emitted. In
+ general, when a final substate (a child of a State) is entered, the parent
+ state's \l{State::finished}{finished}() signal is emitted. FinalState
+ is part of \l{Qt State Machine QML Guide}{Qt State Machine QML API}
+
+ To use a final state, you create a FinalState object and add a transition
+ to it from another state.
+
+ \section1 Example Usage
+
+ \snippet qml/statemachine/finalstate.qml document
+
+ \clearfloat
+
+ \sa StateMachine, State
+*/
diff --git a/src/statemachineqml/finalstate_p.h b/src/statemachineqml/finalstate_p.h
new file mode 100644
index 0000000..c7147c9
--- /dev/null
+++ b/src/statemachineqml/finalstate_p.h
@@ -0,0 +1,59 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef QQMLFINALSTATE_H
+#define QQMLFINALSTATE_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 "qstatemachineqmlglobals_p.h"
+#include "childrenprivate_p.h"
+#include "statemachine_p.h"
+
+#include <QtCore/private/qproperty_p.h>
+#include <QtStateMachine/QFinalState>
+#include <QtQml/QQmlListProperty>
+#include <QtQml/qqml.h>
+
+
+QT_BEGIN_NAMESPACE
+
+class Q_STATEMACHINEQML_EXPORT FinalState : public QFinalState
+{
+ Q_OBJECT
+ Q_PROPERTY(QQmlListProperty<QObject> children READ children
+ NOTIFY childrenChanged BINDABLE bindableChildren)
+ Q_CLASSINFO("DefaultProperty", "children")
+ QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 0)
+
+public:
+ explicit FinalState(QState *parent = 0);
+
+ QQmlListProperty<QObject> children();
+ QBindable<QQmlListProperty<QObject>> bindableChildren() const;
+
+Q_SIGNALS:
+ void childrenChanged();
+
+private:
+ // See the childrenActualCalculation for the mutable explanation
+ mutable ChildrenPrivate<FinalState, ChildrenMode::State> m_children;
+ friend ChildrenPrivate<FinalState, ChildrenMode::State>;
+ void childrenContentChanged();
+ QQmlListProperty<QObject> childrenActualCalculation() const;
+ Q_OBJECT_COMPUTED_PROPERTY(FinalState, QQmlListProperty<QObject>, m_childrenComputedProperty,
+ &FinalState::childrenActualCalculation);
+};
+
+QT_END_NAMESPACE
+#endif
diff --git a/src/statemachineqml/qstatemachineqmlglobals_p.h b/src/statemachineqml/qstatemachineqmlglobals_p.h
new file mode 100644
index 0000000..57229b1
--- /dev/null
+++ b/src/statemachineqml/qstatemachineqmlglobals_p.h
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 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 QSTATEMACHINEQMLGLOBALS_P_H
+#define QSTATEMACHINEQMLGLOBALS_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/qglobal.h>
+#include <QtStateMachineQml/qtstatemachineqmlexports.h>
+
+QT_BEGIN_NAMESPACE
+
+void Q_STATEMACHINEQML_EXPORT qml_register_types_QtQml_StateMachine();
+
+QT_END_NAMESPACE
+
+#endif // QSTATEMACHINEQMLGLOBALS_P_H
diff --git a/src/statemachineqml/signaltransition.cpp b/src/statemachineqml/signaltransition.cpp
new file mode 100644
index 0000000..311edda
--- /dev/null
+++ b/src/statemachineqml/signaltransition.cpp
@@ -0,0 +1,346 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "signaltransition_p.h"
+
+#include <QStateMachine>
+#include <QMetaProperty>
+#include <QQmlInfo>
+#include <QQmlEngine>
+#include <QQmlContext>
+#include <QQmlExpression>
+
+#include <private/qv4qobjectwrapper_p.h>
+#include <private/qjsvalue_p.h>
+#include <private/qv4scopedvalue_p.h>
+#include <private/qqmlcontext_p.h>
+
+SignalTransition::SignalTransition(QState *parent)
+ : QSignalTransition(this, SIGNAL(invokeYourself()), parent), m_complete(false), m_signalExpression(nullptr)
+{
+ connect(this, &SignalTransition::signalChanged, this, [this](){ m_signal.notify(); });
+}
+
+bool SignalTransition::eventTest(QEvent *event)
+{
+ Q_ASSERT(event);
+ if (!QSignalTransition::eventTest(event))
+ return false;
+
+ if (m_guard.value().isEmpty())
+ return true;
+
+ QQmlContext *outerContext = QQmlEngine::contextForObject(this);
+ QQmlContext context(outerContext);
+ QQmlContextData::get(&context)->setImports(QQmlContextData::get(outerContext)->imports());
+
+ QStateMachine::SignalEvent *e = static_cast<QStateMachine::SignalEvent*>(event);
+
+ // Set arguments as context properties
+ int count = e->arguments().size();
+ QMetaMethod metaMethod = e->sender()->metaObject()->method(e->signalIndex());
+ const auto parameterNames = metaMethod.parameterNames();
+ for (int i = 0; i < count; i++)
+ context.setContextProperty(QString::fromUtf8(parameterNames[i]), QVariant::fromValue(e->arguments().at(i)));
+
+ QQmlExpression expr(m_guard.value(), &context, this);
+ QVariant result = expr.evaluate();
+
+ return result.toBool();
+}
+
+void SignalTransition::onTransition(QEvent *event)
+{
+ if (QQmlEnginePrivate *engine = m_signalExpression
+ ? QQmlEnginePrivate::get(m_signalExpression->engine())
+ : nullptr) {
+
+ QStateMachine::SignalEvent *e = static_cast<QStateMachine::SignalEvent*>(event);
+
+ QVarLengthArray<void *, 2> argValues;
+ QVarLengthArray<QMetaType, 2> argTypes;
+
+ QVariantList eventArguments = e->arguments();
+ const int argCount = eventArguments.size();
+ argValues.reserve(argCount + 1);
+ argTypes.reserve(argCount + 1);
+
+ // We're not interested in the return value
+ argValues.append(nullptr);
+ argTypes.append(QMetaType());
+
+ for (QVariant &arg : eventArguments) {
+ argValues.append(arg.data());
+ argTypes.append(arg.metaType());
+ }
+
+ engine->referenceScarceResources();
+ m_signalExpression->QQmlJavaScriptExpression::evaluate(
+ argValues.data(), argTypes.constData(), argCount);
+ engine->dereferenceScarceResources();
+ }
+ QSignalTransition::onTransition(event);
+}
+
+const QJSValue& SignalTransition::signal()
+{
+ return m_signal;
+}
+
+void SignalTransition::setSignal(const QJSValue &signal)
+{
+ m_signal.removeBindingUnlessInWrapper();
+ if (m_signal.valueBypassingBindings().strictlyEquals(signal))
+ return;
+
+ QV4::ExecutionEngine *jsEngine = QQmlEngine::contextForObject(this)->engine()->handle();
+ QV4::Scope scope(jsEngine);
+
+ QObject *sender;
+ QMetaMethod signalMethod;
+
+ m_signal.setValueBypassingBindings(signal);
+ QV4::ScopedValue value(scope, QJSValuePrivate::asReturnedValue(&signal));
+
+ // Did we get the "slot" that can be used to invoke the signal?
+ if (QV4::QObjectMethod *signalSlot = value->as<QV4::QObjectMethod>()) {
+ sender = signalSlot->object();
+ Q_ASSERT(sender);
+ signalMethod = sender->metaObject()->method(signalSlot->methodIndex());
+ } else if (QV4::QmlSignalHandler *signalObject = value->as<QV4::QmlSignalHandler>()) {
+ // or did we get the signal object (the one with the connect()/disconnect() functions) ?
+ sender = signalObject->object();
+ Q_ASSERT(sender);
+ signalMethod = sender->metaObject()->method(signalObject->signalIndex());
+ } else {
+ qmlWarning(this) << tr("Specified signal does not exist.");
+ return;
+ }
+
+ QSignalTransition::setSenderObject(sender);
+ // the call below will emit change signal, and the interceptor lambda in ctor will notify()
+ QSignalTransition::setSignal(signalMethod.methodSignature());
+
+ connectTriggered();
+}
+
+QBindable<QJSValue> SignalTransition::bindableSignal()
+{
+ return &m_signal;
+}
+
+QQmlScriptString SignalTransition::guard() const
+{
+ return m_guard;
+}
+
+void SignalTransition::setGuard(const QQmlScriptString &guard)
+{
+ m_guard = guard;
+}
+
+QBindable<QQmlScriptString> SignalTransition::bindableGuard()
+{
+ return &m_guard;
+}
+
+void SignalTransition::invoke()
+{
+ emit invokeYourself();
+}
+
+void SignalTransition::connectTriggered()
+{
+ if (!m_complete || !m_compilationUnit)
+ return;
+
+ const QObject *target = senderObject();
+ QQmlData *ddata = QQmlData::get(this);
+ QQmlRefPointer<QQmlContextData> ctxtdata = ddata ? ddata->outerContext : nullptr;
+
+ Q_ASSERT(m_bindings.size() == 1);
+ const QV4::CompiledData::Binding *binding = m_bindings.at(0);
+ Q_ASSERT(binding->type() == QV4::CompiledData::Binding::Type_Script);
+
+ QV4::ExecutionEngine *jsEngine = QQmlEngine::contextForObject(this)->engine()->handle();
+ QV4::Scope scope(jsEngine);
+ QV4::Scoped<QV4::QObjectMethod> qobjectSignal(
+ scope, QJSValuePrivate::asReturnedValue(&m_signal.value()));
+ if (!qobjectSignal) {
+ m_signalExpression.adopt(nullptr);
+ return;
+ }
+
+ QMetaMethod metaMethod = target->metaObject()->method(qobjectSignal->methodIndex());
+ int signalIndex = QMetaObjectPrivate::signalIndex(metaMethod);
+
+ auto f = m_compilationUnit->runtimeFunctions[binding->value.compiledScriptIndex];
+ if (ctxtdata) {
+ QQmlRefPointer<QQmlBoundSignalExpression> expression(
+ new QQmlBoundSignalExpression(target, signalIndex, ctxtdata, this, f),
+ QQmlRefPointer<QQmlBoundSignalExpression>::Adopt);
+ expression->setNotifyOnValueChanged(false);
+ m_signalExpression = expression;
+ } else {
+ m_signalExpression.adopt(nullptr);
+ }
+}
+
+void SignalTransitionParser::verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props)
+{
+ for (int ii = 0; ii < props.size(); ++ii) {
+ const QV4::CompiledData::Binding *binding = props.at(ii);
+
+ QString propName = compilationUnit->stringAt(binding->propertyNameIndex);
+
+ if (propName != QLatin1String("onTriggered")) {
+ error(props.at(ii), SignalTransition::tr("Cannot assign to non-existent property \"%1\"").arg(propName));
+ return;
+ }
+
+ if (binding->type() != QV4::CompiledData::Binding::Type_Script) {
+ error(binding, SignalTransition::tr("SignalTransition: script expected"));
+ return;
+ }
+ }
+}
+
+void SignalTransitionParser::applyBindings(
+ QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit,
+ const QList<const QV4::CompiledData::Binding *> &bindings)
+{
+ SignalTransition *st = qobject_cast<SignalTransition*>(object);
+ st->m_compilationUnit = compilationUnit;
+ st->m_bindings = bindings;
+}
+
+/*!
+ \qmltype QAbstractTransition
+ \inqmlmodule QtQml.StateMachine
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief The QAbstractTransition type is the base type of transitions between QAbstractState objects.
+
+ The QAbstractTransition type is the abstract base type of transitions
+ between states (QAbstractState objects) of a StateMachine.
+ QAbstractTransition is part of \l{Qt State Machine QML Guide}{Qt State Machine QML API}
+
+
+ The sourceState() property has the source of the transition. The
+ targetState and targetStates properties return the target(s) of the
+ transition.
+
+ The triggered() signal is emitted when the transition has been triggered.
+
+ Do not use QAbstractTransition directly; use SignalTransition or
+ TimeoutTransition instead.
+
+ \sa SignalTransition, TimeoutTransition
+*/
+
+/*!
+ \qmlproperty bool QAbstractTransition::sourceState
+ \readonly sourceState
+
+ \brief The source state (parent) of this transition.
+*/
+
+/*!
+ \qmlproperty QAbstractState 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).
+*/
+
+/*!
+ \qmlproperty list<QAbstractState> QAbstractTransition::targetStates
+
+ \brief The target states of this transition.
+
+ If multiple states are specified, they all must be descendants of the
+ same parallel group state.
+*/
+
+/*!
+ \qmlsignal QAbstractTransition::triggered()
+
+ This signal is emitted when the transition has been triggered.
+*/
+
+/*!
+ \qmltype QSignalTransition
+ \inqmlmodule QtQml.StateMachine
+ \inherits QAbstractTransition
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief The QSignalTransition type provides a transition based on a Qt signal.
+
+ Do not use QSignalTransition directly; use SignalTransition or
+ TimeoutTransition instead.
+
+ \sa SignalTransition, TimeoutTransition
+*/
+
+/*!
+ \qmlproperty string QSignalTransition::signal
+
+ \brief The signal which is associated with this signal transition.
+*/
+
+/*!
+ \qmlproperty QObject QSignalTransition::senderObject
+
+ \brief The sender object which is associated with this signal transition.
+*/
+
+
+/*!
+ \qmltype SignalTransition
+ \inqmlmodule QtQml.StateMachine
+ \inherits QSignalTransition
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief The SignalTransition type provides a transition based on a Qt signal.
+
+ SignalTransition is part of \l{Qt State Machine QML Guide}{Qt State Machine QML API}.
+
+ \section1 Example Usage
+
+ \snippet qml/statemachine/signaltransition.qml document
+
+ \clearfloat
+
+ \sa StateMachine, FinalState, TimeoutTransition
+*/
+
+/*!
+ \qmlproperty signal SignalTransition::signal
+
+ \brief The signal which is associated with this signal transition.
+
+ \snippet qml/statemachine/signaltransitionsignal.qml document
+*/
+
+/*!
+ \qmlproperty bool SignalTransition::guard
+
+ Guard conditions affect the behavior of a state machine by enabling
+ transitions only when they evaluate to true and disabling them when
+ they evaluate to false.
+
+ When the signal associated with this signal transition is emitted the
+ guard condition is evaluated. In the guard condition the arguments
+ of the signal can be used as demonstrated in the example below.
+
+ \snippet qml/statemachine/guardcondition.qml document
+
+ \sa signal
+*/
+
+#include "moc_signaltransition_p.cpp"
diff --git a/src/statemachineqml/signaltransition_p.h b/src/statemachineqml/signaltransition_p.h
new file mode 100644
index 0000000..2107a09
--- /dev/null
+++ b/src/statemachineqml/signaltransition_p.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef SIGNALTRANSITION_H
+#define SIGNALTRANSITION_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 "qstatemachineqmlglobals_p.h"
+
+#include <QtStateMachine/QSignalTransition>
+#include <QtCore/QVariant>
+#include <QtQml/QJSValue>
+
+#include <QtQml/qqmlscriptstring.h>
+#include <QtQml/qqmlparserstatus.h>
+#include <private/qqmlcustomparser_p.h>
+#include <private/qqmlrefcount_p.h>
+#include <private/qqmlboundsignal_p.h>
+#include <QtCore/private/qproperty_p.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_STATEMACHINEQML_EXPORT SignalTransition : public QSignalTransition, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+ Q_PROPERTY(QJSValue signal READ signal WRITE setSignal
+ NOTIFY qmlSignalChanged BINDABLE bindableSignal)
+ Q_PROPERTY(QQmlScriptString guard READ guard WRITE setGuard
+ NOTIFY guardChanged BINDABLE bindableGuard)
+ QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 0)
+ QML_CUSTOMPARSER
+
+public:
+ explicit SignalTransition(QState *parent = nullptr);
+
+ QQmlScriptString guard() const;
+ void setGuard(const QQmlScriptString &guard);
+ QBindable<QQmlScriptString> bindableGuard();
+
+ bool eventTest(QEvent *event) override;
+ void onTransition(QEvent *event) override;
+
+ const QJSValue &signal();
+ void setSignal(const QJSValue &signal);
+ QBindable<QJSValue> bindableSignal();
+
+ Q_INVOKABLE void invoke();
+
+Q_SIGNALS:
+ void guardChanged();
+ void invokeYourself();
+ /*!
+ * \internal
+ */
+ void qmlSignalChanged();
+
+private:
+ void classBegin() override { m_complete = false; }
+ void componentComplete() override { m_complete = true; connectTriggered(); }
+ void connectTriggered();
+
+ friend class SignalTransitionParser;
+
+ Q_OBJECT_COMPAT_PROPERTY(SignalTransition, QJSValue, m_signal, &SignalTransition::setSignal,
+ &SignalTransition::qmlSignalChanged);
+ Q_OBJECT_BINDABLE_PROPERTY(SignalTransition, QQmlScriptString,
+ m_guard, &SignalTransition::guardChanged);
+ bool m_complete;
+ QQmlRefPointer<QV4::ExecutableCompilationUnit> m_compilationUnit;
+ QList<const QV4::CompiledData::Binding *> m_bindings;
+ QQmlRefPointer<QQmlBoundSignalExpression> m_signalExpression;
+};
+
+class SignalTransitionParser : public QQmlCustomParser
+{
+public:
+ void verifyBindings(const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &props) override;
+ void applyBindings(QObject *object, const QQmlRefPointer<QV4::ExecutableCompilationUnit> &compilationUnit, const QList<const QV4::CompiledData::Binding *> &bindings) override;
+};
+
+template<>
+inline QQmlCustomParser *qmlCreateCustomParser<SignalTransition>()
+{
+ return new SignalTransitionParser;
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachineqml/state.cpp b/src/statemachineqml/state.cpp
new file mode 100644
index 0000000..1701bfa
--- /dev/null
+++ b/src/statemachineqml/state.cpp
@@ -0,0 +1,221 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "state_p.h"
+
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQmlInfo>
+
+State::State(QState *parent)
+ : QState(parent)
+{
+}
+
+QQmlListProperty<QObject> State::childrenActualCalculation() const
+{
+ // Mutating accesses to m_children only happen in the QML thread,
+ // so there are no thread-safety issues.
+ // The engine only creates non-const instances of the class anyway
+ return QQmlListProperty<QObject>(const_cast<State*>(this), &m_children,
+ m_children.append, m_children.count, m_children.at,
+ m_children.clear, m_children.replace, m_children.removeLast);
+}
+
+void State::componentComplete()
+{
+ if (this->machine() == nullptr) {
+ static bool once = false;
+ if (!once) {
+ once = true;
+ qmlWarning(this) << "No top level StateMachine found. Nothing will run without a StateMachine.";
+ }
+ }
+}
+
+QQmlListProperty<QObject> State::children()
+{
+ return m_childrenComputedProperty;
+}
+
+void State::childrenContentChanged()
+{
+ m_childrenComputedProperty.notify();
+ emit childrenChanged();
+}
+
+QBindable<QQmlListProperty<QObject>> State::bindableChildren() const
+{
+ return &m_childrenComputedProperty;
+}
+
+/*!
+ \qmltype QAbstractState
+ \inqmlmodule QtQml.StateMachine
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief The QAbstractState type is the base type of States of a StateMachine.
+
+ Do not use QAbstractState directly; use State, FinalState or
+ StateMachine instead.
+
+ \sa StateMachine, State
+*/
+
+/*!
+ \qmlproperty bool QAbstractState::active
+ \readonly active
+
+ The active property of this state. A state is active between
+ entered() and exited() signals. This property is readonly.
+
+ \sa entered, exited
+*/
+
+/*!
+ \qmlsignal QAbstractState::entered()
+
+ This signal is emitted when the State becomes active.
+
+ \sa active, exited
+*/
+
+/*!
+ \qmlsignal QAbstractState::exited()
+
+ This signal is emitted when the State becomes inactive.
+
+ \sa active, entered
+*/
+
+/*!
+ \qmltype State
+ \inqmlmodule QtQml.StateMachine
+ \inherits QAbstractState
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief Provides a general-purpose state for StateMachine.
+
+ State objects can have child states as well as transitions to other
+ states. State is part of \l{Qt State Machine QML Guide}{Qt State Machine QML API}
+
+ \section1 States with Child States
+
+ The childMode property determines how child states are treated. For
+ non-parallel state groups, the initialState property must be used 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 State::finished() signal when a final child state
+ (FinalState) is entered.
+
+ The errorState 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).
+
+ \section1 Example Usage
+
+ \snippet qml/statemachine/basicstate.qml document
+
+ \clearfloat
+
+ \sa StateMachine, FinalState
+*/
+
+/*!
+ \qmlproperty enumeration State::childMode
+
+ \brief The child mode of this state
+
+ The default value of this property is QState.ExclusiveStates.
+
+ This enum specifies how a state's child states are treated:
+ \list
+ \li QState.ExclusiveStates The child states are mutually exclusive and an initial state must be set by setting initialState property.
+ \li QState.ParallelStates The child states are parallel. When the parent state is entered, all its child states are entered in parallel.
+ \endlist
+*/
+
+/*!
+ \qmlproperty QAbstractState State::errorState
+
+ \brief The error state of this state.
+*/
+
+/*!
+ \qmlproperty QAbstractState State::initialState
+
+ \brief The initial state of this state (one of its child states).
+*/
+
+/*!
+ \qmlsignal State::finished()
+
+ This signal is emitted when a final child state of this state is entered.
+
+ \sa QAbstractState::active, QAbstractState::entered, QAbstractState::exited
+*/
+
+/*!
+ \qmltype HistoryState
+ \inqmlmodule QtQml.StateMachine
+ \inherits QAbstractState
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief The HistoryState type provides a means of returning to a previously active substate.
+
+ A history state is a pseudo-state that represents the child state that the
+ parent state was in the last time the parent state was exited. A transition
+ with a history state as its target is in fact a transition to one of the
+ other child states of the parent state.
+ HistoryState is part of \l{Qt State Machine QML Guide}{Qt State Machine QML API}.
+
+ Use the defaultState property to set the state that should be entered
+ if the parent state has never been entered.
+
+ \section1 Example Usage
+
+ \snippet qml/statemachine/historystate.qml document
+
+ \clearfloat
+
+ By default, a history state is shallow, meaning that it will not remember
+ nested states. This can be configured through the historyType property.
+
+ \sa StateMachine, State
+*/
+
+/*!
+ \qmlproperty QAbstractState HistoryState::defaultState
+
+ \brief The default state of this history state.
+
+ The default state indicates the state to transition to if the parent
+ state has never been entered before.
+*/
+
+/*!
+ \qmlproperty enumeration HistoryState::historyType
+
+ \brief The type of history that this history state records.
+
+ The default value of this property is HistoryState.ShallowHistory.
+
+ This enum specifies the type of history that a HistoryState records.
+ \list
+ \li HistoryState.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.
+ \li HistoryState.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.
+ \endlist
+*/
+
+#include "moc_state_p.cpp"
diff --git a/src/statemachineqml/state_p.h b/src/statemachineqml/state_p.h
new file mode 100644
index 0000000..2f5595e
--- /dev/null
+++ b/src/statemachineqml/state_p.h
@@ -0,0 +1,63 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef STATE_H
+#define STATE_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 "qstatemachineqmlglobals_p.h"
+#include "childrenprivate_p.h"
+
+#include <QtCore/qproperty.h>
+#include <QtStateMachine/QState>
+#include <QtQml/QQmlParserStatus>
+#include <QtQml/QQmlListProperty>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_STATEMACHINEQML_EXPORT State : public QState, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+ Q_PROPERTY(QQmlListProperty<QObject> children READ children
+ NOTIFY childrenChanged BINDABLE bindableChildren)
+ Q_CLASSINFO("DefaultProperty", "children")
+ QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 0)
+
+public:
+ explicit State(QState *parent = 0);
+
+ void classBegin() override {}
+ void componentComplete() override;
+
+ QQmlListProperty<QObject> children();
+ QBindable<QQmlListProperty<QObject>> bindableChildren() const;
+
+Q_SIGNALS:
+ void childrenChanged();
+
+private:
+ // See the childrenActualCalculation for the mutable explanation
+ mutable ChildrenPrivate<State, ChildrenMode::StateOrTransition> m_children;
+ friend ChildrenPrivate<State, ChildrenMode::StateOrTransition>;
+ void childrenContentChanged();
+ QQmlListProperty<QObject> childrenActualCalculation() const;
+ Q_OBJECT_COMPUTED_PROPERTY(State, QQmlListProperty<QObject>, m_childrenComputedProperty,
+ &State::childrenActualCalculation);
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachineqml/statemachine.cpp b/src/statemachineqml/statemachine.cpp
new file mode 100644
index 0000000..3b55817
--- /dev/null
+++ b/src/statemachineqml/statemachine.cpp
@@ -0,0 +1,214 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "statemachine_p.h"
+
+#include <QAbstractTransition>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQmlInfo>
+
+StateMachine::StateMachine(QObject *parent)
+ : QStateMachine(parent), m_completed(false), m_running(false)
+{
+ connect(this, SIGNAL(runningChanged(bool)), SIGNAL(qmlRunningChanged()));
+ connect(this, SIGNAL(childModeChanged()), SLOT(checkChildMode()));
+}
+
+QQmlListProperty<QObject> StateMachine::childrenActualCalculation() const
+{
+ // Mutating accesses to m_children only happen in the QML thread,
+ // so there are no thread-safety issues.
+ // The engine only creates non-const instances of the class anyway
+ return QQmlListProperty<QObject>(const_cast<StateMachine*>(this), &m_children,
+ m_children.append, m_children.count, m_children.at,
+ m_children.clear, m_children.replace, m_children.removeLast);
+}
+
+bool StateMachine::isRunning() const
+{
+ return QStateMachine::isRunning();
+}
+
+void StateMachine::setRunning(bool running)
+{
+ if (m_completed)
+ QStateMachine::setRunning(running);
+ else
+ m_running = running;
+}
+
+void StateMachine::checkChildMode()
+{
+ if (childMode() != QState::ExclusiveStates) {
+ qmlWarning(this) << "Setting the childMode of a StateMachine to anything else than\n"
+ "QState.ExclusiveStates will result in an invalid state machine,\n"
+ "and can lead to incorrect behavior!";
+ }
+}
+
+void StateMachine::componentComplete()
+{
+ if (QStateMachine::initialState() == nullptr && childMode() == QState::ExclusiveStates)
+ qmlWarning(this) << "No initial state set for StateMachine";
+
+ // Everything is proper setup, now start the state-machine if we got
+ // asked to do so.
+ m_completed = true;
+ if (m_running)
+ setRunning(true);
+}
+
+QQmlListProperty<QObject> StateMachine::children()
+{
+ return m_childrenComputedProperty;
+}
+
+void StateMachine::childrenContentChanged()
+{
+ m_childrenComputedProperty.notify();
+ emit childrenChanged();
+}
+
+QBindable<QQmlListProperty<QObject>> StateMachine::bindableChildren() const
+{
+ return &m_childrenComputedProperty;
+}
+
+/*!
+ \qmltype StateMachine
+ \inqmlmodule QtQml.StateMachine
+ \inherits State
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief Provides a hierarchical finite state machine.
+
+ StateMachine is based on the concepts and notation of
+ \l{http://www.wisdom.weizmann.ac.il/~dharel/SCANNED.PAPERS/Statecharts.pdf}{Statecharts}.
+ StateMachine is part of \l{Qt State Machine QML Guide}{Qt State Machine QML API}
+
+ A state machine manages a set of states and transitions between those
+ states; these states and transitions define a state graph. Once a state
+ graph has been built, the state machine can execute it. StateMachine'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 QML Guide}{overview}
+ gives several state graphs and the code to build them.
+
+ Before the machine can be started, the \l{State::initialState}{initialState}
+ must be set. The initial state is the state that the
+ machine enters when started. You can then set running property to true
+ or start() the state machine. The started signal is emitted when the
+ initial state is entered.
+
+ 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 (you can also set running property to false).
+ The stopped signal is emitted in this case.
+
+ \section1 Example Usage
+ The following snippet shows a state machine that will finish when a button
+ is clicked:
+
+ \snippet qml/statemachine/simplestatemachine.qml document
+
+ If an error is encountered, the machine will look for an
+ \l{State::errorState}{errorState}, and if one is available, it will
+ enter this state. 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.
+
+ \warning Setting the childMode of a StateMachine to anything else than QState::ExclusiveStates
+ will result in an invalid state machine, and can lead to incorrect behavior.
+
+ \clearfloat
+
+ \sa QAbstractState, State, SignalTransition, TimeoutTransition, HistoryState {Qt State Machine QML Guide}
+*/
+
+/*!
+ \qmlproperty enumeration StateMachine::globalRestorePolicy
+
+ \brief The restore policy for states of this state machine.
+
+ The default value of this property is QState.DontRestoreProperties.
+
+ 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 QState.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.
+
+ \list
+ \li QState.DontRestoreProperties The state machine should not save the initial values of properties and restore them later.
+ \li QState.RestoreProperties The state machine should save the initial values of properties and restore them later.
+ \endlist
+*/
+
+/*!
+ \qmlproperty bool StateMachine::running
+
+ \brief The running state of this state machine.
+ \sa start(), stop()
+*/
+
+/*!
+ \qmlproperty string StateMachine::errorString
+ \readonly errorString
+
+ \brief The error string of this state machine.
+*/
+
+
+/*!
+ \qmlmethod StateMachine::start()
+
+ Starts this state machine. The machine will reset its configuration and
+ transition to the initial state. When a final top-level state (FinalState)
+ 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, State::finished, stop(), State::initialState, running
+*/
+
+/*!
+ \qmlsignal StateMachine::started()
+
+ This signal is emitted when the state machine has entered its initial state
+ (State::initialState).
+
+ \sa running, start(), State::finished
+*/
+
+/*!
+ \qmlmethod StateMachine::stop()
+
+ Stops this state machine. The state machine will stop processing events
+ and then emit the stopped signal.
+
+ \sa stopped, start(), running
+*/
+
+/*!
+ \qmlsignal StateMachine::stopped()
+
+ This signal is emitted when the state machine has stopped.
+
+ \sa running, stop(), State::finished
+*/
+
+#include "moc_statemachine_p.cpp"
diff --git a/src/statemachineqml/statemachine_p.h b/src/statemachineqml/statemachine_p.h
new file mode 100644
index 0000000..7356f34
--- /dev/null
+++ b/src/statemachineqml/statemachine_p.h
@@ -0,0 +1,78 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef STATEMACHINE_H
+#define STATEMACHINE_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 "qstatemachineqmlglobals_p.h"
+#include "childrenprivate_p.h"
+
+#include <QtCore/private/qproperty_p.h>
+#include <QtStateMachine/QStateMachine>
+#include <QtQml/QQmlParserStatus>
+#include <QtQml/QQmlListProperty>
+#include <QtQml/qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class Q_STATEMACHINEQML_EXPORT StateMachine : public QStateMachine, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+ Q_PROPERTY(QQmlListProperty<QObject> children READ children
+ NOTIFY childrenChanged BINDABLE bindableChildren)
+
+ // Override to delay execution after componentComplete()
+ Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY qmlRunningChanged)
+
+ Q_CLASSINFO("DefaultProperty", "children")
+ QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 0)
+
+public:
+ explicit StateMachine(QObject *parent = 0);
+
+ void classBegin() override {}
+ void componentComplete() override;
+ QQmlListProperty<QObject> children();
+ QBindable<QQmlListProperty<QObject>> bindableChildren() const;
+
+ bool isRunning() const;
+ void setRunning(bool running);
+
+private Q_SLOTS:
+ void checkChildMode();
+
+Q_SIGNALS:
+ void childrenChanged();
+ /*!
+ * \internal
+ */
+ void qmlRunningChanged();
+
+private:
+ // See the childrenActualCalculation for the mutable explanation
+ mutable ChildrenPrivate<StateMachine, ChildrenMode::StateOrTransition> m_children;
+ friend ChildrenPrivate<StateMachine, ChildrenMode::StateOrTransition>;
+ void childrenContentChanged();
+ QQmlListProperty<QObject> childrenActualCalculation() const;
+ Q_OBJECT_COMPUTED_PROPERTY(StateMachine, QQmlListProperty<QObject>, m_childrenComputedProperty,
+ &StateMachine::childrenActualCalculation);
+ bool m_completed;
+ bool m_running;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/statemachineqml/statemachineforeign_p.h b/src/statemachineqml/statemachineforeign_p.h
new file mode 100644
index 0000000..8a05042
--- /dev/null
+++ b/src/statemachineqml/statemachineforeign_p.h
@@ -0,0 +1,61 @@
+// Copyright (C) 2019 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 STATEMACHINEFOREIGN_H
+#define STATEMACHINEFOREIGN_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 "qstatemachineqmlglobals_p.h"
+
+#include <QtQml/qqml.h>
+#include <QtStateMachine/qhistorystate.h>
+#include <QtStateMachine/qstate.h>
+#include <QtStateMachine/qabstractstate.h>
+#include <QtStateMachine/qsignaltransition.h>
+
+struct Q_STATEMACHINEQML_EXPORT QHistoryStateForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QHistoryState)
+ QML_NAMED_ELEMENT(HistoryState)
+ QML_ADDED_IN_VERSION(1, 0)
+};
+
+struct Q_STATEMACHINEQML_EXPORT QStateForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QState)
+ QML_NAMED_ELEMENT(QState)
+ QML_ADDED_IN_VERSION(1, 0)
+ QML_UNCREATABLE("Don't use this, use State instead.")
+};
+
+struct Q_STATEMACHINEQML_EXPORT QAbstractStateForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QAbstractState)
+ QML_NAMED_ELEMENT(QAbstractState)
+ QML_ADDED_IN_VERSION(1, 0)
+ QML_UNCREATABLE("Don't use this, use State instead.")
+};
+
+struct Q_STATEMACHINEQML_EXPORT QSignalTransitionForeign
+{
+ Q_GADGET
+ QML_FOREIGN(QSignalTransition)
+ QML_NAMED_ELEMENT(QSignalTransition)
+ QML_ADDED_IN_VERSION(1, 0)
+ QML_UNCREATABLE("Don't use this, use SignalTransition instead.")
+};
+
+#endif // STATEMACHINEFOREIGN_H
diff --git a/src/statemachineqml/timeouttransition.cpp b/src/statemachineqml/timeouttransition.cpp
new file mode 100644
index 0000000..29bfd45
--- /dev/null
+++ b/src/statemachineqml/timeouttransition.cpp
@@ -0,0 +1,80 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "timeouttransition_p.h"
+
+#include <QQmlInfo>
+#include <QTimer>
+#include <QState>
+
+TimeoutTransition::TimeoutTransition(QState* parent)
+ : QSignalTransition((m_timer = new QTimer), SIGNAL(timeout()), parent)
+{
+ m_timer->setSingleShot(true);
+ m_timer->setInterval(1000);
+}
+
+TimeoutTransition::~TimeoutTransition()
+{
+ delete m_timer;
+}
+
+int TimeoutTransition::timeout() const
+{
+ return m_timer->interval();
+}
+
+void TimeoutTransition::setTimeout(int timeout)
+{
+ m_timer->setInterval(timeout);
+}
+
+QBindable<int> TimeoutTransition::bindableTimeout()
+{
+ return m_timer->bindableInterval();
+}
+
+void TimeoutTransition::componentComplete()
+{
+ QState *state = qobject_cast<QState*>(parent());
+ if (!state) {
+ qmlWarning(this) << "Parent needs to be a State";
+ return;
+ }
+
+ connect(state, SIGNAL(entered()), m_timer, SLOT(start()));
+ connect(state, SIGNAL(exited()), m_timer, SLOT(stop()));
+ if (state->active())
+ m_timer->start();
+}
+
+/*!
+ \qmltype TimeoutTransition
+ \inqmlmodule QtQml.StateMachine
+ \inherits QSignalTransition
+ \ingroup statemachine-qmltypes
+ \since 5.4
+
+ \brief The TimeoutTransition type provides a transition based on a timer.
+
+ \l {QtQml::Timer}{Timer} type can be combined with SignalTransition to enact more complex
+ timeout based transitions.
+
+ TimeoutTransition is part of \l{Qt State Machine QML Guide}{Qt State Machine QML API}
+
+ \section1 Example Usage
+
+ \snippet qml/statemachine/timeouttransition.qml document
+
+ \clearfloat
+
+ \sa StateMachine, SignalTransition, FinalState, HistoryState
+*/
+
+/*!
+ \qmlproperty int TimeoutTransition::timeout
+
+ \brief The timeout interval in milliseconds.
+*/
+
+#include "moc_timeouttransition_p.cpp"
diff --git a/src/statemachineqml/timeouttransition_p.h b/src/statemachineqml/timeouttransition_p.h
new file mode 100644
index 0000000..5c8388e
--- /dev/null
+++ b/src/statemachineqml/timeouttransition_p.h
@@ -0,0 +1,53 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#ifndef TIMEOUTTRANSITION_H
+#define TIMEOUTTRANSITION_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 "qstatemachineqmlglobals_p.h"
+
+#include <QtStateMachine/QSignalTransition>
+#include <QtQml/QQmlParserStatus>
+#include <QtQml/qqml.h>
+#include <QtCore/private/qproperty_p.h>
+
+QT_BEGIN_NAMESPACE
+class QTimer;
+
+class Q_STATEMACHINEQML_EXPORT TimeoutTransition : public QSignalTransition, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_PROPERTY(int timeout READ timeout WRITE setTimeout BINDABLE bindableTimeout)
+ Q_INTERFACES(QQmlParserStatus)
+ QML_ELEMENT
+ QML_ADDED_IN_VERSION(1, 0)
+
+public:
+ TimeoutTransition(QState *parent = nullptr);
+ ~TimeoutTransition();
+
+ int timeout() const;
+ void setTimeout(int timeout);
+ QBindable<int> bindableTimeout();
+
+ void classBegin() override {}
+ void componentComplete() override;
+
+private:
+ QTimer *m_timer;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/sync.profile b/sync.profile
deleted file mode 100644
index 1a8e936..0000000
--- a/sync.profile
+++ /dev/null
@@ -1,9 +0,0 @@
-%modules = ( # path to module name map
- "QtScxml" => "$basedir/src/scxml",
-);
-%moduleheaders = ( # restrict the module headers to those found in relative path
-);
-%classnames = (
-);
-%deprecatedheaders = (
-);
diff --git a/tests/3rdparty/gen-scion-tests.py b/tests/3rdparty/gen-scion-tests.py
index 91bd192..4e1eb38 100755
--- a/tests/3rdparty/gen-scion-tests.py
+++ b/tests/3rdparty/gen-scion-tests.py
@@ -1,30 +1,6 @@
#!/usr/bin/python
-
# Copyright (C) 2016 The Qt Company Ltd.
-# Contact: https://www.qt.io/licensing/
-#
-# This file is part of the QtScxml module of the Qt Toolkit.
-#
-# $QT_BEGIN_LICENSE:GPL-EXCEPT$
-# Commercial License Usage
-# Licensees holding valid commercial Qt licenses may use this file in
-# accordance with the commercial license agreement provided with the
-# Software or, alternatively, in accordance with the terms contained in
-# a written agreement between you and The Qt Company. For licensing terms
-# and conditions see https://www.qt.io/terms-conditions. For further
-# information use the contact form at https://www.qt.io/contact-us.
-#
-# GNU General Public License Usage
-# Alternatively, this file may be used under the terms of the GNU
-# General Public License version 3 as published by the Free Software
-# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-# included in the packaging of this file. Please review the following
-# information to ensure the GNU General Public License requirements will
-# be met: https://www.gnu.org/licenses/gpl-3.0.html.
-#
-# $QT_END_LICENSE$
-
-from os import walk
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
from os.path import isfile, join, splitext
f = open("scion.qrc", "w")
diff --git a/tests/3rdparty/scion-tests/scxml-test-framework/test/w3c-ecma/test557.txt b/tests/3rdparty/scion-tests/scxml-test-framework/test/w3c-ecma/test557.txt
new file mode 100644
index 0000000..a8e51da
--- /dev/null
+++ b/tests/3rdparty/scion-tests/scxml-test-framework/test/w3c-ecma/test557.txt
@@ -0,0 +1,4 @@
+<books xmlns="">
+ <book title="title1"/>
+ <book title="title2"/>
+ </books> \ No newline at end of file
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000..c82db08
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+if(QT_BUILD_STANDALONE_TESTS)
+ # Add qt_find_package calls for extra dependencies that need to be found when building
+ # the standalone tests here.
+endif()
+qt_build_tests()
diff --git a/tests/auto/CMakeLists.txt b/tests/auto/CMakeLists.txt
new file mode 100644
index 0000000..f350d5a
--- /dev/null
+++ b/tests/auto/CMakeLists.txt
@@ -0,0 +1,22 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+add_subdirectory(scxmlcoutput)
+if(TARGET Qt::Gui AND TARGET Qt::Qml)
+ if(TARGET Qt::Scxml)
+ add_subdirectory(compiled)
+ add_subdirectory(dynamicmetaobject)
+ add_subdirectory(parser)
+ add_subdirectory(scion)
+ add_subdirectory(statemachine)
+ add_subdirectory(statemachineinfo)
+ add_subdirectory(cmake)
+ endif()
+ # More checks inside
+ add_subdirectory(qml)
+ add_subdirectory(qmltest)
+endif()
+if(NOT UIKIT AND TARGET Qt::StateMachine)
+ add_subdirectory(qstatemachine)
+endif()
diff --git a/tests/auto/auto.pro b/tests/auto/auto.pro
deleted file mode 100644
index 60576d5..0000000
--- a/tests/auto/auto.pro
+++ /dev/null
@@ -1,8 +0,0 @@
-TEMPLATE = subdirs
-SUBDIRS = cmake\
- compiled\
- dynamicmetaobject\
- parser\
- scion\
- statemachine \
- statemachineinfo
diff --git a/tests/auto/cmake/CMakeLists.txt b/tests/auto/cmake/CMakeLists.txt
index 9c9e098..272ff7d 100644
--- a/tests/auto/cmake/CMakeLists.txt
+++ b/tests/auto/cmake/CMakeLists.txt
@@ -1,15 +1,49 @@
-cmake_minimum_required(VERSION 2.8)
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
-project(qmake_cmake_files)
+# This is an automatic test for the CMake configuration files.
+# To run it manually,
+# 1) mkdir build # Create a build directory
+# 2) cd build
+# 3) # Run cmake on this directory
+# `$qt_prefix/bin/qt-cmake ..` or `cmake -DCMAKE_PREFIX_PATH=/path/to/qt ..`
+# 4) ctest # Run ctest
+cmake_minimum_required(VERSION 3.16)
+project(scxml_cmake_tests)
enable_testing()
-find_package(Qt5Core REQUIRED)
+set(required_packages Core Scxml)
-include("${_Qt5CTestMacros}")
+# Setup the test when called as a completely standalone project.
+if(TARGET Qt6::Core)
+ # Tests are built as part of the repository's build tree.
+ # Setup paths so that the Qt packages are found.
+ qt_internal_set_up_build_dir_package_paths()
+endif()
-test_module_includes(
- Scxml QScxmlEvent
+find_package(Qt6 REQUIRED COMPONENTS ${required_packages})
+
+# Setup common test variables which were previously set by ctest_testcase_common.prf.
+set(CMAKE_MODULES_UNDER_TEST "${required_packages}")
+
+foreach(qt_package ${CMAKE_MODULES_UNDER_TEST})
+ set(package_name "${QT_CMAKE_EXPORT_NAMESPACE}${qt_package}")
+ if(${package_name}_FOUND)
+ set(CMAKE_${qt_package}_MODULE_MAJOR_VERSION "${${package_name}_VERSION_MAJOR}")
+ set(CMAKE_${qt_package}_MODULE_MINOR_VERSION "${${package_name}_VERSION_MINOR}")
+ set(CMAKE_${qt_package}_MODULE_PATCH_VERSION "${${package_name}_VERSION_PATCH}")
+ endif()
+endforeach()
+
+include("${_Qt6CTestMacros}")
+
+set(module_includes
+ Scxml QScxmlEvent
+)
+
+_qt_internal_test_module_includes(
+ ${module_includes}
)
-expect_pass(test_qtscxml_module)
+_qt_internal_test_expect_pass(test_qtscxml_module)
diff --git a/tests/auto/cmake/cmake.pro b/tests/auto/cmake/cmake.pro
deleted file mode 100644
index e91ff69..0000000
--- a/tests/auto/cmake/cmake.pro
+++ /dev/null
@@ -1,6 +0,0 @@
-# Cause make to do nothing.
-TEMPLATE = subdirs
-
-CMAKE_QT_MODULES_UNDER_TEST = scxml
-
-CONFIG += ctest_testcase
diff --git a/tests/auto/cmake/test_qtscxml_module/CMakeLists.txt b/tests/auto/cmake/test_qtscxml_module/CMakeLists.txt
index e2cf03a..ae9efdb 100644
--- a/tests/auto/cmake/test_qtscxml_module/CMakeLists.txt
+++ b/tests/auto/cmake/test_qtscxml_module/CMakeLists.txt
@@ -1,16 +1,20 @@
-cmake_minimum_required(VERSION 2.8)
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+cmake_minimum_required(VERSION 3.16)
project(test_qtscxml_module)
-find_package(Qt5Scxml REQUIRED)
+find_package(Qt6Scxml REQUIRED)
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_executable(mainapp main.cpp)
-set(MAIN_SRCS main.cpp)
-qt5_add_statecharts(MAIN_SRCS
+qt6_add_statecharts(mainapp
../../compiled/connection.scxml
# unused, just for testing whether it's possible to pass multiple files
../../compiled/topmachine.scxml
)
-add_executable(mainapp ${MAIN_SRCS})
-target_include_directories(mainapp PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
-target_link_libraries(mainapp Qt5::Scxml)
+
+target_link_libraries(mainapp Qt::Scxml)
diff --git a/tests/auto/cmake/test_qtscxml_module/main.cpp b/tests/auto/cmake/test_qtscxml_module/main.cpp
index 2d081df..ead56e0 100644
--- a/tests/auto/cmake/test_qtscxml_module/main.cpp
+++ b/tests/auto/cmake/test_qtscxml_module/main.cpp
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 Klaralvdalens Datakonsult AB (KDAB).
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include "connection.h"
diff --git a/tests/auto/compiled/CMakeLists.txt b/tests/auto/compiled/CMakeLists.txt
new file mode 100644
index 0000000..a8cd96e
--- /dev/null
+++ b/tests/auto/compiled/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_compiled Test:
+#####################################################################
+
+if(WASM)
+ return()
+endif()
+
+qt_internal_add_test(tst_compiled
+ SOURCES
+ tst_compiled.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Scxml
+)
+
+# Resources:
+set(tst_compiled_resource_files
+ "submachineA.scxml"
+ "submachineB.scxml"
+ "topmachine.scxml"
+)
+
+qt_internal_add_resource(tst_compiled "tst_compiled"
+ PREFIX
+ "/"
+ FILES
+ ${tst_compiled_resource_files}
+)
+
+
+# Statecharts:
+qt6_add_statecharts(tst_compiled
+ ids1.scxml
+ eventnames1.scxml
+ eventnames2.scxml
+ statemachineunicodename.scxml
+ anonymousstate.scxml
+ submachineunicodename.scxml
+ datainnulldatamodel.scxml
+ initialhistory.scxml
+ connection.scxml
+ topmachine.scxml
+ historyState.scxml
+)
+
+#### Keys ignored in scope 1:.:.:compiled.pro:<TRUE>:
+# TEMPLATE = "app"
diff --git a/tests/auto/compiled/compiled.pro b/tests/auto/compiled/compiled.pro
deleted file mode 100644
index 713f484..0000000
--- a/tests/auto/compiled/compiled.pro
+++ /dev/null
@@ -1,25 +0,0 @@
-QT = core gui qml testlib scxml
-CONFIG += testcase
-
-TARGET = tst_compiled
-CONFIG += console
-CONFIG -= app_bundle
-
-TEMPLATE = app
-
-SOURCES += \
- tst_compiled.cpp
-
-STATECHARTS = \
- ids1.scxml \
- eventnames1.scxml \
- eventnames2.scxml \
- statemachineunicodename.scxml \
- anonymousstate.scxml \
- submachineunicodename.scxml \
- datainnulldatamodel.scxml \
- initialhistory.scxml \
- connection.scxml \
- topmachine.scxml
-
-RESOURCES = tst_compiled.qrc
diff --git a/tests/auto/compiled/connection.scxml b/tests/auto/compiled/connection.scxml
index c5654e5..7dfcb3f 100644
--- a/tests/auto/compiled/connection.scxml
+++ b/tests/auto/compiled/connection.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="Connection">
diff --git a/tests/auto/compiled/eventnames1.scxml b/tests/auto/compiled/eventnames1.scxml
index 475b1b0..6ea7595 100644
--- a/tests/auto/compiled/eventnames1.scxml
+++ b/tests/auto/compiled/eventnames1.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="eventnames1" initial="a">
<state id="a">
diff --git a/tests/auto/compiled/eventnames2.scxml b/tests/auto/compiled/eventnames2.scxml
index 49a5e98..c1275e4 100644
--- a/tests/auto/compiled/eventnames2.scxml
+++ b/tests/auto/compiled/eventnames2.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="eventnames2">
<state id="a">
diff --git a/tests/auto/compiled/historyState.scxml b/tests/auto/compiled/historyState.scxml
new file mode 100644
index 0000000..c1f58ad
--- /dev/null
+++ b/tests/auto/compiled/historyState.scxml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" binding="early" xmlns:qt="http://www.qt.io/2015/02/scxml-ext" name="HistoryState" qt:editorversion="4.9.0">
+ <state id="Off">
+ <transition type="external" event="toOn" target="On">
+ </transition>
+ <transition type="external" event="toHistory" target="History_1">
+ </transition>
+ <onentry>
+ <log label="Off" expr="onentry"/>
+ </onentry>
+ <onexit>
+ <log label="Off" expr="onexit"/>
+ </onexit>
+ </state>
+ <state id="On" initial="Beta">
+ <state id="Alpha">
+ <transition type="external" event="toBeta" target="Beta">
+ </transition>
+ <onentry>
+ <log label="Alpha" expr="onentry"/>
+ </onentry>
+ <onexit>
+ <log label="Alpha" expr="onexit"/>
+ </onexit>
+ </state>
+ <state id="Beta">
+ <transition type="external" event="toDelta" target="Delta">
+ </transition>
+ <onentry>
+ <log label="Beta" expr="onentry"/>
+ </onentry>
+ <onexit>
+ <log label="Beta" expr="onexit"/>
+ </onexit>
+ </state>
+ <history type="shallow" id="History_1">
+ </history>
+ <state id="Delta">
+ <transition type="external" event="toAlpha" target="Alpha">
+ </transition>
+ <onentry>
+ <log label="Delta" expr="onentry"/>
+ </onentry>
+ <onexit>
+ <log label="Delta" expr="onexit"/>
+ </onexit>
+ </state>
+ <transition type="external" event="toOff" target="Off">
+ </transition>
+ <onentry>
+ <log label="On" expr="onentry"/>
+ </onentry>
+ <onexit>
+ <log label="On" expr="onexit"/>
+ </onexit>
+ </state>
+</scxml>
diff --git a/tests/auto/compiled/ids1.scxml b/tests/auto/compiled/ids1.scxml
index 4410678..dc08634 100644
--- a/tests/auto/compiled/ids1.scxml
+++ b/tests/auto/compiled/ids1.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="ids1">
<!-- valid IDs: -->
diff --git a/tests/auto/compiled/tst_compiled.cpp b/tests/auto/compiled/tst_compiled.cpp
index 6382328..add7051 100644
--- a/tests/auto/compiled/tst_compiled.cpp
+++ b/tests/auto/compiled/tst_compiled.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QObject>
@@ -39,6 +14,7 @@
#include "eventnames1.h"
#include "connection.h"
#include "topmachine.h"
+#include "historyState.h"
enum { SpyWaitTime = 8000 };
@@ -56,6 +32,7 @@ private Q_SLOTS:
void topMachine();
void topMachineDynamic();
void publicSignals();
+ void historyState();
};
void tst_Compiled::stateNames()
@@ -77,7 +54,7 @@ void tst_Compiled::stateNames()
QCOMPARE(stateMachine.stateNames(false), ids1States);
- for (const QString &state : qAsConst(ids1States)) {
+ for (const QString &state : std::as_const(ids1States)) {
QVariant prop = stateMachine.property(state.toUtf8().constData());
QVERIFY(!prop.isNull());
QVERIFY(prop.isValid());
@@ -257,16 +234,16 @@ void tst_Compiled::topMachine()
++doneCounter;
});
- QObject::connect(&stateMachine, &QScxmlStateMachine::invokedServicesChanged,
- [&invokableServicesCount](const QVector<QScxmlInvokableService *> &services) {
- invokableServicesCount = services.count();
+ QObject::connect(&stateMachine, &QScxmlStateMachine::invokedServicesChanged, this,
+ [&invokableServicesCount](const QList<QScxmlInvokableService *> &services) {
+ invokableServicesCount = services.size();
});
stateMachine.start();
QTRY_COMPARE(invokableServicesCount, 3);
QTRY_COMPARE(doneCounter, 3);
- QCOMPARE(stateMachine.invokedServices().count(), 3);
+ QCOMPARE(stateMachine.invokedServices().size(), 3);
QTRY_COMPARE(invokableServicesCount, 0);
}
@@ -282,16 +259,16 @@ void tst_Compiled::topMachineDynamic()
++doneCounter;
});
- QObject::connect(stateMachine.data(), &QScxmlStateMachine::invokedServicesChanged,
- [&invokableServicesCount](const QVector<QScxmlInvokableService *> &services) {
- invokableServicesCount = services.count();
+ QObject::connect(stateMachine.data(), &QScxmlStateMachine::invokedServicesChanged, this,
+ [&invokableServicesCount](const QList<QScxmlInvokableService *> &services) {
+ invokableServicesCount = services.size();
});
stateMachine->start();
QTRY_COMPARE(invokableServicesCount, 3);
QTRY_COMPARE(doneCounter, 3);
- QCOMPARE(stateMachine->invokedServices().count(), 3);
+ QCOMPARE(stateMachine->invokedServices().size(), 3);
QTRY_COMPARE(invokableServicesCount, 0);
}
@@ -306,6 +283,20 @@ void tst_Compiled::publicSignals()
QCOMPARE(aChanged.access(), QMetaMethod::Public);
}
+void tst_Compiled::historyState()
+{
+ HistoryState historyStateSM;
+ QSignalSpy stableStateSpy(&historyStateSM, SIGNAL(reachedStableState()));
+ historyStateSM.start();
+
+ stableStateSpy.wait(5000);
+ QCOMPARE(historyStateSM.activeStateNames(), QStringList(QLatin1String("Off")));
+
+ historyStateSM.submitEvent("toHistory");
+ stableStateSpy.wait(5000);
+ QCOMPARE(historyStateSM.activeStateNames(), QStringList(QLatin1String("Beta")));
+}
+
QTEST_MAIN(tst_Compiled)
#include "tst_compiled.moc"
diff --git a/tests/auto/compiled/tst_compiled.qrc b/tests/auto/compiled/tst_compiled.qrc
deleted file mode 100644
index 43d355c..0000000
--- a/tests/auto/compiled/tst_compiled.qrc
+++ /dev/null
@@ -1,7 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file>topmachine.scxml</file>
- <file>submachineA.scxml</file>
- <file>submachineB.scxml</file>
- </qresource>
-</RCC>
diff --git a/tests/auto/dynamicmetaobject/CMakeLists.txt b/tests/auto/dynamicmetaobject/CMakeLists.txt
new file mode 100644
index 0000000..517ee12
--- /dev/null
+++ b/tests/auto/dynamicmetaobject/CMakeLists.txt
@@ -0,0 +1,33 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_dynamicmetaobject Test:
+#####################################################################
+
+qt_internal_add_test(tst_dynamicmetaobject
+ SOURCES
+ tst_dynamicmetaobject.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Scxml
+)
+
+# Resources:
+set(tst_dynamicmetaobject_resource_files
+ "mediaplayer.scxml"
+ "test1.scxml"
+)
+
+qt_internal_add_resource(tst_dynamicmetaobject "tst_dynamicmetaobject"
+ PREFIX
+ "/tst_dynamicmetaobject"
+ FILES
+ ${tst_dynamicmetaobject_resource_files}
+)
+
+
+#### Keys ignored in scope 1:.:.:dynamicmetaobject.pro:<TRUE>:
+# TEMPLATE = "app"
diff --git a/tests/auto/dynamicmetaobject/dynamicmetaobject.pro b/tests/auto/dynamicmetaobject/dynamicmetaobject.pro
deleted file mode 100644
index 5ec4e5f..0000000
--- a/tests/auto/dynamicmetaobject/dynamicmetaobject.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-QT = core gui qml testlib scxml
-CONFIG += testcase
-
-TARGET = tst_dynamicmetaobject
-CONFIG += console
-CONFIG -= app_bundle
-
-TEMPLATE = app
-
-RESOURCES += tst_dynamicmetaobject.qrc
-
-SOURCES += \
- tst_dynamicmetaobject.cpp
diff --git a/tests/auto/dynamicmetaobject/mediaplayer.scxml b/tests/auto/dynamicmetaobject/mediaplayer.scxml
index ca68039..3d72033 100644
--- a/tests/auto/dynamicmetaobject/mediaplayer.scxml
+++ b/tests/auto/dynamicmetaobject/mediaplayer.scxml
@@ -1,54 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
diff --git a/tests/auto/dynamicmetaobject/test1.scxml b/tests/auto/dynamicmetaobject/test1.scxml
index 531b1d5..1bb67d4 100644
--- a/tests/auto/dynamicmetaobject/test1.scxml
+++ b/tests/auto/dynamicmetaobject/test1.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="test1" datamodel="ecmascript">
diff --git a/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp b/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp
index 83fceeb..62d610c 100644
--- a/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp
+++ b/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QObject>
@@ -77,7 +52,7 @@ void tst_DynamicMetaObject::dynamicPartCheck()
QScopedPointer<QScxmlStateMachine> stateMachine(QScxmlStateMachine::fromFile(scxmlFileName));
QVERIFY(!stateMachine.isNull());
- QVERIFY(!stateMachine->parseErrors().count());
+ QVERIFY(!stateMachine->parseErrors().size());
const QMetaObject *metaObject = stateMachine->metaObject();
diff --git a/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.qrc b/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.qrc
deleted file mode 100644
index dbc6d9a..0000000
--- a/tests/auto/dynamicmetaobject/tst_dynamicmetaobject.qrc
+++ /dev/null
@@ -1,6 +0,0 @@
-<RCC>
- <qresource prefix="/tst_dynamicmetaobject">
- <file>test1.scxml</file>
- <file>mediaplayer.scxml</file>
- </qresource>
-</RCC>
diff --git a/tests/auto/parser/CMakeLists.txt b/tests/auto/parser/CMakeLists.txt
new file mode 100644
index 0000000..0e05699
--- /dev/null
+++ b/tests/auto/parser/CMakeLists.txt
@@ -0,0 +1,113 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_scxml_parser Test:
+#####################################################################
+
+qt_internal_add_test(tst_scxml_parser
+ SOURCES
+ tst_parser.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Scxml
+)
+
+# Resources:
+set(tst_parser_resource_files
+ "data/badInitial.scxml"
+ "data/badInitial.scxml.errors"
+ "data/commentInScript.scxml"
+ "data/commentInScript.scxml.errors"
+ "data/empty.scxml"
+ "data/empty.scxml.errors"
+ "data/eventnames.scxml"
+ "data/eventnames.scxml.errors"
+ "data/ids1.scxml"
+ "data/ids1.scxml.errors"
+ "data/ids2.scxml"
+ "data/ids2.scxml.errors"
+ "data/invalidContent.scxml"
+ "data/invalidContent.scxml.errors"
+ "data/invalidRoot1.scxml"
+ "data/invalidRoot1.scxml.errors"
+ "data/invalidRoot2.scxml"
+ "data/invalidRoot2.scxml.errors"
+ "data/invalidRoot3.scxml"
+ "data/invalidRoot3.scxml.errors"
+ "data/invalidRoot6.scxml"
+ "data/invalidRoot6.scxml.errors"
+ "data/invalidUnicode1.scxml"
+ "data/invalidUnicode1.scxml.errors"
+ "data/invalidXmlHeader1.scxml"
+ "data/invalidXmlHeader1.scxml.errors"
+ "data/invalidXmlHeader2.scxml"
+ "data/invalidXmlHeader2.scxml.errors"
+ "data/invalidstatemachinename.scxml"
+ "data/invalidstatemachinename.scxml.errors"
+ "data/misplacedinvoke.scxml"
+ "data/misplacedinvoke.scxml.errors"
+ "data/namespaces1.scxml"
+ "data/namespaces1.scxml.errors"
+ "data/nestedScxml.scxml"
+ "data/nestedScxml.scxml.errors"
+ "data/noContentInInvoke1.scxml"
+ "data/noContentInInvoke1.scxml.errors"
+ "data/noContentInInvoke2.scxml"
+ "data/noContentInInvoke2.scxml.errors"
+ "data/noContentInInvoke3.scxml"
+ "data/noContentInInvoke3.scxml.errors"
+ "data/noContentInInvoke4.scxml"
+ "data/noContentInInvoke4.scxml.errors"
+ "data/prematureEndOfDocument1.scxml"
+ "data/prematureEndOfDocument1.scxml.errors"
+ "data/prematureEndOfDocument2.scxml"
+ "data/prematureEndOfDocument2.scxml.errors"
+ "data/scxml1.scxml"
+ "data/scxml1.scxml.errors"
+ "data/scxml2.scxml"
+ "data/scxml2.scxml.errors"
+ "data/syntaxErrors1.scxml"
+ "data/syntaxErrors1.scxml.errors"
+ "data/syntaxErrors10.scxml"
+ "data/syntaxErrors10.scxml.errors"
+ "data/syntaxErrors11.scxml"
+ "data/syntaxErrors11.scxml.errors"
+ "data/syntaxErrors12.scxml"
+ "data/syntaxErrors12.scxml.errors"
+ "data/syntaxErrors13.scxml"
+ "data/syntaxErrors13.scxml.errors"
+ "data/syntaxErrors14.scxml"
+ "data/syntaxErrors14.scxml.errors"
+ "data/syntaxErrors15.scxml"
+ "data/syntaxErrors15.scxml.errors"
+ "data/syntaxErrors2.scxml"
+ "data/syntaxErrors2.scxml.errors"
+ "data/syntaxErrors3.scxml"
+ "data/syntaxErrors3.scxml.errors"
+ "data/syntaxErrors4.scxml"
+ "data/syntaxErrors4.scxml.errors"
+ "data/syntaxErrors5.scxml"
+ "data/syntaxErrors5.scxml.errors"
+ "data/syntaxErrors7.scxml"
+ "data/syntaxErrors7.scxml.errors"
+ "data/syntaxErrors9.scxml"
+ "data/syntaxErrors9.scxml.errors"
+ "data/test1.scxml"
+ "data/test1.scxml.errors"
+ "data/emptyScript.scxml"
+ "data/emptyScript.scxml.errors"
+)
+
+qt_internal_add_resource(tst_scxml_parser "tst_parser"
+ PREFIX
+ "/tst_parser"
+ FILES
+ ${tst_parser_resource_files}
+)
+
+
+#### Keys ignored in scope 1:.:.:parser.pro:<TRUE>:
+# TEMPLATE = "app"
diff --git a/tests/auto/parser/data/badInitial.scxml.errors b/tests/auto/parser/data/badInitial.scxml.errors
index fa2eba9..fc0b240 100644
--- a/tests/auto/parser/data/badInitial.scxml.errors
+++ b/tests/auto/parser/data/badInitial.scxml.errors
@@ -1,2 +1,3 @@
:/tst_parser/data/badInitial.scxml:8:13: error: Unexpected element initial
+:/tst_parser/data/badInitial.scxml:7:12: error: neither src nor any content has been given in the script tag
:/tst_parser/data/badInitial.scxml:12:8: error: Error parsing SCXML file: Opening and ending tag mismatch.
diff --git a/tests/auto/parser/data/commentInScript.scxml.errors b/tests/auto/parser/data/commentInScript.scxml.errors
index d12133f..211becb 100644
--- a/tests/auto/parser/data/commentInScript.scxml.errors
+++ b/tests/auto/parser/data/commentInScript.scxml.errors
@@ -1 +1,2 @@
+:/tst_parser/data/commentInScript.scxml:6:12: error: neither src nor any content has been given in the script tag
:/tst_parser/data/commentInScript.scxml:6:0: error: Error parsing SCXML file: Premature end of document.
diff --git a/tests/auto/parser/data/emptyScript.scxml b/tests/auto/parser/data/emptyScript.scxml
new file mode 100644
index 0000000..48dd53b
--- /dev/null
+++ b/tests/auto/parser/data/emptyScript.scxml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" binding="early" xmlns:qt="http://www.qt.io/2015/02/scxml-ext" name="StateChart" qt:editorversion="11.0.1">
+ <state id="Initial">
+ <transition type="external" event="crash" target="Final_1">
+ <script>
+ </script>
+ </transition>
+ </state>
+ <final id="Final_1">
+ </final>
+</scxml>
+
diff --git a/tests/auto/parser/data/emptyScript.scxml.errors b/tests/auto/parser/data/emptyScript.scxml.errors
new file mode 100644
index 0000000..bbd55a4
--- /dev/null
+++ b/tests/auto/parser/data/emptyScript.scxml.errors
@@ -0,0 +1 @@
+:/tst_parser/data/emptyScript.scxml:5:20: error: neither src nor any content has been given in the script tag \ No newline at end of file
diff --git a/tests/auto/parser/data/eventnames.scxml b/tests/auto/parser/data/eventnames.scxml
index d5e4ff7..f95e079 100644
--- a/tests/auto/parser/data/eventnames.scxml
+++ b/tests/auto/parser/data/eventnames.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="eventnames">
<state id="a">
diff --git a/tests/auto/parser/data/eventnames.scxml.errors b/tests/auto/parser/data/eventnames.scxml.errors
index 9b4d5de..134c2ac 100644
--- a/tests/auto/parser/data/eventnames.scxml.errors
+++ b/tests/auto/parser/data/eventnames.scxml.errors
@@ -1,5 +1,5 @@
-:/tst_parser/data/eventnames.scxml:50:38: error: '.invalid' is not a valid event
-:/tst_parser/data/eventnames.scxml:51:38: error: 'invalid.' is not a valid event
-:/tst_parser/data/eventnames.scxml:39:36: error: '.invalid' is not a valid event
-:/tst_parser/data/eventnames.scxml:40:36: error: 'invalid.' is not a valid event
-:/tst_parser/data/eventnames.scxml:41:36: error: 'in valid' is not a valid event
+:/tst_parser/data/eventnames.scxml:25:38: error: '.invalid' is not a valid event
+:/tst_parser/data/eventnames.scxml:26:38: error: 'invalid.' is not a valid event
+:/tst_parser/data/eventnames.scxml:14:36: error: '.invalid' is not a valid event
+:/tst_parser/data/eventnames.scxml:15:36: error: 'invalid.' is not a valid event
+:/tst_parser/data/eventnames.scxml:16:36: error: 'in valid' is not a valid event
diff --git a/tests/auto/parser/data/ids1.scxml b/tests/auto/parser/data/ids1.scxml
index 7e8bd15..2e4dc98 100644
--- a/tests/auto/parser/data/ids1.scxml
+++ b/tests/auto/parser/data/ids1.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="ids1">
<state id="foo.bar"/>
diff --git a/tests/auto/parser/data/ids2.scxml b/tests/auto/parser/data/ids2.scxml
index 5378fcb..a2e812b 100644
--- a/tests/auto/parser/data/ids2.scxml
+++ b/tests/auto/parser/data/ids2.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="ids2">
<state id="foo.bar"/>
diff --git a/tests/auto/parser/data/ids2.scxml.errors b/tests/auto/parser/data/ids2.scxml.errors
index 3ca729e..50565fc 100644
--- a/tests/auto/parser/data/ids2.scxml.errors
+++ b/tests/auto/parser/data/ids2.scxml.errors
@@ -1 +1 @@
-:/tst_parser/data/ids2.scxml:35:19: error: '1' is not a valid XML ID
+:/tst_parser/data/ids2.scxml:10:19: error: '1' is not a valid XML ID
diff --git a/tests/auto/parser/data/namespaces1.scxml b/tests/auto/parser/data/namespaces1.scxml
index 551c130..fc5a395 100644
--- a/tests/auto/parser/data/namespaces1.scxml
+++ b/tests/auto/parser/data/namespaces1.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="namespaces1" binding="early"
xmlns:qt="http://www.qt.io/2015/02/scxml-ext" qt:editorversion="1.2.3" initial="State_1">
diff --git a/tests/auto/parser/data/scxml1.scxml b/tests/auto/parser/data/scxml1.scxml
index 9d53420..dcaaded 100644
--- a/tests/auto/parser/data/scxml1.scxml
+++ b/tests/auto/parser/data/scxml1.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="scxml" datamodel="foo">
diff --git a/tests/auto/parser/data/scxml1.scxml.errors b/tests/auto/parser/data/scxml1.scxml.errors
index 870d167..d8df9a2 100644
--- a/tests/auto/parser/data/scxml1.scxml.errors
+++ b/tests/auto/parser/data/scxml1.scxml.errors
@@ -1,2 +1,2 @@
-:/tst_parser/data/scxml1.scxml:32:36: error: Unsupported data model 'foo' in scxml
-:/tst_parser/data/scxml1.scxml:34:30: error: Unexpected element scxml
+:/tst_parser/data/scxml1.scxml:7:36: error: Unsupported data model 'foo' in scxml
+:/tst_parser/data/scxml1.scxml:9:30: error: Unexpected element scxml
diff --git a/tests/auto/parser/data/scxml2.scxml b/tests/auto/parser/data/scxml2.scxml
index 2283afd..2c1159f 100644
--- a/tests/auto/parser/data/scxml2.scxml
+++ b/tests/auto/parser/data/scxml2.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="scxml" binding="foo">
diff --git a/tests/auto/parser/data/scxml2.scxml.errors b/tests/auto/parser/data/scxml2.scxml.errors
index a9f0e30..acd6e30 100644
--- a/tests/auto/parser/data/scxml2.scxml.errors
+++ b/tests/auto/parser/data/scxml2.scxml.errors
@@ -1 +1 @@
-:/tst_parser/data/scxml2.scxml:32:34: error: Unsupperted binding type 'foo'
+:/tst_parser/data/scxml2.scxml:7:34: error: Unsupperted binding type 'foo'
diff --git a/tests/auto/parser/data/test1.scxml b/tests/auto/parser/data/test1.scxml
index 580531f..4bdd762 100644
--- a/tests/auto/parser/data/test1.scxml
+++ b/tests/auto/parser/data/test1.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="test1">
diff --git a/tests/auto/parser/data/test1.scxml.errors b/tests/auto/parser/data/test1.scxml.errors
index bb9d53e..150fab0 100644
--- a/tests/auto/parser/data/test1.scxml.errors
+++ b/tests/auto/parser/data/test1.scxml.errors
@@ -1 +1 @@
-:/tst_parser/data/test1.scxml:34:46: error: unknown state 'b' in target
+:/tst_parser/data/test1.scxml:9:46: error: unknown state 'b' in target
diff --git a/tests/auto/parser/parser.pro b/tests/auto/parser/parser.pro
deleted file mode 100644
index 3a21a71..0000000
--- a/tests/auto/parser/parser.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-QT = core gui qml testlib scxml
-CONFIG += testcase
-
-TARGET = tst_parser
-CONFIG += console
-CONFIG -= app_bundle
-
-TEMPLATE = app
-
-RESOURCES += tst_parser.qrc
-
-SOURCES += \
- tst_parser.cpp
diff --git a/tests/auto/parser/tst_parser.cpp b/tests/auto/parser/tst_parser.cpp
index 22b9059..a7b799c 100644
--- a/tests/auto/parser/tst_parser.cpp
+++ b/tests/auto/parser/tst_parser.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QObject>
@@ -65,7 +40,7 @@ void tst_Parser::error()
QFile errorFile(errorFileName);
errorFile.open(QIODevice::ReadOnly | QIODevice::Text);
const QStringList expectedErrors =
- QString::fromUtf8(errorFile.readAll()).split('\n', QString::SkipEmptyParts);
+ QString::fromUtf8(errorFile.readAll()).split('\n', Qt::SkipEmptyParts);
if (!expectedErrors.isEmpty())
QTest::ignoreMessage(QtWarningMsg, "SCXML document has errors");
@@ -73,15 +48,15 @@ void tst_Parser::error()
QScopedPointer<QScxmlStateMachine> stateMachine(QScxmlStateMachine::fromFile(scxmlFileName));
QVERIFY(!stateMachine.isNull());
- const QVector<QScxmlError> errors = stateMachine->parseErrors();
- if (errors.count() != expectedErrors.count()) {
+ const QList<QScxmlError> errors = stateMachine->parseErrors();
+ if (errors.size() != expectedErrors.size()) {
for (const QScxmlError &error : errors) {
qDebug() << error.toString();
}
}
- QCOMPARE(errors.count(), expectedErrors.count());
+ QCOMPARE(errors.size(), expectedErrors.size());
- for (int i = 0; i < errors.count(); ++i)
+ for (int i = 0; i < errors.size(); ++i)
QCOMPARE(errors.at(i).toString(), expectedErrors.at(i));
}
diff --git a/tests/auto/parser/tst_parser.qrc b/tests/auto/parser/tst_parser.qrc
deleted file mode 100644
index 8d133d2..0000000
--- a/tests/auto/parser/tst_parser.qrc
+++ /dev/null
@@ -1,84 +0,0 @@
-<RCC>
- <qresource prefix="/tst_parser">
- <file>data/badInitial.scxml.errors</file>
- <file>data/badInitial.scxml</file>
- <file>data/commentInScript.scxml.errors</file>
- <file>data/commentInScript.scxml</file>
- <file>data/empty.scxml.errors</file>
- <file>data/empty.scxml</file>
- <file>data/eventnames.scxml.errors</file>
- <file>data/eventnames.scxml</file>
- <file>data/ids1.scxml.errors</file>
- <file>data/ids1.scxml</file>
- <file>data/ids2.scxml.errors</file>
- <file>data/ids2.scxml</file>
- <file>data/invalidContent.scxml.errors</file>
- <file>data/invalidContent.scxml</file>
- <file>data/invalidRoot1.scxml.errors</file>
- <file>data/invalidRoot1.scxml</file>
- <file>data/invalidRoot2.scxml.errors</file>
- <file>data/invalidRoot2.scxml</file>
- <file>data/invalidRoot3.scxml.errors</file>
- <file>data/invalidRoot3.scxml</file>
- <file>data/invalidRoot6.scxml.errors</file>
- <file>data/invalidRoot6.scxml</file>
- <file>data/invalidstatemachinename.scxml.errors</file>
- <file>data/invalidstatemachinename.scxml</file>
- <file>data/invalidUnicode1.scxml.errors</file>
- <file>data/invalidUnicode1.scxml</file>
- <file>data/invalidXmlHeader1.scxml.errors</file>
- <file>data/invalidXmlHeader1.scxml</file>
- <file>data/invalidXmlHeader2.scxml.errors</file>
- <file>data/invalidXmlHeader2.scxml</file>
- <file>data/misplacedinvoke.scxml.errors</file>
- <file>data/misplacedinvoke.scxml</file>
- <file>data/namespaces1.scxml.errors</file>
- <file>data/namespaces1.scxml</file>
- <file>data/nestedScxml.scxml.errors</file>
- <file>data/nestedScxml.scxml</file>
- <file>data/noContentInInvoke1.scxml.errors</file>
- <file>data/noContentInInvoke1.scxml</file>
- <file>data/noContentInInvoke2.scxml.errors</file>
- <file>data/noContentInInvoke2.scxml</file>
- <file>data/noContentInInvoke3.scxml.errors</file>
- <file>data/noContentInInvoke3.scxml</file>
- <file>data/noContentInInvoke4.scxml.errors</file>
- <file>data/noContentInInvoke4.scxml</file>
- <file>data/prematureEndOfDocument1.scxml.errors</file>
- <file>data/prematureEndOfDocument1.scxml</file>
- <file>data/prematureEndOfDocument2.scxml.errors</file>
- <file>data/prematureEndOfDocument2.scxml</file>
- <file>data/scxml1.scxml.errors</file>
- <file>data/scxml1.scxml</file>
- <file>data/scxml2.scxml.errors</file>
- <file>data/scxml2.scxml</file>
- <file>data/syntaxErrors10.scxml.errors</file>
- <file>data/syntaxErrors10.scxml</file>
- <file>data/syntaxErrors11.scxml.errors</file>
- <file>data/syntaxErrors11.scxml</file>
- <file>data/syntaxErrors12.scxml.errors</file>
- <file>data/syntaxErrors12.scxml</file>
- <file>data/syntaxErrors13.scxml.errors</file>
- <file>data/syntaxErrors13.scxml</file>
- <file>data/syntaxErrors14.scxml.errors</file>
- <file>data/syntaxErrors14.scxml</file>
- <file>data/syntaxErrors15.scxml.errors</file>
- <file>data/syntaxErrors15.scxml</file>
- <file>data/syntaxErrors1.scxml.errors</file>
- <file>data/syntaxErrors1.scxml</file>
- <file>data/syntaxErrors2.scxml.errors</file>
- <file>data/syntaxErrors2.scxml</file>
- <file>data/syntaxErrors3.scxml.errors</file>
- <file>data/syntaxErrors3.scxml</file>
- <file>data/syntaxErrors4.scxml.errors</file>
- <file>data/syntaxErrors4.scxml</file>
- <file>data/syntaxErrors5.scxml.errors</file>
- <file>data/syntaxErrors5.scxml</file>
- <file>data/syntaxErrors7.scxml.errors</file>
- <file>data/syntaxErrors7.scxml</file>
- <file>data/syntaxErrors9.scxml.errors</file>
- <file>data/syntaxErrors9.scxml</file>
- <file>data/test1.scxml.errors</file>
- <file>data/test1.scxml</file>
- </qresource>
-</RCC>
diff --git a/tests/auto/qml/CMakeLists.txt b/tests/auto/qml/CMakeLists.txt
new file mode 100644
index 0000000..03f01ab
--- /dev/null
+++ b/tests/auto/qml/CMakeLists.txt
@@ -0,0 +1,10 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(TARGET Qt::StateMachineQml)
+ add_subdirectory(qqmlstatemachine)
+ add_subdirectory(qqmlstatemachinemetatype)
+endif()
+if(TARGET Qt::ScxmlQml)
+ add_subdirectory(scxmlqmlcpp)
+endif()
diff --git a/tests/auto/qml/qqmlstatemachine/CMakeLists.txt b/tests/auto/qml/qqmlstatemachine/CMakeLists.txt
new file mode 100644
index 0000000..e0e50da
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachine/CMakeLists.txt
@@ -0,0 +1,42 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_qqmlstatemachine Test:
+#####################################################################
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qqmlstatemachine
+ SOURCES
+ ../../shared/util.cpp ../../shared/util.h
+ tst_qqmlstatemachine.cpp
+ INCLUDE_DIRECTORIES
+ ../../shared
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ Qt::StateMachineQmlPrivate
+ Qt::TestPrivate
+ TESTDATA ${test_data}
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(tst_qqmlstatemachine CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qqmlstatemachine CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
diff --git a/tests/auto/qml/qqmlstatemachine/data/cppsignal.qml b/tests/auto/qml/qqmlstatemachine/data/cppsignal.qml
new file mode 100644
index 0000000..ae5e9b3
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachine/data/cppsignal.qml
@@ -0,0 +1,40 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQml
+
+import QtQml.StateMachine
+
+import CppObjectEnum 1.0
+
+StateMachine {
+ id: stateMachine
+ initialState: state0
+
+ State {
+ id: state0
+ SignalTransition {
+ targetState: state1
+ signal: _cppObject.mySignal
+ // signalState is mySignal's parameter
+ guard: signalState === CppObject.State1
+ }
+ }
+
+ State {
+ id: state1
+ SignalTransition {
+ targetState: state2
+ signal: _cppObject.mySignal
+ // signalState is mySignal's parameter
+ guard: signalState === CppObject.State2
+ }
+ onEntered: _cppObject.objectState = CppObject.State1
+ }
+
+ FinalState {
+ id: state2
+ onEntered: _cppObject.objectState = CppObject.State2
+ }
+ Component.onCompleted: stateMachine.running = true
+}
diff --git a/tests/auto/qml/qqmlstatemachine/data/signaltransition.qml b/tests/auto/qml/qqmlstatemachine/data/signaltransition.qml
new file mode 100644
index 0000000..63ceacb
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachine/data/signaltransition.qml
@@ -0,0 +1,30 @@
+// Copyright (C) 2021 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQml.StateMachine
+
+Item {
+ id: root
+
+ signal signal1()
+ signal signal2()
+
+ function getSignal1() { return root.signal1 }
+ function getSignal2() { return root.signal2 }
+
+ SignalTransition {
+ objectName: "st1"
+ guard: 1 + 1
+ }
+
+ SignalTransition {
+ objectName: "st2"
+ guard: 2 + 2
+ }
+
+ SignalTransition {
+ // Do not crash on SignalTransition without signal
+ onTriggered: () => {}
+ }
+}
diff --git a/tests/auto/qml/qqmlstatemachine/data/signaltransitionhelper.qml b/tests/auto/qml/qqmlstatemachine/data/signaltransitionhelper.qml
new file mode 100644
index 0000000..a29fcd9
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachine/data/signaltransitionhelper.qml
@@ -0,0 +1,10 @@
+// Copyright (C) 2023 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtQuick
+import QtQml.StateMachine
+
+SignalTransition {
+ // Do not crash on SignalTransition without signal
+ onTriggered: () => {}
+}
diff --git a/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp
new file mode 100644
index 0000000..6b3f5ba
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachine/tst_qqmlstatemachine.cpp
@@ -0,0 +1,168 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+#include <QQmlComponent>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QtQuick/QQuickItem>
+#include <QtStateMachineQml/private/signaltransition_p.h>
+#include <QtStateMachineQml/private/timeouttransition_p.h>
+#include <QtStateMachineQml/private/statemachine_p.h>
+#include <QtStateMachineQml/private/finalstate_p.h>
+#include <QtStateMachineQml/private/state_p.h>
+#include <QtQml/qqmlscriptstring.h>
+
+#include <QTest>
+#include <QtTest/private/qpropertytesthelper_p.h>
+#include "../../shared/util.h"
+#include "../../shared/bindableqmlutils.h"
+
+class tst_qqmlstatemachine : public QQmlDataTest
+{
+ Q_OBJECT
+public:
+ tst_qqmlstatemachine();
+
+private slots:
+ void tst_cppObjectSignal();
+ void tst_bindings();
+};
+
+
+class CppObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(ObjectState objectState READ objectState WRITE setObjectState NOTIFY objectStateChanged)
+ Q_ENUMS(ObjectState)
+public:
+ enum ObjectState {
+ State0,
+ State1,
+ State2
+ };
+
+public:
+ CppObject() {}
+
+ ObjectState objectState() const { return m_objectState; }
+ void setObjectState(ObjectState objectState) { m_objectState = objectState; emit objectStateChanged();}
+
+signals:
+ void objectStateChanged();
+ void mySignal(int signalState);
+
+private:
+ ObjectState m_objectState = State0;
+};
+
+tst_qqmlstatemachine::tst_qqmlstatemachine()
+{
+ QVERIFY(-1 != qmlRegisterUncreatableType<CppObject>("CppObjectEnum", 1, 0, "CppObject", QString()));
+}
+
+void tst_qqmlstatemachine::tst_cppObjectSignal()
+{
+ CppObject cppObject;
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("cppsignal.qml"));
+ QVERIFY2(!component.isError(), qPrintable(component.errorString()));
+
+ QQmlContext *ctxt = engine.rootContext();
+ ctxt->setContextProperty("_cppObject", &cppObject);
+ QScopedPointer<QObject> rootObject(component.create());
+ QVERIFY(rootObject != nullptr);
+
+ // wait for state machine to start
+ QTRY_VERIFY(rootObject->property("running").toBool());
+
+ // emit signal from cpp
+ emit cppObject.mySignal(CppObject::State1);
+
+ // check if the signal was propagated
+ QTRY_COMPARE(cppObject.objectState(), CppObject::State1);
+
+ // emit signal from cpp
+ emit cppObject.mySignal(CppObject::State2);
+
+ // check if the signal was propagated
+ QTRY_COMPARE(cppObject.objectState(), CppObject::State2);
+
+ // wait for state machine to finish
+ QTRY_VERIFY(!rootObject->property("running").toBool());
+}
+
+void tst_qqmlstatemachine::tst_bindings()
+{
+ SignalTransition signalTransition;
+ // Generating QQmlScriptString requires proper qml context setup, and here we
+ // use same the element that we are testing to create the testing material
+ QQmlEngine engine;
+ QQmlComponent component(&engine, testFileUrl("signaltransition.qml"));
+ std::unique_ptr<QObject> obj(component.create());
+ SignalTransition *st1 = qobject_cast<SignalTransition*>(obj->findChild<QObject*>("st1"));
+ SignalTransition *st2 = qobject_cast<SignalTransition*>(obj->findChild<QObject*>("st2"));
+ QVERIFY(st1 && st2 && (st1->guard() != st2->guard()));
+
+ // -- SignalTransition::guard
+ QTestPrivate::testReadWritePropertyBasics<SignalTransition, QQmlScriptString>(
+ signalTransition, st1->guard(), st2->guard(), "guard");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "SignalTransition::guard bindable test failed.";
+ return;
+ }
+
+ // -- SignalTransition::signal
+ // We use QML to create the test material (QJSValues that contain valid methods)
+ QVariant signal1;
+ QVariant signal2;
+ QMetaObject::invokeMethod(obj.get(), "getSignal1", Q_RETURN_ARG(QVariant, signal1));
+ QMetaObject::invokeMethod(obj.get(), "getSignal2", Q_RETURN_ARG(QVariant, signal2));
+ // The setter needs an active engine, so we use a helper component to create
+ // a helper instance for testing binding loops.
+ QQmlComponent helperComponent(&engine, testFileUrl("signaltransitionhelper.qml"));
+ // QJSValue does not implement operator== so we supply own comparator
+ QTestPrivate::testReadWritePropertyBasics<SignalTransition, QJSValue>(
+ *st1, signal1.value<QJSValue>(), signal2.value<QJSValue>(), "signal",
+ [](QJSValue d1, QJSValue d2) { return d1.strictlyEquals(d2); },
+ [](const QJSValue &val) { return QTest::toString(val); },
+ [&helperComponent]() {
+ return std::unique_ptr<SignalTransition>(
+ qobject_cast<SignalTransition*>(helperComponent.create()));
+ });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "SignalTransition::signal bindable test failed.";
+ return;
+ }
+
+ // -- TimeoutTransition::timeout
+ TimeoutTransition timeoutTransition;
+ QCOMPARE(timeoutTransition.timeout(), 1000); // the initialvalue
+ int timeout1{100};
+ int timeout2{200};
+ QTestPrivate::testReadWritePropertyBasics<TimeoutTransition, int>(
+ timeoutTransition, timeout1, timeout2, "timeout");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "TimeoutTransition::timeout bindable test failed.";
+ return;
+ }
+
+ // -- FinalState::children
+ FinalState finalState;
+ QObject object1;
+ QObject object2;
+ testManipulableQmlListBasics<FinalState, QObject*>(
+ finalState, &object1, &object2, "children");
+
+ // -- State::children
+ State state;
+ testManipulableQmlListBasics<State, QObject*>(
+ state, &object1, &object2, "children");
+
+ // -- StateMachine::children
+ StateMachine stateMachine;
+ testManipulableQmlListBasics<StateMachine, QObject*>(
+ stateMachine, &object1, &object2, "children");
+}
+
+QTEST_MAIN(tst_qqmlstatemachine)
+
+#include "tst_qqmlstatemachine.moc"
diff --git a/tests/auto/qml/qqmlstatemachinemetatype/CMakeLists.txt b/tests/auto/qml/qqmlstatemachinemetatype/CMakeLists.txt
new file mode 100644
index 0000000..a530969
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachinemetatype/CMakeLists.txt
@@ -0,0 +1,37 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_qqmlstatemachinemetatype Test:
+#####################################################################
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_qqmlstatemachinemetatype
+ SOURCES
+ ../../shared/util.cpp ../../shared/util.h
+ tst_qqmlstatemachinemetatype.cpp
+ INCLUDE_DIRECTORIES
+ ../../shared
+ LIBRARIES
+ Qt::CorePrivate
+ Qt::Gui
+ Qt::GuiPrivate
+ Qt::QmlPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_qqmlstatemachinemetatype CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_qqmlstatemachinemetatype CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
+
diff --git a/tests/auto/qml/qqmlstatemachinemetatype/data/unregisterAttachedProperties.qml b/tests/auto/qml/qqmlstatemachinemetatype/data/unregisterAttachedProperties.qml
new file mode 100644
index 0000000..ee26be6
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachinemetatype/data/unregisterAttachedProperties.qml
@@ -0,0 +1,7 @@
+// The extra import shuffles the type IDs around, so that we
+// get a different ID for the attached properties. If the attached
+// properties aren't properly cleared, this will crash.
+
+import QtQml.StateMachine 1.0
+import QtQuick 2.2
+Item { KeyNavigation.up: null }
diff --git a/tests/auto/qml/qqmlstatemachinemetatype/tst_qqmlstatemachinemetatype.cpp b/tests/auto/qml/qqmlstatemachinemetatype/tst_qqmlstatemachinemetatype.cpp
new file mode 100644
index 0000000..90ff581
--- /dev/null
+++ b/tests/auto/qml/qqmlstatemachinemetatype/tst_qqmlstatemachinemetatype.cpp
@@ -0,0 +1,38 @@
+// Copyright (C) 2020 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "../../shared/util.h"
+
+#include <qtest.h>
+#include <qqmlengine.h>
+#include <qqmlcomponent.h>
+
+#include <private/qqmlmetatype_p.h>
+#include <private/qqmlengine_p.h>
+
+class tst_qqmlstatemachinemetatype : public QQmlDataTest
+{
+ Q_OBJECT
+
+private slots:
+ void unregisterAttachedProperties()
+ {
+ qmlClearTypeRegistrations();
+
+ QQmlEngine e;
+ QQmlComponent c(&e, testFileUrl("unregisterAttachedProperties.qml"));
+ QVERIFY2(c.isReady(), qPrintable(c.errorString()));
+
+ const QQmlType attachedType = QQmlMetaType::qmlType("QtQuick/KeyNavigation",
+ QTypeRevision::fromVersion(2, 2));
+ QCOMPARE(attachedType.attachedPropertiesType(QQmlEnginePrivate::get(&e)),
+ attachedType.metaObject());
+
+ QScopedPointer<QObject> obj(c.create());
+ QVERIFY(obj);
+ }
+};
+
+QTEST_MAIN(tst_qqmlstatemachinemetatype)
+
+#include "tst_qqmlstatemachinemetatype.moc"
diff --git a/tests/auto/qml/scxmlqmlcpp/CMakeLists.txt b/tests/auto/qml/scxmlqmlcpp/CMakeLists.txt
new file mode 100644
index 0000000..e8ed535
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/CMakeLists.txt
@@ -0,0 +1,38 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(WASM)
+ return()
+endif()
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ data/*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_scxmlqmlcpp
+ SOURCES
+ ../../shared/util.cpp ../../shared/util.h
+ tst_scxmlqmlcpp.cpp
+ LIBRARIES
+ Qt::Scxml
+ Qt::ScxmlQmlPrivate
+ Qt::CorePrivate
+ Qt::TestPrivate
+ TESTDATA ${test_data}
+)
+
+qt_internal_extend_target(tst_scxmlqmlcpp CONDITION ANDROID OR IOS
+ DEFINES
+ QT_QMLTEST_DATADIR=":/data"
+)
+
+qt_internal_extend_target(tst_scxmlqmlcpp CONDITION NOT ANDROID AND NOT IOS
+ DEFINES
+ QT_QMLTEST_DATADIR="${CMAKE_CURRENT_SOURCE_DIR}/data"
+)
+
+qt6_add_statecharts(tst_scxmlqmlcpp
+ "data/topmachine.scxml"
+)
diff --git a/tests/auto/qml/scxmlqmlcpp/data/brokenstatemachine.scxml b/tests/auto/qml/scxmlqmlcpp/data/brokenstatemachine.scxml
new file mode 100644
index 0000000..479e387
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/data/brokenstatemachine.scxml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+-->
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ xmlns:qt="http://theqtcompany.com/scxml/2015/06/"
+ version="1.0"
+ name="IntentionallyBrokenStateMachine"
+ initial="working"
+>
+ I am broken
+</scxml>
diff --git a/tests/auto/qml/scxmlqmlcpp/data/smlHelper.qml b/tests/auto/qml/scxmlqmlcpp/data/smlHelper.qml
new file mode 100644
index 0000000..8deec6e
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/data/smlHelper.qml
@@ -0,0 +1,6 @@
+import QtQuick
+import QtScxml
+
+StateMachineLoader {
+ objectName: 'helper'
+}
diff --git a/tests/auto/qml/scxmlqmlcpp/data/stateMachineLoader.qml b/tests/auto/qml/scxmlqmlcpp/data/stateMachineLoader.qml
new file mode 100644
index 0000000..4014a6b
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/data/stateMachineLoader.qml
@@ -0,0 +1,8 @@
+import QtQuick
+import QtScxml
+
+Item {
+ StateMachineLoader {
+ objectName: 'sml'
+ }
+}
diff --git a/tests/auto/qml/scxmlqmlcpp/data/statemachine.scxml b/tests/auto/qml/scxmlqmlcpp/data/statemachine.scxml
new file mode 100644
index 0000000..dd99562
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/data/statemachine.scxml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+-->
+<scxml
+ xmlns="http://www.w3.org/2005/07/scxml"
+ xmlns:qt="http://theqtcompany.com/scxml/2015/06/"
+ version="1.0"
+ name="TrafficLightStateMachine"
+ initial="working"
+>
+ <state id="working" initial="yellow">
+ <state id="red">
+ <onentry>
+ <send event="startGoingGreen" delay="3s"/>
+ </onentry>
+ <transition event="startGoingGreen" target="redGoingGreen"/>
+ </state>
+
+ <state id="yellow" initial="greenGoingRed">
+ <state id="redGoingGreen">
+ <onentry>
+ <send event="goGreen" delay="1s"/>
+ </onentry>
+ <transition event="goGreen" target="green"/>
+ </state>
+
+ <state id="greenGoingRed">
+ <onentry>
+ <send event="goRed" delay="1s"/>
+ </onentry>
+ <transition event="goRed" target="red"/>
+ </state>
+ </state>
+
+ <state id="green">
+ <onentry>
+ <send event="startGoingRed" delay="3s"/>
+ </onentry>
+ <transition event="startGoingRed" target="greenGoingRed"/>
+ </state>
+
+ <transition event="smash" target="broken"/>
+ </state>
+
+ <state id="broken" initial="blinking">
+ <state id="blinking">
+ <onentry>
+ <send event="unblink" delay="1s"/>
+ </onentry>
+ <transition event="unblink" target="unblinking"/>
+ </state>
+
+ <state id="unblinking">
+ <onentry>
+ <send event="blink" delay="1s"/>
+ </onentry>
+ <transition event="blink" target="blinking"/>
+ </state>
+
+ <transition event="repair" target="working"/>
+ </state>
+</scxml>
diff --git a/tests/auto/qml/scxmlqmlcpp/data/submachineA.scxml b/tests/auto/qml/scxmlqmlcpp/data/submachineA.scxml
new file mode 100644
index 0000000..0924b2e
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/data/submachineA.scxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="SubMachine">
+ <final id="topState"/>
+</scxml>
diff --git a/tests/auto/qml/scxmlqmlcpp/data/submachineB.scxml b/tests/auto/qml/scxmlqmlcpp/data/submachineB.scxml
new file mode 100644
index 0000000..0924b2e
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/data/submachineB.scxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="SubMachine">
+ <final id="topState"/>
+</scxml>
diff --git a/tests/auto/qml/scxmlqmlcpp/data/topmachine.scxml b/tests/auto/qml/scxmlqmlcpp/data/topmachine.scxml
new file mode 100644
index 0000000..b733c6a
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/data/topmachine.scxml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="TopMachine" datamodel="ecmascript">
+ <datamodel>
+ <data id="doneCounter" expr="0"/>
+ </datamodel>
+ <state id="topState">
+ <invoke type="scxml" id="submachine.1" src="file:submachineA.scxml"/>
+ <invoke type="scxml" id="submachine.2" src="file:submachineA.scxml"/>
+ <invoke type="scxml" id="submachine.3" src="file:submachineB.scxml"/>
+ <transition event="done.invoke">
+ <assign location="doneCounter" expr="doneCounter + 1"/>
+ <if cond="doneCounter === 3">
+ <send event="goToFinal" delay="1s"/>
+ </if>
+ </transition>
+ <transition event="goToFinal" target="finalState"/>
+ </state>
+ <final id="finalState"/>
+</scxml>
diff --git a/tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp b/tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp
new file mode 100644
index 0000000..d723ee9
--- /dev/null
+++ b/tests/auto/qml/scxmlqmlcpp/tst_scxmlqmlcpp.cpp
@@ -0,0 +1,191 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "../../shared/util.h"
+
+#include <QtTest>
+#include <QtTest/private/qpropertytesthelper_p.h>
+#include <QtScxml/QScxmlStateMachine>
+#include <QtScxml/QScxmlNullDataModel>
+#include <QtScxmlQml/private/eventconnection_p.h>
+#include <QtScxmlQml/private/invokedservices_p.h>
+#include <QtScxmlQml/private/statemachineloader_p.h>
+#include "topmachine.h"
+#include <functional>
+#include <QtQml/QQmlEngine>
+#include <QtQml/QQmlComponent>
+#include <memory>
+
+class tst_scxmlqmlcpp : public QQmlDataTest
+{
+ Q_OBJECT
+
+private slots:
+ void initTestCase() override;
+
+ void eventConnectionStateMachineBinding();
+ void eventConnectionEventsBinding();
+
+ void invokedServicesStateMachineBinding();
+ void invokedServicesChildrenBinding();
+
+ void stateMachineLoaderInitialValuesBinding();
+ void stateMachineLoaderSourceStateMachineBinding();
+ void stateMachineLoaderDatamodelBinding();
+
+private:
+ QScxmlEventConnection m_eventConnection;
+ QScxmlInvokedServices m_invokedServices;
+ QScxmlStateMachineLoader m_stateMachineLoader;
+ std::unique_ptr<QScxmlStateMachine> m_stateMachine1;
+ std::unique_ptr<QScxmlStateMachine> m_stateMachine2;
+};
+
+void tst_scxmlqmlcpp::initTestCase()
+{
+ QQmlDataTest::initTestCase();
+ m_stateMachine1.reset(QScxmlStateMachine::fromFile("no_real_file_needed"));
+ m_stateMachine2.reset(QScxmlStateMachine::fromFile("no_real_file_needed"));
+}
+
+void tst_scxmlqmlcpp::eventConnectionStateMachineBinding()
+{
+ QCOMPARE(m_eventConnection.bindableStateMachine().value(), nullptr);
+ QTestPrivate::testReadWritePropertyBasics<QScxmlEventConnection, QScxmlStateMachine*>(
+ m_eventConnection, m_stateMachine1.get(), m_stateMachine2.get(), "stateMachine");
+ m_eventConnection.setStateMachine(nullptr); // tidy up
+}
+
+void tst_scxmlqmlcpp::eventConnectionEventsBinding()
+{
+ QStringList eventList1{{"event1"},{"event2"}};
+ QStringList eventList2{{"event3"},{"event4"}};
+ QCOMPARE(m_eventConnection.events(), QStringList());
+ QTestPrivate::testReadWritePropertyBasics<QScxmlEventConnection, QStringList>(
+ m_eventConnection, eventList1, eventList2, "events");
+ m_eventConnection.setEvents(QStringList()); // tidy up
+}
+
+void tst_scxmlqmlcpp::invokedServicesStateMachineBinding()
+{
+ QCOMPARE(m_invokedServices.stateMachine(), nullptr);
+ QTestPrivate::testReadWritePropertyBasics<QScxmlInvokedServices, QScxmlStateMachine*>(
+ m_invokedServices, m_stateMachine1.get(), m_stateMachine2.get(), "stateMachine");
+ m_invokedServices.setStateMachine(nullptr); // tidy up
+}
+
+void tst_scxmlqmlcpp::invokedServicesChildrenBinding()
+{
+ TopMachine topSm;
+ QScxmlInvokedServices invokedServices;
+ invokedServices.setStateMachine(&topSm);
+ QCOMPARE(invokedServices.children().size(), 0);
+ QCOMPARE(topSm.invokedServices().size(), 0);
+ // at some point during the topSm execution there are 3 invoked services
+ // of the same name ('3' filters out as '1' at QML binding)
+ topSm.start();
+ QTRY_COMPARE(topSm.invokedServices().size(), 3);
+ QCOMPARE(invokedServices.children().size(), 1);
+ // after completion invoked services drop back to 0
+ QTRY_COMPARE(topSm.invokedServices().size(), 0);
+ QCOMPARE(invokedServices.children().size(), 0);
+ // bind *to* the invokedservices property and check that we observe same changes
+ // during the topSm execution
+ QProperty<qsizetype> serviceCounter;
+ serviceCounter.setBinding([&](){ return invokedServices.children().size(); });
+ QCOMPARE(serviceCounter, 0);
+ topSm.start();
+ QTRY_COMPARE(serviceCounter, 1);
+ QCOMPARE(topSm.invokedServices().size(), 3);
+}
+
+void tst_scxmlqmlcpp::stateMachineLoaderInitialValuesBinding()
+{
+ QVariantMap values1{{"key1","value1"}, {"key2","value2"}};
+ QVariantMap values2{{"key3","value3"}, {"key4","value4"}};
+ QTestPrivate::testReadWritePropertyBasics<QScxmlStateMachineLoader, QVariantMap>(
+ m_stateMachineLoader, values1, values2, "initialValues");
+ m_stateMachineLoader.setInitialValues(QVariantMap()); // tidy up
+}
+
+void tst_scxmlqmlcpp::stateMachineLoaderSourceStateMachineBinding()
+{
+ // Test source and stateMachine together as they interact with each other
+
+ QUrl source1(testFileUrl("submachineA.scxml"));
+ QUrl source2(testFileUrl("submachineB.scxml"));
+ // The 'setSource' of the statemachineloader assumes a valid qml context
+ QQmlEngine engine;
+ const QUrl smlUrl = testFileUrl("stateMachineLoader.qml");
+ QQmlComponent component(&engine, smlUrl);
+ QVERIFY2(component.isReady(), qPrintable(component.errorString()));
+ std::unique_ptr<QObject> root(component.create());
+ QScxmlStateMachineLoader *sml =
+ qobject_cast<QScxmlStateMachineLoader*>(root->findChild<QObject*>("sml"));
+ QVERIFY(sml != nullptr);
+
+ QQmlComponent otherComponent(&engine, testFileUrl("smlHelper.qml"));
+
+ // -- StateMachineLoader::source
+ QTestPrivate::testReadWritePropertyBasics<QScxmlStateMachineLoader, QUrl>(
+ *sml, source1, source2, "source",
+ [&otherComponent]() {
+ return std::unique_ptr<QScxmlStateMachineLoader>(
+ qobject_cast<QScxmlStateMachineLoader*>(otherComponent.create()));
+ });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QScxmlStateMachineLoader::source property testing failed";
+ return;
+ }
+
+ // -- StateMachineLoader::stateMachine
+ // The statemachine can be set indirectly by setting the 'source'
+ QSignalSpy smSpy(sml, &QScxmlStateMachineLoader::stateMachineChanged);
+ QUrl sourceNonexistent(testFileUrl("file_doesnt_exist.scxml"));
+ QUrl sourceBroken(testFileUrl("brokenstatemachine.scxml"));
+
+ QVERIFY(sml->stateMachine() != nullptr);
+ QTest::ignoreMessage(QtWarningMsg,
+ qPrintable(smlUrl.toString() + ":5:5: QML StateMachineLoader: " +
+ "Cannot open '" + sourceNonexistent.toString() + "' for reading."));
+ sml->setSource(sourceNonexistent);
+ QVERIFY(sml->stateMachine() == nullptr);
+ QCOMPARE(smSpy.size(), 1);
+
+ QString sourceBrokenNoScheme;
+ if (sourceBroken.scheme() == QStringLiteral("qrc"))
+ sourceBrokenNoScheme = QStringLiteral(":") + sourceBroken.path();
+ else
+ sourceBrokenNoScheme = sourceBroken.toLocalFile();
+ QTest::ignoreMessage(QtWarningMsg,
+ qPrintable(smlUrl.toString() + ":5:5: QML StateMachineLoader: " +
+ sourceBrokenNoScheme + ":12:1: error: initial state 'working' " +
+ "not found for <scxml> element"));
+
+ QTest::ignoreMessage(QtWarningMsg,
+ "SCXML document has errors");
+ QTest::ignoreMessage(QtWarningMsg,
+ qPrintable(smlUrl.toString() + ":5:5: QML StateMachineLoader: Something " +
+ "went wrong while parsing '" + sourceBroken.toString() + "':\n"));
+ sml->setSource(sourceBroken);
+ QVERIFY(sml->stateMachine() == nullptr);
+ QCOMPARE(smSpy.size(), 1);
+
+ QProperty<bool> hasStateMachine([&](){ return sml->stateMachine() ? true : false; });
+ QVERIFY(hasStateMachine == false);
+ sml->setSource(source1);
+ QCOMPARE(smSpy.size(), 2);
+ QVERIFY(hasStateMachine == true);
+}
+
+void tst_scxmlqmlcpp::stateMachineLoaderDatamodelBinding()
+{
+ QScxmlNullDataModel model1;
+ QScxmlNullDataModel model2;
+ QTestPrivate::testReadWritePropertyBasics<QScxmlStateMachineLoader,QScxmlDataModel*>
+ (m_stateMachineLoader, &model1, &model2, "dataModel");
+ m_stateMachineLoader.setDataModel(nullptr); // tidy up
+}
+
+QTEST_MAIN(tst_scxmlqmlcpp)
+#include "tst_scxmlqmlcpp.moc"
diff --git a/tests/auto/qmltest/CMakeLists.txt b/tests/auto/qmltest/CMakeLists.txt
new file mode 100644
index 0000000..c580fde
--- /dev/null
+++ b/tests/auto/qmltest/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(TARGET Qt::StateMachineQml)
+ add_subdirectory(statemachine)
+endif()
+if(TARGET Qt::ScxmlQml)
+ add_subdirectory(scxml)
+endif()
diff --git a/tests/auto/qmltest/scxml/CMakeLists.txt b/tests/auto/qmltest/scxml/CMakeLists.txt
new file mode 100644
index 0000000..72ebdc9
--- /dev/null
+++ b/tests/auto/qmltest/scxml/CMakeLists.txt
@@ -0,0 +1,30 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_scxmlqml
+ GUI
+ QMLTEST
+ SOURCES
+ tst_scxmlqml.cpp
+ LIBRARIES
+ Qt::Gui
+ TESTDATA ${test_data}
+)
+
+# Resources:
+set(tst_scxmlqml_resource_files
+ "statemachine.scxml"
+)
+
+qt_internal_add_resource(tst_scxmlqml "tst_scxmlqml"
+ PREFIX
+ "/"
+ FILES
+ ${tst_scxmlqml_resource_files}
+)
diff --git a/tests/auto/qmltest/scxml/statemachine.scxml b/tests/auto/qmltest/scxml/statemachine.scxml
new file mode 100644
index 0000000..11621fa
--- /dev/null
+++ b/tests/auto/qmltest/scxml/statemachine.scxml
@@ -0,0 +1,20 @@
+<?xml version="1.0" ?>
+<!--
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+-->
+<scxml xmlns="http://www.w3.org/2005/07/scxml"
+ version="1.0"
+ name="ColorStepper"
+ initial="red">
+
+ <state id = "red">
+ <transition target="yellow" event="step"/>
+ </state>
+
+ <state id = "yellow">
+ <transition target="green" event="step"/>
+ </state>
+
+ <final id="green"/>
+</scxml>
diff --git a/tests/auto/qmltest/scxml/tst_dynamic.qml b/tests/auto/qmltest/scxml/tst_dynamic.qml
new file mode 100644
index 0000000..fc6a75e
--- /dev/null
+++ b/tests/auto/qmltest/scxml/tst_dynamic.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest 1.15
+import QtScxml 5.15
+
+TestCase {
+ id: testCase
+
+ StateMachineLoader {
+ id: loader
+ source: "qrc:///statemachine.scxml"
+ }
+
+ function test_overloaded_calls_with_dynamic_statemachine()
+ {
+ // This test calls "submitEvent" invokable function which has 3
+ // overloads, differentiated both by parameter types and amounts.
+ // Test verifies that the overloads are callable while using
+ // a dynamic statemachine which has a dynamic metaobject under the hood
+ tryVerify(() => loader.stateMachine.activeStateNames()[0] === "red", 200)
+ loader.stateMachine.submitEvent("step")
+ tryVerify(() => loader.stateMachine.activeStateNames()[0] === "yellow", 200)
+ loader.stateMachine.submitEvent("step", "somedata")
+ tryVerify(() => loader.stateMachine.activeStateNames()[0] === "green", 200)
+ }
+}
diff --git a/tests/auto/qmltest/scxml/tst_scxmlqml.cpp b/tests/auto/qmltest/scxml/tst_scxmlqml.cpp
new file mode 100644
index 0000000..cd141cb
--- /dev/null
+++ b/tests/auto/qmltest/scxml/tst_scxmlqml.cpp
@@ -0,0 +1,5 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQuickTest/quicktest.h>
+QUICK_TEST_MAIN(tst_scxmlqml)
diff --git a/tests/auto/qmltest/statemachine/CMakeLists.txt b/tests/auto/qmltest/statemachine/CMakeLists.txt
new file mode 100644
index 0000000..c4a0bc4
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/CMakeLists.txt
@@ -0,0 +1,18 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+# Collect test data
+file(GLOB_RECURSE test_data_glob
+ RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}
+ ${CMAKE_CURRENT_SOURCE_DIR}/tst_*)
+list(APPEND test_data ${test_data_glob})
+
+qt_internal_add_test(tst_statemachineqml
+ GUI
+ QMLTEST
+ SOURCES
+ tst_statemachineqml.cpp
+ LIBRARIES
+ Qt::Gui
+ TESTDATA ${test_data}
+)
diff --git a/tests/auto/qmltest/statemachine/tst_anonymousstate.qml b/tests/auto/qmltest/statemachine/tst_anonymousstate.qml
new file mode 100644
index 0000000..1e46d92
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_anonymousstate.qml
@@ -0,0 +1,16 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ StateMachine {
+ State {
+ id: stateId
+ }
+ initialState: stateId
+ }
+ name: "anonymousState"
+ // no real tests, just make sure it runs
+}
diff --git a/tests/auto/qmltest/statemachine/tst_enumguard.qml b/tests/auto/qmltest/statemachine/tst_enumguard.qml
new file mode 100644
index 0000000..878abd9
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_enumguard.qml
@@ -0,0 +1,60 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ id: testCase
+ StateMachine {
+ id: machine
+ initialState: startState
+ State {
+ id: startState
+ SignalTransition {
+ id: signalTrans
+ signal: testCase.mysignal
+ guard: alignment === QState.ParallelStates
+ targetState: finalState
+ }
+ }
+ FinalState {
+ id: finalState
+ }
+ }
+
+ SignalSpy {
+ id: finalStateActive
+ target: finalState
+ signalName: "activeChanged"
+ }
+
+ signal mysignal(int alignment)
+
+ name: "testEnumGuard"
+ function test_enumGuard()
+ {
+ // Start statemachine, should not have reached finalState yet.
+ machine.start()
+ tryCompare(finalStateActive, "count", 0)
+ tryCompare(machine, "running", true)
+
+ // Emit the signalTrans.signal which will evaluate the guard. The
+ // guard should return true, finalState be reached and the
+ // statemachine be stopped.
+ testCase.mysignal(QState.ParallelStates)
+ tryCompare(finalStateActive, "count", 1)
+ tryCompare(machine, "running", false)
+
+ // Restart machine.
+ machine.start()
+ tryCompare(machine, "running", true)
+ tryCompare(finalStateActive, "count", 2)
+
+ // Emit signal that makes the signalTrans.guard return false. The
+ // finalState should not have been triggered.
+ testCase.mysignal(QState.ExclusiveStates)
+ tryCompare(finalStateActive, "count", 2)
+ tryCompare(machine, "running", true)
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_guardcondition.qml b/tests/auto/qmltest/statemachine/tst_guardcondition.qml
new file mode 100644
index 0000000..3aba93f
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_guardcondition.qml
@@ -0,0 +1,71 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ id: testCase
+ StateMachine {
+ id: machine
+ initialState: startState
+ State {
+ id: startState
+ SignalTransition {
+ id: signalTrans
+ signal: testCase.mysignal
+ guard: mystr == "test1"
+ targetState: finalState
+ }
+ }
+ FinalState {
+ id: finalState
+ }
+ }
+
+ SignalSpy {
+ id: finalStateActive
+ target: finalState
+ signalName: "activeChanged"
+ }
+
+ signal mysignal(string mystr, bool mybool, int myint)
+
+ name: "testGuardCondition"
+ function test_guardCondition()
+ {
+ // Start statemachine, should not have reached finalState yet.
+ machine.start()
+ tryCompare(finalStateActive, "count", 0)
+ tryCompare(machine, "running", true)
+
+ // Emit the signalTrans.signal which will evaluate the guard. The
+ // guard should return true, finalState be reached and the
+ // statemachine be stopped.
+ testCase.mysignal("test1", true, 2)
+ tryCompare(finalStateActive, "count", 1)
+ tryCompare(machine, "running", false)
+
+ // Restart machine.
+ machine.start()
+ tryCompare(machine, "running", true)
+ tryCompare(finalStateActive, "count", 2)
+
+ // Emit signal that makes the signalTrans.guard return false. The
+ // finalState should not have been triggered.
+ testCase.mysignal("test2", true, 2)
+ tryCompare(finalStateActive, "count", 2)
+ tryCompare(machine, "running", true)
+
+ // Change the guard in javascript to test that boolean true/false
+ // works as expected.
+ signalTrans.guard = false;
+ testCase.mysignal("test1", true, 2)
+ tryCompare(finalStateActive, "count", 2)
+ tryCompare(machine, "running", true)
+ signalTrans.guard = true;
+ testCase.mysignal("test1", true, 2)
+ tryCompare(finalStateActive, "count", 3)
+ tryCompare(machine, "running", false)
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_historystate.qml b/tests/auto/qmltest/statemachine/tst_historystate.qml
new file mode 100644
index 0000000..dbe619d
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_historystate.qml
@@ -0,0 +1,64 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+
+ StateMachine {
+ id: stateMachine
+ initialState: historyState1
+
+ State {
+ id: state1
+ SignalTransition {
+ id: st1
+ targetState: state2
+ }
+ }
+
+ State {
+ id: state2
+ initialState: historyState2
+ HistoryState {
+ id: historyState2
+ defaultState: state21
+ }
+ State {
+ id: state21
+ }
+ }
+
+ HistoryState {
+ id: historyState1
+ defaultState: state1
+ }
+ }
+
+ SignalSpy {
+ id: state1SpyActive
+ target: state1
+ signalName: "activeChanged"
+ }
+
+ SignalSpy {
+ id: state2SpyActive
+ target: state2
+ signalName: "activeChanged"
+ }
+
+
+ function test_historyStateAsInitialState()
+ {
+ stateMachine.start();
+ tryCompare(stateMachine, "running", true);
+ tryCompare(state1SpyActive, "count" , 1);
+ tryCompare(state2SpyActive, "count" , 0);
+ st1.invoke();
+ tryCompare(state1SpyActive, "count" , 2);
+ tryCompare(state2SpyActive, "count" , 1);
+ tryCompare(state21, "active", true);
+ tryCompare(state1, "active", false);
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_initialstate.qml b/tests/auto/qmltest/statemachine/tst_initialstate.qml
new file mode 100644
index 0000000..fb12cc5
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_initialstate.qml
@@ -0,0 +1,27 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ StateMachine {
+ id: myStateMachine
+ initialState: myState;
+ running: true
+ State {
+ id: myState
+ }
+ }
+
+ name: "initialStateTest"
+ function test_initialState() {
+ tryCompare(myStateMachine, "running", true);
+ compare(myState.active, true);
+ myStateMachine.running = false;
+ tryCompare(myStateMachine, "running", false);
+ myStateMachine.running = true;
+ tryCompare(myStateMachine, "running", true);
+ tryCompare(myState, "active", true);
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_nestedinitialstates.qml b/tests/auto/qmltest/statemachine/tst_nestedinitialstates.qml
new file mode 100644
index 0000000..a37e1e7
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_nestedinitialstates.qml
@@ -0,0 +1,35 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ StateMachine {
+ id: myStateMachine
+ initialState: parentState
+ State {
+ id: parentState
+ initialState: childState1
+ State {
+ id: childState1
+ }
+ State {
+ id: childState2
+ }
+ }
+ }
+ name: "nestedInitalStates"
+
+ function test_nestedInitalStates() {
+ compare(myStateMachine.running, false);
+ compare(parentState.active, false);
+ compare(childState1.active, false);
+ compare(childState2.active, false);
+ myStateMachine.start();
+ tryCompare(myStateMachine, "running", true);
+ tryCompare(parentState, "active", true);
+ tryCompare(childState1, "active", true);
+ compare(childState2.active, false);
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_nestedstatemachine.qml b/tests/auto/qmltest/statemachine/tst_nestedstatemachine.qml
new file mode 100644
index 0000000..2164ad0
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_nestedstatemachine.qml
@@ -0,0 +1,41 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ StateMachine {
+ id: myStateMachine
+ initialState: parentState
+ State {
+ id: parentState
+ initialState: childStateMachine
+ StateMachine {
+ id: childStateMachine
+ initialState: childState2
+ State {
+ id: childState1
+ }
+ State {
+ id: childState2
+ }
+ }
+ }
+ }
+ name: "nestedStateMachine"
+
+ function test_nestedStateMachine() {
+ compare(myStateMachine.running, false);
+ compare(parentState.active, false);
+ compare(childStateMachine.running, false);
+ compare(childState1.active, false);
+ compare(childState2.active, false);
+ myStateMachine.start();
+ tryCompare(myStateMachine, "running", true);
+ tryCompare(parentState, "active", true);
+ tryCompare(childStateMachine, "running", true);
+ tryCompare(childState1, "active", false);
+ tryCompare(childState2, "active", true);
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_parallelmachine.qml b/tests/auto/qmltest/statemachine/tst_parallelmachine.qml
new file mode 100644
index 0000000..5e08524
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_parallelmachine.qml
@@ -0,0 +1,56 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ StateMachine {
+ id: myStateMachine
+ initialState: rootState
+ State {
+ id: rootState
+ childMode: State.ParallelStates
+ State {
+ id: childState1
+ childMode: State.ParallelStates
+ State {
+ id: childState11
+ }
+ State {
+ id: childState12
+ }
+ }
+ State {
+ id: childState2
+ initialState: childState21
+ State {
+ id: childState21
+ }
+ State {
+ id: childState22
+ }
+ }
+ }
+ }
+ name: "nestedParallelMachineStates"
+
+ function test_nestedInitalStates() {
+ // uncomment me after vm problems are fixed.
+ // compare(myStateMachine.running, false);
+ compare(childState1.active, false);
+ compare(childState11.active, false);
+ compare(childState12.active, false);
+ compare(childState2.active, false);
+ compare(childState21.active, false);
+ compare(childState22.active, false);
+ myStateMachine.start();
+ tryCompare(myStateMachine, "running", true);
+ tryCompare(childState1, "active", true);
+ tryCompare(childState11, "active", true);
+ tryCompare(childState12, "active", true);
+ tryCompare(childState2, "active", true);
+ tryCompare(childState21, "active", true);
+ tryCompare(childState22, "active", false);
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_signaltransition.qml b/tests/auto/qmltest/statemachine/tst_signaltransition.qml
new file mode 100644
index 0000000..9f3d462
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_signaltransition.qml
@@ -0,0 +1,46 @@
+// Copyright (C) 2017 Ford Motor Company
+// Copyright (C) 2017 The Qt Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ id: testCase
+ StateMachine {
+ id: machine
+ initialState: startState
+ State {
+ id: startState
+ SignalTransition {
+ id: signalTrans
+ signal: testCase.onMysignal
+ targetState: finalState
+ }
+ }
+ FinalState {
+ id: finalState
+ }
+ }
+
+ SignalSpy {
+ id: finalStateActive
+ target: finalState
+ signalName: "activeChanged"
+ }
+
+ signal mysignal()
+
+ name: "testSignalTransition"
+ function test_signalTransition()
+ {
+ // Start statemachine, should not have reached finalState yet.
+ machine.start()
+ tryCompare(finalStateActive, "count", 0)
+ tryCompare(machine, "running", true)
+
+ testCase.mysignal()
+ tryCompare(finalStateActive, "count", 1)
+ tryCompare(machine, "running", false)
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_statemachineqml.cpp b/tests/auto/qmltest/statemachine/tst_statemachineqml.cpp
new file mode 100644
index 0000000..0c2c8cb
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_statemachineqml.cpp
@@ -0,0 +1,5 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtQuickTest/quicktest.h>
+QUICK_TEST_MAIN(tst_statemachineqml)
diff --git a/tests/auto/qmltest/statemachine/tst_trafficlight.qml b/tests/auto/qmltest/statemachine/tst_trafficlight.qml
new file mode 100644
index 0000000..9a81ccc
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_trafficlight.qml
@@ -0,0 +1,119 @@
+// Copyright (C) 2016 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ StateMachine {
+ id: machine
+ initialState: red
+ FinalState {
+ id: finalState
+ }
+
+ State {
+ id: red
+ initialState: justRed
+ State {
+ id: justRed
+ SignalTransition {
+ id: e1
+ targetState: waitingForGreen
+ }
+ SignalTransition {
+ id: finalSignal
+ targetState: finalState
+ }
+ }
+ State {
+ id: waitingForGreen
+ TimeoutTransition {
+ id: e2
+ targetState: yellowred
+ timeout: 30
+ }
+ }
+ }
+ State {
+ id: yellowred
+ TimeoutTransition {
+ id: e3
+ targetState: green
+ timeout: 10
+ }
+ }
+ State {
+ id: green
+ TimeoutTransition {
+ id: e4
+ targetState: yellow
+ timeout: 50
+ }
+ }
+ State {
+ id: yellow
+ TimeoutTransition {
+ id: e5
+ targetState: red
+ timeout: 10
+ }
+ }
+ }
+
+ SignalSpy {
+ id: machineSpyRunning
+ target: machine
+ signalName: "runningChanged"
+ }
+
+ SignalSpy {
+ id: redSpyActive
+ target: red
+ signalName: "activeChanged"
+ }
+
+ SignalSpy {
+ id: yellowredSpyActive
+ target: yellowred
+ signalName: "activeChanged"
+ }
+
+ SignalSpy {
+ id: greenSpyActive
+ target: green
+ signalName: "activeChanged"
+ }
+
+ SignalSpy {
+ id: yellowSpyActive
+ target: yellow
+ signalName: "activeChanged"
+ }
+
+
+ name: "testTrafficLight"
+ function test_trafficLight()
+ {
+ var i = 1;
+ machine.start();
+ tryCompare(machine, "running", true);
+ tryCompare(machineSpyRunning, "count", 1);
+ tryCompare(redSpyActive, "count", 1);
+ for (; i <= 5; ++i) {
+ e1.invoke();
+ tryCompare(yellowredSpyActive, "count", i * 2);
+ tryCompare(greenSpyActive, "count", i * 2);
+ tryCompare(redSpyActive, "count", i * 2 + 1);
+ tryCompare(yellowSpyActive, "count", i * 2);
+ }
+ finalSignal.guard = false;
+ finalSignal.invoke();
+ wait(100);
+ tryCompare(machine, "running", true);
+ finalSignal.guard = true;
+ finalSignal.invoke();
+ tryCompare(machine, "running", false);
+ tryCompare(redSpyActive, "count", i * 2);
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_triggeredArguments1.qml b/tests/auto/qmltest/statemachine/tst_triggeredArguments1.qml
new file mode 100644
index 0000000..08ae498
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_triggeredArguments1.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ id: testCase
+
+ property string mystr
+ property bool mybool
+ property int myint
+
+ StateMachine {
+ id: machine
+ initialState: startState
+ running: true
+ State {
+ id: startState
+ SignalTransition {
+ id: signalTrans
+ signal: testCase.mysignal
+ onTriggered: (mystr, mybool, myint) => {
+ testCase.mystr = mystr
+ testCase.mybool = mybool
+ testCase.myint = myint
+ }
+ targetState: finalState
+ }
+ }
+ FinalState {
+ id: finalState
+ }
+ }
+
+ signal mysignal(string mystr, bool mybool, int myint)
+
+ name: "testTriggeredArguments1"
+ function test_triggeredArguments()
+ {
+ tryCompare(startState, "active", true)
+
+ // Emit the signalTrans.signal
+ testCase.mysignal("test1", true, 2)
+ compare(testCase.mystr, "test1")
+ compare(testCase.mybool, true)
+ compare(testCase.myint, 2)
+ }
+}
diff --git a/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml b/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml
new file mode 100644
index 0000000..57d6176
--- /dev/null
+++ b/tests/auto/qmltest/statemachine/tst_triggeredArguments2.qml
@@ -0,0 +1,49 @@
+// Copyright (C) 2017 Ford Motor Company
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+import QtTest
+import QtQml.StateMachine
+
+TestCase {
+ id: testCase
+
+ property string mystr
+ property bool mybool
+ property int myint
+
+ StateMachine {
+ id: machine
+ initialState: startState
+ running: true
+ State {
+ id: startState
+ SignalTransition {
+ id: signalTrans
+ signal: testCase.mysignal
+ onTriggered: function(strarg, boolarg, intarg) {
+ testCase.mystr = strarg
+ testCase.mybool = boolarg
+ testCase.myint = intarg
+ }
+ targetState: finalState
+ }
+ }
+ FinalState {
+ id: finalState
+ }
+ }
+
+ signal mysignal(string mystr, bool mybool, int myint)
+
+ name: "testTriggeredArguments2"
+ function test_triggeredArguments()
+ {
+ tryCompare(startState, "active", true)
+
+ // Emit the signalTrans.signal
+ testCase.mysignal("test1", true, 2)
+ compare(testCase.mystr, "test1")
+ compare(testCase.mybool, true)
+ compare(testCase.myint, 2)
+ }
+}
diff --git a/tests/auto/qstatemachine/CMakeLists.txt b/tests/auto/qstatemachine/CMakeLists.txt
new file mode 100644
index 0000000..0841afe
--- /dev/null
+++ b/tests/auto/qstatemachine/CMakeLists.txt
@@ -0,0 +1,8 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+if (QT_FEATURE_qeventtransition)
+ add_subdirectory(qstate)
+ add_subdirectory(qstatemachine)
+endif()
diff --git a/tests/auto/qstatemachine/qstate/CMakeLists.txt b/tests/auto/qstatemachine/qstate/CMakeLists.txt
new file mode 100644
index 0000000..6fb9f08
--- /dev/null
+++ b/tests/auto/qstatemachine/qstate/CMakeLists.txt
@@ -0,0 +1,14 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_qstate Test:
+#####################################################################
+
+qt_internal_add_test(tst_qstate
+ SOURCES
+ tst_qstate.cpp
+ LIBRARIES
+ Qt::StateMachine
+)
diff --git a/tests/auto/qstatemachine/qstate/tst_qstate.cpp b/tests/auto/qstatemachine/qstate/tst_qstate.cpp
new file mode 100644
index 0000000..27e0e89
--- /dev/null
+++ b/tests/auto/qstatemachine/qstate/tst_qstate.cpp
@@ -0,0 +1,350 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+
+#include "qeventtransition.h"
+#include "qhistorystate.h"
+#include "qstate.h"
+#include "qstatemachine.h"
+#include "qsignaltransition.h"
+
+class tst_QState : public QObject
+{
+ Q_OBJECT
+
+private slots:
+ void assignProperty();
+ void assignPropertyTwice();
+ void historyInitialState();
+ void transitions();
+ void privateSignals();
+ void parallelStateAndInitialState();
+};
+
+class TestClass: public QObject
+{
+ Q_OBJECT
+public:
+ TestClass() : called(false) {}
+ bool called;
+
+public slots:
+ void slot() { called = true; }
+
+
+};
+
+void tst_QState::assignProperty()
+{
+ QStateMachine machine;
+
+ QObject object;
+ object.setProperty("fooBar", 10);
+
+ QState *s1 = new QState(&machine);
+ s1->assignProperty(&object, "fooBar", 20);
+
+ machine.setInitialState(s1);
+ machine.start();
+
+ QTRY_COMPARE(object.property("fooBar").toInt(), 20);
+}
+
+void tst_QState::assignPropertyTwice()
+{
+ QStateMachine machine;
+
+ QObject object;
+ object.setProperty("fooBar", 10);
+
+ QState *s1 = new QState(&machine);
+ s1->assignProperty(&object, "fooBar", 20);
+ s1->assignProperty(&object, "fooBar", 30);
+
+ machine.setInitialState(s1);
+ machine.start();
+
+ QTRY_COMPARE(object.property("fooBar").toInt(), 30);
+}
+
+class EventTestTransition: public QAbstractTransition
+{
+public:
+ EventTestTransition(QEvent::Type type, QState *targetState)
+ : QAbstractTransition(), m_type(type)
+ {
+ setTargetState(targetState);
+ }
+
+protected:
+ bool eventTest(QEvent *e) override
+ {
+ return e->type() == m_type;
+ }
+
+ void onTransition(QEvent *) override {}
+
+private:
+ QEvent::Type m_type;
+
+};
+
+void tst_QState::historyInitialState()
+{
+ QStateMachine machine;
+
+ QState *s1 = new QState(&machine);
+
+ QState *s2 = new QState(&machine);
+ QHistoryState *h1 = new QHistoryState(s2);
+
+ s2->setInitialState(h1);
+
+ QState *s3 = new QState(s2);
+ h1->setDefaultState(s3);
+
+ QState *s4 = new QState(s2);
+
+ s1->addTransition(new EventTestTransition(QEvent::User, s2));
+ s2->addTransition(new EventTestTransition(QEvent::User, s1));
+ s3->addTransition(new EventTestTransition(QEvent::Type(QEvent::User+1), s4));
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QTRY_VERIFY(machine.configuration().contains(s3));
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QTRY_VERIFY(machine.configuration().contains(s3));
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User+1)));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QTRY_VERIFY(machine.configuration().contains(s4));
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QTRY_VERIFY(machine.configuration().contains(s4));
+}
+
+void tst_QState::transitions()
+{
+ QState s1;
+ QState s2;
+
+ QVERIFY(s1.transitions().isEmpty());
+
+ QAbstractTransition *t1 = s1.addTransition(this, SIGNAL(destroyed()), &s2);
+ QAbstractTransition *t1_1 = s1.addTransition(this, &tst_QState::destroyed, &s2);
+ QVERIFY(t1 != 0);
+ QVERIFY(t1_1 != 0);
+ QCOMPARE(s1.transitions().size(), 2);
+ QCOMPARE(s1.transitions().first(), t1);
+ QCOMPARE(s1.transitions().last(), t1_1);
+ QVERIFY(s2.transitions().isEmpty());
+
+ s1.removeTransition(t1);
+ s1.removeTransition(t1_1); // Releases ownership!
+ delete t1_1; // So, delete manually.
+ QVERIFY(s1.transitions().isEmpty());
+
+ s1.addTransition(t1);
+ QCOMPARE(s1.transitions().size(), 1);
+ QCOMPARE(s1.transitions().first(), t1);
+
+ QAbstractTransition *t2 = new QEventTransition(&s1);
+ QCOMPARE(s1.transitions().size(), 2);
+ QVERIFY(s1.transitions().contains(t1));
+ QVERIFY(s1.transitions().contains(t2));
+
+ // Transitions from child states should not be reported.
+ QState *s21 = new QState(&s2);
+ QAbstractTransition *t3 = s21->addTransition(this, SIGNAL(destroyed()), &s2);
+ QVERIFY(s2.transitions().isEmpty());
+ QCOMPARE(s21->transitions().size(), 1);
+ QCOMPARE(s21->transitions().first(), t3);
+}
+
+class MyState : public QState
+{
+ Q_OBJECT
+public:
+ MyState(QState *parent = 0)
+ : QState(parent)
+ {
+
+ }
+
+ void emitPrivateSignals()
+ {
+ // These deliberately do not compile
+// emit entered();
+// emit exited();
+//
+// emit entered(QPrivateSignal());
+// emit exited(QPrivateSignal());
+//
+// emit entered(QAbstractState::QPrivateSignal());
+// emit exited(QAbstractState::QPrivateSignal());
+ }
+
+};
+
+class MyTransition : public QSignalTransition
+{
+ Q_OBJECT
+public:
+ MyTransition(QObject * sender, const char * signal, QState *sourceState = 0)
+ : QSignalTransition(sender, signal, sourceState)
+ {
+
+ }
+
+ void emitPrivateSignals()
+ {
+ // These deliberately do not compile
+// emit triggered();
+//
+// emit triggered(QPrivateSignal());
+//
+// emit triggered(QAbstractTransition::QPrivateSignal());
+ }
+};
+
+class SignalConnectionTester : public QObject
+{
+ Q_OBJECT
+public:
+ SignalConnectionTester(QObject *parent = 0)
+ : QObject(parent), testPassed(false)
+ {
+
+ }
+
+public Q_SLOTS:
+ void testSlot()
+ {
+ testPassed = true;
+ }
+
+public:
+ bool testPassed;
+};
+
+class TestTrigger : public QObject
+{
+ Q_OBJECT
+public:
+ TestTrigger(QObject *parent = 0)
+ : QObject(parent)
+ {
+
+ }
+
+ void emitTrigger()
+ {
+ emit trigger();
+ }
+
+signals:
+ void trigger();
+};
+
+void tst_QState::privateSignals()
+{
+ QStateMachine machine;
+
+ QState *s1 = new QState(&machine);
+ MyState *s2 = new MyState(&machine);
+
+ TestTrigger testTrigger;
+
+ MyTransition *t1 = new MyTransition(&testTrigger, SIGNAL(trigger()), s1);
+ s1->addTransition(t1);
+ t1->setTargetState(s2);
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ SignalConnectionTester s1Tester;
+ SignalConnectionTester s2Tester;
+ SignalConnectionTester t1Tester;
+
+ QObject::connect(s1, &QState::exited, &s1Tester, &SignalConnectionTester::testSlot);
+ QObject::connect(s2, &QState::entered, &s2Tester, &SignalConnectionTester::testSlot);
+ QObject::connect(t1, &QSignalTransition::triggered, &t1Tester, &SignalConnectionTester::testSlot);
+
+ testTrigger.emitTrigger();
+
+ QCoreApplication::processEvents();
+
+ QTRY_VERIFY(s1Tester.testPassed);
+ QTRY_VERIFY(s2Tester.testPassed);
+ QTRY_VERIFY(t1Tester.testPassed);
+
+}
+
+void tst_QState::parallelStateAndInitialState()
+{
+ QStateMachine machine;
+
+ { // setting an initial state on a parallel state:
+ QState a(QState::ParallelStates, &machine);
+ QState b(&a);
+ QVERIFY(!a.initialState());
+ const QString warning
+ = QString::asprintf("QState::setInitialState: ignoring attempt to set initial state of parallel state group %p", &a);
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ a.setInitialState(&b); // should produce a warning and do nothing.
+ QVERIFY(!a.initialState());
+ }
+
+ { // setting the child-mode from ExclusiveStates to ParallelStates should remove the initial state:
+ QState a(QState::ExclusiveStates, &machine);
+ QState b(&a);
+ a.setInitialState(&b);
+ QCOMPARE(a.initialState(), &b);
+ const QString warning
+ = QString::asprintf("QState::setChildMode: setting the child-mode of state %p to "
+ "parallel removes the initial state", &a);
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ a.setChildMode(QState::ParallelStates); // should produce a warning and remove the initial state
+ QVERIFY(!a.initialState());
+ QCOMPARE(a.childMode(), QState::ParallelStates);
+ }
+}
+
+QTEST_MAIN(tst_QState)
+#include "tst_qstate.moc"
diff --git a/tests/auto/qstatemachine/qstatemachine/CMakeLists.txt b/tests/auto/qstatemachine/qstatemachine/CMakeLists.txt
new file mode 100644
index 0000000..3631ad1
--- /dev/null
+++ b/tests/auto/qstatemachine/qstatemachine/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_qstatemachine Test:
+#####################################################################
+
+qt_internal_add_test(tst_qstatemachine
+ SOURCES
+ tst_qstatemachine.cpp
+ LIBRARIES
+ Qt::StateMachine
+ Qt::StateMachinePrivate
+ Qt::TestPrivate
+)
+
+## Scopes:
+#####################################################################
+
+qt_internal_extend_target(tst_qstatemachine CONDITION TARGET Qt::Widgets
+ LIBRARIES
+ Qt::Widgets
+)
diff --git a/tests/auto/qstatemachine/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/qstatemachine/tst_qstatemachine.cpp
new file mode 100644
index 0000000..f01dd1f
--- /dev/null
+++ b/tests/auto/qstatemachine/qstatemachine/tst_qstatemachine.cpp
@@ -0,0 +1,7054 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest/QtTest>
+#include <QtTest/private/qpropertytesthelper_p.h>
+#include <QtCore/QCoreApplication>
+#ifndef QT_NO_WIDGETS
+#include <QtWidgets/QPushButton>
+#include <QtWidgets/QGraphicsScene>
+#include <QtWidgets/QGraphicsSceneEvent>
+#include <QtWidgets/QGraphicsTextItem>
+#endif
+
+#include "qeventtransition.h"
+#include "qfinalstate.h"
+#include "qstatemachine.h"
+#include "qstate.h"
+#include "qhistorystate.h"
+#include "qsignaltransition.h"
+#if QT_CONFIG(qeventtransition)
+#include "qkeyeventtransition.h"
+#include "qmouseeventtransition.h"
+#endif
+#include "private/qstate_p.h"
+#include "private/qstatemachine_p.h"
+
+static int globalTick;
+
+// Run exec for a maximum of TIMEOUT msecs
+#define QCOREAPPLICATION_EXEC(TIMEOUT) \
+{ \
+ QTimer timer; \
+ timer.setSingleShot(true); \
+ timer.setInterval(TIMEOUT); \
+ timer.start(); \
+ connect(&timer, SIGNAL(timeout()), QCoreApplication::instance(), SLOT(quit())); \
+ QCoreApplication::exec(); \
+}
+
+#define TEST_RUNNING_CHANGED(RUNNING) \
+{ \
+ QTRY_COMPARE(runningSpy.count(), 1); \
+ QList<QVariant> runningArgs = runningSpy.takeFirst(); \
+ QVERIFY(runningArgs.at(0).typeId() == QMetaType::Bool); \
+ QVERIFY(runningArgs.at(0).toBool() == RUNNING); \
+ QCOMPARE(machine.isRunning(), runningArgs.at(0).toBool()); \
+}
+
+#define TEST_RUNNING_CHANGED_STARTED_STOPPED \
+{ \
+ QTRY_COMPARE(runningSpy.count(), 2); \
+ QList<QVariant> runningArgs = runningSpy.takeFirst(); \
+ QVERIFY(runningArgs.at(0).typeId() == QMetaType::Bool); \
+ QVERIFY(runningArgs.at(0).toBool() == true); \
+ runningArgs = runningSpy.takeFirst(); \
+ QVERIFY(runningArgs.at(0).typeId() == QMetaType::Bool); \
+ QVERIFY(runningArgs.at(0).toBool() == false); \
+ QCOMPARE(machine.isRunning(), runningArgs.at(0).toBool()); \
+}
+
+#define DEFINE_ACTIVE_SPY(VAR) \
+ QSignalSpy VAR##_activeSpy(VAR, &QState::activeChanged); \
+ QVERIFY(VAR##_activeSpy.isValid());
+
+#define TEST_ACTIVE_CHANGED(VAR, COUNT) \
+{ \
+ QTRY_COMPARE(VAR##_activeSpy.count(), COUNT); \
+ bool active = true; \
+ for (const QList<QVariant> &activeArgs : static_cast<QList<QList<QVariant> > >(VAR##_activeSpy)) { \
+ QVERIFY(activeArgs.at(0).typeId() == QMetaType::Bool); \
+ QVERIFY(activeArgs.at(0).toBool() == active); \
+ active = !active; \
+ } \
+ QCOMPARE(VAR->active(), !active); \
+}
+
+class SignalEmitter : public QObject
+{
+Q_OBJECT
+ public:
+ SignalEmitter(QObject *parent = 0)
+ : QObject(parent) {}
+public Q_SLOTS:
+ void emitSignalWithNoArg()
+ { emit signalWithNoArg(); }
+ void emitSignalWithIntArg(int arg)
+ { emit signalWithIntArg(arg); }
+ void emitSignalWithStringArg(const QString &arg)
+ { emit signalWithStringArg(arg); }
+ void emitSignalWithDefaultArg()
+ { emit signalWithDefaultArg(); }
+Q_SIGNALS:
+ void signalWithNoArg();
+ void signalWithIntArg(int);
+ void signalWithStringArg(const QString &);
+ void signalWithDefaultArg(int i = 42);
+};
+
+class tst_QStateMachine : public QObject
+{
+ Q_OBJECT
+private slots:
+ void rootState();
+ void machineWithParent();
+#ifdef QT_BUILD_INTERNAL
+ void addAndRemoveState();
+#endif
+ void stateEntryAndExit();
+ void assignProperty();
+ void assignPropertyWithAnimation();
+ void postEvent();
+ void cancelDelayedEvent();
+ void postDelayedEventAndStop();
+ void postDelayedEventFromThread();
+ void stopAndPostEvent();
+ void stateFinished();
+ void parallelStates();
+ void parallelRootState();
+ void allSourceToTargetConfigurations();
+ void signalTransitions();
+#ifndef QT_NO_WIDGETS
+ void eventTransitions();
+ void graphicsSceneEventTransitions();
+#endif
+ void historyStates();
+ void startAndStop();
+ void setRunning();
+ void targetStateWithNoParent();
+ void targetStateDeleted();
+ void transitionToRootState();
+ void transitionFromRootState();
+ void transitionEntersParent();
+
+ void defaultErrorState();
+ void customGlobalErrorState();
+ void customLocalErrorStateInBrokenState();
+ void customLocalErrorStateInOtherState();
+ void customLocalErrorStateInParentOfBrokenState();
+ void customLocalErrorStateOverridesParent();
+ void errorStateHasChildren();
+ void errorStateHasErrors();
+ void errorStateIsRootState();
+ void errorStateEntersParentFirst();
+ void customErrorStateIsNull();
+ void clearError();
+ void historyStateHasNowhereToGo();
+ void historyStateAsInitialState();
+ void historyStateAfterRestart();
+ void brokenStateIsNeverEntered();
+ void customErrorStateNotInGraph();
+ void transitionToStateNotInGraph();
+ void restoreProperties();
+
+ void defaultGlobalRestorePolicy();
+ void globalRestorePolicySetToRestore();
+ void globalRestorePolicySetToDontRestore();
+
+ void noInitialStateForInitialState();
+
+ void transitionWithParent();
+ void transitionsFromParallelStateWithNoChildren();
+ void parallelStateTransition();
+ void parallelStateAssignmentsDone();
+ void nestedRestoreProperties();
+ void nestedRestoreProperties2();
+
+ void simpleAnimation();
+ void twoAnimations();
+ void twoAnimatedTransitions();
+ void playAnimationTwice();
+ void nestedTargetStateForAnimation();
+ void propertiesAssignedSignalTransitionsReuseAnimationGroup();
+ void animatedGlobalRestoreProperty();
+ void specificTargetValueOfAnimation();
+
+ void addDefaultAnimation();
+ void addDefaultAnimationWithUnusedAnimation();
+ void removeDefaultAnimation();
+ void overrideDefaultAnimationWithSpecific();
+
+ void nestedStateMachines();
+ void goToState();
+ void goToStateFromSourceWithTransition();
+
+ void clonedSignals();
+ void postEventFromOtherThread();
+#ifndef QT_NO_WIDGETS
+ void eventFilterForApplication();
+#endif
+ void eventClassesExported();
+ void stopInTransitionToFinalState();
+ void stopInEventTest_data();
+ void stopInEventTest();
+
+ void testIncrementReceivers();
+ void initialStateIsEnteredBeforeStartedEmitted();
+ void deletePropertyAssignmentObjectBeforeEntry();
+ void deletePropertyAssignmentObjectBeforeRestore();
+ void deleteInitialState();
+ void setPropertyAfterRestore();
+ void transitionWithNoTarget_data();
+ void transitionWithNoTarget();
+ void initialStateIsFinal();
+
+ void restorePropertiesSimple();
+ void restoreProperties2();
+ void restoreProperties3();
+ void restoreProperties4();
+ void restorePropertiesSelfTransition();
+ void changeStateWhileAnimatingProperty();
+ void propertiesAreAssignedBeforeEntryCallbacks_data();
+ void propertiesAreAssignedBeforeEntryCallbacks();
+
+ void multiTargetTransitionInsideParallelStateGroup();
+ void signalTransitionNormalizeSignature();
+#ifdef Q_COMPILER_DELEGATING_CONSTRUCTORS
+ void createPointerToMemberSignalTransition();
+#endif
+ void createSignalTransitionWhenRunning();
+ void createEventTransitionWhenRunning();
+ void signalTransitionSenderInDifferentThread();
+ void signalTransitionSenderInDifferentThread2();
+ void signalTransitionRegistrationThreadSafety();
+ void childModeConstructor();
+
+ void qtbug_44963();
+ void qtbug_44783();
+ void internalTransition();
+ void conflictingTransition();
+ void conflictingTransition2();
+ void qtbug_46059();
+ void qtbug_46703();
+ void postEventFromBeginSelectTransitions();
+ void dontProcessSlotsWhenMachineIsNotRunning();
+
+ void cancelDelayedEventWithChrono();
+ void postDelayedEventWithChronoAndStop();
+ void postDelayedEventWithChronoFromThread();
+ void bindings();
+};
+
+class TestState : public QState
+{
+public:
+ enum Event {
+ Entry,
+ Exit
+ };
+ TestState(QState *parent, const QString &objectName = QString())
+ : QState(parent)
+ { setObjectName(objectName); }
+ TestState(ChildMode mode, const QString &objectName = QString())
+ : QState(mode)
+ { setObjectName(objectName); }
+ QList<QPair<int, Event> > events;
+protected:
+ virtual void onEntry(QEvent *) override {
+ events.append(qMakePair(globalTick++, Entry));
+ }
+ virtual void onExit(QEvent *) override {
+ events.append(qMakePair(globalTick++, Exit));
+ }
+};
+
+class TestTransition : public QAbstractTransition
+{
+public:
+ TestTransition(QAbstractState *target, const QString &objectName = QString())
+ : QAbstractTransition()
+ { setTargetState(target); setObjectName(objectName); }
+ QList<int> triggers;
+protected:
+ virtual bool eventTest(QEvent *) override {
+ return true;
+ }
+ virtual void onTransition(QEvent *) override {
+ triggers.append(globalTick++);
+ }
+};
+
+class EventTransition : public QAbstractTransition
+{
+public:
+ EventTransition(QEvent::Type type, QAbstractState *target, QState *parent = 0)
+ : QAbstractTransition(parent), m_type(type)
+ { setTargetState(target); }
+ EventTransition(QEvent::Type type, const QList<QAbstractState *> &targets, QState *parent = 0)
+ : QAbstractTransition(parent), m_type(type)
+ { setTargetStates(targets); }
+protected:
+ virtual bool eventTest(QEvent *e) override {
+ return (e->type() == m_type);
+ }
+ virtual void onTransition(QEvent *) override {}
+private:
+ QEvent::Type m_type;
+};
+
+void tst_QStateMachine::transitionToRootState()
+{
+ QStateMachine machine;
+ machine.setObjectName("machine");
+
+ QState *initialState = new QState();
+ DEFINE_ACTIVE_SPY(initialState);
+ initialState->setObjectName("initial");
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QAbstractTransition *trans = new EventTransition(QEvent::User, &machine);
+ initialState->addTransition(trans);
+ QCOMPARE(trans->sourceState(), initialState);
+ QCOMPARE(trans->targetState(), static_cast<QAbstractState *>(&machine));
+
+ machine.start();
+
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(initialState));
+ TEST_ACTIVE_CHANGED(initialState, 1);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QTest::ignoreMessage(QtWarningMsg,
+ "Unrecoverable error detected in running state machine: "
+ "Child mode of state machine 'machine' is not 'ExclusiveStates'.");
+ QTRY_VERIFY(machine.configuration().isEmpty());
+ QTRY_VERIFY(!machine.isRunning());
+ TEST_ACTIVE_CHANGED(initialState, 2);
+}
+
+void tst_QStateMachine::transitionFromRootState()
+{
+ QStateMachine machine;
+ QState *root = &machine;
+ QState *s1 = new QState(root);
+ EventTransition *trans = new EventTransition(QEvent::User, s1);
+ root->addTransition(trans);
+ QCOMPARE(trans->sourceState(), root);
+ QCOMPARE(trans->targetState(), static_cast<QAbstractState *>(s1));
+}
+
+void tst_QStateMachine::transitionEntersParent()
+{
+ QStateMachine machine;
+
+ QObject *entryController = new QObject(&machine);
+ entryController->setObjectName("entryController");
+ entryController->setProperty("greatGrandParentEntered", false);
+ entryController->setProperty("grandParentEntered", false);
+ entryController->setProperty("parentEntered", false);
+ entryController->setProperty("stateEntered", false);
+
+ QState *greatGrandParent = new QState();
+ greatGrandParent->setObjectName("grandParent");
+ greatGrandParent->assignProperty(entryController, "greatGrandParentEntered", true);
+ machine.addState(greatGrandParent);
+ machine.setInitialState(greatGrandParent);
+
+ QState *grandParent = new QState(greatGrandParent);
+ grandParent->setObjectName("grandParent");
+ grandParent->assignProperty(entryController, "grandParentEntered", true);
+
+ QState *parent = new QState(grandParent);
+ parent->setObjectName("parent");
+ parent->assignProperty(entryController, "parentEntered", true);
+
+ QState *state = new QState(parent);
+ state->setObjectName("state");
+ state->assignProperty(entryController, "stateEntered", true);
+
+ QState *initialStateOfGreatGrandParent = new QState(greatGrandParent);
+ initialStateOfGreatGrandParent->setObjectName("initialStateOfGreatGrandParent");
+ greatGrandParent->setInitialState(initialStateOfGreatGrandParent);
+
+ initialStateOfGreatGrandParent->addTransition(new EventTransition(QEvent::User, state));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(entryController->property("greatGrandParentEntered").toBool(), true);
+ QTRY_COMPARE(entryController->property("grandParentEntered").toBool(), false);
+ QTRY_COMPARE(entryController->property("parentEntered").toBool(), false);
+ QTRY_COMPARE(entryController->property("stateEntered").toBool(), false);
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(greatGrandParent));
+ QTRY_VERIFY(machine.configuration().contains(initialStateOfGreatGrandParent));
+
+ entryController->setProperty("greatGrandParentEntered", false);
+ entryController->setProperty("grandParentEntered", false);
+ entryController->setProperty("parentEntered", false);
+ entryController->setProperty("stateEntered", false);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(entryController->property("greatGrandParentEntered").toBool(), false);
+ QTRY_COMPARE(entryController->property("grandParentEntered").toBool(), true);
+ QTRY_COMPARE(entryController->property("parentEntered").toBool(), true);
+ QTRY_COMPARE(entryController->property("stateEntered").toBool(), true);
+ QTRY_COMPARE(machine.configuration().size(), 4);
+ QTRY_VERIFY(machine.configuration().contains(greatGrandParent));
+ QTRY_VERIFY(machine.configuration().contains(grandParent));
+ QTRY_VERIFY(machine.configuration().contains(parent));
+ QTRY_VERIFY(machine.configuration().contains(state));
+}
+
+void tst_QStateMachine::defaultErrorState()
+{
+ QStateMachine machine;
+ QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
+
+ QState *brokenState = new QState();
+ brokenState->setObjectName("MyInitialState");
+
+ machine.addState(brokenState);
+ machine.setInitialState(brokenState);
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'MyInitialState'");
+
+ // initialState has no initial state
+ machine.start();
+
+ QTRY_COMPARE(machine.error(), QStateMachine::NoInitialStateError);
+ QTRY_COMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'MyInitialState'"));
+ QTRY_COMPARE(machine.isRunning(), false);
+}
+
+class CustomErrorState: public QState
+{
+public:
+ CustomErrorState(QStateMachine *machine, QState *parent = 0)
+ : QState(parent), error(QStateMachine::NoError), m_machine(machine)
+ {
+ }
+
+ void onEntry(QEvent *) override
+ {
+ error = m_machine->error();
+ errorString = m_machine->errorString();
+ }
+
+ QStateMachine::Error error;
+ QString errorString;
+
+private:
+ QStateMachine *m_machine;
+};
+
+void tst_QStateMachine::customGlobalErrorState()
+{
+ QStateMachine machine;
+
+ CustomErrorState *customErrorState = new CustomErrorState(&machine);
+ customErrorState->setObjectName("customErrorState");
+ machine.addState(customErrorState);
+ machine.setErrorState(customErrorState);
+
+ QState *initialState = new QState();
+ initialState->setObjectName("initialState");
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *brokenState = new QState();
+ brokenState->setObjectName("brokenState");
+ machine.addState(brokenState);
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
+ machine.start();
+
+ QTRY_COMPARE(machine.errorState(), static_cast<QAbstractState*>(customErrorState));
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(initialState));
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(initialState));
+
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), true);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(customErrorState));
+ QTRY_COMPARE(customErrorState->error, QStateMachine::NoInitialStateError);
+ QTRY_COMPARE(customErrorState->errorString, QString::fromLatin1("Missing initial state in compound state 'brokenState'"));
+ QTRY_COMPARE(machine.error(), QStateMachine::NoInitialStateError);
+ QTRY_COMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'"));
+}
+
+void tst_QStateMachine::customLocalErrorStateInBrokenState()
+{
+ QStateMachine machine;
+ CustomErrorState *customErrorState = new CustomErrorState(&machine);
+ machine.addState(customErrorState);
+
+ QState *initialState = new QState();
+ initialState->setObjectName("initialState");
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *brokenState = new QState();
+ brokenState->setObjectName("brokenState");
+ machine.addState(brokenState);
+ brokenState->setErrorState(customErrorState);
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), true);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(customErrorState));
+ QTRY_COMPARE(customErrorState->error, QStateMachine::NoInitialStateError);
+}
+
+void tst_QStateMachine::customLocalErrorStateInOtherState()
+{
+ QStateMachine machine;
+ CustomErrorState *customErrorState = new CustomErrorState(&machine);
+ machine.addState(customErrorState);
+
+ QState *initialState = new QState();
+ initialState->setObjectName("initialState");
+ QTest::ignoreMessage(QtWarningMsg, "QState::setErrorState: error state cannot belong to a different state machine");
+ initialState->setErrorState(customErrorState);
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *brokenState = new QState();
+ brokenState->setObjectName("brokenState");
+
+ machine.addState(brokenState);
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
+
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'brokenState'");
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), false);
+}
+
+void tst_QStateMachine::customLocalErrorStateInParentOfBrokenState()
+{
+ QStateMachine machine;
+ CustomErrorState *customErrorState = new CustomErrorState(&machine);
+ machine.addState(customErrorState);
+
+ QState *initialState = new QState();
+ initialState->setObjectName("initialState");
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *parentOfBrokenState = new QState();
+ machine.addState(parentOfBrokenState);
+ parentOfBrokenState->setObjectName("parentOfBrokenState");
+ parentOfBrokenState->setErrorState(customErrorState);
+
+ QState *brokenState = new QState(parentOfBrokenState);
+ brokenState->setObjectName("brokenState");
+ parentOfBrokenState->setInitialState(brokenState);
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), true);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(customErrorState));
+}
+
+void tst_QStateMachine::customLocalErrorStateOverridesParent()
+{
+ QStateMachine machine;
+ CustomErrorState *customErrorStateForParent = new CustomErrorState(&machine);
+ machine.addState(customErrorStateForParent);
+
+ CustomErrorState *customErrorStateForBrokenState = new CustomErrorState(&machine);
+ machine.addState(customErrorStateForBrokenState);
+
+ QState *initialState = new QState();
+ initialState->setObjectName("initialState");
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *parentOfBrokenState = new QState();
+ machine.addState(parentOfBrokenState);
+ parentOfBrokenState->setObjectName("parentOfBrokenState");
+ parentOfBrokenState->setErrorState(customErrorStateForParent);
+
+ QState *brokenState = new QState(parentOfBrokenState);
+ brokenState->setObjectName("brokenState");
+ brokenState->setErrorState(customErrorStateForBrokenState);
+ parentOfBrokenState->setInitialState(brokenState);
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(customErrorStateForBrokenState));
+ QTRY_COMPARE(customErrorStateForBrokenState->error, QStateMachine::NoInitialStateError);
+ QTRY_COMPARE(customErrorStateForParent->error, QStateMachine::NoError);
+}
+
+void tst_QStateMachine::errorStateHasChildren()
+{
+ QStateMachine machine;
+ CustomErrorState *customErrorState = new CustomErrorState(&machine);
+ customErrorState->setObjectName("customErrorState");
+ machine.addState(customErrorState);
+
+ machine.setErrorState(customErrorState);
+
+ QState *childOfErrorState = new QState(customErrorState);
+ childOfErrorState->setObjectName("childOfErrorState");
+ customErrorState->setInitialState(childOfErrorState);
+
+ QState *initialState = new QState();
+ initialState->setObjectName("initialState");
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *brokenState = new QState();
+ brokenState->setObjectName("brokenState");
+ machine.addState(brokenState);
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), true);
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(customErrorState));
+ QTRY_VERIFY(machine.configuration().contains(childOfErrorState));
+}
+
+
+void tst_QStateMachine::errorStateHasErrors()
+{
+ QStateMachine machine;
+ CustomErrorState *customErrorState = new CustomErrorState(&machine);
+ customErrorState->setObjectName("customErrorState");
+ machine.addState(customErrorState);
+
+ machine.setErrorState(customErrorState);
+
+ QState *childOfErrorState = new QState(customErrorState);
+ childOfErrorState->setObjectName("childOfErrorState");
+
+ QState *initialState = new QState();
+ initialState->setObjectName("initialState");
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *brokenState = new QState();
+ brokenState->setObjectName("brokenState");
+ machine.addState(brokenState);
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'customErrorState'");
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), false);
+ QTRY_COMPARE(machine.error(), QStateMachine::NoInitialStateError);
+ QTRY_COMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'customErrorState'"));
+}
+
+void tst_QStateMachine::errorStateIsRootState()
+{
+ QStateMachine machine;
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::setErrorState: root state cannot be error state");
+ machine.setErrorState(&machine);
+
+ QState *initialState = new QState();
+ initialState->setObjectName("initialState");
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *brokenState = new QState();
+ brokenState->setObjectName("brokenState");
+ machine.addState(brokenState);
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialState->addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), brokenState));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)));
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'brokenState'");
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), false);
+}
+
+void tst_QStateMachine::errorStateEntersParentFirst()
+{
+ QStateMachine machine;
+
+ QObject *entryController = new QObject(&machine);
+ entryController->setObjectName("entryController");
+ entryController->setProperty("greatGrandParentEntered", false);
+ entryController->setProperty("grandParentEntered", false);
+ entryController->setProperty("parentEntered", false);
+ entryController->setProperty("errorStateEntered", false);
+
+ QState *greatGrandParent = new QState();
+ greatGrandParent->setObjectName("greatGrandParent");
+ greatGrandParent->assignProperty(entryController, "greatGrandParentEntered", true);
+ machine.addState(greatGrandParent);
+ machine.setInitialState(greatGrandParent);
+
+ QState *grandParent = new QState(greatGrandParent);
+ grandParent->setObjectName("grandParent");
+ grandParent->assignProperty(entryController, "grandParentEntered", true);
+
+ QState *parent = new QState(grandParent);
+ parent->setObjectName("parent");
+ parent->assignProperty(entryController, "parentEntered", true);
+
+ QState *errorState = new QState(parent);
+ errorState->setObjectName("errorState");
+ errorState->assignProperty(entryController, "errorStateEntered", true);
+ machine.setErrorState(errorState);
+
+ QState *initialStateOfGreatGrandParent = new QState(greatGrandParent);
+ initialStateOfGreatGrandParent->setObjectName("initialStateOfGreatGrandParent");
+ greatGrandParent->setInitialState(initialStateOfGreatGrandParent);
+
+ QState *brokenState = new QState(greatGrandParent);
+ brokenState->setObjectName("brokenState");
+
+ QState *childState = new QState(brokenState);
+ childState->setObjectName("childState");
+
+ initialStateOfGreatGrandParent->addTransition(new EventTransition(QEvent::User, brokenState));
+
+ machine.start();
+
+ QTRY_COMPARE(entryController->property("greatGrandParentEntered").toBool(), true);
+ QTRY_COMPARE(entryController->property("grandParentEntered").toBool(), false);
+ QTRY_COMPARE(entryController->property("parentEntered").toBool(), false);
+ QTRY_COMPARE(entryController->property("errorStateEntered").toBool(), false);
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(greatGrandParent));
+ QTRY_VERIFY(machine.configuration().contains(initialStateOfGreatGrandParent));
+
+ entryController->setProperty("greatGrandParentEntered", false);
+ entryController->setProperty("grandParentEntered", false);
+ entryController->setProperty("parentEntered", false);
+ entryController->setProperty("errorStateEntered", false);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(entryController->property("greatGrandParentEntered").toBool(), false);
+ QTRY_COMPARE(entryController->property("grandParentEntered").toBool(), true);
+ QTRY_COMPARE(entryController->property("parentEntered").toBool(), true);
+ QTRY_COMPARE(entryController->property("errorStateEntered").toBool(), true);
+ QTRY_COMPARE(machine.configuration().size(), 4);
+ QTRY_VERIFY(machine.configuration().contains(greatGrandParent));
+ QTRY_VERIFY(machine.configuration().contains(grandParent));
+ QTRY_VERIFY(machine.configuration().contains(parent));
+ QTRY_VERIFY(machine.configuration().contains(errorState));
+}
+
+void tst_QStateMachine::customErrorStateIsNull()
+{
+ QStateMachine machine;
+ machine.setErrorState(0);
+
+ QState *initialState = new QState();
+ machine.addState(initialState);
+ machine.setInitialState(initialState);
+
+ QState *brokenState = new QState();
+ machine.addState(brokenState);
+
+ new QState(brokenState);
+ initialState->addTransition(new EventTransition(QEvent::User, brokenState));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state ''");
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
+ QTRY_COMPARE(machine.isRunning(), false);
+}
+
+void tst_QStateMachine::clearError()
+{
+ QStateMachine machine;
+ machine.setErrorState(new QState(&machine)); // avoid warnings
+
+ QState *brokenState = new QState(&machine);
+ brokenState->setObjectName("brokenState");
+ machine.setInitialState(brokenState);
+ new QState(brokenState);
+
+ machine.start();
+
+ QTRY_COMPARE(machine.isRunning(), true);
+ QTRY_COMPARE(machine.error(), QStateMachine::NoInitialStateError);
+ QTRY_COMPARE(machine.errorString(), QString::fromLatin1("Missing initial state in compound state 'brokenState'"));
+
+ machine.clearError();
+
+ QTRY_COMPARE(machine.error(), QStateMachine::NoError);
+ QTRY_VERIFY(machine.errorString().isEmpty());
+}
+
+void tst_QStateMachine::historyStateAsInitialState()
+{
+ QStateMachine machine;
+
+ QHistoryState *hs = new QHistoryState(&machine);
+ machine.setInitialState(hs);
+
+ QState *s1 = new QState(&machine);
+ hs->setDefaultState(s1);
+
+ QState *s2 = new QState(&machine);
+
+ QHistoryState *s2h = new QHistoryState(s2);
+ s2->setInitialState(s2h);
+
+ QState *s21 = new QState(s2);
+ s2h->setDefaultState(s21);
+
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QTRY_VERIFY(machine.configuration().contains(s21));
+}
+
+void tst_QStateMachine::historyStateHasNowhereToGo()
+{
+ QStateMachine machine;
+
+ QState *initialState = new QState(&machine);
+ initialState->setObjectName("initialState");
+ machine.setInitialState(initialState);
+ QState *errorState = new QState(&machine);
+ errorState->setObjectName("errorState");
+ machine.setErrorState(errorState); // avoid warnings
+
+ QState *brokenState = new QState(&machine);
+ brokenState->setObjectName("brokenState");
+ brokenState->setInitialState(new QState(brokenState));
+
+ QHistoryState *historyState = new QHistoryState(brokenState);
+ historyState->setObjectName("historyState");
+ EventTransition *t = new EventTransition(QEvent::User, historyState);
+ t->setObjectName("initialState->historyState");
+ initialState->addTransition(t);
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), true);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(machine.errorState()));
+ QTRY_COMPARE(machine.error(), QStateMachine::NoDefaultStateInHistoryStateError);
+ QTRY_COMPARE(machine.errorString(), QString::fromLatin1("Missing default state in history state 'historyState'"));
+}
+
+void tst_QStateMachine::historyStateAfterRestart()
+{
+ // QTBUG-8842
+ QStateMachine machine;
+
+ QState *s1 = new QState(&machine);
+ machine.setInitialState(s1);
+ QState *s2 = new QState(&machine);
+ QState *s21 = new QState(s2);
+ QState *s22 = new QState(s2);
+ QHistoryState *s2h = new QHistoryState(s2);
+ s2h->setDefaultState(s21);
+ s1->addTransition(new EventTransition(QEvent::User, s2h));
+ s21->addTransition(new EventTransition(QEvent::User, s22));
+ s2->addTransition(new EventTransition(QEvent::User, s1));
+
+ for (int x = 0; x < 2; ++x) {
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QTRY_VERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QTRY_VERIFY(startedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ // s1 -> s2h -> s21 (default state)
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ // This used to fail on the 2nd run because the
+ // history had not been cleared.
+ QTRY_VERIFY(machine.configuration().contains(s21));
+
+ // s21 -> s22
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QTRY_VERIFY(machine.configuration().contains(s22));
+
+ // s2 -> s1 (s22 saved in s2h)
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ // s1 -> s2h -> s22 (saved state)
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QTRY_VERIFY(machine.configuration().contains(s22));
+
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QTRY_VERIFY(stoppedSpy.isValid());
+ machine.stop();
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ }
+}
+
+void tst_QStateMachine::brokenStateIsNeverEntered()
+{
+ QStateMachine machine;
+
+ QObject *entryController = new QObject(&machine);
+ entryController->setProperty("brokenStateEntered", false);
+ entryController->setProperty("childStateEntered", false);
+ entryController->setProperty("errorStateEntered", false);
+
+ QState *initialState = new QState(&machine);
+ machine.setInitialState(initialState);
+
+ QState *errorState = new QState(&machine);
+ errorState->assignProperty(entryController, "errorStateEntered", true);
+ machine.setErrorState(errorState);
+
+ QState *brokenState = new QState(&machine);
+ brokenState->assignProperty(entryController, "brokenStateEntered", true);
+ brokenState->setObjectName("brokenState");
+
+ QState *childState = new QState(brokenState);
+ childState->assignProperty(entryController, "childStateEntered", true);
+
+ initialState->addTransition(new EventTransition(QEvent::User, brokenState));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(entryController->property("errorStateEntered").toBool(), true);
+ QTRY_COMPARE(entryController->property("brokenStateEntered").toBool(), false);
+ QTRY_COMPARE(entryController->property("childStateEntered").toBool(), false);
+}
+
+void tst_QStateMachine::transitionToStateNotInGraph()
+{
+ QStateMachine machine;
+
+ QState *initialState = new QState(&machine);
+ initialState->setObjectName("initialState");
+ machine.setInitialState(initialState);
+
+ QState independentState;
+ independentState.setObjectName("independentState");
+ initialState->addTransition(&independentState);
+
+ machine.start();
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
+ "Child mode of state machine '' is not 'ExclusiveStates'.");
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.isRunning(), false);
+}
+
+void tst_QStateMachine::customErrorStateNotInGraph()
+{
+ QStateMachine machine;
+
+ QState errorState;
+ errorState.setObjectName("errorState");
+ QTest::ignoreMessage(QtWarningMsg, "QState::setErrorState: error state cannot belong to a different state machine");
+ machine.setErrorState(&errorState);
+ QCOMPARE(machine.errorState(), reinterpret_cast<QAbstractState *>(0));
+
+ QState *initialBrokenState = new QState(&machine);
+ initialBrokenState->setObjectName("initialBrokenState");
+ machine.setInitialState(initialBrokenState);
+ new QState(initialBrokenState);
+
+ machine.start();
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: Missing initial state in compound state 'initialBrokenState'");
+ QCoreApplication::processEvents();
+
+ QCOMPARE(machine.isRunning(), false);
+}
+
+void tst_QStateMachine::restoreProperties()
+{
+ QStateMachine machine;
+ QCOMPARE(machine.globalRestorePolicy(), QState::DontRestoreProperties);
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("a", 1);
+ object->setProperty("b", 2);
+
+ QState *S1 = new QState();
+ S1->setObjectName("S1");
+ S1->assignProperty(object, "a", 3);
+ machine.addState(S1);
+
+ QState *S2 = new QState();
+ S2->setObjectName("S2");
+ S2->assignProperty(object, "b", 5);
+ machine.addState(S2);
+
+ QState *S3 = new QState();
+ S3->setObjectName("S3");
+ machine.addState(S3);
+
+ QFinalState *S4 = new QFinalState();
+ machine.addState(S4);
+
+ S1->addTransition(new EventTransition(QEvent::User, S2));
+ S2->addTransition(new EventTransition(QEvent::User, S3));
+ S3->addTransition(S4);
+
+ machine.setInitialState(S1);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(object->property("a").toInt(), 3);
+ QTRY_COMPARE(object->property("b").toInt(), 2);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(object->property("a").toInt(), 1);
+ QTRY_COMPARE(object->property("b").toInt(), 5);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(object->property("a").toInt(), 1);
+ QTRY_COMPARE(object->property("b").toInt(), 2);
+}
+
+void tst_QStateMachine::rootState()
+{
+ QStateMachine machine;
+ QCOMPARE(qobject_cast<QState*>(machine.parentState()), (QState*)0);
+ QCOMPARE(machine.machine(), (QStateMachine*)0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QCOMPARE(s1->parentState(), static_cast<QState*>(&machine));
+
+ QState *s2 = new QState();
+ DEFINE_ACTIVE_SPY(s2);
+ s2->setParent(&machine);
+ QCOMPARE(s2->parentState(), static_cast<QState*>(&machine));
+ TEST_ACTIVE_CHANGED(s1, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+}
+
+void tst_QStateMachine::machineWithParent()
+{
+ QObject object;
+ QStateMachine *machine = new QStateMachine(&object);
+ QCOMPARE(machine->parent(), &object);
+ QCOMPARE(machine->parentState(), static_cast<QState*>(0));
+}
+
+#ifdef QT_BUILD_INTERNAL
+void tst_QStateMachine::addAndRemoveState()
+{
+ QStateMachine machine;
+ QStatePrivate *root_d = QStatePrivate::get(&machine);
+ QCOMPARE(root_d->childStates().size(), 0);
+
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: cannot add null state");
+ machine.addState(0);
+
+ QState *s1 = new QState();
+ QCOMPARE(s1->parentState(), (QState*)0);
+ QCOMPARE(s1->machine(), (QStateMachine*)0);
+ machine.addState(s1);
+ QCOMPARE(s1->machine(), static_cast<QStateMachine*>(&machine));
+ QCOMPARE(s1->parentState(), static_cast<QState*>(&machine));
+ QCOMPARE(root_d->childStates().size(), 1);
+ QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s1);
+
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: state has already been added to this machine");
+ machine.addState(s1);
+
+ QState *s2 = new QState();
+ QCOMPARE(s2->parentState(), (QState*)0);
+ machine.addState(s2);
+ QCOMPARE(s2->parentState(), static_cast<QState*>(&machine));
+ QCOMPARE(root_d->childStates().size(), 2);
+ QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s1);
+ QCOMPARE(root_d->childStates().at(1), (QAbstractState*)s2);
+
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::addState: state has already been added to this machine");
+ machine.addState(s2);
+
+ machine.removeState(s1);
+ QCOMPARE(s1->parentState(), (QState*)0);
+ QCOMPARE(root_d->childStates().size(), 1);
+ QCOMPARE(root_d->childStates().at(0), (QAbstractState*)s2);
+
+ machine.removeState(s2);
+ QCOMPARE(s2->parentState(), (QState*)0);
+ QCOMPARE(root_d->childStates().size(), 0);
+
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::removeState: cannot remove null state");
+ machine.removeState(0);
+
+ {
+ QStateMachine machine2;
+ {
+ const QString warning
+ = QString::asprintf("QStateMachine::removeState: state %p's machine (%p) is different from this machine (%p)",
+ &machine2, (void*)0, &machine);
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ machine.removeState(&machine2);
+ }
+ // ### check this behavior
+ machine.addState(&machine2);
+ QCOMPARE(machine2.parent(), (QObject*)&machine);
+ }
+
+ delete s1;
+ delete s2;
+ // ### how to deal with this?
+ // machine.removeState(machine.errorState());
+}
+#endif
+
+void tst_QStateMachine::stateEntryAndExit()
+{
+ // Two top-level states
+ {
+ QStateMachine machine;
+
+ TestState *s1 = new TestState(&machine);
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add transition to null state");
+ s1->addTransition((QAbstractState*)0);
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add null transition");
+ s1->addTransition((QAbstractTransition*)0);
+ QTest::ignoreMessage(QtWarningMsg, "QState::removeTransition: cannot remove null transition");
+ s1->removeTransition((QAbstractTransition*)0);
+
+ TestState *s2 = new TestState(&machine);
+ QFinalState *s3 = new QFinalState(&machine);
+
+ TestTransition *t = new TestTransition(s2);
+ QCOMPARE(t->machine(), (QStateMachine*)0);
+ QCOMPARE(t->sourceState(), (QState*)0);
+ QCOMPARE(t->targetState(), (QAbstractState*)s2);
+ QCOMPARE(t->targetStates().size(), 1);
+ QCOMPARE(t->targetStates().at(0), (QAbstractState*)s2);
+ t->setTargetState(0);
+ QCOMPARE(t->targetState(), (QAbstractState*)0);
+ QVERIFY(t->targetStates().isEmpty());
+ t->setTargetState(s2);
+ QCOMPARE(t->targetState(), (QAbstractState*)s2);
+ QTest::ignoreMessage(QtWarningMsg, "QAbstractTransition::setTargetStates: target state(s) cannot be null");
+ t->setTargetStates(QList<QAbstractState*>() << 0);
+ QCOMPARE(t->targetState(), (QAbstractState*)s2);
+ t->setTargetStates(QList<QAbstractState*>() << s2);
+ QCOMPARE(t->targetState(), (QAbstractState*)s2);
+ QCOMPARE(t->targetStates().size(), 1);
+ QCOMPARE(t->targetStates().at(0), (QAbstractState*)s2);
+ s1->addTransition(t);
+ QCOMPARE(t->sourceState(), (QState*)s1);
+ QCOMPARE(t->machine(), &machine);
+
+ {
+ QAbstractTransition *trans = s2->addTransition(s3);
+ QVERIFY(trans != 0);
+ QCOMPARE(trans->sourceState(), (QState*)s2);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s3);
+ {
+ const QString warning
+ = QString::asprintf("QState::removeTransition: transition %p's source state (%p) is different from this state (%p)", trans, s2, s1);
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ s1->removeTransition(trans);
+ }
+ s2->removeTransition(trans);
+ QCOMPARE(trans->sourceState(), (QState*)0);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s3);
+ s2->addTransition(trans);
+ QCOMPARE(trans->sourceState(), (QState*)s2);
+ }
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(stoppedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+
+ machine.setInitialState(s1);
+ QCOMPARE(machine.initialState(), (QAbstractState*)s1);
+ {
+ QString warning
+ = QString::asprintf("QState::setInitialState: state %p is not a child of this state (%p)", &machine, &machine);
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ machine.setInitialState(&machine);
+ QCOMPARE(machine.initialState(), (QAbstractState*)s1);
+ }
+ QVERIFY(machine.configuration().isEmpty());
+ globalTick = 0;
+ QVERIFY(!machine.isRunning());
+ QSignalSpy s1EnteredSpy(s1, &TestState::entered);
+ QSignalSpy s1ExitedSpy(s1, &TestState::exited);
+ QSignalSpy tTriggeredSpy(t, &TestTransition::triggered);
+ QSignalSpy s2EnteredSpy(s2, &TestState::entered);
+ QSignalSpy s2ExitedSpy(s2, &TestState::exited);
+
+ QVERIFY(s1EnteredSpy.isValid());
+ QVERIFY(s1ExitedSpy.isValid());
+ QVERIFY(tTriggeredSpy.isValid());
+ QVERIFY(s2EnteredSpy.isValid());
+ QVERIFY(s2ExitedSpy.isValid());
+
+ machine.start();
+
+ QTRY_COMPARE(startedSpy.size(), 1);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ QTRY_COMPARE(stoppedSpy.size(), 0);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s3));
+
+ // s1 is entered
+ QCOMPARE(s1->events.size(), 2);
+ QCOMPARE(s1->events.at(0).first, 0);
+ QCOMPARE(s1->events.at(0).second, TestState::Entry);
+ // s1 is exited
+ QCOMPARE(s1->events.at(1).first, 1);
+ QCOMPARE(s1->events.at(1).second, TestState::Exit);
+ // t is triggered
+ QCOMPARE(t->triggers.size(), 1);
+ QCOMPARE(t->triggers.at(0), 2);
+ // s2 is entered
+ QCOMPARE(s2->events.size(), 2);
+ QCOMPARE(s2->events.at(0).first, 3);
+ QCOMPARE(s2->events.at(0).second, TestState::Entry);
+ // s2 is exited
+ QCOMPARE(s2->events.at(1).first, 4);
+ QCOMPARE(s2->events.at(1).second, TestState::Exit);
+
+ QCOMPARE(s1EnteredSpy.size(), 1);
+ QCOMPARE(s1ExitedSpy.size(), 1);
+ QCOMPARE(tTriggeredSpy.size(), 1);
+ QCOMPARE(s2EnteredSpy.size(), 1);
+ QCOMPARE(s2ExitedSpy.size(), 1);
+ }
+ // Two top-level states, one has two child states
+ {
+ QStateMachine machine;
+
+ TestState *s1 = new TestState(&machine, "s1");
+ TestState *s11 = new TestState(s1, "s11");
+ TestState *s12 = new TestState(s1, "s12");
+ TestState *s2 = new TestState(&machine, "s2");
+ QFinalState *s3 = new QFinalState(&machine);
+ s3->setObjectName("s3");
+ s1->setInitialState(s11);
+ TestTransition *t1 = new TestTransition(s12, "t1");
+ s11->addTransition(t1);
+ TestTransition *t2 = new TestTransition(s2, "t2");
+ s12->addTransition(t2);
+ s2->addTransition(s3);
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.setInitialState(s1);
+ globalTick = 0;
+ machine.start();
+
+ QTRY_COMPARE(startedSpy.size(), 1);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s3));
+
+ // s1 is entered
+ QCOMPARE(s1->events.size(), 2);
+ QCOMPARE(s1->events.at(0).first, 0);
+ QCOMPARE(s1->events.at(0).second, TestState::Entry);
+ // s11 is entered
+ QCOMPARE(s11->events.size(), 2);
+ QCOMPARE(s11->events.at(0).first, 1);
+ QCOMPARE(s11->events.at(0).second, TestState::Entry);
+ // s11 is exited
+ QCOMPARE(s11->events.at(1).first, 2);
+ QCOMPARE(s11->events.at(1).second, TestState::Exit);
+ // t1 is triggered
+ QCOMPARE(t1->triggers.size(), 1);
+ QCOMPARE(t1->triggers.at(0), 3);
+ // s12 is entered
+ QCOMPARE(s12->events.size(), 2);
+ QCOMPARE(s12->events.at(0).first, 4);
+ QCOMPARE(s12->events.at(0).second, TestState::Entry);
+ // s12 is exited
+ QCOMPARE(s12->events.at(1).first, 5);
+ QCOMPARE(s12->events.at(1).second, TestState::Exit);
+ // s1 is exited
+ QCOMPARE(s1->events.at(1).first, 6);
+ QCOMPARE(s1->events.at(1).second, TestState::Exit);
+ // t2 is triggered
+ QCOMPARE(t2->triggers.size(), 1);
+ QCOMPARE(t2->triggers.at(0), 7);
+ // s2 is entered
+ QCOMPARE(s2->events.size(), 2);
+ QCOMPARE(s2->events.at(0).first, 8);
+ QCOMPARE(s2->events.at(0).second, TestState::Entry);
+ // s2 is exited
+ QCOMPARE(s2->events.at(1).first, 9);
+ QCOMPARE(s2->events.at(1).second, TestState::Exit);
+ }
+}
+
+void tst_QStateMachine::assignProperty()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+
+ QTest::ignoreMessage(QtWarningMsg, "QState::assignProperty: cannot assign property 'foo' of null object");
+ s1->assignProperty(0, "foo", QVariant());
+
+ s1->assignProperty(s1, "objectName", "s1");
+ QFinalState *s2 = new QFinalState(&machine);
+ s1->addTransition(s2);
+ machine.setInitialState(s1);
+ machine.start();
+ QTRY_COMPARE(s1->objectName(), QString::fromLatin1("s1"));
+ TEST_ACTIVE_CHANGED(s1, 2);
+
+ s1->assignProperty(s1, "objectName", "foo");
+ machine.start();
+ QTRY_COMPARE(s1->objectName(), QString::fromLatin1("foo"));
+ TEST_ACTIVE_CHANGED(s1, 4);
+
+ s1->assignProperty(s1, "noSuchProperty", 123);
+ machine.start();
+ QTRY_COMPARE(s1->dynamicPropertyNames().size(), 1);
+ QCOMPARE(s1->dynamicPropertyNames().at(0), QByteArray("noSuchProperty"));
+ QCOMPARE(s1->objectName(), QString::fromLatin1("foo"));
+ TEST_ACTIVE_CHANGED(s1, 6);
+
+ {
+ QSignalSpy propertiesAssignedSpy(s1, &QState::propertiesAssigned);
+ QVERIFY(propertiesAssignedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(propertiesAssignedSpy.size(), 1);
+ TEST_ACTIVE_CHANGED(s1, 8);
+ }
+
+ // nested states
+ {
+ QState *s11 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s11);
+ QString str = QString::fromLatin1("set by nested state");
+ s11->assignProperty(s11, "objectName", str);
+ s1->setInitialState(s11);
+ machine.start();
+ QTRY_COMPARE(s11->objectName(), str);
+ TEST_ACTIVE_CHANGED(s1, 10);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ }
+}
+
+void tst_QStateMachine::assignPropertyWithAnimation()
+{
+ // Single animation
+ {
+ QStateMachine machine;
+ QVERIFY(machine.isAnimated());
+ machine.setAnimated(false);
+ QVERIFY(!machine.isAnimated());
+ machine.setAnimated(true);
+ QVERIFY(machine.isAnimated());
+ QObject obj;
+ obj.setProperty("foo", 321);
+ obj.setProperty("bar", 654);
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(&obj, "foo", 123);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(&obj, "foo", 456);
+ s2->assignProperty(&obj, "bar", 789);
+ QAbstractTransition *trans = s1->addTransition(s2);
+ QVERIFY(trans->animations().isEmpty());
+ QTest::ignoreMessage(QtWarningMsg, "QAbstractTransition::addAnimation: cannot add null animation");
+ trans->addAnimation(0);
+ QPropertyAnimation anim(&obj, "foo");
+ anim.setDuration(250);
+ trans->addAnimation(&anim);
+ QCOMPARE(trans->animations().size(), 1);
+ QCOMPARE(trans->animations().at(0), (QAbstractAnimation*)&anim);
+ QCOMPARE(anim.parent(), (QObject*)0);
+ QTest::ignoreMessage(QtWarningMsg, "QAbstractTransition::removeAnimation: cannot remove null animation");
+ trans->removeAnimation(0);
+ trans->removeAnimation(&anim);
+ QVERIFY(trans->animations().isEmpty());
+ trans->addAnimation(&anim);
+ QCOMPARE(trans->animations().size(), 1);
+ QCOMPARE(trans->animations().at(0), (QAbstractAnimation*)&anim);
+ QFinalState *s3 = new QFinalState(&machine);
+ s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
+
+ machine.setInitialState(s1);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(obj.property("foo").toInt(), 456);
+ QCOMPARE(obj.property("bar").toInt(), 789);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ }
+ // Two animations
+ {
+ QStateMachine machine;
+ QObject obj;
+ obj.setProperty("foo", 321);
+ obj.setProperty("bar", 654);
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(&obj, "foo", 123);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(&obj, "foo", 456);
+ s2->assignProperty(&obj, "bar", 789);
+ QAbstractTransition *trans = s1->addTransition(s2);
+ QPropertyAnimation anim(&obj, "foo");
+ anim.setDuration(150);
+ trans->addAnimation(&anim);
+ QPropertyAnimation anim2(&obj, "bar");
+ anim2.setDuration(150);
+ trans->addAnimation(&anim2);
+ QFinalState *s3 = new QFinalState(&machine);
+ s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
+
+ machine.setInitialState(s1);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(obj.property("foo").toInt(), 456);
+ QCOMPARE(obj.property("bar").toInt(), 789);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ }
+ // Animation group
+ {
+ QStateMachine machine;
+ QObject obj;
+ obj.setProperty("foo", 321);
+ obj.setProperty("bar", 654);
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(&obj, "foo", 123);
+ s1->assignProperty(&obj, "bar", 321);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(&obj, "foo", 456);
+ s2->assignProperty(&obj, "bar", 654);
+ s2->assignProperty(&obj, "baz", 789);
+ QAbstractTransition *trans = s1->addTransition(s2);
+ QSequentialAnimationGroup group;
+ group.addAnimation(new QPropertyAnimation(&obj, "foo"));
+ group.addAnimation(new QPropertyAnimation(&obj, "bar"));
+ trans->addAnimation(&group);
+ QFinalState *s3 = new QFinalState(&machine);
+ s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
+
+ machine.setInitialState(s1);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(obj.property("foo").toInt(), 456);
+ QCOMPARE(obj.property("bar").toInt(), 654);
+ QCOMPARE(obj.property("baz").toInt(), 789);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ }
+ // Nested states
+ {
+ QStateMachine machine;
+ QObject obj;
+ obj.setProperty("foo", 321);
+ obj.setProperty("bar", 654);
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QCOMPARE(s1->childMode(), QState::ExclusiveStates);
+ s1->setChildMode(QState::ParallelStates);
+ QCOMPARE(s1->childMode(), QState::ParallelStates);
+ s1->setChildMode(QState::ExclusiveStates);
+ QCOMPARE(s1->childMode(), QState::ExclusiveStates);
+ QCOMPARE(s1->initialState(), (QAbstractState*)0);
+ s1->setObjectName("s1");
+ s1->assignProperty(&obj, "foo", 123);
+ s1->assignProperty(&obj, "bar", 456);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->setObjectName("s2");
+ s2->assignProperty(&obj, "foo", 321);
+ QState *s21 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s21);
+ s21->setObjectName("s21");
+ s21->assignProperty(&obj, "bar", 654);
+ QState *s22 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s22);
+ s22->setObjectName("s22");
+ s22->assignProperty(&obj, "bar", 789);
+ s2->setInitialState(s21);
+ QCOMPARE(s2->initialState(), (QAbstractState*)s21);
+
+ QAbstractTransition *trans = s1->addTransition(s2);
+ QPropertyAnimation anim(&obj, "foo");
+ anim.setDuration(500);
+ trans->addAnimation(&anim);
+ QPropertyAnimation anim2(&obj, "bar");
+ anim2.setDuration(250);
+ trans->addAnimation(&anim2);
+
+ s21->addTransition(s21, SIGNAL(propertiesAssigned()), s22);
+
+ QFinalState *s3 = new QFinalState(&machine);
+ s22->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
+
+ machine.setInitialState(s1);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(obj.property("foo").toInt(), 321);
+ QCOMPARE(obj.property("bar").toInt(), 789);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s21, 2);
+ TEST_ACTIVE_CHANGED(s22, 2);
+ }
+ // Aborted animation
+ {
+ QStateMachine machine;
+ SignalEmitter emitter;
+ QObject obj;
+ obj.setProperty("foo", 321);
+ obj.setProperty("bar", 654);
+ QState *group = new QState(&machine);
+ QState *s1 = new QState(group);
+ DEFINE_ACTIVE_SPY(s1);
+ group->setInitialState(s1);
+ s1->assignProperty(&obj, "foo", 123);
+ QState *s2 = new QState(group);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(&obj, "foo", 456);
+ s2->assignProperty(&obj, "bar", 789);
+ QAbstractTransition *trans = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s2);
+ QPropertyAnimation anim(&obj, "foo");
+ anim.setDuration(8000);
+ trans->addAnimation(&anim);
+ QPropertyAnimation anim2(&obj, "bar");
+ anim2.setDuration(8000);
+ trans->addAnimation(&anim2);
+ QState *s3 = new QState(group);
+ DEFINE_ACTIVE_SPY(s3);
+ s3->assignProperty(&obj, "foo", 911);
+ s2->addTransition(&emitter, SIGNAL(signalWithNoArg()), s3);
+
+ machine.setInitialState(group);
+ machine.start();
+ QTRY_COMPARE(machine.configuration().contains(s1), true);
+ QSignalSpy propertiesAssignedSpy(s2, &QState::propertiesAssigned);
+ QVERIFY(propertiesAssignedSpy.isValid());
+ emitter.emitSignalWithNoArg();
+ QTRY_COMPARE(machine.configuration().contains(s2), true);
+ QVERIFY(propertiesAssignedSpy.isEmpty());
+ emitter.emitSignalWithNoArg(); // will cause animations from s1-->s2 to abort
+ QTRY_COMPARE(machine.configuration().contains(s3), true);
+ QVERIFY(propertiesAssignedSpy.isEmpty());
+ QCOMPARE(obj.property("foo").toInt(), 911);
+ QCOMPARE(obj.property("bar").toInt(), 789);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ }
+}
+
+struct StringEvent : public QEvent
+{
+public:
+ StringEvent(const QString &val)
+ : QEvent(QEvent::Type(QEvent::User+2)),
+ value(val) {}
+
+ QString value;
+};
+
+class StringTransition : public QAbstractTransition
+{
+public:
+ StringTransition(const QString &value, QAbstractState *target)
+ : QAbstractTransition(), m_value(value)
+ { setTargetState(target); }
+
+protected:
+ virtual bool eventTest(QEvent *e) override
+ {
+ if (e->type() != QEvent::Type(QEvent::User+2))
+ return false;
+ StringEvent *se = static_cast<StringEvent*>(e);
+ return (m_value == se->value) && (!m_cond.isValid() || m_cond.match(m_value).hasMatch());
+ }
+ virtual void onTransition(QEvent *) override {}
+
+private:
+ QString m_value;
+ QRegularExpression m_cond;
+};
+
+class StringEventPoster : public QState
+{
+public:
+ StringEventPoster(const QString &value, QState *parent = 0)
+ : QState(parent), m_value(value), m_delay(-1) {}
+
+ void setString(const QString &value)
+ { m_value = value; }
+ void setDelay(int delay)
+ { m_delay = delay; }
+
+protected:
+ virtual void onEntry(QEvent *) override
+ {
+ if (m_delay == -1)
+ machine()->postEvent(new StringEvent(m_value));
+ else
+ machine()->postDelayedEvent(new StringEvent(m_value), m_delay);
+ }
+ virtual void onExit(QEvent *) override {}
+
+private:
+ QString m_value;
+ int m_delay;
+};
+
+void tst_QStateMachine::postEvent()
+{
+ for (int x = 0; x < 2; ++x) {
+ QStateMachine machine;
+ {
+ QEvent e(QEvent::None);
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::postEvent: cannot post event when the state machine is not running");
+ machine.postEvent(&e);
+ }
+ StringEventPoster *s1 = new StringEventPoster("a");
+ DEFINE_ACTIVE_SPY(s1);
+ if (x == 1)
+ s1->setDelay(100);
+ QFinalState *s2 = new QFinalState;
+ s1->addTransition(new StringTransition("a", s2));
+ machine.addState(s1);
+ machine.addState(s2);
+ machine.setInitialState(s1);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+ TEST_ACTIVE_CHANGED(s1, 2);
+
+ s1->setString("b");
+ QFinalState *s3 = new QFinalState();
+ machine.addState(s3);
+ s1->addTransition(new StringTransition("b", s3));
+ finishedSpy.clear();
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s3));
+ TEST_ACTIVE_CHANGED(s1, 4);
+ }
+}
+
+void tst_QStateMachine::cancelDelayedEvent()
+{
+ QStateMachine machine;
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::cancelDelayedEvent: the machine is not running");
+ QVERIFY(!machine.cancelDelayedEvent(-1));
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *s2 = new QFinalState(&machine);
+ s1->addTransition(new StringTransition("a", s2));
+ machine.setInitialState(s1);
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+ int id1 = machine.postDelayedEvent(new StringEvent("c"), 50000);
+ QVERIFY(id1 != -1);
+ int id2 = machine.postDelayedEvent(new StringEvent("b"), 25000);
+ QVERIFY(id2 != -1);
+ QVERIFY(id2 != id1);
+ int id3 = machine.postDelayedEvent(new StringEvent("a"), 100);
+ QVERIFY(id3 != -1);
+ QVERIFY(id3 != id2);
+ QVERIFY(machine.cancelDelayedEvent(id1));
+ QVERIFY(!machine.cancelDelayedEvent(id1));
+ QVERIFY(machine.cancelDelayedEvent(id2));
+ QVERIFY(!machine.cancelDelayedEvent(id2));
+
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+}
+
+void tst_QStateMachine::postDelayedEventAndStop()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *s2 = new QFinalState(&machine);
+ s1->addTransition(new StringTransition("a", s2));
+ machine.setInitialState(s1);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QVERIFY(startedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ int id1 = machine.postDelayedEvent(new StringEvent("a"), 0);
+ QVERIFY(id1 != -1);
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QVERIFY(stoppedSpy.isValid());
+ machine.stop();
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 2);
+ TEST_RUNNING_CHANGED(true);
+ TEST_ACTIVE_CHANGED(s1, 3);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ int id2 = machine.postDelayedEvent(new StringEvent("a"), 1000);
+ QVERIFY(id2 != -1);
+ machine.stop();
+ QTRY_COMPARE(stoppedSpy.size(), 2);
+ TEST_RUNNING_CHANGED(false);
+ TEST_ACTIVE_CHANGED(s1, 3);
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 3);
+ TEST_RUNNING_CHANGED(true);
+ QTestEventLoop::instance().enterLoop(2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+ TEST_ACTIVE_CHANGED(s1, 5);
+ QVERIFY(machine.isRunning());
+}
+
+class DelayedEventPosterThread : public QThread
+{
+ Q_OBJECT
+public:
+ DelayedEventPosterThread(QStateMachine *machine, QObject *parent = 0)
+ : QThread(parent), firstEventWasCancelled(false),
+ m_machine(machine)
+ {
+ moveToThread(this);
+ QObject::connect(m_machine, SIGNAL(started()),
+ this, SLOT(postEvent()));
+ }
+
+ mutable bool firstEventWasCancelled;
+
+private Q_SLOTS:
+ void postEvent()
+ {
+ int id = m_machine->postDelayedEvent(new QEvent(QEvent::User), 1000);
+ firstEventWasCancelled = m_machine->cancelDelayedEvent(id);
+
+ m_machine->postDelayedEvent(new QEvent(QEvent::User), 1);
+
+ quit();
+ }
+private:
+ QStateMachine *m_machine;
+};
+
+void tst_QStateMachine::postDelayedEventFromThread()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *f = new QFinalState(&machine);
+ s1->addTransition(new EventTransition(QEvent::User, f));
+ machine.setInitialState(s1);
+
+ DelayedEventPosterThread poster(&machine);
+ poster.start();
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QVERIFY(poster.firstEventWasCancelled);
+}
+
+void tst_QStateMachine::stopAndPostEvent()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QVERIFY(startedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QVERIFY(stoppedSpy.isValid());
+ machine.stop();
+ QCOMPARE(stoppedSpy.size(), 0);
+ machine.postEvent(new QEvent(QEvent::User));
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCoreApplication::processEvents();
+}
+
+void tst_QStateMachine::stateFinished()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QState *s1_1 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s1_1);
+ QFinalState *s1_2 = new QFinalState(s1);
+ s1_1->addTransition(s1_2);
+ s1->setInitialState(s1_1);
+ QFinalState *s2 = new QFinalState(&machine);
+ s1->addTransition(s1, SIGNAL(finished()), s2);
+ machine.setInitialState(s1);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s1_1, 2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+}
+
+void tst_QStateMachine::parallelStates()
+{
+ QStateMachine machine;
+
+ TestState *s1 = new TestState(QState::ParallelStates);
+ QCOMPARE(s1->childMode(), QState::ParallelStates);
+ TestState *s1_1 = new TestState(s1);
+ QState *s1_1_1 = new QState(s1_1);
+ QFinalState *s1_1_f = new QFinalState(s1_1);
+ s1_1_1->addTransition(s1_1_f);
+ s1_1->setInitialState(s1_1_1);
+ TestState *s1_2 = new TestState(s1);
+ QState *s1_2_1 = new QState(s1_2);
+ QFinalState *s1_2_f = new QFinalState(s1_2);
+ s1_2_1->addTransition(s1_2_f);
+ s1_2->setInitialState(s1_2_1);
+ {
+ const QString warning
+ = QString::asprintf("QState::setInitialState: ignoring attempt to set initial state of parallel state group %p", s1);
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ s1->setInitialState(0);
+ }
+ machine.addState(s1);
+
+ QFinalState *s2 = new QFinalState();
+ machine.addState(s2);
+
+ s1->addTransition(s1, SIGNAL(finished()), s2);
+
+ machine.setInitialState(s1);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ globalTick = 0;
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+
+ QCOMPARE(s1->events.size(), 2);
+ // s1 is entered
+ QCOMPARE(s1->events.at(0).first, 0);
+ QCOMPARE(s1->events.at(0).second, TestState::Entry);
+ // s1_1 is entered
+ QCOMPARE(s1_1->events.size(), 2);
+ QCOMPARE(s1_1->events.at(0).first, 1);
+ QCOMPARE(s1_1->events.at(0).second, TestState::Entry);
+ // s1_2 is entered
+ QCOMPARE(s1_2->events.at(0).first, 2);
+ QCOMPARE(s1_2->events.at(0).second, TestState::Entry);
+ // s1_2 is exited
+ QCOMPARE(s1_2->events.at(1).first, 3);
+ QCOMPARE(s1_2->events.at(1).second, TestState::Exit);
+ // s1_1 is exited
+ QCOMPARE(s1_1->events.at(1).first, 4);
+ QCOMPARE(s1_1->events.at(1).second, TestState::Exit);
+ // s1 is exited
+ QCOMPARE(s1->events.at(1).first, 5);
+ QCOMPARE(s1->events.at(1).second, TestState::Exit);
+}
+
+void tst_QStateMachine::parallelRootState()
+{
+ QStateMachine machine;
+ QState *root = &machine;
+ QCOMPARE(root->childMode(), QState::ExclusiveStates);
+ root->setChildMode(QState::ParallelStates);
+ QCOMPARE(root->childMode(), QState::ParallelStates);
+
+ QState *s1 = new QState(root);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *s1_f = new QFinalState(s1);
+ s1->setInitialState(s1_f);
+ QState *s2 = new QState(root);
+ DEFINE_ACTIVE_SPY(s2);
+ QFinalState *s2_f = new QFinalState(s2);
+ s2->setInitialState(s2_f);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QVERIFY(startedSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.start();
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
+ "Child mode of state machine '' is not 'ExclusiveStates'.");
+ QTRY_COMPARE(startedSpy.size(), 1);
+ QCOMPARE(machine.configuration().size(), 4);
+ QVERIFY(machine.configuration().contains(s1));
+ QVERIFY(machine.configuration().contains(s1_f));
+ QVERIFY(machine.configuration().contains(s2));
+ QVERIFY(machine.configuration().contains(s2_f));
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QVERIFY(!machine.isRunning());
+}
+
+void tst_QStateMachine::allSourceToTargetConfigurations()
+{
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ s0->setObjectName("s0");
+ QState *s1 = new QState(s0);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->setObjectName("s1");
+ QState *s11 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s11);
+ s11->setObjectName("s11");
+ QState *s2 = new QState(s0);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->setObjectName("s2");
+ QState *s21 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s21);
+ s21->setObjectName("s21");
+ QState *s211 = new QState(s21);
+ DEFINE_ACTIVE_SPY(s211);
+ s211->setObjectName("s211");
+ QFinalState *f = new QFinalState(&machine);
+ f->setObjectName("f");
+
+ s0->setInitialState(s1);
+ s1->setInitialState(s11);
+ s2->setInitialState(s21);
+ s21->setInitialState(s211);
+
+ s11->addTransition(new StringTransition("g", s211));
+ s1->addTransition(new StringTransition("a", s1));
+ s1->addTransition(new StringTransition("b", s11));
+ s1->addTransition(new StringTransition("c", s2));
+ s1->addTransition(new StringTransition("d", s0));
+ s1->addTransition(new StringTransition("f", s211));
+ s211->addTransition(new StringTransition("d", s21));
+ s211->addTransition(new StringTransition("g", s0));
+ s211->addTransition(new StringTransition("h", f));
+ s21->addTransition(new StringTransition("b", s211));
+ s2->addTransition(new StringTransition("c", s1));
+ s2->addTransition(new StringTransition("f", s11));
+ s0->addTransition(new StringTransition("e", s211));
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s21, 0);
+ TEST_ACTIVE_CHANGED(s211, 0);
+
+ machine.postEvent(new StringEvent("a"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s1, 3);
+ TEST_ACTIVE_CHANGED(s11, 3);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s21, 0);
+ TEST_ACTIVE_CHANGED(s211, 0);
+
+ machine.postEvent(new StringEvent("b"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s1, 5);
+ TEST_ACTIVE_CHANGED(s11, 5);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s21, 0);
+ TEST_ACTIVE_CHANGED(s211, 0);
+
+ machine.postEvent(new StringEvent("c"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s1, 6);
+ TEST_ACTIVE_CHANGED(s11, 6);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s21, 1);
+ TEST_ACTIVE_CHANGED(s211, 1);
+
+ machine.postEvent(new StringEvent("d"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s1, 6);
+ TEST_ACTIVE_CHANGED(s11, 6);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s21, 3);
+ TEST_ACTIVE_CHANGED(s211, 3);
+
+ machine.postEvent(new StringEvent("e"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ TEST_ACTIVE_CHANGED(s1, 6);
+ TEST_ACTIVE_CHANGED(s11, 6);
+ TEST_ACTIVE_CHANGED(s2, 3);
+ TEST_ACTIVE_CHANGED(s21, 5);
+ TEST_ACTIVE_CHANGED(s211, 5);
+
+ machine.postEvent(new StringEvent("f"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ TEST_ACTIVE_CHANGED(s1, 7);
+ TEST_ACTIVE_CHANGED(s11, 7);
+ TEST_ACTIVE_CHANGED(s2, 4);
+ TEST_ACTIVE_CHANGED(s21, 6);
+ TEST_ACTIVE_CHANGED(s211, 6);
+
+ machine.postEvent(new StringEvent("g"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ TEST_ACTIVE_CHANGED(s1, 8);
+ TEST_ACTIVE_CHANGED(s11, 8);
+ TEST_ACTIVE_CHANGED(s2, 5);
+ TEST_ACTIVE_CHANGED(s21, 7);
+ TEST_ACTIVE_CHANGED(s211, 7);
+
+ machine.postEvent(new StringEvent("h"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 4);
+ TEST_ACTIVE_CHANGED(s1, 8);
+ TEST_ACTIVE_CHANGED(s11, 8);
+ TEST_ACTIVE_CHANGED(s2, 6);
+ TEST_ACTIVE_CHANGED(s21, 8);
+ TEST_ACTIVE_CHANGED(s211, 8);
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+}
+
+class TestSignalTransition : public QSignalTransition
+{
+public:
+ TestSignalTransition(QState *sourceState = 0)
+ : QSignalTransition(sourceState),
+ m_eventTestSender(0), m_eventTestSignalIndex(-1),
+ m_transitionSender(0), m_transitionSignalIndex(-1)
+ {}
+ TestSignalTransition(QObject *sender, const char *signal,
+ QAbstractState *target)
+ : QSignalTransition(sender, signal),
+ m_eventTestSender(0), m_eventTestSignalIndex(-1),
+ m_transitionSender(0), m_transitionSignalIndex(-1)
+ { setTargetState(target); }
+ QObject *eventTestSenderReceived() const {
+ return m_eventTestSender;
+ }
+ int eventTestSignalIndexReceived() const {
+ return m_eventTestSignalIndex;
+ }
+ QVariantList eventTestArgumentsReceived() const {
+ return m_eventTestArgs;
+ }
+ QObject *transitionSenderReceived() const {
+ return m_transitionSender;
+ }
+ int transitionSignalIndexReceived() const {
+ return m_transitionSignalIndex;
+ }
+ QVariantList transitionArgumentsReceived() const {
+ return m_transitionArgs;
+ }
+protected:
+ bool eventTest(QEvent *e) override {
+ if (!QSignalTransition::eventTest(e))
+ return false;
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
+ m_eventTestSender = se->sender();
+ m_eventTestSignalIndex = se->signalIndex();
+ m_eventTestArgs = se->arguments();
+ return true;
+ }
+ void onTransition(QEvent *e) override {
+ QSignalTransition::onTransition(e);
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
+ m_transitionSender = se->sender();
+ m_transitionSignalIndex = se->signalIndex();
+ m_transitionArgs = se->arguments();
+ }
+private:
+ QObject *m_eventTestSender;
+ int m_eventTestSignalIndex;
+ QVariantList m_eventTestArgs;
+ QObject *m_transitionSender;
+ int m_transitionSignalIndex;
+ QVariantList m_transitionArgs;
+};
+
+void tst_QStateMachine::signalTransitions()
+{
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: sender cannot be null");
+ QCOMPARE(s0->addTransition(0, SIGNAL(noSuchSignal()), 0), (QSignalTransition*)0);
+
+ SignalEmitter emitter;
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: signal cannot be null");
+ QCOMPARE(s0->addTransition(&emitter, 0, 0), (QSignalTransition*)0);
+
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add transition to null state");
+ QCOMPARE(s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), 0), (QSignalTransition*)0);
+
+ QFinalState *s1 = new QFinalState(&machine);
+ QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: no such signal SignalEmitter::noSuchSignal()");
+ QCOMPARE(s0->addTransition(&emitter, SIGNAL(noSuchSignal()), s1), (QSignalTransition*)0);
+
+ QSignalTransition *trans = s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
+ QVERIFY(trans != 0);
+ QCOMPARE(trans->sourceState(), s0);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s1);
+ QCOMPARE(trans->senderObject(), (QObject*)&emitter);
+ QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg())));
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ emitter.emitSignalWithNoArg();
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 2);
+ emitter.emitSignalWithNoArg();
+
+ trans->setSignal(SIGNAL(signalWithIntArg(int)));
+ QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithIntArg(int))));
+ machine.start();
+ QCoreApplication::processEvents();
+ emitter.emitSignalWithIntArg(123);
+ QTRY_COMPARE(finishedSpy.size(), 2);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 4);
+
+ machine.start();
+ QCoreApplication::processEvents();
+ trans->setSignal(SIGNAL(signalWithNoArg()));
+ QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg())));
+ emitter.emitSignalWithNoArg();
+ QTRY_COMPARE(finishedSpy.size(), 3);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 6);
+
+ SignalEmitter emitter2;
+ machine.start();
+ QCoreApplication::processEvents();
+ trans->setSenderObject(&emitter2);
+ emitter2.emitSignalWithNoArg();
+ QTRY_COMPARE(finishedSpy.size(), 4);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 8);
+
+ machine.start();
+ QCoreApplication::processEvents();
+ QTest::ignoreMessage(QtWarningMsg, "QSignalTransition: no such signal: SignalEmitter::noSuchSignal()");
+ trans->setSignal(SIGNAL(noSuchSignal()));
+ QCOMPARE(trans->signal(), QByteArray(SIGNAL(noSuchSignal())));
+ TEST_RUNNING_CHANGED(true);
+ TEST_ACTIVE_CHANGED(s0, 9);
+ QVERIFY(machine.isRunning());
+ }
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ QFinalState *s1 = new QFinalState(&machine);
+ SignalEmitter emitter;
+ QSignalTransition *trans = s0->addTransition(&emitter, "signalWithNoArg()", s1);
+ QVERIFY(trans != 0);
+ QCOMPARE(trans->sourceState(), s0);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s1);
+ QCOMPARE(trans->senderObject(), (QObject*)&emitter);
+ QCOMPARE(trans->signal(), QByteArray("signalWithNoArg()"));
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ emitter.emitSignalWithNoArg();
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 2);
+
+ trans->setSignal("signalWithIntArg(int)");
+ QCOMPARE(trans->signal(), QByteArray("signalWithIntArg(int)"));
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ emitter.emitSignalWithIntArg(123);
+ QTRY_COMPARE(finishedSpy.size(), 2);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 4);
+ }
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ QFinalState *s1 = new QFinalState(&machine);
+ SignalEmitter emitter;
+ TestSignalTransition *trans = new TestSignalTransition(&emitter, SIGNAL(signalWithIntArg(int)), s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ emitter.emitSignalWithIntArg(123);
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 2);
+ QCOMPARE(trans->eventTestSenderReceived(), (QObject*)&emitter);
+ QCOMPARE(trans->eventTestSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithIntArg(int)"));
+ QCOMPARE(trans->eventTestArgumentsReceived().size(), 1);
+ QCOMPARE(trans->eventTestArgumentsReceived().at(0).toInt(), 123);
+ QCOMPARE(trans->transitionSenderReceived(), (QObject*)&emitter);
+ QCOMPARE(trans->transitionSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithIntArg(int)"));
+ QCOMPARE(trans->transitionArgumentsReceived().size(), 1);
+ QCOMPARE(trans->transitionArgumentsReceived().at(0).toInt(), 123);
+ }
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ QFinalState *s1 = new QFinalState(&machine);
+ SignalEmitter emitter;
+ TestSignalTransition *trans = new TestSignalTransition(&emitter, SIGNAL(signalWithStringArg(QString)), s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+
+ QString testString = QString::fromLatin1("hello");
+ emitter.emitSignalWithStringArg(testString);
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 2);
+ QCOMPARE(trans->eventTestSenderReceived(), (QObject*)&emitter);
+ QCOMPARE(trans->eventTestSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithStringArg(QString)"));
+ QCOMPARE(trans->eventTestArgumentsReceived().size(), 1);
+ QCOMPARE(trans->eventTestArgumentsReceived().at(0).toString(), testString);
+ QCOMPARE(trans->transitionSenderReceived(), (QObject*)&emitter);
+ QCOMPARE(trans->transitionSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithStringArg(QString)"));
+ QCOMPARE(trans->transitionArgumentsReceived().size(), 1);
+ QCOMPARE(trans->transitionArgumentsReceived().at(0).toString(), testString);
+ }
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ QFinalState *s1 = new QFinalState(&machine);
+
+ TestSignalTransition *trans = new TestSignalTransition();
+ QCOMPARE(trans->senderObject(), (QObject*)0);
+ QCOMPARE(trans->signal(), QByteArray());
+
+ SignalEmitter emitter;
+ trans->setSenderObject(&emitter);
+ QCOMPARE(trans->senderObject(), (QObject*)&emitter);
+ trans->setSignal(SIGNAL(signalWithNoArg()));
+ QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg())));
+ trans->setTargetState(s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+
+ emitter.emitSignalWithNoArg();
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s0, 2);
+ }
+ // Multiple transitions for same (object,signal)
+ {
+ QStateMachine machine;
+ SignalEmitter emitter;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QSignalTransition *t0 = s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
+ QSignalTransition *t1 = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s0);
+
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s1, 0);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s0));
+
+ emitter.emitSignalWithNoArg();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 2);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ s0->removeTransition(t0);
+ emitter.emitSignalWithNoArg();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s0));
+
+ emitter.emitSignalWithNoArg();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s0));
+
+ s1->removeTransition(t1);
+ emitter.emitSignalWithNoArg();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s0));
+
+ s0->addTransition(t0);
+ s1->addTransition(t1);
+ emitter.emitSignalWithNoArg();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 4);
+ TEST_ACTIVE_CHANGED(s1, 3);
+ QVERIFY(machine.isRunning());
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+ }
+ // multiple signal transitions from same source
+ {
+ QStateMachine machine;
+ SignalEmitter emitter;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ QFinalState *s1 = new QFinalState(&machine);
+ s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
+ QFinalState *s2 = new QFinalState(&machine);
+ s0->addTransition(&emitter, SIGNAL(signalWithIntArg(int)), s2);
+ QFinalState *s3 = new QFinalState(&machine);
+ s0->addTransition(&emitter, SIGNAL(signalWithStringArg(QString)), s3);
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.setInitialState(s0);
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ emitter.emitSignalWithNoArg();
+ TEST_ACTIVE_CHANGED(s0, 2);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ QTRY_COMPARE(startedSpy.size(), 2);
+ TEST_RUNNING_CHANGED(true);
+ emitter.emitSignalWithIntArg(123);
+ TEST_ACTIVE_CHANGED(s0, 4);
+ QTRY_COMPARE(finishedSpy.size(), 2);
+ TEST_RUNNING_CHANGED(false);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 5);
+ QTRY_COMPARE(startedSpy.size(), 3);
+ TEST_RUNNING_CHANGED(true);
+ emitter.emitSignalWithStringArg("hello");
+ TEST_ACTIVE_CHANGED(s0, 6);
+ QTRY_COMPARE(finishedSpy.size(), 3);
+ TEST_RUNNING_CHANGED(false);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s3));
+ }
+ // signature normalization
+ {
+ QStateMachine machine;
+ SignalEmitter emitter;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ QFinalState *s1 = new QFinalState(&machine);
+ QSignalTransition *t0 = s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
+ QVERIFY(t0 != 0);
+ QCOMPARE(t0->signal(), QByteArray(SIGNAL(signalWithNoArg())));
+
+ QSignalTransition *t1 = s0->addTransition(&emitter, SIGNAL(signalWithStringArg(QString)), s1);
+ QVERIFY(t1 != 0);
+ QCOMPARE(t1->signal(), QByteArray(SIGNAL(signalWithStringArg(QString))));
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ QTRY_COMPARE(startedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 0);
+ TEST_RUNNING_CHANGED(true);
+
+ emitter.emitSignalWithNoArg();
+
+ TEST_ACTIVE_CHANGED(s0, 2);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ }
+}
+
+class TestEventTransition : public QEventTransition
+{
+public:
+ TestEventTransition(QState *sourceState = 0)
+ : QEventTransition(sourceState),
+ m_eventSource(0), m_eventType(QEvent::None)
+ {}
+ TestEventTransition(QObject *object, QEvent::Type type,
+ QAbstractState *target)
+ : QEventTransition(object, type),
+ m_eventSource(0), m_eventType(QEvent::None)
+ { setTargetState(target); }
+ QObject *eventSourceReceived() const {
+ return m_eventSource;
+ }
+ QEvent::Type eventTypeReceived() const {
+ return m_eventType;
+ }
+protected:
+ bool eventTest(QEvent *e) override {
+ if (!QEventTransition::eventTest(e))
+ return false;
+ QStateMachine::WrappedEvent *we = static_cast<QStateMachine::WrappedEvent*>(e);
+ m_eventSource = we->object();
+ m_eventType = we->event()->type();
+ return true;
+ }
+private:
+ QObject *m_eventSource;
+ QEvent::Type m_eventType;
+};
+
+#ifndef QT_NO_WIDGETS
+void tst_QStateMachine::eventTransitions()
+{
+ QPushButton button;
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QFinalState *s1 = new QFinalState(&machine);
+
+ QMouseEventTransition *trans;
+ trans = new QMouseEventTransition(&button, QEvent::MouseButtonPress, Qt::LeftButton);
+ QCOMPARE(trans->targetState(), (QAbstractState*)0);
+ trans->setTargetState(s1);
+ QCOMPARE(trans->eventType(), QEvent::MouseButtonPress);
+ QCOMPARE(trans->button(), Qt::LeftButton);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QTest::mousePress(&button, Qt::LeftButton);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+
+ QTest::mousePress(&button, Qt::LeftButton);
+
+ trans->setEventType(QEvent::MouseButtonRelease);
+ QCOMPARE(trans->eventType(), QEvent::MouseButtonRelease);
+ machine.start();
+ QCoreApplication::processEvents();
+ QTest::mouseRelease(&button, Qt::LeftButton);
+ QTRY_COMPARE(finishedSpy.size(), 2);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+
+ machine.start();
+ QCoreApplication::processEvents();
+ trans->setEventType(QEvent::MouseButtonPress);
+ QTest::mousePress(&button, Qt::LeftButton);
+ QTRY_COMPARE(finishedSpy.size(), 3);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+
+ QPushButton button2;
+ machine.start();
+ QCoreApplication::processEvents();
+ trans->setEventSource(&button2);
+ QTest::mousePress(&button2, Qt::LeftButton);
+ QTRY_COMPARE(finishedSpy.size(), 4);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ }
+ for (int x = 0; x < 2; ++x) {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QFinalState *s1 = new QFinalState(&machine);
+
+ QEventTransition *trans = 0;
+ if (x == 0) {
+ trans = new QEventTransition();
+ QCOMPARE(trans->eventSource(), (QObject*)0);
+ QCOMPARE(trans->eventType(), QEvent::None);
+ trans->setEventSource(&button);
+ trans->setEventType(QEvent::MouseButtonPress);
+ trans->setTargetState(s1);
+ } else if (x == 1) {
+ trans = new QEventTransition(&button, QEvent::MouseButtonPress);
+ trans->setTargetState(s1);
+ }
+ QCOMPARE(trans->eventSource(), (QObject*)&button);
+ QCOMPARE(trans->eventType(), QEvent::MouseButtonPress);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QTest::mousePress(&button, Qt::LeftButton);
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ }
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QFinalState *s1 = new QFinalState(&machine);
+
+ QMouseEventTransition *trans = new QMouseEventTransition();
+ QCOMPARE(trans->eventSource(), (QObject*)0);
+ QCOMPARE(trans->eventType(), QEvent::None);
+ QCOMPARE(trans->button(), Qt::NoButton);
+ trans->setEventSource(&button);
+ trans->setEventType(QEvent::MouseButtonPress);
+ trans->setButton(Qt::LeftButton);
+ trans->setTargetState(s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_RUNNING_CHANGED(true);
+ QTest::mousePress(&button, Qt::LeftButton);
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ }
+
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QFinalState *s1 = new QFinalState(&machine);
+
+ QKeyEventTransition *trans = new QKeyEventTransition(&button, QEvent::KeyPress, Qt::Key_A);
+ QCOMPARE(trans->eventType(), QEvent::KeyPress);
+ QCOMPARE(trans->key(), (int)Qt::Key_A);
+ trans->setTargetState(s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_RUNNING_CHANGED(true);
+
+ QTest::keyPress(&button, Qt::Key_A);
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ }
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QFinalState *s1 = new QFinalState(&machine);
+
+ QKeyEventTransition *trans = new QKeyEventTransition();
+ QCOMPARE(trans->eventSource(), (QObject*)0);
+ QCOMPARE(trans->eventType(), QEvent::None);
+ QCOMPARE(trans->key(), 0);
+ trans->setEventSource(&button);
+ trans->setEventType(QEvent::KeyPress);
+ trans->setKey(Qt::Key_A);
+ trans->setTargetState(s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_RUNNING_CHANGED(true);
+
+ QTest::keyPress(&button, Qt::Key_A);
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ }
+ // Multiple transitions for same (object,event)
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QState *s1 = new QState(&machine);
+ QEventTransition *t0 = new QEventTransition(&button, QEvent::MouseButtonPress);
+ t0->setTargetState(s1);
+ s0->addTransition(t0);
+ QEventTransition *t1 = new QEventTransition(&button, QEvent::MouseButtonPress);
+ t1->setTargetState(s0);
+ s1->addTransition(t1);
+
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s0));
+
+ QTest::mousePress(&button, Qt::LeftButton);
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ s0->removeTransition(t0);
+ QTest::mousePress(&button, Qt::LeftButton);
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s0));
+
+ QTest::mousePress(&button, Qt::LeftButton);
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s0));
+
+ s1->removeTransition(t1);
+ QTest::mousePress(&button, Qt::LeftButton);
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s0));
+
+ s0->addTransition(t0);
+ s1->addTransition(t1);
+ QTest::mousePress(&button, Qt::LeftButton);
+ QCoreApplication::processEvents();
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ }
+ // multiple event transitions from same source
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QFinalState *s1 = new QFinalState(&machine);
+ QFinalState *s2 = new QFinalState(&machine);
+ QEventTransition *t0 = new QEventTransition(&button, QEvent::MouseButtonPress);
+ t0->setTargetState(s1);
+ s0->addTransition(t0);
+ QEventTransition *t1 = new QEventTransition(&button, QEvent::MouseButtonRelease);
+ t1->setTargetState(s2);
+ s0->addTransition(t1);
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.setInitialState(s0);
+
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ QTest::mousePress(&button, Qt::LeftButton);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 2);
+ TEST_RUNNING_CHANGED(true);
+ QTest::mouseRelease(&button, Qt::LeftButton);
+ QTRY_COMPARE(finishedSpy.size(), 2);
+ TEST_RUNNING_CHANGED(false);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+ }
+ // custom event
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QFinalState *s1 = new QFinalState(&machine);
+
+ QEventTransition *trans = new QEventTransition(&button, QEvent::Type(QEvent::User+1));
+ trans->setTargetState(s1);
+ s0->addTransition(trans);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QVERIFY(startedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QTest::ignoreMessage(QtWarningMsg, "QObject event transitions are not supported for custom types");
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ }
+ // custom transition
+ {
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ QFinalState *s1 = new QFinalState(&machine);
+
+ TestEventTransition *trans = new TestEventTransition(&button, QEvent::MouseButtonPress, s1);
+ s0->addTransition(trans);
+ QCOMPARE(trans->eventSourceReceived(), (QObject*)0);
+ QCOMPARE(trans->eventTypeReceived(), QEvent::None);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.setInitialState(s0);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_RUNNING_CHANGED(true);
+
+ QTest::mousePress(&button, Qt::LeftButton);
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+
+ QCOMPARE(trans->eventSourceReceived(), (QObject*)&button);
+ QCOMPARE(trans->eventTypeReceived(), QEvent::MouseButtonPress);
+ }
+}
+
+void tst_QStateMachine::graphicsSceneEventTransitions()
+{
+ QGraphicsScene scene;
+ QGraphicsTextItem *textItem = scene.addText("foo");
+
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ QFinalState *s2 = new QFinalState(&machine);
+ QEventTransition *t = new QEventTransition(textItem, QEvent::GraphicsSceneMouseMove);
+ t->setTargetState(s2);
+ s1->addTransition(t);
+ machine.setInitialState(s1);
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 0);
+ TEST_RUNNING_CHANGED(true);
+ QGraphicsSceneMouseEvent mouseEvent(QEvent::GraphicsSceneMouseMove);
+ scene.sendEvent(textItem, &mouseEvent);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+}
+#endif
+
+void tst_QStateMachine::historyStates()
+{
+ for (int x = 0; x < 2; ++x) {
+ QStateMachine machine;
+ QState *root = &machine;
+ QState *s0 = new QState(root);
+ DEFINE_ACTIVE_SPY(s0);
+ QState *s00 = new QState(s0);
+ DEFINE_ACTIVE_SPY(s00);
+ QState *s01 = new QState(s0);
+ DEFINE_ACTIVE_SPY(s01);
+ QHistoryState *s0h;
+ if (x == 0) {
+ s0h = new QHistoryState(s0);
+ QCOMPARE(s0h->historyType(), QHistoryState::ShallowHistory);
+ s0h->setHistoryType(QHistoryState::DeepHistory);
+ } else {
+ s0h = new QHistoryState(QHistoryState::DeepHistory, s0);
+ }
+ QCOMPARE(s0h->historyType(), QHistoryState::DeepHistory);
+ s0h->setHistoryType(QHistoryState::ShallowHistory);
+ QCOMPARE(s0h->historyType(), QHistoryState::ShallowHistory);
+ QCOMPARE(s0h->defaultState(), (QAbstractState*)0);
+ s0h->setDefaultState(s00);
+ QCOMPARE(s0h->defaultState(), (QAbstractState*)s00);
+ const QString warning
+ = QString::asprintf("QHistoryState::setDefaultState: state %p does not belong to this history state's group (%p)", s0, s0);
+ QTest::ignoreMessage(QtWarningMsg, qPrintable(warning));
+ s0h->setDefaultState(s0);
+ QState *s1 = new QState(root);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *s2 = new QFinalState(root);
+
+ s00->addTransition(new StringTransition("a", s01));
+ s0->addTransition(new StringTransition("b", s1));
+ s1->addTransition(new StringTransition("c", s0h));
+ s0->addTransition(new StringTransition("d", s2));
+
+ root->setInitialState(s0);
+ s0->setInitialState(s00);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s00, 1);
+ TEST_ACTIVE_CHANGED(s01, 0);
+ TEST_ACTIVE_CHANGED(s1, 0);
+ QCOMPARE(machine.configuration().size(), 2);
+ QVERIFY(machine.configuration().contains(s0));
+ QVERIFY(machine.configuration().contains(s00));
+
+ machine.postEvent(new StringEvent("a"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s00, 2);
+ TEST_ACTIVE_CHANGED(s01, 1);
+ TEST_ACTIVE_CHANGED(s1, 0);
+ QCOMPARE(machine.configuration().size(), 2);
+ QVERIFY(machine.configuration().contains(s0));
+ QVERIFY(machine.configuration().contains(s01));
+
+ machine.postEvent(new StringEvent("b"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 2);
+ TEST_ACTIVE_CHANGED(s00, 2);
+ TEST_ACTIVE_CHANGED(s01, 2);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ machine.postEvent(new StringEvent("c"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 3);
+ TEST_ACTIVE_CHANGED(s00, 2);
+ TEST_ACTIVE_CHANGED(s01, 3);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QCOMPARE(machine.configuration().size(), 2);
+ QVERIFY(machine.configuration().contains(s0));
+ QVERIFY(machine.configuration().contains(s01));
+
+ machine.postEvent(new StringEvent("d"));
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s0, 4);
+ TEST_ACTIVE_CHANGED(s00, 2);
+ TEST_ACTIVE_CHANGED(s01, 4);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ }
+}
+
+void tst_QStateMachine::startAndStop()
+{
+ QStateMachine machine;
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(stoppedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+
+ QVERIFY(!machine.isRunning());
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::start: No initial state set for machine. Refusing to start.");
+ machine.start();
+ QCOMPARE(startedSpy.size(), 0);
+ QCOMPARE(stoppedSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
+ QCOMPARE(runningSpy.size(), 0);
+ QVERIFY(!machine.isRunning());
+ machine.stop();
+ QCOMPARE(startedSpy.size(), 0);
+ QCOMPARE(stoppedSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
+ QCOMPARE(runningSpy.size(), 0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QTRY_COMPARE(machine.isRunning(), true);
+ QTRY_COMPARE(startedSpy.size(), 1);
+ QCOMPARE(stoppedSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
+ TEST_RUNNING_CHANGED(true);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::start(): already running");
+ machine.start();
+ QCOMPARE(runningSpy.size(), 0);
+
+ machine.stop();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QTRY_COMPARE(machine.isRunning(), false);
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ QCOMPARE(startedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 0);
+ TEST_RUNNING_CHANGED(false);
+
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 3);
+ machine.stop();
+ TEST_ACTIVE_CHANGED(s1, 3);
+ QTRY_COMPARE(startedSpy.size(), 2);
+ QTRY_COMPARE(stoppedSpy.size(), 2);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+}
+
+void tst_QStateMachine::setRunning()
+{
+ QStateMachine machine;
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(stoppedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+
+ QVERIFY(!machine.isRunning());
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::start: No initial state set for machine. Refusing to start.");
+ machine.setRunning(true);
+ QCOMPARE(startedSpy.size(), 0);
+ QCOMPARE(stoppedSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
+ QCOMPARE(runningSpy.size(), 0);
+ QVERIFY(!machine.isRunning());
+ machine.setRunning(false);
+ QCOMPARE(startedSpy.size(), 0);
+ QCOMPARE(stoppedSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
+ QCOMPARE(runningSpy.size(), 0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ machine.setRunning(true);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QTRY_COMPARE(machine.isRunning(), true);
+ QTRY_COMPARE(startedSpy.size(), 1);
+ QCOMPARE(stoppedSpy.size(), 0);
+ QCOMPARE(finishedSpy.size(), 0);
+ TEST_RUNNING_CHANGED(true);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::start(): already running");
+ machine.setRunning(true);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(runningSpy.size(), 0);
+
+ machine.setRunning(false);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QTRY_COMPARE(machine.isRunning(), false);
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ QCOMPARE(startedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 0);
+ TEST_RUNNING_CHANGED(false);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ machine.setRunning(false);
+ QCOMPARE(runningSpy.size(), 0);
+ TEST_ACTIVE_CHANGED(s1, 1);
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 3);
+ machine.setRunning(false);
+ TEST_ACTIVE_CHANGED(s1, 3);
+ QTRY_COMPARE(startedSpy.size(), 2);
+ QTRY_COMPARE(stoppedSpy.size(), 2);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QState *s1_1 = new QState(s1);
+ QFinalState *s1_2 = new QFinalState(s1);
+ s1_1->addTransition(s1_2);
+ s1->setInitialState(s1_1);
+ QFinalState *s2 = new QFinalState(&machine);
+ s1->addTransition(s1, SIGNAL(finished()), s2);
+ machine.setRunning(false);
+ QCOMPARE(runningSpy.size(), 0);
+ machine.setRunning(true);
+ TEST_ACTIVE_CHANGED(s1, 6);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QTRY_COMPARE(startedSpy.size(), 3);
+ QCOMPARE(stoppedSpy.size(), 2);
+ QCOMPARE(finishedSpy.size(), 1);
+}
+
+void tst_QStateMachine::targetStateWithNoParent()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->setObjectName("s1");
+ QState s2;
+ s1->addTransition(&s2);
+ machine.setInitialState(s1);
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(stoppedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+
+ machine.start();
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
+ "Child mode of state machine '' is not 'ExclusiveStates'.");
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QTRY_COMPARE(startedSpy.size(), 1);
+ QCOMPARE(machine.isRunning(), false);
+ QCOMPARE(stoppedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 0);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(machine.error(), QStateMachine::StateMachineChildModeSetToParallelError);
+}
+
+void tst_QStateMachine::targetStateDeleted()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ s1->setObjectName("s1");
+ QState *s2 = new QState(&machine);
+ QAbstractTransition *trans = s1->addTransition(s2);
+ delete s2;
+ QCOMPARE(trans->targetState(), (QAbstractState*)0);
+ QVERIFY(trans->targetStates().isEmpty());
+}
+
+void tst_QStateMachine::defaultGlobalRestorePolicy()
+{
+ QStateMachine machine;
+
+ QObject *propertyHolder = new QObject(&machine);
+ propertyHolder->setProperty("a", 1);
+ propertyHolder->setProperty("b", 2);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(propertyHolder, "a", 3);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(propertyHolder, "b", 4);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+ s2->addTransition(new EventTransition(QEvent::User, s3));
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QCOMPARE(propertyHolder->property("a").toInt(), 3);
+ QCOMPARE(propertyHolder->property("b").toInt(), 2);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QCOMPARE(propertyHolder->property("a").toInt(), 3);
+ QCOMPARE(propertyHolder->property("b").toInt(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QCOMPARE(propertyHolder->property("a").toInt(), 3);
+ QCOMPARE(propertyHolder->property("b").toInt(), 4);
+}
+
+void tst_QStateMachine::noInitialStateForInitialState()
+{
+ QStateMachine machine;
+
+ QState *initialState = new QState(&machine);
+ DEFINE_ACTIVE_SPY(initialState);
+ initialState->setObjectName("initialState");
+ machine.setInitialState(initialState);
+
+ QState *childState = new QState(initialState);
+ DEFINE_ACTIVE_SPY(childState);
+ (void)childState;
+
+ QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: "
+ "Missing initial state in compound state 'initialState'");
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(initialState, 1);
+ TEST_ACTIVE_CHANGED(childState, 0);
+ QCOMPARE(machine.isRunning(), false);
+ QCOMPARE(int(machine.error()), int(QStateMachine::NoInitialStateError));
+}
+
+void tst_QStateMachine::globalRestorePolicySetToDontRestore()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::DontRestoreProperties);
+
+ QObject *propertyHolder = new QObject(&machine);
+ propertyHolder->setProperty("a", 1);
+ propertyHolder->setProperty("b", 2);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(propertyHolder, "a", 3);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(propertyHolder, "b", 4);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+ s2->addTransition(new EventTransition(QEvent::User, s3));
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QCOMPARE(propertyHolder->property("a").toInt(), 3);
+ QCOMPARE(propertyHolder->property("b").toInt(), 2);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QCOMPARE(propertyHolder->property("a").toInt(), 3);
+ QCOMPARE(propertyHolder->property("b").toInt(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QCOMPARE(propertyHolder->property("a").toInt(), 3);
+ QCOMPARE(propertyHolder->property("b").toInt(), 4);
+}
+
+void tst_QStateMachine::globalRestorePolicySetToRestore()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ QObject *propertyHolder = new QObject(&machine);
+ propertyHolder->setProperty("a", 1);
+ propertyHolder->setProperty("b", 2);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(propertyHolder, "a", 3);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(propertyHolder, "b", 4);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+ s2->addTransition(new EventTransition(QEvent::User, s3));
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QCOMPARE(propertyHolder->property("a").toInt(), 3);
+ QCOMPARE(propertyHolder->property("b").toInt(), 2);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QCOMPARE(propertyHolder->property("a").toInt(), 1);
+ QCOMPARE(propertyHolder->property("b").toInt(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QCOMPARE(propertyHolder->property("a").toInt(), 1);
+ QCOMPARE(propertyHolder->property("b").toInt(), 2);
+}
+
+void tst_QStateMachine::transitionWithParent()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ QState *s2 = new QState(&machine);
+ EventTransition *trans = new EventTransition(QEvent::User, s2, s1);
+ QCOMPARE(trans->sourceState(), s1);
+ QCOMPARE(trans->targetState(), (QAbstractState*)s2);
+ QCOMPARE(trans->targetStates().size(), 1);
+ QCOMPARE(trans->targetStates().at(0), (QAbstractState*)s2);
+}
+
+void tst_QStateMachine::simpleAnimation()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("fooBar", 1.0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "fooBar", 2.0);
+
+ EventTransition *et = new EventTransition(QEvent::User, s2);
+ QPropertyAnimation *animation = new QPropertyAnimation(object, "fooBar", s2);
+ et->addAnimation(animation);
+ s1->addTransition(et);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ s2->addTransition(animation, SIGNAL(finished()), s3);
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("fooBar").toDouble(), 2.0);
+}
+
+class SlotCalledCounter: public QObject
+{
+ Q_OBJECT
+public:
+ SlotCalledCounter() : counter(0) {}
+
+ int counter;
+
+public slots:
+ void slot() { counter++; }
+};
+
+void tst_QStateMachine::twoAnimations()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 1.0);
+ object->setProperty("bar", 3.0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 2.0);
+ s2->assignProperty(object, "bar", 10.0);
+
+ QPropertyAnimation *animationFoo = new QPropertyAnimation(object, "foo", s2);
+ QPropertyAnimation *animationBar = new QPropertyAnimation(object, "bar", s2);
+ animationBar->setDuration(900);
+
+ SlotCalledCounter counter;
+ connect(animationFoo, SIGNAL(finished()), &counter, SLOT(slot()));
+ connect(animationBar, SIGNAL(finished()), &counter, SLOT(slot()));
+
+ EventTransition *et = new EventTransition(QEvent::User, s2);
+ et->addAnimation(animationFoo);
+ et->addAnimation(animationBar);
+ s1->addTransition(et);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+ s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3);
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCOREAPPLICATION_EXEC(5000);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("foo").toDouble(), 2.0);
+ QCOMPARE(object->property("bar").toDouble(), 10.0);
+
+ QCOMPARE(counter.counter, 2);
+}
+
+void tst_QStateMachine::twoAnimatedTransitions()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 1.0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 5.0);
+ QPropertyAnimation *fooAnimation = new QPropertyAnimation(object, "foo", s2);
+ EventTransition *trans = new EventTransition(QEvent::User, s2);
+ s1->addTransition(trans);
+ trans->addAnimation(fooAnimation);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+ s2->addTransition(fooAnimation, SIGNAL(finished()), s3);
+
+ QState *s4 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s4);
+ s4->assignProperty(object, "foo", 2.0);
+ QPropertyAnimation *fooAnimation2 = new QPropertyAnimation(object, "foo", s4);
+ trans = new EventTransition(QEvent::User, s4);
+ s3->addTransition(trans);
+ trans->addAnimation(fooAnimation2);
+
+ QState *s5 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s5);
+ QObject::connect(s5, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+ s4->addTransition(fooAnimation2, SIGNAL(finished()), s5);
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ TEST_ACTIVE_CHANGED(s5, 0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ TEST_ACTIVE_CHANGED(s5, 0);
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("foo").toDouble(), 5.0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 2);
+ TEST_ACTIVE_CHANGED(s5, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s5));
+ QCOMPARE(object->property("foo").toDouble(), 2.0);
+}
+
+void tst_QStateMachine::playAnimationTwice()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 1.0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 5.0);
+ QPropertyAnimation *fooAnimation = new QPropertyAnimation(object, "foo", s2);
+ EventTransition *trans = new EventTransition(QEvent::User, s2);
+ s1->addTransition(trans);
+ trans->addAnimation(fooAnimation);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+ s2->addTransition(fooAnimation, SIGNAL(finished()), s3);
+
+ QState *s4 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s4);
+ s4->assignProperty(object, "foo", 2.0);
+ trans = new EventTransition(QEvent::User, s4);
+ s3->addTransition(trans);
+ trans->addAnimation(fooAnimation);
+
+ QState *s5 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s5);
+ QObject::connect(s5, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+ s4->addTransition(fooAnimation, SIGNAL(finished()), s5);
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ TEST_ACTIVE_CHANGED(s5, 0);
+ machine.postEvent(new QEvent(QEvent::User));
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ TEST_ACTIVE_CHANGED(s5, 0);
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("foo").toDouble(), 5.0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 2);
+ TEST_ACTIVE_CHANGED(s5, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s5));
+ QCOMPARE(object->property("foo").toDouble(), 2.0);
+}
+
+void tst_QStateMachine::nestedTargetStateForAnimation()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 1.0);
+ object->setProperty("bar", 3.0);
+
+ SlotCalledCounter counter;
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+
+ s2->assignProperty(object, "foo", 2.0);
+
+ QState *s2Child = new QState(s2);
+ DEFINE_ACTIVE_SPY(s2Child);
+ s2Child->assignProperty(object, "bar", 10.0);
+ s2->setInitialState(s2Child);
+
+ QState *s2Child2 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s2Child2);
+ s2Child2->assignProperty(object, "bar", 11.0);
+ QAbstractTransition *at = new EventTransition(QEvent::User, s2Child2);
+ s2Child->addTransition(at);
+
+ QPropertyAnimation *animation = new QPropertyAnimation(object, "bar", s2);
+ animation->setDuration(2000);
+ connect(animation, SIGNAL(finished()), &counter, SLOT(slot()));
+ at->addAnimation(animation);
+
+ at = new EventTransition(QEvent::User, s2);
+ s1->addTransition(at);
+
+ animation = new QPropertyAnimation(object, "foo", s2);
+ connect(animation, SIGNAL(finished()), &counter, SLOT(slot()));
+ at->addAnimation(animation);
+
+ animation = new QPropertyAnimation(object, "bar", s2);
+ connect(animation, SIGNAL(finished()), &counter, SLOT(slot()));
+ at->addAnimation(animation);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ s2->addTransition(s2Child, SIGNAL(propertiesAssigned()), s3);
+
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s2Child, 0);
+ TEST_ACTIVE_CHANGED(s2Child2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ machine.postEvent(new QEvent(QEvent::User));
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s2Child, 1);
+ TEST_ACTIVE_CHANGED(s2Child2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s2Child, 2);
+ TEST_ACTIVE_CHANGED(s2Child2, 0);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("foo").toDouble(), 2.0);
+ QCOMPARE(object->property("bar").toDouble(), 10.0);
+ QCOMPARE(counter.counter, 2);
+}
+
+void tst_QStateMachine::propertiesAssignedSignalTransitionsReuseAnimationGroup()
+{
+ QStateMachine machine;
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(object, "foo", 123);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 456);
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ s3->assignProperty(object, "foo", 789);
+ QFinalState *s4 = new QFinalState(&machine);
+
+ QParallelAnimationGroup animationGroup;
+ animationGroup.addAnimation(new QPropertyAnimation(object, "foo"));
+ QSignalSpy animationFinishedSpy(&animationGroup, &QParallelAnimationGroup::finished);
+ QVERIFY(animationFinishedSpy.isValid());
+ s1->addTransition(s1, SIGNAL(propertiesAssigned()), s2)->addAnimation(&animationGroup);
+ s2->addTransition(s2, SIGNAL(propertiesAssigned()), s3)->addAnimation(&animationGroup);
+ s3->addTransition(s3, SIGNAL(propertiesAssigned()), s4);
+
+ machine.setInitialState(s1);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy machineFinishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(machineFinishedSpy.isValid());
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ QTRY_COMPARE(machineFinishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QVERIFY(!machine.isRunning());
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s4));
+ QCOMPARE(object->property("foo").toInt(), 789);
+ QCOMPARE(animationFinishedSpy.size(), 2);
+
+}
+
+void tst_QStateMachine::animatedGlobalRestoreProperty()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 1.0);
+
+ SlotCalledCounter counter;
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 2.0);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+
+ QState *s4 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s4);
+ QObject::connect(s4, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+
+ QAbstractTransition *at = new EventTransition(QEvent::User, s2);
+ s1->addTransition(at);
+ QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", s2);
+ connect(pa, SIGNAL(finished()), &counter, SLOT(slot()));
+ at->addAnimation(pa);
+
+ at = s2->addTransition(pa, SIGNAL(finished()), s3);
+ pa = new QPropertyAnimation(object, "foo", s3);
+ connect(pa, SIGNAL(finished()), &counter, SLOT(slot()));
+ at->addAnimation(pa);
+
+ at = s3->addTransition(pa, SIGNAL(finished()), s4);
+ pa = new QPropertyAnimation(object, "foo", s4);
+ connect(pa, SIGNAL(finished()), &counter, SLOT(slot()));
+ at->addAnimation(pa);
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ TEST_ACTIVE_CHANGED(s4, 0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ TEST_ACTIVE_CHANGED(s4, 0);
+
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s4));
+ QCOMPARE(object->property("foo").toDouble(), 1.0);
+ QCOMPARE(counter.counter, 2);
+}
+
+void tst_QStateMachine::specificTargetValueOfAnimation()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 1.0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 2.0);
+
+ QPropertyAnimation *anim = new QPropertyAnimation(object, "foo");
+ anim->setEndValue(10.0);
+ EventTransition *trans = new EventTransition(QEvent::User, s2);
+ s1->addTransition(trans);
+ trans->addAnimation(anim);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+ s2->addTransition(anim, SIGNAL(finished()), s3);
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("foo").toDouble(), 2.0);
+ QCOMPARE(anim->endValue().toDouble(), 10.0);
+
+ delete anim;
+}
+
+void tst_QStateMachine::addDefaultAnimation()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject();
+ object->setProperty("foo", 1.0);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 2.0);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+
+ QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", &machine);
+ machine.addDefaultAnimation(pa);
+ s2->addTransition(pa, SIGNAL(finished()), s3);
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("foo").toDouble(), 2.0);
+
+ delete object;
+}
+
+void tst_QStateMachine::addDefaultAnimationWithUnusedAnimation()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 1.0);
+ object->setProperty("bar", 2.0);
+
+ SlotCalledCounter counter;
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 2.0);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+
+ QPropertyAnimation *pa = new QPropertyAnimation(object, "foo", &machine);
+ connect(pa, SIGNAL(finished()), &counter, SLOT(slot()));
+ machine.addDefaultAnimation(pa);
+ s2->addTransition(pa, SIGNAL(finished()), s3);
+
+ pa = new QPropertyAnimation(object, "bar", &machine);
+ connect(pa, SIGNAL(finished()), &counter, SLOT(slot()));
+ machine.addDefaultAnimation(pa);
+
+ machine.setInitialState(s1);
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("foo").toDouble(), 2.0);
+ QCOMPARE(counter.counter, 1);
+}
+
+void tst_QStateMachine::removeDefaultAnimation()
+{
+ QStateMachine machine;
+
+ QObject propertyHolder;
+ propertyHolder.setProperty("foo", 0);
+
+ QCOMPARE(machine.defaultAnimations().size(), 0);
+
+ QPropertyAnimation *anim = new QPropertyAnimation(&propertyHolder, "foo");
+
+ machine.addDefaultAnimation(anim);
+
+ QCOMPARE(machine.defaultAnimations().size(), 1);
+ QVERIFY(machine.defaultAnimations().contains(anim));
+
+ machine.removeDefaultAnimation(anim);
+
+ QCOMPARE(machine.defaultAnimations().size(), 0);
+
+ machine.addDefaultAnimation(anim);
+
+ QPropertyAnimation *anim2 = new QPropertyAnimation(&propertyHolder, "foo");
+ machine.addDefaultAnimation(anim2);
+
+ QCOMPARE(machine.defaultAnimations().size(), 2);
+ QVERIFY(machine.defaultAnimations().contains(anim));
+ QVERIFY(machine.defaultAnimations().contains(anim2));
+
+ machine.removeDefaultAnimation(anim);
+
+ QCOMPARE(machine.defaultAnimations().size(), 1);
+ QVERIFY(machine.defaultAnimations().contains(anim2));
+
+ machine.removeDefaultAnimation(anim2);
+ QCOMPARE(machine.defaultAnimations().size(), 0);
+
+ delete anim;
+ delete anim2;
+}
+
+void tst_QStateMachine::overrideDefaultAnimationWithSpecific()
+{
+ QStateMachine machine;
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("foo", 1.0);
+
+ SlotCalledCounter counter;
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(object, "foo", 2.0);
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit()));
+
+ QAbstractTransition *at = new EventTransition(QEvent::User, s2);
+ s1->addTransition(at);
+
+ QPropertyAnimation *defaultAnimation = new QPropertyAnimation(object, "foo");
+ connect(defaultAnimation, SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), &counter, SLOT(slot()));
+
+ QPropertyAnimation *moreSpecificAnimation = new QPropertyAnimation(object, "foo");
+ s2->addTransition(moreSpecificAnimation, SIGNAL(finished()), s3);
+ connect(moreSpecificAnimation, SIGNAL(stateChanged(QAbstractAnimation::State,QAbstractAnimation::State)), &counter, SLOT(slot()));
+
+ machine.addDefaultAnimation(defaultAnimation);
+ at->addAnimation(moreSpecificAnimation);
+
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ QCOREAPPLICATION_EXEC(5000);
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QVERIFY(machine.configuration().contains(s3));
+ QCOMPARE(counter.counter, 2); // specific animation started and stopped
+
+ delete defaultAnimation;
+ delete moreSpecificAnimation;
+}
+
+void tst_QStateMachine::parallelStateAssignmentsDone()
+{
+ QStateMachine machine;
+
+ QObject *propertyHolder = new QObject(&machine);
+ propertyHolder->setProperty("foo", 123);
+ propertyHolder->setProperty("bar", 456);
+ propertyHolder->setProperty("zoot", 789);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+
+ QState *parallelState = new QState(QState::ParallelStates, &machine);
+ parallelState->assignProperty(propertyHolder, "foo", 321);
+
+ QState *s2 = new QState(parallelState);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(propertyHolder, "bar", 654);
+
+ QState *s3 = new QState(parallelState);
+ DEFINE_ACTIVE_SPY(s3);
+ s3->assignProperty(propertyHolder, "zoot", 987);
+
+ s1->addTransition(new EventTransition(QEvent::User, parallelState));
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+
+ QCOMPARE(propertyHolder->property("foo").toInt(), 123);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 456);
+ QCOMPARE(propertyHolder->property("zoot").toInt(), 789);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QCOMPARE(propertyHolder->property("foo").toInt(), 321);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 654);
+ QCOMPARE(propertyHolder->property("zoot").toInt(), 987);
+}
+
+void tst_QStateMachine::transitionsFromParallelStateWithNoChildren()
+{
+ QStateMachine machine;
+
+ QState *parallelState = new QState(QState::ParallelStates, &machine);
+ DEFINE_ACTIVE_SPY(parallelState);
+ machine.setInitialState(parallelState);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ parallelState->addTransition(new EventTransition(QEvent::User, s1));
+
+ machine.start();
+ QCoreApplication::processEvents();
+ TEST_ACTIVE_CHANGED(parallelState, 1);
+ TEST_ACTIVE_CHANGED(s1, 0);
+
+ QCOMPARE(1, machine.configuration().size());
+ QVERIFY(machine.configuration().contains(parallelState));
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(parallelState, 2);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QVERIFY(machine.isRunning());
+ QCOMPARE(1, machine.configuration().size());
+ QVERIFY(machine.configuration().contains(s1));
+}
+
+void tst_QStateMachine::parallelStateTransition()
+{
+ // This test checks if the parallel state is exited and re-entered if one compound state
+ // is exited and subsequently re-entered. When the parallel state is exited, the other compound
+ // state in the parallel state has to be exited too. When the parallel state is re-entered, the
+ // other state also needs to be re-entered.
+
+ QStateMachine machine;
+
+ QState *parallelState = new QState(QState::ParallelStates, &machine);
+ parallelState->setObjectName("parallelState");
+ DEFINE_ACTIVE_SPY(parallelState);
+ machine.setInitialState(parallelState);
+
+ QState *s1 = new QState(parallelState);
+ s1->setObjectName("s1");
+ DEFINE_ACTIVE_SPY(s1);
+ QState *s2 = new QState(parallelState);
+ s2->setObjectName("s2");
+ DEFINE_ACTIVE_SPY(s2);
+
+ QState *s1InitialChild = new QState(s1);
+ s1InitialChild->setObjectName("s1InitialChild");
+ DEFINE_ACTIVE_SPY(s1InitialChild);
+ s1->setInitialState(s1InitialChild);
+
+ QState *s2InitialChild = new QState(s2);
+ s2InitialChild->setObjectName("s2InitialChild");
+ DEFINE_ACTIVE_SPY(s2InitialChild);
+ s2->setInitialState(s2InitialChild);
+
+ QState *s1OtherChild = new QState(s1);
+ s1OtherChild->setObjectName("s1OtherChild");
+ DEFINE_ACTIVE_SPY(s1OtherChild);
+
+ // The following transition will exit s1 (which means that parallelState is also exited), and
+ // subsequently re-entered (which means that parallelState is also re-entered).
+ EventTransition *et = new EventTransition(QEvent::User, s1OtherChild);
+ et->setObjectName("s1->s1OtherChild");
+ s1->addTransition(et);
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ // Initial entrance of the parallel state and its sub-states:
+ TEST_ACTIVE_CHANGED(parallelState, 1);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s1InitialChild, 1);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s2InitialChild, 1);
+ TEST_ACTIVE_CHANGED(s1OtherChild, 0);
+
+ QVERIFY(machine.configuration().contains(parallelState));
+ QVERIFY(machine.configuration().contains(s1));
+ QVERIFY(machine.configuration().contains(s2));
+ QVERIFY(machine.configuration().contains(s1InitialChild));
+ QVERIFY(machine.configuration().contains(s2InitialChild));
+ QCOMPARE(machine.configuration().size(), 5);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(parallelState, 3); // initial + exit + entry
+ TEST_ACTIVE_CHANGED(s1, 3); // initial + exit + entry
+ TEST_ACTIVE_CHANGED(s1InitialChild, 2); // initial + exit
+ TEST_ACTIVE_CHANGED(s2, 3); // initial + exit due to parent exit + entry due to parent re-entry
+ TEST_ACTIVE_CHANGED(s2InitialChild, 3); // initial + exit due to parent exit + re-entry due to parent re-entry
+ TEST_ACTIVE_CHANGED(s1OtherChild, 1); // entry due to transition
+ QVERIFY(machine.isRunning());
+
+ // Check that s1InitialChild is not in the configuration, because although s1 is re-entered,
+ // another child state (s1OtherChild) is active, so the initial child should not be activated.
+ QVERIFY(machine.configuration().contains(parallelState));
+ QVERIFY(machine.configuration().contains(s1));
+ QVERIFY(machine.configuration().contains(s2));
+ QVERIFY(machine.configuration().contains(s1OtherChild));
+ QVERIFY(machine.configuration().contains(s2InitialChild));
+ QCOMPARE(machine.configuration().size(), 5);
+}
+
+void tst_QStateMachine::nestedRestoreProperties()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ QObject *propertyHolder = new QObject(&machine);
+ propertyHolder->setProperty("foo", 1);
+ propertyHolder->setProperty("bar", 2);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(propertyHolder, "foo", 3);
+
+ QState *s21 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s21);
+ s21->assignProperty(propertyHolder, "bar", 4);
+ s2->setInitialState(s21);
+
+ QState *s22 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s22);
+ s22->assignProperty(propertyHolder, "bar", 5);
+
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+ s21->addTransition(new EventTransition(QEvent::User, s22));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s21, 0);
+ TEST_ACTIVE_CHANGED(s22, 0);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+ QCOMPARE(propertyHolder->property("foo").toInt(), 1);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 2);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s21, 1);
+ TEST_ACTIVE_CHANGED(s22, 0);
+ QCOMPARE(machine.configuration().size(), 2);
+ QVERIFY(machine.configuration().contains(s2));
+ QVERIFY(machine.configuration().contains(s21));
+ QCOMPARE(propertyHolder->property("foo").toInt(), 3);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s21, 2);
+ TEST_ACTIVE_CHANGED(s22, 1);
+ QVERIFY(machine.isRunning());
+ QCOMPARE(machine.configuration().size(), 2);
+ QVERIFY(machine.configuration().contains(s2));
+ QVERIFY(machine.configuration().contains(s22));
+ QCOMPARE(propertyHolder->property("foo").toInt(), 3);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 5);
+}
+
+void tst_QStateMachine::nestedRestoreProperties2()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ QObject *propertyHolder = new QObject(&machine);
+ propertyHolder->setProperty("foo", 1);
+ propertyHolder->setProperty("bar", 2);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(propertyHolder, "foo", 3);
+
+ QState *s21 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s21);
+ s21->assignProperty(propertyHolder, "bar", 4);
+ s2->setInitialState(s21);
+
+ QState *s22 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s22);
+ s22->assignProperty(propertyHolder, "foo", 6);
+ s22->assignProperty(propertyHolder, "bar", 5);
+
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+ s21->addTransition(new EventTransition(QEvent::User, s22));
+ s22->addTransition(new EventTransition(QEvent::User, s21));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s21, 0);
+ TEST_ACTIVE_CHANGED(s22, 0);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+ QCOMPARE(propertyHolder->property("foo").toInt(), 1);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 2);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s21, 1);
+ TEST_ACTIVE_CHANGED(s22, 0);
+ QCOMPARE(machine.configuration().size(), 2);
+ QVERIFY(machine.configuration().contains(s2));
+ QVERIFY(machine.configuration().contains(s21));
+ QCOMPARE(propertyHolder->property("foo").toInt(), 3);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s21, 2);
+ TEST_ACTIVE_CHANGED(s22, 1);
+ QCOMPARE(machine.configuration().size(), 2);
+ QVERIFY(machine.configuration().contains(s2));
+ QVERIFY(machine.configuration().contains(s22));
+ QCOMPARE(propertyHolder->property("foo").toInt(), 6);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 5);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QCoreApplication::processEvents();
+
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s21, 3);
+ TEST_ACTIVE_CHANGED(s22, 2);
+ QCOMPARE(machine.configuration().size(), 2);
+ QVERIFY(machine.configuration().contains(s2));
+ QVERIFY(machine.configuration().contains(s21));
+ QCOMPARE(propertyHolder->property("foo").toInt(), 3);
+ QCOMPARE(propertyHolder->property("bar").toInt(), 4);
+
+}
+
+void tst_QStateMachine::nestedStateMachines()
+{
+ QStateMachine machine;
+ QState *group = new QState(&machine);
+ DEFINE_ACTIVE_SPY(group);
+ group->setChildMode(QState::ParallelStates);
+ QStateMachine *subMachines[3];
+ for (int i = 0; i < 3; ++i) {
+ QState *subGroup = new QState(group);
+ QStateMachine *subMachine = new QStateMachine(subGroup);
+ {
+ QState *initial = new QState(subMachine);
+ QFinalState *done = new QFinalState(subMachine);
+ initial->addTransition(new EventTransition(QEvent::User, done));
+ subMachine->setInitialState(initial);
+ }
+ QFinalState *subMachineDone = new QFinalState(subGroup);
+ subMachine->addTransition(subMachine, SIGNAL(finished()), subMachineDone);
+ subGroup->setInitialState(subMachine);
+ subMachines[i] = subMachine;
+ }
+ QFinalState *final = new QFinalState(&machine);
+ group->addTransition(group, SIGNAL(finished()), final);
+ machine.setInitialState(group);
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ QTRY_COMPARE(machine.configuration().size(), 1+2*3);
+ QVERIFY(machine.configuration().contains(group));
+ for (int i = 0; i < 3; ++i)
+ QVERIFY(machine.configuration().contains(subMachines[i]));
+
+ QCoreApplication::processEvents(); // starts the submachines
+ TEST_ACTIVE_CHANGED(group, 1);
+
+ for (int i = 0; i < 3; ++i)
+ subMachines[i]->postEvent(new QEvent(QEvent::User));
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ TEST_ACTIVE_CHANGED(group, 2);
+}
+
+void tst_QStateMachine::goToState()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ QState *s2 = new QState(&machine);
+ machine.setInitialState(s1);
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QVERIFY(startedSpy.isValid());
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+
+ QStateMachinePrivate::get(&machine)->goToState(s2);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+
+ QStateMachinePrivate::get(&machine)->goToState(s2);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+
+ QStateMachinePrivate::get(&machine)->goToState(s1);
+ QStateMachinePrivate::get(&machine)->goToState(s2);
+ QStateMachinePrivate::get(&machine)->goToState(s1);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ // go to state in group
+ QState *s2_1 = new QState(s2);
+ s2->setInitialState(s2_1);
+ QStateMachinePrivate::get(&machine)->goToState(s2_1);
+ QTRY_COMPARE(machine.configuration().size(), 2);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QTRY_VERIFY(machine.configuration().contains(s2_1));
+}
+
+void tst_QStateMachine::goToStateFromSourceWithTransition()
+{
+ // QTBUG-21813
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ s1->addTransition(new QSignalTransition);
+ QState *s2 = new QState(&machine);
+ machine.setInitialState(s1);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QVERIFY(startedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+
+ QStateMachinePrivate::get(&machine)->goToState(s2);
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+}
+
+class CloneSignalTransition : public QSignalTransition
+{
+public:
+ CloneSignalTransition(QObject *sender, const char *signal, QAbstractState *target)
+ : QSignalTransition(sender, signal)
+ {
+ setTargetState(target);
+ }
+
+ void onTransition(QEvent *e) override
+ {
+ QSignalTransition::onTransition(e);
+ QStateMachine::SignalEvent *se = static_cast<QStateMachine::SignalEvent*>(e);
+ eventSignalIndex = se->signalIndex();
+ }
+
+ int eventSignalIndex;
+};
+
+void tst_QStateMachine::clonedSignals()
+{
+ SignalEmitter emitter;
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ CloneSignalTransition *t1 = new CloneSignalTransition(&emitter, SIGNAL(signalWithDefaultArg()), s2);
+ s1->addTransition(t1);
+
+ machine.setInitialState(s1);
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ machine.start();
+ QVERIFY(startedSpy.wait());
+
+ QSignalSpy transitionSpy(t1, &CloneSignalTransition::triggered);
+ emitter.emitSignalWithDefaultArg();
+ QTRY_COMPARE(transitionSpy.size(), 1);
+
+ QCOMPARE(t1->eventSignalIndex, emitter.metaObject()->indexOfSignal("signalWithDefaultArg()"));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QVERIFY(machine.isRunning());
+}
+
+class EventPosterThread : public QThread
+{
+ Q_OBJECT
+public:
+ EventPosterThread(QStateMachine *machine, QObject *parent = 0)
+ : QThread(parent), m_machine(machine), m_count(0)
+ {
+ moveToThread(this);
+ QObject::connect(m_machine, SIGNAL(started()),
+ this, SLOT(postEvent()));
+ }
+protected:
+ virtual void run() override
+ {
+ exec();
+ }
+private Q_SLOTS:
+ void postEvent()
+ {
+ m_machine->postEvent(new QEvent(QEvent::User));
+ if (++m_count < 1000)
+ QTimer::singleShot(0, this, SLOT(postEvent()));
+ else
+ quit();
+ }
+private:
+ QStateMachine *m_machine;
+ int m_count;
+};
+
+void tst_QStateMachine::postEventFromOtherThread()
+{
+ QStateMachine machine;
+ EventPosterThread poster(&machine);
+ StringEventPoster *s1 = new StringEventPoster("foo", &machine);
+ s1->addTransition(new EventTransition(QEvent::User, s1));
+ QFinalState *f = new QFinalState(&machine);
+ s1->addTransition(&poster, SIGNAL(finished()), f);
+ machine.setInitialState(s1);
+
+ poster.start();
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+}
+
+#ifndef QT_NO_WIDGETS
+void tst_QStateMachine::eventFilterForApplication()
+{
+ QStateMachine machine;
+
+ QState *s1 = new QState(&machine);
+ {
+ machine.setInitialState(s1);
+ }
+
+ QState *s2 = new QState(&machine);
+
+ QEventTransition *transition = new QEventTransition(QCoreApplication::instance(),
+ QEvent::ApplicationActivate);
+ transition->setTargetState(s2);
+ s1->addTransition(transition);
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ QCoreApplication::postEvent(QCoreApplication::instance(),
+ new QEvent(QEvent::ApplicationActivate));
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().size(), 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+}
+#endif
+
+void tst_QStateMachine::eventClassesExported()
+{
+ // make sure this links
+ auto wrappedEvent = std::make_unique<QStateMachine::WrappedEvent>(nullptr, nullptr);
+ Q_UNUSED(wrappedEvent);
+ auto signalEvent = std::make_unique<QStateMachine::SignalEvent>(nullptr, 0, QList<QVariant>());
+ Q_UNUSED(signalEvent);
+}
+
+void tst_QStateMachine::stopInTransitionToFinalState()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *s2 = new QFinalState(&machine);
+ QAbstractTransition *t1 = s1->addTransition(s2);
+ machine.setInitialState(s1);
+
+ QObject::connect(t1, SIGNAL(triggered()), &machine, SLOT(stop()));
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QSignalSpy s2EnteredSpy(s2, &QFinalState::entered);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(stoppedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ QVERIFY(s2EnteredSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ // Stopping should take precedence over finished.
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 0);
+ QCOMPARE(s2EnteredSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+ TEST_ACTIVE_CHANGED(s1, 2);
+}
+
+class StopInEventTestTransition : public QAbstractTransition
+{
+public:
+ bool eventTest(QEvent *e) override
+ {
+ if (e->type() == QEvent::User)
+ machine()->stop();
+ return false;
+ }
+ void onTransition(QEvent *) override
+ { }
+};
+
+void tst_QStateMachine::stopInEventTest_data()
+{
+ QTest::addColumn<int>("eventPriority");
+ QTest::newRow("NormalPriority") << int(QStateMachine::NormalPriority);
+ QTest::newRow("HighPriority") << int(QStateMachine::HighPriority);
+}
+
+void tst_QStateMachine::stopInEventTest()
+{
+ QFETCH(int, eventPriority);
+
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->addTransition(new StopInEventTestTransition());
+ machine.setInitialState(s1);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QVERIFY(startedSpy.isValid());
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(stoppedSpy.isValid());
+ QVERIFY(finishedSpy.isValid());
+ machine.postEvent(new QEvent(QEvent::User), QStateMachine::EventPriority(eventPriority));
+
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ QCOMPARE(finishedSpy.size(), 0);
+ TEST_RUNNING_CHANGED(false);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+ TEST_ACTIVE_CHANGED(s1, 1);
+}
+
+class IncrementReceiversTest : public QObject
+{
+ Q_OBJECT
+signals:
+ void mySignal();
+public:
+ virtual void connectNotify(const QMetaMethod &signal) override
+ {
+ signalList.append(signal);
+ }
+
+ QList<QMetaMethod> signalList;
+};
+
+void tst_QStateMachine::testIncrementReceivers()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ QFinalState *s2 = new QFinalState(&machine);
+
+ IncrementReceiversTest testObject;
+ s1->addTransition(&testObject, SIGNAL(mySignal()), s2);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ machine.start();
+ TEST_RUNNING_CHANGED(true);
+
+ QMetaObject::invokeMethod(&testObject, "mySignal", Qt::QueuedConnection);
+
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ QCOMPARE(testObject.signalList.size(), 1);
+ QCOMPARE(testObject.signalList.at(0), QMetaMethod::fromSignal(&IncrementReceiversTest::mySignal));
+ TEST_ACTIVE_CHANGED(s1, 2);
+}
+
+void tst_QStateMachine::initialStateIsEnteredBeforeStartedEmitted()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ QFinalState *s2 = new QFinalState(&machine);
+
+ // When started() is emitted, s1 should be the active state, and this
+ // transition should trigger.
+ s1->addTransition(&machine, SIGNAL(started()), s2);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s1, 2);
+}
+
+void tst_QStateMachine::deletePropertyAssignmentObjectBeforeEntry()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+
+ QObject *o1 = new QObject;
+ s1->assignProperty(o1, "objectName", "foo");
+ delete o1;
+ QObject *o2 = new QObject;
+ s1->assignProperty(o2, "objectName", "bar");
+
+ machine.start();
+ // Shouldn't crash
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ QCOMPARE(o2->objectName(), QString::fromLatin1("bar"));
+ delete o2;
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::deletePropertyAssignmentObjectBeforeRestore()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+
+ QObject *o1 = new QObject;
+ s1->assignProperty(o1, "objectName", "foo");
+ QObject *o2 = new QObject;
+ s1->assignProperty(o2, "objectName", "bar");
+
+ QVERIFY(o1->objectName().isEmpty());
+ QVERIFY(o2->objectName().isEmpty());
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QCOMPARE(o1->objectName(), QString::fromLatin1("foo"));
+ QCOMPARE(o2->objectName(), QString::fromLatin1("bar"));
+
+ delete o1;
+ machine.postEvent(new QEvent(QEvent::User));
+ // Shouldn't crash
+ QTRY_VERIFY(machine.configuration().contains(s2));
+
+ QVERIFY(o2->objectName().isEmpty());
+ delete o2;
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::deleteInitialState()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ machine.setInitialState(s1);
+ delete s1;
+ QTest::ignoreMessage(QtWarningMsg, "QStateMachine::start: No initial state set for machine. Refusing to start.");
+ machine.start();
+ // Shouldn't crash
+ QCoreApplication::processEvents();
+}
+
+void tst_QStateMachine::setPropertyAfterRestore()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ QObject *object = new QObject(&machine);
+ object->setProperty("a", 1);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ s1->assignProperty(object, "a", 2);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ s3->assignProperty(object, "a", 4);
+ s2->addTransition(new EventTransition(QEvent::User, s3));
+
+ QState *s4 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s4);
+ s3->addTransition(new EventTransition(QEvent::User, s4));
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QCOMPARE(object->property("a").toInt(), 2);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOMPARE(object->property("a").toInt(), 1); // restored
+
+ // Set property outside of state machine; this is the value
+ // that should be remembered in the next transition
+ object->setProperty("a", 3);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ QTRY_VERIFY(machine.configuration().contains(s3));
+ QCOMPARE(object->property("a").toInt(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 1);
+ QVERIFY(machine.isRunning());
+ QTRY_VERIFY(machine.configuration().contains(s4));
+ QCOMPARE(object->property("a").toInt(), 3); // restored
+
+ delete object;
+}
+
+void tst_QStateMachine::transitionWithNoTarget_data()
+{
+ QTest::addColumn<int>("restorePolicy");
+ QTest::newRow("DontRestoreProperties") << int(QState::DontRestoreProperties);
+ QTest::newRow("RestoreProperties") << int(QState::RestoreProperties);
+}
+
+void tst_QStateMachine::transitionWithNoTarget()
+{
+ QFETCH(int, restorePolicy);
+
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(static_cast<QState::RestorePolicy>(restorePolicy));
+
+ QObject *object = new QObject;
+ object->setProperty("a", 1);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ s1->assignProperty(object, "a", 2);
+ EventTransition *t1 = new EventTransition(QEvent::User, /*target=*/0);
+ s1->addTransition(t1);
+
+ QSignalSpy s1EnteredSpy(s1, &QState::entered);
+ QSignalSpy s1ExitedSpy(s1, &QState::exited);
+ QSignalSpy t1TriggeredSpy(t1, &EventTransition::triggered);
+
+ machine.start();
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QCOMPARE(s1EnteredSpy.size(), 1);
+ QCOMPARE(s1ExitedSpy.size(), 0);
+ QCOMPARE(t1TriggeredSpy.size(), 0);
+ QCOMPARE(object->property("a").toInt(), 2);
+
+ object->setProperty("a", 3);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QTRY_COMPARE(t1TriggeredSpy.size(), 1);
+ QCOMPARE(s1EnteredSpy.size(), 1);
+ QCOMPARE(s1ExitedSpy.size(), 0);
+ // the assignProperty should not be re-executed, nor should the old value
+ // be restored
+ QCOMPARE(object->property("a").toInt(), 3);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ QTRY_COMPARE(t1TriggeredSpy.size(), 2);
+ QCOMPARE(s1EnteredSpy.size(), 1);
+ QCOMPARE(s1ExitedSpy.size(), 0);
+ QCOMPARE(object->property("a").toInt(), 3);
+
+ delete object;
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::initialStateIsFinal()
+{
+ QStateMachine machine;
+ QFinalState *f = new QFinalState(&machine);
+ machine.setInitialState(f);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ machine.start();
+ QTRY_VERIFY(machine.configuration().contains(f));
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+}
+
+class PropertyObject : public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(int prop READ prop WRITE setProp)
+public:
+ PropertyObject(QObject *parent = 0)
+ : QObject(parent), m_propValue(0), m_propWriteCount(0)
+ {}
+ int prop() const { return m_propValue; }
+ void setProp(int value) { m_propValue = value; ++m_propWriteCount; }
+ int propWriteCount() const { return m_propWriteCount; }
+private:
+ int m_propValue;
+ int m_propWriteCount;
+};
+
+void tst_QStateMachine::restorePropertiesSimple()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ PropertyObject *po = new PropertyObject;
+ po->setProp(2);
+ QCOMPARE(po->propWriteCount(), 1);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(po, "prop", 4);
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ s3->assignProperty(po, "prop", 6);
+ s2->addTransition(new EventTransition(QEvent::User, s3));
+
+ QState *s4 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s4);
+ s4->assignProperty(po, "prop", 8);
+ s3->addTransition(new EventTransition(QEvent::User, s4));
+
+ QState *s5 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s5);
+ s4->addTransition(new EventTransition(QEvent::User, s5));
+
+ QState *s6 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s6);
+ s5->addTransition(new EventTransition(QEvent::User, s6));
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ TEST_ACTIVE_CHANGED(s5, 0);
+ TEST_ACTIVE_CHANGED(s6, 0);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QCOMPARE(po->propWriteCount(), 2);
+ QCOMPARE(po->prop(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ TEST_ACTIVE_CHANGED(s5, 0);
+ TEST_ACTIVE_CHANGED(s6, 0);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOMPARE(po->propWriteCount(), 3);
+ QCOMPARE(po->prop(), 2); // restored
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ TEST_ACTIVE_CHANGED(s4, 0);
+ TEST_ACTIVE_CHANGED(s5, 0);
+ TEST_ACTIVE_CHANGED(s6, 0);
+ QTRY_VERIFY(machine.configuration().contains(s3));
+ QCOMPARE(po->propWriteCount(), 4);
+ QCOMPARE(po->prop(), 6);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 1);
+ TEST_ACTIVE_CHANGED(s5, 0);
+ TEST_ACTIVE_CHANGED(s6, 0);
+ QTRY_VERIFY(machine.configuration().contains(s4));
+ QCOMPARE(po->propWriteCount(), 5);
+ QCOMPARE(po->prop(), 8);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 2);
+ TEST_ACTIVE_CHANGED(s5, 1);
+ TEST_ACTIVE_CHANGED(s6, 0);
+ QTRY_VERIFY(machine.configuration().contains(s5));
+ QCOMPARE(po->propWriteCount(), 6);
+ QCOMPARE(po->prop(), 2); // restored
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 2);
+ TEST_ACTIVE_CHANGED(s5, 2);
+ TEST_ACTIVE_CHANGED(s6, 1);
+ QVERIFY(machine.isRunning());
+ QTRY_VERIFY(machine.configuration().contains(s6));
+ QCOMPARE(po->propWriteCount(), 6);
+
+ delete po;
+}
+
+void tst_QStateMachine::restoreProperties2()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ PropertyObject *po = new PropertyObject;
+ po->setProp(2);
+ QCOMPARE(po->propWriteCount(), 1);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(po, "prop", 4);
+ machine.setInitialState(s1);
+
+ QState *s11 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s11);
+ s1->setInitialState(s11);
+
+ QState *s12 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s12);
+ s11->addTransition(new EventTransition(QEvent::User, s12));
+
+ QState *s13 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s13);
+ s13->assignProperty(po, "prop", 6);
+ s12->addTransition(new EventTransition(QEvent::User, s13));
+
+ QState *s14 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s14);
+ s14->assignProperty(po, "prop", 8);
+ s13->addTransition(new EventTransition(QEvent::User, s14));
+
+ QState *s15 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s15);
+ s14->addTransition(new EventTransition(QEvent::User, s15));
+
+ QState *s16 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s16);
+ s15->addTransition(new EventTransition(QEvent::User, s16));
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(po, "prop", 10);
+ s16->addTransition(new EventTransition(QEvent::User, s2));
+
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ s2->addTransition(new EventTransition(QEvent::User, s3));
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 1);
+ TEST_ACTIVE_CHANGED(s12, 0);
+ TEST_ACTIVE_CHANGED(s13, 0);
+ TEST_ACTIVE_CHANGED(s14, 0);
+ TEST_ACTIVE_CHANGED(s15, 0);
+ TEST_ACTIVE_CHANGED(s16, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QTRY_VERIFY(machine.configuration().contains(s11));
+ QCOMPARE(po->propWriteCount(), 2);
+ QCOMPARE(po->prop(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 1);
+ TEST_ACTIVE_CHANGED(s13, 0);
+ TEST_ACTIVE_CHANGED(s14, 0);
+ TEST_ACTIVE_CHANGED(s15, 0);
+ TEST_ACTIVE_CHANGED(s16, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QTRY_VERIFY(machine.configuration().contains(s12));
+ QCOMPARE(po->propWriteCount(), 2);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s13, 1);
+ TEST_ACTIVE_CHANGED(s14, 0);
+ TEST_ACTIVE_CHANGED(s15, 0);
+ TEST_ACTIVE_CHANGED(s16, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QTRY_VERIFY(machine.configuration().contains(s13));
+ QCOMPARE(po->propWriteCount(), 3);
+ QCOMPARE(po->prop(), 6);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s13, 2);
+ TEST_ACTIVE_CHANGED(s14, 1);
+ TEST_ACTIVE_CHANGED(s15, 0);
+ TEST_ACTIVE_CHANGED(s16, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QTRY_VERIFY(machine.configuration().contains(s14));
+ QCOMPARE(po->propWriteCount(), 4);
+ QCOMPARE(po->prop(), 8);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s13, 2);
+ TEST_ACTIVE_CHANGED(s14, 2);
+ TEST_ACTIVE_CHANGED(s15, 1);
+ TEST_ACTIVE_CHANGED(s16, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QTRY_VERIFY(machine.configuration().contains(s15));
+ QCOMPARE(po->propWriteCount(), 5);
+ QCOMPARE(po->prop(), 4); // restored s1
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s13, 2);
+ TEST_ACTIVE_CHANGED(s14, 2);
+ TEST_ACTIVE_CHANGED(s15, 2);
+ TEST_ACTIVE_CHANGED(s16, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QTRY_VERIFY(machine.configuration().contains(s16));
+ QCOMPARE(po->propWriteCount(), 5);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s13, 2);
+ TEST_ACTIVE_CHANGED(s14, 2);
+ TEST_ACTIVE_CHANGED(s15, 2);
+ TEST_ACTIVE_CHANGED(s16, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s3, 0);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOMPARE(po->propWriteCount(), 6);
+ QCOMPARE(po->prop(), 10);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s13, 2);
+ TEST_ACTIVE_CHANGED(s14, 2);
+ TEST_ACTIVE_CHANGED(s15, 2);
+ TEST_ACTIVE_CHANGED(s16, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QVERIFY(machine.isRunning());
+ QTRY_VERIFY(machine.configuration().contains(s3));
+ QCOMPARE(po->propWriteCount(), 7);
+ QCOMPARE(po->prop(), 2); // restored original
+
+ delete po;
+
+}
+
+void tst_QStateMachine::restoreProperties3()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ PropertyObject *po = new PropertyObject;
+ po->setProp(2);
+ QCOMPARE(po->propWriteCount(), 1);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(po, "prop", 4);
+ machine.setInitialState(s1);
+
+ QState *s11 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s11);
+ s11->assignProperty(po, "prop", 6);
+ s1->setInitialState(s11);
+
+ QState *s12 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s12);
+ s11->addTransition(new EventTransition(QEvent::User, s12));
+
+ QState *s13 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s13);
+ s13->assignProperty(po, "prop", 8);
+ s12->addTransition(new EventTransition(QEvent::User, s13));
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s13->addTransition(new EventTransition(QEvent::User, s2));
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 1);
+ TEST_ACTIVE_CHANGED(s12, 0);
+ TEST_ACTIVE_CHANGED(s13, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+
+ QTRY_VERIFY(machine.configuration().contains(s11));
+ QCOMPARE(po->propWriteCount(), 3);
+ QCOMPARE(po->prop(), 6); // s11
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 1);
+ TEST_ACTIVE_CHANGED(s13, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s12));
+ QCOMPARE(po->propWriteCount(), 4);
+ QCOMPARE(po->prop(), 4); // restored s1
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s13, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s13));
+ QCOMPARE(po->propWriteCount(), 5);
+ QCOMPARE(po->prop(), 8);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s13, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QVERIFY(machine.isRunning());
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOMPARE(po->propWriteCount(), 6);
+ QCOMPARE(po->prop(), 2); // restored original
+
+ delete po;
+}
+
+// QTBUG-20362
+void tst_QStateMachine::restoreProperties4()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ PropertyObject *po1 = new PropertyObject;
+ po1->setProp(2);
+ QCOMPARE(po1->propWriteCount(), 1);
+ PropertyObject *po2 = new PropertyObject;
+ po2->setProp(4);
+ QCOMPARE(po2->propWriteCount(), 1);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->setChildMode(QState::ParallelStates);
+ machine.setInitialState(s1);
+
+ QState *s11 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s11);
+ QState *s111 = new QState(s11);
+ DEFINE_ACTIVE_SPY(s111);
+ s111->assignProperty(po1, "prop", 6);
+ s11->setInitialState(s111);
+
+ QState *s112 = new QState(s11);
+ DEFINE_ACTIVE_SPY(s112);
+ s112->assignProperty(po1, "prop", 8);
+ s111->addTransition(new EventTransition(QEvent::User, s112));
+
+ QState *s12 = new QState(s1);
+ DEFINE_ACTIVE_SPY(s12);
+ QState *s121 = new QState(s12);
+ DEFINE_ACTIVE_SPY(s121);
+ s121->assignProperty(po2, "prop", 10);
+ s12->setInitialState(s121);
+
+ QState *s122 = new QState(s12);
+ DEFINE_ACTIVE_SPY(s122);
+ s122->assignProperty(po2, "prop", 12);
+ s121->addTransition(new EventTransition(static_cast<QEvent::Type>(QEvent::User+1), s122));
+
+ QState *s2 = new QState(&machine);
+ s112->addTransition(new EventTransition(QEvent::User, s2));
+ DEFINE_ACTIVE_SPY(s2);
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 1);
+ TEST_ACTIVE_CHANGED(s111, 1);
+ TEST_ACTIVE_CHANGED(s112, 0);
+ TEST_ACTIVE_CHANGED(s12, 1);
+ TEST_ACTIVE_CHANGED(s121, 1);
+ TEST_ACTIVE_CHANGED(s122, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QVERIFY(machine.configuration().contains(s11));
+ QVERIFY(machine.configuration().contains(s111));
+ QVERIFY(machine.configuration().contains(s12));
+ QVERIFY(machine.configuration().contains(s121));
+ QCOMPARE(po1->propWriteCount(), 2);
+ QCOMPARE(po1->prop(), 6);
+ QCOMPARE(po2->propWriteCount(), 2);
+ QCOMPARE(po2->prop(), 10);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 1);
+ TEST_ACTIVE_CHANGED(s111, 2);
+ TEST_ACTIVE_CHANGED(s112, 1);
+ TEST_ACTIVE_CHANGED(s12, 1);
+ TEST_ACTIVE_CHANGED(s121, 1);
+ TEST_ACTIVE_CHANGED(s122, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s112));
+ QCOMPARE(po1->propWriteCount(), 3);
+ QCOMPARE(po1->prop(), 8);
+ QCOMPARE(po2->propWriteCount(), 2);
+
+ machine.postEvent(new QEvent(static_cast<QEvent::Type>(QEvent::User+1)));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 1);
+ TEST_ACTIVE_CHANGED(s111, 2);
+ TEST_ACTIVE_CHANGED(s112, 1);
+ TEST_ACTIVE_CHANGED(s12, 1);
+ TEST_ACTIVE_CHANGED(s121, 2);
+ TEST_ACTIVE_CHANGED(s122, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s122));
+ QCOMPARE(po1->propWriteCount(), 3);
+ QCOMPARE(po2->propWriteCount(), 3);
+ QCOMPARE(po2->prop(), 12);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s11, 2);
+ TEST_ACTIVE_CHANGED(s111, 2);
+ TEST_ACTIVE_CHANGED(s112, 2);
+ TEST_ACTIVE_CHANGED(s12, 2);
+ TEST_ACTIVE_CHANGED(s121, 2);
+ TEST_ACTIVE_CHANGED(s122, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOMPARE(po1->propWriteCount(), 4);
+ QCOMPARE(po1->prop(), 2); // restored original
+ QCOMPARE(po2->propWriteCount(), 4);
+ QCOMPARE(po2->prop(), 4); // restored original
+
+ delete po1;
+ delete po2;
+}
+
+void tst_QStateMachine::restorePropertiesSelfTransition()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ PropertyObject *po = new PropertyObject;
+ po->setProp(2);
+ QCOMPARE(po->propWriteCount(), 1);
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(po, "prop", 4);
+ s1->addTransition(new EventTransition(QEvent::User, s1));
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s1->addTransition(new EventTransition(static_cast<QEvent::Type>(QEvent::User+1), s2));
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QCOMPARE(po->propWriteCount(), 2);
+ QCOMPARE(po->prop(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 3);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_COMPARE(po->propWriteCount(), 3);
+ QCOMPARE(po->prop(), 4);
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 5);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_COMPARE(po->propWriteCount(), 4);
+ QCOMPARE(po->prop(), 4);
+
+ machine.postEvent(new QEvent(static_cast<QEvent::Type>(QEvent::User+1)));
+ TEST_ACTIVE_CHANGED(s1, 6);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOMPARE(po->propWriteCount(), 5);
+ QCOMPARE(po->prop(), 2); // restored
+
+ delete po;
+}
+
+void tst_QStateMachine::changeStateWhileAnimatingProperty()
+{
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(QState::RestoreProperties);
+
+ QObject *o1 = new QObject;
+ o1->setProperty("x", 10.);
+ QObject *o2 = new QObject;
+ o2->setProperty("y", 20.);
+
+ QState *group = new QState(&machine);
+ DEFINE_ACTIVE_SPY(group);
+ machine.setInitialState(group);
+
+ QState *s0 = new QState(group);
+ DEFINE_ACTIVE_SPY(s0);
+ group->setInitialState(s0);
+
+ QState *s1 = new QState(group);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(o1, "x", 15.);
+ QPropertyAnimation *a1 = new QPropertyAnimation(o1, "x", s1);
+ a1->setDuration(800);
+ machine.addDefaultAnimation(a1);
+ group->addTransition(new EventTransition(QEvent::User, s1));
+
+ QState *s2 = new QState(group);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(o2, "y", 25.);
+ QPropertyAnimation *a2 = new QPropertyAnimation(o2, "y", s2);
+ a2->setDuration(800);
+ machine.addDefaultAnimation(a2);
+ group->addTransition(new EventTransition(static_cast<QEvent::Type>(QEvent::User+1), s2));
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(group, 1);
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s1, 0);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s0));
+
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(group, 3);
+ TEST_ACTIVE_CHANGED(s0, 2);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QCOREAPPLICATION_EXEC(400);
+ machine.postEvent(new QEvent(static_cast<QEvent::Type>(QEvent::User+1)));
+ TEST_ACTIVE_CHANGED(group, 5);
+ TEST_ACTIVE_CHANGED(s0, 2);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOREAPPLICATION_EXEC(300);
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(group, 7);
+ TEST_ACTIVE_CHANGED(s0, 2);
+ TEST_ACTIVE_CHANGED(s1, 3);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QCOREAPPLICATION_EXEC(200);
+ machine.postEvent(new QEvent(static_cast<QEvent::Type>(QEvent::User+1)));
+ TEST_ACTIVE_CHANGED(group, 9);
+ TEST_ACTIVE_CHANGED(s0, 2);
+ TEST_ACTIVE_CHANGED(s1, 4);
+ TEST_ACTIVE_CHANGED(s2, 3);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOREAPPLICATION_EXEC(100);
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(group, 11);
+ TEST_ACTIVE_CHANGED(s0, 2);
+ TEST_ACTIVE_CHANGED(s1, 5);
+ TEST_ACTIVE_CHANGED(s2, 4);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QTRY_COMPARE(o1->property("x").toDouble(), 15.);
+ QTRY_COMPARE(o2->property("y").toDouble(), 20.);
+
+ delete o1;
+ delete o2;
+}
+
+class AssignPropertyTestState : public QState
+{
+ Q_OBJECT
+public:
+ AssignPropertyTestState(QState *parent = 0)
+ : QState(parent), onEntryPassed(false), enteredPassed(false)
+ { QObject::connect(this, SIGNAL(entered()), this, SLOT(onEntered())); }
+
+ virtual void onEntry(QEvent *) override
+ { onEntryPassed = property("wasAssigned").toBool(); }
+
+ bool onEntryPassed;
+ bool enteredPassed;
+
+private Q_SLOTS:
+ void onEntered()
+ { enteredPassed = property("wasAssigned").toBool(); }
+};
+
+void tst_QStateMachine::propertiesAreAssignedBeforeEntryCallbacks_data()
+{
+ QTest::addColumn<int>("restorePolicy");
+ QTest::newRow("DontRestoreProperties") << int(QState::DontRestoreProperties);
+ QTest::newRow("RestoreProperties") << int(QState::RestoreProperties);
+}
+
+void tst_QStateMachine::propertiesAreAssignedBeforeEntryCallbacks()
+{
+ QFETCH(int, restorePolicy);
+
+ QStateMachine machine;
+ machine.setGlobalRestorePolicy(static_cast<QState::RestorePolicy>(restorePolicy));
+
+ AssignPropertyTestState *s1 = new AssignPropertyTestState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ s1->assignProperty(s1, "wasAssigned", true);
+ machine.setInitialState(s1);
+
+ AssignPropertyTestState *s2 = new AssignPropertyTestState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s2->assignProperty(s2, "wasAssigned", true);
+ s1->addTransition(new EventTransition(QEvent::User, s2));
+
+ QVERIFY(!s1->property("wasAssigned").toBool());
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ QVERIFY(s1->onEntryPassed);
+ QVERIFY(s1->enteredPassed);
+
+ QVERIFY(!s2->property("wasAssigned").toBool());
+ machine.postEvent(new QEvent(QEvent::User));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+
+ QVERIFY(s2->onEntryPassed);
+ QVERIFY(s2->enteredPassed);
+}
+
+// QTBUG-25958
+void tst_QStateMachine::multiTargetTransitionInsideParallelStateGroup()
+{
+ // TODO QTBUG-25958 was reopened, see https://codereview.qt-project.org/89775
+ return;
+
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState(QState::ParallelStates, &machine);
+ DEFINE_ACTIVE_SPY(s2);
+
+ QState *s21 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s21);
+ QState *s211 = new QState(s21);
+ DEFINE_ACTIVE_SPY(s211);
+ QState *s212 = new QState(s21);
+ DEFINE_ACTIVE_SPY(s212);
+ s21->setInitialState(s212);
+
+ QState *s22 = new QState(s2);
+ DEFINE_ACTIVE_SPY(s22);
+ QState *s221 = new QState(s22);
+ DEFINE_ACTIVE_SPY(s221);
+ QState *s222 = new QState(s22);
+ DEFINE_ACTIVE_SPY(s222);
+ s22->setInitialState(s222);
+
+ QAbstractTransition *t1 = new EventTransition(QEvent::User, QList<QAbstractState *>() << s211 << s221);
+ s1->addTransition(t1);
+
+ machine.start();
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ TEST_ACTIVE_CHANGED(s21, 0);
+ TEST_ACTIVE_CHANGED(s211, 0);
+ TEST_ACTIVE_CHANGED(s212, 0);
+ TEST_ACTIVE_CHANGED(s22, 0);
+ TEST_ACTIVE_CHANGED(s221, 0);
+ TEST_ACTIVE_CHANGED(s222, 0);
+ machine.postEvent(new QEvent(QEvent::User));
+ QTRY_VERIFY(machine.configuration().contains(s2));
+ QCOMPARE(machine.configuration().size(), 5);
+ QVERIFY(machine.configuration().contains(s21));
+ QVERIFY(machine.configuration().contains(s211));
+ QVERIFY(machine.configuration().contains(s22));
+ QVERIFY(machine.configuration().contains(s221));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ TEST_ACTIVE_CHANGED(s21, 1);
+ TEST_ACTIVE_CHANGED(s211, 1);
+ TEST_ACTIVE_CHANGED(s212, 0);
+ TEST_ACTIVE_CHANGED(s22, 1);
+ TEST_ACTIVE_CHANGED(s221, 1);
+ TEST_ACTIVE_CHANGED(s222, 0);
+}
+
+void tst_QStateMachine::signalTransitionNormalizeSignature()
+{
+ QStateMachine machine;
+ QState *s0 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s0);
+ machine.setInitialState(s0);
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ SignalEmitter emitter;
+ TestSignalTransition *t0 = new TestSignalTransition(&emitter, SIGNAL(signalWithNoArg()), s1);
+ s0->addTransition(t0);
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s0, 1);
+ TEST_ACTIVE_CHANGED(s1, 0);
+ QTRY_VERIFY(machine.configuration().contains(s0));
+ emitter.emitSignalWithNoArg();
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ QCOMPARE(t0->eventTestSenderReceived(), (QObject*)&emitter);
+ QCOMPARE(t0->eventTestSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithNoArg()"));
+ QCOMPARE(t0->eventTestArgumentsReceived().size(), 0);
+ QCOMPARE(t0->transitionSenderReceived(), (QObject*)&emitter);
+ QCOMPARE(t0->transitionSignalIndexReceived(), emitter.metaObject()->indexOfSignal("signalWithNoArg()"));
+ QCOMPARE(t0->transitionArgumentsReceived().size(), 0);
+ TEST_ACTIVE_CHANGED(s0, 2);
+ TEST_ACTIVE_CHANGED(s1, 1);
+}
+
+#ifdef Q_COMPILER_DELEGATING_CONSTRUCTORS
+void tst_QStateMachine::createPointerToMemberSignalTransition()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ SignalEmitter emitter;
+ QSignalTransition *t1 = new QSignalTransition(&emitter, &SignalEmitter::signalWithNoArg, s1);
+ QCOMPARE(t1->sourceState(), s1);
+ t1->setTargetState(s2);
+ s1->addTransition(t1);
+ emitter.emitSignalWithNoArg();
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+}
+#endif
+
+void tst_QStateMachine::createSignalTransitionWhenRunning()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ // Create by addTransition()
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ SignalEmitter emitter;
+ QAbstractTransition *t1 = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s2);
+ QCOMPARE(t1->sourceState(), s1);
+ emitter.emitSignalWithNoArg();
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 1);
+ QTRY_VERIFY(machine.configuration().contains(s2));
+
+ // Create by constructor that takes sender, signal, source (parent) state
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QSignalTransition *t2 = new QSignalTransition(&emitter, SIGNAL(signalWithNoArg()), s2);
+ QCOMPARE(t2->sourceState(), s2);
+ t2->setTargetState(s3);
+ emitter.emitSignalWithNoArg();
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 1);
+ QTRY_VERIFY(machine.configuration().contains(s3));
+
+ // Create by constructor that takes source (parent) state
+ QState *s4 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s4);
+ QSignalTransition *t3 = new QSignalTransition(s3);
+ QCOMPARE(t3->sourceState(), s3);
+ t3->setSenderObject(&emitter);
+ t3->setSignal(SIGNAL(signalWithNoArg()));
+ t3->setTargetState(s4);
+ emitter.emitSignalWithNoArg();
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 1);
+ QTRY_VERIFY(machine.configuration().contains(s4));
+
+ // Create by constructor without parent, then set the parent
+ QState *s5 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s5);
+ QSignalTransition *t4 = new QSignalTransition();
+ t4->setSenderObject(&emitter);
+ t4->setParent(s4);
+ QCOMPARE(t4->sourceState(), s4);
+ t4->setSignal(SIGNAL(signalWithNoArg()));
+ t4->setTargetState(s5);
+ emitter.emitSignalWithNoArg();
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 2);
+ TEST_ACTIVE_CHANGED(s5, 1);
+ QTRY_VERIFY(machine.configuration().contains(s5));
+}
+
+void tst_QStateMachine::createEventTransitionWhenRunning()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ // Create by constructor that takes event source, type, source (parent) state
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ QObject object;
+ QEventTransition *t1 = new QEventTransition(&object, QEvent::Timer, s1);
+ QCOMPARE(t1->sourceState(), s1);
+ t1->setTargetState(s2);
+
+ object.startTimer(10); // Will cause QEvent::Timer to fire every 10ms
+ QTRY_VERIFY(machine.configuration().contains(s2));
+
+ // Create by constructor that takes source (parent) state
+ QState *s3 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s3);
+ QEventTransition *t2 = new QEventTransition(s2);
+ QCOMPARE(t2->sourceState(), s2);
+ t2->setEventSource(&object);
+ t2->setEventType(QEvent::Timer);
+ t2->setTargetState(s3);
+ QTRY_VERIFY(machine.configuration().contains(s3));
+
+ // Create by constructor without parent, then set the parent
+ QState *s4 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s4);
+ QEventTransition *t3 = new QEventTransition();
+ t3->setEventSource(&object);
+ t3->setParent(s3);
+ QCOMPARE(t3->sourceState(), s3);
+ t3->setEventType(QEvent::Timer);
+ t3->setTargetState(s4);
+ QTRY_VERIFY(machine.configuration().contains(s4));
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ TEST_ACTIVE_CHANGED(s3, 2);
+ TEST_ACTIVE_CHANGED(s4, 1);
+}
+
+class SignalEmitterThread : public QThread
+{
+ Q_OBJECT
+public:
+ SignalEmitterThread(QObject *parent = 0)
+ : QThread(parent)
+ {
+ moveToThread(this);
+ }
+
+Q_SIGNALS:
+ void signal1();
+ void signal2();
+
+public Q_SLOTS:
+ void emitSignals()
+ {
+ emit signal1();
+ emit signal2();
+ }
+};
+
+// QTBUG-19789
+void tst_QStateMachine::signalTransitionSenderInDifferentThread()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+
+ SignalEmitterThread thread;
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ s1->addTransition(&thread, SIGNAL(signal1()), s2);
+
+ QFinalState *s3 = new QFinalState(&machine);
+ s2->addTransition(&thread, SIGNAL(signal2()), s3);
+
+ thread.start();
+ QTRY_VERIFY(thread.isRunning());
+
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s2, 0);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ QMetaObject::invokeMethod(&thread, "emitSignals");
+ // thread emits both signal1() and signal2(), so we should end in s3
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ QTRY_VERIFY(!machine.isRunning());
+ QTRY_VERIFY(machine.configuration().contains(s3));
+
+ // Run the machine again; transitions should still be registered
+ machine.start();
+ TEST_ACTIVE_CHANGED(s1, 3);
+ TEST_ACTIVE_CHANGED(s2, 2);
+ QTRY_VERIFY(machine.configuration().contains(s1));
+ QMetaObject::invokeMethod(&thread, "emitSignals");
+ QTRY_VERIFY(machine.configuration().contains(s3));
+
+ thread.quit();
+ QTRY_VERIFY(thread.wait());
+ TEST_ACTIVE_CHANGED(s1, 4);
+ TEST_ACTIVE_CHANGED(s2, 4);
+ QVERIFY(!machine.isRunning());
+}
+
+void tst_QStateMachine::signalTransitionSenderInDifferentThread2()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+
+ QState *s2 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s2);
+ SignalEmitter emitter;
+ // At the time of the transition creation, the machine and the emitter
+ // are both in the same thread.
+ s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s2);
+
+ QFinalState *s3 = new QFinalState(&machine);
+ s2->addTransition(&emitter, SIGNAL(signalWithDefaultArg()), s3);
+
+ QThread thread;
+ // Move the machine and its states to a secondary thread, but let the
+ // SignalEmitter stay in the main thread.
+ machine.moveToThread(&thread);
+
+ thread.start();
+ QTRY_VERIFY(thread.isRunning());
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+
+ emitter.emitSignalWithNoArg();
+ // The second emission should not get "lost".
+ emitter.emitSignalWithDefaultArg();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+
+ thread.quit();
+ QTRY_VERIFY(thread.wait());
+ TEST_ACTIVE_CHANGED(s1, 2);
+ TEST_ACTIVE_CHANGED(s2, 2);
+}
+
+class SignalTransitionMutatorThread : public QThread
+{
+public:
+ SignalTransitionMutatorThread(QSignalTransition *transition)
+ : m_transition(transition)
+ {}
+ void run() override
+ {
+ // Cause repeated registration and unregistration
+ for (int i = 0; i < 50000; ++i) {
+ m_transition->setSenderObject(this);
+ m_transition->setSenderObject(0);
+ }
+ }
+private:
+ QSignalTransition *m_transition;
+};
+
+// Should not crash:
+void tst_QStateMachine::signalTransitionRegistrationThreadSafety()
+{
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ machine.setInitialState(s1);
+ machine.start();
+ QTRY_VERIFY(machine.configuration().contains(s1));
+
+ QSignalTransition *t1 = new QSignalTransition();
+ t1->setSignal(SIGNAL(objectNameChanged(QString)));
+ s1->addTransition(t1);
+
+ QSignalTransition *t2 = new QSignalTransition();
+ t2->setSignal(SIGNAL(objectNameChanged(QString)));
+ s1->addTransition(t2);
+
+ SignalTransitionMutatorThread thread(t1);
+ thread.start();
+ QTRY_VERIFY(thread.isRunning());
+
+ // Cause repeated registration and unregistration
+ for (int i = 0; i < 50000; ++i) {
+ t2->setSenderObject(this);
+ t2->setSenderObject(0);
+ }
+
+ thread.quit();
+ QTRY_VERIFY(thread.wait());
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::childModeConstructor()
+{
+ // Any child mode other than ExclusiveStates results in an invalid child mode
+ // warning. So the three warnings here are expected. See more about this in
+ // the QStateMachine constructor.
+ {
+ QStateMachine machine(QState::ExclusiveStates);
+ QCOMPARE(machine.childMode(), QState::ExclusiveStates);
+ QVERIFY(!machine.parent());
+ QVERIFY(!machine.parentState());
+ }
+ {
+ QStateMachine machine(QState::ParallelStates);
+ QCOMPARE(machine.childMode(), QState::ParallelStates);
+ QVERIFY(!machine.parent());
+ QVERIFY(!machine.parentState());
+ }
+ {
+ QStateMachine machine(QState::ExclusiveStates, this);
+ QCOMPARE(machine.childMode(), QState::ExclusiveStates);
+ QCOMPARE(machine.parent(), static_cast<QObject *>(this));
+ QVERIFY(!machine.parentState());
+ }
+ {
+ QStateMachine machine(QState::ParallelStates, this);
+ QCOMPARE(machine.childMode(), QState::ParallelStates);
+ QCOMPARE(machine.parent(), static_cast<QObject *>(this));
+ QVERIFY(!machine.parentState());
+ }
+ QState state;
+ {
+ QStateMachine machine(QState::ExclusiveStates, &state);
+ QCOMPARE(machine.childMode(), QState::ExclusiveStates);
+ QCOMPARE(machine.parent(), static_cast<QObject *>(&state));
+ QCOMPARE(machine.parentState(), &state);
+ }
+ {
+ QStateMachine machine(QState::ParallelStates, &state);
+ QCOMPARE(machine.childMode(), QState::ParallelStates);
+ QCOMPARE(machine.parent(), static_cast<QObject *>(&state));
+ QCOMPARE(machine.parentState(), &state);
+ }
+}
+
+void tst_QStateMachine::qtbug_44963()
+{
+ SignalEmitter emitter;
+
+ QStateMachine machine;
+ QState a(QState::ParallelStates, &machine);
+ QHistoryState ha(QHistoryState::DeepHistory, &a);
+ QState b(QState::ParallelStates, &a);
+ QState c(QState::ParallelStates, &b);
+ QState d(QState::ParallelStates, &c);
+ QState e(QState::ParallelStates, &d);
+ QState i(&e);
+ QState i1(&i);
+ QState i2(&i);
+ QState j(&e);
+ QState h(&d);
+ QState g(&c);
+ QState k(&a);
+ QState l(&machine);
+
+ machine.setInitialState(&a);
+ ha.setDefaultState(&b);
+ i.setInitialState(&i1);
+ i1.addTransition(&emitter, SIGNAL(signalWithIntArg(int)), &i2)->setObjectName("i1->i2");
+ i2.addTransition(&emitter, SIGNAL(signalWithDefaultArg(int)), &l)->setObjectName("i2->l");
+ l.addTransition(&emitter, SIGNAL(signalWithNoArg()), &ha)->setObjectName("l->ha");
+
+ a.setObjectName("a");
+ ha.setObjectName("ha");
+ b.setObjectName("b");
+ c.setObjectName("c");
+ d.setObjectName("d");
+ e.setObjectName("e");
+ i.setObjectName("i");
+ i1.setObjectName("i1");
+ i2.setObjectName("i2");
+ j.setObjectName("j");
+ h.setObjectName("h");
+ g.setObjectName("g");
+ k.setObjectName("k");
+ l.setObjectName("l");
+
+ machine.start();
+
+ QTRY_COMPARE(machine.configuration().contains(&i1), true);
+ QTRY_COMPARE(machine.configuration().contains(&i2), false);
+ QTRY_COMPARE(machine.configuration().contains(&j), true);
+ QTRY_COMPARE(machine.configuration().contains(&h), true);
+ QTRY_COMPARE(machine.configuration().contains(&g), true);
+ QTRY_COMPARE(machine.configuration().contains(&k), true);
+ QTRY_COMPARE(machine.configuration().contains(&l), false);
+
+ emitter.emitSignalWithIntArg(0);
+
+ QTRY_COMPARE(machine.configuration().contains(&i1), false);
+ QTRY_COMPARE(machine.configuration().contains(&i2), true);
+ QTRY_COMPARE(machine.configuration().contains(&j), true);
+ QTRY_COMPARE(machine.configuration().contains(&h), true);
+ QTRY_COMPARE(machine.configuration().contains(&g), true);
+ QTRY_COMPARE(machine.configuration().contains(&k), true);
+ QTRY_COMPARE(machine.configuration().contains(&l), false);
+
+ emitter.emitSignalWithDefaultArg();
+
+ QTRY_COMPARE(machine.configuration().contains(&i1), false);
+ QTRY_COMPARE(machine.configuration().contains(&i2), false);
+ QTRY_COMPARE(machine.configuration().contains(&j), false);
+ QTRY_COMPARE(machine.configuration().contains(&h), false);
+ QTRY_COMPARE(machine.configuration().contains(&g), false);
+ QTRY_COMPARE(machine.configuration().contains(&k), false);
+ QTRY_COMPARE(machine.configuration().contains(&l), true);
+
+ emitter.emitSignalWithNoArg();
+
+ QTRY_COMPARE(machine.configuration().contains(&i1), false);
+ QTRY_COMPARE(machine.configuration().contains(&i2), true);
+ QTRY_COMPARE(machine.configuration().contains(&j), true);
+ QTRY_COMPARE(machine.configuration().contains(&h), true);
+ QTRY_COMPARE(machine.configuration().contains(&g), true);
+ QTRY_COMPARE(machine.configuration().contains(&k), true);
+ QTRY_COMPARE(machine.configuration().contains(&l), false);
+
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::qtbug_44783()
+{
+ SignalEmitter emitter;
+
+ QStateMachine machine;
+ QState s(&machine);
+ QState p(QState::ParallelStates, &s);
+ QState p1(&p);
+ QState p1_1(&p1);
+ QState p1_2(&p1);
+ QState p2(&p);
+ QState s1(&machine);
+
+ machine.setInitialState(&s);
+ s.setInitialState(&p);
+ p1.setInitialState(&p1_1);
+ p1_1.addTransition(&emitter, SIGNAL(signalWithNoArg()), &p1_2)->setObjectName("p1_1->p1_2");
+ p2.addTransition(&emitter, SIGNAL(signalWithNoArg()), &s1)->setObjectName("p2->s1");
+
+ s.setObjectName("s");
+ p.setObjectName("p");
+ p1.setObjectName("p1");
+ p1_1.setObjectName("p1_1");
+ p1_2.setObjectName("p1_2");
+ p2.setObjectName("p2");
+ s1.setObjectName("s1");
+
+ machine.start();
+
+ QTRY_COMPARE(machine.configuration().contains(&s), true);
+ QTRY_COMPARE(machine.configuration().contains(&p), true);
+ QTRY_COMPARE(machine.configuration().contains(&p1), true);
+ QTRY_COMPARE(machine.configuration().contains(&p1_1), true);
+ QTRY_COMPARE(machine.configuration().contains(&p1_2), false);
+ QTRY_COMPARE(machine.configuration().contains(&p2), true);
+ QTRY_COMPARE(machine.configuration().contains(&s1), false);
+
+ emitter.emitSignalWithNoArg();
+
+ // Only one of the following two can be true, because the two possible transitions conflict.
+ if (machine.configuration().contains(&s1)) {
+ // the transition p2 -> s1 was taken, not p1_1 -> p1_2, so:
+ // the parallel state exited, so none of the states inside it are active
+ QTRY_COMPARE(machine.configuration().contains(&s), false);
+ QTRY_COMPARE(machine.configuration().contains(&p), false);
+ QTRY_COMPARE(machine.configuration().contains(&p1), false);
+ QTRY_COMPARE(machine.configuration().contains(&p1_1), false);
+ QTRY_COMPARE(machine.configuration().contains(&p1_2), false);
+ QTRY_COMPARE(machine.configuration().contains(&p2), false);
+ } else {
+ // the transition p1_1 -> p1_2 was taken, not p2 -> s1, so:
+ // the parallel state was not exited and the state is the same as the start state with one
+ // difference: p1_1 inactive and p1_2 active:
+ QTRY_COMPARE(machine.configuration().contains(&s), true);
+ QTRY_COMPARE(machine.configuration().contains(&p), true);
+ QTRY_COMPARE(machine.configuration().contains(&p1), true);
+ QTRY_COMPARE(machine.configuration().contains(&p1_1), false);
+ QTRY_COMPARE(machine.configuration().contains(&p1_2), true);
+ QTRY_COMPARE(machine.configuration().contains(&p2), true);
+ }
+
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::internalTransition()
+{
+ SignalEmitter emitter;
+
+ QStateMachine machine;
+ QState *s = new QState(&machine);
+ QState *s1 = new QState(s);
+ QState *s11 = new QState(s1);
+
+ DEFINE_ACTIVE_SPY(s);
+ DEFINE_ACTIVE_SPY(s1);
+ DEFINE_ACTIVE_SPY(s11);
+
+ machine.setInitialState(s);
+ s->setInitialState(s1);
+ s1->setInitialState(s11);
+ QSignalTransition *t = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s11);
+ t->setObjectName("s1->s11");
+ t->setTransitionType(QAbstractTransition::InternalTransition);
+
+ s->setObjectName("s");
+ s1->setObjectName("s1");
+ s11->setObjectName("s11");
+
+ machine.start();
+
+ QTRY_COMPARE(machine.configuration().contains(s), true);
+ QTRY_COMPARE(machine.configuration().contains(s1), true);
+ QTRY_COMPARE(machine.configuration().contains(s11), true);
+ TEST_ACTIVE_CHANGED(s, 1);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ TEST_ACTIVE_CHANGED(s11, 1);
+
+ emitter.emitSignalWithNoArg();
+
+ QTRY_COMPARE(machine.configuration().contains(s), true);
+ QTRY_COMPARE(machine.configuration().contains(s1), true);
+ QTRY_COMPARE(machine.configuration().contains(s11), true);
+ TEST_ACTIVE_CHANGED(s11, 3);
+ TEST_ACTIVE_CHANGED(s1, 1); // external transitions will return 3, internal transitions should return 1.
+ TEST_ACTIVE_CHANGED(s, 1);
+}
+
+void tst_QStateMachine::conflictingTransition()
+{
+ SignalEmitter emitter;
+
+ QStateMachine machine;
+ QState b(QState::ParallelStates, &machine);
+ QState c(&b);
+ QState d(QState::ParallelStates, &b);
+ QState e(&d);
+ QState e1(&e);
+ QState e2(&e);
+ QState f(&d);
+ QState f1(&f);
+ QState f2(&f);
+ QState a1(&machine);
+
+ machine.setInitialState(&b);
+ e.setInitialState(&e1);
+ f.setInitialState(&f1);
+ c.addTransition(&emitter, SIGNAL(signalWithNoArg()), &a1)->setObjectName("c->a1");
+ e1.addTransition(&emitter, SIGNAL(signalWithNoArg()), &e2)->setObjectName("e1->e2");
+ f1.addTransition(&emitter, SIGNAL(signalWithNoArg()), &f2)->setObjectName("f1->f2");
+
+ b.setObjectName("b");
+ c.setObjectName("c");
+ d.setObjectName("d");
+ e.setObjectName("e");
+ e1.setObjectName("e1");
+ e2.setObjectName("e2");
+ f.setObjectName("f");
+ f1.setObjectName("f1");
+ f2.setObjectName("f2");
+ a1.setObjectName("a1");
+
+ machine.start();
+
+ QTRY_COMPARE(machine.configuration().contains(&b), true);
+ QTRY_COMPARE(machine.configuration().contains(&c), true);
+ QTRY_COMPARE(machine.configuration().contains(&d), true);
+ QTRY_COMPARE(machine.configuration().contains(&e), true);
+ QTRY_COMPARE(machine.configuration().contains(&e1), true);
+ QTRY_COMPARE(machine.configuration().contains(&e2), false);
+ QTRY_COMPARE(machine.configuration().contains(&f), true);
+ QTRY_COMPARE(machine.configuration().contains(&f1), true);
+ QTRY_COMPARE(machine.configuration().contains(&f2), false);
+ QTRY_COMPARE(machine.configuration().contains(&a1), false);
+
+ emitter.emitSignalWithNoArg();
+
+ QTRY_COMPARE(machine.configuration().contains(&b), true);
+ QTRY_COMPARE(machine.configuration().contains(&c), true);
+ QTRY_COMPARE(machine.configuration().contains(&d), true);
+ QTRY_COMPARE(machine.configuration().contains(&e), true);
+ QTRY_COMPARE(machine.configuration().contains(&e1), false);
+ QTRY_COMPARE(machine.configuration().contains(&e2), true);
+ QTRY_COMPARE(machine.configuration().contains(&f), true);
+ QTRY_COMPARE(machine.configuration().contains(&f1), false);
+ QTRY_COMPARE(machine.configuration().contains(&f2), true);
+ QTRY_COMPARE(machine.configuration().contains(&a1), false);
+
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::conflictingTransition2()
+{
+ SignalEmitter emitter;
+
+ QStateMachine machine;
+ QState s0(&machine);
+ QState p0(QState::ParallelStates, &s0);
+ QState p0s1(&p0);
+ QState p0s2(&p0);
+ QState p0s3(&p0);
+ QState s1(&machine);
+
+ machine.setInitialState(&s0);
+ s0.setInitialState(&p0);
+
+ QSignalTransition *t1 = new QSignalTransition(&emitter, SIGNAL(signalWithNoArg()));
+ p0s1.addTransition(t1);
+ QSignalTransition *t2 = p0s2.addTransition(&emitter, SIGNAL(signalWithNoArg()), &p0s1);
+ QSignalTransition *t3 = p0s3.addTransition(&emitter, SIGNAL(signalWithNoArg()), &s1);
+ QSignalSpy t1Spy(t1, &QAbstractTransition::triggered);
+ QSignalSpy t2Spy(t2, &QAbstractTransition::triggered);
+ QSignalSpy t3Spy(t3, &QAbstractTransition::triggered);
+ QVERIFY(t1Spy.isValid());
+ QVERIFY(t2Spy.isValid());
+ QVERIFY(t3Spy.isValid());
+
+ s0.setObjectName("s0");
+ p0.setObjectName("p0");
+ p0s1.setObjectName("p0s1");
+ p0s2.setObjectName("p0s2");
+ p0s3.setObjectName("p0s3");
+ s1.setObjectName("s1");
+ t1->setObjectName("p0s1->p0s1");
+ t2->setObjectName("p0s2->p0s1");
+ t3->setObjectName("p0s3->s1");
+
+ machine.start();
+
+ QTRY_COMPARE(machine.configuration().contains(&s0), true);
+ QTRY_COMPARE(machine.configuration().contains(&p0), true);
+ QTRY_COMPARE(machine.configuration().contains(&p0s1), true);
+ QTRY_COMPARE(machine.configuration().contains(&p0s2), true);
+ QTRY_COMPARE(machine.configuration().contains(&p0s3), true);
+ QTRY_COMPARE(machine.configuration().contains(&s1), false);
+
+ QCOMPARE(t1Spy.size(), 0);
+ QCOMPARE(t2Spy.size(), 0);
+ QCOMPARE(t3Spy.size(), 0);
+
+ emitter.emitSignalWithNoArg();
+
+ QTRY_COMPARE(machine.configuration().contains(&s0), true);
+ QTRY_COMPARE(machine.configuration().contains(&p0), true);
+ QTRY_COMPARE(machine.configuration().contains(&p0s1), true);
+ QTRY_COMPARE(machine.configuration().contains(&p0s2), true);
+ QTRY_COMPARE(machine.configuration().contains(&p0s3), true);
+ QTRY_COMPARE(machine.configuration().contains(&s1), false);
+
+ QCOMPARE(t1Spy.size(), 1);
+ QCOMPARE(t2Spy.size(), 1);
+ QCOMPARE(t3Spy.size(), 0); // t3 got preempted by t2
+
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::qtbug_46059()
+{
+ QStateMachine machine;
+ QState a(&machine);
+ QState b(&a);
+ QState c(&a);
+ QState success(&a);
+ QState failure(&machine);
+
+ machine.setInitialState(&a);
+ a.setInitialState(&b);
+ b.addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), &c));
+ c.addTransition(new EventTransition(QEvent::Type(QEvent::User + 2), &success));
+ b.addTransition(new EventTransition(QEvent::Type(QEvent::User + 2), &failure));
+
+ machine.start();
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().contains(&a), true);
+ QTRY_COMPARE(machine.configuration().contains(&b), true);
+ QTRY_COMPARE(machine.configuration().contains(&c), false);
+ QTRY_COMPARE(machine.configuration().contains(&failure), false);
+ QTRY_COMPARE(machine.configuration().contains(&success), false);
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 0)), QStateMachine::HighPriority);
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1)), QStateMachine::HighPriority);
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 2)), QStateMachine::NormalPriority);
+ QCoreApplication::processEvents();
+
+ QTRY_COMPARE(machine.configuration().contains(&a), true);
+ QTRY_COMPARE(machine.configuration().contains(&b), false);
+ QTRY_COMPARE(machine.configuration().contains(&c), false);
+ QTRY_COMPARE(machine.configuration().contains(&failure), false);
+ QTRY_COMPARE(machine.configuration().contains(&success), true);
+
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::qtbug_46703()
+{
+ QStateMachine machine;
+ QState root(&machine);
+ QHistoryState h(&root);
+ QState p(QState::ParallelStates, &root);
+ QState a(&p);
+ QState a1(&a);
+ QState a2(&a);
+ QState a3(&a);
+ QState b(&p);
+ QState b1(&b);
+ QState b2(&b);
+
+ machine.setObjectName("machine");
+ root.setObjectName("root");
+ h.setObjectName("h");
+ p.setObjectName("p");
+ a.setObjectName("a");
+ a1.setObjectName("a1");
+ a2.setObjectName("a2");
+ a3.setObjectName("a3");
+ b.setObjectName("b");
+ b1.setObjectName("b1");
+ b2.setObjectName("b2");
+
+ machine.setInitialState(&root);
+ root.setInitialState(&h);
+ a.setInitialState(&a3);
+ b.setInitialState(&b1);
+ struct : public QAbstractTransition {
+ virtual bool eventTest(QEvent *) override { return false; }
+ virtual void onTransition(QEvent *) override {}
+ } defaultTransition;
+ defaultTransition.setTargetStates(QList<QAbstractState*>() << &a2 << &b2);
+ h.setDefaultTransition(&defaultTransition);
+
+ machine.start();
+
+ QTRY_COMPARE(machine.configuration().contains(&root), true);
+ QTRY_COMPARE(machine.configuration().contains(&h), false);
+ QTRY_COMPARE(machine.configuration().contains(&p), true);
+ QTRY_COMPARE(machine.configuration().contains(&a), true);
+ QTRY_COMPARE(machine.configuration().contains(&a1), false);
+ QTRY_COMPARE(machine.configuration().contains(&a2), true);
+ QTRY_COMPARE(machine.configuration().contains(&a3), false);
+ QTRY_COMPARE(machine.configuration().contains(&b), true);
+ QTRY_COMPARE(machine.configuration().contains(&b1), false);
+ QTRY_COMPARE(machine.configuration().contains(&b2), true);
+
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::postEventFromBeginSelectTransitions()
+{
+ class StateMachine : public QStateMachine {
+ protected:
+ void beginSelectTransitions(QEvent* e) override {
+ if (e->type() == QEvent::Type(QEvent::User + 2))
+ postEvent(new QEvent(QEvent::Type(QEvent::User + 1)), QStateMachine::HighPriority);
+ }
+ } machine;
+ QState a(&machine);
+ QState success(&machine);
+
+ machine.setInitialState(&a);
+ a.addTransition(new EventTransition(QEvent::Type(QEvent::User + 1), &success));
+
+ machine.start();
+
+ QTRY_COMPARE(machine.configuration().contains(&a), true);
+ QTRY_COMPARE(machine.configuration().contains(&success), false);
+
+ machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 2)), QStateMachine::NormalPriority);
+
+ QTRY_COMPARE(machine.configuration().contains(&a), false);
+ QTRY_COMPARE(machine.configuration().contains(&success), true);
+
+ QVERIFY(machine.isRunning());
+}
+
+void tst_QStateMachine::dontProcessSlotsWhenMachineIsNotRunning()
+{
+ QStateMachine machine;
+ QState initialState;
+ QFinalState finalState;
+
+ struct Emitter : SignalEmitter
+ {
+ QThread thread;
+ Emitter(QObject *parent = nullptr) : SignalEmitter(parent)
+ {
+ moveToThread(&thread);
+ thread.start();
+ }
+ } emitter;
+
+ initialState.addTransition(&emitter, &Emitter::signalWithNoArg, &finalState);
+ machine.addState(&initialState);
+ machine.addState(&finalState);
+ machine.setInitialState(&initialState);
+ connect(&machine, &QStateMachine::started, &emitter, [&]() {
+ metaObject()->invokeMethod(&emitter, "emitSignalWithNoArg");
+ metaObject()->invokeMethod(&emitter, "emitSignalWithNoArg");
+ });
+ connect(&machine, &QStateMachine::finished, &emitter.thread, &QThread::quit);
+ machine.start();
+ QSignalSpy emittedSpy(&emitter, &SignalEmitter::signalWithNoArg);
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QTRY_COMPARE_WITH_TIMEOUT(emittedSpy.size(), 2, 100);
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ QTRY_VERIFY(emitter.thread.isFinished());
+}
+
+void tst_QStateMachine::cancelDelayedEventWithChrono()
+{
+#if __has_include(<chrono>)
+ QStateMachine machine;
+ QTest::ignoreMessage(QtWarningMsg,
+ "QStateMachine::cancelDelayedEvent: the machine is not running");
+ QVERIFY(!machine.cancelDelayedEvent(-1));
+
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *s2 = new QFinalState(&machine);
+ s1->addTransition(new StringTransition("a", s2));
+ machine.setInitialState(s1);
+
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(startedSpy.isValid());
+ QVERIFY(runningSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+ int id1 = machine.postDelayedEvent(new StringEvent("c"), std::chrono::seconds{50});
+ QVERIFY(id1 != -1);
+ int id2 = machine.postDelayedEvent(new StringEvent("b"), std::chrono::seconds{25});
+ QVERIFY(id2 != -1);
+ QVERIFY(id2 != id1);
+ int id3 = machine.postDelayedEvent(new StringEvent("a"), std::chrono::milliseconds{100});
+ QVERIFY(id3 != -1);
+ QVERIFY(id3 != id2);
+ QVERIFY(machine.cancelDelayedEvent(id1));
+ QVERIFY(!machine.cancelDelayedEvent(id1));
+ QVERIFY(machine.cancelDelayedEvent(id2));
+ QVERIFY(!machine.cancelDelayedEvent(id2));
+
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s2));
+#endif
+}
+
+void tst_QStateMachine::postDelayedEventWithChronoAndStop()
+{
+#if __has_include(<chrono>)
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *s2 = new QFinalState(&machine);
+ s1->addTransition(new StringTransition("a", s2));
+ machine.setInitialState(s1);
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy startedSpy(&machine, &QStateMachine::started);
+ QVERIFY(startedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(true);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ int id1 = machine.postDelayedEvent(new StringEvent("a"), std::chrono::milliseconds{0});
+ QVERIFY(id1 != -1);
+ QSignalSpy stoppedSpy(&machine, &QStateMachine::stopped);
+ QVERIFY(stoppedSpy.isValid());
+ machine.stop();
+ QTRY_COMPARE(stoppedSpy.size(), 1);
+ TEST_RUNNING_CHANGED(false);
+ TEST_ACTIVE_CHANGED(s1, 1);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 2);
+ TEST_RUNNING_CHANGED(true);
+ TEST_ACTIVE_CHANGED(s1, 3);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+
+ int id2 = machine.postDelayedEvent(new StringEvent("a"), std::chrono::seconds{1});
+ QVERIFY(id2 != -1);
+ machine.stop();
+ QTRY_COMPARE(stoppedSpy.size(), 2);
+ TEST_RUNNING_CHANGED(false);
+ TEST_ACTIVE_CHANGED(s1, 3);
+ machine.start();
+ QTRY_COMPARE(startedSpy.size(), 3);
+ TEST_RUNNING_CHANGED(true);
+ QTestEventLoop::instance().enterLoop(2);
+ QCOMPARE(machine.configuration().size(), 1);
+ QVERIFY(machine.configuration().contains(s1));
+ TEST_ACTIVE_CHANGED(s1, 5);
+ QVERIFY(machine.isRunning());
+#endif
+}
+
+class DelayedEventWithChronoPosterThread : public QThread
+{
+ Q_OBJECT
+public:
+ DelayedEventWithChronoPosterThread(QStateMachine *machine, QObject *parent = 0)
+ : QThread(parent), firstEventWasCancelled(false), m_machine(machine)
+ {
+ moveToThread(this);
+ QObject::connect(m_machine, SIGNAL(started()), this, SLOT(postEvent()));
+ }
+
+ mutable bool firstEventWasCancelled;
+
+private Q_SLOTS:
+ void postEvent()
+ {
+#if __has_include(<chrono>)
+ int id = m_machine->postDelayedEvent(new QEvent(QEvent::User), std::chrono::seconds{1});
+ firstEventWasCancelled = m_machine->cancelDelayedEvent(id);
+
+ m_machine->postDelayedEvent(new QEvent(QEvent::User), std::chrono::milliseconds{1});
+
+ quit();
+#endif
+ }
+
+private:
+ QStateMachine *m_machine;
+};
+
+void tst_QStateMachine::postDelayedEventWithChronoFromThread()
+{
+#if __has_include(<chrono>)
+ QStateMachine machine;
+ QState *s1 = new QState(&machine);
+ DEFINE_ACTIVE_SPY(s1);
+ QFinalState *f = new QFinalState(&machine);
+ s1->addTransition(new EventTransition(QEvent::User, f));
+ machine.setInitialState(s1);
+
+ DelayedEventWithChronoPosterThread poster(&machine);
+ poster.start();
+
+ QSignalSpy runningSpy(&machine, &QStateMachine::runningChanged);
+ QVERIFY(runningSpy.isValid());
+ QSignalSpy finishedSpy(&machine, &QStateMachine::finished);
+ QVERIFY(finishedSpy.isValid());
+ machine.start();
+ QTRY_COMPARE(finishedSpy.size(), 1);
+ TEST_RUNNING_CHANGED_STARTED_STOPPED;
+ TEST_ACTIVE_CHANGED(s1, 2);
+ QVERIFY(poster.firstEventWasCancelled);
+#endif
+}
+
+void tst_QStateMachine::bindings()
+{
+ QStateMachine machine;
+ TestState *state1 = new TestState(&machine);
+ TestTransition *transition1 = new TestTransition(state1);
+ TestTransition *transition2 = new TestTransition(state1);
+
+ // -- QHistoryState::defaultTransition
+ QHistoryState *historyState = new QHistoryState(&machine);
+ QTestPrivate::testReadWritePropertyBasics<QHistoryState, QAbstractTransition*>(
+ *historyState, transition1, transition2, "defaultTransition");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QHistoryState::defaultTransition bindable test failed.";
+ return;
+ }
+
+ // -- QHistoryState::historyType
+ QHistoryState::HistoryType type1 = QHistoryState::HistoryType::DeepHistory;
+ QHistoryState::HistoryType type2 = QHistoryState::HistoryType::ShallowHistory;
+ QTestPrivate::testReadWritePropertyBasics<QHistoryState, QHistoryState::HistoryType>(
+ *historyState, type1, type2, "historyType");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QHistoryState::historyType bindable test failed.";
+ return;
+ }
+
+ // -- QState::initialState. Cannot be tested for bindable loops.
+ QAbstractState *is1 = new QState(state1);
+ QAbstractState *is2 = new QState(state1);
+ QTestPrivate::testReadWritePropertyBasics<QState, QAbstractState*>(
+ *state1, is1, is2, "initialState", []() { return nullptr; });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QState::initialState bindable test failed.";
+ return;
+ }
+
+ // -- QState::errorState. Cannot be tested for bindable loops.
+ QTestPrivate::testReadWritePropertyBasics<QState, QAbstractState*>(
+ *state1, is1, is2, "errorState", []() { return nullptr; });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QState::errorState bindable test failed.";
+ return;
+ }
+
+ // -- QState::childMode
+ // Make a new state as re-use of state1 would yield irrelevant warnings
+ TestState *state2 = new TestState(&machine);
+ QState::ChildMode mode1 = QState::ChildMode::ParallelStates;
+ QState::ChildMode mode2 = QState::ChildMode::ExclusiveStates;
+ QTestPrivate::testReadWritePropertyBasics<QState, QState::ChildMode>(
+ *state2, mode1, mode2, "childMode");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QState::childMode bindable test failed.";
+ return;
+ }
+
+ // -- QAbstractState::active
+ QState *startState = new QState(&machine);
+ QFinalState *endState = new QFinalState(&machine);
+ DEFINE_ACTIVE_SPY(startState);
+ DEFINE_ACTIVE_SPY(endState);
+ machine.setInitialState(startState);
+ startState->addTransition(endState);
+
+ // Create a binding to the "active" property
+ QProperty<bool> active([&](){ return endState->active(); });
+ QVERIFY(!active);
+ machine.start();
+ // startState should get "enter/active == true" + "exit/active == false"
+ QTRY_COMPARE(startState_activeSpy.size(), 2);
+ // endState should get "enter/active == true"
+ QTRY_COMPARE(endState_activeSpy.size(), 1);
+ // Verify that the "active" value changed via binding
+ QVERIFY(active);
+
+ // -- QSignalTransition::senderObject
+ QSignalTransition signalTransition;
+ QObject sender1;
+ QObject sender2;
+ QTestPrivate::testReadWritePropertyBasics<QSignalTransition, const QObject*>(
+ signalTransition, &sender1, &sender2, "senderObject");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QSignalTransition::senderObject bindable test failed.";
+ return;
+ }
+
+ // -- QSignalTransition::signal
+ QByteArray arr1("arr1");
+ QByteArray arr2("arr2");
+ QTestPrivate::testReadWritePropertyBasics<QSignalTransition, QByteArray>(
+ signalTransition, arr1, arr2, "signal");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QSignalTransition::signal bindable test failed.";
+ return;
+ }
+
+ // -- QAbstractTransition::transitionType
+ auto transitionType1 = QAbstractTransition::InternalTransition;
+ auto transitionType2 = QAbstractTransition::ExternalTransition;
+ QTestPrivate::testReadWritePropertyBasics<QAbstractTransition, QAbstractTransition::TransitionType>(
+ signalTransition, transitionType1, transitionType2, "transitionType",
+ []() { return std::make_unique<QSignalTransition>(); });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QAbstractTransition::transitionType bindable test failed.";
+ return;
+ }
+
+ // -- QStateMachine::errorString
+ QStateMachine brokenMachine;
+ brokenMachine.setErrorState(new QState(&machine)); // avoid warnings
+ QState* brokenState = new QState(&brokenMachine);
+ brokenMachine.setInitialState(brokenState);
+ new QState(brokenState); // make broken state a compound state that lacks the initial state
+ QTestPrivate::testReadOnlyPropertyBasics<QStateMachine, QString>(
+ brokenMachine, QString(), QStringLiteral("Missing initial state in compound state ''"),
+ "errorString",
+ [&](){
+ brokenMachine.start();
+ QTRY_VERIFY(brokenMachine.isRunning());
+ });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QStateMachine::errorString bindable test failed for (non-empty case).";
+ return;
+ }
+ // test also clearing of the error above
+ QTestPrivate::testReadOnlyPropertyBasics<QStateMachine, QString>(
+ brokenMachine, QStringLiteral("Missing initial state in compound state ''"), QString(),
+ "errorString",
+ [&](){ brokenMachine.clearError(); });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QStateMachine::errorString bindable test failed (empty case).";
+ return;
+ }
+
+ // -- QStateMachine::globalRestorePolicy
+ QTestPrivate::testReadWritePropertyBasics<QStateMachine, QState::RestorePolicy>(
+ machine, QState::RestorePolicy::RestoreProperties,
+ QState::RestorePolicy::DontRestoreProperties, "globalRestorePolicy");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QStateMachine::globalRestorePolicy bindable test failed.";
+ return;
+ }
+
+#if QT_CONFIG(animation)
+ // -- QStateMachine::animated
+ QTestPrivate::testReadWritePropertyBasics<QStateMachine, bool>(
+ machine, false, true, "animated");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QStateMachine::animated bindable test failed.";
+ return;
+ }
+#endif
+
+ // -- QEventTransition::eventSource
+ QEventTransition eventTransition;
+ QObject source1;
+ QObject source2;
+ QTestPrivate::testReadWritePropertyBasics<QEventTransition, QObject*>(
+ eventTransition, &source1, &source2, "eventSource");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QEventTransition::eventSource bindable test failed.";
+ return;
+ }
+
+ // -- QEventTransition::eventType
+ auto eventType1 = QEvent::Enter;
+ auto eventType2 = QEvent::Leave;
+ QTestPrivate::testReadWritePropertyBasics<QEventTransition, QEvent::Type>(
+ eventTransition, eventType1, eventType2, "eventType");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QEventTransition::eventType bindable test failed.";
+ return;
+ }
+
+ // -- QKeyEventTransition::key
+ QKeyEventTransition keyEventTransition;
+ int key1{1};
+ int key2{2};
+ QTestPrivate::testReadWritePropertyBasics<QKeyEventTransition, int>(
+ keyEventTransition, key1, key2, "key");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QKeyEventTransition::key bindable test failed.";
+ return;
+ }
+
+ // -- QKeyEventTransition::modifierMask
+ Qt::KeyboardModifiers mod1 = Qt::KeyboardModifier::ShiftModifier;
+ Qt::KeyboardModifiers mod2 = Qt::KeyboardModifier::ControlModifier;
+ QTestPrivate::testReadWritePropertyBasics<QKeyEventTransition>(
+ keyEventTransition, mod1, mod2, "modifierMask");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QKeyEventTransition::modifierMask bindable test failed.";
+ return;
+ }
+
+ // -- QMouseEventTransition::button
+ QMouseEventTransition mouseEventTransition;
+ Qt::MouseButton button1 = Qt::MouseButton::LeftButton;
+ Qt::MouseButton button2 = Qt::MouseButton::RightButton;
+ QTestPrivate::testReadWritePropertyBasics<QMouseEventTransition>(
+ mouseEventTransition, button1, button2, "button");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QMouseEventTransition::button bindable test failed.";
+ return;
+ }
+
+ // -- QMouseEventTransition::modifierMask
+ QTestPrivate::testReadWritePropertyBasics<QMouseEventTransition>(
+ mouseEventTransition, mod1, mod2, "modifierMask");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QMouseEventTransition::modifierMask bindable test failed.";
+ return;
+ }
+}
+
+QTEST_MAIN(tst_QStateMachine)
+#include "tst_qstatemachine.moc"
diff --git a/tests/auto/scion/CMakeLists.txt b/tests/auto/scion/CMakeLists.txt
new file mode 100644
index 0000000..56231af
--- /dev/null
+++ b/tests/auto/scion/CMakeLists.txt
@@ -0,0 +1,149 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_scion Test:
+#####################################################################
+
+qt_internal_add_test(tst_scion
+ SOURCES
+ tst_scion.cpp
+ DEFINES
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Scxml
+)
+#### Ignored generated resource: ${CMAKE_CURRENT_BINARY_DIR}/scion.qrc
+#### Keys ignored in scope 1:.:.:scion.pro:<TRUE>:
+# ALLFILES = "$$SCXMLS_DIR/*.*,"
+# ALLSCXMLS = "$$SCXMLS_DIR/*.scxml,"
+# BLACKLISTED = "test216sub1.scxml" "test226sub1.txml" "test239sub1.scxml" "test242sub1.scxml" "test276sub1.scxml" "test530.txml.scxml" "test301.txml.scxml" "test441a.txml.scxml" "test441b.txml.scxml" "test557.txml.scxml"
+# QMAKE_EXTRA_COMPILERS = "myscxml" "myscxml_hdr"
+# SCXMLS_DIR = "$$absolute_path($$PWD/../../3rdparty/scion-tests/scxml-test-framework/test)"
+# TEMPLATE = "app"
+# contents = "'<!DOCTYPE" "RCC><RCC" "version=\"1.0\">'" "'<qresource>'" "$$qrc" "'</qresource></RCC>'"
+# myscxml.commands = "$$QMAKE_QSCXMLC" "--header" "scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.h" "--impl" "${QMAKE_FILE_OUT}" "--namespace" "${QMAKE_FUNC_nameTheNamespace}" "--classname" "${QMAKE_FUNC_nameTheClass}" "${QMAKE_FILE_IN}"
+# myscxml.depends = "$$QMAKE_QSCXMLC_EXE"
+# myscxml.input = "SCXMLS"
+# myscxml.output = "scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.cpp"
+# myscxml.variable_out = "SOURCES"
+# myscxml_hdr.commands = "$$escape_expand(\\n)"
+# myscxml_hdr.depends = "scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.cpp"
+# myscxml_hdr.input = "SCXMLS"
+# myscxml_hdr.output = "scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.h"
+# myscxml_hdr.variable_out = "SCXML_HEADERS"
+
+## Scopes:
+#####################################################################
+
+#### Keys ignored in scope 2:.:.:scion.pro:defineReplace(nameTheNamespace):
+# sn = "__relative_path___absolute_path___dirname_1_____OUT_PWD____SCXMLS_DIR_"
+
+#### Keys ignored in scope 3:.:.:scion.pro:defineReplace(nameTheClass):
+# cn = "1"
+
+qt_internal_extend_target(tst_scion CONDITION MSVC AND WIN32
+ COMPILE_OPTIONS
+ /bigobj
+)
+
+# For a better explanation about the "blacklisted" tests, see tst_scion.cpp
+# <invoke>. The files with sub in their file name are loaded from other
+# tests, they are not to be run alone.
+
+set(blacklisted
+ test216sub1.scxml
+ test226sub1.txml
+ test239sub1.scxml
+ test242sub1.scxml
+ test276sub1.scxml
+ # 530: Scxml children element and expr attribute in content, not supported
+ # at the moment by the scxml compiler.
+ test530.txml.scxml)
+
+# other
+list(APPEND blacklisted
+ # 301: The scxml is trying to use a nonexistent file as a script souce,
+ # script compilation fails.
+ test301.txml.scxml
+ # 441a and b tests the exmode scxml element attribute that is not
+ # supported by the scxml compiler. This attribute was also removed
+ # from the latest scxml specification.
+ test441a.txml.scxml
+ test441b.txml.scxml
+ # 557: Compilation fails at parsing books as data element content and loading
+ # data content from external file.
+ test557.txml.scxml)
+
+set(scxmls_dir ../../3rdparty/scion-tests/scxml-test-framework/test)
+get_filename_component(scxmls_dir ${scxmls_dir} ABSOLUTE)
+file(GLOB_RECURSE allscxmls ${scxmls_dir}/*.scxml)
+
+_qt_internal_get_tool_wrapper_script_path(tool_wrapper)
+set(qscxmlc_bin "${tool_wrapper}" "$<TARGET_FILE:${QT_CMAKE_EXPORT_NAMESPACE}::qscxmlc>")
+
+set(scxmls)
+set(inc_list)
+set(func_list)
+set(test_bases)
+foreach(f ${allscxmls})
+ get_filename_component(cn ${f} NAME)
+ if(NOT cn IN_LIST blacklisted)
+ list(APPEND scxmls ${f})
+
+ string(REGEX REPLACE "\\.scxml$" "" cn ${cn})
+ set(hn ${cn})
+ string(REGEX REPLACE "\\.txml$" "" cn ${cn})
+ get_filename_component(sn ${f} DIRECTORY)
+ file(RELATIVE_PATH sn ${scxmls_dir} ${sn})
+ string(REGEX REPLACE "[^a-zA-Z_0-9]" "_" sn ${sn})
+
+ set(out_h scxml/${sn}_${hn}.h)
+ set(out_cpp scxml/${sn}_${hn}.cpp)
+ string(APPEND inc_list "#include \"${out_h}\"\n")
+ string(APPEND func_list " []()->QScxmlStateMachine*{return new ${sn}::${cn};},\n")
+
+ file(RELATIVE_PATH tn ${scxmls_dir} ${f})
+ string(REGEX REPLACE "\\.scxml$" "" tn ${tn})
+ string(APPEND test_bases " \"${tn}\",\n")
+
+ file(TO_NATIVE_PATH ${out_h} native_out_h)
+ file(TO_NATIVE_PATH ${out_cpp} native_out_cpp)
+ add_custom_command(
+ OUTPUT ${out_cpp} ${out_h}
+ COMMAND
+ ${qscxmlc_bin} --header ${native_out_h} --impl ${native_out_cpp}
+ --namespace ${sn} --classname ${cn} ${f}
+ DEPENDS ${QT_CMAKE_EXPORT_NAMESPACE}::qscxmlc
+ VERBATIM
+ )
+ set_source_files_properties(${out_h} ${out_cpp} PROPERTIES SKIP_AUTOMOC TRUE)
+ qt_internal_extend_target(tst_scion SOURCES ${out_h} ${out_cpp})
+ endif()
+endforeach()
+
+file(GLOB_RECURSE allfiles ${scxmls_dir}/*.*)
+foreach(f ${allfiles})
+ file(RELATIVE_PATH base ${scxmls_dir} ${f})
+ set_source_files_properties(${f}
+ PROPERTIES
+ QT_SKIP_QUICKCOMPILER ON
+ QT_RESOURCE_ALIAS ${base}
+ )
+endforeach()
+
+qt_internal_add_resource(tst_scion "scion"
+ PREFIX "/"
+ FILES ${allfiles}
+)
+
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/scxml/compiled_tests.h
+ "${inc_list}\nstd::function<QScxmlStateMachine *()> creators[] = {\n${func_list}};")
+file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/scxml/scion.h
+ "const char *testBases[] = {\n${test_bases}};")
+qt_internal_extend_target(tst_scion SOURCES
+ ${CMAKE_CURRENT_BINARY_DIR}/scxml/compiled_tests.h
+ ${CMAKE_CURRENT_BINARY_DIR}/scxml/scion.h)
diff --git a/tests/auto/scion/scion.pro b/tests/auto/scion/scion.pro
deleted file mode 100644
index 1ebc624..0000000
--- a/tests/auto/scion/scion.pro
+++ /dev/null
@@ -1,103 +0,0 @@
-QT = core gui qml testlib scxml
-CONFIG += testcase c++11 console
-CONFIG -= app_bundle
-TARGET = tst_scion
-
-TEMPLATE = app
-
-RESOURCES = $$OUT_PWD/scion.qrc
-DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII
-
-SOURCES += \
- tst_scion.cpp
-
-defineReplace(nameTheNamespace) {
- sn=$$relative_path($$absolute_path($$dirname(1), $$OUT_PWD),$$SCXMLS_DIR)
- sn~=s/\\.txml$//
- sn~=s/[^a-zA-Z_0-9]/_/
- return ($$sn)
-}
-defineReplace(nameTheClass) {
- cn = $$basename(1)
- cn ~= s/\\.scxml$//
- cn ~=s/\\.txml$//
- cn ~= s/[^a-zA-Z_0-9]/_/
- return ($$cn)
-}
-
-qtPrepareTool(QMAKE_QSCXMLC, qscxmlc)
-
-win32 {
- msvc: QMAKE_CXXFLAGS += /bigobj
-}
-
-myscxml.commands = $$QMAKE_QSCXMLC --header scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.h --impl ${QMAKE_FILE_OUT} --namespace ${QMAKE_FUNC_nameTheNamespace} --classname ${QMAKE_FUNC_nameTheClass} ${QMAKE_FILE_IN}
-myscxml.depends += $$QMAKE_QSCXMLC_EXE
-myscxml.output = scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.cpp
-myscxml.input = SCXMLS
-myscxml.variable_out = SOURCES
-QMAKE_EXTRA_COMPILERS += myscxml
-
-myscxml_hdr.input = SCXMLS
-myscxml_hdr.variable_out = SCXML_HEADERS
-myscxml_hdr.commands = $$escape_expand(\\n)
-myscxml_hdr.depends = scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.cpp
-myscxml_hdr.output = scxml/${QMAKE_FUNC_nameTheNamespace}_${QMAKE_FILE_IN_BASE}.h
-QMAKE_EXTRA_COMPILERS += myscxml_hdr
-
-SCXMLS_DIR += $$absolute_path($$PWD/../../3rdparty/scion-tests/scxml-test-framework/test)
-ALLSCXMLS = $$files($$SCXMLS_DIR/*.scxml, true)
-
-# For a better explanation about the "blacklisted" tests, see tst_scion.cpp
-# <invoke>
-BLACKLISTED = \
- test216sub1.scxml \
- test226sub1.txml \
- test239sub1.scxml \
- test242sub1.scxml \
- test276sub1.scxml \
- test530.txml.scxml
-
-# other
-BLACKLISTED += \
- test301.txml.scxml \
- test441a.txml.scxml \
- test441b.txml.scxml \
- test557.txml.scxml
-
-for (f,ALLSCXMLS) {
- cn = $$basename(f)
- if (!contains(BLACKLISTED, $$cn)) {
- SCXMLS += $$f
-
- cn ~= s/\\.scxml$//
- hn = $$cn
- cn ~=s/\\.txml$//
- sn = $$relative_path($$dirname(f), $$SCXMLS_DIR)
- sn ~=s/[^a-zA-Z_0-9]/_/
-
- inc_list += "$${LITERAL_HASH}include \"scxml/$${sn}_$${hn}.h\""
- func_list += " []()->QScxmlStateMachine*{return new $${sn}::$${cn};},"
-
- base = $$relative_path($$f,$$absolute_path($$SCXMLS_DIR))
- tn = $$base
- tn ~= s/\\.scxml$//
- testBases += " \"$$tn\","
- }
-}
-
-ALLFILES = $$files($$SCXMLS_DIR/*.*, true)
-for (f,ALLFILES) {
- base = $$relative_path($$f,$$absolute_path($$SCXMLS_DIR))
- file = $$relative_path($$f, $$absolute_path($$OUT_PWD))
- qrc += '<file alias="$$base">$$file</file>'
-}
-
-contents = $$inc_list "std::function<QScxmlStateMachine *()> creators[] = {" $$func_list "};"
-write_file("$$OUT_PWD/scxml/compiled_tests.h", contents)|error("Aborting.")
-
-contents = "const char *testBases[] = {" $$testBases "};"
-write_file("$$OUT_PWD/scxml/scion.h", contents)|error("Aborting.")
-
-contents = '<!DOCTYPE RCC><RCC version=\"1.0\">' '<qresource>' $$qrc '</qresource></RCC>'
-write_file("$$OUT_PWD/scion.qrc", contents)|error("Aborting.")
diff --git a/tests/auto/scion/tst_scion.cpp b/tests/auto/scion/tst_scion.cpp
index 5473e9d..e9be765 100644
--- a/tests/auto/scion/tst_scion.cpp
+++ b/tests/auto/scion/tst_scion.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
#include <QCoreApplication>
@@ -109,7 +84,7 @@ QByteArray DynamicLoader::load(const QString &name,
QUrl url(name);
if (!url.isLocalFile() && !url.isRelative())
errs << QStringLiteral("src attribute is not a local file (%1)").arg(name);
- QFileInfo fInfo = url.isLocalFile() ? url.toLocalFile() : name;
+ QFileInfo fInfo(url.isLocalFile() ? url.toLocalFile() : name);
if (fInfo.isRelative())
fInfo = QFileInfo(QDir(baseDir).filePath(fInfo.filePath()));
fInfo = QFileInfo(QLatin1String(":/") + fInfo.filePath()); // take it from resources
@@ -269,16 +244,38 @@ void TestScion::compiled()
static bool verifyStates(QScxmlStateMachine *stateMachine, const QJsonObject &stateDescription, const QString &key, int counter)
{
+ const auto errorMessage = [&key, &counter](const QStringList& current,
+ const QStringList& expected, const QLatin1String& details) {
+ qWarning("Incorrect %s (%d)!", qPrintable(key), counter);
+ qWarning() << "Current configuration:" << current;
+ qWarning() << "Expected configuration:" << expected;
+ qWarning() << "Failed state read was done with:" << details;
+ };
+ using namespace Qt::StringLiterals;
+
+ // Verify that activeStateNames() matches the expectation
auto current = stateMachine->activeStateNames();
std::sort(current.begin(), current.end());
auto expected = getStates(stateDescription, key);
- if (current == expected)
- return true;
+ if (current != expected) {
+ errorMessage(current, expected, "activeStateNames()"_L1);
+ return false;
+ }
- qWarning("Incorrect %s (%d)!", qPrintable(key), counter);
- qWarning() << "Current configuration:" << current;
- qWarning() << "Expected configuration:" << expected;
- return false;
+ for (const auto& s : expected) {
+ // Verify that isActive(stateName) matches the expectation
+ if (!stateMachine->isActive(s)) {
+ errorMessage(current, expected, "isActive()"_L1);
+ return false;
+ }
+ // Verify that the metaobject matches the expectation
+ const auto mo = stateMachine->metaObject();
+ if (!mo->property(mo->indexOfProperty(s.toLocal8Bit())).read(stateMachine).toBool()) {
+ errorMessage(current, expected, "metaobject read"_L1);
+ return false;
+ }
+ }
+ return true;
}
static bool playEvent(QScxmlStateMachine *stateMachine, const QJsonObject &eventDescription, int counter)
@@ -329,18 +326,54 @@ static bool playEvent(QScxmlStateMachine *stateMachine, const QJsonObject &event
e->setOrigin(origin);
e->setOriginType(origintype);
e->setInvokeId(invokeid);
- if (eventDescription.contains(QLatin1String("after")))
- QTest::qWait(eventDescription.value(QLatin1String("after")).toInt());
+ int delay = 0;
+ if (eventDescription.contains(QLatin1String("after"))) {
+ delay = eventDescription.value(QLatin1String("after")).toInt();
+ Q_ASSERT(delay > 0);
+ e->setDelay(delay);
+ }
+
+ QTimer trigger;
+ trigger.setSingleShot(true);
+ stateMachine->connectToEvent(eventName, &trigger, [&stateMachine, &trigger](const QScxmlEvent &) {
+ QObject::connect(stateMachine, &QScxmlStateMachine::reachedStableState,
+ &trigger, QOverload<>::of(&QTimer::start));
+ });
+ MySignalSpy triggerSpy(&trigger, SIGNAL(timeout()));
+
+ // Create a signal spy for each state we expect to be (de)activated in next transition.
+ using namespace Qt::StringLiterals;
+ const auto expectedActive = getStates(eventDescription, QLatin1String("nextConfiguration"));
+ std::list<std::unique_ptr<QSignalSpy>> stateSpies;
+ const auto mo = stateMachine->metaObject();
+
+ for (const auto& s : stateMachine->stateNames()) {
+ if (expectedActive.contains(s) == stateMachine->isActive(s))
+ continue;
+ // The state is expected to undergo an activation change, create a signal spy
+ const auto changedSignal = s + "Changed(bool)"_L1;
+ auto signalIndex = mo->indexOfSignal(changedSignal.toUtf8().constData());
+ stateSpies.push_back(std::make_unique<QSignalSpy>(stateMachine, mo->method(signalIndex)));
+ }
stateMachine->submitEvent(e);
- if (!MySignalSpy(stateMachine, SIGNAL(reachedStableState())).fastWait()) {
- qWarning() << "State machine did not reach a stable state!";
+ if (!triggerSpy.fastWait()) {
+ qWarning() << "State machine did not reach a stable state.";
} else if (verifyStates(stateMachine, eventDescription, QLatin1String("nextConfiguration"), counter)) {
+ for (const auto& spy : stateSpies) {
+ if (spy->size() != 1) {
+ qWarning() << "Signal error for:" << spy->signal() << ", size:" << spy->size();
+ return false;
+ }
+ }
return true;
}
- qWarning() << "... after sending event" << event;
+ if (delay > 0)
+ qWarning("... after sending delayed event %s with a delay of %dms.", qPrintable(eventName), delay);
+ else
+ qWarning("... after sending event %s.", qPrintable(eventName));
return false;
}
@@ -369,6 +402,11 @@ QDebug operator<<(QDebug debug, const QScxmlEvent &event)
obj.insert(QLatin1String("invokeid"), event.invokeId());
return debug << obj;
}
+
+QDebug operator<<(QDebug debug, const QList<QScxmlEvent> &events)
+{
+ return QtPrivate::printSequentialContainer(debug, "QList", events);
+}
QT_END_NAMESPACE
static int verifyEvent(const QList<QScxmlEvent> &receivedEvents, const QJsonObject &event,
@@ -406,7 +444,7 @@ static int verifyEvent(const QList<QScxmlEvent> &receivedEvents, const QJsonObje
const QString invokeId = verifyInvokeId ? event.value(QLatin1String("invokeid")).toString()
: QString();
- while (position < receivedEvents.length()) {
+ while (position < receivedEvents.size()) {
const QScxmlEvent &receivedEvent = receivedEvents[position];
if ((verifyName && receivedEvent.name() != name)
|| (verifyEventType && receivedEvent.eventType() != eventType)
diff --git a/tests/auto/scxmlcoutput/CMakeLists.txt b/tests/auto/scxmlcoutput/CMakeLists.txt
new file mode 100644
index 0000000..40a8844
--- /dev/null
+++ b/tests/auto/scxmlcoutput/CMakeLists.txt
@@ -0,0 +1,9 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if (TARGET Qt::Scxml)
+ add_subdirectory(default)
+ add_subdirectory(namespace)
+ add_subdirectory(path)
+ add_subdirectory(arguments)
+endif()
diff --git a/tests/auto/scxmlcoutput/arguments/CMakeLists.txt b/tests/auto/scxmlcoutput/arguments/CMakeLists.txt
new file mode 100644
index 0000000..664ff67
--- /dev/null
+++ b/tests/auto/scxmlcoutput/arguments/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(WASM)
+ return()
+endif()
+
+qt_internal_add_test(tst_scxmlcoutput_arguments
+ SOURCES
+ tst_scxmlcoutput.cpp
+ LIBRARIES
+ Qt::Scxml
+)
+
+# This testcase tests providing the namespace
+qt6_add_statecharts(tst_scxmlcoutput_arguments
+ ../shared/ids1.scxml
+ OPTIONS --classname MyClass
+)
diff --git a/tests/auto/scxmlcoutput/arguments/tst_scxmlcoutput.cpp b/tests/auto/scxmlcoutput/arguments/tst_scxmlcoutput.cpp
new file mode 100644
index 0000000..75a85a5
--- /dev/null
+++ b/tests/auto/scxmlcoutput/arguments/tst_scxmlcoutput.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest>
+#include <QObject>
+
+// This testcase tests the cmake scxmlc generation, and the testcase itself
+// does mere basic compilation & statemachine instantiation; these fail
+// if qscxmlc generation does not work as expected
+#include "ids1.h"
+
+class tst_scxmlcoutput: public QObject
+{
+ Q_OBJECT
+private slots:
+ void instantiates();
+};
+
+void tst_scxmlcoutput::instantiates() {
+ MyClass statemachine;
+ QVERIFY(statemachine.property("foo.bar").isValid());
+}
+
+QTEST_MAIN(tst_scxmlcoutput)
+#include "tst_scxmlcoutput.moc"
diff --git a/tests/auto/scxmlcoutput/default/CMakeLists.txt b/tests/auto/scxmlcoutput/default/CMakeLists.txt
new file mode 100644
index 0000000..294e4b7
--- /dev/null
+++ b/tests/auto/scxmlcoutput/default/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(WASM)
+ return()
+endif()
+
+qt_internal_add_test(tst_scxmlcoutput_default
+ SOURCES
+ tst_scxmlcoutput.cpp
+ LIBRARIES
+ Qt::Scxml
+)
+
+# This testcase tests that the default code generation,
+# ie. no options provided for the qt6_add_statecharts
+qt6_add_statecharts(tst_scxmlcoutput_default
+ ../shared/ids1.scxml
+)
diff --git a/tests/auto/scxmlcoutput/default/tst_scxmlcoutput.cpp b/tests/auto/scxmlcoutput/default/tst_scxmlcoutput.cpp
new file mode 100644
index 0000000..8e014a7
--- /dev/null
+++ b/tests/auto/scxmlcoutput/default/tst_scxmlcoutput.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest>
+#include <QObject>
+
+// This testcase tests the cmake scxmlc generation, and the testcase itself
+// does mere basic compilation & statemachine instantiation; these fail
+// if qscxmlc generation does not work as expected
+#include "ids1.h"
+
+class tst_scxmlcoutput: public QObject
+{
+ Q_OBJECT
+private slots:
+ void instantiates();
+};
+
+void tst_scxmlcoutput::instantiates() {
+ ids1 statemachine;
+ QVERIFY(statemachine.property("foo.bar").isValid());
+}
+
+QTEST_MAIN(tst_scxmlcoutput)
+#include "tst_scxmlcoutput.moc"
diff --git a/tests/auto/scxmlcoutput/namespace/CMakeLists.txt b/tests/auto/scxmlcoutput/namespace/CMakeLists.txt
new file mode 100644
index 0000000..0530193
--- /dev/null
+++ b/tests/auto/scxmlcoutput/namespace/CMakeLists.txt
@@ -0,0 +1,19 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(WASM)
+ return()
+endif()
+
+qt_internal_add_test(tst_scxmlcoutput_namespace
+ SOURCES
+ tst_scxmlcoutput.cpp
+ LIBRARIES
+ Qt::Scxml
+)
+
+# This testcase tests providing the namespace
+qt6_add_statecharts(tst_scxmlcoutput_namespace
+ ../shared/ids1.scxml
+ NAMESPACE MyNamespace
+)
diff --git a/tests/auto/scxmlcoutput/namespace/tst_scxmlcoutput.cpp b/tests/auto/scxmlcoutput/namespace/tst_scxmlcoutput.cpp
new file mode 100644
index 0000000..60f75e0
--- /dev/null
+++ b/tests/auto/scxmlcoutput/namespace/tst_scxmlcoutput.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest>
+#include <QObject>
+
+// This testcase tests the cmake scxmlc generation, and the testcase itself
+// does mere basic compilation & statemachine instantiation; these fail
+// if qscxmlc generation does not work as expected
+#include "ids1.h"
+
+class tst_scxmlcoutput: public QObject
+{
+ Q_OBJECT
+private slots:
+ void instantiates();
+};
+
+void tst_scxmlcoutput::instantiates() {
+ MyNamespace::ids1 statemachine;
+ QVERIFY(statemachine.property("foo.bar").isValid());
+}
+
+QTEST_MAIN(tst_scxmlcoutput)
+#include "tst_scxmlcoutput.moc"
diff --git a/tests/auto/scxmlcoutput/path/CMakeLists.txt b/tests/auto/scxmlcoutput/path/CMakeLists.txt
new file mode 100644
index 0000000..cadcf9b
--- /dev/null
+++ b/tests/auto/scxmlcoutput/path/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+if(WASM)
+ return()
+endif()
+
+qt_internal_add_test(tst_scxmlcoutput_path
+ SOURCES
+ tst_scxmlcoutput.cpp
+ LIBRARIES
+ Qt::Scxml
+)
+
+# This testcase tests setting the output path
+set(outputPath "${CMAKE_CURRENT_BINARY_DIR}/thechosenpath")
+# Create the path first relative to the build directory, otherwise the
+# code generation's output might fail on some platforms.
+file(MAKE_DIRECTORY "${outputPath}")
+
+qt6_add_statecharts(tst_scxmlcoutput_path
+ ../shared/ids1.scxml
+ OUTPUT_DIRECTORY "${outputPath}"
+)
diff --git a/tests/auto/scxmlcoutput/path/tst_scxmlcoutput.cpp b/tests/auto/scxmlcoutput/path/tst_scxmlcoutput.cpp
new file mode 100644
index 0000000..2c2c87e
--- /dev/null
+++ b/tests/auto/scxmlcoutput/path/tst_scxmlcoutput.cpp
@@ -0,0 +1,25 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include <QtTest>
+#include <QObject>
+
+// This testcase tests the cmake scxmlc generation, and the testcase itself
+// does mere basic compilation & statemachine instantiation; these fail
+// if qscxmlc generation does not work as expected
+#include "thechosenpath/ids1.h"
+
+class tst_scxmlcoutput: public QObject
+{
+ Q_OBJECT
+private slots:
+ void instantiates();
+};
+
+void tst_scxmlcoutput::instantiates() {
+ ids1 statemachine;
+ QVERIFY(statemachine.property("foo.bar").isValid());
+}
+
+QTEST_MAIN(tst_scxmlcoutput)
+#include "tst_scxmlcoutput.moc"
diff --git a/tests/auto/scxmlcoutput/shared/ids1.scxml b/tests/auto/scxmlcoutput/shared/ids1.scxml
new file mode 100644
index 0000000..dc08634
--- /dev/null
+++ b/tests/auto/scxmlcoutput/shared/ids1.scxml
@@ -0,0 +1,17 @@
+<?xml version="1.0" ?>
+<!--
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+-->
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="ids1">
+ <!-- valid IDs: -->
+ <state id="foo.bar"/>
+ <state id="foo-bar"/>
+ <state id="foo_bar"/>
+ <state id="_"/>
+ <state id="näl"/>
+ <state id="n_0xe4_l"/>
+ <state id="_VALID"/>
+ <state id="__valid"/>
+ <state id="q&#x00ff;&#x0300;i"/>
+</scxml>
diff --git a/tests/auto/shared/bindableqmlutils.h b/tests/auto/shared/bindableqmlutils.h
new file mode 100644
index 0000000..f58af01
--- /dev/null
+++ b/tests/auto/shared/bindableqmlutils.h
@@ -0,0 +1,116 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef BINDABLEQMLUTILS_H
+#define BINDABLEQMLUTILS_H
+
+#include <QtQml/QQmlListReference>
+#include <QtQml/QQmlProperty>
+#include <QObject>
+#include <QtTest>
+
+// This is a helper function to test basics of typical bindable
+// and manipulable QQmlListProperty
+// "TestedClass" is the class type we are testing
+// "TestedData" is the data type that the list contains
+// "testedClass" is an instance of the class we are interested testing
+// "data1" is a value that can be set and removed from the list
+// "data2" is a value that can be set and removed from the list
+// "propertyName" is the name of the property we are interested in testing
+template<typename TestedClass, typename TestedData>
+void testManipulableQmlListBasics(TestedClass& testedClass,
+ TestedData data1, TestedData data2,
+ const char* propertyName)
+{
+ // Get the property we are testing
+ const QMetaObject *metaObject = testedClass.metaObject();
+ QMetaProperty metaProperty = metaObject->property(metaObject->indexOfProperty(propertyName));
+
+ // Generate a string to help identify failures (as this is a generic template)
+ QString id(metaObject->className());
+ id.append(QStringLiteral("::"));
+ id.append(propertyName);
+
+ // Fail gracefully if preconditions to use this helper function are not met:
+ QQmlListReference listRef(&testedClass, propertyName);
+ QVERIFY2(metaProperty.isBindable(), qPrintable(id));
+ QVERIFY2(listRef.isManipulable(), qPrintable(id));
+ QVERIFY2(data1 != data2, qPrintable(id));
+
+ // Create a signal spy for the property changed -signal if such exists
+ QScopedPointer<QSignalSpy> changedSpy(nullptr);
+ if (metaProperty.hasNotifySignal())
+ changedSpy.reset(new QSignalSpy(&testedClass, metaProperty.notifySignal()));
+
+ // Modify values without binding (verify property basics still work)
+ QVERIFY2(listRef.count() == 0, qPrintable(id));
+ listRef.append(data1);
+ QVERIFY2(listRef.count() == 1, qPrintable(id));
+ if (changedSpy)
+ QVERIFY2(changedSpy->size() == 1, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+ QVERIFY2(listRef.at(0) == data1, qPrintable(id));
+ listRef.clear();
+ QVERIFY2(listRef.count() == 0, qPrintable(id));
+ if (changedSpy)
+ QVERIFY2(changedSpy->size() == 2, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+
+ // Bind to the property and verify that the bindings reflect the listproperty changes
+ QProperty<bool> data1InList([&](){
+ QQmlListReference list(&testedClass, propertyName);
+ for (int i = 0; i < list.count(); i++) {
+ if (list.at(i) == data1)
+ return true;
+ }
+ return false;
+ });
+ QProperty<bool> data2InList([&](){
+ QQmlListReference list(&testedClass, propertyName);
+ for (int i = 0; i < list.count(); i++) {
+ if (list.at(i) == data2)
+ return true;
+ }
+ return false;
+ });
+
+ // initial state
+ QVERIFY2(!data1InList, qPrintable(id));
+ QVERIFY2(!data2InList, qPrintable(id));
+
+ listRef.append(data1);
+ if (changedSpy)
+ QVERIFY2(changedSpy->size() == 3, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+ QVERIFY2(data1InList, qPrintable(id));
+ QVERIFY2(!data2InList, qPrintable(id));
+
+ listRef.append(data2);
+ if (changedSpy)
+ QVERIFY2(changedSpy->size() == 4, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+ QVERIFY2(data1InList, qPrintable(id));
+ QVERIFY2(data2InList, qPrintable(id));
+ QVERIFY2(listRef.count() == 2, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+
+ listRef.clear();
+ if (changedSpy)
+ QVERIFY2(changedSpy->size() == 5, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+ QVERIFY2(!data1InList, qPrintable(id));
+ QVERIFY2(!data2InList, qPrintable(id));
+ QVERIFY2(listRef.count() == 0, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+
+ listRef.append(data1);
+ listRef.replace(0, data2);
+ if (changedSpy)
+ QVERIFY2(changedSpy->size() == 7, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+ QVERIFY2(!data1InList, qPrintable(id));
+ QVERIFY2(data2InList, qPrintable(id));
+ QVERIFY2(listRef.count() == 1, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+
+ listRef.removeLast();
+ if (changedSpy)
+ QVERIFY2(changedSpy->size() == 8, qPrintable(id + ", actual: " + QString::number(changedSpy->size())));
+ QVERIFY2(!data1InList, qPrintable(id));
+ QVERIFY2(!data2InList, qPrintable(id));
+
+ QVERIFY2(listRef.count() == 0, qPrintable(id + ", actual: " + QString::number(listRef.count())));
+}
+
+#endif // BINDABLEQMLUTILS_H
diff --git a/tests/auto/shared/util.cpp b/tests/auto/shared/util.cpp
new file mode 100644
index 0000000..4808ba5
--- /dev/null
+++ b/tests/auto/shared/util.cpp
@@ -0,0 +1,78 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#include "util.h"
+
+#include <QtCore/QDebug>
+#include <QtCore/QMutexLocker>
+
+QQmlDataTest *QQmlDataTest::m_instance = 0;
+
+QQmlDataTest::QQmlDataTest() :
+#ifdef QT_TESTCASE_BUILDDIR
+ m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0, QT_TESTCASE_BUILDDIR)),
+#else
+ m_dataDirectory(QTest::qFindTestData("data", QT_QMLTEST_DATADIR, 0)),
+#endif
+
+ m_dataDirectoryUrl(m_dataDirectory.startsWith(QLatin1Char(':'))
+ ? QUrl(QLatin1String("qrc") + m_dataDirectory)
+ : QUrl::fromLocalFile(m_dataDirectory + QLatin1Char('/')))
+{
+ m_instance = this;
+}
+
+QQmlDataTest::~QQmlDataTest()
+{
+ m_instance = 0;
+}
+
+void QQmlDataTest::initTestCase()
+{
+ QVERIFY2(!m_dataDirectory.isEmpty(), "'data' directory not found");
+ m_directory = QFileInfo(m_dataDirectory).absolutePath();
+ if (m_dataDirectoryUrl.scheme() != QLatin1String("qrc"))
+ QVERIFY2(QDir::setCurrent(m_directory), qPrintable(QLatin1String("Could not chdir to ") + m_directory));
+}
+
+QString QQmlDataTest::testFile(const QString &fileName) const
+{
+ if (m_directory.isEmpty())
+ qFatal("QQmlDataTest::initTestCase() not called.");
+ QString result = m_dataDirectory;
+ result += QLatin1Char('/');
+ result += fileName;
+ return result;
+}
+
+Q_GLOBAL_STATIC(QMutex, qQmlTestMessageHandlerMutex)
+
+QQmlTestMessageHandler *QQmlTestMessageHandler::m_instance = 0;
+
+void QQmlTestMessageHandler::messageHandler(QtMsgType, const QMessageLogContext &context, const QString &message)
+{
+ QMutexLocker locker(qQmlTestMessageHandlerMutex());
+ if (QQmlTestMessageHandler::m_instance) {
+ if (QQmlTestMessageHandler::m_instance->m_includeCategories)
+ QQmlTestMessageHandler::m_instance->m_messages.push_back(QString("%1: %2").arg(context.category, message));
+ else
+ QQmlTestMessageHandler::m_instance->m_messages.push_back(message);
+ }
+}
+
+QQmlTestMessageHandler::QQmlTestMessageHandler()
+{
+ QMutexLocker locker(qQmlTestMessageHandlerMutex());
+ Q_ASSERT(!QQmlTestMessageHandler::m_instance);
+ QQmlTestMessageHandler::m_instance = this;
+ m_oldHandler = qInstallMessageHandler(messageHandler);
+ m_includeCategories = false;
+}
+
+QQmlTestMessageHandler::~QQmlTestMessageHandler()
+{
+ QMutexLocker locker(qQmlTestMessageHandlerMutex());
+ Q_ASSERT(QQmlTestMessageHandler::m_instance);
+ qInstallMessageHandler(m_oldHandler);
+ QQmlTestMessageHandler::m_instance = 0;
+}
diff --git a/tests/auto/shared/util.h b/tests/auto/shared/util.h
new file mode 100644
index 0000000..bf9e685
--- /dev/null
+++ b/tests/auto/shared/util.h
@@ -0,0 +1,75 @@
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
+
+#ifndef QQMLTESTUTILS_H
+#define QQMLTESTUTILS_H
+
+#include <QtCore/QDir>
+#include <QtCore/QUrl>
+#include <QtCore/QCoreApplication>
+#include <QtCore/QStringList>
+#include <QtTest/QTest>
+
+/* Base class for tests with data that are located in a "data" subfolder. */
+
+class QQmlDataTest : public QObject
+{
+ Q_OBJECT
+public:
+ QQmlDataTest();
+ virtual ~QQmlDataTest();
+
+ QString testFile(const QString &fileName) const;
+ inline QString testFile(const char *fileName) const
+ { return testFile(QLatin1String(fileName)); }
+ inline QUrl testFileUrl(const QString &fileName) const
+ {
+ const QString fn = testFile(fileName);
+ return fn.startsWith(QLatin1Char(':'))
+ ? QUrl(QLatin1String("qrc") + fn)
+ : QUrl::fromLocalFile(fn);
+ }
+ inline QUrl testFileUrl(const char *fileName) const
+ { return testFileUrl(QLatin1String(fileName)); }
+
+ inline QString dataDirectory() const { return m_dataDirectory; }
+ inline QUrl dataDirectoryUrl() const { return m_dataDirectoryUrl; }
+ inline QString directory() const { return m_directory; }
+
+ static inline QQmlDataTest *instance() { return m_instance; }
+
+public slots:
+ virtual void initTestCase();
+
+private:
+ static QQmlDataTest *m_instance;
+
+ const QString m_dataDirectory;
+ const QUrl m_dataDirectoryUrl;
+ QString m_directory;
+};
+
+class QQmlTestMessageHandler
+{
+ Q_DISABLE_COPY(QQmlTestMessageHandler)
+public:
+ QQmlTestMessageHandler();
+ ~QQmlTestMessageHandler();
+
+ const QStringList &messages() const { return m_messages; }
+ const QString messageString() const { return m_messages.join(QLatin1Char('\n')); }
+
+ void clear() { m_messages.clear(); }
+
+ void setIncludeCategoriesEnabled(bool enabled) { m_includeCategories = enabled; }
+
+private:
+ static void messageHandler(QtMsgType, const QMessageLogContext &context, const QString &message);
+
+ static QQmlTestMessageHandler *m_instance;
+ QStringList m_messages;
+ QtMessageHandler m_oldHandler;
+ bool m_includeCategories;
+};
+
+#endif // QQMLTESTUTILS_H
diff --git a/tests/auto/statemachine/CMakeLists.txt b/tests/auto/statemachine/CMakeLists.txt
new file mode 100644
index 0000000..3478bec
--- /dev/null
+++ b/tests/auto/statemachine/CMakeLists.txt
@@ -0,0 +1,53 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_statemachine Test:
+#####################################################################
+
+if(WASM)
+ return()
+endif()
+
+qt_internal_add_test(tst_statemachine
+ SOURCES
+ tst_statemachine.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::Scxml
+ Qt::CorePrivate
+ Qt::ScxmlPrivate
+ Qt::TestPrivate
+)
+
+# Resources:
+set(tst_statemachine_resource_files
+ "topmachine.scxml"
+ "submachineA.scxml"
+ "submachineB.scxml"
+ "emptylog.scxml"
+ "eventoccurred.scxml"
+ "historystate.scxml"
+ "ids1.scxml"
+ "invoke.scxml"
+ "multipleinvokableservices.scxml"
+ "stateDotDoneEvent.scxml"
+ "statenames.scxml"
+ "statenamesnested.scxml"
+)
+
+qt_internal_add_resource(tst_statemachine "tst_statemachine"
+ PREFIX
+ "/tst_statemachine"
+ FILES
+ ${tst_statemachine_resource_files}
+)
+
+qt6_add_statecharts(tst_statemachine
+ topmachine.scxml
+)
+
+#### Keys ignored in scope 1:.:.:statemachine.pro:<TRUE>:
+# TEMPLATE = "app"
diff --git a/tests/auto/statemachine/eventoccurred.scxml b/tests/auto/statemachine/eventoccurred.scxml
index c77da54..98a0870 100644
--- a/tests/auto/statemachine/eventoccurred.scxml
+++ b/tests/auto/statemachine/eventoccurred.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="EventOccurred" datamodel="ecmascript">
diff --git a/tests/auto/statemachine/ids1.scxml b/tests/auto/statemachine/ids1.scxml
index bcf00d6..4201aa8 100644
--- a/tests/auto/statemachine/ids1.scxml
+++ b/tests/auto/statemachine/ids1.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="ids1">
<state id="foo.bar"/>
diff --git a/tests/auto/statemachine/invoke.scxml b/tests/auto/statemachine/invoke.scxml
index 1bac47a..23523d7 100644
--- a/tests/auto/statemachine/invoke.scxml
+++ b/tests/auto/statemachine/invoke.scxml
@@ -1,54 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:BSD$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** BSD License Usage
-** Alternatively, you may use this file under the terms of the BSD license
-** as follows:
-**
-** "Redistribution and use in source and binary forms, with or without
-** modification, are permitted provided that the following conditions are
-** met:
-** * Redistributions of source code must retain the above copyright
-** notice, this list of conditions and the following disclaimer.
-** * Redistributions in binary form must reproduce the above copyright
-** notice, this list of conditions and the following disclaimer in
-** the documentation and/or other materials provided with the
-** distribution.
-** * Neither the name of The Qt Company Ltd nor the names of its
-** contributors may be used to endorse or promote products derived
-** from this software without specific prior written permission.
-**
-**
-** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml
xmlns="http://www.w3.org/2005/07/scxml"
diff --git a/tests/auto/statemachine/stateDotDoneEvent.scxml b/tests/auto/statemachine/stateDotDoneEvent.scxml
index 82e68ee..7b7ffb2 100644
--- a/tests/auto/statemachine/stateDotDoneEvent.scxml
+++ b/tests/auto/statemachine/stateDotDoneEvent.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="EventOccurred" datamodel="ecmascript">
diff --git a/tests/auto/statemachine/statemachine.pro b/tests/auto/statemachine/statemachine.pro
deleted file mode 100644
index 0e4de1a..0000000
--- a/tests/auto/statemachine/statemachine.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-QT = core gui qml testlib scxml-private
-CONFIG += testcase
-
-TARGET = tst_statemachine
-CONFIG += console
-CONFIG -= app_bundle
-
-TEMPLATE = app
-
-RESOURCES += tst_statemachine.qrc
-
-SOURCES += \
- tst_statemachine.cpp
diff --git a/tests/auto/statemachine/statenames.scxml b/tests/auto/statemachine/statenames.scxml
index 4cacd15..4e1ba72 100644
--- a/tests/auto/statemachine/statenames.scxml
+++ b/tests/auto/statemachine/statenames.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="statenames">
diff --git a/tests/auto/statemachine/statenamesnested.scxml b/tests/auto/statemachine/statenamesnested.scxml
index 64380e3..70df081 100644
--- a/tests/auto/statemachine/statenamesnested.scxml
+++ b/tests/auto/statemachine/statenamesnested.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0"
name="super_state_machine">
diff --git a/tests/auto/statemachine/submachineA.scxml b/tests/auto/statemachine/submachineA.scxml
new file mode 100644
index 0000000..0924b2e
--- /dev/null
+++ b/tests/auto/statemachine/submachineA.scxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="SubMachine">
+ <final id="topState"/>
+</scxml>
diff --git a/tests/auto/statemachine/submachineB.scxml b/tests/auto/statemachine/submachineB.scxml
new file mode 100644
index 0000000..0924b2e
--- /dev/null
+++ b/tests/auto/statemachine/submachineB.scxml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="SubMachine">
+ <final id="topState"/>
+</scxml>
diff --git a/tests/auto/statemachine/topmachine.scxml b/tests/auto/statemachine/topmachine.scxml
new file mode 100644
index 0000000..b733c6a
--- /dev/null
+++ b/tests/auto/statemachine/topmachine.scxml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="TopMachine" datamodel="ecmascript">
+ <datamodel>
+ <data id="doneCounter" expr="0"/>
+ </datamodel>
+ <state id="topState">
+ <invoke type="scxml" id="submachine.1" src="file:submachineA.scxml"/>
+ <invoke type="scxml" id="submachine.2" src="file:submachineA.scxml"/>
+ <invoke type="scxml" id="submachine.3" src="file:submachineB.scxml"/>
+ <transition event="done.invoke">
+ <assign location="doneCounter" expr="doneCounter + 1"/>
+ <if cond="doneCounter === 3">
+ <send event="goToFinal" delay="1s"/>
+ </if>
+ </transition>
+ <transition event="goToFinal" target="finalState"/>
+ </state>
+ <final id="finalState"/>
+</scxml>
diff --git a/tests/auto/statemachine/tst_statemachine.cpp b/tests/auto/statemachine/tst_statemachine.cpp
index 4bd4ef2..1aa74f7 100644
--- a/tests/auto/statemachine/tst_statemachine.cpp
+++ b/tests/auto/statemachine/tst_statemachine.cpp
@@ -1,38 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
+#include <QtTest/private/qpropertytesthelper_p.h>
#include <QObject>
#include <QXmlStreamReader>
#include <QtScxml/qscxmlcompiler.h>
#include <QtScxml/qscxmlstatemachine.h>
#include <QtScxml/qscxmlinvokableservice.h>
#include <QtScxml/private/qscxmlstatemachine_p.h>
+#include <QtScxml/QScxmlNullDataModel>
+
+#include "topmachine.h"
enum { SpyWaitTime = 8000 };
@@ -52,11 +31,16 @@ private Q_SLOTS:
void doneDotStateEvent();
void running();
+ void restart();
void invokeStateMachine();
void multipleInvokableServices(); // QTBUG-61484
void logWithoutExpr();
+
+ void bindings();
+
+ void setTableDataUpdatesObjectNames();
};
void tst_StateMachine::stateNames_data()
@@ -92,7 +76,7 @@ void tst_StateMachine::stateNames()
QScopedPointer<QScxmlStateMachine> stateMachine(QScxmlStateMachine::fromFile(scxmlFileName));
QVERIFY(!stateMachine.isNull());
- QCOMPARE(stateMachine->parseErrors().count(), 0);
+ QCOMPARE(stateMachine->parseErrors().size(), 0);
QCOMPARE(stateMachine->stateNames(compressed), expectedStates);
}
@@ -383,7 +367,7 @@ void tst_StateMachine::doneDotStateEvent()
stateMachine->start();
finishedSpy.wait(5000);
- QCOMPARE(finishedSpy.count(), 1);
+ QCOMPARE(finishedSpy.size(), 1);
QCOMPARE(stateMachine->activeStateNames(true).size(), 1);
QVERIFY(stateMachine->activeStateNames(true).contains(QLatin1String("success")));
}
@@ -400,15 +384,33 @@ void tst_StateMachine::running()
stateMachine->start();
- QCOMPARE(runningChangedSpy.count(), 1);
+ QCOMPARE(runningChangedSpy.size(), 1);
QCOMPARE(stateMachine->isRunning(), true);
stateMachine->stop();
- QCOMPARE(runningChangedSpy.count(), 2);
+ QCOMPARE(runningChangedSpy.size(), 2);
QCOMPARE(stateMachine->isRunning(), false);
}
+void tst_StateMachine::restart()
+{
+ QScopedPointer<QScxmlStateMachine> stateMachine(
+ QScxmlStateMachine::fromFile(QString(":/tst_statemachine/stateDotDoneEvent.scxml")));
+ QVERIFY(!stateMachine.isNull());
+
+ QSignalSpy finishedSpy(stateMachine.data(), SIGNAL(finished()));
+
+ stateMachine->start();
+ finishedSpy.wait(5000);
+ QCOMPARE(finishedSpy.size(), 1);
+ QCOMPARE(stateMachine->activeStateNames(true).size(), 1);
+ QVERIFY(stateMachine->activeStateNames(true).contains(QLatin1String("success")));
+
+ QTest::ignoreMessage(QtWarningMsg, QRegularExpression("(.*)Can't start finished machine"));
+ stateMachine->start();
+}
+
void tst_StateMachine::invokeStateMachine()
{
QScopedPointer<QScxmlStateMachine> stateMachine(
@@ -419,8 +421,8 @@ void tst_StateMachine::invokeStateMachine()
QCOMPARE(stateMachine->isRunning(), true);
QTRY_VERIFY(stateMachine->activeStateNames().contains(QString("anyplace")));
- QVector<QScxmlInvokableService *> services = stateMachine->invokedServices();
- QCOMPARE(services.length(), 1);
+ QList<QScxmlInvokableService *> services = stateMachine->invokedServices();
+ QCOMPARE(services.size(), 1);
QVariant subMachineVariant = services[0]->property("stateMachine");
QVERIFY(subMachineVariant.isValid());
QScxmlStateMachine *subMachine = qvariant_cast<QScxmlStateMachine *>(subMachineVariant);
@@ -439,7 +441,7 @@ void tst_StateMachine::multipleInvokableServices()
QCOMPARE(stateMachine->isRunning(), true);
finishedSpy.wait(5000);
- QCOMPARE(finishedSpy.count(), 1);
+ QCOMPARE(finishedSpy.size(), 1);
QCOMPARE(stateMachine->activeStateNames(true).size(), 1);
QVERIFY(stateMachine->activeStateNames(true).contains(QLatin1String("success")));
}
@@ -452,11 +454,146 @@ void tst_StateMachine::logWithoutExpr()
QTest::ignoreMessage(QtDebugMsg, "\"Hi2\" : \"\"");
stateMachine->start();
QSignalSpy logSpy(stateMachine.data(), SIGNAL(log(QString,QString)));
- QTRY_COMPARE(logSpy.count(), 1);
+ QTRY_COMPARE(logSpy.size(), 1);
}
-QTEST_MAIN(tst_StateMachine)
+void tst_StateMachine::bindings()
+{
+ // -- QScxmlStateMachine::initialized
+ std::unique_ptr<QScxmlStateMachine> stateMachine1(
+ QScxmlStateMachine::fromFile(QString(":/tst_statemachine/invoke.scxml")));
+ QVERIFY(stateMachine1.get());
+ QTestPrivate::testReadOnlyPropertyBasics<QScxmlStateMachine, bool>(
+ *stateMachine1, false, true, "initialized", [&](){ stateMachine1.get()->start(); });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QScxmlStateMachine::initialized bindable test failed.";
+ return;
+ }
-#include "tst_statemachine.moc"
+ using StateMachinePtr = std::unique_ptr<QScxmlStateMachine>;
+
+ // -- QScxmlStateMachine::initialValues
+ QVariantMap map1{{"map", 1}};
+ QVariantMap map2{{"map", 2}};
+ QTestPrivate::testReadWritePropertyBasics<QScxmlStateMachine, QVariantMap>(
+ *stateMachine1, map1, map2, "initialValues",
+ []() {
+ return StateMachinePtr{QScxmlStateMachine::fromFile(QString("not_a_real_file"))};
+ });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QScxmlStateMachine::initialValues bindable test failed.";
+ return;
+ }
+
+ // -- QScxmlStateMachine::loader
+ class MockLoader: public QScxmlCompiler::Loader
+ {
+ public:
+ QByteArray load(const QString&, const QString&, QStringList*) override { return QByteArray(); }
+ };
+ MockLoader loader1;
+ MockLoader loader2;
+ QTestPrivate::testReadWritePropertyBasics<QScxmlStateMachine, QScxmlCompiler::Loader*>(
+ *stateMachine1, &loader1, &loader2, "loader",
+ []() {
+ return StateMachinePtr{QScxmlStateMachine::fromFile(QString("not_a_real_file"))};
+ });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QScxmlStateMachine::loader bindable test failed.";
+ return;
+ }
+
+ // -- QScxmlStateMachine::dataModel
+ // Use non-existent file below, as valid file would initialize the model
+ std::unique_ptr<QScxmlStateMachine> stateMachine2(
+ QScxmlStateMachine::fromFile(QString("not_a_real_file")));
+ std::unique_ptr<QScxmlStateMachine> stateMachine3(
+ QScxmlStateMachine::fromFile(QString("not_a_real_file")));
+ QScxmlNullDataModel model1;
+ // data can only change once
+ QTestPrivate::testWriteOncePropertyBasics<QScxmlStateMachine, QScxmlDataModel*>(
+ *stateMachine2, nullptr, &model1, "dataModel", true,
+ []() {
+ return StateMachinePtr{QScxmlStateMachine::fromFile(QString("not_a_real_file"))};
+ });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QScxmlStateMachine::dataModel bindable test failed.";
+ return;
+ }
+ // -- QScxmlStateMachine::tableData
+ // Use the statemachine to generate the tableData for testing
+ std::unique_ptr<QScxmlStateMachine> stateMachine4(
+ QScxmlStateMachine::fromFile(QString(":/tst_statemachine/invoke.scxml")));
+ QTestPrivate::testReadWritePropertyBasics<QScxmlStateMachine, QScxmlTableData*>(
+ *stateMachine2, stateMachine1.get()->tableData(), stateMachine4.get()->tableData(),
+ "tableData",
+ []() {
+ return StateMachinePtr{QScxmlStateMachine::fromFile(QString("not_a_real_file"))};
+ });
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QScxmlStateMachine::tableData bindable test failed.";
+ return;
+ }
+ // -- QScxmlStateMachine::invokedServices
+ // Test executes statemachine and observes as the invoked services change
+ TopMachine topSm;
+ QSignalSpy invokedSpy(&topSm, SIGNAL(invokedServicesChanged(const QList<QScxmlInvokableService *>)));
+ QCOMPARE(topSm.invokedServices().size(), 0);
+ // at some point during the topSm execution there are 3 invoked services
+ topSm.start();
+ QTRY_COMPARE(topSm.invokedServices().size(), 3);
+ QCOMPARE(invokedSpy.size(), 1);
+ // after completion invoked services drop back to 0
+ QTRY_COMPARE(topSm.isRunning(), false);
+ QCOMPARE(topSm.invokedServices().size(), 0);
+ QCOMPARE(invokedSpy.size(), 2);
+ // bind *to* the invokedservices property and check that we observe same changes
+ // during the topSm execution
+ QProperty<qsizetype> invokedServicesObserver;
+ invokedServicesObserver.setBinding([&](){ return topSm.invokedServices().size(); });
+ QCOMPARE(invokedServicesObserver, 0);
+ topSm.start();
+ QTRY_COMPARE(invokedServicesObserver, 3);
+ QCOMPARE(topSm.invokedServices().size(), 3);
+ QCOMPARE(invokedSpy.size(), 3);
+
+ // -- QScxmlDataModel::stateMachine
+ QScxmlNullDataModel dataModel1;
+ std::unique_ptr<QScxmlStateMachine> stateMachine5(
+ QScxmlStateMachine::fromFile(QString("not_a_real_file")));
+ // data can only change once
+ QTestPrivate::testWriteOncePropertyBasics<QScxmlNullDataModel, QScxmlStateMachine*>(
+ dataModel1, nullptr, stateMachine5.get(), "stateMachine");
+ if (QTest::currentTestFailed()) {
+ qWarning() << "QScxmlDataModel::stateMachine bindable test failed.";
+ return;
+ }
+}
+
+void tst_StateMachine::setTableDataUpdatesObjectNames()
+{
+ std::unique_ptr<QScxmlStateMachine> stateMachine1(
+ QScxmlStateMachine::fromFile(QString(":/tst_statemachine/emptylog.scxml")));
+ const QString sm1ObjectName = stateMachine1->objectName();
+ std::unique_ptr<QScxmlStateMachine> stateMachine2(
+ QScxmlStateMachine::fromFile(QString(":/tst_statemachine/eventoccurred.scxml")));
+ const QString sm2ObjectName = stateMachine2->objectName();
+ QCOMPARE_NE(sm1ObjectName, sm2ObjectName);
+
+ std::unique_ptr<QScxmlStateMachine> sm(
+ QScxmlStateMachine::fromFile(QString("not_a_real_file")));
+ QVERIFY(sm->objectName().isEmpty());
+ // no name set, so update object name
+ sm->setTableData(stateMachine1->tableData());
+ QCOMPARE_EQ(sm->objectName(), sm1ObjectName);
+
+ // object name already set, so do not update
+ sm->setTableData(stateMachine2->tableData());
+ QCOMPARE_EQ(sm->objectName(), sm1ObjectName); // did not change
+}
+
+QTEST_MAIN(tst_StateMachine)
+
+#include "tst_statemachine.moc"
diff --git a/tests/auto/statemachine/tst_statemachine.qrc b/tests/auto/statemachine/tst_statemachine.qrc
deleted file mode 100644
index 599ba29..0000000
--- a/tests/auto/statemachine/tst_statemachine.qrc
+++ /dev/null
@@ -1,13 +0,0 @@
-<RCC>
- <qresource prefix="/tst_statemachine">
- <file>eventoccurred.scxml</file>
- <file>statenames.scxml</file>
- <file>statenamesnested.scxml</file>
- <file>ids1.scxml</file>
- <file>stateDotDoneEvent.scxml</file>
- <file>invoke.scxml</file>
- <file>historystate.scxml</file>
- <file>multipleinvokableservices.scxml</file>
- <file>emptylog.scxml</file>
- </qresource>
-</RCC>
diff --git a/tests/auto/statemachineinfo/CMakeLists.txt b/tests/auto/statemachineinfo/CMakeLists.txt
new file mode 100644
index 0000000..85d9fc8
--- /dev/null
+++ b/tests/auto/statemachineinfo/CMakeLists.txt
@@ -0,0 +1,32 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+#####################################################################
+## tst_statemachineinfo Test:
+#####################################################################
+
+qt_internal_add_test(tst_statemachineinfo
+ SOURCES
+ tst_statemachineinfo.cpp
+ LIBRARIES
+ Qt::Gui
+ Qt::Qml
+ Qt::ScxmlPrivate
+)
+
+# Resources:
+set(tst_statemachineinfo_resource_files
+ "statemachine.scxml"
+)
+
+qt_internal_add_resource(tst_statemachineinfo "tst_statemachineinfo"
+ PREFIX
+ "/tst_statemachineinfo"
+ FILES
+ ${tst_statemachineinfo_resource_files}
+)
+
+
+#### Keys ignored in scope 1:.:.:statemachineinfo.pro:<TRUE>:
+# TEMPLATE = "app"
diff --git a/tests/auto/statemachineinfo/statemachine.scxml b/tests/auto/statemachineinfo/statemachine.scxml
index 94f06b1..766f278 100644
--- a/tests/auto/statemachineinfo/statemachine.scxml
+++ b/tests/auto/statemachineinfo/statemachine.scxml
@@ -1,32 +1,7 @@
<?xml version="1.0" ?>
<!--
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
-->
<scxml xmlns="http://www.w3.org/2005/07/scxml" version="1.0" name="InfoTest">
<transition/>
diff --git a/tests/auto/statemachineinfo/statemachineinfo.pro b/tests/auto/statemachineinfo/statemachineinfo.pro
deleted file mode 100644
index f8d5b5d..0000000
--- a/tests/auto/statemachineinfo/statemachineinfo.pro
+++ /dev/null
@@ -1,13 +0,0 @@
-QT = core gui qml testlib scxml-private
-CONFIG += testcase
-
-TARGET = tst_statemachineinfo
-CONFIG += console
-CONFIG -= app_bundle
-
-TEMPLATE = app
-
-RESOURCES += tst_statemachineinfo.qrc
-
-SOURCES += \
- tst_statemachineinfo.cpp
diff --git a/tests/auto/statemachineinfo/tst_statemachineinfo.cpp b/tests/auto/statemachineinfo/tst_statemachineinfo.cpp
index a87e807..7246045 100644
--- a/tests/auto/statemachineinfo/tst_statemachineinfo.cpp
+++ b/tests/auto/statemachineinfo/tst_statemachineinfo.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest>
#include <QtScxml/qscxmlstatemachine.h>
@@ -55,13 +30,13 @@ public:
}
public slots:
- void statesEntered(const QVector<QScxmlStateMachineInfo::StateId> &states)
+ void statesEntered(const QList<QScxmlStateMachineInfo::StateId> &states)
{ entered = states; ++enterCount; }
- void statesExited(const QVector<QScxmlStateMachineInfo::StateId> &states)
+ void statesExited(const QList<QScxmlStateMachineInfo::StateId> &states)
{ exited = states; ++exitCount; }
- void transitionsTriggered(const QVector<QScxmlStateMachineInfo::TransitionId> &transitions)
+ void transitionsTriggered(const QList<QScxmlStateMachineInfo::TransitionId> &transitions)
{ this->transitions = transitions; ++transitionTriggerCount; }
void reachedStableState()
@@ -79,11 +54,11 @@ public slots:
public:
int enterCount = 0;
- QVector<QScxmlStateMachineInfo::StateId> entered;
+ QList<QScxmlStateMachineInfo::StateId> entered;
int exitCount = 0;
- QVector<QScxmlStateMachineInfo::StateId> exited;
+ QList<QScxmlStateMachineInfo::StateId> exited;
int transitionTriggerCount = 0;
- QVector<QScxmlStateMachineInfo::TransitionId> transitions;
+ QList<QScxmlStateMachineInfo::TransitionId> transitions;
bool macroStepDone = false;
};
@@ -121,12 +96,12 @@ void tst_StateMachineInfo::checkInfo()
QCOMPARE(info->stateType(states.at(4)), QScxmlStateMachineInfo::FinalState);
QCOMPARE(info->stateChildren(QScxmlStateMachineInfo::InvalidStateId),
- QVector<int>() << 0 << 1 << 4);
- QCOMPARE(info->stateChildren(states.at(0)), QVector<int>());
- QCOMPARE(info->stateChildren(states.at(1)), QVector<int>() << 2 << 3);
- QCOMPARE(info->stateChildren(states.at(2)), QVector<int>());
- QCOMPARE(info->stateChildren(states.at(3)), QVector<int>());
- QCOMPARE(info->stateChildren(states.at(4)), QVector<int>());
+ QList<int>() << 0 << 1 << 4);
+ QCOMPARE(info->stateChildren(states.at(0)), QList<int>());
+ QCOMPARE(info->stateChildren(states.at(1)), QList<int>() << 2 << 3);
+ QCOMPARE(info->stateChildren(states.at(2)), QList<int>());
+ QCOMPARE(info->stateChildren(states.at(3)), QList<int>());
+ QCOMPARE(info->stateChildren(states.at(4)), QList<int>());
QCOMPARE(info->initialTransition(QScxmlStateMachineInfo::InvalidStateId), 4);
QCOMPARE(info->initialTransition(states.at(0)), static_cast<int>(QScxmlStateMachineInfo::InvalidTransitionId));
@@ -199,7 +174,7 @@ void tst_StateMachineInfo::checkInfo()
stateMachine->start();
QVERIFY(recorder.finishMacroStep());
QCOMPARE(recorder.enterCount, 1);
- QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 0);
+ QCOMPARE(recorder.entered, QList<QScxmlStateMachineInfo::StateId>() << 0);
QVERIFY(recorder.exited.isEmpty());
recorder.clear();
@@ -209,10 +184,10 @@ void tst_StateMachineInfo::checkInfo()
stateMachine->submitEvent("step");
QVERIFY(recorder.finishMacroStep());
QCOMPARE(recorder.enterCount, 1);
- QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 1 << 2 << 3);
- QCOMPARE(recorder.exited, QVector<QScxmlStateMachineInfo::StateId>() << 0);
+ QCOMPARE(recorder.entered, QList<QScxmlStateMachineInfo::StateId>() << 1 << 2 << 3);
+ QCOMPARE(recorder.exited, QList<QScxmlStateMachineInfo::StateId>() << 0);
QCOMPARE(recorder.transitionTriggerCount, 1);
- QCOMPARE(recorder.transitions, QVector<QScxmlStateMachineInfo::TransitionId>() << 1);
+ QCOMPARE(recorder.transitions, QList<QScxmlStateMachineInfo::TransitionId>() << 1);
recorder.clear();
@@ -221,10 +196,10 @@ void tst_StateMachineInfo::checkInfo()
stateMachine->submitEvent("step");
QVERIFY(recorder.finishMacroStep());
QCOMPARE(recorder.enterCount, 1);
- QCOMPARE(recorder.entered, QVector<QScxmlStateMachineInfo::StateId>() << 4);
- QCOMPARE(recorder.exited, QVector<QScxmlStateMachineInfo::StateId>() << 3 << 2 << 1);
+ QCOMPARE(recorder.entered, QList<QScxmlStateMachineInfo::StateId>() << 4);
+ QCOMPARE(recorder.exited, QList<QScxmlStateMachineInfo::StateId>() << 3 << 2 << 1);
QCOMPARE(recorder.transitionTriggerCount, 1);
- QCOMPARE(recorder.transitions, QVector<QScxmlStateMachineInfo::TransitionId>() << 2);
+ QCOMPARE(recorder.transitions, QList<QScxmlStateMachineInfo::TransitionId>() << 2);
}
diff --git a/tests/auto/statemachineinfo/tst_statemachineinfo.qrc b/tests/auto/statemachineinfo/tst_statemachineinfo.qrc
deleted file mode 100644
index 0e464d4..0000000
--- a/tests/auto/statemachineinfo/tst_statemachineinfo.qrc
+++ /dev/null
@@ -1,5 +0,0 @@
-<RCC>
- <qresource prefix="/tst_statemachineinfo">
- <file>statemachine.scxml</file>
- </qresource>
-</RCC>
diff --git a/tests/manual/cppgen/CMakeLists.txt b/tests/manual/cppgen/CMakeLists.txt
new file mode 100644
index 0000000..b0f0a86
--- /dev/null
+++ b/tests/manual/cppgen/CMakeLists.txt
@@ -0,0 +1,21 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## tst_cppgen Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name tst_cppgen)
+qt_internal_add_tool(${target_name}
+ SOURCES
+ tst_cppgen.cpp
+ LIBRARIES
+ Qt::Qml
+ Qt::Scxml
+ Qt::Test
+)
+qt_internal_return_unless_building_tools()
+
+#### Keys ignored in scope 1:.:.:cppgen.pro:<TRUE>:
+# TEMPLATE = "app"
+# _OPTION = "host_build"
diff --git a/tests/manual/cppgen/cppgen.pro b/tests/manual/cppgen/cppgen.pro
deleted file mode 100644
index ea57732..0000000
--- a/tests/manual/cppgen/cppgen.pro
+++ /dev/null
@@ -1,18 +0,0 @@
-option(host_build)
-
-QT += testlib scxml
-CONFIG += testcase
-
-QT += core qml
-QT -= gui
-
-TARGET = tst_cppgen
-CONFIG += console c++11
-CONFIG -= app_bundle
-
-TEMPLATE = app
-
-SOURCES += \
- tst_cppgen.cpp
-
-load(qt_tool)
diff --git a/tests/manual/cppgen/tst_cppgen.cpp b/tests/manual/cppgen/tst_cppgen.cpp
index 298a6ab..9aaf0b8 100644
--- a/tests/manual/cppgen/tst_cppgen.cpp
+++ b/tests/manual/cppgen/tst_cppgen.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/QtTest>
diff --git a/tests/manual/testCpp/CMakeLists.txt b/tests/manual/testCpp/CMakeLists.txt
new file mode 100644
index 0000000..8336094
--- /dev/null
+++ b/tests/manual/testCpp/CMakeLists.txt
@@ -0,0 +1,24 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## testCpp Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name testCpp)
+qt_internal_add_tool(${target_name}
+ SOURCES
+ testcpp.cpp
+ LIBRARIES
+ Qt::Qml
+ Qt::Scxml
+)
+qt_internal_return_unless_building_tools()
+
+# Statecharts:
+qt6_add_statecharts(testCpp out.scxml)
+
+#### Keys ignored in scope 1:.:.:testCpp.pro:<TRUE>:
+# OTHER_FILES = "genTestSxcml.py" "out.scxml"
+# TEMPLATE = "app"
+# _OPTION = "host_build"
diff --git a/tests/manual/testCpp/genTestSxcml.py b/tests/manual/testCpp/genTestSxcml.py
index 9170212..380516b 100644
--- a/tests/manual/testCpp/genTestSxcml.py
+++ b/tests/manual/testCpp/genTestSxcml.py
@@ -1,28 +1,5 @@
# Copyright (C) 2016 The Qt Company Ltd.
-# Contact: https://www.qt.io/licensing/
-#
-# This file is part of the QtScxml module of the Qt Toolkit.
-#
-# $QT_BEGIN_LICENSE:GPL-EXCEPT$
-# Commercial License Usage
-# Licensees holding valid commercial Qt licenses may use this file in
-# accordance with the commercial license agreement provided with the
-# Software or, alternatively, in accordance with the terms contained in
-# a written agreement between you and The Qt Company. For licensing terms
-# and conditions see https://www.qt.io/terms-conditions. For further
-# information use the contact form at https://www.qt.io/contact-us.
-#
-# GNU General Public License Usage
-# Alternatively, this file may be used under the terms of the GNU
-# General Public License version 3 as published by the Free Software
-# Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-# included in the packaging of this file. Please review the following
-# information to ensure the GNU General Public License requirements will
-# be met: https://www.gnu.org/licenses/gpl-3.0.html.
-#
-# $QT_END_LICENSE$
-
-import random
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
"""stupid generator of a large scxml state machine"""
nStatesMax=10
depth=5
diff --git a/tests/manual/testCpp/testCpp.pro b/tests/manual/testCpp/testCpp.pro
deleted file mode 100644
index 25c27b4..0000000
--- a/tests/manual/testCpp/testCpp.pro
+++ /dev/null
@@ -1,19 +0,0 @@
-option(host_build)
-
-QT += core qml scxml
-QT -= gui
-
-TARGET = testCpp
-CONFIG += console c++11
-CONFIG -= app_bundle
-
-TEMPLATE = app
-
-SOURCES += \
- testcpp.cpp
-
-OTHER_FILES += genTestSxcml.py out.scxml
-STATECHARTS = out.scxml
-
-load(qt_tool)
-load(qscxmlcpp)
diff --git a/tests/manual/testCpp/testcpp.cpp b/tests/manual/testCpp/testcpp.cpp
index 2da6529..368ee03 100644
--- a/tests/manual/testCpp/testcpp.cpp
+++ b/tests/manual/testCpp/testcpp.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QCoreApplication>
#include <QFile>
diff --git a/tests/tests.pro b/tests/tests.pro
deleted file mode 100644
index d1b99ec..0000000
--- a/tests/tests.pro
+++ /dev/null
@@ -1,4 +0,0 @@
-TEMPLATE = subdirs
-CONFIG += no_docs_target
-
-SUBDIRS += auto
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000..c5831a4
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,7 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+
+if(QT_FEATURE_commandlineparser)
+ add_subdirectory(qscxmlc)
+endif()
diff --git a/tools/qscxmlc/CMakeLists.txt b/tools/qscxmlc/CMakeLists.txt
new file mode 100644
index 0000000..98b7b3e
--- /dev/null
+++ b/tools/qscxmlc/CMakeLists.txt
@@ -0,0 +1,64 @@
+# Copyright (C) 2022 The Qt Company Ltd.
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## qscxmlc Tool:
+#####################################################################
+
+qt_internal_include_in_repo_target_set(qtscxml)
+
+qt_get_tool_target_name(target_name qscxmlc)
+qt_internal_add_tool(${target_name}
+ TOOLS_TARGET Scxml
+ INSTALL_DIR "${INSTALL_LIBEXECDIR}"
+ SOURCES
+ ../../src/scxml/qscxmlcompiler.cpp ../../src/scxml/qscxmlcompiler.h ../../src/scxml/qscxmlcompiler_p.h
+ ../../src/scxml/qscxmlerror.cpp ../../src/scxml/qscxmlerror.h
+ ../../src/scxml/qscxmlexecutablecontent.cpp ../../src/scxml/qscxmlexecutablecontent.h ../../src/scxml/qscxmlexecutablecontent_p.h
+ ../../src/scxml/qscxmlglobals.h
+ ../../src/scxml/qscxmltabledata.cpp ../../src/scxml/qscxmltabledata.h
+ generator.cpp generator.h
+ main.cpp
+ moc.cpp moc.h
+ outputrevision.h
+ qscxmlc.cpp qscxmlc.h
+ scxmlcppdumper.cpp scxmlcppdumper.h
+ utils.h
+ DEFINES
+ BUILD_QSCXMLC
+ QT_NO_CAST_FROM_ASCII
+ QT_NO_CAST_TO_ASCII
+ INCLUDE_DIRECTORIES
+ $<TARGET_PROPERTY:Qt::Scxml,INTERFACE_INCLUDE_DIRECTORIES>
+ $<TARGET_PROPERTY:Qt::ScxmlPrivate,INTERFACE_INCLUDE_DIRECTORIES>
+ LIBRARIES
+ Qt::CorePrivate
+)
+qt_internal_return_unless_building_tools()
+
+# qscxmlc uses header and source files from Scxml library instead of linking it. These sources use
+# includes from the module(using the module include style) so need to sync the module header
+# files first to avoid compiler errors.
+add_dependencies(${target_name} Scxml_sync_headers)
+
+set_property(SOURCE ../../src/scxml/qscxmlerror.h PROPERTY SKIP_AUTOMOC ON)
+set_property(SOURCE ../../src/scxml/qscxmlcompiler.cpp PROPERTY SKIP_AUTOMOC ON)
+
+# Resources:
+set(templates_resource_files
+ "cppdatamodel.t"
+ "data.t"
+ "decl.t"
+)
+
+qt_internal_add_resource(${target_name} "templates"
+ PREFIX
+ "/"
+ FILES
+ ${templates_resource_files}
+ OPTIONS --no-compress
+)
+
+
+#### Keys ignored in scope 1:.:.:qscxmlc.pro:<TRUE>:
+# _OPTION = "host_build"
diff --git a/tools/qscxmlc/data.t b/tools/qscxmlc/data.t
index 3926507..1b1e6f7 100644
--- a/tools/qscxmlc/data.t
+++ b/tools/qscxmlc/data.t
@@ -33,7 +33,9 @@ struct ${classname}::Data: private QScxmlTableData {
{
Q_ASSERT(id >= QScxmlExecutableContent::NoString); Q_ASSERT(id < ${stringCount});
if (id == QScxmlExecutableContent::NoString) return QString();
- return QString({static_cast<QStringData*>(strings.data + id)});
+ const auto dataOffset = strings.offsetsAndSize[id * 2];
+ const auto dataSize = strings.offsetsAndSize[id * 2 + 1];
+ return QString::fromRawData(reinterpret_cast<const QChar*>(&strings.stringdata[dataOffset]), dataSize);
}
const qint32 *stateMachineTable() const override final
@@ -82,8 +84,8 @@ struct ${classname}::Data: private QScxmlTableData {
static QScxmlExecutableContent::ForeachInfo foreaches[];
static const qint32 theStateMachineTable[];
static struct Strings {
- QArrayData data[${stringCount}];
- qunicodechar stringdata[${stringdataSize}];
+ const uint offsetsAndSize[${stringCount} * 2];
+ char16_t stringdata[${stringdataSize}];
} strings;
};
@@ -120,10 +122,6 @@ QScxmlExecutableContent::ForeachInfo ${classname}::Data::foreaches[] = {
${foreaches}
};
-#define STR_LIT(idx, ofs, len) \
- Q_STATIC_STRING_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
- qptrdiff(offsetof(Strings, stringdata) + ofs * sizeof(qunicodechar) - idx * sizeof(QArrayData)) \
- )
${classname}::Data::Strings ${classname}::Data::strings = {{
${strLits}
},{
diff --git a/tools/qscxmlc/doc/qscxmlc.qdoc b/tools/qscxmlc/doc/qscxmlc.qdoc
index bf38a63..7e46b8a 100644
--- a/tools/qscxmlc/doc/qscxmlc.qdoc
+++ b/tools/qscxmlc/doc/qscxmlc.qdoc
@@ -1,29 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the documentation of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:FDL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Free Documentation License Usage
-** Alternatively, this file may be used under the terms of the GNU Free
-** Documentation License version 1.3 as published by the Free Software
-** Foundation and appearing in the file included in the packaging of
-** this file. Please review the following information to ensure
-** the GNU Free Documentation License version 1.3 requirements
-** will be met: https://www.gnu.org/licenses/fdl-1.3.html.
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
/*!
\page qscxmlc.html
@@ -39,27 +15,41 @@
\section1 Usage
- The \c qscxml tool is invoked automatically if the QT variable in the
- project file includes \c scxml, and the .scxml file to use is specified
- using the \c STATECHARTS variable.
+ The \c qscxmlc tool is invoked automatically if the project is linked against
+ the \c scxml library in the project file, and the \c .scxml file to use is specified
+ using the special build directives \c STATECHARTS or qt6_add_statecharts.
- \badcode
- QT += scxml
- STATECHARTS = MyStatemachine.scxml
- \endcode
+ When using cmake:
+
+ \include qtscxml-module-use.qdocinc cmakebuild
+ \include qtscxml-module-use.qdocinc cmakestatecharts
+
+ When using qmake:
+
+ \include qtscxml-module-use.qdocinc qmakebuild
+ \include qtscxml-module-use.qdocinc qmakestatecharts
- With above definitions, \c qmake invokes \c qscxmlc to generate
- MyStatemachine.h and MyStatemachine.cpp, and adds them to \l [QMake]
- HEADERS and \l [QMAKE] SOURCES variables.
+ With above definitions, \c qmake or \c cmake invokes \c qscxmlc to generate
+ MyStatemachine.h and MyStatemachine.cpp, and adds them appropriately
+ to the project as headers and sources.
By default, the name of the generated class that implements the state
machine corresponds with the \e name attribute of the \c <scxml> root
element.
+ The \c qscxmlc tool can also be invoked manually and the resulting header and
+ source files can be used as regular source files in a project. When
+ using these source files as part of a \c cmake project, one must
+ additionally disable automatic moc in the CMakeLists.txt file as
+ illustrated by this example:
+ \code
+ set_source_files_properties(statemachine.h PROPERTIES SKIP_AUTOMOC TRUE)
+ \endcode
+ If you omit this, you will see duplicate symbol errors during compilation.
+
\section1 Command-Line Options
- The \c qscxmlc tool supports the following command-line options, which can be specified using
- the \c QSCXMLC_ARGUMENTS variable in the project file:
+ The \c qscxmlc tool supports the following command-line options:
\table
\header
@@ -67,8 +57,7 @@
\li Description
\row
\li \c {--namespace <namespace>}
- \li Put the generated class(es) in the specified namespace. You can use the
- \c QSCXMLC_NAMESPACE variable to specify this in your project file.
+ \li Put the generated class(es) in the specified namespace.
\row
\li \c {-o <base/out/name>}
\li The base name of the output files. This can include a path. If none is specified, the
@@ -90,4 +79,25 @@
state changes with plain QObject::connect() and directly call a method to find out if
a state is currently active.
\endtable
+
+ The \c qmake and \c CMake project files support the following options:
+
+ \table
+ \header
+ \li Option
+ \li Description
+ \row
+ \li \c {QSCXMLC_DIR|OUTPUT_DIRECTORY <directory>}
+ \li \c QSCXMLC_DIR (qmake) or \c OUTPUT_DIRECTORY (cmake) specifies the directory for
+ the output files. OUTPUT_DIR (cmake) has been deprecated.
+ \row
+ \li \c {QSCXMLC_NAMESPACE|NAMESPACE <namespace>}
+ \li \c QSCXMLC_NAMESPACE (qmake) or \c NAMESPACE (cmake) specifies the namespace for the
+ generated classes.
+ \row
+ \li \c {QSCXMLC_ARGUMENTS|OPTIONS <options>}
+ \li \c QSCXMLC_ARGUMENTS (qmake) or \c OPTIONS (cmake) allows specifying additional
+ options for the \c qscxmlc compiler. QSCXMLC_ARGUMENTS with cmake has been
+ deprecated.
+ \endtable
*/
diff --git a/tools/qscxmlc/generator.cpp b/tools/qscxmlc/generator.cpp
index 6febf09..4586c1d 100644
--- a/tools/qscxmlc/generator.cpp
+++ b/tools/qscxmlc/generator.cpp
@@ -1,32 +1,12 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2020 The Qt Company Ltd.
+// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
+// Copyright (C) 2018 Intel Corporation.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "generator.h"
+#if 0 // -- QtScxml
+#include "cbordevice.h"
+#endif // -- QtScxml
#include "outputrevision.h"
#include "utils.h"
#include <QtCore/qmetatype.h>
@@ -35,12 +15,19 @@
#include <QtCore/qjsonvalue.h>
#include <QtCore/qjsonarray.h>
#include <QtCore/qplugin.h>
+#include <QtCore/qstringview.h>
-#include <private/qmetaobject_p.h> //for the flags.
+#include <math.h>
#include <stdio.h>
+#include <private/qmetaobject_p.h> //for the flags.
+#include <private/qplugin_p.h> //for the flags.
+
QT_BEGIN_NAMESPACE
+using namespace QtMiscUtils;
+
+// -- QtScxml
void fprintf(QIODevice &out, const char *fmt, ...)
{
va_list argp;
@@ -61,13 +48,14 @@ void fputs(const char *s, QIODevice &out)
{
out.write(s);
}
+// -- QtScxml
uint nameToBuiltinType(const QByteArray &name)
{
if (name.isEmpty())
return 0;
- uint tp = QMetaType::type(name.constData());
+ uint tp = qMetaTypeTypeInternal(name.constData());
return tp < uint(QMetaType::User) ? tp : uint(QMetaType::UnknownType);
}
@@ -76,7 +64,7 @@ uint nameToBuiltinType(const QByteArray &name)
*/
bool isBuiltinType(const QByteArray &type)
{
- int id = QMetaType::type(type.constData());
+ int id = qMetaTypeTypeInternal(type.constData());
if (id == QMetaType::UnknownType)
return false;
return (id < QMetaType::User);
@@ -91,42 +79,73 @@ static const char *metaTypeEnumValueString(int type)
QT_FOR_EACH_STATIC_TYPE(RETURN_METATYPENAME_STRING)
}
#undef RETURN_METATYPENAME_STRING
- return 0;
+ return nullptr;
}
-Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, const QHash<QByteArray,
- QByteArray> &knownQObjectClasses, const QHash<QByteArray,
- QByteArray> &knownGadgets, QIODevice &outfile)
- : out(outfile), cdef(classDef), metaTypes(metaTypes), knownQObjectClasses(knownQObjectClasses)
- , knownGadgets(knownGadgets)
+// -- QtScxml
+Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes,
+ const QHash<QByteArray, QByteArray> &knownQObjectClasses,
+ const QHash<QByteArray, QByteArray> &knownGadgets,
+ QIODevice &outfile,
+ bool requireCompleteTypes)
+ : out(outfile),
+ cdef(classDef),
+ metaTypes(metaTypes),
+ knownQObjectClasses(knownQObjectClasses),
+ knownGadgets(knownGadgets),
+ requireCompleteTypes(requireCompleteTypes)
{
if (cdef->superclassList.size())
- purestSuperClass = cdef->superclassList.first().first;
+ purestSuperClass = cdef->superclassList.constFirst().classname;
}
+// -- QtScxml
-static inline int lengthOfEscapeSequence(const QByteArray &s, int i)
+#if 0 // -- QtScxml
+static inline qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i)
{
- if (s.at(i) != '\\' || i >= s.length() - 1)
+ if (s.at(i) != '\\' || i >= s.size() - 1)
return 1;
- const int startPos = i;
+ const qsizetype startPos = i;
++i;
char ch = s.at(i);
if (ch == 'x') {
++i;
- while (i < s.length() && is_hex_char(s.at(i)))
+ while (i < s.size() && isHexDigit(s.at(i)))
++i;
- } else if (is_octal_char(ch)) {
+ } else if (isOctalDigit(ch)) {
while (i < startPos + 4
- && i < s.length()
- && is_octal_char(s.at(i))) {
+ && i < s.size()
+ && isOctalDigit(s.at(i))) {
++i;
}
} else { // single character escape sequence
- i = qMin(i + 1, s.length());
+ i = qMin(i + 1, s.size());
}
return i - startPos;
}
+// Prints \a s to \a out, breaking it into lines of at most ColumnWidth. The
+// opening and closing quotes are NOT included (it's up to the caller).
+static void printStringWithIndentation(QIODevice &out, const QByteArray &s) // -- QtScxml
+{
+ static constexpr int ColumnWidth = 72;
+ const qsizetype len = s.size();
+ qsizetype idx = 0;
+
+ do {
+ qsizetype spanLen = qMin(ColumnWidth - 2, len - idx);
+ // don't cut escape sequences at the end of a line
+ const qsizetype backSlashPos = s.lastIndexOf('\\', idx + spanLen - 1);
+ if (backSlashPos >= idx) {
+ const qsizetype escapeLen = lengthOfEscapeSequence(s, backSlashPos);
+ spanLen = qBound(spanLen, backSlashPos + escapeLen - idx, len - idx);
+ }
+ fprintf(out, "\n \"%.*s\"", int(spanLen), s.constData() + idx);
+ idx += spanLen;
+ } while (idx < len);
+}
+#endif // -- QtSxcml
+
void Generator::strreg(const QByteArray &s)
{
if (!strings.contains(s))
@@ -135,7 +154,7 @@ void Generator::strreg(const QByteArray &s)
int Generator::stridx(const QByteArray &s)
{
- int i = strings.indexOf(s);
+ int i = int(strings.indexOf(s));
Q_ASSERT_X(i != -1, Q_FUNC_INFO, "We forgot to register some strings");
return i;
}
@@ -146,8 +165,8 @@ int Generator::stridx(const QByteArray &s)
static int aggregateParameterCount(const QList<FunctionDef> &list)
{
int sum = 0;
- for (int i = 0; i < list.count(); ++i)
- sum += list.at(i).arguments.count() + 1; // +1 for return type
+ for (const FunctionDef &def : list)
+ sum += int(def.arguments.size()) + 1; // +1 for return type
return sum;
}
@@ -166,31 +185,35 @@ bool Generator::registerableMetaType(const QByteArray &propertyType)
return true;
}
- static const QVector<QByteArray> smartPointers = QVector<QByteArray>()
+ static const QList<QByteArray> smartPointers = QList<QByteArray>()
#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER
- QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER)
+ QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER)
#undef STREAM_SMART_POINTER
- ;
+ ;
- for (const QByteArray &smartPointer : smartPointers)
- if (propertyType.startsWith(smartPointer + "<") && !propertyType.endsWith("&"))
+ for (const QByteArray &smartPointer : smartPointers) {
+ QByteArray ba = smartPointer + "<";
+ if (propertyType.startsWith(ba) && !propertyType.endsWith("&"))
return knownQObjectClasses.contains(propertyType.mid(smartPointer.size() + 1, propertyType.size() - smartPointer.size() - 1 - 1));
+ }
- static const QVector<QByteArray> oneArgTemplates = QVector<QByteArray>()
+ static const QList<QByteArray> oneArgTemplates = QList<QByteArray>()
#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME
- QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE)
+ QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE)
#undef STREAM_1ARG_TEMPLATE
- ;
- for (const QByteArray &oneArgTemplateType : oneArgTemplates)
- if (propertyType.startsWith(oneArgTemplateType + "<") && propertyType.endsWith(">")) {
- const int argumentSize = propertyType.size() - oneArgTemplateType.size() - 1
+ ;
+ for (const QByteArray &oneArgTemplateType : oneArgTemplates) {
+ const QByteArray ba = oneArgTemplateType + "<";
+ if (propertyType.startsWith(ba) && propertyType.endsWith(">")) {
+ const qsizetype argumentSize = propertyType.size() - ba.size()
// The closing '>'
- 1
// templates inside templates have an extra whitespace char to strip.
- (propertyType.at(propertyType.size() - 2) == ' ' ? 1 : 0 );
- const QByteArray templateArg = propertyType.mid(oneArgTemplateType.size() + 1, argumentSize);
+ const QByteArray templateArg = propertyType.sliced(ba.size(), argumentSize);
return isBuiltinType(templateArg) || registerableMetaType(templateArg);
}
+ }
return false;
}
@@ -200,26 +223,41 @@ static bool qualifiedNameEquals(const QByteArray &qualifiedName, const QByteArra
{
if (qualifiedName == name)
return true;
- int index = qualifiedName.indexOf("::");
+ const qsizetype index = qualifiedName.indexOf("::");
if (index == -1)
return false;
return qualifiedNameEquals(qualifiedName.mid(index+2), name);
}
+static QByteArray generateQualifiedClassNameIdentifier(const QByteArray &identifier)
+{
+ QByteArray qualifiedClassNameIdentifier = identifier;
+
+ // Remove ':'s in the name, but be sure not to create any illegal
+ // identifiers in the process. (Don't replace with '_', because
+ // that will create problems with things like NS_::_class.)
+ qualifiedClassNameIdentifier.replace("::", "SCOPE");
+
+ // Also, avoid any leading/trailing underscores (we'll concatenate
+ // the generated name with other prefixes/suffixes, and these latter
+ // may already include an underscore, leading to two underscores)
+ qualifiedClassNameIdentifier = "CLASS" + qualifiedClassNameIdentifier + "ENDCLASS";
+ return qualifiedClassNameIdentifier;
+}
+
void Generator::generateCode()
{
- bool isQt = (cdef->classname == "Qt");
bool isQObject = (cdef->classname == "QObject");
bool isConstructible = !cdef->constructorList.isEmpty();
// filter out undeclared enumerators and sets
{
QList<EnumDef> enumList;
- for (int i = 0; i < cdef->enumList.count(); ++i) {
- EnumDef def = cdef->enumList.at(i);
+ for (EnumDef def : std::as_const(cdef->enumList)) {
if (cdef->enumDeclarations.contains(def.name)) {
enumList += def;
}
+ def.enumName = def.name;
QByteArray alias = cdef->flagAliases.value(def.name);
if (cdef->enumDeclarations.contains(alias)) {
def.name = alias;
@@ -238,102 +276,74 @@ void Generator::generateCode()
registerFunctionStrings(cdef->slotList);
registerFunctionStrings(cdef->methodList);
registerFunctionStrings(cdef->constructorList);
+ registerByteArrayVector(cdef->nonClassSignalList);
registerPropertyStrings();
registerEnumStrings();
- QByteArray qualifiedClassNameIdentifier = cdef->qualified;
- qualifiedClassNameIdentifier.replace(':', '_');
+ const bool hasStaticMetaCall =
+ (cdef->hasQObject || !cdef->methodList.isEmpty()
+ || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty());
+
+ const QByteArray qualifiedClassNameIdentifier = generateQualifiedClassNameIdentifier(cdef->qualified);
+
+ // ensure the qt_meta_stringdata_XXXX_t type is local
+ fprintf(out, "namespace {\n");
//
-// Build stringdata struct
+// Build the strings using QtMocHelpers::StringData
//
- const int constCharArraySizeLimit = 65535;
- fprintf(out, "struct qt_meta_stringdata_%s_t {\n", qualifiedClassNameIdentifier.constData());
- fprintf(out, " QByteArrayData data[%d];\n", strings.size());
- {
- int stringDataLength = 0;
- int stringDataCounter = 0;
- for (int i = 0; i < strings.size(); ++i) {
- int thisLength = strings.at(i).length() + 1;
- stringDataLength += thisLength;
- if (stringDataLength / constCharArraySizeLimit) {
- // save previous stringdata and start computing the next one.
- fprintf(out, " unsigned char stringdata%d[%d];\n", stringDataCounter++,
- stringDataLength - thisLength);
- stringDataLength = thisLength;
- }
- }
- fprintf(out, " unsigned char stringdata%d[%d];\n", stringDataCounter, stringDataLength);
- }
- fprintf(out, "};\n");
-
- // Macro that expands into a QByteArrayData. The offset member is
- // calculated from 1) the offset of the actual characters in the
- // stringdata.stringdata member, and 2) the stringdata.data index of the
- // QByteArrayData being defined. This calculation relies on the
- // QByteArrayData::data() implementation returning simply "this + offset".
- fprintf(out, "#define QT_MOC_LITERAL(idx, ofs, len) \\\n"
- " Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \\\n"
- " qptrdiff(offsetof(qt_meta_stringdata_%s_t, stringdata0) + ofs \\\n"
- " - idx * sizeof(QByteArrayData)) \\\n"
- " )\n",
- qualifiedClassNameIdentifier.constData());
-
- fprintf(out, "static const qt_meta_stringdata_%s_t qt_meta_stringdata_%s = {\n",
+ fprintf(out, "\n#ifdef QT_MOC_HAS_STRINGDATA\n"
+ "struct qt_meta_stringdata_%s_t {};\n"
+ "constexpr auto qt_meta_stringdata_%s = QtMocHelpers::stringData(",
qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData());
- fprintf(out, " {\n");
{
- int idx = 0;
- for (int i = 0; i < strings.size(); ++i) {
- const QByteArray &str = strings.at(i);
- fprintf(out, "QT_MOC_LITERAL(%d, %d, %d)", i, idx, str.length());
- if (i != strings.size() - 1)
- fputc(',', out);
- const QByteArray comment = str.length() > 32 ? str.left(29) + "..." : str;
- fprintf(out, " // \"%s\"\n", comment.constData());
- idx += str.length() + 1;
- for (int j = 0; j < str.length(); ++j) {
- if (str.at(j) == '\\') {
- int cnt = lengthOfEscapeSequence(str, j) - 1;
- idx -= cnt;
- j += cnt;
- }
- }
+ char comma = 0;
+// -- QtScxml
+ for (qsizetype i = 0, end = strings.size(); i < end; ++i) {
+ if (comma)
+ fputc(comma, out);
+ fprintf(out, "\n {");
+ const QByteArray s = strings.at(i);
+ const qsizetype len = s.size();
+ for (qsizetype charPos = 0; charPos < len; ++charPos)
+ fprintf(out, "char(0x%.2x),", static_cast<quint8>(s.at(charPos)));
+ const bool isLast = (i == end - 1);
+ fprintf(out, "char(0)%s // %d: %s", isLast ? "}" : "},", i, s.constData());
+ comma = ',';
}
- fprintf(out, " },{\n");
- }
+// -- QtScxml
-//
-// Build stringdata array
-//
- for (int i = 0; i < strings.size(); ++i) {
- QByteArray s = strings.at(i);
- int len = s.length();
- for (int charPos = 0; charPos < len; ++charPos)
- fprintf(out, "0x%.2x,", static_cast<quint8>(s.at(charPos)));
- fprintf(out, "0%s // %d: %s\n", i < strings.size() - 1 ? "," : "", i, s.constData());
}
-
-// Terminate stringdata struct
- fprintf(out, " }};\n");
- fprintf(out, "#undef QT_MOC_LITERAL\n\n");
+ fprintf(out, "\n);\n"
+ "#else // !QT_MOC_HAS_STRINGDATA\n");
+ fprintf(out, "#error \"qtmochelpers.h not found or too old.\"\n");
+ fprintf(out, "#endif // !QT_MOC_HAS_STRINGDATA\n");
+ fprintf(out, "} // unnamed namespace\n\n");
//
// build the data array
//
int index = MetaObjectPrivateFieldCount;
- fprintf(out, "static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData());
+ fprintf(out, "Q_CONSTINIT static const uint qt_meta_data_%s[] = {\n", qualifiedClassNameIdentifier.constData());
fprintf(out, "\n // content:\n");
fprintf(out, " %4d, // revision\n", int(QMetaObjectPrivate::OutputRevision));
fprintf(out, " %4d, // classname\n", stridx(cdef->qualified));
- fprintf(out, " %4d, %4d, // classinfo\n", cdef->classInfoList.count(), cdef->classInfoList.count() ? index : 0);
- index += cdef->classInfoList.count() * 2;
+ fprintf(out, " %4d, %4d, // classinfo\n", int(cdef->classInfoList.size()), int(cdef->classInfoList.size() ? index : 0));
+ index += cdef->classInfoList.size() * 2;
+
+ qsizetype methodCount = 0;
+ if (qAddOverflow(cdef->signalList.size(), cdef->slotList.size(), &methodCount)
+ || qAddOverflow(cdef->methodList.size(), methodCount, &methodCount)) {
+// -- QtScxml
+ qFatal("internal limit exceeded: the total number of member functions"
+ " (including signals and slots) is too big.");
+// -- QtScxml
+ }
- int methodCount = cdef->signalList.count() + cdef->slotList.count() + cdef->methodList.count();
- fprintf(out, " %4d, %4d, // methods\n", methodCount, methodCount ? index : 0);
- index += methodCount * 5;
+ fprintf(out, " %4" PRIdQSIZETYPE ", %4d, // methods\n", methodCount, methodCount ? index : 0);
+ index += methodCount * QMetaObjectPrivate::IntsPerMethod;
if (cdef->revisionedMethods)
index += methodCount;
int paramsIndex = index;
@@ -343,30 +353,27 @@ void Generator::generateCode()
+ aggregateParameterCount(cdef->constructorList);
index += totalParameterCount * 2 // types and parameter names
- methodCount // return "parameters" don't have names
- - cdef->constructorList.count(); // "this" parameters don't have names
+ - int(cdef->constructorList.size()); // "this" parameters don't have names
- fprintf(out, " %4d, %4d, // properties\n", cdef->propertyList.count(), cdef->propertyList.count() ? index : 0);
- index += cdef->propertyList.count() * 3;
- if(cdef->notifyableProperties)
- index += cdef->propertyList.count();
- if (cdef->revisionedProperties)
- index += cdef->propertyList.count();
- fprintf(out, " %4d, %4d, // enums/sets\n", cdef->enumList.count(), cdef->enumList.count() ? index : 0);
+ fprintf(out, " %4d, %4d, // properties\n", int(cdef->propertyList.size()), int(cdef->propertyList.size() ? index : 0));
+ index += cdef->propertyList.size() * QMetaObjectPrivate::IntsPerProperty;
+ fprintf(out, " %4d, %4d, // enums/sets\n", int(cdef->enumList.size()), cdef->enumList.size() ? index : 0);
int enumsIndex = index;
- for (int i = 0; i < cdef->enumList.count(); ++i)
- index += 4 + (cdef->enumList.at(i).values.count() * 2);
- fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? cdef->constructorList.count() : 0,
+ for (const EnumDef &def : std::as_const(cdef->enumList))
+ index += QMetaObjectPrivate::IntsPerEnum + (def.values.size() * 2);
+
+ fprintf(out, " %4d, %4d, // constructors\n", isConstructible ? int(cdef->constructorList.size()) : 0,
isConstructible ? index : 0);
int flags = 0;
- if (cdef->hasQGadget) {
+ if (cdef->hasQGadget || cdef->hasQNamespace) {
// Ideally, all the classes could have that flag. But this broke classes generated
// by qdbusxml2cpp which generate code that require that we call qt_metacall for properties
flags |= PropertyAccessInStaticMetaCall;
}
fprintf(out, " %4d, // flags\n", flags);
- fprintf(out, " %4d, // signalCount\n", cdef->signalList.count());
+ fprintf(out, " %4d, // signalCount\n", int(cdef->signalList.size()));
//
@@ -374,20 +381,31 @@ void Generator::generateCode()
//
generateClassInfos();
+ qsizetype propEnumCount = 0;
+ // all property metatypes + all enum metatypes + 1 for the type of the current class itself
+ if (qAddOverflow(cdef->propertyList.size(), cdef->enumList.size(), &propEnumCount)
+ || qAddOverflow(propEnumCount, qsizetype(1), &propEnumCount)
+ || propEnumCount >= std::numeric_limits<int>::max()) {
+// -- QtScxml
+ qFatal("internal limit exceeded: number of property and enum metatypes is too big.");
+// -- QtScxml
+ }
+ int initialMetaTypeOffset = int(propEnumCount);
+
//
// Build signals array first, otherwise the signal indices would be wrong
//
- generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex);
+ generateFunctions(cdef->signalList, "signal", MethodSignal, paramsIndex, initialMetaTypeOffset);
//
// Build slots array
//
- generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex);
+ generateFunctions(cdef->slotList, "slot", MethodSlot, paramsIndex, initialMetaTypeOffset);
//
// Build method array
//
- generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex);
+ generateFunctions(cdef->methodList, "method", MethodMethod, paramsIndex, initialMetaTypeOffset);
//
// Build method version arrays
@@ -421,7 +439,7 @@ void Generator::generateCode()
// Build constructors array
//
if (isConstructible)
- generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex);
+ generateFunctions(cdef->constructorList, "constructor", MethodConstructor, paramsIndex, initialMetaTypeOffset);
//
// Terminate data array
@@ -429,41 +447,31 @@ void Generator::generateCode()
fprintf(out, "\n 0 // eod\n};\n\n");
//
-// Generate internal qt_static_metacall() function
-//
- const bool hasStaticMetaCall = !isQt &&
- (cdef->hasQObject || !cdef->methodList.isEmpty()
- || !cdef->propertyList.isEmpty() || !cdef->constructorList.isEmpty());
- if (hasStaticMetaCall)
- generateStaticMetacall();
-
-//
// Build extra array
//
QList<QByteArray> extraList;
- QHash<QByteArray, QByteArray> knownExtraMetaObject = knownGadgets;
+ QMultiHash<QByteArray, QByteArray> knownExtraMetaObject(knownGadgets);
knownExtraMetaObject.unite(knownQObjectClasses);
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
if (isBuiltinType(p.type))
continue;
if (p.type.contains('*') || p.type.contains('<') || p.type.contains('>'))
continue;
- int s = p.type.lastIndexOf("::");
+ const qsizetype s = p.type.lastIndexOf("::");
if (s <= 0)
continue;
QByteArray unqualifiedScope = p.type.left(s);
// The scope may be a namespace for example, so it's only safe to include scopes that are known QObjects (QTBUG-2151)
- QHash<QByteArray, QByteArray>::ConstIterator scopeIt;
+ QMultiHash<QByteArray, QByteArray>::ConstIterator scopeIt;
QByteArray thisScope = cdef->qualified;
do {
- int s = thisScope.lastIndexOf("::");
+ const qsizetype s = thisScope.lastIndexOf("::");
thisScope = thisScope.left(s);
QByteArray currentScope = thisScope.isEmpty() ? unqualifiedScope : thisScope + "::" + unqualifiedScope;
scopeIt = knownExtraMetaObject.constFind(currentScope);
@@ -486,10 +494,10 @@ void Generator::generateCode()
// QTBUG-20639 - Accept non-local enums for QML signal/slot parameters.
// Look for any scoped enum declarations, and add those to the list
// of extra/related metaobjects for this object.
- QList<QByteArray> enumKeys = cdef->enumDeclarations.keys();
- for (int i = 0; i < enumKeys.count(); ++i) {
- const QByteArray &enumKey = enumKeys[i];
- int s = enumKey.lastIndexOf("::");
+ for (auto it = cdef->enumDeclarations.keyBegin(),
+ end = cdef->enumDeclarations.keyEnd(); it != end; ++it) {
+ const QByteArray &enumKey = *it;
+ const qsizetype s = enumKey.lastIndexOf("::");
if (s > 0) {
QByteArray scope = enumKey.left(s);
if (scope != "Qt" && !qualifiedNameEquals(cdef->qualified, scope) && !extraList.contains(scope))
@@ -497,44 +505,114 @@ void Generator::generateCode()
}
}
+//
+// Generate meta object link to parent meta objects
+//
+
if (!extraList.isEmpty()) {
- fprintf(out, "static const QMetaObject * const qt_meta_extradata_%s[] = {\n ", qualifiedClassNameIdentifier.constData());
- for (int i = 0; i < extraList.count(); ++i) {
- fprintf(out, " &%s::staticMetaObject,\n", extraList.at(i).constData());
- }
+ fprintf(out, "Q_CONSTINIT static const QMetaObject::SuperData qt_meta_extradata_%s[] = {\n",
+ qualifiedClassNameIdentifier.constData());
+ for (const QByteArray &ba : std::as_const(extraList))
+ fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", ba.constData());
+
fprintf(out, " nullptr\n};\n\n");
}
//
// Finally create and initialize the static meta object
//
- if (isQt)
- fprintf(out, "const QMetaObject QObject::staticQtMetaObject = {\n");
- else
- fprintf(out, "const QMetaObject %s::staticMetaObject = {\n", cdef->qualified.constData());
+ fprintf(out, "Q_CONSTINIT const QMetaObject %s::staticMetaObject = { {\n",
+ cdef->qualified.constData());
if (isQObject)
- fprintf(out, " { nullptr, ");
- else if (cdef->superclassList.size() && (!cdef->hasQGadget || knownGadgets.contains(purestSuperClass)))
- fprintf(out, " { &%s::staticMetaObject, ", purestSuperClass.constData());
+ fprintf(out, " nullptr,\n");
+ else if (cdef->superclassList.size() && !cdef->hasQGadget && !cdef->hasQNamespace) // for qobject, we know the super class must have a static metaobject
+ fprintf(out, " QMetaObject::SuperData::link<%s::staticMetaObject>(),\n", purestSuperClass.constData());
+ else if (cdef->superclassList.size()) // for gadgets we need to query at compile time for it
+ fprintf(out, " QtPrivate::MetaObjectForType<%s>::value,\n", purestSuperClass.constData());
else
- fprintf(out, " { nullptr, ");
- fprintf(out, "qt_meta_stringdata_%s.data,\n"
- " qt_meta_data_%s, ", qualifiedClassNameIdentifier.constData(),
+ fprintf(out, " nullptr,\n");
+ fprintf(out, " qt_meta_stringdata_%s.offsetsAndSizes,\n"
+ " qt_meta_data_%s,\n", qualifiedClassNameIdentifier.constData(),
qualifiedClassNameIdentifier.constData());
if (hasStaticMetaCall)
- fprintf(out, " qt_static_metacall, ");
+ fprintf(out, " qt_static_metacall,\n");
else
- fprintf(out, " nullptr, ");
+ fprintf(out, " nullptr,\n");
if (extraList.isEmpty())
- fprintf(out, "nullptr, ");
+ fprintf(out, " nullptr,\n");
else
- fprintf(out, "qt_meta_extradata_%s, ", qualifiedClassNameIdentifier.constData());
- fprintf(out, "nullptr}\n};\n\n");
+ fprintf(out, " qt_meta_extradata_%s,\n", qualifiedClassNameIdentifier.constData());
+
+ const char *comma = "";
+ const bool requireCompleteness = requireCompleteTypes || cdef->requireCompleteMethodTypes;
+ auto stringForType = [requireCompleteness](const QByteArray &type, bool forceComplete) -> QByteArray {
+ const char *forceCompleteType = forceComplete ? ", std::true_type>" : ", std::false_type>";
+ if (requireCompleteness)
+ return type;
+ return "QtPrivate::TypeAndForceComplete<" % type % forceCompleteType;
+ };
+ if (!requireCompleteness) {
+ fprintf(out, " qt_incomplete_metaTypeArray<qt_meta_stringdata_%s_t", qualifiedClassNameIdentifier.constData());
+ comma = ",";
+ } else {
+ fprintf(out, " qt_metaTypeArray<");
+ }
+ // metatypes for properties
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
+ fprintf(out, "%s\n // property '%s'\n %s",
+ comma, p.name.constData(), stringForType(p.type, true).constData());
+ comma = ",";
+ }
- if(isQt)
- return;
+ // metatypes for enums
+ for (const EnumDef &e : std::as_const(cdef->enumList)) {
+ fprintf(out, "%s\n // enum '%s'\n %s",
+ comma, e.name.constData(), stringForType(e.qualifiedType(cdef), true).constData());
+ comma = ",";
+ }
+
+ // type name for the Q_OJBECT/GADGET itself, void for namespaces
+ auto ownType = !cdef->hasQNamespace ? cdef->classname.data() : "void";
+ fprintf(out, "%s\n // Q_OBJECT / Q_GADGET\n %s",
+ comma, stringForType(ownType, true).constData());
+ comma = ",";
+
+ // metatypes for all exposed methods
+ // because we definitely printed something above, this section doesn't need comma control
+ const auto allMethods = {&cdef->signalList, &cdef->slotList, &cdef->methodList};
+ for (const QList<FunctionDef> *methodContainer : allMethods) {
+ for (const FunctionDef &fdef : *methodContainer) {
+ fprintf(out, ",\n // method '%s'\n %s",
+ fdef.name.constData(), stringForType(fdef.type.name, false).constData());
+ for (const auto &argument: fdef.arguments)
+ fprintf(out, ",\n %s", stringForType(argument.type.name, false).constData());
+ }
+ }
+
+ // but constructors have no return types, so this needs comma control again
+ for (const FunctionDef &fdef : std::as_const(cdef->constructorList)) {
+ if (fdef.arguments.isEmpty())
+ continue;
+
+ fprintf(out, "%s\n // constructor '%s'", comma, fdef.name.constData());
+ comma = "";
+ for (const auto &argument: fdef.arguments) {
+ fprintf(out, "%s\n %s", comma,
+ stringForType(argument.type.name, false).constData());
+ comma = ",";
+ }
+ }
+ fprintf(out, "\n >,\n");
+
+ fprintf(out, " nullptr\n} };\n\n");
+
+//
+// Generate internal qt_static_metacall() function
+//
+ if (hasStaticMetaCall)
+ generateStaticMetacall();
if (!cdef->hasQObject)
return;
@@ -542,30 +620,35 @@ void Generator::generateCode()
fprintf(out, "\nconst QMetaObject *%s::metaObject() const\n{\n return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;\n}\n",
cdef->qualified.constData());
+
//
// Generate smart cast function
//
fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData());
fprintf(out, " if (!_clname) return nullptr;\n");
- fprintf(out, " if (!strcmp(_clname, reinterpret_cast<const char *>(\n"
- " qt_meta_stringdata_%s.stringdata0)))\n"
- " return static_cast<void*>(const_cast< %s*>(this));\n",
- qualifiedClassNameIdentifier.constData(), cdef->classname.constData());
- for (int i = 1; i < cdef->superclassList.size(); ++i) { // for all superclasses but the first one
- if (cdef->superclassList.at(i).second == FunctionDef::Private)
- continue;
- const char *cname = cdef->superclassList.at(i).first.constData();
- fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(const_cast< %s*>(this));\n",
- cname, cname, cdef->classname.constData());
+ fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n"
+ " return static_cast<void*>(this);\n",
+ qualifiedClassNameIdentifier.constData());
+
+ // for all superclasses but the first one
+ if (cdef->superclassList.size() > 1) {
+ auto it = cdef->superclassList.cbegin() + 1;
+ const auto end = cdef->superclassList.cend();
+ for (; it != end; ++it) {
+ if (it->access == FunctionDef::Private)
+ continue;
+ const char *cname = it->classname.constData();
+ fprintf(out, " if (!strcmp(_clname, \"%s\"))\n return static_cast< %s*>(this);\n",
+ cname, cname);
+ }
}
- for (int i = 0; i < cdef->interfaceList.size(); ++i) {
- const QList<ClassDef::Interface> &iface = cdef->interfaceList.at(i);
- for (int j = 0; j < iface.size(); ++j) {
+
+ for (const QList<ClassDef::Interface> &iface : std::as_const(cdef->interfaceList)) {
+ for (qsizetype j = 0; j < iface.size(); ++j) {
fprintf(out, " if (!strcmp(_clname, %s))\n return ", iface.at(j).interfaceId.constData());
- for (int k = j; k >= 0; --k)
+ for (qsizetype k = j; k >= 0; --k)
fprintf(out, "static_cast< %s*>(", iface.at(k).className.constData());
- fprintf(out, "const_cast< %s*>(this)%s;\n",
- cdef->classname.constData(), QByteArray(j+1, ')').constData());
+ fprintf(out, "this%s;\n", QByteArray(j + 1, ')').constData());
}
}
if (!purestSuperClass.isEmpty() && !isQObject) {
@@ -584,21 +667,51 @@ void Generator::generateCode()
//
// Generate internal signal functions
//
- for (int signalindex = 0; signalindex < cdef->signalList.size(); ++signalindex)
- generateSignal(&cdef->signalList[signalindex], signalindex);
+ for (int signalindex = 0; signalindex < int(cdef->signalList.size()); ++signalindex)
+ generateSignal(&cdef->signalList.at(signalindex), signalindex);
- fprintf(out, "\n");
//
// Generate plugin meta data
//
-// generatePluginMetaData();
+#if 0 // -- QtScxml
+ generatePluginMetaData();
+#endif // -- QtScxml
+
+//
+// Generate function to make sure the non-class signals exist in the parent classes
+//
+ if (!cdef->nonClassSignalList.isEmpty()) {
+ fprintf(out, "namespace CheckNotifySignalValidity_%s {\n", qualifiedClassNameIdentifier.constData());
+ for (const QByteArray &nonClassSignal : std::as_const(cdef->nonClassSignalList)) {
+ const auto propertyIt = std::find_if(cdef->propertyList.constBegin(),
+ cdef->propertyList.constEnd(),
+ [&nonClassSignal](const PropertyDef &p) {
+ return nonClassSignal == p.notify;
+ });
+ // must find something, otherwise checkProperties wouldn't have inserted an entry into nonClassSignalList
+ Q_ASSERT(propertyIt != cdef->propertyList.constEnd());
+ fprintf(out, "template<typename T> using has_nullary_%s = decltype(std::declval<T>().%s());\n",
+ nonClassSignal.constData(),
+ nonClassSignal.constData());
+ const auto &propertyType = propertyIt->type;
+ fprintf(out, "template<typename T> using has_unary_%s = decltype(std::declval<T>().%s(std::declval<%s>()));\n",
+ nonClassSignal.constData(),
+ nonClassSignal.constData(),
+ propertyType.constData());
+ fprintf(out, "static_assert(qxp::is_detected_v<has_nullary_%s, %s> || qxp::is_detected_v<has_unary_%s, %s>,\n"
+ " \"NOTIFY signal %s does not exist in class (or is private in its parent)\");\n",
+ nonClassSignal.constData(), cdef->qualified.constData(),
+ nonClassSignal.constData(), cdef->qualified.constData(),
+ nonClassSignal.constData());
+ }
+ fprintf(out, "}\n");
+ }
}
void Generator::registerClassInfoStrings()
{
- for (int i = 0; i < cdef->classInfoList.size(); ++i) {
- const ClassInfoDef &c = cdef->classInfoList.at(i);
+ for (const ClassInfoDef &c : std::as_const(cdef->classInfoList)) {
strreg(c.name);
strreg(c.value);
}
@@ -611,25 +724,19 @@ void Generator::generateClassInfos()
fprintf(out, "\n // classinfo: key, value\n");
- for (int i = 0; i < cdef->classInfoList.size(); ++i) {
- const ClassInfoDef &c = cdef->classInfoList.at(i);
+ for (const ClassInfoDef &c : std::as_const(cdef->classInfoList))
fprintf(out, " %4d, %4d,\n", stridx(c.name), stridx(c.value));
- }
}
void Generator::registerFunctionStrings(const QList<FunctionDef> &list)
{
- for (int i = 0; i < list.count(); ++i) {
- const FunctionDef &f = list.at(i);
-
+ for (const FunctionDef &f : list) {
strreg(f.name);
if (!isBuiltinType(f.normalizedType))
strreg(f.normalizedType);
strreg(f.tag);
- int argsCount = f.arguments.count();
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &a = f.arguments.at(j);
+ for (const ArgumentDef &a : f.arguments) {
if (!isBuiltinType(a.normalizedType))
strreg(a.normalizedType);
strreg(a.name);
@@ -637,17 +744,22 @@ void Generator::registerFunctionStrings(const QList<FunctionDef> &list)
}
}
-void Generator::generateFunctions(const QList<FunctionDef> &list, const char *functype, int type, int &paramsIndex)
+void Generator::registerByteArrayVector(const QList<QByteArray> &list)
+{
+ for (const QByteArray &ba : list)
+ strreg(ba);
+}
+
+void Generator::generateFunctions(const QList<FunctionDef> &list, const char *functype, int type,
+ int &paramsIndex, int &initialMetatypeOffset)
{
if (list.isEmpty())
return;
- fprintf(out, "\n // %ss: name, argc, parameters, tag, flags\n", functype);
-
- for (int i = 0; i < list.count(); ++i) {
- const FunctionDef &f = list.at(i);
+ fprintf(out, "\n // %ss: name, argc, parameters, tag, flags, initial metatype offsets\n", functype);
+ for (const FunctionDef &f : list) {
QByteArray comment;
- unsigned char flags = type;
+ uint flags = type;
if (f.access == FunctionDef::Private) {
flags |= AccessPrivate;
comment.append("Private");
@@ -675,22 +787,27 @@ void Generator::generateFunctions(const QList<FunctionDef> &list, const char *fu
comment.append(" | MethodRevisioned");
}
- int argc = f.arguments.count();
- fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x /* %s */,\n",
- stridx(f.name), argc, paramsIndex, stridx(f.tag), flags, comment.constData());
+ if (f.isConst) {
+ flags |= MethodIsConst;
+ comment.append(" | MethodIsConst ");
+ }
+
+ const int argc = int(f.arguments.size());
+ fprintf(out, " %4d, %4d, %4d, %4d, 0x%02x, %4d /* %s */,\n",
+ stridx(f.name), argc, paramsIndex, stridx(f.tag), flags, initialMetatypeOffset, comment.constData());
paramsIndex += 1 + argc * 2;
+ // constructors don't have a return type
+ initialMetatypeOffset += (f.isConstructor ? 0 : 1) + argc;
}
}
void Generator::generateFunctionRevisions(const QList<FunctionDef> &list, const char *functype)
{
- if (list.count())
+ if (list.size())
fprintf(out, "\n // %ss: revision\n", functype);
- for (int i = 0; i < list.count(); ++i) {
- const FunctionDef &f = list.at(i);
+ for (const FunctionDef &f : list)
fprintf(out, " %4d,\n", f.revision);
- }
}
void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const char *functype)
@@ -698,25 +815,22 @@ void Generator::generateFunctionParameters(const QList<FunctionDef> &list, const
if (list.isEmpty())
return;
fprintf(out, "\n // %ss: parameters\n", functype);
- for (int i = 0; i < list.count(); ++i) {
- const FunctionDef &f = list.at(i);
+ for (const FunctionDef &f : list) {
fprintf(out, " ");
// Types
- int argsCount = f.arguments.count();
- for (int j = -1; j < argsCount; ++j) {
- if (j > -1)
- fputc(' ', out);
- const QByteArray &typeName = (j < 0) ? f.normalizedType : f.arguments.at(j).normalizedType;
- generateTypeInfo(typeName, /*allowEmptyName=*/f.isConstructor);
+ const bool allowEmptyName = f.isConstructor;
+ generateTypeInfo(f.normalizedType, allowEmptyName);
+ fputc(',', out);
+ for (const ArgumentDef &arg : f.arguments) {
+ fputc(' ', out);
+ generateTypeInfo(arg.normalizedType, allowEmptyName);
fputc(',', out);
}
// Parameter names
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &arg = f.arguments.at(j);
+ for (const ArgumentDef &arg : f.arguments)
fprintf(out, " %4d,", stridx(arg.name));
- }
fprintf(out, "\n");
}
@@ -749,8 +863,7 @@ void Generator::generateTypeInfo(const QByteArray &typeName, bool allowEmptyName
void Generator::registerPropertyStrings()
{
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
strreg(p.name);
if (!isBuiltinType(p.type))
strreg(p.type);
@@ -763,10 +876,9 @@ void Generator::generateProperties()
// Create meta data
//
- if (cdef->propertyList.count())
+ if (cdef->propertyList.size())
fprintf(out, "\n // properties: name, type, flags\n");
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
uint flags = Invalid;
if (!isBuiltinType(p.type))
flags |= EnumOrFlag;
@@ -779,81 +891,52 @@ void Generator::generateProperties()
if (p.stdCppSet())
flags |= StdCppSet;
}
+
if (!p.reset.isEmpty())
flags |= Resettable;
-// if (p.override)
-// flags |= Override;
-
- if (p.designable.isEmpty())
- flags |= ResolveDesignable;
- else if (p.designable != "false")
+ if (p.designable != "false")
flags |= Designable;
- if (p.scriptable.isEmpty())
- flags |= ResolveScriptable;
- else if (p.scriptable != "false")
+ if (p.scriptable != "false")
flags |= Scriptable;
- if (p.stored.isEmpty())
- flags |= ResolveStored;
- else if (p.stored != "false")
+ if (p.stored != "false")
flags |= Stored;
- if (p.editable.isEmpty())
- flags |= ResolveEditable;
- else if (p.editable != "false")
- flags |= Editable;
-
- if (p.user.isEmpty())
- flags |= ResolveUser;
- else if (p.user != "false")
+ if (p.user != "false")
flags |= User;
- if (p.notifyId != -1)
- flags |= Notify;
-
- if (p.revision > 0)
- flags |= Revisioned;
-
if (p.constant)
flags |= Constant;
if (p.final)
flags |= Final;
+ if (p.required)
+ flags |= Required;
+
+ if (!p.bind.isEmpty())
+ flags |= Bindable;
fprintf(out, " %4d, ", stridx(p.name));
generateTypeInfo(p.type);
- fprintf(out, ", 0x%.8x,\n", flags);
- }
-
- if(cdef->notifyableProperties) {
- fprintf(out, "\n // properties: notify_signal_id\n");
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
- if(p.notifyId == -1)
- fprintf(out, " %4d,\n",
- 0);
- else
- fprintf(out, " %4d,\n",
- p.notifyId);
- }
- }
- if (cdef->revisionedProperties) {
- fprintf(out, "\n // properties: revision\n");
- for (int i = 0; i < cdef->propertyList.count(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
- fprintf(out, " %4d,\n", p.revision);
+ int notifyId = p.notifyId;
+ if (p.notifyId < -1) {
+ // signal is in parent class
+ const int indexInStrings = int(strings.indexOf(p.notify));
+ notifyId = indexInStrings | IsUnresolvedSignal;
}
+ fprintf(out, ", 0x%.8x, uint(%d), %d,\n", flags, notifyId, p.revision);
}
}
void Generator::registerEnumStrings()
{
- for (int i = 0; i < cdef->enumList.count(); ++i) {
- const EnumDef &e = cdef->enumList.at(i);
+ for (const EnumDef &e : std::as_const(cdef->enumList)) {
strreg(e.name);
- for (int j = 0; j < e.values.count(); ++j)
- strreg(e.values.at(j));
+ if (!e.enumName.isNull())
+ strreg(e.enumName);
+ for (const QByteArray &val : e.values)
+ strreg(val);
}
}
@@ -862,27 +945,31 @@ void Generator::generateEnums(int index)
if (cdef->enumDeclarations.isEmpty())
return;
- fprintf(out, "\n // enums: name, flags, count, data\n");
- index += 4 * cdef->enumList.count();
+ fprintf(out, "\n // enums: name, alias, flags, count, data\n");
+ index += QMetaObjectPrivate::IntsPerEnum * cdef->enumList.size();
int i;
- for (i = 0; i < cdef->enumList.count(); ++i) {
+ for (i = 0; i < cdef->enumList.size(); ++i) {
const EnumDef &e = cdef->enumList.at(i);
- fprintf(out, " %4d, 0x%.1x, %4d, %4d,\n",
+ int flags = 0;
+ if (cdef->enumDeclarations.value(e.name))
+ flags |= EnumIsFlag;
+ if (e.isEnumClass)
+ flags |= EnumIsScoped;
+ fprintf(out, " %4d, %4d, 0x%.1x, %4d, %4d,\n",
stridx(e.name),
- cdef->enumDeclarations.value(e.name) ? 1 : 0,
- e.values.count(),
+ e.enumName.isNull() ? stridx(e.name) : stridx(e.enumName),
+ flags,
+ int(e.values.size()),
index);
- index += e.values.count() * 2;
+ index += e.values.size() * 2;
}
fprintf(out, "\n // enum data: key, value\n");
- for (i = 0; i < cdef->enumList.count(); ++i) {
- const EnumDef &e = cdef->enumList.at(i);
- for (int j = 0; j < e.values.count(); ++j) {
- const QByteArray &val = e.values.at(j);
+ for (const EnumDef &e : std::as_const(cdef->enumList)) {
+ for (const QByteArray &val : e.values) {
QByteArray code = cdef->qualified.constData();
if (e.isEnumClass)
- code += "::" + e.name;
+ code += "::" + (e.enumName.isNull() ? e.name : e.enumName);
code += "::" + val;
fprintf(out, " %4d, uint(%s),\n",
stridx(val), code.constData());
@@ -902,8 +989,6 @@ void Generator::generateMetacall()
fprintf(out, " _id = %s::qt_metacall(_c, _id, _a);\n", superClass.constData());
}
- fprintf(out, " if (_id < 0)\n return _id;\n");
- fprintf(out, " ");
bool needElse = false;
QList<FunctionDef> methodList;
@@ -911,156 +996,54 @@ void Generator::generateMetacall()
methodList += cdef->slotList;
methodList += cdef->methodList;
+ // If there are no methods or properties, we will return _id anyway, so
+ // don't emit this comparison -- it is unnecessary, and it makes coverity
+ // unhappy.
+ if (methodList.size() || cdef->propertyList.size()) {
+ fprintf(out, " if (_id < 0)\n return _id;\n");
+ }
+
+ fprintf(out, " ");
+
if (methodList.size()) {
needElse = true;
fprintf(out, "if (_c == QMetaObject::InvokeMetaMethod) {\n");
- fprintf(out, " if (_id < %d)\n", methodList.size());
+ fprintf(out, " if (_id < %d)\n", int(methodList.size()));
fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n");
- fprintf(out, " _id -= %d;\n }", methodList.size());
+ fprintf(out, " _id -= %d;\n }", int(methodList.size()));
fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n");
- fprintf(out, " if (_id < %d)\n", methodList.size());
+ fprintf(out, " if (_id < %d)\n", int(methodList.size()));
if (methodsWithAutomaticTypesHelper(methodList).isEmpty())
- fprintf(out, " *reinterpret_cast<int*>(_a[0]) = -1;\n");
+ fprintf(out, " *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType();\n");
else
fprintf(out, " qt_static_metacall(this, _c, _id, _a);\n");
- fprintf(out, " _id -= %d;\n }", methodList.size());
+ fprintf(out, " _id -= %d;\n }", int(methodList.size()));
}
if (cdef->propertyList.size()) {
- bool needDesignable = false;
- bool needScriptable = false;
- bool needStored = false;
- bool needEditable = false;
- bool needUser = false;
- for (int i = 0; i < cdef->propertyList.size(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
- needDesignable |= p.designable.endsWith(')');
- needScriptable |= p.scriptable.endsWith(')');
- needStored |= p.stored.endsWith(')');
- needEditable |= p.editable.endsWith(')');
- needUser |= p.user.endsWith(')');
- }
-
- fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n ");
if (needElse)
fprintf(out, "else ");
fprintf(out,
"if (_c == QMetaObject::ReadProperty || _c == QMetaObject::WriteProperty\n"
- " || _c == QMetaObject::ResetProperty || _c == QMetaObject::RegisterPropertyMetaType) {\n"
+ " || _c == QMetaObject::ResetProperty || _c == QMetaObject::BindableProperty\n"
+ " || _c == QMetaObject::RegisterPropertyMetaType) {\n"
" qt_static_metacall(this, _c, _id, _a);\n"
- " _id -= %d;\n }", cdef->propertyList.count());
-
- fprintf(out, " else ");
- fprintf(out, "if (_c == QMetaObject::QueryPropertyDesignable) {\n");
- if (needDesignable) {
- fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n");
- fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
- const PropertyDef &p = cdef->propertyList.at(propindex);
- if (!p.designable.endsWith(')'))
- continue;
- fprintf(out, " case %d: *_b = %s; break;\n",
- propindex, p.designable.constData());
- }
- fprintf(out, " default: break;\n");
- fprintf(out, " }\n");
- }
- fprintf(out,
- " _id -= %d;\n"
- " }", cdef->propertyList.count());
-
- fprintf(out, " else ");
- fprintf(out, "if (_c == QMetaObject::QueryPropertyScriptable) {\n");
- if (needScriptable) {
- fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n");
- fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
- const PropertyDef &p = cdef->propertyList.at(propindex);
- if (!p.scriptable.endsWith(')'))
- continue;
- fprintf(out, " case %d: *_b = %s; break;\n",
- propindex, p.scriptable.constData());
- }
- fprintf(out, " default: break;\n");
- fprintf(out, " }\n");
- }
- fprintf(out,
- " _id -= %d;\n"
- " }", cdef->propertyList.count());
-
- fprintf(out, " else ");
- fprintf(out, "if (_c == QMetaObject::QueryPropertyStored) {\n");
- if (needStored) {
- fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n");
- fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
- const PropertyDef &p = cdef->propertyList.at(propindex);
- if (!p.stored.endsWith(')'))
- continue;
- fprintf(out, " case %d: *_b = %s; break;\n",
- propindex, p.stored.constData());
- }
- fprintf(out, " default: break;\n");
- fprintf(out, " }\n");
- }
- fprintf(out,
- " _id -= %d;\n"
- " }", cdef->propertyList.count());
-
- fprintf(out, " else ");
- fprintf(out, "if (_c == QMetaObject::QueryPropertyEditable) {\n");
- if (needEditable) {
- fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n");
- fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
- const PropertyDef &p = cdef->propertyList.at(propindex);
- if (!p.editable.endsWith(')'))
- continue;
- fprintf(out, " case %d: *_b = %s; break;\n",
- propindex, p.editable.constData());
- }
- fprintf(out, " default: break;\n");
- fprintf(out, " }\n");
- }
- fprintf(out,
- " _id -= %d;\n"
- " }", cdef->propertyList.count());
-
-
- fprintf(out, " else ");
- fprintf(out, "if (_c == QMetaObject::QueryPropertyUser) {\n");
- if (needUser) {
- fprintf(out, " bool *_b = reinterpret_cast<bool*>(_a[0]);\n");
- fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
- const PropertyDef &p = cdef->propertyList.at(propindex);
- if (!p.user.endsWith(')'))
- continue;
- fprintf(out, " case %d: *_b = %s; break;\n",
- propindex, p.user.constData());
- }
- fprintf(out, " default: break;\n");
- fprintf(out, " }\n");
- }
- fprintf(out,
- " _id -= %d;\n"
- " }", cdef->propertyList.count());
-
- fprintf(out, "\n#endif // QT_NO_PROPERTIES");
+ " _id -= %d;\n }", int(cdef->propertyList.size()));
}
- if (methodList.size() || cdef->signalList.size() || cdef->propertyList.size())
+ if (methodList.size() || cdef->propertyList.size())
fprintf(out, "\n ");
fprintf(out,"return _id;\n}\n");
}
+// ### Qt 7 (6.x?): remove
QMultiMap<QByteArray, int> Generator::automaticPropertyMetaTypesHelper()
{
QMultiMap<QByteArray, int> automaticPropertyMetaTypes;
- for (int i = 0; i < cdef->propertyList.size(); ++i) {
+ for (int i = 0; i < int(cdef->propertyList.size()); ++i) {
const QByteArray propertyType = cdef->propertyList.at(i).type;
if (registerableMetaType(propertyType) && !isBuiltinType(propertyType))
automaticPropertyMetaTypes.insert(propertyType, i);
@@ -1068,12 +1051,13 @@ QMultiMap<QByteArray, int> Generator::automaticPropertyMetaTypesHelper()
return automaticPropertyMetaTypes;
}
-QMap<int, QMultiMap<QByteArray, int> > Generator::methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList)
+QMap<int, QMultiMap<QByteArray, int>>
+Generator::methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList)
{
QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypes;
for (int i = 0; i < methodList.size(); ++i) {
const FunctionDef &f = methodList.at(i);
- for (int j = 0; j < f.arguments.count(); ++j) {
+ for (int j = 0; j < f.arguments.size(); ++j) {
const QByteArray argType = f.arguments.at(j).normalizedType;
if (registerableMetaType(argType) && !isBuiltinType(argType))
methodsWithAutomaticTypes[i].insert(argType, j);
@@ -1090,30 +1074,43 @@ void Generator::generateStaticMetacall()
bool needElse = false;
bool isUsed_a = false;
+ const auto generateCtorArguments = [&](int ctorindex) {
+ const FunctionDef &f = cdef->constructorList.at(ctorindex);
+ Q_ASSERT(!f.isPrivateSignal); // That would be a strange ctor indeed
+ int offset = 1;
+
+ const auto begin = f.arguments.cbegin();
+ const auto end = f.arguments.cend();
+ for (auto it = begin; it != end; ++it) {
+ const ArgumentDef &a = *it;
+ if (it != begin)
+ fprintf(out, ",");
+ fprintf(out, "(*reinterpret_cast<%s>(_a[%d]))",
+ a.typeNameForCast.constData(), offset++);
+ }
+ };
+
if (!cdef->constructorList.isEmpty()) {
fprintf(out, " if (_c == QMetaObject::CreateInstance) {\n");
fprintf(out, " switch (_id) {\n");
- for (int ctorindex = 0; ctorindex < cdef->constructorList.count(); ++ctorindex) {
+ const int ctorend = int(cdef->constructorList.size());
+ for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) {
fprintf(out, " case %d: { %s *_r = new %s(", ctorindex,
cdef->classname.constData(), cdef->classname.constData());
- const FunctionDef &f = cdef->constructorList.at(ctorindex);
- int offset = 1;
-
- int argsCount = f.arguments.count();
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &a = f.arguments.at(j);
- if (j)
- fprintf(out, ",");
- fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))", a.typeNameForCast.constData(), offset++);
- }
- if (f.isPrivateSignal) {
- if (argsCount > 0)
- fprintf(out, ", ");
- fprintf(out, "%s", QByteArray("QPrivateSignal()").constData());
- }
+ generateCtorArguments(ctorindex);
fprintf(out, ");\n");
fprintf(out, " if (_a[0]) *reinterpret_cast<%s**>(_a[0]) = _r; } break;\n",
- cdef->hasQGadget ? "void" : "QObject");
+ (cdef->hasQGadget || cdef->hasQNamespace) ? "void" : "QObject");
+ }
+ fprintf(out, " default: break;\n");
+ fprintf(out, " }\n");
+ fprintf(out, " } else if (_c == QMetaObject::ConstructInPlace) {\n");
+ fprintf(out, " switch (_id) {\n");
+ for (int ctorindex = 0; ctorindex < ctorend; ++ctorindex) {
+ fprintf(out, " case %d: { new (_a[0]) %s(",
+ ctorindex, cdef->classname.constData());
+ generateCtorArguments(ctorindex);
+ fprintf(out, "); } break;\n");
}
fprintf(out, " default: break;\n");
fprintf(out, " }\n");
@@ -1137,25 +1134,23 @@ void Generator::generateStaticMetacall()
#ifndef QT_NO_DEBUG
fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
#endif
- fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
+ fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData());
} else {
- fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
+ fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData());
}
- fprintf(out, " Q_UNUSED(_t)\n");
+ fprintf(out, " (void)_t;\n");
fprintf(out, " switch (_id) {\n");
for (int methodindex = 0; methodindex < methodList.size(); ++methodindex) {
const FunctionDef &f = methodList.at(methodindex);
Q_ASSERT(!f.normalizedType.isEmpty());
fprintf(out, " case %d: ", methodindex);
-
- //---- Changed from the original in moc
+ // -- QtScxml
if (f.implementation) {
fprintf(out, f.implementation, "_o", methodindex);
fprintf(out, " break;\n");
continue;
}
- //---- End of change
-
+ // -- QtScxml
if (f.normalizedType != "void")
fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData());
fprintf(out, "_t->");
@@ -1164,22 +1159,27 @@ void Generator::generateStaticMetacall()
fprintf(out, "%s(", f.name.constData());
int offset = 1;
- int argsCount = f.arguments.count();
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &a = f.arguments.at(j);
- if (j)
- fprintf(out, ",");
- fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++);
- isUsed_a = true;
- }
- if (f.isPrivateSignal) {
- if (argsCount > 0)
- fprintf(out, ", ");
- fprintf(out, "%s", "QPrivateSignal()");
+ if (f.isRawSlot) {
+ fprintf(out, "QMethodRawArguments{ _a }");
+ } else {
+ const auto begin = f.arguments.cbegin();
+ const auto end = f.arguments.cend();
+ for (auto it = begin; it != end; ++it) {
+ const ArgumentDef &a = *it;
+ if (it != begin)
+ fprintf(out, ",");
+ fprintf(out, "(*reinterpret_cast< %s>(_a[%d]))",a.typeNameForCast.constData(), offset++);
+ isUsed_a = true;
+ }
+ if (f.isPrivateSignal) {
+ if (!f.arguments.isEmpty())
+ fprintf(out, ", ");
+ fprintf(out, "%s", "QPrivateSignal()");
+ }
}
fprintf(out, ");");
if (f.normalizedType != "void") {
- fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = _r; } ",
+ fprintf(out, "\n if (_a[0]) *reinterpret_cast< %s*>(_a[0]) = std::move(_r); } ",
noRef(f.normalizedType).constData());
isUsed_a = true;
}
@@ -1195,17 +1195,21 @@ void Generator::generateStaticMetacall()
if (!methodsWithAutomaticTypes.isEmpty()) {
fprintf(out, " else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {\n");
fprintf(out, " switch (_id) {\n");
- fprintf(out, " default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n");
+ fprintf(out, " default: *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType(); break;\n");
QMap<int, QMultiMap<QByteArray, int> >::const_iterator it = methodsWithAutomaticTypes.constBegin();
const QMap<int, QMultiMap<QByteArray, int> >::const_iterator end = methodsWithAutomaticTypes.constEnd();
for ( ; it != end; ++it) {
fprintf(out, " case %d:\n", it.key());
fprintf(out, " switch (*reinterpret_cast<int*>(_a[1])) {\n");
- fprintf(out, " default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n");
- for (const QByteArray &key : it->uniqueKeys()) {
- for (int argumentID : it->values(key))
- fprintf(out, " case %d:\n", argumentID);
- fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", key.constData());
+ fprintf(out, " default: *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType(); break;\n");
+ auto jt = it->begin();
+ const auto jend = it->end();
+ while (jt != jend) {
+ fprintf(out, " case %d:\n", jt.value());
+ const QByteArray &lastKey = jt.key();
+ ++jt;
+ if (jt == jend || jt.key() != lastKey)
+ fprintf(out, " *reinterpret_cast<QMetaType *>(_a[0]) = QMetaType::fromType< %s >(); break;\n", lastKey.constData());
}
fprintf(out, " }\n");
fprintf(out, " break;\n");
@@ -1220,25 +1224,29 @@ void Generator::generateStaticMetacall()
Q_ASSERT(needElse); // if there is signal, there was method.
fprintf(out, " else if (_c == QMetaObject::IndexOfMethod) {\n");
fprintf(out, " int *result = reinterpret_cast<int *>(_a[0]);\n");
- fprintf(out, " void **func = reinterpret_cast<void **>(_a[1]);\n");
bool anythingUsed = false;
- for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) {
+ for (int methodindex = 0; methodindex < int(cdef->signalList.size()); ++methodindex) {
const FunctionDef &f = cdef->signalList.at(methodindex);
- if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic || f.mangledName.isEmpty())
+ if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic)
+ continue;
+ // -- QtScxml
+ if (f.mangledName.isEmpty())
continue;
+ // -- QtScxml
anythingUsed = true;
fprintf(out, " {\n");
- fprintf(out, " typedef %s (%s::*_t)(",f.type.rawName.constData() , cdef->classname.constData());
+ fprintf(out, " using _t = %s (%s::*)(",f.type.rawName.constData() , cdef->classname.constData());
- int argsCount = f.arguments.count();
- for (int j = 0; j < argsCount; ++j) {
- const ArgumentDef &a = f.arguments.at(j);
- if (j)
+ const auto begin = f.arguments.cbegin();
+ const auto end = f.arguments.cend();
+ for (auto it = begin; it != end; ++it) {
+ const ArgumentDef &a = *it;
+ if (it != begin)
fprintf(out, ", ");
fprintf(out, "%s", QByteArray(a.type.name + ' ' + a.rightType).constData());
}
if (f.isPrivateSignal) {
- if (argsCount > 0)
+ if (!f.arguments.isEmpty())
fprintf(out, ", ");
fprintf(out, "%s", "QPrivateSignal");
}
@@ -1246,19 +1254,19 @@ void Generator::generateStaticMetacall()
fprintf(out, ") const;\n");
else
fprintf(out, ");\n");
- fprintf(out, " if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&%s::%s)) {\n",
- cdef->classname.constData(), f.mangledName.constData());
+ fprintf(out, " if (_t _q_method = &%s::%s; *reinterpret_cast<_t *>(_a[1]) == _q_method) {\n",
+ cdef->classname.constData(), f.mangledName.constData()); // -- QtScxml
fprintf(out, " *result = %d;\n", methodindex);
fprintf(out, " return;\n");
fprintf(out, " }\n }\n");
}
if (!anythingUsed)
- fprintf(out, " Q_UNUSED(result);\n Q_UNUSED(func);\n");
+ fprintf(out, " (void)result;\n");
fprintf(out, " }");
needElse = true;
}
- QMultiMap<QByteArray, int> automaticPropertyMetaTypes = automaticPropertyMetaTypesHelper();
+ const QMultiMap<QByteArray, int> automaticPropertyMetaTypes = automaticPropertyMetaTypesHelper();
if (!automaticPropertyMetaTypes.isEmpty()) {
if (needElse)
@@ -1268,13 +1276,17 @@ void Generator::generateStaticMetacall()
fprintf(out, "if (_c == QMetaObject::RegisterPropertyMetaType) {\n");
fprintf(out, " switch (_id) {\n");
fprintf(out, " default: *reinterpret_cast<int*>(_a[0]) = -1; break;\n");
- for (const QByteArray &key : automaticPropertyMetaTypes.uniqueKeys()) {
- for (int propertyID : automaticPropertyMetaTypes.values(key))
- fprintf(out, " case %d:\n", propertyID);
- fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", key.constData());
+ auto it = automaticPropertyMetaTypes.begin();
+ const auto end = automaticPropertyMetaTypes.end();
+ while (it != end) {
+ fprintf(out, " case %d:\n", it.value());
+ const QByteArray &lastKey = it.key();
+ ++it;
+ if (it == end || it.key() != lastKey)
+ fprintf(out, " *reinterpret_cast<int*>(_a[0]) = qRegisterMetaType< %s >(); break;\n", lastKey.constData());
}
fprintf(out, " }\n");
- fprintf(out, " }\n");
+ fprintf(out, " } ");
isUsed_a = true;
needElse = true;
}
@@ -1284,8 +1296,8 @@ void Generator::generateStaticMetacall()
bool needTempVarForGet = false;
bool needSet = false;
bool needReset = false;
- for (int i = 0; i < cdef->propertyList.size(); ++i) {
- const PropertyDef &p = cdef->propertyList.at(i);
+ bool hasBindableProperties = false;
+ for (const PropertyDef &p : std::as_const(cdef->propertyList)) {
needGet |= !p.read.isEmpty() || !p.member.isEmpty();
if (!p.read.isEmpty() || !p.member.isEmpty())
needTempVarForGet |= (p.gspec != PropertyDef::PointerSpec
@@ -1293,26 +1305,30 @@ void Generator::generateStaticMetacall()
needSet |= !p.write.isEmpty() || (!p.member.isEmpty() && !p.constant);
needReset |= !p.reset.isEmpty();
+ hasBindableProperties |= !p.bind.isEmpty();
}
- fprintf(out, "\n#ifndef QT_NO_PROPERTIES\n ");
-
if (needElse)
- fprintf(out, "else ");
+ fprintf(out, " else ");
fprintf(out, "if (_c == QMetaObject::ReadProperty) {\n");
- if (needGet) {
+
+ auto setupMemberAccess = [this]() {
if (cdef->hasQObject) {
#ifndef QT_NO_DEBUG
fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
#endif
- fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
+ fprintf(out, " auto *_t = static_cast<%s *>(_o);\n", cdef->classname.constData());
} else {
- fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
+ fprintf(out, " auto *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData());
}
- fprintf(out, " Q_UNUSED(_t)\n");
+ fprintf(out, " (void)_t;\n");
+ };
+
+ if (needGet) {
+ setupMemberAccess();
if (needTempVarForGet)
fprintf(out, " void *_v = _a[0];\n");
fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
+ for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
if (p.read.isEmpty() && p.member.isEmpty())
continue;
@@ -1320,6 +1336,7 @@ void Generator::generateStaticMetacall()
if (p.inPrivateClass.size()) {
prefix += p.inPrivateClass + "->";
}
+
if (p.gspec == PropertyDef::PointerSpec)
fprintf(out, " case %d: _a[0] = const_cast<void*>(reinterpret_cast<const void*>(%s%s())); break;\n",
propindex, prefix.constData(), p.read.constData());
@@ -1329,14 +1346,15 @@ void Generator::generateStaticMetacall()
else if (cdef->enumDeclarations.value(p.type, false))
fprintf(out, " case %d: *reinterpret_cast<int*>(_v) = QFlag(%s%s()); break;\n",
propindex, prefix.constData(), p.read.constData());
+ else if (p.read == "default")
+ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s().value(); break;\n",
+ propindex, p.type.constData(), prefix.constData(), p.bind.constData());
else if (!p.read.isEmpty())
- //---- Changed from the original in moc
- {
- fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s%s; break;\n",
- propindex, p.type.constData(), prefix.constData(), p.read.constData(),
- p.read.endsWith(')') ? "" : "()");
- }
- //---- End of change
+ // -- QtScxml
+ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s%s; break;\n",
+ propindex, p.type.constData(), prefix.constData(), p.read.constData(),
+ p.read.endsWith(')') ? "" : "()");
+ // -- QtScxml
else
fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n",
propindex, p.type.constData(), prefix.constData(), p.member.constData());
@@ -1351,18 +1369,10 @@ void Generator::generateStaticMetacall()
fprintf(out, "if (_c == QMetaObject::WriteProperty) {\n");
if (needSet) {
- if (cdef->hasQObject) {
-#ifndef QT_NO_DEBUG
- fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
-#endif
- fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
- } else {
- fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
- }
- fprintf(out, " Q_UNUSED(_t)\n");
+ setupMemberAccess();
fprintf(out, " void *_v = _a[0];\n");
fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
+ for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
if (p.constant)
continue;
@@ -1375,6 +1385,12 @@ void Generator::generateStaticMetacall()
if (cdef->enumDeclarations.value(p.type, false)) {
fprintf(out, " case %d: %s%s(QFlag(*reinterpret_cast<int*>(_v))); break;\n",
propindex, prefix.constData(), p.write.constData());
+ } else if (p.write == "default") {
+ fprintf(out, " case %d: {\n", propindex);
+ fprintf(out, " %s%s().setValue(*reinterpret_cast< %s*>(_v));\n",
+ prefix.constData(), p.bind.constData(), p.type.constData());
+ fprintf(out, " break;\n");
+ fprintf(out, " }\n");
} else if (!p.write.isEmpty()) {
fprintf(out, " case %d: %s%s(*reinterpret_cast< %s*>(_v)); break;\n",
propindex, prefix.constData(), p.write.constData(), p.type.constData());
@@ -1384,13 +1400,15 @@ void Generator::generateStaticMetacall()
prefix.constData(), p.member.constData(), p.type.constData());
fprintf(out, " %s%s = *reinterpret_cast< %s*>(_v);\n",
prefix.constData(), p.member.constData(), p.type.constData());
- if (!p.notify.isEmpty() && p.notifyId != -1) {
+ if (!p.notify.isEmpty() && p.notifyId > -1) {
const FunctionDef &f = cdef->signalList.at(p.notifyId);
if (f.arguments.size() == 0)
fprintf(out, " Q_EMIT _t->%s();\n", p.notify.constData());
else if (f.arguments.size() == 1 && f.arguments.at(0).normalizedType == p.type)
fprintf(out, " Q_EMIT _t->%s(%s%s);\n",
p.notify.constData(), prefix.constData(), p.member.constData());
+ } else if (!p.notify.isEmpty() && p.notifyId < -1) {
+ fprintf(out, " Q_EMIT _t->%s();\n", p.notify.constData());
}
fprintf(out, " }\n");
fprintf(out, " break;\n");
@@ -1405,32 +1423,46 @@ void Generator::generateStaticMetacall()
fprintf(out, " else ");
fprintf(out, "if (_c == QMetaObject::ResetProperty) {\n");
if (needReset) {
- if (cdef->hasQObject) {
-#ifndef QT_NO_DEBUG
- fprintf(out, " Q_ASSERT(staticMetaObject.cast(_o));\n");
-#endif
- fprintf(out, " %s *_t = static_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
- } else {
- fprintf(out, " %s *_t = reinterpret_cast<%s *>(_o);\n", cdef->classname.constData(), cdef->classname.constData());
- }
- fprintf(out, " Q_UNUSED(_t)\n");
+ setupMemberAccess();
fprintf(out, " switch (_id) {\n");
- for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
+ for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
const PropertyDef &p = cdef->propertyList.at(propindex);
- if (!p.reset.endsWith(')'))
+ if (p.reset.isEmpty())
continue;
QByteArray prefix = "_t->";
if (p.inPrivateClass.size()) {
prefix += p.inPrivateClass + "->";
}
- fprintf(out, " case %d: %s%s; break;\n",
+ fprintf(out, " case %d: %s%s(); break;\n",
propindex, prefix.constData(), p.reset.constData());
}
fprintf(out, " default: break;\n");
fprintf(out, " }\n");
}
fprintf(out, " }");
- fprintf(out, "\n#endif // QT_NO_PROPERTIES");
+
+ fprintf(out, " else ");
+ fprintf(out, "if (_c == QMetaObject::BindableProperty) {\n");
+ if (hasBindableProperties) {
+ setupMemberAccess();
+ fprintf(out, " switch (_id) {\n");
+ for (int propindex = 0; propindex < int(cdef->propertyList.size()); ++propindex) {
+ const PropertyDef &p = cdef->propertyList.at(propindex);
+ if (p.bind.isEmpty())
+ continue;
+ QByteArray prefix = "_t->";
+ if (p.inPrivateClass.size()) {
+ prefix += p.inPrivateClass + "->";
+ }
+ fprintf(out,
+ " case %d: *static_cast<QUntypedBindable *>(_a[0]) = %s%s(); "
+ "break;\n",
+ propindex, prefix.constData(), p.bind.constData());
+ }
+ fprintf(out, " default: break;\n");
+ fprintf(out, " }\n");
+ }
+ fprintf(out, " }");
needElse = true;
}
@@ -1438,22 +1470,26 @@ void Generator::generateStaticMetacall()
fprintf(out, "\n");
if (methodList.isEmpty()) {
- fprintf(out, " Q_UNUSED(_o);\n");
+ fprintf(out, " (void)_o;\n");
if (cdef->constructorList.isEmpty() && automaticPropertyMetaTypes.isEmpty() && methodsWithAutomaticTypesHelper(methodList).isEmpty()) {
- fprintf(out, " Q_UNUSED(_id);\n");
- fprintf(out, " Q_UNUSED(_c);\n");
+ fprintf(out, " (void)_id;\n");
+ fprintf(out, " (void)_c;\n");
}
}
if (!isUsed_a)
- fprintf(out, " Q_UNUSED(_a);\n");
+ fprintf(out, " (void)_a;\n");
- fprintf(out, "}\n\n");
+ fprintf(out, "}\n");
}
-void Generator::generateSignal(FunctionDef *def,int index)
+void Generator::generateSignal(const FunctionDef *def, int index)
{
- if (def->wasCloned || def->isAbstract || def->implementation)
+ if (def->wasCloned || def->isAbstract)
+ return;
+// -- QtScxml
+ if (def->implementation)
return;
+// -- QtScxml
fprintf(out, "\n// SIGNAL %d\n%s %s::%s(",
index, def->type.name.constData(), cdef->qualified.constData(), def->name.constData());
@@ -1461,17 +1497,12 @@ void Generator::generateSignal(FunctionDef *def,int index)
const char *constQualifier = "";
if (def->isConst) {
- thisPtr = "const_cast< ";
- thisPtr += cdef->qualified;
- thisPtr += " *>(this)";
+ thisPtr = "const_cast< " + cdef->qualified + " *>(this)";
constQualifier = "const";
}
Q_ASSERT(!def->normalizedType.isEmpty());
- if (def->arguments.isEmpty() && def->normalizedType == "void") {
- if (def->isPrivateSignal)
- fprintf(out, "QPrivateSignal");
-
+ if (def->arguments.isEmpty() && def->normalizedType == "void" && !def->isPrivateSignal) {
fprintf(out, ")%s\n{\n"
" QMetaObject::activate(%s, &staticMetaObject, %d, nullptr);\n"
"}\n", constQualifier, thisPtr.constData(), index);
@@ -1479,40 +1510,43 @@ void Generator::generateSignal(FunctionDef *def,int index)
}
int offset = 1;
- for (int j = 0; j < def->arguments.count(); ++j) {
- const ArgumentDef &a = def->arguments.at(j);
- if (j)
- fprintf(out, ", ");
- fprintf(out, "%s _t%d%s", a.type.name.constData(), offset++, a.rightType.constData());
+ const auto begin = def->arguments.cbegin();
+ const auto end = def->arguments.cend();
+ for (auto it = begin; it != end; ++it) {
+ const ArgumentDef &a = *it;
+ if (it != begin)
+ fputs(", ", out);
+ if (a.type.name.size())
+ fputs(a.type.name.constData(), out);
+ fprintf(out, " _t%d", offset++);
+ if (a.rightType.size())
+ fputs(a.rightType.constData(), out);
}
if (def->isPrivateSignal) {
if (!def->arguments.isEmpty())
fprintf(out, ", ");
- fprintf(out, "QPrivateSignal");
+ fprintf(out, "QPrivateSignal _t%d", offset++);
}
fprintf(out, ")%s\n{\n", constQualifier);
if (def->type.name.size() && def->normalizedType != "void") {
QByteArray returnType = noRef(def->normalizedType);
- if (returnType.endsWith('*')) {
- fprintf(out, " %s _t0 = 0;\n", returnType.constData());
- } else {
- fprintf(out, " %s _t0 = %s();\n", returnType.constData(), returnType.constData());
- }
+ fprintf(out, " %s _t0{};\n", returnType.constData());
}
fprintf(out, " void *_a[] = { ");
if (def->normalizedType == "void") {
fprintf(out, "nullptr");
} else {
- fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(&_t0))");
+ // -- QtScxml removed unused returnTypeIsVolatile
+ fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t0)))");
}
int i;
for (i = 1; i < offset; ++i)
- if (def->arguments.at(i - 1).type.isVolatile)
- fprintf(out, ", const_cast<void*>(reinterpret_cast<const volatile void*>(&_t%d))", i);
+ if (i <= def->arguments.size() && def->arguments.at(i - 1).type.isVolatile)
+ fprintf(out, ", const_cast<void*>(reinterpret_cast<const volatile void*>(std::addressof(_t%d)))", i);
else
- fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(&_t%d))", i);
+ fprintf(out, ", const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t%d)))", i);
fprintf(out, " };\n");
fprintf(out, " QMetaObject::activate(%s, &staticMetaObject, %d, _a);\n", thisPtr.constData(), index);
if (def->normalizedType != "void")
@@ -1520,6 +1554,7 @@ void Generator::generateSignal(FunctionDef *def,int index)
fprintf(out, "}\n");
}
+// -- QtScxml
void Generator::generateAccessorDefs()
{
for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
@@ -1549,31 +1584,55 @@ void Generator::generateSignalDefs()
}
#if 0
-static void writePluginMetaData(FILE *out, const QJsonObject &data)
+static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v);
+static CborError jsonObjectToCbor(CborEncoder *parent, const QJsonObject &o)
{
- const QJsonDocument doc(data);
+ auto it = o.constBegin();
+ auto end = o.constEnd();
+ CborEncoder map;
+ cbor_encoder_create_map(parent, &map, o.size());
+
+ for ( ; it != end; ++it) {
+ QByteArray key = it.key().toUtf8();
+ cbor_encode_text_string(&map, key.constData(), key.size());
+ jsonValueToCbor(&map, it.value());
+ }
+ return cbor_encoder_close_container(parent, &map);
+}
- fputs("\nQT_PLUGIN_METADATA_SECTION\n"
- "static const unsigned char qt_pluginMetaData[] = {\n"
- " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', ' ',\n ", out);
-#if 0
- fprintf(out, "\"%s\";\n", doc.toJson().constData());
-#else
- const QByteArray binary = doc.toBinaryData();
- const int last = binary.size() - 1;
- for (int i = 0; i < last; ++i) {
- uchar c = (uchar)binary.at(i);
- if (c < 0x20 || c >= 0x7f)
- fprintf(out, " 0x%02x,", c);
- else if (c == '\'' || c == '\\')
- fprintf(out, " '\\%c',", c);
- else
- fprintf(out, " '%c', ", c);
- if (!((i + 1) % 8))
- fputs("\n ", out);
+static CborError jsonArrayToCbor(CborEncoder *parent, const QJsonArray &a)
+{
+ CborEncoder array;
+ cbor_encoder_create_array(parent, &array, a.size());
+ for (const QJsonValue v : a)
+ jsonValueToCbor(&array, v);
+ return cbor_encoder_close_container(parent, &array);
+}
+
+static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v)
+{
+ switch (v.type()) {
+ case QJsonValue::Null:
+ case QJsonValue::Undefined:
+ return cbor_encode_null(parent);
+ case QJsonValue::Bool:
+ return cbor_encode_boolean(parent, v.toBool());
+ case QJsonValue::Array:
+ return jsonArrayToCbor(parent, v.toArray());
+ case QJsonValue::Object:
+ return jsonObjectToCbor(parent, v.toObject());
+ case QJsonValue::String: {
+ QByteArray s = v.toString().toUtf8();
+ return cbor_encode_text_string(parent, s.constData(), s.size());
}
- fprintf(out, " 0x%02x\n};\n", (uchar)binary.at(last));
-#endif
+ case QJsonValue::Double: {
+ double d = v.toDouble();
+ if (d == floor(d) && fabs(d) <= (Q_INT64_C(1) << std::numeric_limits<double>::digits))
+ return cbor_encode_int(parent, qint64(d));
+ return cbor_encode_double(parent, d);
+ }
+ }
+ Q_UNREACHABLE_RETURN(CborUnknownError);
}
void Generator::generatePluginMetaData()
@@ -1581,40 +1640,90 @@ void Generator::generatePluginMetaData()
if (cdef->pluginData.iid.isEmpty())
return;
- // Write plugin meta data #ifdefed QT_NO_DEBUG with debug=false,
- // true, respectively.
+ auto outputCborData = [this]() {
+ CborDevice dev(out);
+ CborEncoder enc;
+ cbor_encoder_init_writer(&enc, CborDevice::callback, &dev);
- QJsonObject data;
- const QString debugKey = QStringLiteral("debug");
- data.insert(QStringLiteral("IID"), QLatin1String(cdef->pluginData.iid.constData()));
- data.insert(QStringLiteral("className"), QLatin1String(cdef->classname.constData()));
- data.insert(QStringLiteral("version"), (int)QT_VERSION);
- data.insert(debugKey, QJsonValue(false));
- data.insert(QStringLiteral("MetaData"), cdef->pluginData.metaData.object());
+ CborEncoder map;
+ cbor_encoder_create_map(&enc, &map, CborIndefiniteLength);
- // Add -M args from the command line:
- for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it)
- data.insert(it.key(), it.value());
+ dev.nextItem("\"IID\"");
+ cbor_encode_int(&map, int(QtPluginMetaDataKeys::IID));
+ cbor_encode_text_string(&map, cdef->pluginData.iid.constData(), cdef->pluginData.iid.size());
- fputs("\nQT_PLUGIN_METADATA_SECTION const uint qt_section_alignment_dummy = 42;\n\n"
- "#ifdef QT_NO_DEBUG\n", out);
- writePluginMetaData(out, data);
+ dev.nextItem("\"className\"");
+ cbor_encode_int(&map, int(QtPluginMetaDataKeys::ClassName));
+ cbor_encode_text_string(&map, cdef->classname.constData(), cdef->classname.size());
- fputs("\n#else // QT_NO_DEBUG\n", out);
+ QJsonObject o = cdef->pluginData.metaData.object();
+ if (!o.isEmpty()) {
+ dev.nextItem("\"MetaData\"");
+ cbor_encode_int(&map, int(QtPluginMetaDataKeys::MetaData));
+ jsonObjectToCbor(&map, o);
+ }
- data.remove(debugKey);
- data.insert(debugKey, QJsonValue(true));
- writePluginMetaData(out, data);
+ if (!cdef->pluginData.uri.isEmpty()) {
+ dev.nextItem("\"URI\"");
+ cbor_encode_int(&map, int(QtPluginMetaDataKeys::URI));
+ cbor_encode_text_string(&map, cdef->pluginData.uri.constData(), cdef->pluginData.uri.size());
+ }
- fputs("#endif // QT_NO_DEBUG\n\n", out);
+ // Add -M args from the command line:
+ for (auto it = cdef->pluginData.metaArgs.cbegin(), end = cdef->pluginData.metaArgs.cend(); it != end; ++it) {
+ const QJsonArray &a = it.value();
+ QByteArray key = it.key().toUtf8();
+ dev.nextItem(QByteArray("command-line \"" + key + "\"").constData());
+ cbor_encode_text_string(&map, key.constData(), key.size());
+ jsonArrayToCbor(&map, a);
+ }
+
+ // Close the CBOR map manually
+ dev.nextItem();
+ cbor_encoder_close_container(&enc, &map);
+ };
// 'Use' all namespaces.
- int pos = cdef->qualified.indexOf("::");
+ qsizetype pos = cdef->qualified.indexOf("::");
for ( ; pos != -1 ; pos = cdef->qualified.indexOf("::", pos + 2) )
fprintf(out, "using namespace %s;\n", cdef->qualified.left(pos).constData());
- fprintf(out, "QT_MOC_EXPORT_PLUGIN(%s, %s)\n\n",
+
+ fputs("\n#ifdef QT_MOC_EXPORT_PLUGIN_V2", out);
+
+ // Qt 6.3+ output
+ fprintf(out, "\nstatic constexpr unsigned char qt_pluginMetaDataV2_%s[] = {",
+ cdef->classname.constData());
+ outputCborData();
+ fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN_V2(%s, %s, qt_pluginMetaDataV2_%s)\n",
+ cdef->qualified.constData(), cdef->classname.constData(), cdef->classname.constData());
+
+ // compatibility with Qt 6.0-6.2
+ fprintf(out, "#else\nQT_PLUGIN_METADATA_SECTION\n"
+ "Q_CONSTINIT static constexpr unsigned char qt_pluginMetaData_%s[] = {\n"
+ " 'Q', 'T', 'M', 'E', 'T', 'A', 'D', 'A', 'T', 'A', ' ', '!',\n"
+ " // metadata version, Qt version, architectural requirements\n"
+ " 0, QT_VERSION_MAJOR, QT_VERSION_MINOR, qPluginArchRequirements(),",
+ cdef->classname.constData());
+ outputCborData();
+ fprintf(out, "\n};\nQT_MOC_EXPORT_PLUGIN(%s, %s)\n"
+ "#endif // QT_MOC_EXPORT_PLUGIN_V2\n",
cdef->qualified.constData(), cdef->classname.constData());
+
+ fputs("\n", out);
}
+
+QT_WARNING_DISABLE_GCC("-Wunused-function")
+QT_WARNING_DISABLE_CLANG("-Wunused-function")
+QT_WARNING_DISABLE_CLANG("-Wundefined-internal")
+QT_WARNING_DISABLE_MSVC(4334) // '<<': result of 32-bit shift implicitly converted to 64 bits (was 64-bit shift intended?)
+
+#define CBOR_ENCODER_WRITER_CONTROL 1
+#define CBOR_ENCODER_WRITE_FUNCTION CborDevice::callback
#endif
+// -- QtScxml
QT_END_NAMESPACE
+
+#if 0 // -- QtScxml
+#include "cborencoder.c"
+#endif // -- QtScxml
diff --git a/tools/qscxmlc/generator.h b/tools/qscxmlc/generator.h
index e1f10eb..8db1350 100644
--- a/tools/qscxmlc/generator.h
+++ b/tools/qscxmlc/generator.h
@@ -1,60 +1,47 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef GENERATOR_H
#define GENERATOR_H
#include "moc.h"
+
+// -- QtScxml
#include <QtCore/qhash.h>
-#include <QtCore/qvector.h>
+#include <QtCore/qlist.h>
#include <QtCore/qiodevice.h>
+// -- QtScxml
QT_BEGIN_NAMESPACE
class Generator
{
- QIODevice &out;
+ QIODevice &out; // -- QtScxml
ClassDef *cdef;
- QVector<uint> meta_data;
+ QList<uint> meta_data;
+
public:
- Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes, const QHash<QByteArray,
- QByteArray> &knownQObjectClasses, const QHash<QByteArray, QByteArray> &knownGadgets,
- QIODevice &outfile);
+ Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes,
+ const QHash<QByteArray, QByteArray> &knownQObjectClasses,
+ const QHash<QByteArray, QByteArray> &knownGadgets,
+ QIODevice &outfile, // -- QtScxml
+ bool requireCompleteTypes = false);
void generateCode();
+ qsizetype registeredStringsCount() { return strings.size(); };
+
+// -- QtScxml
void generateAccessorDefs();
void generateSignalDefs();
+// -- QtScxml
private:
bool registerableMetaType(const QByteArray &propertyType);
void registerClassInfoStrings();
void generateClassInfos();
void registerFunctionStrings(const QList<FunctionDef> &list);
- void generateFunctions(const QList<FunctionDef> &list, const char *functype, int type, int &paramsIndex);
+ void registerByteArrayVector(const QList<QByteArray> &list);
+ void generateFunctions(const QList<FunctionDef> &list, const char *functype, int type,
+ int &paramsIndex, int &initialMetatypeOffset);
void generateFunctionRevisions(const QList<FunctionDef> &list, const char *functype);
void generateFunctionParameters(const QList<FunctionDef> &list, const char *functype);
void generateTypeInfo(const QByteArray &typeName, bool allowEmptyName = false);
@@ -64,10 +51,13 @@ private:
void generateProperties();
void generateMetacall();
void generateStaticMetacall();
- void generateSignal(FunctionDef *def, int index);
-// void generatePluginMetaData();
+ void generateSignal(const FunctionDef *def, int index);
+#if 0 // -- QtScxml
+ void generatePluginMetaData();
+#endif // -- QtScxml
QMultiMap<QByteArray, int> automaticPropertyMetaTypesHelper();
- QMap<int, QMultiMap<QByteArray, int> > methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList);
+ QMap<int, QMultiMap<QByteArray, int>>
+ methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList);
void strreg(const QByteArray &); // registers a string
int stridx(const QByteArray &); // returns a string's id
@@ -76,6 +66,7 @@ private:
QList<QByteArray> metaTypes;
QHash<QByteArray, QByteArray> knownQObjectClasses;
QHash<QByteArray, QByteArray> knownGadgets;
+ bool requireCompleteTypes;
};
QT_END_NAMESPACE
diff --git a/tools/qscxmlc/main.cpp b/tools/qscxmlc/main.cpp
index ccc99ed..9e09dbf 100644
--- a/tools/qscxmlc/main.cpp
+++ b/tools/qscxmlc/main.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtScxml/qscxmltabledata.h>
#include "qscxmlc.h"
diff --git a/tools/qscxmlc/moc.cpp b/tools/qscxmlc/moc.cpp
new file mode 100644
index 0000000..aeb3cad
--- /dev/null
+++ b/tools/qscxmlc/moc.cpp
@@ -0,0 +1,2215 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// Copyright (C) 2019 Olivier Goffart <ogoffart@woboq.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+#include "moc.h"
+#include "generator.h"
+#include "qdatetime.h"
+#include "utils.h"
+#include "outputrevision.h"
+#include <QtCore/qfile.h>
+#include <QtCore/qfileinfo.h>
+#include <QtCore/qdir.h>
+#include <QtCore/qjsondocument.h>
+
+// -- QtScxml
+#include <QtCore/qjsonobject.h>
+// -- QtScxml
+
+QT_BEGIN_NAMESPACE
+
+using namespace Qt::StringLiterals;
+
+#if 0 // -- QtScxml
+// only moc needs this function
+static QByteArray normalizeType(const QByteArray &ba)
+{
+ return ba.size() ? normalizeTypeInternal(ba.constBegin(), ba.constEnd()) : ba;
+}
+
+const QByteArray &Moc::toFullyQualified(const QByteArray &name) const noexcept
+{
+ if (auto it = knownQObjectClasses.find(name); it != knownQObjectClasses.end())
+ return it.value();
+ if (auto it = knownGadgets.find(name); it != knownGadgets.end())
+ return it.value();
+ return name;
+}
+
+bool Moc::parseClassHead(ClassDef *def)
+{
+ // figure out whether this is a class declaration, or only a
+ // forward or variable declaration.
+ int i = 0;
+ Token token;
+ do {
+ token = lookup(i++);
+ if (token == COLON || token == LBRACE)
+ break;
+ if (token == SEMIC || token == RANGLE)
+ return false;
+ } while (token);
+
+ // support attributes like "class [[deprecated]]] name"
+ skipCxxAttributes();
+
+ if (!test(IDENTIFIER)) // typedef struct { ... }
+ return false;
+ QByteArray name = lexem();
+
+ // support "class IDENT name" and "class IDENT(IDENT) name"
+ // also support "class IDENT name (final|sealed|Q_DECL_FINAL)"
+ if (test(LPAREN)) {
+ until(RPAREN);
+ if (!test(IDENTIFIER))
+ return false;
+ name = lexem();
+ } else if (test(IDENTIFIER)) {
+ const QByteArray lex = lexem();
+ if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL")
+ name = lex;
+ }
+
+ def->qualified += name;
+ while (test(SCOPE)) {
+ def->qualified += lexem();
+ if (test(IDENTIFIER)) {
+ name = lexem();
+ def->qualified += name;
+ }
+ }
+ def->classname = name;
+
+ if (test(IDENTIFIER)) {
+ const QByteArray lex = lexem();
+ if (lex != "final" && lex != "sealed" && lex != "Q_DECL_FINAL")
+ return false;
+ }
+
+ if (test(COLON)) {
+ do {
+ test(VIRTUAL);
+ FunctionDef::Access access = FunctionDef::Public;
+ if (test(PRIVATE))
+ access = FunctionDef::Private;
+ else if (test(PROTECTED))
+ access = FunctionDef::Protected;
+ else
+ test(PUBLIC);
+ test(VIRTUAL);
+ const Type type = parseType();
+ // ignore the 'class Foo : BAR(Baz)' case
+ if (test(LPAREN)) {
+ until(RPAREN);
+ } else {
+ def->superclassList.push_back({type.name, toFullyQualified(type.name), access});
+ }
+ } while (test(COMMA));
+
+ if (!def->superclassList.isEmpty()
+ && knownGadgets.contains(def->superclassList.constFirst().classname)) {
+ // Q_GADGET subclasses are treated as Q_GADGETs
+ knownGadgets.insert(def->classname, def->qualified);
+ knownGadgets.insert(def->qualified, def->qualified);
+ }
+ }
+ if (!test(LBRACE))
+ return false;
+ def->begin = index - 1;
+ bool foundRBrace = until(RBRACE);
+ def->end = index;
+ index = def->begin + 1;
+ return foundRBrace;
+}
+
+Type Moc::parseType()
+{
+ Type type;
+ bool hasSignedOrUnsigned = false;
+ bool isVoid = false;
+ type.firstToken = lookup();
+ for (;;) {
+ skipCxxAttributes();
+ switch (next()) {
+ case SIGNED:
+ case UNSIGNED:
+ hasSignedOrUnsigned = true;
+ Q_FALLTHROUGH();
+ case CONST:
+ case VOLATILE:
+ type.name += lexem();
+ type.name += ' ';
+ if (lookup(0) == VOLATILE)
+ type.isVolatile = true;
+ continue;
+ case Q_MOC_COMPAT_TOKEN:
+ case Q_INVOKABLE_TOKEN:
+ case Q_SCRIPTABLE_TOKEN:
+ case Q_SIGNALS_TOKEN:
+ case Q_SLOTS_TOKEN:
+ case Q_SIGNAL_TOKEN:
+ case Q_SLOT_TOKEN:
+ type.name += lexem();
+ return type;
+ case NOTOKEN:
+ return type;
+ default:
+ prev();
+ break;
+ }
+ break;
+ }
+
+ skipCxxAttributes();
+ test(ENUM) || test(CLASS) || test(STRUCT);
+ for(;;) {
+ skipCxxAttributes();
+ switch (next()) {
+ case IDENTIFIER:
+ // void mySlot(unsigned myArg)
+ if (hasSignedOrUnsigned) {
+ prev();
+ break;
+ }
+ Q_FALLTHROUGH();
+ case CHAR:
+ case SHORT:
+ case INT:
+ case LONG:
+ type.name += lexem();
+ // preserve '[unsigned] long long', 'short int', 'long int', 'long double'
+ if (test(LONG) || test(INT) || test(DOUBLE)) {
+ type.name += ' ';
+ prev();
+ continue;
+ }
+ break;
+ case FLOAT:
+ case DOUBLE:
+ case VOID:
+ case BOOL:
+ case AUTO:
+ type.name += lexem();
+ isVoid |= (lookup(0) == VOID);
+ break;
+ case NOTOKEN:
+ return type;
+ default:
+ prev();
+ ;
+ }
+ if (test(LANGLE)) {
+ if (type.name.isEmpty()) {
+ // '<' cannot start a type
+ return type;
+ }
+ type.name += lexemUntil(RANGLE);
+ }
+ if (test(SCOPE)) {
+ type.name += lexem();
+ type.isScoped = true;
+ } else {
+ break;
+ }
+ }
+ while (test(CONST) || test(VOLATILE) || test(SIGNED) || test(UNSIGNED)
+ || test(STAR) || test(AND) || test(ANDAND)) {
+ type.name += ' ';
+ type.name += lexem();
+ if (lookup(0) == AND)
+ type.referenceType = Type::Reference;
+ else if (lookup(0) == ANDAND)
+ type.referenceType = Type::RValueReference;
+ else if (lookup(0) == STAR)
+ type.referenceType = Type::Pointer;
+ }
+ type.rawName = type.name;
+ // transform stupid things like 'const void' or 'void const' into 'void'
+ if (isVoid && type.referenceType == Type::NoReference) {
+ type.name = "void";
+ }
+ return type;
+}
+
+enum class IncludeState {
+ IncludeBegin,
+ IncludeEnd,
+ NoInclude,
+};
+
+bool Moc::parseEnum(EnumDef *def)
+{
+ bool isTypdefEnum = false; // typedef enum { ... } Foo;
+
+ if (test(CLASS) || test(STRUCT))
+ def->isEnumClass = true;
+
+ if (test(IDENTIFIER)) {
+ def->name = lexem();
+ } else {
+ if (lookup(-1) != TYPEDEF)
+ return false; // anonymous enum
+ isTypdefEnum = true;
+ }
+ if (test(COLON)) { // C++11 strongly typed enum
+ // enum Foo : unsigned long { ... };
+ def->type = normalizeType(parseType().name);
+ }
+ if (!test(LBRACE))
+ return false;
+ auto handleInclude = [this]() -> IncludeState {
+ bool hadIncludeBegin = false;
+ if (test(MOC_INCLUDE_BEGIN)) {
+ currentFilenames.push(symbol().unquotedLexem());
+ // we do not return early to handle empty headers in one go
+ hadIncludeBegin = true;
+ }
+ if (test(NOTOKEN)) {
+ next(MOC_INCLUDE_END);
+ currentFilenames.pop();
+ return IncludeState::IncludeEnd;
+ }
+ if (hadIncludeBegin)
+ return IncludeState::IncludeBegin;
+ else
+ return IncludeState::NoInclude;
+ };
+ do {
+ handleInclude();
+ if (lookup() == RBRACE) // accept trailing comma
+ break;
+ next(IDENTIFIER);
+ def->values += lexem();
+ handleInclude();
+ skipCxxAttributes();
+ } while (test(EQ) ? until(COMMA) : test(COMMA));
+ next(RBRACE);
+ if (isTypdefEnum) {
+ if (!test(IDENTIFIER))
+ return false;
+ def->name = lexem();
+ }
+ return true;
+}
+
+void Moc::parseFunctionArguments(FunctionDef *def)
+{
+ Q_UNUSED(def);
+ while (hasNext()) {
+ ArgumentDef arg;
+ arg.type = parseType();
+ if (arg.type.name == "void")
+ break;
+ if (test(IDENTIFIER))
+ arg.name = lexem();
+ while (test(LBRACK)) {
+ arg.rightType += lexemUntil(RBRACK);
+ }
+ if (test(CONST) || test(VOLATILE)) {
+ arg.rightType += ' ';
+ arg.rightType += lexem();
+ }
+ arg.normalizedType = normalizeType(QByteArray(arg.type.name + ' ' + arg.rightType));
+ arg.typeNameForCast = QByteArray("std::add_pointer_t<"+arg.normalizedType+">");
+ if (test(EQ))
+ arg.isDefault = true;
+ def->arguments += arg;
+ if (!until(COMMA))
+ break;
+ }
+
+ if (!def->arguments.isEmpty()
+ && def->arguments.constLast().normalizedType == "QPrivateSignal") {
+ def->arguments.removeLast();
+ def->isPrivateSignal = true;
+ }
+ if (def->arguments.size() == 1
+ && def->arguments.constLast().normalizedType == "QMethodRawArguments") {
+ def->arguments.removeLast();
+ def->isRawSlot = true;
+ }
+
+ if (Q_UNLIKELY(def->arguments.size() >= std::numeric_limits<int>::max()))
+ error("number of function arguments exceeds std::numeric_limits<int>::max()");
+}
+
+bool Moc::testFunctionAttribute(FunctionDef *def)
+{
+ if (index < symbols.size() && testFunctionAttribute(symbols.at(index).token, def)) {
+ ++index;
+ return true;
+ }
+ return false;
+}
+
+bool Moc::testFunctionAttribute(Token tok, FunctionDef *def)
+{
+ switch (tok) {
+ case Q_MOC_COMPAT_TOKEN:
+ def->isCompat = true;
+ return true;
+ case Q_INVOKABLE_TOKEN:
+ def->isInvokable = true;
+ return true;
+ case Q_SIGNAL_TOKEN:
+ def->isSignal = true;
+ return true;
+ case Q_SLOT_TOKEN:
+ def->isSlot = true;
+ return true;
+ case Q_SCRIPTABLE_TOKEN:
+ def->isInvokable = def->isScriptable = true;
+ return true;
+ default: break;
+ }
+ return false;
+}
+
+bool Moc::skipCxxAttributes()
+{
+ auto rewind = index;
+ if (test(LBRACK) && test(LBRACK) && until(RBRACK) && test(RBRACK))
+ return true;
+ index = rewind;
+ return false;
+}
+
+QTypeRevision Moc::parseRevision()
+{
+ next(LPAREN);
+ QByteArray revisionString = lexemUntil(RPAREN);
+ revisionString.remove(0, 1);
+ revisionString.chop(1);
+ const QList<QByteArray> majorMinor = revisionString.split(',');
+ switch (majorMinor.size()) {
+ case 1: {
+ bool ok = false;
+ const int revision = revisionString.toInt(&ok);
+ if (!ok || !QTypeRevision::isValidSegment(revision))
+ error("Invalid revision");
+ return QTypeRevision::fromMinorVersion(revision);
+ }
+ case 2: { // major.minor
+ bool ok = false;
+ const int major = majorMinor[0].toInt(&ok);
+ if (!ok || !QTypeRevision::isValidSegment(major))
+ error("Invalid major version");
+ const int minor = majorMinor[1].toInt(&ok);
+ if (!ok || !QTypeRevision::isValidSegment(minor))
+ error("Invalid minor version");
+ return QTypeRevision::fromVersion(major, minor);
+ }
+ default:
+ error("Invalid revision");
+ return QTypeRevision();
+ }
+}
+
+bool Moc::testFunctionRevision(FunctionDef *def)
+{
+
+ if (test(Q_REVISION_TOKEN)) {
+ def->revision = parseRevision().toEncodedVersion<int>();
+ return true;
+ }
+
+ return false;
+}
+
+// returns false if the function should be ignored
+bool Moc::parseFunction(FunctionDef *def, bool inMacro)
+{
+ def->isVirtual = false;
+ def->isStatic = false;
+ //skip modifiers and attributes
+ while (testForFunctionModifiers(def)
+ || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {}
+ bool templateFunction = (lookup() == TEMPLATE);
+ def->type = parseType();
+ if (def->type.name.isEmpty()) {
+ if (templateFunction)
+ error("Template function as signal or slot");
+ else
+ error();
+ }
+ bool scopedFunctionName = false;
+ // we might have modifiers and attributes after a tag
+ // note that testFunctionAttribute is handled further below,
+ // and revisions and attributes must come first
+ while (testForFunctionModifiers(def)) {}
+ Type tempType = parseType();;
+ while (!tempType.name.isEmpty() && lookup() != LPAREN) {
+ if (testFunctionAttribute(def->type.firstToken, def))
+ ; // fine
+ else if (def->type.firstToken == Q_SIGNALS_TOKEN)
+ error();
+ else if (def->type.firstToken == Q_SLOTS_TOKEN)
+ error();
+ else {
+ if (!def->tag.isEmpty())
+ def->tag += ' ';
+ def->tag += def->type.name;
+ }
+ def->type = tempType;
+ tempType = parseType();
+ }
+ next(LPAREN, "Not a signal or slot declaration");
+ def->name = tempType.name;
+ scopedFunctionName = tempType.isScoped;
+
+ if (!test(RPAREN)) {
+ parseFunctionArguments(def);
+ next(RPAREN);
+ }
+
+ // support optional macros with compiler specific options
+ while (test(IDENTIFIER))
+ ;
+
+ def->isConst = test(CONST);
+
+ while (test(IDENTIFIER))
+ ;
+
+ if (inMacro) {
+ next(RPAREN);
+ prev();
+ } else {
+ if (test(THROW)) {
+ next(LPAREN);
+ until(RPAREN);
+ }
+
+ if (def->type.name == "auto" && test(ARROW))
+ def->type = parseType(); // Parse trailing return-type
+
+ if (test(SEMIC))
+ ;
+ else if ((def->inlineCode = test(LBRACE)))
+ until(RBRACE);
+ else if ((def->isAbstract = test(EQ)))
+ until(SEMIC);
+ else if (skipCxxAttributes())
+ until(SEMIC);
+ else
+ error();
+ }
+ if (scopedFunctionName) {
+ const QByteArray msg = "Function declaration " + def->name
+ + " contains extra qualification. Ignoring as signal or slot.";
+ warning(msg.constData());
+ return false;
+ }
+
+ QList<QByteArray> typeNameParts = normalizeType(def->type.name).split(' ');
+ if (typeNameParts.contains("auto")) {
+ // We expected a trailing return type but we haven't seen one
+ error("Function declared with auto as return type but missing trailing return type. "
+ "Return type deduction is not supported.");
+ }
+
+ // we don't support references as return types, it's too dangerous
+ if (def->type.referenceType == Type::Reference) {
+ QByteArray rawName = def->type.rawName;
+ def->type = Type("void");
+ def->type.rawName = rawName;
+ }
+
+ def->normalizedType = normalizeType(def->type.name);
+ return true;
+}
+
+bool Moc::testForFunctionModifiers(FunctionDef *def)
+{
+ return test(EXPLICIT) || test(INLINE) ||
+ (test(STATIC) && (def->isStatic = true)) ||
+ (test(VIRTUAL) && (def->isVirtual = true));
+}
+
+// like parseFunction, but never aborts with an error
+bool Moc::parseMaybeFunction(const ClassDef *cdef, FunctionDef *def)
+{
+ def->isVirtual = false;
+ def->isStatic = false;
+ //skip modifiers and attributes
+ while (testForFunctionModifiers(def)
+ || skipCxxAttributes() || testFunctionAttribute(def) || testFunctionRevision(def)) {}
+ bool tilde = test(TILDE);
+ def->type = parseType();
+ if (def->type.name.isEmpty())
+ return false;
+ bool scopedFunctionName = false;
+ if (test(LPAREN)) {
+ def->name = def->type.name;
+ scopedFunctionName = def->type.isScoped;
+ if (def->name == cdef->classname) {
+ def->isDestructor = tilde;
+ def->isConstructor = !tilde;
+ def->type = Type();
+ } else {
+ // missing type name? => Skip
+ return false;
+ }
+ } else {
+ // ### TODO: The condition before testForFunctionModifiers shoulnd't be necessary,
+ // but otherwise we end up with misparses
+ if (def->isSlot || def->isSignal || def->isInvokable)
+ while (testForFunctionModifiers(def)) {}
+ Type tempType = parseType();;
+ while (!tempType.name.isEmpty() && lookup() != LPAREN) {
+ if (testFunctionAttribute(def->type.firstToken, def))
+ ; // fine
+ else if (def->type.name == "Q_SIGNAL")
+ def->isSignal = true;
+ else if (def->type.name == "Q_SLOT")
+ def->isSlot = true;
+ else {
+ if (!def->tag.isEmpty())
+ def->tag += ' ';
+ def->tag += def->type.name;
+ }
+ def->type = tempType;
+ tempType = parseType();
+ }
+ if (!test(LPAREN))
+ return false;
+ def->name = tempType.name;
+ scopedFunctionName = tempType.isScoped;
+ }
+
+ // we don't support references as return types, it's too dangerous
+ if (def->type.referenceType == Type::Reference) {
+ QByteArray rawName = def->type.rawName;
+ def->type = Type("void");
+ def->type.rawName = rawName;
+ }
+
+ def->normalizedType = normalizeType(def->type.name);
+
+ if (!test(RPAREN)) {
+ parseFunctionArguments(def);
+ if (!test(RPAREN))
+ return false;
+ }
+ def->isConst = test(CONST);
+ if (scopedFunctionName
+ && (def->isSignal || def->isSlot || def->isInvokable)) {
+ const QByteArray msg = "parsemaybe: Function declaration " + def->name
+ + " contains extra qualification. Ignoring as signal or slot.";
+ warning(msg.constData());
+ return false;
+ }
+ return true;
+}
+
+inline void handleDefaultArguments(QList<FunctionDef> *functionList, FunctionDef &function)
+{
+ // support a function with a default argument by pretending there is an
+ // overload without the argument (the original function is the overload with
+ // all arguments present)
+ while (function.arguments.size() > 0 && function.arguments.constLast().isDefault) {
+ function.wasCloned = true;
+ function.arguments.removeLast();
+ *functionList += function;
+ }
+}
+
+void Moc::prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const
+{
+ auto it = namespaceList.crbegin();
+ const auto rend = namespaceList.crend();
+ for (; it != rend; ++it) {
+ if (inNamespace(&*it))
+ def.qualified.prepend(it->classname + "::");
+ }
+}
+
+void Moc::checkListSizes(const ClassDef &def)
+{
+ if (Q_UNLIKELY(def.nonClassSignalList.size() > std::numeric_limits<int>::max()))
+ error("number of signals defined in parent class(es) exceeds "
+ "std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.propertyList.size() > std::numeric_limits<int>::max()))
+ error("number of bindable properties exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.classInfoList.size() > std::numeric_limits<int>::max()))
+ error("number of times Q_CLASSINFO macro is used exceeds "
+ "std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.enumList.size() > std::numeric_limits<int>::max()))
+ error("number of enumerations exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.superclassList.size() > std::numeric_limits<int>::max()))
+ error("number of super classes exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.constructorList.size() > std::numeric_limits<int>::max()))
+ error("number of constructor parameters exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.signalList.size() > std::numeric_limits<int>::max()))
+ error("number of signals exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.slotList.size() > std::numeric_limits<int>::max()))
+ error("number of declared slots exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.methodList.size() > std::numeric_limits<int>::max()))
+ error("number of methods exceeds std::numeric_limits<int>::max().");
+
+ if (Q_UNLIKELY(def.publicList.size() > std::numeric_limits<int>::max()))
+ error("number of public functions declared in this class exceeds "
+ "std::numeric_limits<int>::max().");
+}
+
+void Moc::parse()
+{
+ QList<NamespaceDef> namespaceList;
+ bool templateClass = false;
+ while (hasNext()) {
+ Token t = next();
+ switch (t) {
+ case NAMESPACE: {
+ qsizetype rewind = index;
+ if (test(IDENTIFIER)) {
+ QByteArray nsName = lexem();
+ QByteArrayList nested;
+ while (test(SCOPE)) {
+ /* treat (C++20's) namespace A::inline B {} as A::B
+ this is mostly to not break compilation when encountering such
+ a construct in a header; the interaction of Qt's meta-macros with
+ inline namespaces is still rather poor.
+ */
+ test(INLINE);
+ next(IDENTIFIER);
+ nested.append(nsName);
+ nsName = lexem();
+ }
+ if (test(EQ)) {
+ // namespace Foo = Bar::Baz;
+ until(SEMIC);
+ } else if (test(LPAREN)) {
+ // Ignore invalid code such as: 'namespace __identifier("x")' (QTBUG-56634)
+ until(RPAREN);
+ } else if (!test(SEMIC)) {
+ NamespaceDef def;
+ def.classname = nsName;
+ def.doGenerate = currentFilenames.size() <= 1;
+
+ next(LBRACE);
+ def.begin = index - 1;
+ until(RBRACE);
+ def.end = index;
+ index = def.begin + 1;
+
+ prependNamespaces(def, namespaceList);
+
+ for (const QByteArray &ns : nested) {
+ NamespaceDef parentNs;
+ parentNs.classname = ns;
+ parentNs.qualified = def.qualified;
+ def.qualified += ns + "::";
+ parentNs.begin = def.begin;
+ parentNs.end = def.end;
+ namespaceList += parentNs;
+ }
+
+ while (inNamespace(&def) && hasNext()) {
+ switch (next()) {
+ case NAMESPACE:
+ if (test(IDENTIFIER)) {
+ while (test(SCOPE)) {
+ test(INLINE); // ignore inline namespaces
+ next(IDENTIFIER);
+ }
+ if (test(EQ)) {
+ // namespace Foo = Bar::Baz;
+ until(SEMIC);
+ } else if (!test(SEMIC)) {
+ until(RBRACE);
+ }
+ }
+ break;
+ case Q_NAMESPACE_TOKEN:
+ def.hasQNamespace = true;
+ break;
+ case Q_NAMESPACE_EXPORT_TOKEN:
+ next(LPAREN);
+ while (test(IDENTIFIER))
+ {}
+ next(RPAREN);
+ def.hasQNamespace = true;
+ break;
+ case Q_ENUMS_TOKEN:
+ case Q_ENUM_NS_TOKEN:
+ parseEnumOrFlag(&def, false);
+ break;
+ case Q_ENUM_TOKEN:
+ error("Q_ENUM can't be used in a Q_NAMESPACE, use Q_ENUM_NS instead");
+ break;
+ case Q_FLAGS_TOKEN:
+ case Q_FLAG_NS_TOKEN:
+ parseEnumOrFlag(&def, true);
+ break;
+ case Q_FLAG_TOKEN:
+ error("Q_FLAG can't be used in a Q_NAMESPACE, use Q_FLAG_NS instead");
+ break;
+ case Q_DECLARE_FLAGS_TOKEN:
+ parseFlag(&def);
+ break;
+ case Q_CLASSINFO_TOKEN:
+ parseClassInfo(&def);
+ break;
+ case Q_MOC_INCLUDE_TOKEN:
+ // skip it, the namespace is parsed twice
+ next(LPAREN);
+ lexemUntil(RPAREN);
+ break;
+ case ENUM: {
+ EnumDef enumDef;
+ if (parseEnum(&enumDef))
+ def.enumList += enumDef;
+ } break;
+ case CLASS:
+ case STRUCT: {
+ ClassDef classdef;
+ if (!parseClassHead(&classdef))
+ continue;
+ while (inClass(&classdef) && hasNext())
+ next(); // consume all Q_XXXX macros from this class
+ } break;
+ default: break;
+ }
+ }
+ namespaceList += def;
+ index = rewind;
+ if (!def.hasQNamespace && (!def.classInfoList.isEmpty() || !def.enumDeclarations.isEmpty()))
+ error("Namespace declaration lacks Q_NAMESPACE macro.");
+ }
+ }
+ break;
+ }
+ case SEMIC:
+ case RBRACE:
+ templateClass = false;
+ break;
+ case TEMPLATE:
+ templateClass = true;
+ break;
+ case MOC_INCLUDE_BEGIN:
+ currentFilenames.push(symbol().unquotedLexem());
+ break;
+ case MOC_INCLUDE_END:
+ currentFilenames.pop();
+ break;
+ case Q_DECLARE_INTERFACE_TOKEN:
+ parseDeclareInterface();
+ break;
+ case Q_DECLARE_METATYPE_TOKEN:
+ parseDeclareMetatype();
+ break;
+ case Q_MOC_INCLUDE_TOKEN:
+ parseMocInclude();
+ break;
+ case USING:
+ if (test(NAMESPACE)) {
+ while (test(SCOPE) || test(IDENTIFIER))
+ ;
+ // Ignore invalid code such as: 'using namespace __identifier("x")' (QTBUG-63772)
+ if (test(LPAREN))
+ until(RPAREN);
+ next(SEMIC);
+ }
+ break;
+ case CLASS:
+ case STRUCT: {
+ if (currentFilenames.size() <= 1)
+ break;
+
+ ClassDef def;
+ if (!parseClassHead(&def))
+ continue;
+
+ while (inClass(&def) && hasNext()) {
+ switch (next()) {
+ case Q_OBJECT_TOKEN:
+ def.hasQObject = true;
+ break;
+ case Q_GADGET_EXPORT_TOKEN:
+ next(LPAREN);
+ while (test(IDENTIFIER))
+ {}
+ next(RPAREN);
+ Q_FALLTHROUGH();
+ case Q_GADGET_TOKEN:
+ def.hasQGadget = true;
+ break;
+ default: break;
+ }
+ }
+
+ if (!def.hasQObject && !def.hasQGadget)
+ continue;
+
+ prependNamespaces(def, namespaceList);
+
+ QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
+ classHash.insert(def.classname, def.qualified);
+ classHash.insert(def.qualified, def.qualified);
+
+ continue; }
+ default: break;
+ }
+ if ((t != CLASS && t != STRUCT)|| currentFilenames.size() > 1)
+ continue;
+ ClassDef def;
+ if (parseClassHead(&def)) {
+ prependNamespaces(def, namespaceList);
+
+ FunctionDef::Access access = FunctionDef::Private;
+ while (inClass(&def) && hasNext()) {
+ switch ((t = next())) {
+ case PRIVATE:
+ access = FunctionDef::Private;
+ if (test(Q_SIGNALS_TOKEN))
+ error("Signals cannot have access specifier");
+ break;
+ case PROTECTED:
+ access = FunctionDef::Protected;
+ if (test(Q_SIGNALS_TOKEN))
+ error("Signals cannot have access specifier");
+ break;
+ case PUBLIC:
+ access = FunctionDef::Public;
+ if (test(Q_SIGNALS_TOKEN))
+ error("Signals cannot have access specifier");
+ break;
+ case CLASS: {
+ ClassDef nestedDef;
+ if (parseClassHead(&nestedDef)) {
+ while (inClass(&nestedDef) && inClass(&def)) {
+ t = next();
+ if (t >= Q_META_TOKEN_BEGIN && t < Q_META_TOKEN_END)
+ error("Meta object features not supported for nested classes");
+ }
+ }
+ } break;
+ case Q_SIGNALS_TOKEN:
+ parseSignals(&def);
+ break;
+ case Q_SLOTS_TOKEN:
+ switch (lookup(-1)) {
+ case PUBLIC:
+ case PROTECTED:
+ case PRIVATE:
+ parseSlots(&def, access);
+ break;
+ default:
+ error("Missing access specifier for slots");
+ }
+ break;
+ case Q_OBJECT_TOKEN:
+ def.hasQObject = true;
+ if (templateClass)
+ error("Template classes not supported by Q_OBJECT");
+ if (def.classname != "Qt" && def.classname != "QObject" && def.superclassList.isEmpty())
+ error("Class contains Q_OBJECT macro but does not inherit from QObject");
+ break;
+ case Q_GADGET_EXPORT_TOKEN:
+ next(LPAREN);
+ while (test(IDENTIFIER))
+ {}
+ next(RPAREN);
+ Q_FALLTHROUGH();
+ case Q_GADGET_TOKEN:
+ def.hasQGadget = true;
+ if (templateClass)
+ error("Template classes not supported by Q_GADGET");
+ break;
+ case Q_PROPERTY_TOKEN:
+ parseProperty(&def, Named);
+ break;
+ case QT_ANONYMOUS_PROPERTY_TOKEN:
+ parseProperty(&def, Anonymous);
+ break;
+ case Q_PLUGIN_METADATA_TOKEN:
+ parsePluginData(&def);
+ break;
+ case Q_ENUMS_TOKEN:
+ case Q_ENUM_TOKEN:
+ parseEnumOrFlag(&def, false);
+ break;
+ case Q_ENUM_NS_TOKEN:
+ error("Q_ENUM_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_ENUM instead");
+ break;
+ case Q_FLAGS_TOKEN:
+ case Q_FLAG_TOKEN:
+ parseEnumOrFlag(&def, true);
+ break;
+ case Q_FLAG_NS_TOKEN:
+ error("Q_FLAG_NS can't be used in a Q_OBJECT/Q_GADGET, use Q_FLAG instead");
+ break;
+ case Q_DECLARE_FLAGS_TOKEN:
+ parseFlag(&def);
+ break;
+ case Q_CLASSINFO_TOKEN:
+ parseClassInfo(&def);
+ break;
+ case Q_MOC_INCLUDE_TOKEN:
+ parseMocInclude();
+ break;
+ case Q_INTERFACES_TOKEN:
+ parseInterfaces(&def);
+ break;
+ case Q_PRIVATE_SLOT_TOKEN:
+ parseSlotInPrivate(&def, access);
+ break;
+ case Q_PRIVATE_PROPERTY_TOKEN:
+ parsePrivateProperty(&def, Named);
+ break;
+ case QT_ANONYMOUS_PRIVATE_PROPERTY_TOKEN:
+ parsePrivateProperty(&def, Anonymous);
+ break;
+ case ENUM: {
+ EnumDef enumDef;
+ if (parseEnum(&enumDef))
+ def.enumList += enumDef;
+ } break;
+ case SEMIC:
+ case COLON:
+ break;
+ default:
+ FunctionDef funcDef;
+ funcDef.access = access;
+ qsizetype rewind = index--;
+ if (parseMaybeFunction(&def, &funcDef)) {
+ if (funcDef.isConstructor) {
+ if ((access == FunctionDef::Public) && funcDef.isInvokable) {
+ def.constructorList += funcDef;
+ handleDefaultArguments(&def.constructorList, funcDef);
+ }
+ } else if (funcDef.isDestructor) {
+ // don't care about destructors
+ } else {
+ if (access == FunctionDef::Public)
+ def.publicList += funcDef;
+ if (funcDef.isSlot) {
+ def.slotList += funcDef;
+ handleDefaultArguments(&def.slotList, funcDef);
+ if (funcDef.revision > 0)
+ ++def.revisionedMethods;
+ } else if (funcDef.isSignal) {
+ def.signalList += funcDef;
+ handleDefaultArguments(&def.signalList, funcDef);
+ if (funcDef.revision > 0)
+ ++def.revisionedMethods;
+ } else if (funcDef.isInvokable) {
+ def.methodList += funcDef;
+ handleDefaultArguments(&def.methodList, funcDef);
+ if (funcDef.revision > 0)
+ ++def.revisionedMethods;
+ }
+ }
+ } else {
+ index = rewind;
+ }
+ }
+ }
+
+ next(RBRACE);
+
+ if (!def.hasQObject && !def.hasQGadget && def.signalList.isEmpty() && def.slotList.isEmpty()
+ && def.propertyList.isEmpty() && def.enumDeclarations.isEmpty())
+ continue; // no meta object code required
+
+
+ if (!def.hasQObject && !def.hasQGadget)
+ error("Class declaration lacks Q_OBJECT macro.");
+
+ // Add meta tags to the plugin meta data:
+ if (!def.pluginData.iid.isEmpty())
+ def.pluginData.metaArgs = metaArgs;
+
+ if (def.hasQObject && !def.superclassList.isEmpty())
+ checkSuperClasses(&def);
+
+ checkProperties(&def);
+
+ checkListSizes(def);
+
+ classList += def;
+ QHash<QByteArray, QByteArray> &classHash = def.hasQObject ? knownQObjectClasses : knownGadgets;
+ classHash.insert(def.classname, def.qualified);
+ classHash.insert(def.qualified, def.qualified);
+ }
+ }
+ for (const auto &n : std::as_const(namespaceList)) {
+ if (!n.hasQNamespace)
+ continue;
+ ClassDef def;
+ static_cast<BaseDef &>(def) = static_cast<BaseDef>(n);
+ def.qualified += def.classname;
+ def.hasQNamespace = true;
+ auto it = std::find_if(classList.begin(), classList.end(), [&def](const ClassDef &val) {
+ return def.classname == val.classname && def.qualified == val.qualified;
+ });
+
+ if (it != classList.end()) {
+ it->classInfoList += def.classInfoList;
+ Q_ASSERT(it->classInfoList.size() <= std::numeric_limits<int>::max());
+ it->enumDeclarations.insert(def.enumDeclarations);
+ it->enumList += def.enumList;
+ Q_ASSERT(it->enumList.size() <= std::numeric_limits<int>::max());
+ it->flagAliases.insert(def.flagAliases);
+ } else {
+ knownGadgets.insert(def.classname, def.qualified);
+ knownGadgets.insert(def.qualified, def.qualified);
+ if (n.doGenerate)
+ classList += def;
+ }
+ }
+}
+
+static bool any_type_contains(const QList<PropertyDef> &properties, const QByteArray &pattern)
+{
+ for (const auto &p : properties) {
+ if (p.type.contains(pattern))
+ return true;
+ }
+ return false;
+}
+
+static bool any_arg_contains(const QList<FunctionDef> &functions, const QByteArray &pattern)
+{
+ for (const auto &f : functions) {
+ for (const auto &arg : f.arguments) {
+ if (arg.normalizedType.contains(pattern))
+ return true;
+ }
+ }
+ return false;
+}
+
+static QByteArrayList make_candidates()
+{
+ QByteArrayList result;
+ result
+#define STREAM_SMART_POINTER(SMART_POINTER) << #SMART_POINTER
+ QT_FOR_EACH_AUTOMATIC_TEMPLATE_SMART_POINTER(STREAM_SMART_POINTER)
+#undef STREAM_SMART_POINTER
+#define STREAM_1ARG_TEMPLATE(TEMPLATENAME) << #TEMPLATENAME
+ QT_FOR_EACH_AUTOMATIC_TEMPLATE_1ARG(STREAM_1ARG_TEMPLATE)
+#undef STREAM_1ARG_TEMPLATE
+ ;
+ return result;
+}
+
+static QByteArrayList requiredQtContainers(const QList<ClassDef> &classes)
+{
+ static const QByteArrayList candidates = make_candidates();
+
+ QByteArrayList required;
+ required.reserve(candidates.size());
+
+ bool needsQProperty = false;
+
+ for (const auto &candidate : candidates) {
+ const QByteArray pattern = candidate + '<';
+
+ for (const auto &c : classes) {
+ for (const auto &p : c.propertyList)
+ needsQProperty |= !p.bind.isEmpty();
+ if (any_type_contains(c.propertyList, pattern) ||
+ any_arg_contains(c.slotList, pattern) ||
+ any_arg_contains(c.signalList, pattern) ||
+ any_arg_contains(c.methodList, pattern)) {
+ required.push_back(candidate);
+ break;
+ }
+ }
+ }
+
+ if (needsQProperty)
+ required.push_back("QProperty");
+
+ return required;
+}
+
+void Moc::generate(FILE *out, FILE *jsonOutput)
+{
+ QByteArrayView fn = QByteArrayView(filename);
+
+ auto isSlash = [](char ch) { return ch == '/' || ch == '\\'; };
+ auto rit = std::find_if(fn.crbegin(), fn.crend(), isSlash);
+ if (rit != fn.crend())
+ fn = fn.last(rit - fn.crbegin());
+
+ fprintf(out, "/****************************************************************************\n"
+ "** Meta object code from reading C++ file '%s'\n**\n" , fn.constData());
+ fprintf(out, "** Created by: The Qt Meta Object Compiler version %d (Qt %s)\n**\n" , mocOutputRevision, QT_VERSION_STR);
+ fprintf(out, "** WARNING! All changes made in this file will be lost!\n"
+ "*****************************************************************************/\n\n");
+
+ // include header(s) of user class definitions at _first_ to allow
+ // for preprocessor definitions possibly affecting standard headers.
+ // see https://codereview.qt-project.org/c/qt/qtbase/+/445937
+ if (!noInclude) {
+ if (includePath.size() && !includePath.endsWith('/'))
+ includePath += '/';
+ for (QByteArray inc : std::as_const(includeFiles)) {
+ if (!inc.isEmpty() && inc.at(0) != '<' && inc.at(0) != '"') {
+ if (includePath.size() && includePath != "./")
+ inc.prepend(includePath);
+ inc = '\"' + inc + '\"';
+ }
+ fprintf(out, "#include %s\n", inc.constData());
+ }
+ }
+ if (classList.size() && classList.constFirst().classname == "Qt")
+ fprintf(out, "#include <QtCore/qobject.h>\n");
+
+ fprintf(out, "#include <QtCore/qmetatype.h>\n"); // For QMetaType::Type
+ if (mustIncludeQPluginH)
+ fprintf(out, "#include <QtCore/qplugin.h>\n");
+
+ const auto qtContainers = requiredQtContainers(classList);
+ for (const QByteArray &qtContainer : qtContainers)
+ fprintf(out, "#include <QtCore/%s>\n", qtContainer.constData());
+
+ fprintf(out, "\n#include <QtCore/qtmochelpers.h>\n");
+
+ fprintf(out, "\n#include <memory>\n\n"); // For std::addressof
+ fprintf(out, "\n#include <QtCore/qxptype_traits.h>\n"); // is_detected
+
+ fprintf(out, "#if !defined(Q_MOC_OUTPUT_REVISION)\n"
+ "#error \"The header file '%s' doesn't include <QObject>.\"\n", fn.constData());
+ fprintf(out, "#elif Q_MOC_OUTPUT_REVISION != %d\n", mocOutputRevision);
+ fprintf(out, "#error \"This file was generated using the moc from %s."
+ " It\"\n#error \"cannot be used with the include files from"
+ " this version of Qt.\"\n#error \"(The moc has changed too"
+ " much.)\"\n", QT_VERSION_STR);
+ fprintf(out, "#endif\n\n");
+
+#if QT_VERSION <= QT_VERSION_CHECK(7, 0, 0)
+ fprintf(out, "#ifndef Q_CONSTINIT\n"
+ "#define Q_CONSTINIT\n"
+ "#endif\n\n");
+#endif
+
+ fprintf(out, "QT_WARNING_PUSH\n");
+ fprintf(out, "QT_WARNING_DISABLE_DEPRECATED\n");
+ fprintf(out, "QT_WARNING_DISABLE_GCC(\"-Wuseless-cast\")\n");
+
+ fputs("", out);
+ for (ClassDef &def : classList) {
+ Generator generator(this, &def, metaTypes, knownQObjectClasses, knownGadgets, out,
+ requireCompleteTypes);
+ generator.generateCode();
+
+ // generator.generateCode() should have already registered all strings
+ if (Q_UNLIKELY(generator.registeredStringsCount() >= std::numeric_limits<int>::max())) {
+ error("internal limit exceeded: number of parsed strings is too big.");
+ exit(EXIT_FAILURE);
+ }
+ }
+ fputs("", out);
+
+ fprintf(out, "QT_WARNING_POP\n");
+
+ if (jsonOutput) {
+ QJsonObject mocData;
+ mocData["outputRevision"_L1] = mocOutputRevision;
+ mocData["inputFile"_L1] = QLatin1StringView(fn.constData());
+
+ QJsonArray classesJsonFormatted;
+
+ for (const ClassDef &cdef: std::as_const(classList))
+ classesJsonFormatted.append(cdef.toJson());
+
+ if (!classesJsonFormatted.isEmpty())
+ mocData["classes"_L1] = classesJsonFormatted;
+
+ QJsonDocument jsonDoc(mocData);
+ fputs(jsonDoc.toJson().constData(), jsonOutput);
+ }
+}
+
+void Moc::parseSlots(ClassDef *def, FunctionDef::Access access)
+{
+ QTypeRevision defaultRevision;
+ if (test(Q_REVISION_TOKEN))
+ defaultRevision = parseRevision();
+
+ next(COLON);
+ while (inClass(def) && hasNext()) {
+ switch (next()) {
+ case PUBLIC:
+ case PROTECTED:
+ case PRIVATE:
+ case Q_SIGNALS_TOKEN:
+ case Q_SLOTS_TOKEN:
+ prev();
+ return;
+ case SEMIC:
+ continue;
+ case FRIEND:
+ until(SEMIC);
+ continue;
+ case USING:
+ error("'using' directive not supported in 'slots' section");
+ default:
+ prev();
+ }
+
+ FunctionDef funcDef;
+ funcDef.access = access;
+ if (!parseFunction(&funcDef))
+ continue;
+ if (funcDef.revision > 0) {
+ ++def->revisionedMethods;
+ } else if (defaultRevision.isValid()) {
+ funcDef.revision = defaultRevision.toEncodedVersion<int>();
+ ++def->revisionedMethods;
+ }
+ def->slotList += funcDef;
+ handleDefaultArguments(&def->slotList, funcDef);
+ }
+}
+
+void Moc::parseSignals(ClassDef *def)
+{
+ QTypeRevision defaultRevision;
+ if (test(Q_REVISION_TOKEN))
+ defaultRevision = parseRevision();
+
+ next(COLON);
+ while (inClass(def) && hasNext()) {
+ switch (next()) {
+ case PUBLIC:
+ case PROTECTED:
+ case PRIVATE:
+ case Q_SIGNALS_TOKEN:
+ case Q_SLOTS_TOKEN:
+ prev();
+ return;
+ case SEMIC:
+ continue;
+ case FRIEND:
+ until(SEMIC);
+ continue;
+ case USING:
+ error("'using' directive not supported in 'signals' section");
+ default:
+ prev();
+ }
+ FunctionDef funcDef;
+ funcDef.access = FunctionDef::Public;
+ parseFunction(&funcDef);
+ if (funcDef.isVirtual)
+ warning("Signals cannot be declared virtual");
+ if (funcDef.inlineCode)
+ error("Not a signal declaration");
+ if (funcDef.revision > 0) {
+ ++def->revisionedMethods;
+ } else if (defaultRevision.isValid()) {
+ funcDef.revision = defaultRevision.toEncodedVersion<int>();
+ ++def->revisionedMethods;
+ }
+ def->signalList += funcDef;
+ handleDefaultArguments(&def->signalList, funcDef);
+ }
+}
+
+void Moc::createPropertyDef(PropertyDef &propDef, int propertyIndex, Moc::PropertyMode mode)
+{
+ propDef.location = index;
+ propDef.relativeIndex = propertyIndex;
+
+ QByteArray type = parseType().name;
+ if (type.isEmpty())
+ error();
+ propDef.designable = propDef.scriptable = propDef.stored = "true";
+ propDef.user = "false";
+ /*
+ The Q_PROPERTY construct cannot contain any commas, since
+ commas separate macro arguments. We therefore expect users
+ to type "QMap" instead of "QMap<QString, QVariant>". For
+ coherence, we also expect the same for
+ QValueList<QVariant>, the other template class supported by
+ QVariant.
+ */
+ type = normalizeType(type);
+ if (type == "QMap")
+ type = "QMap<QString,QVariant>";
+ else if (type == "QValueList")
+ type = "QValueList<QVariant>";
+ else if (type == "LongLong")
+ type = "qlonglong";
+ else if (type == "ULongLong")
+ type = "qulonglong";
+
+ propDef.type = type;
+
+ if (mode == Moc::Named) {
+ next();
+ propDef.name = lexem();
+ }
+
+ parsePropertyAttributes(propDef);
+}
+
+void Moc::parsePropertyAttributes(PropertyDef &propDef)
+{
+ auto checkIsFunction = [&](const QByteArray &def, const char *name) {
+ if (def.endsWith(')')) {
+ QByteArray msg = "Providing a function for ";
+ msg += name;
+ msg += " in a property declaration is not be supported in Qt 6.";
+ error(msg.constData());
+ }
+ };
+
+ while (test(IDENTIFIER)) {
+ const Symbol &lsym = symbol();
+ const QByteArray l = lsym.lexem();
+ if (l[0] == 'C' && l == "CONSTANT") {
+ propDef.constant = true;
+ continue;
+ } else if (l[0] == 'F' && l == "FINAL") {
+ propDef.final = true;
+ continue;
+ } else if (l[0] == 'N' && l == "NAME") {
+ next(IDENTIFIER);
+ propDef.name = lexem();
+ continue;
+ } else if (l[0] == 'R' && l == "REQUIRED") {
+ propDef.required = true;
+ continue;
+ } else if (l[0] == 'R' && l == "REVISION" && test(LPAREN)) {
+ prev();
+ propDef.revision = parseRevision().toEncodedVersion<int>();
+ continue;
+ }
+
+ QByteArray v, v2;
+ if (test(LPAREN)) {
+ v = lexemUntil(RPAREN);
+ v = v.mid(1, v.size() - 2); // removes the '(' and ')'
+ } else if (test(INTEGER_LITERAL)) {
+ v = lexem();
+ if (l != "REVISION")
+ error(lsym);
+ } else if (test(DEFAULT)) {
+ v = lexem();
+ if (l != "READ" && l != "WRITE")
+ error(lsym);
+ } else {
+ next(IDENTIFIER);
+ v = lexem();
+ if (test(LPAREN))
+ v2 = lexemUntil(RPAREN);
+ else if (v != "true" && v != "false")
+ v2 = "()";
+ }
+ switch (l[0]) {
+ case 'M':
+ if (l == "MEMBER")
+ propDef.member = v;
+ else
+ error(lsym);
+ break;
+ case 'R':
+ if (l == "READ")
+ propDef.read = v;
+ else if (l == "RESET")
+ propDef.reset = v;
+ else if (l == "REVISION") {
+ bool ok = false;
+ const int minor = v.toInt(&ok);
+ if (!ok || !QTypeRevision::isValidSegment(minor))
+ error(lsym);
+ propDef.revision = QTypeRevision::fromMinorVersion(minor).toEncodedVersion<int>();
+ } else
+ error(lsym);
+ break;
+ case 'S':
+ if (l == "SCRIPTABLE") {
+ propDef.scriptable = v + v2;
+ checkIsFunction(propDef.scriptable, "SCRIPTABLE");
+ } else if (l == "STORED") {
+ propDef.stored = v + v2;
+ checkIsFunction(propDef.stored, "STORED");
+ } else
+ error(lsym);
+ break;
+ case 'W': if (l != "WRITE") error(lsym);
+ propDef.write = v;
+ break;
+ case 'B': if (l != "BINDABLE") error(lsym);
+ propDef.bind = v;
+ break;
+ case 'D': if (l != "DESIGNABLE") error(lsym);
+ propDef.designable = v + v2;
+ checkIsFunction(propDef.designable, "DESIGNABLE");
+ break;
+ case 'N': if (l != "NOTIFY") error(lsym);
+ propDef.notify = v;
+ break;
+ case 'U': if (l != "USER") error(lsym);
+ propDef.user = v + v2;
+ checkIsFunction(propDef.user, "USER");
+ break;
+ default:
+ error(lsym);
+ }
+ }
+ if (propDef.constant && !propDef.write.isNull()) {
+ const QByteArray msg = "Property declaration " + propDef.name
+ + " is both WRITEable and CONSTANT. CONSTANT will be ignored.";
+ propDef.constant = false;
+ warning(msg.constData());
+ }
+ if (propDef.constant && !propDef.notify.isNull()) {
+ const QByteArray msg = "Property declaration " + propDef.name
+ + " is both NOTIFYable and CONSTANT. CONSTANT will be ignored.";
+ propDef.constant = false;
+ warning(msg.constData());
+ }
+ if (propDef.constant && !propDef.bind.isNull()) {
+ const QByteArray msg = "Property declaration " + propDef.name
+ + " is both BINDable and CONSTANT. CONSTANT will be ignored.";
+ propDef.constant = false;
+ warning(msg.constData());
+ }
+ if (propDef.read == "default" && propDef.bind.isNull()) {
+ const QByteArray msg = "Property declaration " + propDef.name
+ + " is not BINDable but default-READable. READ will be ignored.";
+ propDef.read = "";
+ warning(msg.constData());
+ }
+ if (propDef.write == "default" && propDef.bind.isNull()) {
+ const QByteArray msg = "Property declaration " + propDef.name
+ + " is not BINDable but default-WRITEable. WRITE will be ignored.";
+ propDef.write = "";
+ warning(msg.constData());
+ }
+}
+
+void Moc::parseProperty(ClassDef *def, Moc::PropertyMode mode)
+{
+ next(LPAREN);
+ PropertyDef propDef;
+ createPropertyDef(propDef, int(def->propertyList.size()), mode);
+ next(RPAREN);
+
+ def->propertyList += propDef;
+}
+
+void Moc::parsePluginData(ClassDef *def)
+{
+ next(LPAREN);
+ QByteArray metaData;
+ while (test(IDENTIFIER)) {
+ QByteArray l = lexem();
+ if (l == "IID") {
+ next(STRING_LITERAL);
+ def->pluginData.iid = unquotedLexem();
+ } else if (l == "URI") {
+ next(STRING_LITERAL);
+ def->pluginData.uri = unquotedLexem();
+ } else if (l == "FILE") {
+ next(STRING_LITERAL);
+ QByteArray metaDataFile = unquotedLexem();
+ QFileInfo fi(QFileInfo(QString::fromLocal8Bit(currentFilenames.top())).dir(),
+ QString::fromLocal8Bit(metaDataFile));
+ for (const IncludePath &p : std::as_const(includes)) {
+ if (fi.exists())
+ break;
+ if (p.isFrameworkPath)
+ continue;
+
+ fi.setFile(QString::fromLocal8Bit(p.path.constData()), QString::fromLocal8Bit(metaDataFile.constData()));
+ // try again, maybe there's a file later in the include paths with the same name
+ if (fi.isDir()) {
+ fi = QFileInfo();
+ continue;
+ }
+ }
+ if (!fi.exists()) {
+ const QByteArray msg = "Plugin Metadata file " + lexem()
+ + " does not exist. Declaration will be ignored";
+ error(msg.constData());
+ return;
+ }
+ QFile file(fi.canonicalFilePath());
+ if (!file.open(QFile::ReadOnly)) {
+ QByteArray msg = "Plugin Metadata file " + lexem() + " could not be opened: "
+ + file.errorString().toUtf8();
+ error(msg.constData());
+ return;
+ }
+ parsedPluginMetadataFiles.append(fi.canonicalFilePath());
+ metaData = file.readAll();
+ }
+ }
+
+ if (!metaData.isEmpty()) {
+ def->pluginData.metaData = QJsonDocument::fromJson(metaData);
+ if (!def->pluginData.metaData.isObject()) {
+ const QByteArray msg = "Plugin Metadata file " + lexem()
+ + " does not contain a valid JSON object. Declaration will be ignored";
+ warning(msg.constData());
+ def->pluginData.iid = QByteArray();
+ def->pluginData.uri = QByteArray();
+ return;
+ }
+ }
+
+ mustIncludeQPluginH = true;
+ next(RPAREN);
+}
+
+QByteArray Moc::parsePropertyAccessor()
+{
+ int nesting = 0;
+ QByteArray accessor;
+ while (1) {
+ Token t = peek();
+ if (!nesting && (t == RPAREN || t == COMMA))
+ break;
+ t = next();
+ if (t == LPAREN)
+ ++nesting;
+ if (t == RPAREN)
+ --nesting;
+ accessor += lexem();
+ }
+ return accessor;
+}
+
+void Moc::parsePrivateProperty(ClassDef *def, Moc::PropertyMode mode)
+{
+ next(LPAREN);
+ PropertyDef propDef;
+ propDef.inPrivateClass = parsePropertyAccessor();
+
+ next(COMMA);
+
+ createPropertyDef(propDef, int(def->propertyList.size()), mode);
+
+ def->propertyList += propDef;
+}
+
+void Moc::parseEnumOrFlag(BaseDef *def, bool isFlag)
+{
+ next(LPAREN);
+ QByteArray identifier;
+ while (test(IDENTIFIER)) {
+ identifier = lexem();
+ while (test(SCOPE) && test(IDENTIFIER)) {
+ identifier += "::";
+ identifier += lexem();
+ }
+ def->enumDeclarations[identifier] = isFlag;
+ }
+ next(RPAREN);
+}
+
+void Moc::parseFlag(BaseDef *def)
+{
+ next(LPAREN);
+ QByteArray flagName, enumName;
+ while (test(IDENTIFIER)) {
+ flagName = lexem();
+ while (test(SCOPE) && test(IDENTIFIER)) {
+ flagName += "::";
+ flagName += lexem();
+ }
+ }
+ next(COMMA);
+ while (test(IDENTIFIER)) {
+ enumName = lexem();
+ while (test(SCOPE) && test(IDENTIFIER)) {
+ enumName += "::";
+ enumName += lexem();
+ }
+ }
+
+ def->flagAliases.insert(enumName, flagName);
+ next(RPAREN);
+}
+
+Moc::EncounteredQmlMacro Moc::parseClassInfo(BaseDef *def)
+{
+ bool encounteredQmlMacro = false;
+ next(LPAREN);
+ ClassInfoDef infoDef;
+ next(STRING_LITERAL);
+ infoDef.name = symbol().unquotedLexem();
+ if (infoDef.name.startsWith("QML."))
+ encounteredQmlMacro = true;
+ next(COMMA);
+ if (test(STRING_LITERAL)) {
+ infoDef.value = symbol().unquotedLexem();
+ } else if (test(Q_REVISION_TOKEN)) {
+ infoDef.value = QByteArray::number(parseRevision().toEncodedVersion<quint16>());
+ } else {
+ // support Q_CLASSINFO("help", QT_TR_NOOP("blah"))
+ next(IDENTIFIER);
+ next(LPAREN);
+ next(STRING_LITERAL);
+ infoDef.value = symbol().unquotedLexem();
+ next(RPAREN);
+ }
+ next(RPAREN);
+ def->classInfoList += infoDef;
+ return encounteredQmlMacro ? EncounteredQmlMacro::Yes : EncounteredQmlMacro::No;
+}
+
+void Moc::parseClassInfo(ClassDef *def)
+{
+ if (parseClassInfo(static_cast<BaseDef *>(def)) == EncounteredQmlMacro::Yes)
+ def->requireCompleteMethodTypes = true;
+}
+
+void Moc::parseInterfaces(ClassDef *def)
+{
+ next(LPAREN);
+ while (test(IDENTIFIER)) {
+ QList<ClassDef::Interface> iface;
+ iface += ClassDef::Interface(lexem());
+ while (test(SCOPE)) {
+ iface.last().className += lexem();
+ next(IDENTIFIER);
+ iface.last().className += lexem();
+ }
+ while (test(COLON)) {
+ next(IDENTIFIER);
+ iface += ClassDef::Interface(lexem());
+ while (test(SCOPE)) {
+ iface.last().className += lexem();
+ next(IDENTIFIER);
+ iface.last().className += lexem();
+ }
+ }
+ // resolve from classnames to interface ids
+ for (qsizetype i = 0; i < iface.size(); ++i) {
+ const QByteArray iid = interface2IdMap.value(iface.at(i).className);
+ if (iid.isEmpty())
+ error("Undefined interface");
+
+ iface[i].interfaceId = iid;
+ }
+ def->interfaceList += iface;
+ }
+ next(RPAREN);
+}
+
+void Moc::parseDeclareInterface()
+{
+ next(LPAREN);
+ QByteArray interface;
+ next(IDENTIFIER);
+ interface += lexem();
+ while (test(SCOPE)) {
+ interface += lexem();
+ next(IDENTIFIER);
+ interface += lexem();
+ }
+ next(COMMA);
+ QByteArray iid;
+ if (test(STRING_LITERAL)) {
+ iid = lexem();
+ } else {
+ next(IDENTIFIER);
+ iid = lexem();
+ }
+ interface2IdMap.insert(interface, iid);
+ next(RPAREN);
+}
+
+void Moc::parseDeclareMetatype()
+{
+ next(LPAREN);
+ QByteArray typeName = lexemUntil(RPAREN);
+ typeName.remove(0, 1);
+ typeName.chop(1);
+ metaTypes.append(typeName);
+}
+
+void Moc::parseMocInclude()
+{
+ next(LPAREN);
+ QByteArray include = lexemUntil(RPAREN);
+ // remove parentheses
+ include.remove(0, 1);
+ include.chop(1);
+ includeFiles.append(include);
+}
+
+void Moc::parseSlotInPrivate(ClassDef *def, FunctionDef::Access access)
+{
+ next(LPAREN);
+ FunctionDef funcDef;
+ next(IDENTIFIER);
+ funcDef.inPrivateClass = lexem();
+ // also allow void functions
+ if (test(LPAREN)) {
+ next(RPAREN);
+ funcDef.inPrivateClass += "()";
+ }
+ next(COMMA);
+ funcDef.access = access;
+ parseFunction(&funcDef, true);
+ def->slotList += funcDef;
+ handleDefaultArguments(&def->slotList, funcDef);
+ if (funcDef.revision > 0)
+ ++def->revisionedMethods;
+
+}
+
+QByteArray Moc::lexemUntil(Token target)
+{
+ qsizetype from = index;
+ until(target);
+ QByteArray s;
+ while (from <= index) {
+ QByteArray n = symbols.at(from++-1).lexem();
+ if (s.size() && n.size()) {
+ char prev = s.at(s.size()-1);
+ char next = n.at(0);
+ if ((is_ident_char(prev) && is_ident_char(next))
+ || (prev == '<' && next == ':')
+ || (prev == '>' && next == '>'))
+ s += ' ';
+ }
+ s += n;
+ }
+ return s;
+}
+
+bool Moc::until(Token target) {
+ int braceCount = 0;
+ int brackCount = 0;
+ int parenCount = 0;
+ int angleCount = 0;
+ if (index) {
+ switch(symbols.at(index-1).token) {
+ case LBRACE: ++braceCount; break;
+ case LBRACK: ++brackCount; break;
+ case LPAREN: ++parenCount; break;
+ case LANGLE: ++angleCount; break;
+ default: break;
+ }
+ }
+
+ //when searching commas within the default argument, we should take care of template depth (anglecount)
+ // unfortunately, we do not have enough semantic information to know if '<' is the operator< or
+ // the beginning of a template type. so we just use heuristics.
+ qsizetype possible = -1;
+
+ while (index < symbols.size()) {
+ Token t = symbols.at(index++).token;
+ switch (t) {
+ case LBRACE: ++braceCount; break;
+ case RBRACE: --braceCount; break;
+ case LBRACK: ++brackCount; break;
+ case RBRACK: --brackCount; break;
+ case LPAREN: ++parenCount; break;
+ case RPAREN: --parenCount; break;
+ case LANGLE:
+ if (parenCount == 0 && braceCount == 0)
+ ++angleCount;
+ break;
+ case RANGLE:
+ if (parenCount == 0 && braceCount == 0)
+ --angleCount;
+ break;
+ case GTGT:
+ if (parenCount == 0 && braceCount == 0) {
+ angleCount -= 2;
+ t = RANGLE;
+ }
+ break;
+ default: break;
+ }
+ if (t == target
+ && braceCount <= 0
+ && brackCount <= 0
+ && parenCount <= 0
+ && (target != RANGLE || angleCount <= 0)) {
+ if (target != COMMA || angleCount <= 0)
+ return true;
+ possible = index;
+ }
+
+ if (target == COMMA && t == EQ && possible != -1) {
+ index = possible;
+ return true;
+ }
+
+ if (braceCount < 0 || brackCount < 0 || parenCount < 0
+ || (target == RANGLE && angleCount < 0)) {
+ --index;
+ break;
+ }
+
+ if (braceCount <= 0 && t == SEMIC) {
+ // Abort on semicolon. Allow recovering bad template parsing (QTBUG-31218)
+ break;
+ }
+ }
+
+ if (target == COMMA && angleCount != 0 && possible != -1) {
+ index = possible;
+ return true;
+ }
+
+ return false;
+}
+
+void Moc::checkSuperClasses(ClassDef *def)
+{
+ Q_ASSERT(!def->superclassList.isEmpty());
+ const QByteArray &firstSuperclass = def->superclassList.at(0).classname;
+
+ if (!knownQObjectClasses.contains(firstSuperclass)) {
+ // enable once we /require/ include paths
+#if 0
+ const QByteArray msg
+ = "Class "
+ + def->className
+ + " contains the Q_OBJECT macro and inherits from "
+ + def->superclassList.value(0)
+ + " but that is not a known QObject subclass. You may get compilation errors.";
+ warning(msg.constData());
+#endif
+ return;
+ }
+
+ auto isRegisteredInterface = [&def](QByteArrayView super) {
+ auto matchesSuperClass = [&super](const auto &ifaces) {
+ return !ifaces.isEmpty() && ifaces.first().className == super;
+ };
+ return std::any_of(def->interfaceList.cbegin(), def->interfaceList.cend(), matchesSuperClass);
+ };
+
+ const auto end = def->superclassList.cend();
+ auto it = def->superclassList.cbegin() + 1;
+ for (; it != end; ++it) {
+ const QByteArray &superClass = it->classname;
+ if (knownQObjectClasses.contains(superClass)) {
+ const QByteArray msg
+ = "Class "
+ + def->classname
+ + " inherits from two QObject subclasses "
+ + firstSuperclass
+ + " and "
+ + superClass
+ + ". This is not supported!";
+ warning(msg.constData());
+ }
+
+ if (interface2IdMap.contains(superClass)) {
+ if (!isRegisteredInterface(superClass)) {
+ const QByteArray msg
+ = "Class "
+ + def->classname
+ + " implements the interface "
+ + superClass
+ + " but does not list it in Q_INTERFACES. qobject_cast to "
+ + superClass
+ + " will not work!";
+ warning(msg.constData());
+ }
+ }
+ }
+}
+
+void Moc::checkProperties(ClassDef *cdef)
+{
+ //
+ // specify get function, for compatibility we accept functions
+ // returning pointers, or const char * for QByteArray.
+ //
+ QDuplicateTracker<QByteArray> definedProperties(cdef->propertyList.size());
+ auto hasNoAttributes = [&](const PropertyDef &p) {
+ if (definedProperties.hasSeen(p.name)) {
+ QByteArray msg = "The property '" + p.name + "' is defined multiple times in class " + cdef->classname + ".";
+ warning(msg.constData());
+ }
+
+ if (p.read.isEmpty() && p.member.isEmpty() && p.bind.isEmpty()) {
+ QByteArray msg = "Property declaration " + p.name + " has neither an associated QProperty<> member"
+ ", nor a READ accessor function nor an associated MEMBER variable. The property will be invalid.";
+ const auto &sym = p.location >= 0 ? symbolAt(p.location) : Symbol();
+ warning(sym, msg.constData());
+ if (p.write.isEmpty())
+ return true;
+ }
+ return false;
+ };
+ cdef->propertyList.removeIf(hasNoAttributes);
+
+ for (PropertyDef &p : cdef->propertyList) {
+ for (const FunctionDef &f : std::as_const(cdef->publicList)) {
+ if (f.name != p.read)
+ continue;
+ if (!f.isConst) // get functions must be const
+ continue;
+ if (f.arguments.size()) // and must not take any arguments
+ continue;
+ PropertyDef::Specification spec = PropertyDef::ValueSpec;
+ QByteArray tmp = f.normalizedType;
+ if (p.type == "QByteArray" && tmp == "const char *")
+ tmp = "QByteArray";
+ if (tmp.left(6) == "const ")
+ tmp = tmp.mid(6);
+ if (p.type != tmp && tmp.endsWith('*')) {
+ tmp.chop(1);
+ spec = PropertyDef::PointerSpec;
+ } else if (f.type.name.endsWith('&')) { // raw type, not normalized type
+ spec = PropertyDef::ReferenceSpec;
+ }
+ if (p.type != tmp)
+ continue;
+ p.gspec = spec;
+ break;
+ }
+ if (!p.notify.isEmpty()) {
+ int notifyId = -1;
+ for (int j = 0; j < int(cdef->signalList.size()); ++j) {
+ const FunctionDef &f = cdef->signalList.at(j);
+ if (f.name != p.notify) {
+ continue;
+ } else {
+ notifyId = j /* Signal indexes start from 0 */;
+ break;
+ }
+ }
+ p.notifyId = notifyId;
+ if (notifyId == -1) {
+ const int index = int(cdef->nonClassSignalList.indexOf(p.notify));
+ if (index == -1) {
+ cdef->nonClassSignalList << p.notify;
+ p.notifyId = int(-1 - cdef->nonClassSignalList.size());
+ } else {
+ p.notifyId = int(-2 - index);
+ }
+ }
+ }
+ }
+}
+#endif // -- QtScxml
+
+QJsonObject ClassDef::toJson() const
+{
+ QJsonObject cls;
+ cls["className"_L1] = QString::fromUtf8(classname.constData());
+ cls["qualifiedClassName"_L1] = QString::fromUtf8(qualified.constData());
+
+ QJsonArray classInfos;
+ for (const auto &info: std::as_const(classInfoList)) {
+ QJsonObject infoJson;
+ infoJson["name"_L1] = QString::fromUtf8(info.name);
+ infoJson["value"_L1] = QString::fromUtf8(info.value);
+ classInfos.append(infoJson);
+ }
+
+ if (classInfos.size())
+ cls["classInfos"_L1] = classInfos;
+
+ const auto appendFunctions = [&cls](const QString &type, const QList<FunctionDef> &funcs) {
+ QJsonArray jsonFuncs;
+
+ for (const FunctionDef &fdef: funcs)
+ jsonFuncs.append(fdef.toJson());
+
+ if (!jsonFuncs.isEmpty())
+ cls[type] = jsonFuncs;
+ };
+
+ appendFunctions("signals"_L1, signalList);
+ appendFunctions("slots"_L1, slotList);
+ appendFunctions("constructors"_L1, constructorList);
+ appendFunctions("methods"_L1, methodList);
+
+ QJsonArray props;
+
+ for (const PropertyDef &propDef: std::as_const(propertyList))
+ props.append(propDef.toJson());
+
+ if (!props.isEmpty())
+ cls["properties"_L1] = props;
+
+ if (hasQObject)
+ cls["object"_L1] = true;
+ if (hasQGadget)
+ cls["gadget"_L1] = true;
+ if (hasQNamespace)
+ cls["namespace"_L1] = true;
+
+ QJsonArray superClasses;
+
+ for (const auto &super: std::as_const(superclassList)) {
+ QJsonObject superCls;
+ superCls["name"_L1] = QString::fromUtf8(super.classname);
+ if (super.classname != super.qualified)
+ superCls["fullyQualifiedName"_L1] = QString::fromUtf8(super.qualified);
+ FunctionDef::accessToJson(&superCls, super.access);
+ superClasses.append(superCls);
+ }
+
+ if (!superClasses.isEmpty())
+ cls["superClasses"_L1] = superClasses;
+
+ QJsonArray enums;
+ for (const EnumDef &enumDef: std::as_const(enumList))
+ enums.append(enumDef.toJson(*this));
+ if (!enums.isEmpty())
+ cls["enums"_L1] = enums;
+
+ QJsonArray ifaces;
+ for (const QList<Interface> &ifaceList : interfaceList) {
+ QJsonArray jsonList;
+ for (const Interface &iface: ifaceList) {
+ QJsonObject ifaceJson;
+ ifaceJson["id"_L1] = QString::fromUtf8(iface.interfaceId);
+ ifaceJson["className"_L1] = QString::fromUtf8(iface.className);
+ jsonList.append(ifaceJson);
+ }
+ ifaces.append(jsonList);
+ }
+ if (!ifaces.isEmpty())
+ cls["interfaces"_L1] = ifaces;
+
+ return cls;
+}
+
+QJsonObject FunctionDef::toJson() const
+{
+ QJsonObject fdef;
+ fdef["name"_L1] = QString::fromUtf8(name);
+ if (!tag.isEmpty())
+ fdef["tag"_L1] = QString::fromUtf8(tag);
+ fdef["returnType"_L1] = QString::fromUtf8(normalizedType);
+
+ QJsonArray args;
+ for (const ArgumentDef &arg: arguments)
+ args.append(arg.toJson());
+
+ if (!args.isEmpty())
+ fdef["arguments"_L1] = args;
+
+ accessToJson(&fdef, access);
+
+ if (revision > 0)
+ fdef["revision"_L1] = revision;
+
+ if (wasCloned)
+ fdef["isCloned"_L1] = true;
+
+ return fdef;
+}
+
+void FunctionDef::accessToJson(QJsonObject *obj, FunctionDef::Access acs)
+{
+ switch (acs) {
+ case Private: (*obj)["access"_L1] = "private"_L1; break;
+ case Public: (*obj)["access"_L1] = "public"_L1; break;
+ case Protected: (*obj)["access"_L1] = "protected"_L1; break;
+ }
+}
+
+QJsonObject ArgumentDef::toJson() const
+{
+ QJsonObject arg;
+ arg["type"_L1] = QString::fromUtf8(normalizedType);
+ if (!name.isEmpty())
+ arg["name"_L1] = QString::fromUtf8(name);
+ return arg;
+}
+
+QJsonObject PropertyDef::toJson() const
+{
+ QJsonObject prop;
+ prop["name"_L1] = QString::fromUtf8(name);
+ prop["type"_L1] = QString::fromUtf8(type);
+
+ const auto jsonify = [&prop](const char *str, const QByteArray &member) {
+ if (!member.isEmpty())
+ prop[QLatin1StringView(str)] = QString::fromUtf8(member);
+ };
+
+ jsonify("member", member);
+ jsonify("read", read);
+ jsonify("write", write);
+ jsonify("bindable", bind);
+ jsonify("reset", reset);
+ jsonify("notify", notify);
+ jsonify("privateClass", inPrivateClass);
+
+ const auto jsonifyBoolOrString = [&prop](const char *str, const QByteArray &boolOrString) {
+ QJsonValue value;
+ if (boolOrString == "true")
+ value = true;
+ else if (boolOrString == "false")
+ value = false;
+ else
+ value = QString::fromUtf8(boolOrString); // function name to query at run-time
+ prop[QLatin1StringView(str)] = value;
+ };
+
+ jsonifyBoolOrString("designable", designable);
+ jsonifyBoolOrString("scriptable", scriptable);
+ jsonifyBoolOrString("stored", stored);
+ jsonifyBoolOrString("user", user);
+
+ prop["constant"_L1] = constant;
+ prop["final"_L1] = final;
+ prop["required"_L1] = required;
+ prop["index"_L1] = relativeIndex;
+ if (revision > 0)
+ prop["revision"_L1] = revision;
+
+ return prop;
+}
+
+QJsonObject EnumDef::toJson(const ClassDef &cdef) const
+{
+ QJsonObject def;
+ def["name"_L1] = QString::fromUtf8(name);
+ if (!enumName.isEmpty())
+ def["alias"_L1] = QString::fromUtf8(enumName);
+ if (!type.isEmpty())
+ def["type"_L1] = QString::fromUtf8(type);
+ def["isFlag"_L1] = cdef.enumDeclarations.value(name);
+ def["isClass"_L1] = isEnumClass;
+
+ QJsonArray valueArr;
+ for (const QByteArray &value: values)
+ valueArr.append(QString::fromUtf8(value));
+ if (!valueArr.isEmpty())
+ def["values"_L1] = valueArr;
+
+ return def;
+}
+
+QByteArray EnumDef::qualifiedType(const ClassDef *cdef) const
+{
+ if (name == cdef->classname) {
+ // The name of the enclosing namespace is the same as the enum class name
+ if (cdef->qualified.contains("::")) {
+ // QTBUG-112996, fully qualify by using cdef->qualified to disambiguate enum
+ // class name and enclosing namespace, e.g.:
+ // namespace A { namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) } }
+ return cdef->qualified % "::" % name;
+ } else {
+ // Just "B"; otherwise the compiler complains about the type "B::B" inside
+ // "B::staticMetaObject" in the generated code; e.g.:
+ // namespace B { Q_NAMESPACE; enum class B { }; Q_ENUM_NS(B) }
+ return name;
+ }
+ }
+ return cdef->classname % "::" % name;
+}
+
+QT_END_NAMESPACE
diff --git a/tools/qscxmlc/moc.h b/tools/qscxmlc/moc.h
index 56aedad..19aefb9 100644
--- a/tools/qscxmlc/moc.h
+++ b/tools/qscxmlc/moc.h
@@ -1,39 +1,17 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef MOC_H
#define MOC_H
+// -- QtScxml
#include <QtCore/qmap.h>
#include <QtCore/qpair.h>
#include <QtCore/qjsondocument.h>
#include <QtCore/qjsonarray.h>
-#include <ctype.h>
+// -- QtScxml
+
+#include <private/qtools_p.h>
QT_BEGIN_NAMESPACE
@@ -43,26 +21,35 @@ struct Type
{
enum ReferenceType { NoReference, Reference, RValueReference, Pointer };
- inline Type() : isVolatile(false), isScoped(false), /*firstToken(NOTOKEN), */referenceType(NoReference) {}
+ inline Type() : isVolatile(false), isScoped(false), /* firstToken(NOTOKEN) -- QtScxml ,*/ referenceType(NoReference) {}
inline explicit Type(const QByteArray &_name)
- : name(_name), rawName(name), isVolatile(false), isScoped(false), /*firstToken(NOTOKEN),*/ referenceType(NoReference) {}
+ : name(_name), rawName(name), isVolatile(false), isScoped(false), /* firstToken(NOTOKEN) -- QtScxml ,*/ referenceType(NoReference) {}
QByteArray name;
//When used as a return type, the type name may be modified to remove the references.
// rawName is the type as found in the function signature
QByteArray rawName;
uint isVolatile : 1;
uint isScoped : 1;
-// Token firstToken;
+#if 0 // -- QtScxml
+ Token firstToken;
+#endif // -- QtScxml
ReferenceType referenceType;
};
+Q_DECLARE_TYPEINFO(Type, Q_RELOCATABLE_TYPE);
+struct ClassDef;
struct EnumDef
{
QByteArray name;
+ QByteArray enumName;
+ QByteArray type;
QList<QByteArray> values;
bool isEnumClass; // c++11 enum class
EnumDef() : isEnumClass(false) {}
+ QJsonObject toJson(const ClassDef &cdef) const;
+ QByteArray qualifiedType(const ClassDef *cdef) const;
};
+Q_DECLARE_TYPEINFO(EnumDef, Q_RELOCATABLE_TYPE);
struct ArgumentDef
{
@@ -71,125 +58,260 @@ struct ArgumentDef
QByteArray rightType, normalizedType, name;
QByteArray typeNameForCast; // type name to be used in cast from void * in metacall
bool isDefault;
+
+ QJsonObject toJson() const;
};
+Q_DECLARE_TYPEINFO(ArgumentDef, Q_RELOCATABLE_TYPE);
struct FunctionDef
{
- FunctionDef(): access(Private), isConst(false), isVirtual(false), isStatic(false),
- inlineCode(false), wasCloned(false), isCompat(false), isInvokable(false),
- isScriptable(false), isSlot(false), isSignal(false), isPrivateSignal(false),
- isConstructor(false), isDestructor(false), isAbstract(false), revision(0), implementation(0) {}
Type type;
+ QList<ArgumentDef> arguments;
QByteArray normalizedType;
QByteArray tag;
QByteArray name;
- QByteArray mangledName;
-
- QList<ArgumentDef> arguments;
+ QByteArray inPrivateClass;
enum Access { Private, Protected, Public };
- Access access;
- bool isConst;
- bool isVirtual;
- bool isStatic;
- bool inlineCode;
- bool wasCloned;
+ Access access = Private;
+ int revision = 0;
- QByteArray inPrivateClass;
- bool isCompat;
- bool isInvokable;
- bool isScriptable;
- bool isSlot;
- bool isSignal;
- bool isPrivateSignal;
- bool isConstructor;
- bool isDestructor;
- bool isAbstract;
-
- int revision;
-
- const char *implementation;
+ bool isConst = false;
+ bool isVirtual = false;
+ bool isStatic = false;
+ bool inlineCode = false;
+ bool wasCloned = false;
+
+#if 0 // -- QtScxml
+ bool returnTypeIsVolatile = false;
+#endif // -- QtScxml
+ bool isCompat = false;
+ bool isInvokable = false;
+ bool isScriptable = false;
+ bool isSlot = false;
+ bool isSignal = false;
+ bool isPrivateSignal = false;
+ bool isConstructor = false;
+ bool isDestructor = false;
+ bool isAbstract = false;
+ bool isRawSlot = false;
+
+ QJsonObject toJson() const;
+ static void accessToJson(QJsonObject *obj, Access acs);
+
+// -- QtScxml
+ QByteArray mangledName;
+ const char *implementation = nullptr;
+// -- QtScxml
};
+Q_DECLARE_TYPEINFO(FunctionDef, Q_RELOCATABLE_TYPE);
struct PropertyDef
{
- PropertyDef():notifyId(-1), constant(false), final(false), gspec(ValueSpec), revision(0){}
- QByteArray name, mangledName, type, member, read, write, reset, designable, scriptable,
- editable, stored, user, notify, inPrivateClass;
- int notifyId;
- bool constant;
- bool final;
- enum Specification { ValueSpec, ReferenceSpec, PointerSpec };
- Specification gspec;
bool stdCppSet() const {
+ if (name.isEmpty())
+ return false;
QByteArray s("set");
- s += toupper(name[0]);
+ s += QtMiscUtils::toAsciiUpper(name[0]);
s += name.mid(1);
return (s == write);
}
- int revision;
+
+ QByteArray name, type, member, read, write, bind, reset, designable, scriptable, stored, user, notify, inPrivateClass;
+ int notifyId = -1; // -1 means no notifyId, >= 0 means signal defined in this class, < -1 means signal not defined in this class
+ enum Specification { ValueSpec, ReferenceSpec, PointerSpec };
+ Specification gspec = ValueSpec;
+ int revision = 0;
+ bool constant = false;
+ bool final = false;
+ bool required = false;
+ int relativeIndex = -1; // property index in current metaobject
+
+ qsizetype location = -1; // token index, used for error reporting
+
+ QJsonObject toJson() const;
+
+// -- QtScxml
+ QByteArray mangledName;
+// -- QtScxml
};
+Q_DECLARE_TYPEINFO(PropertyDef, Q_RELOCATABLE_TYPE);
+struct PrivateQPropertyDef
+{
+ Type type;
+ QByteArray name;
+ QByteArray setter;
+ QByteArray accessor;
+ QByteArray storage;
+};
+Q_DECLARE_TYPEINFO(PrivateQPropertyDef, Q_RELOCATABLE_TYPE);
struct ClassInfoDef
{
QByteArray name;
QByteArray value;
};
+Q_DECLARE_TYPEINFO(ClassInfoDef, Q_RELOCATABLE_TYPE);
+
+struct BaseDef {
+ QByteArray classname;
+ QByteArray qualified;
+ QList<ClassInfoDef> classInfoList;
+ QMap<QByteArray, bool> enumDeclarations;
+ QList<EnumDef> enumList;
+ QMap<QByteArray, QByteArray> flagAliases;
+ qsizetype begin = 0;
+ qsizetype end = 0;
+};
-struct ClassDef {
- ClassDef():
- hasQObject(false), hasQGadget(false), notifyableProperties(0)
- , revisionedMethods(0), revisionedProperties(0), begin(0), end(0){}
+struct SuperClass {
QByteArray classname;
QByteArray qualified;
- QList<QPair<QByteArray, FunctionDef::Access> > superclassList;
+ FunctionDef::Access access;
+};
+Q_DECLARE_TYPEINFO(SuperClass, Q_RELOCATABLE_TYPE);
+
+struct ClassDef : BaseDef {
+ QList<SuperClass> superclassList;
struct Interface
{
+ Interface() { } // for QList, don't use
inline explicit Interface(const QByteArray &_className)
: className(_className) {}
QByteArray className;
QByteArray interfaceId;
};
- QList<QList<Interface> >interfaceList;
-
- bool hasQObject;
- bool hasQGadget;
+ QList<QList<Interface>> interfaceList;
struct PluginData {
QByteArray iid;
+ QByteArray uri;
QMap<QString, QJsonArray> metaArgs;
QJsonDocument metaData;
} pluginData;
QList<FunctionDef> constructorList;
QList<FunctionDef> signalList, slotList, methodList, publicList;
- int notifyableProperties;
+ QList<QByteArray> nonClassSignalList;
QList<PropertyDef> propertyList;
- QList<ClassInfoDef> classInfoList;
- QMap<QByteArray, bool> enumDeclarations;
- QList<EnumDef> enumList;
- QMap<QByteArray, QByteArray> flagAliases;
- int revisionedMethods;
- int revisionedProperties;
+ int revisionedMethods = 0;
+
+ bool hasQObject = false;
+ bool hasQGadget = false;
+ bool hasQNamespace = false;
+ bool requireCompleteMethodTypes = false;
- int begin;
- int end;
+ QJsonObject toJson() const;
};
+Q_DECLARE_TYPEINFO(ClassDef, Q_RELOCATABLE_TYPE);
+Q_DECLARE_TYPEINFO(ClassDef::Interface, Q_RELOCATABLE_TYPE);
-struct NamespaceDef {
- QByteArray name;
- int begin;
- int end;
+struct NamespaceDef : BaseDef {
+ bool hasQNamespace = false;
+ bool doGenerate = false;
+};
+Q_DECLARE_TYPEINFO(NamespaceDef, Q_RELOCATABLE_TYPE);
+
+#if 0 // -- QtScxml
+class Moc : public Parser
+{
+public:
+ enum PropertyMode { Named, Anonymous };
+
+ Moc()
+ : noInclude(false), mustIncludeQPluginH(false), requireCompleteTypes(false)
+ {}
+
+ QByteArray filename;
+
+ bool noInclude;
+ bool mustIncludeQPluginH;
+ bool requireCompleteTypes;
+ QByteArray includePath;
+ QList<QByteArray> includeFiles;
+ QList<ClassDef> classList;
+ QMap<QByteArray, QByteArray> interface2IdMap;
+ QList<QByteArray> metaTypes;
+ // map from class name to fully qualified name
+ QHash<QByteArray, QByteArray> knownQObjectClasses;
+ QHash<QByteArray, QByteArray> knownGadgets;
+ QMap<QString, QJsonArray> metaArgs;
+ QList<QString> parsedPluginMetadataFiles;
+
+ void parse();
+ void generate(FILE *out, FILE *jsonOutput);
+
+ bool parseClassHead(ClassDef *def);
+ inline bool inClass(const ClassDef *def) const {
+ return index > def->begin && index < def->end - 1;
+ }
+
+ inline bool inNamespace(const NamespaceDef *def) const {
+ return index > def->begin && index < def->end - 1;
+ }
+
+ const QByteArray &toFullyQualified(const QByteArray &name) const noexcept;
+
+ void prependNamespaces(BaseDef &def, const QList<NamespaceDef> &namespaceList) const;
+
+ Type parseType();
+
+ bool parseEnum(EnumDef *def);
+
+ bool parseFunction(FunctionDef *def, bool inMacro = false);
+ bool parseMaybeFunction(const ClassDef *cdef, FunctionDef *def);
+
+ void parseSlots(ClassDef *def, FunctionDef::Access access);
+ void parseSignals(ClassDef *def);
+ void parseProperty(ClassDef *def, PropertyMode mode);
+ void parsePluginData(ClassDef *def);
+
+ void createPropertyDef(PropertyDef &def, int propertyIndex, PropertyMode mode);
+
+ void parsePropertyAttributes(PropertyDef &propDef);
+ void parseEnumOrFlag(BaseDef *def, bool isFlag);
+ void parseFlag(BaseDef *def);
+ enum class EncounteredQmlMacro {Yes, No};
+ EncounteredQmlMacro parseClassInfo(BaseDef *def);
+ void parseClassInfo(ClassDef *def);
+ void parseInterfaces(ClassDef *def);
+ void parseDeclareInterface();
+ void parseDeclareMetatype();
+ void parseMocInclude();
+ void parseSlotInPrivate(ClassDef *def, FunctionDef::Access access);
+ QByteArray parsePropertyAccessor();
+ void parsePrivateProperty(ClassDef *def, PropertyMode mode);
+
+ void parseFunctionArguments(FunctionDef *def);
+
+ QByteArray lexemUntil(Token);
+ bool until(Token);
+
+ // test for Q_INVOCABLE, Q_SCRIPTABLE, etc. and set the flags
+ // in FunctionDef accordingly
+ bool testFunctionAttribute(FunctionDef *def);
+ bool testFunctionAttribute(Token tok, FunctionDef *def);
+ bool testFunctionRevision(FunctionDef *def);
+ QTypeRevision parseRevision();
+
+ bool skipCxxAttributes();
+
+ void checkSuperClasses(ClassDef *def);
+ void checkProperties(ClassDef* cdef);
+ bool testForFunctionModifiers(FunctionDef *def);
+
+ void checkListSizes(const ClassDef &def);
};
+#endif // -- QtScxml
inline QByteArray noRef(const QByteArray &type)
{
if (type.endsWith('&')) {
if (type.endsWith("&&"))
- return type.left(type.length()-2);
- return type.left(type.length()-1);
+ return type.left(type.size()-2);
+ return type.left(type.size()-1);
}
return type;
}
diff --git a/tools/qscxmlc/moc_patches/generator.cpp.patch b/tools/qscxmlc/moc_patches/generator.cpp.patch
new file mode 100644
index 0000000..26906d8
--- /dev/null
+++ b/tools/qscxmlc/moc_patches/generator.cpp.patch
@@ -0,0 +1,286 @@
+--- .upstream/generator.cpp 2024-02-01 11:08:00.055494626 +0100
++++ generator.cpp 2024-02-05 14:18:32.229391845 +0100
+@@ -4,7 +4,9 @@
+ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+ #include "generator.h"
++#if 0 // -- QtScxml
+ #include "cbordevice.h"
++#endif // -- QtScxml
+ #include "outputrevision.h"
+ #include "utils.h"
+ #include <QtCore/qmetatype.h>
+@@ -25,6 +27,29 @@
+
+ using namespace QtMiscUtils;
+
++// -- QtScxml
++void fprintf(QIODevice &out, const char *fmt, ...)
++{
++ va_list argp;
++ va_start(argp, fmt);
++ const int bufSize = 4096;
++ char buf[bufSize];
++ vsnprintf(buf, bufSize, fmt, argp);
++ va_end(argp);
++ out.write(buf);
++}
++
++void fputc(char c, QIODevice &out)
++{
++ out.write(&c, 1);
++}
++
++void fputs(const char *s, QIODevice &out)
++{
++ out.write(s);
++}
++// -- QtScxml
++
+ uint nameToBuiltinType(const QByteArray &name)
+ {
+ if (name.isEmpty())
+@@ -57,22 +82,25 @@
+ return nullptr;
+ }
+
+- Generator::Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes,
+- const QHash<QByteArray, QByteArray> &knownQObjectClasses,
+- const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile,
+- bool requireCompleteTypes)
+- : parser(moc),
+- out(outfile),
+- cdef(classDef),
+- metaTypes(metaTypes),
+- knownQObjectClasses(knownQObjectClasses),
+- knownGadgets(knownGadgets),
+- requireCompleteTypes(requireCompleteTypes)
+- {
+- if (cdef->superclassList.size())
+- purestSuperClass = cdef->superclassList.constFirst().classname;
++// -- QtScxml
++Generator::Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes,
++ const QHash<QByteArray, QByteArray> &knownQObjectClasses,
++ const QHash<QByteArray, QByteArray> &knownGadgets,
++ QIODevice &outfile,
++ bool requireCompleteTypes)
++ : out(outfile),
++ cdef(classDef),
++ metaTypes(metaTypes),
++ knownQObjectClasses(knownQObjectClasses),
++ knownGadgets(knownGadgets),
++ requireCompleteTypes(requireCompleteTypes)
++{
++ if (cdef->superclassList.size())
++ purestSuperClass = cdef->superclassList.constFirst().classname;
+ }
++// -- QtScxml
+
++#if 0 // -- QtScxml
+ static inline qsizetype lengthOfEscapeSequence(const QByteArray &s, qsizetype i)
+ {
+ if (s.at(i) != '\\' || i >= s.size() - 1)
+@@ -98,7 +126,7 @@
+
+ // Prints \a s to \a out, breaking it into lines of at most ColumnWidth. The
+ // opening and closing quotes are NOT included (it's up to the caller).
+-static void printStringWithIndentation(FILE *out, const QByteArray &s)
++static void printStringWithIndentation(QIODevice &out, const QByteArray &s) // -- QtScxml
+ {
+ static constexpr int ColumnWidth = 72;
+ const qsizetype len = s.size();
+@@ -116,6 +144,7 @@
+ idx += spanLen;
+ } while (idx < len);
+ }
++#endif // -- QtSxcml
+
+ void Generator::strreg(const QByteArray &s)
+ {
+@@ -270,12 +299,21 @@
+ qualifiedClassNameIdentifier.constData(), qualifiedClassNameIdentifier.constData());
+ {
+ char comma = 0;
+- for (const QByteArray &str : strings) {
++// -- QtScxml
++ for (qsizetype i = 0, end = strings.size(); i < end; ++i) {
+ if (comma)
+ fputc(comma, out);
+- printStringWithIndentation(out, str);
++ fprintf(out, "\n {");
++ const QByteArray s = strings.at(i);
++ const qsizetype len = s.size();
++ for (qsizetype charPos = 0; charPos < len; ++charPos)
++ fprintf(out, "char(0x%.2x),", static_cast<quint8>(s.at(charPos)));
++ const bool isLast = (i == end - 1);
++ fprintf(out, "char(0)%s // %d: %s", isLast ? "}" : "},", i, s.constData());
+ comma = ',';
+ }
++// -- QtScxml
++
+ }
+ fprintf(out, "\n);\n"
+ "#else // !QT_MOC_HAS_STRINGDATA\n");
+@@ -298,8 +336,10 @@
+ qsizetype methodCount = 0;
+ if (qAddOverflow(cdef->signalList.size(), cdef->slotList.size(), &methodCount)
+ || qAddOverflow(cdef->methodList.size(), methodCount, &methodCount)) {
+- parser->error("internal limit exceeded: the total number of member functions"
++// -- QtScxml
++ qFatal("internal limit exceeded: the total number of member functions"
+ " (including signals and slots) is too big.");
++// -- QtScxml
+ }
+
+ fprintf(out, " %4" PRIdQSIZETYPE ", %4d, // methods\n", methodCount, methodCount ? index : 0);
+@@ -346,7 +386,9 @@
+ if (qAddOverflow(cdef->propertyList.size(), cdef->enumList.size(), &propEnumCount)
+ || qAddOverflow(propEnumCount, qsizetype(1), &propEnumCount)
+ || propEnumCount >= std::numeric_limits<int>::max()) {
+- parser->error("internal limit exceeded: number of property and enum metatypes is too big.");
++// -- QtScxml
++ qFatal("internal limit exceeded: number of property and enum metatypes is too big.");
++// -- QtScxml
+ }
+ int initialMetaTypeOffset = int(propEnumCount);
+
+@@ -585,7 +627,7 @@
+ fprintf(out, "\nvoid *%s::qt_metacast(const char *_clname)\n{\n", cdef->qualified.constData());
+ fprintf(out, " if (!_clname) return nullptr;\n");
+ fprintf(out, " if (!strcmp(_clname, qt_meta_stringdata_%s.stringdata0))\n"
+- " return static_cast<void*>(this);\n",
++ " return static_cast<void*>(this);\n",
+ qualifiedClassNameIdentifier.constData());
+
+ // for all superclasses but the first one
+@@ -631,7 +673,9 @@
+ //
+ // Generate plugin meta data
+ //
++#if 0 // -- QtScxml
+ generatePluginMetaData();
++#endif // -- QtScxml
+
+ //
+ // Generate function to make sure the non-class signals exist in the parent classes
+@@ -1100,6 +1144,13 @@
+ const FunctionDef &f = methodList.at(methodindex);
+ Q_ASSERT(!f.normalizedType.isEmpty());
+ fprintf(out, " case %d: ", methodindex);
++ // -- QtScxml
++ if (f.implementation) {
++ fprintf(out, f.implementation, "_o", methodindex);
++ fprintf(out, " break;\n");
++ continue;
++ }
++ // -- QtScxml
+ if (f.normalizedType != "void")
+ fprintf(out, "{ %s _r = ", noRef(f.normalizedType).constData());
+ fprintf(out, "_t->");
+@@ -1178,6 +1229,10 @@
+ const FunctionDef &f = cdef->signalList.at(methodindex);
+ if (f.wasCloned || !f.inPrivateClass.isEmpty() || f.isStatic)
+ continue;
++ // -- QtScxml
++ if (f.mangledName.isEmpty())
++ continue;
++ // -- QtScxml
+ anythingUsed = true;
+ fprintf(out, " {\n");
+ fprintf(out, " using _t = %s (%s::*)(",f.type.rawName.constData() , cdef->classname.constData());
+@@ -1200,7 +1255,7 @@
+ else
+ fprintf(out, ");\n");
+ fprintf(out, " if (_t _q_method = &%s::%s; *reinterpret_cast<_t *>(_a[1]) == _q_method) {\n",
+- cdef->classname.constData(), f.name.constData());
++ cdef->classname.constData(), f.mangledName.constData()); // -- QtScxml
+ fprintf(out, " *result = %d;\n", methodindex);
+ fprintf(out, " return;\n");
+ fprintf(out, " }\n }\n");
+@@ -1295,8 +1350,11 @@
+ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s().value(); break;\n",
+ propindex, p.type.constData(), prefix.constData(), p.bind.constData());
+ else if (!p.read.isEmpty())
+- fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s(); break;\n",
+- propindex, p.type.constData(), prefix.constData(), p.read.constData());
++ // -- QtScxml
++ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s%s; break;\n",
++ propindex, p.type.constData(), prefix.constData(), p.read.constData(),
++ p.read.endsWith(')') ? "" : "()");
++ // -- QtScxml
+ else
+ fprintf(out, " case %d: *reinterpret_cast< %s*>(_v) = %s%s; break;\n",
+ propindex, p.type.constData(), prefix.constData(), p.member.constData());
+@@ -1428,6 +1486,10 @@
+ {
+ if (def->wasCloned || def->isAbstract)
+ return;
++// -- QtScxml
++ if (def->implementation)
++ return;
++// -- QtScxml
+ fprintf(out, "\n// SIGNAL %d\n%s %s::%s(",
+ index, def->type.name.constData(), cdef->qualified.constData(), def->name.constData());
+
+@@ -1476,10 +1538,8 @@
+ if (def->normalizedType == "void") {
+ fprintf(out, "nullptr");
+ } else {
+- if (def->returnTypeIsVolatile)
+- fprintf(out, "const_cast<void*>(reinterpret_cast<const volatile void*>(std::addressof(_t0)))");
+- else
+- fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t0)))");
++ // -- QtScxml removed unused returnTypeIsVolatile
++ fprintf(out, "const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t0)))");
+ }
+ int i;
+ for (i = 1; i < offset; ++i)
+@@ -1494,6 +1554,36 @@
+ fprintf(out, "}\n");
+ }
+
++// -- QtScxml
++void Generator::generateAccessorDefs()
++{
++ for (int propindex = 0; propindex < cdef->propertyList.size(); ++propindex) {
++ const PropertyDef &p = cdef->propertyList.at(propindex);
++ if (p.read.isEmpty() || p.mangledName.isEmpty())
++ continue;
++
++ fprintf(out, "bool %s::%s() const\n{\n return %s;\n}\n\n", cdef->classname.constData(),
++ p.mangledName.constData(), p.read.constData());
++ }
++}
++
++void Generator::generateSignalDefs()
++{
++ for (int methodindex = 0; methodindex < cdef->signalList.size(); ++methodindex) {
++ const FunctionDef &f = cdef->signalList.at(methodindex);
++ if (!f.implementation || f.mangledName.isEmpty())
++ continue;
++
++ fprintf(out, "void %s::%s(bool _t1)\n{\n", cdef->classname.constData(),
++ f.mangledName.constData());
++ fprintf(out, " void *_a[] = { nullptr, "
++ "const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };\n ");
++ fprintf(out, f.implementation, "this", methodindex);
++ fprintf(out, "\n}\n\n");
++ }
++}
++
++#if 0
+ static CborError jsonValueToCbor(CborEncoder *parent, const QJsonValue &v);
+ static CborError jsonObjectToCbor(CborEncoder *parent, const QJsonObject &o)
+ {
+@@ -1629,7 +1719,11 @@
+
+ #define CBOR_ENCODER_WRITER_CONTROL 1
+ #define CBOR_ENCODER_WRITE_FUNCTION CborDevice::callback
++#endif
++// -- QtScxml
+
+ QT_END_NAMESPACE
+
++#if 0 // -- QtScxml
+ #include "cborencoder.c"
++#endif // -- QtScxml
diff --git a/tools/qscxmlc/moc_patches/generator.h.patch b/tools/qscxmlc/moc_patches/generator.h.patch
new file mode 100644
index 0000000..4fc3325
--- /dev/null
+++ b/tools/qscxmlc/moc_patches/generator.h.patch
@@ -0,0 +1,51 @@
+--- .upstream/generator.h 2023-06-22 20:40:43.078529554 +0200
++++ generator.h 2024-02-05 14:06:04.258179059 +0100
+@@ -6,23 +6,34 @@
+
+ #include "moc.h"
+
++// -- QtScxml
++#include <QtCore/qhash.h>
++#include <QtCore/qlist.h>
++#include <QtCore/qiodevice.h>
++// -- QtScxml
++
+ QT_BEGIN_NAMESPACE
+
+ class Generator
+ {
+- Moc *parser = nullptr;
+- FILE *out;
++ QIODevice &out; // -- QtScxml
+ ClassDef *cdef;
+ QList<uint> meta_data;
+
+ public:
+- Generator(Moc *moc, ClassDef *classDef, const QList<QByteArray> &metaTypes,
++ Generator(ClassDef *classDef, const QList<QByteArray> &metaTypes,
+ const QHash<QByteArray, QByteArray> &knownQObjectClasses,
+- const QHash<QByteArray, QByteArray> &knownGadgets, FILE *outfile = nullptr,
++ const QHash<QByteArray, QByteArray> &knownGadgets,
++ QIODevice &outfile, // -- QtScxml
+ bool requireCompleteTypes = false);
+ void generateCode();
+ qsizetype registeredStringsCount() { return strings.size(); };
+
++// -- QtScxml
++ void generateAccessorDefs();
++ void generateSignalDefs();
++// -- QtScxml
++
+ private:
+ bool registerableMetaType(const QByteArray &propertyType);
+ void registerClassInfoStrings();
+@@ -41,7 +52,9 @@
+ void generateMetacall();
+ void generateStaticMetacall();
+ void generateSignal(const FunctionDef *def, int index);
++#if 0 // -- QtScxml
+ void generatePluginMetaData();
++#endif // -- QtScxml
+ QMultiMap<QByteArray, int> automaticPropertyMetaTypesHelper();
+ QMap<int, QMultiMap<QByteArray, int>>
+ methodsWithAutomaticTypesHelper(const QList<FunctionDef> &methodList);
diff --git a/tools/qscxmlc/moc_patches/moc.cpp.patch b/tools/qscxmlc/moc_patches/moc.cpp.patch
new file mode 100644
index 0000000..7b55c65
--- /dev/null
+++ b/tools/qscxmlc/moc_patches/moc.cpp.patch
@@ -0,0 +1,29 @@
+--- .upstream/moc.cpp 2024-02-01 11:08:00.055494626 +0100
++++ moc.cpp 2024-02-05 14:06:04.258179059 +0100
+@@ -12,14 +12,15 @@
+ #include <QtCore/qdir.h>
+ #include <QtCore/qjsondocument.h>
+
+-// for normalizeTypeInternal
+-#include <private/qmetaobject_moc_p.h>
+-#include <private/qduplicatetracker_p.h>
++// -- QtScxml
++#include <QtCore/qjsonobject.h>
++// -- QtScxml
+
+ QT_BEGIN_NAMESPACE
+
+ using namespace Qt::StringLiterals;
+
++#if 0 // -- QtScxml
+ // only moc needs this function
+ static QByteArray normalizeType(const QByteArray &ba)
+ {
+@@ -1997,6 +1998,7 @@
+ }
+ }
+ }
++#endif // -- QtScxml
+
+ QJsonObject ClassDef::toJson() const
+ {
diff --git a/tools/qscxmlc/moc_patches/moc.h.patch b/tools/qscxmlc/moc_patches/moc.h.patch
new file mode 100644
index 0000000..dcdac6b
--- /dev/null
+++ b/tools/qscxmlc/moc_patches/moc.h.patch
@@ -0,0 +1,94 @@
+--- .upstream/moc.h 2024-02-05 14:10:55.937714571 +0100
++++ moc.h 2024-02-05 14:12:52.133597464 +0100
+@@ -4,14 +4,12 @@
+ #ifndef MOC_H
+ #define MOC_H
+
+-#include "parser.h"
+-#include <qstringlist.h>
+-#include <qmap.h>
+-#include <qjsondocument.h>
+-#include <qjsonarray.h>
+-#include <qjsonobject.h>
+-#include <qtyperevision.h>
+-#include <stdio.h>
++// -- QtScxml
++#include <QtCore/qmap.h>
++#include <QtCore/qpair.h>
++#include <QtCore/qjsondocument.h>
++#include <QtCore/qjsonarray.h>
++// -- QtScxml
+
+ #include <private/qtools_p.h>
+
+@@ -23,16 +21,18 @@
+ {
+ enum ReferenceType { NoReference, Reference, RValueReference, Pointer };
+
+- inline Type() : isVolatile(false), isScoped(false), firstToken(NOTOKEN), referenceType(NoReference) {}
++ inline Type() : isVolatile(false), isScoped(false), /* firstToken(NOTOKEN) -- QtScxml ,*/ referenceType(NoReference) {}
+ inline explicit Type(const QByteArray &_name)
+- : name(_name), rawName(name), isVolatile(false), isScoped(false), firstToken(NOTOKEN), referenceType(NoReference) {}
++ : name(_name), rawName(name), isVolatile(false), isScoped(false), /* firstToken(NOTOKEN) -- QtScxml ,*/ referenceType(NoReference) {}
+ QByteArray name;
+ //When used as a return type, the type name may be modified to remove the references.
+ // rawName is the type as found in the function signature
+ QByteArray rawName;
+ uint isVolatile : 1;
+ uint isScoped : 1;
++#if 0 // -- QtScxml
+ Token firstToken;
++#endif // -- QtScxml
+ ReferenceType referenceType;
+ };
+ Q_DECLARE_TYPEINFO(Type, Q_RELOCATABLE_TYPE);
+@@ -82,8 +82,9 @@
+ bool inlineCode = false;
+ bool wasCloned = false;
+
++#if 0 // -- QtScxml
+ bool returnTypeIsVolatile = false;
+-
++#endif // -- QtScxml
+ bool isCompat = false;
+ bool isInvokable = false;
+ bool isScriptable = false;
+@@ -97,6 +98,11 @@
+
+ QJsonObject toJson() const;
+ static void accessToJson(QJsonObject *obj, Access acs);
++
++// -- QtScxml
++ QByteArray mangledName;
++ const char *implementation = nullptr;
++// -- QtScxml
+ };
+ Q_DECLARE_TYPEINFO(FunctionDef, Q_RELOCATABLE_TYPE);
+
+@@ -124,6 +130,10 @@
+ qsizetype location = -1; // token index, used for error reporting
+
+ QJsonObject toJson() const;
++
++// -- QtScxml
++ QByteArray mangledName;
++// -- QtScxml
+ };
+ Q_DECLARE_TYPEINFO(PropertyDef, Q_RELOCATABLE_TYPE);
+
+@@ -204,6 +214,7 @@
+ };
+ Q_DECLARE_TYPEINFO(NamespaceDef, Q_RELOCATABLE_TYPE);
+
++#if 0 // -- QtScxml
+ class Moc : public Parser
+ {
+ public:
+@@ -293,6 +304,7 @@
+
+ void checkListSizes(const ClassDef &def);
+ };
++#endif // -- QtScxml
+
+ inline QByteArray noRef(const QByteArray &type)
+ {
diff --git a/tools/qscxmlc/moc_patches/outputrevision.h.patch b/tools/qscxmlc/moc_patches/outputrevision.h.patch
new file mode 100644
index 0000000..be3c2eb
--- /dev/null
+++ b/tools/qscxmlc/moc_patches/outputrevision.h.patch
@@ -0,0 +1,16 @@
+--- .upstream/outputrevision.h 2022-05-23 08:46:14.490945334 +0200
++++ outputrevision.h 2024-02-05 14:06:04.254179068 +0100
+@@ -4,7 +4,13 @@
+ #ifndef OUTPUTREVISION_H
+ #define OUTPUTREVISION_H
+
++#include <QtCore/qglobal.h> // -- QtScxml
++
++QT_BEGIN_NAMESPACE // -- QtScxml
++
+ // if the output revision changes, you MUST change it in qobjectdefs.h too
+ enum { mocOutputRevision = 68 }; // moc format output revision
+
++QT_END_NAMESPACE // -- QtScxml
++
+ #endif // OUTPUTREVISION_H
diff --git a/tools/qscxmlc/moc_patches/update_moc.sh b/tools/qscxmlc/moc_patches/update_moc.sh
new file mode 100755
index 0000000..410c9da
--- /dev/null
+++ b/tools/qscxmlc/moc_patches/update_moc.sh
@@ -0,0 +1,94 @@
+#!/bin/bash
+# Copyright (C) 2021 The Qt Company Ltd.
+# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
+
+usage() {
+ cat <<EOF >&2
+
+Usage: update_moc [gu] [moc directory]
+
+The moc directory should be qtbase's moc source directory
+(absolute or relative). The script must be run from the directory
+containing the moc files (moc.h et. al).
+
+The script has two modes:
+
+'g' Generates updated patches of the current qtscxml-specific moc
+ changes. Use this when the qtscxml-specific parts in moc files
+ have changed and the patches should be updated accordingly.
+
+'u' Updates the moc from the upstream qtbase moc.
+ This mode gets the applicable qtbase moc files from the provided
+ directory, and updates their qtscxml copies by applying patches to them.
+ If you have not modified qtscxml moc code and just want to update the moc
+ code from upstream, this is the one you should use.
+
+Examples:
+moc_patches/update_moc g ../../../qtbase/src/tools/moc
+moc_patches/update_moc u ../../../qtbase/src/tools/moc
+
+EOF
+ die "$@"
+}
+
+checkFile () {
+ for f
+ do [[ -f "$f" ]] || die "Error: file \"$f\" does not exist."
+ done
+}
+
+warn () { echo "$@" >&2; }
+die () {
+ [[ -h .upstream ]] && rm .upstream
+ warn "$@"
+ exit 1
+}
+
+generate_patch() {
+ echo Generating patches recording how qscxmlc moc differs from upstream.
+
+ # Link the upstream moc files to create a patch file with filepaths
+ # that are independent of the actual used upstream moc location.
+ ln -s "$MOC_DIR" .upstream
+
+ for file in "${FILES[@]}"
+ do
+ checkFile "$file" ".upstream/$file"
+ diff -u ".upstream/$file" "$file" > "moc_patches/$file.patch"
+ echo Generated "moc_patches/$file.patch"
+ done
+ # tidy up
+ rm .upstream
+}
+
+update_moc() {
+ echo Updating qscxmlc moc from upstream by applying saved patches.
+
+ for file in "${FILES[@]}"
+ do
+ checkFile "moc_patches/$file.patch" "$MOC_DIR/$file"
+ echo Patching file: "$file" with "moc_patches/$file.patch"
+ # overwrite the current file from upstream
+ cp "$MOC_DIR/$file" "$file"
+ if patch "$file" "moc_patches/$file.patch"
+ then echo Patched "$file"
+ else warn "Please hand-patch $file; see $file.orig and $file.rej and tidy them away when you are done."
+ fi
+ done
+}
+
+MODE="$1"
+MOC_DIR="$2"
+FILES=( "outputrevision.h" "moc.cpp" "moc.h" "generator.h" "generator.cpp" )
+
+[[ -f moc_patches/update_moc.sh ]] || usage "Error: script must be run from the tools/qscxmlc/ directory."
+[[ -n "$MOC_DIR" ]] || usage "Error: You did not specify a moc directory."
+[[ -d "$MOC_DIR" ]] || usage "Error: moc directory \"$MOC_DIR\" does not exist."
+
+case "$MODE" in
+ g) generate_patch ;;
+ u) update_moc ;;
+ *) usage "Error: mode \"$MODE\" is not recognized." ;;
+esac
+
+echo Done
diff --git a/tools/qscxmlc/outputrevision.h b/tools/qscxmlc/outputrevision.h
index 89a0b6e..c170203 100644
--- a/tools/qscxmlc/outputrevision.h
+++ b/tools/qscxmlc/outputrevision.h
@@ -1,41 +1,16 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef OUTPUTREVISION_H
#define OUTPUTREVISION_H
-#include <QtCore/qglobal.h>
+#include <QtCore/qglobal.h> // -- QtScxml
-QT_BEGIN_NAMESPACE
+QT_BEGIN_NAMESPACE // -- QtScxml
// if the output revision changes, you MUST change it in qobjectdefs.h too
-enum { mocOutputRevision = 67 }; // moc format output revision
+enum { mocOutputRevision = 68 }; // moc format output revision
-QT_END_NAMESPACE
+QT_END_NAMESPACE // -- QtScxml
#endif // OUTPUTREVISION_H
diff --git a/tools/qscxmlc/qscxmlc.cpp b/tools/qscxmlc/qscxmlc.cpp
index d83e786..fdf89a1 100644
--- a/tools/qscxmlc/qscxmlc.cpp
+++ b/tools/qscxmlc/qscxmlc.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include <QtScxml/private/qscxmlcompiler_p.h>
#include <QtScxml/qscxmltabledata.h>
@@ -35,7 +10,7 @@
#include <QCommandLineParser>
#include <QFile>
#include <QFileInfo>
-#include <QTextCodec>
+#include <QStringConverter>
QT_BEGIN_NAMESPACE
@@ -68,17 +43,17 @@ int write(TranslationUnit *tu)
}
// Make sure it outputs UTF-8, as that is what C++ expects.
- QTextCodec *utf8 = QTextCodec::codecForName("UTF-8");
+ auto utf8 = QStringConverter::encodingForName("UTF-8");
if (!utf8) {
- errs << QStringLiteral("Error: cannot find a QTextCodec for generating UTF-8.");
+ errs << QStringLiteral("Error: cannot find a QStringConverter for generating UTF-8.");
return NoTextCodecError;
}
QTextStream h(&outH);
- h.setCodec(utf8);
+ h.setEncoding(utf8.value());
h.setGenerateByteOrderMark(true);
QTextStream c(&outCpp);
- c.setCodec(utf8);
+ c.setEncoding(utf8.value());
c.setGenerateByteOrderMark(true);
CppDumper dumper(h, c);
dumper.dump(tu);
@@ -93,7 +68,7 @@ static void collectAllDocuments(DocumentModel::ScxmlDocument *doc,
QList<DocumentModel::ScxmlDocument *> *docs)
{
docs->append(doc);
- for (DocumentModel::ScxmlDocument *subDoc : qAsConst(doc->allSubDocuments))
+ for (DocumentModel::ScxmlDocument *subDoc : std::as_const(doc->allSubDocuments))
collectAllDocuments(subDoc, docs);
}
@@ -138,12 +113,12 @@ int run(const QStringList &arguments)
const QStringList inputFiles = cmdParser.positionalArguments();
- if (inputFiles.count() < 1) {
+ if (inputFiles.size() < 1) {
errs << QCoreApplication::translate("main", "Error: no input file.") << Qt::endl;
cmdParser.showHelp(NoInputFilesError);
}
- if (inputFiles.count() > 1) {
+ if (inputFiles.size() > 1) {
errs << QCoreApplication::translate("main", "Error: unexpected argument(s): %1")
.arg(inputFiles.mid(1).join(QLatin1Char(' '))) << Qt::endl;
cmdParser.showHelp(NoInputFilesError);
@@ -217,7 +192,7 @@ int run(const QStringList &arguments)
docs.pop_front();
- for (DocumentModel::ScxmlDocument *doc : qAsConst(docs)) {
+ for (DocumentModel::ScxmlDocument *doc : std::as_const(docs)) {
auto name = doc->root->name;
auto prefix = name;
if (name.isEmpty()) {
diff --git a/tools/qscxmlc/qscxmlc.h b/tools/qscxmlc/qscxmlc.h
index 81c2427..160ad76 100644
--- a/tools/qscxmlc/qscxmlc.h
+++ b/tools/qscxmlc/qscxmlc.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef QSCXMLC_H
#define QSCXMLC_H
@@ -43,7 +7,6 @@
#include <QtCore/qglobal.h>
QT_BEGIN_NAMESPACE
-class QStringList;
int run(const QStringList &arguments);
diff --git a/tools/qscxmlc/qscxmlc.pri b/tools/qscxmlc/qscxmlc.pri
deleted file mode 100644
index 4c7c991..0000000
--- a/tools/qscxmlc/qscxmlc.pri
+++ /dev/null
@@ -1,32 +0,0 @@
-DEFINES += BUILD_QSCXMLC
-
-SOURCES += \
- $$PWD/generator.cpp \
- $$PWD/qscxmlc.cpp \
- $$PWD/scxmlcppdumper.cpp
-
-HEADERS += \
- $$PWD/moc.h \
- $$PWD/generator.h \
- $$PWD/outputrevision.h \
- $$PWD/qscxmlc.h \
- $$PWD/utils.h \
- $$PWD/scxmlcppdumper.h
-
-HEADERS += \
- $$PWD/../../src/scxml/qscxmlcompiler.h \
- $$PWD/../../src/scxml/qscxmlcompiler_p.h \
- $$PWD/../../src/scxml/qscxmlglobals.h \
- $$PWD/../../src/scxml/qscxmlexecutablecontent.h \
- $$PWD/../../src/scxml/qscxmlexecutablecontent_p.h \
- $$PWD/../../src/scxml/qscxmlerror.h \
- $$PWD/../../src/scxml/qscxmltabledata.h
-
-SOURCES += \
- $$PWD/../../src/scxml/qscxmlcompiler.cpp \
- $$PWD/../../src/scxml/qscxmlexecutablecontent.cpp \
- $$PWD/../../src/scxml/qscxmlerror.cpp \
- $$PWD/../../src/scxml/qscxmltabledata.cpp
-
-DEFINES += QT_NO_CAST_TO_ASCII QT_NO_CAST_FROM_ASCII
-INCLUDEPATH *= $$QT.scxml.includes $$QT.scxml_private.includes
diff --git a/tools/qscxmlc/qscxmlc.pro b/tools/qscxmlc/qscxmlc.pro
deleted file mode 100644
index 7620d3d..0000000
--- a/tools/qscxmlc/qscxmlc.pro
+++ /dev/null
@@ -1,16 +0,0 @@
-option(host_build)
-
-QT = core-private
-
-include(qscxmlc.pri)
-
-TARGET = qscxmlc
-CONFIG += console c++11
-
-SOURCES += \
- main.cpp
-
-load(qt_tool)
-load(resources)
-
-RESOURCES += templates.qrc
diff --git a/tools/qscxmlc/scxmlcppdumper.cpp b/tools/qscxmlc/scxmlcppdumper.cpp
index 1aca09e..765498c 100644
--- a/tools/qscxmlc/scxmlcppdumper.cpp
+++ b/tools/qscxmlc/scxmlcppdumper.cpp
@@ -1,30 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#include "scxmlcppdumper.h"
#include "generator.h"
@@ -67,7 +42,7 @@ QString cEscape(const QString &str)
{
QString res;
int lastI = 0;
- for (int i = 0; i < str.length(); ++i) {
+ for (int i = 0; i < str.size(); ++i) {
QChar c = str.at(i);
if (c < QLatin1Char(' ') || c == QLatin1Char('\\') || c == QLatin1Char('\"')) {
res.append(str.mid(lastI, i - lastI));
@@ -109,27 +84,26 @@ static void genTemplate(QTextStream &out, const QString &filename, const Replace
qFatal("Unable to open template '%s'", qPrintable(filename));
}
Q_ASSERT(file.compressionAlgorithm() == QResource::NoCompression);
- QByteArray data;
- data = QByteArray::fromRawData(reinterpret_cast<const char *>(file.data()),
- int(file.size()));
- const QString t = QString::fromLatin1(data);
- data.clear();
+ const QString data = QString::fromLatin1(
+ QByteArray::fromRawData(reinterpret_cast<const char *>(file.data()), int(file.size()))
+ );
+ const QStringView t { data };
int start = 0;
for (int openIdx = t.indexOf(QStringLiteral("${"), start); openIdx >= 0; openIdx =
t.indexOf(QStringLiteral("${"), start)) {
- out << t.midRef(start, openIdx - start);
+ out << t.mid(start, openIdx - start);
openIdx += 2;
const int closeIdx = t.indexOf(QLatin1Char('}'), openIdx);
Q_ASSERT(closeIdx >= openIdx);
- QString key = t.mid(openIdx, closeIdx - openIdx);
+ QString key = t.mid(openIdx, closeIdx - openIdx).toString();
if (!replacements.contains(key)) {
qFatal("Replacing '%s' failed: no replacement found", qPrintable(key));
}
out << replacements.value(key);
start = closeIdx + 1;
}
- out << t.midRef(start);
+ out << t.mid(start);
}
static const char *headerStart =
@@ -163,7 +137,7 @@ static void generateList(QString &out, std::function<QString(int)> next)
if (i != 0)
line += QLatin1Char(',');
- if (line.length() + nr.length() + 1 > maxLineLength) {
+ if (line.size() + nr.size() + 1 > maxLineLength) {
out += line + QLatin1Char('\n');
line.clear();
} else if (i != 0) {
@@ -267,8 +241,8 @@ void generateTables(const GeneratedTableData &td, Replacements &replacements)
return QString();
const int length = strings.at(idx).size();
- const QString str = QStringLiteral("STR_LIT(%1, %2, %3)").arg(
- QString::number(idx), QString::number(ucharCount), QString::number(length));
+ const QString str = QStringLiteral("%1, %2").arg(
+ QString::number(ucharCount), QString::number(length));
ucharCount += length + 1;
return str;
});
@@ -365,8 +339,8 @@ void generateCppDataModelEvaluators(const GeneratedTableData::DataModelInfo &inf
int createFactoryId(QStringList &factories, const QString &className,
const QString &namespacePrefix,
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &namelist,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters)
+ const QList<QScxmlExecutableContent::StringId> &namelist,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters)
{
const int idx = factories.size();
@@ -421,10 +395,10 @@ void CppDumper::dump(TranslationUnit *unit)
}
QStringList classNames;
- QVector<GeneratedTableData> tables;
- QVector<GeneratedTableData::MetaDataInfo> metaDataInfos;
- QVector<GeneratedTableData::DataModelInfo> dataModelInfos;
- QVector<QStringList> factories;
+ QList<GeneratedTableData> tables;
+ QList<GeneratedTableData::MetaDataInfo> metaDataInfos;
+ QList<GeneratedTableData::DataModelInfo> dataModelInfos;
+ QList<QStringList> factories;
auto docs = m_translationUnit->allDocuments;
tables.resize(docs.size());
metaDataInfos.resize(tables.size());
@@ -438,8 +412,8 @@ void CppDumper::dump(TranslationUnit *unit)
GeneratedTableData::build(doc, &tables[i], metaDataInfo, &dataModelInfos[i],
[this, &factories, i, &classnameForDocument, &namespacePrefix](
const QScxmlExecutableContent::InvokeInfo &invokeInfo,
- const QVector<QScxmlExecutableContent::StringId> &names,
- const QVector<QScxmlExecutableContent::ParameterInfo> &parameters,
+ const QList<QScxmlExecutableContent::StringId> &names,
+ const QList<QScxmlExecutableContent::ParameterInfo> &parameters,
const QSharedPointer<DocumentModel::ScxmlDocument> &content) -> int {
QString className;
if (invokeInfo.expr == QScxmlExecutableContent::NoEvaluator) {
@@ -538,13 +512,13 @@ void CppDumper::writeImplStart()
<< Qt::endl;
QStringList includes;
- for (DocumentModel::ScxmlDocument *doc : qAsConst(m_translationUnit->allDocuments)) {
+ for (DocumentModel::ScxmlDocument *doc : std::as_const(m_translationUnit->allDocuments)) {
switch (doc->root->dataModel) {
case DocumentModel::Scxml::NullDataModel:
includes += l("QScxmlNullDataModel");
break;
case DocumentModel::Scxml::JSDataModel:
- includes += l("QScxmlEcmaScriptDataModel");
+ includes += l("QScxmlDataModel");
break;
case DocumentModel::Scxml::CppDataModel:
includes += doc->root->cppDataModelHeaderName;
@@ -559,8 +533,10 @@ void CppDumper::writeImplStart()
cpp << l("#include \"") << headerName << l("\"") << Qt::endl;
cpp << Qt::endl
<< QStringLiteral("#include <qscxmlinvokableservice.h>") << Qt::endl
- << QStringLiteral("#include <qscxmltabledata.h>") << Qt::endl;
- for (const QString &inc : qAsConst(includes)) {
+ << QStringLiteral("#include <qscxmltabledata.h>") << Qt::endl
+ << QStringLiteral("#include <QtCore/qtmochelpers.h>") << Qt::endl;
+
+ for (const QString &inc : std::as_const(includes)) {
cpp << l("#include <") << inc << l(">") << Qt::endl;
}
cpp << Qt::endl
@@ -585,8 +561,11 @@ void CppDumper::writeImplBody(const GeneratedTableData &table,
dataModelInitialization = l("stateMachine.setDataModel(&dataModel);");
break;
case DocumentModel::Scxml::JSDataModel:
- dataModelField = l("QScxmlEcmaScriptDataModel dataModel;");
- dataModelInitialization = l("stateMachine.setDataModel(&dataModel);");
+ dataModelField = l("std::unique_ptr<QScxmlDataModel> dataModel;");
+ dataModelInitialization = l(
+ " dataModel.reset(QScxmlDataModel::createScxmlDataModel(QStringLiteral(\"ecmascriptdatamodel\")));\n"
+ " stateMachine.setDataModel(dataModel.get());\n"
+ );
break;
case DocumentModel::Scxml::CppDataModel:
dataModelField = QStringLiteral("// Data model %1 is set from outside.").arg(
@@ -675,7 +654,7 @@ QString CppDumper::mangleIdentifier(const QString &str)
}
}
- for (int ei = str.length(); i != ei; ++i) {
+ for (int ei = str.size(); i != ei; ++i) {
auto c = str.at(i);
if ((c >= QLatin1Char('0') && c <= QLatin1Char('9')) || isNonDigit(c)) {
mangled += c;
@@ -750,7 +729,11 @@ QString CppDumper::generateMetaObject(const QString &className,
ClassDef classDef;
classDef.classname = className.toUtf8();
classDef.qualified = classDef.classname;
- classDef.superclassList << qMakePair(QByteArray("QScxmlStateMachine"), FunctionDef::Public);
+ classDef.superclassList << SuperClass {
+ QByteArray("QScxmlStateMachine"),
+ QByteArray(QT_STRINGIFY(QT_PREPEND_NAMESPACE(QScxmlStateMachine))),
+ FunctionDef::Public
+ };
classDef.hasQObject = true;
FunctionDef constructor;
constructor.name = className.toUtf8();
@@ -795,7 +778,6 @@ QString CppDumper::generateMetaObject(const QString &className,
signal.arguments << arg;
classDef.signalList << signal;
- ++classDef.notifyableProperties;
PropertyDef prop;
prop.name = stateName.toUtf8();
if (m_translationUnit->stateMethods)
diff --git a/tools/qscxmlc/scxmlcppdumper.h b/tools/qscxmlc/scxmlcppdumper.h
index 35849c0..a3123b0 100644
--- a/tools/qscxmlc/scxmlcppdumper.h
+++ b/tools/qscxmlc/scxmlcppdumper.h
@@ -1,36 +1,10 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:GPL-EXCEPT$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 3 as published by the Free Software
-** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef SCXMLCPPDUMPER_H
#define SCXMLCPPDUMPER_H
-#include "qscxmlglobals.h"
-
+#include <QtScxml/qscxmlglobals.h>
#include <QtScxml/private/qscxmlcompiler_p.h>
#include <QtScxml/private/qscxmltabledata_p.h>
diff --git a/tools/qscxmlc/templates.qrc b/tools/qscxmlc/templates.qrc
deleted file mode 100644
index a00f24e..0000000
--- a/tools/qscxmlc/templates.qrc
+++ /dev/null
@@ -1,7 +0,0 @@
-<RCC>
- <qresource prefix="/">
- <file compression-algorithm="none">data.t</file>
- <file compression-algorithm="none">decl.t</file>
- <file compression-algorithm="none">cppdatamodel.t</file>
- </qresource>
-</RCC>
diff --git a/tools/qscxmlc/utils.h b/tools/qscxmlc/utils.h
index 58cf924..358780a 100644
--- a/tools/qscxmlc/utils.h
+++ b/tools/qscxmlc/utils.h
@@ -1,41 +1,5 @@
-/****************************************************************************
-**
-** Copyright (C) 2016 The Qt Company Ltd.
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtScxml module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2016 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
#ifndef UTILS_H
#define UTILS_H
diff --git a/tools/tools.pro b/tools/tools.pro
deleted file mode 100644
index 6884612..0000000
--- a/tools/tools.pro
+++ /dev/null
@@ -1,2 +0,0 @@
-TEMPLATE = subdirs
-qtConfig(commandlineparser): SUBDIRS = qscxmlc