summaryrefslogtreecommitdiffstats
path: root/src/imports/scxmlstatemachine
diff options
context:
space:
mode:
Diffstat (limited to 'src/imports/scxmlstatemachine')
-rw-r--r--src/imports/scxmlstatemachine/plugin.cpp45
-rw-r--r--src/imports/scxmlstatemachine/qmldir4
-rw-r--r--src/imports/scxmlstatemachine/scxmlstatemachine.pro19
-rw-r--r--src/imports/scxmlstatemachine/signalevent.cpp82
-rw-r--r--src/imports/scxmlstatemachine/signalevent.h62
-rw-r--r--src/imports/scxmlstatemachine/state.cpp94
-rw-r--r--src/imports/scxmlstatemachine/state.h74
-rw-r--r--src/imports/scxmlstatemachine/statemachine.cpp163
-rw-r--r--src/imports/scxmlstatemachine/statemachine.h71
9 files changed, 614 insertions, 0 deletions
diff --git a/src/imports/scxmlstatemachine/plugin.cpp b/src/imports/scxmlstatemachine/plugin.cpp
new file mode 100644
index 0000000..9b2f4d4
--- /dev/null
+++ b/src/imports/scxmlstatemachine/plugin.cpp
@@ -0,0 +1,45 @@
+/****************************************************************************
+ **
+ ** Copyright (c) 2015 Digia Plc
+ ** For any questions to Digia, please use contact form at http://qt.digia.com/
+ **
+ ** All Rights Reserved.
+ **
+ ** NOTICE: All information contained herein is, and remains
+ ** the property of Digia Plc and its suppliers,
+ ** if any. The intellectual and technical concepts contained
+ ** herein are proprietary to Digia Plc
+ ** and its suppliers and may be covered by Finnish and Foreign Patents,
+ ** patents in process, and are protected by trade secret or copyright law.
+ ** Dissemination of this information or reproduction of this material
+ ** is strictly forbidden unless prior written permission is obtained
+ ** from Digia Plc.
+ ****************************************************************************/
+
+#include "statemachine.h"
+#include "state.h"
+#include "signalevent.h"
+
+#include <QQmlExtensionPlugin>
+#include <qqml.h>
+
+QT_BEGIN_NAMESPACE
+
+class ScxmlStateMachinePlugin : public QQmlExtensionPlugin
+{
+ Q_OBJECT
+ Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Scxml/1.0")
+
+public:
+ void registerTypes(const char *uri)
+ {
+ qmlRegisterType<StateMachine>(uri, 1, 0, "StateMachine");
+ qmlRegisterType<State>(uri, 1, 0, "State");
+ qmlRegisterType<SignalEvent>(uri, 1, 0, "SignalEvent");
+ qmlProtectModule(uri, 1);
+ }
+};
+
+QT_END_NAMESPACE
+
+#include "plugin.moc"
diff --git a/src/imports/scxmlstatemachine/qmldir b/src/imports/scxmlstatemachine/qmldir
new file mode 100644
index 0000000..a411f70
--- /dev/null
+++ b/src/imports/scxmlstatemachine/qmldir
@@ -0,0 +1,4 @@
+module Scxml
+plugin scxmlstatemachine
+classname ScxmlStateMachinePlugin
+
diff --git a/src/imports/scxmlstatemachine/scxmlstatemachine.pro b/src/imports/scxmlstatemachine/scxmlstatemachine.pro
new file mode 100644
index 0000000..4cbf853
--- /dev/null
+++ b/src/imports/scxmlstatemachine/scxmlstatemachine.pro
@@ -0,0 +1,19 @@
+CXX_MODULE = qml
+TARGET = scxmlstatemachine
+TARGETPATH = Scxml
+IMPORT_VERSION = 1.0
+
+QT = qscxml qml-private core-private
+
+SOURCES = \
+ $$PWD/plugin.cpp \
+ $$PWD/signalevent.cpp \
+ $$PWD/state.cpp \
+ $$PWD/statemachine.cpp
+
+HEADERS = \
+ $$PWD/signalevent.h \
+ $$PWD/state.h \
+ $$PWD/statemachine.h
+
+load(qml_plugin)
diff --git a/src/imports/scxmlstatemachine/signalevent.cpp b/src/imports/scxmlstatemachine/signalevent.cpp
new file mode 100644
index 0000000..a9c3739
--- /dev/null
+++ b/src/imports/scxmlstatemachine/signalevent.cpp
@@ -0,0 +1,82 @@
+/****************************************************************************
+ **
+ ** Copyright (c) 2015 Digia Plc
+ ** For any questions to Digia, please use contact form at http://qt.digia.com/
+ **
+ ** All Rights Reserved.
+ **
+ ** NOTICE: All information contained herein is, and remains
+ ** the property of Digia Plc and its suppliers,
+ ** if any. The intellectual and technical concepts contained
+ ** herein are proprietary to Digia Plc
+ ** and its suppliers and may be covered by Finnish and Foreign Patents,
+ ** patents in process, and are protected by trade secret or copyright law.
+ ** Dissemination of this information or reproduction of this material
+ ** is strictly forbidden unless prior written permission is obtained
+ ** from Digia Plc.
+ ****************************************************************************/
+
+#include "signalevent.h"
+#include "statemachine.h"
+
+#include <QQmlContext>
+#include <QQmlInfo>
+#include <QQmlEngine>
+
+#include <private/qjsvalue_p.h>
+#include <private/qv8engine_p.h>
+
+SignalEvent::SignalEvent(StateMachine *parent)
+ : QObject(parent)
+{}
+
+QString SignalEvent::eventName() const
+{
+ return QString::fromUtf8(m_eventName);
+}
+
+void SignalEvent::setEventName(const QString &eventName)
+{
+ QByteArray name = eventName.toUtf8();
+ if (name != m_eventName) {
+ m_eventName = name;
+ emit eventNameChanged();
+ }
+}
+
+const QJSValue &SignalEvent::signal()
+{
+ return m_signal;
+}
+
+void SignalEvent::setSignal(const QJSValue &signal)
+{
+ if (m_signal.strictlyEquals(signal))
+ return;
+
+ disconnect(m_signalConnection);
+
+ m_signal = signal;
+
+ QV4::ExecutionEngine *jsEngine = QV8Engine::getV4(QQmlEngine::contextForObject(this)->engine());
+ QV4::Scope scope(jsEngine);
+
+ QV4::Scoped<QV4::QObjectMethod> qobjectSignal(scope, QJSValuePrivate::convertedToValue(jsEngine, m_signal));
+ Q_ASSERT(qobjectSignal);
+
+ QObject *sender = qobjectSignal->object();
+ QByteArray signature = sender->metaObject()->method(qobjectSignal->methodIndex()).methodSignature();
+ signature.prepend(SIGNAL());
+ m_signalConnection = connect(sender, signature.constData(), this, SLOT(invoke()));
+
+ emit signalChanged();
+}
+
+void SignalEvent::invoke()
+{
+ if (Scxml::StateTable *table = qobject_cast<StateMachine *>(parent())->stateMachine()) {
+ QMetaObject::invokeMethod(table, "submitEvent", Q_ARG(QByteArray, m_eventName));
+ } else {
+ qmlInfo(this) << QStringLiteral("No state table found to submit event '%1'.").arg(QString::fromUtf8(m_eventName));
+ }
+}
diff --git a/src/imports/scxmlstatemachine/signalevent.h b/src/imports/scxmlstatemachine/signalevent.h
new file mode 100644
index 0000000..473fc35
--- /dev/null
+++ b/src/imports/scxmlstatemachine/signalevent.h
@@ -0,0 +1,62 @@
+/****************************************************************************
+ **
+ ** Copyright (c) 2015 Digia Plc
+ ** For any questions to Digia, please use contact form at http://qt.digia.com/
+ **
+ ** All Rights Reserved.
+ **
+ ** NOTICE: All information contained herein is, and remains
+ ** the property of Digia Plc and its suppliers,
+ ** if any. The intellectual and technical concepts contained
+ ** herein are proprietary to Digia Plc
+ ** and its suppliers and may be covered by Finnish and Foreign Patents,
+ ** patents in process, and are protected by trade secret or copyright law.
+ ** Dissemination of this information or reproduction of this material
+ ** is strictly forbidden unless prior written permission is obtained
+ ** from Digia Plc.
+ ****************************************************************************/
+
+#ifndef SIGNALEVENT_H
+#define SIGNALEVENT_H
+
+#include <QJSValue>
+#include <QObject>
+
+namespace Scxml {
+class ScxmlState;
+}
+
+QT_BEGIN_NAMESPACE
+
+class StateMachine;
+class SignalEvent: public QObject
+{
+ Q_OBJECT
+ Q_PROPERTY(QJSValue signal READ signal WRITE setSignal NOTIFY signalChanged)
+ Q_PROPERTY(QString eventName READ eventName WRITE setEventName NOTIFY eventNameChanged)
+
+public:
+ explicit SignalEvent(StateMachine *parent = 0);
+
+ QString eventName() const;
+ void setEventName(const QString &eventName);
+
+ const QJSValue &signal();
+ void setSignal(const QJSValue &signal);
+
+Q_SIGNALS:
+ void signalChanged();
+ void eventNameChanged();
+
+public Q_SLOTS:
+ void invoke();
+
+private:
+ QJSValue m_signal;
+ QByteArray m_eventName;
+ QMetaObject::Connection m_signalConnection;
+};
+
+QT_END_NAMESPACE
+
+#endif // SIGNALEVENT_H
diff --git a/src/imports/scxmlstatemachine/state.cpp b/src/imports/scxmlstatemachine/state.cpp
new file mode 100644
index 0000000..da240b4
--- /dev/null
+++ b/src/imports/scxmlstatemachine/state.cpp
@@ -0,0 +1,94 @@
+/****************************************************************************
+ **
+ ** Copyright (c) 2015 Digia Plc
+ ** For any questions to Digia, please use contact form at http://qt.digia.com/
+ **
+ ** All Rights Reserved.
+ **
+ ** NOTICE: All information contained herein is, and remains
+ ** the property of Digia Plc and its suppliers,
+ ** if any. The intellectual and technical concepts contained
+ ** herein are proprietary to Digia Plc
+ ** and its suppliers and may be covered by Finnish and Foreign Patents,
+ ** patents in process, and are protected by trade secret or copyright law.
+ ** Dissemination of this information or reproduction of this material
+ ** is strictly forbidden unless prior written permission is obtained
+ ** from Digia Plc.
+ ****************************************************************************/
+
+#include "state.h"
+#include "statemachine.h"
+
+#include <QScxml/scxmlstatetable.h>
+#include <QQmlInfo>
+
+State::State(StateMachine *parent)
+ : QObject(parent)
+ , completed(false)
+ , active(false)
+{}
+
+void State::componentComplete()
+{
+ completed = true;
+ establishConnections();
+
+ if (Scxml::StateTable *table = qobject_cast<StateMachine *>(parent())->stateMachine())
+ active = table->currentStates().contains(m_scxmlName);
+ if (active)
+ emit activeChanged(active);
+}
+
+bool State::isActive() const
+{
+ return active;
+}
+
+QString State::scxmlName() const
+{
+ return m_scxmlName;
+}
+
+void State::setScxmlName(const QString &scxmlName)
+{
+ if (m_scxmlName != scxmlName) {
+ m_scxmlName = scxmlName;
+ breakConnections();
+ establishConnections();
+ emit scxmlNameChanged();
+ }
+}
+
+void State::setActive(bool active)
+{
+ this->active = active;
+ emit activeChanged(active);
+}
+
+void State::breakConnections()
+{
+ disconnect(activeConnection);
+ disconnect(didEnterConnection);
+ disconnect(willExitConnection);
+}
+
+void State::establishConnections()
+{
+ if (!completed)
+ return;
+
+ Scxml::StateTable *table = qobject_cast<StateMachine *>(parent())->stateMachine();
+ if (table == nullptr) {
+ qmlInfo(this) << QStringLiteral("State is not part of a StateTable.");
+ return;
+ }
+
+ if (!table->hasState(m_scxmlName)) {
+ qmlInfo(this) << QStringLiteral("No state '%1' found.").arg(m_scxmlName);
+ return;
+ }
+
+ activeConnection = table->connect(m_scxmlName, SIGNAL(activeChanged(bool)), this, SLOT(setActive(bool)));
+ didEnterConnection = table->connect(m_scxmlName, SIGNAL(didEnter()), this, SIGNAL(didEnter()));
+ willExitConnection = table->connect(m_scxmlName, SIGNAL(willExit()), this, SIGNAL(willExit()));
+}
diff --git a/src/imports/scxmlstatemachine/state.h b/src/imports/scxmlstatemachine/state.h
new file mode 100644
index 0000000..53c3e37
--- /dev/null
+++ b/src/imports/scxmlstatemachine/state.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+ **
+ ** Copyright (c) 2015 Digia Plc
+ ** For any questions to Digia, please use contact form at http://qt.digia.com/
+ **
+ ** All Rights Reserved.
+ **
+ ** NOTICE: All information contained herein is, and remains
+ ** the property of Digia Plc and its suppliers,
+ ** if any. The intellectual and technical concepts contained
+ ** herein are proprietary to Digia Plc
+ ** and its suppliers and may be covered by Finnish and Foreign Patents,
+ ** patents in process, and are protected by trade secret or copyright law.
+ ** Dissemination of this information or reproduction of this material
+ ** is strictly forbidden unless prior written permission is obtained
+ ** from Digia Plc.
+ ****************************************************************************/
+
+#ifndef STATE_H
+#define STATE_H
+
+#include <QtQml/QQmlParserStatus>
+#include <QtQml/QQmlListProperty>
+
+namespace Scxml {
+class ScxmlState;
+}
+
+QT_BEGIN_NAMESPACE
+
+class StateMachine;
+class State: public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+ Q_PROPERTY(bool active READ isActive NOTIFY activeChanged)
+ Q_PROPERTY(QString scxmlName READ scxmlName WRITE setScxmlName NOTIFY scxmlNameChanged)
+
+public:
+ explicit State(StateMachine *parent = 0);
+
+ void classBegin() {}
+ void componentComplete();
+
+ bool isActive() const;
+
+ QString scxmlName() const;
+ void setScxmlName(const QString &scxmlName);
+
+Q_SIGNALS:
+ void activeChanged(bool active);
+ void scxmlNameChanged();
+ void didEnter();
+ void willExit();
+
+private Q_SLOTS:
+ void setActive(bool active);
+
+private:
+ void breakConnections();
+ void establishConnections();
+
+private:
+ QString m_scxmlName;
+ QMetaObject::Connection activeConnection;
+ QMetaObject::Connection didEnterConnection;
+ QMetaObject::Connection willExitConnection;
+ bool completed;
+ bool active;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/imports/scxmlstatemachine/statemachine.cpp b/src/imports/scxmlstatemachine/statemachine.cpp
new file mode 100644
index 0000000..c953018
--- /dev/null
+++ b/src/imports/scxmlstatemachine/statemachine.cpp
@@ -0,0 +1,163 @@
+/****************************************************************************
+ **
+ ** Copyright (c) 2015 Digia Plc
+ ** For any questions to Digia, please use contact form at http://qt.digia.com/
+ **
+ ** All Rights Reserved.
+ **
+ ** NOTICE: All information contained herein is, and remains
+ ** the property of Digia Plc and its suppliers,
+ ** if any. The intellectual and technical concepts contained
+ ** herein are proprietary to Digia Plc
+ ** and its suppliers and may be covered by Finnish and Foreign Patents,
+ ** patents in process, and are protected by trade secret or copyright law.
+ ** Dissemination of this information or reproduction of this material
+ ** is strictly forbidden unless prior written permission is obtained
+ ** from Digia Plc.
+ ****************************************************************************/
+
+#include "signalevent.h"
+#include "state.h"
+#include "statemachine.h"
+
+#include <QScxml/ecmascriptdatamodel.h>
+#include <QScxml/scxmlstatetable.h>
+#include <QScxml/scxmlparser.h>
+#include <QQmlContext>
+#include <QQmlEngine>
+#include <QQmlInfo>
+#include <QQmlFile>
+#include <QBuffer>
+
+static void append(QQmlListProperty<QObject> *prop, QObject *o)
+{
+ if (!o)
+ return;
+
+ if (State *state = qobject_cast<State *>(o)) {
+ state->setParent(prop->object);
+ static_cast<StateMachine::Kids *>(prop->data)->append(state);
+ emit static_cast<StateMachine *>(prop->object)->statesChanged();
+ } else if (SignalEvent *event = qobject_cast<SignalEvent *>(o)) {
+ event->setParent(prop->object);
+ static_cast<StateMachine::Kids *>(prop->data)->append(event);
+ emit static_cast<StateMachine *>(prop->object)->statesChanged();
+ }
+}
+
+static int count(QQmlListProperty<QObject> *prop)
+{
+ return static_cast<StateMachine::Kids *>(prop->data)->count();
+}
+
+static QObject *at(QQmlListProperty<QObject> *prop, int index)
+{
+ return static_cast<StateMachine::Kids *>(prop->data)->at(index);
+}
+
+static void clear(QQmlListProperty<QObject> *prop)
+{
+ static_cast<StateMachine::Kids *>(prop->data)->clear();
+ emit static_cast<StateMachine *>(prop->object)->statesChanged();
+}
+
+StateMachine::StateMachine(QObject *parent)
+ : QObject(parent)
+{
+}
+
+void StateMachine::componentComplete()
+{
+ if (m_table == nullptr) {
+ qmlInfo(this) << "No state machine loaded.";
+ return;
+ }
+
+ bool ok = false;
+ bool moreOk = QMetaObject::invokeMethod(m_table, "init", Qt::DirectConnection, Q_RETURN_ARG(bool, ok));
+ if (ok && moreOk) {
+ QMetaObject::invokeMethod(m_table, "start");
+ } else {
+ qmlInfo(this) << "Failed to initialize the state machine.";
+ }
+}
+
+QQmlListProperty<QObject> StateMachine::states()
+{
+ return QQmlListProperty<QObject>(this, &m_children, append, count, at, clear);
+}
+
+Scxml::StateTable *StateMachine::stateMachine() const
+{
+ return m_table;
+}
+
+void StateMachine::setStateMachine(Scxml::StateTable *table)
+{
+ qDebug()<<"setting state machine to"<<table;
+ if (m_table == nullptr && table != nullptr) {
+ m_table = table;
+ } else if (m_table) {
+ qmlInfo(this) << "Can set the table only once";
+ }
+}
+
+QUrl StateMachine::filename()
+{
+ return m_filename;
+}
+
+void StateMachine::setFilename(const QUrl &filename)
+{
+ QUrl oldFilename = m_filename;
+ if (m_table) {
+ delete m_table;
+ m_table = nullptr;
+ }
+
+ if (parse(filename)) {
+ m_filename = filename;
+ emit filenameChanged();
+ } else {
+ m_filename.clear();
+ if (!oldFilename.isEmpty()) {
+ emit filenameChanged();
+ }
+ }
+}
+
+bool StateMachine::parse(const QUrl &filename)
+{
+ if (!QQmlFile::isSynchronous(filename)) {
+ qmlInfo(this) << QStringLiteral("ERROR: cannot open '%1' for reading: only synchronous file access is supported.").arg(filename.fileName());
+ return false;
+ }
+ QQmlFile scxmlFile(QQmlEngine::contextForObject(this)->engine(), filename);
+ if (scxmlFile.isError()) {
+ // the synchronous case can only fail when the file is not found (or not readable).
+ qmlInfo(this) << QStringLiteral("ERROR: cannot open '%1' for reading.").arg(filename.fileName());
+ return false;
+ }
+
+ QByteArray data(scxmlFile.dataByteArray());
+ QBuffer buf(&data);
+ Q_ASSERT(buf.open(QIODevice::ReadOnly));
+ QXmlStreamReader xmlReader(&buf);
+ Scxml::ScxmlParser parser(&xmlReader);
+ parser.parse();
+ setStateMachine(parser.table());
+
+ if (parser.state() != Scxml::ScxmlParser::FinishedParsing || m_table == nullptr) {
+ qmlInfo(this) << QStringLiteral("Something went wrong while parsing '%1':").arg(filename.fileName()) << endl;
+ foreach (const Scxml::ErrorMessage &msg, parser.errors()) {
+ qmlInfo(this) << msg.fileName << QStringLiteral(":") << msg.line
+ << QStringLiteral(":") << msg.column
+ << QStringLiteral(": ") << msg.severityString()
+ << QStringLiteral(": ") << msg.msg;
+ }
+
+ return false;
+ }
+
+ return true;
+}
diff --git a/src/imports/scxmlstatemachine/statemachine.h b/src/imports/scxmlstatemachine/statemachine.h
new file mode 100644
index 0000000..554e25d
--- /dev/null
+++ b/src/imports/scxmlstatemachine/statemachine.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+ **
+ ** Copyright (c) 2015 Digia Plc
+ ** For any questions to Digia, please use contact form at http://qt.digia.com/
+ **
+ ** All Rights Reserved.
+ **
+ ** NOTICE: All information contained herein is, and remains
+ ** the property of Digia Plc and its suppliers,
+ ** if any. The intellectual and technical concepts contained
+ ** herein are proprietary to Digia Plc
+ ** and its suppliers and may be covered by Finnish and Foreign Patents,
+ ** patents in process, and are protected by trade secret or copyright law.
+ ** Dissemination of this information or reproduction of this material
+ ** is strictly forbidden unless prior written permission is obtained
+ ** from Digia Plc.
+ ****************************************************************************/
+
+#ifndef STATEMACHINE_H
+#define STATEMACHINE_H
+
+#include <QUrl>
+#include <QVector>
+#include <QQmlParserStatus>
+#include <QQmlListProperty>
+#include <QScxml/scxmlstatetable.h>
+
+QT_BEGIN_NAMESPACE
+
+class State;
+class QQmlOpenMetaObject;
+class StateMachine: public QObject, public QQmlParserStatus
+{
+ Q_OBJECT
+ Q_INTERFACES(QQmlParserStatus)
+ Q_PROPERTY(QQmlListProperty<QObject> states READ states NOTIFY statesChanged DESIGNABLE false)
+ Q_PROPERTY(QUrl filename READ filename WRITE setFilename NOTIFY filenameChanged)
+ Q_PROPERTY(Scxml::StateTable* stateMachine READ stateMachine WRITE setStateMachine)
+
+ Q_CLASSINFO("DefaultProperty", "states")
+
+public:
+ typedef QVector<QObject *> Kids;
+ explicit StateMachine(QObject *parent = 0);
+
+ void classBegin() {}
+ void componentComplete();
+ QQmlListProperty<QObject> states();
+
+ Scxml::StateTable *stateMachine() const;
+ void setStateMachine(Scxml::StateTable *stateMachine);
+
+ QUrl filename();
+ void setFilename(const QUrl &filename);
+
+Q_SIGNALS:
+ void statesChanged();
+ void filenameChanged();
+
+private:
+ bool parse(const QUrl &filename);
+
+private:
+ QUrl m_filename;
+ Kids m_children;
+ Scxml::StateTable *m_table = nullptr;
+};
+
+QT_END_NAMESPACE
+
+#endif