diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000000..f419eacb3d --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,65 @@ +FROM mcr.microsoft.com/devcontainers/cpp:ubuntu-24.04 + +ARG DEBIAN_FRONTEND=noninteractive +ARG QT_VERSION=6.8.3 +ARG QT_MODULES="qtpositioning qtwebchannel qtwebengine qtwebsockets qtwebview debug_info qt5compat qtshadertools qtwaylandcompositor" + +# 1) Base OS packages per Docs/development/jasp-build-guide-linux.md +RUN apt-get update && apt-get install -y --no-install-recommends \ + ca-certificates curl git gnupg software-properties-common \ + build-essential cmake ninja-build \ + autoconf bison flex \ + g++ gfortran \ + zlib1g zlib1g-dev \ + libssl-dev \ + libgl1-mesa-dev \ + libsqlite3-dev \ + libarchive13 libarchive-dev \ + libxkbcommon-dev libxkbcommon-x11-dev libxcb-xkb-dev libxcb-xinerama0 libxcb-cursor0 \ + libglpk-dev \ + libminizip-dev \ + libfreexl-dev \ + libboost-dev libboost-filesystem-dev libboost-system-dev libboost-date-time-dev libboost-timer-dev libboost-chrono-dev \ + librdata-dev jags \ + libnss3-dev libnspr4-dev libxcomposite-dev libxdamage-dev libxrandr-dev libxtst-dev libxi-dev libasound2-dev libxkbfile-dev libxcb-icccm4-dev libxcb-shape0-dev libxcb-keysyms1-dev \ + libjsoncpp25 libjsoncpp-dev libxcb-xkb1 \ + r-base \ + libfontconfig1-dev libharfbuzz-dev libfribidi-dev libfreetype6-dev libpng-dev libtiff5-dev libjpeg-dev libwebp-dev \ + libcairo2-dev libgsl-dev \ + fonts-noto-cjk fonts-arphic-ukai fonts-arphic-uming fonts-wqy-zenhei fonts-wqy-microhei \ + patchelf \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +# 2) Install R from CRAN Ubuntu repo for a recent R (or use distro one if sufficient) +RUN set -eux; \ + . /etc/os-release; \ + echo "deb https://cloud.r-project.org/bin/linux/ubuntu ${UBUNTU_CODENAME}-cran40/" | tee /etc/apt/sources.list.d/cran-r.list; \ + curl -fsSL https://cloud.r-project.org/bin/linux/ubuntu/marutter_pubkey.asc | gpg --dearmor -o /etc/apt/trusted.gpg.d/cran.gpg; \ + apt-get update && apt-get install -y --no-install-recommends r-base r-base-dev && rm -rf /var/lib/apt/lists/* + +# 3) JAGS (use distro for convenience; can be replaced by source build later) +RUN apt-get update && apt-get install -y --no-install-recommends jags \ + && rm -rf /var/lib/apt/lists/* + +# 4) Python + pip tooling for aqtinstall (Qt online installer alternative) +RUN apt-get update && apt-get install -y --no-install-recommends python3 python3-pip python3-venv && rm -rf /var/lib/apt/lists/* + +# 5) Create venv for aqtinstall and install aqtinstall there +RUN python3 -m venv /opt/aqt-venv \ + && /opt/aqt-venv/bin/pip install --no-cache-dir aqtinstall + +# 6) Install Qt 6.7.x with required modules via aqt from venv +RUN set -eux; \ + /opt/aqt-venv/bin/aqt install-qt -O /opt/Qt linux desktop ${QT_VERSION} linux_gcc_64 -m ${QT_MODULES}; \ + ln -s /opt/Qt/${QT_VERSION}/linux_gcc_64/bin/qmake /usr/local/bin/qmake || true + +# 7) Install Qt Creator and other tools +RUN /opt/aqt-venv/bin/aqt install-tool -O /opt/Qt linux desktop tools_qtcreator qt.tools.qtcreator && \ + /opt/aqt-venv/bin/aqt install-tool -O /opt/Qt linux desktop tools_cmake && \ + /opt/aqt-venv/bin/aqt install-tool -O /opt/Qt linux desktop tools_ninja + +# 8) Install readstat +RUN wget https://github.com/WizardMac/ReadStat/releases/download/v1.1.9/readstat-1.1.9.tar.gz && \ + tar -xzf readstat-*.tar.gz && cd readstat-*/ && \ + ./configure && make CFLAGS='-Wno-error=use-after-free' CXXFLAGS='-Wno-error=use-after-free' && make install diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000000..bd070bc547 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,16 @@ +{ + "name": "JASP Desktop Dev", + "build": { + "dockerfile": "Dockerfile" + }, + "runArgs": [ + "--shm-size=2g" + ], + "customizations": { + "vscode": { + "extensions": [ + "TheQtCompany.qt-cpp-pack" + ] + } + } +} diff --git a/.github/workflows/ubuntu.yml b/.github/workflows/ubuntu.yml index 213d883723..5b50312e80 100644 --- a/.github/workflows/ubuntu.yml +++ b/.github/workflows/ubuntu.yml @@ -6,7 +6,12 @@ on: workflow_dispatch: jobs: - + # to run locally install https://github.com/nektos/act and run `act --reuse -j build-jasp --env DISPLAY=$DISPLAY ` + # docker commit 484f3a622a13 my-jasp-image:latest + # run desktop with + # xhost +local:docker + # docker exec -it -e DISPLAY=$DISPLAY -v /tmp/.X11-unix:/tmp/.X11-unix --entrypoint bash my-jasp-image:latest + # ./jasp-build/Desktop/JASP build-jasp: runs-on: ubuntu-latest @@ -33,22 +38,31 @@ jobs: cache: 'true' modules: 'qtpositioning qtwebchannel qtwebengine qtwebsockets qtwebview debug_info qt5compat qtshadertools qtwaylandcompositor' tools: 'tools_qtcreator,qt.tools.qtcreator tools_cmake tools_ninja' + # act can not use ../ (the default) as dir, so be explicit + dir: '${{ github.workspace }}/qtinstall' - name: Install dependencies of build system - run: | - sudo apt install libboost-dev libjsoncpp25 libjsoncpp-dev libarchive13 libarchive-dev - sudo apt install libxcb-xkb-dev libxcb-xkb1 libxcb-xinerama0 libxkbcommon-dev libxkbcommon-x11-dev autoconf zlib1g zlib1g-dev cmake - sudo apt install gfortran build-essential flex libssl-dev libgl1-mesa-dev libsqlite3-dev - sudo apt install libharfbuzz-dev libfribidi-dev libfreetype6-dev libpng-dev libtiff5-dev libjpeg-dev #required by some r packgaes - sudo apt install libglpk-dev libcurl4-openssl-dev libmpfr-dev libfontconfig1-dev libcairo2-dev netcdf-bin #required by some r packages - sudo apt install jags - sudo apt install libminizip-dev # required by freexl - sudo apt install librdata-dev + run: | + # act -j build-jasp was stuck on typing y to continue + sudo apt install -y libboost-dev libjsoncpp25 libjsoncpp-dev libarchive13 libarchive-dev + sudo apt install -y libxcb-xkb-dev libxcb-xkb1 libxcb-xinerama0 libxkbcommon-dev libxkbcommon-x11-dev autoconf zlib1g zlib1g-dev cmake + sudo apt install -y gfortran build-essential flex libssl-dev libgl1-mesa-dev libsqlite3-dev + sudo apt install -y libharfbuzz-dev libfribidi-dev libfreetype6-dev libpng-dev libtiff5-dev libjpeg-dev #required by some r packgaes + sudo apt install -y libglpk-dev libcurl4-openssl-dev libmpfr-dev libfontconfig1-dev libcairo2-dev netcdf-bin #required by some r packages + sudo apt install -y jags + sudo apt install -y libminizip-dev # required by freexl + sudo apt install -y librdata-dev + # to solve qt missing so files. + sudo apt install -y libnss3-dev libnspr4-dev + sudo apt install -y libxcomposite-dev libxdamage-dev libxrandr-dev libxtst-dev libxi-dev + sudo apt install -y libasound2-dev libxkbfile-dev git clone https://github.com/jasp-stats/freexl.git cd freexl && ./configure && make && sudo make install + env: + DEBIAN_FRONTEND: noninteractive - name: Install boost - uses: MarkusJx/install-boost@v2.4.4 + uses: MarkusJx/install-boost@v2 id: install-boost with: # REQUIRED: Specify the required boost version @@ -77,4 +91,5 @@ jobs: - name: Build JASP desktop run: | - cmake --build jasp-build --target all + # speed up build with -j + cmake --build jasp-build --target all -j6 diff --git a/.gitignore b/.gitignore index 7412e66744..3514e22cd0 100644 --- a/.gitignore +++ b/.gitignore @@ -63,6 +63,7 @@ build*/ bundle/ bundle*/ CMakeLists.txt.user +jasp-build # CLion Stuff cmake-build-* @@ -89,4 +90,4 @@ Tools/ModuleBundleBuildDir Modules/local # User configurations -.qmlls.ini \ No newline at end of file +.qmlls.ini diff --git a/Common/enginedefinitions.h b/Common/enginedefinitions.h index dac03d82eb..428171abb2 100644 --- a/Common/enginedefinitions.h +++ b/Common/enginedefinitions.h @@ -8,10 +8,10 @@ /// Using enumutilities templates to make sure we can easily and quickly go from enum -> string -> enum for json communication /// -DECLARE_ENUM(engineState, initializing, idle, analysis, filter, filterByName, rCode, computeColumn, moduleInstallRequest, moduleLoadRequest, pauseRequested, paused, resuming, stopRequested, stopped, logCfg, settings, killed, reloadData); +DECLARE_ENUM(engineState, initializing, idle, analysis, filter, filterByName, rCode, computeColumn, moduleInstallRequest, moduleUninstallRequest, moduleLoadRequest, pauseRequested, paused, resuming, stopRequested, stopped, logCfg, settings, killed, reloadData); DECLARE_ENUM(performType, run, abort, saveImg, editImg, rewriteImgs); DECLARE_ENUM(analysisResultStatus, validationError, fatalError, imageSaved, imageEdited, imagesRewritten, complete, running, changed, waiting); -DECLARE_ENUM(moduleStatus, initializing, installNeeded, loading, readyForUse, error); +DECLARE_ENUM(moduleStatus, initializing, installNeeded, uninstallNeeded, loading, readyForUse, error); DECLARE_ENUM(engineAnalysisStatus, empty, toRun, running, changed, complete, error, exception, aborted, stopped, saveImg, editImg, rewriteImgs, synchingData); DECLARE_ENUM(enginesListRoles, channel = 257, module, engineState, analysisStatus, runsWhat, running, idle, idleSoon); //hardcoded Qt::UserRole + 1, sue me. diff --git a/Desktop/components/JASP/Widgets/FileMenu/PrefsAdvanced.qml b/Desktop/components/JASP/Widgets/FileMenu/PrefsAdvanced.qml index 713aa14916..d6ebbb65e7 100644 --- a/Desktop/components/JASP/Widgets/FileMenu/PrefsAdvanced.qml +++ b/Desktop/components/JASP/Widgets/FileMenu/PrefsAdvanced.qml @@ -77,7 +77,6 @@ PrefsScrollView } } - CheckBox { id: githubPatDefault @@ -152,10 +151,50 @@ PrefsScrollView onCheckedChanged: preferencesModel.generateMarkdown = checked visible: preferencesModel.developerMode enabled: preferencesModel.developerMode - KeyNavigation.tab: cleanModulesFolder + KeyNavigation.tab: moduleLibraryUrl } + Item + { + id: moduleLibraryUrlItem + width: parent.width + height: moduleLibraryUrl.height + visible: preferencesModel.developerMode + enabled: preferencesModel.developerMode + + Label + { + id: moduleLibraryUrlLabel + text: qsTr("Module library URL: ") + + anchors + { + left: parent.left + verticalCenter: parent.verticalCenter + margins: jaspTheme.generalAnchorMargin + } + } + + PrefsTextInput + { + id: moduleLibraryUrl + + text: preferencesModel.moduleLibraryURL + onEditingFinished: preferencesModel.moduleLibraryURL = text + nextEl: cleanModulesFolder + + height: browseDeveloperFolderButton.height + anchors + { + left: moduleLibraryUrlLabel.right + right: parent.right + margins: jaspTheme.generalAnchorMargin + } + + KeyNavigation.tab: cleanModulesFolder + } + } RoundedButton { diff --git a/Desktop/components/JASP/Widgets/ModulesMenu.qml b/Desktop/components/JASP/Widgets/ModulesMenu.qml index 44b8c602a2..31a328d1e6 100644 --- a/Desktop/components/JASP/Widgets/ModulesMenu.qml +++ b/Desktop/components/JASP/Widgets/ModulesMenu.qml @@ -2,6 +2,8 @@ import QtQuick import QtQuick.Controls as QTC import QtQuick.Layouts import JASP.Controls +import QtWebEngine +import QtWebChannel import "./FileMenu" FocusScope @@ -71,7 +73,7 @@ FocusScope { id: slidePart x: modulesMenu.opened ? 0 : width - width: 340 * preferencesModel.uiScale + width: modulesFlick.width + vertScroller.width + moduleStoreContainer.width + 2 * jaspTheme.contentMargin height: modulesMenu.height color: jaspTheme.fileMenuColorBackground border.width: 1 @@ -117,6 +119,127 @@ FocusScope extraSpace: modulesFlick.contentY } + WebEngineProfile { + id: moduleStoreProfile + downloadPath: jaspTmpDir + + onDownloadRequested: function(request) { + console.log("Download requested:", request.url) + let name = request.downloadFileName + let index = name.lastIndexOf('.'); + let extension = index !== -1 ? name.substring(index + 1) : ''; + if(extension === 'JASPModule') { + moduleStore.downloadInProgress = true + moduleStore.downloadTotal = request.totalBytes + moduleStore.downloadProgress = Qt.binding(function() { return request.receivedBytes; }) + moduleStore.currentDownloadRequest = request + request.accept() + } + else + request.cancel() + } + + onDownloadFinished: function(request) { + moduleStore.downloadInProgress = false + moduleStore.currentDownloadRequest = null + if (request.state !== WebEngineDownloadRequest.DownloadCompleted) { + console.log("Download interrupted:", request.interruptReasonString) + return + } + console.log("Download finished:", request.downloadFileName) + let path = request.downloadDirectory + '/' + request.downloadFileName + moduleLibrary.startInstalling() + dynamicModules.installJASPModule(path) + } + } + + Item + { + id: moduleStoreContainer + visible: !ribbonModel.dataMode + clip: true + width: visible ? 500 * preferencesModel.uiScale : 0 + anchors + { + top: modulesFlick.top + right: modulesFlick.left + bottom: modulesFlick.bottom + margins: jaspTheme.contentMargin + } + + WebEngineView + { + id: moduleStore + visible: preferencesModel.checkUpdates + anchors.fill: parent + url: preferencesModel.checkUpdates ? preferencesModel.moduleLibraryURL : "about:blank" + profile: moduleStoreProfile + + property bool downloadInProgress: false; + property bool installInProgress: false; + property int downloadProgress; + property int downloadTotal; + property var currentDownloadRequest: null; + + webChannel.registeredObjects: [ moduleStoreWebChannel ] + + QtObject { + id: moduleStoreWebChannel + WebChannel.id: "moduleStore" + + function info() { + return moduleLibrary.getEnvironmentInfo(); + } + + signal environmentInfoChanged(var environmentInfo) + + Component.onCompleted: moduleLibrary.environmentInfoChanged.connect(moduleStoreWebChannel.environmentInfoChanged) + Component.onDestruction: moduleLibrary.environmentInfoChanged.disconnect(moduleStoreWebChannel.environmentInfoChanged) + + function uninstall(moduleName) { + moduleLibrary.uninstallJASPModule(moduleName) + } + } + } + + Rectangle + { + id: checkUpdatesDisabledMessage + visible: !preferencesModel.checkUpdates + anchors.fill: parent + color: jaspTheme.uiBackground + border.width: 1 + border.color: jaspTheme.uiBorder + + Column + { + anchors.centerIn: parent + anchors.margins: 20 * preferencesModel.uiScale + width: parent.width - 40 * preferencesModel.uiScale + spacing: 10 * preferencesModel.uiScale + + Text + { + text: qsTr("Not allowed to show the module library to install modules") + width: parent.width + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + font: jaspTheme.fontGroupTitle + color: jaspTheme.textEnabled + } + + Text + { + width: parent.width + wrapMode: Text.WordWrap + horizontalAlignment: Text.AlignHCenter + text: qsTr("In \"Preferences\" > \"Interface\" the \"Check for updates\" option is turned off. Please turn it on to see the module library.") + font: jaspTheme.font + color: jaspTheme.textEnabled + } + } + } + } Flickable @@ -125,12 +248,12 @@ FocusScope flickableDirection: Flickable.VerticalFlick contentHeight: workspaceSpecs.visible ? workspaceSpecs.height : modules.height contentWidth: width + width: visible ? 340 * preferencesModel.uiScale : 0 anchors { top: parent.top - topMargin: 5 * preferencesModel.uiScale - left: parent.left + margins: jaspTheme.contentMargin right: vertScroller.left bottom: parent.bottom } @@ -139,7 +262,7 @@ FocusScope { id: workspaceSpecs spacing: jaspTheme.rowSpacing - width: slidePart.width - vertScroller.width + width: modulesFlick.width visible: ribbonModel.dataMode MenuHeader @@ -203,8 +326,9 @@ FocusScope { id: modules spacing: 4 * preferencesModel.uiScale - width: slidePart.width - vertScroller.width + width: modulesFlick visible: !ribbonModel.dataMode + anchors.right: vertScroller.visible ? vertScroller.left : parent.right property int buttonMargin: 3 * preferencesModel.uiScale property int buttonWidth: width - (buttonMargin * 2) @@ -213,7 +337,7 @@ FocusScope MenuButton { id: addModuleButton - text: qsTr("Install Module") + text: qsTr("Install Local Module") width: modules.buttonWidth height: modules.buttonHeight anchors.leftMargin: modules.buttonMargin @@ -222,7 +346,7 @@ FocusScope iconSource: jaspTheme.iconPath + "/install_icon.png" // icon from https://icons8.com/icon/set/install/cotton showIconAndText: true iconLeft: false - toolTip: qsTr("Install a module") + toolTip: qsTr("Install a local module") visible: preferencesModel.developerMode focus: currentIndex === -2 activeFocusOnTab: false @@ -352,9 +476,12 @@ FocusScope } } } - } + } } - } + + + + } JASPScrollBar { @@ -372,6 +499,89 @@ FocusScope focus: true + // Progress bar overlay for download and installation + Rectangle + { + id: progressOverlay + anchors.fill: parent + color: jaspTheme.grayDarker + visible: moduleStore.downloadInProgress || moduleLibrary.isInstalling + z: 10 + + Column + { + anchors.centerIn: parent + spacing: 10 * preferencesModel.uiScale + width: 300 * preferencesModel.uiScale + + Text + { + id: progressText + text: moduleStore.downloadInProgress ? qsTr("Downloading module...") : qsTr("Installing module...") + color: "white" + font.pixelSize: 16 * preferencesModel.uiScale + anchors.horizontalCenter: parent.horizontalCenter + } + + // TODO show progress of installation + Rectangle + { + id: progressBarBackground + width: parent.width + height: 30 * preferencesModel.uiScale + color: jaspTheme.grayDarker + border.color: jaspTheme.uiBorder + border.width: 1 + radius: 3 + visible: moduleStore.downloadInProgress + + Rectangle + { + id: progressBarFill + width: moduleStore.downloadTotal > 0 ? (parent.width * moduleStore.downloadProgress / moduleStore.downloadTotal) : 0 + height: parent.height + color: jaspTheme.blue + radius: parent.radius + + Behavior on width + { + enabled: preferencesModel.animationsOn + PropertyAnimation { duration: 100 } + } + } + + Text + { + anchors.centerIn: parent + text: moduleStore.downloadTotal > 0 ? Math.round((moduleStore.downloadProgress / moduleStore.downloadTotal) * 100) + "%" : "0%" + color: "white" + font.pixelSize: 12 * preferencesModel.uiScale + } + } + + RoundedButton + { + id: cancelButton + text: qsTr("Cancel") + width: 120 * preferencesModel.uiScale + height: 30 * preferencesModel.uiScale + anchors.horizontalCenter: parent.horizontalCenter + // TODO also allow to cancel installation + visible: moduleStore.downloadInProgress + + onClicked: + { + if (moduleStore.currentDownloadRequest !== null) { + moduleStore.currentDownloadRequest.cancel() + } + moduleStore.downloadInProgress = false + moduleStore.currentDownloadRequest = null + } + toolTip: qsTr("Cancel download") + } + } + } + Item { id: dropShadow diff --git a/Desktop/engine/enginerepresentation.cpp b/Desktop/engine/enginerepresentation.cpp index 0bddb03858..59684ff6d5 100644 --- a/Desktop/engine/enginerepresentation.cpp +++ b/Desktop/engine/enginerepresentation.cpp @@ -313,6 +313,7 @@ void EngineRepresentation::processReplies() case engineState::paused: processEnginePausedReply(); break; case engineState::resuming: processEngineResumedReply(json); break; case engineState::stopped: processEngineStoppedReply(); break; + case engineState::moduleUninstallRequest: case engineState::moduleInstallRequest: case engineState::moduleLoadRequest: processModuleRequestReply(json); break; case engineState::logCfg: processLogCfgReply(); break; @@ -934,6 +935,16 @@ void EngineRepresentation::runModuleInstallRequestOnProcess(Json::Value request) sendString(request); } +void EngineRepresentation::runModuleUnInstallRequestOnProcess(Json::Value request) +{ + setState(engineState::moduleUninstallRequest); + request["typeRequest"] = engineStateToString(_engineState); + + _requestModName = request["moduleName"].asString(); + + sendString(request); +} + void EngineRepresentation::runModuleLoadRequestOnProcess(Json::Value request) { _moduleLoaded = false; @@ -968,6 +979,11 @@ void EngineRepresentation::processModuleRequestReply(Json::Value & json) else emit moduleInstallationFailed(moduleName, getError()); break; + case moduleStatus::uninstallNeeded: + if(succes) emit moduleUninstallationSucceeded(result); + else emit moduleUninstallationFailed(moduleName, getError()); + break; + case moduleStatus::loading: if(succes) { emit moduleLoadingSucceeded(moduleName, channelNumber()); _dynModName = fq(moduleName); _moduleLoaded = true; } else { emit moduleLoadingFailed(moduleName, getError(), channelNumber()); _dynModName = ""; } diff --git a/Desktop/engine/enginerepresentation.h b/Desktop/engine/enginerepresentation.h index 730167b31f..25b67e8633 100644 --- a/Desktop/engine/enginerepresentation.h +++ b/Desktop/engine/enginerepresentation.h @@ -44,6 +44,7 @@ class EngineRepresentation : public QObject void runScriptOnProcess( RComputeColumnStore * computeColumnStore); void runModuleInstallRequestOnProcess( Json::Value request); + void runModuleUnInstallRequestOnProcess( Json::Value request); void runModuleLoadRequestOnProcess( Json::Value request); void sendLogCfg(); @@ -79,7 +80,8 @@ class EngineRepresentation : public QObject bool killed() const { return _engineState == engineState::killed; } bool idle() const { return _engineState == engineState::idle; } bool installingModule() const { return _engineState == engineState::moduleInstallRequest; } - bool reloadingData() const { return _engineState == engineState::reloadData; } + bool unInstallingModule() const { return _engineState == engineState::moduleUninstallRequest; } + bool reloadingData() const { return _engineState == engineState::reloadData; } bool moduleLoading() const { return _engineState == engineState::moduleLoadRequest; } bool idleSoon() const; bool shouldSendSettings() const { return idle() && _settingsChanged; } @@ -164,7 +166,8 @@ public slots: void moduleLoadingSucceeded( const QString & moduleName, int channelID); void moduleLoadingFailed( const QString & moduleName, const QString & errorMessage, int channelID); void moduleUnloadingFinished( const QString & moduleName, int channelID); - void moduleUninstallingFinished( const QString & moduleName); + void moduleUninstallationSucceeded( const QString & moduleName); + void moduleUninstallationFailed( const QString & moduleName, const QString & errorMessage); void logCfgReplyReceived( EngineRepresentation * engine); void requestEngineRestartAfterCrash( EngineRepresentation * engine); diff --git a/Desktop/engine/enginesync.cpp b/Desktop/engine/enginesync.cpp index 7da077b414..52739067ac 100644 --- a/Desktop/engine/enginesync.cpp +++ b/Desktop/engine/enginesync.cpp @@ -65,6 +65,10 @@ EngineSync::EngineSync(QObject *parent) { connect(this, &EngineSync::moduleInstallationFailed, DynamicModules::dynMods(), &DynamicModules::installationPackagesFailed, Qt::DirectConnection); connect(this, &EngineSync::moduleInstallationSucceeded, DynamicModules::dynMods(), &DynamicModules::installationPackagesSucceeded, Qt::DirectConnection); + + connect(this, &EngineSync::moduleUninstallationSucceeded, DynamicModules::dynMods(), &DynamicModules::unInstallationPackagesSucceeded, Qt::DirectConnection); + connect(this, &EngineSync::moduleUninstallationFailed, DynamicModules::dynMods(), &DynamicModules::unInstallationPackagesFailed, Qt::DirectConnection); + connect(this, &EngineSync::moduleUninstallationFailed, this, &EngineSync::moduleInstallationFailedHandler ); } if(PreferencesModel::prefs()) @@ -270,7 +274,8 @@ EngineRepresentation * EngineSync::createNewEngine(bool addToEngines, int overri connect(engine, &EngineRepresentation::computeColumnFailed, this, &EngineSync::computeColumnFailed, Qt::QueuedConnection ); connect(engine, &EngineRepresentation::moduleInstallationFailed, this, &EngineSync::moduleInstallationFailed ); connect(engine, &EngineRepresentation::moduleInstallationSucceeded, this, &EngineSync::moduleInstallationSucceeded ); - connect(engine, &EngineRepresentation::moduleUninstallingFinished, this, &EngineSync::moduleUninstallingFinished ); + connect(engine, &EngineRepresentation::moduleUninstallationSucceeded, this, &EngineSync::moduleUninstallationSucceeded ); + connect(engine, &EngineRepresentation::moduleUninstallationFailed, this, &EngineSync::moduleUninstallationFailed ); connect(engine, &EngineRepresentation::moduleLoadingSucceeded, this, &EngineSync::moduleLoadingSucceeded ); connect(engine, &EngineRepresentation::moduleLoadingFailed, this, &EngineSync::moduleLoadingFailed ); connect(engine, &EngineRepresentation::logCfgReplyReceived, this, &EngineSync::logCfgReplyReceived ); @@ -774,16 +779,30 @@ stringset EngineSync::processDynamicModules() try { stringset wantToRunInstall = DynMods::dynMods()->moduleBundlesNeedingInstall(); - if(wantToRunInstall.size() > 0) + stringset wantToRunUninstall = DynMods::dynMods()->modulesNeedingUninstall(); + + for(auto & engine : _engines) //lets only process one dynamic module install/remove at a time for the sake of sanity. + if(engine->installingModule() || engine->unInstallingModule()) + return {}; + + if(wantToRunInstall.size() > 0 || wantToRunUninstall.size() > 0) { for(auto & engine : _engines) if(engine->idle() && engine->runsUtility()) //We don't care if the engine is meant for some module or other. We restart afterwards anyway { - engine->runModuleInstallRequestOnProcess(DynMods::dynMods()->getJsonForBundleInstallRequest()); - return {}; + if(wantToRunInstall.size() > 0) { + engine->runModuleInstallRequestOnProcess(DynMods::dynMods()->getJsonForBundleInstallRequest()); + wantToRunInstall = {}; + continue; + } + if(wantToRunUninstall.size() > 0) { + engine->runModuleUnInstallRequestOnProcess(DynMods::dynMods()->getJsonForModuleUninstallRequest()); + wantToRunUninstall = {}; + } } } - + + wantToRunInstall.insert(wantToRunUninstall.begin(), wantToRunUninstall.end()); return wantToRunInstall; } catch(Modules::ModuleException & e) { Log::log() << "Exception thrown in processDynamicModules: " << e.what() << std::endl; } diff --git a/Desktop/engine/enginesync.h b/Desktop/engine/enginesync.h index 12f0f37ec9..012eceaba1 100644 --- a/Desktop/engine/enginesync.h +++ b/Desktop/engine/enginesync.h @@ -98,7 +98,8 @@ public slots: void moduleInstallationFailed( const QString & moduleName, const QString & errorMessage); void moduleLoadingSucceeded( const QString & moduleName); void moduleLoadingFailed( const QString & moduleName, const QString & errorMessage); - void moduleUninstallingFinished( const QString & moduleName); + void moduleUninstallationSucceeded( const QString & moduleName); + void moduleUninstallationFailed( const QString & moduleName, const QString & errorMessage); void refreshAllPlotsExcept(const std::set & inProgress); void plotEditorRefresh(); diff --git a/Desktop/gui/preferencesmodel.cpp b/Desktop/gui/preferencesmodel.cpp index a763e53ccd..e07f965b31 100644 --- a/Desktop/gui/preferencesmodel.cpp +++ b/Desktop/gui/preferencesmodel.cpp @@ -161,6 +161,7 @@ GET_PREF_FUNC_INT( maxFlickVelocity, Settings::QML_MAX_FLICK_VELOCITY ) GET_PREF_FUNC_BOOL( modulesRemember, Settings::MODULES_REMEMBER ) GET_PREF_FUNC_BOOL( safeGraphics, Settings::SAFE_GRAPHICS_MODE ) GET_PREF_FUNC_STR( cranRepoURL, Settings::CRAN_REPO_URL ) +GET_PREF_FUNC_STR( moduleLibraryURL, Settings::MODULE_LIBRARY_URL ) GET_PREF_FUNC_BOOL( githubPatUseDefault, Settings::GITHUB_PAT_USE_DEFAULT ) GET_PREF_FUNC_STR( currentThemeName, Settings::THEME_NAME ) GET_PREF_FUNC_BOOL( useNativeFileDialog, Settings::USE_NATIVE_FILE_DIALOG ) @@ -369,6 +370,7 @@ SET_PREF_FUNCTION( int, setLogFilesMax, logFilesMax, logFilesMaxChange SET_PREF_FUNCTION_EMIT_NO_ARG( int, setMaxFlickVelocity, maxFlickVelocity, maxFlickVelocityChanged, Settings::QML_MAX_FLICK_VELOCITY ) SET_PREF_FUNCTION( bool, setModulesRemember, modulesRemember, modulesRememberChanged, Settings::MODULES_REMEMBER ) SET_PREF_FUNCTION( QString, setCranRepoURL, cranRepoURL, cranRepoURLChanged, Settings::CRAN_REPO_URL ) +SET_PREF_FUNCTION( QString, setModuleLibraryURL, moduleLibraryURL, moduleLibraryURLChanged, Settings::MODULE_LIBRARY_URL ) SET_PREF_FUNCTION( bool, setGithubPatUseDefault, githubPatUseDefault, githubPatUseDefaultChanged, Settings::GITHUB_PAT_USE_DEFAULT ) SET_PREF_FUNCTION( QString, setPlotBackground, plotBackground, plotBackgroundChanged, Settings::IMAGE_BACKGROUND ) SET_PREF_FUNCTION( bool, setUseNativeFileDialog, useNativeFileDialog, useNativeFileDialogChanged, Settings::USE_NATIVE_FILE_DIALOG ) diff --git a/Desktop/gui/preferencesmodel.h b/Desktop/gui/preferencesmodel.h index c31c027d70..8b39567c71 100644 --- a/Desktop/gui/preferencesmodel.h +++ b/Desktop/gui/preferencesmodel.h @@ -40,6 +40,7 @@ class PreferencesModel : public PreferencesModelBase Q_PROPERTY(QStringList modulesRemembered READ modulesRemembered WRITE setModulesRemembered NOTIFY modulesRememberedChanged ) Q_PROPERTY(bool safeGraphics READ safeGraphics WRITE setSafeGraphics NOTIFY safeGraphicsChanged ) Q_PROPERTY(QString cranRepoURL READ cranRepoURL WRITE setCranRepoURL NOTIFY cranRepoURLChanged ) + Q_PROPERTY(QString moduleLibraryURL READ moduleLibraryURL WRITE setModuleLibraryURL NOTIFY moduleLibraryURLChanged ) Q_PROPERTY(bool githubPatUseDefault READ githubPatUseDefault WRITE setGithubPatUseDefault NOTIFY githubPatUseDefaultChanged ) Q_PROPERTY(QString githubPatCustom READ githubPatCustom WRITE setGithubPatCustom NOTIFY githubPatCustomChanged ) Q_PROPERTY(QString interfaceFont READ interfaceFont WRITE setInterfaceFont NOTIFY interfaceFontChanged ) @@ -119,6 +120,7 @@ class PreferencesModel : public PreferencesModelBase QStringList modulesRemembered() const; bool safeGraphics() const; QString cranRepoURL() const; + QString moduleLibraryURL() const; QString githubPatResolved() const; QString githubPatCustom() const; bool githubPatUseDefault() const; @@ -215,6 +217,7 @@ public slots: void setModulesRemembered( QStringList modulesRemembered); void setSafeGraphics( bool safeGraphics); void setCranRepoURL( QString cranRepoURL); + void setModuleLibraryURL( QString moduleLibraryURL); void setGithubPatUseDefault( bool useDefault); void setGithubPatCustom( QString pat); void moduleEnabledChanged( QString moduleName, bool enabled); @@ -280,6 +283,7 @@ public slots: void modulesRememberedChanged(); void safeGraphicsChanged( bool safeGraphics); void cranRepoURLChanged( QString cranRepoURL); + void moduleLibraryURLChanged( QString moduleLibraryURL); void githubPatUseDefaultChanged( bool githubPatUseDefault); void githubPatCustomChanged(); void codeFontChanged( QString codeFont); diff --git a/Desktop/mainwindow.cpp b/Desktop/mainwindow.cpp index b793bfc4ba..24ff2481ab 100644 --- a/Desktop/mainwindow.cpp +++ b/Desktop/mainwindow.cpp @@ -44,7 +44,7 @@ #include "ALTNavigation/altnavcontrol.h" #include "utilities/messageforwarder.h" -#include "modules/activemodules.h" +#include "modules/installedmodules.h" #include "modules/dynamicmodules.h" #include "modules/menumodel.h" @@ -128,6 +128,7 @@ MainWindow::MainWindow(Application * application) : QObject(application), _appli _plotEditorModel = new PlotEditorModel(); _columnTypesModel = new ColumnTypesModel(this); _jaspConfiguration = JASPConfiguration::getInstance(this); + _moduleLibrary = new ModuleLibrary(); #ifdef WIN32 _windowsWorkaroundCPs = new CodePagesWindows(this); @@ -607,6 +608,7 @@ void MainWindow::loadQML() _qml->rootContext()->setContextProperty("baseBlockDim", 20 ); //should be taken from Theme _qml->rootContext()->setContextProperty("baseFontSize", 16 ); _qml->rootContext()->setContextProperty("languageModel", _languageModel ); + _qml->rootContext()->setContextProperty("jaspTmpDir", tq(Dirs::tempDir()) ); _qml->rootContext()->setContextProperty("columnTypeScale", int(columnType::scale) ); _qml->rootContext()->setContextProperty("columnTypeOrdinal", int(columnType::ordinal) ); @@ -619,6 +621,7 @@ void MainWindow::loadQML() _qml->rootContext()->setContextProperty("computedColumnTypeNotComputed", int(computedColumnType::notComputed) ); _qml->rootContext()->setContextProperty("computedColumnTypeConstructorCode", int(computedColumnType::constructorCode) ); _qml->rootContext()->setContextProperty("computedColumnTypeAnalysisNotComputed", int(computedColumnType::analysisNotComputed) ); + _qml->rootContext()->setContextProperty("moduleLibrary", _moduleLibrary ); _qml->setOutputWarningsToStandardError(true); @@ -701,9 +704,9 @@ void MainWindow::loadQML() disconnect(exitOnFailConnection); //Load the ribbonmodel modules now because we have an actual qml context to do so in. - _ribbonModel->loadModules( - ActiveModules::getActiveCommonModules(), - ActiveModules::getActiveExtraModules()); + _ribbonModel->loadModules( + InstalledModules::getActiveCommonModules(), + InstalledModules::getActiveExtraModules()); qmlLoaded(); } diff --git a/Desktop/mainwindow.h b/Desktop/mainwindow.h index 289b9b8782..c0129f1156 100644 --- a/Desktop/mainwindow.h +++ b/Desktop/mainwindow.h @@ -41,6 +41,7 @@ #include "gui/preferencesmodel.h" #include "modules/ribbonmodelfiltered.h" #include "modules/ribbonmodel.h" +#include "modules/modulelibrary.h" #include "modules/upgrader/upgrader.h" #include "jasptheme.h" #include "results/ploteditormodel.h" @@ -335,6 +336,7 @@ private slots: CodePagesWindows * _windowsWorkaroundCPs = nullptr; WorkspaceModel * _workspaceModel = nullptr; JASPConfiguration * _jaspConfiguration = nullptr; + ModuleLibrary * _moduleLibrary = nullptr; QSettings _settings; diff --git a/Desktop/modules/dynamicmodules.cpp b/Desktop/modules/dynamicmodules.cpp index 5071f62702..01189ad7fd 100644 --- a/Desktop/modules/dynamicmodules.cpp +++ b/Desktop/modules/dynamicmodules.cpp @@ -20,8 +20,11 @@ #include "log.h" #include "dynamicmodules.h" +#include "dynamicmodules.h" #include "utilities/qutils.h" #include +#include +#include #include "utilities/appdirs.h" #include "utilities/settings.h" #include "utilities/extractarchive.h" @@ -37,6 +40,9 @@ #include "modules/description/description.h" #include "modules/description/entrybase.h" #include "engine/enginesync.h" +#include "installedmodules.h" +#include "utilities/dynamicruntimeinfo.h" + #ifdef __APPLE__ #include "otoolstuff.h" @@ -235,6 +241,15 @@ void DynamicModules::registerForInstalling(const std::string & moduleName) } } +void DynamicModules::registerForUninstall(const std::string &moduleName) +{ + if(_modulesNeedingRemoval.find(moduleName) == _modulesNeedingRemoval.end()) + { + Log::log() << "Bundle '" << moduleName << "' being registered for removal" << std::endl; + _modulesNeedingRemoval.insert(moduleName); + } +} + QStringList DynamicModules::importPaths() const { QStringList allImportPaths; @@ -280,7 +295,7 @@ void DynamicModules::uninstallModule(const std::string & moduleName) _devModRWatcher = nullptr; } - bool removeFolder = true, + bool registerForDynamicUninstall = true, replacedWithBundled = bundledModuleInFilesystem(moduleName); if(replacedWithBundled) @@ -291,7 +306,7 @@ void DynamicModules::uninstallModule(const std::string & moduleName) _modules[moduleName]->setInstalled(false); if(_modules[moduleName]->isBundled() || _modules[moduleName]->isLibpathDevMod()) - removeFolder = false; + registerForDynamicUninstall = false; for(int i=int(_moduleNames.size()) - 1; i>=0; i--) if(_moduleNames[size_t(i)] == moduleName) @@ -301,32 +316,13 @@ void DynamicModules::uninstallModule(const std::string & moduleName) _modules.erase(moduleName); } - if(removeFolder) - removeUninstalledModuleFolder(moduleName); + if(registerForDynamicUninstall) + registerForUninstall(moduleName); if(!replacedWithBundled) emit dynamicModuleUninstalled(QString::fromStdString(moduleName)); } -void DynamicModules::removeUninstalledModuleFolder(const std::string & moduleName) -{ - Log::log() << "DynamicModules::removeUninstalledModuleFolder("<< moduleName << ")" << std::endl; - - std::wstring modulePath = moduleDirectoryW(moduleName); - - try - { - if(std::filesystem::exists(modulePath)) - std::filesystem::remove_all(modulePath); //Can fail because R might have a library from this folder still loaded. On Windows (and perhaps other OSs) these opened files can't be removed. - - } - catch (std::filesystem::filesystem_error & e) - { - MessageForwarder::showWarning(tr("Something went wrong removing files for module %1 at path '%2' and the error was: %3").arg(tq(moduleName)).arg(tq(moduleDirectory(moduleName))).arg(e.what())); - return; - } -} - DynamicModule* DynamicModules::requestModuleForSomethingAndRemoveIt(std::set & theSet) { if(theSet.size() == 0) @@ -344,6 +340,11 @@ stringset DynamicModules::moduleBundlesNeedingInstall() const return _moduleBundlesNeedingInstall; } +stringset DynamicModules::modulesNeedingUninstall() const +{ + return _modulesNeedingRemoval; +} + Json::Value DynamicModules::getJsonForBundleInstallRequest() { if(_moduleBundlesNeedingInstall.size() == 0) @@ -359,7 +360,7 @@ Json::Value DynamicModules::getJsonForBundleInstallRequest() .libPaths("%1"); library("jaspModuleBundleManager") bundles <- c(%2) - f <- function(bundle) {jaspModuleBundleManager::installJaspModuleBundle(installPath="%3", bundlePath=bundle, repoNames=c())} + f <- function(bundle) {jaspModuleBundleManager::installJaspModuleBundle(installPath="%3", bundlePath=bundle)} paste(sapply(bundles, f), collapse = ';') )readableR") .arg(AppDirs::bundledModulesDir() + "Tools/jaspModuleBundleManager_library/") @@ -376,6 +377,37 @@ Json::Value DynamicModules::getJsonForBundleInstallRequest() } +Json::Value DynamicModules::getJsonForModuleUninstallRequest() +{ + if(_modulesNeedingRemoval.size() == 0) + throw std::runtime_error("Tried to get json for Module uninstall request but there are none, getJsonForModuleUninstallRequest should never have been called."); + + QString list = ""; + for(auto& module : _modulesNeedingRemoval) list += "'" + QString(module.c_str()) + "'" + ","; + list.removeLast(); + + QString code = QString( + R"readableR( + tmp <- .libPaths(); + .libPaths("%1"); + library("jaspModuleBundleManager") + bundles <- c(%2) + f <- function(module) {jaspModuleBundleManager::uninstallJaspModuleBundle(installPath="%3", name=module)} + paste(sapply(bundles, f), collapse = ';') + )readableR") + .arg(AppDirs::bundledModulesDir() + "Tools/jaspModuleBundleManager_library/") + .arg(list) + .arg(AppDirs::userModulesDir()); + + + Json::Value requestJson(Json::objectValue); + requestJson["moduleRequest"] = moduleStatusToString(moduleStatus::uninstallNeeded); + requestJson["moduleCode"] = code.toStdString(); + requestJson["moduleName"] = list.toStdString(); + + return requestJson; +} + DynamicModule *DynamicModules::dynamicModuleLowerCased(QString moduleName) const { moduleName = moduleName.toLower(); //just enforce it @@ -422,7 +454,22 @@ void DynamicModules::installationPackagesSucceeded(const QString & moduleNames) #endif } _moduleBundlesNeedingInstall.clear(); - MessageForwarder::showWarning(tr("Install complete"), tr("Completed installation of Bundles: ") + listStr); + // MessageForwarder::showWarning(tr("Install complete"), tr("Completed installation of Bundles: ") + listStr); +} + + +void DynamicModules::unInstallationPackagesSucceeded(const QString &moduleNames) +{ + Log::log() << "Modules succesfully uninstalled" << std::endl; + _modulesNeedingRemoval.clear(); +} + +void DynamicModules::unInstallationPackagesFailed(const QString &moduleName, const QString &errorMessage) +{ + _modulesNeedingRemoval.clear(); + MessageForwarder::showWarning( + tq("Uninstall of Module %1 failed").arg(moduleName), + tr("The removal of Module %1 failed with the following errormessage:\n%2").arg(moduleName).arg(errorMessage)); } diff --git a/Desktop/modules/dynamicmodules.h b/Desktop/modules/dynamicmodules.h index b37d49eada..1b6f45b927 100644 --- a/Desktop/modules/dynamicmodules.h +++ b/Desktop/modules/dynamicmodules.h @@ -44,6 +44,7 @@ class DynamicModules : public QObject Q_PROPERTY(QStringList loadedModules READ loadedModules NOTIFY loadedModulesChanged ) Q_PROPERTY(QStringList loadedModulesTitles READ loadedModulesTitles NOTIFY loadedModulesChanged ) + public: explicit DynamicModules(QObject *parent) ; ~DynamicModules() override; @@ -72,8 +73,11 @@ class DynamicModules : public QObject void applyUpgrade( const std::string & module, const std::string & function, const Version & version, Json::Value & analysesJson, Modules::UpgradeMsgs & msgs, Modules::StepsTaken & stepsTaken); stringset moduleBundlesNeedingInstall() const; + stringset modulesNeedingUninstall() const; Json::Value getJsonForBundleInstallRequest(); + Json::Value getJsonForModuleUninstallRequest(); + Modules::DynamicModule* dynamicModuleLowerCased( QString moduleName) const; Modules::DynamicModule* dynamicModule( const std::string & moduleName) const { return _modules.count(moduleName) == 0 ? nullptr : _modules.at(moduleName); } @@ -113,7 +117,10 @@ class DynamicModules : public QObject public slots: void installationPackagesSucceeded( const QString & moduleNames); void installationPackagesFailed( const QString & moduleName, const QString & errorMessage); + void unInstallationPackagesSucceeded( const QString & moduleNames); + void unInstallationPackagesFailed( const QString & moduleName, const QString & errorMessage); void registerForInstalling( const std::string & moduleName); + void registerForUninstall( const std::string & moduleName); void setDevelopersModuleInstallButtonEnabled(bool developersModuleInstallButtonEnabled); void setDataLoaded(bool dataLoaded); void uninstallJASPDeveloperModule(); @@ -142,7 +149,6 @@ public slots: void loadedModulesChanged(); private: - void removeUninstalledModuleFolder(const std::string & moduleName); Modules::DynamicModule * requestModuleForSomethingAndRemoveIt(std::set & theSet); void devModCopyDescription(QString filename); void devModWatchFolder(QString folder, QFileSystemWatcher * & watcher); @@ -155,6 +161,7 @@ public slots: std::vector _moduleNames; std::map _modules; std::set _moduleBundlesNeedingInstall; + std::set _modulesNeedingRemoval; std::filesystem::path _modulesInstallDirectory; QString _currentInstallMsg = "", _currentInstallName = ""; diff --git a/Desktop/modules/activemodules.cpp b/Desktop/modules/installedmodules.cpp similarity index 61% rename from Desktop/modules/activemodules.cpp rename to Desktop/modules/installedmodules.cpp index f23682e645..5ea851d7ce 100644 --- a/Desktop/modules/activemodules.cpp +++ b/Desktop/modules/installedmodules.cpp @@ -1,14 +1,14 @@ -#include "activemodules.h" +#include "installedmodules.h" #include "utilities/appdirs.h" -#include #include #include #include #include #include "gui/preferencesmodel.h" +#include "log.h" -const std::string ActiveModules::settingsPath = "modules-settings.json"; +const std::string InstalledModules::settingsPath = "modules-settings.json"; QStringList getModulesFromDir(const std::string& path) @@ -20,7 +20,7 @@ QStringList getModulesFromDir(const std::string& path) return dir.entryList({"jasp*"}, QDir::Dirs); } -std::vector ActiveModules::getModules(bool extra) { +std::vector InstalledModules::getModules(bool extra) { //get the module orders and groupings form the modules settings file //This will probably be replaced with org specific settings from toml file in future std::string settings = AppDirs::bundledModulesDir().toStdString() + settingsPath; @@ -62,14 +62,48 @@ std::vector ActiveModules::getModules(bool extra) { return commonModules; } -std::vector ActiveModules::getActiveCommonModules() +std::vector InstalledModules::getActiveCommonModules() { return getModules(); } -std::vector ActiveModules::getActiveExtraModules() +std::vector InstalledModules::getActiveExtraModules() { return getModules(true); } +std::map InstalledModules::getInstalledModuleVersions() +{ + std::map moduleVersionMap; + + auto parseManifests = [&](const std::string& path) { + auto dir = QDir(path.c_str()); + dir.cdUp(); dir.cd("manifests"); + if(!dir.exists()) + return; + + auto manifests = dir.entryList({"jasp*.json"}, QDir::Files); + for(auto& manifest : manifests) { + std::ifstream in(dir.absoluteFilePath(manifest).toStdString()); + Json::Value root; + Json::Reader().parse(in, root); + Json::Value version = root["version"][0]; + std::string strVersion = ""; + for(int i = 0; i < version.size(); i++) { + auto x = version[i]; + strVersion = strVersion + version[i].asString() + "."; + } + strVersion.pop_back(); + moduleVersionMap[root["name"].asString()] = strVersion; + } + + }; + + parseManifests(AppDirs::bundledModulesLibDir().toStdString()); + parseManifests(AppDirs::userModulesLibDir().toStdString()); + + return moduleVersionMap; +} + + diff --git a/Desktop/modules/activemodules.h b/Desktop/modules/installedmodules.h similarity index 88% rename from Desktop/modules/activemodules.h rename to Desktop/modules/installedmodules.h index 713f74560b..3d4b74f0bc 100644 --- a/Desktop/modules/activemodules.h +++ b/Desktop/modules/installedmodules.h @@ -20,11 +20,12 @@ // that file instead if you want your changes to reflect in the app // -#ifndef ACTIVEMODULES_H -#define ACTIVEMODULES_H +#ifndef INSTALLEDMODULES_H +#define INSTALLEDMODULES_H #include #include +#include /** * @brief A minimal class for reporting the list active modules, to be used by `loadModules`. @@ -32,13 +33,15 @@ * @details Reads all available shipped and installed modules and divides them into two groups common (on ribbon) and extra (selectable) * The order equals the order in the Modules/modules.json which specifies these groups */ -class ActiveModules { +class InstalledModules { public: static std::vector getActiveCommonModules(); static std::vector getActiveExtraModules(); + static std::map getInstalledModuleVersions(); + private: static std::vector getModules(bool extra = false); @@ -46,4 +49,4 @@ class ActiveModules { }; -#endif // ACTIVEMODULES_H +#endif // INSTALLEDMODULES_H diff --git a/Desktop/modules/modulelibrary.cpp b/Desktop/modules/modulelibrary.cpp new file mode 100644 index 0000000000..3f741b005b --- /dev/null +++ b/Desktop/modules/modulelibrary.cpp @@ -0,0 +1,126 @@ +#include "modulelibrary.h" + +#include +#include + +#include "appinfo.h" +#include "gui/preferencesmodel.h" +#include "installedmodules.h" +#include "dynamicmodules.h" +#include "dynamicmodule.h" +#include "engine/enginesync.h" +#include "utilities/appdirs.h" +#include "utilities/dynamicruntimeinfo.h" + +ModuleLibrary * ModuleLibrary::_singleton = nullptr; + +ModuleLibrary::ModuleLibrary(QObject *parent) + : QObject(parent) +{ + _singleton = this; + + if (auto *dynMods = Modules::DynamicModules::dynMods()) + { + connect(dynMods, &Modules::DynamicModules::dynamicModuleAdded, this, [this](Modules::DynamicModule *) { + emitEnvironmentInfoChanged(); + finishInstalling(); + }); + connect(dynMods, &Modules::DynamicModules::dynamicModuleChanged, this, [this](Modules::DynamicModule *) { emitEnvironmentInfoChanged(); }); + connect(dynMods, &Modules::DynamicModules::dynamicModuleReplaced, this, [this](Modules::DynamicModule *, Modules::DynamicModule *) { emitEnvironmentInfoChanged(); }); + } + if (auto *engineSync = EngineSync::singleton()) + { + connect(engineSync, &EngineSync::moduleInstallationSucceeded, this, [this]() { + emitEnvironmentInfoChanged(); + finishInstalling(); + }); + connect(engineSync, &EngineSync::moduleInstallationFailed, this, [this](const QString &, const QString &) { + emitEnvironmentInfoChanged(); + finishInstalling(); + }); + connect(engineSync, &EngineSync::moduleUninstallationSucceeded, this, [this]() { + emitEnvironmentInfoChanged(); + finishInstalling(); + }); + connect(engineSync, &EngineSync::moduleUninstallationFailed, this, [this](const QString &, const QString &) { + emitEnvironmentInfoChanged(); + finishInstalling(); + }); + } + + if (auto *prefs = PreferencesModel::prefs()) + { + connect(prefs, &PreferencesModel::developerModeChanged, this, [this](bool) { emitEnvironmentInfoChanged(); }); + connect(prefs, &PreferencesModel::languageCodeChanged, this, [this]() { emitEnvironmentInfoChanged(); }); + connect(prefs, &PreferencesModel::interfaceFontChanged, this, [this]() { emitEnvironmentInfoChanged(); }); + connect(prefs, &PreferencesModel::currentThemeNameChanged, this, [this](const QString &) { emitEnvironmentInfoChanged(); }); + } +} + +QVariantMap ModuleLibrary::getEnvironmentInfo() const +{ + QVariantMap envInfo; + envInfo["version"] = QString(AppInfo::version.asString(3).c_str()); + + auto platform = DynamicRuntimeInfo::getRuntimeEnvironment(); + auto arch = DynamicRuntimeInfo::getMicroArch(); + std::string platformArch; + if(platform == RuntimeEnvironment::MAC) + platformArch = arch == MicroArch::AARCH64 ? "MacOS_arm64" : "MacOS_x86_64"; + else if(platform == RuntimeEnvironment::FLATPAK) + platformArch = arch == MicroArch::AARCH64 ? "Flatpak_aarch64" : "Flatpak_x86_64"; + else if(platform == RuntimeEnvironment::LINUX_LOCAL) + // When developing within devcontainer then jaspModule files with Flatpak_x86_64 also work? + platformArch = arch == MicroArch::AARCH64 ? "Flatpak_aarch64" : "Flatpak_x86_64"; + else + platformArch = "Windows_x86-64"; + envInfo["arch"] = QString::fromStdString(platformArch); + // Preferences needed in webapp + envInfo["developerMode"] = PreferencesModel::prefs()->developerMode(); + envInfo["theme"] = PreferencesModel::prefs()->currentThemeName().replace("Theme", ""); + envInfo["font"] = PreferencesModel::prefs()->interfaceFont(); + // do replace to enforce BCP 47 language tag format + envInfo["language"] = PreferencesModel::prefs()->languageCode().replace("_", "-"); + + envInfo["installedModules"] = installedModulesInfo(); + envInfo["uninstallableModules"] = getUninstallableModules(); + return envInfo; +} + +void ModuleLibrary::uninstallJASPModule(const QString &moduleName) +{ + if (auto *dynMods = Modules::DynamicModules::dynMods()) + dynMods->uninstallModule(moduleName.toStdString()); +} + +QVariantMap ModuleLibrary::installedModulesInfo() const +{ + QVariantMap installedModules; + for (auto const& [key, val] : InstalledModules::getInstalledModuleVersions()) + installedModules[QString::fromStdString(key)] = QString::fromStdString(val); + return installedModules; +} + +QStringList ModuleLibrary::getUninstallableModules() const +{ + // Only modules installed in user modules dir are uninstallable + auto dir = QDir(AppDirs::userModulesLibDir()); + return dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); +} + +void ModuleLibrary::emitEnvironmentInfoChanged() +{ + emit environmentInfoChanged(getEnvironmentInfo()); +} + +void ModuleLibrary::startInstalling() +{ + _isInstalling = true; + emit isInstallingChanged(); +} + +void ModuleLibrary::finishInstalling() +{ + _isInstalling = false; + emit isInstallingChanged(); +} diff --git a/Desktop/modules/modulelibrary.h b/Desktop/modules/modulelibrary.h new file mode 100644 index 0000000000..e2d4664c3e --- /dev/null +++ b/Desktop/modules/modulelibrary.h @@ -0,0 +1,63 @@ +// +// Copyright (C) 2013-2017 University of Amsterdam +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as +// published by the Free Software Foundation, either version 3 of the +// License, or (at your option) any later version. +// +// This program 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public +// License along with this program. If not, see +// . +// + +#ifndef MODULELIBRARY_H +#define MODULELIBRARY_H + +#include +#include +#include +#include + +namespace Modules +{ +class DynamicModule; +} + +class ModuleLibrary : public QObject +{ + Q_OBJECT + Q_PROPERTY(bool isInstalling READ isInstalling NOTIFY isInstallingChanged) + +public: + explicit ModuleLibrary(QObject *parent = 0); + + static ModuleLibrary * singleton() { return _singleton; } + + Q_INVOKABLE QVariantMap getEnvironmentInfo() const; + Q_INVOKABLE void uninstallJASPModule(const QString &moduleName); + Q_INVOKABLE void startInstalling(); + Q_INVOKABLE void finishInstalling(); + + bool isInstalling() const { return _isInstalling; } + +signals: + void environmentInfoChanged(const QVariantMap &environmentInfo); + void isInstallingChanged(); + +private: + QVariantMap installedModulesInfo() const; + QStringList getUninstallableModules() const; + void emitEnvironmentInfoChanged(); + +private: + static ModuleLibrary *_singleton; + bool _isInstalling = false; +}; + +#endif // MODULELIBRARY_H \ No newline at end of file diff --git a/Desktop/modules/ribbonmodel.cpp b/Desktop/modules/ribbonmodel.cpp index 7aacdc02c2..f1d4b04201 100644 --- a/Desktop/modules/ribbonmodel.cpp +++ b/Desktop/modules/ribbonmodel.cpp @@ -304,16 +304,17 @@ void RibbonModel::removeRibbonButtonModel(std::string moduleName) if(!isModuleName(moduleName)) return; - for(size_t row=0; row= 0; i--) + for(int i=_buttonNames[row].size() - 1; i >= 0; i--) { if(_buttonNames[row][i] == moduleName) { indexRemoved = i; break; } + } if(indexRemoved != -1) { diff --git a/Desktop/modules/ribbonmodel.h b/Desktop/modules/ribbonmodel.h index 27391e85c9..b29416eae7 100644 --- a/Desktop/modules/ribbonmodel.h +++ b/Desktop/modules/ribbonmodel.h @@ -127,7 +127,7 @@ class RibbonModel : public QAbstractListModel public slots: void addRibbonButtonModelFromDynamicModule(Modules::DynamicModule * module); - void removeDynamicRibbonButtonModel(QString moduleName) { removeRibbonButtonModel(moduleName.toStdString()); } + void removeDynamicRibbonButtonModel(QString moduleName) { removeRibbonButtonModel(moduleName.toStdString());} void setHighlightedModuleIndex(int highlightedModuleIndex); void analysisClicked(QString analysisFunction, QString analysisQML, QString analysisTitle, QString module); void setCurrentRow(int currentRow); diff --git a/Desktop/po/jaspDesktop-ar.po b/Desktop/po/jaspDesktop-ar.po index caaac51166..488c2fc378 100644 --- a/Desktop/po/jaspDesktop-ar.po +++ b/Desktop/po/jaspDesktop-ar.po @@ -653,6 +653,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "" +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "عنوان URL لمكتبة الوحدة: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "" @@ -1290,6 +1294,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "غير مسموح بإظهار مكتبة الوحدات لتثبيت الوحدات" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"في \"التفضيلات\" > \"الواجهة\"، تم إيقاف تشغيل خيار \"التحقق من التحديثات\". " +"يرجى تشغيله لرؤية مكتبة الوحدات." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/po/jaspDesktop-cs.po b/Desktop/po/jaspDesktop-cs.po index 37348c7ce6..6e65d2982e 100644 --- a/Desktop/po/jaspDesktop-cs.po +++ b/Desktop/po/jaspDesktop-cs.po @@ -692,6 +692,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "" +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "Adresa URL knihovny modulů: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "" @@ -1340,6 +1344,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Není povoleno zobrazit knihovnu modulů pro instalaci modulů" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"V \"Předvolby\" > \"Rozhraní\" je možnost \"Kontrolovat aktualizace\" " +"vypnuta. Prosím zapněte ji pro zobrazení knihovny modulů." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/po/jaspDesktop-de.po b/Desktop/po/jaspDesktop-de.po index 42a45b5b32..20d3bb7a12 100644 --- a/Desktop/po/jaspDesktop-de.po +++ b/Desktop/po/jaspDesktop-de.po @@ -646,6 +646,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "CRAN-Archiv wechseln: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL der Modulbibliothek: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Markdown-Dateien für Hilfe anlegen" @@ -1259,6 +1263,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Modul deinstallieren " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Es ist nicht erlaubt, die Modulbibliothek zum Installieren von Modulen anzuzeigen" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"In \"Einstellungen\" > \"Benutzeroberfläche\" ist die Option \"Nach Updates suchen\" " +"deaktiviert. Bitte aktivieren Sie diese, um die Modulbibliothek zu sehen." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R in JASP" diff --git a/Desktop/po/jaspDesktop-es.po b/Desktop/po/jaspDesktop-es.po index 830265205a..89628b4cb9 100644 --- a/Desktop/po/jaspDesktop-es.po +++ b/Desktop/po/jaspDesktop-es.po @@ -647,6 +647,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Cambiar el repositorio de CRAN: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL de la biblioteca de módulos: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Generar archivos reducidos para la ayuda" @@ -1263,6 +1267,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Desinstalar el módulo " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "No está permitido mostrar la biblioteca de módulos para instalar módulos" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"En \"Preferencias\" > \"Interfaz\", la opción \"Buscar actualizaciones\" está " +"desactivada. Por favor, actívela para ver la biblioteca de módulos." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R en JASP" diff --git a/Desktop/po/jaspDesktop-et.po b/Desktop/po/jaspDesktop-et.po index d39337caa0..ae5d2c14e1 100644 --- a/Desktop/po/jaspDesktop-et.po +++ b/Desktop/po/jaspDesktop-et.po @@ -607,6 +607,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "" +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "Moodulite teegi URL: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "" @@ -1189,6 +1193,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Moodulite installimiseks ei ole lubatud kuvada mooduliteeki" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"Menüüs \"Eelistused\" > \"Liides\" on \"Kontrolli uuendusi\" valik välja lülitatud. " +"Palun lülitage see sisse, et näha mooduliteeki." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/po/jaspDesktop-eu.po b/Desktop/po/jaspDesktop-eu.po index 533b245ce6..c7d222b527 100644 --- a/Desktop/po/jaspDesktop-eu.po +++ b/Desktop/po/jaspDesktop-eu.po @@ -654,6 +654,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Aldatu CRAN biltegia: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "Moduluen liburutegiaren URLa: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Sortu markdown fitxategiak laguntzarako" @@ -1265,6 +1269,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Desinstalatu modulua " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Ez dago baimenduta modulu-liburutegia erakustea moduluak instalatzeko" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"\"Hobespenak\" > \"Interfazea\" atalean \"Egiaztatu eguneratzeak\" aukera " +"desaktibatuta dago. Mesedez, aktibatu modulu-liburutegia ikusteko." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R JASP-en" diff --git a/Desktop/po/jaspDesktop-fr.po b/Desktop/po/jaspDesktop-fr.po index adaede3e2a..fd2e6b051a 100644 --- a/Desktop/po/jaspDesktop-fr.po +++ b/Desktop/po/jaspDesktop-fr.po @@ -648,6 +648,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Modifiez le référentiel CRAN : " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL de la bibliothèque des modules : " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Générer des fichiers markdown pour l'aide" @@ -1265,6 +1269,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Désinstaller le module " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Non autorisé à afficher la bibliothèque de modules pour installer des modules" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"Dans \"Préférences\" > \"Interface\", l'option \"Rechercher les mises à jour\" est " +"désactivée. Veuillez l'activer pour voir la bibliothèque de modules." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R dans JASP" diff --git a/Desktop/po/jaspDesktop-gl.po b/Desktop/po/jaspDesktop-gl.po index bedf46d5a5..dd031b04dd 100644 --- a/Desktop/po/jaspDesktop-gl.po +++ b/Desktop/po/jaspDesktop-gl.po @@ -616,6 +616,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Trocar o repositorio CRAN: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL da biblioteca de módulos: " + msgctxt "PrefsAdvanced|" msgid "Max logfiles to keep: " msgstr "Máx. ficheiros inicio a manter: " @@ -1854,6 +1858,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Desinstalar o módulo " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Non está permitido mostrar a biblioteca de módulos para instalar módulos" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"En \"Preferencias\" > \"Interface\", a opción \"Comprobar actualizacións\" está " +"desactivada. Por favor, actívea para ver a biblioteca de módulos." + msgctxt "ModulesMenu|" msgid "Install Developer Module" msgstr "Instalar un Módulo de Desenvolvemento" diff --git a/Desktop/po/jaspDesktop-hu.po b/Desktop/po/jaspDesktop-hu.po index acc80c161b..61c1ffeead 100644 --- a/Desktop/po/jaspDesktop-hu.po +++ b/Desktop/po/jaspDesktop-hu.po @@ -751,6 +751,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "" +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "A modulkönyvtár URL-címe: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Markdown súgó fájlok generálása" @@ -1472,6 +1476,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Nem engedélyezett a modulkönyvtár megjelenítése a modulok telepítéséhez" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"A \"Beállítások\" > \"Felület\" menüpontban a \"Frissítések keresése\" opció " +"ki van kapcsolva. Kérjük, kapcsolja be a modulkönyvtár megtekintéséhez." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/po/jaspDesktop-id.po b/Desktop/po/jaspDesktop-id.po index 0bde62f41d..843491f713 100644 --- a/Desktop/po/jaspDesktop-id.po +++ b/Desktop/po/jaspDesktop-id.po @@ -644,6 +644,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Ubah repositori CRAN: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL pustaka modul: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Buat berkas markdown untuk bantuan" @@ -1259,6 +1263,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Tidak diizinkan untuk menampilkan perpustakaan modul untuk memasang modul" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"Di \"Preferensi\" > \"Antarmuka\", opsi \"Periksa pembaruan\" dimatikan. " +"Silakan aktifkan untuk melihat perpustakaan modul." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/po/jaspDesktop-it.po b/Desktop/po/jaspDesktop-it.po index 8b1bdc2531..c81b45ff47 100644 --- a/Desktop/po/jaspDesktop-it.po +++ b/Desktop/po/jaspDesktop-it.po @@ -744,6 +744,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "" +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL della libreria dei moduli: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "" @@ -1451,6 +1455,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Non è consentito mostrare la libreria di moduli per installare moduli" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"In \"Preferenze\" > \"Interfaccia\", l'opzione \"Verifica aggiornamenti\" è " +"disattivata. Si prega di attivarla per vedere la libreria di moduli." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/po/jaspDesktop-ja.po b/Desktop/po/jaspDesktop-ja.po index 6ae2bdc51d..fc1d85f3ea 100644 --- a/Desktop/po/jaspDesktop-ja.po +++ b/Desktop/po/jaspDesktop-ja.po @@ -623,6 +623,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "CRANリポジトリの変更 " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "モジュールライブラリのURL: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "ヘルプ用マークダウンファイルの生成" @@ -1232,6 +1236,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "モジュールのアンインストール " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "モジュールをインストールするためのモジュールライブラリを表示することは許可されていません" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"「設定」>「インターフェース」で「更新の確認」オプションがオフになっています。" +"モジュールライブラリを表示するには、これをオンにしてください。" + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "JASPでR" diff --git a/Desktop/po/jaspDesktop-lt.po b/Desktop/po/jaspDesktop-lt.po index 19ee0f908c..00f1d92bc0 100644 --- a/Desktop/po/jaspDesktop-lt.po +++ b/Desktop/po/jaspDesktop-lt.po @@ -609,6 +609,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "" +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "Modulių bibliotekos URL: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "" @@ -1191,6 +1195,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Neleidžiama rodyti modulių bibliotekos modulių įdiegimui" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"\"Nuostatos\" > \"Sąsaja\" dalyje \"Tikrinti atnaujinimus\" parinktis yra " +"išjungta. Įjunkite ją, kad pamatytumėte modulių biblioteką." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/po/jaspDesktop-nl.po b/Desktop/po/jaspDesktop-nl.po index 8774dc5b8a..8b038c448e 100644 --- a/Desktop/po/jaspDesktop-nl.po +++ b/Desktop/po/jaspDesktop-nl.po @@ -644,6 +644,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Verander de CRAN locatie: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL van de modulebibliotheek: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Genereer markdown bestanden voor help" @@ -1251,6 +1255,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Deïnstalleer module " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Niet toegestaan om de modulebibliotheek te tonen voor het installeren van modules" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"In \"Voorkeuren\" > \"Interface\" is de optie \"Controleren op updates\" " +"uitgeschakeld. Schakel deze in om de modulebibliotheek te zien." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R in JASP" diff --git a/Desktop/po/jaspDesktop-pl.po b/Desktop/po/jaspDesktop-pl.po index b3d765e6d2..856b9cd7ee 100644 --- a/Desktop/po/jaspDesktop-pl.po +++ b/Desktop/po/jaspDesktop-pl.po @@ -638,6 +638,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Zmień repozytorium CRAN: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "Adres URL biblioteki modułów: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Generuj pliki pomocnicze w formacie markdown" @@ -1245,6 +1249,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Odinstaluj moduł " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Nie można wyświetlić biblioteki modułów w celu instalacji modułów" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"W \"Preferencje\" > \"Interfejs\" opcja \"Sprawdź aktualizacje\" jest " +"wyłączona. Proszę ją włączyć, aby zobaczyć bibliotekę modułów." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R w JASP" diff --git a/Desktop/po/jaspDesktop-pt.po b/Desktop/po/jaspDesktop-pt.po index ce0f6eea90..cffdc5e947 100644 --- a/Desktop/po/jaspDesktop-pt.po +++ b/Desktop/po/jaspDesktop-pt.po @@ -197,6 +197,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Altere o repositório CRAN: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL da biblioteca de módulos: " + msgctxt "PrefsAdvanced|" msgid "Logging options" msgstr "Opções de registro" @@ -368,6 +372,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Desinstalar módulo " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Não é permitido mostrar a biblioteca de módulos para instalar módulos" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"Em \"Preferências\" > \"Interface\", a opção \"Verificar atualizações\" está " +"desativada. Por favor, ative-a para ver a biblioteca de módulos." + msgctxt "SortMenuButton|" msgid "Sort the items" msgstr "Classifique os itens" diff --git a/Desktop/po/jaspDesktop-ru.po b/Desktop/po/jaspDesktop-ru.po index 7721f1a7ca..8895171765 100644 --- a/Desktop/po/jaspDesktop-ru.po +++ b/Desktop/po/jaspDesktop-ru.po @@ -638,6 +638,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Изменить репозиторий CRAN: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL библиотеки модулей: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Создать файлы разметки для справки" @@ -1245,6 +1249,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Удалить модуль " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Не разрешено показывать библиотеку модулей для установки модулей" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"В разделе «Настройки» > «Интерфейс» параметр «Проверять обновления» " +"отключён. Пожалуйста, включите его, чтобы увидеть библиотеку модулей." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R в JASP" diff --git a/Desktop/po/jaspDesktop-sq.po b/Desktop/po/jaspDesktop-sq.po index 9461897d7b..736738277b 100644 --- a/Desktop/po/jaspDesktop-sq.po +++ b/Desktop/po/jaspDesktop-sq.po @@ -607,6 +607,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "" +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL-ja e bibliotekës së moduleve: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "" @@ -1189,6 +1193,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Nuk lejohet të shfaqet biblioteka e moduleve për të instaluar module" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"Në \"Preferencat\" > \"Ndërfaqja\", opcioni \"Kontrollo përditësimet\" është " +"i çaktivizuar. Ju lutem aktivizojeni për të parë bibliotekën e moduleve." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/po/jaspDesktop-sr.po b/Desktop/po/jaspDesktop-sr.po index 8a50eaf5a2..259060675e 100644 --- a/Desktop/po/jaspDesktop-sr.po +++ b/Desktop/po/jaspDesktop-sr.po @@ -648,6 +648,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "Промените CRAN репозиторијум: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "URL библиотеке модула: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Генериши markdown фајлове за помоћ" @@ -1271,6 +1275,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Деинсталирајте модул " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Није дозвољено приказивање библиотеке модула за инсталирање модула" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"У \"Подешавања\" > \"Интерфејс\", опција \"Провери ажурирања\" је " +"искључена. Молимо вас да је укључите да бисте видели библиотеку модула." + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R у JASP-у" diff --git a/Desktop/po/jaspDesktop-tr.po b/Desktop/po/jaspDesktop-tr.po index d02f7ed8b5..76cdd6da07 100644 --- a/Desktop/po/jaspDesktop-tr.po +++ b/Desktop/po/jaspDesktop-tr.po @@ -643,6 +643,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "CRAN reposunu değiştirin: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "Modül kitaplığı URL'si: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "Yardım için markdown dosyaları oluşturun" @@ -1262,6 +1266,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "Modülü kaldır " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "Modülleri yüklemek için modül kitaplığını göstermesine izin verilmiyor" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"\"Tercihler\" > \"Arayüz\" bölümünde \"Güncellemeleri kontrol et\" seçeneği " +"kapalı. Modül kitaplığını görmek için lütfen açın." + msgctxt "PlotEditingAxis|" msgid "Title" msgstr "Başlık" diff --git a/Desktop/po/jaspDesktop-zh_Hans.po b/Desktop/po/jaspDesktop-zh_Hans.po index e906c69b6d..49bdc07c0e 100644 --- a/Desktop/po/jaspDesktop-zh_Hans.po +++ b/Desktop/po/jaspDesktop-zh_Hans.po @@ -613,6 +613,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "更改 CRAN 源: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "模块库 URL: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "生成用于帮助的markdown文件" @@ -1204,6 +1208,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "卸载模块 " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "不允许显示模块库以安装模块" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"在\"首选项\">\"界面\"中,\"检查更新\"选项已关闭。" +"请将其打开以查看模块库。" + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "JASP 中的 R" diff --git a/Desktop/po/jaspDesktop-zh_Hant.po b/Desktop/po/jaspDesktop-zh_Hant.po index d530fe07db..17a2549f54 100644 --- a/Desktop/po/jaspDesktop-zh_Hant.po +++ b/Desktop/po/jaspDesktop-zh_Hant.po @@ -607,6 +607,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "改變CRAN知識庫: " +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "模組庫 URL: " + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "產生MD檔" @@ -1194,6 +1198,18 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "移除模組 " +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "不允許顯示模組庫以安裝模組" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" +"在「偏好設定」>「介面」中,「檢查更新」選項已關閉。" +"請將其開啟以查看模組庫。" + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "R指令視窗" diff --git a/Desktop/po/jaspDesktop.pot b/Desktop/po/jaspDesktop.pot index 6e37f8490a..ff5e761b23 100644 --- a/Desktop/po/jaspDesktop.pot +++ b/Desktop/po/jaspDesktop.pot @@ -603,6 +603,10 @@ msgctxt "PrefsAdvanced|" msgid "Change the CRAN repository: " msgstr "" +msgctxt "PrefsAdvanced|" +msgid "Module library URL: " +msgstr "" + msgctxt "PrefsAdvanced|" msgid "Generate markdown files for help" msgstr "" @@ -1185,6 +1189,16 @@ msgctxt "ModulesMenu|" msgid "Uninstall module " msgstr "" +msgctxt "ModulesMenu|" +msgid "Not allowed to show the module library to install modules" +msgstr "" + +msgctxt "ModulesMenu|" +msgid "" +"In \"Preferences\" > \"Interface\" the \"Check for updates\" option is " +"turned off. Please turn it on to see the module library." +msgstr "" + msgctxt "RCommanderWindow|" msgid "R in JASP" msgstr "" diff --git a/Desktop/utilities/settings.cpp b/Desktop/utilities/settings.cpp index fa40f8c2d1..8205b3255b 100644 --- a/Desktop/utilities/settings.cpp +++ b/Desktop/utilities/settings.cpp @@ -36,6 +36,7 @@ const Settings::Setting Settings::Values[] = { {"modulesRemembered", ""}, {"safeGraphicsMode", false}, {"cranRepositoryURL", "https://cloud.r-project.org"}, + {"moduleLibraryURL", "https://module-library.jasp-stats.org"}, {"userHasGitHubAccount", false}, {"preferredLanguage", "en"}, {"preferredCountry", QLocale::World}, diff --git a/Desktop/utilities/settings.h b/Desktop/utilities/settings.h index a09d20ec35..9a045e6487 100644 --- a/Desktop/utilities/settings.h +++ b/Desktop/utilities/settings.h @@ -40,6 +40,7 @@ class Settings { MODULES_REMEMBERED, SAFE_GRAPHICS_MODE, CRAN_REPO_URL, + MODULE_LIBRARY_URL, USER_HAS_GITHUB_ACCOUNT, PREFERRED_LANGUAGE, PREFERRED_COUNTRY, diff --git a/Docs/development/jasp-build-guide-linux.md b/Docs/development/jasp-build-guide-linux.md index 5573606356..771685ba83 100644 --- a/Docs/development/jasp-build-guide-linux.md +++ b/Docs/development/jasp-build-guide-linux.md @@ -127,3 +127,53 @@ The previously mentioned script achieves the build of each module by calling `./ If you want to reinstall those modules (possibly after checking out different commits in there) you can of course run that line yourself. And suppose you have a module installed anywhere and you would like to simply build and install only that module you can run `./buildModuleBundlesLocally.sh /path/to/your/module`. +## Using the VS Code Dev Container + +The repository includes a ready-to-use Dev Container that provides all build dependencies (CMake, Ninja, R, JAGS, Qt 6.8.3, …) preconfigured. + +### Prerequisites +- Docker or a compatible container runtime +- VS Code with the Dev Containers extension + +### Open in the Dev Container +1. Open the `jasp-desktop` folder in VS Code. +2. When prompted, select “Reopen in Container” (or open the Command Palette and run “Dev Containers: Reopen in Container”). +3. First start will: + - Initialize submodules + - Detect a Conan profile + + +### Build & run inside the container + +You can build either with terminal, by opening a new terminal and: + +```shell +mkdir ~/.config +export GITHUB_PAT= +export Qt6_DIR=/opt/Qt/6.8.3/gcc_64/lib/cmake +CMAKE_PREFIX_PATH=/opt/Qt/6.8.3/gcc_64/lib/cmake/ cmake -GNinja -S . -B jasp-build +cmake --build jasp-build --target all -j6 + +# Start JASP desktop application +./jasp-build/Desktop/JASP --safeGraphics +``` + +```shell +QTWEBENGINE_REMOTE_DEBUGGING=8123 ./jasp-build/Desktop/JASP --safeGraphics +# In Chrome or Edge, open http://localhost:8123 to inspect the webengine process +``` + +If X11 says no then allow local X clients: + +```bash +xhost +local: +# and try again +``` + +If you see the JASP window is fully black then resize the window to force a redraw. + +You can also use QT creator with + +```shell +/opt/Qt/Tools/QtCreator/bin/qtcreator +``` \ No newline at end of file diff --git a/Engine/engine.cpp b/Engine/engine.cpp index ac46cd606c..e96f8e09f6 100644 --- a/Engine/engine.cpp +++ b/Engine/engine.cpp @@ -259,6 +259,7 @@ bool Engine::receiveMessages(int timeout) case engineState::computeColumn: receiveComputeColumnMessage(jsonRequest); break; case engineState::pauseRequested: pauseEngine(jsonRequest); break; case engineState::resuming: resumeEngine(jsonRequest); break; + case engineState::moduleUninstallRequest: case engineState::moduleInstallRequest: case engineState::moduleLoadRequest: receiveModuleRequestMessage(jsonRequest); break; case engineState::stopRequested: stopEngine(); break; @@ -585,7 +586,7 @@ void Engine::receiveModuleRequestMessage(const Json::Value & jsonRequest) std::string result = jaspRCPP_evalRCode(moduleCode.c_str(), false); bool succes = result == "succes!"; //Defined in DynamicModule::succesResultString() - if(moduleStatusFromString((moduleRequest)) == moduleStatus::installNeeded) + if(moduleStatusFromString((moduleRequest)) == moduleStatus::installNeeded || moduleStatusFromString((moduleRequest)) == moduleStatus::uninstallNeeded) succes = (result.find("null") == std::string::npos); Log::log() << "Was " << (succes ? "succesful" : "a failure") << ", now crafting answer." << std::endl; diff --git a/Modules/remote-bundles.json b/Modules/remote-bundles.json index 790578eba7..269125da64 100644 --- a/Modules/remote-bundles.json +++ b/Modules/remote-bundles.json @@ -421,4 +421,4 @@ "checksum": "2e00f50365f6cc37981f9a6b54189640fa77ca6e7f5b4a7dfdcea3bf1a0d7aee" } ] -} \ No newline at end of file +} diff --git a/Resources/Help/preferences/PrefsAdvanced.md b/Resources/Help/preferences/PrefsAdvanced.md index dc990a21eb..3e78476bae 100644 --- a/Resources/Help/preferences/PrefsAdvanced.md +++ b/Resources/Help/preferences/PrefsAdvanced.md @@ -12,7 +12,7 @@ If you've enabled this option then JASP will remember which modules are activate ### Developer mode -If you enable this you see a few extra options appear. One is "Generate markdown files for help", which will ignore any markdown helpfiles for an analysis and instead will only show generated markdown from each `info` field on each qml item and jaspObject. +If you enable this you see a few extra options appear. One is "Generate markdown files for help", which will ignore any markdown helpfiles for an analysis and instead will only show generated markdown from each `info` field on each qml item and jaspObject. Another option is the "Module library URL" field, which allows you to change the URL of the JASP module library web application from [https://module-library.jasp-stats.org/](https://module-library.jasp-stats.org/) to a locally running instance. #### Development module Besides that it will show "Development module" where you can load an R-package installed/restored with `renv`. For this you need to know the name of your module (for instance: `jaspAnova`) and the R-library or `.libPath` that `renv` created for you. diff --git a/Resources/Translations/jaspDesktop-de.qm b/Resources/Translations/jaspDesktop-de.qm index 5820fcb192..80bffdf408 100644 Binary files a/Resources/Translations/jaspDesktop-de.qm and b/Resources/Translations/jaspDesktop-de.qm differ diff --git a/Resources/Translations/jaspDesktop-es.qm b/Resources/Translations/jaspDesktop-es.qm index d33d6728c2..4ecd8d9b9a 100644 Binary files a/Resources/Translations/jaspDesktop-es.qm and b/Resources/Translations/jaspDesktop-es.qm differ diff --git a/Resources/Translations/jaspDesktop-fr.qm b/Resources/Translations/jaspDesktop-fr.qm index 668c31e309..d7db688f7f 100644 Binary files a/Resources/Translations/jaspDesktop-fr.qm and b/Resources/Translations/jaspDesktop-fr.qm differ diff --git a/Resources/Translations/jaspDesktop-gl.qm b/Resources/Translations/jaspDesktop-gl.qm index 42c5668a28..7d4be7a31f 100644 Binary files a/Resources/Translations/jaspDesktop-gl.qm and b/Resources/Translations/jaspDesktop-gl.qm differ diff --git a/Resources/Translations/jaspDesktop-id.qm b/Resources/Translations/jaspDesktop-id.qm index 700dc7c151..e196691064 100644 Binary files a/Resources/Translations/jaspDesktop-id.qm and b/Resources/Translations/jaspDesktop-id.qm differ diff --git a/Resources/Translations/jaspDesktop-it.qm b/Resources/Translations/jaspDesktop-it.qm index 6351137be6..a9afdb123e 100644 Binary files a/Resources/Translations/jaspDesktop-it.qm and b/Resources/Translations/jaspDesktop-it.qm differ diff --git a/Resources/Translations/jaspDesktop-ja.qm b/Resources/Translations/jaspDesktop-ja.qm index 431a8a9d37..aae4c2de48 100644 Binary files a/Resources/Translations/jaspDesktop-ja.qm and b/Resources/Translations/jaspDesktop-ja.qm differ diff --git a/Resources/Translations/jaspDesktop-nl.qm b/Resources/Translations/jaspDesktop-nl.qm index 54c340e13a..19f588e4e3 100644 Binary files a/Resources/Translations/jaspDesktop-nl.qm and b/Resources/Translations/jaspDesktop-nl.qm differ diff --git a/Resources/Translations/jaspDesktop-pl.qm b/Resources/Translations/jaspDesktop-pl.qm index 3059cdd6cd..3f4f340268 100644 Binary files a/Resources/Translations/jaspDesktop-pl.qm and b/Resources/Translations/jaspDesktop-pl.qm differ diff --git a/Resources/Translations/jaspDesktop-pt.qm b/Resources/Translations/jaspDesktop-pt.qm index 3bfe4e5d58..6877b7219e 100644 Binary files a/Resources/Translations/jaspDesktop-pt.qm and b/Resources/Translations/jaspDesktop-pt.qm differ diff --git a/Resources/Translations/jaspDesktop-ru.qm b/Resources/Translations/jaspDesktop-ru.qm index 0a9142ccae..9667aaee8a 100644 Binary files a/Resources/Translations/jaspDesktop-ru.qm and b/Resources/Translations/jaspDesktop-ru.qm differ diff --git a/Resources/Translations/jaspDesktop-sr.qm b/Resources/Translations/jaspDesktop-sr.qm index af40fd5ca4..eb5ab26af7 100644 Binary files a/Resources/Translations/jaspDesktop-sr.qm and b/Resources/Translations/jaspDesktop-sr.qm differ diff --git a/Resources/Translations/jaspDesktop-tr.qm b/Resources/Translations/jaspDesktop-tr.qm index 6fe7dc25fd..8fd4e5f229 100644 Binary files a/Resources/Translations/jaspDesktop-tr.qm and b/Resources/Translations/jaspDesktop-tr.qm differ diff --git a/Resources/Translations/jaspDesktop-zh_Hans.qm b/Resources/Translations/jaspDesktop-zh_Hans.qm index 2171c9b06a..fe54e41646 100644 Binary files a/Resources/Translations/jaspDesktop-zh_Hans.qm and b/Resources/Translations/jaspDesktop-zh_Hans.qm differ diff --git a/Resources/Translations/jaspDesktop-zh_Hant.qm b/Resources/Translations/jaspDesktop-zh_Hant.qm index c9ffcb7950..a288407225 100644 Binary files a/Resources/Translations/jaspDesktop-zh_Hant.qm and b/Resources/Translations/jaspDesktop-zh_Hant.qm differ