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 int n = qMin( tl, 3);
00206 if ( t.isRightToLeft() ) {
00207 x += maxW;
00208 if (tl > 3) {
00209 x -= fm.width( t.left( tl - 3 ) );
00210 p->drawText( x, y, t.left( tl - 3 ) );
00211 }
00212 for (int i = 0; i < n; i++) {
00213 p->setPen( mixColors( 0.70 - i * 0.25, textColor, bgColor ) );
00214 QString s( t.at( tl - n + i ) );
00215 x -= fm.width( s );
00216 p->drawText( x, y, s );
00217 }
00218 }
00219 else {
00220 if (tl > 3) {
00221 p->drawText( x, y, t.left( tl - 3 ) );
00222 x += fm.width( t.left( tl - 3 ) );
00223 }
00224 for (int i = 0; i < n; i++) {
00225 p->setPen( mixColors( 0.70 - i * 0.25, textColor, bgColor ) );
00226 QString s( t.at( tl - n + i ) );
00227 p->drawText( x, y, s );
00228 x += fm.width( s );
00229 }
00230 }
00231 }
00232 else
00233 p->drawText( x, y, t );
00234 }
00235
00236 void KWordWrap::drawTruncateText(QPainter *p, int x, int y, int maxW,
00237 const QString &t) {
00238 QString tmpText = p->fontMetrics().elidedText(t, Qt::ElideRight, maxW);
00239 p->drawText( x, y, tmpText );
00240 }
00241
00242 void KWordWrap::drawText( QPainter *painter, int textX, int textY, int flags ) const
00243 {
00244
00245
00246 int start = 0;
00247 int y = 0;
00248 QFontMetrics fm = painter->fontMetrics();
00249 int height = fm.height();
00250 int ascent = fm.ascent();
00251 int maxwidth = d->m_boundingRect.width();
00252 int i;
00253 int lwidth = 0;
00254 int end = 0;
00255 for (i = 0; i < d->m_breakPositions.count() ; ++i )
00256 {
00257
00258 if ( (d->m_constrainingRect.height() >= 0) &&
00259 ((y + 2 * height) > d->m_constrainingRect.height()) )
00260 break;
00261 end = d->m_breakPositions.at(i);
00262 lwidth = d->m_lineWidths.at(i);
00263 int x = textX;
00264 if ( flags & Qt::AlignHCenter )
00265 x += ( maxwidth - lwidth ) / 2;
00266 else if ( flags & Qt::AlignRight )
00267 x += maxwidth - lwidth;
00268 painter->drawText( x, textY + y + ascent, d->m_text.mid( start, end - start + 1 ) );
00269 y += height;
00270 start = end + 1;
00271 }
00272
00273
00274 lwidth = d->m_lineWidths.last();
00275 int x = textX;
00276 if ( flags & Qt::AlignHCenter )
00277 x += ( maxwidth - lwidth ) / 2;
00278 else if ( flags & Qt::AlignRight )
00279 x += maxwidth - lwidth;
00280 if ( (d->m_constrainingRect.height() < 0) ||
00281 ((y + height) <= d->m_constrainingRect.height()) ) {
00282 if ( i == d->m_breakPositions.count() )
00283 painter->drawText( x, textY + y + ascent, d->m_text.mid( start ) );
00284 else if (flags & FadeOut)
00285 drawFadeoutText( painter, textX, textY + y + ascent,
00286 d->m_constrainingRect.width(),
00287 d->m_text.mid( start ) );
00288 else if (flags & Truncate)
00289 drawTruncateText( painter, textX, textY + y + ascent,
00290 d->m_constrainingRect.width(),
00291 d->m_text.mid( start ) );
00292 else
00293 painter->drawText( x, textY + y + ascent,
00294 d->m_text.mid( start ) );
00295 }
00296 }
00297
00298 QRect KWordWrap::boundingRect() const
00299 {
00300 return d->m_boundingRect;
00301 }
00302