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