summaryrefslogtreecommitdiffstats
path: root/tdehtml/rendering/render_box.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tdehtml/rendering/render_box.cpp')
-rw-r--r--tdehtml/rendering/render_box.cpp2325
1 files changed, 2325 insertions, 0 deletions
diff --git a/tdehtml/rendering/render_box.cpp b/tdehtml/rendering/render_box.cpp
new file mode 100644
index 000000000..4cf7080c7
--- /dev/null
+++ b/tdehtml/rendering/render_box.cpp
@@ -0,0 +1,2325 @@
+/**
+ * This file is part of the DOM implementation for KDE.
+ *
+ * Copyright (C) 1999-2003 Lars Knoll ([email protected])
+ * (C) 1999 Antti Koivisto ([email protected])
+ * (C) 2002-2003 Apple Computer, Inc.
+ * (C) 2005 Allan Sandfeld Jensen ([email protected])
+ * (C) 2006 Samuel Weinig ([email protected])
+ *
+ * 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 <tqpainter.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 <tdehtmlview.h>
+#include <kdebug.h>
+#include <kglobal.h>
+#include <assert.h>
+
+
+using namespace DOM;
+using namespace tdehtml;
+
+#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();
+ TQColor 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(TQColorGroup::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(TQPainter *p, const TQColor& 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(TQPainter *p, const TQColor& 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(TQPainter *p, const TQColor &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(TQRect(x, y, width, height), TQPainter::CoordPainter);
+ }
+
+ CachedImage* bg = bgLayer->backgroundImage();
+ bool shouldPaintBackgroundImage = bg && bg->pixmap_size() == bg->valid_rect().size() && !bg->isTransparent() && !bg->isErrorImage();
+ TQColor bgColor = c;
+
+ // Paint the color first underneath all images.
+ if (!bgLayer->next() && bgColor.isValid() && tqAlpha(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
+ TQRect 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;
+ }
+
+ TQRect fix(cx, cy, cw, ch);
+ TQRect ele(_tx, _ty, w, h);
+ TQRect 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(TQPainter *p, int _tx, int _ty, const char *color)
+{
+ p->setPen(TQPen(TQColor(color), 1, Qt::DotLine));
+ p->setBrush( Qt::NoBrush );
+ p->drawRect(_tx, _ty, m_width, m_height);
+}
+
+TQRect 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 TQRect(clipx,clipy,clipw,cliph);
+}
+
+TQRect 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 TQRect(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 TQFontMetrics &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 TQFontMetrics &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