Add a truncation variable to text run boxes that will eventually be used to know...
[WebKit-https.git] / WebCore / khtml / rendering / render_text.cpp
1 /**
2  * This file is part of the DOM implementation for KDE.
3  *
4  * (C) 1999 Lars Knoll (knoll@kde.org)
5  * (C) 2000 Dirk Mueller (mueller@kde.org)
6  * Copyright (C) 2004 Apple Computer, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24 //#define DEBUG_LAYOUT
25 //#define BIDI_DEBUG
26
27 #include "rendering/render_canvas.h"
28 #include "rendering/render_text.h"
29 #include "rendering/break_lines.h"
30 #include "xml/dom_nodeimpl.h"
31 #include "xml/dom_docimpl.h"
32 #include "xml/dom_position.h"
33 #include "render_arena.h"
34
35 #include "misc/loader.h"
36
37 #include <qpainter.h>
38 #include <kdebug.h>
39 #include <assert.h>
40
41 using namespace khtml;
42 using namespace DOM;
43
44 #ifndef NDEBUG
45 static bool inInlineTextBoxDetach;
46 #endif
47
48 void InlineTextBox::detach(RenderArena* renderArena)
49 {
50 #ifndef NDEBUG
51     inInlineTextBoxDetach = true;
52 #endif
53     delete this;
54 #ifndef NDEBUG
55     inInlineTextBoxDetach = false;
56 #endif
57     
58     // Recover the size left there for us by operator delete and free the memory.
59     renderArena->free(*(size_t *)this, this);
60 }
61
62 void* InlineTextBox::operator new(size_t sz, RenderArena* renderArena) throw()
63 {
64     return renderArena->allocate(sz);
65 }
66
67 void InlineTextBox::operator delete(void* ptr, size_t sz)
68 {
69     assert(inInlineTextBoxDetach);
70     
71     // Stash size where detach can find it.
72     *(size_t *)ptr = sz;
73 }
74
75 void InlineTextBox::deleteLine(RenderArena* arena)
76 {
77     static_cast<RenderText*>(m_object)->removeTextBox(this);
78     detach(arena);
79 }
80
81 void InlineTextBox::extractLine()
82 {
83     if (m_extracted)
84         return;
85
86     static_cast<RenderText*>(m_object)->extractTextBox(this);
87 }
88
89 void InlineTextBox::attachLine()
90 {
91     if (!m_extracted)
92         return;
93     
94     static_cast<RenderText*>(m_object)->attachTextBox(this);
95 }
96
97 void InlineTextBox::paintSelection(const Font *f, RenderText *text, QPainter *p, RenderStyle* style, int tx, int ty, int startPos, int endPos, bool extendSelection)
98 {
99     int offset = m_start;
100     int sPos = kMax(startPos - offset, 0);
101     int ePos = kMin(endPos - offset, (int)m_len);
102
103     if (sPos >= ePos)
104         return;
105
106     p->save();
107 #if APPLE_CHANGES
108     // Macintosh-style text highlighting is to draw with a particular background color, not invert.
109     QColor textColor = style->color();
110     QColor c = p->selectedTextBackgroundColor();
111     
112     // if text color and selection background color are identical, invert background color.
113     if (textColor == c)
114         c = QColor(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
115
116     RenderStyle* pseudoStyle = object()->getPseudoStyle(RenderStyle::SELECTION);
117     if (pseudoStyle && pseudoStyle->backgroundColor().isValid())
118         c = pseudoStyle->backgroundColor();
119     p->setPen(c); // Don't draw text at all!
120     
121 #else
122     QColor c = style->color();
123     p->setPen(QColor(0xff-c.red(),0xff-c.green(),0xff-c.blue()));
124     ty + m_baseline;
125 #endif
126     
127 #if APPLE_CHANGES
128     // Do the calculations to draw selections as tall as the line.
129     // Use the bottom of the line above as the y position (if there is one, 
130     // otherwise use the top of this renderer's line) and the height of the line as the height. 
131     // This mimics Cocoa.
132     RenderBlock *cb = object()->containingBlock();
133
134     if (root()->prevRootBox())
135         ty = root()->prevRootBox()->bottomOverflow();
136     else
137         ty = root()->topOverflow();
138
139     int h = root()->bottomOverflow() - ty;
140
141     int absx, absy;
142     cb->absolutePosition(absx, absy);
143     
144     int x = m_x + tx;
145     int minX = x;
146     int maxX = x;
147     if ((extendSelection || startPos < m_start) && root()->firstLeafChild() == this)
148         minX = absx + kMax(cb->leftOffset(ty), cb->leftOffset(root()->blockHeight()));
149     if ((extendSelection || endPos > m_start + m_len) && root()->lastLeafChild() == this)
150         maxX = absx + kMin(cb->rightOffset(ty), cb->rightOffset(root()->blockHeight()));
151     
152     f->drawHighlightForText(p, x, minX, maxX, absy + ty, h, text->str->s, text->str->l, m_start, m_len,
153                 m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, style->visuallyOrdered(), sPos, ePos, c);
154 #else
155     f->drawHighlightForText(p, m_x + tx, m_y + ty, text->str->s, text->str->l, m_start, m_len,
156                 m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, sPos, ePos, c);
157 #endif
158     p->restore();
159 }
160
161 #ifdef APPLE_CHANGES
162 void InlineTextBox::paintDecoration( QPainter *pt, int _tx, int _ty, int deco)
163 {
164     _tx += m_x;
165     _ty += m_y;
166
167     // Get the text decoration colors.
168     QColor underline, overline, linethrough;
169     object()->getTextDecorationColors(deco, underline, overline, linethrough, true);
170     
171     // Use a special function for underlines to get the positioning exactly right.
172     if (deco & UNDERLINE) {
173         pt->setPen(underline);
174         pt->drawLineForText(_tx, _ty, m_baseline, m_width);
175     }
176     if (deco & OVERLINE) {
177         pt->setPen(overline);
178         pt->drawLineForText(_tx, _ty, 0, m_width);
179     }
180     if (deco & LINE_THROUGH) {
181         pt->setPen(linethrough);
182         pt->drawLineForText(_tx, _ty, 2*m_baseline/3, m_width);
183     }
184 }
185 #else
186 void InlineTextBox::paintDecoration( QPainter *pt, int _tx, int _ty, int decoration)
187 {
188     _tx += m_x;
189     _ty += m_y;
190
191     int width = m_width - 1;
192
193     QColor underline, overline, linethrough;
194     object()->getTextDecorationColors(decoration, underline, overline, linethrough, true);
195     
196     int underlineOffset = ( pt->fontMetrics().height() + m_baseline ) / 2;
197     if(underlineOffset <= m_baseline) underlineOffset = m_baseline+1;
198
199     if(deco & UNDERLINE){
200         pt->setPen(underline);
201         pt->drawLine(_tx, _ty + underlineOffset, _tx + width, _ty + underlineOffset );
202     }
203     if (deco & OVERLINE) {
204         pt->setPen(overline);
205         pt->drawLine(_tx, _ty, _tx + width, _ty );
206     }
207     if(deco & LINE_THROUGH) {
208         pt->setPen(linethrough);
209         pt->drawLine(_tx, _ty + 2*m_baseline/3, _tx + width, _ty + 2*m_baseline/3 );
210     }
211     // NO! Do NOT add BLINK! It is the most annouing feature of Netscape, and IE has a reason not to
212     // support it. Lars
213 }
214 #endif
215
216 long InlineTextBox::caretMinOffset() const
217 {
218     return m_start;
219 }
220
221 long InlineTextBox::caretMaxOffset() const
222 {
223     return m_start + m_len;
224 }
225
226 unsigned long InlineTextBox::caretMaxRenderedOffset() const
227 {
228     return m_start + m_len;
229 }
230
231 #define LOCAL_WIDTH_BUF_SIZE    1024
232
233 int InlineTextBox::offsetForPosition(int _x, int _tx, const Font *f, const RenderText *text)
234 {
235 #if APPLE_CHANGES
236     return f->checkSelectionPoint(text->str->s, text->str->l, m_start, m_len, m_toAdd, _x - (_tx + m_x), m_reversed);
237 #else
238     int pos = 0;
239     int delta = _x - (_tx + m_x);
240     if (m_reversed) {
241         delta -= m_width;
242         while (pos < m_len) {
243             int w = f->width( text->str->s, text->str->l, m_start + pos);
244             int w2 = w/2;
245             w -= w2;
246             delta += w2;
247             if(delta >= 0) break;
248             pos++;
249             delta += w;
250         }
251     } 
252     else {
253         while (pos < m_len) {
254             int w = f->width( text->str->s, text->str->l, m_start + pos);
255             int w2 = w/2;
256             w -= w2;
257             delta -= w2;
258             if(delta <= 0) break;
259             pos++;
260             delta -= w;
261         }
262     }
263     return pos;
264 #endif
265 }
266
267 // -------------------------------------------------------------------------------------
268
269 RenderText::RenderText(DOM::NodeImpl* node, DOMStringImpl *_str)
270     : RenderObject(node), m_linesDirty(false)
271 {
272     // init RenderObject attributes
273     setRenderText();   // our object inherits from RenderText
274
275     m_minWidth = -1;
276     m_maxWidth = -1;
277
278 #ifdef APPLE_CHANGES
279     m_monospaceCharacterWidth = 0;
280     m_allAsciiChecked = false;
281     m_allAscii = false;
282 #endif
283
284     str = _str;
285     if (str) {
286         str = str->replace('\\', backslashAsCurrencySymbol());
287         str->ref();
288     }
289     KHTMLAssert(!str || !str->l || str->s);
290
291     m_firstTextBox = m_lastTextBox = 0;
292     
293     m_selectionState = SelectionNone;
294
295 #ifdef DEBUG_LAYOUT
296     QConstString cstr(str->s, str->l);
297     kdDebug( 6040 ) << "RenderText ctr( "<< cstr.string().length() << " )  '" << cstr.string() << "'" << endl;
298 #endif
299 }
300
301 void RenderText::setStyle(RenderStyle *_style)
302 {
303     if ( style() != _style ) {
304         bool needToTransformText = (!style() && _style->textTransform() != TTNONE) ||
305                                    (style() && style()->textTransform() != _style->textTransform());
306
307         RenderObject::setStyle( _style );
308
309         if (needToTransformText) {
310             DOM::DOMStringImpl* textToTransform = originalString();
311             if (textToTransform)
312                 setText(textToTransform, true);
313         }
314 #if APPLE_CHANGES
315         // setText also calls cacheWidths(), so there is no need to call it again in that case.
316         else
317             cacheWidths();
318 #endif
319     }
320 }
321
322 RenderText::~RenderText()
323 {
324     if(str) str->deref();
325 }
326
327 void RenderText::detach()
328 {
329     if (!documentBeingDestroyed()) {
330         if (firstTextBox())
331             for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
332                 box->remove();
333         else if (parent() && isBR())
334             parent()->dirtyLinesFromChangedChild(this);
335     }
336     deleteTextBoxes();
337     RenderObject::detach();
338 }
339
340 void RenderText::extractTextBox(InlineTextBox* box)
341 {
342     m_lastTextBox = box->prevTextBox();
343     if (box == m_firstTextBox)
344         m_firstTextBox = 0;
345     if (box->prevTextBox())
346         box->prevTextBox()->setNextLineBox(0);
347     box->setPreviousLineBox(0);
348     for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
349         curr->setExtracted();
350 }
351
352 void RenderText::attachTextBox(InlineTextBox* box)
353 {
354     if (m_lastTextBox) {
355         m_lastTextBox->setNextLineBox(box);
356         box->setPreviousLineBox(m_lastTextBox);
357     }
358     else
359         m_firstTextBox = box;
360     InlineTextBox* last = box;
361     for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
362         curr->setExtracted(false);
363         last = curr;
364     }
365     m_lastTextBox = last;
366 }
367
368 void RenderText::removeTextBox(InlineTextBox* box)
369 {
370     if (box == m_firstTextBox)
371         m_firstTextBox = box->nextTextBox();
372     if (box == m_lastTextBox)
373         m_lastTextBox = box->prevTextBox();
374     if (box->nextTextBox())
375         box->nextTextBox()->setPreviousLineBox(box->prevTextBox());
376     if (box->prevTextBox())
377         box->prevTextBox()->setNextLineBox(box->nextTextBox());
378 }
379
380 void RenderText::deleteTextBoxes()
381 {
382     if (firstTextBox()) {
383         RenderArena* arena = renderArena();
384         InlineTextBox *curr = firstTextBox(), *next = 0;
385         while (curr) {
386             next = curr->nextTextBox();
387             curr->detach(arena);
388             curr = next;
389         }
390         m_firstTextBox = m_lastTextBox = 0;
391     }
392 }
393
394 bool RenderText::isTextFragment() const
395 {
396     return false;
397 }
398
399 DOM::DOMStringImpl* RenderText::originalString() const
400 {
401     return element() ? element()->string() : 0;
402 }
403
404 void RenderText::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
405 {
406     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
407         rects.append(QRect(_tx + box->xPos(), 
408                            _ty + box->yPos(), 
409                            box->width(), 
410                            box->height()));
411 }
412
413 InlineTextBox* RenderText::findNextInlineTextBox(int offset, int &pos)
414 {
415     // The text runs point to parts of the rendertext's str string
416     // (they don't include '\n')
417     // Find the text run that includes the character at @p offset
418     // and return pos, which is the position of the char in the run.
419
420     if (!m_firstTextBox)
421         return 0;
422     
423     InlineTextBox* s = m_firstTextBox;
424     int off = s->m_len;
425     while (offset > off && s->nextTextBox())
426     {
427         s = s->nextTextBox();
428         off = s->m_start + s->m_len;
429     }
430     // we are now in the correct text run
431     pos = (offset > off ? s->m_len : s->m_len - (off - offset) );
432     return s;
433 }
434
435 bool RenderText::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
436                              HitTestAction hitTestAction, bool inside)
437 {
438     assert(parent());
439
440     for (InlineTextBox *s = m_firstTextBox; s; s = s->nextTextBox()) {
441         if((_y >=_ty + s->m_y) && (_y < _ty + s->m_y + s->height()) &&
442            (_x >= _tx + s->m_x) && (_x <_tx + s->m_x + s->m_width) ) {
443             inside = true;
444             break;
445         }
446     }
447
448     if (inside && element()) {
449         if (info.innerNode() && info.innerNode()->renderer() && 
450             !info.innerNode()->renderer()->isInline()) {
451             // Within the same layer, inlines are ALWAYS fully above blocks.  Change inner node.
452             info.setInnerNode(element());
453             
454             // Clear everything else.
455             info.setInnerNonSharedNode(0);
456             info.setURLElement(0);
457         }
458         
459         if (!info.innerNode())
460             info.setInnerNode(element());
461
462         if (!info.innerNonSharedNode())
463             info.setInnerNonSharedNode(element());
464     }
465
466     return inside;
467 }
468
469 Position RenderText::positionForCoordinates(int _x, int _y)
470 {
471     if (!firstTextBox() || stringLength() == 0)
472         return Position(element(), 0);
473
474     int absx, absy;
475     containingBlock()->absolutePosition(absx, absy);
476
477     if (firstTextBox() && _y < absy + firstTextBox()->root()->bottomOverflow() && _x < absx + firstTextBox()->m_x) {
478         // at the y coordinate of the first line or above
479         // and the x coordinate is to the left than the first text box left edge
480         return Position(element(), firstTextBox()->m_start);
481     }
482
483     if (lastTextBox() && _y >= absy + lastTextBox()->root()->topOverflow() && _x >= absx + lastTextBox()->m_x + lastTextBox()->m_width) {
484         // at the y coordinate of the last line or below
485         // and the x coordinate is to the right than the last text box right edge
486         return Position(element(), lastTextBox()->m_start + lastTextBox()->m_len);
487     }
488
489     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
490         if (_y >= absy + box->root()->topOverflow() && _y < absy + box->root()->bottomOverflow()) {
491             if (_x < absx + box->m_x + box->m_width) {
492                 // and the x coordinate is to the left of the right edge of this box
493                 // check to see if position goes in this box
494                 const Font *f = htmlFont(box == firstTextBox());
495                 int offset = box->offsetForPosition(_x, absx, f, this);
496                 if (offset != -1) {
497                     return Position(element(), offset + box->m_start);
498                 }
499             }
500             else if (!box->prevOnLine() && _x < absx + box->m_x)
501                 // box is first on line
502                 // and the x coordinate is to the left than the first text box left edge
503                 return Position(element(), box->m_start);
504             else if (!box->nextOnLine() && _x >= absx + box->m_x + box->m_width)
505                 // box is last on line
506                 // and the x coordinate is to the right than the last text box right edge
507                 return Position(element(), box->m_start + box->m_len);
508         }
509     }
510     
511     return Position(element(), 0);
512 }
513
514 void RenderText::caretPos(int offset, bool override, int &_x, int &_y, int &width, int &height)
515 {
516     if (!firstTextBox() || stringLength() == 0) {
517         _x = _y = height = -1;
518         return;
519     }
520
521     // Find the text box for the given offset
522     InlineTextBox *box = 0;
523     for (box = firstTextBox(); box; box = box->nextTextBox()) {
524         if (offset <= box->m_start + box->m_len)
525             break;
526     }
527     
528     if (!box) {
529         _x = _y = height = -1;
530         return;
531     }
532
533     height = box->root()->bottomOverflow() - box->root()->topOverflow();
534     _y = box->root()->topOverflow();
535
536     const QFontMetrics &fm = metrics(box->isFirstLineStyle());
537     QString string(str->s + box->m_start, box->m_len);
538     long pos = offset - box->m_start; // the number of characters we are into the string
539     _x = box->m_x + (fm.boundingRect(string, pos)).right();
540
541 #if 0
542     // EDIT FIXME
543     if (pos)
544         _x += fm.rightBearing(*(str->s + box->m_start + offset));
545 #endif
546
547     int absx, absy;
548     absolutePosition(absx,absy);
549     _x += absx;
550     _y += absy;
551 }
552
553 void RenderText::posOfChar(int chr, int &x, int &y)
554 {
555     absolutePosition( x, y, false );
556
557     //if( chr > (int) str->l )
558     //chr = str->l;
559
560     int pos;
561     InlineTextBox * s = findNextInlineTextBox( chr, pos );
562
563     if ( s )
564     {
565         // s is the line containing the character
566         x += s->m_x; // this is the x of the beginning of the line, but it's good enough for now
567         y += s->m_y;
568     }
569 }
570
571 static int
572 simpleDifferenceBetweenColors(QColor c1, QColor c2)
573 {
574     // a distance could be computed by squaring the differences between components, but
575     // this is faster and so far seems good enough for our purposes.
576     return abs(c1.red() - c2.red()) + abs(c1.green() - c2.green()) + abs(c1.blue() - c2.blue());
577 }
578
579 static QColor 
580 correctedTextColor(QColor textColor, QColor backgroundColor) 
581 {
582     // Adjust the text color if it is too close to the background color,
583     // by darkening or lightening it to move it further away.
584     
585     int d = simpleDifferenceBetweenColors(textColor, backgroundColor);
586     // semi-arbitrarily chose 255 value here after a few tests; 
587     if (d > 255) {
588         return textColor;
589     }
590     
591     int distanceFromWhite = simpleDifferenceBetweenColors(textColor, Qt::white);
592     int distanceFromBlack = simpleDifferenceBetweenColors(textColor, Qt::black);
593
594     if (distanceFromWhite < distanceFromBlack) {
595         return textColor.dark();
596     }
597     
598     return textColor.light();
599 }
600
601 void RenderText::paint(PaintInfo& i, int tx, int ty)
602 {
603     if (i.phase != PaintActionForeground && i.phase != PaintActionSelection)
604         return;
605     
606     if (!shouldPaintWithinRoot(i))
607         return;
608         
609     if (style()->visibility() != VISIBLE || !firstTextBox())
610         return;
611     
612     if (ty + firstTextBox()->yPos() > i.r.y() + i.r.height()) return;
613     if (ty + lastTextBox()->yPos() + lastTextBox()->height() < i.r.y()) return;
614     
615     QPainter* p = i.p;
616     RenderStyle* pseudoStyle = style(true);
617     if (pseudoStyle == style()) pseudoStyle = 0;
618     int d = style()->textDecorationsInEffect();
619     bool isPrinting = (p->device()->devType() == QInternal::Printer);
620     
621     // Walk forward until we hit the first line that needs to be painted.
622     InlineTextBox* s = firstTextBox();
623     for (; s && !s->checkVerticalPoint(i.r.y(), ty, i.r.height()); s = s->nextTextBox());
624     if (!s) return;
625     
626     // Now calculate startPos and endPos, for painting selection.
627     // We paint selection while endPos > 0
628     int endPos, startPos;
629     if (!isPrinting && (selectionState() != SelectionNone)) {
630         if (selectionState() == SelectionInside) {
631             //kdDebug(6040) << this << " SelectionInside -> 0 to end" << endl;
632             startPos = 0;
633             endPos = str->l;
634         } else {
635             selectionStartEnd(startPos, endPos);
636             if(selectionState() == SelectionStart)
637                 endPos = str->l;
638             else if(selectionState() == SelectionEnd)
639                 startPos = 0;
640         }
641         //kdDebug(6040) << this << " Selection from " << startPos << " to " << endPos << endl;
642     }
643
644     const Font *font = &style()->htmlFont();
645
646 #if APPLE_CHANGES
647     // Do one pass for the selection, then another for the rest.
648     bool haveSelection = startPos != endPos && !isPrinting && selectionState() != SelectionNone;
649     if (!haveSelection && i.phase == PaintActionSelection) {
650         // When only painting the selection, don't bother to paint if there is none.
651         return;
652     }
653
654     InlineTextBox* startBox = s;
655     for (int pass = 0; pass < (haveSelection ? 2 : 1); pass++) {
656         s = startBox;
657         bool drawSelectionBackground = haveSelection && pass == 0 && i.phase != PaintActionSelection;
658         bool drawText = !haveSelection || pass == 1;
659 #endif
660
661     // run until we find one that is outside the range, then we
662     // know we can stop
663     do {
664         if (isPrinting)
665         {
666             if (ty+s->m_y+s->height() > i.r.y() + i.r.height())
667             {
668                RenderCanvas* canvasObj = canvas();
669                if (ty+s->m_y < canvasObj->truncatedAt())
670 #if APPLE_CHANGES
671                    canvasObj->setBestTruncatedAt(ty+s->m_y, this);
672 #else
673                    canvasObj->setTruncatedAt(ty+s->m_y);
674 #endif
675                // Let's stop here.
676                break;
677             }
678         }
679
680         RenderStyle* _style = pseudoStyle && s->m_firstLine ? pseudoStyle : style();
681
682         if (_style->font() != p->font())
683             p->setFont(_style->font());
684
685         font = &_style->htmlFont(); // Always update, since smallCaps is not stored in the QFont.
686
687 #if APPLE_CHANGES
688         if (drawText) {
689 #endif
690         
691         QColor textColor = _style->color();
692         if (_style->shouldCorrectTextColor()) {
693             textColor = correctedTextColor(textColor, _style->backgroundColor());
694         }
695
696         if(textColor != p->pen().color())
697             p->setPen(textColor);
698
699 #if APPLE_CHANGES
700         // Set a text shadow if we have one.
701         // FIXME: Support multiple shadow effects.  Need more from the CG API before
702         // we can do this.
703         bool setShadow = false;
704         if (_style->textShadow()) {
705             p->setShadow(_style->textShadow()->x, _style->textShadow()->y,
706                          _style->textShadow()->blur, _style->textShadow()->color);
707             setShadow = true;
708         }
709 #endif
710         
711         if (s->m_len > 0) {
712             bool paintSelectedTextOnly = (i.phase == PaintActionSelection);
713             bool paintSelectedTextSeparately = false; // Whether or not we have to do multiple paints.  Only
714                                            // necessary when a custom ::selection foreground color is applied.
715             QColor selectionColor = p->pen().color();
716             ShadowData* selectionTextShadow = 0;
717             if (haveSelection) {
718                 RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SELECTION);
719                 if (pseudoStyle) {
720                     if (pseudoStyle->color() != selectionColor || pseudoStyle->textShadow()) {
721                         if (!paintSelectedTextOnly)
722                             paintSelectedTextSeparately = true;
723                         if (pseudoStyle->color() != selectionColor)
724                             selectionColor = pseudoStyle->color();
725                         if (pseudoStyle->textShadow())
726                             selectionTextShadow = pseudoStyle->textShadow();
727                     }
728                 }
729             }
730             
731             if (!paintSelectedTextOnly && !paintSelectedTextSeparately) {
732 #if APPLE_CHANGES
733                 font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline,
734                                str->s, str->l, s->m_start, s->m_len,
735                                s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered());
736 #else
737                 font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline,
738                                str->s, str->l, s->m_start, s->m_len,
739                                s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR);
740 #endif
741             }
742             else {
743                 int offset = s->m_start;
744                 int sPos = QMAX( startPos - offset, 0 );
745                 int ePos = QMIN( endPos - offset, s->m_len );
746                 if (paintSelectedTextSeparately) {
747                     if (sPos >= ePos)
748 #if APPLE_CHANGES
749                         font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline,
750                                        str->s, str->l, s->m_start, s->m_len,
751                                        s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered());
752 #else
753                         font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline,
754                                        str->s, str->l, s->m_start, s->m_len,
755                                        s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR);
756 #endif
757                     else {
758                         if (sPos-1 >= 0)
759 #if APPLE_CHANGES
760                             font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
761                                         str->l, s->m_start, s->m_len,
762                                         s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered(), 0, sPos);
763 #else
764                             font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
765                                         str->l, s->m_start, s->m_len,
766                                         s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, 0, sPos);
767 #endif
768                         if (ePos < s->m_start+s->m_len)
769 #if APPLE_CHANGES
770                             font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
771                                         str->l, s->m_start, s->m_len,
772                                         s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered(), ePos, -1);
773 #else
774                             font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
775                                         str->l, s->m_start, s->m_len,
776                                         s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, ePos, -1);
777 #endif
778                     }
779                 }
780                 
781                 if ( sPos < ePos ) {
782                     if (selectionColor != p->pen().color())
783                         p->setPen(selectionColor);
784
785 #if APPLE_CHANGES
786                     if (selectionTextShadow)
787                         p->setShadow(selectionTextShadow->x,
788                                      selectionTextShadow->y,
789                                      selectionTextShadow->blur,
790                                      selectionTextShadow->color);
791 #endif                       
792
793 #if APPLE_CHANGES
794                     font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
795                                    str->l, s->m_start, s->m_len,
796                                    s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered(), sPos, ePos);
797 #else
798                     font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
799                                    str->l, s->m_start, s->m_len,
800                                    s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, sPos, ePos);
801 #endif
802
803 #if APPLE_CHANGES
804                     if (selectionTextShadow)
805                         p->clearShadow();
806 #endif
807                 }
808             } 
809         }
810         
811         if (d != TDNONE && i.phase == PaintActionForeground &&
812             style()->htmlHacks()) {
813             p->setPen(_style->color());
814             s->paintDecoration(p, tx, ty, d);
815         }
816
817 #if APPLE_CHANGES
818         if (setShadow)
819             p->clearShadow();
820         
821         } // drawText
822 #endif
823
824 #if APPLE_CHANGES
825         if (drawSelectionBackground)
826 #endif
827         if (!isPrinting && (selectionState() != SelectionNone))
828             s->paintSelection(font, this, p, _style, tx, ty, startPos, endPos, selectionState() == SelectionInside);
829
830 #ifdef BIDI_DEBUG
831         {
832             int h = lineHeight( false ) + paddingTop() + paddingBottom() + borderTop() + borderBottom();
833             QColor c2 = QColor("#0000ff");
834             drawBorder(p, tx, ty, tx+1, ty + h,
835                           RenderObject::BSLeft, c2, c2, SOLID, 1, 1);
836             drawBorder(p, tx + s->m_width, ty, tx + s->m_width + 1, ty + h,
837                           RenderObject::BSRight, c2, c2, SOLID, 1, 1);
838         }
839 #endif
840
841     } while (((s = s->nextTextBox()) != 0) && s->checkVerticalPoint(i.r.y(), ty, i.r.height()));
842
843 #if APPLE_CHANGES
844     } // end of for loop
845 #endif
846 }
847
848 #ifdef APPLE_CHANGES
849
850 bool RenderText::allAscii() const
851 {
852     if (m_allAsciiChecked)
853         return m_allAscii;
854     m_allAsciiChecked = true;
855     
856     unsigned int i;
857     for (i = 0; i < str->l; i++){
858         if (str->s[i].unicode() >= 0x7f){
859             m_allAscii = false;
860             return m_allAscii;
861         }
862     }
863     
864     m_allAscii = true;
865     
866     return m_allAscii;
867 }
868
869 bool RenderText::shouldUseMonospaceCache(const Font *f) const
870 {
871     return (f && f->isFixedPitch() && allAscii() && !style()->htmlFont().isSmallCaps());
872 }
873
874 // We cache the width of the ' ' character for <pre> text.  We could go futher
875 // and cache a widths array for all styles, at the expense of increasing the size of the
876 // RenderText.
877 void RenderText::cacheWidths()
878 {
879     const Font *f = htmlFont( false );
880     
881     if (shouldUseMonospaceCache(f)){    
882         float fw;
883         QChar c(' ');
884         f->floatCharacterWidths( &c, 1, 0, 1, 0, &fw);
885         m_monospaceCharacterWidth = (int)fw;
886     }
887     else
888         m_monospaceCharacterWidth = 0;
889 }
890
891
892 inline int RenderText::widthFromCache(const Font *f, int start, int len) const
893 {
894     if (m_monospaceCharacterWidth != 0){
895         int i, w = 0;
896         for (i = start; i < start+len; i++){
897             int dir = str->s[i].direction();
898             if (dir != QChar::DirNSM && dir != QChar::DirBN)
899                 w += m_monospaceCharacterWidth;
900         }
901         return w;
902     }
903     
904     return f->width(str->s, str->l, start, len);
905 }
906 #ifdef XXX
907 inline int RenderText::widthFromCache(const Font *f, int start, int len) const
908 {
909     if (m_monospaceCharacterWidth != 0){
910         return len * m_monospaceCharacterWidth;
911     }
912
913     return f->width(str->s, str->l, start, len);
914 }
915 #endif
916
917 #endif
918
919 void RenderText::trimmedMinMaxWidth(int& beginMinW, bool& beginWS, 
920                                     int& endMinW, bool& endWS,
921                                     bool& hasBreakableChar, bool& hasBreak,
922                                     int& beginMaxW, int& endMaxW,
923                                     int& minW, int& maxW, bool& stripFrontSpaces)
924 {
925     bool isPre = style()->whiteSpace() == PRE;
926     if (isPre)
927         stripFrontSpaces = false;
928     
929     int len = str->l;
930     if (len == 0 || (stripFrontSpaces && str->containsOnlyWhitespace())) {
931         maxW = 0;
932         hasBreak = false;
933         return;
934     }
935     
936     minW = m_minWidth;
937     maxW = m_maxWidth;
938     beginWS = stripFrontSpaces ? false : m_hasBeginWS;
939     endWS = m_hasEndWS;
940     
941     beginMinW = m_beginMinWidth;
942     endMinW = m_endMinWidth;
943     
944     hasBreakableChar = m_hasBreakableChar;
945     hasBreak = m_hasBreak;
946
947     if (stripFrontSpaces && (str->s[0] == ' ' || (!isPre && str->s[0] == '\n'))) {
948         const Font *f = htmlFont( false );
949         QChar space[1]; space[0] = ' ';
950         int spaceWidth = f->width(space, 1, 0);
951         maxW -= spaceWidth;
952     }
953     
954     stripFrontSpaces = !isPre && m_hasEndWS;
955     
956     if (style()->whiteSpace() == NOWRAP)
957         minW = maxW;
958     else if (minW > maxW)
959         minW = maxW;
960         
961     // Compute our max widths by scanning the string for newlines.
962     if (hasBreak) {
963         const Font *f = htmlFont( false );
964         bool firstLine = true;
965         beginMaxW = endMaxW = maxW;
966         for (int i = 0; i < len; i++)
967         {
968             int linelen = 0;
969             while (i+linelen < len && str->s[i+linelen] != '\n')
970                 linelen++;
971                 
972             if (linelen)
973             {
974 #if !APPLE_CHANGES
975                 endMaxW = f->width(str->s, str->l, i, linelen);
976 #else
977                 endMaxW = widthFromCache(f, i, linelen);
978 #endif
979                 if (firstLine) {
980                     firstLine = false;
981                     beginMaxW = endMaxW;
982                 }
983                 i += linelen;
984             }
985             else if (firstLine) {
986                 beginMaxW = 0;
987                 firstLine = false;
988             }
989             
990             if (i == len-1)
991                 // A <pre> run that ends with a newline, as in, e.g.,
992                 // <pre>Some text\n\n<span>More text</pre>
993                 endMaxW = 0;
994         }
995     }
996 }
997
998 void RenderText::calcMinMaxWidth()
999 {
1000     KHTMLAssert( !minMaxKnown() );
1001
1002     // ### calc Min and Max width...
1003     m_minWidth = m_beginMinWidth = m_endMinWidth = 0;
1004     m_maxWidth = 0;
1005
1006     if (isBR())
1007         return;
1008         
1009     int currMinWidth = 0;
1010     int currMaxWidth = 0;
1011     m_hasBreakableChar = m_hasBreak = m_hasBeginWS = m_hasEndWS = false;
1012     
1013     // ### not 100% correct for first-line
1014     const Font *f = htmlFont( false );
1015     int wordSpacing = style()->wordSpacing();
1016     int len = str->l;
1017     bool ignoringSpaces = false;
1018     bool isSpace = false;
1019     bool isPre = style()->whiteSpace() == PRE;
1020     bool firstWord = true;
1021     bool firstLine = true;
1022     for(int i = 0; i < len; i++)
1023     {
1024         const QChar c = str->s[i];
1025         
1026         bool previousCharacterIsSpace = isSpace;
1027         
1028         bool isNewline = false;
1029         if (c == '\n') {
1030             if (isPre) {
1031                 m_hasBreak = true;
1032                 isNewline = true;
1033                 isSpace = false;
1034             }
1035             else
1036                 isSpace = true;
1037         } else {
1038             isSpace = c == ' ';
1039         }
1040         
1041         if ((isSpace || isNewline) && i == 0)
1042             m_hasBeginWS = true;
1043         if ((isSpace || isNewline) && i == len-1)
1044             m_hasEndWS = true;
1045             
1046         if (!ignoringSpaces && !isPre && previousCharacterIsSpace && isSpace)
1047             ignoringSpaces = true;
1048         
1049         if (ignoringSpaces && !isSpace)
1050             ignoringSpaces = false;
1051             
1052         if (ignoringSpaces || (i > 0 && c.unicode() == SOFT_HYPHEN)) // Ignore spaces and soft hyphens
1053             continue;
1054         
1055         int wordlen = 0;
1056         while (i+wordlen < len && str->s[i+wordlen] != '\n' && str->s[i+wordlen] != ' ' &&
1057                (i+wordlen == 0 || str->s[i+wordlen].unicode() != SOFT_HYPHEN) && // Skip soft hyphens
1058                (wordlen == 0 || !isBreakable( str->s, i+wordlen, str->l)))
1059             wordlen++;
1060             
1061         if (wordlen)
1062         {
1063 #if !APPLE_CHANGES
1064             int w = f->width(str->s, str->l, i, wordlen);
1065 #else
1066             int w = widthFromCache(f, i, wordlen);
1067 #endif
1068             currMinWidth += w;
1069             currMaxWidth += w;
1070             
1071             bool isBreakableCharSpace = (i+wordlen < len) ? ((!isPre && str->s[i+wordlen] == '\n') || 
1072                                                              str->s[i+wordlen] == ' ') : false;
1073
1074             if (i+wordlen < len && style()->whiteSpace() == NORMAL)
1075                 m_hasBreakableChar = true;
1076             
1077             // Add in wordspacing to our maxwidth, but not if this is the last word on a line or the
1078             // last word in the run.
1079             if (wordSpacing && isBreakableCharSpace && !containsOnlyWhitespace(i+wordlen, len-(i+wordlen)))
1080                 currMaxWidth += wordSpacing;
1081
1082             if (firstWord) {
1083                 firstWord = false;
1084                 // If the first character in the run is breakable, then we consider ourselves to have a beginning
1085                 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
1086                 // being appended to a previous text run when considering the total minimum width of the containing block.
1087                 bool hasBreak = isBreakable(str->s, i, str->l);
1088                 if (hasBreak)
1089                     m_hasBreakableChar = true;
1090                 m_beginMinWidth = hasBreak ? 0 : w;
1091             }
1092             m_endMinWidth = w;
1093             
1094             if (currMinWidth > m_minWidth) m_minWidth = currMinWidth;
1095             currMinWidth = 0;
1096             
1097             i += wordlen-1;
1098         }
1099         else {
1100             // Nowrap can never be broken, so don't bother setting the
1101             // breakable character boolean. Pre can only be broken if we encounter a newline.
1102             if (style()->whiteSpace() == NORMAL || isNewline)
1103                 m_hasBreakableChar = true;
1104
1105             if (currMinWidth > m_minWidth) m_minWidth = currMinWidth;
1106             currMinWidth = 0;
1107             
1108             if (isNewline) // Only set if isPre was true and we saw a newline.
1109             {
1110                 if (firstLine) {
1111                     firstLine = false;
1112                     m_beginMinWidth = currMaxWidth;
1113                 }
1114                 
1115                 if (currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
1116                 currMaxWidth = 0;
1117             }
1118             else
1119             {
1120                 currMaxWidth += f->width( str->s, str->l, i + wordlen );
1121             }
1122         }
1123     }
1124     
1125     if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
1126     if(currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
1127
1128     if (style()->whiteSpace() != NORMAL)
1129         m_minWidth = m_maxWidth;
1130
1131     if (isPre) {
1132         if (firstLine)
1133             m_beginMinWidth = m_maxWidth;
1134         m_endMinWidth = currMaxWidth;
1135     }
1136     
1137     setMinMaxKnown();
1138     //kdDebug( 6040 ) << "Text::calcMinMaxWidth(): min = " << m_minWidth << " max = " << m_maxWidth << endl;
1139 }
1140
1141 bool RenderText::containsOnlyWhitespace(unsigned int from, unsigned int len) const
1142 {
1143     unsigned int currPos;
1144     for (currPos = from; 
1145          currPos < from+len && (str->s[currPos] == '\n' || str->s[currPos].unicode() == ' '); 
1146          currPos++);
1147     return currPos >= (from+len);
1148 }
1149
1150 int RenderText::minXPos() const
1151 {
1152     if (!m_firstTextBox) return 0;
1153     int retval=6666666;
1154     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1155         retval = kMin(retval, (int)box->m_x);
1156     return retval;
1157 }
1158
1159 int RenderText::xPos() const
1160 {
1161     return m_firstTextBox ? m_firstTextBox->m_x : 0;
1162 }
1163
1164 int RenderText::yPos() const
1165 {
1166     return m_firstTextBox ? m_firstTextBox->m_y : 0;
1167 }
1168
1169 const QFont &RenderText::font()
1170 {
1171     return style()->font();
1172 }
1173
1174 void RenderText::setTextWithOffset(DOMStringImpl *text, uint offset, uint len, bool force)
1175 {
1176     uint oldLen = str ? str->l : 0;
1177     uint newLen = text ? text->l : 0;
1178     int delta = newLen - oldLen;
1179     uint end = len ? offset+len-1 : offset;
1180
1181     RootInlineBox* firstRootBox = 0;
1182     RootInlineBox* lastRootBox = 0;
1183     
1184     bool dirtiedLines = false;
1185     
1186     // Dirty all text boxes that include characters in between offset and offset+len.
1187     for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1188         // Text run is entirely before the affected range.
1189         if (curr->end() < offset)
1190             continue;
1191         
1192         // Text run is entirely after the affected range.
1193         if (curr->start() > end) {
1194             curr->offsetRun(delta);
1195             RootInlineBox* root = curr->root();
1196             if (!firstRootBox) {
1197                 firstRootBox = root;
1198                 if (!dirtiedLines) { // The affected area was in between two runs. Go ahead and mark the root box of the run after the affected area as dirty.
1199                     firstRootBox->markDirty();
1200                     dirtiedLines = true;
1201                 }
1202             }
1203             lastRootBox = root;
1204         }
1205         else if (curr->end() >= offset && curr->end() <= end) {
1206             curr->dirtyLineBoxes(); // Text run overlaps with the left end of the affected range.
1207             dirtiedLines = true;
1208         }
1209         else if (curr->start() <= offset && curr->end() >= end) {
1210             curr->dirtyLineBoxes(); // Text run subsumes the affected range.
1211             dirtiedLines = true;
1212         }
1213         else if (curr->start() <= end && curr->end() >= end) {
1214             curr->dirtyLineBoxes(); // Text run overlaps with right end of the affected range.
1215             dirtiedLines = true;
1216         }
1217     }
1218     
1219     // Now we have to walk all of the clean lines and adjust their cached line break information
1220     // to reflect our updated offsets.
1221     if (lastRootBox)
1222         lastRootBox = lastRootBox->nextRootBox();
1223     if (firstRootBox) {
1224         RootInlineBox* prev = firstRootBox->prevRootBox();
1225         if (prev)
1226             firstRootBox = prev;
1227     }
1228     for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
1229         if (!curr->isDirty() && curr->lineBreakObj() == this && curr->lineBreakPos() > end)
1230             curr->setLineBreakPos(curr->lineBreakPos()+delta);
1231     }
1232     
1233     m_linesDirty = dirtiedLines;
1234     setText(text, force);
1235 }
1236
1237 void RenderText::setText(DOMStringImpl *text, bool force)
1238 {
1239     if (!text)
1240         return;
1241     if (!force && str == text)
1242         return;
1243     if (str)
1244         str->deref();
1245
1246     str = text;
1247     if (str) {
1248         str = str->replace('\\', backslashAsCurrencySymbol());
1249         if ( style() ) {
1250             switch(style()->textTransform()) {
1251                 case CAPITALIZE:   str = str->capitalize();  break;
1252                 case UPPERCASE:   str = str->upper();       break;
1253                 case LOWERCASE:  str = str->lower();       break;
1254                 case NONE:
1255                 default:;
1256             }
1257         }
1258         str->ref();
1259     }
1260
1261 #if APPLE_CHANGES
1262     cacheWidths();
1263 #endif
1264
1265     // ### what should happen if we change the text of a
1266     // RenderBR object ?
1267     KHTMLAssert(!isBR() || (str->l == 1 && (*str->s) == '\n'));
1268     KHTMLAssert(!str->l || str->s);
1269
1270     setNeedsLayoutAndMinMaxRecalc();
1271     
1272 #ifdef BIDI_DEBUG
1273     QConstString cstr(str->s, str->l);
1274     kdDebug( 6040 ) << "RenderText::setText( " << cstr.string().length() << " ) '" << cstr.string() << "'" << endl;
1275 #endif
1276 }
1277
1278 int RenderText::height() const
1279 {
1280     // FIXME: Why use line-height? Shouldn't we be adding in the height of the last text box? -dwh
1281     int retval = 0;
1282     if (firstTextBox())
1283         retval = lastTextBox()->m_y + lineHeight(false) - firstTextBox()->m_y;
1284     return retval;
1285 }
1286
1287 short RenderText::lineHeight(bool firstLine, bool) const
1288 {
1289     // Always use the interior line height of the parent (e.g., if our parent is an inline block).
1290     return parent()->lineHeight(firstLine, true);
1291 }
1292
1293 short RenderText::baselinePosition( bool firstLine, bool ) const
1294 {
1295     const QFontMetrics &fm = metrics( firstLine );
1296     return fm.ascent() +
1297         ( lineHeight( firstLine ) - fm.height() ) / 2;
1298 }
1299
1300 void RenderText::dirtyLineBoxes(bool fullLayout, bool)
1301 {
1302     if (fullLayout)
1303         deleteTextBoxes();
1304     else if (!m_linesDirty) {
1305         for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1306             box->dirtyLineBoxes();
1307     }
1308     m_linesDirty = false;
1309 }
1310
1311 InlineBox* RenderText::createInlineBox(bool, bool isRootLineBox, bool)
1312 {
1313     KHTMLAssert(!isRootLineBox);
1314     InlineTextBox* textBox = new (renderArena()) InlineTextBox(this);
1315     if (!m_firstTextBox)
1316         m_firstTextBox = m_lastTextBox = textBox;
1317     else {
1318         m_lastTextBox->setNextLineBox(textBox);
1319         textBox->setPreviousLineBox(m_lastTextBox);
1320         m_lastTextBox = textBox;
1321     }
1322     return textBox;
1323 }
1324
1325 void RenderText::position(InlineBox* box, int from, int len, bool reverse)
1326 {
1327     InlineTextBox *s = static_cast<InlineTextBox*>(box);
1328     
1329     // ### should not be needed!!!
1330     if (len == 0) {
1331         // We want the box to be destroyed.
1332         s->remove();
1333         s->detach(renderArena());
1334         m_firstTextBox = m_lastTextBox = 0;
1335         return;
1336     }
1337     
1338     reverse = reverse && !style()->visuallyOrdered();
1339
1340 #ifdef DEBUG_LAYOUT
1341     QChar *ch = str->s+from;
1342     QConstString cstr(ch, len);
1343     qDebug("setting run text to *%s*, len=%d, w)=%d" , cstr.string().latin1(), len, width );//" << y << ")" << " height=" << lineHeight(false) << " fontHeight=" << metrics(false).height() << " ascent =" << metrics(false).ascent() << endl;
1344 #endif
1345
1346     s->m_reversed = reverse;
1347     s->m_start = from;
1348     s->m_len = len;
1349 }
1350
1351 unsigned int RenderText::width(unsigned int from, unsigned int len, bool firstLine) const
1352 {
1353     if(!str->s || from > str->l ) return 0;
1354     if ( from + len > str->l ) len = str->l - from;
1355
1356     const Font *f = htmlFont( firstLine );
1357     return width( from, len, f );
1358 }
1359
1360 unsigned int RenderText::width(unsigned int from, unsigned int len, const Font *f) const
1361 {
1362     if(!str->s || from > str->l ) return 0;
1363     if ( from + len > str->l ) len = str->l - from;
1364
1365     int w;
1366     if ( f == &style()->htmlFont() && from == 0 && len == str->l )
1367          w = m_maxWidth;
1368 #if APPLE_CHANGES
1369     else if (f == &style()->htmlFont())
1370         w = widthFromCache (f, from, len);
1371 #endif
1372     else
1373         w = f->width(str->s, str->l, from, len );
1374
1375     //kdDebug( 6040 ) << "RenderText::width(" << from << ", " << len << ") = " << w << endl;
1376     return w;
1377 }
1378
1379 int RenderText::width() const
1380 {
1381     int w;
1382     int minx = 100000000;
1383     int maxx = 0;
1384     // slooow
1385     for (InlineTextBox* s = firstTextBox(); s; s = s->nextTextBox()) {
1386         if(s->m_x < minx)
1387             minx = s->m_x;
1388         if(s->m_x + s->m_width > maxx)
1389             maxx = s->m_x + s->m_width;
1390     }
1391
1392     w = kMax(0, maxx-minx);
1393
1394     return w;
1395 }
1396
1397 QRect RenderText::getAbsoluteRepaintRect()
1398 {
1399     RenderObject *cb = containingBlock();
1400     return cb->getAbsoluteRepaintRect();
1401 }
1402
1403 short RenderText::verticalPositionHint( bool firstLine ) const
1404 {
1405     return parent()->verticalPositionHint( firstLine );
1406 }
1407
1408 const QFontMetrics &RenderText::metrics(bool firstLine) const
1409 {
1410     return style(firstLine)->fontMetrics();
1411 }
1412
1413 const Font *RenderText::htmlFont(bool firstLine) const
1414 {
1415     return &style(firstLine)->htmlFont();
1416 }
1417
1418 long RenderText::caretMinOffset() const
1419 {
1420     if (!firstTextBox()) 
1421         return 0;
1422     // EDIT FIXME: it is *not* guaranteed that the first run contains the lowest offset
1423     // Either make this a linear search (slow),
1424     // or maintain an index (needs much mem),
1425     // or calculate and store it in bidi.cpp (needs calculation even if not needed)
1426     return firstTextBox()->m_start;
1427 }
1428
1429 long RenderText::caretMaxOffset() const
1430 {
1431     if (!firstTextBox()) 
1432         return str->l;
1433     // EDIT FIXME: it is *not* guaranteed that the last run contains the highest offset
1434     // Either make this a linear search (slow),
1435     // or maintain an index (needs much mem),
1436     // or calculate and store it in bidi.cpp (needs calculation even if not needed)
1437     return lastTextBox()->m_start + lastTextBox()->m_len;
1438 }
1439
1440 unsigned long RenderText::caretMaxRenderedOffset() const
1441 {
1442     int l = 0;
1443     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1444         l += box->m_len;
1445     return l;
1446 }
1447
1448 InlineBox *RenderText::inlineBox(long offset)
1449 {
1450     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1451         if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
1452             return box;
1453         }
1454         else if (offset < box->m_start) {
1455             // The offset we're looking for is before this node
1456             // this means the offset must be in content that is
1457             // not rendered.
1458             return box->prevTextBox() ? box->prevTextBox() : firstTextBox();
1459         }
1460     }
1461     
1462     return 0;
1463 }
1464
1465 void RenderText::clearTextOverflowTruncation()
1466 {
1467     // Walk our text boxes and clear any truncation bits that might be set.
1468     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1469         box->clearTruncation();
1470 }
1471    
1472 RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str,
1473                                        int startOffset, int endOffset)
1474 :RenderText(_node, _str->substring(startOffset, endOffset)), 
1475 m_start(startOffset), m_end(endOffset), m_generatedContentStr(0)
1476 {}
1477
1478 RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str)
1479 :RenderText(_node, _str), m_start(0)
1480 {
1481     m_generatedContentStr = _str;
1482     if (_str) {
1483         _str->ref();
1484         m_end = _str->l;
1485     }
1486     else
1487         m_end = 0;
1488 }
1489     
1490 RenderTextFragment::~RenderTextFragment()
1491 {
1492     if (m_generatedContentStr)
1493         m_generatedContentStr->deref();
1494 }
1495
1496 bool RenderTextFragment::isTextFragment() const
1497 {
1498     return true;
1499 }
1500
1501 DOM::DOMStringImpl* RenderTextFragment::originalString() const
1502 {
1503     DOM::DOMStringImpl* result = 0;
1504     if (element())
1505         result = element()->string();
1506     else
1507         result = contentString();
1508     if (result && (start() > 0 || start() < result->l))
1509         result = result->substring(start(), end());
1510     return result;
1511 }
1512 #undef BIDI_DEBUG
1513 #undef DEBUG_LAYOUT