/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape Public License * Version 1.0 (the "NPL"); you may not use this file except in * compliance with the NPL. You may obtain a copy of the NPL at * http://www.mozilla.org/NPL/ * * Software distributed under the NPL is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL * for the specific language governing rights and limitations under the * NPL. * * The Initial Developer of this code under the NPL is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All Rights * Reserved. */ #include "nsTablePart.h" #include "nsTableOuterFrame.h" #include "nsITableContent.h" #include "nsTableCol.h" #include "nsTableColGroup.h" #include "nsTableRowGroup.h" #include "nsTableCaption.h" #include "nsTableRow.h" #include "nsTableCell.h" #include "nsCellMap.h" #include "nsHTMLParts.h" #include "nsIPresContext.h" #include "nsContainerFrame.h" #include "nsIRenderingContext.h" #include "nsHTMLIIDs.h" #include "nsIAtom.h" #include "nsIStyleContext.h" #include "nsHTMLAtoms.h" #include "nsStyleConsts.h" #include "nsUnitConversion.h" static NS_DEFINE_IID(kISupportsIID, NS_ISUPPORTS_IID); static NS_DEFINE_IID(kITableContentIID, NS_ITABLECONTENT_IID); static NS_DEFINE_IID(kStylePositionSID, NS_STYLEPOSITION_SID); static NS_DEFINE_IID(kStyleBorderSID, NS_STYLEBORDER_SID); static NS_DEFINE_IID(kStyleSpacingSID, NS_STYLESPACING_SID); #ifdef NS_DEBUG static PRBool gsDebug = PR_FALSE; static PRBool gsNoisyRefs = PR_FALSE; #else static const PRBool gsDebug = PR_FALSE; static const PRBool gsNoisyRefs = PR_FALSE; #endif const char *nsTablePart::kCaptionTagString="CAPTION"; const char *nsTablePart::kRowGroupBodyTagString="TBODY"; const char *nsTablePart::kRowGroupHeadTagString="THEAD"; const char *nsTablePart::kRowGroupFootTagString="TFOOT"; const char *nsTablePart::kRowTagString="TR"; const char *nsTablePart::kColGroupTagString="COLGROUP"; const char *nsTablePart::kColTagString="COL"; const char *nsTablePart::kDataCellTagString="TD"; const char *nsTablePart::kHeaderCellTagString="TH"; CellData::CellData() { mCell = nsnull; mRealCell = nsnull; mOverlap = nsnull; } CellData::~CellData() {} /*---------- nsTablePart implementation -----------*/ /** * * BUGS: * * * TODO: * * * * CSS1: * * * CSS2: * * */ /** constructor * I do not check or addref aTag because my superclass does that for me */ nsTablePart::nsTablePart(nsIAtom* aTag) : nsHTMLContainer(aTag), mColCount(0), mSpecifiedColCount(0), mCellMap(0) { } /** constructor * I do not check or addref aTag because my superclass does that for me */ nsTablePart::nsTablePart (nsIAtom* aTag, PRInt32 aColumnCount) : nsHTMLContainer(aTag), mColCount(aColumnCount), mSpecifiedColCount(0), mCellMap(0) { } /** */ nsTablePart::~nsTablePart() { if (nsnull!=mCellMap) { delete mCellMap; } } /** */ /* nsTablePart::void compact() { compact(); } */ // for debugging only nsrefcnt nsTablePart::AddRef(void) { if (gsNoisyRefs==PR_TRUE) printf("Add Ref: nsTablePart cnt = %d \n",mRefCnt+1); return ++mRefCnt; } // for debugging only nsrefcnt nsTablePart::Release(void) { if (gsNoisyRefs==PR_TRUE) printf("Release: nsTablePart cnt = %d \n",mRefCnt-1); if (--mRefCnt == 0) { if (gsNoisyRefs==PR_TRUE) printf("Delete: nsTablePart \n"); delete this; return 0; } return mRefCnt; } PRInt32 nsTablePart::GetMaxColumns () { if (nsnull == mCellMap) { BuildCellMap (); } return mColCount; } // XXX what do rows with no cells turn into? PRInt32 nsTablePart::GetRowCount () { // if we've already built the cellMap, ask it for the row count if (nsnull != mCellMap) return mCellMap->GetRowCount(); // otherwise, we need to compute it by walking our children int rowCount = 0; int index = ChildCount (); while (0 < index) { nsIContent *child = ChildAt (--index); // child: REFCNT++ nsTableContent *tableContent = (nsTableContent *)child; const int contentType = tableContent->GetType(); if (contentType == nsITableContent::kTableRowGroupType) rowCount += ((nsTableRowGroup *)tableContent)->GetRowCount (); NS_RELEASE(child); // child: REFCNT-- } return rowCount; } /* counts columns in column groups */ PRInt32 nsTablePart::GetSpecifiedColumnCount () { if (mSpecifiedColCount < 0) { mSpecifiedColCount = 0; int count = ChildCount (); for (int index = 0; index < count; index++) { nsIContent *child = ChildAt (index); // child: REFCNT++ nsTableContent *tableContent = (nsTableContent *)child; const int contentType = tableContent->GetType(); if (contentType == nsITableContent::kTableColGroupType) { ((nsTableColGroup *)tableContent)->SetStartColumnIndex (mSpecifiedColCount); mSpecifiedColCount += ((nsTableColGroup *)tableContent)->GetColumnCount (); } NS_RELEASE(child); // child: REFCNT-- } } return mSpecifiedColCount; } // returns the actual cell map, not a copy, so don't mess with it! nsCellMap* nsTablePart::GetCellMap() const { return mCellMap; } /* call when the cell structure has changed. mCellMap will be rebuilt on demand. */ void nsTablePart::ResetCellMap () { if (nsnull==mCellMap) delete mCellMap; mCellMap = nsnull; // for now, will rebuild when needed } /* call when column structure has changed. */ void nsTablePart::ResetColumns () { mSpecifiedColCount = -1; if (nsnull != mCellMap) { int colCount = GetSpecifiedColumnCount (); GrowCellMap (colCount); // make sure we're at least as big as specified columns } else mColCount = 0; // we'll compute later (as part of building cell map) } /** sum the columns represented by all nsTableColGroup objects * if the cell map says there are more columns than this, * add extra implicit columns to the content tree. */ void nsTablePart::EnsureColumns() { if (nsnull!=mCellMap) { PRInt32 actualColumns = 0; PRInt32 numColGroups = ChildCount(); nsTableColGroup *lastColGroup = nsnull; for (PRInt32 colGroupIndex = 0; colGroupIndex < numColGroups; colGroupIndex++) { nsTableContent *colGroup = (nsTableContent*)ChildAt(colGroupIndex); // colGroup: REFCNT++ const int contentType = colGroup->GetType(); if (contentType==nsTableContent::kTableColGroupType) { PRInt32 numCols = ((nsTableColGroup *)colGroup)->GetColumnCount(); actualColumns += numCols; lastColGroup = (nsTableColGroup *)colGroup; NS_RELEASE(colGroup); } else if (contentType==nsTableContent::kTableRowGroupType) { NS_RELEASE(colGroup); break; } } if (actualColumns < mCellMap->GetColCount()) { if (nsnull==lastColGroup) { lastColGroup = new nsTableColGroup (PR_TRUE); AppendColGroup(lastColGroup); } PRInt32 excessColumns = mCellMap->GetColCount() - actualColumns; for ( ; excessColumns > 0; excessColumns--) { nsTableCol *col = new nsTableCol(PR_TRUE); lastColGroup->AppendChild (col); } } } } /** */ void nsTablePart::ReorderChildren() { NS_ASSERTION(PR_FALSE, "not yet implemented."); } void nsTablePart::NotifyContentComplete() { // set the children in order ReorderChildren(); } /** add a child to the table content. * tables are special because they require the content to be normalized, in order. * so this function doesn't really "append" the content, but adds it in the proper place, * possibly nested inside of implicit parents. * order is: * Captions (optional) * Column Groups (1 or more, possibly implicit) * Row Groups * THEADs (optional) * TFOOTs (optional) * TBODY (at least 1, possibly implicit) * * should be broken out into separate functions! */ PRBool nsTablePart::AppendChild (nsIContent * aContent) { NS_PRECONDITION(nsnull!=aContent, "bad arg"); PRBool result = PR_FALSE; PRBool newCells = PR_FALSE; PRBool contentHandled = PR_FALSE; // wait, stop! need to check to see if this is really tableContent or not! nsITableContent *tableContentInterface = nsnull; nsresult rv = aContent->QueryInterface(kITableContentIID, (void **)&tableContentInterface); // tableContentInterface: REFCNT++ if (NS_FAILED(rv)) { // create an implicit cell and return the result of adding it to the table if (gsDebug==PR_TRUE) printf ("*** bad HTML in table, not yet implemented.\n"); return PR_FALSE; } else { nsTableContent *tableContent = (nsTableContent *)tableContentInterface; const int contentType = tableContent->GetType(); if ((contentType == nsITableContent::kTableCellType) || (contentType==nsITableContent::kTableRowType)) { if (gsDebug==PR_TRUE) { if (contentType == nsITableContent::kTableRowType) printf ("nsTablePart::AppendChild -- adding a row.\n"); else printf ("nsTablePart::AppendChild -- adding a cell.\n"); } // find last row group, if ! implicit, make one, append there nsTableRowGroup *group = nsnull; int index = ChildCount (); while ((0 < index) && (nsnull==group)) { nsIContent *child = ChildAt (--index); // child: REFCNT++ if (nsnull != child) { nsTableContent * content = (nsTableContent *)child; if (content->GetType()==nsITableContent::kTableRowGroupType) { group = (nsTableRowGroup *)content; NS_ADDREF(group); // group: REFCNT++ // SEC: this code might be a space leak for tables >1 row group } } NS_RELEASE(child); // child: REFCNT-- } if ((nsnull == group) || (! group->IsImplicit ())) { if (gsDebug==PR_TRUE) printf ("nsTablePart::AppendChild -- creating an implicit row group.\n"); nsIAtom * rowGroupTag = NS_NewAtom(kRowGroupBodyTagString); // rowGroupTag: REFCNT++ group = new nsTableRowGroup (rowGroupTag, PR_TRUE); NS_ADDREF(group); // group: REFCNT++ AppendChild (group); group->SetTable(this); NS_RELEASE(rowGroupTag); // rowGroupTag: REFCNT-- } // group is guaranteed to be allocated at this point result = group->AppendChild(aContent); newCells = result; contentHandled = PR_TRUE; NS_RELEASE(group); // group: REFCNT-- } else if (contentType == nsITableContent::kTableColType) { result = AppendColumn((nsTableCol *)aContent); newCells = result; contentHandled = PR_TRUE; } else if (contentType == nsITableContent::kTableCaptionType) { result = AppendCaption((nsTableCaption *)aContent); contentHandled = PR_TRUE; // whether we succeeded or not, we've "handled" this request } else if (contentType == nsITableContent::kTableRowGroupType) { result = AppendRowGroup((nsTableRowGroup *)aContent); if (PR_TRUE==result) { newCells = PR_TRUE; } contentHandled = PR_TRUE; // whether we succeeded or not, we've "handled" this request } else if (contentType == nsITableContent::kTableColGroupType) { result = AppendColGroup((nsTableColGroup *)aContent); if (PR_TRUE==result) { newCells = PR_TRUE; } contentHandled = PR_TRUE; // whether we succeeded or not, we've "handled" this request } // Remember to set the table variable -- gpk if (tableContentInterface) tableContentInterface->SetTable(this); /* if aContent is not a known content type, make a capion out of it */ // SEC the logic here is very suspicious!!!! if (PR_FALSE==contentHandled) { if (gsDebug==PR_TRUE) printf ("nsTablePart::AppendChild -- content not handled!!!\n"); nsTableCaption *caption = nsnull; nsIContent *lastChild = ChildAt (ChildCount() - 1); // lastChild: REFCNT++ if (nsnull != lastChild) { nsTableContent * content = (nsTableContent *)lastChild; if (content->GetType()==nsITableContent::kTableCaptionType) caption = (nsTableCaption *)content; NS_RELEASE(lastChild); // lastChild: REFCNT-- } if ((nsnull == caption) || (! caption->IsImplicit ())) { if (gsDebug==PR_TRUE) printf ("nsTablePart::AppendChild -- adding an implicit caption.\n"); caption = new nsTableCaption (PR_TRUE); AppendCaption (caption); } result = caption->AppendChild (aContent); } /* if we added new cells, we need to fix up the cell map */ if (newCells) { ResetCellMap (); } } NS_RELEASE(tableContentInterface); // tableContentInterface: REFCNT-- return result; } /* SEC: why can we only insertChildAt (captions or groups) ? */ PRBool nsTablePart::InsertChildAt(nsIContent * aContent, PRInt32 aIndex) { NS_PRECONDITION(nsnull!=aContent, "bad arg"); // aIndex checked in nsHTMLContainer PRBool result = PR_FALSE; nsTableContent *tableContent = (nsTableContent *)aContent; const int contentType = tableContent->GetType(); if ((contentType == nsITableContent::kTableCaptionType) || (contentType == nsITableContent::kTableColGroupType) || (contentType == nsITableContent::kTableRowGroupType)) { result = nsHTMLContainer::InsertChildAt (aContent, aIndex); if (result) { tableContent->SetTable (this); ResetCellMap (); } } return result; } PRBool nsTablePart::ReplaceChildAt (nsIContent *aContent, PRInt32 aIndex) { NS_PRECONDITION(nsnull!=aContent, "bad aContent arg to ReplaceChildAt"); NS_PRECONDITION(0<=aIndex && aIndexGetType(); if ( (contentType == nsITableContent::kTableCaptionType) || (contentType == nsITableContent::kTableColGroupType) || (contentType == nsITableContent::kTableRowGroupType)) { nsIContent *lastChild = ChildAt (aIndex);// lastChild: REFCNT++ result = nsHTMLContainer::ReplaceChildAt (aContent, aIndex); if (result) { if (nsnull != lastChild) tableContent->SetTable (nsnull); tableContent->SetTable (this); ResetCellMap (); } NS_IF_RELEASE(lastChild); // lastChild: REFCNT-- } return result; } /** * Remove a child at the given position. The method is a no-op if * the index is invalid (too small or too large). */ PRBool nsTablePart::RemoveChildAt (PRInt32 aIndex) { NS_PRECONDITION(0<=aIndex && aIndexGetType(); if (contentType == nsITableContent::kTableRowType) { if (nsnull != lastChild) ((nsTableRow *)tableContent)->SetRowGroup (nsnull); tableContent->SetTable(nsnull); ResetCellMap (); } } NS_IF_RELEASE(lastChild); return result; } /** protected method for appending a column group to this table */ PRBool nsTablePart::AppendRowGroup (nsTableRowGroup *aContent) { NS_PRECONDITION(nsnull!=aContent, "null arg."); PRBool result = PR_TRUE; if (gsDebug==PR_TRUE) printf ("nsTablePart::AppendRowGroup -- adding a row group.\n"); // find the last row group of this kind and insert this row group after it // if there is no row group of this type already in the table, // find the appropriate slot depending on this row group tag nsIAtom * rowGroupTag = aContent->GetTag(); int childCount = ChildCount (); nsIAtom * tHeadTag = NS_NewAtom(kRowGroupHeadTagString); // tHeadTag: REFCNT++ nsIAtom * tFootTag = NS_NewAtom(kRowGroupFootTagString); // tFootTag: REFCNT++ nsIAtom * tBodyTag = NS_NewAtom(kRowGroupBodyTagString); // tBodyTag: REFCNT++ for (PRInt32 childIndex = 0; childIndex < childCount; childIndex++) { nsTableContent *tableChild = (nsTableContent *)ChildAt(childIndex); // tableChild: REFCNT++ const int tableChildType = tableChild->GetType(); // if we've found caption or colgroup, then just skip it and keep going if ((tableChildType == nsITableContent::kTableCaptionType) || (tableChildType == nsITableContent::kTableColGroupType)) { NS_RELEASE(tableChild); // tableChild: REFCNT-- (a) continue; } // if we've found a row group, our action depends on what kind of row group else if (tableChildType == nsITableContent::kTableRowGroupType) { nsIAtom * tableChildTag = tableChild->GetTag(); NS_RELEASE(tableChild); // tableChild: REFCNT-- (b) // if aContent is a header and the current child is a header, keep going if (tHeadTag==rowGroupTag && tHeadTag==tableChildTag) { continue; } // if aContent is a footer and the current child is either a header or a footer, keep going else if (tFootTag==rowGroupTag && (tHeadTag==tableChildTag || tFootTag==tableChildTag)) { continue; } // if aContent is a body and the current child is a footer, stop, we've found the spot else if (tBodyTag==rowGroupTag && tFootTag==tableChildTag) { continue; } // if aContent is a body and we've gotten this far, keep going else if (tBodyTag==rowGroupTag) { continue; } // otherwise, we must have found the right spot so stop else { break; } } // otherwise we're already at the right spot, so stop else { NS_RELEASE(tableChild); // tableChild: REFCNT-- (c) break; } } NS_RELEASE(tFootTag); NS_RELEASE(tHeadTag); NS_RELEASE(tBodyTag); result = nsHTMLContainer::InsertChildAt(aContent, childIndex); if (result) ((nsTableContent *)aContent)->SetTable (this); return result; } /** protected method for appending a column group to this table */ PRBool nsTablePart::AppendColGroup(nsTableColGroup *aContent) { NS_PRECONDITION(nsnull!=aContent, "null arg."); if (gsDebug==PR_TRUE) printf ("nsTablePart::AppendColGroup -- adding a column group.\n"); // find the last column group and insert this column group after it. // if there is no column group already in the table, make this the first child // after any caption int childCount = ChildCount (); for (PRInt32 childIndex = 0; childIndex < childCount; childIndex++) { nsTableContent *tableChild = (nsTableContent *)ChildAt(childIndex); // tableChild: REFCNT++ const int tableChildType = tableChild->GetType(); NS_RELEASE(tableChild); // tableChild: REFCNT-- if (!((tableChildType == nsITableContent::kTableCaptionType) || (tableChildType == nsITableContent::kTableColGroupType))) break; } PRBool result = nsHTMLContainer::InsertChildAt(aContent, childIndex); if (result) { ((nsTableContent *)aContent)->SetTable (this); } // if col group has a SPAN attribute, create implicit columns for the value of SPAN // what sucks is if we then get a COL for this COLGROUP, we have to delete all the // COLs we created for SPAN, and just contain the explicit COLs. PRInt32 span = 0; // SEC: TODO find a way to really get this for (PRInt32 i=0; iAppendChild (col); } return result; } /** protected method for appending a column group to this table */ PRBool nsTablePart::AppendColumn(nsTableCol *aContent) { NS_PRECONDITION(nsnull!=aContent, "null arg."); if (gsDebug==PR_TRUE) printf ("nsTablePart::AppendColumn -- adding a column.\n"); // find last col group, if ! implicit, make one, append there nsTableColGroup *group = nsnull; PRBool foundColGroup = PR_FALSE; int index = ChildCount (); while ((0 < index) && (PR_FALSE==foundColGroup)) { nsIContent *child = ChildAt (--index); // child: REFCNT++ if (nsnull != child) { group = (nsTableColGroup *)child; foundColGroup = (PRBool) (group->GetType()==nsITableContent::kTableColGroupType); NS_RELEASE(child); // child: REFCNT-- } } if ((PR_FALSE == foundColGroup) || (! group->IsImplicit ())) { if (gsDebug==PR_TRUE) printf ("nsTablePart::AppendChild -- creating an implicit column group.\n"); group = new nsTableColGroup (PR_TRUE); AppendChild (group); group->SetTable(this); } PRBool result = group->AppendChild (aContent); return result; } /** protected method for appending a column group to this table */ PRBool nsTablePart::AppendCaption(nsTableCaption *aContent) { NS_PRECONDITION(nsnull!=aContent, "null arg."); if (gsDebug==PR_TRUE) printf ("nsTablePart::AppendCaption -- adding a caption.\n"); // find the last caption and insert this caption after it. // if there is no caption already in the table, make this the first child int childCount = ChildCount (); for (PRInt32 childIndex = 0; childIndex < childCount; childIndex++) { nsTableContent *tableChild = (nsTableContent *)ChildAt(childIndex); const int tableChildType = tableChild->GetType(); NS_RELEASE(tableChild); if (tableChildType != nsITableContent::kTableCaptionType) break; } PRBool result = nsHTMLContainer::InsertChildAt(aContent, childIndex); if (PR_TRUE==result) { ((nsTableContent *)aContent)->SetTable (this); } return result; } /* return the index of the first row group after aStartIndex */ PRInt32 nsTablePart::NextRowGroup (PRInt32 aStartIndex) { int index = aStartIndex; int count = ChildCount (); while (++index < count) { nsIContent * child = ChildAt (index); // child: REFCNT++ nsTableContent *tableContent = (nsTableColGroup *)child; const int contentType = tableContent->GetType(); NS_RELEASE(child); // child: REFCNT-- if (contentType == nsITableContent::kTableRowGroupType) return index; } return count; } // XXX This should be computed incrementally and updated // automatically when rows are added/deleted. To do this, however, // we need to change the way the ContentSink works and somehow // notify this table when a row is modified. We can give the rows // parent pointers, but that would bite. // XXX nuke this; instead pretend the content sink is working // incrementally like we want it to and build up the data a row at a // time. void nsTablePart::BuildCellMap () { if (gsDebug==PR_TRUE) printf("Build Cell Map...\n"); int rowCount = GetRowCount (); if (0 == rowCount) { if (gsDebug==PR_TRUE) printf("0 row count. Returning.\n"); mColCount = GetSpecifiedColumnCount (); // at least set known column count EnsureColumns(); return; } // Make an educated guess as to how many columns we have. It's // only a guess because we can't know exactly until we have // processed the last row. int childCount = ChildCount (); int groupIndex = NextRowGroup (-1); if (0 == mColCount) mColCount = GetSpecifiedColumnCount (); if (0 == mColCount) // no column parts { nsTableRowGroup *rowGroup = (nsTableRowGroup*)(ChildAt (groupIndex)); // rowGroup: REFCNT++ nsTableRow *row = (nsTableRow *)(rowGroup->ChildAt (0)); // row: REFCNT++ mColCount = row->GetMaxColumns (); if (gsDebug==PR_TRUE) printf("mColCount=0 at start. Guessing col count to be %d from a row.\n", mColCount); NS_RELEASE(rowGroup); // rowGroup: REFCNT-- NS_RELEASE(row); // row: REFCNT-- } if (nsnull==mCellMap) mCellMap = new nsCellMap(rowCount, mColCount); else mCellMap->Reset(rowCount, mColCount); if (gsDebug==PR_TRUE) printf("mCellMap set to (%d, %d)\n", rowCount, mColCount); int rowStart = 0; if (gsDebug==PR_TRUE) printf("childCount is %d\n", childCount); while (groupIndex < childCount) { if (gsDebug==PR_TRUE) printf(" groupIndex is %d\n", groupIndex); if (gsDebug==PR_TRUE) printf(" rowStart is %d\n", rowStart); nsTableRowGroup *rowGroup = (nsTableRowGroup *)ChildAt (groupIndex); // rowGroup: REFCNT++ int groupRowCount = rowGroup->ChildCount (); if (gsDebug==PR_TRUE) printf(" groupRowCount is %d\n", groupRowCount); for (int rowIndex = 0; rowIndex < groupRowCount; rowIndex++) { nsTableRow *row = (nsTableRow *)(rowGroup->ChildAt (rowIndex)); // row: REFCNT++ int cellCount = row->ChildCount (); int cellIndex = 0; int colIndex = 0; if (gsDebug==PR_TRUE) DumpCellMap(); if (gsDebug==PR_TRUE) printf(" rowIndex is %d, row->SetRowIndex(%d)\n", rowIndex, rowIndex + rowStart); row->SetRowIndex (rowIndex + rowStart); while (colIndex < mColCount) { if (gsDebug==PR_TRUE) printf(" colIndex = %d, with mColCount = %d\n", colIndex, mColCount); CellData *data =mCellMap->GetCellAt(rowIndex + rowStart, colIndex); if (nsnull == data) { if (gsDebug==PR_TRUE) printf(" null data from GetCellAt(%d,%d)\n", rowIndex+rowStart, colIndex); if (gsDebug==PR_TRUE) printf(" cellIndex=%d, cellCount=%d)\n", cellIndex, cellCount); if (cellIndex < cellCount) { nsTableCell* cell = (nsTableCell *) row->ChildAt (cellIndex); // cell: REFCNT++ if (gsDebug==PR_TRUE) printf(" calling BuildCellIntoMap(cell, %d, %d), and incrementing cellIndex\n", rowIndex + rowStart, colIndex); BuildCellIntoMap (cell, rowIndex + rowStart, colIndex); NS_RELEASE(cell); // cell: REFCNT-- cellIndex++; } } colIndex++; } if (cellIndex < cellCount) { // We didn't use all the cells in this row up. Grow the cell // data because we now know that we have more columns than we // originally thought we had. if (gsDebug==PR_TRUE) printf(" calling GrowCellMap because cellIndex < %d\n", cellIndex, cellCount); GrowCellMap (cellCount); while (cellIndex < cellCount) { if (gsDebug==PR_TRUE) printf(" calling GrowCellMap again because cellIndex < %d\n", cellIndex, cellCount); GrowCellMap (colIndex + 1); // ensure enough cols in map, may be low due to colspans CellData *data =mCellMap->GetCellAt(rowIndex + rowStart, colIndex); if (data == nsnull) { nsTableCell* cell = (nsTableCell *) row->ChildAt (cellIndex); // cell: REFCNT++ BuildCellIntoMap (cell, rowIndex + rowStart, colIndex); cellIndex++; NS_RELEASE(cell); // cell: REFCNT-- } colIndex++; } } NS_RELEASE(row); // row: REFCNT-- } NS_RELEASE(rowGroup); // rowGroup: REFCNT-- rowStart += groupRowCount; groupIndex = NextRowGroup (groupIndex); } if (gsDebug==PR_TRUE) DumpCellMap (); EnsureColumns(); } /** */ void nsTablePart::DumpCellMap () const { printf("dumping CellMap:\n"); if (nsnull != mCellMap) { int rowCount = mCellMap->GetRowCount(); int cols = mCellMap->GetColCount(); for (int r = 0; r < rowCount; r++) { if (gsDebug==PR_TRUE) { printf("row %d", r); printf(": "); } for (int c = 0; c < cols; c++) { CellData *cd =mCellMap->GetCellAt(r, c); if (cd != nsnull) { if (cd->mCell != nsnull) { printf("C%d,%d ", r, c); printf(" "); } else { nsTableCell *cell = cd->mRealCell->mCell; nsTableRow *row = cell->GetRow(); int rr = row->GetRowIndex (); int cc = cell->GetColIndex (); printf("S%d,%d ", rr, cc); if (cd->mOverlap != nsnull) { cell = cd->mOverlap->mCell; nsTableRow* row2 = cell->GetRow(); rr = row2->GetRowIndex (); cc = cell->GetColIndex (); printf("O%d,%c ", rr, cc); NS_RELEASE(row2); } else printf(" "); NS_RELEASE(row); } } else printf("---- "); } printf("\n"); } } else printf ("[nsnull]"); } void nsTablePart::BuildCellIntoMap (nsTableCell *aCell, PRInt32 aRowIndex, PRInt32 aColIndex) { NS_PRECONDITION (nsnull!=aCell, "bad cell arg"); NS_PRECONDITION (aColIndex < mColCount, "bad column index arg"); NS_PRECONDITION (aRowIndex < GetRowCount(), "bad row index arg"); // Setup CellMap for this cell int rowSpan = GetEffectiveRowSpan (aRowIndex, aCell); int colSpan = aCell->GetColSpan (); if (gsDebug==PR_TRUE) printf(" BuildCellIntoMap. rowSpan = %d, colSpan = %d\n", rowSpan, colSpan); // Grow the mCellMap array if we will end up addressing // some new columns. if (mColCount < (aColIndex + colSpan)) { if (gsDebug==PR_TRUE) printf(" mColCount=%dmCell = aCell; data->mRealCell = data; if (gsDebug==PR_TRUE) printf(" calling mCellMap->SetCellAt(data, %d, %d)\n", aRowIndex, aColIndex); mCellMap->SetCellAt(data, aRowIndex, aColIndex); aCell->SetColIndex (aColIndex); // Create CellData objects for the rows that this cell spans. Set // their mCell to nsnull and their mRealCell to point to data. If // there were no column overlaps then we could use the same // CellData object for each row that we span... if ((1 < rowSpan) || (1 < colSpan)) { if (gsDebug==PR_TRUE) printf(" spans\n"); for (int rowIndex = 0; rowIndex < rowSpan; rowIndex++) { if (gsDebug==PR_TRUE) printf(" rowIndex = %d\n", rowIndex); int workRow = aRowIndex + rowIndex; if (gsDebug==PR_TRUE) printf(" workRow = %d\n", workRow); for (int colIndex = 0; colIndex < colSpan; colIndex++) { if (gsDebug==PR_TRUE) printf(" colIndex = %d\n", colIndex); int workCol = aColIndex + colIndex; if (gsDebug==PR_TRUE) printf(" workCol = %d\n", workCol); CellData *testData = mCellMap->GetCellAt(workRow, workCol); if (nsnull == testData) { CellData *spanData = new CellData (); spanData->mRealCell = data; if (gsDebug==PR_TRUE) printf(" null GetCellAt(%d, %d) so setting to spanData\n", workRow, workCol); mCellMap->SetCellAt(spanData, workRow, workCol); } else if ((0 < rowIndex) || (0 < colIndex)) { // we overlap, replace existing data, it might be shared if (gsDebug==PR_TRUE) printf(" overlapping Cell from GetCellAt(%d, %d) so setting to spanData\n", workRow, workCol); CellData *overlap = new CellData (); overlap->mCell = testData->mCell; overlap->mRealCell = testData->mRealCell; overlap->mOverlap = data; mCellMap->SetCellAt(overlap, workRow, workCol); } } } } } void nsTablePart::GrowCellMap (PRInt32 aColCount) { if (nsnull!=mCellMap) { if (mColCount < aColCount) { mCellMap->GrowTo(aColCount); } mColCount = aColCount; } } PRInt32 nsTablePart::GetEffectiveRowSpan (PRInt32 aRowIndex, nsTableCell *aCell) { NS_PRECONDITION (nsnull!=aCell, "bad cell arg"); NS_PRECONDITION (0<=aRowIndex && aRowIndexGetRowSpan (); int rowCount = GetRowCount (); if (rowCount < (aRowIndex + rowSpan)) return (rowCount - aRowIndex); return rowSpan; } /** * Create a frame object that will layout this table. */ nsIFrame* nsTablePart::CreateFrame(nsIPresContext* aPresContext, nsIFrame* aParentFrame) { nsIFrame* rv; nsresult status = nsTableOuterFrame::NewFrame(&rv, this, aParentFrame); return rv; } void nsTablePart::SetAttribute(nsIAtom* aAttribute, const nsString& aValue) { nsHTMLValue val; if (aAttribute == nsHTMLAtoms::width) { ParseValueOrPercent(aValue, val, eHTMLUnit_Pixel); nsHTMLTagContent::SetAttribute(aAttribute, val); } else if ( aAttribute == nsHTMLAtoms::border) { nsHTMLValue val; nsAutoString tmp(aValue); tmp.StripWhitespace(); if (0 == tmp.Length()) { // Just enable the border; same as border=1 val.SetEmptyValue();/* XXX pixels? */ } else { ParseValue(aValue, 1, val, eHTMLUnit_Pixel); } nsHTMLTagContent::SetAttribute(aAttribute, val); } else if (aAttribute == nsHTMLAtoms::cellspacing || aAttribute == nsHTMLAtoms::cellpadding) { ParseValue(aValue, 0, val, eHTMLUnit_Pixel); nsHTMLTagContent::SetAttribute(aAttribute, val); return; } else if (aAttribute == nsHTMLAtoms::bgcolor) { ParseColor(aValue, val); nsHTMLTagContent::SetAttribute(aAttribute, val); return; } } void nsTablePart::MapAttributesInto(nsIStyleContext* aContext, nsIPresContext* aPresContext) { NS_PRECONDITION(nsnull!=aContext, "bad style context arg"); NS_PRECONDITION(nsnull!=aPresContext, "bad presentation context arg"); float p2t; nsHTMLValue value; // width GetAttribute(nsHTMLAtoms::width, value); if (value.GetUnit() != eHTMLUnit_Null) { nsStylePosition* position = (nsStylePosition*) aContext->GetData(kStylePositionSID); switch (value.GetUnit()) { case eHTMLUnit_Percent: position->mWidth.SetPercentValue(value.GetPercentValue()); break; case eHTMLUnit_Pixel: p2t = aPresContext->GetPixelsToTwips(); position->mWidth.SetCoordValue(nscoord(p2t * (float)value.GetPixelValue())); break; } } // border GetTableBorder(this, aContext, aPresContext, PR_FALSE); } void nsTablePart::GetTableBorder(nsIHTMLContent* aContent, nsIStyleContext* aContext, nsIPresContext* aPresContext, PRBool aForCell) { NS_PRECONDITION(nsnull!=aContent, "bad content arg"); NS_PRECONDITION(nsnull!=aContext, "bad style context arg"); NS_PRECONDITION(nsnull!=aPresContext, "bad presentation context arg"); nsHTMLValue value; aContent->GetAttribute(nsHTMLAtoms::border, value); if ((value.GetUnit() == eHTMLUnit_Pixel) || (value.GetUnit() == eHTMLUnit_Empty)) { nsStyleBorder* border = (nsStyleBorder*) aContext->GetData(kStyleBorderSID); nsStyleSpacing* spacing = (nsStyleSpacing*) aContext->GetData(kStyleSpacingSID); float p2t = aPresContext->GetPixelsToTwips(); nscoord twips = (aForCell || (value.GetUnit() == eHTMLUnit_Empty)) ? nscoord(NS_INT_PIXELS_TO_TWIPS(1, p2t)) : nscoord(NS_INT_PIXELS_TO_TWIPS(value.GetPixelValue(), p2t)); nscoord two = nscoord(NS_INT_PIXELS_TO_TWIPS(2,p2t)); spacing->mPadding.top = two; spacing->mPadding.right = two; spacing->mPadding.bottom = two; spacing->mPadding.left = two; border->mSize.top = twips; border->mSize.right = twips; border->mSize.bottom = twips; border->mSize.left = twips; if (border->mStyle[0] == NS_STYLE_BORDER_STYLE_NONE) { border->mStyle[0] = NS_STYLE_BORDER_STYLE_SOLID; } if (border->mStyle[1] == NS_STYLE_BORDER_STYLE_NONE) { border->mStyle[1] = NS_STYLE_BORDER_STYLE_SOLID; } if (border->mStyle[2] == NS_STYLE_BORDER_STYLE_NONE) { border->mStyle[2] = NS_STYLE_BORDER_STYLE_SOLID; } if (border->mStyle[3] == NS_STYLE_BORDER_STYLE_NONE) { border->mStyle[3] = NS_STYLE_BORDER_STYLE_SOLID; } } } /* ---------- Global Functions ---------- */ /** */ nsresult NS_NewTablePart(nsIHTMLContent** aInstancePtrResult, nsIAtom* aTag) { NS_PRECONDITION(nsnull != aInstancePtrResult, "nsnull ptr"); if (nsnull == aInstancePtrResult) { return NS_ERROR_NULL_POINTER; } nsIHTMLContent* table = new nsTablePart(aTag); if (nsnull == table) { return NS_ERROR_OUT_OF_MEMORY; } return table->QueryInterface(kIHTMLContentIID, (void **) aInstancePtrResult); }