1 | // Copyright (C) 2016 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only |
3 | |
4 | #ifndef QSCXMLSTATEMACHINE_H |
5 | #define QSCXMLSTATEMACHINE_H |
6 | |
7 | #include <QtScxml/qscxmldatamodel.h> |
8 | #include <QtScxml/qscxmlerror.h> |
9 | #include <QtScxml/qscxmlevent.h> |
10 | #include <QtScxml/qscxmlcompiler.h> |
11 | #include <QtScxml/qscxmlinvokableservice.h> |
12 | |
13 | #include <QtCore/qlist.h> |
14 | #include <QtCore/qpointer.h> |
15 | #include <QtCore/qstring.h> |
16 | #include <QtCore/qurl.h> |
17 | #include <QtCore/qvariant.h> |
18 | |
19 | #include <functional> |
20 | |
21 | Q_MOC_INCLUDE(qscxmltabledata.h) |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | class QIODevice; |
25 | class QXmlStreamWriter; |
26 | class QTextStream; |
27 | class QScxmlTableData; |
28 | |
29 | class QScxmlStateMachinePrivate; |
30 | class Q_SCXML_EXPORT QScxmlStateMachine: public QObject |
31 | { |
32 | Q_DECLARE_PRIVATE(QScxmlStateMachine) |
33 | Q_OBJECT |
34 | Q_PROPERTY(bool running READ isRunning WRITE setRunning NOTIFY runningChanged) |
35 | Q_PROPERTY(bool initialized READ isInitialized |
36 | NOTIFY initializedChanged BINDABLE bindableInitialized) |
37 | Q_PROPERTY(QScxmlDataModel *dataModel READ dataModel WRITE setDataModel |
38 | NOTIFY dataModelChanged BINDABLE bindableDataModel) |
39 | Q_PROPERTY(QVariantMap initialValues READ initialValues WRITE setInitialValues |
40 | NOTIFY initialValuesChanged BINDABLE bindableInitialValues) |
41 | Q_PROPERTY(QList<QScxmlInvokableService*> invokedServices READ invokedServices |
42 | NOTIFY invokedServicesChanged BINDABLE bindableInvokedServices) |
43 | Q_PROPERTY(QString sessionId READ sessionId CONSTANT) |
44 | Q_PROPERTY(QString name READ name CONSTANT) |
45 | Q_PROPERTY(bool invoked READ isInvoked CONSTANT) |
46 | Q_PROPERTY(QList<QScxmlError> parseErrors READ parseErrors CONSTANT) |
47 | Q_PROPERTY(QScxmlCompiler::Loader *loader READ loader WRITE setLoader |
48 | NOTIFY loaderChanged BINDABLE bindableLoader) |
49 | Q_PROPERTY(QScxmlTableData *tableData READ tableData WRITE setTableData |
50 | NOTIFY tableDataChanged BINDABLE bindableTableData) |
51 | |
52 | protected: |
53 | explicit QScxmlStateMachine(const QMetaObject *metaObject, QObject *parent = nullptr); |
54 | QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *parent = nullptr); |
55 | |
56 | public: |
57 | static QScxmlStateMachine *fromFile(const QString &fileName); |
58 | static QScxmlStateMachine *fromData(QIODevice *data, const QString &fileName = QString()); |
59 | QList<QScxmlError> parseErrors() const; |
60 | |
61 | QString sessionId() const; |
62 | |
63 | bool isInvoked() const; |
64 | bool isInitialized() const; |
65 | QBindable<bool> bindableInitialized() const; |
66 | |
67 | void setDataModel(QScxmlDataModel *model); |
68 | QScxmlDataModel *dataModel() const; |
69 | QBindable<QScxmlDataModel*> bindableDataModel(); |
70 | |
71 | void setLoader(QScxmlCompiler::Loader *loader); |
72 | QScxmlCompiler::Loader *loader() const; |
73 | QBindable<QScxmlCompiler::Loader*> bindableLoader(); |
74 | |
75 | bool isRunning() const; |
76 | void setRunning(bool running); |
77 | |
78 | QVariantMap initialValues(); |
79 | void setInitialValues(const QVariantMap &initialValues); |
80 | QBindable<QVariantMap> bindableInitialValues(); |
81 | |
82 | QString name() const; |
83 | Q_INVOKABLE QStringList stateNames(bool compress = true) const; |
84 | Q_INVOKABLE QStringList activeStateNames(bool compress = true) const; |
85 | Q_INVOKABLE bool isActive(const QString &scxmlStateName) const; |
86 | |
87 | QMetaObject::Connection connectToState(const QString &scxmlStateName, |
88 | const QObject *receiver, const char *method, |
89 | Qt::ConnectionType type = Qt::AutoConnection); |
90 | |
91 | // connect state with context |
92 | template <typename Functor> |
93 | #ifdef Q_QDOC |
94 | QMetaObject::Connection |
95 | #else |
96 | inline std::enable_if_t<!std::is_same_v<const char*, Functor>, QMetaObject::Connection> |
97 | #endif |
98 | connectToState( |
99 | const QString &scxmlStateName, |
100 | #ifdef Q_QDOC |
101 | const QObject *context, |
102 | #else |
103 | const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *context, |
104 | #endif |
105 | Functor &&functor, |
106 | Qt::ConnectionType type = Qt::AutoConnection) |
107 | { |
108 | using Prototype = void(*)(bool); |
109 | return connectToStateImpl( |
110 | scxmlStateName, receiver: context, slot: nullptr, |
111 | slotObj: QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(functor)), |
112 | type); |
113 | } |
114 | |
115 | // connect state without context |
116 | template <typename Functor> |
117 | #ifdef Q_QDOC |
118 | QMetaObject::Connection |
119 | #else |
120 | inline std::enable_if_t<!std::is_same_v<const char*, Functor>, QMetaObject::Connection> |
121 | #endif |
122 | connectToState( |
123 | const QString &scxmlStateName, |
124 | Functor &&functor, |
125 | Qt::ConnectionType type = Qt::AutoConnection) |
126 | { |
127 | return connectToState(scxmlStateName, this, std::forward<Functor>(functor), type); |
128 | } |
129 | |
130 | //! [onentry] |
131 | static std::function<void(bool)> onEntry(const QObject *receiver, const char *method) |
132 | { |
133 | const QPointer<QObject> receiverPointer(const_cast<QObject *>(receiver)); |
134 | return [receiverPointer, method](bool isEnteringState) { |
135 | if (isEnteringState && !receiverPointer.isNull()) |
136 | QMetaObject::invokeMethod(obj: const_cast<QObject *>(receiverPointer.data()), member: method); |
137 | }; |
138 | } |
139 | |
140 | //! [onexit] |
141 | static std::function<void(bool)> onExit(const QObject *receiver, const char *method) |
142 | { |
143 | const QPointer<QObject> receiverPointer(const_cast<QObject *>(receiver)); |
144 | return [receiverPointer, method](bool isEnteringState) { |
145 | if (!isEnteringState && !receiverPointer.isNull()) |
146 | QMetaObject::invokeMethod(obj: receiverPointer.data(), member: method); |
147 | }; |
148 | } |
149 | |
150 | //! [onentry-functor] |
151 | template<typename Functor> |
152 | static std::function<void(bool)> onEntry(Functor functor) |
153 | { |
154 | return [functor](bool isEnteringState) { |
155 | if (isEnteringState) |
156 | functor(); |
157 | }; |
158 | } |
159 | |
160 | //! [onexit-functor] |
161 | template<typename Functor> |
162 | static std::function<void(bool)> onExit(Functor functor) |
163 | { |
164 | return [functor](bool isEnteringState) { |
165 | if (!isEnteringState) |
166 | functor(); |
167 | }; |
168 | } |
169 | |
170 | //! [onentry-template] |
171 | template<typename PointerToMemberFunction> |
172 | static std::function<void(bool)> onEntry( |
173 | const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver, |
174 | PointerToMemberFunction method) |
175 | { |
176 | typedef typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object Object; |
177 | const QPointer<Object> receiverPointer(const_cast<Object *>(receiver)); |
178 | return [receiverPointer, method](bool isEnteringState) { |
179 | if (isEnteringState && !receiverPointer.isNull()) |
180 | (receiverPointer->*method)(); |
181 | }; |
182 | } |
183 | |
184 | //! [onexit-template] |
185 | template<typename PointerToMemberFunction> |
186 | static std::function<void(bool)> onExit( |
187 | const typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object *receiver, |
188 | PointerToMemberFunction method) |
189 | { |
190 | typedef typename QtPrivate::FunctionPointer<PointerToMemberFunction>::Object Object; |
191 | const QPointer<Object> receiverPointer(const_cast<Object *>(receiver)); |
192 | return [receiverPointer, method](bool isEnteringState) { |
193 | if (!isEnteringState && !receiverPointer.isNull()) |
194 | (receiverPointer->*method)(); |
195 | }; |
196 | } |
197 | |
198 | QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec, |
199 | const QObject *receiver, const char *method, |
200 | Qt::ConnectionType type = Qt::AutoConnection); |
201 | |
202 | // connect event with context |
203 | template <typename Functor> |
204 | #ifdef Q_QDOC |
205 | QMetaObject::Connection |
206 | #else |
207 | inline std::enable_if_t<!std::is_same_v<const char*, Functor>, QMetaObject::Connection> |
208 | #endif |
209 | connectToEvent( |
210 | const QString &scxmlEventSpec, |
211 | #ifdef Q_QDOC |
212 | const QObject *context, |
213 | #else |
214 | const typename QtPrivate::ContextTypeForFunctor<Functor>::ContextType *context, |
215 | #endif |
216 | Functor &&functor, |
217 | Qt::ConnectionType type = Qt::AutoConnection) |
218 | { |
219 | using Prototype = void(*)(QScxmlEvent); |
220 | return connectToEventImpl( |
221 | scxmlEventSpec, receiver: context, slot: nullptr, |
222 | slotObj: QtPrivate::makeCallableObject<Prototype>(std::forward<Functor>(functor)), |
223 | type); |
224 | } |
225 | |
226 | // connect event without context |
227 | template <typename Functor> |
228 | #ifdef Q_QDOC |
229 | QMetaObject::Connection |
230 | #else |
231 | inline std::enable_if_t<!std::is_same_v<const char*, Functor>, QMetaObject::Connection> |
232 | #endif |
233 | connectToEvent(const QString &scxmlEventSpec, Functor &&functor, |
234 | Qt::ConnectionType type = Qt::AutoConnection) |
235 | { |
236 | // Use this as context |
237 | return connectToEvent(scxmlEventSpec, this, std::forward<Functor>(functor), type); |
238 | } |
239 | |
240 | Q_INVOKABLE void submitEvent(QScxmlEvent *event); |
241 | Q_INVOKABLE void submitEvent(const QString &eventName); |
242 | Q_INVOKABLE void submitEvent(const QString &eventName, const QVariant &data); |
243 | Q_INVOKABLE void cancelDelayedEvent(const QString &sendId); |
244 | |
245 | Q_INVOKABLE bool isDispatchableTarget(const QString &target) const; |
246 | |
247 | QList<QScxmlInvokableService *> invokedServices() const; |
248 | QBindable<QList<QScxmlInvokableService*>> bindableInvokedServices(); |
249 | |
250 | QScxmlTableData *tableData() const; |
251 | void setTableData(QScxmlTableData *tableData); |
252 | QBindable<QScxmlTableData*> bindableTableData(); |
253 | |
254 | Q_SIGNALS: |
255 | void runningChanged(bool running); |
256 | void invokedServicesChanged(const QList<QScxmlInvokableService *> &invokedServices); |
257 | void log(const QString &label, const QString &msg); |
258 | void reachedStableState(); |
259 | void finished(); |
260 | void dataModelChanged(QScxmlDataModel *model); |
261 | void initialValuesChanged(const QVariantMap &initialValues); |
262 | void initializedChanged(bool initialized); |
263 | void loaderChanged(QScxmlCompiler::Loader *loader); |
264 | void tableDataChanged(QScxmlTableData *tableData); |
265 | |
266 | public Q_SLOTS: |
267 | void start(); |
268 | void stop(); |
269 | bool init(); |
270 | |
271 | protected: // methods for friends: |
272 | friend class QScxmlDataModel; |
273 | friend class QScxmlEventBuilder; |
274 | friend class QScxmlInvokableServicePrivate; |
275 | friend class QScxmlExecutionEngine; |
276 | |
277 | // The methods below are used by the compiled state machines. |
278 | bool isActive(int stateIndex) const; |
279 | |
280 | private: |
281 | QMetaObject::Connection connectToStateImpl(const QString &scxmlStateName, |
282 | const QObject *receiver, void **slot, |
283 | QtPrivate::QSlotObjectBase *slotObj, |
284 | Qt::ConnectionType type = Qt::AutoConnection); |
285 | QMetaObject::Connection connectToEventImpl(const QString &scxmlEventSpec, |
286 | const QObject *receiver, void **slot, |
287 | QtPrivate::QSlotObjectBase *slotObj, |
288 | Qt::ConnectionType type = Qt::AutoConnection); |
289 | }; |
290 | |
291 | QT_END_NAMESPACE |
292 | |
293 | Q_DECLARE_METATYPE(QScxmlStateMachine *) |
294 | Q_DECLARE_METATYPE(QList<QScxmlInvokableService *>) |
295 | |
296 | #endif // QSCXMLSTATEMACHINE_H |
297 | |