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

KDEUI

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • KIO
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • Kross
  • KUtils
  • Nepomuk
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.4
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