// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL // SPDX-FileCopyrightText: 2021 Harald Sitter #include "language.h" #include #include #include "config-workspace.h" #include "debug.h" #ifdef HAVE_PACKAGEKIT #include #include class LanguageCompleter : public QObject { Q_OBJECT public: explicit LanguageCompleter(const QStringList &packages, QObject *parent = nullptr) : QObject(parent) , m_packages(packages) { } void start() { auto transaction = PackageKit::Daemon::resolve(m_packages, PackageKit::Transaction::FilterNotInstalled | PackageKit::Transaction::FilterArch); connect(transaction, &PackageKit::Transaction::package, this, [this](PackageKit::Transaction::Info info, const QString &packageID, const QString &summary) { Q_UNUSED(info); Q_UNUSED(summary); m_packageIDs << packageID; }); connect(transaction, &PackageKit::Transaction::errorCode, this, [](PackageKit::Transaction::Error error, const QString &details) { qCDebug(KCM_TRANSLATIONS) << "resolve error" << error << details; }); connect(transaction, &PackageKit::Transaction::finished, this, [this](PackageKit::Transaction::Exit status, uint code) { qCDebug(KCM_TRANSLATIONS) << "resolve finished" << status << code << m_packageIDs; if (m_packageIDs.size() != m_packages.size()) { qCWarning(KCM_TRANSLATIONS) << "Not all missing packages managed to resolve!" << m_packages << m_packageIDs; } install(); }); } Q_SIGNALS: void complete(); private: void install() { auto transaction = PackageKit::Daemon::installPackages(m_packageIDs); connect(transaction, &PackageKit::Transaction::errorCode, this, [](PackageKit::Transaction::Error error, const QString &details) { qCDebug(KCM_TRANSLATIONS) << "install error:" << error << details; }); connect(transaction, &PackageKit::Transaction::finished, this, [this](PackageKit::Transaction::Exit status, uint code) { qCDebug(KCM_TRANSLATIONS) << "install finished:" << status << code; Q_EMIT complete(); }); } const QStringList m_packages; QStringList m_packageIDs; }; #endif class CompletionCheck : public QObject { Q_OBJECT public: enum class Result { Error, Incomplete, Complete }; template static CompletionCheck *create(Args &&..._args); ~CompletionCheck() override = default; virtual void start() = 0; Q_SIGNALS: void finished(Result result, QStringList missingPackages); protected: explicit CompletionCheck(const QString &languageCode, QObject *parent = nullptr) : QObject(parent) , m_languageCode(languageCode) { } const QString m_languageCode; private: Q_DISABLE_COPY_MOVE(CompletionCheck); }; class UbuntuCompletionCheck : public CompletionCheck { public: using CompletionCheck::CompletionCheck; void start() override { proc.setProgram("/usr/bin/check-language-support"); proc.setArguments({"--language", m_languageCode.left(m_languageCode.indexOf(QLatin1Char('@')))}); connect(&proc, &QProcess::finished, this, [this] { const QString output = QString::fromUtf8(proc.readAllStandardOutput().simplified()); // Whenever we don't get packages back simply pretend the language is complete as we can't // give any useful information on what's wrong anyway. Q_EMIT finished(output.isEmpty() ? Result::Complete : Result::Incomplete, output.split(QLatin1Char(' '))); }); proc.start(); } private: QProcess proc; }; template CompletionCheck *CompletionCheck::create(Args &&..._args) { KOSRelease os; if (os.id() == QLatin1String("ubuntu") || os.idLike().contains(QLatin1String("ubuntu"))) { return new UbuntuCompletionCheck(std::forward(_args)...); } return nullptr; } void Language::reloadCompleteness() { auto *check = CompletionCheck::create(code, this); if (!check) { return; // no checking support - default to assume complete } connect(check, &CompletionCheck::finished, this, [this, check](CompletionCheck::Result result, const QStringList &missingPackages) { check->deleteLater(); switch (result) { case CompletionCheck::Result::Error: qCWarning(KCM_TRANSLATIONS) << "Failed to get completion status for" << code; return; case CompletionCheck::Result::Incomplete: { // Cache this, we need to modify the data before marking the change on the model. const bool changed = (packages != missingPackages); state = Language::State::Incomplete; packages = missingPackages; if (changed) { Q_EMIT stateChanged(); } return; } case CompletionCheck::Result::Complete: if (state != Language::State::Complete) { state = Language::State::Complete; packages.clear(); Q_EMIT stateChanged(); } return; } }); check->start(); } void Language::complete() { #ifdef HAVE_PACKAGEKIT auto completer = new LanguageCompleter(packages, this); connect(completer, &LanguageCompleter::complete, this, [completer, this] { completer->deleteLater(); reloadCompleteness(); }); state = Language::State::Installing; packages.clear(); completer->start(); Q_EMIT stateChanged(); #endif } Language::Language(const QString &code_, State state_, QObject *parent) : QObject(parent) , code(code_) , state(state_) { } Language::Language(const QString &code_, QObject *parent) : Language(code_, State::Complete, parent) { } #include "language.moc"