/************************************************************************** * Copyright (C) 2006, 2007 by Michel Ludwig (michel.ludwig@kdemail.net) * ***************************************************************************/ /************************************************************************** * * * 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. * * * ***************************************************************************/ #include "kilejscript.h" /* * KJS uses a garbage collection mechanism. */ #include #include #include "kiledebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "kileconfig.h" #include "kileedit.h" #include "kileinfo.h" #include "kileversion.h" #include "kileviewmanager.h" #include "editorkeysequencemanager.h" // Modified declaration from // Acknowledgements go to: // Copyright (C) 1999 Harri Porten (porten@kde.org) // Copyright (C) 2001 Peter Kelly (pmk@post.com) class KJSCPUGuard { public: KJSCPUGuard() {} void start(unsigned int msec=5000, unsigned int i_msec=0); void stop(); private: void (*oldAlarmHandler)(int); static void alarmHandler(int); itimerval oldtv; }; // Modified implementation originating from // Acknowledgements go to: // Copyright (C) 1999-2001 Harri Porten (porten@kde.org) // Copyright (C) 2001,2003 Peter Kelly (pmk@post.com) // Copyright (C) 2001-2003 David Faure (faure@kde.org) void KJSCPUGuard::start(unsigned int ms, unsigned int i_ms) { oldAlarmHandler = signal(SIGVTALRM, alarmHandler); itimerval tv = { { i_ms / 1000, (i_ms % 1000) * 1000 }, { ms / 1000, (ms % 1000) * 1000 } }; setitimer(ITIMER_VIRTUAL, &tv, &oldtv); } void KJSCPUGuard::stop() { setitimer(ITIMER_VIRTUAL, &oldtv, 0L); signal(SIGVTALRM, oldAlarmHandler); } void KJSCPUGuard::alarmHandler(int) { KJS::ExecState::requestTerminate(); } namespace KJS { // Taken from // Acknowledgements go to: // Copyright (C) 1999-2003 Harri Porten (porten@kde.org) // Copyright (C) 2001-2003 David Faure (faure@kde.org) // Copyright (C) 2003 Apple Computer, Inc. UString::UString(const TQString &d) { unsigned int len = d.length(); UChar *dat = new UChar[len]; memcpy(dat, d.unicode(), len * sizeof(UChar)); rep = UString::Rep::create(dat, len); } TQString UString::qstring() const { return TQString((TQChar*) data(), size()); } TQConstString UString::qconststring() const { return TQConstString((TQChar*) data(), size()); } } namespace KJS { class KileJSObjectFunc; // forward declaration } class KileJSObject : public KJS::ObjectImp { friend class KJS::KileJSObjectFunc; public: KileJSObject(KJS::ExecState *exec, KileInfo *kileInfo); virtual ~KileJSObject(); // there are no non-functional properties // KJS::Value get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const; // KJS::Value getValueProperty(KJS::ExecState *exec, int token) const; // void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None); // void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr); virtual const KJS::ClassInfo *classInfo() const; enum { CurrentTextDocument, GetInputValue }; static const KJS::ClassInfo info; protected: KileInfo *m_kileInfo; }; namespace KJS { class KileTextDocumentJSObjectFunc; // forward declaration } class KileTextDocumentJSObject : public KJS::ObjectImp { friend class KJS::KileTextDocumentJSObjectFunc; public: KileTextDocumentJSObject(KJS::ExecState *exec, Kate::View* view, KileInfo *kileInfo); virtual ~KileTextDocumentJSObject(); // there are no non-functional properties // KJS::Value get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const; // KJS::Value getValueProperty(KJS::ExecState *exec, int token) const; // void put(KJS::ExecState *exec, const KJS::Identifier &propertyName, const KJS::Value& value, int attr = KJS::None); // void putValueProperty(KJS::ExecState *exec, int token, const KJS::Value& value, int attr); virtual const KJS::ClassInfo *classInfo() const; enum { InsertBullet, InsertText, NextBullet, PreviousBullet, CursorLeft, CursorRight, Up, Down, CursorLine, CursorColumn, SetCursorLine, SetCursorColumn, Backspace }; static const KJS::ClassInfo info; protected: Kate::View* view; KileInfo *m_kileInfo; }; #include "kilejscript.lut.h" /* * The number of supplied arguments is checked automatically. */ // Only functional properties: /* @begin KileJSObjectProtoTable 2 currentTextDocument KileJSObject::CurrentTextDocument DontDelete|Function 0 getInputValue KileJSObject::GetInputValue DontDelete|Function 2 @end */ // Non-functional properties go here: /* @begin KileJSObjectTable 0 @end */ DEFINE_PROTOTYPE("KileJSObject", KileJSObjectProto) IMPLEMENT_PROTOFUNC(KileJSObjectFunc) IMPLEMENT_PROTOTYPE(KileJSObjectProto, KileJSObjectFunc) const KJS::ClassInfo KileJSObject::info = {"KileJSObject", 0, &KileJSObjectTable, 0}; KileJSObject::KileJSObject(KJS::ExecState *exec, KileInfo *kileInfo) : ObjectImp(KileJSObjectProto::self(exec)), m_kileInfo(kileInfo) { } KileJSObject::~KileJSObject() { } const KJS::ClassInfo* KileJSObject::classInfo() const { return &info; } /* KJS::Value KileJSObject::get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const { return KJS::lookupGetValue(exec, propertyName, &KileJSObjectTable, this); } */ /* KJS::Value KileJSObject::getValueProperty(KJS::ExecState *exec, int token) const { if (!m_kileInfo) { return KJS::Undefined(); } switch (token) { } return KJS::Undefined(); } */ KJS::Value KJS::KileJSObjectFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args) { KJS_CHECK_THIS(KileJSObject, thisObj); // KileJSObject *obj = static_cast(thisObj.imp())->doc; KileInfo* kileInfo = static_cast(thisObj.imp())->m_kileInfo; if(!kileInfo) { return KJS::Undefined(); } TQString caption, label, value; KileTextDocumentJSObject *kileViewJSObject; Kate::View *kateView; switch (id) { case KileJSObject::CurrentTextDocument: kateView = kileInfo->viewManager()->currentTextView(); if(kateView == 0L) { return KJS::Null(); } else { kileViewJSObject = new KileTextDocumentJSObject(exec, kateView, kileInfo); return KJS::Object(kileViewJSObject); } break; case KileJSObject::GetInputValue: caption = args[0].toString(exec).qstring(); label = args[1].toString(exec).qstring(); if(caption.isEmpty()) { caption = i18n("Enter Value"); } if(label.isEmpty()) { label = i18n("Please enter a value"); } value = KInputDialog::getText(caption, label, TQString(), 0, 0L); return KJS::String(value); break; } return KJS::Undefined(); } // Only functional properties: /* @begin KileTextDocumentJSObjectProtoTable 13 # insertText KileTextDocumentJSObject::InsertText DontDelete|Function 1 insertBullet KileTextDocumentJSObject::InsertBullet DontDelete|Function 0 nextBullet KileTextDocumentJSObject::NextBullet DontDelete|Function 0 previousBullet KileTextDocumentJSObject::PreviousBullet DontDelete|Function 0 cursorLeft KileTextDocumentJSObject::CursorLeft DontDelete|Function 0 cursorRight KileTextDocumentJSObject::CursorRight DontDelete|Function 0 up KileTextDocumentJSObject::Up DontDelete|Function 0 down KileTextDocumentJSObject::Down DontDelete|Function 0 cursorLine KileTextDocumentJSObject::CursorLine DontDelete|Function 0 cursorColumn KileTextDocumentJSObject::CursorColumn DontDelete|Function 0 setCursorLine KileTextDocumentJSObject::SetCursorLine DontDelete|Function 1 setCursorColumn KileTextDocumentJSObject::SetCursorColumn DontDelete|Function 1 backspace KileTextDocumentJSObject::Backspace DontDelete|Function 0 # @end */ // Non-functional properties go here: /* @begin KileTextDocumentJSObjectTable 0 @end */ DEFINE_PROTOTYPE("KileTextDocumentJSObject", KileTextDocumentJSObjectProto) IMPLEMENT_PROTOFUNC(KileTextDocumentJSObjectFunc) IMPLEMENT_PROTOTYPE(KileTextDocumentJSObjectProto, KileTextDocumentJSObjectFunc) KileTextDocumentJSObject::KileTextDocumentJSObject(KJS::ExecState *exec, Kate::View* view, KileInfo *kileInfo) : ObjectImp(KileTextDocumentJSObjectProto::self(exec)), view(view), m_kileInfo(kileInfo) { } KileTextDocumentJSObject::~KileTextDocumentJSObject() { } /* KJS::Value KileTextDocumentJSObject::get(KJS::ExecState *exec, const KJS::Identifier &propertyName) const { return KJS::lookupGetValue(exec, propertyName, &KileTextDocumentJSObjectTable, this); } */ /* KJS::Value KileTextDocumentJSObject::getValueProperty(KJS::ExecState *exec, int token) const { return KJS::Undefined (); } */ KJS::Value KJS::KileTextDocumentJSObjectFunc::call(KJS::ExecState *exec, KJS::Object &thisObj, const KJS::List &args) { KJS_CHECK_THIS(KileTextDocumentJSObject, thisObj); // KileJSObject *obj = static_cast(thisObj.imp())->doc; KileInfo* kileInfo = static_cast(thisObj.imp())->m_kileInfo; Kate::View* view = ((KileTextDocumentJSObject*)(thisObj.imp()))->view; uint col, line; switch (id) { case KileTextDocumentJSObject::InsertText: view->insertText(args[0].toString(exec).qstring()); break; case KileTextDocumentJSObject::InsertBullet: kileInfo->editorExtension()->insertBullet(view); break; case KileTextDocumentJSObject::NextBullet: kileInfo->editorExtension()->nextBullet(view); break; case KileTextDocumentJSObject::PreviousBullet: kileInfo->editorExtension()->prevBullet(view); break; case KileTextDocumentJSObject::CursorRight: view->cursorRight(); break; case KileTextDocumentJSObject::CursorLeft: view->cursorLeft(); break; case KileTextDocumentJSObject::Up: view->up(); break; case KileTextDocumentJSObject::Down: view->down(); break; case KileTextDocumentJSObject::CursorLine: view->cursorPositionReal(&line, &col); return KJS::Number(line); break; case KileTextDocumentJSObject::CursorColumn: view->cursorPositionReal(&line, &col); return KJS::Number(col); break; case KileTextDocumentJSObject::SetCursorLine: view->cursorPositionReal(&line, &col); view->setCursorPositionReal(args[0].toUInt32(exec), col); break; case KileTextDocumentJSObject::SetCursorColumn: view->cursorPositionReal(&line, &col); view->setCursorPositionReal(line, args[0].toUInt32(exec)); break; case KileTextDocumentJSObject::Backspace: view->backspace(); break; } return KJS::Undefined(); } const KJS::ClassInfo* KileTextDocumentJSObject::classInfo() const { return &info; } const KJS::ClassInfo KileTextDocumentJSObject::info = {"KileTextDocumentJSObject", 0, &KileTextDocumentJSObjectTable, 0}; namespace KileJScript { ////////////////////////////// JScript ////////////////////////////// /* The IDs of the scripts are used to maintain correct bindings with KAction objects, i.e. for example, we * want to make sure the action script_execution_0 always refers to same script (the script with id 0 !), even * after reloading all the scripts. */ JScript::JScript(unsigned int id, const TQString& file) : m_id(id), m_file(file), m_action(NULL) { m_name = KGlobal::dirs()->relativeLocation("appdata", file); if(m_name.startsWith("scripts")) { m_name = m_name.mid(8); // remove "scripts" + path separator } if(m_name.endsWith(".js")) { // remove the extension m_name = m_name.left(m_name.length() - 3); } } TQString JScript::getName() const { return m_name; } TQString JScript::getCode() const { TQFile qFile(m_file); if(qFile.open(IO_ReadOnly)) { TQTextStream inputStream(&qFile); // inputStream.setEncoding(TQTextStream::UnicodeUTF8); TQString code = inputStream.read(); qFile.close(); return code; } else { return TQString(); } } TQString JScript::getFileName() const { return m_file; } unsigned int JScript::getID() const { return m_id; } void JScript::setID(unsigned int id) { m_id = id; } void JScript::setActionObject(KAction* action) { m_action = action; } const KAction* JScript::getActionObject() const { return m_action; } KAction* JScript::getActionObject() { return m_action; } void JScript::setKeySequence(const TQString& str) { m_keySequence = str; } TQString JScript::getKeySequence() const { return m_keySequence; } ////////////////////////////// JScriptEnvironment ////////////////////////////// JScriptEnvironment::JScriptEnvironment(KileInfo *kileInfo) : m_interpreter(new KJS::Interpreter()), m_kileJSObject(new KJS::Object(new KileJSObject(m_interpreter->globalExec(), kileInfo))), m_kileInfo(kileInfo) { // no garbage collection because of an external reference m_interpreter->globalObject().put(m_interpreter->globalExec(), "kile", *m_kileJSObject, KJS::DontDelete|KJS::Internal); } JScriptEnvironment::~JScriptEnvironment() { //kileJSObject->imp() will be deleted during the next garbage cleanup delete m_kileJSObject; delete m_interpreter; } void JScriptEnvironment::execute(const TQString& s) { bool useGuard = KileConfig::timeLimitEnabled(); uint timeLimit = (uint)KileConfig::timeLimit(); KJSCPUGuard guard; if(useGuard) { guard.start(timeLimit*1000); } Completion completion = m_interpreter->evaluate(s); if(useGuard) { guard.stop(); } if(completion.complType() == Throw) { Value value = completion.value(); if(value.type() == ObjectType) { Object o = Object::dynamicCast(value); if(o.isValid()) { Value lineValue = o.get(m_interpreter->globalExec(), "line"); if(lineValue.type() == NumberType) { KMessageBox::sorry(0L, i18n("The following exception has occurred at line %1 during execution of the script:\n%2").arg(lineValue.toInt32(m_interpreter->globalExec())).arg(value.toString(m_interpreter->globalExec()).qstring()), i18n("Exception")); return; } } } KMessageBox::sorry(0L, i18n("The following exception has occurred during execution of the script:\n%1").arg(value.toString(m_interpreter->globalExec()).qstring()), i18n("Exception")); } } ////////////////////////////// Manager ////////////////////////////// Manager::Manager(KileInfo *kileInfo, KConfig *config, KActionCollection *actionCollection, TQObject *parent, const char *name) : TQObject(parent, name), m_jScriptDirWatch(NULL), m_kileInfo(kileInfo), m_config(config), m_actionCollection(actionCollection) { // create a local scripts directory if it doesn't exist yet m_localJScriptDir = locateLocal("appdata", "scripts/", true); m_jScriptDirWatch = new KDirWatch(this, "KileJScript::Manager::JScriptDirWatch"); connect(m_jScriptDirWatch, TQT_SIGNAL(dirty(const TQString&)), this, TQT_SLOT(scanJScriptDirectories())); connect(m_jScriptDirWatch, TQT_SIGNAL(created(const TQString&)), this, TQT_SLOT(scanJScriptDirectories())); connect(m_jScriptDirWatch, TQT_SIGNAL(deleted(const TQString&)), this, TQT_SLOT(scanJScriptDirectories())); m_jScriptDirWatch->startScan(); } Manager::~Manager() { delete m_jScriptDirWatch; //still need to delete the scripts for(TQValueList::iterator it = m_jScriptList.begin(); it != m_jScriptList.end(); ++it) { delete *it; } } void Manager::executeJScript(const JScript *script) { JScriptEnvironment env(m_kileInfo); // demonstrate repainting bug: /*KMessageBox::information(0L, "works!!!"); m_kileInfo->viewManager()->currentView()->down(); m_kileInfo->viewManager()->currentView()->down(); m_kileInfo->viewManager()->currentView()->down(); m_kileInfo->viewManager()->currentView()->down(); m_kileInfo->viewManager()->currentView()->down();*/ TQString code = script->getCode(); TQRegExp endOfLineExp("(\r\n)|\n|\r"); int i = code.find(endOfLineExp); TQString firstLine = (i >= 0 ? code.left(i) : code); TQRegExp requiredVersionTagExp("(kile-version:\\s*)(\\d+\\.\\d+(.\\d+)?)"); if(requiredVersionTagExp.search(firstLine) != -1) { TQString requiredKileVersion = requiredVersionTagExp.cap(2); if(compareVersionStrings(requiredKileVersion, kileFullVersion) > 0) { KMessageBox::sorry(0L, i18n("Version %1 of Kile is at least required to execute the script \"%2\". The execution has been aborted.").arg(requiredKileVersion).arg(script->getName()), i18n("Version Error")); return; } } env.execute(code); } void Manager::executeJScript(unsigned int id) { TQMap::iterator i = m_idScriptMap.find(id); if(i != m_idScriptMap.end()) { executeJScript(*i); } } const JScript* Manager::getScript(unsigned int id) { TQMap::iterator i = m_idScriptMap.find(id); return ((i != m_idScriptMap.end()) ? (*i) : NULL); } void Manager::scanJScriptDirectories() { if(!KileConfig::scriptingEnabled()) { return; } deleteJScripts(); populateDirWatch(); m_config->setGroup("Scripts"); TQValueList idList = readUnsignedIntListEntry("IDs"); unsigned int maxID = 0; TQMap pathIDMap; TQMap takenIDMap; for(TQValueList::iterator i = idList.begin(); i != idList.end(); ++i) { TQString fileName = m_config->readPathEntry("Script" + TQString::number(*i)); if(!fileName.isEmpty()) { unsigned int id = *i; pathIDMap[fileName] = id; takenIDMap[id] = true; maxID = TQMAX(maxID, id); } } m_config->setGroup(TQString()); TQStringList scriptFileNamesList = KGlobal::dirs()->findAllResources("appdata", "scripts/*.js", true, true); for(TQStringList::iterator i = scriptFileNamesList.begin(); i != scriptFileNamesList.end(); ++i) { registerScript(*i, pathIDMap, takenIDMap, maxID); } //rewrite the IDs that are currently in use writeIDs(); m_actionCollection->readShortcutSettings("Shortcuts"); emit jScriptsChanged(); } void Manager::deleteJScripts() { TQValueList scriptList = m_jScriptList; m_jScriptList.clear(); // pretend that there are no scripts left TQStringList keySequenceList; for(TQValueList::iterator it = scriptList.begin(); it != scriptList.end(); ++it) { keySequenceList.push_back((*it)->getKeySequence()); } m_idScriptMap.clear(); m_kileInfo->editorKeySequenceManager()->removeKeySequence(keySequenceList); for(TQValueList::iterator it = scriptList.begin(); it != scriptList.end(); ++it) { KAction *action = (*it)->getActionObject(); if(action) { action->unplugAll(); delete action; } delete *it; } emit jScriptsChanged(); } TQValueList Manager::getJScripts() { return m_jScriptList; } void Manager::registerScript(const TQString& fileName, TQMap& pathIDMap, TQMap& takenIDMap, unsigned int &maxID) { unsigned int id; TQMap::iterator it = pathIDMap.find(fileName); if(it != pathIDMap.end()) { id = *it; } else { id = findFreeID(takenIDMap, maxID); pathIDMap[fileName] = id; takenIDMap[id] = true; maxID = TQMAX(maxID, id); } JScript* script = new JScript(id, fileName); m_jScriptList.push_back(script); m_idScriptMap[id] = script; // start with setting up the key sequence m_config->setGroup("Scripts"); TQString editorKeySequence = m_config->readEntry("Script" + TQString::number(id) + "KeySequence"); m_config->setGroup(TQString()); if(!editorKeySequence.isEmpty()) { script->setKeySequence(editorKeySequence); m_kileInfo->editorKeySequenceManager()->addAction(editorKeySequence, new KileEditorKeySequence::ExecuteJScriptAction(script, this)); } // now set up a regular action object ScriptExecutionAction *action = new ScriptExecutionAction(id, this, m_actionCollection); script->setActionObject(action); } void Manager::writeConfig() { // don't delete the key sequence settings if scripting has been disabled if(!KileConfig::scriptingEnabled()) { return; } m_config->deleteGroup("Scripts"); writeIDs(); // write the key sequences m_config->setGroup("Scripts"); for(TQValueList::iterator i = m_jScriptList.begin(); i != m_jScriptList.end(); ++i) { m_config->writeEntry("Script" + TQString::number((*i)->getID()) + "KeySequence", (*i)->getKeySequence()); } m_config->setGroup(TQString()); } void Manager::setEditorKeySequence(JScript* script, const TQString& keySequence) { if(keySequence.isEmpty()) { return; } if(script) { TQString oldSequence = script->getKeySequence(); if(oldSequence == keySequence) { return; } m_kileInfo->editorKeySequenceManager()->removeKeySequence(oldSequence); script->setKeySequence(keySequence); m_kileInfo->editorKeySequenceManager()->addAction(keySequence, new KileEditorKeySequence::ExecuteJScriptAction(script, this)); writeConfig(); } } void Manager::removeEditorKeySequence(JScript* script) { if(script) { TQString keySequence = script->getKeySequence(); if(keySequence.isEmpty()) { return; } script->setKeySequence(TQString()); m_kileInfo->editorKeySequenceManager()->removeKeySequence(keySequence); writeConfig(); } } void Manager::populateDirWatch() { TQStringList jScriptDirectories = KGlobal::dirs()->findDirs("appdata", "scripts"); for(TQStringList::iterator i = jScriptDirectories.begin(); i != jScriptDirectories.end(); ++i) { // FIXME: future KDE versions could support the recursive // watching of directories out of the box. addDirectoryToDirWatch(*i); } //we do not remove the directories that were once added as this apparently causes some strange //bugs (on KDE 3.5.x) } TQString Manager::getLocalJScriptDirectory() const { return m_localJScriptDir; } void Manager::readConfig() { deleteJScripts(); scanJScriptDirectories(); } TQValueList Manager::readUnsignedIntListEntry(const TQString& key) { TQValueList toReturn; TQStringList stringList = m_config->readListEntry(key); for(TQStringList::iterator i = stringList.begin(); i != stringList.end(); ++i) { toReturn.push_back((*i).toUInt()); } return toReturn; } void Manager::writeEntry(const TQString& key, const TQValueList& l) { TQStringList stringList; for(TQValueList::const_iterator i = l.begin(); i != l.end(); ++i) { stringList.push_back(TQString::number(*i)); } m_config->writeEntry(key, stringList); } unsigned int Manager::findFreeID(const TQMap& takenIDMap, unsigned int maxID) { if(takenIDMap.size() == 0) { return 0; } // maxID should have a real meaning now for(unsigned int i = 0; i < maxID; ++i) { if(takenIDMap.find(i) == takenIDMap.end()) { return i; } } return (maxID + 1); } void Manager::writeIDs() { m_config->setGroup("Scripts"); //delete old entries TQValueList idList = readUnsignedIntListEntry("IDs"); for(TQValueList::iterator i = idList.begin(); i != idList.end(); ++i) { m_config->deleteEntry("Script" + TQString::number(*i)); } //write new ones idList.clear(); for(TQMap::iterator i = m_idScriptMap.begin(); i != m_idScriptMap.end(); ++i) { unsigned int id = i.key(); idList.push_back(id); m_config->writePathEntry("Script" + TQString::number(id), (*i)->getFileName()); } writeEntry("IDs", idList); m_config->setGroup(TQString()); } void Manager::addDirectoryToDirWatch(const TQString& dir) { //FIXME: no recursive watching and no watching of files as it isn't implemented // yet if(!m_jScriptDirWatch->contains(dir)) { m_jScriptDirWatch->addDir(dir, false, false); } TQDir qDir(dir); TQStringList list = qDir.entryList(TQDir::Dirs); for(TQStringList::iterator i = list.begin(); i != list.end(); ++i) { TQString subdir = *i; if(subdir != "." && subdir != "..") { addDirectoryToDirWatch(qDir.filePath(subdir)); } } } ////////////////////////////// ScriptExecutionAction ////////////////////////////// ScriptExecutionAction::ScriptExecutionAction(unsigned int id, KileJScript::Manager *manager, KActionCollection* parent) : KAction(TQString(), KShortcut(), NULL, NULL, parent, TQString("script_execution_" + TQString::number(id)).ascii()), m_manager(manager), m_id(id) { const KileJScript::JScript *script = m_manager->getScript(m_id); Q_ASSERT(script); setText(i18n("Execution of %1").arg(script->getName())); connect(this, TQT_SIGNAL(activated()), this, TQT_SLOT(executeScript())); } ScriptExecutionAction::~ScriptExecutionAction() { } void ScriptExecutionAction::executeScript() { m_manager->executeJScript(m_id); } }; #include "kilejscript.moc"