Warning, file /frameworks/knewstuff/src/qtquick/quickitemsmodel.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
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 "engine.h" 0011 #include "itemsmodel.h" 0012 #include "quickengine.h" 0013 0014 #include <KShell> 0015 #include <QProcess> 0016 0017 class ItemsModelPrivate 0018 { 0019 public: 0020 ItemsModelPrivate(ItemsModel *qq) 0021 : q(qq) 0022 , model(nullptr) 0023 , engine(nullptr) 0024 , coreEngine(nullptr) 0025 { 0026 } 0027 ItemsModel *q; 0028 KNSCore::ItemsModel *model; 0029 Engine *engine; 0030 KNSCore::Engine *coreEngine; 0031 0032 QHash<QString, KNSCore::CommentsModel *> commentsModels; 0033 0034 bool isLoadingData{false}; 0035 0036 bool initModel() 0037 { 0038 if (model) { 0039 return true; 0040 } 0041 if (!coreEngine) { 0042 return false; 0043 } 0044 model = new KNSCore::ItemsModel(coreEngine, q); 0045 q->connect(coreEngine, &KNSCore::Engine::busyStateChanged, q, [=]() { 0046 // If we install/update an entry the spinner should be hidden, BUG: 422047 0047 const KNSCore::Engine::BusyState state = coreEngine->busyState(); 0048 const bool busy = state && !state.testFlag(KNSCore::Engine::BusyOperation::InstallingEntry); 0049 if (isLoadingData != busy) { 0050 isLoadingData = busy; 0051 Q_EMIT q->isLoadingDataChanged(); 0052 } 0053 }); 0054 0055 q->connect(coreEngine, &KNSCore::Engine::signalProvidersLoaded, coreEngine, &KNSCore::Engine::reloadEntries); 0056 // Entries have been fetched and should be shown: 0057 q->connect(coreEngine, &KNSCore::Engine::signalEntriesLoaded, model, [this](const KNSCore::EntryInternal::List &entries) { 0058 if (coreEngine->filter() != KNSCore::Provider::Updates) { 0059 model->slotEntriesLoaded(entries); 0060 } 0061 }); 0062 q->connect(coreEngine, 0063 &KNSCore::Engine::signalEntryEvent, 0064 model, 0065 [this](const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event) { 0066 if (event == KNSCore::EntryInternal::DetailsLoadedEvent && coreEngine->filter() != KNSCore::Provider::Updates) { 0067 model->slotEntriesLoaded(KNSCore::EntryInternal::List{entry}); 0068 } 0069 }); 0070 q->connect(coreEngine, &KNSCore::Engine::signalUpdateableEntriesLoaded, model, [this](const KNSCore::EntryInternal::List &entries) { 0071 if (coreEngine->filter() == KNSCore::Provider::Updates) { 0072 model->slotEntriesLoaded(entries); 0073 } 0074 }); 0075 0076 q->connect(coreEngine, &KNSCore::Engine::signalEntryEvent, q, [this](const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event) { 0077 onEntryEvent(entry, event); 0078 }); 0079 q->connect(coreEngine, &KNSCore::Engine::signalResetView, model, &KNSCore::ItemsModel::clearEntries); 0080 q->connect(coreEngine, &KNSCore::Engine::signalEntryPreviewLoaded, model, &KNSCore::ItemsModel::slotEntryPreviewLoaded); 0081 0082 q->connect(model, &KNSCore::ItemsModel::rowsInserted, q, &ItemsModel::rowsInserted); 0083 q->connect(model, &KNSCore::ItemsModel::rowsRemoved, q, &ItemsModel::rowsRemoved); 0084 q->connect(model, &KNSCore::ItemsModel::dataChanged, q, &ItemsModel::dataChanged); 0085 q->connect(model, &KNSCore::ItemsModel::modelReset, q, &ItemsModel::modelReset); 0086 return true; 0087 } 0088 0089 void onEntryEvent(const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event) 0090 { 0091 if (event == KNSCore::EntryInternal::StatusChangedEvent) { 0092 model->slotEntryChanged(entry); 0093 Q_EMIT q->entryChanged(model->row(entry)); 0094 0095 // If we update/uninstall an entry we have to update the UI, see BUG: 425135 0096 if (coreEngine->filter() == KNSCore::Provider::Updates && entry.status() != KNS3::Entry::Updateable && entry.status() != KNS3::Entry::Updating) { 0097 model->removeEntry(entry); 0098 } else if (coreEngine->filter() == KNSCore::Provider::Installed && entry.status() == KNS3::Entry::Deleted) { 0099 model->removeEntry(entry); 0100 } 0101 } 0102 } 0103 }; 0104 0105 ItemsModel::ItemsModel(QObject *parent) 0106 : QAbstractListModel(parent) 0107 , d(new ItemsModelPrivate(this)) 0108 { 0109 } 0110 0111 ItemsModel::~ItemsModel() = default; 0112 0113 QHash<int, QByteArray> ItemsModel::roleNames() const 0114 { 0115 // clang-format off 0116 static const QHash<int, QByteArray> roles = QHash<int, QByteArray>{ 0117 {Qt::DisplayRole, "display"}, 0118 {NameRole, "name"}, 0119 {UniqueIdRole, "uniqueId"}, 0120 {CategoryRole, "category"}, 0121 {HomepageRole, "homepage"}, 0122 {AuthorRole, "author"}, 0123 {LicenseRole, "license"}, 0124 {ShortSummaryRole, "shortSummary"}, 0125 {SummaryRole, "summary"}, 0126 {ChangelogRole, "changelog"}, 0127 {VersionRole, "version"}, 0128 {ReleaseDateRole, "releaseDate"}, 0129 {UpdateVersionRole, "updateVersion"}, 0130 {UpdateReleaseDateRole, "updateReleaseDate"}, 0131 {PayloadRole, "payload"}, 0132 {Qt::DecorationRole, "decoration"}, 0133 {PreviewsSmallRole, "previewsSmall"}, 0134 {PreviewsRole, "previews"}, 0135 {InstalledFilesRole, "installedFiles"}, 0136 {UnInstalledFilesRole, "uninstalledFiles"}, 0137 {RatingRole, "rating"}, 0138 {NumberOfCommentsRole, "numberOfComments"}, 0139 {DownloadCountRole, "downloadCount"}, 0140 {NumberFansRole, "numberFans"}, 0141 {NumberKnowledgebaseEntriesRole, "numberKnowledgebaseEntries"}, 0142 {KnowledgebaseLinkRole, "knowledgebaseLink"}, 0143 {DownloadLinksRole, "downloadLinks"}, 0144 {DonationLinkRole, "donationLink"}, 0145 {ProviderIdRole, "providerId"}, 0146 {SourceRole, "source"}, 0147 {StatusRole, "status"}, 0148 {EntryTypeRole, "entryType"}, 0149 }; 0150 // clang-format on 0151 return roles; 0152 } 0153 0154 int ItemsModel::rowCount(const QModelIndex &parent) const 0155 { 0156 if (parent.isValid()) { 0157 return 0; 0158 } 0159 if (d->initModel()) { 0160 return d->model->rowCount(QModelIndex()); 0161 } 0162 return 0; 0163 } 0164 0165 QVariant ItemsModel::data(const QModelIndex &index, int role) const 0166 { 0167 QVariant data; 0168 if (index.isValid() && d->initModel()) { 0169 KNSCore::EntryInternal entry = d->model->data(d->model->index(index.row()), Qt::UserRole).value<KNSCore::EntryInternal>(); 0170 switch (role) { 0171 case NameRole: 0172 case Qt::DisplayRole: 0173 data.setValue<QString>(entry.name()); 0174 break; 0175 case UniqueIdRole: 0176 data.setValue<QString>(entry.uniqueId()); 0177 break; 0178 case CategoryRole: 0179 data.setValue<QString>(entry.category()); 0180 break; 0181 case HomepageRole: 0182 data.setValue<QUrl>(entry.homepage()); 0183 break; 0184 case AuthorRole: { 0185 KNSCore::Author author = entry.author(); 0186 QVariantMap returnAuthor; 0187 returnAuthor[QStringLiteral("id")] = author.id(); 0188 returnAuthor[QStringLiteral("name")] = author.name(); 0189 returnAuthor[QStringLiteral("email")] = author.email(); 0190 returnAuthor[QStringLiteral("homepage")] = author.homepage(); 0191 returnAuthor[QStringLiteral("jabber")] = author.jabber(); 0192 returnAuthor[QStringLiteral("avatarUrl")] = author.avatarUrl(); 0193 returnAuthor[QStringLiteral("description")] = author.description(); 0194 data.setValue<>(returnAuthor); 0195 } break; 0196 case LicenseRole: 0197 data.setValue<QString>(entry.license()); 0198 break; 0199 case ShortSummaryRole: 0200 data.setValue<QString>(entry.shortSummary()); 0201 break; 0202 case SummaryRole: 0203 data.setValue<QString>(entry.summary()); 0204 break; 0205 case ChangelogRole: 0206 data.setValue<QString>(entry.changelog()); 0207 break; 0208 case VersionRole: 0209 data.setValue<QString>(entry.version()); 0210 break; 0211 case ReleaseDateRole: 0212 data.setValue<QDate>(entry.releaseDate()); 0213 break; 0214 case UpdateVersionRole: 0215 data.setValue<QString>(entry.updateVersion()); 0216 break; 0217 case UpdateReleaseDateRole: 0218 data.setValue<QDate>(entry.updateReleaseDate()); 0219 break; 0220 case PayloadRole: 0221 data.setValue<QString>(entry.payload()); 0222 break; 0223 case Qt::DecorationRole: 0224 data.setValue<QString>(entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1)); 0225 break; 0226 case PreviewsSmallRole: { 0227 QStringList previews; 0228 previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1); 0229 previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall2); 0230 previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall3); 0231 while (!previews.isEmpty() && previews.last().isEmpty()) { 0232 previews.takeLast(); 0233 } 0234 data.setValue(previews); 0235 } break; 0236 case PreviewsRole: { 0237 QStringList previews; 0238 previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig1); 0239 previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig2); 0240 previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig3); 0241 while (!previews.isEmpty() && previews.last().isEmpty()) { 0242 previews.takeLast(); 0243 } 0244 data.setValue(previews); 0245 } break; 0246 case InstalledFilesRole: 0247 data.setValue(entry.installedFiles()); 0248 break; 0249 case UnInstalledFilesRole: 0250 data.setValue(entry.uninstalledFiles()); 0251 break; 0252 case RatingRole: 0253 data.setValue<int>(entry.rating()); 0254 break; 0255 case NumberOfCommentsRole: 0256 data.setValue<int>(entry.numberOfComments()); 0257 break; 0258 case DownloadCountRole: 0259 data.setValue<int>(entry.downloadCount()); 0260 break; 0261 case NumberFansRole: 0262 data.setValue<int>(entry.numberFans()); 0263 break; 0264 case NumberKnowledgebaseEntriesRole: 0265 data.setValue<int>(entry.numberKnowledgebaseEntries()); 0266 break; 0267 case KnowledgebaseLinkRole: 0268 data.setValue<QString>(entry.knowledgebaseLink()); 0269 break; 0270 case DownloadLinksRole: { 0271 // This would be good to cache... but it also needs marking as dirty, somehow... 0272 const QList<KNSCore::EntryInternal::DownloadLinkInformation> dllinks = entry.downloadLinkInformationList(); 0273 QObjectList list; 0274 for (const KNSCore::EntryInternal::DownloadLinkInformation &link : dllinks) { 0275 DownloadLinkInfo *info = new DownloadLinkInfo(); 0276 info->setData(link); 0277 list.append(info); 0278 } 0279 if (list.isEmpty() && !entry.payload().isEmpty()) { 0280 DownloadLinkInfo *info = new DownloadLinkInfo(); 0281 KNSCore::EntryInternal::DownloadLinkInformation data; 0282 data.descriptionLink = entry.payload(); 0283 info->setData(data); 0284 list.append(info); 0285 } 0286 data.setValue(list); 0287 } break; 0288 case DonationLinkRole: 0289 data.setValue<QString>(entry.donationLink()); 0290 break; 0291 case ProviderIdRole: 0292 data.setValue<QString>(entry.providerId()); 0293 break; 0294 case SourceRole: { 0295 KNSCore::EntryInternal::Source src = entry.source(); 0296 switch (src) { 0297 case KNSCore::EntryInternal::Cache: 0298 data.setValue<QString>(QStringLiteral("Cache")); 0299 break; 0300 case KNSCore::EntryInternal::Online: 0301 data.setValue<QString>(QStringLiteral("Online")); 0302 break; 0303 case KNSCore::EntryInternal::Registry: 0304 data.setValue<QString>(QStringLiteral("Registry")); 0305 break; 0306 default: 0307 data.setValue<QString>(QStringLiteral("Unknown source - shouldn't be possible")); 0308 break; 0309 } 0310 } break; 0311 case StatusRole: { 0312 KNS3::Entry::Status status = entry.status(); 0313 switch (status) { 0314 case KNS3::Entry::Downloadable: 0315 data.setValue<ItemsModel::ItemStatus>(ItemsModel::DownloadableStatus); 0316 break; 0317 case KNS3::Entry::Installed: 0318 data.setValue<ItemsModel::ItemStatus>(ItemsModel::InstalledStatus); 0319 break; 0320 case KNS3::Entry::Updateable: 0321 data.setValue<ItemsModel::ItemStatus>(ItemsModel::UpdateableStatus); 0322 break; 0323 case KNS3::Entry::Deleted: 0324 data.setValue<ItemsModel::ItemStatus>(ItemsModel::DeletedStatus); 0325 break; 0326 case KNS3::Entry::Installing: 0327 data.setValue<ItemsModel::ItemStatus>(ItemsModel::InstallingStatus); 0328 break; 0329 case KNS3::Entry::Updating: 0330 data.setValue<ItemsModel::ItemStatus>(ItemsModel::UpdatingStatus); 0331 break; 0332 case KNS3::Entry::Invalid: 0333 default: 0334 data.setValue<ItemsModel::ItemStatus>(ItemsModel::InvalidStatus); 0335 break; 0336 } 0337 } break; 0338 case CommentsModelRole: { 0339 KNSCore::CommentsModel *commentsModel{nullptr}; 0340 if (!d->commentsModels.contains(entry.uniqueId())) { 0341 commentsModel = d->coreEngine->commentsForEntry(entry); 0342 d->commentsModels[entry.uniqueId()] = commentsModel; 0343 } else { 0344 commentsModel = d->commentsModels[entry.uniqueId()]; 0345 } 0346 data.setValue<QObject *>(commentsModel); 0347 } break; 0348 case EntryTypeRole: { 0349 KNSCore::EntryInternal::EntryType type = entry.entryType(); 0350 if (type == KNSCore::EntryInternal::GroupEntry) { 0351 data.setValue<ItemsModel::EntryType>(ItemsModel::GroupEntry); 0352 } else { 0353 data.setValue<ItemsModel::EntryType>(ItemsModel::CatalogEntry); 0354 } 0355 } break; 0356 default: 0357 data.setValue<QString>(QStringLiteral("Unknown role")); 0358 break; 0359 } 0360 } 0361 return data; 0362 } 0363 0364 bool ItemsModel::canFetchMore(const QModelIndex &parent) const 0365 { 0366 return !parent.isValid() && d->coreEngine && d->coreEngine->categoriesMetadata().count() > 0; 0367 } 0368 0369 void ItemsModel::fetchMore(const QModelIndex &parent) 0370 { 0371 if (parent.isValid() || !d->coreEngine) { 0372 return; 0373 } 0374 d->coreEngine->requestMoreData(); 0375 } 0376 0377 QObject *ItemsModel::engine() const 0378 { 0379 return d->engine; 0380 } 0381 0382 void ItemsModel::setEngine(QObject *newEngine) 0383 { 0384 if (d->engine != newEngine) { 0385 beginResetModel(); 0386 d->engine = qobject_cast<Engine *>(newEngine); 0387 d->model->deleteLater(); 0388 d->model = nullptr; 0389 d->coreEngine = nullptr; 0390 if (d->engine) { 0391 d->coreEngine = qobject_cast<KNSCore::Engine *>(d->engine->engine()); 0392 } 0393 connect(d->engine, &Engine::engineChanged, this, [this]() { 0394 beginResetModel(); 0395 d->model->deleteLater(); 0396 d->model = nullptr; 0397 d->coreEngine = qobject_cast<KNSCore::Engine *>(d->engine->engine()); 0398 endResetModel(); 0399 }); 0400 Q_EMIT engineChanged(); 0401 endResetModel(); 0402 } 0403 } 0404 0405 int ItemsModel::indexOfEntryId(const QString &providerId, const QString &entryId) 0406 { 0407 int idx{-1}; 0408 if (d->coreEngine && d->model) { 0409 for (int i = 0; i < rowCount(); ++i) { 0410 KNSCore::EntryInternal testEntry = d->model->data(d->model->index(i), Qt::UserRole).value<KNSCore::EntryInternal>(); 0411 if (providerId == QUrl(testEntry.providerId()).host() && entryId == testEntry.uniqueId()) { 0412 idx = i; 0413 break; 0414 } 0415 } 0416 } 0417 return idx; 0418 } 0419 0420 bool ItemsModel::isLoadingData() const 0421 { 0422 return d->isLoadingData; 0423 } 0424 0425 void ItemsModel::installItem(int index, int linkId) 0426 { 0427 if (d->coreEngine) { 0428 KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value<KNSCore::EntryInternal>(); 0429 if (entry.isValid()) { 0430 d->coreEngine->install(entry, linkId); 0431 } 0432 } 0433 } 0434 0435 void ItemsModel::updateItem(int index) 0436 { 0437 installItem(index, AutoDetectLinkId); 0438 } 0439 0440 void ItemsModel::uninstallItem(int index) 0441 { 0442 if (d->coreEngine) { 0443 KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value<KNSCore::EntryInternal>(); 0444 if (entry.isValid()) { 0445 d->coreEngine->uninstall(entry); 0446 } 0447 } 0448 } 0449 0450 void ItemsModel::adoptItem(int index) 0451 { 0452 if (d->coreEngine) { 0453 KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value<KNSCore::EntryInternal>(); 0454 if (entry.isValid()) { 0455 d->coreEngine->adoptEntry(entry); 0456 } 0457 } 0458 } 0459 0460 #include "moc_quickitemsmodel.cpp"