bump product version to 4.1.6.2
[LibreOffice.git] / svx / source / table / tablelayouter.cxx
blob779ee150e47795cd375b93fff452668d11da21a5
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 "svx/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;
50 // -----------------------------------------------------------------------------
52 namespace sdr { namespace table {
54 // -----------------------------------------------------------------------------
56 static SvxBorderLine gEmptyBorder;
58 // -----------------------------------------------------------------------------
60 TableLayouter::TableLayouter( const TableModelRef& xTableModel )
61 : mxTable( xTableModel )
62 , meWritingMode( WritingMode_LR_TB )
63 , msSize( "Size" )
67 // -----------------------------------------------------------------------------
69 TableLayouter::~TableLayouter()
71 ClearBorderLayout();
74 // -----------------------------------------------------------------------------
76 basegfx::B2ITuple TableLayouter::getCellSize( const CellPos& rPos ) const
78 sal_Int32 width = 0;
79 sal_Int32 height = 0;
81 try
83 CellRef xCell( getCell( rPos ) );
84 if( xCell.is() && !xCell->isMerged() )
86 CellPos aPos( rPos );
88 sal_Int32 nRowCount = getRowCount();
89 sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), (sal_Int32)1 );
90 while( nRowSpan && (aPos.mnRow < nRowCount) )
92 if( ((sal_Int32)maRows.size()) <= aPos.mnRow )
93 break;
95 height += maRows[aPos.mnRow++].mnSize;
96 nRowSpan--;
99 sal_Int32 nColCount = getColumnCount();
100 sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), (sal_Int32)1 );
101 while( nColSpan && (aPos.mnCol < nColCount ) )
103 if( ((sal_Int32)maColumns.size()) <= aPos.mnCol )
104 break;
106 width += maColumns[aPos.mnCol++].mnSize;
107 nColSpan--;
111 catch( Exception& )
113 OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
116 return basegfx::B2ITuple( width, height );
119 // -----------------------------------------------------------------------------
121 bool TableLayouter::getCellArea( const CellPos& rPos, basegfx::B2IRectangle& rArea ) const
125 CellRef xCell( getCell( rPos ) );
126 if( xCell.is() && !xCell->isMerged() && isValid(rPos) )
128 const basegfx::B2ITuple aCellSize( getCellSize( rPos ) );
129 const bool bRTL = meWritingMode == WritingMode_RL_TB;
131 if( (rPos.mnCol < ((sal_Int32)maColumns.size()) && (rPos.mnRow < ((sal_Int32)maRows.size()) ) ) )
133 const sal_Int32 y = maRows[rPos.mnRow].mnPos;
135 if(bRTL)
137 ///For RTL Table Calculate the Right End of cell instead of Left
138 const sal_Int32 x = maColumns[rPos.mnCol].mnPos + maColumns[rPos.mnCol].mnSize;
139 rArea = basegfx::B2IRectangle( x-aCellSize.getX(), y, x, y + aCellSize.getY() );
141 else
143 const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
144 rArea = basegfx::B2IRectangle( x, y, x + aCellSize.getX(), y + aCellSize.getY() );
146 return true;
150 catch( Exception& )
152 OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
154 return false;
157 // -----------------------------------------------------------------------------
158 sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const
160 if( isValidRow(nRow) )
161 return maRows[nRow].mnSize;
162 else
163 return 0;
166 // -----------------------------------------------------------------------------
167 sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const
169 if( isValidColumn(nColumn) )
170 return maColumns[nColumn].mnSize;
171 else
172 return 0;
175 // -----------------------------------------------------------------------------
177 bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const
179 const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
181 if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
182 (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
184 return rMap[nEdgeX][nEdgeY] != 0;
186 else
188 OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
191 return false;
194 // -----------------------------------------------------------------------------
196 /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
197 SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const
199 SvxBorderLine* pLine = 0;
201 const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
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())) )
206 pLine = rMap[nEdgeX][nEdgeY];
207 if( pLine == &gEmptyBorder )
208 pLine = 0;
210 else
212 OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
215 return pLine;
218 // -----------------------------------------------------------------------------
220 sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
222 sal_Int32 nRet = 0;
223 const sal_Int32 nRowCount = getRowCount();
224 if( (nEdgeY >= 0) && (nEdgeY <= nRowCount ) )
225 nRet = maRows[std::min((sal_Int32)nEdgeY,nRowCount-1)].mnPos;
227 if( nEdgeY == nRowCount )
228 nRet += maRows[nEdgeY - 1].mnSize;
230 if( pnMin )
232 if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) )
234 *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo
236 else
238 *pnMin = nRet;
242 if( pnMax )
244 *pnMax = 0x0fffffff;
246 return nRet;
249 // -----------------------------------------------------------------------------
251 sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
253 sal_Int32 nRet = 0;
255 const sal_Int32 nColCount = getColumnCount();
256 if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) )
257 nRet = maColumns[std::min((sal_Int32)nEdgeX,nColCount-1)].mnPos;
259 const bool bRTL = meWritingMode == WritingMode_RL_TB;
260 if( bRTL )
262 if( (nEdgeX >= 0) && (nEdgeX < nColCount) )
263 nRet += maColumns[nEdgeX].mnSize;
265 else
267 if( nEdgeX == nColCount )
268 nRet += maColumns[nEdgeX - 1].mnSize;
271 if( pnMin )
273 *pnMin = nRet;
274 if( bRTL )
276 if( nEdgeX < nColCount )
277 *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX);
279 else
281 if( (nEdgeX > 0) && (nEdgeX <= nColCount ) )
282 *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 );
286 if( pnMax )
288 *pnMax = 0x0fffffff; // todo
289 if( bRTL )
291 if( nEdgeX > 0 )
292 *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 );
294 else
296 if( (nEdgeX >= 0) && (nEdgeX < nColCount ) )
297 *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX );
301 return nRet;
304 // -----------------------------------------------------------------------------
306 static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning )
308 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY );
309 if( xCell.is() && !xCell->isMerged() )
311 const sal_Int32 nRight = xCell->getColumnSpan() + nCellX;
312 const sal_Int32 nBottom = xCell->getRowSpan() + nCellY;
313 if( (nMergedX < nRight) && (nMergedY < nBottom) )
314 return true;
316 bRunning = false;
318 return false;
321 /** returns true if the cell(nMergedX,nMergedY) is merged with other cells.
322 the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge.
324 bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY )
326 rOriginX = nMergedX;
327 rOriginY = nMergedY;
329 if( xTable.is() ) try
331 // check if this cell already the origin or not merged at all
332 Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW );
333 if( !xCell.is() || !xCell->isMerged() )
334 return true;
336 bool bCheckVert = true;
337 bool bCheckHorz = true;
339 sal_Int32 nMinCol = 0;
340 sal_Int32 nMinRow = 0;
342 sal_Int32 nStep = 1, i;
344 sal_Int32 nRow, nCol;
347 if( bCheckVert )
349 nRow = nMergedY - nStep;
350 if( nRow >= nMinRow )
352 nCol = nMergedX;
353 for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- )
355 if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) )
357 rOriginX = nCol; rOriginY = nRow;
358 return true;
361 if( !bCheckVert )
363 if( nCol == nMergedX )
365 nMinRow = nRow+1;
367 else
369 bCheckVert = true;
371 break;
375 else
377 bCheckVert = false;
381 if( bCheckHorz )
383 nCol = nMergedX - nStep;
384 if( nCol >= nMinCol )
386 nRow = nMergedY;
387 for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- )
389 if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) )
391 rOriginX = nCol; rOriginY = nRow;
392 return true;
395 if( !bCheckHorz )
397 if( nRow == nMergedY )
399 nMinCol = nCol+1;
401 else
403 bCheckHorz = true;
405 break;
409 else
411 bCheckHorz = false;
414 nStep++;
416 while( bCheckVert || bCheckHorz );
418 catch( Exception& )
420 OSL_FAIL("sdr::table::TableLayouter::findMergeOrigin(), exception caught!");
422 return false;
425 // -----------------------------------------------------------------------------
427 sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn )
429 if( isValidColumn( nColumn ) )
431 return maColumns[nColumn].mnMinSize;
433 else
435 OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
436 return 0;
440 // -----------------------------------------------------------------------------
442 sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute )
444 // break loops after 100 runs to avoid freezing office due to developer error
445 sal_Int32 nSafe = 100;
447 const sal_Size nCount = rLayouts.size();
448 sal_Size nIndex;
450 bool bConstrainsBroken = false;
454 // first enforce minimum size constrains on all entities
455 for( nIndex = 0; nIndex < nCount; ++nIndex )
457 Layout& rLayout = rLayouts[nIndex];
458 if( rLayout.mnSize < rLayout.mnMinSize )
460 nDistribute -= rLayout.mnMinSize - rLayout.mnSize;
461 rLayout.mnSize = rLayout.mnMinSize;
465 // calculate current width
466 // if nDistribute is < 0 (shrinking), entities that are already
467 // at minimum width are not counted
468 sal_Int32 nCurrentWidth = 0;
469 for( nIndex = 0; nIndex < nCount; ++nIndex )
471 Layout& rLayout = rLayouts[nIndex];
472 if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
473 nCurrentWidth += rLayout.mnSize;
476 bConstrainsBroken = false;
478 // now distribute over entities
479 if( (nCurrentWidth != 0) && (nDistribute != 0) )
481 sal_Int32 nDistributed = nDistribute;
482 for( nIndex = 0; nIndex < nCount; ++nIndex )
484 Layout& rLayout = rLayouts[nIndex];
485 if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
487 sal_Int32 n;
488 if( nIndex == (nCount-1) )
489 n = nDistributed; // for last entitie, use up rest
490 else
491 n = (nDistribute * rLayout.mnSize) / nCurrentWidth; //
493 nDistributed -= n;
494 rLayout.mnSize += n;
496 if( rLayout.mnSize < rLayout.mnMinSize )
497 bConstrainsBroken = true;
501 } while( bConstrainsBroken && --nSafe );
503 sal_Int32 nSize = 0;
504 for( nIndex = 0; nIndex < nCount; ++nIndex )
505 nSize += rLayouts[nIndex].mnSize;
507 return nSize;
510 // -----------------------------------------------------------------------------
512 typedef std::vector< CellRef > MergeableCellVector;
513 typedef std::vector< MergeableCellVector > MergeVector;
514 typedef std::vector< sal_Int32 > Int32Vector;
516 // -----------------------------------------------------------------------------
518 void TableLayouter::LayoutTableWidth( Rectangle& rArea, bool bFit )
520 const sal_Int32 nColCount = getColumnCount();
521 const sal_Int32 nRowCount = getRowCount();
522 if( nColCount == 0 )
523 return;
525 MergeVector aMergedCells( nColCount );
526 Int32Vector aOptimalColumns;
528 const OUString sOptimalSize("OptimalSize");
530 if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount )
531 maColumns.resize( nColCount );
533 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
535 // first calculate current width and initial minimum width per column,
536 // merged cells will be counted later
537 sal_Int32 nCurrentWidth = 0;
538 sal_Int32 nCol = 0, nRow = 0;
539 for( nCol = 0; nCol < nColCount; nCol++ )
541 sal_Int32 nMinWidth = 0;
543 bool bIsEmpty = true; // check if all cells in this column are merged
545 for( nRow = 0; nRow < nRowCount; ++nRow )
547 CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
548 if( xCell.is() && !xCell->isMerged() )
550 bIsEmpty = false;
552 sal_Int32 nColSpan = xCell->getColumnSpan();
553 if( nColSpan > 1 )
555 // merged cells will be evaluated later
556 aMergedCells[nCol+nColSpan-1].push_back( xCell );
558 else
560 nMinWidth = std::max( nMinWidth, xCell->getMinimumSize().Width );
565 maColumns[nCol].mnMinSize = nMinWidth;
567 if( bIsEmpty )
569 maColumns[nCol].mnSize = 0;
571 else
573 sal_Int32 nColWidth = 0;
574 Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
575 sal_Bool bOptimal = sal_False;
576 xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
577 if( bOptimal )
579 aOptimalColumns.push_back(nCol);
581 else
583 xColSet->getPropertyValue( msSize ) >>= nColWidth;
586 maColumns[nCol].mnSize = nColWidth;
588 if( maColumns[nCol].mnSize < nMinWidth )
589 maColumns[nCol].mnSize = nMinWidth;
591 nCurrentWidth += maColumns[nCol].mnSize;
595 // if we have optimal sized rows, distribute what is given (left)
596 if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getWidth()) )
598 sal_Int32 nLeft = rArea.getWidth() - nCurrentWidth;
599 sal_Int32 nDistribute = nLeft / aOptimalColumns.size();
601 Int32Vector::iterator iter( aOptimalColumns.begin() );
602 while( iter != aOptimalColumns.end() )
604 sal_Int32 nOptCol = (*iter++);
605 if( iter == aOptimalColumns.end() )
606 nDistribute = nLeft;
608 maColumns[nOptCol].mnSize += nDistribute;
609 nLeft -= nDistribute;
612 DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
615 // now check if merged cells fit
616 for( nCol = 1; nCol < nColCount; ++nCol )
618 bool bChanges = false;
619 MergeableCellVector::iterator iter( aMergedCells[nCol].begin() );
621 const sal_Int32 nOldSize = maColumns[nCol].mnSize;
623 while( iter != aMergedCells[nCol].end() )
625 CellRef xCell( (*iter++) );
626 sal_Int32 nMinWidth = xCell->getMinimumSize().Width;
628 for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol )
629 nMinWidth -= maColumns[nMCol].mnSize;
631 if( nMinWidth > maColumns[nCol].mnMinSize )
632 maColumns[nCol].mnMinSize = nMinWidth;
634 if( nMinWidth > maColumns[nCol].mnSize )
636 maColumns[nCol].mnSize = nMinWidth;
637 bChanges = true;
641 if( bChanges )
642 nCurrentWidth += maColumns[nCol].mnSize - nOldSize;
645 // now scale if wanted and needed
646 if( bFit && (nCurrentWidth != rArea.getWidth()) )
647 distribute( maColumns, rArea.getWidth() - nCurrentWidth );
649 // last step, update left edges
650 sal_Int32 nNewWidth = 0;
652 const bool bRTL = meWritingMode == WritingMode_RL_TB;
653 RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL );
654 while( coliter.next(nCol ) )
656 maColumns[nCol].mnPos = nNewWidth;
657 nNewWidth += maColumns[nCol].mnSize;
658 if( bFit )
660 Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW );
661 xColSet->setPropertyValue( msSize, Any( maColumns[nCol].mnSize ) );
665 rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) );
666 updateCells( rArea );
669 // -----------------------------------------------------------------------------
671 void TableLayouter::LayoutTableHeight( Rectangle& rArea, bool bFit )
673 const sal_Int32 nColCount = getColumnCount();
674 const sal_Int32 nRowCount = getRowCount();
675 if( nRowCount == 0 )
676 return;
678 Reference< XTableRows > xRows( mxTable->getRows() );
680 MergeVector aMergedCells( nRowCount );
681 Int32Vector aOptimalRows;
683 const OUString sOptimalSize("OptimalSize");
685 // first calculate current height and initial minimum size per column,
686 // merged cells will be counted later
687 sal_Int32 nCurrentHeight = 0;
688 sal_Int32 nCol, nRow;
689 for( nRow = 0; nRow < nRowCount; ++nRow )
691 sal_Int32 nMinHeight = 0;
693 bool bIsEmpty = true; // check if all cells in this row are merged
695 for( nCol = 0; nCol < nColCount; ++nCol )
697 CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
698 if( xCell.is() && !xCell->isMerged() )
700 bIsEmpty = false;
702 sal_Int32 nRowSpan = xCell->getRowSpan();
703 if( nRowSpan > 1 )
705 // merged cells will be evaluated later
706 aMergedCells[nRow+nRowSpan-1].push_back( xCell );
708 else
710 nMinHeight = std::max( nMinHeight, xCell->getMinimumSize().Height );
715 maRows[nRow].mnMinSize = nMinHeight;
717 if( bIsEmpty )
719 maRows[nRow].mnSize = 0;
721 else
723 sal_Int32 nRowHeight = 0;
724 Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
726 sal_Bool bOptimal = sal_False;
727 xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
728 if( bOptimal )
730 aOptimalRows.push_back( nRow );
732 else
734 xRowSet->getPropertyValue( msSize ) >>= nRowHeight;
737 maRows[nRow].mnSize = nRowHeight;
739 if( maRows[nRow].mnSize < nMinHeight )
740 maRows[nRow].mnSize = nMinHeight;
742 nCurrentHeight += maRows[nRow].mnSize;
746 // if we have optimal sized rows, distribute what is given (left)
747 if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getHeight()) )
749 sal_Int32 nLeft = rArea.getHeight() - nCurrentHeight;
750 sal_Int32 nDistribute = nLeft / aOptimalRows.size();
752 Int32Vector::iterator iter( aOptimalRows.begin() );
753 while( iter != aOptimalRows.end() )
755 sal_Int32 nOptRow = (*iter++);
756 if( iter == aOptimalRows.end() )
757 nDistribute = nLeft;
759 maRows[nOptRow].mnSize += nDistribute;
760 nLeft -= nDistribute;
764 DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
767 // now check if merged cells fit
768 for( nRow = 1; nRow < nRowCount; ++nRow )
770 bool bChanges = false;
771 sal_Int32 nOldSize = maRows[nRow].mnSize;
773 MergeableCellVector::iterator iter( aMergedCells[nRow].begin() );
774 while( iter != aMergedCells[nRow].end() )
776 CellRef xCell( (*iter++) );
777 sal_Int32 nMinHeight = xCell->getMinimumSize().Height;
779 for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow )
780 nMinHeight -= maRows[nMRow].mnSize;
782 if( nMinHeight > maRows[nRow].mnMinSize )
783 maRows[nRow].mnMinSize = nMinHeight;
785 if( nMinHeight > maRows[nRow].mnSize )
787 maRows[nRow].mnSize = nMinHeight;
788 bChanges = true;
791 if( bChanges )
792 nCurrentHeight += maRows[nRow].mnSize - nOldSize;
795 // now scale if wanted and needed
796 if( bFit && nCurrentHeight != rArea.getHeight() )
797 distribute( maRows, rArea.getHeight() - nCurrentHeight );
799 // last step, update left edges
800 sal_Int32 nNewHeight = 0;
801 for( nRow = 0; nRow < nRowCount; ++nRow )
803 maRows[nRow].mnPos = nNewHeight;
804 nNewHeight += maRows[nRow].mnSize;
806 if( bFit )
808 Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
809 xRowSet->setPropertyValue( msSize, Any( maRows[nRow].mnSize ) );
813 rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) );
814 updateCells( rArea );
817 // -----------------------------------------------------------------------------
819 /** try to fit the table into the given rectangle.
820 If the rectangle is to small, it will be grown to fit the table. */
821 void TableLayouter::LayoutTable( Rectangle& rRectangle, bool bFitWidth, bool bFitHeight )
823 if( !mxTable.is() )
824 return;
826 const sal_Int32 nRowCount = mxTable->getRowCount();
827 const sal_Int32 nColCount = mxTable->getColumnCount();
829 if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) )
831 if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount )
832 maRows.resize( nRowCount );
834 Reference< XTableRows > xRows( mxTable->getRows() );
835 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
836 maRows[nRow].clear();
838 if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount )
839 maColumns.resize( nColCount );
841 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
842 maColumns[nCol].clear();
845 LayoutTableWidth( rRectangle, bFitWidth );
846 LayoutTableHeight( rRectangle, bFitHeight );
847 UpdateBorderLayout();
850 // -----------------------------------------------------------------------------
852 void TableLayouter::updateCells( Rectangle& rRectangle )
854 const sal_Int32 nColCount = getColumnCount();
855 const sal_Int32 nRowCount = getRowCount();
857 CellPos aPos;
858 for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
860 for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
862 CellRef xCell( getCell( aPos ) );
863 if( xCell.is() )
865 basegfx::B2IRectangle aCellArea;
866 getCellArea( aPos, aCellArea );
868 Rectangle aCellRect;
869 aCellRect.Left() = aCellArea.getMinX();
870 aCellRect.Right() = aCellArea.getMaxX();
871 aCellRect.Top() = aCellArea.getMinY();
872 aCellRect.Bottom() = aCellArea.getMaxY();
873 aCellRect.Move( rRectangle.Left(), rRectangle.Top() );
874 xCell->setCellRect( aCellRect );
880 // -----------------------------------------------------------------------------
882 CellRef TableLayouter::getCell( const CellPos& rPos ) const
884 CellRef xCell;
885 if( mxTable.is() ) try
887 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
889 catch( Exception& )
891 OSL_FAIL( "sdr::table::TableLayouter::getCell(), exception caught!" );
893 return xCell;
896 // -----------------------------------------------------------------------------
898 bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
900 if (!pThis || ((pThis == &gEmptyBorder) && (pOther != 0)))
901 return false;
902 if (!pOther || (pOther == &gEmptyBorder))
903 return true;
905 sal_uInt16 nThisSize = pThis->GetOutWidth() + pThis->GetDistance() + pThis->GetInWidth();
906 sal_uInt16 nOtherSize = pOther->GetOutWidth() + pOther->GetDistance() + pOther->GetInWidth();
908 if (nThisSize > nOtherSize)
909 return true;
911 else if (nThisSize < nOtherSize)
913 return false;
915 else
917 if ( pOther->GetInWidth() && !pThis->GetInWidth() )
919 return true;
921 else if ( pThis->GetInWidth() && !pOther->GetInWidth() )
923 return false;
925 else
927 return true; //! ???
932 // -----------------------------------------------------------------------------
934 void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
936 if( pLine == 0 )
937 pLine = &gEmptyBorder;
939 SvxBorderLine *pOld = bHorizontal ? maHorizontalBorders[nCol][nRow] : maVerticalBorders[nCol][nRow];
941 if( HasPriority( pLine, pOld ) )
943 if( (pOld != 0) && (pOld != &gEmptyBorder) )
944 delete pOld;
946 SvxBorderLine* pNew = ( pLine != &gEmptyBorder ) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
948 if( bHorizontal )
949 maHorizontalBorders[nCol][nRow] = pNew;
950 else
951 maVerticalBorders[nCol][nRow] = pNew;
955 // -----------------------------------------------------------------------------
957 void TableLayouter::ClearBorderLayout()
959 ClearBorderLayout(maHorizontalBorders);
960 ClearBorderLayout(maVerticalBorders);
963 // -----------------------------------------------------------------------------
965 void TableLayouter::ClearBorderLayout(BorderLineMap& rMap)
967 const sal_Int32 nColCount = rMap.size();
969 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
971 const sal_Int32 nRowCount = rMap[nCol].size();
972 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
974 SvxBorderLine* pLine = rMap[nCol][nRow];
975 if( pLine )
977 if( pLine != &gEmptyBorder )
978 delete pLine;
980 rMap[nCol][nRow] = 0;
986 // -----------------------------------------------------------------------------
988 void TableLayouter::ResizeBorderLayout()
990 ClearBorderLayout();
991 ResizeBorderLayout(maHorizontalBorders);
992 ResizeBorderLayout(maVerticalBorders);
995 // -----------------------------------------------------------------------------
997 void TableLayouter::ResizeBorderLayout( BorderLineMap& rMap )
999 const sal_Int32 nColCount = getColumnCount() + 1;
1000 const sal_Int32 nRowCount = getRowCount() + 1;
1002 if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount )
1003 rMap.resize( nColCount );
1005 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
1007 if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount )
1008 rMap[nCol].resize( nRowCount );
1012 // -----------------------------------------------------------------------------
1014 void TableLayouter::UpdateBorderLayout()
1016 // make sure old border layout is cleared and border maps have correct size
1017 ResizeBorderLayout();
1019 const sal_Int32 nColCount = getColumnCount();
1020 const sal_Int32 nRowCount = getRowCount();
1022 CellPos aPos;
1023 for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
1025 for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
1027 CellRef xCell( getCell( aPos ) );
1028 if( !xCell.is() )
1029 continue;
1031 const SvxBoxItem* pThisAttr = (const SvxBoxItem*)xCell->GetItemSet().GetItem( SDRATTR_TABLE_BORDER );
1032 OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1034 if( !pThisAttr )
1035 continue;
1037 const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow;
1038 const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol;
1040 for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ )
1042 SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() );
1043 SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() );
1046 for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ )
1048 SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() );
1049 SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() );
1055 // -----------------------------------------------------------------------------
1057 void TableLayouter::DistributeColumns( ::Rectangle& rArea, sal_Int32 nFirstCol, sal_Int32 nLastCol )
1059 if( mxTable.is() ) try
1061 const sal_Int32 nColCount = getColumnCount();
1063 if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) )
1064 return;
1066 sal_Int32 nAllWidth = 0;
1067 for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1068 nAllWidth += getColumnWidth(nCol);
1070 sal_Int32 nWidth = nAllWidth / (nLastCol-nFirstCol+1);
1072 Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
1074 for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1076 if( nCol == nLastCol )
1077 nWidth = nAllWidth; // last column get round errors
1079 Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
1080 xColSet->setPropertyValue( msSize, Any( nWidth ) );
1082 nAllWidth -= nWidth;
1085 LayoutTable( rArea, true, false );
1087 catch( Exception& e )
1089 (void)e;
1090 OSL_FAIL("sdr::table::TableLayouter::DistributeColumns(), exception caught!");
1094 // -----------------------------------------------------------------------------
1096 void TableLayouter::DistributeRows( ::Rectangle& rArea, sal_Int32 nFirstRow, sal_Int32 nLastRow )
1098 if( mxTable.is() ) try
1100 const sal_Int32 nRowCount = mxTable->getRowCount();
1102 if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) )
1103 return;
1105 sal_Int32 nAllHeight = 0;
1106 sal_Int32 nMinHeight = 0;
1108 for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1110 nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight );
1111 nAllHeight += maRows[nRow].mnSize;
1114 const sal_Int32 nRows = (nLastRow-nFirstRow+1);
1115 sal_Int32 nHeight = nAllHeight / nRows;
1117 if( nHeight < nMinHeight )
1119 sal_Int32 nNeededHeight = nRows * nMinHeight;
1120 rArea.Bottom() += nNeededHeight - nAllHeight;
1121 nHeight = nMinHeight;
1122 nAllHeight = nRows * nMinHeight;
1125 Reference< XTableRows > xRows( mxTable->getRows(), UNO_QUERY_THROW );
1126 for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1128 if( nRow == nLastRow )
1129 nHeight = nAllHeight; // last row get round errors
1131 Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
1132 xRowSet->setPropertyValue( msSize, Any( nHeight ) );
1134 nAllHeight -= nHeight;
1137 LayoutTable( rArea, false, true );
1139 catch( Exception& e )
1141 (void)e;
1142 OSL_FAIL("sdr::table::TableLayouter::DistributeRows(), exception caught!");
1146 // -----------------------------------------------------------------------------
1147 void TableLayouter::SetWritingMode( com::sun::star::text::WritingMode eWritingMode )
1149 meWritingMode = eWritingMode;
1154 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */