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 Thu Sep 30 05:16:53 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2003