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"