| /************************************************************** |
| * |
| * 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 <com/sun/star/table/XMergeableCell.hpp> |
| |
| #include <algorithm> |
| #include <boost/bind.hpp> |
| |
| #include <vcl/svapp.hxx> |
| #include <vos/mutex.hxx> |
| |
| #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<sal_Int32>(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<RowVector,RowVector::iterator>( 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<RowVector,RowVector::iterator,TableRowRef>( 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<ColumnVector,ColumnVector::iterator>( 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<ColumnVector,ColumnVector::iterator,TableColumnRef>( 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 ) |
| { |
| CellVector::iterator aIter2 = aIter + nRow * nCount; |
| DBG_ASSERT(aIter2 < aCells.end(), "sdr::table::TableModel::UndoRemoveColumns(), invalid iterator!"); |
| maRows[nRow]->insertColumns( nIndex, nCount, &aIter2 ); |
| } |
| |
| 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 <stdio.h> |
| #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, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\r" ); |
| fprintf( file, "<table columns=\"%ld\" rows=\"%ld\" updated=\"%s\">\n\r", nColCount, nRowCount, mbNotifyPending ? "false" : "true"); |
| |
| for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol ) |
| { |
| fprintf( file, "<column this=\"%lx\"/>\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, "<row this=\"%lx\">\n\r", maRows[nRow].get() ); |
| for( sal_Int32 nCol = 0; nCol < nColCount; ++nCol ) |
| { |
| CellRef xCell( getCell( nCol, nRow ) ); |
| fprintf( file, "<cell this=\"%lx\"", xCell.get() ); |
| |
| sal_Int32 nRowSpan = xCell->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</row>\n\r" ); |
| } |
| |
| fprintf( file, "</table>\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<ColumnVector,ColumnVector::iterator,TableColumnRef>( 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<ColumnVector,ColumnVector::iterator>( 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<RowVector,RowVector::iterator,TableRowRef>( 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<RowVector,RowVector::iterator>( 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++; |
| } |
| } |
| |
| // ----------------------------------------------------------------------------- |
| |
| } } |