File indexing completed on 2024-09-08 12:23:19
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2010 Teo Mrnjavac <teo@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.1-or-later 0006 */ 0007 0008 #include "kaboutapplicationpersonlistdelegate_p.h" 0009 0010 #include "kaboutapplicationlistview_p.h" 0011 #include "kaboutapplicationpersonmodel_p.h" 0012 #include "ktoolbar.h" 0013 0014 #include <KLocalizedString> 0015 0016 #include <QAction> 0017 #include <QApplication> 0018 #include <QDesktopServices> 0019 #include <QLabel> 0020 #include <QPainter> 0021 #include <QStandardPaths> 0022 0023 namespace KDEPrivate 0024 { 0025 Q_GLOBAL_STATIC(QPixmap, s_avatarFallback) 0026 static QPixmap avatarFallback() 0027 { 0028 if (s_avatarFallback->isNull()) { 0029 const QIcon icon = QIcon::fromTheme(QStringLiteral("user")); 0030 *s_avatarFallback = icon.pixmap(icon.actualSize(QSize(AVATAR_WIDTH / qGuiApp->devicePixelRatio(), AVATAR_HEIGHT / qGuiApp->devicePixelRatio())), 0031 QIcon::Normal, 0032 QIcon::On); 0033 } 0034 return *s_avatarFallback; 0035 } 0036 0037 KAboutApplicationPersonListDelegate::KAboutApplicationPersonListDelegate(QAbstractItemView *itemView, QObject *parent) 0038 : KWidgetItemDelegate(itemView, parent) 0039 { 0040 } 0041 0042 QList<QWidget *> KAboutApplicationPersonListDelegate::createItemWidgets(const QModelIndex &index) const 0043 { 0044 Q_UNUSED(index); 0045 QList<QWidget *> list; 0046 0047 QLabel *textLabel = new QLabel(itemView()); 0048 list.append(textLabel); 0049 0050 KToolBar *mainLinks = new KToolBar(itemView(), false, false); 0051 0052 QAction *emailAction = new QAction(QIcon::fromTheme(QStringLiteral("mail-send")), // 0053 i18nc("Action to send an email to a contributor", "Email contributor"), 0054 mainLinks); 0055 emailAction->setVisible(false); 0056 mainLinks->addAction(emailAction); 0057 QAction *homepageAction = new QAction(QIcon::fromTheme(QStringLiteral("internet-services")), // 0058 i18n("Visit contributor's homepage"), 0059 mainLinks); 0060 homepageAction->setVisible(false); 0061 mainLinks->addAction(homepageAction); 0062 QAction *visitProfileAction = new QAction(QIcon::fromTheme(QStringLiteral("get-hot-new-stuff")), QString(), mainLinks); 0063 visitProfileAction->setVisible(false); 0064 mainLinks->addAction(visitProfileAction); 0065 0066 list.append(mainLinks); 0067 0068 connect(mainLinks, &QToolBar::actionTriggered, this, &KAboutApplicationPersonListDelegate::launchUrl); 0069 0070 return list; 0071 } 0072 0073 void KAboutApplicationPersonListDelegate::updateItemWidgets(const QList<QWidget *> widgets, 0074 const QStyleOptionViewItem &option, 0075 const QPersistentModelIndex &index) const 0076 { 0077 const int margin = option.fontMetrics.height() / 2; 0078 0079 KAboutApplicationPersonProfile profile = index.data().value<KAboutApplicationPersonProfile>(); 0080 0081 QRect wRect = widgetsRect(option, index); 0082 0083 // Let's fill in the text first... 0084 QLabel *label = qobject_cast<QLabel *>(widgets.at(TextLabel)); 0085 label->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0086 0087 QString text = buildTextForProfile(profile); 0088 0089 label->move(wRect.left(), wRect.top()); 0090 label->resize(wRect.width(), heightForString(text, wRect.width() - margin, option) + margin); 0091 label->setWordWrap(true); 0092 label->setContentsMargins(0, 0, 0, 0); 0093 label->setAlignment(Qt::AlignBottom | Qt::AlignLeft); 0094 label->setForegroundRole(QPalette::WindowText); 0095 0096 label->setText(text); 0097 0098 // And now we fill in the main links (email + homepage + OCS profile)... 0099 KToolBar *mainLinks = qobject_cast<KToolBar *>(widgets.at(MainLinks)); 0100 mainLinks->setIconSize(QSize(22, 22)); 0101 mainLinks->setContentsMargins(0, 0, 0, 0); 0102 mainLinks->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0103 QAction *action; 0104 if (!profile.email().isEmpty()) { 0105 action = mainLinks->actions().at(EmailAction); 0106 action->setToolTip(i18nc("@info:tooltip Action to send an email to a contributor", "Email contributor\n%1", profile.email())); 0107 action->setData(QString(QLatin1String("mailto:") + profile.email())); 0108 action->setVisible(true); 0109 } 0110 if (!profile.homepage().isEmpty()) { 0111 action = mainLinks->actions().at(HomepageAction); 0112 action->setToolTip(i18nc("@info:tooltip", "Visit contributor's homepage\n%1", profile.homepage().toString())); 0113 action->setData(profile.homepage().toString()); 0114 action->setVisible(true); 0115 } 0116 if (!profile.ocsProfileUrl().isEmpty()) { 0117 action = mainLinks->actions().at(VisitProfileAction); 0118 KAboutApplicationPersonModel *model = qobject_cast<KAboutApplicationPersonModel *>(itemView()->model()); 0119 action->setToolTip(i18nc("@info:tooltip", "Visit contributor's profile on %1\n%2", model->providerName(), profile.ocsProfileUrl())); 0120 action->setData(profile.ocsProfileUrl()); 0121 action->setVisible(true); 0122 } 0123 mainLinks->resize(QSize(mainLinks->sizeHint().width(), MAIN_LINKS_HEIGHT)); 0124 mainLinks->move(wRect.left(), wRect.top() + label->height()); 0125 itemView()->reset(); 0126 } 0127 0128 QSize KAboutApplicationPersonListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0129 { 0130 int margin = option.fontMetrics.height() / 2; 0131 0132 int height = qMax(widgetsRect(option, index).height(), AVATAR_HEIGHT + 2 * margin); 0133 0134 QSize metrics(option.fontMetrics.height() * 7, height); 0135 return metrics; 0136 } 0137 0138 void KAboutApplicationPersonListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0139 { 0140 int margin = option.fontMetrics.height() / 2; 0141 0142 QStyle *style = QApplication::style(); 0143 style->drawPrimitive(QStyle::PE_Widget, &option, painter, nullptr); 0144 0145 const KAboutApplicationPersonModel *model = qobject_cast<const KAboutApplicationPersonModel *>(index.model()); 0146 0147 if (model->showRemoteAvatars() && model->hasAvatarPixmaps()) { 0148 int height = qMax(widgetsRect(option, index).height(), AVATAR_HEIGHT + 2 * margin); 0149 QPoint point(option.rect.left() + 2 * margin, // 0150 option.rect.top() + ((height - AVATAR_HEIGHT) / 2)); 0151 0152 KAboutApplicationPersonProfile profile = index.data().value<KAboutApplicationPersonProfile>(); 0153 0154 QPixmap fallback; 0155 if (profile.avatar().isNull()) { 0156 fallback = avatarFallback(); 0157 fallback.setDevicePixelRatio(itemView()->devicePixelRatio()); 0158 } 0159 const QPixmap &pixmap = profile.avatar().isNull() ? fallback : profile.avatar(); 0160 point.setX((AVATAR_WIDTH - pixmap.width()) / 2 + 5); 0161 point.setY(option.rect.top() + ((height - pixmap.height()) / 2)); 0162 painter->drawPixmap(point, pixmap); 0163 0164 QPoint framePoint(point.x() - 5, point.y() - 5); 0165 QPixmap framePixmap(QStringLiteral(":/kxmlgui5/thumb_frame.png")); 0166 painter->drawPixmap(framePoint, framePixmap.scaled(pixmap.width() + 10, pixmap.height() + 10)); 0167 } 0168 } 0169 0170 void KAboutApplicationPersonListDelegate::launchUrl(QAction *action) const 0171 { 0172 QString url = action->data().toString(); 0173 if (!url.isEmpty()) { 0174 QDesktopServices::openUrl(QUrl(url)); 0175 } 0176 } 0177 0178 int KAboutApplicationPersonListDelegate::heightForString(const QString &string, int lineWidth, const QStyleOptionViewItem &option) const 0179 { 0180 QFontMetrics fm = option.fontMetrics; 0181 constexpr auto opts = Qt::AlignLeft | Qt::AlignBottom | Qt::TextWordWrap; 0182 QRect boundingRect = fm.boundingRect(0, 0, lineWidth, 9999, opts, string); 0183 return boundingRect.height(); 0184 } 0185 0186 QString KAboutApplicationPersonListDelegate::buildTextForProfile(const KAboutApplicationPersonProfile &profile) const 0187 { 0188 QString text = QLatin1String("<b>") + i18nc("@item Contributor name in about dialog.", "%1", profile.name()) + QLatin1String("</b>"); 0189 0190 if (!profile.task().isEmpty()) { 0191 text += QLatin1String("\n<br><i>%1</i>").arg(profile.task()); 0192 } 0193 0194 if (!profile.location().isEmpty()) { 0195 text += QLatin1String("\n<br>") + profile.location(); 0196 } 0197 return text; 0198 } 0199 0200 QRect KAboutApplicationPersonListDelegate::widgetsRect(const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const 0201 { 0202 KAboutApplicationPersonProfile profile = index.data().value<KAboutApplicationPersonProfile>(); 0203 int margin = option.fontMetrics.height() / 2; 0204 0205 QRect widgetsRect; 0206 if (qobject_cast<const KAboutApplicationPersonModel *>(index.model())->showRemoteAvatars()) { 0207 widgetsRect = QRect(option.rect.left() + AVATAR_WIDTH + 3 * margin, // 0208 margin / 2, // 0209 option.rect.width() - AVATAR_WIDTH - 4 * margin, // 0210 0); 0211 } else { 0212 widgetsRect = QRect(option.rect.left() + margin, // 0213 margin / 2, // 0214 option.rect.width() - 2 * margin, // 0215 0); 0216 } 0217 0218 int textHeight = heightForString(buildTextForProfile(profile), widgetsRect.width() - margin, option); 0219 widgetsRect.setHeight(textHeight + MAIN_LINKS_HEIGHT + 1.5 * margin); 0220 0221 return widgetsRect; 0222 } 0223 0224 } // namespace KDEPrivate 0225 0226 #include "moc_kaboutapplicationpersonlistdelegate_p.cpp"