File indexing completed on 2024-04-21 15:05:45

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"