telldus/telldus-gui/TelldusCenter/scriptenvironment.cpp

202 lines
5.7 KiB
C++

#include "scriptenvironment.h"
#include "configurationdialog.h"
#include <limits>
#include <QScriptEngine>
#include <QScriptValueIterator>
#include <QScriptContextInfo>
#include <QStringList>
#include <QTimerEvent>
#include <QDebug>
class ScriptEnvironment::TimerObj{
public:
int originalTimerId;
qint64 remainingDelay;
QScriptValue expression;
};
class ScriptEnvironment::PrivateData {
public:
QScriptEngine *scriptEngine;
QHash<int, QScriptValue> intervalHash;
QHash<int, TimerObj*> timeoutHash;
};
ScriptEnvironment::ScriptEnvironment(QObject *parent) :
QObject(parent)
{
d = new PrivateData;
d->scriptEngine = new QScriptEngine();
connect(d->scriptEngine, SIGNAL(signalHandlerException(const QScriptValue &)), this, SLOT(scriptException(const QScriptValue&)));
d->scriptEngine->installTranslatorFunctions();
//Self is our new global object
QScriptValue self = d->scriptEngine->newQObject(this, QScriptEngine::QtOwnership, QScriptEngine::ExcludeSuperClassContents);
{
//Copy everything from our old global object
QScriptValueIterator it(d->scriptEngine->globalObject());
while (it.hasNext()) {
it.next();
self.setProperty(it.scriptName(), it.value(), it.flags());
}
}
self.setProperty("self", self);
d->scriptEngine->setGlobalObject(self);
QScriptValue application = d->scriptEngine->newQObject(parent);
d->scriptEngine->globalObject().setProperty("application", application);
//Create configuration dialog
QScriptValue configurationDialogObject = d->scriptEngine->newQObject(new ConfigurationDialog(d->scriptEngine), QScriptEngine::ScriptOwnership, QScriptEngine::ExcludeSuperClassContents);
d->scriptEngine->globalObject().property("application").setProperty("configuration", configurationDialogObject);
//Collect garbage (ie our old global object)
d->scriptEngine->collectGarbage();
}
ScriptEnvironment::~ScriptEnvironment() {
foreach(TimerObj *tim, d->timeoutHash){
delete tim;
}
// delete d->scriptEngine; //This seems to crash for some reason?
delete d;
}
QDir ScriptEnvironment::currentDir() const {
QScriptContextInfo info(d->scriptEngine->currentContext()->parentContext());
QFileInfo fileinfo(info.fileName());
return fileinfo.dir();
}
QScriptEngine *ScriptEnvironment::engine() const {
return d->scriptEngine;
}
void ScriptEnvironment::scriptException(const QScriptValue & exception) {
qDebug() << "ScriptException:" << d->scriptEngine->uncaughtExceptionLineNumber() << exception.toString();
qDebug() << "Backtrace:";
foreach( QString row, d->scriptEngine->uncaughtExceptionBacktrace() ) {
qDebug() << row;
}
d->scriptEngine->clearExceptions();
}
void ScriptEnvironment::include(const QString &filename) {
QDir dir = this->currentDir();
QFile file(dir.filePath(filename));
file.open(QFile::ReadOnly);
QString fileContents = file.readAll();
file.close();
QScriptContext *ctx = d->scriptEngine->currentContext();
ctx->setActivationObject(ctx->parentContext()->activationObject());
d->scriptEngine->evaluate(fileContents, dir.filePath(filename));
}
void ScriptEnvironment::timerEvent(QTimerEvent *event) {
int id = event->timerId();
QScriptValue expression = d->intervalHash.value(id);
if (!expression.isValid()) {
qint64 remainingDelay = d->timeoutHash.value(id)->remainingDelay;
if(remainingDelay > 0){
qint64 delay = remainingDelay;
remainingDelay = delay - std::numeric_limits<int>::max();
d->timeoutHash.value(id)->remainingDelay = remainingDelay;
if(remainingDelay > 0){
return; //just run same timer again with same interval (max int)
}
TimerObj *to = d->timeoutHash.value(id);
d->timeoutHash.remove(to->originalTimerId);
int newTimerId = startTimer(delay); //delay differs from last time, start a new timer
killTimer(id);
d->timeoutHash.insert(newTimerId, to);
return;
}
expression = d->timeoutHash.value(id)->expression;
this->clearTimeout(d->timeoutHash.value(id)->originalTimerId);
}
if (expression.isString()) {
d->scriptEngine->evaluate(expression.toString());
} else if (expression.isFunction()) {
expression.call();
}
}
int ScriptEnvironment::setTimeout(const QScriptValue &expression, qint64 delay) {
if (expression.isString() || expression.isFunction()) {
if (delay < 0) {
delay = 0;
}
TimerObj *to = new TimerObj;
to->expression = expression;
to->remainingDelay = delay - std::numeric_limits<int>::max();
if(to->remainingDelay > 0){
delay = std::numeric_limits<int>::max();
}
int timerId = startTimer(delay);
to->originalTimerId = timerId;
d->timeoutHash.insert(timerId, to);
return timerId;
}
return -1;
}
void ScriptEnvironment::clearTimeout(int timerId) {
bool found = true;
if(!d->timeoutHash.contains(timerId) || d->timeoutHash.value(timerId)->originalTimerId != timerId){
//not original timer id, find the key
found = false;
if(d->timeoutHash.count() > 0){
QHashIterator<int, TimerObj*> i(d->timeoutHash);
while(i.hasNext()){
i.next();
if(i.value()->originalTimerId == timerId){
timerId = i.key();
found = true;
break;
}
}
}
}
killTimer(timerId);
if(!found){
return;
}
delete d->timeoutHash.value(timerId);
d->timeoutHash.remove(timerId);
}
int ScriptEnvironment::setInterval(const QScriptValue &expression, int delay) {
//not safe for longer intervals (as setTimeout is)
//if it has to be done in the future, maybe do something like this:
//delay/((delay/maxint)+1)=new timer that can be run over and over again, and every ((delay/maxint)+1) time, run "expression"
if (expression.isString() || expression.isFunction()) {
if (delay < 0) {
delay = 0;
}
int timerId = startTimer(delay);
d->intervalHash.insert(timerId, expression);
return timerId;
}
return -1;
}
void ScriptEnvironment::clearInterval(int timerId) {
killTimer(timerId);
d->intervalHash.remove(timerId);
}