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