?? render_box.cpp
字號:
/**
* This file is part of the DOM implementation for KDE.
*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* Copyright (C) 2004 Apple Computer, Inc.
*
* 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., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*
*/
// -------------------------------------------------------------------------
//#define DEBUG_LAYOUT
#include <qpainter.h>
#include "rendering/render_box.h"
#include "rendering/render_replaced.h"
#include "rendering/render_canvas.h"
#include "rendering/render_table.h"
#include "render_flexbox.h"
#include "render_arena.h"
#include "misc/htmlhashes.h"
#include "xml/dom_nodeimpl.h"
#include "xml/dom_docimpl.h"
#include "html/html_elementimpl.h"
#include "render_line.h"
#include <khtmlview.h>
#include <kdebug.h>
#include <assert.h>
using namespace DOM;
using namespace khtml;
#define TABLECELLMARGIN -0x4000
RenderBox::RenderBox(DOM::NodeImpl* node)
: RenderObject(node)
{
m_minWidth = -1;
m_maxWidth = -1;
m_overrideSize = -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_layer = 0;
m_inlineBoxWrapper = 0;
}
void RenderBox::setStyle(RenderStyle *_style)
{
RenderObject::setStyle(_style);
// The root always paints its background/border.
if (isRoot())
setShouldPaintBackgroundOrBorder(true);
setInline(_style->isDisplayInlineType());
switch(_style->position())
{
case ABSOLUTE:
case FIXED:
setPositioned(true);
break;
default:
setPositioned(false);
if (_style->isFloating())
setFloating(true);
if (_style->position() == RELATIVE)
setRelPositioned(true);
}
// FIXME: Note that we restrict overflow to blocks for now. One day table bodies and cells
// will need to support overflow.
// We also deal with the body scroll quirk here, since it sets the scrollbars for the document.
if (_style->overflow() != OVISIBLE && isBlockFlow() && !isTableCell() &&
(!document()->isHTMLDocument() || !isBody()))
setHasOverflowClip();
if (requiresLayer()) {
if (!m_layer) {
m_layer = new (renderArena()) RenderLayer(this);
m_layer->insertOnlyThisLayer();
if (containingBlock())
m_layer->updateLayerPositions();
}
}
else if (m_layer && !isRoot() && !isCanvas()) {
m_layer->removeOnlyThisLayer();
m_layer = 0;
}
if (m_layer)
m_layer->styleChanged();
// Set the text color if we're the body.
if (isBody())
element()->getDocument()->setTextColor(_style->color());
if (style()->outlineWidth() > 0 && style()->outlineSize() > maximalOutlineSize(PaintActionOutline))
static_cast<RenderCanvas*>(document()->renderer())->setMaximalOutlineSize(style()->outlineSize());
}
RenderBox::~RenderBox()
{
//kdDebug( 6040 ) << "Element destructor: this=" << nodeName().string() << endl;
}
void RenderBox::detach()
{
RenderLayer* layer = m_layer;
RenderArena* arena = renderArena();
// This must be done before we detach the RenderObject.
if (layer)
layer->clearClipRect();
if (m_inlineBoxWrapper) {
if (!documentBeingDestroyed())
m_inlineBoxWrapper->remove();
m_inlineBoxWrapper->detach(arena);
m_inlineBoxWrapper = 0;
}
RenderObject::detach();
if (layer)
layer->detach(arena);
}
int RenderBox::contentWidth() const
{
int w = m_width - borderLeft() - borderRight();
w -= paddingLeft() + paddingRight();
if (includeScrollbarSize())
w -= m_layer->verticalScrollbarWidth();
//kdDebug( 6040 ) << "RenderBox::contentWidth(2) = " << w << endl;
return w;
}
int RenderBox::contentHeight() const
{
int h = m_height - borderTop() - borderBottom();
h -= paddingTop() + paddingBottom();
if (includeScrollbarSize())
h -= m_layer->horizontalScrollbarHeight();
return h;
}
int RenderBox::overrideWidth() const
{
return m_overrideSize == -1 ? m_width : m_overrideSize;
}
int RenderBox::overrideHeight() const
{
return m_overrideSize == -1 ? m_height : m_overrideSize;
}
void RenderBox::setPos( int xPos, int yPos )
{
if (xPos == m_x && yPos == m_y)
return; // Optimize for the case where we don't move at all.
m_x = xPos; m_y = yPos;
}
int RenderBox::width() const
{
return m_width;
}
int RenderBox::height() const
{
return m_height;
}
// Hit Testing
bool RenderBox::nodeAtPoint(NodeInfo& info, int _x, int _y, int _tx, int _ty,
HitTestAction hitTestAction)
{
// Check kids first.
_tx += m_x;
_ty += m_y;
for (RenderObject* child = lastChild(); child; child = child->previousSibling()) {
// FIXME: We have to skip over inline flows, since they can show up inside table rows at the moment (a demoted inline <form> for example). If we ever implement a
// table-specific hit-test method (which we should do for performance reasons anyway), then we can remove this check.
if (!child->layer() && !child->isInlineFlow() && child->nodeAtPoint(info, _x, _y, _tx, _ty, hitTestAction)) {
setInnerNode(info);
return true;
}
}
// Check our bounds next. For this purpose always assume that we can only be hit in the
// foreground phase (which is true for replaced elements like images).
if (hitTestAction != HitTestForeground)
return false;
QRect boundsRect(_tx, _ty, m_width, m_height);
if (boundsRect.contains(_x, _y)) {
setInnerNode(info);
return true;
}
return false;
}
// --------------------- painting stuff -------------------------------
void RenderBox::paint(PaintInfo& i, int _tx, int _ty)
{
_tx += m_x;
_ty += m_y;
// default implementation. Just pass paint through to the children
for (RenderObject* child = firstChild(); child; child = child->nextSibling())
child->paint(i, _tx, _ty);
}
#ifdef NOKIA_CHANGES
void RenderBox::getRenderersInRect(QPtrList<BoxInfo>& boxInfoList,int deltaX,int deltaY,const QRect& rect)
{
deltaX += m_x;
deltaY += m_y;
BoxInfo* sel = new BoxInfo;
sel->renderObject = this;
sel->absoluteXPos = deltaX;
sel->absoluteYPos = deltaY;
sel->width = m_width;
sel->height = m_height;
sel->area = 0;
boxInfoList.append(sel);
for (RenderObject *child = firstChild(); child; child = child->nextSibling())
child->getRenderersInRect(boxInfoList, deltaX, deltaY,rect);
}
#endif
void RenderBox::paintRootBoxDecorations(PaintInfo& i, int _tx, int _ty)
{
//kdDebug( 6040 ) << renderName() << "::paintBoxDecorations()" << _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();
}
}
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()->width();
rh = canvas()->height();
}
// kdDebug(0) << "rw = " << rw <<endl;
int bx = _tx - marginLeft();
int by = _ty - marginTop();
int bw = kMax(w + marginLeft() + marginRight() + borderLeft() + borderRight(), rw);
int bh = kMax(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, i.r.y());
paintBackgrounds(i.p, bgColor, bgLayer, my, i.r.height(), bx, by, bw, bh);
if (style()->hasBorder() && style()->display() != INLINE)
paintBorder( i.p, _tx, _ty, w, h, style() );
}
void RenderBox::paintBoxDecorations(PaintInfo& i, int _tx, int _ty)
{
if (!shouldPaintWithinRoot(i))
return;
//kdDebug( 6040 ) << renderName() << "::paintDecorations()" << endl;
if (isRoot())
return paintRootBoxDecorations(i, _tx, _ty);
int w = width();
int h = height() + borderTopExtra() + borderBottomExtra();
_ty -= borderTopExtra();
int my = kMax(_ty, i.r.y());
int mh;
if (_ty < i.r.y())
mh= kMax(0, h - (i.r.y() - _ty));
else
mh = kMin(i.r.height(), h);
// 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(i.p, style()->backgroundColor(), style()->backgroundLayers(), my, mh, _tx, _ty, w, h);
if (style()->hasBorder())
paintBorder(i.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());
}
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)
{
CachedImage* bg = bgLayer->backgroundImage();
bool shouldPaintBackgroundImage = bg && bg->pixmap_size() == bg->valid_rect().size() && !bg->isTransparent() && !bg->isErrorImage();
QColor bgColor = c;
// When this style flag is set, change existing background colors and images to a solid white background.
// If there's no bg color or image, leave it untouched to avoid affecting transparency.
// We don't try to avoid loading the background images, because this style flag is only set
// when printing, and at that point we've already loaded the background images anyway. (To avoid
// loading the background images we'd have to do this check when applying styles rather than
// while rendering.)
if (style()->forceBackgroundsToWhite()) {
// Note that we can't reuse this variable below because the bgColor might be changed
bool shouldPaintBackgroundColor = !bgLayer->next() && bgColor.isValid() && qAlpha(bgColor.rgb()) > 0;
if (shouldPaintBackgroundImage || shouldPaintBackgroundColor) {
bgColor = Qt::white;
shouldPaintBackgroundImage = false;
}
}
// Only fill with a base color (e.g., white) if we're the root document, since iframes/frames with
// no background in the child document should show the parent's background.
if (!bgLayer->next() && isRoot() && !(bgColor.isValid() && qAlpha(bgColor.rgb()) > 0) && canvas()->view()) {
bool isTransparent;
DOM::NodeImpl* elt = document()->ownerElement();
if (elt) {
if (elt->id() == ID_FRAME)
isTransparent = false;
else {
// 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();
isTransparent = !body || body->id() != ID_FRAMESET; // Can't scroll a frameset document anyway.
}
} else
isTransparent = canvas()->view()->isTransparent();
if (isTransparent)
canvas()->view()->useSlowRepaints(); // The parent must show behind the child.
else
bgColor = canvas()->view()->palette().active().color(QColorGroup::Base);
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -