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
21Q_MOC_INCLUDE(qscxmltabledata.h)
22
23QT_BEGIN_NAMESPACE
24class QIODevice;
25class QXmlStreamWriter;
26class QTextStream;
27class QScxmlTableData;
28
29class QScxmlStateMachinePrivate;
30class 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
52protected:
53 explicit QScxmlStateMachine(const QMetaObject *metaObject, QObject *parent = nullptr);
54 QScxmlStateMachine(QScxmlStateMachinePrivate &dd, QObject *parent = nullptr);
55
56public:
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
254Q_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
266public Q_SLOTS:
267 void start();
268 void stop();
269 bool init();
270
271protected: // 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
280private:
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
291QT_END_NAMESPACE
292
293Q_DECLARE_METATYPE(QScxmlStateMachine *)
294Q_DECLARE_METATYPE(QList<QScxmlInvokableService *>)
295
296#endif // QSCXMLSTATEMACHINE_H
297

source code of qtscxml/src/scxml/qscxmlstatemachine.h