/** * Copyright (c) 2011-2018 by Andrew Mustun. All rights reserved. * * This file is part of the QCAD project. * * QCAD 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 3 of the License, or * (at your option) any later version. * * QCAD 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 QCAD. */ //include("WidgetFactory.js"); /** * \class AddOn * \brief Initializes and manages all add-ons. Every directory in the * script directory of QCAD is a add-on if it contains an ECMAScript * file with the same name as the directory and the extension ".js". * For example MyAddOn/MyAddOn.js. */ function AddOn(addOnPath) { this.fileInfo = new QFileInfo(addOnPath); this.widget = undefined; AddOn.xslt = undefined; } /** * For debugging. */ AddOn.prototype.toString = function() { var actionText = ""; if (!isNull(this.getGuiAction())) { actionText = this.getGuiAction().text; } return "AddOn(" + "actionText: '" + actionText + "'" + ", groupName: " + this.getGroupName() + ", filePath: " + this.getFilePath() + ")"; }; /** * Called once on startup to initialize the add-on, its preference page * and its documentation if applicable. */ AddOn.prototype.init = function(splash, text) { this.initAddOn(splash, text); // slow: //this.initPreferencePage(); }; /** * Called once on startup to post-initialize the add-on. */ AddOn.prototype.postInit = function(splash, text) { this.postInitAddOn(splash, text); }; /** * Loads the add-on by including its main source file. * * \param initOnly True: only load the add-on if no file ClassNameInit.js * is present. */ AddOn.prototype.load = function(initOnly) { if (isNull(initOnly)) { initOnly = false; } if (initOnly) { var initFilePath = this.getInitFilePath(); if (new QFileInfo(initFilePath).exists()) { return; } } var fileName = this.getFilePath(); if (!new QFileInfo(fileName).exists()) { qWarning("AddOn " + fileName + " not found and not loaded."); return; } include(fileName); }; /** * \internal Called once on startup. */ AddOn.prototype.initAddOn = function(splash, text) { var className = this.getClassName(); var path = this.getPath(); var bak = includeBasePath; includeBasePath = path; var initFilePath = this.getInitFilePath(); if (new QFileInfo(initFilePath).exists()) { include(initFilePath, className, true); init(includeBasePath); } else { if (typeof(global[className])!=='undefined' && isFunction(global[className].init)) { global[className].init(includeBasePath, splash, text); } } includeBasePath = bak; // TODO: release add-on to free memory: //global[className] = undefined; }; /** * \internal Called once in the end after startup. */ AddOn.prototype.postInitAddOn = function(splash, text) { var className = this.getClassName(); var path = this.getPath(); var bak = includeBasePath; includeBasePath = path; var postInitFilePath = this.getPostInitFilePath(); if (new QFileInfo(postInitFilePath).exists()) { include(postInitFilePath); postInit(includeBasePath); } else { if (!isNull(global[className]) && isFunction(global[className].postInit)) { global[className].postInit(includeBasePath, splash, text); } } includeBasePath = bak; }; AddOn.prototype.uninit = function() { //var className = this.getClassName(); //global[className] = undefined; var className = this.getClassName(); var path = this.getPath(); //var bak = includeBasePath; //includeBasePath = path; var uninitFilePath = this.getUninitFilePath(); if (new QFileInfo(uninitFilePath).exists()) { include(uninitFilePath, className); uninit(); } else { if (typeof(global[className])!=='undefined' && isFunction(global[className].uninit)) { global[className].uninit(); } } //includeBasePath = bak; }; /** * \internal Called once on startup. */ /* AddOn.prototype.initPreferencePage = function() { var className = this.getClassName(); var uiFileName = this.getPreferenceFile(); // load preference page, if one exists if (new QFileInfo(uiFileName).exists()) { var settingsKey = "PreferencePageInitialized/" + className; var alreadyInitialized = RSettings.getBoolValue(settingsKey, false); // freak bug FS#406 with custom key codes / command configured: //isDeleted(""); isNull(); if (alreadyInitialized) { return; } var widget = WidgetFactory.createWidget("", uiFileName); try { eval("if( !isNull(" + className + ") && isFunction(" + className + ".initPreferences) ) {" + className + ".initPreferences(widget);" + "}"); } catch (e) { qDebug("AddOn.js:", "AddOn has no method initPreferences(): " + e ); } WidgetFactory.restoreState(widget); WidgetFactory.saveState(widget); WidgetFactory.requiresRestart = false; destr(widget); RSettings.setValue(settingsKey, true); } }; */ /** * \return Absolute path of the preference UI definition for this add-on. * The file name is PreferencesPage.ui and it must be located in the add-on * path to be found. */ AddOn.prototype.getPreferenceFile = function() { var path = this.getPath(); var uiFile = new QFileInfo("%1/PreferencesPage.ui".arg(path)); return uiFile.absoluteFilePath(); }; /** * \return True if this add-on has a preferences UI file. */ AddOn.prototype.preferenceFileExists = function() { var uiFileName = this.getPreferenceFile(); var ret = (new QFileInfo(uiFileName).exists()); if (!ret) { qWarning("AddOn.js: File does not exists: " + uiFileName); } return ret; }; /** * \return True if shortcuts can be defined for this add-on (default) or * false if a add-on explicitly defines, that is does not support * shortcuts (ClassName.hasShortcuts()). */ AddOn.prototype.hasShortcuts = function() { var className = this.getClassName(); try { var ret = true; if (!isNull(global[className]) && isFunction(global[className].hasShortcuts)) { ret = global[className].hasShortcuts(); } return ret; } catch (e) { debugger; } return undefined; }; /** * \return True if this add-on has a GUI action (QAction) attached to it * which can be used to launch the action this add-on represents. */ AddOn.prototype.hasGuiAction = function() { return this.getGuiAction() != null; }; /** * \return The GUI action attached to this add-on. */ AddOn.prototype.getGuiAction = function() { return RGuiAction.getByScriptFile(this.getFilePath()); }; AddOn.prototype.setPreferenceWidget = function(w) { this.widget = w; }; AddOn.prototype.getPreferenceWidget = function() { return this.widget; }; AddOn.prototype.getGroupName = function() { var dir = new QDir(this.fileInfo.path()); dir.cdUp(); return dir.dirName(); }; /** * \return Translated title of this add on (from GUI action or class info). */ AddOn.prototype.getTitle = function() { var title; // try to get title from class (getTitle): var className = this.getClassName(); title = AddOn.getClassTitle(className); if (isNull(title)) { // try to get title from gui action: var a = this.getGuiAction(); if (!isNull(a)) { title = a.text; } } if (!isNull(title)) { title = title.replace("&", ""); // remove everything after tab (shortcuts under windows): if (title.indexOf("\t")!==-1) { title = title.substring(0, title.indexOf("\t")); } } return title; }; /** * \return The title of this add-on's parent add-on. E.g. for * scripts/Draw/Line/Line2P.js, that's the title returned by * Line.getTitle(). */ AddOn.prototype.getParentTitle = function() { var gn = this.getGroupName(); return AddOn.getClassTitle(gn); }; /** * \return Translated title for the given class name. */ AddOn.getClassTitle = function(cn) { var title; try { if (!isNull(global[cn]) && isFunction(global[cn].getTitle)) { title = global[cn].getTitle(); } } catch (e) { qWarning("AddOn.js:", "getClassTitle(): Exception:", e); } if (!isNull(title)) { title = title.replace("&", ""); } return title; }; /** * \return Hierarchy of titles for this add on (e.g. [Draw, Line, Line 2P]). */ AddOn.prototype.getParentTitles = function() { var ret = []; var dir = new QDir(this.fileInfo.path()); ret.unshift(this.getTitle()); do { if (!dir.cdUp()) { break; } var dn = dir.dirName(); var title = AddOn.getClassTitle(dn); if (isNull(title)) { break; } ret.unshift(title); } while (true); return ret; }; /** * \return true If the add-on's parent is "scripts", the top level * directory that contains all script add-ons. */ AddOn.prototype.isTopLevel = function() { return this.getGroupName() === "scripts"; }; AddOn.prototype.getClassName = function() { return this.fileInfo.completeBaseName(); }; AddOn.prototype.getPath = function() { return this.fileInfo.path(); }; /** * \return The ECMAScript file path of the add-on implementation. */ AddOn.prototype.getFilePath = function() { return this.fileInfo.filePath(); }; /** * \return The ECMAScript file path of the add-on init function implementation. */ AddOn.prototype.getInitFilePath = function() { return this.fileInfo.absolutePath() + QDir.separator + this.fileInfo.completeBaseName() + "Init.js"; }; /** * \return The ECMAScript file path of the add-on postInit function implementation. */ AddOn.prototype.getPostInitFilePath = function() { return this.fileInfo.absolutePath() + QDir.separator + this.fileInfo.completeBaseName() + "PostInit.js"; }; AddOn.prototype.getUninitFilePath = function() { return this.fileInfo.absolutePath() + QDir.separator + this.fileInfo.completeBaseName() + "Uninit.js"; }; AddOn.prototype.getBaseName = function() { return this.fileInfo.completeBaseName(); }; AddOn.prototype.hasDoc = function() { return this.getDocPath().exists(); }; AddOn.prototype.getDocPath = function() { return new QFileInfo(this.getPath() + "/doc"); }; AddOn.prototype.getDocJsFile = function() { return new QFile(this.getDocPath().filePath() + "/" + this.getClassName() + "_doc.js"); }; AddOn.prototype.getDocHtmlUrl = function() { return QUrl.fromLocalFile(this.getDocPath().absoluteFilePath() + "/" + this.getClassName() + "_" + RSettings.getLocale() + ".html"); }; AddOn.findByGroupName = function(addOns, groupName) { for ( var i = 0; i < addOns.length; ++i) { var addOn = addOns[i]; //qDebug("class name: ", addOn.getClassName(), " groupName: ", groupName); if (addOn.getClassName() === groupName) { return addOn; } } return undefined; }; AddOn.getParentAddOn = function(addOns, addOn) { var ret = AddOn.findByGroupName(addOns, addOn.getGroupName()); if (isNull(ret)) { qWarning("AddOn.getParentAddOn: no parent add-on found for group name: ", addOn.getGroupName()); } return ret; }; /** * \return Array of all AddOn objects found. * * \param dir Used internally for recursive calls */ AddOn.getAddOns = function(dir) { var topCall = isNull(dir); if (topCall && !isNull(AddOn.addOns)) { // return cached list: return AddOn.addOns; } var fileMenuList, i, k; var addOns = []; var dirFilter = makeQDirFilters(QDir.NoDotAndDotDot, QDir.Readable, QDir.Dirs); var sortFlags = makeQDirSortFlags(QDir.NoSort); // first call without recursion: if (topCall) { var args = RSettings.getOriginalArguments(); // ignore all scripts in ./scripts directory // and only load scripts from plugins // used mainly for testing / development if (args.contains("-ignore-script-files")) { dir = undefined; } else { dir = "scripts"; } // fixed set of directories that will be scanned for add-ons first to // ensure fixed order of menus and tool bars: fileMenuList = [ // scripts compiled as resources in plugins: new QFileInfo(":scripts"), // local scripts: new QFileInfo(RSettings.getDataLocation() + "/scripts"), ]; // local scripts from installed add-ons: var localAddOns = AddOn.getLocalAddOns(); for (i=0; ibPathNoColon) ? 1 : -1; } // prioritize 'scripts' paths (QCAD) over ':scripts' (plugin): if (!aPath.startsWith(":") && bPath.startsWith(":")) { return -1; } else if (aPath.startsWith(":") && !bPath.startsWith(":")) { return 1; } else { return 0; } }); // for (var i=0; i