File indexing completed on 2024-06-16 04:56:14
0001 /* 0002 view/anchorcache.cpp 0003 0004 This file is part of Kleopatra, the KDE keymanager 0005 SPDX-FileCopyrightText: 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 "anchorcache_p.h" 0014 0015 #include <QTextBlock> 0016 #include <QTextCursor> 0017 #include <QTextDocument> 0018 0019 void AnchorCache::setText(const QString &text) 0020 { 0021 mText = text; 0022 mAnchorsValid = false; 0023 } 0024 0025 void AnchorCache::clear() 0026 { 0027 mText.clear(); 0028 mAnchorsValid = false; 0029 } 0030 0031 int AnchorCache::size() const 0032 { 0033 return anchors().size(); 0034 } 0035 0036 const AnchorData &AnchorCache::operator[](int index) const 0037 { 0038 return anchors()[index]; 0039 } 0040 0041 int AnchorCache::findAnchor(int start) const 0042 { 0043 anchors(); // ensure that the anchor cache is valid 0044 auto it = std::find_if(std::cbegin(mAnchors), std::cend(mAnchors), [start](const auto &anchor) { 0045 return anchor.start == start; 0046 }); 0047 if (it != std::cend(mAnchors)) { 0048 return std::distance(std::cbegin(mAnchors), it); 0049 } 0050 return -1; 0051 } 0052 0053 const std::vector<AnchorData> &AnchorCache::anchors() const 0054 { 0055 if (mAnchorsValid) { 0056 return mAnchors; 0057 } 0058 0059 mAnchors.clear(); 0060 if (mText.isEmpty()) { 0061 return mAnchors; 0062 } 0063 0064 QTextDocument doc; 0065 doc.setHtml(mText); 0066 0067 // taken from QWidgetTextControl::setFocusToNextOrPreviousAnchor and QWidgetTextControl::findNextPrevAnchor 0068 for (QTextBlock block = doc.begin(); block.isValid(); block = block.next()) { 0069 QTextBlock::Iterator it = block.begin(); 0070 0071 while (!it.atEnd()) { 0072 const QTextFragment fragment = it.fragment(); 0073 const QTextCharFormat fmt = fragment.charFormat(); 0074 0075 if (fmt.isAnchor() && fmt.hasProperty(QTextFormat::AnchorHref)) { 0076 const int anchorStart = fragment.position(); 0077 const QString anchorHref = fmt.anchorHref(); 0078 int anchorEnd = -1; 0079 0080 // find next non-anchor fragment 0081 for (; !it.atEnd(); ++it) { 0082 const QTextFragment fragment = it.fragment(); 0083 const QTextCharFormat fmt = fragment.charFormat(); 0084 0085 if (!fmt.isAnchor() || fmt.anchorHref() != anchorHref) { 0086 anchorEnd = fragment.position(); 0087 break; 0088 } 0089 } 0090 0091 if (anchorEnd == -1) { 0092 anchorEnd = block.position() + block.length() - 1; 0093 } 0094 0095 QTextCursor cursor{&doc}; 0096 cursor.setPosition(anchorStart); 0097 cursor.setPosition(anchorEnd, QTextCursor::KeepAnchor); 0098 QString anchorText = cursor.selectedText(); 0099 mAnchors.push_back({anchorStart, anchorEnd, anchorText, anchorHref}); 0100 } else { 0101 ++it; 0102 } 0103 } 0104 } 0105 0106 mAnchorsValid = true; 0107 return mAnchors; 0108 }