diff --git a/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp b/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp index 34dd69da..57d5d732 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitBackend.cpp @@ -1,424 +1,419 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * Copyright © 2013 Lukas Appelhans * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PackageKitBackend.h" #include "PackageKitSourcesBackend.h" #include "PackageKitResource.h" #include "PackageKitUpdater.h" #include "AppPackageKitResource.h" #include "PKTransaction.h" #include "AppstreamReviews.h" #include #include #include #include #include #include #include #include #include #include #include #include #include MUON_BACKEND_PLUGIN(PackageKitBackend) PackageKitBackend::PackageKitBackend(QObject* parent) : AbstractResourcesBackend(parent) , m_updater(new PackageKitUpdater(this)) , m_refresher(nullptr) , m_isFetching(0) , m_reviews(new AppstreamReviews(this)) { bool b = m_appdata.open(); if (!b) { qWarning() << "Couldn't open the AppStream database"; auto msg = new QAction(i18n("Got it"), this); msg->setWhatsThis(i18n("Please make sure that Appstream is properly set up on your system")); msg->setPriority(QAction::HighPriority); connect(msg, &QAction::triggered, msg, [msg](){ msg->setVisible(false); }); m_messageActions << msg; } reloadPackageList(); QTimer* t = new QTimer(this); connect(t, &QTimer::timeout, this, &PackageKitBackend::refreshDatabase); t->setInterval(60 * 60 * 1000); t->setSingleShot(false); t->start(); QAction* updateAction = new QAction(this); updateAction->setIcon(QIcon::fromTheme(QStringLiteral("system-software-update"))); updateAction->setText(i18nc("@action Checks the Internet for updates", "Check for Updates")); updateAction->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_R)); connect(this, &PackageKitBackend::fetchingChanged, updateAction, [updateAction, this](){ updateAction->setEnabled(!isFetching()); }); connect(updateAction, &QAction::triggered, this, &PackageKitBackend::refreshDatabase); m_messageActions += updateAction; connect(PackageKit::Daemon::global(), &PackageKit::Daemon::updatesChanged, this, &PackageKitBackend::fetchUpdates); connect(PackageKit::Daemon::global(), &PackageKit::Daemon::isRunningChanged, this, &PackageKitBackend::checkDaemonRunning); connect(m_reviews, &AppstreamReviews::ratingsReady, this, &AbstractResourcesBackend::emitRatingsReady); SourcesModel::global()->addSourcesBackend(new PackageKitSourcesBackend(this)); } PackageKitBackend::~PackageKitBackend() { } bool PackageKitBackend::isFetching() const { return m_isFetching; } void PackageKitBackend::acquireFetching(bool f) { if (f) m_isFetching++; else m_isFetching--; - if (m_isFetching==0) { - m_packages = m_updatingPackages; - m_updatingPackages.clear(); - } - if ((!f && m_isFetching==0) || (f && m_isFetching==1)) { emit fetchingChanged(); } Q_ASSERT(m_isFetching>=0); } void PackageKitBackend::reloadPackageList() { - m_updatingPackages = m_packages; - + acquireFetching(true); if (m_refresher) { disconnect(m_refresher.data(), &PackageKit::Transaction::finished, this, &PackageKitBackend::reloadPackageList); } const auto components = m_appdata.allComponents(); QStringList neededPackages; neededPackages.reserve(components.size()); foreach(const Appstream::Component& component, components) { if (component.packageNames().isEmpty()) { qDebug() << "no packages for" << component.name(); continue; } neededPackages += component.packageNames(); const auto res = new AppPackageKitResource(component, this); - m_updatingPackages.packages[component.id()] = res; + m_packages.packages[component.id()] = res; foreach (const QString& pkg, component.packageNames()) { - m_updatingPackages.packageToApp[pkg] += component.id(); + m_packages.packageToApp[pkg] += component.id(); } foreach (const QString& pkg, component.extends()) { - m_updatingPackages.extendedBy[pkg] += res; + m_packages.extendedBy[pkg] += res; } } + acquireFetching(false); neededPackages.removeDuplicates(); PackageKit::Transaction * t = PackageKit::Daemon::resolve(neededPackages); connect(t, &PackageKit::Transaction::finished, this, &PackageKitBackend::getPackagesFinished); connect(t, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackage); connect(t, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError); fetchUpdates(); - acquireFetching(true); } void PackageKitBackend::fetchUpdates() { PackageKit::Transaction * tUpdates = PackageKit::Daemon::getUpdates(); connect(tUpdates, &PackageKit::Transaction::finished, this, &PackageKitBackend::getUpdatesFinished); connect(tUpdates, &PackageKit::Transaction::package, this, &PackageKitBackend::addPackageToUpdate); connect(tUpdates, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError); - acquireFetching(true); m_updatesPackageId.clear(); } void PackageKitBackend::addPackage(PackageKit::Transaction::Info info, const QString &packageId, const QString &summary) { const QString packageName = PackageKit::Daemon::packageName(packageId); - QSet r = resourcesByPackageName(packageName, true); + QSet r = resourcesByPackageName(packageName); if (r.isEmpty()) { auto pk = new PackageKitResource(packageName, summary, this); r = { pk }; - m_updatingPackages.packages[packageName] = pk; + m_packagesToAdd.insert(pk); } foreach(auto res, r) static_cast(res)->addPackageId(info, packageId); } void PackageKitBackend::getPackagesFinished(PackageKit::Transaction::Exit exit) { - Q_ASSERT(m_isFetching); - if (exit != PackageKit::Transaction::ExitSuccess) { qWarning() << "error while fetching details" << exit; } - for(auto it = m_updatingPackages.packages.begin(); it != m_updatingPackages.packages.end(); ) { + for(auto it = m_packages.packages.begin(); it != m_packages.packages.end(); ) { auto pkr = qobject_cast(it.value()); if (pkr->packages().isEmpty()) { qWarning() << "Failed to find package for" << it.key(); it.value()->deleteLater(); - it = m_updatingPackages.packages.erase(it); + it = m_packages.packages.erase(it); } else ++it; } + includePackagesToAdd(); +} +void PackageKitBackend::includePackagesToAdd() +{ + if (m_packagesToAdd.isEmpty()) + return; + + acquireFetching(true); + foreach(PackageKitResource* res, m_packagesToAdd) { + m_packages.packages[res->packageName()] = res; + } acquireFetching(false); } void PackageKitBackend::transactionError(PackageKit::Transaction::Error, const QString& message) { qWarning() << "Transaction error: " << message << sender(); } void PackageKitBackend::packageDetails(const PackageKit::Details& details) { - const QSet resources = resourcesByPackageName(PackageKit::Daemon::packageName(details.packageId()), true); + const QSet resources = resourcesByPackageName(PackageKit::Daemon::packageName(details.packageId())); if (resources.isEmpty()) qWarning() << "couldn't find package for" << details.packageId(); foreach(AbstractResource* res, resources) { qobject_cast(res)->setDetails(details); } } -QSet PackageKitBackend::resourcesByPackageName(const QString& name, bool updating) const +QSet PackageKitBackend::resourcesByPackageName(const QString& name) const { - const Packages * const f = (updating ? &m_updatingPackages : &m_packages); - const QHash *dictionary = &f->packageToApp; - const QHash *pkgs = &f->packages; - - const QStringList names = dictionary->value(name, QStringList(name)); + const QStringList names = m_packages.packageToApp.value(name, QStringList(name)); QSet ret; ret.reserve(names.size()); foreach(const QString& name, names) { - AbstractResource* res = pkgs->value(name); + AbstractResource* res = m_packages.packages.value(name); if (res) ret += res; } return ret; } void PackageKitBackend::refreshDatabase() { if (!m_refresher) { m_refresher = PackageKit::Daemon::refreshCache(false); connect(m_refresher.data(), &PackageKit::Transaction::finished, this, [this]() { reloadPackageList(); acquireFetching(false); delete m_refresher; }); - acquireFetching(true); } else { qWarning() << "already resetting"; } } QVector PackageKitBackend::allResources() const { return containerValues>(m_packages.packages); } AbstractResource* PackageKitBackend::resourceByPackageName(const QString& name) const { const QStringList ids = m_packages.packageToApp.value(name, QStringList(name)); return ids.isEmpty() ? nullptr : m_packages.packages[ids.first()]; } QList PackageKitBackend::searchPackageName(const QString& searchText) { QList ret; Q_FOREACH (AbstractResource* res, m_packages.packages) { if (res->name().contains(searchText, Qt::CaseInsensitive)) { ret += res; } } return ret; } int PackageKitBackend::updatesCount() const { return m_updatesPackageId.count(); } void PackageKitBackend::transactionCanceled(Transaction* t) { qDebug() << "Cancel transaction:" << t->resource()->packageName() << "with" << m_transactions.size() << "transactions running"; int count = m_transactions.removeAll(t); Q_ASSERT(count==1); Q_UNUSED(count) TransactionModel::global()->cancelTransaction(t); } void PackageKitBackend::removeTransaction(Transaction* t) { qDebug() << "Remove transaction:" << t->resource()->packageName() << "with" << m_transactions.size() << "transactions running"; int count = m_transactions.removeAll(t); Q_ASSERT(count==1); Q_UNUSED(count) TransactionModel::global()->removeTransaction(t); } void PackageKitBackend::addTransaction(PKTransaction* t) { m_transactions.append(t); TransactionModel::global()->addTransaction(t); t->start(); } void PackageKitBackend::installApplication(AbstractResource* app, const AddonList& addons) { if(!addons.addonsToInstall().isEmpty()) { QVector appsToInstall; if(!app->isInstalled()) appsToInstall << app; foreach(const QString& toInstall, addons.addonsToInstall()) { appsToInstall += m_packages.packages.value(toInstall); Q_ASSERT(appsToInstall.last()); } addTransaction(new PKTransaction(appsToInstall, Transaction::ChangeAddonsRole)); } if (!addons.addonsToRemove().isEmpty()) { QVector appsToRemove; foreach(const QString& toRemove, addons.addonsToRemove()) { appsToRemove += m_packages.packages.value(toRemove); } addTransaction(new PKTransaction(appsToRemove, Transaction::RemoveRole)); } } void PackageKitBackend::installApplication(AbstractResource* app) { addTransaction(new PKTransaction({app}, Transaction::InstallRole)); } void PackageKitBackend::removeApplication(AbstractResource* app) { Q_ASSERT(!isFetching()); addTransaction(new PKTransaction({app}, Transaction::RemoveRole)); } QSet PackageKitBackend::upgradeablePackages() const { QSet ret; ret.reserve(m_updatesPackageId.size()); Q_FOREACH (const QString& pkgid, m_updatesPackageId) { const QString pkgname = PackageKit::Daemon::packageName(pkgid); - const auto pkgs = resourcesByPackageName(pkgname, false); + const auto pkgs = resourcesByPackageName(pkgname); if (pkgs.isEmpty()) { qWarning() << "couldn't find resource for" << pkgid; } ret.unite(pkgs); } return ret; } void PackageKitBackend::addPackageToUpdate(PackageKit::Transaction::Info info, const QString& packageId, const QString& summary) { if (info != PackageKit::Transaction::InfoBlocked) { m_updatesPackageId += packageId; addPackage(info, packageId, summary); } } void PackageKitBackend::getUpdatesFinished(PackageKit::Transaction::Exit, uint) { if (!m_updatesPackageId.isEmpty()) { - acquireFetching(true); PackageKit::Transaction* transaction = PackageKit::Daemon::getDetails(m_updatesPackageId.toList()); connect(transaction, &PackageKit::Transaction::details, this, &PackageKitBackend::packageDetails); connect(transaction, &PackageKit::Transaction::errorCode, this, &PackageKitBackend::transactionError); connect(transaction, &PackageKit::Transaction::finished, this, &PackageKitBackend::getUpdatesDetailsFinished); } - acquireFetching(false); + includePackagesToAdd(); emit updatesCountChanged(); } void PackageKitBackend::getUpdatesDetailsFinished(PackageKit::Transaction::Exit exit, uint) { if (exit != PackageKit::Transaction::ExitSuccess) { qWarning() << "Couldn't figure out the updates on PackageKit backend" << exit; } - acquireFetching(false); } bool PackageKitBackend::isPackageNameUpgradeable(const PackageKitResource* res) const { return !upgradeablePackageId(res).isEmpty(); } QString PackageKitBackend::upgradeablePackageId(const PackageKitResource* res) const { QString name = res->packageName(); foreach (const QString& pkgid, m_updatesPackageId) { if (PackageKit::Daemon::packageName(pkgid) == name) return pkgid; } return QString(); } void PackageKitBackend::checkDaemonRunning() { if (!PackageKit::Daemon::isRunning()) { qWarning() << "PackageKit stopped running!"; } } AbstractBackendUpdater* PackageKitBackend::backendUpdater() const { return m_updater; } QList PackageKitBackend::messageActions() const { return m_messageActions; } QVector PackageKitBackend::extendedBy(const QString& id) const { return m_packages.extendedBy[id]; } AbstractReviewsBackend* PackageKitBackend::reviewsBackend() const { return m_reviews; } #include "PackageKitBackend.moc" diff --git a/libdiscover/backends/PackageKitBackend/PackageKitBackend.h b/libdiscover/backends/PackageKitBackend/PackageKitBackend.h index b8440543..1c79571f 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitBackend.h +++ b/libdiscover/backends/PackageKitBackend/PackageKitBackend.h @@ -1,108 +1,109 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #ifndef PACKAGEKITBACKEND_H #define PACKAGEKITBACKEND_H #include "PackageKitResource.h" #include #include #include #include #include #include #include #include class AppstreamReviews; class AppPackageKitResource; class PackageKitUpdater; class PKTransaction; class DISCOVERCOMMON_EXPORT PackageKitBackend : public AbstractResourcesBackend { Q_OBJECT public: explicit PackageKitBackend(QObject* parent = nullptr); ~PackageKitBackend() override; AbstractBackendUpdater* backendUpdater() const override; AbstractReviewsBackend* reviewsBackend() const override; QVector< AbstractResource* > allResources() const override; AbstractResource* resourceByPackageName(const QString& name) const override; QList searchPackageName(const QString& searchText) override; int updatesCount() const override; void installApplication(AbstractResource* app) override; void installApplication(AbstractResource* app, const AddonList& addons) override; void removeApplication(AbstractResource* app) override; bool isValid() const override { return true; } QSet upgradeablePackages() const; bool isFetching() const override; QList messageActions() const override; bool isPackageNameUpgradeable(const PackageKitResource* res) const; QString upgradeablePackageId(const PackageKitResource* res) const; - QSet resourcesByPackageName(const QString& name, bool updating) const; + QSet resourcesByPackageName(const QString& name) const; QVector extendedBy(const QString& id) const; void fetchUpdates(); public Q_SLOTS: void transactionCanceled(Transaction* t); void removeTransaction(Transaction* t); void reloadPackageList(); void refreshDatabase(); private Q_SLOTS: void getPackagesFinished(PackageKit::Transaction::Exit exit); void addPackage(PackageKit::Transaction::Info info, const QString &packageId, const QString &summary); void packageDetails(const PackageKit::Details& details); void transactionError(PackageKit::Transaction::Error, const QString& message); void addPackageToUpdate(PackageKit::Transaction::Info, const QString& pkgid, const QString& summary); void getUpdatesFinished(PackageKit::Transaction::Exit,uint); void getUpdatesDetailsFinished(PackageKit::Transaction::Exit,uint); private: void addTransaction(PKTransaction* trans); void checkDaemonRunning(); void acquireFetching(bool f); + void includePackagesToAdd(); Appstream::Database m_appdata; QList m_transactions; PackageKitUpdater* m_updater; QPointer m_refresher; int m_isFetching; QSet m_updatesPackageId; + QSet m_packagesToAdd; QList m_messageActions; struct Packages { QHash packages; QHash packageToApp; QHash> extendedBy; void clear() { *this = {}; } }; Packages m_packages; - Packages m_updatingPackages; AppstreamReviews* const m_reviews; }; #endif // PACKAGEKITBACKEND_H diff --git a/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp b/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp index a2c72b6e..bc5386f3 100644 --- a/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp +++ b/libdiscover/backends/PackageKitBackend/PackageKitResource.cpp @@ -1,370 +1,371 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * Copyright © 2013 Lukas Appelhans * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "PackageKitResource.h" #include "PackageKitBackend.h" #include "PackageKitMessages.h" #include #include #include #include PackageKitResource::PackageKitResource(QString packageName, QString summary, PackageKitBackend* parent) : AbstractResource(parent) , m_summary(std::move(summary)) , m_name(std::move(packageName)) { setObjectName(m_name); } QString PackageKitResource::name() { return m_name; } QString PackageKitResource::packageName() const { return m_name; } QStringList PackageKitResource::allPackageNames() const { return QStringList(m_name); } QString PackageKitResource::availablePackageId() const { //First we check if it's upgradeable and use this version to display const QString pkgid = backend()->upgradeablePackageId(this); if (!pkgid.isEmpty()) return pkgid; QMap::const_iterator it = m_packages.constFind(PackageKit::Transaction::InfoAvailable); if (it != m_packages.constEnd()) return it->first(); return installedPackageId(); } QString PackageKitResource::installedPackageId() const { - return m_packages[PackageKit::Transaction::InfoInstalled].first(); + const auto installed = m_packages[PackageKit::Transaction::InfoInstalled]; + return installed.isEmpty() ? QString() : installed.first(); } QString PackageKitResource::comment() { return m_summary; } QString PackageKitResource::longDescription() { fetchDetails(); return m_details.description(); } QUrl PackageKitResource::homepage() { fetchDetails(); return QUrl(m_details.url()); } QString PackageKitResource::icon() const { return QStringLiteral("applications-other"); } QString PackageKitResource::license() { fetchDetails(); return m_details.license(); } QList PackageKitResource::addonsInformation() { return QList(); } QString PackageKitResource::availableVersion() const { return PackageKit::Daemon::packageVersion(availablePackageId()); } QString PackageKitResource::installedVersion() const { return PackageKit::Daemon::packageVersion(installedPackageId()); } int PackageKitResource::size() { fetchDetails(); return m_details.size(); } QString PackageKitResource::origin() const { //TODO return QStringLiteral("PackageKit"); } QString PackageKitResource::section() { return QString(); } QUrl PackageKitResource::screenshotUrl() { return QUrl(MuonDataSources::screenshotsSource().toString() + QStringLiteral("/screenshot/") + packageName()); } QUrl PackageKitResource::thumbnailUrl() { return QUrl(MuonDataSources::screenshotsSource().toString() + QStringLiteral("/thumbnail/") + packageName()); } AbstractResource::State PackageKitResource::state() { if (backend()->isPackageNameUpgradeable(this)) return Upgradeable; else if(m_packages.contains(PackageKit::Transaction::InfoInstalled)) return Installed; else if(m_packages.contains(PackageKit::Transaction::InfoAvailable)) return None; else return Broken; } void PackageKitResource::setPackages(const QMap &packages) { m_packages = packages; emit stateChanged(); } void PackageKitResource::addPackageId(PackageKit::Transaction::Info info, const QString &packageId) { m_packages[info].append(packageId); emit stateChanged(); } QStringList PackageKitResource::categories() { /*fetchDetails(); QStringList categories; switch (m_group) { case PackageKit::Transaction::GroupUnknown: categories << "Unknown"; break; case PackageKit::Transaction::GroupAccessibility: categories << "Accessibility"; break; case PackageKit::Transaction::GroupAccessories: categories << "Utility"; break; case PackageKit::Transaction::GroupAdminTools: categories << "System"; break; case PackageKit::Transaction::GroupCommunication: categories << "Chat"; break; case PackageKit::Transaction::GroupDesktopGnome: categories << "Unknown"; break; case PackageKit::Transaction::GroupDesktopKde: categories << "Unknown"; break; case PackageKit::Transaction::GroupDesktopOther: categories << "Unknown"; break; case PackageKit::Transaction::GroupDesktopXfce: categories << "Unknown"; break; case PackageKit::Transaction::GroupEducation: categories << "Science"; break; case PackageKit::Transaction::GroupFonts: categories << "Fonts"; break; case PackageKit::Transaction::GroupGames: categories << "Games"; break; case PackageKit::Transaction::GroupGraphics: categories << "Graphics"; break; case PackageKit::Transaction::GroupInternet: categories << "Internet"; break; case PackageKit::Transaction::GroupLegacy: categories << "Unknown"; break; case PackageKit::Transaction::GroupLocalization: categories << "Localization"; break; case PackageKit::Transaction::GroupMaps: categories << "Geography"; break; case PackageKit::Transaction::GroupMultimedia: categories << "Multimedia"; break; case PackageKit::Transaction::GroupNetwork: categories << "Network"; break; case PackageKit::Transaction::GroupOffice: categories << "Office"; break; case PackageKit::Transaction::GroupOther: categories << "Unknown"; break; case PackageKit::Transaction::GroupPowerManagement: categories << "System"; break; case PackageKit::Transaction::GroupProgramming: categories << "Development"; break; case PackageKit::Transaction::GroupPublishing: categories << "Publishing"; break; case PackageKit::Transaction::GroupRepos: categories << "System"; break; case PackageKit::Transaction::GroupSecurity: categories << "System"; break; case PackageKit::Transaction::GroupServers: categories << "System"; break; case PackageKit::Transaction::GroupSystem: categories << "System"; break; case PackageKit::Transaction::GroupVirtualization: categories << "System"; break; case PackageKit::Transaction::GroupScience: categories << "Science"; break; case PackageKit::Transaction::GroupDocumentation: categories << "System"; break; case PackageKit::Transaction::GroupElectronics: categories << "System"; break; case PackageKit::Transaction::GroupCollections: categories << "System"; break; case PackageKit::Transaction::GroupVendor: categories << "System"; break; case PackageKit::Transaction::GroupNewest: categories << "System"; break; }; return categories;*/ return QStringList(QStringLiteral("Unknown")); //NOTE: I commented the category fetching code, as it seems to get called even for non-technical items //when selecting a category, and receiving details for all packages takes about 20 mins in my VirtualBox and probably not much less on real systems } bool PackageKitResource::isTechnical() const { return true;//!m_availablePackageId.startsWith("flash"); } void PackageKitResource::fetchDetails() { if (!m_details.isEmpty()) return; m_details.insert(QStringLiteral("fetching"), true);//we add an entry so it's not re-fetched. PackageKit::Transaction* t = PackageKit::Daemon::getDetails(availablePackageId()); connect(t, &PackageKit::Transaction::details, this, &PackageKitResource::setDetails); connect(t, &PackageKit::Transaction::errorCode, this, &PackageKitResource::failedFetchingDetails); } void PackageKitResource::failedFetchingDetails(PackageKit::Transaction::Error, const QString& msg) { qWarning() << "error fetching details" << msg; } void PackageKitResource::setDetails(const PackageKit::Details & details) { const bool ourDetails = details.packageId() == availablePackageId(); if (!ourDetails) return; if (m_details != details) { m_details = details; emit stateChanged(); if (!backend()->isFetching()) backend()->resourcesChanged(this, {"size", "homepage", "license"}); } } void PackageKitResource::fetchChangelog() { PackageKit::Transaction* t = PackageKit::Daemon::getUpdateDetail(availablePackageId()); connect(t, &PackageKit::Transaction::updateDetail, this, &PackageKitResource::updateDetail); connect(t, &PackageKit::Transaction::errorCode, this, [this](PackageKit::Transaction::Error err, const QString & error) { qWarning() << "error fetching updates:" << err << error; emit changelogFetched(QString()); }); } static void addIfNotEmpty(const QString& title, const QString& content, QString& where) { if (!content.isEmpty()) where += QStringLiteral("

") + title + QStringLiteral(" ") + content + QStringLiteral("

"); } static QString joinPackages(const QStringList& pkgids) { QStringList ret; foreach(const QString& pkgid, pkgids) { ret += i18nc("package-name (version)", "%1 (%2)", PackageKit::Daemon::packageName(pkgid), PackageKit::Daemon::packageVersion(pkgid)); } return ret.join(i18nc("comma separating package names", ", ")); } static QStringList urlToLinks(const QStringList& urls) { QStringList ret; foreach(const QString& in, urls) ret += QStringLiteral("%1").arg(in); return ret; } void PackageKitResource::updateDetail(const QString& /*packageID*/, const QStringList& updates, const QStringList& obsoletes, const QStringList& vendorUrls, const QStringList& /*bugzillaUrls*/, const QStringList& /*cveUrls*/, PackageKit::Transaction::Restart restart, const QString& updateText, const QString& changelog, PackageKit::Transaction::UpdateState state, const QDateTime& /*issued*/, const QDateTime& /*updated*/) { QString info; addIfNotEmpty(i18n("Reason:"), updateText, info); addIfNotEmpty(i18n("Obsoletes:"), joinPackages(obsoletes), info); addIfNotEmpty(i18n("Updates:"), joinPackages(updates), info); addIfNotEmpty(i18n("Change Log:"), changelog, info); addIfNotEmpty(i18n("Update State:"), PackageKitMessages::updateStateMessage(state), info); addIfNotEmpty(i18n("Restart:"), PackageKitMessages::restartMessage(restart), info); if (!vendorUrls.isEmpty()) addIfNotEmpty(i18n("Vendor:"), urlToLinks(vendorUrls).join(QStringLiteral(", ")), info); emit changelogFetched(info); } PackageKitBackend* PackageKitResource::backend() const { return qobject_cast(parent()); } diff --git a/libdiscover/resources/ResourcesModel.cpp b/libdiscover/resources/ResourcesModel.cpp index 3b230ef4..282f26f3 100644 --- a/libdiscover/resources/ResourcesModel.cpp +++ b/libdiscover/resources/ResourcesModel.cpp @@ -1,462 +1,461 @@ /*************************************************************************** * Copyright © 2012 Aleix Pol Gonzalez * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * * published by the Free Software Foundation; either version 2 of * * the License or (at your option) version 3 or any later version * * accepted by the membership of KDE e.V. (or its successor approved * * by the membership of KDE e.V.), which shall act as a proxy * * defined in Section 14 of version 3 of the license. * * * * 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see . * ***************************************************************************/ #include "ResourcesModel.h" #include "AbstractResource.h" #include "resources/AbstractResourcesBackend.h" #include #include #include #include #include #include "Transaction/TransactionModel.h" #include "Category/CategoryModel.h" #include #include #include #include #include ResourcesModel *ResourcesModel::s_self = nullptr; ResourcesModel *ResourcesModel::global() { if(!s_self) s_self = new ResourcesModel; return s_self; } ResourcesModel::ResourcesModel(QObject* parent, bool load) : QAbstractListModel(parent) , m_initializingBackends(0) , m_actionCollection(nullptr) , m_roles(QAbstractItemModel::roleNames().unite({ { NameRole, "name" }, { IconRole, "icon" }, { CommentRole, "comment" }, { StateRole, "state" }, { RatingRole, "rating" }, { RatingPointsRole, "ratingPoints" }, { RatingCountRole, "ratingCount" }, { SortableRatingRole, "sortableRating" }, { ActiveRole, "active" }, { InstalledRole, "isInstalled" }, { ApplicationRole, "application" }, { OriginRole, "origin" }, { CanUpgrade, "canUpgrade" }, { PackageNameRole, "packageName" }, { IsTechnicalRole, "isTechnical" }, { CategoryRole, "category" }, { SectionRole, "section" }, { MimeTypes, "mimetypes" }, { SizeRole, "size" } }) ) { init(load); connect(this, &ResourcesModel::allInitialized, this, &ResourcesModel::fetchingChanged); } void ResourcesModel::init(bool load) { Q_ASSERT(!s_self); Q_ASSERT(QCoreApplication::instance()->thread()==QThread::currentThread()); connect(TransactionModel::global(), &TransactionModel::transactionAdded, this, &ResourcesModel::resourceChangedByTransaction); connect(TransactionModel::global(), &TransactionModel::transactionRemoved, this, &ResourcesModel::resourceChangedByTransaction); if(load) QMetaObject::invokeMethod(this, "registerAllBackends", Qt::QueuedConnection); } ResourcesModel::ResourcesModel(const QString& backendName, QObject* parent) : ResourcesModel(parent, false) { s_self = this; registerBackendByName(backendName); } ResourcesModel::~ResourcesModel() { qDeleteAll(m_backends); } QHash ResourcesModel::roleNames() const { return m_roles; } void ResourcesModel::addResourcesBackend(AbstractResourcesBackend* backend) { Q_ASSERT(!m_backends.contains(backend)); if(!backend->isValid()) { qWarning() << "Discarding invalid backend" << backend->name(); CategoryModel::blacklistPlugin(backend->name()); backend->deleteLater(); return; } if(!backend->isFetching()) { QVector newResources = backend->allResources(); int current = rowCount(); beginInsertRows(QModelIndex(), current, current+newResources.size()); m_backends += backend; m_resources.append(newResources); endInsertRows(); emit updatesCountChanged(); } else { m_initializingBackends++; m_backends += backend; m_resources.append(QVector()); } if(m_actionCollection) backend->integrateActions(m_actionCollection); connect(backend, &AbstractResourcesBackend::fetchingChanged, this, &ResourcesModel::callerFetchingChanged); connect(backend, &AbstractResourcesBackend::allDataChanged, this, &ResourcesModel::updateCaller); connect(backend, &AbstractResourcesBackend::resourcesChanged, this, &ResourcesModel::emitResourceChanges); connect(backend, &AbstractResourcesBackend::updatesCountChanged, this, &ResourcesModel::updatesCountChanged); connect(backend, &AbstractResourcesBackend::searchInvalidated, this, &ResourcesModel::searchInvalidated); emit backendsChanged(); if(m_initializingBackends==0) emit allInitialized(); else emit fetchingChanged(); } AbstractResource* ResourcesModel::resourceAt(int row) const { for (auto it = m_resources.constBegin(); it != m_resources.constEnd(); ++it) { if (it->size()<=row) row -= it->size(); else return it->at(row); } return nullptr; } QModelIndex ResourcesModel::resourceIndex(AbstractResource* res) const { AbstractResourcesBackend* backend = res->backend(); int row = 0, backends = m_backends.count(); for(int i=0; iisFetching()); int pos = m_resources[i].indexOf(res); - Q_ASSERT(pos>=0); - return index(row+pos); + return pos>=0 ? index(row+pos) : QModelIndex(); } } return QModelIndex(); } QVariant ResourcesModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } AbstractResource* const resource = resourceAt(index.row()); switch(role) { case ActiveRole: return TransactionModel::global()->transactionFromResource(resource) != nullptr; case ApplicationRole: return qVariantFromValue(resource); case RatingPointsRole: case RatingRole: case RatingCountRole: case SortableRatingRole: { Rating* const rating = resource->rating(); if (rating) return rating->property(roleNames().value(role).constData()); else { const int idx = Rating::staticMetaObject.indexOfProperty(roleNames().value(role).constData()); QVariant val = QVariant(0); val.convert(Rating::staticMetaObject.property(idx).type()); return val; } } case Qt::DecorationRole: case Qt::DisplayRole: case Qt::StatusTipRole: case Qt::ToolTipRole: return QVariant(); default: { QByteArray roleText = roleNames().value(role); const QMetaObject* m = resource->metaObject(); int propidx = roleText.isEmpty() ? -1 : m->indexOfProperty(roleText.constData()); if(Q_UNLIKELY(propidx < 0)) { qDebug() << "unknown role:" << role << roleText; return QVariant(); } else return m->property(propidx).read(resource); } } } int ResourcesModel::rowCount(const QModelIndex& parent) const { if(parent.isValid()) return 0; // Not the root element, and children don't have subchildren // The root element parents all resources from all backends int ret = 0; Q_FOREACH (const QVector& resources, m_resources) ret += resources.size(); return ret; } int ResourcesModel::rowsBeforeBackend(AbstractResourcesBackend* backend, QVector>::iterator& backendsResources) { Q_ASSERT(backend); int pos = m_backends.indexOf(backend); Q_ASSERT(pos>=0); backendsResources = m_resources.begin()+pos; int before = 0; for(auto it = m_resources.constBegin(); it != m_resources.constEnd() && it != backendsResources; ++it) { before+= it->size(); } return before; } void ResourcesModel::cleanBackend(AbstractResourcesBackend* backend) { QVector>::iterator backendsResources; int before = rowsBeforeBackend(backend, backendsResources); if (backendsResources->isEmpty()) { return; } beginRemoveRows(QModelIndex(), before, before + backendsResources->count() - 1); backendsResources->clear(); endRemoveRows(); } void ResourcesModel::callerFetchingChanged() { AbstractResourcesBackend* backend = qobject_cast(sender()); if (!backend->isValid()) { qWarning() << "Discarding invalid backend" << backend->name(); cleanBackend(backend); int idx = m_backends.indexOf(backend); Q_ASSERT(idx>=0); m_backends.removeAt(idx); m_resources.removeAt(idx); CategoryModel::blacklistPlugin(backend->name()); backend->deleteLater(); return; } if(backend->isFetching()) { m_initializingBackends++; cleanBackend(backend); emit fetchingChanged(); } else { resetBackend(backend); m_initializingBackends--; if(m_initializingBackends==0) emit allInitialized(); else emit fetchingChanged(); } } void ResourcesModel::resetBackend(AbstractResourcesBackend* backend) { QVector res = backend->allResources(); if(!res.isEmpty()) { QVector>::iterator backendsResources; int before = rowsBeforeBackend(backend, backendsResources); Q_ASSERT(backendsResources->isEmpty()); beginInsertRows(QModelIndex(), before, before+res.size()-1); *backendsResources = res; endInsertRows(); emit updatesCountChanged(); } } void ResourcesModel::updateCaller(const QVector& properties) { AbstractResourcesBackend* backend = qobject_cast(sender()); QVector>::iterator backendsResources; int before = rowsBeforeBackend(backend, backendsResources); if (backendsResources->isEmpty()) return; emit dataChanged(index(before), index(before+backendsResources->size()-1), propertiesToRoles(properties)); } QVector ResourcesModel::propertiesToRoles(const QVector& properties) const { QVector roles; roles.reserve(properties.size()); std::transform(properties.cbegin(), properties.cend(), roles.begin(), [this](const QByteArray& arr) { return roleNames().key(arr, -1); }); roles.removeAll(-1); return roles; } void ResourcesModel::emitResourceChanges(AbstractResource* resource, const QVector &properties) { const QModelIndex idx = resourceIndex(resource); if (!idx.isValid()) return; emit dataChanged(idx, idx, propertiesToRoles(properties)); } QVector< AbstractResourcesBackend* > ResourcesModel::backends() const { return m_backends; } AbstractResource* ResourcesModel::resourceByPackageName(const QString& name) { if (m_backends.isEmpty()) { qWarning() << "looking for package" << name << "without any backends loaded"; } foreach(AbstractResourcesBackend* backend, m_backends) { AbstractResource* res = backend->resourceByPackageName(name); if(res) { return res; } } return nullptr; } int ResourcesModel::updatesCount() const { int ret = 0; foreach(AbstractResourcesBackend* backend, m_backends) { ret += backend->updatesCount(); } return ret; } void ResourcesModel::installApplication(AbstractResource* app) { Q_ASSERT(!isFetching()); app->backend()->installApplication(app); } void ResourcesModel::installApplication(AbstractResource* app, const AddonList& addons) { Q_ASSERT(!isFetching()); app->backend()->installApplication(app, addons); } void ResourcesModel::removeApplication(AbstractResource* app) { Q_ASSERT(!isFetching()); app->backend()->removeApplication(app); } QMap ResourcesModel::itemData(const QModelIndex& index) const { QMap ret; QHash names = roleNames(); for (auto it = names.constBegin(); it != names.constEnd(); ++it) { ret.insert(it.key(), data(index, it.key())); } return ret; } void ResourcesModel::registerAllBackends() { DiscoverBackendsFactory f; QList backends = f.allBackends(); if(m_initializingBackends==0 && backends.isEmpty()) { qWarning() << "Couldn't find any backends"; emit allInitialized(); } else { foreach(AbstractResourcesBackend* b, backends) { addResourcesBackend(b); } } } void ResourcesModel::registerBackendByName(const QString& name) { DiscoverBackendsFactory f; addResourcesBackend(f.backend(name)); } void ResourcesModel::integrateActions(KActionCollection* w) { Q_ASSERT(w->thread()==thread()); m_actionCollection = w; setParent(w); foreach(AbstractResourcesBackend* b, m_backends) { b->integrateActions(w); } } void ResourcesModel::resourceChangedByTransaction(Transaction* t) { if (!t->resource()) return; Q_ASSERT(!t->resource()->backend()->isFetching()); QModelIndex idx = resourceIndex(t->resource()); if(idx.isValid()) emit dataChanged(idx, idx); } bool ResourcesModel::isFetching() const { foreach(AbstractResourcesBackend* b, m_backends) { if(b->isFetching()) return true; } return false; } QList ResourcesModel::messageActions() const { QList ret; foreach(AbstractResourcesBackend* b, m_backends) { ret += b->messageActions(); } Q_ASSERT(!ret.contains(nullptr)); return ret; } bool ResourcesModel::isBusy() const { return TransactionModel::global()->rowCount() > 0; }