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;
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;
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;
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 
699  // only rows before and inside the removed rows are considered
700  nColCount = nIndex + nCount + 1;
701 
702  const sal_Int32 nRowCount = getRowCountImpl();
703 
704  // first check merged cells before and inside the removed rows
705  for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
706  {
707  for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
708  {
709  CellRef xCell( getCell( nCol, nRow ) );
710  sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1;
711  if( nColSpan <= 1 )
712  continue;
713 
714  if( nCol >= nIndex )
715  {
716  // current cell is inside the removed columns
717  if( (nCol + nColSpan) > ( nIndex + nCount ) )
718  {
719  // current cells merges with columns after the removed columns
720  const sal_Int32 nRemove = nCount - nCol + nIndex;
721 
722  CellRef xTargetCell( getCell( nIndex + nCount, nRow ) );
723  if( xTargetCell.is() )
724  {
725  if( bUndo )
726  xTargetCell->AddUndo();
727  xTargetCell->merge( nColSpan - nRemove, xCell->getRowSpan() );
728  xTargetCell->replaceContentAndFormatting( xCell );
729  }
730  }
731  }
732  else if( nColSpan > (nIndex - nCol) )
733  {
734  // current cells spans inside the removed columns, so adjust
735  const sal_Int32 nRemove = ::std::min( nCount, nCol + nColSpan - nIndex );
736  if( bUndo )
737  xCell->AddUndo();
738  xCell->merge( nColSpan - nRemove, xCell->getRowSpan() );
739  }
740  }
741  }
742 
743  // We must not add RemoveColUndo before we make cell spans correct, otherwise we
744  // get invalid cell span after undo.
745  if( bUndo )
746  {
747  TableModelRef xThis( this );
748  ColumnVector aRemovedCols( nCount );
749  sal_Int32 nOffset;
750  for( nOffset = 0; nOffset < nCount; ++nOffset )
751  {
752  aRemovedCols[nOffset] = maColumns[nIndex+nOffset];
753  }
754 
755  CellVector aRemovedCells( nCount * nRows );
756  CellVector::iterator aCellIter( aRemovedCells.begin() );
757  for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
758  {
759  for( nOffset = 0; nOffset < nCount; ++nOffset )
760  (*aCellIter++) = getCell( nIndex + nOffset, nRow );
761  }
762 
763  rModel.AddUndo( std::make_unique<RemoveColUndo>( xThis, nIndex, aRemovedCols, aRemovedCells ) );
764  }
765 
766  // now remove the columns
767  remove_range<ColumnVector,ColumnVector::iterator>( maColumns, nIndex, nCount );
768  while( nRows-- )
769  maRows[nRows]->removeColumns( nIndex, nCount );
770 
771  if( bUndo )
772  rModel.EndUndo();
773 
774  rModel.SetChanged();
775  }
776  catch( Exception& )
777  {
778  TOOLS_WARN_EXCEPTION("svx", "");
779  }
780 
781  updateColumns();
782  setModified(true);
783 }
784 
785 
786 void TableModel::insertRows( sal_Int32 nIndex, sal_Int32 nCount )
787 {
788  if( !(nCount && mpTableObj) )
789  return;
790 
792  const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
793 
794  try
795  {
796  TableModelNotifyGuard aGuard( this );
797 
798  nIndex = insert_range<RowVector,RowVector::iterator,TableRowRef>( maRows, nIndex, nCount );
799 
800  RowVector aNewRows(nCount);
801  const sal_Int32 nColCount = getColumnCountImpl();
802  for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
803  {
804  TableRowRef xNewRow( new TableRow( this, nIndex+nOffset, nColCount ) );
805  maRows[nIndex+nOffset] = xNewRow;
806  aNewRows[nOffset] = xNewRow;
807  }
808 
809  if( bUndo )
810  {
811  rModel.BegUndo( SvxResId(STR_TABLE_INSROW) );
812  rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
813  TableModelRef xThis( this );
814  rModel.AddUndo( std::make_unique<InsertRowUndo>( xThis, nIndex, aNewRows ) );
815  }
816 
817  // check if cells merge over new columns
818  for( sal_Int32 nRow = 0; nRow < nIndex; ++nRow )
819  {
820  for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
821  {
822  CellRef xCell( getCell( nCol, nRow ) );
823  sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1;
824  if( (nRowSpan > 1) && ((nRowSpan + nRow) > nIndex) )
825  {
826  // cell merges over newly created columns, so add the new columns to the merged cell
827  const sal_Int32 nColSpan = xCell->getColumnSpan();
828  nRowSpan += nCount;
829  merge( nCol, nRow, nColSpan, nRowSpan );
830  }
831  }
832  }
833  }
834  catch( Exception& )
835  {
836  TOOLS_WARN_EXCEPTION("svx", "");
837  }
838  if( bUndo )
839  rModel.EndUndo();
840 
841  rModel.SetChanged();
842 
843  updateRows();
844  setModified(true);
845 }
846 
847 
848 void TableModel::removeRows( sal_Int32 nIndex, sal_Int32 nCount )
849 {
850  sal_Int32 nRowCount = getRowCountImpl();
851 
852  if( !(mpTableObj && nCount && (nIndex >= 0) && (nIndex < nRowCount)) )
853  return;
854 
856  const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
857 
858  try
859  {
860  TableModelNotifyGuard aGuard( this );
861 
862  // clip removed rows to rows actually available
863  if( (nIndex + nCount) > nRowCount )
864  nCount = nRowCount - nIndex;
865 
866  if( bUndo )
867  {
868  rModel.BegUndo( SvxResId(STR_UNDO_ROW_DELETE) );
869  rModel.AddUndo( rModel.GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) );
870  }
871 
872  // only rows before and inside the removed rows are considered
873  nRowCount = nIndex + nCount + 1;
874 
875  const sal_Int32 nColCount = getColumnCountImpl();
876 
877  // first check merged cells before and inside the removed rows
878  for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow )
879  {
880  for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol )
881  {
882  CellRef xCell( getCell( nCol, nRow ) );
883  sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1;
884  if( nRowSpan <= 1 )
885  continue;
886 
887  if( nRow >= nIndex )
888  {
889  // current cell is inside the removed rows
890  if( (nRow + nRowSpan) > (nIndex + nCount) )
891  {
892  // current cells merges with rows after the removed rows
893  const sal_Int32 nRemove = nCount - nRow + nIndex;
894 
895  CellRef xTargetCell( getCell( nCol, nIndex + nCount ) );
896  if( xTargetCell.is() )
897  {
898  if( bUndo )
899  xTargetCell->AddUndo();
900  xTargetCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
901  xTargetCell->replaceContentAndFormatting( xCell );
902  }
903  }
904  }
905  else if( nRowSpan > (nIndex - nRow) )
906  {
907  // current cells spans inside the removed rows, so adjust
908  const sal_Int32 nRemove = ::std::min( nCount, nRow + nRowSpan - nIndex );
909  if( bUndo )
910  xCell->AddUndo();
911  xCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove );
912  }
913  }
914  }
915 
916  if( bUndo )
917  {
918  TableModelRef xThis( this );
919 
920  RowVector aRemovedRows( nCount );
921  for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset )
922  aRemovedRows[nOffset] = maRows[nIndex+nOffset];
923 
924  // We must not RemoveRowUndo before we make cell spans correct, otherwise we
925  // get invalid cell span after undo.
926  rModel.AddUndo( std::make_unique<RemoveRowUndo>( xThis, nIndex, aRemovedRows ) );
927  }
928  // now remove the rows
929  remove_range<RowVector,RowVector::iterator>( maRows, nIndex, nCount );
930 
931  if( bUndo )
932  rModel.EndUndo();
933 
934  rModel.SetChanged();
935  }
936  catch( Exception& )
937  {
938  TOOLS_WARN_EXCEPTION("svx", "");
939  }
940 
941  updateRows();
942  setModified(true);
943 }
944 
945 
946 TableRowRef const & TableModel::getRow( sal_Int32 nRow ) const
947 {
948  if( (nRow >= 0) && (nRow < getRowCountImpl()) )
949  return maRows[nRow];
950 
951  throw IndexOutOfBoundsException();
952 }
953 
954 
955 TableColumnRef const & TableModel::getColumn( sal_Int32 nColumn ) const
956 {
957  if( (nColumn >= 0) && (nColumn < getColumnCountImpl()) )
958  return maColumns[nColumn];
959 
960  throw IndexOutOfBoundsException();
961 }
962 
963 
966 {
967  TableModelNotifyGuard aGuard( this );
968 
969  bool bWasModified = false;
970 
971  if( !maRows.empty() && !maColumns.empty() )
972  {
973  sal_Int32 nCol = getColumnCountImpl() - 1;
974  sal_Int32 nRows = getRowCountImpl();
975  while( nCol > 0 )
976  {
977  bool bEmpty = true;
978  for( sal_Int32 nRow = 0; (nRow < nRows) && bEmpty; nRow++ )
979  {
980  Reference< XMergeableCell > xCell( getCellByPosition( nCol, nRow ), UNO_QUERY );
981  if( xCell.is() && !xCell->isMerged() )
982  bEmpty = false;
983  }
984 
985  if( bEmpty )
986  {
987  try
988  {
989  static const OUStringLiteral sWidth(u"Width");
990  sal_Int32 nWidth1 = 0, nWidth2 = 0;
991  Reference< XPropertySet > xSet1( static_cast< XCellRange* >( maColumns[nCol].get() ), UNO_QUERY_THROW );
992  Reference< XPropertySet > xSet2( static_cast< XCellRange* >( maColumns[nCol-1].get() ), UNO_QUERY_THROW );
993  xSet1->getPropertyValue( sWidth ) >>= nWidth1;
994  xSet2->getPropertyValue( sWidth ) >>= nWidth2;
995  nWidth1 = o3tl::saturating_add(nWidth1, nWidth2);
996  xSet2->setPropertyValue( sWidth, Any( nWidth1 ) );
997  }
998  catch( Exception& )
999  {
1000  TOOLS_WARN_EXCEPTION("svx", "");
1001  }
1002 
1003  removeColumns( nCol, 1 );
1004  bWasModified = true;
1005  }
1006 
1007  nCol--;
1008  }
1009 
1010  sal_Int32 nRow = getRowCountImpl() - 1;
1011  sal_Int32 nCols = getColumnCountImpl();
1012  while( nRow > 0 )
1013  {
1014  bool bEmpty = true;
1015  for( nCol = 0; (nCol < nCols) && bEmpty; nCol++ )
1016  {
1017  Reference< XMergeableCell > xCell( getCellByPosition( nCol, nRow ), UNO_QUERY );
1018  if( xCell.is() && !xCell->isMerged() )
1019  bEmpty = false;
1020  }
1021 
1022  if( bEmpty )
1023  {
1024  try
1025  {
1026  static const OUStringLiteral sHeight(u"Height");
1027  sal_Int32 nHeight1 = 0, nHeight2 = 0;
1028  Reference< XPropertySet > xSet1( static_cast< XCellRange* >( maRows[nRow].get() ), UNO_QUERY_THROW );
1029  Reference< XPropertySet > xSet2( static_cast< XCellRange* >( maRows[nRow-1].get() ), UNO_QUERY_THROW );
1030  xSet1->getPropertyValue( sHeight ) >>= nHeight1;
1031  xSet2->getPropertyValue( sHeight ) >>= nHeight2;
1032  nHeight1 = o3tl::saturating_add(nHeight1, nHeight2);
1033  xSet2->setPropertyValue( sHeight, Any( nHeight1 ) );
1034  }
1035  catch( Exception& )
1036  {
1037  TOOLS_WARN_EXCEPTION("svx", "");
1038  }
1039 
1040  removeRows( nRow, 1 );
1041  bWasModified = true;
1042  }
1043 
1044  nRow--;
1045  }
1046  }
1047  if( bWasModified )
1048  setModified(true);
1049 }
1050 
1051 
1052 void TableModel::merge( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan )
1053 {
1054  if(nullptr == mpTableObj)
1055  return;
1056 
1058  const bool bUndo(mpTableObj->IsInserted() && rModel.IsUndoEnabled());
1059  const sal_Int32 nLastRow = nRow + nRowSpan;
1060  const sal_Int32 nLastCol = nCol + nColSpan;
1061 
1062  if( (nLastRow > getRowCount()) || (nLastCol > getColumnCount() ) )
1063  {
1064  OSL_FAIL("TableModel::merge(), merge beyond the table!");
1065  }
1066 
1067  // merge first cell
1068  CellRef xOriginCell( dynamic_cast< Cell* >( getCellByPosition( nCol, nRow ).get() ) );
1069  if(!xOriginCell.is())
1070  return;
1071 
1072  if( bUndo )
1073  xOriginCell->AddUndo();
1074  xOriginCell->merge( nColSpan, nRowSpan );
1075 
1076  sal_Int32 nTempCol = nCol + 1;
1077 
1078  // merge remaining cells
1079  for( ; nRow < nLastRow; nRow++ )
1080  {
1081  for( ; nTempCol < nLastCol; nTempCol++ )
1082  {
1083  CellRef xCell( dynamic_cast< Cell* >( getCellByPosition( nTempCol, nRow ).get() ) );
1084  if( xCell.is() && !xCell->isMerged() )
1085  {
1086  if( bUndo )
1087  xCell->AddUndo();
1088  xCell->setMerged();
1089  xOriginCell->mergeContent( xCell );
1090  }
1091  }
1092  nTempCol = nCol;
1093  }
1094 }
1095 
1097 {
1098  sal_Int32 nRow = 0;
1099  for( auto& rpRow : maRows )
1100  {
1101  rpRow->mnRow = nRow++;
1102  }
1103 }
1104 
1106 {
1107  sal_Int32 nColumn = 0;
1108  for( auto& rpCol : maColumns )
1109  {
1110  rpCol->mnColumn = nColumn++;
1111  }
1112 }
1113 
1115 {
1116  (void)xmlTextWriterStartElement(pWriter, BAD_CAST("TableModel"));
1117  for (sal_Int32 nRow = 0; nRow < getRowCountImpl(); ++nRow)
1118  for (sal_Int32 nCol = 0; nCol < getColumnCountImpl(); ++nCol)
1119  {
1120  maRows[nRow]->maCells[nCol]->dumpAsXml(pWriter, nRow, nCol);
1121  }
1122  (void)xmlTextWriterEndElement(pWriter);
1123 }
1124 
1125 }
1126 
1127 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
std::vector< CellRef > CellVector
Definition: celltypes.hxx:38
struct _xmlTextWriter * xmlTextWriterPtr
void removeRows(sal_Int32 nIndex, sal_Int32 nCount)
Definition: tablemodel.cxx:848
void createCell(sdr::table::CellRef &xCell)
Definition: svdotable.cxx:2354
bool IsInserted() const
Definition: svdobj.hxx:742
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:965
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:366
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
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
OUString SvxResId(TranslateId aId)
Definition: dialmgr.cxx:24
std::mutex m_aMutex
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:955
std::vector< TableRowRef > RowVector
Definition: celltypes.hxx:39
float u
unsigned char sal_Bool
SdrModel & getSdrModelFromSdrObject() const
Definition: svdobj.cxx:279
virtual sal_Int32 getLeft() override
Definition: tablemodel.cxx:178
TableRowRef const & getRow(sal_Int32 nRow) const
Definition: tablemodel.cxx:946
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
T saturating_add(T a, T b)
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:786
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
typedef void(CALLTYPE *GetFuncDataPtr)(sal_uInt16 &nNo