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"