/**************************************************************
 * 
 * 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++;
    }
}

// -----------------------------------------------------------------------------

} }