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 <svx/svdotable.hxx>
43 #include <svx/svdmodel.hxx>
44 #include <svx/strings.hrc>
45 #include <svx/dialmgr.hxx>
49 namespace sdr::table
{
52 // removes the given range from a vector
53 template< class Vec
, class Iter
> static void remove_range( Vec
& rVector
, sal_Int32 nIndex
, sal_Int32 nCount
)
55 const sal_Int32 nSize
= static_cast<sal_Int32
>(rVector
.size());
56 if( nCount
&& (nIndex
>= 0) && (nIndex
< nSize
) )
58 if( (nIndex
+ nCount
) >= nSize
)
61 rVector
.resize( nIndex
);
65 rVector
.erase(rVector
.begin() + nIndex
, rVector
.begin() + nIndex
+ nCount
);
71 /** inserts a range into a vector */
72 template< class Vec
, class Iter
, class Entry
> static sal_Int32
insert_range( Vec
& rVector
, sal_Int32 nIndex
, sal_Int32 nCount
)
76 if( nIndex
>= static_cast< sal_Int32
>( rVector
.size() ) )
79 nIndex
= static_cast< sal_Int32
>( rVector
.size() ); // cap to end
80 rVector
.resize( nIndex
+ nCount
);
85 Iter
aIter( rVector
.begin() );
86 std::advance( aIter
, nIndex
);
89 rVector
.insert( aIter
, nCount
, aEmpty
);
96 TableModel::TableModel( SdrTableObj
* pTableObj
)
97 : mpTableObj( pTableObj
)
99 , mbNotifyPending( false )
104 TableModel::TableModel( SdrTableObj
* pTableObj
, const TableModelRef
& xSourceTable
)
105 : mpTableObj( pTableObj
)
106 , mbModified( false )
107 , mbNotifyPending( false )
110 if( !xSourceTable
.is() )
113 const sal_Int32 nColCount
= xSourceTable
->getColumnCountImpl();
114 const sal_Int32 nRowCount
= xSourceTable
->getRowCountImpl();
116 init( nColCount
, nRowCount
);
118 sal_Int32 nRows
= nRowCount
;
120 (*maRows
[nRows
]) = *xSourceTable
->maRows
[nRows
];
122 sal_Int32 nColumns
= nColCount
;
124 (*maColumns
[nColumns
]) = *xSourceTable
->maColumns
[nColumns
];
127 for( sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
129 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; ++nRow
)
131 CellRef
xTargetCell( getCell( nCol
, nRow
) );
132 if( xTargetCell
.is() )
133 xTargetCell
->cloneFrom( xSourceTable
->getCell( nCol
, nRow
) );
139 TableModel::~TableModel()
144 void TableModel::init( sal_Int32 nColumns
, sal_Int32 nRows
)
147 maRows
.reserve( 20 );
150 maColumns
.reserve( 20 );
152 if( nRows
&& nColumns
)
154 maColumns
.resize( nColumns
);
155 maRows
.resize( nRows
);
158 maRows
[nRows
].set( new TableRow( this, nRows
, nColumns
) );
161 maColumns
[nColumns
].set( new TableColumn( this, nColumns
) );
169 sal_Int32
TableModel::getLeft()
175 sal_Int32
TableModel::getTop()
181 sal_Int32
TableModel::getRight()
183 return getColumnCount();
187 sal_Int32
TableModel::getBottom()
189 return getRowCount();
193 uno::Reference
<css::table::XTable
> TableModel::getTable()
199 void TableModel::UndoInsertRows( sal_Int32 nIndex
, sal_Int32 nCount
)
201 TableModelNotifyGuard
aGuard( this );
204 remove_range
<RowVector
,RowVector::iterator
>( maRows
, nIndex
, nCount
);
210 void TableModel::UndoRemoveRows( sal_Int32 nIndex
, RowVector
& aRows
)
212 TableModelNotifyGuard
aGuard( this );
214 const sal_Int32 nCount
= sal::static_int_cast
< sal_Int32
>( aRows
.size() );
216 nIndex
= insert_range
<RowVector
,RowVector::iterator
,TableRowRef
>( maRows
, nIndex
, nCount
);
218 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
219 maRows
[nIndex
+nOffset
] = aRows
[nOffset
];
226 void TableModel::UndoInsertColumns( sal_Int32 nIndex
, sal_Int32 nCount
)
228 TableModelNotifyGuard
aGuard( this );
230 // now remove the columns
231 remove_range
<ColumnVector
,ColumnVector::iterator
>( maColumns
, nIndex
, nCount
);
232 sal_Int32 nRows
= getRowCountImpl();
234 maRows
[nRows
]->removeColumns( nIndex
, nCount
);
241 void TableModel::UndoRemoveColumns( sal_Int32 nIndex
, ColumnVector
& aCols
, CellVector
& aCells
)
243 TableModelNotifyGuard
aGuard( this );
245 const sal_Int32 nCount
= sal::static_int_cast
< sal_Int32
>( aCols
.size() );
247 // assert if there are not enough cells saved
248 DBG_ASSERT( (aCols
.size() * maRows
.size()) == aCells
.size(), "sdr::table::TableModel::UndoRemoveColumns(), invalid undo data!" );
250 nIndex
= insert_range
<ColumnVector
,ColumnVector::iterator
,TableColumnRef
>( maColumns
, nIndex
, nCount
);
251 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
252 maColumns
[nIndex
+nOffset
] = aCols
[nOffset
];
254 CellVector::iterator
aIter( aCells
.begin() );
256 sal_Int32 nRows
= getRowCountImpl();
257 for( sal_Int32 nRow
= 0; nRow
< nRows
; ++nRow
)
259 CellVector::iterator aIter2
= aIter
+ nRow
* nCount
;
260 OSL_ENSURE(aIter2
< aCells
.end(), "invalid iterator!");
261 maRows
[nRow
]->insertColumns( nIndex
, nCount
, &aIter2
);
272 uno::Reference
<css::table::XCellCursor
> SAL_CALL
TableModel::createCursor()
274 ::SolarMutexGuard aGuard
;
275 return createCursorByRange( uno::Reference
< XCellRange
>( this ) );
279 uno::Reference
<css::table::XCellCursor
> SAL_CALL
TableModel::createCursorByRange( const uno::Reference
< XCellRange
>& rRange
)
281 ::SolarMutexGuard aGuard
;
283 ICellRange
* pRange
= dynamic_cast< ICellRange
* >( rRange
.get() );
284 if( (pRange
== nullptr) || (pRange
->getTable().get() != this) )
285 throw lang::IllegalArgumentException();
287 TableModelRef
xModel( this );
288 return new CellCursor( xModel
, pRange
->getLeft(), pRange
->getTop(), pRange
->getRight(), pRange
->getBottom() );
292 sal_Int32 SAL_CALL
TableModel::getRowCount()
294 ::SolarMutexGuard aGuard
;
295 return getRowCountImpl();
298 sal_Int32 SAL_CALL
TableModel::getColumnCount()
300 ::SolarMutexGuard aGuard
;
301 return getColumnCountImpl();
304 std::vector
<sal_Int32
> TableModel::getColumnWidths()
306 std::vector
<sal_Int32
> aRet
;
307 for (const TableColumnRef
& xColumn
: maColumns
)
308 aRet
.push_back(xColumn
->getWidth());
316 sal_Bool SAL_CALL
TableModel::isModified( )
318 ::SolarMutexGuard aGuard
;
323 void SAL_CALL
TableModel::setModified( sal_Bool bModified
)
326 ::SolarMutexGuard aGuard
;
327 mbModified
= bModified
;
330 notifyModification();
334 // XModifyBroadcaster
337 void SAL_CALL
TableModel::addModifyListener( const uno::Reference
<util::XModifyListener
>& xListener
)
339 std::unique_lock
aGuard(m_aMutex
);
340 maModifyListeners
.addInterface( aGuard
, xListener
);
344 void SAL_CALL
TableModel::removeModifyListener( const uno::Reference
<util::XModifyListener
>& xListener
)
346 std::unique_lock
aGuard(m_aMutex
);
347 maModifyListeners
.removeInterface( aGuard
, xListener
);
354 uno::Reference
<css::table::XTableColumns
> SAL_CALL
TableModel::getColumns()
356 ::SolarMutexGuard aGuard
;
358 if( !mxTableColumns
.is() )
359 mxTableColumns
.set( new TableColumns( this ) );
360 return mxTableColumns
;
364 uno::Reference
<css::table::XTableRows
> SAL_CALL
TableModel::getRows()
366 ::SolarMutexGuard aGuard
;
368 if( !mxTableRows
.is() )
369 mxTableRows
.set( new TableRows( this ) );
377 uno::Reference
<css::table::XCell
> SAL_CALL
TableModel::getCellByPosition( sal_Int32 nColumn
, sal_Int32 nRow
)
379 ::SolarMutexGuard aGuard
;
381 sal_Int32 nRowCount
= getRowCountImpl();
382 if( nRow
< 0 || nRow
>= nRowCount
)
383 throw lang::IndexOutOfBoundsException(OUString::Concat("row ") + OUString::number(nRow
)
384 + " out of range 0.." + OUString::number(nRowCount
));
386 sal_Int32 nColCount
= getColumnCountImpl();
387 if( nColumn
< 0 || nColumn
>= nColCount
)
388 throw lang::IndexOutOfBoundsException(OUString::Concat("col ") + OUString::number(nColumn
)
389 + " out of range 0.." + OUString::number(nColCount
));
391 return maRows
[nRow
]->maCells
[nColumn
];
395 uno::Reference
<css::table::XCellRange
> SAL_CALL
TableModel::getCellRangeByPosition( sal_Int32 nLeft
, sal_Int32 nTop
, sal_Int32 nRight
, sal_Int32 nBottom
)
397 ::SolarMutexGuard aGuard
;
399 if( (nLeft
>= 0) && (nTop
>= 0) && (nRight
>= nLeft
) && (nBottom
>= nTop
) && (nRight
< getColumnCountImpl()) && (nBottom
< getRowCountImpl() ) )
401 TableModelRef
xModel( this );
402 return new CellRange( xModel
, nLeft
, nTop
, nRight
, nBottom
);
405 throw lang::IndexOutOfBoundsException();
409 uno::Reference
<css::table::XCellRange
> SAL_CALL
TableModel::getCellRangeByName( const OUString
& /*aRange*/ )
411 return uno::Reference
< XCellRange
>();
418 uno::Reference
<beans::XPropertySetInfo
> SAL_CALL
TableModel::getPropertySetInfo( )
420 uno::Reference
<beans::XPropertySetInfo
> xInfo
;
425 void SAL_CALL
TableModel::setPropertyValue( const OUString
& /*aPropertyName*/, const uno::Any
& /*aValue*/ )
430 uno::Any SAL_CALL
TableModel::getPropertyValue( const OUString
& /*PropertyName*/ )
436 void SAL_CALL
TableModel::addPropertyChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
<beans::XPropertyChangeListener
>& /*xListener*/ )
441 void SAL_CALL
TableModel::removePropertyChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
<beans::XPropertyChangeListener
>& /*xListener*/ )
446 void SAL_CALL
TableModel::addVetoableChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
<beans::XVetoableChangeListener
>& /*xListener*/ )
451 void SAL_CALL
TableModel::removeVetoableChangeListener( const OUString
& /*aPropertyName*/, const uno::Reference
<beans::XVetoableChangeListener
>& /*xListener*/ )
459 void SAL_CALL
TableModel::setFastPropertyValue( ::sal_Int32
/*nHandle*/, const uno::Any
& /*aValue*/ )
464 uno::Any SAL_CALL
TableModel::getFastPropertyValue( ::sal_Int32
/*nHandle*/ )
474 sal_Int32
TableModel::getRowCountImpl() const
476 return static_cast< sal_Int32
>( maRows
.size() );
480 sal_Int32
TableModel::getColumnCountImpl() const
482 return static_cast< sal_Int32
>( maColumns
.size() );
486 void TableModel::disposing(std::unique_lock
<std::mutex
>& rGuard
)
488 rGuard
.unlock(); // do not hold this while taking solar mutex
489 ::SolarMutexGuard aGuard
;
491 if( !maRows
.empty() )
493 for( auto& rpRow
: maRows
)
495 RowVector().swap(maRows
);
498 if( !maColumns
.empty() )
500 for( auto& rpCol
: maColumns
)
502 ColumnVector().swap(maColumns
);
505 if( mxTableColumns
.is() )
507 mxTableColumns
->dispose();
508 mxTableColumns
.clear();
511 if( mxTableRows
.is() )
513 mxTableRows
->dispose();
517 mpTableObj
= nullptr;
526 void TableModel::lockBroadcasts()
528 ::SolarMutexGuard aGuard
;
533 void TableModel::unlockBroadcasts()
535 ::SolarMutexGuard aGuard
;
537 if( mnNotifyLock
<= 0 )
540 if( mbNotifyPending
)
541 notifyModification();
546 void TableModel::notifyModification()
548 if( (mnNotifyLock
== 0) && mpTableObj
)
550 mbNotifyPending
= false;
552 lang::EventObject aSource
;
553 aSource
.Source
= getXWeak();
554 std::unique_lock
aGuard(m_aMutex
);
555 maModifyListeners
.notifyEach(aGuard
, &util::XModifyListener::modified
, aSource
);
559 mbNotifyPending
= true;
564 CellRef
TableModel::getCell( sal_Int32 nCol
, sal_Int32 nRow
) const
566 if( ((nRow
>= 0) && (nRow
< getRowCountImpl())) && (nCol
>= 0) && (nCol
< getColumnCountImpl()) )
568 return maRows
[nRow
]->maCells
[nCol
];
578 CellRef
TableModel::createCell()
582 mpTableObj
->createCell( xCell
);
587 void TableModel::insertColumns( sal_Int32 nIndex
, sal_Int32 nCount
)
589 if( !(nCount
&& mpTableObj
) )
594 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
595 TableModelNotifyGuard
aGuard( this );
596 nIndex
= insert_range
<ColumnVector
,ColumnVector::iterator
,TableColumnRef
>( maColumns
, nIndex
, nCount
);
598 sal_Int32 nRows
= getRowCountImpl();
600 maRows
[nRows
]->insertColumns( nIndex
, nCount
, nullptr );
602 ColumnVector
aNewColumns(nCount
);
603 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
605 TableColumnRef
xNewCol( new TableColumn( this, nIndex
+nOffset
) );
606 maColumns
[nIndex
+nOffset
] = xNewCol
;
607 aNewColumns
[nOffset
] = std::move(xNewCol
);
610 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
614 rModel
.BegUndo( SvxResId(STR_TABLE_INSCOL
) );
615 rModel
.AddUndo( rModel
.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj
) );
617 TableModelRef
xThis( this );
619 nRows
= getRowCountImpl();
620 CellVector
aNewCells( nCount
* nRows
);
621 CellVector::iterator
aCellIter( aNewCells
.begin() );
623 nRows
= getRowCountImpl();
624 for( sal_Int32 nRow
= 0; nRow
< nRows
; ++nRow
)
626 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
627 (*aCellIter
++) = getCell( nIndex
+ nOffset
, nRow
);
630 rModel
.AddUndo( std::make_unique
<InsertColUndo
>( xThis
, nIndex
, aNewColumns
, aNewCells
) );
633 const sal_Int32 nRowCount
= getRowCountImpl();
634 // check if cells merge over new columns
635 for( sal_Int32 nCol
= 0; nCol
< nIndex
; ++nCol
)
637 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; ++nRow
)
639 CellRef
xCell( getCell( nCol
, nRow
) );
640 sal_Int32 nColSpan
= (xCell
.is() && !xCell
->isMerged()) ? xCell
->getColumnSpan() : 1;
641 if( (nColSpan
!= 1) && ((nColSpan
+ nCol
) > nIndex
) )
643 // cell merges over newly created columns, so add the new columns to the merged cell
644 const sal_Int32 nRowSpan
= xCell
->getRowSpan();
646 merge( nCol
, nRow
, nColSpan
, nRowSpan
);
656 catch( uno::Exception
& )
658 TOOLS_WARN_EXCEPTION("svx", "");
665 void TableModel::removeColumns( sal_Int32 nIndex
, sal_Int32 nCount
)
667 sal_Int32 nColCount
= getColumnCountImpl();
669 if( !(mpTableObj
&& nCount
&& (nIndex
>= 0) && (nIndex
< nColCount
)) )
674 TableModelNotifyGuard
aGuard( this );
676 // clip removed columns to columns actually available
677 if( (nIndex
+ nCount
) > nColCount
)
678 nCount
= nColCount
- nIndex
;
680 sal_Int32 nRows
= getRowCountImpl();
681 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
682 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
686 rModel
.BegUndo( SvxResId(STR_UNDO_COL_DELETE
) );
687 rModel
.AddUndo( rModel
.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj
) );
690 // only rows before and inside the removed rows are considered
691 nColCount
= nIndex
+ nCount
+ 1;
693 const sal_Int32 nRowCount
= getRowCountImpl();
695 // first check merged cells before and inside the removed rows
696 for( sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
698 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; ++nRow
)
700 CellRef
xCell( getCell( nCol
, nRow
) );
701 sal_Int32 nColSpan
= (xCell
.is() && !xCell
->isMerged()) ? xCell
->getColumnSpan() : 1;
707 // current cell is inside the removed columns
708 if( (nCol
+ nColSpan
) > ( nIndex
+ nCount
) )
710 // current cells merges with columns after the removed columns
711 const sal_Int32 nRemove
= nCount
- nCol
+ nIndex
;
713 CellRef
xTargetCell( getCell( nIndex
+ nCount
, nRow
) );
714 if( xTargetCell
.is() )
717 xTargetCell
->AddUndo();
718 xTargetCell
->merge( nColSpan
- nRemove
, xCell
->getRowSpan() );
719 xTargetCell
->replaceContentAndFormatting( xCell
);
723 else if( nColSpan
> (nIndex
- nCol
) )
725 // current cells spans inside the removed columns, so adjust
726 const sal_Int32 nRemove
= ::std::min( nCount
, nCol
+ nColSpan
- nIndex
);
729 xCell
->merge( nColSpan
- nRemove
, xCell
->getRowSpan() );
734 // We must not add RemoveColUndo before we make cell spans correct, otherwise we
735 // get invalid cell span after undo.
738 TableModelRef
xThis( this );
739 ColumnVector
aRemovedCols( nCount
);
741 for( nOffset
= 0; nOffset
< nCount
; ++nOffset
)
743 aRemovedCols
[nOffset
] = maColumns
[nIndex
+nOffset
];
746 CellVector
aRemovedCells( nCount
* nRows
);
747 CellVector::iterator
aCellIter( aRemovedCells
.begin() );
748 for( sal_Int32 nRow
= 0; nRow
< nRows
; ++nRow
)
750 for( nOffset
= 0; nOffset
< nCount
; ++nOffset
)
751 (*aCellIter
++) = getCell( nIndex
+ nOffset
, nRow
);
754 rModel
.AddUndo( std::make_unique
<RemoveColUndo
>( xThis
, nIndex
, aRemovedCols
, aRemovedCells
) );
757 // now remove the columns
758 remove_range
<ColumnVector
,ColumnVector::iterator
>( maColumns
, nIndex
, nCount
);
760 maRows
[nRows
]->removeColumns( nIndex
, nCount
);
767 catch( uno::Exception
& )
769 TOOLS_WARN_EXCEPTION("svx", "");
777 void TableModel::insertRows( sal_Int32 nIndex
, sal_Int32 nCount
)
779 if( !(nCount
&& mpTableObj
) )
782 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
783 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
787 TableModelNotifyGuard
aGuard( this );
789 nIndex
= insert_range
<RowVector
,RowVector::iterator
,TableRowRef
>( maRows
, nIndex
, nCount
);
791 RowVector
aNewRows(nCount
);
792 const sal_Int32 nColCount
= getColumnCountImpl();
793 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
795 TableRowRef
xNewRow( new TableRow( this, nIndex
+nOffset
, nColCount
) );
796 maRows
[nIndex
+nOffset
] = xNewRow
;
797 aNewRows
[nOffset
] = std::move(xNewRow
);
802 rModel
.BegUndo( SvxResId(STR_TABLE_INSROW
) );
803 rModel
.AddUndo( rModel
.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj
) );
804 TableModelRef
xThis( this );
805 rModel
.AddUndo( std::make_unique
<InsertRowUndo
>( xThis
, nIndex
, aNewRows
) );
808 // check if cells merge over new columns
809 for( sal_Int32 nRow
= 0; nRow
< nIndex
; ++nRow
)
811 for( sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
813 CellRef
xCell( getCell( nCol
, nRow
) );
814 sal_Int32 nRowSpan
= (xCell
.is() && !xCell
->isMerged()) ? xCell
->getRowSpan() : 1;
815 if( (nRowSpan
> 1) && ((nRowSpan
+ nRow
) > nIndex
) )
817 // cell merges over newly created columns, so add the new columns to the merged cell
818 const sal_Int32 nColSpan
= xCell
->getColumnSpan();
820 merge( nCol
, nRow
, nColSpan
, nRowSpan
);
825 catch( uno::Exception
& )
827 TOOLS_WARN_EXCEPTION("svx", "");
839 void TableModel::removeRows( sal_Int32 nIndex
, sal_Int32 nCount
)
841 sal_Int32 nRowCount
= getRowCountImpl();
843 if( !(mpTableObj
&& nCount
&& (nIndex
>= 0) && (nIndex
< nRowCount
)) )
846 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
847 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
851 TableModelNotifyGuard
aGuard( this );
853 // clip removed rows to rows actually available
854 if( (nIndex
+ nCount
) > nRowCount
)
855 nCount
= nRowCount
- nIndex
;
859 rModel
.BegUndo( SvxResId(STR_UNDO_ROW_DELETE
) );
860 rModel
.AddUndo( rModel
.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj
) );
863 // only rows before and inside the removed rows are considered
864 nRowCount
= nIndex
+ nCount
+ 1;
866 const sal_Int32 nColCount
= getColumnCountImpl();
868 // first check merged cells before and inside the removed rows
869 for( sal_Int32 nRow
= 0; nRow
< nRowCount
; ++nRow
)
871 for( sal_Int32 nCol
= 0; nCol
< nColCount
; ++nCol
)
873 CellRef
xCell( getCell( nCol
, nRow
) );
874 sal_Int32 nRowSpan
= (xCell
.is() && !xCell
->isMerged()) ? xCell
->getRowSpan() : 1;
880 // current cell is inside the removed rows
881 if( (nRow
+ nRowSpan
) > (nIndex
+ nCount
) )
883 // current cells merges with rows after the removed rows
884 const sal_Int32 nRemove
= nCount
- nRow
+ nIndex
;
886 CellRef
xTargetCell( getCell( nCol
, nIndex
+ nCount
) );
887 if( xTargetCell
.is() )
890 xTargetCell
->AddUndo();
891 xTargetCell
->merge( xCell
->getColumnSpan(), nRowSpan
- nRemove
);
892 xTargetCell
->replaceContentAndFormatting( xCell
);
896 else if( nRowSpan
> (nIndex
- nRow
) )
898 // current cells spans inside the removed rows, so adjust
899 const sal_Int32 nRemove
= ::std::min( nCount
, nRow
+ nRowSpan
- nIndex
);
902 xCell
->merge( xCell
->getColumnSpan(), nRowSpan
- nRemove
);
909 TableModelRef
xThis( this );
911 RowVector
aRemovedRows( nCount
);
912 for( sal_Int32 nOffset
= 0; nOffset
< nCount
; ++nOffset
)
913 aRemovedRows
[nOffset
] = maRows
[nIndex
+nOffset
];
915 // We must not RemoveRowUndo before we make cell spans correct, otherwise we
916 // get invalid cell span after undo.
917 rModel
.AddUndo( std::make_unique
<RemoveRowUndo
>( xThis
, nIndex
, aRemovedRows
) );
919 // now remove the rows
920 remove_range
<RowVector
,RowVector::iterator
>( maRows
, nIndex
, nCount
);
927 catch( uno::Exception
& )
929 TOOLS_WARN_EXCEPTION("svx", "");
937 TableRowRef
const & TableModel::getRow( sal_Int32 nRow
) const
939 if( (nRow
>= 0) && (nRow
< getRowCountImpl()) )
942 throw lang::IndexOutOfBoundsException();
946 TableColumnRef
const & TableModel::getColumn( sal_Int32 nColumn
) const
948 if( (nColumn
>= 0) && (nColumn
< getColumnCountImpl()) )
949 return maColumns
[nColumn
];
951 throw lang::IndexOutOfBoundsException();
955 /** deletes rows and columns that are completely merged. Must be called between BegUndo/EndUndo! */
956 void TableModel::optimize()
958 TableModelNotifyGuard
aGuard( this );
960 bool bWasModified
= false;
962 if( !maRows
.empty() && !maColumns
.empty() )
964 sal_Int32 nCol
= getColumnCountImpl() - 1;
965 sal_Int32 nRows
= getRowCountImpl();
969 for( sal_Int32 nRow
= 0; (nRow
< nRows
) && bEmpty
; nRow
++ )
971 uno::Reference
<css::table::XMergeableCell
> xCell( getCellByPosition( nCol
, nRow
), uno::UNO_QUERY
);
972 if( xCell
.is() && !xCell
->isMerged() )
980 static constexpr OUString
sWidth(u
"Width"_ustr
);
981 sal_Int32 nWidth1
= 0, nWidth2
= 0;
982 uno::Reference
<beans::XPropertySet
> xSet1( static_cast< XCellRange
* >( maColumns
[nCol
].get() ), uno::UNO_QUERY_THROW
);
983 uno::Reference
<beans::XPropertySet
> xSet2( static_cast< XCellRange
* >( maColumns
[nCol
-1].get() ), uno::UNO_QUERY_THROW
);
984 xSet1
->getPropertyValue( sWidth
) >>= nWidth1
;
985 xSet2
->getPropertyValue( sWidth
) >>= nWidth2
;
986 nWidth1
= o3tl::saturating_add(nWidth1
, nWidth2
);
987 xSet2
->setPropertyValue( sWidth
, uno::Any( nWidth1
) );
989 catch( uno::Exception
& )
991 TOOLS_WARN_EXCEPTION("svx", "");
994 removeColumns( nCol
, 1 );
1001 sal_Int32 nRow
= getRowCountImpl() - 1;
1002 sal_Int32 nCols
= getColumnCountImpl();
1006 for( nCol
= 0; (nCol
< nCols
) && bEmpty
; nCol
++ )
1008 uno::Reference
<css::table::XMergeableCell
> xCell( getCellByPosition( nCol
, nRow
), uno::UNO_QUERY
);
1009 if( xCell
.is() && !xCell
->isMerged() )
1017 static constexpr OUString
sHeight(u
"Height"_ustr
);
1018 sal_Int32 nHeight1
= 0, nHeight2
= 0;
1019 uno::Reference
<beans::XPropertySet
> xSet1( static_cast< XCellRange
* >( maRows
[nRow
].get() ), uno::UNO_QUERY_THROW
);
1020 uno::Reference
<beans::XPropertySet
> xSet2( static_cast< XCellRange
* >( maRows
[nRow
-1].get() ), uno::UNO_QUERY_THROW
);
1021 xSet1
->getPropertyValue( sHeight
) >>= nHeight1
;
1022 xSet2
->getPropertyValue( sHeight
) >>= nHeight2
;
1023 nHeight1
= o3tl::saturating_add(nHeight1
, nHeight2
);
1024 xSet2
->setPropertyValue( sHeight
, uno::Any( nHeight1
) );
1026 catch( uno::Exception
& )
1028 TOOLS_WARN_EXCEPTION("svx", "");
1031 removeRows( nRow
, 1 );
1032 bWasModified
= true;
1043 void TableModel::merge( sal_Int32 nCol
, sal_Int32 nRow
, sal_Int32 nColSpan
, sal_Int32 nRowSpan
)
1045 if(nullptr == mpTableObj
)
1048 SdrModel
& rModel(mpTableObj
->getSdrModelFromSdrObject());
1049 const bool bUndo(mpTableObj
->IsInserted() && rModel
.IsUndoEnabled());
1050 const sal_Int32 nLastRow
= nRow
+ nRowSpan
;
1051 const sal_Int32 nLastCol
= nCol
+ nColSpan
;
1053 if( (nLastRow
> getRowCount()) || (nLastCol
> getColumnCount() ) )
1055 OSL_FAIL("TableModel::merge(), merge beyond the table!");
1059 CellRef
xOriginCell( getCell( nCol
, nRow
) );
1060 if(!xOriginCell
.is())
1064 xOriginCell
->AddUndo();
1065 xOriginCell
->merge( nColSpan
, nRowSpan
);
1067 sal_Int32 nTempCol
= nCol
+ 1;
1069 // merge remaining cells
1070 for( ; nRow
< nLastRow
; nRow
++ )
1072 for( ; nTempCol
< nLastCol
; nTempCol
++ )
1074 CellRef
xCell( getCell( nTempCol
, nRow
) );
1075 if( xCell
.is() && !xCell
->isMerged() )
1080 xOriginCell
->mergeContent( xCell
);
1087 void TableModel::updateRows()
1090 for( auto& rpRow
: maRows
)
1092 rpRow
->mnRow
= nRow
++;
1096 void TableModel::updateColumns()
1098 sal_Int32 nColumn
= 0;
1099 for( auto& rpCol
: maColumns
)
1101 rpCol
->mnColumn
= nColumn
++;
1105 void TableModel::dumpAsXml(xmlTextWriterPtr pWriter
) const
1107 (void)xmlTextWriterStartElement(pWriter
, BAD_CAST("TableModel"));
1108 for (sal_Int32 nRow
= 0; nRow
< getRowCountImpl(); ++nRow
)
1109 for (sal_Int32 nCol
= 0; nCol
< getColumnCountImpl(); ++nCol
)
1111 maRows
[nRow
]->maCells
[nCol
]->dumpAsXml(pWriter
, nRow
, nCol
);
1113 (void)xmlTextWriterEndElement(pWriter
);
1118 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */