00001
00002
00003
00004
00005
00006
00007 #include "scriptercore.h"
00008
00009 #include <QGlobalStatic>
00010 #include <QWidget>
00011 #include <QString>
00012 #include <QApplication>
00013 #include <QMessageBox>
00014 #include <QTextCodec>
00015 #include <QByteArray>
00016 #include <QPixmap>
00017 #include <cstdlib>
00018
00019 #include "runscriptdialog.h"
00020 #include "helpbrowser.h"
00021 #include "propertiespalette.h"
00022 #include "pagepalette.h"
00023 #include "layers.h"
00024 #include "outlinepalette.h"
00025 #include "menumanager.h"
00026 #include "pconsole.h"
00027 #include "scraction.h"
00028 #include "scribuscore.h"
00029 #include "scpaths.h"
00030 #include "selection.h"
00031 #include "prefsfile.h"
00032 #include "prefscontext.h"
00033 #include "prefstable.h"
00034 #include "prefsmanager.h"
00035
00036 ScripterCore::ScripterCore(QWidget* parent)
00037 {
00038 pcon = new PythonConsole(parent);
00039 scrScripterActions.clear();
00040 scrRecentScriptActions.clear();
00041 returnString = "init";
00042
00043 scrScripterActions.insert("scripterExecuteScript", new ScrAction(QObject::tr("&Execute Script..."), QKeySequence(), this));
00044 scrScripterActions.insert("scripterShowConsole", new ScrAction(QObject::tr("Show &Console"), QKeySequence(), this));
00045 scrScripterActions.insert("scripterAboutScript", new ScrAction(QObject::tr("&About Script..."), QKeySequence(), this));
00046
00047 scrScripterActions["scripterShowConsole"]->setToggleAction(true);
00048
00049 QObject::connect( scrScripterActions["scripterExecuteScript"], SIGNAL(triggered()) , this, SLOT(runScriptDialog()) );
00050 QObject::connect( scrScripterActions["scripterShowConsole"], SIGNAL(toggled(bool)) , this, SLOT(slotInteractiveScript(bool)) );
00051 QObject::connect( scrScripterActions["scripterAboutScript"], SIGNAL(triggered()) , this, SLOT(aboutScript()) );
00052
00053 SavedRecentScripts.clear();
00054 ReadPlugPrefs();
00055
00056 QObject::connect(pcon, SIGNAL(runCommand()), this, SLOT(slotExecute()));
00057 QObject::connect(pcon, SIGNAL(paletteShown(bool)), this, SLOT(slotInteractiveScript(bool)));
00058 }
00059
00060 ScripterCore::~ScripterCore()
00061 {
00062 SavePlugPrefs();
00063 delete pcon;
00064 }
00065
00066 void ScripterCore::addToMainWindowMenu(ScribusMainWindow *mw)
00067 {
00068 menuMgr = mw->scrMenuMgr;
00069 menuMgr->createMenu("Scripter", QObject::tr("&Script"));
00070 menuMgr->addMenuToMenuBarBefore("Scripter","Windows");
00071 menuMgr->createMenu("ScribusScripts", QObject::tr("&Scribus Scripts"), "Scripter");
00072 menuMgr->addMenuItem(scrScripterActions["scripterExecuteScript"], "Scripter");
00073 menuMgr->createMenu("RecentScripts", QObject::tr("&Recent Scripts"), "Scripter");
00074 menuMgr->addMenuSeparator("Scripter");
00075 menuMgr->addMenuItem(scrScripterActions["scripterShowConsole"], "Scripter");
00076 menuMgr->addMenuItem(scrScripterActions["scripterAboutScript"], "Scripter");
00077 buildScribusScriptsMenu();
00078 buildRecentScriptsMenu();
00079 }
00080
00081
00082 void ScripterCore::buildScribusScriptsMenu()
00083 {
00084 QString pfad = ScPaths::instance().scriptDir();
00085 QString pfad2;
00086 pfad2 = QDir::convertSeparators(pfad);
00087 QDir ds(pfad2, "*.py", QDir::Name | QDir::IgnoreCase, QDir::Files | QDir::NoSymLinks);
00088 if ((ds.exists()) && (ds.count() != 0))
00089 {
00090 for (uint dc = 0; dc < ds.count(); ++dc)
00091 {
00092 QFileInfo fs(ds[dc]);
00093 QString strippedName=fs.baseName();
00094 scrScripterActions.insert(strippedName, new ScrAction( ScrAction::RecentScript, QPixmap(), QPixmap(), strippedName, QKeySequence(), this));
00095 connect( scrScripterActions[strippedName], SIGNAL(triggeredData(QString)), this, SLOT(StdScript(QString)) );
00096 menuMgr->addMenuItem(scrScripterActions[strippedName], "ScribusScripts");
00097 }
00098 }
00099
00100
00101 }
00102
00103 void ScripterCore::rebuildRecentScriptsMenu()
00104 {
00105 for( QMap<QString, QPointer<ScrAction> >::Iterator it = scrRecentScriptActions.begin(); it!=scrRecentScriptActions.end(); ++it )
00106 menuMgr->removeMenuItem((*it), "RecentScripts");
00107
00108 scrRecentScriptActions.clear();
00109 uint max = qMin(PrefsManager::instance()->appPrefs.RecentDCount, RecentScripts.count());
00110 for (uint m = 0; m < max; ++m)
00111 {
00112 QString strippedName=RecentScripts[m];
00113 strippedName.remove(QDir::separator());
00114 scrRecentScriptActions.insert(strippedName, new ScrAction( ScrAction::RecentScript, QPixmap(), QPixmap(), RecentScripts[m], QKeySequence(), this));
00115 connect( scrRecentScriptActions[strippedName], SIGNAL(triggeredData(QString)), this, SLOT(RecentScript(QString)) );
00116 menuMgr->addMenuItem(scrRecentScriptActions[strippedName], "RecentScripts");
00117 }
00118 }
00119
00120 void ScripterCore::buildRecentScriptsMenu()
00121 {
00122 RecentScripts = SavedRecentScripts;
00123 scrRecentScriptActions.clear();
00124 if (SavedRecentScripts.count() != 0)
00125 {
00126 uint max = qMin(PrefsManager::instance()->appPrefs.RecentDCount, SavedRecentScripts.count());
00127 for (uint m = 0; m < max; ++m)
00128 {
00129 QFileInfo fd(SavedRecentScripts[m]);
00130 if (fd.exists())
00131 {
00132 QString strippedName=SavedRecentScripts[m];
00133 strippedName.remove(QDir::separator());
00134 scrRecentScriptActions.insert(strippedName, new ScrAction( ScrAction::RecentScript, QPixmap(), QPixmap(), SavedRecentScripts[m], QKeySequence(), this));
00135 connect( scrRecentScriptActions[strippedName], SIGNAL(triggeredData(QString)), this, SLOT(RecentScript(QString)) );
00136 menuMgr->addMenuItem(scrRecentScriptActions[strippedName], "RecentScripts");
00137 }
00138 }
00139 }
00140 }
00141
00142 void ScripterCore::FinishScriptRun()
00143 {
00144 ScribusMainWindow* ScMW=ScCore->primaryMainWindow();
00145 if (ScMW->HaveDoc)
00146 {
00147 ScMW->propertiesPalette->setDoc(ScMW->doc);
00148 ScMW->layerPalette->setDoc(ScMW->doc);
00149 ScMW->outlinePalette->setDoc(ScMW->doc);
00150 ScMW->outlinePalette->BuildTree();
00151 ScMW->pagePalette->setView(ScMW->view);
00152 ScMW->pagePalette->Rebuild();
00153 ScMW->doc->RePos = true;
00154
00155
00156
00157
00158
00159
00160
00161
00162
00163
00164
00165
00166
00167
00168
00169
00170
00171
00172
00173
00174
00175
00176
00177
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189 ScMW->doc->RePos = false;
00190 if (ScMW->doc->m_Selection->count() != 0)
00191 {
00192 ScMW->doc->m_Selection->itemAt(0)->emitAllToGUI();
00193 ScMW->HaveNewSel(ScMW->doc->m_Selection->itemAt(0)->itemType());
00194 }
00195 else
00196 ScMW->HaveNewSel(-1);
00197 ScMW->view->DrawNew();
00198
00199
00200 ScMW->HaveNewDoc();
00201 }
00202 }
00203
00204 void ScripterCore::runScriptDialog()
00205 {
00206 QString fileName;
00207 QString curDirPath = QDir::currentPath();
00208 RunScriptDialog dia( ScCore->primaryMainWindow(), m_enableExtPython );
00209 if (dia.exec())
00210 {
00211 fileName = dia.selectedFile();
00212 slotRunScriptFile(fileName, dia.extensionRequested());
00213
00214 if (RecentScripts.indexOf(fileName) == -1)
00215 RecentScripts.prepend(fileName);
00216 else
00217 {
00218 RecentScripts.removeAll(fileName);
00219 RecentScripts.prepend(fileName);
00220 }
00221 rebuildRecentScriptsMenu();
00222 }
00223 QDir::setCurrent(curDirPath);
00224 FinishScriptRun();
00225 }
00226
00227 void ScripterCore::StdScript(QString basefilename)
00228 {
00229 QString pfad = ScPaths::instance().scriptDir();
00230 QString pfad2;
00231 pfad2 = QDir::convertSeparators(pfad);
00232 QString fn = pfad2+basefilename+".py";
00233 QFileInfo fd(fn);
00234 if (!fd.exists())
00235 return;
00236 slotRunScriptFile(fn);
00237 FinishScriptRun();
00238 }
00239
00240 void ScripterCore::RecentScript(QString fn)
00241 {
00242 QFileInfo fd(fn);
00243 if (!fd.exists())
00244 {
00245 RecentScripts.removeAll(fn);
00246 rebuildRecentScriptsMenu();
00247 return;
00248 }
00249 slotRunScriptFile(fn);
00250 FinishScriptRun();
00251 }
00252
00253 void ScripterCore::slotRunScriptFile(QString fileName, bool inMainInterpreter)
00254 {
00255 PyThreadState *stateo = NULL;
00256 PyThreadState *state = NULL;
00257 QFileInfo fi(fileName);
00258 QByteArray na = fi.fileName().toLatin1();
00259
00260 if (!inMainInterpreter)
00261 {
00262 ScCore->primaryMainWindow()->ScriptRunning = true;
00263 qApp->changeOverrideCursor(QCursor(Qt::WaitCursor));
00264
00265
00266 stateo = PyEval_SaveThread();
00267 state = Py_NewInterpreter();
00268
00269 QDir::setCurrent(fi.absolutePath());
00270
00271 initscribus(ScCore->primaryMainWindow());
00272 }
00273
00274 char* comm[2];
00275 comm[0] = na.data();
00276
00277
00278 if (inMainInterpreter)
00279 comm[1] = const_cast<char*>("ext");
00280 else
00281 comm[1] = const_cast<char*>("sub");
00282 PySys_SetArgv(2, comm);
00283
00284 PyObject* m = PyImport_AddModule((char*)"__main__");
00285 if (m == NULL)
00286 qDebug("Failed to get __main__ - aborting script");
00287 else
00288 {
00289
00290 PyObject* globals = PyModule_GetDict(m);
00291
00292
00293 QString cm = QString("import sys\n");
00294 cm += QString("import cStringIO\n");
00295
00296
00297
00298 cm += QString("import os\nos.environ['PAGER'] = '/bin/false'\n");
00299 cm += QString("sys.path[0] = \"%1\"\n").arg(fi.absolutePath());
00300
00301
00302 cm += QString("sys.stdin = cStringIO.StringIO()\n");
00303 cm += QString("try:\n");
00304 cm += QString(" execfile(\"%1\")\n").arg(fileName);
00305 cm += QString("except SystemExit:\n");
00306 cm += QString(" pass\n");
00307
00308
00309 cm += QString("except:\n");
00310 cm += QString(" import traceback\n");
00311 cm += QString(" import scribus\n");
00312 cm += QString(" scribus._f=cStringIO.StringIO()\n");
00313 cm += QString(" traceback.print_exc(file=scribus._f)\n");
00314 cm += QString(" _errorMsg = scribus._f.getvalue()\n");
00315 cm += QString(" del(scribus._f)\n");
00316
00317
00318 cm += QString(" raise\n");
00319
00320 QByteArray cmd = cm.toLatin1();
00321
00322
00323
00324 PyObject* result = PyRun_String(cmd.data(), Py_file_input, globals, globals);
00325
00326
00327 if (result == NULL)
00328 {
00329
00330 PyErr_Clear();
00331 PyObject* errorMsgPyStr = PyMapping_GetItemString(globals, (char*)"_errorMsg");
00332 if (errorMsgPyStr == NULL)
00333 {
00334
00335
00336 qDebug("Error retrieving error message content after script exception!");
00337 qDebug("Exception was:");
00338 PyErr_Print();
00339 }
00340 else
00341 {
00342 QString errorMsg = PyString_AsString(errorMsgPyStr);
00343
00344 QClipboard *cp = QApplication::clipboard();
00345 cp->setText(errorMsg);
00346 ScCore->closeSplash();
00347 QMessageBox::warning(ScCore->primaryMainWindow(),
00348 tr("Script error"),
00349 "<qt><p>"
00350 + tr("If you are running an official script report it at <a href=\"http://bugs.scribus.net\">bugs.scribus.net</a> please.")
00351 + "</p><pre>" +errorMsg + "</pre><p>"
00352 + tr("This message is in your clipboard too. Use Ctrl+V to paste it into bug tracker.")
00353 + "</p></qt>");
00354 }
00355 }
00356
00357 Py_XDECREF(result);
00358 }
00359 if (!inMainInterpreter)
00360 {
00361 Py_EndInterpreter(state);
00362 PyEval_RestoreThread(stateo);
00363
00364 }
00365 ScCore->primaryMainWindow()->ScriptRunning = false;
00366 }
00367
00368 void ScripterCore::slotRunScript(const QString Script)
00369 {
00370 ScCore->primaryMainWindow()->ScriptRunning = true;
00371 inValue = Script;
00372 QString cm;
00373 cm = "# -*- coding: utf8 -*- \n";
00374 if (PyThreadState_Get() != NULL)
00375 {
00376 initscribus(ScCore->primaryMainWindow());
00377
00378
00379
00380
00381
00382
00383 cm += ("import cStringIO\n"
00384 "scribus._bu = cStringIO.StringIO()\n"
00385 "sys.stdout = scribus._bu\n"
00386 "sys.stderr = scribus._bu\n"
00387 "sys.argv = ['scribus', 'ext']\n"
00388 "for i in scribus.getval().splitlines():\n"
00389 " scribus._ia.push(i)\n"
00390 "scribus.retval(scribus._bu.getvalue())\n"
00391 "sys.stdout = sys.__stdout__\n"
00392 "sys.stderr = sys.__stderr__\n");
00393 }
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407 PyObject* m = PyImport_AddModule((char*)"__main__");
00408 if (m == NULL)
00409 qDebug("Failed to get __main__ - aborting script");
00410 else
00411 {
00412 PyObject* globals = PyModule_GetDict(m);
00413 PyObject* result = PyRun_String(cm.toUtf8().data(), Py_file_input, globals, globals);
00414 if (result == NULL)
00415 {
00416 PyErr_Print();
00417 QMessageBox::warning(ScCore->primaryMainWindow(), tr("Script error"),
00418 "<qt>" + tr("There was an internal error while trying the "
00419 "command you entered. Details were printed to "
00420 "stderr. ") + "</qt>");
00421 }
00422 }
00423 ScCore->primaryMainWindow()->ScriptRunning = false;
00424 }
00425
00426 void ScripterCore::slotInteractiveScript(bool visible)
00427 {
00428 QObject::disconnect( scrScripterActions["scripterShowConsole"], SIGNAL(toggled(bool)) , this, SLOT(slotInteractiveScript(bool)) );
00429
00430 scrScripterActions["scripterShowConsole"]->setChecked(visible);
00431 pcon->setFonts();
00432 pcon->setShown(visible);
00433
00434 QObject::connect( scrScripterActions["scripterShowConsole"], SIGNAL(toggled(bool)) , this, SLOT(slotInteractiveScript(bool)) );
00435 }
00436
00437 void ScripterCore::slotExecute()
00438 {
00439 slotRunScript(pcon->command());
00440 pcon->outputEdit->append(returnString);
00441 pcon->commandEdit->ensureCursorVisible();
00442 FinishScriptRun();
00443 }
00444
00445 void ScripterCore::ReadPlugPrefs()
00446 {
00447 PrefsContext* prefs = PrefsManager::instance()->prefsFile->getPluginContext("scriptplugin");
00448 if (!prefs)
00449 {
00450 qDebug("scriptplugin: Unable to load prefs");
00451 return;
00452 }
00453 PrefsTable* prefRecentScripts = prefs->getTable("recentscripts");
00454 if (!prefRecentScripts)
00455 {
00456 qDebug("scriptplugin: Unable to get recent scripts");
00457 return;
00458 }
00459
00460 for (int i = 0; i < prefRecentScripts->getRowCount(); i++)
00461 SavedRecentScripts.append(prefRecentScripts->get(i,0));
00462
00463 m_enableExtPython = prefs->getBool("extensionscripts",false);
00464 m_importAllNames = prefs->getBool("importall",true);
00465 m_startupScript = prefs->get("startupscript", QString::null);
00466
00467 }
00468
00469 void ScripterCore::SavePlugPrefs()
00470 {
00471 PrefsContext* prefs = PrefsManager::instance()->prefsFile->getPluginContext("scriptplugin");
00472 if (!prefs)
00473 {
00474 qDebug("scriptplugin: Unable to load prefs");
00475 return;
00476 }
00477 PrefsTable* prefRecentScripts = prefs->getTable("recentscripts");
00478 if (!prefRecentScripts)
00479 {
00480 qDebug("scriptplugin: Unable to get recent scripts");
00481 return;
00482 }
00483 for (int i = 0; i < RecentScripts.count(); i++)
00484 prefRecentScripts->set(i, 0, RecentScripts[i]);
00485
00486 prefs->set("extensionscripts", m_enableExtPython);
00487 prefs->set("importall", m_importAllNames);
00488 prefs->set("startupscript", m_startupScript);
00489 }
00490
00491 void ScripterCore::aboutScript()
00492 {
00493 QString fname = ScCore->primaryMainWindow()->CFileDialog(".", tr("Examine Script"), tr("Python Scripts (*.py *.PY);;All Files (*)"), "", fdNone);
00494 if (fname == QString::null)
00495 return;
00496 QString html("<html><body>");
00497 QFileInfo fi = QFileInfo(fname);
00498 QFile input(fname);
00499 if(!input.open(QIODevice::ReadOnly))
00500 return;
00501 QTextStream intputstream(&input);
00502 QString content = intputstream.readAll();
00503 QString docstring = content.section("\"\"\"", 1, 1);
00504 if (!docstring.isEmpty())
00505 {
00506 html += QString("<h1>%1 %2</h1>").arg( tr("Documentation for:")).arg(fi.fileName());
00507 html += QString("<p>%1</p>").arg(docstring.replace("\n\n", "<br><br>"));
00508 }
00509 else
00510 {
00511 html += QString("<p><b>%1 %2 %3</b></p>").arg( tr("Script")).arg(fi.fileName()).arg( tr(" doesn't contain any docstring!"));
00512 html += QString("<pre>%4</pre>").arg(content);
00513 }
00514 html += "</body></html>";
00515 input.close();
00516 HelpBrowser *dia = new HelpBrowser(0, QObject::tr("About Script") + " " + fi.fileName(), "en");
00517 dia->setText(html);
00518 dia->show();
00519 }
00520
00521 void ScripterCore::initExtensionScripts()
00522 {
00523
00524 }
00525
00526 void ScripterCore::runStartupScript()
00527 {
00528 if ((m_enableExtPython) && (!m_startupScript.isNull()))
00529 {
00530 if (QFile::exists(this->m_startupScript))
00531 {
00532
00533
00534 this->slotRunScriptFile(this->m_startupScript, true);
00535 }
00536 else
00537 qDebug("Startup script enabled, but couln't find script %s.", m_startupScript.toAscii().constData());
00538 }
00539 }
00540
00541 void ScripterCore::languageChange()
00542 {
00543 scrScripterActions["scripterExecuteScript"]->setText(QObject::tr("&Execute Script..."));
00544 scrScripterActions["scripterShowConsole"]->setText(QObject::tr("Show &Console"));
00545 scrScripterActions["scripterAboutScript"]->setText(QObject::tr("&About Script..."));
00546
00547 menuMgr->setText("Scripter", QObject::tr("&Script"));
00548 menuMgr->setText("ScribusScripts", QObject::tr("&Scribus Scripts"));
00549 menuMgr->setText("RecentScripts", QObject::tr("&Recent Scripts"));
00550 }
00551
00552 bool ScripterCore::setupMainInterpreter()
00553 {
00554 QString cm = QString(
00555 "# -*- coding: utf-8 -*-\n"
00556 "import scribus\n"
00557 "import sys\n"
00558 "sys.path[0] = \"%1\"\n"
00559 "import cStringIO\n"
00560 "sys.stdin = cStringIO.StringIO()\n"
00561 "import code\n"
00562 "scribus._ia = code.InteractiveConsole(globals())\n"
00563 ).arg(ScPaths::instance().scriptDir());
00564 if (m_importAllNames)
00565 cm += "from scribus import *\n";
00566 QByteArray cmd = cm.toUtf8();
00567 if (PyRun_SimpleString(cmd.data()))
00568 {
00569 PyErr_Print();
00570 QMessageBox::warning(ScCore->primaryMainWindow(), tr("Script error"),
00571 tr("Setting up the Python plugin failed. "
00572 "Error details were printed to stderr. "));
00573 return false;
00574 }
00575 else
00576 return true;
00577 }
00578
00579 void ScripterCore::setStartupScript(const QString& newScript)
00580 {
00581 m_startupScript = newScript;
00582 }
00583
00584 void ScripterCore::setExtensionsEnabled(bool enable)
00585 {
00586 m_enableExtPython = enable;
00587 }
00588
00589 const QString & ScripterCore::startupScript() const
00590 {
00591 return m_startupScript;
00592 }
00593
00594 bool ScripterCore::extensionsEnabled() const
00595 {
00596 return m_enableExtPython;
00597 }