webengine/osswebengine/WebCore/rendering/RenderListItem.cpp
changeset 0 dd21522fd290
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/webengine/osswebengine/WebCore/rendering/RenderListItem.cpp	Mon Mar 30 12:54:55 2009 +0300
@@ -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