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 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
00074 isBreakable = ( c.isSpace() || c.isPunct() || c.isSymbol() ) & !isParens;
00075
00076
00077 if ( !isBreakable && i < len-1 ) {
00078 const QChar nextc = inputString.at(i + 1);
00079 isBreakable = ( nextc == QLatin1Char('(')
00080 || nextc == QLatin1Char('[')
00081 || nextc == QLatin1Char('{') );
00082 }
00083
00084
00085
00086 if ( c == QLatin1Char('/') && (wasBreakable || wasParens) )
00087 isBreakable = false;
00088
00089
00090
00091
00092 int breakAt = -1;
00093 if ( x + ww > w && lastBreak != -1 )
00094 breakAt = lastBreak;
00095 if ( x + ww > w - 4 && lastBreak == -1 )
00096 breakAt = i;
00097 if (i == len - 2 && x + ww + fm.charWidth(inputString, i+1) > w)
00098 breakAt = lastBreak == -1 ? i - 1 : lastBreak;
00099 if ( c == QLatin1Char('\n') )
00100 {
00101 if ( breakAt == -1 && lastBreak != -1)
00102 {
00103 breakAt = i - 1;
00104 lastBreak = -1;
00105 }
00106
00107 kw->d->m_text.remove(i, 1);
00108 inputString.remove(i, 1);
00109 len--;
00110 }
00111 if ( breakAt != -1 )
00112 {
00113
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
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
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
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;
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
00247
00248 int start = 0;
00249 int y = 0;
00250 QFontMetrics fm = painter->fontMetrics();
00251 int height = fm.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
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
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