render_container.cpp [plain text]
#include "render_container.h"
#include "render_table.h"
#include "render_text.h"
#include "render_image.h"
#include "render_canvas.h"
#include "xml/dom_docimpl.h"
#include "xml/dom_position.h"
#include <kdebug.h>
#include <assert.h>
#if APPLE_CHANGES
#include "KWQAccObjectCache.h"
#endif
using DOM::Position;
using namespace khtml;
RenderContainer::RenderContainer(DOM::NodeImpl* node)
: RenderBox(node)
{
m_first = 0;
m_last = 0;
}
RenderContainer::~RenderContainer()
{
}
void RenderContainer::detach()
{
if (continuation())
continuation()->detach();
while (m_first) {
if (m_first->isListMarker())
m_first->remove();
else
m_first->detach();
}
RenderBox::detach();
}
bool RenderContainer::canHaveChildren() const
{
return true;
}
void RenderContainer::addChild(RenderObject *newChild, RenderObject *beforeChild)
{
#ifdef DEBUG_LAYOUT
kdDebug( 6040 ) << this << ": " << renderName() << "(RenderObject)::addChild( " << newChild << ": " <<
newChild->renderName() << ", " << (beforeChild ? beforeChild->renderName() : "0") << " )" << endl;
#endif
bool needsTable = false;
if(!newChild->isText() && !newChild->isReplaced()) {
switch(newChild->style()->display()) {
case INLINE:
case BLOCK:
case INLINE_BLOCK:
case LIST_ITEM:
case RUN_IN:
case COMPACT:
case BOX:
case INLINE_BOX:
case TABLE:
case INLINE_TABLE:
case TABLE_COLUMN:
break;
case TABLE_COLUMN_GROUP:
case TABLE_CAPTION:
case TABLE_ROW_GROUP:
case TABLE_HEADER_GROUP:
case TABLE_FOOTER_GROUP:
if ( !isTable() )
needsTable = true;
break;
case TABLE_ROW:
if ( !isTableSection() )
needsTable = true;
break;
case TABLE_CELL:
if ( !isTableRow() )
needsTable = true;
#if APPLE_CHANGES
if ( isTableCell() && !firstChild() && !newChild->isTableCell() )
needsTable = false;
#endif
break;
case NONE:
kdDebug( 6000 ) << "error in RenderObject::addChild()!!!!" << endl;
break;
}
}
if ( needsTable ) {
RenderTable *table;
if( !beforeChild )
beforeChild = lastChild();
if( beforeChild && beforeChild->isAnonymous() && beforeChild->isTable() )
table = static_cast<RenderTable *>(beforeChild);
else {
table = new (renderArena()) RenderTable(document() );
RenderStyle *newStyle = new (renderArena()) RenderStyle();
newStyle->inheritFrom(style());
newStyle->setDisplay(TABLE);
table->setStyle(newStyle);
addChild(table, beforeChild);
}
table->addChild(newChild);
} else {
insertChildNode(newChild, beforeChild);
}
}
RenderObject* RenderContainer::removeChildNode(RenderObject* oldChild)
{
KHTMLAssert(oldChild->parent() == this);
if (!documentBeingDestroyed()) {
oldChild->setNeedsLayoutAndMinMaxRecalc();
oldChild->repaint();
oldChild->removeLayers(enclosingLayer());
if (oldChild->isSelectionBorder())
canvas()->clearSelection();
}
if (oldChild->previousSibling())
oldChild->previousSibling()->setNextSibling(oldChild->nextSibling());
if (oldChild->nextSibling())
oldChild->nextSibling()->setPreviousSibling(oldChild->previousSibling());
if (m_first == oldChild)
m_first = oldChild->nextSibling();
if (m_last == oldChild)
m_last = oldChild->previousSibling();
oldChild->setPreviousSibling(0);
oldChild->setNextSibling(0);
oldChild->setParent(0);
#if APPLE_CHANGES
if (KWQAccObjectCache::accessibilityEnabled())
document()->getAccObjectCache()->childrenChanged(this);
#endif
return oldChild;
}
void RenderContainer::removeChild(RenderObject *oldChild)
{
removeChildNode(oldChild);
}
void RenderContainer::updatePseudoChild(RenderStyle::PseudoId type, RenderObject* child)
{
if (style()->styleType() == RenderStyle::BEFORE || style()->styleType() == RenderStyle::AFTER)
return;
RenderStyle* pseudo = getPseudoStyle(type);
bool oldContentPresent = child && (child->style()->styleType() == type);
bool newContentWanted = pseudo && pseudo->display() != NONE;
if (type == RenderStyle::BEFORE && isInlineContinuation())
newContentWanted = false;
if (type == RenderStyle::AFTER && isRenderInline() && continuation())
newContentWanted = false;
if (!newContentWanted ||
(oldContentPresent && !child->style()->contentDataEquivalent(pseudo))) {
if (child && child->style()->styleType() == type) {
oldContentPresent = false;
removeChild(child);
child = (type == RenderStyle::BEFORE) ? firstChild() : lastChild();
}
}
if (!newContentWanted)
return;
if (isInlineFlow() && pseudo->display() != INLINE)
pseudo->setDisplay(INLINE);
if (oldContentPresent) {
if (child && child->style()->styleType() == type) {
child->setStyle(pseudo);
for (RenderObject* genChild = child->firstChild(); genChild; genChild = genChild->nextSibling()) {
if (genChild->isText())
genChild->setStyle(pseudo);
else {
RenderStyle* style = new (renderArena()) RenderStyle();
style->inheritFrom(pseudo);
genChild->setStyle(style);
}
}
}
return; }
RenderObject* insertBefore = (type == RenderStyle::BEFORE) ? child : 0;
RenderObject* pseudoContainer = 0;
for (ContentData* contentData = pseudo->contentData();
contentData; contentData = contentData->_nextContent) {
if (!pseudoContainer)
pseudoContainer = RenderFlow::createAnonymousFlow(document(), pseudo);
if (contentData->contentType() == CONTENT_TEXT)
{
RenderText* t = new (renderArena()) RenderTextFragment(document() , contentData->contentText());
t->setStyle(pseudo);
pseudoContainer->addChild(t);
}
else if (contentData->contentType() == CONTENT_OBJECT)
{
RenderImage* img = new (renderArena()) RenderImage(document());
RenderStyle* style = new (renderArena()) RenderStyle();
style->inheritFrom(pseudo);
img->setStyle(style);
img->setContentObject(contentData->contentObject());
pseudoContainer->addChild(img);
}
}
if (pseudoContainer) {
addChild(pseudoContainer, insertBefore);
}
}
void RenderContainer::appendChildNode(RenderObject* newChild)
{
KHTMLAssert(newChild->parent() == 0);
newChild->setParent(this);
RenderObject* lChild = lastChild();
if(lChild)
{
newChild->setPreviousSibling(lChild);
lChild->setNextSibling(newChild);
}
else
setFirstChild(newChild);
setLastChild(newChild);
if (newChild->firstChild() || newChild->layer()) {
RenderLayer* layer = enclosingLayer();
newChild->addLayers(layer, newChild);
}
newChild->setNeedsLayoutAndMinMaxRecalc(); if (!normalChildNeedsLayout())
setChildNeedsLayout(true);
if (!newChild->isFloatingOrPositioned() && childrenInline())
dirtyLinesFromChangedChild(newChild);
#if APPLE_CHANGES
if (KWQAccObjectCache::accessibilityEnabled())
document()->getAccObjectCache()->childrenChanged(this);
#endif
}
void RenderContainer::insertChildNode(RenderObject* child, RenderObject* beforeChild)
{
if(!beforeChild) {
appendChildNode(child);
return;
}
KHTMLAssert(!child->parent());
while ( beforeChild->parent() != this && beforeChild->parent()->isAnonymousBlock() )
beforeChild = beforeChild->parent();
KHTMLAssert(beforeChild->parent() == this);
if(beforeChild == firstChild())
setFirstChild(child);
RenderObject* prev = beforeChild->previousSibling();
child->setNextSibling(beforeChild);
beforeChild->setPreviousSibling(child);
if(prev) prev->setNextSibling(child);
child->setPreviousSibling(prev);
child->setParent(this);
RenderLayer* layer = enclosingLayer();
child->addLayers(layer, child);
child->setNeedsLayoutAndMinMaxRecalc();
if (!normalChildNeedsLayout())
setChildNeedsLayout(true);
if (!child->isFloatingOrPositioned() && childrenInline())
dirtyLinesFromChangedChild(child);
#if APPLE_CHANGES
if (KWQAccObjectCache::accessibilityEnabled())
document()->getAccObjectCache()->childrenChanged(this);
#endif
}
void RenderContainer::layout()
{
KHTMLAssert( needsLayout() );
KHTMLAssert( minMaxKnown() );
RenderObject *child = firstChild();
while( child ) {
child->layoutIfNeeded();
child = child->nextSibling();
}
setNeedsLayout(false);
}
void RenderContainer::removeLeftoverAnonymousBoxes()
{
RenderObject *child = firstChild();
while( child ) {
RenderObject *next = child->nextSibling();
if ( child->isRenderBlock() && child->isAnonymousBlock() && !child->continuation() && !child->childrenInline() && !child->isTableCell() ) {
RenderObject *firstAnChild = child->firstChild();
RenderObject *lastAnChild = child->lastChild();
if ( firstAnChild ) {
RenderObject *o = firstAnChild;
while( o ) {
o->setParent( this );
o = o->nextSibling();
}
firstAnChild->setPreviousSibling( child->previousSibling() );
lastAnChild->setNextSibling( child->nextSibling() );
if ( child->previousSibling() )
child->previousSibling()->setNextSibling( firstAnChild );
if ( child->nextSibling() )
child->nextSibling()->setPreviousSibling( lastAnChild );
} else {
if ( child->previousSibling() )
child->previousSibling()->setNextSibling( child->nextSibling() );
if ( child->nextSibling() )
child->nextSibling()->setPreviousSibling( child->previousSibling() );
}
if ( child == firstChild() )
m_first = firstAnChild;
if ( child == lastChild() )
m_last = lastAnChild;
child->setParent( 0 );
child->setPreviousSibling( 0 );
child->setNextSibling( 0 );
if ( !child->isText() ) {
RenderContainer *c = static_cast<RenderContainer *>(child);
c->m_first = 0;
c->m_next = 0;
}
child->detach();
}
child = next;
}
if ( parent() )
parent()->removeLeftoverAnonymousBoxes();
}
VisiblePosition RenderContainer::positionForCoordinates(int _x, int _y)
{
if (!firstChild())
return VisiblePosition(element(), 0, DOWNSTREAM);
int min = INT_MAX;
RenderObject *closestRenderer = 0;
for (RenderObject *renderer = firstChild(); renderer; renderer = renderer->nextSibling()) {
if (!renderer->firstChild() && !renderer->isInline() && !renderer->isBlockFlow())
continue;
int absx, absy;
renderer->absolutePosition(absx, absy);
int top = absy + borderTop() + paddingTop();
int bottom = top + renderer->contentHeight();
int left = absx + borderLeft() + paddingLeft();
int right = left + renderer->contentWidth();
int cmp;
cmp = abs(_y - top); if (cmp < min) { closestRenderer = renderer; min = cmp; }
cmp = abs(_y - bottom); if (cmp < min) { closestRenderer = renderer; min = cmp; }
cmp = abs(_x - left); if (cmp < min) { closestRenderer = renderer; min = cmp; }
cmp = abs(_x - right); if (cmp < min) { closestRenderer = renderer; min = cmp; }
}
if (closestRenderer)
return closestRenderer->positionForCoordinates(_x, _y);
return VisiblePosition(element(), 0, DOWNSTREAM);
}
#undef DEBUG_LAYOUT