You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
587 lines
16 KiB
587 lines
16 KiB
/**
|
|
* 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 <tqvaluelist.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);
|
|
|
|
TQPainter* p = paintInfo.p;
|
|
#ifdef DEBUG_LAYOUT
|
|
kdDebug( 6040 ) << nodeName().string() << "(ListMarker)::paintObject(" << _tx << ", " << _ty << ")" << endl;
|
|
#endif
|
|
p->setFont(style()->font());
|
|
const TQFontMetrics 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( TQPoint( _tx + xoff, _ty ), m_listImage->pixmap());
|
|
return;
|
|
}
|
|
|
|
#ifdef BOX_DEBUG
|
|
p->setPen( Qt::red );
|
|
p->drawRect( _tx + xoff, _ty + yoff, offset, offset );
|
|
#endif
|
|
|
|
const TQColor 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 TQPointArray diamond(4);
|
|
int x = _tx + xoff;
|
|
int y = _ty + (3 * yoff)/2 - 1;
|
|
int s = (offset>>2)+1;
|
|
diamond[0] = TQPoint(x+s, y);
|
|
diamond[1] = TQPoint(x+2*s, y+s);
|
|
diamond[2] = TQPoint(x+s, y+2*s);
|
|
diamond[3] = TQPoint(x, y+s);
|
|
p->setBrush( color );
|
|
p->tqdrawConvexPolygon( 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|TQt::DontClip, m_item);
|
|
p->drawText(_tx + fm.width(m_item), _ty, 0, 0, Qt::AlignLeft|TQt::DontClip,
|
|
TQString::tqfromLatin1(". "));
|
|
}
|
|
else {
|
|
const TQString& punct(TQString::tqfromLatin1(" ."));
|
|
p->drawText(_tx, _ty, 0, 0, Qt::AlignLeft|TQt::DontClip, punct);
|
|
p->drawText(_tx + fm.width(punct), _ty, 0, 0, Qt::AlignLeft|TQt::DontClip, m_item);
|
|
}
|
|
} else {
|
|
if (style()->direction() == LTR) {
|
|
const TQString& punct(TQString::tqfromLatin1(". "));
|
|
p->drawText(_tx-offset/2, _ty, 0, 0, Qt::AlignRight|TQt::DontClip, punct);
|
|
p->drawText(_tx-offset/2-fm.width(punct), _ty, 0, 0, Qt::AlignRight|TQt::DontClip, m_item);
|
|
}
|
|
else {
|
|
const TQString& punct(TQString::tqfromLatin1(" ."));
|
|
p->drawText(_tx+offset/2, _ty, 0, 0, Qt::AlignLeft|TQt::DontClip, punct);
|
|
p->drawText(_tx+offset/2+fm.width(punct), _ty, 0, 0, Qt::AlignLeft|TQt::DontClip, m_item);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void RenderListMarker::layout()
|
|
{
|
|
KHTMLAssert( needsLayout() );
|
|
|
|
if ( !minMaxKnown() )
|
|
calcMinMaxWidth();
|
|
|
|
setNeedsLayout(false);
|
|
}
|
|
|
|
void RenderListMarker::setPixmap( const TQPixmap &p, const TQRect& 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
|
|
tqrepaintRectangle(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 TQFontMetrics &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);
|
|
TQString num = TQString::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(TQString::tqfromLatin1(". "));
|
|
}
|
|
|
|
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
|