/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_svx.hxx" #include #include #include #include #include #include "cell.hxx" #include "cellcursor.hxx" #include "tablemodel.hxx" #include "tablerow.hxx" #include "tablerows.hxx" #include "tablecolumn.hxx" #include "tablecolumns.hxx" #include "tableundo.hxx" #include "svx/svdotable.hxx" #include "svx/svdmodel.hxx" #include "svx/svdstr.hrc" #include "svx/svdglob.hxx" //#define PLEASE_DEBUG_THE_TABLES 1 using ::rtl::OUString; using namespace ::osl; using namespace ::vos; using namespace ::com::sun::star::uno; using namespace ::com::sun::star::table; using namespace ::com::sun::star::lang; using namespace ::com::sun::star::container; using namespace ::com::sun::star::beans; using namespace ::com::sun::star::util; // ----------------------------------------------------------------------------- namespace sdr { namespace table { // ----------------------------------------------------------------------------- // removes the given range from a vector template< class Vec, class Iter > void remove_range( Vec& rVector, sal_Int32 nIndex, sal_Int32 nCount ) { const sal_Int32 nSize = static_cast(rVector.size()); if( nCount && (nIndex >= 0) && (nIndex < nSize) ) { if( (nIndex + nCount) >= nSize ) { // remove at end rVector.resize( nIndex ); } else { Iter aBegin( rVector.begin() ); while( nIndex-- ) aBegin++; if( nCount == 1 ) { rVector.erase( aBegin ); } else { Iter aEnd( aBegin ); while( nCount-- ) aEnd++; rVector.erase( aBegin, aEnd ); } } } } // ----------------------------------------------------------------------------- /** inserts a range into a vector */ template< class Vec, class Iter, class Entry > sal_Int32 insert_range( Vec& rVector, sal_Int32 nIndex, sal_Int32 nCount ) { if( nCount ) { if( nIndex >= static_cast< sal_Int32 >( rVector.size() ) ) { // append at end nIndex = static_cast< sal_Int32 >( rVector.size() ); // cap to end rVector.resize( nIndex + nCount ); } else { // insert sal_Int32 nFind = nIndex; Iter aIter( rVector.begin() ); while( nFind-- ) aIter++; Entry aEmpty; rVector.insert( aIter, nCount, aEmpty ); } } return nIndex; } // ----------------------------------------------------------------------------- TableModel::TableModel( SdrTableObj* pTableObj ) : TableModelBase( m_aMutex ) , mpTableObj( pTableObj ) , mbModified( sal_False ) , mbNotifyPending( false ) , mnNotifyLock( 0 ) { } TableModel::TableModel( SdrTableObj* pTableObj, const TableModelRef& xSourceTable ) : TableModelBase( m_aMutex ) , mpTableObj( pTableObj ) , mbModified( sal_False ) , mbNotifyPending( false ) , mnNotifyLock( 0 ) { if( xSourceTable.is() ) { const sal_Int32 nColCount = xSourceTable->getColumnCountImpl(); const sal_Int32 nRowCount = xSourceTable->getRowCountImpl(); init( nColCount, nRowCount ); sal_Int32 nRows = nRowCount; while( nRows-- ) (*maRows[nRows]) = (*xSourceTable->maRows[nRows]); sal_Int32 nColumns = nColCount; while( nColumns-- ) (*maColumns[nColumns]) = (*xSourceTable->maColumns[nColumns]); // copy cells for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol ) { for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow ) { CellRef xTargetCell( getCell( nCol, nRow ) ); if( xTargetCell.is() ) xTargetCell->cloneFrom( xSourceTable->getCell( nCol, nRow ) ); } } } } // ----------------------------------------------------------------------------- TableModel::~TableModel() { } // ----------------------------------------------------------------------------- void TableModel::init( sal_Int32 nColumns, sal_Int32 nRows ) { if( nRows < 20 ) maRows.reserve( 20 ); if( nColumns < 20 ) maColumns.reserve( 20 ); if( nRows && nColumns ) { maColumns.resize( nColumns ); maRows.resize( nRows ); while( nRows-- ) maRows[nRows].set( new TableRow( this, nRows, nColumns ) ); while( nColumns-- ) maColumns[nColumns].set( new TableColumn( this, nColumns ) ); } } // ----------------------------------------------------------------------------- // ICellRange // ----------------------------------------------------------------------------- sal_Int32 TableModel::getLeft() { return 0; } // ----------------------------------------------------------------------------- sal_Int32 TableModel::getTop() { return 0; } // ----------------------------------------------------------------------------- sal_Int32 TableModel::getRight() { return getColumnCount(); } // ----------------------------------------------------------------------------- sal_Int32 TableModel::getBottom() { return getRowCount(); } // ----------------------------------------------------------------------------- Reference< XTable > TableModel::getTable() { return this; } // ----------------------------------------------------------------------------- void TableModel::UndoInsertRows( sal_Int32 nIndex, sal_Int32 nCount ) { TableModelNotifyGuard aGuard( this ); // remove the rows remove_range( maRows, nIndex, nCount ); updateRows(); setModified(sal_True); } // ----------------------------------------------------------------------------- void TableModel::UndoRemoveRows( sal_Int32 nIndex, RowVector& aRows ) { TableModelNotifyGuard aGuard( this ); const sal_Int32 nCount = sal::static_int_cast< sal_Int32 >( aRows.size() ); nIndex = insert_range( maRows, nIndex, nCount ); for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset ) maRows[nIndex+nOffset] = aRows[nOffset]; updateRows(); setModified(sal_True); } // ----------------------------------------------------------------------------- void TableModel::UndoInsertColumns( sal_Int32 nIndex, sal_Int32 nCount ) { TableModelNotifyGuard aGuard( this ); // now remove the columns remove_range( maColumns, nIndex, nCount ); sal_Int32 nRows = getRowCountImpl(); while( nRows-- ) maRows[nRows]->removeColumns( nIndex, nCount ); updateColumns(); setModified(sal_True); } // ----------------------------------------------------------------------------- void TableModel::UndoRemoveColumns( sal_Int32 nIndex, ColumnVector& aCols, CellVector& aCells ) { TableModelNotifyGuard aGuard( this ); const sal_Int32 nCount = sal::static_int_cast< sal_Int32 >( aCols.size() ); // assert if there are not enough cells saved DBG_ASSERT( (aCols.size() * maRows.size()) == aCells.size(), "sdr::table::TableModel::UndoRemoveColumns(), invalid undo data!" ); nIndex = insert_range( maColumns, nIndex, nCount ); for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset ) maColumns[nIndex+nOffset] = aCols[nOffset]; CellVector::iterator aIter( aCells.begin() ); sal_Int32 nRows = getRowCountImpl(); for( sal_Int32 nRow = 0; nRow < nRows; ++nRow ) maRows[nRow]->insertColumns( nIndex, nCount, &aIter ); updateColumns(); setModified(sal_True); } // ----------------------------------------------------------------------------- // XTable // ----------------------------------------------------------------------------- Reference< XCellCursor > SAL_CALL TableModel::createCursor() throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); return createCursorByRange( Reference< XCellRange >( this ) ); } // ----------------------------------------------------------------------------- Reference< XCellCursor > SAL_CALL TableModel::createCursorByRange( const Reference< XCellRange >& Range ) throw (IllegalArgumentException, RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); ICellRange* pRange = dynamic_cast< ICellRange* >( Range.get() ); if( (pRange == 0) || (pRange->getTable().get() != this) ) throw IllegalArgumentException(); TableModelRef xModel( this ); return new CellCursor( xModel, pRange->getLeft(), pRange->getTop(), pRange->getRight(), pRange->getBottom() ); } // ----------------------------------------------------------------------------- sal_Int32 SAL_CALL TableModel::getRowCount() throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); return getRowCountImpl(); } // ----------------------------------------------------------------------------- sal_Int32 SAL_CALL TableModel::getColumnCount() throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); return getColumnCountImpl(); } // ----------------------------------------------------------------------------- // XComponent // ----------------------------------------------------------------------------- void TableModel::dispose() throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); TableModelBase::dispose(); } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::addEventListener( const Reference< XEventListener >& xListener ) throw (RuntimeException) { TableModelBase::addEventListener( xListener ); } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::removeEventListener( const Reference< XEventListener >& xListener ) throw (RuntimeException) { TableModelBase::removeEventListener( xListener ); } // ----------------------------------------------------------------------------- // XModifiable // ----------------------------------------------------------------------------- sal_Bool SAL_CALL TableModel::isModified( ) throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); return mbModified; } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::setModified( sal_Bool bModified ) throw (PropertyVetoException, RuntimeException) { { OGuard aGuard( Application::GetSolarMutex() ); mbModified = bModified; } if( bModified ) notifyModification(); } // ----------------------------------------------------------------------------- // XModifyBroadcaster // ----------------------------------------------------------------------------- void SAL_CALL TableModel::addModifyListener( const Reference< XModifyListener >& xListener ) throw (RuntimeException) { rBHelper.addListener( XModifyListener::static_type() , xListener ); } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::removeModifyListener( const Reference< XModifyListener >& xListener ) throw (RuntimeException) { rBHelper.removeListener( XModifyListener::static_type() , xListener ); } // ----------------------------------------------------------------------------- // XColumnRowRange // ----------------------------------------------------------------------------- Reference< XTableColumns > SAL_CALL TableModel::getColumns() throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); if( !mxTableColumns.is() ) mxTableColumns.set( new TableColumns( this ) ); return mxTableColumns.get(); } // ----------------------------------------------------------------------------- Reference< XTableRows > SAL_CALL TableModel::getRows() throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); if( !mxTableRows.is() ) mxTableRows.set( new TableRows( this ) ); return mxTableRows.get(); } // ----------------------------------------------------------------------------- // XCellRange // ----------------------------------------------------------------------------- Reference< XCell > SAL_CALL TableModel::getCellByPosition( sal_Int32 nColumn, sal_Int32 nRow ) throw ( IndexOutOfBoundsException, RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); CellRef xCell( getCell( nColumn, nRow ) ); if( xCell.is() ) return xCell.get(); throw IndexOutOfBoundsException(); } // ----------------------------------------------------------------------------- Reference< XCellRange > SAL_CALL TableModel::getCellRangeByPosition( sal_Int32 nLeft, sal_Int32 nTop, sal_Int32 nRight, sal_Int32 nBottom ) throw (IndexOutOfBoundsException, RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); if( (nLeft >= 0) && (nTop >= 0) && (nRight >= nLeft) && (nBottom >= nTop) && (nRight < getColumnCountImpl()) && (nBottom < getRowCountImpl() ) ) { TableModelRef xModel( this ); return new CellRange( xModel, nLeft, nTop, nRight, nBottom ); } throw IndexOutOfBoundsException(); } // ----------------------------------------------------------------------------- Reference< XCellRange > SAL_CALL TableModel::getCellRangeByName( const OUString& /*aRange*/ ) throw (RuntimeException) { return Reference< XCellRange >(); } // ----------------------------------------------------------------------------- // XPropertySet // ----------------------------------------------------------------------------- Reference< XPropertySetInfo > SAL_CALL TableModel::getPropertySetInfo( ) throw (RuntimeException) { Reference< XPropertySetInfo > xInfo; return xInfo; } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::setPropertyValue( const ::rtl::OUString& /*aPropertyName*/, const Any& /*aValue*/ ) throw (UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException) { } // ----------------------------------------------------------------------------- Any SAL_CALL TableModel::getPropertyValue( const OUString& /*PropertyName*/ ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { return Any(); } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::addPropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::removePropertyChangeListener( const OUString& /*aPropertyName*/, const Reference< XPropertyChangeListener >& /*xListener*/ ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::addVetoableChangeListener( const OUString& /*aPropertyName*/, const Reference< XVetoableChangeListener >& /*xListener*/ ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { } // ----------------------------------------------------------------------------- void SAL_CALL TableModel::removeVetoableChangeListener( const OUString& /*aPropertyName*/, const Reference< XVetoableChangeListener >& /*xListener*/ ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { } // ----------------------------------------------------------------------------- // XFastPropertySet // ----------------------------------------------------------------------------- void SAL_CALL TableModel::setFastPropertyValue( ::sal_Int32 /*nHandle*/, const Any& /*aValue*/ ) throw (UnknownPropertyException, PropertyVetoException, IllegalArgumentException, WrappedTargetException, RuntimeException) { } // ----------------------------------------------------------------------------- Any SAL_CALL TableModel::getFastPropertyValue( ::sal_Int32 /*nHandle*/ ) throw (UnknownPropertyException, WrappedTargetException, RuntimeException) { Any aAny; return aAny; } // ----------------------------------------------------------------------------- // internals // ----------------------------------------------------------------------------- sal_Int32 TableModel::getRowCountImpl() const { return static_cast< sal_Int32 >( maRows.size() ); } // ----------------------------------------------------------------------------- sal_Int32 TableModel::getColumnCountImpl() const { return static_cast< sal_Int32 >( maColumns.size() ); } // ----------------------------------------------------------------------------- void TableModel::disposing() { if( !maRows.empty() ) { RowVector::iterator aIter( maRows.begin() ); while( aIter != maRows.end() ) (*aIter++)->dispose(); RowVector().swap(maRows); } if( !maColumns.empty() ) { ColumnVector::iterator aIter( maColumns.begin() ); while( aIter != maColumns.end() ) (*aIter++)->dispose(); ColumnVector().swap(maColumns); } if( mxTableColumns.is() ) { mxTableColumns->dispose(); mxTableColumns.clear(); } if( mxTableRows.is() ) { mxTableRows->dispose(); mxTableRows.clear(); } mpTableObj = 0; } // ----------------------------------------------------------------------------- // XBroadcaster // ----------------------------------------------------------------------------- void TableModel::lockBroadcasts() throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); ++mnNotifyLock; } // ----------------------------------------------------------------------------- void TableModel::unlockBroadcasts() throw (RuntimeException) { OGuard aGuard( Application::GetSolarMutex() ); --mnNotifyLock; if( mnNotifyLock <= 0 ) { mnNotifyLock = 0; if( mbNotifyPending ) notifyModification(); } } // ----------------------------------------------------------------------------- #ifdef PLEASE_DEBUG_THE_TABLES #include #endif void TableModel::notifyModification() { ::osl::MutexGuard guard( m_aMutex ); if( (mnNotifyLock == 0) && mpTableObj && mpTableObj->GetModel() ) { mbNotifyPending = false; ::cppu::OInterfaceContainerHelper * pModifyListeners = rBHelper.getContainer( XModifyListener::static_type() ); if( pModifyListeners ) { EventObject aSource; aSource.Source = static_cast< ::cppu::OWeakObject* >(this); pModifyListeners->notifyEach( &XModifyListener::modified, aSource); } } else { mbNotifyPending = true; } #ifdef PLEASE_DEBUG_THE_TABLES FILE* file = fopen( "c:\\table.xml","w" ); const sal_Int32 nColCount = getColumnCountImpl(); const sal_Int32 nRowCount = getRowCountImpl(); fprintf( file, "\n\r" ); fprintf( file, "\n\r", nColCount, nRowCount, mbNotifyPending ? "false" : "true"); for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol ) { fprintf( file, "\n\r", maColumns[nCol].get() ); } // first check merged cells before and inside the removed rows for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow ) { fprintf( file, "\n\r", maRows[nRow].get() ); for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol ) { CellRef xCell( getCell( nCol, nRow ) ); fprintf( file, "getRowSpan(); sal_Int32 nColSpan = xCell->getColumnSpan(); sal_Bool bMerged = xCell->isMerged(); if( nColSpan != 1 ) fprintf( file, " column-span=\"%ld\"", nColSpan ); if( nRowSpan != 1 ) fprintf( file, " row-span=\"%ld\"", nRowSpan ); if( bMerged ) fprintf( file, " merged=\"true\"" ); fprintf( file, "/>" ); } fprintf( file, "\n\r\n\r" ); } fprintf( file, "
\n\r" ); fclose( file ); #endif } // ----------------------------------------------------------------------------- CellRef TableModel::getCell( sal_Int32 nCol, sal_Int32 nRow ) const { if( ((nRow >= 0) && (nRow < getRowCountImpl())) && (nCol >= 0) && (nCol < getColumnCountImpl()) ) { return maRows[nRow]->maCells[nCol]; } else { CellRef xRet; return xRet; } } // ----------------------------------------------------------------------------- /* bool TableModel::getCellPos( const CellRef& xCell, ::sal_Int32& rnCol, ::sal_Int32& rnRow ) const { const sal_Int32 nRowCount = getRowCount(); const sal_Int32 nColCount = getColumnCount(); for( rnRow = 0; rnRow < nRowCount; rnRow++ ) { for( rnCol = 0; rnCol < nColCount; rnCol++ ) { if( maRows[rnRow]->maCells[rnCol] == xCell ) { return true; } } } return false; } */ // ----------------------------------------------------------------------------- CellRef TableModel::createCell() { CellRef xCell; if( mpTableObj ) mpTableObj->createCell( xCell ); return xCell; } // ----------------------------------------------------------------------------- void TableModel::insertColumns( sal_Int32 nIndex, sal_Int32 nCount ) { if( nCount && mpTableObj ) { try { SdrModel* pModel = mpTableObj->GetModel(); TableModelNotifyGuard aGuard( this ); nIndex = insert_range( maColumns, nIndex, nCount ); sal_Int32 nRows = getRowCountImpl(); while( nRows-- ) maRows[nRows]->insertColumns( nIndex, nCount ); ColumnVector aNewColumns(nCount); for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset ) { TableColumnRef xNewCol( new TableColumn( this, nIndex+nOffset ) ); maColumns[nIndex+nOffset] = xNewCol; aNewColumns[nOffset] = xNewCol; } const bool bUndo = pModel && mpTableObj->IsInserted() && pModel->IsUndoEnabled(); if( bUndo ) { pModel->BegUndo( ImpGetResStr(STR_TABLE_INSCOL) ); pModel->AddUndo( pModel->GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) ); TableModelRef xThis( this ); nRows = getRowCountImpl(); CellVector aNewCells( nCount * nRows ); CellVector::iterator aCellIter( aNewCells.begin() ); nRows = getRowCountImpl(); for( sal_Int32 nRow = 0; nRow < nRows; ++nRow ) { for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset ) (*aCellIter++) = getCell( nIndex + nOffset, nRow ); } pModel->AddUndo( new InsertColUndo( xThis, nIndex, aNewColumns, aNewCells ) ); } const sal_Int32 nRowCount = getRowCountImpl(); // check if cells merge over new columns for( sal_Int32 nCol = 0; nCol < nIndex; ++nCol ) { for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow ) { CellRef xCell( getCell( nCol, nRow ) ); sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1; if( (nColSpan != 1) && ((nColSpan + nCol ) > nIndex) ) { // cell merges over newly created columns, so add the new columns to the merged cell const sal_Int32 nRowSpan = xCell->getRowSpan(); nColSpan += nCount; merge( nCol, nRow, nColSpan, nRowSpan ); } } } if( bUndo ) pModel->EndUndo(); if( pModel ) pModel->SetChanged(); } catch( Exception& ) { DBG_ERROR("sdr::table::TableModel::insertColumns(), exception caught!"); } updateColumns(); setModified(sal_True); } } // ----------------------------------------------------------------------------- void TableModel::removeColumns( sal_Int32 nIndex, sal_Int32 nCount ) { sal_Int32 nColCount = getColumnCountImpl(); if( mpTableObj && nCount && (nIndex >= 0) && (nIndex < nColCount) ) { try { TableModelNotifyGuard aGuard( this ); // clip removed columns to columns actually avalaible if( (nIndex + nCount) > nColCount ) nCount = nColCount - nIndex; sal_Int32 nRows = getRowCountImpl(); SdrModel* pModel = mpTableObj->GetModel(); const bool bUndo = pModel && mpTableObj->IsInserted() && pModel->IsUndoEnabled(); if( bUndo ) { pModel->BegUndo( ImpGetResStr(STR_UNDO_COL_DELETE) ); pModel->AddUndo( pModel->GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) ); TableModelRef xThis( this ); ColumnVector aRemovedCols( nCount ); sal_Int32 nOffset; for( nOffset = 0; nOffset < nCount; ++nOffset ) { aRemovedCols[nOffset] = maColumns[nIndex+nOffset]; } CellVector aRemovedCells( nCount * nRows ); CellVector::iterator aCellIter( aRemovedCells.begin() ); for( sal_Int32 nRow = 0; nRow < nRows; ++nRow ) { for( nOffset = 0; nOffset < nCount; ++nOffset ) (*aCellIter++) = getCell( nIndex + nOffset, nRow ); } pModel->AddUndo( new RemoveColUndo( xThis, nIndex, aRemovedCols, aRemovedCells ) ); } // only rows before and inside the removed rows are considered nColCount = nIndex + nCount + 1; const sal_Int32 nRowCount = getRowCountImpl(); // first check merged cells before and inside the removed rows for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol ) { for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow ) { CellRef xCell( getCell( nCol, nRow ) ); sal_Int32 nColSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getColumnSpan() : 1; if( nColSpan <= 1 ) continue; if( nCol >= nIndex ) { // current cell is inside the removed columns if( (nCol + nColSpan) > ( nIndex + nCount ) ) { // current cells merges with columns after the removed columns const sal_Int32 nRemove = nCount - nCol + nIndex; CellRef xTargetCell( getCell( nIndex + nCount, nRow ) ); if( xTargetCell.is() ) { if( bUndo ) xTargetCell->AddUndo(); xTargetCell->merge( nColSpan - nRemove, xCell->getRowSpan() ); xTargetCell->replaceContentAndFormating( xCell ); } } } else if( nColSpan > (nIndex - nCol) ) { // current cells spans inside the removed columns, so adjust const sal_Int32 nRemove = ::std::min( nCount, nCol + nColSpan - nIndex ); if( bUndo ) xCell->AddUndo(); xCell->merge( nColSpan - nRemove, xCell->getRowSpan() ); } } } // now remove the columns remove_range( maColumns, nIndex, nCount ); while( nRows-- ) maRows[nRows]->removeColumns( nIndex, nCount ); if( bUndo ) pModel->EndUndo(); if( pModel ) pModel->SetChanged(); } catch( Exception& ) { DBG_ERROR("sdr::table::TableModel::removeColumns(), exception caught!"); } updateColumns(); setModified(sal_True); } } // ----------------------------------------------------------------------------- void TableModel::insertRows( sal_Int32 nIndex, sal_Int32 nCount ) { if( nCount && mpTableObj ) { SdrModel* pModel = mpTableObj->GetModel(); const bool bUndo = pModel && mpTableObj->IsInserted() && pModel->IsUndoEnabled(); try { TableModelNotifyGuard aGuard( this ); nIndex = insert_range( maRows, nIndex, nCount ); RowVector aNewRows(nCount); const sal_Int32 nColCount = getColumnCountImpl(); for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset ) { TableRowRef xNewRow( new TableRow( this, nIndex+nOffset, nColCount ) ); maRows[nIndex+nOffset] = xNewRow; aNewRows[nOffset] = xNewRow; } if( bUndo ) { pModel->BegUndo( ImpGetResStr(STR_TABLE_INSROW) ); pModel->AddUndo( pModel->GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) ); TableModelRef xThis( this ); pModel->AddUndo( new InsertRowUndo( xThis, nIndex, aNewRows ) ); } // check if cells merge over new columns for( sal_Int32 nRow = 0; nRow < nIndex; ++nRow ) { for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol ) { CellRef xCell( getCell( nCol, nRow ) ); sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1; if( (nRowSpan > 1) && ((nRowSpan + nRow) > nIndex) ) { // cell merges over newly created columns, so add the new columns to the merged cell const sal_Int32 nColSpan = xCell->getColumnSpan(); nRowSpan += nCount; merge( nCol, nRow, nColSpan, nRowSpan ); } } } } catch( Exception& ) { DBG_ERROR("sdr::table::TableModel::insertRows(), exception caught!"); } if( bUndo ) pModel->EndUndo(); if( pModel ) pModel->SetChanged(); updateRows(); setModified(sal_True); } } // ----------------------------------------------------------------------------- void TableModel::removeRows( sal_Int32 nIndex, sal_Int32 nCount ) { sal_Int32 nRowCount = getRowCountImpl(); if( mpTableObj && nCount && (nIndex >= 0) && (nIndex < nRowCount) ) { SdrModel* pModel = mpTableObj->GetModel(); const bool bUndo = pModel && mpTableObj->IsInserted()&& pModel->IsUndoEnabled(); try { TableModelNotifyGuard aGuard( this ); // clip removed rows to rows actually avalaible if( (nIndex + nCount) > nRowCount ) nCount = nRowCount - nIndex; if( bUndo ) { pModel->BegUndo( ImpGetResStr(STR_UNDO_ROW_DELETE) ); pModel->AddUndo( pModel->GetSdrUndoFactory().CreateUndoGeoObject(*mpTableObj) ); TableModelRef xThis( this ); RowVector aRemovedRows( nCount ); for( sal_Int32 nOffset = 0; nOffset < nCount; ++nOffset ) aRemovedRows[nOffset] = maRows[nIndex+nOffset]; pModel->AddUndo( new RemoveRowUndo( xThis, nIndex, aRemovedRows ) ); } // only rows before and inside the removed rows are considered nRowCount = nIndex + nCount + 1; const sal_Int32 nColCount = getColumnCountImpl(); // first check merged cells before and inside the removed rows for( sal_Int32 nRow = 0; nRow < nRowCount; ++nRow ) { for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol ) { CellRef xCell( getCell( nCol, nRow ) ); sal_Int32 nRowSpan = (xCell.is() && !xCell->isMerged()) ? xCell->getRowSpan() : 1; if( nRowSpan <= 1 ) continue; if( nRow >= nIndex ) { // current cell is inside the removed rows if( (nRow + nRowSpan) > (nIndex + nCount) ) { // current cells merges with rows after the removed rows const sal_Int32 nRemove = nCount - nRow + nIndex; CellRef xTargetCell( getCell( nCol, nIndex + nCount ) ); if( xTargetCell.is() ) { if( bUndo ) xTargetCell->AddUndo(); xTargetCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove ); xTargetCell->replaceContentAndFormating( xCell ); } } } else if( nRowSpan > (nIndex - nRow) ) { // current cells spans inside the removed rows, so adjust const sal_Int32 nRemove = ::std::min( nCount, nRow + nRowSpan - nIndex ); if( bUndo ) xCell->AddUndo(); xCell->merge( xCell->getColumnSpan(), nRowSpan - nRemove ); } } } // now remove the rows remove_range( maRows, nIndex, nCount ); if( bUndo ) pModel->EndUndo(); if( pModel ) pModel->SetChanged(); } catch( Exception& ) { DBG_ERROR("sdr::table::TableModel::removeRows(), exception caught!"); } updateRows(); setModified(sal_True); } } // ----------------------------------------------------------------------------- TableRowRef TableModel::getRow( sal_Int32 nRow ) const throw (IndexOutOfBoundsException) { if( (nRow >= 0) && (nRow < getRowCountImpl()) ) return maRows[nRow]; throw IndexOutOfBoundsException(); } // ----------------------------------------------------------------------------- TableColumnRef TableModel::getColumn( sal_Int32 nColumn ) const throw (IndexOutOfBoundsException) { if( (nColumn >= 0) && (nColumn < getColumnCountImpl()) ) return maColumns[nColumn]; throw IndexOutOfBoundsException(); } // ----------------------------------------------------------------------------- /** deletes rows and columns that are completly merged. Must be called between BegUndo/EndUndo! */ void TableModel::optimize() { TableModelNotifyGuard aGuard( this ); bool bWasModified = false; if( !maRows.empty() && !maColumns.empty() ) { sal_Int32 nCol = getColumnCountImpl() - 1; while( nCol > 0 ) { bool bEmpty = true; for( sal_Int32 nRow = 0; (nRow < getRowCountImpl()) && bEmpty; nRow++ ) { Reference< XMergeableCell > xCell( getCellByPosition( nCol, nRow ), UNO_QUERY ); if( xCell.is() && !xCell->isMerged() ) bEmpty = false; } if( bEmpty ) { if( nCol > 0 ) try { const OUString sWidth( RTL_CONSTASCII_USTRINGPARAM("Width") ); sal_Int32 nWidth1 = 0, nWidth2 = 0; Reference< XPropertySet > xSet1( static_cast< XCellRange* >( maColumns[nCol].get() ), UNO_QUERY_THROW ); Reference< XPropertySet > xSet2( static_cast< XCellRange* >( maColumns[nCol-1].get() ), UNO_QUERY_THROW ); xSet1->getPropertyValue( sWidth ) >>= nWidth1; xSet2->getPropertyValue( sWidth ) >>= nWidth2; nWidth1 += nWidth2; xSet2->setPropertyValue( sWidth, Any( nWidth1 ) ); } catch( Exception& e ) { (void)e; DBG_ERROR("svx::TableModel::optimize(), exception caught!"); } removeColumns( nCol, 1 ); bWasModified = true; } nCol--; } sal_Int32 nRow = getRowCountImpl() - 1; while( nRow > 0 ) { bool bEmpty = true; for( nCol = 0; (nCol < getColumnCountImpl()) && bEmpty; nCol++ ) { Reference< XMergeableCell > xCell( getCellByPosition( nCol, nRow ), UNO_QUERY ); if( xCell.is() && !xCell->isMerged() ) bEmpty = false; } if( bEmpty ) { if( nRow > 0 ) try { const OUString sHeight( RTL_CONSTASCII_USTRINGPARAM("Height") ); sal_Int32 nHeight1 = 0, nHeight2 = 0; Reference< XPropertySet > xSet1( static_cast< XCellRange* >( maRows[nRow].get() ), UNO_QUERY_THROW ); Reference< XPropertySet > xSet2( static_cast< XCellRange* >( maRows[nRow-1].get() ), UNO_QUERY_THROW ); xSet1->getPropertyValue( sHeight ) >>= nHeight1; xSet2->getPropertyValue( sHeight ) >>= nHeight2; nHeight1 += nHeight2; xSet2->setPropertyValue( sHeight, Any( nHeight1 ) ); } catch( Exception& e ) { (void)e; DBG_ERROR("svx::TableModel::optimize(), exception caught!"); } removeRows( nRow, 1 ); bWasModified = true; } nRow--; } } if( bWasModified ) setModified(sal_True); } // ----------------------------------------------------------------------------- void TableModel::merge( sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan ) { SdrModel* pModel = mpTableObj->GetModel(); const bool bUndo = pModel && mpTableObj->IsInserted() && pModel->IsUndoEnabled(); const sal_Int32 nLastRow = nRow + nRowSpan; const sal_Int32 nLastCol = nCol + nColSpan; if( (nLastRow > getRowCount()) || (nLastCol > getRowCount() ) ) { DBG_ERROR("TableModel::merge(), merge beyound the table!"); } // merge first cell CellRef xOriginCell( dynamic_cast< Cell* >( getCellByPosition( nCol, nRow ).get() ) ); if( xOriginCell.is() ) { if( bUndo ) xOriginCell->AddUndo(); xOriginCell->merge( nColSpan, nRowSpan ); } sal_Int32 nTempCol = nCol + 1; // merge remaining cells for( ; nRow < nLastRow; nRow++ ) { for( ; nTempCol < nLastCol; nTempCol++ ) { CellRef xCell( dynamic_cast< Cell* >( getCellByPosition( nTempCol, nRow ).get() ) ); if( xCell.is() && !xCell->isMerged() ) { if( bUndo ) xCell->AddUndo(); xCell->setMerged(); xOriginCell->mergeContent( xCell ); } } nTempCol = nCol; } } // ----------------------------------------------------------------------------- void TableModel::updateRows() { sal_Int32 nRow = 0; RowVector::iterator iter = maRows.begin(); while( iter != maRows.end() ) { (*iter++)->mnRow = nRow++; } } // ----------------------------------------------------------------------------- void TableModel::updateColumns() { sal_Int32 nColumn = 0; ColumnVector::iterator iter = maColumns.begin(); while( iter != maColumns.end() ) { (*iter++)->mnColumn = nColumn++; } } // ----------------------------------------------------------------------------- } }