/*
This is part of TeXworks, an environment for working with TeX documents
Copyright (C) 2007-09 Jonathan Kew
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
For links to further information, or to contact the author,
see .
*/
#include "TWScriptable.h"
#include "TWApp.h"
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if QT_VERSION >= 0x040500
#include
#endif
bool TWScript::setFile(QString filename)
{
if (!QFile::exists(filename))
return false;
m_Filename = filename;
return parseHeader();
}
void TWScript::clearHeaderData()
{
m_Type = ScriptUnknown;
m_Title = "";
m_Description = "";
m_Author = "";
m_Version = "";
m_Hook = "";
}
bool JSScript::parseHeader()
{
QString line, key, value;
QFile file(m_Filename);
clearHeaderData();
if (!file.exists() || !file.open(QIODevice::ReadOnly))
return false;
// skip any empty lines
QTextStream s(&file);
while (!s.atEnd()) {
line = s.readLine().trimmed();
if (!line.isEmpty())
break;
}
// is this a valid TW script?
if (!line.startsWith("//"))
return false;
line = line.mid(2).trimmed();
if (!line.startsWith("TeXworksScript"))
return false;
// read the header lines
while (!s.atEnd()) {
line = s.readLine().trimmed();
// have we reached the end?
if (!line.startsWith("//"))
break;
line = line.mid(2).trimmed();
key = line.section(':', 0, 0).trimmed();
value = line.section(':', 1).trimmed();
if (key == "Title") m_Title = value;
else if (key == "Description") m_Description = value;
else if (key == "Author") m_Author = value;
else if (key == "Version") m_Version = value;
else if (key == "Script-Type") {
if (value == "hook") m_Type = ScriptHook;
else if (value == "standalone") m_Type = ScriptStandalone;
else m_Type = ScriptUnknown;
}
else if (key == "Hook") m_Hook = value;
}
return m_Type != ScriptUnknown && !m_Title.isEmpty();
}
static
QVariant convertValue(const QScriptValue& value)
{
if (value.isArray()) {
QVariantList lst;
int len = value.property("length").toUInt32();
for (int i = 0; i < len; ++i) {
QScriptValue p = value.property(i);
lst.append(convertValue(p));
}
return lst;
}
else
return value.toVariant();
}
bool JSScript::run(QObject *context, QVariant& result) const
{
QFile scriptFile(m_Filename);
if (!scriptFile.open(QIODevice::ReadOnly)) {
// handle error
return false;
}
QTextStream stream(&scriptFile);
QString contents = stream.readAll();
scriptFile.close();
QScriptEngine engine;
QScriptValue targetObject = engine.newQObject(context);
engine.globalObject().setProperty("target", targetObject);
QScriptValue appObject = engine.newQObject(TWApp::instance());
engine.globalObject().setProperty("app", appObject);
QScriptValue val;
#if QT_VERSION >= 0x040500
QSETTINGS_OBJECT(settings);
if (settings.value("scriptDebugger", false).toBool()) {
QScriptEngineDebugger debugger;
debugger.attachTo(&engine);
val = engine.evaluate(contents, m_Filename);
}
else {
val = engine.evaluate(contents, m_Filename);
}
#else
val = engine.evaluate(contents, m_Filename);
#endif
if (engine.hasUncaughtException()) {
result = engine.uncaughtException().toString();
return false;
}
else {
if (!val.isUndefined()) {
result = convertValue(val);
}
return true;
}
}
void TWScriptManager::clear()
{
foreach (TWScript *s, m_Scripts) {
delete s;
}
foreach (TWScript *s, m_Hooks) {
delete s;
}
m_Scripts.clear();
m_Hooks.clear();
}
bool TWScriptManager::addScript(TWScript* script)
{
QList& scriptList = script->getType() == TWScript::ScriptHook ? m_Hooks : m_Scripts;
if (scriptList.contains(script))
return false;
foreach(TWScript *s, scriptList) {
if (*s == *script)
return false;
}
scriptList.append(script);
return true;
}
int TWScriptManager::addScriptsInDirectory(const QDir& dir)
{
int num = 0;
QStringList filters;
filters << "*.lua" << "*.js";
foreach (QString filename, dir.entryList(filters)) {
if (filename.endsWith(".lua")) {
// if (addScript(new LuaScript(dir.absoluteFilePath(filename))))
// ++num;
}
else if (filename.endsWith(".js")) {
TWScript *s = new JSScript(dir.absoluteFilePath(filename));
if (s) {
if (s->parseHeader() && addScript(s))
++num;
else
delete s;
}
}
}
return num;
}
QList TWScriptManager::getHookScripts(const QString& hook) const
{
QList result;
foreach (TWScript *script, m_Hooks) {
if (script->getHook().compare(hook, Qt::CaseInsensitive) == 0) {
result.append(script);
}
}
return result;
}
TWScriptable::TWScriptable()
: QMainWindow(),
scriptsMenu(NULL),
staticScriptMenuItemCount(0)
{
}
void
TWScriptable::initScriptable(QMenu* theScriptsMenu,
QAction* manageScriptsAction,
QAction* updateScriptsAction,
QAction* showScriptsFolderAction)
{
scriptsMenu = theScriptsMenu;
connect(manageScriptsAction, SIGNAL(triggered()), this, SLOT(doManageScriptsDialog()));
connect(updateScriptsAction, SIGNAL(triggered()), TWApp::instance(), SLOT(updateScriptsList()));
connect(showScriptsFolderAction, SIGNAL(triggered()), TWApp::instance(), SLOT(showScriptsFolder()));
scriptMapper = new QSignalMapper(this);
connect(scriptMapper, SIGNAL(mapped(QObject*)), this, SLOT(runScript(QObject*)));
staticScriptMenuItemCount = scriptsMenu->actions().count();
updateScriptsMenu();
}
void
TWScriptable::updateScriptsMenu()
{
QList actions = scriptsMenu->actions();
for (int i = staticScriptMenuItemCount; i < actions.count(); ++i) {
scriptsMenu->removeAction(actions[i]);
delete actions[i];
}
foreach (TWScript *s, TWApp::instance()->getScriptManager().getScripts()) {
QAction *a = scriptsMenu->addAction(s->getTitle());
scriptMapper->setMapping(a, s);
connect(a, SIGNAL(triggered()), scriptMapper, SLOT(map()));
}
}
void
TWScriptable::runScript(QObject* script, TWScript::ScriptType scriptType)
{
TWScript * s = qobject_cast(script);
if (!s || s->getType() != scriptType)
return;
QVariant result;
bool success = s->run(this, result);
if (success) {
if (!result.isNull()) {
if (scriptType == TWScript::ScriptHook)
statusBar()->showMessage(tr("Script \"%1\": %2").arg(s->getTitle()).arg(result.toString()), kStatusMessageDuration);
else
QMessageBox::information(this, "Script result", result.toString(), QMessageBox::Ok, QMessageBox::Ok);
}
}
else {
if (result.isNull())
result = tr("unknown error");
statusBar()->showMessage(tr("Script \"%1\": %2").arg(s->getTitle()).arg(result.toString()));
}
}
void
TWScriptable::runHooks(const QString& hookName)
{
foreach (TWScript *s, TWApp::instance()->getScriptManager().getHookScripts(hookName)) {
runScript(s, TWScript::ScriptHook);
}
}
void
TWScriptable::doManageScriptsDialog()
{
}