KNewStuff

itemsgridviewdelegate.cpp
1 /*
2  SPDX-FileCopyrightText: 2008 Jeremy Whiting <[email protected]>
3  SPDX-FileCopyrightText: 2010 Reza Fatahilah Shah <[email protected]>
4  SPDX-FileCopyrightText: 2010 Frederik Gladhorn <[email protected]>
5 
6  SPDX-License-Identifier: LGPL-2.1-or-later
7 */
8 
9 #include "itemsgridviewdelegate_p.h"
10 
11 #include <QPainter>
12 #include <QApplication>
13 #include <QLabel>
14 #include <QToolButton>
15 #include <QMenu>
16 #include <QHBoxLayout>
17 #include <QAbstractItemView>
18 
19 #include <KFormat>
20 #include <knewstuff_debug.h>
21 #include <KLocalizedString>
22 #include <KRatingWidget>
23 #include <KSqueezedTextLabel>
24 
25 #include "core/itemsmodel.h"
26 
27 namespace KNS3
28 {
29 enum { DelegateTitleLabel, DelegateAuthorLabel, DelegateDownloadCounterLabel,
30  DelegateGridRatingWidget
31  };
32 
33 ItemsGridViewDelegate::ItemsGridViewDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent)
34  : ItemsViewBaseDelegate(itemView, engine, parent)
35  , m_elementYPos(0)
36 {
37  createOperationBar();
38 }
39 
40 ItemsGridViewDelegate::~ItemsGridViewDelegate()
41 {
42 }
43 
44 QList<QWidget *> ItemsGridViewDelegate::createItemWidgets(const QModelIndex &index) const
45 {
46  Q_UNUSED(index)
47 
48  QList<QWidget *> m_widgetList;
49  KSqueezedTextLabel *titleLabel = new KSqueezedTextLabel();
50  titleLabel->setOpenExternalLinks(true);
51  titleLabel->setTextElideMode(Qt::ElideRight);
52  // not so nice - work around constness to install the event filter
53  ItemsGridViewDelegate *delegate = const_cast<ItemsGridViewDelegate *>(this);
54  titleLabel->installEventFilter(delegate);
55  m_widgetList << titleLabel;
56 
57  KSqueezedTextLabel *authorLabel = new KSqueezedTextLabel();
58  authorLabel->setTextElideMode(Qt::ElideRight);
59  m_widgetList << authorLabel;
60 
61  KSqueezedTextLabel *downloadCounterLabel = new KSqueezedTextLabel();
62  downloadCounterLabel->setTextElideMode(Qt::ElideRight);
63  m_widgetList << downloadCounterLabel;
64 
65  KRatingWidget *rating = new KRatingWidget();
66  rating->setMaxRating(10);
67  rating->setHalfStepsEnabled(true);
68  m_widgetList << rating;
69 
70  return m_widgetList;
71 }
72 
73 void ItemsGridViewDelegate::updateItemWidgets(const QList<QWidget *> widgets,
74  const QStyleOptionViewItem &option,
75  const QPersistentModelIndex &index) const
76 {
77  const KNSCore::ItemsModel *model = qobject_cast<const KNSCore::ItemsModel *>(index.model());
78  if (!model) {
79  qCDebug(KNEWSTUFF) << "WARNING - INVALID MODEL!";
80  return;
81  }
82 
83  KNSCore::EntryInternal entry = index.data(Qt::UserRole).value<KNSCore::EntryInternal>();
84  int elementYPos = KNSCore::PreviewHeight + ItemMargin + FrameThickness * 2;
85 
86  //setup rating widget
87  KRatingWidget *rating = qobject_cast<KRatingWidget *>(widgets.at(DelegateGridRatingWidget));
88  if (rating) {
89  if (entry.rating() > 0) {
90  rating->setToolTip(i18n("Rating: %1%", entry.rating()));
91  // assume all entries come with rating 0..100 but most are in the range 20 - 80, so 20 is 0 stars, 80 is 5 stars
92  rating->setRating((entry.rating() - 20) * 10 / 60);
93  //make the rating widget smaller than the one at list view
94  int newWidth = 68;
95  QSize size(newWidth, 15);
96  rating->resize(size);
97  //put rating widget under image rectangle
98  rating->move((ItemGridWidth - newWidth) / 2, elementYPos);
99  elementYPos += rating->height();
100  } else {
101  //is it better to stay visible?
102  rating->setVisible(false);
103  }
104  }
105  elementYPos += ItemMargin;
106 
107  //setup title label
108  QLabel *titleLabel = qobject_cast<QLabel *>(widgets.at(DelegateTitleLabel));
109  if (titleLabel != nullptr) {
110  titleLabel->setWordWrap(true);
111  titleLabel->setAlignment(Qt::AlignHCenter);
112  //titleLabel->setFrameStyle(QFrame::Panel);
113  titleLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height() * 2));
114  titleLabel->move((ItemGridWidth - titleLabel->width()) / 2, elementYPos);
115 
116  QString title;
117  QUrl link = qvariant_cast<QUrl>(entry.homepage());
118  if (!link.isEmpty()) {
119  title += QLatin1String("<b><a href=\"") + link.url() + QLatin1String("\">") + entry.name() + QLatin1String("</a></b>\n");
120  } else {
121  title += QLatin1String("<b>") + entry.name() + QLatin1String("</b>");
122  }
123 
124  const auto downloadInfo = entry.downloadLinkInformationList();
125  if (!downloadInfo.isEmpty() && downloadInfo.at(0).size > 0) {
126  QString sizeString = KFormat().formatByteSize(downloadInfo.at(0).size * 1000);
127  title += i18nc("Show the size of the file in a list", "<br />Size: %1", sizeString);
128  }
129 
130  titleLabel->setText(title);
131  elementYPos += titleLabel->height();
132  }
133  //setup author label
134  QLabel *authorLabel = qobject_cast<QLabel *>(widgets.at(DelegateAuthorLabel));
135  if (authorLabel) {
136  authorLabel->setWordWrap(true);
137  authorLabel->setAlignment(Qt::AlignHCenter);
138  authorLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height()));
139  authorLabel->move((ItemGridWidth - authorLabel->width()) / 2, elementYPos);
140 
141  QString text;
142  QString authorName = entry.author().name();
143  QString email = entry.author().email();
144  QString authorPage = entry.author().homepage();
145 
146  if (!authorName.isEmpty()) {
147  if (!authorPage.isEmpty()) {
148  text += QLatin1String("<p>") + i18nc("Show the author of this item in a list", "By <i>%1</i>", QLatin1String(" <a href=\"") + authorPage + QLatin1String("\">") + authorName + QLatin1String("</a>")) + QLatin1String("</p>\n");
149  } else if (!email.isEmpty()) {
150  text += QLatin1String("<p>") + i18nc("Show the author of this item in a list", "By <i>%1</i>", authorName) + QLatin1String(" <a href=\"mailto:") + email + QLatin1String("\">") + email + QLatin1String("</a></p>\n");
151  } else {
152  text += QLatin1String("<p>") + i18nc("Show the author of this item in a list", "By <i>%1</i>", authorName) + QLatin1String("</p>\n");
153  }
154  }
155  authorLabel->setText(text);
156  elementYPos += authorLabel->height();
157  }
158  elementYPos += ItemMargin;
159 
160  //setup download label
161  QLabel *downloadLabel = qobject_cast<QLabel *>(widgets.at(DelegateDownloadCounterLabel));
162  if (downloadLabel != nullptr) {
163  downloadLabel->setWordWrap(true);
164  downloadLabel->setAlignment(Qt::AlignHCenter);
165  downloadLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height()));
166  downloadLabel->move((ItemGridWidth - downloadLabel->width()) / 2, elementYPos);
167 
168  unsigned int fans = entry.numberFans();
169  unsigned int downloads = entry.downloadCount();
170 
171  QString text;
172  QString fanString;
173  QString downloadString;
174  if (fans > 0) {
175  fanString = i18ncp("fan as in supporter", "1 fan", "%1 fans", fans);
176  }
177  if (downloads > 0) {
178  downloadString = i18np("1 download", "%1 downloads", downloads);
179  }
180  if (downloads > 0 || fans > 0) {
181  text += QLatin1String("<p>") + downloadString;
182  if (downloads > 0 && fans > 0) {
183  text += QLatin1String(", ");
184  }
185  text += fanString + QLatin1String("</p>");
186  }
187  downloadLabel->setText(text);
188  elementYPos += downloadLabel->height();
189  }
190  elementYPos += ItemMargin;
191  m_elementYPos = elementYPos;
192 }
193 
194 void ItemsGridViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
195 {
196  if (option.state & QStyle::State_MouseOver) {
197  QModelIndex focIndex = focusedIndex();
198  if (m_oldIndex != focIndex || !m_operationBar->isVisible()) {
199  ItemsGridViewDelegate *delegate = const_cast<ItemsGridViewDelegate *>(this);
200 
201  delegate->displayOperationBar(option.rect, index);
202  delegate->m_oldIndex = focIndex;
203  }
204  } else {
205  QModelIndex focindex = focusedIndex();
206  if (!focindex.isValid()) {
207  qCDebug(KNEWSTUFF) << "INVALID hide selection";
208  m_operationBar->hide();
209  }
210  }
211 
212  QStyle *style = QApplication::style();
213  style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr);
214 
215  painter->save();
216 
217  if (option.state & QStyle::State_Selected) {
218  painter->setPen(QPen(option.palette.highlightedText().color()));
219  } else {
220  painter->setPen(QPen(option.palette.text().color()));
221  }
222 
223  const KNSCore::ItemsModel *realmodel = qobject_cast<const KNSCore::ItemsModel *>(index.model());
224 
225  if (realmodel->hasPreviewImages()) {
226  int width = option.rect.width();
227 
229  if (entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1).isEmpty()) {
230  ;
231  } else {
232  QPoint centralPoint(option.rect.left() + width / 2, option.rect.top() + ItemMargin + FrameThickness + KNSCore::PreviewHeight / 2);
233  QImage image = entry.previewImage(KNSCore::EntryInternal::PreviewSmall1);
234  if (!image.isNull()) {
235  QPoint previewPoint(centralPoint.x() - image.width() / 2, centralPoint.y() - image.height() / 2);
236  painter->drawImage(previewPoint, image);
237 
238  QPixmap frameImageScaled = m_frameImage.scaled(image.width() + FrameThickness * 2, image.height() + FrameThickness * 2);
239  QPoint framePoint(centralPoint.x() - frameImageScaled.width() / 2, centralPoint.y() - frameImageScaled.height() / 2);
240  painter->drawPixmap(framePoint, frameImageScaled);
241  } else {
242  QPoint thumbnailPoint(option.rect.left() + ((width - KNSCore::PreviewWidth - FrameThickness * 2) / 2), option.rect.top() + ItemMargin);
243  QRect rect(thumbnailPoint, QSize(KNSCore::PreviewWidth + FrameThickness * 2, KNSCore::PreviewHeight + FrameThickness * 2));
244  painter->drawText(rect, Qt::AlignCenter | Qt::TextWordWrap, i18n("Loading Preview"));
245  }
246  }
247  }
248 
249  painter->restore();
250 }
251 
252 QSize ItemsGridViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
253 {
254  Q_UNUSED(option);
255  Q_UNUSED(index);
256 
257  QSize size;
258 
259  size.setWidth(ItemGridWidth);
260  size.setHeight(qMax(option.fontMetrics.height() * 13, ItemGridHeight)); // up to 6 lines of text, and two margins
261  return size;
262 }
263 
264 void ItemsGridViewDelegate::createOperationBar()
265 {
266  m_operationBar = new QWidget(this->itemView()->viewport());
267 
268  m_detailsButton = new QToolButton();
269  m_detailsButton->setToolButtonStyle(Qt::ToolButtonFollowStyle);
270  m_detailsButton->setPopupMode(QToolButton::InstantPopup);
271  m_detailsButton->setToolTip(i18n("Details"));
272  m_detailsButton->setIcon(QIcon::fromTheme(QStringLiteral("documentinfo")));
273  setBlockedEventTypes(m_detailsButton, QList<QEvent::Type>() << QEvent::MouseButtonPress
275  connect(m_detailsButton, &QToolButton::clicked,
276  this, static_cast<void(ItemsGridViewDelegate::*)()>(&ItemsGridViewDelegate::slotDetailsClicked));
277 
278  m_installButton = new QToolButton();
279  m_installButton->setToolButtonStyle(Qt::ToolButtonFollowStyle);
280  m_installButton->setPopupMode(QToolButton::InstantPopup);
281 
282  setBlockedEventTypes(m_installButton, QList<QEvent::Type>() << QEvent::MouseButtonPress
284  connect(m_installButton, &QAbstractButton::clicked, this, &ItemsGridViewDelegate::slotInstallClicked);
285  connect(m_installButton, &QToolButton::triggered, this, &ItemsGridViewDelegate::slotInstallActionTriggered);
286 
287  if (m_installButton->menu()) {
288  QMenu *buttonMenu = m_installButton->menu();
289  buttonMenu->clear();
290  m_installButton->setMenu(nullptr);
291  buttonMenu->deleteLater();
292  }
293 
294  QHBoxLayout *layout = new QHBoxLayout(m_operationBar);
295 
296  layout->setSpacing(1);
297  layout->addWidget(m_installButton);
298  layout->addWidget(m_detailsButton);
299 
300  m_operationBar->adjustSize();
301  m_operationBar->hide();
302 }
303 
304 void ItemsGridViewDelegate::displayOperationBar(const QRect &rect, const QModelIndex &index)
305 {
307  if (m_installButton != nullptr) {
308  if (m_installButton->menu() != nullptr) {
309  QMenu *buttonMenu = m_installButton->menu();
310  buttonMenu->clear();
311  m_installButton->setMenu(nullptr);
312  buttonMenu->deleteLater();
313  }
314 
315  bool installable = false;
316  bool enabled = true;
317  QString text;
318  QIcon icon;
319 
320  switch (entry.status()) {
321  case Entry::Installed:
322  text = i18n("Uninstall");
323  icon = m_iconDelete;
324  break;
325  case Entry::Updateable:
326  text = i18n("Update");
327  icon = m_iconUpdate;
328  installable = true;
329  break;
330  case Entry::Installing:
331  text = i18n("Installing");
332  enabled = false;
333  icon = m_iconUpdate;
334  break;
335  case Entry::Updating:
336  text = i18n("Updating");
337  enabled = false;
338  icon = m_iconUpdate;
339  break;
340  case Entry::Downloadable:
341  text = i18n("Install");
342  icon = m_iconInstall;
343  installable = true;
344  break;
345  case Entry::Deleted:
346  text = i18n("Install Again");
347  icon = m_iconInstall;
348  installable = true;
349  break;
350  default:
351  text = i18n("Install");
352  }
353  m_installButton->setToolTip(text);
354  m_installButton->setIcon(icon);
355  m_installButton->setEnabled(enabled);
356  if (installable && entry.downloadLinkCount() > 1) {
357  QMenu *installMenu = new QMenu(m_installButton);
358  const auto lst = entry.downloadLinkInformationList();
359  for (const KNSCore::EntryInternal::DownloadLinkInformation &info : lst) {
360  QString text = info.name;
361  if (!info.distributionType.trimmed().isEmpty()) {
362  text + " (" + info.distributionType.trimmed() + QLatin1Char(')');
363  }
364  QAction *installAction = installMenu->addAction(m_iconInstall, text);
365  installAction->setData(QPoint(index.row(), info.id));
366  }
367  m_installButton->setMenu(installMenu);
368  }
369 
370  m_operationBar->move(rect.left() + (ItemGridWidth - m_operationBar->width()) / 2, rect.top() + m_elementYPos);
371  m_operationBar->show();
372  }
373 }
374 }
375 
QString url(QUrl::FormattingOptions options) const const
void resize(int w, int h)
MouseButtonPress
void setHeight(int height)
void setRating(int rating)
int width() const const
int numberFans() const
How many people have marked themselves as fans of this entry.
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...
virtual void setVisible(bool visible)
void save()
T value() const const
void setAlignment(Qt::Alignment)
QString i18np(const char *singular, const char *plural, const TYPE &arg...)
bool isEmpty() const const
QImage previewImage(PreviewType type=PreviewSmall1) const
This will not be loaded automatically, instead use Engine to load the actual images.
void triggered(QAction *action)
QAction * addAction(const QString &text)
AlignHCenter
void clear()
bool isValid() const const
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void installEventFilter(QObject *filterObj)
QUrl homepage() const
A link to a website containing information about this entry.
int top() const const
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setPen(const QColor &color)
QList< DownloadLinkInformation > downloadLinkInformationList() const
A list of downloadable data for this entry.
int left() const const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
void setWidth(int width)
int downloadLinkCount() const
The number of available download options for this entry.
UserRole
bool isEmpty() const const
QString trimmed() const const
void clicked(bool checked)
int row() const const
void drawText(const QPointF &position, const QString &text)
ToolButtonFollowStyle
void deleteLater()
void setText(const QString &)
QPixmap scaled(int width, int height, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QString homepage() const
Retrieve the author&#39;s homepage.
PE_PanelItemViewItem
QString previewUrl(PreviewType type=PreviewSmall1) const
Retrieve the file name of an image containing a preview of the object.
void setData(const QVariant &userData)
QString formatByteSize(double size, int precision=1, KFormat::BinaryUnitDialect dialect=KFormat::DefaultBinaryDialect, KFormat::BinarySizeUnits units=KFormat::DefaultBinaryUnits) const
int height() const const
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
void move(int x, int y)
void restore()
QString i18n(const char *text, const TYPE &arg...)
void drawImage(const QRectF &target, const QImage &image, const QRectF &source, Qt::ImageConversionFlags flags)
const QAbstractItemModel * model() const const
QVariant data(int role) const const
QStyle * style()
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...
KIOCORE_EXPORT CopyJob * link(const QUrl &src, const QUrl &destDir, JobFlags flags=DefaultFlags)
KNewStuff engine.
Definition: engine.h:59
TextWordWrap
virtual void drawPrimitive(QStyle::PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const =0
QIcon fromTheme(const QString &name)
void setToolTip(const QString &)
KNS3::Entry::Status status() const
Retrieves the entry&#39;s status.
Definition: button.cpp:18
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.
T qobject_cast(QObject *object)
QString email() const
Retrieve the author&#39;s email address.
Definition: core/author.cpp:92
void setWordWrap(bool on)
void setSpacing(int spacing)
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-2020 The KDE developers.
Generated on Tue Aug 11 2020 22:43:23 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.