| # kdeui Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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 }

This file is part of the documentation for kdeui Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:20:24 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003