File indexing completed on 2024-04-28 07:45:16
0001 /* 0002 SPDX-FileCopyrightText: 2016 Dan Leinir Turthra Jensen <admin@leinir.dk> 0003 0004 SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0005 */ 0006 0007 #include "quickitemsmodel.h" 0008 #include "core/commentsmodel.h" 0009 #include "downloadlinkinfo.h" 0010 #include "itemsmodel.h" 0011 #include "quickengine.h" 0012 0013 #include <KShell> 0014 #include <QProcess> 0015 0016 class ItemsModelPrivate 0017 { 0018 public: 0019 ItemsModelPrivate(ItemsModel *qq) 0020 : q(qq) 0021 , model(nullptr) 0022 , engine(nullptr) 0023 { 0024 } 0025 ItemsModel *q; 0026 KNSCore::ItemsModel *model; 0027 Engine *engine; 0028 0029 QHash<QString, KNSCore::CommentsModel *> commentsModels; 0030 0031 bool initModel() 0032 { 0033 if (model) { 0034 return true; 0035 } 0036 if (!engine) { 0037 return false; 0038 } 0039 model = new KNSCore::ItemsModel(engine, q); 0040 0041 q->connect(engine, &Engine::signalProvidersLoaded, engine, &Engine::reloadEntries); 0042 // Entries have been fetched and should be shown: 0043 q->connect(engine, &Engine::signalEntriesLoaded, model, [this](const KNSCore::Entry::List &entries) { 0044 model->slotEntriesLoaded(entries); 0045 }); 0046 q->connect(engine, &Engine::entryEvent, model, [this](const KNSCore::Entry &entry, KNSCore::Entry::EntryEvent event) { 0047 if (event == KNSCore::Entry::DetailsLoadedEvent && engine->filter() != KNSCore::Provider::Updates) { 0048 model->slotEntriesLoaded(KNSCore::Entry::List{entry}); 0049 } 0050 }); 0051 0052 // Check if we need intermediate states 0053 q->connect(engine, &Engine::entryEvent, q, [this](const KNSCore::Entry &entry, KNSCore::Entry::EntryEvent event) { 0054 onEntryEvent(entry, event); 0055 }); 0056 q->connect(engine, &Engine::signalResetView, model, &KNSCore::ItemsModel::clearEntries); 0057 0058 q->connect(model, &KNSCore::ItemsModel::loadPreview, engine, &Engine::loadPreview); 0059 q->connect(engine, &Engine::entryPreviewLoaded, model, &KNSCore::ItemsModel::slotEntryPreviewLoaded); 0060 0061 q->connect(model, &KNSCore::ItemsModel::rowsInserted, q, &ItemsModel::rowsInserted); 0062 q->connect(model, &KNSCore::ItemsModel::rowsRemoved, q, &ItemsModel::rowsRemoved); 0063 q->connect(model, &KNSCore::ItemsModel::dataChanged, q, &ItemsModel::dataChanged); 0064 q->connect(model, &KNSCore::ItemsModel::modelAboutToBeReset, q, &ItemsModel::modelAboutToBeReset); 0065 q->connect(model, &KNSCore::ItemsModel::modelReset, q, &ItemsModel::modelReset); 0066 return true; 0067 } 0068 0069 void onEntryEvent(const KNSCore::Entry &entry, KNSCore::Entry::EntryEvent event) 0070 { 0071 if (event == KNSCore::Entry::StatusChangedEvent) { 0072 model->slotEntryChanged(entry); 0073 Q_EMIT q->entryChanged(entry); 0074 0075 // If we update/uninstall an entry we have to update the UI, see BUG: 425135 0076 if (engine->filter() == KNSCore::Provider::Updates && entry.status() != KNSCore::Entry::Updateable && entry.status() != KNSCore::Entry::Updating) { 0077 model->removeEntry(entry); 0078 } else if (engine->filter() == KNSCore::Provider::Installed && entry.status() == KNSCore::Entry::Deleted) { 0079 model->removeEntry(entry); 0080 } 0081 } 0082 0083 if (event == KNSCore::Entry::DetailsLoadedEvent) { 0084 model->slotEntryChanged(entry); 0085 Q_EMIT q->entryChanged(entry); 0086 } 0087 } 0088 }; 0089 0090 ItemsModel::ItemsModel(QObject *parent) 0091 : QAbstractListModel(parent) 0092 , d(new ItemsModelPrivate(this)) 0093 { 0094 } 0095 0096 ItemsModel::~ItemsModel() = default; 0097 0098 QHash<int, QByteArray> ItemsModel::roleNames() const 0099 { 0100 static const QHash<int, QByteArray> roles = QHash<int, QByteArray>{ 0101 {Qt::DisplayRole, "display"}, 0102 {NameRole, "name"}, 0103 {UniqueIdRole, "uniqueId"}, 0104 {CategoryRole, "category"}, 0105 {HomepageRole, "homepage"}, 0106 {AuthorRole, "author"}, 0107 {LicenseRole, "license"}, 0108 {ShortSummaryRole, "shortSummary"}, 0109 {SummaryRole, "summary"}, 0110 {ChangelogRole, "changelog"}, 0111 {VersionRole, "version"}, 0112 {ReleaseDateRole, "releaseDate"}, 0113 {UpdateVersionRole, "updateVersion"}, 0114 {UpdateReleaseDateRole, "updateReleaseDate"}, 0115 {PayloadRole, "payload"}, 0116 {Qt::DecorationRole, "decoration"}, 0117 {PreviewsSmallRole, "previewsSmall"}, 0118 {PreviewsRole, "previews"}, 0119 {InstalledFilesRole, "installedFiles"}, 0120 {UnInstalledFilesRole, "uninstalledFiles"}, 0121 {RatingRole, "rating"}, 0122 {NumberOfCommentsRole, "numberOfComments"}, 0123 {DownloadCountRole, "downloadCount"}, 0124 {NumberFansRole, "numberFans"}, 0125 {NumberKnowledgebaseEntriesRole, "numberKnowledgebaseEntries"}, 0126 {KnowledgebaseLinkRole, "knowledgebaseLink"}, 0127 {DownloadLinksRole, "downloadLinks"}, 0128 {DonationLinkRole, "donationLink"}, 0129 {ProviderIdRole, "providerId"}, 0130 {SourceRole, "source"}, 0131 {EntryRole, "entry"}, 0132 }; 0133 return roles; 0134 } 0135 0136 int ItemsModel::rowCount(const QModelIndex &parent) const 0137 { 0138 if (parent.isValid()) { 0139 return 0; 0140 } 0141 if (d->initModel()) { 0142 return d->model->rowCount(QModelIndex()); 0143 } 0144 return 0; 0145 } 0146 0147 QVariant ItemsModel::data(const QModelIndex &index, int role) const 0148 { 0149 if (index.isValid() && d->initModel()) { 0150 KNSCore::Entry entry = d->model->data(d->model->index(index.row()), Qt::UserRole).value<KNSCore::Entry>(); 0151 switch (role) { 0152 case NameRole: 0153 case Qt::DisplayRole: 0154 return entry.name(); 0155 case EntryRole: 0156 return QVariant::fromValue(entry); 0157 case UniqueIdRole: 0158 return entry.uniqueId(); 0159 case CategoryRole: 0160 return entry.category(); 0161 case HomepageRole: 0162 return entry.homepage(); 0163 break; 0164 case AuthorRole: { 0165 KNSCore::Author author = entry.author(); 0166 QVariantMap returnAuthor; 0167 returnAuthor[QStringLiteral("id")] = author.id(); 0168 returnAuthor[QStringLiteral("name")] = author.name(); 0169 returnAuthor[QStringLiteral("email")] = author.email(); 0170 returnAuthor[QStringLiteral("homepage")] = author.homepage(); 0171 returnAuthor[QStringLiteral("jabber")] = author.jabber(); 0172 returnAuthor[QStringLiteral("avatarUrl")] = author.avatarUrl(); 0173 returnAuthor[QStringLiteral("description")] = author.description(); 0174 return returnAuthor; 0175 } break; 0176 case LicenseRole: 0177 return entry.license(); 0178 case ShortSummaryRole: 0179 return entry.shortSummary(); 0180 case SummaryRole: 0181 return entry.summary(); 0182 case ChangelogRole: 0183 return entry.changelog(); 0184 case VersionRole: 0185 return entry.version(); 0186 case ReleaseDateRole: 0187 return entry.releaseDate(); 0188 case UpdateVersionRole: 0189 return entry.updateVersion(); 0190 case UpdateReleaseDateRole: 0191 return entry.updateReleaseDate(); 0192 case PayloadRole: 0193 return entry.payload(); 0194 case Qt::DecorationRole: 0195 return entry.previewUrl(KNSCore::Entry::PreviewSmall1); 0196 case PreviewsSmallRole: { 0197 QStringList previews; 0198 previews << entry.previewUrl(KNSCore::Entry::PreviewSmall1); 0199 previews << entry.previewUrl(KNSCore::Entry::PreviewSmall2); 0200 previews << entry.previewUrl(KNSCore::Entry::PreviewSmall3); 0201 while (!previews.isEmpty() && previews.last().isEmpty()) { 0202 previews.takeLast(); 0203 } 0204 return previews; 0205 } 0206 case PreviewsRole: { 0207 QStringList previews; 0208 previews << entry.previewUrl(KNSCore::Entry::PreviewBig1); 0209 previews << entry.previewUrl(KNSCore::Entry::PreviewBig2); 0210 previews << entry.previewUrl(KNSCore::Entry::PreviewBig3); 0211 while (!previews.isEmpty() && previews.last().isEmpty()) { 0212 previews.takeLast(); 0213 } 0214 return previews; 0215 } 0216 case InstalledFilesRole: 0217 return entry.installedFiles(); 0218 case UnInstalledFilesRole: 0219 return entry.uninstalledFiles(); 0220 case RatingRole: 0221 return entry.rating(); 0222 case NumberOfCommentsRole: 0223 return entry.numberOfComments(); 0224 case DownloadCountRole: 0225 return entry.downloadCount(); 0226 case NumberFansRole: 0227 return entry.numberFans(); 0228 case NumberKnowledgebaseEntriesRole: 0229 return entry.numberKnowledgebaseEntries(); 0230 case KnowledgebaseLinkRole: 0231 return entry.knowledgebaseLink(); 0232 case DownloadLinksRole: { 0233 // This would be good to cache... but it also needs marking as dirty, somehow... 0234 const QList<KNSCore::Entry::DownloadLinkInformation> dllinks = entry.downloadLinkInformationList(); 0235 QVariantList list; 0236 for (const KNSCore::Entry::DownloadLinkInformation &link : dllinks) { 0237 list.append(QVariant::fromValue(DownloadLinkInfo(link))); 0238 } 0239 if (list.isEmpty() && !entry.payload().isEmpty()) { 0240 KNSCore::Entry::DownloadLinkInformation data; 0241 data.descriptionLink = entry.payload(); 0242 list.append(QVariant::fromValue(DownloadLinkInfo(data))); 0243 } 0244 return QVariant::fromValue(list); 0245 } 0246 case DonationLinkRole: 0247 return entry.donationLink(); 0248 case ProviderIdRole: 0249 return entry.providerId(); 0250 case SourceRole: { 0251 KNSCore::Entry::Source src = entry.source(); 0252 switch (src) { 0253 case KNSCore::Entry::Cache: 0254 return QStringLiteral("Cache"); 0255 case KNSCore::Entry::Online: 0256 return QStringLiteral("Online"); 0257 case KNSCore::Entry::Registry: 0258 return QStringLiteral("Registry"); 0259 default: 0260 return QStringLiteral("Unknown source - shouldn't be possible"); 0261 } 0262 } 0263 case CommentsModelRole: { 0264 KNSCore::CommentsModel *commentsModel{nullptr}; 0265 if (!d->commentsModels.contains(entry.uniqueId())) { 0266 commentsModel = new KNSCore::CommentsModel(d->engine); 0267 commentsModel->setEntry(entry); 0268 d->commentsModels[entry.uniqueId()] = commentsModel; 0269 } else { 0270 commentsModel = d->commentsModels[entry.uniqueId()]; 0271 } 0272 return QVariant::fromValue(commentsModel); 0273 } 0274 default: 0275 return QStringLiteral("Unknown role"); 0276 } 0277 } 0278 return QVariant(); 0279 } 0280 0281 bool ItemsModel::canFetchMore(const QModelIndex &parent) const 0282 { 0283 return !parent.isValid() && d->engine && d->engine->categoriesMetadata().count() > 0; 0284 } 0285 0286 void ItemsModel::fetchMore(const QModelIndex &parent) 0287 { 0288 if (parent.isValid() || !d->engine) { 0289 return; 0290 } 0291 d->engine->requestMoreData(); 0292 } 0293 0294 QObject *ItemsModel::engine() const 0295 { 0296 return d->engine; 0297 } 0298 0299 void ItemsModel::setEngine(QObject *newEngine) 0300 { 0301 if (d->engine != newEngine) { 0302 beginResetModel(); 0303 d->engine = qobject_cast<Engine *>(newEngine); 0304 if (d->model) { 0305 d->model->deleteLater(); 0306 d->model = nullptr; 0307 } 0308 Q_EMIT engineChanged(); 0309 endResetModel(); 0310 } 0311 } 0312 0313 int ItemsModel::indexOfEntryId(const QString &providerId, const QString &entryId) 0314 { 0315 int idx{-1}; 0316 if (d->engine && d->model) { 0317 for (int i = 0; i < rowCount(); ++i) { 0318 KNSCore::Entry testEntry = d->model->data(d->model->index(i), Qt::UserRole).value<KNSCore::Entry>(); 0319 if (providerId == testEntry.providerId() && entryId == testEntry.uniqueId()) { 0320 idx = i; 0321 break; 0322 } 0323 } 0324 } 0325 return idx; 0326 } 0327 0328 #include "moc_quickitemsmodel.cpp"