Coverage Report

Created: 2025-12-08 09:28

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