# khtml Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:44 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001