gecko-dev/layout/tables/nsTableCellFrame.cpp

514 строки
16 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "nsTableCellFrame.h"
#include "nsBodyFrame.h"
#include "nsReflowCommand.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsIPresContext.h"
#include "nsIRenderingContext.h"
#include "nsCSSRendering.h"
#include "nsIContent.h"
#include "nsIContentDelegate.h"
#include "nsCSSLayout.h"
#include "nsHTMLAtoms.h"
#ifdef NS_DEBUG
static PRBool gsDebug = PR_FALSE;
//#define NOISY_STYLE
//#define NOISY_FLOW
#else
static const PRBool gsDebug = PR_FALSE;
#endif
static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID);
static NS_DEFINE_IID(kStyleBorderSID, NS_STYLEBORDER_SID);
static NS_DEFINE_IID(kStyleColorSID, NS_STYLECOLOR_SID);
static NS_DEFINE_IID(kStyleTextSID, NS_STYLETEXT_SID);
/**
*/
nsTableCellFrame::nsTableCellFrame(nsIContent* aContent,
nsIFrame* aParentFrame)
: nsContainerFrame(aContent, aParentFrame)
{
}
nsTableCellFrame::~nsTableCellFrame()
{
}
NS_METHOD nsTableCellFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
{
nsStyleColor* myColor =
(nsStyleColor*)mStyleContext->GetData(kStyleColorSID);
nsStyleBorder* myBorder =
(nsStyleBorder*)mStyleContext->GetData(kStyleBorderSID);
NS_ASSERTION(nsnull!=myColor, "bad style color");
NS_ASSERTION(nsnull!=myBorder, "bad style border");
nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
aDirtyRect, mRect, *myColor);
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
aDirtyRect, mRect, *myBorder, 0);
/*
printf("painting borders, size = %d %d %d %d\n",
myBorder->mSize.left, myBorder->mSize.top,
myBorder->mSize.right, myBorder->mSize.bottom);
*/
// for debug...
if (nsIFrame::GetShowFrameBorders()) {
aRenderingContext.SetColor(NS_RGB(0,128,128));
aRenderingContext.DrawRect(0, 0, mRect.width, mRect.height);
}
PaintChildren(aPresContext, aRenderingContext, aDirtyRect);
return NS_OK;
}
/**
*
* Align the cell's child frame within the cell
*
**/
void nsTableCellFrame::VerticallyAlignChild(nsIPresContext* aPresContext)
{
nsStyleSpacing* spacing =
(nsStyleSpacing*)mStyleContext->GetData(kStyleSpacingSID);
nsStyleText* textStyle =
(nsStyleText*)mStyleContext->GetData(kStyleTextSID);
nscoord topInset = spacing->mBorderPadding.top;
nscoord bottomInset = spacing->mBorderPadding.bottom;
PRUint8 verticalAlignFlags = NS_STYLE_VERTICAL_ALIGN_MIDDLE;
if (textStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
verticalAlignFlags = textStyle->mVerticalAlign.GetIntValue();
}
nscoord height = mRect.height;
nsRect kidRect;
mFirstChild->GetRect(kidRect);
nscoord childHeight = kidRect.height;
// Vertically align the child
nscoord kidYTop = 0;
switch (verticalAlignFlags)
{
case NS_STYLE_VERTICAL_ALIGN_BASELINE:
// Align the child's baseline at the max baseline
//kidYTop = aMaxAscent - kidAscent;
break;
case NS_STYLE_VERTICAL_ALIGN_TOP:
// Align the top of the child frame with the top of the box,
// minus the top padding
kidYTop = topInset;
break;
case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
kidYTop = height - childHeight - bottomInset;
break;
default:
case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
kidYTop = height/2 - childHeight/2;
}
mFirstChild->MoveTo(kidRect.x, kidYTop);
}
/** helper method to get the row span of this frame's content (which must be a cell) */
PRInt32 nsTableCellFrame::GetRowSpan()
{
PRInt32 result = 0;
nsTableCell *cellContent = (nsTableCell *)mContent;
if (nsnull!=cellContent)
{
result = cellContent->GetRowSpan();
}
return result;
}
/** helper method to get the col span of this frame's content (which must be a cell) */
PRInt32 nsTableCellFrame::GetColSpan()
{
PRInt32 result = 0;
nsTableCell *cellContent = (nsTableCell *)mContent;
if (nsnull!=cellContent)
{
result = cellContent->GetColSpan();
}
return result;
}
/** helper method to get the col index of this frame's content (which must be a cell) */
PRInt32 nsTableCellFrame::GetColIndex()
{
PRInt32 result = 0;
nsTableCell *cellContent = (nsTableCell *)mContent;
if (nsnull!=cellContent)
{
result = cellContent->GetColIndex();
}
return result;
}
void nsTableCellFrame::CreatePsuedoFrame(nsIPresContext* aPresContext)
{
// Do we have a prev-in-flow?
if (nsnull == mPrevInFlow) {
// No, create a column pseudo frame
nsBodyFrame::NewFrame(&mFirstChild, mContent, this);
mChildCount = 1;
// Resolve style and set the style context
nsIStyleContext* styleContext =
aPresContext->ResolveStyleContextFor(mContent, this); // styleContext: ADDREF++
mFirstChild->SetStyleContext(aPresContext,styleContext);
NS_RELEASE(styleContext); // styleContext: ADDREF--
} else {
nsTableCellFrame* prevFrame = (nsTableCellFrame *)mPrevInFlow;
nsIFrame* prevPseudoFrame = prevFrame->mFirstChild;
NS_ASSERTION(prevFrame->ChildIsPseudoFrame(prevPseudoFrame), "bad previous pseudo-frame");
// Create a continuing column
nsIStyleContext* kidSC;
prevPseudoFrame->GetStyleContext(aPresContext, kidSC);
prevPseudoFrame->CreateContinuingFrame(aPresContext, this, kidSC, mFirstChild);
NS_RELEASE(kidSC);
mChildCount = 1;
}
}
/**
*/
NS_METHOD nsTableCellFrame::ResizeReflow(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsSize& aMaxSize,
nsSize* aMaxElementSize,
nsReflowStatus& aStatus)
{
NS_PRECONDITION(nsnull!=aPresContext, "bad arg");
#ifdef NS_DEBUG
//PreReflowCheck();
#endif
aStatus = NS_FRAME_COMPLETE;
if (gsDebug==PR_TRUE)
printf("nsTableCellFrame::ResizeReflow: maxSize=%d,%d\n",
aMaxSize.width, aMaxSize.height);
mFirstContentOffset = mLastContentOffset = 0;
nsSize availSize(aMaxSize);
nsSize maxElementSize;
nsSize *pMaxElementSize = aMaxElementSize;
if (NS_UNCONSTRAINEDSIZE==aMaxSize.width)
pMaxElementSize = &maxElementSize;
nsReflowMetrics kidSize;
nscoord x = 0;
// SEC: what about ascent and decent???
// Compute the insets (sum of border and padding)
nsStyleSpacing* spacing =
(nsStyleSpacing*)mStyleContext->GetData(kStyleSpacingSID);
nscoord topInset = spacing->mBorderPadding.top;
nscoord rightInset = spacing->mBorderPadding.right;
nscoord bottomInset = spacing->mBorderPadding.bottom;
nscoord leftInset = spacing->mBorderPadding.left;
// reduce available space by insets
if (NS_UNCONSTRAINEDSIZE!=availSize.width)
availSize.width -= leftInset+rightInset;
if (NS_UNCONSTRAINEDSIZE!=availSize.height)
availSize.height -= topInset+bottomInset;
mLastContentIsComplete = PR_TRUE;
// get frame, creating one if needed
if (nsnull==mFirstChild)
{
CreatePsuedoFrame(aPresContext);
}
// Try to reflow the child into the available space. It might not
// fit or might need continuing.
if (availSize.height < 0)
availSize.height = 1;
nsSize maxKidElementSize;
if (gsDebug==PR_TRUE)
printf(" nsTableCellFrame::ResizeReflow calling ReflowChild with availSize=%d,%d\n",
availSize.width, availSize.height);
aStatus = ReflowChild(mFirstChild, aPresContext, kidSize, availSize, pMaxElementSize);
if (gsDebug==PR_TRUE)
{
if (nsnull!=pMaxElementSize)
printf(" nsTableCellFrame::ResizeReflow: child returned desiredSize=%d,%d,\
and maxElementSize=%d,%d\n",
kidSize.width, kidSize.height,
pMaxElementSize->width, pMaxElementSize->height);
else
printf(" nsTableCellFrame::ResizeReflow: child returned desiredSize=%d,%d,\
and maxElementSize=nsnull\n",
kidSize.width, kidSize.height);
}
SetFirstContentOffset(mFirstChild);
if (gsDebug) printf("CELL: set first content offset to %d\n", GetFirstContentOffset()); //@@@
SetLastContentOffset(mFirstChild);
if (gsDebug) printf("CELL: set last content offset to %d\n", GetLastContentOffset()); //@@@
// Place the child since some of it's content fit in us.
mFirstChild->SetRect(nsRect(leftInset + x, topInset,
kidSize.width, kidSize.height));
if (NS_FRAME_IS_NOT_COMPLETE(aStatus)) {
// If the child didn't finish layout then it means that it used
// up all of our available space (or needs us to split).
mLastContentIsComplete = PR_FALSE;
}
// Return our size and our result
PRInt32 kidWidth = kidSize.width;
if (NS_UNCONSTRAINEDSIZE!=kidSize.width) //&& NS_UNCONSTRAINEDSIZE!=aMaxSize.width)
kidWidth += leftInset + rightInset;
PRInt32 kidHeight = kidSize.height;
// height can be set w/o being restricted by aMaxSize.height
if (NS_UNCONSTRAINEDSIZE!=kidSize.height)
kidHeight += topInset + bottomInset;
aDesiredSize.width = kidWidth;
aDesiredSize.height = kidHeight;
aDesiredSize.ascent = topInset;
aDesiredSize.descent = bottomInset;
if (nsnull!=aMaxElementSize)
{
*aMaxElementSize = *pMaxElementSize;
aMaxElementSize->height += topInset + bottomInset;
aMaxElementSize->width += leftInset + rightInset;
}
if (gsDebug==PR_TRUE)
printf(" nsTableCellFrame::ResizeReflow returning aDesiredSize=%d,%d\n",
aDesiredSize.width, aDesiredSize.height);
#ifdef NS_DEBUG
//PostReflowCheck(result);
#endif
return NS_OK;
}
NS_METHOD nsTableCellFrame::IncrementalReflow(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsSize& aMaxSize,
nsReflowCommand& aReflowCommand,
nsReflowStatus& aStatus)
{
if (gsDebug == PR_TRUE) printf("nsTableCellFrame::IncrementalReflow\n");
// total hack for now, just some hard-coded values
ResizeReflow(aPresContext, aDesiredSize, aMaxSize, nsnull, aStatus);
return NS_OK;
}
NS_METHOD
nsTableCellFrame::CreateContinuingFrame(nsIPresContext* aPresContext,
nsIFrame* aParent,
nsIStyleContext* aStyleContext,
nsIFrame*& aContinuingFrame)
{
nsTableCellFrame* cf = new nsTableCellFrame(mContent, aParent);
if (nsnull == cf) {
return NS_ERROR_OUT_OF_MEMORY;
}
PrepareContinuingFrame(aPresContext, aParent, aStyleContext, cf);
aContinuingFrame = cf;
return NS_OK;
}
/**
*
* Update the border style to map to the HTML border style
*
*/
void nsTableCellFrame::MapHTMLBorderStyle(nsStyleBorder& aBorderStyle, nscoord aBorderWidth)
{
for (PRInt32 index = 0; index < 4; index++)
aBorderStyle.mSizeFlag[index] = NS_STYLE_BORDER_WIDTH_LENGTH_VALUE;
aBorderStyle.mSize.top =
aBorderStyle.mSize.left =
aBorderStyle.mSize.bottom =
aBorderStyle.mSize.right = aBorderWidth;
aBorderStyle.mStyle[NS_SIDE_TOP] = NS_STYLE_BORDER_STYLE_INSET;
aBorderStyle.mStyle[NS_SIDE_LEFT] = NS_STYLE_BORDER_STYLE_INSET;
aBorderStyle.mStyle[NS_SIDE_BOTTOM] = NS_STYLE_BORDER_STYLE_OUTSET;
aBorderStyle.mStyle[NS_SIDE_RIGHT] = NS_STYLE_BORDER_STYLE_OUTSET;
NS_ColorNameToRGB("white",&aBorderStyle.mColor[NS_SIDE_TOP]);
NS_ColorNameToRGB("white",&aBorderStyle.mColor[NS_SIDE_LEFT]);
// This should be the background color of the tables
// container
NS_ColorNameToRGB("gray",&aBorderStyle.mColor[NS_SIDE_BOTTOM]);
NS_ColorNameToRGB("gray",&aBorderStyle.mColor[NS_SIDE_RIGHT]);
}
PRBool nsTableCellFrame::ConvertToIntValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult)
{
PRInt32 result = 0;
if (aValue.GetUnit() == eHTMLUnit_Integer)
aResult = aValue.GetIntValue();
else if (aValue.GetUnit() == eHTMLUnit_Pixel)
aResult = aValue.GetPixelValue();
else if (aValue.GetUnit() == eHTMLUnit_Empty)
aResult = aDefault;
else
return PR_FALSE;
return PR_TRUE;
}
void nsTableCellFrame::MapBorderMarginPadding(nsIPresContext* aPresContext)
{
// Check to see if the table has either cell padding or
// Cell spacing defined for the table. If true, then
// this setting overrides any specific border, margin or
// padding information in the cell. If these attributes
// are not defined, the the cells attributes are used
nsHTMLValue padding_value;
nsHTMLValue spacing_value;
nsHTMLValue border_value;
nsContentAttr padding_result;
nsContentAttr spacing_result;
nsContentAttr border_result;
nscoord padding = 0;
nscoord spacing = 0;
nscoord border = 1;
float p2t = aPresContext->GetPixelsToTwips();
nsTablePart* table = ((nsTableContent*)mContent)->GetTable();
NS_ASSERTION(table,"Table Must not be null");
if (!table)
return;
padding_result = table->GetAttribute(nsHTMLAtoms::cellpadding,padding_value);
spacing_result = table->GetAttribute(nsHTMLAtoms::cellspacing,spacing_value);
border_result = table->GetAttribute(nsHTMLAtoms::border,border_value);
// check to see if cellpadding or cellspacing is defined
if (spacing_result == eContentAttr_HasValue || padding_result == eContentAttr_HasValue)
{
PRInt32 value;
if (padding_result == eContentAttr_HasValue && ConvertToIntValue(padding_value,0,value))
padding = (nscoord)(p2t*(float)value);
if (spacing_result == eContentAttr_HasValue && ConvertToIntValue(spacing_value,0,value))
spacing = (nscoord)(p2t*(float)value);
nsStyleSpacing* spacingData = (nsStyleSpacing*)mStyleContext->GetData(kStyleSpacingSID);
spacingData->mMargin.SizeTo(spacing,spacing,spacing,spacing);
spacingData->mPadding.top =
spacingData->mPadding.left =
spacingData->mPadding.bottom =
spacingData->mPadding.right = padding;
}
if (border_result == eContentAttr_HasValue)
{
PRInt32 intValue = 0;
if (ConvertToIntValue(border_value,1,intValue))
{
if (intValue > 0)
intValue = 1;
border = nscoord(p2t*(float)intValue);
}
}
nsStyleBorder& borderData = *(nsStyleBorder*)mStyleContext->GetData(kStyleBorderSID);
MapHTMLBorderStyle(borderData,border);
}
void nsTableCellFrame::MapTextAttributes(nsIPresContext* aPresContext)
{
nsHTMLValue value;
((nsTableCell*)mContent)->GetAttribute(nsHTMLAtoms::align, value);
if (value.GetUnit() == eHTMLUnit_Enumerated)
{
nsStyleText* text = (nsStyleText*)mStyleContext->GetData(kStyleTextSID);
text->mTextAlign = value.GetIntValue();
}
}
// Subclass hook for style post processing
NS_METHOD nsTableCellFrame::DidSetStyleContext(nsIPresContext* aPresContext)
{
#ifdef NOISY_STYLE
printf("nsTableCellFrame::DidSetStyleContext \n");
#endif
MapTextAttributes(aPresContext);
MapBorderMarginPadding(aPresContext);
mStyleContext->RecalcAutomaticData();
return NS_OK;
}
/* ----- static methods ----- */
nsresult nsTableCellFrame::NewFrame(nsIFrame** aInstancePtrResult,
nsIContent* aContent,
nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
nsIFrame* it = new nsTableCellFrame(aContent, aParent);
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aInstancePtrResult = it;
return NS_OK;
}