1 /* 2 * Stellarium 3 * Copyright (C) 2007 Fabien Chereau, 2009 Matthew Gates 4 * 5 * This program is free software; you can redistribute it and/or 6 * modify it under the terms of the GNU General Public License 7 * as published by the Free Software Foundation; either version 2 8 * of the License, or (at your option) any later version. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA. 18 */ 19 20 #ifndef STELSCRIPTMGR_HPP 21 #define STELSCRIPTMGR_HPP 22 23 #include <QObject> 24 #include <QStringList> 25 #include <QFile> 26 #include <QTimer> 27 #include <QEventLoop> 28 #include <QMap> 29 #include <QPair> 30 #include <QSet> 31 32 // class StelMainScriptAPI; 33 #include "StelMainScriptAPI.hpp" 34 35 class StelScriptEngineAgent; 36 class QScriptEngine; 37 38 #ifdef ENABLE_SCRIPT_CONSOLE 39 class ScriptConsole; 40 #endif 41 42 //! Manage scripting in Stellarium 43 class StelScriptMgr : public QObject 44 { 45 Q_OBJECT 46 47 Q_PROPERTY(QString runningScriptId READ runningScriptId NOTIFY runningScriptIdChanged) 48 49 #ifdef ENABLE_SCRIPT_CONSOLE 50 friend class ScriptConsole; 51 #endif 52 53 public: 54 StelScriptMgr(QObject *parent=Q_NULLPTR); 55 ~StelScriptMgr(); 56 57 QStringList getScriptList() const; 58 59 //! Find out if a script is running 60 //! @return true if a script is running, else false 61 bool scriptIsRunning() const; 62 //! Get the ID (usually filename) of the currently running script 63 //! @return Empty string if no script is running, else the 64 //! ID of the script which is running. 65 QString runningScriptId() const; 66 67 // Pre-processor functions 68 //! Preprocess script, esp. process include instructions. 69 //! if the command line option --verbose has been given, 70 //! this dumps the preprocessed script with line numbers attached to log. 71 //! This helps to understand the line number given by the usual error message. 72 bool preprocessScript(const QString fileName, const QString& input, QString& output, const QString& scriptDir, int &errLoc); 73 bool preprocessFile(const QString fileName, QFile &input, QString& output, const QString& scriptDir); 74 75 //! Add all the StelModules into the script engine 76 void addModules(); 77 78 //! Define JS classes Vec3f, Vec3d 79 static void defVecClasses(QScriptEngine *engine); 80 81 //! Permit access to StelScriptMainAPI's methods getMetaOfStelMainScriptAPI()82 const QMetaObject * getMetaOfStelMainScriptAPI(){ return mainAPI->metaObject(); } 83 84 //! Accessor to QEventLoop getWaitEventLoop()85 QEventLoop* getWaitEventLoop(){ return waitEventLoop; } 86 87 public slots: 88 //! Returns a HTML description of the specified script. 89 //! Includes name, author, description... 90 //! @param s the file name of the script whose HTML description is to be returned. 91 //! @param generateDocumentTags if true, the main wrapping document tags (\<html\>\<body\>...\</body\>\</html\>) are also generated 92 QString getHtmlDescription(const QString& s, bool generateDocumentTags=true) const; 93 94 //! Gets a single line name of the script. 95 //! @param s the file name of the script whose name is to be returned. 96 //! @return text following a comment with Name: at the start. If no 97 //! such comment is found, the file name will be returned. If the file 98 //! is not found or cannot be opened for some reason, an Empty string 99 //! will be returned. 100 QString getName(const QString& s) const; 101 102 //! Gets the name of the script Author 103 //! @param s the file name of the script whose name is to be returned. 104 //! @return text following a comment with Author: at the start. If no 105 //! such comment is found, "" is returned. If the file 106 //! is not found or cannot be opened for some reason, an Empty string 107 //! will be returned. 108 QString getAuthor(const QString& s) const; 109 110 //! Gets the licensing terms for the script 111 //! @param s the file name of the script whose name is to be returned. 112 //! @return text following a comment with License: at the start. If no 113 //! such comment is found, "" is returned. If the file 114 //! is not found or cannot be opened for some reason, an Empty string 115 //! will be returned. 116 QString getLicense(const QString& s) const; 117 118 //! Gets the version of the script 119 //! @param s the file name of the script whose name is to be returned. 120 //! @return text following a comment with Version: at the start. If no 121 //! such comment is found, "" is returned. If the file 122 //! is not found or cannot be opened for some reason, an Empty string 123 //! will be returned. 124 QString getVersion(const QString& s) const; 125 126 //! Gets a description of the script. 127 //! @param s the file name of the script whose name is to be returned. 128 //! @return text following a comment with Description: at the start. 129 //! The description is considered to be over when a line with no comment 130 //! is found. If no such comment is found, QString("") is returned. 131 //! If the file is not found or cannot be opened for some reason, an 132 //! Empty string will be returned. 133 QString getDescription(const QString& s) const; 134 135 //! Gets the default shortcut of the script. 136 //! @param s the file name of the script whose name is to be returned. 137 //! @return text following a comment with Shortcut: at the start. 138 //! If no such comment is found, QString("") is returned. 139 //! If the file is not found or cannot be opened for some reason, an 140 //! Empty string will be returned. 141 QString getShortcut(const QString& s) const; 142 143 //! Run the script located in the given file. In essence, this calls prepareScript and runPreprocessedScript. 144 //! @note This is a blocking call! The event queue is held up by calls of QCoreApplication::processEvents(). 145 //! @param fileName the location of the file containing the script. 146 //! @param includePath the directory to use when searching for include files 147 //! in the SSC preprocessor. If empty, this will be the same as the 148 //! directory where the script file resides. If you're running a generated script from 149 //! a temp directory, but want to include a file from elsewhere, it 150 //! can be usetul to set it to something else (e.g. in ScriptConsole). 151 //! @return false if the named script could not be prepared or run, true otherwise 152 bool runScript(const QString& fileName, const QString& includePath=""); 153 154 //! Runs the script code given. This can be used for quick script executions, without having to create a 155 //! temporary file first. 156 //! @note This is a blocking call! The event queue is held up by calls of QCoreApplication::processEvents(). 157 //! @param scriptId path name, if available, or something helpful 158 //! @param scriptCode The script to execute 159 //! @param errLoc offset of erroneous include line, or -1 160 //! @param includePath If a null string (the default), no pre-processing is done. If an empty string, the default 161 //! script directories are used (script/ in both user and install directory). Otherwise, the given directory is used. 162 //! @return false if the named script code could not be prepared or run, true otherwise 163 bool runScriptDirect(const QString scriptId, const QString& scriptCode, int &errLoc, const QString &includePath = QString()); 164 165 //! Convenience method similar to runScriptDirect(const QString scriptId, const QString& scriptCode, int &errLoc, const QString &includePath = QString()); 166 //! when scriptId and errLoc are not relevant. (Required e.g. in RemoteControl) 167 bool runScriptDirect(const QString& scriptCode, const QString &includePath = QString()); 168 169 //! Runs preprocessed script code which has been generated using runPreprocessedScript(). 170 //! In general, you do not want to use this method, use runScript() or runScriptDirect() instead. 171 //! @note This is a blocking call! The event queue is held up by calls of QCoreApplication::processEvents(). 172 //! @param preprocessedScript the string containing the preprocessed script. 173 //! @param scriptId The name of the script. Usually should correspond to the file name. 174 //! @return false if the given script code could not be run, true otherwise 175 bool runPreprocessedScript(const QString& preprocessedScript, const QString &scriptId); 176 177 //! Loads a script file and does all preparatory steps except for actually executing the script in the engine. 178 //! Use runPreprocessedScript to execute the script. 179 //! It should be safe to call this method from another thread. 180 //! @param script returns the preprocessed script text 181 //! @param fileName the location of the file containing the script. 182 //! @param includePath the directory to use when searching for include files 183 //! in the SSC preprocessor. Usually this will be the same as the 184 //! script file itself, but if you're running a generated script from 185 //! a temp directory, but want to include a file from elsewhere, it 186 //! can be usetul to set it to something else (e.g. in ScriptConsole). 187 //! @return false if the named script could not be prepared, true otherwise 188 bool prepareScript(QString& script, const QString& fileName, const QString& includePath=""); 189 190 //! Stops any running script. 191 //! @return false if no script was running, true otherwise. 192 void stopScript(); 193 194 //! Changes the rate at which the script executes as a multiple of real time. 195 //! Note that this is not the same as the rate at which simulation time passes 196 //! because the script running at normal rate might set the simulation time rate 197 //! to be non-real time. 198 //! @param r rate, e.g. 2.0 = script runs at twice the normal rate 199 void setScriptRate(double r); 200 201 //! Get the rate at which the script is running as a multiple of the normal 202 //! execution rate. 203 double getScriptRate() const; 204 205 //! cause the emission of the scriptDebug signal. This is so that functions in 206 //! StelMainScriptAPI can explicitly send information to the ScriptConsole 207 void debug(const QString& msg); 208 209 //! cause the emission of the scriptOutput signal. This is so that functions in 210 //! StelMainScriptAPI can explicitly send information to the ScriptConsole 211 void output(const QString& msg); 212 213 //! Reset output file and cause the emission of an (empty) scriptOutput signal. 214 void resetOutput(void); 215 216 //! Save output file to new file. 217 //! This is required to allow reading with other program on Windows while output.txt is still open. 218 //! @param filename new filename. If this is not an absolute path, it will be created in the same directory as output.txt 219 //! @note For storing to absolute path names, set [scripts]/flag_script_allow_write_absolute_path=true. 220 void saveOutputAs(const QString &filename); 221 222 //! Pause a running script. 223 void pauseScript(); 224 225 //! Resume a paused script. 226 void resumeScript(); 227 228 private slots: 229 //! Called at the end of the running threa 230 void scriptEnded(); 231 signals: 232 //! Emitted when the running script id changes (also on start/stop) 233 void runningScriptIdChanged(const QString& id); 234 //! Notification when a script starts running 235 void scriptRunning(); 236 //! Notification when a script has paused running 237 void scriptPaused(); 238 //! Notification when a script has stopped running 239 void scriptStopped(); 240 //! Notification of a script event - warnings, current execution line etc. 241 void scriptDebug(const QString&) const; 242 //! Notification of a script event - output line. 243 void scriptOutput(const QString&) const; 244 245 private: 246 // Utility functions for preprocessor 247 QMap<QString, QString> mappify(const QStringList& args, bool lowerKey=false); 248 bool strToBool(const QString& str); 249 // The recursive preprocessing workhorse. 250 void expand(const QString fileName, const QString &input, QString &output, const QString &scriptDir, int &errLoc); 251 252 //! Generate one StelAction per script. 253 //! The name of the action is of the form: "actionScript/<script-path>" 254 void initActions(); 255 256 //! This function is for use with getName, getAuthor and getLicense. 257 //! @param s the script id 258 //! @param id the command line id, e.g. "Name" 259 //! @param notFoundText the text to be returned if the key is not found 260 //! @return the text following the id and : on a comment line near the top of 261 //! the script file (i.e. before there is a non-comment line). 262 QString getHeaderSingleLineCommentText(const QString& s, const QString& id, const QString& notFoundText="") const; 263 QScriptEngine* engine; 264 265 //! The thread in which scripts are run 266 StelMainScriptAPI *mainAPI; 267 268 //! The QEventLoop for wait and waitFor 269 QEventLoop* waitEventLoop; 270 271 QString scriptFileName; 272 273 //Script engine agent 274 StelScriptEngineAgent *agent; 275 276 // Map line numbers of output to <path>:<line> 277 int outline; 278 QMap<int,QPair<QString,int>> num2loc; 279 QString lookup( int outline ); 280 281 // Registry for include files 282 QSet<QString> includeSet; 283 }; 284 285 #endif // STELSCRIPTMGR_HPP 286 287