1 /************************************************************** 2 * 3 * Licensed to the Apache Software Foundation (ASF) under one 4 * or more contributor license agreements. See the NOTICE file 5 * distributed with this work for additional information 6 * regarding copyright ownership. The ASF licenses this file 7 * to you under the Apache License, Version 2.0 (the 8 * "License"); you may not use this file except in compliance 9 * with the License. You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, 14 * software distributed under the License is distributed on an 15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 * KIND, either express or implied. See the License for the 17 * specific language governing permissions and limitations 18 * under the License. 19 * 20 *************************************************************/ 21 22 23 24 // MARKER(update_precomp.py): autogen include statement, do not remove 25 #include "precompiled_svx.hxx" 26 27 #include <com/sun/star/table/XMergeableCell.hpp> 28 #include <com/sun/star/awt/XLayoutConstrains.hpp> 29 #include <boost/bind.hpp> 30 31 #include "cell.hxx" 32 #include "cellrange.hxx" 33 #include "tablemodel.hxx" 34 #include "tablerow.hxx" 35 #include "tablerows.hxx" 36 #include "tablecolumn.hxx" 37 #include "tablecolumns.hxx" 38 #include "tablelayouter.hxx" 39 #include "svx/svdotable.hxx" 40 #include "editeng/borderline.hxx" 41 #include "editeng/boxitem.hxx" 42 #include "svx/svdmodel.hxx" 43 #include "svx/svdstr.hrc" 44 #include "svx/svdglob.hxx" 45 46 using ::rtl::OUString; 47 using ::com::sun::star::awt::XLayoutConstrains; 48 using namespace ::com::sun::star::uno; 49 using namespace ::com::sun::star::table; 50 using namespace ::com::sun::star::lang; 51 using namespace ::com::sun::star::container; 52 using namespace ::com::sun::star::beans; 53 using namespace ::com::sun::star::table; 54 using namespace ::com::sun::star::text; 55 56 // ----------------------------------------------------------------------------- 57 58 namespace sdr { namespace table { 59 60 // ----------------------------------------------------------------------------- 61 62 static SvxBorderLine gEmptyBorder; 63 64 // ----------------------------------------------------------------------------- 65 66 TableLayouter::TableLayouter( const TableModelRef& xTableModel ) 67 : mxTable( xTableModel ) 68 , meWritingMode( WritingMode_LR_TB ) 69 , msSize( RTL_CONSTASCII_USTRINGPARAM( "Size" ) ) 70 { 71 } 72 73 // ----------------------------------------------------------------------------- 74 75 TableLayouter::~TableLayouter() 76 { 77 ClearBorderLayout(); 78 } 79 80 // ----------------------------------------------------------------------------- 81 82 basegfx::B2ITuple TableLayouter::getCellSize( const CellPos& rPos ) const 83 { 84 sal_Int32 width = 0; 85 sal_Int32 height = 0; 86 87 try 88 { 89 CellRef xCell( getCell( rPos ) ); 90 if( xCell.is() && !xCell->isMerged() ) 91 { 92 CellPos aPos( rPos ); 93 94 sal_Int32 nRowCount = getRowCount(); 95 sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), (sal_Int32)1 ); 96 while( nRowSpan && (aPos.mnRow < nRowCount) ) 97 { 98 if( ((sal_Int32)maRows.size()) <= aPos.mnRow ) 99 break; 100 101 height += maRows[aPos.mnRow++].mnSize; 102 nRowSpan--; 103 } 104 105 sal_Int32 nColCount = getColumnCount(); 106 sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), (sal_Int32)1 ); 107 while( nColSpan && (aPos.mnCol < nColCount ) ) 108 { 109 if( ((sal_Int32)maColumns.size()) <= aPos.mnCol ) 110 break; 111 112 width += maColumns[aPos.mnCol++].mnSize; 113 nColSpan--; 114 } 115 } 116 } 117 catch( Exception& ) 118 { 119 DBG_ERROR( "TableLayouter::getCellSize(), exception caught!" ); 120 } 121 122 return basegfx::B2ITuple( width, height ); 123 } 124 125 // ----------------------------------------------------------------------------- 126 127 bool TableLayouter::getCellArea( const CellPos& rPos, basegfx::B2IRectangle& rArea ) const 128 { 129 try 130 { 131 CellRef xCell( getCell( rPos ) ); 132 if( xCell.is() && !xCell->isMerged() && isValid(rPos) ) 133 { 134 const basegfx::B2ITuple aCellSize( getCellSize( rPos ) ); 135 136 if( (rPos.mnCol < ((sal_Int32)maColumns.size()) && (rPos.mnRow < ((sal_Int32)maRows.size()) ) ) ) 137 { 138 const sal_Int32 x = maColumns[rPos.mnCol].mnPos; 139 const sal_Int32 y = maRows[rPos.mnRow].mnPos; 140 141 rArea = basegfx::B2IRectangle( x, y, x + aCellSize.getX(), y + aCellSize.getY() ); 142 return true; 143 } 144 } 145 } 146 catch( Exception& ) 147 { 148 DBG_ERROR( "TableLayouter::getCellSize(), exception caught!" ); 149 } 150 return false; 151 } 152 153 // ----------------------------------------------------------------------------- 154 155 sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) 156 { 157 if( isValidRow(nRow) ) 158 return maRows[nRow].mnSize; 159 else 160 return 0; 161 } 162 163 // ----------------------------------------------------------------------------- 164 165 void TableLayouter::setRowHeight( sal_Int32 nRow, sal_Int32 nHeight ) 166 { 167 if( isValidRow(nRow) ) 168 { 169 maRows[nRow].mnSize = nHeight; 170 } 171 else 172 { 173 DBG_ERROR( "TableLayouter::setRowHeight(), row out of range!" ); 174 } 175 } 176 177 // ----------------------------------------------------------------------------- 178 179 sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) 180 { 181 if( isValidColumn(nColumn) ) 182 return maColumns[nColumn].mnSize; 183 else 184 return 0; 185 } 186 187 // ----------------------------------------------------------------------------- 188 189 void TableLayouter::setColumnWidth( sal_Int32 nColumn, sal_Int32 nWidth ) 190 { 191 if( isValidColumn(nColumn) ) 192 maColumns[nColumn].mnSize = nWidth; 193 else 194 DBG_ERROR( "TableLayouter::setColumnWidth(), column out of range!" ); 195 } 196 197 // ----------------------------------------------------------------------------- 198 199 bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const 200 { 201 const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders; 202 203 if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) && 204 (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) ) 205 { 206 return rMap[nEdgeX][nEdgeY] != 0; 207 } 208 else 209 { 210 OSL_ENSURE( false, "sdr::table::TableLayouter::getBorderLine(), invalid edge!" ); 211 } 212 213 return false; 214 } 215 216 // ----------------------------------------------------------------------------- 217 218 /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */ 219 SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const 220 { 221 SvxBorderLine* pLine = 0; 222 223 const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders; 224 225 if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) && 226 (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) ) 227 { 228 pLine = rMap[nEdgeX][nEdgeY]; 229 if( pLine == &gEmptyBorder ) 230 pLine = 0; 231 } 232 else 233 { 234 OSL_ENSURE( false, "sdr::table::TableLayouter::getBorderLine(), invalid edge!" ); 235 } 236 237 return pLine; 238 } 239 240 // ----------------------------------------------------------------------------- 241 242 sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ ) 243 { 244 sal_Int32 nRet = 0; 245 if( (nEdgeY >= 0) && (nEdgeY <= getRowCount() ) ) 246 nRet = maRows[std::min((sal_Int32)nEdgeY,getRowCount()-1)].mnPos; 247 248 if( nEdgeY == getRowCount() ) 249 nRet += maRows[nEdgeY - 1].mnSize; 250 251 if( pnMin ) 252 { 253 if( (nEdgeY > 0) && (nEdgeY <= getRowCount() ) ) 254 { 255 *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo 256 } 257 else 258 { 259 *pnMin = nRet; 260 } 261 } 262 263 if( pnMax ) 264 { 265 *pnMax = 0x0fffffff; 266 } 267 return nRet; 268 } 269 270 // ----------------------------------------------------------------------------- 271 272 sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ ) 273 { 274 sal_Int32 nRet = 0; 275 276 const sal_Int32 nColCount = getColumnCount(); 277 if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) ) 278 nRet = maColumns[std::min((sal_Int32)nEdgeX,nColCount-1)].mnPos; 279 280 const bool bRTL = meWritingMode == WritingMode_RL_TB; 281 if( bRTL ) 282 { 283 if( (nEdgeX >= 0) && (nEdgeX < nColCount) ) 284 nRet += maColumns[nEdgeX].mnSize; 285 } 286 else 287 { 288 if( nEdgeX == getColumnCount() ) 289 nRet += maColumns[nEdgeX - 1].mnSize; 290 } 291 292 if( pnMin ) 293 { 294 *pnMin = nRet; 295 if( bRTL ) 296 { 297 if( nEdgeX < nColCount ) 298 *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX); 299 } 300 else 301 { 302 if( (nEdgeX > 0) && (nEdgeX <= nColCount ) ) 303 *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 ); 304 } 305 } 306 307 if( pnMax ) 308 { 309 *pnMax = 0x0fffffff; // todo 310 if( bRTL ) 311 { 312 if( nEdgeX > 0 ) 313 *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 ); 314 } 315 else 316 { 317 if( (nEdgeX >= 0) && (nEdgeX < nColCount ) ) 318 *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX ); 319 } 320 } 321 322 return nRet; 323 } 324 325 // ----------------------------------------------------------------------------- 326 327 static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning ) 328 { 329 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY ); 330 if( xCell.is() && !xCell->isMerged() ) 331 { 332 const sal_Int32 nRight = xCell->getColumnSpan() + nCellX; 333 const sal_Int32 nBottom = xCell->getRowSpan() + nCellY; 334 if( (nMergedX < nRight) && (nMergedY < nBottom) ) 335 return true; 336 337 bRunning = false; 338 } 339 return false; 340 } 341 342 /** returns true if the cell(nMergedX,nMergedY) is merged with other cells. 343 the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge. 344 */ 345 bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY ) 346 { 347 rOriginX = nMergedX; 348 rOriginY = nMergedY; 349 350 if( xTable.is() ) try 351 { 352 // check if this cell already the origin or not merged at all 353 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW ); 354 if( !xCell.is() || !xCell->isMerged() ) 355 return true; 356 357 bool bCheckVert = true; 358 bool bCheckHorz = true; 359 360 sal_Int32 nMinCol = 0; 361 sal_Int32 nMinRow = 0; 362 363 sal_Int32 nStep = 1, i; 364 365 sal_Int32 nRow, nCol; 366 do 367 { 368 if( bCheckVert ) 369 { 370 nRow = nMergedY - nStep; 371 if( nRow >= nMinRow ) 372 { 373 nCol = nMergedX; 374 for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- ) 375 { 376 if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) ) 377 { 378 rOriginX = nCol; rOriginY = nRow; 379 return true; 380 } 381 382 if( !bCheckVert ) 383 { 384 if( nCol == nMergedX ) 385 { 386 nMinRow = nRow+1; 387 } 388 else 389 { 390 bCheckVert = true; 391 } 392 break; 393 } 394 } 395 } 396 else 397 { 398 bCheckVert = false; 399 } 400 } 401 402 if( bCheckHorz ) 403 { 404 nCol = nMergedX - nStep; 405 if( nCol >= nMinCol ) 406 { 407 nRow = nMergedY; 408 for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- ) 409 { 410 if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) ) 411 { 412 rOriginX = nCol; rOriginY = nRow; 413 return true; 414 } 415 416 if( !bCheckHorz ) 417 { 418 if( nRow == nMergedY ) 419 { 420 nMinCol = nCol+1; 421 } 422 else 423 { 424 bCheckHorz = true; 425 } 426 break; 427 } 428 } 429 } 430 else 431 { 432 bCheckHorz = false; 433 } 434 } 435 nStep++; 436 } 437 while( bCheckVert || bCheckHorz ); 438 } 439 catch( Exception& ) 440 { 441 DBG_ERROR("sdr::table::TableLayouter::findMergeOrigin(), exception caught!"); 442 } 443 return false; 444 } 445 446 // ----------------------------------------------------------------------------- 447 448 sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn ) 449 { 450 if( isValidColumn( nColumn ) ) 451 { 452 return maColumns[nColumn].mnMinSize; 453 } 454 else 455 { 456 DBG_ERROR( "TableLayouter::getMinimumColumnWidth(), column out of range!" ); 457 return 0; 458 } 459 } 460 461 // ----------------------------------------------------------------------------- 462 463 sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute ) 464 { 465 // break loops after 100 runs to avoid freezing office due to developer error 466 sal_Int32 nSafe = 100; 467 468 const sal_Size nCount = rLayouts.size(); 469 sal_Size nIndex; 470 471 bool bConstrainsBroken = false; 472 473 do 474 { 475 // first enforce minimum size constrains on all entities 476 for( nIndex = 0; nIndex < nCount; ++nIndex ) 477 { 478 Layout& rLayout = rLayouts[nIndex]; 479 if( rLayout.mnSize < rLayout.mnMinSize ) 480 { 481 nDistribute -= rLayout.mnMinSize - rLayout.mnSize; 482 rLayout.mnSize = rLayout.mnMinSize; 483 } 484 } 485 486 // calculate current width 487 // if nDistribute is < 0 (shrinking), entities that are already 488 // at minimum width are not counted 489 sal_Int32 nCurrentWidth = 0; 490 for( nIndex = 0; nIndex < nCount; ++nIndex ) 491 { 492 Layout& rLayout = rLayouts[nIndex]; 493 if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) ) 494 nCurrentWidth += rLayout.mnSize; 495 } 496 497 bConstrainsBroken = false; 498 499 // now distribute over entities 500 if( (nCurrentWidth != 0) && (nDistribute != 0) ) 501 { 502 sal_Int32 nDistributed = nDistribute; 503 for( nIndex = 0; nIndex < nCount; ++nIndex ) 504 { 505 Layout& rLayout = rLayouts[nIndex]; 506 if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) ) 507 { 508 sal_Int32 n; 509 if( nIndex == (nCount-1) ) 510 n = nDistributed; // for last entitie, use up rest 511 else 512 n = (nDistribute * rLayout.mnSize) / nCurrentWidth; // 513 514 nDistributed -= n; 515 rLayout.mnSize += n; 516 517 if( rLayout.mnSize < rLayout.mnMinSize ) 518 bConstrainsBroken = true; 519 } 520 } 521 } 522 } while( bConstrainsBroken && --nSafe ); 523 524 sal_Int32 nSize = 0; 525 for( nIndex = 0; nIndex < nCount; ++nIndex ) 526 nSize += rLayouts[nIndex].mnSize; 527 528 return nSize; 529 } 530 531 // ----------------------------------------------------------------------------- 532 533 typedef std::vector< CellRef > MergeableCellVector; 534 typedef std::vector< MergeableCellVector > MergeVector; 535 typedef std::vector< sal_Int32 > Int32Vector; 536 537 // ----------------------------------------------------------------------------- 538 539 void TableLayouter::LayoutTableWidth( Rectangle& rArea, bool bFit ) 540 { 541 const sal_Int32 nColCount = getColumnCount(); 542 const sal_Int32 nRowCount = getRowCount(); 543 if( nColCount == 0 ) 544 return; 545 546 MergeVector aMergedCells( nColCount ); 547 Int32Vector aOptimalColumns; 548 549 const OUString sOptimalSize( RTL_CONSTASCII_USTRINGPARAM("OptimalSize") ); 550 551 if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount ) 552 maColumns.resize( nColCount ); 553 554 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW ); 555 556 // first calculate current width and initial minimum width per column, 557 // merged cells will be counted later 558 sal_Int32 nCurrentWidth = 0; 559 sal_Int32 nCol = 0, nRow = 0; 560 for( nCol = 0; nCol < nColCount; nCol++ ) 561 { 562 sal_Int32 nMinWidth = 0; 563 564 bool bIsEmpty = true; // check if all cells in this column are merged 565 566 for( nRow = 0; nRow < nRowCount; ++nRow ) 567 { 568 CellRef xCell( getCell( CellPos( nCol, nRow ) ) ); 569 if( xCell.is() && !xCell->isMerged() ) 570 { 571 bIsEmpty = false; 572 573 sal_Int32 nColSpan = xCell->getColumnSpan(); 574 if( nColSpan > 1 ) 575 { 576 // merged cells will be evaluated later 577 aMergedCells[nCol+nColSpan-1].push_back( xCell ); 578 } 579 else 580 { 581 nMinWidth = std::max( nMinWidth, xCell->getMinimumSize().Width ); 582 } 583 } 584 } 585 586 maColumns[nCol].mnMinSize = nMinWidth; 587 588 if( bIsEmpty ) 589 { 590 maColumns[nCol].mnSize = 0; 591 } 592 else 593 { 594 sal_Int32 nColWidth = 0; 595 Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW ); 596 sal_Bool bOptimal = sal_False; 597 xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal; 598 if( bOptimal ) 599 { 600 aOptimalColumns.push_back(nCol); 601 } 602 else 603 { 604 xColSet->getPropertyValue( msSize ) >>= nColWidth; 605 } 606 607 maColumns[nCol].mnSize = nColWidth; 608 609 if( maColumns[nCol].mnSize < nMinWidth ) 610 maColumns[nCol].mnSize = nMinWidth; 611 612 nCurrentWidth += maColumns[nCol].mnSize; 613 } 614 } 615 616 // if we have optimal sized rows, distribute what is given (left) 617 if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getWidth()) ) 618 { 619 sal_Int32 nLeft = rArea.getWidth() - nCurrentWidth; 620 sal_Int32 nDistribute = nLeft / aOptimalColumns.size(); 621 622 Int32Vector::iterator iter( aOptimalColumns.begin() ); 623 while( iter != aOptimalColumns.end() ) 624 { 625 sal_Int32 nOptCol = (*iter++); 626 if( iter == aOptimalColumns.end() ) 627 nDistribute = nLeft; 628 629 maColumns[nOptCol].mnSize += nDistribute; 630 nLeft -= nDistribute; 631 } 632 633 DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" ); 634 } 635 636 // now check if merged cells fit 637 for( nCol = 1; nCol < nColCount; ++nCol ) 638 { 639 bool bChanges = false; 640 MergeableCellVector::iterator iter( aMergedCells[nCol].begin() ); 641 642 const sal_Int32 nOldSize = maColumns[nCol].mnSize; 643 644 while( iter != aMergedCells[nCol].end() ) 645 { 646 CellRef xCell( (*iter++) ); 647 sal_Int32 nMinWidth = xCell->getMinimumSize().Width; 648 649 for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol ) 650 nMinWidth -= maColumns[nMCol].mnSize; 651 652 if( nMinWidth > maColumns[nCol].mnMinSize ) 653 maColumns[nCol].mnMinSize = nMinWidth; 654 655 if( nMinWidth > maColumns[nCol].mnSize ) 656 { 657 maColumns[nCol].mnSize = nMinWidth; 658 bChanges = true; 659 } 660 } 661 662 if( bChanges ) 663 nCurrentWidth += maColumns[nCol].mnSize - nOldSize; 664 } 665 666 // now scale if wanted and needed 667 if( bFit && (nCurrentWidth != rArea.getWidth()) ) 668 distribute( maColumns, rArea.getWidth() - nCurrentWidth ); 669 670 // last step, update left edges 671 sal_Int32 nNewWidth = 0; 672 673 const bool bRTL = meWritingMode == WritingMode_RL_TB; 674 RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL ); 675 while( coliter.next(nCol ) ) 676 { 677 maColumns[nCol].mnPos = nNewWidth; 678 nNewWidth += maColumns[nCol].mnSize; 679 if( bFit ) 680 { 681 Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW ); 682 xColSet->setPropertyValue( msSize, Any( maColumns[nCol].mnSize ) ); 683 } 684 } 685 686 rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) ); 687 updateCells( rArea ); 688 } 689 690 // ----------------------------------------------------------------------------- 691 692 void TableLayouter::LayoutTableHeight( Rectangle& rArea, bool bFit ) 693 { 694 const sal_Int32 nColCount = getColumnCount(); 695 const sal_Int32 nRowCount = getRowCount(); 696 if( nRowCount == 0 ) 697 return; 698 699 Reference< XTableRows > xRows( mxTable->getRows() ); 700 701 MergeVector aMergedCells( nRowCount ); 702 Int32Vector aOptimalRows; 703 704 const OUString sOptimalSize( RTL_CONSTASCII_USTRINGPARAM("OptimalSize") ); 705 706 // first calculate current height and initial minimum size per column, 707 // merged cells will be counted later 708 sal_Int32 nCurrentHeight = 0; 709 sal_Int32 nCol, nRow; 710 for( nRow = 0; nRow < nRowCount; ++nRow ) 711 { 712 sal_Int32 nMinHeight = 0; 713 714 bool bIsEmpty = true; // check if all cells in this row are merged 715 716 for( nCol = 0; nCol < nColCount; ++nCol ) 717 { 718 CellRef xCell( getCell( CellPos( nCol, nRow ) ) ); 719 if( xCell.is() && !xCell->isMerged() ) 720 { 721 bIsEmpty = false; 722 723 sal_Int32 nRowSpan = xCell->getRowSpan(); 724 if( nRowSpan > 1 ) 725 { 726 // merged cells will be evaluated later 727 aMergedCells[nRow+nRowSpan-1].push_back( xCell ); 728 } 729 else 730 { 731 nMinHeight = std::max( nMinHeight, xCell->getMinimumSize().Height ); 732 } 733 } 734 } 735 736 maRows[nRow].mnMinSize = nMinHeight; 737 738 if( bIsEmpty ) 739 { 740 maRows[nRow].mnSize = 0; 741 } 742 else 743 { 744 sal_Int32 nRowHeight = 0; 745 Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW ); 746 747 sal_Bool bOptimal = sal_False; 748 xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal; 749 if( bOptimal ) 750 { 751 aOptimalRows.push_back( nRow ); 752 } 753 else 754 { 755 xRowSet->getPropertyValue( msSize ) >>= nRowHeight; 756 } 757 758 maRows[nRow].mnSize = nRowHeight; 759 760 if( maRows[nRow].mnSize < nMinHeight ) 761 maRows[nRow].mnSize = nMinHeight; 762 763 nCurrentHeight += maRows[nRow].mnSize; 764 } 765 } 766 767 // if we have optimal sized rows, distribute what is given (left) 768 if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getHeight()) ) 769 { 770 sal_Int32 nLeft = rArea.getHeight() - nCurrentHeight; 771 sal_Int32 nDistribute = nLeft / aOptimalRows.size(); 772 773 Int32Vector::iterator iter( aOptimalRows.begin() ); 774 while( iter != aOptimalRows.end() ) 775 { 776 sal_Int32 nOptRow = (*iter++); 777 if( iter == aOptimalRows.end() ) 778 nDistribute = nLeft; 779 780 maRows[nOptRow].mnSize += nDistribute; 781 nLeft -= nDistribute; 782 783 } 784 785 DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" ); 786 } 787 788 // now check if merged cells fit 789 for( nRow = 1; nRow < nRowCount; ++nRow ) 790 { 791 bool bChanges = false; 792 sal_Int32 nOldSize = maRows[nRow].mnSize; 793 794 MergeableCellVector::iterator iter( aMergedCells[nRow].begin() ); 795 while( iter != aMergedCells[nRow].end() ) 796 { 797 CellRef xCell( (*iter++) ); 798 sal_Int32 nMinHeight = xCell->getMinimumSize().Height; 799 800 for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow ) 801 nMinHeight -= maRows[nMRow].mnSize; 802 803 if( nMinHeight > maRows[nRow].mnMinSize ) 804 maRows[nRow].mnMinSize = nMinHeight; 805 806 if( nMinHeight > maRows[nRow].mnSize ) 807 { 808 maRows[nRow].mnSize = nMinHeight; 809 bChanges = true; 810 } 811 } 812 if( bChanges ) 813 nCurrentHeight += maRows[nRow].mnSize - nOldSize; 814 } 815 816 // now scale if wanted and needed 817 if( bFit && nCurrentHeight != rArea.getHeight() ) 818 distribute( maRows, rArea.getHeight() - nCurrentHeight ); 819 820 // last step, update left edges 821 sal_Int32 nNewHeight = 0; 822 for( nRow = 0; nRow < nRowCount; ++nRow ) 823 { 824 maRows[nRow].mnPos = nNewHeight; 825 nNewHeight += maRows[nRow].mnSize; 826 827 if( bFit ) 828 { 829 Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW ); 830 xRowSet->setPropertyValue( msSize, Any( maRows[nRow].mnSize ) ); 831 } 832 } 833 834 rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) ); 835 updateCells( rArea ); 836 } 837 838 // ----------------------------------------------------------------------------- 839 840 /** try to fit the table into the given rectangle. 841 If the rectangle is to small, it will be grown to fit the table. */ 842 void TableLayouter::LayoutTable( Rectangle& rRectangle, bool bFitWidth, bool bFitHeight ) 843 { 844 if( !mxTable.is() ) 845 return; 846 847 const sal_Int32 nRowCount = mxTable->getRowCount(); 848 const sal_Int32 nColCount = mxTable->getColumnCount(); 849 850 if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) ) 851 { 852 if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount ) 853 maRows.resize( nRowCount ); 854 855 Reference< XTableRows > xRows( mxTable->getRows() ); 856 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ ) 857 maRows[nRow].clear(); 858 859 if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount ) 860 maColumns.resize( nColCount ); 861 862 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) 863 maColumns[nCol].clear(); 864 } 865 866 LayoutTableWidth( rRectangle, bFitWidth ); 867 LayoutTableHeight( rRectangle, bFitHeight ); 868 UpdateBorderLayout(); 869 } 870 871 // ----------------------------------------------------------------------------- 872 873 void TableLayouter::updateCells( Rectangle& rRectangle ) 874 { 875 const sal_Int32 nColCount = getColumnCount(); 876 const sal_Int32 nRowCount = getRowCount(); 877 878 CellPos aPos; 879 for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ ) 880 { 881 for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ ) 882 { 883 CellRef xCell( getCell( aPos ) ); 884 if( xCell.is() ) 885 { 886 basegfx::B2IRectangle aCellArea; 887 getCellArea( aPos, aCellArea ); 888 889 Rectangle aCellRect; 890 aCellRect.nLeft = aCellArea.getMinX(); 891 aCellRect.nRight = aCellArea.getMaxX(); 892 aCellRect.nTop = aCellArea.getMinY(); 893 aCellRect.nBottom = aCellArea.getMaxY(); 894 aCellRect.Move( rRectangle.nLeft, rRectangle.nTop ); 895 xCell->setCellRect( aCellRect ); 896 } 897 } 898 } 899 } 900 901 // ----------------------------------------------------------------------------- 902 903 CellRef TableLayouter::getCell( const CellPos& rPos ) const 904 { 905 CellRef xCell; 906 if( mxTable.is() ) try 907 { 908 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) ); 909 } 910 catch( Exception& ) 911 { 912 DBG_ERROR( "sdr::table::TableLayouter::getCell(), exception caught!" ); 913 } 914 return xCell; 915 } 916 917 // ----------------------------------------------------------------------------- 918 919 bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther ) 920 { 921 if (!pThis || ((pThis == &gEmptyBorder) && (pOther != 0))) 922 return false; 923 if (!pOther || (pOther == &gEmptyBorder)) 924 return true; 925 926 sal_uInt16 nThisSize = pThis->GetOutWidth() + pThis->GetDistance() + pThis->GetInWidth(); 927 sal_uInt16 nOtherSize = pOther->GetOutWidth() + pOther->GetDistance() + pOther->GetInWidth(); 928 929 if (nThisSize > nOtherSize) 930 return true; 931 932 else if (nThisSize < nOtherSize) 933 { 934 return false; 935 } 936 else 937 { 938 if ( pOther->GetInWidth() && !pThis->GetInWidth() ) 939 { 940 return true; 941 } 942 else if ( pThis->GetInWidth() && !pOther->GetInWidth() ) 943 { 944 return false; 945 } 946 else 947 { 948 return true; //! ??? 949 } 950 } 951 } 952 953 // ----------------------------------------------------------------------------- 954 955 void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine ) 956 { 957 if( pLine == 0 ) 958 pLine = &gEmptyBorder; 959 960 SvxBorderLine *pOld = bHorizontal ? maHorizontalBorders[nCol][nRow] : maVerticalBorders[nCol][nRow]; 961 962 if( HasPriority( pLine, pOld ) ) 963 { 964 if( (pOld != 0) && (pOld != &gEmptyBorder) ) 965 delete pOld; 966 967 SvxBorderLine* pNew = ( pLine != &gEmptyBorder ) ? new SvxBorderLine(*pLine) : &gEmptyBorder; 968 969 if( bHorizontal ) 970 maHorizontalBorders[nCol][nRow] = pNew; 971 else 972 maVerticalBorders[nCol][nRow] = pNew; 973 } 974 } 975 976 // ----------------------------------------------------------------------------- 977 978 void TableLayouter::ClearBorderLayout() 979 { 980 ClearBorderLayout(maHorizontalBorders); 981 ClearBorderLayout(maVerticalBorders); 982 } 983 984 // ----------------------------------------------------------------------------- 985 986 void TableLayouter::ClearBorderLayout(BorderLineMap& rMap) 987 { 988 const sal_Int32 nColCount = rMap.size(); 989 990 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) 991 { 992 const sal_Int32 nRowCount = rMap[nCol].size(); 993 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ ) 994 { 995 SvxBorderLine* pLine = rMap[nCol][nRow]; 996 if( pLine ) 997 { 998 if( pLine != &gEmptyBorder ) 999 delete pLine; 1000 1001 rMap[nCol][nRow] = 0; 1002 } 1003 } 1004 } 1005 } 1006 1007 // ----------------------------------------------------------------------------- 1008 1009 void TableLayouter::ResizeBorderLayout() 1010 { 1011 ClearBorderLayout(); 1012 ResizeBorderLayout(maHorizontalBorders); 1013 ResizeBorderLayout(maVerticalBorders); 1014 } 1015 1016 // ----------------------------------------------------------------------------- 1017 1018 void TableLayouter::ResizeBorderLayout( BorderLineMap& rMap ) 1019 { 1020 const sal_Int32 nColCount = getColumnCount() + 1; 1021 const sal_Int32 nRowCount = getRowCount() + 1; 1022 1023 if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount ) 1024 rMap.resize( nColCount ); 1025 1026 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) 1027 { 1028 if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount ) 1029 rMap[nCol].resize( nRowCount ); 1030 } 1031 } 1032 1033 // ----------------------------------------------------------------------------- 1034 1035 void TableLayouter::UpdateBorderLayout() 1036 { 1037 // make sure old border layout is cleared and border maps have correct size 1038 ResizeBorderLayout(); 1039 1040 const sal_Int32 nColCount = getColumnCount(); 1041 const sal_Int32 nRowCount = getRowCount(); 1042 1043 CellPos aPos; 1044 for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ ) 1045 { 1046 for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ ) 1047 { 1048 CellRef xCell( getCell( aPos ) ); 1049 if( !xCell.is() || xCell->isMerged() ) 1050 continue; 1051 1052 const SvxBoxItem* pThisAttr = (const SvxBoxItem*)xCell->GetItemSet().GetItem( SDRATTR_TABLE_BORDER ); 1053 OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?"); 1054 1055 if( !pThisAttr ) 1056 continue; 1057 1058 const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow; 1059 const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol; 1060 1061 for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ ) 1062 { 1063 SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() ); 1064 SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() ); 1065 } 1066 1067 for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ ) 1068 { 1069 SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() ); 1070 SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() ); 1071 } 1072 } 1073 } 1074 } 1075 1076 // ----------------------------------------------------------------------------- 1077 /* 1078 void TableLayouter::SetLayoutToModel() 1079 { 1080 const sal_Int32 nRowCount = getRowCount(); 1081 const sal_Int32 nColCount = getColumnCount(); 1082 1083 try 1084 { 1085 sal_Int32 nOldSize = 0; 1086 1087 Reference< XIndexAccess > xRows( mxTable->getRows(), UNO_QUERY_THROW ); 1088 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ ) 1089 { 1090 Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW ); 1091 xRowSet->getPropertyValue( msSize ) >>= nOldSize; 1092 if( maRows[nRow].mnSize != nOldSize ) 1093 xRowSet->setPropertyValue( msSize, Any( maRows[nRow].mnSize ) ); 1094 } 1095 1096 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ ) 1097 { 1098 Reference< XPropertySet > xColSet( getColumnByIndex( nCol ), UNO_QUERY_THROW ); 1099 xColSet->getPropertyValue( msSize ) >>= nOldSize; 1100 if( maColumns[nCol].mnSize != nOldSize ) 1101 xColSet->setPropertyValue( msSize, Any( maColumns[nCol].mnSize ) ); 1102 } 1103 } 1104 catch( Exception& ) 1105 { 1106 DBG_ERROR("sdr::table::TableLayouter::SetLayoutToModel(), exception caught!"); 1107 } 1108 } 1109 */ 1110 // ----------------------------------------------------------------------------- 1111 1112 void TableLayouter::DistributeColumns( ::Rectangle& rArea, sal_Int32 nFirstCol, sal_Int32 nLastCol ) 1113 { 1114 if( mxTable.is() ) try 1115 { 1116 const sal_Int32 nColCount = getColumnCount(); 1117 1118 if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) ) 1119 return; 1120 1121 sal_Int32 nAllWidth = 0; 1122 for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol ) 1123 nAllWidth += getColumnWidth(nCol); 1124 1125 sal_Int32 nWidth = nAllWidth / (nLastCol-nFirstCol+1); 1126 1127 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW ); 1128 1129 for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol ) 1130 { 1131 if( nCol == nLastCol ) 1132 nWidth = nAllWidth; // last column get round errors 1133 1134 Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW ); 1135 xColSet->setPropertyValue( msSize, Any( nWidth ) ); 1136 1137 nAllWidth -= nWidth; 1138 } 1139 1140 LayoutTable( rArea, true, false ); 1141 } 1142 catch( Exception& e ) 1143 { 1144 (void)e; 1145 DBG_ERROR("sdr::table::TableLayouter::DistributeColumns(), exception caught!"); 1146 } 1147 } 1148 1149 // ----------------------------------------------------------------------------- 1150 1151 void TableLayouter::DistributeRows( ::Rectangle& rArea, sal_Int32 nFirstRow, sal_Int32 nLastRow ) 1152 { 1153 if( mxTable.is() ) try 1154 { 1155 const sal_Int32 nRowCount = mxTable->getRowCount(); 1156 1157 if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) ) 1158 return; 1159 1160 sal_Int32 nAllHeight = 0; 1161 sal_Int32 nMinHeight = 0; 1162 1163 for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow ) 1164 { 1165 nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight ); 1166 nAllHeight += maRows[nRow].mnSize; 1167 } 1168 1169 const sal_Int32 nRows = (nLastRow-nFirstRow+1); 1170 sal_Int32 nHeight = nAllHeight / nRows; 1171 1172 if( nHeight < nMinHeight ) 1173 { 1174 sal_Int32 nNeededHeight = nRows * nMinHeight; 1175 rArea.nBottom += nNeededHeight - nAllHeight; 1176 nHeight = nMinHeight; 1177 nAllHeight = nRows * nMinHeight; 1178 } 1179 1180 Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW ); 1181 for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow ) 1182 { 1183 if( nRow == nLastRow ) 1184 nHeight = nAllHeight; // last row get round errors 1185 1186 Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW ); 1187 xRowSet->setPropertyValue( msSize, Any( nHeight ) ); 1188 1189 nAllHeight -= nHeight; 1190 } 1191 1192 LayoutTable( rArea, false, true ); 1193 } 1194 catch( Exception& e ) 1195 { 1196 (void)e; 1197 DBG_ERROR("sdr::table::TableLayouter::DistributeRows(), exception caught!"); 1198 } 1199 } 1200 1201 // ----------------------------------------------------------------------------- 1202 1203 void TableLayouter::SetWritingMode( com::sun::star::text::WritingMode eWritingMode ) 1204 { 1205 meWritingMode = eWritingMode; 1206 } 1207 1208 // ----------------------------------------------------------------------------- 1209 1210 sal_Int32 TableLayouter::getColumnStart( sal_Int32 nColumn ) const 1211 { 1212 if( isValidColumn(nColumn) ) 1213 return maColumns[nColumn].mnPos; 1214 else 1215 return 0; 1216 } 1217 1218 // ----------------------------------------------------------------------------- 1219 1220 sal_Int32 TableLayouter::getRowStart( sal_Int32 nRow ) const 1221 { 1222 if( isValidRow(nRow) ) 1223 return maRows[nRow].mnPos; 1224 else 1225 return 0; 1226 } 1227 1228 // ----------------------------------------------------------------------------- 1229 1230 /* 1231 sal_Int32 TableLayouter::detectInsertedOrRemovedRows() 1232 { 1233 sal_Int32 nHeightChange = 0; 1234 1235 try 1236 { 1237 Reference< XIndexAccess > xRows( mxTable->getRows(), UNO_QUERY_THROW ); 1238 std::vector< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > >::iterator oldIter( mxRows.begin() ); 1239 sal_Int32 nCount = xRows->getCount(); 1240 for( sal_Int32 nRow = 0; nRow < nCount; nRow++ ) 1241 { 1242 Reference< XInterface > xRow( xRows->getByIndex(nRow), UNO_QUERY ); 1243 1244 std::vector< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > >::iterator searchIter = mxRows.end(); 1245 if( oldIter != mxRows.end() ) 1246 searchIter = std::find( oldIter,mxRows.end(), xRow ); 1247 1248 if( searchIter == mxRows.end() ) 1249 { 1250 // new row 1251 Reference< XPropertySet > xSet( xRow, UNO_QUERY_THROW ); 1252 sal_Int32 nSize = 0; 1253 xSet->getPropertyValue( msSize ) >>= nSize; 1254 nHeightChange += nSize; 1255 } 1256 else if( searchIter == oldIter ) 1257 { 1258 // no change 1259 oldIter++; 1260 } 1261 else 1262 { 1263 // rows removed 1264 do 1265 { 1266 Reference< XPropertySet > xSet( (*oldIter), UNO_QUERY_THROW ); 1267 sal_Int32 nSize = 0; 1268 xSet->getPropertyValue( msSize ) >>= nSize; 1269 nHeightChange -= nSize; 1270 } 1271 while( oldIter++ != searchIter ); 1272 } 1273 } 1274 1275 while( oldIter != mxRows.end() ) 1276 { 1277 // rows removed 1278 Reference< XPropertySet > xSet( (*oldIter++), UNO_QUERY_THROW ); 1279 sal_Int32 nSize = 0; 1280 xSet->getPropertyValue( msSize ) >>= nSize; 1281 nHeightChange -= nSize; 1282 } 1283 } 1284 catch( Exception& e ) 1285 { 1286 (void)e; 1287 DBG_ERROR("svx::TableLayouter::detectInsertedOrRemovedRows(), exception caught!"); 1288 } 1289 1290 return nHeightChange; 1291 } 1292 */ 1293 1294 // ----------------------------------------------------------------------------- 1295 1296 } } 1297