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

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
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.5.7
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