1/*
2 This file is part of KDE.
3
4 Copyright (c) 2009 Thomas McGuire <mcguire@kde.org>
5 Copyright (c) 2010 Stephen Kelly <steveire@gmail.com>
6
7 This library is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Library General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or (at your
10 option) any later version.
11
12 This library is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
15 License for more details.
16
17 You should have received a copy of the GNU Library General Public License
18 along with this library; see the file COPYING.LIB. If not, write to the
19 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
20 02110-1301, USA.
21*/
22
23#include "textutils.h"
24
25#include <QTextBlock>
26#include <QTextCharFormat>
27#include <QTextDocument>
28#include <KDE/KDebug>
29
30using namespace KPIMTextEdit;
31
32static bool isCharFormatFormatted( const QTextCharFormat &format, const QFont &defaultFont,
33 const QTextCharFormat &defaultBlockFormat )
34{
35 if ( !format.anchorHref().isEmpty() ||
36 format.font() != defaultFont ||
37 format.isAnchor() ||
38 format.verticalAlignment() != defaultBlockFormat.verticalAlignment() ||
39 format.layoutDirection() != defaultBlockFormat.layoutDirection() ||
40 format.underlineStyle() != defaultBlockFormat.underlineStyle() ||
41 format.foreground().color() != defaultBlockFormat.foreground().color() ||
42 format.background().color() != defaultBlockFormat.background().color() ) {
43 return true;
44 }
45
46 return false;
47}
48
49static bool isBlockFormatFormatted( const QTextBlockFormat &format,
50 const QTextBlockFormat &defaultFormat )
51{
52 if ( format.alignment() != defaultFormat.alignment() ||
53 format.layoutDirection() != defaultFormat.layoutDirection() ||
54 format.indent() != defaultFormat.indent() ||
55 format.textIndent() != defaultFormat.textIndent() ) {
56 return true;
57 }
58
59 return false;
60}
61
62/// @return true if the format represents a list, table, image or something like that.
63static bool isSpecial( const QTextFormat &charFormat )
64{
65 return charFormat.isFrameFormat() || charFormat.isImageFormat() ||
66 charFormat.isListFormat() || charFormat.isTableFormat() || charFormat.isTableCellFormat();
67}
68
69bool TextUtils::containsFormatting( const QTextDocument *document )
70{
71 if ( !document ) {
72 return false;
73 }
74
75 QTextDocument defaultTextDocument;
76 const QTextCharFormat defaultCharFormat = defaultTextDocument.begin().charFormat();
77 const QTextBlockFormat defaultBlockFormat = defaultTextDocument.begin().blockFormat();
78 const QFont defaultFont = defaultTextDocument.defaultFont();
79
80 QTextBlock block = document->firstBlock();
81 while ( block.isValid() ) {
82
83 if ( isBlockFormatFormatted( block.blockFormat(), defaultBlockFormat ) ) {
84 return true;
85 }
86
87 if ( isSpecial( block.charFormat() ) || isSpecial( block.blockFormat() ) ||
88 block.textList() ) {
89 return true;
90 }
91
92 QTextBlock::iterator it = block.begin();
93 while ( !it.atEnd() ) {
94 const QTextFragment fragment = it.fragment();
95 const QTextCharFormat charFormat = fragment.charFormat();
96 if ( isSpecial( charFormat ) ) {
97 return true;
98 }
99 if ( isCharFormatFormatted( fragment.charFormat(), defaultFont, defaultCharFormat ) ) {
100 return true;
101 }
102
103 it++;
104 }
105
106 block = block.next();
107 }
108
109 if ( document->toHtml().contains( QLatin1String( "<hr />" ) ) ) {
110 return true;
111 }
112
113 return false;
114}
115
116QString TextUtils::flowText( QString &wrappedText, const QString &indent, int maxLength )
117{
118 if ( wrappedText.isEmpty() ) {
119 return indent;
120 }
121
122 if ( maxLength <= indent.length() ) {
123 kWarning() << "indent was set to a string that is longer or the same length "
124 << "as maxLength, setting maxLength to indent.length() + 1";
125 maxLength = indent.length() + 1;
126 }
127
128 maxLength -= indent.length(); // take into account indent
129 QString result;
130 while ( !wrappedText.isEmpty() ) {
131 // first check for the next newline. if it's before maxLength, break there, and continue
132 int newLine = wrappedText.indexOf( QLatin1Char( '\n' ) );
133 if ( newLine > 0 && newLine <= maxLength ) {
134 result += indent + wrappedText.left( newLine + 1 );
135 wrappedText = wrappedText.mid( newLine + 1 );
136 continue;
137 }
138 // Find the next point in the wrappedText where we have to do a line break.
139 // Start searching at maxLength position and then walk backwards looking
140 // for a space.
141 int breakPosition;
142 if ( wrappedText.length() > maxLength ) {
143 breakPosition = maxLength;
144 while ( ( breakPosition >= 0 ) && ( wrappedText[breakPosition] != QLatin1Char( ' ' ) ) ) {
145 breakPosition--;
146 }
147 if ( breakPosition <= 0 ) {
148 // Couldn't break before maxLength.
149 breakPosition = maxLength;
150 }
151 } else {
152 breakPosition = wrappedText.length();
153 }
154
155 QString line = wrappedText.left( breakPosition );
156 if ( breakPosition < wrappedText.length() ) {
157 wrappedText = wrappedText.mid( breakPosition );
158 } else {
159 wrappedText.clear();
160 }
161
162 // Strip leading whitespace of new lines, since that looks strange
163 if ( !result.isEmpty() && line.startsWith( QLatin1Char( ' ' ) ) ) {
164 line = line.mid( 1 );
165 }
166
167 result += indent + line + QLatin1Char( '\n' );
168 }
169
170 return result.left( result.length() - 1 );
171}
172