KNewStuff

quickitemsmodel.cpp
1 /*
2  SPDX-FileCopyrightText: 2016 Dan Leinir Turthra Jensen <[email protected]>
3 
4  SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
5 */
6 
7 #include "quickitemsmodel.h"
8 #include "quickengine.h"
9 #include "itemsmodel.h"
10 #include "engine.h"
11 #include "downloadlinkinfo.h"
12 #include "core/commentsmodel.h"
13 
14 #include <KShell>
15 #include <QProcess>
16 
17 class ItemsModel::Private {
18 public:
19  Private(ItemsModel *qq)
20  : q(qq)
21  , model(nullptr)
22  , engine(nullptr)
23  , coreEngine(nullptr)
24  {}
25  ~Private()
26  {
27  }
28  ItemsModel *q;
29  KNSCore::ItemsModel *model;
30  Engine *engine;
31  KNSCore::Engine *coreEngine;
32 
34 
35  bool isLoadingData{false};
36 
37  bool initModel()
38  {
39  if (model) {
40  return true;
41  }
42  if (!coreEngine) {
43  return false;
44  }
45  model = new KNSCore::ItemsModel(coreEngine, q);
46  q->connect(coreEngine, &KNSCore::Engine::busyStateChanged, q, [=](){
47  // If we install/update an entry the spinner should be hidden, BUG: 422047
48  const KNSCore::Engine::BusyState state = coreEngine->busyState();
49  const bool busy = state && !state.testFlag(KNSCore::Engine::BusyOperation::InstallingEntry);
50  if (isLoadingData != busy) {
51  isLoadingData = busy;
53  }
54  });
55 
56  q->connect(coreEngine, &KNSCore::Engine::signalProvidersLoaded, coreEngine, &KNSCore::Engine::reloadEntries);
57  // Entries have been fetched and should be shown:
58  q->connect(coreEngine, &KNSCore::Engine::signalEntriesLoaded, model, [this](const KNSCore::EntryInternal::List& entries){
59  if (coreEngine->filter() != KNSCore::Provider::Updates) {
60  model->slotEntriesLoaded(entries);
61  }
62  });
63  q->connect(coreEngine, &KNSCore::Engine::signalUpdateableEntriesLoaded, model, [this](const KNSCore::EntryInternal::List& entries){
64  if (coreEngine->filter() == KNSCore::Provider::Updates) {
65  model->slotEntriesLoaded(entries);
66  }
67  });
68 
69  q->connect(coreEngine, &KNSCore::Engine::signalEntryEvent, q, [this](const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event) {
70  onEntryEvent(entry, event);
71  });
72  q->connect(coreEngine, &KNSCore::Engine::signalResetView, model, &KNSCore::ItemsModel::clearEntries);
73  q->connect(coreEngine, &KNSCore::Engine::signalEntryPreviewLoaded, model, &KNSCore::ItemsModel::slotEntryPreviewLoaded);
74 
75  q->connect(model, &KNSCore::ItemsModel::rowsInserted, q, &ItemsModel::rowsInserted);
76  q->connect(model, &KNSCore::ItemsModel::rowsRemoved, q, &ItemsModel::rowsRemoved);
77  q->connect(model, &KNSCore::ItemsModel::dataChanged, q, &ItemsModel::dataChanged);
78  q->connect(model, &KNSCore::ItemsModel::modelReset, q, &ItemsModel::modelReset);
79  return true;
80  }
81 
82  void onEntryEvent(const KNSCore::EntryInternal &entry, KNSCore::EntryInternal::EntryEvent event)
83  {
84  if (event == KNSCore::EntryInternal::StatusChangedEvent) {
85  model->slotEntryChanged(entry);
86  Q_EMIT q->entryChanged(model->row(entry));
87 
88  // If we update/uninstall an entry we have to update the UI, see BUG: 425135
89  if (coreEngine->filter() == KNSCore::Provider::Updates
90  && entry.status() != KNS3::Entry::Updateable && entry.status() != KNS3::Entry::Updating) {
91  model->removeEntry(entry);
92  } else if (coreEngine->filter() == KNSCore::Provider::Installed && entry.status() == KNS3::Entry::Deleted) {
93  model->removeEntry(entry);
94  }
95  }
96  }
97 };
98 
99 ItemsModel::ItemsModel(QObject *parent)
100  : QAbstractListModel(parent)
101  , d(new Private(this))
102 {
103 }
104 
105 ItemsModel::~ItemsModel()
106 {
107  delete d;
108 }
109 
110 QHash<int, QByteArray> ItemsModel::roleNames() const
111 {
112  static const QHash<int, QByteArray> roles = QHash<int, QByteArray>{
113  {Qt::DisplayRole, "display"},
114  {NameRole, "name"},
115  {UniqueIdRole, "uniqueId"},
116  {CategoryRole, "category"},
117  {HomepageRole, "homepage"},
118  {AuthorRole, "author"},
119  {LicenseRole, "license"},
120  {ShortSummaryRole, "shortSummary"},
121  {SummaryRole, "summary"},
122  {ChangelogRole, "changelog"},
123  {VersionRole, "version"},
124  {ReleaseDateRole, "releaseDate"},
125  {UpdateVersionRole, "updateVersion"},
126  {UpdateReleaseDateRole, "updateReleaseDate"},
127  {PayloadRole, "payload"},
128  {Qt::DecorationRole, "decoration"},
129  {PreviewsSmallRole, "previewsSmall"},
130  {PreviewsRole, "previews"},
131  {InstalledFilesRole, "installedFiles"},
132  {UnInstalledFilesRole, "uninstalledFiles"},
133  {RatingRole, "rating"},
134  {NumberOfCommentsRole, "numberOfComments"},
135  {DownloadCountRole, "downloadCount"},
136  {NumberFansRole, "numberFans"},
137  {NumberKnowledgebaseEntriesRole, "numberKnowledgebaseEntries"},
138  {KnowledgebaseLinkRole, "knowledgebaseLink"},
139  {DownloadLinksRole, "downloadLinks"},
140  {DonationLinkRole, "donationLink"},
141  {ProviderIdRole, "providerId"},
142  {SourceRole, "source"},
143  {StatusRole, "status"}
144  };
145  return roles;
146 }
147 
148 int ItemsModel::rowCount(const QModelIndex& parent) const
149 {
150  if (parent.isValid())
151  return 0;
152  if (d->initModel())
153  return d->model->rowCount(QModelIndex());
154  return 0;
155 }
156 
157 QVariant ItemsModel::data(const QModelIndex &index, int role) const
158 {
159  QVariant data;
160  if (index.isValid() && d->initModel())
161  {
162  KNSCore::EntryInternal entry = d->model->data(d->model->index(index.row()), Qt::UserRole).value<KNSCore::EntryInternal>();
163  switch (role)
164  {
165  case NameRole:
166  case Qt::DisplayRole:
167  data.setValue<QString>(entry.name());
168  break;
169  case UniqueIdRole:
170  data.setValue<QString>(entry.uniqueId());
171  break;
172  case CategoryRole:
173  data.setValue<QString>(entry.category());
174  break;
175  case HomepageRole:
176  data.setValue<QUrl>(entry.homepage());
177  break;
178  case AuthorRole:
179  {
180  KNSCore::Author author = entry.author();
181  QVariantMap returnAuthor;
182  returnAuthor[QStringLiteral("id")] = author.id();
183  returnAuthor[QStringLiteral("name")] = author.name();
184  returnAuthor[QStringLiteral("email")] = author.email();
185  returnAuthor[QStringLiteral("homepage")] = author.homepage();
186  returnAuthor[QStringLiteral("jabber")] = author.jabber();
187  returnAuthor[QStringLiteral("avatarUrl")] = author.avatarUrl();
188  returnAuthor[QStringLiteral("description")] = author.description();
189  data.setValue<>(returnAuthor);
190  }
191  break;
192  case LicenseRole:
193  data.setValue<QString>(entry.license());
194  break;
195  case ShortSummaryRole:
196  data.setValue<QString>(entry.shortSummary());
197  break;
198  case SummaryRole:
199  data.setValue<QString>(entry.summary());
200  break;
201  case ChangelogRole:
202  data.setValue<QString>(entry.changelog());
203  break;
204  case VersionRole:
205  data.setValue<QString>(entry.version());
206  break;
207  case ReleaseDateRole:
208  data.setValue<QDate>(entry.releaseDate());
209  break;
210  case UpdateVersionRole:
211  data.setValue<QString>(entry.updateVersion());
212  break;
213  case UpdateReleaseDateRole:
214  data.setValue<QDate>(entry.updateReleaseDate());
215  break;
216  case PayloadRole:
217  data.setValue<QString>(entry.payload());
218  break;
219  case Qt::DecorationRole:
220  data.setValue<QString>(entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1));
221  break;
222  case PreviewsSmallRole:
223  {
224  QStringList previews;
225  previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1);
226  previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall2);
227  previews << entry.previewUrl(KNSCore::EntryInternal::PreviewSmall3);
228  while(!previews.isEmpty() && previews.last().isEmpty()) {
229  previews.takeLast();
230  }
231  data.setValue<QStringList>(previews);
232  }
233  break;
234  case PreviewsRole:
235  {
236  QStringList previews;
237  previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig1);
238  previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig2);
239  previews << entry.previewUrl(KNSCore::EntryInternal::PreviewBig3);
240  while(!previews.isEmpty() && previews.last().isEmpty()) {
241  previews.takeLast();
242  }
243  data.setValue<QStringList>(previews);
244  }
245  break;
246  case InstalledFilesRole:
247  data.setValue<QStringList>(entry.installedFiles());
248  break;
249  case UnInstalledFilesRole:
250  data.setValue<QStringList>(entry.uninstalledFiles());
251  break;
252  case RatingRole:
253  data.setValue<int>(entry.rating());
254  break;
255  case NumberOfCommentsRole:
256  data.setValue<int>(entry.numberOfComments());
257  break;
258  case DownloadCountRole:
259  data.setValue<int>(entry.downloadCount());
260  break;
261  case NumberFansRole:
262  data.setValue<int>(entry.numberFans());
263  break;
264  case NumberKnowledgebaseEntriesRole:
265  data.setValue<int>(entry.numberKnowledgebaseEntries());
266  break;
267  case KnowledgebaseLinkRole:
268  data.setValue<QString>(entry.knowledgebaseLink());
269  break;
270  case DownloadLinksRole:
271  {
272  // This would be good to cache... but it also needs marking as dirty, somehow...
275  for(const KNSCore::EntryInternal::DownloadLinkInformation &link : dllinks)
276  {
277  DownloadLinkInfo *info = new DownloadLinkInfo();
278  info->setData(link);
279  list.append(info);
280  }
281  if (list.isEmpty() && !entry.payload().isEmpty()) {
282  DownloadLinkInfo *info = new DownloadLinkInfo();
283  KNSCore::EntryInternal::DownloadLinkInformation data;
284  data.descriptionLink = entry.payload();
285  info->setData(data);
286  list.append(info);
287  }
288  data.setValue<QObjectList>(list);
289  }
290  break;
291  case DonationLinkRole:
292  data.setValue<QString>(entry.donationLink());
293  break;
294  case ProviderIdRole:
295  data.setValue<QString>(entry.providerId());
296  break;
297  case SourceRole:
298  {
299  KNSCore::EntryInternal::Source src = entry.source();
300  switch(src)
301  {
302  case KNSCore::EntryInternal::Cache:
303  data.setValue<QString>(QStringLiteral("Cache"));
304  break;
305  case KNSCore::EntryInternal::Online:
306  data.setValue<QString>(QStringLiteral("Online"));
307  break;
308  case KNSCore::EntryInternal::Registry:
309  data.setValue<QString>(QStringLiteral("Registry"));
310  break;
311  default:
312  data.setValue<QString>(QStringLiteral("Unknown source - shouldn't be possible"));
313  break;
314  }
315  }
316  break;
317  case StatusRole:
318  {
320  switch(status)
321  {
322  case KNS3::Entry::Downloadable:
323  data.setValue<ItemsModel::ItemStatus>(ItemsModel::DownloadableStatus);
324  break;
325  case KNS3::Entry::Installed:
326  data.setValue<ItemsModel::ItemStatus>(ItemsModel::InstalledStatus);
327  break;
328  case KNS3::Entry::Updateable:
329  data.setValue<ItemsModel::ItemStatus>(ItemsModel::UpdateableStatus);
330  break;
331  case KNS3::Entry::Deleted:
332  data.setValue<ItemsModel::ItemStatus>(ItemsModel::DeletedStatus);
333  break;
334  case KNS3::Entry::Installing:
335  data.setValue<ItemsModel::ItemStatus>(ItemsModel::InstallingStatus);
336  break;
337  case KNS3::Entry::Updating:
338  data.setValue<ItemsModel::ItemStatus>(ItemsModel::UpdatingStatus);
339  break;
340  case KNS3::Entry::Invalid:
341  default:
342  data.setValue<ItemsModel::ItemStatus>(ItemsModel::InvalidStatus);
343  break;
344  }
345  }
346  break;
347  case CommentsModelRole:
348  {
349  KNSCore::CommentsModel *commentsModel{nullptr};
350  if (!d->commentsModels.contains(entry.uniqueId())) {
351  commentsModel = d->coreEngine->commentsForEntry(entry);
352  d->commentsModels[entry.uniqueId()] = commentsModel;
353  } else {
354  commentsModel = d->commentsModels[entry.uniqueId()];
355  }
356  data.setValue<QObject*>(commentsModel);
357  }
358  break;
359  default:
360  data.setValue<QString>(QStringLiteral("Unknown role"));
361  break;
362  }
363  }
364  return data;
365 }
366 
367 bool ItemsModel::canFetchMore(const QModelIndex &parent) const
368 {
369  return !parent.isValid() && d->coreEngine && d->coreEngine->categoriesMetadata().count() > 0;
370 }
371 
372 void ItemsModel::fetchMore(const QModelIndex &parent)
373 {
374  if (parent.isValid() || !d->coreEngine) {
375  return;
376  }
377  d->coreEngine->requestMoreData();
378 }
379 
381 {
382  return d->engine;
383 }
384 
385 void ItemsModel::setEngine(QObject *newEngine)
386 {
387  if (d->engine != newEngine) {
388  beginResetModel();
389  d->engine = qobject_cast<Engine*>(newEngine);
390  d->model->deleteLater();
391  d->model = nullptr;
392  d->coreEngine = nullptr;
393  if (d->engine) {
394  d->coreEngine = qobject_cast<KNSCore::Engine*>(d->engine->engine());
395  }
396  connect(d->engine, &Engine::engineChanged, this, [this](){
397  beginResetModel();
398  d->model->deleteLater();
399  d->model = nullptr;
400  d->coreEngine = qobject_cast<KNSCore::Engine*>(d->engine->engine());
401  endResetModel();
402  });
403  Q_EMIT engineChanged();
404  endResetModel();
405  }
406 }
407 
408 bool ItemsModel::isLoadingData() const
409 {
410  return d->isLoadingData;
411 }
412 
413 void ItemsModel::installItem(int index, int linkId)
414 {
415  if (d->coreEngine) {
416  KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value<KNSCore::EntryInternal>();
417  if(entry.isValid()) {
418  d->coreEngine->install(entry, linkId);
419  }
420  }
421 }
422 
423 void ItemsModel::updateItem(int index)
424 {
425  installItem(index, AutoDetectLinkId);
426 }
427 
429 {
430  if (d->coreEngine) {
431  KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value<KNSCore::EntryInternal>();
432  if(entry.isValid()) {
433  d->coreEngine->uninstall(entry);
434  }
435  }
436 }
437 
438 void ItemsModel::adoptItem(int index)
439 {
440  if (d->coreEngine) {
441  KNSCore::EntryInternal entry = d->model->data(d->model->index(index), Qt::UserRole).value<KNSCore::EntryInternal>();
442  if (entry.isValid()) {
443  d->coreEngine->adoptEntry(entry);
444  }
445  }
446 }
QString donationLink() const
A string representing the URL for a website where the user can donate to the author of this entry...
One downloadable item as contained within one content item.
int numberFans() const
How many people have marked themselves as fans of this entry.
QString summary() const
Retrieve a (potentially very long) description of the object.
QString uniqueId() const
Get the object&#39;s unique ID.
Author author() const
Retrieve the author of the object.
int downloadCount() const
Retrieve the download count for the object, which has been determined by its hosting sites and thus m...
QString shortSummary() const
Retrieve a short description of what the object is all about (should be very short) ...
bool isLoadingData() const
Whether or not the model is fetching information from a remote location.
Q_INVOKABLE void installItem(int index, int linkId)
This will install (or update, if already installed) the item at the given index.
QDate releaseDate() const
Retrieve the date of the object&#39;s publication.
Source
Source of the entry, A entry&#39;s data is coming from either cache, or an online provider this helps the...
Definition: entryinternal.h:59
Encapsulates a KNSCore::Engine for use in Qt Quick.
Definition: quickengine.h:23
QUrl avatarUrl() const
Retrieve the url of the user&#39;s avatar image.
KNewStuff author information.
Definition: core/author.h:30
virtual bool event(QEvent *e)
Q_SIGNAL void busyStateChanged()
Signal gets emitted when the busy state changes.
bool isValid() const const
QString id() const
Retrieve the author&#39;s user ID.
Definition: core/author.cpp:72
QString payload() const
Retrieve the file name of the object.
QString version() const
Retrieve the version string of the object.
Q_SIGNAL void isLoadingDataChanged()
Fired when the isLoadingData value changes.
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector< int > &roles)
QUrl homepage() const
A link to a website containing information about this entry.
QList< DownloadLinkInformation > downloadLinkInformationList() const
A list of downloadable data for this entry.
bool isEmpty() const const
QStringList installedFiles() const
Retrieve the locally installed files.
DisplayRole
bool isEmpty() const const
int row() const const
QString description() const
Retrieve the user&#39;s description text.
A model which takes care of the comments for a single EntryInternal.
QString category() const
Retrieve the category of the data object.
QString homepage() const
Retrieve the author&#39;s homepage.
QDate updateReleaseDate() const
Retrieve the date of the newer version that is available as update.
void rowsRemoved(const QModelIndex &parent, int first, int last)
Q_INVOKABLE void adoptItem(int index)
Run the adoption command on an already installed item.
QString previewUrl(PreviewType type=PreviewSmall1) const
Retrieve the file name of an image containing a preview of the object.
Provider::Filter filter() const
The result filter set on the current request.
Definition: engine.cpp:526
A model which shows the contents found in an Engine.
Status
Status of the entry.
Definition: entry.h:46
void setValue(const T &value)
Q_SIGNAL void entryChanged(int index)
Fired when an entry&#39;s data changes.
T takeLast()
int numberKnowledgebaseEntries() const
The number of entries in the knowledgebase for this entry.
QObject engine
The NewStuffQuickEngine to show items from.
KNewStuff data entry container.
Definition: entryinternal.h:49
int rating() const
Retrieve the rating for the object, which has been determined by its users and thus might change over...
T & last()
int numberOfComments() const
KNewStuff engine.
Definition: engine.h:59
QStringList uninstalledFiles() const
Retrieve the locally uninstalled files.
QString updateVersion() const
Retrieve the version string of the object that is available as update.
Q_INVOKABLE void uninstallItem(int index)
Uninstall an already installed item.
QString knowledgebaseLink() const
The link for the knowledgebase for this entry.
BusyState busyState
Current state of the engine, the state con contain multiple operations an empty BusyState represents ...
Definition: engine.h:68
KNS3::Entry::Status status() const
Retrieves the entry&#39;s status.
void rowsInserted(const QModelIndex &parent, int first, int last)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QString name() const
Retrieve the name of the data object.
QString jabber() const
Retrieve the author&#39;s jabber address.
T qobject_cast(QObject *object)
QObject * parent() const const
QString license() const
Retrieve the license name of the object.
QString email() const
Retrieve the author&#39;s email address.
Definition: core/author.cpp:92
QString providerId() const
The id of the provider this entry belongs to.
bool testFlag(Enum flag) const const
Q_INVOKABLE void updateItem(int index)
This will request an update of the given item.
Q_EMITQ_EMIT
typedef QObjectList
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
QString name() const
Retrieve the author&#39;s name.
Definition: core/author.cpp:82
This file is part of the KDE documentation.
Documentation copyright © 1996-2021 The KDE developers.
Generated on Mon Jan 18 2021 22:43:50 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.