tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / svx / source / table / tablemodel.cxx
blob7fb11fd7a48200d323d0716a909157b5e82877a7
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 <svx/svdotable.hxx>
43 #include <svx/svdmodel.hxx>
44 #include <svx/strings.hrc>
45 #include <svx/dialmgr.hxx>
47 using namespace css;
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 )
60 // remove at end
61 rVector.resize( nIndex );
63 else
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 )
74 if( nCount )
76 if( nIndex >= static_cast< sal_Int32 >( rVector.size() ) )
78 // append at end
79 nIndex = static_cast< sal_Int32 >( rVector.size() ); // cap to end
80 rVector.resize( nIndex + nCount );
82 else
84 // insert
85 Iter aIter( rVector.begin() );
86 std::advance( aIter, nIndex );
88 Entry aEmpty;
89 rVector.insert( aIter, nCount, aEmpty );
92 return nIndex;
96 TableModel::TableModel( SdrTableObj* pTableObj )
97 : mpTableObj( pTableObj )
98 , mbModified( false )
99 , mbNotifyPending( false )
100 , mnNotifyLock( 0 )
104 TableModel::TableModel( SdrTableObj* pTableObj, const TableModelRef& xSourceTable )
105 : mpTableObj( pTableObj )
106 , mbModified( false )
107 , mbNotifyPending( false )
108 , mnNotifyLock( 0 )
110 if( !xSourceTable.is() )
111 return;
113 const sal_Int32 nColCount = xSourceTable->getColumnCountImpl();
114 const sal_Int32 nRowCount = xSourceTable->getRowCountImpl();
116 init( nColCount, nRowCount );
118 sal_Int32 nRows = nRowCount;
119 while( nRows-- )
120 (*maRows[nRows]) = *xSourceTable->maRows[nRows];
122 sal_Int32 nColumns = nColCount;
123 while( nColumns-- )
124 (*maColumns[nColumns]) = *xSourceTable->maColumns[nColumns];
126 // copy cells
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 )
146 if( nRows < 20 )
147 maRows.reserve( 20 );
149 if( nColumns < 20 )
150 maColumns.reserve( 20 );
152 if( nRows && nColumns )
154 maColumns.resize( nColumns );
155 maRows.resize( nRows );
157 while( nRows-- )
158 maRows[nRows].set( new TableRow( this, nRows, nColumns ) );
160 while( nColumns-- )
161 maColumns[nColumns].set( new TableColumn( this, nColumns ) );
166 // ICellRange
169 sal_Int32 TableModel::getLeft()
171 return 0;
175 sal_Int32 TableModel::getTop()
177 return 0;
181 sal_Int32 TableModel::getRight()
183 return getColumnCount();
187 sal_Int32 TableModel::getBottom()
189 return getRowCount();
193 uno::Reference<css::table::XTable> TableModel::getTable()
195 return this;
199 void TableModel::UndoInsertRows( sal_Int32 nIndex, sal_Int32 nCount )
201 TableModelNotifyGuard aGuard( this );
203 // remove the rows
204 remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
205 updateRows();
206 setModified(true);
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];
221 updateRows();
222 setModified(true);
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();
233 while( nRows-- )
234 maRows[nRows]->removeColumns( nIndex, nCount );
236 updateColumns();
237 setModified(true);
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 );
264 updateColumns();
265 setModified(true);
269 // XTable
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());
309 return aRet;
313 // XModifiable
316 sal_Bool SAL_CALL TableModel::isModified( )
318 ::SolarMutexGuard aGuard;
319 return mbModified;
323 void SAL_CALL TableModel::setModified( sal_Bool bModified )
326 ::SolarMutexGuard aGuard;
327 mbModified = bModified;
329 if( 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 );
351 // XColumnRowRange
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 ) );
370 return mxTableRows;
374 // XCellRange
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 >();
415 // XPropertySet
418 uno::Reference<beans::XPropertySetInfo> SAL_CALL TableModel::getPropertySetInfo( )
420 uno::Reference<beans::XPropertySetInfo> xInfo;
421 return xInfo;
425 void SAL_CALL TableModel::setPropertyValue( const OUString& /*aPropertyName*/, const uno::Any& /*aValue*/ )
430 uno::Any SAL_CALL TableModel::getPropertyValue( const OUString& /*PropertyName*/ )
432 return uno::Any();
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*/ )
456 // XFastPropertySet
459 void SAL_CALL TableModel::setFastPropertyValue( ::sal_Int32 /*nHandle*/, const uno::Any& /*aValue*/ )
464 uno::Any SAL_CALL TableModel::getFastPropertyValue( ::sal_Int32 /*nHandle*/ )
466 uno::Any aAny;
467 return aAny;
471 // internals
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 )
494 rpRow->dispose();
495 RowVector().swap(maRows);
498 if( !maColumns.empty() )
500 for( auto& rpCol : maColumns )
501 rpCol->dispose();
502 ColumnVector().swap(maColumns);
505 if( mxTableColumns.is() )
507 mxTableColumns->dispose();
508 mxTableColumns.clear();
511 if( mxTableRows.is() )
513 mxTableRows->dispose();
514 mxTableRows.clear();
517 mpTableObj = nullptr;
519 rGuard.lock();
523 // XBroadcaster
526 void TableModel::lockBroadcasts()
528 ::SolarMutexGuard aGuard;
529 ++mnNotifyLock;
533 void TableModel::unlockBroadcasts()
535 ::SolarMutexGuard aGuard;
536 --mnNotifyLock;
537 if( mnNotifyLock <= 0 )
539 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);
557 else
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];
570 else
572 CellRef xRet;
573 return xRet;
578 CellRef TableModel::createCell()
580 CellRef xCell;
581 if( mpTableObj )
582 mpTableObj->createCell( xCell );
583 return xCell;
587 void TableModel::insertColumns( sal_Int32 nIndex, sal_Int32 nCount )
589 if( !(nCount && mpTableObj) )
590 return;
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();
599 while( nRows-- )
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());
612 if( bUndo )
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();
645 nColSpan += nCount;
646 merge( nCol, nRow, nColSpan, nRowSpan );
651 if( bUndo )
652 rModel.EndUndo();
654 rModel.SetChanged();
656 catch( uno::Exception& )
658 TOOLS_WARN_EXCEPTION("svx", "");
660 updateColumns();
661 setModified(true);
665 void TableModel::removeColumns( sal_Int32 nIndex, sal_Int32 nCount )
667 sal_Int32 nColCount = getColumnCountImpl();
669 if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nColCount)) )
670 return;
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());
684 if( bUndo )
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;
702 if( nColSpan <= 1 )
703 continue;
705 if( nCol >= nIndex )
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() )
716 if( bUndo )
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 );
727 if( bUndo )
728 xCell->AddUndo();
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.
736 if( bUndo )
738 TableModelRef xThis( this );
739 ColumnVector aRemovedCols( nCount );
740 sal_Int32 nOffset;
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 );
759 while( nRows-- )
760 maRows[nRows]->removeColumns( nIndex, nCount );
762 if( bUndo )
763 rModel.EndUndo();
765 rModel.SetChanged();
767 catch( uno::Exception& )
769 TOOLS_WARN_EXCEPTION("svx", "");
772 updateColumns();
773 setModified(true);
777 void TableModel::insertRows( sal_Int32 nIndex, sal_Int32 nCount )
779 if( !(nCount && mpTableObj) )
780 return;
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);
800 if( bUndo )
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();
819 nRowSpan += nCount;
820 merge( nCol, nRow, nColSpan, nRowSpan );
825 catch( uno::Exception& )
827 TOOLS_WARN_EXCEPTION("svx", "");
829 if( bUndo )
830 rModel.EndUndo();
832 rModel.SetChanged();
834 updateRows();
835 setModified(true);
839 void TableModel::removeRows( sal_Int32 nIndex, sal_Int32 nCount )
841 sal_Int32 nRowCount = getRowCountImpl();
843 if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nRowCount)) )
844 return;
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;
857 if( bUndo )
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;
875 if( nRowSpan <= 1 )
876 continue;
878 if( nRow >= nIndex )
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() )
889 if( bUndo )
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 );
900 if( bUndo )
901 xCell->AddUndo();
902 xCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
907 if( bUndo )
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 );
922 if( bUndo )
923 rModel.EndUndo();
925 rModel.SetChanged();
927 catch( uno::Exception& )
929 TOOLS_WARN_EXCEPTION("svx", "");
932 updateRows();
933 setModified(true);
937 TableRowRef const & TableModel::getRow( sal_Int32 nRow ) const
939 if( (nRow >= 0) && (nRow < getRowCountImpl()) )
940 return maRows[nRow];
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();
966 while( nCol > 0 )
968 bool bEmpty = true;
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() )
973 bEmpty = false;
976 if( bEmpty )
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 );
995 bWasModified = true;
998 nCol--;
1001 sal_Int32 nRow = getRowCountImpl() - 1;
1002 sal_Int32 nCols = getColumnCountImpl();
1003 while( nRow > 0 )
1005 bool bEmpty = true;
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() )
1010 bEmpty = false;
1013 if( bEmpty )
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;
1035 nRow--;
1038 if( bWasModified )
1039 setModified(true);
1043 void TableModel::merge( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan )
1045 if(nullptr == mpTableObj)
1046 return;
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!");
1058 // merge first cell
1059 CellRef xOriginCell( getCell( nCol, nRow ) );
1060 if(!xOriginCell.is())
1061 return;
1063 if( bUndo )
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() )
1077 if( bUndo )
1078 xCell->AddUndo();
1079 xCell->setMerged();
1080 xOriginCell->mergeContent( xCell );
1083 nTempCol = nCol;
1087 void TableModel::updateRows()
1089 sal_Int32 nRow = 0;
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: */