summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--examples/scxml/calculator-qml/calculator-qml.qml7
-rw-r--r--examples/scxml/calculator-widgets/mainwindow.cpp8
-rw-r--r--examples/scxml/pinball/mainwindow.cpp9
-rw-r--r--src/imports/scxmlstatemachine/eventconnection.cpp104
-rw-r--r--src/imports/scxmlstatemachine/eventconnection.h85
-rw-r--r--src/imports/scxmlstatemachine/plugin.cpp2
-rw-r--r--src/imports/scxmlstatemachine/scxmlstatemachine.pro6
-rw-r--r--src/scxml/qscxmlparser.cpp1
-rw-r--r--src/scxml/qscxmlstatemachine.cpp129
-rw-r--r--src/scxml/qscxmlstatemachine.h65
-rw-r--r--src/scxml/qscxmlstatemachine_p.h24
-rw-r--r--tests/auto/statemachine/statemachine.pro2
-rw-r--r--tests/auto/statemachine/tst_statemachine.cpp78
13 files changed, 457 insertions, 63 deletions
diff --git a/examples/scxml/calculator-qml/calculator-qml.qml b/examples/scxml/calculator-qml/calculator-qml.qml
index 846589f..9d679f6 100644
--- a/examples/scxml/calculator-qml/calculator-qml.qml
+++ b/examples/scxml/calculator-qml/calculator-qml.qml
@@ -51,6 +51,7 @@
import CalculatorStateMachine 1.0
import QtQuick 2.5
import QtQuick.Window 2.0
+import QtScxml 5.7
Window {
id: window
@@ -61,9 +62,9 @@ Window {
CalculatorStateMachine {
id: statemachine
running: true
- onEventOccurred: {
- if (event.name === "updateDisplay")
- resultText.text = event.data.display;
+ property var connection: EventConnection {
+ events: ["updateDisplay"]
+ onOccurred: resultText.text = event.data.display
}
}
diff --git a/examples/scxml/calculator-widgets/mainwindow.cpp b/examples/scxml/calculator-widgets/mainwindow.cpp
index eacafaa..d8c77f7 100644
--- a/examples/scxml/calculator-widgets/mainwindow.cpp
+++ b/examples/scxml/calculator-widgets/mainwindow.cpp
@@ -115,11 +115,9 @@ MainWindow::MainWindow(QScxmlStateMachine *machine, QWidget *parent) :
m_machine->submitEvent("C");
});
- connect(m_machine, &QScxmlStateMachine::eventOccurred, [this](const QScxmlEvent &event) {
- if (event.name() == QLatin1String("updateDisplay")) {
- const QString display = event.data().toMap().value("display").toString();
- ui->display->setText(display);
- }
+ m_machine->connectToEvent(QLatin1String("updateDisplay"), this, [this](const QScxmlEvent &event) {
+ const QString display = event.data().toMap().value("display").toString();
+ ui->display->setText(display);
});
}
diff --git a/examples/scxml/pinball/mainwindow.cpp b/examples/scxml/pinball/mainwindow.cpp
index 9e525c2..1e7b13c 100644
--- a/examples/scxml/pinball/mainwindow.cpp
+++ b/examples/scxml/pinball/mainwindow.cpp
@@ -89,8 +89,7 @@ MainWindow::MainWindow(Pinball *machine, QWidget *parent) :
initAndConnect(QLatin1String("onState"), m_ui->ballOutButton);
// datamodel update
- connect(m_machine, SIGNAL(eventOccurred(const QScxmlEvent &)),
- this, SLOT(eventOccurred(const QScxmlEvent &)));
+ m_machine->connectToEvent("updateScore", this, &MainWindow::eventOccurred);
// gui interaction
connect(m_ui->cButton, &QAbstractButton::clicked,
@@ -129,12 +128,6 @@ void MainWindow::initAndConnect(const QString &state, QWidget *widget)
void MainWindow::eventOccurred(const QScxmlEvent &event)
{
- if (event.originType() != QLatin1String("qt:signal"))
- return;
-
- if (event.name() != QLatin1String("updateScore"))
- return;
-
const QVariant data = event.data();
const QString highScore = data.toMap().value("highScore").toString();
m_ui->highScoreLabel->setText(highScore);
diff --git a/src/imports/scxmlstatemachine/eventconnection.cpp b/src/imports/scxmlstatemachine/eventconnection.cpp
new file mode 100644
index 0000000..3c0cc94
--- /dev/null
+++ b/src/imports/scxmlstatemachine/eventconnection.cpp
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** 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.h"
+
+QT_BEGIN_NAMESPACE
+
+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()
+{
+ foreach (const QMetaObject::Connection &connection, m_connections)
+ disconnect(connection);
+ m_connections.clear();
+ if (m_stateMachine) {
+ foreach (const QString &event, 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.h b/src/imports/scxmlstatemachine/eventconnection.h
new file mode 100644
index 0000000..3492634
--- /dev/null
+++ b/src/imports/scxmlstatemachine/eventconnection.h
@@ -0,0 +1,85 @@
+/****************************************************************************
+**
+** 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_H
+#define EVENTCONNECTION_H
+
+#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_H
diff --git a/src/imports/scxmlstatemachine/plugin.cpp b/src/imports/scxmlstatemachine/plugin.cpp
index 1837935..df833f0 100644
--- a/src/imports/scxmlstatemachine/plugin.cpp
+++ b/src/imports/scxmlstatemachine/plugin.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include "statemachineloader.h"
+#include "eventconnection.h"
#include "qscxmlevent.h"
#include <QQmlExtensionPlugin>
@@ -66,6 +67,7 @@ public:
static const int qScxmlEventMetaTypeId = qMetaTypeId<QScxmlEvent>();
Q_UNUSED(qScxmlEventMetaTypeId)
qmlRegisterType<QScxmlStateMachineLoader>(uri, major, minor, "StateMachineLoader");
+ qmlRegisterType<QScxmlEventConnection>(uri, major, minor, "EventConnection");
qmlProtectModule(uri, 1);
}
};
diff --git a/src/imports/scxmlstatemachine/scxmlstatemachine.pro b/src/imports/scxmlstatemachine/scxmlstatemachine.pro
index 23497ac..40c3b49 100644
--- a/src/imports/scxmlstatemachine/scxmlstatemachine.pro
+++ b/src/imports/scxmlstatemachine/scxmlstatemachine.pro
@@ -5,10 +5,12 @@ QT = scxml qml-private core-private
SOURCES = \
$$PWD/plugin.cpp \
- $$PWD/statemachineloader.cpp
+ $$PWD/statemachineloader.cpp \
+ $$PWD/eventconnection.cpp
HEADERS = \
- $$PWD/statemachineloader.h
+ $$PWD/statemachineloader.h \
+ $$PWD/eventconnection.h
load(qml_plugin)
diff --git a/src/scxml/qscxmlparser.cpp b/src/scxml/qscxmlparser.cpp
index a93ea79..e760621 100644
--- a/src/scxml/qscxmlparser.cpp
+++ b/src/scxml/qscxmlparser.cpp
@@ -722,7 +722,6 @@ private:
<< QStringLiteral("log")
<< QStringLiteral("reachedStableState")
<< QStringLiteral("finished")
- << QStringLiteral("eventOccurred")
<< QStringLiteral("start")
<< QStringLiteral("setDataBinding")
<< QStringLiteral("setService")
diff --git a/src/scxml/qscxmlstatemachine.cpp b/src/scxml/qscxmlstatemachine.cpp
index d2e376b..c75c5cc 100644
--- a/src/scxml/qscxmlstatemachine.cpp
+++ b/src/scxml/qscxmlstatemachine.cpp
@@ -94,25 +94,6 @@ Q_LOGGING_CATEGORY(scxmlLog, "scxml.statemachine")
* \e event attribute of one \c <send> tag in the SCXML file.
*/
-/*!
- \fn QScxmlStateMachine::eventOccurred(const QScxmlEvent &event)
-
- This signal is emitted when the SCXML event \a event occurs. This signal is
- emitted for all external events.
-
- \sa externalEventOccurred()
-*/
-
-/*!
- \fn QScxmlStateMachine::externalEventOccurred(const QScxmlEvent &event)
-
- This signal is emitted for each \c <send> element in the SCXML file that
- contains the attribute \c {type="qt:signal"}. The event that occurred is
- specified by \a event.
-
- \sa eventOccurred()
-*/
-
namespace QScxmlInternal {
static int signalIndex(const QMetaObject *meta, const QByteArray &signalName)
@@ -166,6 +147,91 @@ void EventLoopHook::timerEvent(QTimerEvent *timerEvent)
}
}
+void ScxmlEventRouter::route(const QStringList &segments, QScxmlEvent *event)
+{
+ emit eventOccurred(*event);
+ if (!segments.isEmpty()) {
+ auto it = children.find(segments.first());
+ if (it != children.end())
+ it.value()->route(segments.mid(1), event);
+ }
+}
+
+static QString nextSegment(const QStringList &segments)
+{
+ if (segments.isEmpty())
+ return QString();
+
+ const QString &segment = segments.first();
+ return segment == QLatin1String("*") ? QString() : segment;
+}
+
+ScxmlEventRouter *ScxmlEventRouter::child(const QString &segment)
+{
+ ScxmlEventRouter *&child = children[segment];
+ if (child == nullptr)
+ child = new ScxmlEventRouter(this);
+ return child;
+}
+
+void ScxmlEventRouter::disconnectNotify(const QMetaMethod &signal)
+{
+ Q_UNUSED(signal);
+
+ // Defer the actual work, as this may be called from a destructor, or the signal may not
+ // actually be disconnected, yet.
+ QTimer::singleShot(0, this, [this] {
+ if (!children.isEmpty() || receivers(SIGNAL(eventOccurred(QScxmlEvent))) > 0)
+ return;
+
+ ScxmlEventRouter *parentRouter = qobject_cast<ScxmlEventRouter *>(parent());
+ if (!parentRouter) // root node
+ return;
+
+ QHash<QString, ScxmlEventRouter *>::Iterator it = parentRouter->children.begin(),
+ end = parentRouter->children.end();
+ for (; it != end; ++it) {
+ if (it.value() == this) {
+ parentRouter->children.erase(it);
+ parentRouter->disconnectNotify(QMetaMethod());
+ break;
+ }
+ }
+
+ deleteLater(); // The parent might delete itself, triggering QObject delete cascades.
+ });
+}
+
+QMetaObject::Connection ScxmlEventRouter::connectToEvent(const QStringList &segments,
+ const QObject *receiver,
+ const char *method,
+ Qt::ConnectionType type)
+{
+ QString segment = nextSegment(segments);
+ return segment.isEmpty() ?
+ connect(this, SIGNAL(eventOccurred(QScxmlEvent)), receiver, method, type) :
+ child(segment)->connectToEvent(segments.mid(1), receiver, method, type);
+}
+
+QMetaObject::Connection ScxmlEventRouter::connectToEvent(const QStringList &segments,
+ const QObject *receiver, void **slot,
+ QtPrivate::QSlotObjectBase *method,
+ Qt::ConnectionType type)
+{
+ QString segment = nextSegment(segments);
+ if (segment.isEmpty()) {
+ const int *types = Q_NULLPTR;
+ if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
+ types = QtPrivate::ConnectionTypes<QtPrivate::List<QScxmlEvent> >::types();
+
+ const QMetaObject *meta = metaObject();
+ static const int eventOccurredIndex = signalIndex(meta, "eventOccurred(QScxmlEvent)");
+ return QObjectPrivate::connectImpl(this, eventOccurredIndex, receiver, slot, method, type,
+ types, meta);
+ } else {
+ return child(segment)->connectToEvent(segments.mid(1), receiver, slot, method, type);
+ }
+}
} // namespace QScxmlInternal
@@ -313,10 +379,7 @@ void QScxmlStateMachinePrivate::postEvent(QScxmlEvent *event)
}
if (event->eventType() == QScxmlEvent::ExternalEvent)
- emit q->eventOccurred(*event);
-
- if (event->originType() == QLatin1String("qt:signal"))
- emit q->externalEventOccurred(*event);
+ m_router.route(event->name().split(QLatin1Char('.')), event);
const int signalIndex = m_tableData->signalIndexForEvent(event->name());
if (signalIndex != -1) {
@@ -1519,6 +1582,26 @@ QMetaObject::Connection QScxmlStateMachine::connectToState(const QString &scxmlS
return QObject::connect(this, signalName.constData(), receiver, method, type);
}
+QMetaObject::Connection QScxmlStateMachine::connectToEvent(const QString &scxmlEventSpec,
+ const QObject *receiver,
+ const char *method,
+ Qt::ConnectionType type)
+{
+ Q_D(QScxmlStateMachine);
+ return d->m_router.connectToEvent(scxmlEventSpec.split(QLatin1Char('.')), receiver, method,
+ type);
+}
+
+QMetaObject::Connection QScxmlStateMachine::connectToEventImpl(const QString &scxmlEventSpec,
+ const QObject *receiver, void **slot,
+ QtPrivate::QSlotObjectBase *slotObj,
+ Qt::ConnectionType type)
+{
+ Q_D(QScxmlStateMachine);
+ return d->m_router.connectToEvent(scxmlEventSpec.split(QLatin1Char('.')), receiver, slot,
+ slotObj, type);
+}
+
/*!
* Initializes the state machine.
*
diff --git a/src/scxml/qscxmlstatemachine.h b/src/scxml/qscxmlstatemachine.h
index 7fb1184..f38416d 100644
--- a/src/scxml/qscxmlstatemachine.h
+++ b/src/scxml/qscxmlstatemachine.h
@@ -165,6 +165,65 @@ public:
}
#endif
+ QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec,
+ const QObject *receiver, const char *method,
+ Qt::ConnectionType type = Qt::AutoConnection);
+
+#ifdef Q_QDOC
+ template<typename PointerToMemberFunction>
+ QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec,
+ const QObject *receiver, PointerToMemberFunction method,
+ Qt::ConnectionType type = Qt::AutoConnection);
+ template<typename Functor>
+ QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec, Functor functor,
+ Qt::ConnectionType type = Qt::AutoConnection);
+ template<typename Functor>
+ QMetaObject::Connection connectToEvent(const QString &scxmlEventSpec,
+ const QObject *context, Functor functor,
+ Qt::ConnectionType type = Qt::AutoConnection);
+#else
+
+ // connect state to a QObject slot
+ template <typename Func1>
+ inline QMetaObject::Connection connectToEvent(
+ const QString &scxmlEventSpec,
+ const typename QtPrivate::FunctionPointer<Func1>::Object *receiver, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ typedef QtPrivate::FunctionPointer<Func1> SlotType;
+ return connectToEventImpl(
+ scxmlEventSpec, receiver, nullptr,
+ new QtPrivate::QSlotObject<Func1, typename SlotType::Arguments, void>(slot),
+ type);
+ }
+
+ // connect state to a functor or function pointer (without context)
+ template <typename Func1>
+ inline typename QtPrivate::QEnableIf<
+ !QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
+ !QtPrivate::is_same<const char*, Func1>::value, QMetaObject::Connection>::Type
+ connectToEvent(const QString &scxmlEventSpec, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ // Use this as context
+ return connectToEvent(scxmlEventSpec, this, slot, type);
+ }
+
+ // connectToEvent to a functor or function pointer (with context)
+ template <typename Func1>
+ inline typename QtPrivate::QEnableIf<
+ !QtPrivate::FunctionPointer<Func1>::IsPointerToMemberFunction &&
+ !QtPrivate::is_same<const char*, Func1>::value, QMetaObject::Connection>::Type
+ connectToEvent(const QString &scxmlEventSpec, QObject *context, Func1 slot,
+ Qt::ConnectionType type = Qt::AutoConnection)
+ {
+ QtPrivate::QSlotObjectBase *slotObj = new QtPrivate::QFunctorSlotObject<Func1, 1,
+ QtPrivate::List<QScxmlEvent>, void>(slot);
+ return connectToEventImpl(scxmlEventSpec, context, reinterpret_cast<void **>(&slot),
+ slotObj, type);
+ }
+#endif
+
Q_INVOKABLE void submitEvent(QScxmlEvent *event);
Q_INVOKABLE void submitEvent(const QString &eventName);
Q_INVOKABLE void submitEvent(const QString &eventName, const QVariant &data);
@@ -177,11 +236,9 @@ Q_SIGNALS:
void log(const QString &label, const QString &msg);
void reachedStableState();
void finished();
- void eventOccurred(const QScxmlEvent &event);
void dataModelChanged(QScxmlDataModel *model);
void initialValuesChanged(const QVariantMap &initialValues);
void initializedChanged(bool initialized);
- void externalEventOccurred(const QScxmlEvent &event);
public Q_SLOTS:
void start();
@@ -207,6 +264,10 @@ private:
const QObject *receiver, void **slot,
QtPrivate::QSlotObjectBase *slotObj,
Qt::ConnectionType type = Qt::AutoConnection);
+ QMetaObject::Connection connectToEventImpl(const QString &scxmlEventSpec,
+ const QObject *receiver, void **slot,
+ QtPrivate::QSlotObjectBase *slotObj,
+ Qt::ConnectionType type = Qt::AutoConnection);
};
QT_END_NAMESPACE
diff --git a/src/scxml/qscxmlstatemachine_p.h b/src/scxml/qscxmlstatemachine_p.h
index 123d935..e1b2066 100644
--- a/src/scxml/qscxmlstatemachine_p.h
+++ b/src/scxml/qscxmlstatemachine_p.h
@@ -78,6 +78,29 @@ public:
protected:
void timerEvent(QTimerEvent *timerEvent) Q_DECL_OVERRIDE;
};
+
+class ScxmlEventRouter : public QObject
+{
+ Q_OBJECT
+public:
+ ScxmlEventRouter(QObject *parent = nullptr) : QObject(parent) {}
+ QMetaObject::Connection connectToEvent(const QStringList &segments, const QObject *receiver,
+ const char *method, Qt::ConnectionType type);
+ QMetaObject::Connection connectToEvent(const QStringList &segments, const QObject *receiver,
+ void **slot, QtPrivate::QSlotObjectBase *method,
+ Qt::ConnectionType type);
+
+ void route(const QStringList &segments, QScxmlEvent *event);
+
+signals:
+ void eventOccurred(const QScxmlEvent &event);
+
+private:
+ QHash<QString, ScxmlEventRouter *> children;
+ ScxmlEventRouter *child(const QString &segment);
+
+ void disconnectNotify(const QMetaMethod &signal) override;
+};
} // QScxmlInternal namespace
class QScxmlInvokableService;
@@ -297,6 +320,7 @@ public: // types & data fields:
typedef std::vector<std::pair<int, QScxmlEvent *>> DelayedQueue;
DelayedQueue m_delayedEvents;
const QMetaObject *m_metaObject;
+ QScxmlInternal::ScxmlEventRouter m_router;
private:
QScopedPointer<ParserData> m_parserData; // used when created by StateMachine::fromFile.
diff --git a/tests/auto/statemachine/statemachine.pro b/tests/auto/statemachine/statemachine.pro
index c96ce21..eefbd7f 100644
--- a/tests/auto/statemachine/statemachine.pro
+++ b/tests/auto/statemachine/statemachine.pro
@@ -1,4 +1,4 @@
-QT = core gui qml testlib scxml
+QT = core gui qml testlib scxml-private
CONFIG += testcase
TARGET = tst_statemachine
diff --git a/tests/auto/statemachine/tst_statemachine.cpp b/tests/auto/statemachine/tst_statemachine.cpp
index b3c7251..227d427 100644
--- a/tests/auto/statemachine/tst_statemachine.cpp
+++ b/tests/auto/statemachine/tst_statemachine.cpp
@@ -31,6 +31,7 @@
#include <QXmlStreamReader>
#include <QtScxml/qscxmlparser.h>
#include <QtScxml/qscxmlstatemachine.h>
+#include <QtScxml/private/qscxmlstatemachine_p.h>
Q_DECLARE_METATYPE(QScxmlError);
@@ -183,6 +184,13 @@ void tst_StateMachine::connections()
QVERIFY(disconnect(final));
}
+bool hasChildEventRouters(QScxmlStateMachine *stateMachine)
+{
+ // Cast to QObject, to avoid ambigous "children" member.
+ const QObject &parentRouter = QScxmlStateMachinePrivate::get(stateMachine)->m_router;
+ return !parentRouter.children().isEmpty();
+}
+
void tst_StateMachine::eventOccurred()
{
QScopedPointer<QScxmlStateMachine> stateMachine(QScxmlStateMachine::fromFile(QString(":/tst_statemachine/eventoccurred.scxml")));
@@ -190,29 +198,63 @@ void tst_StateMachine::eventOccurred()
qRegisterMetaType<QScxmlEvent>();
QSignalSpy finishedSpy(stateMachine.data(), SIGNAL(finished()));
- QSignalSpy eventOccurredSpy(stateMachine.data(), SIGNAL(eventOccurred(QScxmlEvent)));
- QSignalSpy externalEventOccurredSpy(stateMachine.data(), SIGNAL(externalEventOccurred(QScxmlEvent)));
+
+ int events = 0;
+ auto con1 = stateMachine->connectToEvent("internalEvent2", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 1);
+ QCOMPARE(event.name(), QString("internalEvent2"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con1);
+
+ auto con2 = stateMachine->connectToEvent("externalEvent", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 2);
+ QCOMPARE(event.name(), QString("externalEvent"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con2);
+
+ auto con3 = stateMachine->connectToEvent("timeout", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 3);
+ QCOMPARE(event.name(), QString("timeout"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con3);
+
+ auto con4 = stateMachine->connectToEvent("done.*", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 4);
+ QCOMPARE(event.name(), QString("done.state.top"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con4);
+
+ auto con5 = stateMachine->connectToEvent("done.state", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 5);
+ QCOMPARE(event.name(), QString("done.state.top"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con5);
+
+ auto con6 = stateMachine->connectToEvent("done.state.top", [&events](const QScxmlEvent &event) {
+ QCOMPARE(++events, 6);
+ QCOMPARE(event.name(), QString("done.state.top"));
+ QCOMPARE(event.eventType(), QScxmlEvent::ExternalEvent);
+ });
+ QVERIFY(con6);
stateMachine->start();
finishedSpy.wait(5000);
+ QCOMPARE(events, 6);
+
+ QVERIFY(disconnect(con1));
+ QVERIFY(disconnect(con2));
+ QVERIFY(disconnect(con3));
+ QVERIFY(disconnect(con4));
+ QVERIFY(disconnect(con5));
+ QVERIFY(disconnect(con6));
- auto event = [&eventOccurredSpy](int eventIndex) -> QScxmlEvent {
- return qvariant_cast<QScxmlEvent>(eventOccurredSpy.at(eventIndex).at(0));
- };
-
- QCOMPARE(eventOccurredSpy.count(), 4);
- QCOMPARE(event(0).name(), QLatin1String("internalEvent2"));
- QCOMPARE(event(0).eventType(), QScxmlEvent::ExternalEvent);
- QCOMPARE(event(1).name(), QLatin1String("externalEvent"));
- QCOMPARE(event(1).eventType(), QScxmlEvent::ExternalEvent);
- QCOMPARE(event(2).name(), QLatin1String("timeout"));
- QCOMPARE(event(2).eventType(), QScxmlEvent::ExternalEvent);
- QCOMPARE(event(3).name(), QLatin1String("done.state.top"));
- QCOMPARE(event(3).eventType(), QScxmlEvent::ExternalEvent);
-
- QCOMPARE(externalEventOccurredSpy.count(), 1);
- QCOMPARE(qvariant_cast<QScxmlEvent>(externalEventOccurredSpy.at(0).at(0)).name(), QLatin1String("externalEvent"));
+ QTRY_VERIFY(!hasChildEventRouters(stateMachine.data()));
}
void tst_StateMachine::doneDotStateEvent()