nsCellMap.h (21867B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 #ifndef nsCellMap_h__ 6 #define nsCellMap_h__ 7 8 #include <algorithm> 9 10 #include "TableArea.h" 11 #include "celldata.h" 12 #include "nsCOMPtr.h" 13 #include "nsRect.h" 14 #include "nsTArray.h" 15 #include "nscore.h" 16 17 #undef DEBUG_TABLE_CELLMAP 18 19 class nsTableCellFrame; 20 class nsTableRowFrame; 21 class nsTableRowGroupFrame; 22 class nsTableFrame; 23 class nsCellMap; 24 class nsPresContext; 25 class nsCellMapColumnIterator; 26 27 struct nsColInfo { 28 int32_t mNumCellsOrig; // number of cells originating in the col 29 int32_t mNumCellsSpan; // number of cells spanning into the col via colspans 30 // (not rowspans) 31 32 nsColInfo(); 33 nsColInfo(int32_t aNumCellsOrig, int32_t aNumCellsSpan); 34 }; 35 36 struct BCInfo { 37 nsTArray<BCData> mIEndBorders; 38 nsTArray<BCData> mBEndBorders; 39 BCData mBEndIEndCorner; 40 }; 41 42 class nsTableCellMap { 43 typedef mozilla::TableArea TableArea; 44 45 public: 46 nsTableCellMap(nsTableFrame& aTableFrame, bool aBorderCollapse); 47 48 /** destructor 49 * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED 50 */ 51 ~nsTableCellMap(); 52 53 void RemoveGroupCellMap(nsTableRowGroupFrame* aRowGroup); 54 55 void InsertGroupCellMap(nsTableRowGroupFrame* aNewRowGroup, 56 nsTableRowGroupFrame*& aPrevRowGroup); 57 58 /** 59 * Get the nsCellMap for the given row group. If aStartHint is non-null, 60 * will start looking with that cellmap and only fall back to starting at the 61 * beginning of the list if that doesn't find us the right nsCellMap. 62 * Otherwise, just start at the beginning. 63 * 64 * aRowGroup must not be null. 65 */ 66 nsCellMap* GetMapFor(const nsTableRowGroupFrame* aRowGroup, 67 nsCellMap* aStartHint) const; 68 69 /** synchronize the cellmaps with the rowgroups again **/ 70 void Synchronize(nsTableFrame* aTableFrame); 71 72 nsTableCellFrame* GetCellFrame(int32_t aRowIndex, int32_t aColIndex, 73 CellData& aData, bool aUseRowIfOverlap) const; 74 75 /** return the CellData for the cell at (aRowIndex, aColIndex) */ 76 CellData* GetDataAt(int32_t aRowIndex, int32_t aColIndex) const; 77 78 // this function creates a col if needed 79 nsColInfo* GetColInfoAt(int32_t aColIndex); 80 81 /** append the cellFrame at the end of the row at aRowIndex and return the col 82 * index 83 */ 84 CellData* AppendCell(nsTableCellFrame& aCellFrame, int32_t aRowIndex, 85 bool aRebuildIfNecessary, TableArea& aDamageArea); 86 87 void InsertCells(nsTArray<nsTableCellFrame*>& aCellFrames, int32_t aRowIndex, 88 int32_t aColIndexBefore, TableArea& aDamageArea); 89 90 void RemoveCell(nsTableCellFrame* aCellFrame, int32_t aRowIndex, 91 TableArea& aDamageArea); 92 /** Remove the previously gathered column information */ 93 void ClearCols(); 94 void InsertRows(nsTableRowGroupFrame* aRowGroup, 95 nsTArray<nsTableRowFrame*>& aRows, int32_t aFirstRowIndex, 96 bool aConsiderSpans, TableArea& aDamageArea); 97 98 void RemoveRows(int32_t aFirstRowIndex, int32_t aNumRowsToRemove, 99 bool aConsiderSpans, TableArea& aDamageArea); 100 101 int32_t GetNumCellsOriginatingInRow(int32_t aRowIndex) const; 102 int32_t GetNumCellsOriginatingInCol(int32_t aColIndex) const; 103 104 /** indicate whether the row has more than one cell that either originates 105 * or is spanned from the rows above 106 */ 107 bool HasMoreThanOneCell(int32_t aRowIndex) const; 108 109 int32_t GetEffectiveRowSpan(int32_t aRowIndex, int32_t aColIndex) const; 110 int32_t GetEffectiveColSpan(int32_t aRowIndex, int32_t aColIndex) const; 111 112 /** return the total number of columns in the table represented by this 113 * CellMap */ 114 int32_t GetColCount() const; 115 116 /** return the actual number of rows in the table represented by this CellMap 117 */ 118 int32_t GetRowCount() const; 119 120 nsTableCellFrame* GetCellInfoAt(int32_t aRowX, int32_t aColX, 121 bool* aOriginates = nullptr, 122 int32_t* aColSpan = nullptr) const; 123 124 /** 125 * Returns the index at the given row and column coordinates. 126 * 127 * @see nsITableLayout::GetIndexByRowAndColumn() 128 * 129 * @param aRow [in] the row coordinate 130 * @param aColumn [in] the column coordinate 131 * @returns the index for the cell 132 */ 133 int32_t GetIndexByRowAndColumn(int32_t aRow, int32_t aColumn) const; 134 135 /** 136 * Retrieves the row and column coordinates for the given index. 137 * 138 * @see nsITableLayout::GetRowAndColumnByIndex() 139 * 140 * @param aIndex [in] the index for which coordinates are to be retrieved 141 * @param aRow [out] the row coordinate to be returned 142 * @param aColumn [out] the column coordinate to be returned 143 */ 144 void GetRowAndColumnByIndex(int32_t aIndex, int32_t* aRow, 145 int32_t* aColumn) const; 146 147 void AddColsAtEnd(uint32_t aNumCols); 148 void RemoveColsAtEnd(); 149 150 bool RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) const; 151 bool RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) const; 152 void RebuildConsideringCells(nsCellMap* aCellMap, 153 nsTArray<nsTableCellFrame*>* aCellFrames, 154 int32_t aRowIndex, int32_t aColIndex, 155 bool aInsert, TableArea& aDamageArea); 156 157 protected: 158 /** 159 * Rebuild due to rows being inserted or deleted with cells spanning 160 * into or out of the rows. This function can only handle insertion 161 * or deletion but NOT both. So either aRowsToInsert must be null 162 * or aNumRowsToRemove must be 0. 163 * 164 * // XXXbz are both allowed to happen? That'd be a no-op... 165 */ 166 void RebuildConsideringRows(nsCellMap* aCellMap, int32_t aStartRowIndex, 167 nsTArray<nsTableRowFrame*>* aRowsToInsert, 168 int32_t aNumRowsToRemove, TableArea& aDamageArea); 169 170 public: 171 void ResetBStartStart(mozilla::LogicalSide aSide, nsCellMap& aCellMap, 172 uint32_t aRowGroupStart, uint32_t aYPos, 173 uint32_t aXPos); 174 175 void SetBCBorderEdge(mozilla::LogicalSide aEdge, nsCellMap& aCellMap, 176 uint32_t aCellMapStart, uint32_t aYPos, uint32_t aXPos, 177 uint32_t aLength, BCBorderOwner aOwner, nscoord aSize, 178 bool aChanged); 179 180 void SetBCBorderCorner(mozilla::LogicalCorner aCorner, nsCellMap& aCellMap, 181 uint32_t aCellMapStart, uint32_t aYPos, uint32_t aXPos, 182 mozilla::LogicalSide aOwner, nscoord aSubSize, 183 bool aBevel, bool aIsBottomRight = false); 184 185 /** dump a representation of the cell map to stdout for debugging */ 186 #ifdef DEBUG 187 void Dump(char* aString = nullptr) const; 188 #endif 189 190 protected: 191 BCData* GetIEndMostBorder(int32_t aRowIndex); 192 BCData* GetBEndMostBorder(int32_t aColIndex); 193 194 friend class nsCellMap; 195 friend class BCMapCellIterator; 196 friend class BCPaintBorderIterator; 197 friend class nsCellMapColumnIterator; 198 199 /** Insert a row group cellmap after aPrevMap, if aPrefMap is null insert it 200 * at the beginning, the ordering of the cellmap corresponds to the ordering 201 * of rowgroups once OrderRowGroups has been called 202 */ 203 void InsertGroupCellMap(nsCellMap* aPrevMap, nsCellMap& aNewMap); 204 void DeleteIEndBEndBorders(); 205 206 nsTableFrame& mTableFrame; 207 AutoTArray<nsColInfo, 8> mCols; 208 nsCellMap* mFirstMap; 209 // border collapsing info 210 BCInfo* mBCInfo; 211 }; 212 213 /** 214 * It maintains an Rows x Columns grid onto which the cells of the table are 215 * mapped. This makes processing of rowspan and colspan attributes much easier. 216 * Each cell is represented by a CellData object. 217 * 218 * @see CellData 219 * @see nsTableFrame::AddCellToMap 220 * @see nsTableFrame::GrowCellMap 221 * @see nsTableFrame::BuildCellIntoMap 222 * 223 * mRows is an array of rows. Each row is an array of cells. a cell 224 * can be null. 225 */ 226 class nsCellMap { 227 typedef mozilla::TableArea TableArea; 228 229 public: 230 /** constructor 231 * @param aRowGroupFrame the row group frame this is a cellmap for 232 * @param aIsBC whether the table is doing border-collapse 233 */ 234 nsCellMap(nsTableRowGroupFrame* aRowGroupFrame, bool aIsBC); 235 236 /** destructor 237 * NOT VIRTUAL BECAUSE THIS CLASS SHOULD **NEVER** BE SUBCLASSED 238 */ 239 ~nsCellMap(); 240 241 static void Init(); 242 static void Shutdown(); 243 244 nsCellMap* GetNextSibling() const; 245 void SetNextSibling(nsCellMap* aSibling); 246 247 nsTableRowGroupFrame* GetRowGroup() const; 248 249 nsTableCellFrame* GetCellFrame(int32_t aRowIndex, int32_t aColIndex, 250 CellData& aData, 251 bool aUseRowSpanIfOverlap) const; 252 253 /** 254 * Returns highest cell index within the cell map. 255 * 256 * @param aColCount [in] the number of columns in the table 257 */ 258 int32_t GetHighestIndex(int32_t aColCount); 259 260 /** 261 * Returns the index of the given row and column coordinates. 262 * 263 * @see nsITableLayout::GetIndexByRowAndColumn() 264 * 265 * @param aColCount [in] the number of columns in the table 266 * @param aRow [in] the row coordinate 267 * @param aColumn [in] the column coordinate 268 */ 269 int32_t GetIndexByRowAndColumn(int32_t aColCount, int32_t aRow, 270 int32_t aColumn) const; 271 272 /** 273 * Get the row and column coordinates at the given index. 274 * 275 * @see nsITableLayout::GetRowAndColumnByIndex() 276 * 277 * @param aColCount [in] the number of columns in the table 278 * @param aIndex [in] the index for which coordinates are to be retrieved 279 * @param aRow [out] the row coordinate to be returned 280 * @param aColumn [out] the column coordinate to be returned 281 */ 282 void GetRowAndColumnByIndex(int32_t aColCount, int32_t aIndex, int32_t* aRow, 283 int32_t* aColumn) const; 284 285 /** append the cellFrame at an empty or dead cell or finally at the end of 286 * the row at aRowIndex and return a pointer to the celldata entry in the 287 * cellmap 288 * 289 * @param aMap - reference to the table cell map 290 * @param aCellFrame - a pointer to the cellframe which will be 291 * appended to the row 292 * @param aRowIndex - to this row the celldata entry will be added 293 * @param aRebuildIfNecessay - if a cell spans into a row below it might be 294 * necesserary to rebuild the cellmap as this 295 * rowspan might overlap another cell. 296 * @param aDamageArea - area in cellmap coordinates which have been 297 * updated. 298 * @param aColToBeginSearch - if not null contains the column number where 299 * the search for a empty or dead cell in the 300 * row should start 301 * @return - a pointer to the celldata entry inserted into 302 * the cellmap 303 */ 304 CellData* AppendCell(nsTableCellMap& aMap, nsTableCellFrame* aCellFrame, 305 int32_t aRowIndex, bool aRebuildIfNecessary, 306 int32_t aRgFirstRowIndex, TableArea& aDamageArea, 307 int32_t* aBeginSearchAtCol = nullptr); 308 309 void InsertCells(nsTableCellMap& aMap, 310 nsTArray<nsTableCellFrame*>& aCellFrames, int32_t aRowIndex, 311 int32_t aColIndexBefore, int32_t aRgFirstRowIndex, 312 TableArea& aDamageArea); 313 314 void RemoveCell(nsTableCellMap& aMap, nsTableCellFrame* aCellFrame, 315 int32_t aRowIndex, int32_t aRgFirstRowIndex, 316 TableArea& aDamageArea); 317 318 void InsertRows(nsTableCellMap& aMap, nsTArray<nsTableRowFrame*>& aRows, 319 int32_t aFirstRowIndex, bool aConsiderSpans, 320 int32_t aRgFirstRowIndex, TableArea& aDamageArea); 321 322 void RemoveRows(nsTableCellMap& aMap, int32_t aFirstRowIndex, 323 int32_t aNumRowsToRemove, bool aConsiderSpans, 324 int32_t aRgFirstRowIndex, TableArea& aDamageArea); 325 326 int32_t GetNumCellsOriginatingInRow(int32_t aRowIndex) const; 327 int32_t GetNumCellsOriginatingInCol(int32_t aColIndex) const; 328 329 /** return the number of rows in the table represented by this CellMap */ 330 int32_t GetRowCount(bool aConsiderDeadRowSpanRows = false) const; 331 332 nsTableCellFrame* GetCellInfoAt(const nsTableCellMap& aMap, int32_t aRowX, 333 int32_t aColX, bool* aOriginates = nullptr, 334 int32_t* aColSpan = nullptr) const; 335 336 bool RowIsSpannedInto(int32_t aRowIndex, int32_t aNumEffCols) const; 337 338 bool RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols) const; 339 340 /** indicate whether the row has more than one cell that either originates 341 * or is spanned from the rows above 342 */ 343 bool HasMoreThanOneCell(int32_t aRowIndex) const; 344 345 /* Get the rowspan for a cell starting at aRowIndex and aColIndex. 346 * If aGetEffective is true the size will not exceed the last content based 347 * row. Cells can have a specified rowspan that extends below the last 348 * content based row. This is legitimate considering incr. reflow where the 349 * content rows will arive later. 350 */ 351 int32_t GetRowSpan(int32_t aRowIndex, int32_t aColIndex, 352 bool aGetEffective) const; 353 354 int32_t GetEffectiveColSpan(const nsTableCellMap& aMap, int32_t aRowIndex, 355 int32_t aColIndex) const; 356 357 typedef nsTArray<CellData*> CellDataArray; 358 359 /** dump a representation of the cell map to stdout for debugging */ 360 #ifdef DEBUG 361 void Dump(bool aIsBorderCollapse) const; 362 #endif 363 364 protected: 365 friend class nsTableCellMap; 366 friend class BCMapCellIterator; 367 friend class BCPaintBorderIterator; 368 friend class nsTableFrame; 369 friend class nsCellMapColumnIterator; 370 371 /** 372 * Increase the number of rows in this cellmap by aNumRows. Put the 373 * new rows at aRowIndex. If aRowIndex is -1, put them at the end. 374 */ 375 bool Grow(nsTableCellMap& aMap, int32_t aNumRows, int32_t aRowIndex = -1); 376 377 void GrowRow(CellDataArray& aRow, int32_t aNumCols); 378 379 /** assign aCellData to the cell at (aRow,aColumn) */ 380 void SetDataAt(nsTableCellMap& aMap, CellData& aCellData, 381 int32_t aMapRowIndex, int32_t aColIndex); 382 383 CellData* GetDataAt(int32_t aMapRowIndex, int32_t aColIndex) const; 384 385 int32_t GetNumCellsIn(int32_t aColIndex) const; 386 387 void ExpandWithRows(nsTableCellMap& aMap, 388 nsTArray<nsTableRowFrame*>& aRowFrames, 389 int32_t aStartRowIndex, int32_t aRgFirstRowIndex, 390 TableArea& aDamageArea); 391 392 void ExpandWithCells(nsTableCellMap& aMap, 393 nsTArray<nsTableCellFrame*>& aCellFrames, 394 int32_t aRowIndex, int32_t aColIndex, int32_t aRowSpan, 395 bool aRowSpanIsZero, int32_t aRgFirstRowIndex, 396 TableArea& aDamageArea); 397 398 void ShrinkWithoutRows(nsTableCellMap& aMap, int32_t aFirstRowIndex, 399 int32_t aNumRowsToRemove, int32_t aRgFirstRowIndex, 400 TableArea& aDamageArea); 401 402 void ShrinkWithoutCell(nsTableCellMap& aMap, nsTableCellFrame& aCellFrame, 403 int32_t aRowIndex, int32_t aColIndex, 404 int32_t aRgFirstRowIndex, TableArea& aDamageArea); 405 406 /** 407 * Rebuild due to rows being inserted or deleted with cells spanning 408 * into or out of the rows. This function can only handle insertion 409 * or deletion but NOT both. So either aRowsToInsert must be null 410 * or aNumRowsToRemove must be 0. 411 * 412 * // XXXbz are both allowed to happen? That'd be a no-op... 413 */ 414 void RebuildConsideringRows(nsTableCellMap& aMap, int32_t aStartRowIndex, 415 nsTArray<nsTableRowFrame*>* aRowsToInsert, 416 int32_t aNumRowsToRemove); 417 418 void RebuildConsideringCells(nsTableCellMap& aMap, int32_t aNumOrigCols, 419 nsTArray<nsTableCellFrame*>* aCellFrames, 420 int32_t aRowIndex, int32_t aColIndex, 421 bool aInsert); 422 423 bool CellsSpanOut(nsTArray<nsTableRowFrame*>& aNewRows) const; 424 425 /** If a cell spans out of the area defined by aStartRowIndex, aEndRowIndex 426 * and aStartColIndex, aEndColIndex the cellmap changes are more severe so 427 * the corresponding routines needs to be called. This is also necessary if 428 * cells outside spans into this region. 429 * @aStartRowIndex - y start index 430 * @aEndRowIndex - y end index 431 * @param aStartColIndex - x start index 432 * @param aEndColIndex - x end index 433 * @return - true if a cell span crosses the border of the 434 region 435 */ 436 bool CellsSpanInOrOut(int32_t aStartRowIndex, int32_t aEndRowIndex, 437 int32_t aStartColIndex, int32_t aEndColIndex) const; 438 439 bool CreateEmptyRow(int32_t aRowIndex, int32_t aNumCols); 440 441 int32_t GetRowSpanForNewCell(nsTableCellFrame* aCellFrameToAdd, 442 int32_t aRowIndex, bool& aIsZeroRowSpan) const; 443 444 // Destroy a CellData struct. This will handle the case of aData 445 // actually being a BCCellData properly. 446 void DestroyCellData(CellData* aData); 447 // Allocate a CellData struct. This will handle needing to create a 448 // BCCellData properly. 449 // @param aOrigCell the originating cell to pass to the celldata constructor 450 CellData* AllocCellData(nsTableCellFrame* aOrigCell); 451 452 /** an array containing, for each row, the CellDatas for the cells 453 * in that row. It can be larger than mContentRowCount due to row spans 454 * extending beyond the table */ 455 // XXXbz once we have auto TArrays, we should probably use them here. 456 nsTArray<CellDataArray> mRows; 457 458 /** the number of rows in the table (content) which is not indentical to the 459 * number of rows in the cell map due to row spans extending beyond the end 460 * of thetable (dead rows) or empty tr tags 461 */ 462 int32_t mContentRowCount; 463 464 // the row group that corresponds to this map 465 nsTableRowGroupFrame* mRowGroupFrame; 466 467 // the next row group cell map 468 nsCellMap* mNextSibling; 469 470 // Whether this is a BC cellmap or not 471 bool mIsBC; 472 473 // Prescontext to deallocate and allocate celldata 474 RefPtr<nsPresContext> mPresContext; 475 }; 476 477 /** 478 * A class for iterating the cells in a given column. Must be given a 479 * non-null nsTableCellMap and a column number valid for that cellmap. 480 */ 481 class nsCellMapColumnIterator { 482 public: 483 nsCellMapColumnIterator(const nsTableCellMap* aMap, int32_t aCol) 484 : mMap(aMap), 485 mCurMap(aMap->mFirstMap), 486 mCurMapStart(0), 487 mCurMapRow(0), 488 mCol(aCol), 489 mFoundCells(0), 490 mCurMapContentRowCount(0), 491 mCurMapRelevantRowCount(0) { 492 MOZ_ASSERT(aMap, "Must have map"); 493 MOZ_ASSERT(mCol < aMap->GetColCount(), "Invalid column"); 494 mOrigCells = aMap->GetNumCellsOriginatingInCol(mCol); 495 if (mCurMap) { 496 mCurMapContentRowCount = mCurMap->GetRowCount(); 497 uint32_t rowArrayLength = mCurMap->mRows.Length(); 498 mCurMapRelevantRowCount = 499 std::min(mCurMapContentRowCount, rowArrayLength); 500 if (mCurMapRelevantRowCount == 0 && mOrigCells > 0) { 501 // This row group is useless; advance! 502 AdvanceRowGroup(); 503 } 504 } 505 #ifdef DEBUG 506 else { 507 NS_ASSERTION(mOrigCells == 0, "Why no rowgroups?"); 508 } 509 #endif 510 } 511 512 nsTableCellFrame* GetNextFrame(int32_t* aRow, int32_t* aColSpan); 513 514 private: 515 void AdvanceRowGroup(); 516 517 // Advance the row; aIncrement is considered to be a cell's rowspan, 518 // so if 0 is passed in we'll advance to the next rowgroup. 519 void IncrementRow(int32_t aIncrement); 520 521 const nsTableCellMap* mMap; 522 const nsCellMap* mCurMap; 523 524 // mCurMapStart is the row in the entire nsTableCellMap where 525 // mCurMap starts. This is used to compute row indices to pass to 526 // nsTableCellMap::GetDataAt, so must be a _content_ row index. 527 uint32_t mCurMapStart; 528 529 // In steady-state mCurMapRow is the row in our current nsCellMap 530 // that we'll use the next time GetNextFrame() is called. Due to 531 // the way we skip over rowspans, the entry in mCurMapRow and mCol 532 // is either null, dead, originating, or a colspan. In particular, 533 // it cannot be a rowspan or overlap entry. 534 uint32_t mCurMapRow; 535 const int32_t mCol; 536 uint32_t mOrigCells; 537 uint32_t mFoundCells; 538 539 // The number of content rows in mCurMap. This may be bigger than the number 540 // of "relevant" rows, or it might be smaller. 541 uint32_t mCurMapContentRowCount; 542 543 // The number of "relevant" rows in mCurMap. That is, the number of rows 544 // which might have an originating cell in them. Once mCurMapRow reaches 545 // mCurMapRelevantRowCount, we should move to the next map. 546 uint32_t mCurMapRelevantRowCount; 547 }; 548 549 /* ----- inline methods ----- */ 550 inline int32_t nsTableCellMap::GetColCount() const { return mCols.Length(); } 551 552 inline nsCellMap* nsCellMap::GetNextSibling() const { return mNextSibling; } 553 554 inline void nsCellMap::SetNextSibling(nsCellMap* aSibling) { 555 mNextSibling = aSibling; 556 } 557 558 inline nsTableRowGroupFrame* nsCellMap::GetRowGroup() const { 559 return mRowGroupFrame; 560 } 561 562 inline int32_t nsCellMap::GetRowCount(bool aConsiderDeadRowSpanRows) const { 563 int32_t rowCount = 564 (aConsiderDeadRowSpanRows) ? mRows.Length() : mContentRowCount; 565 return rowCount; 566 } 567 568 // nsColInfo 569 570 inline nsColInfo::nsColInfo() : mNumCellsOrig(0), mNumCellsSpan(0) {} 571 572 inline nsColInfo::nsColInfo(int32_t aNumCellsOrig, int32_t aNumCellsSpan) 573 : mNumCellsOrig(aNumCellsOrig), mNumCellsSpan(aNumCellsSpan) {} 574 575 #endif