Coverage Report

Created: 2026-06-30 11:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/svx/source/table/tablemodel.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
#include <sal/config.h>
21
22
#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
23
#include <com/sun/star/table/XMergeableCell.hpp>
24
25
#include <algorithm>
26
27
#include <vcl/svapp.hxx>
28
#include <osl/mutex.hxx>
29
#include <libxml/xmlwriter.h>
30
#include <tools/debug.hxx>
31
#include <comphelper/diagnose_ex.hxx>
32
33
#include <cell.hxx>
34
#include "cellcursor.hxx"
35
#include <tablemodel.hxx>
36
#include "tablerow.hxx"
37
#include "tablerows.hxx"
38
#include "tablecolumn.hxx"
39
#include "tablecolumns.hxx"
40
#include "tableundo.hxx"
41
#include <o3tl/safeint.hxx>
42
#include <svx/svdotable.hxx>
43
#include <svx/svdmodel.hxx>
44
#include <svx/strings.hrc>
45
#include <svx/dialmgr.hxx>
46
47
using namespace css;
48
49
namespace sdr::table {
50
51
52
// removes the given range from a vector
53
template< class Vec, class Iter > static void remove_range( Vec& rVector, sal_Int32 nIndex, sal_Int32 nCount )
54
2.73k
{
55
2.73k
    const sal_Int32 nSize = static_cast<sal_Int32>(rVector.size());
56
2.73k
    if( nCount && (nIndex >= 0) && (nIndex < nSize) )
57
2.73k
    {
58
2.73k
        if( (nIndex + nCount) >= nSize )
59
2.55k
        {
60
            // remove at end
61
2.55k
            rVector.resize( nIndex );
62
2.55k
        }
63
175
        else
64
175
        {
65
175
            rVector.erase(rVector.begin() + nIndex, rVector.begin() + nIndex + nCount);
66
175
        }
67
2.73k
    }
68
2.73k
}
tablemodel.cxx:void sdr::table::remove_range<std::__1::vector<rtl::Reference<sdr::table::TableRow>, std::__1::allocator<rtl::Reference<sdr::table::TableRow> > >, std::__1::__wrap_iter<rtl::Reference<sdr::table::TableRow>*> >(std::__1::vector<rtl::Reference<sdr::table::TableRow>, std::__1::allocator<rtl::Reference<sdr::table::TableRow> > >&, int, int)
Line
Count
Source
54
1.51k
{
55
1.51k
    const sal_Int32 nSize = static_cast<sal_Int32>(rVector.size());
56
1.51k
    if( nCount && (nIndex >= 0) && (nIndex < nSize) )
57
1.51k
    {
58
1.51k
        if( (nIndex + nCount) >= nSize )
59
1.41k
        {
60
            // remove at end
61
1.41k
            rVector.resize( nIndex );
62
1.41k
        }
63
98
        else
64
98
        {
65
98
            rVector.erase(rVector.begin() + nIndex, rVector.begin() + nIndex + nCount);
66
98
        }
67
1.51k
    }
68
1.51k
}
tablemodel.cxx:void sdr::table::remove_range<std::__1::vector<rtl::Reference<sdr::table::TableColumn>, std::__1::allocator<rtl::Reference<sdr::table::TableColumn> > >, std::__1::__wrap_iter<rtl::Reference<sdr::table::TableColumn>*> >(std::__1::vector<rtl::Reference<sdr::table::TableColumn>, std::__1::allocator<rtl::Reference<sdr::table::TableColumn> > >&, int, int)
Line
Count
Source
54
1.22k
{
55
1.22k
    const sal_Int32 nSize = static_cast<sal_Int32>(rVector.size());
56
1.22k
    if( nCount && (nIndex >= 0) && (nIndex < nSize) )
57
1.22k
    {
58
1.22k
        if( (nIndex + nCount) >= nSize )
59
1.14k
        {
60
            // remove at end
61
1.14k
            rVector.resize( nIndex );
62
1.14k
        }
63
77
        else
64
77
        {
65
77
            rVector.erase(rVector.begin() + nIndex, rVector.begin() + nIndex + nCount);
66
77
        }
67
1.22k
    }
68
1.22k
}
69
70
71
/** inserts a range into a vector */
72
template< class Vec, class Iter, class Entry > static sal_Int32 insert_range( Vec& rVector, sal_Int32 nIndex, sal_Int32 nCount )
73
2.70k
{
74
2.70k
    if( nCount )
75
2.70k
    {
76
2.70k
        if( nIndex >= static_cast< sal_Int32 >( rVector.size() ) )
77
846
        {
78
            // append at end
79
846
            nIndex = static_cast< sal_Int32 >( rVector.size() ); // cap to end
80
846
            rVector.resize( nIndex + nCount );
81
846
        }
82
1.85k
        else
83
1.85k
        {
84
            // insert
85
1.85k
            Iter aIter( rVector.begin() );
86
1.85k
            std::advance( aIter, nIndex );
87
88
1.85k
            Entry aEmpty;
89
1.85k
            rVector.insert( aIter, nCount, aEmpty );
90
1.85k
        }
91
2.70k
    }
92
2.70k
    return nIndex;
93
2.70k
}
tablemodel.cxx:int sdr::table::insert_range<std::__1::vector<rtl::Reference<sdr::table::TableRow>, std::__1::allocator<rtl::Reference<sdr::table::TableRow> > >, std::__1::__wrap_iter<rtl::Reference<sdr::table::TableRow>*>, rtl::Reference<sdr::table::TableRow> >(std::__1::vector<rtl::Reference<sdr::table::TableRow>, std::__1::allocator<rtl::Reference<sdr::table::TableRow> > >&, int, int)
Line
Count
Source
73
921
{
74
921
    if( nCount )
75
921
    {
76
921
        if( nIndex >= static_cast< sal_Int32 >( rVector.size() ) )
77
0
        {
78
            // append at end
79
0
            nIndex = static_cast< sal_Int32 >( rVector.size() ); // cap to end
80
0
            rVector.resize( nIndex + nCount );
81
0
        }
82
921
        else
83
921
        {
84
            // insert
85
921
            Iter aIter( rVector.begin() );
86
921
            std::advance( aIter, nIndex );
87
88
921
            Entry aEmpty;
89
921
            rVector.insert( aIter, nCount, aEmpty );
90
921
        }
91
921
    }
92
921
    return nIndex;
93
921
}
tablemodel.cxx:int sdr::table::insert_range<std::__1::vector<rtl::Reference<sdr::table::TableColumn>, std::__1::allocator<rtl::Reference<sdr::table::TableColumn> > >, std::__1::__wrap_iter<rtl::Reference<sdr::table::TableColumn>*>, rtl::Reference<sdr::table::TableColumn> >(std::__1::vector<rtl::Reference<sdr::table::TableColumn>, std::__1::allocator<rtl::Reference<sdr::table::TableColumn> > >&, int, int)
Line
Count
Source
73
1.78k
{
74
1.78k
    if( nCount )
75
1.78k
    {
76
1.78k
        if( nIndex >= static_cast< sal_Int32 >( rVector.size() ) )
77
846
        {
78
            // append at end
79
846
            nIndex = static_cast< sal_Int32 >( rVector.size() ); // cap to end
80
846
            rVector.resize( nIndex + nCount );
81
846
        }
82
937
        else
83
937
        {
84
            // insert
85
937
            Iter aIter( rVector.begin() );
86
937
            std::advance( aIter, nIndex );
87
88
937
            Entry aEmpty;
89
937
            rVector.insert( aIter, nCount, aEmpty );
90
937
        }
91
1.78k
    }
92
1.78k
    return nIndex;
93
1.78k
}
94
95
96
TableModel::TableModel( SdrTableObj* pTableObj )
97
1.03k
: mpTableObj( pTableObj )
98
1.03k
, mbModified( false )
99
1.03k
, mbNotifyPending( false )
100
1.03k
, mnNotifyLock( 0 )
101
1.03k
{
102
1.03k
}
Unexecuted instantiation: sdr::table::TableModel::TableModel(sdr::table::SdrTableObj*)
sdr::table::TableModel::TableModel(sdr::table::SdrTableObj*)
Line
Count
Source
97
1.03k
: mpTableObj( pTableObj )
98
1.03k
, mbModified( false )
99
1.03k
, mbNotifyPending( false )
100
1.03k
, mnNotifyLock( 0 )
101
1.03k
{
102
1.03k
}
103
104
TableModel::TableModel( SdrTableObj* pTableObj, const TableModelRef& xSourceTable )
105
0
: mpTableObj( pTableObj )
106
0
, mbModified( false )
107
0
, mbNotifyPending( false )
108
0
, mnNotifyLock( 0 )
109
0
{
110
0
    if( !xSourceTable.is() )
111
0
        return;
112
113
0
    const sal_Int32 nColCount = xSourceTable->getColumnCountImpl();
114
0
    const sal_Int32 nRowCount = xSourceTable->getRowCountImpl();
115
116
0
    init( nColCount, nRowCount );
117
118
0
    sal_Int32 nRows = nRowCount;
119
0
    while( nRows-- )
120
0
        (*maRows[nRows]) = *xSourceTable->maRows[nRows];
121
122
0
    sal_Int32 nColumns = nColCount;
123
0
    while( nColumns-- )
124
0
        (*maColumns[nColumns]) = *xSourceTable->maColumns[nColumns];
125
126
    // copy cells
127
0
    for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
128
0
    {
129
0
        for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
130
0
        {
131
0
            CellRef xTargetCell( getCell( nCol, nRow ) );
132
0
            if( xTargetCell.is() )
133
0
                xTargetCell->cloneFrom( xSourceTable->getCell( nCol, nRow ) );
134
0
        }
135
0
    }
136
0
}
Unexecuted instantiation: sdr::table::TableModel::TableModel(sdr::table::SdrTableObj*, rtl::Reference<sdr::table::TableModel> const&)
Unexecuted instantiation: sdr::table::TableModel::TableModel(sdr::table::SdrTableObj*, rtl::Reference<sdr::table::TableModel> const&)
137
138
139
TableModel::~TableModel()
140
1.03k
{
141
1.03k
}
142
143
144
void TableModel::init( sal_Int32 nColumns, sal_Int32 nRows )
145
1.03k
{
146
1.03k
    if( nRows < 20 )
147
1.03k
        maRows.reserve( 20 );
148
149
1.03k
    if( nColumns < 20 )
150
1.03k
        maColumns.reserve( 20 );
151
152
1.03k
    if( nRows && nColumns )
153
1.03k
    {
154
1.03k
        maColumns.resize( nColumns );
155
1.03k
        maRows.resize( nRows );
156
157
2.06k
        while( nRows-- )
158
1.03k
            maRows[nRows].set( new TableRow( this, nRows, nColumns ) );
159
160
2.06k
        while( nColumns-- )
161
1.03k
            maColumns[nColumns].set( new TableColumn( this, nColumns ) );
162
1.03k
    }
163
1.03k
}
164
165
166
// ICellRange
167
168
169
sal_Int32 TableModel::getLeft()
170
0
{
171
0
    return 0;
172
0
}
173
174
175
sal_Int32 TableModel::getTop()
176
0
{
177
0
    return 0;
178
0
}
179
180
181
sal_Int32 TableModel::getRight()
182
0
{
183
0
    return getColumnCount();
184
0
}
185
186
187
sal_Int32 TableModel::getBottom()
188
0
{
189
0
    return getRowCount();
190
0
}
191
192
193
uno::Reference<css::table::XTable> TableModel::getTable()
194
0
{
195
0
    return this;
196
0
}
197
198
199
void TableModel::UndoInsertRows( sal_Int32 nIndex, sal_Int32 nCount )
200
0
{
201
0
    TableModelNotifyGuard aGuard( this );
202
203
    // remove the rows
204
0
    remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
205
0
    updateRows();
206
0
    setModified(true);
207
0
}
208
209
210
void TableModel::UndoRemoveRows( sal_Int32 nIndex, RowVector& aRows )
211
0
{
212
0
    TableModelNotifyGuard aGuard( this );
213
214
0
    const sal_Int32 nCount = sal::static_int_cast< sal_Int32 >( aRows.size() );
215
216
0
    nIndex = insert_range<RowVector,RowVector::iterator,TableRowRef>( maRows, nIndex, nCount );
217
218
0
    for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
219
0
        maRows[nIndex+nOffset] = aRows[nOffset];
220
221
0
    updateRows();
222
0
    setModified(true);
223
0
}
224
225
226
void TableModel::UndoInsertColumns( sal_Int32 nIndex, sal_Int32 nCount )
227
0
{
228
0
    TableModelNotifyGuard aGuard( this );
229
230
    // now remove the columns
231
0
    remove_range<ColumnVector,ColumnVector::iterator>( maColumns, nIndex, nCount );
232
0
    sal_Int32 nRows = getRowCountImpl();
233
0
    while( nRows-- )
234
0
        maRows[nRows]->removeColumns( nIndex, nCount );
235
236
0
    updateColumns();
237
0
    setModified(true);
238
0
}
239
240
241
void TableModel::UndoRemoveColumns( sal_Int32 nIndex, ColumnVector& aCols, CellVector& aCells )
242
0
{
243
0
    TableModelNotifyGuard aGuard( this );
244
245
0
    const sal_Int32 nCount = sal::static_int_cast< sal_Int32 >( aCols.size() );
246
247
    // assert if there are not enough cells saved
248
0
    DBG_ASSERT( (aCols.size() * maRows.size()) == aCells.size(), "sdr::table::TableModel::UndoRemoveColumns(), invalid undo data!" );
249
250
0
    nIndex = insert_range<ColumnVector,ColumnVector::iterator,TableColumnRef>( maColumns, nIndex, nCount );
251
0
    for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
252
0
        maColumns[nIndex+nOffset] = aCols[nOffset];
253
254
0
    CellVector::iterator aIter( aCells.begin() );
255
256
0
    sal_Int32 nRows = getRowCountImpl();
257
0
    for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
258
0
    {
259
0
        CellVector::iterator aIter2 = aIter + nRow * nCount;
260
0
        OSL_ENSURE(aIter2 < aCells.end(), "invalid iterator!");
261
0
        maRows[nRow]->insertColumns( nIndex, nCount, &aIter2 );
262
0
    }
263
264
0
    updateColumns();
265
0
    setModified(true);
266
0
}
267
268
269
// XTable
270
271
272
uno::Reference<css::table::XCellCursor> SAL_CALL TableModel::createCursor()
273
0
{
274
0
    ::SolarMutexGuard aGuard;
275
0
    return createCursorByRange( uno::Reference< XCellRange >( this ) );
276
0
}
277
278
279
uno::Reference<css::table::XCellCursor> SAL_CALL TableModel::createCursorByRange( const uno::Reference< XCellRange >& rRange )
280
3.24k
{
281
3.24k
    ::SolarMutexGuard aGuard;
282
283
3.24k
    ICellRange* pRange = dynamic_cast< ICellRange* >( rRange.get() );
284
3.24k
    if( (pRange == nullptr) || (pRange->getTable().get() != this) )
285
0
        throw lang::IllegalArgumentException();
286
287
3.24k
    TableModelRef xModel( this );
288
3.24k
    return new CellCursor( xModel, pRange->getLeft(), pRange->getTop(), pRange->getRight(), pRange->getBottom() );
289
3.24k
}
290
291
292
sal_Int32 SAL_CALL TableModel::getRowCount()
293
9.49k
{
294
9.49k
    ::SolarMutexGuard aGuard;
295
9.49k
    return getRowCountImpl();
296
9.49k
}
297
298
sal_Int32 SAL_CALL TableModel::getColumnCount()
299
40.1k
{
300
40.1k
    ::SolarMutexGuard aGuard;
301
40.1k
    return getColumnCountImpl();
302
40.1k
}
303
304
std::vector<sal_Int32> TableModel::getColumnWidths()
305
0
{
306
0
    std::vector<sal_Int32> aRet;
307
0
    for (const TableColumnRef& xColumn : maColumns)
308
0
        aRet.push_back(xColumn->getWidth());
309
0
    return aRet;
310
0
}
311
312
313
// XModifiable
314
315
316
sal_Bool SAL_CALL TableModel::isModified(  )
317
0
{
318
0
    ::SolarMutexGuard aGuard;
319
0
    return mbModified;
320
0
}
321
322
323
void SAL_CALL TableModel::setModified( sal_Bool bModified )
324
184k
{
325
184k
    {
326
184k
        ::SolarMutexGuard aGuard;
327
184k
        mbModified = bModified;
328
184k
    }
329
184k
    if( bModified )
330
184k
        notifyModification();
331
184k
}
332
333
334
// XModifyBroadcaster
335
336
337
void SAL_CALL TableModel::addModifyListener( const uno::Reference<util::XModifyListener>& xListener )
338
1.03k
{
339
1.03k
    std::unique_lock aGuard(m_aMutex);
340
1.03k
    maModifyListeners.addInterface( aGuard, xListener );
341
1.03k
}
342
343
344
void SAL_CALL TableModel::removeModifyListener( const uno::Reference<util::XModifyListener>& xListener )
345
1.03k
{
346
1.03k
    std::unique_lock aGuard(m_aMutex);
347
1.03k
    maModifyListeners.removeInterface( aGuard, xListener );
348
1.03k
}
349
350
351
// XColumnRowRange
352
353
354
uno::Reference<css::table::XTableColumns> SAL_CALL TableModel::getColumns()
355
1.03k
{
356
1.03k
    ::SolarMutexGuard aGuard;
357
358
1.03k
    if( !mxTableColumns.is() )
359
1.03k
        mxTableColumns.set( new TableColumns( this ) );
360
1.03k
    return mxTableColumns;
361
1.03k
}
362
363
364
uno::Reference<css::table::XTableRows> SAL_CALL TableModel::getRows()
365
1.03k
{
366
1.03k
    ::SolarMutexGuard aGuard;
367
368
1.03k
    if( !mxTableRows.is() )
369
1.03k
        mxTableRows.set( new TableRows( this ) );
370
1.03k
    return mxTableRows;
371
1.03k
}
372
373
374
// XCellRange
375
376
377
uno::Reference<css::table::XCell> SAL_CALL TableModel::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow )
378
295k
{
379
295k
    ::SolarMutexGuard aGuard;
380
381
295k
    sal_Int32 nRowCount = getRowCountImpl();
382
295k
    if( nRow < 0 || nRow >= nRowCount )
383
153
        throw lang::IndexOutOfBoundsException(OUString::Concat("row ") + OUString::number(nRow)
384
153
                    + " out of range 0.." + OUString::number(nRowCount));
385
386
295k
    sal_Int32 nColCount = getColumnCountImpl();
387
295k
    if( nColumn < 0 || nColumn >= nColCount )
388
70
        throw lang::IndexOutOfBoundsException(OUString::Concat("col ") + OUString::number(nColumn)
389
70
                    + " out of range 0.." + OUString::number(nColCount));
390
391
295k
    return maRows[nRow]->maCells[nColumn];
392
295k
}
393
394
395
uno::Reference<css::table::XCellRange> SAL_CALL TableModel::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom )
396
3.37k
{
397
3.37k
    ::SolarMutexGuard aGuard;
398
399
3.37k
    if( (nLeft >= 0) && (nTop >= 0) && (nRight >= nLeft) && (nBottom >= nTop) && (nRight < getColumnCountImpl()) && (nBottom < getRowCountImpl() ) )
400
3.24k
    {
401
3.24k
        TableModelRef xModel( this );
402
3.24k
        return new CellRange( xModel, nLeft, nTop, nRight, nBottom );
403
3.24k
    }
404
405
127
    throw lang::IndexOutOfBoundsException();
406
3.37k
}
407
408
409
uno::Reference<css::table::XCellRange> SAL_CALL TableModel::getCellRangeByName( const OUString& /*aRange*/ )
410
0
{
411
0
    return uno::Reference< XCellRange >();
412
0
}
413
414
415
// XPropertySet
416
417
418
uno::Reference<beans::XPropertySetInfo> SAL_CALL TableModel::getPropertySetInfo(  )
419
0
{
420
0
    uno::Reference<beans::XPropertySetInfo> xInfo;
421
0
    return xInfo;
422
0
}
423
424
425
void SAL_CALL TableModel::setPropertyValue( const OUString& /*aPropertyName*/, const uno::Any& /*aValue*/ )
426
0
{
427
0
}
428
429
430
uno::Any SAL_CALL TableModel::getPropertyValue( const OUString& /*PropertyName*/ )
431
0
{
432
0
    return uno::Any();
433
0
}
434
435
436
void SAL_CALL TableModel::addPropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/ )
437
0
{
438
0
}
439
440
441
void SAL_CALL TableModel::removePropertyChangeListener( const OUString& /*aPropertyName*/, const uno::Reference<beans::XPropertyChangeListener>& /*xListener*/ )
442
0
{
443
0
}
444
445
446
void SAL_CALL TableModel::addVetoableChangeListener( const OUString& /*aPropertyName*/, const uno::Reference<beans::XVetoableChangeListener>& /*xListener*/ )
447
0
{
448
0
}
449
450
451
void SAL_CALL TableModel::removeVetoableChangeListener( const OUString& /*aPropertyName*/, const uno::Reference<beans::XVetoableChangeListener>& /*xListener*/ )
452
0
{
453
0
}
454
455
456
// XFastPropertySet
457
458
459
void SAL_CALL TableModel::setFastPropertyValue( ::sal_Int32 /*nHandle*/, const uno::Any& /*aValue*/ )
460
0
{
461
0
}
462
463
464
uno::Any SAL_CALL TableModel::getFastPropertyValue( ::sal_Int32 /*nHandle*/ )
465
0
{
466
0
    uno::Any aAny;
467
0
    return aAny;
468
0
}
469
470
471
// internals
472
473
474
sal_Int32 TableModel::getRowCountImpl() const
475
891k
{
476
891k
    return static_cast< sal_Int32 >( maRows.size() );
477
891k
}
478
479
480
sal_Int32 TableModel::getColumnCountImpl() const
481
914k
{
482
914k
    return static_cast< sal_Int32 >( maColumns.size() );
483
914k
}
484
485
486
void TableModel::disposing(std::unique_lock<std::mutex>& rGuard)
487
1.03k
{
488
1.03k
    rGuard.unlock(); // do not hold this while taking solar mutex
489
1.03k
    ::SolarMutexGuard aGuard;
490
491
1.03k
    if( !maRows.empty() )
492
1.03k
    {
493
1.03k
        for( auto& rpRow : maRows )
494
3.38k
            rpRow->dispose();
495
1.03k
        RowVector().swap(maRows);
496
1.03k
    }
497
498
1.03k
    if( !maColumns.empty() )
499
1.03k
    {
500
1.03k
        for( auto& rpCol : maColumns )
501
4.01k
            rpCol->dispose();
502
1.03k
        ColumnVector().swap(maColumns);
503
1.03k
    }
504
505
1.03k
    if( mxTableColumns.is() )
506
1.03k
    {
507
1.03k
        mxTableColumns->dispose();
508
1.03k
        mxTableColumns.clear();
509
1.03k
    }
510
511
1.03k
    if( mxTableRows.is() )
512
1.03k
    {
513
1.03k
        mxTableRows->dispose();
514
1.03k
        mxTableRows.clear();
515
1.03k
    }
516
517
1.03k
    mpTableObj = nullptr;
518
519
1.03k
    rGuard.lock();
520
1.03k
}
521
522
523
// XBroadcaster
524
525
526
void TableModel::lockBroadcasts()
527
9.67k
{
528
9.67k
    ::SolarMutexGuard aGuard;
529
9.67k
    ++mnNotifyLock;
530
9.67k
}
531
532
533
void TableModel::unlockBroadcasts()
534
9.67k
{
535
9.67k
    ::SolarMutexGuard aGuard;
536
9.67k
    --mnNotifyLock;
537
9.67k
    if( mnNotifyLock <= 0 )
538
2.06k
    {
539
2.06k
        mnNotifyLock = 0;
540
2.06k
        if( mbNotifyPending )
541
1.02k
            notifyModification();
542
2.06k
    }
543
9.67k
}
544
545
546
void TableModel::notifyModification()
547
185k
{
548
185k
    if( (mnNotifyLock == 0) && mpTableObj )
549
1.02k
    {
550
1.02k
        mbNotifyPending = false;
551
552
1.02k
        lang::EventObject aSource;
553
1.02k
        aSource.Source = getXWeak();
554
1.02k
        std::unique_lock aGuard(m_aMutex);
555
1.02k
        maModifyListeners.notifyEach(aGuard, &util::XModifyListener::modified, aSource);
556
1.02k
    }
557
184k
    else
558
184k
    {
559
184k
        mbNotifyPending = true;
560
184k
    }
561
185k
}
562
563
564
CellRef TableModel::getCell( sal_Int32 nCol, sal_Int32 nRow ) const
565
565k
{
566
565k
    if( ((nRow >= 0) && (nRow < getRowCountImpl())) && (nCol >= 0) && (nCol < getColumnCountImpl()) )
567
555k
    {
568
555k
        return maRows[nRow]->maCells[nCol];
569
555k
    }
570
10.3k
    else
571
10.3k
    {
572
10.3k
        CellRef xRet;
573
10.3k
        return xRet;
574
10.3k
    }
575
565k
}
576
577
578
CellRef TableModel::createCell()
579
27.7k
{
580
27.7k
    CellRef xCell;
581
27.7k
    if( mpTableObj )
582
27.7k
        mpTableObj->createCell( xCell );
583
27.7k
    return xCell;
584
27.7k
}
585
586
587
void TableModel::insertColumns( sal_Int32 nIndex, sal_Int32 nCount )
588
1.78k
{
589
1.78k
    if( !(nCount && mpTableObj) )
590
0
        return;
591
592
1.78k
    try
593
1.78k
    {
594
1.78k
        SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
595
1.78k
        TableModelNotifyGuard aGuard( this );
596
1.78k
        nIndex = insert_range<ColumnVector,ColumnVector::iterator,TableColumnRef>( maColumns, nIndex, nCount );
597
598
1.78k
        sal_Int32 nRows = getRowCountImpl();
599
6.87k
        while( nRows-- )
600
5.09k
            maRows[nRows]->insertColumns( nIndex, nCount, nullptr );
601
602
1.78k
        ColumnVector aNewColumns(nCount);
603
5.98k
        for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
604
4.19k
        {
605
4.19k
            TableColumnRef xNewCol( new TableColumn( this, nIndex+nOffset ) );
606
4.19k
            maColumns[nIndex+nOffset] = xNewCol;
607
4.19k
            aNewColumns[nOffset] = std::move(xNewCol);
608
4.19k
        }
609
610
1.78k
        const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
611
612
1.78k
        if( bUndo )
613
846
        {
614
846
            rModel.BegUndo( SvxResId(STR_TABLE_INSCOL) );
615
846
            rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
616
617
846
            TableModelRef xThis( this );
618
619
846
            nRows = getRowCountImpl();
620
846
            CellVector aNewCells( nCount * nRows );
621
846
            CellVector::iterator aCellIter( aNewCells.begin() );
622
623
846
            nRows = getRowCountImpl();
624
1.69k
            for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
625
846
            {
626
1.69k
                for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
627
846
                    (*aCellIter++) = getCell( nIndex + nOffset, nRow );
628
846
            }
629
630
846
            rModel.AddUndo( std::make_unique<InsertColUndo>( xThis, nIndex, aNewColumns, aNewCells ) );
631
846
        }
632
633
1.78k
        const sal_Int32 nRowCount = getRowCountImpl();
634
        // check if cells merge over new columns
635
360k
        for( sal_Int32 nCol = 0; nCol < nIndex; ++nCol )
636
358k
        {
637
716k
            for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
638
358k
            {
639
358k
                CellRef xCell( getCell( nCol, nRow ) );
640
358k
                sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1;
641
358k
                if( (nColSpan != 1) && ((nColSpan + nCol ) > nIndex) )
642
0
                {
643
                    // cell merges over newly created columns, so add the new columns to the merged cell
644
0
                    const sal_Int32 nRowSpan = xCell->getRowSpan();
645
0
                    nColSpan += nCount;
646
0
                    merge( nCol, nRow, nColSpan, nRowSpan );
647
0
                }
648
358k
            }
649
358k
        }
650
651
1.78k
        if( bUndo )
652
846
            rModel.EndUndo();
653
654
1.78k
        rModel.SetChanged();
655
1.78k
    }
656
1.78k
    catch( uno::Exception& )
657
1.78k
    {
658
0
        TOOLS_WARN_EXCEPTION("svx", "");
659
0
    }
660
1.78k
    updateColumns();
661
1.78k
    setModified(true);
662
1.78k
}
663
664
665
void TableModel::removeColumns( sal_Int32 nIndex, sal_Int32 nCount )
666
1.22k
{
667
1.22k
    sal_Int32 nColCount = getColumnCountImpl();
668
669
1.22k
    if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nColCount)) )
670
0
        return;
671
672
1.22k
    try
673
1.22k
    {
674
1.22k
        TableModelNotifyGuard aGuard( this );
675
676
        // clip removed columns to columns actually available
677
1.22k
        if( (nIndex + nCount) > nColCount )
678
0
            nCount = nColCount - nIndex;
679
680
1.22k
        sal_Int32 nRows = getRowCountImpl();
681
1.22k
        SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
682
1.22k
        const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
683
684
1.22k
        if( bUndo  )
685
0
        {
686
0
            rModel.BegUndo( SvxResId(STR_UNDO_COL_DELETE) );
687
0
            rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
688
0
        }
689
690
        // only rows before and inside the removed rows are considered
691
1.22k
        nColCount = nIndex + nCount + 1;
692
693
1.22k
        const sal_Int32 nRowCount = getRowCountImpl();
694
695
        // first check merged cells before and inside the removed rows
696
7.78k
        for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
697
6.56k
        {
698
65.3k
            for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
699
58.7k
            {
700
58.7k
                CellRef xCell( getCell( nCol, nRow ) );
701
58.7k
                sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1;
702
58.7k
                if( nColSpan <= 1 )
703
57.3k
                    continue;
704
705
1.38k
                if( nCol >= nIndex )
706
37
                {
707
                    // current cell is inside the removed columns
708
37
                    if( (nCol + nColSpan) > ( nIndex + nCount ) )
709
37
                    {
710
                        // current cells merges with columns after the removed columns
711
37
                        const sal_Int32 nRemove = nCount - nCol + nIndex;
712
713
37
                        CellRef xTargetCell( getCell( nIndex + nCount, nRow ) );
714
37
                        if( xTargetCell.is() )
715
37
                        {
716
37
                            if( bUndo )
717
0
                                xTargetCell->AddUndo();
718
37
                            xTargetCell->merge( nColSpan - nRemove, xCell->getRowSpan() );
719
37
                            xTargetCell->replaceContentAndFormatting( xCell );
720
37
                        }
721
37
                    }
722
37
                }
723
1.35k
                else if( nColSpan > (nIndex - nCol) )
724
1.30k
                {
725
                    // current cells spans inside the removed columns, so adjust
726
1.30k
                    const sal_Int32 nRemove = ::std::min( nCount, nCol + nColSpan - nIndex );
727
1.30k
                    if( bUndo )
728
0
                        xCell->AddUndo();
729
1.30k
                    xCell->merge( nColSpan - nRemove, xCell->getRowSpan() );
730
1.30k
                }
731
1.38k
            }
732
6.56k
        }
733
734
        // We must not add RemoveColUndo before we make cell spans correct, otherwise we
735
        // get invalid cell span after undo.
736
1.22k
        if( bUndo  )
737
0
        {
738
0
            TableModelRef xThis( this );
739
0
            ColumnVector aRemovedCols( nCount );
740
0
            sal_Int32 nOffset;
741
0
            for( nOffset = 0; nOffset < nCount; ++nOffset )
742
0
            {
743
0
                aRemovedCols[nOffset] = maColumns[nIndex+nOffset];
744
0
            }
745
746
0
            CellVector aRemovedCells( nCount * nRows );
747
0
            CellVector::iterator aCellIter( aRemovedCells.begin() );
748
0
            for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
749
0
            {
750
0
                for( nOffset = 0; nOffset < nCount; ++nOffset )
751
0
                    (*aCellIter++) = getCell( nIndex + nOffset, nRow );
752
0
            }
753
754
0
            rModel.AddUndo( std::make_unique<RemoveColUndo>( xThis, nIndex, aRemovedCols, aRemovedCells ) );
755
0
        }
756
757
        // now remove the columns
758
1.22k
        remove_range<ColumnVector,ColumnVector::iterator>( maColumns, nIndex, nCount );
759
10.3k
        while( nRows-- )
760
9.13k
            maRows[nRows]->removeColumns( nIndex, nCount );
761
762
1.22k
        if( bUndo )
763
0
            rModel.EndUndo();
764
765
1.22k
        rModel.SetChanged();
766
1.22k
    }
767
1.22k
    catch( uno::Exception& )
768
1.22k
    {
769
0
        TOOLS_WARN_EXCEPTION("svx", "");
770
0
    }
771
772
1.22k
    updateColumns();
773
1.22k
    setModified(true);
774
1.22k
}
775
776
777
void TableModel::insertRows( sal_Int32 nIndex, sal_Int32 nCount )
778
921
{
779
921
    if( !(nCount && mpTableObj) )
780
0
        return;
781
782
921
    SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
783
921
    const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
784
785
921
    try
786
921
    {
787
921
        TableModelNotifyGuard aGuard( this );
788
789
921
        nIndex = insert_range<RowVector,RowVector::iterator,TableRowRef>( maRows, nIndex, nCount );
790
791
921
        RowVector aNewRows(nCount);
792
921
        const sal_Int32 nColCount = getColumnCountImpl();
793
4.78k
        for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
794
3.86k
        {
795
3.86k
            TableRowRef xNewRow( new TableRow( this, nIndex+nOffset, nColCount ) );
796
3.86k
            maRows[nIndex+nOffset] = xNewRow;
797
3.86k
            aNewRows[nOffset] = std::move(xNewRow);
798
3.86k
        }
799
800
921
        if( bUndo )
801
0
        {
802
0
            rModel.BegUndo( SvxResId(STR_TABLE_INSROW) );
803
0
            rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
804
0
            TableModelRef xThis( this );
805
0
            rModel.AddUndo( std::make_unique<InsertRowUndo>( xThis, nIndex, aNewRows ) );
806
0
        }
807
808
        // check if cells merge over new columns
809
921
        for( sal_Int32 nRow = 0; nRow < nIndex; ++nRow )
810
0
        {
811
0
            for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
812
0
            {
813
0
                CellRef xCell( getCell( nCol, nRow ) );
814
0
                sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1;
815
0
                if( (nRowSpan > 1) && ((nRowSpan + nRow) > nIndex) )
816
0
                {
817
                    // cell merges over newly created columns, so add the new columns to the merged cell
818
0
                    const sal_Int32 nColSpan = xCell->getColumnSpan();
819
0
                    nRowSpan += nCount;
820
0
                    merge( nCol, nRow, nColSpan, nRowSpan );
821
0
                }
822
0
            }
823
0
        }
824
921
    }
825
921
    catch( uno::Exception& )
826
921
    {
827
0
        TOOLS_WARN_EXCEPTION("svx", "");
828
0
    }
829
921
    if( bUndo )
830
0
        rModel.EndUndo();
831
832
921
    rModel.SetChanged();
833
834
921
    updateRows();
835
921
    setModified(true);
836
921
}
837
838
839
void TableModel::removeRows( sal_Int32 nIndex, sal_Int32 nCount )
840
1.51k
{
841
1.51k
    sal_Int32 nRowCount = getRowCountImpl();
842
843
1.51k
    if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nRowCount)) )
844
0
        return;
845
846
1.51k
    SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
847
1.51k
    const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
848
849
1.51k
    try
850
1.51k
    {
851
1.51k
        TableModelNotifyGuard aGuard( this );
852
853
        // clip removed rows to rows actually available
854
1.51k
        if( (nIndex + nCount) > nRowCount )
855
0
            nCount = nRowCount - nIndex;
856
857
1.51k
        if( bUndo )
858
0
        {
859
0
            rModel.BegUndo( SvxResId(STR_UNDO_ROW_DELETE) );
860
0
            rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
861
0
        }
862
863
        // only rows before and inside the removed rows are considered
864
1.51k
        nRowCount = nIndex + nCount + 1;
865
866
1.51k
        const sal_Int32 nColCount = getColumnCountImpl();
867
868
        // first check merged cells before and inside the removed rows
869
11.9k
        for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
870
10.4k
        {
871
27.2k
            for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
872
16.8k
            {
873
16.8k
                CellRef xCell( getCell( nCol, nRow ) );
874
16.8k
                sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1;
875
16.8k
                if( nRowSpan <= 1 )
876
15.2k
                    continue;
877
878
1.60k
                if( nRow >= nIndex )
879
17
                {
880
                    // current cell is inside the removed rows
881
17
                    if( (nRow + nRowSpan) > (nIndex + nCount) )
882
17
                    {
883
                        // current cells merges with rows after the removed rows
884
17
                        const sal_Int32 nRemove = nCount - nRow + nIndex;
885
886
17
                        CellRef xTargetCell( getCell( nCol, nIndex + nCount ) );
887
17
                        if( xTargetCell.is() )
888
17
                        {
889
17
                            if( bUndo )
890
0
                                xTargetCell->AddUndo();
891
17
                            xTargetCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
892
17
                            xTargetCell->replaceContentAndFormatting( xCell );
893
17
                        }
894
17
                    }
895
17
                }
896
1.58k
                else if( nRowSpan > (nIndex - nRow) )
897
1.55k
                {
898
                    // current cells spans inside the removed rows, so adjust
899
1.55k
                    const sal_Int32 nRemove = ::std::min( nCount, nRow + nRowSpan - nIndex );
900
1.55k
                    if( bUndo )
901
0
                        xCell->AddUndo();
902
1.55k
                    xCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
903
1.55k
                }
904
1.60k
            }
905
10.4k
        }
906
907
1.51k
        if( bUndo )
908
0
        {
909
0
            TableModelRef xThis( this );
910
911
0
            RowVector aRemovedRows( nCount );
912
0
            for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
913
0
                aRemovedRows[nOffset] = maRows[nIndex+nOffset];
914
915
            // We must not RemoveRowUndo before we make cell spans correct, otherwise we
916
            // get invalid cell span after undo.
917
0
            rModel.AddUndo( std::make_unique<RemoveRowUndo>( xThis, nIndex, aRemovedRows ) );
918
0
        }
919
        // now remove the rows
920
1.51k
        remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
921
922
1.51k
        if( bUndo )
923
0
            rModel.EndUndo();
924
925
1.51k
        rModel.SetChanged();
926
1.51k
    }
927
1.51k
    catch( uno::Exception& )
928
1.51k
    {
929
0
        TOOLS_WARN_EXCEPTION("svx", "");
930
0
    }
931
932
1.51k
    updateRows();
933
1.51k
    setModified(true);
934
1.51k
}
935
936
937
TableRowRef const & TableModel::getRow( sal_Int32 nRow ) const
938
4.89k
{
939
4.89k
    if( (nRow >= 0) && (nRow < getRowCountImpl()) )
940
4.89k
        return maRows[nRow];
941
942
0
    throw lang::IndexOutOfBoundsException();
943
4.89k
}
944
945
946
TableColumnRef const & TableModel::getColumn( sal_Int32 nColumn ) const
947
4.38k
{
948
4.38k
    if( (nColumn >= 0) && (nColumn < getColumnCountImpl()) )
949
4.38k
        return maColumns[nColumn];
950
951
0
    throw lang::IndexOutOfBoundsException();
952
4.38k
}
953
954
955
/** deletes rows and columns that are completely merged. Must be called between BegUndo/EndUndo! */
956
void TableModel::optimize()
957
2.05k
{
958
2.05k
    TableModelNotifyGuard aGuard( this );
959
960
2.05k
    bool bWasModified = false;
961
962
2.05k
    if( !maRows.empty() && !maColumns.empty() )
963
2.05k
    {
964
2.05k
        sal_Int32 nCol = getColumnCountImpl() - 1;
965
2.05k
        sal_Int32 nRows = getRowCountImpl();
966
12.8k
        while( nCol > 0 )
967
10.8k
        {
968
10.8k
            bool bEmpty = true;
969
31.6k
            for( sal_Int32 nRow = 0; (nRow < nRows) && bEmpty; nRow++ )
970
20.8k
            {
971
20.8k
                uno::Reference<css::table::XMergeableCell> xCell( getCellByPosition( nCol, nRow ), uno::UNO_QUERY );
972
20.8k
                if( xCell.is() && !xCell->isMerged() )
973
9.59k
                    bEmpty = false;
974
20.8k
            }
975
976
10.8k
            if( bEmpty )
977
1.22k
            {
978
1.22k
                try
979
1.22k
                {
980
1.22k
                    static constexpr OUString sWidth(u"Width"_ustr);
981
1.22k
                    sal_Int32 nWidth1 = 0, nWidth2 = 0;
982
1.22k
                    uno::Reference<beans::XPropertySet> xSet1( static_cast< XCellRange* >( maColumns[nCol].get() ), uno::UNO_QUERY_THROW );
983
1.22k
                    uno::Reference<beans::XPropertySet> xSet2( static_cast< XCellRange* >( maColumns[nCol-1].get() ), uno::UNO_QUERY_THROW );
984
1.22k
                    xSet1->getPropertyValue( sWidth ) >>= nWidth1;
985
1.22k
                    xSet2->getPropertyValue( sWidth ) >>= nWidth2;
986
1.22k
                    nWidth1 = o3tl::saturating_add(nWidth1, nWidth2);
987
1.22k
                    xSet2->setPropertyValue( sWidth, uno::Any( nWidth1 ) );
988
1.22k
                }
989
1.22k
                catch( uno::Exception& )
990
1.22k
                {
991
0
                    TOOLS_WARN_EXCEPTION("svx", "");
992
0
                }
993
994
1.22k
                removeColumns( nCol, 1 );
995
1.22k
                bWasModified = true;
996
1.22k
            }
997
998
10.8k
            nCol--;
999
10.8k
        }
1000
1001
2.05k
        sal_Int32 nRow = getRowCountImpl() - 1;
1002
2.05k
        sal_Int32 nCols = getColumnCountImpl();
1003
16.9k
        while( nRow > 0 )
1004
14.8k
        {
1005
14.8k
            bool bEmpty = true;
1006
32.4k
            for( nCol = 0; (nCol < nCols) && bEmpty; nCol++ )
1007
17.6k
            {
1008
17.6k
                uno::Reference<css::table::XMergeableCell> xCell( getCellByPosition( nCol, nRow ), uno::UNO_QUERY );
1009
17.6k
                if( xCell.is() && !xCell->isMerged() )
1010
13.3k
                    bEmpty = false;
1011
17.6k
            }
1012
1013
14.8k
            if( bEmpty )
1014
1.51k
            {
1015
1.51k
                try
1016
1.51k
                {
1017
1.51k
                    static constexpr OUString sHeight(u"Height"_ustr);
1018
1.51k
                    sal_Int32 nHeight1 = 0, nHeight2 = 0;
1019
1.51k
                    uno::Reference<beans::XPropertySet> xSet1( static_cast< XCellRange* >( maRows[nRow].get() ), uno::UNO_QUERY_THROW );
1020
1.51k
                    uno::Reference<beans::XPropertySet> xSet2( static_cast< XCellRange* >( maRows[nRow-1].get() ), uno::UNO_QUERY_THROW );
1021
1.51k
                    xSet1->getPropertyValue( sHeight ) >>= nHeight1;
1022
1.51k
                    xSet2->getPropertyValue( sHeight ) >>= nHeight2;
1023
1.51k
                    nHeight1 = o3tl::saturating_add(nHeight1, nHeight2);
1024
1.51k
                    xSet2->setPropertyValue( sHeight, uno::Any( nHeight1 ) );
1025
1.51k
                }
1026
1.51k
                catch( uno::Exception& )
1027
1.51k
                {
1028
0
                    TOOLS_WARN_EXCEPTION("svx", "");
1029
0
                }
1030
1031
1.51k
                removeRows( nRow, 1 );
1032
1.51k
                bWasModified = true;
1033
1.51k
            }
1034
1035
14.8k
            nRow--;
1036
14.8k
        }
1037
2.05k
    }
1038
2.05k
    if( bWasModified )
1039
432
        setModified(true);
1040
2.05k
}
1041
1042
1043
void TableModel::merge( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan )
1044
2.05k
{
1045
2.05k
    if(nullptr == mpTableObj)
1046
0
        return;
1047
1048
2.05k
    SdrModel& rModel(mpTableObj->getSdrModelFromSdrObject());
1049
2.05k
    const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
1050
2.05k
    const sal_Int32 nLastRow = nRow + nRowSpan;
1051
2.05k
    const sal_Int32 nLastCol = nCol + nColSpan;
1052
1053
2.05k
    if( (nLastRow > getRowCount()) || (nLastCol > getColumnCount() ) )
1054
0
    {
1055
0
        OSL_FAIL("TableModel::merge(), merge beyond the table!");
1056
0
    }
1057
1058
    // merge first cell
1059
2.05k
    CellRef xOriginCell( getCell( nCol, nRow ) );
1060
2.05k
    if(!xOriginCell.is())
1061
0
        return;
1062
1063
2.05k
    if( bUndo )
1064
0
        xOriginCell->AddUndo();
1065
2.05k
    xOriginCell->merge( nColSpan, nRowSpan );
1066
1067
2.05k
    sal_Int32 nTempCol = nCol + 1;
1068
1069
    // merge remaining cells
1070
8.65k
    for( ; nRow < nLastRow; nRow++ )
1071
6.59k
    {
1072
29.9k
        for( ; nTempCol < nLastCol; nTempCol++ )
1073
23.3k
        {
1074
23.3k
            CellRef xCell( getCell( nTempCol, nRow ) );
1075
23.3k
            if( xCell.is() && !xCell->isMerged() )
1076
16.3k
            {
1077
16.3k
                if( bUndo )
1078
0
                    xCell->AddUndo();
1079
16.3k
                xCell->setMerged();
1080
16.3k
                xOriginCell->mergeContent( xCell );
1081
16.3k
            }
1082
23.3k
        }
1083
6.59k
        nTempCol = nCol;
1084
6.59k
    }
1085
2.05k
}
1086
1087
void TableModel::updateRows()
1088
2.43k
{
1089
2.43k
    sal_Int32 nRow = 0;
1090
2.43k
    for( auto& rpRow : maRows )
1091
12.4k
    {
1092
12.4k
        rpRow->mnRow = nRow++;
1093
12.4k
    }
1094
2.43k
}
1095
1096
void TableModel::updateColumns()
1097
3.00k
{
1098
3.00k
    sal_Int32 nColumn = 0;
1099
3.00k
    for( auto& rpCol : maColumns )
1100
367k
    {
1101
367k
        rpCol->mnColumn = nColumn++;
1102
367k
    }
1103
3.00k
}
1104
1105
void TableModel::dumpAsXml(xmlTextWriterPtr pWriter) const
1106
0
{
1107
0
    (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableModel"));
1108
0
    for (sal_Int32 nRow = 0; nRow < getRowCountImpl(); ++nRow)
1109
0
        for (sal_Int32 nCol = 0; nCol < getColumnCountImpl(); ++nCol)
1110
0
        {
1111
0
            maRows[nRow]->maCells[nCol]->dumpAsXml(pWriter, nRow, nCol);
1112
0
        }
1113
0
    (void)xmlTextWriterEndElement(pWriter);
1114
0
}
1115
1116
}
1117
1118
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */