/* -*- 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 "nsIPresContext.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 static PRInt32 gsDebugCount = 0; // The priority of allocations for columns is as follows // 1) max(MIN, MIN_ADJ) // 2) max (PCT, PCT_ADJ) // 3) FIX // 4) FIX_ADJ // 5) max(DES_CON, DES_ADJ), but use MIN_PRO if present // 6) for a fixed width table, the column may get more // space if the sum of the col allocations is insufficient // the logic here is kept in synch with that in CalculateTotals. PRBool CanAllocate(PRInt32 aType, PRInt32 aPrevType, nsTableColFrame* aColFrame) { switch(aType) { case PCT: case FIX: case DES_CON: return (WIDTH_NOT_SET == aPrevType); case FIX_ADJ: return (WIDTH_NOT_SET == aPrevType) || (FIX == aPrevType); default: NS_ASSERTION(PR_FALSE, "invalid call"); } 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; mCellSpacingTotal = 0; mIsNavQuirksMode = aIsNavQuirks; } BasicTableLayoutStrategy::~BasicTableLayoutStrategy() { MOZ_COUNT_DTOR(BasicTableLayoutStrategy); } PRBool BasicTableLayoutStrategy::Initialize(nsIPresContext* aPresContext, nsSize* aMaxElementSize, nscoord aMaxWidth, const nsHTMLReflowState& aReflowState) { ContinuingFrameCheck(); PRBool result = PR_TRUE; // re-init instance variables mCellSpacingTotal = 0; mCols = mTableFrame->GetEffectiveCOLSAttribute(); // assign the width of all fixed-width columns float p2t; aPresContext->GetScaledPixelsToTwips(&p2t); AssignNonPctColumnWidths(aPresContext, aMaxWidth, aReflowState, p2t); // set aMaxElementSize here because we compute mMinTableWidth in AssignNonPctColumnWidths if (nsnull != aMaxElementSize) { SetMaxElementSize(aMaxElementSize, aReflowState.mComputedPadding); } return result; } void BasicTableLayoutStrategy::SetMaxElementSize(nsSize* aMaxElementSize, const nsMargin& aPadding) { if (nsnull != aMaxElementSize) { aMaxElementSize->height = 0; nsMargin borderPadding; const nsStylePosition* tablePosition; mTableFrame->GetStyleData(eStyleStruct_Position, ((const nsStyleStruct *&)tablePosition)); mTableFrame->GetTableBorder(borderPadding); borderPadding += aPadding; nscoord horBorderPadding = borderPadding.left + borderPadding.right; nscoord minTableWidth = GetTableMinWidth(); if (tablePosition->mWidth.GetUnit() == eStyleUnit_Coord) { aMaxElementSize->width = tablePosition->mWidth.GetCoordValue(); if (minTableWidth + horBorderPadding > aMaxElementSize->width) { aMaxElementSize->width = minTableWidth + horBorderPadding; } } else { aMaxElementSize->width = minTableWidth + 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; } void ResetPctValues(nsTableFrame* aTableFrame, PRInt32 aNumCols) { // initialize the col percent and cell percent values to 0. PRInt32 colX; for (colX = 0; colX < aNumCols; colX++) { nsTableColFrame* colFrame = aTableFrame->GetColFrame(colX); if (colFrame) { colFrame->SetWidth(PCT, WIDTH_NOT_SET); colFrame->SetWidth(PCT_ADJ, WIDTH_NOT_SET); } } } PRBool BasicTableLayoutStrategy::BalanceColumnWidths(nsIPresContext* aPresContext, nsIStyleContext* aTableStyle, const nsHTMLReflowState& aReflowState, nscoord aMaxWidthIn) { if (gsDebugBalance) {printf("BalanceColumnWidths en max=%d count=%d \n", aMaxWidthIn, gsDebugCount++); mTableFrame->Dump(aPresContext, PR_FALSE, PR_TRUE, PR_FALSE);} float p2t; aPresContext->GetScaledPixelsToTwips(&p2t); 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) { maxWidth -= aReflowState.mComputedBorderPadding.left + aReflowState.mComputedBorderPadding.right; maxWidth = PR_MAX(0, maxWidth); } // initialize the col percent and cell percent values to 0. ResetPctValues(mTableFrame, numCols); // 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 nscoord basis = (NS_UNCONSTRAINEDSIZE == maxWidth) ? NS_UNCONSTRAINEDSIZE : maxWidth - mCellSpacingTotal; // this may have to reallocate MIN_ADJ, FIX_ADJ, DES_ADJ if there are // cells spanning cols which have PCT values perAdjTableWidth = AssignPctColumnWidths(aReflowState, basis, tableIsAutoWidth, p2t); } // set the table's columns to the min width PRInt32 colX; for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; nscoord colMinWidth = colFrame->GetMinWidth(); mTableFrame->SetColumnWidth(colX, colMinWidth); } // if the max width available is less than the min content width for fixed table, we're done nscoord minTableWidth = GetTableMinWidth(); if (!tableIsAutoWidth && (maxWidth < minTableWidth)) { 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 < minTableWidth) && (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 totalCounts[NUM_WIDTHS]; // num of cols of a particular type PRInt32 dupedWidths[NUM_WIDTHS]; PRInt32 num0Proportional; CalculateTotals(cellSpacingTotal, totalCounts, totalWidths, dupedWidths, num0Proportional); // auto width table's adjusted width needs cell spacing if (tableIsAutoWidth && perAdjTableWidth > 0) { perAdjTableWidth = PR_MIN(perAdjTableWidth + cellSpacingTotal, maxWidth); maxWidth = perAdjTableWidth; } 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 PCT/PCT_ADJ cols if (totalCounts[PCT] > 0) { if (totalAllocated + totalWidths[PCT] - dupedWidths[PCT] <= maxWidth) { AllocateFully(totalAllocated, allocTypes, PCT); //NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated"); } else { AllocateConstrained(maxWidth - totalAllocated, PCT, PR_FALSE, allocTypes, p2t); return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes); } } // allocate FIX cols if ((totalAllocated < maxWidth) && (totalCounts[FIX] > 0)) { if (totalAllocated + totalWidths[FIX] - dupedWidths[FIX] <= maxWidth) { AllocateFully(totalAllocated, allocTypes, FIX); //NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated"); } else { AllocateConstrained(maxWidth - totalAllocated, FIX, PR_TRUE, allocTypes, p2t); return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes); } } // allocate fixed adjusted cols if ((totalAllocated < maxWidth) && (totalCounts[FIX_ADJ] > 0)) { if (totalAllocated + totalWidths[FIX_ADJ] - dupedWidths[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, p2t); return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes); } } // allocate proportional and auto cols together if ((totalAllocated < maxWidth) && (totalCounts[MIN_PRO] + totalCounts[DES_CON] > 0)) { if (totalAllocated + totalWidths[MIN_PRO] - dupedWidths[MIN_PRO] + totalWidths[DES_CON] - dupedWidths[DES_CON] <= maxWidth) { AllocateFully(totalAllocated, allocTypes, DES_CON); //NS_WARN_IF_FALSE(totalAllocated <= maxWidth, "over allocated"); } else { AllocateConstrained(maxWidth - totalAllocated, DES_CON, PR_TRUE, allocTypes, p2t); 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 to auto columns, with some exceptions if ( (tableIsAutoWidth && (perAdjTableWidth - totalAllocated > 0)) || (!tableIsAutoWidth && (totalAllocated < maxWidth)) ) { PRBool excludePct = (totalCounts[PCT] != numCols); PRBool excludeFix = (totalCounts[PCT] + totalCounts[FIX] < numCols); PRBool excludePro = (totalCounts[DES_CON] > 0); PRBool exclude0Pro = (totalCounts[MIN_PRO] != num0Proportional); if (tableIsAutoWidth) { AllocateUnconstrained(perAdjTableWidth - totalAllocated, allocTypes, excludePct, excludeFix, excludePro, exclude0Pro, p2t); } else { AllocateUnconstrained(maxWidth - totalAllocated, allocTypes, excludePct, excludeFix, excludePro, exclude0Pro, p2t); } } return BCW_Wrapup(aPresContext, this, mTableFrame, allocTypes); } nscoord GetColWidth(nsTableColFrame* aColFrame, PRInt32 aWidthType) { switch(aWidthType) { case DES_CON: return aColFrame->GetDesWidth(); case FIX: case FIX_ADJ: return aColFrame->GetWidth(aWidthType); case PCT: return aColFrame->GetPctWidth(); default: NS_ASSERTION(PR_FALSE, "invalid call"); return WIDTH_NOT_SET; } } // Allocate aWidthType values to all cols available in aIsAllocated void BasicTableLayoutStrategy::AllocateFully(nscoord& aTotalAllocated, PRInt32* aAllocTypes, PRInt32 aWidthType) { PRInt32 numCols = mTableFrame->GetColCount(); for (PRInt32 colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; } nscoord oldWidth = mTableFrame->GetColumnWidth(colX); nscoord newWidth = GetColWidth(colFrame, aWidthType); // proportional and desired widths are handled together PRBool haveProWidth = PR_FALSE; if (DES_CON == aWidthType) { nscoord proWidth = colFrame->GetWidth(MIN_PRO); if (proWidth >= 0) { haveProWidth = PR_TRUE; newWidth = proWidth; } } if (WIDTH_NOT_SET == newWidth) continue; if (newWidth > oldWidth) { mTableFrame->SetColumnWidth(colX, newWidth); aTotalAllocated += newWidth - oldWidth; } aAllocTypes[colX] = (haveProWidth) ? MIN_PRO : aWidthType; } } // This method is the last allocation method to get called, but just in case // code gets added later after, set the allocated cols to some final value. #define FINISHED 99 void BasicTableLayoutStrategy::AllocateUnconstrained(PRInt32 aAllocAmount, PRInt32* aAllocTypes, PRBool aExcludePct, PRBool aExcludeFix, PRBool aExcludePro, PRBool aExclude0Pro, float aPixelToTwips) { // set up allocTypes to exclude anything but auto cols if possible PRInt32 colX; PRInt32 numCols = mTableFrame->GetColCount(); for (colX = 0; colX < numCols; colX++) { if (aExcludePct && (PCT == aAllocTypes[colX])) { aAllocTypes[colX] = FINISHED; } else if (aExcludeFix && ((FIX == aAllocTypes[colX]) || (FIX_ADJ == aAllocTypes[colX]))) { aAllocTypes[colX] = FINISHED; } else if (MIN_PRO == aAllocTypes[colX]) { if (aExcludePro) { aAllocTypes[colX] = FINISHED; } else { if (aExclude0Pro) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (colFrame->GetConstraint() == e0ProportionConstraint) { aAllocTypes[colX] = FINISHED; } } } } } nscoord divisor = 0; PRInt32 numColsAllocated = 0; PRInt32 totalAllocated = 0; for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); PRBool skipColumn = aExclude0Pro && (e0ProportionConstraint == colFrame->GetConstraint()); if (FINISHED != aAllocTypes[colX] && !skipColumn ) { divisor += mTableFrame->GetColumnWidth(colX); numColsAllocated++; } } for (colX = 0; colX < numCols; colX++) { if (FINISHED != aAllocTypes[colX]) { if (aExclude0Pro) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (colFrame && (e0ProportionConstraint == colFrame->GetConstraint())) { continue; } } nscoord oldWidth = mTableFrame->GetColumnWidth(colX); float percent = (divisor == 0) ? (1.0f / ((float)numColsAllocated)) : ((float)oldWidth) / ((float)divisor); nscoord addition = nsTableFrame::RoundToPixel(NSToCoordRound(((float)aAllocAmount) * percent), aPixelToTwips); 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_PCT 0 // retrict allocations to only the pct width cols #define LIMIT_FIX 1 // retrict allocations to only the fix width cols #define LIMIT_DES 2 // retrict allocations to only the auto width cols #define LIMIT_NONE 3 // retrict allocations to only the auto widths unless there are none // and then restrict fix or pct. void BasicTableLayoutStrategy::ComputeNonPctColspanWidths(const nsHTMLReflowState& aReflowState, PRBool aConsiderPct, float aPixelToTwips) { #ifdef DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugTimeNonPctColspans(*mTableFrame, (nsHTMLReflowState&)aReflowState, PR_TRUE); #endif PRInt32 numCols = mTableFrame->GetColCount(); // zero out prior ADJ values PRInt32 colX; for (colX = numCols - 1; colX >= 0; colX--) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; colFrame->SetWidth(MIN_ADJ, WIDTH_NOT_SET); colFrame->SetWidth(FIX_ADJ, WIDTH_NOT_SET); colFrame->SetWidth(DES_ADJ, WIDTH_NOT_SET); } // 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. PRInt32 numRows = mTableFrame->GetRowCount(); for (colX = numCols - 1; colX >= 0; colX--) { for (PRInt32 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++) { nscoord cellWidth = 0; if (MIN_CON == widthX) { cellWidth = cellFrame->GetPass1MaxElementSize().width; } else if (DES_CON == widthX) { cellWidth = cellFrame->GetMaximumWidth(); } else { // FIX width // 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()) { // need to add padding into fixed width nsMargin padding = nsTableFrame::GetPadding(nsSize(aReflowState.mComputedWidth, 0), cellFrame); cellWidth = cellPosition->mWidth.GetCoordValue() + padding.left + padding.right; cellWidth = PR_MAX(cellWidth, cellFrame->GetPass1MaxElementSize().width); } } if (0 >= cellWidth) continue; // first allocate pct cells up to their value if aConsiderPct, then // allocate fixed cells up to their value, then allocate auto cells // up to their value, then allocate auto cells proportionally. PRInt32 limit = (aConsiderPct) ? LIMIT_PCT : LIMIT_FIX; if (MIN_CON != widthX) { // FIX and DES only need to allocate NONE limit = LIMIT_NONE; } while (limit <= LIMIT_NONE) { if (ComputeNonPctColspanWidths(widthX, cellFrame, cellWidth, colX, colSpan, limit, aPixelToTwips)) { break; } limit++; } } } } #ifdef DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugTimeNonPctColspans(*mTableFrame, (nsHTMLReflowState&)aReflowState, PR_FALSE); #endif } #ifdef DEBUG static char* widths[] = {"MIN", "DES", "FIX"}; static char* limits[] = {"PCT", "FIX", "DES", "NONE"}; static PRInt32 dumpCount = 0; void DumpColWidths(nsTableFrame& aTableFrame, char* aMessage, nsTableCellFrame* aCellFrame, PRInt32 aColIndex, PRInt32 aWidthType, PRInt32 aLimitType) { PRInt32 rowIndex; aCellFrame->GetRowIndex(rowIndex); printf ("%s (row,col)=(%d,%d) width=%s limit=%s count=%d\n", aMessage, rowIndex, aColIndex, widths[aWidthType], limits[aLimitType], dumpCount++); for (PRInt32 i = 0; i < aTableFrame.GetColCount(); i++) { printf(" col %d = ", i); nsTableColFrame* colFrame = aTableFrame.GetColFrame(i); for (PRInt32 j = 0; j < NUM_WIDTHS; j++) { printf("%d ", colFrame->GetWidth(j)); } printf("\n"); } } #endif // (aWidthIndex == MIN_CON) is called with: // (aLimitType == LIMIT_PCT) to give MIN_ADJ to PCT cols (up to their PCT value) // (aLimitType == LIMIT_FIX) to give MIN_ADJ to FIX cols (up to their FIX value) // (aLimitType == LIMIT_DES) to give MIN_ADJ to auto cols (up to their preferred value) // (aLimitType == LIMIT_NONE) to give MIN_ADJ to auto cols // (aWidthIndex == FIX), (aWidthIndex == DES) is called with: // (aLimitType == LIMIT_NONE) to give FIX_ADJ to auto cols PRBool BasicTableLayoutStrategy::ComputeNonPctColspanWidths(PRInt32 aWidthIndex, nsTableCellFrame* aCellFrame, nscoord aCellWidth, PRInt32 aColIndex, PRInt32 aColSpan, PRInt32& aLimitType, float aPixelToTwips) { //DumpColWidths(*mTableFrame, "enter ComputeNonPctColspanWidths", aCellFrame, aColIndex, aWidthIndex, aLimitType); PRBool result = PR_TRUE; nscoord spanCellSpacing = 0; // total cell spacing cells being spanned nscoord minTotal = 0; // the sum of min widths of spanned cells. nscoord autoMinTotal = 0; // the sum of min widths of spanned auto cells. nscoord fixMinTotal = 0; // the sum of min widths of spanned fix cells. nscoord pctMinTotal = 0; // the sum of min widths of spanned pct cells. nscoord pctTotal = 0; // the sum of pct widths of spanned cells. nscoord fixTotal = 0; // the sum of fix widths of spanned cells nscoord autoDesTotal = 0; // the sum of desired widths of spanned cells which are auto nscoord pctLimitTotal = 0; // the sum of pct widths not exceeding aCellWidth nscoord fixLimitTotal = 0; // the sum of fix widths not exceeding aCellWidth nscoord desLimitTotal = 0; // the sum of des widths not exceeding aCellWidth PRInt32 numAutoCols = 0; 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); if (!colFrame) continue; nscoord minWidth = PR_MAX(colFrame->GetMinWidth(), 0); nscoord pctWidth = PR_MAX(colFrame->GetPctWidth(), 0); nscoord fixWidth = PR_MAX(colFrame->GetFixWidth(), 0); nscoord desWidth = PR_MAX(colFrame->GetDesWidth(), minWidth); minTotal += minWidth; if (pctWidth > 0) { pctTotal += pctWidth; pctMinTotal += minWidth; } else if (fixWidth > 0) { fixTotal += fixWidth; fixMinTotal += minWidth; } if (colFrame->GetWidth(PCT) + colFrame->GetWidth(FIX) <= 0) { autoDesTotal += desWidth; autoMinTotal += minWidth; numAutoCols++; } if ((spanX > 0) && (mTableFrame->GetNumCellsOriginatingInCol(aColIndex + spanX) > 0)) { spanCellSpacing += spacingX; } } nscoord availWidth = aCellWidth - spanCellSpacing; if (MIN_CON == aWidthIndex) { // MIN widths (unlike the others) are added to existing values, so subtract out the min total availWidth -= minTotal; pctLimitTotal = ((pctTotal - pctMinTotal) < availWidth) ? pctTotal - pctMinTotal : availWidth; fixLimitTotal = ((fixTotal - fixMinTotal) < availWidth) ? fixTotal - fixMinTotal : availWidth; desLimitTotal = ((autoDesTotal - autoMinTotal) < availWidth) ? autoDesTotal - autoMinTotal : availWidth; } else { if (autoDesTotal > 0) { availWidth -= pctTotal + fixTotal; // the pct and fix cols already reached their values } else if (fixTotal > 0) { availWidth -= pctTotal; // the pct already reached their values } } // Below are some optimizations which can skip steps. if (LIMIT_PCT == aLimitType) { NS_ASSERTION(MIN_CON == aWidthIndex, "invalid call to ComputeNonPctColspanWidths"); // if there are no pct cols, limit fix cols if (0 == pctTotal) { aLimitType = LIMIT_FIX; } } if (LIMIT_FIX == aLimitType) { NS_ASSERTION(MIN_CON == aWidthIndex, "invalid call to ComputeNonPctColspanWidths"); // if there are no fix cols, limit des if (0 == fixTotal) { aLimitType = LIMIT_DES; } } if (LIMIT_DES == aLimitType) { NS_ASSERTION(MIN_CON == aWidthIndex, "invalid call to ComputeNonPctColspanWidths"); // if there are no auto cols, limit none if (0 == autoDesTotal) { aLimitType = LIMIT_NONE; } } // there are some cases where allocating up to aCellWidth will not be satisfied switch (aLimitType) { case LIMIT_PCT: if (pctLimitTotal < availWidth) { // the pct cols won't satisfy the request alone availWidth = pctLimitTotal; result = PR_FALSE; } break; case LIMIT_FIX: if (fixLimitTotal < availWidth) { // the fix cols won't satisfy the request alone availWidth = fixLimitTotal; result = PR_FALSE; } break; case LIMIT_DES: if (desLimitTotal < availWidth) { // the auto cols won't satisfy the request alone availWidth = desLimitTotal; result = PR_FALSE; } break; default: // LIMIT_NONE break; } if (availWidth > 0) { nscoord usedWidth = 0; // get the correct numerator in a similar fashion to getting the divisor for (spanX = 0; spanX < aColSpan; spanX++) { if (usedWidth >= availWidth) break; nsTableColFrame* colFrame = mTableFrame->GetColFrame(aColIndex + spanX); if (!colFrame) continue; nscoord minWidth = colFrame->GetMinWidth(); nscoord pctWidth = PR_MAX(colFrame->GetPctWidth(), 0); nscoord fixWidth = PR_MAX(colFrame->GetFixWidth(), 0); nscoord desWidth = PR_MAX(colFrame->GetDesWidth(), minWidth); nscoord colWidth = PR_MAX(colFrame->GetWidth(aWidthIndex), colFrame->GetWidth(aWidthIndex + NUM_MAJOR_WIDTHS)); colWidth = PR_MAX(colWidth, minWidth); nscoord numeratorMin = 0; nscoord numeratorPct = 0; nscoord numeratorFix = 0; nscoord numeratorAutoDes = 0; if (pctWidth > 0) { numeratorPct = pctWidth; } else if (fixWidth > 0) { numeratorFix = fixWidth; } if (colFrame->GetWidth(PCT) + colFrame->GetWidth(FIX) <= 0) { numeratorAutoDes = desWidth; } nscoord divisor; nscoord numerator; // let pct cols reach their target if (LIMIT_PCT == aLimitType) { if (pctLimitTotal > 0) { divisor = pctTotal - pctMinTotal; numerator = PR_MAX(numeratorPct - minWidth, 0); } } // let fix cols reach their target else if (LIMIT_FIX == aLimitType) { if (fixLimitTotal > 0) { divisor = fixTotal - fixMinTotal; numerator = PR_MAX(numeratorFix - minWidth, 0); } } // let auto cols reach their target else if (LIMIT_DES == aLimitType) { if (desLimitTotal > 0) { if (autoDesTotal > 0) { // there were auto cols divisor = autoDesTotal - autoMinTotal; numerator = PR_MAX(numeratorAutoDes - minWidth, 0); } else if (fixTotal > 0) { // there were fix cols but no auto divisor = fixTotal - fixMinTotal; numerator = PR_MAX(numeratorFix - minWidth, 0); } else if (pctTotal > 0) { // there were only pct cols divisor = pctTotal - pctMinTotal; numerator = PR_MAX(numeratorPct - minWidth, 0); } else continue; } } else if (LIMIT_NONE == aLimitType) { if ((MIN_CON == aWidthIndex) && (0 == minTotal)) { // divide evenly among the spanned cols divisor = aColSpan; numerator = 1; } else { if (autoDesTotal > 0) { // there were auto cols divisor = autoDesTotal; numerator = numeratorAutoDes; } else if (fixTotal > 0) { // there were fix cols but no auto divisor = fixTotal; numerator = numeratorFix; } else if (pctTotal > 0) { // there were only pct cols divisor = pctTotal; numerator = numeratorPct; } else continue; } } // calculate the adj col width nscoord newColAdjWidth = (0 >= divisor) ? NSToCoordRound( ((float)availWidth) / ((float)aColSpan) ) : NSToCoordRound( (((float)numerator) / ((float)divisor)) * availWidth); newColAdjWidth = nsTableFrame::RoundToPixel(newColAdjWidth, aPixelToTwips); // don't let the new allocation exceed the avail total newColAdjWidth = PR_MIN(newColAdjWidth, availWidth - usedWidth); // put the remainder of the avail total in the last spanned col if (spanX == aColSpan - 1) { newColAdjWidth = availWidth - usedWidth; } usedWidth += newColAdjWidth; // MIN_CON gets added to what is there, the others don't if (MIN_CON == aWidthIndex) { newColAdjWidth += colWidth; } if (newColAdjWidth > colWidth) { 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? } } } //DumpColWidths(*mTableFrame, "exit ComputeNonPctColspanWidths", aCellFrame, aColIndex, aWidthIndex, aLimitType); return result; } // XXX percent left and right padding are not figured in the calculations // The table frame needs to be used as the percent base because the reflow // state may have an unconstrained width. There should probably be a frame // state bit indicating that horizontal padding is percentage based. // Determine min, desired, fixed, and proportional sizes for the cols and // and calculate min/max table width PRBool BasicTableLayoutStrategy::AssignNonPctColumnWidths(nsIPresContext* aPresContext, nscoord aMaxWidth, const nsHTMLReflowState& aReflowState, float aPixelToTwips) { #ifdef DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugTimeNonPctCols(*mTableFrame, (nsHTMLReflowState&)aReflowState, PR_TRUE); #endif if (gsDebugAssign) {printf("AssignNonPctColWidths en max=%d count=%d \n", aMaxWidth, gsDebugCount++); 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 rawPropTotal = -1; // 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); if (!colFrame) continue; 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 nsSize percentBase(aReflowState.mComputedWidth, 0); nsMargin padding = nsTableFrame::GetPadding(percentBase, cellFrame); nscoord newFixWidth = coordValue + padding.left + padding.right; // 2nd part of condition is Nav/IE Quirk like below if ((newFixWidth > fixWidth) || ((newFixWidth == fixWidth) && (desContributor == cellFrame))) { fixWidth = newFixWidth; fixContributor = cellFrame; } } } } // Nav/IE Quirk like above if (fixWidth > 0) { if (mIsNavQuirksMode && (desWidth > fixWidth) && (fixContributor != desContributor)) { fixWidth = WIDTH_NOT_SET; fixContributor = nsnull; } else { fixWidth = PR_MAX(fixWidth, minWidth); } } desWidth = PR_MAX(desWidth, minWidth); // 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; 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; } } if (proportion >= 0) { rawPropTotal = PR_MAX(rawPropTotal, 0); // it was initialized to -1 colFrame->SetWidth(MIN_PRO, proportion); nsColConstraint colConstraint = (0 == proportion) ? e0ProportionConstraint : eProportionConstraint; rawPropTotal += proportion; colFrame->SetConstraint(colConstraint); } } } if (mCellSpacingTotal > 0) { mCellSpacingTotal += spacingX; // add last cell spacing on right } ComputeNonPctColspanWidths(aReflowState, PR_FALSE, aPixelToTwips); // figure the proportional widths for porportional cols if (rawPropTotal > 0) { // get the total desired widths nscoord desTotal = 0; for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; // consider 1*, 2*, etc cols, but ignore 0* cols if (colFrame->GetWidth(MIN_PRO) > 0) { desTotal += colFrame->GetDesWidth(); } } // find the largest combined prop size considering each prop col and // its desired size nscoord maxPropTotal = 0; for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); nscoord rawProp = colFrame->GetWidth(MIN_PRO); if (rawProp > 0) { nscoord desWidth = colFrame->GetDesWidth(); nscoord propTotal = NSToCoordRound( ((float)desWidth) * ((float)rawPropTotal) / (float)rawProp ); nsTableFrame::RoundToPixel(propTotal, aPixelToTwips); maxPropTotal = PR_MAX(maxPropTotal, propTotal); } } // set MIN_PRO widths based on the maxPropTotal for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; nscoord rawProp = colFrame->GetWidth(MIN_PRO); if (0 == rawProp) { // a 0* col gets only the min width colFrame->SetWidth(MIN_PRO, colFrame->GetMinWidth()); } else if ((rawProp > 0) && (rawPropTotal > 0)) { nscoord desWidth = colFrame->GetDesWidth(); nscoord propWidth = NSToCoordRound( ((float)maxPropTotal) * ((float)rawProp) / (float)rawPropTotal ) ; propWidth = nsTableFrame::RoundToPixel(propWidth, aPixelToTwips); colFrame->SetWidth(MIN_PRO, PR_MAX(propWidth, colFrame->GetMinWidth())); } } } // 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); if (!colFrame) continue; 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) { nsStyleCoord colStyleWidth = colFrame->GetStyleWidth(); if (eStyleUnit_Coord == colStyleWidth.GetUnit()) { fixColWidth = colStyleWidth.GetCoordValue(); if (fixColWidth > 0) { colFrame->SetWidth(FIX, fixColWidth); } } } nscoord minWidth = colFrame->GetMinWidth(); mTableFrame->SetColumnWidth(colX, minWidth); } if (gsDebugAssign) {printf("AssignNonPctColWidths ex\n"); mTableFrame->Dump(aPresContext, PR_FALSE, PR_TRUE, PR_FALSE);} #ifdef DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugTimeNonPctCols(*mTableFrame, (nsHTMLReflowState&)aReflowState, PR_FALSE); #endif return rv; } void BasicTableLayoutStrategy::ReduceOverSpecifiedPctCols(nscoord aExcess) { nscoord numCols = mTableFrame->GetColCount(); for (PRInt32 colX = numCols - 1; (colX >= 0) && (aExcess > 0); colX--) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; nscoord pctWidth = colFrame->GetWidth(PCT); nscoord reduction = 0; if (pctWidth > 0) { reduction = (aExcess > pctWidth) ? pctWidth : aExcess; nscoord newPctWidth = (reduction == pctWidth) ? WIDTH_NOT_SET : pctWidth - reduction; colFrame->SetWidth(PCT, PR_MAX(newPctWidth, colFrame->GetMinWidth())); } else { nscoord pctAdjWidth = colFrame->GetWidth(PCT_ADJ); if (pctAdjWidth > 0) { reduction = (aExcess > pctAdjWidth) ? pctAdjWidth : aExcess; nscoord newPctAdjWidth = (reduction == pctAdjWidth) ? WIDTH_NOT_SET : pctAdjWidth - reduction; colFrame->SetWidth(PCT_ADJ, PR_MAX(newPctAdjWidth, colFrame->GetMinWidth())); } } aExcess -= reduction; } } #ifdef DEBUG_TABLE_REFLOW_TIMING nscoord WrapupAssignPctColumnWidths(nsTableFrame* aTableFrame, const nsHTMLReflowState& aReflowState, nscoord aValue) { nsTableFrame::DebugTimePctCols(*aTableFrame, (nsHTMLReflowState&)aReflowState, PR_FALSE); return aValue; } #else inline nscoord WrapupAssignPctColumnWidths(nsTableFrame* aTableFrame, const nsHTMLReflowState& aReflowState, nscoord aValue) { return aValue; } #endif // Determine percentage col widths for each col frame nscoord BasicTableLayoutStrategy::AssignPctColumnWidths(const nsHTMLReflowState& aReflowState, nscoord aBasisIn, PRBool aTableIsAutoWidth, float aPixelToTwips) { #ifdef DEBUG_TABLE_REFLOW_TIMING nsTableFrame::DebugTimePctCols(*mTableFrame, (nsHTMLReflowState&)aReflowState, PR_TRUE); #endif mTableFrame->SetHasCellSpanningPctCol(PR_FALSE); // this gets refigured below PRInt32 numRows = mTableFrame->GetRowCount(); PRInt32 numCols = mTableFrame->GetColCount(); // consider cols at end without orig cells nscoord spacingX = mTableFrame->GetCellSpacingX(); PRInt32 colX, rowX; nscoord basis; // basis to use for percentage based calculations if (!aTableIsAutoWidth) { if (NS_UNCONSTRAINEDSIZE == aBasisIn) { // don't do the calculations on unconstrained basis return WrapupAssignPctColumnWidths(mTableFrame, aReflowState, 0); } basis = aBasisIn; } else { // 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 basis = 0; float* rawPctValues = new float[numCols]; // store the raw pct values, allow for spans past the effective numCols if (!rawPctValues) return NS_ERROR_OUT_OF_MEMORY; //XXX not sure if this really sets each element to 0.0f //memset(rawPctValues, 0.0f, numCols * sizeof(float)); for (colX = 0; colX < numCols; colX++) { rawPctValues[colX] = 0.0f; } for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; nscoord maxColBasis = -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(); if (percent > 0.0f) { // calculate the preferred width of the cell based on fixWidth and desWidth nscoord cellDesWidth = 0; float spanPct = percent / float(colSpan); for (PRInt32 spanX = 0; spanX < colSpan; spanX++) { nsTableColFrame* spanFrame = mTableFrame->GetColFrame(colX + spanX); if (!spanFrame) continue; cellDesWidth += spanFrame->GetWidth(DES_CON); // don't consider DES_ADJ rawPctValues[colX + spanX] = PR_MAX(rawPctValues[colX + spanX], spanPct); } // figure the basis using the cell's desired width and percent nscoord colBasis = nsTableFrame::RoundToPixel(NSToCoordRound((float)cellDesWidth / percent), aPixelToTwips); maxColBasis = PR_MAX(maxColBasis, colBasis); } } } if (-1 == maxColBasis) { // see if the col has a style percent width specified nsStyleCoord colStyleWidth = colFrame->GetStyleWidth(); if (eStyleUnit_Percent == colStyleWidth.GetUnit()) { float percent = colStyleWidth.GetPercentValue(); maxColBasis = 0; if (percent > 0.0f) { rawPctValues[colX] = PR_MAX(rawPctValues[colX], percent); nscoord desWidth = colFrame->GetWidth(DES_CON); // don't consider DES_ADJ maxColBasis = nsTableFrame::RoundToPixel(NSToCoordRound((float)desWidth / percent), aPixelToTwips); } } } basis = PR_MAX(basis, maxColBasis); } // end for (colX .. float perTotal = 0.0f; // total of percentage constrained cols and/or cells in cols nscoord fixWidthTotal = 0; // total of fixed widths of all cols PRInt32 numPerCols = 0; // number of colums that have percentage constraints nscoord fixDesTotal = 0; // total of fix or des widths of cols nscoord fixDesTotalNoPct = 0; // total of fix or des widths of cols without pct for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); nscoord fixWidth = colFrame->GetFixWidth(); nscoord fixDesWidth = (fixWidth > 0) ? fixWidth : colFrame->GetDesWidth(); fixDesTotal += fixDesWidth; if (rawPctValues[colX] + perTotal > 1.0f) { rawPctValues[colX] = PR_MAX(1.0f - perTotal, 0.0f); } if (rawPctValues[colX] > 0.0f) { numPerCols++; perTotal += rawPctValues[colX]; } else { fixDesTotalNoPct += fixDesWidth; } } delete [] rawPctValues; // destroy the raw pct values // If there are no pct cells or cols, there is nothing to do. if ((0 == numPerCols) || (0.0f == perTotal)) { return WrapupAssignPctColumnWidths(mTableFrame, aReflowState, 0); } // If there is only one col and it is % based, it won't affect anything if ((1 == numCols) && (numCols == numPerCols)) { return WrapupAssignPctColumnWidths(mTableFrame, aReflowState, 0); } // compute a basis considering total percentages and the desired width of everything else if (perTotal < 1.0f) { if (perTotal > 0.0f) { nscoord otherBasis = nsTableFrame::RoundToPixel(NSToCoordRound((float)fixDesTotalNoPct / (1.0f - perTotal)), aPixelToTwips); basis = PR_MAX(basis, otherBasis); } } else if ((fixDesTotalNoPct > 0) && (NS_UNCONSTRAINEDSIZE != aBasisIn)) { // make the basis as big as possible basis = aBasisIn; } basis = PR_MAX(basis, fixDesTotal); basis = PR_MIN(basis, aBasisIn); // don't exceed the max we were given } nscoord colPctTotal = 0; // Determine the percentage contribution for cols and for cells with colspan = 1 // Iterate backwards, similarly to the reasoning in AssignNonPctColumnWidths for (colX = numCols - 1; colX >= 0; colX--) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; nscoord maxColPctWidth = WIDTH_NOT_SET; float maxColPct = 0.0f; 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 nsMargin padding = nsTableFrame::GetPadding(nsSize(basis, 0), cellFrame); maxColPctWidth += padding.left + padding.right; } } } } if (WIDTH_NOT_SET == maxColPctWidth) { // see if the col has a style percent width specified nsStyleCoord colStyleWidth = colFrame->GetStyleWidth(); if (eStyleUnit_Percent == colStyleWidth.GetUnit()) { maxColPct = colStyleWidth.GetPercentValue(); maxColPctWidth = nsTableFrame::RoundToPixel(NSToCoordRound( ((float)basis) * maxColPct ), aPixelToTwips); } } // 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) { maxColPctWidth = PR_MAX(maxColPctWidth, colFrame->GetWidth(MIN_CON)); colFrame->SetWidth(PCT, maxColPctWidth); colFrame->SetConstrainingCell(percentContributor); colPctTotal += NSToCoordRound(100.0f * (float)maxColPct); } } // if the percent total went over 100%, adjustments need to be made to right most cols if (colPctTotal > 100) { ReduceOverSpecifiedPctCols(NSToCoordRound(((float)(colPctTotal - 100)) * 0.01f * (float)basis)); colPctTotal = 100; } // check to see if a cell spans a percentage col. This will cause the MIN_ADJ, // FIX_ADJ, and DES_ADJ values to be recomputed PRBool done = PR_FALSE; for (colX = 0; (colX < numCols) && !done; colX++) { for (rowX = 0; (rowX < numRows) && !done; rowX++) { PRBool originates; PRInt32 colSpan; nsTableCellFrame* cellFrame = mTableFrame->GetCellInfoAt(rowX, colX, &originates, &colSpan); if (!originates || (1 == colSpan)) { continue; } // determine if the cell spans cols which have a pct value for (PRInt32 spanX = 0; (spanX < colSpan) && !done; spanX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX + spanX); if (!colFrame) continue; if (colFrame->GetWidth(PCT) > 0) { mTableFrame->SetHasCellSpanningPctCol(PR_TRUE); // recompute the MIN_ADJ, FIX_ADJ, and DES_ADJ values ComputeNonPctColspanWidths(aReflowState, PR_TRUE, aPixelToTwips); done = PR_TRUE; break; } } } } // 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 = nsTableFrame::RoundToPixel(NSToCoordRound( ((float)basis) * cellPct ), aPixelToTwips); if (!mIsNavQuirksMode) { // need to add padding nsMargin padding = nsTableFrame::GetPadding(nsSize(basis, 0), cellFrame); cellPctWidth += padding.left + padding.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); if (!colFrame) continue; 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) continue; 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 avail = (float)PR_MAX(cellPctWidth - colPctWidthTotal, 0); float colPctAdj = (0 == spanTotal) ? avail / ((float) colSpan) / ((float)basis) : (avail / (float)basis) * (((float)colWidth) / (float)spanTotal); if (colPctAdj > 0) { nscoord colPctAdjWidth = colFrame->GetWidth(PCT_ADJ); nscoord newColPctAdjWidth = nsTableFrame::RoundToPixel(NSToCoordRound(colPctAdj * (float)basis), aPixelToTwips); if (newColPctAdjWidth > colPctAdjWidth) { newColPctAdjWidth = PR_MAX(newColPctAdjWidth, minWidth); if (newColPctAdjWidth > colFrame->GetWidth(PCT)) { colFrame->SetWidth(PCT_ADJ, newColPctAdjWidth); colFrame->SetConstrainingCell(cellFrame); } } } } } } } // end for (rowX .. nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; colPctTotal += NSToCoordRound(100.0f * (float)colFrame->GetWidth(PCT_ADJ) / (float)basis); } // end for (colX .. // if the percent total went over 100%, adjustments need to be made to right most cols if (colPctTotal > 100) { ReduceOverSpecifiedPctCols(NSToCoordRound(((float)(colPctTotal - 100)) * 0.01f * (float)basis)); } return WrapupAssignPctColumnWidths(mTableFrame, aReflowState, basis); } nscoord BasicTableLayoutStrategy::GetTableMinWidth() const { nscoord minWidth = 0; nscoord spacingX = mTableFrame->GetCellSpacingX(); PRInt32 numCols = mTableFrame->GetColCount(); for (PRInt32 colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; minWidth += PR_MAX(colFrame->GetMinWidth(), colFrame->GetWidth(MIN_ADJ)); if (mTableFrame->GetNumCellsOriginatingInCol(colX) > 0) { minWidth += spacingX; } } // if it is not a degenerate table, add the last spacing on the right if (minWidth > 0) { minWidth += spacingX; } return minWidth; } nscoord BasicTableLayoutStrategy::GetTableMaxWidth(const nsHTMLReflowState& aReflowState) const { nscoord maxWidth = 0; if (mTableFrame->IsAutoWidth()) { nscoord spacingX = mTableFrame->GetCellSpacingX(); PRInt32 numCols = mTableFrame->GetColCount(); for (PRInt32 colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; nscoord width = colFrame->GetPctWidth(); if (width <= 0) { width = colFrame->GetFixWidth(); if (width <= 0) { width = colFrame->GetWidth(MIN_PRO); if (width <= 0) { width = colFrame->GetDesWidth(); } } } maxWidth += width; if (mTableFrame->GetNumCellsOriginatingInCol(colX) > 0) { maxWidth += spacingX; } } // if it is not a degenerate table, add the last spacing on the right if (maxWidth > 0) { maxWidth += spacingX; } } else { maxWidth = PR_MAX(GetTableMinWidth(), aReflowState.mComputedWidth); } return maxWidth; } // calculate totals by width type. The logic here is kept in synch with // that in CanAllocate. aDupedWidths (duplicatd) are widths that will be // allocated in BalanceColumnWidths before aTotalsWidths (e.g. aTotalWidths[PCT] // will have aDuplicatedWidths[PCT] consisting of the MIN widths of cols which // have a PCT width). void BasicTableLayoutStrategy::CalculateTotals(PRInt32& aCellSpacing, PRInt32* aTotalCounts, PRInt32* aTotalWidths, PRInt32* aDupedWidths, PRInt32& a0ProportionalCount) { //mTableFrame->Dump(PR_TRUE, PR_FALSE); aCellSpacing = 0; for (PRInt32 widthX = 0; widthX < NUM_WIDTHS; widthX++) { aTotalCounts[widthX] = 0; aTotalWidths[widthX] = 0; aDupedWidths[widthX] = 0; } a0ProportionalCount = 0; nscoord spacingX = mTableFrame->GetCellSpacingX(); PRInt32 numCols = mTableFrame->GetColCount(); PRInt32 colX; for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; 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] += PR_MAX(pct, minCol); aDupedWidths[PCT] += minCol; } else if ((fix > 0) || (fixAdj > 0)) { if (fix > 0) { aTotalCounts[FIX]++; aTotalWidths[FIX] += PR_MAX(fix, minCol); aDupedWidths[FIX] += minCol; } nscoord desCon = colFrame->GetWidth(DES_CON); if (fixAdj > 0) { if (fixAdj > fix) { aTotalCounts[FIX_ADJ]++; aTotalWidths[FIX_ADJ] += PR_MAX(fixAdj, minCol); if (fix > 0) { aDupedWidths[FIX_ADJ] += fix; } else { // there was no fix aDupedWidths[FIX_ADJ] += minCol; } } } } else if ((eProportionConstraint == colFrame->GetConstraint()) || (e0ProportionConstraint == colFrame->GetConstraint())) { aTotalCounts[MIN_PRO]++; aTotalWidths[MIN_PRO] += PR_MAX(colFrame->GetWidth(MIN_PRO), minCol); aDupedWidths[MIN_PRO] += minCol; } else { // desired alone is lowest priority aTotalCounts[DES_CON]++; aTotalWidths[DES_CON] += PR_MAX(colFrame->GetDesWidth(), minCol); aDupedWidths[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, float aPixelToTwips) { for (PRInt32 i = 0; i < aNumAutoCols; i++) { if ((aAvailWidth <= 0) || (aDivisor <= 0)) { break; } // aDivisor represents the sum of unallocated space (diff between max and min values) float percent = ((float)aColInfo[i]->mMaxWidth - (float)aColInfo[i]->mMinWidth) / (float)aDivisor; aDivisor -= aColInfo[i]->mMaxWidth - aColInfo[i]->mMinWidth; nscoord addition = nsTableFrame::RoundToPixel(NSToCoordRound(((float)(aAvailWidth)) * percent), aPixelToTwips); // 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, float aPixelToTwips) { 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 = nsTableFrame::RoundToPixel(NSToCoordRound(((float)(aExcess)) * percent), aPixelToTwips); // 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, float aPixelToTwips) { 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; nscoord sumMinConstraints = 0; PRInt32 colX; // find out how many constrained cols there are for (colX = 0; colX < numCols; colX++) { nsTableColFrame* colFrame = mTableFrame->GetColFrame(colX); if (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; } numConstrainedCols++; } // allocate storage for the constrained 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 (!colFrame) continue; if (!CanAllocate(aWidthType, aAllocTypes[colX], colFrame)) { continue; } nscoord minWidth = mTableFrame->GetColumnWidth(colX); nscoord maxWidth = GetColWidth(colFrame, aWidthType); // proportional and desired widths are handled together PRBool haveProWidth = PR_FALSE; if (DES_CON == aWidthType) { nscoord proWidth = colFrame->GetWidth(MIN_PRO); if (proWidth >= 0) { haveProWidth = PR_TRUE; maxWidth = proWidth; } } if (maxWidth <= 0) continue; sumMaxConstraints += maxWidth; sumMinConstraints += minWidth; 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] = (haveProWidth) ? MIN_PRO : 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 - sumMinConstraints, availWidth, aPixelToTwips); } 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, aPixelToTwips); } AC_Wrapup(mTableFrame, numConstrainedCols, colInfo); } // XXX this function will be improved after the colspan algorithms have been extracted // from AssignNonPctColumnWidths and AssignPctColumnWidths. 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); if (!colFrame) return PR_FALSE; 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 (!colSpanFrame) continue; 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 AssignNonPctColumnWidths and AssignPctColumnWidths. 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); if (!colFrame) return PR_FALSE; nscoord colSpan = mTableFrame->GetEffectiveColSpan(aCellFrame); // check to see if DES_CON can affect columns nsTableColFrame* spanFrame = colFrame; for (PRInt32 span = 0; span < colSpan; span++) { if (!spanFrame) return PR_FALSE; // 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); if (!colFrame) return PR_TRUE; 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)) || (cellDes == aPrevCellDes)) { // 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 } } nscoord colFix = colFrame->GetWidth(FIX); if ((colFrame->GetWidth(FIX_ADJ) > 0) && (colFix <= 0)) { if (desChanged) { return PR_FALSE; // its unfortunate that the balancing algorithms cause this // XXX add cases where table has coord width } } if ((colFix > 0) && (desChanged) && (cellDes < aPrevCellDes) && (aPrevCellDes == colFix)) { return PR_FALSE; } } 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 (colIndexGetColumnFrame(aColIndex, colFrame); nsStyleCoord colStyleWidth = colFrame->GetStyleWidth(); switch (colStyleWidth.GetUnit()) { 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]; if (!indent) return; 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 cellSpacing=%d propRatio=%.2f navQuirks=%d", indent, mCellSpacingTotal, mMinToDesProportionRatio, mIsNavQuirksMode); printf(" **END BASIC STRATEGY DUMP** \n"); delete [] indent; }