blob: b69612ed7903b7b1c7aa476ee51044b831157faa [file] [log] [blame]
eseidele34973b2006-05-15 21:16:29 +00001/**
2 * This file is part of the DOM implementation for KDE.
3 *
4 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc.
7 * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public License
20 * along with this library; see the file COPYING.LIB. If not, write to
ddkilzerc8eccec2007-09-26 02:29:57 +000021 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
eseidele34973b2006-05-15 21:16:29 +000023 *
24 */
darinec375482007-01-06 01:36:24 +000025
eseidele34973b2006-05-15 21:16:29 +000026#include "config.h"
27#include "RenderListItem.h"
28
29#include "CachedImage.h"
30#include "HTMLNames.h"
31#include "HTMLOListElement.h"
32#include "RenderListMarker.h"
weinigfef13632007-04-29 20:09:08 +000033#include "RenderView.h"
eseidele34973b2006-05-15 21:16:29 +000034
35using namespace std;
36
37namespace WebCore {
38
39using namespace HTMLNames;
40
41RenderListItem::RenderListItem(Node* node)
42 : RenderBlock(node)
eseidele34973b2006-05-15 21:16:29 +000043 , m_marker(0)
darinec375482007-01-06 01:36:24 +000044 , m_hasExplicitValue(false)
45 , m_isValueUpToDate(false)
eseidele34973b2006-05-15 21:16:29 +000046 , m_notInList(false)
eseidele34973b2006-05-15 21:16:29 +000047{
darinec375482007-01-06 01:36:24 +000048 setInline(false);
eseidele34973b2006-05-15 21:16:29 +000049}
50
simon.fraser@apple.coma89b8cd2008-10-10 03:15:31 +000051void RenderListItem::styleDidChange(RenderStyle::Diff diff, const RenderStyle* oldStyle)
eseidele34973b2006-05-15 21:16:29 +000052{
simon.fraser@apple.coma89b8cd2008-10-10 03:15:31 +000053 RenderBlock::styleDidChange(diff, oldStyle);
eseidele34973b2006-05-15 21:16:29 +000054
55 if (style()->listStyleType() != LNONE ||
kmcculloadfd67d2007-03-03 02:18:43 +000056 (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) {
hyatt@apple.comdec0cbf22008-10-17 00:25:33 +000057 RefPtr<RenderStyle> newStyle = RenderStyle::create();
eseidele34973b2006-05-15 21:16:29 +000058 // The marker always inherits from the list item, regardless of where it might end
darinec375482007-01-06 01:36:24 +000059 // up (e.g., in some deeply nested line box). See CSS3 spec.
eseidele34973b2006-05-15 21:16:29 +000060 newStyle->inheritFrom(style());
darinec375482007-01-06 01:36:24 +000061 if (!m_marker)
62 m_marker = new (renderArena()) RenderListMarker(this);
hyatt@apple.comdec0cbf22008-10-17 00:25:33 +000063 m_marker->setStyle(newStyle.release());
eseidele34973b2006-05-15 21:16:29 +000064 } else if (m_marker) {
65 m_marker->destroy();
66 m_marker = 0;
67 }
68}
69
70void RenderListItem::destroy()
71{
72 if (m_marker) {
73 m_marker->destroy();
74 m_marker = 0;
75 }
76 RenderBlock::destroy();
77}
78
79static Node* enclosingList(Node* node)
80{
darin76d3b832006-07-29 15:06:04 +000081 Node* parent = node->parentNode();
82 for (Node* n = parent; n; n = n->parentNode())
eseidele34973b2006-05-15 21:16:29 +000083 if (n->hasTagName(ulTag) || n->hasTagName(olTag))
84 return n;
darin76d3b832006-07-29 15:06:04 +000085 // If there's no actual <ul> or <ol> list element, then our parent acts as
86 // our list for purposes of determining what other list items should be
87 // numbered as part of the same list.
88 return parent;
eseidele34973b2006-05-15 21:16:29 +000089}
90
darinec375482007-01-06 01:36:24 +000091static RenderListItem* previousListItem(Node* list, const RenderListItem* item)
eseidele34973b2006-05-15 21:16:29 +000092{
eseidele34973b2006-05-15 21:16:29 +000093 for (Node* n = item->node()->traversePreviousNode(); n != list; n = n->traversePreviousNode()) {
94 RenderObject* o = n->renderer();
95 if (o && o->isListItem()) {
96 Node* otherList = enclosingList(n);
97 // This item is part of our current list, so it's what we're looking for.
98 if (list == otherList)
99 return static_cast<RenderListItem*>(o);
100 // We found ourself inside another list; lets skip the rest of it.
darin76d3b832006-07-29 15:06:04 +0000101 // Use traverseNextNode() here because the other list itself may actually
102 // be a list item itself. We need to examine it, so we do this to counteract
103 // the traversePreviousNode() that will be done by the loop.
eseidele34973b2006-05-15 21:16:29 +0000104 if (otherList)
darin76d3b832006-07-29 15:06:04 +0000105 n = otherList->traverseNextNode();
eseidele34973b2006-05-15 21:16:29 +0000106 }
107 }
108 return 0;
109}
110
darinec375482007-01-06 01:36:24 +0000111inline int RenderListItem::calcValue() const
eseidele34973b2006-05-15 21:16:29 +0000112{
darinec375482007-01-06 01:36:24 +0000113 if (m_hasExplicitValue)
114 return m_explicitValue;
115 Node* list = enclosingList(node());
116 // FIXME: This recurses to a possible depth of the length of the list.
117 // That's not good -- we need to change this to an iterative algorithm.
118 if (RenderListItem* previousItem = previousListItem(list, this))
119 return previousItem->value() + 1;
120 if (list && list->hasTagName(olTag))
121 return static_cast<HTMLOListElement*>(list)->start();
122 return 1;
123}
124
125void RenderListItem::updateValueNow() const
126{
127 m_value = calcValue();
128 m_isValueUpToDate = true;
eseidele34973b2006-05-15 21:16:29 +0000129}
130
131bool RenderListItem::isEmpty() const
132{
133 return lastChild() == m_marker;
134}
135
bdashccffb432007-07-13 11:51:40 +0000136static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* marker)
eseidele34973b2006-05-15 21:16:29 +0000137{
138 RenderObject* firstChild = curr->firstChild();
139 if (!firstChild)
140 return 0;
weinigc24ab182006-10-30 22:41:29 +0000141
eseidele34973b2006-05-15 21:16:29 +0000142 for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) {
143 if (currChild == marker)
144 continue;
weinigc24ab182006-10-30 22:41:29 +0000145
bdashccffb432007-07-13 11:51:40 +0000146 if (currChild->isInline() && (!currChild->isInlineFlow() || curr->generatesLineBoxesForInlineChild(currChild)))
eseidele34973b2006-05-15 21:16:29 +0000147 return curr;
weinigc24ab182006-10-30 22:41:29 +0000148
eseidele34973b2006-05-15 21:16:29 +0000149 if (currChild->isFloating() || currChild->isPositioned())
150 continue;
weinigc24ab182006-10-30 22:41:29 +0000151
eseidele34973b2006-05-15 21:16:29 +0000152 if (currChild->isTable() || !currChild->isRenderBlock())
153 break;
weinigc24ab182006-10-30 22:41:29 +0000154
bdashccffb432007-07-13 11:51:40 +0000155 if (curr->isListItem() && currChild->style()->htmlHacks() && currChild->element() &&
eseidele34973b2006-05-15 21:16:29 +0000156 (currChild->element()->hasTagName(ulTag)|| currChild->element()->hasTagName(olTag)))
157 break;
weinigc24ab182006-10-30 22:41:29 +0000158
bdashccffb432007-07-13 11:51:40 +0000159 RenderObject* lineBox = getParentOfFirstLineBox(static_cast<RenderBlock*>(currChild), marker);
eseidele34973b2006-05-15 21:16:29 +0000160 if (lineBox)
161 return lineBox;
162 }
weinigc24ab182006-10-30 22:41:29 +0000163
eseidele34973b2006-05-15 21:16:29 +0000164 return 0;
165}
166
darinec375482007-01-06 01:36:24 +0000167void RenderListItem::updateValue()
eseidele34973b2006-05-15 21:16:29 +0000168{
darinec375482007-01-06 01:36:24 +0000169 if (!m_hasExplicitValue) {
170 m_isValueUpToDate = false;
171 if (m_marker)
darin7e7c8e42007-04-25 01:14:03 +0000172 m_marker->setNeedsLayoutAndPrefWidthsRecalc();
darinec375482007-01-06 01:36:24 +0000173 }
eseidele34973b2006-05-15 21:16:29 +0000174}
175
hyatt818bb122007-04-25 19:10:24 +0000176static RenderObject* firstNonMarkerChild(RenderObject* parent)
177{
178 RenderObject* result = parent->firstChild();
179 while (result && result->isListMarker())
180 result = result->nextSibling();
181 return result;
182}
183
eseidele34973b2006-05-15 21:16:29 +0000184void RenderListItem::updateMarkerLocation()
185{
186 // Sanity check the location of our marker.
187 if (m_marker) {
188 RenderObject* markerPar = m_marker->parent();
189 RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker);
190 if (!lineBoxParent) {
191 // If the marker is currently contained inside an anonymous box,
192 // then we are the only item in that anonymous box (since no line box
193 // parent was found). It's ok to just leave the marker where it is
194 // in this case.
195 if (markerPar && markerPar->isAnonymousBlock())
196 lineBoxParent = markerPar;
197 else
198 lineBoxParent = this;
199 }
weinigc24ab182006-10-30 22:41:29 +0000200
darin7e7c8e42007-04-25 01:14:03 +0000201 if (markerPar != lineBoxParent || m_marker->prefWidthsDirty()) {
weinigfef13632007-04-29 20:09:08 +0000202 // Removing and adding the marker can trigger repainting in
203 // containers other than ourselves, so we need to disable LayoutState.
204 view()->disableLayoutState();
hyatt818bb122007-04-25 19:10:24 +0000205 updateFirstLetter();
hyattb8663442006-08-04 21:04:11 +0000206 m_marker->remove();
eseidele34973b2006-05-15 21:16:29 +0000207 if (!lineBoxParent)
208 lineBoxParent = this;
hyatt818bb122007-04-25 19:10:24 +0000209 lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent));
darin7e7c8e42007-04-25 01:14:03 +0000210 if (m_marker->prefWidthsDirty())
211 m_marker->calcPrefWidths();
weinigfef13632007-04-29 20:09:08 +0000212 view()->enableLayoutState();
eseidele34973b2006-05-15 21:16:29 +0000213 }
214 }
215}
216
darin7e7c8e42007-04-25 01:14:03 +0000217void RenderListItem::calcPrefWidths()
eseidele34973b2006-05-15 21:16:29 +0000218{
hyatt818bb122007-04-25 19:10:24 +0000219 ASSERT(prefWidthsDirty());
220
eseidele34973b2006-05-15 21:16:29 +0000221 updateMarkerLocation();
hyatt818bb122007-04-25 19:10:24 +0000222
223 RenderBlock::calcPrefWidths();
eseidele34973b2006-05-15 21:16:29 +0000224}
225
226void RenderListItem::layout()
227{
hyatt818bb122007-04-25 19:10:24 +0000228 ASSERT(needsLayout());
229
eseidele34973b2006-05-15 21:16:29 +0000230 updateMarkerLocation();
231 RenderBlock::layout();
232}
233
234void RenderListItem::positionListMarker()
235{
mitz@apple.comb2141332008-09-10 18:19:39 +0000236 if (m_marker && !m_marker->isInside() && m_marker->inlineBoxWrapper()) {
237 int markerOldX = m_marker->xPos();
238 int yOffset = 0;
239 int xOffset = 0;
240 for (RenderObject* o = m_marker->parent(); o != this; o = o->parent()) {
241 yOffset += o->yPos();
242 xOffset += o->xPos();
weinigf1db1db2007-02-28 00:04:39 +0000243 }
mitz@apple.com8de23dd2008-09-10 18:17:14 +0000244
mitz@apple.comb2141332008-09-10 18:19:39 +0000245 bool adjustOverflow = false;
246 int markerXPos;
247 RootInlineBox* root = m_marker->inlineBoxWrapper()->root();
248
249 if (style()->direction() == LTR) {
250 int leftLineOffset = leftRelOffset(yOffset, leftOffset(yOffset));
251 markerXPos = leftLineOffset - xOffset - paddingLeft() - borderLeft() + m_marker->marginLeft();
252 m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0);
253 if (markerXPos < root->leftOverflow()) {
254 root->setHorizontalOverflowPositions(markerXPos, root->rightOverflow());
255 adjustOverflow = true;
256 }
257 } else {
258 int rightLineOffset = rightRelOffset(yOffset, rightOffset(yOffset));
259 markerXPos = rightLineOffset - xOffset + paddingRight() + borderRight() + m_marker->marginLeft();
260 m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0);
261 if (markerXPos + m_marker->width() > root->rightOverflow()) {
262 root->setHorizontalOverflowPositions(root->leftOverflow(), markerXPos + m_marker->width());
263 adjustOverflow = true;
264 }
265 }
266
267 if (adjustOverflow) {
268 IntRect markerRect(markerXPos + xOffset, yOffset, m_marker->width(), m_marker->height());
269 RenderObject* o = m_marker;
270 do {
271 o = o->parent();
272 if (o->isRenderBlock())
273 static_cast<RenderBlock*>(o)->addVisualOverflow(markerRect);
274 markerRect.move(-o->xPos(), -o->yPos());
275 } while (o != this);
276 }
eseidele34973b2006-05-15 21:16:29 +0000277 }
278}
279
weinigc24ab182006-10-30 22:41:29 +0000280void RenderListItem::paint(PaintInfo& paintInfo, int tx, int ty)
eseidele34973b2006-05-15 21:16:29 +0000281{
282 if (!m_height)
283 return;
284
weinigc24ab182006-10-30 22:41:29 +0000285 RenderBlock::paint(paintInfo, tx, ty);
eseidele34973b2006-05-15 21:16:29 +0000286}
287
darinec375482007-01-06 01:36:24 +0000288const String& RenderListItem::markerText() const
eseidele34973b2006-05-15 21:16:29 +0000289{
darinec375482007-01-06 01:36:24 +0000290 if (m_marker)
291 return m_marker->text();
292 static String staticNullString;
293 return staticNullString;
294}
295
296void RenderListItem::explicitValueChanged()
297{
298 if (m_marker)
darin7e7c8e42007-04-25 01:14:03 +0000299 m_marker->setNeedsLayoutAndPrefWidthsRecalc();
darinec375482007-01-06 01:36:24 +0000300 Node* listNode = enclosingList(node());
adele60f28362007-01-06 06:24:08 +0000301 RenderObject* listRenderer = 0;
darinec375482007-01-06 01:36:24 +0000302 if (listNode)
303 listRenderer = listNode->renderer();
304 for (RenderObject* r = this; r; r = r->nextInPreOrder(listRenderer))
305 if (r->isListItem()) {
306 RenderListItem* item = static_cast<RenderListItem*>(r);
307 if (!item->m_hasExplicitValue) {
308 item->m_isValueUpToDate = false;
309 if (RenderListMarker* marker = item->m_marker)
darin7e7c8e42007-04-25 01:14:03 +0000310 marker->setNeedsLayoutAndPrefWidthsRecalc();
darinec375482007-01-06 01:36:24 +0000311 }
312 }
313}
314
315void RenderListItem::setExplicitValue(int value)
316{
317 if (m_hasExplicitValue && m_explicitValue == value)
318 return;
319 m_explicitValue = value;
320 m_value = value;
321 m_hasExplicitValue = true;
322 explicitValueChanged();
323}
324
325void RenderListItem::clearExplicitValue()
326{
327 if (!m_hasExplicitValue)
328 return;
329 m_hasExplicitValue = false;
330 m_isValueUpToDate = false;
331 explicitValueChanged();
eseidele34973b2006-05-15 21:16:29 +0000332}
333
weinigc24ab182006-10-30 22:41:29 +0000334} // namespace WebCore