bump product version to 5.0.4.1
[LibreOffice.git] / svx / source / table / tablelayouter.cxx
blob0793e77d524a9c6999c8fe9f00852c0feadfabc3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include <com/sun/star/table/XMergeableCell.hpp>
22 #include <com/sun/star/awt/XLayoutConstrains.hpp>
24 #include <tools/gen.hxx>
26 #include "cell.hxx"
27 #include "cellrange.hxx"
28 #include "tablemodel.hxx"
29 #include "tablerow.hxx"
30 #include "tablerows.hxx"
31 #include "tablecolumn.hxx"
32 #include "tablecolumns.hxx"
33 #include "tablelayouter.hxx"
34 #include "svx/svdotable.hxx"
35 #include "editeng/borderline.hxx"
36 #include "editeng/boxitem.hxx"
37 #include "svx/svdmodel.hxx"
38 #include "svx/svdstr.hrc"
39 #include "svdglob.hxx"
41 using ::editeng::SvxBorderLine;
42 using ::com::sun::star::awt::XLayoutConstrains;
43 using namespace ::com::sun::star::uno;
44 using namespace ::com::sun::star::lang;
45 using namespace ::com::sun::star::container;
46 using namespace ::com::sun::star::beans;
47 using namespace ::com::sun::star::table;
48 using namespace ::com::sun::star::text;
52 namespace sdr { namespace table {
56 static SvxBorderLine gEmptyBorder;
60 TableLayouter::TableLayouter( const TableModelRef& xTableModel )
61 : mxTable( xTableModel )
62 , msSize( "Size" )
68 TableLayouter::~TableLayouter()
70 ClearBorderLayout();
75 basegfx::B2ITuple TableLayouter::getCellSize( const CellRef& xCell, const CellPos& rPos ) const
77 sal_Int32 width = 0;
78 sal_Int32 height = 0;
80 try
82 if( xCell.is() && !xCell->isMerged() )
84 CellPos aPos( rPos );
86 sal_Int32 nRowCount = getRowCount();
87 sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), (sal_Int32)1 );
88 while( nRowSpan && (aPos.mnRow < nRowCount) )
90 if( ((sal_Int32)maRows.size()) <= aPos.mnRow )
91 break;
93 height += maRows[aPos.mnRow++].mnSize;
94 nRowSpan--;
97 sal_Int32 nColCount = getColumnCount();
98 sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), (sal_Int32)1 );
99 while( nColSpan && (aPos.mnCol < nColCount ) )
101 if( ((sal_Int32)maColumns.size()) <= aPos.mnCol )
102 break;
104 width += maColumns[aPos.mnCol++].mnSize;
105 nColSpan--;
109 catch( Exception& )
111 OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
114 return basegfx::B2ITuple( width, height );
119 bool TableLayouter::getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const
123 if( xCell.is() && !xCell->isMerged() && isValid(rPos) )
125 const basegfx::B2ITuple aCellSize( getCellSize( xCell, rPos ) );
126 const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
128 if( (rPos.mnCol < ((sal_Int32)maColumns.size()) && (rPos.mnRow < ((sal_Int32)maRows.size()) ) ) )
130 const sal_Int32 y = maRows[rPos.mnRow].mnPos;
132 if(bRTL)
134 ///For RTL Table Calculate the Right End of cell instead of Left
135 const sal_Int32 x = maColumns[rPos.mnCol].mnPos + maColumns[rPos.mnCol].mnSize;
136 rArea = basegfx::B2IRectangle( x-aCellSize.getX(), y, x, y + aCellSize.getY() );
138 else
140 const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
141 rArea = basegfx::B2IRectangle( x, y, x + aCellSize.getX(), y + aCellSize.getY() );
143 return true;
147 catch( Exception& )
149 OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
151 return false;
155 sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const
157 if( isValidRow(nRow) )
158 return maRows[nRow].mnSize;
159 else
160 return 0;
164 sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const
166 if( isValidColumn(nColumn) )
167 return maColumns[nColumn].mnSize;
168 else
169 return 0;
174 bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const
176 const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
178 if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
179 (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
181 return rMap[nEdgeX][nEdgeY] != 0;
183 else
185 OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
188 return false;
193 /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
194 SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const
196 SvxBorderLine* pLine = 0;
198 const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
200 if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
201 (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
203 pLine = rMap[nEdgeX][nEdgeY];
204 if( pLine == &gEmptyBorder )
205 pLine = 0;
207 else
209 OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
212 return pLine;
217 sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
219 sal_Int32 nRet = 0;
220 const sal_Int32 nRowCount = getRowCount();
221 if( (nEdgeY >= 0) && (nEdgeY <= nRowCount ) )
222 nRet = maRows[std::min((sal_Int32)nEdgeY,nRowCount-1)].mnPos;
224 if( nEdgeY == nRowCount )
225 nRet += maRows[nEdgeY - 1].mnSize;
227 if( pnMin )
229 if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) )
231 *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo
233 else
235 *pnMin = nRet;
239 if( pnMax )
241 *pnMax = 0x0fffffff;
243 return nRet;
248 sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
250 sal_Int32 nRet = 0;
252 const sal_Int32 nColCount = getColumnCount();
253 if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) )
254 nRet = maColumns[std::min((sal_Int32)nEdgeX,nColCount-1)].mnPos;
256 const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
257 if( bRTL )
259 if( (nEdgeX >= 0) && (nEdgeX < nColCount) )
260 nRet += maColumns[nEdgeX].mnSize;
262 else
264 if( nEdgeX == nColCount )
265 nRet += maColumns[nEdgeX - 1].mnSize;
268 if( pnMin )
270 *pnMin = nRet;
271 if( bRTL )
273 if( nEdgeX < nColCount )
274 *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX);
276 else
278 if( (nEdgeX > 0) && (nEdgeX <= nColCount ) )
279 *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 );
283 if( pnMax )
285 *pnMax = 0x0fffffff; // todo
286 if( bRTL )
288 if( nEdgeX > 0 )
289 *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 );
291 else
293 if( (nEdgeX >= 0) && (nEdgeX < nColCount ) )
294 *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX );
298 return nRet;
303 static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning )
305 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY );
306 if( xCell.is() && !xCell->isMerged() )
308 const sal_Int32 nRight = xCell->getColumnSpan() + nCellX;
309 const sal_Int32 nBottom = xCell->getRowSpan() + nCellY;
310 if( (nMergedX < nRight) && (nMergedY < nBottom) )
311 return true;
313 bRunning = false;
315 return false;
318 /** returns true if the cell(nMergedX,nMergedY) is merged with other cells.
319 the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge.
321 bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY )
323 rOriginX = nMergedX;
324 rOriginY = nMergedY;
326 if( xTable.is() ) try
328 // check if this cell already the origin or not merged at all
329 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW );
330 if( !xCell.is() || !xCell->isMerged() )
331 return true;
333 bool bCheckVert = true;
334 bool bCheckHorz = true;
336 sal_Int32 nMinCol = 0;
337 sal_Int32 nMinRow = 0;
339 sal_Int32 nStep = 1, i;
341 sal_Int32 nRow, nCol;
344 if( bCheckVert )
346 nRow = nMergedY - nStep;
347 if( nRow >= nMinRow )
349 nCol = nMergedX;
350 for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- )
352 if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) )
354 rOriginX = nCol; rOriginY = nRow;
355 return true;
358 if( !bCheckVert )
360 if( nCol == nMergedX )
362 nMinRow = nRow+1;
364 else
366 bCheckVert = true;
368 break;
372 else
374 bCheckVert = false;
378 if( bCheckHorz )
380 nCol = nMergedX - nStep;
381 if( nCol >= nMinCol )
383 nRow = nMergedY;
384 for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- )
386 if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) )
388 rOriginX = nCol; rOriginY = nRow;
389 return true;
392 if( !bCheckHorz )
394 if( nRow == nMergedY )
396 nMinCol = nCol+1;
398 else
400 bCheckHorz = true;
402 break;
406 else
408 bCheckHorz = false;
411 nStep++;
413 while( bCheckVert || bCheckHorz );
415 catch( Exception& )
417 OSL_FAIL("sdr::table::TableLayouter::findMergeOrigin(), exception caught!");
419 return false;
424 sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn )
426 if( isValidColumn( nColumn ) )
428 return maColumns[nColumn].mnMinSize;
430 else
432 OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
433 return 0;
439 sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute )
441 // break loops after 100 runs to avoid freezing office due to developer error
442 sal_Int32 nSafe = 100;
444 const sal_Size nCount = rLayouts.size();
445 sal_Size nIndex;
447 bool bConstrainsBroken = false;
451 // first enforce minimum size constrains on all entities
452 for( nIndex = 0; nIndex < nCount; ++nIndex )
454 Layout& rLayout = rLayouts[nIndex];
455 if( rLayout.mnSize < rLayout.mnMinSize )
457 nDistribute -= rLayout.mnMinSize - rLayout.mnSize;
458 rLayout.mnSize = rLayout.mnMinSize;
462 // calculate current width
463 // if nDistribute is < 0 (shrinking), entities that are already
464 // at minimum width are not counted
465 sal_Int32 nCurrentWidth = 0;
466 for( nIndex = 0; nIndex < nCount; ++nIndex )
468 Layout& rLayout = rLayouts[nIndex];
469 if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
470 nCurrentWidth += rLayout.mnSize;
473 bConstrainsBroken = false;
475 // now distribute over entities
476 if( (nCurrentWidth != 0) && (nDistribute != 0) )
478 sal_Int32 nDistributed = nDistribute;
479 for( nIndex = 0; nIndex < nCount; ++nIndex )
481 Layout& rLayout = rLayouts[nIndex];
482 if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
484 sal_Int32 n;
485 if( nIndex == (nCount-1) )
486 n = nDistributed; // for last entitie, use up rest
487 else
488 n = (nDistribute * rLayout.mnSize) / nCurrentWidth;
490 nDistributed -= n;
491 rLayout.mnSize += n;
493 if( rLayout.mnSize < rLayout.mnMinSize )
494 bConstrainsBroken = true;
498 } while( bConstrainsBroken && --nSafe );
500 sal_Int32 nSize = 0;
501 for( nIndex = 0; nIndex < nCount; ++nIndex )
502 nSize += rLayouts[nIndex].mnSize;
504 return nSize;
509 typedef std::vector< CellRef > MergeableCellVector;
510 typedef std::vector< MergeableCellVector > MergeVector;
511 typedef std::vector< sal_Int32 > Int32Vector;
515 void TableLayouter::LayoutTableWidth( Rectangle& rArea, bool bFit )
517 const sal_Int32 nColCount = getColumnCount();
518 const sal_Int32 nRowCount = getRowCount();
519 if( nColCount == 0 )
520 return;
522 MergeVector aMergedCells( nColCount );
523 Int32Vector aOptimalColumns;
525 const OUString sOptimalSize("OptimalSize");
527 if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount )
528 maColumns.resize( nColCount );
530 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
532 // first calculate current width and initial minimum width per column,
533 // merged cells will be counted later
534 sal_Int32 nCurrentWidth = 0;
535 sal_Int32 nCol = 0, nRow = 0;
536 for( nCol = 0; nCol < nColCount; nCol++ )
538 sal_Int32 nMinWidth = 0;
540 bool bIsEmpty = true; // check if all cells in this column are merged
542 for( nRow = 0; nRow < nRowCount; ++nRow )
544 CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
545 if( xCell.is() && !xCell->isMerged() )
547 bIsEmpty = false;
549 sal_Int32 nColSpan = xCell->getColumnSpan();
550 if( nColSpan > 1 )
552 // merged cells will be evaluated later
553 aMergedCells[nCol+nColSpan-1].push_back( xCell );
555 else
557 nMinWidth = std::max( nMinWidth, xCell->getMinimumWidth() );
562 maColumns[nCol].mnMinSize = nMinWidth;
564 if( bIsEmpty )
566 maColumns[nCol].mnSize = 0;
568 else
570 sal_Int32 nColWidth = 0;
571 Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
572 bool bOptimal = false;
573 xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
574 if( bOptimal )
576 aOptimalColumns.push_back(nCol);
578 else
580 xColSet->getPropertyValue( msSize ) >>= nColWidth;
583 maColumns[nCol].mnSize = nColWidth;
585 if( maColumns[nCol].mnSize < nMinWidth )
586 maColumns[nCol].mnSize = nMinWidth;
588 nCurrentWidth += maColumns[nCol].mnSize;
592 // if we have optimal sized rows, distribute what is given (left)
593 if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getWidth()) )
595 sal_Int32 nLeft = rArea.getWidth() - nCurrentWidth;
596 sal_Int32 nDistribute = nLeft / aOptimalColumns.size();
598 Int32Vector::iterator iter( aOptimalColumns.begin() );
599 while( iter != aOptimalColumns.end() )
601 sal_Int32 nOptCol = (*iter++);
602 if( iter == aOptimalColumns.end() )
603 nDistribute = nLeft;
605 maColumns[nOptCol].mnSize += nDistribute;
606 nLeft -= nDistribute;
609 DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
612 // now check if merged cells fit
613 for( nCol = 1; nCol < nColCount; ++nCol )
615 bool bChanges = false;
616 MergeableCellVector::iterator iter( aMergedCells[nCol].begin() );
618 const sal_Int32 nOldSize = maColumns[nCol].mnSize;
620 while( iter != aMergedCells[nCol].end() )
622 CellRef xCell( (*iter++) );
623 sal_Int32 nMinWidth = xCell->getMinimumWidth();
625 for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol )
626 nMinWidth -= maColumns[nMCol].mnSize;
628 if( nMinWidth > maColumns[nCol].mnMinSize )
629 maColumns[nCol].mnMinSize = nMinWidth;
631 if( nMinWidth > maColumns[nCol].mnSize )
633 maColumns[nCol].mnSize = nMinWidth;
634 bChanges = true;
638 if( bChanges )
639 nCurrentWidth += maColumns[nCol].mnSize - nOldSize;
642 // now scale if wanted and needed
643 if( bFit && (nCurrentWidth != rArea.getWidth()) )
644 distribute( maColumns, rArea.getWidth() - nCurrentWidth );
646 // last step, update left edges
647 sal_Int32 nNewWidth = 0;
649 const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
650 RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL );
651 while( coliter.next(nCol ) )
653 maColumns[nCol].mnPos = nNewWidth;
654 nNewWidth += maColumns[nCol].mnSize;
655 if( bFit )
657 Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW );
658 xColSet->setPropertyValue( msSize, Any( maColumns[nCol].mnSize ) );
662 rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) );
663 updateCells( rArea );
668 void TableLayouter::LayoutTableHeight( Rectangle& rArea, bool bFit )
670 const sal_Int32 nColCount = getColumnCount();
671 const sal_Int32 nRowCount = getRowCount();
672 if( nRowCount == 0 )
673 return;
675 Reference< XTableRows > xRows( mxTable->getRows() );
677 MergeVector aMergedCells( nRowCount );
678 Int32Vector aOptimalRows;
680 const OUString sOptimalSize("OptimalSize");
682 // first calculate current height and initial minimum size per column,
683 // merged cells will be counted later
684 sal_Int32 nCurrentHeight = 0;
685 sal_Int32 nCol, nRow;
686 for( nRow = 0; nRow < nRowCount; ++nRow )
688 sal_Int32 nMinHeight = 0;
690 bool bIsEmpty = true; // check if all cells in this row are merged
692 for( nCol = 0; nCol < nColCount; ++nCol )
694 CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
695 if( xCell.is() && !xCell->isMerged() )
697 bIsEmpty = false;
699 sal_Int32 nRowSpan = xCell->getRowSpan();
700 if( nRowSpan > 1 )
702 // merged cells will be evaluated later
703 aMergedCells[nRow+nRowSpan-1].push_back( xCell );
705 else
707 nMinHeight = std::max( nMinHeight, xCell->getMinimumHeight() );
712 maRows[nRow].mnMinSize = nMinHeight;
714 if( bIsEmpty )
716 maRows[nRow].mnSize = 0;
718 else
720 sal_Int32 nRowHeight = 0;
721 Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
723 bool bOptimal = false;
724 xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
725 if( bOptimal )
727 aOptimalRows.push_back( nRow );
729 else
731 xRowSet->getPropertyValue( msSize ) >>= nRowHeight;
734 maRows[nRow].mnSize = nRowHeight;
736 if( maRows[nRow].mnSize < nMinHeight )
737 maRows[nRow].mnSize = nMinHeight;
739 nCurrentHeight += maRows[nRow].mnSize;
743 // if we have optimal sized rows, distribute what is given (left)
744 if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getHeight()) )
746 sal_Int32 nLeft = rArea.getHeight() - nCurrentHeight;
747 sal_Int32 nDistribute = nLeft / aOptimalRows.size();
749 Int32Vector::iterator iter( aOptimalRows.begin() );
750 while( iter != aOptimalRows.end() )
752 sal_Int32 nOptRow = (*iter++);
753 if( iter == aOptimalRows.end() )
754 nDistribute = nLeft;
756 maRows[nOptRow].mnSize += nDistribute;
757 nLeft -= nDistribute;
761 DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
764 // now check if merged cells fit
765 for( nRow = 1; nRow < nRowCount; ++nRow )
767 bool bChanges = false;
768 sal_Int32 nOldSize = maRows[nRow].mnSize;
770 MergeableCellVector::iterator iter( aMergedCells[nRow].begin() );
771 while( iter != aMergedCells[nRow].end() )
773 CellRef xCell( (*iter++) );
774 sal_Int32 nMinHeight = xCell->getMinimumHeight();
776 for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow )
777 nMinHeight -= maRows[nMRow].mnSize;
779 if( nMinHeight > maRows[nRow].mnMinSize )
780 maRows[nRow].mnMinSize = nMinHeight;
782 if( nMinHeight > maRows[nRow].mnSize )
784 maRows[nRow].mnSize = nMinHeight;
785 bChanges = true;
788 if( bChanges )
789 nCurrentHeight += maRows[nRow].mnSize - nOldSize;
792 // now scale if wanted and needed
793 if( bFit && nCurrentHeight != rArea.getHeight() )
794 distribute( maRows, rArea.getHeight() - nCurrentHeight );
796 // last step, update left edges
797 sal_Int32 nNewHeight = 0;
798 for( nRow = 0; nRow < nRowCount; ++nRow )
800 maRows[nRow].mnPos = nNewHeight;
801 nNewHeight += maRows[nRow].mnSize;
803 if( bFit )
805 Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
806 xRowSet->setPropertyValue( msSize, Any( maRows[nRow].mnSize ) );
810 rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) );
811 updateCells( rArea );
816 /** try to fit the table into the given rectangle.
817 If the rectangle is to small, it will be grown to fit the table. */
818 void TableLayouter::LayoutTable( Rectangle& rRectangle, bool bFitWidth, bool bFitHeight )
820 if( !mxTable.is() )
821 return;
823 const sal_Int32 nRowCount = mxTable->getRowCount();
824 const sal_Int32 nColCount = mxTable->getColumnCount();
826 if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) )
828 if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount )
829 maRows.resize( nRowCount );
831 Reference< XTableRows > xRows( mxTable->getRows() );
832 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
833 maRows[nRow].clear();
835 if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount )
836 maColumns.resize( nColCount );
838 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
839 maColumns[nCol].clear();
842 LayoutTableWidth( rRectangle, bFitWidth );
843 LayoutTableHeight( rRectangle, bFitHeight );
844 UpdateBorderLayout();
849 void TableLayouter::updateCells( Rectangle& rRectangle )
851 const sal_Int32 nColCount = getColumnCount();
852 const sal_Int32 nRowCount = getRowCount();
854 CellPos aPos;
855 for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
857 for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
859 CellRef xCell( getCell( aPos ) );
860 if( xCell.is() )
862 basegfx::B2IRectangle aCellArea;
863 if( getCellArea( xCell, aPos, aCellArea ) )
865 Rectangle aCellRect;
866 aCellRect.Left() = aCellArea.getMinX();
867 aCellRect.Right() = aCellArea.getMaxX();
868 aCellRect.Top() = aCellArea.getMinY();
869 aCellRect.Bottom() = aCellArea.getMaxY();
870 aCellRect.Move( rRectangle.Left(), rRectangle.Top() );
871 xCell->setCellRect( aCellRect );
880 CellRef TableLayouter::getCell( const CellPos& rPos ) const
882 CellRef xCell;
883 if( mxTable.is() ) try
885 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
887 catch( Exception& )
889 OSL_FAIL( "sdr::table::TableLayouter::getCell(), exception caught!" );
891 return xCell;
896 bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
898 if (!pThis || ((pThis == &gEmptyBorder) && (pOther != 0)))
899 return false;
900 if (!pOther || (pOther == &gEmptyBorder))
901 return true;
903 sal_uInt16 nThisSize = pThis->GetScaledWidth();
904 sal_uInt16 nOtherSize = pOther->GetScaledWidth();
906 if (nThisSize > nOtherSize)
907 return true;
909 else if (nThisSize < nOtherSize)
911 return false;
913 else
915 if ( pOther->GetInWidth() && !pThis->GetInWidth() )
917 return true;
919 else if ( pThis->GetInWidth() && !pOther->GetInWidth() )
921 return false;
923 else
925 return true; //! ???
932 void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
934 if( pLine == 0 )
935 pLine = &gEmptyBorder;
937 SvxBorderLine *pOld = bHorizontal ? maHorizontalBorders[nCol][nRow] : maVerticalBorders[nCol][nRow];
939 if( HasPriority( pLine, pOld ) )
941 if( (pOld != 0) && (pOld != &gEmptyBorder) )
942 delete pOld;
944 SvxBorderLine* pNew = ( pLine != &gEmptyBorder ) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
946 if( bHorizontal )
947 maHorizontalBorders[nCol][nRow] = pNew;
948 else
949 maVerticalBorders[nCol][nRow] = pNew;
955 void TableLayouter::ClearBorderLayout()
957 ClearBorderLayout(maHorizontalBorders);
958 ClearBorderLayout(maVerticalBorders);
963 void TableLayouter::ClearBorderLayout(BorderLineMap& rMap)
965 const sal_Int32 nColCount = rMap.size();
967 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
969 const sal_Int32 nRowCount = rMap[nCol].size();
970 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
972 SvxBorderLine* pLine = rMap[nCol][nRow];
973 if( pLine )
975 if( pLine != &gEmptyBorder )
976 delete pLine;
978 rMap[nCol][nRow] = 0;
986 void TableLayouter::ResizeBorderLayout()
988 ClearBorderLayout();
989 ResizeBorderLayout(maHorizontalBorders);
990 ResizeBorderLayout(maVerticalBorders);
995 void TableLayouter::ResizeBorderLayout( BorderLineMap& rMap )
997 const sal_Int32 nColCount = getColumnCount() + 1;
998 const sal_Int32 nRowCount = getRowCount() + 1;
1000 if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount )
1001 rMap.resize( nColCount );
1003 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
1005 if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount )
1006 rMap[nCol].resize( nRowCount );
1012 void TableLayouter::UpdateBorderLayout()
1014 // make sure old border layout is cleared and border maps have correct size
1015 ResizeBorderLayout();
1017 const sal_Int32 nColCount = getColumnCount();
1018 const sal_Int32 nRowCount = getRowCount();
1020 CellPos aPos;
1021 for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
1023 for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
1025 CellRef xCell( getCell( aPos ) );
1026 if( !xCell.is() )
1027 continue;
1029 const SvxBoxItem* pThisAttr = static_cast<const SvxBoxItem*>(xCell->GetItemSet().GetItem( SDRATTR_TABLE_BORDER ));
1030 OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1032 if( !pThisAttr )
1033 continue;
1035 const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow;
1036 const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol;
1038 for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ )
1040 SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() );
1041 SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() );
1044 for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ )
1046 SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() );
1047 SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() );
1055 void TableLayouter::DistributeColumns( ::Rectangle& rArea, sal_Int32 nFirstCol, sal_Int32 nLastCol )
1057 if( mxTable.is() ) try
1059 const sal_Int32 nColCount = getColumnCount();
1061 if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) )
1062 return;
1064 sal_Int32 nAllWidth = 0;
1065 for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1066 nAllWidth += getColumnWidth(nCol);
1068 sal_Int32 nWidth = nAllWidth / (nLastCol-nFirstCol+1);
1070 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
1072 for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1074 if( nCol == nLastCol )
1075 nWidth = nAllWidth; // last column get round errors
1077 Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
1078 xColSet->setPropertyValue( msSize, Any( nWidth ) );
1080 nAllWidth -= nWidth;
1083 LayoutTable( rArea, true, false );
1085 catch( Exception& e )
1087 (void)e;
1088 OSL_FAIL("sdr::table::TableLayouter::DistributeColumns(), exception caught!");
1094 void TableLayouter::DistributeRows( ::Rectangle& rArea, sal_Int32 nFirstRow, sal_Int32 nLastRow )
1096 if( mxTable.is() ) try
1098 const sal_Int32 nRowCount = mxTable->getRowCount();
1100 if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) )
1101 return;
1103 sal_Int32 nAllHeight = 0;
1104 sal_Int32 nMinHeight = 0;
1106 for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1108 nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight );
1109 nAllHeight += maRows[nRow].mnSize;
1112 const sal_Int32 nRows = (nLastRow-nFirstRow+1);
1113 sal_Int32 nHeight = nAllHeight / nRows;
1115 if( nHeight < nMinHeight )
1117 sal_Int32 nNeededHeight = nRows * nMinHeight;
1118 rArea.Bottom() += nNeededHeight - nAllHeight;
1119 nHeight = nMinHeight;
1120 nAllHeight = nRows * nMinHeight;
1123 Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW );
1124 for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1126 if( nRow == nLastRow )
1127 nHeight = nAllHeight; // last row get round errors
1129 Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
1130 xRowSet->setPropertyValue( msSize, Any( nHeight ) );
1132 nAllHeight -= nHeight;
1135 LayoutTable( rArea, false, true );
1137 catch( Exception& e )
1139 (void)e;
1140 OSL_FAIL("sdr::table::TableLayouter::DistributeRows(), exception caught!");
1146 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */