1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 2021 Julius Künzel <jk.kdedev@smartlab.uber.space> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.1-or-later |
6 | */ |
7 | |
8 | #include "kaboutapplicationcomponentlistdelegate_p.h" |
9 | |
10 | #include "kaboutapplicationcomponentmodel_p.h" |
11 | #include "kaboutapplicationlistview_p.h" |
12 | #include "klicensedialog_p.h" |
13 | #include "ktoolbar.h" |
14 | |
15 | #include <KLocalizedString> |
16 | |
17 | #include <QAction> |
18 | #include <QApplication> |
19 | #include <QDesktopServices> |
20 | #include <QLabel> |
21 | #include <QPainter> |
22 | #include <QStandardPaths> |
23 | #include <QUrl> |
24 | |
25 | namespace KDEPrivate |
26 | { |
27 | |
28 | KAboutApplicationComponentListDelegate::KAboutApplicationComponentListDelegate(QAbstractItemView *itemView, QObject *parent) |
29 | : KWidgetItemDelegate(itemView, parent) |
30 | { |
31 | } |
32 | |
33 | QList<QWidget *> KAboutApplicationComponentListDelegate::createItemWidgets(const QModelIndex &index) const |
34 | { |
35 | Q_UNUSED(index); |
36 | QList<QWidget *> list; |
37 | |
38 | QLabel *textLabel = new QLabel(itemView()); |
39 | list.append(t: textLabel); |
40 | |
41 | KAboutApplicationComponentProfile profile = index.data().value<KAboutApplicationComponentProfile>(); |
42 | textLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); |
43 | QObject::connect(sender: textLabel, signal: &QLabel::linkActivated, context: itemView(), slot: [this, profile]() { |
44 | auto *dialog = new KLicenseDialog(profile.license(), itemView()); |
45 | dialog->show(); |
46 | }); |
47 | |
48 | KToolBar *mainLinks = new KToolBar(itemView(), false, false); |
49 | |
50 | QAction *homepageAction = new QAction(QIcon::fromTheme(QStringLiteral("internet-services" )), // |
51 | i18n("Visit component's homepage" ), |
52 | mainLinks); |
53 | homepageAction->setVisible(false); |
54 | mainLinks->addAction(action: homepageAction); |
55 | QAction *visitProfileAction = new QAction(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff" )), QString(), mainLinks); |
56 | visitProfileAction->setVisible(false); |
57 | mainLinks->addAction(action: visitProfileAction); |
58 | |
59 | list.append(t: mainLinks); |
60 | |
61 | connect(sender: mainLinks, signal: &QToolBar::actionTriggered, context: this, slot: &KAboutApplicationComponentListDelegate::launchUrl); |
62 | |
63 | return list; |
64 | } |
65 | |
66 | void KAboutApplicationComponentListDelegate::updateItemWidgets(const QList<QWidget *> &widgets, |
67 | const QStyleOptionViewItem &option, |
68 | const QPersistentModelIndex &index) const |
69 | { |
70 | const int margin = option.fontMetrics.height() / 2; |
71 | |
72 | KAboutApplicationComponentProfile profile = index.data().value<KAboutApplicationComponentProfile>(); |
73 | |
74 | QRect wRect = widgetsRect(option, index); |
75 | |
76 | // Let's fill in the text first... |
77 | QLabel *label = qobject_cast<QLabel *>(object: widgets.at(i: TextLabel)); |
78 | label->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Fixed); |
79 | |
80 | QString text = buildTextForProfile(profile); |
81 | |
82 | label->move(ax: wRect.left(), ay: wRect.top()); |
83 | label->resize(w: wRect.width(), h: heightForString(string: text, lineWidth: wRect.width() - margin, option) + margin); |
84 | label->setWordWrap(true); |
85 | label->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0); |
86 | label->setAlignment(Qt::AlignBottom | Qt::AlignLeft); |
87 | label->setForegroundRole(QPalette::WindowText); |
88 | |
89 | label->setText(text); |
90 | |
91 | // And now we fill in the main links (email + homepage + OCS profile)... |
92 | KToolBar *mainLinks = qobject_cast<KToolBar *>(object: widgets.at(i: MainLinks)); |
93 | mainLinks->setIconSize(QSize(22, 22)); |
94 | mainLinks->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0); |
95 | mainLinks->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Fixed); |
96 | QAction *action; |
97 | if (!profile.webAdress().isEmpty()) { |
98 | action = mainLinks->actions().at(i: HomepageAction); |
99 | action->setToolTip(i18nc("@info:tooltip" , "Visit components's homepage\n%1" , profile.webAdress())); |
100 | action->setData(profile.webAdress()); |
101 | action->setVisible(true); |
102 | } |
103 | mainLinks->resize(QSize(mainLinks->sizeHint().width(), LINK_HEIGHT)); |
104 | mainLinks->move(ax: wRect.left(), ay: wRect.top() + label->height()); |
105 | itemView()->reset(); |
106 | } |
107 | |
108 | QSize KAboutApplicationComponentListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const |
109 | { |
110 | int height = widgetsRect(option, index).height(); |
111 | |
112 | QSize metrics(option.fontMetrics.height() * 7, height); |
113 | return metrics; |
114 | } |
115 | |
116 | void KAboutApplicationComponentListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &) const |
117 | { |
118 | QStyle *style = QApplication::style(); |
119 | style->drawPrimitive(pe: QStyle::PE_Widget, opt: &option, p: painter, w: nullptr); |
120 | } |
121 | |
122 | void KAboutApplicationComponentListDelegate::launchUrl(QAction *action) const |
123 | { |
124 | QString url = action->data().toString(); |
125 | if (!url.isEmpty()) { |
126 | QDesktopServices::openUrl(url: QUrl(url)); |
127 | } |
128 | } |
129 | |
130 | int KAboutApplicationComponentListDelegate::heightForString(const QString &string, int lineWidth, const QStyleOptionViewItem &option) const |
131 | { |
132 | QFontMetrics fm = option.fontMetrics; |
133 | constexpr auto opts = Qt::AlignLeft | Qt::AlignBottom | Qt::TextWordWrap; |
134 | QRect boundingRect = fm.boundingRect(x: 0, y: 0, w: lineWidth, h: 9999, flags: opts, text: string); |
135 | return boundingRect.height(); |
136 | } |
137 | |
138 | QString KAboutApplicationComponentListDelegate::buildTextForProfile(const KAboutApplicationComponentProfile &profile) const |
139 | { |
140 | QString text = QLatin1String("<b>" ) + i18nc("@item Component name in about dialog." , "%1" , profile.name()) + QLatin1String("</b>" ); |
141 | |
142 | if (!profile.version().isEmpty()) { |
143 | text += QStringLiteral("\n<br><i>%1</i>" ).arg(i18n("Version %1" , profile.version())); |
144 | } |
145 | |
146 | if (!profile.description().isEmpty()) { |
147 | text += QLatin1String("\n<br>" ) + profile.description(); |
148 | } |
149 | |
150 | if (profile.license().key() != KAboutLicense::Unknown) { |
151 | text += QLatin1String("\n<br>" ); |
152 | text += QStringLiteral("<a href=\"#\">%2</a>" ).arg(i18n("License: %1" , profile.license().name(KAboutLicense::FullName))); |
153 | } |
154 | |
155 | return text; |
156 | } |
157 | |
158 | QRect KAboutApplicationComponentListDelegate::widgetsRect(const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const |
159 | { |
160 | KAboutApplicationComponentProfile profile = index.data().value<KAboutApplicationComponentProfile>(); |
161 | int margin = option.fontMetrics.height() / 2; |
162 | |
163 | QRect widgetsRect = QRect(option.rect.left() + margin, // |
164 | margin / 2, // |
165 | option.rect.width() - 2 * margin, // |
166 | 0); |
167 | |
168 | int textHeight = heightForString(string: buildTextForProfile(profile), lineWidth: widgetsRect.width() - margin, option); |
169 | widgetsRect.setHeight(textHeight + LINK_HEIGHT + 1.5 * margin); |
170 | |
171 | return widgetsRect; |
172 | } |
173 | |
174 | } // namespace KDEPrivate |
175 | |
176 | #include "moc_kaboutapplicationcomponentlistdelegate_p.cpp" |
177 | |