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