diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -77,6 +77,16 @@ find_package(Phonon4Qt5 CONFIG REQUIRED) +find_package(PackageKitQt5) +set_package_properties(PackageKitQt5 + PROPERTIES DESCRIPTION "Software Manager integration" + TYPE OPTIONAL + PURPOSE "Used in the service menu installer" + ) +if(PackageKitQt5_FOUND) + set(HAVE_PACKAGEKIT TRUE) +endif() + find_package(KF5Baloo ${KF5_MIN_VERSION}) set_package_properties(KF5Baloo PROPERTIES DESCRIPTION "Baloo Core libraries" URL "https://www.kde.org" diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,8 @@ configure_file(config-kactivities.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kactivities.h) +configure_file(config-packagekit.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-packagekit.h) + configure_file(config-terminal.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-terminal.h) add_definitions( diff --git a/src/config-packagekit.h.cmake b/src/config-packagekit.h.cmake new file mode 100644 --- /dev/null +++ b/src/config-packagekit.h.cmake @@ -0,0 +1 @@ +#cmakedefine HAVE_PACKAGEKIT diff --git a/src/settings/services/servicemenuinstaller/CMakeLists.txt b/src/settings/services/servicemenuinstaller/CMakeLists.txt --- a/src/settings/services/servicemenuinstaller/CMakeLists.txt +++ b/src/settings/services/servicemenuinstaller/CMakeLists.txt @@ -8,4 +8,8 @@ KF5::I18n KF5::CoreAddons ) + +if(HAVE_PACKAGEKIT) + target_link_libraries(servicemenuinstaller PRIVATE PK::packagekitqt5) +endif() install(TARGETS servicemenuinstaller ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp b/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp --- a/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp +++ b/src/settings/services/servicemenuinstaller/servicemenuinstaller.cpp @@ -20,29 +20,42 @@ #include #include +#include #include #include #include #include #include #include -#include #include - #include #include +#include "../../../config-packagekit.h" + +const static QStringList binaryPackages = {QStringLiteral("application/vnd.debian.binary-package"), + QStringLiteral("application/x-rpm"), + QStringLiteral("application/x-xz"), + QStringLiteral("application/zstd")}; +enum PackageOperation { + Install, + Uninstall +}; + +#ifdef HAVE_PACKAGEKIT +#include +#include +#include +#else +#include +#endif + // @param msg Error that gets logged to CLI Q_NORETURN void fail(const QString &str) { qCritical() << str; - - QProcess process; - const QStringList args = {"--passivepopup", i18n("Dolphin service menu installation failed"), "15"}; - process.start("kdialog", args, QIODevice::ReadOnly); - if (!process.waitForStarted()) { - qFatal("Failed to run kdialog"); - } + const QStringList args = {"--detailederror" ,i18n("Dolphin service menu installation failed"), str}; + QProcess::startDetached("kdialog", args); exit(1); } @@ -53,6 +66,84 @@ return QDir(dataLocation).absoluteFilePath("kservices5/ServiceMenus"); } +#ifdef HAVE_PACKAGEKIT +void packageKitInstall(const QString &fileName) +{ + PackageKit::Transaction *transaction = PackageKit::Daemon::installFile(fileName); + + const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) { + fail(details); + }; + + QObject::connect(transaction, &PackageKit::Transaction::finished, + [=](PackageKit::Transaction::Exit status, uint) { + if (status == PackageKit::Transaction::ExitSuccess) { + exit(0); + } + // Fallback error handling + QTimer::singleShot(500, [=](){ + fail(i18n("Failed to install \"%1\", exited with status \"%2\"", + fileName, QVariant::fromValue(status).toString())); + }); + }); + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); +} + +void packageKitUninstall(const QString &fileName) +{ + const auto exitWithError = [=](PackageKit::Transaction::Error, const QString &details) { + fail(details); + }; + const auto uninstallLambda = [=](PackageKit::Transaction::Exit status, uint) { + if (status == PackageKit::Transaction::ExitSuccess) { + exit(0); + } + }; + + PackageKit::Transaction *transaction = PackageKit::Daemon::getDetailsLocal(fileName); + QObject::connect(transaction, &PackageKit::Transaction::details, + [=](const PackageKit::Details &details) { + PackageKit::Transaction *transaction = PackageKit::Daemon::removePackage(details.packageId()); + QObject::connect(transaction, &PackageKit::Transaction::finished, uninstallLambda); + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); + }); + + QObject::connect(transaction, &PackageKit::Transaction::errorCode, exitWithError); + // Fallback error handling + QObject::connect(transaction, &PackageKit::Transaction::finished, + [=](PackageKit::Transaction::Exit status, uint) { + if (status != PackageKit::Transaction::ExitSuccess) { + QTimer::singleShot(500, [=]() { + fail(i18n("Failed to uninstall \"%1\", exited with status \"%2\"", + fileName, QVariant::fromValue(status).toString())); + }); + } + }); + } +#endif + +Q_NORETURN void packageKit(PackageOperation operation, const QString &fileName) +{ +#ifdef HAVE_PACKAGEKIT + QFileInfo fileInfo(fileName); + if (!fileInfo.exists()) { + fail(i18n("The file does not exist!")); + } + const QString absPath = fileInfo.absoluteFilePath(); + if (operation == PackageOperation::Install) { + packageKitInstall(absPath); + } else { + packageKitUninstall(absPath); + } + QGuiApplication::exec(); // For event handling, no return after signals finish + fail(i18n("Unknown error when installing package")); +#else + Q_UNUSED(operation) + QDesktopServices::openUrl(QUrl(fileName)); + exit(0); +#endif +} + struct UncompressCommand { QString command; @@ -220,9 +311,8 @@ return false; } } else { - const QStringList binaryPackages = {"application/vnd.debian.binary-package", "application/x-rpm"}; if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { - return QDesktopServices::openUrl(QUrl(archive)); + packageKit(PackageOperation::Install, archive); } const QString dir = generateDirPath(archive); if (QFile::exists(dir)) { @@ -290,6 +380,9 @@ return false; } } else { + if (binaryPackages.contains(QMimeDatabase().mimeTypeForFile(archive).name())) { + packageKit(PackageOperation::Uninstall, archive); + } const QString dir = generateDirPath(archive); // Try "deinstall" first