From 65e5705f1955b2a780668d9766abc64973d6ee12 Mon Sep 17 00:00:00 2001 From: Noam Rosenthal Date: Mon, 15 Jun 2009 15:04:01 +0200 Subject: Bug fixes after testing with VUI --- src/qscxml.cpp | 362 +++++++++++++++++++++++++++++++++++++++++------------- src/qscxmlgui.cpp | 2 - 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 #include #include +#include #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 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(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 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(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(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(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 _s_defaultInvokerFactory; static QScxmlAutoInvokerFactory _s_bindingInvokerFactory; registerInvokerFactory(&_s_defaultInvokerFactory); @@ -672,8 +797,38 @@ void QScxml::beginSelectTransitions(QEvent* ev) } +static QString _q_configToString (QAbstractState* from,int level, const QSet & config) +{ + QString str; + if (from) { + if (level >= 0) { + for (int i=0; i < level; ++i) + str += "\t"; + + QState* p = qobject_cast(from->parent()); + char c = '$'; + if (qobject_cast(from)) + c = '^'; + else if (qobject_cast(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(o),level+1,config); + } + + return str; +} + /*! \internal */ -void QScxml::endMicrostep(QEvent*) + + +void QScxml::endMicrostep(QEvent* e) { scriptEngine()->globalObject().setProperty("_event",QScriptValue()); for (QHash::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"<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(v.property("parent").toQObject()); @@ -85,7 +84,6 @@ void QScxmlMenuInvoker::activate () } void QScxmlMenuInvoker::cancel () { - qDebug() << "QScxmlMenuInvoker::cancel"; if (menu) menu->deleteLater(); QScxmlInvoker::cancel(); -- cgit v1.2.3