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"