f9cb05d94b56b929008226ac438d6ff0c941b46a
[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_object.h"
29 #include "rendering/render_text.h"
30 #include "rendering/break_lines.h"
31 #include "dom/dom2_range.h"
32 #include "xml/dom_nodeimpl.h"
33 #include "xml/dom_docimpl.h"
34 #include "xml/dom_position.h"
35 #include "render_arena.h"
36
37 #include "misc/loader.h"
38
39 #include "khtml_part.h"
40
41 #include <qpainter.h>
42 #include <kdebug.h>
43 #include <assert.h>
44
45 using namespace khtml;
46 using namespace DOM;
47
48 #ifndef NDEBUG
49 static bool inInlineTextBoxDetach;
50 #endif
51
52 void InlineTextBox::detach(RenderArena* renderArena)
53 {
54 #ifndef NDEBUG
55     inInlineTextBoxDetach = true;
56 #endif
57     delete this;
58 #ifndef NDEBUG
59     inInlineTextBoxDetach = false;
60 #endif
61     
62     // Recover the size left there for us by operator delete and free the memory.
63     renderArena->free(*(size_t *)this, this);
64 }
65
66 void* InlineTextBox::operator new(size_t sz, RenderArena* renderArena) throw()
67 {
68     return renderArena->allocate(sz);
69 }
70
71 void InlineTextBox::operator delete(void* ptr, size_t sz)
72 {
73     assert(inInlineTextBoxDetach);
74     
75     // Stash size where detach can find it.
76     *(size_t *)ptr = sz;
77 }
78
79 RenderText* InlineTextBox::textObject()
80 {
81     return static_cast<RenderText*>(m_object);
82 }
83
84 bool InlineTextBox::checkVerticalPoint(int _y, int _ty, int _h)
85 {
86     int topY = m_y;
87     int bottomY = m_y + m_height;
88     if (root()->hasSelectedChildren()) {
89         topY = kMin(root()->selectionTop(), topY);
90         bottomY = kMax(bottomY, root()->bottomOverflow());
91     }
92     if ((_ty + topY >= _y + _h) || (_ty + bottomY <= _y))
93         return false;
94     return true;
95 }
96
97 bool InlineTextBox::isSelected(int startPos, int endPos) const
98 {
99     int sPos = kMax(startPos - m_start, 0);
100     int ePos = kMin(endPos - m_start, (int)m_len);
101     return (sPos < ePos);
102 }
103
104 RenderObject::SelectionState InlineTextBox::selectionState()
105 {
106     RenderObject::SelectionState state = object()->selectionState();
107     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd ||
108         state == RenderObject::SelectionBoth) {
109         int startPos, endPos;
110         object()->selectionStartEnd(startPos, endPos);
111         
112         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
113         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= m_start + m_len);
114         if (start && end)
115             state = RenderObject::SelectionBoth;
116         else if (start)
117             state = RenderObject::SelectionStart;
118         else if (end)
119             state = RenderObject::SelectionEnd;
120         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
121                  (state == RenderObject::SelectionStart || endPos > m_start + m_len))
122             state = RenderObject::SelectionInside;
123     }
124     return state;
125 }
126
127 QRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
128 {
129     int sPos = kMax(startPos - m_start, 0);
130     int ePos = kMin(endPos - m_start, (int)m_len);
131     
132     if (sPos >= ePos)
133         return QRect();
134
135     RootInlineBox* rootBox = root();
136     int selStart = m_reversed ? m_x + m_width : m_x;
137     int selEnd = selStart;
138     int selTop = rootBox->selectionTop();
139     int selHeight = rootBox->selectionHeight();
140     
141     // FIXME: For justified text, just return the entire text box's rect.  At the moment there's still no easy
142     // way to get the width of a run including the justification padding.
143     if (sPos > 0 && !m_toAdd) {
144         // The selection begins in the middle of our run.
145         int w = textObject()->width(m_start, sPos, m_firstLine);
146         if (m_reversed)
147             selStart -= w;
148         else
149             selStart += w;
150     }
151
152     if (m_toAdd || (sPos == 0 && ePos == m_len)) {
153         if (m_reversed)
154             selEnd = m_x;
155         else
156             selEnd = m_x + m_width;
157     }
158     else {
159         // Our run is partially selected, and so we have to actually do a measurement.
160         int w = textObject()->width(sPos + m_start, ePos - sPos, m_firstLine);
161         if (m_reversed)
162             selEnd = selStart - w;
163         else
164             selEnd = selStart + w;
165     }
166
167     int selLeft = m_reversed ? selEnd : selStart;
168     int selRight = m_reversed ? selStart : selEnd;
169
170     return QRect(selLeft + tx, selTop + ty, selRight - selLeft, selHeight);
171 }
172
173 void InlineTextBox::deleteLine(RenderArena* arena)
174 {
175     static_cast<RenderText*>(m_object)->removeTextBox(this);
176     detach(arena);
177 }
178
179 void InlineTextBox::extractLine()
180 {
181     if (m_extracted)
182         return;
183
184     static_cast<RenderText*>(m_object)->extractTextBox(this);
185 }
186
187 void InlineTextBox::attachLine()
188 {
189     if (!m_extracted)
190         return;
191     
192     static_cast<RenderText*>(m_object)->attachTextBox(this);
193 }
194
195 int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
196 {
197     if (foundBox) {
198         m_truncation = cFullTruncation;
199         return -1;
200     }
201
202     int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth;
203     
204     // For LTR, if the left edge of the ellipsis is to the left of our text run, then we are the run that will get truncated.
205     if (ltr) {
206         if (ellipsisX <= m_x) {
207             // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
208             m_truncation = cFullTruncation;
209             foundBox = true;
210             return -1;
211         }
212
213         if (ellipsisX < m_x + m_width) {
214             if (m_reversed)
215                 return -1; // FIXME: Support LTR truncation when the last run is RTL someday.
216
217             foundBox = true;
218
219             int offset = offsetForPosition(ellipsisX, false);
220             if (offset == 0) {
221                 // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
222                 // and the ellipsis edge.
223                 m_truncation = cFullTruncation;
224                 return kMin(ellipsisX, m_x);
225             }
226             
227             // Set the truncation index on the text run.  The ellipsis needs to be placed just after the last visible character.
228             m_truncation = offset + m_start;
229             return m_x + static_cast<RenderText*>(m_object)->width(m_start, offset, m_firstLine);
230         }
231     }
232     else {
233         // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR)
234     }
235     return -1;
236 }
237
238 void InlineTextBox::paintSelection(const Font *f, RenderText *text, QPainter *p, RenderStyle* style, int tx, int ty, int startPos, int endPos)
239 {
240     int offset = m_start;
241     int sPos = kMax(startPos - offset, 0);
242     int ePos = kMin(endPos - offset, (int)m_len);
243
244     if (sPos >= ePos)
245         return;
246
247     // Macintosh-style text highlighting is to draw with a particular background color, not invert.
248     QColor textColor = style->color();
249     QColor c = object()->selectionColor(p);
250     if (!c.isValid())
251         return;
252
253     // If the text color ends up being the same as the selection background, invert the selection
254     // background.  This should basically never happen, since the selection has transparency.
255     if (textColor == c)
256         c = QColor(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
257
258     p->save();
259     p->setPen(c); // Don't draw text at all!
260     RootInlineBox* r = root();
261     int x = m_x + tx;
262     int y = r->selectionTop();
263     int h = r->selectionHeight();
264     f->drawHighlightForText(p, x, y + ty, h,
265                             text->str->s, text->str->l, m_start, m_len,
266                             m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, style->visuallyOrdered(), sPos, ePos, c);
267     p->restore();
268 }
269
270 #if APPLE_CHANGES
271 void InlineTextBox::paintMarkedTextBackground(const Font *f, RenderText *text, QPainter *p, RenderStyle* style, int tx, int ty, int startPos, int endPos)
272 {
273     int offset = m_start;
274     int sPos = kMax(startPos - offset, 0);
275     int ePos = kMin(endPos - offset, (int)m_len);
276
277     if (sPos >= ePos)
278         return;
279
280     p->save();
281
282     QColor c = QColor(225, 221, 85);
283     
284     p->setPen(c); // Don't draw text at all!
285
286     RootInlineBox* r = root();
287     int x = m_x + tx;
288     int y = r->selectionTop();
289     int h = r->selectionHeight();
290     f->drawHighlightForText(p, x, y + ty, h, text->str->s, text->str->l, m_start, m_len,
291                 m_toAdd, m_reversed ? QPainter::RTL : QPainter::LTR, style->visuallyOrdered(), sPos, ePos, c);
292     p->restore();
293 }
294 #endif
295
296 #ifdef APPLE_CHANGES
297 void InlineTextBox::paintDecoration( QPainter *pt, int _tx, int _ty, int deco)
298 {
299     _tx += m_x;
300     _ty += m_y;
301
302     if (m_truncation == cFullTruncation)
303         return;
304     
305     int width = (m_truncation == cNoTruncation) ? 
306                 m_width : static_cast<RenderText*>(m_object)->width(m_start, m_truncation - m_start, m_firstLine);
307     
308     // Get the text decoration colors.
309     QColor underline, overline, linethrough;
310     object()->getTextDecorationColors(deco, underline, overline, linethrough, true);
311     
312     // Use a special function for underlines to get the positioning exactly right.
313     if (deco & UNDERLINE) {
314         pt->setPen(underline);
315         pt->drawLineForText(_tx, _ty, m_baseline, width);
316     }
317     if (deco & OVERLINE) {
318         pt->setPen(overline);
319         pt->drawLineForText(_tx, _ty, 0, width);
320     }
321     if (deco & LINE_THROUGH) {
322         pt->setPen(linethrough);
323         pt->drawLineForText(_tx, _ty, 2*m_baseline/3, width);
324     }
325 }
326 #else
327 void InlineTextBox::paintDecoration( QPainter *pt, int _tx, int _ty, int decoration)
328 {
329     _tx += m_x;
330     _ty += m_y;
331
332     int width = m_width - 1;
333
334     QColor underline, overline, linethrough;
335     object()->getTextDecorationColors(decoration, underline, overline, linethrough, true);
336     
337     int underlineOffset = ( pt->fontMetrics().height() + m_baseline ) / 2;
338     if(underlineOffset <= m_baseline) underlineOffset = m_baseline+1;
339
340     if(deco & UNDERLINE){
341         pt->setPen(underline);
342         pt->drawLine(_tx, _ty + underlineOffset, _tx + width, _ty + underlineOffset );
343     }
344     if (deco & OVERLINE) {
345         pt->setPen(overline);
346         pt->drawLine(_tx, _ty, _tx + width, _ty );
347     }
348     if(deco & LINE_THROUGH) {
349         pt->setPen(linethrough);
350         pt->drawLine(_tx, _ty + 2*m_baseline/3, _tx + width, _ty + 2*m_baseline/3 );
351     }
352     // NO! Do NOT add BLINK! It is the most annouing feature of Netscape, and IE has a reason not to
353     // support it. Lars
354 }
355 #endif
356
357 void InlineTextBox::paintMarker(QPainter *pt, int _tx, int _ty, DocumentMarker marker)
358 {
359     _tx += m_x;
360     _ty += m_y;
361
362     if (m_truncation == cFullTruncation)
363         return;
364     
365     int start = 0;                  // start of line to draw, relative to _tx
366     int width = m_width;            // how much line to draw
367     bool useWholeWidth = true;
368     ulong paintStart = m_start;
369     ulong paintEnd = end()+1;      // end points at the last char, not past it
370     if (paintStart <= marker.startOffset) {
371         paintStart = marker.startOffset;
372         useWholeWidth = false;
373         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, m_firstLine);
374     }
375     if (paintEnd != marker.endOffset) {      // end points at the last char, not past it
376         paintEnd = kMin(paintEnd, marker.endOffset);
377         useWholeWidth = false;
378     }
379     if (m_truncation != cNoTruncation) {
380         paintEnd = kMin(paintEnd, (ulong)m_truncation);
381         useWholeWidth = false;
382     }
383     if (!useWholeWidth) {
384         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, m_firstLine);
385     }
386
387     // AppKit uses a more complicated formula to figure the offset from the baseline for the misspelling
388     // underline.  Using "+2" made the marker draw outside the selection rect in Times-16, using "+1"
389     // leaves it a pixel too high with Times-24.  The former is the lesser evil.
390     int underlineOffset = m_baseline + 1;
391     pt->drawLineForMisspelling(_tx + start, _ty + underlineOffset, width);
392 }
393
394 long InlineTextBox::caretMinOffset() const
395 {
396     return m_start;
397 }
398
399 long InlineTextBox::caretMaxOffset() const
400 {
401     return m_start + m_len;
402 }
403
404 unsigned long InlineTextBox::caretMaxRenderedOffset() const
405 {
406     return m_start + m_len;
407 }
408
409 #define LOCAL_WIDTH_BUF_SIZE    1024
410
411 int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs)
412 {
413     RenderText* text = static_cast<RenderText*>(m_object);
414     const Font* f = text->htmlFont(m_firstLine);
415     return f->checkSelectionPoint(text->str->s, text->str->l, m_start, m_len, m_toAdd, _x - m_x, m_reversed, includePartialGlyphs);
416 }
417
418 // -------------------------------------------------------------------------------------
419
420 RenderText::RenderText(DOM::NodeImpl* node, DOMStringImpl *_str)
421     : RenderObject(node), m_linesDirty(false)
422 {
423     // init RenderObject attributes
424     setRenderText();   // our object inherits from RenderText
425
426     m_minWidth = -1;
427     m_maxWidth = -1;
428
429 #ifdef APPLE_CHANGES
430     m_monospaceCharacterWidth = 0;
431     m_allAsciiChecked = false;
432     m_allAscii = false;
433 #endif
434
435     str = _str;
436     if (str) {
437         str = str->replace('\\', backslashAsCurrencySymbol());
438         str->ref();
439     }
440     KHTMLAssert(!str || !str->l || str->s);
441
442     m_firstTextBox = m_lastTextBox = 0;
443     
444     m_selectionState = SelectionNone;
445
446 #ifdef DEBUG_LAYOUT
447     QConstString cstr(str->s, str->l);
448     kdDebug( 6040 ) << "RenderText ctr( "<< cstr.string().length() << " )  '" << cstr.string() << "'" << endl;
449 #endif
450 }
451
452 void RenderText::setStyle(RenderStyle *_style)
453 {
454     if ( style() != _style ) {
455         bool needToTransformText = (!style() && _style->textTransform() != TTNONE) ||
456                                    (style() && style()->textTransform() != _style->textTransform());
457
458         RenderObject::setStyle( _style );
459
460         if (needToTransformText) {
461             DOM::DOMStringImpl* textToTransform = originalString();
462             if (textToTransform)
463                 setText(textToTransform, true);
464         }
465 #if APPLE_CHANGES
466         // setText also calls cacheWidths(), so there is no need to call it again in that case.
467         else
468             cacheWidths();
469 #endif
470     }
471 }
472
473 RenderText::~RenderText()
474 {
475     if(str) str->deref();
476 }
477
478 void RenderText::detach()
479 {
480     if (!documentBeingDestroyed()) {
481         if (firstTextBox()) {
482             if (isBR()) {
483                 RootInlineBox* next = firstTextBox()->root()->nextRootBox();
484                 if (next)
485                     next->markDirty();
486             }
487             for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
488                 box->remove();
489         }
490         else if (parent())
491             parent()->dirtyLinesFromChangedChild(this, false);
492     }
493     deleteTextBoxes();
494     RenderObject::detach();
495 }
496
497 void RenderText::extractTextBox(InlineTextBox* box)
498 {
499     m_lastTextBox = box->prevTextBox();
500     if (box == m_firstTextBox)
501         m_firstTextBox = 0;
502     if (box->prevTextBox())
503         box->prevTextBox()->setNextLineBox(0);
504     box->setPreviousLineBox(0);
505     for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
506         curr->setExtracted();
507 }
508
509 void RenderText::attachTextBox(InlineTextBox* box)
510 {
511     if (m_lastTextBox) {
512         m_lastTextBox->setNextLineBox(box);
513         box->setPreviousLineBox(m_lastTextBox);
514     }
515     else
516         m_firstTextBox = box;
517     InlineTextBox* last = box;
518     for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
519         curr->setExtracted(false);
520         last = curr;
521     }
522     m_lastTextBox = last;
523 }
524
525 void RenderText::removeTextBox(InlineTextBox* box)
526 {
527     if (box == m_firstTextBox)
528         m_firstTextBox = box->nextTextBox();
529     if (box == m_lastTextBox)
530         m_lastTextBox = box->prevTextBox();
531     if (box->nextTextBox())
532         box->nextTextBox()->setPreviousLineBox(box->prevTextBox());
533     if (box->prevTextBox())
534         box->prevTextBox()->setNextLineBox(box->nextTextBox());
535 }
536
537 void RenderText::deleteTextBoxes()
538 {
539     if (firstTextBox()) {
540         RenderArena* arena = renderArena();
541         InlineTextBox *curr = firstTextBox(), *next = 0;
542         while (curr) {
543             next = curr->nextTextBox();
544             curr->detach(arena);
545             curr = next;
546         }
547         m_firstTextBox = m_lastTextBox = 0;
548     }
549 }
550
551 bool RenderText::isTextFragment() const
552 {
553     return false;
554 }
555
556 DOM::DOMStringImpl* RenderText::originalString() const
557 {
558     return element() ? element()->string() : 0;
559 }
560
561 void RenderText::absoluteRects(QValueList<QRect>& rects, int _tx, int _ty)
562 {
563     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
564         rects.append(QRect(_tx + box->xPos(), 
565                            _ty + box->yPos(), 
566                            box->width(), 
567                            box->height()));
568 }
569
570 InlineTextBox* RenderText::findNextInlineTextBox(int offset, int &pos)
571 {
572     // The text runs point to parts of the rendertext's str string
573     // (they don't include '\n')
574     // Find the text run that includes the character at @p offset
575     // and return pos, which is the position of the char in the run.
576
577     if (!m_firstTextBox)
578         return 0;
579     
580     InlineTextBox* s = m_firstTextBox;
581     int off = s->m_len;
582     while (offset > off && s->nextTextBox())
583     {
584         s = s->nextTextBox();
585         off = s->m_start + s->m_len;
586     }
587     // we are now in the correct text run
588     pos = (offset > off ? s->m_len : s->m_len - (off - offset) );
589     return s;
590 }
591
592 bool RenderText::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
593                              HitTestAction hitTestAction, bool inside)
594 {
595     assert(parent());
596
597     if (style()->visibility() == HIDDEN)
598         return inside;
599
600     for (InlineTextBox *s = m_firstTextBox; s; s = s->nextTextBox()) {
601         if((_y >=_ty + s->m_y) && (_y < _ty + s->m_y + s->height()) &&
602            (_x >= _tx + s->m_x) && (_x <_tx + s->m_x + s->m_width) ) {
603             inside = true;
604             break;
605         }
606     }
607
608     if (inside && element()) {
609         if (info.innerNode() && info.innerNode()->renderer() && 
610             !info.innerNode()->renderer()->isInline()) {
611             // Within the same layer, inlines are ALWAYS fully above blocks.  Change inner node.
612             info.setInnerNode(element());
613             
614             // Clear everything else.
615             info.setInnerNonSharedNode(0);
616             info.setURLElement(0);
617         }
618         
619         if (!info.innerNode())
620             info.setInnerNode(element());
621
622         if (!info.innerNonSharedNode())
623             info.setInnerNonSharedNode(element());
624     }
625
626     return inside;
627 }
628
629 Position RenderText::positionForCoordinates(int _x, int _y, EAffinity *affinity)
630 {
631     if (affinity)
632         *affinity = UPSTREAM;
633
634     if (!firstTextBox() || stringLength() == 0)
635         return Position(element(), 0);
636
637     int absx, absy;
638     containingBlock()->absolutePosition(absx, absy);
639
640     if (firstTextBox() && _y < absy + firstTextBox()->root()->bottomOverflow() && _x < absx + firstTextBox()->m_x) {
641         // at the y coordinate of the first line or above
642         // and the x coordinate is to the left than the first text box left edge
643         if (affinity)
644             *affinity = DOWNSTREAM;
645         return Position(element(), firstTextBox()->m_start);
646     }
647
648     if (lastTextBox() && _y >= absy + lastTextBox()->root()->topOverflow() && _x >= absx + lastTextBox()->m_x + lastTextBox()->m_width) {
649         // at the y coordinate of the last line or below
650         // and the x coordinate is to the right than the last text box right edge
651         return Position(element(), lastTextBox()->m_start + lastTextBox()->m_len);
652     }
653
654     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
655         if (_y >= absy + box->root()->topOverflow() && _y < absy + box->root()->bottomOverflow()) {
656             if (_x < absx + box->m_x + box->m_width) {
657                 // and the x coordinate is to the left of the right edge of this box
658                 // check to see if position goes in this box
659                 int offset = box->offsetForPosition(_x - absx);
660                 if (offset != -1) {
661                     if (affinity)
662                         *affinity = DOWNSTREAM;
663                     return Position(element(), offset + box->m_start);
664                 }
665             }
666             else if (!box->prevOnLine() && _x < absx + box->m_x) {
667                 // box is first on line
668                 // and the x coordinate is to the left of the first text box left edge
669                 if (affinity)
670                     *affinity = DOWNSTREAM;
671                 return Position(element(), box->m_start);
672             }
673             else if (!box->nextOnLine() && _x >= absx + box->m_x + box->m_width)
674                 // box is last on line
675                 // and the x coordinate is to the right of the last text box right edge
676                 return Position(element(), box->m_start + box->m_len);
677         }
678     }
679     
680     return Position(element(), 0);
681 }
682
683 static RenderObject *firstRendererOnNextLine(InlineBox *box)
684 {
685     if (!box)
686         return 0;
687
688     RootInlineBox *root = box->root();
689     if (!root)
690         return 0;
691         
692     if (root->endsWithBreak())
693         return 0;
694     
695     RootInlineBox *nextRoot = root->nextRootBox();
696     if (!nextRoot)
697         return 0;
698     
699     InlineBox *firstChild = nextRoot->firstChild();
700     if (!firstChild)
701         return 0;
702
703     return firstChild->object();
704 }
705
706 QRect RenderText::caretRect(int offset, EAffinity affinity)
707 {
708     if (!firstTextBox() || stringLength() == 0) {
709         return QRect();
710     }
711
712     // Find the text box for the given offset
713     InlineTextBox *box = 0;
714     for (box = firstTextBox(); box; box = box->nextTextBox()) {
715         if (affinity == DOWNSTREAM && offset == box->m_start + box->m_len) {
716             // We're at the end of a line and affinity is downstream.
717             // Try to jump down to the next line.
718             if (box->nextTextBox()) {
719                 // Use the next text box
720                 box = box->nextTextBox();
721                 offset = box->m_start;
722             }
723             else {
724                 // Look on the next line
725                 RenderObject *object = firstRendererOnNextLine(box);
726                 if (object)
727                     return object->caretRect(0, affinity);
728             }
729         }
730         if (offset <= box->m_start + box->m_len)
731             break;
732     }
733     
734     if (!box) {
735         return QRect();
736     }
737
738     int height = box->root()->bottomOverflow() - box->root()->topOverflow();
739     int top = box->root()->topOverflow();
740
741     const QFontMetrics &fm = metrics(box->isFirstLineStyle());
742     QString string(str->s + box->m_start, box->m_len);
743     long pos = offset - box->m_start; // the number of characters we are into the string
744     int left = box->m_x + fm.boundingRect(string, pos).right();
745
746 #if 0
747     // EDIT FIXME
748     if (pos)
749         left += fm.rightBearing(*(str->s + box->m_start + offset));
750 #endif
751
752     int absx, absy;
753     absolutePosition(absx,absy);
754     left += absx;
755     top += absy;
756
757     // FIXME: Need the +1 to match caret position of other programs on Macintosh.
758     // Would be better to somehow derive it once we understand exactly why it's needed.
759     left += 1;
760
761     RenderBlock *cb = containingBlock();
762     int availableWidth = cb->lineWidth(height);
763     if (style()->whiteSpace() == NORMAL)
764         left = kMin(left, absx + availableWidth - 1);
765     
766     return QRect(left, top, 1, height);
767 }
768
769 void RenderText::posOfChar(int chr, int &x, int &y)
770 {
771     absolutePosition( x, y, false );
772
773     //if( chr > (int) str->l )
774     //chr = str->l;
775
776     int pos;
777     InlineTextBox * s = findNextInlineTextBox( chr, pos );
778
779     if ( s )
780     {
781         // s is the line containing the character
782         x += s->m_x; // this is the x of the beginning of the line, but it's good enough for now
783         y += s->m_y;
784     }
785 }
786
787 static int
788 simpleDifferenceBetweenColors(QColor c1, QColor c2)
789 {
790     // a distance could be computed by squaring the differences between components, but
791     // this is faster and so far seems good enough for our purposes.
792     return abs(c1.red() - c2.red()) + abs(c1.green() - c2.green()) + abs(c1.blue() - c2.blue());
793 }
794
795 static QColor 
796 correctedTextColor(QColor textColor, QColor backgroundColor) 
797 {
798     // Adjust the text color if it is too close to the background color,
799     // by darkening or lightening it to move it further away.
800     
801     int d = simpleDifferenceBetweenColors(textColor, backgroundColor);
802     // semi-arbitrarily chose 255 value here after a few tests; 
803     if (d > 255) {
804         return textColor;
805     }
806     
807     int distanceFromWhite = simpleDifferenceBetweenColors(textColor, Qt::white);
808     int distanceFromBlack = simpleDifferenceBetweenColors(textColor, Qt::black);
809
810     if (distanceFromWhite < distanceFromBlack) {
811         return textColor.dark();
812     }
813     
814     return textColor.light();
815 }
816
817 void RenderText::paint(PaintInfo& i, int tx, int ty)
818 {
819     if (i.phase != PaintActionForeground && i.phase != PaintActionSelection)
820         return;
821     
822     if (!shouldPaintWithinRoot(i))
823         return;
824         
825     if (style()->visibility() != VISIBLE || !firstTextBox())
826         return;
827     
828     // Selection paints all the way up to the previous line's bottomOverflow.  We need to check
829     // for this when doing our dirty rect intersection tests.
830     int topY = firstTextBox()->yPos();
831     if (firstTextBox()->root()->hasSelectedChildren())
832         topY = kMin(topY, firstTextBox()->root()->selectionTop());
833     int bottomY = lastTextBox()->yPos() + lastTextBox()->height();
834     if (lastTextBox()->root()->hasSelectedChildren())
835         bottomY = kMax(bottomY, lastTextBox()->root()->bottomOverflow());
836
837     if (ty + topY >= i.r.y() + i.r.height()) 
838         return;
839
840     if (ty + bottomY <= i.r.y()) 
841         return;
842     
843     QPainter* p = i.p;
844     RenderStyle* pseudoStyle = style(true);
845     if (pseudoStyle == style()) pseudoStyle = 0;
846     int d = style()->textDecorationsInEffect();
847     bool isPrinting = (p->device()->devType() == QInternal::Printer);
848     
849     // Walk forward until we hit the first line that needs to be painted.
850     InlineTextBox* s = firstTextBox();
851     for (; s && !s->checkVerticalPoint(i.r.y(), ty, i.r.height()); s = s->nextTextBox());
852     if (!s) return;
853     
854     // Now calculate startPos and endPos, for painting selection.
855     // We paint selection while endPos > 0
856     int endPos, startPos;
857     if (!isPrinting && (selectionState() != SelectionNone)) {
858         if (selectionState() == SelectionInside) {
859             //kdDebug(6040) << this << " SelectionInside -> 0 to end" << endl;
860             startPos = 0;
861             endPos = str->l;
862         } else {
863             selectionStartEnd(startPos, endPos);
864             if(selectionState() == SelectionStart)
865                 endPos = str->l;
866             else if(selectionState() == SelectionEnd)
867                 startPos = 0;
868         }
869         //kdDebug(6040) << this << " Selection from " << startPos << " to " << endPos << endl;
870     }
871
872     const Font *font = &style()->htmlFont();
873
874 #if APPLE_CHANGES
875     // We will iterate over the text boxes in two passes. First we
876     // paint all the backgrounds, then the foregrounds.
877
878     InlineTextBox* startBox = s;
879
880     // Pass 1: paint backgrounds for selection or marked text, if we have any
881
882     bool haveSelection = startPos != endPos && !isPrinting && selectionState() != SelectionNone;
883     if (!haveSelection && i.phase == PaintActionSelection) {
884         // When only painting the selection, don't bother to paint if there is none.
885         return;
886     }
887
888     Range markedTextRange = KWQ(document()->part())->markedTextRange();
889     bool haveMarkedText = markedTextRange.handle() != 0 && markedTextRange.startContainer() == node();
890
891     if ((haveSelection || haveMarkedText) && i.phase != PaintActionSelection && !isPrinting) {
892         s = startBox;
893
894         // run until we find a text box that is outside the range, then we know we can stop
895         do {
896             if (s->m_truncation == cFullTruncation)
897                 continue;
898
899             RenderStyle* _style = pseudoStyle && s->m_firstLine ? pseudoStyle : style();
900
901             if (_style->font() != p->font())
902                 p->setFont(_style->font());
903
904             font = &_style->htmlFont(); // Always update, since smallCaps is not stored in the QFont.
905
906             if (haveMarkedText)
907                 s->paintMarkedTextBackground(font, this, p, _style, tx, ty, markedTextRange.startOffset(), markedTextRange.endOffset());
908
909             if (haveSelection)
910                 s->paintSelection(font, this, p, _style, tx, ty, startPos, endPos);
911
912         } while (((s = s->nextTextBox()) != 0) && s->checkVerticalPoint(i.r.y(), ty, i.r.height()));
913     }
914
915
916     // Pass 2: now paint the foreground, including text and decorations
917
918     QValueList<DocumentMarker> markers = document()->markersForNode(node());
919     QValueListIterator <DocumentMarker> markerIt = markers.begin();
920
921     s = startBox;
922
923     // run until we find a text box that is outside the range, then we know we can stop
924     do {
925         if (isPrinting) {
926             if (ty+s->m_y+s->height() > i.r.y() + i.r.height()) {
927                 RenderCanvas* canvasObj = canvas();
928                 if (ty+s->m_y < canvasObj->truncatedAt())
929                     canvasObj->setBestTruncatedAt(ty+s->m_y, this);
930                 // Let's stop here.
931                 break;
932             }
933         }
934
935         if (s->m_truncation == cFullTruncation)
936             continue;
937         
938         RenderStyle* _style = pseudoStyle && s->m_firstLine ? pseudoStyle : style();
939
940         if (_style->font() != p->font())
941             p->setFont(_style->font());
942
943         font = &_style->htmlFont(); // Always update, since smallCaps is not stored in the QFont.
944
945         QColor textColor = _style->color();
946         if (_style->shouldCorrectTextColor()) {
947             textColor = correctedTextColor(textColor, _style->backgroundColor());
948         }
949
950         if(textColor != p->pen().color())
951             p->setPen(textColor);
952
953         // Set a text shadow if we have one.
954         // FIXME: Support multiple shadow effects.  Need more from the CG API before
955         // we can do this.
956         bool setShadow = false;
957         if (_style->textShadow()) {
958             p->setShadow(_style->textShadow()->x, _style->textShadow()->y,
959                          _style->textShadow()->blur, _style->textShadow()->color);
960             setShadow = true;
961         }
962         
963         if (s->m_len > 0) {
964             bool paintSelectedTextOnly = (i.phase == PaintActionSelection);
965             bool paintSelectedTextSeparately = false; // Whether or not we have to do multiple paints.  Only
966             // necessary when a custom ::selection foreground color is applied.
967             QColor selectionColor = p->pen().color();
968             ShadowData* selectionTextShadow = 0;
969             if (haveSelection) {
970                 RenderStyle* pseudoStyle = getPseudoStyle(RenderStyle::SELECTION);
971                 if (pseudoStyle) {
972                     if (pseudoStyle->color() != selectionColor || pseudoStyle->textShadow()) {
973                         if (!paintSelectedTextOnly)
974                             paintSelectedTextSeparately = true;
975                         if (pseudoStyle->color() != selectionColor)
976                             selectionColor = pseudoStyle->color();
977                         if (pseudoStyle->textShadow())
978                             selectionTextShadow = pseudoStyle->textShadow();
979                     }
980                 }
981             }
982
983             if (!paintSelectedTextOnly && !paintSelectedTextSeparately) {
984                 // paint all the text
985
986                 // FIXME: Handle RTL direction, handle reversed strings.  For now truncation can only be turned on
987                 // for non-reversed LTR strings.
988                 int endPoint = s->m_len;
989                 if (s->m_truncation != cNoTruncation)
990                     endPoint = s->m_truncation - s->m_start;
991                 font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline,
992                                str->s, str->l, s->m_start, endPoint,
993                                s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered());
994             } else {
995                 int offset = s->m_start;
996                 int sPos = QMAX( startPos - offset, 0 );
997                 int ePos = QMIN( endPos - offset, s->m_len );
998                 if (paintSelectedTextSeparately) {
999                     // paint only the text that is not selected
1000
1001                     if (sPos >= ePos) {
1002                         font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline,
1003                                        str->s, str->l, s->m_start, s->m_len,
1004                                        s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered());
1005                     } else {
1006                         if (sPos-1 >= 0) {
1007                             font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
1008                                            str->l, s->m_start, s->m_len,
1009                                            s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered(), 0, sPos);
1010                         }
1011                         if (ePos < s->m_start+s->m_len) {
1012                             font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
1013                                            str->l, s->m_start, s->m_len,
1014                                            s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered(), ePos, -1);
1015                         }
1016                     }
1017                 }
1018                 
1019                 if ( sPos < ePos ) {
1020                     // paint only the text that is selected
1021                     if (selectionColor != p->pen().color())
1022                         p->setPen(selectionColor);
1023                     
1024                     if (selectionTextShadow)
1025                         p->setShadow(selectionTextShadow->x,
1026                                      selectionTextShadow->y,
1027                                      selectionTextShadow->blur,
1028                                      selectionTextShadow->color);
1029                     font->drawText(p, s->m_x + tx, s->m_y + ty + s->m_baseline, str->s,
1030                                    str->l, s->m_start, s->m_len,
1031                                    s->m_toAdd, s->m_reversed ? QPainter::RTL : QPainter::LTR, style()->visuallyOrdered(), sPos, ePos);
1032                     if (selectionTextShadow)
1033                         p->clearShadow();
1034                 }
1035             } 
1036         }
1037
1038         // paint decorations
1039
1040         if (d != TDNONE && i.phase != PaintActionSelection &&
1041             style()->htmlHacks()) {
1042             p->setPen(_style->color());
1043             s->paintDecoration(p, tx, ty, d);
1044         }
1045
1046         // Draw any doc markers that touch this run
1047         // Note s->end() points at the last char, not one past it like endOffset and ranges do
1048         
1049         if (i.phase != PaintActionSelection) {
1050             for ( ; markerIt != markers.end(); markerIt++) {
1051                 DocumentMarker marker = *markerIt;
1052
1053                 if (marker.endOffset <= s->start()) {
1054                     // marker is completely before this run.  This might be a marker that sits before the
1055                     // first run we draw, or markers that were within runs we skipped due to truncation.
1056                     continue;
1057                 }
1058                 
1059                 if (marker.startOffset <= s->end()) {
1060                     // marker intersects this run.  Paint it.
1061                     s->paintMarker(p, tx, ty, marker);
1062                     if (marker.endOffset > s->end()+1) {
1063                         // marker also runs into the next run. Bail now, no more marker advancement.
1064                         break;
1065                     }
1066                 } else {
1067                     // marker is completely after this run, bail.  A later run will paint it.
1068                     break;
1069                 }
1070             }
1071         }
1072
1073         if (setShadow)
1074             p->clearShadow();
1075         
1076 #ifdef BIDI_DEBUG
1077         {
1078             int h = lineHeight( false ) + paddingTop() + paddingBottom() + borderTop() + borderBottom();
1079             QColor c2 = QColor("#0000ff");
1080             drawBorder(p, tx, ty, tx+1, ty + h,
1081                        RenderObject::BSLeft, c2, c2, SOLID, 1, 1);
1082             drawBorder(p, tx + s->m_width, ty, tx + s->m_width + 1, ty + h,
1083                        RenderObject::BSRight, c2, c2, SOLID, 1, 1);
1084         }
1085 #endif
1086     } while (((s = s->nextTextBox()) != 0) && s->checkVerticalPoint(i.r.y(), ty, i.r.height()));
1087 #else
1088 #error This file no longer works without Apple changes
1089 #endif
1090 }
1091
1092 #ifdef APPLE_CHANGES
1093
1094 bool RenderText::allAscii() const
1095 {
1096     if (m_allAsciiChecked)
1097         return m_allAscii;
1098     m_allAsciiChecked = true;
1099     
1100     unsigned int i;
1101     for (i = 0; i < str->l; i++){
1102         if (str->s[i].unicode() >= 0x7f){
1103             m_allAscii = false;
1104             return m_allAscii;
1105         }
1106     }
1107     
1108     m_allAscii = true;
1109     
1110     return m_allAscii;
1111 }
1112
1113 bool RenderText::shouldUseMonospaceCache(const Font *f) const
1114 {
1115     return (f && f->isFixedPitch() && allAscii() && !style()->htmlFont().isSmallCaps());
1116 }
1117
1118 // We cache the width of the ' ' character for <pre> text.  We could go futher
1119 // and cache a widths array for all styles, at the expense of increasing the size of the
1120 // RenderText.
1121 void RenderText::cacheWidths()
1122 {
1123     const Font *f = htmlFont( false );
1124     
1125     if (shouldUseMonospaceCache(f)){    
1126         float fw;
1127         QChar c(' ');
1128         f->floatCharacterWidths( &c, 1, 0, 1, 0, &fw);
1129         m_monospaceCharacterWidth = (int)fw;
1130     }
1131     else
1132         m_monospaceCharacterWidth = 0;
1133 }
1134
1135
1136 inline int RenderText::widthFromCache(const Font *f, int start, int len) const
1137 {
1138     if (m_monospaceCharacterWidth != 0){
1139         int i, w = 0;
1140         for (i = start; i < start+len; i++){
1141             int dir = str->s[i].direction();
1142             if (dir != QChar::DirNSM && dir != QChar::DirBN)
1143                 w += m_monospaceCharacterWidth;
1144         }
1145         return w;
1146     }
1147     
1148     return f->width(str->s, str->l, start, len);
1149 }
1150 #ifdef XXX
1151 inline int RenderText::widthFromCache(const Font *f, int start, int len) const
1152 {
1153     if (m_monospaceCharacterWidth != 0){
1154         return len * m_monospaceCharacterWidth;
1155     }
1156
1157     return f->width(str->s, str->l, start, len);
1158 }
1159 #endif
1160
1161 #endif
1162
1163 void RenderText::trimmedMinMaxWidth(int& beginMinW, bool& beginWS, 
1164                                     int& endMinW, bool& endWS,
1165                                     bool& hasBreakableChar, bool& hasBreak,
1166                                     int& beginMaxW, int& endMaxW,
1167                                     int& minW, int& maxW, bool& stripFrontSpaces)
1168 {
1169     bool isPre = style()->whiteSpace() == PRE;
1170     if (isPre)
1171         stripFrontSpaces = false;
1172     
1173     int len = str->l;
1174     if (len == 0 || (stripFrontSpaces && str->containsOnlyWhitespace())) {
1175         maxW = 0;
1176         hasBreak = false;
1177         return;
1178     }
1179     
1180     minW = m_minWidth;
1181     maxW = m_maxWidth;
1182     beginWS = stripFrontSpaces ? false : m_hasBeginWS;
1183     endWS = m_hasEndWS;
1184     
1185     beginMinW = m_beginMinWidth;
1186     endMinW = m_endMinWidth;
1187     
1188     hasBreakableChar = m_hasBreakableChar;
1189     hasBreak = m_hasBreak;
1190
1191     if (stripFrontSpaces && (str->s[0] == ' ' || (!isPre && str->s[0] == '\n'))) {
1192         const Font *f = htmlFont( false );
1193         QChar space[1]; space[0] = ' ';
1194         int spaceWidth = f->width(space, 1, 0);
1195         maxW -= spaceWidth;
1196     }
1197     
1198     stripFrontSpaces = !isPre && m_hasEndWS;
1199     
1200     if (style()->whiteSpace() == NOWRAP)
1201         minW = maxW;
1202     else if (minW > maxW)
1203         minW = maxW;
1204         
1205     // Compute our max widths by scanning the string for newlines.
1206     if (hasBreak) {
1207         const Font *f = htmlFont( false );
1208         bool firstLine = true;
1209         beginMaxW = endMaxW = maxW;
1210         for (int i = 0; i < len; i++)
1211         {
1212             int linelen = 0;
1213             while (i+linelen < len && str->s[i+linelen] != '\n')
1214                 linelen++;
1215                 
1216             if (linelen)
1217             {
1218 #if !APPLE_CHANGES
1219                 endMaxW = f->width(str->s, str->l, i, linelen);
1220 #else
1221                 endMaxW = widthFromCache(f, i, linelen);
1222 #endif
1223                 if (firstLine) {
1224                     firstLine = false;
1225                     beginMaxW = endMaxW;
1226                 }
1227                 i += linelen;
1228             }
1229             else if (firstLine) {
1230                 beginMaxW = 0;
1231                 firstLine = false;
1232             }
1233             
1234             if (i == len-1)
1235                 // A <pre> run that ends with a newline, as in, e.g.,
1236                 // <pre>Some text\n\n<span>More text</pre>
1237                 endMaxW = 0;
1238         }
1239     }
1240 }
1241
1242 void RenderText::calcMinMaxWidth()
1243 {
1244     KHTMLAssert( !minMaxKnown() );
1245
1246     // ### calc Min and Max width...
1247     m_minWidth = m_beginMinWidth = m_endMinWidth = 0;
1248     m_maxWidth = 0;
1249
1250     if (isBR())
1251         return;
1252         
1253     int currMinWidth = 0;
1254     int currMaxWidth = 0;
1255     m_hasBreakableChar = m_hasBreak = m_hasBeginWS = m_hasEndWS = false;
1256     
1257     // ### not 100% correct for first-line
1258     const Font *f = htmlFont( false );
1259     int wordSpacing = style()->wordSpacing();
1260     int len = str->l;
1261     bool ignoringSpaces = false;
1262     bool isSpace = false;
1263     bool isPre = style()->whiteSpace() == PRE;
1264     bool firstWord = true;
1265     bool firstLine = true;
1266     for(int i = 0; i < len; i++)
1267     {
1268         const QChar c = str->s[i];
1269         
1270         bool previousCharacterIsSpace = isSpace;
1271         
1272         bool isNewline = false;
1273         if (c == '\n') {
1274             if (isPre) {
1275                 m_hasBreak = true;
1276                 isNewline = true;
1277                 isSpace = false;
1278             }
1279             else
1280                 isSpace = true;
1281         } else {
1282             isSpace = c == ' ';
1283         }
1284         
1285         if ((isSpace || isNewline) && i == 0)
1286             m_hasBeginWS = true;
1287         if ((isSpace || isNewline) && i == len-1)
1288             m_hasEndWS = true;
1289             
1290         if (!ignoringSpaces && !isPre && previousCharacterIsSpace && isSpace)
1291             ignoringSpaces = true;
1292         
1293         if (ignoringSpaces && !isSpace)
1294             ignoringSpaces = false;
1295             
1296         if (ignoringSpaces || (i > 0 && c.unicode() == SOFT_HYPHEN)) // Ignore spaces and soft hyphens
1297             continue;
1298         
1299         int wordlen = 0;
1300         while (i+wordlen < len && str->s[i+wordlen] != '\n' && str->s[i+wordlen] != ' ' &&
1301                (i+wordlen == 0 || str->s[i+wordlen].unicode() != SOFT_HYPHEN) && // Skip soft hyphens
1302                (wordlen == 0 || !isBreakable( str->s, i+wordlen, str->l)))
1303             wordlen++;
1304             
1305         if (wordlen)
1306         {
1307 #if !APPLE_CHANGES
1308             int w = f->width(str->s, str->l, i, wordlen);
1309 #else
1310             int w = widthFromCache(f, i, wordlen);
1311 #endif
1312             currMinWidth += w;
1313             currMaxWidth += w;
1314             
1315             bool isBreakableCharSpace = (i+wordlen < len) ? ((!isPre && str->s[i+wordlen] == '\n') || 
1316                                                              str->s[i+wordlen] == ' ') : false;
1317
1318             if (i+wordlen < len && style()->whiteSpace() == NORMAL)
1319                 m_hasBreakableChar = true;
1320             
1321             // Add in wordspacing to our maxwidth, but not if this is the last word on a line or the
1322             // last word in the run.
1323             if (wordSpacing && isBreakableCharSpace && !containsOnlyWhitespace(i+wordlen, len-(i+wordlen)))
1324                 currMaxWidth += wordSpacing;
1325
1326             if (firstWord) {
1327                 firstWord = false;
1328                 // If the first character in the run is breakable, then we consider ourselves to have a beginning
1329                 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
1330                 // being appended to a previous text run when considering the total minimum width of the containing block.
1331                 bool hasBreak = isBreakable(str->s, i, str->l);
1332                 if (hasBreak)
1333                     m_hasBreakableChar = true;
1334                 m_beginMinWidth = hasBreak ? 0 : w;
1335             }
1336             m_endMinWidth = w;
1337             
1338             if (currMinWidth > m_minWidth) m_minWidth = currMinWidth;
1339             currMinWidth = 0;
1340             
1341             i += wordlen-1;
1342         }
1343         else {
1344             // Nowrap can never be broken, so don't bother setting the
1345             // breakable character boolean. Pre can only be broken if we encounter a newline.
1346             if (style()->whiteSpace() == NORMAL || isNewline)
1347                 m_hasBreakableChar = true;
1348
1349             if (currMinWidth > m_minWidth) m_minWidth = currMinWidth;
1350             currMinWidth = 0;
1351             
1352             if (isNewline) // Only set if isPre was true and we saw a newline.
1353             {
1354                 if (firstLine) {
1355                     firstLine = false;
1356                     m_beginMinWidth = currMaxWidth;
1357                 }
1358                 
1359                 if (currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
1360                 currMaxWidth = 0;
1361             }
1362             else
1363             {
1364                 currMaxWidth += f->width( str->s, str->l, i + wordlen );
1365             }
1366         }
1367     }
1368     
1369     if(currMinWidth > m_minWidth) m_minWidth = currMinWidth;
1370     if(currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
1371
1372     if (style()->whiteSpace() != NORMAL)
1373         m_minWidth = m_maxWidth;
1374
1375     if (isPre) {
1376         if (firstLine)
1377             m_beginMinWidth = m_maxWidth;
1378         m_endMinWidth = currMaxWidth;
1379     }
1380     
1381     setMinMaxKnown();
1382     //kdDebug( 6040 ) << "Text::calcMinMaxWidth(): min = " << m_minWidth << " max = " << m_maxWidth << endl;
1383 }
1384
1385 bool RenderText::containsOnlyWhitespace(unsigned int from, unsigned int len) const
1386 {
1387     unsigned int currPos;
1388     for (currPos = from; 
1389          currPos < from+len && (str->s[currPos] == '\n' || str->s[currPos].unicode() == ' '); 
1390          currPos++);
1391     return currPos >= (from+len);
1392 }
1393
1394 int RenderText::minXPos() const
1395 {
1396     if (!m_firstTextBox) return 0;
1397     int retval=6666666;
1398     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1399         retval = kMin(retval, (int)box->m_x);
1400     return retval;
1401 }
1402
1403 int RenderText::xPos() const
1404 {
1405     return m_firstTextBox ? m_firstTextBox->m_x : 0;
1406 }
1407
1408 int RenderText::yPos() const
1409 {
1410     return m_firstTextBox ? m_firstTextBox->m_y : 0;
1411 }
1412
1413 const QFont &RenderText::font()
1414 {
1415     return style()->font();
1416 }
1417
1418 void RenderText::setSelectionState(SelectionState s)
1419 {
1420     m_selectionState = s;
1421     if (s == SelectionStart || s == SelectionEnd || s == SelectionBoth) {
1422         int startPos, endPos;
1423         selectionStartEnd(startPos, endPos);
1424         if(selectionState() == SelectionStart)
1425             endPos = str->l;
1426         else if(selectionState() == SelectionEnd)
1427             startPos = 0;
1428         
1429         for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1430             if (box->isSelected(startPos, endPos)) {
1431                 RootInlineBox* line = box->root();
1432                 if (line)
1433                     line->setHasSelectedChildren(true);
1434             }
1435         }
1436     }
1437     else {
1438         for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1439             RootInlineBox* line = box->root();
1440             if (line)
1441                 line->setHasSelectedChildren(s == SelectionInside);
1442         }
1443     }
1444     
1445     containingBlock()->setSelectionState(s);
1446 }
1447
1448 void RenderText::setTextWithOffset(DOMStringImpl *text, uint offset, uint len, bool force)
1449 {
1450     uint oldLen = str ? str->l : 0;
1451     uint newLen = text ? text->l : 0;
1452     int delta = newLen - oldLen;
1453     uint end = len ? offset+len-1 : offset;
1454
1455     RootInlineBox* firstRootBox = 0;
1456     RootInlineBox* lastRootBox = 0;
1457     
1458     bool dirtiedLines = false;
1459     
1460     // Dirty all text boxes that include characters in between offset and offset+len.
1461     for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
1462         // Text run is entirely before the affected range.
1463         if (curr->end() < offset)
1464             continue;
1465         
1466         // Text run is entirely after the affected range.
1467         if (curr->start() > end) {
1468             curr->offsetRun(delta);
1469             RootInlineBox* root = curr->root();
1470             if (!firstRootBox) {
1471                 firstRootBox = root;
1472                 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.
1473                     firstRootBox->markDirty();
1474                     dirtiedLines = true;
1475                 }
1476             }
1477             lastRootBox = root;
1478         }
1479         else if (curr->end() >= offset && curr->end() <= end) {
1480             curr->dirtyLineBoxes(); // Text run overlaps with the left end of the affected range.
1481             dirtiedLines = true;
1482         }
1483         else if (curr->start() <= offset && curr->end() >= end) {
1484             curr->dirtyLineBoxes(); // Text run subsumes the affected range.
1485             dirtiedLines = true;
1486         }
1487         else if (curr->start() <= end && curr->end() >= end) {
1488             curr->dirtyLineBoxes(); // Text run overlaps with right end of the affected range.
1489             dirtiedLines = true;
1490         }
1491     }
1492     
1493     // Now we have to walk all of the clean lines and adjust their cached line break information
1494     // to reflect our updated offsets.
1495     if (lastRootBox)
1496         lastRootBox = lastRootBox->nextRootBox();
1497     if (firstRootBox) {
1498         RootInlineBox* prev = firstRootBox->prevRootBox();
1499         if (prev)
1500             firstRootBox = prev;
1501     }
1502     for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
1503         if (!curr->isDirty() && curr->lineBreakObj() == this && curr->lineBreakPos() > end)
1504             curr->setLineBreakPos(curr->lineBreakPos()+delta);
1505     }
1506     
1507     m_linesDirty = dirtiedLines;
1508     setText(text, force);
1509 }
1510
1511 void RenderText::setText(DOMStringImpl *text, bool force)
1512 {
1513     if (!text)
1514         return;
1515     if (!force && str == text)
1516         return;
1517     if (str)
1518         str->deref();
1519
1520     str = text;
1521     if (str) {
1522         str = str->replace('\\', backslashAsCurrencySymbol());
1523         if ( style() ) {
1524             switch(style()->textTransform()) {
1525                 case CAPITALIZE:   str = str->capitalize();  break;
1526                 case UPPERCASE:   str = str->upper();       break;
1527                 case LOWERCASE:  str = str->lower();       break;
1528                 case NONE:
1529                 default:;
1530             }
1531         }
1532         str->ref();
1533     }
1534
1535 #if APPLE_CHANGES
1536     cacheWidths();
1537 #endif
1538
1539     // ### what should happen if we change the text of a
1540     // RenderBR object ?
1541     KHTMLAssert(!isBR() || (str->l == 1 && (*str->s) == '\n'));
1542     KHTMLAssert(!str->l || str->s);
1543
1544     setNeedsLayoutAndMinMaxRecalc();
1545     
1546 #ifdef BIDI_DEBUG
1547     QConstString cstr(str->s, str->l);
1548     kdDebug( 6040 ) << "RenderText::setText( " << cstr.string().length() << " ) '" << cstr.string() << "'" << endl;
1549 #endif
1550 }
1551
1552 int RenderText::height() const
1553 {
1554     // FIXME: Why use line-height? Shouldn't we be adding in the height of the last text box? -dwh
1555     int retval = 0;
1556     if (firstTextBox())
1557         retval = lastTextBox()->m_y + lineHeight(false) - firstTextBox()->m_y;
1558     return retval;
1559 }
1560
1561 short RenderText::lineHeight(bool firstLine, bool) const
1562 {
1563     // Always use the interior line height of the parent (e.g., if our parent is an inline block).
1564     return parent()->lineHeight(firstLine, true);
1565 }
1566
1567 short RenderText::baselinePosition( bool firstLine, bool ) const
1568 {
1569     const QFontMetrics &fm = metrics( firstLine );
1570     return fm.ascent() +
1571         ( lineHeight( firstLine ) - fm.height() ) / 2;
1572 }
1573
1574 void RenderText::dirtyLineBoxes(bool fullLayout, bool)
1575 {
1576     if (fullLayout)
1577         deleteTextBoxes();
1578     else if (!m_linesDirty) {
1579         for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1580             box->dirtyLineBoxes();
1581     }
1582     m_linesDirty = false;
1583 }
1584
1585 InlineBox* RenderText::createInlineBox(bool, bool isRootLineBox, bool)
1586 {
1587     KHTMLAssert(!isRootLineBox);
1588     InlineTextBox* textBox = new (renderArena()) InlineTextBox(this);
1589     if (!m_firstTextBox)
1590         m_firstTextBox = m_lastTextBox = textBox;
1591     else {
1592         m_lastTextBox->setNextLineBox(textBox);
1593         textBox->setPreviousLineBox(m_lastTextBox);
1594         m_lastTextBox = textBox;
1595     }
1596     return textBox;
1597 }
1598
1599 void RenderText::position(InlineBox* box, int from, int len, bool reverse)
1600 {
1601     InlineTextBox *s = static_cast<InlineTextBox*>(box);
1602     
1603     // ### should not be needed!!!
1604     if (len == 0) {
1605         // We want the box to be destroyed.
1606         s->remove();
1607         s->detach(renderArena());
1608         m_firstTextBox = m_lastTextBox = 0;
1609         return;
1610     }
1611     
1612     reverse = reverse && !style()->visuallyOrdered();
1613
1614 #ifdef DEBUG_LAYOUT
1615     QChar *ch = str->s+from;
1616     QConstString cstr(ch, len);
1617     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;
1618 #endif
1619
1620     s->m_reversed = reverse;
1621     s->m_start = from;
1622     s->m_len = len;
1623 }
1624
1625 unsigned int RenderText::width(unsigned int from, unsigned int len, bool firstLine) const
1626 {
1627     if(!str->s || from > str->l ) return 0;
1628     if ( from + len > str->l ) len = str->l - from;
1629
1630     const Font *f = htmlFont( firstLine );
1631     return width( from, len, f );
1632 }
1633
1634 unsigned int RenderText::width(unsigned int from, unsigned int len, const Font *f) const
1635 {
1636     if(!str->s || from > str->l ) return 0;
1637     if ( from + len > str->l ) len = str->l - from;
1638
1639     int w;
1640     if ( f == &style()->htmlFont() && from == 0 && len == str->l )
1641          w = m_maxWidth;
1642 #if APPLE_CHANGES
1643     else if (f == &style()->htmlFont())
1644         w = widthFromCache (f, from, len);
1645 #endif
1646     else
1647         w = f->width(str->s, str->l, from, len );
1648
1649     //kdDebug( 6040 ) << "RenderText::width(" << from << ", " << len << ") = " << w << endl;
1650     return w;
1651 }
1652
1653 int RenderText::width() const
1654 {
1655     int w;
1656     int minx = 100000000;
1657     int maxx = 0;
1658     // slooow
1659     for (InlineTextBox* s = firstTextBox(); s; s = s->nextTextBox()) {
1660         if(s->m_x < minx)
1661             minx = s->m_x;
1662         if(s->m_x + s->m_width > maxx)
1663             maxx = s->m_x + s->m_width;
1664     }
1665
1666     w = kMax(0, maxx-minx);
1667
1668     return w;
1669 }
1670
1671 QRect RenderText::getAbsoluteRepaintRect()
1672 {
1673     RenderObject *cb = containingBlock();
1674     return cb->getAbsoluteRepaintRect();
1675 }
1676
1677 QRect RenderText::selectionRect()
1678 {
1679     QRect rect;
1680     if (selectionState() == SelectionNone)
1681         return rect;
1682     RenderBlock* cb =  containingBlock();
1683     if (!cb)
1684         return rect;
1685     
1686     // Now calculate startPos and endPos for painting selection.
1687     // We include a selection while endPos > 0
1688     int startPos, endPos;
1689     if (selectionState() == SelectionInside) {
1690         // We are fully selected.
1691         startPos = 0;
1692         endPos = str->l;
1693     } else {
1694         selectionStartEnd(startPos, endPos);
1695         if (selectionState() == SelectionStart)
1696             endPos = str->l;
1697         else if (selectionState() == SelectionEnd)
1698             startPos = 0;
1699     }
1700     
1701     if (startPos == endPos)
1702         return rect;
1703
1704     int absx, absy;
1705     cb->absolutePosition(absx, absy);
1706     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox()) {
1707         QRect r = box->selectionRect(absx, absy, startPos, endPos);
1708         if (!r.isEmpty()) {
1709             if (rect.isEmpty())
1710                 rect = r;
1711             else
1712                 rect = rect.unite(r);
1713         }
1714     }
1715
1716     return rect;
1717 }
1718
1719 short RenderText::verticalPositionHint( bool firstLine ) const
1720 {
1721     return parent()->verticalPositionHint( firstLine );
1722 }
1723
1724 const QFontMetrics &RenderText::metrics(bool firstLine) const
1725 {
1726     return style(firstLine)->fontMetrics();
1727 }
1728
1729 const Font *RenderText::htmlFont(bool firstLine) const
1730 {
1731     return &style(firstLine)->htmlFont();
1732 }
1733
1734 long RenderText::caretMinOffset() const
1735 {
1736     if (!firstTextBox()) 
1737         return 0;
1738     // EDIT FIXME: it is *not* guaranteed that the first run contains the lowest offset
1739     // Either make this a linear search (slow),
1740     // or maintain an index (needs much mem),
1741     // or calculate and store it in bidi.cpp (needs calculation even if not needed)
1742     return firstTextBox()->m_start;
1743 }
1744
1745 long RenderText::caretMaxOffset() const
1746 {
1747     if (!firstTextBox()) 
1748         return str->l;
1749     // EDIT FIXME: it is *not* guaranteed that the last run contains the highest offset
1750     // Either make this a linear search (slow),
1751     // or maintain an index (needs much mem),
1752     // or calculate and store it in bidi.cpp (needs calculation even if not needed)
1753     return lastTextBox()->m_start + lastTextBox()->m_len;
1754 }
1755
1756 unsigned long RenderText::caretMaxRenderedOffset() const
1757 {
1758     int l = 0;
1759     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1760         l += box->m_len;
1761     return l;
1762 }
1763
1764 InlineBox *RenderText::inlineBox(long offset, EAffinity affinity)
1765 {
1766     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1767         if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
1768             if (affinity == DOWNSTREAM && box->nextTextBox() && offset == box->m_start + box->m_len)
1769                 return box->nextTextBox();
1770             return box;
1771         }
1772         else if (offset < box->m_start) {
1773             // The offset we're looking for is before this node
1774             // this means the offset must be in content that is
1775             // not rendered.
1776             return box->prevTextBox() ? box->prevTextBox() : firstTextBox();
1777         }
1778     }
1779     
1780     return 0;
1781 }
1782
1783 RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str,
1784                                        int startOffset, int endOffset)
1785 :RenderText(_node, _str->substring(startOffset, endOffset)), 
1786 m_start(startOffset), m_end(endOffset), m_generatedContentStr(0)
1787 {}
1788
1789 RenderTextFragment::RenderTextFragment(DOM::NodeImpl* _node, DOM::DOMStringImpl* _str)
1790 :RenderText(_node, _str), m_start(0)
1791 {
1792     m_generatedContentStr = _str;
1793     if (_str) {
1794         _str->ref();
1795         m_end = _str->l;
1796     }
1797     else
1798         m_end = 0;
1799 }
1800     
1801 RenderTextFragment::~RenderTextFragment()
1802 {
1803     if (m_generatedContentStr)
1804         m_generatedContentStr->deref();
1805 }
1806
1807 bool RenderTextFragment::isTextFragment() const
1808 {
1809     return true;
1810 }
1811
1812 DOM::DOMStringImpl* RenderTextFragment::originalString() const
1813 {
1814     DOM::DOMStringImpl* result = 0;
1815     if (element())
1816         result = element()->string();
1817     else
1818         result = contentString();
1819     if (result && (start() > 0 || start() < result->l))
1820         result = result->substring(start(), end());
1821     return result;
1822 }
1823 #undef BIDI_DEBUG
1824 #undef DEBUG_LAYOUT