Rewrote DistributeRemainingSpace a 2nd time and added compat mode.

bug 6068, 6933. nsTableRowGroupFrame::CalculateRowHeights - changed row spans to consider
top and bottom margins during height calculations. Calculations using percentages ensure that the sum of
rows sum exactly to excess.

bug 6404 BasicTableLayoutStrategy::BalanceColumnsConstrained cols in span other than 1st col was
not getting additional allocations from cell. ::DistributeRemainSpace uses max width of col
instead of max effective width. Also added checks for negative value during percentage calculations.
This commit is contained in:
karnaze@netscape.com
1999-05-26 22:22:23 +00:00
parent aa7748569b
commit a138139e00
10 changed files with 532 additions and 168 deletions

View File

@@ -217,14 +217,15 @@ PRBool BasicTableLayoutStrategy::IsFixedWidth(const nsStylePosition* aStylePosit
}
BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame *aFrame)
BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame *aFrame, PRBool aIsNavQuirks)
{
NS_ASSERTION(nsnull != aFrame, "bad frame arg");
mTableFrame = aFrame;
mMinTableWidth = 0;
mMaxTableWidth = 0;
mFixedTableWidth = 0;
mTableFrame = aFrame;
mMinTableWidth = 0;
mMaxTableWidth = 0;
mFixedTableWidth = 0;
mIsNavQuirksMode = aIsNavQuirks;
}
BasicTableLayoutStrategy::~BasicTableLayoutStrategy()
@@ -416,9 +417,7 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
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
horPadding[colIndex] = 0;
// Get column information
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colIndex);
@@ -433,7 +432,7 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
// Get fixed column width if it has one
if (eStyleUnit_Coord == colPosition->mWidth.GetUnit()) {
haveColWidth = PR_TRUE;
colFrame->SetHasConstrainedWidth(PR_TRUE);
specifiedFixedColWidth = colPosition->mWidth.GetCoordValue();
horPadding[colIndex] = CalcHorizontalPadding(colIndex);
specifiedFixedColWidth += horPadding[colIndex];
@@ -489,7 +488,7 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
rowIndex, colSpan, cellMinSize.width, cellMinSize.height,
cellDesiredSize.width, cellDesiredSize.height);
if (PR_TRUE == haveColWidth) {
if (colFrame->HasConstrainedWidth()) {
// 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)
@@ -594,13 +593,13 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
} // end for (rowIndex = 0; rowIndex < numRows; rowIndex++)
// adjust the "fixed" width for content that is too wide
if (haveColWidth && (effectiveMinColumnWidth > specifiedFixedColWidth)) {
if (colFrame->HasConstrainedWidth() && (effectiveMinColumnWidth > specifiedFixedColWidth)) {
specifiedFixedColWidth = effectiveMinColumnWidth;
}
// do all the global bookkeeping, factoring in margins
nscoord colInset = mTableFrame->GetCellSpacingX();
// keep a running total of the amount of space taken up by all fixed-width columns
if ((PR_TRUE == haveColWidth) &&
if ((colFrame->HasConstrainedWidth()) &&
(nsTableColFrame::eWIDTH_SOURCE_CELL == colFrame->GetWidthSource())) {
mFixedTableWidth += specifiedFixedColWidth + colInset;
if (0 == colIndex) {
@@ -616,7 +615,7 @@ PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths()
colFrame->SetEffectiveMaxColWidth(effectiveMaxColumnWidth);
// this is the default, the real adjustment happens below where we deal with colspans
colFrame->SetAdjustedMinColWidth(effectiveMinColumnWidth);
if ((PR_TRUE == haveColWidth) &&
if ((colFrame->HasConstrainedWidth()) &&
(nsTableColFrame::eWIDTH_SOURCE_CELL_WITH_SPAN!=colFrame->GetWidthSource())) {
mTableFrame->SetColumnWidth(colIndex, specifiedFixedColWidth);
}
@@ -1740,9 +1739,7 @@ PRBool BasicTableLayoutStrategy::
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;
PRBool needsExtraMinWidth = (spanInfo->effectiveMinWidthOfSpannedCols < spanInfo->cellMinWidth);
if (PR_TRUE == needsExtraMinWidth) {
if (0 != spanInfo->effectiveMinWidthOfSpannedCols) {
spanCellMinWidth = (spanInfo->cellMinWidth * colFrame->GetEffectiveMinColWidth()) /
@@ -1951,7 +1948,7 @@ PRBool BasicTableLayoutStrategy::
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
mTableFrame->SetColumnWidth(colIndex, minColWidth); // reserve the column's min width
atLeastOneAutoWidthColumn = PR_TRUE;
}
else if (-1 != specifiedProportionColumnWidth) {
@@ -2080,36 +2077,200 @@ PRBool BasicTableLayoutStrategy::
}
struct nsColInfo {
nsColInfo(nsTableColFrame* aColFrame,
PRInt32 aColIndex,
PRInt32 aColWidth,
float aMaximizeFactor)
: mColFrame(aColFrame), mColIndex(aColIndex), mColWidth(aColWidth),
mMaximizeFactor(aMaximizeFactor)
{}
nsTableColFrame* mColFrame;
PRInt32 mColIndex;
PRInt32 mColWidth;
float mMaximizeFactor;
nsColInfo(nsTableColFrame* aFrame,
PRInt32 aIndex,
PRInt32 aMinWidth,
PRInt32 aWidth,
PRInt32 aMaxEffWidth,
PRInt32 aMaxWidth)
: mFrame(aFrame), mIndex(aIndex), mMinWidth(aMinWidth),
mWidth(aWidth), mMaxEffWidth(aMaxEffWidth), mMaxWidth(aMaxWidth), mWeight(0)
{}
nsTableColFrame* mFrame;
PRInt32 mIndex;
PRInt32 mMinWidth;
PRInt32 mWidth;
PRInt32 mMaxEffWidth;
PRInt32 mMaxWidth;
float mWeight;
};
void
DistributeRemainingSpaceCleanup(PRInt32 aNumItems, nsColInfo** aInfoArray)
DRS_Wrapup(nsTableFrame* aTableFrame,
PRInt32 aNumItems,
nsColInfo** aColInfo)
{
for (int i = 0; i < aNumItems; i++) {
delete aInfoArray[i];
aTableFrame->SetColumnWidth(aColInfo[i]->mIndex, aColInfo[i]->mWidth);
delete aColInfo[i];
}
delete [] aColInfo;
}
void
DRS_Increase(PRInt32 aNumAutoCols,
nsColInfo** aColInfo,
PRInt32 aDivisor,
PRBool aDontGoOverMax,
PRInt32& aAvailWidth)
{
for (PRInt32 i = 0; i < aNumAutoCols; i++) {
if ((aAvailWidth <= 0) || (aDivisor <= 0)) {
break;
}
//float percent = ((float)aColInfo[i]->mMaxEffWidth) / (float)aDivisor;
float percent = ((float)aColInfo[i]->mMaxWidth) / (float)aDivisor;
//aDivisor -= aColInfo[i]->mWidth;
aDivisor -= aColInfo[i]->mMaxWidth;
nscoord addition = PR_MIN(aAvailWidth, NSToCoordRound(((float)(aAvailWidth)) * percent));
if (aDontGoOverMax) {
addition = PR_MIN(addition, aColInfo[i]->mMaxWidth - aColInfo[i]->mWidth);
}
// don't let the total additions exceed what is available
if (i == aNumAutoCols - 1) {
addition = PR_MIN(addition, aAvailWidth);
}
aColInfo[i]->mWidth += addition;
aAvailWidth -= addition;
}
}
void
DRS_Decrease(PRInt32 aNumAutoCols,
nsColInfo** aColInfo,
PRInt32 aDivisor,
PRInt32& aExcess)
{
for (PRInt32 i = 0; i < aNumAutoCols; i++) {
if ((aExcess <= 0) || (aDivisor <= 0)) {
break;
}
//float percent = ((float)aColInfo[i]->mMaxEffWidth) / (float)aDivisor;
float percent = ((float)aColInfo[i]->mMaxWidth) / (float)aDivisor;
//aDivisor -= aColInfo[i]->mWidth;
aDivisor -= aColInfo[i]->mMaxWidth;
nscoord reduction = PR_MIN(aExcess, NSToCoordRound(((float)(aExcess)) * percent));
// don't go over the col min
reduction = PR_MIN(reduction, aColInfo[i]->mWidth - aColInfo[i]->mMinWidth);
// don't let the total reductions exceed what is available
if (i == aNumAutoCols - 1) {
reduction = PR_MIN(reduction, aExcess);
}
aColInfo[i]->mWidth -= reduction;
aExcess -= reduction;
}
}
void
DSR_Sort(nsColInfo** aColInfo, PRInt32 aNumCols)
{
// sort the cols based on the Weight
for (PRInt32 j = aNumCols - 1; j > 0; j--) {
for (PRInt32 i = 0; i < j; i++) {
if (aColInfo[i]->mWeight < aColInfo[i+1]->mWeight) { // swap them
nsColInfo* save = aColInfo[i];
aColInfo[i] = aColInfo[i+1];
aColInfo[i+1] = save;
}
}
}
delete [] aInfoArray;
}
// Take the remaining space in the table and distribute it to the auto-width cols
// in the table. If a col reaches its desired width, stop at that width unless all
// other cols have reached their desired widths.
// in the table.
void BasicTableLayoutStrategy::DistributeRemainingSpace(nscoord aTableSpecifiedWidth,
nscoord& aComputedTableWidth,
PRBool aTableIsAutoWidth)
{
// availWidth is the difference between the total available width and the
// amount of space already assigned.
nscoord availWidth = PR_MAX(aTableSpecifiedWidth - aComputedTableWidth, 0);
TDBG_SDD(" aTableSpecifiedWidth specified as %d, availWidth is = %d\n", aTableSpecifiedWidth, availWidth);
if ((0 == availWidth) || (aTableSpecifiedWidth <= 0)) {
return;
}
// This algorithm has been reworked from the orignal to fix bugs (e.g. bug 6184)
// and remove the recursion. For auto width tables it uses colFrame->GetMaxColSize()
// instead of GetMaxEffectiveColSize, so for some auto width tables with colspans
// it claculates more like IE5 than Nav4.5, which appears to be more reasonable.
// Get the auto width cols. Only they get adjusted.
PRInt32 numAutoCols = 0;
PRInt32* autoCols = nsnull;
mTableFrame->GetColumnsByType(eStyleUnit_Auto, numAutoCols, autoCols);
if (0 == numAutoCols) {
return;
}
nsColInfo** colInfo = new nsColInfo*[numAutoCols];
nscoord totalDivisor = 0;
nscoord totalAutoWidthGiven = 0;
PRInt32 i;
// start out by giving each col its max effective width (or adj width if it is greater)
// for standard mode and its min width for nav quirks mode
for (i = 0; i < numAutoCols; i++) {
PRInt32 colIndex = autoCols[i];
nsTableColFrame* colFrame = mTableFrame->GetColFrame(autoCols[i]);
nscoord origWidth = mTableFrame->GetColumnWidth(autoCols[i]);
nscoord minWidth = PR_MAX(colFrame->GetAdjustedMinColWidth(), origWidth);
nscoord maxEffWidth = PR_MAX(colFrame->GetEffectiveMaxColWidth(), minWidth);
nscoord maxWidth = colFrame->GetMaxColWidth();
nscoord startWidth = (mIsNavQuirksMode) ? minWidth : maxWidth;
colInfo[i] = new nsColInfo(colFrame, autoCols[i], minWidth, startWidth, maxEffWidth, maxWidth);
// totalDivisor += maxEffWidth;
totalDivisor += maxWidth;
totalAutoWidthGiven += startWidth - origWidth;
}
availWidth = aTableSpecifiedWidth - aComputedTableWidth - totalAutoWidthGiven;
if (availWidth >= 0) { // have more space to allocate
for (i = 0; i < numAutoCols; i++) {
// the weight here is a relative metric for determining when cols reach their max.
// A col with a larger weight will reach its max before one with a smaller value.
nscoord delta = colInfo[i]->mMaxWidth - colInfo[i]->mWidth;
colInfo[i]->mWeight = (delta <= 0)
? 1000000 // cols which have already reached their max get a large value
: ((float)colInfo[i]->mMaxWidth) / ((float)delta);
}
// sort the cols based on the weight so that in one pass cols with higher
// weights will get their max earlier than ones with lower weights
// This is an innefficient bubble sort, but unless there are an unlikely
// large number of cols, it is not an issue.
DSR_Sort(colInfo, numAutoCols);
// compute the proportion to be added to each column, don't go beyond the col's
// max. This algorithm assumes that the Weight works as stated above
DRS_Increase(numAutoCols, colInfo, totalDivisor, PR_TRUE, availWidth);
// if every col got its max and there is more to go, allocate a 2nd time
if (!aTableIsAutoWidth && (availWidth > 0)) {
DRS_Increase(numAutoCols, colInfo, totalDivisor, PR_FALSE, availWidth);
}
aComputedTableWidth = aTableSpecifiedWidth - availWidth;
}
else { // reduce each col width if appropriate
for (i = 0; i < numAutoCols; i++) {
// the weight here is a relative metric for determining when cols reach their min.
// A col with a larger weight will reach its min before one with a smaller value.
nscoord delta = colInfo[i]->mWidth - colInfo[i]->mMinWidth;
colInfo[i]->mWeight = (delta <= 0)
? 1000000 // cols which have already reached their min get a large value
: ((float)colInfo[i]->mWidth) / ((float)delta);
}
// sort the cols based on the Weight
DSR_Sort(colInfo, numAutoCols);
// compute the proportion to be subtracted from each column, don't go beyond
// the col's min. This algorithm assumes that the Weight works as stated above
PRInt32 totalExcessGiven = -availWidth;
DRS_Decrease(numAutoCols, colInfo, totalDivisor, totalExcessGiven);
aComputedTableWidth = aTableSpecifiedWidth + totalExcessGiven;
}
DRS_Wrapup(mTableFrame, numAutoCols, colInfo);
TDBG_WIDTHS4("at end of DistributeRemainingSpace: ",PR_FALSE,PR_FALSE);
}
#if 0
void BasicTableLayoutStrategy::DistributeRemainingSpace(nscoord aTableSpecifiedWidth,
nscoord& aComputedTableWidth,
PRBool aTableIsAutoWidth)
@@ -2141,21 +2302,19 @@ void BasicTableLayoutStrategy::DistributeRemainingSpace(nscoord aTableSpecified
PRInt32 colIndex = autoColumns[i];
nsTableColFrame* colFrame = mTableFrame->GetColFrame(autoColumns[i]);
nscoord startingColWidth = mTableFrame->GetColumnWidth(colIndex);
nscoord maxColWidth = (PR_FALSE == aTableIsAutoWidth)
? colFrame->GetEffectiveMaxColWidth() : colFrame->GetMaxColWidth();
if ((PR_FALSE == aTableIsAutoWidth) || (startingColWidth < maxColWidth)) {
if (0 == maxColWidth)
maxColWidth = startingColWidth;
totalResizeColWidth += maxColWidth;
// the maximizeFactor is a relative metric for determining when cols reach their max.
// A col with a smaller value will reach its max before one with a larger value.
nscoord delta = maxColWidth - startingColWidth;
float maximizeFactor = (0 == delta)
? 1000000 : ((float)maxColWidth) / ((float)(maxColWidth - startingColWidth));
resizeCols[numResizeCols] = new nsColInfo(colFrame, colIndex, startingColWidth,
maximizeFactor);
numResizeCols++;
}
nscoord maxColWidth = colFrame->GetMaxColWidth();
if (0 == maxColWidth)
maxColWidth = startingColWidth;
totalResizeColWidth += maxColWidth;
// the maximizeFactor is a relative metric for determining when cols reach their max.
// A col with a smaller value will reach its max before one with a larger value.
nscoord delta = maxColWidth - startingColWidth;
float maximizeFactor = (0 >= delta)
? 1000000 // cols which have already reached their max get a large value
: ((float)maxColWidth) / ((float)(maxColWidth - startingColWidth));
resizeCols[numResizeCols] = new nsColInfo(colFrame, colIndex, startingColWidth,
maximizeFactor);
numResizeCols++;
}
if (totalResizeColWidth <= 0) {
NS_ASSERTION(PR_TRUE, "need to handle this case");
@@ -2187,10 +2346,7 @@ void BasicTableLayoutStrategy::DistributeRemainingSpace(nscoord aTableSpecified
}
nsTableColFrame* colFrame = resizeCols[i]->mColFrame;
nscoord startingColWidth = resizeCols[i]->mColWidth;
nscoord maxColWidth = (PR_FALSE == aTableIsAutoWidth)
? colFrame->GetEffectiveMaxColWidth() : colFrame->GetMaxColWidth();
// if we actually have room to distribute, do it here
// otherwise, the auto columns already are set to their minimum
nscoord maxColWidth = colFrame->GetMaxColWidth();
float percent = ((float)maxColWidth) / (float)totalResizeColWidthRemaining;
nscoord delta = PR_MIN(availWidth, NSToCoordRound(((float)(availWidth)) * percent));
// don't go over the col max right now
@@ -2214,7 +2370,7 @@ void BasicTableLayoutStrategy::DistributeRemainingSpace(nscoord aTableSpecified
}
nsTableColFrame* colFrame = resizeCols[i]->mColFrame;
nscoord startingColWidth = resizeCols[i]->mColWidth;
nscoord maxColWidth = colFrame->GetEffectiveMaxColWidth();
nscoord maxColWidth = colFrame->GetMaxColWidth();
// if we actually have room to distribute, do it here
float percent = ((float)maxColWidth) / (float)totalResizeColWidth;
nscoord delta = PR_MIN(availWidth, NSToCoordRound(((float)(availWidth)) * percent));
@@ -2229,6 +2385,7 @@ void BasicTableLayoutStrategy::DistributeRemainingSpace(nscoord aTableSpecified
DistributeRemainingSpaceCleanup(numResizeCols, resizeCols);
TDBG_WIDTHS4("at end of DistributeRemainingSpace: ",PR_FALSE,PR_FALSE);
}
#endif
void BasicTableLayoutStrategy::AdjustTableThatIsTooWide(nscoord aComputedWidth,
nscoord aTableWidth,