fix logic
[personal-kdelibs.git] / khtml / rendering / render_object.cpp
blobcee71e4a2afe4f44689f8248771e17935fa2dc73
1 /**
2 * This file is part of the html renderer for KDE.
4 * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org)
5 * (C) 1999 Antti Koivisto (koivisto@kde.org)
6 * (C) 2000-2003 Dirk Mueller (mueller@kde.org)
7 * (C) 2004-2008 Apple Computer, Inc.
8 * (C) 2006 Germain Garand <germain@ebooksfrance.org>
9 * (C) 2008-2009 Fredrik Höglund <fredrik@kde.org>
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Library General Public
13 * License as published by the Free Software Foundation; either
14 * version 2 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Library General Public License for more details.
21 * You should have received a copy of the GNU Library General Public License
22 * along with this library; see the file COPYING.LIB. If not, write to
23 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 * Boston, MA 02110-1301, USA.
28 #include "rendering/render_object.h"
29 #include "rendering/render_table.h"
30 #include "rendering/render_list.h"
31 #include "rendering/render_canvas.h"
32 #include "rendering/render_block.h"
33 #include "rendering/render_arena.h"
34 #include "rendering/render_layer.h"
35 #include "rendering/render_line.h"
36 #include "rendering/render_inline.h"
37 #include "rendering/render_text.h"
38 #include "rendering/render_replaced.h"
39 #include "rendering/render_generated.h"
40 #include "rendering/counter_tree.h"
42 #include "xml/dom_elementimpl.h"
43 #include "xml/dom_docimpl.h"
44 #include "xml/dom_position.h"
45 #include "dom/dom_doc.h"
46 #include "misc/htmlhashes.h"
47 #include "misc/loader.h"
48 #include "misc/borderarcstroker.h"
50 #include <kdebug.h>
51 #include <kglobal.h>
52 #include <QtGui/QPainter>
53 #include "khtmlview.h"
54 #include <khtml_part.h>
55 #include <QPaintEngine>
57 #include <assert.h>
58 using namespace DOM;
59 using namespace khtml;
61 #define RED_LUMINOSITY 30
62 #define GREEN_LUMINOSITY 59
63 #define BLUE_LUMINOSITY 11
64 #define INTENSITY_FACTOR 25
65 #define LIGHT_FACTOR 0
66 #define LUMINOSITY_FACTOR 75
68 #define MAX_COLOR 255
69 #define COLOR_DARK_THRESHOLD 51
70 #define COLOR_LIGHT_THRESHOLD 204
72 #define COLOR_LITE_BS_FACTOR 45
73 #define COLOR_LITE_TS_FACTOR 70
75 #define COLOR_DARK_BS_FACTOR 30
76 #define COLOR_DARK_TS_FACTOR 50
78 #define LIGHT_GRAY qRgb(192, 192, 192)
79 #define DARK_GRAY qRgb(96, 96, 96)
81 #ifndef NDEBUG
82 static void *baseOfRenderObjectBeingDeleted;
83 #endif
85 QCache<quint64, QPixmap> *RenderObject::s_dashedLineCache = 0;
87 void RenderObject::cleanup()
89 delete s_dashedLineCache;
90 s_dashedLineCache = 0;
93 //#define MASK_DEBUG
95 void* RenderObject::operator new(size_t sz, RenderArena* renderArena) throw()
97 return renderArena->allocate(sz);
100 void RenderObject::operator delete(void* ptr, size_t sz)
102 assert(baseOfRenderObjectBeingDeleted == ptr);
104 #ifdef KHTML_USE_ARENA_ALLOCATOR
105 // Stash size where detach can find it.
106 *(size_t *)ptr = sz;
107 #endif
110 RenderObject *RenderObject::createObject(DOM::NodeImpl* node, RenderStyle* style)
112 RenderObject *o = 0;
113 khtml::RenderArena* arena = node->document()->renderArena();
114 switch(style->display())
116 case NONE:
117 break;
118 case INLINE:
119 o = new (arena) RenderInline(node);
120 break;
121 case BLOCK:
122 o = new (arena) RenderBlock(node);
123 break;
124 case INLINE_BLOCK:
125 o = new (arena) RenderBlock(node);
126 break;
127 case LIST_ITEM:
128 o = new (arena) RenderListItem(node);
129 break;
130 case RUN_IN:
131 case COMPACT:
132 o = new (arena) RenderBlock(node);
133 break;
134 case TABLE:
135 case INLINE_TABLE:
136 style->setFlowAroundFloats(true);
137 o = new (arena) RenderTable(node);
138 break;
139 case TABLE_ROW_GROUP:
140 case TABLE_HEADER_GROUP:
141 case TABLE_FOOTER_GROUP:
142 o = new (arena) RenderTableSection(node);
143 break;
144 case TABLE_ROW:
145 o = new (arena) RenderTableRow(node);
146 break;
147 case TABLE_COLUMN_GROUP:
148 case TABLE_COLUMN:
149 o = new (arena) RenderTableCol(node);
150 break;
151 case TABLE_CELL:
152 o = new (arena) RenderTableCell(node);
153 break;
154 case TABLE_CAPTION:
155 o = new (arena) RenderBlock(node);
156 break;
158 return o;
162 RenderObject::RenderObject(DOM::NodeImpl* node)
163 : CachedObjectClient(),
164 m_style( 0 ),
165 m_node( node ),
166 m_parent( 0 ),
167 m_previous( 0 ),
168 m_next( 0 ),
169 m_verticalPosition( PositionUndefined ),
170 m_needsLayout( false ),
171 m_normalChildNeedsLayout( false ),
172 m_markedForRepaint( false ),
173 m_posChildNeedsLayout( false ),
174 m_minMaxKnown( false ),
175 m_floating( false ),
177 m_positioned( false ),
178 m_relPositioned( false ),
179 m_paintBackground( false ),
181 m_isAnonymous( node->isDocumentNode() ),
182 m_recalcMinMax( false ),
183 m_isText( false ),
184 m_inline( true ),
185 m_attached( false ),
187 m_replaced( false ),
188 m_mouseInside( false ),
189 m_hasFirstLine( false ),
190 m_isSelectionBorder( false ),
191 m_isRoot( false ),
192 m_afterPageBreak( false ),
193 m_needsPageClear( false ),
194 m_containsPageBreak( false ),
195 m_hasOverflowClip(false),
196 m_inPosObjectList(false),
197 m_doNotDelete(false)
199 assert( node );
200 if (node->document()->documentElement() == node) setIsRoot(true);
203 RenderObject::~RenderObject()
205 const BackgroundLayer* bgLayer = m_style->backgroundLayers();
206 while (bgLayer) {
207 if(bgLayer->backgroundImage())
208 bgLayer->backgroundImage()->deref(this);
209 bgLayer = bgLayer->next();
212 m_style->deref();
215 RenderObject* RenderObject::objectBelow() const
217 RenderObject* obj = firstChild();
218 if ( !obj ) {
219 obj = nextSibling();
220 if ( !obj )
222 obj = parent();
223 while (obj && !obj->nextSibling())
224 obj = obj->parent();
225 if (obj)
226 obj = obj->nextSibling();
229 return obj;
232 RenderObject* RenderObject::objectAbove() const
234 RenderObject* obj = previousSibling();
235 if ( !obj )
236 return parent();
238 RenderObject* last = obj->lastChild();
239 while ( last )
241 obj = last;
242 last = last->lastChild();
244 return obj;
247 bool RenderObject::isRoot() const
249 return !isAnonymous() &&
250 element()->document()->documentElement() == element();
253 bool RenderObject::isHR() const
255 return element() && element()->id() == ID_HR;
257 bool RenderObject::isWordBreak() const
259 return element() && element()->id() == ID_WBR;
261 bool RenderObject::isHTMLMarquee() const
263 return element() && element()->renderer() == this && element()->id() == ID_MARQUEE;
266 void RenderObject::addChild(RenderObject* , RenderObject *)
268 KHTMLAssert(0);
271 RenderObject* RenderObject::removeChildNode(RenderObject*)
273 KHTMLAssert(0);
274 return 0;
277 void RenderObject::removeChild(RenderObject*)
279 KHTMLAssert(0);
282 void RenderObject::appendChildNode(RenderObject*)
284 KHTMLAssert(0);
287 void RenderObject::insertChildNode(RenderObject*, RenderObject*)
289 KHTMLAssert(0);
292 RenderObject *RenderObject::nextRenderer() const
294 if (firstChild())
295 return firstChild();
296 else if (nextSibling())
297 return nextSibling();
298 else {
299 const RenderObject *r = this;
300 while (r && !r->nextSibling())
301 r = r->parent();
302 if (r)
303 return r->nextSibling();
305 return 0;
308 RenderObject *RenderObject::previousRenderer() const
310 if (previousSibling()) {
311 RenderObject *r = previousSibling();
312 while (r->lastChild())
313 r = r->lastChild();
314 return r;
316 else if (parent()) {
317 return parent();
319 else {
320 return 0;
324 bool RenderObject::isEditable() const
326 RenderText *textRenderer = 0;
327 if (isText()) {
328 textRenderer = static_cast<RenderText *>(const_cast<RenderObject *>(this));
331 return style()->visibility() == VISIBLE &&
332 element() && element()->isContentEditable() &&
333 ((isBlockFlow() && !firstChild()) ||
334 isReplaced() ||
335 isBR() ||
336 (textRenderer && textRenderer->firstTextBox()));
339 RenderObject *RenderObject::nextEditable() const
341 RenderObject *r = const_cast<RenderObject *>(this);
342 RenderObject *n = firstChild();
343 if (n) {
344 while (n) {
345 r = n;
346 n = n->firstChild();
348 if (r->isEditable())
349 return r;
350 else
351 return r->nextEditable();
353 n = r->nextSibling();
354 if (n) {
355 r = n;
356 while (n) {
357 r = n;
358 n = n->firstChild();
360 if (r->isEditable())
361 return r;
362 else
363 return r->nextEditable();
365 n = r->parent();
366 while (n) {
367 r = n;
368 n = r->nextSibling();
369 if (n) {
370 r = n;
371 n = r->firstChild();
372 while (n) {
373 r = n;
374 n = n->firstChild();
376 if (r->isEditable())
377 return r;
378 else
379 return r->nextEditable();
381 n = r->parent();
383 return 0;
386 RenderObject *RenderObject::previousEditable() const
388 RenderObject *r = const_cast<RenderObject *>(this);
389 RenderObject *n = firstChild();
390 if (n) {
391 while (n) {
392 r = n;
393 n = n->lastChild();
395 if (r->isEditable())
396 return r;
397 else
398 return r->previousEditable();
400 n = r->previousSibling();
401 if (n) {
402 r = n;
403 while (n) {
404 r = n;
405 n = n->lastChild();
407 if (r->isEditable())
408 return r;
409 else
410 return r->previousEditable();
412 n = r->parent();
413 while (n) {
414 r = n;
415 n = r->previousSibling();
416 if (n) {
417 r = n;
418 n = r->lastChild();
419 while (n) {
420 r = n;
421 n = n->lastChild();
423 if (r->isEditable())
424 return r;
425 else
426 return r->previousEditable();
428 n = r->parent();
430 return 0;
433 RenderObject *RenderObject::firstLeafChild() const
435 RenderObject *r = firstChild();
436 while (r) {
437 RenderObject *n = 0;
438 n = r->firstChild();
439 if (!n)
440 break;
441 r = n;
443 return r;
446 RenderObject *RenderObject::lastLeafChild() const
448 RenderObject *r = lastChild();
449 while (r) {
450 RenderObject *n = 0;
451 n = r->lastChild();
452 if (!n)
453 break;
454 r = n;
456 return r;
459 static void addLayers(RenderObject* obj, RenderLayer* parentLayer, RenderObject*& newObject,
460 RenderLayer*& beforeChild)
462 if (obj->layer()) {
463 if (!beforeChild && newObject) {
464 // We need to figure out the layer that follows newObject. We only do
465 // this the first time we find a child layer, and then we update the
466 // pointer values for newObject and beforeChild used by everyone else.
467 beforeChild = newObject->parent()->findNextLayer(parentLayer, newObject);
468 newObject = 0;
470 parentLayer->addChild(obj->layer(), beforeChild);
471 return;
474 for (RenderObject* curr = obj->firstChild(); curr; curr = curr->nextSibling())
475 addLayers(curr, parentLayer, newObject, beforeChild);
478 void RenderObject::addLayers(RenderLayer* parentLayer, RenderObject* newObject)
480 if (!parentLayer)
481 return;
483 RenderObject* object = newObject;
484 RenderLayer* beforeChild = 0;
485 ::addLayers(this, parentLayer, object, beforeChild);
488 void RenderObject::removeLayers(RenderLayer* parentLayer)
490 if (!parentLayer)
491 return;
493 if (layer()) {
494 parentLayer->removeChild(layer());
495 return;
498 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
499 curr->removeLayers(parentLayer);
502 void RenderObject::moveLayers(RenderLayer* oldParent, RenderLayer* newParent)
504 if (!newParent)
505 return;
507 if (layer()) {
508 if (oldParent)
509 oldParent->removeChild(layer());
510 newParent->addChild(layer());
511 return;
514 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
515 curr->moveLayers(oldParent, newParent);
518 RenderLayer* RenderObject::findNextLayer(RenderLayer* parentLayer, RenderObject* startPoint,
519 bool checkParent)
521 // Error check the parent layer passed in. If it's null, we can't find anything.
522 if (!parentLayer)
523 return 0;
525 // Step 1: If our layer is a child of the desired parent, then return our layer.
526 RenderLayer* ourLayer = layer();
527 if (ourLayer && ourLayer->parent() == parentLayer)
528 return ourLayer;
530 // Step 2: If we don't have a layer, or our layer is the desired parent, then descend
531 // into our siblings trying to find the next layer whose parent is the desired parent.
532 if (!ourLayer || ourLayer == parentLayer) {
533 for (RenderObject* curr = startPoint ? startPoint->nextSibling() : firstChild();
534 curr; curr = curr->nextSibling()) {
535 RenderLayer* nextLayer = curr->findNextLayer(parentLayer, 0, false);
536 if (nextLayer)
537 return nextLayer;
541 // Step 3: If our layer is the desired parent layer, then we're finished. We didn't
542 // find anything.
543 if (parentLayer == ourLayer)
544 return 0;
546 // Step 4: If |checkParent| is set, climb up to our parent and check its siblings that
547 // follow us to see if we can locate a layer.
548 if (checkParent && parent())
549 return parent()->findNextLayer(parentLayer, this, true);
551 return 0;
554 RenderLayer* RenderObject::enclosingLayer() const
556 const RenderObject* curr = this;
557 while (curr) {
558 RenderLayer *layer = curr->layer();
559 if (layer)
560 return layer;
561 curr = curr->parent();
563 return 0;
566 RenderLayer* RenderObject::enclosingStackingContext() const
568 RenderLayer* l = enclosingLayer();
569 while (l && !l->isStackingContext())
570 l = l->parent();
571 return l;
574 int RenderObject::offsetLeft() const
576 if (isBody())
577 return 0;
579 RenderObject* offsetPar = offsetParent();
580 int x, dummy;
581 if (!offsetPar || offsetPar->isBody()) {
582 absolutePosition(x, dummy);
583 return x;
586 x = xPos() -offsetPar->borderLeft();
587 if ( isPositioned() )
588 return x;
590 if (isRelPositioned()) {
591 int y = 0;
592 static_cast<const RenderBox*>(this)->relativePositionOffset(x, y);
595 for( RenderObject* curr = parent();
596 curr && curr != offsetPar;
597 curr = curr->parent() )
598 x += curr->xPos();
600 return x;
603 int RenderObject::offsetTop() const
605 if (isBody())
606 return 0;
608 RenderObject* offsetPar = offsetParent();
609 int y, dummy;
611 if (!offsetPar || offsetPar->isBody()) {
612 absolutePosition(dummy, y);
613 return y;
616 y = yPos() -offsetPar->borderTop();
617 if ( isPositioned() )
618 return y;
620 if (isRelPositioned()) {
621 int x = 0;
622 static_cast<const RenderBox*>(this)->relativePositionOffset(x, y);
624 for( RenderObject* curr = parent();
625 curr && curr != offsetPar;
626 curr = curr->parent() )
627 y += curr->yPos();
629 return y;
632 RenderObject* RenderObject::offsetParent() const
634 if (isBody() || style()->position() == PFIXED)
635 return 0;
637 // can't really use containing blocks here (#113280)
638 bool skipTables = isPositioned() || isRelPositioned();
639 bool strict = !style()->htmlHacks();
640 RenderObject* curr = parent();
641 while (curr && (!curr->element() ||
642 (!curr->isPositioned() && !curr->isRelPositioned() &&
643 !(strict && skipTables ? curr->isRoot() : curr->isBody())))) {
644 if (!skipTables && curr->element() && (curr->isTableCell() || curr->isTable()))
645 break;
646 curr = curr->parent();
648 return curr;
651 // IE extensions.
652 // clientWidth and clientHeight represent the interior of an object
653 short RenderObject::clientWidth() const
655 return width() - borderLeft() - borderRight() -
656 (layer() ? layer()->verticalScrollbarWidth() : 0);
659 int RenderObject::clientLeft() const
661 return borderLeft();
664 int RenderObject::clientTop() const
666 return borderTop();
669 int RenderObject::clientHeight() const
671 return height() - borderTop() - borderBottom() -
672 (layer() ? layer()->horizontalScrollbarHeight() : 0);
675 // scrollWidth/scrollHeight is the size including the overflow area
676 short RenderObject::scrollWidth() const
678 return (hasOverflowClip() && layer()) ? layer()->scrollWidth() : overflowWidth() - overflowLeft();
681 int RenderObject::scrollHeight() const
683 return (hasOverflowClip() && layer()) ? layer()->scrollHeight() : overflowHeight() - overflowTop();
686 void RenderObject::updatePixmap(const QRect& /*r*/, CachedImage* image)
688 #ifdef __GNUC__
689 #warning "FIXME: Check if complete!"
690 #endif
691 //repaint bg when it finished loading
692 if(image && parent() && style() && style()->backgroundLayers()->containsImage(image)) {
693 isBody() ? canvas()->repaint() : repaint();
697 void RenderObject::setNeedsLayout(bool b, bool markParents)
699 bool alreadyNeededLayout = m_needsLayout;
700 m_needsLayout = b;
701 if (b) {
702 if (!alreadyNeededLayout && markParents && m_parent) {
703 dirtyFormattingContext( false );
704 markContainingBlocksForLayout();
707 else {
708 m_posChildNeedsLayout = false;
709 m_normalChildNeedsLayout = false;
713 void RenderObject::setChildNeedsLayout(bool b, bool markParents)
715 bool alreadyNeededLayout = m_normalChildNeedsLayout;
716 m_normalChildNeedsLayout = b;
717 if (b) {
718 if (!alreadyNeededLayout && markParents)
719 markContainingBlocksForLayout();
721 else {
722 m_posChildNeedsLayout = false;
723 m_normalChildNeedsLayout = false;
727 void RenderObject::markContainingBlocksForLayout()
729 RenderObject *o = container();
730 RenderObject *last = this;
732 while (o) {
733 if (!last->isText() && (last->style()->position() == PFIXED || last->style()->position() == PABSOLUTE)) {
734 if (o->m_posChildNeedsLayout)
735 return;
736 o->m_posChildNeedsLayout = true;
738 else {
739 if (o->m_normalChildNeedsLayout)
740 return;
741 o->m_normalChildNeedsLayout = true;
744 last = o;
745 o = o->container();
748 last->scheduleRelayout();
751 RenderBlock *RenderObject::containingBlock() const
753 if(isTableCell())
754 return static_cast<RenderBlock*>( parent()->parent()->parent() );
755 if (isCanvas())
756 return const_cast<RenderBlock*>( static_cast<const RenderBlock*>(this) );
758 RenderObject *o = parent();
759 if(m_style->position() == PFIXED) {
760 while ( o && !o->isCanvas() )
761 o = o->parent();
763 else if(m_style->position() == PABSOLUTE) {
764 while (o &&
765 ( o->style()->position() == PSTATIC || ( o->isInline() && !o->isReplaced() ) ) && !o->isCanvas()) {
766 // for relpos inlines, return the nearest block - it will host the positioned objects list
767 if (o->isInline() && !o->isReplaced() && o->style()->position() == PRELATIVE)
768 return o->containingBlock();
769 o = o->parent();
771 } else {
772 while(o && ( ( o->isInline() && !o->isReplaced() ) || o->isTableRow() || o->isTableSection() ||
773 o->isTableCol() || o->isFrameSet() ||
774 o->isSVGContainer() || o->isSVGRoot() ) ) // for svg
776 o = o->parent();
778 // this is just to make sure we return a valid element.
779 // the case below should never happen...
780 if(!o || !o->isRenderBlock()) {
781 if(!isCanvas()) {
782 #ifndef NDEBUG
783 kDebug( 6040 ) << this << ": " << renderName() << "(RenderObject): No containingBlock!";
784 kDebug( 6040 ) << kBacktrace();
785 const RenderObject* p = this;
786 while (p->parent()) p = p->parent();
787 p->printTree();
788 #endif
790 return canvas(); // likely wrong, but better than a crash
793 return static_cast<RenderBlock*>( o );
796 short RenderObject::containingBlockWidth(RenderObject*) const
798 // ###
799 return containingBlock()->contentWidth();
802 int RenderObject::containingBlockHeight(RenderObject*) const
804 // ###
805 return containingBlock()->contentHeight();
808 bool RenderObject::sizesToMaxWidth() const
810 // Marquees in WinIE are like a mixture of blocks and inline-blocks. They size as though they're blocks,
811 // but they allow text to sit on the same line as the marquee.
812 if (isFloating() || isCompact() ||
813 (isInlineBlockOrInlineTable() && !isHTMLMarquee()) ||
814 (element() && (element()->id() == ID_BUTTON || element()->id() == ID_LEGEND)))
815 return true;
817 // Children of a horizontal marquee do not fill the container by default.
818 // FIXME: Need to deal with MAUTO value properly. It could be vertical.
819 if (parent()->style()->overflowX() == OMARQUEE) {
820 EMarqueeDirection dir = parent()->style()->marqueeDirection();
821 if (dir == MAUTO || dir == MFORWARD || dir == MBACKWARD || dir == MLEFT || dir == MRIGHT)
822 return true;
825 #ifdef APPLE_CHANGES // ### what the heck is a flexbox?
826 // Flexible horizontal boxes lay out children at their maxwidths. Also vertical boxes
827 // that don't stretch their kids lay out their children at their maxwidths.
828 if (parent()->isFlexibleBox() &&
829 (parent()->style()->boxOrient() == HORIZONTAL || parent()->style()->boxAlign() != BSTRETCH))
830 return true;
831 #endif
833 return false;
836 // from Mozilla's nsCSSColorUtils.cpp
837 static int brightness(int red, int green, int blue)
840 int intensity = (red + green + blue) / 3;
842 int luminosity =
843 ((RED_LUMINOSITY * red) / 100) +
844 ((GREEN_LUMINOSITY * green) / 100) +
845 ((BLUE_LUMINOSITY * blue) / 100);
847 return ((intensity * INTENSITY_FACTOR) +
848 (luminosity * LUMINOSITY_FACTOR)) / 100;
851 static void calc3DColor(QColor &color, bool darken)
853 int rb = color.red();
854 int gb = color.green();
855 int bb = color.blue();
856 int a = color.alpha();
858 int brightness_ = brightness(rb,gb,bb);
860 int f0, f1;
861 if (brightness_ < COLOR_DARK_THRESHOLD) {
862 f0 = COLOR_DARK_BS_FACTOR;
863 f1 = COLOR_DARK_TS_FACTOR;
864 } else if (brightness_ > COLOR_LIGHT_THRESHOLD) {
865 f0 = COLOR_LITE_BS_FACTOR;
866 f1 = COLOR_LITE_TS_FACTOR;
867 } else {
868 f0 = COLOR_DARK_BS_FACTOR +
869 (brightness_ *
870 (COLOR_LITE_BS_FACTOR - COLOR_DARK_BS_FACTOR) / MAX_COLOR);
871 f1 = COLOR_DARK_TS_FACTOR +
872 (brightness_ *
873 (COLOR_LITE_TS_FACTOR - COLOR_DARK_TS_FACTOR) / MAX_COLOR);
876 if (darken) {
877 int r = rb - (f0 * rb / 100);
878 int g = gb - (f0 * gb / 100);
879 int b = bb - (f0 * bb / 100);
880 if ((r == rb) && (g == gb) && (b == bb))
881 color = (color == Qt::black) ? QColor(DARK_GRAY) : QColor(Qt::black);
882 else
883 color.setRgb(r, g, b);
884 } else {
885 int r = qMin(rb + (f1 * (MAX_COLOR - rb) / 100), 255);
886 int g = qMin(gb + (f1 * (MAX_COLOR - gb) / 100), 255);
887 int b = qMin(bb + (f1 * (MAX_COLOR - bb) / 100), 255);
888 if ((r == rb) && (g == gb) && (b == bb))
889 color = (color == Qt::white) ? QColor(LIGHT_GRAY) : QColor(Qt::white);
890 else
891 color.setRgb(r, g, b);
893 color.setAlpha(a);
896 void RenderObject::drawBorder(QPainter *p, int x1, int y1, int x2, int y2,
897 BorderSide s, QColor c, const QColor& textcolor, EBorderStyle style,
898 int adjbw1, int adjbw2, bool invalidisInvert, qreal *nextDashOffset)
900 if(nextDashOffset && style != DOTTED && style != DASHED)
901 *nextDashOffset = 0;
903 if (p->hasClipping() && !p->clipRegion().boundingRect().intersects(QRect(x1, y1, x2 - x1, y2 - y1))) {
904 if (nextDashOffset && (style == DOTTED || style == DASHED))
905 *nextDashOffset += (s == BSTop || s == BSBottom) ? (x2 - x1) : (y2 - y1);
906 return;
909 int width = (s==BSTop||s==BSBottom?y2-y1:x2-x1);
911 if(style == DOUBLE && width < 3)
912 style = SOLID;
914 if(!c.isValid()) {
915 if(invalidisInvert)
917 // handle 'outline-color: invert'
918 if (p->paintEngine()->hasFeature(QPaintEngine::BlendModes)) {
919 p->setCompositionMode(QPainter::CompositionMode_Difference);
920 c = Qt::white;
921 } else {
922 // 'invert' is not supported on this platform (for instance XRender)
923 // CSS3 UI 8.4: If the UA does not support the 'invert' value then the initial value of
924 // the 'outline-color' property is the 'currentColor' [CSS3COLOR] keyword.
925 c = m_style->color();
928 else {
929 if(style == INSET || style == OUTSET || style == RIDGE || style ==
930 GROOVE)
931 c = Qt::white;
932 else
933 c = textcolor;
937 switch(style)
939 case BNATIVE:
940 case BNONE:
941 case BHIDDEN:
942 // should not happen
943 if(invalidisInvert && p->compositionMode() == QPainter::CompositionMode_Difference)
944 p->setCompositionMode(QPainter::CompositionMode_SourceOver);
946 return;
947 case DOTTED:
948 case DASHED:
950 if (width <= 0)
951 break;
953 //Figure out on/off spacing
954 int onLen = width;
955 int offLen = width;
957 if (style == DASHED)
959 if (width == 1)
961 onLen = 3;
962 offLen = 3;
964 else
966 onLen = width * 3;
967 offLen = width;
971 // Compute the offset for the dash pattern, taking the direction of
972 // the line into account. (The borders are drawn counter-clockwise)
973 QPoint offset(0, 0);
974 if (nextDashOffset) {
975 switch (s)
977 // The left border is drawn top to bottom
978 case BSLeft:
979 offset.ry() = -qRound(*nextDashOffset);
980 *nextDashOffset += (y2 - y1);
981 break;
983 // The bottom border is drawn left to right
984 case BSBottom:
985 offset.rx() = -qRound(*nextDashOffset);
986 *nextDashOffset += (x2 - x1);
987 break;
989 // The top border is drawn right to left
990 case BSTop:
991 offset.rx() = (x2 - x1) + offLen + qRound(*nextDashOffset);
992 *nextDashOffset += (x2 - x1);
993 break;
995 // The right border is drawn bottom to top
996 case BSRight:
997 offset.ry() = (y2 - y1) + offLen + qRound(*nextDashOffset);
998 *nextDashOffset += (y2 - y1);
999 break;
1002 offset.rx() = offset.x() % (onLen + offLen);
1003 offset.ry() = offset.y() % (onLen + offLen);
1006 if ((onLen + offLen) <= 32 && width < 0x7fff)
1008 if (!s_dashedLineCache)
1009 s_dashedLineCache = new QCache<quint64, QPixmap>(30);
1011 bool horizontal = (s == BSBottom || s == BSTop);
1012 quint64 key = int(horizontal) << 31 | (onLen & 0xff) << 23 | (offLen & 0xff) << 15 | (width & 0x7fff);
1013 key = key << 32 | c.rgba();
1015 QPixmap *tile = s_dashedLineCache->object(key);
1016 if (!tile)
1018 QPainterPath path;
1019 int size = (onLen + offLen) * (64 / (onLen + offLen));
1020 if (horizontal)
1022 tile = new QPixmap(size, width);
1023 tile->fill(Qt::transparent);
1024 for (int x = 0; x < tile->width(); x += onLen + offLen)
1025 path.addRect(x, 0, onLen, tile->height());
1027 else //Vertical
1029 tile = new QPixmap(width, size);
1030 tile->fill(Qt::transparent);
1031 for (int y = 0; y < tile->height(); y += onLen + offLen)
1032 path.addRect(0, y, tile->width(), onLen);
1034 QPainter p2(tile);
1035 p2.fillPath(path, c);
1036 p2.end();
1037 s_dashedLineCache->insert(key, tile);
1040 QRect r = QRect(x1, y1, x2 - x1, y2 - y1);
1041 if (p->hasClipping())
1042 r &= p->clipRegion().boundingRect();
1044 // Make sure we're drawing the pattern in the correct phase
1045 if (horizontal && r.left() > x1)
1046 offset.rx() += (x1 - r.left());
1047 else if (!horizontal && r.top() > y1)
1048 offset.ry() += (y1 - r.top());
1050 p->drawTiledPixmap(r, *tile, -offset);
1052 else
1054 const QRect bounding(x1, y1, x2 - x1, y2 - y1);
1055 QPainterPath path;
1056 if (s == BSBottom || s == BSTop) //Horizontal
1058 if (offset.x() > 0)
1059 offset.rx() -= onLen + offLen;
1060 for (int x = x1 + offset.x(); x < x2; x += onLen + offLen) {
1061 const QRect r(x, y1, qMin(onLen, (x2 - x)), width);
1062 path.addRect(r & bounding);
1065 else //Vertical
1067 if (offset.y() > 0)
1068 offset.ry() -= onLen + offLen;
1069 for (int y = y1 + offset.y(); y < y2; y += onLen + offLen) {
1070 const QRect r(x1, y, width, qMin(onLen, (y2 - y)));
1071 path.addRect(r & bounding);
1075 p->fillPath(path, c);
1077 break;
1079 case DOUBLE:
1081 int third = (width+1)/3;
1083 if (adjbw1 == 0 && adjbw2 == 0)
1085 p->setPen(Qt::NoPen);
1086 p->setBrush(c);
1087 switch(s)
1089 case BSTop:
1090 case BSBottom:
1091 p->drawRect(x1, y1 , x2-x1, third);
1092 p->drawRect(x1, y2-third, x2-x1, third);
1093 break;
1094 case BSLeft:
1095 p->drawRect(x1 , y1, third, y2-y1);
1096 p->drawRect(x2-third, y1, third, y2-y1);
1097 break;
1098 case BSRight:
1099 p->drawRect(x1 , y1, third, y2-y1);
1100 p->drawRect(x2-third, y1, third, y2-y1);
1101 break;
1104 else
1106 int adjbw1bigthird;
1107 if (adjbw1>0) adjbw1bigthird = adjbw1+1;
1108 else adjbw1bigthird = adjbw1 - 1;
1109 adjbw1bigthird /= 3;
1111 int adjbw2bigthird;
1112 if (adjbw2>0) adjbw2bigthird = adjbw2 + 1;
1113 else adjbw2bigthird = adjbw2 - 1;
1114 adjbw2bigthird /= 3;
1116 switch(s)
1118 case BSTop:
1119 drawBorder(p, x1+qMax((-adjbw1*2+1)/3,0), y1 , x2-qMax((-adjbw2*2+1)/3,0), y1 + third, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
1120 drawBorder(p, x1+qMax(( adjbw1*2+1)/3,0), y2 - third, x2-qMax(( adjbw2*2+1)/3,0), y2 , s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
1121 break;
1122 case BSLeft:
1123 drawBorder(p, x1 , y1+qMax((-adjbw1*2+1)/3,0), x1+third, y2-qMax((-adjbw2*2+1)/3,0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
1124 drawBorder(p, x2 - third, y1+qMax(( adjbw1*2+1)/3,0), x2 , y2-qMax(( adjbw2*2+1)/3,0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
1125 break;
1126 case BSBottom:
1127 drawBorder(p, x1+qMax(( adjbw1*2+1)/3,0), y1 , x2-qMax(( adjbw2*2+1)/3,0), y1+third, s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
1128 drawBorder(p, x1+qMax((-adjbw1*2+1)/3,0), y2-third, x2-qMax((-adjbw2*2+1)/3,0), y2 , s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
1129 break;
1130 case BSRight:
1131 drawBorder(p, x1 , y1+qMax(( adjbw1*2+1)/3,0), x1+third, y2-qMax(( adjbw2*2+1)/3,0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
1132 drawBorder(p, x2-third, y1+qMax((-adjbw1*2+1)/3,0), x2 , y2-qMax((-adjbw2*2+1)/3,0), s, c, textcolor, SOLID, adjbw1bigthird, adjbw2bigthird);
1133 break;
1134 default:
1135 break;
1138 break;
1140 case RIDGE:
1141 case GROOVE:
1143 EBorderStyle s1;
1144 EBorderStyle s2;
1145 if (style==GROOVE)
1147 s1 = INSET;
1148 s2 = OUTSET;
1150 else
1152 s1 = OUTSET;
1153 s2 = INSET;
1156 int adjbw1bighalf;
1157 int adjbw2bighalf;
1158 if (adjbw1>0) adjbw1bighalf=adjbw1+1;
1159 else adjbw1bighalf=adjbw1-1;
1160 adjbw1bighalf/=2;
1162 if (adjbw2>0) adjbw2bighalf=adjbw2+1;
1163 else adjbw2bighalf=adjbw2-1;
1164 adjbw2bighalf/=2;
1166 switch (s)
1168 case BSTop:
1169 drawBorder(p, x1+qMax(-adjbw1 ,0)/2, y1 , x2-qMax(-adjbw2,0)/2, (y1+y2+1)/2, s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf);
1170 drawBorder(p, x1+qMax( adjbw1+1,0)/2, (y1+y2+1)/2, x2-qMax( adjbw2+1,0)/2, y2 , s, c, textcolor, s2, adjbw1/2, adjbw2/2);
1171 break;
1172 case BSLeft:
1173 drawBorder(p, x1 , y1+qMax(-adjbw1 ,0)/2, (x1+x2+1)/2, y2-qMax(-adjbw2,0)/2, s, c, textcolor, s1, adjbw1bighalf, adjbw2bighalf);
1174 drawBorder(p, (x1+x2+1)/2, y1+qMax( adjbw1+1,0)/2, x2 , y2-qMax( adjbw2+1,0)/2, s, c, textcolor, s2, adjbw1/2, adjbw2/2);
1175 break;
1176 case BSBottom:
1177 drawBorder(p, x1+qMax( adjbw1 ,0)/2, y1 , x2-qMax( adjbw2,0)/2, (y1+y2+1)/2, s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf);
1178 drawBorder(p, x1+qMax(-adjbw1+1,0)/2, (y1+y2+1)/2, x2-qMax(-adjbw2+1,0)/2, y2 , s, c, textcolor, s1, adjbw1/2, adjbw2/2);
1179 break;
1180 case BSRight:
1181 drawBorder(p, x1 , y1+qMax( adjbw1 ,0)/2, (x1+x2+1)/2, y2-qMax( adjbw2,0)/2, s, c, textcolor, s2, adjbw1bighalf, adjbw2bighalf);
1182 drawBorder(p, (x1+x2+1)/2, y1+qMax(-adjbw1+1,0)/2, x2 , y2-qMax(-adjbw2+1,0)/2, s, c, textcolor, s1, adjbw1/2, adjbw2/2);
1183 break;
1185 break;
1187 case INSET:
1188 case OUTSET:
1189 calc3DColor(c, (style == OUTSET && (s == BSBottom || s == BSRight)) ||
1190 (style == INSET && ( s == BSTop || s == BSLeft ) ) );
1191 /* nobreak; */
1192 case SOLID:
1193 p->setPen(Qt::NoPen);
1194 p->setBrush(c);
1195 Q_ASSERT(x2>=x1);
1196 Q_ASSERT(y2>=y1);
1197 if (adjbw1==0 && adjbw2 == 0) {
1198 p->drawRect(x1,y1,x2-x1,y2-y1);
1199 return;
1201 QPolygon quad(4);
1202 switch(s) {
1203 case BSTop:
1204 quad.setPoints(4,
1205 x1+qMax(-adjbw1,0), y1,
1206 x1+qMax( adjbw1,0), y2,
1207 x2-qMax( adjbw2,0), y2,
1208 x2-qMax(-adjbw2,0), y1);
1209 break;
1210 case BSBottom:
1211 quad.setPoints(4,
1212 x1+qMax( adjbw1,0), y1,
1213 x1+qMax(-adjbw1,0), y2,
1214 x2-qMax(-adjbw2,0), y2,
1215 x2-qMax( adjbw2,0), y1);
1216 break;
1217 case BSLeft:
1218 quad.setPoints(4,
1219 x1, y1+qMax(-adjbw1,0),
1220 x1, y2-qMax(-adjbw2,0),
1221 x2, y2-qMax( adjbw2,0),
1222 x2, y1+qMax( adjbw1,0));
1223 break;
1224 case BSRight:
1225 quad.setPoints(4,
1226 x1, y1+qMax( adjbw1,0),
1227 x1, y2-qMax( adjbw2,0),
1228 x2, y2-qMax(-adjbw2,0),
1229 x2, y1+qMax(-adjbw1,0));
1230 break;
1232 p->drawConvexPolygon(quad);
1233 break;
1236 if(invalidisInvert && p->compositionMode() == QPainter::CompositionMode_Difference)
1237 p->setCompositionMode(QPainter::CompositionMode_SourceOver);
1240 void RenderObject::adjustBorderRadii(BorderRadii &tl, BorderRadii &tr, BorderRadii &bl, BorderRadii &br, int w, int h) const
1242 // CSS Backgrounds and Borders Module Level 3 (WD-css3-background-20080910), chapter 4.5:
1243 // " Corners do not overlap: When the sum of two adjacent corner radii exceeds the size of the border box,
1244 // UAs must reduce one or more of the radii. The algorithm for reducing radii is as follows:"
1245 // The sum of two adjacent radii may not be more than the width or height (whichever is relevant) of the box.
1246 // If any sum exceeds that value, all radii are reduced according to the following formula:"
1247 const int horS = qMax(tl.horizontal + tr.horizontal, bl.horizontal + br.horizontal);
1248 const int verS = qMax(tl.vertical + bl.vertical, tr.vertical + br.vertical);
1250 qreal f = 1.0;
1251 if (horS > 0)
1252 f = qMin(f, w / qreal(horS));
1253 if (verS > 0)
1254 f = qMin(f, h / qreal(verS));
1256 if (f < 1.0) {
1257 tl.horizontal *= f;
1258 tr.horizontal *= f;
1259 bl.horizontal *= f;
1260 br.horizontal *= f;
1261 tl.vertical *= f;
1262 tr.vertical *= f;
1263 bl.vertical *= f;
1264 br.vertical *= f;
1268 static QImage blendCornerImages(const QImage &image1, const QImage &image2)
1270 QImage mask(image1.size(), QImage::Format_ARGB32_Premultiplied);
1271 QImage composite = image1;
1272 QImage temp = image2;
1274 // Construct the mask image
1275 QConicalGradient gradient(mask.width() / 2, mask.height() / 2, 0);
1276 gradient.setColorAt(0.00, Qt::transparent);
1277 gradient.setColorAt(0.25, Qt::black);
1278 gradient.setColorAt(0.50, Qt::black);
1279 gradient.setColorAt(0.75, Qt::transparent);
1280 gradient.setColorAt(1.00, Qt::transparent);
1282 QBrush gradientBrush = gradient;
1284 if (mask.width() != mask.height()) {
1285 int min = qMin(mask.width(), mask.height());
1286 QTransform xform;
1287 xform.translate(mask.width() / 2, mask.height() / 2);
1288 xform.scale(min / mask.width(), min / mask.height());
1289 gradientBrush.setTransform(xform);
1292 QPainter p;
1293 p.begin(&mask);
1294 p.setCompositionMode(QPainter::CompositionMode_Source);
1295 p.fillRect(mask.rect(), gradientBrush);
1296 p.end();
1298 p.begin(&temp);
1299 p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
1300 p.drawImage(0, 0, mask);
1301 p.end();
1303 p.begin(&composite);
1304 p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
1305 p.drawImage(0, 0, mask);
1306 p.setCompositionMode(QPainter::CompositionMode_SourceOver);
1307 p.drawImage(0, 0, temp);
1308 p.end();
1310 return composite;
1313 static QBrush cornerGradient(int cx, int cy, const BorderRadii &radius, int angleStart, int angleSpan,
1314 const QColor &startColor, const QColor &finalColor)
1316 QConicalGradient g(0, 0, angleStart);
1317 g.setColorAt(0, startColor);
1318 g.setColorAt(angleSpan / 360.0, finalColor);
1320 QBrush brush(g);
1322 QTransform xform;
1323 xform.translate(cx, cy);
1325 if (radius.horizontal < radius.vertical)
1326 xform.scale(radius.horizontal / radius.vertical, 1);
1327 else if (radius.vertical < radius.horizontal)
1328 xform.scale(1, radius.vertical / radius.horizontal);
1330 brush.setTransform(xform);
1331 return brush;
1334 void RenderObject::drawBorderArc(QPainter *p, int x, int y, float horThickness, float vertThickness,
1335 const BorderRadii &radius, int angleStart, int angleSpan, const QBrush &brush,
1336 const QColor &textColor, EBorderStyle style, qreal *nextDashOffset) const
1338 QColor c = brush.color();
1339 if (!c.isValid()) {
1340 if (style == INSET || style == OUTSET || style == RIDGE || style == GROOVE)
1341 c = Qt::white;
1342 else
1343 c = textColor;
1346 QColor light = c;
1347 QColor dark = c;
1348 calc3DColor(light, false);
1349 calc3DColor(dark, true);
1351 if (style == DOUBLE && horThickness < 3 && vertThickness < 3)
1352 style = SOLID;
1354 if (nextDashOffset && style != DOTTED && style != DASHED)
1355 *nextDashOffset = 0;
1357 p->save();
1358 p->setRenderHint(QPainter::Antialiasing);
1360 switch (style)
1362 case BNATIVE:
1363 case BNONE:
1364 case BHIDDEN:
1366 // Should not happen
1367 break;
1370 case SOLID:
1372 const QRect outerRect = QRect(x - radius.horizontal, y - radius.vertical, radius.horizontal * 2, radius.vertical * 2);
1373 const QRect innerRect = outerRect.adjusted(horThickness, vertThickness, -horThickness, -vertThickness);
1374 QPainterPath path;
1375 path.arcMoveTo(outerRect, angleStart);
1376 path.arcTo(outerRect, angleStart, angleSpan);
1377 if (innerRect.isValid())
1378 path.arcTo(innerRect, angleStart + angleSpan, -angleSpan);
1379 else
1380 path.lineTo(x, y);
1381 path.closeSubpath();
1382 p->fillPath(path, brush);
1383 break;
1386 case DOUBLE:
1388 const qreal hw = (horThickness + 1) / 3;
1389 const qreal vw = (vertThickness + 1) / 3;
1391 BorderRadii br;
1392 br.horizontal = radius.horizontal - hw * 2 + 1;
1393 br.vertical = radius.vertical - vw * 2 + 1;
1395 drawBorderArc(p, x, y, hw, vw, radius, angleStart, angleSpan, brush, textColor, SOLID);
1396 drawBorderArc(p, x, y, hw, vw, br, angleStart, angleSpan, brush, textColor, SOLID);
1397 break;
1400 case INSET:
1401 case OUTSET:
1403 QImage image1(radius.horizontal * 2, radius.vertical * 2, QImage::Format_ARGB32_Premultiplied);
1404 image1.fill(0);
1406 QImage image2 = image1;
1408 const QColor c1 = style == OUTSET ? dark : light;
1409 const QColor c2 = style == OUTSET ? light : dark;
1411 QPainter p2;
1412 p2.begin(&image1);
1413 drawBorderArc(&p2, radius.horizontal, radius.vertical, horThickness, vertThickness,
1414 radius, angleStart, angleSpan, c1, textColor, SOLID);
1415 p2.end();
1417 p2.begin(&image2);
1418 drawBorderArc(&p2, radius.horizontal, radius.vertical, horThickness, vertThickness,
1419 radius, angleStart, angleSpan, c2, textColor, SOLID);
1420 p2.end();
1422 p->drawImage(x - radius.horizontal, y - radius.vertical, blendCornerImages(image1, image2));
1423 break;
1426 case RIDGE:
1427 case GROOVE:
1429 QImage image1(radius.horizontal * 2, radius.vertical * 2, QImage::Format_ARGB32_Premultiplied);
1430 image1.fill(0);
1432 QImage image2 = image1;
1434 const QColor c1 = style == RIDGE ? dark : light;
1435 const QColor c2 = style == RIDGE ? light : dark;
1437 const qreal hw = horThickness / 2;
1438 const qreal vw = vertThickness / 2;
1439 int cx = radius.horizontal;
1440 int cy = radius.vertical;
1442 BorderRadii innerRadius;
1443 innerRadius.horizontal = radius.horizontal - hw;
1444 innerRadius.vertical = radius.vertical - vw;
1446 QPainter p2;
1447 p2.begin(&image1);
1448 drawBorderArc(&p2, cx, cy, hw, vw, radius, angleStart, angleSpan, c1, textColor, SOLID);
1449 drawBorderArc(&p2, cx, cy, hw, vw, innerRadius, angleStart, angleSpan, c2, textColor, SOLID);
1450 p2.end();
1452 p2.begin(&image2);
1453 drawBorderArc(&p2, cx, cy, hw, vw, radius, angleStart, angleSpan, c2, textColor, SOLID);
1454 drawBorderArc(&p2, cx, cy, hw, vw, innerRadius, angleStart, angleSpan, c1, textColor, SOLID);
1455 p2.end();
1457 p->drawImage(x - radius.horizontal, y - radius.vertical, blendCornerImages(image1, image2));
1458 break;
1461 case DOTTED:
1462 case DASHED:
1464 const QRectF rect = QRectF(x - radius.horizontal, y - radius.vertical, radius.horizontal * 2, radius.vertical * 2);
1465 int width;
1467 // Figure out which border we're starting from
1468 angleStart = angleStart % 360;
1469 if (angleStart < 0)
1470 angleStart += 360;
1472 if ((angleStart > 45 && angleStart <= 135) || (angleStart > 225 && angleStart <= 315))
1473 width = vertThickness;
1474 else
1475 width = horThickness;
1477 int onLen = width;
1478 int offLen = width;
1480 if (style == DASHED)
1482 if (width == 1)
1484 onLen = 3;
1485 offLen = 3;
1487 else
1489 onLen = width * 3;
1490 offLen = width;
1494 BorderArcStroker stroker;
1495 stroker.setArc(rect, angleStart, angleSpan);
1496 stroker.setPenWidth(horThickness, vertThickness);
1497 stroker.setDashPattern(onLen, offLen);
1498 stroker.setDashOffset(*nextDashOffset);
1500 const QPainterPath path = stroker.createStroke(nextDashOffset);
1501 p->fillPath(path, brush);
1505 p->restore();
1508 void RenderObject::paintBorder(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle* style, bool begin, bool end)
1510 const QColor& tc = style->borderTopColor();
1511 const QColor& bc = style->borderBottomColor();
1512 const QColor& lc = style->borderLeftColor();
1513 const QColor& rc = style->borderRightColor();
1515 bool tt = style->borderTopIsTransparent();
1516 bool bt = style->borderBottomIsTransparent();
1517 bool rt = style->borderRightIsTransparent();
1518 bool lt = style->borderLeftIsTransparent();
1520 EBorderStyle ts = style->borderTopStyle();
1521 EBorderStyle bs = style->borderBottomStyle();
1522 EBorderStyle ls = style->borderLeftStyle();
1523 EBorderStyle rs = style->borderRightStyle();
1525 bool render_t = ts > BHIDDEN && !tt;
1526 bool render_l = ls > BHIDDEN && begin && !lt;
1527 bool render_r = rs > BHIDDEN && end && !rt;
1528 bool render_b = bs > BHIDDEN && !bt;
1530 BorderRadii topLeft = style->borderTopLeftRadius();
1531 BorderRadii topRight = style->borderTopRightRadius();
1532 BorderRadii bottomLeft = style->borderBottomLeftRadius();
1533 BorderRadii bottomRight = style->borderBottomRightRadius();
1535 if (style->hasBorderRadius()) {
1536 // Adjust the border radii so they don't overlap when taking the size of the box
1537 // into account.
1538 adjustBorderRadii(topLeft, topRight, bottomLeft, bottomRight, w, h);
1541 bool upperLeftBorderStylesMatch = render_l && (ts == ls) && (tc == lc);
1542 bool upperRightBorderStylesMatch = render_r && (ts == rs) && (tc == rc);
1543 bool lowerLeftBorderStylesMatch = render_l && (bs == ls) && (bc == lc);
1544 bool lowerRightBorderStylesMatch = render_r && (bs == rs) && (bc == rc);
1546 // We do a gradient transition for dotted, dashed, solid and double lines
1547 // when the styles match but the colors differ.
1548 bool upperLeftGradient = render_t && render_l && ts == ls && tc != lc && ts > OUTSET;
1549 bool upperRightGradient = render_t && render_r && ts == rs && tc != rc && ts > OUTSET;
1550 bool lowerLeftGradient = render_b && render_l && bs == ls && bc != lc && bs > OUTSET;
1551 bool lowerRightGradient = render_b && render_r && bs == rs && bc != rc && bs > OUTSET;
1553 qreal nextDashOffset = 0;
1555 // Draw the borders counter-clockwise starting with the upper right corner
1556 if(render_t) {
1557 bool ignore_left = (topLeft.horizontal > 0) ||
1558 ((tc == lc) && (tt == lt) &&
1559 (ts >= OUTSET) &&
1560 (ls == DOTTED || ls == DASHED || ls == SOLID || ls == OUTSET));
1562 bool ignore_right = (topRight.horizontal > 0) ||
1563 ((tc == rc) && (tt == rt) &&
1564 (ts >= OUTSET) &&
1565 (rs == DOTTED || rs == DASHED || rs == SOLID || rs == INSET));
1567 int x = _tx + topLeft.horizontal;
1568 int x2 = _tx + w - topRight.horizontal;
1570 if (topRight.hasBorderRadius()) {
1571 int x = _tx + w - topRight.horizontal;
1572 int y = _ty + topRight.vertical;
1573 int startAngle, span;
1575 if (upperRightBorderStylesMatch || upperRightGradient) {
1576 startAngle = 0;
1577 span = 90;
1578 } else {
1579 startAngle = 45;
1580 span = 45;
1583 const QBrush brush = upperRightGradient ?
1584 cornerGradient(x, y, topRight, startAngle, span, rc, tc) : tc;
1586 // Draw the upper right arc
1587 drawBorderArc(p, x, y, style->borderRightWidth(), style->borderTopWidth(),
1588 topRight, startAngle, span, brush, style->color(), ts, &nextDashOffset);
1591 drawBorder(p, x, _ty, x2, _ty + style->borderTopWidth(), BSTop, tc, style->color(), ts,
1592 ignore_left?0:style->borderLeftWidth(),
1593 ignore_right?0:style->borderRightWidth(), false, &nextDashOffset);
1595 if (topLeft.hasBorderRadius()) {
1596 int x = _tx + topLeft.horizontal;
1597 int y = _ty + topLeft.vertical;
1598 int startAngle = 90;
1599 int span = (upperLeftBorderStylesMatch || upperLeftGradient) ? 90 : 45;
1600 const QBrush brush = upperLeftGradient ?
1601 cornerGradient(x, y, topLeft, startAngle, span, tc, lc) : tc;
1603 // Draw the upper left arc
1604 drawBorderArc(p, x, y, style->borderLeftWidth(), style->borderTopWidth(),
1605 topLeft, startAngle, span, brush, style->color(), ts, &nextDashOffset);
1606 } else if (ls == DASHED || ls == DOTTED)
1607 nextDashOffset = 0; // Reset the offset to avoid partially overlapping dashes
1610 if(render_l)
1612 bool ignore_top = (topLeft.vertical > 0) ||
1613 ((tc == lc) && (tt == lt) &&
1614 (ls >= OUTSET) &&
1615 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET));
1617 bool ignore_bottom = (bottomLeft.vertical > 0) ||
1618 ((bc == lc) && (bt == lt) &&
1619 (ls >= OUTSET) &&
1620 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET));
1622 int y = _ty + topLeft.vertical;
1623 int y2 = _ty + h - bottomLeft.vertical;
1625 if (!upperLeftBorderStylesMatch && !upperLeftGradient && topLeft.hasBorderRadius()) {
1626 int x = _tx + topLeft.horizontal;
1627 int y = _ty + topLeft.vertical;
1628 int startAngle = 135;
1629 int span = 45;
1631 // Draw the upper left arc
1632 drawBorderArc(p, x, y, style->borderLeftWidth(), style->borderTopWidth(),
1633 topLeft, startAngle, span, lc, style->color(), ls, &nextDashOffset);
1636 drawBorder(p, _tx, y, _tx + style->borderLeftWidth(), y2, BSLeft, lc, style->color(), ls,
1637 ignore_top?0:style->borderTopWidth(),
1638 ignore_bottom?0:style->borderBottomWidth(), false, &nextDashOffset);
1640 if (!lowerLeftBorderStylesMatch && !lowerLeftGradient && bottomLeft.hasBorderRadius()) {
1641 int x = _tx + bottomLeft.horizontal;
1642 int y = _ty + h - bottomLeft.vertical;
1643 int startAngle = 180;
1644 int span = 45;
1646 // Draw the bottom left arc
1647 drawBorderArc(p, x, y, style->borderLeftWidth(), style->borderBottomWidth(),
1648 bottomLeft, startAngle, span, lc, style->color(), ls, &nextDashOffset);
1651 // Reset the offset to avoid partially overlapping dashes
1652 if (!bottomLeft.hasBorderRadius() && (bs == DASHED || bs == DOTTED))
1653 nextDashOffset = 0;
1656 if(render_b) {
1657 bool ignore_left = (bottomLeft.horizontal > 0) ||
1658 ((bc == lc) && (bt == lt) &&
1659 (bs >= OUTSET) &&
1660 (ls == DOTTED || ls == DASHED || ls == SOLID || ls == INSET));
1662 bool ignore_right = (bottomRight.horizontal > 0) ||
1663 ((bc == rc) && (bt == rt) &&
1664 (bs >= OUTSET) &&
1665 (rs == DOTTED || rs == DASHED || rs == SOLID || rs == OUTSET));
1667 int x = _tx + bottomLeft.horizontal;
1668 int x2 = _tx + w - bottomRight.horizontal;
1670 if (bottomLeft.hasBorderRadius()) {
1671 int x = _tx + bottomLeft.horizontal;
1672 int y = _ty + h - bottomLeft.vertical;
1673 int startAngle, span;
1675 if (lowerLeftBorderStylesMatch || lowerLeftGradient) {
1676 startAngle = 180;
1677 span = 90;
1678 } else {
1679 startAngle = 225;
1680 span = 45;
1683 const QBrush brush = lowerLeftGradient ?
1684 cornerGradient(x, y, bottomLeft, startAngle, span, lc, bc) : bc;
1686 // Draw the bottom left arc
1687 drawBorderArc(p, x, y, style->borderLeftWidth(), style->borderBottomWidth(),
1688 bottomLeft, startAngle, span, brush, style->color(), bs, &nextDashOffset);
1691 drawBorder(p, x, _ty + h - style->borderBottomWidth(), x2, _ty + h, BSBottom, bc, style->color(), bs,
1692 ignore_left?0:style->borderLeftWidth(),
1693 ignore_right?0:style->borderRightWidth(), false, &nextDashOffset);
1695 if (bottomRight.hasBorderRadius()) {
1696 int x = _tx + w - bottomRight.horizontal;
1697 int y = _ty + h - bottomRight.vertical;
1698 int startAngle = 270;
1699 int span = (lowerRightBorderStylesMatch || lowerRightGradient) ? 90 : 45;
1700 const QBrush brush = lowerRightGradient ?
1701 cornerGradient(x, y, bottomRight, startAngle, span, bc, rc) : bc;
1703 // Draw the bottom right arc
1704 drawBorderArc(p, x, y, style->borderRightWidth(), style->borderBottomWidth(),
1705 bottomRight, startAngle, span, brush, style->color(), bs, &nextDashOffset);
1707 else if (rs == DASHED || rs == DOTTED)
1708 nextDashOffset = 0; // Reset the offset to avoid partially overlapping dashes
1711 if(render_r)
1713 bool ignore_top = (topRight.vertical > 0) ||
1714 ((tc == rc) && (tt == rt) &&
1715 (rs >= DOTTED || rs == INSET) &&
1716 (ts == DOTTED || ts == DASHED || ts == SOLID || ts == OUTSET));
1718 bool ignore_bottom = (bottomRight.vertical > 0) ||
1719 ((bc == rc) && (bt == rt) &&
1720 (rs >= DOTTED || rs == INSET) &&
1721 (bs == DOTTED || bs == DASHED || bs == SOLID || bs == INSET));
1723 int y = _ty + topRight.vertical;
1724 int y2 = _ty + h - bottomRight.vertical;
1726 if (!lowerRightBorderStylesMatch && !lowerRightGradient && bottomRight.hasBorderRadius()) {
1727 int x = _tx + w - bottomRight.horizontal;
1728 int y = _ty + h - bottomRight.vertical;
1729 int startAngle = 315;
1730 int span = 45;
1732 // Draw the bottom right arc
1733 drawBorderArc(p, x, y, style->borderRightWidth(), style->borderBottomWidth(),
1734 bottomRight, startAngle, span, rc, style->color(), rs, &nextDashOffset);
1737 drawBorder(p, _tx + w - style->borderRightWidth(), y, _tx + w, y2, BSRight, rc, style->color(), rs,
1738 ignore_top?0:style->borderTopWidth(),
1739 ignore_bottom?0:style->borderBottomWidth(), false, &nextDashOffset);
1741 if (!upperRightBorderStylesMatch && !upperRightGradient && topRight.hasBorderRadius()) {
1742 int x = _tx + w - topRight.horizontal;
1743 int y = _ty + topRight.vertical;
1744 int startAngle = 0;
1745 int span = 45;
1747 // Draw the upper right arc
1748 drawBorderArc(p, x, y, style->borderRightWidth(), style->borderTopWidth(),
1749 topRight, startAngle, span, rc, style->color(), rs, &nextDashOffset);
1754 void RenderObject::paintOutline(QPainter *p, int _tx, int _ty, int w, int h, const RenderStyle* style)
1756 int ow = style->outlineWidth();
1757 if(!ow) return;
1759 const QColor& oc = style->outlineColor();
1760 EBorderStyle os = style->outlineStyle();
1761 int offset = style->outlineOffset();
1763 #ifdef APPLE_CHANGES
1764 if (style->outlineStyleIsAuto()) {
1765 p->initFocusRing(ow, offset, oc);
1766 addFocusRingRects(p, _tx, _ty);
1767 p->drawFocusRing();
1768 p->clearFocusRing();
1769 return;
1771 #endif
1773 _tx -= offset;
1774 _ty -= offset;
1775 w += 2*offset;
1776 h += 2*offset;
1778 drawBorder(p, _tx-ow, _ty-ow, _tx, _ty+h+ow, BSLeft,
1779 QColor(oc), style->color(),
1780 os, ow, ow, true);
1782 drawBorder(p, _tx-ow, _ty-ow, _tx+w+ow, _ty, BSTop,
1783 QColor(oc), style->color(),
1784 os, ow, ow, true);
1786 drawBorder(p, _tx+w, _ty-ow, _tx+w+ow, _ty+h+ow, BSRight,
1787 QColor(oc), style->color(),
1788 os, ow, ow, true);
1790 drawBorder(p, _tx-ow, _ty+h, _tx+w+ow, _ty+h+ow, BSBottom,
1791 QColor(oc), style->color(),
1792 os, ow, ow, true);
1796 void RenderObject::paint( PaintInfo&, int /*tx*/, int /*ty*/)
1800 void RenderObject::repaintRectangle(int x, int y, int w, int h, Priority p, bool f)
1802 if(parent()) parent()->repaintRectangle(x, y, w, h, p, f);
1805 #ifdef ENABLE_DUMP
1807 QString RenderObject::information() const
1809 QString str;
1810 int x; int y;
1811 absolutePosition(x,y);
1812 x += inlineXPos();
1813 y += inlineYPos();
1814 QTextStream ts( &str, QIODevice::WriteOnly );
1815 ts << renderName()
1816 << "(" << (style() ? style()->refCount() : 0) << ")"
1817 << ": " << (void*)this << " ";
1818 ts << "{" << x << " " << y << "} ";
1819 if (isInline()) ts << "il ";
1820 if (childrenInline()) ts << "ci ";
1821 if (isFloating()) ts << "fl ";
1822 if (isAnonymous()) ts << "an ";
1823 if (isRelPositioned()) ts << "rp ";
1824 if (isPositioned()) ts << "ps ";
1825 if (isReplaced()) ts << "rp ";
1826 if (needsLayout()) ts << "nl ";
1827 if (minMaxKnown()) ts << "mmk ";
1828 if (m_recalcMinMax) ts << "rmm ";
1829 if (mouseInside()) ts << "mi ";
1830 if (style() && style()->zIndex()) ts << "zI: " << style()->zIndex();
1831 if (style() && style()->hasAutoZIndex()) ts << "zI: auto ";
1832 if (element()) {
1833 if (element()->active()) ts << "act ";
1834 if (element()->hasAnchor()) ts << "anchor ";
1835 if (element()->focused()) ts << "focus ";
1836 ts << " <" << LocalName::fromId(localNamePart(element()->id())).toString().string() << ">";
1838 } else if (isPseudoAnonymous() && style() && style()->styleType() != RenderStyle::NOPSEUDO) {
1839 ts << " <" << LocalName::fromId(localNamePart(node()->id())).toString().string();
1840 QString pseudo;
1841 switch (style()->styleType()) {
1842 case RenderStyle::FIRST_LETTER:
1843 pseudo = ":first-letter"; break;
1844 case RenderStyle::BEFORE:
1845 pseudo = ":before"; break;
1846 case RenderStyle::AFTER:
1847 pseudo = ":after"; break;
1848 default:
1849 pseudo = ":pseudo-element";
1851 ts << pseudo;
1852 ts << ">";
1854 ts << " (" << xPos() << "," << yPos() << "," << width() << "," << height() << ")"
1855 << " [" << minWidth() << "-" << maxWidth() << "]"
1856 << " { mT: " << marginTop() << " qT: " << isTopMarginQuirk()
1857 << " mB: " << marginBottom() << " qB: " << isBottomMarginQuirk()
1858 << "}"
1859 << (isTableCell() ?
1860 ( QLatin1String(" [r=") +
1861 QString::number( static_cast<const RenderTableCell *>(this)->row() ) +
1862 QLatin1String(" c=") +
1863 QString::number( static_cast<const RenderTableCell *>(this)->col() ) +
1864 QLatin1String(" rs=") +
1865 QString::number( static_cast<const RenderTableCell *>(this)->rowSpan() ) +
1866 QLatin1String(" cs=") +
1867 QString::number( static_cast<const RenderTableCell *>(this)->colSpan() ) +
1868 QLatin1String("]") ) : QString() );
1869 if ( layer() )
1870 ts << " layer=" << layer();
1871 if ( continuation() )
1872 ts << " continuation=" << continuation();
1873 if (isText())
1874 ts << " \"" << QString::fromRawData(static_cast<const RenderText *>(this)->text(), qMin(static_cast<const RenderText *>(this)->length(), 10u)) << "\"";
1875 return str;
1878 void RenderObject::printTree(int indent) const
1880 QString ind;
1881 ind.fill(' ', indent);
1883 kDebug() << (ind + information());
1885 RenderObject *child = firstChild();
1886 while( child != 0 )
1888 child->printTree(indent+2);
1889 child = child->nextSibling();
1893 static QTextStream &operator<<(QTextStream &ts, const QRect &r)
1895 return ts << "at (" << r.x() << "," << r.y() << ") size " << r.width() << "x" << r.height();
1898 //A bit like getTagName, but handles XML, too.
1899 static QString lookupTagName(NodeImpl* node) {
1900 return LocalName::fromId(node->id()).toString().string();
1903 void RenderObject::dump(QTextStream &ts, const QString &ind) const
1905 if ( !layer() )
1906 ts << endl;
1908 ts << ind << renderName();
1910 if (style() && style()->zIndex()) {
1911 ts << " zI: " << style()->zIndex();
1914 if (element()) {
1915 QString tagName(lookupTagName(element()));
1916 if (!tagName.isEmpty()) {
1917 ts << " {" << tagName << "}";
1919 } else if (isPseudoAnonymous() && style() && style()->styleType() != RenderStyle::NOPSEUDO) {
1920 QString pseudo;
1921 QString tagName(lookupTagName(node()));
1922 switch (style()->styleType()) {
1923 case RenderStyle::FIRST_LETTER:
1924 pseudo = ":first-letter"; break;
1925 case RenderStyle::BEFORE:
1926 pseudo = ":before"; break;
1927 case RenderStyle::AFTER:
1928 pseudo = ":after"; break;
1929 default:
1930 pseudo = ":pseudo-element";
1932 ts << " {" << tagName << pseudo << "}";
1935 QRect r(xPos(), yPos(), width(), height());
1936 ts << " " << r;
1938 if ( parent() )
1939 ts << style()->createDiff( *parent()->style() );
1941 if (isAnonymous()) { ts << " anonymousBox"; }
1942 if (isFloating()) { ts << " floating"; }
1943 if (isPositioned()) { ts << " positioned"; }
1944 if (isRelPositioned()) { ts << " relPositioned"; }
1945 if (isText()) { ts << " text"; }
1946 if (isInline()) { ts << " inline"; }
1947 if (isReplaced()) { ts << " replaced"; }
1948 if (shouldPaintBackgroundOrBorder()) { ts << " paintBackground"; }
1949 if (needsLayout()) { ts << " needsLayout"; }
1950 if (minMaxKnown()) { ts << " minMaxKnown"; }
1951 if (hasFirstLine()) { ts << " hasFirstLine"; }
1952 if (afterPageBreak()) { ts << " afterPageBreak"; }
1954 #endif
1956 bool RenderObject::shouldSelect() const
1958 #if 0 // ### merge
1959 const RenderObject* curr = this;
1960 DOM::NodeImpl *node = 0;
1961 bool forcedOn = false;
1963 while (curr) {
1964 if (curr->style()->userSelect() == SELECT_TEXT)
1965 forcedOn = true;
1966 if (!forcedOn && curr->style()->userSelect() == SELECT_NONE)
1967 return false;
1969 if (!node)
1970 node = curr->element();
1971 curr = curr->parent();
1974 // somewhere up the render tree there must be an element!
1975 assert(node);
1977 return node->dispatchHTMLEvent(DOM::EventImpl::SELECTSTART_EVENT, true, true);
1978 #else
1979 return true;
1980 #endif
1983 void RenderObject::selectionStartEnd(int& spos, int& epos)
1985 if (parent())
1986 parent()->selectionStartEnd(spos, epos);
1989 void RenderObject::setStyle(RenderStyle *style)
1991 if (m_style == style)
1992 return;
1994 RenderStyle::Diff d = m_style ? m_style->diff( style ) : RenderStyle::Layout;
1995 //qDebug("m_style: %p new style, diff=%d", m_style, d);
1997 Priority pri = NormalPriority;
1998 if (m_style) {
1999 pri = HighPriority;
2000 if ( d >= RenderStyle::Visible && !isText() && m_parent &&
2001 ( d == RenderStyle::Position ||
2002 m_style->outlineWidth() > style->outlineWidth() ||
2003 (!m_style->hidesOverflow() && style->hidesOverflow()) ||
2004 ( m_style->hasClip() && !(m_style->clip() == style->clip()) ) ) ) {
2005 // schedule a repaint with the old style
2006 if (layer() && !isInlineFlow())
2007 layer()->repaint(pri);
2008 else
2009 repaint(pri);
2012 if ( ( isFloating() && m_style->floating() != style->floating() ) ||
2013 ( isPositioned() && m_style->position() != style->position() &&
2014 style->position() != PABSOLUTE && style->position() != PFIXED ) )
2015 removeFromObjectLists();
2017 if ( layer() ) {
2018 if ( ( m_style->hasAutoZIndex() != style->hasAutoZIndex() ||
2019 m_style->zIndex() != style->zIndex() ||
2020 m_style->visibility() != style->visibility() ) ) {
2021 layer()->stackingContext()->dirtyZOrderLists();
2022 layer()->dirtyZOrderLists();
2024 // keep layer hierarchy visibility bits up to date if visibility changes
2025 if (m_style->visibility() != style->visibility()) {
2026 RenderLayer* l = enclosingLayer();
2027 if (style->visibility() == VISIBLE && l)
2028 l->setHasVisibleContent(true);
2029 else if (l && l->hasVisibleContent() &&
2030 (this == l->renderer() || l->renderer()->style()->visibility() != VISIBLE))
2031 l->dirtyVisibleContentStatus();
2035 // reset style flags
2036 m_floating = false;
2037 m_positioned = false;
2038 m_relPositioned = false;
2039 m_paintBackground = false;
2040 m_hasOverflowClip = false;
2043 // only honor z-index for non-static objects and objects with opacity
2044 if ( style->position() == PSTATIC && style->opacity() == 1.0f ) {
2045 style->setHasAutoZIndex();
2047 // force establishment of a stacking context by transparent objects, as those define
2048 // the bounds of an atomically painted region.
2049 if (style->hasAutoZIndex() && (isRoot() || style->opacity() < 1.0f))
2050 style->setZIndex( 0 );
2052 if ( d > RenderStyle::Position &&
2053 (style->hasFixedBackgroundImage() != (m_style && m_style->hasFixedBackgroundImage())
2054 || (style->position() == PFIXED) != (m_style && (m_style->position() == PFIXED)))
2055 && canvas() && canvas()->view() ) {
2056 // some sort of fixed object is added or removed. Let's find out more and report to the canvas,
2057 // so that it does some bookkeeping and optimizes the view's background display mode accordingly.
2058 bool fixedBG = style->hasFixedBackgroundImage();
2059 bool oldFixedBG = m_style && m_style->hasFixedBackgroundImage();
2060 bool fixedPos = (style->position() == PFIXED);
2061 bool oldFixedPos = m_style && (m_style->position() == PFIXED);
2062 if (fixedBG != oldFixedBG) {
2063 if (fixedBG) {
2064 canvas()->addStaticObject(this);
2065 } else {
2066 canvas()->removeStaticObject(this);
2069 if (fixedPos != oldFixedPos) {
2070 if (fixedPos)
2071 canvas()->addStaticObject( this, true /*positioned*/ );
2072 else
2073 canvas()->removeStaticObject( this, true );
2077 RenderStyle *oldStyle = m_style;
2078 m_style = style;
2080 updateBackgroundImages(oldStyle);
2082 m_style->ref();
2084 if (oldStyle)
2085 oldStyle->deref();
2087 setShouldPaintBackgroundOrBorder(m_style->hasBorder() || m_style->hasBackground());
2089 m_hasFirstLine = (style->getPseudoStyle(RenderStyle::FIRST_LINE) != 0);
2090 if (m_parent) {
2091 if (d == RenderStyle::Position && !attemptDirectLayerTranslation())
2092 d = RenderStyle::Layout;
2094 if ( d > RenderStyle::Position) {
2095 // we must perform a full layout
2096 if (!isText() && d == RenderStyle::CbLayout) {
2097 dirtyFormattingContext( true );
2099 setNeedsLayoutAndMinMaxRecalc();
2100 } else if (!isText() && d >= RenderStyle::Visible) {
2101 // a repaint is enough
2102 if (layer()) {
2103 if (canvas() && canvas()->needsWidgetMasks()) {
2104 // update our widget masks
2105 RenderLayer *p, *d = 0;
2106 for (p=layer()->parent();p;p=p->parent())
2107 if (p->hasOverlaidWidgets()) d=p;
2108 if (d) // deepest
2109 d->updateWidgetMasks( canvas()->layer() );
2112 if (layer() && !isInlineFlow())
2113 layer()->repaint(pri);
2114 else
2115 repaint(pri);
2120 bool RenderObject::attemptDirectLayerTranslation()
2122 // When the difference between two successive styles is only 'Position'
2123 // we may attempt to save a layout by directly updating the object position.
2125 KHTMLAssert( m_style->position() != PSTATIC );
2126 if (!layer())
2127 return false;
2128 setInline(m_style->isDisplayInlineType());
2129 setPositioned(m_style->position() != PRELATIVE);
2130 setRelPositioned(m_style->position() == PRELATIVE);
2131 int oldXPos = xPos();
2132 int oldYPos = yPos();
2133 int oldWidth = width();
2134 int oldHeight = height();
2135 calcWidth();
2136 calcHeight();
2137 if (oldWidth != width() || oldHeight != height()) {
2138 // implicit size change or overconstrained dimensions:
2139 // we'll need a layout.
2140 setWidth(oldWidth);
2141 setHeight(oldHeight);
2142 // kDebug() << "Layer translation failed for " << information();
2143 return false;
2145 layer()->updateLayerPosition();
2146 if (m_style->position() != PFIXED) {
2147 bool needsDocSizeUpdate = true;
2148 RenderObject *cb = container();
2149 while (cb) {
2150 if (cb->hasOverflowClip() && cb->layer()) {
2151 cb->layer()->checkScrollbarsAfterLayout();
2152 needsDocSizeUpdate = false;
2153 break;
2155 cb = cb->container();
2157 if (needsDocSizeUpdate && canvas()) {
2158 bool posXOffset = (xPos()-oldXPos >= 0);
2159 bool posYOffset = (yPos()-oldYPos >= 0);
2160 canvas()->updateDocSizeAfterLayerTranslation(this, posXOffset, posYOffset);
2163 // success
2164 return true;
2167 void RenderObject::dirtyFormattingContext( bool checkContainer )
2169 if (m_markedForRepaint && !checkContainer)
2170 return;
2171 m_markedForRepaint = true;
2172 if (layer() && (style()->position() == PFIXED || style()->position() == PABSOLUTE))
2173 return;
2174 if (m_parent && (checkContainer || style()->width().isVariable() || style()->height().isVariable() ||
2175 !(isFloating() || flowAroundFloats() || isTableCell())))
2176 m_parent->dirtyFormattingContext(false);
2179 void RenderObject::repaintDuringLayout()
2181 if (canvas()->needsFullRepaint() || isText())
2182 return;
2183 if (layer() && !isInlineFlow()) {
2184 layer()->repaint( NormalPriority, true );
2185 } else {
2186 repaint();
2187 canvas()->deferredRepaint( this );
2191 void RenderObject::updateBackgroundImages(RenderStyle* oldStyle)
2193 // FIXME: This will be slow when a large number of images is used. Fix by using a dict.
2194 const BackgroundLayer* oldLayers = oldStyle ? oldStyle->backgroundLayers() : 0;
2195 const BackgroundLayer* newLayers = m_style ? m_style->backgroundLayers() : 0;
2196 for (const BackgroundLayer* currOld = oldLayers; currOld; currOld = currOld->next()) {
2197 if (currOld->backgroundImage() && (!newLayers || !newLayers->containsImage(currOld->backgroundImage())))
2198 currOld->backgroundImage()->deref(this);
2200 for (const BackgroundLayer* currNew = newLayers; currNew; currNew = currNew->next()) {
2201 if (currNew->backgroundImage() && (!oldLayers || !oldLayers->containsImage(currNew->backgroundImage())))
2202 currNew->backgroundImage()->ref(this);
2206 QRect RenderObject::viewRect() const
2208 return containingBlock()->viewRect();
2211 bool RenderObject::absolutePosition(int &xPos, int &yPos, bool f) const
2213 RenderObject* p = parent();
2214 if (p) {
2215 p->absolutePosition(xPos, yPos, f);
2216 if ( p->hasOverflowClip() )
2217 p->layer()->subtractScrollOffset( xPos, yPos );
2218 return true;
2220 else
2222 xPos = yPos = 0;
2223 return false;
2227 void RenderObject::caretPos(int /*offset*/, int /*flags*/, int &_x, int &_y, int &width, int &height) const
2229 _x = _y = height = -1;
2230 width = 1; // the caret has a default width of one pixel. If you want
2231 // to check for validity, only test the x-coordinate for >= 0.
2234 int RenderObject::paddingTop() const
2236 int w = 0;
2237 Length padding = m_style->paddingTop();
2238 if (padding.isPercent())
2239 w = containingBlock()->contentWidth();
2240 w = padding.minWidth(w);
2241 if ( isTableCell() && padding.isVariable() )
2242 w = static_cast<const RenderTableCell *>(this)->table()->cellPadding();
2243 return w;
2246 int RenderObject::paddingBottom() const
2248 int w = 0;
2249 Length padding = style()->paddingBottom();
2250 if (padding.isPercent())
2251 w = containingBlock()->contentWidth();
2252 w = padding.minWidth(w);
2253 if ( isTableCell() && padding.isVariable() )
2254 w = static_cast<const RenderTableCell *>(this)->table()->cellPadding();
2255 return w;
2258 int RenderObject::paddingLeft() const
2260 int w = 0;
2261 Length padding = style()->paddingLeft();
2262 if (padding.isPercent())
2263 w = containingBlock()->contentWidth();
2264 w = padding.minWidth(w);
2265 if ( isTableCell() && padding.isVariable() )
2266 w = static_cast<const RenderTableCell *>(this)->table()->cellPadding();
2267 return w;
2270 int RenderObject::paddingRight() const
2272 int w = 0;
2273 Length padding = style()->paddingRight();
2274 if (padding.isPercent())
2275 w = containingBlock()->contentWidth();
2276 w = padding.minWidth(w);
2277 if ( isTableCell() && padding.isVariable() )
2278 w = static_cast<const RenderTableCell *>(this)->table()->cellPadding();
2279 return w;
2282 RenderObject *RenderObject::container() const
2284 // This method is extremely similar to containingBlock(), but with a few notable
2285 // exceptions.
2286 // (1) It can be used on orphaned subtrees, i.e., it can be called safely even when
2287 // the object is not part of the primary document subtree yet.
2288 // (2) For normal flow elements, it just returns the parent.
2289 // (3) For absolute positioned elements, it will return a relative positioned inline.
2290 // containingBlock() simply skips relpositioned inlines and lets an enclosing block handle
2291 // the layout of the positioned object. This does mean that calcAbsoluteHorizontal and
2292 // calcAbsoluteVertical have to use container().
2293 EPosition pos = m_style->position();
2294 RenderObject *o = 0;
2295 if( pos == PFIXED ) {
2296 // container() can be called on an object that is not in the
2297 // tree yet. We don't call canvas() since it will assert if it
2298 // can't get back to the canvas. Instead we just walk as high up
2299 // as we can. If we're in the tree, we'll get the root. If we
2300 // aren't we'll get the root of our little subtree (most likely
2301 // we'll just return 0).
2302 o = parent();
2303 while ( o && o->parent() ) o = o->parent();
2305 else if ( pos == PABSOLUTE ) {
2306 // Same goes here. We technically just want our containing block, but
2307 // we may not have one if we're part of an uninstalled subtree. We'll
2308 // climb as high as we can though.
2309 o = parent();
2310 while (o && o->style()->position() == PSTATIC && !o->isCanvas())
2311 o = o->parent();
2313 else
2314 o = parent();
2315 return o;
2318 DOM::DocumentImpl* RenderObject::document() const
2320 return m_node->document();
2323 void RenderObject::removeFromObjectLists()
2325 // in destruction mode, don't care.
2326 if ( documentBeingDestroyed() ) return;
2328 if (isFloating()) {
2329 RenderBlock* outermostBlock = containingBlock();
2330 for (RenderBlock* p = outermostBlock; p && !p->isCanvas() && p->containsFloat(this);) {
2331 outermostBlock = p;
2332 if (p->isFloatingOrPositioned())
2333 break;
2334 p = p->containingBlock();
2337 if (outermostBlock)
2338 outermostBlock->markAllDescendantsWithFloatsForLayout(this);
2341 if (isPositioned()) {
2342 RenderObject *p;
2343 for (p = parent(); p; p = p->parent()) {
2344 if (p->isRenderBlock())
2345 static_cast<RenderBlock*>(p)->removePositionedObject(this);
2350 RenderArena* RenderObject::renderArena() const
2352 return m_node->document()->renderArena();
2355 void RenderObject::detach()
2357 detachCounters();
2358 remove();
2360 // make sure our DOM-node don't think we exist
2361 if ( node() && node()->renderer() == this)
2362 node()->setRenderer(0);
2364 // by default no refcounting
2365 arenaDelete(renderArena(), this);
2368 void RenderObject::arenaDelete(RenderArena *arena, void *base)
2370 #ifndef NDEBUG
2371 void *savedBase = baseOfRenderObjectBeingDeleted;
2372 baseOfRenderObjectBeingDeleted = base;
2373 #endif
2374 delete this;
2375 #ifndef NDEBUG
2376 baseOfRenderObjectBeingDeleted = savedBase;
2377 #endif
2379 // Recover the size left there for us by operator delete and free the memory.
2380 arena->free(*(size_t *)base, base);
2383 void RenderObject::arenaDelete(RenderArena *arena)
2385 // static_cast unfortunately doesn't work, since we multiple inherit
2386 // in eg. RenderWidget.
2387 arenaDelete(arena, dynamic_cast<void *>(this));
2390 Position RenderObject::positionForCoordinates(int /*x*/, int /*y*/)
2392 return Position(element(), caretMinOffset());
2395 bool RenderObject::isPointInsideSelection(int x, int y, const Selection &sel) const
2397 SelectionState selstate = selectionState();
2398 if (selstate == SelectionInside) return true;
2399 if (selstate == SelectionNone || !element()) return false;
2400 return element()->isPointInsideSelection(x, y, sel);
2403 #if 0
2404 FindSelectionResult RenderObject::checkSelectionPoint( int _x, int _y, int _tx, int _ty, DOM::NodeImpl*& node, int & offset, SelPointState &state )
2406 #if 0
2407 NodeInfo info(true, false);
2408 if ( nodeAtPoint( info, _x, _y, _tx, _ty ) && info.innerNode() )
2410 RenderObject* r = info.innerNode()->renderer();
2411 if ( r ) {
2412 if ( r == this ) {
2413 node = info.innerNode();
2414 offset = 0; // we have no text...
2415 return SelectionPointInside;
2417 else
2418 return r->checkSelectionPoint( _x, _y, _tx, _ty, node, offset, state );
2421 //kDebug(6030) << "nodeAtPoint Failed. Fallback - hmm, SelectionPointAfter";
2422 node = 0;
2423 offset = 0;
2424 return SelectionPointAfter;
2425 #endif
2426 int off = offset;
2427 DOM::NodeImpl* nod = node;
2429 for (RenderObject *child = firstChild(); child; child=child->nextSibling()) {
2430 // ignore empty text boxes, they produce totally bogus information
2431 // for caret navigation (LS)
2432 if (child->isText() && !static_cast<RenderText *>(child)->firstTextBox())
2433 continue;
2435 // kDebug(6040) << "iterating " << (child ? child->renderName() : "") << "@" << child << (child->isText() ? " contains: \"" + QString::fromRawData(static_cast<RenderText *>(child)->text(), qMin(static_cast<RenderText *>(child)->length(), 10u)) + "\"" : QString());
2436 // kDebug(6040) << "---------- checkSelectionPoint recursive -----------";
2437 khtml::FindSelectionResult pos = child->checkSelectionPoint(_x, _y, _tx+xPos(), _ty+yPos(), nod, off, state);
2438 // kDebug(6040) << "-------- end checkSelectionPoint recursive ---------";
2439 // kDebug(6030) << this << " child->findSelectionNode returned result=" << pos << " nod=" << nod << " off=" << off;
2440 switch(pos) {
2441 case SelectionPointBeforeInLine:
2442 case SelectionPointInside:
2443 //kDebug(6030) << "RenderObject::checkSelectionPoint " << this << " returning SelectionPointInside offset=" << offset;
2444 node = nod;
2445 offset = off;
2446 return SelectionPointInside;
2447 case SelectionPointBefore:
2448 //x,y is before this element -> stop here
2449 if ( state.m_lastNode ) {
2450 node = state.m_lastNode;
2451 offset = state.m_lastOffset;
2452 //kDebug(6030) << "RenderObject::checkSelectionPoint " << this << " before this child "
2453 // << node << "-> returning SelectionPointInside, offset=" << offset << endl;
2454 return SelectionPointInside;
2455 } else {
2456 node = nod;
2457 offset = off;
2458 //kDebug(6030) << "RenderObject::checkSelectionPoint " << this << " before us -> returning SelectionPointBefore " << node << "/" << offset;
2459 return SelectionPointBefore;
2461 break;
2462 case SelectionPointAfter:
2463 if (state.m_afterInLine) break;
2464 // fall through
2465 case SelectionPointAfterInLine:
2466 if (pos == SelectionPointAfterInLine) state.m_afterInLine = true;
2467 //kDebug(6030) << "RenderObject::checkSelectionPoint: selection after: " << nod << " offset: " << off << " afterInLine: " << state.m_afterInLine;
2468 state.m_lastNode = nod;
2469 state.m_lastOffset = off;
2470 // No "return" here, obviously. We must keep looking into the children.
2471 break;
2474 // If we are after the last child, return lastNode/lastOffset
2475 // But lastNode can be 0L if there is no child, for instance.
2476 if ( state.m_lastNode )
2478 node = state.m_lastNode;
2479 offset = state.m_lastOffset;
2481 //kDebug(6030) << "fallback - SelectionPointAfter node=" << node << " offset=" << offset;
2482 return SelectionPointAfter;
2484 #endif
2486 bool RenderObject::mouseInside() const
2488 if (!m_mouseInside && continuation())
2489 return continuation()->mouseInside();
2490 return m_mouseInside;
2493 bool RenderObject::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty, HitTestAction hitTestAction, bool inside)
2495 int tx = _tx + xPos();
2496 int ty = _ty + yPos();
2498 inside |= ( style()->visibility() != HIDDEN &&
2499 (_y >= ty) && (_y < ty + height()) && (_x >= tx) && (_x < tx + width())) || isRoot() || isBody();
2500 bool inOverflowRect = inside;
2501 if ( !inOverflowRect ) {
2502 int ol = overflowLeft();
2503 int ot = overflowTop();
2504 QRect overflowRect( tx+ol, ty+ot, overflowWidth()-ol, overflowHeight()-ot );
2505 inOverflowRect = overflowRect.contains( _x, _y );
2508 // ### table should have its own, more performant method
2509 if (hitTestAction != HitTestSelfOnly &&
2510 (( !isRenderBlock() ||
2511 !static_cast<RenderBlock*>( this )->isPointInScrollbar( _x, _y, _tx, _ty )) &&
2512 (inOverflowRect || isInline() || isRoot() || isCanvas() ||
2513 isTableRow() || isTableSection() || inside || mouseInside() ))) {
2514 if ( hitTestAction == HitTestChildrenOnly )
2515 inside = false;
2516 if ( hasOverflowClip() && layer() )
2517 layer()->subtractScrollOffset(tx, ty);
2518 for (RenderObject* child = lastChild(); child; child = child->previousSibling())
2519 if (!child->layer() && child->nodeAtPoint(info, _x, _y, tx, ty, HitTestAll))
2520 inside = true;
2523 if (inside)
2524 setInnerNode(info);
2526 return inside;
2530 void RenderObject::setInnerNode(NodeInfo& info)
2532 if (!info.innerNode() && !isInline() && continuation()) {
2533 // We are in the margins of block elements that are part of a continuation. In
2534 // this case we're actually still inside the enclosing inline element that was
2535 // split. Go ahead and set our inner node accordingly.
2536 info.setInnerNode(continuation()->element());
2537 if (!info.innerNonSharedNode())
2538 info.setInnerNonSharedNode(continuation()->element());
2541 if (!info.innerNode() && element())
2542 info.setInnerNode(element());
2544 if(!info.innerNonSharedNode() && element())
2545 info.setInnerNonSharedNode(element());
2549 short RenderObject::verticalPositionHint( bool firstLine ) const
2551 short vpos = m_verticalPosition;
2552 if ( m_verticalPosition == PositionUndefined || firstLine ) {
2553 vpos = getVerticalPosition( firstLine );
2554 if ( !firstLine )
2555 const_cast<RenderObject *>(this)->m_verticalPosition = vpos;
2557 return vpos;
2561 short RenderObject::getVerticalPosition( bool firstLine, RenderObject* ref ) const
2563 // vertical align for table cells has a different meaning
2564 int vpos = 0;
2565 if ( !isTableCell() && isInline() ) {
2566 EVerticalAlign va = style()->verticalAlign();
2567 if ( va == TOP ) {
2568 vpos = PositionTop;
2569 } else if ( va == BOTTOM ) {
2570 vpos = PositionBottom;
2571 } else {
2572 if (!ref) ref = parent();
2573 bool checkParent = ref->isInline() && !ref->isReplacedBlock() &&
2574 !( ref->style()->verticalAlign() == TOP || ref->style()->verticalAlign() == BOTTOM );
2575 vpos = checkParent ? ref->verticalPositionHint( firstLine ) : 0;
2576 // don't allow elements nested inside text-top to have a different valignment.
2577 if ( va == BASELINE )
2578 return vpos;
2579 else if ( va == LENGTH )
2580 return vpos - style()->verticalAlignLength().width( lineHeight( firstLine ) );
2582 const QFont &f = ref->font( firstLine );
2583 int fontsize = f.pixelSize();
2585 if ( va == SUB )
2586 vpos += fontsize/5 + 1;
2587 else if ( va == SUPER )
2588 vpos -= fontsize/3 + 1;
2589 else if ( va == TEXT_TOP ) {
2590 vpos += baselinePosition( firstLine ) - (QFontMetrics(f).ascent() + QFontMetrics(f).leading()/2);
2591 } else if ( va == MIDDLE ) {
2592 QRect b = QFontMetrics(f).boundingRect('x');
2593 vpos += -b.height()/2 - lineHeight( firstLine )/2 + baselinePosition( firstLine );
2594 } else if ( va == TEXT_BOTTOM ) {
2595 vpos += QFontMetrics(f).descent() + QFontMetrics(f).leading()/2;
2596 if ( !isReplaced() )
2597 vpos -= fontMetrics(firstLine).descent();
2598 } else if ( va == BASELINE_MIDDLE )
2599 vpos += - lineHeight( firstLine )/2 + baselinePosition( firstLine );
2602 return vpos;
2605 short RenderObject::lineHeight( bool firstLine ) const
2607 // Inline blocks are replaced elements. Otherwise, just pass off to
2608 // the base class. If we're being queried as though we're the root line
2609 // box, then the fact that we're an inline-block is irrelevant, and we behave
2610 // just like a block.
2612 if (isReplaced() && (!isInlineBlockOrInlineTable() || !needsLayout()))
2613 return height()+marginTop()+marginBottom();
2615 Length lh;
2616 if( firstLine && hasFirstLine() ) {
2617 RenderStyle *pseudoStyle = style()->getPseudoStyle(RenderStyle::FIRST_LINE);
2618 if ( pseudoStyle )
2619 lh = pseudoStyle->lineHeight();
2621 else
2622 lh = style()->lineHeight();
2624 // its "unset", choose nice default
2625 if ( lh.value() < 0 )
2626 return style()->fontMetrics().lineSpacing();
2628 if ( lh.isPercent() )
2629 return lh.minWidth( style()->font().pixelSize() );
2631 // its fixed
2632 return lh.value();
2635 short RenderObject::baselinePosition( bool firstLine ) const
2637 // If we're an inline-block and need layout, it means our replaced boundaries
2638 // are not yet fully established, so we behave just like a block.
2639 if (isReplaced() && (!isInlineBlockOrInlineTable() || !needsLayout()))
2640 return height()+marginTop()+marginBottom();
2642 const QFontMetrics &fm = fontMetrics( firstLine );
2643 return fm.ascent() + ( lineHeight( firstLine) - fm.height() ) / 2;
2646 void RenderObject::invalidateVerticalPosition()
2648 m_verticalPosition = PositionUndefined;
2651 void RenderObject::recalcMinMaxWidths()
2653 KHTMLAssert( m_recalcMinMax );
2655 #ifdef DEBUG_LAYOUT
2656 kDebug( 6040 ) << renderName() << " recalcMinMaxWidths() this=" << this;
2657 #endif
2659 RenderObject *child = firstChild();
2660 int cmin=0;
2661 int cmax=0;
2663 while( child ) {
2664 bool test = false;
2665 if ( ( m_minMaxKnown && child->m_recalcMinMax ) || !child->m_minMaxKnown ) {
2666 cmin = child->minWidth();
2667 cmax = child->maxWidth();
2668 test = true;
2670 if ( child->m_recalcMinMax )
2671 child->recalcMinMaxWidths();
2672 if ( !child->m_minMaxKnown )
2673 child->calcMinMaxWidth();
2674 if ( m_minMaxKnown && test && (cmin != child->minWidth() || cmax != child->maxWidth()) )
2675 m_minMaxKnown = false;
2676 child = child->nextSibling();
2679 // we need to recalculate, if the contains inline children, as the change could have
2680 // happened somewhere deep inside the child tree
2681 if ( ( !isInline() || isReplacedBlock() ) && childrenInline() )
2682 m_minMaxKnown = false;
2684 if ( !m_minMaxKnown )
2685 calcMinMaxWidth();
2686 m_recalcMinMax = false;
2689 void RenderObject::scheduleRelayout(RenderObject *clippedObj)
2691 if (!isCanvas()) return;
2692 KHTMLView *view = static_cast<RenderCanvas *>(this)->view();
2693 if ( view )
2694 view->scheduleRelayout(clippedObj);
2697 InlineBox* RenderObject::createInlineBox(bool /*makePlaceHolderBox*/, bool /*isRootLineBox*/)
2699 KHTMLAssert(false);
2700 return 0;
2703 void RenderObject::getTextDecorationColors(int decorations, QColor& underline, QColor& overline,
2704 QColor& linethrough, bool quirksMode)
2706 RenderObject* curr = this;
2707 do {
2708 RenderStyle *st = curr->style();
2709 int currDecs = st->textDecoration();
2710 if (currDecs) {
2711 if (currDecs & UNDERLINE) {
2712 decorations &= ~UNDERLINE;
2713 underline = st->color();
2715 if (currDecs & OVERLINE) {
2716 decorations &= ~OVERLINE;
2717 overline = st->color();
2719 if (currDecs & LINE_THROUGH) {
2720 decorations &= ~LINE_THROUGH;
2721 linethrough = st->color();
2724 curr = curr->parent();
2725 if (curr && curr->isRenderBlock() && curr->continuation())
2726 curr = curr->continuation();
2727 } while (curr && decorations && (!quirksMode || !curr->element() ||
2728 (curr->element()->id() != ID_A && curr->element()->id() != ID_FONT)));
2730 // If we bailed out, use the element we bailed out at (typically a <font> or <a> element).
2731 if (decorations && curr) {
2732 RenderStyle *st = curr->style();
2733 if (decorations & UNDERLINE)
2734 underline = st->color();
2735 if (decorations & OVERLINE)
2736 overline = st->color();
2737 if (decorations & LINE_THROUGH)
2738 linethrough = st->color();
2742 int RenderObject::maximalOutlineSize(PaintAction p) const
2744 if (p != PaintActionOutline)
2745 return 0;
2746 return static_cast<RenderCanvas*>(document()->renderer())->maximalOutlineSize();
2749 void RenderObject::collectBorders(QList<CollapsedBorderValue>& borderStyles)
2751 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling())
2752 curr->collectBorders(borderStyles);
2755 bool RenderObject::flowAroundFloats() const
2757 return isReplaced() || hasOverflowClip() || style()->flowAroundFloats();
2760 bool RenderObject::usesLineWidth() const
2762 // 1. All auto-width objects that avoid floats should always use lineWidth
2763 // 2. For objects with a specified width, we match WinIE's behavior:
2764 // (a) tables use contentWidth
2765 // (b) <hr>s use lineWidth
2766 // (c) all other objects use lineWidth in quirks mode and contentWidth in strict mode.
2767 return (flowAroundFloats() && (style()->width().isVariable() || isHR() || (style()->htmlHacks() && !isTable())));
2770 long RenderObject::caretMinOffset() const
2772 return 0;
2775 long RenderObject::caretMaxOffset() const
2777 return 0;
2780 unsigned long RenderObject::caretMaxRenderedOffset() const
2782 return 0;
2785 InlineBox *RenderObject::inlineBox(long /*offset*/)
2787 if (isBox())
2788 return static_cast<RenderBox*>(this)->placeHolderBox();
2789 return 0;
2792 bool RenderObject::hasCounter(const QString& counter) const
2794 if (style() && (!isText() || isCounter())) {
2795 if (lookupCounter(counter)) return true;
2796 if (style()->hasCounterReset(counter)) {
2797 return true;
2799 else if (style()->hasCounterIncrement(counter)) {
2800 return true;
2803 if (counter == "list-item") {
2804 if (isListItem()) return true;
2805 if (element() && (
2806 element()->id() == ID_OL ||
2807 element()->id() == ID_UL ||
2808 element()->id() == ID_MENU ||
2809 element()->id() == ID_DIR))
2810 return true;
2811 } else
2812 if (counter == "-khtml-quotes" && isQuote()) {
2813 return (static_cast<const RenderQuote*>(this)->quoteCount() != 0);
2815 return false;
2818 CounterNode* RenderObject::getCounter(const QString& counter, bool view, bool counters)
2820 // kDebug( 6040 ) << renderName() << " getCounter(" << counter << ")";
2822 if (!style()) return 0;
2824 if (isText() && !isCounter()) return 0;
2826 CounterNode *i = lookupCounter(counter);
2827 if (i) return i;
2828 int val = 0;
2830 if (style()->hasCounterReset(counter) || isRoot()) {
2831 i = new CounterReset(this);
2832 val = style()->counterReset(counter);
2833 if (style()->hasCounterIncrement(counter)) {
2834 val += style()->counterIncrement(counter);
2836 // kDebug( 6040 ) << renderName() << " counter-reset: " << counter << " " << val;
2838 else
2839 if (style()->hasCounterIncrement(counter)) {
2840 i = new CounterNode(this);
2841 val = style()->counterIncrement(counter);
2842 // kDebug( 6040 ) << renderName() << " counter-increment: " << counter << " " << val;
2844 else if (counter == "list-item") {
2845 if (isListItem()) {
2846 if (element() && element()->id() == ID_LI) {
2847 DOMString v = static_cast<ElementImpl*>(element())->getAttribute(ATTR_VALUE);
2848 if ( !v.isEmpty() ) {
2849 i = new CounterReset(this);
2850 val = v.toInt();
2851 // kDebug( 6040 ) << renderName() << " counter-reset: " << counter << " " << val;
2854 if (!i) {
2855 i = new CounterNode(this);
2856 val = 1;
2857 // kDebug( 6040 ) << renderName() << " counter-increment: " << counter << " " << val;
2860 else
2861 if (element() && element()->id() == ID_OL) {
2862 i = new CounterReset(this);
2863 DOMString v = static_cast<ElementImpl*>(element())->getAttribute(ATTR_START);
2864 if ( !v.isEmpty() )
2865 val = v.toInt()-1;
2866 else
2867 val = 0;
2868 // kDebug( 6040 ) << renderName() << " counter-reset: " << counter << " " << val;
2870 else
2871 if (element() &&
2872 (element()->id() == ID_UL ||
2873 element()->id() == ID_MENU||
2874 element()->id() == ID_DIR))
2876 i = new CounterReset(this);
2877 val = 0;
2878 // kDebug( 6040 ) << renderName() << " counter-reset: " << counter << " " << val;
2881 else if (counter == "-khtml-quotes" && isQuote()) {
2882 i = new CounterNode(this);
2883 val = static_cast<RenderQuote*>(this)->quoteCount();
2886 if (!i) {
2887 i = new CounterNode(this);
2888 val = 0;
2889 // kDebug( 6040 ) << renderName() << " counter-increment: " << counter << " " << val;
2891 i->setValue(val);
2892 if (view) i->setIsVisual();
2893 if (counters) i->setHasCounters();
2895 insertCounter(counter, i);
2897 if (!isRoot()) {
2898 CounterNode *last=0, *current=0;
2899 RenderObject *n = previousSibling();
2900 while(n) {
2901 if (n->hasCounter(counter)) {
2902 current = n->getCounter(counter);
2903 break;
2905 else
2906 n = n->previousSibling();
2908 last = current;
2910 CounterNode *sibling = current;
2911 // counter-reset on same render-level is our counter-parent
2912 if (last) {
2913 // Found render-sibling, now search for later counter-siblings among its render-children
2914 n = n->lastChild();
2915 while (n) {
2916 if (n->hasCounter(counter)) {
2917 current = n->getCounter(counter);
2918 if (last->parent() == current->parent() || sibling == current->parent()) {
2919 last = current;
2920 // If the current counter is not the last, search deeper
2921 if (current->nextSibling()) {
2922 n = n->lastChild();
2923 continue;
2925 else
2926 break;
2929 n = n->previousSibling();
2931 if (sibling->isReset())
2933 if (last != sibling)
2934 sibling->insertAfter(i, last);
2935 else
2936 sibling->insertAfter(i, 0);
2938 else if (last->parent())
2939 last->parent()->insertAfter(i, last);
2941 else if (parent()) {
2942 // Nothing found among siblings, let our parent search
2943 last = parent()->getCounter(counter, false);
2944 if (last->isReset())
2945 last->insertAfter(i, 0);
2946 else if (last->parent())
2947 last->parent()->insertAfter(i, last);
2951 return i;
2954 CounterNode* RenderObject::lookupCounter(const QString& counter) const
2956 QHash<QString,khtml::CounterNode*>* counters = document()->counters(this);
2957 return counters ? counters->value(counter) : 0;
2960 void RenderObject::detachCounters()
2962 QHash<QString,khtml::CounterNode*>* counters = document()->counters(this);
2963 if (!counters) return;
2965 QHashIterator<QString,khtml::CounterNode*> i(*counters);
2967 while (i.hasNext()) {
2968 i.next();
2969 i.value()->remove();
2970 delete i.value();
2972 document()->removeCounters(this);
2975 void RenderObject::insertCounter(const QString& counter, CounterNode* val)
2977 QHash<QString,khtml::CounterNode*>* counters = document()->counters(this);
2979 if (!counters) {
2980 counters = new QHash<QString,khtml::CounterNode*>();
2981 document()->setCounters(this, counters);
2984 counters->insert(counter, val);
2987 void RenderObject::updateWidgetMasks() {
2988 for (RenderObject* curr = firstChild(); curr; curr = curr->nextSibling()) {
2989 if ( curr->isWidget() && static_cast<RenderWidget*>(curr)->needsMask() ) {
2990 QWidget* w = static_cast<RenderWidget*>(curr)->widget();
2991 if (!w)
2992 return;
2993 RenderLayer* l = curr->enclosingStackingContext();
2994 QRegion r = l ? l->getMask() : QRegion();
2995 int x,y;
2996 if (!r.isEmpty() && curr->absolutePosition(x,y)) {
2997 int pbx = curr->borderLeft()+curr->paddingLeft();
2998 int pby = curr->borderTop()+curr->paddingTop();
2999 x+= pbx;
3000 y+= pby;
3001 r = r.intersect(QRect(x,y,
3002 curr->width()-pbx-curr->borderRight()-curr->paddingRight(),
3003 curr->height()-pby-curr->borderBottom()-curr->paddingBottom()));
3004 #ifdef MASK_DEBUG
3005 QVector<QRect> ar = r.rects();
3006 kDebug(6040) << "|| Setting widget mask for " << curr->information();
3007 for (int i = 0; i < ar.size() ; ++i) {
3008 kDebug(6040) << " " << ar[i];
3010 #endif
3011 r.translate(-x,-y);
3013 // ### Scrollarea's widget doesn't update when mask change.
3014 // Might be a Qt bug. Might be the way we handle updates. Investigate.
3015 if (::qobject_cast<QScrollArea*>(w)) {
3016 QScrollArea* sa = static_cast<QScrollArea*>(w);
3017 if (!w->mask().isEmpty()) {
3018 QPoint off( sa->horizontalScrollBar()->value(),
3019 sa->verticalScrollBar()->value() );
3020 sa->widget()->update(w->mask().translated(off));
3021 sa->horizontalScrollBar()->update();
3022 sa->verticalScrollBar()->update();
3025 w->setMask(r);
3026 } else {
3027 w->clearMask();
3030 else if (!curr->layer() || !curr->layer()->isStackingContext())
3031 curr->updateWidgetMasks();
3036 QRegion RenderObject::visibleFlowRegion(int x, int y) const
3038 QRegion r;
3039 bool returnSelf = false;
3040 for (RenderObject* ro=firstChild();ro;ro=ro->nextSibling()) {
3041 if( !ro->layer() && !ro->isFloating() && ro->style()->visibility() == VISIBLE) {
3042 // ### fix horizontal float extent
3043 const RenderStyle *s = ro->style();
3044 int ow = s->outlineSize();
3045 if (ro->isInlineFlow() || ro->isText()) {
3046 returnSelf = true;
3047 break;
3049 if ( s->backgroundImage() || s->backgroundColor().isValid() || s->hasBorder() || ro->isReplaced() ) {
3050 r += QRect(x -ow +ro->effectiveXPos(),y -ow + ro->effectiveYPos(),
3051 ro->effectiveWidth()+ow*2, ro->effectiveHeight()+ow*2);
3052 } else {
3053 r += ro->visibleFlowRegion(x+ro->xPos(), y+ro->yPos());
3057 if (returnSelf) {
3058 int ow = style()->outlineSize();
3059 r+= QRect(x-xPos()-ow +effectiveXPos(),y-yPos()-ow + effectiveYPos(),
3060 effectiveWidth()+ow*2, effectiveHeight()+ow*2);
3062 return r;
3065 // SVG
3066 FloatRect RenderObject::relativeBBox(bool includeStroke) const
3068 return FloatRect();
3071 AffineTransform RenderObject::localTransform() const
3073 return AffineTransform(1, 0, 0, 1, xPos(), yPos());
3076 AffineTransform RenderObject::absoluteTransform() const
3078 if (parent())
3079 return localTransform() * parent()->absoluteTransform();
3080 return localTransform();
3082 // END SVG
3084 #undef RED_LUMINOSITY
3085 #undef GREEN_LUMINOSITY
3086 #undef BLUE_LUMINOSITY
3087 #undef INTENSITY_FACTOR
3088 #undef LIGHT_FACTOR
3089 #undef LUMINOSITY_FACTOR
3091 #undef MAX_COLOR
3092 #undef COLOR_DARK_THRESHOLD
3093 #undef COLOR_LIGHT_THRESHOLD
3095 #undef COLOR_LITE_BS_FACTOR
3096 #undef COLOR_LITE_TS_FACTOR
3098 #undef COLOR_DARK_BS_FACTOR
3099 #undef COLOR_DARK_TS_FACTOR
3101 #undef LIGHT_GRAY
3102 #undef DARK_GRAY