File indexing completed on 2025-01-26 04:15:00

0001 /*
0002  * Copyright (C) 2020 Dan Leinir Turthra Jensen <admin@leinir.dk>
0003  *
0004  * This library is free software; you can redistribute it and/or
0005  * modify it under the terms of the GNU Lesser General Public
0006  * License as published by the Free Software Foundation; either
0007  * version 2.1 of the License, or (at your option) any later version.
0008  *
0009  * This library is distributed in the hope that it will be useful,
0010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
0012  * Lesser General Public License for more details.
0013  *
0014  * You should have received a copy of the GNU Lesser General Public
0015  * License along with this library; if not, write to the Free Software
0016  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
0017  *
0018  */
0019 
0020 #include "TextDocumentEditor.h"
0021 
0022 #include <QQuickTextDocument>
0023 #include <QTextBlock>
0024 
0025 class TextDocumentEditor::Private {
0026 public:
0027     Private() {}
0028     QQuickTextDocument* textDocument{nullptr};
0029 };
0030 
0031 TextDocumentEditor::TextDocumentEditor(QObject* parent)
0032     : QObject(parent)
0033     , d(new Private)
0034 {
0035 }
0036 
0037 TextDocumentEditor::~TextDocumentEditor() = default;
0038 
0039 QObject * TextDocumentEditor::textDocument() const
0040 {
0041     return d->textDocument;
0042 }
0043 
0044 void TextDocumentEditor::setTextDocument(QObject* textDocument)
0045 {
0046     if (d->textDocument != textDocument) {
0047         d->textDocument = qobject_cast<QQuickTextDocument*>(textDocument);
0048         Q_EMIT textDocumentChanged();
0049     }
0050 }
0051 
0052 QPoint TextDocumentEditor::linkStartEnd(int cursorPosition)
0053 {
0054     QPoint point;
0055     if (d->textDocument) {
0056         QTextBlock block = d->textDocument->textDocument()->findBlock(cursorPosition);
0057         while (block.isValid() && block.contains(cursorPosition)) {
0058             // Don't be so heavy on large documents...
0059             qApp->processEvents();
0060             QTextBlock::iterator it;
0061             for (it = block.begin(); !(it.atEnd()); ++it) {
0062                 QTextFragment currentFragment = it.fragment();
0063                 if (currentFragment.isValid() && currentFragment.contains(cursorPosition)) {
0064                     point.setX(currentFragment.position());
0065                     point.setY(currentFragment.position() + currentFragment.length());
0066                     break;
0067                 }
0068             }
0069             block = block.next();
0070         }
0071     }
0072     return point;
0073 }
0074 
0075 QString TextDocumentEditor::linkText(int cursorPosition)
0076 {
0077     QString text;
0078     if (d->textDocument) {
0079         QTextBlock block = d->textDocument->textDocument()->findBlock(cursorPosition);
0080         while (block.isValid() && block.contains(cursorPosition)) {
0081             // Don't be so heavy on large documents...
0082             qApp->processEvents();
0083             QTextBlock::iterator it;
0084             for (it = block.begin(); !(it.atEnd()); ++it) {
0085                 QTextFragment currentFragment = it.fragment();
0086                 if (currentFragment.isValid() && currentFragment.contains(cursorPosition)) {
0087                     text = currentFragment.text();
0088                     break;
0089                 }
0090             }
0091             if (!text.isEmpty()) {
0092                 break;
0093             }
0094             block = block.next();
0095         }
0096     }
0097     return text;
0098 }
0099 
0100 QString TextDocumentEditor::linkHref(int cursorPosition)
0101 {
0102     QString text;
0103     if (d->textDocument) {
0104         QTextBlock block = d->textDocument->textDocument()->findBlock(cursorPosition);
0105         while (block.isValid() && block.contains(cursorPosition)) {
0106             // Don't be so heavy on large documents...
0107             qApp->processEvents();
0108             QTextBlock::iterator it;
0109             for (it = block.begin(); !(it.atEnd()); ++it) {
0110                 QTextFragment currentFragment = it.fragment();
0111                 if (currentFragment.isValid() && currentFragment.contains(cursorPosition)) {
0112                     text = currentFragment.charFormat().anchorHref();
0113                     break;
0114                 }
0115             }
0116             if (!text.isEmpty()) {
0117                 break;
0118             }
0119             block = block.next();
0120         }
0121     }
0122     return text;
0123 }
0124 
0125 QStringList TextDocumentEditor::paragraphs() const
0126 {
0127     QStringList paragraphs;
0128     if (d->textDocument) {
0129         QTextBlock block = d->textDocument->textDocument()->firstBlock();
0130         while (block.isValid()) {
0131             QString paragraph;
0132             QStringList tagStack;
0133             QTextBlock::iterator it;
0134             for (it = block.begin(); !(it.atEnd()); ++it) {
0135                 QTextFragment fragment = it.fragment();
0136                 if (fragment.isValid()) {
0137                     QTextCharFormat format = fragment.charFormat();
0138                     if (fragment.isValid()) {
0139                         QStringList endStack;
0140                         // First set all the tags that ACBF supports, and add the tags we need to close to our tag stack
0141                         // <strong>...</strong>
0142                         if (format.fontWeight() == QFont::Bold) {
0143                             paragraph += "<strong>";
0144                             endStack << "</strong>";
0145                         }
0146                         // <emphasis>...</emphasis>
0147                         if (format.fontItalic()) {
0148                             paragraph += "<emphasis>";
0149                             endStack << "</emphasis>";
0150                         }
0151                         // <strikethrough>...</strikethrough>
0152                         if (format.fontStrikeOut()) {
0153                             paragraph += "<strikethrough>";
0154                             endStack << "</strikethrough>";
0155                         }
0156                         // <sub>...</sub>
0157                         if (format.verticalAlignment() == QTextCharFormat::AlignSubScript) {
0158                             paragraph += "<sub>";
0159                             endStack << "</sub>";
0160                         }
0161                         // <sup>...</sup>
0162                         if (format.verticalAlignment() == QTextCharFormat::AlignSuperScript) {
0163                             paragraph += "<sup>";
0164                             endStack << "</sup>";
0165                         }
0166                         // <a href="">...</a>
0167                         if (format.isAnchor()) {
0168                             paragraph += QString("<a href=\"%1\">").arg(format.anchorHref().toHtmlEscaped());
0169                             endStack << "</a>";
0170                         }
0171 
0172                         // Add in the fragment's text contents
0173                         paragraph += fragment.text().toHtmlEscaped();
0174 
0175                         // Finally, close our own tags again, from the inside
0176                         while (endStack.count() > 0) {
0177                             paragraph += endStack.takeLast();
0178                         }
0179                     }
0180                 }
0181             }
0182             paragraphs << paragraph;
0183             block = block.next();
0184         }
0185     }
0186     return paragraphs;
0187 }