00001
00025
00026
00027
00028 #include "rendering/render_text.h"
00029 #include "rendering/render_root.h"
00030 #include "rendering/break_lines.h"
00031 #include "xml/dom_nodeimpl.h"
00032
00033 #include "misc/loader.h"
00034
00035 #include <qpainter.h>
00036 #include <kdebug.h>
00037 #include <assert.h>
00038
00039 using namespace khtml;
00040 using namespace DOM;
00041
00042 void TextSlave::paintSelection(const Font *f, RenderText *text, QPainter *p, RenderStyle* style, int tx, int ty, int startPos, int endPos)
00043 {
00044 if(startPos > m_len) return;
00045 if(startPos < 0) startPos = 0;
00046
00047 p->save();
00048 QColor c = style->color();
00049 p->setPen(QColor(0xff-c.red(),0xff-c.green(),0xff-c.blue()));
00050
00051 ty += m_baseline;
00052
00053
00054 f->drawText(p, m_x + tx, m_y + ty, text->str->s, text->str->l, m_start, m_len,
00055 m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, startPos, endPos, c);
00056 p->restore();
00057 }
00058
00059 void TextSlave::paintDecoration( QPainter *pt, RenderText* p, int _tx, int _ty, int deco, bool begin, bool end)
00060 {
00061 _tx += m_x;
00062 _ty += m_y;
00063
00064 int width = m_width - 1;
00065
00066 if( begin )
00067 width -= p->paddingLeft() + p->borderLeft();
00068
00069 if ( end )
00070 width -= p->paddingRight() + p->borderRight();
00071
00072 int underlineOffset = ( pt->fontMetrics().height() + m_baseline ) / 2;
00073 if(underlineOffset <= m_baseline) underlineOffset = m_baseline+1;
00074
00075 if(deco & UNDERLINE)
00076 pt->drawLine(_tx, _ty + underlineOffset, _tx + width, _ty + underlineOffset );
00077 if(deco & OVERLINE)
00078 pt->drawLine(_tx, _ty, _tx + width, _ty );
00079 if(deco & LINE_THROUGH)
00080 pt->drawLine(_tx, _ty + 2*m_baseline/3, _tx + width, _ty + 2*m_baseline/3 );
00081
00082
00083 }
00084
00085 void TextSlave::paintBoxDecorations(QPainter *pt, RenderStyle* style, RenderText *p, int _tx, int _ty, bool begin, bool end)
00086 {
00087 int topExtra = p->borderTop() + p->paddingTop();
00088 int bottomExtra = p->borderBottom() + p->paddingBottom();
00089
00090 int halfleading = (p->m_lineHeight - style->font().pixelSize() ) / 2;
00091
00092 _tx += m_x;
00093 _ty += m_y + halfleading - topExtra;
00094
00095 int width = m_width;
00096
00097
00098 int height = style->font().pixelSize() + topExtra + bottomExtra;
00099
00100 if( begin )
00101 _tx -= p->paddingLeft() + p->borderLeft();
00102
00103 QColor c = style->backgroundColor();
00104 CachedImage *i = style->backgroundImage();
00105 if(c.isValid() && (!i || i->tiled_pixmap(c).mask()))
00106 pt->fillRect(_tx, _ty, width, height, c);
00107
00108 if(i) {
00109
00110
00111 pt->drawTiledPixmap(_tx, _ty, width, height, i->tiled_pixmap(c));
00112 }
00113
00114 #ifdef DEBUG_VALIGN
00115 pt->fillRect(_tx, _ty, width, height, Qt::cyan );
00116 #endif
00117
00118 if(style->hasBorder())
00119 p->paintBorder(pt, _tx, _ty, width, height, style, begin, end);
00120 }
00121
00122 FindSelectionResult TextSlave::checkSelectionPoint(int _x, int _y, int _tx, int _ty, const Font *f, RenderText *text, int & offset, short lineHeight)
00123 {
00124
00125
00126 offset = 0;
00127
00128 if ( _y < _ty + m_y )
00129 return SelectionPointBefore;
00130
00131 if ( _y > _ty + m_y + lineHeight ) {
00132
00133
00134 offset = m_len;
00135 return SelectionPointAfter;
00136 }
00137 if ( _x > _tx + m_x + m_width ) {
00138
00139 return m_reversed ? SelectionPointBeforeInLine : SelectionPointAfterInLine;
00140 }
00141
00142
00143 if ( _x < _tx + m_x ) {
00144 return m_reversed ? SelectionPointAfterInLine : SelectionPointBeforeInLine;
00145 }
00146
00147 int delta = _x - (_tx + m_x);
00148
00149 int pos = 0;
00150 if ( m_reversed ) {
00151 delta -= m_width;
00152 while(pos < m_len) {
00153 int w = f->width( text->str->s, text->str->l, m_start + pos);
00154 int w2 = w/2;
00155 w -= w2;
00156 delta += w2;
00157 if(delta >= 0) break;
00158 pos++;
00159 delta += w;
00160 }
00161 } else {
00162 while(pos < m_len) {
00163 int w = f->width( text->str->s, text->str->l, m_start + pos);
00164 int w2 = w/2;
00165 w -= w2;
00166 delta -= w2;
00167 if(delta <= 0) break;
00168 pos++;
00169 delta -= w;
00170 }
00171 }
00172
00173 offset = pos;
00174 return SelectionPointInside;
00175 }
00176
00177
00178
00179 TextSlaveArray::TextSlaveArray()
00180 {
00181 setAutoDelete(true);
00182 }
00183
00184 int TextSlaveArray::compareItems( Item d1, Item d2 )
00185 {
00186 assert(d1);
00187 assert(d2);
00188
00189 return static_cast<TextSlave*>(d1)->m_y - static_cast<TextSlave*>(d2)->m_y;
00190 }
00191
00192
00193 int TextSlaveArray::findFirstMatching(Item d) const
00194 {
00195 int len = count();
00196
00197 if ( !len )
00198 return -1;
00199 if ( !d )
00200 return -1;
00201 int n1 = 0;
00202 int n2 = len - 1;
00203 int mid = 0;
00204 bool found = FALSE;
00205 while ( n1 <= n2 ) {
00206 int res;
00207 mid = (n1 + n2)/2;
00208 if ( (*this)[mid] == 0 )
00209 res = -1;
00210 else
00211 res = ((QGVector*)this)->compareItems( d, (*this)[mid] );
00212 if ( res < 0 )
00213 n2 = mid - 1;
00214 else if ( res > 0 )
00215 n1 = mid + 1;
00216 else {
00217 found = TRUE;
00218 break;
00219 }
00220 }
00221
00222
00223
00224 while ( found && (mid > 0) && !((QGVector*)this)->compareItems(d, (*this)[mid-1]) )
00225 mid--;
00226 return mid;
00227 }
00228
00229
00230
00231 RenderText::RenderText(DOM::NodeImpl* node, DOMStringImpl *_str)
00232 : RenderObject(node)
00233 {
00234
00235 setRenderText();
00236
00237 m_minWidth = -1;
00238 m_maxWidth = -1;
00239 str = _str;
00240 if(str) str->ref();
00241 KHTMLAssert(!str || !str->l || str->s);
00242
00243 m_selectionState = SelectionNone;
00244 m_hasReturn = true;
00245
00246 #ifdef DEBUG_LAYOUT
00247 QConstString cstr(str->s, str->l);
00248 kdDebug( 6040 ) << "RenderText ctr( "<< cstr.string().length() << " ) '" << cstr.string() << "'" << endl;
00249 #endif
00250 }
00251
00252 void RenderText::setStyle(RenderStyle *_style)
00253 {
00254 if ( style() != _style ) {
00255
00256 bool changedText = (!style() && (_style->fontVariant() != FVNORMAL || _style->textTransform() != TTNONE)) ||
00257 ((style() && style()->textTransform() != _style->textTransform()) ||
00258 (style() && style()->fontVariant() != _style->fontVariant()));
00259
00260 RenderObject::setStyle( _style );
00261 m_lineHeight = RenderObject::lineHeight(false);
00262
00263 if (changedText && element() && element()->string())
00264 setText(element()->string(), changedText);
00265 }
00266 }
00267
00268 RenderText::~RenderText()
00269 {
00270 deleteSlaves();
00271 if(str) str->deref();
00272 }
00273
00274 void RenderText::deleteSlaves()
00275 {
00276
00277
00278
00279
00280 unsigned int len = m_lines.size();
00281 for(unsigned int i=0; i < len; i++)
00282 m_lines.remove(i);
00283
00284 KHTMLAssert(m_lines.count() == 0);
00285 }
00286
00287 TextSlave * RenderText::findTextSlave( int offset, int &pos )
00288 {
00289
00290
00291
00292
00293
00294 if ( m_lines.isEmpty() )
00295 return 0L;
00296
00297 TextSlave* s = m_lines[0];
00298 uint si = 0;
00299 int off = s->m_len;
00300 while(offset > off && ++si < m_lines.count())
00301 {
00302 s = m_lines[si];
00303 off = s->m_start + s->m_len;
00304 }
00305
00306 pos = (offset > off ? s->m_len : offset - s->m_start );
00307 return s;
00308 }
00309
00310 bool RenderText::nodeAtPoint(NodeInfo& , int _x, int _y, int _tx, int _ty)
00311 {
00312 assert(parent());
00313
00314 _tx -= paddingLeft() + borderLeft();
00315 _ty -= borderTop() + paddingTop();
00316
00317 int height = m_lineHeight + borderTop() + paddingTop() +
00318 borderBottom() + paddingBottom();
00319
00320 bool inside = false;
00321 if (style()->visibility() != HIDDEN) {
00322 TextSlave *s = m_lines.count() ? m_lines[0] : 0;
00323 int si = 0;
00324 while(s) {
00325 if((_y >=_ty + s->m_y) && (_y < _ty + s->m_y + height) &&
00326 (_x >= _tx + s->m_x) && (_x <_tx + s->m_x + s->m_width) ) {
00327 inside = true;
00328 break;
00329 }
00330
00331 s = si < (int) m_lines.count()-1 ? m_lines[++si] : 0;
00332 }
00333 }
00334
00335 setMouseInside(inside);
00336
00337 return inside;
00338 }
00339
00340 FindSelectionResult RenderText::checkSelectionPoint(int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int &offset)
00341 {
00342
00343
00344 TextSlave *lastPointAfterInline=0;
00345
00346 for(unsigned int si = 0; si < m_lines.count(); si++)
00347 {
00348 TextSlave* s = m_lines[si];
00349 int result;
00350 const Font *f = htmlFont( si==0 );
00351 result = s->checkSelectionPoint(_x, _y, _tx, _ty, f, this, offset, m_lineHeight);
00352
00353
00354 if ( result == SelectionPointInside )
00355 {
00356 offset += s->m_start;
00357
00358 node = element();
00359 return SelectionPointInside;
00360 } else if ( result == SelectionPointBefore || result == SelectionPointBeforeInLine ) {
00361
00362 if ( si > 0 && lastPointAfterInline ) {
00363 offset = lastPointAfterInline->m_start + lastPointAfterInline->m_len;
00364
00365 node = element();
00366 return SelectionPointInside;
00367 } else {
00368 offset = 0;
00369
00370 node = element();
00371 return SelectionPointBefore;
00372 }
00373 } else if ( result == SelectionPointAfterInLine ) {
00374 lastPointAfterInline = s;
00375 }
00376
00377 }
00378
00379
00380 offset = str->l;
00381
00382 node = element();
00383 return SelectionPointAfter;
00384 }
00385
00386 void RenderText::cursorPos(int offset, int &_x, int &_y, int &height)
00387 {
00388 if (!m_lines.count()) {
00389 _x = _y = height = -1;
00390 return;
00391 }
00392
00393 int pos;
00394 TextSlave * s = findTextSlave( offset, pos );
00395 _y = s->m_y;
00396 height = m_lineHeight;
00397
00398 const QFontMetrics &fm = metrics( false );
00399 QString tekst(str->s + s->m_start, s->m_len);
00400 _x = s->m_x + (fm.boundingRect(tekst, pos)).right();
00401 if(pos)
00402 _x += fm.rightBearing( *(str->s + s->m_start + pos - 1 ) );
00403
00404 int absx, absy;
00405
00406 RenderObject *cb = containingBlock();
00407
00408 if (cb && cb != this && cb->absolutePosition(absx,absy))
00409 {
00410 _x += absx;
00411 _y += absy;
00412 } else {
00413
00414
00415 _x = _y = -1;
00416 }
00417 }
00418
00419 bool RenderText::absolutePosition(int &xPos, int &yPos, bool)
00420 {
00421 return RenderObject::absolutePosition(xPos, yPos, false);
00422
00423 if(parent() && parent()->absolutePosition(xPos, yPos, false)) {
00424 xPos -= paddingLeft() + borderLeft();
00425 yPos -= borderTop() + paddingTop();
00426 return true;
00427 }
00428 xPos = yPos = 0;
00429 return false;
00430 }
00431
00432 bool RenderText::posOfChar(int chr, int &x, int &y)
00433 {
00434 if (!parent())
00435 return false;
00436 parent()->absolutePosition( x, y, false );
00437
00438 int pos;
00439 TextSlave * s = findTextSlave( chr, pos );
00440
00441 if ( s ) {
00442
00443 x += s->m_x;
00444 y += s->m_y;
00445 return true;
00446 }
00447
00448 return false;
00449 }
00450
00451 int RenderText::rightmostPosition() const
00452 {
00453 if (style()->whiteSpace() != NORMAL)
00454 return maxWidth();
00455
00456 return 0;
00457 }
00458
00459 void RenderText::paintObject( QPainter *p, int , int y, int , int h,
00460 int tx, int ty)
00461 {
00462 int ow = style()->outlineWidth();
00463 RenderStyle* pseudoStyle = hasFirstLine() ? style()->getPseudoStyle(RenderStyle::FIRST_LINE) : 0;
00464 int d = style()->textDecoration();
00465 TextSlave f(0, y-ty);
00466 int si = m_lines.findFirstMatching(&f);
00467
00468 bool isPrinting = (p->device()->devType() == QInternal::Printer);
00469 if(si >= 0)
00470 {
00471
00472 while(si > 0 && m_lines[si-1]->checkVerticalPoint(y, ty, h, m_lineHeight))
00473 si--;
00474
00475
00476
00477 int endPos, startPos;
00478 if (!isPrinting && (selectionState() != SelectionNone)) {
00479 if (selectionState() == SelectionInside) {
00480
00481 startPos = 0;
00482 endPos = str->l;
00483 } else {
00484 selectionStartEnd(startPos, endPos);
00485 if(selectionState() == SelectionStart)
00486 endPos = str->l;
00487 else if(selectionState() == SelectionEnd)
00488 startPos = 0;
00489 }
00490
00491 }
00492
00493 TextSlave* s;
00494 int minx = 1000000;
00495 int maxx = -1000000;
00496 int outlinebox_y = m_lines[si]->m_y;
00497 QPtrList <QRect> linerects;
00498 linerects.setAutoDelete(true);
00499 linerects.append(new QRect());
00500
00501 bool renderOutline = style()->outlineWidth()!=0;
00502
00503 const Font *font = &style()->htmlFont();
00504
00505
00506
00507 do {
00508 s = m_lines[si];
00509
00510 if (isPrinting)
00511 {
00512 int lh = lineHeight( false ) + paddingBottom() + borderBottom();
00513 if (ty+s->m_y < y)
00514 {
00515
00516 continue;
00517 }
00518
00519 if (ty+lh+s->m_y > y+h)
00520 {
00521 RenderRoot *rootObj = root();
00522 if (ty+s->m_y < rootObj->truncatedAt())
00523 rootObj->setTruncatedAt(ty+s->m_y);
00524
00525 break;
00526 }
00527 }
00528
00529 RenderStyle* _style = pseudoStyle && s->m_firstLine ? pseudoStyle : style();
00530
00531 if(_style->font() != p->font()) {
00532 p->setFont(_style->font());
00533 font = &_style->htmlFont();
00534 }
00535
00536 if ((pseudoStyle && s->m_firstLine) ||
00537 (!pseudoStyle && hasSpecialObjects() && parent()->isInline()))
00538 s->paintBoxDecorations(p, _style, this, tx, ty, si == 0, si == (int)m_lines.count()-1);
00539
00540 if(_style->color() != p->pen().color())
00541 p->setPen(_style->color());
00542
00543 if (s->m_len > 0)
00544 font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s, str->l, s->m_start, s->m_len,
00545 s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR);
00546
00547 if(d != TDNONE)
00548 {
00549 p->setPen(_style->textDecorationColor());
00550 s->paintDecoration(p, this, tx, ty, d, si == 0, si == ( int ) m_lines.count()-1);
00551 }
00552
00553 if (!isPrinting && (selectionState() != SelectionNone)) {
00554 int offset = s->m_start;
00555 int sPos = QMAX( startPos - offset, 0 );
00556 int ePos = QMIN( endPos - offset, s->m_len );
00557
00558 if ( sPos < ePos )
00559 s->paintSelection(font, this, p, _style, tx, ty, sPos, ePos);
00560
00561 }
00562 if(renderOutline) {
00563 if(outlinebox_y == s->m_y) {
00564 if(minx > s->m_x) minx = s->m_x;
00565 int newmaxx = s->m_x+s->m_width;
00566
00567 if (parent()->isInline() && si==int(m_lines.count())-1) newmaxx-=paddingRight();
00568 if(maxx < newmaxx) maxx = newmaxx;
00569 }
00570 else {
00571 QRect *curLine = new QRect(minx, outlinebox_y, maxx-minx, m_lineHeight);
00572 linerects.append(curLine);
00573
00574 outlinebox_y = s->m_y;
00575 minx = s->m_x;
00576 maxx = s->m_x+s->m_width;
00577
00578 if (parent()->isInline() && si==int(m_lines.count())-1) maxx-=paddingRight();
00579 }
00580 }
00581 #ifdef BIDI_DEBUG
00582 {
00583 int h = lineHeight( false ) + paddingTop() + paddingBottom() + borderTop() + borderBottom();
00584 QColor c2 = QColor("#0000ff");
00585 drawBorder(p, tx + s->m_x, ty + s->m_y, tx + s->m_x + 1, ty + s->m_y + h,
00586 RenderObject::BSLeft, c2, c2, SOLID, 1, 1);
00587 drawBorder(p, tx + s->m_x + s->m_width, ty + s->m_y, tx + s->m_x + s->m_width + 1, ty + s->m_y + h,
00588 RenderObject::BSRight, c2, c2, SOLID, 1, 1);
00589 }
00590 #endif
00591
00592 } while (++si < (int)m_lines.count() && m_lines[si]->checkVerticalPoint(y-ow, ty, h, m_lineHeight));
00593
00594 if(renderOutline)
00595 {
00596 linerects.append(new QRect(minx, outlinebox_y, maxx-minx, m_lineHeight));
00597 linerects.append(new QRect());
00598 for (unsigned int i = 1; i < linerects.count() - 1; i++)
00599 paintTextOutline(p, tx, ty, *linerects.at(i-1), *linerects.at(i), *linerects.at(i+1));
00600 }
00601 }
00602 }
00603
00604 void RenderText::paint( QPainter *p, int x, int y, int w, int h,
00605 int tx, int ty)
00606 {
00607 if (style()->visibility() != VISIBLE) return;
00608
00609 int s = m_lines.count() - 1;
00610 if ( s < 0 ) return;
00611
00612
00613 if ( ty + m_lines[0]->m_y > y + h + 64 ) return;
00614 if ( ty + m_lines[s]->m_y + m_lines[s]->m_baseline + m_lineHeight + 64 < y ) return;
00615
00616 paintObject(p, x, y, w, h, tx, ty);
00617 }
00618
00619 void RenderText::calcMinMaxWidth()
00620 {
00621 KHTMLAssert( !minMaxKnown() );
00622
00623
00624 m_minWidth = 0;
00625 m_maxWidth = 0;
00626
00627 int add = (parent()->isInline() && parent()->firstChild()==this) ? paddingLeft() + borderLeft() : 0;
00628
00629 int currMinWidth = add;
00630 int currMaxWidth = add;
00631 m_hasReturn = false;
00632 m_hasBreakableChar = false;
00633
00634
00635 const Font *f = htmlFont( false );
00636 int len = str->l;
00637 m_startMin = 0;
00638 m_endMin = 0;
00639 bool isPre = style()->whiteSpace() == PRE;
00640 if ( len == 1 && str->s->latin1() == '\n' )
00641 m_hasReturn = true;
00642 bool first = true;
00643 for(int i = 0; i < len; i++)
00644 {
00645 int wordlen = 0;
00646 if (isPre)
00647 while( i+wordlen < len && str->s[i+wordlen] != '\n' )
00648 wordlen++;
00649 else
00650 while( i+wordlen < len && !(isBreakable( str->s, i+wordlen, str->l )) )
00651 wordlen++;
00652
00653 if (wordlen) {
00654 int w = f->width(str->s, str->l, i, wordlen);
00655 currMinWidth += w;
00656 currMaxWidth += w;
00657 } else {
00658 first = false;
00659 }
00660 if(i+wordlen < len) {
00661 m_hasBreakableChar = true;
00662 if ( (*(str->s+i+wordlen)).latin1() == '\n' ) {
00663 m_hasReturn = true;
00664 if ( first ) m_startMin = QMIN( currMinWidth, 0x1ff );
00665 if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
00666 currMinWidth = 0;
00667 if(currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
00668 currMaxWidth = 0;
00669 } else {
00670 if ( first ) m_startMin = QMIN( currMinWidth , 0x1ff );
00671 if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
00672 currMinWidth = 0;
00673 currMaxWidth += f->width( str->s, str->l, i + wordlen );
00674 }
00675
00676
00677
00678
00679
00680
00681
00682 first = false;
00683 }
00684 i += wordlen;
00685 }
00686 add = (parent()->isInline() && parent()->lastChild()==this) ? borderRight() + paddingRight() : 0;
00687 currMinWidth += add;
00688 currMaxWidth += add;
00689 if ( first ) m_startMin = QMIN( currMinWidth, 0x1ff );
00690 if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
00691 if(currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
00692 m_endMin = QMIN( currMinWidth , 0x1ff );
00693
00694 if (style()->whiteSpace() == NOWRAP) {
00695 m_startMin = QMIN( m_minWidth, 0x1ff );
00696 m_endMin = m_startMin;
00697 m_minWidth = m_maxWidth;
00698 }
00699
00700 setMinMaxKnown();
00701
00702 }
00703
00704 int RenderText::minXPos() const
00705 {
00706 if (!m_lines.count())
00707 return 0;
00708 int retval=6666666;
00709 for (unsigned i=0;i < m_lines.count(); i++)
00710 {
00711 retval = QMIN ( retval, m_lines[i]->m_x);
00712 }
00713 return retval;
00714 }
00715
00716 int RenderText::xPos() const
00717 {
00718 if (m_lines.count())
00719 return m_lines[0]->m_x;
00720 else
00721 return 0;
00722 }
00723
00724 int RenderText::yPos() const
00725 {
00726 if (m_lines.count())
00727 return m_lines[0]->m_y;
00728 else
00729 return 0;
00730 }
00731
00732 const QFont &RenderText::font()
00733 {
00734 return style()->font();
00735 }
00736
00737 void RenderText::setText(DOMStringImpl *text, bool force)
00738 {
00739 if( !force && str == text ) return;
00740 if(str) str->deref();
00741 str = text;
00742
00743 if ( str && style() ) {
00744 if ( style()->fontVariant() == SMALL_CAPS )
00745 str = str->upper();
00746 else
00747 switch(style()->textTransform()) {
00748 case CAPITALIZE: str = str->capitalize(); break;
00749 case UPPERCASE: str = str->upper(); break;
00750 case LOWERCASE: str = str->lower(); break;
00751 case NONE:
00752 default:;
00753 }
00754 str->ref();
00755 }
00756
00757
00758
00759 KHTMLAssert(!isBR() || (str->l == 1 && (*str->s) == '\n'));
00760 KHTMLAssert(!str->l || str->s);
00761
00762 setLayouted(false);
00763 setMinMaxKnown(false);
00764 #ifdef BIDI_DEBUG
00765 QConstString cstr(str->s, str->l);
00766 kdDebug( 6040 ) << "RenderText::setText( " << cstr.string().length() << " ) '" << cstr.string() << "'" << endl;
00767 #endif
00768 }
00769
00770 int RenderText::height() const
00771 {
00772 int retval;
00773 if ( m_lines.count() )
00774 retval = m_lines[m_lines.count()-1]->m_y + m_lineHeight - m_lines[0]->m_y;
00775 else
00776 retval = metrics( false ).height();
00777
00778 retval += paddingTop() + paddingBottom() + borderTop() + borderBottom();
00779
00780 return retval;
00781 }
00782
00783 short RenderText::lineHeight( bool firstLine ) const
00784 {
00785 if ( firstLine )
00786 return RenderObject::lineHeight( firstLine );
00787
00788 return m_lineHeight;
00789 }
00790
00791 short RenderText::baselinePosition( bool firstLine ) const
00792 {
00793 const QFontMetrics &fm = metrics( firstLine );
00794 return fm.ascent() +
00795 ( lineHeight( firstLine ) - fm.height() ) / 2;
00796 }
00797
00798 void RenderText::position(int x, int y, int from, int len, int width, bool reverse, bool firstLine, int spaceAdd)
00799 {
00800
00801
00802
00803 if (len == 0 || (str->l && len == 1 && *(str->s+from) == '\n') ) return;
00804
00805 reverse = reverse && !style()->visuallyOrdered();
00806
00807
00808 if(from == 0 && parent()->isInline() && parent()->firstChild()==this)
00809 {
00810 x += paddingLeft() + borderLeft() + marginLeft();
00811 width -= marginLeft();
00812 }
00813
00814 if(from + len >= int(str->l) && parent()->isInline() && parent()->lastChild()==this)
00815 width -= marginRight();
00816
00817 #ifdef DEBUG_LAYOUT
00818 QChar *ch = str->s+from;
00819 QConstString cstr(ch, len);
00820 #endif
00821
00822 TextSlave *s = new TextSlave(x, y, from, len,
00823 baselinePosition( firstLine ),
00824 width, reverse, spaceAdd, firstLine);
00825
00826 if(m_lines.count() == m_lines.size())
00827 m_lines.resize(m_lines.size()*2+1);
00828
00829 m_lines.insert(m_lines.count(), s);
00830 }
00831
00832 unsigned int RenderText::width(unsigned int from, unsigned int len, bool firstLine) const
00833 {
00834 if(!str->s || from > str->l ) return 0;
00835 if ( from + len > str->l ) len = str->l - from;
00836
00837 const Font *f = htmlFont( firstLine );
00838 return width( from, len, f );
00839 }
00840
00841 unsigned int RenderText::width(unsigned int from, unsigned int len, const Font *f) const
00842 {
00843 if(!str->s || from > str->l ) return 0;
00844 if ( from + len > str->l ) len = str->l - from;
00845
00846 int w;
00847 if ( f == &style()->htmlFont() && from == 0 && len == str->l ) {
00848 w = m_maxWidth;
00849
00850 if(parent()->isInline()) {
00851 if( parent()->firstChild() == static_cast<const RenderObject*>(this) )
00852 w += marginLeft();
00853 if( parent()->lastChild() == static_cast<const RenderObject*>(this) )
00854 w += marginRight();
00855 }
00856 return w;
00857 }
00858
00859 w = f->width(str->s, str->l, from, len );
00860
00861
00862
00863 if(parent()->isInline()) {
00864 if(from == 0 && parent()->firstChild() == static_cast<const RenderObject*>(this))
00865 w += borderLeft() + paddingLeft() + marginLeft();
00866 if(from + len == str->l &&
00867 parent()->lastChild() == static_cast<const RenderObject*>(this))
00868 w += borderRight() + paddingRight() +marginRight();
00869 }
00870
00871
00872 return w;
00873 }
00874
00875 short RenderText::width() const
00876 {
00877 int w;
00878 int minx = 100000000;
00879 int maxx = 0;
00880
00881 for(unsigned int si = 0; si < m_lines.count(); si++) {
00882 TextSlave* s = m_lines[si];
00883 if(s->m_x < minx)
00884 minx = s->m_x;
00885 if(s->m_x + s->m_width > maxx)
00886 maxx = s->m_x + s->m_width;
00887 }
00888
00889 w = QMAX(0, maxx-minx);
00890
00891 if(parent()->isInline())
00892 {
00893 if(parent()->firstChild() == static_cast<const RenderObject*>(this))
00894 w += borderLeft() + paddingLeft();
00895 if(parent()->lastChild() == static_cast<const RenderObject*>(this))
00896 w += borderRight() + paddingRight();
00897 }
00898
00899 return w;
00900 }
00901
00902 void RenderText::repaint()
00903 {
00904 RenderObject *cb = containingBlock();
00905 if(cb != this)
00906 cb->repaint();
00907 }
00908
00909 bool RenderText::isFixedWidthFont() const
00910 {
00911 return QFontInfo(style()->font()).fixedPitch();
00912 }
00913
00914 short RenderText::verticalPositionHint( bool firstLine ) const
00915 {
00916 return parent()->verticalPositionHint( firstLine );
00917 }
00918
00919 const QFontMetrics &RenderText::metrics(bool firstLine) const
00920 {
00921 if( firstLine && hasFirstLine() ) {
00922 RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
00923 if ( pseudoStyle )
00924 return pseudoStyle->fontMetrics();
00925 }
00926 return style()->fontMetrics();
00927 }
00928
00929 const Font *RenderText::htmlFont(bool firstLine) const
00930 {
00931 const Font *f = 0;
00932 if( firstLine && hasFirstLine() ) {
00933 RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
00934 if ( pseudoStyle )
00935 f = &pseudoStyle->htmlFont();
00936 } else {
00937 f = &style()->htmlFont();
00938 }
00939 return f;
00940 }
00941
00942 void RenderText::paintTextOutline(QPainter *p, int tx, int ty, const QRect &lastline, const QRect &thisline, const QRect &nextline)
00943 {
00944 int ow = style()->outlineWidth();
00945 EBorderStyle os = style()->outlineStyle();
00946 QColor oc = style()->outlineColor();
00947
00948 int t = ty + thisline.top();
00949 int l = tx + thisline.left();
00950 int b = ty + thisline.bottom() + 1;
00951 int r = tx + thisline.right() + 1;
00952
00953
00954 drawBorder(p,
00955 l - ow,
00956 t - (lastline.isEmpty() || thisline.left() < lastline.left() || lastline.right() <= thisline.left() ? ow : 0),
00957 l,
00958 b + (nextline.isEmpty() || thisline.left() <= nextline.left() || nextline.right() <= thisline.left() ? ow : 0),
00959 BSLeft,
00960 oc, style()->color(), os,
00961 (lastline.isEmpty() || thisline.left() < lastline.left() || lastline.right() <= thisline.left() ? ow : -ow),
00962 (nextline.isEmpty() || thisline.left() <= nextline.left() || nextline.right() <= thisline.left() ? ow : -ow),
00963 true);
00964
00965
00966 drawBorder(p,
00967 r,
00968 t - (lastline.isEmpty() || lastline.right() < thisline.right() || thisline.right() <= lastline.left() ? ow : 0),
00969 r + ow,
00970 b + (nextline.isEmpty() || nextline.right() <= thisline.right() || thisline.right() <= nextline.left() ? ow : 0),
00971 BSRight,
00972 oc, style()->color(), os,
00973 (lastline.isEmpty() || lastline.right() < thisline.right() || thisline.right() <= lastline.left() ? ow : -ow),
00974 (nextline.isEmpty() || nextline.right() <= thisline.right() || thisline.right() <= nextline.left() ? ow : -ow),
00975 true);
00976
00977 if ( thisline.left() < lastline.left())
00978 drawBorder(p,
00979 l - ow,
00980 t - ow,
00981 QMIN(r+ow, (lastline.isValid()? tx+lastline.left() : 1000000)),
00982 t ,
00983 BSTop, oc, style()->color(), os,
00984 ow,
00985 (lastline.isValid() && tx+lastline.left()+1<r+ow ? -ow : ow),
00986 true);
00987
00988 if (lastline.right() < thisline.right())
00989 drawBorder(p,
00990 QMAX(lastline.isValid()?tx + lastline.right() + 1:-1000000, l - ow),
00991 t - ow,
00992 r + ow,
00993 t ,
00994 BSTop, oc, style()->color(), os,
00995 (lastline.isValid() && l-ow < tx+lastline.right()+1 ? -ow : ow),
00996 ow,
00997 true);
00998
00999
01000 if ( thisline.left() < nextline.left())
01001 drawBorder(p,
01002 l - ow,
01003 b,
01004 QMIN(r+ow, nextline.isValid()? tx+nextline.left()+1 : 1000000),
01005 b + ow,
01006 BSBottom, oc, style()->color(), os,
01007 ow,
01008 (nextline.isValid() && tx+nextline.left()+1<r+ow? -ow : ow),
01009 true);
01010
01011 if (nextline.right() < thisline.right())
01012 drawBorder(p,
01013 QMAX(nextline.isValid()?tx+nextline.right()+1:-1000000 , l-ow),
01014 b,
01015 r + ow,
01016 b + ow,
01017 BSBottom, oc, style()->color(), os,
01018 (nextline.isValid() && l-ow < tx+nextline.right()+1? -ow : ow),
01019 ow,
01020 true);
01021 }
01022
01023 #undef BIDI_DEBUG
01024 #undef DEBUG_LAYOUT