kdeui Library API Documentation

kwordwrap.cpp

00001 /* This file is part of the KDE libraries 00002 Copyright (C) 2001 David Faure <david@mandrakesoft.com> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License version 2 as published by the Free Software Foundation. 00007 00008 This library is distributed in the hope that it will be useful, 00009 but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 Library General Public License for more details. 00012 00013 You should have received a copy of the GNU Library General Public License 00014 along with this library; see the file COPYING.LIB. If not, write to 00015 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00016 Boston, MA 02111-1307, USA. 00017 */ 00018 00019 #include "kwordwrap.h" 00020 #include <kdebug.h> 00021 #include <kstringhandler.h> 00022 #include <qpainter.h> 00023 00024 class KWordWrapPrivate { 00025 public: 00026 QRect m_constrainingRect; 00027 }; 00028 00029 KWordWrap::KWordWrap(const QRect & r) { 00030 d = new KWordWrapPrivate; 00031 d->m_constrainingRect = r; 00032 } 00033 00034 KWordWrap* KWordWrap::formatText( QFontMetrics &fm, const QRect & r, int /*flags*/, const QString & str, int len ) 00035 { 00036 KWordWrap* kw = new KWordWrap( r ); 00037 // The wordwrap algorithm 00038 // The variable names and the global shape of the algorithm are inspired 00039 // from QTextFormatterBreakWords::format(). 00040 //kdDebug() << "KWordWrap::formatText " << str << " r=" << r.x() << "," << r.y() << " " << r.width() << "x" << r.height() << endl; 00041 int height = fm.height(); 00042 if ( len == -1 ) 00043 kw->m_text = str; 00044 else 00045 kw->m_text = str.left( len ); 00046 if ( len == -1 ) 00047 len = str.length(); 00048 int lastBreak = -1; 00049 int lineWidth = 0; 00050 int x = 0; 00051 int y = 0; 00052 int w = r.width(); 00053 int textwidth = 0; 00054 bool isBreakable = false; 00055 bool wasBreakable = false; // value of isBreakable for last char (i-1) 00056 bool isParens = false; // true if one of ({[ 00057 bool wasParens = false; // value of isParens for last char (i-1) 00058 00059 for ( int i = 0 ; i < len; ++i ) 00060 { 00061 QChar c = str[i]; 00062 int ww = fm.charWidth( str, i ); 00063 00064 isParens = ( c == '(' || c == '[' || c == '{' ); 00065 // isBreakable is true when we can break _after_ this character. 00066 isBreakable = ( c.isSpace() || c.isPunct() || c.isSymbol() ) & !isParens; 00067 00068 // Special case for '(', '[' and '{': we want to break before them 00069 if ( !isBreakable && i < len-1 ) { 00070 QChar nextc = str[i+1]; // look at next char 00071 isBreakable = ( nextc == '(' || nextc == '[' || nextc == '{' ); 00072 } 00073 // Special case for '/': after normal chars it's breakable (e.g. inside a path), 00074 // but after another breakable char it's not (e.g. "mounted at /foo") 00075 // Same thing after a parenthesis (e.g. "dfaure [/fool]") 00076 if ( c == '/' && (wasBreakable || wasParens) ) 00077 isBreakable = false; 00078 00079 /*kdDebug() << "c='" << QString(c) << "' i=" << i << "/" << len 00080 << " x=" << x << " ww=" << ww << " w=" << w 00081 << " lastBreak=" << lastBreak << " isBreakable=" << isBreakable << endl;*/ 00082 int breakAt = -1; 00083 if ( x + ww > w && lastBreak != -1 ) // time to break and we know where 00084 breakAt = lastBreak; 00085 if ( x + ww > w - 4 && lastBreak == -1 ) // time to break but found nowhere [-> break here] 00086 breakAt = i; 00087 if ( i == len - 2 && x + ww + fm.charWidth( str, i+1 ) > w ) // don't leave the last char alone 00088 breakAt = lastBreak == -1 ? i - 1 : lastBreak; 00089 if ( c == '\n' ) // Forced break here 00090 { 00091 if ( breakAt == -1 && lastBreak != -1) // only break if not already breaking 00092 { 00093 breakAt = i - 1; 00094 lastBreak = -1; 00095 } 00096 // remove the line feed from the string 00097 kw->m_text.remove(i, 1); 00098 len--; 00099 } 00100 if ( breakAt != -1 ) 00101 { 00102 //kdDebug() << "KWordWrap::formatText breaking after " << breakAt << endl; 00103 kw->m_breakPositions.append( breakAt ); 00104 int thisLineWidth = lastBreak == -1 ? x + ww : lineWidth; 00105 kw->m_lineWidths.append( thisLineWidth ); 00106 textwidth = QMAX( textwidth, thisLineWidth ); 00107 x = 0; 00108 y += height; 00109 wasBreakable = true; 00110 wasParens = false; 00111 if ( lastBreak != -1 ) 00112 { 00113 // Breakable char was found, restart from there 00114 i = lastBreak; 00115 lastBreak = -1; 00116 continue; 00117 } 00118 } else if ( isBreakable ) 00119 { 00120 lastBreak = i; 00121 lineWidth = x + ww; 00122 } 00123 x += ww; 00124 wasBreakable = isBreakable; 00125 wasParens = isParens; 00126 } 00127 textwidth = QMAX( textwidth, x ); 00128 kw->m_lineWidths.append( x ); 00129 y += height; 00130 //kdDebug() << "KWordWrap::formatText boundingRect:" << r.x() << "," << r.y() << " " << textwidth << "x" << y << endl; 00131 if ( r.height() >= 0 && y > r.height() ) 00132 textwidth = r.width(); 00133 int realY = y; 00134 if ( r.height() >= 0 ) 00135 { 00136 while ( realY > r.height() ) 00137 realY -= height; 00138 realY = QMAX( realY, 0 ); 00139 } 00140 kw->m_boundingRect.setRect( 0, 0, textwidth, realY ); 00141 return kw; 00142 } 00143 00144 KWordWrap::~KWordWrap() { 00145 delete d; 00146 } 00147 00148 QString KWordWrap::wrappedString() const 00149 { 00150 // We use the calculated break positions to insert '\n' into the string 00151 QString ws; 00152 int start = 0; 00153 QValueList<int>::ConstIterator it = m_breakPositions.begin(); 00154 for ( ; it != m_breakPositions.end() ; ++it ) 00155 { 00156 int end = (*it); 00157 ws += m_text.mid( start, end - start + 1 ) + '\n'; 00158 start = end + 1; 00159 } 00160 ws += m_text.mid( start ); 00161 return ws; 00162 } 00163 00164 QString KWordWrap::truncatedString( bool dots ) const 00165 { 00166 if ( m_breakPositions.isEmpty() ) 00167 return m_text; 00168 00169 QString ts = m_text.left( m_breakPositions.first() + 1 ); 00170 if ( dots ) 00171 ts += "..."; 00172 return ts; 00173 } 00174 00175 static QColor mixColors(double p1, QColor c1, QColor c2) { 00176 return QColor(int(c1.red() * p1 + c2.red() * (1.0-p1)), 00177 int(c1.green() * p1 + c2.green() * (1.0-p1)), 00178 int(c1.blue() * p1 + c2.blue() * (1.0-p1))); 00179 } 00180 00181 void KWordWrap::drawFadeoutText(QPainter *p, int x, int y, int maxW, 00182 const QString &t) { 00183 QFontMetrics fm = p->fontMetrics(); 00184 QColor bgColor = p->backgroundColor(); 00185 QColor textColor = p->pen().color(); 00186 00187 if ( ( fm.boundingRect( t ).width() > maxW ) && ( t.length() > 1 ) ) { 00188 unsigned int tl = 0; 00189 int w = 0; 00190 while ( tl < t.length() ) { 00191 w += fm.charWidth( t, tl ); 00192 if ( w >= maxW ) 00193 break; 00194 tl++; 00195 } 00196 00197 if (tl > 3) { 00198 p->drawText( x, y, t.left( tl - 3 ) ); 00199 x += fm.width( t.left( tl - 3 ) ); 00200 } 00201 int n = QMIN( tl, 3); 00202 for (int i = 0; i < n; i++) { 00203 p->setPen( mixColors( 0.70 - i * 0.25, textColor, bgColor ) ); 00204 QString s( t.at( tl - n + i ) ); 00205 p->drawText( x, y, s ); 00206 x += fm.width( s ); 00207 } 00208 } 00209 else 00210 p->drawText( x, y, t ); 00211 } 00212 00213 void KWordWrap::drawTruncateText(QPainter *p, int x, int y, int maxW, 00214 const QString &t) { 00215 QString tmpText = KStringHandler::rPixelSqueeze( t, p->fontMetrics(), maxW ); 00216 p->drawText( x, y, tmpText, maxW ); 00217 } 00218 00219 void KWordWrap::drawText( QPainter *painter, int textX, int textY, int flags ) const 00220 { 00221 //kdDebug() << "KWordWrap::drawText text=" << wrappedString() << " x=" << textX << " y=" << textY << endl; 00222 // We use the calculated break positions to draw the text line by line using QPainter 00223 int start = 0; 00224 int y = 0; 00225 QFontMetrics fm = painter->fontMetrics(); 00226 int height = fm.height(); // line height 00227 int ascent = fm.ascent(); 00228 int maxwidth = m_boundingRect.width(); 00229 QValueList<int>::ConstIterator it = m_breakPositions.begin(); 00230 QValueList<int>::ConstIterator itw = m_lineWidths.begin(); 00231 for ( ; it != m_breakPositions.end() ; ++it, ++itw ) 00232 { 00233 // if this is the last line, leave the loop 00234 if ( (d->m_constrainingRect.height() >= 0) && 00235 ((y + 2 * height) > d->m_constrainingRect.height()) ) 00236 break; 00237 int end = (*it); 00238 int x = textX; 00239 if ( flags & Qt::AlignHCenter ) 00240 x += ( maxwidth - *itw ) / 2; 00241 else if ( flags & Qt::AlignRight ) 00242 x += maxwidth - *itw; 00243 painter->drawText( x, textY + y + ascent, m_text.mid( start, end - start + 1 ) ); 00244 y += height; 00245 start = end + 1; 00246 } 00247 // Draw the last line 00248 int x = textX; 00249 if ( flags & Qt::AlignHCenter ) 00250 x += ( maxwidth - *itw ) / 2; 00251 else if ( flags & Qt::AlignRight ) 00252 x += maxwidth - *itw; 00253 if ( (d->m_constrainingRect.height() < 0) || 00254 ((y + height) <= d->m_constrainingRect.height()) ) { 00255 if ( it == m_breakPositions.end() ) 00256 painter->drawText( x, textY + y + ascent, m_text.mid( start ) ); 00257 else if (flags & FadeOut) 00258 drawFadeoutText( painter, textX, textY + y + ascent, 00259 d->m_constrainingRect.width(), 00260 m_text.mid( start ) ); 00261 else if (flags & Truncate) 00262 drawTruncateText( painter, textX, textY + y + ascent, 00263 d->m_constrainingRect.width(), 00264 m_text.mid( start ) ); 00265 else 00266 painter->drawText( x, textY + y + ascent, 00267 m_text.mid( start, (*it) - start + 1 ) ); 00268 } 00269 }
KDE Logo
This file is part of the documentation for kdeui Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:40:35 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003