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