File indexing completed on 2024-05-19 11:43:33
0001 /* 0002 SPDX-FileCopyrightText: 2008 Jeremy Whiting <jpwhiting@kde.org> 0003 SPDX-FileCopyrightText: 2010 Reza Fatahilah Shah <rshah0385@kireihana.com> 0004 SPDX-FileCopyrightText: 2010 Frederik Gladhorn <gladhorn@kde.org> 0005 0006 SPDX-License-Identifier: LGPL-2.1-or-later 0007 */ 0008 0009 #include "itemsgridviewdelegate_p.h" 0010 0011 #include <QAbstractItemView> 0012 #include <QApplication> 0013 #include <QHBoxLayout> 0014 #include <QLabel> 0015 #include <QMenu> 0016 #include <QPainter> 0017 #include <QToolButton> 0018 0019 #include <KFormat> 0020 #include <KLocalizedString> 0021 #include <KRatingWidget> 0022 #include <KSqueezedTextLabel> 0023 #include <knewstuff_debug.h> 0024 0025 #include "core/itemsmodel.h" 0026 0027 namespace KNS3 0028 { 0029 enum { DelegateTitleLabel, DelegateAuthorLabel, DelegateDownloadCounterLabel, DelegateGridRatingWidget }; 0030 0031 ItemsGridViewDelegate::ItemsGridViewDelegate(QAbstractItemView *itemView, KNSCore::Engine *engine, QObject *parent) 0032 : ItemsViewBaseDelegate(itemView, engine, parent) 0033 , m_elementYPos(0) 0034 { 0035 createOperationBar(); 0036 } 0037 0038 ItemsGridViewDelegate::~ItemsGridViewDelegate() 0039 { 0040 } 0041 0042 QList<QWidget *> ItemsGridViewDelegate::createItemWidgets(const QModelIndex &index) const 0043 { 0044 Q_UNUSED(index) 0045 0046 QList<QWidget *> m_widgetList; 0047 KSqueezedTextLabel *titleLabel = new KSqueezedTextLabel(); 0048 titleLabel->setOpenExternalLinks(true); 0049 titleLabel->setTextElideMode(Qt::ElideRight); 0050 // not so nice - work around constness to install the event filter 0051 ItemsGridViewDelegate *delegate = const_cast<ItemsGridViewDelegate *>(this); 0052 titleLabel->installEventFilter(delegate); 0053 m_widgetList << titleLabel; 0054 0055 KSqueezedTextLabel *authorLabel = new KSqueezedTextLabel(); 0056 authorLabel->setTextElideMode(Qt::ElideRight); 0057 m_widgetList << authorLabel; 0058 0059 KSqueezedTextLabel *downloadCounterLabel = new KSqueezedTextLabel(); 0060 downloadCounterLabel->setTextElideMode(Qt::ElideRight); 0061 m_widgetList << downloadCounterLabel; 0062 0063 KRatingWidget *rating = new KRatingWidget(); 0064 rating->setMaxRating(10); 0065 rating->setHalfStepsEnabled(true); 0066 m_widgetList << rating; 0067 0068 return m_widgetList; 0069 } 0070 0071 void ItemsGridViewDelegate::updateItemWidgets(const QList<QWidget *> widgets, const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const 0072 { 0073 const KNSCore::ItemsModel *model = qobject_cast<const KNSCore::ItemsModel *>(index.model()); 0074 if (!model) { 0075 qCDebug(KNEWSTUFF) << "WARNING - INVALID MODEL!"; 0076 return; 0077 } 0078 0079 KNSCore::EntryInternal entry = index.data(Qt::UserRole).value<KNSCore::EntryInternal>(); 0080 int elementYPos = KNSCore::PreviewHeight + ItemMargin + FrameThickness * 2; 0081 0082 // setup rating widget 0083 KRatingWidget *rating = qobject_cast<KRatingWidget *>(widgets.at(DelegateGridRatingWidget)); 0084 if (rating) { 0085 if (entry.rating() > 0) { 0086 rating->setToolTip(i18n("Rating: %1%", entry.rating())); 0087 // 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 0088 rating->setRating((entry.rating() - 20) * 10 / 60); 0089 // make the rating widget smaller than the one at list view 0090 int newWidth = 68; 0091 QSize size(newWidth, 15); 0092 rating->resize(size); 0093 // put rating widget under image rectangle 0094 rating->move((ItemGridWidth - newWidth) / 2, elementYPos); 0095 elementYPos += rating->height(); 0096 } else { 0097 // is it better to stay visible? 0098 rating->setVisible(false); 0099 } 0100 } 0101 elementYPos += ItemMargin; 0102 0103 // setup title label 0104 QLabel *titleLabel = qobject_cast<QLabel *>(widgets.at(DelegateTitleLabel)); 0105 if (titleLabel != nullptr) { 0106 titleLabel->setWordWrap(true); 0107 titleLabel->setAlignment(Qt::AlignHCenter); 0108 // titleLabel->setFrameStyle(QFrame::Panel); 0109 titleLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height() * 2)); 0110 titleLabel->move((ItemGridWidth - titleLabel->width()) / 2, elementYPos); 0111 0112 QString title; 0113 QUrl link = qvariant_cast<QUrl>(entry.homepage()); 0114 if (!link.isEmpty()) { 0115 title += QLatin1String("<b><a href=\"") + link.url() + QLatin1String("\">") + entry.name() + QLatin1String("</a></b>\n"); 0116 } else { 0117 title += QLatin1String("<b>") + entry.name() + QLatin1String("</b>"); 0118 } 0119 0120 const auto downloadInfo = entry.downloadLinkInformationList(); 0121 if (!downloadInfo.isEmpty() && downloadInfo.at(0).size > 0) { 0122 QString sizeString = KFormat().formatByteSize(downloadInfo.at(0).size * 1000); 0123 title += i18nc("Show the size of the file in a list", "<br />Size: %1", sizeString); 0124 } 0125 0126 titleLabel->setText(title); 0127 elementYPos += titleLabel->height(); 0128 } 0129 // setup author label 0130 QLabel *authorLabel = qobject_cast<QLabel *>(widgets.at(DelegateAuthorLabel)); 0131 if (authorLabel) { 0132 authorLabel->setWordWrap(true); 0133 authorLabel->setAlignment(Qt::AlignHCenter); 0134 authorLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height())); 0135 authorLabel->move((ItemGridWidth - authorLabel->width()) / 2, elementYPos); 0136 0137 QString text; 0138 QString authorName = entry.author().name(); 0139 QString email = entry.author().email(); 0140 QString authorPage = entry.author().homepage(); 0141 0142 if (!authorName.isEmpty()) { 0143 if (!authorPage.isEmpty()) { 0144 text += QLatin1String("<p>") 0145 + i18nc("Show the author of this item in a list", 0146 "By <i>%1</i>", 0147 QLatin1String(" <a href=\"") + authorPage + QLatin1String("\">") + authorName + QLatin1String("</a>")) 0148 + QLatin1String("</p>\n"); 0149 } else if (!email.isEmpty()) { 0150 text += QLatin1String("<p>") + i18nc("Show the author of this item in a list", "By <i>%1</i>", authorName) + QLatin1String(" <a href=\"mailto:") 0151 + email + QLatin1String("\">") + email + QLatin1String("</a></p>\n"); 0152 } else { 0153 text += QLatin1String("<p>") + i18nc("Show the author of this item in a list", "By <i>%1</i>", authorName) + QLatin1String("</p>\n"); 0154 } 0155 } 0156 authorLabel->setText(text); 0157 elementYPos += authorLabel->height(); 0158 } 0159 elementYPos += ItemMargin; 0160 0161 // setup download label 0162 QLabel *downloadLabel = qobject_cast<QLabel *>(widgets.at(DelegateDownloadCounterLabel)); 0163 if (downloadLabel != nullptr) { 0164 downloadLabel->setWordWrap(true); 0165 downloadLabel->setAlignment(Qt::AlignHCenter); 0166 downloadLabel->resize(QSize(option.rect.width() - (ItemMargin * 2), option.fontMetrics.height())); 0167 downloadLabel->move((ItemGridWidth - downloadLabel->width()) / 2, elementYPos); 0168 0169 unsigned int fans = entry.numberFans(); 0170 unsigned int downloads = entry.downloadCount(); 0171 0172 QString text; 0173 QString fanString; 0174 QString downloadString; 0175 if (fans > 0) { 0176 fanString = i18ncp("fan as in supporter", "1 fan", "%1 fans", fans); 0177 } 0178 if (downloads > 0) { 0179 downloadString = i18np("1 download", "%1 downloads", downloads); 0180 } 0181 if (downloads > 0 || fans > 0) { 0182 text += QLatin1String("<p>") + downloadString; 0183 if (downloads > 0 && fans > 0) { 0184 text += QLatin1String(", "); 0185 } 0186 text += fanString + QLatin1String("</p>"); 0187 } 0188 downloadLabel->setText(text); 0189 elementYPos += downloadLabel->height(); 0190 } 0191 elementYPos += ItemMargin; 0192 m_elementYPos = elementYPos; 0193 } 0194 0195 void ItemsGridViewDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0196 { 0197 if (option.state & QStyle::State_MouseOver) { 0198 QModelIndex focIndex = focusedIndex(); 0199 if (m_oldIndex != focIndex || !m_operationBar->isVisible()) { 0200 ItemsGridViewDelegate *delegate = const_cast<ItemsGridViewDelegate *>(this); 0201 0202 delegate->displayOperationBar(option.rect, index); 0203 delegate->m_oldIndex = focIndex; 0204 } 0205 } else { 0206 QModelIndex focindex = focusedIndex(); 0207 if (!focindex.isValid()) { 0208 qCDebug(KNEWSTUFF) << "INVALID hide selection"; 0209 m_operationBar->hide(); 0210 } 0211 } 0212 0213 QStyle *style = QApplication::style(); 0214 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, nullptr); 0215 0216 painter->save(); 0217 0218 if (option.state & QStyle::State_Selected) { 0219 painter->setPen(QPen(option.palette.highlightedText().color())); 0220 } else { 0221 painter->setPen(QPen(option.palette.text().color())); 0222 } 0223 0224 const KNSCore::ItemsModel *realmodel = qobject_cast<const KNSCore::ItemsModel *>(index.model()); 0225 0226 if (realmodel->hasPreviewImages()) { 0227 int width = option.rect.width(); 0228 0229 KNSCore::EntryInternal entry = index.data(Qt::UserRole).value<KNSCore::EntryInternal>(); 0230 if (entry.previewUrl(KNSCore::EntryInternal::PreviewSmall1).isEmpty()) { 0231 ; 0232 } else { 0233 QPoint centralPoint(option.rect.left() + width / 2, option.rect.top() + ItemMargin + FrameThickness + KNSCore::PreviewHeight / 2); 0234 QImage image = entry.previewImage(KNSCore::EntryInternal::PreviewSmall1); 0235 if (!image.isNull()) { 0236 QPoint previewPoint(centralPoint.x() - image.width() / 2, centralPoint.y() - image.height() / 2); 0237 painter->drawImage(previewPoint, image); 0238 0239 QPixmap frameImageScaled = m_frameImage.scaled(image.width() + FrameThickness * 2, image.height() + FrameThickness * 2); 0240 QPoint framePoint(centralPoint.x() - frameImageScaled.width() / 2, centralPoint.y() - frameImageScaled.height() / 2); 0241 painter->drawPixmap(framePoint, frameImageScaled); 0242 } else { 0243 QPoint thumbnailPoint(option.rect.left() + ((width - KNSCore::PreviewWidth - FrameThickness * 2) / 2), option.rect.top() + ItemMargin); 0244 QRect rect(thumbnailPoint, QSize(KNSCore::PreviewWidth + FrameThickness * 2, KNSCore::PreviewHeight + FrameThickness * 2)); 0245 painter->drawText(rect, Qt::AlignCenter | Qt::TextWordWrap, i18n("Loading Preview")); 0246 } 0247 } 0248 } 0249 0250 painter->restore(); 0251 } 0252 0253 QSize ItemsGridViewDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0254 { 0255 Q_UNUSED(option); 0256 Q_UNUSED(index); 0257 0258 QSize size; 0259 0260 size.setWidth(ItemGridWidth); 0261 size.setHeight(qMax(option.fontMetrics.height() * 13, ItemGridHeight)); // up to 6 lines of text, and two margins 0262 return size; 0263 } 0264 0265 void ItemsGridViewDelegate::createOperationBar() 0266 { 0267 m_operationBar = new QWidget(this->itemView()->viewport()); 0268 0269 m_detailsButton = new QToolButton(); 0270 m_detailsButton->setToolButtonStyle(Qt::ToolButtonFollowStyle); 0271 m_detailsButton->setPopupMode(QToolButton::InstantPopup); 0272 m_detailsButton->setToolTip(i18n("Details")); 0273 m_detailsButton->setIcon(QIcon::fromTheme(QStringLiteral("documentinfo"))); 0274 setBlockedEventTypes(m_detailsButton, QList<QEvent::Type>() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick); 0275 connect(m_detailsButton, &QToolButton::clicked, this, static_cast<void (ItemsGridViewDelegate::*)()>(&ItemsGridViewDelegate::slotDetailsClicked)); 0276 0277 m_installButton = new QToolButton(); 0278 m_installButton->setToolButtonStyle(Qt::ToolButtonFollowStyle); 0279 m_installButton->setPopupMode(QToolButton::InstantPopup); 0280 0281 setBlockedEventTypes(m_installButton, QList<QEvent::Type>() << QEvent::MouseButtonPress << QEvent::MouseButtonRelease << QEvent::MouseButtonDblClick); 0282 connect(m_installButton, &QAbstractButton::clicked, this, &ItemsGridViewDelegate::slotInstallClicked); 0283 connect(m_installButton, &QToolButton::triggered, this, &ItemsGridViewDelegate::slotInstallActionTriggered); 0284 0285 if (m_installButton->menu()) { 0286 QMenu *buttonMenu = m_installButton->menu(); 0287 buttonMenu->clear(); 0288 m_installButton->setMenu(nullptr); 0289 buttonMenu->deleteLater(); 0290 } 0291 0292 QHBoxLayout *layout = new QHBoxLayout(m_operationBar); 0293 0294 layout->setSpacing(1); 0295 layout->addWidget(m_installButton); 0296 layout->addWidget(m_detailsButton); 0297 0298 m_operationBar->adjustSize(); 0299 m_operationBar->hide(); 0300 } 0301 0302 void ItemsGridViewDelegate::displayOperationBar(const QRect &rect, const QModelIndex &index) 0303 { 0304 KNSCore::EntryInternal entry = index.data(Qt::UserRole).value<KNSCore::EntryInternal>(); 0305 if (m_installButton != nullptr) { 0306 if (m_installButton->menu() != nullptr) { 0307 QMenu *buttonMenu = m_installButton->menu(); 0308 buttonMenu->clear(); 0309 m_installButton->setMenu(nullptr); 0310 buttonMenu->deleteLater(); 0311 } 0312 0313 bool installable = false; 0314 bool enabled = true; 0315 QString text; 0316 QIcon icon; 0317 0318 switch (entry.status()) { 0319 case Entry::Installed: 0320 text = i18n("Uninstall"); 0321 icon = m_iconDelete; 0322 break; 0323 case Entry::Updateable: 0324 text = i18n("Update"); 0325 icon = m_iconUpdate; 0326 installable = true; 0327 break; 0328 case Entry::Installing: 0329 text = i18n("Installing"); 0330 enabled = false; 0331 icon = m_iconUpdate; 0332 break; 0333 case Entry::Updating: 0334 text = i18n("Updating"); 0335 enabled = false; 0336 icon = m_iconUpdate; 0337 break; 0338 case Entry::Downloadable: 0339 text = i18n("Install"); 0340 icon = m_iconInstall; 0341 installable = true; 0342 break; 0343 case Entry::Deleted: 0344 text = i18n("Install Again"); 0345 icon = m_iconInstall; 0346 installable = true; 0347 break; 0348 default: 0349 text = i18n("Install"); 0350 } 0351 m_installButton->setToolTip(text); 0352 m_installButton->setIcon(icon); 0353 m_installButton->setEnabled(enabled); 0354 if (installable && entry.downloadLinkCount() > 1) { 0355 QMenu *installMenu = new QMenu(m_installButton); 0356 const auto lst = entry.downloadLinkInformationList(); 0357 for (const KNSCore::EntryInternal::DownloadLinkInformation &info : lst) { 0358 QString text = info.name; 0359 if (!info.distributionType.trimmed().isEmpty()) { 0360 text + " (" + info.distributionType.trimmed() + QLatin1Char(')'); 0361 } 0362 QAction *installAction = installMenu->addAction(m_iconInstall, text); 0363 installAction->setData(QPoint(index.row(), info.id)); 0364 } 0365 m_installButton->setMenu(installMenu); 0366 } 0367 0368 m_operationBar->move(rect.left() + (ItemGridWidth - m_operationBar->width()) / 2, rect.top() + m_elementYPos); 0369 m_operationBar->show(); 0370 } 0371 } 0372 } 0373 0374 #include "moc_itemsgridviewdelegate_p.cpp"