1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
23 #include <tools/gen.hxx>
24 #include <libxml/xmlwriter.h>
27 #include "cellrange.hxx"
28 #include <o3tl/safeint.hxx>
29 #include <tablemodel.hxx>
30 #include "tablerow.hxx"
31 #include "tablerows.hxx"
32 #include "tablecolumn.hxx"
33 #include "tablecolumns.hxx"
34 #include "tablelayouter.hxx"
35 #include <svx/svdotable.hxx>
36 #include <editeng/borderline.hxx>
37 #include <editeng/boxitem.hxx>
38 #include <svx/svdmodel.hxx>
39 #include <svx/strings.hrc>
40 #include <svdglob.hxx>
42 using ::editeng::SvxBorderLine
;
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
;
51 namespace sdr
{ namespace table
{
54 static SvxBorderLine gEmptyBorder
;
57 TableLayouter::TableLayouter( const TableModelRef
& xTableModel
)
58 : mxTable( xTableModel
)
64 TableLayouter::~TableLayouter()
70 basegfx::B2ITuple
TableLayouter::getCellSize( const CellRef
& xCell
, const CellPos
& rPos
) const
77 if( xCell
.is() && !xCell
->isMerged() )
81 sal_Int32 nRowCount
= getRowCount();
82 sal_Int32 nRowSpan
= std::max( xCell
->getRowSpan(), (sal_Int32
)1 );
83 while( nRowSpan
&& (aPos
.mnRow
< nRowCount
) )
85 if( ((sal_Int32
)maRows
.size()) <= aPos
.mnRow
)
88 height
= o3tl::saturating_add(height
, maRows
[aPos
.mnRow
++].mnSize
);
92 sal_Int32 nColCount
= getColumnCount();
93 sal_Int32 nColSpan
= std::max( xCell
->getColumnSpan(), (sal_Int32
)1 );
94 while( nColSpan
&& (aPos
.mnCol
< nColCount
) )
96 if( ((sal_Int32
)maColumns
.size()) <= aPos
.mnCol
)
99 width
= o3tl::saturating_add(width
, maColumns
[aPos
.mnCol
++].mnSize
);
106 OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
109 return basegfx::B2ITuple( width
, height
);
113 bool TableLayouter::getCellArea( const CellRef
& xCell
, const CellPos
& rPos
, basegfx::B2IRectangle
& rArea
) const
117 if( xCell
.is() && !xCell
->isMerged() && isValid(rPos
) )
119 const basegfx::B2ITuple
aCellSize( getCellSize( xCell
, rPos
) );
120 const bool bRTL
= (mxTable
->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB
);
122 if( (rPos
.mnCol
< ((sal_Int32
)maColumns
.size())) && (rPos
.mnRow
< ((sal_Int32
)maRows
.size()) ) )
124 const sal_Int32 y
= maRows
[rPos
.mnRow
].mnPos
;
127 if (o3tl::checked_add(y
, aCellSize
.getY(), endy
))
132 ///For RTL Table Calculate the Right End of cell instead of Left
133 const sal_Int32 x
= maColumns
[rPos
.mnCol
].mnPos
+ maColumns
[rPos
.mnCol
].mnSize
;
135 if (o3tl::checked_sub(x
, aCellSize
.getX(), startx
))
137 rArea
= basegfx::B2IRectangle(startx
, y
, x
, endy
);
141 const sal_Int32 x
= maColumns
[rPos
.mnCol
].mnPos
;
143 if (o3tl::checked_add(x
, aCellSize
.getX(), endx
))
145 rArea
= basegfx::B2IRectangle(x
, y
, endx
, endy
);
153 OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
159 sal_Int32
TableLayouter::getRowHeight( sal_Int32 nRow
) const
161 if( isValidRow(nRow
) )
162 return maRows
[nRow
].mnSize
;
168 sal_Int32
TableLayouter::getColumnWidth( sal_Int32 nColumn
) const
170 if( isValidColumn(nColumn
) )
171 return maColumns
[nColumn
].mnSize
;
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
] != nullptr;
188 OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
195 /** returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge */
196 SvxBorderLine
* TableLayouter::getBorderLine( sal_Int32 nEdgeX
, sal_Int32 nEdgeY
, bool bHorizontal
)const
198 SvxBorderLine
* pLine
= nullptr;
200 const BorderLineMap
& rMap
= bHorizontal
? maHorizontalBorders
: maVerticalBorders
;
202 if( (nEdgeX
>= 0) && (nEdgeX
< sal::static_int_cast
<sal_Int32
>(rMap
.size())) &&
203 (nEdgeY
>= 0) && (nEdgeY
< sal::static_int_cast
<sal_Int32
>(rMap
[nEdgeX
].size())) )
205 pLine
= rMap
[nEdgeX
][nEdgeY
];
206 if( pLine
== &gEmptyBorder
)
211 OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
218 sal_Int32
TableLayouter::getHorizontalEdge( int nEdgeY
, sal_Int32
* pnMin
/*= 0*/, sal_Int32
* pnMax
/*= 0*/ )
221 const sal_Int32 nRowCount
= getRowCount();
222 if( (nEdgeY
>= 0) && (nEdgeY
<= nRowCount
) )
223 nRet
= maRows
[std::min((sal_Int32
)nEdgeY
,nRowCount
-1)].mnPos
;
225 if( nEdgeY
== nRowCount
)
226 nRet
+= maRows
[nEdgeY
- 1].mnSize
;
230 if( (nEdgeY
> 0) && (nEdgeY
<= nRowCount
) )
232 *pnMin
= maRows
[nEdgeY
-1].mnPos
+ 600; // todo
248 sal_Int32
TableLayouter::getVerticalEdge( int nEdgeX
, sal_Int32
* pnMin
/*= 0*/, sal_Int32
* pnMax
/*= 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
);
259 if( (nEdgeX
>= 0) && (nEdgeX
< nColCount
) )
260 nRet
+= maColumns
[nEdgeX
].mnSize
;
264 if( nEdgeX
== nColCount
)
265 nRet
+= maColumns
[nEdgeX
- 1].mnSize
;
273 if( nEdgeX
< nColCount
)
274 *pnMin
= nRet
- maColumns
[nEdgeX
].mnSize
+ getMinimumColumnWidth(nEdgeX
);
278 if( (nEdgeX
> 0) && (nEdgeX
<= nColCount
) )
279 *pnMin
= maColumns
[nEdgeX
-1].mnPos
+ getMinimumColumnWidth( nEdgeX
-1 );
285 *pnMax
= 0x0fffffff; // todo
289 *pnMax
= nRet
+ maColumns
[nEdgeX
-1].mnSize
- getMinimumColumnWidth( nEdgeX
-1 );
293 if( (nEdgeX
>= 0) && (nEdgeX
< nColCount
) )
294 *pnMax
= maColumns
[nEdgeX
].mnPos
+ maColumns
[nEdgeX
].mnSize
- getMinimumColumnWidth( nEdgeX
);
302 static bool checkMergeOrigin( const TableModelRef
& xTable
, sal_Int32 nMergedX
, sal_Int32 nMergedY
, sal_Int32 nCellX
, sal_Int32 nCellY
, bool& bRunning
)
304 Reference
< XMergeableCell
> xCell( xTable
->getCellByPosition( nCellX
, nCellY
), UNO_QUERY
);
305 if( xCell
.is() && !xCell
->isMerged() )
307 const sal_Int32 nRight
= xCell
->getColumnSpan() + nCellX
;
308 const sal_Int32 nBottom
= xCell
->getRowSpan() + nCellY
;
309 if( (nMergedX
< nRight
) && (nMergedY
< nBottom
) )
317 /** returns true if the cell(nMergedX,nMergedY) is merged with other cells.
318 the returned cell( rOriginX, rOriginY ) is the origin( top left cell ) of the merge.
320 bool findMergeOrigin( const TableModelRef
& xTable
, sal_Int32 nMergedX
, sal_Int32 nMergedY
, sal_Int32
& rOriginX
, sal_Int32
& rOriginY
)
325 if( xTable
.is() ) try
327 // check if this cell already the origin or not merged at all
328 Reference
< XMergeableCell
> xCell( xTable
->getCellByPosition( nMergedX
, nMergedY
), UNO_QUERY_THROW
);
329 if( !xCell
.is() || !xCell
->isMerged() )
332 bool bCheckVert
= true;
333 bool bCheckHorz
= true;
335 sal_Int32 nMinCol
= 0;
336 sal_Int32 nMinRow
= 0;
338 sal_Int32 nStep
= 1, i
;
340 sal_Int32 nRow
, nCol
;
345 nRow
= nMergedY
- nStep
;
346 if( nRow
>= nMinRow
)
349 for( i
= 0; (i
<= nStep
) && (nCol
>= nMinCol
); i
++, nCol
-- )
351 if( checkMergeOrigin( xTable
, nMergedX
, nMergedY
, nCol
, nRow
, bCheckVert
) )
353 rOriginX
= nCol
; rOriginY
= nRow
;
359 if( nCol
== nMergedX
)
379 nCol
= nMergedX
- nStep
;
380 if( nCol
>= nMinCol
)
383 for( i
= 0; (i
< nStep
) && (nRow
>= nMinRow
); i
++, nRow
-- )
385 if( checkMergeOrigin( xTable
, nMergedX
, nMergedY
, nCol
, nRow
, bCheckHorz
) )
387 rOriginX
= nCol
; rOriginY
= nRow
;
393 if( nRow
== nMergedY
)
412 while( bCheckVert
|| bCheckHorz
);
416 OSL_FAIL("sdr::table::TableLayouter::findMergeOrigin(), exception caught!");
422 sal_Int32
TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn
)
424 if( isValidColumn( nColumn
) )
426 return maColumns
[nColumn
].mnMinSize
;
430 OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
436 sal_Int32
TableLayouter::distribute( LayoutVector
& rLayouts
, sal_Int32 nDistribute
)
438 // break loops after 100 runs to avoid freezing office due to developer error
439 sal_Int32 nSafe
= 100;
441 const std::size_t nCount
= rLayouts
.size();
444 bool bConstrainsBroken
= false;
448 bConstrainsBroken
= false;
450 // first enforce minimum size constrains on all entities
451 for( nIndex
= 0; nIndex
< nCount
; ++nIndex
)
453 Layout
& rLayout
= rLayouts
[nIndex
];
454 if( rLayout
.mnSize
< rLayout
.mnMinSize
)
457 bConstrainsBroken
|= o3tl::checked_sub(rLayout
.mnMinSize
, rLayout
.mnSize
, nDiff
);
458 bConstrainsBroken
|= o3tl::checked_sub(nDistribute
, nDiff
, nDistribute
);
459 rLayout
.mnSize
= rLayout
.mnMinSize
;
463 // calculate current width
464 // if nDistribute is < 0 (shrinking), entities that are already
465 // at minimum width are not counted
466 sal_Int32 nCurrentWidth
= 0;
467 for( nIndex
= 0; nIndex
< nCount
; ++nIndex
)
469 Layout
& rLayout
= rLayouts
[nIndex
];
470 if( (nDistribute
> 0) || (rLayout
.mnSize
> rLayout
.mnMinSize
) )
471 nCurrentWidth
= o3tl::saturating_add(nCurrentWidth
, rLayout
.mnSize
);
474 // now distribute over entities
475 if( (nCurrentWidth
!= 0) && (nDistribute
!= 0) )
477 sal_Int32 nDistributed
= nDistribute
;
478 for( nIndex
= 0; nIndex
< nCount
; ++nIndex
)
480 Layout
& rLayout
= rLayouts
[nIndex
];
481 if( (nDistribute
> 0) || (rLayout
.mnSize
> rLayout
.mnMinSize
) )
483 sal_Int32
n(nDistributed
); // for last entity use up rest
484 if (nIndex
!= (nCount
-1))
486 bConstrainsBroken
|= o3tl::checked_multiply(nDistribute
, rLayout
.mnSize
, n
);
490 bConstrainsBroken
|= o3tl::checked_add(rLayout
.mnSize
, n
, rLayout
.mnSize
);
493 if( rLayout
.mnSize
< rLayout
.mnMinSize
)
494 bConstrainsBroken
= true;
498 } while( bConstrainsBroken
&& --nSafe
);
501 for( nIndex
= 0; nIndex
< nCount
; ++nIndex
)
502 nSize
+= rLayouts
[nIndex
].mnSize
;
508 typedef std::vector
< CellRef
> MergeableCellVector
;
509 typedef std::vector
< MergeableCellVector
> MergeVector
;
510 typedef std::vector
< sal_Int32
> Int32Vector
;
513 void TableLayouter::LayoutTableWidth( tools::Rectangle
& rArea
, bool bFit
)
515 const sal_Int32 nColCount
= getColumnCount();
516 const sal_Int32 nRowCount
= getRowCount();
520 MergeVector
aMergedCells( nColCount
);
521 Int32Vector aOptimalColumns
;
523 const OUString
sOptimalSize("OptimalSize");
525 if( sal::static_int_cast
< sal_Int32
>( maColumns
.size() ) != nColCount
)
526 maColumns
.resize( nColCount
);
528 Reference
< XTableColumns
> xCols( mxTable
->getColumns(), UNO_QUERY_THROW
);
530 // first calculate current width and initial minimum width per column,
531 // merged cells will be counted later
532 sal_Int32 nCurrentWidth
= 0;
533 sal_Int32 nCol
= 0, nRow
= 0;
534 for( nCol
= 0; nCol
< nColCount
; nCol
++ )
536 sal_Int32 nMinWidth
= 0;
538 bool bIsEmpty
= true; // check if all cells in this column are merged
540 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
542 CellRef
xCell( getCell( CellPos( nCol
, nRow
) ) );
543 if( xCell
.is() && !xCell
->isMerged() )
547 sal_Int32 nColSpan
= xCell
->getColumnSpan();
550 // merged cells will be evaluated later
551 aMergedCells
[nCol
+nColSpan
-1].push_back( xCell
);
555 nMinWidth
= std::max( nMinWidth
, xCell
->getMinimumWidth() );
560 maColumns
[nCol
].mnMinSize
= nMinWidth
;
564 maColumns
[nCol
].mnSize
= 0;
568 sal_Int32 nColWidth
= 0;
569 Reference
< XPropertySet
> xColSet( xCols
->getByIndex( nCol
), UNO_QUERY_THROW
);
570 bool bOptimal
= false;
571 xColSet
->getPropertyValue( sOptimalSize
) >>= bOptimal
;
574 aOptimalColumns
.push_back(nCol
);
578 xColSet
->getPropertyValue( msSize
) >>= nColWidth
;
581 maColumns
[nCol
].mnSize
= nColWidth
;
583 if( maColumns
[nCol
].mnSize
< nMinWidth
)
584 maColumns
[nCol
].mnSize
= nMinWidth
;
586 nCurrentWidth
= o3tl::saturating_add(nCurrentWidth
, maColumns
[nCol
].mnSize
);
590 // if we have optimal sized rows, distribute what is given (left)
591 if( !bFit
&& !aOptimalColumns
.empty() && (nCurrentWidth
< rArea
.getWidth()) )
593 sal_Int32 nLeft
= rArea
.getWidth() - nCurrentWidth
;
594 sal_Int32 nDistribute
= nLeft
/ aOptimalColumns
.size();
596 Int32Vector::iterator
iter( aOptimalColumns
.begin() );
597 while( iter
!= aOptimalColumns
.end() )
599 sal_Int32 nOptCol
= (*iter
++);
600 if( iter
== aOptimalColumns
.end() )
603 maColumns
[nOptCol
].mnSize
+= nDistribute
;
604 nLeft
-= nDistribute
;
607 DBG_ASSERT( nLeft
== 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
610 // now check if merged cells fit
611 for( nCol
= 1; nCol
< nColCount
; ++nCol
)
613 bool bChanges
= false;
614 MergeableCellVector::iterator
iter( aMergedCells
[nCol
].begin() );
616 const sal_Int32 nOldSize
= maColumns
[nCol
].mnSize
;
618 while( iter
!= aMergedCells
[nCol
].end() )
620 CellRef
xCell( (*iter
++) );
621 sal_Int32 nMinWidth
= xCell
->getMinimumWidth();
623 for( sal_Int32 nMCol
= nCol
- xCell
->getColumnSpan() + 1; (nMCol
> 0) && (nMCol
< nCol
); ++nMCol
)
624 nMinWidth
-= maColumns
[nMCol
].mnSize
;
626 if( nMinWidth
> maColumns
[nCol
].mnMinSize
)
627 maColumns
[nCol
].mnMinSize
= nMinWidth
;
629 if( nMinWidth
> maColumns
[nCol
].mnSize
)
631 maColumns
[nCol
].mnSize
= nMinWidth
;
637 nCurrentWidth
+= maColumns
[nCol
].mnSize
- nOldSize
;
640 // now scale if wanted and needed
641 if( bFit
&& (nCurrentWidth
!= rArea
.getWidth()) )
642 distribute( maColumns
, rArea
.getWidth() - nCurrentWidth
);
644 // last step, update left edges
645 sal_Int32 nNewWidth
= 0;
647 const bool bRTL
= (mxTable
->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB
);
648 RangeIterator
<sal_Int32
> coliter( 0, nColCount
, !bRTL
);
649 while( coliter
.next(nCol
) )
651 maColumns
[nCol
].mnPos
= nNewWidth
;
652 nNewWidth
= o3tl::saturating_add(nNewWidth
, maColumns
[nCol
].mnSize
);
655 Reference
< XPropertySet
> xColSet( xCols
->getByIndex(nCol
), UNO_QUERY_THROW
);
656 xColSet
->setPropertyValue( msSize
, Any( maColumns
[nCol
].mnSize
) );
660 rArea
.SetSize( Size( nNewWidth
, rArea
.GetHeight() ) );
661 updateCells( rArea
);
665 void TableLayouter::LayoutTableHeight( tools::Rectangle
& rArea
, bool bFit
)
667 const sal_Int32 nColCount
= getColumnCount();
668 const sal_Int32 nRowCount
= getRowCount();
672 Reference
< XTableRows
> xRows( mxTable
->getRows() );
674 MergeVector
aMergedCells( nRowCount
);
675 Int32Vector aOptimalRows
;
677 const OUString
sOptimalSize("OptimalSize");
679 // first calculate current height and initial minimum size per column,
680 // merged cells will be counted later
681 sal_Int32 nCurrentHeight
= 0;
682 sal_Int32 nCol
, nRow
;
683 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
685 sal_Int32 nMinHeight
= 0;
687 bool bIsEmpty
= true; // check if all cells in this row are merged
688 bool bRowHasText
= false;
690 for( nCol
= 0; nCol
< nColCount
; ++nCol
)
692 CellRef
xCell( getCell( CellPos( nCol
, nRow
) ) );
693 if( xCell
.is() && !xCell
->isMerged() )
697 sal_Int32 nRowSpan
= xCell
->getRowSpan();
700 // merged cells will be evaluated later
701 aMergedCells
[nRow
+nRowSpan
-1].push_back( xCell
);
705 bool bCellHasText
= xCell
->hasText();
706 if ( (!bRowHasText
&& !bCellHasText
) || ( bRowHasText
&& bCellHasText
) )
708 nMinHeight
= std::max( nMinHeight
, xCell
->getMinimumHeight() );
710 else if ( !bRowHasText
&& bCellHasText
)
713 nMinHeight
= xCell
->getMinimumHeight();
719 maRows
[nRow
].mnMinSize
= nMinHeight
;
723 maRows
[nRow
].mnSize
= 0;
727 sal_Int32 nRowHeight
= 0;
728 Reference
< XPropertySet
> xRowSet( xRows
->getByIndex(nRow
), UNO_QUERY_THROW
);
730 bool bOptimal
= false;
731 xRowSet
->getPropertyValue( sOptimalSize
) >>= bOptimal
;
734 aOptimalRows
.push_back( nRow
);
738 xRowSet
->getPropertyValue( msSize
) >>= nRowHeight
;
741 maRows
[nRow
].mnSize
= nRowHeight
;
743 if( maRows
[nRow
].mnSize
< nMinHeight
)
744 maRows
[nRow
].mnSize
= nMinHeight
;
746 nCurrentHeight
= o3tl::saturating_add(nCurrentHeight
, maRows
[nRow
].mnSize
);
750 // if we have optimal sized rows, distribute what is given (left)
751 if( !bFit
&& !aOptimalRows
.empty() && (nCurrentHeight
< rArea
.getHeight()) )
753 sal_Int32 nLeft
= rArea
.getHeight() - nCurrentHeight
;
754 sal_Int32 nDistribute
= nLeft
/ aOptimalRows
.size();
756 Int32Vector::iterator
iter( aOptimalRows
.begin() );
757 while( iter
!= aOptimalRows
.end() )
759 sal_Int32 nOptRow
= (*iter
++);
760 if( iter
== aOptimalRows
.end() )
763 maRows
[nOptRow
].mnSize
+= nDistribute
;
764 nLeft
-= nDistribute
;
768 DBG_ASSERT( nLeft
== 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
771 // now check if merged cells fit
772 for( nRow
= 1; nRow
< nRowCount
; ++nRow
)
774 bool bChanges
= false;
775 sal_Int32 nOldSize
= maRows
[nRow
].mnSize
;
777 MergeableCellVector::iterator
iter( aMergedCells
[nRow
].begin() );
778 while( iter
!= aMergedCells
[nRow
].end() )
780 CellRef
xCell( (*iter
++) );
781 sal_Int32 nMinHeight
= xCell
->getMinimumHeight();
783 for( sal_Int32 nMRow
= nRow
- xCell
->getRowSpan() + 1; (nMRow
> 0) && (nMRow
< nRow
); ++nMRow
)
784 nMinHeight
-= maRows
[nMRow
].mnSize
;
786 if( nMinHeight
> maRows
[nRow
].mnMinSize
)
787 maRows
[nRow
].mnMinSize
= nMinHeight
;
789 if( nMinHeight
> maRows
[nRow
].mnSize
)
791 maRows
[nRow
].mnSize
= nMinHeight
;
796 nCurrentHeight
+= maRows
[nRow
].mnSize
- nOldSize
;
799 // now scale if wanted and needed
800 if( bFit
&& nCurrentHeight
!= rArea
.getHeight() )
801 distribute( maRows
, rArea
.getHeight() - nCurrentHeight
);
803 // last step, update left edges
804 sal_Int32 nNewHeight
= 0;
805 for( nRow
= 0; nRow
< nRowCount
; ++nRow
)
807 maRows
[nRow
].mnPos
= nNewHeight
;
808 nNewHeight
= o3tl::saturating_add(nNewHeight
, maRows
[nRow
].mnSize
);
812 Reference
< XPropertySet
> xRowSet( xRows
->getByIndex(nRow
), UNO_QUERY_THROW
);
813 xRowSet
->setPropertyValue( msSize
, Any( maRows
[nRow
].mnSize
) );
817 rArea
.SetSize( Size( rArea
.GetWidth(), nNewHeight
) );
818 updateCells( rArea
);
822 /** try to fit the table into the given rectangle.
823 If the rectangle is to small, it will be grown to fit the table. */
824 void TableLayouter::LayoutTable( tools::Rectangle
& rRectangle
, bool bFitWidth
, bool bFitHeight
)
829 const sal_Int32 nRowCount
= mxTable
->getRowCount();
830 const sal_Int32 nColCount
= mxTable
->getColumnCount();
832 if( (nRowCount
!= getRowCount()) || (nColCount
!= getColumnCount()) )
834 if( static_cast< sal_Int32
>( maRows
.size() ) != nRowCount
)
835 maRows
.resize( nRowCount
);
837 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; nRow
++ )
838 maRows
[nRow
].clear();
840 if( static_cast< sal_Int32
>( maColumns
.size() ) != nColCount
)
841 maColumns
.resize( nColCount
);
843 for( sal_Int32 nCol
= 0; nCol
< nColCount
; nCol
++ )
844 maColumns
[nCol
].clear();
847 LayoutTableWidth( rRectangle
, bFitWidth
);
848 LayoutTableHeight( rRectangle
, bFitHeight
);
849 UpdateBorderLayout();
853 void TableLayouter::updateCells( tools::Rectangle
& rRectangle
)
855 const sal_Int32 nColCount
= getColumnCount();
856 const sal_Int32 nRowCount
= getRowCount();
859 for( aPos
.mnRow
= 0; aPos
.mnRow
< nRowCount
; aPos
.mnRow
++ )
861 for( aPos
.mnCol
= 0; aPos
.mnCol
< nColCount
; aPos
.mnCol
++ )
863 CellRef
xCell( getCell( aPos
) );
866 basegfx::B2IRectangle aCellArea
;
867 if( getCellArea( xCell
, aPos
, aCellArea
) )
869 tools::Rectangle aCellRect
;
870 aCellRect
.Left() = aCellArea
.getMinX();
871 aCellRect
.Right() = aCellArea
.getMaxX();
872 aCellRect
.Top() = aCellArea
.getMinY();
873 aCellRect
.Bottom() = aCellArea
.getMaxY();
874 aCellRect
.Move( rRectangle
.Left(), rRectangle
.Top() );
875 xCell
->setCellRect( aCellRect
);
883 CellRef
TableLayouter::getCell( const CellPos
& rPos
) const
886 if( mxTable
.is() ) try
888 xCell
.set( dynamic_cast< Cell
* >( mxTable
->getCellByPosition( rPos
.mnCol
, rPos
.mnRow
).get() ) );
892 OSL_FAIL( "sdr::table::TableLayouter::getCell(), exception caught!" );
898 bool TableLayouter::HasPriority( const SvxBorderLine
* pThis
, const SvxBorderLine
* pOther
)
900 if (!pThis
|| ((pThis
== &gEmptyBorder
) && (pOther
!= nullptr)))
902 if (!pOther
|| (pOther
== &gEmptyBorder
))
905 sal_uInt16 nThisSize
= pThis
->GetScaledWidth();
906 sal_uInt16 nOtherSize
= pOther
->GetScaledWidth();
908 if (nThisSize
> nOtherSize
)
911 else if (nThisSize
< nOtherSize
)
917 if ( pOther
->GetInWidth() && !pThis
->GetInWidth() )
921 else if ( pThis
->GetInWidth() && !pOther
->GetInWidth() )
932 void TableLayouter::SetBorder( sal_Int32 nCol
, sal_Int32 nRow
, bool bHorizontal
, const SvxBorderLine
* pLine
)
935 pLine
= &gEmptyBorder
;
937 BorderLineMap
& rMap
= bHorizontal
? maHorizontalBorders
: maVerticalBorders
;
939 if( (nCol
>= 0) && (nCol
< sal::static_int_cast
<sal_Int32
>(rMap
.size())) &&
940 (nRow
>= 0) && (nRow
< sal::static_int_cast
<sal_Int32
>(rMap
[nCol
].size())) )
942 SvxBorderLine
*pOld
= rMap
[nCol
][nRow
];
944 if (HasPriority(pLine
, pOld
))
946 if (pOld
&& pOld
!= &gEmptyBorder
)
949 SvxBorderLine
* pNew
= (pLine
!= &gEmptyBorder
) ? new SvxBorderLine(*pLine
) : &gEmptyBorder
;
951 rMap
[nCol
][nRow
] = pNew
;
956 OSL_FAIL( "sdr::table::TableLayouter::SetBorder(), invalid border!" );
960 void TableLayouter::ClearBorderLayout()
962 ClearBorderLayout(maHorizontalBorders
);
963 ClearBorderLayout(maVerticalBorders
);
966 void TableLayouter::ClearBorderLayout(BorderLineMap
& rMap
)
968 const sal_Int32 nColCount
= rMap
.size();
970 for( sal_Int32 nCol
= 0; nCol
< nColCount
; nCol
++ )
972 const sal_Int32 nRowCount
= rMap
[nCol
].size();
973 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; nRow
++ )
975 SvxBorderLine
* pLine
= rMap
[nCol
][nRow
];
978 if( pLine
!= &gEmptyBorder
)
981 rMap
[nCol
][nRow
] = nullptr;
987 void TableLayouter::ResizeBorderLayout()
990 ResizeBorderLayout(maHorizontalBorders
);
991 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
);
1011 void TableLayouter::UpdateBorderLayout()
1013 // make sure old border layout is cleared and border maps have correct size
1014 ResizeBorderLayout();
1016 const sal_Int32 nColCount
= getColumnCount();
1017 const sal_Int32 nRowCount
= getRowCount();
1020 for( aPos
.mnRow
= 0; aPos
.mnRow
< nRowCount
; aPos
.mnRow
++ )
1022 for( aPos
.mnCol
= 0; aPos
.mnCol
< nColCount
; aPos
.mnCol
++ )
1024 CellRef
xCell( getCell( aPos
) );
1028 const SvxBoxItem
* pThisAttr
= xCell
->GetItemSet().GetItem
<SvxBoxItem
>( SDRATTR_TABLE_BORDER
);
1029 OSL_ENSURE(pThisAttr
,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1034 const sal_Int32 nLastRow
= xCell
->getRowSpan() + aPos
.mnRow
;
1035 const sal_Int32 nLastCol
= xCell
->getColumnSpan() + aPos
.mnCol
;
1037 for( sal_Int32 nRow
= aPos
.mnRow
; nRow
< nLastRow
; nRow
++ )
1039 SetBorder( aPos
.mnCol
, nRow
, false, pThisAttr
->GetLeft() );
1040 SetBorder( nLastCol
, nRow
, false, pThisAttr
->GetRight() );
1043 for( sal_Int32 nCol
= aPos
.mnCol
; nCol
< nLastCol
; nCol
++ )
1045 SetBorder( nCol
, aPos
.mnRow
, true, pThisAttr
->GetTop() );
1046 SetBorder( nCol
, nLastRow
, true, pThisAttr
->GetBottom() );
1053 void TableLayouter::DistributeColumns( ::tools::Rectangle
& rArea
, sal_Int32 nFirstCol
, sal_Int32 nLastCol
)
1055 if( mxTable
.is() ) try
1057 const sal_Int32 nColCount
= getColumnCount();
1059 if( (nFirstCol
< 0) || (nFirstCol
>= nLastCol
) || (nLastCol
>= nColCount
) )
1062 sal_Int32 nAllWidth
= 0;
1063 for( sal_Int32 nCol
= nFirstCol
; nCol
<= nLastCol
; ++nCol
)
1064 nAllWidth
+= getColumnWidth(nCol
);
1066 sal_Int32 nWidth
= nAllWidth
/ (nLastCol
-nFirstCol
+1);
1068 Reference
< XTableColumns
> xCols( mxTable
->getColumns(), UNO_QUERY_THROW
);
1070 for( sal_Int32 nCol
= nFirstCol
; nCol
<= nLastCol
; ++nCol
)
1072 if( nCol
== nLastCol
)
1073 nWidth
= nAllWidth
; // last column get round errors
1075 Reference
< XPropertySet
> xColSet( xCols
->getByIndex( nCol
), UNO_QUERY_THROW
);
1076 xColSet
->setPropertyValue( msSize
, Any( nWidth
) );
1078 nAllWidth
-= nWidth
;
1081 LayoutTable( rArea
, true, false );
1085 OSL_FAIL("sdr::table::TableLayouter::DistributeColumns(), exception caught!");
1090 void TableLayouter::DistributeRows( ::tools::Rectangle
& rArea
, sal_Int32 nFirstRow
, sal_Int32 nLastRow
)
1092 if( mxTable
.is() ) try
1094 const sal_Int32 nRowCount
= mxTable
->getRowCount();
1096 if( (nFirstRow
< 0) || (nFirstRow
>= nLastRow
) || (nLastRow
>= nRowCount
) )
1099 sal_Int32 nAllHeight
= 0;
1100 sal_Int32 nMinHeight
= 0;
1102 for( sal_Int32 nRow
= nFirstRow
; nRow
<= nLastRow
; ++nRow
)
1104 nMinHeight
= std::max( maRows
[nRow
].mnMinSize
, nMinHeight
);
1105 nAllHeight
+= maRows
[nRow
].mnSize
;
1108 const sal_Int32 nRows
= (nLastRow
-nFirstRow
+1);
1109 sal_Int32 nHeight
= nAllHeight
/ nRows
;
1111 if( nHeight
< nMinHeight
)
1113 sal_Int32 nNeededHeight
= nRows
* nMinHeight
;
1114 rArea
.Bottom() += nNeededHeight
- nAllHeight
;
1115 nHeight
= nMinHeight
;
1116 nAllHeight
= nRows
* nMinHeight
;
1119 Reference
< XTableRows
> xRows( mxTable
->getRows(), UNO_QUERY_THROW
);
1120 for( sal_Int32 nRow
= nFirstRow
; nRow
<= nLastRow
; ++nRow
)
1122 if( nRow
== nLastRow
)
1123 nHeight
= nAllHeight
; // last row get round errors
1125 Reference
< XPropertySet
> xRowSet( xRows
->getByIndex( nRow
), UNO_QUERY_THROW
);
1126 xRowSet
->setPropertyValue( msSize
, Any( nHeight
) );
1128 nAllHeight
-= nHeight
;
1131 LayoutTable( rArea
, false, true );
1135 OSL_FAIL("sdr::table::TableLayouter::DistributeRows(), exception caught!");
1139 void TableLayouter::dumpAsXml(xmlTextWriterPtr pWriter
) const
1141 xmlTextWriterStartElement(pWriter
, BAD_CAST("TableLayouter"));
1143 xmlTextWriterStartElement(pWriter
, BAD_CAST("columns"));
1144 for (const auto& rColumn
: maColumns
)
1145 rColumn
.dumpAsXml(pWriter
);
1146 xmlTextWriterEndElement(pWriter
);
1148 xmlTextWriterStartElement(pWriter
, BAD_CAST("rows"));
1149 for (const auto& rRow
: maRows
)
1150 rRow
.dumpAsXml(pWriter
);
1151 xmlTextWriterEndElement(pWriter
);
1153 xmlTextWriterEndElement(pWriter
);
1156 void TableLayouter::Layout::dumpAsXml(xmlTextWriterPtr pWriter
) const
1158 xmlTextWriterStartElement(pWriter
, BAD_CAST("TableLayouter_Layout"));
1160 xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("pos"), BAD_CAST(OString::number(mnPos
).getStr()));
1161 xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("size"), BAD_CAST(OString::number(mnSize
).getStr()));
1162 xmlTextWriterWriteAttribute(pWriter
, BAD_CAST("minSize"), BAD_CAST(OString::number(mnMinSize
).getStr()));
1164 xmlTextWriterEndElement(pWriter
);
1169 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */