diff options
Diffstat (limited to 'khtml/rendering/render_list.cpp')
-rw-r--r-- | khtml/rendering/render_list.cpp | 586 |
1 files changed, 586 insertions, 0 deletions
diff --git a/khtml/rendering/render_list.cpp b/khtml/rendering/render_list.cpp new file mode 100644 index 000000000..139201e03 --- /dev/null +++ b/khtml/rendering/render_list.cpp @@ -0,0 +1,586 @@ +/** + * This file is part of the HTML rendering engine for KDE. + * + * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2000-2002 Dirk Mueller (mueller@kde.org) + * (C) 2003 Apple Computer, Inc. + * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com) + * + * 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 "rendering/render_list.h" +#include "rendering/render_canvas.h" +#include "rendering/enumerate.h" +#include "rendering/counter_tree.h" +#include "html/html_listimpl.h" +#include "misc/helper.h" +#include "misc/htmltags.h" +#include "misc/loader.h" +#include "xml/dom_docimpl.h" + +#include <kdebug.h> +#include <kglobal.h> +#include <qvaluelist.h> + +//#define BOX_DEBUG + +using namespace khtml; +using namespace Enumerate; + +const int cMarkerPadding = 7; + +// ------------------------------------------------------------------------- + +RenderListItem::RenderListItem(DOM::NodeImpl* node) + : RenderBlock(node) +{ + // init RenderObject attributes + setInline(false); // our object is not Inline + + predefVal = -1; + m_marker = 0; + m_counter = 0; + m_insideList = false; + m_deleteMarker = false; +} + +void RenderListItem::setStyle(RenderStyle *_style) +{ + RenderBlock::setStyle(_style); + + RenderStyle *newStyle = new RenderStyle(); + newStyle->ref(); + + newStyle->inheritFrom(style()); + + if(!m_marker && style()->listStyleType() != LNONE) { + m_marker = new (renderArena()) RenderListMarker(element()); + m_marker->setIsAnonymous( true ); + m_marker->setStyle(newStyle); + m_marker->setListItem( this ); + m_deleteMarker = true; + } else if ( m_marker && style()->listStyleType() == LNONE) { + m_marker->detach(); + m_marker = 0; + } + else if ( m_marker ) { + m_marker->setStyle(newStyle); + } + + newStyle->deref(); +} + +void RenderListItem::detach() +{ + if ( m_marker && m_deleteMarker ) + m_marker->detach(); + RenderBlock::detach(); +} + +static RenderObject* getParentOfFirstLineBox(RenderObject* 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()) + return curr; + + if (currChild->isFloating() || currChild->isPositioned()) + continue; + + if (currChild->isTable() || !currChild->isRenderBlock()) + break; + + if (currChild->style()->htmlHacks() && currChild->element() && + (currChild->element()->id() == ID_UL || currChild->element()->id() == ID_OL)) + break; + + RenderObject* lineBox = getParentOfFirstLineBox(currChild, marker); + if (lineBox) + return lineBox; + } + + return 0; +} + + +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) + { + if (markerPar) + markerPar->removeChild(m_marker); + if (!lineBoxParent) + lineBoxParent = this; + lineBoxParent->addChild(m_marker, lineBoxParent->firstChild()); + m_deleteMarker = false; + if (!m_marker->minMaxKnown()) + m_marker->calcMinMaxWidth(); + recalcMinMaxWidths(); + } + } +} + +void RenderListItem::calcMinMaxWidth() +{ + // Make sure our marker is in the correct location. + updateMarkerLocation(); + if (!minMaxKnown()) + RenderBlock::calcMinMaxWidth(); +} +/* +short RenderListItem::marginLeft() const +{ + if (m_insideList) + return RenderBlock::marginLeft(); + else + return kMax(m_marker->markerWidth(), RenderBlock::marginLeft()); +} + +short RenderListItem::marginRight() const +{ + return RenderBlock::marginRight(); +}*/ + +void RenderListItem::layout( ) +{ + KHTMLAssert( needsLayout() ); + KHTMLAssert( minMaxKnown() ); + + updateMarkerLocation(); + RenderBlock::layout(); +} + +// ----------------------------------------------------------- + +RenderListMarker::RenderListMarker(DOM::NodeImpl* node) + : RenderBox(node), m_listImage(0), m_markerWidth(0) +{ + // init RenderObject attributes + setInline(true); // our object is Inline + setReplaced(true); // pretend to be replaced + // val = -1; + // m_listImage = 0; +} + +RenderListMarker::~RenderListMarker() +{ + if(m_listImage) + m_listImage->deref(this); + if (m_listItem) + m_listItem->resetListMarker(); +} + +void RenderListMarker::setStyle(RenderStyle *s) +{ + if ( s && style() && s->listStylePosition() != style()->listStylePosition() ) + setNeedsLayoutAndMinMaxRecalc(); + + RenderBox::setStyle(s); + + if ( m_listImage != style()->listStyleImage() ) { + if(m_listImage) m_listImage->deref(this); + m_listImage = style()->listStyleImage(); + if(m_listImage) m_listImage->ref(this); + } +} + + +void RenderListMarker::paint(PaintInfo& paintInfo, int _tx, int _ty) +{ + if (paintInfo.phase != PaintActionForeground) + return; + + if (style()->visibility() != VISIBLE) return; + + _tx += m_x; + _ty += m_y; + + if((_ty > paintInfo.r.bottom()) || (_ty + m_height <= paintInfo.r.top())) + return; + + if(shouldPaintBackgroundOrBorder()) + paintBoxDecorations(paintInfo, _tx, _ty); + + QPainter* p = paintInfo.p; +#ifdef DEBUG_LAYOUT + kdDebug( 6040 ) << nodeName().string() << "(ListMarker)::paintObject(" << _tx << ", " << _ty << ")" << endl; +#endif + p->setFont(style()->font()); + const QFontMetrics fm = p->fontMetrics(); + + + // The marker needs to adjust its tx, for the case where it's an outside marker. + RenderObject* listItem = 0; + int leftLineOffset = 0; + int rightLineOffset = 0; + if (!listPositionInside()) { + listItem = this; + int yOffset = 0; + int xOffset = 0; + while (listItem && listItem != m_listItem) { + yOffset += listItem->yPos(); + xOffset += listItem->xPos(); + listItem = listItem->parent(); + } + + // Now that we have our xoffset within the listbox, we need to adjust ourselves by the delta + // between our current xoffset and our desired position (which is just outside the border box + // of the list item). + if (style()->direction() == LTR) { + leftLineOffset = m_listItem->leftRelOffset(yOffset, m_listItem->leftOffset(yOffset)); + _tx -= (xOffset - leftLineOffset) + m_listItem->paddingLeft() + m_listItem->borderLeft(); + } + else { + rightLineOffset = m_listItem->rightRelOffset(yOffset, m_listItem->rightOffset(yOffset)); + _tx += (rightLineOffset-xOffset) + m_listItem->paddingRight() + m_listItem->borderRight(); + } + } + + int offset = fm.ascent()*2/3; + bool haveImage = m_listImage && !m_listImage->isErrorImage(); + if (haveImage) + offset = m_listImage->pixmap().width(); + + int xoff = 0; + int yoff = fm.ascent() - offset; + + int bulletWidth = offset/2; + if (offset%2) + bulletWidth++; + if (!listPositionInside()) { + if (listItem && listItem->style()->direction() == LTR) + xoff = -cMarkerPadding - offset; + else + xoff = cMarkerPadding + (haveImage ? 0 : (offset - bulletWidth)); + } + else if (style()->direction() == RTL) + xoff += haveImage ? cMarkerPadding : (m_width - bulletWidth); + + if ( m_listImage && !m_listImage->isErrorImage()) { + p->drawPixmap( QPoint( _tx + xoff, _ty ), m_listImage->pixmap()); + return; + } + +#ifdef BOX_DEBUG + p->setPen( Qt::red ); + p->drawRect( _tx + xoff, _ty + yoff, offset, offset ); +#endif + + const QColor color( style()->color() ); + p->setPen( color ); + + switch(style()->listStyleType()) { + case LDISC: + p->setBrush( color ); + p->drawEllipse( _tx + xoff, _ty + (3 * yoff)/2, (offset>>1)+1, (offset>>1)+1 ); + return; + case LCIRCLE: + p->setBrush( Qt::NoBrush ); + p->drawEllipse( _tx + xoff, _ty + (3 * yoff)/2, (offset>>1)+1, (offset>>1)+1 ); + return; + case LSQUARE: + p->setBrush( color ); + p->drawRect( _tx + xoff, _ty + (3 * yoff)/2, (offset>>1)+1, (offset>>1)+1 ); + return; + case LBOX: + p->setBrush( Qt::NoBrush ); + p->drawRect( _tx + xoff, _ty + (3 * yoff)/2, (offset>>1)+1, (offset>>1)+1 ); + return; + case LDIAMOND: { + static QPointArray diamond(4); + int x = _tx + xoff; + int y = _ty + (3 * yoff)/2 - 1; + int s = (offset>>2)+1; + diamond[0] = QPoint(x+s, y); + diamond[1] = QPoint(x+2*s, y+s); + diamond[2] = QPoint(x+s, y+2*s); + diamond[3] = QPoint(x, y+s); + p->setBrush( color ); + p->drawConvexPolygon( diamond, 0, 4 ); + return; + } + case LNONE: + return; + default: + if (!m_item.isEmpty()) { + if(listPositionInside()) { + if( style()->direction() == LTR) { + p->drawText(_tx, _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, m_item); + p->drawText(_tx + fm.width(m_item), _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, + QString::fromLatin1(". ")); + } + else { + const QString& punct(QString::fromLatin1(" .")); + p->drawText(_tx, _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, punct); + p->drawText(_tx + fm.width(punct), _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, m_item); + } + } else { + if (style()->direction() == LTR) { + const QString& punct(QString::fromLatin1(". ")); + p->drawText(_tx-offset/2, _ty, 0, 0, Qt::AlignRight|Qt::DontClip, punct); + p->drawText(_tx-offset/2-fm.width(punct), _ty, 0, 0, Qt::AlignRight|Qt::DontClip, m_item); + } + else { + const QString& punct(QString::fromLatin1(" .")); + p->drawText(_tx+offset/2, _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, punct); + p->drawText(_tx+offset/2+fm.width(punct), _ty, 0, 0, Qt::AlignLeft|Qt::DontClip, m_item); + } + } + } + } +} + +void RenderListMarker::layout() +{ + KHTMLAssert( needsLayout() ); + + if ( !minMaxKnown() ) + calcMinMaxWidth(); + + setNeedsLayout(false); +} + +void RenderListMarker::setPixmap( const QPixmap &p, const QRect& r, CachedImage *o) +{ + if(o != m_listImage) { + RenderBox::setPixmap(p, r, o); + return; + } + + if(m_width != m_listImage->pixmap_size().width() || m_height != m_listImage->pixmap_size().height()) + setNeedsLayoutAndMinMaxRecalc(); + else + repaintRectangle(0, 0, m_width, m_height); +} + +void RenderListMarker::calcMinMaxWidth() +{ + KHTMLAssert( !minMaxKnown() ); + + m_markerWidth = m_width = 0; + + if(m_listImage && !m_listImage->isErrorImage()) { + m_markerWidth = m_listImage->pixmap().width() + cMarkerPadding; + if (listPositionInside()) + m_width = m_markerWidth; + m_height = m_listImage->pixmap().height(); + m_minWidth = m_maxWidth = m_width; + setMinMaxKnown(); + return; + } + + const QFontMetrics &fm = style()->fontMetrics(); + m_height = fm.ascent(); + + // Skip uncounted elements + switch(style()->listStyleType()) { + // Glyphs: + case LDISC: + case LCIRCLE: + case LSQUARE: + case LBOX: + case LDIAMOND: + m_markerWidth = fm.ascent(); + goto end; + default: + break; + } + + { // variable scope + CounterNode *counter = m_listItem->m_counter; + if (!counter) { + counter = m_listItem->getCounter("list-item", true); + counter->setRenderer(this); + m_listItem->m_counter = counter; + } + + + assert(counter); + int value = counter->count(); + if (counter->isReset()) value = counter->value(); + int total = value; + if (counter->parent()) total = counter->parent()->total(); + + switch(style()->listStyleType()) + { +// Numeric: + case LDECIMAL: + m_item.setNum ( value ); + break; + case DECIMAL_LEADING_ZERO: { + int decimals = 2; + int t = total/100; + while (t>0) { + t = t/10; + decimals++; + } + decimals = kMax(decimals, 2); + QString num = QString::number(value); + m_item.fill('0',decimals-num.length()); + m_item.append(num); + break; + } + case ARABIC_INDIC: + m_item = toArabicIndic( value ); + break; + case LAO: + m_item = toLao( value ); + break; + case PERSIAN: + case URDU: + m_item = toPersianUrdu( value ); + break; + case THAI: + m_item = toThai( value ); + break; + case TIBETAN: + m_item = toTibetan( value ); + break; +// Algoritmic: + case LOWER_ROMAN: + m_item = toRoman( value, false ); + break; + case UPPER_ROMAN: + m_item = toRoman( value, true ); + break; + case HEBREW: + m_item = toHebrew( value ); + break; + case ARMENIAN: + m_item = toArmenian( value ); + break; + case GEORGIAN: + m_item = toGeorgian( value ); + break; +// Alphabetic: + case LOWER_ALPHA: + case LOWER_LATIN: + m_item = toLowerLatin( value ); + break; + case UPPER_ALPHA: + case UPPER_LATIN: + m_item = toUpperLatin( value ); + break; + case LOWER_GREEK: + m_item = toLowerGreek( value ); + break; + case UPPER_GREEK: + m_item = toUpperGreek( value ); + break; + case HIRAGANA: + m_item = toHiragana( value ); + break; + case HIRAGANA_IROHA: + m_item = toHiraganaIroha( value ); + break; + case KATAKANA: + m_item = toKatakana( value ); + break; + case KATAKANA_IROHA: + m_item = toKatakanaIroha( value ); + break; +// Ideographic: + case JAPANESE_FORMAL: + m_item = toJapaneseFormal( value ); + break; + case JAPANESE_INFORMAL: + m_item = toJapaneseInformal( value ); + break; + case SIMP_CHINESE_FORMAL: + m_item = toSimpChineseFormal( value ); + break; + case SIMP_CHINESE_INFORMAL: + m_item = toSimpChineseInformal( value ); + break; + case TRAD_CHINESE_FORMAL: + m_item = toTradChineseFormal( value ); + break; + case CJK_IDEOGRAPHIC: + // CSS 3 List says treat as trad-chinese-informal + case TRAD_CHINESE_INFORMAL: + m_item = toTradChineseInformal( value ); + break; +// special: + case LNONE: + break; + default: + KHTMLAssert(false); + } + m_markerWidth = fm.width(m_item) + fm.width(QString::fromLatin1(". ")); + } + +end: + if(listPositionInside()) + m_width = m_markerWidth; + + m_minWidth = m_width; + m_maxWidth = m_width; + + setMinMaxKnown(); +} + +short RenderListMarker::lineHeight(bool /*b*/) const +{ + return height(); +} + +short RenderListMarker::baselinePosition(bool /*b*/) const +{ + return height(); +} + +void RenderListMarker::calcWidth() +{ + RenderBox::calcWidth(); +} + +/* +int CounterListItem::recount() const +{ + static_cast<RenderListItem*>(m_renderer)->m_marker->setNeedsLayoutAndMinMaxRecalc(); +} + +void CounterListItem::setSelfDirty() +{ + +}*/ + + +#undef BOX_DEBUG |