2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
5 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
25 #include "RenderListMarker.h"
27 #include "CachedImage.h"
28 #include "CharacterNames.h"
30 #include "GraphicsContext.h"
31 #include "RenderLayer.h"
32 #include "RenderListItem.h"
33 #include "RenderView.h"
37 using namespace Unicode;
41 const int cMarkerPadding = 7;
43 static String toRoman(int number, bool upper)
45 // FIXME: CSS3 describes how to make this work for much larger numbers,
46 // using overbars and special characters. It also specifies the characters
47 // in the range U+2160 to U+217F instead of standard ASCII ones.
48 if (number < 1 || number > 3999)
49 return String::number(number);
51 // Big enough to store largest roman number less than 3999 which
52 // is 3888 (MMMDCCCLXXXVIII)
53 const int lettersSize = 15;
54 UChar letters[lettersSize];
57 const UChar ldigits[] = { 'i', 'v', 'x', 'l', 'c', 'd', 'm' };
58 const UChar udigits[] = { 'I', 'V', 'X', 'L', 'C', 'D', 'M' };
59 const UChar* digits = upper ? udigits : ldigits;
62 int num = number % 10;
64 for (int i = num % 5; i > 0; i--)
65 letters[lettersSize - ++length] = digits[d];
66 if (num >= 4 && num <= 8)
67 letters[lettersSize - ++length] = digits[d + 1];
69 letters[lettersSize - ++length] = digits[d + 2];
71 letters[lettersSize - ++length] = digits[d];
76 ASSERT(length <= lettersSize);
77 return String(&letters[lettersSize - length], length);
80 static String toAlphabetic(int number, const UChar* alphabet, int alphabetSize)
82 ASSERT(alphabetSize >= 10);
85 return String::number(number);
87 const int lettersSize = 10; // big enough for a 32-bit int, with a 10-letter alphabet
88 UChar letters[lettersSize];
91 letters[lettersSize - 1] = alphabet[number % alphabetSize];
93 while ((number /= alphabetSize) > 0)
94 letters[lettersSize - ++length] = alphabet[number % alphabetSize - 1];
96 ASSERT(length <= lettersSize);
97 return String(&letters[lettersSize - length], length);
100 static int toHebrewUnder1000(int number, UChar letters[5])
102 // FIXME: CSS3 mentions various refinements not implemented here.
103 // FIXME: Should take a look at Mozilla's HebrewToText function (in nsBulletFrame).
104 ASSERT(number >= 0 && number < 1000);
106 int fourHundreds = number / 400;
107 for (int i = 0; i < fourHundreds; i++)
108 letters[length++] = 1511 + 3;
111 letters[length++] = 1511 + (number / 100) - 1;
113 if (number == 15 || number == 16) {
114 letters[length++] = 1487 + 9;
115 letters[length++] = 1487 + number - 9;
117 if (int tens = number / 10) {
118 static const UChar hebrewTens[9] = { 1497, 1499, 1500, 1502, 1504, 1505, 1506, 1508, 1510 };
119 letters[length++] = hebrewTens[tens - 1];
121 if (int ones = number % 10)
122 letters[length++] = 1487 + ones;
128 static String toHebrew(int number)
130 // FIXME: CSS3 mentions ways to make this work for much larger numbers.
131 if (number < 0 || number > 999999)
132 return String::number(number);
135 static const UChar hebrewZero[3] = { 0x05D0, 0x05E4, 0x05E1 };
136 return String(hebrewZero, 3);
139 const int lettersSize = 11; // big enough for two 5-digit sequences plus a quote mark between
140 UChar letters[lettersSize];
146 length = toHebrewUnder1000(number / 1000, letters);
147 letters[length++] = '\'';
148 number = number % 1000;
150 length += toHebrewUnder1000(number, letters + length);
152 ASSERT(length <= lettersSize);
153 return String(letters, length);
156 static int toArmenianUnder10000(int number, bool upper, bool addCircumflex, UChar letters[9])
158 ASSERT(number >= 0 && number < 10000);
161 int lowerOffset = upper ? 0 : 0x0030;
163 if (int thousands = number / 1000) {
164 if (thousands == 7) {
165 letters[length++] = 0x0548 + lowerOffset;
166 letters[length++] = 0x0552 + lowerOffset;
168 letters[length++] = 0x0302;
170 letters[length++] = (0x054C - 1 + lowerOffset) + thousands;
172 letters[length++] = 0x0302;
176 if (int hundreds = (number / 100) % 10) {
177 letters[length++] = (0x0543 - 1 + lowerOffset) + hundreds;
179 letters[length++] = 0x0302;
182 if (int tens = (number / 10) % 10) {
183 letters[length++] = (0x053A - 1 + lowerOffset) + tens;
185 letters[length++] = 0x0302;
188 if (int ones = number % 10) {
189 letters[length++] = (0x531 - 1 + lowerOffset) + ones;
191 letters[length++] = 0x0302;
197 static String toArmenian(int number, bool upper)
199 if (number < 1 || number > 99999999)
200 return String::number(number);
202 const int lettersSize = 18; // twice what toArmenianUnder10000 needs
203 UChar letters[lettersSize];
205 int length = toArmenianUnder10000(number / 10000, upper, true, letters);
206 length += toArmenianUnder10000(number % 10000, upper, false, letters + length);
208 ASSERT(length <= lettersSize);
209 return String(letters, length);
212 static String toGeorgian(int number)
214 if (number < 1 || number > 19999)
215 return String::number(number);
217 const int lettersSize = 5;
218 UChar letters[lettersSize];
223 letters[length++] = 0x10F5;
225 if (int thousands = (number / 1000) % 10) {
226 static const UChar georgianThousands[9] = {
227 0x10E9, 0x10EA, 0x10EB, 0x10EC, 0x10ED, 0x10EE, 0x10F4, 0x10EF, 0x10F0
229 letters[length++] = georgianThousands[thousands - 1];
232 if (int hundreds = (number / 100) % 10) {
233 static const UChar georgianHundreds[9] = {
234 0x10E0, 0x10E1, 0x10E2, 0x10F3, 0x10E4, 0x10E5, 0x10E6, 0x10E7, 0x10E8
236 letters[length++] = georgianHundreds[hundreds - 1];
239 if (int tens = (number / 10) % 10) {
240 static const UChar georgianTens[9] = {
241 0x10D8, 0x10D9, 0x10DA, 0x10DB, 0x10DC, 0x10F2, 0x10DD, 0x10DE, 0x10DF
243 letters[length++] = georgianTens[tens - 1];
246 if (int ones = number % 10) {
247 static const UChar georgianOnes[9] = {
248 0x10D0, 0x10D1, 0x10D2, 0x10D3, 0x10D4, 0x10D5, 0x10D6, 0x10F1, 0x10D7
250 letters[length++] = georgianOnes[ones - 1];
253 ASSERT(length <= lettersSize);
254 return String(letters, length);
257 // The table uses the order from the CSS3 specification:
258 // first 3 group markers, then 3 digit markers, then ten digits.
259 static String toCJKIdeographic(int number, const UChar table[16])
262 return String::number(number);
264 enum AbstractCJKChar {
266 secondGroupMarker, thirdGroupMarker, fourthGroupMarker,
267 secondDigitMarker, thirdDigitMarker, fourthDigitMarker,
268 digit0, digit1, digit2, digit3, digit4,
269 digit5, digit6, digit7, digit8, digit9
273 return String(&table[digit0 - 1], 1);
275 const int groupLength = 8; // 4 digits, 3 digit markers, and a group marker
276 const int bufferLength = 4 * groupLength;
277 AbstractCJKChar buffer[bufferLength] = { noChar };
279 for (int i = 0; i < 4; ++i) {
280 int groupValue = number % 10000;
283 // Process least-significant group first, but put it in the buffer last.
284 AbstractCJKChar* group = &buffer[(3 - i) * groupLength];
287 group[7] = static_cast<AbstractCJKChar>(secondGroupMarker - 1 + i);
289 // Put in the four digits and digit markers for any non-zero digits.
290 group[6] = static_cast<AbstractCJKChar>(digit0 + (groupValue % 10));
291 if (number != 0 || groupValue > 9) {
292 int digitValue = ((groupValue / 10) % 10);
293 group[4] = static_cast<AbstractCJKChar>(digit0 + digitValue);
295 group[5] = secondDigitMarker;
297 if (number != 0 || groupValue > 99) {
298 int digitValue = ((groupValue / 100) % 10);
299 group[2] = static_cast<AbstractCJKChar>(digit0 + digitValue);
301 group[3] = thirdDigitMarker;
303 if (number != 0 || groupValue > 999) {
304 int digitValue = groupValue / 1000;
305 group[0] = static_cast<AbstractCJKChar>(digit0 + digitValue);
307 group[1] = fourthDigitMarker;
310 // Remove the tens digit, but leave the marker, for any group that has
311 // a value of less than 20.
312 if (groupValue < 20) {
313 ASSERT(group[4] == noChar || group[4] == digit0 || group[4] == digit1);
321 // Convert into characters, omitting consecutive runs of digit0 and
322 // any trailing digit0.
324 UChar characters[bufferLength];
325 AbstractCJKChar last = noChar;
326 for (int i = 0; i < bufferLength; ++i) {
327 AbstractCJKChar a = buffer[i];
329 if (a != digit0 || last != digit0)
330 characters[length++] = table[a - 1];
337 return String(characters, length);
340 String listMarkerText(EListStyleType type, int value)
346 // We use the same characters for text security.
347 // See RenderText::setInternalString.
349 return String(&whiteBullet, 1);
351 return String(&bullet, 1);
353 // The CSS 2.1 test suite uses U+25EE BLACK MEDIUM SMALL SQUARE
354 // instead, but I think this looks better.
355 return String(&blackSquare, 1);
358 return String::number(value);
359 case DECIMAL_LEADING_ZERO:
360 if (value < -9 || value > 9)
361 return String::number(value);
363 return "-0" + String::number(-value); // -01 to -09
364 return "0" + String::number(value); // 00 to 09
368 static const UChar lowerLatinAlphabet[26] = {
369 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
370 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
372 return toAlphabetic(value, lowerLatinAlphabet, 26);
376 static const UChar upperLatinAlphabet[26] = {
377 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
378 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
380 return toAlphabetic(value, upperLatinAlphabet, 26);
383 static const UChar lowerGreekAlphabet[24] = {
384 0x03B1, 0x03B2, 0x03B3, 0x03B4, 0x03B5, 0x03B6, 0x03B7, 0x03B8,
385 0x03B9, 0x03BA, 0x03BB, 0x03BC, 0x03BD, 0x03BE, 0x03BF, 0x03C0,
386 0x03C1, 0x03C3, 0x03C4, 0x03C5, 0x03C6, 0x03C7, 0x03C8, 0x03C9
388 return toAlphabetic(value, lowerGreekAlphabet, 24);
392 // FIXME: This table comes from the CSS3 draft, and is probably
393 // incorrect, given the comments in that draft.
394 static const UChar hiraganaAlphabet[48] = {
395 0x3042, 0x3044, 0x3046, 0x3048, 0x304A, 0x304B, 0x304D, 0x304F,
396 0x3051, 0x3053, 0x3055, 0x3057, 0x3059, 0x305B, 0x305D, 0x305F,
397 0x3061, 0x3064, 0x3066, 0x3068, 0x306A, 0x306B, 0x306C, 0x306D,
398 0x306E, 0x306F, 0x3072, 0x3075, 0x3078, 0x307B, 0x307E, 0x307F,
399 0x3080, 0x3081, 0x3082, 0x3084, 0x3086, 0x3088, 0x3089, 0x308A,
400 0x308B, 0x308C, 0x308D, 0x308F, 0x3090, 0x3091, 0x3092, 0x3093
402 return toAlphabetic(value, hiraganaAlphabet, 48);
404 case HIRAGANA_IROHA: {
405 // FIXME: This table comes from the CSS3 draft, and is probably
406 // incorrect, given the comments in that draft.
407 static const UChar hiraganaIrohaAlphabet[47] = {
408 0x3044, 0x308D, 0x306F, 0x306B, 0x307B, 0x3078, 0x3068, 0x3061,
409 0x308A, 0x306C, 0x308B, 0x3092, 0x308F, 0x304B, 0x3088, 0x305F,
410 0x308C, 0x305D, 0x3064, 0x306D, 0x306A, 0x3089, 0x3080, 0x3046,
411 0x3090, 0x306E, 0x304A, 0x304F, 0x3084, 0x307E, 0x3051, 0x3075,
412 0x3053, 0x3048, 0x3066, 0x3042, 0x3055, 0x304D, 0x3086, 0x3081,
413 0x307F, 0x3057, 0x3091, 0x3072, 0x3082, 0x305B, 0x3059
415 return toAlphabetic(value, hiraganaIrohaAlphabet, 47);
418 // FIXME: This table comes from the CSS3 draft, and is probably
419 // incorrect, given the comments in that draft.
420 static const UChar katakanaAlphabet[48] = {
421 0x30A2, 0x30A4, 0x30A6, 0x30A8, 0x30AA, 0x30AB, 0x30AD, 0x30AF,
422 0x30B1, 0x30B3, 0x30B5, 0x30B7, 0x30B9, 0x30BB, 0x30BD, 0x30BF,
423 0x30C1, 0x30C4, 0x30C6, 0x30C8, 0x30CA, 0x30CB, 0x30CC, 0x30CD,
424 0x30CE, 0x30CF, 0x30D2, 0x30D5, 0x30D8, 0x30DB, 0x30DE, 0x30DF,
425 0x30E0, 0x30E1, 0x30E2, 0x30E4, 0x30E6, 0x30E8, 0x30E9, 0x30EA,
426 0x30EB, 0x30EC, 0x30ED, 0x30EF, 0x30F0, 0x30F1, 0x30F2, 0x30F3
428 return toAlphabetic(value, katakanaAlphabet, 48);
430 case KATAKANA_IROHA: {
431 // FIXME: This table comes from the CSS3 draft, and is probably
432 // incorrect, given the comments in that draft.
433 static const UChar katakanaIrohaAlphabet[47] = {
434 0x30A4, 0x30ED, 0x30CF, 0x30CB, 0x30DB, 0x30D8, 0x30C8, 0x30C1,
435 0x30EA, 0x30CC, 0x30EB, 0x30F2, 0x30EF, 0x30AB, 0x30E8, 0x30BF,
436 0x30EC, 0x30BD, 0x30C4, 0x30CD, 0x30CA, 0x30E9, 0x30E0, 0x30A6,
437 0x30F0, 0x30CE, 0x30AA, 0x30AF, 0x30E4, 0x30DE, 0x30B1, 0x30D5,
438 0x30B3, 0x30A8, 0x30C6, 0x30A2, 0x30B5, 0x30AD, 0x30E6, 0x30E1,
439 0x30DF, 0x30B7, 0x30F1, 0x30D2, 0x30E2, 0x30BB, 0x30B9
441 return toAlphabetic(value, katakanaIrohaAlphabet, 47);
444 case CJK_IDEOGRAPHIC: {
445 static const UChar traditionalChineseInformalTable[16] = {
446 0x842C, 0x5104, 0x5146,
447 0x5341, 0x767E, 0x5343,
448 0x96F6, 0x4E00, 0x4E8C, 0x4E09, 0x56DB,
449 0x4E94, 0x516D, 0x4E03, 0x516B, 0x4E5D
451 return toCJKIdeographic(value, traditionalChineseInformalTable);
455 return toRoman(value, false);
457 return toRoman(value, true);
460 // CSS3 says "armenian" means "lower-armenian".
461 // But the CSS2.1 test suite contains uppercase test results for "armenian",
462 // so we'll match the test suite.
463 return toArmenian(value, true);
465 return toGeorgian(value);
467 return toHebrew(value);
470 ASSERT_NOT_REACHED();
474 RenderListMarker::RenderListMarker(RenderListItem* item)
475 : RenderBox(item->document())
478 // init RenderObject attributes
479 setInline(true); // our object is Inline
480 setReplaced(true); // pretend to be replaced
483 RenderListMarker::~RenderListMarker()
486 m_image->removeClient(this);
489 void RenderListMarker::styleWillChange(StyleDifference diff, const RenderStyle* newStyle)
491 if (style() && (newStyle->listStylePosition() != style()->listStylePosition() || newStyle->listStyleType() != style()->listStyleType()))
492 setNeedsLayoutAndPrefWidthsRecalc();
494 RenderBox::styleWillChange(diff, newStyle);
497 void RenderListMarker::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
499 RenderBox::styleDidChange(diff, oldStyle);
501 if (m_image != style()->listStyleImage()) {
503 m_image->removeClient(this);
504 m_image = style()->listStyleImage();
506 m_image->addClient(this);
510 InlineBox* RenderListMarker::createInlineBox()
512 InlineBox* result = RenderBox::createInlineBox();
513 result->setIsText(isText());
517 bool RenderListMarker::isImage() const
519 return m_image && !m_image->errorOccurred();
522 void RenderListMarker::paint(PaintInfo& paintInfo, int tx, int ty)
524 if (paintInfo.phase != PaintPhaseForeground)
527 if (style()->visibility() != VISIBLE)
530 IntRect marker = getRelativeMarkerRect();
533 IntRect box(tx + x(), ty + y(), width(), height());
535 if (box.y() > paintInfo.rect.bottom() || box.y() + box.height() < paintInfo.rect.y())
538 if (hasBoxDecorations())
539 paintBoxDecorations(paintInfo, box.x(), box.y());
541 GraphicsContext* context = paintInfo.context;
545 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
546 paintCustomHighlight(tx, ty, style()->highlight(), true);
548 context->drawImage(m_image->image(this, marker.size()), marker.location());
549 if (selectionState() != SelectionNone) {
550 // FIXME: selectionRect() is in absolute, not painting coordinates.
551 context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace());
557 // FIXME: paint gap between marker and list item proper
558 if (style()->highlight() != nullAtom && !paintInfo.context->paintingDisabled())
559 paintCustomHighlight(tx, ty, style()->highlight(), true);
562 if (selectionState() != SelectionNone) {
563 // FIXME: selectionRect() is in absolute, not painting coordinates.
564 context->fillRect(selectionRect(), selectionBackgroundColor(), style()->colorSpace());
567 const Color color(style()->color());
568 context->setStrokeColor(color, style()->colorSpace());
569 context->setStrokeStyle(SolidStroke);
570 context->setStrokeThickness(1.0f);
571 context->setFillColor(color, style()->colorSpace());
573 switch (style()->listStyleType()) {
575 context->drawEllipse(marker);
578 context->setFillColor(Color::transparent, DeviceColorSpace);
579 context->drawEllipse(marker);
582 context->drawRect(marker);
587 case CJK_IDEOGRAPHIC:
588 case DECIMAL_LEADING_ZERO:
605 if (m_text.isEmpty())
608 TextRun textRun(m_text);
610 // Text is not arbitrary. We can judge whether it's RTL from the first character,
611 // and we only need to handle the direction RightToLeft for now.
612 bool textNeedsReversing = direction(m_text[0]) == RightToLeft;
613 Vector<UChar> reversedText;
614 if (textNeedsReversing) {
615 int length = m_text.length();
616 reversedText.grow(length);
617 for (int i = 0; i < length; ++i)
618 reversedText[length - i - 1] = m_text[i];
619 textRun = TextRun(reversedText.data(), length);
622 const Font& font = style()->font();
623 if (style()->direction() == LTR) {
624 int width = font.width(textRun);
625 context->drawText(style()->font(), textRun, marker.location());
626 const UChar periodSpace[2] = { '.', ' ' };
627 context->drawText(style()->font(), TextRun(periodSpace, 2), marker.location() + IntSize(width, 0));
629 const UChar spacePeriod[2] = { ' ', '.' };
630 TextRun spacePeriodRun(spacePeriod, 2);
631 int width = font.width(spacePeriodRun);
632 context->drawText(style()->font(), spacePeriodRun, marker.location());
633 context->drawText(style()->font(), textRun, marker.location() + IntSize(width, 0));
637 void RenderListMarker::layout()
639 ASSERT(needsLayout());
640 ASSERT(!prefWidthsDirty());
643 setWidth(m_image->imageSize(this, style()->effectiveZoom()).width());
644 setHeight(m_image->imageSize(this, style()->effectiveZoom()).height());
646 setWidth(minPrefWidth());
647 setHeight(style()->font().height());
650 m_marginLeft = m_marginRight = 0;
652 Length leftMargin = style()->marginLeft();
653 Length rightMargin = style()->marginRight();
654 if (leftMargin.isFixed())
655 m_marginLeft = leftMargin.value();
656 if (rightMargin.isFixed())
657 m_marginRight = rightMargin.value();
659 setNeedsLayout(false);
662 void RenderListMarker::imageChanged(WrappedImagePtr o, const IntRect*)
664 // A list marker can't have a background or border image, so no need to call the base class method.
665 if (o != m_image->data())
668 if (width() != m_image->imageSize(this, style()->effectiveZoom()).width() || height() != m_image->imageSize(this, style()->effectiveZoom()).height() || m_image->errorOccurred())
669 setNeedsLayoutAndPrefWidthsRecalc();
674 void RenderListMarker::calcPrefWidths()
676 ASSERT(prefWidthsDirty());
680 const Font& font = style()->font();
683 // FIXME: This is a somewhat arbitrary width. Generated images for markers really won't become particularly useful
684 // until we support the CSS3 marker pseudoclass to allow control over the width and height of the marker box.
685 int bulletWidth = font.ascent() / 2;
686 m_image->setImageContainerSize(IntSize(bulletWidth, bulletWidth));
687 m_minPrefWidth = m_maxPrefWidth = m_image->imageSize(this, style()->effectiveZoom()).width();
688 setPrefWidthsDirty(false);
694 EListStyleType type = style()->listStyleType();
701 m_text = listMarkerText(type, 0); // value is ignored for these types
702 width = (font.ascent() * 2 / 3 + 1) / 2 + 2;
705 case CJK_IDEOGRAPHIC:
706 case DECIMAL_LEADING_ZERO:
721 m_text = listMarkerText(type, m_listItem->value());
722 if (m_text.isEmpty())
725 int itemWidth = font.width(m_text);
726 const UChar periodSpace[2] = { '.', ' ' };
727 int periodSpaceWidth = font.width(TextRun(periodSpace, 2));
728 width = itemWidth + periodSpaceWidth;
733 m_minPrefWidth = width;
734 m_maxPrefWidth = width;
736 setPrefWidthsDirty(false);
741 void RenderListMarker::updateMargins()
743 const Font& font = style()->font();
750 if (style()->direction() == LTR)
751 marginRight = cMarkerPadding;
753 marginLeft = cMarkerPadding;
754 } else switch (style()->listStyleType()) {
758 if (style()->direction() == LTR) {
760 marginRight = font.ascent() - minPrefWidth() + 1;
762 marginLeft = font.ascent() - minPrefWidth() + 1;
770 if (style()->direction() == LTR) {
772 marginLeft = -minPrefWidth() - cMarkerPadding;
774 int offset = font.ascent() * 2 / 3;
775 switch (style()->listStyleType()) {
779 marginLeft = -offset - cMarkerPadding - 1;
784 marginLeft = m_text.isEmpty() ? 0 : -minPrefWidth() - offset / 2;
789 marginLeft = cMarkerPadding;
791 int offset = font.ascent() * 2 / 3;
792 switch (style()->listStyleType()) {
796 marginLeft = offset + cMarkerPadding + 1 - minPrefWidth();
801 marginLeft = m_text.isEmpty() ? 0 : offset / 2;
805 marginRight = -marginLeft - minPrefWidth();
808 style()->setMarginLeft(Length(marginLeft, Fixed));
809 style()->setMarginRight(Length(marginRight, Fixed));
812 int RenderListMarker::lineHeight(bool, bool) const
815 return m_listItem->lineHeight(false, true);
819 int RenderListMarker::baselinePosition(bool, bool) const
822 const Font& font = style()->font();
823 return font.ascent() + (lineHeight(false) - font.height())/2;
828 bool RenderListMarker::isInside() const
830 return m_listItem->notInList() || style()->listStylePosition() == INSIDE;
833 IntRect RenderListMarker::getRelativeMarkerRect()
836 return IntRect(x(), y(), m_image->imageSize(this, style()->effectiveZoom()).width(), m_image->imageSize(this, style()->effectiveZoom()).height());
838 switch (style()->listStyleType()) {
842 // FIXME: Are these particular rounding rules necessary?
843 const Font& font = style()->font();
844 int ascent = font.ascent();
845 int bulletWidth = (ascent * 2 / 3 + 1) / 2;
846 return IntRect(x() + 1, y() + 3 * (ascent - ascent * 2 / 3) / 2, bulletWidth, bulletWidth);
851 case CJK_IDEOGRAPHIC:
852 case DECIMAL_LEADING_ZERO:
867 if (m_text.isEmpty())
869 const Font& font = style()->font();
870 int itemWidth = font.width(m_text);
871 const UChar periodSpace[2] = { '.', ' ' };
872 int periodSpaceWidth = font.width(TextRun(periodSpace, 2));
873 return IntRect(x(), y() + font.ascent(), itemWidth + periodSpaceWidth, font.height());
879 void RenderListMarker::setSelectionState(SelectionState state)
881 RenderBox::setSelectionState(state);
882 if (InlineBox* box = inlineBoxWrapper())
883 if (RootInlineBox* root = box->root())
884 root->setHasSelectedChildren(state != SelectionNone);
885 containingBlock()->setSelectionState(state);
888 IntRect RenderListMarker::selectionRectForRepaint(RenderBoxModelObject* repaintContainer, bool clipToVisibleContent)
890 ASSERT(!needsLayout());
892 if (selectionState() == SelectionNone || !inlineBoxWrapper())
895 RootInlineBox* root = inlineBoxWrapper()->root();
896 IntRect rect(0, root->selectionTop() - y(), width(), root->selectionHeight());
898 if (clipToVisibleContent)
899 computeRectForRepaint(repaintContainer, rect);
901 rect = localToContainerQuad(FloatRect(rect), repaintContainer).enclosingBoundingBox();
906 } // namespace WebCore