Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / svx / source / table / tablemodel.cxx
blob5dcbcf7ce61716feecd29ea9a5c6a7c9c4db0f04
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
25 #include <algorithm>
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>
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 <sdr/properties/cellproperties.hxx>
43 #include <svx/svdotable.hxx>
44 #include <svx/svdmodel.hxx>
45 #include <svx/strings.hrc>
46 #include <svx/dialmgr.hxx>
48 using namespace css;
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 )
61 // remove at end
62 rVector.resize( nIndex );
64 else
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 )
75 if( nCount )
77 if( nIndex >= static_cast< sal_Int32 >( rVector.size() ) )
79 // append at end
80 nIndex = static_cast< sal_Int32 >( rVector.size() ); // cap to end
81 rVector.resize( nIndex + nCount );
83 else
85 // insert
86 Iter aIter( rVector.begin() );
87 std::advance( aIter, nIndex );
89 Entry aEmpty;
90 rVector.insert( aIter, nCount, aEmpty );
93 return nIndex;
97 TableModel::TableModel( SdrTableObj* pTableObj )
98 : TableModelBase( m_aMutex )
99 , mpTableObj( pTableObj )
100 , mbModified( false )
101 , mbNotifyPending( false )
102 , mnNotifyLock( 0 )
106 TableModel::TableModel( SdrTableObj* pTableObj, const TableModelRef& xSourceTable )
107 : TableModelBase( m_aMutex )
108 , mpTableObj( pTableObj )
109 , mbModified( false )
110 , mbNotifyPending( false )
111 , mnNotifyLock( 0 )
113 if( !xSourceTable.is() )
114 return;
116 const sal_Int32 nColCount = xSourceTable->getColumnCountImpl();
117 const sal_Int32 nRowCount = xSourceTable->getRowCountImpl();
119 init( nColCount, nRowCount );
121 sal_Int32 nRows = nRowCount;
122 while( nRows-- )
123 (*maRows[nRows]) = *xSourceTable->maRows[nRows];
125 sal_Int32 nColumns = nColCount;
126 while( nColumns-- )
127 (*maColumns[nColumns]) = *xSourceTable->maColumns[nColumns];
129 // copy cells
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 )
149 if( nRows < 20 )
150 maRows.reserve( 20 );
152 if( nColumns < 20 )
153 maColumns.reserve( 20 );
155 if( nRows && nColumns )
157 maColumns.resize( nColumns );
158 maRows.resize( nRows );
160 while( nRows-- )
161 maRows[nRows].set( new TableRow( this, nRows, nColumns ) );
163 while( nColumns-- )
164 maColumns[nColumns].set( new TableColumn( this, nColumns ) );
169 // ICellRange
172 sal_Int32 TableModel::getLeft()
174 return 0;
178 sal_Int32 TableModel::getTop()
180 return 0;
184 sal_Int32 TableModel::getRight()
186 return getColumnCount();
190 sal_Int32 TableModel::getBottom()
192 return getRowCount();
196 uno::Reference<css::table::XTable> TableModel::getTable()
198 return this;
202 void TableModel::UndoInsertRows( sal_Int32 nIndex, sal_Int32 nCount )
204 TableModelNotifyGuard aGuard( this );
206 // remove the rows
207 remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
208 updateRows();
209 setModified(true);
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];
224 updateRows();
225 setModified(true);
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();
236 while( nRows-- )
237 maRows[nRows]->removeColumns( nIndex, nCount );
239 updateColumns();
240 setModified(true);
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 );
267 updateColumns();
268 setModified(true);
272 // XTable
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());
312 return aRet;
315 // XComponent
318 void TableModel::dispose()
320 ::SolarMutexGuard aGuard;
321 TableModelBase::dispose();
325 // XModifiable
328 sal_Bool SAL_CALL TableModel::isModified( )
330 ::SolarMutexGuard aGuard;
331 return mbModified;
335 void SAL_CALL TableModel::setModified( sal_Bool bModified )
338 ::SolarMutexGuard aGuard;
339 mbModified = bModified;
341 if( 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 );
361 // XColumnRowRange
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 ) );
380 return mxTableRows;
384 // XCellRange
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 ) );
392 if( xCell.is() )
393 return xCell;
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 >();
419 // XPropertySet
422 uno::Reference<beans::XPropertySetInfo> SAL_CALL TableModel::getPropertySetInfo( )
424 uno::Reference<beans::XPropertySetInfo> xInfo;
425 return xInfo;
429 void SAL_CALL TableModel::setPropertyValue( const OUString& /*aPropertyName*/, const uno::Any& /*aValue*/ )
434 uno::Any SAL_CALL TableModel::getPropertyValue( const OUString& /*PropertyName*/ )
436 return uno::Any();
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*/ )
460 // XFastPropertySet
463 void SAL_CALL TableModel::setFastPropertyValue( ::sal_Int32 /*nHandle*/, const uno::Any& /*aValue*/ )
468 uno::Any SAL_CALL TableModel::getFastPropertyValue( ::sal_Int32 /*nHandle*/ )
470 uno::Any aAny;
471 return aAny;
475 // internals
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 )
495 rpRow->dispose();
496 RowVector().swap(maRows);
499 if( !maColumns.empty() )
501 for( auto& rpCol : maColumns )
502 rpCol->dispose();
503 ColumnVector().swap(maColumns);
506 if( mxTableColumns.is() )
508 mxTableColumns->dispose();
509 mxTableColumns.clear();
512 if( mxTableRows.is() )
514 mxTableRows->dispose();
515 mxTableRows.clear();
518 mpTableObj = nullptr;
522 // XBroadcaster
525 void TableModel::lockBroadcasts()
527 ::SolarMutexGuard aGuard;
528 ++mnNotifyLock;
532 void TableModel::unlockBroadcasts()
534 ::SolarMutexGuard aGuard;
535 --mnNotifyLock;
536 if( mnNotifyLock <= 0 )
538 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);
560 else
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];
573 else
575 CellRef xRet;
576 return xRet;
581 CellRef TableModel::createCell()
583 CellRef xCell;
584 if( mpTableObj )
585 mpTableObj->createCell( xCell );
586 return xCell;
590 void TableModel::insertColumns( sal_Int32 nIndex, sal_Int32 nCount )
592 if( !(nCount && mpTableObj) )
593 return;
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();
602 while( nRows-- )
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());
615 if( bUndo )
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();
648 nColSpan += nCount;
649 merge( nCol, nRow, nColSpan, nRowSpan );
654 if( bUndo )
655 rModel.EndUndo();
657 rModel.SetChanged();
659 catch( uno::Exception& )
661 TOOLS_WARN_EXCEPTION("svx", "");
663 updateColumns();
664 setModified(true);
668 void TableModel::removeColumns( sal_Int32 nIndex, sal_Int32 nCount )
670 sal_Int32 nColCount = getColumnCountImpl();
672 if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nColCount)) )
673 return;
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());
687 if( bUndo )
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;
705 if( nColSpan <= 1 )
706 continue;
708 if( nCol >= nIndex )
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() )
719 if( bUndo )
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 );
730 if( bUndo )
731 xCell->AddUndo();
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.
739 if( bUndo )
741 TableModelRef xThis( this );
742 ColumnVector aRemovedCols( nCount );
743 sal_Int32 nOffset;
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 );
762 while( nRows-- )
763 maRows[nRows]->removeColumns( nIndex, nCount );
765 if( bUndo )
766 rModel.EndUndo();
768 rModel.SetChanged();
770 catch( uno::Exception& )
772 TOOLS_WARN_EXCEPTION("svx", "");
775 updateColumns();
776 setModified(true);
780 void TableModel::insertRows( sal_Int32 nIndex, sal_Int32 nCount )
782 if( !(nCount && mpTableObj) )
783 return;
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;
803 if( bUndo )
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();
822 nRowSpan += nCount;
823 merge( nCol, nRow, nColSpan, nRowSpan );
828 catch( uno::Exception& )
830 TOOLS_WARN_EXCEPTION("svx", "");
832 if( bUndo )
833 rModel.EndUndo();
835 rModel.SetChanged();
837 updateRows();
838 setModified(true);
842 void TableModel::removeRows( sal_Int32 nIndex, sal_Int32 nCount )
844 sal_Int32 nRowCount = getRowCountImpl();
846 if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nRowCount)) )
847 return;
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;
860 if( bUndo )
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;
878 if( nRowSpan <= 1 )
879 continue;
881 if( nRow >= nIndex )
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() )
892 if( bUndo )
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 );
903 if( bUndo )
904 xCell->AddUndo();
905 xCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
910 if( bUndo )
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 );
925 if( bUndo )
926 rModel.EndUndo();
928 rModel.SetChanged();
930 catch( uno::Exception& )
932 TOOLS_WARN_EXCEPTION("svx", "");
935 updateRows();
936 setModified(true);
940 TableRowRef const & TableModel::getRow( sal_Int32 nRow ) const
942 if( (nRow >= 0) && (nRow < getRowCountImpl()) )
943 return maRows[nRow];
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();
969 while( nCol > 0 )
971 bool bEmpty = true;
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() )
976 bEmpty = false;
979 if( bEmpty )
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 );
998 bWasModified = true;
1001 nCol--;
1004 sal_Int32 nRow = getRowCountImpl() - 1;
1005 sal_Int32 nCols = getColumnCountImpl();
1006 while( nRow > 0 )
1008 bool bEmpty = true;
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() )
1013 bEmpty = false;
1016 if( bEmpty )
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;
1038 nRow--;
1041 if( bWasModified )
1042 setModified(true);
1046 void TableModel::merge( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan )
1048 if(nullptr == mpTableObj)
1049 return;
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!");
1061 // merge first cell
1062 CellRef xOriginCell( dynamic_cast< Cell* >( getCellByPosition( nCol, nRow ).get() ) );
1063 if(!xOriginCell.is())
1064 return;
1066 if( bUndo )
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() )
1080 if( bUndo )
1081 xCell->AddUndo();
1082 xCell->setMerged();
1083 xOriginCell->mergeContent( xCell );
1086 nTempCol = nCol;
1090 void TableModel::updateRows()
1092 sal_Int32 nRow = 0;
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: */