File indexing completed on 2024-06-16 04:56:14

0001 /*
0002     view/htmllabel.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2021, 2022 g10 Code GmbH
0006     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include <config-kleopatra.h>
0012 
0013 #include "htmllabel.h"
0014 
0015 #include "anchorcache_p.h"
0016 
0017 #include <QAccessible>
0018 #include <QDesktopServices>
0019 
0020 using namespace Kleo;
0021 
0022 class HtmlLabel::Private
0023 {
0024     HtmlLabel *q;
0025 
0026 public:
0027     Private(HtmlLabel *qq)
0028         : q{qq}
0029     {
0030     }
0031 
0032     void updateText(const QString &newText = {});
0033 
0034     AnchorCache mAnchorCache;
0035     QColor linkColor;
0036 };
0037 
0038 void HtmlLabel::Private::updateText(const QString &newText)
0039 {
0040     static const QString styleTemplate{QLatin1StringView{"<style type=\"text/css\">a {color: %1;}</style>"}};
0041 
0042     if (newText.isEmpty() && q->text().isEmpty()) {
0043         return;
0044     }
0045 
0046     const auto styleTag = styleTemplate.arg(linkColor.isValid() ? linkColor.name() : q->palette().link().color().name());
0047     if (newText.isEmpty()) {
0048         q->setText(styleTag + q->text().mid(styleTag.size()));
0049     } else {
0050         q->setText(styleTag + newText);
0051     }
0052     mAnchorCache.setText(q->text());
0053 }
0054 
0055 HtmlLabel::HtmlLabel(QWidget *parent)
0056     : HtmlLabel{{}, parent}
0057 {
0058 }
0059 
0060 HtmlLabel::HtmlLabel(const QString &html, QWidget *parent)
0061     : QLabel{parent}
0062     , d{new Private{this}}
0063 {
0064     setTextFormat(Qt::RichText);
0065     setTextInteractionFlags(Qt::TextBrowserInteraction);
0066     setHtml(html);
0067 }
0068 
0069 HtmlLabel::~HtmlLabel() = default;
0070 
0071 void HtmlLabel::setHtml(const QString &html)
0072 {
0073     if (html.isEmpty()) {
0074         clear();
0075         d->mAnchorCache.clear();
0076         return;
0077     }
0078     d->updateText(html);
0079 }
0080 
0081 void HtmlLabel::setLinkColor(const QColor &color)
0082 {
0083     d->linkColor = color;
0084     d->updateText();
0085 }
0086 
0087 int HtmlLabel::numberOfAnchors() const
0088 {
0089     return d->mAnchorCache.size();
0090 }
0091 
0092 QString HtmlLabel::anchorText(int index) const
0093 {
0094     if (index >= 0 && index < d->mAnchorCache.size()) {
0095         return d->mAnchorCache[index].text;
0096     }
0097     return {};
0098 }
0099 
0100 QString HtmlLabel::anchorHref(int index) const
0101 {
0102     if (index >= 0 && index < d->mAnchorCache.size()) {
0103         return d->mAnchorCache[index].href;
0104     }
0105     return {};
0106 }
0107 
0108 void HtmlLabel::activateAnchor(int index)
0109 {
0110     // based on QWidgetTextControlPrivate::activateLinkUnderCursor
0111     if (index < 0 || index >= d->mAnchorCache.size()) {
0112         return;
0113     }
0114     const auto &anchor = d->mAnchorCache[index];
0115     if (anchor.href.isEmpty()) {
0116         return;
0117     }
0118     if (hasFocus()) {
0119         // move cursor just before the anchor and clear the selection
0120         setSelection(anchor.start, 0);
0121         // focus the anchor
0122         focusNextPrevChild(true);
0123     } else {
0124         // clear the selection moving the cursor just after the anchor
0125         setSelection(anchor.end, 0);
0126     }
0127     if (openExternalLinks()) {
0128         QDesktopServices::openUrl(QUrl{anchor.href});
0129     } else {
0130         Q_EMIT linkActivated(anchor.href);
0131     }
0132 }
0133 
0134 int HtmlLabel::selectedAnchor() const
0135 {
0136     return d->mAnchorCache.findAnchor(selectionStart());
0137 }
0138 
0139 bool HtmlLabel::focusNextPrevChild(bool next)
0140 {
0141     const bool result = QLabel::focusNextPrevChild(next);
0142     if (hasFocus() && QAccessible::isActive()) {
0143         const int anchorIndex = selectedAnchor();
0144         if (anchorIndex >= 0) {
0145             QAccessibleEvent focusEvent(this, QAccessible::Focus);
0146             focusEvent.setChild(anchorIndex);
0147             QAccessible::updateAccessibility(&focusEvent);
0148         }
0149     }
0150     return result;
0151 }
0152 
0153 #include "moc_htmllabel.cpp"