1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
42 #include "nsVoidArray.h"
43 #include "nsTPtrArray.h"
47 #undef DEBUG_TABLE_CELLMAP
49 class nsTableColFrame
;
50 class nsTableCellFrame
;
51 class nsTableRowGroupFrame
;
55 class nsCellMapColumnIterator
;
59 PRInt32 mNumCellsOrig
; // number of cells originating in the col
60 PRInt32 mNumCellsSpan
; // number of cells spanning into the col via colspans (not rowspans)
63 nsColInfo(PRInt32 aNumCellsOrig
,
64 PRInt32 aNumCellsSpan
);
77 nsVoidArray mRightBorders
;
78 nsVoidArray mBottomBorders
;
79 BCData mLowerRightCorner
;
85 nsTableCellMap(nsTableFrame
& aTableFrame
,
86 PRBool aBorderCollapse
);
89 * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED
93 void RemoveGroupCellMap(nsTableRowGroupFrame
* aRowGroup
);
95 void InsertGroupCellMap(nsTableRowGroupFrame
& aNewRowGroup
,
96 nsTableRowGroupFrame
*& aPrevRowGroup
);
99 * Get the nsCellMap for the given row group. If aStartHint is non-null,
100 * will start looking with that cellmap and only fall back to starting at the
101 * beginning of the list if that doesn't find us the right nsCellMap.
102 * Otherwise, just start at the beginning.
104 * aRowGroup must not be null.
106 nsCellMap
* GetMapFor(const nsTableRowGroupFrame
* aRowGroup
,
107 nsCellMap
* aStartHint
) const;
109 /** synchronize the cellmaps with the rowgroups again **/
110 void Synchronize(nsTableFrame
* aTableFrame
);
112 nsTableCellFrame
* GetCellFrame(PRInt32 aRowIndex
,
115 PRBool aUseRowIfOverlap
) const;
117 /** return the CellData for the cell at (aRowIndex, aColIndex) */
118 CellData
* GetDataAt(PRInt32 aRowIndex
,
119 PRInt32 aColIndex
) const;
121 // this function creates a col if needed
122 nsColInfo
* GetColInfoAt(PRInt32 aColIndex
);
124 /** append the cellFrame at the end of the row at aRowIndex and return the col index
126 CellData
* AppendCell(nsTableCellFrame
& aCellFrame
,
128 PRBool aRebuildIfNecessary
,
129 nsRect
& aDamageArea
);
131 void InsertCells(nsVoidArray
& aCellFrames
,
133 PRInt32 aColIndexBefore
,
134 nsRect
& aDamageArea
);
136 void RemoveCell(nsTableCellFrame
* aCellFrame
,
138 nsRect
& aDamageArea
);
139 /** Remove the previously gathered column information */
141 void InsertRows(nsTableRowGroupFrame
& aRowGroup
,
143 PRInt32 aFirstRowIndex
,
144 PRBool aConsiderSpans
,
145 nsRect
& aDamageArea
);
147 void RemoveRows(PRInt32 aFirstRowIndex
,
148 PRInt32 aNumRowsToRemove
,
149 PRBool aConsiderSpans
,
150 nsRect
& aDamageArea
);
152 PRInt32
GetNumCellsOriginatingInRow(PRInt32 aRowIndex
) const;
153 PRInt32
GetNumCellsOriginatingInCol(PRInt32 aColIndex
) const;
155 /** indicate whether the row has more than one cell that either originates
156 * or is spanned from the rows above
158 PRBool
HasMoreThanOneCell(PRInt32 aRowIndex
) const;
160 PRInt32
GetEffectiveRowSpan(PRInt32 aRowIndex
,
161 PRInt32 aColIndex
) const;
162 PRInt32
GetEffectiveColSpan(PRInt32 aRowIndex
,
163 PRInt32 aColIndex
) const;
165 /** return the total number of columns in the table represented by this CellMap */
166 PRInt32
GetColCount() const;
168 /** return the actual number of rows in the table represented by this CellMap */
169 PRInt32
GetRowCount() const;
171 nsTableCellFrame
* GetCellInfoAt(PRInt32 aRowX
,
173 PRBool
* aOriginates
= nsnull
,
174 PRInt32
* aColSpan
= nsnull
) const;
177 * Returns the index at the given row and column coordinates.
179 * @see nsITableLayout::GetIndexByRowAndColumn()
181 * @param aRow [in] the row coordinate
182 * @param aColumn [in] the column coordinate
183 * @returns the index for the cell
185 PRInt32
GetIndexByRowAndColumn(PRInt32 aRow
, PRInt32 aColumn
) const;
188 * Retrieves the row and column coordinates for the given index.
190 * @see nsITableLayout::GetRowAndColumnByIndex()
192 * @param aIndex [in] the index for which coordinates are to be retrieved
193 * @param aRow [out] the row coordinate to be returned
194 * @param aColumn [out] the column coordinate to be returned
196 void GetRowAndColumnByIndex(PRInt32 aIndex
,
197 PRInt32
*aRow
, PRInt32
*aColumn
) const;
199 void AddColsAtEnd(PRUint32 aNumCols
);
200 void RemoveColsAtEnd();
202 PRBool
RowIsSpannedInto(PRInt32 aRowIndex
, PRInt32 aNumEffCols
) const;
203 PRBool
RowHasSpanningCells(PRInt32 aRowIndex
, PRInt32 aNumEffCols
) const;
204 void RebuildConsideringCells(nsCellMap
* aCellMap
,
205 nsVoidArray
* aCellFrames
,
209 nsRect
& aDamageArea
);
213 * Rebuild due to rows being inserted or deleted with cells spanning
214 * into or out of the rows. This function can only handle insertion
215 * or deletion but NOT both. So either aRowsToInsert must be null
216 * or aNumRowsToRemove must be 0.
218 * // XXXbz are both allowed to happen? That'd be a no-op...
220 void RebuildConsideringRows(nsCellMap
* aCellMap
,
221 PRInt32 aStartRowIndex
,
222 nsVoidArray
* aRowsToInsert
,
223 PRInt32 aNumRowsToRemove
,
224 nsRect
& aDamageArea
);
227 PRBool
ColIsSpannedInto(PRInt32 aColIndex
) const;
228 PRBool
ColHasSpanningCells(PRInt32 aColIndex
) const;
230 void ExpandZeroColSpans();
232 BCData
* GetBCData(PRUint8 aSide
,
236 PRBool aIsLowerRight
= PR_FALSE
);
238 void SetBCBorderEdge(PRUint8 aEdge
,
240 PRUint32 aCellMapStart
,
244 BCBorderOwner aOwner
,
248 void SetBCBorderCorner(Corner aCorner
,
250 PRUint32 aCellMapStart
,
256 PRBool aIsBottomRight
= PR_FALSE
);
258 /** dump a representation of the cell map to stdout for debugging */
260 void Dump(char* aString
= nsnull
) const;
264 BCData
* GetRightMostBorder(PRInt32 aRowIndex
);
265 BCData
* GetBottomMostBorder(PRInt32 aColIndex
);
267 friend class nsCellMap
;
268 friend class BCMapCellIterator
;
269 friend class BCMapBorderIterator
;
270 friend class nsCellMapColumnIterator
;
272 /** Insert a row group cellmap after aPrevMap, if aPrefMap is null insert it
273 * at the beginning, the ordering of the cellmap corresponds to the ordering of
274 * rowgroups once OrderRowGroups has been called
276 void InsertGroupCellMap(nsCellMap
* aPrevMap
,
278 void DeleteRightBottomBorders();
280 nsTableFrame
& mTableFrame
;
281 nsAutoVoidArray mCols
;
282 nsCellMap
* mFirstMap
;
283 // border collapsing info
287 /** nsCellMap is a support class for nsTablePart.
288 * It maintains an Rows x Columns grid onto which the cells of the table are mapped.
289 * This makes processing of rowspan and colspan attributes much easier.
290 * Each cell is represented by a CellData object.
293 * @see nsTableFrame::AddCellToMap
294 * @see nsTableFrame::GrowCellMap
295 * @see nsTableFrame::BuildCellIntoMap
297 * mRows is an array of rows. Each row is an array of cells. a cell
304 * @param aRowGroupFrame the row group frame this is a cellmap for
305 * @param aIsBC whether the table is doing border-collapse
307 nsCellMap(nsTableRowGroupFrame
& aRowGroupFrame
, PRBool aIsBC
);
310 * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED
314 static nsresult
Init();
315 static void Shutdown();
317 nsCellMap
* GetNextSibling() const;
318 void SetNextSibling(nsCellMap
* aSibling
);
320 nsTableRowGroupFrame
* GetRowGroup() const;
322 nsTableCellFrame
* GetCellFrame(PRInt32 aRowIndex
,
325 PRBool aUseRowSpanIfOverlap
) const;
328 * Returns the index of the given row and column coordinates.
330 * @see nsITableLayout::GetIndexByRowAndColumn()
332 * @param aColCount [in] the number of columns in a row
333 * @param aRow [in] the row coordinate
334 * @param aColumn [in] the column coordinate
336 PRInt32
GetIndexByRowAndColumn(PRInt32 aColCount
,
337 PRInt32 aRow
, PRInt32 aColumn
) const;
340 * Get the row and column coordinates at the given index.
342 * @see nsITableLayout::GetRowAndColumnByIndex()
344 * @param aColCount [in] the number of columns in a row
345 * @param aIndex [in] the index for which coordinates are to be retrieved
346 * @param aRow [out] the row coordinate to be returned
347 * @param aColumn [out] the column coordinate to be returned
349 void GetRowAndColumnByIndex(PRInt32 aColCount
, PRInt32 aIndex
,
350 PRInt32
*aRow
, PRInt32
*aColumn
) const;
352 /** append the cellFrame at an empty or dead cell or finally at the end of
353 * the row at aRowIndex and return a pointer to the celldata entry in the
356 * @param aMap - reference to the table cell map
357 * @param aCellFrame - a pointer to the cellframe which will be appended
359 * @param aRowIndex - to this row the celldata entry will be added
360 * @param aRebuildIfNecessay - if a cell spans into a row below it might be
361 * necesserary to rebuild the cellmap as this rowspan
362 * might overlap another cell.
363 * @param aDamageArea - area in cellmap coordinates which have been updated.
364 * @param aColToBeginSearch - if not null contains the column number where
365 * the search for a empty or dead cell in the
367 * @return - a pointer to the celldata entry inserted into
370 CellData
* AppendCell(nsTableCellMap
& aMap
,
371 nsTableCellFrame
* aCellFrame
,
373 PRBool aRebuildIfNecessary
,
375 PRInt32
* aBeginSearchAtCol
= nsnull
);
377 /** Function to be called when a cell is added at a location which is spanned
378 * to by a zero colspan. We handle this situation by collapsing the zero
379 * colspan, since there is really no good way to deal with it (trying to
380 * increase the number of columns to hold the new cell would just mean the
381 * zero colspan needs to expand).
383 * @param aMap - reference to the table cell map
384 * @param aOrigData - zero colspanned cell that will be collapsed
385 * @param aRowIndex - row where the first collision appears
386 * @param aColIndex - column where the first collision appears
388 void CollapseZeroColSpan(nsTableCellMap
& aMap
,
393 void InsertCells(nsTableCellMap
& aMap
,
394 nsVoidArray
& aCellFrames
,
396 PRInt32 aColIndexBefore
,
397 nsRect
& aDamageArea
);
399 void RemoveCell(nsTableCellMap
& aMap
,
400 nsTableCellFrame
* aCellFrame
,
402 nsRect
& aDamageArea
);
404 void InsertRows(nsTableCellMap
& aMap
,
406 PRInt32 aFirstRowIndex
,
407 PRBool aConsiderSpans
,
408 nsRect
& aDamageArea
);
410 void RemoveRows(nsTableCellMap
& aMap
,
411 PRInt32 aFirstRowIndex
,
412 PRInt32 aNumRowsToRemove
,
413 PRBool aConsiderSpans
,
414 nsRect
& aDamageArea
);
416 PRInt32
GetNumCellsOriginatingInRow(PRInt32 aRowIndex
) const;
417 PRInt32
GetNumCellsOriginatingInCol(PRInt32 aColIndex
) const;
419 /** return the number of rows in the table represented by this CellMap */
420 PRInt32
GetRowCount(PRBool aConsiderDeadRowSpanRows
= PR_FALSE
) const;
422 nsTableCellFrame
* GetCellInfoAt(const nsTableCellMap
& aMap
,
425 PRBool
* aOriginates
= nsnull
,
426 PRInt32
* aColSpan
= nsnull
) const;
428 PRBool
RowIsSpannedInto(PRInt32 aRowIndex
,
429 PRInt32 aNumEffCols
) const;
431 PRBool
RowHasSpanningCells(PRInt32 aRowIndex
,
432 PRInt32 aNumEffCols
) const;
434 PRBool
ColHasSpanningCells(PRInt32 aColIndex
) const;
436 void ExpandZeroColSpans(nsTableCellMap
& aMap
);
438 /** indicate whether the row has more than one cell that either originates
439 * or is spanned from the rows above
441 PRBool
HasMoreThanOneCell(PRInt32 aRowIndex
) const;
443 /* Get the rowspan for a cell starting at aRowIndex and aColIndex.
444 * If aGetEffective is true the size will not exceed the last content based
445 * row. Cells can have a specified rowspan that extends below the last
446 * content based row. This is legitimate considering incr. reflow where the
447 * content rows will arive later.
449 PRInt32
GetRowSpan(PRInt32 aRowIndex
,
451 PRBool aGetEffective
) const;
453 PRInt32
GetEffectiveColSpan(const nsTableCellMap
& aMap
,
456 PRBool
& aIsZeroColSpan
) const;
458 typedef nsTPtrArray
<CellData
> CellDataArray
;
460 /** dump a representation of the cell map to stdout for debugging */
462 void Dump(PRBool aIsBorderCollapse
) const;
466 friend class nsTableCellMap
;
467 friend class BCMapCellIterator
;
468 friend class BCMapBorderIterator
;
469 friend class nsTableFrame
;
470 friend class nsCellMapColumnIterator
;
473 * Increase the number of rows in this cellmap by aNumRows. Put the
474 * new rows at aRowIndex. If aRowIndex is -1, put them at the end.
476 PRBool
Grow(nsTableCellMap
& aMap
,
478 PRInt32 aRowIndex
= -1);
480 void GrowRow(CellDataArray
& aRow
,
483 /** assign aCellData to the cell at (aRow,aColumn) */
484 void SetDataAt(nsTableCellMap
& aMap
,
486 PRInt32 aMapRowIndex
,
489 CellData
* GetDataAt(PRInt32 aMapRowIndex
,
490 PRInt32 aColIndex
) const;
492 PRInt32
GetNumCellsIn(PRInt32 aColIndex
) const;
494 void ExpandWithRows(nsTableCellMap
& aMap
,
495 nsVoidArray
& aRowFrames
,
496 PRInt32 aStartRowIndex
,
497 nsRect
& aDamageArea
);
499 void ExpandWithCells(nsTableCellMap
& aMap
,
500 nsVoidArray
& aCellFrames
,
504 PRBool aRowSpanIsZero
,
505 nsRect
& aDamageArea
);
507 void ShrinkWithoutRows(nsTableCellMap
& aMap
,
508 PRInt32 aFirstRowIndex
,
509 PRInt32 aNumRowsToRemove
,
510 nsRect
& aDamageArea
);
512 void ShrinkWithoutCell(nsTableCellMap
& aMap
,
513 nsTableCellFrame
& aCellFrame
,
516 nsRect
& aDamageArea
);
519 * Rebuild due to rows being inserted or deleted with cells spanning
520 * into or out of the rows. This function can only handle insertion
521 * or deletion but NOT both. So either aRowsToInsert must be null
522 * or aNumRowsToRemove must be 0.
524 * // XXXbz are both allowed to happen? That'd be a no-op...
526 void RebuildConsideringRows(nsTableCellMap
& aMap
,
527 PRInt32 aStartRowIndex
,
528 nsVoidArray
* aRowsToInsert
,
529 PRInt32 aNumRowsToRemove
,
530 nsRect
& aDamageArea
);
532 void RebuildConsideringCells(nsTableCellMap
& aMap
,
533 PRInt32 aNumOrigCols
,
534 nsVoidArray
* aCellFrames
,
538 nsRect
& aDamageArea
);
540 PRBool
CellsSpanOut(nsVoidArray
& aNewRows
) const;
542 /** If a cell spans out of the area defined by aStartRowIndex, aEndRowIndex
543 * and aStartColIndex, aEndColIndex the cellmap changes are more severe so
544 * the corresponding routines needs to be called. This is also necessary if
545 * cells outside spans into this region.
546 * @aStartRowIndex - y start index
547 * @aEndRowIndex - y end index
548 * @param aStartColIndex - x start index
549 * @param aEndColIndex - x end index
550 * @return - true if a cell span crosses the border of the
553 PRBool
CellsSpanInOrOut(PRInt32 aStartRowIndex
,
554 PRInt32 aEndRowIndex
,
555 PRInt32 aStartColIndex
,
556 PRInt32 aEndColIndex
) const;
558 void ExpandForZeroSpan(nsTableCellFrame
* aCellFrame
,
559 PRInt32 aNumColsInTable
);
561 PRBool
CreateEmptyRow(PRInt32 aRowIndex
,
564 PRInt32
GetRowSpanForNewCell(nsTableCellFrame
* aCellFrameToAdd
,
566 PRBool
& aIsZeroRowSpan
) const;
568 PRInt32
GetColSpanForNewCell(nsTableCellFrame
& aCellFrameToAdd
,
569 PRBool
& aIsZeroColSpan
) const;
571 PRBool
IsZeroColSpan(PRInt32 aRowIndex
,
572 PRInt32 aColIndex
) const;
574 // Destroy a CellData struct. This will handle the case of aData
575 // actually being a BCCellData properly.
576 void DestroyCellData(CellData
* aData
);
577 // Allocate a CellData struct. This will handle needing to create a
578 // BCCellData properly.
579 // @param aOrigCell the originating cell to pass to the celldata constructor
580 CellData
* AllocCellData(nsTableCellFrame
* aOrigCell
);
582 /** an array containing, for each row, the CellDatas for the cells
583 * in that row. It can be larger than mContentRowCount due to row spans
584 * extending beyond the table */
585 // XXXbz once we have auto TArrays, we should probably use them here.
586 nsTArray
<CellDataArray
> mRows
;
588 /** the number of rows in the table (content) which is not indentical to the
589 * number of rows in the cell map due to row spans extending beyond the end
590 * of thetable (dead rows) or empty tr tags
592 PRInt32 mContentRowCount
;
594 // the row group that corresponds to this map
595 nsTableRowGroupFrame
* mRowGroupFrame
;
597 // the next row group cell map
598 nsCellMap
* mNextSibling
;
600 // Whether this is a BC cellmap or not
603 // Prescontext to deallocate and allocate celldata
604 nsCOMPtr
<nsPresContext
> mPresContext
;
608 * A class for iterating the cells in a given column. Must be given a
609 * non-null nsTableCellMap and a column number valid for that cellmap.
611 class nsCellMapColumnIterator
614 nsCellMapColumnIterator(const nsTableCellMap
* aMap
, PRInt32 aCol
) :
615 mMap(aMap
), mCurMap(aMap
->mFirstMap
), mCurMapStart(0),
616 mCurMapRow(0), mCol(aCol
), mFoundCells(0)
618 NS_PRECONDITION(aMap
, "Must have map");
619 NS_PRECONDITION(mCol
< aMap
->GetColCount(), "Invalid column");
620 mOrigCells
= aMap
->GetNumCellsOriginatingInCol(mCol
);
622 mCurMapContentRowCount
= mCurMap
->GetRowCount();
623 PRUint32 rowArrayLength
= mCurMap
->mRows
.Length();
624 mCurMapRelevantRowCount
= PR_MIN(mCurMapContentRowCount
, rowArrayLength
);
625 if (mCurMapRelevantRowCount
== 0 && mOrigCells
> 0) {
626 // This row group is useless; advance!
632 NS_ASSERTION(mOrigCells
== 0, "Why no rowgroups?");
637 nsTableCellFrame
* GetNextFrame(PRInt32
* aRow
, PRInt32
* aColSpan
);
640 void AdvanceRowGroup();
642 // Advance the row; aIncrement is considered to be a cell's rowspan,
643 // so if 0 is passed in we'll advance to the next rowgroup.
644 void IncrementRow(PRInt32 aIncrement
);
646 const nsTableCellMap
* mMap
;
647 const nsCellMap
* mCurMap
;
649 // mCurMapStart is the row in the entire nsTableCellMap where
650 // mCurMap starts. This is used to compute row indices to pass to
651 // nsTableCellMap::GetDataAt, so must be a _content_ row index.
652 PRUint32 mCurMapStart
;
654 // In steady-state mCurMapRow is the row in our current nsCellMap
655 // that we'll use the next time GetNextFrame() is called. Due to
656 // the way we skip over rowspans, the entry in mCurMapRow and mCol
657 // is either null, dead, originating, or a colspan. In particular,
658 // it cannot be a rowspan or overlap entry.
662 PRUint32 mFoundCells
;
664 // The number of content rows in mCurMap. This may be bigger than the number
665 // of "relevant" rows, or it might be smaller.
666 PRUint32 mCurMapContentRowCount
;
668 // The number of "relevant" rows in mCurMap. That is, the number of rows
669 // which might have an originating cell in them. Once mCurMapRow reaches
670 // mCurMapRelevantRowCount, we should move to the next map.
671 PRUint32 mCurMapRelevantRowCount
;
675 /* ----- inline methods ----- */
676 inline PRInt32
nsTableCellMap::GetColCount() const
678 return mCols
.Count();
681 inline nsCellMap
* nsCellMap::GetNextSibling() const
686 inline void nsCellMap::SetNextSibling(nsCellMap
* aSibling
)
688 mNextSibling
= aSibling
;
691 inline nsTableRowGroupFrame
* nsCellMap::GetRowGroup() const
693 return mRowGroupFrame
;
696 inline PRInt32
nsCellMap::GetRowCount(PRBool aConsiderDeadRowSpanRows
) const
698 PRInt32 rowCount
= (aConsiderDeadRowSpanRows
) ? mRows
.Length() : mContentRowCount
;
704 inline nsColInfo::nsColInfo()
705 :mNumCellsOrig(0), mNumCellsSpan(0)
708 inline nsColInfo::nsColInfo(PRInt32 aNumCellsOrig
,
709 PRInt32 aNumCellsSpan
)
710 :mNumCellsOrig(aNumCellsOrig
), mNumCellsSpan(aNumCellsSpan
)