| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700 |
- /**
- * 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; i<localAddOns.length; i++) {
- var localAddOnDir = RSettings.getDataLocation() + "/" + localAddOns[i] + "/scripts";
- var fi = new QFileInfo(localAddOnDir);
- if (fi.exists()) {
- qDebug("adding scripts dir of add-on: ", localAddOnDir);
- fileMenuList.push(fi);
- }
- }
- // append directories not in the list above:
- if (!isNull(dir)) {
- var menuList = new QDir(dir).entryInfoList(dirFilter, sortFlags);
- for (i=0; i<menuList.length; ++i) {
- if (!fileMenuList.contains(menuList[i], function(a,b) { return a.absoluteFilePath()===b.absoluteFilePath(); })) {
- fileMenuList.push(menuList[i]);
- }
- }
- }
- } else {
- fileMenuList = new QDir(dir).entryInfoList(dirFilter, sortFlags);
- }
- for (i=0; i<fileMenuList.length; ++i) {
- var dirInfo = fileMenuList[i];
- if (!dirInfo.exists()) {
- continue;
- }
- if (AddOn.isIgnored(dirInfo.filePath())) {
- continue;
- }
- // don't dive into translation, doc or Tests folders:
- if (dirInfo.fileName() === "ts" || dirInfo.fileName() === "doc" || dirInfo.fileName() === "Tests") {
- continue;
- }
- var fileInfo = new QFileInfo(dirInfo.filePath() + "/" + dirInfo.fileName() + ".js");
- if (dirInfo.fileName().startsWith("AutoLoad")) {
- if (typeof(RAutoLoadEcma)!=="undefined") {
- RAutoLoadEcma.addAutoLoadFile(fileInfo.filePath());
- }
- if (typeof(RAutoLoadJs)!=="undefined") {
- RAutoLoadJs.addAutoLoadFile(fileInfo.filePath());
- }
- }
- else {
- if (fileInfo.exists() && !AddOn.isIgnored(fileInfo.absoluteFilePath())) {
- // if (addOns.contains(fileInfo.filePath(), function(a,b) { qDebug(a.getPath(), b); return a.getPath()===b; })) {
- // qDebug("already loaded: ", fileInfo.filePath());
- // }
- if (!addOns.contains(fileInfo.filePath(), function(a,b) { return a.getPath()===b; })) {
- addOns.push(new AddOn(fileInfo.filePath()));
- }
- }
- }
- addOns = addOns.concat(AddOn.getAddOns(dirInfo.filePath()));
- }
- if (topCall) {
- // sort add ons by menu order:
- var menuNames = {
- //"Reset":10,
- "File":20,// "Data": 30, "Interaction": 40, "Optimize": 50
- // , "View":40, "Select":50,
- "Layer":1500,
- "Window":1600, "Widgets":1700//, "Local":170
- };
- // var menuNames = {
- // "Reset":10, "File":20, "Edit":30, "View":40, "Select":50, "Draw":60, "Dimension":70,
- // "Modify":80, "Snap":90, "Information":100, "Layer":110, "Block":120, "Developer":130,
- // "Window":140, "Widgets":150, "Misc":160, "Local":170, "Help":100000
- // };
- var rx1 = new RegExp(":?\/?scripts\/");
- var rx2 = new RegExp("\/.*");
- var gotRegularFile = false;
- addOns.sort(function(a,b) {
- var aPath = a.getPath();
- var bPath = b.getPath();
- if (!gotRegularFile && (aPath[0]!==":" || bPath[0]!==":")) {
- gotRegularFile = true;
- }
- // get top dir name after scripts (e.g. 'File', 'Layer', ...):
- var da = aPath.replace(rx1, "").replace(rx2, "");
- var db = bPath.replace(rx1, "").replace(rx2, "");
- // first priority:
- // sort by predefined menu order:
- // get sort order from menuNames map:
- var oa = menuNames[da];
- var ob = menuNames[db];
- // menu not in list:
- // insert between Local and Help:
- if (isNull(oa)) {
- oa = 1000;
- }
- if (isNull(ob)) {
- ob = 1000;
- }
- if (oa!==ob) {
- return oa-ob;
- }
- // secondary priority:
- // sort alphabetically:
- var aPathNoColon = aPath;
- var bPathNoColon = bPath;
- if (aPathNoColon[0]===":") {
- aPathNoColon = aPathNoColon.substring(1);
- }
- if (bPathNoColon[0]===":") {
- bPathNoColon = bPathNoColon.substring(1);
- }
- if (aPathNoColon!==bPathNoColon) {
- return (aPathNoColon>bPathNoColon) ? 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<addOns.length; i++) {
- // qDebug("addOn: ", addOns[i].getPath());
- // }
- if (gotRegularFile) {
- // filter out duplicate paths from plugins:
- // files take priority over resources:
- var addOnsFiltered = [];
- var prevPathNoColon = undefined;
- for (var i=0; i<addOns.length; i++) {
- var addOn = addOns[i];
- var pathNoColon = addOn.fileInfo.filePath();
- if (pathNoColon[0]===":") {
- pathNoColon = pathNoColon.substring(1);
- }
- if (!isNull(prevPathNoColon) && prevPathNoColon===pathNoColon) {
- continue;
- }
- addOnsFiltered.push(addOn);
- prevPathNoColon = pathNoColon;
- }
- addOns = addOnsFiltered;
- }
- // cache:
- AddOn.addOns = addOns;
- }
- return addOns;
- };
- AddOn.getLocalAddOns = function() {
- var dataDir = new QDir(RSettings.getDataLocation());
- var fs = makeQDirFilters(QDir.NoDotAndDotDot, QDir.Readable, QDir.Dirs, QDir.Executable);
- var sf = makeQDirSortFlags(QDir.Name);
- return dataDir.entryList([], fs, sf);
- };
- AddOn.isIgnored = function(path) {
- if (isNull(AddOn.ignores)) {
- var args = RSettings.getOriginalArguments();
- AddOn.ignores = [];
- for (var i=1; i < args.length; ++i) {
- if (args[i] === "-ignore") {
- ++i;
- if (isNull(args[i])) {
- break;
- }
- AddOn.ignores.push(args[i]);
- }
- }
- }
- for (var k=0; k<AddOn.ignores.length; ++k) {
- if (path.contains(AddOn.ignores[k])) {
- return true;
- }
- }
- return false;
- };
- AddOn.getAddOnPath = function(name) {
- var addOns = AddOn.getAddOns();
- for (var i = 0; i < addOns.length; ++i) {
- var addOn = addOns[i];
- if (addOn.getClassName()===name) {
- return addOn.getPath();
- }
- }
- };
|