diff --git a/CMakeLists.txt b/CMakeLists.txt index de3935a..9f7bdc3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,8 @@ qt_add_qml_module(apparchpack cpp/utils/utils.cpp cpp/updatemanager/updatemanager.hpp cpp/updatemanager/updatemanager.cpp + cpp/updatemanager/private.cpp + cpp/updatemanager/qinvokable.cpp ) target_include_directories(apparchpack PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/cpp/utils) diff --git a/Main.qml b/Main.qml index bb2c3e6..953db51 100644 --- a/Main.qml +++ b/Main.qml @@ -21,6 +21,8 @@ ApplicationWindow { property string packageManager: "yay" property bool consoleOpen: false + Component.onCompleted: UpdateManager.checkUpdates() + ColumnLayout { id: mainLayout anchors.fill: parent @@ -36,6 +38,8 @@ ApplicationWindow { id: title Layout.maximumWidth: Number.POSITIVE_INFINITY Layout.alignment: Qt.AlignBottom + packageCount: UpdateManager.packageCount + isChecking: UpdateManager.isChecking } Buttons.SettingsBtn { diff --git a/cpp/updatemanager/private.cpp b/cpp/updatemanager/private.cpp new file mode 100644 index 0000000..0faaa5d --- /dev/null +++ b/cpp/updatemanager/private.cpp @@ -0,0 +1,71 @@ +#include "updatemanager.hpp" +#include + +void UpdateManager::set_packages(const QVariantList& value) +{ + const int oldCount = packageCount(); + m_packages = value; + + emit packagesChanged(); + if (packageCount() != oldCount) emit packageCountChanged(); +} + +void UpdateManager::set_is_checking(bool value) +{ + if (m_isChecking == value) return; + m_isChecking = value; + emit isCheckingChanged(); +} + +void UpdateManager::on_process_finished(int exit_code, QProcess::ExitStatus status) +{ + if (status == QProcess::CrashExit || (exit_code != 0 && exit_code != 2)) + { + on_process_error(m_process.error()); + return; + } + + const QString output = QString::fromUtf8(m_process.readAllStandardOutput()); + const QStringList lines = output.split(QLatin1Char('\n'), Qt::SkipEmptyParts); + + QVariantList package_list; + package_list.reserve(lines.size()); + + for (const QString &line : lines) + { + const QStringList parts = line.split(QLatin1Char(' '), Qt::SkipEmptyParts); + if (parts.size() != 4 || parts.at(2) != QStringLiteral("->")) continue; + + QVariantMap package; + package.insert(QStringLiteral("name"), parts.at(0)); + package.insert(QStringLiteral("version"), parts.at(1) + QStringLiteral(" → ") + parts.at(3)); + package.insert(QStringLiteral("updated"), false); + package.insert(QStringLiteral("updating"), false); + package_list.append(package); + } + + set_packages(package_list); + set_is_checking(false); +} + +void UpdateManager::on_process_error(QProcess::ProcessError error) +{ + if (!m_isChecking) return; + + QString reason; + if (error == QProcess::FailedToStart) + { + reason = QStringLiteral("failed to start: ") + m_process.errorString(); + } + else + { + reason = QStringLiteral("failed, exit code %1").arg(m_process.exitCode()); + const QString errText = QString::fromUtf8(m_process.readAllStandardError()).trimmed(); + if (!errText.isEmpty()) reason += QStringLiteral(": ") + errText; + } + + qWarning().noquote() << "checkupdates" << reason; + set_is_checking(false); +} + + diff --git a/cpp/updatemanager/qinvokable.cpp b/cpp/updatemanager/qinvokable.cpp new file mode 100644 index 0000000..b0d6e09 --- /dev/null +++ b/cpp/updatemanager/qinvokable.cpp @@ -0,0 +1,20 @@ +#include "updatemanager.hpp" +#include + +void UpdateManager::checkUpdates() +{ + if (m_isChecking || m_isInstalling || m_isRemoving) return; + + set_packages({}); + set_is_checking(true); + + const QString program = QStandardPaths::findExecutable(QStringLiteral("checkupdates")); + if (program.isEmpty()) + { + qWarning() << "checkupdates not found: install pacman-contrib"; + set_is_checking(false); + return; + } + + m_process.start(program, {}); +} diff --git a/cpp/updatemanager/updatemanager.cpp b/cpp/updatemanager/updatemanager.cpp index c11cc1f..6133c1a 100644 --- a/cpp/updatemanager/updatemanager.cpp +++ b/cpp/updatemanager/updatemanager.cpp @@ -1,12 +1,17 @@ #include "updatemanager.hpp" +#include UpdateManager::UpdateManager(QObject* parent) : QObject{parent} { + connect(&m_process, &QProcess::finished, this, &UpdateManager::on_process_finished); + connect(&m_process, &QProcess::errorOccurred, this, &UpdateManager::on_process_error); } int UpdateManager::packageCount() const { int count = 0; + for (const QVariant &item : m_packages) + if (!item.toMap().value(QStringLiteral("updated")).toBool()) ++count; return count; } @@ -29,3 +34,8 @@ bool UpdateManager::isRemoving() const { return m_isRemoving; } + +QVariantList UpdateManager::packages() const +{ + return m_packages; +} diff --git a/cpp/updatemanager/updatemanager.hpp b/cpp/updatemanager/updatemanager.hpp index 03858bc..0f112a8 100644 --- a/cpp/updatemanager/updatemanager.hpp +++ b/cpp/updatemanager/updatemanager.hpp @@ -20,6 +20,7 @@ class UpdateManager : public QObject Q_PROPERTY(bool isUpdating READ isUpdating NOTIFY isUpdatingChanged) Q_PROPERTY(bool isInstalling READ isInstalling NOTIFY isInstallingChanged) Q_PROPERTY(bool isRemoving READ isRemoving NOTIFY isRemovingChanged) + Q_PROPERTY(QVariantList packages READ packages NOTIFY packagesChanged) public: explicit UpdateManager(QObject* parent = nullptr); @@ -30,6 +31,18 @@ class UpdateManager : public QObject bool isUpdating() const; bool isInstalling() const; bool isRemoving() const; + QVariantList packages() const; + + public: + Q_INVOKABLE void checkUpdates(); + + private: + void set_packages(const QVariantList& value); + void set_is_checking(bool value); + + private: + void on_process_finished(int exitCode, QProcess::ExitStatus status); + void on_process_error(QProcess::ProcessError error); private: QProcess m_process; @@ -39,6 +52,7 @@ class UpdateManager : public QObject bool m_isUpdating = false; bool m_isInstalling = false; bool m_isRemoving = false; + QVariantList m_packages; signals: void packageCountChanged(); @@ -46,5 +60,6 @@ class UpdateManager : public QObject void isUpdatingChanged(); void isInstallingChanged(); void isRemovingChanged(); + void packagesChanged(); };