diff --git a/libdiscover/UpdateModel/UpdateModel.cpp b/libdiscover/UpdateModel/UpdateModel.cpp index 00ab39d8..f37ee873 100644 --- a/libdiscover/UpdateModel/UpdateModel.cpp +++ b/libdiscover/UpdateModel/UpdateModel.cpp @@ -1,286 +1,281 @@ /*************************************************************************** * Copyright © 2011 Jonathan Thomas * * * * 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 "UpdateModel.h" // Qt includes #include #include // KDE includes #include #include // Own includes #include "UpdateItem.h" #include #include #include UpdateModel::UpdateModel(QObject *parent) : QAbstractListModel(parent) , m_updates(nullptr) { - connect(ResourcesModel::global(), &ResourcesModel::fetchingChanged, this, &UpdateModel::activityChanged); connect(ResourcesModel::global(), &ResourcesModel::updatesCountChanged, this, &UpdateModel::activityChanged); connect(ResourcesModel::global(), &ResourcesModel::resourceDataChanged, this, &UpdateModel::resourceDataChanged); } UpdateModel::~UpdateModel() = default; QHash UpdateModel::roleNames() const { return QAbstractItemModel::roleNames().unite({ { Qt::CheckStateRole, "checked" }, { ResourceProgressRole, "resourceProgress" }, { ResourceRole, "resource" }, { SizeRole, "size" }, { VersionRole, "version" }, { SectionRole, "section" }, { ChangelogRole, "changelog" } } ); } void UpdateModel::setBackend(ResourcesUpdatesModel* updates) { if (m_updates) { disconnect(m_updates, nullptr, this, nullptr); } m_updates = updates; connect(m_updates, &ResourcesUpdatesModel::progressingChanged, this, &UpdateModel::activityChanged); connect(m_updates, &ResourcesUpdatesModel::resourceProgressed, this, &UpdateModel::resourceHasProgressed); activityChanged(); } void UpdateModel::resourceHasProgressed(AbstractResource* res, qreal progress) { UpdateItem* item = itemFromResource(res); if (!item) return; item->setProgress(progress); const QModelIndex idx = indexFromItem(item); Q_EMIT dataChanged(idx, idx, { ResourceProgressRole }); } void UpdateModel::activityChanged() { if (!m_updates || m_updates->isProgressing()) return; m_updates->prepare(); setResources(m_updates->toUpdate()); } QVariant UpdateModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return QVariant(); } UpdateItem *item = itemFromIndex(index); switch (role) { case Qt::DisplayRole: return item->name(); case Qt::DecorationRole: return item->icon(); case Qt::CheckStateRole: return item->checked(); case VersionRole: return item->version(); case SizeRole: return KFormat().formatByteSize(item->size()); case ResourceRole: return QVariant::fromValue(item->resource()); case ResourceProgressRole: return item->progress(); case ChangelogRole: return item->changelog(); case SectionRole: return item->section(); default: break; } return QVariant(); } void UpdateModel::checkResources(const QList& resource, bool checked) { if(checked) m_updates->addResources(resource); else m_updates->removeResources(resource); } Qt::ItemFlags UpdateModel::flags(const QModelIndex &index) const { if (!index.isValid()) return nullptr; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } int UpdateModel::rowCount(const QModelIndex &parent) const { return !parent.isValid() ? m_updateItems.count() : 0; } bool UpdateModel::setData(const QModelIndex &idx, const QVariant &value, int role) { if (role == Qt::CheckStateRole) { UpdateItem *item = itemFromIndex(idx); const bool newValue = value.toInt() == Qt::Checked; const QList apps = { item->app() }; checkResources(apps, newValue); Q_ASSERT(idx.data(Qt::CheckStateRole) == value); Q_EMIT dataChanged(idx, idx, { Qt::CheckStateRole }); Q_EMIT toUpdateChanged(); return true; } return false; } void UpdateModel::fetchChangelog(int row) { UpdateItem *item = itemFromIndex(index(row, 0)); Q_ASSERT(item); if (!item) return; item->app()->fetchChangelog(); } void UpdateModel::integrateChangelog(const QString &changelog) { auto app = qobject_cast(sender()); Q_ASSERT(app); auto item = itemFromResource(app); if (!item) return; item->setChangelog(changelog); const QModelIndex idx = indexFromItem(item); Q_ASSERT(idx.isValid()); emit dataChanged(idx, idx, { ChangelogRole }); } void UpdateModel::setResources(const QList< AbstractResource* >& resources) { beginResetModel(); qDeleteAll(m_updateItems); m_updateItems.clear(); const QString importantUpdatesSection = i18nc("@item:inlistbox", "Important Security Updates"); const QString appUpdatesSection = i18nc("@item:inlistbox", "Application Updates"); const QString systemUpdateSection = i18nc("@item:inlistbox", "System Updates"); - QVector securityItems, appItems, systemItems; + QVector appItems, systemItems; foreach(AbstractResource* res, resources) { connect(res, &AbstractResource::changelogFetched, this, &UpdateModel::integrateChangelog, Qt::UniqueConnection); UpdateItem *updateItem = new UpdateItem(res); - if (res->isFromSecureOrigin()) { - updateItem->setSection(importantUpdatesSection); - securityItems += updateItem; - } else if(!res->isTechnical()) { + if(!res->isTechnical()) { updateItem->setSection(appUpdatesSection); appItems += updateItem; } else { updateItem->setSection(systemUpdateSection); systemItems += updateItem; } } const auto sortUpdateItems = [](UpdateItem *a, UpdateItem *b) { return a->name() < b->name(); }; - qSort(securityItems.begin(), securityItems.end(), sortUpdateItems); qSort(appItems.begin(), appItems.end(), sortUpdateItems); qSort(systemItems.begin(), systemItems.end(), sortUpdateItems); - m_updateItems = (QVector() << securityItems << appItems << systemItems); + m_updateItems = (QVector() << appItems << systemItems); endResetModel(); Q_EMIT hasUpdatesChanged(!resources.isEmpty()); Q_EMIT toUpdateChanged(); } bool UpdateModel::hasUpdates() const { return rowCount() > 0; } ResourcesUpdatesModel* UpdateModel::backend() const { return m_updates; } int UpdateModel::toUpdateCount() const { int ret = 0; foreach (UpdateItem* item, m_updateItems) { ret += item->checked() != Qt::Unchecked ? 1 : 0; } return ret; } UpdateItem * UpdateModel::itemFromResource(AbstractResource* res) { foreach (UpdateItem* item, m_updateItems) { if (item->app() == res) return item; } return nullptr; } QString UpdateModel::updateSize() const { double ret = 0; foreach (UpdateItem* item, m_updateItems) { if (item->checked() == Qt::Checked) ret += item->size(); } return KFormat().formatByteSize(ret); } QModelIndex UpdateModel::indexFromItem(UpdateItem* item) const { return index(m_updateItems.indexOf(item), 0, {}); } UpdateItem * UpdateModel::itemFromIndex(const QModelIndex& index) const { return m_updateItems[index.row()]; } void UpdateModel::resourceDataChanged(AbstractResource* res, const QVector& properties) { auto item = itemFromResource(res); if (!item) return; const auto index = indexFromItem(item); if (properties.contains("state")) dataChanged(index, index, {SizeRole, VersionRole}); } diff --git a/libdiscover/resources/AbstractResource.cpp b/libdiscover/resources/AbstractResource.cpp index 0324a9e7..6d0ece21 100644 --- a/libdiscover/resources/AbstractResource.cpp +++ b/libdiscover/resources/AbstractResource.cpp @@ -1,250 +1,245 @@ /*************************************************************************** * 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 "AbstractResource.h" #include "AbstractResourcesBackend.h" #include #include #include #include #include #include #include #include AbstractResource::AbstractResource(AbstractResourcesBackend* parent) : QObject(parent) { connect(this, &AbstractResource::stateChanged, this, &AbstractResource::sizeChanged); connect(this, &AbstractResource::stateChanged, this, &AbstractResource::reportNewState); } bool AbstractResource::canExecute() const { return !executables().isEmpty(); } void AbstractResource::invokeApplication() const { QStringList exes = executables(); if(!exes.isEmpty()) { const QString exe = exes.at(0); auto args = KShell::splitArgs(exe); QProcess::startDetached(args.takeFirst(), args); } } bool AbstractResource::isTechnical() const { return false; } void AbstractResource::addMetadata(const QString &key, const QJsonValue &value) { m_metadata.insert(key, value); } QJsonValue AbstractResource::getMetadata(const QString &key) { return m_metadata.value(key); } bool AbstractResource::canUpgrade() { return state() == Upgradeable; } bool AbstractResource::isInstalled() { return state() >= Installed; } void AbstractResource::fetchScreenshots() { emit screenshotsFetched({}, {}); } QStringList AbstractResource::mimetypes() const { return QStringList(); } QStringList AbstractResource::executables() const { return QStringList(); } AbstractResourcesBackend* AbstractResource::backend() const { return static_cast(parent()); } QString AbstractResource::status() { switch(state()) { case Broken: return i18n("Broken"); case None: return i18n("Available"); case Installed: return i18n("Installed"); case Upgradeable: return i18n("Upgradeable"); } return QString(); } -bool AbstractResource::isFromSecureOrigin() const -{ - return false; -} - QString AbstractResource::sizeDescription() { return KFormat().formatByteSize(size()); } QCollatorSortKey AbstractResource::nameSortKey() { if (!m_collatorKey) { m_collatorKey.reset(new QCollatorSortKey(QCollator().sortKey(name()))); } return *m_collatorKey; } Rating* AbstractResource::rating() const { AbstractReviewsBackend* ratings = backend()->reviewsBackend(); return ratings ? ratings->ratingForApplication(const_cast(this)) : nullptr; } QStringList AbstractResource::extends() const { return {}; } QString AbstractResource::appstreamId() const { return {}; } void AbstractResource::reportNewState() { if (backend()->isFetching()) return; emit backend()->resourcesChanged(this, {"state", "status", "canUpgrade", "size", "sizeDescription", "installedVersion", "availableVersion" }); } static bool shouldFilter(AbstractResource* res, const QPair& filter) { bool ret = true; switch (filter.first) { case CategoryFilter: ret = res->categories().contains(filter.second); break; case PkgSectionFilter: ret = res->section() == filter.second; break; case PkgWildcardFilter: { QString wildcard = filter.second; wildcard.remove(QLatin1Char('*')); ret = res->packageName().contains(wildcard); } break; case AppstreamIdWildcardFilter: { QString wildcard = filter.second; wildcard.remove(QLatin1Char('*')); ret = res->appstreamId().contains(wildcard); } break; case PkgNameFilter: // Only useful in the not filters ret = res->packageName() == filter.second; break; case InvalidFilter: break; } return ret; } bool AbstractResource::categoryMatches(Category* cat) { { const auto orFilters = cat->orFilters(); bool orValue = orFilters.isEmpty(); for (const auto& filter: orFilters) { if(shouldFilter(this, filter)) { orValue = true; break; } } if(!orValue) return false; } Q_FOREACH (const auto &filter, cat->andFilters()) { if(!shouldFilter(this, filter)) return false; } Q_FOREACH (const auto &filter, cat->notFilters()) { if(shouldFilter(this, filter)) return false; } return true; } static QSet walkCategories(AbstractResource* res, const QVector& cats) { QSet ret; foreach (Category* cat, cats) { if (res->categoryMatches(cat)) { const auto subcats = walkCategories(res, cat->subCategories()); if (subcats.isEmpty()) { ret += cat; } else { ret += subcats; } } } return ret; } QSet AbstractResource::categoryObjects() const { return walkCategories(const_cast(this), CategoryModel::global()->rootCategories()); } QString AbstractResource::categoryDisplay() const { const auto matchedCategories = categoryObjects(); QStringList ret; foreach(auto cat, matchedCategories) { ret.append(cat->name()); } ret.sort(); return ret.join(QStringLiteral(", ")); } QUrl AbstractResource::url() const { const QString asid = appstreamId(); return asid.isEmpty() ? QUrl(backend()->name() + QStringLiteral("://") + packageName()) : QUrl(QStringLiteral("appstream://") + asid); } QString AbstractResource::displayOrigin() const { return i18nc("origin (backend name)", "%1 (%2)", origin(), backend()->displayName()); } diff --git a/libdiscover/resources/AbstractResource.h b/libdiscover/resources/AbstractResource.h index 27acba76..36072b15 100644 --- a/libdiscover/resources/AbstractResource.h +++ b/libdiscover/resources/AbstractResource.h @@ -1,219 +1,218 @@ /*************************************************************************** * 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 ABSTRACTRESOURCE_H #define ABSTRACTRESOURCE_H #include #include #include #include #include #include #include #include "discovercommon_export.h" #include "PackageState.h" class Category; class Rating; class AbstractResourcesBackend; /** * \class AbstractResource AbstractResource.h "AbstractResource.h" * * \brief This is the base class of all resources. * * Each backend must reimplement its own resource class which needs to derive from this one. */ class DISCOVERCOMMON_EXPORT AbstractResource : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name CONSTANT) Q_PROPERTY(QString packageName READ packageName CONSTANT) Q_PROPERTY(QString comment READ comment CONSTANT) Q_PROPERTY(QVariant icon READ icon NOTIFY iconChanged) Q_PROPERTY(bool canExecute READ canExecute CONSTANT) Q_PROPERTY(State state READ state NOTIFY stateChanged) Q_PROPERTY(QString status READ status NOTIFY stateChanged) Q_PROPERTY(QStringList category READ categories CONSTANT) Q_PROPERTY(bool isTechnical READ isTechnical CONSTANT) Q_PROPERTY(QUrl homepage READ homepage CONSTANT) Q_PROPERTY(bool canUpgrade READ canUpgrade NOTIFY stateChanged) Q_PROPERTY(bool isInstalled READ isInstalled NOTIFY stateChanged) Q_PROPERTY(QString license READ license CONSTANT) Q_PROPERTY(QString longDescription READ longDescription CONSTANT) Q_PROPERTY(QString origin READ origin CONSTANT) Q_PROPERTY(QString displayOrigin READ displayOrigin CONSTANT) Q_PROPERTY(int size READ size NOTIFY sizeChanged) Q_PROPERTY(QString sizeDescription READ sizeDescription NOTIFY sizeChanged) Q_PROPERTY(QString installedVersion READ installedVersion NOTIFY stateChanged) Q_PROPERTY(QString availableVersion READ availableVersion NOTIFY stateChanged) Q_PROPERTY(QString section READ section CONSTANT) Q_PROPERTY(QStringList mimetypes READ mimetypes CONSTANT) Q_PROPERTY(AbstractResourcesBackend* backend READ backend CONSTANT) Q_PROPERTY(Rating* rating READ rating NOTIFY ratingFetched) Q_PROPERTY(QString appstreamId READ appstreamId CONSTANT) Q_PROPERTY(QString categoryDisplay READ categoryDisplay CONSTANT) Q_PROPERTY(QUrl url READ url CONSTANT) public: /** * This describes the state of the resource */ enum State { /** * When the resource is somehow broken */ Broken, /** * This means that the resource is neither installed nor broken */ None, /** * The resource is installed and up-to-date */ Installed, /** * The resource is installed and an update is available */ Upgradeable }; Q_ENUM(State) /** * Constructs the AbstractResource with its corresponding backend */ explicit AbstractResource(AbstractResourcesBackend* parent); ///used as internal identification of a resource virtual QString packageName() const = 0; ///resource name to be displayed virtual QString name() = 0; ///short description of the resource virtual QString comment() = 0; ///xdg-compatible icon name to represent the resource, url or QIcon virtual QVariant icon() const = 0; ///@returns whether invokeApplication makes something /// false if not overridden virtual bool canExecute() const; ///executes the resource, if applies. Q_SCRIPTABLE virtual void invokeApplication() const; virtual State state() = 0; virtual QStringList categories() = 0; ///@returns a URL that points to the content virtual QUrl homepage() = 0; virtual bool isTechnical() const; virtual int size() = 0; virtual QString sizeDescription(); virtual QString license() = 0; virtual QString installedVersion() const = 0; virtual QString availableVersion() const = 0; virtual QString longDescription() = 0; virtual QString origin() const = 0; QString displayOrigin() const; virtual QString section() = 0; ///@returns what kind of mime types the resource can consume virtual QStringList mimetypes() const; virtual QList addonsInformation() = 0; - bool isFromSecureOrigin() const; virtual QStringList executables() const; virtual QStringList extends() const; virtual QString appstreamId() const; void addMetadata(const QString &key, const QJsonValue &value); QJsonValue getMetadata(const QString &key); bool canUpgrade(); bool isInstalled(); ///@returns a user-readable explaination of the resource status ///by default, it will specify what state() is returning virtual QString status(); AbstractResourcesBackend* backend() const; /** * @returns a name sort key for faster sorting */ QCollatorSortKey nameSortKey(); /** * Convenience method to fetch the resource's rating * * @returns the rating for the resource or null if not available */ Rating* rating() const; /** * @returns a string defining the categories the resource belongs to */ QString categoryDisplay() const; bool categoryMatches(Category* cat); QSet categoryObjects() const; /** * @returns a url that uniquely identifies the application */ virtual QUrl url() const; public Q_SLOTS: virtual void fetchScreenshots(); virtual void fetchChangelog() = 0; Q_SIGNALS: void iconChanged(); void sizeChanged(); void stateChanged(); void ratingFetched(); ///response to the fetchScreenshots method ///@p thumbnails and @p screenshots should have the same number of elements void screenshotsFetched(const QList& thumbnails, const QList& screenshots); void changelogFetched(const QString& changelog); private: void reportNewState(); // TODO: make it std::optional or make QCollatorSortKey() QScopedPointer m_collatorKey; QJsonObject m_metadata; }; Q_DECLARE_METATYPE(QVector) #endif // ABSTRACTRESOURCE_H