File indexing completed on 2024-05-05 05:21:42

0001 /*
0002     This file is part of KDE.
0003 
0004     SPDX-FileCopyrightText: 2009 Thomas McGuire <mcguire@kde.org>
0005     SPDX-FileCopyrightText: 2010 Stephen Kelly <steveire@gmail.com>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-or-later
0008 */
0009 
0010 #include "textutils.h"
0011 #include "kpimtextedit_debug.h"
0012 #include <QTextBlock>
0013 #include <QTextCharFormat>
0014 #include <QTextDocument>
0015 
0016 using namespace KPIMTextEdit;
0017 
0018 static bool isCharFormatFormatted(const QTextCharFormat &format, const QFont &defaultFont, const QTextCharFormat &defaultBlockFormat)
0019 {
0020     if (!format.anchorHref().isEmpty() || format.font() != defaultFont || format.isAnchor()
0021         || format.verticalAlignment() != defaultBlockFormat.verticalAlignment() || format.layoutDirection() != defaultBlockFormat.layoutDirection()
0022         || format.underlineStyle() != defaultBlockFormat.underlineStyle() || format.foreground().color() != defaultBlockFormat.foreground().color()
0023         || format.background().color() != defaultBlockFormat.background().color()) {
0024         return true;
0025     }
0026 
0027     return false;
0028 }
0029 
0030 static bool isBlockFormatFormatted(const QTextBlockFormat &format, const QTextBlockFormat &defaultFormat)
0031 {
0032     if (format.alignment() != defaultFormat.alignment() || format.layoutDirection() != defaultFormat.layoutDirection()
0033         || format.indent() != defaultFormat.indent() || format.textIndent() != defaultFormat.textIndent()) {
0034         return true;
0035     }
0036 
0037     return false;
0038 }
0039 
0040 /// @return true if the format represents a list, table, image or something like that.
0041 static bool isSpecial(const QTextFormat &charFormat)
0042 {
0043     return charFormat.isFrameFormat() || charFormat.isImageFormat() || charFormat.isListFormat() || charFormat.isTableFormat()
0044         || charFormat.isTableCellFormat();
0045 }
0046 
0047 bool TextUtils::containsFormatting(const QTextDocument *document)
0048 {
0049     if (!document) {
0050         return false;
0051     }
0052 
0053     QTextDocument defaultTextDocument;
0054     const QTextCharFormat defaultCharFormat = defaultTextDocument.begin().charFormat();
0055     const QTextBlockFormat defaultBlockFormat = defaultTextDocument.begin().blockFormat();
0056     const QFont defaultFont = defaultTextDocument.defaultFont();
0057 
0058     QTextBlock block = document->firstBlock();
0059     while (block.isValid()) {
0060         if (isBlockFormatFormatted(block.blockFormat(), defaultBlockFormat)) {
0061             return true;
0062         }
0063 
0064         if (isSpecial(block.charFormat()) || isSpecial(block.blockFormat()) || block.textList()) {
0065             return true;
0066         }
0067 
0068         QTextBlock::iterator it = block.begin();
0069         while (!it.atEnd()) {
0070             const QTextFragment fragment = it.fragment();
0071             const QTextCharFormat charFormat = fragment.charFormat();
0072             if (isSpecial(charFormat)) {
0073                 return true;
0074             }
0075             if (isCharFormatFormatted(fragment.charFormat(), defaultFont, defaultCharFormat)) {
0076                 return true;
0077             }
0078 
0079             ++it;
0080         }
0081 
0082         block = block.next();
0083     }
0084 
0085     if (document->toHtml().contains(QLatin1StringView("<hr />"))) {
0086         return true;
0087     }
0088 
0089     return false;
0090 }
0091 
0092 QString TextUtils::flowText(QString &wrappedText, const QString &indent, int maxLength)
0093 {
0094     if (wrappedText.isEmpty()) {
0095         return indent;
0096     }
0097 
0098     if (maxLength <= indent.length()) {
0099         qCWarning(KPIMTEXTEDIT_LOG) << "indent was set to a string that is longer or the same length "
0100                                     << "as maxLength, setting maxLength to indent.length() + 1";
0101         maxLength = indent.length() + 1;
0102     }
0103 
0104     maxLength -= indent.length(); // take into account indent
0105     QString result;
0106     while (!wrappedText.isEmpty()) {
0107         // first check for the next newline. if it's before maxLength, break there, and continue
0108         int newLine = wrappedText.indexOf(QLatin1Char('\n'));
0109         if (newLine > 0 && newLine <= maxLength) {
0110             result += indent + wrappedText.left(newLine + 1);
0111             wrappedText = wrappedText.mid(newLine + 1);
0112             continue;
0113         }
0114         // Find the next point in the wrappedText where we have to do a line break.
0115         // Start searching at maxLength position and then walk backwards looking
0116         // for a space.
0117         int breakPosition;
0118         if (wrappedText.length() > maxLength) {
0119             breakPosition = maxLength;
0120             while ((breakPosition >= 0) && (wrappedText[breakPosition] != QLatin1Char(' '))) {
0121                 breakPosition--;
0122             }
0123             if (breakPosition <= 0) {
0124                 // Couldn't break before maxLength.
0125                 breakPosition = maxLength;
0126             }
0127         } else {
0128             breakPosition = wrappedText.length();
0129         }
0130 
0131         QString line = wrappedText.left(breakPosition);
0132         if (breakPosition < wrappedText.length()) {
0133             wrappedText = wrappedText.mid(breakPosition);
0134         } else {
0135             wrappedText.clear();
0136         }
0137 
0138         // Strip leading whitespace of new lines, since that looks strange
0139         if (!result.isEmpty() && line.startsWith(QLatin1Char(' '))) {
0140             line.remove(0, 1);
0141         }
0142 
0143         result += indent + line + QLatin1Char('\n');
0144     }
0145 
0146     return result.left(result.length() - 1);
0147 }