diff --git a/Engine/otoolstuff.cpp b/Common/otoolstuff.cpp similarity index 79% rename from Engine/otoolstuff.cpp rename to Common/otoolstuff.cpp index f858fd7905..44e1faa2e8 100644 --- a/Engine/otoolstuff.cpp +++ b/Common/otoolstuff.cpp @@ -1,29 +1,26 @@ #include "otoolstuff.h" #include "stringutils.h" -#include "rbridge.h" #include #include #include "utils.h" #include "appinfo.h" #include +#include std::string _system(std::string cmd) { - const char *root, *relativePath; - - if (!rbridge_requestTempFileName("log", &root, &relativePath)) + auto path = std::filesystem::temp_directory_path() / "jaspTmpSystem"; + std::ofstream ofs(path , std::ofstream::out); + if (!ofs.is_open()) throw std::runtime_error("Cannot open output file for separate R/System cmd!"); - - std::string path = std::string(root) + "/" + relativePath; - - cmd += " > " + path + " 2>&1 "; + cmd += " > " + path.generic_string() + " 2>&1 "; #ifdef WIN32 cmd = '"' + cmd + '"'; // See: https://stackoverflow.com/questions/2642551/windows-c-system-call-with-spaces-in-command #endif system(cmd.c_str()); - + ofs.close(); std::ifstream readLog(path); std::stringstream out; @@ -36,7 +33,7 @@ std::string _system(std::string cmd) return out.str(); } -void _moduleLibraryFixer(const std::string & moduleLibraryPath, bool engineCall, bool printStuff) +void _moduleLibraryFixer(const std::string & moduleLibraryPath, bool engineCall, bool printStuff, bool devMod) { using namespace boost; @@ -117,17 +114,27 @@ void _moduleLibraryFixer(const std::string & moduleLibraryPath, bool engineCall, {"/opt/X11/lib", framework_resources + "opt/X11/lib"}, }; - // Known fix library id's and paths - const std::map ids_to_be_replaced = - { - {"libtbbmalloc.dylib", "@executable_path/../Modules/" + jaspModuleName + "/RcppParallel/lib/libtbbmalloc.dylib"}, - {"libtbbmalloc_proxy.dylib", "@executable_path/../Modules/" + jaspModuleName + "/RcppParallel/lib/libtbbmalloc_proxy.dylib"}, - {"libtbb.dylib", "@executable_path/../Modules/" + jaspModuleName + "/RcppParallel/lib/libtbb.dylib"} + // Known fix library id's and paths + std::map ids_to_be_replaced; + if(devMod) { + ids_to_be_replaced = { #ifndef __aarch64__ - ,{"libgfortran.dylib", framework_resources + "opt/local/gfortran/lib/libgfortran.dylib"} - ,{"libquadmath.dylib", framework_resources + "opt/local/gfortran/lib/libquadmath.dylib"} + ,{"libgfortran.dylib", framework_resources + "opt/local/gfortran/lib/libgfortran.dylib"} + ,{"libquadmath.dylib", framework_resources + "opt/local/gfortran/lib/libquadmath.dylib"} #endif - }; + }; + } + else { + ids_to_be_replaced = { + {"libtbbmalloc.dylib", "@executable_path/../Modules/" + jaspModuleName + "/RcppParallel/lib/libtbbmalloc.dylib"}, + {"libtbbmalloc_proxy.dylib", "@executable_path/../Modules/" + jaspModuleName + "/RcppParallel/lib/libtbbmalloc_proxy.dylib"}, + {"libtbb.dylib", "@executable_path/../Modules/" + jaspModuleName + "/RcppParallel/lib/libtbb.dylib"} +#ifndef __aarch64__ + ,{"libgfortran.dylib", framework_resources + "opt/local/gfortran/lib/libgfortran.dylib"} + ,{"libquadmath.dylib", framework_resources + "opt/local/gfortran/lib/libquadmath.dylib"} +#endif + }; + } auto install_name_tool_cmd = [&](const std::string & replaceThisLine, const std::string & withThisLine) { @@ -188,13 +195,16 @@ void _moduleLibraryFixer(const std::string & moduleLibraryPath, bool engineCall, } - std::cout << "Signing the modified library\n"; - const std::string sign_command = "codesign --force --deep --verbose=4 --timestamp --sign \"" + AppInfo::getSigningIdentity() + "\" " + path.string(); + if(!devMod) + { + std::cout << "Signing the modified library\n"; + const std::string sign_command = "codesign --force --deep --verbose=4 --timestamp --sign \"" + AppInfo::getSigningIdentity() + "\" " + path.string(); - if (printStuff) - std::cout << sign_command << std::endl; + if (printStuff) + std::cout << sign_command << std::endl; - _system(sign_command); + _system(sign_command); + } } } catch(std::filesystem::filesystem_error & error) diff --git a/Engine/otoolstuff.h b/Common/otoolstuff.h similarity index 84% rename from Engine/otoolstuff.h rename to Common/otoolstuff.h index af4b0ef772..246dfd6130 100644 --- a/Engine/otoolstuff.h +++ b/Common/otoolstuff.h @@ -7,6 +7,6 @@ /// Depends on otool being present and callable in the users system. std::string _system( std::string cmd); -void _moduleLibraryFixer(const std::string & moduleLibrary, bool engineCall = false, bool printStuff = false); +void _moduleLibraryFixer(const std::string & moduleLibrary, bool engineCall = false, bool printStuff = false, bool devMod = false); #endif // OTOOLSTUFF_H diff --git a/Desktop/analysis/analyses.cpp b/Desktop/analysis/analyses.cpp index d03f29690e..a3fb9a60ab 100644 --- a/Desktop/analysis/analyses.cpp +++ b/Desktop/analysis/analyses.cpp @@ -305,8 +305,7 @@ void Analyses::removeAnalysesOfDynamicModule(Modules::DynamicModule * module) void Analyses::refreshAnalysesOfDynamicModule(Modules::DynamicModule * module) { - //Log::log() << "void Analyses::refreshAnalysesOfDynamicModule(" << module->toString() << ")" << std::endl; - + Log::log() << "void Analyses::refreshAnalysesOfDynamicModule(" << module->toString() << ")" << std::endl; for(auto & keyval : _analysisMap) if(keyval.second->dynamicModule() == module) @@ -352,6 +351,13 @@ void Analyses::rescanAnalysisEntriesOfDynamicModule(Modules::DynamicModule * mod removeAnalysisById(size_t(id)); } +void Analyses::reloadQmlAnalysesDynamicModule(Modules::DynamicModule * module) +{ + for(auto idAnalysis : _analysisMap) + if(idAnalysis.second->dynamicModule() == module) + idAnalysis.second->analysisQMLFileChanged(); +} + void Analyses::refreshAllAnalyses() { for(auto idAnalysis : _analysisMap) diff --git a/Desktop/analysis/analyses.h b/Desktop/analysis/analyses.h index 6edc9d6c70..98c1e27575 100644 --- a/Desktop/analysis/analyses.h +++ b/Desktop/analysis/analyses.h @@ -124,6 +124,7 @@ public slots: void refreshAnalysesOfDynamicModule(Modules::DynamicModule * module); void replaceAnalysesOfDynamicModule(Modules::DynamicModule * oldModule, Modules::DynamicModule * newModule); void rescanAnalysisEntriesOfDynamicModule(Modules::DynamicModule * module); + void reloadQmlAnalysesDynamicModule(Modules::DynamicModule * module); void setChangedAnalysisTitle(); void analysisTitleChangedInResults(int id, QString title); void setCurrentFormPrevH(double currentFormPrevH); diff --git a/Desktop/components/JASP/Widgets/FileMenu/PrefsAdvanced.qml b/Desktop/components/JASP/Widgets/FileMenu/PrefsAdvanced.qml index f296dfe11e..1ec7065c6f 100644 --- a/Desktop/components/JASP/Widgets/FileMenu/PrefsAdvanced.qml +++ b/Desktop/components/JASP/Widgets/FileMenu/PrefsAdvanced.qml @@ -140,9 +140,6 @@ ScrollView KeyNavigation.tab: githubPatCustomToken } - - - Item { id: githubPatCustomTokenItem @@ -170,7 +167,7 @@ ScrollView text: preferencesModel.githubPatCustom onEditingFinished: preferencesModel.githubPatCustom = text - nextEl: generateMarkdown + nextEl: directLibpathDevModEnabled height: browseDeveloperFolderButton.height anchors @@ -182,7 +179,7 @@ ScrollView textInput.echoMode: TextInput.Password - KeyNavigation.tab: generateMarkdown + KeyNavigation.tab: directLibpathDevModEnabled } } @@ -197,6 +194,103 @@ ScrollView KeyNavigation.tab: cleanModulesFolder } + + + CheckBox + { + id: directLibpathDevModEnabled + label: qsTr("Enable direct libpath mode") + checked: preferencesModel.directLibpathEnabled + onCheckedChanged: preferencesModel.directLibpathEnabled = checked + toolTip: qsTr("To use JASP Modules enable this option.") + visible: preferencesModel.developerMode + + KeyNavigation.tab: directLibpathFolder + } + + Item + { + id: directLibpath + enabled: preferencesModel.directLibpathEnabled + width: parent.width + height: cranRepoUrl.height + visible: preferencesModel.developerMode && preferencesModel.directLibpathEnabled + + Label + { + id: directLibPathLabel + text: qsTr("DevModule libpath:") + + anchors + { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: jaspTheme.subOptionOffset + } + } + + PrefsTextInput + { + id: directLibpathFolder + + text: preferencesModel.directLibpathFolder + onEditingFinished: preferencesModel.directLibpathFolder = text + + nextEl: moduleName + + height: browseDeveloperFolderButton.height + anchors + { + left: directLibPathLabel.right + right: parent.right + margins: jaspTheme.generalAnchorMargin + } + + KeyNavigation.tab: moduleName + } + } + + Item { + + id: directDevMod + enabled: preferencesModel.directLibpathEnabled + width: parent.width + height: cranRepoUrl.height + visible: preferencesModel.developerMode && preferencesModel.directLibpathEnabled + + Label + { + id: directDevModName + text: qsTr("Developer module name:") + + anchors + { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: jaspTheme.subOptionOffset + } + } + + PrefsTextInput + { + id: moduleName + + text: preferencesModel.directDevModName + onEditingFinished: preferencesModel.directDevModName = text + + nextEl: cleanModulesFolder + + height: browseDeveloperFolderButton.height + anchors + { + left: directDevModName.right + right: parent.right + margins: jaspTheme.generalAnchorMargin + } + + KeyNavigation.tab: cleanModulesFolder + } + } RoundedButton { diff --git a/Desktop/components/JASP/Widgets/MainWindow.qml b/Desktop/components/JASP/Widgets/MainWindow.qml index fe50b22b9f..64fb401b95 100644 --- a/Desktop/components/JASP/Widgets/MainWindow.qml +++ b/Desktop/components/JASP/Widgets/MainWindow.qml @@ -109,6 +109,10 @@ Window Shortcut { onActivated: mainWindow.undo(); sequences: ["Ctrl+Z", Qt.Key_Undo]; context: Qt.ApplicationShortcut; } Shortcut { onActivated: mainWindow.redo(); sequences: ["Ctrl+Shift+Z", "Ctrl-Y", Qt.Key_Redo]; context: Qt.ApplicationShortcut; } + Shortcut { onActivated: { dynamicModules.refreshDeveloperModule(false, true);} sequences: ["Ctrl+Shift+U"]; context: Qt.ApplicationShortcut; } + Shortcut { onActivated: { dynamicModules.refreshDeveloperModule(true, false);} sequences: ["Ctrl+Shift+R"]; context: Qt.ApplicationShortcut; } + Shortcut { onActivated: { dynamicModules.refreshDeveloperModule(true, true);} sequences: ["Ctrl+Shift+D"]; context: Qt.ApplicationShortcut; } + RibbonBar { diff --git a/Desktop/components/JASP/Widgets/ModulesMenu.qml b/Desktop/components/JASP/Widgets/ModulesMenu.qml index f59f990d51..36d9697d1d 100644 --- a/Desktop/components/JASP/Widgets/ModulesMenu.qml +++ b/Desktop/components/JASP/Widgets/ModulesMenu.qml @@ -219,7 +219,7 @@ FocusScope focus: currentIndex === -1 activeFocusOnTab: false - readonly property bool folderSelected: preferencesModel.developerFolder != "" + readonly property bool folderSelected: preferencesModel.developerFolder != "" || (preferencesModel.directLibpathEnabled && preferencesModel.directLibpathFolder != "") } QTC.ToolSeparator @@ -265,11 +265,28 @@ FocusScope anchors { left : parent.left - right : minusButton.left + right : refreshButton.left verticalCenter : parent.verticalCenter } } + + MenuButton + { + z: 1 + id: refreshButton + visible: !isBundled && !isSpecial + iconSource: jaspTheme.iconPath + "/redo.svg" + width: visible ? height : 0 + onClicked: dynamicModules.refreshDeveloperModule(); + toolTip: qsTr("Refresh developer module ") + displayText + anchors + { + right : minusButton.left + verticalCenter : parent.verticalCenter + } + } + MenuButton { z: 1 diff --git a/Desktop/gui/preferencesmodel.cpp.in b/Desktop/gui/preferencesmodel.cpp.in index 2ee6fcc3c1..c233a78c85 100644 --- a/Desktop/gui/preferencesmodel.cpp.in +++ b/Desktop/gui/preferencesmodel.cpp.in @@ -138,6 +138,10 @@ GET_PREF_FUNC_BOOL( checkUpdates, Settings::CHECK_UPDATES ) GET_PREF_FUNC_INT( maxScaleLevels, Settings::MAX_SCALE_LEVELS ) GET_PREF_FUNC_BOOL( pdfLandscape, Settings::PDF_LANDSCAPE ) GET_PREF_FUNC_INT( pdfPageSize, Settings::PDF_PAGESIZE ) +GET_PREF_FUNC_BOOL( directLibpathEnabled, Settings::DIRECT_LIBPATH_ENABLED ) +GET_PREF_FUNC_STR( directLibpathFolder, Settings::DIRECT_LIBPATH_FOLDER ) +GET_PREF_FUNC_STR( directDevModName, Settings::DIRECT_DEVMOD_NAME ) + int PreferencesModel::maxEngines() const { @@ -314,6 +318,9 @@ SET_PREF_FUNCTION( bool, setCheckUpdates, checkUpdates, checkUpdatesCha SET_PREF_FUNCTION( int, setMaxScaleLevels, maxScaleLevels, maxScaleLevelsChanged, Settings::MAX_SCALE_LEVELS ) SET_PREF_FUNCTION( bool, setPdfLandscape, pdfLandscape, pdfLandscapeChanged, Settings::PDF_LANDSCAPE ) SET_PREF_FUNCTION( int, setPdfPageSize, pdfPageSize, pdfPageSizeChanged, Settings::PDF_PAGESIZE ) +SET_PREF_FUNCTION( bool, setDirectLibpathEnabled, directLibpathEnabled, directLibpathEnabledChanged, Settings::DIRECT_LIBPATH_ENABLED ) +SET_PREF_FUNCTION( QString, setDirectDevModName, directDevModName, directDevModNameChanged, Settings::DIRECT_DEVMOD_NAME ) + void PreferencesModel::setGithubPatCustom(QString newPat) { @@ -551,3 +558,12 @@ QStringList PreferencesModel::_splitValues(const QString &values) const return QStringList(orderedValues.begin(), orderedValues.end()); } + +void PreferencesModel::setDirectLibpathFolder(QString libpath) +{ + if (libpath != "") + { + Settings::setValue(Settings::DIRECT_LIBPATH_FOLDER, libpath); + emit directLibpathFolderChanged(); + } +} diff --git a/Desktop/gui/preferencesmodel.h b/Desktop/gui/preferencesmodel.h index 472dcba94b..e85fb4e51b 100644 --- a/Desktop/gui/preferencesmodel.h +++ b/Desktop/gui/preferencesmodel.h @@ -29,6 +29,9 @@ class PreferencesModel : public PreferencesModelBase Q_PROPERTY(int defaultPPI READ defaultPPI WRITE setDefaultPPI NOTIFY defaultPPIChanged ) Q_PROPERTY(bool developerMode READ developerMode WRITE setDeveloperMode NOTIFY developerModeChanged ) Q_PROPERTY(QString developerFolder READ developerFolder WRITE setDeveloperFolder NOTIFY developerFolderChanged ) + Q_PROPERTY(bool directLibpathEnabled READ directLibpathEnabled WRITE setDirectLibpathEnabled NOTIFY directLibpathEnabledChanged ) + Q_PROPERTY(QString directLibpathFolder READ directLibpathFolder WRITE setDirectLibpathFolder NOTIFY directLibpathFolderChanged ) + Q_PROPERTY(QString directDevModName READ directDevModName WRITE setDirectDevModName NOTIFY directDevModNameChanged ) Q_PROPERTY(int thresholdScale READ thresholdScale WRITE setThresholdScale NOTIFY thresholdScaleChanged ) Q_PROPERTY(bool logToFile READ logToFile WRITE setLogToFile NOTIFY logToFileChanged ) Q_PROPERTY(int logFilesMax READ logFilesMax WRITE setLogFilesMax NOTIFY logFilesMaxChanged ) @@ -146,7 +149,10 @@ class PreferencesModel : public PreferencesModelBase QVariantList pdfPageSizeModel() const { return _pdfPageSizeModel; } int pdfPageSize() const; bool pdfLandscape() const; - + bool directLibpathEnabled() const; + QString directLibpathFolder() const; + QString directDevModName() const; + bool checkUpdatesAskUser() const; void setCheckUpdatesAskUser(bool newCheckUpdatesAskUser); @@ -213,6 +219,9 @@ public slots: void setMaxScaleLevels( int maxScaleLevels); void setPdfPageSize( int pdfPageSize); void setPdfLandscape( bool pdfLandscape); + void setDirectLibpathEnabled( bool setDirectLibpathEnabled); + void setDirectLibpathFolder( QString libpath); + void setDirectDevModName( QString name); signals: void fixedDecimalsChanged( bool fixedDecimals); @@ -268,6 +277,9 @@ public slots: void maxScaleLevelsChanged( int maxScaleLevels); void pdfPageSizeChanged( int pdfPageSize); void pdfLandscapeChanged( bool pdfLandscape); + void directLibpathEnabledChanged( bool directLibpathEnabled); + void directLibpathFolderChanged(); + void directDevModNameChanged( QString name); private slots: void dataLabelNAChangedSlot(QString label); diff --git a/Desktop/mainwindow.cpp b/Desktop/mainwindow.cpp index 1d1e386b25..2223c3771b 100644 --- a/Desktop/mainwindow.cpp +++ b/Desktop/mainwindow.cpp @@ -557,6 +557,7 @@ void MainWindow::makeConnections() connect(_dynamicModules, &DynamicModules::dynamicModuleUnloadBegin, _analyses, &Analyses::removeAnalysesOfDynamicModule ); connect(_dynamicModules, &DynamicModules::dynamicModuleChanged, _analyses, &Analyses::refreshAnalysesOfDynamicModule ); + connect(_dynamicModules, &DynamicModules::dynamicModuleQmlChanged, _analyses, &Analyses::reloadQmlAnalysesDynamicModule ); connect(_dynamicModules, &DynamicModules::dynamicModuleReplaced, _analyses, &Analyses::replaceAnalysesOfDynamicModule, Qt::DirectConnection); connect(_dynamicModules, &DynamicModules::descriptionReloaded, _analyses, &Analyses::rescanAnalysisEntriesOfDynamicModule, Qt::QueuedConnection); connect(_dynamicModules, &DynamicModules::reloadHelpPage, _helpModel, &HelpModel::reloadPage ); diff --git a/Desktop/modules/dynamicmodule.cpp b/Desktop/modules/dynamicmodule.cpp index 5dcd7190fa..7e3bbca1d0 100644 --- a/Desktop/modules/dynamicmodule.cpp +++ b/Desktop/modules/dynamicmodule.cpp @@ -38,6 +38,11 @@ #include "utilities/qmlutils.h" #include "mainwindow.h" +#ifdef __APPLE__ +#include "otoolstuff.h" +#include +#endif + namespace Modules { @@ -100,6 +105,25 @@ DynamicModule::DynamicModule(QObject * parent) : QObject(parent), _isDeveloperMo } +///This constructor is meant specifically for the development module from a libpath *it*! +DynamicModule::DynamicModule(QObject * parent, QString libpath) : QObject(parent), _isDeveloperMod(true), _isLibpathDevMod(true) +{ + libpath = patchLibPathHelperFunc(libpath); + _modulePackage = fq(libpath + "/" + Settings::value(Settings::DIRECT_DEVMOD_NAME).toString() + "/"); + _moduleFolder = QFileInfo(libpath + "/"); + _name = extractPackageNameFromFolder(_modulePackage); + + if(_name == "") _name = defaultDevelopmentModuleName(); + + Log::log() << "Development Module is constructed with name: '" << _name << "' and will intialized from libpath: " << _moduleFolder.absoluteFilePath().toStdString() << std::endl; + + _developmentModuleName = _name; + + loadDescriptionFromFolder(_modulePackage); + setInstalled(true); +} + + void DynamicModule::initialize() { @@ -112,7 +136,6 @@ void DynamicModule::initialize() // else if(!_moduleFolder.isWritable()) throw std::runtime_error(_moduleFolder.absolutePath().toStdString() + " is not writable!"); setInitialized(true); - auto checkForExistence = [&](std::string name, bool isFile = false) { QString modPath = _moduleFolder.absolutePath() + "/" + nameQ() + "/" + QString::fromStdString(name); @@ -499,7 +522,7 @@ std::string DynamicModule::generateModuleUninstallingR() std::string DynamicModule::moduleInstFolder() const { //Because in a developer mod everything is loaded directly from the source folder we give an actual inst folder: - if(_isDeveloperMod) return _modulePackage + "/inst"; + if(_isDeveloperMod && !_isLibpathDevMod) return _modulePackage + "/inst"; //But after install this is in a R-library and therefore there is no more inst folder: else return moduleRLibrary().toStdString() + "/" + _name + "/"; } @@ -917,5 +940,23 @@ stringset DynamicModule::requiredModules() const return out; } +QString DynamicModule::patchLibPathHelperFunc(QString libpath) { +#ifdef __APPLE__ + //we copy everything because we need to patch and resign it all + auto path = std::filesystem::temp_directory_path() / Settings::value(Settings::DIRECT_DEVMOD_NAME).toString().toStdString(); + std::filesystem::remove_all(path); + std::filesystem::copy(libpath.toStdString(), path, std::filesystem::copy_options::recursive); + _moduleLibraryFixer(path, true, true, true); + return tq(path.generic_string()); +#else + return libpath; +#endif + +} + } + + + + diff --git a/Desktop/modules/dynamicmodule.h b/Desktop/modules/dynamicmodule.h index 329ec46196..3c051d51c6 100644 --- a/Desktop/modules/dynamicmodule.h +++ b/Desktop/modules/dynamicmodule.h @@ -78,6 +78,10 @@ class DynamicModule : public QObject ///This constructor is meant specifically for the development module and only *it*! explicit DynamicModule(QObject * parent); + ///This constructor is meant specifically for development modules initialized form a libpaths + explicit DynamicModule(QObject *parent, QString libpath); + + ~DynamicModule() override { for(auto * entry : _menuEntries) @@ -165,6 +169,7 @@ class DynamicModule : public QObject bool installing() const { return _installing; } bool initialized() const { return _initialized; } bool isBundled() const { return _bundled; } + bool isLibpathDevMod() const { return _isLibpathDevMod; } void initialize(); void loadDescriptionQml(const QString & descriptionTxt, const QUrl & url); @@ -242,6 +247,7 @@ public slots: bool _installing = false, _installed = false, _isDeveloperMod = false, + _isLibpathDevMod = false, _initialized = false, _bundled = false, _isCommon = false, @@ -253,6 +259,8 @@ public slots: static std::string _developmentModuleName; static const std::string _moduleDirPostfix; + + QString patchLibPathHelperFunc(QString libpath); }; diff --git a/Desktop/modules/dynamicmodules.cpp b/Desktop/modules/dynamicmodules.cpp index c4e23fe140..9b8189fb18 100644 --- a/Desktop/modules/dynamicmodules.cpp +++ b/Desktop/modules/dynamicmodules.cpp @@ -36,6 +36,7 @@ #include "modules/upgrader/changeincompatible.h" #include "modules/description/description.h" #include "modules/description/entrybase.h" +#include "engine/enginesync.h" namespace Modules { @@ -298,7 +299,7 @@ void DynamicModules::uninstallModule(const std::string & moduleName) unloadModule(moduleName); _modules[moduleName]->setInstalled(false); - if(_modules[moduleName]->isBundled()) + if(_modules[moduleName]->isBundled() || _modules[moduleName]->isLibpathDevMod()) removeFolder = false; for(int i=int(_moduleNames.size()) - 1; i>=0; i--) @@ -527,16 +528,31 @@ void DynamicModules::uninstallJASPDeveloperModule() uninstallModule(developmentModuleName()); } +void DynamicModules::refreshDeveloperModule(bool R, bool Qml) +{ + if(_modules.count(developmentModuleName())) { + EngineSync::singleton()->killModuleEngine(_modules[developmentModuleName()]); + if(R && Qml) + installJASPDeveloperModule(); + else if(R) + emit dynamicModuleChanged(_modules[developmentModuleName()]); + else if(Qml) + emit dynamicModuleQmlChanged(_modules[developmentModuleName()]); + } +} + void DynamicModules::installJASPDeveloperModule() { - if(Settings::value(Settings::DEVELOPER_FOLDER).toString() == "") + bool directLibpathEnabled = Settings::value(Settings::DIRECT_LIBPATH_ENABLED).toBool(); + QString modulePath = directLibpathEnabled ? Settings::value(Settings::DIRECT_LIBPATH_FOLDER).toString() : Settings::value(Settings::DEVELOPER_FOLDER).toString(); + if(modulePath == "") { MessageForwarder::showWarning(tr("Select a folder"), tr("To install a development module you need to select the folder you want to watch and load, you can do this under the filemenu, Preferences->Advanced.")); return; } - else if(!QDir(Settings::value(Settings::DEVELOPER_FOLDER).toString()).exists()) + else if(!QDir(modulePath).exists()) { - MessageForwarder::showWarning(tr("Select an exisiting folder"), tr("To install a development module you need to select and existing folder, you selected '$1' but it doesn't exist.").arg(Settings::value(Settings::DEVELOPER_FOLDER).toString())); + MessageForwarder::showWarning(tr("Select an exisiting folder"), tr("To install a development module you need to select and existing folder, you selected '$1' but it doesn't exist.").arg(modulePath)); return; } @@ -544,12 +560,7 @@ void DynamicModules::installJASPDeveloperModule() try { - - _devModSourceDirectory = QDir(Settings::value(Settings::DEVELOPER_FOLDER).toString()); - - DynamicModule * devMod = new DynamicModule(this); - - devMod->loadDescriptionFromFolder(fq(_devModSourceDirectory.absolutePath())); + DynamicModule * devMod = directLibpathEnabled ? new DynamicModule(this, modulePath) : new DynamicModule(this); std::string origin = devMod->modulePackage(), name = devMod->name(), @@ -562,11 +573,14 @@ void DynamicModules::installJASPDeveloperModule() else if(_modules.count(name) > 0 && _modules[name] != devMod) replaceModule(devMod); - DynamicModule::developmentModuleFolderCreate(); - _modules[name] = devMod; - - registerForInstalling(name); + if(directLibpathEnabled) { + initializeModule(devMod); + } + else { + DynamicModule::developmentModuleFolderCreate(); + registerForInstalling(name); + } } catch(ModuleException & e) { @@ -602,8 +616,14 @@ void DynamicModules::startWatchingDevelopersModule() if(dir == "icons") iconsFound = true; } } + else if(entry.isDir() && entry.fileName().toLower() == "qml") + qmlFound = true; + else if(entry.isDir() && entry.fileName().toLower() == "icons") + iconsFound = true; else if(entry.isDir() && entry.fileName().toUpper() == "R") rFound = true; + else if(entry.isFile() && DynamicModule::isDescriptionFile(entry.fileName())) + descFound = entry.fileName(); if(!(descFound != "" && rFound && qmlFound && iconsFound)) { @@ -755,7 +775,10 @@ void DynamicModules::regenerateDeveloperModuleRPackage() throw std::runtime_error("void DynamicModules::regenerateDeveloperModuleRPackage() called but the development module is not initialized..."); auto * devMod = _modules[developmentModuleName()]; - devMod->setStatus(moduleStatus::installModPkgNeeded); + if(devMod->isLibpathDevMod()) + emit dynamicModuleChanged(devMod); + else + devMod->setStatus(moduleStatus::installModPkgNeeded); } QString DynamicModules::moduleDirectoryQ(const QString & moduleName) const diff --git a/Desktop/modules/dynamicmodules.h b/Desktop/modules/dynamicmodules.h index 092a112556..9cd826519e 100644 --- a/Desktop/modules/dynamicmodules.h +++ b/Desktop/modules/dynamicmodules.h @@ -120,6 +120,7 @@ public slots: void setDevelopersModuleInstallButtonEnabled(bool developersModuleInstallButtonEnabled); void setDataLoaded(bool dataLoaded); void uninstallJASPDeveloperModule(); + void refreshDeveloperModule(bool R = true, bool Qml = true); QStringList requiredModulesLibPaths(QString moduleName); @@ -129,6 +130,7 @@ public slots: void dynamicModuleAdded( Modules::DynamicModule * dynamicModule); void dynamicModuleUnloadBegin( Modules::DynamicModule * dynamicModule); void dynamicModuleChanged( Modules::DynamicModule * dynamicModule); + void dynamicModuleQmlChanged( Modules::DynamicModule * dynamicModule); void descriptionReloaded( Modules::DynamicModule * dynamicModule); void loadModuleTranslationFile( Modules::DynamicModule * dynamicModule); void dynamicModuleReplaced( Modules::DynamicModule * oldMod, Modules::DynamicModule * newMod); diff --git a/Desktop/utilities/settings.cpp b/Desktop/utilities/settings.cpp index d267d65511..6ae6d33e2c 100644 --- a/Desktop/utilities/settings.cpp +++ b/Desktop/utilities/settings.cpp @@ -95,7 +95,10 @@ const Settings::Setting Settings::Values[] = { {"checkUpdatesLastTime", -1 }, {"maxScaleLevels", 100 }, {"pdfLandscape", false }, - {"pdfPageSize", int(pdfPageSize::A4) } + {"pdfPageSize", int(pdfPageSize::A4) }, + {"directLibpathEnabled", false }, + {"directLibpathFolder", "" }, + {"directDevModName", "" } }; diff --git a/Desktop/utilities/settings.h b/Desktop/utilities/settings.h index 4adbdf1598..7b11d2c06d 100644 --- a/Desktop/utilities/settings.h +++ b/Desktop/utilities/settings.h @@ -79,7 +79,10 @@ class Settings { LAST_CHECK, MAX_SCALE_LEVELS, PDF_LANDSCAPE, - PDF_PAGESIZE + PDF_PAGESIZE, + DIRECT_LIBPATH_ENABLED, + DIRECT_LIBPATH_FOLDER, + DIRECT_DEVMOD_NAME }; static QVariant value(Settings::Type key);