File indexing completed on 2024-12-08 03:43:54
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) 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 mainLinks->resize(QSize(mainLinks->sizeHint().width(), MAIN_LINKS_HEIGHT)); 0117 mainLinks->move(wRect.left(), wRect.top() + label->height()); 0118 itemView()->reset(); 0119 } 0120 0121 QSize KAboutApplicationPersonListDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0122 { 0123 int margin = option.fontMetrics.height() / 2; 0124 0125 int height = qMax(widgetsRect(option, index).height(), AVATAR_HEIGHT + 2 * margin); 0126 0127 QSize metrics(option.fontMetrics.height() * 7, height); 0128 return metrics; 0129 } 0130 0131 void KAboutApplicationPersonListDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0132 { 0133 int margin = option.fontMetrics.height() / 2; 0134 0135 QStyle *style = QApplication::style(); 0136 style->drawPrimitive(QStyle::PE_Widget, &option, painter, nullptr); 0137 0138 const KAboutApplicationPersonModel *model = qobject_cast<const KAboutApplicationPersonModel *>(index.model()); 0139 0140 if (model->showRemoteAvatars() && model->hasAvatarPixmaps()) { 0141 int height = qMax(widgetsRect(option, index).height(), AVATAR_HEIGHT + 2 * margin); 0142 QPoint point(option.rect.left() + 2 * margin, // 0143 option.rect.top() + ((height - AVATAR_HEIGHT) / 2)); 0144 0145 KAboutApplicationPersonProfile profile = index.data().value<KAboutApplicationPersonProfile>(); 0146 0147 QPixmap fallback; 0148 if (profile.avatar().isNull()) { 0149 fallback = avatarFallback(); 0150 fallback.setDevicePixelRatio(itemView()->devicePixelRatio()); 0151 } 0152 const QPixmap &pixmap = profile.avatar().isNull() ? fallback : profile.avatar(); 0153 point.setX((AVATAR_WIDTH - pixmap.width()) / 2 + 5); 0154 point.setY(option.rect.top() + ((height - pixmap.height()) / 2)); 0155 painter->drawPixmap(point, pixmap); 0156 0157 QPoint framePoint(point.x() - 5, point.y() - 5); 0158 QPixmap framePixmap(QStringLiteral(":/kxmlgui5/thumb_frame.png")); 0159 painter->drawPixmap(framePoint, framePixmap.scaled(pixmap.width() + 10, pixmap.height() + 10)); 0160 } 0161 } 0162 0163 void KAboutApplicationPersonListDelegate::launchUrl(QAction *action) const 0164 { 0165 QString url = action->data().toString(); 0166 if (!url.isEmpty()) { 0167 QDesktopServices::openUrl(QUrl(url)); 0168 } 0169 } 0170 0171 int KAboutApplicationPersonListDelegate::heightForString(const QString &string, int lineWidth, const QStyleOptionViewItem &option) const 0172 { 0173 QFontMetrics fm = option.fontMetrics; 0174 constexpr auto opts = Qt::AlignLeft | Qt::AlignBottom | Qt::TextWordWrap; 0175 QRect boundingRect = fm.boundingRect(0, 0, lineWidth, 9999, opts, string); 0176 return boundingRect.height(); 0177 } 0178 0179 QString KAboutApplicationPersonListDelegate::buildTextForProfile(const KAboutApplicationPersonProfile &profile) const 0180 { 0181 QString text = QLatin1String("<b>") + i18nc("@item Contributor name in about dialog.", "%1", profile.name()) + QLatin1String("</b>"); 0182 0183 if (!profile.task().isEmpty()) { 0184 text += QLatin1String("\n<br><i>%1</i>").arg(profile.task()); 0185 } 0186 0187 if (!profile.location().isEmpty()) { 0188 text += QLatin1String("\n<br>") + profile.location(); 0189 } 0190 return text; 0191 } 0192 0193 QRect KAboutApplicationPersonListDelegate::widgetsRect(const QStyleOptionViewItem &option, const QPersistentModelIndex &index) const 0194 { 0195 KAboutApplicationPersonProfile profile = index.data().value<KAboutApplicationPersonProfile>(); 0196 int margin = option.fontMetrics.height() / 2; 0197 0198 QRect widgetsRect; 0199 if (qobject_cast<const KAboutApplicationPersonModel *>(index.model())->showRemoteAvatars()) { 0200 widgetsRect = QRect(option.rect.left() + AVATAR_WIDTH + 3 * margin, // 0201 margin / 2, // 0202 option.rect.width() - AVATAR_WIDTH - 4 * margin, // 0203 0); 0204 } else { 0205 widgetsRect = QRect(option.rect.left() + margin, // 0206 margin / 2, // 0207 option.rect.width() - 2 * margin, // 0208 0); 0209 } 0210 0211 int textHeight = heightForString(buildTextForProfile(profile), widgetsRect.width() - margin, option); 0212 widgetsRect.setHeight(textHeight + MAIN_LINKS_HEIGHT + 1.5 * margin); 0213 0214 return widgetsRect; 0215 } 0216 0217 } // namespace KDEPrivate 0218 0219 #include "moc_kaboutapplicationpersonlistdelegate_p.cpp"