pjs/layout/tables/nsTableFrame.cpp

2706 строки
92 KiB
C++
Исходник Обычный вид История

1998-04-14 00:24:54 +04:00
/* -*- 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 "nsTableFrame.h"
#include "nsTablePart.h"
#include "nsIRenderingContext.h"
#include "nsIStyleContext.h"
#include "nsIContent.h"
#include "nsIContentDelegate.h"
#include "nsCellMap.h"
#include "nsTableContent.h"
#include "nsTableCell.h"
#include "nsTableCellFrame.h"
#include "nsTableCol.h"
#include "nsTableColFrame.h"
1998-04-14 00:24:54 +04:00
#include "nsTableRowFrame.h"
#include "nsTableRowGroupFrame.h"
#include "nsColLayoutData.h"
#include "BasicTableLayoutStrategy.h"
1998-04-14 00:24:54 +04:00
#include "nsIPresContext.h"
#include "nsCSSRendering.h"
#include "nsStyleConsts.h"
#include "nsCellLayoutData.h"
#include "nsVoidArray.h"
#include "prinrval.h"
#include "nsIPtr.h"
#include "nsIView.h"
#include "nsHTMLAtoms.h"
#include "nsHTMLIIDs.h"
1998-04-14 00:24:54 +04:00
#ifdef NS_DEBUG
1998-06-02 01:07:56 +04:00
static PRBool gsDebug = PR_FALSE;
1998-04-14 00:24:54 +04:00
static PRBool gsDebugCLD = PR_FALSE;
static PRBool gsTiming = PR_FALSE;
static PRBool gsDebugMBP = PR_FALSE;
//#define NOISY
//#define NOISY_FLOW
//#ifdef NOISY_STYLE
1998-04-14 00:24:54 +04:00
#else
static const PRBool gsDebug = PR_FALSE;
static const PRBool gsDebugCLD = PR_FALSE;
static const PRBool gsTiming = PR_FALSE;
static const PRBool gsDebugMBP = PR_FALSE;
#endif
#ifndef max
#define max(x, y) ((x) > (y) ? (x) : (y))
#endif
NS_DEF_PTR(nsIStyleContext);
NS_DEF_PTR(nsIContent);
NS_DEF_PTR(nsTableContent);
NS_DEF_PTR(nsTableCol);
NS_DEF_PTR(nsTableCell);
1998-04-14 00:24:54 +04:00
/* ----------- InnerTableReflowState ---------- */
struct InnerTableReflowState {
// The body's style molecule
// Our reflow state
const nsReflowState& reflowState;
1998-04-14 00:24:54 +04:00
// The body's available size (computed from the body's parent)
nsSize availSize;
1998-04-25 22:48:48 +04:00
nscoord leftInset, topInset;
1998-04-14 00:24:54 +04:00
// Margin tracking information
nscoord prevMaxPosBottomMargin;
nscoord prevMaxNegBottomMargin;
// Flags for whether the max size is unconstrained
PRBool unconstrainedWidth;
PRBool unconstrainedHeight;
// Running y-offset
nscoord y;
// Flag for whether we're dealing with the first interior row group
PRBool firstRowGroup;
// a list of the footers in this table frame, for quick access when inserting bodies
nsVoidArray *footerList;
// cache the total height of the footers for placing body rows
nscoord footerHeight;
InnerTableReflowState(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
const nsMargin& aBorderPadding)
: reflowState(aReflowState)
1998-04-14 00:24:54 +04:00
{
prevMaxPosBottomMargin = 0;
prevMaxNegBottomMargin = 0;
y=0; // border/padding/margin???
1998-04-25 22:48:48 +04:00
unconstrainedWidth = PRBool(aReflowState.maxSize.width == NS_UNCONSTRAINEDSIZE);
availSize.width = aReflowState.maxSize.width;
1998-04-25 22:48:48 +04:00
if (!unconstrainedWidth) {
1998-05-13 02:28:01 +04:00
availSize.width -= aBorderPadding.left + aBorderPadding.right;
1998-04-25 22:48:48 +04:00
}
1998-05-13 02:28:01 +04:00
leftInset = aBorderPadding.left;
1998-04-25 22:48:48 +04:00
unconstrainedHeight = PRBool(aReflowState.maxSize.height == NS_UNCONSTRAINEDSIZE);
availSize.height = aReflowState.maxSize.height;
1998-04-25 22:48:48 +04:00
if (!unconstrainedHeight) {
1998-05-13 02:28:01 +04:00
availSize.height -= aBorderPadding.top + aBorderPadding.bottom;
1998-04-25 22:48:48 +04:00
}
1998-05-13 02:28:01 +04:00
topInset = aBorderPadding.top;
1998-04-25 22:48:48 +04:00
1998-04-14 00:24:54 +04:00
firstRowGroup = PR_TRUE;
footerHeight = 0;
footerList = nsnull;
1998-04-14 00:24:54 +04:00
}
~InnerTableReflowState() {
if (nsnull!=footerList)
delete footerList;
1998-04-14 00:24:54 +04:00
}
};
/* ----------- ColumnInfoCache ---------- */
static const PRInt32 NUM_COL_WIDTH_TYPES=4;
class ColumnInfoCache
{
public:
ColumnInfoCache(PRInt32 aNumColumns);
~ColumnInfoCache();
void AddColumnInfo(const nsStyleUnit aType,
PRInt32 aColumnIndex);
void GetColumnsByType(const nsStyleUnit aType,
PRInt32& aOutNumColumns,
PRInt32 *& aOutColumnIndexes);
enum ColWidthType {
eColWidthType_Auto = 0, // width based on contents
eColWidthType_Percent = 1, // (float) 1.0 == 100%
eColWidthType_Coord = 2, // (nscoord) value is twips
eColWidthType_Proportional = 3, // (int) value has proportional meaning
};
private:
PRInt32 mColCounts [4];
PRInt32 *mColIndexes[4];
PRInt32 mNumColumns;
};
ColumnInfoCache::ColumnInfoCache(PRInt32 aNumColumns)
{
mNumColumns = aNumColumns;
for (PRInt32 i=0; i<NUM_COL_WIDTH_TYPES; i++)
{
mColCounts[i] = 0;
mColIndexes[i] = nsnull;
}
}
ColumnInfoCache::~ColumnInfoCache()
{
for (PRInt32 i=0; i<NUM_COL_WIDTH_TYPES; i++)
{
if (nsnull!=mColIndexes[i])
{
delete [] mColIndexes[i];
}
}
}
void ColumnInfoCache::AddColumnInfo(const nsStyleUnit aType,
PRInt32 aColumnIndex)
{
switch (aType)
{
case eStyleUnit_Auto:
if (nsnull==mColIndexes[eColWidthType_Auto])
mColIndexes[eColWidthType_Auto] = new PRInt32[mNumColumns]; // TODO : be much more efficient
mColIndexes[eColWidthType_Auto][mColCounts[eColWidthType_Auto]] = aColumnIndex;
mColCounts[eColWidthType_Auto]++;
break;
case eStyleUnit_Percent:
if (nsnull==mColIndexes[eColWidthType_Percent])
mColIndexes[eColWidthType_Percent] = new PRInt32[mNumColumns]; // TODO : be much more efficient
mColIndexes[eColWidthType_Percent][mColCounts[eColWidthType_Percent]] = aColumnIndex;
mColCounts[eColWidthType_Percent]++;
break;
case eStyleUnit_Coord:
if (nsnull==mColIndexes[eColWidthType_Coord])
mColIndexes[eColWidthType_Coord] = new PRInt32[mNumColumns]; // TODO : be much more efficient
mColIndexes[eColWidthType_Coord][mColCounts[eColWidthType_Coord]] = aColumnIndex;
mColCounts[eColWidthType_Coord]++;
break;
case eStyleUnit_Proportional:
if (nsnull==mColIndexes[eColWidthType_Proportional])
mColIndexes[eColWidthType_Proportional] = new PRInt32[mNumColumns]; // TODO : be much more efficient
mColIndexes[eColWidthType_Proportional][mColCounts[eColWidthType_Proportional]] = aColumnIndex;
mColCounts[eColWidthType_Proportional]++;
break;
}
}
void ColumnInfoCache::GetColumnsByType(const nsStyleUnit aType,
PRInt32& aOutNumColumns,
PRInt32 *& aOutColumnIndexes)
{
switch (aType)
{
case eStyleUnit_Auto:
aOutNumColumns = mColCounts[eColWidthType_Auto];
aOutColumnIndexes = mColIndexes[eColWidthType_Auto];
break;
case eStyleUnit_Percent:
aOutNumColumns = mColCounts[eColWidthType_Percent];
aOutColumnIndexes = mColIndexes[eColWidthType_Percent];
break;
case eStyleUnit_Coord:
aOutNumColumns = mColCounts[eColWidthType_Coord];
aOutColumnIndexes = mColIndexes[eColWidthType_Coord];
break;
case eStyleUnit_Proportional:
aOutNumColumns = mColCounts[eColWidthType_Proportional];
aOutColumnIndexes = mColIndexes[eColWidthType_Proportional];
break;
}
}
1998-04-14 00:24:54 +04:00
/* ----------- nsTableFrame ---------- */
nsTableFrame::nsTableFrame(nsIContent* aContent, nsIFrame* aParentFrame)
: nsContainerFrame(aContent, aParentFrame),
1998-04-14 00:24:54 +04:00
mColumnLayoutData(nsnull),
mColCache(nsnull),
1998-04-14 00:24:54 +04:00
mColumnWidths(nsnull),
mTableLayoutStrategy(nsnull),
1998-04-14 00:24:54 +04:00
mFirstPassValid(PR_FALSE),
mPass(kPASS_UNDEFINED),
mIsInvariantWidth(PR_FALSE)
{
}
/**
* Method to delete all owned objects assoicated
* with the ColumnLayoutObject instance variable
*/
void nsTableFrame::DeleteColumnLayoutData()
{
if (nsnull!=mColumnLayoutData)
{
PRInt32 numCols = mColumnLayoutData->Count();
for (PRInt32 i = 0; i<numCols; i++)
{
nsColLayoutData *colData = (nsColLayoutData *)(mColumnLayoutData->ElementAt(i));
delete colData;
}
delete mColumnLayoutData;
mColumnLayoutData = nsnull;
}
}
nsTableFrame::~nsTableFrame()
{
DeleteColumnLayoutData();
if (nsnull!=mColumnWidths)
delete [] mColumnWidths;
if (nsnull!=mTableLayoutStrategy)
delete mTableLayoutStrategy;
if (nsnull!=mColCache)
delete mColCache;
1998-04-14 00:24:54 +04:00
}
/**
* Lists the column layout data which turns
* around and lists the cell layout data.
* This is for debugging purposes only.
*/
void nsTableFrame::ListColumnLayoutData(FILE* out, PRInt32 aIndent) const
{
// if this is a continuing frame, there will be no output
if (nsnull!=mColumnLayoutData)
{
fprintf(out,"Column Layout Data \n");
PRInt32 numCols = mColumnLayoutData->Count();
for (PRInt32 i = 0; i<numCols; i++)
{
nsColLayoutData *colData = (nsColLayoutData *)(mColumnLayoutData->ElementAt(i));
for (PRInt32 indent = aIndent; --indent >= 0; ) fputs(" ", out);
fprintf(out,"Column Data [%d] \n",i);
colData->List(out,aIndent+2);
}
}
}
/**
* For the TableCell in CellData, find the CellLayoutData assocated
* and add it to the list
**/
void nsTableFrame::AppendLayoutData(nsVoidArray* aList, nsTableCell* aTableCell)
{
if (aTableCell != nsnull)
{
nsCellLayoutData* layoutData = GetCellLayoutData(aTableCell);
1998-04-14 00:24:54 +04:00
if (layoutData != nsnull)
aList->AppendElement((void*)layoutData);
}
}
void nsTableFrame::RecalcLayoutData()
1998-04-14 00:24:54 +04:00
{
nsTablePart* table = (nsTablePart*)mContent;
nsCellMap* cellMap = table->GetCellMap();
PRInt32 colCount = cellMap->GetColCount();
PRInt32 rowCount = cellMap->GetRowCount();
PRInt32 row = 0;
PRInt32 col = 0;
nsTableCell* above = nsnull;
nsTableCell* below = nsnull;
nsTableCell* left = nsnull;
nsTableCell* right = nsnull;
PRInt32 edge = 0;
nsVoidArray* boundaryCells[4];
for (edge = 0; edge < 4; edge++)
boundaryCells[edge] = new nsVoidArray();
if (colCount != 0 && rowCount != 0)
{
for (row = 0; row < rowCount; row++)
{
for (col = 0; col < colCount; col++)
{
nsTableCell* cell = nsnull;
1998-04-14 00:24:54 +04:00
CellData* cellData = cellMap->GetCellAt(row,col);
if (cellData)
cell = cellData->mCell;
1998-04-14 00:24:54 +04:00
if (!cell)
continue;
PRInt32 colSpan = cell->GetColSpan();
PRInt32 rowSpan = cell->GetRowSpan();
// clear the cells for all for edges
for (edge = 0; edge < 4; edge++)
boundaryCells[edge]->Clear();
// Check to see if the cell data represents the top,left
// corner of a a table cell
// Check to see if cell the represents a top edge cell
1998-04-14 00:24:54 +04:00
if (row == 0)
above = nsnull;
else
{
cellData = cellMap->GetCellAt(row-1,col);
if (cellData != nsnull)
above = cellData->mRealCell->mCell;
1998-04-14 00:24:54 +04:00
// Does the cell data point to the same cell?
// If it is, then continue
if (above != nsnull && above == cell)
continue;
}
// Check to see if cell the represents a left edge cell
1998-04-14 00:24:54 +04:00
if (col == 0)
left = nsnull;
1998-04-14 00:24:54 +04:00
else
{
cellData = cellMap->GetCellAt(row,col-1);
if (cellData != nsnull)
left = cellData->mRealCell->mCell;
1998-04-14 00:24:54 +04:00
if (left != nsnull && left == cell)
continue;
}
// If this is the top,left edged cell
// Then add the cells on the for edges to the array
// Do the top and bottom edge
PRInt32 r,c;
PRInt32 r1,r2;
PRInt32 c1,c2;
PRInt32 last;
r1 = row - 1;
r2 = row + rowSpan;
c = col;
last = col + colSpan -1;
1998-05-28 22:31:44 +04:00
last = PR_MIN(last,colCount-1);
1998-04-14 00:24:54 +04:00
while (c <= last)
{
if (r1 != -1)
{
// Add top edge cells
if (c != col)
{
cellData = cellMap->GetCellAt(r1,c);
if ((cellData != nsnull) && (cellData->mCell != above))
1998-04-14 00:24:54 +04:00
{
above = cellData->mCell;
if (above != nsnull)
AppendLayoutData(boundaryCells[NS_SIDE_TOP],above);
}
}
else if (above != nsnull)
{
AppendLayoutData(boundaryCells[NS_SIDE_TOP],above);
}
}
if (r2 < rowCount)
{
// Add bottom edge cells
cellData = cellMap->GetCellAt(r2,c);
if ((cellData != nsnull) && cellData->mCell != below)
1998-04-14 00:24:54 +04:00
{
below = cellData->mCell;
if (below != nsnull)
AppendLayoutData(boundaryCells[NS_SIDE_BOTTOM],below);
}
}
c++;
}
// Do the left and right edge
c1 = col - 1;
c2 = col + colSpan;
r = row ;
last = row + rowSpan-1;
1998-05-28 22:31:44 +04:00
last = PR_MIN(last,rowCount-1);
1998-04-14 00:24:54 +04:00
while (r <= last)
{
// Add left edge cells
if (c1 != -1)
{
if (r != row)
{
cellData = cellMap->GetCellAt(r,c1);
if ((cellData != nsnull) && (cellData->mCell != left))
1998-04-14 00:24:54 +04:00
{
left = cellData->mCell;
if (left != nsnull)
AppendLayoutData(boundaryCells[NS_SIDE_LEFT],left);
}
}
else if (left != nsnull)
{
AppendLayoutData(boundaryCells[NS_SIDE_LEFT],left);
}
}
if (c2 < colCount)
{
// Add right edge cells
cellData = cellMap->GetCellAt(r,c2);
if ((cellData != nsnull) && (cellData->mCell != right))
1998-04-14 00:24:54 +04:00
{
right = cellData->mCell;
if (right != nsnull)
AppendLayoutData(boundaryCells[NS_SIDE_RIGHT],right);
}
}
r++;
}
nsCellLayoutData* cellLayoutData = GetCellLayoutData(cell);
if (cellLayoutData != nsnull)
cellLayoutData->RecalcLayoutData(this,boundaryCells);
1998-04-14 00:24:54 +04:00
}
}
}
for (edge = 0; edge < 4; edge++)
delete boundaryCells[edge];
}
/* SEC: TODO: adjust the rect for captions */
NS_METHOD nsTableFrame::Paint(nsIPresContext& aPresContext,
nsIRenderingContext& aRenderingContext,
const nsRect& aDirtyRect)
1998-04-14 00:24:54 +04:00
{
// table paint code is concerned primarily with borders and bg color
1998-06-05 10:09:09 +04:00
const nsStyleDisplay* disp =
(const nsStyleDisplay*)mStyleContext->GetStyleData(eStyleStruct_Display);
1998-05-27 03:17:29 +04:00
if (disp->mVisible) {
1998-06-05 10:09:09 +04:00
const nsStyleColor* myColor =
(const nsStyleColor*)mStyleContext->GetStyleData(eStyleStruct_Color);
const nsStyleSpacing* mySpacing =
(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
1998-05-27 03:17:29 +04:00
NS_ASSERTION(nsnull != myColor, "null style color");
NS_ASSERTION(nsnull != mySpacing, "null style spacing");
if (nsnull!=mySpacing)
{
nsCSSRendering::PaintBackground(aPresContext, aRenderingContext, this,
aDirtyRect, mRect, *myColor);
nsCSSRendering::PaintBorder(aPresContext, aRenderingContext, this,
aDirtyRect, mRect, *mySpacing, 0);
}
1998-04-14 00:24:54 +04:00
}
// for debug...
if (nsIFrame::GetShowFrameBorders()) {
aRenderingContext.SetColor(NS_RGB(0,128,0));
aRenderingContext.DrawRect(0, 0, mRect.width, mRect.height);
}
PaintChildren(aPresContext, aRenderingContext, aDirtyRect);
return NS_OK;
1998-04-14 00:24:54 +04:00
}
PRBool nsTableFrame::NeedsReflow(const nsSize& aMaxSize)
{
PRBool result = PR_TRUE;
if (PR_TRUE==mIsInvariantWidth)
result = PR_FALSE;
// TODO: other cases...
return result;
}
// SEC: TODO need to worry about continuing frames prev/next in flow for splitting across pages.
// SEC: TODO need to keep "first pass done" state, update it when ContentChanged notifications come in
/* overview:
if mFirstPassValid is false, this is our first time through since content was last changed
set pass to 1
do pass 1
get min/max info for all cells in an infinite space
do column balancing
set mFirstPassValid to true
do pass 2
if pass is 1,
set pass to 2
use column widths to ResizeReflow cells
shrinkWrap Cells in each row to tallest, realigning contents within the cell
*/
/* Layout the entire inner table. */
NS_METHOD nsTableFrame::Reflow(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
1998-04-14 00:24:54 +04:00
{
NS_PRECONDITION(nsnull != aPresContext, "null arg");
if (gsDebug==PR_TRUE)
{
printf("-----------------------------------------------------------------\n");
printf("nsTableFrame::Reflow: table %p given maxSize=%d,%d\n",
this, aReflowState.maxSize.width, aReflowState.maxSize.height);
1998-04-14 00:24:54 +04:00
}
#ifdef NS_DEBUG
PreReflowCheck();
#endif
1998-05-12 08:17:56 +04:00
aStatus = NS_FRAME_COMPLETE;
1998-04-14 00:24:54 +04:00
1998-04-25 22:48:48 +04:00
PRIntervalTime startTime;
if (gsTiming) {
startTime = PR_IntervalNow();
}
1998-04-14 00:24:54 +04:00
if (PR_TRUE==NeedsReflow(aReflowState.maxSize))
1998-04-14 00:24:54 +04:00
{
if (PR_FALSE==IsFirstPassValid())
{ // we treat the table as if we've never seen the layout data before
mPass = kPASS_FIRST;
aStatus = ResizeReflowPass1(aPresContext, aDesiredSize, aReflowState);
1998-04-14 00:24:54 +04:00
// check result
}
mPass = kPASS_SECOND;
// assign column widths, and assign aMaxElementSize->width
BalanceColumnWidths(aPresContext, aReflowState, aReflowState.maxSize,
aDesiredSize.maxElementSize);
1998-04-14 00:24:54 +04:00
// assign table width
1998-04-25 22:48:48 +04:00
SetTableWidth(aPresContext);
1998-04-14 00:24:54 +04:00
aStatus = ResizeReflowPass2(aPresContext, aDesiredSize, aReflowState, 0, 0);
1998-04-14 00:24:54 +04:00
1998-04-25 22:48:48 +04:00
if (gsTiming) {
PRIntervalTime endTime = PR_IntervalNow();
printf("Table reflow took %ld ticks for frame %d\n",
endTime-startTime, this);/* XXX need to use LL_* macros! */
}
1998-04-14 00:24:54 +04:00
mPass = kPASS_UNDEFINED;
}
else
{
// set aDesiredSize and aMaxElementSize
}
#ifdef NS_DEBUG
PostReflowCheck(aStatus);
1998-04-14 00:24:54 +04:00
#endif
if (PR_TRUE==gsDebug) printf("end reflow for table %p\n", this);
return NS_OK;
1998-04-14 00:24:54 +04:00
}
/** the first of 2 reflow passes
* lay out the captions and row groups in an infinite space (NS_UNCONSTRAINEDSIZE)
* cache the results for each caption and cell.
* if successful, set mFirstPassValid=PR_TRUE, so we know we can skip this step
* next time. mFirstPassValid is set to PR_FALSE when content is changed.
* NOTE: should never get called on a continuing frame! All cached pass1 state
* is stored in the inner table first-in-flow.
*/
1998-05-12 08:17:56 +04:00
nsReflowStatus nsTableFrame::ResizeReflowPass1(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState)
1998-04-14 00:24:54 +04:00
{
NS_PRECONDITION(aReflowState.frame == this, "bad reflow state");
NS_PRECONDITION(aReflowState.parentReflowState->frame == mGeometricParent,
"bad parent reflow state");
1998-04-14 00:24:54 +04:00
NS_ASSERTION(nsnull!=aPresContext, "bad pres context param");
NS_ASSERTION(nsnull==mPrevInFlow, "illegal call, cannot call pass 1 on a continuing frame.");
if (PR_TRUE==gsDebug) printf("nsTableFrame::ResizeReflow Pass1 for %p: maxSize=%d,%d\n",
this, aReflowState.maxSize.width, aReflowState.maxSize.height);
1998-05-12 08:17:56 +04:00
nsReflowStatus result = NS_FRAME_COMPLETE;
1998-04-14 00:24:54 +04:00
mChildCount = 0;
mFirstContentOffset = mLastContentOffset = 0;
NS_ASSERTION(nsnull != mContent, "null content");
nsTablePart* table = (nsTablePart*)mContent;
1998-04-14 00:24:54 +04:00
// Ensure the cell map
table->EnsureCellMap();
1998-04-14 00:24:54 +04:00
nsSize availSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE); // availSize is the space available at any given time in the process
nsSize maxSize(0, 0); // maxSize is the size of the largest child so far in the process
nsSize kidMaxSize(0,0);
nsReflowMetrics kidSize(&kidMaxSize);
1998-04-14 00:24:54 +04:00
nscoord y = 0;
nscoord maxAscent = 0;
nscoord maxDescent = 0;
PRInt32 kidIndex = 0;
PRInt32 lastIndex = table->ChildCount();
1998-04-14 00:24:54 +04:00
PRInt32 contentOffset=0;
nsIFrame* prevKidFrame = nsnull;/* XXX incremental reflow! */
// Compute the insets (sum of border and padding)
1998-06-05 10:09:09 +04:00
const nsStyleSpacing* spacing =
(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
1998-05-13 02:28:01 +04:00
nsMargin borderPadding;
spacing->CalcBorderPaddingFor(this, borderPadding);
nscoord topInset = borderPadding.top;
nscoord rightInset = borderPadding.right;
nscoord bottomInset = borderPadding.bottom;
nscoord leftInset = borderPadding.left;
nsReflowReason reflowReason = aReflowState.reason;
1998-04-14 00:24:54 +04:00
/* assumes that Table's children are in the following order:
* Captions
* ColGroups, in order
* THead, in order
* TFoot, in order
* TBody, in order
*/
for (;;) {
nsIContentPtr kid = table->ChildAt(kidIndex); // kid: REFCNT++
if (kid.IsNull()) {
1998-05-12 08:17:56 +04:00
result = NS_FRAME_COMPLETE;
1998-04-14 00:24:54 +04:00
break;
}
mLastContentIsComplete = PR_TRUE;
const PRInt32 contentType = ((nsTableContent *)(nsIContent*)kid)->GetType();
if (contentType!=nsITableContent::kTableCaptionType)
1998-04-14 00:24:54 +04:00
{
// Resolve style
nsIStyleContextPtr kidStyleContext =
1998-04-14 00:24:54 +04:00
aPresContext->ResolveStyleContextFor(kid, this);
NS_ASSERTION(kidStyleContext.IsNotNull(), "null style context for kid");
1998-04-14 00:24:54 +04:00
// SEC: TODO: when content is appended or deleted, be sure to clear out the frame hierarchy!!!!
1998-04-21 02:49:15 +04:00
// get next frame, creating one if needed
nsIFrame* kidFrame=nsnull;
if (nsnull!=prevKidFrame)
prevKidFrame->GetNextSibling(kidFrame); // no need to check for an error, just see if it returned null...
else
ChildAt(0, kidFrame);
// if this is the first time, allocate the frame
1998-04-14 00:24:54 +04:00
if (nsnull==kidFrame)
{
nsIContentDelegate* kidDel;
kidDel = kid->GetDelegate(aPresContext);
nsresult rv = kidDel->CreateFrame(aPresContext, kid,
this, kidStyleContext, kidFrame);
reflowReason = eReflowReason_Initial;
1998-04-14 00:24:54 +04:00
NS_RELEASE(kidDel);
}
nsSize maxKidElementSize(0,0);
nsReflowState kidReflowState(kidFrame, aReflowState, availSize,
reflowReason);
kidFrame->WillReflow(*aPresContext);
result = ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState);
1998-04-14 00:24:54 +04:00
// Place the child since some of it's content fit in us.
if (gsDebug) {
printf ("reflow of row group returned desired=%d,%d, max-element=%d,%d\n",
kidSize.width, kidSize.height, kidMaxSize.width, kidMaxSize.height);
1998-04-14 00:24:54 +04:00
}
PRInt32 yCoord = y;
if (NS_UNCONSTRAINEDSIZE!=yCoord)
yCoord+= topInset;
kidFrame->SetRect(nsRect(leftInset, yCoord,
kidSize.width, kidSize.height));
if (NS_UNCONSTRAINEDSIZE==kidSize.height)
y = NS_UNCONSTRAINEDSIZE;
else
y += kidSize.height;
if (kidMaxSize.width > maxSize.width) {
maxSize.width = kidMaxSize.width;
}
if (kidMaxSize.height > maxSize.height) {
maxSize.height = kidMaxSize.height;
1998-04-14 00:24:54 +04:00
}
// Link child frame into the list of children
if (nsnull != prevKidFrame) {
prevKidFrame->SetNextSibling(kidFrame);
} else {
// Our first child (**blush**)
mFirstChild = kidFrame;
SetFirstContentOffset(kidFrame);
if (gsDebug) printf("INNER: set first content offset to %d\n", GetFirstContentOffset()); //@@@
}
prevKidFrame = kidFrame;
mChildCount++;
1998-05-12 08:17:56 +04:00
if (NS_FRAME_IS_NOT_COMPLETE(result)) {
1998-04-14 00:24:54 +04:00
// 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;
break;
}
}
contentOffset++;
kidIndex++;
1998-05-12 08:17:56 +04:00
if (NS_FRAME_IS_NOT_COMPLETE(result)) {
1998-04-14 00:24:54 +04:00
// 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;
break;
}
}
// Recalculate Layout Dependencies
RecalcLayoutData();
1998-04-14 00:24:54 +04:00
if (nsnull != prevKidFrame) {
NS_ASSERTION(IsLastChild(prevKidFrame), "unexpected last child");
1998-04-14 00:24:54 +04:00
// can't use SetLastContentOffset here
mLastContentOffset = contentOffset-1; // takes into account colGroup frame we're not using
if (gsDebug) printf("INNER: set last content offset to %d\n", GetLastContentOffset()); //@@@
}
mFirstPassValid = PR_TRUE;
return result;
}
/** the second of 2 reflow passes
*/
1998-05-12 08:17:56 +04:00
nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
const nsReflowState& aReflowState,
1998-05-12 08:17:56 +04:00
PRInt32 aMinCaptionWidth,
PRInt32 mMaxCaptionWidth)
1998-04-14 00:24:54 +04:00
{
NS_PRECONDITION(aReflowState.frame == this, "bad reflow state");
NS_PRECONDITION(aReflowState.parentReflowState->frame == mGeometricParent,
"bad parent reflow state");
1998-04-14 00:24:54 +04:00
if (gsDebug==PR_TRUE)
printf("nsTableFrame::ResizeReflow Pass2 %p: maxSize=%d,%d\n",
this, aReflowState.maxSize.width, aReflowState.maxSize.height);
1998-04-14 00:24:54 +04:00
1998-05-12 08:17:56 +04:00
nsReflowStatus result = NS_FRAME_COMPLETE;
1998-04-14 00:24:54 +04:00
// now that we've computed the column width information, reflow all children
nsIContent* c = mContent;
NS_ASSERTION(nsnull != c, "null kid");
nsSize kidMaxSize(0,0);
PRInt32 kidIndex = 0;
PRInt32 lastIndex = c->ChildCount();
nsIFrame* prevKidFrame = nsnull;/* XXX incremental reflow! */
#ifdef NS_DEBUG
//PreReflowCheck();
1998-04-14 00:24:54 +04:00
#endif
// Initialize out parameter
if (nsnull != aDesiredSize.maxElementSize) {
aDesiredSize.maxElementSize->width = 0;
aDesiredSize.maxElementSize->height = 0;
1998-04-14 00:24:54 +04:00
}
PRBool reflowMappedOK = PR_TRUE;
1998-05-12 08:17:56 +04:00
nsReflowStatus status = NS_FRAME_COMPLETE;
1998-04-14 00:24:54 +04:00
// Check for an overflow list
MoveOverflowToChildList();
1998-06-05 10:09:09 +04:00
const nsStyleSpacing* mySpacing = (const nsStyleSpacing*)
mStyleContext->GetStyleData(eStyleStruct_Spacing);
1998-05-13 02:28:01 +04:00
nsMargin myBorderPadding;
mySpacing->CalcBorderPaddingFor(this, myBorderPadding);
InnerTableReflowState state(aPresContext, aReflowState, myBorderPadding);
1998-04-14 00:24:54 +04:00
// Reflow the existing frames
if (nsnull != mFirstChild) {
reflowMappedOK = ReflowMappedChildren(aPresContext, state, aDesiredSize.maxElementSize);
1998-04-14 00:24:54 +04:00
if (PR_FALSE == reflowMappedOK) {
status = NS_FRAME_NOT_COMPLETE;
1998-04-14 00:24:54 +04:00
}
}
1998-04-15 01:45:28 +04:00
// Did we successfully reflow our mapped children?
1998-04-14 00:24:54 +04:00
if (PR_TRUE == reflowMappedOK) {
// Any space left?
if (state.availSize.height <= 0) {
1998-04-14 00:24:54 +04:00
// No space left. Don't try to pull-up children or reflow unmapped
if (NextChildOffset() < mContent->ChildCount()) {
status = NS_FRAME_NOT_COMPLETE;
1998-04-14 00:24:54 +04:00
}
} else if (NextChildOffset() < mContent->ChildCount()) {
// Try and pull-up some children from a next-in-flow
if (PullUpChildren(aPresContext, state, aDesiredSize.maxElementSize)) {
1998-04-14 00:24:54 +04:00
// If we still have unmapped children then create some new frames
if (NextChildOffset() < mContent->ChildCount()) {
status = ReflowUnmappedChildren(aPresContext, state, aDesiredSize.maxElementSize);
1998-04-14 00:24:54 +04:00
}
} else {
// We were unable to pull-up all the existing frames from the
// next in flow
status = NS_FRAME_NOT_COMPLETE;
1998-04-14 00:24:54 +04:00
}
}
}
// Return our size and our status
1998-05-12 08:17:56 +04:00
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
1998-04-14 00:24:54 +04:00
// Don't forget to add in the bottom margin from our last child.
// Only add it in if there's room for it.
nscoord margin = state.prevMaxPosBottomMargin -
state.prevMaxNegBottomMargin;
if (state.availSize.height >= margin) {
state.y += margin;
}
}
// Return our desired rect
//NS_ASSERTION(0<state.y, "illegal height after reflow");
aDesiredSize.width = aReflowState.maxSize.width;
1998-04-14 00:24:54 +04:00
aDesiredSize.height = state.y;
// shrink wrap rows to height of tallest cell in that row
ShrinkWrapChildren(aPresContext, aDesiredSize, aDesiredSize.maxElementSize);
1998-04-14 00:24:54 +04:00
if (gsDebug==PR_TRUE)
{
if (nsnull!=aDesiredSize.maxElementSize)
printf("Inner table reflow complete, returning aDesiredSize = %d,%d and aMaxElementSize=%d,%d\n",
1998-04-14 00:24:54 +04:00
aDesiredSize.width, aDesiredSize.height,
aDesiredSize.maxElementSize->width, aDesiredSize.maxElementSize->height);
1998-04-14 00:24:54 +04:00
else
printf("Inner table reflow complete, returning aDesiredSize = %d,%d and NSNULL aMaxElementSize\n",
1998-04-14 00:24:54 +04:00
aDesiredSize.width, aDesiredSize.height);
}
// SEC: assign our real width and height based on this reflow step and return
mPass = kPASS_UNDEFINED; // we're no longer in-process
#ifdef NS_DEBUG
//PostReflowCheck(status);
1998-04-14 00:24:54 +04:00
#endif
return status;
}
// Collapse child's top margin with previous bottom margin
nscoord nsTableFrame::GetTopMarginFor(nsIPresContext* aCX,
InnerTableReflowState& aState,
1998-05-13 02:28:01 +04:00
const nsMargin& aKidMargin)
1998-04-14 00:24:54 +04:00
{
nscoord margin;
nscoord maxNegTopMargin = 0;
nscoord maxPosTopMargin = 0;
1998-05-13 02:28:01 +04:00
if ((margin = aKidMargin.top) < 0) {
1998-04-14 00:24:54 +04:00
maxNegTopMargin = -margin;
} else {
maxPosTopMargin = margin;
}
nscoord maxPos = PR_MAX(aState.prevMaxPosBottomMargin, maxPosTopMargin);
nscoord maxNeg = PR_MAX(aState.prevMaxNegBottomMargin, maxNegTopMargin);
margin = maxPos - maxNeg;
return margin;
}
// Position and size aKidFrame and update our reflow state. The origin of
// aKidRect is relative to the upper-left origin of our frame, and includes
// any left/top margin.
void nsTableFrame::PlaceChild(nsIPresContext* aPresContext,
InnerTableReflowState& aState,
nsIFrame* aKidFrame,
const nsRect& aKidRect,
nsSize* aMaxElementSize,
nsSize& aKidMaxElementSize)
{
1998-04-21 02:49:15 +04:00
if (PR_TRUE==gsDebug)
printf ("table: placing row group at %d, %d, %d, %d\n",
aKidRect.x, aKidRect.y, aKidRect.width, aKidRect.height);
1998-04-14 00:24:54 +04:00
// Place and size the child
aKidFrame->SetRect(aKidRect);
// Adjust the running y-offset
aState.y += aKidRect.height;
// If our height is constrained then update the available height
if (PR_FALSE == aState.unconstrainedHeight) {
aState.availSize.height -= aKidRect.height;
}
// If this is a footer row group, add it to the list of footer row groups
nsIAtom * tFootTag = NS_NewAtom(nsTablePart::kRowGroupFootTagString); // tFootTag: REFCNT++
nsIAtom *kidType=nsnull;
((nsTableRowGroupFrame *)aKidFrame)->GetRowGroupType(kidType);
if (tFootTag==kidType)
{
if (nsnull==aState.footerList)
aState.footerList = new nsVoidArray();
aState.footerList->AppendElement((void *)aKidFrame);
aState.footerHeight += aKidRect.height;
}
// else if this is a body row group, push down all the footer row groups
else
{
// don't bother unless there are footers to push down
if (nsnull!=aState.footerList && 0!=aState.footerList->Count())
{
nsPoint origin;
aKidFrame->GetOrigin(origin);
origin.y -= aState.footerHeight;
aKidFrame->MoveTo(origin.x, origin.y);
nsIAtom * tBodyTag = NS_NewAtom(nsTablePart::kRowGroupBodyTagString); // tBodyTag: REFCNT++
if (tBodyTag==kidType)
{
PRInt32 numFooters = aState.footerList->Count();
for (PRInt32 footerIndex = 0; footerIndex < numFooters; footerIndex++)
{
nsTableRowGroupFrame * footer = (nsTableRowGroupFrame *)(aState.footerList->ElementAt(footerIndex));
NS_ASSERTION(nsnull!=footer, "bad footer list in table inner frame.");
if (nsnull!=footer)
{
footer->GetOrigin(origin);
origin.y += aKidRect.height;
footer->MoveTo(origin.x, origin.y);
}
}
}
NS_RELEASE(tBodyTag);
}
}
NS_RELEASE(tFootTag);
1998-04-14 00:24:54 +04:00
// Update the maximum element size
if (PR_TRUE==aState.firstRowGroup)
{
aState.firstRowGroup = PR_FALSE;
if (nsnull != aMaxElementSize) {
aMaxElementSize->width = aKidMaxElementSize.width;
aMaxElementSize->height = aKidMaxElementSize.height;
}
}
}
/**
* Reflow the frames we've already created
*
* @param aPresContext presentation context to use
* @param aState current inline state
* @return true if we successfully reflowed all the mapped children and false
* otherwise, e.g. we pushed children to the next in flow
*/
PRBool nsTableFrame::ReflowMappedChildren( nsIPresContext* aPresContext,
InnerTableReflowState& aState,
nsSize* aMaxElementSize)
{
#ifdef NS_DEBUG
VerifyLastIsComplete();
#endif
#ifdef NOISY
ListTag(stdout);
printf(": reflow mapped (childCount=%d) [%d,%d,%c]\n",
mChildCount,
mFirstContentOffset, mLastContentOffset,
(mLastContentIsComplete ? 'T' : 'F'));
#ifdef NOISY_FLOW
{
nsTableFrame* flow = (nsTableFrame*) mNextInFlow;
while (flow != 0) {
printf(" %p: [%d,%d,%c]\n",
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
(flow->mLastContentIsComplete ? 'T' : 'F'));
flow = (nsTableFrame*) flow->mNextInFlow;
}
}
#endif
#endif
NS_PRECONDITION(nsnull != mFirstChild, "no children");
PRInt32 childCount = 0;
nsIFrame* prevKidFrame = nsnull;
// Remember our original mLastContentIsComplete so that if we end up
// having to push children, we have the correct value to hand to
// PushChildren.
PRBool originalLastContentIsComplete = mLastContentIsComplete;
1998-04-14 00:24:54 +04:00
nsSize kidMaxElementSize(0,0);
1998-04-14 00:24:54 +04:00
nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull;
PRBool result = PR_TRUE;
for (nsIFrame* kidFrame = mFirstChild; nsnull != kidFrame; ) {
1998-05-12 08:17:56 +04:00
nsSize kidAvailSize(aState.availSize);
nsReflowMetrics desiredSize(pKidMaxElementSize);
desiredSize.width=desiredSize.height=desiredSize.ascent=desiredSize.descent=0;
1998-05-12 08:17:56 +04:00
nsReflowStatus status;
1998-04-14 00:24:54 +04:00
// Get top margin for this kid
nsIContentPtr kid;
kidFrame->GetContent(kid.AssignRef());
const int contentType = ((nsTableContent *)(nsIContent*)kid)->GetType();
if (contentType != nsITableContent::kTableCaptionType)
{ // for all colgroups and rowgroups...
nsIStyleContextPtr kidSC;
kidFrame->GetStyleContext(aPresContext, kidSC.AssignRef());
1998-06-05 10:09:09 +04:00
const nsStyleSpacing* kidSpacing = (const nsStyleSpacing*)
kidSC->GetStyleData(eStyleStruct_Spacing);
1998-05-13 02:28:01 +04:00
nsMargin kidMargin;
kidSpacing->CalcMarginFor(kidFrame, kidMargin);
nscoord topMargin = GetTopMarginFor(aPresContext, aState, kidMargin);
nscoord bottomMargin = kidMargin.bottom;
1998-04-14 00:24:54 +04:00
// Figure out the amount of available size for the child (subtract
// off the top margin we are going to apply to it)
if (PR_FALSE == aState.unconstrainedHeight) {
kidAvailSize.height -= topMargin;
}
// Subtract off for left and right margin
if (PR_FALSE == aState.unconstrainedWidth) {
1998-05-13 02:28:01 +04:00
kidAvailSize.width -= kidMargin.left + kidMargin.right;
1998-04-14 00:24:54 +04:00
}
// Reflow the child into the available space
nsReflowState kidReflowState(kidFrame, aState.reflowState, kidAvailSize,
eReflowReason_Resize);
kidFrame->WillReflow(*aPresContext);
status = ReflowChild(kidFrame, aPresContext, desiredSize, kidReflowState);
1998-04-14 00:24:54 +04:00
// Did the child fit?
if ((kidFrame != mFirstChild) && (desiredSize.height > kidAvailSize.height))
1998-04-14 00:24:54 +04:00
{
// The child is too wide to fit in the available space, and it's
// not our first child
1998-04-14 00:24:54 +04:00
// Since we are giving the next-in-flow our last child, we
// give it our original mLastContentIsComplete too (in case we
1998-04-14 00:24:54 +04:00
// are pushing into an empty next-in-flow)
PushChildren(kidFrame, prevKidFrame, originalLastContentIsComplete);
SetLastContentOffset(prevKidFrame);
1998-04-14 00:24:54 +04:00
result = PR_FALSE;
break;
}
// Place the child after taking into account it's margin
aState.y += topMargin;
nsRect kidRect (0, 0, desiredSize.width, desiredSize.height);
1998-05-13 02:28:01 +04:00
kidRect.x += aState.leftInset + kidMargin.left;
kidRect.y += aState.topInset + aState.y;
if (contentType == nsITableContent::kTableRowGroupType)
{
PlaceChild(aPresContext, aState, kidFrame, kidRect,
aMaxElementSize, kidMaxElementSize);
if (bottomMargin < 0) {
aState.prevMaxNegBottomMargin = -bottomMargin;
} else {
aState.prevMaxPosBottomMargin = bottomMargin;
}
1998-04-14 00:24:54 +04:00
}
childCount++;
// Remember where we just were in case we end up pushing children
prevKidFrame = kidFrame;
1998-04-14 00:24:54 +04:00
// Update mLastContentIsComplete now that this kid fits
1998-05-12 08:17:56 +04:00
mLastContentIsComplete = PRBool(NS_FRAME_IS_COMPLETE(status));
1998-04-14 00:24:54 +04:00
// Special handling for incomplete children
1998-05-12 08:17:56 +04:00
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
nsIFrame* kidNextInFlow;
kidFrame->GetNextInFlow(kidNextInFlow);
PRBool lastContentIsComplete = mLastContentIsComplete;
1998-04-14 00:24:54 +04:00
if (nsnull == kidNextInFlow) {
// The child doesn't have a next-in-flow so create a continuing
// frame. This hooks the child into the flow
nsIFrame* continuingFrame;
nsIStyleContext* kidSC;
kidFrame->GetStyleContext(aPresContext, kidSC);
kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame);
NS_RELEASE(kidSC);
NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
1998-04-14 00:24:54 +04:00
// Add the continuing frame to the sibling list
nsIFrame* nextSib;
kidFrame->GetNextSibling(nextSib);
1998-04-14 00:24:54 +04:00
continuingFrame->SetNextSibling(nextSib);
kidFrame->SetNextSibling(continuingFrame);
if (nsnull == nextSib) {
// Assume that the continuation frame we just created is
// complete, for now. It will get reflowed by our
// next-in-flow (we are going to push it now)
lastContentIsComplete = PR_TRUE;
}
}
// We've used up all of our available space so push the remaining
// children to the next-in-flow
nsIFrame* nextSibling;
kidFrame->GetNextSibling(nextSibling);
if (nsnull != nextSibling) {
PushChildren(nextSibling, kidFrame, lastContentIsComplete);
SetLastContentOffset(prevKidFrame);
}
result = PR_FALSE;
break;
1998-04-14 00:24:54 +04:00
}
}
// Get the next child
kidFrame->GetNextSibling(kidFrame);
1998-04-14 00:24:54 +04:00
// XXX talk with troy about checking for available space here
}
// Update the child count
mChildCount = childCount;
#ifdef NS_DEBUG
1998-04-14 00:24:54 +04:00
NS_POSTCONDITION(LengthOf(mFirstChild) == mChildCount, "bad child count");
nsIFrame* lastChild;
PRInt32 lastIndexInParent;
LastChild(lastChild);
lastChild->GetContentIndex(lastIndexInParent);
NS_POSTCONDITION(lastIndexInParent == mLastContentOffset, "bad last content offset");
#endif
1998-04-14 00:24:54 +04:00
#ifdef NS_DEBUG
VerifyLastIsComplete();
#endif
#ifdef NOISY
ListTag(stdout);
printf(": reflow mapped %sok (childCount=%d) [%d,%d,%c]\n",
(result ? "" : "NOT "),
mChildCount,
mFirstContentOffset, mLastContentOffset,
(mLastContentIsComplete ? 'T' : 'F'));
#ifdef NOISY_FLOW
{
nsTableFrame* flow = (nsTableFrame*) mNextInFlow;
while (flow != 0) {
printf(" %p: [%d,%d,%c]\n",
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
(flow->mLastContentIsComplete ? 'T' : 'F'));
flow = (nsTableFrame*) flow->mNextInFlow;
}
}
#endif
#endif
return result;
}
/**
* Try and pull-up frames from our next-in-flow
*
* @param aPresContext presentation context to use
* @param aState current inline state
* @return true if we successfully pulled-up all the children and false
* otherwise, e.g. child didn't fit
*/
PRBool nsTableFrame::PullUpChildren(nsIPresContext* aPresContext,
InnerTableReflowState& aState,
nsSize* aMaxElementSize)
1998-04-14 00:24:54 +04:00
{
#ifdef NS_DEBUG
VerifyLastIsComplete();
#endif
#ifdef NOISY
ListTag(stdout);
printf(": pullup (childCount=%d) [%d,%d,%c]\n",
mChildCount,
mFirstContentOffset, mLastContentOffset,
(mLastContentIsComplete ? 'T' : 'F'));
#ifdef NOISY_FLOW
{
nsTableFrame* flow = (nsTableFrame*) mNextInFlow;
while (flow != 0) {
printf(" %p: [%d,%d,%c]\n",
flow, flow->mFirstContentOffset, flow->mLastContentOffset,
(flow->mLastContentIsComplete ? 'T' : 'F'));
flow = (nsTableFrame*) flow->mNextInFlow;
}
}
#endif
#endif
nsTableFrame* nextInFlow = (nsTableFrame*)mNextInFlow;
nsSize kidMaxElementSize(0,0);
nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull;
#ifdef NS_DEBUG
PRInt32 kidIndex = NextChildOffset();
#endif
nsIFrame* prevKidFrame;
LastChild(prevKidFrame);
1998-04-14 00:24:54 +04:00
// This will hold the prevKidFrame's mLastContentIsComplete
// status. If we have to push the frame that follows prevKidFrame
// then this will become our mLastContentIsComplete state. Since
// prevKidFrame is initially our last frame, it's completion status
// is our mLastContentIsComplete value.
PRBool prevLastContentIsComplete = mLastContentIsComplete;
1998-04-14 00:24:54 +04:00
PRBool result = PR_TRUE;
while (nsnull != nextInFlow) {
nsReflowMetrics kidSize(pKidMaxElementSize);
kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0;
1998-05-12 08:17:56 +04:00
nsReflowStatus status;
// Get the next child
nsIFrame* kidFrame = nextInFlow->mFirstChild;
1998-04-14 00:24:54 +04:00
// Any more child frames?
1998-04-14 00:24:54 +04:00
if (nsnull == kidFrame) {
// No. Any frames on its overflow list?
if (nsnull != nextInFlow->mOverflowList) {
// Move the overflow list to become the child list
nextInFlow->AppendChildren(nextInFlow->mOverflowList);
nextInFlow->mOverflowList = nsnull;
kidFrame = nextInFlow->mFirstChild;
} else {
// We've pulled up all the children, so move to the next-in-flow.
nextInFlow->GetNextInFlow((nsIFrame*&)nextInFlow);
continue;
}
1998-04-14 00:24:54 +04:00
}
// See if the child fits in the available space. If it fits or
// it's splittable then reflow it. The reason we can't just move
// it is that we still need ascent/descent information
nsSize kidFrameSize(0,0);
nsSplittableType kidIsSplittable;
kidFrame->GetSize(kidFrameSize);
kidFrame->IsSplittable(kidIsSplittable);
if ((kidFrameSize.height > aState.availSize.height) &&
NS_FRAME_IS_NOT_SPLITTABLE(kidIsSplittable)) {
result = PR_FALSE;
mLastContentIsComplete = prevLastContentIsComplete;
break;
1998-04-14 00:24:54 +04:00
}
nsReflowState kidReflowState(kidFrame, aState.reflowState, aState.availSize,
eReflowReason_Resize);
kidFrame->WillReflow(*aPresContext);
status = ReflowChild(kidFrame, aPresContext, kidSize, kidReflowState);
// Did the child fit?
if ((kidSize.height > aState.availSize.height) && (nsnull != mFirstChild)) {
// The child is too wide to fit in the available space, and it's
// not our first child
result = PR_FALSE;
mLastContentIsComplete = prevLastContentIsComplete;
break;
1998-04-14 00:24:54 +04:00
}
// Advance y by the topMargin between children. Zero out the
// topMargin in case this frame is continued because
// continuations do not have a top margin. Update the prev
// bottom margin state in the body reflow state so that we can
// apply the bottom margin when we hit the next child (or
// finish).
//aState.y += topMargin;
nsRect kidRect (0, 0, kidSize.width, kidSize.height);
//kidRect.x += kidMol->margin.left;
kidRect.y += aState.y;
nsIContent* content=nsnull;
kidFrame->GetContent(content);
NS_ASSERTION(nsnull!=content, "bad kid");
const int contentType = ((nsITableContent*)content)->GetType();
NS_RELEASE(content);
if (contentType == nsITableContent::kTableRowGroupType)
{
PlaceChild(aPresContext, aState, kidFrame, kidRect, aMaxElementSize, *pKidMaxElementSize);
}
// Remove the frame from its current parent
kidFrame->GetNextSibling(nextInFlow->mFirstChild);
nextInFlow->mChildCount--;
// Update the next-in-flows first content offset
if (nsnull != nextInFlow->mFirstChild) {
nextInFlow->SetFirstContentOffset(nextInFlow->mFirstChild);
}
1998-04-14 00:24:54 +04:00
// Link the frame into our list of children
kidFrame->SetGeometricParent(this);
nsIFrame* contentParent;
kidFrame->GetContentParent(contentParent);
if (nextInFlow == contentParent) {
kidFrame->SetContentParent(this);
}
if (nsnull == prevKidFrame) {
mFirstChild = kidFrame;
SetFirstContentOffset(kidFrame);
} else {
prevKidFrame->SetNextSibling(kidFrame);
}
kidFrame->SetNextSibling(nsnull);
mChildCount++;
1998-04-14 00:24:54 +04:00
// Remember where we just were in case we end up pushing children
prevKidFrame = kidFrame;
prevLastContentIsComplete = mLastContentIsComplete;
1998-04-14 00:24:54 +04:00
// Is the child we just pulled up complete?
1998-05-12 08:17:56 +04:00
mLastContentIsComplete = PRBool(NS_FRAME_IS_COMPLETE(status));
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
// No the child isn't complete
nsIFrame* kidNextInFlow;
kidFrame->GetNextInFlow(kidNextInFlow);
if (nsnull == kidNextInFlow) {
// The child doesn't have a next-in-flow so create a
// continuing frame. The creation appends it to the flow and
// prepares it for reflow.
nsIFrame* continuingFrame;
nsIStyleContext* kidSC;
kidFrame->GetStyleContext(aPresContext, kidSC);
kidFrame->CreateContinuingFrame(aPresContext, this, kidSC, continuingFrame);
NS_RELEASE(kidSC);
NS_ASSERTION(nsnull != continuingFrame, "frame creation failed");
// Add the continuing frame to our sibling list and then push
// it to the next-in-flow. This ensures the next-in-flow's
// content offsets and child count are set properly. Note that
// we can safely assume that the continuation is complete so
// we pass PR_TRUE into PushChidren in case our next-in-flow
// was just drained and now needs to know it's
// mLastContentIsComplete state.
kidFrame->SetNextSibling(continuingFrame);
1998-04-14 00:24:54 +04:00
PushChildren(continuingFrame, kidFrame, PR_TRUE);
1998-04-14 00:24:54 +04:00
// After we push the continuation frame we don't need to fuss
// with mLastContentIsComplete beause the continuation frame
// is no longer on *our* list.
1998-04-14 00:24:54 +04:00
}
// If the child isn't complete then it means that we've used up
// all of our available space.
result = PR_FALSE;
break;
}
1998-04-14 00:24:54 +04:00
}
// Update our last content offset
1998-04-14 00:24:54 +04:00
if (nsnull != prevKidFrame) {
NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
1998-04-14 00:24:54 +04:00
SetLastContentOffset(prevKidFrame);
}
// We need to make sure the first content offset is correct for any empty
// next-in-flow frames (frames where we pulled up all the child frames)
nextInFlow = (nsTableFrame*)mNextInFlow;
if ((nsnull != nextInFlow) && (nsnull == nextInFlow->mFirstChild)) {
// We have at least one empty frame. Did we succesfully pull up all the
// child frames?
if (PR_FALSE == result) {
// No, so we need to adjust the first content offset of all the empty
// frames
AdjustOffsetOfEmptyNextInFlows();
#ifdef NS_DEBUG
} else {
// Yes, we successfully pulled up all the child frames which means all
// the next-in-flows must be empty. Do a sanity check
while (nsnull != nextInFlow) {
NS_ASSERTION(nsnull == nextInFlow->mFirstChild, "non-empty next-in-flow");
nextInFlow->GetNextInFlow((nsIFrame*&)nextInFlow);
1998-04-14 00:24:54 +04:00
}
#endif
}
}
#ifdef NS_DEBUG
VerifyLastIsComplete();
#endif
return result;
}
/**
* Create new frames for content we haven't yet mapped
*
* @param aPresContext presentation context to use
* @param aState current inline state
* @return frComplete if all content has been mapped and frNotComplete
* if we should be continued
*/
1998-05-12 08:17:56 +04:00
nsReflowStatus
1998-04-14 00:24:54 +04:00
nsTableFrame::ReflowUnmappedChildren(nsIPresContext* aPresContext,
InnerTableReflowState& aState,
nsSize* aMaxElementSize)
1998-04-14 00:24:54 +04:00
{
#ifdef NS_DEBUG
VerifyLastIsComplete();
#endif
nsIFrame* kidPrevInFlow = nsnull;
nsReflowStatus result = NS_FRAME_NOT_COMPLETE;
1998-04-14 00:24:54 +04:00
// If we have no children and we have a prev-in-flow then we need to pick
// up where it left off. If we have children, e.g. we're being resized, then
// our content offset should already be set correctly...
if ((nsnull == mFirstChild) && (nsnull != mPrevInFlow)) {
nsTableFrame* prev = (nsTableFrame*)mPrevInFlow;
NS_ASSERTION(prev->mLastContentOffset >= prev->mFirstContentOffset, "bad prevInFlow");
mFirstContentOffset = prev->NextChildOffset();
if (!prev->mLastContentIsComplete) {
// Our prev-in-flow's last child is not complete
prev->LastChild(kidPrevInFlow);
1998-04-14 00:24:54 +04:00
}
}
mLastContentIsComplete = PR_TRUE;
1998-04-14 00:24:54 +04:00
// Place our children, one at a time until we are out of children
nsSize kidMaxElementSize(0,0);
1998-04-14 00:24:54 +04:00
nsSize* pKidMaxElementSize = (nsnull != aMaxElementSize) ? &kidMaxElementSize : nsnull;
PRInt32 kidIndex = NextChildOffset();
nsIFrame* prevKidFrame;
1998-04-14 00:24:54 +04:00
LastChild(prevKidFrame);
1998-04-14 00:24:54 +04:00
for (;;) {
// Get the next content object
nsIContentPtr kid = mContent->ChildAt(kidIndex);
if (kid.IsNull()) {
1998-05-12 08:17:56 +04:00
result = NS_FRAME_COMPLETE;
1998-04-14 00:24:54 +04:00
break;
}
// Make sure we still have room left
if (aState.availSize.height <= 0) {
// Note: return status was set to frNotComplete above...
break;
}
// Resolve style for the child
nsIStyleContextPtr kidStyleContext =
1998-04-14 00:24:54 +04:00
aPresContext->ResolveStyleContextFor(kid, this);
// Figure out how we should treat the child
nsIFrame* kidFrame;
1998-04-14 00:24:54 +04:00
// Create a child frame
if (nsnull == kidPrevInFlow) {
nsIContentDelegate* kidDel = nsnull;
kidDel = kid->GetDelegate(aPresContext);
nsresult rv = kidDel->CreateFrame(aPresContext, kid, this,
kidStyleContext, kidFrame);
1998-04-14 00:24:54 +04:00
NS_RELEASE(kidDel);
} else {
kidPrevInFlow->CreateContinuingFrame(aPresContext, this, kidStyleContext,
kidFrame);
1998-04-14 00:24:54 +04:00
}
// Try to reflow the child into the available space. It might not
// fit or might need continuing.
nsReflowMetrics kidSize(pKidMaxElementSize);
kidSize.width=kidSize.height=kidSize.ascent=kidSize.descent=0;
nsReflowState kidReflowState(kidFrame, aState.reflowState, aState.availSize,
eReflowReason_Initial);
kidFrame->WillReflow(*aPresContext);
nsReflowStatus status = ReflowChild(kidFrame,aPresContext, kidSize, kidReflowState);
// Did the child fit?
if ((kidSize.height > aState.availSize.height) && (nsnull != mFirstChild)) {
// The child is too wide to fit in the available space, and it's
// not our first child. Add the frame to our overflow list
NS_ASSERTION(nsnull == mOverflowList, "bad overflow list");
mOverflowList = kidFrame;
prevKidFrame->SetNextSibling(nsnull);
break;
}
// Advance y by the topMargin between children. Zero out the
// topMargin in case this frame is continued because
// continuations do not have a top margin. Update the prev
// bottom margin state in the body reflow state so that we can
// apply the bottom margin when we hit the next child (or
// finish).
//aState.y += topMargin;
nsRect kidRect (0, 0, kidSize.width, kidSize.height);
kidRect.y += aState.y;
nsIContent* content=nsnull;
kidFrame->GetContent(content);
NS_ASSERTION(nsnull!=content, "bad kid");
const int contentType = ((nsITableContent*)content)->GetType();
NS_RELEASE(content);
if (contentType == nsITableContent::kTableRowGroupType)
{
PlaceChild(aPresContext, aState, kidFrame, kidRect, aMaxElementSize, *pKidMaxElementSize);
}
// Link child frame into the list of children
1998-04-14 00:24:54 +04:00
if (nsnull != prevKidFrame) {
prevKidFrame->SetNextSibling(kidFrame);
} else {
mFirstChild = kidFrame; // our first child
1998-04-14 00:24:54 +04:00
SetFirstContentOffset(kidFrame);
}
prevKidFrame = kidFrame;
1998-04-14 00:24:54 +04:00
mChildCount++;
kidIndex++;
1998-04-14 00:24:54 +04:00
// Did the child complete?
1998-05-12 08:17:56 +04:00
if (NS_FRAME_IS_NOT_COMPLETE(status)) {
// If the child isn't complete then it means that we've used up
// all of our available space
mLastContentIsComplete = PR_FALSE;
break;
}
1998-04-14 00:24:54 +04:00
kidPrevInFlow = nsnull;
}
// Update the content mapping
NS_ASSERTION(IsLastChild(prevKidFrame), "bad last child");
SetLastContentOffset(prevKidFrame);
1998-04-14 00:24:54 +04:00
#ifdef NS_DEBUG
PRInt32 len = LengthOf(mFirstChild);
NS_ASSERTION(len == mChildCount, "bad child count");
#endif
#ifdef NS_DEBUG
1998-04-14 00:24:54 +04:00
VerifyLastIsComplete();
#endif
return result;
}
1998-04-14 00:24:54 +04:00
/**
Now I've got all the cells laid out in an infinite space.
For each column, use the min size for each cell in that column
along with the attributes of the table, column group, and column
to assign widths to each column.
*/
// use the cell map to determine which cell is in which column.
void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext,
const nsReflowState& aReflowState,
1998-04-14 00:24:54 +04:00
const nsSize& aMaxSize,
nsSize* aMaxElementSize)
{
NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!");
if (gsDebug)
printf ("BalanceColumnWidths...\n");
nsVoidArray *columnLayoutData = GetColumnLayoutData();
PRInt32 numCols = columnLayoutData->Count();
if (nsnull==mColumnWidths)
{
mColumnWidths = new PRInt32[numCols];
// SEC should be a memset
for (PRInt32 i = 0; i<numCols; i++)
mColumnWidths[i] = 0;
}
// need to track min and max table widths
PRInt32 minTableWidth = 0;
PRInt32 maxTableWidth = 0;
PRInt32 totalFixedWidth = 0;
1998-06-05 10:09:09 +04:00
const nsStyleSpacing* spacing =
(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
1998-05-13 02:28:01 +04:00
nsMargin borderPadding;
spacing->CalcBorderPaddingFor(this, borderPadding);
1998-04-25 22:48:48 +04:00
1998-04-14 00:24:54 +04:00
// need to figure out the overall table width constraint
1998-04-25 22:48:48 +04:00
// default case, get 100% of available space
PRInt32 maxWidth;
1998-06-05 10:09:09 +04:00
const nsStylePosition* position =
(const nsStylePosition*)mStyleContext->GetStyleData(eStyleStruct_Position);
1998-04-30 23:55:51 +04:00
switch (position->mWidth.GetUnit()) {
case eStyleUnit_Coord:
maxWidth = position->mWidth.GetCoordValue();
1998-04-25 22:48:48 +04:00
break;
1998-04-30 23:55:51 +04:00
case eStyleUnit_Percent:
case eStyleUnit_Proportional:
1998-04-25 22:48:48 +04:00
// XXX for now these fall through
default:
1998-04-30 23:55:51 +04:00
case eStyleUnit_Auto:
case eStyleUnit_Inherit:
1998-04-25 22:48:48 +04:00
maxWidth = aMaxSize.width;
break;
1998-04-14 00:24:54 +04:00
}
1998-04-25 22:48:48 +04:00
// now, if maxWidth is not NS_UNCONSTRAINED, subtract out my border
// and padding
1998-04-14 00:24:54 +04:00
if (NS_UNCONSTRAINEDSIZE!=maxWidth)
{
1998-05-13 02:28:01 +04:00
maxWidth -= borderPadding.left + borderPadding.right;
1998-04-14 00:24:54 +04:00
if (0>maxWidth) // nonsense style specification
maxWidth = 0;
}
if (gsDebug) printf (" maxWidth=%d from aMaxSize=%d,%d\n", maxWidth, aMaxSize.width, aMaxSize.height);
// based on the compatibility mode, create a table layout strategy
if (nsnull==mTableLayoutStrategy)
{ // TODO: build a different strategy based on the compatibility mode
mTableLayoutStrategy = new BasicTableLayoutStrategy(this);
1998-04-14 00:24:54 +04:00
}
mTableLayoutStrategy->BalanceColumnWidths(aPresContext, mStyleContext,
aReflowState,
maxWidth, numCols,
totalFixedWidth, minTableWidth, maxTableWidth,
aMaxElementSize);
1998-04-25 22:48:48 +04:00
1998-04-14 00:24:54 +04:00
}
/**
sum the width of each column
add in table insets
set rect
*/
1998-04-25 22:48:48 +04:00
void nsTableFrame::SetTableWidth(nsIPresContext* aPresContext)
1998-04-14 00:24:54 +04:00
{
NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!");
if (gsDebug==PR_TRUE) printf ("SetTableWidth...");
PRInt32 tableWidth = 0;
nsVoidArray *columnLayoutData = GetColumnLayoutData();
PRInt32 numCols = columnLayoutData->Count();
for (PRInt32 i = 0; i<numCols; i++)
{
tableWidth += mColumnWidths[i];
if (gsDebug==PR_TRUE) printf (" += %d ", mColumnWidths[i]);
}
// Compute the insets (sum of border and padding)
1998-06-05 10:09:09 +04:00
const nsStyleSpacing* spacing =
(const nsStyleSpacing*)mStyleContext->GetStyleData(eStyleStruct_Spacing);
1998-05-13 02:28:01 +04:00
nsMargin borderPadding;
spacing->CalcBorderPaddingFor(this, borderPadding);
nscoord rightInset = borderPadding.right;
nscoord leftInset = borderPadding.left;
1998-04-14 00:24:54 +04:00
tableWidth += (leftInset + rightInset);
nsRect tableSize = mRect;
1998-04-14 00:24:54 +04:00
tableSize.width = tableWidth;
if (gsDebug==PR_TRUE)
{
printf ("setting table rect to %d, %d after adding insets %d, %d\n",
tableSize.width, tableSize.height, rightInset, leftInset);
}
SetRect(tableSize);
}
/**
*/
void nsTableFrame::ShrinkWrapChildren(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,
nsSize* aMaxElementSize)
{
#ifdef NS_DEBUG
PRBool gsDebugWas = gsDebug;
//gsDebug = PR_TRUE; // turn on debug in this method
1998-04-14 00:24:54 +04:00
#endif
// iterate children, tell all row groups to ShrinkWrap
PRBool atLeastOneRowSpanningCell = PR_FALSE;
1998-04-25 22:48:48 +04:00
1998-05-20 03:11:28 +04:00
PRInt32 rowIndex;
1998-04-14 00:24:54 +04:00
PRInt32 tableHeight = 0;
1998-04-25 22:48:48 +04:00
1998-06-05 10:09:09 +04:00
const nsStyleSpacing* spacing = (const nsStyleSpacing*)
mStyleContext->GetStyleData(eStyleStruct_Spacing);
1998-05-13 02:28:01 +04:00
nsMargin borderPadding;
spacing->CalcBorderPaddingFor(this, borderPadding);
tableHeight += borderPadding.top + borderPadding.bottom;
1998-04-25 22:48:48 +04:00
PRInt32 childCount = mChildCount;
nsIFrame * kidFrame;
1998-04-14 00:24:54 +04:00
for (PRInt32 i = 0; i < childCount; i++)
{
PRInt32 childHeight=0;
// for every child that is a rowFrame, set the row frame height = sum of row heights
if (0==i)
ChildAt(i, kidFrame); // frames are not ref counted
else
kidFrame->GetNextSibling(kidFrame);
1998-04-14 00:24:54 +04:00
NS_ASSERTION(nsnull != kidFrame, "bad kid frame");
nsTableContentPtr kid;
kidFrame->GetContent((nsIContent*&)(kid.AssignRef())); // kid: REFCNT++
NS_ASSERTION(kid.IsNotNull(), "bad kid");
nscoord topInnerMargin = 0;
nscoord bottomInnerMargin = 0;
1998-04-14 00:24:54 +04:00
if (kid->GetType() == nsITableContent::kTableRowGroupType)
{
/* Step 1: set the row height to the height of the tallest cell,
* and resize all cells in that row to that height (except cells with rowspan>1)
*/
PRInt32 rowGroupHeight = 0;
nsTableRowGroupFrame * rowGroupFrame = (nsTableRowGroupFrame *)kidFrame;
PRInt32 numRows;
rowGroupFrame->ChildCount(numRows);
1998-04-14 00:24:54 +04:00
PRInt32 *rowHeights = new PRInt32[numRows];
if (gsDebug==PR_TRUE) printf("Height Step 1...\n");
1998-05-20 03:11:28 +04:00
for (rowIndex = 0; rowIndex < numRows; rowIndex++)
1998-04-14 00:24:54 +04:00
{
// get the height of the tallest cell in the row (excluding cells that span rows)
nsTableRowFrame *rowFrame;
rowGroupFrame->ChildAt(rowIndex, (nsIFrame*&)rowFrame);
1998-04-14 00:24:54 +04:00
NS_ASSERTION(nsnull != rowFrame, "bad row frame");
nscoord maxCellHeight = rowFrame->GetTallestChild();
nscoord maxCellTopMargin = rowFrame->GetChildMaxTopMargin();
nscoord maxCellBottomMargin = rowFrame->GetChildMaxBottomMargin();
nscoord maxRowHeight = maxCellHeight + maxCellTopMargin + maxCellBottomMargin;
rowHeights[rowIndex] = maxRowHeight;
if (rowIndex == 0)
topInnerMargin = maxCellTopMargin;
if (rowIndex+1 == numRows)
bottomInnerMargin = maxCellBottomMargin;
nsSize rowFrameSize(0,0);
rowFrame->GetSize(rowFrameSize);
rowFrame->SizeTo(rowFrameSize.width, maxRowHeight);
rowGroupHeight += maxRowHeight;
1998-04-14 00:24:54 +04:00
// resize all the cells based on the rowHeight
PRInt32 numCells;
rowFrame->ChildCount(numCells);
1998-04-14 00:24:54 +04:00
for (PRInt32 cellIndex = 0; cellIndex < numCells; cellIndex++)
{
nsTableCellFrame *cellFrame;
rowFrame->ChildAt(cellIndex, (nsIFrame*&)cellFrame);
1998-04-14 00:24:54 +04:00
PRInt32 rowSpan = cellFrame->GetRowSpan();
if (1==rowSpan)
{
if (gsDebug==PR_TRUE) printf(" setting cell[%d,%d] height to %d\n", rowIndex, cellIndex, rowHeights[rowIndex]);
nsSize cellFrameSize(0,0);
cellFrame->GetSize(cellFrameSize);
cellFrame->SizeTo(cellFrameSize.width, maxCellHeight);
1998-04-14 00:24:54 +04:00
// Realign cell content based on new height
cellFrame->VerticallyAlignChild(aPresContext);
}
else
{
if (gsDebug==PR_TRUE) printf(" skipping cell[%d,%d]\n", rowIndex, cellIndex);
1998-04-14 00:24:54 +04:00
atLeastOneRowSpanningCell = PR_TRUE;
}
}
}
/* Step 2: now account for cells that span rows.
* a spanning cell's height is the sum of the heights of the rows it spans,
* or it's own desired height, whichever is greater.
* If the cell's desired height is the larger value, resize the rows and contained
* cells by an equal percentage of the additional space.
*/
/* TODO
* 1. optimization, if (PR_TRUE==atLeastOneRowSpanningCell) ... otherwise skip this step entirely
* 2. find cases where spanning cells effect other spanning cells that began in rows above themselves.
* I think in this case, we have to make another pass through step 2.
* There should be a "rational" check to terminate that kind of loop after n passes, probably 3 or 4.
*/
if (gsDebug==PR_TRUE) printf("Height Step 2...\n");
rowGroupHeight=0;
nsTableRowFrame *rowFrame=nsnull;
1998-04-14 00:24:54 +04:00
for (rowIndex = 0; rowIndex < numRows; rowIndex++)
{
// get the next row
if (0==rowIndex)
rowGroupFrame->ChildAt(rowIndex, (nsIFrame*&)rowFrame);
else
rowFrame->GetNextSibling((nsIFrame*&)rowFrame);
PRInt32 numCells;
rowFrame->ChildCount(numCells);
// check this row for a cell with rowspans
1998-04-14 00:24:54 +04:00
for (PRInt32 cellIndex = 0; cellIndex < numCells; cellIndex++)
{
// get the next cell
nsTableCellFrame *cellFrame;
rowFrame->ChildAt(cellIndex, (nsIFrame*&)cellFrame);
1998-04-14 00:24:54 +04:00
PRInt32 rowSpan = cellFrame->GetRowSpan();
if (1<rowSpan)
{ // found a cell with rowspan > 1, determine it's height
1998-04-14 00:24:54 +04:00
if (gsDebug==PR_TRUE) printf(" cell[%d,%d] has a rowspan = %d\n", rowIndex, cellIndex, rowSpan);
nscoord heightOfRowsSpanned = 0;
1998-04-14 00:24:54 +04:00
for (PRInt32 i=0; i<rowSpan; i++)
heightOfRowsSpanned += rowHeights[i+rowIndex];
heightOfRowsSpanned -= topInnerMargin + bottomInnerMargin;
/* if the cell height fits in the rows, expand the spanning cell's height and slap it in */
nsSize cellFrameSize(0,0);
cellFrame->GetSize(cellFrameSize);
if (heightOfRowsSpanned>cellFrameSize.height)
1998-04-14 00:24:54 +04:00
{
if (gsDebug==PR_TRUE) printf(" cell[%d,%d] fits, setting height to %d\n", rowIndex, cellIndex, heightOfRowsSpanned);
cellFrame->SizeTo(cellFrameSize.width, heightOfRowsSpanned);
1998-04-14 00:24:54 +04:00
// Realign cell content based on new height
cellFrame->VerticallyAlignChild(aPresContext);
}
/* otherwise, distribute the excess height to the rows effected, and to the cells in those rows
* push all subsequent rows down by the total change in height of all the rows above it
1998-04-14 00:24:54 +04:00
*/
else
{
PRInt32 excessHeight = cellFrameSize.height - heightOfRowsSpanned;
1998-04-14 00:24:54 +04:00
PRInt32 excessHeightPerRow = excessHeight/rowSpan;
if (gsDebug==PR_TRUE) printf(" cell[%d,%d] does not fit, excessHeight = %d, excessHeightPerRow = %d\n",
rowIndex, cellIndex, excessHeight, excessHeightPerRow);
// for every row starting at the row with the spanning cell...
1998-04-14 00:24:54 +04:00
for (i=rowIndex; i<numRows; i++)
{
nsTableRowFrame *rowFrameToBeResized;
rowGroupFrame->ChildAt(i, (nsIFrame*&)rowFrameToBeResized);
// if the row is within the spanned range, resize the row and it's cells
1998-04-14 00:24:54 +04:00
if (i<rowIndex+rowSpan)
{
rowHeights[i] += excessHeightPerRow;
if (gsDebug==PR_TRUE) printf(" rowHeight[%d] set to %d\n", i, rowHeights[i]);
nsSize rowFrameSize(0,0);
rowFrameToBeResized->GetSize(rowFrameSize);
rowFrameToBeResized->SizeTo(rowFrameSize.width, rowHeights[i]);
PRInt32 cellCount;
rowFrameToBeResized->ChildCount(cellCount);
1998-04-14 00:24:54 +04:00
for (PRInt32 j=0; j<cellCount; j++)
{
if (i==rowIndex && j==cellIndex)
{
if (gsDebug==PR_TRUE) printf(" cell[%d, %d] skipping self\n", i, j);
continue; // don't do math on myself, only the other cells I effect
}
nsTableCellFrame *frame;
rowFrameToBeResized->ChildAt(j, (nsIFrame*&)frame);
1998-04-14 00:24:54 +04:00
if (frame->GetRowSpan()==1)
{
nsSize frameSize(0,0);
frame->GetSize(frameSize);
if (gsDebug==PR_TRUE) printf(" cell[%d, %d] set height to %d\n", i, j, frameSize.height+excessHeightPerRow);
frame->SizeTo(frameSize.width, frameSize.height+excessHeightPerRow);
1998-04-14 00:24:54 +04:00
// Realign cell content based on new height
frame->VerticallyAlignChild(aPresContext);
}
}
}
// if we're dealing with a row below the row containing the spanning cell,
// push that row down by the amount we've expanded the cell heights by
if (i>=rowIndex && i!=0)
1998-04-14 00:24:54 +04:00
{
nsRect rowRect;
rowFrameToBeResized->GetRect(rowRect);
nscoord delta = excessHeightPerRow*(i-rowIndex);
if (delta > excessHeight)
delta = excessHeight;
rowFrameToBeResized->MoveTo(rowRect.x, rowRect.y + delta);
if (gsDebug==PR_TRUE) printf(" row %d (%p) moved by %d to y-offset %d\n",
i, rowFrameToBeResized, delta, rowRect.y + delta);
1998-04-14 00:24:54 +04:00
}
}
}
}
}
rowGroupHeight += rowHeights[rowIndex];
}
if (gsDebug==PR_TRUE) printf("row group height set to %d\n", rowGroupHeight);
nsSize rowGroupFrameSize(0,0);
rowGroupFrame->GetSize(rowGroupFrameSize);
rowGroupFrame->SizeTo(rowGroupFrameSize.width, rowGroupHeight);
1998-04-14 00:24:54 +04:00
tableHeight += rowGroupHeight;
if (nsnull!=rowHeights)
delete [] rowHeights;
1998-04-14 00:24:54 +04:00
}
}
if (0!=tableHeight)
{
if (gsDebug==PR_TRUE) printf("table desired height set to %d\n", tableHeight);
aDesiredSize.height = tableHeight;
}
#ifdef NS_DEBUG
gsDebug = gsDebugWas;
#endif
}
void nsTableFrame::VerticallyAlignChildren(nsIPresContext* aPresContext,
nscoord* aAscents,
nscoord aMaxAscent,
nscoord aMaxHeight)
1998-04-14 00:24:54 +04:00
{
}
/* there's an easy way and a hard way. The easy way is to look in our
* cache and pull the frame from there.
* If the cache isn't built yet, then we have to go hunting.
*/
NS_METHOD nsTableFrame::GetColumnFrame(PRInt32 aColIndex, nsTableColFrame *&aColFrame)
{
aColFrame = nsnull; // initialize out parameter
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
if (nsnull!=firstInFlow->mColumnLayoutData)
{ // hooray, we get to do this the easy way because the info is cached
nsColLayoutData * colData = (nsColLayoutData *)
(firstInFlow->mColumnLayoutData->ElementAt(aColIndex));
NS_ASSERTION(nsnull != colData, "bad column data");
aColFrame = colData->GetColFrame();
NS_ASSERTION(nsnull!=aColFrame, "bad col frame");
}
else
{ // ah shucks, we have to go hunt for the column frame brute-force style
nsIFrame *childFrame;
FirstChild(childFrame);
for (;;)
{
if (nsnull==childFrame)
{
NS_ASSERTION (PR_FALSE, "scanned the frame hierarchy and no column frame could be found.");
break;
}
nsIContentPtr kid;
childFrame->GetContent(kid.AssignRef());
const PRInt32 contentType = ((nsTableContent *)(nsIContent*)kid)->GetType();
if (contentType==nsITableContent::kTableColGroupType)
{
nsTableColGroup *colGroup = (nsTableColGroup *)((nsIContent*)kid);
PRInt32 colGroupStartingIndex = colGroup->GetStartColumnIndex();
if (aColIndex >= colGroupStartingIndex)
{ // the cell's col might be in this col group
if (aColIndex < colGroupStartingIndex + colGroup->ChildCount())
{ // yep, we've found it
childFrame->ChildAt(aColIndex-colGroupStartingIndex, (nsIFrame *&)aColFrame);
break;
}
}
}
childFrame->GetNextSibling(childFrame);
}
}
return NS_OK;
}
1998-04-14 00:24:54 +04:00
nsVoidArray * nsTableFrame::GetColumnLayoutData()
{
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow");
NS_ASSERTION(nsnull!=firstInFlow->mColumnLayoutData, "illegal state -- no column layout data");
1998-04-14 00:24:54 +04:00
return firstInFlow->mColumnLayoutData;
}
/** Associate aData with the cell at (aRow,aCol)
* @return PR_TRUE if the data was successfully associated with a Cell
* PR_FALSE if there was an error, such as aRow or aCol being invalid
*/
PRBool nsTableFrame::SetCellLayoutData(nsIPresContext* aPresContext,
nsCellLayoutData * aData, nsTableCell *aCell)
1998-04-14 00:24:54 +04:00
{
NS_ASSERTION(nsnull != aData, "bad arg");
NS_ASSERTION(nsnull != aCell, "bad arg");
PRBool result = PR_TRUE;
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow");
if (this!=firstInFlow)
result = firstInFlow->SetCellLayoutData(aPresContext, aData, aCell);
1998-04-14 00:24:54 +04:00
else
{
if (kPASS_FIRST==GetReflowPass())
{
if (nsnull==mColumnLayoutData)
{
mColumnLayoutData = new nsVoidArray();
NS_ASSERTION(nsnull != mColumnLayoutData, "bad alloc");
nsTablePart * tablePart = (nsTablePart *)mContent;
PRInt32 cols = tablePart->GetMaxColumns();
PRInt32 rows = tablePart->GetRowCount();
mColCache = new ColumnInfoCache(cols);
1998-04-14 00:24:54 +04:00
PRInt32 tableKidCount = tablePart->ChildCount();
nsIFrame * colGroupFrame = mFirstChild;
1998-04-14 00:24:54 +04:00
for (PRInt32 i=0; i<tableKidCount; i++)
{
nsTableContentPtr tableKid = (nsTableContent *)tablePart->ChildAt(i);
NS_ASSERTION(tableKid.IsNotNull(), "bad kid");
1998-04-14 00:24:54 +04:00
const int contentType = tableKid->GetType();
if (contentType == nsITableContent::kTableColGroupType)
{
PRInt32 colsInGroup = tableKid->ChildCount();
for (PRInt32 j=0; j<colsInGroup; j++)
{
// TODO: unify these 2 kinds of column data
// TODO: cache more column data, like the mWidth.GetUnit and what its value
nsTableColFrame *colFrame=nsnull;
1998-06-11 08:57:56 +04:00
// XXX Why is this using ChildAt() instead of just walking the sibling list?
colGroupFrame->ChildAt(j, (nsIFrame *&)colFrame);
nsColLayoutData *colData = new nsColLayoutData(colFrame, rows);
1998-04-14 00:24:54 +04:00
mColumnLayoutData->AppendElement((void *)colData);
// also add the column to the column cache
// assumes that the col style has been twiddled to account for first cell width attribute
nsIStyleContextPtr colSC;
colFrame->GetStyleContext(aPresContext, colSC.AssignRef());
1998-06-05 10:09:09 +04:00
const nsStylePosition* colPosition = (const nsStylePosition*)colSC->GetStyleData(eStyleStruct_Position);
mColCache->AddColumnInfo(colPosition->mWidth.GetUnit(), colFrame->GetColumnIndex());
1998-04-14 00:24:54 +04:00
}
}
// can't have col groups after row groups, so stop if you find a row group
else if (contentType == nsITableContent::kTableRowGroupType)
{
break;
}
ChildAt(i, colGroupFrame); // can't use colGroupFrame->GetNextSibling because it hasn't been set yet
1998-04-14 00:24:54 +04:00
}
}
// create cell layout data objects for the passed in data, one per column spanned.
// For now, divide width equally between spanned columns
1998-06-11 08:57:56 +04:00
// XXX Add a GetRowIndex() member function to nsTableCell and we can save the
// reference counting overhead...
1998-04-14 00:24:54 +04:00
nsTableRow *row = aCell->GetRow(); // row: ADDREF++
PRInt32 rowIndex = row->GetRowIndex();
NS_RELEASE(row); // row: ADDREF--
PRInt32 colSpan = aCell->GetColSpan();
PRInt32 firstColIndex = aCell->GetColIndex();
for (PRInt32 i = 0; i < colSpan; i++) {
if (i > 0) {
int j = 5;
}
nsColLayoutData * colData = (nsColLayoutData *)(mColumnLayoutData->ElementAt(firstColIndex + i));
nsVoidArray *col = colData->GetCells();
if (gsDebugCLD) printf (" ~ SetCellLayoutData with row = %d, firstCol = %d, colSpan = %d, colData = %ld, col=%ld\n",
rowIndex, firstColIndex, colSpan, colData, col);
1998-04-14 00:24:54 +04:00
nsSize * cellSize = aData->GetMaxElementSize();
nsSize partialCellSize(*cellSize);
partialCellSize.width = (cellSize->width)/colSpan;
// This method will copy the nsReflowMetrics pointed at by aData->GetDesiredSize()
nsCellLayoutData * kidLayoutData = new nsCellLayoutData(aData->GetCellFrame(),
aData->GetDesiredSize(),
&partialCellSize);
NS_ASSERTION(col->Count() > rowIndex, "unexpected count");
if (gsDebugCLD) printf (" ~ replacing rowIndex = %d\n", rowIndex);
nsCellLayoutData* data = (nsCellLayoutData*)col->ElementAt(rowIndex);
col->ReplaceElementAt((void *)kidLayoutData, rowIndex);
if (data != nsnull) {
delete data;
1998-04-14 00:24:54 +04:00
}
}
}
else
result = PR_FALSE;
}
return result;
}
/** Get the layout data associated with the cell at (aRow,aCol)
* @return nsnull if there was an error, such as aRow or aCol being invalid
* otherwise, the data is returned.
*/
nsCellLayoutData * nsTableFrame::GetCellLayoutData(nsTableCell *aCell)
{
NS_ASSERTION(nsnull != aCell, "bad arg");
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow");
nsCellLayoutData *result = nsnull;
if (this!=firstInFlow)
result = firstInFlow->GetCellLayoutData(aCell);
else
{
if (nsnull!=mColumnLayoutData)
{
PRInt32 firstColIndex = aCell->GetColIndex();
nsColLayoutData * colData = (nsColLayoutData *)(mColumnLayoutData->ElementAt(firstColIndex));
nsTableRow *row = aCell->GetRow(); // row: ADDREF++
PRInt32 rowIndex = row->GetRowIndex();
NS_RELEASE(row); // row: ADDREF--
result = colData->ElementAt(rowIndex);
#ifdef NS_DEBUG
// Do some sanity checking
if (nsnull != result) {
nsIContent* content;
result->GetCellFrame()->GetContent(content);
NS_ASSERTION(content == aCell, "unexpected cell");
NS_IF_RELEASE(content);
1998-04-14 00:24:54 +04:00
}
#endif
1998-04-14 00:24:54 +04:00
}
}
return result;
}
PRInt32 nsTableFrame::GetReflowPass() const
{
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow");
return firstInFlow->mPass;
}
void nsTableFrame::SetReflowPass(PRInt32 aReflowPass)
{
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow");
firstInFlow->mPass = aReflowPass;
}
PRBool nsTableFrame::IsFirstPassValid() const
{
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow");
return firstInFlow->mFirstPassValid;
}
NS_METHOD
nsTableFrame::CreateContinuingFrame(nsIPresContext* aPresContext,
nsIFrame* aParent,
nsIStyleContext* aStyleContext,
nsIFrame*& aContinuingFrame)
1998-04-14 00:24:54 +04:00
{
nsTableFrame* cf = new nsTableFrame(mContent, aParent);
if (nsnull == cf) {
return NS_ERROR_OUT_OF_MEMORY;
}
PrepareContinuingFrame(aPresContext, aParent, aStyleContext, cf);
1998-04-14 00:24:54 +04:00
if (PR_TRUE==gsDebug) printf("nsTableFrame::CCF parent = %p, this=%p, cf=%p\n", aParent, this, cf);
// set my width, because all frames in a table flow are the same width
// code in nsTableOuterFrame depends on this being set
cf->SetRect(nsRect(0, 0, mRect.width, 0));
1998-04-14 00:24:54 +04:00
// add headers and footers to cf
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
nsIFrame * rg = nsnull;
firstInFlow->ChildAt(0, rg);
NS_ASSERTION (nsnull!=rg, "previous frame has no children");
nsIAtom * tHeadTag = NS_NewAtom(nsTablePart::kRowGroupHeadTagString); // tHeadTag: REFCNT++
nsIAtom * tFootTag = NS_NewAtom(nsTablePart::kRowGroupFootTagString); // tFootTag: REFCNT++
PRInt32 index = 0;
nsIFrame * bodyRowGroupFromOverflow = mOverflowList;
nsIFrame * lastSib = nsnull;
for ( ; nsnull!=rg; index++)
1998-04-14 00:24:54 +04:00
{
nsIContent *content = nsnull;
rg->GetContent(content); // content: REFCNT++
NS_ASSERTION(nsnull!=content, "bad frame, returned null content.");
nsIAtom * rgTag = content->GetTag();
// if we've found a header or a footer, replicate it
if (tHeadTag==rgTag || tFootTag==rgTag)
{
printf("found a head or foot in continuing frame\n");
// Resolve style for the child
nsIStyleContext* kidStyleContext =
aPresContext->ResolveStyleContextFor(content, cf); // kidStyleContext: REFCNT++
nsIContentDelegate* kidDel = nsnull;
kidDel = content->GetDelegate(aPresContext); // kidDel: REFCNT++
nsIFrame* duplicateFrame;
nsresult rv = kidDel->CreateFrame(aPresContext, content, cf,
kidStyleContext, duplicateFrame);
NS_RELEASE(kidDel); // kidDel: REFCNT--
NS_RELEASE(kidStyleContext); // kidStyleContenxt: REFCNT--
if (nsnull==lastSib)
{
mOverflowList = duplicateFrame;
}
else
{
lastSib->SetNextSibling(duplicateFrame);
}
duplicateFrame->SetNextSibling(bodyRowGroupFromOverflow);
lastSib = duplicateFrame;
}
NS_RELEASE(content); // content: REFCNT--
// get the next row group
rg->GetNextSibling(rg);
1998-04-14 00:24:54 +04:00
}
NS_RELEASE(tFootTag); // tHeadTag: REFCNT --
NS_RELEASE(tHeadTag); // tFootTag: REFCNT --
aContinuingFrame = cf;
return NS_OK;
1998-04-14 00:24:54 +04:00
}
PRInt32 nsTableFrame::GetColumnWidth(PRInt32 aColIndex)
{
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow");
PRInt32 result = 0;
if (this!=firstInFlow)
result = firstInFlow->GetColumnWidth(aColIndex);
else
{
NS_ASSERTION(nsnull!=mColumnWidths, "illegal state");
#ifdef DEBUG
nsVoidArray *cld = GetColumnLayoutData();
NS_ASSERTION(nsnull!=cld, "no column layout data");
PRInt32 numCols = cld->Count();
NS_ASSERTION (numCols > aColIndex, "bad arg, col index out of bounds");
#endif
1998-04-14 00:24:54 +04:00
if (nsnull!=mColumnWidths)
result = mColumnWidths[aColIndex];
}
//printf("GET_COL_WIDTH: %p, FIF=%p getting col %d and returning %d\n", this, firstInFlow, aColIndex, result);
1998-04-25 22:48:48 +04:00
// XXX hack
#if 0
if (result <= 0) {
result = 100;
}
#endif
1998-04-14 00:24:54 +04:00
return result;
}
void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth)
1998-04-14 00:24:54 +04:00
{
nsTableFrame * firstInFlow = (nsTableFrame *)GetFirstInFlow();
NS_ASSERTION(nsnull!=firstInFlow, "illegal state -- no first in flow");
//printf("SET_COL_WIDTH: %p, FIF=%p setting col %d to %d\n", this, firstInFlow, aColIndex, aWidth);
1998-04-14 00:24:54 +04:00
if (this!=firstInFlow)
firstInFlow->SetColumnWidth(aColIndex, aWidth);
else
{
NS_ASSERTION(nsnull!=mColumnWidths, "illegal state");
if (nsnull!=mColumnWidths)
mColumnWidths[aColIndex] = aWidth;
}
}
/**
*
* Update the border style to map to the HTML border style
*
*/
void nsTableFrame::MapHTMLBorderStyle(nsStyleSpacing& aSpacingStyle, nscoord aBorderWidth)
{
nsStyleCoord width;
width.SetCoordValue(aBorderWidth);
aSpacingStyle.mBorder.SetTop(width);
aSpacingStyle.mBorder.SetLeft(width);
aSpacingStyle.mBorder.SetBottom(width);
aSpacingStyle.mBorder.SetRight(width);
aSpacingStyle.mBorderStyle[NS_SIDE_TOP] = NS_STYLE_BORDER_STYLE_OUTSET;
aSpacingStyle.mBorderStyle[NS_SIDE_LEFT] = NS_STYLE_BORDER_STYLE_OUTSET;
aSpacingStyle.mBorderStyle[NS_SIDE_BOTTOM] = NS_STYLE_BORDER_STYLE_OUTSET;
aSpacingStyle.mBorderStyle[NS_SIDE_RIGHT] = NS_STYLE_BORDER_STYLE_OUTSET;
nsIStyleContext* styleContext = mStyleContext;
1998-06-05 10:09:09 +04:00
const nsStyleColor* colorData = (const nsStyleColor*)styleContext->GetStyleData(eStyleStruct_Color);
// Look until we find a style context with a NON-transparent background color
while (styleContext)
{
if ((colorData->mBackgroundFlags & NS_STYLE_BG_COLOR_TRANSPARENT)!=0)
{
nsIStyleContext* temp = styleContext;
styleContext = styleContext->GetParent();
if (temp != mStyleContext)
NS_RELEASE(temp);
1998-06-05 10:09:09 +04:00
colorData = (const nsStyleColor*)styleContext->GetStyleData(eStyleStruct_Color);
}
else
{
break;
}
}
// Yaahoo, we found a style context which has a background color
nscolor borderColor = 0xFFC0C0C0;
if (styleContext != nsnull)
{
borderColor = colorData->mBackgroundColor;
if (styleContext != mStyleContext)
NS_RELEASE(styleContext);
}
// if the border color is white, then shift to grey
if (borderColor == 0xFFFFFFFF)
borderColor = 0xFFC0C0C0;
aSpacingStyle.mBorderColor[NS_SIDE_TOP] = borderColor;
aSpacingStyle.mBorderColor[NS_SIDE_LEFT] = borderColor;
aSpacingStyle.mBorderColor[NS_SIDE_BOTTOM] = borderColor;
aSpacingStyle.mBorderColor[NS_SIDE_RIGHT] = borderColor;
}
PRBool nsTableFrame::ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult)
{
PRInt32 result = 0;
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 nsTableFrame::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 border_result;
nscoord padding = 0;
nscoord spacing = 0;
nscoord border = 1;
float p2t = aPresContext->GetPixelsToTwips();
nsTablePart* table = (nsTablePart*)mContent;
NS_ASSERTION(table,"Table Must not be null");
if (!table)
return;
1998-06-05 10:09:09 +04:00
nsStyleSpacing* spacingData = (nsStyleSpacing*)mStyleContext->GetMutableStyleData(eStyleStruct_Spacing);
border_result = table->GetAttribute(nsHTMLAtoms::border,border_value);
if (border_result == eContentAttr_HasValue)
{
PRInt32 intValue = 0;
if (ConvertToPixelValue(border_value,1,intValue))
border = nscoord(p2t*(float)intValue);
}
MapHTMLBorderStyle(*spacingData,border);
}
// Subclass hook for style post processing
NS_METHOD nsTableFrame::DidSetStyleContext(nsIPresContext* aPresContext)
{
#ifdef NOISY_STYLE
printf("nsTableFrame::DidSetStyleContext \n");
#endif
MapBorderMarginPadding(aPresContext);
return NS_OK;
}
1998-04-14 00:24:54 +04:00
NS_METHOD nsTableFrame::GetCellMarginData(nsIFrame* aKidFrame, nsMargin& aMargin)
{
nsresult result = NS_ERROR_NOT_INITIALIZED;
nsIContent* content = nsnull;
aKidFrame->GetContent(content);
if (nsnull != content)
{
nsTableCell* cell = (nsTableCell*)content;
nsCellLayoutData* layoutData = GetCellLayoutData(cell);
if (layoutData)
result = layoutData->GetMargin(aMargin);
NS_RELEASE(content);
}
return result;
}
void nsTableFrame::GetColumnsByType(const nsStyleUnit aType,
PRInt32& aOutNumColumns,
PRInt32 *& aOutColumnIndexes)
{
mColCache->GetColumnsByType(aType, aOutNumColumns, aOutColumnIndexes);
}
1998-04-14 00:24:54 +04:00
/* ---------- static methods ---------- */
nsresult nsTableFrame::NewFrame(nsIFrame** aInstancePtrResult,
nsIContent* aContent,
nsIFrame* aParent)
{
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
if (nsnull == aInstancePtrResult) {
return NS_ERROR_NULL_POINTER;
}
nsIFrame* it = new nsTableFrame(aContent, aParent);
1998-04-14 00:24:54 +04:00
if (nsnull == it) {
return NS_ERROR_OUT_OF_MEMORY;
}
*aInstancePtrResult = it;
return NS_OK;
}
/* helper method for getting the width of the table's containing block */
nscoord nsTableFrame::GetTableContainerWidth(const nsReflowState& aReflowState)
{
#ifdef STEVES_WAY // from BasicTableLayoutStrategy::TableIsAutoWidth()
// get the parent's width (available only from parent frames that claim they can provide it)
// note that we start with our parent's parent (the outer table frame's parent)
nscoord parentWidth = 0;
NS_ASSERTION(nsnull!=aReflowState.parentReflowState, "bad outer table reflow state.");
NS_ASSERTION(nsnull!=aReflowState.parentReflowState->parentReflowState, "bad table parent reflow state.");
if ((nsnull!=aReflowState.parentReflowState) &&
(nsnull!=aReflowState.parentReflowState->parentReflowState))
{
const nsReflowState *parentReflowState = aReflowState.parentReflowState->parentReflowState;
nsIFrame *parentFrame=parentReflowState->frame;
NS_ASSERTION(nsnull!=parentFrame, "bad parent frame in reflow state struct.");
while(nsnull!=parentFrame)
{
PRBool isPercentageBase=PR_FALSE;
parentFrame->IsPercentageBase(isPercentageBase);
if (PR_TRUE==isPercentageBase)
{ // found the ancestor who claims to be the container to base my percentage width on
parentWidth = parentReflowState->maxSize.width;
if (PR_TRUE==gsDebug) printf(" ** width for parent frame %p = %d\n", parentFrame, parentWidth);
break;
}
parentReflowState = parentReflowState->parentReflowState; // get next ancestor
if (nsnull!=parentReflowState)
parentFrame = parentReflowState->frame;
else
parentFrame = nsnull; // terminates loop.
// TODO: do we need a backstop in case there are no IsPercentageBase==true frames?
}
}
#else
nscoord parentWidth = aReflowState.maxSize.width;
// Walk up the reflow state chain until we find a block
// frame. Our width is computed relative to there.
const nsReflowState* rs = &aReflowState;
while (nsnull != rs) {
nsIFrame* block = nsnull;
rs->frame->QueryInterface(kBlockFrameCID, (void**) &block);
if (nsnull != block) {
// We found the nearest containing block which defines what
// a percentage size is relative to. Use the width that it
// will reflow to as the basis for computing our width.
parentWidth = rs->maxSize.width;
break;
}
rs = rs->parentReflowState;
}
#endif
return parentWidth;
}
// aSpecifiedTableWidth is filled if the table witdth is not auto
PRBool nsTableFrame::TableIsAutoWidth(nsTableFrame *aTableFrame,
nsIStyleContext *aTableStyle,
const nsReflowState& aReflowState,
nscoord& aSpecifiedTableWidth)
{
NS_ASSERTION(nsnull!=aTableStyle, "bad arg - aTableStyle");
PRBool result = PR_TRUE; // the default
if (nsnull!=aTableStyle)
{
//nsStylePosition* tablePosition = (nsStylePosition*)aTableStyle->GetData(eStyleStruct_Position);
/* this is sick and wrong, but what the hell
we grab the style of our parent (nsTableOuterFrame) and ask it for width info,
until the style resolution stuff does the cool stuff about splitting style between outer and inner
*/
// begin REMOVE_ME_WHEN_TABLE_STYLE_IS_RESOLVED!
nsIStyleContext* parentStyle = nsnull;
nsIFrame * parent = nsnull;
aTableFrame->GetGeometricParent(parent);
parent->GetStyleContext(nsnull, parentStyle);
1998-06-05 10:09:09 +04:00
const nsStylePosition* tablePosition = (const nsStylePosition*)parentStyle->GetStyleData(eStyleStruct_Position);
// end REMOVE_ME_WHEN_TABLE_STYLE_IS_RESOLVED!
switch (tablePosition->mWidth.GetUnit()) {
case eStyleUnit_Auto: // specified auto width
case eStyleUnit_Proportional: // illegal for table, so ignored
break;
case eStyleUnit_Inherit:
// get width of parent and see if it is a specified value or not
// XXX for now, just return true
break;
case eStyleUnit_Coord:
aSpecifiedTableWidth = tablePosition->mWidth.GetCoordValue();
result = PR_FALSE;
break;
case eStyleUnit_Percent:
// set aSpecifiedTableWidth to be the given percent of the parent.
nscoord parentWidth = nsTableFrame::GetTableContainerWidth(aReflowState);
float percent = tablePosition->mWidth.GetPercentValue();
aSpecifiedTableWidth = (PRInt32)(parentWidth*percent);
if (PR_TRUE==gsDebug) printf(" ** aSpecifiedTableWidth = %d\n", aSpecifiedTableWidth);
result = PR_FALSE;
break;
}
}
return result;
}
1998-04-14 00:24:54 +04:00
/* valuable code not yet used anywhere
// since the table is a specified width, we need to expand the columns to fill the table
nsVoidArray *columnLayoutData = GetColumnLayoutData();
PRInt32 numCols = columnLayoutData->Count();
PRInt32 spaceUsed = 0;
for (PRInt32 colIndex = 0; colIndex<numCols; colIndex++)
spaceUsed += mColumnWidths[colIndex];
PRInt32 spaceRemaining = spaceUsed - aMaxWidth;
PRInt32 additionalSpaceAdded = 0;
if (0<spaceRemaining)
{
for (colIndex = 0; colIndex<numCols; colIndex++)
{
nsColLayoutData * colData = (nsColLayoutData *)(columnLayoutData->ElementAt(colIndex));
nsTableColPtr col = colData->GetCol(); // col: ADDREF++
1998-04-14 00:24:54 +04:00
nsStyleMolecule* colStyle =
(nsStyleMolecule*)mStyleContext->GetData(eStyleStruct_Molecule);
1998-04-14 00:24:54 +04:00
if (PR_TRUE==IsProportionalWidth(colStyle))
{
PRInt32 percentage = (100*mColumnWidths[colIndex]) / aMaxWidth;
PRInt32 additionalSpace = (spaceRemaining*percentage)/100;
mColumnWidths[colIndex] += additionalSpace;
additionalSpaceAdded += additionalSpace;
}
}
if (spaceUsed+additionalSpaceAdded < aMaxTableWidth)
mColumnWidths[numCols-1] += (aMaxTableWidth - (spaceUsed+additionalSpaceAdded));
}
*/
// For Debugging ONLY
NS_METHOD nsTableFrame::MoveTo(nscoord aX, nscoord aY)
{
if ((aX != mRect.x) || (aY != mRect.y)) {
mRect.x = aX;
mRect.y = aY;
nsIView* view;
GetView(view);
// Let the view know
if (nsnull != view) {
// Position view relative to it's parent, not relative to our
// parent frame (our parent frame may not have a view).
nsIView* parentWithView;
nsPoint origin;
GetOffsetFromView(origin, parentWithView);
view->SetPosition(origin.x, origin.y);
NS_IF_RELEASE(parentWithView);
}
}
1998-04-14 00:24:54 +04:00
return NS_OK;
}
NS_METHOD nsTableFrame::SizeTo(nscoord aWidth, nscoord aHeight)
{
mRect.width = aWidth;
mRect.height = aHeight;
1998-04-14 00:24:54 +04:00
nsIView* view;
GetView(view);
// Let the view know
if (nsnull != view) {
view->SetDimensions(aWidth, aHeight);
}
return NS_OK;
}
1998-04-14 00:24:54 +04:00