diff options
Diffstat (limited to 'khtml/rendering/render_box.cpp')
-rw-r--r-- | khtml/rendering/render_box.cpp | 2325 |
1 files changed, 2325 insertions, 0 deletions
diff --git a/khtml/rendering/render_box.cpp b/khtml/rendering/render_box.cpp new file mode 100644 index 000000000..56a3109d5 --- /dev/null +++ b/khtml/rendering/render_box.cpp @@ -0,0 +1,2325 @@ +/** + * This file is part of the DOM implementation for KDE. + * + * Copyright (C) 1999-2003 Lars Knoll (knoll@kde.org) + * (C) 1999 Antti Koivisto (koivisto@kde.org) + * (C) 2002-2003 Apple Computer, Inc. + * (C) 2005 Allan Sandfeld Jensen (kde@carewolf.com) + * (C) 2006 Samuel Weinig (sam.weinig@gmail.com) + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + * + */ +// ------------------------------------------------------------------------- +//#define DEBUG_LAYOUT +//#define CLIP_DEBUG + + +#include <qpainter.h> + +#include "misc/loader.h" +#include "rendering/render_replaced.h" +#include "rendering/render_canvas.h" +#include "rendering/render_table.h" +#include "rendering/render_inline.h" +#include "rendering/render_block.h" +#include "rendering/render_line.h" +#include "rendering/render_layer.h" +#include "misc/htmlhashes.h" +#include "xml/dom_nodeimpl.h" +#include "xml/dom_docimpl.h" +#include "html/html_elementimpl.h" + +#include <khtmlview.h> +#include <kdebug.h> +#include <kglobal.h> +#include <assert.h> + + +using namespace DOM; +using namespace khtml; + +#define TABLECELLMARGIN -0x4000 + +RenderBox::RenderBox(DOM::NodeImpl* node) + : RenderContainer(node) +{ + m_minWidth = -1; + m_maxWidth = -1; + m_width = m_height = 0; + m_x = 0; + m_y = 0; + m_marginTop = 0; + m_marginBottom = 0; + m_marginLeft = 0; + m_marginRight = 0; + m_staticX = 0; + m_staticY = 0; + + m_placeHolderBox = 0; + m_layer = 0; +} + +RenderBlock* RenderBox::createAnonymousBlock() +{ + RenderStyle *newStyle = new RenderStyle(); + newStyle->inheritFrom(style()); + newStyle->setDisplay(BLOCK); + + RenderBlock *newBox = new (renderArena()) RenderBlock(document() /* anonymous*/); + newBox->setStyle(newStyle); + return newBox; +} + +void RenderBox::restructureParentFlow() { + if (!parent() || parent()->childrenInline() == isInline()) + return; + // We have gone from not affecting the inline status of the parent flow to suddenly + // having an impact. See if there is a mismatch between the parent flow's + // childrenInline() state and our state. + if (!isInline()) { + if (parent()->isRenderInline()) { + // We have to split the parent flow. + RenderInline* parentInline = static_cast<RenderInline*>(parent()); + RenderBlock* newBox = parentInline->createAnonymousBlock(); + + RenderFlow* oldContinuation = parent()->continuation(); + parentInline->setContinuation(newBox); + + RenderObject* beforeChild = nextSibling(); + parent()->removeChildNode(this); + parentInline->splitFlow(beforeChild, newBox, this, oldContinuation); + } + else if (parent()->isRenderBlock()) + static_cast<RenderBlock*>(parent())->makeChildrenNonInline(); + } + else { + // An anonymous block must be made to wrap this inline. + RenderBlock* box = createAnonymousBlock(); + parent()->insertChildNode(box, this); + box->appendChildNode(parent()->removeChildNode(this)); + } +} + +static inline bool overflowAppliesTo(RenderObject* o) +{ + // css 2.1-11.1.1 + // 1) overflow only applies to non-replaced block-level elements, table cells, and inline-block elements + if (o->isRenderBlock() || o->isTableRow() || o->isTableSection()) + // 2) overflow on root applies to the viewport (cf. KHTMLView::layout) + if (!o->isRoot()) + // 3) overflow on body may apply to the viewport... + if (!o->isBody() + // ...but only for HTML documents... + || !o->document()->isHTMLDocument() + // ...and only when the root has a visible overflow + || !o->document()->documentElement()->renderer() + || !o->document()->documentElement()->renderer()->style() + || o->document()->documentElement()->renderer()->style()->hidesOverflow()) + return true; + + return false; +} + +void RenderBox::setStyle(RenderStyle *_style) +{ + bool affectsParent = style() && isFloatingOrPositioned() && + (!_style->isFloating() && _style->position() != ABSOLUTE && _style->position() != FIXED) && + parent() && (parent()->isBlockFlow() || parent()->isInlineFlow()); + + RenderContainer::setStyle(_style); + + // The root always paints its background/border. + if (isRoot()) + setShouldPaintBackgroundOrBorder(true); + + switch(_style->display()) + { + case INLINE: + case INLINE_BLOCK: + case INLINE_TABLE: + setInline(true); + break; + case RUN_IN: + if (isInline() && parent() && parent()->childrenInline()) + break; + default: + setInline(false); + } + + switch(_style->position()) + { + case ABSOLUTE: + case FIXED: + setPositioned(true); + break; + default: + setPositioned(false); + if( !isTableCell() && _style->isFloating() ) + setFloating(true); + + if( _style->position() == RELATIVE ) + setRelPositioned(true); + } + + if (overflowAppliesTo(this) && _style->hidesOverflow()) + setHasOverflowClip(); + + if (requiresLayer()) { + if (!m_layer) { + m_layer = new (renderArena()) RenderLayer(this); + m_layer->insertOnlyThisLayer(); + if (parent() && containingBlock()) + m_layer->updateLayerPosition(); + } + } + else if (m_layer && !isCanvas()) { + m_layer->removeOnlyThisLayer(); + m_layer = 0; + } + + if (m_layer) + m_layer->styleChanged(); + + if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintActionOutline)) + static_cast<RenderCanvas*>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize()); + if (affectsParent) + restructureParentFlow(); +} + +RenderBox::~RenderBox() +{ + //kdDebug( 6040 ) << "Element destructor: this=" << nodeName().string() << endl; +} + +void RenderBox::detach() +{ + RenderLayer* layer = m_layer; + RenderArena* arena = renderArena(); + + RenderContainer::detach(); + + if (layer) + layer->detach(arena); +} + +InlineBox* RenderBox::createInlineBox(bool /*makePlaceHolderBox*/, bool /*isRootLineBox*/) +{ + if (m_placeHolderBox) + m_placeHolderBox->detach(renderArena()); + return (m_placeHolderBox = new (renderArena()) InlineBox(this)); +} + +void RenderBox::deleteInlineBoxes(RenderArena* arena) +{ + if (m_placeHolderBox) { + m_placeHolderBox->detach( arena ? arena : renderArena() ); + m_placeHolderBox = 0; + } +} + +short RenderBox::contentWidth() const +{ + short w = m_width - style()->borderLeftWidth() - style()->borderRightWidth(); + w -= paddingLeft() + paddingRight(); + + if (m_layer && scrollsOverflowY()) + w -= m_layer->verticalScrollbarWidth(); + + //kdDebug( 6040 ) << "RenderBox::contentWidth(2) = " << w << endl; + return w; +} + +int RenderBox::contentHeight() const +{ + int h = m_height - style()->borderTopWidth() - style()->borderBottomWidth(); + h -= paddingTop() + paddingBottom(); + + if (m_layer && scrollsOverflowX()) + h -= m_layer->horizontalScrollbarHeight(); + + return h; +} + +void RenderBox::setPos( int xPos, int yPos ) +{ + m_x = xPos; m_y = yPos; +} + +short RenderBox::width() const +{ + return m_width; +} + +int RenderBox::height() const +{ + return m_height; +} + +void RenderBox::setWidth( int width ) +{ + m_width = width; +} + +void RenderBox::setHeight( int height ) +{ + m_height = height; +} + +int RenderBox::calcBoxHeight(int h) const +{ + if (style()->boxSizing() == CONTENT_BOX) + h += borderTop() + borderBottom() + paddingTop() + paddingBottom(); + + return h; +} + +int RenderBox::calcBoxWidth(int w) const +{ + if (style()->boxSizing() == CONTENT_BOX) + w += borderLeft() + borderRight() + paddingLeft() + paddingRight(); + + return w; +} + +int RenderBox::calcContentHeight(int h) const +{ + if (style()->boxSizing() == BORDER_BOX) + h -= borderTop() + borderBottom() + paddingTop() + paddingBottom(); + + return kMax(0, h); +} + +int RenderBox::calcContentWidth(int w) const +{ + if (style()->boxSizing() == BORDER_BOX) + w -= borderLeft() + borderRight() + paddingLeft() + paddingRight(); + + return kMax(0, w); +} + +// --------------------- painting stuff ------------------------------- + +void RenderBox::paint(PaintInfo& i, int _tx, int _ty) +{ + _tx += m_x; + _ty += m_y; + + if (hasOverflowClip() && m_layer) + m_layer->subtractScrollOffset(_tx, _ty); + + // default implementation. Just pass things through to the children + for(RenderObject* child = firstChild(); child; child = child->nextSibling()) + child->paint(i, _tx, _ty); +} + +void RenderBox::paintRootBoxDecorations(PaintInfo& paintInfo, int _tx, int _ty) +{ + //kdDebug( 6040 ) << renderName() << "::paintRootBoxDecorations()" << _tx << "/" << _ty << endl; + const BackgroundLayer* bgLayer = style()->backgroundLayers(); + QColor bgColor = style()->backgroundColor(); + if (document()->isHTMLDocument() && !style()->hasBackground()) { + // Locate the <body> element using the DOM. This is easier than trying + // to crawl around a render tree with potential :before/:after content and + // anonymous blocks created by inline <body> tags etc. We can locate the <body> + // render object very easily via the DOM. + HTMLElementImpl* body = document()->body(); + RenderObject* bodyObject = (body && body->id() == ID_BODY) ? body->renderer() : 0; + + if (bodyObject) { + bgLayer = bodyObject->style()->backgroundLayers(); + bgColor = bodyObject->style()->backgroundColor(); + } + } + + if( !bgColor.isValid() && canvas()->view()) + bgColor = canvas()->view()->palette().active().color(QColorGroup::Base); + + int w = width(); + int h = height(); + + // kdDebug(0) << "width = " << w <<endl; + + int rw, rh; + if (canvas()->view()) { + rw = canvas()->view()->contentsWidth(); + rh = canvas()->view()->contentsHeight(); + } else { + rw = canvas()->docWidth(); + rh = canvas()->docHeight(); + } + + // kdDebug(0) << "rw = " << rw <<endl; + + int bx = _tx - marginLeft(); + int by = _ty - marginTop(); + int bw = QMAX(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw); + int bh = QMAX(h + marginTop() + marginBottom() + borderTop() + borderBottom(), rh); + + // CSS2 14.2: + // " The background of the box generated by the root element covers the entire canvas." + // hence, paint the background even in the margin areas (unlike for every other element!) + // I just love these little inconsistencies .. :-( (Dirk) + int my = kMax(by, paintInfo.r.y()); + + paintBackgrounds(paintInfo.p, bgColor, bgLayer, my, paintInfo.r.height(), bx, by, bw, bh); + + if(style()->hasBorder()) + paintBorder( paintInfo.p, _tx, _ty, w, h, style() ); +} + +void RenderBox::paintBoxDecorations(PaintInfo& paintInfo, int _tx, int _ty) +{ + //kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl; + + if(isRoot()) + return paintRootBoxDecorations(paintInfo, _tx, _ty); + + int w = width(); + int h = height() + borderTopExtra() + borderBottomExtra(); + _ty -= borderTopExtra(); + + int my = kMax(_ty,paintInfo.r.y()); + int end = kMin( paintInfo.r.y() + paintInfo.r.height(), _ty + h ); + int mh = end - my; + + // The <body> only paints its background if the root element has defined a background + // independent of the body. Go through the DOM to get to the root element's render object, + // since the root could be inline and wrapped in an anonymous block. + + if (!isBody() || !document()->isHTMLDocument() || document()->documentElement()->renderer()->style()->hasBackground()) + paintBackgrounds(paintInfo.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h); + + if(style()->hasBorder()) { + paintBorder(paintInfo.p, _tx, _ty, w, h, style()); + } +} + +void RenderBox::paintBackgrounds(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int height) + { + if (!bgLayer) return; + paintBackgrounds(p, c, bgLayer->next(), clipy, cliph, _tx, _ty, w, height); + paintBackground(p, c, bgLayer, clipy, cliph, _tx, _ty, w, height); +} + +void RenderBox::paintBackground(QPainter *p, const QColor& c, const BackgroundLayer* bgLayer, int clipy, int cliph, int _tx, int _ty, int w, int height) +{ + paintBackgroundExtended(p, c, bgLayer, clipy, cliph, _tx, _ty, w, height, + borderLeft(), borderRight(), paddingLeft(), paddingRight()); +} + +static void calculateBackgroundSize(const BackgroundLayer* bgLayer, int& scaledWidth, int& scaledHeight) +{ + CachedImage* bg = bgLayer->backgroundImage(); + + if (bgLayer->isBackgroundSizeSet()) { + Length bgWidth = bgLayer->backgroundSize().width; + Length bgHeight = bgLayer->backgroundSize().height; + + if (bgWidth.isPercent()) + scaledWidth = scaledWidth * bgWidth.value() / 100; + else if (bgWidth.isFixed()) + scaledWidth = bgWidth.value(); + else if (bgWidth.isVariable()) { + // If the width is auto and the height is not, we have to use the appropriate + // scale to maintain our aspect ratio. + if (bgHeight.isPercent()) { + int scaledH = scaledHeight * bgHeight.value() / 100; + scaledWidth = bg->pixmap_size().width() * scaledH / bg->pixmap_size().height(); + } else if (bgHeight.isFixed()) + scaledWidth = bg->pixmap_size().width() * bgHeight.value() / bg->pixmap_size().height(); + } + + if (bgHeight.isPercent()) + scaledHeight = scaledHeight * bgHeight.value() / 100; + else if (bgHeight.isFixed()) + scaledHeight = bgHeight.value(); + else if (bgHeight.isVariable()) { + // If the height is auto and the width is not, we have to use the appropriate + // scale to maintain our aspect ratio. + if (bgWidth.isPercent()) + scaledHeight = bg->pixmap_size().height() * scaledWidth / bg->pixmap_size().width(); + else if (bgWidth.isFixed()) + scaledHeight = bg->pixmap_size().height() * bgWidth.value() / bg->pixmap_size().width(); + else if (bgWidth.isVariable()) { + // If both width and height are auto, we just want to use the image's + // intrinsic size. + scaledWidth = bg->pixmap_size().width(); + scaledHeight = bg->pixmap_size().height(); + } + } + } else { + scaledWidth = bg->pixmap_size().width(); + scaledHeight = bg->pixmap_size().height(); + } +} + +void RenderBox::paintBackgroundExtended(QPainter *p, const QColor &c, const BackgroundLayer* bgLayer, int clipy, int cliph, + int _tx, int _ty, int w, int h, + int bleft, int bright, int pleft, int pright) +{ + if ( cliph < 0 ) + return; + + if (bgLayer->backgroundClip() != BGBORDER) { + // Clip to the padding or content boxes as necessary. + bool includePadding = bgLayer->backgroundClip() == BGCONTENT; + int x = _tx + bleft + (includePadding ? pleft : 0); + int y = _ty + borderTop() + (includePadding ? paddingTop() : 0); + int width = w - bleft - bright - (includePadding ? pleft + pright : 0); + int height = h - borderTop() - borderBottom() - (includePadding ? paddingTop() + paddingBottom() : 0); + p->save(); + p->setClipRect(QRect(x, y, width, height), QPainter::CoordPainter); + } + + CachedImage* bg = bgLayer->backgroundImage(); + bool shouldPaintBackgroundImage = bg && bg->pixmap_size() == bg->valid_rect().size() && !bg->isTransparent() && !bg->isErrorImage(); + QColor bgColor = c; + + // Paint the color first underneath all images. + if (!bgLayer->next() && bgColor.isValid() && qAlpha(bgColor.rgb()) > 0) + p->fillRect(_tx, clipy, w, cliph, bgColor); + + // no progressive loading of the background image + if (shouldPaintBackgroundImage) { + int sx = 0; + int sy = 0; + int cw,ch; + int cx,cy; + int scaledImageWidth, scaledImageHeight; + + // CSS2 chapter 14.2.1 + + if (bgLayer->backgroundAttachment()) { + //scroll + int hpab = 0, vpab = 0, left = 0, top = 0; // Init to 0 for background-origin of 'border' + if (bgLayer->backgroundOrigin() != BGBORDER) { + hpab += bleft + bright; + vpab += borderTop() + borderBottom(); + left += bleft; + top += borderTop(); + if (bgLayer->backgroundOrigin() == BGCONTENT) { + hpab += pleft + pright; + vpab += paddingTop() + paddingBottom(); + left += pleft; + top += paddingTop(); + } + } + + int pw = w - hpab; + int ph = h - vpab; + scaledImageWidth = pw; + scaledImageHeight = ph; + calculateBackgroundSize(bgLayer, scaledImageWidth, scaledImageHeight); + + EBackgroundRepeat bgr = bgLayer->backgroundRepeat(); + if (bgr == NO_REPEAT || bgr == REPEAT_Y) { + cw = scaledImageWidth; + int xPosition = bgLayer->backgroundXPosition().minWidth(pw-scaledImageWidth); + if ( xPosition >= 0 ) { + cx = _tx + xPosition; + cw = kMin(scaledImageWidth, pw - xPosition); + } + else { + cx = _tx; + if (scaledImageWidth > 0) { + sx = -xPosition; + cw = kMin(scaledImageWidth+xPosition, pw); + } + } + cx += left; + } else { + // repeat over x + cw = w; + cx = _tx; + if (scaledImageWidth > 0) { + int xPosition = bgLayer->backgroundXPosition().minWidth(pw-scaledImageWidth); + sx = scaledImageWidth - (xPosition % scaledImageWidth); + sx -= left % scaledImageWidth; + } + } + if (bgr == NO_REPEAT || bgr == REPEAT_X) { + ch = scaledImageHeight; + int yPosition = bgLayer->backgroundYPosition().minWidth(ph - scaledImageHeight); + if ( yPosition >= 0 ) { + cy = _ty + yPosition; + ch = kMin(ch, ph - yPosition); + } + else { + cy = _ty; + if (scaledImageHeight > 0) { + sy = -yPosition; + ch = kMin(scaledImageHeight+yPosition, ph); + } + } + + cy += top; + } else { + // repeat over y + ch = h; + cy = _ty; + if (scaledImageHeight > 0) { + int yPosition = bgLayer->backgroundYPosition().minWidth(ph - scaledImageHeight); + sy = scaledImageHeight - (yPosition % scaledImageHeight); + sy -= top % scaledImageHeight; + } + } + if (layer()) + layer()->scrollOffset(sx, sy); + } + else + { + //fixed + QRect vr = viewRect(); + int pw = vr.width(); + int ph = vr.height(); + scaledImageWidth = pw; + scaledImageHeight = ph; + calculateBackgroundSize(bgLayer, scaledImageWidth, scaledImageHeight); + EBackgroundRepeat bgr = bgLayer->backgroundRepeat(); + + int xPosition = bgLayer->backgroundXPosition().minWidth(pw-scaledImageWidth); + if (bgr == NO_REPEAT || bgr == REPEAT_Y) { + cw = kMin(scaledImageWidth, pw - xPosition); + cx = vr.x() + xPosition; + } else { + cw = pw; + cx = vr.x(); + if (scaledImageWidth > 0) + sx = scaledImageWidth - xPosition % scaledImageWidth; + } + + int yPosition = bgLayer->backgroundYPosition().minWidth(ph-scaledImageHeight); + if (bgr == NO_REPEAT || bgr == REPEAT_X) { + ch = kMin(scaledImageHeight, ph - yPosition); + cy = vr.y() + yPosition; + } else { + ch = ph; + cy = vr.y(); + if (scaledImageHeight > 0) + sy = scaledImageHeight - yPosition % scaledImageHeight; + } + + QRect fix(cx, cy, cw, ch); + QRect ele(_tx, _ty, w, h); + QRect b = fix.intersect(ele); + + //kdDebug() <<" ele is " << ele << " b is " << b << " fix is " << fix << endl; + sx+=b.x()-cx; + sy+=b.y()-cy; + cx=b.x();cy=b.y();cw=b.width();ch=b.height(); + } + // restrict painting to repaint-clip + if (cy < clipy) { + ch -= (clipy - cy); + sy += (clipy - cy); + cy = clipy; + } + ch = kMin(ch, cliph); + +// kdDebug() << " clipy, cliph: " << clipy << ", " << cliph << endl; +// kdDebug() << " drawTiledPixmap(" << cx << ", " << cy << ", " << cw << ", " << ch << ", " << sx << ", " << sy << ")" << endl; + if (cw>0 && ch>0) + p->drawTiledPixmap(cx, cy, cw, ch, bg->tiled_pixmap(c, scaledImageWidth, scaledImageHeight), sx, sy); + + } + + if (bgLayer->backgroundClip() != BGBORDER) + p->restore(); // Undo the background clip + +} + +void RenderBox::outlineBox(QPainter *p, int _tx, int _ty, const char *color) +{ + p->setPen(QPen(QColor(color), 1, Qt::DotLine)); + p->setBrush( Qt::NoBrush ); + p->drawRect(_tx, _ty, m_width, m_height); +} + +QRect RenderBox::getOverflowClipRect(int tx, int ty) +{ + // XXX When overflow-clip (CSS3) is implemented, we'll obtain the property + // here. + int bl=borderLeft(),bt=borderTop(),bb=borderBottom(),br=borderRight(); + int clipx = tx+bl; + int clipy = ty+bt; + int clipw = m_width-bl-br; + int cliph = m_height-bt-bb+borderTopExtra()+borderBottomExtra(); + + // Substract out scrollbars if we have them. + if (m_layer) { + clipw -= m_layer->verticalScrollbarWidth(); + cliph -= m_layer->horizontalScrollbarHeight(); + } + + return QRect(clipx,clipy,clipw,cliph); +} + +QRect RenderBox::getClipRect(int tx, int ty) +{ + int bl=borderLeft(),bt=borderTop(),bb=borderBottom(),br=borderRight(); + // ### what about paddings? + int clipw = m_width-bl-br; + int cliph = m_height-bt-bb; + + bool rtl = (style()->direction() == RTL); + + int clipleft = 0; + int clipright = clipw; + int cliptop = 0; + int clipbottom = cliph; + + if ( style()->hasClip() && style()->position() == ABSOLUTE ) { + // the only case we use the clip property according to CSS 2.1 + if (!style()->clipLeft().isVariable()) { + int c = style()->clipLeft().width(clipw); + if ( rtl ) + clipleft = clipw - c; + else + clipleft = c; + } + if (!style()->clipRight().isVariable()) { + int w = style()->clipRight().width(clipw); + if ( rtl ) { + clipright = clipw - w; + } else { + clipright = w; + } + } + if (!style()->clipTop().isVariable()) + cliptop = style()->clipTop().width(cliph); + if (!style()->clipBottom().isVariable()) + clipbottom = style()->clipBottom().width(cliph); + } + int clipx = tx + clipleft; + int clipy = ty + cliptop; + clipw = clipright-clipleft; + cliph = clipbottom-cliptop; + + //kdDebug( 6040 ) << "setting clip("<<clipx<<","<<clipy<<","<<clipw<<","<<cliph<<")"<<endl; + + return QRect(clipx,clipy,clipw,cliph); +} + +void RenderBox::close() +{ + setNeedsLayoutAndMinMaxRecalc(); +} + +short RenderBox::containingBlockWidth() const +{ + if (isCanvas() && canvas()->view()) + { + if (canvas()->pagedMode()) + return canvas()->width(); + else + return canvas()->view()->visibleWidth(); + } + + RenderBlock* cb = containingBlock(); + if (isRenderBlock() && cb->isTable() && static_cast<RenderTable*>(cb)->caption() == this) { + //captions are not affected by table border or padding + return cb->width(); + } + if (usesLineWidth()) + return cb->lineWidth(m_y); + else + return cb->contentWidth(); +} + +bool RenderBox::absolutePosition(int &_xPos, int &_yPos, bool f) const +{ + if ( style()->position() == FIXED ) + f = true; + RenderObject *o = container(); + if( o && o->absolutePosition(_xPos, _yPos, f)) + { + if ( o->layer() ) { + if (o->hasOverflowClip()) + o->layer()->subtractScrollOffset( _xPos, _yPos ); + if (isPositioned()) + o->layer()->checkInlineRelOffset(this, _xPos, _yPos); + } + + if(!isInline() || isReplaced()) { + _xPos += xPos(), + _yPos += yPos(); + } + + if(isRelPositioned()) + relativePositionOffset(_xPos, _yPos); + return true; + } + else + { + _xPos = 0; + _yPos = 0; + return false; + } +} + +void RenderBox::position(InlineBox* box, int /*from*/, int /*len*/, bool /*reverse*/) +{ + if (isPositioned()) { + // Cache the x position only if we were an INLINE type originally. + bool wasInline = style()->isOriginalDisplayInlineType(); + + if (wasInline && hasStaticX()) { + // The value is cached in the xPos of the box. We only need this value if + // our object was inline originally, since otherwise it would have ended up underneath + // the inlines. + m_staticX = box->xPos(); + } + else if (!wasInline && hasStaticY()) { + // Our object was a block originally, so we make our normal flow position be + // just below the line box (as though all the inlines that came before us got + // wrapped in an anonymous block, which is what would have happened had we been + // in flow). This value was cached in the yPos() of the box. + m_staticY = box->yPos(); + } + } + else if (isReplaced()) + setPos( box->xPos(), box->yPos() ); +} + +void RenderBox::repaint(Priority prior) +{ + int ow = style() ? style()->outlineSize() : 0; + if( isInline() && !isReplaced() ) + { + RenderObject* p = parent(); + Q_ASSERT(p); + while( p->isInline() && !p->isReplaced() ) + p = p->parent(); + int xoff = p->hasOverflowClip() ? 0 : p->overflowLeft(); + int yoff = p->hasOverflowClip() ? 0 : p->overflowTop(); + p->repaintRectangle( -ow + xoff, -ow + yoff, p->effectiveWidth()+ow*2, p->effectiveHeight()+ow*2, prior); + } + else + { + int xoff = hasOverflowClip() ? 0 : overflowLeft(); + int yoff = hasOverflowClip() ? 0 : overflowTop(); + repaintRectangle( -ow + xoff, -ow + yoff, effectiveWidth()+ow*2, effectiveHeight()+ow*2, prior); + } +} + +void RenderBox::repaintRectangle(int x, int y, int w, int h, Priority p, bool f) +{ + x += m_x; + y += m_y; + + // Apply the relative position offset when invalidating a rectangle. The layer + // is translated, but the render box isn't, so we need to do this to get the + // right dirty rect. Since this is called from RenderObject::setStyle, the relative position + // flag on the RenderObject has been cleared, so use the one on the style(). + if (style()->position() == RELATIVE && m_layer) + relativePositionOffset(x,y); + + if (style()->position() == FIXED) f=true; + + // kdDebug( 6040 ) << "RenderBox(" <<this << ", " << renderName() << ")::repaintRectangle (" << x << "/" << y << ") (" << w << "/" << h << ")" << endl; + RenderObject *o = container(); + if( o ) { + if (o->layer()) { + if (o->style()->hidesOverflow() && o->layer() && !o->isInlineFlow()) + o->layer()->subtractScrollOffset(x,y); // For overflow:auto/scroll/hidden. + if (style()->position() == ABSOLUTE) + o->layer()->checkInlineRelOffset(this,x,y); + } + o->repaintRectangle(x, y, w, h, p, f); + } +} + +void RenderBox::relativePositionOffset(int &tx, int &ty) const +{ + if(!style()->left().isVariable()) + tx += style()->left().width(containingBlockWidth()); + else if(!style()->right().isVariable()) + tx -= style()->right().width(containingBlockWidth()); + if(!style()->top().isVariable()) + { + if (!style()->top().isPercent() + || containingBlock()->style()->height().isFixed()) + ty += style()->top().width(containingBlockHeight()); + } + else if(!style()->bottom().isVariable()) + { + if (!style()->bottom().isPercent() + || containingBlock()->style()->height().isFixed()) + ty -= style()->bottom().width(containingBlockHeight()); + } +} + +void RenderBox::calcWidth() +{ +#ifdef DEBUG_LAYOUT + kdDebug( 6040 ) << "RenderBox("<<renderName()<<")::calcWidth()" << endl; +#endif + if (isPositioned()) + { + calcAbsoluteHorizontal(); + } + else + { + bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable(); + Length w; + if (treatAsReplaced) + w = Length( calcReplacedWidth(), Fixed ); + else + w = style()->width(); + + Length ml = style()->marginLeft(); + Length mr = style()->marginRight(); + + int cw = containingBlockWidth(); + if (cw<0) cw = 0; + + m_marginLeft = 0; + m_marginRight = 0; + + if (isInline() && !isInlineBlockOrInlineTable()) + { + // just calculate margins + m_marginLeft = ml.minWidth(cw); + m_marginRight = mr.minWidth(cw); + if (treatAsReplaced) + { + m_width = calcBoxWidth(w.width(cw)); + m_width = KMAX(m_width, m_minWidth); + } + + return; + } + else + { + LengthType widthType, minWidthType, maxWidthType; + if (treatAsReplaced) { + m_width = calcBoxWidth(w.width(cw)); + widthType = w.type(); + } else { + m_width = calcWidthUsing(Width, cw, widthType); + int minW = calcWidthUsing(MinWidth, cw, minWidthType); + int maxW = style()->maxWidth().value() == UNDEFINED ? + m_width : calcWidthUsing(MaxWidth, cw, maxWidthType); + + if (m_width > maxW) { + m_width = maxW; + widthType = maxWidthType; + } + if (m_width < minW) { + m_width = minW; + widthType = minWidthType; + } + } + + if (widthType == Variable) { + // kdDebug( 6040 ) << "variable" << endl; + m_marginLeft = ml.minWidth(cw); + m_marginRight = mr.minWidth(cw); + } + else + { +// kdDebug( 6040 ) << "non-variable " << w.type << ","<< w.value << endl; + calcHorizontalMargins(ml,mr,cw); + } + } + + if (cw && cw != m_width + m_marginLeft + m_marginRight && !isFloating() && !isInline()) + { + if (containingBlock()->style()->direction()==LTR) + m_marginRight = cw - m_width - m_marginLeft; + else + m_marginLeft = cw - m_width - m_marginRight; + } + } + +#ifdef DEBUG_LAYOUT + kdDebug( 6040 ) << "RenderBox::calcWidth(): m_width=" << m_width << " containingBlockWidth()=" << containingBlockWidth() << endl; + kdDebug( 6040 ) << "m_marginLeft=" << m_marginLeft << " m_marginRight=" << m_marginRight << endl; +#endif +} + +int RenderBox::calcWidthUsing(WidthType widthType, int cw, LengthType& lengthType) +{ + int width = m_width; + Length w; + if (widthType == Width) + w = style()->width(); + else if (widthType == MinWidth) + w = style()->minWidth(); + else + w = style()->maxWidth(); + + lengthType = w.type(); + + if (lengthType == Variable) { + int marginLeft = style()->marginLeft().minWidth(cw); + int marginRight = style()->marginRight().minWidth(cw); + if (cw) width = cw - marginLeft - marginRight; + + // size to max width? + if (sizesToMaxWidth()) { + width = KMAX(width, (int)m_minWidth); + width = KMIN(width, (int)m_maxWidth); + } + } + else + { + width = calcBoxWidth(w.width(cw)); + } + + return width; +} + +void RenderBox::calcHorizontalMargins(const Length& ml, const Length& mr, int cw) +{ + if (isFloating() || isInline()) // Inline blocks/tables and floats don't have their margins increased. + { + m_marginLeft = ml.minWidth(cw); + m_marginRight = mr.minWidth(cw); + } + else + { + if ( (ml.isVariable() && mr.isVariable() && m_width<cw) || + (!ml.isVariable() && !mr.isVariable() && + containingBlock()->style()->textAlign() == KHTML_CENTER) ) + { + m_marginLeft = (cw - m_width)/2; + if (m_marginLeft<0) m_marginLeft=0; + m_marginRight = cw - m_width - m_marginLeft; + } + else if ( (mr.isVariable() && m_width<cw) || + (!ml.isVariable() && containingBlock()->style()->direction() == RTL && + containingBlock()->style()->textAlign() == KHTML_LEFT)) + { + m_marginLeft = ml.width(cw); + m_marginRight = cw - m_width - m_marginLeft; + } + else if ( (ml.isVariable() && m_width<cw) || + (!mr.isVariable() && containingBlock()->style()->direction() == LTR && + containingBlock()->style()->textAlign() == KHTML_RIGHT)) + { + m_marginRight = mr.width(cw); + m_marginLeft = cw - m_width - m_marginRight; + } + else + { + // this makes auto margins 0 if we failed a m_width<cw test above (css2.1, 10.3.3) + m_marginLeft = ml.minWidth(cw); + m_marginRight = mr.minWidth(cw); + } + } +} + +void RenderBox::calcHeight() +{ + +#ifdef DEBUG_LAYOUT + kdDebug( 6040 ) << "RenderBox::calcHeight()" << endl; +#endif + + //cell height is managed by table, inline elements do not have a height property. + if ( isTableCell() || (isInline() && !isReplaced()) ) + return; + + if (isPositioned()) + calcAbsoluteVertical(); + else + { + calcVerticalMargins(); + + // For tables, calculate margins only + if (isTable()) + return; + + Length h; + bool treatAsReplaced = isReplaced() && !isInlineBlockOrInlineTable(); + bool checkMinMaxHeight = false; + + if ( treatAsReplaced ) + h = Length( calcReplacedHeight(), Fixed ); + else { + h = style()->height(); + checkMinMaxHeight = true; + } + + int height; + if (checkMinMaxHeight) { + height = calcHeightUsing(style()->height()); + if (height == -1) + height = m_height; + int minH = calcHeightUsing(style()->minHeight()); // Leave as -1 if unset. + int maxH = style()->maxHeight().value() == UNDEFINED ? height : calcHeightUsing(style()->maxHeight()); + if (maxH == -1) + maxH = height; + height = kMin(maxH, height); + height = kMax(minH, height); + } + else { + // The only times we don't check min/max height are when a fixed length has + // been given as an override. Just use that. + height = calcBoxHeight(h.value()); + } + + if (height<m_height && !overhangingContents() && !hasOverflowClip()) + setOverhangingContents(); + + m_height = height; + } + + // Unfurling marquees override with the furled height. + if (style()->overflowX() == OMARQUEE && m_layer && m_layer->marquee() && + m_layer->marquee()->isUnfurlMarquee() && !m_layer->marquee()->isHorizontal()) { + m_layer->marquee()->setEnd(m_height); + m_height = kMin(m_height, m_layer->marquee()->unfurlPos()); + } + +} + +int RenderBox::calcHeightUsing(const Length& h) +{ + int height = -1; + if (!h.isVariable()) { + if (h.isFixed()) + height = h.value(); + else if (h.isPercent()) + height = calcPercentageHeight(h); + if (height != -1) { + height = calcBoxHeight(height); + return height; + } + } + return height; +} + +int RenderBox::calcImplicitHeight() const { + assert(hasImplicitHeight()); + + RenderBlock* cb = containingBlock(); + // padding-box height + int ch = cb->height() - cb->borderTop() + cb->borderBottom(); + int top = style()->top().width(ch); + int bottom = style()->bottom().width(ch); + + return ch - top - bottom; +} + +int RenderBox::calcPercentageHeight(const Length& height, bool treatAsReplaced) const +{ + int result = -1; + RenderBlock* cb = containingBlock(); + // In quirk mode, table cells violate what the CSS spec says to do with heights. + if (cb->isTableCell() && style()->htmlHacks()) { + result = static_cast<RenderTableCell*>(cb)->cellPercentageHeight(); + } + + // Otherwise we only use our percentage height if our containing block had a specified + // height. + else if (cb->style()->height().isFixed()) + result = cb->calcContentHeight(cb->style()->height().value()); + else if (cb->style()->height().isPercent()) { + // We need to recur and compute the percentage height for our containing block. + result = cb->calcPercentageHeight(cb->style()->height(), treatAsReplaced); + if (result != -1) + result = cb->calcContentHeight(result); + } + else if (cb->isCanvas()) { + if (!canvas()->pagedMode()) + result = static_cast<RenderCanvas*>(cb)->viewportHeight(); + else + result = static_cast<RenderCanvas*>(cb)->height(); + result -= cb->style()->borderTopWidth() - cb->style()->borderBottomWidth(); + result -= cb->paddingTop() + cb->paddingBottom(); + } + else if (cb->isBody() && style()->htmlHacks() && + cb->style()->height().isVariable() && !cb->isFloatingOrPositioned()) { + int margins = cb->collapsedMarginTop() + cb->collapsedMarginBottom(); + int visHeight = canvas()->viewportHeight(); + RenderObject* p = cb->parent(); + result = visHeight - (margins + p->marginTop() + p->marginBottom() + + p->borderTop() + p->borderBottom() + + p->paddingTop() + p->paddingBottom()); + } + else if (cb->isRoot() && style()->htmlHacks() && cb->style()->height().isVariable()) { + int visHeight = canvas()->viewportHeight(); + result = visHeight - (marginTop() + marginBottom() + + borderTop() + borderBottom() + + paddingTop() + paddingBottom()); + } + else if (cb->isAnonymousBlock() || treatAsReplaced && style()->htmlHacks()) { + // IE quirk. + result = cb->calcPercentageHeight(cb->style()->height(), treatAsReplaced); + } + else if (cb->hasImplicitHeight()) { + result = cb->calcImplicitHeight(); + } + + if (result != -1) { + result = height.width(result); + if (cb->isTableCell() && style()->boxSizing() != BORDER_BOX) { + result -= (borderTop() + paddingTop() + borderBottom() + paddingBottom()); + result = kMax(0, result); + } + } + return result; +} + +short RenderBox::calcReplacedWidth() const +{ + int width = calcReplacedWidthUsing(Width); + int minW = calcReplacedWidthUsing(MinWidth); + int maxW = style()->maxWidth().value() == UNDEFINED ? width : calcReplacedWidthUsing(MaxWidth); + + if (width > maxW) + width = maxW; + + if (width < minW) + width = minW; + + return width; +} + +int RenderBox::calcReplacedWidthUsing(WidthType widthType) const +{ + Length w; + if (widthType == Width) + w = style()->width(); + else if (widthType == MinWidth) + w = style()->minWidth(); + else + w = style()->maxWidth(); + + switch (w.type()) { + case Fixed: + return w.value(); + case Percent: + { + const int cw = containingBlockWidth(); + if (cw > 0) { + int result = w.minWidth(cw); + return result; + } + } + // fall through + default: + return intrinsicWidth(); + } +} + +int RenderBox::calcReplacedHeight() const +{ + int height = calcReplacedHeightUsing(Height); + int minH = calcReplacedHeightUsing(MinHeight); + int maxH = style()->maxHeight().value() == UNDEFINED ? height : calcReplacedHeightUsing(MaxHeight); + + if (height > maxH) + height = maxH; + + if (height < minH) + height = minH; + + return height; +} + +int RenderBox::calcReplacedHeightUsing(HeightType heightType) const +{ + Length h; + if (heightType == Height) + h = style()->height(); + else if (heightType == MinHeight) + h = style()->minHeight(); + else + h = style()->maxHeight(); + switch( h.type() ) { + case Fixed: + return h.value(); + case Percent: + { + int th = calcPercentageHeight(h, true); + if (th != -1) + return th; + // fall through + } + default: + return intrinsicHeight(); + }; +} + +int RenderBox::availableHeight() const +{ + return availableHeightUsing(style()->height()); +} + +int RenderBox::availableHeightUsing(const Length& h) const +{ + if (h.isFixed()) + return calcContentHeight(h.value()); + + if (isCanvas()) + if (static_cast<const RenderCanvas*>(this)->pagedMode()) + return static_cast<const RenderCanvas*>(this)->pageHeight(); + else + return static_cast<const RenderCanvas*>(this)->viewportHeight(); + + // We need to stop here, since we don't want to increase the height of the table + // artificially. We're going to rely on this cell getting expanded to some new + // height, and then when we lay out again we'll use the calculation below. + if (isTableCell() && (h.isVariable() || h.isPercent())) { + const RenderTableCell* tableCell = static_cast<const RenderTableCell*>(this); + return tableCell->cellPercentageHeight() - + (borderTop()+borderBottom()+paddingTop()+paddingBottom()); + } + + if (h.isPercent()) + return calcContentHeight(h.width(containingBlock()->availableHeight())); + + // Check for implicit height + if (hasImplicitHeight()) + return calcImplicitHeight(); + + return containingBlock()->availableHeight(); +} + +int RenderBox::availableWidth() const +{ + return availableWidthUsing(style()->width()); +} + +int RenderBox::availableWidthUsing(const Length& w) const +{ + if (w.isFixed()) + return calcContentWidth(w.value()); + + if (isCanvas()) + return static_cast<const RenderCanvas*>(this)->viewportWidth(); + + if (w.isPercent()) + return calcContentWidth(w.width(containingBlock()->availableWidth())); + + return containingBlock()->availableWidth(); +} + +void RenderBox::calcVerticalMargins() +{ + if( isTableCell() ) { + // table margins are basically infinite + m_marginTop = TABLECELLMARGIN; + m_marginBottom = TABLECELLMARGIN; + return; + } + + Length tm = style()->marginTop(); + Length bm = style()->marginBottom(); + + // margins are calculated with respect to the _width_ of + // the containing block (8.3) + int cw = containingBlock()->contentWidth(); + + m_marginTop = tm.minWidth(cw); + m_marginBottom = bm.minWidth(cw); +} + +void RenderBox::setStaticX(short staticX) +{ + m_staticX = staticX; +} + +void RenderBox::setStaticY(int staticY) +{ + m_staticY = staticY; +} + +void RenderBox::calcAbsoluteHorizontal() +{ + if (isReplaced()) { + calcAbsoluteHorizontalReplaced(); + return; + } + + // QUESTIONS + // FIXME 1: Which RenderObject's 'direction' property should used: the + // containing block (cb) as the spec seems to imply, the parent (parent()) as + // was previously done in calculating the static distances, or ourself, which + // was also previously done for deciding what to override when you had + // over-constrained margins? Also note that the container block is used + // in similar situations in other parts of the RenderBox class (see calcWidth() + // and calcHorizontalMargins()). For now we are using the parent for quirks + // mode and the containing block for strict mode. + + // FIXME 2: Can perhaps optimize out cases when max-width/min-width are greater + // than or less than the computed m_width. Be careful of box-sizing and + // percentage issues. + + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.3.7 "Absolutely positioned, non-replaced elements" + // <http://www.w3.org/TR/CSS21/visudet.html#abs-non-replaced-width> + // (block-style-comments in this function and in calcAbsoluteHorizontalValues() + // correspond to text from the spec) + + + // We don't use containingBlock(), since we may be positioned by an enclosing + // relative positioned inline. + const RenderObject* containerBlock = container(); + + // FIXME: This is incorrect for cases where the container block is a relatively + // positioned inline. + const int containerWidth = containingBlockWidth() + containerBlock->paddingLeft() + containerBlock->paddingRight(); + + // To match WinIE, in quirks mode use the parent's 'direction' property + // instead of the the container block's. + EDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction(); + + const int bordersPlusPadding = borderLeft() + borderRight() + paddingLeft() + paddingRight(); + const Length marginLeft = style()->marginLeft(); + const Length marginRight = style()->marginRight(); + Length left = style()->left(); + Length right = style()->right(); + + /*---------------------------------------------------------------------------*\ + * For the purposes of this section and the next, the term "static position" + * (of an element) refers, roughly, to the position an element would have had + * in the normal flow. More precisely: + * + * * The static position for 'left' is the distance from the left edge of the + * containing block to the left margin edge of a hypothetical box that would + * have been the first box of the element if its 'position' property had + * been 'static' and 'float' had been 'none'. The value is negative if the + * hypothetical box is to the left of the containing block. + * * The static position for 'right' is the distance from the right edge of the + * containing block to the right margin edge of the same hypothetical box as + * above. The value is positive if the hypothetical box is to the left of the + * containing block's edge. + * + * But rather than actually calculating the dimensions of that hypothetical box, + * user agents are free to make a guess at its probable position. + * + * For the purposes of calculating the static position, the containing block of + * fixed positioned elements is the initial containing block instead of the + * viewport, and all scrollable boxes should be assumed to be scrolled to their + * origin. + \*---------------------------------------------------------------------------*/ + + // Calculate the static distance if needed. + if (left.isVariable() && right.isVariable()) { + if (containerDirection == LTR) { + // 'm_staticX' should already have been set through layout of the parent. + int staticPosition = m_staticX - containerBlock->borderLeft(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) + staticPosition += po->xPos(); + left = Length(staticPosition, Fixed); + } else { + RenderObject* po = parent(); + // 'm_staticX' should already have been set through layout of the parent. + int staticPosition = m_staticX + containerWidth + containerBlock->borderRight() - po->width(); + for (; po && po != containerBlock; po = po->parent()) + staticPosition -= po->xPos(); + right = Length(staticPosition, Fixed); + } + } + + // Calculate constraint equation values for 'width' case. + calcAbsoluteHorizontalValues(style()->width(), containerBlock, containerDirection, + containerWidth, bordersPlusPadding, + left, right, marginLeft, marginRight, + m_width, m_marginLeft, m_marginRight, m_x); + // Calculate constraint equation values for 'max-width' case.calcContentWidth(width.width(containerWidth)); + if (style()->maxWidth().value() != UNDEFINED) { + short maxWidth; + short maxMarginLeft; + short maxMarginRight; + short maxXPos; + + calcAbsoluteHorizontalValues(style()->maxWidth(), containerBlock, containerDirection, + containerWidth, bordersPlusPadding, + left, right, marginLeft, marginRight, + maxWidth, maxMarginLeft, maxMarginRight, maxXPos); + + if (m_width > maxWidth) { + m_width = maxWidth; + m_marginLeft = maxMarginLeft; + m_marginRight = maxMarginRight; + m_x = maxXPos; + } + } + + // Calculate constraint equation values for 'min-width' case. + if (style()->minWidth().value()) { + short minWidth; + short minMarginLeft; + short minMarginRight; + short minXPos; + + calcAbsoluteHorizontalValues(style()->minWidth(), containerBlock, containerDirection, + containerWidth, bordersPlusPadding, + left, right, marginLeft, marginRight, + minWidth, minMarginLeft, minMarginRight, minXPos); + + if (m_width < minWidth) { + m_width = minWidth; + m_marginLeft = minMarginLeft; + m_marginRight = minMarginRight; + m_x = minXPos; + } + } + + // Put m_width into correct form. + m_width += bordersPlusPadding; +} + +void RenderBox::calcAbsoluteHorizontalValues(Length width, const RenderObject* containerBlock, EDirection containerDirection, + const int containerWidth, const int bordersPlusPadding, + const Length left, const Length right, const Length marginLeft, const Length marginRight, + short& widthValue, short& marginLeftValue, short& marginRightValue, short& xPos) +{ + // 'left' and 'right' cannot both be 'auto' because one would of been + // converted to the static postion already + assert(!(left.isVariable() && right.isVariable())); + + int leftValue = 0; + + bool widthIsAuto = width.isVariable(); + bool leftIsAuto = left.isVariable(); + bool rightIsAuto = right.isVariable(); + + if (!leftIsAuto && !widthIsAuto && !rightIsAuto) { + /*-----------------------------------------------------------------------*\ + * If none of the three is 'auto': If both 'margin-left' and 'margin- + * right' are 'auto', solve the equation under the extra constraint that + * the two margins get equal values, unless this would make them negative, + * in which case when direction of the containing block is 'ltr' ('rtl'), + * set 'margin-left' ('margin-right') to zero and solve for 'margin-right' + * ('margin-left'). If one of 'margin-left' or 'margin-right' is 'auto', + * solve the equation for that value. If the values are over-constrained, + * ignore the value for 'left' (in case the 'direction' property of the + * containing block is 'rtl') or 'right' (in case 'direction' is 'ltr') + * and solve for that value. + \*-----------------------------------------------------------------------*/ + // NOTE: It is not necessary to solve for 'right' in the over constrained + // case because the value is not used for any further calculations. + + leftValue = left.width(containerWidth); + widthValue = calcContentWidth(width.width(containerWidth)); + + const int availableSpace = containerWidth - (leftValue + widthValue + right.width(containerWidth) + bordersPlusPadding); + + // Margins are now the only unknown + if (marginLeft.isVariable() && marginRight.isVariable()) { + // Both margins auto, solve for equality + if (availableSpace >= 0) { + marginLeftValue = availableSpace / 2; // split the diference + marginRightValue = availableSpace - marginLeftValue; // account for odd valued differences + } else { + // see FIXME 1 + if (containerDirection == LTR) { + marginLeftValue = 0; + marginRightValue = availableSpace; // will be negative + } else { + marginLeftValue = availableSpace; // will be negative + marginRightValue = 0; + } + } + } else if (marginLeft.isVariable()) { + // Solve for left margin + marginRightValue = marginRight.width(containerWidth); + marginLeftValue = availableSpace - marginRightValue; + } else if (marginRight.isVariable()) { + // Solve for right margin + marginLeftValue = marginLeft.width(containerWidth); + marginRightValue = availableSpace - marginLeftValue; + } else { + // Over-constrained, solve for left if direction is RTL + marginLeftValue = marginLeft.width(containerWidth); + marginRightValue = marginRight.width(containerWidth); + + // see FIXME 1 -- used to be "this->style()->direction()" + if (containerDirection == RTL) + leftValue = (availableSpace + leftValue) - marginLeftValue - marginRightValue; + } + } else { + /*--------------------------------------------------------------------*\ + * Otherwise, set 'auto' values for 'margin-left' and 'margin-right' + * to 0, and pick the one of the following six rules that applies. + * + * 1. 'left' and 'width' are 'auto' and 'right' is not 'auto', then the + * width is shrink-to-fit. Then solve for 'left' + * + * OMIT RULE 2 AS IT SHOULD NEVER BE HIT + * ------------------------------------------------------------------ + * 2. 'left' and 'right' are 'auto' and 'width' is not 'auto', then if + * the 'direction' property of the containing block is 'ltr' set + * 'left' to the static position, otherwise set 'right' to the + * static position. Then solve for 'left' (if 'direction is 'rtl') + * or 'right' (if 'direction' is 'ltr'). + * ------------------------------------------------------------------ + * + * 3. 'width' and 'right' are 'auto' and 'left' is not 'auto', then the + * width is shrink-to-fit . Then solve for 'right' + * 4. 'left' is 'auto', 'width' and 'right' are not 'auto', then solve + * for 'left' + * 5. 'width' is 'auto', 'left' and 'right' are not 'auto', then solve + * for 'width' + * 6. 'right' is 'auto', 'left' and 'width' are not 'auto', then solve + * for 'right' + * + * Calculation of the shrink-to-fit width is similar to calculating the + * width of a table cell using the automatic table layout algorithm. + * Roughly: calculate the preferred width by formatting the content + * without breaking lines other than where explicit line breaks occur, + * and also calculate the preferred minimum width, e.g., by trying all + * possible line breaks. CSS 2.1 does not define the exact algorithm. + * Thirdly, calculate the available width: this is found by solving + * for 'width' after setting 'left' (in case 1) or 'right' (in case 3) + * to 0. + * + * Then the shrink-to-fit width is: + * kMin(kMax(preferred minimum width, available width), preferred width). + \*--------------------------------------------------------------------*/ + // NOTE: For rules 3 and 6 it is not necessary to solve for 'right' + // because the value is not used for any further calculations. + + // Calculate margins, 'auto' margins are ignored. + marginLeftValue = marginLeft.minWidth(containerWidth); + marginRightValue = marginRight.minWidth(containerWidth); + + const int availableSpace = containerWidth - (marginLeftValue + marginRightValue + bordersPlusPadding); + + // FIXME: Is there a faster way to find the correct case? + // Use rule/case that applies. + if (leftIsAuto && widthIsAuto && !rightIsAuto) { + // RULE 1: (use shrink-to-fit for width, and solve of left) + int rightValue = right.width(containerWidth); + + // FIXME: would it be better to have shrink-to-fit in one step? + int preferredWidth = m_maxWidth - bordersPlusPadding; + int preferredMinWidth = m_minWidth - bordersPlusPadding; + int availableWidth = availableSpace - rightValue; + widthValue = kMin(kMax(preferredMinWidth, availableWidth), preferredWidth); + leftValue = availableSpace - (widthValue + rightValue); + } else if (!leftIsAuto && widthIsAuto && rightIsAuto) { + // RULE 3: (use shrink-to-fit for width, and no need solve of right) + leftValue = left.width(containerWidth); + + // FIXME: would it be better to have shrink-to-fit in one step? + int preferredWidth = m_maxWidth - bordersPlusPadding; + int preferredMinWidth = m_minWidth - bordersPlusPadding; + int availableWidth = availableSpace - leftValue; + widthValue = kMin(kMax(preferredMinWidth, availableWidth), preferredWidth); + } else if (leftIsAuto && !width.isVariable() && !rightIsAuto) { + // RULE 4: (solve for left) + widthValue = calcContentWidth(width.width(containerWidth)); + leftValue = availableSpace - (widthValue + right.width(containerWidth)); + } else if (!leftIsAuto && widthIsAuto && !rightIsAuto) { + // RULE 5: (solve for width) + leftValue = left.width(containerWidth); + widthValue = availableSpace - (leftValue + right.width(containerWidth)); + } else if (!leftIsAuto&& !widthIsAuto && rightIsAuto) { + // RULE 6: (no need solve for right) + leftValue = left.width(containerWidth); + widthValue = calcContentWidth(width.width(containerWidth)); + } + } + + // Use computed values to calculate the horizontal position. + xPos = leftValue + marginLeftValue + containerBlock->borderLeft(); +} + + +void RenderBox::calcAbsoluteVertical() +{ + if (isReplaced()) { + calcAbsoluteVerticalReplaced(); + return; + } + + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.6.4 "Absolutely positioned, non-replaced elements" + // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-non-replaced-height> + // (block-style-comments in this function and in calcAbsoluteVerticalValues() + // correspond to text from the spec) + + + // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. + const RenderObject* containerBlock = container(); + const int containerHeight = containerBlock->height() - containerBlock->borderTop() - containerBlock->borderBottom(); + + const int bordersPlusPadding = borderTop() + borderBottom() + paddingTop() + paddingBottom(); + const Length marginTop = style()->marginTop(); + const Length marginBottom = style()->marginBottom(); + Length top = style()->top(); + Length bottom = style()->bottom(); + + /*---------------------------------------------------------------------------*\ + * For the purposes of this section and the next, the term "static position" + * (of an element) refers, roughly, to the position an element would have had + * in the normal flow. More precisely, the static position for 'top' is the + * distance from the top edge of the containing block to the top margin edge + * of a hypothetical box that would have been the first box of the element if + * its 'position' property had been 'static' and 'float' had been 'none'. The + * value is negative if the hypothetical box is above the containing block. + * + * But rather than actually calculating the dimensions of that hypothetical + * box, user agents are free to make a guess at its probable position. + * + * For the purposes of calculating the static position, the containing block + * of fixed positioned elements is the initial containing block instead of + * the viewport. + \*---------------------------------------------------------------------------*/ + + // Calculate the static distance if needed. + if (top.isVariable() && bottom.isVariable()) { + // m_staticY should already have been set through layout of the parent() + int staticTop = m_staticY - containerBlock->borderTop(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { + staticTop += po->yPos(); + } + top.setValue(Fixed, staticTop); + } + + + int height; // Needed to compute overflow. + + // Calculate constraint equation values for 'height' case. + calcAbsoluteVerticalValues(style()->height(), containerBlock, containerHeight, bordersPlusPadding, + top, bottom, marginTop, marginBottom, + height, m_marginTop, m_marginBottom, m_y); + + // Avoid doing any work in the common case (where the values of min-height and max-height are their defaults). + // see FIXME 2 + + // Calculate constraint equation values for 'max-height' case. + if (style()->maxHeight().value() != UNDEFINED) { + int maxHeight; + short maxMarginTop; + short maxMarginBottom; + int maxYPos; + + calcAbsoluteVerticalValues(style()->maxHeight(), containerBlock, containerHeight, bordersPlusPadding, + top, bottom, marginTop, marginBottom, + maxHeight, maxMarginTop, maxMarginBottom, maxYPos); + + if (height > maxHeight) { + height = maxHeight; + m_marginTop = maxMarginTop; + m_marginBottom = maxMarginBottom; + m_y = maxYPos; + } + } + + // Calculate constraint equation values for 'min-height' case. + if (style()->minHeight().value()) { + int minHeight; + short minMarginTop; + short minMarginBottom; + int minYPos; + + calcAbsoluteVerticalValues(style()->minHeight(), containerBlock, containerHeight, bordersPlusPadding, + top, bottom, marginTop, marginBottom, + minHeight, minMarginTop, minMarginBottom, minYPos); + + if (height < minHeight) { + height = minHeight; + m_marginTop = minMarginTop; + m_marginBottom = minMarginBottom; + m_y = minYPos; + } + } + + height += bordersPlusPadding; + + // Set final height value. + m_height = height; +} + +void RenderBox::calcAbsoluteVerticalValues(Length height, const RenderObject* containerBlock, + const int containerHeight, const int bordersPlusPadding, + const Length top, const Length bottom, const Length marginTop, const Length marginBottom, + int& heightValue, short& marginTopValue, short& marginBottomValue, int& yPos) +{ + // 'top' and 'bottom' cannot both be 'auto' because 'top would of been + // converted to the static position in calcAbsoluteVertical() + assert(!(top.isVariable() && bottom.isVariable())); + + int contentHeight = m_height - bordersPlusPadding; + + int topValue = 0; + + bool heightIsAuto = height.isVariable(); + bool topIsAuto = top.isVariable(); + bool bottomIsAuto = bottom.isVariable(); + + if (isTable() && heightIsAuto) { + // Height is never unsolved for tables. "auto" means shrink to fit. + // Use our height instead. + heightValue = contentHeight; + heightIsAuto = false; + } else if (!heightIsAuto) { + heightValue = calcContentHeight(height.width(containerHeight)); + if (contentHeight > heightValue) { + if (!isTable()) + contentHeight = heightValue; + else + heightValue = contentHeight; + } + } + + + if (!topIsAuto && !heightIsAuto && !bottomIsAuto) { + /*-----------------------------------------------------------------------*\ + * If none of the three are 'auto': If both 'margin-top' and 'margin- + * bottom' are 'auto', solve the equation under the extra constraint that + * the two margins get equal values. If one of 'margin-top' or 'margin- + * bottom' is 'auto', solve the equation for that value. If the values + * are over-constrained, ignore the value for 'bottom' and solve for that + * value. + \*-----------------------------------------------------------------------*/ + // NOTE: It is not necessary to solve for 'bottom' in the over constrained + // case because the value is not used for any further calculations. + + topValue = top.width(containerHeight); + + const int availableSpace = containerHeight - (topValue + heightValue + bottom.width(containerHeight) + bordersPlusPadding); + + // Margins are now the only unknown + if (marginTop.isVariable() && marginBottom.isVariable()) { + // Both margins auto, solve for equality + // NOTE: This may result in negative values. + marginTopValue = availableSpace / 2; // split the diference + marginBottomValue = availableSpace - marginTopValue; // account for odd valued differences + } else if (marginTop.isVariable()) { + // Solve for top margin + marginBottomValue = marginBottom.width(containerHeight); + marginTopValue = availableSpace - marginBottomValue; + } else if (marginBottom.isVariable()) { + // Solve for bottom margin + marginTopValue = marginTop.width(containerHeight); + marginBottomValue = availableSpace - marginTopValue; + } else { + // Over-constrained, (no need solve for bottom) + marginTopValue = marginTop.width(containerHeight); + marginBottomValue = marginBottom.width(containerHeight); + } + } else { + /*--------------------------------------------------------------------*\ + * Otherwise, set 'auto' values for 'margin-top' and 'margin-bottom' + * to 0, and pick the one of the following six rules that applies. + * + * 1. 'top' and 'height' are 'auto' and 'bottom' is not 'auto', then + * the height is based on the content, and solve for 'top'. + * + * OMIT RULE 2 AS IT SHOULD NEVER BE HIT + * ------------------------------------------------------------------ + * 2. 'top' and 'bottom' are 'auto' and 'height' is not 'auto', then + * set 'top' to the static position, and solve for 'bottom'. + * ------------------------------------------------------------------ + * + * 3. 'height' and 'bottom' are 'auto' and 'top' is not 'auto', then + * the height is based on the content, and solve for 'bottom'. + * 4. 'top' is 'auto', 'height' and 'bottom' are not 'auto', and + * solve for 'top'. + * 5. 'height' is 'auto', 'top' and 'bottom' are not 'auto', and + * solve for 'height'. + * 6. 'bottom' is 'auto', 'top' and 'height' are not 'auto', and + * solve for 'bottom'. + \*--------------------------------------------------------------------*/ + // NOTE: For rules 3 and 6 it is not necessary to solve for 'bottom' + // because the value is not used for any further calculations. + + // Calculate margins, 'auto' margins are ignored. + marginTopValue = marginTop.minWidth(containerHeight); + marginBottomValue = marginBottom.minWidth(containerHeight); + + const int availableSpace = containerHeight - (marginTopValue + marginBottomValue + bordersPlusPadding); + + // Use rule/case that applies. + if (topIsAuto && heightIsAuto && !bottomIsAuto) { + // RULE 1: (height is content based, solve of top) + heightValue = contentHeight; + topValue = availableSpace - (heightValue + bottom.width(containerHeight)); + } + else if (topIsAuto && !heightIsAuto && bottomIsAuto) { + // RULE 2: (shouldn't happen) + } + else if (!topIsAuto && heightIsAuto && bottomIsAuto) { + // RULE 3: (height is content based, no need solve of bottom) + heightValue = contentHeight; + topValue = top.width(containerHeight); + } else if (topIsAuto && !heightIsAuto && !bottomIsAuto) { + // RULE 4: (solve of top) + topValue = availableSpace - (heightValue + bottom.width(containerHeight)); + } else if (!topIsAuto && heightIsAuto && !bottomIsAuto) { + // RULE 5: (solve of height) + topValue = top.width(containerHeight); + heightValue = kMax(0, availableSpace - (topValue + bottom.width(containerHeight))); + } else if (!topIsAuto && !heightIsAuto && bottomIsAuto) { + // RULE 6: (no need solve of bottom) + topValue = top.width(containerHeight); + } + } + + // Use computed values to calculate the vertical position. + yPos = topValue + marginTopValue + containerBlock->borderTop(); +} + +void RenderBox::calcAbsoluteHorizontalReplaced() +{ + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.3.8 "Absolutly positioned, replaced elements" + // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-width> + // (block-style-comments in this function correspond to text from the spec and + // the numbers correspond to numbers in spec) + + // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. + const RenderObject* containerBlock = container(); + + // FIXME: This is incorrect for cases where the container block is a relatively + // positioned inline. + const int containerWidth = containingBlockWidth() + containerBlock->paddingLeft() + containerBlock->paddingRight(); + + // To match WinIE, in quirks mode use the parent's 'direction' property + // instead of the the container block's. + EDirection containerDirection = (style()->htmlHacks()) ? parent()->style()->direction() : containerBlock->style()->direction(); + + // Variables to solve. + Length left = style()->left(); + Length right = style()->right(); + Length marginLeft = style()->marginLeft(); + Length marginRight = style()->marginRight(); + + + /*-----------------------------------------------------------------------*\ + * 1. The used value of 'width' is determined as for inline replaced + * elements. + \*-----------------------------------------------------------------------*/ + // NOTE: This value of width is FINAL in that the min/max width calculations + // are dealt with in calcReplacedWidth(). This means that the steps to produce + // correct max/min in the non-replaced version, are not necessary. + m_width = calcReplacedWidth() + borderLeft() + borderRight() + paddingLeft() + paddingRight(); + const int availableSpace = containerWidth - m_width; + + /*-----------------------------------------------------------------------*\ + * 2. If both 'left' and 'right' have the value 'auto', then if 'direction' + * of the containing block is 'ltr', set 'left' to the static position; + * else if 'direction' is 'rtl', set 'right' to the static position. + \*-----------------------------------------------------------------------*/ + if (left.isVariable() && right.isVariable()) { + // see FIXME 1 + if (containerDirection == LTR) { + // 'm_staticX' should already have been set through layout of the parent. + int staticPosition = m_staticX - containerBlock->borderLeft(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) + staticPosition += po->xPos(); + left.setValue(Fixed, staticPosition); + } else { + RenderObject* po = parent(); + // 'm_staticX' should already have been set through layout of the parent. + int staticPosition = m_staticX + containerWidth + containerBlock->borderRight() - po->width(); + for (; po && po != containerBlock; po = po->parent()) + staticPosition -= po->xPos(); + right.setValue(Fixed, staticPosition); + } + } + + /*-----------------------------------------------------------------------*\ + * 3. If 'left' or 'right' are 'auto', replace any 'auto' on 'margin-left' + * or 'margin-right' with '0'. + \*-----------------------------------------------------------------------*/ + if (left.isVariable() || right.isVariable()) { + if (marginLeft.isVariable()) + marginLeft.setValue(Fixed, 0); + if (marginRight.isVariable()) + marginRight.setValue(Fixed, 0); + } + + /*-----------------------------------------------------------------------*\ + * 4. If at this point both 'margin-left' and 'margin-right' are still + * 'auto', solve the equation under the extra constraint that the two + * margins must get equal values, unless this would make them negative, + * in which case when the direction of the containing block is 'ltr' + * ('rtl'), set 'margin-left' ('margin-right') to zero and solve for + * 'margin-right' ('margin-left'). + \*-----------------------------------------------------------------------*/ + int leftValue = 0; + int rightValue = 0; + + if (marginLeft.isVariable() && marginRight.isVariable()) { + // 'left' and 'right' cannot be 'auto' due to step 3 + assert(!(left.isVariable() && right.isVariable())); + + leftValue = left.width(containerWidth); + rightValue = right.width(containerWidth); + + int difference = availableSpace - (leftValue + rightValue); + if (difference > 0) { + m_marginLeft = difference / 2; // split the diference + m_marginRight = difference - m_marginLeft; // account for odd valued differences + } else { + // see FIXME 1 + if (containerDirection == LTR) { + m_marginLeft = 0; + m_marginRight = difference; // will be negative + } else { + m_marginLeft = difference; // will be negative + m_marginRight = 0; + } + } + + /*-----------------------------------------------------------------------*\ + * 5. If at this point there is an 'auto' left, solve the equation for + * that value. + \*-----------------------------------------------------------------------*/ + } else if (left.isVariable()) { + m_marginLeft = marginLeft.width(containerWidth); + m_marginRight = marginRight.width(containerWidth); + rightValue = right.width(containerWidth); + + // Solve for 'left' + leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight); + } else if (right.isVariable()) { + m_marginLeft = marginLeft.width(containerWidth); + m_marginRight = marginRight.width(containerWidth); + leftValue = left.width(containerWidth); + + // Solve for 'right' + rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight); + } else if (marginLeft.isVariable()) { + m_marginRight = marginRight.width(containerWidth); + leftValue = left.width(containerWidth); + rightValue = right.width(containerWidth); + + // Solve for 'margin-left' + m_marginLeft = availableSpace - (leftValue + rightValue + m_marginRight); + } else if (marginRight.isVariable()) { + m_marginLeft = marginLeft.width(containerWidth); + leftValue = left.width(containerWidth); + rightValue = right.width(containerWidth); + + // Solve for 'margin-right' + m_marginRight = availableSpace - (leftValue + rightValue + m_marginLeft); + } + + /*-----------------------------------------------------------------------*\ + * 6. If at this point the values are over-constrained, ignore the value + * for either 'left' (in case the 'direction' property of the + * containing block is 'rtl') or 'right' (in case 'direction' is + * 'ltr') and solve for that value. + \*-----------------------------------------------------------------------*/ + else { + m_marginLeft = marginLeft.width(containerWidth); + m_marginRight = marginRight.width(containerWidth); + if (containerDirection == LTR) { + leftValue = left.width(containerWidth); + rightValue = availableSpace - (leftValue + m_marginLeft + m_marginRight); + } + else { + rightValue = right.width(containerWidth); + leftValue = availableSpace - (rightValue + m_marginLeft + m_marginRight); + } + } + + int totalWidth = m_width + leftValue + rightValue + m_marginLeft + m_marginRight; + if (totalWidth > containerWidth && (containerDirection == RTL)) + leftValue = containerWidth - (totalWidth - leftValue); + + // Use computed values to calculate the horizontal position. + m_x = leftValue + m_marginLeft + containerBlock->borderLeft(); +} + +void RenderBox::calcAbsoluteVerticalReplaced() +{ + // The following is based off of the W3C Working Draft from April 11, 2006 of + // CSS 2.1: Section 10.6.5 "Absolutly positioned, replaced elements" + // <http://www.w3.org/TR/2005/WD-CSS21-20050613/visudet.html#abs-replaced-height> + // (block-style-comments in this function correspond to text from the spec and + // the numbers correspond to numbers in spec) + + // We don't use containingBlock(), since we may be positioned by an enclosing relpositioned inline. + const RenderObject* containerBlock = container(); + const int containerHeight = containerBlock->height() - containerBlock->borderTop() - containerBlock->borderBottom(); + + // Variables to solve. + Length top = style()->top(); + Length bottom = style()->bottom(); + Length marginTop = style()->marginTop(); + Length marginBottom = style()->marginBottom(); + + + /*-----------------------------------------------------------------------*\ + * 1. The used value of 'height' is determined as for inline replaced + * elements. + \*-----------------------------------------------------------------------*/ + // NOTE: This value of height is FINAL in that the min/max height calculations + // are dealt with in calcReplacedHeight(). This means that the steps to produce + // correct max/min in the non-replaced version, are not necessary. + m_height = calcReplacedHeight() + borderTop() + borderBottom() + paddingTop() + paddingBottom(); + const int availableSpace = containerHeight - m_height; + + /*-----------------------------------------------------------------------*\ + * 2. If both 'top' and 'bottom' have the value 'auto', replace 'top' + * with the element's static position. + \*-----------------------------------------------------------------------*/ + if (top.isVariable() && bottom.isVariable()) { + // m_staticY should already have been set through layout of the parent(). + int staticTop = m_staticY - containerBlock->borderTop(); + for (RenderObject* po = parent(); po && po != containerBlock; po = po->parent()) { + staticTop += po->yPos(); + } + top.setValue(Fixed, staticTop); + } + + /*-----------------------------------------------------------------------*\ + * 3. If 'bottom' is 'auto', replace any 'auto' on 'margin-top' or + * 'margin-bottom' with '0'. + \*-----------------------------------------------------------------------*/ + // FIXME: The spec. says that this step should only be taken when bottom is + // auto, but if only top is auto, this makes step 4 impossible. + if (top.isVariable() || bottom.isVariable()) { + if (marginTop.isVariable()) + marginTop.setValue(Fixed, 0); + if (marginBottom.isVariable()) + marginBottom.setValue(Fixed, 0); + } + + /*-----------------------------------------------------------------------*\ + * 4. If at this point both 'margin-top' and 'margin-bottom' are still + * 'auto', solve the equation under the extra constraint that the two + * margins must get equal values. + \*-----------------------------------------------------------------------*/ + int topValue = 0; + int bottomValue = 0; + + if (marginTop.isVariable() && marginBottom.isVariable()) { + // 'top' and 'bottom' cannot be 'auto' due to step 2 and 3 combinded. + assert(!(top.isVariable() || bottom.isVariable())); + + topValue = top.width(containerHeight); + bottomValue = bottom.width(containerHeight); + + int difference = availableSpace - (topValue + bottomValue); + // NOTE: This may result in negative values. + m_marginTop = difference / 2; // split the difference + m_marginBottom = difference - m_marginTop; // account for odd valued differences + + /*-----------------------------------------------------------------------*\ + * 5. If at this point there is only one 'auto' left, solve the equation + * for that value. + \*-----------------------------------------------------------------------*/ + } else if (top.isVariable()) { + m_marginTop = marginTop.width(containerHeight); + m_marginBottom = marginBottom.width(containerHeight); + bottomValue = bottom.width(containerHeight); + + // Solve for 'top' + topValue = availableSpace - (bottomValue + m_marginTop + m_marginBottom); + } else if (bottom.isVariable()) { + m_marginTop = marginTop.width(containerHeight); + m_marginBottom = marginBottom.width(containerHeight); + topValue = top.width(containerHeight); + + // Solve for 'bottom' + // NOTE: It is not necessary to solve for 'bottom' because we don't ever + // use the value. + } else if (marginTop.isVariable()) { + m_marginBottom = marginBottom.width(containerHeight); + topValue = top.width(containerHeight); + bottomValue = bottom.width(containerHeight); + + // Solve for 'margin-top' + m_marginTop = availableSpace - (topValue + bottomValue + m_marginBottom); + } else if (marginBottom.isVariable()) { + m_marginTop = marginTop.width(containerHeight); + topValue = top.width(containerHeight); + bottomValue = bottom.width(containerHeight); + + // Solve for 'margin-bottom' + m_marginBottom = availableSpace - (topValue + bottomValue + m_marginTop); + } + + /*-----------------------------------------------------------------------*\ + * 6. If at this point the values are over-constrained, ignore the value + * for 'bottom' and solve for that value. + \*-----------------------------------------------------------------------*/ + else { + m_marginTop = marginTop.width(containerHeight); + m_marginBottom = marginBottom.width(containerHeight); + topValue = top.width(containerHeight); + + // Solve for 'bottom' + // NOTE: It is not necessary to solve for 'bottom' because we don't ever + // use the value. + } + + // Use computed values to calculate the vertical position. + m_y = topValue + m_marginTop + containerBlock->borderTop(); +} + +int RenderBox::highestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +{ + return includeSelf ? 0 : m_height; +} + +int RenderBox::lowestPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +{ + return includeSelf ? m_height : 0; +} + +int RenderBox::rightmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +{ + return includeSelf ? m_width : 0; +} + +int RenderBox::leftmostPosition(bool /*includeOverflowInterior*/, bool includeSelf) const +{ + return includeSelf ? 0 : m_width; +} + +int RenderBox::pageTopAfter(int y) const +{ + RenderObject* cb = container(); + if (cb) + return cb->pageTopAfter(y+yPos()) - yPos(); + else + return 0; +} + +int RenderBox::crossesPageBreak(int t, int b) const +{ + RenderObject* cb = container(); + if (cb) + return cb->crossesPageBreak(yPos()+t, yPos()+b); + else + return false; +} + +void RenderBox::caretPos(int /*offset*/, int flags, int &_x, int &_y, int &width, int &height) +{ +#if 0 + _x = -1; + + // propagate it downwards to its children, someone will feel responsible + RenderObject *child = firstChild(); +// if (child) kdDebug(6040) << "delegating caretPos to " << child->renderName() << endl; + if (child) child->caretPos(offset, override, _x, _y, width, height); + + // if not, use the extents of this box. offset 0 means left, offset 1 means + // right + if (_x == -1) { + //kdDebug(6040) << "no delegation" << endl; + _x = xPos() + (offset == 0 ? 0 : m_width); + _y = yPos(); + height = m_height; + width = override && offset == 0 ? m_width : 1; + + // If height of box is smaller than font height, use the latter one, + // otherwise the caret might become invisible. + // FIXME: ignoring :first-line, missing good reason to take care of + int fontHeight = style()->fontMetrics().height(); + if (fontHeight > height) + height = fontHeight; + + int absx, absy; + + RenderObject *cb = containingBlock(); + + if (cb && cb != this && cb->absolutePosition(absx,absy)) { + //kdDebug(6040) << "absx=" << absx << " absy=" << absy << endl; + _x += absx; + _y += absy; + } else { + // we don't know our absolute position, and there is no point returning + // just a relative one + _x = _y = -1; + } + } +#endif + + _x = xPos(); + _y = yPos(); +// kdDebug(6040) << "_x " << _x << " _y " << _y << endl; + width = 1; // no override is indicated in boxes + + RenderBlock *cb = containingBlock(); + + // Place caret outside the border + if (flags & CFOutside) { + + RenderStyle *s = element() && element()->parent() + && element()->parent()->renderer() + ? element()->parent()->renderer()->style() + : cb->style(); + + const QFontMetrics &fm = s->fontMetrics(); + height = fm.height(); + + bool rtl = s->direction() == RTL; + bool outsideEnd = flags & CFOutsideEnd; + + if (outsideEnd) { + _x += this->width(); + } else { + _x--; + } + + int hl = fm.leading() / 2; + if (!isReplaced() || style()->display() == BLOCK) { + if (!outsideEnd ^ rtl) + _y -= hl; + else + _y += kMax(this->height() - fm.ascent() - hl, 0); + } else { + _y += baselinePosition(false) - fm.ascent() - hl; + } + + // Place caret inside the element + } else { + const QFontMetrics &fm = style()->fontMetrics(); + height = fm.height(); + + RenderStyle *s = style(); + + _x += borderLeft() + paddingLeft(); + _y += borderTop() + paddingTop(); + + // ### regard direction + switch (s->textAlign()) { + case LEFT: + case KHTML_LEFT: + case TAAUTO: // ### find out what this does + case JUSTIFY: + break; + case CENTER: + case KHTML_CENTER: + _x += contentWidth() / 2; + break; + case KHTML_RIGHT: + case RIGHT: + _x += contentWidth(); + break; + } + } + + int absx, absy; + if (cb && cb != this && cb->absolutePosition(absx,absy)) { +// kdDebug(6040) << "absx=" << absx << " absy=" << absy << endl; + _x += absx; + _y += absy; + } else { + // we don't know our absolute position, and there is no point returning + // just a relative one + _x = _y = -1; + } +} + +#undef DEBUG_LAYOUT |