LibreOffice Module svx (master)  1
tablelayouter.cxx
Go to the documentation of this file.
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 
21 #include <com/sun/star/table/XMergeableCell.hpp>
22 
23 #include <tools/debug.hxx>
24 #include <tools/gen.hxx>
25 #include <libxml/xmlwriter.h>
26 
27 #include <cell.hxx>
28 #include <o3tl/safeint.hxx>
29 #include <tablemodel.hxx>
30 #include "tablelayouter.hxx"
31 #include <svx/svdotable.hxx>
32 #include <editeng/borderline.hxx>
33 #include <editeng/boxitem.hxx>
34 
35 using ::editeng::SvxBorderLine;
36 using namespace ::com::sun::star::uno;
37 using namespace ::com::sun::star::lang;
38 using namespace ::com::sun::star::container;
39 using namespace ::com::sun::star::beans;
40 using namespace ::com::sun::star::table;
41 using namespace ::com::sun::star::text;
42 
43 
44 namespace sdr::table {
45 
46 
47 static SvxBorderLine gEmptyBorder;
48 
49 const OUStringLiteral gsSize( "Size" );
50 
52 : mxTable( xTableModel )
53 {
54 }
55 
56 
58 {
60 }
61 
62 
63 basegfx::B2ITuple TableLayouter::getCellSize( const CellRef& xCell, const CellPos& rPos ) const
64 {
65  sal_Int32 width = 0;
66  sal_Int32 height = 0;
67 
68  try
69  {
70  if( xCell.is() && !xCell->isMerged() )
71  {
72  CellPos aPos( rPos );
73 
74  sal_Int32 nRowCount = getRowCount();
75  sal_Int32 nRowSpan = std::max( xCell->getRowSpan(), sal_Int32(1) );
76  while( nRowSpan && (aPos.mnRow < nRowCount) )
77  {
78  if( static_cast<sal_Int32>(maRows.size()) <= aPos.mnRow )
79  break;
80 
81  height = o3tl::saturating_add(height, maRows[aPos.mnRow++].mnSize);
82  nRowSpan--;
83  }
84 
85  sal_Int32 nColCount = getColumnCount();
86  sal_Int32 nColSpan = std::max( xCell->getColumnSpan(), sal_Int32(1) );
87  while( nColSpan && (aPos.mnCol < nColCount ) )
88  {
89  if( static_cast<sal_Int32>(maColumns.size()) <= aPos.mnCol )
90  break;
91 
92  width = o3tl::saturating_add(width, maColumns[aPos.mnCol++].mnSize);
93  nColSpan--;
94  }
95  }
96  }
97  catch( Exception& )
98  {
99  OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
100  }
101 
102  return basegfx::B2ITuple( width, height );
103 }
104 
105 
106 bool TableLayouter::getCellArea( const CellRef& xCell, const CellPos& rPos, basegfx::B2IRectangle& rArea ) const
107 {
108  try
109  {
110  if( xCell.is() && !xCell->isMerged() && isValid(rPos) )
111  {
112  const basegfx::B2ITuple aCellSize( getCellSize( xCell, rPos ) );
113  const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
114 
115  if( (rPos.mnCol < static_cast<sal_Int32>(maColumns.size())) && (rPos.mnRow < static_cast<sal_Int32>(maRows.size()) ) )
116  {
117  const sal_Int32 y = maRows[rPos.mnRow].mnPos;
118 
119  sal_Int32 endy;
120  if (o3tl::checked_add(y, aCellSize.getY(), endy))
121  return false;
122 
123  if(bRTL)
124  {
126  const sal_Int32 x = maColumns[rPos.mnCol].mnPos + maColumns[rPos.mnCol].mnSize;
127  sal_Int32 startx;
128  if (o3tl::checked_sub(x, aCellSize.getX(), startx))
129  return false;
130  rArea = basegfx::B2IRectangle(startx, y, x, endy);
131  }
132  else
133  {
134  const sal_Int32 x = maColumns[rPos.mnCol].mnPos;
135  sal_Int32 endx;
136  if (o3tl::checked_add(x, aCellSize.getX(), endx))
137  return false;
138  rArea = basegfx::B2IRectangle(x, y, endx, endy);
139  }
140  return true;
141  }
142  }
143  }
144  catch( Exception& )
145  {
146  OSL_FAIL( "TableLayouter::getCellSize(), exception caught!" );
147  }
148  return false;
149 }
150 
151 
152 sal_Int32 TableLayouter::getRowHeight( sal_Int32 nRow ) const
153 {
154  if( isValidRow(nRow) )
155  return maRows[nRow].mnSize;
156  else
157  return 0;
158 }
159 
160 
161 sal_Int32 TableLayouter::getColumnWidth( sal_Int32 nColumn ) const
162 {
163  if( isValidColumn(nColumn) )
164  return maColumns[nColumn].mnSize;
165  else
166  return 0;
167 }
168 
169 sal_Int32 TableLayouter::calcPreferredColumnWidth( sal_Int32 nColumn, Size aSize ) const
170 {
171  sal_Int32 nRet = 0;
172  for ( sal_uInt32 nRow = 0; nRow < static_cast<sal_uInt32>(maRows.size()); ++nRow )
173  {
174  // Account for the space desired by spanned columns.
175  // Only the last spanned cell will try to ensure sufficient space,
176  // by looping through the previous columns, subtracting their portion.
177  sal_Int32 nWish = 0;
178  sal_Int32 nSpannedColumn = nColumn;
179  bool bFindSpan = true;
180  while ( bFindSpan && isValidColumn(nSpannedColumn) )
181  {
182  // recursive function call gets earlier portion of spanned column.
183  if ( nSpannedColumn < nColumn )
184  nWish -= calcPreferredColumnWidth( nSpannedColumn, aSize );
185 
186  CellRef xCell( getCell( CellPos( nSpannedColumn, nRow ) ) );
187  if ( xCell.is() && !xCell->isMerged()
188  && (xCell->getColumnSpan() == 1 || nSpannedColumn < nColumn) )
189  {
190  nWish += xCell->calcPreferredWidth(aSize);
191  bFindSpan = false;
192  }
193  else if ( xCell.is() && xCell->isMerged()
194  && nColumn == nSpannedColumn
195  && isValidColumn(nColumn + 1) )
196  {
197  xCell = getCell( CellPos( nColumn + 1, nRow ) );
198  bFindSpan = xCell.is() && !xCell->isMerged();
199  }
200  nSpannedColumn--;
201  }
202  nRet = std::max( nRet, nWish );
203  }
204  return nRet;
205 }
206 
207 
208 bool TableLayouter::isEdgeVisible( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal ) const
209 {
210  const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
211 
212  if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
213  (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
214  {
215  return rMap[nEdgeX][nEdgeY] != nullptr;
216  }
217  else
218  {
219  OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
220  }
221 
222  return false;
223 }
224 
225 
227 SvxBorderLine* TableLayouter::getBorderLine( sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal )const
228 {
229  SvxBorderLine* pLine = nullptr;
230 
231  const BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
232 
233  if( (nEdgeX >= 0) && (nEdgeX < sal::static_int_cast<sal_Int32>(rMap.size())) &&
234  (nEdgeY >= 0) && (nEdgeY < sal::static_int_cast<sal_Int32>(rMap[nEdgeX].size())) )
235  {
236  pLine = rMap[nEdgeX][nEdgeY];
237  if( pLine == &gEmptyBorder )
238  pLine = nullptr;
239  }
240  else
241  {
242  OSL_FAIL( "sdr::table::TableLayouter::getBorderLine(), invalid edge!" );
243  }
244 
245  return pLine;
246 }
247 
248 std::vector<EdgeInfo> TableLayouter::getHorizontalEdges()
249 {
250  std::vector<EdgeInfo> aReturn;
251  sal_Int32 nRowSize = sal_Int32(maRows.size());
252  for (sal_Int32 i = 0; i <= nRowSize; i++)
253  {
254  sal_Int32 nEdgeMin = 0;
255  sal_Int32 nEdgeMax = 0;
256  sal_Int32 nEdge = getHorizontalEdge(i, &nEdgeMin, &nEdgeMax);
257  nEdgeMin -= nEdge;
258  nEdgeMax -= nEdge;
259  aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax);
260  }
261  return aReturn;
262 }
263 
264 std::vector<EdgeInfo> TableLayouter::getVerticalEdges()
265 {
266  std::vector<EdgeInfo> aReturn;
267  sal_Int32 nColumnSize = sal_Int32(maColumns.size());
268  for (sal_Int32 i = 0; i <= nColumnSize; i++)
269  {
270  sal_Int32 nEdgeMin = 0;
271  sal_Int32 nEdgeMax = 0;
272  sal_Int32 nEdge = getVerticalEdge(i, &nEdgeMin, &nEdgeMax);
273  nEdgeMin -= nEdge;
274  nEdgeMax -= nEdge;
275  aReturn.emplace_back(i, nEdge, nEdgeMin, nEdgeMax);
276  }
277  return aReturn;
278 }
279 
280 sal_Int32 TableLayouter::getHorizontalEdge( int nEdgeY, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
281 {
282  sal_Int32 nRet = 0;
283  const sal_Int32 nRowCount = getRowCount();
284  if( (nEdgeY >= 0) && (nEdgeY <= nRowCount ) )
285  nRet = maRows[std::min(static_cast<sal_Int32>(nEdgeY),nRowCount-1)].mnPos;
286 
287  if( nEdgeY == nRowCount )
288  nRet += maRows[nEdgeY - 1].mnSize;
289 
290  if( pnMin )
291  {
292  if( (nEdgeY > 0) && (nEdgeY <= nRowCount ) )
293  {
294  *pnMin = maRows[nEdgeY-1].mnPos + 600; // todo
295  }
296  else
297  {
298  *pnMin = nRet;
299  }
300  }
301 
302  if( pnMax )
303  {
304  *pnMax = 0x0fffffff;
305  }
306  return nRet;
307 }
308 
309 
310 sal_Int32 TableLayouter::getVerticalEdge( int nEdgeX, sal_Int32* pnMin /*= 0*/, sal_Int32* pnMax /*= 0*/ )
311 {
312  sal_Int32 nRet = 0;
313 
314  const sal_Int32 nColCount = getColumnCount();
315  if( (nEdgeX >= 0) && (nEdgeX <= nColCount ) )
316  nRet = maColumns[std::min(static_cast<sal_Int32>(nEdgeX),nColCount-1)].mnPos;
317 
318  const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
319  if( bRTL )
320  {
321  if( (nEdgeX >= 0) && (nEdgeX < nColCount) )
322  nRet += maColumns[nEdgeX].mnSize;
323  }
324  else
325  {
326  if( nEdgeX == nColCount )
327  nRet += maColumns[nEdgeX - 1].mnSize;
328  }
329 
330  if( pnMin )
331  {
332  *pnMin = nRet;
333  if( bRTL )
334  {
335  if( nEdgeX < nColCount )
336  *pnMin = nRet - maColumns[nEdgeX].mnSize + getMinimumColumnWidth(nEdgeX);
337  }
338  else
339  {
340  if( (nEdgeX > 0) && (nEdgeX <= nColCount ) )
341  *pnMin = maColumns[nEdgeX-1].mnPos + getMinimumColumnWidth( nEdgeX-1 );
342  }
343  }
344 
345  if( pnMax )
346  {
347  *pnMax = 0x0fffffff; // todo
348  if( bRTL )
349  {
350  if( nEdgeX > 0 )
351  *pnMax = nRet + maColumns[nEdgeX-1].mnSize - getMinimumColumnWidth( nEdgeX-1 );
352  }
353  else
354  {
355  if( (nEdgeX >= 0) && (nEdgeX < nColCount ) )
356  *pnMax = maColumns[nEdgeX].mnPos + maColumns[nEdgeX].mnSize - getMinimumColumnWidth( nEdgeX );
357  }
358  }
359 
360  return nRet;
361 }
362 
363 
364 static bool checkMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool& bRunning )
365 {
366  Reference< XMergeableCell > xCell( xTable->getCellByPosition( nCellX, nCellY ), UNO_QUERY );
367  if( xCell.is() && !xCell->isMerged() )
368  {
369  const sal_Int32 nRight = xCell->getColumnSpan() + nCellX;
370  const sal_Int32 nBottom = xCell->getRowSpan() + nCellY;
371  if( (nMergedX < nRight) && (nMergedY < nBottom) )
372  return true;
373 
374  bRunning = false;
375  }
376  return false;
377 }
378 
382 bool findMergeOrigin( const TableModelRef& xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32& rOriginX, sal_Int32& rOriginY )
383 {
384  rOriginX = nMergedX;
385  rOriginY = nMergedY;
386 
387  if( xTable.is() ) try
388  {
389  // check if this cell already the origin or not merged at all
390  Reference< XMergeableCell > xCell( xTable->getCellByPosition( nMergedX, nMergedY ), UNO_QUERY_THROW );
391  if( !xCell->isMerged() )
392  return true;
393 
394  bool bCheckVert = true;
395  bool bCheckHorz = true;
396 
397  sal_Int32 nMinCol = 0;
398  sal_Int32 nMinRow = 0;
399 
400  sal_Int32 nStep = 1, i;
401 
402  sal_Int32 nRow, nCol;
403  do
404  {
405  if( bCheckVert )
406  {
407  nRow = nMergedY - nStep;
408  if( nRow >= nMinRow )
409  {
410  nCol = nMergedX;
411  for( i = 0; (i <= nStep) && (nCol >= nMinCol); i++, nCol-- )
412  {
413  if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckVert ) )
414  {
415  rOriginX = nCol; rOriginY = nRow;
416  return true;
417  }
418 
419  if( !bCheckVert )
420  {
421  if( nCol == nMergedX )
422  {
423  nMinRow = nRow+1;
424  }
425  else
426  {
427  bCheckVert = true;
428  }
429  break;
430  }
431  }
432  }
433  else
434  {
435  bCheckVert = false;
436  }
437  }
438 
439  if( bCheckHorz )
440  {
441  nCol = nMergedX - nStep;
442  if( nCol >= nMinCol )
443  {
444  nRow = nMergedY;
445  for( i = 0; (i < nStep) && (nRow >= nMinRow); i++, nRow-- )
446  {
447  if( checkMergeOrigin( xTable, nMergedX, nMergedY, nCol, nRow, bCheckHorz ) )
448  {
449  rOriginX = nCol; rOriginY = nRow;
450  return true;
451  }
452 
453  if( !bCheckHorz )
454  {
455  if( nRow == nMergedY )
456  {
457  nMinCol = nCol+1;
458  }
459  else
460  {
461  bCheckHorz = true;
462  }
463  break;
464  }
465  }
466  }
467  else
468  {
469  bCheckHorz = false;
470  }
471  }
472  nStep++;
473  }
474  while( bCheckVert || bCheckHorz );
475  }
476  catch( Exception& )
477  {
478  OSL_FAIL("sdr::table::TableLayouter::findMergeOrigin(), exception caught!");
479  }
480  return false;
481 }
482 
483 
484 sal_Int32 TableLayouter::getMinimumColumnWidth( sal_Int32 nColumn )
485 {
486  if( isValidColumn( nColumn ) )
487  {
488  return maColumns[nColumn].mnMinSize;
489  }
490  else
491  {
492  OSL_FAIL( "TableLayouter::getMinimumColumnWidth(), column out of range!" );
493  return 0;
494  }
495 }
496 
497 
498 sal_Int32 TableLayouter::distribute( LayoutVector& rLayouts, sal_Int32 nDistribute )
499 {
500  // break loops after 100 runs to avoid freezing office due to developer error
501  sal_Int32 nSafe = 100;
502 
503  const std::size_t nCount = rLayouts.size();
504  std::size_t nIndex;
505 
506  bool bConstrainsBroken = false;
507 
508  do
509  {
510  bConstrainsBroken = false;
511 
512  // first enforce minimum size constrains on all entities
513  for( nIndex = 0; nIndex < nCount; ++nIndex )
514  {
515  Layout& rLayout = rLayouts[nIndex];
516  if( rLayout.mnSize < rLayout.mnMinSize )
517  {
518  sal_Int32 nDiff(0);
519  bConstrainsBroken |= o3tl::checked_sub(rLayout.mnMinSize, rLayout.mnSize, nDiff);
520  bConstrainsBroken |= o3tl::checked_sub(nDistribute, nDiff, nDistribute);
521  rLayout.mnSize = rLayout.mnMinSize;
522  }
523  }
524 
525  // calculate current width
526  // if nDistribute is < 0 (shrinking), entities that are already
527  // at minimum width are not counted
528  sal_Int32 nCurrentWidth = 0;
529  for( nIndex = 0; nIndex < nCount; ++nIndex )
530  {
531  Layout& rLayout = rLayouts[nIndex];
532  if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
533  nCurrentWidth = o3tl::saturating_add(nCurrentWidth, rLayout.mnSize);
534  }
535 
536  // now distribute over entities
537  if( (nCurrentWidth != 0) && (nDistribute != 0) )
538  {
539  sal_Int32 nDistributed = nDistribute;
540  for( nIndex = 0; nIndex < nCount; ++nIndex )
541  {
542  Layout& rLayout = rLayouts[nIndex];
543  if( (nDistribute > 0) || (rLayout.mnSize > rLayout.mnMinSize) )
544  {
545  sal_Int32 n(nDistributed); // for last entity use up rest
546  if (nIndex != (nCount-1))
547  {
548  bConstrainsBroken |= o3tl::checked_multiply(nDistribute, rLayout.mnSize, n);
549  n /= nCurrentWidth;
550  }
551 
552  bConstrainsBroken |= o3tl::checked_add(rLayout.mnSize, n, rLayout.mnSize);
553  nDistributed -= n;
554 
555  if( rLayout.mnSize < rLayout.mnMinSize )
556  bConstrainsBroken = true;
557  }
558  }
559  }
560  } while( bConstrainsBroken && --nSafe );
561 
562  sal_Int32 nSize = 0;
563  for( nIndex = 0; nIndex < nCount; ++nIndex )
564  nSize = o3tl::saturating_add(nSize, rLayouts[nIndex].mnSize);
565 
566  return nSize;
567 }
568 
569 
570 typedef std::vector< CellRef > MergeableCellVector;
571 typedef std::vector< MergeableCellVector > MergeVector;
572 
573 
575 {
576  const sal_Int32 nColCount = getColumnCount();
577  const sal_Int32 nRowCount = getRowCount();
578  if( nColCount == 0 )
579  return;
580 
581  MergeVector aMergedCells( nColCount );
582  std::vector<sal_Int32> aOptimalColumns;
583 
584  const OUString sOptimalSize("OptimalSize");
585 
586  if( sal::static_int_cast< sal_Int32 >( maColumns.size() ) != nColCount )
587  maColumns.resize( nColCount );
588 
589  Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
590 
591  // first calculate current width and initial minimum width per column,
592  // merged cells will be counted later
593  sal_Int32 nCurrentWidth = 0;
594  sal_Int32 nCol = 0, nRow = 0;
595  for( nCol = 0; nCol < nColCount; nCol++ )
596  {
597  sal_Int32 nMinWidth = 0;
598 
599  bool bIsEmpty = true; // check if all cells in this column are merged
600 
601  for( nRow = 0; nRow < nRowCount; ++nRow )
602  {
603  CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
604  if( xCell.is() && !xCell->isMerged() )
605  {
606  bIsEmpty = false;
607 
608  sal_Int32 nColSpan = xCell->getColumnSpan();
609  if( nColSpan > 1 )
610  {
611  // merged cells will be evaluated later
612  aMergedCells[nCol+nColSpan-1].push_back( xCell );
613  }
614  else
615  {
616  nMinWidth = std::max( nMinWidth, xCell->getMinimumWidth() );
617  }
618  }
619  }
620 
621  maColumns[nCol].mnMinSize = nMinWidth;
622 
623  if( bIsEmpty )
624  {
625  maColumns[nCol].mnSize = 0;
626  }
627  else
628  {
629  sal_Int32 nColWidth = 0;
630  Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
631  bool bOptimal = false;
632  xColSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
633  if( bOptimal )
634  {
635  aOptimalColumns.push_back(nCol);
636  }
637  else
638  {
639  xColSet->getPropertyValue( gsSize ) >>= nColWidth;
640  }
641 
642  maColumns[nCol].mnSize = std::max( nColWidth, nMinWidth);
643 
644  nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize);
645  }
646  }
647 
648  // if we have optimal sized rows, distribute what is given (left)
649  if( !bFit && !aOptimalColumns.empty() && (nCurrentWidth < rArea.getWidth()) )
650  {
651  sal_Int32 nLeft = rArea.getWidth() - nCurrentWidth;
652  sal_Int32 nDistribute = nLeft / aOptimalColumns.size();
653 
654  auto iter( aOptimalColumns.begin() );
655  while( iter != aOptimalColumns.end() )
656  {
657  sal_Int32 nOptCol = *iter++;
658  if( iter == aOptimalColumns.end() )
659  nDistribute = nLeft;
660 
661  maColumns[nOptCol].mnSize += nDistribute;
662  nLeft -= nDistribute;
663  }
664 
665  DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableWidtht(), layouting failed!" );
666  }
667 
668  // now check if merged cells fit
669  for( nCol = 1; nCol < nColCount; ++nCol )
670  {
671  bool bChanges = false;
672 
673  const sal_Int32 nOldSize = maColumns[nCol].mnSize;
674 
675  for( const CellRef& xCell : aMergedCells[nCol] )
676  {
677  sal_Int32 nMinWidth = xCell->getMinimumWidth();
678 
679  for( sal_Int32 nMCol = nCol - xCell->getColumnSpan() + 1; (nMCol > 0) && (nMCol < nCol); ++nMCol )
680  nMinWidth -= maColumns[nMCol].mnSize;
681 
682  if( nMinWidth > maColumns[nCol].mnMinSize )
683  maColumns[nCol].mnMinSize = nMinWidth;
684 
685  if( nMinWidth > maColumns[nCol].mnSize )
686  {
687  maColumns[nCol].mnSize = nMinWidth;
688  bChanges = true;
689  }
690  }
691 
692  if( bChanges )
693  {
694  nCurrentWidth = o3tl::saturating_add(nCurrentWidth, maColumns[nCol].mnSize - nOldSize);
695  }
696  }
697 
698  // now scale if wanted and needed
699  if( bFit && (nCurrentWidth != rArea.getWidth()) )
700  distribute( maColumns, rArea.getWidth() - nCurrentWidth );
701 
702  // last step, update left edges
703  sal_Int32 nNewWidth = 0;
704 
705  const bool bRTL = (mxTable->getSdrTableObj()->GetWritingMode() == WritingMode_RL_TB);
706  RangeIterator<sal_Int32> coliter( 0, nColCount, !bRTL );
707  while( coliter.next(nCol ) )
708  {
709  maColumns[nCol].mnPos = nNewWidth;
710  nNewWidth = o3tl::saturating_add(nNewWidth, maColumns[nCol].mnSize);
711  if( bFit )
712  {
713  Reference< XPropertySet > xColSet( xCols->getByIndex(nCol), UNO_QUERY_THROW );
714  xColSet->setPropertyValue( gsSize, Any( maColumns[nCol].mnSize ) );
715  }
716  }
717 
718  rArea.SetSize( Size( nNewWidth, rArea.GetHeight() ) );
719  updateCells( rArea );
720 }
721 
722 
724 {
725  const sal_Int32 nColCount = getColumnCount();
726  const sal_Int32 nRowCount = getRowCount();
727  if( nRowCount == 0 )
728  return;
729 
730  Reference< XTableRows > xRows( mxTable->getRows() );
731 
732  MergeVector aMergedCells( nRowCount );
733  std::vector<sal_Int32> aOptimalRows;
734 
735  const OUString sOptimalSize("OptimalSize");
736 
737  // first calculate current height and initial minimum size per column,
738  // merged cells will be counted later
739  sal_Int32 nCurrentHeight = 0;
740  sal_Int32 nCol, nRow;
741  for( nRow = 0; nRow < nRowCount; ++nRow )
742  {
743  sal_Int32 nMinHeight = 0;
744 
745  bool bIsEmpty = true; // check if all cells in this row are merged
746  bool bRowHasText = false;
747 
748  for( nCol = 0; nCol < nColCount; ++nCol )
749  {
750  CellRef xCell( getCell( CellPos( nCol, nRow ) ) );
751  if( xCell.is() && !xCell->isMerged() )
752  {
753  bIsEmpty = false;
754 
755  sal_Int32 nRowSpan = xCell->getRowSpan();
756  if( nRowSpan > 1 )
757  {
758  // merged cells will be evaluated later
759  aMergedCells[nRow+nRowSpan-1].push_back( xCell );
760  }
761  else
762  {
763  bool bCellHasText = xCell->hasText();
764  if (bRowHasText == bCellHasText)
765  {
766  nMinHeight = std::max( nMinHeight, xCell->getMinimumHeight() );
767  }
768  else if ( !bRowHasText && bCellHasText )
769  {
770  bRowHasText = true;
771  nMinHeight = xCell->getMinimumHeight();
772  }
773  }
774  }
775  }
776 
777  maRows[nRow].mnMinSize = nMinHeight;
778 
779  if( bIsEmpty )
780  {
781  maRows[nRow].mnSize = 0;
782  }
783  else
784  {
785  sal_Int32 nRowHeight = 0;
786  Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
787 
788  bool bOptimal = false;
789  xRowSet->getPropertyValue( sOptimalSize ) >>= bOptimal;
790  if( bOptimal )
791  {
792  aOptimalRows.push_back( nRow );
793  }
794  else
795  {
796  xRowSet->getPropertyValue( gsSize ) >>= nRowHeight;
797  }
798 
799  maRows[nRow].mnSize = nRowHeight;
800 
801  if( maRows[nRow].mnSize < nMinHeight )
802  maRows[nRow].mnSize = nMinHeight;
803 
804  nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize);
805  }
806  }
807 
808  // if we have optimal sized rows, distribute what is given (left)
809  if( !bFit && !aOptimalRows.empty() && (nCurrentHeight < rArea.getHeight()) )
810  {
811  sal_Int32 nLeft = rArea.getHeight() - nCurrentHeight;
812  sal_Int32 nDistribute = nLeft / aOptimalRows.size();
813 
814  auto iter( aOptimalRows.begin() );
815  while( iter != aOptimalRows.end() )
816  {
817  sal_Int32 nOptRow = *iter++;
818  if( iter == aOptimalRows.end() )
819  nDistribute = nLeft;
820 
821  maRows[nOptRow].mnSize += nDistribute;
822  nLeft -= nDistribute;
823 
824  }
825 
826  DBG_ASSERT( nLeft == 0, "svx::TableLayouter::LayoutTableHeight(), layouting failed!" );
827  }
828 
829  // now check if merged cells fit
830  for( nRow = 1; nRow < nRowCount; ++nRow )
831  {
832  bool bChanges = false;
833  sal_Int32 nOldSize = maRows[nRow].mnSize;
834 
835  for( const CellRef& xCell : aMergedCells[nRow] )
836  {
837  sal_Int32 nMinHeight = xCell->getMinimumHeight();
838 
839  for( sal_Int32 nMRow = nRow - xCell->getRowSpan() + 1; (nMRow > 0) && (nMRow < nRow); ++nMRow )
840  nMinHeight -= maRows[nMRow].mnSize;
841 
842  if( nMinHeight > maRows[nRow].mnMinSize )
843  maRows[nRow].mnMinSize = nMinHeight;
844 
845  if( nMinHeight > maRows[nRow].mnSize )
846  {
847  maRows[nRow].mnSize = nMinHeight;
848  bChanges = true;
849  }
850  }
851  if( bChanges )
852  nCurrentHeight = o3tl::saturating_add(nCurrentHeight, maRows[nRow].mnSize - nOldSize);
853  }
854 
855  // now scale if wanted and needed
856  if( bFit && nCurrentHeight != rArea.getHeight() )
857  distribute(maRows, o3tl::saturating_sub<sal_Int32>(rArea.getHeight(), nCurrentHeight));
858 
859  // last step, update left edges
860  sal_Int32 nNewHeight = 0;
861  for( nRow = 0; nRow < nRowCount; ++nRow )
862  {
863  maRows[nRow].mnPos = nNewHeight;
864  nNewHeight = o3tl::saturating_add(nNewHeight, maRows[nRow].mnSize);
865 
866  if( bFit )
867  {
868  Reference< XPropertySet > xRowSet( xRows->getByIndex(nRow), UNO_QUERY_THROW );
869  xRowSet->setPropertyValue( gsSize, Any( maRows[nRow].mnSize ) );
870  }
871  }
872 
873  rArea.SetSize( Size( rArea.GetWidth(), nNewHeight ) );
874  updateCells( rArea );
875 }
876 
877 
880 void TableLayouter::LayoutTable( tools::Rectangle& rRectangle, bool bFitWidth, bool bFitHeight )
881 {
882  if( !mxTable.is() )
883  return;
884 
885  const sal_Int32 nRowCount = mxTable->getRowCount();
886  const sal_Int32 nColCount = mxTable->getColumnCount();
887 
888  if( (nRowCount != getRowCount()) || (nColCount != getColumnCount()) )
889  {
890  if( static_cast< sal_Int32 >( maRows.size() ) != nRowCount )
891  maRows.resize( nRowCount );
892 
893  for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
894  maRows[nRow].clear();
895 
896  if( static_cast< sal_Int32 >( maColumns.size() ) != nColCount )
897  maColumns.resize( nColCount );
898 
899  for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
900  maColumns[nCol].clear();
901  }
902 
903  LayoutTableWidth( rRectangle, bFitWidth );
904  LayoutTableHeight( rRectangle, bFitHeight );
906 }
907 
908 
910 {
911  const sal_Int32 nColCount = getColumnCount();
912  const sal_Int32 nRowCount = getRowCount();
913 
914  CellPos aPos;
915  for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
916  {
917  for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
918  {
919  CellRef xCell( getCell( aPos ) );
920  if( xCell.is() )
921  {
922  basegfx::B2IRectangle aCellArea;
923  if( getCellArea( xCell, aPos, aCellArea ) )
924  {
925  tools::Rectangle aCellRect;
926  aCellRect.SetLeft( aCellArea.getMinX() );
927  aCellRect.SetRight( aCellArea.getMaxX() );
928  aCellRect.SetTop( aCellArea.getMinY() );
929  aCellRect.SetBottom( aCellArea.getMaxY() );
930  aCellRect.Move( rRectangle.Left(), rRectangle.Top() );
931  xCell->setCellRect( aCellRect );
932  }
933  }
934  }
935  }
936 }
937 
938 
940 {
941  CellRef xCell;
942  if( mxTable.is() ) try
943  {
944  xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
945  }
946  catch( Exception& )
947  {
948  OSL_FAIL( "sdr::table::TableLayouter::getCell(), exception caught!" );
949  }
950  return xCell;
951 }
952 
953 
954 bool TableLayouter::HasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther )
955 {
956  if (!pThis || ((pThis == &gEmptyBorder) && (pOther != nullptr)))
957  return false;
958  if (!pOther || (pOther == &gEmptyBorder))
959  return true;
960 
961  sal_uInt16 nThisSize = pThis->GetScaledWidth();
962  sal_uInt16 nOtherSize = pOther->GetScaledWidth();
963 
964  if (nThisSize > nOtherSize)
965  return true;
966 
967  else if (nThisSize < nOtherSize)
968  {
969  return false;
970  }
971  else
972  {
973  if ( pOther->GetInWidth() && !pThis->GetInWidth() )
974  {
975  return true;
976  }
977  else if ( pThis->GetInWidth() && !pOther->GetInWidth() )
978  {
979  return false;
980  }
981  else
982  {
983  return true;
984  }
985  }
986 }
987 
988 void TableLayouter::SetBorder( sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const SvxBorderLine* pLine )
989 {
990  if (!pLine)
991  pLine = &gEmptyBorder;
992 
993  BorderLineMap& rMap = bHorizontal ? maHorizontalBorders : maVerticalBorders;
994 
995  if( (nCol >= 0) && (nCol < sal::static_int_cast<sal_Int32>(rMap.size())) &&
996  (nRow >= 0) && (nRow < sal::static_int_cast<sal_Int32>(rMap[nCol].size())) )
997  {
998  SvxBorderLine *pOld = rMap[nCol][nRow];
999 
1000  if (HasPriority(pLine, pOld))
1001  {
1002  if (pOld && pOld != &gEmptyBorder)
1003  delete pOld;
1004 
1005  SvxBorderLine* pNew = (pLine != &gEmptyBorder) ? new SvxBorderLine(*pLine) : &gEmptyBorder;
1006 
1007  rMap[nCol][nRow] = pNew;
1008  }
1009  }
1010  else
1011  {
1012  OSL_FAIL( "sdr::table::TableLayouter::SetBorder(), invalid border!" );
1013  }
1014 }
1015 
1017 {
1020 }
1021 
1023 {
1024  const sal_Int32 nColCount = rMap.size();
1025 
1026  for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
1027  {
1028  const sal_Int32 nRowCount = rMap[nCol].size();
1029  for( sal_Int32 nRow = 0; nRow < nRowCount; nRow++ )
1030  {
1031  SvxBorderLine* pLine = rMap[nCol][nRow];
1032  if( pLine )
1033  {
1034  if( pLine != &gEmptyBorder )
1035  delete pLine;
1036 
1037  rMap[nCol][nRow] = nullptr;
1038  }
1039  }
1040  }
1041 }
1042 
1044 {
1048 }
1049 
1050 
1052 {
1053  const sal_Int32 nColCount = getColumnCount() + 1;
1054  const sal_Int32 nRowCount = getRowCount() + 1;
1055 
1056  if( sal::static_int_cast<sal_Int32>(rMap.size()) != nColCount )
1057  rMap.resize( nColCount );
1058 
1059  for( sal_Int32 nCol = 0; nCol < nColCount; nCol++ )
1060  {
1061  if( sal::static_int_cast<sal_Int32>(rMap[nCol].size()) != nRowCount )
1062  rMap[nCol].resize( nRowCount );
1063  }
1064 }
1065 
1066 
1068 {
1069  // make sure old border layout is cleared and border maps have correct size
1071 
1072  const sal_Int32 nColCount = getColumnCount();
1073  const sal_Int32 nRowCount = getRowCount();
1074 
1075  CellPos aPos;
1076  for( aPos.mnRow = 0; aPos.mnRow < nRowCount; aPos.mnRow++ )
1077  {
1078  for( aPos.mnCol = 0; aPos.mnCol < nColCount; aPos.mnCol++ )
1079  {
1080  CellRef xCell( getCell( aPos ) );
1081  if( !xCell.is() )
1082  continue;
1083 
1084  const SvxBoxItem* pThisAttr = xCell->GetItemSet().GetItem<SvxBoxItem>( SDRATTR_TABLE_BORDER );
1085  OSL_ENSURE(pThisAttr,"sdr::table::TableLayouter::UpdateBorderLayout(), no border attribute?");
1086 
1087  if( !pThisAttr )
1088  continue;
1089 
1090  const sal_Int32 nLastRow = xCell->getRowSpan() + aPos.mnRow;
1091  const sal_Int32 nLastCol = xCell->getColumnSpan() + aPos.mnCol;
1092 
1093  for( sal_Int32 nRow = aPos.mnRow; nRow < nLastRow; nRow++ )
1094  {
1095  SetBorder( aPos.mnCol, nRow, false, pThisAttr->GetLeft() );
1096  SetBorder( nLastCol, nRow, false, pThisAttr->GetRight() );
1097  }
1098 
1099  for( sal_Int32 nCol = aPos.mnCol; nCol < nLastCol; nCol++ )
1100  {
1101  SetBorder( nCol, aPos.mnRow, true, pThisAttr->GetTop() );
1102  SetBorder( nCol, nLastRow, true, pThisAttr->GetBottom() );
1103  }
1104  }
1105  }
1106 }
1107 
1108 
1110  sal_Int32 nFirstCol,
1111  sal_Int32 nLastCol,
1112  const bool bOptimize,
1113  const bool bMinimize )
1114 {
1115  if( mxTable.is() ) try
1116  {
1117  const sal_Int32 nColCount = getColumnCount();
1118  Reference< XTableColumns > xCols( mxTable->getColumns(), UNO_SET_THROW );
1119  const Size aSize(0xffffff, 0xffffff);
1120 
1121  //special case - optimize a single column
1122  if ( (bOptimize || bMinimize) && nFirstCol == nLastCol )
1123  {
1124  const sal_Int32 nWish = calcPreferredColumnWidth(nFirstCol, aSize);
1125  if ( nWish < getColumnWidth(nFirstCol) )
1126  {
1127  Reference< XPropertySet > xColSet( xCols->getByIndex(nFirstCol), UNO_QUERY_THROW );
1128  xColSet->setPropertyValue( gsSize, Any( nWish ) );
1129 
1130  //FitWidth automatically distributes the new excess space
1131  LayoutTable( rArea, /*bFitWidth=*/!bMinimize, /*bFitHeight=*/false );
1132  }
1133  }
1134 
1135  if( (nFirstCol < 0) || (nFirstCol>= nLastCol) || (nLastCol >= nColCount) )
1136  return;
1137 
1138  sal_Int32 nAllWidth = 0;
1139  float fAllWish = 0;
1140  sal_Int32 nUnused = 0;
1141  std::vector<sal_Int32> aWish(nColCount);
1142 
1143  for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1144  nAllWidth += getColumnWidth(nCol);
1145 
1146  const sal_Int32 nEqualWidth = nAllWidth / (nLastCol-nFirstCol+1);
1147 
1148  //pass 1 - collect unneeded space (from an equal width perspective)
1149  if ( bMinimize || bOptimize )
1150  {
1151  for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1152  {
1153  const sal_Int32 nIndex = nCol - nFirstCol;
1154  aWish[nIndex] = calcPreferredColumnWidth(nCol, aSize);
1155  fAllWish += aWish[nIndex];
1156  if ( aWish[nIndex] < nEqualWidth )
1157  nUnused += nEqualWidth - aWish[nIndex];
1158  }
1159  }
1160  const sal_Int32 nDistributeExcess = nAllWidth - fAllWish;
1161 
1162  sal_Int32 nWidth = nEqualWidth;
1163  for( sal_Int32 nCol = nFirstCol; nCol <= nLastCol; ++nCol )
1164  {
1165  if ( !bMinimize && nCol == nLastCol )
1166  nWidth = nAllWidth; // last column gets rounding/logic errors
1167  else if ( (bMinimize || bOptimize) && fAllWish )
1168  {
1169  //pass 2 - first come, first served when requesting from the
1170  // unneeded pool, or proportionally allocate excess.
1171  const sal_Int32 nIndex = nCol - nFirstCol;
1172  if ( aWish[nIndex] > nEqualWidth + nUnused )
1173  {
1174  nWidth = nEqualWidth + nUnused;
1175  nUnused = 0;
1176  }
1177  else
1178  {
1179  nWidth = aWish[nIndex];
1180  if ( aWish[nIndex] > nEqualWidth )
1181  nUnused -= aWish[nIndex] - nEqualWidth;
1182 
1183  if ( !bMinimize && nDistributeExcess > 0 )
1184  nWidth += nWidth / fAllWish * nDistributeExcess;
1185  }
1186  }
1187 
1188  Reference< XPropertySet > xColSet( xCols->getByIndex( nCol ), UNO_QUERY_THROW );
1189  xColSet->setPropertyValue( gsSize, Any( nWidth ) );
1190 
1191  nAllWidth -= nWidth;
1192  }
1193 
1194  LayoutTable( rArea, !bMinimize, false );
1195  }
1196  catch( Exception& )
1197  {
1198  OSL_FAIL("sdr::table::TableLayouter::DistributeColumns(), exception caught!");
1199  }
1200 }
1201 
1202 
1204  sal_Int32 nFirstRow,
1205  sal_Int32 nLastRow,
1206  const bool bOptimize,
1207  const bool bMinimize )
1208 {
1209  if( mxTable.is() ) try
1210  {
1211  const sal_Int32 nRowCount = mxTable->getRowCount();
1212  Reference< XTableRows > xRows( mxTable->getRows(), UNO_SET_THROW );
1213  sal_Int32 nMinHeight = 0;
1214 
1215  //special case - minimize a single row
1216  if ( bMinimize && nFirstRow == nLastRow )
1217  {
1218  const sal_Int32 nWish = std::max( maRows[nFirstRow].mnMinSize, nMinHeight );
1219  if ( nWish < getRowHeight(nFirstRow) )
1220  {
1221  Reference< XPropertySet > xRowSet( xRows->getByIndex( nFirstRow ), UNO_QUERY_THROW );
1222  xRowSet->setPropertyValue( gsSize, Any( nWish ) );
1223 
1224  LayoutTable( rArea, /*bFitWidth=*/false, /*bFitHeight=*/!bMinimize );
1225  }
1226  }
1227 
1228  if( (nFirstRow < 0) || (nFirstRow>= nLastRow) || (nLastRow >= nRowCount) )
1229  return;
1230 
1231  sal_Int32 nAllHeight = 0;
1232  sal_Int32 nMaxHeight = 0;
1233 
1234  for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1235  {
1236  nMinHeight = std::max( maRows[nRow].mnMinSize, nMinHeight );
1237  nMaxHeight = std::max( maRows[nRow].mnSize, nMaxHeight );
1238  nAllHeight += maRows[nRow].mnSize;
1239  }
1240 
1241  const sal_Int32 nRows = nLastRow-nFirstRow+1;
1242  sal_Int32 nHeight = nAllHeight / nRows;
1243 
1244  if ( !bMinimize && nHeight < nMaxHeight )
1245  {
1246  if ( !bOptimize )
1247  {
1248  sal_Int32 nNeededHeight = nRows * nMaxHeight;
1249  rArea.AdjustBottom(nNeededHeight - nAllHeight );
1250  nHeight = nMaxHeight;
1251  nAllHeight = nRows * nMaxHeight;
1252  }
1253  else if ( nHeight < nMinHeight )
1254  {
1255  sal_Int32 nNeededHeight = nRows * nMinHeight;
1256  rArea.AdjustBottom(nNeededHeight - nAllHeight );
1257  nHeight = nMinHeight;
1258  nAllHeight = nRows * nMinHeight;
1259  }
1260  }
1261 
1262  for( sal_Int32 nRow = nFirstRow; nRow <= nLastRow; ++nRow )
1263  {
1264  if ( bMinimize )
1265  nHeight = maRows[nRow].mnMinSize;
1266  else if ( nRow == nLastRow )
1267  nHeight = nAllHeight; // last row get round errors
1268 
1269  Reference< XPropertySet > xRowSet( xRows->getByIndex( nRow ), UNO_QUERY_THROW );
1270  xRowSet->setPropertyValue( gsSize, Any( nHeight ) );
1271 
1272  nAllHeight -= nHeight;
1273  }
1274 
1275  LayoutTable( rArea, false, !bMinimize );
1276  }
1277  catch( Exception& )
1278  {
1279  OSL_FAIL("sdr::table::TableLayouter::DistributeRows(), exception caught!");
1280  }
1281 }
1282 
1284 {
1285  xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter"));
1286 
1287  xmlTextWriterStartElement(pWriter, BAD_CAST("columns"));
1288  for (const auto& rColumn : maColumns)
1289  rColumn.dumpAsXml(pWriter);
1290  xmlTextWriterEndElement(pWriter);
1291 
1292  xmlTextWriterStartElement(pWriter, BAD_CAST("rows"));
1293  for (const auto& rRow : maRows)
1294  rRow.dumpAsXml(pWriter);
1295  xmlTextWriterEndElement(pWriter);
1296 
1297  xmlTextWriterEndElement(pWriter);
1298 }
1299 
1301 {
1302  xmlTextWriterStartElement(pWriter, BAD_CAST("TableLayouter_Layout"));
1303 
1304  xmlTextWriterWriteAttribute(pWriter, BAD_CAST("pos"), BAD_CAST(OString::number(mnPos).getStr()));
1305  xmlTextWriterWriteAttribute(pWriter, BAD_CAST("size"), BAD_CAST(OString::number(mnSize).getStr()));
1306  xmlTextWriterWriteAttribute(pWriter, BAD_CAST("minSize"), BAD_CAST(OString::number(mnMinSize).getStr()));
1307 
1308  xmlTextWriterEndElement(pWriter);
1309 }
1310 
1311 }
1312 
1313 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool findMergeOrigin(const TableModelRef &xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 &rOriginX, sal_Int32 &rOriginY)
returns true if the cell(nMergedX,nMergedY) is merged with other cells.
const OUStringLiteral gsSize("Size")
long getHeight() const
const int nColCount
std::enable_if< std::is_signed< T >::value, bool >::type checked_sub(T a, T b, T &result)
::sal_Int32 getColumnCount() const
long GetWidth() const
void DistributeColumns(::tools::Rectangle &rArea, sal_Int32 nFirstCol, sal_Int32 nLastCol, const bool bOptimize, const bool bMinimize)
sal_Int32 nIndex
void dumpAsXml(xmlTextWriterPtr pWriter) const
long GetHeight() const
struct _xmlTextWriter * xmlTextWriterPtr
basegfx::B2ITuple getCellSize(const CellRef &xCell, const CellPos &rPos) const
std::vector< Layout > LayoutVector
static bool checkMergeOrigin(const TableModelRef &xTable, sal_Int32 nMergedX, sal_Int32 nMergedY, sal_Int32 nCellX, sal_Int32 nCellY, bool &bRunning)
long getWidth() const
::sal_Int32 getRowCount() const
std::vector< EdgeInfo > getHorizontalEdges()
sal_Int64 n
static SvxBorderLine gEmptyBorder
sal_Int32 getMinY() const
static sal_Int32 distribute(LayoutVector &rLayouts, sal_Int32 nDistribute)
const editeng::SvxBorderLine * GetRight() const
sal_Int32 getMinimumColumnWidth(sal_Int32 nColumn)
float x
bool isValidColumn(sal_Int32 nColumn) const
sal_Int32 getHorizontalEdge(int nEdgeY, sal_Int32 *pnMin, sal_Int32 *pnMax)
long AdjustBottom(long nVertMoveDelta)
void SetBorder(sal_Int32 nCol, sal_Int32 nRow, bool bHorizontal, const editeng::SvxBorderLine *pLine)
void Move(long nHorzMoveDelta, long nVertMoveDelta)
editeng::SvxBorderLine * getBorderLine(sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal) const
returns the requested borderline in rpBorderLine or a null pointer if there is no border at this edge...
bool next(T &rValue)
Definition: celltypes.hxx:75
sal_Int32 getMaxX() const
int nCount
long Top() const
float y
const editeng::SvxBorderLine * GetTop() const
void SetTop(long v)
const editeng::SvxBorderLine * GetLeft() const
#define DBG_ASSERT(sCon, aError)
int i
sal_Int32 getX() const
void dumpAsXml(xmlTextWriterPtr pWriter) const
void SetSize(const Size &rSize)
std::enable_if< std::is_signed< T >::value, T >::type saturating_add(T a, T b)
void updateCells(::tools::Rectangle const &rRectangle)
sal_Int32 getColumnWidth(sal_Int32 nColumn) const
BorderLineMap maHorizontalBorders
void SetRight(long v)
bool getCellArea(const CellRef &xCell, const CellPos &rPos, basegfx::B2IRectangle &rArea) const
std::enable_if< std::is_signed< T >::value, bool >::type checked_add(T a, T b, T &result)
std::enable_if< std::is_signed< T >::value, bool >::type checked_multiply(T a, T b, T &result)
CellRef getCell(const CellPos &rPos) const
size
sal_Int32 getMinX() const
bool isValid(const CellPos &rPos) const
std::vector< CellRef > MergeableCellVector
static bool HasPriority(const editeng::SvxBorderLine *pThis, const editeng::SvxBorderLine *pOther)
std::vector< MergeableCellVector > MergeVector
bool isEdgeVisible(sal_Int32 nEdgeX, sal_Int32 nEdgeY, bool bHorizontal) const
checks if the given edge is visible.
constexpr TypedWhichId< SvxBoxItem > SDRATTR_TABLE_BORDER(SDRATTR_TABLE_FIRST+0)
void LayoutTableHeight(::tools::Rectangle &rArea, bool bFit)
bool isValidRow(sal_Int32 nRow) const
TableLayouter(const TableModelRef &xTableModel)
void SetBottom(long v)
sal_Int32 getY() const
sal_Int32 getRowHeight(sal_Int32 nRow) const
long Left() const
sal_Int32 calcPreferredColumnWidth(sal_Int32 nColumn, Size aSize) const
B2IRange B2IRectangle
std::vector< BorderLineVector > BorderLineMap
void LayoutTableWidth(::tools::Rectangle &rArea, bool bFit)
void LayoutTable(::tools::Rectangle &rRectangle, bool bFitWidth, bool bFitHeight)
try to fit the table into the given rectangle.
sal_Int32 getMaxY() const
void SetLeft(long v)
void DistributeRows(::tools::Rectangle &rArea, sal_Int32 nFirstRow, sal_Int32 nLastRow, const bool bOptimize, const bool bMinimize)
const editeng::SvxBorderLine * GetBottom() const
std::vector< EdgeInfo > getVerticalEdges()
sal_Int32 getVerticalEdge(int nEdgeX, sal_Int32 *pnMin, sal_Int32 *pnMax)
sal_uInt32 mnSize