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