зеркало из https://github.com/mozilla/pjs.git
1913 строки
71 KiB
C++
1913 строки
71 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
*
|
|
* The contents of this file are subject to the Netscape Public
|
|
* License Version 1.1 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.mozilla.org/NPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS
|
|
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
|
|
* implied. See the License for the specific language governing
|
|
* rights and limitations under the License.
|
|
*
|
|
* The Original Code is mozilla.org code.
|
|
*
|
|
* The Initial Developer of the Original Code is Netscape
|
|
* Communications Corporation. Portions created by Netscape are
|
|
* Copyright (C) 1998 Netscape Communications Corporation. All
|
|
* Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
*/
|
|
|
|
#include "BasicTableLayoutStrategy.h"
|
|
#include "nsTableFrame.h"
|
|
#include "nsTableColFrame.h"
|
|
#include "nsTableCellFrame.h"
|
|
#include "nsIStyleContext.h"
|
|
#include "nsStyleConsts.h"
|
|
#include "nsVoidArray.h"
|
|
#include "nsHTMLIIDs.h"
|
|
|
|
#if 1
|
|
static PRBool gsDebugAssign = PR_FALSE;
|
|
static PRBool gsDebugBalance = PR_FALSE;
|
|
#else
|
|
static PRBool gsDebugAssign = PR_TRUE;
|
|
static PRBool gsDebugBalance = PR_TRUE;
|
|
#endif
|
|
|
|
PRBool CanAllocate(PRInt32 aTypeToAllocate,
|
|
PRInt32 aTypeAlreadyAllocated,
|
|
nsTableColFrame* aColFrame,
|
|
PRBool& aConsiderAdj)
|
|
{
|
|
aConsiderAdj = PR_TRUE;
|
|
if ((aTypeToAllocate == FIX_ADJ) && (aTypeAlreadyAllocated == FIX)) {
|
|
return PR_TRUE;
|
|
}
|
|
if ((aTypeToAllocate == DES_CON) && (aTypeAlreadyAllocated == FIX_ADJ)) {
|
|
aConsiderAdj = PR_FALSE;
|
|
return PR_TRUE;
|
|
}
|
|
if ((aTypeToAllocate == DES_CON) && (aTypeAlreadyAllocated == FIX) &&
|
|
(aColFrame->GetWidth(DES_ADJ) > aColFrame->GetWidth(DES_CON))) {
|
|
return PR_TRUE;
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
/* ---------- BasicTableLayoutStrategy ---------- */
|
|
|
|
MOZ_DECL_CTOR_COUNTER(BasicTableLayoutStrategy);
|
|
|
|
|
|
BasicTableLayoutStrategy::BasicTableLayoutStrategy(nsTableFrame *aFrame, PRBool aIsNavQuirks)
|
|
{
|
|
MOZ_COUNT_CTOR(BasicTableLayoutStrategy);
|
|
NS_ASSERTION(nsnull != aFrame, "bad frame arg");
|
|
|
|
mTableFrame = aFrame;
|
|
mMinTableContentWidth = 0;
|
|
mMaxTableContentWidth = 0;
|
|
mCellSpacingTotal = 0;
|
|
mIsNavQuirksMode = aIsNavQuirks;
|
|
}
|
|
|
|
BasicTableLayoutStrategy::~BasicTableLayoutStrategy()
|
|
{
|
|
MOZ_COUNT_DTOR(BasicTableLayoutStrategy);
|
|
}
|
|
|
|
PRBool BasicTableLayoutStrategy::Initialize(nsIPresContext* aPresContext,
|
|
nsSize* aMaxElementSize,
|
|
nscoord aMaxWidth)
|
|
{
|
|
ContinuingFrameCheck();
|
|
|
|
PRBool result = PR_TRUE;
|
|
|
|
// re-init instance variables
|
|
mMinTableContentWidth = 0;
|
|
mMaxTableContentWidth = 0;
|
|
mCellSpacingTotal = 0;
|
|
mCols = mTableFrame->GetEffectiveCOLSAttribute();
|
|
// assign the width of all fixed-width columns
|
|
AssignPreliminaryColumnWidths(aPresContext, aMaxWidth);
|
|
|
|
// 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));
|
|
mTableFrame->GetTableBorder(borderPadding);
|
|
nsMargin padding;
|
|
tableSpacing->GetPadding(padding);
|
|
borderPadding += padding;
|
|
nscoord horBorderPadding = borderPadding.left + borderPadding.right;
|
|
if (tablePosition->mWidth.GetUnit() == eStyleUnit_Coord) {
|
|
aMaxElementSize->width = tablePosition->mWidth.GetCoordValue();
|
|
if (mMinTableContentWidth + horBorderPadding > aMaxElementSize->width) {
|
|
aMaxElementSize->width = mMinTableContentWidth + horBorderPadding;
|
|
}
|
|
}
|
|
else {
|
|
aMaxElementSize->width = mMinTableContentWidth + horBorderPadding;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BasicTableLayoutStrategy::ContinuingFrameCheck()
|
|
{
|
|
#ifdef NS_DEBUG
|
|
nsIFrame* tablePIF = nsnull;
|
|
mTableFrame->GetPrevInFlow(&tablePIF);
|
|
NS_ASSERTION(!tablePIF, "never ever call me on a continuing frame!");
|
|
#endif
|
|
}
|
|
|
|
PRBool BCW_Wrapup(nsIPresContext* aPresContext,
|
|
BasicTableLayoutStrategy* aStrategy,
|
|
nsTableFrame* aTableFrame,
|
|
PRInt32* aAllocTypes)
|
|
{
|
|
if (aAllocTypes)
|
|
delete [] aAllocTypes;
|
|
if (gsDebugBalance) {printf("BalanceColumnWidths ex \n"); aTableFrame->Dump(aPresContext, PR_FALSE, PR_TRUE, PR_FALSE);}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
// The priority of allocations for a given column are as follows
|
|
// 1) max(MIN, MIN_ADJ)
|
|
// 2) max (PCT, PCT_ADJ) go to 7
|
|
// 3) FIX
|
|
// 4) FIX_ADJ, if FIX was not set do DES_CON, go to 7
|
|
// 5) PROportional go to 7
|
|
// 6) max(DES_CON, DES_ADJ)
|
|
// 7) for a fixed width table, the column may get more
|
|
// space if the sum of the col allocations is insufficient
|
|
|
|
PRBool
|
|
BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresContext,
|
|
nsIStyleContext* aTableStyle,
|
|
const nsHTMLReflowState& aReflowState,
|
|
nscoord aMaxWidthIn)
|
|
{
|
|
if (gsDebugBalance) {printf("BalanceColumnWidths en max=%d\n", aMaxWidthIn); mTableFrame->Dump(aPresContext, PR_FALSE, PR_TRUE, PR_FALSE);}
|
|
|
|
ContinuingFrameCheck();
|
|
if (!aTableStyle) {
|
|
NS_ASSERTION(aTableStyle, "bad style arg");
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
|
|
// determine if the table is auto/fixed and get the fixed width if available
|
|
nscoord maxWidth = aMaxWidthIn;
|
|
nscoord specifiedTableWidth = mTableFrame->CalcBorderBoxWidth(aReflowState);
|
|
PRBool tableIsAutoWidth = mTableFrame->IsAutoWidth();
|
|
// a specifiedTableWidth of <= 0 indicates percentage based
|
|
if (!tableIsAutoWidth && (specifiedTableWidth > 0)) {
|
|
maxWidth = PR_MIN(specifiedTableWidth, aMaxWidthIn); // specifiedWidth usually == aMaxWidthIn for fixed table
|
|
}
|
|
// reduce the maxWidth by border and padding, since we will be dealing with content width
|
|
// XXX should this be done in aMaxWidthIn by the caller?
|
|
if (maxWidth != NS_UNCONSTRAINEDSIZE) {
|
|
const nsStyleSpacing* spacing;
|
|
mTableFrame->GetStyleData(eStyleStruct_Spacing, (const nsStyleStruct *&)spacing);
|
|
nsMargin borderPadding;
|
|
spacing->CalcBorderPaddingFor(mTableFrame, borderPadding);
|
|
maxWidth -= borderPadding.left + borderPadding.right;
|
|
maxWidth = PR_MAX(0, maxWidth);
|
|
}
|
|
// initialize the col percent and cell percent values to 0.
|
|
PRInt32 colX;
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
colFrame->SetWidth(PCT, WIDTH_NOT_SET);
|
|
colFrame->SetWidth(PCT_ADJ, WIDTH_NOT_SET);
|
|
}
|
|
// set PCT and PCT_ADJ widths on col frames. An auto table returns
|
|
// a new table width based on percent cells/cols if they exist
|
|
nscoord perAdjTableWidth = 0;
|
|
if ((NS_UNCONSTRAINEDSIZE != maxWidth) || (tableIsAutoWidth)) {
|
|
// for an auto width table, use a large basis just so that the quirky
|
|
// auto table sizing will get as big as it should
|
|
perAdjTableWidth = AssignPercentageColumnWidths(maxWidth - mCellSpacingTotal, tableIsAutoWidth);
|
|
}
|
|
|
|
PRBool recomputedAdjMin = RecomputeAdjMinIfNecessary();
|
|
// set the table's columns to the min width
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
nscoord colMinWidth = colFrame->GetMinWidth();
|
|
mTableFrame->SetColumnWidth(colX, colMinWidth);
|
|
}
|
|
if (recomputedAdjMin) {
|
|
SetMinAndMaxTableContentWidths();
|
|
}
|
|
|
|
// if the max width available is less than the min content width for fixed table, we're done
|
|
if (!tableIsAutoWidth && (maxWidth < mMinTableContentWidth)) {
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, nsnull);
|
|
}
|
|
|
|
// if the max width available is less than the min content width for auto table
|
|
// that had no % cells/cols, we're done
|
|
if (tableIsAutoWidth && (maxWidth < mMinTableContentWidth) && (0 == perAdjTableWidth)) {
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, nsnull);
|
|
}
|
|
|
|
PRInt32 cellSpacingTotal;
|
|
// the following are of size NUM_WIDTHS, but only MIN_CON, DES_CON, FIX, FIX_ADJ, PCT
|
|
// are used and they account for colspan ADJusted values
|
|
PRInt32 totalWidths[NUM_WIDTHS]; // sum of col widths of a particular type
|
|
PRInt32 totalAvailWidths[NUM_WIDTHS];
|
|
PRInt32 totalCounts[NUM_WIDTHS]; // num of cols of a particular type
|
|
PRInt32 minWidths[NUM_WIDTHS];
|
|
PRInt32 num0Proportional;
|
|
|
|
CalculateTotals(cellSpacingTotal, totalCounts, totalWidths, totalAvailWidths, minWidths, num0Proportional);
|
|
// auto width table's adjusted width needs cell spacing
|
|
if (tableIsAutoWidth && perAdjTableWidth > 0) {
|
|
perAdjTableWidth = PR_MIN(perAdjTableWidth + cellSpacingTotal, maxWidth);
|
|
}
|
|
nscoord totalAllocated = totalWidths[MIN_CON] + cellSpacingTotal;
|
|
|
|
// allocate and initialize arrays indicating what col gets set
|
|
PRInt32* allocTypes = new PRInt32[numCols];
|
|
if (!allocTypes) return PR_FALSE;
|
|
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
allocTypes[colX] = -1;
|
|
}
|
|
|
|
// allocate percentage cols
|
|
if (totalCounts[PCT] > 0) {
|
|
if (totalAllocated + totalAvailWidths[PCT] - minWidths[PCT] <= maxWidth) {
|
|
AllocateFully(totalAllocated, allocTypes, PCT);
|
|
//NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
|
|
}
|
|
else {
|
|
AllocateConstrained(maxWidth - totalAllocated, PCT, PR_FALSE, allocTypes);
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes);
|
|
}
|
|
}
|
|
// allocate fixed cols
|
|
if (totalAllocated < maxWidth && totalCounts[FIX] > 0) {
|
|
if (totalAllocated + totalAvailWidths[FIX] - minWidths[FIX] <= maxWidth) {
|
|
AllocateFully(totalAllocated, allocTypes, FIX);
|
|
//NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
|
|
}
|
|
else {
|
|
AllocateConstrained(maxWidth - totalAllocated, FIX, PR_TRUE, allocTypes);
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes);
|
|
}
|
|
}
|
|
// allocate fixed adjusted cols
|
|
if (totalAllocated < maxWidth && totalCounts[FIX_ADJ] > 0) {
|
|
if (totalAllocated + totalAvailWidths[FIX_ADJ] - minWidths[FIX_ADJ] <= maxWidth) {
|
|
AllocateFully(totalAllocated, allocTypes, FIX_ADJ);
|
|
//NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
|
|
}
|
|
else {
|
|
AllocateConstrained(maxWidth - totalAllocated, FIX_ADJ, PR_TRUE, allocTypes);
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes);
|
|
}
|
|
}
|
|
// allocate proportional cols up to their min proportional value
|
|
if (totalAllocated < maxWidth && totalCounts[MIN_PRO] > 0) {
|
|
if (totalAllocated + totalAvailWidths[MIN_PRO] - minWidths[MIN_PRO] <= maxWidth) {
|
|
AllocateFully(totalAllocated, allocTypes, MIN_PRO, PR_FALSE);
|
|
//NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
|
|
}
|
|
else {
|
|
AllocateConstrained(maxWidth - totalAllocated, MIN_PRO, PR_FALSE, allocTypes);
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes);
|
|
}
|
|
}
|
|
|
|
// allocate auto cols, considering even those that are proportional
|
|
if (totalAllocated < maxWidth && totalCounts[DES_CON] > 0) {
|
|
if (totalAllocated + totalAvailWidths[DES_CON] - minWidths[DES_CON]<= maxWidth) {
|
|
AllocateFully(totalAllocated, allocTypes, DES_CON);
|
|
//NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated");
|
|
}
|
|
else {
|
|
AllocateConstrained(maxWidth - totalAllocated, DES_CON, PR_FALSE, allocTypes);
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes);
|
|
}
|
|
}
|
|
|
|
// if this is a nested non auto table and pass1 reflow, we are done
|
|
if ((maxWidth == NS_UNCONSTRAINEDSIZE) && (!tableIsAutoWidth)) {
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes);
|
|
}
|
|
|
|
// allocate the rest unconstrained
|
|
PRBool skip0Proportional = totalCounts[DES_CON] > num0Proportional;
|
|
if ( (tableIsAutoWidth && (perAdjTableWidth - totalAllocated > 0)) ||
|
|
(!tableIsAutoWidth && (totalAllocated < maxWidth)) ) {
|
|
if (totalCounts[PCT] != numCols) {
|
|
//PRBool onlyAuto = (totalCounts[DES_CON] > 0) && !mIsNavQuirksMode;
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
if (PCT == allocTypes[colX]) {
|
|
allocTypes[colX] = -1;
|
|
}
|
|
//else if ((FIX == allocTypes[colX]) && onlyAuto) {
|
|
// allocTypes[colX] = -1;
|
|
//}
|
|
}
|
|
}
|
|
if (tableIsAutoWidth) {
|
|
AllocateUnconstrained(perAdjTableWidth - totalAllocated, allocTypes, skip0Proportional);
|
|
}
|
|
else {
|
|
AllocateUnconstrained(maxWidth - totalAllocated, allocTypes, skip0Proportional);
|
|
}
|
|
}
|
|
// give the proportional cols the rest up to the max width in quirks mode
|
|
else if (tableIsAutoWidth && mIsNavQuirksMode && (totalCounts[MIN_PRO] > 0)) {
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
if (DES_CON != allocTypes[colX]) {
|
|
allocTypes[colX] = -1;
|
|
}
|
|
}
|
|
AllocateUnconstrained(maxWidth - totalAllocated, allocTypes, skip0Proportional);
|
|
}
|
|
|
|
|
|
return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes);
|
|
}
|
|
|
|
|
|
// Allocate aWidthType values to all cols available in aIsAllocated
|
|
void BasicTableLayoutStrategy::AllocateFully(nscoord& aTotalAllocated,
|
|
PRInt32* aAllocTypes,
|
|
PRInt32 aWidthType,
|
|
PRBool aMarkAllocated)
|
|
{
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
for (PRInt32 colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
nscoord oldWidth = mTableFrame->GetColumnWidth(colX);
|
|
nscoord newWidth = colFrame->GetWidth(aWidthType);
|
|
|
|
PRBool useAdj;
|
|
if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame, useAdj)) {
|
|
if (-1 != aAllocTypes[colX]) {
|
|
continue;
|
|
}
|
|
}
|
|
// account for col span overrides with DES_CON and FIX
|
|
if ((DES_CON == aWidthType) && useAdj) {
|
|
newWidth = PR_MAX(newWidth, colFrame->GetWidth(DES_ADJ));
|
|
}
|
|
else if (PCT == aWidthType) {
|
|
newWidth = PR_MAX(newWidth, colFrame->GetWidth(PCT_ADJ));
|
|
}
|
|
if (WIDTH_NOT_SET == newWidth) {
|
|
continue;
|
|
}
|
|
if (newWidth > oldWidth) {
|
|
mTableFrame->SetColumnWidth(colX, newWidth);
|
|
aTotalAllocated += newWidth - oldWidth;
|
|
}
|
|
if (aMarkAllocated) {
|
|
aAllocTypes[colX] = aWidthType;
|
|
}
|
|
}
|
|
}
|
|
|
|
void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32 aAllocAmount,
|
|
PRInt32* aAllocTypes,
|
|
PRBool aSkip0Proportional)
|
|
{
|
|
nscoord divisor = 0;
|
|
PRInt32 numColsAllocated = 0;
|
|
PRInt32 totalAllocated = 0;
|
|
PRInt32 colX;
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
if (-1 != aAllocTypes[colX]) {
|
|
divisor += mTableFrame->GetColumnWidth(colX);
|
|
numColsAllocated++;
|
|
}
|
|
}
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
if (-1 != aAllocTypes[colX]) {
|
|
if (aSkip0Proportional) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
if (e0ProportionConstraint == colFrame->GetConstraint()) {
|
|
continue;
|
|
}
|
|
}
|
|
nscoord oldWidth = mTableFrame->GetColumnWidth(colX);
|
|
float percent = (divisor == 0)
|
|
? (1.0f / ((float)numColsAllocated))
|
|
: ((float)oldWidth) / ((float)divisor);
|
|
nscoord addition = NSToCoordRound(((float)aAllocAmount) * percent);
|
|
if (addition > (aAllocAmount - totalAllocated)) {
|
|
mTableFrame->SetColumnWidth(colX, oldWidth + (aAllocAmount - totalAllocated));
|
|
break;
|
|
}
|
|
mTableFrame->SetColumnWidth(colX, oldWidth + addition);
|
|
totalAllocated += addition;
|
|
}
|
|
}
|
|
}
|
|
|
|
nscoord GetConstrainedWidth(nsTableColFrame* colFrame,
|
|
PRBool aConsiderPct)
|
|
|
|
{
|
|
nscoord conWidth = WIDTH_NOT_SET;
|
|
if (aConsiderPct) {
|
|
conWidth = colFrame->GetPctWidth();
|
|
}
|
|
if (conWidth <= 0 ) {
|
|
conWidth = colFrame->GetFixWidth();
|
|
}
|
|
return conWidth;
|
|
}
|
|
|
|
#define LIMIT_DES 0
|
|
#define LIMIT_CON 1
|
|
#define LIMIT_NONE 2
|
|
|
|
void
|
|
BasicTableLayoutStrategy::ComputeColspanWidths(PRInt32 aWidthIndex,
|
|
nsTableCellFrame* aCellFrame,
|
|
PRInt32 aColIndex,
|
|
PRInt32 aColSpan,
|
|
PRBool aConsiderPct)
|
|
{
|
|
if (!aCellFrame || (aColIndex < 0) || (aColIndex < 0) || (aColSpan < 0)) {
|
|
NS_ASSERTION(PR_FALSE, "ComputeColspanWidths called incorrectly");
|
|
return;
|
|
}
|
|
|
|
nscoord cellWidth = 0;
|
|
if (MIN_CON == aWidthIndex) {
|
|
cellWidth = aCellFrame->GetPass1MaxElementSize().width;
|
|
}
|
|
else if (DES_CON == aWidthIndex) {
|
|
cellWidth = aCellFrame->GetMaximumWidth();
|
|
}
|
|
else { // FIX width
|
|
// see if the cell has a style width specified
|
|
const nsStylePosition* cellPosition;
|
|
aCellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)cellPosition);
|
|
if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit()) {
|
|
// need to add padding into fixed width
|
|
const nsStyleSpacing* spacing;
|
|
aCellFrame->GetStyleData(eStyleStruct_Spacing,(const nsStyleStruct *&)spacing);
|
|
nsMargin paddingMargin;
|
|
spacing->CalcPaddingFor(aCellFrame, paddingMargin);
|
|
nscoord padding = paddingMargin.left + paddingMargin.right;
|
|
cellWidth = cellPosition->mWidth.GetCoordValue() + padding;
|
|
cellWidth = PR_MAX(cellWidth, aCellFrame->GetPass1MaxElementSize().width);
|
|
}
|
|
}
|
|
|
|
if (0 >= cellWidth) {
|
|
return;
|
|
}
|
|
|
|
if (MIN_CON == aWidthIndex) {
|
|
// for min width, first allocate fixed cells up to their fixed value, then if
|
|
// necessary allocate auto cells up to their auto value, then if necessary
|
|
// allocate auto cells.
|
|
for (PRInt32 limitX = LIMIT_CON; limitX <= LIMIT_NONE; limitX++) {
|
|
if (ComputeColspanWidths(aWidthIndex, aCellFrame, cellWidth,
|
|
aColIndex, aColSpan, aConsiderPct, limitX)) {
|
|
return;
|
|
}
|
|
}
|
|
} else {
|
|
// for des width, just allocate auto cells
|
|
ComputeColspanWidths(aWidthIndex, aCellFrame, cellWidth,
|
|
aColIndex, aColSpan, aConsiderPct, LIMIT_NONE);
|
|
}
|
|
}
|
|
|
|
|
|
PRBool
|
|
BasicTableLayoutStrategy::ComputeColspanWidths(PRInt32 aWidthIndex,
|
|
nsTableCellFrame* aCellFrame,
|
|
nscoord aCellWidth,
|
|
PRInt32 aColIndex,
|
|
PRInt32 aColSpan,
|
|
PRBool aConsiderPct,
|
|
PRInt32 aLimitType)
|
|
{
|
|
PRBool result = PR_TRUE;
|
|
// skip DES_CON if there is a FIX since FIX is really the desired size
|
|
//if ((DES_CON == widthX) && (cellWidths[FIX] > 0))
|
|
nscoord spanCellSpacing = 0; // total cell spacing cells being spanned
|
|
nscoord spanTotal = 0; // total width of type aWidthIndex of spanned cells
|
|
nscoord divisorCon = 0; // the sum of constrained (fix or pct if specified) widths of spanned cells
|
|
nscoord divisorDes = 0; // the sum of desired widths
|
|
// the following are only used for MIN_CON calculations. Takings differences between
|
|
// actual and target values allows target values to be reached and not exceeded. This
|
|
// is not as accurate as the method in AllocateConstrained, but it is a lot cheaper.
|
|
nscoord divisorConLimit = 0; // the sum of differences between constrained width and min width
|
|
nscoord divisorDesLimit = 0; // the sum of differences between desired width and min width
|
|
|
|
nscoord spacingX = mTableFrame->GetCellSpacingX();
|
|
PRInt32 spanX;
|
|
// accumulate the various divisors to be used later
|
|
for (spanX = 0; spanX < aColSpan; spanX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX);
|
|
nscoord colWidth = PR_MAX(colFrame->GetWidth(aWidthIndex),
|
|
colFrame->GetWidth(aWidthIndex + NUM_MAJOR_WIDTHS));
|
|
colWidth = PR_MAX(colWidth, 0);
|
|
if (aWidthIndex == DES_CON) {
|
|
nscoord conWidth = GetConstrainedWidth(colFrame, aConsiderPct);
|
|
if (conWidth <= 0) {
|
|
divisorDes += colWidth;
|
|
spanTotal += colWidth;
|
|
}
|
|
else {
|
|
divisorCon += conWidth;
|
|
spanTotal += conWidth;
|
|
}
|
|
}
|
|
else if (aWidthIndex == MIN_CON) {
|
|
nscoord conWidth = GetConstrainedWidth(colFrame, aConsiderPct);
|
|
nscoord desWidth = colFrame->GetDesWidth();
|
|
if (conWidth > 0) {
|
|
divisorCon += conWidth;
|
|
divisorConLimit += PR_MAX(conWidth - colWidth, 0);
|
|
}
|
|
else {
|
|
divisorDes += desWidth;
|
|
divisorDesLimit += PR_MAX(desWidth - colWidth, 0);
|
|
}
|
|
spanTotal += colWidth;
|
|
}
|
|
else { // FIX width
|
|
if (colWidth > 0) {
|
|
divisorCon += colWidth;
|
|
}
|
|
else {
|
|
divisorDes += colFrame->GetDesWidth();
|
|
}
|
|
spanTotal += colWidth;
|
|
}
|
|
if ((spanX > 0) && (mTableFrame->GetNumCellsOriginatingInCol(aColIndex + spanX) > 0)) {
|
|
spanCellSpacing += spacingX;
|
|
}
|
|
}
|
|
|
|
if (MIN_CON != aWidthIndex) {
|
|
aLimitType = LIMIT_NONE; // just in case we were called incorrectly
|
|
}
|
|
else {
|
|
// this method gets called up to 3 times, first to let fixed cols reach their
|
|
// target, 2nd to let auto cols reach their target and 3rd to let auto cols
|
|
// fill out the remainder. Below are some optimizations which can skip steps.
|
|
|
|
// if there are no constrained cols to focus on, focus on auto cols
|
|
if ((LIMIT_CON == aLimitType) && (0 == divisorConLimit)) {
|
|
aLimitType = LIMIT_DES;
|
|
}
|
|
// if there are no auto cols to focus on, focus on nothing
|
|
if ((LIMIT_DES == aLimitType) && (0 == divisorDesLimit)) {
|
|
aLimitType = LIMIT_NONE;
|
|
}
|
|
}
|
|
|
|
nscoord availWidth = aCellWidth - spanTotal - spanCellSpacing;
|
|
// there are 2 cases where the target will not be reached
|
|
if ((LIMIT_CON == aLimitType) && (divisorConLimit < availWidth)) {
|
|
availWidth = divisorConLimit;
|
|
result = PR_FALSE;
|
|
}
|
|
if ((LIMIT_DES == aLimitType) && (divisorDesLimit < availWidth)) {
|
|
availWidth = divisorDesLimit;
|
|
result = PR_FALSE;
|
|
}
|
|
if (availWidth > 0) {
|
|
// get the correct numerator in a similar fashion to getting the divisor
|
|
for (spanX = 0; spanX < aColSpan; spanX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX);
|
|
nscoord colWidth = PR_MAX(colFrame->GetWidth(aWidthIndex),
|
|
colFrame->GetWidth(aWidthIndex + NUM_MAJOR_WIDTHS));
|
|
nscoord minWidth = colFrame->GetMinWidth();
|
|
|
|
colWidth = PR_MAX(colWidth, 0);
|
|
nscoord numeratorDes = 0;
|
|
nscoord numeratorCon = 0;
|
|
nscoord numeratorConLimit = 0;
|
|
nscoord numeratorDesLimit = 0;
|
|
if (aWidthIndex == DES_CON) {
|
|
nscoord conWidth = GetConstrainedWidth(colFrame, aConsiderPct);
|
|
if (conWidth <= 0) {
|
|
numeratorDes = colWidth;
|
|
}
|
|
else {
|
|
numeratorCon = conWidth;
|
|
}
|
|
}
|
|
else if (aWidthIndex == MIN_CON) {
|
|
nscoord conWidth = GetConstrainedWidth(colFrame, aConsiderPct);
|
|
nscoord desWidth = colFrame->GetDesWidth();
|
|
if (conWidth > 0) {
|
|
numeratorCon = conWidth;
|
|
numeratorConLimit = PR_MAX(conWidth - colWidth, 0);
|
|
}
|
|
else {
|
|
numeratorDes = desWidth;
|
|
numeratorDesLimit = PR_MAX(desWidth - colWidth, 0);
|
|
}
|
|
}
|
|
else { // FIX width
|
|
if (colWidth <= 0) {
|
|
numeratorDes = colFrame->GetDesWidth();
|
|
}
|
|
else {
|
|
numeratorCon = colWidth;
|
|
}
|
|
}
|
|
|
|
nscoord divisor;
|
|
nscoord numerator;
|
|
if (divisorDes > 0) { // there were auto cols
|
|
divisor = divisorDes;
|
|
numerator = numeratorDes;
|
|
}
|
|
else { // there were only constrained cols
|
|
divisor = divisorCon;
|
|
numerator = numeratorCon;
|
|
}
|
|
// let constrained cols reach their target
|
|
if (LIMIT_CON == aLimitType) {
|
|
if (divisorConLimit > 0) {
|
|
divisor = divisorConLimit;
|
|
numerator = numeratorConLimit;
|
|
}
|
|
}
|
|
// let auto cols reach their target
|
|
else if (LIMIT_DES == aLimitType) {
|
|
if (divisorDesLimit > 0) {
|
|
divisor = divisorDesLimit;
|
|
numerator = numeratorDesLimit;
|
|
}
|
|
}
|
|
|
|
// calculate the amount of additional width the col will get
|
|
nscoord addition = (0 >= divisor)
|
|
? NSToCoordRound( ((float)availWidth) / ((float)aColSpan) )
|
|
: NSToCoordRound( (((float)numerator) / ((float)divisor)) * availWidth);
|
|
if (addition > 0) {
|
|
nscoord newColAdjWidth = colWidth + addition;
|
|
if (newColAdjWidth > minWidth) {
|
|
if (FIX == aWidthIndex) {
|
|
// a colspan cannot fix a column below what a cell desires on its own
|
|
nscoord desCon = colFrame->GetWidth(DES_CON); // do not consider DES_ADJ
|
|
if (desCon > newColAdjWidth) {
|
|
newColAdjWidth = desCon;
|
|
}
|
|
}
|
|
colFrame->SetWidth(aWidthIndex + NUM_MAJOR_WIDTHS, newColAdjWidth);
|
|
colFrame->SetConstrainingCell(aCellFrame); // XXX is this right?
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// Determine min, desired, fixed, and proportional sizes for the cols and
|
|
// and calculate min/max table width
|
|
PRBool BasicTableLayoutStrategy::AssignPreliminaryColumnWidths(nsIPresContext* aPresContext,
|
|
nscoord aMaxWidth)
|
|
{
|
|
if (gsDebugAssign) {printf("AssignPrelimColWidths en max=%d\n", aMaxWidth); mTableFrame->Dump(aPresContext, PR_FALSE, PR_TRUE, PR_FALSE);}
|
|
PRBool rv = PR_FALSE;
|
|
PRInt32 numRows = mTableFrame->GetRowCount();
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
nscoord spacingX = mTableFrame->GetCellSpacingX();
|
|
PRInt32 colX, rowX;
|
|
mCellSpacingTotal = 0;
|
|
|
|
PRInt32 propTotal = 0; // total of numbers of the type 1*, 2*, etc
|
|
PRInt32 numColsForColsAttr = 0; // Nav Quirks cols attribute for equal width cols
|
|
if (NS_STYLE_TABLE_COLS_NONE != mCols) {
|
|
numColsForColsAttr = (NS_STYLE_TABLE_COLS_ALL == mCols) ? numCols : mCols;
|
|
}
|
|
|
|
// For every column, determine it's min and desired width based on cell style
|
|
// base on cells which do not span cols. Also, determine mCellSpacingTotal
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nscoord minWidth = 0;
|
|
nscoord desWidth = 0;
|
|
nscoord fixWidth = WIDTH_NOT_SET;
|
|
|
|
// Get column frame and reset it
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
NS_ASSERTION(nsnull != colFrame, "bad col frame");
|
|
colFrame->ResetSizingInfo();
|
|
|
|
if (mTableFrame->GetNumCellsOriginatingInCol(colX) > 0) {
|
|
mCellSpacingTotal += spacingX;
|
|
}
|
|
|
|
// Scan the cells in the col that have colspan = 1 and find the maximum
|
|
// min, desired, and fixed cells.
|
|
nsTableCellFrame* fixContributor = nsnull;
|
|
nsTableCellFrame* desContributor = nsnull;
|
|
for (rowX = 0; rowX < numRows; rowX++) {
|
|
PRBool originates;
|
|
PRInt32 colSpan;
|
|
nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
|
|
// skip cells that don't originate at (rowX, colX); colspans are handled in the
|
|
// next pass, row spans don't need to be handled
|
|
if (!cellFrame || !originates || (colSpan > 1)) {
|
|
continue;
|
|
}
|
|
// these values include borders and padding
|
|
minWidth = PR_MAX(minWidth, cellFrame->GetPass1MaxElementSize().width);
|
|
nscoord cellDesWidth = cellFrame->GetMaximumWidth();
|
|
if (cellDesWidth > desWidth) {
|
|
desContributor = cellFrame;
|
|
desWidth = cellDesWidth;
|
|
}
|
|
// see if the cell has a style width specified
|
|
const nsStylePosition* cellPosition;
|
|
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)cellPosition);
|
|
if (eStyleUnit_Coord == cellPosition->mWidth.GetUnit()) {
|
|
nscoord coordValue = cellPosition->mWidth.GetCoordValue();
|
|
if (coordValue > 0) { // ignore if width == 0
|
|
// need to add padding into fixed width
|
|
const nsStyleSpacing* spacing;
|
|
cellFrame->GetStyleData(eStyleStruct_Spacing,(const nsStyleStruct *&)spacing);
|
|
nsMargin paddingMargin;
|
|
spacing->CalcPaddingFor(cellFrame, paddingMargin);
|
|
nscoord newFixWidth = coordValue + paddingMargin.left + paddingMargin.right;
|
|
// 2nd part of condition is Nav Quirk like below
|
|
if ((newFixWidth > fixWidth) || ((newFixWidth == fixWidth) && (desContributor == cellFrame))) {
|
|
fixWidth = newFixWidth;
|
|
fixContributor = cellFrame;
|
|
}
|
|
fixWidth = PR_MAX(fixWidth, minWidth);
|
|
}
|
|
}
|
|
}
|
|
|
|
desWidth = PR_MAX(desWidth, minWidth);
|
|
|
|
// Nav Quirk like above
|
|
if ((fixWidth > 0) && (desWidth > fixWidth) && (fixContributor != desContributor)) {
|
|
fixWidth = WIDTH_NOT_SET;
|
|
fixContributor = nsnull;
|
|
}
|
|
|
|
// cache the computed column info
|
|
colFrame->SetWidth(MIN_CON, minWidth);
|
|
colFrame->SetWidth(DES_CON, desWidth);
|
|
colFrame->SetConstrainingCell(fixContributor);
|
|
if (fixWidth > 0) {
|
|
colFrame->SetWidth(FIX, PR_MAX(fixWidth, minWidth));
|
|
}
|
|
|
|
// determine if there is a proportional column either from html4
|
|
// proportional width on a col or Nav Quirks cols attr
|
|
if (fixWidth <= 0) {
|
|
nscoord proportion = WIDTH_NOT_SET;
|
|
#ifdef foobar
|
|
const nsStylePosition* colPosition;
|
|
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)colPosition);
|
|
if (eStyleUnit_Proportional == colPosition->mWidth.GetUnit()) {
|
|
proportion = colPosition->mWidth.GetIntValue();
|
|
}
|
|
else if (colX < numColsForColsAttr) {
|
|
proportion = 1;
|
|
if ((eStyleUnit_Percent == colPosition->mWidth.GetUnit()) &&
|
|
(colPosition->mWidth.GetPercentValue() > 0.0f)) {
|
|
proportion = WIDTH_NOT_SET;
|
|
}
|
|
}
|
|
#else
|
|
nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
|
|
if (eStyleUnit_Proportional == colStyleWidth.GetUnit()) {
|
|
proportion = colStyleWidth.GetIntValue();
|
|
}
|
|
else if (colX < numColsForColsAttr) {
|
|
proportion = 1;
|
|
if ((eStyleUnit_Percent == colStyleWidth.GetUnit()) &&
|
|
(colStyleWidth.GetPercentValue() > 0.0f)) {
|
|
proportion = WIDTH_NOT_SET;
|
|
}
|
|
}
|
|
#endif
|
|
if (proportion >= 0) {
|
|
colFrame->SetWidth(MIN_PRO, proportion);
|
|
if (proportion > 0) {
|
|
propTotal += proportion;
|
|
colFrame->SetConstraint(eProportionConstraint);
|
|
}
|
|
else {
|
|
colFrame->SetConstraint(e0ProportionConstraint);
|
|
// override the desired, proportional widths
|
|
nscoord colMinWidth = colFrame->GetWidth(MIN_CON);
|
|
colFrame->SetWidth(DES_CON, colMinWidth);
|
|
colFrame->SetWidth(MIN_PRO, colMinWidth);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (mCellSpacingTotal > 0) {
|
|
mCellSpacingTotal += spacingX; // add last cell spacing on right
|
|
}
|
|
|
|
// figure the proportional width for porportional cols
|
|
if (propTotal > 0) {
|
|
nscoord minPropTotal = 0;
|
|
nscoord desPropTotal = 0;
|
|
// figure the totals of all proportional cols which support every min and desired width
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
nscoord colProp = colFrame->GetWidth(MIN_PRO);
|
|
if (colProp > 0) {
|
|
nscoord minWidth = colFrame->GetWidth(MIN_CON);
|
|
nscoord desWidth = colFrame->GetWidth(DES_CON);
|
|
minPropTotal = PR_MAX(minPropTotal, NSToCoordRound(((float)propTotal * minWidth) / (float)colProp));
|
|
desPropTotal = PR_MAX(desPropTotal, NSToCoordRound(((float)propTotal * desWidth) / (float)colProp));
|
|
}
|
|
}
|
|
// store a ratio to allow going from a min to a desired proportional width
|
|
if (minPropTotal > 0) {
|
|
mMinToDesProportionRatio = ((float)desPropTotal) / ((float)minPropTotal);
|
|
}
|
|
// figure the cols proportional min width based on the new totals
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
nscoord colProp = colFrame->GetWidth(MIN_PRO);
|
|
if (colProp > 0) {
|
|
nscoord minProp = NSToCoordRound(((float)colProp * minPropTotal) / (float)propTotal);
|
|
colFrame->SetWidth(MIN_PRO, minProp);
|
|
colFrame->SetWidth(DES_CON, NSToCoordRound(((float)minProp) * mMinToDesProportionRatio));
|
|
}
|
|
}
|
|
}
|
|
|
|
// For each col, consider the cells originating in it with colspans > 1.
|
|
// Adjust the cols that each cell spans if necessary. Iterate backwards
|
|
// so that nested and/or overlaping col spans handle the inner ones first,
|
|
// ensuring more accurated calculations.
|
|
for (colX = numCols - 1; colX >= 0; colX--) {
|
|
for (rowX = 0; rowX < numRows; rowX++) {
|
|
PRBool originates;
|
|
PRInt32 colSpan;
|
|
nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
|
|
if (!originates || (1 == colSpan)) {
|
|
continue;
|
|
}
|
|
// set MIN_ADJ, DES_ADJ, FIX_ADJ
|
|
for (PRInt32 widthX = 0; widthX < NUM_MAJOR_WIDTHS; widthX++) {
|
|
ComputeColspanWidths(widthX, cellFrame, colX, colSpan, PR_FALSE);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Set the col's fixed width if present
|
|
// Set the table col width for each col to the content min.
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
nscoord fixColWidth = colFrame->GetWidth(FIX);
|
|
// use the style width of a col only if the col hasn't gotten a fixed width from any cell
|
|
if (fixColWidth <= 0) {
|
|
#ifdef foobar
|
|
const nsStylePosition* colPosition;
|
|
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
|
|
if (eStyleUnit_Coord == colPosition->mWidth.GetUnit()) {
|
|
fixColWidth = colPosition->mWidth.GetCoordValue();
|
|
if (fixColWidth > 0) {
|
|
colFrame->SetWidth(FIX, fixColWidth);
|
|
}
|
|
}
|
|
#else
|
|
nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
|
|
if (eStyleUnit_Coord == colStyleWidth.GetUnit()) {
|
|
fixColWidth = colStyleWidth.GetCoordValue();
|
|
if (fixColWidth > 0) {
|
|
colFrame->SetWidth(FIX, fixColWidth);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
nscoord minWidth = colFrame->GetMinWidth();
|
|
mTableFrame->SetColumnWidth(colX, minWidth);
|
|
}
|
|
SetMinAndMaxTableContentWidths();
|
|
|
|
if (gsDebugAssign) {printf("AssignPrelimColWidths ex\n"); mTableFrame->Dump(aPresContext, PR_FALSE, PR_TRUE, PR_FALSE);}
|
|
return rv;
|
|
}
|
|
|
|
// Determine percentage col widths for each col frame
|
|
nscoord BasicTableLayoutStrategy::AssignPercentageColumnWidths(nscoord aBasisIn,
|
|
PRBool aTableIsAutoWidth)
|
|
{
|
|
PRInt32 numRows = mTableFrame->GetRowCount();
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
nscoord spacingX = mTableFrame->GetCellSpacingX();
|
|
PRInt32 colX, rowX;
|
|
nscoord basis = aBasisIn;
|
|
// For an auto table, determine the potentially new percent adjusted width based
|
|
// on percent cells/cols. This probably should only be a NavQuirks thing, since
|
|
// a percentage based cell or column on an auto table should force the column to auto
|
|
if (aTableIsAutoWidth) {
|
|
nscoord fixWidthTotal = 0; // total of fixed widths of all cols
|
|
float perTotal = 0.0f; // total of percentage constrained cols and/or cells in cols
|
|
PRInt32 numPerCols = 0; // number of colums that have percentage constraints
|
|
nscoord prefWidthTotal = 0;// total of des/fix widths of cols that don't have percentage constraints
|
|
basis = 0; // basis to use for percentages, computed considering percentage values
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
nscoord colBasis = -1;
|
|
// Scan the cells in the col
|
|
for (rowX = 0; rowX < numRows; rowX++) {
|
|
PRBool originates;
|
|
PRInt32 colSpan;
|
|
nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
|
|
if (!originates) { // skip cells that don't originate in the col
|
|
continue;
|
|
}
|
|
// see if the cell has a style percent width specified
|
|
const nsStylePosition* cellPosition;
|
|
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)cellPosition);
|
|
if (eStyleUnit_Percent == cellPosition->mWidth.GetUnit()) {
|
|
float percent = cellPosition->mWidth.GetPercentValue();
|
|
colBasis = 0;
|
|
if (percent > 0.0f) {
|
|
// calculate the preferred width of the cell based on fixWidth and desWidth
|
|
nscoord cellDesWidth = 0;
|
|
for (PRInt32 spanX = 0; spanX < colSpan; spanX++) {
|
|
nsTableColFrame* spanFrame = mTableFrame->GetColFrame(colX + spanX);
|
|
cellDesWidth += spanFrame->GetDesWidth();
|
|
}
|
|
// figure the basis using the cell's desired width and percent
|
|
colBasis = NSToCoordRound((float)cellDesWidth / percent);
|
|
perTotal += percent;
|
|
}
|
|
}
|
|
}
|
|
if (-1 == colBasis) {
|
|
// see if the col has a style percent width specified
|
|
#ifdef foobar
|
|
const nsStylePosition* colPosition;
|
|
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)colPosition);
|
|
if (eStyleUnit_Percent == colPosition->mWidth.GetUnit()) {
|
|
float percent = colPosition->mWidth.GetPercentValue();
|
|
colBasis = 0;
|
|
if (percent > 0.0f) {
|
|
nscoord desWidth = colFrame->GetDesWidth();
|
|
colBasis = NSToCoordRound((float)desWidth / percent);
|
|
}
|
|
}
|
|
#else
|
|
nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
|
|
if (eStyleUnit_Percent == colStyleWidth.GetUnit()) {
|
|
float percent = colStyleWidth.GetPercentValue();
|
|
colBasis = 0;
|
|
if (percent > 0.0f) {
|
|
nscoord desWidth = colFrame->GetDesWidth();
|
|
colBasis = NSToCoordRound((float)desWidth / percent);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
basis = PR_MAX(basis, colBasis);
|
|
nscoord fixWidth = colFrame->GetFixWidth();
|
|
fixWidthTotal += fixWidth;
|
|
if (colBasis >= 0) {
|
|
numPerCols++;
|
|
}
|
|
else {
|
|
prefWidthTotal += (fixWidth > 0) ? fixWidth : colFrame->GetDesWidth();
|
|
}
|
|
} // end for (colX ..
|
|
// If there are no pct cells or cols, there is nothing to do.
|
|
if (0 == numPerCols) {
|
|
return 0;
|
|
}
|
|
// If there is only one col and it is % based, it won't affect anything
|
|
if ((1 == numCols) && (numCols == numPerCols)) {
|
|
return 0;
|
|
}
|
|
// compute a basis considering total percentages and the desired width of everything else
|
|
if (perTotal < 1.0f) {
|
|
if (perTotal > 0.0f) {
|
|
nscoord otherBasis = NSToCoordRound((float)prefWidthTotal / (1.0f - perTotal));
|
|
basis = PR_MAX(basis, otherBasis);
|
|
}
|
|
}
|
|
else if (prefWidthTotal > 0) { // make the basis as big as possible
|
|
basis = aBasisIn;
|
|
}
|
|
|
|
basis = PR_MAX(basis, fixWidthTotal);
|
|
basis = PR_MIN(basis, aBasisIn);
|
|
}
|
|
|
|
nscoord colPctTotal = 0;
|
|
nscoord* colPcts = new nscoord[numCols];
|
|
if (!colPcts) return 0;
|
|
|
|
// Determine the percentage contribution for cols and for cells with colspan = 1
|
|
// Iterate backwards, similarly to the reasoning in AssignPreliminaryColumnWidths
|
|
for (colX = numCols - 1; colX >= 0; colX--) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
nscoord maxColPctWidth = WIDTH_NOT_SET;
|
|
float maxColPct = 0.0f;
|
|
colPcts[colX] = 0;
|
|
|
|
nsTableCellFrame* percentContributor = nsnull;
|
|
// Scan the cells in the col that have colspan = 1; assign PER widths
|
|
for (rowX = 0; rowX < numRows; rowX++) {
|
|
PRBool originates;
|
|
PRInt32 colSpan;
|
|
nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
|
|
// skip cells that don't originate at (rowX, colX); colspans are handled in the
|
|
// next pass, row spans don't need to be handled
|
|
if (!cellFrame || !originates || (colSpan > 1)) {
|
|
continue;
|
|
}
|
|
// see if the cell has a style percent width specified
|
|
const nsStylePosition* cellPosition;
|
|
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)cellPosition);
|
|
if (eStyleUnit_Percent == cellPosition->mWidth.GetUnit()) {
|
|
float percent = cellPosition->mWidth.GetPercentValue();
|
|
if (percent > maxColPct) {
|
|
maxColPct = percent;
|
|
maxColPctWidth = NSToCoordRound( ((float)basis) * maxColPct );
|
|
percentContributor = cellFrame;
|
|
if (!mIsNavQuirksMode) {
|
|
// need to add padding
|
|
const nsStyleSpacing* spacing;
|
|
cellFrame->GetStyleData(eStyleStruct_Spacing,(const nsStyleStruct *&)spacing);
|
|
nsMargin paddingMargin;
|
|
spacing->CalcPaddingFor(cellFrame, paddingMargin);
|
|
maxColPctWidth += paddingMargin.left + paddingMargin.right;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (WIDTH_NOT_SET == maxColPctWidth) {
|
|
// see if the col has a style percent width specified
|
|
#ifdef foobar
|
|
const nsStylePosition* colPosition;
|
|
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)colPosition);
|
|
if (eStyleUnit_Percent == colPosition->mWidth.GetUnit()) {
|
|
maxColPct = colPosition->mWidth.GetPercentValue();
|
|
maxColPctWidth = NSToCoordRound( ((float)basis) * maxColPct );
|
|
}
|
|
#else
|
|
nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
|
|
if (eStyleUnit_Percent == colStyleWidth.GetUnit()) {
|
|
maxColPct = colStyleWidth.GetPercentValue();
|
|
maxColPctWidth = NSToCoordRound( ((float)basis) * maxColPct );
|
|
}
|
|
#endif
|
|
}
|
|
// conflicting pct/fixed widths are recorded. Nav 4.x may be changing the
|
|
// fixed width value if it exceeds the pct value and not recording the pct
|
|
// value. This is not being done and IE5 doesn't do it either.
|
|
if (maxColPctWidth > 0) {
|
|
nscoord minWidth = colFrame->GetMinWidth();
|
|
if (minWidth > maxColPctWidth) {
|
|
maxColPctWidth = minWidth;
|
|
colPcts[colX] = NSToCoordRound( 100.0f * ((float)maxColPctWidth) / ((float)basis) );
|
|
}
|
|
else {
|
|
colPcts[colX] = NSToCoordRound(maxColPct * 100.0f);
|
|
}
|
|
colFrame->SetWidth(PCT, maxColPctWidth);
|
|
colFrame->SetConstrainingCell(percentContributor);
|
|
colPctTotal += colPcts[colX];
|
|
}
|
|
}
|
|
|
|
// For each col, consider the cells originating in it with colspans > 1.
|
|
// Adjust the cols that each cell spans if necessary.
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
for (rowX = 0; rowX < numRows; rowX++) {
|
|
PRBool originates;
|
|
PRInt32 colSpan;
|
|
nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
|
|
if (!originates || (1 == colSpan)) {
|
|
continue;
|
|
}
|
|
nscoord cellPctWidth = WIDTH_NOT_SET;
|
|
// see if the cell has a style percentage width specified
|
|
const nsStylePosition* cellPosition;
|
|
cellFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct *&)cellPosition);
|
|
float cellPct = 0.0f;
|
|
if (eStyleUnit_Percent == cellPosition->mWidth.GetUnit()) {
|
|
cellPct = cellPosition->mWidth.GetPercentValue();
|
|
cellPctWidth = NSToCoordRound( ((float)basis) * cellPct );
|
|
if (!mIsNavQuirksMode) {
|
|
// need to add padding
|
|
const nsStyleSpacing* spacing;
|
|
cellFrame->GetStyleData(eStyleStruct_Spacing,(const nsStyleStruct *&)spacing);
|
|
nsMargin paddingMargin;
|
|
spacing->CalcPaddingFor(cellFrame, paddingMargin);
|
|
cellPctWidth += paddingMargin.left + paddingMargin.right;
|
|
}
|
|
}
|
|
if (cellPctWidth > 0) {
|
|
nscoord spanCellSpacing = 0;
|
|
nscoord spanTotal = 0;
|
|
nscoord colPctWidthTotal = 0;
|
|
// accumulate the spanTotal as the max of MIN, DES, FIX, PCT
|
|
PRInt32 spanX;
|
|
for (spanX = 0; spanX < colSpan; spanX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX);
|
|
nscoord colPctWidth = colFrame->GetWidth(PCT);
|
|
if (colPctWidth > 0) { // skip pct cols
|
|
colPctWidthTotal += colPctWidth;
|
|
continue;
|
|
}
|
|
nscoord colWidth = PR_MAX(colFrame->GetMinWidth(), colFrame->GetFixWidth());
|
|
colWidth = PR_MAX(colWidth, colFrame->GetDesWidth()); // XXX check this
|
|
//colWidth = PR_MAX(colWidth, colFrame->GetPctWidth());
|
|
spanTotal += colWidth;
|
|
if ((spanX > 0) && (mTableFrame->GetNumCellsOriginatingInCol(colX + spanX) > 0)) {
|
|
spanCellSpacing += spacingX;
|
|
}
|
|
}
|
|
cellPctWidth += spanCellSpacing; // add it back in since it was subtracted from aBasisIn
|
|
if (cellPctWidth <= 0) {
|
|
continue;
|
|
}
|
|
if (colPctWidthTotal < cellPctWidth) {
|
|
// record the percent contributions for the spanned cols
|
|
for (spanX = 0; spanX < colSpan; spanX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX);
|
|
if (colFrame->GetWidth(PCT) > 0) { // skip pct cols
|
|
continue;
|
|
}
|
|
nscoord minWidth = colFrame->GetMinWidth();
|
|
nscoord colWidth = PR_MAX(minWidth, colFrame->GetFixWidth());
|
|
colWidth = PR_MAX(colWidth, colFrame->GetDesWidth()); // XXX check this
|
|
//float colPctAdj = (0 == spanTotal)
|
|
// ? cellPctWidth / ((float) colSpan)
|
|
// : cellPct * ((float)colWidth) / (float)spanTotal;
|
|
float avail = (float)PR_MAX(cellPctWidth - colPctWidthTotal, 0);
|
|
float colPctAdj = (0 == spanTotal)
|
|
? avail / ((float) colSpan)
|
|
: (avail / (float)basis) * (((float)colWidth) / (float)spanTotal);
|
|
if (colPctAdj > 0) {
|
|
nscoord colPctAdjWidth = colFrame->GetWidth(PCT_ADJ);
|
|
nscoord newColPctAdjWidth = NSToCoordRound(colPctAdj * (float)basis);
|
|
if (newColPctAdjWidth > colPctAdjWidth) {
|
|
if (colPctAdjWidth > 0) { // remove its contribution
|
|
colPctTotal -= colPcts[colX + spanX];
|
|
}
|
|
if (minWidth > newColPctAdjWidth) {
|
|
newColPctAdjWidth = minWidth;
|
|
colPcts[colX + spanX] = NSToCoordRound( 100.0f * ((float)newColPctAdjWidth) / ((float)basis) );
|
|
}
|
|
else {
|
|
colPcts[colX + spanX] = NSToCoordRound( 100.0f * colPctAdj );
|
|
}
|
|
if (newColPctAdjWidth > colFrame->GetWidth(PCT)) {
|
|
colFrame->SetWidth(PCT_ADJ, newColPctAdjWidth);
|
|
colFrame->SetConstrainingCell(cellFrame);
|
|
}
|
|
colPctTotal += colPcts[colX + spanX];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} // end for (rowX ..
|
|
} // end for (colX ..
|
|
|
|
// if the percent total went over 100%, adjustments need to be made to right most cols
|
|
if (colPctTotal > 100) {
|
|
for (colX = numCols - 1; colX >= 0; colX--) {
|
|
if (colPcts[colX] > 0) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
nscoord newPct = colPcts[colX] - (colPctTotal - 100);
|
|
if (newPct > 0) { // this col has enough percent alloc to handle it
|
|
nscoord newPctWidth = NSToCoordRound( ((float)basis) * ((float)newPct) / 100.0f );
|
|
newPctWidth = PR_MAX(newPctWidth, colFrame->GetMinWidth());
|
|
// since we don't care which one contributed, set both
|
|
colFrame->SetWidth(PCT, newPctWidth);
|
|
colFrame->SetWidth(PCT_ADJ, newPctWidth);
|
|
break;
|
|
}
|
|
else { // this col cannot handle all the reduction, reduce it down to zero
|
|
colFrame->SetWidth(PCT, WIDTH_NOT_SET);
|
|
colFrame->SetWidth(PCT_ADJ, WIDTH_NOT_SET);
|
|
colPctTotal -= colPcts[colX];
|
|
if (colPctTotal <= 100) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
delete [] colPcts;
|
|
return basis;
|
|
}
|
|
|
|
|
|
// if there are cols with cells originating in them that are not percent based but span
|
|
// cells which are percent based then the ADJ_MIN needs to be recomputed for the spanned cols.
|
|
// this code was derived from AssignPreliminaryColumnWidths
|
|
PRBool BasicTableLayoutStrategy::RecomputeAdjMinIfNecessary()
|
|
{
|
|
PRInt32 numRows = mTableFrame->GetRowCount();
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
PRInt32 colX, rowX, spanX;
|
|
|
|
PRBool spansPercent = PR_FALSE;
|
|
for (colX = numCols - 1; colX >= 0; colX--) {
|
|
for (rowX = 0; rowX < numRows; rowX++) {
|
|
PRBool originates;
|
|
PRInt32 colSpan;
|
|
mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
|
|
if (!originates || (1 == colSpan)) {
|
|
continue;
|
|
}
|
|
PRBool cellSpansPercent = PR_FALSE;
|
|
// see if any of the spanned cols are percent based
|
|
for (spanX = 0; spanX < colSpan; spanX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX);
|
|
if (colFrame->GetWidth(PCT) > 0) {
|
|
cellSpansPercent = PR_TRUE;
|
|
spansPercent = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (cellSpansPercent) {
|
|
for (spanX = 0; spanX < colSpan; spanX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX);
|
|
colFrame->SetWidth(MIN_ADJ, WIDTH_NOT_SET);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!spansPercent) {
|
|
return PR_FALSE;
|
|
}
|
|
|
|
for (colX = numCols - 1; colX >= 0; colX--) {
|
|
for (rowX = 0; rowX < numRows; rowX++) {
|
|
PRBool originates;
|
|
PRInt32 colSpan;
|
|
nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan);
|
|
if (!originates || (1 == colSpan)) {
|
|
continue;
|
|
}
|
|
PRBool cellSpansPercent = PR_FALSE;
|
|
|
|
// see if any of the spanned cols are percent based
|
|
for (spanX = 0; spanX < colSpan; spanX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX);
|
|
if (colFrame->GetWidth(PCT) > 0) {
|
|
cellSpansPercent = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cellSpansPercent) {
|
|
ComputeColspanWidths(MIN_CON, cellFrame, colX, colSpan, PR_TRUE);
|
|
}
|
|
}
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
|
|
void BasicTableLayoutStrategy::SetMinAndMaxTableContentWidths()
|
|
{
|
|
mMinTableContentWidth = 0;
|
|
mMaxTableContentWidth = 0;
|
|
|
|
nscoord spacingX = mTableFrame->GetCellSpacingX();
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
for (PRInt32 colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
mMinTableContentWidth += colFrame->GetMinWidth();
|
|
mMaxTableContentWidth += PR_MAX(colFrame->GetDesWidth(), colFrame->GetFixWidth());
|
|
if (mTableFrame->GetNumCellsOriginatingInCol(colX) > 0) {
|
|
mMaxTableContentWidth += spacingX;
|
|
mMinTableContentWidth += spacingX;
|
|
}
|
|
}
|
|
// if it is not a degenerate table, add the last spacing on the right
|
|
if (mMinTableContentWidth > 0) {
|
|
mMinTableContentWidth += spacingX;
|
|
mMaxTableContentWidth += spacingX;
|
|
}
|
|
}
|
|
|
|
// calculate totals by width type.
|
|
void BasicTableLayoutStrategy::CalculateTotals(PRInt32& aCellSpacing,
|
|
PRInt32* aTotalCounts,
|
|
PRInt32* aTotalWidths,
|
|
PRInt32* aTotalAvailWidths,
|
|
PRInt32* aMinWidths,
|
|
PRInt32& a0ProportionalCount)
|
|
{
|
|
//mTableFrame->Dump(PR_TRUE, PR_FALSE);
|
|
aCellSpacing = 0;
|
|
for (PRInt32 widthX = 0; widthX < NUM_WIDTHS; widthX++) {
|
|
aTotalCounts[widthX] = 0;
|
|
aTotalWidths[widthX] = 0;
|
|
aTotalAvailWidths[widthX] = 0;
|
|
aMinWidths[widthX] = 0;
|
|
}
|
|
a0ProportionalCount = 0;
|
|
|
|
nscoord spacingX = mTableFrame->GetCellSpacingX();
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
for (PRInt32 colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
if (mTableFrame->GetNumCellsOriginatingInCol(colX) > 0) {
|
|
aCellSpacing += spacingX;
|
|
}
|
|
nscoord minCol = colFrame->GetMinWidth();
|
|
aTotalCounts[MIN_CON]++;
|
|
aTotalWidths[MIN_CON] += minCol;
|
|
|
|
if (e0ProportionConstraint == colFrame->GetConstraint()) {
|
|
a0ProportionalCount++;
|
|
}
|
|
|
|
nscoord pct = colFrame->GetPctWidth();
|
|
nscoord fix = colFrame->GetWidth(FIX);
|
|
nscoord fixAdj = colFrame->GetWidth(FIX_ADJ);
|
|
// if there is a pct width then no others are considered
|
|
if (pct > 0) {
|
|
aTotalCounts[PCT]++;
|
|
aTotalWidths[PCT] += pct;
|
|
aTotalAvailWidths[PCT] += pct;
|
|
aMinWidths[PCT] += minCol;
|
|
continue;
|
|
}
|
|
if ((fix > 0) || (fixAdj > 0)) {
|
|
if (fix > 0) {
|
|
aTotalCounts[FIX]++;
|
|
aTotalWidths[FIX] += fix;
|
|
aTotalAvailWidths[FIX] += fix;
|
|
aMinWidths[FIX] += minCol;
|
|
}
|
|
nscoord desCon = colFrame->GetWidth(DES_CON);
|
|
if (fixAdj > 0) {
|
|
if (fixAdj > fix) {
|
|
aTotalCounts[FIX_ADJ]++;
|
|
aTotalWidths[FIX_ADJ] += fixAdj;
|
|
aTotalAvailWidths[FIX_ADJ] += fixAdj;
|
|
if (fix > 0) {
|
|
aTotalAvailWidths[FIX_ADJ] -= fix;
|
|
}
|
|
else { // there was no fix
|
|
aMinWidths[FIX_ADJ] += minCol;
|
|
}
|
|
// include the DES_CON of a FIX_ADJ that has no FIX
|
|
if ((desCon > fixAdj) && (fix <= 0)) {
|
|
aTotalCounts[DES_CON]++;
|
|
aTotalWidths[DES_CON] += desCon;
|
|
aTotalAvailWidths[DES_CON] += desCon;
|
|
if (fixAdj > 0) {
|
|
aTotalAvailWidths[DES_CON] -= fixAdj;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
nscoord desAdj = colFrame->GetWidth(DES_ADJ);
|
|
if ((desAdj > desCon) && (desAdj > fix)) {
|
|
aTotalCounts[DES_CON]++;
|
|
aTotalWidths[DES_CON] += desAdj;
|
|
aTotalAvailWidths[DES_CON] += desAdj - fix;
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (eProportionConstraint == colFrame->GetConstraint()) {
|
|
nscoord minProp = colFrame->GetWidth(MIN_PRO);
|
|
aTotalCounts[MIN_PRO]++;
|
|
aTotalWidths[MIN_PRO] += minProp;
|
|
aTotalAvailWidths[MIN_PRO] = aTotalWidths[MIN_PRO];
|
|
aTotalCounts[DES_CON]++;
|
|
aTotalWidths[DES_CON] += NSToCoordRound(((float)minProp) * mMinToDesProportionRatio);
|
|
aTotalAvailWidths[DES_CON] = aTotalWidths[DES_CON];
|
|
aMinWidths[MIN_PRO] += minCol;
|
|
aMinWidths[DES_CON] += minProp;
|
|
continue;
|
|
}
|
|
|
|
// desired alone is lowest priority
|
|
aTotalCounts[DES_CON]++;
|
|
aTotalWidths[DES_CON] += colFrame->GetDesWidth();
|
|
aTotalAvailWidths[DES_CON] = aTotalWidths[DES_CON];
|
|
aMinWidths[DES_CON] += minCol;
|
|
}
|
|
// if it is not a degenerate table, add the last spacing on the right
|
|
if (numCols > 0) {
|
|
aCellSpacing += spacingX;
|
|
}
|
|
}
|
|
|
|
|
|
struct nsColInfo {
|
|
nsColInfo(nsTableColFrame* aFrame,
|
|
PRInt32 aIndex,
|
|
PRInt32 aMinWidth,
|
|
PRInt32 aWidth,
|
|
PRInt32 aMaxWidth)
|
|
: mFrame(aFrame), mIndex(aIndex), mMinWidth(aMinWidth),
|
|
mWidth(aWidth), mMaxWidth(aMaxWidth), mWeight(0)
|
|
{}
|
|
nsTableColFrame* mFrame;
|
|
PRInt32 mIndex;
|
|
PRInt32 mMinWidth;
|
|
PRInt32 mWidth;
|
|
PRInt32 mMaxWidth;
|
|
float mWeight;
|
|
};
|
|
|
|
void
|
|
AC_Wrapup(nsTableFrame* aTableFrame,
|
|
PRInt32 aNumItems,
|
|
nsColInfo** aColInfo,
|
|
PRBool aAbort = PR_FALSE)
|
|
{
|
|
if (aColInfo) {
|
|
for (PRInt32 i = 0; i < aNumItems; i++) {
|
|
if (aColInfo[i]) {
|
|
if (!aAbort) {
|
|
aTableFrame->SetColumnWidth(aColInfo[i]->mIndex, aColInfo[i]->mWidth);
|
|
}
|
|
delete aColInfo[i];
|
|
}
|
|
}
|
|
delete [] aColInfo;
|
|
}
|
|
}
|
|
|
|
void
|
|
AC_Increase(PRInt32 aNumAutoCols,
|
|
nsColInfo** aColInfo,
|
|
PRInt32 aDivisor,
|
|
PRInt32& aAvailWidth)
|
|
{
|
|
for (PRInt32 i = 0; i < aNumAutoCols; i++) {
|
|
if ((aAvailWidth <= 0) || (aDivisor <= 0)) {
|
|
break;
|
|
}
|
|
float percent = ((float)aColInfo[i]->mMaxWidth) / (float)aDivisor;
|
|
aDivisor -= aColInfo[i]->mMaxWidth;
|
|
nscoord addition = NSToCoordRound(((float)(aAvailWidth)) * percent);
|
|
// if its the last col, try to give what's left to it
|
|
if ((i == aNumAutoCols - 1) && (addition < aAvailWidth)) {
|
|
addition = aAvailWidth;
|
|
}
|
|
// don't let the addition exceed what is available to add
|
|
addition = PR_MIN(addition, aAvailWidth);
|
|
// don't go over the col max
|
|
addition = PR_MIN(addition, aColInfo[i]->mMaxWidth - aColInfo[i]->mWidth);
|
|
aColInfo[i]->mWidth += addition;
|
|
aAvailWidth -= addition;
|
|
}
|
|
}
|
|
|
|
void
|
|
AC_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]->mMaxWidth) / (float)aDivisor;
|
|
aDivisor -= aColInfo[i]->mMaxWidth;
|
|
nscoord reduction = NSToCoordRound(((float)(aExcess)) * percent);
|
|
// if its the last col, try to remove the remaining excess from it
|
|
if ((i == aNumAutoCols - 1) && (reduction < aExcess)) {
|
|
reduction = aExcess;
|
|
}
|
|
// don't let the reduction exceed what is available to reduce
|
|
reduction = PR_MIN(reduction, aExcess);
|
|
// don't go under the col min
|
|
reduction = PR_MIN(reduction, aColInfo[i]->mWidth - aColInfo[i]->mMinWidth);
|
|
aColInfo[i]->mWidth -= reduction;
|
|
aExcess -= reduction;
|
|
}
|
|
}
|
|
|
|
void
|
|
AC_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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// this assumes that the table has set the width for each col to be its min
|
|
void BasicTableLayoutStrategy::AllocateConstrained(PRInt32 aAvailWidth,
|
|
PRInt32 aWidthType,
|
|
PRBool aStartAtMin,
|
|
PRInt32* aAllocTypes)
|
|
{
|
|
if ((0 == aAvailWidth) || (aWidthType < 0) || (aWidthType >= NUM_WIDTHS)) {
|
|
NS_ASSERTION(PR_TRUE, "invalid args to AllocateConstrained");
|
|
return;
|
|
}
|
|
|
|
PRInt32 numCols = mTableFrame->GetColCount();
|
|
PRInt32 numConstrainedCols = 0;
|
|
nscoord sumMaxConstraints = 0;
|
|
PRBool useAdj = PR_TRUE;
|
|
PRInt32 colX;
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame, useAdj)) {
|
|
if (-1 != aAllocTypes[colX]) {
|
|
continue;
|
|
}
|
|
}
|
|
numConstrainedCols++;
|
|
nscoord width = colFrame->GetWidth(aWidthType);
|
|
if ((DES_CON == aWidthType) && useAdj) {
|
|
width = PR_MAX(width, colFrame->GetWidth(DES_ADJ));
|
|
}
|
|
else if (PCT == aWidthType) {
|
|
width = PR_MAX(width, colFrame->GetWidth(PCT_ADJ));
|
|
}
|
|
sumMaxConstraints += width;
|
|
}
|
|
|
|
|
|
// allocate storage for the affected cols. Only they get adjusted.
|
|
nsColInfo** colInfo = new nsColInfo*[numConstrainedCols];
|
|
if (!colInfo) return;
|
|
memset(colInfo, 0, numConstrainedCols * sizeof(nsColInfo *));
|
|
|
|
PRInt32 maxMinDiff = 0;
|
|
PRInt32 constrColX = 0;
|
|
// set the col info entries for each constrained col
|
|
for (colX = 0; colX < numCols; colX++) {
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX);
|
|
if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame, useAdj)) {
|
|
if (-1 != aAllocTypes[colX]) {
|
|
continue;
|
|
}
|
|
}
|
|
nscoord minWidth = mTableFrame->GetColumnWidth(colX);
|
|
nscoord maxWidth = colFrame->GetWidth(aWidthType);
|
|
if ((DES_CON == aWidthType) && useAdj) {
|
|
maxWidth = PR_MAX(maxWidth, colFrame->GetWidth(DES_ADJ));
|
|
}
|
|
else if (PCT == aWidthType) {
|
|
maxWidth = PR_MAX(maxWidth, colFrame->GetWidth(PCT_ADJ));
|
|
}
|
|
if (maxWidth <= 0) {
|
|
continue;
|
|
}
|
|
|
|
maxWidth = PR_MAX(maxWidth, minWidth);
|
|
maxMinDiff += maxWidth - minWidth;
|
|
nscoord startWidth = (aStartAtMin) ? minWidth : maxWidth;
|
|
colInfo[constrColX] = new nsColInfo(colFrame, colX, minWidth, startWidth, maxWidth);
|
|
if (!colInfo[constrColX]) {
|
|
AC_Wrapup(mTableFrame, numConstrainedCols, colInfo, PR_TRUE);
|
|
return;
|
|
}
|
|
aAllocTypes[colX] = aWidthType;
|
|
constrColX++;
|
|
}
|
|
|
|
if (constrColX < numConstrainedCols) {
|
|
// some of the constrainted cols might have been 0 and skipped
|
|
numConstrainedCols = constrColX;
|
|
}
|
|
|
|
PRInt32 i;
|
|
if (aStartAtMin) { // allocate extra space
|
|
nscoord availWidth = aAvailWidth;
|
|
for (i = 0; i < numConstrainedCols; i++) {
|
|
// the weight here is a relative metric for determining when cols reach their max constraint.
|
|
// 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.
|
|
AC_Sort(colInfo, numConstrainedCols);
|
|
|
|
// 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
|
|
AC_Increase(numConstrainedCols, colInfo, sumMaxConstraints, availWidth);
|
|
}
|
|
else { // reduce each col width
|
|
nscoord reduceWidth = maxMinDiff - aAvailWidth;
|
|
if (reduceWidth < 0) {
|
|
NS_ASSERTION(PR_TRUE, "AllocateConstrained called incorrectly");
|
|
AC_Wrapup(mTableFrame, numConstrainedCols, colInfo);
|
|
return;
|
|
}
|
|
for (i = 0; i < numConstrainedCols; 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
|
|
AC_Sort(colInfo, numConstrainedCols);
|
|
|
|
// 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
|
|
AC_Decrease(numConstrainedCols, colInfo, sumMaxConstraints, reduceWidth);
|
|
}
|
|
AC_Wrapup(mTableFrame, numConstrainedCols, colInfo);
|
|
}
|
|
|
|
// XXX this function will be improved after the colspan algorithms have been extracted
|
|
// from AssignPreliminarColumnWidths and AssignPercentageColumnWidths. For now, pessimistic
|
|
// assumptions are made
|
|
PRBool BasicTableLayoutStrategy::ColumnsCanBeInvalidatedBy(nsStyleCoord* aPrevStyleWidth,
|
|
const nsTableCellFrame& aCellFrame) const
|
|
{
|
|
if (!mTableFrame)
|
|
return PR_TRUE;
|
|
|
|
const nsStylePosition* cellPosition;
|
|
aCellFrame.GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)cellPosition);
|
|
const nsStyleCoord& styleWidth = cellPosition->mWidth;
|
|
|
|
PRInt32 colIndex;
|
|
aCellFrame.GetColIndex(colIndex);
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colIndex);
|
|
nscoord colSpan = mTableFrame->GetEffectiveColSpan(aCellFrame);
|
|
|
|
if (aPrevStyleWidth) {
|
|
nsTableColFrame* colSpanFrame = colFrame;
|
|
// see if this cell is responsible for setting a fixed or percentage based col
|
|
for (PRInt32 span = 1; span <= colSpan; span++) {
|
|
if (&aCellFrame == colSpanFrame->GetConstrainingCell())
|
|
return PR_TRUE; // assume that the style change will affect cols
|
|
if (span < colSpan)
|
|
colSpanFrame = mTableFrame->GetColFrame(colIndex + span);
|
|
}
|
|
// if we get here, the cell was not responsible for a fixed or percentage col
|
|
switch(aPrevStyleWidth->GetUnit()) {
|
|
case eStyleUnit_Percent:
|
|
if (eStyleUnit_Percent == styleWidth.GetUnit()) {
|
|
// PCT to PCT
|
|
if (aPrevStyleWidth->GetPercentValue() < styleWidth.GetPercentValue())
|
|
return PR_TRUE; // XXX see comments above
|
|
}
|
|
// PCT to FIX and PCT to AUTO changes have no effect since PCT allocations
|
|
// are the highest priority and the cell's previous PCT value did not
|
|
// cause it to be responsible for setting any cells PCT_ADJ
|
|
case eStyleUnit_Coord:
|
|
if (eStyleUnit_Percent == styleWidth.GetUnit()) {
|
|
// FIX to PCT
|
|
return PR_TRUE; // XXX see comments above
|
|
}
|
|
else if (eStyleUnit_Coord == styleWidth.GetUnit()) {
|
|
// FIX to FIX
|
|
nscoord newWidth = styleWidth.GetCoordValue();
|
|
if (aPrevStyleWidth->GetCoordValue() < newWidth) {
|
|
if (colSpan > 1)
|
|
return PR_TRUE; // XXX see comments above
|
|
if (newWidth > colFrame->GetFixWidth())
|
|
return PR_TRUE; // XXX see comments above
|
|
}
|
|
}
|
|
// FIX to AUTO results in no column changes here
|
|
case eStyleUnit_Auto:
|
|
if (eStyleUnit_Percent == styleWidth.GetUnit()) {
|
|
// AUTO to PCT
|
|
return PR_TRUE; // XXX see comments above
|
|
}
|
|
else if (eStyleUnit_Coord == styleWidth.GetUnit()) {
|
|
// AUTO to FIX
|
|
return PR_TRUE; // XXX see comments above
|
|
}
|
|
// AUTO to AUTO is not a style change
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
|
|
// XXX this function will be improved after the colspan algorithms have been extracted
|
|
// from AssignPreliminarColumnWidths and AssignPercentageColumnWidths. For now, pessimistic
|
|
// assumptions are made
|
|
PRBool BasicTableLayoutStrategy::ColumnsCanBeInvalidatedBy(const nsTableCellFrame& aCellFrame,
|
|
PRBool aConsiderMinWidth) const
|
|
{
|
|
if (aConsiderMinWidth || !mTableFrame)
|
|
return PR_TRUE;
|
|
|
|
PRInt32 colIndex;
|
|
aCellFrame.GetColIndex(colIndex);
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colIndex);
|
|
nscoord colSpan = mTableFrame->GetEffectiveColSpan(aCellFrame);
|
|
|
|
// check to see if DES_CON can affect columns
|
|
nsTableColFrame* spanFrame = colFrame;
|
|
for (PRInt32 span = 0; span < colSpan; span++) {
|
|
// see if the column width is constrained
|
|
if ((spanFrame->GetPctWidth() > 0) || (spanFrame->GetFixWidth() > 0) ||
|
|
(spanFrame->GetWidth(MIN_PRO) > 0)) {
|
|
if ((spanFrame->GetWidth(PCT_ADJ) > 0) && (spanFrame->GetWidth(PCT) <= 0)) {
|
|
return PR_TRUE;
|
|
}
|
|
if ((spanFrame->GetWidth(FIX_ADJ) > 0) && (spanFrame->GetWidth(FIX) <= 0)) {
|
|
return PR_TRUE; // its unfortunate that the balancing algorithms cause this
|
|
}
|
|
}
|
|
else {
|
|
return PR_TRUE; // XXX need to add cases if table has coord width specified
|
|
}
|
|
if (span < colSpan - 1)
|
|
spanFrame = mTableFrame->GetColFrame(colIndex + span + 1);
|
|
}
|
|
return PR_FALSE;
|
|
}
|
|
|
|
PRBool BasicTableLayoutStrategy::ColumnsAreValidFor(const nsTableCellFrame& aCellFrame,
|
|
nscoord aPrevCellMin,
|
|
nscoord aPrevCellDes) const
|
|
{
|
|
PRInt32 colIndex;
|
|
aCellFrame.GetColIndex(colIndex);
|
|
nsTableColFrame* colFrame = mTableFrame->GetColFrame(colIndex);
|
|
nscoord colSpan = mTableFrame->GetEffectiveColSpan(aCellFrame);
|
|
|
|
nscoord cellMin = aCellFrame.GetPass1MaxElementSize().width;
|
|
nscoord cellDes = aCellFrame.GetMaximumWidth();
|
|
nscoord colMin = colFrame->GetMinWidth();
|
|
nscoord colDes = colFrame->GetDesWidth();
|
|
|
|
PRBool minChanged = PR_TRUE;
|
|
if (((cellMin > aPrevCellMin) && (cellMin <= colMin)) ||
|
|
((cellMin <= aPrevCellMin) && (aPrevCellMin <= colMin))) {
|
|
minChanged = PR_FALSE;
|
|
}
|
|
if (minChanged) {
|
|
return PR_FALSE; // XXX add cases where table has coord width and cell is constrained
|
|
}
|
|
|
|
PRBool desChanged = PR_TRUE;
|
|
if ((cellDes > aPrevCellDes) && (cellDes <= colDes)) {
|
|
// XXX This next check causes a problem if the cell's desired width shrinks,
|
|
// because the comparison (aPresCellDes <= colDes) will always be TRUE and
|
|
// so we always end up setting desChanged to PR_FALSE. That means the column
|
|
// won't shrink like it should
|
|
#if 0
|
|
|| ((cellDes <= aPrevCellDes) && (aPrevCellDes <= colDes))) {
|
|
#endif
|
|
desChanged = PR_FALSE;
|
|
}
|
|
|
|
if (1 == colSpan) {
|
|
// see if the column width is constrained
|
|
if ((colFrame->GetPctWidth() > 0) || (colFrame->GetFixWidth() > 0) ||
|
|
(colFrame->GetWidth(MIN_PRO) > 0)) {
|
|
if ((colFrame->GetWidth(PCT_ADJ) > 0) && (colFrame->GetWidth(PCT) <= 0)) {
|
|
if (desChanged) {
|
|
return PR_FALSE; // XXX add cases where table has coord width
|
|
}
|
|
}
|
|
if ((colFrame->GetWidth(FIX_ADJ) > 0) && (colFrame->GetWidth(FIX) <= 0)) {
|
|
if (desChanged) {
|
|
return PR_FALSE; // its unfortunate that the balancing algorithms cause this
|
|
// XXX add cases where table has coord width
|
|
}
|
|
}
|
|
}
|
|
else { // the column width is not constrained
|
|
if (desChanged) {
|
|
return PR_FALSE;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
return PR_FALSE; // XXX this needs a lot of cases
|
|
}
|
|
return PR_TRUE;
|
|
}
|
|
|
|
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);
|
|
#ifdef foobar
|
|
const nsStylePosition* colPosition;
|
|
colFrame->GetStyleData(eStyleStruct_Position, (const nsStyleStruct*&)colPosition);
|
|
switch (colPosition->mWidth.GetUnit()) {
|
|
#else
|
|
nsStyleCoord colStyleWidth = colFrame->GetStyleWidth();
|
|
switch (colStyleWidth.GetUnit()) {
|
|
#endif
|
|
case eStyleUnit_Coord:
|
|
if (0 == colStyleWidth.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 = colStyleWidth.GetPercentValue();
|
|
if (0.0f == percent || 0.01f == percent) {
|
|
result = PR_TRUE;
|
|
}
|
|
break;
|
|
}
|
|
case eStyleUnit_Proportional:
|
|
if (0 == colStyleWidth.GetIntValue()) {
|
|
result = PR_TRUE;
|
|
}
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void BasicTableLayoutStrategy::Dump(PRInt32 aIndent)
|
|
{
|
|
char* indent = new char[aIndent + 1];
|
|
for (PRInt32 i = 0; i < aIndent + 1; i++) {
|
|
indent[i] = ' ';
|
|
}
|
|
indent[aIndent] = 0;
|
|
|
|
printf("%s**START BASIC STRATEGY DUMP** table=%p cols=%X",
|
|
indent, mTableFrame, mCols);
|
|
printf("\n%s minConWidth=%d maxConWidth=%d cellSpacing=%d propRatio=%.2f navQuirks=%d",
|
|
indent, mMinTableContentWidth, mMaxTableContentWidth, mCellSpacingTotal, mMinToDesProportionRatio, mIsNavQuirksMode);
|
|
printf(" **END BASIC STRATEGY DUMP** \n");
|
|
delete [] indent;
|
|
}
|
|
|