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