diff --git a/libdiscover/UpdateModel/UpdateModel.cpp b/libdiscover/UpdateModel/UpdateModel.cpp index 7a3ef61b..3dcb6461 100644 --- a/libdiscover/UpdateModel/UpdateModel.cpp +++ b/libdiscover/UpdateModel/UpdateModel.cpp @@ -1,357 +1,357 @@ /*************************************************************************** * 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 #include "libdiscover_debug.h" // KDE includes #include #include // Own includes #include "UpdateItem.h" #include #include #include UpdateModel::UpdateModel(QObject *parent) : QAbstractListModel(parent) , m_updateSizeTimer(new QTimer(this)) , 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); connect(this, &UpdateModel::toUpdateChanged, this, &UpdateModel::updateSizeChanged); m_updateSizeTimer->setInterval(100); m_updateSizeTimer->setSingleShot(true); connect(m_updateSizeTimer, &QTimer::timeout, this, &UpdateModel::updateSizeChanged); } UpdateModel::~UpdateModel() { qDeleteAll(m_updateItems); m_updateItems.clear(); } QHash UpdateModel::roleNames() const { return QAbstractItemModel::roleNames().unite({ { Qt::CheckStateRole, "checked" }, { ResourceProgressRole, "resourceProgress" }, { ResourceRole, "resource" }, { SizeRole, "size" }, { SectionRole, "section" }, { ChangelogRole, "changelog" }, { InstalledVersionRole, "installedVersion" }, { AvailableVersionRole, "availableVersion" } } ); } 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, SectionResourceProgressRole }); } void UpdateModel::activityChanged() { if (m_updates) { if (!m_updates->isProgressing()) { m_updates->prepare(); setResources(m_updates->toUpdate()); for(auto item : qAsConst(m_updateItems)) { item->setProgress(0); } } else 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 InstalledVersionRole: return item->installedVersion(); case AvailableVersionRole: return item->availableVersion(); 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: { static const QString appUpdatesSection = i18nc("@item:inlistbox", "Application Updates"); static const QString systemUpdateSection = i18nc("@item:inlistbox", "System Updates"); static const QString addonsSection = i18nc("@item:inlistbox", "Addons"); switch(item->resource()->type()) { case AbstractResource::Application: return appUpdatesSection; case AbstractResource::Technical: return systemUpdateSection; case AbstractResource::Addon: return addonsSection; } Q_UNREACHABLE(); } case SectionResourceProgressRole: return (100-item->progress()) + (101 * item->resource()->type()); 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); //When un/checking some backends will decide to add or remove a bunch of packages, so refresh it all auto m = idx.model(); Q_EMIT dataChanged(m->index(0, 0), m->index(m->rowCount()-1, 0), { Qt::CheckStateRole }); Q_EMIT toUpdateChanged(); return true; } return false; } void UpdateModel::fetchUpdateDetails(int row) { UpdateItem *item = itemFromIndex(index(row, 0)); Q_ASSERT(item); if (!item) return; item->app()->fetchUpdateDetails(); } 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& resources) { if (resources == m_resources) { return; } m_resources = resources; beginResetModel(); qDeleteAll(m_updateItems); m_updateItems.clear(); QVector appItems, systemItems, addonItems; foreach(AbstractResource* res, resources) { connect(res, &AbstractResource::changelogFetched, this, &UpdateModel::integrateChangelog, Qt::UniqueConnection); UpdateItem *updateItem = new UpdateItem(res); switch(res->type()) { case AbstractResource::Technical: systemItems += updateItem; break; case AbstractResource::Application: appItems += updateItem; break; case AbstractResource::Addon: addonItems += updateItem; break; } } const auto sortUpdateItems = [](UpdateItem *a, UpdateItem *b) { return a->name() < b->name(); }; - qSort(appItems.begin(), appItems.end(), sortUpdateItems); - qSort(systemItems.begin(), systemItems.end(), sortUpdateItems); - qSort(addonItems.begin(), addonItems.end(), sortUpdateItems); + std::sort(appItems.begin(), appItems.end(), sortUpdateItems); + std::sort(systemItems.begin(), systemItems.end(), sortUpdateItems); + std::sort(addonItems.begin(), addonItems.end(), sortUpdateItems); m_updateItems = (QVector() << appItems << addonItems << 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; QSet packages; foreach (UpdateItem* item, m_updateItems) { const auto packageName = item->resource()->packageName(); if (packages.contains(packageName)) { continue; } packages.insert(packageName); ret += item->checked() != Qt::Unchecked ? 1 : 0; } return ret; } int UpdateModel::totalUpdatesCount() const { int ret = 0; QSet packages; foreach (UpdateItem* item, m_updateItems) { const auto packageName = item->resource()->packageName(); if (packages.contains(packageName)) { continue; } packages.insert(packageName); ret += 1; } return ret; } UpdateItem * UpdateModel::itemFromResource(AbstractResource* res) { foreach (UpdateItem* item, m_updateItems) { if (item->app() == res) return item; } return nullptr; } QString UpdateModel::updateSize() const { return KFormat().formatByteSize(m_updates->updateSize()); } 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")) Q_EMIT dataChanged(index, index, {SizeRole, AvailableVersionRole}); else if (properties.contains("size")) { Q_EMIT dataChanged(index, index, {SizeRole}); m_updateSizeTimer->start(); } } void UpdateModel::checkAll() { for(int i=0, c=rowCount(); i * * 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 "ResourcesProxyModel.h" #include "libdiscover_debug.h" #include #include #include "ResourcesModel.h" #include "AbstractResource.h" #include "AbstractResourcesBackend.h" #include #include #include ResourcesProxyModel::ResourcesProxyModel(QObject *parent) : QAbstractListModel(parent) , m_sortRole(NameRole) , m_sortOrder(Qt::AscendingOrder) , m_sortByRelevancy(false) , m_roles({ { NameRole, "name" }, { IconRole, "icon" }, { CommentRole, "comment" }, { StateRole, "state" }, { RatingRole, "rating" }, { RatingPointsRole, "ratingPoints" }, { RatingCountRole, "ratingCount" }, { SortableRatingRole, "sortableRating" }, { InstalledRole, "isInstalled" }, { ApplicationRole, "application" }, { OriginRole, "origin" }, { DisplayOriginRole, "displayOrigin" }, { CanUpgrade, "canUpgrade" }, { PackageNameRole, "packageName" }, { CategoryRole, "category" }, { CategoryDisplayRole, "categoryDisplay" }, { SectionRole, "section" }, { MimeTypes, "mimetypes" }, { LongDescriptionRole, "longDescription" }, { SourceIconRole, "sourceIcon" }, { SizeRole, "size" }, { ReleaseDateRole, "releaseDate" } }) , m_currentStream(nullptr) { // new ModelTest(this, this); connect(ResourcesModel::global(), &ResourcesModel::backendsChanged, this, &ResourcesProxyModel::invalidateFilter); connect(ResourcesModel::global(), &ResourcesModel::backendDataChanged, this, &ResourcesProxyModel::refreshBackend); connect(ResourcesModel::global(), &ResourcesModel::resourceDataChanged, this, &ResourcesProxyModel::refreshResource); connect(ResourcesModel::global(), &ResourcesModel::resourceRemoved, this, &ResourcesProxyModel::removeResource); connect(this, &QAbstractItemModel::modelReset, this, &ResourcesProxyModel::countChanged); connect(this, &QAbstractItemModel::rowsInserted, this, &ResourcesProxyModel::countChanged); connect(this, &QAbstractItemModel::rowsRemoved, this, &ResourcesProxyModel::countChanged); } void ResourcesProxyModel::componentComplete() { m_setup = true; invalidateFilter(); } QHash ResourcesProxyModel::roleNames() const { return m_roles; } void ResourcesProxyModel::setSortRole(Roles sortRole) { if (sortRole != m_sortRole) { Q_ASSERT(roleNames().contains(sortRole)); m_sortRole = sortRole; Q_EMIT sortRoleChanged(sortRole); invalidateSorting(); } } void ResourcesProxyModel::setSortOrder(Qt::SortOrder sortOrder) { if (sortOrder != m_sortOrder) { m_sortOrder = sortOrder; Q_EMIT sortRoleChanged(sortOrder); invalidateSorting(); } } void ResourcesProxyModel::setSearch(const QString &_searchText) { // 1-character searches are painfully slow. >= 2 chars are fine, though const QString searchText = _searchText.count() <= 1 ? QString() : _searchText; const bool diff = searchText != m_filters.search; if (diff) { m_filters.search = searchText; if (m_sortByRelevancy == searchText.isEmpty()) { m_sortByRelevancy = !searchText.isEmpty(); Q_EMIT sortByRelevancyChanged(m_sortByRelevancy); } invalidateFilter(); Q_EMIT searchChanged(m_filters.search); } } void ResourcesProxyModel::removeDuplicates(QVector& resources) { const auto cab = ResourcesModel::global()->currentApplicationBackend(); QHash::iterator> storedIds; for(auto it = m_displayedResources.begin(); it != m_displayedResources.end(); ++it) { const auto appstreamid = (*it)->appstreamId(); if (appstreamid.isEmpty()) { continue; } auto at = storedIds.find(appstreamid); if (at == storedIds.end()) { storedIds[appstreamid] = it; } else { qCWarning(LIBDISCOVER_LOG) << "We should have sanitized the displayed resources. There is a bug"; Q_UNREACHABLE(); } } QHash::iterator> ids; for(auto it = resources.begin(); it != resources.end(); ) { const auto appstreamid = (*it)->appstreamId(); if (appstreamid.isEmpty()) { ++it; continue; } auto at = storedIds.find(appstreamid); if (at == storedIds.end()) { auto at = ids.find(appstreamid); if (at == ids.end()) { ids[appstreamid] = it; ++it; } else { if ((*it)->backend() == cab) { qSwap(*it, **at); } it = resources.erase(it); } } else { if ((*it)->backend() == cab) { **at = *it; auto pos = index(*at - m_displayedResources.begin(), 0); Q_EMIT dataChanged(pos, pos); } it = resources.erase(it); } } } void ResourcesProxyModel::addResources(const QVector& _res) { auto res = _res; m_filters.filterJustInCase(res); if (res.isEmpty()) return; if (!m_sortByRelevancy) - qSort(res.begin(), res.end(), [this](AbstractResource* res, AbstractResource* res2){ return lessThan(res, res2); }); + std::sort(res.begin(), res.end(), [this](AbstractResource* res, AbstractResource* res2){ return lessThan(res, res2); }); sortedInsertion(res); fetchSubcategories(); } void ResourcesProxyModel::invalidateSorting() { if (m_displayedResources.isEmpty()) return; if (!m_sortByRelevancy) { beginResetModel(); - qSort(m_displayedResources.begin(), m_displayedResources.end(), [this](AbstractResource* res, AbstractResource* res2){ return lessThan(res, res2); }); + std::sort(m_displayedResources.begin(), m_displayedResources.end(), [this](AbstractResource* res, AbstractResource* res2){ return lessThan(res, res2); }); endResetModel(); } } QString ResourcesProxyModel::lastSearch() const { return m_filters.search; } void ResourcesProxyModel::setOriginFilter(const QString &origin) { if (origin == m_filters.origin) return; m_filters.origin = origin; invalidateFilter(); } QString ResourcesProxyModel::originFilter() const { return m_filters.origin; } void ResourcesProxyModel::setFiltersFromCategory(Category *category) { if(category==m_filters.category) return; m_filters.category = category; invalidateFilter(); emit categoryChanged(); } void ResourcesProxyModel::fetchSubcategories() { auto cats = kVectorToSet(m_filters.category ? m_filters.category->subCategories() : CategoryModel::global()->rootCategories()); const int count = rowCount(); QSet done; for (int i=0; icategoryObjects(kSetToVector(cats)); done.unite(found); cats.subtract(found); } const QVariantList ret = kTransform(done, [](Category* cat) { return QVariant::fromValue(cat); }); if (ret != m_subcategories) { m_subcategories = ret; Q_EMIT subcategoriesChanged(m_subcategories); } } QVariantList ResourcesProxyModel::subcategories() const { return m_subcategories; } void ResourcesProxyModel::invalidateFilter() { if (!m_setup || ResourcesModel::global()->backends().isEmpty()) { return; } if (m_currentStream) { qCWarning(LIBDISCOVER_LOG) << "last stream isn't over yet" << m_filters << this; delete m_currentStream; } m_currentStream = ResourcesModel::global()->search(m_filters); Q_EMIT busyChanged(true); if (!m_displayedResources.isEmpty()) { beginResetModel(); m_displayedResources.clear(); endResetModel(); } connect(m_currentStream, &AggregatedResultsStream::resourcesFound, this, &ResourcesProxyModel::addResources); connect(m_currentStream, &AggregatedResultsStream::finished, this, [this]() { m_currentStream = nullptr; Q_EMIT busyChanged(false); }); } int ResourcesProxyModel::rowCount(const QModelIndex& parent) const { return parent.isValid() ? 0 : m_displayedResources.count(); } bool ResourcesProxyModel::lessThan(AbstractResource* leftPackage, AbstractResource* rightPackage) const { auto role = m_sortRole; Qt::SortOrder order = m_sortOrder; QVariant leftValue; QVariant rightValue; //if we're comparing two equal values, we want the model sorted by application name if(role != NameRole) { leftValue = roleToValue(leftPackage, role); rightValue = roleToValue(rightPackage, role); if (leftValue == rightValue) { role = NameRole; order = Qt::AscendingOrder; } } bool ret; if(role == NameRole) { ret = leftPackage->nameSortKey().compare(rightPackage->nameSortKey()) < 0; } else if(role == CanUpgrade) { ret = leftValue.toBool(); } else { ret = leftValue < rightValue; } return ret != (order != Qt::AscendingOrder); } Category* ResourcesProxyModel::filteredCategory() const { return m_filters.category; } void ResourcesProxyModel::setStateFilter(AbstractResource::State s) { if (s != m_filters.state) { m_filters.state = s; invalidateFilter(); emit stateFilterChanged(); } } AbstractResource::State ResourcesProxyModel::stateFilter() const { return m_filters.state; } QString ResourcesProxyModel::mimeTypeFilter() const { return m_filters.mimetype; } void ResourcesProxyModel::setMimeTypeFilter(const QString& mime) { if (m_filters.mimetype != mime) { m_filters.mimetype = mime; invalidateFilter(); } } QString ResourcesProxyModel::extends() const { return m_filters.extends; } void ResourcesProxyModel::setExtends(const QString& extends) { if (m_filters.extends != extends) { m_filters.extends = extends; invalidateFilter(); } } void ResourcesProxyModel::setFilterMinimumState(bool filterMinimumState) { if (filterMinimumState != m_filters.filterMinimumState) { m_filters.filterMinimumState = filterMinimumState; invalidateFilter(); Q_EMIT filterMinimumStateChanged(m_filters.filterMinimumState); } } bool ResourcesProxyModel::filterMinimumState() const { return m_filters.filterMinimumState; } QUrl ResourcesProxyModel::resourcesUrl() const { return m_filters.resourceUrl; } void ResourcesProxyModel::setResourcesUrl(const QUrl& resourcesUrl) { if (m_filters.resourceUrl != resourcesUrl) { m_filters.resourceUrl = resourcesUrl; invalidateFilter(); } } bool ResourcesProxyModel::allBackends() const { return m_filters.allBackends; } void ResourcesProxyModel::setAllBackends(bool allBackends) { m_filters.allBackends = allBackends; } QVariant ResourcesProxyModel::data(const QModelIndex& index, int role) const { if (!index.isValid()) { return QVariant(); } AbstractResource* const resource = m_displayedResources[index.row()]; return roleToValue(resource, role); } QVariant ResourcesProxyModel::roleToValue(AbstractResource* resource, int role) const { switch(role) { case ApplicationRole: return qVariantFromValue(resource); case RatingPointsRole: case RatingRole: case RatingCountRole: case SortableRatingRole: { Rating* const rating = resource->rating(); const int idx = Rating::staticMetaObject.indexOfProperty(roleNames().value(role).constData()); Q_ASSERT(idx >= 0); auto prop = Rating::staticMetaObject.property(idx); if (rating) return prop.read(rating); else { QVariant val(0); val.convert(prop.type()); return val; } } case Qt::DecorationRole: case Qt::DisplayRole: case Qt::StatusTipRole: case Qt::ToolTipRole: return QVariant(); default: { QByteArray roleText = roleNames().value(role); if(Q_UNLIKELY(roleText.isEmpty())) { qCDebug(LIBDISCOVER_LOG) << "unsupported role" << role; return {}; } static const QMetaObject* m = &AbstractResource::staticMetaObject; int propidx = roleText.isEmpty() ? -1 : m->indexOfProperty(roleText.constData()); if(Q_UNLIKELY(propidx < 0)) { qCWarning(LIBDISCOVER_LOG) << "unknown role:" << role << roleText; return QVariant(); } else return m->property(propidx).read(resource); } } } bool ResourcesProxyModel::isSorted(const QVector & resources) { auto last = resources.constFirst(); for(auto it = resources.constBegin()+1, itEnd = resources.constEnd(); it != itEnd; ++it) { if(!lessThan(last, *it)) { return false; } last = *it; } return true; } void ResourcesProxyModel::sortedInsertion(const QVector & _res) { auto resources = _res; Q_ASSERT(!resources.isEmpty()); if (!m_filters.allBackends) { removeDuplicates(resources); if (resources.isEmpty()) return; } if (m_sortByRelevancy || m_displayedResources.isEmpty()) { // Q_ASSERT(m_sortByRelevancy || isSorted(resources)); int rows = rowCount(); beginInsertRows({}, rows, rows+resources.count()-1); m_displayedResources += resources; endInsertRows(); return; } for(auto resource: qAsConst(resources)) { const auto finder = [this](AbstractResource* resource, AbstractResource* res){ return lessThan(resource, res); }; const auto it = std::upper_bound(m_displayedResources.constBegin(), m_displayedResources.constEnd(), resource, finder); const auto newIdx = it == m_displayedResources.constEnd() ? m_displayedResources.count() : (it - m_displayedResources.constBegin()); if ((it-1) != m_displayedResources.constEnd() && *(it-1) == resource) continue; beginInsertRows({}, newIdx, newIdx); m_displayedResources.insert(newIdx, resource); endInsertRows(); // Q_ASSERT(isSorted(resources)); } } void ResourcesProxyModel::refreshResource(AbstractResource* resource, const QVector& properties) { const auto residx = m_displayedResources.indexOf(resource); if (residx<0) { if (!m_sortByRelevancy && m_filters.shouldFilter(resource)) { sortedInsertion({resource}); } return; } if (!m_filters.shouldFilter(resource)) { beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); return; } const QModelIndex idx = index(residx, 0); Q_ASSERT(idx.isValid()); const auto roles = propertiesToRoles(properties); if (!m_sortByRelevancy && roles.contains(m_sortRole)) { beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); sortedInsertion({resource}); } else emit dataChanged(idx, idx, roles); } void ResourcesProxyModel::removeResource(AbstractResource* resource) { const auto residx = m_displayedResources.indexOf(resource); if (residx < 0) return; beginRemoveRows({}, residx, residx); m_displayedResources.removeAt(residx); endRemoveRows(); } void ResourcesProxyModel::refreshBackend(AbstractResourcesBackend* backend, const QVector& properties) { auto roles = propertiesToRoles(properties); const int count = m_displayedResources.count(); bool found = false; for(int i = 0; ibackend()) continue; int j = i+1; for(; jbackend(); ++j) {} Q_EMIT dataChanged(index(i, 0), index(j-1, 0), roles); i = j; found = true; } if (found && properties.contains(m_roles.value(m_sortRole))) { invalidateSorting(); } } QVector ResourcesProxyModel::propertiesToRoles(const QVector& properties) const { QVector roles = kTransform>(properties, [this](const QByteArray& arr) { return roleNames().key(arr, -1); }); roles.removeAll(-1); return roles; } int ResourcesProxyModel::indexOf(AbstractResource* res) { return m_displayedResources.indexOf(res); } AbstractResource * ResourcesProxyModel::resourceAt(int row) const { return m_displayedResources[row]; } bool ResourcesProxyModel::canFetchMore(const QModelIndex& parent) const { Q_ASSERT(!parent.isValid()); return m_currentStream; } void ResourcesProxyModel::fetchMore(const QModelIndex& parent) { Q_ASSERT(!parent.isValid()); if (!m_currentStream) return; m_currentStream->fetchMore(); } bool ResourcesProxyModel::sortByRelevancy() const { return m_sortByRelevancy; } diff --git a/libdiscover/resources/StandardBackendUpdater.cpp b/libdiscover/resources/StandardBackendUpdater.cpp index 79739e16..1662424b 100644 --- a/libdiscover/resources/StandardBackendUpdater.cpp +++ b/libdiscover/resources/StandardBackendUpdater.cpp @@ -1,253 +1,253 @@ /* * Copyright (C) 2012 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * 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 Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include "ResourcesModel.h" #include #include #include #include #include "libdiscover_debug.h" #include #include StandardBackendUpdater::StandardBackendUpdater(AbstractResourcesBackend* parent) : AbstractBackendUpdater(parent) , m_backend(parent) , m_settingUp(false) , m_progress(0) , m_lastUpdate(QDateTime()) { connect(m_backend, &AbstractResourcesBackend::fetchingChanged, this, &StandardBackendUpdater::refreshUpdateable); connect(m_backend, &AbstractResourcesBackend::resourcesChanged, this, &StandardBackendUpdater::resourcesChanged); connect(m_backend, &AbstractResourcesBackend::resourceRemoved, this, [this](AbstractResource* resource){ m_upgradeable.remove(resource); m_toUpgrade.remove(resource); }); connect(TransactionModel::global(), &TransactionModel::transactionRemoved, this, &StandardBackendUpdater::transactionRemoved); connect(TransactionModel::global(), &TransactionModel::transactionAdded, this, &StandardBackendUpdater::transactionAdded); m_timer.setSingleShot(true); m_timer.setInterval(10); connect(&m_timer, &QTimer::timeout, this, &StandardBackendUpdater::refreshUpdateable); } void StandardBackendUpdater::resourcesChanged(AbstractResource* res, const QVector& props) { if (props.contains("state") && (res->state() == AbstractResource::Upgradeable || m_upgradeable.contains(res))) m_timer.start(); } bool StandardBackendUpdater::hasUpdates() const { return !m_upgradeable.isEmpty(); } void StandardBackendUpdater::start() { m_settingUp = true; emit progressingChanged(true); setProgress(0); auto upgradeList = m_toUpgrade.toList(); - qSort(upgradeList.begin(), upgradeList.end(), [](const AbstractResource* a, const AbstractResource* b){ return a->name() < b->name(); }); + std::sort(upgradeList.begin(), upgradeList.end(), [](const AbstractResource* a, const AbstractResource* b){ return a->name() < b->name(); }); const bool couldCancel = m_canCancel; foreach(AbstractResource* res, upgradeList) { m_pendingResources += res; auto t = m_backend->installApplication(res); t->setVisible(false); t->setProperty("updater", QVariant::fromValue(this)); connect(t, &Transaction::downloadSpeedChanged, this, [this](){ Q_EMIT downloadSpeedChanged(downloadSpeed()); }); connect(this, &StandardBackendUpdater::cancelTransaction, t, &Transaction::cancel); TransactionModel::global()->addTransaction(t); m_canCancel |= t->isCancellable(); } if (m_canCancel != couldCancel) { Q_EMIT cancelableChanged(m_canCancel); } m_settingUp = false; if(m_pendingResources.isEmpty()) { cleanup(); } else { setProgress(1); } } void StandardBackendUpdater::cancel() { Q_EMIT cancelTransaction(); } void StandardBackendUpdater::transactionAdded(Transaction* newTransaction) { if (!m_pendingResources.contains(newTransaction->resource())) return; connect(newTransaction, &Transaction::progressChanged, this, &StandardBackendUpdater::transactionProgressChanged); } void StandardBackendUpdater::transactionProgressChanged(int percentage) { Transaction* t = qobject_cast(sender()); Q_EMIT resourceProgressed(t->resource(), percentage); } void StandardBackendUpdater::transactionRemoved(Transaction* t) { const bool fromOurBackend = t->resource() && t->resource()->backend()==m_backend; if (!fromOurBackend) { return; } const bool found = fromOurBackend && m_pendingResources.remove(t->resource()); if(found && !m_settingUp) { qreal p = 1-(qreal(m_pendingResources.size())/m_toUpgrade.size()); setProgress(100*p); if(m_pendingResources.isEmpty()) { cleanup(); } } refreshUpdateable(); } void StandardBackendUpdater::refreshUpdateable() { if (m_backend->isFetching() || !m_backend->isValid()) { return; } if (isProgressing()) { m_timer.start(1000); return; } m_settingUp = true; Q_EMIT progressingChanged(true); AbstractResourcesBackend::Filters f; f.state = AbstractResource::Upgradeable; m_upgradeable.clear(); auto r = m_backend->search(f); connect(r, &ResultsStream::resourcesFound, this, [this](const QVector &resources){ for(auto res : resources) if (res->state() == AbstractResource::Upgradeable) m_upgradeable.insert(res); }); connect(r, &ResultsStream::destroyed, this, [this](){ m_settingUp = false; Q_EMIT updatesCountChanged(updatesCount()); Q_EMIT progressingChanged(false); }); } qreal StandardBackendUpdater::progress() const { return m_progress; } void StandardBackendUpdater::setProgress(qreal p) { if(p>m_progress || p<0) { m_progress = p; emit progressChanged(p); } } void StandardBackendUpdater::prepare() { m_lastUpdate = QDateTime::currentDateTime(); m_toUpgrade = m_upgradeable; } int StandardBackendUpdater::updatesCount() const { return m_upgradeable.count(); } void StandardBackendUpdater::addResources(const QList< AbstractResource* >& apps) { Q_ASSERT(m_upgradeable.contains(apps.toSet())); m_toUpgrade += apps.toSet(); } void StandardBackendUpdater::removeResources(const QList< AbstractResource* >& apps) { Q_ASSERT(m_upgradeable.contains(apps.toSet())); Q_ASSERT(m_toUpgrade.contains(apps.toSet())); m_toUpgrade -= apps.toSet(); } void StandardBackendUpdater::cleanup() { m_lastUpdate = QDateTime::currentDateTime(); m_toUpgrade.clear(); refreshUpdateable(); emit progressingChanged(false); } QList StandardBackendUpdater::toUpdate() const { return m_toUpgrade.toList(); } bool StandardBackendUpdater::isMarked(AbstractResource* res) const { return m_toUpgrade.contains(res); } QDateTime StandardBackendUpdater::lastUpdate() const { return m_lastUpdate; } bool StandardBackendUpdater::isCancelable() const { return m_canCancel; } bool StandardBackendUpdater::isProgressing() const { return m_settingUp || !m_pendingResources.isEmpty(); } double StandardBackendUpdater::updateSize() const { double ret = 0.; for(AbstractResource* res: m_toUpgrade) { ret += res->size(); } return ret; } quint64 StandardBackendUpdater::downloadSpeed() const { quint64 ret = 0; for(Transaction* t: TransactionModel::global()->transactions()) { if (t->property("updater").value() == this) ret += t->downloadSpeed(); } return ret; } diff --git a/libdiscover/tests/CategoriesTest.cpp b/libdiscover/tests/CategoriesTest.cpp index c39b18fb..9d86aa5c 100644 --- a/libdiscover/tests/CategoriesTest.cpp +++ b/libdiscover/tests/CategoriesTest.cpp @@ -1,69 +1,69 @@ /* * Copyright (C) 2015 Aleix Pol Gonzalez * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library/Lesser General Public License * version 2, or (at your option) any later version, as published by the * Free Software Foundation * * 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 Library/Lesser General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include #include #include class CategoriesTest : public QObject { Q_OBJECT public: CategoriesTest() {} QVector populateCategories() { const QVector categoryFiles = { QFINDTESTDATA("../backends/BodegaBackend/muon-bodegawallpapers-backend-categories.xml"), QFINDTESTDATA("../backends/PackageKitBackend/packagekit-backend-categories.xml"), QFINDTESTDATA("../backends/AkabeiBackend/akabei-backend-categories.xml"), QFINDTESTDATA("../backends/KNSBackend/knscomic-backend-categories.xml"), QFINDTESTDATA("../backends/KNSBackend/knsplasmoids-backend-categories.xml"), QFINDTESTDATA("../backends/ApplicationBackend/ubuntu_sso_dbus_interface.xml"), QFINDTESTDATA("../backends/ApplicationBackend/qapt-backend-categories.xml"), QFINDTESTDATA("../backends/DummyBackend/dummy-backend-categories.xml") }; QVector ret; CategoriesReader reader; Q_FOREACH (const QString& name, categoryFiles) { const QVector cats = reader.loadCategoriesPath(name); if(ret.isEmpty()) { ret = cats; } else { Q_FOREACH (Category* c, cats) Category::addSubcategory(ret, c); } } - qSort(ret.begin(), ret.end(), Category::categoryLessThan); + std::sort(ret.begin(), ret.end(), Category::categoryLessThan); return ret; } private Q_SLOTS: void testReadCategories() { auto categories = populateCategories(); QVERIFY(!categories.isEmpty()); } }; QTEST_MAIN( CategoriesTest ) #include "CategoriesTest.moc"