Files
tubestation/layout/tables/BasicTableLayoutStrategy.cpp
buster@netscape.com 2c2bdfb430 the inner table frame now maintains 2 child lists: the main child lists that holds rowgroups and unknown frame
types, and mColGroups. Besides being cleaner and easier to maintain, this should speed some things up slightly
     because I don't have to check display types every time I iterate through colgroup frames.  I *know*
     mColGroups contains only colgroups.  I might do the same for rowgroups (that is, keep unknown frame types
     in their own list as well.)  But that's optional.
the inner table frame used to incorrectly create actual content objects for anonymous colgroups and cols.  Now,
     it just creates frames as appropriate, not content.  To support this, I added some pseudo style contexts and
     related atoms.
1998-12-03 18:01:35 +00:00

2540 lines
111 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
*
* The contents of this file are subject to the Netscape Public License
* Version 1.0 (the "NPL"); you may not use this file except in
* compliance with the NPL. You may obtain a copy of the NPL at
* http://www.mozilla.org/NPL/
*
* Software distributed under the NPL is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
* for the specific language governing rights and limitations under the
* NPL.
*
* The Initial Developer of this code under the NPL is Netscape
* Communications Corporation. Portions created by Netscape are
* Copyright (C) 1998 Netscape Communications Corporation. All Rights
* Reserved.
*/
#include "BasicTableLayoutStrategy.h"
#include "nsTableFrame.h"
#include "nsTableColFrame.h"
#include "nsTableCellFrame.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsVoidArray.h"
#include "nsIPtr.h"
#include "nsHTMLIIDs.h"
NS_DEF_PTR(nsIStyleContext);
#ifdef NS_DEBUG
static PRBool gsDebug = PR_FALSE;
#else
static const PRBool gsDebug = PR_FALSE;
#endif
const nscoord gBigSpace = 100000;
/* ---------- ProportionalColumnLayoutStruct ---------- */
// TODO: make public so other subclasses can use it
/** useful info about a column for layout */
struct ProportionalColumnLayoutStruct
{
ProportionalColumnLayoutStruct(PRInt32 aColIndex,
nscoord aMinColWidth,
nscoord aMaxColWidth,
PRInt32 aProportion)
{
mColIndex = aColIndex;
mMinColWidth = aMinColWidth;
mMaxColWidth = aMaxColWidth;
mProportion = aProportion;
};
PRInt32 mColIndex;
nscoord mMinColWidth;
nscoord mMaxColWidth;
PRInt32 mProportion;
};
/* ---------- ColSpanStruct ---------- */
struct ColSpanStruct
{
PRInt32 colIndex;
PRInt32 colSpan;
nscoord width;
ColSpanStruct(PRInt32 aSpan, PRInt32 aIndex, nscoord aWidth)
{
colSpan = aSpan;
colIndex = aIndex;
width = aWidth;
}
};
/* ---------- BasicTableLayoutStrategy ---------- */
/* return true if the style indicates that the width is fixed
* for the purposes of column width determination
*/
inline
PRBool BasicTableLayoutStrategy::IsFixedWidth(const nsStylePosition* aStylePosition,
const nsStyleTable* aStyleTable)
{
return PRBool ((eStyleUnit_Coord==aStylePosition->mWidth.GetUnit()) ||
(eStyleUnit_Coord==aStyleTable->mSpanWidth.GetUnit()));
}
BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame *aFrame)
{
NS_ASSERTION(nsnull!=aFrame, "bad frame arg");
mTableFrame = aFrame;
mMinTableWidth=0;
mMaxTableWidth=0;
mFixedTableWidth=0;
}
BasicTableLayoutStrategy::~BasicTableLayoutStrategy()
{
}
PRBool BasicTableLayoutStrategy::Initialize(nsSize* aMaxElementSize, PRInt32 aNumCols)
{
#ifdef NS_DEBUG
nsIFrame *tablePIF=nsnull;
mTableFrame->GetPrevInFlow(tablePIF);
NS_ASSERTION(nsnull==tablePIF, "never ever call me on a continuing frame!");
#endif
PRBool result = PR_TRUE;
// re-init instance variables
mNumCols = aNumCols;
mMinTableWidth=0;
mMaxTableWidth=0;
mFixedTableWidth=0;
mCols = mTableFrame->GetEffectiveCOLSAttribute();
// Step 1 - assign the width of all fixed-width columns
AssignPreliminaryColumnWidths();
// set aMaxElementSize here because we compute mMinTableWidth in AssignPreliminaryColumnWidths
if (nsnull!=aMaxElementSize)
{
SetMaxElementSize(aMaxElementSize);
}
return result;
}
void BasicTableLayoutStrategy::SetMaxElementSize(nsSize* aMaxElementSize)
{
if (nsnull!=aMaxElementSize)
{
aMaxElementSize->height = 0;
nsMargin borderPadding;
const nsStylePosition* tablePosition;
const nsStyleSpacing* tableSpacing;
mTableFrame->GetStyleData(eStyleStruct_Position, ((const nsStyleStruct *&)tablePosition));
mTableFrame->GetStyleData(eStyleStruct_Spacing , ((const nsStyleStruct *&)tableSpacing));
tableSpacing->CalcBorderPaddingFor(mTableFrame, borderPadding);
if (tablePosition->mWidth.GetUnit()==eStyleUnit_Coord)
{
aMaxElementSize->width = tablePosition->mWidth.GetCoordValue();
aMaxElementSize->width = PR_MAX(aMaxElementSize->width, mMinTableWidth);
//XXX: need to factor in borderpadding here!
}
else
{
aMaxElementSize->width = mMinTableWidth + borderPadding.left + borderPadding.right;
}
if (PR_TRUE==gsDebug)
printf("%p BTLS::Init setting aMaxElementSize->width = %d\n",
mTableFrame, aMaxElementSize->width);
}
}
PRBool BasicTableLayoutStrategy::BalanceColumnWidths(nsIStyleContext *aTableStyle,
const nsHTMLReflowState& aReflowState,
nscoord aMaxWidth)
{
#ifdef NS_DEBUG
nsIFrame *tablePIF=nsnull;
mTableFrame->GetPrevInFlow(tablePIF);
NS_ASSERTION(nsnull==tablePIF, "never ever call me on a continuing frame!");
#endif
PRBool result;
NS_ASSERTION(nsnull!=aTableStyle, "bad arg");
if (nsnull==aTableStyle)
return PR_FALSE;
nscoord specifiedTableWidth = 0; // not cached as a data member because it can vary depending on aMaxWidth
PRBool tableIsAutoWidth = nsTableFrame::TableIsAutoWidth(mTableFrame, aTableStyle, aReflowState, specifiedTableWidth);
// HACK! Fix TableIsAutoWidth to return the right width
if (specifiedTableWidth>aMaxWidth)
specifiedTableWidth = aMaxWidth;
if (NS_UNCONSTRAINEDSIZE==specifiedTableWidth)
{
specifiedTableWidth=0;
tableIsAutoWidth=PR_TRUE;
}
// Step 2 - determine how much space is really available
nscoord availWidth = aMaxWidth; // start with the max width I've been given
if (NS_UNCONSTRAINEDSIZE!=availWidth) // if that's not infinite, subtract the fixed columns
availWidth -= mFixedTableWidth; // that have already been accounted for
if (PR_FALSE==tableIsAutoWidth) // if the table has a specified width
availWidth = specifiedTableWidth - mFixedTableWidth; // use it, minus the fixed columns already accounted for
//if (0!=mMinTableWidth && mMinTableWidth>availWidth) // if the computed available size is too small
//availWidth = mMinTableWidth; // bump it up to the min
if (0>availWidth) // avail width can never be negative
availWidth=0;
// Step 3 - assign the width of all proportional-width columns in the remaining space
if (PR_TRUE==gsDebug)
{
printf ("BalanceColumnWidths with aMaxWidth = %d, availWidth = %d\n", aMaxWidth, availWidth);
printf ("\t\t specifiedTW = %d, min/maxTW = %d %d\n", specifiedTableWidth, mMinTableWidth, mMaxTableWidth);
printf ("\t\t reflow reason = %d\n", aReflowState.reason);
}
if (PR_TRUE==gsDebug)
{
printf("\n%p: BEGIN BALANCE COLUMN WIDTHS\n", mTableFrame);
for (PRInt32 i=0; i<mNumCols; i++)
printf(" col %d assigned width %d\n", i, mTableFrame->GetColumnWidth(i));
printf("\n");
}
result = BalanceProportionalColumns(aReflowState, availWidth, aMaxWidth,
specifiedTableWidth, tableIsAutoWidth);
if (PR_TRUE==gsDebug)
{
printf("\n%p: END BALANCE COLUMN WIDTHS\n", mTableFrame);
for (PRInt32 i=0; i<mNumCols; i++)
printf(" col %d assigned width %d\n", i, mTableFrame->GetColumnWidth(i));
printf("\n");
}
#ifdef NS_DEBUG // sanity check for table column widths
for (PRInt32 i=0; i<mNumCols; i++)
{
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(i, colFrame);
nscoord minColWidth = colFrame->GetMinColWidth();
nscoord assignedColWidth = mTableFrame->GetColumnWidth(i);
NS_ASSERTION(assignedColWidth >= minColWidth, "illegal width assignment");
}
#endif
return result;
}
// Step 1 - assign the width of all fixed-width columns, all other columns get there max,
// and calculate min/max table width
PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
{
if (gsDebug==PR_TRUE) printf ("** %p: AssignPreliminaryColumnWidths **\n", mTableFrame);
nsVoidArray *spanList=nsnull;
nsVoidArray *colSpanList=nsnull;
PRBool hasColsAttribute = (PRBool)(NS_STYLE_TABLE_COLS_NONE!=mCols);
PRInt32 *minColWidthArray = nsnull; // used for computing the effect of COLS attribute
PRInt32 *maxColWidthArray = nsnull; // used for computing the effect of COLS attribute
if (PR_TRUE==hasColsAttribute)
{
minColWidthArray = new PRInt32[mNumCols];
maxColWidthArray = new PRInt32[mNumCols];
}
const nsStyleTable* tableStyle;
mTableFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)tableStyle);
nscoord cellPadding=0;
if (eStyleUnit_Coord==tableStyle->mCellPadding.GetUnit())
cellPadding=tableStyle->mCellPadding.GetCoordValue();
if (gsDebug) printf ("table cell padding = %d\n", cellPadding);
PRInt32 numRows = mTableFrame->GetRowCount();
PRInt32 colIndex, rowIndex;
// for every column, determine it's min and max width, and keep track of the table width
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
nscoord minColWidth = 0; // min col width factoring in table attributes
nscoord minColContentWidth = 0; // min width of the col's contents, does not take into account table attributes
nscoord maxColWidth = 0; // max col width factoring in table attributes
nscoord effectiveMinColumnWidth = 0; // min col width ignoring cells with colspans
nscoord effectiveMaxColumnWidth = 0; // max col width ignoring cells with colspans
nscoord specifiedFixedColWidth = 0; /* the width of the column if given stylistically (or via cell Width attribute)
only applicable if haveColWidth==PR_TRUE
*/
PRBool haveColWidth = PR_FALSE; /* if true, the column has a width either from HTML width attribute,
from a style rule on the column,
or from a width attr/style on a cell that has colspan==1
*/
// Get column information
nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
if (gsDebug) printf("BTLS::APCW - got colFrame %p for colIndex %d\n", colFrame, colIndex);
NS_ASSERTION(nsnull!=colFrame, "bad col frame");
// Get the columns's style
const nsStylePosition* colPosition;
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
const nsStyleTable* colTableStyle;
colFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)colTableStyle);
// Get fixed column width if it has one
if (eStyleUnit_Coord==colPosition->mWidth.GetUnit())
{
haveColWidth = PR_TRUE;
specifiedFixedColWidth = colPosition->mWidth.GetCoordValue();
specifiedFixedColWidth += (cellPadding*2);
if (gsDebug) printf("BTLS::APCW - got specified col width = %d\n", specifiedFixedColWidth);
}
/* Scan the column, simulatneously assigning column widths
* and computing the min/max column widths
*/
PRInt32 firstRowIndex = -1;
PRInt32 maxColSpan = 1;
PRBool cellGrantingWidth=PR_TRUE;
for (rowIndex = 0; rowIndex<numRows; rowIndex++)
{
nsTableCellFrame * cellFrame = mTableFrame->GetCellFrameAt(rowIndex, colIndex);
if (nsnull==cellFrame)
{ // there is no cell in this row that corresponds to this column
continue;
}
if (-1==firstRowIndex)
firstRowIndex = rowIndex;
if (rowIndex!=cellFrame->GetRowIndex()) {
// For cells that span rows, we only figure it in once
NS_ASSERTION(1 != cellFrame->GetRowSpan(), "row index does not match row span"); // sanity check
continue;
}
PRInt32 colSpan = mTableFrame->GetEffectiveColSpan(colIndex, cellFrame);
if (colSpan>maxColSpan)
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 col span"); // sanity check
continue;
}
nsSize cellMinSize = cellFrame->GetPass1MaxElementSize();
nsSize cellDesiredSize = cellFrame->GetPass1DesiredSize();
nscoord cellDesiredWidth = cellDesiredSize.width;
nscoord cellMinWidth = cellMinSize.width;
if (1==colSpan)
{
if (0==minColContentWidth)
minColContentWidth = cellMinWidth;
else
minColContentWidth = PR_MAX(minColContentWidth, cellMinWidth);
}
else
minColContentWidth = PR_MAX(minColContentWidth, cellMinWidth/colSpan); // no need to divide this proportionately
if (gsDebug==PR_TRUE)
printf (" for cell %d with colspan=%d, min = %d,%d and des = %d,%d\n",
rowIndex, colSpan, cellMinSize.width, cellMinSize.height,
cellDesiredSize.width, cellDesiredSize.height);
if (PR_TRUE==haveColWidth)
{
/* This col has a specified coord fixed width, so set the min and max width to the larger of
(specified width, largest max_element_size of the cells in the column)
factoring in the min width of the prior cells (stored in minColWidth) */
nscoord widthForThisCell = specifiedFixedColWidth;
if (0==specifiedFixedColWidth) // set to min
specifiedFixedColWidth = cellMinWidth;
widthForThisCell = PR_MAX(widthForThisCell, effectiveMinColumnWidth);
if (1==colSpan)
widthForThisCell = PR_MAX(widthForThisCell, cellMinWidth);
mTableFrame->SetColumnWidth(colIndex, widthForThisCell);
maxColWidth = widthForThisCell;
minColWidth = PR_MAX(minColWidth, cellMinWidth);
if (1==colSpan)
{
effectiveMaxColumnWidth = PR_MAX(effectiveMaxColumnWidth, widthForThisCell);
if (0==effectiveMinColumnWidth)
effectiveMinColumnWidth = cellMinWidth;//widthForThisCell;
else
//effectiveMinColumnWidth = PR_MIN(effectiveMinColumnWidth, widthForThisCell);
//above line works for most tables, but it can't be right
effectiveMinColumnWidth = PR_MAX(effectiveMinColumnWidth, cellMinWidth);
//above line seems right and works for xT1, but breaks lots of other tables.
}
}
else
{
if (maxColWidth < cellDesiredWidth)
maxColWidth = cellDesiredWidth;
if ((1==colSpan) && (effectiveMaxColumnWidth < cellDesiredWidth))
effectiveMaxColumnWidth = cellDesiredWidth;
if (minColWidth < cellMinWidth)
minColWidth = cellMinWidth;
// effectiveMinColumnWidth is the min width as if no cells with colspans existed
if ((1==colSpan) && (effectiveMinColumnWidth < cellMinWidth))
effectiveMinColumnWidth = cellMinWidth;
}
if (1<colSpan)
{
// add the column to our list of post-process columns, if all of the intersected columns are auto
PRBool okToAdd = PR_TRUE;
if (numRows==1 || (PR_FALSE==IsFixedWidth(colPosition, colTableStyle)))
okToAdd = PR_FALSE;
else
{
for (PRInt32 i=1; i<colSpan; i++)
{
nsTableColFrame *cf;
mTableFrame->GetColumnFrame(i+colIndex, cf);
const nsStylePosition* colPos;
cf->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPos);
if (colPos->mWidth.GetUnit() != eStyleUnit_Auto &&
(nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN!=cf->GetWidthSource()))
{
okToAdd = PR_FALSE;
break;
}
}
}
nscoord width = cellDesiredSize.width; // used below as the cell's "natural" width
if (eStyleUnit_Coord==colTableStyle->mSpanWidth.GetUnit())
width = colSpan*colTableStyle->mSpanWidth.GetCoordValue(); // "colSpan*" because table frame divided per column spanned
if (PR_TRUE==okToAdd)
{
nscoord colwidth=PR_MAX(width, cellMinWidth);
ColSpanStruct *colSpanInfo = new ColSpanStruct(colSpan, colIndex, colwidth);
if (nsnull==colSpanList)
colSpanList = new nsVoidArray();
colSpanList->AppendElement(colSpanInfo);
}
if (PR_TRUE==cellGrantingWidth)
{
// add the cell to our list of spanning cells
SpanInfo *spanInfo = new SpanInfo(colIndex, colSpan, cellMinWidth, width);
if (nsnull==spanList)
spanList = new nsVoidArray();
spanList->AppendElement(spanInfo);
}
}
//bookkeeping: is this the cell that gave the column it's fixed width attribute?
// must be done after "haveColWidth && cellGrantingWidth" used above
/* since we want the biggest, I don't think this is valid anymore
it made sense when the rule was to grab the first cell that contributed a width
*/
/*
if (PR_TRUE==cellGrantingWidth)
{
const nsStylePosition* cellPosition;
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)cellPosition);
if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit())
cellGrantingWidth=PR_FALSE; //I've found the cell that gave the col it's width
}
*/
// book 'em, Danno
if (gsDebug) {
printf (" after cell %d, minColWidth = %d and maxColWidth = %d\n",
rowIndex, minColWidth, maxColWidth);
}
} // end rowIndex for loop
// adjust the "fixed" width for content that is too wide
if (effectiveMinColumnWidth>specifiedFixedColWidth)
specifiedFixedColWidth=effectiveMinColumnWidth;
// do all the global bookkeeping, factoring in margins
nscoord colInset = mTableFrame->GetCellSpacing();
// keep a running total of the amount of space taken up by all fixed-width columns
if ((PR_TRUE==haveColWidth) && (nsTableColFrame::eWIDTH_SOURCE_CELL==colFrame->GetWidthSource()))
{
mFixedTableWidth += specifiedFixedColWidth + colInset;
if (0==colIndex)
mFixedTableWidth += colInset;
if (PR_TRUE==gsDebug) printf("setting mFixedTableWidth=%d\n", mFixedTableWidth);
}
// cache the computed column info
colFrame->SetMinColWidth(effectiveMinColumnWidth);
colFrame->SetMaxColWidth(effectiveMaxColumnWidth);
colFrame->SetEffectiveMinColWidth(effectiveMinColumnWidth);
colFrame->SetEffectiveMaxColWidth(effectiveMaxColumnWidth);
// this is the default, the real adjustment happens below where we deal with colspans
colFrame->SetAdjustedMinColWidth(effectiveMinColumnWidth);
if ((PR_TRUE==haveColWidth) &&
(nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN!=colFrame->GetWidthSource()))
mTableFrame->SetColumnWidth(colIndex, specifiedFixedColWidth);
else
mTableFrame->SetColumnWidth(colIndex, effectiveMaxColumnWidth);
if (gsDebug)
printf ("col %d, set col width to = %d\n", colIndex, mTableFrame->GetColumnWidth(colIndex));
if (PR_TRUE==hasColsAttribute)
{
minColWidthArray[colIndex] = minColWidth;
maxColWidthArray[colIndex] = maxColWidth;
}
if (gsDebug==PR_TRUE)
printf ("after col %d, minColWidth=%d effectiveMinColumnWidth=%d\n\teffectiveMaxColumnWidth = %d\n",
colIndex, minColWidth, effectiveMinColumnWidth, effectiveMaxColumnWidth);
}
// now, post-process the computed values based on the table attributes
// first, factor in max values of spanning cells proportionately.
// We can't do this above in the first loop, because we don't have enough information
// to determine the proportion each column gets from spanners.
if (nsnull!=spanList)
{
// we only want to do this if there are auto-cells involved
// or if we have columns that are provisionally fixed-width with colspans
PRInt32 numAutoColumns=0;
PRInt32 *autoColumns=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoColumns, autoColumns);
// for every column, handle spanning cells that impact that column
for (PRInt32 colIndex=0; colIndex<mNumCols; colIndex++)
{
if (gsDebug) printf("handling span for %d\n", colIndex);
PRInt32 spanCount = spanList->Count();
// go through the list backwards so we can delete easily
for (PRInt32 spanIndex=spanCount-1; 0<=spanIndex; spanIndex--)
{ // get each spanInfo struct and see if it impacts this column
SpanInfo *spanInfo = (SpanInfo *)(spanList->ElementAt(spanIndex));
// if the spanInfo is about a column before the current column, it effects
// the current column (otherwise it would have already been deleted.)
if (spanInfo->initialColIndex <= colIndex)
{
if (-1==spanInfo->effectiveMaxWidthOfSpannedCols)
{ // if we have not yet computed effectiveMaxWidthOfSpannedCols, do it now
// first, initialize the sums
spanInfo->effectiveMaxWidthOfSpannedCols=0;
spanInfo->effectiveMinWidthOfSpannedCols=0;
// then compute the sums
for (PRInt32 span=0; span<spanInfo->initialColSpan; span++)
{
nsTableColFrame *nextColFrame = mTableFrame->GetColFrame(colIndex+span);
if (nsnull==nextColFrame)
break;
spanInfo->effectiveMaxWidthOfSpannedCols += nextColFrame->GetEffectiveMaxColWidth();
spanInfo->effectiveMinWidthOfSpannedCols += nextColFrame->GetEffectiveMinColWidth();
}
if (gsDebug) printf("effective min total = %d, max total = %d\n",
spanInfo->effectiveMinWidthOfSpannedCols, spanInfo->effectiveMaxWidthOfSpannedCols);
}
nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
nscoord colMinWidth = colFrame->GetMinColWidth();
// compute the spanning cell's contribution to the column min width
// this is the "adjusted" column width, used in SetTableToMinWidth
nscoord spanCellMinWidth;
if (0!=spanInfo->effectiveMinWidthOfSpannedCols)
{
float percent = ((float)(colFrame->GetEffectiveMinColWidth())) /
((float)(spanInfo->effectiveMinWidthOfSpannedCols));
spanCellMinWidth = NSToCoordRound(((float)(spanInfo->cellMinWidth)) * percent);
if (gsDebug==PR_TRUE)
printf ("spanCellMinWidth portion = %d from percent=%f, cellMW=%d, effMinColW=%d, sum=%d\n",
spanCellMinWidth, percent, spanInfo->cellMinWidth, colFrame->GetEffectiveMinColWidth(),
spanInfo->effectiveMinWidthOfSpannedCols);
if (colMinWidth < spanCellMinWidth)
colFrame->SetAdjustedMinColWidth(spanCellMinWidth); // set the new min width for the col
}
else
{
spanCellMinWidth = spanInfo->cellMinWidth/spanInfo->initialColSpan;
if (colMinWidth < spanCellMinWidth)
{
colFrame->SetAdjustedMinColWidth(spanCellMinWidth);
}
}
// compute the spanning cell's contribution to the column max width
nscoord colMaxWidth = colFrame->GetMaxColWidth();
nscoord spanCellMaxWidth;
if (0!=spanInfo->effectiveMaxWidthOfSpannedCols)
{
float percent = ((float)(colFrame->GetEffectiveMaxColWidth())) /
((float)(spanInfo->effectiveMaxWidthOfSpannedCols));
spanCellMaxWidth = NSToCoordRound(((float)(spanInfo->cellDesiredWidth)) * percent);
if (gsDebug==PR_TRUE)
printf ("spanCellMaxWidth portion = %d with percent = %f from cellDW = %d, effMaxColW=%d and sum=%d\n",
spanCellMaxWidth, percent, spanInfo->cellDesiredWidth, colFrame->GetEffectiveMaxColWidth(),
spanInfo->effectiveMaxWidthOfSpannedCols);
if (colMaxWidth < spanCellMaxWidth)
{
// make sure we're at least as big as our min
spanCellMaxWidth = PR_MAX(spanCellMaxWidth, colMinWidth);
colFrame->SetMaxColWidth(spanCellMaxWidth); // set the new max width for the col
mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth); // set the column to the new desired max width
if (gsDebug==PR_TRUE)
{
printf ("for spanning cell into col %d with remaining span=%d, old max = %d, new max = %d\n",
colIndex, spanInfo->span, colMaxWidth, spanCellMaxWidth);
}
}
}
else
{
spanCellMaxWidth = spanInfo->cellDesiredWidth/spanInfo->initialColSpan;
nscoord minColWidth = colFrame->GetMinColWidth();
spanCellMaxWidth = PR_MAX(spanCellMaxWidth, minColWidth);
colFrame->SetMaxColWidth(spanCellMaxWidth);
mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, old max = %d, new max = %d\n",
colIndex, spanInfo->span, colMaxWidth, spanCellMaxWidth);
}
spanInfo->span--;
if (0==spanInfo->span)
{
spanList->RemoveElementAt(spanIndex);
delete spanInfo;
}
// begin code that respects column width attribute
/* the code below checks to see if the column has a width attribute (either it's own or a cell's)
* if so, if it fits within the constraints we computed above, we use it.
* why go through all that pain above, then? because the given width attribute might not
* be rational. So we need to act as if it doesn't exist and then fit it in if it makes sense.
* a shortcut that checks for a column width attribute and skips the above computations
* would not work without lots of extra computation that would lead you down the same path anyway.
*/
const nsStylePosition* colPosition;
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
// Get fixed column width if it has one
if (eStyleUnit_Coord==colPosition->mWidth.GetUnit())
{
if (nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN!=colFrame->GetWidthSource())
{
nscoord specifiedFixedColWidth = colPosition->mWidth.GetCoordValue();
specifiedFixedColWidth += (cellPadding*2);
if (specifiedFixedColWidth>=colFrame->GetEffectiveMinColWidth())
{
mTableFrame->SetColumnWidth(colIndex, specifiedFixedColWidth);
colFrame->SetMaxColWidth(specifiedFixedColWidth);
}
}
}
// end code that respects column width attribute
}
}
}
}
// now set the min and max table widths
SetMinAndMaxTableWidths();
// then, handle the COLS attribute (equal column widths)
// if there is a COLS attribute, fix up mMinTableWidth and mMaxTableWidth
if (PR_TRUE==hasColsAttribute)
{
if (gsDebug) printf("has COLS attribute = %d\n", mCols);
// for every effected column, subtract out its prior contribution and add back in the new value
PRInt32 numColsEffected = mNumCols;
if (NS_STYLE_TABLE_COLS_ALL!=mCols)
numColsEffected = mCols;
PRInt32 maxOfMinColWidths=0;
PRInt32 maxOfMaxColWidths=0;
PRInt32 effectedColIndex;
for (effectedColIndex=0; effectedColIndex<numColsEffected; effectedColIndex++)
{
if (maxOfMinColWidths < minColWidthArray[effectedColIndex])
maxOfMinColWidths = minColWidthArray[effectedColIndex];
if (maxOfMaxColWidths < maxColWidthArray[effectedColIndex])
maxOfMaxColWidths = maxColWidthArray[effectedColIndex];
}
for (effectedColIndex=0; effectedColIndex<numColsEffected; effectedColIndex++)
{
// subtract out the prior contributions of this column
// and add back in the adjusted value
if (NS_UNCONSTRAINEDSIZE!=mMinTableWidth)
{
mMinTableWidth -= minColWidthArray[effectedColIndex];
mMinTableWidth += maxOfMinColWidths;
}
if (NS_UNCONSTRAINEDSIZE!=mMaxTableWidth)
{
mMaxTableWidth -= maxColWidthArray[effectedColIndex];
mMaxTableWidth += maxOfMaxColWidths;
}
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(effectedColIndex, colFrame);
colFrame->SetMaxColWidth(maxOfMaxColWidths); // cache the new column max width (min width is uneffected)
colFrame->SetEffectiveMaxColWidth(maxOfMaxColWidths);
if (gsDebug) printf ("col %d now has max col width %d\n", effectedColIndex, maxOfMaxColWidths);
}
delete [] minColWidthArray;
delete [] maxColWidthArray;
}
if (nsnull!=colSpanList)
DistributeFixedSpace(colSpanList);
if (PR_TRUE==gsDebug)
printf ("%p: aMinTW=%d, aMaxTW=%d\n", mTableFrame, mMinTableWidth, mMaxTableWidth);
// clean up
if (nsnull!=spanList)
{
if (gsDebug) printf("BTLS::APCW...space leak, span list not empty\n");
delete spanList;
}
if (nsnull!=colSpanList)
{
PRInt32 colSpanListCount = colSpanList->Count();
for (PRInt32 i=0; i<colSpanListCount; i++)
{
ColSpanStruct * colSpanInfo = (ColSpanStruct *)(colSpanList->ElementAt(i));
if (nsnull!=colSpanInfo)
delete colSpanInfo;
}
delete colSpanList;
}
return PR_TRUE;
}
void BasicTableLayoutStrategy::SetMinAndMaxTableWidths()
{
if (gsDebug) printf("SetMinAndMaxTableWidths\n");
PRInt32 colIndex, rowIndex;
PRInt32 numRows = mTableFrame->GetRowCount();
nscoord colInset = mTableFrame->GetCellSpacing();
for (rowIndex = 0; rowIndex<numRows; rowIndex++)
{
if (gsDebug) printf(" row %d\n", rowIndex);
nscoord rowMinWidth = colInset;
nscoord rowMaxWidth = colInset;
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
if (gsDebug) printf(" col %d\n", colIndex);
nsTableCellFrame * cellFrame = mTableFrame->GetCellFrameAt(rowIndex, colIndex);
rowMinWidth += colInset;
rowMaxWidth += colInset;
if (nsnull==cellFrame)
{ // there is no cell in this row that corresponds to this column
if (gsDebug) printf(" col %d skipped because there is no cell\n", colIndex);
continue;
}
if (colIndex!=cellFrame->GetColIndex()) {
// For cells that span cols, we figured in the cell the first time we saw it
if (gsDebug) printf(" col %d skipped because it has colspan so we've already added it in\n", colIndex);
continue;
}
nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
nsSize cellMinSize = cellFrame->GetPass1MaxElementSize();
nscoord cellMinWidth = PR_MAX(cellMinSize.width, colFrame->GetEffectiveMinColWidth());
nsSize cellMaxSize = cellFrame->GetPass1DesiredSize();
nscoord cellMaxWidth = PR_MAX(cellMaxSize.width, colFrame->GetEffectiveMaxColWidth());
PRInt32 colSpan = mTableFrame->GetEffectiveColSpan(colIndex, cellFrame);
nscoord spanningCellMinWidth = (colSpan-1)*colInset;
if (gsDebug) printf(" cellMin=%d, cellMax=%d, spanningCellMin=%d\n",
cellMinWidth, cellMaxWidth, spanningCellMinWidth);
// spanning cells must be at least as wide as the columns they span, including the cell spacing spanned.
if (NS_UNCONSTRAINEDSIZE!=rowMinWidth)
rowMinWidth += PR_MAX(cellMinWidth, spanningCellMinWidth);
if (NS_UNCONSTRAINEDSIZE!=rowMaxWidth)
rowMaxWidth += PR_MAX(cellMaxWidth, spanningCellMinWidth);
if (gsDebug) printf(" rowMinWidth=%d, rowMaxWidth=%d\n", rowMinWidth, rowMaxWidth);
}
if (gsDebug) printf(" rowMinWidth=%d, rowMaxWidth=%d\n", rowMinWidth, rowMaxWidth);
// the largest row widths are the table widths
mMinTableWidth = PR_MAX(mMinTableWidth, rowMinWidth);
mMaxTableWidth = PR_MAX(mMaxTableWidth, rowMaxWidth);
if (gsDebug) printf(" mMinTableWidth=%d, mMaxTableWidth=%d\n", mMinTableWidth, mMaxTableWidth);
}
// verify max of min row widths vs. sum of adjusted column min widths. bigger one wins
nscoord sumOfAdjustedColMinWidths=colInset;
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(colIndex, colFrame);
sumOfAdjustedColMinWidths += colFrame->GetAdjustedMinColWidth() + colInset;
if (gsDebug) printf(" col %d has amcw=%d, cellspacing=%d, sum=%d\n",
colIndex, colFrame->GetAdjustedMinColWidth(), colInset, sumOfAdjustedColMinWidths);
}
if (gsDebug) printf(" sumOfAdjustedColMinWidths=%d\n", sumOfAdjustedColMinWidths);
/*
mMinTableWidth = PR_MAX(mMinTableWidth, sumOfAdjustedColMinWidths);
mMaxTableWidth = PR_MAX(mMinTableWidth, mMaxTableWidth);
*/
if (gsDebug) printf("end SetMinAndMaxTW: minTW=%d, maxTW=%d with DMCW=%d\n", mMinTableWidth, mMaxTableWidth, sumOfAdjustedColMinWidths);
}
// take the fixed space spanned by the columns in aColSpanList
// and distribute it proportionately (based on desired width)
void BasicTableLayoutStrategy::DistributeFixedSpace(nsVoidArray *aColSpanList)
{
nscoord excess = 0;
if (PR_TRUE==gsDebug) printf ("** DistributeFixedSpace:\n");
// for all fixed-width columns, determine the amount of the specified width each column spanned recieves
PRInt32 numSpanningCells = aColSpanList->Count();
for (PRInt32 nextSpanningCell=0; nextSpanningCell<numSpanningCells; nextSpanningCell++)
{ // proportionately distributed extra space, based on the column's fixed width
ColSpanStruct * colInfo = (ColSpanStruct *)aColSpanList->ElementAt(nextSpanningCell);
PRInt32 colIndex = colInfo->colIndex;
PRInt32 colSpan = colInfo->colSpan;
nscoord totalColWidth = colInfo->width;
// 1. get the sum of the effective widths of the columns in the span
nscoord totalEffectiveWidth=0;
nsTableColFrame * colFrame;
PRInt32 i;
for (i = 0; i<colSpan; i++)
{
mTableFrame->GetColumnFrame(colIndex+i, colFrame);
totalEffectiveWidth += colFrame->GetColWidthForComputation();
}
// 2. next, compute the proportion to be added to each column, and add it
for (i = 0; i<colSpan; i++)
{
mTableFrame->GetColumnFrame(colIndex+i, colFrame);
float percent;
percent = ((float)(colFrame->GetColWidthForComputation()))/((float)totalEffectiveWidth);
nscoord newColWidth = NSToCoordRound(((float)totalColWidth)*percent);
nscoord minColWidth = colFrame->GetEffectiveMinColWidth();
nscoord oldColWidth = mTableFrame->GetColumnWidth(colIndex+i);
if (newColWidth>minColWidth)
{
if (gsDebug==PR_TRUE)
{
printf(" assigning fixed col width for spanning cells: column %d set to %d\n",
colIndex+i, newColWidth);
printf(" minCW = %d oldCW = %d\n", minColWidth, oldColWidth);
}
mTableFrame->SetColumnWidth(colIndex+i, newColWidth);
colFrame->SetEffectiveMaxColWidth(newColWidth);
}
}
}
}
/* assign column widths to all non-fixed-width columns (adjusting fixed-width columns if absolutely necessary)
aAvailWidth is the amount of space left in the table to distribute after fixed-width columns are accounted for
aMaxWidth is the space the parent gave us (minus border & padding) to fit ourselves into
aTableIsAutoWidth is true if the table is auto-width, false if it is anything else (percent, fixed, etc)
*/
PRBool BasicTableLayoutStrategy::BalanceProportionalColumns(const nsHTMLReflowState& aReflowState,
nscoord aAvailWidth,
nscoord aMaxWidth,
nscoord aTableSpecifiedWidth,
PRBool aTableIsAutoWidth)
{
PRBool result = PR_TRUE;
nscoord actualMaxWidth; // the real target width, depends on if we're auto or specified width
if (PR_TRUE==aTableIsAutoWidth)
actualMaxWidth = aMaxWidth;
else
actualMaxWidth = PR_MIN(aMaxWidth, aTableSpecifiedWidth);
if (NS_UNCONSTRAINEDSIZE==aMaxWidth || NS_UNCONSTRAINEDSIZE==mMinTableWidth)
{ // the max width of the table fits comfortably in the available space
if (gsDebug) printf (" * table laying out in NS_UNCONSTRAINEDSIZE, calling BalanceColumnsTableFits\n");
nscoord bigSpace = gBigSpace;
bigSpace = PR_MAX(bigSpace, mMaxTableWidth);
result = BalanceColumnsTableFits(aReflowState, bigSpace,
bigSpace, aTableSpecifiedWidth, aTableIsAutoWidth);
}
else if (mMinTableWidth >= actualMaxWidth)
{ // the table doesn't fit in the available space
if (gsDebug) printf (" * table minTW does not fit, calling BalanceColumnsTableDoesNotFit\n");
result = BalanceColumnsTableDoesNotFit();
}
else if (mMaxTableWidth < actualMaxWidth)
{ // the max width of the table fits comfortably in the available space
if (gsDebug) printf (" * table desired size fits, calling BalanceColumnsTableFits\n");
result = BalanceColumnsTableFits(aReflowState, aAvailWidth,
aMaxWidth, aTableSpecifiedWidth, aTableIsAutoWidth);
}
else
{ // the table fits somewhere between its min and desired size
if (gsDebug) printf (" * table desired size does not fit, calling BalanceColumnsConstrained\n");
result = BalanceColumnsConstrained(aReflowState, aAvailWidth,
actualMaxWidth, aTableIsAutoWidth);
}
return result;
}
// the table doesn't fit, so squeeze every column down to its minimum
PRBool BasicTableLayoutStrategy::BalanceColumnsTableDoesNotFit()
{
PRBool result = PR_TRUE;
PRBool hasColsAttribute = (PRBool)(NS_STYLE_TABLE_COLS_NONE!=mCols);
PRInt32 *minColWidthArray = nsnull;
if (PR_TRUE==hasColsAttribute)
{
minColWidthArray = new PRInt32[mNumCols];
}
PRInt32 numRows = mTableFrame->GetRowCount();
for (PRInt32 colIndex = 0; colIndex<mNumCols; colIndex++)
{
nscoord minAdjustedColWidth = 0;
if (gsDebug==PR_TRUE) printf (" for col %d\n", colIndex);
nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
NS_ASSERTION(nsnull!=colFrame, "bad col frame");
// Get the columns's style
const nsStylePosition* colPosition;
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
const nsStyleTable* colTableStyle;
colFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)colTableStyle);
//if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
{
minAdjustedColWidth = colFrame->GetAdjustedMinColWidth();
mTableFrame->SetColumnWidth(colIndex, minAdjustedColWidth);
if (PR_TRUE==hasColsAttribute)
{
minColWidthArray[colIndex] = minAdjustedColWidth;
}
}
if (gsDebug==PR_TRUE)
printf (" 2: col %d, set to width = %d\n", colIndex, mTableFrame->GetColumnWidth(colIndex));
}
// now, post-process the computed values based on the table attributes
// if there is a COLS attribute, fix up mMinTableWidth and mMaxTableWidth
if (PR_TRUE==hasColsAttribute)
{ // for every effected column, subtract out its prior contribution and add back in the new value
PRInt32 numColsEffected = mNumCols;
if (NS_STYLE_TABLE_COLS_ALL!=mCols)
numColsEffected = mCols;
nscoord maxOfEffectedColWidths=0;
PRInt32 effectedColIndex;
// XXX need to fix this and all similar code if any fixed-width columns intersect COLS
for (effectedColIndex=0; effectedColIndex<numColsEffected; effectedColIndex++)
{
if (maxOfEffectedColWidths < minColWidthArray[effectedColIndex])
maxOfEffectedColWidths = minColWidthArray[effectedColIndex];
}
for (effectedColIndex=0; effectedColIndex<numColsEffected; effectedColIndex++)
{
// set each effected column to the size of the largest column in the group
mTableFrame->SetColumnWidth(effectedColIndex, maxOfEffectedColWidths);
if (PR_TRUE==gsDebug)
printf(" 2 (cols): setting %d to %d\n", effectedColIndex, maxOfEffectedColWidths);
}
// we're guaranteed here that minColWidthArray has been allocated, and that
// if we don't get here, it was never allocated
delete [] minColWidthArray;
}
return result;
}
/* the table fits in the given space. Set all columns to their desired width,
* and if we are not an auto-width table add extra space to fluff out the total width
*/
PRBool BasicTableLayoutStrategy::BalanceColumnsTableFits(const nsHTMLReflowState& aReflowState,
nscoord aAvailWidth,
nscoord aMaxWidth,
nscoord aTableSpecifiedWidth,
PRBool aTableIsAutoWidth)
{
PRBool result = PR_TRUE;
nscoord tableWidth=0; // the width of the table as a result of setting column widths
nscoord widthOfFixedTableColumns=0; // the sum of the width of all fixed-width columns plus margins
// tableWidth - widthOfFixedTableColumns is the width of columns computed in this method
PRInt32 totalSlices=0; // the total number of slices the proportional-width columns request
nsVoidArray *proportionalColumnsList=nsnull; // a list of the columns that are proportional-width
nsVoidArray *spanList=nsnull; // a list of the cells that span columns
PRInt32 numRows = mTableFrame->GetRowCount();
nscoord colInset = mTableFrame->GetCellSpacing();
PRInt32 colIndex;
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
if (gsDebug==PR_TRUE) printf ("for col %d\n", colIndex);
// Get column information
nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
NS_ASSERTION(nsnull!=colFrame, "bad col frame");
PRInt32 minColWidth = colFrame->GetMinColWidth();
PRInt32 maxColWidth = 0;
PRInt32 rowIndex;
PRInt32 firstRowIndex = -1;
const nsStylePosition* colPosition;
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
const nsStyleTable* colTableStyle;
colFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)colTableStyle);
// first, deal with any cells that span into this column from a pervious column
// go through the list backwards so we can delete easily
if (nsnull!=spanList)
{
PRInt32 spanCount = spanList->Count();
// go through the list backwards so we can delete easily
for (PRInt32 spanIndex=spanCount-1; 0<=spanIndex; spanIndex--)
{
SpanInfo *spanInfo = (SpanInfo *)(spanList->ElementAt(spanIndex));
if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
{
// compute the spanning cell's contribution to the column min width
nscoord spanCellMinWidth;
PRBool needsExtraMinWidth = PR_FALSE;
/*
if (spanInfo->effectiveMinWidthOfSpannedCols<spanInfo->cellMinWidth)
needsExtraMinWidth = PR_TRUE;
*/
if (PR_TRUE==needsExtraMinWidth)
{
if (0!=spanInfo->effectiveMinWidthOfSpannedCols)
{
spanCellMinWidth = (spanInfo->cellMinWidth * colFrame->GetEffectiveMinColWidth()) /
(spanInfo->effectiveMinWidthOfSpannedCols);
if (gsDebug==PR_TRUE)
printf (" spanlist min: %d of %d\n", spanCellMinWidth, spanInfo->effectiveMaxWidthOfSpannedCols);
if (minColWidth < spanCellMinWidth)
{
minColWidth = spanCellMinWidth; // set the new min width for the col
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, new min = %d\n",
colIndex, spanInfo->span, minColWidth);
// if the new min width is greater than the desired width, bump up the desired width
maxColWidth = PR_MAX(maxColWidth, minColWidth);
if (gsDebug)
printf (" and maxColWidth = %d\n", maxColWidth);
}
}
else
{
spanCellMinWidth = spanInfo->cellMinWidth/spanInfo->initialColSpan;
if (minColWidth < spanCellMinWidth)
{
minColWidth = spanCellMinWidth;
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, new min = %d\n",
colIndex, spanInfo->span, minColWidth);
// if the new min width is greater than the desired width, bump up the desired width
maxColWidth = PR_MAX(maxColWidth, minColWidth);
if (gsDebug)
printf (" and maxColWidth = %d\n", maxColWidth);
}
}
}
// compute the spanning cell's contribution to the column max width
nscoord spanCellMaxWidth;
if (0!=spanInfo->effectiveMaxWidthOfSpannedCols)
{
spanCellMaxWidth = (spanInfo->cellDesiredWidth * colFrame->GetEffectiveMaxColWidth()) /
(spanInfo->effectiveMaxWidthOfSpannedCols);
if (gsDebug==PR_TRUE)
printf (" spanlist max: %d of %d\n", spanCellMaxWidth, spanInfo->effectiveMaxWidthOfSpannedCols);
if (maxColWidth < spanCellMaxWidth)
{
spanCellMaxWidth = PR_MAX(spanCellMaxWidth, minColWidth);
maxColWidth = spanCellMaxWidth; // set the new max width for the col
mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth); // set the column to the new desired max width
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, \
new max = %d\n",
colIndex, spanInfo->span, maxColWidth);
}
}
else
{
spanCellMaxWidth = spanInfo->cellDesiredWidth/spanInfo->initialColSpan;
maxColWidth = spanCellMaxWidth;
mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, \
new max = %d\n",
colIndex, spanInfo->span, maxColWidth);
}
}
spanInfo->span--;
if (0==spanInfo->span)
{
spanList->RemoveElementAt(spanIndex);
delete spanInfo;
}
}
}
// second, process non-fixed-width columns
if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
{
for (rowIndex = 0; rowIndex<numRows; rowIndex++)
{ // this col has proportional width, so determine its width requirements
nsTableCellFrame * cellFrame = mTableFrame->GetCellFrameAt(rowIndex, colIndex);
if (nsnull==cellFrame)
{ // there is no cell in this row that corresponds to this column
continue;
}
if (-1==firstRowIndex)
firstRowIndex = rowIndex;
if (rowIndex!=cellFrame->GetRowIndex()) {
// For cells that span rows, we only figure it in once
NS_ASSERTION(1 != cellFrame->GetRowSpan(), "row index does not match row span"); // sanity check
continue;
}
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
continue;
}
PRInt32 colSpan = mTableFrame->GetEffectiveColSpan(colIndex, cellFrame);
nsSize cellMinSize = cellFrame->GetPass1MaxElementSize();
nsSize cellDesiredSize = cellFrame->GetPass1DesiredSize();
nscoord cellMinWidth=colFrame->GetMinColWidth();
nscoord cellDesiredWidth=colFrame->GetMaxColWidth();
// then get the desired size info factoring in the cell style attributes
if (1==colSpan)
{
cellMinWidth = cellMinSize.width;
cellDesiredWidth = cellDesiredSize.width;
nscoord specifiedCellWidth=-1;
const nsStylePosition* cellPosition;
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
if (eStyleUnit_Percent==cellPosition->mWidth.GetUnit())
{
if (PR_FALSE==aTableIsAutoWidth)
{
float percent = cellPosition->mWidth.GetPercentValue();
specifiedCellWidth = (PRInt32)(aTableSpecifiedWidth*percent);
if (gsDebug) printf("specified percent width %f of %d = %d\n",
percent, aTableSpecifiedWidth, specifiedCellWidth);
}
// otherwise we need to post-process, set to max for now
// do we want to set specifiedCellWidth off of aAvailWidth? aMaxWidth? XXX
}
if (-1!=specifiedCellWidth)
{
if (specifiedCellWidth>cellMinWidth)
{
if (gsDebug) printf("setting cellDesiredWidth from %d to %d\n",
cellDesiredWidth, specifiedCellWidth);
cellDesiredWidth = specifiedCellWidth;
}
}
}
else
{ // colSpan>1, get the proportion for this column
// then get the desired size info factoring in the cell style attributes
nscoord effectiveMaxWidthOfSpannedCols = colFrame->GetEffectiveMaxColWidth();
nscoord effectiveMinWidthOfSpannedCols = colFrame->GetEffectiveMinColWidth();
for (PRInt32 span=1; span<colSpan; span++)
{
nsTableColFrame *nextColFrame = mTableFrame->GetColFrame(colIndex+span);
if (nsnull==nextColFrame)
break;
effectiveMaxWidthOfSpannedCols += nextColFrame->GetEffectiveMaxColWidth();
effectiveMinWidthOfSpannedCols += nextColFrame->GetEffectiveMinColWidth();
}
nscoord specifiedCellWidth=-1;
const nsStylePosition* cellPosition;
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
if (eStyleUnit_Percent==cellPosition->mWidth.GetUnit())
{ //XXX what if table is auto width?
float percent = cellPosition->mWidth.GetPercentValue();
specifiedCellWidth = (PRInt32)(aTableSpecifiedWidth*percent);
if (gsDebug) printf("specified percent width %f of %d = %d\n",
percent, aTableSpecifiedWidth, specifiedCellWidth);
}
if (-1!=specifiedCellWidth)
{
float percentForThisCol = (float)(cellDesiredSize.width * colFrame->GetEffectiveMaxColWidth()) /
(float)effectiveMaxWidthOfSpannedCols;
nscoord cellWidthForThisCol = (nscoord)(specifiedCellWidth * percentForThisCol);
if (cellWidthForThisCol>cellMinWidth)
{
if (gsDebug) printf("setting cellDesiredWidth from %d to %d\n",
cellDesiredWidth, cellWidthForThisCol);
cellDesiredWidth = cellWidthForThisCol;
}
}
// otherwise it's already been factored in.
// now, if this column holds the cell, create a spanInfo struct for the cell
// so subsequent columns can take a proportion of this cell's space into account
if (cellFrame->GetColIndex()==colIndex)
{ // add this cell to span list iff we are currently processing the column the cell starts in
SpanInfo *spanInfo = new SpanInfo(colIndex, colSpan-1, cellMinSize.width, cellDesiredSize.width);
spanInfo->effectiveMaxWidthOfSpannedCols = effectiveMaxWidthOfSpannedCols;
spanInfo->effectiveMinWidthOfSpannedCols = effectiveMinWidthOfSpannedCols;
if (nsnull==spanList)
spanList = new nsVoidArray();
spanList->AppendElement(spanInfo);
}
}
if (PR_TRUE==gsDebug)
printf("factoring in cell %d with colSpan=%d\n factoring in min=%d and desired=%d\n",
rowIndex, colSpan, cellMinWidth, cellDesiredWidth);
if (maxColWidth < cellDesiredWidth)
maxColWidth = cellDesiredWidth;
if (gsDebug==PR_TRUE)
printf (" after cell %d, minColWidth=%d maxColWidth=%d effColWidth[%d]=%d\n",
rowIndex, minColWidth, maxColWidth,
colIndex, colFrame->GetEffectiveMaxColWidth());
} // end looping through cells in the column
if (gsDebug==PR_TRUE)
{
printf (" for determining width of col %d %s:\n",
colIndex, !IsFixedWidth(colPosition, colTableStyle)? "(P)":"(A)");
printf (" minColWidth = %d and maxColWidth = %d\n", minColWidth, maxColWidth);
printf (" aAvailWidth = %d\n", aAvailWidth);
}
// Get column width if it has one
nscoord specifiedProportionColumnWidth = -1;
float specifiedPercentageColWidth = -1.0f;
nscoord specifiedFixedColumnWidth = -1;
PRBool isAutoWidth = PR_FALSE;
switch (colPosition->mWidth.GetUnit()) {
case eStyleUnit_Percent:
specifiedPercentageColWidth = colPosition->mWidth.GetPercentValue();
if (gsDebug) printf("column %d has specified percent width = %f\n", colIndex, specifiedPercentageColWidth);
break;
case eStyleUnit_Proportional:
specifiedProportionColumnWidth = colPosition->mWidth.GetIntValue();
if (gsDebug) printf("column %d has specified percent width = %d\n", colIndex, specifiedProportionColumnWidth);
break;
case eStyleUnit_Auto:
isAutoWidth = PR_TRUE;
break;
default:
break;
}
/* set the column width, knowing that the table fits in the available space */
if (0==specifiedProportionColumnWidth || 0.0==specifiedPercentageColWidth)
{ // col width is specified to be the minimum
mTableFrame->SetColumnWidth(colIndex, minColWidth);
if (gsDebug==PR_TRUE)
printf (" 3 min: col %d set to min width = %d because style set proportionalWidth=0\n",
colIndex, mTableFrame->GetColumnWidth(colIndex));
}
else if ((PR_TRUE==isAutoWidth) ||
((PR_TRUE==aTableIsAutoWidth) && (-1==specifiedProportionColumnWidth)))
{ // col width is determined by the cells' content,
// so give each remaining column it's desired width (because we know we fit.)
// if there is width left over, we'll factor that in after this loop is complete
// the OR clause is because we fix up percentage widths as a post-process
// in auto-width tables
mTableFrame->SetColumnWidth(colIndex, maxColWidth);
if (gsDebug==PR_TRUE)
printf (" 3 auto: col %d with availWidth %d, set to width = %d\n",
colIndex, aAvailWidth, mTableFrame->GetColumnWidth(colIndex));
}
else if (-1!=specifiedProportionColumnWidth)
{ // we need to save these and do them after all other columns have been calculated
/* the calculation will be:
sum up n, the total number of slices for the columns with proportional width
compute the table "required" width, fixed-width + percentage-width +
the sum of the proportional column's max widths (especially because in this routine I know the table fits)
compute the remaining width: the required width - the used width (fixed + percentage)
compute the width per slice
set the width of each proportional-width column to it's number of slices * width per slice
*/
mTableFrame->SetColumnWidth(colIndex, 0); // set the column width to 0, since it isn't computed yet
if (nsnull==proportionalColumnsList)
proportionalColumnsList = new nsVoidArray();
ProportionalColumnLayoutStruct * info =
new ProportionalColumnLayoutStruct(colIndex, minColWidth,
maxColWidth, specifiedProportionColumnWidth);
proportionalColumnsList->AppendElement(info);
totalSlices += specifiedProportionColumnWidth; // keep track of the total number of proportions
if (gsDebug==PR_TRUE)
printf (" 3 proportional: col %d with availWidth %d, gets %d slices with %d slices so far.\n",
colIndex, aAvailWidth, specifiedProportionColumnWidth, totalSlices);
}
else
{ // give the column a percentage of the remaining space
PRInt32 percentage = -1;
if (NS_UNCONSTRAINEDSIZE==aAvailWidth)
{ // since the "remaining space" is infinite, give the column it's max requested size
mTableFrame->SetColumnWidth(colIndex, maxColWidth);
}
else
{
if (-1.0f != specifiedPercentageColWidth)
{
percentage = (PRInt32)(specifiedPercentageColWidth*100.0f); // TODO: rounding errors?
// base the % on the total specified fixed width of the table
mTableFrame->SetColumnWidth(colIndex, (percentage*aTableSpecifiedWidth)/100);
if (gsDebug==PR_TRUE)
printf (" 3 percent specified: col %d given %d percent of aTableSpecifiedWidth %d, set to width = %d\n",
colIndex, percentage, aTableSpecifiedWidth, mTableFrame->GetColumnWidth(colIndex));
}
if (-1==percentage)
{
percentage = 100/mNumCols;
// base the % on the remaining available width
mTableFrame->SetColumnWidth(colIndex, (percentage*aAvailWidth)/100);
if (gsDebug==PR_TRUE)
printf (" 3 percent default: col %d given %d percent of aAvailWidth %d, set to width = %d\n",
colIndex, percentage, aAvailWidth, mTableFrame->GetColumnWidth(colIndex));
}
// if the column was computed to be too small, enlarge the column
if (mTableFrame->GetColumnWidth(colIndex) <= minColWidth)
{
mTableFrame->SetColumnWidth(colIndex, minColWidth);
if (PR_TRUE==gsDebug) printf(" enlarging column to it's minimum = %d\n", minColWidth);
}
}
}
}
else
{ // need to maintain this so we know how much we have left over at the end
nscoord maxEffectiveColWidth = colFrame->GetEffectiveMaxColWidth();
mTableFrame->SetColumnWidth(colIndex, maxEffectiveColWidth);
widthOfFixedTableColumns += maxEffectiveColWidth + colInset;
}
tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset;
}
tableWidth += colInset;
/* --- post-process if necessary --- */
// XXX the following implementation does not account for borders, cell spacing, cell padding
// first, assign column widths in auto-width tables
PRInt32 numPercentColumns=0;
PRInt32 *percentColumns=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Percent, numPercentColumns, percentColumns);
if ((PR_TRUE==aTableIsAutoWidth) && (0!=numPercentColumns))
{
// variables common to lots of code in this block
nscoord colWidth;
float percent;
const nsStylePosition* colPosition;
nsTableColFrame *colFrame;
if (numPercentColumns!=mNumCols)
{
if (PR_TRUE==gsDebug)
printf("assigning widths to percent colums in auto-width table\n");
nscoord widthOfPercentCells=0; // the total width of those percent-width cells that have been given a width
nscoord widthOfOtherCells=0; // the total width of those non-percent-width cells that have been given a width
float sumOfPercentColumns=0.0f; // the total of the percent widths
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
// every column contributes to either widthOfOtherCells or widthOfPercentCells, but not both
if (PR_TRUE==IsColumnInList(colIndex, percentColumns, numPercentColumns))
{
colFrame = mTableFrame->GetColFrame(colIndex);
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
percent = colPosition->mWidth.GetPercentValue(); // we know this will work
sumOfPercentColumns += percent;
if (sumOfPercentColumns>1.0f)
sumOfPercentColumns=1.0f; // values greater than 100% are meaningless
widthOfPercentCells += mTableFrame->GetColumnWidth(colIndex);
}
else
{
widthOfOtherCells += mTableFrame->GetColumnWidth(colIndex);
}
}
if (0==widthOfOtherCells)
widthOfOtherCells=aMaxWidth;
if (PR_TRUE==gsDebug)
printf(" widthOfOtherCells=%d widthOfPercentCells = %d sumOfPercentColumns=%f\n",
widthOfOtherCells, widthOfPercentCells, sumOfPercentColumns);
float remainingPercent = 1.0f - sumOfPercentColumns;
nscoord newTableWidth; // the table width after all cells have been resized
if (1.0f == sumOfPercentColumns) // this definately seems like a QUIRK!
newTableWidth = aMaxWidth;
else
{ // the newTableWidth is the larger of the calculation from the percent cells and non-percent cells
nscoord percentWidth = (nscoord)((1.0f/sumOfPercentColumns)*((float)(widthOfPercentCells)));
nscoord otherWidth = (nscoord)((1.0f/remainingPercent)*((float)(widthOfOtherCells)));
if (PR_TRUE==gsDebug)
printf(" percentWidth=%d otherWidth=%d\n", percentWidth, otherWidth);
newTableWidth = PR_MAX(percentWidth, otherWidth); // the table width is the larger of the two computations
newTableWidth = PR_MIN(newTableWidth, aMaxWidth); // an auto-width table can't normally be wider than it's parent
newTableWidth = PR_MIN(newTableWidth, mMaxTableWidth);// an auto-width table can't normally be wider than it's own computed max width
}
nscoord excess = newTableWidth-mFixedTableWidth; // the amount of new space that needs to be
// accounted for in the non-fixed columns
if (PR_TRUE==gsDebug)
printf(" newTableWidth=%d \n", newTableWidth);
PRInt32 i; // just a counter
for (i=0; i<numPercentColumns; i++)
{
colIndex = percentColumns[i];
colFrame = mTableFrame->GetColFrame(colIndex);
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
percent = colPosition->mWidth.GetPercentValue(); // we know this will work
colWidth = (nscoord)((percent)*((float)newTableWidth));
nscoord minColWidth = colFrame->GetEffectiveMinColWidth();
colWidth = PR_MAX(colWidth, minColWidth);
mTableFrame->SetColumnWidth(colIndex, colWidth);
if (PR_TRUE==gsDebug)
printf(" col %d with percentwidth=%f set to %d\n",
colIndex, percent, colWidth);
excess -= colWidth;
}
if (0<excess)
{
PRInt32 numAutoColumns=0;
PRInt32 *autoColumns=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoColumns, autoColumns);
if (PR_TRUE==gsDebug)
printf(" excess=%d with %d autoColumns\n", excess, numAutoColumns);
if (0<numAutoColumns)
{
nscoord excessPerColumn = excess/numAutoColumns;
for (i=0; i<numAutoColumns; i++)
{
colIndex = autoColumns[i];
nscoord newWidth = PR_MAX(excessPerColumn, mTableFrame->GetColumnWidth(colIndex));
if (PR_TRUE==gsDebug)
printf(" col %d was %d set to \n",
colIndex, mTableFrame->GetColumnWidth(colIndex), newWidth);
mTableFrame->SetColumnWidth(colIndex, newWidth);
excess -= excessPerColumn;
}
// handle division underflow
if (0<excess)
{
if (PR_TRUE==gsDebug)
printf(" after first pass through auto-width columns, excess=%d\n", excess);
for (i=0; i<numAutoColumns; i++)
{
colIndex = autoColumns[i];
nscoord newWidth = 1 + mTableFrame->GetColumnWidth(colIndex);
mTableFrame->SetColumnWidth(colIndex, newWidth);
}
}
}
}
}
else
{ // all columns have a percent width. Each already has its desired width.
// find the smallest percentage and base the widths of the others off that
PRInt32 indexOfSmallest;
nscoord colWidthOfSmallest;
float smallestPercent=2.0f; // an illegal large number
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
colFrame = mTableFrame->GetColFrame(colIndex);
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
percent = colPosition->mWidth.GetPercentValue(); // we know this will work
if (percent < smallestPercent)
{
smallestPercent=percent;
indexOfSmallest=colIndex;
colWidthOfSmallest = mTableFrame->GetColumnWidth(colIndex);
}
else if (percent==smallestPercent)
{ // the largest desired size among equal-percent columns wins
nscoord colWidth = mTableFrame->GetColumnWidth(colIndex);
if (colWidth>colWidthOfSmallest)
{
indexOfSmallest = colIndex;
colWidthOfSmallest = colWidth;
}
}
}
// now that we know which column to base the other columns' widths off of, do it!
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
colFrame = mTableFrame->GetColFrame(colIndex);
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
percent = colPosition->mWidth.GetPercentValue(); // we know this will work
if (percent==smallestPercent)
{ // set col width to the max width of all columns that shared the smallest percent-width
mTableFrame->SetColumnWidth(colIndex, colWidthOfSmallest);
}
else
{
colWidth = (nscoord)((percent/smallestPercent)*((float)colWidthOfSmallest));
nscoord minColWidth = colFrame->GetEffectiveMinColWidth();
colWidth = PR_MAX(colWidth, minColWidth);
mTableFrame->SetColumnWidth(colIndex, colWidth);
}
if (PR_TRUE==gsDebug)
printf(" col %d with percentwidth=%f set to %d\n",
colIndex, percent, colWidth);
}
}
}
// second, assign column widths to proportional-width columns
if (nsnull!=proportionalColumnsList)
{
// first, figure out the amount of space per slice
nscoord maxWidthForTable = (0!=aTableSpecifiedWidth) ? aTableSpecifiedWidth : aMaxWidth;
if (NS_UNCONSTRAINEDSIZE!=maxWidthForTable)
{
nscoord widthRemaining = maxWidthForTable - tableWidth;
nscoord widthPerSlice = widthRemaining/totalSlices;
PRInt32 numSpecifiedProportionalColumns = proportionalColumnsList->Count();
for (PRInt32 i=0; i<numSpecifiedProportionalColumns; i++)
{ // for every proportionally-sized column, set the col width to the computed width
ProportionalColumnLayoutStruct * info =
(ProportionalColumnLayoutStruct *)(proportionalColumnsList->ElementAt(i));
// verify that the computed width is at least the minimum width
nscoord computedColWidth = info->mProportion*widthPerSlice;
// compute the requested proportional width
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(info->mColIndex, colFrame);
nscoord minColWidth = colFrame->GetMinColWidth();
computedColWidth = PR_MAX(computedColWidth, minColWidth);
mTableFrame->SetColumnWidth(info->mColIndex, computedColWidth);
if (gsDebug==PR_TRUE)
printf (" 3 proportional step 2: col %d given %d proportion of remaining space %d, set to width = %d\n",
info->mColIndex, info->mProportion, widthRemaining, computedColWidth);
tableWidth += computedColWidth;
delete info;
}
}
else
{ // we're in an unconstrained situation, so give each column the max of the max column widths
PRInt32 numSpecifiedProportionalColumns = proportionalColumnsList->Count();
PRInt32 maxOfMaxColWidths = 0;
PRInt32 i;
for (i=0; i<numSpecifiedProportionalColumns; i++)
{
ProportionalColumnLayoutStruct * info =
(ProportionalColumnLayoutStruct *)(proportionalColumnsList->ElementAt(i));
if (maxOfMaxColWidths < info->mMaxColWidth)
maxOfMaxColWidths = info->mMaxColWidth;
}
for (i=0; i<numSpecifiedProportionalColumns; i++)
{
ProportionalColumnLayoutStruct * info =
(ProportionalColumnLayoutStruct *)(proportionalColumnsList->ElementAt(i));
mTableFrame->SetColumnWidth(info->mColIndex, maxOfMaxColWidths);
if (gsDebug==PR_TRUE)
printf (" 3 proportional step 2 (unconstrained!): col %d set to width = %d\n",
info->mColIndex, maxOfMaxColWidths);
tableWidth += maxOfMaxColWidths;
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(info->mColIndex, colFrame);
colFrame->SetEffectiveMaxColWidth(maxOfMaxColWidths);
delete info;
}
}
delete proportionalColumnsList;
}
// next, account for table width constraints
// if the caption min width is wider than than the table, expand the table
nscoord captionMinWidth=mTableFrame->GetMinCaptionWidth();
if (captionMinWidth>tableWidth)
{
DistributeExcessSpace(captionMinWidth, tableWidth, widthOfFixedTableColumns);
}
else
{ // else, if the caption min width is not an issue...
/* if the specified width of the table is greater than the table's computed width, expand the
* table's computed width to match the specified width, giving the extra space to proportionately-sized
* columns if possible. */
if ((PR_FALSE==aTableIsAutoWidth) && (aAvailWidth > (tableWidth-widthOfFixedTableColumns)) &&
(gBigSpace!=aAvailWidth))
{
DistributeExcessSpace(aAvailWidth, tableWidth, widthOfFixedTableColumns);
}
// IFF the table is NOT (auto-width && all columns have fixed width)
PRInt32 numFixedColumns=0;
PRInt32 *fixedColumns=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Coord, numFixedColumns, fixedColumns);
if (!((PR_TRUE==aTableIsAutoWidth) && (numFixedColumns==mNumCols)))
{
nscoord computedWidth=0;
for (PRInt32 i=0; i<mNumCols; i++) {
computedWidth += mTableFrame->GetColumnWidth(i) + colInset;
}
if (computedWidth>aMaxWidth) {
AdjustTableThatIsTooWide(computedWidth, aMaxWidth, PR_FALSE);
}
}
}
// cleanup
if (nsnull!=spanList)
{
if (gsDebug) printf("BTLS::BCTFits...space leak, span list not empty");
delete spanList;
}
return result;
}
// take the extra space in the table and distribute it proportionately (based on desired width)
// give extra space to auto-width cells first, or if there are none to all cells
void BasicTableLayoutStrategy::DistributeExcessSpace(nscoord aAvailWidth,
nscoord aTableWidth,
nscoord aWidthOfFixedTableColumns)
{
nscoord excess = 0;
if (PR_TRUE==gsDebug) printf ("DistributeExcessSpace: fixed width %d > computed table width %d, woftc=%d\n",
aAvailWidth, aTableWidth, aWidthOfFixedTableColumns);
nscoord widthMinusFixedColumns = aTableWidth - aWidthOfFixedTableColumns;
// if there are auto-sized columns, give them the extra space
// the trick here is to do the math excluding non-auto width columns
PRInt32 numAutoColumns=0;
PRInt32 *autoColumns=nsnull;
GetColumnsThatActLikeAutoWidth(numAutoColumns, autoColumns); // allocates autoColumns, be sure to delete it
if (0!=numAutoColumns)
{
// there's at least one auto-width column, so give it (them) the extra space
// proportionately distributed extra space, based on the column's desired size
nscoord totalEffectiveWidthOfAutoColumns = 0;
// 1. first, get the total width of the auto columns
PRInt32 i;
for (i = 0; i<numAutoColumns; i++)
{
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(autoColumns[i], colFrame);
nscoord effectiveColWidth = colFrame->GetEffectiveMaxColWidth();
if (0!=effectiveColWidth)
totalEffectiveWidthOfAutoColumns += effectiveColWidth;
else
totalEffectiveWidthOfAutoColumns += mTableFrame->GetColumnWidth(autoColumns[i]);
}
// excess is the amount of space that was available minus the computed available width
// XXX shouldn't it just be aMaxWidth - aTableWidth???
if (gsDebug==PR_TRUE)
printf(" aAvailWidth specified as %d, expanding columns by excess = %d\n",
aAvailWidth, excess);
excess = aAvailWidth - widthMinusFixedColumns;
// 2. next, compute the proportion to be added to each column, and add it
nscoord totalAdded=0;
for (i = 0; i<numAutoColumns; i++)
{
PRInt32 colIndex = autoColumns[i];
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(colIndex, colFrame);
nscoord oldColWidth = mTableFrame->GetColumnWidth(colIndex);
float percent;
if (0!=totalEffectiveWidthOfAutoColumns)
percent = ((float)(colFrame->GetEffectiveMaxColWidth()))/((float)totalEffectiveWidthOfAutoColumns);
else
percent = ((float)1)/((float)numAutoColumns);
nscoord excessForThisColumn = (nscoord)(excess*percent);
totalAdded += excessForThisColumn;
nscoord colWidth = excessForThisColumn+oldColWidth;
if (gsDebug==PR_TRUE)
printf(" distribute excess to auto columns: column %d was %d, now set to %d\n",
colIndex, oldColWidth, colWidth);
mTableFrame->SetColumnWidth(colIndex, colWidth);
}
if (PR_TRUE==gsDebug)
printf("lost a few twips due to rounding errors: excess=%d, totalAdded=%d\n", excess, totalAdded);
}
// otherwise, distribute the space between all the columns
// (they must be all fixed and percentage-width columns, or we would have gone into the block above)
else
{
excess = aAvailWidth - widthMinusFixedColumns;
if (gsDebug==PR_TRUE)
printf(" aAvailWidth specified as %d, expanding columns by excess = %d\n",
aAvailWidth, excess);
nscoord totalAdded=0;
for (PRInt32 colIndex = 0; colIndex<mNumCols; colIndex++)
{
nscoord oldColWidth=0;
if (0==oldColWidth)
oldColWidth = mTableFrame->GetColumnWidth(colIndex);
float percent;
if (0!=aTableWidth)
percent = (float)oldColWidth/(float)aTableWidth;
else
percent = (float)1/(float)mNumCols;
nscoord excessForThisColumn = (nscoord)(NSToCoordRound(excess*percent));
totalAdded += excessForThisColumn;
nscoord colWidth = excessForThisColumn+oldColWidth;
if (gsDebug==PR_TRUE)
printf(" distribute excess: column %d was %d, now %d from %=%f\n",
colIndex, oldColWidth, colWidth, percent);
mTableFrame->SetColumnWidth(colIndex, colWidth);
}
if (PR_TRUE==gsDebug)
printf("lost a few twips due to rounding errors: excess=%d, totalAdded=%d\n", excess, totalAdded);
}
if (nsnull!=autoColumns)
delete [] autoColumns;
}
/* assign columns widths for a table whose max size doesn't fit in the available space
*/
PRBool BasicTableLayoutStrategy::BalanceColumnsConstrained( const nsHTMLReflowState& aReflowState,
nscoord aAvailWidth,
nscoord aMaxWidth,
PRBool aTableIsAutoWidth)
{
#ifdef NS_DEBUG
nsIFrame *tablePIF=nsnull;
mTableFrame->GetPrevInFlow(tablePIF);
NS_ASSERTION(nsnull==tablePIF, "never ever call me on a continuing frame!");
#endif
PRBool result = PR_TRUE;
PRInt32 maxOfAllMinColWidths = 0; // for the case where we have equal column widths, this is the smallest a column can be
nscoord tableWidth=0; // the width of the table as a result of setting column widths
PRInt32 totalSlices=0; // the total number of slices the proportional-width columns request
nsVoidArray *proportionalColumnsList=nsnull; // a list of the columns that are proportional-width
PRBool equalWidthColumns = PR_TRUE; // remember if we're in the special case where all
// proportional-width columns are equal (if any are anything other than 1)
PRBool atLeastOneAutoWidthColumn = PR_FALSE; // true if at least one column is auto-width, requiring us to post-process
nsVoidArray *spanList=nsnull; // a list of the cells that span columns
PRInt32 numRows = mTableFrame->GetRowCount();
nscoord colInset = mTableFrame->GetCellSpacing();
for (PRInt32 colIndex = 0; colIndex<mNumCols; colIndex++)
{
if (gsDebug==PR_TRUE) printf (" for col %d\n", colIndex);
nscoord minColWidth = 0;
nscoord maxColWidth = 0;
// Get column information
nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
NS_ASSERTION(nsnull!=colFrame, "bad col frame");
// Get the columns's style and margins
PRInt32 rowIndex;
PRInt32 firstRowIndex = -1;
const nsStylePosition* colPosition;
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
const nsStyleTable* colTableStyle;
colFrame->GetStyleData(eStyleStruct_Table, (const nsStyleStruct*&)colTableStyle);
// first, deal with any cells that span into this column from a pervious column
// go through the list backwards so we can delete easily
if (nsnull!=spanList)
{
PRInt32 spanCount = spanList->Count();
// go through the list backwards so we can delete easily
for (PRInt32 spanIndex=spanCount-1; 0<=spanIndex; spanIndex--)
{
SpanInfo *spanInfo = (SpanInfo *)(spanList->ElementAt(spanIndex));
if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
{
// compute the spanning cell's contribution to the column min width
nscoord spanCellMinWidth;
PRBool needsExtraMinWidth = PR_FALSE;
/*
if (spanInfo->effectiveMinWidthOfSpannedCols<spanInfo->cellMinWidth)
needsExtraMinWidth = PR_TRUE;
*/
if (PR_TRUE==needsExtraMinWidth)
{
if (0!=spanInfo->effectiveMinWidthOfSpannedCols)
{
spanCellMinWidth = (spanInfo->cellMinWidth * colFrame->GetEffectiveMinColWidth()) /
(spanInfo->effectiveMinWidthOfSpannedCols);
if (gsDebug==PR_TRUE)
printf (" spanlist min: %d of %d\n", spanCellMinWidth, spanInfo->effectiveMaxWidthOfSpannedCols);
if (minColWidth < spanCellMinWidth)
{
minColWidth = spanCellMinWidth; // set the new min width for the col
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, new min = %d\n",
colIndex, spanInfo->span, minColWidth);
maxColWidth = PR_MAX(maxColWidth, minColWidth);
if (gsDebug)
printf (" maxColWidth = %d\n", maxColWidth);
}
}
else
{
spanCellMinWidth = spanInfo->cellMinWidth/spanInfo->initialColSpan;
if (minColWidth < spanCellMinWidth)
{
minColWidth = spanCellMinWidth;
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, new min = %d\n",
colIndex, spanInfo->span, minColWidth);
maxColWidth = PR_MAX(maxColWidth, minColWidth);
if (gsDebug)
printf (" maxColWidth = %d\n", maxColWidth);
}
}
}
// compute the spanning cell's contribution to the column max width
nscoord spanCellMaxWidth;
if (0!=spanInfo->effectiveMaxWidthOfSpannedCols)
{
spanCellMaxWidth = (spanInfo->cellDesiredWidth * colFrame->GetEffectiveMinColWidth()) /
(spanInfo->effectiveMaxWidthOfSpannedCols);
if (gsDebug==PR_TRUE)
printf (" spanlist max: %d of %d\n", spanCellMaxWidth, spanInfo->effectiveMaxWidthOfSpannedCols);
if (maxColWidth < spanCellMaxWidth)
{
maxColWidth = spanCellMaxWidth; // set the new max width for the col
mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth); // set the column to the new desired max width
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, \
new max = %d\n",
colIndex, spanInfo->span, maxColWidth);
}
}
else
{
spanCellMaxWidth = spanInfo->cellDesiredWidth/spanInfo->initialColSpan;
maxColWidth = spanCellMaxWidth;
mTableFrame->SetColumnWidth(colIndex, spanCellMaxWidth);
if (gsDebug==PR_TRUE)
printf (" for spanning cell into col %d with remaining span=%d, \
new max = %d\n",
colIndex, spanInfo->span, maxColWidth);
}
}
spanInfo->span--;
if (0==spanInfo->span)
{
spanList->RemoveElementAt(spanIndex);
delete spanInfo;
}
}
}
// second, process non-fixed-width columns
if (PR_FALSE==IsFixedWidth(colPosition, colTableStyle))
{
for (rowIndex = 0; rowIndex<numRows; rowIndex++)
{
nsTableCellFrame * cellFrame = mTableFrame->GetCellFrameAt(rowIndex, colIndex);
if (nsnull==cellFrame)
{ // there is no cell in this row that corresponds to this column
continue;
}
if (rowIndex!=cellFrame->GetRowIndex()) {
// For cells that span rows, we only figure it in once
NS_ASSERTION(1 != cellFrame->GetRowSpan(), "row index does not match row span"); // sanity check
continue;
}
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
continue;
}
PRInt32 colSpan = mTableFrame->GetEffectiveColSpan(colIndex, cellFrame);
nsSize cellMinSize = cellFrame->GetPass1MaxElementSize();
nsSize cellDesiredSize = cellFrame->GetPass1DesiredSize();
nscoord cellMinWidth=colFrame->GetMinColWidth();
nscoord cellDesiredWidth=colFrame->GetMaxColWidth();
if (1==colSpan)
{
cellMinWidth = cellMinSize.width;
cellDesiredWidth = cellDesiredSize.width;
nscoord specifiedCellWidth=-1;
const nsStylePosition* cellPosition;
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
if (eStyleUnit_Percent==cellPosition->mWidth.GetUnit())
{
float percent = cellPosition->mWidth.GetPercentValue();
specifiedCellWidth = (PRInt32)(aMaxWidth*percent);
if (gsDebug) printf("specified percent width %f of %d = %d\n",
percent, aMaxWidth, specifiedCellWidth);
}
if (-1!=specifiedCellWidth)
{
if (specifiedCellWidth>cellMinWidth)
{
if (gsDebug) printf("setting cellDesiredWidth from %d to %d\n",
cellDesiredWidth, specifiedCellWidth);
cellDesiredWidth = specifiedCellWidth;
}
}
}
else
{ // colSpan>1, get the proportion for this column
// then get the desired size info factoring in the cell style attributes
nscoord effectiveMaxWidthOfSpannedCols = colFrame->GetEffectiveMaxColWidth();
nscoord effectiveMinWidthOfSpannedCols = colFrame->GetEffectiveMinColWidth();
for (PRInt32 span=1; span<colSpan; span++)
{
nsTableColFrame *nextColFrame = mTableFrame->GetColFrame(colIndex+span);
if (nsnull==nextColFrame)
break;
effectiveMaxWidthOfSpannedCols += nextColFrame->GetEffectiveMaxColWidth();
effectiveMinWidthOfSpannedCols += nextColFrame->GetEffectiveMinColWidth();
}
nscoord specifiedCellWidth=-1;
const nsStylePosition* cellPosition;
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
if (eStyleUnit_Percent==cellPosition->mWidth.GetUnit())
{ //XXX what if table is auto width?
float percent = cellPosition->mWidth.GetPercentValue();
specifiedCellWidth = (PRInt32)(aMaxWidth*percent);
if (gsDebug) printf("specified percent width %f of %d = %d\n",
percent, aMaxWidth, specifiedCellWidth);
}
if (-1!=specifiedCellWidth)
{
float percentForThisCol = (float)(cellDesiredSize.width * colFrame->GetEffectiveMaxColWidth()) /
(float)effectiveMaxWidthOfSpannedCols;
nscoord cellWidthForThisCol = (nscoord)(specifiedCellWidth * percentForThisCol);
if (cellWidthForThisCol>cellMinWidth)
{
if (gsDebug) printf("setting cellDesiredWidth from %d to %d\n",
cellDesiredWidth, cellWidthForThisCol);
cellDesiredWidth = cellWidthForThisCol;
}
}
// otherwise it's already been factored in.
// now, if this column holds the cell, create a spanInfo struct for the cell
// so subsequent columns can take a proportion of this cell's space into account
if (cellFrame->GetColIndex()==colIndex)
{ // add this cell to span list iff we are currently processing the column the cell starts in
SpanInfo *spanInfo = new SpanInfo(colIndex, colSpan-1, cellMinSize.width, cellDesiredSize.width);
spanInfo->effectiveMaxWidthOfSpannedCols = effectiveMaxWidthOfSpannedCols;
spanInfo->effectiveMinWidthOfSpannedCols = effectiveMinWidthOfSpannedCols;
if (nsnull==spanList)
spanList = new nsVoidArray();
spanList->AppendElement(spanInfo);
}
}
if (PR_TRUE==gsDebug)
printf("factoring in cell %d with colSpan=%d\n factoring in min=%d and desired=%d\n",
rowIndex, colSpan, cellMinWidth, cellDesiredWidth);
// remember the widest min cell width
if (minColWidth < cellMinWidth)
minColWidth = cellMinWidth;
// remember the max desired cell width
if (maxColWidth < cellDesiredWidth)
maxColWidth = cellDesiredWidth;
// effectiveMaxColumnWidth is the width as if no cells with colspans existed
// if ((1==colSpan) && (colFrame->GetEffectiveMaxColWidth() < maxColWidth))
// colFrame->SetEffectiveMaxColWidth(cellDesiredWidth);
if (gsDebug==PR_TRUE)
printf (" after cell %d, minColWidth=%d maxColWidth=%d effColWidth[%d]=%d,%d\n",
rowIndex, minColWidth, maxColWidth,
colIndex, colFrame->GetEffectiveMinColWidth(), colFrame->GetEffectiveMaxColWidth());
}
if (gsDebug==PR_TRUE)
{
printf (" for determining width of col %d %s:\n",
colIndex, !IsFixedWidth(colPosition, colTableStyle)? "(P)":"(A)");
printf (" minTableWidth = %d and maxTableWidth = %d\n",
mMinTableWidth, mMaxTableWidth);
printf (" minColWidth = %d, maxColWidth = %d, eff=%d,%d\n",
minColWidth, maxColWidth, colFrame->GetEffectiveMinColWidth(),
colFrame->GetEffectiveMaxColWidth());
printf (" aAvailWidth = %d\n", aAvailWidth);
}
// Get column width if it has one
nscoord specifiedProportionColumnWidth = -1;
float specifiedPercentageColWidth = -1.0f;
PRBool isAutoWidth = PR_FALSE;
switch (colPosition->mWidth.GetUnit()) {
case eStyleUnit_Percent:
specifiedPercentageColWidth = colPosition->mWidth.GetPercentValue();
if (gsDebug) printf("column %d has specified percent width = %f\n", colIndex, specifiedPercentageColWidth);
break;
case eStyleUnit_Proportional:
specifiedProportionColumnWidth = colPosition->mWidth.GetIntValue();
if (gsDebug) printf("column %d has specified percent width = %d\n", colIndex, specifiedProportionColumnWidth);
break;
case eStyleUnit_Auto:
isAutoWidth = PR_TRUE;
break;
default:
break;
}
/* set the column width, knowing that the table is constrained */
if (0==specifiedProportionColumnWidth || 0.0==specifiedPercentageColWidth)
{ // col width is specified to be the minimum
mTableFrame->SetColumnWidth(colIndex, minColWidth);
if (gsDebug==PR_TRUE)
printf (" 4 (0): col %d set to min width = %d because style set proportionalWidth=0\n",
colIndex, mTableFrame->GetColumnWidth(colIndex));
}
else if (1==mNumCols)
{ // there is only one column, and we know that it's desired width doesn't fit
// so the column should be as wide as the available space allows it to be minus cell spacing
nscoord colWidth = aAvailWidth - (2*colInset);
mTableFrame->SetColumnWidth(colIndex, colWidth);
if (gsDebug==PR_TRUE)
printf (" 4 one-col: col %d set to width = %d from available width %d and cell spacing %d\n",
colIndex, mTableFrame->GetColumnWidth(colIndex), aAvailWidth, colInset);
}
else if (PR_TRUE==isAutoWidth)
{ // column's width is determined by its content, done in post-processing
mTableFrame->SetColumnWidth(colIndex, minColWidth); // reserve the column's min width
atLeastOneAutoWidthColumn = PR_TRUE;
}
else if (-1!=specifiedProportionColumnWidth)
{ // we need to save these and do them after all other columns have been calculated
/* the calculation will be:
sum up n, the total number of slices for the columns with proportional width
compute the table "required" width, fixed-width + percentage-width +
the sum of the proportional column's max widths (especially because in this routine I know the table fits)
compute the remaining width: the required width - the used width (fixed + percentage)
compute the width per slice
set the width of each proportional-width column to it's number of slices * width per slice
*/
mTableFrame->SetColumnWidth(colIndex, 0); // set the column width to 0, since it isn't computed yet
if (nsnull==proportionalColumnsList)
proportionalColumnsList = new nsVoidArray();
ProportionalColumnLayoutStruct * info =
new ProportionalColumnLayoutStruct(colIndex, minColWidth, maxColWidth, specifiedProportionColumnWidth);
proportionalColumnsList->AppendElement(info);
totalSlices += specifiedProportionColumnWidth;
if (1!=specifiedProportionColumnWidth)
equalWidthColumns = PR_FALSE;
}
else
{ // give the column a percentage of the remaining space
PRInt32 percentage = -1;
if (NS_UNCONSTRAINEDSIZE==aAvailWidth)
{ // since the "remaining space" is infinite, give the column it's max requested size
mTableFrame->SetColumnWidth(colIndex, maxColWidth);
}
else
{
if (-1.0f != specifiedPercentageColWidth)
{
percentage = (PRInt32)(specifiedPercentageColWidth*100.0f); // TODO: rounding errors?
// base the % on the total specified fixed width of the table
mTableFrame->SetColumnWidth(colIndex, (percentage*aMaxWidth)/100);
if (gsDebug==PR_TRUE)
printf (" 4 percent specified: col %d given %d percent of aMaxWidth %d, set to width = %d\n",
colIndex, percentage, aMaxWidth, mTableFrame->GetColumnWidth(colIndex));
}
if (-1==percentage)
{
percentage = 100/mNumCols;
// base the % on the remaining available width
mTableFrame->SetColumnWidth(colIndex, (percentage*aAvailWidth)/100);
if (gsDebug==PR_TRUE)
printf (" 4 percent default: col %d given %d percent of aAvailWidth %d, set to width = %d\n",
colIndex, percentage, aAvailWidth, mTableFrame->GetColumnWidth(colIndex));
}
// if the column was computed to be too small, enlarge the column
if (mTableFrame->GetColumnWidth(colIndex) <= minColWidth)
{
mTableFrame->SetColumnWidth(colIndex, minColWidth);
if (PR_TRUE==gsDebug) printf(" enlarging column to it's minimum = %d\n", minColWidth);
if (maxOfAllMinColWidths < minColWidth)
{
maxOfAllMinColWidths = minColWidth;
if (PR_TRUE==gsDebug)
printf(" and setting maxOfAllMins to %d\n", maxOfAllMinColWidths);
}
}
}
}
}
else
{
mTableFrame->SetColumnWidth(colIndex, colFrame->GetMaxColWidth());
}
tableWidth += mTableFrame->GetColumnWidth(colIndex) + colInset;
}
tableWidth += colInset;
/* --- post-process if necessary --- */
// first, assign autoWidth columns a width
if (PR_TRUE==atLeastOneAutoWidthColumn)
{ // proportionately distribute the remaining space to autowidth columns
// "0" for the last param tells DistributeRemainingSpace that this is the top (non-recursive) call
PRInt32 topRecursiveControl=0;
DistributeRemainingSpace(aMaxWidth, tableWidth, aTableIsAutoWidth, topRecursiveControl);
}
// second, fix up tables where column width attributes give us a table that is too wide or too narrow
nscoord computedWidth=colInset;
for (PRInt32 i=0; i<mNumCols; i++) {
computedWidth += mTableFrame->GetColumnWidth(i) + colInset;
}
if (computedWidth<aMaxWidth)
{ // then widen the table because it's too narrow
// do not widen auto-width tables, they shrinkwrap to their content's width
if (PR_FALSE==aTableIsAutoWidth)
AdjustTableThatIsTooNarrow(computedWidth, aMaxWidth);
}
else if (computedWidth>aMaxWidth)
{ // then shrink the table width because its too wide
AdjustTableThatIsTooWide(computedWidth, aMaxWidth, PR_FALSE);
}
// finally, assign a width to proportional-width columns
if (nsnull!=proportionalColumnsList)
{
// first, figure out the amount of space per slice
nscoord widthRemaining = aMaxWidth - tableWidth;
nscoord widthPerSlice = widthRemaining/totalSlices;
PRInt32 numSpecifiedProportionalColumns = proportionalColumnsList->Count();
for (PRInt32 i=0; i<numSpecifiedProportionalColumns; i++)
{
ProportionalColumnLayoutStruct * info =
(ProportionalColumnLayoutStruct *)(proportionalColumnsList->ElementAt(i));
if (PR_TRUE==equalWidthColumns && 0!=maxOfAllMinColWidths)
{
if (gsDebug==PR_TRUE)
printf(" EqualColWidths specified and some column couldn't fit, so setting col %d width to %d\n",
info->mColIndex, maxOfAllMinColWidths);
mTableFrame->SetColumnWidth(info->mColIndex, maxOfAllMinColWidths);
}
else
{
// compute the requested proportional width
nscoord computedColWidth = info->mProportion*widthPerSlice;
// verify that the computed width is at least the minimum width
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(info->mColIndex, colFrame);
nscoord minColWidth = colFrame->GetMinColWidth();
computedColWidth = PR_MAX(computedColWidth, minColWidth);
mTableFrame->SetColumnWidth(info->mColIndex, computedColWidth);
if (gsDebug==PR_TRUE)
printf (" 4 proportion: col %d given %d proportion of remaining space %d, set to width = %d\n",
info->mColIndex, info->mProportion, widthRemaining, computedColWidth);
}
delete info;
}
delete proportionalColumnsList;
}
// clean up
if (nsnull!=spanList)
{
if (gsDebug) printf("BTLS::BCTConstrained...space leak, span list not empty");
delete spanList;
}
return result;
}
static const PRInt32 kRecursionLimit=10; // backwards compatible with Nav4
// take the remaining space in the table and distribute it proportionately
// to the auto-width cells in the table (based on desired width)
void BasicTableLayoutStrategy::DistributeRemainingSpace(nscoord aTableSpecifiedWidth,
nscoord &aComputedTableWidth,
PRBool aTableIsAutoWidth,
PRInt32 &aRecursionControl)
{
aRecursionControl++;
if (kRecursionLimit<=aRecursionControl) // only allow kRecursionLimit iterations, as per Nav4. See laytable.c
return;
nscoord sumOfMinWidths = 0; // sum of min widths of each auto column
nscoord aStartingComputedTableWidth = aComputedTableWidth; // remember this so we can see if we're making any progress
if (PR_TRUE==gsDebug)
printf ("DistributeRemainingSpace: fixed width %d > computed table width %d\n",
aTableSpecifiedWidth, aComputedTableWidth);
// if there are auto-sized columns, give them the extra space
PRInt32 numAutoColumns=0;
PRInt32 *autoColumns=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoColumns, autoColumns);
if (0!=numAutoColumns)
{
PRInt32 numColumnsToBeResized=0;
// there's at least one auto-width column, so give it (them) the extra space
// proportionately distributed extra space, based on the column's desired size
nscoord totalEffectiveWidthOfAutoColumns = 0;
// 1. first, get the total width of the auto columns
PRInt32 i;
for (i = 0; i<numAutoColumns; i++)
{
PRInt32 colIndex = autoColumns[i];
nsTableColFrame *colFrame = mTableFrame->GetColFrame(autoColumns[i]);
nscoord startingColWidth = mTableFrame->GetColumnWidth(colIndex);
nscoord maxEffectiveColWidth = colFrame->GetEffectiveMaxColWidth();
if ((PR_FALSE==aTableIsAutoWidth)||(startingColWidth<maxEffectiveColWidth))
{
numColumnsToBeResized++;
if (0!=maxEffectiveColWidth)
totalEffectiveWidthOfAutoColumns += maxEffectiveColWidth;
else
totalEffectiveWidthOfAutoColumns += mTableFrame->GetColumnWidth(autoColumns[i]);
}
}
// availWidth is the difference between the total available width and the
// amount of space already assigned, assuming auto col widths were assigned 0.
nscoord availWidth;
availWidth = aTableSpecifiedWidth - aComputedTableWidth;
if (gsDebug==PR_TRUE)
printf(" aTableSpecifiedWidth specified as %d, availWidth is = %d\n",
aTableSpecifiedWidth, availWidth);
// 2. next, compute the proportion to be added to each column, and add it
for (i = 0; i<numAutoColumns; i++)
{
PRInt32 colIndex = autoColumns[i];
nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
nscoord startingColWidth = mTableFrame->GetColumnWidth(colIndex);
nscoord maxEffectiveColWidth = colFrame->GetEffectiveMaxColWidth();
// if we actually have room to distribute, do it here
// otherwise, the auto columns already are set to their minimum
if (0<availWidth)
{
if ((PR_FALSE==aTableIsAutoWidth)||(startingColWidth<maxEffectiveColWidth))
{
float percent;
if (0!=aTableSpecifiedWidth)
percent = ((float)maxEffectiveColWidth)/((float)totalEffectiveWidthOfAutoColumns);
else
percent = ((float)1)/((float)numColumnsToBeResized);
nscoord colWidth = startingColWidth + (nscoord)(availWidth*percent);
if (PR_TRUE==gsDebug)
printf(" colWidth = %d from startingColWidth %d plus %d percent of availWidth %d\n",
colWidth, startingColWidth, (nscoord)((float)100*percent), availWidth);
// in an auto width table, the column cannot be wider than its max width
if (PR_TRUE==aTableIsAutoWidth)
{ // since the table shrinks to the content width, don't be wider than the content max width
colWidth = PR_MIN(colWidth, colFrame->GetMaxColWidth());
}
aComputedTableWidth += colWidth - startingColWidth;
if (gsDebug==PR_TRUE)
printf(" distribute width to auto columns: column %d was %d, now set to %d\n",
colIndex, colFrame->GetEffectiveMaxColWidth(), colWidth);
mTableFrame->SetColumnWidth(colIndex, colWidth);
}
}
}
if (aComputedTableWidth!=aStartingComputedTableWidth)
{ // othewise we made no progress and shouldn't continue
if (aComputedTableWidth<aTableSpecifiedWidth)
{
DistributeRemainingSpace(aTableSpecifiedWidth, aComputedTableWidth, aTableIsAutoWidth, aRecursionControl);
}
}
}
if (PR_TRUE==gsDebug)
{
nscoord tableWidth=0;
printf("at end of DistributeRemainingSpace: ");
for (PRInt32 i=0; i<mNumCols; i++)
{
tableWidth += mTableFrame->GetColumnWidth(i);
printf(" %d ", mTableFrame->GetColumnWidth(i));
}
printf ("\n computed table width is %d\n",tableWidth);
}
}
void BasicTableLayoutStrategy::AdjustTableThatIsTooWide(nscoord aComputedWidth,
nscoord aTableWidth,
PRBool aShrinkFixedCols)
{
if (PR_TRUE==gsDebug)
{
nscoord tableWidth=0;
printf("before AdjustTableThatIsTooWide: ");
for (PRInt32 i=0; i<mNumCols; i++)
{
tableWidth += mTableFrame->GetColumnWidth(i);
printf(" %d ", mTableFrame->GetColumnWidth(i));
}
printf ("\n computed table width is %d\n",tableWidth);
}
PRInt32 numFixedColumns=0;
PRInt32 *fixedColumns=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Coord, numFixedColumns, fixedColumns);
PRInt32 numAutoColumns=0;
PRInt32 *autoColumns=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoColumns, autoColumns);
nscoord excess = aComputedWidth - aTableWidth;
nscoord minDiff; // the smallest non-zero delta between a column's current width and its min width
PRInt32 * colsToShrink = new PRInt32[mNumCols];
// while there is still extra computed space in the table
while (0<excess)
{
// reinit state variables
PRInt32 colIndex;
for (colIndex=0; colIndex<mNumCols; colIndex++)
colsToShrink[colIndex]=0;
minDiff=0;
// determine what columns we can remove width from
PRInt32 numColsToShrink = 0;
PRBool shrinkAutoOnly=PR_TRUE;
PRBool keepLooking=PR_TRUE;
while (PR_TRUE==keepLooking)
{
for (colIndex=0; colIndex<mNumCols; colIndex++)
{
nscoord currentColWidth = mTableFrame->GetColumnWidth(colIndex);
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(colIndex, colFrame);
nscoord minColWidth = colFrame->GetAdjustedMinColWidth();
if (currentColWidth==minColWidth)
continue;
if ((PR_FALSE==aShrinkFixedCols) &&
(PR_TRUE==IsColumnInList(colIndex, fixedColumns, numFixedColumns)))
continue;
if ((PR_TRUE==shrinkAutoOnly) &&
(PR_FALSE==IsColumnInList(colIndex, autoColumns, numAutoColumns)))
continue;
colsToShrink[numColsToShrink] = colIndex;
numColsToShrink++;
nscoord diff = currentColWidth - minColWidth;
if ((0==minDiff) || (diff<minDiff))
minDiff = diff;
}
if (PR_FALSE==shrinkAutoOnly)
keepLooking=PR_FALSE; // we've looked everywhere, so bail. this breaks us out of the loop
if (0!=numColsToShrink)
keepLooking=PR_FALSE; // we found at least one column to shrink, so bail. this breaks us out of the loop
shrinkAutoOnly=PR_FALSE;// this guarantees we'll go through this loop only one more time
}
// if there are no columns we can remove space from, we're done
if (0==numColsToShrink)
break;
// determine the amount to remove from each column
nscoord excessPerColumn;
if (excess<numColsToShrink)
excessPerColumn=1;
else
excessPerColumn = excess/numColsToShrink; // guess we can remove as much as we want
if (excessPerColumn>minDiff) // then adjust for minimum col widths
excessPerColumn=minDiff;
// remove excessPerColumn from every column we've determined we can remove width from
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
if ((PR_TRUE==IsColumnInList(colIndex, colsToShrink, numColsToShrink)))
{
nscoord colWidth = mTableFrame->GetColumnWidth(colIndex);
colWidth -= excessPerColumn;
mTableFrame->SetColumnWidth(colIndex, colWidth);
excess -= excessPerColumn;
if (0==excess)
break;
}
}
} // end while (0<excess)
if (PR_TRUE==gsDebug)
{
nscoord tableWidth=0;
printf("after AdjustTableThatIsTooWide: ");
for (PRInt32 i=0; i<mNumCols; i++)
{
tableWidth += mTableFrame->GetColumnWidth(i);
printf(" %d ", mTableFrame->GetColumnWidth(i));
}
printf ("\n computed table width is %d with aShrinkFixedCols = %s\n",
tableWidth, aShrinkFixedCols ? "TRUE" : "FALSE");
}
delete [] colsToShrink;
// deal with any excess left over
if ((PR_FALSE==aShrinkFixedCols) && (0!=excess))
{
// if there's any excess left, we know we've shrunk every non-fixed column to its min
// so we have to shrink fixed width columns if possible
AdjustTableThatIsTooWide(aComputedWidth, aTableWidth, PR_TRUE);
}
// otherwise we've shrunk the table to its min, and that's all we can do
}
void BasicTableLayoutStrategy::AdjustTableThatIsTooNarrow(nscoord aComputedWidth,
nscoord aTableWidth)
{
if (PR_TRUE==gsDebug)
{
nscoord tableWidth=0;
printf("before AdjustTableThatIsTooNarrow: ");
for (PRInt32 i=0; i<mNumCols; i++)
{
tableWidth += mTableFrame->GetColumnWidth(i);
printf(" %d ", mTableFrame->GetColumnWidth(i));
}
printf ("\n computed table width is %d\n",tableWidth);
}
PRInt32 numFixedColumns=0;
PRInt32 *fixedColumns=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Coord, numFixedColumns, fixedColumns);
nscoord excess = aTableWidth - aComputedWidth;
while (0<excess)
{
PRInt32 colIndex;
PRInt32 * colsToGrow = new PRInt32[mNumCols];
// determine what columns we can take add width to
PRInt32 numColsToGrow = 0;
PRBool expandFixedCols = PRBool(mNumCols==numFixedColumns);
for (colIndex=0; colIndex<mNumCols; colIndex++)
{
if ((PR_FALSE==expandFixedCols) &&
(PR_TRUE==IsColumnInList(colIndex, fixedColumns, numFixedColumns)))
// skip fixed-width cells if we're told to
continue;
if (PR_TRUE==ColIsSpecifiedAsMinimumWidth(colIndex))
// skip columns that are forced by their attributes to be their minimum width
continue;
colsToGrow[numColsToGrow] = colIndex;
numColsToGrow++;
}
if (0!=numColsToGrow)
{
nscoord excessPerColumn;
if (excess<numColsToGrow)
excessPerColumn=1;
else
excessPerColumn = excess/numColsToGrow;
for (colIndex = 0; colIndex<mNumCols; colIndex++)
{
if ((PR_TRUE==IsColumnInList(colIndex, colsToGrow, numColsToGrow)))
{
nsTableColFrame *colFrame = mTableFrame->GetColFrame(colIndex);
nscoord colWidth = mTableFrame->GetColumnWidth(colIndex);
colWidth += excessPerColumn;
if (colWidth > colFrame->GetMinColWidth())
{
excess -= excessPerColumn;
mTableFrame->SetColumnWidth(colIndex, colWidth);
}
else
{
excess -= mTableFrame->GetColumnWidth(colIndex) - colFrame->GetMinColWidth();
mTableFrame->SetColumnWidth(colIndex, colFrame->GetMinColWidth());
}
if (0>=excess)
break;
}
}
}
delete [] colsToGrow;
if (0==numColsToGrow)
break;
} // end while (0<excess)
if (PR_TRUE==gsDebug)
{
nscoord tableWidth=0;
printf("after AdjustTableThatIsTooNarrow: ");
for (PRInt32 i=0; i<mNumCols; i++)
{
tableWidth += mTableFrame->GetColumnWidth(i);
printf(" %d ", mTableFrame->GetColumnWidth(i));
}
printf ("\n computed table width is %d\n",tableWidth);
}
}
PRBool BasicTableLayoutStrategy::IsColumnInList(const PRInt32 colIndex,
PRInt32 *colIndexes,
PRInt32 aNumFixedColumns)
{
PRBool result = PR_FALSE;
for (PRInt32 i=0; i<aNumFixedColumns; i++)
{
if (colIndex==colIndexes[i])
{
result = PR_TRUE;
break;
}
else if (colIndex<colIndexes[i])
break;
}
return result;
}
PRBool BasicTableLayoutStrategy::ColIsSpecifiedAsMinimumWidth(PRInt32 aColIndex)
{
PRBool result = PR_FALSE;
nsTableColFrame* colFrame;
mTableFrame->GetColumnFrame(aColIndex, colFrame);
const nsStylePosition* colPosition;
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
switch (colPosition->mWidth.GetUnit())
{
case eStyleUnit_Coord:
if (0==colPosition->mWidth.GetCoordValue())
result = PR_TRUE;
break;
case eStyleUnit_Percent:
{
// total hack for now for 0% and 1% specifications
// should compare percent to available parent width and see that it is below minimum
// for this column
float percent = colPosition->mWidth.GetPercentValue();
if (0.0f == percent || 0.01f == percent)
result = PR_TRUE;
break;
}
case eStyleUnit_Proportional:
if (0==colPosition->mWidth.GetIntValue())
result=PR_TRUE;
}
return result;
}
// returns a list and count of all columns that behave like they have width=auto
// this includes columns with no width specified, and columns whose fixed width comes from a span
void BasicTableLayoutStrategy::GetColumnsThatActLikeAutoWidth(PRInt32 &aNumCols, PRInt32 *&aColList)
{
// initialize out params
aNumCols=0;
aColList=nsnull;
// get the auto columns
PRInt32 numAutoCols=0;
PRInt32 *autoColList=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoCols, autoColList);
// XXX: have to do next step for %-width as well?
// get the fixed columns
PRInt32 numFixedCols=0;
PRInt32 *fixedColList=nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Coord, numFixedCols, fixedColList);
if (0<numAutoCols+numFixedCols)
{
// allocate the out param aColList to the biggest it could be (usually a little wasteful, but it's
// temp space and the numbers here are small, even for very big tables
aColList = new PRInt32[numAutoCols+numFixedCols];
// transfer the auto columns to aColList
PRInt32 i;
for (i=0; i<numAutoCols; i++)
{
aColList[i]=autoColList[i];
aNumCols++;
}
// get the columns whose fixed width is due to a colspan and transfer them to aColList
for (i=0; i<numFixedCols; i++)
{
nsTableColFrame *colFrame;
mTableFrame->GetColumnFrame(fixedColList[i], colFrame);
if (nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN==colFrame->GetWidthSource())
{
aColList[aNumCols] = fixedColList[i];
aNumCols++;
}
}
}
return;
}