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