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