Adjust includes
[LibreOffice.git] / svx / source / table / tablelayouter.cxx
blobd1de33c1b464f34d4e1f3dfed7a0f4692eda2800
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>
23 #include <tools/gen.hxx>
24 #include <libxml/xmlwriter.h>
26 #include <cell.hxx>
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 )
59 , msSize( "Size" )
64 TableLayouter::~TableLayouter()
66 ClearBorderLayout();
70 basegfx::B2ITuple TableLayouter::getCellSize( const CellRef& xCell, const CellPos& rPos ) const
72 sal_Int32 width = 0;
73 sal_Int32 height = 0;
75 try
77 if( xCell.is() && !xCell->isMerged() )
79 CellPos aPos( rPos );
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 )
86 break;
88 height = o3tl::saturating_add(height, maRows[aPos.mnRow++].mnSize);
89 nRowSpan--;
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 )
97 break;
99 width = o3tl::saturating_add(width, maColumns[aPos.mnCol++].mnSize);
100 nColSpan--;
104 catch( Exception& )
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;
126 sal_Int32 endy;
127 if (o3tl::checked_add(y, aCellSize.getY(), endy))
128 return false;
130 if(bRTL)
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;
134 sal_Int32 startx;
135 if (o3tl::checked_sub(x, aCellSize.getX(), startx))
136 return false;
137 rArea = basegfx::B2IRectangle(startx, y, x, endy);
139 else
141 const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
142 sal_Int32 endx;
143 if (o3tl::checked_add(x, aCellSize.getX(), endx))
144 return false;
145 rArea = basegfx::B2IRectangle(x, y, endx, endy);
147 return true;
151 catch( Exception& )
153 OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
155 return false;
159 sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const
161 if( isValidRow(nRow) )
162 return maRows[nRow].mnSize;
163 else
164 return 0;
168 sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const
170 if( isValidColumn(nColumn) )
171 return maColumns[nColumn].mnSize;
172 else
173 return 0;
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;
186 else
188 OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
191 return false;
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 )
207 pLine = nullptr;
209 else
211 OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
214 return pLine;
218 sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
220 sal_Int32 nRet = 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;
228 if( pnMin )
230 if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) )
232 *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo
234 else
236 *pnMin = nRet;
240 if( pnMax )
242 *pnMax = 0x0fffffff;
244 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;
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) )
310 return true;
312 bRunning = false;
314 return false;
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 )
322 rOriginX = nMergedX;
323 rOriginY = nMergedY;
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() )
330 return true;
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;
343 if( bCheckVert )
345 nRow = nMergedY - nStep;
346 if( nRow >= nMinRow )
348 nCol = nMergedX;
349 for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- )
351 if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) )
353 rOriginX = nCol; rOriginY = nRow;
354 return true;
357 if( !bCheckVert )
359 if( nCol == nMergedX )
361 nMinRow = nRow+1;
363 else
365 bCheckVert = true;
367 break;
371 else
373 bCheckVert = false;
377 if( bCheckHorz )
379 nCol = nMergedX - nStep;
380 if( nCol >= nMinCol )
382 nRow = nMergedY;
383 for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- )
385 if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) )
387 rOriginX = nCol; rOriginY = nRow;
388 return true;
391 if( !bCheckHorz )
393 if( nRow == nMergedY )
395 nMinCol = nCol+1;
397 else
399 bCheckHorz = true;
401 break;
405 else
407 bCheckHorz = false;
410 nStep++;
412 while( bCheckVert || bCheckHorz );
414 catch( Exception& )
416 OSL_FAIL("sdr::table::TableLayouter::findMergeOrigin(), exception caught!");
418 return false;
422 sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn )
424 if( isValidColumn( nColumn ) )
426 return maColumns[nColumn].mnMinSize;
428 else
430 OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
431 return 0;
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();
442 std::size_t nIndex;
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 )
456 sal_Int32 nDiff(0);
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);
487 n /= nCurrentWidth;
490 bConstrainsBroken |= o3tl::checked_add(rLayout.mnSize, n, rLayout.mnSize);
491 nDistributed -= 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;
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();
517 if( nColCount == 0 )
518 return;
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() )
545 bIsEmpty = false;
547 sal_Int32 nColSpan = xCell->getColumnSpan();
548 if( nColSpan > 1 )
550 // merged cells will be evaluated later
551 aMergedCells[nCol+nColSpan-1].push_back( xCell );
553 else
555 nMinWidth = std::max( nMinWidth, xCell->getMinimumWidth() );
560 maColumns[nCol].mnMinSize = nMinWidth;
562 if( bIsEmpty )
564 maColumns[nCol].mnSize = 0;
566 else
568 sal_Int32 nColWidth = 0;
569 Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
570 bool bOptimal = false;
571 xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
572 if( bOptimal )
574 aOptimalColumns.push_back(nCol);
576 else
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() )
601 nDistribute = nLeft;
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;
632 bChanges = true;
636 if( bChanges )
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);
653 if( bFit )
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();
669 if( nRowCount == 0 )
670 return;
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() )
695 bIsEmpty = false;
697 sal_Int32 nRowSpan = xCell->getRowSpan();
698 if( nRowSpan > 1 )
700 // merged cells will be evaluated later
701 aMergedCells[nRow+nRowSpan-1].push_back( xCell );
703 else
705 bool bCellHasText = xCell->hasText();
706 if ( (!bRowHasText && !bCellHasText) || ( bRowHasText && bCellHasText ) )
708 nMinHeight = std::max( nMinHeight, xCell->getMinimumHeight() );
710 else if ( !bRowHasText && bCellHasText )
712 bRowHasText = true;
713 nMinHeight = xCell->getMinimumHeight();
719 maRows[nRow].mnMinSize = nMinHeight;
721 if( bIsEmpty )
723 maRows[nRow].mnSize = 0;
725 else
727 sal_Int32 nRowHeight = 0;
728 Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
730 bool bOptimal = false;
731 xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
732 if( bOptimal )
734 aOptimalRows.push_back( nRow );
736 else
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() )
761 nDistribute = nLeft;
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;
792 bChanges = true;
795 if( bChanges )
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);
810 if( bFit )
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 )
826 if( !mxTable.is() )
827 return;
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();
858 CellPos aPos;
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 ) );
864 if( xCell.is() )
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
885 CellRef xCell;
886 if( mxTable.is() ) try
888 xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
890 catch( Exception& )
892 OSL_FAIL( "sdr::table::TableLayouter::getCell(), exception caught!" );
894 return xCell;
898 bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
900 if (!pThis || ((pThis == &gEmptyBorder) && (pOther != nullptr)))
901 return false;
902 if (!pOther || (pOther == &gEmptyBorder))
903 return true;
905 sal_uInt16 nThisSize = pThis->GetScaledWidth();
906 sal_uInt16 nOtherSize = pOther->GetScaledWidth();
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 void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
934 if (!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)
947 delete pOld;
949 SvxBorderLine* pNew = (pLine != &gEmptyBorder) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
951 rMap[nCol][nRow] = pNew;
954 else
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];
976 if( pLine )
978 if( pLine != &gEmptyBorder )
979 delete pLine;
981 rMap[nCol][nRow] = nullptr;
987 void TableLayouter::ResizeBorderLayout()
989 ClearBorderLayout();
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();
1019 CellPos aPos;
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 ) );
1025 if( !xCell.is() )
1026 continue;
1028 const SvxBoxItem* pThisAttr = xCell->GetItemSet().GetItem<SvxBoxItem>( SDRATTR_TABLE_BORDER );
1029 OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1031 if( !pThisAttr )
1032 continue;
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) )
1060 return;
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 );
1083 catch( Exception& )
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) )
1097 return;
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 );
1133 catch( Exception& )
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: */