00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
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 , const QString & str, int len )
00041 {
00042 KWordWrap* kw = new KWordWrap( r );
00043
00044
00045
00046
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;
00062 bool isParens = false;
00063 bool wasParens = false;
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
00073 isBreakable = ( c.isSpace() || c.isPunct() || c.isSymbol() ) & !isParens;
00074
00075
00076 if ( !isBreakable && i < len-1 ) {
00077 QChar nextc = str.at(i + 1);
00078 isBreakable = ( nextc == QLatin1Char('(')
00079 || nextc == QLatin1Char('[')
00080 || nextc == QLatin1Char('{') );
00081 }
00082
00083
00084
00085 if ( c == QLatin1Char('/') && (wasBreakable || wasParens) )
00086 isBreakable = false;
00087
00088
00089
00090
00091 int breakAt = -1;
00092 if ( x + ww > w && lastBreak != -1 )
00093 breakAt = lastBreak;
00094 if ( x + ww > w - 4 && lastBreak == -1 )
00095 breakAt = i;
00096 if ( i == len - 2 && x + ww + fm.charWidth( str, i+1 ) > w )
00097 breakAt = lastBreak == -1 ? i - 1 : lastBreak;
00098 if ( c == QLatin1Char('\n') )
00099 {
00100 if ( breakAt == -1 && lastBreak != -1)
00101 {
00102 breakAt = i - 1;
00103 lastBreak = -1;
00104 }
00105
00106 kw->d->m_text.remove(i, 1);
00107 len--;
00108 }
00109 if ( breakAt != -1 )
00110 {
00111
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
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
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
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
00230
00231 int start = 0;
00232 int y = 0;
00233 QFontMetrics fm = painter->fontMetrics();
00234 int height = fm.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
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
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