File indexing completed on 2024-05-05 17:33:20
0001 /* 0002 * SPDX-FileCopyrightText: 2012 Aleix Pol Gonzalez <aleixpol@blue-systems.com> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0005 */ 0006 0007 #include "AbstractResource.h" 0008 #include "AbstractResourcesBackend.h" 0009 #include "libdiscover_debug.h" 0010 #include <Category/CategoryModel.h> 0011 #include <KFormat> 0012 #include <KLocalizedString> 0013 #include <KShell> 0014 #include <QList> 0015 #include <QProcess> 0016 #include <QString> 0017 #include <ReviewsBackend/AbstractReviewsBackend.h> 0018 #include <ReviewsBackend/Rating.h> 0019 0020 AbstractResource::AbstractResource(AbstractResourcesBackend *parent) 0021 : QObject(parent) 0022 { 0023 connect(this, &AbstractResource::stateChanged, this, &AbstractResource::sizeChanged); 0024 connect(this, &AbstractResource::stateChanged, this, &AbstractResource::versionsChanged); 0025 connect(this, &AbstractResource::stateChanged, this, &AbstractResource::reportNewState); 0026 } 0027 0028 AbstractResource::~AbstractResource() = default; 0029 0030 QUrl AbstractResource::homepage() 0031 { 0032 return QUrl(); 0033 } 0034 0035 QUrl AbstractResource::helpURL() 0036 { 0037 return QUrl(); 0038 } 0039 0040 QUrl AbstractResource::bugURL() 0041 { 0042 return QUrl(); 0043 } 0044 0045 QUrl AbstractResource::donationURL() 0046 { 0047 return QUrl(); 0048 } 0049 0050 QUrl AbstractResource::contributeURL() 0051 { 0052 return {}; 0053 } 0054 0055 void AbstractResource::addMetadata(const QString &key, const QJsonValue &value) 0056 { 0057 m_metadata.insert(key, value); 0058 } 0059 0060 QJsonValue AbstractResource::getMetadata(const QString &key) 0061 { 0062 return m_metadata.value(key); 0063 } 0064 0065 bool AbstractResource::canUpgrade() 0066 { 0067 return state() == Upgradeable; 0068 } 0069 0070 bool AbstractResource::isInstalled() 0071 { 0072 return state() >= Installed; 0073 } 0074 0075 void AbstractResource::fetchScreenshots() 0076 { 0077 Q_EMIT screenshotsFetched({}); 0078 } 0079 0080 QStringList AbstractResource::mimetypes() const 0081 { 0082 return QStringList(); 0083 } 0084 0085 AbstractResourcesBackend *AbstractResource::backend() const 0086 { 0087 return static_cast<AbstractResourcesBackend *>(parent()); 0088 } 0089 0090 QObject *AbstractResource::backendObject() const 0091 { 0092 return parent(); 0093 } 0094 0095 QString AbstractResource::status() 0096 { 0097 switch (state()) { 0098 case Broken: 0099 return i18n("Broken"); 0100 case None: 0101 return i18n("Available"); 0102 case Installed: 0103 return i18n("Installed"); 0104 case Upgradeable: 0105 return i18n("Upgradeable"); 0106 } 0107 return QString(); 0108 } 0109 0110 QString AbstractResource::sizeDescription() 0111 { 0112 return KFormat().formatByteSize(size()); 0113 } 0114 0115 QCollatorSortKey AbstractResource::nameSortKey() 0116 { 0117 if (!m_collatorKey) { 0118 m_collatorKey.reset(new QCollatorSortKey(QCollator().sortKey(name()))); 0119 } 0120 return *m_collatorKey; 0121 } 0122 0123 Rating *AbstractResource::rating() const 0124 { 0125 AbstractReviewsBackend *ratings = backend()->reviewsBackend(); 0126 return ratings ? ratings->ratingForApplication(const_cast<AbstractResource *>(this)) : nullptr; 0127 } 0128 0129 QVariant AbstractResource::ratingVariant() const 0130 { 0131 auto instance = rating(); 0132 return instance ? QVariant::fromValue<Rating>(*instance) : QVariant(); 0133 } 0134 0135 QStringList AbstractResource::extends() const 0136 { 0137 return {}; 0138 } 0139 0140 QString AbstractResource::appstreamId() const 0141 { 0142 return {}; 0143 } 0144 0145 void AbstractResource::reportNewState() 0146 { 0147 if (backend()->isFetching()) 0148 return; 0149 0150 static const QVector<QByteArray> ns = {"state", "status", "canUpgrade", "size", "sizeDescription", "installedVersion", "availableVersion"}; 0151 Q_EMIT backend()->resourcesChanged(this, ns); 0152 } 0153 0154 static bool shouldFilter(AbstractResource *res, const CategoryFilter &filter) 0155 { 0156 bool ret = true; 0157 switch (filter.type) { 0158 case CategoryFilter::CategoryNameFilter: 0159 ret = res->categories().contains(std::get<QString>(filter.value)); 0160 break; 0161 case CategoryFilter::PkgSectionFilter: 0162 ret = res->section() == std::get<QString>(filter.value); 0163 break; 0164 case CategoryFilter::PkgWildcardFilter: { 0165 QString wildcard = std::get<QString>(filter.value); 0166 wildcard.remove(QLatin1Char('*')); 0167 ret = res->packageName().contains(wildcard); 0168 } break; 0169 case CategoryFilter::AppstreamIdWildcardFilter: { 0170 QString wildcard = std::get<QString>(filter.value); 0171 wildcard.remove(QLatin1Char('*')); 0172 ret = res->appstreamId().contains(wildcard); 0173 } break; 0174 case CategoryFilter::PkgNameFilter: // Only useful in the not filters 0175 ret = res->packageName() == std::get<QString>(filter.value); 0176 break; 0177 case CategoryFilter::AndFilter: { 0178 const auto filters = std::get<QVector<CategoryFilter>>(filter.value); 0179 ret = std::all_of(filters.begin(), filters.end(), [res](const CategoryFilter &f) { 0180 return shouldFilter(res, f); 0181 }); 0182 break; 0183 } 0184 case CategoryFilter::OrFilter: { 0185 const auto filters = std::get<QVector<CategoryFilter>>(filter.value); 0186 ret = std::any_of(filters.begin(), filters.end(), [res](const CategoryFilter &f) { 0187 return shouldFilter(res, f); 0188 }); 0189 break; 0190 } 0191 case CategoryFilter::NotFilter: { 0192 const auto filters = std::get<QVector<CategoryFilter>>(filter.value); 0193 ret = !std::any_of(filters.begin(), filters.end(), [res](const CategoryFilter &f) { 0194 return shouldFilter(res, f); 0195 }); 0196 break; 0197 } 0198 } 0199 return ret; 0200 } 0201 0202 bool AbstractResource::categoryMatches(Category *cat) 0203 { 0204 return shouldFilter(this, cat->filter()); 0205 } 0206 0207 static QSet<Category *> walkCategories(AbstractResource *res, const QVector<Category *> &cats) 0208 { 0209 QSet<Category *> ret; 0210 for (Category *cat : cats) { 0211 if (res->categoryMatches(cat)) { 0212 const auto subcats = walkCategories(res, cat->subCategories()); 0213 if (subcats.isEmpty()) { 0214 ret += cat; 0215 } else { 0216 ret += subcats; 0217 } 0218 } 0219 } 0220 0221 return ret; 0222 } 0223 0224 QSet<Category *> AbstractResource::categoryObjects(const QVector<Category *> &cats) const 0225 { 0226 return walkCategories(const_cast<AbstractResource *>(this), cats); 0227 } 0228 0229 QUrl AbstractResource::url() const 0230 { 0231 const QString asid = appstreamId(); 0232 return asid.isEmpty() ? QUrl(backend()->name() + QStringLiteral("://") + packageName()) : QUrl(QStringLiteral("appstream://") + asid); 0233 } 0234 0235 QString AbstractResource::displayOrigin() const 0236 { 0237 return origin(); 0238 } 0239 0240 QString AbstractResource::executeLabel() const 0241 { 0242 return i18n("Launch"); 0243 } 0244 0245 QString AbstractResource::upgradeText() const 0246 { 0247 QString installed = installedVersion(), available = availableVersion(); 0248 if (installed == available) { 0249 // Update of the same version; show when old and new are 0250 // the same (common with Flatpak runtimes) 0251 return i18nc("@info 'Refresh' is used as a noun here, and %1 is an app's version number", "Refresh of version %1", available); 0252 } else if (!installed.isEmpty() && !available.isEmpty()) { 0253 // Old and new version numbers 0254 // This thing with \u009C is a fancy feature in QML text handling: 0255 // when the string will be elided, it shows the string after 0256 // the last \u009C. This allows us to show a smaller string 0257 // when there's now enough room 0258 0259 // All of this is mostly for the benefit of KDE Neon users, 0260 // since the version strings there are really really long 0261 return i18nc("Do not translate or alter \\u009C", "%1 → %2\u009C%1 → %2\u009C%2", installed, available); 0262 } else { 0263 // Available version only, for when the installed version 0264 // isn't available for some reason 0265 return available; 0266 } 0267 } 0268 0269 QString AbstractResource::versionString() 0270 { 0271 const QString version = isInstalled() ? installedVersion() : availableVersion(); 0272 if (version.isEmpty()) { 0273 return {}; 0274 } else { 0275 QLocale l; 0276 const QString releaseString = l.toString(releaseDate(), QLocale::ShortFormat); 0277 if (!releaseString.isEmpty()) { 0278 return i18n("%1, released on %2", version, releaseString); 0279 } else { 0280 return version; 0281 } 0282 } 0283 } 0284 0285 QString AbstractResource::contentRatingDescription() const 0286 { 0287 return {}; 0288 } 0289 0290 AbstractResource::ContentIntensity AbstractResource::contentRatingIntensity() const 0291 { 0292 return Mild; 0293 } 0294 0295 QString AbstractResource::contentRatingText() const 0296 { 0297 return {}; 0298 } 0299 0300 uint AbstractResource::contentRatingMinimumAge() const 0301 { 0302 return 0; 0303 }