diff options
Diffstat (limited to 'WebCore/rendering/RenderListItem.cpp')
-rw-r--r-- | WebCore/rendering/RenderListItem.cpp | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/WebCore/rendering/RenderListItem.cpp b/WebCore/rendering/RenderListItem.cpp new file mode 100644 index 0000000..ff4a6fa --- /dev/null +++ b/WebCore/rendering/RenderListItem.cpp @@ -0,0 +1,336 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * Copyright (C) 2003, 2004, 2005, 2006 Apple Computer, Inc. + * Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ + +#include "config.h" +#include "RenderListItem.h" + +#include "CachedImage.h" +#include "HTMLNames.h" +#include "HTMLOListElement.h" +#include "RenderListMarker.h" +#include "RenderView.h" + +using namespace std; + +namespace WebCore { + +using namespace HTMLNames; + +RenderListItem::RenderListItem(Node* node) + : RenderBlock(node) + , m_marker(0) + , m_hasExplicitValue(false) + , m_isValueUpToDate(false) + , m_notInList(false) +{ + setInline(false); +} + +void RenderListItem::setStyle(RenderStyle* newStyle) +{ + RenderBlock::setStyle(newStyle); + + if (style()->listStyleType() != LNONE || + (style()->listStyleImage() && !style()->listStyleImage()->errorOccurred())) { + RenderStyle* newStyle = new (renderArena()) RenderStyle; + newStyle->ref(); + // The marker always inherits from the list item, regardless of where it might end + // up (e.g., in some deeply nested line box). See CSS3 spec. + newStyle->inheritFrom(style()); + if (!m_marker) + m_marker = new (renderArena()) RenderListMarker(this); + m_marker->setStyle(newStyle); + newStyle->deref(renderArena()); + } else if (m_marker) { + m_marker->destroy(); + m_marker = 0; + } +} + +void RenderListItem::destroy() +{ + if (m_marker) { + m_marker->destroy(); + m_marker = 0; + } + RenderBlock::destroy(); +} + +static Node* enclosingList(Node* node) +{ + Node* parent = node->parentNode(); + for (Node* n = parent; n; n = n->parentNode()) + if (n->hasTagName(ulTag) || n->hasTagName(olTag)) + return n; + // If there's no actual <ul> or <ol> list element, then our parent acts as + // our list for purposes of determining what other list items should be + // numbered as part of the same list. + return parent; +} + +static RenderListItem* previousListItem(Node* list, const RenderListItem* item) +{ + for (Node* n = item->node()->traversePreviousNode(); n != list; n = n->traversePreviousNode()) { + RenderObject* o = n->renderer(); + if (o && o->isListItem()) { + Node* otherList = enclosingList(n); + // This item is part of our current list, so it's what we're looking for. + if (list == otherList) + return static_cast<RenderListItem*>(o); + // We found ourself inside another list; lets skip the rest of it. + // Use traverseNextNode() here because the other list itself may actually + // be a list item itself. We need to examine it, so we do this to counteract + // the traversePreviousNode() that will be done by the loop. + if (otherList) + n = otherList->traverseNextNode(); + } + } + return 0; +} + +inline int RenderListItem::calcValue() const +{ + if (m_hasExplicitValue) + return m_explicitValue; + Node* list = enclosingList(node()); + // FIXME: This recurses to a possible depth of the length of the list. + // That's not good -- we need to change this to an iterative algorithm. + if (RenderListItem* previousItem = previousListItem(list, this)) + return previousItem->value() + 1; + if (list && list->hasTagName(olTag)) + return static_cast<HTMLOListElement*>(list)->start(); + return 1; +} + +void RenderListItem::updateValueNow() const +{ + m_value = calcValue(); + m_isValueUpToDate = true; +} + +bool RenderListItem::isEmpty() const +{ + return lastChild() == m_marker; +} + +static RenderObject* getParentOfFirstLineBox(RenderBlock* curr, RenderObject* marker) +{ + RenderObject* firstChild = curr->firstChild(); + if (!firstChild) + return 0; + + for (RenderObject* currChild = firstChild; currChild; currChild = currChild->nextSibling()) { + if (currChild == marker) + continue; + + if (currChild->isInline() && (!currChild->isInlineFlow() || curr->generatesLineBoxesForInlineChild(currChild))) + return curr; + + if (currChild->isFloating() || currChild->isPositioned()) + continue; + + if (currChild->isTable() || !currChild->isRenderBlock()) + break; + + if (curr->isListItem() && currChild->style()->htmlHacks() && currChild->element() && + (currChild->element()->hasTagName(ulTag)|| currChild->element()->hasTagName(olTag))) + break; + + RenderObject* lineBox = getParentOfFirstLineBox(static_cast<RenderBlock*>(currChild), marker); + if (lineBox) + return lineBox; + } + + return 0; +} + +void RenderListItem::updateValue() +{ + if (!m_hasExplicitValue) { + m_isValueUpToDate = false; + if (m_marker) + m_marker->setNeedsLayoutAndPrefWidthsRecalc(); + } +} + +static RenderObject* firstNonMarkerChild(RenderObject* parent) +{ + RenderObject* result = parent->firstChild(); + while (result && result->isListMarker()) + result = result->nextSibling(); + return result; +} + +void RenderListItem::updateMarkerLocation() +{ + // Sanity check the location of our marker. + if (m_marker) { + RenderObject* markerPar = m_marker->parent(); + RenderObject* lineBoxParent = getParentOfFirstLineBox(this, m_marker); + if (!lineBoxParent) { + // If the marker is currently contained inside an anonymous box, + // then we are the only item in that anonymous box (since no line box + // parent was found). It's ok to just leave the marker where it is + // in this case. + if (markerPar && markerPar->isAnonymousBlock()) + lineBoxParent = markerPar; + else + lineBoxParent = this; + } + + if (markerPar != lineBoxParent || m_marker->prefWidthsDirty()) { + // Removing and adding the marker can trigger repainting in + // containers other than ourselves, so we need to disable LayoutState. + view()->disableLayoutState(); + updateFirstLetter(); + m_marker->remove(); + if (!lineBoxParent) + lineBoxParent = this; + lineBoxParent->addChild(m_marker, firstNonMarkerChild(lineBoxParent)); + if (m_marker->prefWidthsDirty()) + m_marker->calcPrefWidths(); + view()->enableLayoutState(); + } + } +} + +void RenderListItem::calcPrefWidths() +{ + ASSERT(prefWidthsDirty()); + + updateMarkerLocation(); + + RenderBlock::calcPrefWidths(); +} + +void RenderListItem::layout() +{ + ASSERT(needsLayout()); + + updateMarkerLocation(); + RenderBlock::layout(); +} + +void RenderListItem::positionListMarker() +{ + if (m_marker && !m_marker->isInside() && m_marker->inlineBoxWrapper()) { + int markerOldX = m_marker->xPos(); + int yOffset = 0; + int xOffset = 0; + for (RenderObject* o = m_marker->parent(); o != this; o = o->parent()) { + yOffset += o->yPos(); + xOffset += o->xPos(); + } + + bool adjustOverflow = false; + int markerXPos; + RootInlineBox* root = m_marker->inlineBoxWrapper()->root(); + + if (style()->direction() == LTR) { + int leftLineOffset = leftRelOffset(yOffset, leftOffset(yOffset)); + markerXPos = leftLineOffset - xOffset - paddingLeft() - borderLeft() + m_marker->marginLeft(); + m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0); + if (markerXPos < root->leftOverflow()) { + root->setHorizontalOverflowPositions(markerXPos, root->rightOverflow()); + adjustOverflow = true; + } + } else { + int rightLineOffset = rightRelOffset(yOffset, rightOffset(yOffset)); + markerXPos = rightLineOffset - xOffset + paddingRight() + borderRight() + m_marker->marginLeft(); + m_marker->inlineBoxWrapper()->adjustPosition(markerXPos - markerOldX, 0); + if (markerXPos + m_marker->width() > root->rightOverflow()) { + root->setHorizontalOverflowPositions(root->leftOverflow(), markerXPos + m_marker->width()); + adjustOverflow = true; + } + } + + if (adjustOverflow) { + IntRect markerRect(markerXPos + xOffset, yOffset, m_marker->width(), m_marker->height()); + RenderObject* o = m_marker; + do { + o = o->parent(); + if (o->isRenderBlock()) + static_cast<RenderBlock*>(o)->addVisualOverflow(markerRect); + markerRect.move(-o->xPos(), -o->yPos()); + } while (o != this); + } + } +} + +void RenderListItem::paint(PaintInfo& paintInfo, int tx, int ty) +{ + if (!m_height) + return; + + RenderBlock::paint(paintInfo, tx, ty); +} + +const String& RenderListItem::markerText() const +{ + if (m_marker) + return m_marker->text(); + static String staticNullString; + return staticNullString; +} + +void RenderListItem::explicitValueChanged() +{ + if (m_marker) + m_marker->setNeedsLayoutAndPrefWidthsRecalc(); + Node* listNode = enclosingList(node()); + RenderObject* listRenderer = 0; + if (listNode) + listRenderer = listNode->renderer(); + for (RenderObject* r = this; r; r = r->nextInPreOrder(listRenderer)) + if (r->isListItem()) { + RenderListItem* item = static_cast<RenderListItem*>(r); + if (!item->m_hasExplicitValue) { + item->m_isValueUpToDate = false; + if (RenderListMarker* marker = item->m_marker) + marker->setNeedsLayoutAndPrefWidthsRecalc(); + } + } +} + +void RenderListItem::setExplicitValue(int value) +{ + if (m_hasExplicitValue && m_explicitValue == value) + return; + m_explicitValue = value; + m_value = value; + m_hasExplicitValue = true; + explicitValueChanged(); +} + +void RenderListItem::clearExplicitValue() +{ + if (!m_hasExplicitValue) + return; + m_hasExplicitValue = false; + m_isValueUpToDate = false; + explicitValueChanged(); +} + +} // namespace WebCore |