• Skip to content
  • Skip to link menu
  • KDE API Reference
  • kdelibs-4.14.38 API Reference
  • KDE Home
  • Contact Us
 

KDEUI

  • kdeui
  • util
kwordwrap.cpp
Go to the documentation of this file.
1/* This file is part of the KDE libraries
2 Copyright (C) 2001 David Faure <faure@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License version 2 as published by the Free Software Foundation.
7
8 This library is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 Library General Public License for more details.
12
13 You should have received a copy of the GNU Library General Public License
14 along with this library; see the file COPYING.LIB. If not, write to
15 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16 Boston, MA 02110-1301, USA.
17*/
18
19#include "kwordwrap.h"
20#include <kdebug.h>
21
22#include <QtGui/QPainter>
23#include <QtCore/QMutableVectorIterator>
24
25class KWordWrapPrivate {
26public:
27 QRect m_constrainingRect;
28 QVector<int> m_breakPositions;
29 QVector<int> m_lineWidths;
30 QRect m_boundingRect;
31 QString m_text;
32};
33
34KWordWrap::KWordWrap(const QRect & r)
35: d(new KWordWrapPrivate)
36{
37 d->m_constrainingRect = r;
38}
39
40KWordWrap* KWordWrap::formatText( QFontMetrics &fm, const QRect & r, int /*flags*/, const QString & str, int len )
41{
42 KWordWrap* kw = new KWordWrap( r );
43 // The wordwrap algorithm
44 // The variable names and the global shape of the algorithm are inspired
45 // from QTextFormatterBreakWords::format().
46 //kDebug() << "KWordWrap::formatText " << str << " r=" << r.x() << "," << r.y() << " " << r.width() << "x" << r.height();
47 int height = fm.height();
48 if ( len == -1 )
49 kw->d->m_text = str;
50 else
51 kw->d->m_text = str.left( len );
52 if ( len == -1 )
53 len = str.length();
54 int lastBreak = -1;
55 int lineWidth = 0;
56 int x = 0;
57 int y = 0;
58 int w = r.width();
59 int textwidth = 0;
60 bool isBreakable = false;
61 bool wasBreakable = false; // value of isBreakable for last char (i-1)
62 bool isParens = false; // true if one of ({[
63 bool wasParens = false; // value of isParens for last char (i-1)
64 QString inputString = str;
65
66 for ( int i = 0 ; i < len; ++i )
67 {
68 const QChar c = inputString.at(i);
69 const int ww = fm.charWidth(inputString, i);
70
71 isParens = ( c == QLatin1Char('(') || c == QLatin1Char('[')
72 || c == QLatin1Char('{') );
73 // isBreakable is true when we can break _after_ this character.
74 isBreakable = ( c.isSpace() || c.isPunct() || c.isSymbol() ) & !isParens;
75
76 // Special case for '(', '[' and '{': we want to break before them
77 if ( !isBreakable && i < len-1 ) {
78 const QChar nextc = inputString.at(i + 1); // look at next char
79 isBreakable = ( nextc == QLatin1Char('(')
80 || nextc == QLatin1Char('[')
81 || nextc == QLatin1Char('{') );
82 }
83 // Special case for '/': after normal chars it's breakable (e.g. inside a path),
84 // but after another breakable char it's not (e.g. "mounted at /foo")
85 // Same thing after a parenthesis (e.g. "dfaure [/fool]")
86 if ( c == QLatin1Char('/') && (wasBreakable || wasParens) )
87 isBreakable = false;
88
89 /*kDebug() << "c='" << QString(c) << "' i=" << i << "/" << len
90 << " x=" << x << " ww=" << ww << " w=" << w
91 << " lastBreak=" << lastBreak << " isBreakable=" << isBreakable << endl;*/
92 int breakAt = -1;
93 if ( x + ww > w && lastBreak != -1 ) // time to break and we know where
94 breakAt = lastBreak;
95 if ( x + ww > w - 4 && lastBreak == -1 ) // time to break but found nowhere [-> break here]
96 breakAt = i;
97 if (i == len - 2 && x + ww + fm.charWidth(inputString, i+1) > w) // don't leave the last char alone
98 breakAt = lastBreak == -1 ? i - 1 : lastBreak;
99 if ( c == QLatin1Char('\n') ) // Forced break here
100 {
101 if ( breakAt == -1 && lastBreak != -1) // only break if not already breaking
102 {
103 breakAt = i - 1;
104 lastBreak = -1;
105 }
106 // remove the line feed from the string
107 kw->d->m_text.remove(i, 1);
108 inputString.remove(i, 1);
109 len--;
110 }
111 if ( breakAt != -1 )
112 {
113 //kDebug() << "KWordWrap::formatText breaking after " << breakAt;
114 kw->d->m_breakPositions.append( breakAt );
115 int thisLineWidth = lastBreak == -1 ? x + ww : lineWidth;
116 kw->d->m_lineWidths.append( thisLineWidth );
117 textwidth = qMax( textwidth, thisLineWidth );
118 x = 0;
119 y += height;
120 wasBreakable = true;
121 wasParens = false;
122 if ( lastBreak != -1 )
123 {
124 // Breakable char was found, restart from there
125 i = lastBreak;
126 lastBreak = -1;
127 continue;
128 }
129 } else if ( isBreakable )
130 {
131 lastBreak = i;
132 lineWidth = x + ww;
133 }
134 x += ww;
135 wasBreakable = isBreakable;
136 wasParens = isParens;
137 }
138 textwidth = qMax( textwidth, x );
139 kw->d->m_lineWidths.append( x );
140 y += height;
141 //kDebug() << "KWordWrap::formatText boundingRect:" << r.x() << "," << r.y() << " " << textwidth << "x" << y;
142 if ( r.height() >= 0 && y > r.height() )
143 textwidth = r.width();
144 int realY = y;
145 if ( r.height() >= 0 )
146 {
147 while ( realY > r.height() )
148 realY -= height;
149 realY = qMax( realY, 0 );
150 }
151 kw->d->m_boundingRect.setRect( 0, 0, textwidth, realY );
152 return kw;
153}
154
155KWordWrap::~KWordWrap() {
156 delete d;
157}
158
159QString KWordWrap::wrappedString() const
160{
161 // We use the calculated break positions to insert '\n' into the string
162 QString ws;
163 int start = 0;
164 for (int i = 0; i < d->m_breakPositions.count(); ++i) {
165 int end = d->m_breakPositions.at(i);
166 ws += d->m_text.mid( start, end - start + 1 );
167 ws += QLatin1Char('\n');
168 start = end + 1;
169 }
170 ws += d->m_text.mid( start );
171 return ws;
172}
173
174QString KWordWrap::truncatedString( bool dots ) const
175{
176 if ( d->m_breakPositions.isEmpty() )
177 return d->m_text;
178
179 QString ts = d->m_text.left( d->m_breakPositions.first() + 1 );
180 if ( dots )
181 ts += QLatin1String("...");
182 return ts;
183}
184
185static QColor mixColors(double p1, QColor c1, QColor c2) {
186 return QColor(int(c1.red() * p1 + c2.red() * (1.0-p1)),
187 int(c1.green() * p1 + c2.green() * (1.0-p1)),
188 int(c1.blue() * p1 + c2.blue() * (1.0-p1)));
189}
190
191void KWordWrap::drawFadeoutText(QPainter *p, int x, int y, int maxW,
192 const QString &t) {
193 QFontMetrics fm = p->fontMetrics();
194 QColor bgColor = p->background().color();
195 QColor textColor = p->pen().color();
196
197 if ( ( fm.boundingRect( t ).width() > maxW ) && ( t.length() > 1 ) ) {
198 int tl = 0;
199 int w = 0;
200 while ( tl < t.length() ) {
201 w += fm.charWidth( t, tl );
202 if ( w >= maxW )
203 break;
204 tl++;
205 }
206
207 int n = qMin( tl, 3);
208 if ( t.isRightToLeft() ) {
209 x += maxW; // start from the right side for RTL string
210 if (tl > 3) {
211 x -= fm.width( t.left( tl - 3 ) );
212 p->drawText( x, y, t.left( tl - 3 ) );
213 }
214 for (int i = 0; i < n; i++) {
215 p->setPen( mixColors( 0.70 - i * 0.25, textColor, bgColor ) );
216 QString s( t.at( tl - n + i ) );
217 x -= fm.width( s );
218 p->drawText( x, y, s );
219 }
220 }
221 else {
222 if (tl > 3) {
223 p->drawText( x, y, t.left( tl - 3 ) );
224 x += fm.width( t.left( tl - 3 ) );
225 }
226 for (int i = 0; i < n; i++) {
227 p->setPen( mixColors( 0.70 - i * 0.25, textColor, bgColor ) );
228 QString s( t.at( tl - n + i ) );
229 p->drawText( x, y, s );
230 x += fm.width( s );
231 }
232 }
233 }
234 else
235 p->drawText( x, y, t );
236}
237
238void KWordWrap::drawTruncateText(QPainter *p, int x, int y, int maxW,
239 const QString &t) {
240 QString tmpText = p->fontMetrics().elidedText(t, Qt::ElideRight, maxW);
241 p->drawText( x, y, tmpText );
242}
243
244void KWordWrap::drawText( QPainter *painter, int textX, int textY, int flags ) const
245{
246 //kDebug() << "KWordWrap::drawText text=" << wrappedString() << " x=" << textX << " y=" << textY;
247 // We use the calculated break positions to draw the text line by line using QPainter
248 int start = 0;
249 int y = 0;
250 QFontMetrics fm = painter->fontMetrics();
251 int height = fm.height(); // line height
252 int ascent = fm.ascent();
253 int maxwidth = d->m_boundingRect.width();
254 int i;
255 int lwidth = 0;
256 int end = 0;
257 for (i = 0; i < d->m_breakPositions.count() ; ++i )
258 {
259 // if this is the last line, leave the loop
260 if ( (d->m_constrainingRect.height() >= 0) &&
261 ((y + 2 * height) > d->m_constrainingRect.height()) )
262 break;
263 end = d->m_breakPositions.at(i);
264 lwidth = d->m_lineWidths.at(i);
265 int x = textX;
266 if ( flags & Qt::AlignHCenter )
267 x += ( maxwidth - lwidth ) / 2;
268 else if ( flags & Qt::AlignRight )
269 x += maxwidth - lwidth;
270 painter->drawText( x, textY + y + ascent, d->m_text.mid( start, end - start + 1 ) );
271 y += height;
272 start = end + 1;
273 }
274
275 // Draw the last line
276 lwidth = d->m_lineWidths.last();
277 int x = textX;
278 if ( flags & Qt::AlignHCenter )
279 x += ( maxwidth - lwidth ) / 2;
280 else if ( flags & Qt::AlignRight )
281 x += maxwidth - lwidth;
282 if ( (d->m_constrainingRect.height() < 0) ||
283 ((y + height) <= d->m_constrainingRect.height()) ) {
284 if ( i == d->m_breakPositions.count() )
285 painter->drawText( x, textY + y + ascent, d->m_text.mid( start ) );
286 else if (flags & FadeOut)
287 drawFadeoutText( painter, textX, textY + y + ascent,
288 d->m_constrainingRect.width(),
289 d->m_text.mid( start ) );
290 else if (flags & Truncate)
291 drawTruncateText( painter, textX, textY + y + ascent,
292 d->m_constrainingRect.width(),
293 d->m_text.mid( start ) );
294 else
295 painter->drawText( x, textY + y + ascent,
296 d->m_text.mid( start ) );
297 }
298}
299
300QRect KWordWrap::boundingRect() const
301{
302 return d->m_boundingRect;
303}
304
KWordWrap
Word-wrap algorithm that takes into account beautifulness ;)
Definition: kwordwrap.h:50
KWordWrap::boundingRect
QRect boundingRect() const
Definition: kwordwrap.cpp:300
KWordWrap::Truncate
@ Truncate
Definition: kwordwrap.h:56
KWordWrap::FadeOut
@ FadeOut
Definition: kwordwrap.h:56
KWordWrap::formatText
static KWordWrap * formatText(QFontMetrics &fm, const QRect &r, int flags, const QString &str, int len=-1)
Main method for wrapping text.
Definition: kwordwrap.cpp:40
KWordWrap::drawFadeoutText
static void drawFadeoutText(QPainter *p, int x, int y, int maxW, const QString &t)
Draws the string t at the given coordinates, if it does not fit into maxW the text will be faded out.
Definition: kwordwrap.cpp:191
KWordWrap::truncatedString
QString truncatedString(bool dots=true) const
Definition: kwordwrap.cpp:174
KWordWrap::drawTruncateText
static void drawTruncateText(QPainter *p, int x, int y, int maxW, const QString &t)
Draws the string t at the given coordinates, if it does not fit into maxW the text will be truncated.
Definition: kwordwrap.cpp:238
KWordWrap::~KWordWrap
~KWordWrap()
Destructor.
Definition: kwordwrap.cpp:155
KWordWrap::drawText
void drawText(QPainter *painter, int x, int y, int flags=Qt::AlignLeft) const
Draw the text that has been previously wrapped, at position x,y.
Definition: kwordwrap.cpp:244
KWordWrap::wrappedString
QString wrappedString() const
Definition: kwordwrap.cpp:159
kdebug.h
mixColors
static QColor mixColors(double p1, QColor c1, QColor c2)
Definition: kwordwrap.cpp:185
kwordwrap.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Mon Feb 20 2023 00:00:00 by doxygen 1.9.6 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDEUI

Skip menu "KDEUI"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • File Members
  • Modules
  • Related Pages

kdelibs-4.14.38 API Reference

Skip menu "kdelibs-4.14.38 API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal