khtml Library API Documentation

render_text.cpp

00001 
00025 //#define DEBUG_LAYOUT
00026 //#define BIDI_DEBUG
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     //kdDebug( 6040 ) << "textSlave::painting(" << s.string() << ") at(" << x+_tx << "/" << y+_ty << ")" << endl;
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     // NO! Do NOT add BLINK! It is the most annouing feature of Netscape, and IE has a reason not to
00082     // support it. Lars
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     // ### firstline
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     // the height of the decorations is:  topBorder + topPadding + CSS font-size + bottomPadding + bottomBorder
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         // ### might need to add some correct offsets
00110         // ### use paddingX/Y
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 //     kdDebug(6040) << "TextSlave::checkSelectionPoint " << this << " _x=" << _x << " _y=" << _y
00125 //                   << " _tx+m_x=" << _tx+m_x << " _ty+m_y=" << _ty+m_y << endl;
00126     offset = 0;
00127 
00128     if ( _y < _ty + m_y )
00129         return SelectionPointBefore; // above -> before
00130 
00131     if ( _y > _ty + m_y + lineHeight ) {
00132         // below -> after
00133         // Set the offset to the max
00134         offset = m_len;
00135         return SelectionPointAfter;
00136     }
00137     if ( _x > _tx + m_x + m_width ) {
00138     // to the right
00139     return m_reversed ? SelectionPointBeforeInLine : SelectionPointAfterInLine;
00140     }
00141 
00142     // The Y matches, check if we're on the left
00143     if ( _x < _tx + m_x ) {
00144         return m_reversed ? SelectionPointAfterInLine : SelectionPointBeforeInLine;
00145     }
00146 
00147     int delta = _x - (_tx + m_x);
00148     //kdDebug(6040) << "TextSlave::checkSelectionPoint delta=" << delta << endl;
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 //     kdDebug( 6040 ) << " Text  --> inside at position " << pos << endl;
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 // remove this once QVector::bsearch is fixed
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 )            // null item greater
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 {                  // found it
00217         found = TRUE;
00218         break;
00219     }
00220     }
00221     /* if ( !found )
00222     return -1; */
00223     // search to first one equal or bigger
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     // init RenderObject attributes
00235     setRenderText();   // our object inherits from RenderText
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         // ### fontvariant being implemented as text-transform: upper. sucks!
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     // this is a slight variant of QArray::clear().
00277     // We don't delete the array itself here because its
00278     // likely to be used in the same size later again, saves
00279     // us resize() calls
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     // The text slaves point to parts of the rendertext's str string
00290     // (they don't include '\n')
00291     // Find the text slave that includes the character at @p offset
00292     // and return pos, which is the position of the char in the slave.
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     // we are now in the correct text slave
00306     pos = (offset > off ? s->m_len : offset - s->m_start );
00307     return s;
00308 }
00309 
00310 bool RenderText::nodeAtPoint(NodeInfo& /*info*/, 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 //     kdDebug(6040) << "RenderText::checkSelectionPoint " << this << " _x=" << _x << " _y=" << _y
00343 //                   << " _tx=" << _tx << " _ty=" << _ty << endl;
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 //         kdDebug(6040) << "RenderText::checkSelectionPoint " << this << " line " << si << " result=" << result << " offset=" << offset << endl;
00354         if ( result == SelectionPointInside ) // x,y is inside the textslave
00355         {
00356             offset += s->m_start; // add the offset from the previous lines
00357             //kdDebug(6040) << "RenderText::checkSelectionPoint inside -> " << offset << endl;
00358             node = element();
00359             return SelectionPointInside;
00360         } else if ( result == SelectionPointBefore || result == SelectionPointBeforeInLine ) {
00361             // x,y is before the textslave -> stop here
00362             if ( si > 0 && lastPointAfterInline ) {
00363                 offset = lastPointAfterInline->m_start + lastPointAfterInline->m_len;
00364                 //kdDebug(6040) << "RenderText::checkSelectionPoint before -> " << offset << endl;
00365                 node = element();
00366                 return SelectionPointInside;
00367             } else {
00368                 offset = 0;
00369                 //kdDebug(6040) << "RenderText::checkSelectionPoint " << this << "before us -> returning Before" << endl;
00370                 node = element();
00371                 return SelectionPointBefore;
00372             }
00373         } else if ( result == SelectionPointAfterInLine ) {
00374         lastPointAfterInline = s;
00375     }
00376 
00377     }
00378 
00379     // set offset to max
00380     offset = str->l;
00381     //qDebug("setting node to %p", element());
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; // ### firstLine!!! s->m_height;
00397 
00398   const QFontMetrics &fm = metrics( false ); // #### wrong for first-line!
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     // we don't know our absolute position, and there is not point returning
00414     // just a relative one
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         // s is the line containing the character
00443         x += s->m_x; // this is the x of the beginning of the line, but it's good enough for now
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 /*x*/, int y, int /*w*/, 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     // something matching found, find the first one to paint
00468     bool isPrinting = (p->device()->devType() == QInternal::Printer);
00469     if(si >= 0)
00470     {
00471         // Move up until out of area to be painted
00472         while(si > 0 && m_lines[si-1]->checkVerticalPoint(y, ty, h, m_lineHeight))
00473             si--;
00474 
00475         // Now calculate startPos and endPos, for painting selection.
00476         // We paint selection while endPos > 0
00477         int endPos, startPos;
00478         if (!isPrinting && (selectionState() != SelectionNone)) {
00479             if (selectionState() == SelectionInside) {
00480                 //kdDebug(6040) << this << " SelectionInside -> 0 to end" << endl;
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             //kdDebug(6040) << this << " Selection from " << startPos << " to " << endPos << endl;
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         // run until we find one that is outside the range, then we
00506         // know we can stop
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                    // This has been painted already we suppose.
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                    // Let's stop here.
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                 //kdDebug(6040) << this << " paintSelection with startPos=" << sPos << " endPos=" << ePos << endl;
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                     //if (parent()->isInline() && si==0) newmaxx-=paddingLeft();
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                     //if (parent()->isInline() && si==0) maxx-=paddingLeft();
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     // ### incorporate padding/border here!
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     // ### calc Min and Max width...
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     // ### not 100% correct for first-line
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             /* else if( c == '-')
00676             {
00677                 currMinWidth += minus_width;
00678                 if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
00679                 currMinWidth = 0;
00680                 currMaxWidth += minus_width;
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     //kdDebug( 6040 ) << "Text::calcMinMaxWidth(): min = " << m_minWidth << " max = " << m_maxWidth << endl;
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     // ### what should happen if we change the text of a
00758     // RenderBR object ?
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     // ### should not be needed!!!
00801     // asserts sometimes with pre (that unibw-hamburg testcase). ### find out why
00802     //KHTMLAssert(!(len == 0 || (str->l && len == 1 && *(str->s+from) == '\n') ));
00803     if (len == 0 || (str->l && len == 1 && *(str->s+from) == '\n') ) return;
00804 
00805     reverse = reverse && !style()->visuallyOrdered();
00806 
00807     // ### margins and RTL
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      // m_maxWidth already contains borders and paddings.
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     // ### add margins and support for RTL
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     //kdDebug( 6040 ) << "RenderText::width(" << from << ", " << len << ") = " << w << endl;
00872     return w;
00873 }
00874 
00875 short RenderText::width() const
00876 {
00877     int w;
00878     int minx = 100000000;
00879     int maxx = 0;
00880     // slooow
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   // left edge
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   // right edge
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   // upper edge
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   // lower edge
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
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:16:40 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001