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

KDEUI

  • sources
  • kde-4.14
  • kdelibs
  • 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 
25 class KWordWrapPrivate {
26 public:
27  QRect m_constrainingRect;
28  QVector<int> m_breakPositions;
29  QVector<int> m_lineWidths;
30  QRect m_boundingRect;
31  QString m_text;
32 };
33 
34 KWordWrap::KWordWrap(const QRect & r)
35 : d(new KWordWrapPrivate)
36 {
37  d->m_constrainingRect = r;
38 }
39 
40 KWordWrap* 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 
155 KWordWrap::~KWordWrap() {
156  delete d;
157 }
158 
159 QString 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 
174 QString 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 
185 static 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 
191 void 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 
238 void 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 
244 void 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 
300 QRect KWordWrap::boundingRect() const
301 {
302  return d->m_boundingRect;
303 }
304 
QFontMetrics::ascent
int ascent() const
QPainter::background
const QBrush & background() const
kdebug.h
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::truncatedString
QString truncatedString(bool dots=true) const
Definition: kwordwrap.cpp:174
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
QChar
QRect::height
int height() const
mixColors
static QColor mixColors(double p1, QColor c1, QColor c2)
Definition: kwordwrap.cpp:185
QFontMetrics
KWordWrap::boundingRect
QRect boundingRect() const
Definition: kwordwrap.cpp:300
QString::remove
QString & remove(int position, int n)
QBrush::color
const QColor & color() const
QPen::color
QColor color() const
QRect
QString::isRightToLeft
bool isRightToLeft() const
QFontMetrics::boundingRect
QRect boundingRect(QChar ch) const
QChar::isSpace
bool isSpace() const
KWordWrap::~KWordWrap
~KWordWrap()
Destructor.
Definition: kwordwrap.cpp:155
QColor::red
int red() const
QPainter::setPen
void setPen(const QColor &color)
QPainter
QFontMetrics::elidedText
QString elidedText(const QString &text, Qt::TextElideMode mode, int width, int flags) const
QPainter::drawText
void drawText(const QPointF &position, const QString &text)
QString
QColor
QChar::isPunct
bool isPunct() const
QFontMetrics::charWidth
int charWidth(const QString &text, int pos) const
KWordWrap
Word-wrap algorithm that takes into account beautifulness ;)
Definition: kwordwrap.h:49
QColor::green
int green() const
KWordWrap::wrappedString
QString wrappedString() const
Definition: kwordwrap.cpp:159
QLatin1Char
QFontMetrics::width
int width(const QString &text, int len) const
QColor::blue
int blue() const
QRect::width
int width() const
QVector< int >
QLatin1String
QPainter::fontMetrics
QFontMetrics fontMetrics() const
QString::at
const QChar at(int position) const
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
QFontMetrics::height
int height() const
QString::length
int length() const
QString::left
QString left(int n) const
KWordWrap::Truncate
Definition: kwordwrap.h:56
KWordWrap::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
KStandardShortcut::end
const KShortcut & end()
Goto end of the document.
Definition: kstandardshortcut.cpp:348
QPainter::pen
const QPen & pen() const
QChar::isSymbol
bool isSymbol() const
kwordwrap.h
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:24:00 by doxygen 1.8.7 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 API Reference

Skip menu "kdelibs 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

Search



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