• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kdeui

kwordwrap.cpp

Go to the documentation of this file.
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., 51 Franklin Street, Fifth Floor,
00016    Boston, MA 02110-1301, 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 }

kdeui

Skip menu "kdeui"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • dcop
  • DNSSD
  • interfaces
  • Kate
  • kconf_update
  • KDECore
  • KDED
  • kdefx
  • KDEsu
  • kdeui
  • KDocTools
  • KHTML
  • KImgIO
  • KInit
  • kio
  • kioslave
  • KJS
  • KNewStuff
  • KParts
  • KUtils
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal