reimplemented cell map in terms of nsVoidArrays for incremental building.

the cellmap is now built incrementally, driven by nsTableRowFrame::InitialReflow


git-svn-id: svn://10.0.0.236/trunk@8888 18797224-902f-48f8-a5cc-f745e15eee43
This commit is contained in:
buster%netscape.com
1998-08-31 21:23:28 +00:00
parent afe1ba95d3
commit cb664f26da
20 changed files with 880 additions and 528 deletions

View File

@@ -321,7 +321,7 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
maxColSpan = colSpan;
if (colIndex!=cellFrame->GetColIndex()) {
// For cells that span cols, we figure in the row using previously-built SpanInfo
NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match row span"); // sanity check
NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match col span"); // sanity check
continue;
}
@@ -1251,6 +1251,8 @@ PRBool BasicTableLayoutStrategy::BalanceColumnsTableFits(const nsReflowState& aR
}
tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset;
}
if (1==mNumCols)
tableWidth += colInset;
/* --- post-process if necessary --- */
@@ -1966,6 +1968,8 @@ PRBool BasicTableLayoutStrategy::BalanceColumnsConstrained( const nsReflowState&
}
tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset;
}
if (1==mNumCols)
tableWidth += colInset;
/* --- post-process if necessary --- */
// first, assign autoWidth columns a width
if (PR_TRUE==atLeastOneAutoWidthColumn)

View File

@@ -29,6 +29,9 @@ struct nsStylePosition;
/* ----------- SpanInfo ---------- */
/** SpanInfo is a transient data structure that holds info about
* cells that have col spans. Used during column balancing.
*/
struct SpanInfo
{
PRInt32 span;
@@ -59,6 +62,11 @@ inline SpanInfo::SpanInfo(PRInt32 aColIndex, PRInt32 aSpan,
/* ---------- BasicTableLayoutStrategy ---------- */
/** Implementation of Nav4 compatible HTML browser table layout.
* The input to this class is the results from pass1 table layout.
* The output from this class is to set the column widths in
* mTableFrame.
*/
class BasicTableLayoutStrategy : public nsITableLayoutStrategy
{
public:
@@ -69,15 +77,25 @@ public:
*/
BasicTableLayoutStrategy(nsTableFrame *aFrame, PRInt32 aNumCols);
~BasicTableLayoutStrategy();
/** destructor */
virtual ~BasicTableLayoutStrategy();
/** call once every time any table thing changes (content, structure, or style) */
virtual PRBool Initialize(nsSize* aMaxElementSize);
/** Called during resize reflow to determine the new column widths
* @param aTableStyle - the resolved style for mTableFrame
* @param aReflowState - the reflow state for mTableFrame
* @param aMaxWidth - the computed max width for columns to fit into
*/
virtual PRBool BalanceColumnWidths(nsIStyleContext * aTableStyle,
const nsReflowState& aReflowState,
nscoord aMaxWidth);
nscoord GetTableMaxWidth() const;
protected:
/** assign widths for each column.
* if the column has a fixed coord width, use it.
* if the column includes col spanning cells,
@@ -235,5 +253,8 @@ protected:
};
inline nscoord BasicTableLayoutStrategy::GetTableMaxWidth() const
{ return mMaxTableWidth; };
#endif

View File

@@ -33,6 +33,10 @@ class nsTableCellFrame;
class CellData
{
public:
/** if not null, the cell that this CellData maps.
* if null, mRealCell points to the CellData that holds the
* mapped cell frame.
*/
nsTableCellFrame *mCell;
CellData *mRealCell;
CellData *mOverlap;

View File

@@ -16,121 +16,155 @@
* Reserved.
*/
#include "nsCRT.h"
#include "nsVoidArray.h"
#include "nsCellMap.h"
#include "nsTableFrame.h"
#ifdef NS_DEBUG
static PRBool gsDebug1 = PR_FALSE;
static PRBool gsDebug = PR_FALSE;
#else
static const PRBool gsDebug1 = PR_FALSE;
static const PRBool gsDebug = PR_FALSE;
#endif
static const PRInt32 gBytesPerPointer = sizeof(PRInt32);
nsCellMap::nsCellMap(int aRows, int aColumns)
: mRowCount(aRows),
mColCount(aColumns)
nsCellMap::nsCellMap(int aRowCount, int aColCount)
: mRowCount(0),
mColCount(0),
mTotalRowCount(0)
{
mCells = nsnull;
mRows = nsnull;
mColFrames = nsnull;
mMinColSpans = nsnull;
Reset(aRows, aColumns);
Reset(aRowCount, aColCount);
}
nsCellMap::~nsCellMap()
{
if (nsnull!=mCells)
if (nsnull!=mRows)
{
for (int i=0;i<mRowCount;i++)
for (int i=mTotalRowCount-1; 0<i; i--)
{
for (int j=0;j<mColCount;j++)
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(i));
for (int j=mColCount; 0<j ;j--)
{
int index = (i*mColCount)+j;
CellData* data = (CellData*)mCells[index];
CellData* data = (CellData *)(row->ElementAt(j));
if (data != nsnull)
{
delete data;
mCells[index] = 0;
}
}
delete row;
}
delete [] mCells;
delete mRows;
}
if (nsnull != mColFrames)
delete mColFrames;
if (nsnull != mMinColSpans)
delete [] mMinColSpans;
mCells = nsnull;
mRows = nsnull;
mColFrames = nsnull;
mMinColSpans = nsnull;
};
void nsCellMap::Reset(int aRows, int aColumns)
void nsCellMap::Reset(int aRowCount, int aColCount)
{
if (gsDebug) printf("calling Reset(%d,%d) with mRC=%d, mCC=%d, mTRC=%d\n",
aRowCount, aColCount, mRowCount, mColCount, mTotalRowCount);
if (nsnull==mColFrames)
{
mColFrames = new nsVoidArray();
mColFrames = new nsVoidArray(); // don't give the array a count, because null col frames are illegal (unlike null cell entries in a row)
}
// needs to be more efficient, to reuse space if possible
if (nsnull!=mCells)
if (nsnull==mRowCount)
{
delete [] mCells;
mCells = nsnull;
mRows = new nsVoidArray(); // don't give the array a count, because null rows are illegal (unlike null cell entries in a row)
}
mRowCount = aRows;
mColCount = aColumns;
mCells = new PRInt32 [mRowCount*mColCount*gBytesPerPointer];
nsCRT::memset (mCells, 0, (mRowCount*mColCount)*gBytesPerPointer);
}
void nsCellMap::GrowTo(int aColCount)
{
if (aColCount <= mColCount)
return;
PRInt32 * newCells = new PRInt32 [mRowCount*aColCount*gBytesPerPointer];
for (int rowIndex = 0; rowIndex < mRowCount; rowIndex++)
// void arrays force the caller to handle null padding elements themselves
// so if the number of columns has increased, we need to add extra cols to each row
PRInt32 newCols = mColCount-aColCount;
for (PRInt32 rowIndex=0; rowIndex<mRowCount; rowIndex++)
{
PRInt32* rowMap = (PRInt32*)&(newCells[rowIndex*aColCount]);
nsCRT::memset (rowMap, 0, aColCount*gBytesPerPointer);
nsCRT::memcpy(rowMap, &(mCells[rowIndex*mColCount]), mColCount*gBytesPerPointer);
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(rowIndex));
const PRInt32 colsInRow = row->Count();
if (colsInRow == aColCount)
break; // we already have enough columns in each row
for (PRInt32 colIndex = colsInRow; colIndex<aColCount; colIndex++)
row->AppendElement(nsnull);
}
if (mCells != nsnull)
delete [] mCells;
mCells = newCells;
// if the number of rows has increased, add the extra rows
PRInt32 newRows = aRowCount-mTotalRowCount; // (new row count) - (total row allocation)
for ( ; newRows>0; newRows--)
{
nsVoidArray *row;
if (0!=aColCount)
row = new nsVoidArray(aColCount);
else
row = new nsVoidArray();
mRows->AppendElement(row);
}
mRowCount = aRowCount;
mTotalRowCount = PR_MAX(mTotalRowCount, mRowCount);
mColCount = aColCount;
if (gsDebug) printf("leaving Reset with mRC=%d, mCC=%d, mTRC=%d\n",
mRowCount, mColCount, mTotalRowCount);
}
void nsCellMap::DumpCellMap() const
{
if (gsDebug1==PR_TRUE)
if (gsDebug==PR_TRUE)
{
printf("Cell Map =\n");
for (int i=0;i<mRowCount;i++)
for (int j=0;j<mColCount;j++)
for (int rowIndex=0; rowIndex<mRowCount ; rowIndex++)
{
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(rowIndex));
for (int colIndex=0; colIndex<mColCount; colIndex++)
{
int index = (i*mColCount)+j;
printf("Cell [%d,%d] = %d for index = %d\n", i, j, mCells[index], index);
CellData* data = (CellData *)(row->ElementAt(colIndex));
printf("Cell [%d,%d] = %p for index = %d\n", rowIndex, colIndex, data);
}
}
}
}
void nsCellMap::SetCellAt(CellData *aCell, int aRow, int aColumn)
void nsCellMap::GrowToRow(PRInt32 aRowCount)
{
//Assert aRow, aColumn
int index = (aRow*mColCount)+aColumn;
CellData* cell = GetCellAt(aRow,aColumn);
if (cell != nsnull)
delete cell;
mCells[index] = (PRInt32)aCell;
Reset(aRowCount, mColCount);
}
nsTableColFrame* nsCellMap::GetColumnFrame(PRInt32 aColIndex)
void nsCellMap::GrowToCol(PRInt32 aColCount)
{
Reset(mRowCount, aColCount);
}
void nsCellMap::SetCellAt(CellData *aCell, int aRowIndex, int aColIndex)
{
NS_PRECONDITION(nsnull!=aCell, "bad aCell");
PRInt32 newRows = (aRowIndex+1)-mRowCount; // add 1 to the "index" to get a "count"
if (0<newRows)
{
for ( ; newRows>0; newRows--)
{
nsVoidArray *row = new nsVoidArray(mColCount);
mRows->AppendElement(row);
}
mTotalRowCount = aRowIndex+1; // remember to always add 1 to an index when you want a count
}
CellData* cell = GetCellAt(aRowIndex,aColIndex);
if (cell != nsnull)
delete cell;
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex));
row->ReplaceElementAt(aCell, aColIndex);
if (gsDebug) printf("leaving SetCellAt(%p,%d,%d) with mRC=%d, mCC=%d, mTRC=%d\n",
aCell, aRowIndex, aColIndex, mRowCount, mColCount, mTotalRowCount);
}
nsTableColFrame* nsCellMap::GetColumnFrame(PRInt32 aColIndex) const
{
NS_ASSERTION(nsnull!=mColFrames, "bad state");
return (nsTableColFrame *)(mColFrames->ElementAt(aColIndex));
}
@@ -151,7 +185,7 @@ void nsCellMap::SetMinColSpan(PRInt32 aColIndex, PRBool aColSpan)
mMinColSpans[aColIndex] = aColSpan;
}
PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex)
PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex) const
{
NS_ASSERTION(aColIndex<mColCount, "bad aColIndex");
@@ -161,6 +195,34 @@ PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex)
return result;
}
/** return the index of the next column in aRowIndex that does not have a cell assigned to it */
PRInt32 nsCellMap::GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const
{
PRInt32 result = 0;
if (aColIndex > mColCount)
{
result = aColIndex;
}
else
{
if (aRowIndex < mRowCount)
{
result = aColIndex;
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex));
PRInt32 count = row->Count();
for (PRInt32 colIndex=aColIndex; colIndex<count; colIndex++)
{
void * data = row->ElementAt(colIndex);
if (nsnull==data)
{
result = colIndex;
break;
}
result++;
}
}
}
return result;
}

View File

@@ -31,17 +31,18 @@ class nsTableCellFrame;
* Each cell is represented by a CellData object.
*
* @see CellData
* @see nsTableFrame::BuildCellMap
* @see nsTableFrame::AddCellToMap
* @see nsTableFrame::GrowCellMap
* @see nsTableFrame::BuildCellIntoMap
*
* acts like a 2-dimensional array, so all offsets are 0-indexed
* mRows is an array of rows. a row cannot be null.
* each row is an array of cells. a cell can be null.
*/
class nsCellMap
{
protected:
/** storage for CellData pointers */
PRInt32 *mCells; ///XXX CellData *?
/** storage for rows */
nsVoidArray *mRows;
/** storage for CellData pointers */
PRInt32 *mMinColSpans;
@@ -49,19 +50,28 @@ protected:
/** a cache of the column frames, by col index */
nsVoidArray * mColFrames;
/** the number of rows */
PRInt32 mRowCount; // in java, we could just do fCellMap.length;
/** the number of rows. mRows[0] - mRows[mRowCount-1] are non-null. */
PRInt32 mRowCount;
/** the number of rows allocated (due to cells having rowspans extending beyond the end of the table */
PRInt32 mTotalRowCount;
/** the number of columns (the max of all row lengths) */
PRInt32 mColCount;
public:
/** constructor
* @param aRows - initial number of rows
* @param aColumns - initial number of columns
*/
nsCellMap(PRInt32 aRows, PRInt32 aColumns);
// NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED
/** destructor
* NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED
*/
~nsCellMap();
/** initialize the CellMap to (aRows x aColumns) */
/** set the CellMap to (aRows x aColumns) */
void Reset(PRInt32 aRows, PRInt32 aColumns);
/** return the CellData for the cell at (aRowIndex,aColIndex) */
@@ -73,24 +83,49 @@ public:
/** assign aCellData to the cell at (aRow,aColumn) */
void SetCellAt(CellData *aCellData, PRInt32 aRow, PRInt32 aColumn);
/** expand the CellMap to have aRowCount rows. The number of columns remains the same */
void GrowToRow(PRInt32 aRowCount);
/** expand the CellMap to have aColCount columns. The number of rows remains the same */
void GrowTo(PRInt32 aColCount);
void GrowToCol(PRInt32 aColCount);
/** return the total number of columns in the table represented by this CellMap */
PRInt32 GetColCount() const;
/** return the total number of rows in the table represented by this CellMap */
/** return the actual number of rows in the table represented by this CellMap */
PRInt32 GetRowCount() const;
nsTableColFrame * GetColumnFrame(PRInt32 aColIndex);
/** return the column frame associated with aColIndex */
nsTableColFrame * GetColumnFrame(PRInt32 aColIndex) const;
/** return the index of the next column in aRowIndex after aColIndex
* that does not have a cell assigned to it.
* If aColIndex is past the end of the row, it is returned.
* If the row is not initialized in the cell map, 0 is returned.
*/
PRInt32 GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const;
/** cache the min col span for all cells in aColIndex */
void SetMinColSpan(PRInt32 aColIndex, PRBool aColSpan);
PRInt32 GetMinColSpan(PRInt32 aColIndex);
/** get the cached min col span for aColIndex */
PRInt32 GetMinColSpan(PRInt32 aColIndex) const;
/** add a column frame to the list of column frames
* column frames must be added in order
*/
void AppendColumnFrame(nsTableColFrame *aColFrame);
/** return PR_TRUE if aRowIndex has any cells with rowspan>1 contained
* within it (not just cells that are in the row, but cells that span
* into the row as well.
*/
PRBool RowImpactedBySpanningCell(PRInt32 aRowIndex);
/** return PR_TRUE if aColIndex has any cells with colspan>1 contained
* within it (not just cells that are in the col, but cells that span
* into the col as well.
*/
PRBool ColumnImpactedBySpanningCell(PRInt32 aColIndex);
/** for debugging */
@@ -102,16 +137,22 @@ public:
inline CellData * nsCellMap::GetCellAt(PRInt32 aRowIndex, PRInt32 aColIndex) const
{
NS_PRECONDITION(0<=aRowIndex && aRowIndex < mRowCount, "bad aRowIndex arg");
NS_PRECONDITION(0<=aColIndex && aColIndex < mColCount, "bad aColIndex arg");
NS_PRECONDITION(0<=aRowIndex, "bad aRowIndex arg");
NS_PRECONDITION(0<=aColIndex, "bad aColIndex arg");
// don't check index vs. count for row or col, because it's ok to ask for a cell that doesn't yet exist
NS_PRECONDITION(nsnull!=mRows, "bad mRows");
PRInt32 index = (aRowIndex*mColCount)+aColIndex;
return (CellData *)mCells[index];
CellData *result = nsnull;
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex));
if (nsnull!=row)
result = (CellData *)(row->ElementAt(aColIndex));
return result;
}
inline nsTableCellFrame * nsCellMap::GetCellFrameAt(PRInt32 aRowIndex, PRInt32 aColIndex) const
{
NS_PRECONDITION(0<=aRowIndex && aRowIndex < mRowCount, "bad aRowIndex arg");
NS_PRECONDITION(0<=aRowIndex, "bad aRowIndex arg");
// don't check aRowIndex vs. mRowCount, because it's ok to ask for a cell in a row that doesn't yet exist
NS_PRECONDITION(0<=aColIndex && aColIndex < mColCount, "bad aColIndex arg");
nsTableCellFrame *result = nsnull;
@@ -134,8 +175,6 @@ inline PRInt32 nsCellMap::GetRowCount() const
inline void nsCellMap::AppendColumnFrame(nsTableColFrame *aColFrame)
{
mColFrames->AppendElement(aColFrame);
// sanity check
NS_ASSERTION(mColFrames->Count()<=mColCount, "too many columns appended to CellMap");
}

View File

@@ -44,6 +44,15 @@ public:
virtual PRBool BalanceColumnWidths(nsIStyleContext *aTableStyle,
const nsReflowState& aReflowState,
nscoord aMaxWidth)=0;
/** return the computed max "natural" size of the table.
* this is the sum of the desired size of the content taking into account table
* attributes, but NOT factoring in the available size the table is laying out into.
* the actual table width in a given situation will depend on the available size
* provided by the parent (especially for percent-width tables.)
*/
virtual nscoord GetTableMaxWidth() const = 0;
};
#endif

View File

@@ -17,6 +17,7 @@
*/
#include "nsTableColGroupFrame.h"
#include "nsTableColFrame.h"
#include "nsTableFrame.h"
#include "nsITableContent.h"
#include "nsIReflowCommand.h"
#include "nsIStyleContext.h"
@@ -117,6 +118,9 @@ NS_METHOD nsTableColGroupFrame::Reflow(nsIPresContext& aPresContext,
// set nsColFrame-specific information
((nsTableColFrame *)kidFrame)->SetColumnIndex(colIndex+mStartColIndex);
nsIFrame* tableFrame=nsnull;
GetGeometricParent(tableFrame);
((nsTableFrame *)tableFrame)->AddColumnFrame((nsTableColFrame *)kidFrame);
// Link child frame into the list of children
if (nsnull != prevKidFrame) {

View File

@@ -517,12 +517,6 @@ void nsTableFrame::ResetCellMap ()
mCellMap = nsnull; // for now, will rebuild when needed
}
/* call when column structure has changed. */
void nsTableFrame::ResetColumns ()
{
EnsureCellMap();
}
/** sum the columns represented by all nsTableColGroup objects
* if the cell map says there are more columns than this,
* add extra implicit columns to the content tree.
@@ -533,7 +527,7 @@ void nsTableFrame::EnsureColumns(nsIPresContext* aPresContext,
nsReflowStatus& aStatus)
{
// XXX sec should only be called on firstInFlow
EnsureCellMap();
SetMinColSpanForTable();
if (nsnull==mCellMap)
return; // no info yet, so nothing useful to do
@@ -637,10 +631,6 @@ void nsTableFrame::EnsureColumnFrameAt(PRInt32 aColIndex,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
if (nsnull!=mCellMap)
return; // we already have a cell map so this makes no sense
PRInt32 actualColumns = 0;
nsTableColGroupFrame *lastColGroupFrame = nsnull;
nsIFrame * firstRowGroupFrame=nsnull;
@@ -724,6 +714,32 @@ void nsTableFrame::EnsureColumnFrameAt(PRInt32 aColIndex,
}
}
void nsTableFrame::AddColumnFrame (nsTableColFrame *aColFrame)
{
mCellMap->AppendColumnFrame(aColFrame);
}
/** return the index of the next row that is not yet assigned */
PRInt32 nsTableFrame::GetNextAvailRowIndex() const
{
PRInt32 result=0;
if (nsnull!=mCellMap)
{
result = mCellMap->GetRowCount(); // the next index is the current count
mCellMap->GrowToRow(result+1); // expand the cell map to include this new row
}
return result;
}
/** return the index of the next column in aRowIndex that does not have a cell assigned to it */
PRInt32 nsTableFrame::GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const
{
PRInt32 result=0;
if (nsnull!=mCellMap)
result = mCellMap->GetNextAvailColIndex(aRowIndex, aColIndex);
return result;
}
/** Get the cell map for this table frame. It is not always mCellMap.
* Only the firstInFlow has a legit cell map
*/
@@ -737,123 +753,10 @@ nsCellMap * nsTableFrame::GetCellMap()
return mCellMap;
}
void nsTableFrame::EnsureCellMap()
void nsTableFrame::SetMinColSpanForTable()
{ // XXX: must be called ONLY on first-in-flow
if (mCellMap == nsnull)
BuildCellMap();
}
void nsTableFrame::BuildCellMap ()
{
// XXX: must be called only on first-in-flow!
if (gsDebug==PR_TRUE) printf("Build Cell Map...\n");
int rowCount = GetRowCount();
if (0 == rowCount)
{
// until we have some rows, there's nothing useful to do
return;
}
// Make an educated guess as to how many columns we have. It's
// only a guess because we can't know exactly until we have
// processed the last row.
nsTableRowGroupFrame *rowGroupFrame = NextRowGroupFrame(nsnull);
if (0 == mColCount)
mColCount = GetSpecifiedColumnCount();
if (0 == mColCount) // no column parts
{
// Use the first row to estimate the number of columns
nsTableRowFrame *rowFrame;
rowGroupFrame->FirstChild((nsIFrame*&)rowFrame);
if (nsnull!=rowFrame)
{
mColCount = rowFrame->GetMaxColumns();
if (gsDebug==PR_TRUE)
printf("mColCount=0 at start. Guessing col count to be %d from a row.\n", mColCount);
}
}
// If we have a cell map reset it; otherwise allocate a new cell map
if (nsnull==mCellMap)
mCellMap = new nsCellMap(rowCount, mColCount);
else
mCellMap->Reset(rowCount, mColCount);
if (gsDebug==PR_TRUE) printf("mCellMap set to (%d, %d)\n", rowCount, mColCount);
// Iterate over each row group frame
PRInt32 rowIndex = -1;
while (nsnull != rowGroupFrame)
{
// Iterate over each row frame within the row group
nsTableRowFrame *rowFrame;
rowGroupFrame->FirstChild((nsIFrame*&)rowFrame);
while (nsnull != rowFrame)
{
// Set the row frame's row index. Note that this is a table-wide index,
// and not the index within the row group
rowIndex++;
rowFrame->SetRowIndex(rowIndex);
// Iterate the table cells
PRInt32 cellIndex = 0;
PRInt32 colIndex = 0;
nsIFrame* cellFrame;
rowFrame->FirstChild(cellFrame);
while ((nsnull != cellFrame) && (colIndex < mColCount))
{
if (gsDebug==PR_TRUE) printf(" colIndex = %d, with mColCount = %d\n", colIndex, mColCount);
CellData *data = mCellMap->GetCellAt(rowIndex, colIndex);
if (nsnull == data)
{
BuildCellIntoMap((nsTableCellFrame*)cellFrame, rowIndex, colIndex);
cellIndex++;
// Get the next cell frame
cellFrame->GetNextSibling(cellFrame);
}
colIndex++;
}
// See if there are any cell frames left in this row
if (nsnull != cellFrame)
{
// We didn't use all the cells in this row up. Grow the cell
// data because we now know that we have more columns than we
// originally thought we had.
PRInt32 cellCount = cellIndex + LengthOf(cellFrame);
if (gsDebug==PR_TRUE) printf(" calling GrowCellMap because cellIndex < %d\n", cellIndex, cellCount);
GrowCellMap (cellCount);
while (nsnull != cellFrame)
{
if (gsDebug==PR_TRUE) printf(" calling GrowCellMap again because cellIndex < %d\n", cellIndex, cellCount);
GrowCellMap (colIndex + 1); // ensure enough cols in map, may be low due to colspans
CellData *data =mCellMap->GetCellAt(rowIndex, colIndex);
if (data == nsnull)
{
BuildCellIntoMap((nsTableCellFrame*)cellFrame, rowIndex, colIndex);
cellIndex++;
// Get the next cell frame
cellFrame->GetNextSibling(cellFrame);
}
colIndex++;
}
}
// Get the next row frame
rowFrame->GetNextSibling((nsIFrame *&)rowFrame);
}
// Get the next row group frame
rowGroupFrame = NextRowGroupFrame(rowGroupFrame);
}
if (gsDebug==PR_TRUE)
DumpCellMap ();
// iterate through the columns setting the min col span if necessary
// it would be more efficient if we could do this in the above loop
// set the minColSpan for each column
PRInt32 rowCount = mCellMap->GetRowCount();
for (PRInt32 colIndex=0; colIndex<mColCount; colIndex++)
{
PRInt32 minColSpan;
@@ -874,6 +777,56 @@ void nsTableFrame::BuildCellMap ()
}
}
void nsTableFrame::AddCellToTable (nsTableRowFrame *aRowFrame,
nsTableCellFrame *aCellFrame,
PRBool aAddRow)
{
NS_ASSERTION(nsnull!=aRowFrame, "bad aRowFrame arg");
NS_ASSERTION(nsnull!=aCellFrame, "bad aCellFrame arg");
NS_PRECONDITION(nsnull!=mCellMap, "bad cellMap");
// XXX: must be called only on first-in-flow!
if (gsDebug==PR_TRUE) printf("Build Cell Map...\n");
// Make an educated guess as to how many columns we have. It's
// only a guess because we can't know exactly until we have
// processed the last row.
if (0 == mColCount)
mColCount = GetSpecifiedColumnCount();
if (0 == mColCount) // no column parts
{
mColCount = aRowFrame->GetMaxColumns();
}
PRInt32 rowIndex;
// If we have a cell map reset it; otherwise allocate a new cell map
// also determine the index of aRowFrame and set it if necessary
if (0==mCellMap->GetRowCount())
{ // this is the first time we've ever been called
rowIndex = 0;
if (gsDebug==PR_TRUE) printf("rowFrame %p set to index %d\n", aRowFrame, rowIndex);
}
else
{
rowIndex = mCellMap->GetRowCount() - 1; // rowIndex is 0-indexed, rowCount is 1-indexed
}
PRInt32 colIndex=0;
while (PR_TRUE)
{
CellData *data = mCellMap->GetCellAt(rowIndex, colIndex);
if (nsnull == data)
{
BuildCellIntoMap(aCellFrame, rowIndex, colIndex);
break;
}
colIndex++;
}
if (gsDebug==PR_TRUE)
DumpCellMap ();
}
/**
*/
void nsTableFrame::DumpCellMap ()
@@ -882,7 +835,7 @@ void nsTableFrame::DumpCellMap ()
if (nsnull != mCellMap)
{
PRInt32 rowCount = mCellMap->GetRowCount();
PRInt32 cols = GetColCount();
PRInt32 cols = mCellMap->GetColCount();
for (PRInt32 r = 0; r < rowCount; r++)
{
if (gsDebug==PR_TRUE)
@@ -933,29 +886,35 @@ void nsTableFrame::DumpCellMap ()
void nsTableFrame::BuildCellIntoMap (nsTableCellFrame *aCell, PRInt32 aRowIndex, PRInt32 aColIndex)
{
NS_PRECONDITION (nsnull!=aCell, "bad cell arg");
NS_PRECONDITION (aColIndex < mColCount, "bad column index arg");
NS_PRECONDITION (aRowIndex < GetRowCount(), "bad row index arg");
NS_PRECONDITION (0 <= aColIndex, "bad column index arg");
NS_PRECONDITION (0 <= aRowIndex, "bad row index arg");
// Setup CellMap for this cell
int rowSpan = GetEffectiveRowSpan (aRowIndex, aCell);
int colSpan = aCell->GetColSpan ();
int rowSpan = aCell->GetRowSpan();
int colSpan = aCell->GetColSpan();
if (gsDebug==PR_TRUE) printf(" BuildCellIntoMap. rowSpan = %d, colSpan = %d\n", rowSpan, colSpan);
// Grow the mCellMap array if we will end up addressing
// some new columns.
if (mColCount < (aColIndex + colSpan))
if (mCellMap->GetColCount() < (aColIndex + colSpan))
{
if (gsDebug==PR_TRUE) printf(" mColCount=%d<aColIndex+colSpan so calling GrowCellMap(%d)\n", mColCount, aColIndex+colSpan);
if (gsDebug==PR_TRUE)
printf(" calling GrowCellMap(%d)\n", aColIndex+colSpan);
GrowCellMap (aColIndex + colSpan);
}
if (mCellMap->GetRowCount() < (aRowIndex+1))
{
printf("*********************************************** calling GrowToRow(%d)\n", aRowIndex+1);
mCellMap->GrowToRow(aRowIndex+1);
}
// Setup CellMap for this cell in the table
CellData *data = new CellData ();
data->mCell = aCell;
data->mRealCell = data;
if (gsDebug==PR_TRUE) printf(" calling mCellMap->SetCellAt(data, %d, %d)\n", aRowIndex, aColIndex);
mCellMap->SetCellAt(data, aRowIndex, aColIndex);
aCell->SetColIndex (aColIndex);
// Create CellData objects for the rows that this cell spans. Set
// their mCell to nsnull and their mRealCell to point to data. If
@@ -1000,10 +959,7 @@ void nsTableFrame::GrowCellMap (PRInt32 aColCount)
{
if (nsnull!=mCellMap)
{
if (mColCount < aColCount)
{
mCellMap->GrowTo(aColCount);
}
mCellMap->GrowToCol(aColCount);
mColCount = aColCount;
}
}
@@ -1444,6 +1400,9 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext& aPresContext,
{
if (PR_FALSE==IsFirstPassValid())
{ // we treat the table as if we've never seen the layout data before
if (mCellMap!=nsnull)
delete mCellMap;
mCellMap = new nsCellMap(0, 0);
mPass = kPASS_FIRST;
aStatus = ResizeReflowPass1(&aPresContext, aDesiredSize, aReflowState, aStatus);
// check result
@@ -1651,8 +1610,6 @@ nsReflowStatus nsTableFrame::ResizeReflowPass1(nsIPresContext* aPresContext,
}
}
// BuildColumnCache calls EnsureCellMap. If that ever changes, be sure to call EnsureCellMap
// here first.
BuildColumnCache(aPresContext, aDesiredSize, aReflowState, aStatus);
// Recalculate Layout Dependencies
RecalcLayoutData();
@@ -1749,7 +1706,7 @@ nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext,
}
// Return our size and our status
aDesiredSize.width = aReflowState.maxSize.width;
aDesiredSize.width = ComputeDesiredWidth(aReflowState);
aDesiredSize.height = state.y + myBorderPadding.top + myBorderPadding.bottom;
@@ -1773,6 +1730,20 @@ nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext,
}
nscoord nsTableFrame::ComputeDesiredWidth(const nsReflowState& aReflowState) const
{
nscoord desiredWidth=aReflowState.maxSize.width;
// this is the biggest hack in the world. But there's no other rational way to handle nested percent tables
nsStylePosition* position;
PRBool isNested=IsNested(aReflowState, position);
if((eReflowReason_Initial==aReflowState.reason) &&
(PR_TRUE==isNested) && (eStyleUnit_Percent==position->mWidth.GetUnit()))
{
desiredWidth = mTableLayoutStrategy->GetTableMaxWidth();
}
return desiredWidth;
}
// Collapse child's top margin with previous bottom margin
nscoord nsTableFrame::GetTopMarginFor(nsIPresContext* aCX,
InnerTableReflowState& aState,
@@ -2492,9 +2463,7 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext,
nsSize* aMaxElementSize)
{
NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!");
if (nsnull==mCellMap)
return; // we don't have any information yet, so we can't do any useful work
NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!");
PRInt32 numCols = GetColCount();
if (nsnull==mColumnWidths)
@@ -2570,13 +2539,12 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext,
void nsTableFrame::SetTableWidth(nsIPresContext* aPresContext)
{
NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!");
NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!");
nscoord cellSpacing = GetCellSpacing();
if (gsDebug==PR_TRUE)
printf ("SetTableWidth with cellSpacing = %d ", cellSpacing);
PRInt32 tableWidth = cellSpacing;
if (nsnull==mCellMap)
return; // no info, so nothing to do
PRInt32 numCols = GetColCount();
for (PRInt32 colIndex = 0; colIndex<numCols; colIndex++)
@@ -2762,6 +2730,8 @@ void nsTableFrame::BuildColumnCache( nsIPresContext* aPresContext,
nsReflowStatus& aStatus
)
{
// probably want this assertion : NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!");
NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!");
EnsureColumns(aPresContext, aDesiredSize, aReflowState, aStatus);
if (nsnull==mColCache)
{
@@ -2938,8 +2908,8 @@ PRInt32 nsTableFrame::GetColumnWidth(PRInt32 aColIndex)
else
{
NS_ASSERTION(nsnull!=mColumnWidths, "illegal state");
#ifdef DEBUG
NS_ASSERTION(nsnull!=mCellMap, "no column layout data");
#ifdef NS_DEBUG
NS_ASSERTION(nsnull!=mCellMap, "no cell map");
PRInt32 numCols = GetColCount();
NS_ASSERTION (numCols > aColIndex, "bad arg, col index out of bounds");
#endif
@@ -2957,7 +2927,7 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth)
{
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);
if (this!=firstInFlow)
firstInFlow->SetColumnWidth(aColIndex, aWidth);
else
@@ -2968,9 +2938,6 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth)
}
}
/**
*
* Update the border style to map to the HTML border style
@@ -3176,6 +3143,34 @@ nsresult nsTableFrame::NewFrame(nsIFrame** aInstancePtrResult,
return NS_OK;
}
/* helper method for determining if this is a nested table or not */
PRBool nsTableFrame::IsNested(const nsReflowState& aReflowState, nsStylePosition *& aPosition) const
{
PRBool result = PR_FALSE;
#ifdef NS_DEBUG
PRInt32 counter=0;
#endif
// Walk up the reflow state chain until we find a cell or the root
const nsReflowState* rs = aReflowState.parentReflowState;
while (nsnull != rs)
{
#ifdef NS_DEBUG
counter++;
NS_ASSERTION(counter<100000, "infinite loop in IsNested");
#endif
nsIFrame* parentTable = nsnull;
rs->frame->QueryInterface(kTableFrameCID, (void**) &parentTable);
if (nsnull!=parentTable)
{
result = PR_TRUE;
parentTable->GetStyleData(eStyleStruct_Position, ((nsStyleStruct *&)aPosition));
break;
}
rs = rs->parentReflowState;
}
return result;
}
/* helper method for getting the width of the table's containing block */
nscoord nsTableFrame::GetTableContainerWidth(const nsReflowState& aReflowState)
{

View File

@@ -73,6 +73,9 @@ public:
// nsISupports
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
/** helper method for determining if this is a nested table or not */
PRBool IsNested(const nsReflowState& aReflowState, nsStylePosition *& aPosition) const;
/** helper method for getting the width of the table's containing block */
static nscoord GetTableContainerWidth(const nsReflowState& aState);
@@ -210,6 +213,26 @@ public:
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
/** return the index of the next row that is not yet assigned.
* If no row is initialized, 0 is returned.
*/
PRInt32 GetNextAvailRowIndex() const;
/** return the index of the next column in aRowIndex after aColIndex
* that does not have a cell assigned to it.
* If aColIndex is past the end of the row, it is returned.
* If the row is not initialized, 0 is returned.
*/
PRInt32 GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const;
/** build as much of the CellMap as possible from the info we have so far
*/
virtual void AddCellToTable (nsTableRowFrame *aRowFrame,
nsTableCellFrame *aCellFrame,
PRBool aAddRow);
virtual void AddColumnFrame (nsTableColFrame *aColFrame);
protected:
/** protected constructor.
@@ -256,6 +279,11 @@ protected:
nsIFrame* aKidFrame,
nscoord aDeltaY);
/** return the desired width of this table accounting for the current
* reflow state, and for the table attributes and parent
*/
nscoord ComputeDesiredWidth(const nsReflowState& aReflowState) const;
nscoord GetTopMarginFor(nsIPresContext* aCX,
InnerTableReflowState& aState,
const nsMargin& aKidMargin);
@@ -345,10 +373,6 @@ protected:
void MapHTMLBorderStyle(nsStyleSpacing& aSpacingStyle, nscoord aBorderWidth);
PRBool ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult);
/** build as much of the CellMap as possible from the info we have so far
*/
virtual void BuildCellMap ();
/** called whenever the number of columns changes, to increase the storage in mCellMap
*/
virtual void GrowCellMap(PRInt32 aColCount);
@@ -374,12 +398,6 @@ protected:
*/
void ListColumnLayoutData(FILE* out, PRInt32 aIndent);
/** ResetColumns is called when the column structure of the table is changed.
* Call with caution, only when adding or removing columns, changing
* column attributes, changing the rowspan or colspan attribute of a cell, etc.
*/
virtual void ResetColumns ();
/** sum the columns represented by all nsTableColGroup objects.
* if the cell map says there are more columns than this,
* add extra implicit columns to the content tree.
@@ -389,9 +407,8 @@ protected:
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
/** Ensure that the cell map has been built for the table
*/
virtual void EnsureCellMap();
/** Set the min col span for every column in the table. Scans the whole table. */
virtual void SetMinColSpanForTable();
virtual void BuildColumnCache(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,

View File

@@ -31,6 +31,11 @@
#include "nsIPtr.h"
#include "nsIReflowCommand.h"
#include "nsCSSRendering.h"
// the following header files are required for style optimizations that work only when the child content is really a cell
#include "nsITableContent.h"
#include "nsTableCell.h"
static NS_DEFINE_IID(kITableContentIID, NS_ITABLECONTENT_IID);
// end includes for style optimizations that require real content knowledge
NS_DEF_PTR(nsIStyleContext);
@@ -502,12 +507,13 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext,
// Place our children, one at a time, until we are out of children
nsSize kidMaxElementSize;
PRInt32 kidIndex = 0;
PRInt32 colIndex = -1;
PRInt32 colIndex = 0;
nsIFrame* prevKidFrame = nsnull;
nscoord maxTopMargin = 0;
nscoord maxBottomMargin = 0;
nscoord x = 0;
nsresult result = NS_OK;
PRBool isFirst=PR_TRUE;
for (;;) {
// Get the next content object
@@ -517,22 +523,41 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext,
break; // no more content
}
// what column does this cell span to?
nsHTMLValue value;
((nsHTMLTagContent *)cell)->GetAttribute(nsHTMLAtoms::align, value);
if (value.GetUnit() == eHTMLUnit_Integer)
colIndex += value.GetIntValue();
else
colIndex += 1;
// what row am I?
if (PR_TRUE==isFirst)
SetRowIndex(aState.tableFrame->GetNextAvailRowIndex());
// create column frames if necessary
// what column does this cell belong to?
colIndex = aState.tableFrame->GetNextAvailColIndex(mRowIndex, colIndex);
if (gsDebug) printf("%p : next col index = %d\n", this, colIndex);
/* 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
*/
nsITableContent *tableContentInterface = nsnull;
nsresult rv = cell->QueryInterface(kITableContentIID,
(void **)&tableContentInterface); // tableContentInterface: REFCNT++
if (NS_SUCCEEDED(rv))
{ // we know it's a table part of some sort, is it a cell?
const int contentType = ((nsTableContent *)tableContentInterface)->GetType();
if (contentType == nsITableContent::kTableCellType)
{
((nsTableCell *)tableContentInterface)->SetColIndex(colIndex);
if (gsDebug) printf("%p : set cell content %p to col index = %d\n", this, tableContentInterface, colIndex);
}
NS_RELEASE(tableContentInterface);
}
// part of the style optimization is to ensure that the column frame for the cell exists
// we used to do this post-pass1, now we do it incrementally for the optimization
nsReflowStatus status;
aState.tableFrame->EnsureColumnFrameAt(colIndex,
&aPresContext,
aDesiredSize,
aState.reflowState,
status);
// Create a child frame -- always an nsTableCell frame
// Create a child frame -- always an nsTableCellFrame
nsIStyleContext* kidSC = aPresContext.ResolveStyleContextFor(cell, this, PR_FALSE);
nsIContentDelegate* kidDel = cell->GetDelegate(&aPresContext);
nsIFrame* kidFrame;
@@ -544,21 +569,16 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext,
NS_RELEASE(kidSC);
break;
}
// this sets the frame's notion of it's column index
((nsTableCellFrame *)kidFrame)->SetColIndex(colIndex);
if (gsDebug) printf("%p : set cell frame %p to col index = %d\n", this, kidFrame, colIndex);
// add the cell frame to the table's cell map
aState.tableFrame->AddCellToTable(this, (nsTableCellFrame *)kidFrame, isFirst);
// Because we're not splittable always allow the child to be as high as
// it wants. The default available width is also unconstrained so we can
// get the child's maximum width
nsSize kidAvailSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
// See if there's a width constraint for the cell
/*
const nsStylePosition* cellPosition = (const nsStylePosition*)
kidSC->GetStyleData(eStyleStruct_Position);
if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit())
{
kidAvailSize.width = cellPosition->mWidth.GetCoordValue();
}
*/
NS_IF_RELEASE(kidSC);
// Get the child's margins
@@ -622,6 +642,7 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext,
// Move to the next content child
prevKidFrame = kidFrame;
kidIndex++;
isFirst=PR_FALSE;
}
SetMaxChildHeight(aState.maxCellHeight, maxTopMargin, maxBottomMargin); // remember height of tallest child who doesn't have a row span

View File

@@ -321,7 +321,7 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
maxColSpan = colSpan;
if (colIndex!=cellFrame->GetColIndex()) {
// For cells that span cols, we figure in the row using previously-built SpanInfo
NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match row span"); // sanity check
NS_ASSERTION(1 != cellFrame->GetColSpan(), "col index does not match col span"); // sanity check
continue;
}
@@ -1251,6 +1251,8 @@ PRBool BasicTableLayoutStrategy::BalanceColumnsTableFits(const nsReflowState& aR
}
tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset;
}
if (1==mNumCols)
tableWidth += colInset;
/* --- post-process if necessary --- */
@@ -1966,6 +1968,8 @@ PRBool BasicTableLayoutStrategy::BalanceColumnsConstrained( const nsReflowState&
}
tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset;
}
if (1==mNumCols)
tableWidth += colInset;
/* --- post-process if necessary --- */
// first, assign autoWidth columns a width
if (PR_TRUE==atLeastOneAutoWidthColumn)

View File

@@ -29,6 +29,9 @@ struct nsStylePosition;
/* ----------- SpanInfo ---------- */
/** SpanInfo is a transient data structure that holds info about
* cells that have col spans. Used during column balancing.
*/
struct SpanInfo
{
PRInt32 span;
@@ -59,6 +62,11 @@ inline SpanInfo::SpanInfo(PRInt32 aColIndex, PRInt32 aSpan,
/* ---------- BasicTableLayoutStrategy ---------- */
/** Implementation of Nav4 compatible HTML browser table layout.
* The input to this class is the results from pass1 table layout.
* The output from this class is to set the column widths in
* mTableFrame.
*/
class BasicTableLayoutStrategy : public nsITableLayoutStrategy
{
public:
@@ -69,15 +77,25 @@ public:
*/
BasicTableLayoutStrategy(nsTableFrame *aFrame, PRInt32 aNumCols);
~BasicTableLayoutStrategy();
/** destructor */
virtual ~BasicTableLayoutStrategy();
/** call once every time any table thing changes (content, structure, or style) */
virtual PRBool Initialize(nsSize* aMaxElementSize);
/** Called during resize reflow to determine the new column widths
* @param aTableStyle - the resolved style for mTableFrame
* @param aReflowState - the reflow state for mTableFrame
* @param aMaxWidth - the computed max width for columns to fit into
*/
virtual PRBool BalanceColumnWidths(nsIStyleContext * aTableStyle,
const nsReflowState& aReflowState,
nscoord aMaxWidth);
nscoord GetTableMaxWidth() const;
protected:
/** assign widths for each column.
* if the column has a fixed coord width, use it.
* if the column includes col spanning cells,
@@ -235,5 +253,8 @@ protected:
};
inline nscoord BasicTableLayoutStrategy::GetTableMaxWidth() const
{ return mMaxTableWidth; };
#endif

View File

@@ -33,6 +33,10 @@ class nsTableCellFrame;
class CellData
{
public:
/** if not null, the cell that this CellData maps.
* if null, mRealCell points to the CellData that holds the
* mapped cell frame.
*/
nsTableCellFrame *mCell;
CellData *mRealCell;
CellData *mOverlap;

View File

@@ -16,121 +16,155 @@
* Reserved.
*/
#include "nsCRT.h"
#include "nsVoidArray.h"
#include "nsCellMap.h"
#include "nsTableFrame.h"
#ifdef NS_DEBUG
static PRBool gsDebug1 = PR_FALSE;
static PRBool gsDebug = PR_FALSE;
#else
static const PRBool gsDebug1 = PR_FALSE;
static const PRBool gsDebug = PR_FALSE;
#endif
static const PRInt32 gBytesPerPointer = sizeof(PRInt32);
nsCellMap::nsCellMap(int aRows, int aColumns)
: mRowCount(aRows),
mColCount(aColumns)
nsCellMap::nsCellMap(int aRowCount, int aColCount)
: mRowCount(0),
mColCount(0),
mTotalRowCount(0)
{
mCells = nsnull;
mRows = nsnull;
mColFrames = nsnull;
mMinColSpans = nsnull;
Reset(aRows, aColumns);
Reset(aRowCount, aColCount);
}
nsCellMap::~nsCellMap()
{
if (nsnull!=mCells)
if (nsnull!=mRows)
{
for (int i=0;i<mRowCount;i++)
for (int i=mTotalRowCount-1; 0<i; i--)
{
for (int j=0;j<mColCount;j++)
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(i));
for (int j=mColCount; 0<j ;j--)
{
int index = (i*mColCount)+j;
CellData* data = (CellData*)mCells[index];
CellData* data = (CellData *)(row->ElementAt(j));
if (data != nsnull)
{
delete data;
mCells[index] = 0;
}
}
delete row;
}
delete [] mCells;
delete mRows;
}
if (nsnull != mColFrames)
delete mColFrames;
if (nsnull != mMinColSpans)
delete [] mMinColSpans;
mCells = nsnull;
mRows = nsnull;
mColFrames = nsnull;
mMinColSpans = nsnull;
};
void nsCellMap::Reset(int aRows, int aColumns)
void nsCellMap::Reset(int aRowCount, int aColCount)
{
if (gsDebug) printf("calling Reset(%d,%d) with mRC=%d, mCC=%d, mTRC=%d\n",
aRowCount, aColCount, mRowCount, mColCount, mTotalRowCount);
if (nsnull==mColFrames)
{
mColFrames = new nsVoidArray();
mColFrames = new nsVoidArray(); // don't give the array a count, because null col frames are illegal (unlike null cell entries in a row)
}
// needs to be more efficient, to reuse space if possible
if (nsnull!=mCells)
if (nsnull==mRowCount)
{
delete [] mCells;
mCells = nsnull;
mRows = new nsVoidArray(); // don't give the array a count, because null rows are illegal (unlike null cell entries in a row)
}
mRowCount = aRows;
mColCount = aColumns;
mCells = new PRInt32 [mRowCount*mColCount*gBytesPerPointer];
nsCRT::memset (mCells, 0, (mRowCount*mColCount)*gBytesPerPointer);
}
void nsCellMap::GrowTo(int aColCount)
{
if (aColCount <= mColCount)
return;
PRInt32 * newCells = new PRInt32 [mRowCount*aColCount*gBytesPerPointer];
for (int rowIndex = 0; rowIndex < mRowCount; rowIndex++)
// void arrays force the caller to handle null padding elements themselves
// so if the number of columns has increased, we need to add extra cols to each row
PRInt32 newCols = mColCount-aColCount;
for (PRInt32 rowIndex=0; rowIndex<mRowCount; rowIndex++)
{
PRInt32* rowMap = (PRInt32*)&(newCells[rowIndex*aColCount]);
nsCRT::memset (rowMap, 0, aColCount*gBytesPerPointer);
nsCRT::memcpy(rowMap, &(mCells[rowIndex*mColCount]), mColCount*gBytesPerPointer);
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(rowIndex));
const PRInt32 colsInRow = row->Count();
if (colsInRow == aColCount)
break; // we already have enough columns in each row
for (PRInt32 colIndex = colsInRow; colIndex<aColCount; colIndex++)
row->AppendElement(nsnull);
}
if (mCells != nsnull)
delete [] mCells;
mCells = newCells;
// if the number of rows has increased, add the extra rows
PRInt32 newRows = aRowCount-mTotalRowCount; // (new row count) - (total row allocation)
for ( ; newRows>0; newRows--)
{
nsVoidArray *row;
if (0!=aColCount)
row = new nsVoidArray(aColCount);
else
row = new nsVoidArray();
mRows->AppendElement(row);
}
mRowCount = aRowCount;
mTotalRowCount = PR_MAX(mTotalRowCount, mRowCount);
mColCount = aColCount;
if (gsDebug) printf("leaving Reset with mRC=%d, mCC=%d, mTRC=%d\n",
mRowCount, mColCount, mTotalRowCount);
}
void nsCellMap::DumpCellMap() const
{
if (gsDebug1==PR_TRUE)
if (gsDebug==PR_TRUE)
{
printf("Cell Map =\n");
for (int i=0;i<mRowCount;i++)
for (int j=0;j<mColCount;j++)
for (int rowIndex=0; rowIndex<mRowCount ; rowIndex++)
{
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(rowIndex));
for (int colIndex=0; colIndex<mColCount; colIndex++)
{
int index = (i*mColCount)+j;
printf("Cell [%d,%d] = %d for index = %d\n", i, j, mCells[index], index);
CellData* data = (CellData *)(row->ElementAt(colIndex));
printf("Cell [%d,%d] = %p for index = %d\n", rowIndex, colIndex, data);
}
}
}
}
void nsCellMap::SetCellAt(CellData *aCell, int aRow, int aColumn)
void nsCellMap::GrowToRow(PRInt32 aRowCount)
{
//Assert aRow, aColumn
int index = (aRow*mColCount)+aColumn;
CellData* cell = GetCellAt(aRow,aColumn);
if (cell != nsnull)
delete cell;
mCells[index] = (PRInt32)aCell;
Reset(aRowCount, mColCount);
}
nsTableColFrame* nsCellMap::GetColumnFrame(PRInt32 aColIndex)
void nsCellMap::GrowToCol(PRInt32 aColCount)
{
Reset(mRowCount, aColCount);
}
void nsCellMap::SetCellAt(CellData *aCell, int aRowIndex, int aColIndex)
{
NS_PRECONDITION(nsnull!=aCell, "bad aCell");
PRInt32 newRows = (aRowIndex+1)-mRowCount; // add 1 to the "index" to get a "count"
if (0<newRows)
{
for ( ; newRows>0; newRows--)
{
nsVoidArray *row = new nsVoidArray(mColCount);
mRows->AppendElement(row);
}
mTotalRowCount = aRowIndex+1; // remember to always add 1 to an index when you want a count
}
CellData* cell = GetCellAt(aRowIndex,aColIndex);
if (cell != nsnull)
delete cell;
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex));
row->ReplaceElementAt(aCell, aColIndex);
if (gsDebug) printf("leaving SetCellAt(%p,%d,%d) with mRC=%d, mCC=%d, mTRC=%d\n",
aCell, aRowIndex, aColIndex, mRowCount, mColCount, mTotalRowCount);
}
nsTableColFrame* nsCellMap::GetColumnFrame(PRInt32 aColIndex) const
{
NS_ASSERTION(nsnull!=mColFrames, "bad state");
return (nsTableColFrame *)(mColFrames->ElementAt(aColIndex));
}
@@ -151,7 +185,7 @@ void nsCellMap::SetMinColSpan(PRInt32 aColIndex, PRBool aColSpan)
mMinColSpans[aColIndex] = aColSpan;
}
PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex)
PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex) const
{
NS_ASSERTION(aColIndex<mColCount, "bad aColIndex");
@@ -161,6 +195,34 @@ PRInt32 nsCellMap::GetMinColSpan(PRInt32 aColIndex)
return result;
}
/** return the index of the next column in aRowIndex that does not have a cell assigned to it */
PRInt32 nsCellMap::GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const
{
PRInt32 result = 0;
if (aColIndex > mColCount)
{
result = aColIndex;
}
else
{
if (aRowIndex < mRowCount)
{
result = aColIndex;
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex));
PRInt32 count = row->Count();
for (PRInt32 colIndex=aColIndex; colIndex<count; colIndex++)
{
void * data = row->ElementAt(colIndex);
if (nsnull==data)
{
result = colIndex;
break;
}
result++;
}
}
}
return result;
}

View File

@@ -31,17 +31,18 @@ class nsTableCellFrame;
* Each cell is represented by a CellData object.
*
* @see CellData
* @see nsTableFrame::BuildCellMap
* @see nsTableFrame::AddCellToMap
* @see nsTableFrame::GrowCellMap
* @see nsTableFrame::BuildCellIntoMap
*
* acts like a 2-dimensional array, so all offsets are 0-indexed
* mRows is an array of rows. a row cannot be null.
* each row is an array of cells. a cell can be null.
*/
class nsCellMap
{
protected:
/** storage for CellData pointers */
PRInt32 *mCells; ///XXX CellData *?
/** storage for rows */
nsVoidArray *mRows;
/** storage for CellData pointers */
PRInt32 *mMinColSpans;
@@ -49,19 +50,28 @@ protected:
/** a cache of the column frames, by col index */
nsVoidArray * mColFrames;
/** the number of rows */
PRInt32 mRowCount; // in java, we could just do fCellMap.length;
/** the number of rows. mRows[0] - mRows[mRowCount-1] are non-null. */
PRInt32 mRowCount;
/** the number of rows allocated (due to cells having rowspans extending beyond the end of the table */
PRInt32 mTotalRowCount;
/** the number of columns (the max of all row lengths) */
PRInt32 mColCount;
public:
/** constructor
* @param aRows - initial number of rows
* @param aColumns - initial number of columns
*/
nsCellMap(PRInt32 aRows, PRInt32 aColumns);
// NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED
/** destructor
* NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED
*/
~nsCellMap();
/** initialize the CellMap to (aRows x aColumns) */
/** set the CellMap to (aRows x aColumns) */
void Reset(PRInt32 aRows, PRInt32 aColumns);
/** return the CellData for the cell at (aRowIndex,aColIndex) */
@@ -73,24 +83,49 @@ public:
/** assign aCellData to the cell at (aRow,aColumn) */
void SetCellAt(CellData *aCellData, PRInt32 aRow, PRInt32 aColumn);
/** expand the CellMap to have aRowCount rows. The number of columns remains the same */
void GrowToRow(PRInt32 aRowCount);
/** expand the CellMap to have aColCount columns. The number of rows remains the same */
void GrowTo(PRInt32 aColCount);
void GrowToCol(PRInt32 aColCount);
/** return the total number of columns in the table represented by this CellMap */
PRInt32 GetColCount() const;
/** return the total number of rows in the table represented by this CellMap */
/** return the actual number of rows in the table represented by this CellMap */
PRInt32 GetRowCount() const;
nsTableColFrame * GetColumnFrame(PRInt32 aColIndex);
/** return the column frame associated with aColIndex */
nsTableColFrame * GetColumnFrame(PRInt32 aColIndex) const;
/** return the index of the next column in aRowIndex after aColIndex
* that does not have a cell assigned to it.
* If aColIndex is past the end of the row, it is returned.
* If the row is not initialized in the cell map, 0 is returned.
*/
PRInt32 GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const;
/** cache the min col span for all cells in aColIndex */
void SetMinColSpan(PRInt32 aColIndex, PRBool aColSpan);
PRInt32 GetMinColSpan(PRInt32 aColIndex);
/** get the cached min col span for aColIndex */
PRInt32 GetMinColSpan(PRInt32 aColIndex) const;
/** add a column frame to the list of column frames
* column frames must be added in order
*/
void AppendColumnFrame(nsTableColFrame *aColFrame);
/** return PR_TRUE if aRowIndex has any cells with rowspan>1 contained
* within it (not just cells that are in the row, but cells that span
* into the row as well.
*/
PRBool RowImpactedBySpanningCell(PRInt32 aRowIndex);
/** return PR_TRUE if aColIndex has any cells with colspan>1 contained
* within it (not just cells that are in the col, but cells that span
* into the col as well.
*/
PRBool ColumnImpactedBySpanningCell(PRInt32 aColIndex);
/** for debugging */
@@ -102,16 +137,22 @@ public:
inline CellData * nsCellMap::GetCellAt(PRInt32 aRowIndex, PRInt32 aColIndex) const
{
NS_PRECONDITION(0<=aRowIndex && aRowIndex < mRowCount, "bad aRowIndex arg");
NS_PRECONDITION(0<=aColIndex && aColIndex < mColCount, "bad aColIndex arg");
NS_PRECONDITION(0<=aRowIndex, "bad aRowIndex arg");
NS_PRECONDITION(0<=aColIndex, "bad aColIndex arg");
// don't check index vs. count for row or col, because it's ok to ask for a cell that doesn't yet exist
NS_PRECONDITION(nsnull!=mRows, "bad mRows");
PRInt32 index = (aRowIndex*mColCount)+aColIndex;
return (CellData *)mCells[index];
CellData *result = nsnull;
nsVoidArray *row = (nsVoidArray *)(mRows->ElementAt(aRowIndex));
if (nsnull!=row)
result = (CellData *)(row->ElementAt(aColIndex));
return result;
}
inline nsTableCellFrame * nsCellMap::GetCellFrameAt(PRInt32 aRowIndex, PRInt32 aColIndex) const
{
NS_PRECONDITION(0<=aRowIndex && aRowIndex < mRowCount, "bad aRowIndex arg");
NS_PRECONDITION(0<=aRowIndex, "bad aRowIndex arg");
// don't check aRowIndex vs. mRowCount, because it's ok to ask for a cell in a row that doesn't yet exist
NS_PRECONDITION(0<=aColIndex && aColIndex < mColCount, "bad aColIndex arg");
nsTableCellFrame *result = nsnull;
@@ -134,8 +175,6 @@ inline PRInt32 nsCellMap::GetRowCount() const
inline void nsCellMap::AppendColumnFrame(nsTableColFrame *aColFrame)
{
mColFrames->AppendElement(aColFrame);
// sanity check
NS_ASSERTION(mColFrames->Count()<=mColCount, "too many columns appended to CellMap");
}

View File

@@ -44,6 +44,15 @@ public:
virtual PRBool BalanceColumnWidths(nsIStyleContext *aTableStyle,
const nsReflowState& aReflowState,
nscoord aMaxWidth)=0;
/** return the computed max "natural" size of the table.
* this is the sum of the desired size of the content taking into account table
* attributes, but NOT factoring in the available size the table is laying out into.
* the actual table width in a given situation will depend on the available size
* provided by the parent (especially for percent-width tables.)
*/
virtual nscoord GetTableMaxWidth() const = 0;
};
#endif

View File

@@ -17,6 +17,7 @@
*/
#include "nsTableColGroupFrame.h"
#include "nsTableColFrame.h"
#include "nsTableFrame.h"
#include "nsITableContent.h"
#include "nsIReflowCommand.h"
#include "nsIStyleContext.h"
@@ -117,6 +118,9 @@ NS_METHOD nsTableColGroupFrame::Reflow(nsIPresContext& aPresContext,
// set nsColFrame-specific information
((nsTableColFrame *)kidFrame)->SetColumnIndex(colIndex+mStartColIndex);
nsIFrame* tableFrame=nsnull;
GetGeometricParent(tableFrame);
((nsTableFrame *)tableFrame)->AddColumnFrame((nsTableColFrame *)kidFrame);
// Link child frame into the list of children
if (nsnull != prevKidFrame) {

View File

@@ -517,12 +517,6 @@ void nsTableFrame::ResetCellMap ()
mCellMap = nsnull; // for now, will rebuild when needed
}
/* call when column structure has changed. */
void nsTableFrame::ResetColumns ()
{
EnsureCellMap();
}
/** sum the columns represented by all nsTableColGroup objects
* if the cell map says there are more columns than this,
* add extra implicit columns to the content tree.
@@ -533,7 +527,7 @@ void nsTableFrame::EnsureColumns(nsIPresContext* aPresContext,
nsReflowStatus& aStatus)
{
// XXX sec should only be called on firstInFlow
EnsureCellMap();
SetMinColSpanForTable();
if (nsnull==mCellMap)
return; // no info yet, so nothing useful to do
@@ -637,10 +631,6 @@ void nsTableFrame::EnsureColumnFrameAt(PRInt32 aColIndex,
const nsReflowState& aReflowState,
nsReflowStatus& aStatus)
{
if (nsnull!=mCellMap)
return; // we already have a cell map so this makes no sense
PRInt32 actualColumns = 0;
nsTableColGroupFrame *lastColGroupFrame = nsnull;
nsIFrame * firstRowGroupFrame=nsnull;
@@ -724,6 +714,32 @@ void nsTableFrame::EnsureColumnFrameAt(PRInt32 aColIndex,
}
}
void nsTableFrame::AddColumnFrame (nsTableColFrame *aColFrame)
{
mCellMap->AppendColumnFrame(aColFrame);
}
/** return the index of the next row that is not yet assigned */
PRInt32 nsTableFrame::GetNextAvailRowIndex() const
{
PRInt32 result=0;
if (nsnull!=mCellMap)
{
result = mCellMap->GetRowCount(); // the next index is the current count
mCellMap->GrowToRow(result+1); // expand the cell map to include this new row
}
return result;
}
/** return the index of the next column in aRowIndex that does not have a cell assigned to it */
PRInt32 nsTableFrame::GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const
{
PRInt32 result=0;
if (nsnull!=mCellMap)
result = mCellMap->GetNextAvailColIndex(aRowIndex, aColIndex);
return result;
}
/** Get the cell map for this table frame. It is not always mCellMap.
* Only the firstInFlow has a legit cell map
*/
@@ -737,123 +753,10 @@ nsCellMap * nsTableFrame::GetCellMap()
return mCellMap;
}
void nsTableFrame::EnsureCellMap()
void nsTableFrame::SetMinColSpanForTable()
{ // XXX: must be called ONLY on first-in-flow
if (mCellMap == nsnull)
BuildCellMap();
}
void nsTableFrame::BuildCellMap ()
{
// XXX: must be called only on first-in-flow!
if (gsDebug==PR_TRUE) printf("Build Cell Map...\n");
int rowCount = GetRowCount();
if (0 == rowCount)
{
// until we have some rows, there's nothing useful to do
return;
}
// Make an educated guess as to how many columns we have. It's
// only a guess because we can't know exactly until we have
// processed the last row.
nsTableRowGroupFrame *rowGroupFrame = NextRowGroupFrame(nsnull);
if (0 == mColCount)
mColCount = GetSpecifiedColumnCount();
if (0 == mColCount) // no column parts
{
// Use the first row to estimate the number of columns
nsTableRowFrame *rowFrame;
rowGroupFrame->FirstChild((nsIFrame*&)rowFrame);
if (nsnull!=rowFrame)
{
mColCount = rowFrame->GetMaxColumns();
if (gsDebug==PR_TRUE)
printf("mColCount=0 at start. Guessing col count to be %d from a row.\n", mColCount);
}
}
// If we have a cell map reset it; otherwise allocate a new cell map
if (nsnull==mCellMap)
mCellMap = new nsCellMap(rowCount, mColCount);
else
mCellMap->Reset(rowCount, mColCount);
if (gsDebug==PR_TRUE) printf("mCellMap set to (%d, %d)\n", rowCount, mColCount);
// Iterate over each row group frame
PRInt32 rowIndex = -1;
while (nsnull != rowGroupFrame)
{
// Iterate over each row frame within the row group
nsTableRowFrame *rowFrame;
rowGroupFrame->FirstChild((nsIFrame*&)rowFrame);
while (nsnull != rowFrame)
{
// Set the row frame's row index. Note that this is a table-wide index,
// and not the index within the row group
rowIndex++;
rowFrame->SetRowIndex(rowIndex);
// Iterate the table cells
PRInt32 cellIndex = 0;
PRInt32 colIndex = 0;
nsIFrame* cellFrame;
rowFrame->FirstChild(cellFrame);
while ((nsnull != cellFrame) && (colIndex < mColCount))
{
if (gsDebug==PR_TRUE) printf(" colIndex = %d, with mColCount = %d\n", colIndex, mColCount);
CellData *data = mCellMap->GetCellAt(rowIndex, colIndex);
if (nsnull == data)
{
BuildCellIntoMap((nsTableCellFrame*)cellFrame, rowIndex, colIndex);
cellIndex++;
// Get the next cell frame
cellFrame->GetNextSibling(cellFrame);
}
colIndex++;
}
// See if there are any cell frames left in this row
if (nsnull != cellFrame)
{
// We didn't use all the cells in this row up. Grow the cell
// data because we now know that we have more columns than we
// originally thought we had.
PRInt32 cellCount = cellIndex + LengthOf(cellFrame);
if (gsDebug==PR_TRUE) printf(" calling GrowCellMap because cellIndex < %d\n", cellIndex, cellCount);
GrowCellMap (cellCount);
while (nsnull != cellFrame)
{
if (gsDebug==PR_TRUE) printf(" calling GrowCellMap again because cellIndex < %d\n", cellIndex, cellCount);
GrowCellMap (colIndex + 1); // ensure enough cols in map, may be low due to colspans
CellData *data =mCellMap->GetCellAt(rowIndex, colIndex);
if (data == nsnull)
{
BuildCellIntoMap((nsTableCellFrame*)cellFrame, rowIndex, colIndex);
cellIndex++;
// Get the next cell frame
cellFrame->GetNextSibling(cellFrame);
}
colIndex++;
}
}
// Get the next row frame
rowFrame->GetNextSibling((nsIFrame *&)rowFrame);
}
// Get the next row group frame
rowGroupFrame = NextRowGroupFrame(rowGroupFrame);
}
if (gsDebug==PR_TRUE)
DumpCellMap ();
// iterate through the columns setting the min col span if necessary
// it would be more efficient if we could do this in the above loop
// set the minColSpan for each column
PRInt32 rowCount = mCellMap->GetRowCount();
for (PRInt32 colIndex=0; colIndex<mColCount; colIndex++)
{
PRInt32 minColSpan;
@@ -874,6 +777,56 @@ void nsTableFrame::BuildCellMap ()
}
}
void nsTableFrame::AddCellToTable (nsTableRowFrame *aRowFrame,
nsTableCellFrame *aCellFrame,
PRBool aAddRow)
{
NS_ASSERTION(nsnull!=aRowFrame, "bad aRowFrame arg");
NS_ASSERTION(nsnull!=aCellFrame, "bad aCellFrame arg");
NS_PRECONDITION(nsnull!=mCellMap, "bad cellMap");
// XXX: must be called only on first-in-flow!
if (gsDebug==PR_TRUE) printf("Build Cell Map...\n");
// Make an educated guess as to how many columns we have. It's
// only a guess because we can't know exactly until we have
// processed the last row.
if (0 == mColCount)
mColCount = GetSpecifiedColumnCount();
if (0 == mColCount) // no column parts
{
mColCount = aRowFrame->GetMaxColumns();
}
PRInt32 rowIndex;
// If we have a cell map reset it; otherwise allocate a new cell map
// also determine the index of aRowFrame and set it if necessary
if (0==mCellMap->GetRowCount())
{ // this is the first time we've ever been called
rowIndex = 0;
if (gsDebug==PR_TRUE) printf("rowFrame %p set to index %d\n", aRowFrame, rowIndex);
}
else
{
rowIndex = mCellMap->GetRowCount() - 1; // rowIndex is 0-indexed, rowCount is 1-indexed
}
PRInt32 colIndex=0;
while (PR_TRUE)
{
CellData *data = mCellMap->GetCellAt(rowIndex, colIndex);
if (nsnull == data)
{
BuildCellIntoMap(aCellFrame, rowIndex, colIndex);
break;
}
colIndex++;
}
if (gsDebug==PR_TRUE)
DumpCellMap ();
}
/**
*/
void nsTableFrame::DumpCellMap ()
@@ -882,7 +835,7 @@ void nsTableFrame::DumpCellMap ()
if (nsnull != mCellMap)
{
PRInt32 rowCount = mCellMap->GetRowCount();
PRInt32 cols = GetColCount();
PRInt32 cols = mCellMap->GetColCount();
for (PRInt32 r = 0; r < rowCount; r++)
{
if (gsDebug==PR_TRUE)
@@ -933,29 +886,35 @@ void nsTableFrame::DumpCellMap ()
void nsTableFrame::BuildCellIntoMap (nsTableCellFrame *aCell, PRInt32 aRowIndex, PRInt32 aColIndex)
{
NS_PRECONDITION (nsnull!=aCell, "bad cell arg");
NS_PRECONDITION (aColIndex < mColCount, "bad column index arg");
NS_PRECONDITION (aRowIndex < GetRowCount(), "bad row index arg");
NS_PRECONDITION (0 <= aColIndex, "bad column index arg");
NS_PRECONDITION (0 <= aRowIndex, "bad row index arg");
// Setup CellMap for this cell
int rowSpan = GetEffectiveRowSpan (aRowIndex, aCell);
int colSpan = aCell->GetColSpan ();
int rowSpan = aCell->GetRowSpan();
int colSpan = aCell->GetColSpan();
if (gsDebug==PR_TRUE) printf(" BuildCellIntoMap. rowSpan = %d, colSpan = %d\n", rowSpan, colSpan);
// Grow the mCellMap array if we will end up addressing
// some new columns.
if (mColCount < (aColIndex + colSpan))
if (mCellMap->GetColCount() < (aColIndex + colSpan))
{
if (gsDebug==PR_TRUE) printf(" mColCount=%d<aColIndex+colSpan so calling GrowCellMap(%d)\n", mColCount, aColIndex+colSpan);
if (gsDebug==PR_TRUE)
printf(" calling GrowCellMap(%d)\n", aColIndex+colSpan);
GrowCellMap (aColIndex + colSpan);
}
if (mCellMap->GetRowCount() < (aRowIndex+1))
{
printf("*********************************************** calling GrowToRow(%d)\n", aRowIndex+1);
mCellMap->GrowToRow(aRowIndex+1);
}
// Setup CellMap for this cell in the table
CellData *data = new CellData ();
data->mCell = aCell;
data->mRealCell = data;
if (gsDebug==PR_TRUE) printf(" calling mCellMap->SetCellAt(data, %d, %d)\n", aRowIndex, aColIndex);
mCellMap->SetCellAt(data, aRowIndex, aColIndex);
aCell->SetColIndex (aColIndex);
// Create CellData objects for the rows that this cell spans. Set
// their mCell to nsnull and their mRealCell to point to data. If
@@ -1000,10 +959,7 @@ void nsTableFrame::GrowCellMap (PRInt32 aColCount)
{
if (nsnull!=mCellMap)
{
if (mColCount < aColCount)
{
mCellMap->GrowTo(aColCount);
}
mCellMap->GrowToCol(aColCount);
mColCount = aColCount;
}
}
@@ -1444,6 +1400,9 @@ NS_METHOD nsTableFrame::Reflow(nsIPresContext& aPresContext,
{
if (PR_FALSE==IsFirstPassValid())
{ // we treat the table as if we've never seen the layout data before
if (mCellMap!=nsnull)
delete mCellMap;
mCellMap = new nsCellMap(0, 0);
mPass = kPASS_FIRST;
aStatus = ResizeReflowPass1(&aPresContext, aDesiredSize, aReflowState, aStatus);
// check result
@@ -1651,8 +1610,6 @@ nsReflowStatus nsTableFrame::ResizeReflowPass1(nsIPresContext* aPresContext,
}
}
// BuildColumnCache calls EnsureCellMap. If that ever changes, be sure to call EnsureCellMap
// here first.
BuildColumnCache(aPresContext, aDesiredSize, aReflowState, aStatus);
// Recalculate Layout Dependencies
RecalcLayoutData();
@@ -1749,7 +1706,7 @@ nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext,
}
// Return our size and our status
aDesiredSize.width = aReflowState.maxSize.width;
aDesiredSize.width = ComputeDesiredWidth(aReflowState);
aDesiredSize.height = state.y + myBorderPadding.top + myBorderPadding.bottom;
@@ -1773,6 +1730,20 @@ nsReflowStatus nsTableFrame::ResizeReflowPass2(nsIPresContext* aPresContext,
}
nscoord nsTableFrame::ComputeDesiredWidth(const nsReflowState& aReflowState) const
{
nscoord desiredWidth=aReflowState.maxSize.width;
// this is the biggest hack in the world. But there's no other rational way to handle nested percent tables
nsStylePosition* position;
PRBool isNested=IsNested(aReflowState, position);
if((eReflowReason_Initial==aReflowState.reason) &&
(PR_TRUE==isNested) && (eStyleUnit_Percent==position->mWidth.GetUnit()))
{
desiredWidth = mTableLayoutStrategy->GetTableMaxWidth();
}
return desiredWidth;
}
// Collapse child's top margin with previous bottom margin
nscoord nsTableFrame::GetTopMarginFor(nsIPresContext* aCX,
InnerTableReflowState& aState,
@@ -2492,9 +2463,7 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext,
nsSize* aMaxElementSize)
{
NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!");
if (nsnull==mCellMap)
return; // we don't have any information yet, so we can't do any useful work
NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!");
PRInt32 numCols = GetColCount();
if (nsnull==mColumnWidths)
@@ -2570,13 +2539,12 @@ void nsTableFrame::BalanceColumnWidths(nsIPresContext* aPresContext,
void nsTableFrame::SetTableWidth(nsIPresContext* aPresContext)
{
NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!");
NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!");
nscoord cellSpacing = GetCellSpacing();
if (gsDebug==PR_TRUE)
printf ("SetTableWidth with cellSpacing = %d ", cellSpacing);
PRInt32 tableWidth = cellSpacing;
if (nsnull==mCellMap)
return; // no info, so nothing to do
PRInt32 numCols = GetColCount();
for (PRInt32 colIndex = 0; colIndex<numCols; colIndex++)
@@ -2762,6 +2730,8 @@ void nsTableFrame::BuildColumnCache( nsIPresContext* aPresContext,
nsReflowStatus& aStatus
)
{
// probably want this assertion : NS_ASSERTION(nsnull==mPrevInFlow, "never ever call me on a continuing frame!");
NS_ASSERTION(nsnull!=mCellMap, "never ever call me until the cell map is built!");
EnsureColumns(aPresContext, aDesiredSize, aReflowState, aStatus);
if (nsnull==mColCache)
{
@@ -2938,8 +2908,8 @@ PRInt32 nsTableFrame::GetColumnWidth(PRInt32 aColIndex)
else
{
NS_ASSERTION(nsnull!=mColumnWidths, "illegal state");
#ifdef DEBUG
NS_ASSERTION(nsnull!=mCellMap, "no column layout data");
#ifdef NS_DEBUG
NS_ASSERTION(nsnull!=mCellMap, "no cell map");
PRInt32 numCols = GetColCount();
NS_ASSERTION (numCols > aColIndex, "bad arg, col index out of bounds");
#endif
@@ -2957,7 +2927,7 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth)
{
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);
if (this!=firstInFlow)
firstInFlow->SetColumnWidth(aColIndex, aWidth);
else
@@ -2968,9 +2938,6 @@ void nsTableFrame::SetColumnWidth(PRInt32 aColIndex, nscoord aWidth)
}
}
/**
*
* Update the border style to map to the HTML border style
@@ -3176,6 +3143,34 @@ nsresult nsTableFrame::NewFrame(nsIFrame** aInstancePtrResult,
return NS_OK;
}
/* helper method for determining if this is a nested table or not */
PRBool nsTableFrame::IsNested(const nsReflowState& aReflowState, nsStylePosition *& aPosition) const
{
PRBool result = PR_FALSE;
#ifdef NS_DEBUG
PRInt32 counter=0;
#endif
// Walk up the reflow state chain until we find a cell or the root
const nsReflowState* rs = aReflowState.parentReflowState;
while (nsnull != rs)
{
#ifdef NS_DEBUG
counter++;
NS_ASSERTION(counter<100000, "infinite loop in IsNested");
#endif
nsIFrame* parentTable = nsnull;
rs->frame->QueryInterface(kTableFrameCID, (void**) &parentTable);
if (nsnull!=parentTable)
{
result = PR_TRUE;
parentTable->GetStyleData(eStyleStruct_Position, ((nsStyleStruct *&)aPosition));
break;
}
rs = rs->parentReflowState;
}
return result;
}
/* helper method for getting the width of the table's containing block */
nscoord nsTableFrame::GetTableContainerWidth(const nsReflowState& aReflowState)
{

View File

@@ -73,6 +73,9 @@ public:
// nsISupports
NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
/** helper method for determining if this is a nested table or not */
PRBool IsNested(const nsReflowState& aReflowState, nsStylePosition *& aPosition) const;
/** helper method for getting the width of the table's containing block */
static nscoord GetTableContainerWidth(const nsReflowState& aState);
@@ -210,6 +213,26 @@ public:
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
/** return the index of the next row that is not yet assigned.
* If no row is initialized, 0 is returned.
*/
PRInt32 GetNextAvailRowIndex() const;
/** return the index of the next column in aRowIndex after aColIndex
* that does not have a cell assigned to it.
* If aColIndex is past the end of the row, it is returned.
* If the row is not initialized, 0 is returned.
*/
PRInt32 GetNextAvailColIndex(PRInt32 aRowIndex, PRInt32 aColIndex) const;
/** build as much of the CellMap as possible from the info we have so far
*/
virtual void AddCellToTable (nsTableRowFrame *aRowFrame,
nsTableCellFrame *aCellFrame,
PRBool aAddRow);
virtual void AddColumnFrame (nsTableColFrame *aColFrame);
protected:
/** protected constructor.
@@ -256,6 +279,11 @@ protected:
nsIFrame* aKidFrame,
nscoord aDeltaY);
/** return the desired width of this table accounting for the current
* reflow state, and for the table attributes and parent
*/
nscoord ComputeDesiredWidth(const nsReflowState& aReflowState) const;
nscoord GetTopMarginFor(nsIPresContext* aCX,
InnerTableReflowState& aState,
const nsMargin& aKidMargin);
@@ -345,10 +373,6 @@ protected:
void MapHTMLBorderStyle(nsStyleSpacing& aSpacingStyle, nscoord aBorderWidth);
PRBool ConvertToPixelValue(nsHTMLValue& aValue, PRInt32 aDefault, PRInt32& aResult);
/** build as much of the CellMap as possible from the info we have so far
*/
virtual void BuildCellMap ();
/** called whenever the number of columns changes, to increase the storage in mCellMap
*/
virtual void GrowCellMap(PRInt32 aColCount);
@@ -374,12 +398,6 @@ protected:
*/
void ListColumnLayoutData(FILE* out, PRInt32 aIndent);
/** ResetColumns is called when the column structure of the table is changed.
* Call with caution, only when adding or removing columns, changing
* column attributes, changing the rowspan or colspan attribute of a cell, etc.
*/
virtual void ResetColumns ();
/** sum the columns represented by all nsTableColGroup objects.
* if the cell map says there are more columns than this,
* add extra implicit columns to the content tree.
@@ -389,9 +407,8 @@ protected:
const nsReflowState& aReflowState,
nsReflowStatus& aStatus);
/** Ensure that the cell map has been built for the table
*/
virtual void EnsureCellMap();
/** Set the min col span for every column in the table. Scans the whole table. */
virtual void SetMinColSpanForTable();
virtual void BuildColumnCache(nsIPresContext* aPresContext,
nsReflowMetrics& aDesiredSize,

View File

@@ -31,6 +31,11 @@
#include "nsIPtr.h"
#include "nsIReflowCommand.h"
#include "nsCSSRendering.h"
// the following header files are required for style optimizations that work only when the child content is really a cell
#include "nsITableContent.h"
#include "nsTableCell.h"
static NS_DEFINE_IID(kITableContentIID, NS_ITABLECONTENT_IID);
// end includes for style optimizations that require real content knowledge
NS_DEF_PTR(nsIStyleContext);
@@ -502,12 +507,13 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext,
// Place our children, one at a time, until we are out of children
nsSize kidMaxElementSize;
PRInt32 kidIndex = 0;
PRInt32 colIndex = -1;
PRInt32 colIndex = 0;
nsIFrame* prevKidFrame = nsnull;
nscoord maxTopMargin = 0;
nscoord maxBottomMargin = 0;
nscoord x = 0;
nsresult result = NS_OK;
PRBool isFirst=PR_TRUE;
for (;;) {
// Get the next content object
@@ -517,22 +523,41 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext,
break; // no more content
}
// what column does this cell span to?
nsHTMLValue value;
((nsHTMLTagContent *)cell)->GetAttribute(nsHTMLAtoms::align, value);
if (value.GetUnit() == eHTMLUnit_Integer)
colIndex += value.GetIntValue();
else
colIndex += 1;
// what row am I?
if (PR_TRUE==isFirst)
SetRowIndex(aState.tableFrame->GetNextAvailRowIndex());
// create column frames if necessary
// what column does this cell belong to?
colIndex = aState.tableFrame->GetNextAvailColIndex(mRowIndex, colIndex);
if (gsDebug) printf("%p : next col index = %d\n", this, colIndex);
/* 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
*/
nsITableContent *tableContentInterface = nsnull;
nsresult rv = cell->QueryInterface(kITableContentIID,
(void **)&tableContentInterface); // tableContentInterface: REFCNT++
if (NS_SUCCEEDED(rv))
{ // we know it's a table part of some sort, is it a cell?
const int contentType = ((nsTableContent *)tableContentInterface)->GetType();
if (contentType == nsITableContent::kTableCellType)
{
((nsTableCell *)tableContentInterface)->SetColIndex(colIndex);
if (gsDebug) printf("%p : set cell content %p to col index = %d\n", this, tableContentInterface, colIndex);
}
NS_RELEASE(tableContentInterface);
}
// part of the style optimization is to ensure that the column frame for the cell exists
// we used to do this post-pass1, now we do it incrementally for the optimization
nsReflowStatus status;
aState.tableFrame->EnsureColumnFrameAt(colIndex,
&aPresContext,
aDesiredSize,
aState.reflowState,
status);
// Create a child frame -- always an nsTableCell frame
// Create a child frame -- always an nsTableCellFrame
nsIStyleContext* kidSC = aPresContext.ResolveStyleContextFor(cell, this, PR_FALSE);
nsIContentDelegate* kidDel = cell->GetDelegate(&aPresContext);
nsIFrame* kidFrame;
@@ -544,21 +569,16 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext,
NS_RELEASE(kidSC);
break;
}
// this sets the frame's notion of it's column index
((nsTableCellFrame *)kidFrame)->SetColIndex(colIndex);
if (gsDebug) printf("%p : set cell frame %p to col index = %d\n", this, kidFrame, colIndex);
// add the cell frame to the table's cell map
aState.tableFrame->AddCellToTable(this, (nsTableCellFrame *)kidFrame, isFirst);
// Because we're not splittable always allow the child to be as high as
// it wants. The default available width is also unconstrained so we can
// get the child's maximum width
nsSize kidAvailSize(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
// See if there's a width constraint for the cell
/*
const nsStylePosition* cellPosition = (const nsStylePosition*)
kidSC->GetStyleData(eStyleStruct_Position);
if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit())
{
kidAvailSize.width = cellPosition->mWidth.GetCoordValue();
}
*/
NS_IF_RELEASE(kidSC);
// Get the child's margins
@@ -622,6 +642,7 @@ nsTableRowFrame::InitialReflow(nsIPresContext& aPresContext,
// Move to the next content child
prevKidFrame = kidFrame;
kidIndex++;
isFirst=PR_FALSE;
}
SetMaxChildHeight(aState.maxCellHeight, maxTopMargin, maxBottomMargin); // remember height of tallest child who doesn't have a row span