1 /**
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
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 *
24 */
25
26 #include "config.h"
27 #include "RenderListItem.h"
28
29 #include "CachedImage.h"
30 #include "HTMLNames.h"
31 #include "HTMLOListElement.h"
32 #include "RenderListMarker.h"
33 #include "RenderView.h"
34 #include <wtf/StdLibExtras.h>
35
36 using namespace std;
37
38 namespace WebCore {
39
40 using namespace HTMLNames;
41
RenderListItem(Node * node)42 RenderListItem::RenderListItem(Node* node)
43 : RenderBlock(node)
44 , m_marker(0)
45 , m_hasExplicitValue(false)
46 , m_isValueUpToDate(false)
47 , m_notInList(false)
48 {
49 setInline(false);
50 }
51
styleDidChange(StyleDifference diff,const RenderStyle * oldStyle)52 void RenderListItem::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
53 {
54 RenderBlock::styleDidChange(diff, oldStyle);
55
56 if (style()->listStyleType() != LNONE ||
57 (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) {
58 RefPtr<RenderStyle> newStyle = RenderStyle::create();
59 // The marker always inherits from the list item, regardless of where it might end
60 // up (e.g., in some deeply nested line box). See CSS3 spec.
61 newStyle->inheritFrom(style());
62 if (!m_marker)
63 m_marker = new (renderArena()) RenderListMarker(this);
64 m_marker->setStyle(newStyle.release());
65 } else if (m_marker) {
66 m_marker->destroy();
67 m_marker = 0;
68 }
69 }
70
destroy()71 void RenderListItem::destroy()
72 {
73 if (m_marker) {
74 m_marker->destroy();
75 m_marker = 0;
76 }
77 RenderBlock::destroy();
78 }
79
enclosingList(Node * node)80 static Node* enclosingList(Node* node)
81 {
82 Node* parent = node->parentNode();
83 for (Node* n = parent; n; n = n->parentNode())
84 if (n->hasTagName(ulTag) || n->hasTagName(olTag))
85 return n;
86 // If there's no actual <ul> or <ol> list element, then our parent acts as
87 // our list for purposes of determining what other list items should be
88 // numbered as part of the same list.
89 return parent;
90 }
91
previousListItem(Node * list,const RenderListItem * item)92 static RenderListItem* previousListItem(Node* list, const RenderListItem* item)
93 {
94 for (Node* node = item->node()->traversePreviousNode(); node != list; node = node->traversePreviousNode()) {
95 RenderObject* renderer = node->renderer();
96 if (!renderer || !renderer->isListItem())
97 continue;
98 Node* otherList = enclosingList(node);
99 // This item is part of our current list, so it's what we're looking for.
100 if (list == otherList)
101 return toRenderListItem(renderer);
102 // We found ourself inside another list; lets skip the rest of it.
103 // Use traverseNextNode() here because the other list itself may actually
104 // be a list item itself. We need to examine it, so we do this to counteract
105 // the traversePreviousNode() that will be done by the loop.
106 if (otherList)
107 node = otherList->traverseNextNode();
108 }
109 return 0;
110 }
111
calcValue() const112 inline int RenderListItem::calcValue() const
113 {
114 if (m_hasExplicitValue)
115 return m_explicitValue;
116 Node* list = enclosingList(node());
117 // FIXME: This recurses to a possible depth of the length of the list.
118 // That's not good -- we need to change this to an iterative algorithm.
119 if (RenderListItem* previousItem = previousListItem(list, this))
120 return previousItem->value() + 1;
121 if (list && list->hasTagName(olTag))
122 return static_cast<HTMLOListElement*>(list)->start();
123 return 1;
124 }
125
updateValueNow() const126 void RenderListItem::updateValueNow() const
127 {
128 m_value = calcValue();
129 m_isValueUpToDate = true;
130 }
131
isEmpty() const132 bool RenderListItem::isEmpty() const
133 {
134 return lastChild() == m_marker;
135 }
136
getParentOfFirstLineBox(RenderBlock * curr,RenderObject * marker)137 static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* marker)
138 {
139 RenderObject* firstChild = curr->firstChild();
140 if (!firstChild)
141 return 0;
142
143 for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) {
144 if (currChild == marker)
145 continue;
146
147 if (currChild->isInline() && (!currChild->isRenderInline() || curr->generatesLineBoxesForInlineChild(currChild)))
148 return curr;
149
150 if (currChild->isFloating() || currChild->isPositioned())
151 continue;
152
153 if (currChild->isTable() || !currChild->isRenderBlock())
154 break;
155
156 if (curr->isListItem() && currChild->style()->htmlHacks() && currChild->node() &&
157 (currChild->node()->hasTagName(ulTag)|| currChild->node()->hasTagName(olTag)))
158 break;
159
160 RenderObject* lineBox = getParentOfFirstLineBox(toRenderBlock(currChild), marker);
161 if (lineBox)
162 return lineBox;
163 }
164
165 return 0;
166 }
167
updateValue()168 void RenderListItem::updateValue()
169 {
170 if (!m_hasExplicitValue) {
171 m_isValueUpToDate = false;
172 if (m_marker)
173 m_marker->setNeedsLayoutAndPrefWidthsRecalc();
174 }
175 }
176
firstNonMarkerChild(RenderObject * parent)177 static RenderObject* firstNonMarkerChild(RenderObject* parent)
178 {
179 RenderObject* result = parent->firstChild();
180 while (result && result->isListMarker())
181 result = result->nextSibling();
182 return result;
183 }
184
updateMarkerLocation()185 void RenderListItem::updateMarkerLocation()
186 {
187 // Sanity check the location of our marker.
188 if (m_marker) {
189 RenderObject* markerPar = m_marker->parent();
190 RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker);
191 if (!lineBoxParent) {
192 // If the marker is currently contained inside an anonymous box,
193 // then we are the only item in that anonymous box (since no line box
194 // parent was found). It's ok to just leave the marker where it is
195 // in this case.
196 if (markerPar && markerPar->isAnonymousBlock())
197 lineBoxParent = markerPar;
198 else
199 lineBoxParent = this;
200 }
201
202 if (markerPar != lineBoxParent || m_marker->prefWidthsDirty()) {
203 // Removing and adding the marker can trigger repainting in
204 // containers other than ourselves, so we need to disable LayoutState.
205 view()->disableLayoutState();
206 updateFirstLetter();
207 m_marker->remove();
208 if (!lineBoxParent)
209 lineBoxParent = this;
210 lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent));
211 if (m_marker->prefWidthsDirty())
212 m_marker->calcPrefWidths();
213 view()->enableLayoutState();
214 }
215 }
216 }
217
calcPrefWidths()218 void RenderListItem::calcPrefWidths()
219 {
220 ASSERT(prefWidthsDirty());
221
222 updateMarkerLocation();
223
224 RenderBlock::calcPrefWidths();
225 }
226
layout()227 void RenderListItem::layout()
228 {
229 ASSERT(needsLayout());
230
231 updateMarkerLocation();
232 RenderBlock::layout();
233 }
234
positionListMarker()235 void RenderListItem::positionListMarker()
236 {
237 if (m_marker && !m_marker->isInside() && m_marker->inlineBoxWrapper()) {
238 int markerOldX = m_marker->x();
239 int yOffset = 0;
240 int xOffset = 0;
241 for (RenderBox* o = m_marker->parentBox(); o != this; o = o->parentBox()) {
242 yOffset += o->y();
243 xOffset += o->x();
244 }
245
246 bool adjustOverflow = false;
247 int markerXPos;
248 RootInlineBox* root = m_marker->inlineBoxWrapper()->root();
249
250 if (style()->direction() == LTR) {
251 int leftLineOffset = leftRelOffset(yOffset, leftOffset(yOffset, false), false);
252 markerXPos = leftLineOffset - xOffset - paddingLeft() - borderLeft() + m_marker->marginLeft();
253 m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0);
254 if (markerXPos < root->leftOverflow()) {
255 root->setHorizontalOverflowPositions(markerXPos, root->rightOverflow());
256 adjustOverflow = true;
257 }
258 } else {
259 int rightLineOffset = rightRelOffset(yOffset, rightOffset(yOffset, false), false);
260 markerXPos = rightLineOffset - xOffset + paddingRight() + borderRight() + m_marker->marginLeft();
261 m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0);
262 if (markerXPos + m_marker->width() > root->rightOverflow()) {
263 root->setHorizontalOverflowPositions(root->leftOverflow(), markerXPos + m_marker->width());
264 adjustOverflow = true;
265 }
266 }
267
268 if (adjustOverflow) {
269 IntRect markerRect(markerXPos + xOffset, yOffset, m_marker->width(), m_marker->height());
270 RenderBox* o = m_marker;
271 do {
272 o = o->parentBox();
273 if (o->isRenderBlock())
274 toRenderBlock(o)->addVisualOverflow(markerRect);
275 markerRect.move(-o->x(), -o->y());
276 } while (o != this);
277 }
278 }
279 }
280
paint(PaintInfo & paintInfo,int tx,int ty)281 void RenderListItem::paint(PaintInfo& paintInfo, int tx, int ty)
282 {
283 if (!height())
284 return;
285
286 RenderBlock::paint(paintInfo, tx, ty);
287 }
288
markerText() const289 const String& RenderListItem::markerText() const
290 {
291 if (m_marker)
292 return m_marker->text();
293 DEFINE_STATIC_LOCAL(String, staticNullString, ());
294 return staticNullString;
295 }
296
explicitValueChanged()297 void RenderListItem::explicitValueChanged()
298 {
299 if (m_marker)
300 m_marker->setNeedsLayoutAndPrefWidthsRecalc();
301 Node* listNode = enclosingList(node());
302 RenderObject* listRenderer = 0;
303 if (listNode)
304 listRenderer = listNode->renderer();
305 for (RenderObject* renderer = this; renderer; renderer = renderer->nextInPreOrder(listRenderer))
306 if (renderer->isListItem()) {
307 RenderListItem* item = toRenderListItem(renderer);
308 if (!item->m_hasExplicitValue) {
309 item->m_isValueUpToDate = false;
310 if (RenderListMarker* marker = item->m_marker)
311 marker->setNeedsLayoutAndPrefWidthsRecalc();
312 }
313 }
314 }
315
setExplicitValue(int value)316 void RenderListItem::setExplicitValue(int value)
317 {
318 if (m_hasExplicitValue && m_explicitValue == value)
319 return;
320 m_explicitValue = value;
321 m_value = value;
322 m_hasExplicitValue = true;
323 explicitValueChanged();
324 }
325
clearExplicitValue()326 void RenderListItem::clearExplicitValue()
327 {
328 if (!m_hasExplicitValue)
329 return;
330 m_hasExplicitValue = false;
331 m_isValueUpToDate = false;
332 explicitValueChanged();
333 }
334
335 } // namespace WebCore
336