summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNoam Rosenthal <nrosenth@nokia.com>2009-06-15 15:04:01 +0200
committerNoam Rosenthal <nrosenth@nokia.com>2009-06-15 15:04:01 +0200
commit65e5705f1955b2a780668d9766abc64973d6ee12 (patch)
treefdbaa0d687218784f763fe4519b3c9e597ff73cf
parent632fdef78091e8e3f1a2b31d31a7f42c78b10706 (diff)
Bug fixes after testing with VUI
-rw-r--r--src/qscxml.cpp362
-rw-r--r--src/qscxmlgui.cpp2
2 files changed, 274 insertions, 90 deletions
diff --git a/src/qscxml.cpp b/src/qscxml.cpp
index 3ecf709..f94a1c5 100644
--- a/src/qscxml.cpp
+++ b/src/qscxml.cpp
@@ -36,11 +36,122 @@
#include <QHistoryState>
#include <QFinalState>
#include <QState>
+#include <QMetaMethod>
#ifdef QT_GUI_LIB
#include "qscxmlgui.h"
#endif
+
+class QtScxmlSnoopInternal : public QObject
+ {
+ Q_OBJECT
+
+ friend class QtScxmlSnoop;
+
+ QtScxmlSnoopInternal(QObject* o) :QObject(o) { }
+ ~QtScxmlSnoopInternal() {
+ if (parent()) parent()->deleteLater();
+ }
+
+ signals:
+ void signal (const QVariantList &);
+ };
+ class QtScxmlSnoop: public QObject
+ {
+ public:
+ QtScxmlSnoopInternal* inobj;
+ QtScxmlSnoop(QObject *obj, const char *aSignal):QObject(obj)
+ {
+#ifdef Q_CC_BOR
+ const int memberOffset = QObject::staticMetaObject.methodCount();
+#else
+ static const int memberOffset = QObject::staticMetaObject.methodCount();
+#endif
+ Q_ASSERT(obj);
+ Q_ASSERT(aSignal);
+
+ if (aSignal[0] - '0' != QSIGNAL_CODE) {
+ qWarning("QtScxmlSnoop: Not a valid signal, use the SIGNAL macro");
+ return;
+ }
+
+ QByteArray ba = QMetaObject::normalizedSignature(aSignal + 1);
+ const QMetaObject *mo = obj->metaObject();
+ int sigIndex = mo->indexOfMethod(ba.constData());
+ if (sigIndex < 0) {
+ qWarning("QtScxmlSnoop: No such signal: '%s'", ba.constData());
+ return;
+ }
+
+ if (!QMetaObject::connect(obj, sigIndex, this, memberOffset,
+ Qt::QueuedConnection, 0)) {
+ qWarning("QtScxmlSnoop: QMetaObject::connect returned false. Unable to connect.");
+ return;
+ }
+ sig = ba;
+ QMetaMethod member = mo->method(sigIndex);
+ QList<QByteArray> params = member.parameterTypes();
+ for (int i = 0; i < params.count(); ++i) {
+ int tp = QMetaType::type(params.at(i).constData());
+ if (tp == QMetaType::Void)
+ qWarning("Don't know how to handle '%s', use qRegisterMetaType to register it.",
+ params.at(i).constData());
+ args << tp;
+ }
+ inobj = new QtScxmlSnoopInternal (this);
+ }
+
+ inline bool isValid() const { return !sig.isEmpty(); }
+ inline QByteArray signal() const { return sig; }
+
+
+ int qt_metacall(QMetaObject::Call call, int id, void **a)
+ {
+ id = QObject::qt_metacall(call, id, a);
+ if (id < 0)
+ return id;
+
+ if (call == QMetaObject::InvokeMetaMethod) {
+ if (id == 0) {
+ QVariantList list;
+ for (int i = 0; i < args.count(); ++i) {
+ QMetaType::Type type = static_cast<QMetaType::Type>(args.at(i));
+ QVariant v(type, a[i + 1]);
+ list << v;
+
+ }
+ emit inobj->signal (list);
+ }
+ --id;
+ }
+ return id;
+ }
+
+
+
+ // the full, normalized signal name
+ QByteArray sig;
+ // holds the QMetaType types for the argument list of the signal
+ QList<int> args;
+
+ };
+
+QObject* q_snoopConnect (
+ QObject* sender,
+ const char* signal,
+ QObject* receiver,
+ const char* method
+ )
+{
+ QtScxmlSnoop* o = new QtScxmlSnoop(sender,signal);
+ if (o->isValid()) {
+ QObject::connect (o->inobj, SIGNAL(signal(QVariantList)),receiver,method);
+ QObject::connect (receiver, SIGNAL(destroyed()), o, SLOT(deleteLater()));
+ }
+ return o;
+}
+
class QScxmlPrivate
{
public:
@@ -109,10 +220,45 @@ static QScriptValue _q_deepCopy(const QScriptValue & val)
} else
return val;
}
-
-
+class QScxmlSignalReceiver : public QObject
+{
+ Q_OBJECT
+ QScxml* scxml;
+ QString eventName;
+ public:
+ QScxmlSignalReceiver(QScxml* s, QString ename) : QObject(s),scxml(s),eventName(ename)
+ {
+ }
+ public Q_SLOTS:
+ void receiveSignal(const QVariantList & pvals)
+ {
+ QStringList pnames;
+ for (int i=0; i < pvals.count(); ++i) {
+ pnames << QString::number(i);
+ }
+ QScxmlEvent* ev = new QScxmlEvent(eventName,pnames,pvals,QScriptValue());
+ ev->metaData.kind = QScxmlEvent::MetaData::Platform;
+ scxml->postEvent(ev);
+ }
+};
struct QScxmlFunctions
{
+static QScriptValue connectSignalToEvent(QScriptContext* context, QScriptEngine* engine)
+{
+ QScxml* scxml = qobject_cast<QScxml*>(context->thisObject().toQObject());
+ if (scxml) {
+ QObject* obj = context->argument(0).toQObject();
+ QString sig = ('0'+QSIGNAL_CODE)+context->argument(1).toString();
+ QString ename = context->argument(2).toString();
+
+ if (obj)
+ {
+ q_snoopConnect(obj,sig.toAscii().constData(),new QScxmlSignalReceiver(scxml,ename),SLOT(receiveSignal(QVariantList)));
+ }
+ }
+ return QScriptValue();
+}
+
static QScriptValue cssTime(QScriptContext *context, QScriptEngine *engine)
{
QString str;
@@ -162,26 +308,6 @@ static QScriptValue deepCopy(QScriptContext *context, QScriptEngine *)
return _q_deepCopy(context->argument(0));
}
-static QScriptValue receiveSignal(QScriptContext *context, QScriptEngine *engine)
-{
- QString eventName = context->thisObject().property("e").toString();
- if (!eventName.isEmpty()) {
- QScxml* scxml = qobject_cast<QScxml*>(engine->globalObject().property("scxml").toQObject());
- if (scxml) {
- QStringList pnames;
- QVariantList pvals;
- for (int i=0; i < context->argumentCount(); ++i) {
- pnames << QString::number(i);
- pvals << context->argument(i).toVariant();
- }
- QScxmlEvent* ev = new QScxmlEvent(eventName,pnames,pvals,QScriptValue());
- ev->metaData.kind = QScxmlEvent::MetaData::Platform;
- scxml->postEvent(ev);
- }
- }
- return QScriptValue();
-}
-
static QScriptValue postEvent(QScriptContext *context, QScriptEngine *engine)
{
QScxml* scxml = qobject_cast<QScxml*>(context->thisObject().toQObject());
@@ -266,8 +392,7 @@ static QScriptValue invoke(QScriptContext *context, QScriptEngine *engine)
}
} if (context->argumentCount() > 4)
cnt = context->argument(4);
-
-
+
QScxmlInvokerFactory* invf = NULL;
for (int i=0; i < scxml->pvt->invokerFactories.count() && invf == NULL; ++i)
if (scxml->pvt->invokerFactories[i]->isTypeSupported(type))
@@ -591,19 +716,19 @@ QScxml::QScxml(QObject* parent)
QScriptValue glob = pvt->scriptEng->globalObject();
QScriptValue scxmlObj = pvt->scriptEng->newQObject(this);
glob.setProperty("In",pvt->scriptEng->newFunction(QScxmlFunctions::isInState));
- glob.setProperty("_rcvSig",pvt->scriptEng->newFunction(QScxmlFunctions::receiveSignal));
- glob.setProperty("print",pvt->scriptEng->newFunction(QScxmlFunctions::script_print));
+// glob.setProperty("_rcvSig",pvt->scriptEng->newFunction(QScxmlFunctions::receiveSignal));
+ scxmlObj.setProperty("print",pvt->scriptEng->newFunction(QScxmlFunctions::script_print));
scxmlObj.setProperty("postEvent",pvt->scriptEng->newFunction(QScxmlFunctions::postEvent));
scxmlObj.setProperty("invoke",pvt->scriptEng->newFunction(QScxmlFunctions::invoke));
scxmlObj.setProperty("cssTime",pvt->scriptEng->newFunction(QScxmlFunctions::cssTime));
scxmlObj.setProperty("clone",pvt->scriptEng->newFunction(QScxmlFunctions::deepCopy));
scxmlObj.setProperty("setTimeout",pvt->scriptEng->newFunction(QScxmlFunctions::setTimeout));
scxmlObj.setProperty("clearTimeout",pvt->scriptEng->newFunction(QScxmlFunctions::clearTimeout));
+ scxmlObj.setProperty("connectSignalToEvent",pvt->scriptEng->newFunction(QScxmlFunctions::connectSignalToEvent));
QScriptValue dmObj = pvt->scriptEng->newObject();
glob.setProperty("_data",pvt->scriptEng->newObject());
glob.setProperty("_global",pvt->scriptEng->globalObject());
glob.setProperty("scxml",scxmlObj);
- glob.setProperty("connectSignalToEvent",pvt->scriptEng->evaluate("function(sig,ev) {sig.connect({'e':ev},_rcvSig);}"));
static QScxmlAutoInvokerFactory<QScxmlDefaultInvoker> _s_defaultInvokerFactory;
static QScxmlAutoInvokerFactory<QScxmlBindingInvoker> _s_bindingInvokerFactory;
registerInvokerFactory(&_s_defaultInvokerFactory);
@@ -672,8 +797,38 @@ void QScxml::beginSelectTransitions(QEvent* ev)
}
+static QString _q_configToString (QAbstractState* from,int level, const QSet<QAbstractState*> & config)
+{
+ QString str;
+ if (from) {
+ if (level >= 0) {
+ for (int i=0; i < level; ++i)
+ str += "\t";
+
+ QState* p = qobject_cast<QState*>(from->parent());
+ char c = '$';
+ if (qobject_cast<QHistoryState*>(from))
+ c = '^';
+ else if (qobject_cast<QFinalState*>(from))
+ c = '~';
+ else if (p) {
+ if (p->childMode() == QState::ParallelStates)
+ c = '{';
+ }
+ str += QString("%1%2 %3\n").arg(config.contains(from)?">":" ").arg(c).arg(from->objectName());
+ }
+ QObjectList ch = from->children();
+ foreach (QObject* o, ch)
+ str += _q_configToString(qobject_cast<QAbstractState*>(o),level+1,config);
+ }
+
+ return str;
+}
+
/*! \internal */
-void QScxml::endMicrostep(QEvent*)
+
+
+void QScxml::endMicrostep(QEvent* e)
{
scriptEngine()->globalObject().setProperty("_event",QScriptValue());
for (QHash<QString,QScxmlPrivate::AnchorSnapshot>::iterator
@@ -687,7 +842,11 @@ void QScxml::endMicrostep(QEvent*)
pvt->snapshotStack.remove(0,pvt->snapshotStack.size()-100);
}
pvt->curSnapshot.clear();
-// qDebug() << configuration();
+ /*
+ if (e->type() == QScxmlEvent::eventType()) {
+ qDebug() << "\n" + _q_configToString(rootState(),-1,configuration());
+ }
+ */
}
/*! Returns the script engine attached to the state-machine. */
@@ -735,7 +894,7 @@ void QScxml::postNamedEvent(const QString & event)
void QScxml::executeScript (const QString & s)
{
-// qDebug() << "Executing\n-----------------------------\n"<<s;
+// qDebug() << "Executing\n--------------------------\n"<<s;
pvt->scriptEng->evaluate (s,baseUrl().toLocalFile());
if (pvt->scriptEng->hasUncaughtException()) {
QScxmlEvent* e = new QScxmlEvent("error.illegalvalue",
@@ -746,6 +905,7 @@ void QScxml::executeScript (const QString & s)
<< QVariant(pvt->scriptEng->uncaughtExceptionLineNumber())
<< QVariant(pvt->scriptEng->uncaughtExceptionBacktrace()));
e->metaData.kind = QScxmlEvent::MetaData::Platform;
+ qDebug() << pvt->scriptEng->uncaughtException().toString();
postEvent(e);
pvt->scriptEng->clearExceptions();
}
@@ -1056,7 +1216,8 @@ void QScxmlLoader::loadState (
QState* topLevelState = NULL;
QHistoryState* curHistoryState = NULL;
QString initialID = "";
- QString idLocation;
+ QString idLocation, target, targetType, eventName, delay, content;
+ QStringList paramNames, paramVals;
QScxmlTransition* curTransition = NULL;
bool inRoot = true;
while (!r.atEnd()) {
@@ -1154,7 +1315,7 @@ void QScxmlLoader::loadState (
curExecContext.script += txt;
} else if (r.name().toString().compare("log",Qt::CaseInsensitive) == 0) {
curExecContext.script +=
- QString("print('[' + %1 + '][' + %2 + ']' + %3);")
+ QString("scxml.print('[' + %1 + '][' + %2 + ']' + %3);")
.arg(sanitize(r.attributes().value("label")))
.arg(sanitize(r.attributes().value("level")))
.arg(sanitize(r.attributes().value("expr")));
@@ -1184,35 +1345,39 @@ void QScxmlLoader::loadState (
curExecContext.type = ScExecContext::StateExit;
curExecContext.script = "";
} else if (r.name().toString().compare("raise",Qt::CaseInsensitive) == 0 || r.name().toString().compare("event",Qt::CaseInsensitive) == 0 ) {
- QString ev = r.attributes().value("event").toString();
- if (ev.isEmpty())
- ev = r.attributes().value("name").toString();
- curExecContext.script +=
- QString("{"
- "var paramNames = []; var paramValues = []; "
- "var content = ''; var eventName='%1'; "
- "var target = '_internal'; var targetType = 'scxml'; ").arg(ev);
-
+ eventName = QString("\"%1\"").arg(r.attributes().value("event").toString());
+ target = "'_internal'";
+ targetType = "scxml";
+ content = "{}";
+ paramNames.clear();
+ paramVals.clear();
} else if (r.name().toString().compare("send",Qt::CaseInsensitive) == 0) {
- QString type = r.attributes().value("type").toString();
- if (type.isEmpty())
- type = r.attributes().value("targettype").toString();
- curExecContext.script +=
- QString("{"
- "var paramNames = [%1]; var paramValues = []; "
- "var content = ''; var eventName=%2; "
- "var targetType = %3; var target = %4;")
- .arg(r.attributes().value("namelist").toString().replace(" ",","))
- .arg(sanitize(r.attributes().value("event").toString()))
- .arg(type.isEmpty() ? "'scxml'" : sanitize(r.attributes().value("type")))
- .arg(r.attributes().value("target").length() ? sanitize(r.attributes().value("target")) : "''");
+ paramNames.clear ();
+ paramVals.clear();
+ content = "{}";
+
+ target = r.attributes().value("target").toString();
+ if (target == "")
+ target = "\"\"";
+ targetType = r.attributes().value("type").toString();
+ eventName = r.attributes().value("event").toString();
+ QStringList nameList = r.attributes().value("namelist").toString().split(" ");
+ foreach (QString name,nameList) {
+ if (name != "") {
+ paramNames << name;
+ paramVals << QString("_data.") + name;
+ }
+ }
idLocation = r.attributes().value("idlocation").toString();
if (idLocation.isEmpty())
idLocation = r.attributes().value("sendid").toString();
+
+ delay = r.attributes().value("delay").toString();
+ if (delay == "")
+ delay = "0";
+ else
+ delay = QString("scxml.cssTime(%1)").arg(delay);
- curExecContext.script += QString("var delay = %1; ").arg(r.attributes().value("delay").length()
- ? QString("scxml.cssTime(%1)").arg(sanitize(r.attributes().value("delay")))
- : "0");
} else if (r.name().toString().compare("invoke",Qt::CaseInsensitive) == 0) {
idLocation = r.attributes().value("idlocation").toString();
if (idLocation.isEmpty())
@@ -1221,18 +1386,16 @@ void QScxmlLoader::loadState (
QString type = r.attributes().value("type").toString();
if (type.isEmpty())
- type = r.attributes().value("targettype").toString();
+ type = "scxml";
curExecContext.type = ScExecContext::StateEntry;
curExecContext.state = curState;
- curExecContext.script =
- QString("{"
- "var paramNames = []; var paramValues = []; "
- "var content = ''; "
- "var srcType = \"%1\"; var src = %2;")
- .arg(type.length() ? type : "scxml")
- .arg(r.attributes().value("src").length() ? sanitize(r.attributes().value("target")) : "\"\"");
-
-
+ paramNames.clear ();
+ paramVals.clear ();
+ content = "{}";
+ target = r.attributes().value("src").toString();
+ if (target == "")
+ target = "\"\"";
+ targetType = r.attributes().value("type").toString();
} else if (r.name().toString().compare("transition",Qt::CaseInsensitive) == 0) {
if (curHistoryState) {
ScHistoryInfo inf;
@@ -1279,15 +1442,10 @@ void QScxmlLoader::loadState (
stateMachine->scriptEngine()->evaluate("_data")
.setProperty(id,val);
} else if (r.name().toString().compare("param",Qt::CaseInsensitive) == 0) {
- curExecContext.script +=
- QString("paramNames[paramNames.length] = \"%1\";")
- .arg(r.attributes().value("name").toString());
- curExecContext.script +=
- QString("paramValues[paramValues.length] = %1;")
- .arg(sanitize(r.attributes().value("expr")));
-
+ paramNames << r.attributes().value("name").toString();
+ paramVals << r.attributes().value("expr").toString();
} else if (r.name().toString().compare("content",Qt::CaseInsensitive) == 0) {
- curExecContext.script += QString("content = %1; ").arg(sanitize(r.readElementText()));
+ content = r.readElementText();
}
} else if (r.isEndElement()) {
if (r.name().toString().compare("state",Qt::CaseInsensitive) == 0 || r.name().toString().compare("parallel",Qt::CaseInsensitive) == 0) {
@@ -1301,18 +1459,29 @@ void QScxmlLoader::loadState (
curHistoryState = NULL;
} else if (r.name().toString().compare("final",Qt::CaseInsensitive) == 0) {
curExecContext.state = (curExecContext.state->parentState());
- } else if (r.name().toString().compare("send",Qt::CaseInsensitive) == 0) {
+ } else if (r.name().toString().compare("if",Qt::CaseInsensitive) == 0) {
+ curExecContext.script += "}\n";
+ } else if (r.name().toString().compare("send",Qt::CaseInsensitive) == 0 || r.name().toString().compare("raise",Qt::CaseInsensitive) == 0) {
if (!idLocation.isEmpty())
curExecContext.script += idLocation + " = ";
- curExecContext.script += QString("scxml.setTimeout(function() { "
- "scxml.postEvent("
- "eventName,target,targetType,paramNames,paramValues,content"
- ");"
- "}, delay); }");
+ QString pnames;
+ bool first = true;
+ foreach (QString n, paramNames) {
+ if (!first)
+ pnames +=",";
+ pnames += QString("\"%1\"").arg(n);
+ first = false;
+ }
+ QString innerScript = QString("scxml.postEvent(%1,%2,\"%3\",[%4],[%5],%6);")
+ .arg(eventName).arg(target).arg(targetType)
+ .arg(pnames).arg(paramVals.join(",")).arg(content);
+ if (target == "'_internal'")
+ curExecContext.script += innerScript;
+ else
+ curExecContext.script += QString("scxml.setTimeout(function() {%1}, %2);")
+ .arg(innerScript).arg(delay);
idLocation = "";
- } else if (r.name().toString().compare("raise",Qt::CaseInsensitive) == 0) {
- curExecContext.script += "scxml.postEvent(eventName,target,targetType,paramNames,paramValues,content); }";
- } else if (
+ } else if (
r.name().toString().compare("onentry",Qt::CaseInsensitive) == 0
|| r.name().toString().compare("onexit",Qt::CaseInsensitive) == 0
|| r.name().toString().compare("scxml",Qt::CaseInsensitive) == 0) {
@@ -1332,7 +1501,15 @@ void QScxmlLoader::loadState (
ti->script = curExecContext.script;
curExecContext.type = ScExecContext::None;
} else if (r.name().toString().compare("invoke",Qt::CaseInsensitive) == 0) {
- curExecContext.script += QString("_data.invoke_%1 = scxml.invoke(srcType,src,paramNames,paramValues,content); }").arg(curState->objectName());
+ QString pnames;
+ bool first = true;
+ foreach (QString n, paramNames) {
+ if (!first)
+ pnames +=",";
+ pnames += QString("\"%1\"").arg(n);
+ first = false;
+ }
+ curExecContext.script += QString("_data.invoke_%1 = scxml.invoke(\"%2\",%3,[%4],[%5],%6); _data.invoke_%1.id = \"%1\";").arg(curState->objectName()).arg(targetType).arg(target).arg(pnames).arg(paramVals.join(",")).arg(content);
if (!idLocation.isEmpty()) {
curExecContext.script += QString("%1 = _data.invoke_%2;").arg(idLocation).arg(curState->objectName());
}
@@ -1350,6 +1527,10 @@ void QScxmlLoader::loadState (
QScxml* QScxmlLoader::load(QIODevice* device, QObject* obj, const QString & filename)
{
+ if (device->bytesAvailable() == 0) {
+ qWarning() << QString("File %1 invalid or not found").arg(filename);
+ return NULL;
+ }
stateMachine = new QScxml(obj);
// traverse through the states
loadState(stateMachine->rootState(),device,"",filename);
@@ -1361,9 +1542,14 @@ QScxml* QScxmlLoader::load(QIODevice* device, QObject* obj, const QString & file
foreach (QString s, signalEvents) {
QString sig = s;
sig = sig.mid(sig.indexOf(':')+1);
- sig = sig.left(sig.indexOf('('));
- QString scr = QString("%1.connect({e:\"%2\"},_rcvSig);\n").arg(sig).arg(s);
- stateMachine->pvt->startScript += scr;
+// sig = sig.left(sig.indexOf('('));
+ int liop = sig.lastIndexOf('.');
+ QString obj = sig.left(liop);
+ sig = sig.mid(liop+1);
+ stateMachine->pvt->startScript += QString("scxml.connectSignalToEvent(%1,'%2',\"%3\");").arg(obj).arg(sig).arg(s);
+
+// QString scr = QString("%1.connect({e:\"%2\"},_rcvSig);\n").arg(sig).arg(s);
+/// stateMachine->pvt->startScript += scr;
}
foreach (QState* s, statesWithFinal) {
@@ -1409,4 +1595,4 @@ QScxml* QScxml::load (const QString & filename, QObject* o)
}
#include "qscxml.moc"
-#endif \ No newline at end of file
+#endif
diff --git a/src/qscxmlgui.cpp b/src/qscxmlgui.cpp
index 724e12a..cb6745f 100644
--- a/src/qscxmlgui.cpp
+++ b/src/qscxmlgui.cpp
@@ -68,7 +68,6 @@ namespace
void QScxmlMenuInvoker::activate ()
{
- qDebug() << "Activating menu";
QScxmlEvent* ie = initEvent;
QScriptValue v = ie->content();
QWidget* parent = qobject_cast<QWidget*>(v.property("parent").toQObject());
@@ -85,7 +84,6 @@ void QScxmlMenuInvoker::activate ()
}
void QScxmlMenuInvoker::cancel ()
{
- qDebug() << "QScxmlMenuInvoker::cancel";
if (menu)
menu->deleteLater();
QScxmlInvoker::cancel();