Reviewed by Hyatt.
[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 <kxmlcore/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 = f->selectionRectForText(IntPoint(tx + m_x, ty + selTop), selHeight, textObj->tabWidth(), textPos(), 
121                                         textObj->str->unicode(), textObj->str->length(), m_start, m_len,
122                                         m_toAdd, m_reversed, m_dirOverride, sPos, ePos);
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 = i.p->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     if (!paintSelectedTextOnly && !paintSelectedTextSeparately) {
329         // paint all the text
330         // FIXME: Handle RTL direction, handle reversed strings.  For now truncation can only be turned on
331         // for non-reversed LTR strings.
332         int endPoint = m_len;
333         if (m_truncation != cNoTruncation)
334             endPoint = m_truncation - m_start;
335         i.p->drawText(IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
336                       textObject()->string()->unicode(), textObject()->string()->length(), m_start, endPoint,
337                       m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered());
338     } else {
339         int sPos, ePos;
340         selectionStartEnd(sPos, ePos);
341         if (paintSelectedTextSeparately) {
342             // paint only the text that is not selected
343             if (sPos >= ePos)
344                 i.p->drawText(IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
345                               textObject()->string()->unicode(), textObject()->string()->length(), m_start, m_len,
346                               m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered());
347             else {
348                 if (sPos - 1 >= 0)
349                     i.p->drawText(IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
350                                   textObject()->string()->unicode(), textObject()->string()->length(), m_start, m_len,
351                                   m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered(), 0, sPos);
352                 if (ePos < m_start + m_len)
353                     i.p->drawText(IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
354                                   textObject()->string()->unicode(), textObject()->string()->length(), m_start, m_len,
355                                   m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered(), ePos, -1);
356             }
357         }
358             
359         if (sPos < ePos) {
360             // paint only the text that is selected
361             if (selectionColor != i.p->pen().color())
362                 i.p->setPen(selectionColor);
363             
364             if (selectionTextShadow)
365                 i.p->setShadow(IntSize(selectionTextShadow->x, selectionTextShadow->y),
366                                selectionTextShadow->blur,
367                                selectionTextShadow->color);
368             i.p->drawText(IntPoint(m_x + tx, m_y + ty + m_baseline), textObject()->tabWidth(), textPos(),
369                           textObject()->string()->unicode(), textObject()->string()->length(), m_start, m_len,
370                           m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || styleToUse->visuallyOrdered(), sPos, ePos);
371             if (selectionTextShadow)
372                 i.p->clearShadow();
373         }
374     }
375
376     // Paint decorations
377     if (d != TDNONE && i.phase != PaintPhaseSelection && styleToUse->htmlHacks()) {
378         i.p->setPen(styleToUse->color());
379         paintDecoration(i.p, tx, ty, d);
380     }
381
382     if (i.phase != PaintPhaseSelection) {
383         paintAllMarkersOfType(i.p, tx, ty, DocumentMarker::Spelling, styleToUse, font);
384
385         for ( ; underlineIt != underlines.end(); underlineIt++) {
386             MarkedTextUnderline underline = *underlineIt;
387
388             if (underline.endOffset <= start())
389                 // underline is completely before this run.  This might be an underline that sits
390                 // before the first run we draw, or underlines that were within runs we skipped 
391                 // due to truncation.
392                 continue;
393             
394             if (underline.startOffset <= end()) {
395                 // underline intersects this run.  Paint it.
396                 paintMarkedTextUnderline(i.p, tx, ty, underline);
397                 if (underline.endOffset > end() + 1)
398                     // underline also runs into the next run. Bail now, no more marker advancement.
399                     break;
400             } else
401                 // underline is completely after this run, bail.  A later run will paint it.
402                 break;
403         }
404     }
405
406     if (setShadow)
407         i.p->clearShadow();
408 }
409
410 void InlineTextBox::selectionStartEnd(int& sPos, int& ePos)
411 {
412     int startPos, endPos;
413     if (object()->selectionState() == RenderObject::SelectionInside) {
414         startPos = 0;
415         endPos = textObject()->string()->length();
416     } else {
417         textObject()->selectionStartEnd(startPos, endPos);
418         if (object()->selectionState() == RenderObject::SelectionStart)
419             endPos = textObject()->string()->length();
420         else if (object()->selectionState() == RenderObject::SelectionEnd)
421             startPos = 0;
422     }
423
424     sPos = max(startPos - m_start, 0);
425     ePos = min(endPos - m_start, (int)m_len);
426 }
427
428 void InlineTextBox::paintSelection(GraphicsContext* p, int tx, int ty, RenderStyle* style, const Font* f)
429 {
430     // See if we have a selection to paint at all.
431     int sPos, ePos;
432     selectionStartEnd(sPos, ePos);
433     if (sPos >= ePos)
434         return;
435
436     // Macintosh-style text highlighting is to draw with a particular background color, not invert.
437     Color textColor = style->color();
438     Color c = object()->selectionColor(p);
439     if (!c.isValid())
440         return;
441
442     // If the text color ends up being the same as the selection background, invert the selection
443     // background.  This should basically never happen, since the selection has transparency.
444     if (textColor == c)
445         c = Color(0xff - c.red(), 0xff - c.green(), 0xff - c.blue());
446
447     p->save();
448     p->setPen(c); // Don't draw text at all!
449     RootInlineBox* r = root();
450     int y = r->selectionTop();
451     int h = r->selectionHeight();
452     p->addClip(IntRect(m_x + tx, y + ty, m_width, h));
453     p->drawHighlightForText(IntPoint(m_x + tx, y + ty), h, textObject()->tabWidth(), textPos(), 
454                             textObject()->str->unicode(), textObject()->str->length(), m_start, m_len,
455                             m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || style->visuallyOrdered(), sPos, ePos, c);
456     p->restore();
457 }
458
459 void InlineTextBox::paintMarkedTextBackground(GraphicsContext* p, int tx, int ty, RenderStyle* style, const Font* f, int startPos, int endPos)
460 {
461     int offset = m_start;
462     int sPos = max(startPos - offset, 0);
463     int ePos = min(endPos - offset, (int)m_len);
464
465     if (sPos >= ePos)
466         return;
467
468     p->save();
469
470     Color c = Color(225, 221, 85);
471     
472     p->setPen(c); // Don't draw text at all!
473
474     RootInlineBox* r = root();
475     int y = r->selectionTop();
476     int h = r->selectionHeight();
477     p->drawHighlightForText(IntPoint(m_x + tx, y + ty), h, textObject()->tabWidth(), textPos(),
478                             textObject()->str->unicode(), textObject()->str->length(), m_start, m_len,
479                             m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || style->visuallyOrdered(), sPos, ePos, c);
480     p->restore();
481 }
482
483 void InlineTextBox::paintDecoration(GraphicsContext *pt, int _tx, int _ty, int deco)
484 {
485     _tx += m_x;
486     _ty += m_y;
487
488     if (m_truncation == cFullTruncation)
489         return;
490     
491     int width = (m_truncation == cNoTruncation) ? 
492                 m_width : static_cast<RenderText*>(m_object)->width(m_start, m_truncation - m_start, textPos(), m_firstLine);
493     
494     // Get the text decoration colors.
495     Color underline, overline, linethrough;
496     object()->getTextDecorationColors(deco, underline, overline, linethrough, true);
497     
498     // Use a special function for underlines to get the positioning exactly right.
499     if (deco & UNDERLINE) {
500         pt->setPen(underline);
501         pt->drawLineForText(IntPoint(_tx, _ty), m_baseline, width);
502     }
503     if (deco & OVERLINE) {
504         pt->setPen(overline);
505         pt->drawLineForText(IntPoint(_tx, _ty), 0, width);
506     }
507     if (deco & LINE_THROUGH) {
508         pt->setPen(linethrough);
509         pt->drawLineForText(IntPoint(_tx, _ty), 2*m_baseline/3, width);
510     }
511 }
512
513 void InlineTextBox::paintSpellingMarker(GraphicsContext* pt, int _tx, int _ty, DocumentMarker marker)
514 {
515     _tx += m_x;
516     _ty += m_y;
517     
518     if (m_truncation == cFullTruncation)
519         return;
520     
521     int start = 0;                  // start of line to draw, relative to _tx
522     int width = m_width;            // how much line to draw
523     bool useWholeWidth = true;
524     unsigned paintStart = m_start;
525     unsigned paintEnd = end()+1;      // end points at the last char, not past it
526     if (paintStart <= marker.startOffset) {
527         paintStart = marker.startOffset;
528         useWholeWidth = false;
529         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine);
530     }
531     if (paintEnd != marker.endOffset) {      // end points at the last char, not past it
532         paintEnd = min(paintEnd, marker.endOffset);
533         useWholeWidth = false;
534     }
535     if (m_truncation != cNoTruncation) {
536         paintEnd = min(paintEnd, (unsigned)m_truncation);
537         useWholeWidth = false;
538     }
539     if (!useWholeWidth) {
540         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
541     }
542     
543     // IMPORTANT: The misspelling underline is not considered when calculating the text bounds, so we have to
544     // make sure to fit within those bounds.  This means the top pixel(s) of the underline will overlap the
545     // bottom pixel(s) of the glyphs in smaller font sizes.  The alternatives are to increase the line spacing (bad!!)
546     // or decrease the underline thickness.  The overlap is actually the most useful, and matches what AppKit does.
547     // So, we generally place the underline at the bottom of the text, but in larger fonts that's not so good so
548     // we pin to two pixels under the baseline.
549     int lineThickness = pt->misspellingLineThickness();
550     int descent = m_height - m_baseline;
551     int underlineOffset;
552     if (descent <= (2 + lineThickness)) {
553         // place the underline at the very bottom of the text in small/medium fonts
554         underlineOffset = m_height - lineThickness;
555     } else {
556         // in larger fonts, tho, place the underline up near the baseline to prevent big gap
557         underlineOffset = m_baseline + 2;
558     }
559     pt->drawLineForMisspelling(IntPoint(_tx + start, _ty + underlineOffset), width);
560 }
561
562 void InlineTextBox::paintTextMatchMarker(GraphicsContext* pt, int _tx, int _ty, DocumentMarker marker, RenderStyle* style, const Font* f)
563 {
564     Color yellow = Color(255, 255, 0);
565     pt->save();
566     pt->setPen(yellow); // Don't draw text at all!
567    // Use same y positioning and height as for selection, so that when the selection and this highlight are on
568    // the same word there are no pieces sticking out.
569     RootInlineBox* r = root();
570     int y = r->selectionTop();
571     int h = r->selectionHeight();
572     pt->addClip(IntRect(_tx + m_x, _ty + y, m_width, h));
573     int sPos = max(marker.startOffset - m_start, (unsigned)0);
574     int ePos = min(marker.endOffset - m_start, (unsigned)m_len);
575     
576     pt->drawHighlightForText(IntPoint(m_x + _tx, y + _ty), h, textObject()->tabWidth(), textPos(), 
577                              textObject()->str->unicode(), textObject()->str->length(), m_start, m_len,
578                              m_toAdd, m_reversed ? RTL : LTR, m_dirOverride || style->visuallyOrdered(), sPos, ePos, yellow);
579     pt->restore();
580 }
581
582 void InlineTextBox::paintAllMarkersOfType(GraphicsContext* pt, int _tx, int _ty, DocumentMarker::MarkerType markerType, RenderStyle* style, const Font* f)
583 {
584     DeprecatedValueList<DocumentMarker> markers = object()->document()->markersForNode(object()->node());
585     DeprecatedValueListIterator <DocumentMarker> markerIt = markers.begin();
586
587     // Give any document markers that touch this run a chance to draw before the text has been drawn.
588     // Note end() points at the last char, not one past it like endOffset and ranges do.
589     for ( ; markerIt != markers.end(); markerIt++) {
590         DocumentMarker marker = *markerIt;
591         
592         if (marker.type != markerType)
593             continue;
594         
595         if (marker.endOffset <= start())
596             // marker is completely before this run.  This might be a marker that sits before the
597             // first run we draw, or markers that were within runs we skipped due to truncation.
598             continue;
599         
600         if (marker.startOffset > end())
601             // marker is completely after this run, bail.  A later run will paint it.
602             break;
603         
604         // marker intersects this run.  Paint it.
605         switch (markerType) {
606             case DocumentMarker::Spelling:
607                 paintSpellingMarker(pt, _tx, _ty, marker);
608                 break;
609             case DocumentMarker::TextMatch:
610                 paintTextMatchMarker(pt, _tx, _ty, marker, style, f);
611                 break;
612             default:
613                 assert(false);
614         }
615
616         if (marker.endOffset > end() + 1)
617             // marker also runs into the next run. Bail now, no more marker advancement.
618             break;
619     }
620 }
621
622
623 void InlineTextBox::paintMarkedTextUnderline(GraphicsContext* pt, int _tx, int _ty, MarkedTextUnderline& underline)
624 {
625     _tx += m_x;
626     _ty += m_y;
627
628     if (m_truncation == cFullTruncation)
629         return;
630     
631     int start = 0;                  // start of line to draw, relative to _tx
632     int width = m_width;            // how much line to draw
633     bool useWholeWidth = true;
634     unsigned paintStart = m_start;
635     unsigned paintEnd = end()+1;      // end points at the last char, not past it
636     if (paintStart <= underline.startOffset) {
637         paintStart = underline.startOffset;
638         useWholeWidth = false;
639         start = static_cast<RenderText*>(m_object)->width(m_start, paintStart - m_start, textPos(), m_firstLine);
640     }
641     if (paintEnd != underline.endOffset) {      // end points at the last char, not past it
642         paintEnd = min(paintEnd, (unsigned)underline.endOffset);
643         useWholeWidth = false;
644     }
645     if (m_truncation != cNoTruncation) {
646         paintEnd = min(paintEnd, (unsigned)m_truncation);
647         useWholeWidth = false;
648     }
649     if (!useWholeWidth) {
650         width = static_cast<RenderText*>(m_object)->width(paintStart, paintEnd - paintStart, textPos() + start, m_firstLine);
651     }
652
653     int underlineOffset = m_height - 3;
654     pt->setPen(Pen(underline.color, underline.thick ? 2 : 0));
655     pt->drawLineForText(IntPoint(_tx + start, _ty), underlineOffset, width);
656 }
657
658 int InlineTextBox::caretMinOffset() const
659 {
660     return m_start;
661 }
662
663 int InlineTextBox::caretMaxOffset() const
664 {
665     return m_start + m_len;
666 }
667
668 unsigned InlineTextBox::caretMaxRenderedOffset() const
669 {
670     return m_start + m_len;
671 }
672
673 int InlineTextBox::textPos() const
674 {
675     if (xPos() == 0)
676         return 0;
677         
678     RenderBlock *blockElement = object()->containingBlock();
679     return m_reversed ? xPos() - blockElement->borderRight() - blockElement->paddingRight()
680                       : xPos() - blockElement->borderLeft() - blockElement->paddingLeft();
681 }
682
683 int InlineTextBox::offsetForPosition(int _x, bool includePartialGlyphs) const
684 {
685     if (isLineBreak())
686         return 0;
687
688     RenderText* text = static_cast<RenderText*>(m_object);
689     RenderStyle *style = text->style(m_firstLine);
690     const Font* f = &style->font();
691     return f->checkSelectionPoint(text->str->unicode(), text->str->length(), m_start, m_len,
692         m_toAdd, text->tabWidth(), textPos(), _x - m_x,
693         m_reversed ? RTL : LTR, m_dirOverride || style->visuallyOrdered(), includePartialGlyphs);
694 }
695
696 int InlineTextBox::positionForOffset(int offset) const
697 {
698     if (isLineBreak())
699         return m_x;
700
701     RenderText *text = static_cast<RenderText *>(m_object);
702     const Font *f = text->font(m_firstLine);
703     int from = m_reversed ? offset - m_start : 0;
704     int to = m_reversed ? m_len : offset - m_start;
705     // FIXME: Do we need to add rightBearing here?
706     return f->selectionRectForText(IntPoint(m_x, 0), 0, text->tabWidth(), textPos(), text->str->unicode(), text->str->length(), m_start, m_len,
707         m_toAdd, m_reversed, m_dirOverride, from, to).right();
708 }
709
710 }