2 * This file is part of the HTML rendering engine for KDE.
4 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2000-2002 Dirk Mueller (mueller@kde.org)
7 * (C) 2003 Apple Computer, Inc.
8 * (C) 2004-2005 Allan Sandfeld Jensen (kde@carewolf.com)
10 * This library is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Library General Public
12 * License as published by the Free Software Foundation; either
13 * version 2 of the License, or (at your option) any later version.
15 * This library is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Library General Public License for more details.
20 * You should have received a copy of the GNU Library General Public License
21 * along with this library; see the file COPYING.LIB. If not, write to
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
23 * Boston, MA 02110-1301, USA.
27 #include "rendering/render_list.h"
28 #include "rendering/render_canvas.h"
29 #include "rendering/enumerate.h"
30 #include "rendering/counter_tree.h"
31 #include "html/html_listimpl.h"
32 #include "imload/imagepainter.h"
33 #include "misc/helper.h"
34 #include "misc/htmltags.h"
35 #include "misc/loader.h"
36 #include "xml/dom_docimpl.h"
43 using namespace khtml
;
44 using namespace Enumerate
;
45 using namespace khtmlImLoad
;
47 const int cMarkerPadding
= 7;
49 // -------------------------------------------------------------------------
51 RenderListItem::RenderListItem(DOM::NodeImpl
* node
)
54 // init RenderObject attributes
55 setInline(false); // our object is not Inline
61 m_deleteMarker
= false;
64 void RenderListItem::setStyle(RenderStyle
*_style
)
66 RenderBlock::setStyle(_style
);
68 RenderStyle
*newStyle
= new RenderStyle();
71 newStyle
->inheritFrom(style());
73 if(!m_marker
&& style()->listStyleType() != LNONE
) {
74 m_marker
= new (renderArena()) RenderListMarker(element());
75 m_marker
->setIsAnonymous( true );
76 m_marker
->setStyle(newStyle
);
77 m_marker
->setListItem( this );
78 m_deleteMarker
= true;
79 } else if ( m_marker
&& style()->listStyleType() == LNONE
) {
83 else if ( m_marker
) {
84 m_marker
->setStyle(newStyle
);
90 void RenderListItem::detach()
92 if ( m_marker
&& m_deleteMarker
)
94 RenderBlock::detach();
97 static RenderObject
* getParentOfFirstLineBox(RenderObject
* curr
, RenderObject
* marker
)
99 RenderObject
* firstChild
= curr
->firstChild();
103 for (RenderObject
* currChild
= firstChild
;
104 currChild
; currChild
= currChild
->nextSibling()) {
105 if (currChild
== marker
)
108 if (currChild
->isInline())
111 if (currChild
->isFloating() || currChild
->isPositioned())
114 if (currChild
->isTable() || !currChild
->isRenderBlock())
117 if (currChild
->style()->htmlHacks() && currChild
->element() &&
118 (currChild
->element()->id() == ID_UL
|| currChild
->element()->id() == ID_OL
||
119 currChild
->element()->id() == ID_DIR
|| currChild
->element()->id() == ID_MENU
))
122 RenderObject
* lineBox
= getParentOfFirstLineBox(currChild
, marker
);
131 void RenderListItem::updateMarkerLocation()
133 // Sanity check the location of our marker.
135 RenderObject
* markerPar
= m_marker
->parent();
136 RenderObject
* lineBoxParent
= getParentOfFirstLineBox(this, m_marker
);
137 if (!lineBoxParent
) {
138 // If the marker is currently contained inside an anonymous box,
139 // then we are the only item in that anonymous box (since no line box
140 // parent was found). It's ok to just leave the marker where it is
142 if (markerPar
&& markerPar
->isAnonymousBlock())
143 lineBoxParent
= markerPar
;
145 lineBoxParent
= this;
147 if (markerPar
!= lineBoxParent
)
150 markerPar
->removeChild(m_marker
);
152 lineBoxParent
= this;
153 lineBoxParent
->addChild(m_marker
, lineBoxParent
->firstChild());
154 m_deleteMarker
= false;
155 if (!m_marker
->minMaxKnown())
156 m_marker
->calcMinMaxWidth();
157 recalcMinMaxWidths();
162 void RenderListItem::calcMinMaxWidth()
164 // Make sure our marker is in the correct location.
165 updateMarkerLocation();
167 RenderBlock::calcMinMaxWidth();
170 short RenderListItem::marginLeft() const
173 return RenderBlock::marginLeft();
175 return qMax(m_marker->markerWidth(), RenderBlock::marginLeft());
178 short RenderListItem::marginRight() const
180 return RenderBlock::marginRight();
183 void RenderListItem::layout( )
185 KHTMLAssert( needsLayout() );
186 KHTMLAssert( minMaxKnown() );
188 updateMarkerLocation();
189 RenderBlock::layout();
192 // -----------------------------------------------------------
194 RenderListMarker::RenderListMarker(DOM::NodeImpl
* node
)
195 : RenderBox(node
), m_listImage(0), m_markerWidth(0)
197 // init RenderObject attributes
198 setInline(true); // our object is Inline
199 setReplaced(true); // pretend to be replaced
204 RenderListMarker::~RenderListMarker()
207 m_listImage
->deref(this);
209 m_listItem
->resetListMarker();
212 void RenderListMarker::setStyle(RenderStyle
*s
)
214 if ( style() && s
->listStylePosition() != style()->listStylePosition() )
215 setNeedsLayoutAndMinMaxRecalc();
217 RenderBox::setStyle(s
);
219 if ( m_listImage
!= style()->listStyleImage() ) {
220 if(m_listImage
) m_listImage
->deref(this);
221 m_listImage
= style()->listStyleImage();
222 if(m_listImage
) m_listImage
->ref(this);
227 void RenderListMarker::paint(PaintInfo
& paintInfo
, int _tx
, int _ty
)
229 if (paintInfo
.phase
!= PaintActionForeground
)
232 if (style()->visibility() != VISIBLE
) return;
237 if((_ty
> paintInfo
.r
.bottom()) || (_ty
+ m_height
<= paintInfo
.r
.top()))
240 if(shouldPaintBackgroundOrBorder())
241 paintBoxDecorations(paintInfo
, _tx
, _ty
);
243 QPainter
* p
= paintInfo
.p
;
245 kDebug( 6040 ) << nodeName().string() << "(ListMarker)::paintObject(" << _tx
<< ", " << _ty
<< ")" << endl
;
247 p
->setFont(style()->font());
248 const QFontMetrics fm
= p
->fontMetrics();
251 // The marker needs to adjust its tx, for the case where it's an outside marker.
252 RenderObject
* listItem
= 0;
253 int leftLineOffset
= 0;
254 int rightLineOffset
= 0;
255 if (!listPositionInside()) {
259 while (listItem
&& listItem
!= m_listItem
) {
260 yOffset
+= listItem
->yPos();
261 xOffset
+= listItem
->xPos();
262 listItem
= listItem
->parent();
265 // Now that we have our xoffset within the listbox, we need to adjust ourselves by the delta
266 // between our current xoffset and our desired position (which is just outside the border box
267 // of the list item).
268 if (style()->direction() == LTR
) {
269 leftLineOffset
= m_listItem
->leftRelOffset(yOffset
, m_listItem
->leftOffset(yOffset
));
270 _tx
-= (xOffset
- leftLineOffset
) + m_listItem
->paddingLeft() + m_listItem
->borderLeft();
273 rightLineOffset
= m_listItem
->rightRelOffset(yOffset
, m_listItem
->rightOffset(yOffset
));
274 _tx
+= (rightLineOffset
-xOffset
) + m_listItem
->paddingRight() + m_listItem
->borderRight();
278 int offset
= fm
.ascent()*2/3;
279 bool haveImage
= m_listImage
&& !m_listImage
->isErrorImage();
281 offset
= m_listImage
->pixmap_size().width();
284 int yoff
= fm
.ascent() - offset
;
286 int bulletWidth
= offset
/2;
289 if (!listPositionInside()) {
290 if (listItem
&& listItem
->style()->direction() == LTR
)
291 xoff
= -cMarkerPadding
- offset
;
293 xoff
= cMarkerPadding
+ (haveImage
? 0 : (offset
- bulletWidth
));
295 else if (style()->direction() == RTL
)
296 xoff
+= haveImage
? cMarkerPadding
: (m_width
- bulletWidth
);
298 if ( m_listImage
&& !m_listImage
->isErrorImage()) {
299 ImagePainter
painter(m_listImage
->image());
300 painter
.paint( _tx
+ xoff
, _ty
, p
);
305 p
->setPen( Qt::red
);
306 p
->drawRect( _tx
+ xoff
, _ty
+ yoff
, offset
, offset
);
309 const QColor
color( style()->color() );
312 switch(style()->listStyleType()) {
314 p
->setBrush( color
);
315 p
->drawEllipse( _tx
+ xoff
, _ty
+ (3 * yoff
)/2, (offset
>>1), (offset
>>1) );
318 p
->setBrush( Qt::NoBrush
);
319 p
->drawEllipse( _tx
+ xoff
, _ty
+ (3 * yoff
)/2, (offset
>>1), (offset
>>1) );
322 p
->setBrush( color
);
323 p
->drawRect( _tx
+ xoff
, _ty
+ (3 * yoff
)/2, (offset
>>1), (offset
>>1) );
326 p
->setBrush( Qt::NoBrush
);
327 p
->drawRect( _tx
+ xoff
, _ty
+ (3 * yoff
)/2, (offset
>>1), (offset
>>1) );
330 static QPolygon
diamond(4);
332 int y
= _ty
+ (3 * yoff
)/2 - 1;
333 int s
= (offset
>>2)+1;
334 diamond
[0] = QPoint(x
+s
, y
);
335 diamond
[1] = QPoint(x
+2*s
, y
+s
);
336 diamond
[2] = QPoint(x
+s
, y
+2*s
);
337 diamond
[3] = QPoint(x
, y
+s
);
338 p
->setBrush( color
);
340 p
->drawConvexPolygon( diamond
.constData(), 4 );
346 if (!m_item
.isEmpty()) {
347 if(listPositionInside()) {
350 #warning "KDE4: hack for testregression, remove when main branch"
352 _tx
+= qMax(-fm
.minLeftBearing(), 0);
354 if( style()->direction() == LTR
) {
355 p
->drawText(_tx
, _ty
, 0, 0, Qt::AlignLeft
|Qt::TextDontClip
, m_item
);
356 p
->drawText(_tx
+ fm
.width(m_item
), _ty
, 0, 0, Qt::AlignLeft
|Qt::TextDontClip
,
357 QLatin1String(". "));
360 const QString
& punct(QLatin1String(" ."));
361 p
->drawText(_tx
, _ty
, 0, 0, Qt::AlignLeft
|Qt::TextDontClip
, punct
);
362 p
->drawText(_tx
+ fm
.width(punct
), _ty
, 0, 0, Qt::AlignLeft
|Qt::TextDontClip
, m_item
);
365 if (style()->direction() == LTR
) {
366 const QString
& punct(QLatin1String(". "));
367 int itemWidth
= fm
.width(m_item
);
368 int punctWidth
= fm
.width(punct
);
369 p
->drawText(_tx
-offset
/2-punctWidth
, _ty
, 0, 0, Qt::AlignLeft
|Qt::TextDontClip
, punct
);
370 p
->drawText(_tx
-offset
/2-punctWidth
-itemWidth
, _ty
, 0, 0, Qt::AlignLeft
|Qt::TextDontClip
, m_item
);
375 #warning "KDE4: hack for testregression, remove when main branch"
377 _tx
+= qMax(-fm
.minLeftBearing(), 0);
380 const QString
& punct(QLatin1String(" ."));
381 p
->drawText(_tx
+offset
/2, _ty
, 0, 0, Qt::AlignLeft
|Qt::TextDontClip
, punct
);
382 p
->drawText(_tx
+offset
/2+fm
.width(punct
), _ty
, 0, 0, Qt::AlignLeft
|Qt::TextDontClip
, m_item
);
389 void RenderListMarker::layout()
391 KHTMLAssert( needsLayout() );
393 if ( !minMaxKnown() )
396 setNeedsLayout(false);
399 void RenderListMarker::updatePixmap( const QRect
& r
, CachedImage
*o
)
401 if(o
!= m_listImage
) {
402 RenderBox::updatePixmap(r
, o
);
406 if(m_width
!= m_listImage
->pixmap_size().width() || m_height
!= m_listImage
->pixmap_size().height())
407 setNeedsLayoutAndMinMaxRecalc();
409 repaintRectangle(0, 0, m_width
, m_height
);
412 void RenderListMarker::calcMinMaxWidth()
414 KHTMLAssert( !minMaxKnown() );
416 m_markerWidth
= m_width
= 0;
418 if(m_listImage
&& !m_listImage
->isErrorImage()) {
419 m_markerWidth
= m_listImage
->pixmap_size().width() + cMarkerPadding
;
420 if (listPositionInside())
421 m_width
= m_markerWidth
;
422 m_height
= m_listImage
->pixmap_size().height();
423 m_minWidth
= m_maxWidth
= m_width
;
428 const QFontMetrics
&fm
= style()->fontMetrics();
429 m_height
= fm
.ascent();
431 // Skip uncounted elements
432 switch(style()->listStyleType()) {
439 m_markerWidth
= fm
.ascent();
446 CounterNode
*counter
= m_listItem
->m_counter
;
448 counter
= m_listItem
->getCounter("list-item", true);
449 counter
->setRenderer(this);
450 m_listItem
->m_counter
= counter
;
455 int value
= counter
->count();
456 if (counter
->isReset()) value
= counter
->value();
458 if (counter
->parent()) total
= counter
->parent()->total();
460 switch(style()->listStyleType())
464 m_item
.setNum ( value
);
466 case DECIMAL_LEADING_ZERO
: {
473 decimals
= qMax(decimals
, 2);
474 QString num
= QString::number(value
);
475 m_item
.fill('0',decimals
-num
.length());
480 m_item
= toArabicIndic( value
);
483 m_item
= toLao( value
);
487 m_item
= toPersianUrdu( value
);
490 m_item
= toThai( value
);
493 m_item
= toTibetan( value
);
497 m_item
= toRoman( value
, false );
500 m_item
= toRoman( value
, true );
503 m_item
= toHebrew( value
);
506 m_item
= toArmenian( value
);
509 m_item
= toGeorgian( value
);
514 m_item
= toLowerLatin( value
);
518 m_item
= toUpperLatin( value
);
521 m_item
= toLowerGreek( value
);
524 m_item
= toUpperGreek( value
);
527 m_item
= toHiragana( value
);
530 m_item
= toHiraganaIroha( value
);
533 m_item
= toKatakana( value
);
536 m_item
= toKatakanaIroha( value
);
539 case JAPANESE_FORMAL
:
540 m_item
= toJapaneseFormal( value
);
542 case JAPANESE_INFORMAL
:
543 m_item
= toJapaneseInformal( value
);
545 case SIMP_CHINESE_FORMAL
:
546 m_item
= toSimpChineseFormal( value
);
548 case SIMP_CHINESE_INFORMAL
:
549 m_item
= toSimpChineseInformal( value
);
551 case TRAD_CHINESE_FORMAL
:
552 m_item
= toTradChineseFormal( value
);
554 case CJK_IDEOGRAPHIC
:
555 // CSS 3 List says treat as trad-chinese-informal
556 case TRAD_CHINESE_INFORMAL
:
557 m_item
= toTradChineseInformal( value
);
565 m_markerWidth
= fm
.width(m_item
) + fm
.width(QLatin1String(". "));
569 if(listPositionInside())
570 m_width
= m_markerWidth
;
572 m_minWidth
= m_width
;
573 m_maxWidth
= m_width
;
578 short RenderListMarker::lineHeight(bool /*b*/) const
583 short RenderListMarker::baselinePosition(bool /*b*/) const
588 void RenderListMarker::calcWidth()
590 RenderBox::calcWidth();
594 int CounterListItem::recount() const
596 static_cast<RenderListItem*>(m_renderer)->m_marker->setNeedsLayoutAndMinMaxRecalc();
599 void CounterListItem::setSelfDirty()