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