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