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