1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 // MARKER(update_precomp.py): autogen include statement, do not remove 29 #include "precompiled_sw.hxx" 30 31 #include <swtable.hxx> 32 #include <tblsel.hxx> 33 #include <tblrwcl.hxx> 34 #include <node.hxx> 35 #include <UndoTable.hxx> 36 #include <pam.hxx> 37 #include <frmfmt.hxx> 38 #include <frmatr.hxx> 39 #include <cellfrm.hxx> 40 #include <fmtfsize.hxx> 41 #include <doc.hxx> 42 #include <IDocumentUndoRedo.hxx> 43 #include <vector> 44 #include <set> 45 #include <list> 46 #include <memory> 47 #include <editeng/boxitem.hxx> 48 #include <editeng/protitem.hxx> 49 #include <swtblfmt.hxx> 50 #include <switerator.hxx> 51 52 #ifndef DBG_UTIL 53 #define CHECK_TABLE(t) 54 #else 55 #ifdef DEBUG 56 #define CHECK_TABLE(t) (t).CheckConsistency(); 57 #else 58 #define CHECK_TABLE(t) 59 #endif 60 #endif 61 62 // --------------------------------------------------------------- 63 64 /** SwBoxSelection is a small helperclass (structure) to handle selections 65 of cells (boxes) between table functions 66 67 It contains an "array" of table boxes, a rectangulare selection of table boxes. 68 To be more specific, it contains a vector of box selections, 69 every box selection (SwSelBoxes) contains the selected boxes inside one row. 70 The member mnMergeWidth contains the width of the selected boxes 71 */ 72 73 class SwBoxSelection 74 { 75 public: 76 std::vector<const SwSelBoxes*> aBoxes; 77 long mnMergeWidth; 78 SwBoxSelection() : mnMergeWidth(0) {} 79 bool isEmpty() const { return aBoxes.size() == 0; } 80 void insertBoxes( const SwSelBoxes* pNew ){ aBoxes.insert( aBoxes.end(), pNew ); } 81 }; 82 83 /** NewMerge(..) removes the superfluous cells after cell merge 84 85 SwTable::NewMerge(..) does some cleaning up, 86 it simply deletes the superfluous cells ("cell span") 87 and notifies the Undo about it. 88 The main work has been done by SwTable::PrepareMerge(..) already. 89 90 @param rBoxes 91 the boxes to remove 92 93 @param pUndo 94 the undo object to notify, maybe empty 95 96 @return sal_True for compatibility reasons with OldMerge(..) 97 */ 98 99 sal_Bool SwTable::NewMerge( SwDoc* pDoc, const SwSelBoxes& rBoxes, 100 const SwSelBoxes& rMerged, SwTableBox*, SwUndoTblMerge* pUndo ) 101 { 102 if( pUndo ) 103 pUndo->SetSelBoxes( rBoxes ); 104 DeleteSel( pDoc, rBoxes, &rMerged, 0, sal_True, sal_True ); 105 106 CHECK_TABLE( *this ) 107 return sal_True; 108 } 109 110 /** lcl_CheckMinMax helps evaluating (horizontal) min/max of boxes 111 112 lcl_CheckMinMax(..) compares the left border and the right border 113 of a given cell with the given range and sets it accordingly. 114 115 @param rMin 116 will be decremented if necessary to the left border of the cell 117 118 @param rMax 119 will be incremented if necessary to the right border of the cell 120 121 @param rLine 122 the row (table line) of the interesting box 123 124 @param nCheck 125 the index of the box in the table box array of the given row 126 127 @param bSet 128 if bSet is false, rMin and rMax will be manipulated if necessary 129 if bSet is true, rMin and rMax will be set to the left and right border of the box 130 131 */ 132 133 void lcl_CheckMinMax( long& rMin, long& rMax, const SwTableLine& rLine, sal_uInt16 nCheck, bool bSet ) 134 { 135 ++nCheck; 136 if( rLine.GetTabBoxes().Count() < nCheck ) 137 { // robust 138 ASSERT( false, "Box out of table line" ); 139 nCheck = rLine.GetTabBoxes().Count(); 140 } 141 142 long nNew = 0; // will be the right border of the current box 143 long nWidth = 0; // the width of the current box 144 for( sal_uInt16 nCurrBox = 0; nCurrBox < nCheck; ++nCurrBox ) 145 { 146 SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox]; 147 ASSERT( pBox, "Missing table box" ); 148 nWidth = pBox->GetFrmFmt()->GetFrmSize().GetWidth(); 149 nNew += nWidth; 150 } 151 // nNew is the right border of the wished box 152 if( bSet || nNew > rMax ) 153 rMax = nNew; 154 nNew -= nWidth; // nNew becomes the left border of the wished box 155 if( bSet || nNew < rMin ) 156 rMin = nNew; 157 } 158 159 /** lcl_Box2LeftBorder(..) delivers the left (logical) border of a table box 160 161 The left logical border of a table box is the sum of the cell width before this 162 box. 163 164 @param rBox 165 is the requested table box 166 167 @return is the left logical border (long, even it cannot be negative) 168 169 */ 170 171 long lcl_Box2LeftBorder( const SwTableBox& rBox ) 172 { 173 if( !rBox.GetUpper() ) 174 return 0; 175 long nLeft = 0; 176 const SwTableLine &rLine = *rBox.GetUpper(); 177 sal_uInt16 nCount = rLine.GetTabBoxes().Count(); 178 for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox ) 179 { 180 SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox]; 181 ASSERT( pBox, "Missing table box" ); 182 if( pBox == &rBox ) 183 return nLeft; 184 nLeft += pBox->GetFrmFmt()->GetFrmSize().GetWidth(); 185 } 186 ASSERT( false, "Box not found in own upper?" ); 187 return nLeft; 188 } 189 190 /** lcl_LeftBorder2Box delivers the box to a given left border 191 192 It's used to find the master/follow table boxes in previous/next rows. 193 Don't call this function to check if there is such a box, 194 call it if you know there has to be such box. 195 196 @param nLeft 197 the left border (logical x-value) of the demanded box 198 199 @param rLine 200 the row (table line) to be scanned 201 202 @return a pointer to the table box inside the given row with the wished left border 203 204 */ 205 206 SwTableBox* lcl_LeftBorder2Box( long nLeft, const SwTableLine* pLine ) 207 { 208 if( !pLine ) 209 return 0; 210 long nCurrLeft = 0; 211 sal_uInt16 nCount = pLine->GetTabBoxes().Count(); 212 for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox ) 213 { 214 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox]; 215 ASSERT( pBox, "Missing table box" ); 216 if( nCurrLeft >= nLeft && pBox->GetFrmFmt()->GetFrmSize().GetWidth() ) 217 { 218 ASSERT( nCurrLeft == nLeft, "Wrong box found" ); 219 return pBox; 220 } 221 nCurrLeft += pBox->GetFrmFmt()->GetFrmSize().GetWidth(); 222 } 223 ASSERT( false, "Didn't found wished box" ); 224 return 0; 225 } 226 227 /** lcl_ChangeRowSpan corrects row span after insertion/deletion of rows 228 229 lcl_ChangeRowSpan(..) has to be called after an insertion or deletion of rows 230 to adjust the row spans of previous rows accordingly. 231 If rows are deleted, the previous rows with row spans into the deleted area 232 have to be decremented by the number of _overlapped_ inserted rows. 233 If rows are inserted, the previous rows with row span into the inserted area 234 have to be incremented by the number of inserted rows. 235 For those row spans which ends exactly above the inserted area it has to be 236 decided by the parameter bSingle if they have to be expanded or not. 237 238 @param rTable 239 the table to manipulate (has to be a new model table) 240 241 @param nDiff 242 the number of rows which has been inserted (nDiff > 0) or deleted (nDiff < 0) 243 244 @param nRowIdx 245 the index of the first row which has to be checked 246 247 @param bSingle 248 true if the new inserted row should not extend row spans which ends in the row above 249 this is for rows inserted by UI "insert row" 250 false if all cells of an inserted row has to be overlapped by the previous row 251 this is for rows inserted by "split row" 252 false is also needed for deleted rows 253 254 */ 255 256 void lcl_ChangeRowSpan( const SwTable& rTable, const long nDiff, 257 sal_uInt16 nRowIdx, const bool bSingle ) 258 { 259 if( !nDiff || nRowIdx >= rTable.GetTabLines().Count() ) 260 return; 261 ASSERT( !bSingle || nDiff > 0, "Don't set bSingle when deleting lines!" ); 262 bool bGoOn; 263 // nDistance is the distance between the current row and the critical row, 264 // e.g. the deleted rows or the inserted rows. 265 // If the row span is lower than the distance there is nothing to do 266 // because the row span ends before the critical area. 267 // When the inserted rows should not be overlapped by row spans which ends 268 // exactly in the row above, the trick is to start with a distance of 1. 269 long nDistance = bSingle ? 1 : 0; 270 do 271 { 272 bGoOn = false; // will be set to true if we found a non-master cell 273 // which has to be manipulated => we have to chekc the previous row, too. 274 const SwTableLine* pLine = rTable.GetTabLines()[ nRowIdx ]; 275 sal_uInt16 nBoxCount = pLine->GetTabBoxes().Count(); 276 for( sal_uInt16 nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox ) 277 { 278 long nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan(); 279 long nAbsSpan = nRowSpan > 0 ? nRowSpan : -nRowSpan; 280 // Check if the last overlapped cell is above or below 281 // the critical area 282 if( nAbsSpan > nDistance ) 283 { 284 if( nDiff > 0 ) 285 { 286 if( nRowSpan > 0 ) 287 nRowSpan += nDiff; // increment row span of master cell 288 else 289 { 290 nRowSpan -= nDiff; // increment row span of non-master cell 291 bGoOn = true; 292 } 293 } 294 else 295 { 296 if( nRowSpan > 0 ) 297 { // A master cell 298 // end of row span behind the deleted area .. 299 if( nRowSpan - nDistance > -nDiff ) 300 nRowSpan += nDiff; 301 else // .. or inside the deleted area 302 nRowSpan = nDistance + 1; 303 } 304 else 305 { // Same for a non-master cell 306 if( nRowSpan + nDistance < nDiff ) 307 nRowSpan -= nDiff; 308 else 309 nRowSpan = -nDistance - 1; 310 bGoOn = true; // We have to continue 311 } 312 } 313 pLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan ); 314 } 315 } 316 ++nDistance; 317 if( nRowIdx ) 318 --nRowIdx; 319 else 320 bGoOn = false; //robust 321 } while( bGoOn ); 322 } 323 324 /** CollectBoxSelection(..) create a rectangulare selection based on the given SwPaM 325 and prepares the selected cells for merging 326 */ 327 328 SwBoxSelection* SwTable::CollectBoxSelection( const SwPaM& rPam ) const 329 { 330 ASSERT( bNewModel, "Don't call me for old tables" ); 331 if( !aLines.Count() ) 332 return 0; 333 const SwNode* pStartNd = rPam.Start()->nNode.GetNode().FindTableBoxStartNode(); 334 const SwNode* pEndNd = rPam.End()->nNode.GetNode().FindTableBoxStartNode(); 335 if( !pStartNd || !pEndNd || pStartNd == pEndNd ) 336 return 0; 337 338 sal_uInt16 nLines = aLines.Count(); 339 sal_uInt16 nTop = 0, nBottom = 0; 340 long nMin = 0, nMax = 0; 341 int nFound = 0; 342 for( sal_uInt16 nRow = 0; nFound < 2 && nRow < nLines; ++nRow ) 343 { 344 SwTableLine* pLine = aLines[nRow]; 345 ASSERT( pLine, "Missing table line" ); 346 sal_uInt16 nCols = pLine->GetTabBoxes().Count(); 347 for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol ) 348 { 349 SwTableBox* pBox = pLine->GetTabBoxes()[nCol]; 350 ASSERT( pBox, "Missing table box" ); 351 if( nFound ) 352 { 353 if( pBox->GetSttNd() == pEndNd ) 354 { 355 nBottom = nRow; 356 lcl_CheckMinMax( nMin, nMax, *pLine, nCol, false ); 357 ++nFound; 358 break; 359 } 360 } 361 else if( pBox->GetSttNd() == pStartNd ) 362 { 363 nTop = nRow; 364 lcl_CheckMinMax( nMin, nMax, *pLine, nCol, true ); 365 ++nFound; 366 } 367 } 368 } 369 if( nFound < 2 ) 370 return 0; 371 372 bool bOkay = true; 373 long nMid = ( nMin + nMax ) / 2; 374 375 SwBoxSelection* pRet = new SwBoxSelection(); 376 std::list< std::pair< SwTableBox*, long > > aNewWidthList; 377 sal_uInt16 nCheckBottom = nBottom; 378 long nLeftSpan = 0; 379 long nRightSpan = 0; 380 long nLeftSpanCnt = 0; 381 long nRightSpanCnt = 0; 382 for( sal_uInt16 nRow = nTop; nRow <= nBottom && bOkay; ++nRow ) 383 { 384 SwTableLine* pLine = aLines[nRow]; 385 ASSERT( pLine, "Missing table line" ); 386 SwSelBoxes *pBoxes = new SwSelBoxes(); 387 long nLeft = 0; 388 long nRight = 0; 389 long nRowSpan = 1; 390 sal_uInt16 nCount = pLine->GetTabBoxes().Count(); 391 for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox ) 392 { 393 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox]; 394 ASSERT( pBox, "Missing table box" ); 395 nLeft = nRight; 396 nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth(); 397 nRowSpan = pBox->getRowSpan(); 398 if( nRight <= nMin ) 399 { 400 if( nRight == nMin && nLeftSpanCnt ) 401 bOkay = false; 402 continue; 403 } 404 SwTableBox* pInnerBox = 0; 405 SwTableBox* pLeftBox = 0; 406 SwTableBox* pRightBox = 0; 407 long nDiff = 0; 408 long nDiff2 = 0; 409 if( nLeft < nMin ) 410 { 411 if( nRight >= nMid || nRight + nLeft >= nMin + nMin ) 412 { 413 if( nCurrBox ) 414 { 415 pBoxes->Insert( pBox ); 416 pInnerBox = pBox; 417 pLeftBox = pLine->GetTabBoxes()[nCurrBox-1]; 418 nDiff = nMin - nLeft; 419 if( nRight > nMax ) 420 { 421 if( nCurrBox+1 < nCount ) 422 { 423 pRightBox = pLine->GetTabBoxes()[nCurrBox+1]; 424 nDiff2 = nRight - nMax; 425 } 426 else 427 bOkay = false; 428 } 429 else if( nRightSpanCnt && nRight == nMax ) 430 bOkay = false; 431 } 432 else 433 bOkay = false; 434 } 435 else if( nCurrBox+1 < nCount ) 436 { 437 pLeftBox = pBox; 438 pInnerBox = pLine->GetTabBoxes()[nCurrBox+1]; 439 nDiff = nMin - nRight; 440 } 441 else 442 bOkay = false; 443 } 444 else if( nRight <= nMax ) 445 { 446 pBoxes->Insert( pBox ); 447 if( nRow == nTop && nRowSpan < 0 ) 448 { 449 bOkay = false; 450 break; 451 } 452 if( nRowSpan > 1 && nRow + nRowSpan - 1 > nBottom ) 453 nBottom = nRow + (sal_uInt16)nRowSpan - 1; 454 if( nRowSpan < -1 && nRow - nRowSpan - 1 > nBottom ) 455 nBottom = (sal_uInt16)(nRow - nRowSpan - 1); 456 if( nRightSpanCnt && nRight == nMax ) 457 bOkay = false; 458 } 459 else if( nLeft < nMax ) 460 { 461 if( nLeft <= nMid || nRight + nLeft <= nMax ) 462 { 463 if( nCurrBox+1 < nCount ) 464 { 465 pBoxes->Insert( pBox ); 466 pInnerBox = pBox; 467 pRightBox = pLine->GetTabBoxes()[nCurrBox+1]; 468 nDiff = nRight - nMax; 469 } 470 else 471 bOkay = false; 472 } 473 else if( nCurrBox ) 474 { 475 pRightBox = pBox; 476 pInnerBox = pLine->GetTabBoxes()[nCurrBox-1]; 477 nDiff = nLeft - nMax; 478 } 479 else 480 bOkay = false; 481 } 482 else 483 break; 484 if( pInnerBox ) 485 { 486 if( nRow == nBottom ) 487 { 488 long nTmpSpan = pInnerBox->getRowSpan(); 489 if( nTmpSpan > 1 ) 490 nBottom += (sal_uInt16)nTmpSpan - 1; 491 else if( nTmpSpan < -1 ) 492 nBottom = (sal_uInt16)( nBottom - nTmpSpan - 1 ); 493 } 494 SwTableBox* pOuterBox = pLeftBox; 495 do 496 { 497 if( pOuterBox ) 498 { 499 long nOutSpan = pOuterBox->getRowSpan(); 500 if( nOutSpan != 1 ) 501 { 502 sal_uInt16 nCheck = nRow; 503 if( nOutSpan < 0 ) 504 { 505 const SwTableBox& rBox = 506 pOuterBox->FindStartOfRowSpan( *this, USHRT_MAX ); 507 nOutSpan = rBox.getRowSpan(); 508 const SwTableLine* pTmpL = rBox.GetUpper(); 509 nCheck = GetTabLines().C40_GETPOS( SwTableLine, pTmpL ); 510 if( nCheck < nTop ) 511 bOkay = false; 512 if( pOuterBox == pLeftBox ) 513 { 514 if( !nLeftSpanCnt || nMin - nDiff != nLeftSpan ) 515 bOkay = false; 516 } 517 else 518 { 519 if( !nRightSpanCnt || nMax + nDiff != nRightSpan ) 520 bOkay = false; 521 } 522 } 523 else 524 { 525 if( pOuterBox == pLeftBox ) 526 { 527 if( nLeftSpanCnt ) 528 bOkay = false; 529 nLeftSpan = nMin - nDiff; 530 nLeftSpanCnt = nOutSpan; 531 } 532 else 533 { 534 if( nRightSpanCnt ) 535 bOkay = false; 536 nRightSpan = nMax + nDiff; 537 nRightSpanCnt = nOutSpan; 538 } 539 } 540 nCheck += (sal_uInt16)nOutSpan - 1; 541 if( nCheck > nCheckBottom ) 542 nCheckBottom = nCheck; 543 } 544 else if( ( nLeftSpanCnt && pLeftBox == pOuterBox ) || 545 ( nRightSpanCnt && pRightBox == pOuterBox ) ) 546 bOkay = false; 547 std::pair< SwTableBox*, long > aTmp; 548 aTmp.first = pInnerBox; 549 aTmp.second = -nDiff; 550 aNewWidthList.push_back( aTmp ); 551 aTmp.first = pOuterBox; 552 aTmp.second = nDiff; 553 aNewWidthList.push_back( aTmp ); 554 } 555 pOuterBox = pOuterBox == pRightBox ? 0 : pRightBox; 556 if( nDiff2 ) 557 nDiff = nDiff2; 558 } while( pOuterBox ); 559 } 560 } 561 if( nLeftSpanCnt ) 562 --nLeftSpanCnt; 563 if( nRightSpanCnt ) 564 --nRightSpanCnt; 565 pRet->insertBoxes( pBoxes ); 566 } 567 pRet->mnMergeWidth = nMax - nMin; 568 if( nCheckBottom > nBottom ) 569 bOkay = false; 570 if( bOkay ) 571 { 572 std::list< std::pair< SwTableBox*, long > >::iterator 573 pCurr = aNewWidthList.begin(); 574 while( pCurr != aNewWidthList.end() ) 575 { 576 SwFrmFmt* pFmt = pCurr->first->ClaimFrmFmt(); 577 long nNewWidth = pFmt->GetFrmSize().GetWidth() + pCurr->second; 578 pFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, nNewWidth, 0 ) ); 579 ++pCurr; 580 } 581 } 582 else 583 { 584 delete pRet; 585 pRet = 0; 586 } 587 return pRet; 588 } 589 590 /** lcl_InvalidateCellFrm(..) invalidates all layout representations of a given cell 591 to initiate a reformatting 592 */ 593 594 void lcl_InvalidateCellFrm( const SwTableBox& rBox ) 595 { 596 SwIterator<SwCellFrm,SwFmt> aIter( *rBox.GetFrmFmt() ); 597 for( SwCellFrm* pCell = aIter.First(); pCell; pCell = aIter.Next() ) 598 { 599 if( pCell->GetTabBox() == &rBox ) 600 { 601 pCell->InvalidateSize(); 602 SwFrm* pLower = pCell->GetLower(); 603 if( pLower ) 604 pLower->_InvalidateSize(); 605 } 606 } 607 } 608 609 /** lcl_InsertPosition(..) evaluates the insert positions in every table line, 610 when a selection of cells is given and returns the average cell widths 611 */ 612 613 long lcl_InsertPosition( SwTable &rTable, std::vector<sal_uInt16>& rInsPos, 614 const SwSelBoxes& rBoxes, sal_Bool bBehind ) 615 { 616 sal_Int32 nAddWidth = 0; 617 long nCount = 0; 618 for( sal_uInt16 j = 0; j < rBoxes.Count(); ++j ) 619 { 620 SwTableBox *pBox = rBoxes[j]; 621 SwTableLine* pLine = pBox->GetUpper(); 622 long nWidth = rBoxes[j]->GetFrmFmt()->GetFrmSize().GetWidth(); 623 nAddWidth += nWidth; 624 sal_uInt16 nCurrBox = pLine->GetTabBoxes().C40_GETPOS(SwTableBox, pBox ); 625 sal_uInt16 nCurrLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pLine ); 626 ASSERT( nCurrLine != USHRT_MAX, "Time to say Good-Bye.." ); 627 if( rInsPos[ nCurrLine ] == USHRT_MAX ) 628 { 629 rInsPos[ nCurrLine ] = nCurrBox; 630 ++nCount; 631 } 632 else if( ( rInsPos[ nCurrLine ] > nCurrBox ) == !bBehind ) 633 rInsPos[ nCurrLine ] = nCurrBox; 634 } 635 if( nCount ) 636 nAddWidth /= nCount; 637 return nAddWidth; 638 } 639 640 /** SwTable::NewInsertCol(..) insert new column(s) into a table 641 642 643 @param pDoc 644 the document 645 646 @param rBoxes 647 the selected boxes 648 649 @param nCnt 650 the number of columns to insert 651 652 @param bBehind 653 insertion behind (true) or before (false) the selected boxes 654 655 @return true, if any insertion has been done successfully 656 657 */ 658 659 sal_Bool SwTable::NewInsertCol( SwDoc* pDoc, const SwSelBoxes& rBoxes, 660 sal_uInt16 nCnt, sal_Bool bBehind ) 661 { 662 if( !aLines.Count() || !nCnt ) 663 return sal_False; 664 665 CHECK_TABLE( *this ) 666 long nNewBoxWidth = 0; 667 std::vector< sal_uInt16 > aInsPos( aLines.Count(), USHRT_MAX ); 668 { // Calculation of the insert positions and the width of the new boxes 669 sal_uInt64 nTableWidth = 0; 670 for( sal_uInt16 i = 0; i < aLines[0]->GetTabBoxes().Count(); ++i ) 671 nTableWidth += aLines[0]->GetTabBoxes()[i]->GetFrmFmt()->GetFrmSize().GetWidth(); 672 673 // Fill the vector of insert positions and the (average) width to insert 674 sal_uInt64 nAddWidth = lcl_InsertPosition( *this, aInsPos, rBoxes, bBehind ); 675 676 // Given is the (average) width of the selected boxes, if we would 677 // insert nCnt of columns the table would grow 678 // So we will shrink the table first, then insert the new boxes and 679 // get a table with the same width than before. 680 // But we will not shrink the table by the full already calculated value, 681 // we will reduce this value proportional to the old table width 682 nAddWidth *= nCnt; // we have to insert nCnt boxes per line 683 sal_uInt64 nResultingWidth = nAddWidth + nTableWidth; 684 if( !nResultingWidth ) 685 return sal_False; 686 nAddWidth = (nAddWidth * nTableWidth) / nResultingWidth; 687 nNewBoxWidth = long( nAddWidth / nCnt ); // Rounding 688 nAddWidth = nNewBoxWidth * nCnt; // Rounding 689 if( !nAddWidth || nAddWidth >= nTableWidth ) 690 return sal_False; 691 AdjustWidths( static_cast< long >(nTableWidth), static_cast< long >(nTableWidth - nAddWidth) ); 692 } 693 694 _FndBox aFndBox( 0, 0 ); 695 aFndBox.SetTableLines( rBoxes, *this ); 696 aFndBox.DelFrms( *this ); 697 // aFndBox.SaveChartData( *this ); 698 699 SwTableNode* pTblNd = GetTableNode(); 700 std::vector<SwTableBoxFmt*> aInsFormat( nCnt, 0 ); 701 sal_uInt16 nLastLine = USHRT_MAX; 702 long nLastRowSpan = 1; 703 704 for( sal_uInt16 i = 0; i < aLines.Count(); ++i ) 705 { 706 SwTableLine* pLine = aLines[ i ]; 707 sal_uInt16 nInsPos = aInsPos[i]; 708 ASSERT( nInsPos != USHRT_MAX, "Didn't found insert position" ); 709 SwTableBox* pBox = pLine->GetTabBoxes()[ nInsPos ]; 710 if( bBehind ) 711 ++nInsPos; 712 SwTableBoxFmt* pBoxFrmFmt = (SwTableBoxFmt*)pBox->GetFrmFmt(); 713 ::_InsTblBox( pDoc, pTblNd, pLine, pBoxFrmFmt, pBox, nInsPos, nCnt ); 714 long nRowSpan = pBox->getRowSpan(); 715 long nDiff = i - nLastLine; 716 bool bNewSpan = false; 717 if( nLastLine != USHRT_MAX && nDiff <= nLastRowSpan && 718 nRowSpan != nDiff - nLastRowSpan ) 719 { 720 bNewSpan = true; 721 while( nLastLine < i ) 722 { 723 SwTableLine* pTmpLine = aLines[ nLastLine ]; 724 sal_uInt16 nTmpPos = aInsPos[nLastLine]; 725 if( bBehind ) 726 ++nTmpPos; 727 for( sal_uInt16 j = 0; j < nCnt; ++j ) 728 pTmpLine->GetTabBoxes()[nTmpPos+j]->setRowSpan( nDiff ); 729 if( nDiff > 0 ) 730 nDiff = -nDiff; 731 ++nDiff; 732 ++nLastLine; 733 } 734 } 735 if( nRowSpan > 0 ) 736 bNewSpan = true; 737 if( bNewSpan ) 738 { 739 nLastLine = i; 740 if( nRowSpan < 0 ) 741 nLastRowSpan = -nRowSpan; 742 else 743 nLastRowSpan = nRowSpan; 744 } 745 const SvxBoxItem& aSelBoxItem = pBoxFrmFmt->GetBox(); 746 SvxBoxItem* pNoRightBorder = 0; 747 if( aSelBoxItem.GetRight() ) 748 { 749 pNoRightBorder = new SvxBoxItem( aSelBoxItem ); 750 pNoRightBorder->SetLine( 0, BOX_LINE_RIGHT ); 751 } 752 for( sal_uInt16 j = 0; j < nCnt; ++j ) 753 { 754 SwTableBox *pCurrBox = pLine->GetTabBoxes()[nInsPos+j]; 755 if( bNewSpan ) 756 { 757 pCurrBox->setRowSpan( nLastRowSpan ); 758 SwFrmFmt* pFrmFmt = pCurrBox->ClaimFrmFmt(); 759 SwFmtFrmSize aFrmSz( pFrmFmt->GetFrmSize() ); 760 aFrmSz.SetWidth( nNewBoxWidth ); 761 pFrmFmt->SetFmtAttr( aFrmSz ); 762 if( pNoRightBorder && ( !bBehind || j+1 < nCnt ) ) 763 pFrmFmt->SetFmtAttr( *pNoRightBorder ); 764 aInsFormat[j] = (SwTableBoxFmt*)pFrmFmt; 765 } 766 else 767 pCurrBox->ChgFrmFmt( aInsFormat[j] ); 768 } 769 if( bBehind && pNoRightBorder ) 770 { 771 SwFrmFmt* pFrmFmt = pBox->ClaimFrmFmt(); 772 pFrmFmt->SetFmtAttr( *pNoRightBorder ); 773 } 774 delete pNoRightBorder; 775 } 776 777 aFndBox.MakeFrms( *this ); 778 // aFndBox.RestoreChartData( *this ); 779 #ifdef DBG_UTIL 780 { 781 const SwTableBoxes &rTabBoxes = aLines[0]->GetTabBoxes(); 782 long nNewWidth = 0; 783 for( sal_uInt16 i = 0; i < rTabBoxes.Count(); ++i ) 784 nNewWidth += rTabBoxes[i]->GetFrmFmt()->GetFrmSize().GetWidth(); 785 ASSERT( nNewWidth > 0, "Very small" ); 786 } 787 #endif 788 CHECK_TABLE( *this ) 789 790 return sal_True; 791 } 792 793 /** SwTable::PrepareMerge(..) some preparation for the coming Merge(..) 794 795 For the old table model, ::GetMergeSel(..) is called only, 796 for the new table model, PrepareMerge does the main work. 797 It modifices all cells to merge (width, border, rowspan etc.) and collects 798 the cells which have to be deleted by Merge(..) afterwards. 799 If there are superfluous rows, these cells are put into the deletion list as well. 800 801 @param rPam 802 the selection to merge 803 804 @param rBoxes 805 should be empty at the beginning, at the end it is filled with boxes to delete. 806 807 @param ppMergeBox 808 will be set to the master cell box 809 810 @param pUndo 811 the undo object to record all changes 812 can be Null, e.g. when called by Redo(..) 813 814 @return 815 816 */ 817 818 bool SwTable::PrepareMerge( const SwPaM& rPam, SwSelBoxes& rBoxes, 819 SwSelBoxes& rMerged, SwTableBox** ppMergeBox, SwUndoTblMerge* pUndo ) 820 { 821 if( !bNewModel ) 822 { 823 ::GetMergeSel( rPam, rBoxes, ppMergeBox, pUndo ); 824 return rBoxes.Count() > 1; 825 } 826 CHECK_TABLE( *this ) 827 // We have to assert a "rectangular" box selection before we start to merge 828 std::auto_ptr< SwBoxSelection > pSel( CollectBoxSelection( rPam ) ); 829 if( !pSel.get() || pSel->isEmpty() ) 830 return false; 831 // Now we should have a rectangle of boxes, 832 // i.e. contiguous cells in contiguous rows 833 bool bMerge = false; // will be set if any content is transferred from 834 // a "not already overlapped" cell into the new master cell. 835 SwTableBox *pMergeBox = (*pSel->aBoxes[0])[0]; // the master cell box 836 if( !pMergeBox ) 837 return false; 838 (*ppMergeBox) = pMergeBox; 839 // The new master box will get the left and the top border of the top-left 840 // box of the selection and because the new master cell _is_ the top-left 841 // box, the left and right border does not need to be changed. 842 // The right and bottom border instead has to be derived from the right- 843 // bottom box of the selection. If this is a overlapped cell, 844 // the appropiate master box. 845 SwTableBox* pLastBox = 0; // the right-bottom (master) cell 846 SwDoc* pDoc = GetFrmFmt()->GetDoc(); 847 SwPosition aInsPos( *pMergeBox->GetSttNd()->EndOfSectionNode() ); 848 SwPaM aChkPam( aInsPos ); 849 // The number of lines in the selection rectangle: nLineCount 850 const sal_uInt16 nLineCount = sal_uInt16(pSel->aBoxes.size()); 851 // BTW: nLineCount is the rowspan of the new master cell 852 long nRowSpan = nLineCount; 853 // We will need the first and last line of the selection 854 // to check if there any superfluous row after merging 855 SwTableLine* pFirstLn = 0; 856 SwTableLine* pLastLn = 0; 857 // Iteration over the lines of the selection... 858 for( sal_uInt16 nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine ) 859 { 860 // The selected boxes in the current line 861 const SwSelBoxes* pBoxes = pSel->aBoxes[ nCurrLine ]; 862 sal_uInt16 nColCount = pBoxes->Count(); 863 // Iteration over the selected cell in the current row 864 for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol ) 865 { 866 SwTableBox* pBox = (*pBoxes)[nCurrCol]; 867 rMerged.Insert( pBox ); 868 // Only the first selected cell in every row will be alive, 869 // the other will be deleted => put into rBoxes 870 if( nCurrCol ) 871 rBoxes.Insert( pBox ); 872 else 873 { 874 if( nCurrLine == 1 ) 875 pFirstLn = pBox->GetUpper(); // we need this line later on 876 if( nCurrLine + 1 == nLineCount ) 877 pLastLn = pBox->GetUpper(); // and this one, too. 878 } 879 // A box has to be merged if it's not the master box itself, 880 // but an already overlapped cell must not be merged as well. 881 bool bDoMerge = pBox != pMergeBox && pBox->getRowSpan() > 0; 882 // The last box has to be in the last "column" of the selection 883 // and it has to be a master cell 884 if( nCurrCol+1 == nColCount && pBox->getRowSpan() > 0 ) 885 pLastBox = pBox; 886 if( bDoMerge ) 887 { 888 bMerge = true; 889 // If the cell to merge contains only one empty paragraph, 890 // we do not transfer this paragraph. 891 if( !IsEmptyBox( *pBox, aChkPam ) ) 892 { 893 SwNodeIndex& rInsPosNd = aInsPos.nNode; 894 SwPaM aPam( aInsPos ); 895 aPam.GetPoint()->nNode.Assign( *pBox->GetSttNd()->EndOfSectionNode(), -1 ); 896 SwCntntNode* pCNd = aPam.GetCntntNode(); 897 sal_uInt16 nL = pCNd ? pCNd->Len() : 0; 898 aPam.GetPoint()->nContent.Assign( pCNd, nL ); 899 SwNodeIndex aSttNdIdx( *pBox->GetSttNd(), 1 ); 900 bool const bUndo = pDoc->GetIDocumentUndoRedo().DoesUndo(); 901 if( pUndo ) 902 { 903 pDoc->GetIDocumentUndoRedo().DoUndo(false); 904 } 905 pDoc->AppendTxtNode( *aPam.GetPoint() ); 906 if( pUndo ) 907 { 908 pDoc->GetIDocumentUndoRedo().DoUndo(bUndo); 909 } 910 SwNodeRange aRg( aSttNdIdx, aPam.GetPoint()->nNode ); 911 if( pUndo ) 912 pUndo->MoveBoxCntnt( pDoc, aRg, rInsPosNd ); 913 else 914 { 915 pDoc->MoveNodeRange( aRg, rInsPosNd, 916 IDocumentContentOperations::DOC_NO_DELFRMS ); 917 } 918 } 919 } 920 // Only the cell of the first selected column will stay alive 921 // and got a new row span 922 if( !nCurrCol ) 923 pBox->setRowSpan( nRowSpan ); 924 } 925 if( nRowSpan > 0 ) // the master cell is done, from now on we set 926 nRowSpan = -nRowSpan; // negative row spans 927 ++nRowSpan; // ... -3, -2, -1 928 } 929 if( bMerge ) 930 { 931 // A row containing overlapped cells is superfluous, 932 // these cells can be put into rBoxes for deletion 933 _FindSuperfluousRows( rBoxes, pFirstLn, pLastLn ); 934 // pNewFmt will be set to the new master box and the overlapped cells 935 SwFrmFmt* pNewFmt = pMergeBox->ClaimFrmFmt(); 936 pNewFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, pSel->mnMergeWidth, 0 ) ); 937 for( sal_uInt16 nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine ) 938 { 939 const SwSelBoxes* pBoxes = pSel->aBoxes[ nCurrLine ]; 940 sal_uInt16 nColCount = pBoxes->Count(); 941 for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol ) 942 { 943 SwTableBox* pBox = (*pBoxes)[nCurrCol]; 944 if( nCurrCol ) 945 { 946 // Even this box will be deleted soon, 947 // we have to correct the width to avoid side effects 948 SwFrmFmt* pFmt = pBox->ClaimFrmFmt(); 949 pFmt->SetFmtAttr( SwFmtFrmSize( ATT_VAR_SIZE, 0, 0 ) ); 950 } 951 else 952 pBox->ChgFrmFmt( (SwTableBoxFmt*)pNewFmt ); 953 } 954 } 955 if( pLastBox ) // Robust 956 { 957 // The new borders of the master cell... 958 SvxBoxItem aBox( pMergeBox->GetFrmFmt()->GetBox() ); 959 bool bOld = aBox.GetRight() || aBox.GetBottom(); 960 const SvxBoxItem& rBox = pLastBox->GetFrmFmt()->GetBox(); 961 aBox.SetLine( rBox.GetRight(), BOX_LINE_RIGHT ); 962 aBox.SetLine( rBox.GetBottom(), BOX_LINE_BOTTOM ); 963 if( bOld || aBox.GetLeft() || aBox.GetTop() || aBox.GetRight() || aBox.GetBottom() ) 964 (*ppMergeBox)->GetFrmFmt()->SetFmtAttr( aBox ); 965 } 966 967 if( pUndo ) 968 pUndo->AddNewBox( pMergeBox->GetSttIdx() ); 969 } 970 return bMerge; 971 } 972 973 /** SwTable::_FindSuperfluousRows(..) is looking for superfluous rows, i.e. rows 974 containing overlapped cells only. 975 */ 976 977 void SwTable::_FindSuperfluousRows( SwSelBoxes& rBoxes, 978 SwTableLine* pFirstLn, SwTableLine* pLastLn ) 979 { 980 if( !pFirstLn || !pLastLn ) 981 { 982 if( !rBoxes.Count() ) 983 return; 984 pFirstLn = rBoxes[0]->GetUpper(); 985 pLastLn = rBoxes[ rBoxes.Count() - 1 ]->GetUpper(); 986 } 987 sal_uInt16 nFirstLn = GetTabLines().C40_GETPOS(SwTableLine, pFirstLn ); 988 sal_uInt16 nLastLn = GetTabLines().C40_GETPOS(SwTableLine, pLastLn ); 989 for( sal_uInt16 nRow = nFirstLn; nRow <= nLastLn; ++nRow ) 990 { 991 SwTableLine* pLine = aLines[nRow]; 992 ASSERT( pLine, "Missing table line" ); 993 sal_uInt16 nCols = pLine->GetTabBoxes().Count(); 994 bool bSuperfl = true; 995 for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol ) 996 { 997 SwTableBox *pBox = pLine->GetTabBoxes()[nCol]; 998 if( pBox->getRowSpan() > 0 && 999 USHRT_MAX == rBoxes.GetPos( pBox ) ) 1000 { 1001 bSuperfl = false; 1002 break; 1003 } 1004 } 1005 if( bSuperfl ) 1006 { 1007 for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol ) 1008 { 1009 SwTableBox* pBox = pLine->GetTabBoxes()[nCol]; 1010 rBoxes.Insert( pBox ); 1011 } 1012 } 1013 } 1014 } 1015 1016 /** SwTableBox::FindStartOfRowSpan(..) retruns the "master" cell, the cell which 1017 overlaps the given cell, it maybe the cell itself. 1018 */ 1019 1020 SwTableBox& SwTableBox::FindStartOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep ) 1021 { 1022 if( getRowSpan() > 0 || !nMaxStep ) 1023 return *this; 1024 1025 long nLeftBorder = lcl_Box2LeftBorder( *this ); 1026 SwTableBox* pBox = this; 1027 const SwTableLine* pMyUpper = GetUpper(); 1028 sal_uInt16 nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper ); 1029 if( nLine && nLine < rTable.GetTabLines().Count() ) 1030 { 1031 SwTableBox* pNext; 1032 do 1033 { 1034 pNext = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[--nLine] ); 1035 if( pNext ) 1036 pBox = pNext; 1037 } while( nLine && --nMaxStep && pNext && pBox->getRowSpan() < 1 ); 1038 } 1039 1040 return *pBox; 1041 } 1042 1043 /** SwTableBox::FindEndOfRowSpan(..) returns the last overlapped cell if there is 1044 any. Otherwise the cell itself will returned. 1045 */ 1046 1047 SwTableBox& SwTableBox::FindEndOfRowSpan( const SwTable& rTable, sal_uInt16 nMaxStep ) 1048 { 1049 long nAbsSpan = getRowSpan(); 1050 if( nAbsSpan < 0 ) 1051 nAbsSpan = -nAbsSpan; 1052 if( nAbsSpan == 1 || !nMaxStep ) 1053 return *this; 1054 1055 if( nMaxStep > --nAbsSpan ) 1056 nMaxStep = (sal_uInt16)nAbsSpan; 1057 const SwTableLine* pMyUpper = GetUpper(); 1058 sal_uInt16 nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper ); 1059 nMaxStep = nLine + nMaxStep; 1060 if( nMaxStep >= rTable.GetTabLines().Count() ) 1061 nMaxStep = rTable.GetTabLines().Count() - 1; 1062 long nLeftBorder = lcl_Box2LeftBorder( *this ); 1063 SwTableBox* pBox = 1064 lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[ nMaxStep ] ); 1065 if ( !pBox ) 1066 pBox = this; 1067 1068 return *pBox; 1069 } 1070 1071 /** lcl_getAllMergedBoxes(..) collects all overlapped boxes to a given (master) box 1072 */ 1073 1074 void lcl_getAllMergedBoxes( const SwTable& rTable, SwSelBoxes& rBoxes, SwTableBox& rBox ) 1075 { 1076 SwTableBox* pBox = &rBox; 1077 ASSERT( pBox == &rBox.FindStartOfRowSpan( rTable, USHRT_MAX ), "Not a master box" ); 1078 rBoxes.Insert( pBox ); 1079 if( pBox->getRowSpan() == 1 ) 1080 return; 1081 const SwTableLine* pMyUpper = pBox->GetUpper(); 1082 sal_uInt16 nLine = rTable.GetTabLines().C40_GETPOS(SwTableLine, pMyUpper ); 1083 long nLeftBorder = lcl_Box2LeftBorder( *pBox ); 1084 sal_uInt16 nCount = rTable.GetTabLines().Count(); 1085 while( ++nLine < nCount && pBox && pBox->getRowSpan() != -1 ) 1086 { 1087 pBox = lcl_LeftBorder2Box( nLeftBorder, rTable.GetTabLines()[nLine] ); 1088 if( pBox ) 1089 rBoxes.Insert( pBox ); 1090 }; 1091 } 1092 1093 /** lcl_UnMerge(..) manipulates the row span attribute of a given master cell 1094 and its overlapped cells to split them into several pieces. 1095 */ 1096 1097 void lcl_UnMerge( const SwTable& rTable, SwTableBox& rBox, sal_uInt16 nCnt, 1098 sal_Bool bSameHeight ) 1099 { 1100 SwSelBoxes aBoxes; 1101 lcl_getAllMergedBoxes( rTable, aBoxes, rBox ); 1102 sal_uInt16 nCount = aBoxes.Count(); 1103 if( nCount < 2 ) 1104 return; 1105 if( nCnt > nCount ) 1106 nCnt = nCount; 1107 sal_uInt16 *pSplitIdx = new sal_uInt16[ nCnt ]; 1108 if( bSameHeight ) 1109 { 1110 SwTwips *pHeights = new SwTwips[ nCount ]; 1111 SwTwips nHeight = 0; 1112 for( sal_uInt16 i = 0; i < nCount; ++i ) 1113 { 1114 SwTableLine* pLine = aBoxes[ i ]->GetUpper(); 1115 SwFrmFmt *pRowFmt = pLine->GetFrmFmt(); 1116 pHeights[ i ] = pRowFmt->GetFrmSize().GetHeight(); 1117 nHeight += pHeights[ i ]; 1118 } 1119 SwTwips nSumH = 0; 1120 sal_uInt16 nIdx = 0; 1121 for( sal_uInt16 i = 1; i <= nCnt; ++i ) 1122 { 1123 SwTwips nSplit = ( i * nHeight ) / nCnt; 1124 while( nSumH < nSplit && nIdx < nCount ) 1125 nSumH += pHeights[ nIdx++ ]; 1126 pSplitIdx[ i - 1 ] = nIdx; 1127 } 1128 delete[] pHeights; 1129 } 1130 else 1131 { 1132 for( long i = 1; i <= nCnt; ++i ) 1133 pSplitIdx[ i - 1 ] = (sal_uInt16)( ( i * nCount ) / nCnt ); 1134 } 1135 sal_uInt16 nIdx = 0; 1136 for( long i = 0; i < nCnt; ++i ) 1137 { 1138 sal_uInt16 nNextIdx = pSplitIdx[ i ]; 1139 aBoxes[ nIdx ]->setRowSpan( nNextIdx - nIdx ); 1140 lcl_InvalidateCellFrm( *aBoxes[ nIdx ] ); 1141 while( ++nIdx < nNextIdx ) 1142 aBoxes[ nIdx ]->setRowSpan( nIdx - nNextIdx ); 1143 } 1144 delete[] pSplitIdx; 1145 } 1146 1147 /** lcl_FillSelBoxes(..) puts all boxes of a given line into the selection structure 1148 */ 1149 1150 void lcl_FillSelBoxes( SwSelBoxes &rBoxes, SwTableLine &rLine ) 1151 { 1152 sal_uInt16 nBoxCount = rLine.GetTabBoxes().Count(); 1153 sal_uInt16 nCurrBox; 1154 for( nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox ) 1155 rBoxes.Insert( rLine.GetTabBoxes()[nCurrBox] ); 1156 } 1157 1158 /** SwTable::InsertSpannedRow(..) inserts "superfluous" rows, i.e. rows containig 1159 overlapped cells only. This is a preparation for an upcoming split. 1160 */ 1161 1162 void SwTable::InsertSpannedRow( SwDoc* pDoc, sal_uInt16 nRowIdx, sal_uInt16 nCnt ) 1163 { 1164 CHECK_TABLE( *this ) 1165 ASSERT( nCnt && nRowIdx < GetTabLines().Count(), "Wrong call of InsertSpannedRow" ); 1166 SwSelBoxes aBoxes; 1167 SwTableLine& rLine = *GetTabLines()[ nRowIdx ]; 1168 lcl_FillSelBoxes( aBoxes, rLine ); 1169 SwFmtFrmSize aFSz( rLine.GetFrmFmt()->GetFrmSize() ); 1170 if( ATT_VAR_SIZE != aFSz.GetHeightSizeType() ) 1171 { 1172 SwFrmFmt* pFrmFmt = rLine.ClaimFrmFmt(); 1173 long nNewHeight = aFSz.GetHeight() / ( nCnt + 1 ); 1174 if( !nNewHeight ) 1175 ++nNewHeight; 1176 aFSz.SetHeight( nNewHeight ); 1177 pFrmFmt->SetFmtAttr( aFSz ); 1178 } 1179 _InsertRow( pDoc, aBoxes, nCnt, sal_True ); 1180 sal_uInt16 nBoxCount = rLine.GetTabBoxes().Count(); 1181 for( sal_uInt16 n = 0; n < nCnt; ++n ) 1182 { 1183 SwTableLine *pNewLine = GetTabLines()[ nRowIdx + nCnt - n ]; 1184 for( sal_uInt16 nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox ) 1185 { 1186 long nRowSpan = rLine.GetTabBoxes()[nCurrBox]->getRowSpan(); 1187 if( nRowSpan > 0 ) 1188 nRowSpan = - nRowSpan; 1189 pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n ); 1190 } 1191 } 1192 lcl_ChangeRowSpan( *this, nCnt, nRowIdx, false ); 1193 CHECK_TABLE( *this ) 1194 } 1195 1196 typedef std::pair< sal_uInt16, sal_uInt16 > SwLineOffset; 1197 typedef std::list< SwLineOffset > SwLineOffsetArray; 1198 1199 /****************************************************************************** 1200 When a couple of table boxes has to be split, 1201 lcl_SophisticatedFillLineIndices delivers the information where and how many 1202 rows have to be inserted. 1203 Input 1204 rTable: the table to manipulate 1205 rBoxes: an array of boxes to split 1206 nCnt: how many parts are wanted 1207 Output 1208 rArr: a list of pairs ( line index, number of lines to insert ) 1209 1210 ******************************************************************************/ 1211 1212 void lcl_SophisticatedFillLineIndices( SwLineOffsetArray &rArr, 1213 const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt ) 1214 { 1215 std::list< SwLineOffset > aBoxes; 1216 SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX ); 1217 for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i ) 1218 { // Collect all end line indices and the row spans 1219 const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable ); 1220 ASSERT( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" ) 1221 if( nCnt > rBox.getRowSpan() ) 1222 { 1223 const SwTableLine *pLine = rBox.GetUpper(); 1224 const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() + 1225 rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine ) ); 1226 // The next if statement is a small optimization 1227 if( aLnOfs.first != nEnd || aLnOfs.second != rBox.getRowSpan() ) 1228 { 1229 aLnOfs.first = nEnd; // ok, this is the line behind the box 1230 aLnOfs.second = sal_uInt16( rBox.getRowSpan() ); // the row span 1231 aBoxes.insert( aBoxes.end(), aLnOfs ); 1232 } 1233 } 1234 } 1235 // As I said, I noted the line index _behind_ the last line of the boxes 1236 // in the resulting array the index has to be _on_ the line 1237 // nSum is to evaluate the wished value 1238 sal_uInt16 nSum = 1; 1239 while( aBoxes.size() ) 1240 { 1241 // I. step: 1242 // Looking for the "smallest" line end with the smallest row span 1243 std::list< SwLineOffset >::iterator pCurr = aBoxes.begin(); 1244 aLnOfs = *pCurr; // the line end and row span of the first box 1245 while( ++pCurr != aBoxes.end() ) 1246 { 1247 if( aLnOfs.first > pCurr->first ) 1248 { // Found a smaller line end 1249 aLnOfs.first = pCurr->first; 1250 aLnOfs.second = pCurr->second; // row span 1251 } 1252 else if( aLnOfs.first == pCurr->first && 1253 aLnOfs.second < pCurr->second ) 1254 aLnOfs.second = pCurr->second; // Found a smaller row span 1255 } 1256 ASSERT( aLnOfs.second < nCnt, "Clean-up failed" ) 1257 aLnOfs.second = nCnt - aLnOfs.second; // the number of rows to insert 1258 rArr.insert( rArr.end(), 1259 SwLineOffset( aLnOfs.first - nSum, aLnOfs.second ) ); 1260 // the correction has to be incremented because in the following 1261 // loops the line ends were manipulated 1262 nSum = nSum + aLnOfs.second; 1263 1264 pCurr = aBoxes.begin(); 1265 while( pCurr != aBoxes.end() ) 1266 { 1267 if( pCurr->first == aLnOfs.first ) 1268 { // These boxes can be removed because the last insertion 1269 // of rows will expand their row span above the needed value 1270 std::list< SwLineOffset >::iterator pDel = pCurr; 1271 ++pCurr; 1272 aBoxes.erase( pDel ); 1273 } 1274 else 1275 { 1276 bool bBefore = ( pCurr->first - pCurr->second < aLnOfs.first ); 1277 // Manipulation of the end line indices as if the rows are 1278 // already inserted 1279 pCurr->first = pCurr->first + aLnOfs.second; 1280 if( bBefore ) 1281 { // If the insertion is inside the box, 1282 // its row span has to be incremented 1283 pCurr->second = pCurr->second + aLnOfs.second; 1284 if( pCurr->second >= nCnt ) 1285 { // if the row span is bigger than the split factor 1286 // this box is done 1287 std::list< SwLineOffset >::iterator pDel = pCurr; 1288 ++pCurr; 1289 aBoxes.erase( pDel ); 1290 } 1291 else 1292 ++pCurr; 1293 } 1294 else 1295 ++pCurr; 1296 } 1297 } 1298 } 1299 } 1300 1301 typedef std::set< SwTwips > SwSplitLines; 1302 1303 /** lcl_CalculateSplitLineHeights(..) delivers all y-positions where table rows have 1304 to be splitted to fulfill the requested "split same height" 1305 */ 1306 1307 sal_uInt16 lcl_CalculateSplitLineHeights( SwSplitLines &rCurr, SwSplitLines &rNew, 1308 const SwTable& rTable, const SwSelBoxes& rBoxes, sal_uInt16 nCnt ) 1309 { 1310 if( nCnt < 2 ) 1311 return 0; 1312 std::list< SwLineOffset > aBoxes; 1313 SwLineOffset aLnOfs( USHRT_MAX, USHRT_MAX ); 1314 sal_uInt16 nFirst = USHRT_MAX; // becomes the index of the first line 1315 sal_uInt16 nLast = 0; // becomes the index of the last line of the splitting 1316 for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i ) 1317 { // Collect all pairs (start+end) of line indices to split 1318 const SwTableBox &rBox = rBoxes[ i ]->FindStartOfRowSpan( rTable ); 1319 ASSERT( rBox.getRowSpan() > 0, "Didn't I say 'StartOfRowSpan' ??" ) 1320 const SwTableLine *pLine = rBox.GetUpper(); 1321 const sal_uInt16 nStart = rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine ); 1322 const sal_uInt16 nEnd = sal_uInt16( rBox.getRowSpan() + nStart - 1 ); 1323 // The next if statement is a small optimization 1324 if( aLnOfs.first != nStart || aLnOfs.second != nEnd ) 1325 { 1326 aLnOfs.first = nStart; 1327 aLnOfs.second = nEnd; 1328 aBoxes.insert( aBoxes.end(), aLnOfs ); 1329 if( nStart < nFirst ) 1330 nFirst = nStart; 1331 if( nEnd > nLast ) 1332 nLast = nEnd; 1333 } 1334 } 1335 1336 if( aBoxes.empty() ) 1337 return 0; 1338 SwTwips nHeight = 0; 1339 SwTwips* pLines = new SwTwips[ nLast + 1 - nFirst ]; 1340 for( sal_uInt16 i = nFirst; i <= nLast; ++i ) 1341 { 1342 bool bLayoutAvailable = false; 1343 nHeight += rTable.GetTabLines()[ i ]->GetTableLineHeight( bLayoutAvailable ); 1344 rCurr.insert( rCurr.end(), nHeight ); 1345 pLines[ i - nFirst ] = nHeight; 1346 } 1347 std::list< SwLineOffset >::iterator pSplit = aBoxes.begin(); 1348 while( pSplit != aBoxes.end() ) 1349 { 1350 SwTwips nBase = pSplit->first <= nFirst ? 0 : 1351 pLines[ pSplit->first - nFirst - 1 ]; 1352 SwTwips nDiff = pLines[ pSplit->second - nFirst ] - nBase; 1353 for( sal_uInt16 i = 1; i < nCnt; ++i ) 1354 { 1355 SwTwips nSplit = nBase + ( i * nDiff ) / nCnt; 1356 rNew.insert( nSplit ); 1357 } 1358 ++pSplit; 1359 } 1360 delete[] pLines; 1361 return nFirst; 1362 } 1363 1364 /** lcl_LineIndex(..) delivers the line index of the line behind or above 1365 the box selection. 1366 */ 1367 1368 sal_uInt16 lcl_LineIndex( const SwTable& rTable, const SwSelBoxes& rBoxes, 1369 bool bBehind ) 1370 { 1371 sal_uInt16 nDirect = USHRT_MAX; 1372 sal_uInt16 nSpan = USHRT_MAX; 1373 for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i ) 1374 { 1375 SwTableBox *pBox = rBoxes[i]; 1376 const SwTableLine* pLine = rBoxes[i]->GetUpper(); 1377 sal_uInt16 nPos = rTable.GetTabLines().C40_GETPOS( SwTableLine, pLine ); 1378 if( USHRT_MAX != nPos ) 1379 { 1380 if( bBehind ) 1381 { 1382 if( nPos > nDirect || nDirect == USHRT_MAX ) 1383 nDirect = nPos; 1384 long nRowSpan = pBox->getRowSpan(); 1385 if( nRowSpan < 2 ) 1386 nSpan = 0; 1387 else if( nSpan ) 1388 { 1389 sal_uInt16 nEndOfRowSpan = (sal_uInt16)(nPos + nRowSpan - 1); 1390 if( nEndOfRowSpan > nSpan || nSpan == USHRT_MAX ) 1391 nSpan = nEndOfRowSpan; 1392 } 1393 } 1394 else if( nPos < nDirect ) 1395 nDirect = nPos; 1396 } 1397 } 1398 if( nSpan && nSpan < USHRT_MAX ) 1399 return nSpan; 1400 return nDirect; 1401 } 1402 1403 /** SwTable::NewSplitRow(..) splits all selected boxes horizontally. 1404 */ 1405 1406 sal_Bool SwTable::NewSplitRow( SwDoc* pDoc, const SwSelBoxes& rBoxes, sal_uInt16 nCnt, 1407 sal_Bool bSameHeight ) 1408 { 1409 CHECK_TABLE( *this ) 1410 ++nCnt; 1411 _FndBox aFndBox( 0, 0 ); 1412 aFndBox.SetTableLines( rBoxes, *this ); 1413 1414 if( bSameHeight && pDoc->GetCurrentViewShell() ) //swmod 071108//swmod 071225 1415 { 1416 SwSplitLines aRowLines; 1417 SwSplitLines aSplitLines; 1418 sal_uInt16 nFirst = lcl_CalculateSplitLineHeights( aRowLines, aSplitLines, 1419 *this, rBoxes, nCnt ); 1420 aFndBox.DelFrms( *this ); 1421 SwTwips nLast = 0; 1422 SwSplitLines::iterator pSplit = aSplitLines.begin(); 1423 SwSplitLines::iterator pCurr = aRowLines.begin(); 1424 while( pCurr != aRowLines.end() ) 1425 { 1426 while( pSplit != aSplitLines.end() && *pSplit < *pCurr ) 1427 { 1428 InsertSpannedRow( pDoc, nFirst, 1 ); 1429 SwTableLine* pRow = GetTabLines()[ nFirst ]; 1430 SwFrmFmt* pRowFmt = pRow->ClaimFrmFmt(); 1431 SwFmtFrmSize aFSz( pRowFmt->GetFrmSize() ); 1432 aFSz.SetHeightSizeType( ATT_MIN_SIZE ); 1433 aFSz.SetHeight( *pSplit - nLast ); 1434 pRowFmt->SetFmtAttr( aFSz ); 1435 nLast = *pSplit; 1436 ++pSplit; 1437 ++nFirst; 1438 } 1439 if( pSplit != aSplitLines.end() && *pCurr == *pSplit ) 1440 ++pSplit; 1441 SwTableLine* pRow = GetTabLines()[ nFirst ]; 1442 SwFrmFmt* pRowFmt = pRow->ClaimFrmFmt(); 1443 SwFmtFrmSize aFSz( pRowFmt->GetFrmSize() ); 1444 aFSz.SetHeightSizeType( ATT_MIN_SIZE ); 1445 aFSz.SetHeight( *pCurr - nLast ); 1446 pRowFmt->SetFmtAttr( aFSz ); 1447 nLast = *pCurr; 1448 ++pCurr; 1449 ++nFirst; 1450 } 1451 } 1452 else 1453 { 1454 aFndBox.DelFrms( *this ); 1455 bSameHeight = sal_False; 1456 } 1457 if( !bSameHeight ) 1458 { 1459 SwLineOffsetArray aLineOffs; 1460 lcl_SophisticatedFillLineIndices( aLineOffs, *this, rBoxes, nCnt ); 1461 SwLineOffsetArray::reverse_iterator pCurr( aLineOffs.rbegin() ); 1462 while( pCurr != aLineOffs.rend() ) 1463 { 1464 InsertSpannedRow( pDoc, pCurr->first, pCurr->second ); 1465 ++pCurr; 1466 } 1467 } 1468 1469 std::set< sal_uInt16> aIndices; 1470 for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i ) 1471 { 1472 ASSERT( rBoxes[i]->getRowSpan() != 1, "Forgot to split?" ) 1473 if( rBoxes[i]->getRowSpan() > 1 ) 1474 aIndices.insert( i ); 1475 } 1476 1477 std::set< sal_uInt16 >::iterator pCurrBox = aIndices.begin(); 1478 while( pCurrBox != aIndices.end() ) 1479 lcl_UnMerge( *this, *rBoxes[*pCurrBox++], nCnt, bSameHeight ); 1480 1481 CHECK_TABLE( *this ) 1482 //Layout updaten 1483 aFndBox.MakeFrms( *this ); 1484 1485 return sal_True; 1486 } 1487 1488 /** SwTable::InsertRow(..) inserts one or more rows before or behind the selected 1489 boxes. 1490 */ 1491 1492 sal_Bool SwTable::InsertRow( SwDoc* pDoc, const SwSelBoxes& rBoxes, 1493 sal_uInt16 nCnt, sal_Bool bBehind ) 1494 { 1495 bool bRet = false; 1496 if( IsNewModel() ) 1497 { 1498 CHECK_TABLE( *this ) 1499 sal_uInt16 nRowIdx = lcl_LineIndex( *this, rBoxes, bBehind ); 1500 if( nRowIdx < USHRT_MAX ) 1501 { 1502 _FndBox aFndBox( 0, 0 ); 1503 aFndBox.SetTableLines( rBoxes, *this ); 1504 aFndBox.DelFrms( *this ); 1505 // aFndBox.SaveChartData( *this ); 1506 1507 bRet = true; 1508 SwTableLine *pLine = GetTabLines()[ nRowIdx ]; 1509 SwSelBoxes aLineBoxes; 1510 lcl_FillSelBoxes( aLineBoxes, *pLine ); 1511 _InsertRow( pDoc, aLineBoxes, nCnt, bBehind ); 1512 sal_uInt16 nBoxCount = pLine->GetTabBoxes().Count(); 1513 sal_uInt16 nOfs = bBehind ? 0 : 1; 1514 for( sal_uInt16 n = 0; n < nCnt; ++n ) 1515 { 1516 SwTableLine *pNewLine = GetTabLines()[ nRowIdx+nCnt-n-nOfs]; 1517 for( sal_uInt16 nCurrBox = 0; nCurrBox < nBoxCount; ++nCurrBox ) 1518 { 1519 long nRowSpan = pLine->GetTabBoxes()[nCurrBox]->getRowSpan(); 1520 if( bBehind ) 1521 { 1522 if( nRowSpan == 1 || nRowSpan == -1 ) 1523 nRowSpan = n + 1; 1524 else if( nRowSpan > 1 ) 1525 nRowSpan = - nRowSpan; 1526 } 1527 else 1528 { 1529 if( nRowSpan > 0 ) 1530 nRowSpan = n + 1; 1531 else 1532 --nRowSpan; 1533 } 1534 pNewLine->GetTabBoxes()[ nCurrBox ]->setRowSpan( nRowSpan - n ); 1535 } 1536 } 1537 if( bBehind ) 1538 ++nRowIdx; 1539 if( nRowIdx ) 1540 lcl_ChangeRowSpan( *this, nCnt, --nRowIdx, true ); 1541 //Layout update 1542 aFndBox.MakeFrms( *this ); 1543 // aFndBox.RestoreChartData( *this ); 1544 } 1545 CHECK_TABLE( *this ) 1546 } 1547 else 1548 bRet = _InsertRow( pDoc, rBoxes, nCnt, bBehind ); 1549 return bRet; 1550 } 1551 1552 /** SwTable::PrepareDelBoxes(..) adjusts the row span attributes for an upcoming 1553 deletion of table cells and invalidates the layout of these cells. 1554 */ 1555 1556 void SwTable::PrepareDelBoxes( const SwSelBoxes& rBoxes ) 1557 { 1558 if( IsNewModel() ) 1559 { 1560 for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i ) 1561 { 1562 SwTableBox* pBox = rBoxes[i]; 1563 long nRowSpan = pBox->getRowSpan(); 1564 if( nRowSpan != 1 && pBox->GetFrmFmt()->GetFrmSize().GetWidth() ) 1565 { 1566 long nLeft = lcl_Box2LeftBorder( *pBox ); 1567 SwTableLine *pLine = pBox->GetUpper(); 1568 sal_uInt16 nLinePos = GetTabLines().C40_GETPOS(SwTableLine, pLine); 1569 ASSERT( nLinePos < USHRT_MAX, "Box/table mismatch" ) 1570 if( nRowSpan > 1 ) 1571 { 1572 if( ++nLinePos < GetTabLines().Count() ) 1573 { 1574 pLine = GetTabLines()[ nLinePos ]; 1575 pBox = lcl_LeftBorder2Box( nLeft, pLine ); 1576 ASSERT( pBox, "RowSpan irritation I" ) 1577 if( pBox ) 1578 pBox->setRowSpan( --nRowSpan ); 1579 } 1580 } 1581 else if( nLinePos > 0 ) 1582 { 1583 do 1584 { 1585 pLine = GetTabLines()[ --nLinePos ]; 1586 pBox = lcl_LeftBorder2Box( nLeft, pLine ); 1587 ASSERT( pBox, "RowSpan irritation II" ) 1588 if( pBox ) 1589 { 1590 nRowSpan = pBox->getRowSpan(); 1591 if( nRowSpan > 1 ) 1592 { 1593 lcl_InvalidateCellFrm( *pBox ); 1594 --nRowSpan; 1595 } 1596 else 1597 ++nRowSpan; 1598 pBox->setRowSpan( nRowSpan ); 1599 } 1600 else 1601 nRowSpan = 1; 1602 } 1603 while( nRowSpan < 0 && nLinePos > 0 ); 1604 } 1605 } 1606 } 1607 } 1608 } 1609 1610 /** lcl_SearchSelBox(..) adds cells of a given table row to the selection structure 1611 if it overlaps with the given x-position range 1612 */ 1613 1614 void lcl_SearchSelBox( const SwTable &rTable, SwSelBoxes& rBoxes, long nMin, long nMax, 1615 SwTableLine& rLine, bool bChkProtected, bool bColumn ) 1616 { 1617 long nLeft = 0; 1618 long nRight = 0; 1619 long nMid = ( nMax + nMin )/ 2; 1620 sal_uInt16 nCount = rLine.GetTabBoxes().Count(); 1621 for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox ) 1622 { 1623 SwTableBox* pBox = rLine.GetTabBoxes()[nCurrBox]; 1624 ASSERT( pBox, "Missing table box" ); 1625 long nWidth = pBox->GetFrmFmt()->GetFrmSize().GetWidth(); 1626 nRight += nWidth; 1627 if( nRight > nMin ) 1628 { 1629 bool bAdd = false; 1630 if( nRight <= nMax ) 1631 bAdd = nLeft >= nMin || nRight >= nMid || 1632 nRight - nMin > nMin - nLeft; 1633 else 1634 bAdd = nLeft <= nMid || nRight - nMax < nMax - nLeft; 1635 long nRowSpan = pBox->getRowSpan(); 1636 if( bAdd && 1637 //( bColumn || nRowSpan > 0 ) && 1638 ( !bChkProtected || 1639 !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() ) ) 1640 { 1641 sal_uInt16 nOldCnt = rBoxes.Count(); 1642 rBoxes.Insert( pBox ); 1643 if( bColumn && nRowSpan != 1 && nOldCnt < rBoxes.Count() ) 1644 { 1645 SwTableBox *pMasterBox = pBox->getRowSpan() > 0 ? pBox 1646 : &pBox->FindStartOfRowSpan( rTable, USHRT_MAX ); 1647 lcl_getAllMergedBoxes( rTable, rBoxes, *pMasterBox ); 1648 } 1649 } 1650 } 1651 if( nRight >= nMax ) 1652 break; 1653 nLeft = nRight; 1654 } 1655 } 1656 1657 /** void SwTable::CreateSelection(..) fills the selection structure with table cells 1658 for a given SwPaM, ie. start and end position inside a table 1659 */ 1660 1661 void SwTable::CreateSelection( const SwPaM& rPam, SwSelBoxes& rBoxes, 1662 const SearchType eSearch, bool bChkProtected ) const 1663 { 1664 ASSERT( bNewModel, "Don't call me for old tables" ); 1665 if( !aLines.Count() ) 1666 return; 1667 const SwNode* pStartNd = rPam.GetPoint()->nNode.GetNode().FindTableBoxStartNode(); 1668 const SwNode* pEndNd = rPam.GetMark()->nNode.GetNode().FindTableBoxStartNode(); 1669 if( !pStartNd || !pEndNd ) 1670 return; 1671 CreateSelection( pStartNd, pEndNd, rBoxes, eSearch, bChkProtected ); 1672 } 1673 1674 /** void SwTable::CreateSelection(..) fills the selection structure with table cells 1675 for given start and end nodes inside a table 1676 */ 1677 void SwTable::CreateSelection( const SwNode* pStartNd, const SwNode* pEndNd, 1678 SwSelBoxes& rBoxes, const SearchType eSearch, bool bChkProtected ) const 1679 { 1680 // SwSelBoxes aKeepBoxes; 1681 if( rBoxes.Count() ) 1682 { 1683 // aKeepBoxes.Insert( &rBoxes ); 1684 rBoxes.Remove( sal_uInt16(0), rBoxes.Count() ); 1685 } 1686 // Looking for start and end of the selection given by SwNode-pointer 1687 sal_uInt16 nLines = aLines.Count(); 1688 // nTop becomes the line number of the upper box 1689 // nBottom becomes the line number of the lower box 1690 sal_uInt16 nTop = 0, nBottom = 0; 1691 // nUpperMin becomes the left border value of the upper box 1692 // nUpperMax becomes the right border of the upper box 1693 // nLowerMin and nLowerMax the borders of the lower box 1694 long nUpperMin = 0, nUpperMax = 0; 1695 long nLowerMin = 0, nLowerMax = 0; 1696 // nFound will incremented if a box is found 1697 // 0 => no box found; 1 => the upper box has been found; 2 => both found 1698 int nFound = 0; 1699 for( sal_uInt16 nRow = 0; nFound < 2 && nRow < nLines; ++nRow ) 1700 { 1701 SwTableLine* pLine = aLines[nRow]; 1702 ASSERT( pLine, "Missing table line" ); 1703 sal_uInt16 nCols = pLine->GetTabBoxes().Count(); 1704 for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol ) 1705 { 1706 SwTableBox* pBox = pLine->GetTabBoxes()[nCol]; 1707 ASSERT( pBox, "Missing table box" ); 1708 if( pBox->GetSttNd() == pEndNd || pBox->GetSttNd() == pStartNd ) 1709 { 1710 if( !bChkProtected || 1711 !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() ) 1712 rBoxes.Insert( pBox ); 1713 if( nFound ) 1714 { 1715 nBottom = nRow; 1716 lcl_CheckMinMax( nLowerMin, nLowerMax, *pLine, nCol, true ); 1717 ++nFound; 1718 break; 1719 } 1720 else 1721 { 1722 nTop = nRow; 1723 lcl_CheckMinMax( nUpperMin, nUpperMax, *pLine, nCol, true ); 1724 ++nFound; 1725 // If start and end node are identical, we're nearly done.. 1726 if( pEndNd == pStartNd ) 1727 { 1728 nBottom = nTop; 1729 nLowerMin = nUpperMin; 1730 nLowerMax = nUpperMax; 1731 ++nFound; 1732 } 1733 } 1734 } 1735 } 1736 } 1737 if( nFound < 2 ) 1738 return; // At least one node was not a part of the given table 1739 if( eSearch == SEARCH_ROW ) 1740 { 1741 // Selection of a row is quiet easy: 1742 // every (unprotected) box between start and end line 1743 // with a positive row span will be collected 1744 for( sal_uInt16 nRow = nTop; nRow <= nBottom; ++nRow ) 1745 { 1746 SwTableLine* pLine = aLines[nRow]; 1747 ASSERT( pLine, "Missing table line" ); 1748 sal_uInt16 nCount = pLine->GetTabBoxes().Count(); 1749 for( sal_uInt16 nCurrBox = 0; nCurrBox < nCount; ++nCurrBox ) 1750 { 1751 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox]; 1752 ASSERT( pBox, "Missing table box" ); 1753 if( pBox->getRowSpan() > 0 && ( !bChkProtected || 1754 !pBox->GetFrmFmt()->GetProtect().IsCntntProtected() ) ) 1755 rBoxes.Insert( pBox ); 1756 } 1757 } 1758 return; 1759 } 1760 bool bCombine = nTop == nBottom; 1761 if( !bCombine ) 1762 { 1763 long nMinWidth = nUpperMax - nUpperMin; 1764 long nTmp = nLowerMax - nLowerMin; 1765 if( nMinWidth > nTmp ) 1766 nMinWidth = nTmp; 1767 nTmp = nLowerMax < nUpperMax ? nLowerMax : nUpperMax; 1768 nTmp -= ( nLowerMin < nUpperMin ) ? nUpperMin : nLowerMin; 1769 // If the overlapping between upper and lower box is less than half 1770 // of the width (of the smaller cell), bCombine is set, 1771 // e.g. if upper and lower cell are in different columns 1772 bCombine = ( nTmp + nTmp < nMinWidth ); 1773 } 1774 if( bCombine ) 1775 { 1776 if( nUpperMin < nLowerMin ) 1777 nLowerMin = nUpperMin; 1778 else 1779 nUpperMin = nLowerMin; 1780 if( nUpperMax > nLowerMax ) 1781 nLowerMax = nUpperMax; 1782 else 1783 nUpperMax = nLowerMax; 1784 } 1785 const bool bColumn = eSearch == SEARCH_COL; 1786 if( bColumn ) 1787 { 1788 for( sal_uInt16 i = 0; i < nTop; ++i ) 1789 lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax, 1790 *aLines[i], bChkProtected, bColumn ); 1791 } 1792 1793 { 1794 long nMin = nUpperMin < nLowerMin ? nUpperMin : nLowerMin; 1795 long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax; 1796 for( sal_uInt16 i = nTop; i <= nBottom; ++i ) 1797 lcl_SearchSelBox( *this, rBoxes, nMin, nMax, *aLines[i], 1798 bChkProtected, bColumn ); 1799 } 1800 /* if( nTop + 1 < nBottom ) 1801 { 1802 long nInnerMin = nUpperMin < nLowerMin ? nLowerMin : nUpperMin; 1803 long nInnerMax = nUpperMax < nLowerMax ? nUpperMax : nLowerMax; 1804 for( sal_uInt16 i = nTop + 1; i < nBottom; ++i ) 1805 lcl_SearchSelBox( *this, rBoxes, nInnerMin, nInnerMax, *aLines[i], 1806 bChkProtected, bColumn ); 1807 } 1808 if( bCombine ) // => nUpperMin == nLowerMin, nUpperMax == nLowerMax 1809 { 1810 if( nBottom > nTop ) 1811 lcl_SearchSelBox( *this, rBoxes, nUpperMin, nUpperMax, *aLines[nTop], 1812 bChkProtected, bColumn ); 1813 lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *aLines[nBottom], 1814 bChkProtected, bColumn ); 1815 } 1816 else if( aKeepBoxes.Count() ) 1817 { 1818 long nMin = nUpperMin < nLowerMin ? nUpperMin : nLowerMin; 1819 long nMax = nUpperMax < nLowerMax ? nLowerMax : nUpperMax; 1820 SwSelBoxes aCandidates; 1821 for( sal_uInt16 i = nTop; i <= nBottom; ++i ) 1822 lcl_SearchSelBox( *this, aCandidates, nMin, nMax, *aLines[i], 1823 bChkProtected, bColumn ); 1824 sal_uInt16 nOld = 0, nNew = 0; 1825 while ( nOld < aKeepBoxes.Count() && nNew < aCandidates.Count() ) 1826 { 1827 const SwTableBox* pPOld = *( aKeepBoxes.GetData() + nOld ); 1828 SwTableBox* pPNew = *( aCandidates.GetData() + nNew ); 1829 if( pPOld == pPNew ) 1830 { // this box will stay 1831 rBoxes.Insert( pPNew ); 1832 ++nOld; 1833 ++nNew; 1834 } 1835 else if( pPOld->GetSttIdx() < pPNew->GetSttIdx() ) 1836 ++nOld; 1837 else 1838 ++nNew; 1839 } 1840 } */ 1841 if( bColumn ) 1842 { 1843 for( sal_uInt16 i = nBottom + 1; i < nLines; ++i ) 1844 lcl_SearchSelBox( *this, rBoxes, nLowerMin, nLowerMax, *aLines[i], 1845 bChkProtected, true ); 1846 } 1847 } 1848 1849 /** void SwTable::ExpandColumnSelection(..) adds cell to the give selection to 1850 assure that at least one cell of every row is part of the selection. 1851 */ 1852 1853 void SwTable::ExpandColumnSelection( SwSelBoxes& rBoxes, long &rMin, long &rMax ) const 1854 { 1855 ASSERT( bNewModel, "Don't call me for old tables" ); 1856 rMin = 0; 1857 rMax = 0; 1858 if( !aLines.Count() || !rBoxes.Count() ) 1859 return; 1860 1861 sal_uInt16 nLineCnt = aLines.Count(); 1862 sal_uInt16 nBoxCnt = rBoxes.Count(); 1863 sal_uInt16 nBox = 0; 1864 for( sal_uInt16 nRow = 0; nRow < nLineCnt && nBox < nBoxCnt; ++nRow ) 1865 { 1866 SwTableLine* pLine = aLines[nRow]; 1867 ASSERT( pLine, "Missing table line" ); 1868 sal_uInt16 nCols = pLine->GetTabBoxes().Count(); 1869 for( sal_uInt16 nCol = 0; nCol < nCols; ++nCol ) 1870 { 1871 SwTableBox* pBox = pLine->GetTabBoxes()[nCol]; 1872 ASSERT( pBox, "Missing table box" ); 1873 if( pBox == rBoxes[nBox] ) 1874 { 1875 lcl_CheckMinMax( rMin, rMax, *pLine, nCol, nBox == 0 ); 1876 if( ++nBox >= nBoxCnt ) 1877 break; 1878 } 1879 } 1880 } 1881 nBox = 0; 1882 for( sal_uInt16 nRow = 0; nRow < nLineCnt; ++nRow ) 1883 { 1884 SwTableLine* pLine = aLines[nRow]; 1885 sal_uInt16 nCols = pLine->GetTabBoxes().Count(); 1886 long nLeft = 0; 1887 long nRight = 0; 1888 for( sal_uInt16 nCurrBox = 0; nCurrBox < nCols; ++nCurrBox ) 1889 { 1890 nLeft = nRight; 1891 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox]; 1892 nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth(); 1893 if( nLeft >= rMin && nRight <= rMax ) 1894 rBoxes.Insert( pBox ); 1895 } 1896 } 1897 } 1898 1899 /** SwTable::PrepareDeleteCol(..) adjusts the widths of the neighbour cells of 1900 a cell selection for an upcoming (column) deletion 1901 */ 1902 void SwTable::PrepareDeleteCol( long nMin, long nMax ) 1903 { 1904 ASSERT( bNewModel, "Don't call me for old tables" ); 1905 if( !aLines.Count() || nMax < nMin ) 1906 return; 1907 long nMid = nMin ? ( nMin + nMax ) / 2 : 0; 1908 const SwTwips nTabSize = GetFrmFmt()->GetFrmSize().GetWidth(); 1909 if( nTabSize == nMax ) 1910 nMid = nMax; 1911 sal_uInt16 nLineCnt = aLines.Count(); 1912 for( sal_uInt16 nRow = 0; nRow < nLineCnt; ++nRow ) 1913 { 1914 SwTableLine* pLine = aLines[nRow]; 1915 sal_uInt16 nCols = pLine->GetTabBoxes().Count(); 1916 long nLeft = 0; 1917 long nRight = 0; 1918 for( sal_uInt16 nCurrBox = 0; nCurrBox < nCols; ++nCurrBox ) 1919 { 1920 nLeft = nRight; 1921 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrBox]; 1922 nRight += pBox->GetFrmFmt()->GetFrmSize().GetWidth(); 1923 if( nRight < nMin ) 1924 continue; 1925 if( nLeft > nMax ) 1926 break; 1927 long nNewWidth = -1; 1928 if( nLeft < nMin ) 1929 { 1930 if( nRight <= nMax ) 1931 nNewWidth = nMid - nLeft; 1932 } 1933 else if( nRight > nMax ) 1934 nNewWidth = nRight - nMid; 1935 else 1936 nNewWidth = 0; 1937 if( nNewWidth >= 0 ) 1938 { 1939 SwFrmFmt* pFrmFmt = pBox->ClaimFrmFmt(); 1940 SwFmtFrmSize aFrmSz( pFrmFmt->GetFrmSize() ); 1941 aFrmSz.SetWidth( nNewWidth ); 1942 pFrmFmt->SetFmtAttr( aFrmSz ); 1943 } 1944 } 1945 } 1946 } 1947 1948 1949 1950 /** SwTable::ExpandSelection(..) adds all boxes to the box selections which are 1951 overlapped by it. 1952 */ 1953 1954 void SwTable::ExpandSelection( SwSelBoxes& rBoxes ) const 1955 { 1956 for( sal_uInt16 i = 0; i < rBoxes.Count(); ++i ) 1957 { 1958 SwTableBox *pBox = rBoxes[i]; 1959 long nRowSpan = pBox->getRowSpan(); 1960 if( nRowSpan != 1 ) 1961 { 1962 SwTableBox *pMasterBox = nRowSpan > 0 ? pBox 1963 : &pBox->FindStartOfRowSpan( *this, USHRT_MAX ); 1964 lcl_getAllMergedBoxes( *this, rBoxes, *pMasterBox ); 1965 } 1966 } 1967 } 1968 1969 /** SwTable::CheckRowSpan(..) looks for the next line without an overlapping to 1970 the previous line. 1971 */ 1972 1973 void SwTable::CheckRowSpan( SwTableLinePtr &rpLine, bool bUp ) const 1974 { 1975 ASSERT( IsNewModel(), "Don't call me for old tables" ); 1976 sal_uInt16 nLineIdx = GetTabLines().C40_GETPOS( SwTableLine, rpLine ); 1977 ASSERT( nLineIdx < GetTabLines().Count(), "Start line out of range" ); 1978 bool bChange = true; 1979 if( bUp ) 1980 { 1981 while( bChange ) 1982 { 1983 bChange = false; 1984 rpLine = GetTabLines()[ nLineIdx ]; 1985 sal_uInt16 nCols = rpLine->GetTabBoxes().Count(); 1986 for( sal_uInt16 nCol = 0; !bChange && nCol < nCols; ++nCol ) 1987 { 1988 SwTableBox* pBox = rpLine->GetTabBoxes()[nCol]; 1989 if( pBox->getRowSpan() > 1 || pBox->getRowSpan() < -1 ) 1990 bChange = true; 1991 } 1992 if( bChange ) 1993 { 1994 if( nLineIdx ) 1995 --nLineIdx; 1996 else 1997 { 1998 bChange = false; 1999 rpLine = 0; 2000 } 2001 } 2002 } 2003 } 2004 else 2005 { 2006 sal_uInt16 nMaxLine = GetTabLines().Count(); 2007 while( bChange ) 2008 { 2009 bChange = false; 2010 rpLine = GetTabLines()[ nLineIdx ]; 2011 sal_uInt16 nCols = rpLine->GetTabBoxes().Count(); 2012 for( sal_uInt16 nCol = 0; !bChange && nCol < nCols; ++nCol ) 2013 { 2014 SwTableBox* pBox = rpLine->GetTabBoxes()[nCol]; 2015 if( pBox->getRowSpan() < 0 ) 2016 bChange = true; 2017 } 2018 if( bChange ) 2019 { 2020 ++nLineIdx; 2021 if( nLineIdx >= nMaxLine ) 2022 { 2023 bChange = false; 2024 rpLine = 0; 2025 } 2026 } 2027 } 2028 } 2029 } 2030 2031 // This structure corrects the row span attributes for a top line of a table 2032 // In a top line no negative row span is allowed, so these have to be corrected. 2033 // If there has been at least one correction, all values are stored 2034 // and can be used by undo of table split 2035 SwSaveRowSpan::SwSaveRowSpan( SwTableBoxes& rBoxes, sal_uInt16 nSplitLn ) 2036 : mnSplitLine( nSplitLn ) 2037 { 2038 bool bDontSave = true; // nothing changed, nothing to save 2039 sal_uInt16 nColCount = rBoxes.Count(); 2040 ASSERT( nColCount, "Empty Table Line" ) 2041 mnRowSpans.resize( nColCount ); 2042 for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol ) 2043 { 2044 SwTableBox* pBox = rBoxes[nCurrCol]; 2045 ASSERT( pBox, "Missing Table Box" ); 2046 long nRowSp = pBox->getRowSpan(); 2047 mnRowSpans[ nCurrCol ] = nRowSp; 2048 if( nRowSp < 0 ) 2049 { 2050 bDontSave = false; 2051 nRowSp = -nRowSp; 2052 pBox->setRowSpan( nRowSp ); // correction needed 2053 } 2054 } 2055 if( bDontSave ) 2056 mnRowSpans.clear(); 2057 } 2058 2059 // This function is called by undo of table split to restore the old row span 2060 // values at the split line 2061 void SwTable::RestoreRowSpan( const SwSaveRowSpan& rSave ) 2062 { 2063 if( !IsNewModel() ) // for new model only 2064 return; 2065 sal_uInt16 nLineCount = GetTabLines().Count(); 2066 ASSERT( rSave.mnSplitLine < nLineCount, "Restore behind last line?" ) 2067 if( rSave.mnSplitLine < nLineCount ) 2068 { 2069 SwTableLine* pLine = GetTabLines()[rSave.mnSplitLine]; 2070 sal_uInt16 nColCount = pLine->GetTabBoxes().Count(); 2071 ASSERT( nColCount, "Empty Table Line" ) 2072 ASSERT( nColCount == rSave.mnRowSpans.size(), "Wrong row span store" ) 2073 if( nColCount == rSave.mnRowSpans.size() ) 2074 { 2075 for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol ) 2076 { 2077 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol]; 2078 ASSERT( pBox, "Missing Table Box" ); 2079 long nRowSp = pBox->getRowSpan(); 2080 if( nRowSp != rSave.mnRowSpans[ nCurrCol ] ) 2081 { 2082 ASSERT( -nRowSp == rSave.mnRowSpans[ nCurrCol ], "Pardon me?!" ) 2083 ASSERT( rSave.mnRowSpans[ nCurrCol ] < 0, "Pardon me?!" ) 2084 pBox->setRowSpan( -nRowSp ); 2085 2086 sal_uInt16 nLine = rSave.mnSplitLine; 2087 if( nLine ) 2088 { 2089 long nLeftBorder = lcl_Box2LeftBorder( *pBox ); 2090 SwTableBox* pNext; 2091 do 2092 { 2093 pNext = lcl_LeftBorder2Box( nLeftBorder, GetTabLines()[--nLine] ); 2094 if( pNext ) 2095 { 2096 pBox = pNext; 2097 long nNewSpan = pBox->getRowSpan(); 2098 if( pBox->getRowSpan() < 1 ) 2099 nNewSpan -= nRowSp; 2100 else 2101 { 2102 nNewSpan += nRowSp; 2103 pNext = 0; 2104 } 2105 pBox->setRowSpan( nNewSpan ); 2106 } 2107 } while( nLine && pNext ); 2108 } 2109 } 2110 } 2111 } 2112 } 2113 } 2114 2115 SwSaveRowSpan* SwTable::CleanUpTopRowSpan( sal_uInt16 nSplitLine ) 2116 { 2117 SwSaveRowSpan* pRet = 0; 2118 if( !IsNewModel() ) 2119 return pRet; 2120 pRet = new SwSaveRowSpan( GetTabLines()[0]->GetTabBoxes(), nSplitLine ); 2121 if( pRet->mnRowSpans.size() == 0 ) 2122 { 2123 delete pRet; 2124 pRet = 0; 2125 } 2126 return pRet; 2127 } 2128 2129 void SwTable::CleanUpBottomRowSpan( sal_uInt16 nDelLines ) 2130 { 2131 if( !IsNewModel() ) 2132 return; 2133 sal_uInt16 nLastLine = GetTabLines().Count()-1; 2134 SwTableLine* pLine = GetTabLines()[nLastLine]; 2135 sal_uInt16 nColCount = pLine->GetTabBoxes().Count(); 2136 ASSERT( nColCount, "Empty Table Line" ) 2137 for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol ) 2138 { 2139 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol]; 2140 ASSERT( pBox, "Missing Table Box" ); 2141 long nRowSp = pBox->getRowSpan(); 2142 if( nRowSp < 0 ) 2143 nRowSp = -nRowSp; 2144 if( nRowSp > 1 ) 2145 { 2146 lcl_ChangeRowSpan( *this, -static_cast<long>(nDelLines), nLastLine, false ); 2147 break; 2148 } 2149 } 2150 } 2151 2152 #ifdef DBG_UTIL 2153 2154 struct RowSpanCheck 2155 { 2156 long nRowSpan; 2157 SwTwips nLeft; 2158 SwTwips nRight; 2159 }; 2160 2161 void SwTable::CheckConsistency() const 2162 { 2163 if( !IsNewModel() ) 2164 return; 2165 sal_uInt16 nLineCount = GetTabLines().Count(); 2166 const SwTwips nTabSize = GetFrmFmt()->GetFrmSize().GetWidth(); 2167 SwTwips nLineWidth = 0; 2168 std::list< RowSpanCheck > aRowSpanCells; 2169 std::list< RowSpanCheck >::iterator aIter = aRowSpanCells.end(); 2170 for( sal_uInt16 nCurrLine = 0; nCurrLine < nLineCount; ++nCurrLine ) 2171 { 2172 SwTwips nWidth = 0; 2173 SwTableLine* pLine = GetTabLines()[nCurrLine]; 2174 ASSERT( pLine, "Missing Table Line" ) 2175 sal_uInt16 nColCount = pLine->GetTabBoxes().Count(); 2176 ASSERT( nColCount, "Empty Table Line" ) 2177 for( sal_uInt16 nCurrCol = 0; nCurrCol < nColCount; ++nCurrCol ) 2178 { 2179 SwTableBox* pBox = pLine->GetTabBoxes()[nCurrCol]; 2180 ASSERT( pBox, "Missing Table Box" ); 2181 SwTwips nNewWidth = pBox->GetFrmFmt()->GetFrmSize().GetWidth() + nWidth; 2182 long nRowSp = pBox->getRowSpan(); 2183 if( nRowSp < 0 ) 2184 { 2185 ASSERT( aIter != aRowSpanCells.end(), "Missing master box" ) 2186 #ifdef DBG_UTIL 2187 //RowSpanCheck &rCheck = *aIter; 2188 #endif 2189 ASSERT( aIter->nLeft == nWidth && aIter->nRight == nNewWidth, 2190 "Wrong position/size of overlapped table box" ); 2191 --(aIter->nRowSpan); 2192 ASSERT( aIter->nRowSpan == -nRowSp, "Wrong row span value" ); 2193 if( nRowSp == -1 ) 2194 { 2195 std::list< RowSpanCheck >::iterator aEraseIter = aIter; 2196 ++aIter; 2197 aRowSpanCells.erase( aEraseIter ); 2198 } 2199 else 2200 ++aIter; 2201 } 2202 else if( nRowSp != 1 ) 2203 { 2204 ASSERT( nRowSp, "Zero row span?!" ); 2205 RowSpanCheck aEntry; 2206 aEntry.nLeft = nWidth; 2207 aEntry.nRight = nNewWidth; 2208 aEntry.nRowSpan = nRowSp; 2209 aRowSpanCells.insert( aIter, aEntry ); 2210 } 2211 nWidth = nNewWidth; 2212 } 2213 if( !nCurrLine ) 2214 nLineWidth = nWidth; 2215 ASSERT( nWidth == nLineWidth, "Different Line Widths" ) 2216 ASSERT( nWidth == nTabSize, "Boxen der Line zu klein/gross" ) 2217 ASSERT( nWidth >= 0 && nWidth <= USHRT_MAX, "Width out of range" ) 2218 ASSERT( aIter == aRowSpanCells.end(), "Missing overlapped box" ) 2219 aIter = aRowSpanCells.begin(); 2220 } 2221 bool bEmpty = aRowSpanCells.empty(); 2222 ASSERT( bEmpty, "Open row span detected" ) 2223 } 2224 2225 #endif 2226 2227 2228 #ifdef FINDSTARTENDOFROWSPANCACHE 2229 /* 2230 * A small optimization for FindStartEndOfRowSpan START 2231 * 2232 * NOTE: Results of some measurement revealed that this cache 2233 * does not improve performance! 2234 */ 2235 2236 class SwFindRowSpanCache 2237 { 2238 private: 2239 2240 struct SwFindRowSpanCacheObj 2241 { 2242 const SwTableBox* mpKeyBox; 2243 const SwTableBox* mpCacheBox; 2244 sal_uInt16 mnSteps; 2245 bool mbStart; 2246 2247 SwFindRowSpanCacheObj( const SwTableBox& rKeyBox, const SwTableBox& rCacheBox, sal_uInt16 nSteps, bool bStart ) : 2248 mpKeyBox( &rKeyBox ), mpCacheBox( &rCacheBox ), mnSteps( nSteps ), mbStart( bStart ) {} 2249 }; 2250 2251 std::list< SwFindRowSpanCacheObj > aCache; 2252 bool mbUseCache; 2253 static SwFindRowSpanCache* mpFindRowSpanCache; 2254 SwFindRowSpanCache(); 2255 2256 public: 2257 2258 static SwFindRowSpanCache& getSwFindRowSpanCache(); 2259 const SwTableBox* FindCachedStartEndOfRowSpan( const SwTableBox& rKeyBox, sal_uInt16 nSteps, bool bStart ); 2260 void SetCachedStartEndOfRowSpan( const SwTableBox& rKeyBox, const SwTableBox& rCacheBox, sal_uInt16 nSteps, bool bStart ); 2261 void SetUseCache( bool bNew ); 2262 }; 2263 2264 SwFindRowSpanCache* SwFindRowSpanCache::mpFindRowSpanCache = 0; 2265 SwFindRowSpanCache& SwFindRowSpanCache::getSwFindRowSpanCache() 2266 { 2267 if ( !mpFindRowSpanCache ) mpFindRowSpanCache = new SwFindRowSpanCache; 2268 return *mpFindRowSpanCache; 2269 } 2270 2271 SwFindRowSpanCache::SwFindRowSpanCache() : mbUseCache( false ) 2272 { 2273 } 2274 2275 void SwFindRowSpanCache::SetUseCache( bool bNew ) 2276 { 2277 mbUseCache = bNew; aCache.clear(); 2278 } 2279 2280 const SwTableBox* SwFindRowSpanCache::FindCachedStartEndOfRowSpan( const SwTableBox& rKeyBox, 2281 sal_uInt16 nSteps, 2282 bool bStart ) 2283 { 2284 static nCallCount = 0; 2285 static nSuccessCount = 0; 2286 ++nCallCount; 2287 2288 if ( !mbUseCache ) return 0; 2289 2290 const SwTableBox* pRet = 0; 2291 2292 std::list< SwFindRowSpanCacheObj >::const_iterator aIter; 2293 for ( aIter = aCache.begin(); aIter != aCache.end(); ++aIter ) 2294 { 2295 if ( aIter->mpKeyBox == &rKeyBox && 2296 aIter->mnSteps == nSteps && 2297 aIter->mbStart == bStart ) 2298 { 2299 pRet = aIter->mpCacheBox; 2300 ++nSuccessCount; 2301 break; 2302 } 2303 } 2304 2305 return pRet; 2306 } 2307 2308 const int FindBoxCacheSize = 2; 2309 2310 void SwFindRowSpanCache::SetCachedStartEndOfRowSpan( const SwTableBox& rKeyBox, 2311 const SwTableBox& rCacheBox, 2312 sal_uInt16 nSteps, 2313 bool bStart ) 2314 { 2315 if ( !mbUseCache ) return; 2316 2317 const SwFindRowSpanCacheObj aNew( rKeyBox, rCacheBox, nSteps, bStart ); 2318 aCache.push_front( aNew ); 2319 if ( aCache.size() > FindBoxCacheSize ) 2320 aCache.pop_back(); 2321 } 2322 2323 /* 2324 * A small optimization for FindStartEndOfRowSpan END 2325 */ 2326 2327 #endif 2328 2329