1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: tablelayouter.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
34 #include <com/sun/star/table/XMergeableCell.hpp>
35 #include <com/sun/star/awt/XLayoutConstrains.hpp>
36 #include <boost/bind.hpp>
39 #include "cellrange.hxx"
40 #include "tablemodel.hxx"
41 #include "tablerow.hxx"
42 #include "tablerows.hxx"
43 #include "tablecolumn.hxx"
44 #include "tablecolumns.hxx"
45 #include "tablelayouter.hxx"
46 #include "svx/svdotable.hxx"
47 #include "svx/borderline.hxx"
48 #include "svx/boxitem.hxx"
49 #include "svx/svdmodel.hxx"
51 #include "svdglob.hxx"
53 using ::rtl::OUString
;
54 using ::com::sun::star::awt::XLayoutConstrains
;
55 using namespace ::com::sun::star::uno
;
56 using namespace ::com::sun::star::table
;
57 using namespace ::com::sun::star::lang
;
58 using namespace ::com::sun::star::container
;
59 using namespace ::com::sun::star::beans
;
60 using namespace ::com::sun::star::table
;
61 using namespace ::com::sun::star::text
;
63 // -----------------------------------------------------------------------------
65 namespace sdr
{ namespace table
{
67 // -----------------------------------------------------------------------------
69 static SvxBorderLine gEmptyBorder
;
71 // -----------------------------------------------------------------------------
73 TableLayouter::TableLayouter( const TableModelRef
& xTableModel
)
74 : mxTable( xTableModel
)
75 , meWritingMode( WritingMode_LR_TB
)
76 , msSize( RTL_CONSTASCII_USTRINGPARAM( "Size" ) )
80 // -----------------------------------------------------------------------------
82 TableLayouter::~TableLayouter()
87 // -----------------------------------------------------------------------------
89 basegfx::B2ITuple
TableLayouter::getCellSize( const CellPos
& rPos
) const
96 CellRef
xCell( getCell( rPos
) );
97 if( xCell
.is() && !xCell
->isMerged() )
101 sal_Int32 nRowCount
= getRowCount();
102 sal_Int32 nRowSpan
= std::max( xCell
->getRowSpan(), (sal_Int32
)1 );
103 while( nRowSpan
&& (aPos
.mnRow
< nRowCount
) )
105 if( ((sal_Int32
)maRows
.size()) <= aPos
.mnRow
)
108 height
+= maRows
[aPos
.mnRow
++].mnSize
;
112 sal_Int32 nColCount
= getColumnCount();
113 sal_Int32 nColSpan
= std::max( xCell
->getColumnSpan(), (sal_Int32
)1 );
114 while( nColSpan
&& (aPos
.mnCol
< nColCount
) )
116 if( ((sal_Int32
)maColumns
.size()) <= aPos
.mnCol
)
119 width
+= maColumns
[aPos
.mnCol
++].mnSize
;
126 DBG_ERROR( "TableLayouter::getCellSize(), exception caught!" );
129 return basegfx::B2ITuple( width
, height
);
132 // -----------------------------------------------------------------------------
134 bool TableLayouter::getCellArea( const CellPos
& rPos
, basegfx::B2IRectangle
& rArea
) const
138 CellRef
xCell( getCell( rPos
) );
139 if( xCell
.is() && !xCell
->isMerged() && isValid(rPos
) )
141 const basegfx::B2ITuple
aCellSize( getCellSize( rPos
) );
143 if( (rPos
.mnCol
< ((sal_Int32
)maColumns
.size()) && (rPos
.mnRow
< ((sal_Int32
)maRows
.size()) ) ) )
145 const sal_Int32 x
= maColumns
[rPos
.mnCol
].mnPos
;
146 const sal_Int32 y
= maRows
[rPos
.mnRow
].mnPos
;
148 rArea
= basegfx::B2IRectangle( x
, y
, x
+ aCellSize
.getX(), y
+ aCellSize
.getY() );
155 DBG_ERROR( "TableLayouter::getCellSize(), exception caught!" );
160 // -----------------------------------------------------------------------------
162 sal_Int32
TableLayouter::getRowHeight( sal_Int32 nRow
)
164 if( isValidRow(nRow
) )
165 return maRows
[nRow
].mnSize
;
170 // -----------------------------------------------------------------------------
172 void TableLayouter::setRowHeight( sal_Int32 nRow
, sal_Int32 nHeight
)
174 if( isValidRow(nRow
) )
176 maRows
[nRow
].mnSize
= nHeight
;
180 DBG_ERROR( "TableLayouter::setRowHeight(), row out of range!" );
184 // -----------------------------------------------------------------------------
186 sal_Int32
TableLayouter::getColumnWidth( sal_Int32 nColumn
)
188 if( isValidColumn(nColumn
) )
189 return maColumns
[nColumn
].mnSize
;
194 // -----------------------------------------------------------------------------
196 void TableLayouter::setColumnWidth( sal_Int32 nColumn
, sal_Int32 nWidth
)
198 if( isValidColumn(nColumn
) )
199 maColumns
[nColumn
].mnSize
= nWidth
;
201 DBG_ERROR( "TableLayouter::setColumnWidth(), column out of range!" );
204 // -----------------------------------------------------------------------------
206 bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX
, sal_Int32 nEdgeY
, bool bHorizontal
) const
208 const BorderLineMap
& rMap
= bHorizontal
? maHorizontalBorders
: maVerticalBorders
;
210 if( (nEdgeX
>= 0) && (nEdgeX
< sal::static_int_cast
<sal_Int32
>(rMap
.size())) &&
211 (nEdgeY
>= 0) && (nEdgeY
< sal::static_int_cast
<sal_Int32
>(rMap
[nEdgeX
].size())) )
213 return rMap
[nEdgeX
][nEdgeY
] != 0;
217 OSL_ENSURE( false, "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
223 // -----------------------------------------------------------------------------
225 /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
226 SvxBorderLine
* TableLayouter::getBorderLine( sal_Int32 nEdgeX
, sal_Int32 nEdgeY
, bool bHorizontal
)const
228 SvxBorderLine
* pLine
= 0;
230 const BorderLineMap
& rMap
= bHorizontal
? maHorizontalBorders
: maVerticalBorders
;
232 if( (nEdgeX
>= 0) && (nEdgeX
< sal::static_int_cast
<sal_Int32
>(rMap
.size())) &&
233 (nEdgeY
>= 0) && (nEdgeY
< sal::static_int_cast
<sal_Int32
>(rMap
[nEdgeX
].size())) )
235 pLine
= rMap
[nEdgeX
][nEdgeY
];
236 if( pLine
== &gEmptyBorder
)
241 OSL_ENSURE( false, "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
247 // -----------------------------------------------------------------------------
249 sal_Int32
TableLayouter::getHorizontalEdge( int nEdgeY
, sal_Int32
* pnMin
/*= 0*/, sal_Int32
* pnMax
/*= 0*/ )
252 if( (nEdgeY
>= 0) && (nEdgeY
<= getRowCount() ) )
253 nRet
= maRows
[std::min((sal_Int32
)nEdgeY
,getRowCount()-1)].mnPos
;
255 if( nEdgeY
== getRowCount() )
256 nRet
+= maRows
[nEdgeY
- 1].mnSize
;
260 if( (nEdgeY
> 0) && (nEdgeY
<= getRowCount() ) )
262 *pnMin
= maRows
[nEdgeY
-1].mnPos
+ 600; // todo
277 // -----------------------------------------------------------------------------
279 sal_Int32
TableLayouter::getVerticalEdge( int nEdgeX
, sal_Int32
* pnMin
/*= 0*/, sal_Int32
* pnMax
/*= 0*/ )
283 const sal_Int32 nColCount
= getColumnCount();
284 if( (nEdgeX
>= 0) && (nEdgeX
<= nColCount
) )
285 nRet
= maColumns
[std::min((sal_Int32
)nEdgeX
,nColCount
-1)].mnPos
;
287 const bool bRTL
= meWritingMode
== WritingMode_RL_TB
;
290 if( (nEdgeX
>= 0) && (nEdgeX
< nColCount
) )
291 nRet
+= maColumns
[nEdgeX
].mnSize
;
295 if( nEdgeX
== getColumnCount() )
296 nRet
+= maColumns
[nEdgeX
- 1].mnSize
;
304 if( nEdgeX
< nColCount
)
305 *pnMin
= nRet
- maColumns
[nEdgeX
].mnSize
+ getMinimumColumnWidth(nEdgeX
);
309 if( (nEdgeX
> 0) && (nEdgeX
<= nColCount
) )
310 *pnMin
= maColumns
[nEdgeX
-1].mnPos
+ getMinimumColumnWidth( nEdgeX
-1 );
316 *pnMax
= 0x0fffffff; // todo
320 *pnMax
= nRet
+ maColumns
[nEdgeX
-1].mnSize
- getMinimumColumnWidth( nEdgeX
-1 );
324 if( (nEdgeX
>= 0) && (nEdgeX
< nColCount
) )
325 *pnMax
= maColumns
[nEdgeX
].mnPos
+ maColumns
[nEdgeX
].mnSize
- getMinimumColumnWidth( nEdgeX
);
332 // -----------------------------------------------------------------------------
334 static bool checkMergeOrigin( const TableModelRef
& xTable
, sal_Int32 nMergedX
, sal_Int32 nMergedY
, sal_Int32 nCellX
, sal_Int32 nCellY
, bool& bRunning
)
336 Reference
< XMergeableCell
> xCell( xTable
->getCellByPosition( nCellX
, nCellY
), UNO_QUERY
);
337 if( xCell
.is() && !xCell
->isMerged() )
339 const sal_Int32 nRight
= xCell
->getColumnSpan() + nCellX
;
340 const sal_Int32 nBottom
= xCell
->getRowSpan() + nCellY
;
341 if( (nMergedX
< nRight
) && (nMergedY
< nBottom
) )
349 /** returns true if the cell(nMergedX,nMergedY) is merged with other cells.
350 the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge.
352 bool findMergeOrigin( const TableModelRef
& xTable
, sal_Int32 nMergedX
, sal_Int32 nMergedY
, sal_Int32
& rOriginX
, sal_Int32
& rOriginY
)
357 if( xTable
.is() ) try
359 // check if this cell already the origin or not merged at all
360 Reference
< XMergeableCell
> xCell( xTable
->getCellByPosition( nMergedX
, nMergedY
), UNO_QUERY_THROW
);
361 if( !xCell
.is() || !xCell
->isMerged() )
364 bool bCheckVert
= true;
365 bool bCheckHorz
= true;
367 sal_Int32 nMinCol
= 0;
368 sal_Int32 nMinRow
= 0;
370 sal_Int32 nStep
= 1, i
;
372 sal_Int32 nRow
, nCol
;
377 nRow
= nMergedY
- nStep
;
378 if( nRow
>= nMinRow
)
381 for( i
= 0; (i
<= nStep
) && (nCol
>= nMinCol
); i
++, nCol
-- )
383 if( checkMergeOrigin( xTable
, nMergedX
, nMergedY
, nCol
, nRow
, bCheckVert
) )
385 rOriginX
= nCol
; rOriginY
= nRow
;
391 if( nCol
== nMergedX
)
411 nCol
= nMergedX
- nStep
;
412 if( nCol
>= nMinCol
)
415 for( i
= 0; (i
< nStep
) && (nRow
>= nMinRow
); i
++, nRow
-- )
417 if( checkMergeOrigin( xTable
, nMergedX
, nMergedY
, nCol
, nRow
, bCheckHorz
) )
419 rOriginX
= nCol
; rOriginY
= nRow
;
425 if( nRow
== nMergedY
)
444 while( bCheckVert
|| bCheckHorz
);
448 DBG_ERROR("sdr::table::TableLayouter::findMergeOrigin(), exception caught!");
453 // -----------------------------------------------------------------------------
455 sal_Int32
TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn
)
457 if( isValidColumn( nColumn
) )
459 return maColumns
[nColumn
].mnMinSize
;
463 DBG_ERROR( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
468 // -----------------------------------------------------------------------------
470 sal_Int32
TableLayouter::distribute( LayoutVector
& rLayouts
, sal_Int32 nDistribute
)
472 // break loops after 100 runs to avoid freezing office due to developer error
473 sal_Int32 nSafe
= 100;
475 const sal_Size nCount
= rLayouts
.size();
478 bool bConstrainsBroken
= false;
482 // first enforce minimum size constrains on all entities
483 for( nIndex
= 0; nIndex
< nCount
; ++nIndex
)
485 Layout
& rLayout
= rLayouts
[nIndex
];
486 if( rLayout
.mnSize
< rLayout
.mnMinSize
)
488 nDistribute
-= rLayout
.mnMinSize
- rLayout
.mnSize
;
489 rLayout
.mnSize
= rLayout
.mnMinSize
;
493 // calculate current width
494 // if nDistribute is < 0 (shrinking), entities that are already
495 // at minimum width are not counted
496 sal_Int32 nCurrentWidth
= 0;
497 for( nIndex
= 0; nIndex
< nCount
; ++nIndex
)
499 Layout
& rLayout
= rLayouts
[nIndex
];
500 if( (nDistribute
> 0) || (rLayout
.mnSize
> rLayout
.mnMinSize
) )
501 nCurrentWidth
+= rLayout
.mnSize
;
504 bConstrainsBroken
= false;
506 // now distribute over entities
507 if( (nCurrentWidth
!= 0) && (nDistribute
!= 0) )
509 sal_Int32 nDistributed
= nDistribute
;
510 for( nIndex
= 0; nIndex
< nCount
; ++nIndex
)
512 Layout
& rLayout
= rLayouts
[nIndex
];
513 if( (nDistribute
> 0) || (rLayout
.mnSize
> rLayout
.mnMinSize
) )
516 if( nIndex
== (nCount
-1) )
517 n
= nDistributed
; // for last entitie, use up rest
519 n
= (nDistribute
* rLayout
.mnSize
) / nCurrentWidth
; //
524 if( rLayout
.mnSize
< rLayout
.mnMinSize
)
525 bConstrainsBroken
= true;
529 } while( bConstrainsBroken
&& --nSafe
);
532 for( nIndex
= 0; nIndex
< nCount
; ++nIndex
)
533 nSize
+= rLayouts
[nIndex
].mnSize
;
538 // -----------------------------------------------------------------------------
540 typedef std::vector
< CellRef
> MergeableCellVector
;
541 typedef std::vector
< MergeableCellVector
> MergeVector
;
542 typedef std::vector
< sal_Int32
> Int32Vector
;
544 // -----------------------------------------------------------------------------
546 void TableLayouter::LayoutTableWidth( Rectangle
& rArea
, bool bFit
)
548 const sal_Int32 nColCount
= getColumnCount();
549 const sal_Int32 nRowCount
= getRowCount();
553 MergeVector
aMergedCells( nColCount
);
554 Int32Vector aOptimalColumns
;
556 const OUString
sOptimalSize( RTL_CONSTASCII_USTRINGPARAM("OptimalSize") );
558 if( sal::static_int_cast
< sal_Int32
>( maColumns
.size() ) != nColCount
)
559 maColumns
.resize( nColCount
);
561 Reference
< XTableColumns
> xCols( mxTable
->getColumns(), UNO_QUERY_THROW
);
563 // first calculate current width and initial minimum width per column,
564 // merged cells will be counted later
565 sal_Int32 nCurrentWidth
= 0;
566 sal_Int32 nCol
= 0, nRow
= 0;
567 for( nCol
= 0; nCol
< nColCount
; nCol
++ )
569 sal_Int32 nMinWidth
= 0;
571 bool bIsEmpty
= true; // check if all cells in this column are merged
573 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
575 CellRef
xCell( getCell( CellPos( nCol
, nRow
) ) );
576 if( xCell
.is() && !xCell
->isMerged() )
580 sal_Int32 nColSpan
= xCell
->getColumnSpan();
583 // merged cells will be evaluated later
584 aMergedCells
[nCol
+nColSpan
-1].push_back( xCell
);
588 nMinWidth
= std::max( nMinWidth
, xCell
->getMinimumSize().Width
);
593 maColumns
[nCol
].mnMinSize
= nMinWidth
;
597 maColumns
[nCol
].mnSize
= 0;
601 sal_Int32 nColWidth
= 0;
602 Reference
< XPropertySet
> xColSet( xCols
->getByIndex( nCol
), UNO_QUERY_THROW
);
603 sal_Bool bOptimal
= sal_False
;
604 xColSet
->getPropertyValue( sOptimalSize
) >>= bOptimal
;
607 aOptimalColumns
.push_back(nCol
);
611 xColSet
->getPropertyValue( msSize
) >>= nColWidth
;
614 maColumns
[nCol
].mnSize
= nColWidth
;
616 if( maColumns
[nCol
].mnSize
< nMinWidth
)
617 maColumns
[nCol
].mnSize
= nMinWidth
;
619 nCurrentWidth
+= maColumns
[nCol
].mnSize
;
623 // if we have optimal sized rows, distribute what is given (left)
624 if( !bFit
&& !aOptimalColumns
.empty() && (nCurrentWidth
< rArea
.getWidth()) )
626 sal_Int32 nLeft
= rArea
.getWidth() - nCurrentWidth
;
627 sal_Int32 nDistribute
= nLeft
/ aOptimalColumns
.size();
629 Int32Vector::iterator
iter( aOptimalColumns
.begin() );
630 while( iter
!= aOptimalColumns
.end() )
632 sal_Int32 nOptCol
= (*iter
++);
633 if( iter
== aOptimalColumns
.end() )
636 maColumns
[nOptCol
].mnSize
+= nDistribute
;
637 nLeft
-= nDistribute
;
640 DBG_ASSERT( nLeft
== 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
643 // now check if merged cells fit
644 for( nCol
= 1; nCol
< nColCount
; ++nCol
)
646 bool bChanges
= false;
647 MergeableCellVector::iterator
iter( aMergedCells
[nCol
].begin() );
649 const sal_Int32 nOldSize
= maColumns
[nCol
].mnSize
;
651 while( iter
!= aMergedCells
[nCol
].end() )
653 CellRef
xCell( (*iter
++) );
654 sal_Int32 nMinWidth
= xCell
->getMinimumSize().Width
;
656 for( sal_Int32 nMCol
= nCol
- xCell
->getColumnSpan() + 1; (nMCol
> 0) && (nMCol
< nCol
); ++nMCol
)
657 nMinWidth
-= maColumns
[nMCol
].mnSize
;
659 if( nMinWidth
> maColumns
[nCol
].mnMinSize
)
660 maColumns
[nCol
].mnMinSize
= nMinWidth
;
662 if( nMinWidth
> maColumns
[nCol
].mnSize
)
664 maColumns
[nCol
].mnSize
= nMinWidth
;
670 nCurrentWidth
+= maColumns
[nCol
].mnSize
- nOldSize
;
673 // now scale if wanted and needed
674 if( bFit
&& (nCurrentWidth
!= rArea
.getWidth()) )
675 distribute( maColumns
, rArea
.getWidth() - nCurrentWidth
);
677 // last step, update left edges
678 sal_Int32 nNewWidth
= 0;
680 const bool bRTL
= meWritingMode
== WritingMode_RL_TB
;
681 RangeIterator
<sal_Int32
> coliter( 0, nColCount
, !bRTL
);
682 while( coliter
.next(nCol
) )
684 maColumns
[nCol
].mnPos
= nNewWidth
;
685 nNewWidth
+= maColumns
[nCol
].mnSize
;
688 Reference
< XPropertySet
> xColSet( xCols
->getByIndex(nCol
), UNO_QUERY_THROW
);
689 xColSet
->setPropertyValue( msSize
, Any( maColumns
[nCol
].mnSize
) );
693 rArea
.SetSize( Size( nNewWidth
, rArea
.GetHeight() ) );
694 updateCells( rArea
);
697 // -----------------------------------------------------------------------------
699 void TableLayouter::LayoutTableHeight( Rectangle
& rArea
, bool bFit
)
701 const sal_Int32 nColCount
= getColumnCount();
702 const sal_Int32 nRowCount
= getRowCount();
706 Reference
< XTableRows
> xRows( mxTable
->getRows() );
708 MergeVector
aMergedCells( nRowCount
);
709 Int32Vector aOptimalRows
;
711 const OUString
sOptimalSize( RTL_CONSTASCII_USTRINGPARAM("OptimalSize") );
713 // first calculate current height and initial minimum size per column,
714 // merged cells will be counted later
715 sal_Int32 nCurrentHeight
= 0;
716 sal_Int32 nCol
, nRow
;
717 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
719 sal_Int32 nMinHeight
= 0;
721 bool bIsEmpty
= true; // check if all cells in this row are merged
723 for( nCol
= 0; nCol
< nColCount
; ++nCol
)
725 CellRef
xCell( getCell( CellPos( nCol
, nRow
) ) );
726 if( xCell
.is() && !xCell
->isMerged() )
730 sal_Int32 nRowSpan
= xCell
->getRowSpan();
733 // merged cells will be evaluated later
734 aMergedCells
[nRow
+nRowSpan
-1].push_back( xCell
);
738 nMinHeight
= std::max( nMinHeight
, xCell
->getMinimumSize().Height
);
743 maRows
[nRow
].mnMinSize
= nMinHeight
;
747 maRows
[nRow
].mnSize
= 0;
751 sal_Int32 nRowHeight
= 0;
752 Reference
< XPropertySet
> xRowSet( xRows
->getByIndex(nRow
), UNO_QUERY_THROW
);
754 sal_Bool bOptimal
= sal_False
;
755 xRowSet
->getPropertyValue( sOptimalSize
) >>= bOptimal
;
758 aOptimalRows
.push_back( nRow
);
762 xRowSet
->getPropertyValue( msSize
) >>= nRowHeight
;
765 maRows
[nRow
].mnSize
= nRowHeight
;
767 if( maRows
[nRow
].mnSize
< nMinHeight
)
768 maRows
[nRow
].mnSize
= nMinHeight
;
770 nCurrentHeight
+= maRows
[nRow
].mnSize
;
774 // if we have optimal sized rows, distribute what is given (left)
775 if( !bFit
&& !aOptimalRows
.empty() && (nCurrentHeight
< rArea
.getHeight()) )
777 sal_Int32 nLeft
= rArea
.getHeight() - nCurrentHeight
;
778 sal_Int32 nDistribute
= nLeft
/ aOptimalRows
.size();
780 Int32Vector::iterator
iter( aOptimalRows
.begin() );
781 while( iter
!= aOptimalRows
.end() )
783 sal_Int32 nOptRow
= (*iter
++);
784 if( iter
== aOptimalRows
.end() )
787 maRows
[nOptRow
].mnSize
+= nDistribute
;
788 nLeft
-= nDistribute
;
792 DBG_ASSERT( nLeft
== 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
795 // now check if merged cells fit
796 for( nRow
= 1; nRow
< nRowCount
; ++nRow
)
798 bool bChanges
= false;
799 sal_Int32 nOldSize
= maRows
[nRow
].mnSize
;
801 MergeableCellVector::iterator
iter( aMergedCells
[nRow
].begin() );
802 while( iter
!= aMergedCells
[nRow
].end() )
804 CellRef
xCell( (*iter
++) );
805 sal_Int32 nMinHeight
= xCell
->getMinimumSize().Height
;
807 for( sal_Int32 nMRow
= nRow
- xCell
->getRowSpan() + 1; (nMRow
> 0) && (nMRow
< nRow
); ++nMRow
)
808 nMinHeight
-= maRows
[nMRow
].mnSize
;
810 if( nMinHeight
> maRows
[nRow
].mnMinSize
)
811 maRows
[nRow
].mnMinSize
= nMinHeight
;
813 if( nMinHeight
> maRows
[nRow
].mnSize
)
815 maRows
[nRow
].mnSize
= nMinHeight
;
820 nCurrentHeight
+= maRows
[nRow
].mnSize
- nOldSize
;
823 // now scale if wanted and needed
824 if( bFit
&& nCurrentHeight
!= rArea
.getHeight() )
825 distribute( maRows
, rArea
.getHeight() - nCurrentHeight
);
827 // last step, update left edges
828 sal_Int32 nNewHeight
= 0;
829 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
831 maRows
[nRow
].mnPos
= nNewHeight
;
832 nNewHeight
+= maRows
[nRow
].mnSize
;
836 Reference
< XPropertySet
> xRowSet( xRows
->getByIndex(nRow
), UNO_QUERY_THROW
);
837 xRowSet
->setPropertyValue( msSize
, Any( maRows
[nRow
].mnSize
) );
841 rArea
.SetSize( Size( rArea
.GetWidth(), nNewHeight
) );
842 updateCells( rArea
);
845 // -----------------------------------------------------------------------------
847 /** try to fit the table into the given rectangle.
848 If the rectangle is to small, it will be grown to fit the table. */
849 void TableLayouter::LayoutTable( Rectangle
& rRectangle
, bool bFitWidth
, bool bFitHeight
)
854 const sal_Int32 nRowCount
= mxTable
->getRowCount();
855 const sal_Int32 nColCount
= mxTable
->getColumnCount();
857 if( (nRowCount
!= getRowCount()) || (nColCount
!= getColumnCount()) )
859 if( static_cast< sal_Int32
>( maRows
.size() ) != nRowCount
)
860 maRows
.resize( nRowCount
);
862 Reference
< XTableRows
> xRows( mxTable
->getRows() );
863 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; nRow
++ )
864 maRows
[nRow
].clear();
866 if( static_cast< sal_Int32
>( maColumns
.size() ) != nColCount
)
867 maColumns
.resize( nColCount
);
869 for( sal_Int32 nCol
= 0; nCol
< nColCount
; nCol
++ )
870 maColumns
[nCol
].clear();
873 LayoutTableWidth( rRectangle
, bFitWidth
);
874 LayoutTableHeight( rRectangle
, bFitHeight
);
875 UpdateBorderLayout();
878 // -----------------------------------------------------------------------------
880 void TableLayouter::updateCells( Rectangle
& rRectangle
)
882 const sal_Int32 nColCount
= getColumnCount();
883 const sal_Int32 nRowCount
= getRowCount();
886 for( aPos
.mnRow
= 0; aPos
.mnRow
< nRowCount
; aPos
.mnRow
++ )
888 for( aPos
.mnCol
= 0; aPos
.mnCol
< nColCount
; aPos
.mnCol
++ )
890 CellRef
xCell( getCell( aPos
) );
893 basegfx::B2IRectangle aCellArea
;
894 getCellArea( aPos
, aCellArea
);
897 aCellRect
.nLeft
= aCellArea
.getMinX();
898 aCellRect
.nRight
= aCellArea
.getMaxX();
899 aCellRect
.nTop
= aCellArea
.getMinY();
900 aCellRect
.nBottom
= aCellArea
.getMaxY();
901 aCellRect
.Move( rRectangle
.nLeft
, rRectangle
.nTop
);
902 xCell
->setCellRect( aCellRect
);
908 // -----------------------------------------------------------------------------
910 CellRef
TableLayouter::getCell( const CellPos
& rPos
) const
913 if( mxTable
.is() ) try
915 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( rPos
.mnCol
, rPos
.mnRow
).get() ) );
919 DBG_ERROR( "sdr::table::TableLayouter::getCell(), exception caught!" );
924 // -----------------------------------------------------------------------------
926 bool TableLayouter::HasPriority( const SvxBorderLine
* pThis
, const SvxBorderLine
* pOther
)
928 if (!pThis
|| ((pThis
== &gEmptyBorder
) && (pOther
!= 0)))
930 if (!pOther
|| (pOther
== &gEmptyBorder
))
933 USHORT nThisSize
= pThis
->GetOutWidth() + pThis
->GetDistance() + pThis
->GetInWidth();
934 USHORT nOtherSize
= pOther
->GetOutWidth() + pOther
->GetDistance() + pOther
->GetInWidth();
936 if (nThisSize
> nOtherSize
)
939 else if (nThisSize
< nOtherSize
)
945 if ( pOther
->GetInWidth() && !pThis
->GetInWidth() )
949 else if ( pThis
->GetInWidth() && !pOther
->GetInWidth() )
960 // -----------------------------------------------------------------------------
962 void TableLayouter::SetBorder( sal_Int32 nCol
, sal_Int32 nRow
, bool bHorizontal
, const SvxBorderLine
* pLine
)
965 pLine
= &gEmptyBorder
;
967 SvxBorderLine
*pOld
= bHorizontal
? maHorizontalBorders
[nCol
][nRow
] : maVerticalBorders
[nCol
][nRow
];
969 if( HasPriority( pLine
, pOld
) )
971 if( (pOld
!= 0) && (pOld
!= &gEmptyBorder
) )
974 SvxBorderLine
* pNew
= ( pLine
!= &gEmptyBorder
) ? new SvxBorderLine(*pLine
) : &gEmptyBorder
;
977 maHorizontalBorders
[nCol
][nRow
] = pNew
;
979 maVerticalBorders
[nCol
][nRow
] = pNew
;
983 // -----------------------------------------------------------------------------
985 void TableLayouter::ClearBorderLayout()
987 ClearBorderLayout(maHorizontalBorders
);
988 ClearBorderLayout(maVerticalBorders
);
991 // -----------------------------------------------------------------------------
993 void TableLayouter::ClearBorderLayout(BorderLineMap
& rMap
)
995 const sal_Int32 nColCount
= rMap
.size();
997 for( sal_Int32 nCol
= 0; nCol
< nColCount
; nCol
++ )
999 const sal_Int32 nRowCount
= rMap
[nCol
].size();
1000 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; nRow
++ )
1002 SvxBorderLine
* pLine
= rMap
[nCol
][nRow
];
1005 if( pLine
!= &gEmptyBorder
)
1008 rMap
[nCol
][nRow
] = 0;
1014 // -----------------------------------------------------------------------------
1016 void TableLayouter::ResizeBorderLayout()
1018 ClearBorderLayout();
1019 ResizeBorderLayout(maHorizontalBorders
);
1020 ResizeBorderLayout(maVerticalBorders
);
1023 // -----------------------------------------------------------------------------
1025 void TableLayouter::ResizeBorderLayout( BorderLineMap
& rMap
)
1027 const sal_Int32 nColCount
= getColumnCount() + 1;
1028 const sal_Int32 nRowCount
= getRowCount() + 1;
1030 if( sal::static_int_cast
<sal_Int32
>(rMap
.size()) != nColCount
)
1031 rMap
.resize( nColCount
);
1033 for( sal_Int32 nCol
= 0; nCol
< nColCount
; nCol
++ )
1035 if( sal::static_int_cast
<sal_Int32
>(rMap
[nCol
].size()) != nRowCount
)
1036 rMap
[nCol
].resize( nRowCount
);
1040 // -----------------------------------------------------------------------------
1042 void TableLayouter::UpdateBorderLayout()
1044 // make sure old border layout is cleared and border maps have correct size
1045 ResizeBorderLayout();
1047 const sal_Int32 nColCount
= getColumnCount();
1048 const sal_Int32 nRowCount
= getRowCount();
1051 for( aPos
.mnRow
= 0; aPos
.mnRow
< nRowCount
; aPos
.mnRow
++ )
1053 for( aPos
.mnCol
= 0; aPos
.mnCol
< nColCount
; aPos
.mnCol
++ )
1055 CellRef
xCell( getCell( aPos
) );
1056 if( !xCell
.is() || xCell
->isMerged() )
1059 const SvxBoxItem
* pThisAttr
= (const SvxBoxItem
*)xCell
->GetItemSet().GetItem( SDRATTR_TABLE_BORDER
);
1060 OSL_ENSURE(pThisAttr
,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1065 const sal_Int32 nLastRow
= xCell
->getRowSpan() + aPos
.mnRow
;
1066 const sal_Int32 nLastCol
= xCell
->getColumnSpan() + aPos
.mnCol
;
1068 for( sal_Int32 nRow
= aPos
.mnRow
; nRow
< nLastRow
; nRow
++ )
1070 SetBorder( aPos
.mnCol
, nRow
, false, pThisAttr
->GetLeft() );
1071 SetBorder( nLastCol
, nRow
, false, pThisAttr
->GetRight() );
1074 for( sal_Int32 nCol
= aPos
.mnCol
; nCol
< nLastCol
; nCol
++ )
1076 SetBorder( nCol
, aPos
.mnRow
, true, pThisAttr
->GetTop() );
1077 SetBorder( nCol
, nLastRow
, true, pThisAttr
->GetBottom() );
1083 // -----------------------------------------------------------------------------
1085 void TableLayouter::SetLayoutToModel()
1087 const sal_Int32 nRowCount = getRowCount();
1088 const sal_Int32 nColCount = getColumnCount();
1092 sal_Int32 nOldSize = 0;
1094 Reference< XIndexAccess > xRows( mxTable->getRows(), UNO_QUERY_THROW );
1095 for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1097 Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
1098 xRowSet->getPropertyValue( msSize ) >>= nOldSize;
1099 if( maRows[nRow].mnSize != nOldSize )
1100 xRowSet->setPropertyValue( msSize, Any( maRows[nRow].mnSize ) );
1103 for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
1105 Reference< XPropertySet > xColSet( getColumnByIndex( nCol ), UNO_QUERY_THROW );
1106 xColSet->getPropertyValue( msSize ) >>= nOldSize;
1107 if( maColumns[nCol].mnSize != nOldSize )
1108 xColSet->setPropertyValue( msSize, Any( maColumns[nCol].mnSize ) );
1113 DBG_ERROR("sdr::table::TableLayouter::SetLayoutToModel(), exception caught!");
1117 // -----------------------------------------------------------------------------
1119 void TableLayouter::DistributeColumns( ::Rectangle
& rArea
, sal_Int32 nFirstCol
, sal_Int32 nLastCol
)
1121 if( mxTable
.is() ) try
1123 const sal_Int32 nColCount
= getColumnCount();
1125 if( (nFirstCol
< 0) || (nFirstCol
>= nLastCol
) || (nLastCol
>= nColCount
) )
1128 sal_Int32 nAllWidth
= 0;
1129 for( sal_Int32 nCol
= nFirstCol
; nCol
<= nLastCol
; ++nCol
)
1130 nAllWidth
+= getColumnWidth(nCol
);
1132 sal_Int32 nWidth
= nAllWidth
/ (nLastCol
-nFirstCol
+1);
1134 Reference
< XTableColumns
> xCols( mxTable
->getColumns(), UNO_QUERY_THROW
);
1136 for( sal_Int32 nCol
= nFirstCol
; nCol
<= nLastCol
; ++nCol
)
1138 if( nCol
== nLastCol
)
1139 nWidth
= nAllWidth
; // last column get round errors
1141 Reference
< XPropertySet
> xColSet( xCols
->getByIndex( nCol
), UNO_QUERY_THROW
);
1142 xColSet
->setPropertyValue( msSize
, Any( nWidth
) );
1144 nAllWidth
-= nWidth
;
1147 LayoutTable( rArea
, true, false );
1149 catch( Exception
& e
)
1152 DBG_ERROR("sdr::table::TableLayouter::DistributeColumns(), exception caught!");
1156 // -----------------------------------------------------------------------------
1158 void TableLayouter::DistributeRows( ::Rectangle
& rArea
, sal_Int32 nFirstRow
, sal_Int32 nLastRow
)
1160 if( mxTable
.is() ) try
1162 const sal_Int32 nRowCount
= mxTable
->getRowCount();
1164 if( (nFirstRow
< 0) || (nFirstRow
>= nLastRow
) || (nLastRow
>= nRowCount
) )
1167 sal_Int32 nAllHeight
= 0;
1168 sal_Int32 nMinHeight
= 0;
1170 for( sal_Int32 nRow
= nFirstRow
; nRow
<= nLastRow
; ++nRow
)
1172 nMinHeight
= std::max( maRows
[nRow
].mnMinSize
, nMinHeight
);
1173 nAllHeight
+= maRows
[nRow
].mnSize
;
1176 const sal_Int32 nRows
= (nLastRow
-nFirstRow
+1);
1177 sal_Int32 nHeight
= nAllHeight
/ nRows
;
1179 if( nHeight
< nMinHeight
)
1181 sal_Int32 nNeededHeight
= nRows
* nMinHeight
;
1182 rArea
.nBottom
+= nNeededHeight
- nAllHeight
;
1183 nHeight
= nMinHeight
;
1184 nAllHeight
= nRows
* nMinHeight
;
1187 Reference
< XTableRows
> xRows( mxTable
->getRows(), UNO_QUERY_THROW
);
1188 for( sal_Int32 nRow
= nFirstRow
; nRow
<= nLastRow
; ++nRow
)
1190 if( nRow
== nLastRow
)
1191 nHeight
= nAllHeight
; // last row get round errors
1193 Reference
< XPropertySet
> xRowSet( xRows
->getByIndex( nRow
), UNO_QUERY_THROW
);
1194 xRowSet
->setPropertyValue( msSize
, Any( nHeight
) );
1196 nAllHeight
-= nHeight
;
1199 LayoutTable( rArea
, false, true );
1201 catch( Exception
& e
)
1204 DBG_ERROR("sdr::table::TableLayouter::DistributeRows(), exception caught!");
1208 // -----------------------------------------------------------------------------
1210 void TableLayouter::SetWritingMode( com::sun::star::text::WritingMode eWritingMode
)
1212 meWritingMode
= eWritingMode
;
1215 // -----------------------------------------------------------------------------
1217 sal_Int32
TableLayouter::getColumnStart( sal_Int32 nColumn
) const
1219 if( isValidColumn(nColumn
) )
1220 return maColumns
[nColumn
].mnPos
;
1225 // -----------------------------------------------------------------------------
1227 sal_Int32
TableLayouter::getRowStart( sal_Int32 nRow
) const
1229 if( isValidRow(nRow
) )
1230 return maRows
[nRow
].mnPos
;
1235 // -----------------------------------------------------------------------------
1238 sal_Int32 TableLayouter::detectInsertedOrRemovedRows()
1240 sal_Int32 nHeightChange = 0;
1244 Reference< XIndexAccess > xRows( mxTable->getRows(), UNO_QUERY_THROW );
1245 std::vector< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > >::iterator oldIter( mxRows.begin() );
1246 sal_Int32 nCount = xRows->getCount();
1247 for( sal_Int32 nRow = 0; nRow < nCount; nRow++ )
1249 Reference< XInterface > xRow( xRows->getByIndex(nRow), UNO_QUERY );
1251 std::vector< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > >::iterator searchIter = mxRows.end();
1252 if( oldIter != mxRows.end() )
1253 searchIter = std::find( oldIter,mxRows.end(), xRow );
1255 if( searchIter == mxRows.end() )
1258 Reference< XPropertySet > xSet( xRow, UNO_QUERY_THROW );
1259 sal_Int32 nSize = 0;
1260 xSet->getPropertyValue( msSize ) >>= nSize;
1261 nHeightChange += nSize;
1263 else if( searchIter == oldIter )
1273 Reference< XPropertySet > xSet( (*oldIter), UNO_QUERY_THROW );
1274 sal_Int32 nSize = 0;
1275 xSet->getPropertyValue( msSize ) >>= nSize;
1276 nHeightChange -= nSize;
1278 while( oldIter++ != searchIter );
1282 while( oldIter != mxRows.end() )
1285 Reference< XPropertySet > xSet( (*oldIter++), UNO_QUERY_THROW );
1286 sal_Int32 nSize = 0;
1287 xSet->getPropertyValue( msSize ) >>= nSize;
1288 nHeightChange -= nSize;
1291 catch( Exception& e )
1294 DBG_ERROR("svx::TableLayouter::detectInsertedOrRemovedRows(), exception caught!");
1297 return nHeightChange;
1301 // -----------------------------------------------------------------------------