the printing of headers and footers. Printing of selection is implemented by the frames figuring out if they are in the selection and painting if they or not they they don't paint. This also only allows the printing of the first page of selections, alothough it is well documented where this is implemeted so it can be removed. Bugs 63426, 31218, 61075 r=dcone,kmcclusk,erik,buster sr=waterson
1412 lines
48 KiB
C++
1412 lines
48 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.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Pierre Phaneuf <pp@ludusdesign.com>
|
|
*/
|
|
#include "nsTableFrame.h"
|
|
#include "nsTableColFrame.h"
|
|
#include "nsTableCellFrame.h"
|
|
#include "nsTableFrame.h"
|
|
#include "nsIReflowCommand.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsIPresContext.h"
|
|
#include "nsIRenderingContext.h"
|
|
#include "nsCSSRendering.h"
|
|
#include "nsIContent.h"
|
|
#include "nsIHTMLContent.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsHTMLParts.h"
|
|
#include "nsHTMLValue.h"
|
|
#include "nsHTMLAtoms.h"
|
|
#include "nsHTMLIIDs.h"
|
|
#include "nsVoidArray.h"
|
|
#include "nsIView.h"
|
|
#include "nsStyleUtil.h"
|
|
#include "nsLayoutAtoms.h"
|
|
#include "nsIFrameManager.h"
|
|
#include "nsIPresShell.h"
|
|
#include "nsCOMPtr.h"
|
|
#include "nsIHTMLTableCellElement.h"
|
|
#include "nsIDOMHTMLTableCellElement.h"
|
|
|
|
//TABLECELL SELECTION
|
|
#include "nsIFrameSelection.h"
|
|
#include "nsILookAndFeel.h"
|
|
|
|
|
|
nsTableCellFrame::nsTableCellFrame()
|
|
{
|
|
mColIndex = 0;
|
|
mPriorAvailWidth = 0;
|
|
mBorderEdges = nsnull;
|
|
#ifdef DEBUG_TABLE_REFLOW_TIMING
|
|
mTimer = new nsReflowTimer(this);
|
|
mBlockTimer = new nsReflowTimer(this);
|
|
#endif
|
|
}
|
|
|
|
nsTableCellFrame::~nsTableCellFrame()
|
|
{
|
|
delete mBorderEdges;
|
|
#ifdef DEBUG_TABLE_REFLOW_TIMING
|
|
nsTableFrame::DebugReflowDone(this);
|
|
#endif
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::Init(nsIPresContext* aPresContext,
|
|
nsIContent* aContent,
|
|
nsIFrame* aParent,
|
|
nsIStyleContext* aContext,
|
|
nsIFrame* aPrevInFlow)
|
|
{
|
|
nsresult rv;
|
|
|
|
// Let the base class do its initialization
|
|
rv = nsHTMLContainerFrame::Init(aPresContext, aContent, aParent, aContext,
|
|
aPrevInFlow);
|
|
|
|
if (aPrevInFlow) {
|
|
// Set the column index
|
|
nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
|
|
PRInt32 colIndex;
|
|
cellFrame->GetColIndex(colIndex);
|
|
InitCellFrame(colIndex);
|
|
}
|
|
|
|
return rv;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::AttributeChanged(nsIPresContext* aPresContext,
|
|
nsIContent* aChild,
|
|
PRInt32 aNameSpaceID,
|
|
nsIAtom* aAttribute,
|
|
PRInt32 aHint)
|
|
{
|
|
// let the table frame decide what to do
|
|
nsTableFrame* tableFrame = nsnull;
|
|
nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if ((NS_SUCCEEDED(rv)) && (tableFrame)) {
|
|
tableFrame->AttributeChangedFor(aPresContext, this, aChild, aAttribute);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
void nsTableCellFrame::SetPass1MaxElementSize(nscoord aMaxWidth,
|
|
const nsSize& aMaxElementSize)
|
|
{
|
|
mPass1MaxElementSize.height = aMaxElementSize.height;
|
|
nscoord maxElemWidth = aMaxElementSize.width;
|
|
// the max elem width needs to take into account a cell that is NOWRAP
|
|
const nsStyleText* styleText;
|
|
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&) styleText);
|
|
if (NS_STYLE_WHITESPACE_NOWRAP == styleText->mWhiteSpace) {
|
|
const nsStylePosition* stylePosition;
|
|
GetStyleData(eStyleStruct_Position, ((const nsStyleStruct *&)stylePosition));
|
|
if (stylePosition->mWidth.GetUnit() == eStyleUnit_Coord) {
|
|
nscoord styleWidth = stylePosition->mWidth.GetCoordValue();
|
|
// Nav and IE only honor the nowrap up to the style width, if present
|
|
maxElemWidth = PR_MAX(maxElemWidth, styleWidth);
|
|
}
|
|
else {
|
|
maxElemWidth = PR_MAX(maxElemWidth, aMaxWidth);
|
|
}
|
|
}
|
|
mPass1MaxElementSize.width = maxElemWidth;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::AppendFrames(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
NS_PRECONDITION(PR_FALSE, "unsupported operation");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::InsertFrames(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aPrevFrame,
|
|
nsIFrame* aFrameList)
|
|
{
|
|
NS_PRECONDITION(PR_FALSE, "unsupported operation");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::RemoveFrame(nsIPresContext* aPresContext,
|
|
nsIPresShell& aPresShell,
|
|
nsIAtom* aListName,
|
|
nsIFrame* aOldFrame)
|
|
{
|
|
NS_PRECONDITION(PR_FALSE, "unsupported operation");
|
|
return NS_ERROR_NOT_IMPLEMENTED;
|
|
}
|
|
|
|
void nsTableCellFrame::InitCellFrame(PRInt32 aColIndex)
|
|
{
|
|
nsTableFrame* tableFrame=nsnull; // I should be checking my own style context, but border-collapse isn't inheriting correctly
|
|
nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if ((NS_SUCCEEDED(rv)) && (nsnull!=tableFrame)) {
|
|
SetColIndex(aColIndex);
|
|
if (NS_STYLE_BORDER_COLLAPSE == tableFrame->GetBorderCollapseStyle()) {
|
|
if (mBorderEdges) delete mBorderEdges; // this could be non null during a reinitialization
|
|
mBorderEdges = new nsBorderEdges;
|
|
mBorderEdges->mOutsideEdge=PR_FALSE;
|
|
|
|
PRInt32 rowspan = tableFrame->GetEffectiveRowSpan(*this);
|
|
PRInt32 i;
|
|
for (i=0; i<rowspan; i++) {
|
|
nsBorderEdge *borderToAdd = new nsBorderEdge();
|
|
mBorderEdges->mEdges[NS_SIDE_LEFT].AppendElement(borderToAdd);
|
|
borderToAdd = new nsBorderEdge();
|
|
mBorderEdges->mEdges[NS_SIDE_RIGHT].AppendElement(borderToAdd);
|
|
}
|
|
PRInt32 colspan = tableFrame->GetEffectiveColSpan(*this);
|
|
for (i=0; i<colspan; i++) {
|
|
nsBorderEdge *borderToAdd = new nsBorderEdge();
|
|
mBorderEdges->mEdges[NS_SIDE_TOP].AppendElement(borderToAdd);
|
|
borderToAdd = new nsBorderEdge();
|
|
mBorderEdges->mEdges[NS_SIDE_BOTTOM].AppendElement(borderToAdd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
nsresult nsTableCellFrame::SetColIndex(PRInt32 aColIndex)
|
|
{
|
|
mColIndex = aColIndex;
|
|
// for style context optimization, set the content's column index if possible.
|
|
// this can only be done if we really have an nsTableCell.
|
|
// other tags mapped to table cell display won't benefit from this optimization
|
|
// see nsHTMLStyleSheet::RulesMatching
|
|
|
|
//nsIContent* cell;
|
|
//kidFrame->GetContent(&cell);
|
|
nsCOMPtr<nsIContent> cell;
|
|
nsresult rv = GetContent(getter_AddRefs(cell));
|
|
if (NS_FAILED(rv) || !cell)
|
|
return rv;
|
|
|
|
nsIHTMLTableCellElement* cellContent = nsnull;
|
|
rv = cell->QueryInterface(NS_GET_IID(nsIHTMLTableCellElement),
|
|
(void **)&cellContent); // cellContent: REFCNT++
|
|
if (cellContent && NS_SUCCEEDED(rv)) { // it's a table cell
|
|
cellContent->SetColIndex(aColIndex);
|
|
NS_RELEASE(cellContent);
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
void nsTableCellFrame::SetBorderEdgeLength(PRUint8 aSide,
|
|
PRInt32 aIndex,
|
|
nscoord aLength)
|
|
{
|
|
NS_PRECONDITION(mBorderEdges, "haven't allocated border edges struct");
|
|
if ((NS_SIDE_LEFT==aSide) || (NS_SIDE_RIGHT==aSide))
|
|
{
|
|
PRInt32 baseRowIndex;
|
|
GetRowIndex(baseRowIndex);
|
|
PRInt32 rowIndex = aIndex-baseRowIndex;
|
|
nsBorderEdge *border = (nsBorderEdge *)(mBorderEdges->mEdges[aSide].ElementAt(rowIndex));
|
|
if (border) {
|
|
border->mLength = aLength;
|
|
}
|
|
}
|
|
else {
|
|
NS_ASSERTION(PR_FALSE, "bad arg aSide passed to SetBorderEdgeLength");
|
|
}
|
|
}
|
|
|
|
|
|
NS_METHOD nsTableCellFrame::Paint(nsIPresContext* aPresContext,
|
|
nsIRenderingContext& aRenderingContext,
|
|
const nsRect& aDirtyRect,
|
|
nsFramePaintLayer aWhichLayer)
|
|
{
|
|
PRBool isVisible;
|
|
if (NS_SUCCEEDED(IsVisibleForPainting(aPresContext, aRenderingContext, PR_FALSE, &isVisible)) && !isVisible) {
|
|
return NS_OK;
|
|
}
|
|
const nsStyleDisplay* disp =
|
|
(const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
|
|
|
|
if (NS_FRAME_PAINT_LAYER_BACKGROUND == aWhichLayer) {
|
|
if (disp->IsVisibleOrCollapsed()) {
|
|
const nsStyleColor* myColor =
|
|
(const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
|
|
|
|
|
|
//TABLECELL SELECTION
|
|
PRInt16 displaySelection;
|
|
displaySelection = DisplaySelection(aPresContext);
|
|
if (displaySelection)
|
|
{
|
|
nsFrameState frameState;
|
|
PRBool isSelected;
|
|
GetFrameState(&frameState);
|
|
isSelected = (frameState & NS_FRAME_SELECTED_CONTENT) == NS_FRAME_SELECTED_CONTENT;
|
|
if (isSelected)
|
|
{
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
nsresult result = aPresContext->GetShell(getter_AddRefs(shell));
|
|
if (NS_FAILED(result))
|
|
return result;
|
|
nsCOMPtr<nsIFrameSelection> frameSelection;
|
|
result = shell->GetFrameSelection(getter_AddRefs(frameSelection));
|
|
if (NS_SUCCEEDED(result))
|
|
{
|
|
PRBool tableCellSelectionMode;
|
|
result = frameSelection->GetTableCellSelection(&tableCellSelectionMode);
|
|
if (NS_SUCCEEDED(result) && tableCellSelectionMode)
|
|
{
|
|
frameSelection->GetTableCellSelectionStyleColor(&myColor);
|
|
if(displaySelection==nsISelectionController::SELECTION_DISABLED)
|
|
{
|
|
((nsStyleColor *)myColor)->mBackgroundColor = NS_RGB(176,176,176);// disabled color
|
|
}
|
|
else
|
|
{
|
|
nsILookAndFeel* look = nsnull;
|
|
if (NS_SUCCEEDED(aPresContext->GetLookAndFeel(&look)) && look) {
|
|
look->GetColor(nsILookAndFeel::eColor_TextSelectBackground, ((nsStyleColor *)myColor)->mBackgroundColor);//VERY BAD CAST..TEMPORARY
|
|
NS_RELEASE(look);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//END SELECTION
|
|
|
|
const nsStyleSpacing* mySpacing =
|
|
(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
|
|
NS_ASSERTION(nsnull!=myColor, "bad style color");
|
|
NS_ASSERTION(nsnull!=mySpacing, "bad style spacing");
|
|
|
|
const nsStyleTable* cellTableStyle;
|
|
GetStyleData(eStyleStruct_Table, ((const nsStyleStruct *&)cellTableStyle));
|
|
nsRect rect(0, 0, mRect.width, mRect.height);
|
|
|
|
// only non empty cells render their background
|
|
if (PR_FALSE == GetContentEmpty()) {
|
|
nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
|
|
aDirtyRect, rect, *myColor, *mySpacing, 0, 0);
|
|
}
|
|
|
|
// empty cells do not render their border
|
|
PRBool renderBorder = PR_TRUE;
|
|
if (PR_TRUE==GetContentEmpty())
|
|
{
|
|
if (NS_STYLE_TABLE_EMPTY_CELLS_HIDE==cellTableStyle->mEmptyCells)
|
|
renderBorder=PR_FALSE;
|
|
}
|
|
if (PR_TRUE==renderBorder)
|
|
{
|
|
PRIntn skipSides = GetSkipSides();
|
|
nsTableFrame* tableFrame=nsnull; // I should be checking my own style context, but border-collapse isn't inheriting correctly
|
|
nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if ((NS_SUCCEEDED(rv)) && (nsnull!=tableFrame))
|
|
{
|
|
const nsStyleTable* tableStyle;
|
|
tableFrame->GetStyleData(eStyleStruct_Table, ((const nsStyleStruct *&)tableStyle));
|
|
if (NS_STYLE_BORDER_SEPARATE == tableFrame->GetBorderCollapseStyle())
|
|
{
|
|
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
|
|
aDirtyRect, rect, *mySpacing, mStyleContext, skipSides);
|
|
}
|
|
else
|
|
{
|
|
nsCSSRendering::PaintBorderEdges(aPresContext, aRenderingContext, this,
|
|
aDirtyRect, rect, mBorderEdges, mStyleContext, skipSides);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
// for debug...
|
|
if ((NS_FRAME_PAINT_LAYER_DEBUG == aWhichLayer) && GetShowFrameBorders()) {
|
|
aRenderingContext.SetColor(NS_RGB(0, 0, 128));
|
|
aRenderingContext.DrawRect(0, 0, mRect.width, mRect.height);
|
|
}
|
|
#endif
|
|
|
|
// if the cell originates in a row and/or col that is collapsed, the
|
|
// bottom and/or right portion of the cell is painted by translating
|
|
// the rendering context.
|
|
PRBool clipState;
|
|
nsPoint offset;
|
|
GetCollapseOffset(aPresContext, offset);
|
|
if ((0 != offset.x) || (0 != offset.y)) {
|
|
aRenderingContext.PushState();
|
|
aRenderingContext.Translate(offset.x, offset.y);
|
|
aRenderingContext.SetClipRect(nsRect(-offset.x, -offset.y, mRect.width, mRect.height),
|
|
nsClipCombine_kIntersect, clipState);
|
|
}
|
|
PaintChildren(aPresContext, aRenderingContext, aDirtyRect, aWhichLayer);
|
|
if ((0 != offset.x) || (0 != offset.y)) {
|
|
aRenderingContext.PopState(clipState);
|
|
}
|
|
|
|
return NS_OK;
|
|
/*nsFrame::Paint(aPresContext,
|
|
aRenderingContext,
|
|
aDirtyRect,
|
|
aWhichLayer);*/
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::GetFrameForPoint(nsIPresContext* aPresContext,
|
|
const nsPoint& aPoint,
|
|
nsFramePaintLayer aWhichLayer,
|
|
nsIFrame** aFrame)
|
|
{
|
|
// this should act like a block, so we need to override
|
|
return GetFrameForPointUsing(aPresContext, aPoint, nsnull, aWhichLayer, (aWhichLayer == NS_FRAME_PAINT_LAYER_BACKGROUND), aFrame);
|
|
}
|
|
|
|
//null range means the whole thing
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::SetSelected(nsIPresContext* aPresContext,
|
|
nsIDOMRange *aRange,
|
|
PRBool aSelected,
|
|
nsSpread aSpread)
|
|
{
|
|
//traverse through children unselect tables
|
|
#if 0
|
|
if ((aSpread == eSpreadDown)){
|
|
nsIFrame* kid;
|
|
FirstChild(nsnull, &kid);
|
|
while (nsnull != kid) {
|
|
kid->SetSelected(nsnull,aSelected,eSpreadDown);
|
|
|
|
kid->GetNextSibling(&kid);
|
|
}
|
|
}
|
|
//return nsFrame::SetSelected(aRange,aSelected,eSpreadNone);
|
|
#endif
|
|
// Must call base class to set mSelected state and trigger repaint of frame
|
|
// Note that in current version, aRange and aSpread are ignored,
|
|
// only this frame is considered
|
|
nsFrame::SetSelected(aPresContext, aRange, aSelected, aSpread);
|
|
|
|
nsCOMPtr<nsIPresShell> shell;
|
|
nsresult result = aPresContext->GetShell(getter_AddRefs(shell));
|
|
if (NS_FAILED(result))
|
|
return result;
|
|
nsCOMPtr<nsIFrameSelection> frameSelection;
|
|
result = shell->GetFrameSelection(getter_AddRefs(frameSelection));
|
|
if (NS_SUCCEEDED(result) && frameSelection)
|
|
{
|
|
PRBool tableCellSelectionMode;
|
|
result = frameSelection->GetTableCellSelection(&tableCellSelectionMode);
|
|
if (NS_SUCCEEDED(result) && tableCellSelectionMode)
|
|
{
|
|
nsRect frameRect;
|
|
GetRect(frameRect);
|
|
nsRect rect(0, 0, frameRect.width, frameRect.height);
|
|
Invalidate(aPresContext, rect, PR_FALSE);
|
|
}
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
PRIntn
|
|
nsTableCellFrame::GetSkipSides() const
|
|
{
|
|
PRIntn skip = 0;
|
|
if (nsnull != mPrevInFlow) {
|
|
skip |= 1 << NS_SIDE_TOP;
|
|
}
|
|
if (nsnull != mNextInFlow) {
|
|
skip |= 1 << NS_SIDE_BOTTOM;
|
|
}
|
|
return skip;
|
|
}
|
|
|
|
PRBool nsTableCellFrame::ParentDisablesSelection() const //override default behavior
|
|
{
|
|
PRBool returnval;
|
|
if (NS_FAILED(GetSelected(&returnval)))
|
|
return PR_FALSE;
|
|
if (returnval)
|
|
return PR_TRUE;
|
|
return nsFrame::ParentDisablesSelection();
|
|
}
|
|
|
|
void nsTableCellFrame::SetBorderEdge(PRUint8 aSide,
|
|
PRInt32 aRowIndex,
|
|
PRInt32 aColIndex,
|
|
nsBorderEdge *aBorder,
|
|
nscoord aOddAmountToAdd)
|
|
{
|
|
NS_PRECONDITION(mBorderEdges, "haven't allocated border edges struct");
|
|
nsBorderEdge *border = nsnull;
|
|
switch (aSide)
|
|
{
|
|
case NS_SIDE_TOP:
|
|
{
|
|
PRInt32 baseColIndex;
|
|
GetColIndex(baseColIndex);
|
|
PRInt32 colIndex = aColIndex-baseColIndex;
|
|
border = (nsBorderEdge *)(mBorderEdges->mEdges[aSide].ElementAt(colIndex));
|
|
mBorderEdges->mMaxBorderWidth.top = PR_MAX(aBorder->mWidth+aOddAmountToAdd, mBorderEdges->mMaxBorderWidth.top);
|
|
break;
|
|
}
|
|
|
|
case NS_SIDE_BOTTOM:
|
|
{
|
|
PRInt32 baseColIndex;
|
|
GetColIndex(baseColIndex);
|
|
PRInt32 colIndex = aColIndex-baseColIndex;
|
|
border = (nsBorderEdge *)(mBorderEdges->mEdges[aSide].ElementAt(colIndex));
|
|
mBorderEdges->mMaxBorderWidth.bottom = PR_MAX(aBorder->mWidth+aOddAmountToAdd, mBorderEdges->mMaxBorderWidth.bottom);
|
|
break;
|
|
}
|
|
|
|
case NS_SIDE_LEFT:
|
|
{
|
|
PRInt32 baseRowIndex;
|
|
GetRowIndex(baseRowIndex);
|
|
PRInt32 rowIndex = aRowIndex-baseRowIndex;
|
|
border = (nsBorderEdge *)(mBorderEdges->mEdges[aSide].ElementAt(rowIndex));
|
|
mBorderEdges->mMaxBorderWidth.left = PR_MAX(aBorder->mWidth+aOddAmountToAdd, mBorderEdges->mMaxBorderWidth.left);
|
|
break;
|
|
}
|
|
|
|
case NS_SIDE_RIGHT:
|
|
{
|
|
PRInt32 baseRowIndex;
|
|
GetRowIndex(baseRowIndex);
|
|
PRInt32 rowIndex = aRowIndex-baseRowIndex;
|
|
border = (nsBorderEdge *)(mBorderEdges->mEdges[aSide].ElementAt(rowIndex));
|
|
mBorderEdges->mMaxBorderWidth.right = PR_MAX(aBorder->mWidth+aOddAmountToAdd, mBorderEdges->mMaxBorderWidth.right);
|
|
break;
|
|
}
|
|
}
|
|
if (nsnull!=border) {
|
|
*border=*aBorder;
|
|
border->mWidth += aOddAmountToAdd;
|
|
}
|
|
else {
|
|
//XXX determine why this was asserting (after beta)
|
|
//NS_ASSERTION(PR_FALSE, "bad border edge state");
|
|
}
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Align the cell's child frame within the cell
|
|
*
|
|
*/
|
|
void nsTableCellFrame::VerticallyAlignChild(nsIPresContext* aPresContext,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nscoord aMaxAscent)
|
|
{
|
|
const nsStyleText* textStyle =
|
|
(const nsStyleText*)mStyleContext->GetStyleData(eStyleStruct_Text);
|
|
/* XXX: remove tableFrame when border-collapse inherits */
|
|
nsTableFrame* tableFrame = nsnull;
|
|
(void) nsTableFrame::GetTableFrame(this, tableFrame);
|
|
nsMargin borderPadding;
|
|
GetCellBorder (borderPadding, tableFrame);
|
|
nsMargin padding = nsTableFrame::GetPadding(aReflowState, this);
|
|
borderPadding += padding;
|
|
|
|
nscoord topInset = borderPadding.top;
|
|
nscoord bottomInset = borderPadding.bottom;
|
|
|
|
// As per bug 10207, we map 'sub', 'super', 'text-top', 'text-bottom',
|
|
// length and percentage values to 'baseline'
|
|
// XXX It seems that we don't get to see length and percentage values here
|
|
// because the Style System has already fixed the error and mapped them
|
|
// to whatever is inherited from the parent, i.e, 'middle' in most cases.
|
|
PRUint8 verticalAlignFlags = NS_STYLE_VERTICAL_ALIGN_BASELINE;
|
|
if (textStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
|
|
verticalAlignFlags = textStyle->mVerticalAlign.GetIntValue();
|
|
if (verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_TOP &&
|
|
verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_MIDDLE &&
|
|
verticalAlignFlags != NS_STYLE_VERTICAL_ALIGN_BOTTOM)
|
|
{
|
|
verticalAlignFlags = NS_STYLE_VERTICAL_ALIGN_BASELINE;
|
|
}
|
|
}
|
|
|
|
nscoord height = mRect.height;
|
|
nsRect kidRect;
|
|
nsIFrame* firstKid = mFrames.FirstChild();
|
|
firstKid->GetRect(kidRect);
|
|
nscoord childHeight = kidRect.height;
|
|
|
|
|
|
// Vertically align the child
|
|
nscoord kidYTop = 0;
|
|
switch (verticalAlignFlags)
|
|
{
|
|
case NS_STYLE_VERTICAL_ALIGN_BASELINE:
|
|
// Align the baselines of the child frame with the baselines of
|
|
// other children in the same row which have 'vertical-align: baseline'
|
|
kidYTop = topInset + aMaxAscent - GetDesiredAscent();
|
|
break;
|
|
|
|
case NS_STYLE_VERTICAL_ALIGN_TOP:
|
|
// Align the top of the child frame with the top of the content area,
|
|
kidYTop = topInset;
|
|
break;
|
|
|
|
case NS_STYLE_VERTICAL_ALIGN_BOTTOM:
|
|
// Align the bottom of the child frame with the bottom of the content area,
|
|
kidYTop = height - childHeight - bottomInset;
|
|
break;
|
|
|
|
default:
|
|
case NS_STYLE_VERTICAL_ALIGN_MIDDLE:
|
|
// Align the middle of the child frame with the middle of the content area,
|
|
kidYTop = (height - childHeight - bottomInset + topInset) / 2;
|
|
}
|
|
firstKid->MoveTo(aPresContext, kidRect.x, kidYTop);
|
|
if (kidYTop != kidRect.y) {
|
|
// Make sure any child views are correctly positioned. We know the inner table
|
|
// cell won't have a view
|
|
nsContainerFrame::PositionChildViews(aPresContext, firstKid);
|
|
}
|
|
}
|
|
|
|
// As per bug 10207, we map 'sub', 'super', 'text-top', 'text-bottom',
|
|
// length and percentage values to 'baseline'
|
|
// XXX It seems that we don't get to see length and percentage values here
|
|
// because the Style System has already fixed the error and mapped them
|
|
// to whatever is inherited from the parent, i.e, 'middle' in most cases.
|
|
PRBool
|
|
nsTableCellFrame::HasVerticalAlignBaseline()
|
|
{
|
|
const nsStyleText* textStyle;
|
|
GetStyleData(eStyleStruct_Text, (const nsStyleStruct*&)textStyle);
|
|
if (textStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
|
|
PRUint8 verticalAlignFlags = textStyle->mVerticalAlign.GetIntValue();
|
|
if (verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_TOP ||
|
|
verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_MIDDLE ||
|
|
verticalAlignFlags == NS_STYLE_VERTICAL_ALIGN_BOTTOM)
|
|
{
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
PRInt32 nsTableCellFrame::GetRowSpan()
|
|
{
|
|
PRInt32 rowSpan=1;
|
|
nsIHTMLContent *hc=nsnull;
|
|
nsresult rv = mContent->QueryInterface(kIHTMLContentIID, (void**) &hc);
|
|
if (NS_OK==rv)
|
|
{
|
|
nsHTMLValue val;
|
|
hc->GetHTMLAttribute(nsHTMLAtoms::rowspan, val);
|
|
if (eHTMLUnit_Integer == val.GetUnit()) {
|
|
rowSpan=val.GetIntValue();
|
|
}
|
|
NS_RELEASE(hc);
|
|
}
|
|
return rowSpan;
|
|
}
|
|
|
|
PRInt32 nsTableCellFrame::GetColSpan()
|
|
{
|
|
PRInt32 colSpan=1;
|
|
nsIHTMLContent *hc=nsnull;
|
|
nsresult rv = mContent->QueryInterface(kIHTMLContentIID, (void**) &hc);
|
|
if (NS_OK==rv)
|
|
{
|
|
nsHTMLValue val;
|
|
hc->GetHTMLAttribute(nsHTMLAtoms::colspan, val);
|
|
if (eHTMLUnit_Integer == val.GetUnit()) {
|
|
colSpan=val.GetIntValue();
|
|
}
|
|
NS_RELEASE(hc);
|
|
}
|
|
return colSpan;
|
|
}
|
|
|
|
|
|
static
|
|
void DebugCheckChildSize(nsIFrame* aChild,
|
|
nsHTMLReflowMetrics& aMet,
|
|
nsSize& aAvailSize,
|
|
PRBool aIsPass2Reflow)
|
|
{
|
|
|
|
/* approved for commenting out by rickg
|
|
if (aMet.width > aAvailSize.width) {
|
|
nsAutoString tmp;
|
|
aChild->GetFrameName(tmp);
|
|
printf("WARNING: cell ");
|
|
fputs(tmp, stdout);
|
|
printf(" content has desired width %d given avail width %d\n",
|
|
aMet.width, aAvailSize.width);
|
|
}
|
|
*/
|
|
if (aIsPass2Reflow) {
|
|
if ((aMet.width < 0) || (aMet.width > 60000)) {
|
|
printf("WARNING: cell content %p has large width %d \n", aChild, aMet.width);
|
|
}
|
|
if ((aMet.height < 0) || (aMet.height > 60000)) {
|
|
printf("WARNING: cell content %p has large height %d \n", aChild, aMet.height);
|
|
}
|
|
}
|
|
if (aMet.maxElementSize) {
|
|
nscoord tmp = aMet.maxElementSize->width;
|
|
if ((tmp < 0) || (tmp > 60000)) {
|
|
printf("WARNING: cell content %p has large max element width %d \n", aChild, tmp);
|
|
}
|
|
tmp = aMet.maxElementSize->height;
|
|
if ((tmp < 0) || (tmp > 60000)) {
|
|
printf("WARNING: cell content %p has large max element height %d \n", aChild, tmp);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
*/
|
|
NS_METHOD nsTableCellFrame::Reflow(nsIPresContext* aPresContext,
|
|
nsHTMLReflowMetrics& aDesiredSize,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nsReflowStatus& aStatus)
|
|
{
|
|
DO_GLOBAL_REFLOW_COUNT("nsTableCellFrame", aReflowState.reason);
|
|
#if defined DEBUG_TABLE_REFLOW | DEBUG_TABLE_REFLOW_TIMING
|
|
nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState);
|
|
#endif
|
|
|
|
nsresult rv = NS_OK;
|
|
// this should probably be cached somewhere
|
|
nsCompatibility compatMode;
|
|
aPresContext->GetCompatibilityMode(&compatMode);
|
|
|
|
// Initialize out parameter
|
|
if (nsnull != aDesiredSize.maxElementSize) {
|
|
aDesiredSize.maxElementSize->width = 0;
|
|
aDesiredSize.maxElementSize->height = 0;
|
|
}
|
|
|
|
aStatus = NS_FRAME_COMPLETE;
|
|
nsSize availSize(aReflowState.availableWidth, aReflowState.availableHeight);
|
|
nsSize maxElementSize;
|
|
nsSize *pMaxElementSize = aDesiredSize.maxElementSize;
|
|
if (NS_UNCONSTRAINEDSIZE==aReflowState.availableWidth)
|
|
pMaxElementSize = &maxElementSize;
|
|
|
|
/* XXX: remove tableFrame when border-collapse inherits */
|
|
nsTableFrame* tableFrame=nsnull;
|
|
rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
nsMargin borderPadding = aReflowState.mComputedPadding;
|
|
nsMargin border;
|
|
GetCellBorder(border, tableFrame);
|
|
if ((NS_UNCONSTRAINEDSIZE == availSize.width) || !GetContentEmpty()) {
|
|
borderPadding += border;
|
|
}
|
|
|
|
nscoord topInset = borderPadding.top;
|
|
nscoord rightInset = borderPadding.right;
|
|
nscoord bottomInset = borderPadding.bottom;
|
|
nscoord leftInset = borderPadding.left;
|
|
|
|
// reduce available space by insets, if we're in a constrained situation
|
|
if (NS_UNCONSTRAINEDSIZE!=availSize.width)
|
|
availSize.width -= leftInset+rightInset;
|
|
if (NS_UNCONSTRAINEDSIZE!=availSize.height)
|
|
availSize.height -= topInset+bottomInset;
|
|
|
|
PRBool isStyleChanged = PR_FALSE;
|
|
if (eReflowReason_Incremental == aReflowState.reason)
|
|
{
|
|
// We *must* do this otherwise incremental reflow that's
|
|
// passing through will not work right.
|
|
nsIFrame* next;
|
|
aReflowState.reflowCommand->GetNext(next);
|
|
|
|
// if it is a StyleChanged reflow targeted at this cell frame,
|
|
// handle that here
|
|
// first determine if this frame is the target or not
|
|
nsIFrame *target=nsnull;
|
|
rv = aReflowState.reflowCommand->GetTarget(target);
|
|
if ((PR_TRUE==NS_SUCCEEDED(rv)) && (nsnull!=target))
|
|
{
|
|
if (this==target)
|
|
{
|
|
nsIReflowCommand::ReflowType type;
|
|
aReflowState.reflowCommand->GetType(type);
|
|
if (nsIReflowCommand::StyleChanged==type)
|
|
{
|
|
isStyleChanged = PR_TRUE;
|
|
}
|
|
else {
|
|
NS_ASSERTION(PR_FALSE, "table cell target of illegal incremental reflow type");
|
|
}
|
|
}
|
|
}
|
|
// if any of these conditions are not true, we just pass the reflow command down
|
|
}
|
|
|
|
// Try to reflow the child into the available space. It might not
|
|
// fit or might need continuing.
|
|
if (availSize.height < 0)
|
|
availSize.height = 1;
|
|
|
|
nsHTMLReflowMetrics kidSize(pMaxElementSize, aDesiredSize.mFlags);
|
|
kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0;
|
|
SetPriorAvailWidth(aReflowState.availableWidth);
|
|
nsIFrame* firstKid = mFrames.FirstChild();
|
|
nsHTMLReflowState kidReflowState(aPresContext, aReflowState, firstKid,
|
|
availSize);
|
|
|
|
// If it was a style change targeted at us, then reflow the child using
|
|
// the special reflow reason
|
|
if (isStyleChanged) {
|
|
kidReflowState.reason = eReflowReason_StyleChange;
|
|
kidReflowState.reflowCommand = nsnull;
|
|
}
|
|
|
|
// Assume the inner child will stay positioned exactly where it is. Later in
|
|
// VerticallyAlignChild() we'll move it if it turns out to be wrong. This
|
|
// avoids excessive movement and is more stable
|
|
nsPoint kidOrigin;
|
|
if (eReflowReason_Initial == aReflowState.reason) {
|
|
kidOrigin.MoveTo(leftInset, topInset);
|
|
} else {
|
|
// handle percent padding-left which was 0 during initial reflow
|
|
if (eStyleUnit_Percent == aReflowState.mStyleSpacing->mPadding.GetLeftUnit()) {
|
|
nsRect kidRect;
|
|
firstKid->GetRect(kidRect);
|
|
// only move in the x direction for the same reason as above
|
|
kidOrigin.MoveTo(leftInset, kidRect.y);
|
|
firstKid->MoveTo(aPresContext, leftInset, kidRect.y);
|
|
}
|
|
firstKid->GetOrigin(kidOrigin);
|
|
}
|
|
|
|
#if defined DEBUG_TABLE_REFLOW | DEBUG_TABLE_REFLOW_TIMING
|
|
nsTableFrame::DebugReflow(firstKid, (nsHTMLReflowState&)kidReflowState);
|
|
#endif
|
|
ReflowChild(firstKid, aPresContext, kidSize, kidReflowState,
|
|
kidOrigin.x, kidOrigin.y, 0, aStatus);
|
|
if (isStyleChanged) {
|
|
Invalidate(aPresContext, mRect);
|
|
}
|
|
#if defined DEBUG_TABLE_REFLOW | DEBUG_TABLE_REFLOW_TIMING
|
|
nsTableFrame::DebugReflow(firstKid, (nsHTMLReflowState&)kidReflowState, &kidSize, aStatus);
|
|
#endif
|
|
|
|
#ifdef NS_DEBUG
|
|
DebugCheckChildSize(firstKid, kidSize, availSize, (NS_UNCONSTRAINEDSIZE != aReflowState.availableWidth));
|
|
#endif
|
|
|
|
// 0 dimensioned cells need to be treated specially in Standard/NavQuirks mode
|
|
// see testcase "emptyCells.html"
|
|
if ((0 == kidSize.width) || (0 == kidSize.height)) { // XXX why was this &&
|
|
SetContentEmpty(PR_TRUE);
|
|
if (NS_UNCONSTRAINEDSIZE == kidReflowState.availableWidth) {
|
|
// need to reduce the insets by border if the cell is empty
|
|
leftInset -= border.left;
|
|
rightInset -= border.right;
|
|
topInset -= border.top;
|
|
bottomInset -= border.bottom;
|
|
}
|
|
}
|
|
else {
|
|
SetContentEmpty(PR_FALSE);
|
|
}
|
|
|
|
const nsStylePosition* pos;
|
|
GetStyleData(eStyleStruct_Position, ((const nsStyleStruct *&)pos));
|
|
|
|
// calculate the min cell width
|
|
float p2t;
|
|
aPresContext->GetScaledPixelsToTwips(&p2t);
|
|
nscoord onePixel = NSIntPixelsToTwips(1, p2t);
|
|
nscoord smallestMinWidth = onePixel;
|
|
if (eCompatibility_NavQuirks == compatMode) {
|
|
if ((pos->mWidth.GetUnit() != eStyleUnit_Coord) &&
|
|
(pos->mWidth.GetUnit() != eStyleUnit_Percent)) {
|
|
if (PR_TRUE == GetContentEmpty()) {
|
|
if (border.left > 0)
|
|
smallestMinWidth += onePixel;
|
|
if (border.right > 0)
|
|
smallestMinWidth += onePixel;
|
|
}
|
|
}
|
|
}
|
|
PRInt32 colspan = tableFrame->GetEffectiveColSpan(*this);
|
|
if (colspan > 1) {
|
|
smallestMinWidth = PR_MAX(smallestMinWidth, colspan * onePixel);
|
|
nscoord spacingX = tableFrame->GetCellSpacingX();
|
|
nscoord spacingExtra = spacingX * (colspan - 1);
|
|
smallestMinWidth += spacingExtra;
|
|
if (aReflowState.mComputedPadding.left > 0) {
|
|
smallestMinWidth -= onePixel;
|
|
}
|
|
}
|
|
|
|
if ((0 == kidSize.width) && (NS_UNCONSTRAINEDSIZE != kidReflowState.availableWidth)) {
|
|
// empty content has to be forced to the assigned width for resize or incremental reflow
|
|
kidSize.width = kidReflowState.availableWidth;
|
|
}
|
|
if (0 == kidSize.height) {
|
|
if ((pos->mHeight.GetUnit() != eStyleUnit_Coord) &&
|
|
(pos->mHeight.GetUnit() != eStyleUnit_Percent)) {
|
|
// Standard mode should probably be 0 pixels high instead of 1
|
|
PRInt32 pixHeight = (eCompatibility_Standard == compatMode) ? 1 : 2;
|
|
kidSize.height = NSIntPixelsToTwips(pixHeight, p2t);
|
|
if ((nsnull != aDesiredSize.maxElementSize) && (0 == pMaxElementSize->height))
|
|
pMaxElementSize->height = kidSize.height;
|
|
}
|
|
}
|
|
// end 0 dimensioned cells
|
|
|
|
kidSize.width = PR_MAX(kidSize.width, smallestMinWidth);
|
|
if (!tableFrame->IsAutoLayout()) {
|
|
// a cell in a fixed layout table is constrained to the avail width
|
|
kidSize.width = PR_MIN(kidSize.width, availSize.width);
|
|
}
|
|
//if (eReflowReason_Resize == aReflowState.reason) {
|
|
// NS_ASSERTION(kidSize.width <= availSize.width, "child needed more space during resize reflow");
|
|
//}
|
|
// Place the child
|
|
FinishReflowChild(firstKid, aPresContext, kidSize,
|
|
kidOrigin.x, kidOrigin.y, 0);
|
|
|
|
// first, compute the height which can be set w/o being restricted by aMaxSize.height
|
|
nscoord cellHeight = kidSize.height;
|
|
if (NS_UNCONSTRAINEDSIZE != cellHeight) {
|
|
cellHeight += topInset + bottomInset;
|
|
}
|
|
|
|
// next determine the cell's width
|
|
nscoord cellWidth = kidSize.width; // at this point, we've factored in the cell's style attributes
|
|
|
|
// factor in border and padding
|
|
if (NS_UNCONSTRAINEDSIZE != cellWidth) {
|
|
cellWidth += leftInset + rightInset;
|
|
}
|
|
|
|
// set the cell's desired size and max element size
|
|
aDesiredSize.width = cellWidth;
|
|
aDesiredSize.height = cellHeight;
|
|
aDesiredSize.ascent = topInset;
|
|
aDesiredSize.descent = bottomInset;
|
|
|
|
aDesiredSize.ascent += kidSize.ascent;
|
|
aDesiredSize.descent += kidSize.descent;
|
|
|
|
if (aDesiredSize.mFlags & NS_REFLOW_CALC_MAX_WIDTH) {
|
|
aDesiredSize.mMaximumWidth = kidSize.mMaximumWidth + leftInset + rightInset;
|
|
}
|
|
if (nsnull!=aDesiredSize.maxElementSize) {
|
|
*aDesiredSize.maxElementSize = *pMaxElementSize;
|
|
if (0!=pMaxElementSize->height) {
|
|
aDesiredSize.maxElementSize->height += topInset + bottomInset;
|
|
}
|
|
aDesiredSize.maxElementSize->width = PR_MAX(smallestMinWidth, aDesiredSize.maxElementSize->width);
|
|
aDesiredSize.maxElementSize->width += leftInset + rightInset;
|
|
}
|
|
// remember my desired size for this reflow
|
|
SetDesiredSize(aDesiredSize);
|
|
|
|
#if defined DEBUG_TABLE_REFLOW | DEBUG_TABLE_REFLOW_TIMING
|
|
nsTableFrame::DebugReflow(this, (nsHTMLReflowState&)aReflowState, &aDesiredSize, aStatus);
|
|
#endif
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
/**
|
|
*
|
|
* Update the border style to map to the HTML border style
|
|
*
|
|
*/
|
|
void nsTableCellFrame::MapHTMLBorderStyle(nsIPresContext* aPresContext,
|
|
nsStyleSpacing& aSpacingStyle,
|
|
nsTableFrame* aTableFrame)
|
|
{
|
|
//adjust the border style based on the table rules attribute
|
|
|
|
/* The RULES code below has been disabled because collapsing borders have been disabled
|
|
and RULES depend on collapsing borders
|
|
|
|
const nsStyleTable* tableStyle;
|
|
aTableFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct *&)tableStyle);
|
|
|
|
switch (tableStyle->mRules)
|
|
{
|
|
case NS_STYLE_TABLE_RULES_NONE:
|
|
aSpacingStyle.SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE);
|
|
aSpacingStyle.SetBorderStyle(NS_SIDE_LEFT, NS_STYLE_BORDER_STYLE_NONE);
|
|
aSpacingStyle.SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE);
|
|
aSpacingStyle.SetBorderStyle(NS_SIDE_RIGHT, NS_STYLE_BORDER_STYLE_NONE);
|
|
break;
|
|
|
|
case NS_STYLE_TABLE_RULES_COLS:
|
|
aSpacingStyle.SetBorderStyle(NS_SIDE_TOP, NS_STYLE_BORDER_STYLE_NONE);
|
|
aSpacingStyle.SetBorderStyle(NS_SIDE_BOTTOM, NS_STYLE_BORDER_STYLE_NONE);
|
|
break;
|
|
|
|
case NS_STYLE_TABLE_RULES_ROWS:
|
|
aSpacingStyle.SetBorderStyle(NS_SIDE_LEFT, NS_STYLE_BORDER_STYLE_NONE);
|
|
aSpacingStyle.SetBorderStyle(NS_SIDE_RIGHT, NS_STYLE_BORDER_STYLE_NONE);
|
|
break;
|
|
|
|
default:
|
|
// do nothing for "GROUPS" or "ALL" or for any illegal value
|
|
// "GROUPS" will be handled in nsTableFrame::ProcessGroupRules
|
|
break;
|
|
}
|
|
*/
|
|
}
|
|
|
|
|
|
PRBool nsTableCellFrame::ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult)
|
|
{
|
|
if (aValue.GetUnit() == eHTMLUnit_Pixel)
|
|
aResult = aValue.GetPixelValue();
|
|
else if (aValue.GetUnit() == eHTMLUnit_Empty)
|
|
aResult = aDefault;
|
|
else
|
|
{
|
|
NS_ERROR("Unit must be pixel or empty");
|
|
return PR_FALSE;
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
void nsTableCellFrame::MapBorderPadding(nsIPresContext* aPresContext)
|
|
{
|
|
// Check to see if the table has cell padding or defined for the table. If true,
|
|
// then this setting overrides any specific border or padding information in the
|
|
// cell. If these attributes are not defined, the the cells attributes are used
|
|
|
|
nsTableFrame* tableFrame;
|
|
nsTableFrame::GetTableFrame(this, tableFrame);
|
|
NS_ASSERTION(tableFrame,"Table must not be null");
|
|
if (!tableFrame)
|
|
return;
|
|
|
|
// get the table frame style context, and from it get cellpadding, cellspacing, and border info
|
|
const nsStyleTable* tableStyle;
|
|
tableFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct *&)tableStyle);
|
|
const nsStyleSpacing* tableSpacingStyle;
|
|
tableFrame->GetStyleData(eStyleStruct_Spacing,(const nsStyleStruct *&)tableSpacingStyle);
|
|
nsStyleSpacing* spacingData = (nsStyleSpacing*)mStyleContext->GetMutableStyleData(eStyleStruct_Spacing);
|
|
|
|
float p2t;
|
|
aPresContext->GetPixelsToTwips(&p2t);
|
|
|
|
// Get the table's cellpadding or use 2 pixels as the default if it is not set.
|
|
// This assumes that ua.css does not set padding for the cell.
|
|
nscoord defaultPadding = tableFrame->GetCellPadding();
|
|
if (-1 == defaultPadding) { // not set in table
|
|
defaultPadding = NSIntPixelsToTwips(1, p2t);
|
|
}
|
|
|
|
// if the padding is not already set, set it to the table's cellpadding
|
|
if (eStyleUnit_Null == spacingData->mPadding.GetTopUnit())
|
|
spacingData->mPadding.SetTop(defaultPadding);
|
|
if (eStyleUnit_Null == spacingData->mPadding.GetRightUnit())
|
|
spacingData->mPadding.SetRight(defaultPadding);
|
|
if (eStyleUnit_Null == spacingData->mPadding.GetBottomUnit())
|
|
spacingData->mPadding.SetBottom(defaultPadding);
|
|
if (eStyleUnit_Null == spacingData->mPadding.GetLeftUnit())
|
|
spacingData->mPadding.SetLeft(defaultPadding);
|
|
|
|
MapHTMLBorderStyle(aPresContext, *spacingData, tableFrame);
|
|
|
|
MapVAlignAttribute(aPresContext, tableFrame);
|
|
MapHAlignAttribute(aPresContext, tableFrame);
|
|
|
|
}
|
|
|
|
/* XXX: this code will not work properly until the style and layout code has been updated
|
|
* as outlined in Bugzilla bug report 1802 and 915 */
|
|
void nsTableCellFrame::MapVAlignAttribute(nsIPresContext* aPresContext, nsTableFrame *aTableFrame)
|
|
{
|
|
const nsStyleText* textStyle;
|
|
GetStyleData(eStyleStruct_Text,(const nsStyleStruct *&)textStyle);
|
|
// check if valign is set on the cell
|
|
// this condition will also be true if we inherited valign from the row or rowgroup
|
|
if (textStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
|
|
return; // valign is already set on this cell
|
|
}
|
|
|
|
// check if valign is set on the cell's COL (or COLGROUP by inheritance)
|
|
nsTableColFrame* colFrame;
|
|
PRInt32 colIndex;
|
|
GetColIndex(colIndex);
|
|
aTableFrame->GetColumnFrame(colIndex, colFrame);
|
|
if (colFrame) {
|
|
const nsStyleText* colTextStyle;
|
|
colFrame->GetStyleData(eStyleStruct_Text,(const nsStyleStruct *&)colTextStyle);
|
|
if (colTextStyle->mVerticalAlign.GetUnit() == eStyleUnit_Enumerated) {
|
|
nsStyleText* cellTextStyle = (nsStyleText*)mStyleContext->GetMutableStyleData(eStyleStruct_Text);
|
|
cellTextStyle->mVerticalAlign.SetIntValue(colTextStyle->mVerticalAlign.GetIntValue(), eStyleUnit_Enumerated);
|
|
return; // valign set from COL info
|
|
}
|
|
}
|
|
|
|
// otherwise, set the vertical align attribute to the HTML default
|
|
nsStyleText* cellTextStyle = (nsStyleText*)mStyleContext->GetMutableStyleData(eStyleStruct_Text);
|
|
cellTextStyle->mVerticalAlign.SetIntValue(NS_STYLE_VERTICAL_ALIGN_MIDDLE, eStyleUnit_Enumerated);
|
|
}
|
|
|
|
/* XXX: this code will not work properly until the style and layout code has been updated
|
|
* as outlined in Bugzilla bug report 1802 and 915.
|
|
* In particular, mTextAlign has to be an nsStyleCoord, not just an int */
|
|
void nsTableCellFrame::MapHAlignAttribute(nsIPresContext* aPresContext,
|
|
nsTableFrame* aTableFrame)
|
|
{
|
|
#if 0
|
|
const nsStyleText* textStyle;
|
|
GetStyleData(eStyleStruct_Text,(const nsStyleStruct *&)textStyle);
|
|
// check if halign is set on the cell
|
|
// cells do not inherited halign from the row or rowgroup
|
|
if (NS_STYLE_TEXT_ALIGN_DEFAULT != textStyle->mTextAlign) {
|
|
return; // text align is already set on this cell
|
|
}
|
|
|
|
// check if halign is set on the cell's ROW (or ROWGROUP by inheritance)
|
|
nsIFrame* rowFrame;
|
|
GetParent(&rowFrame);
|
|
const nsStyleText* rowTextStyle;
|
|
rowFrame->GetStyleData(eStyleStruct_Text,(const nsStyleStruct *&)rowTextStyle);
|
|
if (NS_STYLE_TEXT_ALIGN_DEFAULT != rowTextStyle->mTextAlign) {
|
|
nsStyleText* cellTextStyle = (nsStyleText*)mStyleContext->GetMutableStyleData(eStyleStruct_Text);
|
|
cellTextStyle->mTextAlign = rowTextStyle->mTextAlign;
|
|
return; // halign set from ROW info
|
|
}
|
|
|
|
// check if halign is set on the cell's COL (or COLGROUP by inheritance)
|
|
nsTableColFrame* colFrame;
|
|
PRInt32 colIndex;
|
|
GetColIndex(colIndex);
|
|
aTableFrame->GetColumnFrame(colIndex, colFrame);
|
|
if (colFrame) {
|
|
const nsStyleText* colTextStyle;
|
|
colFrame->GetStyleData(eStyleStruct_Text,(const nsStyleStruct *&)colTextStyle);
|
|
if (NS_STYLE_TEXT_ALIGN_DEFAULT != colTextStyle->mTextAlign) {
|
|
nsStyleText* cellTextStyle = (nsStyleText*)mStyleContext->GetMutableStyleData(eStyleStruct_Text);
|
|
cellTextStyle->mTextAlign = colTextStyle->mTextAlign;
|
|
return; // halign set from COL info
|
|
}
|
|
}
|
|
|
|
// otherwise, set the text align to the HTML default (center for TH, left for TD)
|
|
nsStyleText* cellTextStyle = (nsStyleText*)mStyleContext->GetMutableStyleData(eStyleStruct_Text);
|
|
nsIAtom* tag;
|
|
if (mContent) {
|
|
mContent->GetTag(tag);
|
|
if (tag) {
|
|
cellTextStyle->mTextAlign = (nsHTMLAtoms::th == tag)
|
|
? NS_STYLE_TEXT_ALIGN_CENTER
|
|
: NS_STYLE_TEXT_ALIGN_LEFT;
|
|
}
|
|
NS_IF_RELEASE(tag);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// Subclass hook for style post processing
|
|
NS_METHOD nsTableCellFrame::DidSetStyleContext(nsIPresContext* aPresContext)
|
|
{
|
|
#ifdef NOISY_STYLE
|
|
printf("nsTableCellFrame::DidSetStyleContext \n");
|
|
#endif
|
|
|
|
MapBorderPadding(aPresContext);
|
|
mStyleContext->RecalcAutomaticData(aPresContext);
|
|
return NS_OK;
|
|
}
|
|
|
|
/* ----- global methods ----- */
|
|
|
|
NS_IMPL_ADDREF_INHERITED(nsTableCellFrame, nsHTMLContainerFrame)
|
|
NS_IMPL_RELEASE_INHERITED(nsTableCellFrame, nsHTMLContainerFrame)
|
|
|
|
nsresult nsTableCellFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
|
|
{
|
|
if (NULL == aInstancePtr) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
if (aIID.Equals(NS_GET_IID(nsITableCellLayout))) {
|
|
*aInstancePtr = (void*) (nsITableCellLayout *)this;
|
|
return NS_OK;
|
|
} else {
|
|
return nsHTMLContainerFrame::QueryInterface(aIID, aInstancePtr);
|
|
}
|
|
}
|
|
|
|
/* This is primarily for editor access via nsITableLayout */
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::GetCellIndexes(PRInt32 &aRowIndex, PRInt32 &aColIndex)
|
|
{
|
|
nsresult res = GetRowIndex(aRowIndex);
|
|
if (NS_FAILED(res))
|
|
{
|
|
aColIndex = 0;
|
|
return res;
|
|
}
|
|
aColIndex = mColIndex;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::GetPreviousCellInColumn(nsITableCellLayout **aCellLayout)
|
|
{
|
|
if (!aCellLayout) return NS_ERROR_NULL_POINTER;
|
|
*aCellLayout = nsnull;
|
|
|
|
nsTableFrame* tableFrame = nsnull;
|
|
nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!tableFrame) return NS_ERROR_FAILURE;
|
|
|
|
// Get current cell location
|
|
PRInt32 rowIndex, colIndex;
|
|
GetCellIndexes(rowIndex, colIndex);
|
|
if (colIndex > 0)
|
|
{
|
|
// Get the cellframe at previous colIndex
|
|
nsTableCellFrame *cellFrame = tableFrame->GetCellFrameAt(rowIndex, colIndex-1);
|
|
if (cellFrame)
|
|
cellFrame->QueryInterface(NS_GET_IID(nsITableCellLayout), (void **)aCellLayout);
|
|
}
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::GetNextCellInColumn(nsITableCellLayout **aCellLayout)
|
|
{
|
|
if (!aCellLayout) return NS_ERROR_NULL_POINTER;
|
|
*aCellLayout = nsnull;
|
|
|
|
nsTableFrame* tableFrame = nsnull;
|
|
nsresult rv = nsTableFrame::GetTableFrame(this, tableFrame);
|
|
if (NS_FAILED(rv)) return rv;
|
|
if (!tableFrame) return NS_ERROR_FAILURE;
|
|
|
|
// Get current cell location
|
|
PRInt32 rowIndex, colIndex;
|
|
GetCellIndexes(rowIndex, colIndex);
|
|
|
|
// Get the cellframe at next colIndex
|
|
nsTableCellFrame *cellFrame = tableFrame->GetCellFrameAt(rowIndex, colIndex+1);
|
|
if (cellFrame)
|
|
cellFrame->QueryInterface(NS_GET_IID(nsITableCellLayout), (void **)aCellLayout);
|
|
|
|
return NS_OK;
|
|
}
|
|
|
|
nsresult
|
|
NS_NewTableCellFrame(nsIPresShell* aPresShell, nsIFrame** aNewFrame)
|
|
{
|
|
NS_PRECONDITION(aNewFrame, "null OUT ptr");
|
|
if (nsnull == aNewFrame) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
nsTableCellFrame* it = new (aPresShell) nsTableCellFrame;
|
|
if (nsnull == it) {
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
}
|
|
*aNewFrame = it;
|
|
return NS_OK;
|
|
}
|
|
|
|
|
|
|
|
/* ----- methods from CellLayoutData ----- */
|
|
|
|
/**
|
|
* Given an Edge, find the opposing edge (top<-->bottom, left<-->right)
|
|
*
|
|
**/
|
|
PRUint8 nsTableCellFrame::GetOpposingEdge(PRUint8 aEdge)
|
|
{
|
|
PRUint8 result;
|
|
|
|
switch (aEdge)
|
|
{
|
|
case NS_SIDE_LEFT:
|
|
result = NS_SIDE_RIGHT;
|
|
break;
|
|
|
|
case NS_SIDE_RIGHT:
|
|
result = NS_SIDE_LEFT;
|
|
break;
|
|
|
|
case NS_SIDE_TOP:
|
|
result = NS_SIDE_BOTTOM;
|
|
break;
|
|
|
|
case NS_SIDE_BOTTOM:
|
|
result = NS_SIDE_TOP;
|
|
break;
|
|
|
|
default:
|
|
result = NS_SIDE_TOP;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
void
|
|
nsTableCellFrame::GetCellBorder(nsMargin& aBorder,
|
|
nsTableFrame* aTableFrame)
|
|
{
|
|
aBorder.left = aBorder.right = aBorder.top = aBorder.bottom = 0;
|
|
if (nsnull==aTableFrame) {
|
|
return;
|
|
}
|
|
|
|
if (NS_STYLE_BORDER_COLLAPSE==aTableFrame->GetBorderCollapseStyle()) {
|
|
NS_PRECONDITION(mBorderEdges, "haven't allocated border edges struct");
|
|
aBorder = mBorderEdges->mMaxBorderWidth;
|
|
} else {
|
|
const nsStyleSpacing* spacing;
|
|
GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct*&)spacing);
|
|
spacing->GetBorder(aBorder);
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::GetFrameType(nsIAtom** aType) const
|
|
{
|
|
NS_PRECONDITION(nsnull != aType, "null OUT parameter pointer");
|
|
*aType = nsLayoutAtoms::tableCellFrame;
|
|
NS_ADDREF(*aType);
|
|
return NS_OK;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::GetFrameName(nsString& aResult) const
|
|
{
|
|
return MakeFrameName("TableCell", aResult);
|
|
}
|
|
#endif
|
|
|
|
// Destructor function for the collapse offset frame property
|
|
static void
|
|
DestroyPointFunc(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
nsIAtom* aPropertyName,
|
|
void* aPropertyValue)
|
|
{
|
|
delete (nsPoint*)aPropertyValue;
|
|
}
|
|
|
|
static nsPoint*
|
|
GetCollapseOffsetProperty(nsIPresContext* aPresContext,
|
|
nsIFrame* aFrame,
|
|
PRBool aCreateIfNecessary = PR_FALSE)
|
|
{
|
|
nsCOMPtr<nsIPresShell> presShell;
|
|
aPresContext->GetShell(getter_AddRefs(presShell));
|
|
|
|
if (presShell) {
|
|
nsCOMPtr<nsIFrameManager> frameManager;
|
|
presShell->GetFrameManager(getter_AddRefs(frameManager));
|
|
|
|
if (frameManager) {
|
|
void* value;
|
|
|
|
frameManager->GetFrameProperty(aFrame, nsLayoutAtoms::collapseOffsetProperty,
|
|
0, &value);
|
|
if (value) {
|
|
return (nsPoint*)value; // the property already exists
|
|
|
|
} else if (aCreateIfNecessary) {
|
|
// The property isn't set yet, so allocate a new point, set the property,
|
|
// and return the newly allocated point
|
|
nsPoint* offset = new nsPoint(0, 0);
|
|
if (!offset) return nsnull;
|
|
|
|
frameManager->SetFrameProperty(aFrame, nsLayoutAtoms::collapseOffsetProperty,
|
|
offset, DestroyPointFunc);
|
|
return offset;
|
|
}
|
|
}
|
|
}
|
|
|
|
return nsnull;
|
|
}
|
|
|
|
void nsTableCellFrame::SetCollapseOffsetX(nsIPresContext* aPresContext,
|
|
nscoord aXOffset)
|
|
{
|
|
// Get the frame property (creating a point struct if necessary)
|
|
nsPoint* offset = ::GetCollapseOffsetProperty(aPresContext, this, PR_TRUE);
|
|
|
|
if (offset) {
|
|
offset->x = aXOffset;
|
|
}
|
|
}
|
|
|
|
void nsTableCellFrame::SetCollapseOffsetY(nsIPresContext* aPresContext,
|
|
nscoord aYOffset)
|
|
{
|
|
// Get the property (creating a point struct if necessary)
|
|
nsPoint* offset = ::GetCollapseOffsetProperty(aPresContext, this, PR_TRUE);
|
|
|
|
if (offset) {
|
|
offset->y = aYOffset;
|
|
}
|
|
}
|
|
|
|
void nsTableCellFrame::GetCollapseOffset(nsIPresContext* aPresContext,
|
|
nsPoint& aOffset)
|
|
{
|
|
// See if the property is set
|
|
nsPoint* offset = ::GetCollapseOffsetProperty(aPresContext, this);
|
|
|
|
if (offset) {
|
|
aOffset = *offset;
|
|
} else {
|
|
aOffset.MoveTo(0, 0);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
NS_IMETHODIMP
|
|
nsTableCellFrame::SizeOf(nsISizeOfHandler* aHandler, PRUint32* aResult) const
|
|
{
|
|
if (!aResult) {
|
|
return NS_ERROR_NULL_POINTER;
|
|
}
|
|
PRUint32 sum = sizeof(*this);
|
|
*aResult = sum;
|
|
return NS_OK;
|
|
}
|
|
#endif
|