File indexing completed on 2024-12-01 12:40:34

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2021 Julius Künzel <jk.kdedev@smartlab.uber.space>
0004 
0005     SPDX-License-Identifier: LGPL-2.1-or-later
0006 */
0007 
0008 #include "kaboutapplicationcomponentlistdelegate_p.h"
0009 
0010 #include "kaboutapplicationcomponentmodel_p.h"
0011 #include "kaboutapplicationlistview_p.h"
0012 #include "klicensedialog_p.h"
0013 #include "ktoolbar.h"
0014 
0015 #include <KLocalizedString>
0016 
0017 #include <QAction>
0018 #include <QApplication>
0019 #include <QDesktopServices>
0020 #include <QLabel>
0021 #include <QPainter>
0022 #include <QStandardPaths>
0023 #include <QUrl>
0024 
0025 namespace KDEPrivate
0026 {
0027 
0028 KAboutApplicationComponentListDelegate::KAboutApplicationComponentListDelegate(QAbstractItemView *itemView, QObject *parent)
0029     : KWidgetItemDelegate(itemView, parent)
0030 {
0031 }
0032 
0033 QList<QWidget *> KAboutApplicationComponentListDelegate::createItemWidgets(const QModelIndex &index) const
0034 {
0035     Q_UNUSED(index);
0036     QList<QWidget *> list;
0037 
0038     QLabel *textLabel = new QLabel(itemView());
0039     list.append(textLabel);
0040 
0041     KAboutApplicationComponentProfile profile = index.data().value<KAboutApplicationComponentProfile>();
0042     textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction);
0043     QObject::connect(textLabel, &QLabel::linkActivated, itemView(), [this, profile]() {
0044         auto *dialog = new KLicenseDialog(profile.license(), itemView());
0045         dialog->show();
0046     });
0047 
0048     KToolBar *mainLinks = new KToolBar(itemView(), false, false);
0049 
0050     QAction *homepageAction = new QAction(QIcon::fromTheme(QStringLiteral("internet-services")), //
0051                                           i18n("Visit component's homepage"),
0052                                           mainLinks);
0053     homepageAction->setVisible(false);
0054     mainLinks->addAction(homepageAction);
0055     QAction *visitProfileAction = new QAction(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")), QString(), mainLinks);
0056     visitProfileAction->setVisible(false);
0057     mainLinks->addAction(visitProfileAction);
0058 
0059     list.append(mainLinks);
0060 
0061     connect(mainLinks, &QToolBar::actionTriggered, this, &KAboutApplicationComponentListDelegate::launchUrl);
0062 
0063     return list;
0064 }
0065 
0066 void KAboutApplicationComponentListDelegate::updateItemWidgets(const QList<QWidget *> widgets,
0067                                                                const QStyleOptionViewItem &option,
0068                                                                const QPersistentModelIndex &index) const
0069 {
0070     const int margin = option.fontMetrics.height() / 2;
0071 
0072     KAboutApplicationComponentProfile profile = index.data().value<KAboutApplicationComponentProfile>();
0073 
0074     QRect wRect = widgetsRect(option, index);
0075 
0076     // Let's fill in the text first...
0077     QLabel *label = qobject_cast<QLabel *>(widgets.at(TextLabel));
0078     label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0079 
0080     QString text = buildTextForProfile(profile);
0081 
0082     label->move(wRect.left(), wRect.top());
0083     label->resize(wRect.width(), heightForString(text, wRect.width() - margin, option) + margin);
0084     label->setWordWrap(true);
0085     label->setContentsMargins(0, 0, 0, 0);
0086     label->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
0087     label->setForegroundRole(QPalette::WindowText);
0088 
0089     label->setText(text);
0090 
0091     // And now we fill in the main links (email + homepage + OCS profile)...
0092     KToolBar *mainLinks = qobject_cast<KToolBar *>(widgets.at(MainLinks));
0093     mainLinks->setIconSize(QSize(22, 22));
0094     mainLinks->setContentsMargins(0, 0, 0, 0);
0095     mainLinks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed);
0096     QAction *action;
0097     if (!profile.webAdress().isEmpty()) {
0098         action = mainLinks->actions().at(HomepageAction);
0099         action->setToolTip(i18nc("@info:tooltip", "Visit components's homepage\n%1", profile.webAdress()));
0100         action->setData(profile.webAdress());
0101         action->setVisible(true);
0102     }
0103     mainLinks->resize(QSize(mainLinks->sizeHint().width(), LINK_HEIGHT));
0104     mainLinks->move(wRect.left(), wRect.top() + label->height());
0105     itemView()->reset();
0106 }
0107 
0108 QSize KAboutApplicationComponentListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
0109 {
0110     int height = widgetsRect(option, index).height();
0111 
0112     QSize metrics(option.fontMetrics.height() * 7, height);
0113     return metrics;
0114 }
0115 
0116 void KAboutApplicationComponentListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &) const
0117 {
0118     QStyle *style = QApplication::style();
0119     style->drawPrimitive(QStyle::PE_Widget, &option, painter, nullptr);
0120 }
0121 
0122 void KAboutApplicationComponentListDelegate::launchUrl(QAction *action) const
0123 {
0124     QString url = action->data().toString();
0125     if (!url.isEmpty()) {
0126         QDesktopServices::openUrl(QUrl(url));
0127     }
0128 }
0129 
0130 int KAboutApplicationComponentListDelegate::heightForString(const QString &string, int lineWidth, const QStyleOptionViewItem &option) const
0131 {
0132     QFontMetrics fm = option.fontMetrics;
0133     constexpr auto opts = Qt::AlignLeft | Qt::AlignBottom | Qt::TextWordWrap;
0134     QRect boundingRect = fm.boundingRect(0, 0, lineWidth, 9999, opts, string);
0135     return boundingRect.height();
0136 }
0137 
0138 QString KAboutApplicationComponentListDelegate::buildTextForProfile(const KAboutApplicationComponentProfile &profile) const
0139 {
0140     QString text = QLatin1String("<b>") + i18nc("@item Component name in about dialog.", "%1", profile.name()) + QLatin1String("</b>");
0141 
0142     if (!profile.version().isEmpty()) {
0143         text += QStringLiteral("\n<br><i>%1</i>").arg(i18n("Version %1", profile.version()));
0144     }
0145 
0146     if (!profile.description().isEmpty()) {
0147         text += QLatin1String("\n<br>") + profile.description();
0148     }
0149 
0150     if (profile.license().key() != KAboutLicense::Unknown) {
0151         text += QLatin1String("\n<br>");
0152         text += QStringLiteral("<a href=\"#\">%2</a>").arg(i18n("License: %1", profile.license().name(KAboutLicense::FullName)));
0153     }
0154 
0155     return text;
0156 }
0157 
0158 QRect KAboutApplicationComponentListDelegate::widgetsRect(const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const
0159 {
0160     KAboutApplicationComponentProfile profile = index.data().value<KAboutApplicationComponentProfile>();
0161     int margin = option.fontMetrics.height() / 2;
0162 
0163     QRect widgetsRect = QRect(option.rect.left() + margin, //
0164                               margin / 2, //
0165                               option.rect.width() - 2 * margin, //
0166                               0);
0167 
0168     int textHeight = heightForString(buildTextForProfile(profile), widgetsRect.width() - margin, option);
0169     widgetsRect.setHeight(textHeight + LINK_HEIGHT + 1.5 * margin);
0170 
0171     return widgetsRect;
0172 }
0173 
0174 } // namespace KDEPrivate
0175 
0176 #include "moc_kaboutapplicationcomponentlistdelegate_p.cpp"