1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2010 Teo Mrnjavac <teo@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.1-or-later
6*/
7
8#include "kaboutapplicationpersonlistdelegate_p.h"
9
10#include "kaboutapplicationlistview_p.h"
11#include "kaboutapplicationpersonmodel_p.h"
12#include "ktoolbar.h"
13
14#include <KLocalizedString>
15
16#include <QAction>
17#include <QApplication>
18#include <QDesktopServices>
19#include <QLabel>
20#include <QPainter>
21#include <QStandardPaths>
22
23namespace KDEPrivate
24{
25Q_GLOBAL_STATIC(QPixmap, s_avatarFallback)
26static QPixmap avatarFallback()
27{
28 if (s_avatarFallback->isNull()) {
29 const QIcon icon = QIcon::fromTheme(QStringLiteral("user"));
30 *s_avatarFallback = icon.pixmap(size: icon.actualSize(size: QSize(AVATAR_WIDTH / qGuiApp->devicePixelRatio(), AVATAR_HEIGHT / qGuiApp->devicePixelRatio())),
31 mode: QIcon::Normal,
32 state: QIcon::On);
33 }
34 return *s_avatarFallback;
35}
36
37KAboutApplicationPersonListDelegate::KAboutApplicationPersonListDelegate(QAbstractItemView *itemView, QObject *parent)
38 : KWidgetItemDelegate(itemView, parent)
39{
40}
41
42QList<QWidget *> KAboutApplicationPersonListDelegate::createItemWidgets(const QModelIndex &index) const
43{
44 Q_UNUSED(index);
45 QList<QWidget *> list;
46
47 QLabel *textLabel = new QLabel(itemView());
48 list.append(t: textLabel);
49
50 KToolBar *mainLinks = new KToolBar(itemView(), false, false);
51
52 QAction *emailAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), //
53 i18nc("Action to send an email to a contributor", "Email contributor"),
54 mainLinks);
55 emailAction->setVisible(false);
56 mainLinks->addAction(action: emailAction);
57 QAction *homepageAction = new QAction(QIcon::fromTheme(QStringLiteral("internet-services")), //
58 i18n("Visit contributor's homepage"),
59 mainLinks);
60 homepageAction->setVisible(false);
61 mainLinks->addAction(action: homepageAction);
62 QAction *visitProfileAction = new QAction(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")), QString(), mainLinks);
63 visitProfileAction->setVisible(false);
64 mainLinks->addAction(action: visitProfileAction);
65
66 list.append(t: mainLinks);
67
68 connect(sender: mainLinks, signal: &QToolBar::actionTriggered, context: this, slot: &KAboutApplicationPersonListDelegate::launchUrl);
69
70 return list;
71}
72
73void KAboutApplicationPersonListDelegate::updateItemWidgets(const QList<QWidget *> &widgets,
74 const QStyleOptionViewItem &option,
75 const QPersistentModelIndex &index) const
76{
77 const int margin = option.fontMetrics.height() / 2;
78
79 KAboutApplicationPersonProfile profile = index.data().value<KAboutApplicationPersonProfile>();
80
81 QRect wRect = widgetsRect(option, index);
82
83 // Let's fill in the text first...
84 QLabel *label = qobject_cast<QLabel *>(object: widgets.at(i: TextLabel));
85 label->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Fixed);
86
87 QString text = buildTextForProfile(profile);
88
89 label->move(ax: wRect.left(), ay: wRect.top());
90 label->resize(w: wRect.width(), h: heightForString(string: text, lineWidth: wRect.width() - margin, option) + margin);
91 label->setWordWrap(true);
92 label->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
93 label->setAlignment(Qt::AlignBottom | Qt::AlignLeft);
94 label->setForegroundRole(QPalette::WindowText);
95
96 label->setText(text);
97
98 // And now we fill in the main links (email + homepage)
99 KToolBar *mainLinks = qobject_cast<KToolBar *>(object: widgets.at(i: MainLinks));
100 mainLinks->setIconSize(QSize(22, 22));
101 mainLinks->setContentsMargins(left: 0, top: 0, right: 0, bottom: 0);
102 mainLinks->setSizePolicy(hor: QSizePolicy::Fixed, ver: QSizePolicy::Fixed);
103 QAction *action;
104 if (!profile.email().isEmpty()) {
105 action = mainLinks->actions().at(i: EmailAction);
106 action->setToolTip(i18nc("@info:tooltip Action to send an email to a contributor", "Email contributor\n%1", profile.email()));
107 action->setData(QString(QLatin1String("mailto:") + profile.email()));
108 action->setVisible(true);
109 }
110 if (!profile.homepage().isEmpty()) {
111 action = mainLinks->actions().at(i: HomepageAction);
112 action->setToolTip(i18nc("@info:tooltip", "Visit contributor's homepage\n%1", profile.homepage().toString()));
113 action->setData(profile.homepage().toString());
114 action->setVisible(true);
115 }
116 mainLinks->resize(QSize(mainLinks->sizeHint().width(), MAIN_LINKS_HEIGHT));
117 mainLinks->move(ax: wRect.left(), ay: wRect.top() + label->height());
118 itemView()->reset();
119}
120
121QSize KAboutApplicationPersonListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
122{
123 int margin = option.fontMetrics.height() / 2;
124
125 int height = qMax(a: widgetsRect(option, index).height(), b: AVATAR_HEIGHT + 2 * margin);
126
127 QSize metrics(option.fontMetrics.height() * 7, height);
128 return metrics;
129}
130
131void KAboutApplicationPersonListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
132{
133 int margin = option.fontMetrics.height() / 2;
134
135 QStyle *style = QApplication::style();
136 style->drawPrimitive(pe: QStyle::PE_Widget, opt: &option, p: painter, w: nullptr);
137
138 const KAboutApplicationPersonModel *model = qobject_cast<const KAboutApplicationPersonModel *>(object: index.model());
139
140 if (model->showRemoteAvatars() && model->hasAvatarPixmaps()) {
141 int height = qMax(a: widgetsRect(option, index).height(), b: AVATAR_HEIGHT + 2 * margin);
142 QPoint point(option.rect.left() + 2 * margin, //
143 option.rect.top() + ((height - AVATAR_HEIGHT) / 2));
144
145 KAboutApplicationPersonProfile profile = index.data().value<KAboutApplicationPersonProfile>();
146
147 QPixmap fallback;
148 if (profile.avatar().isNull()) {
149 fallback = avatarFallback();
150 fallback.setDevicePixelRatio(itemView()->devicePixelRatio());
151 }
152 const QPixmap &pixmap = profile.avatar().isNull() ? fallback : profile.avatar();
153 point.setX((AVATAR_WIDTH - pixmap.width()) / 2 + 5);
154 point.setY(option.rect.top() + ((height - pixmap.height()) / 2));
155 painter->drawPixmap(p: point, pm: pixmap);
156
157 QPoint framePoint(point.x() - 5, point.y() - 5);
158 QPixmap framePixmap(QStringLiteral(":/kxmlgui5/thumb_frame.png"));
159 painter->drawPixmap(p: framePoint, pm: framePixmap.scaled(w: pixmap.width() + 10, h: pixmap.height() + 10));
160 }
161}
162
163void KAboutApplicationPersonListDelegate::launchUrl(QAction *action) const
164{
165 QString url = action->data().toString();
166 if (!url.isEmpty()) {
167 QDesktopServices::openUrl(url: QUrl(url));
168 }
169}
170
171int KAboutApplicationPersonListDelegate::heightForString(const QString &string, int lineWidth, const QStyleOptionViewItem &option) const
172{
173 QFontMetrics fm = option.fontMetrics;
174 constexpr auto opts = Qt::AlignLeft | Qt::AlignBottom | Qt::TextWordWrap;
175 QRect boundingRect = fm.boundingRect(x: 0, y: 0, w: lineWidth, h: 9999, flags: opts, text: string);
176 return boundingRect.height();
177}
178
179QString KAboutApplicationPersonListDelegate::buildTextForProfile(const KAboutApplicationPersonProfile &profile) const
180{
181 QString text = QLatin1String("<b>") + i18nc("@item Contributor name in about dialog.", "%1", profile.name()) + QLatin1String("</b>");
182
183 if (!profile.task().isEmpty()) {
184 text += QLatin1String("\n<br><i>%1</i>").arg(args: profile.task());
185 }
186
187 if (!profile.location().isEmpty()) {
188 text += QLatin1String("\n<br>") + profile.location();
189 }
190 return text;
191}
192
193QRect KAboutApplicationPersonListDelegate::widgetsRect(const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const
194{
195 KAboutApplicationPersonProfile profile = index.data().value<KAboutApplicationPersonProfile>();
196 int margin = option.fontMetrics.height() / 2;
197
198 QRect widgetsRect;
199 if (qobject_cast<const KAboutApplicationPersonModel *>(object: index.model())->showRemoteAvatars()) {
200 widgetsRect = QRect(option.rect.left() + AVATAR_WIDTH + 3 * margin, //
201 margin / 2, //
202 option.rect.width() - AVATAR_WIDTH - 4 * margin, //
203 0);
204 } else {
205 widgetsRect = QRect(option.rect.left() + margin, //
206 margin / 2, //
207 option.rect.width() - 2 * margin, //
208 0);
209 }
210
211 int textHeight = heightForString(string: buildTextForProfile(profile), lineWidth: widgetsRect.width() - margin, option);
212 widgetsRect.setHeight(textHeight + MAIN_LINKS_HEIGHT + 1.5 * margin);
213
214 return widgetsRect;
215}
216
217} // namespace KDEPrivate
218
219#include "moc_kaboutapplicationpersonlistdelegate_p.cpp"
220

source code of kxmlgui/src/kaboutapplicationpersonlistdelegate_p.cpp