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 <qpainter.h> 00022 00023 class KWordWrapPrivate { 00024 public: 00025 QRect m_constrainingRect; 00026 }; 00027 00028 KWordWrap::KWordWrap(const QRect & r) { 00029 d = new KWordWrapPrivate; 00030 d->m_constrainingRect = r; 00031 } 00032 00033 KWordWrap* KWordWrap::formatText( QFontMetrics &fm, const QRect & r, int /*flags*/, const QString & str, int len ) 00034 { 00035 KWordWrap* kw = new KWordWrap( r ); 00036 // The wordwrap algorithm 00037 // The variable names and the global shape of the algorithm are inspired 00038 // from QTextFormatterBreakWords::format(). 00039 //kdDebug() << "KWordWrap::formatText " << str << " r=" << r.x() << "," << r.y() << " " << r.width() << "x" << r.height() << endl; 00040 int height = fm.height(); 00041 if ( len == -1 ) 00042 kw->m_text = str; 00043 else 00044 kw->m_text = str.left( len ); 00045 if ( len == -1 ) 00046 len = str.length(); 00047 int lastBreak = -1; 00048 int lineWidth = 0; 00049 int x = 0; 00050 int y = 0; 00051 int w = r.width(); 00052 int textwidth = 0; 00053 bool isBreakable = false; 00054 bool wasBreakable = false; // value of isBreakable for last char (i-1) 00055 bool isParens = false; // true if one of ({[ 00056 bool wasParens = false; // value of isParens for last char (i-1) 00057 00058 for ( int i = 0 ; i < len; ++i ) 00059 { 00060 QChar c = str[i]; 00061 int ww = fm.charWidth( str, i ); 00062 00063 isParens = ( c == '(' || c == '[' || c == '{' ); 00064 // isBreakable is true when we can break _after_ this character. 00065 isBreakable = ( c.isSpace() || c.isPunct() || c.isSymbol() ) & !isParens; 00066 00067 // Special case for '(', '[' and '{': we want to break before them 00068 if ( !isBreakable && i < len-1 ) { 00069 QChar nextc = str[i+1]; // look at next char 00070 isBreakable = ( nextc == '(' || nextc == '[' || nextc == '{' ); 00071 } 00072 // Special case for '/': after normal chars it's breakable (e.g. inside a path), 00073 // but after another breakable char it's not (e.g. "mounted at /foo") 00074 // Same thing after a parenthesis (e.g. "dfaure [/fool]") 00075 if ( c == '/' && (wasBreakable || wasParens) ) 00076 isBreakable = false; 00077 00078 /*kdDebug() << "c='" << QString(c) << "' i=" << i << "/" << len 00079 << " x=" << x << " ww=" << ww << " w=" << w 00080 << " lastBreak=" << lastBreak << " isBreakable=" << isBreakable << endl;*/ 00081 int breakAt = -1; 00082 if ( x + ww > w && lastBreak != -1 ) // time to break and we know where 00083 breakAt = lastBreak; 00084 if ( x + ww > w - 4 && lastBreak == -1 ) // time to break but found nowhere [-> break here] 00085 breakAt = i; 00086 if ( i == len - 2 && x + ww + fm.charWidth( str, i+1 ) > w ) // don't leave the last char alone 00087 breakAt = lastBreak == -1 ? i - 1 : lastBreak; 00088 if ( c == '\n' ) // Forced break here 00089 { 00090 if ( breakAt == -1 && lastBreak != -1) // only break if not already breaking 00091 { 00092 breakAt = i - 1; 00093 lastBreak = -1; 00094 } 00095 // remove the line feed from the string 00096 kw->m_text.remove(i, 1); 00097 len--; 00098 } 00099 if ( breakAt != -1 ) 00100 { 00101 //kdDebug() << "KWordWrap::formatText breaking after " << breakAt << endl; 00102 kw->m_breakPositions.append( breakAt ); 00103 int thisLineWidth = lastBreak == -1 ? x + ww : lineWidth; 00104 kw->m_lineWidths.append( thisLineWidth ); 00105 textwidth = QMAX( textwidth, thisLineWidth ); 00106 x = 0; 00107 y += height; 00108 wasBreakable = true; 00109 wasParens = false; 00110 if ( lastBreak != -1 ) 00111 { 00112 // Breakable char was found, restart from there 00113 i = lastBreak; 00114 lastBreak = -1; 00115 continue; 00116 } 00117 } else if ( isBreakable ) 00118 { 00119 lastBreak = i; 00120 lineWidth = x + ww; 00121 } 00122 x += ww; 00123 wasBreakable = isBreakable; 00124 wasParens = isParens; 00125 } 00126 textwidth = QMAX( textwidth, x ); 00127 kw->m_lineWidths.append( x ); 00128 y += height; 00129 //kdDebug() << "KWordWrap::formatText boundingRect:" << r.x() << "," << r.y() << " " << textwidth << "x" << y << endl; 00130 kw->m_boundingRect.setRect( 0, 0, textwidth, y ); 00131 return kw; 00132 } 00133 00134 KWordWrap::~KWordWrap() { 00135 delete d; 00136 } 00137 00138 QString KWordWrap::wrappedString() const 00139 { 00140 // We use the calculated break positions to insert '\n' into the string 00141 QString ws; 00142 int start = 0; 00143 QValueList<int>::ConstIterator it = m_breakPositions.begin(); 00144 for ( ; it != m_breakPositions.end() ; ++it ) 00145 { 00146 int end = (*it); 00147 ws += m_text.mid( start, end - start + 1 ) + '\n'; 00148 start = end + 1; 00149 } 00150 ws += m_text.mid( start ); 00151 return ws; 00152 } 00153 00154 QString KWordWrap::truncatedString( bool dots ) const 00155 { 00156 QString ts; 00157 QValueList<int>::ConstIterator it = m_breakPositions.begin(); 00158 if ( it != m_breakPositions.end() ) 00159 { 00160 ts = m_text.left( (*it) + 1 ); 00161 if ( dots ) 00162 ts += "..."; 00163 } 00164 else 00165 ts = m_text; 00166 return ts; 00167 } 00168 00169 static QColor mixColors(double p1, QColor c1, QColor c2) { 00170 return QColor(int(c1.red() * p1 + c2.red() * (1.0-p1)), 00171 int(c1.green() * p1 + c2.green() * (1.0-p1)), 00172 int(c1.blue() * p1 + c2.blue() * (1.0-p1))); 00173 } 00174 00175 void KWordWrap::drawFadeoutText(QPainter *p, int x, int y, int maxW, 00176 const QString &t) { 00177 QFontMetrics fm = p->fontMetrics(); 00178 QColor bgColor = p->backgroundColor(); 00179 QColor textColor = p->pen().color(); 00180 00181 if ( ( fm.boundingRect( t ).width() > maxW ) && ( t.length() > 1 ) ) { 00182 unsigned int tl = 0; 00183 int w = 0; 00184 while ( tl < t.length() ) { 00185 w += fm.charWidth( t, tl ); 00186 if ( w >= maxW ) 00187 break; 00188 tl++; 00189 } 00190 00191 if (tl > 3) { 00192 p->drawText( x, y, t.left( tl - 3 ) ); 00193 x += fm.width( t.left( tl - 3 ) ); 00194 } 00195 int n = QMIN( tl, 3); 00196 for (int i = 0; i < n; i++) { 00197 p->setPen( mixColors( 0.70 - i * 0.25, textColor, bgColor ) ); 00198 QString s( t.at( tl - n + i ) ); 00199 p->drawText( x, y, s ); 00200 x += fm.width( s ); 00201 } 00202 } 00203 else 00204 p->drawText( x, y, t ); 00205 } 00206 00207 void KWordWrap::drawText( QPainter *painter, int textX, int textY, int flags ) const 00208 { 00209 //kdDebug() << "KWordWrap::drawText text=" << wrappedString() << " x=" << textX << " y=" << textY << endl; 00210 // We use the calculated break positions to draw the text line by line using QPainter 00211 int start = 0; 00212 int y = 0; 00213 QFontMetrics fm = painter->fontMetrics(); 00214 int height = fm.height(); // line height 00215 int ascent = fm.ascent(); 00216 int maxwidth = m_boundingRect.width(); 00217 QValueList<int>::ConstIterator it = m_breakPositions.begin(); 00218 QValueList<int>::ConstIterator itw = m_lineWidths.begin(); 00219 for ( ; it != m_breakPositions.end() ; ++it, ++itw ) 00220 { 00221 // if this is the last line, leave the loop 00222 if ( (d->m_constrainingRect.height() >= 0) && 00223 ((y + 2 * height) > d->m_constrainingRect.height()) ) 00224 break; 00225 int end = (*it); 00226 int x = textX; 00227 if ( flags & Qt::AlignHCenter ) 00228 x += ( maxwidth - *itw ) / 2; 00229 else if ( flags & Qt::AlignRight ) 00230 x += maxwidth - *itw; 00231 painter->drawText( x, textY + y + ascent, m_text.mid( start, end - start + 1 ) ); 00232 y += height; 00233 start = end + 1; 00234 } 00235 // Draw the last line 00236 int x = textX; 00237 if ( flags & Qt::AlignHCenter ) 00238 x += ( maxwidth - *itw ) / 2; 00239 else if ( flags & Qt::AlignRight ) 00240 x += maxwidth - *itw; 00241 if ( (d->m_constrainingRect.height() < 0) || 00242 ((y + height) <= d->m_constrainingRect.height()) ) { 00243 if ( it == m_breakPositions.end() ) 00244 painter->drawText( x, textY + y + ascent, m_text.mid( start ) ); 00245 else if (flags & FadeOut) 00246 drawFadeoutText( painter, x, textY + y + ascent, 00247 d->m_constrainingRect.width() - x + textX, 00248 m_text.mid( start ) ); 00249 else 00250 painter->drawText( x, textY + y + ascent, 00251 m_text.mid( start, (*it) - start + 1 ) ); 00252 } 00253 } 00254
KDE Logo
This file is part of the documentation for kdeui Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Mon Aug 30 22:54:01 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003