1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <sal/config.h>
22 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
23 #include <com/sun/star/table/XMergeableCell.hpp>
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>
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 <sdr/properties/cellproperties.hxx>
43 #include <svx/svdotable.hxx>
44 #include <svx/svdmodel.hxx>
45 #include <svx/strings.hrc>
46 #include <svx/dialmgr.hxx>
50 namespace sdr::table
{
53 // removes the given range from a vector
54 template< class Vec
, class Iter
> static void remove_range( Vec
& rVector
, sal_Int32 nIndex
, sal_Int32 nCount
)
56 const sal_Int32 nSize
= static_cast<sal_Int32
>(rVector
.size());
57 if( nCount
&& (nIndex
>= 0) && (nIndex
< nSize
) )
59 if( (nIndex
+ nCount
) >= nSize
)
62 rVector
.resize( nIndex
);
66 rVector
.erase(rVector
.begin() + nIndex
, rVector
.begin() + nIndex
+ nCount
);
72 /** inserts a range into a vector */
73 template< class Vec
, class Iter
, class Entry
> static sal_Int32
insert_range( Vec
& rVector
, sal_Int32 nIndex
, sal_Int32 nCount
)
77 if( nIndex
>= static_cast< sal_Int32
>( rVector
.size() ) )
80 nIndex
= static_cast< sal_Int32
>( rVector
.size() ); // cap to end
81 rVector
.resize( nIndex
+ nCount
);
86 Iter
aIter( rVector
.begin() );
87 std::advance( aIter
, nIndex
);
90 rVector
.insert( aIter
, nCount
, aEmpty
);
97 TableModel::TableModel( SdrTableObj
* pTableObj
)
98 : TableModelBase( m_aMutex
)
99 , mpTableObj( pTableObj
)
100 , mbModified( false )
101 , mbNotifyPending( false )
106 TableModel::TableModel( SdrTableObj
* pTableObj
, const TableModelRef
& xSourceTable
)
107 : TableModelBase( m_aMutex
)
108 , mpTableObj( pTableObj
)
109 , mbModified( false )
110 , mbNotifyPending( false )
113 if( !xSourceTable
.is() )
116 const sal_Int32 nColCount
= xSourceTable
->getColumnCountImpl();
117 const sal_Int32 nRowCount
= xSourceTable
->getRowCountImpl();
119 init( nColCount
, nRowCount
);
121 sal_Int32 nRows
= nRowCount
;
123 (*maRows
[nRows
]) = *xSourceTable
->maRows
[nRows
];
125 sal_Int32 nColumns
= nColCount
;
127 (*maColumns
[nColumns
]) = *xSourceTable
->maColumns
[nColumns
];
130 for( sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
132 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; ++nRow
)
134 CellRef
xTargetCell( getCell( nCol
, nRow
) );
135 if( xTargetCell
.is() )
136 xTargetCell
->cloneFrom( xSourceTable
->getCell( nCol
, nRow
) );
142 TableModel::~TableModel()
147 void TableModel::init( sal_Int32 nColumns
, sal_Int32 nRows
)
150 maRows
.reserve( 20 );
153 maColumns
.reserve( 20 );
155 if( nRows
&& nColumns
)
157 maColumns
.resize( nColumns
);
158 maRows
.resize( nRows
);
161 maRows
[nRows
].set( new TableRow( this, nRows
, nColumns
) );
164 maColumns
[nColumns
].set( new TableColumn( this, nColumns
) );
172 sal_Int32
TableModel::getLeft()
178 sal_Int32
TableModel::getTop()
184 sal_Int32
TableModel::getRight()
186 return getColumnCount();
190 sal_Int32
TableModel::getBottom()
192 return getRowCount();
196 uno::Reference
<css::table::XTable
> TableModel::getTable()
202 void TableModel::UndoInsertRows( sal_Int32 nIndex
, sal_Int32 nCount
)
204 TableModelNotifyGuard
aGuard( this );
207 remove_range
<RowVector
,RowVector::iterator
>( maRows
, nIndex
, nCount
);
213 void TableModel::UndoRemoveRows( sal_Int32 nIndex
, RowVector
& aRows
)
215 TableModelNotifyGuard
aGuard( this );
217 const sal_Int32 nCount
= sal::static_int_cast
< sal_Int32
>( aRows
.size() );
219 nIndex
= insert_range
<RowVector
,RowVector::iterator
,TableRowRef
>( maRows
, nIndex
, nCount
);
221 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
222 maRows
[nIndex
+nOffset
] = aRows
[nOffset
];
229 void TableModel::UndoInsertColumns( sal_Int32 nIndex
, sal_Int32 nCount
)
231 TableModelNotifyGuard
aGuard( this );
233 // now remove the columns
234 remove_range
<ColumnVector
,ColumnVector::iterator
>( maColumns
, nIndex
, nCount
);
235 sal_Int32 nRows
= getRowCountImpl();
237 maRows
[nRows
]->removeColumns( nIndex
, nCount
);
244 void TableModel::UndoRemoveColumns( sal_Int32 nIndex
, ColumnVector
& aCols
, CellVector
& aCells
)
246 TableModelNotifyGuard
aGuard( this );
248 const sal_Int32 nCount
= sal::static_int_cast
< sal_Int32
>( aCols
.size() );
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!" );
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
];
257 CellVector::iterator
aIter( aCells
.begin() );
259 sal_Int32 nRows
= getRowCountImpl();
260 for( sal_Int32 nRow
= 0; nRow
< nRows
; ++nRow
)
262 CellVector::iterator aIter2
= aIter
+ nRow
* nCount
;
263 OSL_ENSURE(aIter2
< aCells
.end(), "invalid iterator!");
264 maRows
[nRow
]->insertColumns( nIndex
, nCount
, &aIter2
);
275 uno::Reference
<css::table::XCellCursor
> SAL_CALL
TableModel::createCursor()
277 ::SolarMutexGuard aGuard
;
278 return createCursorByRange( uno::Reference
< XCellRange
>( this ) );
282 uno::Reference
<css::table::XCellCursor
> SAL_CALL
TableModel::createCursorByRange( const uno::Reference
< XCellRange
>& rRange
)
284 ::SolarMutexGuard aGuard
;
286 ICellRange
* pRange
= dynamic_cast< ICellRange
* >( rRange
.get() );
287 if( (pRange
== nullptr) || (pRange
->getTable().get() != this) )
288 throw lang::IllegalArgumentException();
290 TableModelRef
xModel( this );
291 return new CellCursor( xModel
, pRange
->getLeft(), pRange
->getTop(), pRange
->getRight(), pRange
->getBottom() );
295 sal_Int32 SAL_CALL
TableModel::getRowCount()
297 ::SolarMutexGuard aGuard
;
298 return getRowCountImpl();
301 sal_Int32 SAL_CALL
TableModel::getColumnCount()
303 ::SolarMutexGuard aGuard
;
304 return getColumnCountImpl();
307 std::vector
<sal_Int32
> TableModel::getColumnWidths()
309 std::vector
<sal_Int32
> aRet
;
310 for (const TableColumnRef
& xColumn
: maColumns
)
311 aRet
.push_back(xColumn
->getWidth());
318 void TableModel::dispose()
320 ::SolarMutexGuard aGuard
;
321 TableModelBase::dispose();
328 sal_Bool SAL_CALL
TableModel::isModified( )
330 ::SolarMutexGuard aGuard
;
335 void SAL_CALL
TableModel::setModified( sal_Bool bModified
)
338 ::SolarMutexGuard aGuard
;
339 mbModified
= bModified
;
342 notifyModification();
346 // XModifyBroadcaster
349 void SAL_CALL
TableModel::addModifyListener( const uno::Reference
<util::XModifyListener
>& xListener
)
351 rBHelper
.addListener( cppu::UnoType
<util::XModifyListener
>::get() , xListener
);
355 void SAL_CALL
TableModel::removeModifyListener( const uno::Reference
<util::XModifyListener
>& xListener
)
357 rBHelper
.removeListener( cppu::UnoType
<util::XModifyListener
>::get() , xListener
);
364 uno::Reference
<css::table::XTableColumns
> SAL_CALL
TableModel::getColumns()
366 ::SolarMutexGuard aGuard
;
368 if( !mxTableColumns
.is() )
369 mxTableColumns
.set( new TableColumns( this ) );
370 return mxTableColumns
;
374 uno::Reference
<css::table::XTableRows
> SAL_CALL
TableModel::getRows()
376 ::SolarMutexGuard aGuard
;
378 if( !mxTableRows
.is() )
379 mxTableRows
.set( new TableRows( this ) );
387 uno::Reference
<css::table::XCell
> SAL_CALL
TableModel::getCellByPosition( sal_Int32 nColumn
, sal_Int32 nRow
)
389 ::SolarMutexGuard aGuard
;
391 CellRef
xCell( getCell( nColumn
, nRow
) );
395 throw lang::IndexOutOfBoundsException();
399 uno::Reference
<css::table::XCellRange
> SAL_CALL
TableModel::getCellRangeByPosition( sal_Int32 nLeft
, sal_Int32 nTop
, sal_Int32 nRight
, sal_Int32 nBottom
)
401 ::SolarMutexGuard aGuard
;
403 if( (nLeft
>= 0) && (nTop
>= 0) && (nRight
>= nLeft
) && (nBottom
>= nTop
) && (nRight
< getColumnCountImpl()) && (nBottom
< getRowCountImpl() ) )
405 TableModelRef
xModel( this );
406 return new CellRange( xModel
, nLeft
, nTop
, nRight
, nBottom
);
409 throw lang::IndexOutOfBoundsException();
413 uno::Reference
<css::table::XCellRange
> SAL_CALL
TableModel::getCellRangeByName( const OUString
& /*aRange*/ )
415 return uno::Reference
< XCellRange
>();
422 uno::Reference
<beans::XPropertySetInfo
> SAL_CALL
TableModel::getPropertySetInfo( )
424 uno::Reference
<beans::XPropertySetInfo
> xInfo
;
429 void SAL_CALL
TableModel::setPropertyValue( const OUString
& /*aPropertyName*/, const uno::Any
& /*aValue*/ )
434 uno::Any SAL_CALL
TableModel::getPropertyValue( const OUString
& /*PropertyName*/ )
440 void SAL_CALL
TableModel::addPropertyChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
<beans::XPropertyChangeListener
>& /*xListener*/ )
445 void SAL_CALL
TableModel::removePropertyChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
<beans::XPropertyChangeListener
>& /*xListener*/ )
450 void SAL_CALL
TableModel::addVetoableChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
<beans::XVetoableChangeListener
>& /*xListener*/ )
455 void SAL_CALL
TableModel::removeVetoableChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
<beans::XVetoableChangeListener
>& /*xListener*/ )
463 void SAL_CALL
TableModel::setFastPropertyValue( ::sal_Int32
/*nHandle*/, const uno::Any
& /*aValue*/ )
468 uno::Any SAL_CALL
TableModel::getFastPropertyValue( ::sal_Int32
/*nHandle*/ )
478 sal_Int32
TableModel::getRowCountImpl() const
480 return static_cast< sal_Int32
>( maRows
.size() );
484 sal_Int32
TableModel::getColumnCountImpl() const
486 return static_cast< sal_Int32
>( maColumns
.size() );
490 void TableModel::disposing()
492 if( !maRows
.empty() )
494 for( auto& rpRow
: maRows
)
496 RowVector().swap(maRows
);
499 if( !maColumns
.empty() )
501 for( auto& rpCol
: maColumns
)
503 ColumnVector().swap(maColumns
);
506 if( mxTableColumns
.is() )
508 mxTableColumns
->dispose();
509 mxTableColumns
.clear();
512 if( mxTableRows
.is() )
514 mxTableRows
->dispose();
518 mpTableObj
= nullptr;
525 void TableModel::lockBroadcasts()
527 ::SolarMutexGuard aGuard
;
532 void TableModel::unlockBroadcasts()
534 ::SolarMutexGuard aGuard
;
536 if( mnNotifyLock
<= 0 )
539 if( mbNotifyPending
)
540 notifyModification();
545 void TableModel::notifyModification()
547 ::osl::MutexGuard
guard( m_aMutex
);
548 if( (mnNotifyLock
== 0) && mpTableObj
)
550 mbNotifyPending
= false;
552 ::cppu::OInterfaceContainerHelper
* pModifyListeners
= rBHelper
.getContainer( cppu::UnoType
<util::XModifyListener
>::get() );
553 if( pModifyListeners
)
555 lang::EventObject aSource
;
556 aSource
.Source
= static_cast< ::cppu::OWeakObject
* >(this);
557 pModifyListeners
->notifyEach(&util::XModifyListener::modified
, aSource
);
562 mbNotifyPending
= true;
567 CellRef
TableModel::getCell( sal_Int32 nCol
, sal_Int32 nRow
) const
569 if( ((nRow
>= 0) && (nRow
< getRowCountImpl())) && (nCol
>= 0) && (nCol
< getColumnCountImpl()) )
571 return maRows
[nRow
]->maCells
[nCol
];
581 CellRef
TableModel::createCell()
585 mpTableObj
->createCell( xCell
);
590 void TableModel::insertColumns( sal_Int32 nIndex
, sal_Int32 nCount
)
592 if( !(nCount
&& mpTableObj
) )
597 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
598 TableModelNotifyGuard
aGuard( this );
599 nIndex
= insert_range
<ColumnVector
,ColumnVector::iterator
,TableColumnRef
>( maColumns
, nIndex
, nCount
);
601 sal_Int32 nRows
= getRowCountImpl();
603 maRows
[nRows
]->insertColumns( nIndex
, nCount
, nullptr );
605 ColumnVector
aNewColumns(nCount
);
606 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
608 TableColumnRef
xNewCol( new TableColumn( this, nIndex
+nOffset
) );
609 maColumns
[nIndex
+nOffset
] = xNewCol
;
610 aNewColumns
[nOffset
] = xNewCol
;
613 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
617 rModel
.BegUndo( SvxResId(STR_TABLE_INSCOL
) );
618 rModel
.AddUndo( rModel
.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj
) );
620 TableModelRef
xThis( this );
622 nRows
= getRowCountImpl();
623 CellVector
aNewCells( nCount
* nRows
);
624 CellVector::iterator
aCellIter( aNewCells
.begin() );
626 nRows
= getRowCountImpl();
627 for( sal_Int32 nRow
= 0; nRow
< nRows
; ++nRow
)
629 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
630 (*aCellIter
++) = getCell( nIndex
+ nOffset
, nRow
);
633 rModel
.AddUndo( std::make_unique
<InsertColUndo
>( xThis
, nIndex
, aNewColumns
, aNewCells
) );
636 const sal_Int32 nRowCount
= getRowCountImpl();
637 // check if cells merge over new columns
638 for( sal_Int32 nCol
= 0; nCol
< nIndex
; ++nCol
)
640 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; ++nRow
)
642 CellRef
xCell( getCell( nCol
, nRow
) );
643 sal_Int32 nColSpan
= (xCell
.is() && !xCell
->isMerged()) ? xCell
->getColumnSpan() : 1;
644 if( (nColSpan
!= 1) && ((nColSpan
+ nCol
) > nIndex
) )
646 // cell merges over newly created columns, so add the new columns to the merged cell
647 const sal_Int32 nRowSpan
= xCell
->getRowSpan();
649 merge( nCol
, nRow
, nColSpan
, nRowSpan
);
659 catch( uno::Exception
& )
661 TOOLS_WARN_EXCEPTION("svx", "");
668 void TableModel::removeColumns( sal_Int32 nIndex
, sal_Int32 nCount
)
670 sal_Int32 nColCount
= getColumnCountImpl();
672 if( !(mpTableObj
&& nCount
&& (nIndex
>= 0) && (nIndex
< nColCount
)) )
677 TableModelNotifyGuard
aGuard( this );
679 // clip removed columns to columns actually available
680 if( (nIndex
+ nCount
) > nColCount
)
681 nCount
= nColCount
- nIndex
;
683 sal_Int32 nRows
= getRowCountImpl();
684 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
685 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
689 rModel
.BegUndo( SvxResId(STR_UNDO_COL_DELETE
) );
690 rModel
.AddUndo( rModel
.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj
) );
693 // only rows before and inside the removed rows are considered
694 nColCount
= nIndex
+ nCount
+ 1;
696 const sal_Int32 nRowCount
= getRowCountImpl();
698 // first check merged cells before and inside the removed rows
699 for( sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
701 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; ++nRow
)
703 CellRef
xCell( getCell( nCol
, nRow
) );
704 sal_Int32 nColSpan
= (xCell
.is() && !xCell
->isMerged()) ? xCell
->getColumnSpan() : 1;
710 // current cell is inside the removed columns
711 if( (nCol
+ nColSpan
) > ( nIndex
+ nCount
) )
713 // current cells merges with columns after the removed columns
714 const sal_Int32 nRemove
= nCount
- nCol
+ nIndex
;
716 CellRef
xTargetCell( getCell( nIndex
+ nCount
, nRow
) );
717 if( xTargetCell
.is() )
720 xTargetCell
->AddUndo();
721 xTargetCell
->merge( nColSpan
- nRemove
, xCell
->getRowSpan() );
722 xTargetCell
->replaceContentAndFormatting( xCell
);
726 else if( nColSpan
> (nIndex
- nCol
) )
728 // current cells spans inside the removed columns, so adjust
729 const sal_Int32 nRemove
= ::std::min( nCount
, nCol
+ nColSpan
- nIndex
);
732 xCell
->merge( nColSpan
- nRemove
, xCell
->getRowSpan() );
737 // We must not add RemoveColUndo before we make cell spans correct, otherwise we
738 // get invalid cell span after undo.
741 TableModelRef
xThis( this );
742 ColumnVector
aRemovedCols( nCount
);
744 for( nOffset
= 0; nOffset
< nCount
; ++nOffset
)
746 aRemovedCols
[nOffset
] = maColumns
[nIndex
+nOffset
];
749 CellVector
aRemovedCells( nCount
* nRows
);
750 CellVector::iterator
aCellIter( aRemovedCells
.begin() );
751 for( sal_Int32 nRow
= 0; nRow
< nRows
; ++nRow
)
753 for( nOffset
= 0; nOffset
< nCount
; ++nOffset
)
754 (*aCellIter
++) = getCell( nIndex
+ nOffset
, nRow
);
757 rModel
.AddUndo( std::make_unique
<RemoveColUndo
>( xThis
, nIndex
, aRemovedCols
, aRemovedCells
) );
760 // now remove the columns
761 remove_range
<ColumnVector
,ColumnVector::iterator
>( maColumns
, nIndex
, nCount
);
763 maRows
[nRows
]->removeColumns( nIndex
, nCount
);
770 catch( uno::Exception
& )
772 TOOLS_WARN_EXCEPTION("svx", "");
780 void TableModel::insertRows( sal_Int32 nIndex
, sal_Int32 nCount
)
782 if( !(nCount
&& mpTableObj
) )
785 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
786 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
790 TableModelNotifyGuard
aGuard( this );
792 nIndex
= insert_range
<RowVector
,RowVector::iterator
,TableRowRef
>( maRows
, nIndex
, nCount
);
794 RowVector
aNewRows(nCount
);
795 const sal_Int32 nColCount
= getColumnCountImpl();
796 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
798 TableRowRef
xNewRow( new TableRow( this, nIndex
+nOffset
, nColCount
) );
799 maRows
[nIndex
+nOffset
] = xNewRow
;
800 aNewRows
[nOffset
] = xNewRow
;
805 rModel
.BegUndo( SvxResId(STR_TABLE_INSROW
) );
806 rModel
.AddUndo( rModel
.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj
) );
807 TableModelRef
xThis( this );
808 rModel
.AddUndo( std::make_unique
<InsertRowUndo
>( xThis
, nIndex
, aNewRows
) );
811 // check if cells merge over new columns
812 for( sal_Int32 nRow
= 0; nRow
< nIndex
; ++nRow
)
814 for( sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
816 CellRef
xCell( getCell( nCol
, nRow
) );
817 sal_Int32 nRowSpan
= (xCell
.is() && !xCell
->isMerged()) ? xCell
->getRowSpan() : 1;
818 if( (nRowSpan
> 1) && ((nRowSpan
+ nRow
) > nIndex
) )
820 // cell merges over newly created columns, so add the new columns to the merged cell
821 const sal_Int32 nColSpan
= xCell
->getColumnSpan();
823 merge( nCol
, nRow
, nColSpan
, nRowSpan
);
828 catch( uno::Exception
& )
830 TOOLS_WARN_EXCEPTION("svx", "");
842 void TableModel::removeRows( sal_Int32 nIndex
, sal_Int32 nCount
)
844 sal_Int32 nRowCount
= getRowCountImpl();
846 if( !(mpTableObj
&& nCount
&& (nIndex
>= 0) && (nIndex
< nRowCount
)) )
849 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
850 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
854 TableModelNotifyGuard
aGuard( this );
856 // clip removed rows to rows actually available
857 if( (nIndex
+ nCount
) > nRowCount
)
858 nCount
= nRowCount
- nIndex
;
862 rModel
.BegUndo( SvxResId(STR_UNDO_ROW_DELETE
) );
863 rModel
.AddUndo( rModel
.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj
) );
866 // only rows before and inside the removed rows are considered
867 nRowCount
= nIndex
+ nCount
+ 1;
869 const sal_Int32 nColCount
= getColumnCountImpl();
871 // first check merged cells before and inside the removed rows
872 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; ++nRow
)
874 for( sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
876 CellRef
xCell( getCell( nCol
, nRow
) );
877 sal_Int32 nRowSpan
= (xCell
.is() && !xCell
->isMerged()) ? xCell
->getRowSpan() : 1;
883 // current cell is inside the removed rows
884 if( (nRow
+ nRowSpan
) > (nIndex
+ nCount
) )
886 // current cells merges with rows after the removed rows
887 const sal_Int32 nRemove
= nCount
- nRow
+ nIndex
;
889 CellRef
xTargetCell( getCell( nCol
, nIndex
+ nCount
) );
890 if( xTargetCell
.is() )
893 xTargetCell
->AddUndo();
894 xTargetCell
->merge( xCell
->getColumnSpan(), nRowSpan
- nRemove
);
895 xTargetCell
->replaceContentAndFormatting( xCell
);
899 else if( nRowSpan
> (nIndex
- nRow
) )
901 // current cells spans inside the removed rows, so adjust
902 const sal_Int32 nRemove
= ::std::min( nCount
, nRow
+ nRowSpan
- nIndex
);
905 xCell
->merge( xCell
->getColumnSpan(), nRowSpan
- nRemove
);
912 TableModelRef
xThis( this );
914 RowVector
aRemovedRows( nCount
);
915 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
916 aRemovedRows
[nOffset
] = maRows
[nIndex
+nOffset
];
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
) );
922 // now remove the rows
923 remove_range
<RowVector
,RowVector::iterator
>( maRows
, nIndex
, nCount
);
930 catch( uno::Exception
& )
932 TOOLS_WARN_EXCEPTION("svx", "");
940 TableRowRef
const & TableModel::getRow( sal_Int32 nRow
) const
942 if( (nRow
>= 0) && (nRow
< getRowCountImpl()) )
945 throw lang::IndexOutOfBoundsException();
949 TableColumnRef
const & TableModel::getColumn( sal_Int32 nColumn
) const
951 if( (nColumn
>= 0) && (nColumn
< getColumnCountImpl()) )
952 return maColumns
[nColumn
];
954 throw lang::IndexOutOfBoundsException();
958 /** deletes rows and columns that are completely merged. Must be called between BegUndo/EndUndo! */
959 void TableModel::optimize()
961 TableModelNotifyGuard
aGuard( this );
963 bool bWasModified
= false;
965 if( !maRows
.empty() && !maColumns
.empty() )
967 sal_Int32 nCol
= getColumnCountImpl() - 1;
968 sal_Int32 nRows
= getRowCountImpl();
972 for( sal_Int32 nRow
= 0; (nRow
< nRows
) && bEmpty
; nRow
++ )
974 uno::Reference
<css::table::XMergeableCell
> xCell( getCellByPosition( nCol
, nRow
), uno::UNO_QUERY
);
975 if( xCell
.is() && !xCell
->isMerged() )
983 static const 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
) );
992 catch( uno::Exception
& )
994 TOOLS_WARN_EXCEPTION("svx", "");
997 removeColumns( nCol
, 1 );
1004 sal_Int32 nRow
= getRowCountImpl() - 1;
1005 sal_Int32 nCols
= getColumnCountImpl();
1009 for( nCol
= 0; (nCol
< nCols
) && bEmpty
; nCol
++ )
1011 uno::Reference
<css::table::XMergeableCell
> xCell( getCellByPosition( nCol
, nRow
), uno::UNO_QUERY
);
1012 if( xCell
.is() && !xCell
->isMerged() )
1020 static const 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
) );
1029 catch( uno::Exception
& )
1031 TOOLS_WARN_EXCEPTION("svx", "");
1034 removeRows( nRow
, 1 );
1035 bWasModified
= true;
1046 void TableModel::merge( sal_Int32 nCol
, sal_Int32 nRow
, sal_Int32 nColSpan
, sal_Int32 nRowSpan
)
1048 if(nullptr == mpTableObj
)
1051 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
1052 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
1053 const sal_Int32 nLastRow
= nRow
+ nRowSpan
;
1054 const sal_Int32 nLastCol
= nCol
+ nColSpan
;
1056 if( (nLastRow
> getRowCount()) || (nLastCol
> getColumnCount() ) )
1058 OSL_FAIL("TableModel::merge(), merge beyond the table!");
1062 CellRef
xOriginCell( dynamic_cast< Cell
* >( getCellByPosition( nCol
, nRow
).get() ) );
1063 if(!xOriginCell
.is())
1067 xOriginCell
->AddUndo();
1068 xOriginCell
->merge( nColSpan
, nRowSpan
);
1070 sal_Int32 nTempCol
= nCol
+ 1;
1072 // merge remaining cells
1073 for( ; nRow
< nLastRow
; nRow
++ )
1075 for( ; nTempCol
< nLastCol
; nTempCol
++ )
1077 CellRef
xCell( dynamic_cast< Cell
* >( getCellByPosition( nTempCol
, nRow
).get() ) );
1078 if( xCell
.is() && !xCell
->isMerged() )
1083 xOriginCell
->mergeContent( xCell
);
1090 void TableModel::updateRows()
1093 for( auto& rpRow
: maRows
)
1095 rpRow
->mnRow
= nRow
++;
1099 void TableModel::updateColumns()
1101 sal_Int32 nColumn
= 0;
1102 for( auto& rpCol
: maColumns
)
1104 rpCol
->mnColumn
= nColumn
++;
1108 void TableModel::dumpAsXml(xmlTextWriterPtr pWriter
) const
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
)
1114 maRows
[nRow
]->maCells
[nCol
]->dumpAsXml(pWriter
, nRow
, nCol
);
1116 (void)xmlTextWriterEndElement(pWriter
);
1121 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */