This patch makes drawHighlightForText a completely cross-platform method
[WebKit-https.git] / WebCore / rendering / InlineTextBox.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  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Library General Public
10  * License as published by the Free Software Foundation; either
11  * version 2 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Library General Public License for more details.
17  *
18  * You should have received a copy of the GNU Library General Public License
19  * along with this library; see the file COPYING.LIB.  If not, write to
20  * the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
21  * Boston, MA 02111-1307, USA.
22  *
23  */
24
25 #include "config.h"
26 #include "InlineTextBox.h"
27
28 #include "Document.h"
29 #include "Frame.h"
30 #include "GraphicsContext.h"
31 #include "RenderBlock.h"
32 #include "break_lines.h"
33 #include "Range.h"
34 #include "RenderArena.h"
35 #include <wtf/AlwaysInline.h>
36
37 using namespace std;
38
39 namespace WebCore {
40
41 #ifndef NDEBUG
42 static bool inInlineTextBoxDetach;
43 #endif
44
45 void InlineTextBox::destroy(RenderArena* renderArena)
46 {
47 #ifndef NDEBUG
48     inInlineTextBoxDetach = true;
49 #endif
50     delete this;
51 #ifndef NDEBUG
52     inInlineTextBoxDetach = false;
53 #endif
54     
55     // Recover the size left there for us by operator delete and free the memory.
56     renderArena->free(*(size_t *)this, this);
57 }
58
59 void* InlineTextBox::operator new(size_t sz, RenderArena* renderArena) throw()
60 {
61     return renderArena->allocate(sz);
62 }
63
64 void InlineTextBox::operator delete(void* ptr, size_t sz)
65 {
66     assert(inInlineTextBoxDetach);
67     
68     // Stash size where destroy can find it.
69     *static_cast<size_t*>(ptr) = sz;
70 }
71
72 bool InlineTextBox::isSelected(int startPos, int endPos) const
73 {
74     int sPos = max(startPos - m_start, 0);
75     int ePos = min(endPos - m_start, (int)m_len);
76     return (sPos < ePos);
77 }
78
79 RenderObject::SelectionState InlineTextBox::selectionState()
80 {
81     RenderObject::SelectionState state = object()->selectionState();
82     if (state == RenderObject::SelectionStart || state == RenderObject::SelectionEnd || state == RenderObject::SelectionBoth) {
83         int startPos, endPos;
84         object()->selectionStartEnd(startPos, endPos);
85         
86         // If we're at a line wrap, then the selection is going to extend onto the next line (and thus needs to be thought of as
87         // extending beyond our box.
88         if (textObject()->atLineWrap(this, endPos))
89             endPos++;
90
91         bool start = (state != RenderObject::SelectionEnd && startPos >= m_start && startPos < m_start + m_len);
92         bool end = (state != RenderObject::SelectionStart && endPos > m_start && endPos <= m_start + m_len);
93         if (start && end)
94             state = RenderObject::SelectionBoth;
95         else if (start)
96             state = RenderObject::SelectionStart;
97         else if (end)
98             state = RenderObject::SelectionEnd;
99         else if ((state == RenderObject::SelectionEnd || startPos < m_start) &&
100                  (state == RenderObject::SelectionStart || endPos > m_start + m_len))
101             state = RenderObject::SelectionInside;
102     }
103     return state;
104 }
105
106 IntRect InlineTextBox::selectionRect(int tx, int ty, int startPos, int endPos)
107 {
108     int sPos = max(startPos - m_start, 0);
109     int ePos = min(endPos - m_start, (int)m_len);
110     
111     if (sPos >= ePos)
112         return IntRect();
113
114     RootInlineBox* rootBox = root();
115     RenderText* textObj = textObject();
116     int selTop = rootBox->selectionTop();
117     int selHeight = rootBox->selectionHeight();
118     const Font *f = textObj->font(m_firstLine);
119
120     IntRect r = enclosingIntRect(f->selectionRectForText(TextRun(textObj->string(), m_start, sPos, ePos),
121                                         IntPoint(tx + m_x, ty + selTop), selHeight, textObj->tabWidth(), textPos(), 
122                                         m_toAdd, m_reversed, m_dirOverride));
123     if (r.x() > tx + m_x + m_width)
124         r.setWidth(0);
125     else if (r.right() - 1 > tx + m_x + m_width)
126         r.setWidth(tx + m_x + m_width - r.x());
127     return r;
128 }
129
130 void InlineTextBox::deleteLine(RenderArena* arena)
131 {
132     static_cast<RenderText*>(m_object)->removeTextBox(this);
133     destroy(arena);
134 }
135
136 void InlineTextBox::extractLine()
137 {
138     if (m_extracted)
139         return;
140
141     static_cast<RenderText*>(m_object)->extractTextBox(this);
142 }
143
144 void InlineTextBox::attachLine()
145 {
146     if (!m_extracted)
147         return;
148     
149     static_cast<RenderText*>(m_object)->attachTextBox(this);
150 }
151
152 int InlineTextBox::placeEllipsisBox(bool ltr, int blockEdge, int ellipsisWidth, bool& foundBox)
153 {
154     if (foundBox) {
155         m_truncation = cFullTruncation;
156         return -1;
157     }
158
159     int ellipsisX = ltr ? blockEdge - ellipsisWidth : blockEdge + ellipsisWidth;
160     
161     // For LTR, if the left edge of the ellipsis is to the left of our text run, then we are the run that will get truncated.
162     if (ltr) {
163         if (ellipsisX <= m_x) {
164             // Too far.  Just set full truncation, but return -1 and let the ellipsis just be placed at the edge of the box.
165             m_truncation = cFullTruncation;
166             foundBox = true;
167             return -1;
168         }
169
170         if (ellipsisX < m_x + m_width) {
171             if (m_reversed)
172                 return -1; // FIXME: Support LTR truncation when the last run is RTL someday.
173
174             foundBox = true;
175
176             int offset = offsetForPosition(ellipsisX, false);
177             if (offset == 0) {
178                 // No characters should be rendered.  Set ourselves to full truncation and place the ellipsis at the min of our start
179                 // and the ellipsis edge.
180                 m_truncation = cFullTruncation;
181                 return min(ellipsisX, m_x);
182             }
183             
184             // Set the truncation index on the text run.  The ellipsis needs to be placed just after the last visible character.
185             m_truncation = offset + m_start;
186             return m_x + static_cast<RenderText*>(m_object)->width(m_start, offset, textPos(), m_firstLine);
187         }
188     }
189     else {
190         // FIXME: Support RTL truncation someday, including both modes (when the leftmost run on the line is either RTL or LTR)
191     }
192     return -1;
193 }
194
195 static Color 
196 correctedTextColor(Color textColor, Color backgroundColor) 
197 {
198     // Adjust the text color if it is too close to the background color,
199     // by darkening or lightening it to move it further away.
200     
201     int d = differenceSquared(textColor, backgroundColor);
202     // semi-arbitrarily chose 65025 (255^2) value here after a few tests; 
203     if (d > 65025) {
204         return textColor;
205     }
206     
207     int distanceFromWhite = differenceSquared(textColor, Color::white);
208     int distanceFromBlack = differenceSquared(textColor, Color::black);
209
210     if (distanceFromWhite < distanceFromBlack) {
211         return textColor.dark();
212     }
213     
214     return textColor.light();
215 }
216
217 bool InlineTextBox::isLineBreak() const
218 {
219     return object()->isBR() || (object()->style()->preserveNewline() && len() == 1 && (*textObject()->string())[start()] == '\n');
220 }
221
222 bool InlineTextBox::nodeAtPoint(RenderObject::NodeInfo& i, int x, int y, int tx, int ty)
223 {
224     if (isLineBreak())
225         return false;
226
227     IntRect rect(tx + m_x, ty + m_y, m_width, m_height);
228     if (m_truncation != cFullTruncation && object()->style()->visibility() == VISIBLE && rect.contains(x, y)) {
229         object()->setInnerNode(i);
230         return true;
231     }
232     return false;
233 }
234
235 void InlineTextBox::paint(RenderObject::PaintInfo& i, int tx, int ty)
236 {
237     if (isLineBreak() || !object()->shouldPaintWithinRoot(i) || object()->style()->visibility() != VISIBLE ||
238         m_truncation == cFullTruncation || i.phase == PaintPhaseOutline)
239         return;
240     
241     assert(i.phase != PaintPhaseSelfOutline && i.phase != PaintPhaseChildOutlines);
242
243     int xPos = tx + m_x - parent()->maxHorizontalShadow();
244     int w = width() + 2 * parent()->maxHorizontalShadow();
245     if (xPos >= i.r.right() || xPos + w <= i.r.x())
246         return;
247
248     bool isPrinting = textObject()->document()->printing();
249     
250     // Determine whether or not we're selected.
251     bool haveSelection = !isPrinting && selectionState() != RenderObject::SelectionNone;
252     if (!haveSelection && i.phase == PaintPhaseSelection)
253         // When only painting the selection, don't bother to paint if there is none.
254         return;
255
256     // Determine whether or not we have marked text.
257     Range *markedTextRange = object()->document()->frame()->markedTextRange();
258     int exception = 0;
259     bool haveMarkedText = markedTextRange && markedTextRange->startContainer(exception) == object()->node();
260     bool markedTextUsesUnderlines = object()->document()->frame()->markedTextUsesUnderlines();
261
262     // Set our font.
263     RenderStyle* styleToUse = object()->style(m_firstLine);
264     int d = styleToUse->textDecorationsInEffect();
265     const Font* font = &styleToUse->font();
266     if (*font != i.p->font())
267         i.p->setFont(*font);
268
269     // 1. Paint backgrounds behind text if needed.  Examples of such backgrounds include selection
270     // and marked text.
271     if (i.phase != PaintPhaseSelection && !isPrinting) {
272         if (haveMarkedText  && !markedTextUsesUnderlines)
273             paintMarkedTextBackground(i.p, tx, ty, styleToUse, font, markedTextRange->startOffset(exception), markedTextRange->endOffset(exception));
274
275         paintAllMarkersOfType(i.p, tx, ty, DocumentMarker::TextMatch, styleToUse, font);
276
277         if (haveSelection)
278             paintSelection(i.p, tx, ty, styleToUse, font);
279     }
280     
281     // 2. Now paint the foreground, including text and decorations like underline/overline (in quirks mode only).
282     if (m_len <= 0) return;
283     
284     DeprecatedValueList<MarkedTextUnderline> underlines;
285     if (haveMarkedText && markedTextUsesUnderlines) {
286         underlines = object()->document()->frame()->markedTextUnderlines();
287     }
288     DeprecatedValueListIterator<MarkedTextUnderline> underlineIt = underlines.begin();
289
290     Color textColor = styleToUse->color();
291     
292     // Make the text color legible against a white background
293     if (styleToUse->forceBackgroundsToWhite())
294         textColor = correctedTextColor(textColor, Color::white);
295
296     if (textColor != i.p->pen().color())
297         i.p->setPen(textColor);
298
299     // Set a text shadow if we have one.
300     // FIXME: Support multiple shadow effects.  Need more from the CG API before
301     // we can do this.
302     bool setShadow = false;
303     if (styleToUse->textShadow()) {
304         i.p->setShadow(IntSize(styleToUse->textShadow()->x, styleToUse->textShadow()->y),
305             styleToUse->textShadow()->blur, styleToUse->textShadow()->color);
306         setShadow = true;
307     }
308
309     bool paintSelectedTextOnly = (i.phase == PaintPhaseSelection);
310     bool paintSelectedTextSeparately = false; // Whether or not we have to do multiple paints.  Only
311                                               // necessary when a custom ::selection foreground color is applied.
312     Color selectionColor = i.p->pen().color();
313     ShadowData* selectionTextShadow = 0;
314     if (haveSelection) {
315         RenderStyle* pseudoStyle = object()->getPseudoStyle(RenderStyle::SELECTION);
316         if (pseudoStyle) {
317             if (pseudoStyle->color() != selectionColor || pseudoStyle->textShadow()) {
318                 if (!paintSelectedTextOnly)
319                     paintSelectedTextSeparately = true;
320                 if (pseudoStyle->color() != selectionColor)
321                     selectionColor = pseudoStyle->color();
322                 if (pseudoStyle->textShadow())
323                     selectionTextShadow = pseudoStyle->textShadow();
324             }
325         }
326     }
327
328     StringImpl* textStr = textObject()->string();
329
330     if (!paintSelectedTextOnly && !paintSelectedTextSeparately) {
331         // paint all the text
332         // FIXME: Handle RTL direction, handle reversed strings.  For now truncation can only be turned on
333         // for non-reversed LTR strings.
334         int endPoint = m_len;
335         if (m_truncation != cNoTruncation)
336             endPoint = m_truncation - m_start;
337         i.p->drawText(TextRun(textStr, m_start, 0, endPoint),
338                       IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
339                       m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered());
340     } else {
341         int sPos, ePos;
342         selectionStartEnd(sPos, ePos);
343         if (paintSelectedTextSeparately) {
344             // paint only the text that is not selected
345             if (sPos >= ePos)
346                 i.p->drawText(TextRun(textStr, m_start), IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
347                               m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered());
348             else {
349                 if (sPos - 1 >= 0)
350                     i.p->drawText(TextRun(textStr, m_start, 0, sPos), IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
351                                   m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered());
352                 if (ePos < m_start + m_len)
353                     i.p->drawText(TextRun(textStr, m_start, ePos), IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
354                                   m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered());
355             }
356         }
357             
358         if (sPos < ePos) {
359             // paint only the text that is selected
360             if (selectionColor != i.p->pen().color())
361                 i.p->setPen(selectionColor);
362             
363             if (selectionTextShadow)
364                 i.p->setShadow(IntSize(selectionTextShadow->x, selectionTextShadow->y),
365                                selectionTextShadow->blur,
366                                selectionTextShadow->color);
367             i.p->drawText(TextRun(textStr, m_start, sPos, ePos), IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
368                           m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered());
369             if (selectionTextShadow)
370                 i.p->clearShadow();
371         }
372     }
373
374     // Paint decorations
375     if (d != TDNONE && i.phase != PaintPhaseSelection && styleToUse->htmlHacks()) {
376         i.p->setPen(styleToUse->color());
377         paintDecoration(i.p, tx, ty, d);
378     }
379
380     if (i.phase != PaintPhaseSelection) {
381         paintAllMarkersOfType(i.p, tx, ty, DocumentMarker::Spelling, styleToUse, font);
382
383         for ( ; underlineIt != underlines.end(); underlineIt++) {
384             MarkedTextUnderline underline = *underlineIt;
385
386             if (underline.endOffset <= start())
387                 // underline is completely before this run.  This might be an underline that sits
388                 // before the first run we draw, or underlines that were within runs we skipped 
389                 // due to truncation.
390                 continue;
391             
392             if (underline.startOffset <= end()) {
393                 // underline intersects this run.  Paint it.
394                 paintMarkedTextUnderline(i.p, tx, ty, underline);
395                 if (underline.endOffset > end() + 1)
396                     // underline also runs into the next run. Bail now, no more marker advancement.
397                     break;
398             } else
399                 // underline is completely after this run, bail.  A later run will paint it.
400                 break;
401         }
402     }
403
404     if (setShadow)
405         i.p->clearShadow();
406 }
407
408 void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
409 {
410     int startPos, endPos;
411     if (object()->selectionState() == RenderObject::SelectionInside) {
412         startPos = 0;
413         endPos = textObject()->string()->length();
414     } else {
415         textObject()->selectionStartEnd(startPos, endPos);
416         if (object()->selectionState() == RenderObject::SelectionStart)
417             endPos = textObject()->string()->length();
418         else if (object()->selectionState() == RenderObject::SelectionEnd)
419             startPos = 0;
420     }
421
422     sPos = max(startPos - m_start, 0);
423     ePos = min(endPos - m_start, (int)m_len);
424 }
425
426 void InlineTextBox::paintSelection(GraphicsContext* p, int tx, int ty, RenderStyle* style, const Font* f)
427 {
428     // See if we have a selection to paint at all.
429     int sPos, ePos;
430     selectionStartEnd(sPos, ePos);
431     if (sPos >= ePos)
432         return;
433
434     // Macintosh-style text highlighting is to draw with a particular background color, not invert.
435     Color textColor = style->color();
436     Color c = object()->selectionColor(p);
437     if (!c.isValid())
438         return;
439
440     // If the text color ends up being the same as the selection background, invert the selection
441     // background.  This should basically never happen, since the selection has transparency.
442     if (textColor == c)
443         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
444
445     p->save();
446     p->setPen(c); // Don't draw text at all!
447     RootInlineBox* r = root();
448     int y = r->selectionTop();
449     int h = r->selectionHeight();
450     p->addClip(IntRect(m_x + tx, y + ty, m_width, h));
451     p->drawHighlightForText(TextRun(textObject()->string(), m_start, sPos, ePos), IntPoint(m_x + tx, y + ty), h, textObject()->tabWidth(), textPos(), 
452                             m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || style->visuallyOrdered(), c);
453     p->restore();
454 }
455
456 void InlineTextBox::paintMarkedTextBackground(GraphicsContext* p, int tx, int ty, RenderStyle* style, const Font* f, int startPos, int endPos)
457 {
458     int offset = m_start;
459     int sPos = max(startPos - offset, 0);
460     int ePos = min(endPos - offset, (int)m_len);
461
462     if (sPos >= ePos)
463         return;
464
465     p->save();
466
467     Color c = Color(225, 221, 85);
468     
469     p->setPen(c); // Don't draw text at all!
470
471     RootInlineBox* r = root();
472     int y = r->selectionTop();
473     int h = r->selectionHeight();
474     p->drawHighlightForText(TextRun(textObject()->string(), m_start, sPos, ePos),
475                             IntPoint(m_x + tx, y + ty), h, textObject()->tabWidth(), textPos(),
476                             m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || style->visuallyOrdered(), c);
477     p->restore();
478 }
479
480 void InlineTextBox::paintDecoration(GraphicsContext *pt, int _tx, int _ty, int deco)
481 {
482     _tx += m_x;
483     _ty += m_y;
484
485     if (m_truncation == cFullTruncation)
486         return;
487     
488     int width = (m_truncation == cNoTruncation) ? 
489                 m_width : static_cast<RenderText*>(m_object)->width(m_start, m_truncation - m_start, textPos(), m_firstLine);
490     
491     // Get the text decoration colors.
492     Color underline, overline, linethrough;
493     object()->getTextDecorationColors(deco, underline, overline, linethrough, true);
494     
495     // Use a special function for underlines to get the positioning exactly right.
496     if (deco & UNDERLINE) {
497         pt->setPen(underline);
498         pt->drawLineForText(IntPoint(_tx, _ty), m_baseline, width);
499     }
500     if (deco & OVERLINE) {
501         pt->setPen(overline);
502         pt->drawLineForText(IntPoint(_tx, _ty), 0, width);
503     }
504     if (deco & LINE_THROUGH) {
505         pt->setPen(linethrough);
506         pt->drawLineForText(IntPoint(_tx, _ty), 2*m_baseline/3, width);
507     }
508 }
509
510 void InlineTextBox::paintSpellingMarker(GraphicsContext* pt, int _tx, int _ty, DocumentMarker marker)
511 {
512     _tx += m_x;
513     _ty += m_y;
514     
515     if (m_truncation == cFullTruncation)
516         return;
517     
518     int start = 0;                  // start of line to draw, relative to _tx
519     int width = m_width;            // how much line to draw
520     bool useWholeWidth = true;
521     unsigned paintStart = m_start;
522     unsigned paintEnd = end()+1;      // end points at the last char, not past it
523     if (paintStart <= marker.startOffset) {
524         paintStart = marker.startOffset;
525         useWholeWidth = false;
526         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine);
527     }
528     if (paintEnd != marker.endOffset) {      // end points at the last char, not past it
529         paintEnd = min(paintEnd, marker.endOffset);
530         useWholeWidth = false;
531     }
532     if (m_truncation != cNoTruncation) {
533         paintEnd = min(paintEnd, (unsigned)m_truncation);
534         useWholeWidth = false;
535     }
536     if (!useWholeWidth) {
537         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
538     }
539     
540     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
541     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
542     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
543     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
544     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
545     // we pin to two pixels under the baseline.
546     int lineThickness = pt->misspellingLineThickness();
547     int descent = m_height - m_baseline;
548     int underlineOffset;
549     if (descent <= (2 + lineThickness)) {
550         // place the underline at the very bottom of the text in small/medium fonts
551         underlineOffset = m_height - lineThickness;
552     } else {
553         // in larger fonts, tho, place the underline up near the baseline to prevent big gap
554         underlineOffset = m_baseline + 2;
555     }
556     pt->drawLineForMisspelling(IntPoint(_tx + start, _ty + underlineOffset), width);
557 }
558
559 void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int _tx, int _ty, DocumentMarker marker, RenderStyle* style, const Font* f)
560 {
561     Color yellow = Color(255, 255, 0);
562     pt->save();
563     pt->setPen(yellow); // Don't draw text at all!
564    // Use same y positioning and height as for selection, so that when the selection and this highlight are on
565    // the same word there are no pieces sticking out.
566     RootInlineBox* r = root();
567     int y = r->selectionTop();
568     int h = r->selectionHeight();
569     pt->addClip(IntRect(_tx + m_x, _ty + y, m_width, h));
570     int sPos = max(marker.startOffset - m_start, (unsigned)0);
571     int ePos = min(marker.endOffset - m_start, (unsigned)m_len);
572     
573     pt->drawHighlightForText(TextRun(textObject()->string(), m_start, sPos, ePos), IntPoint(m_x + _tx, y + _ty), h, textObject()->tabWidth(), textPos(), 
574                              m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || style->visuallyOrdered(), yellow);
575     pt->restore();
576 }
577
578 void InlineTextBox::paintAllMarkersOfType(GraphicsContext* pt, int _tx, int _ty, DocumentMarker::MarkerType markerType, RenderStyle* style, const Font* f)
579 {
580     DeprecatedValueList<DocumentMarker> markers = object()->document()->markersForNode(object()->node());
581     DeprecatedValueListIterator <DocumentMarker> markerIt = markers.begin();
582
583     // Give any document markers that touch this run a chance to draw before the text has been drawn.
584     // Note end() points at the last char, not one past it like endOffset and ranges do.
585     for ( ; markerIt != markers.end(); markerIt++) {
586         DocumentMarker marker = *markerIt;
587         
588         if (marker.type != markerType)
589             continue;
590         
591         if (marker.endOffset <= start())
592             // marker is completely before this run.  This might be a marker that sits before the
593             // first run we draw, or markers that were within runs we skipped due to truncation.
594             continue;
595         
596         if (marker.startOffset > end())
597             // marker is completely after this run, bail.  A later run will paint it.
598             break;
599         
600         // marker intersects this run.  Paint it.
601         switch (markerType) {
602             case DocumentMarker::Spelling:
603                 paintSpellingMarker(pt, _tx, _ty, marker);
604                 break;
605             case DocumentMarker::TextMatch:
606                 paintTextMatchMarker(pt, _tx, _ty, marker, style, f);
607                 break;
608             default:
609                 assert(false);
610         }
611
612         if (marker.endOffset > end() + 1)
613             // marker also runs into the next run. Bail now, no more marker advancement.
614             break;
615     }
616 }
617
618
619 void InlineTextBox::paintMarkedTextUnderline(GraphicsContext* pt, int _tx, int _ty, MarkedTextUnderline& underline)
620 {
621     _tx += m_x;
622     _ty += m_y;
623
624     if (m_truncation == cFullTruncation)
625         return;
626     
627     int start = 0;                  // start of line to draw, relative to _tx
628     int width = m_width;            // how much line to draw
629     bool useWholeWidth = true;
630     unsigned paintStart = m_start;
631     unsigned paintEnd = end()+1;      // end points at the last char, not past it
632     if (paintStart <= underline.startOffset) {
633         paintStart = underline.startOffset;
634         useWholeWidth = false;
635         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine);
636     }
637     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
638         paintEnd = min(paintEnd, (unsigned)underline.endOffset);
639         useWholeWidth = false;
640     }
641     if (m_truncation != cNoTruncation) {
642         paintEnd = min(paintEnd, (unsigned)m_truncation);
643         useWholeWidth = false;
644     }
645     if (!useWholeWidth) {
646         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
647     }
648
649     int underlineOffset = m_height - 3;
650     pt->setPen(Pen(underline.color, underline.thick ? 2 : 0));
651     pt->drawLineForText(IntPoint(_tx + start, _ty), underlineOffset, width);
652 }
653
654 int InlineTextBox::caretMinOffset() const
655 {
656     return m_start;
657 }
658
659 int InlineTextBox::caretMaxOffset() const
660 {
661     return m_start + m_len;
662 }
663
664 unsigned InlineTextBox::caretMaxRenderedOffset() const
665 {
666     return m_start + m_len;
667 }
668
669 int InlineTextBox::textPos() const
670 {
671     if (xPos() == 0)
672         return 0;
673         
674     RenderBlock *blockElement = object()->containingBlock();
675     return m_reversed ? xPos() - blockElement->borderRight() - blockElement->paddingRight()
676                       : xPos() - blockElement->borderLeft() - blockElement->paddingLeft();
677 }
678
679 int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const
680 {
681     if (isLineBreak())
682         return 0;
683
684     RenderText* text = static_cast<RenderText*>(m_object);
685     RenderStyle *style = text->style(m_firstLine);
686     const Font* f = &style->font();
687     return f->checkSelectionPoint(TextRun(textObject()->string(), m_start),
688                                   m_toAdd, text->tabWidth(), textPos(), _x - m_x,
689                                   m_reversed ? RTL : LTR, m_dirOverride || style->visuallyOrdered(), includePartialGlyphs);
690 }
691
692 int InlineTextBox::positionForOffset(int offset) const
693 {
694     if (isLineBreak())
695         return m_x;
696
697     RenderText *text = static_cast<RenderText *>(m_object);
698     const Font *f = text->font(m_firstLine);
699     int from = m_reversed ? offset - m_start : 0;
700     int to = m_reversed ? m_len : offset - m_start;
701     // FIXME: Do we need to add rightBearing here?
702     return enclosingIntRect(f->selectionRectForText(TextRun(text->string(), m_start, from, to), IntPoint(m_x, 0), 0, text->tabWidth(), textPos(), m_toAdd, m_reversed, m_dirOverride)).right();
703 }
704
705 }