From 73f42d8972746905014337babe9c30aa48babab1 Mon Sep 17 00:00:00 2001
From: ale rimoldi <ale@graphicslab.org>
Date: Thu, 28 Feb 2019 17:42:10 +0100
Subject: [PATCH] add a scripter > scripts with user's scripts

---
 .../plugins/scriptplugin/prefs_scripter.cpp   | 21 ++++
 .../scriptplugin/prefs_scripterbase.ui        | 14 +++
 scribus/plugins/scriptplugin/scriptercore.cpp | 99 +++++++++++++++++++
 scribus/plugins/scriptplugin/scriptercore.h   |  9 ++
 scribus/scraction.cpp                         |  4 +
 scribus/scraction.h                           |  2 +-
 6 files changed, 148 insertions(+), 1 deletion(-)

diff --git a/scribus/plugins/scriptplugin/prefs_scripter.cpp b/scribus/plugins/scriptplugin/prefs_scripter.cpp
index b4bcf8094a..856aa8d6b8 100644
--- a/scribus/plugins/scriptplugin/prefs_scripter.cpp
+++ b/scribus/plugins/scriptplugin/prefs_scripter.cpp
@@ -31,6 +31,17 @@ Prefs_Scripter::Prefs_Scripter(QWidget* parent)
 	// The startup script box should be disabled  if ext scripts are off
 	startupScriptEdit->setEnabled(extensionScriptsChk->isChecked());
 	startupScriptEdit->setText(scripterCore->startupScript());
+
+	PrefsContext* prefs = PrefsManager::instance()->prefsFile->getPluginContext("scriptplugin");
+	PrefsTable* prefUserScriptsPath = prefs->getTable("userscriptspaths");
+	QStringList paths{};
+	for (int i = 0; i < prefUserScriptsPath->getRowCount(); i++)
+	{
+		paths.append(prefUserScriptsPath->get(i, 0));
+	}
+	userScriptPathEdit->setText(paths.join(";"));
+	userScriptPathEdit->setToolTip( "<qt>" + tr("Multiple paths are separated by semicolons. You need to restart Scribus to see new scripts.") + "</qt>");
+
 	// signals and slots connections
 	connect(extensionScriptsChk, SIGNAL(toggled(bool)), startupScriptEdit, SLOT(setEnabled(bool)));
 	// colors
@@ -78,6 +89,16 @@ void Prefs_Scripter::apply()
 		prefs->set("syntaxstring", stringColor.name());
 		prefs->set("syntaxtext", textColor.name());
 
+		// qDebug() << userScriptPathEdit->text();
+		PrefsTable* prefUserScriptsPath = prefs->getTable("userscriptspaths");
+		prefUserScriptsPath->clear();
+		int i = 0;
+		for (auto path: userScriptPathEdit->text().split(";"))
+		{
+			prefUserScriptsPath->set(i, 0, path);
+			i++;
+		}
+
 		emit prefsChanged();
 	}
 }
diff --git a/scribus/plugins/scriptplugin/prefs_scripterbase.ui b/scribus/plugins/scriptplugin/prefs_scripterbase.ui
index 7c35b7505c..892a31b1e3 100644
--- a/scribus/plugins/scriptplugin/prefs_scripterbase.ui
+++ b/scribus/plugins/scriptplugin/prefs_scripterbase.ui
@@ -98,6 +98,20 @@
          </item>
         </layout>
        </item>
+       <item>
+        <layout class="QHBoxLayout" name="horizontalLayout_4">
+         <item>
+          <widget class="QLabel" name="label_3">
+           <property name="text">
+            <string>User Script:</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QLineEdit" name="userScriptPathEdit"/>
+         </item>
+        </layout>
+       </item>
        <item>
         <spacer name="verticalSpacer_2">
          <property name="orientation">
diff --git a/scribus/plugins/scriptplugin/scriptercore.cpp b/scribus/plugins/scriptplugin/scriptercore.cpp
index c9c8672cca..07548adb7a 100644
--- a/scribus/plugins/scriptplugin/scriptercore.cpp
+++ b/scribus/plugins/scriptplugin/scriptercore.cpp
@@ -15,6 +15,8 @@ for which a new license (GPL+exception) is in place.
 #include <QTextCodec>
 #include <QByteArray>
 #include <QPixmap>
+#include <QDir>
+#include <QDirIterator>
 #include <cstdlib>
 
 #include "runscriptdialog.h"
@@ -48,6 +50,7 @@ ScripterCore::ScripterCore(QWidget* parent)
 	pcon = new PythonConsole(parent);
 	scrScripterActions.clear();
 	scrRecentScriptActions.clear();
+	scrUserScriptActions.clear();
 	returnString = "init";
 
 	scrScripterActions.insert("scripterExecuteScript", new ScrAction(QObject::tr("&Execute Script..."), QKeySequence(), this));
@@ -90,6 +93,11 @@ void ScripterCore::addToMainWindowMenu(ScribusMainWindow *mw)
 	menuMgr->addMenuItemString("scripterExecuteScript", "Scripter");
 	menuMgr->createMenu("RecentScripts", QObject::tr("&Recent Scripts"), "Scripter", false, true);
 	menuMgr->addMenuItemString("RecentScripts", "Scripter");
+	if (m_userScriptsEnabled)
+	{
+		menuMgr->createMenu("UserScripts", QObject::tr("S&cripts"), "Scripter", false, true);
+		menuMgr->addMenuItemString("UserScripts", "Scripter");
+	}
 	menuMgr->addMenuItemString("scripterExecuteScript", "Scripter");
 	menuMgr->addMenuItemString("SEPARATOR", "Scripter");
 	menuMgr->addMenuItemString("scripterShowConsole", "Scripter");
@@ -101,6 +109,10 @@ void ScripterCore::addToMainWindowMenu(ScribusMainWindow *mw)
 	menuMgr->addMenuItemStringstoMenuBar("Scripter", scrScripterActions);
 	RecentScripts = SavedRecentScripts;
 	rebuildRecentScriptsMenu();
+	if (m_userScriptsEnabled)
+	{
+		rebuildUserScriptsMenu();
+	}
 }
 
 void ScripterCore::enableMainWindowMenu()
@@ -109,6 +121,10 @@ void ScripterCore::enableMainWindowMenu()
 		return;
 	menuMgr->setMenuEnabled("ScribusScripts", true);
 	menuMgr->setMenuEnabled("RecentScripts", true);
+	if (m_userScriptsEnabled)
+	{
+		menuMgr->setMenuEnabled("UserScripts", true);
+	}
 	scrScripterActions["scripterExecuteScript"]->setEnabled(true);
 }
 
@@ -118,6 +134,10 @@ void ScripterCore::disableMainWindowMenu()
 		return;
 	menuMgr->setMenuEnabled("ScribusScripts", false);
 	menuMgr->setMenuEnabled("RecentScripts", false);
+	if (m_userScriptsEnabled)
+	{
+		menuMgr->setMenuEnabled("UserScripts", false);
+	}
 	scrScripterActions["scripterExecuteScript"]->setEnabled(false);
 }
 
@@ -156,6 +176,20 @@ void ScripterCore::rebuildRecentScriptsMenu()
 	menuMgr->addMenuItemStringstoRememberedMenu("RecentScripts", scrRecentScriptActions);
 }
 
+void ScripterCore::rebuildUserScriptsMenu()
+{
+	menuMgr->clearMenuStrings("UserScripts");
+	scrUserScriptActions.clear();
+	for (auto script: m_userScripts)
+	{
+		QString menuEntry = QFileInfo(script).baseName();
+		scrUserScriptActions.insert(menuEntry, new ScrAction( ScrAction::RecentScript, menuEntry, QKeySequence(), this, script));
+		connect( scrUserScriptActions[menuEntry], SIGNAL(triggeredData(QString)), this, SLOT(runUserScript(QString)) );
+		menuMgr->addMenuItemString(menuEntry, "UserScripts");
+	}
+	menuMgr->addMenuItemStringstoRememberedMenu("UserScripts", scrUserScriptActions);
+}
+
 void ScripterCore::FinishScriptRun()
 {
 	ScribusMainWindow* ScMW=ScCore->primaryMainWindow();
@@ -228,6 +262,12 @@ void ScripterCore::RecentScript(const QString& fn)
 	FinishScriptRun();
 }
 
+void ScripterCore::runUserScript(const QString& fn)
+{
+	slotRunScriptFile(fn);
+	FinishScriptRun();
+}
+
 void ScripterCore::slotRunScriptFile(const QString& fileName, bool inMainInterpreter)
 {
 	slotRunScriptFile(fileName, QStringList(), inMainInterpreter);
@@ -506,6 +546,61 @@ void ScripterCore::ReadPlugPrefs()
 	m_importAllNames = prefs->getBool("importall",true);
 	m_startupScript = prefs->get("startupscript", QString::null);
 	// and have the console window set up its position
+
+	auto prefUserScriptsPath = prefs->getTable("userscriptspaths");
+	m_userScriptsPaths.clear();
+	for (int i = 0; i < prefUserScriptsPath->getRowCount(); i++)
+	{
+		m_userScriptsPaths.append(prefUserScriptsPath->get(i, 0));
+	}
+
+	m_userScriptsEnabled = !m_userScriptsPaths.empty();
+
+	if (m_userScriptsEnabled)
+	{
+		m_userScripts = getUserScripts(m_userScriptsPaths);
+	}
+}
+
+/**
+ * list all the scripts in the paths' main directory and all
+ * the main.py and subdirectory.py in the sub directories.
+ */
+QStringList ScripterCore::getUserScripts(QStringList paths)
+{
+	QStringList result{};
+	for (auto path: paths)
+	{
+		QDir dir{path};
+		if (!dir.exists())
+			continue;
+		// TODO: do we need to check for .PY?
+		for (auto scriptPath: dir.entryList(QStringList() << "*.py" << "*.PY", QDir::Files))
+		{
+
+				
+			result << dir.filePath(scriptPath);
+		}
+
+		QDirIterator dirIter(path, QDir::Dirs | QDir::NoDotAndDotDot, QDirIterator::Subdirectories);
+		while(dirIter.hasNext())
+		{
+			QDir subPath{dirIter.next()};
+			// TODO: do we need to check for .PY?
+			QStringList scriptPaths{
+				subPath.filePath(subPath.dirName() + ".py"),
+				subPath.filePath("main.py")
+			};
+			for (auto scriptPath: scriptPaths)
+			{
+				if(QFileInfo(scriptPath).exists() && !QDir(scriptPath).exists())
+				{
+					result << scriptPath;
+				}
+			}
+		}
+	}
+	return result;
 }
 
 void ScripterCore::SavePlugPrefs()
@@ -592,6 +687,10 @@ void ScripterCore::languageChange()
 	menuMgr->setText("Scripter", QObject::tr("&Script"));
 	menuMgr->setText("ScribusScripts", QObject::tr("&Scribus Scripts"));
 	menuMgr->setText("RecentScripts", QObject::tr("&Recent Scripts"));
+	if (m_userScriptsEnabled)
+	{
+		menuMgr->setText("UserScripts", QObject::tr("S&cripts"));
+	}
 }
 
 bool ScripterCore::setupMainInterpreter()
diff --git a/scribus/plugins/scriptplugin/scriptercore.h b/scribus/plugins/scriptplugin/scriptercore.h
index 8682a96adb..d9c4795b18 100644
--- a/scribus/plugins/scriptplugin/scriptercore.h
+++ b/scribus/plugins/scriptplugin/scriptercore.h
@@ -33,11 +33,13 @@ class ScripterCore : public QObject
 	QString returnString;
 	/** @brief String representation of line of code to be passed to the Python interactive interpreter */
 	QString inValue;
+	QStringList getUserScriptsPaths() { return m_userScriptsPaths; }
 
 public slots:
 	void runScriptDialog();
 	void StdScript(const QString& filebasename);
 	void RecentScript(const QString& fn);
+	void runUserScript(const QString& script);
 	void slotRunScriptFile(const QString& fileName, bool inMainInterpreter = false);
 	void slotRunScriptFile(const QString& fileName, QStringList arguments, bool inMainInterpreter = false);
 	void slotRunPythonScript(); // needed for running python script from CLI
@@ -78,9 +80,13 @@ public slots:
 	PythonConsole *pcon;
 	QStringList SavedRecentScripts;
 	QStringList RecentScripts;
+	QStringList m_userScriptsPaths;
+	bool m_userScriptsEnabled{false};
+	QStringList m_userScripts;
 	MenuManager *menuMgr;
 	QMap<QString, QPointer<ScrAction> > scrScripterActions;
 	QMap<QString, QPointer<ScrAction> > scrRecentScriptActions;
+	QMap<QString, QPointer<ScrAction> > scrUserScriptActions;
 
 	// Preferences
 	/** \brief pref: Enable access to main interpreter and 'extension scripts' */
@@ -89,6 +95,9 @@ public slots:
 	bool m_importAllNames;
 	/** \brief pref: Load this script on startup */
 	QString m_startupScript;
+private:
+	QStringList getUserScripts(QStringList paths);
+	void rebuildUserScriptsMenu();
 };
 
 #endif
diff --git a/scribus/scraction.cpp b/scribus/scraction.cpp
index 9ff8ec0cb4..dddc62eb11 100644
--- a/scribus/scraction.cpp
+++ b/scribus/scraction.cpp
@@ -114,6 +114,8 @@ void ScrAction::triggeredToTriggeredData()
 		emit triggeredData(data().toString());
 	if (m_actionType==ScrAction::RecentScript)
 		emit triggeredData(data().toString());
+	if (m_actionType==ScrAction::UserScript)
+		emit triggeredData(data().toString());
 	if (m_actionType==ScrAction::UnicodeChar)
 		emit triggeredUnicodeShortcut(data().toInt());
 	if (m_actionType==ScrAction::Layer)
@@ -143,6 +145,8 @@ void ScrAction::toggledToToggledData(bool ison)
 			emit toggledData(ison, data().toString());
 		if (m_actionType==ScrAction::RecentScript)
 			emit toggledData(ison, text());
+		if (m_actionType==ScrAction::UserScript)
+			emit toggledData(ison, text());
 		if (m_actionType==ScrAction::Layer)
 			emit toggledData(ison, data().toInt());
 		// no toggle for UnicodeChar
diff --git a/scribus/scraction.h b/scribus/scraction.h
index 0d409124a0..b526779ea9 100644
--- a/scribus/scraction.h
+++ b/scribus/scraction.h
@@ -35,7 +35,7 @@ class SCRIBUS_API ScrAction : public QAction
 	Q_OBJECT
 
 public:
-	typedef enum {Normal, DataInt, DataDouble, DataQString, RecentFile, DLL, Window, RecentScript, UnicodeChar, Layer, ActionDLL, RecentPaste, ActionDLLSE } ActionType;
+	typedef enum {Normal, DataInt, DataDouble, DataQString, RecentFile, DLL, Window, RecentScript, UserScript, UnicodeChar, Layer, ActionDLL, RecentPaste, ActionDLLSE } ActionType;
 	
 	/*!
 		\author Craig Bradney
