StelScriptMgr.cpp   StelScriptMgr.cpp 
skipping to change at line 23 skipping to change at line 23
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software * along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U SA. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, U SA.
*/ */
#include "StelScriptMgr.hpp" #include "StelScriptMgr.hpp"
#include "StelMainScriptAPI.hpp" #include "StelMainScriptAPI.hpp"
#include "ScreenImageMgr.hpp"
#include "StelApp.hpp" #include "StelApp.hpp"
#include "StelCore.hpp" #include "StelCore.hpp"
#include "StelFileMgr.hpp" #include "StelFileMgr.hpp"
#include "StelModuleMgr.hpp" #include "StelModuleMgr.hpp"
#include "StelMovementMgr.hpp" #include "StelMovementMgr.hpp"
#include "StelNavigator.hpp" #include "StelNavigator.hpp"
#include "StelSkyDrawer.hpp" #include "StelSkyDrawer.hpp"
#include "StelSkyLayerMgr.hpp" #include "StelSkyLayerMgr.hpp"
skipping to change at line 49 skipping to change at line 50
#include <QRegExp> #include <QRegExp>
#include <QSet> #include <QSet>
#include <QStringList> #include <QStringList>
#include <QTemporaryFile> #include <QTemporaryFile>
#include <QVariant> #include <QVariant>
#include <cmath> #include <cmath>
Q_DECLARE_METATYPE(Vec3f); Q_DECLARE_METATYPE(Vec3f);
class StelScriptThread : public QThread
{
public:
StelScriptThread(const QString& ascriptCode, QScriptEngine*
aengine, QString fileName) : scriptCode(ascriptCode), engine(aengine), fnam
e(fileName) {;}
QString getFileName() {return fname;}
protected:
void run()
{
// seed the QT PRNG
qsrand(QDateTime::currentDateTime().toTime_t());
// For startup scripts, the gui object might not
// have completed init when we run. Wait for that.
Q_ASSERT(StelApp::getInstance().getGui());
engine->evaluate(scriptCode);
}
private:
QString scriptCode;
QScriptEngine* engine;
QString fname;
};
QScriptValue vec3fToScriptValue(QScriptEngine *engine, const Vec3f& c) QScriptValue vec3fToScriptValue(QScriptEngine *engine, const Vec3f& c)
{ {
QScriptValue obj = engine->newObject(); QScriptValue obj = engine->newObject();
obj.setProperty("r", QScriptValue(engine, c[0])); obj.setProperty("r", QScriptValue(engine, c[0]));
obj.setProperty("g", QScriptValue(engine, c[1])); obj.setProperty("g", QScriptValue(engine, c[1]));
obj.setProperty("b", QScriptValue(engine, c[2])); obj.setProperty("b", QScriptValue(engine, c[2]));
return obj; return obj;
} }
void vec3fFromScriptValue(const QScriptValue& obj, Vec3f& c) void vec3fFromScriptValue(const QScriptValue& obj, Vec3f& c)
skipping to change at line 98 skipping to change at line 75
QScriptValue createVec3f(QScriptContext* context, QScriptEngine *engine) QScriptValue createVec3f(QScriptContext* context, QScriptEngine *engine)
{ {
Vec3f c; Vec3f c;
c[0] = context->argument(0).toNumber(); c[0] = context->argument(0).toNumber();
c[1] = context->argument(1).toNumber(); c[1] = context->argument(1).toNumber();
c[2] = context->argument(2).toNumber(); c[2] = context->argument(2).toNumber();
return vec3fToScriptValue(engine, c); return vec3fToScriptValue(engine, c);
} }
StelScriptMgr::StelScriptMgr(QObject *parent) StelScriptMgr::StelScriptMgr(QObject *parent): QObject(parent)
: QObject(parent),
thread(NULL)
{ {
// Scripting images
ScreenImageMgr* scriptImages = new ScreenImageMgr();
scriptImages->init();
StelApp::getInstance().getModuleMgr().registerModule(scriptImages);
// Allow Vec3f managment in scripts // Allow Vec3f managment in scripts
qScriptRegisterMetaType(&engine, vec3fToScriptValue, vec3fFromScript Value); qScriptRegisterMetaType(&engine, vec3fToScriptValue, vec3fFromScript Value);
// Constructor // Constructor
QScriptValue ctor = engine.newFunction(createVec3f); QScriptValue ctor = engine.newFunction(createVec3f);
engine.globalObject().setProperty("Vec3f", ctor); engine.globalObject().setProperty("Vec3f", ctor);
// Add the core object to access methods related to core // Add the core object to access methods related to core
mainAPI = new StelMainScriptAPI(this); mainAPI = new StelMainScriptAPI(this);
QScriptValue objectValue = engine.newQObject(mainAPI); QScriptValue objectValue = engine.newQObject(mainAPI);
engine.globalObject().setProperty("core", objectValue); engine.globalObject().setProperty("core", objectValue);
engine.evaluate("function mywait__(sleepDurationSec) {"
"if (sleepDurationSec<0) return;"
"var date = new Date();"
"var curDate = null;"
"do {curDate = new Date();}"
" while(curDate-date < sleepDurat
ionSec*1000/scriptRateReadOnly);}");
engine.evaluate("core['wait'] = mywait__;");
//! Waits until a specified simulation date/time. This function
//! will take into account the rate (and direction) in which simulat
ion
//! time is passing. e.g. if a future date is specified and the
//! time is moving backwards, the function will return immediately.
//! If the time rate is 0, the function will not wait. This is to
//! prevent infinite wait time.
//! @param dt the date string to use
//! @param spec "local" or "utc"
engine.evaluate("function mywaitFor__(dt, spec) {if (!spec) spec=\"u
tc\";"
" var JD = core.jdFromDateString(dt, spec);"
" var timeSpeed = core.getTimeRate();"
" if (timeSpeed == 0.) {core.debug(\"waitFor called with no ti
me passing - would be infinite. not waiting!\"); return;}"
" if (timeSpeed > 0) {core.wait((JD-core.getJDay())*timeSpeed)
;}"
" else {core.wait((core.getJDay()-JD)*timeSpeed);}}");
engine.evaluate("core['waitFor'] = mywaitFor__;");
// Add all the StelModules into the script engine // Add all the StelModules into the script engine
StelModuleMgr* mmgr = &StelApp::getInstance().getModuleMgr(); StelModuleMgr* mmgr = &StelApp::getInstance().getModuleMgr();
foreach (StelModule* m, mmgr->getAllModules()) foreach (StelModule* m, mmgr->getAllModules())
{ {
objectValue = engine.newQObject(m); objectValue = engine.newQObject(m);
engine.globalObject().setProperty(m->objectName(), objectVal ue); engine.globalObject().setProperty(m->objectName(), objectVal ue);
} }
// Add other classes which we want to be directly accessible from sc ripts // Add other classes which we want to be directly accessible from sc ripts
if(StelSkyLayerMgr* smgr = GETSTELMODULE(StelSkyLayerMgr)) if(StelSkyLayerMgr* smgr = GETSTELMODULE(StelSkyLayerMgr))
objectValue = engine.newQObject(smgr); objectValue = engine.newQObject(smgr);
// For accessing star scale, twinkle etc. // For accessing star scale, twinkle etc.
objectValue = engine.newQObject(StelApp::getInstance().getCore()->ge tSkyDrawer()); objectValue = engine.newQObject(StelApp::getInstance().getCore()->ge tSkyDrawer());
engine.globalObject().setProperty("StelSkyDrawer", objectValue); engine.globalObject().setProperty("StelSkyDrawer", objectValue);
setScriptRate(1.0);
engine.setProcessEventsInterval(10);
} }
StelScriptMgr::~StelScriptMgr() StelScriptMgr::~StelScriptMgr()
{ {
} }
QStringList StelScriptMgr::getScriptList(void) QStringList StelScriptMgr::getScriptList()
{ {
QStringList scriptFiles; QStringList scriptFiles;
try try
{ {
QSet<QString> files = StelFileMgr::listContents("scripts", S telFileMgr::File, true); QSet<QString> files = StelFileMgr::listContents("scripts", S telFileMgr::File, true);
foreach(QString f, files) foreach(QString f, files)
{ {
#ifdef ENABLE_STRATOSCRIPT_COMPAT #ifdef ENABLE_STRATOSCRIPT_COMPAT
QRegExp fileRE("^.*\\.(ssc|sts)$"); QRegExp fileRE("^.*\\.(ssc|sts)$");
#else // ENABLE_STRATOSCRIPT_COMPAT #else // ENABLE_STRATOSCRIPT_COMPAT
skipping to change at line 160 skipping to change at line 168
} }
catch (std::runtime_error& e) catch (std::runtime_error& e)
{ {
QString msg = QString("WARNING: could not list scripts: %1") .arg(e.what()); QString msg = QString("WARNING: could not list scripts: %1") .arg(e.what());
qWarning() << msg; qWarning() << msg;
emit(scriptDebug(msg)); emit(scriptDebug(msg));
} }
return scriptFiles; return scriptFiles;
} }
bool StelScriptMgr::scriptIsRunning(void) bool StelScriptMgr::scriptIsRunning()
{ {
return (thread != NULL); return engine.isEvaluating();
} }
QString StelScriptMgr::runningScriptId(void) QString StelScriptMgr::runningScriptId()
{ {
if (thread) if (engine.isEvaluating())
return thread->getFileName(); return scriptFileName;
else else
return QString(); return QString();
} }
const QString StelScriptMgr::getHeaderSingleLineCommentText(const QString& s, const QString& id, const QString& notFoundText) const QString StelScriptMgr::getHeaderSingleLineCommentText(const QString& s, const QString& id, const QString& notFoundText)
{ {
try try
{ {
QFile file(StelFileMgr::findFile("scripts/" + s, StelFileMgr ::File)); QFile file(StelFileMgr::findFile("scripts/" + s, StelFileMgr ::File));
if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{ {
QString msg = QString("WARNING: script file %1 could not be opened for reading").arg(s); QString msg = QString("WARNING: script file %1 could not be opened for reading").arg(s);
emit(scriptDebug(msg)); emit(scriptDebug(msg));
qWarning() << msg; qWarning() << msg;
return QString(); return QString();
} }
QRegExp nameExp("^\\s*//\\s*" + id + ":\\s*(.+)$"); QRegExp nameExp("^\\s*//\\s*" + id + ":\\s*(.+)$");
while (!file.atEnd()) { while (!file.atEnd())
QString line(file.readLine()); {
QString line = QString::fromUtf8(file.readLine());
if (nameExp.exactMatch(line)) if (nameExp.exactMatch(line))
{ {
file.close(); file.close();
return nameExp.capturedTexts().at(1); return nameExp.capturedTexts().at(1);
} }
} }
file.close(); file.close();
return notFoundText; return notFoundText;
} }
catch(std::runtime_error& e) catch(std::runtime_error& e)
skipping to change at line 240 skipping to change at line 249
emit(scriptDebug(msg)); emit(scriptDebug(msg));
qWarning() << msg; qWarning() << msg;
return QString(); return QString();
} }
QString desc = ""; QString desc = "";
bool inDesc = false; bool inDesc = false;
QRegExp descExp("^\\s*//\\s*Description:\\s*([^\\s].+)\\s*$" ); QRegExp descExp("^\\s*//\\s*Description:\\s*([^\\s].+)\\s*$" );
QRegExp descNewlineExp("^\\s*//\\s*$"); QRegExp descNewlineExp("^\\s*//\\s*$");
QRegExp descContExp("^\\s*//\\s*([^\\s].*)\\s*$"); QRegExp descContExp("^\\s*//\\s*([^\\s].*)\\s*$");
while (!file.atEnd()) { while (!file.atEnd())
QString line(file.readLine()); {
QString line = QString::fromUtf8(file.readLine());
if (!inDesc && descExp.exactMatch(line)) if (!inDesc && descExp.exactMatch(line))
{ {
inDesc = true; inDesc = true;
desc = descExp.capturedTexts().at(1) + " "; desc = descExp.capturedTexts().at(1) + " ";
desc.replace("\n",""); desc.replace("\n","");
} }
else if (inDesc) else if (inDesc)
{ {
QString d(""); QString d("");
if (descNewlineExp.exactMatch(line)) if (descNewlineExp.exactMatch(line))
skipping to change at line 282 skipping to change at line 291
QString msg = QString("WARNING: script file %1 could not be found: %2").arg(s).arg(e.what()); QString msg = QString("WARNING: script file %1 could not be found: %2").arg(s).arg(e.what());
emit(scriptDebug(msg)); emit(scriptDebug(msg));
qWarning() << msg; qWarning() << msg;
return QString(); return QString();
} }
} }
// Run the script located at the given location // Run the script located at the given location
bool StelScriptMgr::runScript(const QString& fileName, const QString& inclu dePath) bool StelScriptMgr::runScript(const QString& fileName, const QString& inclu dePath)
{ {
if (thread!=NULL) if (engine.isEvaluating())
{ {
QString msg = QString("ERROR: there is already a script runn ing, please wait that it's over."); QString msg = QString("ERROR: there is already a script runn ing, please wait that it's over.");
emit(scriptDebug(msg)); emit(scriptDebug(msg));
qWarning() << msg; qWarning() << msg;
return false; return false;
} }
QString absPath; QString absPath;
QString scriptDir; QString scriptDir;
try try
{ {
skipping to change at line 307 skipping to change at line 316
scriptDir = QFileInfo(absPath).dir().path(); scriptDir = QFileInfo(absPath).dir().path();
} }
catch (std::runtime_error& e) catch (std::runtime_error& e)
{ {
QString msg = QString("WARNING: could not find script file % 1: %2").arg(fileName).arg(e.what()); QString msg = QString("WARNING: could not find script file % 1: %2").arg(fileName).arg(e.what());
emit(scriptDebug(msg)); emit(scriptDebug(msg));
qWarning() << msg; qWarning() << msg;
return false; return false;
} }
// pre-process the script into a temporary file
QTemporaryFile tmpFile;
bool ok = false;
if (!tmpFile.open())
{
QString msg = QString("WARNING: cannot create temporary file
for script pre-processing");
emit(scriptDebug(msg));
qWarning() << msg;
return false;
}
QFile fic(absPath); QFile fic(absPath);
if (!fic.open(QIODevice::ReadOnly)) if (!fic.open(QIODevice::ReadOnly))
{ {
QString msg = QString("WARNING: cannot open script: %1").arg (fileName); QString msg = QString("WARNING: cannot open script: %1").arg (fileName);
emit(scriptDebug(msg)); emit(scriptDebug(msg));
qWarning() << msg; qWarning() << msg;
tmpFile.close();
return false; return false;
} }
if (includePath!="" && !includePath.isEmpty()) scriptFileName = fileName;
if (!includePath.isEmpty())
scriptDir = includePath; scriptDir = includePath;
if (fileName.right(4) == ".ssc") // Seed the PRNG so that script random numbers aren't always the sam
ok = preprocessScript(fic, tmpFile, scriptDir); e sequence
#ifdef ENABLE_STRATOSCRIPT_COMPAT qsrand(QDateTime::currentDateTime().toTime_t());
else if (fileName.right(4) == ".sts")
ok = preprocessStratoScript(fic, tmpFile, scriptDir);
#endif
fic.close(); // Make sure that the gui object have been completely initialized (t
here used to be problems with startup scripts).
Q_ASSERT(StelApp::getInstance().getGui());
if (ok==false) savedTimeRate = StelApp::getInstance().getCore()->getNavigator()->ge
tTimeRate();
engine.globalObject().setProperty("scriptRateReadOnly", 1.0);
// Notify that the script starts here although we still have to prep
rocess it.
emit(scriptRunning());
QString preprocessedScript;
bool ok=false;
if (fileName.endsWith(".ssc"))
ok = preprocessScript(fic, preprocessedScript, scriptDir);
#ifdef ENABLE_STRATOSCRIPT_COMPAT
else if (fileName.endsWith(".sts"))
ok = preprocessStratoScript(fic, preprocessedScript, scriptD
ir);
#endif
if (!ok)
{ {
tmpFile.close(); scriptEnded();
return false; return false;
} }
tmpFile.seek(0); // run that script
thread = new StelScriptThread(QTextStream(&tmpFile).readAll(), &engi engine.evaluate(preprocessedScript);
ne, fileName); scriptEnded();
tmpFile.close();
connect(thread, SIGNAL(finished()), this, SLOT(scriptEnded()));
thread->start();
emit(scriptRunning());
return true; return true;
} }
bool StelScriptMgr::stopScript(void) void StelScriptMgr::stopScript()
{ {
if (thread) if (engine.isEvaluating())
{ {
QString msg = QString("INFO: asking running script to exit") ; QString msg = QString("INFO: asking running script to exit") ;
emit(scriptDebug(msg)); emit(scriptDebug(msg));
qDebug() << msg; //qDebug() << msg;
thread->terminate(); engine.abortEvaluation();
return true;
}
else
{
QString msg = QString("WARNING: no script is running");
emit(scriptDebug(msg));
qWarning() << msg;
return false;
} }
scriptEnded();
} }
void StelScriptMgr::setScriptRate(double r) void StelScriptMgr::setScriptRate(float r)
{ {
//qDebug() << "StelScriptMgr::setScriptRate(" << r << ")";
if (!engine.isEvaluating())
{
engine.globalObject().setProperty("scriptRateReadOnly", r);
return;
}
float currentScriptRate = engine.globalObject().property("scriptRate
ReadOnly").toNumber();
// pre-calculate the new time rate in an effort to prevent there bei ng much latency // pre-calculate the new time rate in an effort to prevent there bei ng much latency
// between setting the script rate and the time rate. // between setting the script rate and the time rate.
double factor = r / mainAPI->getScriptSleeper().getRate(); float factor = r / currentScriptRate;
StelNavigator* nav = StelApp::getInstance().getCore()->getNavigator( ); StelNavigator* nav = StelApp::getInstance().getCore()->getNavigator( );
double newTimeRate = nav->getTimeRate() * factor; nav->setTimeRate(nav->getTimeRate() * factor);
mainAPI->getScriptSleeper().setRate(r); GETSTELMODULE(StelMovementMgr)->setMovementSpeedFactor(nav->getTimeR
if (scriptIsRunning()) ate());
nav->setTimeRate(newTimeRate); engine.globalObject().setProperty("scriptRateReadOnly", r);
GETSTELMODULE(StelMovementMgr)->setMovementSpeedFactor(newTimeRate);
} }
double StelScriptMgr::getScriptRate(void) double StelScriptMgr::getScriptRate()
{ {
return mainAPI->getScriptSleeper().getRate(); return engine.globalObject().property("scriptRateReadOnly").toNumber ();
} }
void StelScriptMgr::debug(const QString& msg) void StelScriptMgr::debug(const QString& msg)
{ {
emit(scriptDebug(msg)); emit(scriptDebug(msg));
} }
void StelScriptMgr::scriptEnded() void StelScriptMgr::scriptEnded()
{ {
mainAPI->getScriptSleeper().setRate(1);
thread->deleteLater();
thread=NULL;
if (engine.hasUncaughtException()) if (engine.hasUncaughtException())
{ {
QString msg = QString("script error: \"%1\" @ line %2").arg( engine.uncaughtException().toString()).arg(engine.uncaughtExceptionLineNumb er()); QString msg = QString("script error: \"%1\" @ line %2").arg( engine.uncaughtException().toString()).arg(engine.uncaughtExceptionLineNumb er());
emit(scriptDebug(msg)); emit(scriptDebug(msg));
qWarning() << msg; qWarning() << msg;
} }
// reset time rate to non-scaped script rates... TODO
StelNavigator* nav = StelApp::getInstance().getCore()->getNavigator(
);
nav->setTimeRate(savedTimeRate);
GETSTELMODULE(StelMovementMgr)->setMovementSpeedFactor(1.0);
emit(scriptStopped()); emit(scriptStopped());
} }
QMap<QString, QString> StelScriptMgr::mappify(const QStringList& args, bool lowerKey) QMap<QString, QString> StelScriptMgr::mappify(const QStringList& args, bool lowerKey)
{ {
QMap<QString, QString> map; QMap<QString, QString> map;
for(int i=0; i+1<args.size(); i++) for(int i=0; i+1<args.size(); i++)
if (lowerKey) if (lowerKey)
map[args.at(i).toLower()] = args.at(i+1); map[args.at(i).toLower()] = args.at(i+1);
else else
skipping to change at line 432 skipping to change at line 440
bool StelScriptMgr::strToBool(const QString& str) bool StelScriptMgr::strToBool(const QString& str)
{ {
if (str.toLower() == "off" || str.toLower() == "no") if (str.toLower() == "off" || str.toLower() == "no")
return false; return false;
else if (str.toLower() == "on" || str.toLower() == "yes") else if (str.toLower() == "on" || str.toLower() == "yes")
return true; return true;
return QVariant(str).toBool(); return QVariant(str).toBool();
} }
bool StelScriptMgr::preprocessScript(QFile& input, QFile& output, const QSt ring& scriptDir) bool StelScriptMgr::preprocessScript(QFile& input, QString& output, const Q String& scriptDir)
{ {
QRegExp includeRe("^include\\s*\\(\\s*\"([^\"]+)\"\\s*\\)\\s*;\\s*(/ /.*)?$");
while (!input.atEnd()) while (!input.atEnd())
{ {
QString line = QString::fromUtf8(input.readLine()); QString line = QString::fromUtf8(input.readLine());
QRegExp includeRe("^include\\s*\\(\\s*\"([^\"]+)\"\\s*\\)\\s *;\\s*(//.*)?$");
if (includeRe.exactMatch(line)) if (includeRe.exactMatch(line))
{ {
QString fileName = includeRe.capturedTexts().at(1); QString fileName = includeRe.capturedTexts().at(1);
QString path; QString path;
// Search for the include file. Rules are: // Search for the include file. Rules are:
// 1. If path is absolute, just use that // 1. If path is absolute, just use that
// 2. If path is relative, look in scriptDir + inclu ded filename // 2. If path is relative, look in scriptDir + inclu ded filename
if (QFileInfo(fileName).isAbsolute()) if (QFileInfo(fileName).isAbsolute())
path = fileName; path = fileName;
skipping to change at line 476 skipping to change at line 484
preprocessScript(fic, output, scriptDir); preprocessScript(fic, output, scriptDir);
} }
else else
{ {
qWarning() << "WARNING: could not open scrip t include file for reading:" << path; qWarning() << "WARNING: could not open scrip t include file for reading:" << path;
return false; return false;
} }
} }
else else
{ {
output.write(line.toUtf8()); output += line;
output += '\n';
} }
} }
return true; return true;
} }
 End of changes. 39 change blocks. 
98 lines changed or deleted 116 lines changed or added

This html diff was produced by rfcdiff 1.41. The latest version is available from http://tools.ietf.org/tools/rfcdiff/