c5005f090ee1e8b36b3e04452b35f32c9e0abb64
[WebKit-https.git] / WebCore / rendering / RenderText.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, 2005, 2006 Apple Computer, Inc.
7  * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22  * Boston, MA 02111-1307, USA.
23  *
24  */
25
26 #include "config.h"
27 #include "RenderText.h"
28
29 #include "DeprecatedString.h"
30 #include "InlineTextBox.h"
31 #include "Pen.h"
32 #include "Range.h"
33 #include "RenderArena.h"
34 #include "RenderBlock.h"
35 #include "break_lines.h"
36 #include <unicode/ubrk.h>
37 #include <wtf/AlwaysInline.h>
38
39 using namespace std;
40
41 namespace WebCore {
42
43 UBreakIterator* characterBreakIterator(const StringImpl* i)
44 {
45     if (!i)
46         return 0;
47
48     // The locale is currently ignored when determining character cluster breaks.
49     // This may change in the future, according to Deborah Goldsmith.
50     static bool createdIterator = false;
51     static UBreakIterator* iterator;
52     UErrorCode status;
53     if (!createdIterator) {
54         status = U_ZERO_ERROR;
55         iterator = ubrk_open(UBRK_CHARACTER, "en_us", 0, 0, &status);
56         createdIterator = true;
57     }
58     if (!iterator)
59         return 0;
60
61     status = U_ZERO_ERROR;
62     ubrk_setText(iterator, reinterpret_cast<const UChar*>(i->characters()), i->length(), &status);
63     if (status != U_ZERO_ERROR)
64         return 0;
65
66     return iterator;
67 }
68
69 int RenderText::previousOffset(int current) const
70 {
71     UBreakIterator* iterator = characterBreakIterator(str.get());
72     if (!iterator)
73         return current - 1;
74
75     long result = ubrk_preceding(iterator, current);
76     if (result == UBRK_DONE)
77         result = current - 1;
78
79     return result;
80 }
81
82 int RenderText::nextOffset(int current) const
83 {
84     UBreakIterator* iterator = characterBreakIterator(str.get());
85     if (!iterator)
86         return current + 1;
87     
88     long result = ubrk_following(iterator, current);
89     if (result == UBRK_DONE)
90         result = current + 1;
91
92     return result;
93 }
94
95 RenderText::RenderText(WebCore::Node* node, StringImpl *_str)
96      : RenderObject(node), str(_str), m_firstTextBox(0), m_lastTextBox(0)
97      , m_minWidth(-1), m_maxWidth(-1), m_selectionState(SelectionNone)
98      , m_linesDirty(false), m_containsReversedText(false)
99      , m_allAsciiChecked(false), m_allAscii(false)
100      , m_monospaceCharacterWidth(0)
101 {
102     setRenderText();
103     if (str)
104         str = str->replace('\\', backslashAsCurrencySymbol());
105     KHTMLAssert(!str || !str->length() || str->characters());
106 }
107
108 void RenderText::setStyle(RenderStyle *_style)
109 {
110     if ( style() != _style ) {
111         bool needToTransformText = (!style() && _style->textTransform() != TTNONE) ||
112                                    (style() && style()->textTransform() != _style->textTransform());
113
114         RenderObject::setStyle( _style );
115
116         if (needToTransformText) {
117             RefPtr<StringImpl> textToTransform = originalString();
118             if (textToTransform)
119                 setText(textToTransform.get(), true);
120         }
121         // setText also calls cacheWidths(), so there is no need to call it again in that case.
122         else
123             cacheWidths();
124     }
125 }
126
127 void RenderText::destroy()
128 {
129     if (!documentBeingDestroyed()) {
130         if (firstTextBox()) {
131             if (isBR()) {
132                 RootInlineBox* next = firstTextBox()->root()->nextRootBox();
133                 if (next)
134                     next->markDirty();
135             }
136             for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
137                 box->remove();
138         }
139         else if (parent())
140             parent()->dirtyLinesFromChangedChild(this);
141     }
142     deleteTextBoxes();
143     RenderObject::destroy();
144 }
145
146 void RenderText::extractTextBox(InlineTextBox* box)
147 {
148     m_lastTextBox = box->prevTextBox();
149     if (box == m_firstTextBox)
150         m_firstTextBox = 0;
151     if (box->prevTextBox())
152         box->prevTextBox()->setNextLineBox(0);
153     box->setPreviousLineBox(0);
154     for (InlineRunBox* curr = box; curr; curr = curr->nextLineBox())
155         curr->setExtracted();
156 }
157
158 void RenderText::attachTextBox(InlineTextBox* box)
159 {
160     if (m_lastTextBox) {
161         m_lastTextBox->setNextLineBox(box);
162         box->setPreviousLineBox(m_lastTextBox);
163     }
164     else
165         m_firstTextBox = box;
166     InlineTextBox* last = box;
167     for (InlineTextBox* curr = box; curr; curr = curr->nextTextBox()) {
168         curr->setExtracted(false);
169         last = curr;
170     }
171     m_lastTextBox = last;
172 }
173
174 void RenderText::removeTextBox(InlineTextBox* box)
175 {
176     if (box == m_firstTextBox)
177         m_firstTextBox = box->nextTextBox();
178     if (box == m_lastTextBox)
179         m_lastTextBox = box->prevTextBox();
180     if (box->nextTextBox())
181         box->nextTextBox()->setPreviousLineBox(box->prevTextBox());
182     if (box->prevTextBox())
183         box->prevTextBox()->setNextLineBox(box->nextTextBox());
184 }
185
186 void RenderText::deleteTextBoxes()
187 {
188     if (firstTextBox()) {
189         RenderArena* arena = renderArena();
190         InlineTextBox *curr = firstTextBox(), *next = 0;
191         while (curr) {
192             next = curr->nextTextBox();
193             curr->destroy(arena);
194             curr = next;
195         }
196         m_firstTextBox = m_lastTextBox = 0;
197     }
198 }
199
200 bool RenderText::isTextFragment() const
201 {
202     return false;
203 }
204
205 PassRefPtr<StringImpl> RenderText::originalString() const
206 {
207     return element() ? element()->string() : 0;
208 }
209
210 void RenderText::absoluteRects(DeprecatedValueList<IntRect>& rects, int _tx, int _ty)
211 {
212     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
213         rects.append(IntRect(_tx + box->xPos(), 
214                            _ty + box->yPos(), 
215                            box->width(), 
216                            box->height()));
217 }
218
219 DeprecatedValueList<IntRect> RenderText::lineBoxRects()
220 {
221     DeprecatedValueList<IntRect> rects;
222     int x = 0, y = 0;
223     absolutePositionForContent(x, y);
224     absoluteRects(rects, x, y);
225     return rects;
226 }
227
228 InlineTextBox* RenderText::findNextInlineTextBox(int offset, int &pos) const
229 {
230     // The text runs point to parts of the rendertext's str string
231     // (they don't include '\n')
232     // Find the text run that includes the character at @p offset
233     // and return pos, which is the position of the char in the run.
234
235     if (!m_firstTextBox)
236         return 0;
237     
238     InlineTextBox* s = m_firstTextBox;
239     int off = s->m_len;
240     while (offset > off && s->nextTextBox())
241     {
242         s = s->nextTextBox();
243         off = s->m_start + s->m_len;
244     }
245     // we are now in the correct text run
246     pos = (offset > off ? s->m_len : s->m_len - (off - offset) );
247     return s;
248 }
249
250 VisiblePosition RenderText::positionForCoordinates(int _x, int _y)
251 {
252     if (!firstTextBox() || stringLength() == 0)
253         return VisiblePosition(element(), 0, DOWNSTREAM);
254
255     int absx, absy;
256     RenderBlock *cb = containingBlock();
257     cb->absolutePositionForContent(absx, absy);
258     if (cb->hasOverflowClip())
259         cb->layer()->subtractScrollOffset(absx, absy);
260
261     // Get the offset for the position, since this will take rtl text into account.
262     int offset;
263     
264     // FIXME: We should be able to roll these special cases into the general cases in the loop below.
265     if (firstTextBox() && _y < absy + firstTextBox()->root()->bottomOverflow() && _x < absx + firstTextBox()->m_x) {
266         // at the y coordinate of the first line or above
267         // and the x coordinate is to the left than the first text box left edge
268         offset = firstTextBox()->offsetForPosition(_x - absx);
269         return VisiblePosition(element(), offset + firstTextBox()->m_start, DOWNSTREAM);
270     }
271     if (lastTextBox() && _y >= absy + lastTextBox()->root()->topOverflow() && _x >= absx + lastTextBox()->m_x + lastTextBox()->m_width) {
272         // at the y coordinate of the last line or below
273         // and the x coordinate is to the right than the last text box right edge
274         offset = lastTextBox()->offsetForPosition(_x - absx);
275         return VisiblePosition(element(), offset + lastTextBox()->m_start, DOWNSTREAM);
276     }
277
278     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
279         if (_y >= absy + box->root()->topOverflow() && _y < absy + box->root()->bottomOverflow()) {
280             offset = box->offsetForPosition(_x - absx);
281
282             if (_x < absx + box->m_x + box->m_width)
283                 // and the x coordinate is to the left of the right edge of this box
284                 // check to see if position goes in this box
285                 return VisiblePosition(element(), offset + box->m_start, VP_UPSTREAM_IF_POSSIBLE);
286
287             if (!box->prevOnLine() && _x < absx + box->m_x)
288                 // box is first on line
289                 // and the x coordinate is to the left of the first text box left edge
290                 return VisiblePosition(element(), offset + box->m_start, DOWNSTREAM);
291
292             if (!box->nextOnLine())
293                 // box is last on line
294                 // and the x coordinate is to the right of the last text box right edge
295                 // generate VisiblePosition, use UPSTREAM affinity if possible
296                 return VisiblePosition(element(), offset + box->m_start, VP_UPSTREAM_IF_POSSIBLE);
297         }
298     }
299     
300     return VisiblePosition(element(), 0, DOWNSTREAM);
301 }
302
303 #if __GNUC__
304 static RenderObject *firstRendererOnNextLine(InlineBox *box) __attribute__ ((unused));
305 #endif
306 static RenderObject *firstRendererOnNextLine(InlineBox *box)
307 {
308     if (!box)
309         return 0;
310
311     RootInlineBox *root = box->root();
312     if (!root)
313         return 0;
314         
315     if (root->endsWithBreak())
316         return 0;
317     
318     RootInlineBox *nextRoot = root->nextRootBox();
319     if (!nextRoot)
320         return 0;
321     
322     InlineBox *firstChild = nextRoot->firstChild();
323     if (!firstChild)
324         return 0;
325
326     return firstChild->object();
327 }
328
329 static RenderObject *lastRendererOnPrevLine(InlineBox *box)
330 {
331     if (!box)
332         return 0;
333     
334     RootInlineBox *root = box->root();
335     if (!root)
336         return 0;
337     
338     if (root->endsWithBreak())
339         return 0;
340     
341     RootInlineBox *prevRoot = root->prevRootBox();
342     if (!prevRoot)
343         return 0;
344     
345     InlineBox *lastChild = prevRoot->lastChild();
346     if (!lastChild)
347         return 0;
348     
349     return lastChild->object();
350 }
351
352 bool RenderText::atLineWrap(InlineTextBox *box, int offset)
353 {
354     if (box->nextTextBox() && !box->nextOnLine() && offset == box->m_start + box->m_len) {
355         if (!style()->preserveNewline() || box->isLineBreak())
356             return true;
357     }
358     
359     return false;
360 }
361
362 IntRect RenderText::caretRect(int offset, EAffinity affinity, int *extraWidthToEndOfLine)
363 {
364     if (!firstTextBox() || stringLength() == 0)
365         return IntRect();
366
367     // Find the text box for the given offset
368     InlineTextBox *box = 0;
369     for (box = firstTextBox(); box; box = box->nextTextBox()) {
370         if ((offset >= box->m_start) && (offset <= box->m_start + box->m_len)) {
371             // Check if downstream affinity would make us move to the next line.
372             if (atLineWrap(box, offset)) {
373                 // Use the next text box
374                 box = box->nextTextBox();
375                 offset = box->m_start;
376             } else {
377                 InlineTextBox *prevBox = box->prevTextBox();
378                 if (offset == box->m_start && affinity == UPSTREAM && prevBox && !box->prevOnLine()) {
379                     if (prevBox) {
380                         box = prevBox;
381                         offset = box->m_start + box->m_len;
382                     } else {
383                         RenderObject *object = lastRendererOnPrevLine(box);
384                         if (object)
385                             return object->caretRect(0, affinity);
386                     }
387                 }
388             }
389             break;
390         }
391     }
392     
393     if (!box) {
394         return IntRect();
395     }
396
397     int height = box->root()->bottomOverflow() - box->root()->topOverflow();
398     int top = box->root()->topOverflow();
399
400     int left = box->positionForOffset(offset);
401
402     // FIXME: should we use the width of the root inline box or the
403     // width of the containing block for this?
404     if (extraWidthToEndOfLine)
405         *extraWidthToEndOfLine = (box->root()->width() + box->root()->xPos()) - (left + 1);
406
407     int absx, absy;
408     absolutePositionForContent(absx, absy);
409     left += absx;
410     top += absy;
411
412     RenderBlock *cb = containingBlock();
413     int availableWidth = cb->lineWidth(top);
414     if (style()->autoWrap())
415         left = min(left, absx + box->m_x + availableWidth - 1);
416     
417     return IntRect(left, top, 1, height);
418 }
419
420 void RenderText::posOfChar(int chr, int &x, int &y)
421 {
422     absolutePositionForContent(x, y);
423
424     int pos;
425     if (InlineTextBox* s = findNextInlineTextBox(chr, pos)) {
426         // s is the line containing the character
427         x += s->m_x; // this is the x of the beginning of the line, but it's good enough for now
428         y += s->m_y;
429     }
430 }
431
432 bool RenderText::allAscii() const
433 {
434     if (m_allAsciiChecked)
435         return m_allAscii;
436     m_allAsciiChecked = true;
437     
438     unsigned i;
439     for (i = 0; i < str->length(); i++)
440         if ((*str)[i] > 0x7f) {
441             m_allAscii = false;
442             return m_allAscii;
443         }
444     
445     m_allAscii = true;
446     
447     return m_allAscii;
448 }
449
450 bool RenderText::shouldUseMonospaceCache(const Font *f) const
451 {
452     return (f && f->isFixedPitch() && allAscii() && !style()->font().isSmallCaps());
453 }
454
455 // We cache the width of the ' ' character for <pre> text.  We could go further
456 // and cache a widths array for all styles, at the expense of increasing the size of the
457 // RenderText.
458 void RenderText::cacheWidths()
459 {
460     const Font* f = font(false);
461     if (shouldUseMonospaceCache(f)) {
462         const UChar c = ' ';
463         m_monospaceCharacterWidth = f->width(TextRun(&c, 1));
464     } else {
465         m_monospaceCharacterWidth = 0;
466     }
467 }
468
469 ALWAYS_INLINE int RenderText::widthFromCache(const Font* f, int start, int len, int tabWidth, int xpos) const
470 {
471     if (m_monospaceCharacterWidth != 0) {
472         int i, w = 0;
473         for (i = start; i < start+len; i++) {
474             UChar c = (*str)[i];
475             UCharDirection dir = u_charDirection(c);
476             if (dir != U_DIR_NON_SPACING_MARK && dir != U_BOUNDARY_NEUTRAL) {
477                 if (c == '\t' && tabWidth != 0)
478                     w += tabWidth - ((xpos + w) % tabWidth);
479                 else
480                     w += m_monospaceCharacterWidth;
481                 if (QChar(c).isSpace() && i > start && !QChar((*str)[i - 1]).isSpace())
482                     w += f->wordSpacing();        
483             }
484         }
485
486         return w;
487     }
488     
489     return f->width(TextRun(string(), start, 0, len), tabWidth, xpos);
490 }
491
492 void RenderText::trimmedMinMaxWidth(int leadWidth,
493                                     int& beginMinW, bool& beginWS, 
494                                     int& endMinW, bool& endWS,
495                                     bool& hasBreakableChar, bool& hasBreak,
496                                     int& beginMaxW, int& endMaxW,
497                                     int& minW, int& maxW, bool& stripFrontSpaces)
498 {
499     bool collapseWhiteSpace = style()->collapseWhiteSpace();
500     if (!collapseWhiteSpace)
501         stripFrontSpaces = false;
502     
503     int len = str->length();
504     if (len == 0 || (stripFrontSpaces && str->containsOnlyWhitespace())) {
505         maxW = 0;
506         hasBreak = false;
507         return;
508     }
509     
510     // if the text has a variable width tab, we need to call 
511     if (m_hasTab)
512         calcMinMaxWidth(leadWidth);
513     
514     minW = m_minWidth;
515     maxW = m_maxWidth;
516     beginWS = !stripFrontSpaces && m_hasBeginWS;
517     endWS = m_hasEndWS;
518     
519     beginMinW = m_beginMinWidth;
520     endMinW = m_endMinWidth;
521     
522     hasBreakableChar = m_hasBreakableChar;
523     hasBreak = m_hasBreak;
524
525     if (stripFrontSpaces && ((*str)[0] == ' ' || ((*str)[0] == '\n' && !style()->preserveNewline()) || (*str)[0] == '\t')) {
526         const Font *f = font(false); // FIXME: Why is it ok to ignore first-line here?
527         const UChar space = ' ';
528         int spaceWidth = f->width(TextRun(&space, 1));
529         maxW -= spaceWidth + f->wordSpacing();
530     }
531     
532     stripFrontSpaces = collapseWhiteSpace && m_hasEndWS;
533     
534     if (!style()->autoWrap() || minW > maxW)
535         minW = maxW;
536
537     // Compute our max widths by scanning the string for newlines.
538     if (hasBreak) {
539         const Font *f = font(false);
540         bool firstLine = true;
541         beginMaxW = endMaxW = maxW;
542         for (int i = 0; i < len; i++)
543         {
544             int linelen = 0;
545             while (i+linelen < len && (*str)[i+linelen] != '\n')
546                 linelen++;
547                 
548             if (linelen)
549             {
550                 endMaxW = widthFromCache(f, i, linelen, tabWidth(), leadWidth + endMaxW);
551                 if (firstLine) {
552                     firstLine = false;
553                     leadWidth = 0;
554                     beginMaxW = endMaxW;
555                 }
556                 i += linelen;
557             }
558             else if (firstLine) {
559                 beginMaxW = 0;
560                 firstLine = false;
561                 leadWidth = 0;
562             }
563     
564             if (i == len-1)
565                 // A <pre> run that ends with a newline, as in, e.g.,
566                 // <pre>Some text\n\n<span>More text</pre>
567                 endMaxW = 0;
568         }
569     }
570 }
571
572 void RenderText::calcMinMaxWidth()
573 {
574     // Use 0 for the leadWidth.   If the text contains a variable width tab, the real width
575     // will get measured when trimmedMinMaxWidth calls again with the real leadWidth.
576     KHTMLAssert( !minMaxKnown() );
577     calcMinMaxWidth(0);
578 }
579
580 void RenderText::calcMinMaxWidth(int leadWidth)
581 {
582     // ### calc Min and Max width...
583     m_minWidth = m_beginMinWidth = m_endMinWidth = 0;
584     m_maxWidth = 0;
585
586     if (isBR())
587         return;
588         
589     int currMinWidth = 0;
590     int currMaxWidth = 0;
591     m_hasBreakableChar = m_hasBreak = m_hasTab = m_hasBeginWS = m_hasEndWS = false;
592     
593     // FIXME: not 100% correct for first-line
594     const Font* f = font(false);
595     int wordSpacing = style()->wordSpacing();
596     int len = str->length();
597     const UChar* txt = str->characters();
598     bool needsWordSpacing = false;
599     bool ignoringSpaces = false;
600     bool isSpace = false;
601     bool firstWord = true;
602     bool firstLine = true;
603     int nextBreakable = -1;
604     for (int i = 0; i < len; i++) {
605         UChar c = txt[i];
606         
607         bool previousCharacterIsSpace = isSpace;
608         
609         bool isNewline = false;
610         if (c == '\n') {
611             if (style()->preserveNewline()) {
612                 m_hasBreak = true;
613                 isNewline = true;
614                 isSpace = false;
615             } else
616                 isSpace = true;
617         } else if (c == '\t') {
618             if (!style()->collapseWhiteSpace()) {
619                 m_hasTab = true;
620                 isSpace = false;
621             } else
622                 isSpace = true;
623         } else {
624             isSpace = c == ' ';
625         }
626         
627         if ((isSpace || isNewline) && i == 0)
628             m_hasBeginWS = true;
629         if ((isSpace || isNewline) && i == len-1)
630             m_hasEndWS = true;
631             
632         if (!ignoringSpaces && style()->collapseWhiteSpace() && previousCharacterIsSpace && isSpace)
633             ignoringSpaces = true;
634         
635         if (ignoringSpaces && !isSpace)
636             ignoringSpaces = false;
637         
638         // Ignore spaces and soft hyphens
639         if (ignoringSpaces || c == SOFT_HYPHEN) {
640             continue;
641         }
642         
643         bool hasBreak = isBreakable(txt, i, len, nextBreakable);
644         int j = i;
645         while (c != '\n' && c != ' ' && c != '\t' && c != SOFT_HYPHEN) {
646             j++;
647             if (j == len)
648                 break;
649             c = txt[j];
650             if (isBreakable(txt, j, len, nextBreakable))
651                 break;
652         }
653             
654         int wordlen = j - i;
655         if (wordlen) {
656             int w = widthFromCache(f, i, wordlen, tabWidth(), leadWidth + currMaxWidth);
657             currMinWidth += w;
658             currMaxWidth += w;
659             
660             bool isSpace = (j < len) && c == ' ';
661             bool isCollapsibleWhiteSpace = (j < len) && style()->isCollapsibleWhiteSpace(c);
662             if (j < len && style()->autoWrap())
663                 m_hasBreakableChar = true;
664             
665             // Add in wordSpacing to our currMaxWidth, but not if this is the last word on a line or the
666             // last word in the run.
667             if (wordSpacing && (isSpace || isCollapsibleWhiteSpace) && !containsOnlyWhitespace(j, len-j))
668                 currMaxWidth += wordSpacing;
669
670             if (firstWord) {
671                 firstWord = false;
672                 // If the first character in the run is breakable, then we consider ourselves to have a beginning
673                 // minimum width of 0, since a break could occur right before our run starts, preventing us from ever
674                 // being appended to a previous text run when considering the total minimum width of the containing block.
675                 if (hasBreak)
676                     m_hasBreakableChar = true;
677                 m_beginMinWidth = hasBreak ? 0 : w;
678             }
679             m_endMinWidth = w;
680             
681             if (currMinWidth > m_minWidth) m_minWidth = currMinWidth;
682             currMinWidth = 0;
683             
684             i += wordlen-1;
685         }
686         else {
687             // Nowrap can never be broken, so don't bother setting the
688             // breakable character boolean. Pre can only be broken if we encounter a newline.     
689             if (style()->autoWrap() || isNewline)
690                 m_hasBreakableChar = true;
691
692             if (currMinWidth > m_minWidth) m_minWidth = currMinWidth;
693             currMinWidth = 0;
694             
695             if (isNewline) { // Only set if preserveNewline was true and we saw a newline.
696                 if (firstLine) {
697                     firstLine = false;
698                     leadWidth = 0;
699                     m_beginMinWidth = currMaxWidth;
700                 }
701                 
702                 if (currMaxWidth > m_maxWidth) m_maxWidth = currMaxWidth;
703                 currMaxWidth = 0;
704             }
705             else
706             {
707                 currMaxWidth += f->width(TextRun(txt + i, 1), tabWidth(), leadWidth + currMaxWidth);
708                 needsWordSpacing = isSpace && !previousCharacterIsSpace && i == len-1;
709             }
710         }
711     }
712
713     if (needsWordSpacing && len > 1) 
714         currMaxWidth += wordSpacing;
715     
716     m_minWidth = max(currMinWidth, m_minWidth);
717     m_maxWidth = max(currMaxWidth, m_maxWidth);
718         
719     if (!style()->autoWrap())
720         m_minWidth = m_maxWidth;
721
722     if (style()->whiteSpace() == PRE) {
723         // FIXME: pre-wrap and pre-line need to be dealt with possibly?  This code won't be right
724         // for them though.
725         if (firstLine)
726             m_beginMinWidth = m_maxWidth;
727         m_endMinWidth = currMaxWidth;
728     }
729     
730     setMinMaxKnown();
731     //kdDebug( 6040 ) << "Text::calcMinMaxWidth(): min = " << m_minWidth << " max = " << m_maxWidth << endl;
732 }
733
734 bool RenderText::containsOnlyWhitespace(unsigned from, unsigned len) const
735 {
736     unsigned currPos;
737     for (currPos = from; 
738          currPos < from+len && ((*str)[currPos] == '\n' || (*str)[currPos] == ' ' || (*str)[currPos] == '\t'); 
739          currPos++);
740     return currPos >= (from+len);
741 }
742
743 int RenderText::minXPos() const
744 {
745     if (!m_firstTextBox) return 0;
746     int retval=6666666;
747     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
748         retval = min(retval, (int)box->m_x);
749     return retval;
750 }
751
752 int RenderText::xPos() const
753 {
754     return m_firstTextBox ? m_firstTextBox->m_x : 0;
755 }
756
757 int RenderText::yPos() const
758 {
759     return m_firstTextBox ? m_firstTextBox->m_y : 0;
760 }
761
762 const Font& RenderText::font()
763 {
764     return style()->font();
765 }
766
767 void RenderText::setSelectionState(SelectionState s)
768 {
769     InlineTextBox* box;
770     
771     m_selectionState = s;
772     if (s == SelectionStart || s == SelectionEnd || s == SelectionBoth) {
773         int startPos, endPos;
774         selectionStartEnd(startPos, endPos);
775         if(selectionState() == SelectionStart) {
776             endPos = str->length();
777             
778             // to handle selection from end of text to end of line
779             if (startPos != 0 && startPos == endPos) {
780                 startPos = endPos - 1;
781             }
782         } else if(selectionState() == SelectionEnd)
783             startPos = 0;
784         
785         for (box = firstTextBox(); box; box = box->nextTextBox()) {
786             if (box->isSelected(startPos, endPos)) {
787                 RootInlineBox* line = box->root();
788                 if (line)
789                     line->setHasSelectedChildren(true);
790             }
791         }
792     }
793     else {
794         for (box = firstTextBox(); box; box = box->nextTextBox()) {
795             RootInlineBox* line = box->root();
796             if (line)
797                 line->setHasSelectedChildren(s == SelectionInside);
798         }
799     }
800     
801     containingBlock()->setSelectionState(s);
802 }
803
804 void RenderText::setTextWithOffset(StringImpl *text, unsigned offset, unsigned len, bool force)
805 {
806     unsigned oldLen = str ? str->length() : 0;
807     unsigned newLen = text ? text->length() : 0;
808     int delta = newLen - oldLen;
809     unsigned end = len ? offset+len-1 : offset;
810
811     RootInlineBox* firstRootBox = 0;
812     RootInlineBox* lastRootBox = 0;
813     
814     bool dirtiedLines = false;
815     
816     // Dirty all text boxes that include characters in between offset and offset+len.
817     for (InlineTextBox* curr = firstTextBox(); curr; curr = curr->nextTextBox()) {
818         // Text run is entirely before the affected range.
819         if (curr->end() < offset)
820             continue;
821         
822         // Text run is entirely after the affected range.
823         if (curr->start() > end) {
824             curr->offsetRun(delta);
825             RootInlineBox* root = curr->root();
826             if (!firstRootBox) {
827                 firstRootBox = root;
828                 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.
829                     firstRootBox->markDirty();
830                     dirtiedLines = true;
831                 }
832             }
833             lastRootBox = root;
834         }
835         else if (curr->end() >= offset && curr->end() <= end) {
836             curr->dirtyLineBoxes(); // Text run overlaps with the left end of the affected range.
837             dirtiedLines = true;
838         }
839         else if (curr->start() <= offset && curr->end() >= end) {
840             curr->dirtyLineBoxes(); // Text run subsumes the affected range.
841             dirtiedLines = true;
842         }
843         else if (curr->start() <= end && curr->end() >= end) {
844             curr->dirtyLineBoxes(); // Text run overlaps with right end of the affected range.
845             dirtiedLines = true;
846         }
847     }
848     
849     // Now we have to walk all of the clean lines and adjust their cached line break information
850     // to reflect our updated offsets.
851     if (lastRootBox)
852         lastRootBox = lastRootBox->nextRootBox();
853     if (firstRootBox) {
854         RootInlineBox* prev = firstRootBox->prevRootBox();
855         if (prev)
856             firstRootBox = prev;
857     }
858     for (RootInlineBox* curr = firstRootBox; curr && curr != lastRootBox; curr = curr->nextRootBox()) {
859         if (curr->lineBreakObj() == this && curr->lineBreakPos() > end)
860             curr->setLineBreakPos(curr->lineBreakPos()+delta);
861     }
862     
863     m_linesDirty = dirtiedLines;
864     setText(text, force);
865 }
866
867 void RenderText::setText(StringImpl *text, bool force)
868 {
869     if (!text)
870         return;
871     if (!force && str == text)
872         return;
873
874     m_allAsciiChecked = false;
875
876     str = text;
877     if (str) {
878         str = str->replace('\\', backslashAsCurrencySymbol());
879         if (style()) {
880             switch (style()->textTransform()) {
881                 case CAPITALIZE:
882                 {
883                     // find previous text renderer if one exists
884                     RenderObject* o;
885                     UChar previous = ' ';
886                     for (o = previousInPreOrder(); o && o->isInlineFlow(); o = o->previousInPreOrder())
887                         ;
888                     if (o && o->isText()) {
889                         StringImpl* prevStr = static_cast<RenderText*>(o)->string();
890                         previous = (*prevStr)[prevStr->length() - 1];
891                     }
892                     str = str->capitalize(previous);
893                 }
894                     break;
895                 case UPPERCASE:  str = str->upper();       break;
896                 case LOWERCASE:  str = str->lower();       break;
897                 case NONE:
898                 default:;
899             }
900         }
901     }
902
903     cacheWidths();
904
905     // ### what should happen if we change the text of a
906     // RenderBR object ?
907     KHTMLAssert(!isBR() || (str->length() == 1 && (*str)[0] == '\n'));
908     KHTMLAssert(!str->length() || str->characters());
909
910     setNeedsLayoutAndMinMaxRecalc();
911 }
912
913 int RenderText::height() const
914 {
915     int retval = 0;
916     if (firstTextBox())
917         retval = lastTextBox()->m_y + lastTextBox()->height() - firstTextBox()->m_y;
918     return retval;
919 }
920
921 short RenderText::lineHeight(bool firstLine, bool) const
922 {
923     // Always use the interior line height of the parent (e.g., if our parent is an inline block).
924     return parent()->lineHeight(firstLine, true);
925 }
926
927 void RenderText::dirtyLineBoxes(bool fullLayout, bool)
928 {
929     if (fullLayout)
930         deleteTextBoxes();
931     else if (!m_linesDirty) {
932         for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
933             box->dirtyLineBoxes();
934     }
935     m_linesDirty = false;
936 }
937
938 InlineBox* RenderText::createInlineBox(bool, bool isRootLineBox, bool)
939 {
940     KHTMLAssert(!isRootLineBox);
941     InlineTextBox* textBox = new (renderArena()) InlineTextBox(this);
942     if (!m_firstTextBox)
943         m_firstTextBox = m_lastTextBox = textBox;
944     else {
945         m_lastTextBox->setNextLineBox(textBox);
946         textBox->setPreviousLineBox(m_lastTextBox);
947         m_lastTextBox = textBox;
948     }
949     return textBox;
950 }
951
952 void RenderText::position(InlineBox* box, int from, int len, bool reverse, bool override)
953 {
954     InlineTextBox *s = static_cast<InlineTextBox*>(box);
955     
956     // ### should not be needed!!!
957     if (len == 0) {
958         // We want the box to be destroyed.
959         s->remove();
960         s->destroy(renderArena());
961         m_firstTextBox = m_lastTextBox = 0;
962         return;
963     }
964     
965     reverse = reverse && !style()->visuallyOrdered();
966     m_containsReversedText |= reverse;
967
968     s->m_reversed = reverse;
969     s->m_dirOverride = override || style()->visuallyOrdered();
970     s->m_start = from;
971     s->m_len = len;
972 }
973
974 unsigned int RenderText::width(unsigned int from, unsigned int len, int xpos, bool firstLine) const
975 {
976     if (from >= str->length())
977         return 0;
978     if (from + len > str->length())
979         len = str->length() - from;
980
981     const Font *f = font(firstLine);
982     return width(from, len, f, xpos);
983 }
984
985 unsigned int RenderText::width(unsigned int from, unsigned int len, const Font *f, int xpos) const
986 {
987     if (!str->characters() || from > str->length())
988         return 0;
989     if (from + len > str->length())
990         len = str->length() - from;
991
992     int w;
993     if (!style()->preserveNewline() && f == &style()->font() && from == 0 && len == str->length())
994         w = m_maxWidth;
995     else if (f == &style()->font())
996         w = widthFromCache(f, from, len, tabWidth(), xpos);
997     else
998         w = f->width(TextRun(string(), from, 0, len), tabWidth(), xpos );
999         
1000     return w;
1001 }
1002
1003 int RenderText::width() const
1004 {
1005     int minx = 100000000;
1006     int maxx = 0;
1007     // slooow
1008     for (InlineTextBox* s = firstTextBox(); s; s = s->nextTextBox()) {
1009         if(s->m_x < minx)
1010             minx = s->m_x;
1011         if(s->m_x + s->m_width > maxx)
1012             maxx = s->m_x + s->m_width;
1013     }
1014
1015     return max(0, maxx-minx);
1016 }
1017
1018 IntRect RenderText::getAbsoluteRepaintRect()
1019 {
1020     RenderObject *cb = containingBlock();
1021     return cb->getAbsoluteRepaintRect();
1022 }
1023
1024 IntRect RenderText::selectionRect()
1025 {
1026     IntRect rect;
1027     if (selectionState() == SelectionNone)
1028         return rect;
1029     RenderBlock* cb =  containingBlock();
1030     if (!cb)
1031         return rect;
1032     
1033     // Now calculate startPos and endPos for painting selection.
1034     // We include a selection while endPos > 0
1035     int startPos, endPos;
1036     if (selectionState() == SelectionInside) {
1037         // We are fully selected.
1038         startPos = 0;
1039         endPos = str->length();
1040     } else {
1041         selectionStartEnd(startPos, endPos);
1042         if (selectionState() == SelectionStart)
1043             endPos = str->length();
1044         else if (selectionState() == SelectionEnd)
1045             startPos = 0;
1046     }
1047     
1048     if (startPos == endPos)
1049         return rect;
1050
1051     int absx, absy;
1052     cb->absolutePositionForContent(absx, absy);
1053     RenderLayer* layer = cb->layer();
1054     if (layer)
1055        layer->subtractScrollOffset(absx, absy); 
1056     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1057         rect.unite(box->selectionRect(absx, absy, startPos, endPos));
1058
1059     return rect;
1060 }
1061
1062 short RenderText::verticalPositionHint( bool firstLine ) const
1063 {
1064     return parent()->verticalPositionHint( firstLine );
1065 }
1066
1067 const Font *RenderText::font(bool firstLine) const
1068 {
1069     return &style(firstLine)->font();
1070 }
1071
1072 int RenderText::caretMinOffset() const
1073 {
1074     InlineTextBox *box = firstTextBox();
1075     if (!box)
1076         return 0;
1077     int minOffset = box->m_start;
1078     for (box = box->nextTextBox(); box; box = box->nextTextBox())
1079         minOffset = min(minOffset, box->m_start);
1080     return minOffset;
1081 }
1082
1083 int RenderText::caretMaxOffset() const
1084 {
1085     InlineTextBox* box = lastTextBox();
1086     if (!box) 
1087         return str->length();
1088     int maxOffset = box->m_start + box->m_len;
1089     for (box = box->prevTextBox(); box; box = box->prevTextBox())
1090         maxOffset = max(maxOffset,box->m_start + box->m_len);
1091     return maxOffset;
1092 }
1093
1094 unsigned RenderText::caretMaxRenderedOffset() const
1095 {
1096     int l = 0;
1097     for (InlineTextBox* box = firstTextBox(); box; box = box->nextTextBox())
1098         l += box->m_len;
1099     return l;
1100 }
1101
1102 InlineBox *RenderText::inlineBox(int offset, EAffinity affinity)
1103 {
1104     for (InlineTextBox *box = firstTextBox(); box; box = box->nextTextBox()) {
1105         if (offset >= box->m_start && offset <= box->m_start + box->m_len) {
1106             if (atLineWrap(box, offset))
1107                 return box->nextTextBox();
1108             return box;
1109         }
1110         
1111         if (offset < box->m_start) {
1112             // The offset we're looking for is before this node
1113             // this means the offset must be in content that is
1114             // not rendered.
1115             return box->prevTextBox() ? box->prevTextBox() : firstTextBox();
1116         }
1117     }
1118     
1119     return 0;
1120 }
1121
1122 }