/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: svdotable.cxx,v $
 * $Revision: 1.4.18.1 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_svx.hxx"

#define ITEMID_BOX SDRATTR_TABLE_BORDER
#define ITEMID_BOXINFO SDRATTR_TABLE_BORDER_INNER

#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
#include <com/sun/star/container/XNamed.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>

#include <vcl/canvastools.hxx>
#include <com/sun/star/style/XStyle.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <basegfx/polygon/b2dpolygontools.hxx>
#include <basegfx/polygon/b2dpolypolygon.hxx>
#include <basegfx/polygon/b2dpolygon.hxx>
#include <svl/style.hxx>
#include "editstat.hxx"
#include "svx/outlobj.hxx"
#include "svx/svdview.hxx"
#include "svx/sdr/properties/textproperties.hxx"
#include "svx/svdotable.hxx"
#include "svx/svdhdl.hxx"
#include "viewcontactoftableobj.hxx"
#include "svx/svdoutl.hxx"
#include "svx/svddrag.hxx"
#include "svx/svdpagv.hxx"
#include "tablemodel.hxx"
#include "cell.hxx"
#include "svx/xflclit.hxx"
#include "tablelayouter.hxx"
#include "svx/svdetc.hxx"
#include "tablehandles.hxx"
#include "svx/boxitem.hxx"
#include "svx/framelink.hxx"
#include "svx/sdr/table/tabledesign.hxx"
#include "svx/svdundo.hxx"
#include "svdstr.hrc"
#include "svdglob.hxx"
#include "svx/writingmodeitem.hxx"
#include "svx/frmdiritem.hxx"
#include "svx/xflhtit.hxx"
#include "svx/xflftrit.hxx"
#include "svx/xfltrit.hxx"

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

using ::rtl::OUString;
using ::com::sun::star::uno::Any;
using ::com::sun::star::uno::Reference;
using ::com::sun::star::uno::XInterface;
using ::com::sun::star::uno::UNO_QUERY;
using ::com::sun::star::uno::UNO_QUERY_THROW;
using ::com::sun::star::uno::Exception;
using ::com::sun::star::container::XIndexAccess;
using ::com::sun::star::style::XStyle;
using ::com::sun::star::table::XTableRows;
using ::com::sun::star::table::XTableColumns;
using ::com::sun::star::table::XTable;
using ::com::sun::star::beans::XPropertySet;
using ::com::sun::star::util::XModifyBroadcaster;
using sdr::properties::TextProperties;
using sdr::properties::BaseProperties;
using namespace ::com::sun::star::text;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::style;

namespace sdr { namespace table {

class TableProperties : public TextProperties
{
protected:
    // create a new itemset
    SfxItemSet& CreateObjectSpecificItemSet(SfxItemPool& rPool);

public:
    // basic constructor
    TableProperties(SdrObject& rObj );

    // constructor for copying, but using new object
    TableProperties(const TableProperties& rProps, SdrObject& rObj );

    // destructor
    ~TableProperties();

    // Clone() operator, normally just calls the local copy constructor
    BaseProperties& Clone(SdrObject& rObj) const;

    virtual void ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem);
};

TableProperties::TableProperties(SdrObject& rObj)
: TextProperties(rObj)
{
}

TableProperties::TableProperties(const TableProperties& rProps, SdrObject& rObj)
: TextProperties(rProps, rObj)
{
}

TableProperties::~TableProperties()
{
}

BaseProperties& TableProperties::Clone(SdrObject& rObj) const
{
    return *(new TableProperties(*this, rObj));
}

void TableProperties::ItemChange(const sal_uInt16 nWhich, const SfxPoolItem* pNewItem)
{
    if( nWhich == SDRATTR_TEXTDIRECTION )
        AttributeProperties::ItemChange( nWhich, pNewItem );
    else
        TextProperties::ItemChange( nWhich, pNewItem );
}

// create a new itemset
SfxItemSet& TableProperties::CreateObjectSpecificItemSet(SfxItemPool& rPool)
{
    return *(new SfxItemSet(rPool,

        // range from SdrAttrObj
        SDRATTR_START, SDRATTR_SHADOW_LAST,
        SDRATTR_MISC_FIRST, SDRATTR_MISC_LAST,
        SDRATTR_TEXTDIRECTION, SDRATTR_TEXTDIRECTION,

        // range for SdrTableObj
        SDRATTR_TABLE_FIRST, SDRATTR_TABLE_LAST,

        // range from SdrTextObj
        EE_ITEMS_START, EE_ITEMS_END,

        // end
        0, 0));
}

class TableObjectGeoData : public SdrTextObjGeoData
{
public:
    Rectangle   maLogicRect;
};

//------------------------------------------------------------------------
// TableStyleSettings
//------------------------------------------------------------------------

TableStyleSettings::TableStyleSettings()
: mbUseFirstRow(true)
, mbUseLastRow(false)
, mbUseFirstColumn(false)
, mbUseLastColumn(false)
, mbUseRowBanding(true)
, mbUseColumnBanding(false)
{
}

TableStyleSettings::TableStyleSettings( const TableStyleSettings& rStyle )
{
    (*this) = rStyle;
}

TableStyleSettings& TableStyleSettings::operator=(const TableStyleSettings& rStyle)
{
    mbUseFirstRow = rStyle.mbUseFirstRow;
    mbUseLastRow = rStyle.mbUseLastRow;
    mbUseFirstColumn = rStyle.mbUseFirstColumn;
    mbUseLastColumn = rStyle.mbUseLastColumn;
    mbUseRowBanding = rStyle.mbUseRowBanding;
    mbUseColumnBanding = rStyle.mbUseColumnBanding;
    return *this;
}

bool TableStyleSettings::operator==( const TableStyleSettings& rStyle ) const
{
    return
        (mbUseFirstRow == rStyle.mbUseFirstRow) &&
        (mbUseLastRow == rStyle.mbUseLastRow) &&
        (mbUseFirstColumn == rStyle.mbUseFirstColumn) &&
        (mbUseLastColumn == rStyle.mbUseLastColumn) &&
        (mbUseRowBanding == rStyle.mbUseRowBanding) &&
        (mbUseColumnBanding == rStyle.mbUseColumnBanding);
}

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

class SdrTableObjImpl : public TableDesignUser, public ::cppu::WeakImplHelper1< ::com::sun::star::util::XModifyListener >
{
public:
    CellRef mxActiveCell;
    TableModelRef mxTable;
    SdrTableObj* mpTableObj;
    TableLayouter* mpLayouter;
    CellPos maEditPos;
    TableStyleSettings maTableStyle;
    Reference< XIndexAccess > mxTableStyle;
    bool mbModifyPending;
//  sal_Int32 mnSavedEditRowHeight;

    void SetModel(SdrModel* pOldModel, SdrModel* pNewModel);

    CellRef getCell( const CellPos& rPos ) const;
    void LayoutTable( Rectangle& rArea, bool bFitWidth, bool bFitHeight );

    bool ApplyCellStyles();
    void UpdateCells( Rectangle& rArea );

    SdrTableObjImpl();
    virtual ~SdrTableObjImpl();

    void init( SdrTableObj* pTable, sal_Int32 nColumns, sal_Int32 nRows );
    void dispose();

    sal_Int32 getColumnCount() const;
    sal_Int32 getRowCount() const;

    void DragEdge( bool mbHorizontal, int nEdge, sal_Int32 nOffset );

    const SfxPoolItem* GetCellItem( const CellPos& rPos, sal_uInt16 nWhich ) const;
//  void GetBorderLines( const CellPos& rPos, const SvxBorderLine** ppLeft, const SvxBorderLine** ppTop, const SvxBorderLine** ppRight, const SvxBorderLine** ppBottom ) const;

    void operator=( const SdrTableObjImpl& rSource );

    // XModifyListener
    virtual void SAL_CALL modified( const ::com::sun::star::lang::EventObject& aEvent ) throw (::com::sun::star::uno::RuntimeException);

    // XEventListener
    virtual void SAL_CALL disposing( const ::com::sun::star::lang::EventObject& Source ) throw (::com::sun::star::uno::RuntimeException);

    void update();

    void connectTableStyle();
    void disconnectTableStyle();
    virtual bool isInUse();

    bool UpdateWritingMode();
};

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

SdrTableObjImpl::SdrTableObjImpl()
: mpTableObj( 0 )
, mpLayouter( 0 )
{
}

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

SdrTableObjImpl::~SdrTableObjImpl()
{
}

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

void SdrTableObjImpl::init( SdrTableObj* pTable, sal_Int32 nColumns, sal_Int32 nRows )
{
    mpTableObj = pTable;
    mxTable = new TableModel( pTable );
    mxTable->init( nColumns, nRows );
    mpLayouter = new TableLayouter( mxTable );
    Reference< XModifyListener > xListener( static_cast< ::com::sun::star::util::XModifyListener* >(this) );
    mxTable->addModifyListener( xListener );
    UpdateWritingMode();
    LayoutTable( mpTableObj->aRect, true, true );
    mpTableObj->maLogicRect = mpTableObj->aRect;
}

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

void SdrTableObjImpl::operator=( const SdrTableObjImpl& rSource )
{
    if( mpLayouter )
    {
        delete mpLayouter;
        mpLayouter = 0;
    }

    if( mxTable.is() )
    {
        Reference< XModifyListener > xListener( static_cast< ::com::sun::star::util::XModifyListener* >(this) );
        mxTable->removeModifyListener( xListener );
        mxTable->dispose();
        mxTable.clear();
    }

    maTableStyle = rSource.maTableStyle;

    mxTable = new TableModel( mpTableObj, rSource.mxTable );
    mpLayouter = new TableLayouter( mxTable );
    Reference< XModifyListener > xListener( static_cast< ::com::sun::star::util::XModifyListener* >(this) );
    mxTable->addModifyListener( xListener );
    mxTableStyle = rSource.mxTableStyle;
    UpdateWritingMode();
    ApplyCellStyles();
    mpTableObj->aRect = mpTableObj->maLogicRect;
    LayoutTable( mpTableObj->aRect, false, false );
}

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

void SdrTableObjImpl::SetModel(SdrModel* /*pOldModel*/, SdrModel* pNewModel)
{
    // try to find new table style

    Reference< XIndexAccess > xNewTableStyle;
    if( mxTableStyle.is() ) try
    {
        const OUString sStyleName( Reference< XNamed >( mxTableStyle, UNO_QUERY_THROW )->getName() );

        Reference< XStyleFamiliesSupplier > xSFS( pNewModel->getUnoModel(), UNO_QUERY_THROW );
        Reference< XNameAccess > xFamilyNameAccess( xSFS->getStyleFamilies(), UNO_QUERY_THROW );
        const rtl::OUString sFamilyName( RTL_CONSTASCII_USTRINGPARAM( "table" ) );
        Reference< XNameAccess > xTableFamilyAccess( xFamilyNameAccess->getByName( sFamilyName ), UNO_QUERY_THROW );

        if( xTableFamilyAccess->hasByName( sStyleName ) )
        {
            // found table style with the same name
            xTableFamilyAccess->getByName( sStyleName ) >>= xNewTableStyle;
        }
        else
        {
            // copy or?
            Reference< XIndexAccess > xIndexAccess( xTableFamilyAccess, UNO_QUERY_THROW );
            xIndexAccess->getByIndex( 0 ) >>= xNewTableStyle;
        }
    }
    catch( Exception& )
    {
        DBG_ERROR("svx::SdrTableObjImpl::SetModel(), exception caught!");
    }

    mxTableStyle = xNewTableStyle;

    update();
}

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

bool SdrTableObjImpl::ApplyCellStyles()
{
    if( !mxTable.is() || !mxTable.is() || !mxTableStyle.is() )
        return false;

    bool bChanges = false;

    const sal_Int32 nColCount = getColumnCount();
    const sal_Int32 nRowCount = getRowCount();

    const TableStyleSettings& rStyle = maTableStyle;

    CellPos aPos;
    for( aPos.mnRow = 0; aPos.mnRow < nRowCount; ++aPos.mnRow )
    {
        const bool bFirstRow = (aPos.mnRow == 0) && rStyle.mbUseFirstRow;
        const bool bLastRow = (aPos.mnRow == nRowCount-1) && rStyle.mbUseLastRow;

        for( aPos.mnCol = 0; aPos.mnCol < nColCount; ++aPos.mnCol )
        {
            Reference< XStyle > xStyle;

            // first and last row win first, if used and available
            if( bFirstRow )
            {
                mxTableStyle->getByIndex(first_row_style) >>= xStyle;
            }
            else if( bLastRow )
            {
                mxTableStyle->getByIndex(last_row_style) >>= xStyle;
            }

            if( !xStyle.is() )
            {
                // next come first and last column, if used and available
                if( rStyle.mbUseFirstColumn && (aPos.mnCol == 0)  )
                {
                    mxTableStyle->getByIndex(first_column_style) >>= xStyle;
                }
                else if( rStyle.mbUseLastColumn && (aPos.mnCol == nColCount-1) )
                {
                    mxTableStyle->getByIndex(last_column_style) >>= xStyle;
                }
            }

            if( !xStyle.is() && rStyle.mbUseRowBanding )
            {
                if( (aPos.mnRow & 1) == 0 )
                {
                    mxTableStyle->getByIndex(even_rows_style) >>= xStyle;
                }
                else
                {
                    mxTableStyle->getByIndex(odd_rows_style) >>= xStyle;
                }
            }

            if( !xStyle.is() && rStyle.mbUseColumnBanding )
            {
                if( (aPos.mnCol & 1) == 0 )
                {
                    mxTableStyle->getByIndex(even_columns_style) >>= xStyle;
                }
                else
                {
                    mxTableStyle->getByIndex(odd_columns_style) >>= xStyle;
                }
            }

            if( !xStyle.is() )
            {
                // use default cell style if non found yet
                mxTableStyle->getByIndex(body_style) >>= xStyle;
            }


            if( xStyle.is() )
            {
                SfxUnoStyleSheet* pStyle = SfxUnoStyleSheet::getUnoStyleSheet(xStyle);

                if( pStyle )
                {
                    CellRef xCell( getCell( aPos ) );
                    if( xCell.is() && ( xCell->GetStyleSheet() != pStyle ) )
                    {
                        bChanges = true;
                        xCell->SetStyleSheet( pStyle, sal_True );
                    }
                }
            }
        }
    }

    return bChanges;
}

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

void SdrTableObjImpl::dispose()
{
    if( mxTable.is() )
        mxTable->dispose();
}

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

void SdrTableObjImpl::DragEdge( bool mbHorizontal, int nEdge, sal_Int32 nOffset )
{
    if( (nEdge > 0) && mxTable.is()) try
    {
        const OUString sSize( RTL_CONSTASCII_USTRINGPARAM( "Size" ) );
        nEdge--;
        if( mbHorizontal )
        {
            if( (nEdge >= 0) && (nEdge < getRowCount()) )
            {
                sal_Int32 nHeigth = mpLayouter->getRowHeight( nEdge );
                nHeigth += nOffset;
                Reference< XIndexAccess > xRows( mxTable->getRows(), UNO_QUERY_THROW );
                Reference< XPropertySet > xRowSet( xRows->getByIndex( nEdge ), UNO_QUERY_THROW );
                xRowSet->setPropertyValue( sSize, Any( nHeigth ) );
            }
        }
        else
        {
            if( (nEdge >= 0) && (nEdge < getColumnCount()) )
            {
                sal_Int32 nWidth = mpLayouter->getColumnWidth( nEdge );
                nWidth += nOffset;

                Reference< XIndexAccess > xCols( mxTable->getColumns(), UNO_QUERY_THROW );
                Reference< XPropertySet > xColSet( xCols->getByIndex( nEdge ), UNO_QUERY_THROW );
                xColSet->setPropertyValue( sSize, Any( nWidth ) );

                if( nEdge > 0 && nEdge < mxTable->getColumnCount() )
                {
                    const bool bRTL = mpLayouter->GetWritingMode() == WritingMode_RL_TB;

                    if( bRTL )
                        nEdge--;
                    else
                        nEdge++;

                    if( (bRTL && (nEdge >= 0)) || (!bRTL && (nEdge < mxTable->getColumnCount())) )
                    {
                        nWidth = mpLayouter->getColumnWidth( nEdge );
                        nWidth = std::max( (sal_Int32)(nWidth - nOffset), (sal_Int32)0 );

                        xColSet = Reference< XPropertySet >( xCols->getByIndex( nEdge ), UNO_QUERY_THROW );
                        xColSet->setPropertyValue( sSize, Any( nWidth ) );
                    }
                }
            }
        }
    }
    catch( Exception& )
    {
        DBG_ERROR( "svx::SdrTableObjImpl::DragEdge(), exception caught!" );
    }
}

// -----------------------------------------------------------------------------
// XModifyListener
// -----------------------------------------------------------------------------

void SAL_CALL SdrTableObjImpl::modified( const ::com::sun::star::lang::EventObject& /*aEvent*/ ) throw (::com::sun::star::uno::RuntimeException)
{
    update();
}

void SdrTableObjImpl::update()
{
    // source can be the table model itself or the assigned table template
    TableModelNotifyGuard aGuard( mxTable.get() );
    if( mpTableObj )
    {
        if( (maEditPos.mnRow >= getRowCount()) || (maEditPos.mnCol >= getColumnCount()) || (getCell( maEditPos ) != mxActiveCell) )
        {
            if(maEditPos.mnRow >= getRowCount())
                maEditPos.mnRow = getRowCount()-1;

            if(maEditPos.mnCol >= getColumnCount())
                maEditPos.mnCol = getColumnCount()-1;

            mpTableObj->setActiveCell( maEditPos );
        }

        ApplyCellStyles();

        mpTableObj->aRect = mpTableObj->maLogicRect;
        LayoutTable( mpTableObj->aRect, false, false );

        mpTableObj->SetRectsDirty();
        mpTableObj->ActionChanged();
        mpTableObj->BroadcastObjectChange();
    }
}

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

void SdrTableObjImpl::connectTableStyle()
{
    if( mxTableStyle.is() )
    {
        Reference< XModifyBroadcaster > xBroadcaster( mxTableStyle, UNO_QUERY );
        if( xBroadcaster.is() )
        {
            Reference< XModifyListener > xListener( static_cast< ::com::sun::star::util::XModifyListener* >(this) );
            xBroadcaster->addModifyListener( xListener );
        }
    }
}

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

void SdrTableObjImpl::disconnectTableStyle()
{
    if( mxTableStyle.is() )
    {
        Reference< XModifyBroadcaster > xBroadcaster( mxTableStyle, UNO_QUERY );
        if( xBroadcaster.is() )
        {
            Reference< XModifyListener > xListener( static_cast< ::com::sun::star::util::XModifyListener* >(this) );
            xBroadcaster->removeModifyListener( xListener );
        }
    }
}

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

bool SdrTableObjImpl::isInUse()
{
    return mpTableObj && mpTableObj->IsInserted();
}

// -----------------------------------------------------------------------------
// XEventListener
// -----------------------------------------------------------------------------

void SAL_CALL SdrTableObjImpl::disposing( const ::com::sun::star::lang::EventObject& /*Source*/ ) throw (::com::sun::star::uno::RuntimeException)
{
    mxActiveCell.clear();
    mxTable.clear();
    if( mpLayouter )
    {
        delete mpLayouter;
        mpLayouter = 0;
    }
    mpTableObj = 0;
}

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

CellRef SdrTableObjImpl::getCell(  const CellPos& rPos  ) const
{
    CellRef xCell;
    if( mxTable.is() ) try
    {
        xCell.set( dynamic_cast< Cell* >( mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
    }
    catch( Exception& )
    {
        DBG_ERROR( "svx::SdrTableObjImpl::getCell(), exception caught!" );
    }
    return xCell;
}

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

sal_Int32 SdrTableObjImpl::getColumnCount() const
{
    return mxTable.is() ? mxTable->getColumnCount() : 0;
}

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

sal_Int32 SdrTableObjImpl::getRowCount() const
{
    return mxTable.is() ? mxTable->getRowCount() : 0;
}

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

void SdrTableObjImpl::LayoutTable( Rectangle& rArea, bool bFitWidth, bool bFitHeight )
{
    if( mpLayouter && mpTableObj->GetModel() )
    {
        TableModelNotifyGuard aGuard( mxTable.get() );
        mpLayouter->LayoutTable( rArea, bFitWidth, bFitHeight );
    }
}

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

bool SdrTableObjImpl::UpdateWritingMode()
{
    if( mpTableObj && mpLayouter )
    {
        WritingMode eWritingMode = (WritingMode)static_cast< const SvxWritingModeItem& >( mpTableObj->GetObjectItem( SDRATTR_TEXTDIRECTION ) ).GetValue();

        if( eWritingMode != WritingMode_TB_RL )
        {
            if( static_cast< const SvxFrameDirectionItem& >( mpTableObj->GetObjectItem( EE_PARA_WRITINGDIR ) ).GetValue() == FRMDIR_HORI_LEFT_TOP )
                eWritingMode = WritingMode_LR_TB;
            else
                eWritingMode = WritingMode_RL_TB;
        }

        if( eWritingMode != mpLayouter->GetWritingMode() )
        {
            mpLayouter->SetWritingMode( eWritingMode );
            return true;
        }
    }
    return false;
}

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

void SdrTableObjImpl::UpdateCells( Rectangle& rArea )
{
    if( mpLayouter && mxTable.is() )
    {
        TableModelNotifyGuard aGuard( mxTable.get() );
        mpLayouter->updateCells( rArea );
        mxTable->setModified(sal_True);
    }
}

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

const SfxPoolItem* SdrTableObjImpl::GetCellItem( const CellPos& rPos, sal_uInt16 nWhich ) const
{
    CellRef xCell( getCell( rPos  ) );
    if( xCell.is() )
        return xCell->GetItemSet().GetItem( nWhich );
    else
        return 0;
}

// -----------------------------------------------------------------------------
// BaseProperties section
// -----------------------------------------------------------------------------

sdr::properties::BaseProperties* SdrTableObj::CreateObjectSpecificProperties()
{
    return new TableProperties(*this);
}

// -----------------------------------------------------------------------------
// DrawContact section
// -----------------------------------------------------------------------------

sdr::contact::ViewContact* SdrTableObj::CreateObjectSpecificViewContact()
{
    return new sdr::contact::ViewContactOfTableObj(*this);
}

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

TYPEINIT1(SdrTableObj,SdrTextObj);

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

SdrTableObj::SdrTableObj(SdrModel* _pModel)
{
    pModel = _pModel;
    init( 1, 1 );
}

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

SdrTableObj::SdrTableObj(SdrModel* _pModel, const ::Rectangle& rNewRect, sal_Int32 nColumns, sal_Int32 nRows)
: SdrTextObj( rNewRect )
, maLogicRect( rNewRect )
{
    pModel = _pModel;

    if( nColumns <= 0 )
        nColumns = 1;

    if( nRows <= 0 )
        nRows = 1;

    init( nColumns, nRows );
}

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

void SdrTableObj::init( sal_Int32 nColumns, sal_Int32 nRows )
{
    bClosedObj = TRUE;

    mpImpl = new SdrTableObjImpl;
    mpImpl->acquire();
    mpImpl->init( this, nColumns, nRows );
}

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

SdrTableObj::~SdrTableObj()
{
    mpImpl->dispose();
    mpImpl->release();
}

// --------------------------------------------------------------------
// table stuff
// --------------------------------------------------------------------

Reference< XTable > SdrTableObj::getTable() const
{
    return Reference< XTable >( mpImpl->mxTable.get() );
}

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

bool SdrTableObj::isValid( const CellPos& rPos ) const
{
    return (rPos.mnCol >= 0) && (rPos.mnCol < mpImpl->getColumnCount()) && (rPos.mnRow >= 0) && (rPos.mnRow < mpImpl->getRowCount());
}

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

CellPos SdrTableObj::getFirstCell() const
{
    return CellPos( 0,0 );
}

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

CellPos SdrTableObj::getLastCell() const
{
    CellPos aPos;
    if( mpImpl->mxTable.is() )
    {
        aPos.mnCol = mpImpl->getColumnCount()-1;
        aPos.mnRow = mpImpl->getRowCount()-1;
    }
    return aPos;
}

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

CellPos SdrTableObj::getLeftCell( const CellPos& rPos, bool bEdgeTravel ) const
{
    switch( GetWritingMode() )
    {
    default:
    case WritingMode_LR_TB:
        return getPreviousCell( rPos, bEdgeTravel );
    case WritingMode_RL_TB:
        return getNextCell( rPos, bEdgeTravel );
    case WritingMode_TB_RL:
        return getPreviousRow( rPos, bEdgeTravel );
    }
}

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

CellPos SdrTableObj::getRightCell( const CellPos& rPos, bool bEdgeTravel  ) const
{
    switch( GetWritingMode() )
    {
    default:
    case WritingMode_LR_TB:
        return getNextCell( rPos, bEdgeTravel );
    case WritingMode_RL_TB:
        return getPreviousCell( rPos, bEdgeTravel );
    case WritingMode_TB_RL:
        return getNextRow( rPos, bEdgeTravel );
    }
}

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

CellPos SdrTableObj::getUpCell( const CellPos& rPos, bool bEdgeTravel ) const
{
    switch( GetWritingMode() )
    {
    default:
    case WritingMode_LR_TB:
    case WritingMode_RL_TB:
        return getPreviousRow( rPos, bEdgeTravel );
    case WritingMode_TB_RL:
        return getPreviousCell( rPos, bEdgeTravel );
    }
}

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

CellPos SdrTableObj::getDownCell( const CellPos& rPos, bool bEdgeTravel ) const
{
    switch( GetWritingMode() )
    {
    default:
    case WritingMode_LR_TB:
    case WritingMode_RL_TB:
        return getNextRow( rPos, bEdgeTravel );
    case WritingMode_TB_RL:
        return getNextCell( rPos, bEdgeTravel );
    }
}

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

CellPos SdrTableObj::getPreviousCell( const CellPos& rPos, bool bEdgeTravel ) const
{
    CellPos aPos( rPos );
    if( mpImpl )
    {
        CellRef xCell( mpImpl->getCell( aPos ) );
        if( xCell.is() && xCell->isMerged() )
        {
            sal_Int32 nTemp = 0;
            findMergeOrigin( mpImpl->mxTable.get(), aPos.mnCol, aPos.mnRow, aPos.mnCol, nTemp );
        }

        if( aPos.mnCol > 0 )
        {
            --aPos.mnCol;
        }

        else if( bEdgeTravel && (aPos.mnRow > 0) )
        {
            aPos.mnCol = mpImpl->mxTable->getColumnCount()-1;
            --aPos.mnRow;
        }
    }
    return aPos;
}

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

CellPos SdrTableObj::getNextCell( const CellPos& rPos, bool bEdgeTravel ) const
{
    CellPos aPos( rPos );
    if( mpImpl )
    {
        CellRef xCell( mpImpl->getCell( aPos ) );
        if( xCell.is() )
        {
            if( xCell->isMerged() )
            {
                findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, aPos.mnCol, aPos.mnRow );

                xCell = mpImpl->getCell(aPos);

                if( xCell.is() )
                {
                    aPos.mnCol += xCell->getColumnSpan();
                    aPos.mnRow = rPos.mnRow;
                }
            }
            else
            {
                aPos.mnCol += xCell->getColumnSpan();
            }

            if( aPos.mnCol < mpImpl->mxTable->getColumnCount() )
                return aPos;

            if( bEdgeTravel && ((aPos.mnRow + 1) < mpImpl->getRowCount()) )
            {
                aPos.mnCol = 0;
                aPos.mnRow += 1;
                return aPos;
            }
        }
    }

    // last cell reached, no traveling possible
    return rPos;
}

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

CellPos SdrTableObj::getPreviousRow( const CellPos& rPos, bool bEdgeTravel ) const
{
    CellPos aPos( rPos );
    if( mpImpl )
    {
        CellRef xCell( mpImpl->getCell( aPos ) );
        if( xCell.is() )
        {
            if( xCell->isMerged() )
            {
                sal_Int32 nTemp = 0;
                findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, nTemp, aPos.mnRow );
            }
        }

        if( aPos.mnRow > 0 )
        {
            --aPos.mnRow;
        }
        else if( bEdgeTravel && (aPos.mnCol > 0) )
        {
            aPos.mnRow = mpImpl->mxTable->getRowCount()-1;
            --aPos.mnCol;
        }
    }
    return aPos;
}

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

CellPos SdrTableObj::getNextRow( const CellPos& rPos, bool bEdgeTravel ) const
{
    CellPos aPos( rPos );

    if( mpImpl )
    {
        CellRef xCell( mpImpl->getCell( rPos ) );
        if( xCell.is() )
        {
            if( xCell->isMerged() )
            {
                findMergeOrigin( mpImpl->mxTable, aPos.mnCol, aPos.mnRow, aPos.mnCol, aPos.mnRow );
                xCell = mpImpl->getCell(aPos);
                aPos.mnCol = rPos.mnCol;
            }

            if( xCell.is() )
                aPos.mnRow += xCell->getRowSpan();

            if( aPos.mnRow < mpImpl->mxTable->getRowCount() )
                return aPos;

            if( bEdgeTravel && (aPos.mnCol + 1) < mpImpl->mxTable->getColumnCount() )
            {
                aPos.mnRow = 0;
                aPos.mnCol += 1;

                while( aPos.mnCol < mpImpl->mxTable->getColumnCount() )
                {
                    xCell = mpImpl->getCell( aPos );
                    if( xCell.is() && !xCell->isMerged() )
                        return aPos;
                    aPos.mnCol += 1;
                }
            }
        }
    }

    // last position reached, no more traveling possible
    return rPos;
}

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

const TableStyleSettings& SdrTableObj::getTableStyleSettings() const
{
    if( mpImpl )
    {
        return mpImpl->maTableStyle;
    }
    else
    {
        static TableStyleSettings aTmp;
        return aTmp;
    }
}

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

void SdrTableObj::setTableStyleSettings( const TableStyleSettings& rStyle )
{
    if( mpImpl )
    {
        mpImpl->maTableStyle = rStyle;
        mpImpl->update();
    }
}

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

TableHitKind SdrTableObj::CheckTableHit( const Point& rPos, sal_Int32& rnX, sal_Int32& rnY, int nTol ) const
{
    if( !mpImpl || !mpImpl->mxTable.is() )
        return SDRTABLEHIT_NONE;

    rnX = 0;
    rnY = 0;

    const sal_Int32 nColCount = mpImpl->getColumnCount();
    const sal_Int32 nRowCount = mpImpl->getRowCount();

    sal_Int32 nX = rPos.X() + nTol - aRect.nLeft;
    sal_Int32 nY = rPos.Y() + nTol - aRect.nTop;

    if( (nX < 0) || (nX > (aRect.GetWidth() + nTol)) || (nY < 0) || (nY > (aRect.GetHeight() + nTol) ) )
        return SDRTABLEHIT_NONE;

    // get vertical edge number and check for a hit
    const bool bRTL = GetWritingMode() == WritingMode_RL_TB;
    bool bVrtHit = false;
    if( nX >= 0 )
    {
        if( !bRTL )
        {
            while( rnX <= nColCount )
            {
                if( nX <= (2*nTol) )
                {
                    bVrtHit = true;
                    break;
                }

                if( rnX == nColCount )
                    break;

                nX -= mpImpl->mpLayouter->getColumnWidth( rnX );
                if( nX < 0 )
                    break;
                rnX++;
            }
        }
        else
        {
            rnX = nColCount;
            while( rnX >= 0 )
            {
                if( nX <= (2*nTol) )
                {
                    bVrtHit = true;
                    break;
                }

                if( rnX == 0 )
                    break;

                rnX--;
                nX -= mpImpl->mpLayouter->getColumnWidth( rnX );
                if( nX < 0 )
                    break;
            }
        }
    }

    // rnX is now the edge number left to the pointer, if it was hit bHrzHit is also true

    // get vertical edge number and check for a hit
    bool bHrzHit = false;
    if( nY >= 0 )
    {
        while( rnY <= nRowCount )
        {
            if( nY <= (2*nTol) )
            {
                bHrzHit = true;
                break;
            }

            if( rnY == nRowCount )
                break;

            nY -= mpImpl->mpLayouter->getRowHeight(rnY);
            if( nY < 0 )
                break;
            rnY++;
        }
    }

    // rnY is now the edge number above the pointer, if it was hit bVrtHit is also true

    if( bVrtHit && mpImpl->mpLayouter->isEdgeVisible( rnX, rnY, false ) )
        return SDRTABLEHIT_VERTICAL_BORDER;

    if( bHrzHit && mpImpl->mpLayouter->isEdgeVisible( rnX, rnY, true ) )
        return SDRTABLEHIT_HORIZONTAL_BORDER;

    CellRef xCell( mpImpl->getCell( CellPos( rnX, rnY ) ) );
    if( xCell.is() && xCell->isMerged() )
        findMergeOrigin( mpImpl->mxTable.get(), rnX, rnY, rnX, rnY );

    if( xCell.is() )
    {
        nX += mpImpl->mpLayouter->getColumnWidth( rnX );
        if( nX < xCell->GetTextLeftDistance() )
            return SDRTABLEHIT_CELL;
    }

    return SDRTABLEHIT_CELLTEXTAREA;
}

const SfxItemSet& SdrTableObj::GetActiveCellItemSet() const
{
    return getActiveCell()->GetItemSet();
}

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

void SdrTableObj::InsertRows( sal_Int32 nIndex, sal_Int32 nCount /*= 1*/ )
{
    if( mpImpl->mxTable.is() ) try
    {
        Reference< XTableRows > xRows( mpImpl->mxTable->getRows(), UNO_QUERY_THROW );
        xRows->insertByIndex( nIndex, nCount );
    }
    catch( Exception& )
    {
        DBG_ERROR("SdrTableObj::InsertRows(), exception caught!");
    }
}

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

void SdrTableObj::InsertColumns( sal_Int32 nIndex, sal_Int32 nCount /*= 1*/ )
{
    if( mpImpl->mxTable.is() ) try
    {
        Reference< XTableColumns > xColumns( mpImpl->mxTable->getColumns(), UNO_QUERY_THROW );
        xColumns->insertByIndex( nIndex, nCount );
    }
    catch( Exception& )
    {
        DBG_ERROR("SdrTableObj::InsertColumns(), exception caught!");
    }
}

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

void SdrTableObj::DeleteRows( sal_Int32 nIndex, sal_Int32 nCount /*= 1*/ )
{
    if( mpImpl->mxTable.is() ) try
    {
        Reference< XTableRows > xRows( mpImpl->mxTable->getRows(), UNO_QUERY_THROW );
        xRows->removeByIndex( nIndex, nCount );
    }
    catch( Exception& )
    {
        DBG_ERROR("SdrTableObj::DeleteRows(), exception caught!");
    }
}

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

void SdrTableObj::DeleteColumns( sal_Int32 nIndex, sal_Int32 nCount /*= 1*/ )
{
    if( mpImpl->mxTable.is() ) try
    {
        Reference< XTableColumns > xColumns( mpImpl->mxTable->getColumns(), UNO_QUERY_THROW );
        xColumns->removeByIndex( nIndex, nCount );
    }
    catch( Exception& )
    {
        DBG_ERROR("SdrTableObj::DeleteColumns(), exception caught!");
    }
}

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

void SdrTableObj::setTableStyle( const Reference< XIndexAccess >& xTableStyle )
{
    if( mpImpl && (mpImpl->mxTableStyle != xTableStyle) )
    {
        mpImpl->disconnectTableStyle();
        mpImpl->mxTableStyle = xTableStyle;
        mpImpl->connectTableStyle();
        mpImpl->update();
    }
}

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

const Reference< XIndexAccess >& SdrTableObj::getTableStyle() const
{
    if( mpImpl )
    {
        return mpImpl->mxTableStyle;
    }
    else
    {
        static Reference< XIndexAccess > aTmp;
        return aTmp;
    }
}

// --------------------------------------------------------------------
// text stuff
// --------------------------------------------------------------------

/** returns the currently active text. */
SdrText* SdrTableObj::getActiveText() const
{
    return dynamic_cast< SdrText* >( getActiveCell().get() );
}

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

/** returns the nth available text. */
SdrText* SdrTableObj::getText( sal_Int32 nIndex ) const
{
    if( mpImpl->mxTable.is() )
    {
        const sal_Int32 nColCount = mpImpl->getColumnCount();
        if( nColCount )
        {
            CellPos aPos( nIndex % nColCount, nIndex / nColCount );

            CellRef xCell( mpImpl->getCell( aPos ) );
            return dynamic_cast< SdrText* >( xCell.get() );
        }
    }
    return 0;
}

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

/** returns the number of texts available for this object. */
sal_Int32 SdrTableObj::getTextCount() const
{
    if( mpImpl->mxTable.is() )
    {
        const sal_Int32 nColCount = mpImpl->getColumnCount();
        const sal_Int32 nRowCount = mpImpl->getRowCount();

        return nColCount * nRowCount;
    }
    else
    {
        return 0;
    }
}

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

/** changes the current active text */
void SdrTableObj::setActiveText( sal_Int32 nIndex )
{
    if( mpImpl && mpImpl->mxTable.is() )
    {
        const sal_Int32 nColCount = mpImpl->mxTable->getColumnCount();
        if( nColCount )
        {
            CellPos aPos( nIndex % nColCount, nIndex / nColCount );
            if( isValid( aPos ) )
                setActiveCell( aPos );
        }
    }
}

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

/** returns the index of the text that contains the given point or -1 */
sal_Int32 SdrTableObj::CheckTextHit(const Point& rPnt) const
{
    if( mpImpl && mpImpl->mxTable.is() )
    {
        CellPos aPos;
        if( CheckTableHit( rPnt, aPos.mnCol, aPos.mnRow, 0 ) == SDRTABLEHIT_CELLTEXTAREA )
            return aPos.mnRow * mpImpl->mxTable->getColumnCount() + aPos.mnCol;
    }

    return 0;
}

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

SdrOutliner* SdrTableObj::GetCellTextEditOutliner( const Cell& rCell ) const
{
    if( mpImpl && (mpImpl->getCell( mpImpl->maEditPos ).get() == &rCell) )
        return pEdtOutl;
    else
        return 0;
}


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

const TableLayouter& SdrTableObj::getTableLayouter() const
{
    OSL_ENSURE(mpImpl && mpImpl->mpLayouter, "getTableLayouter() error: no mpImpl or mpLayouter (!)");
    return *(mpImpl->mpLayouter);
}

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

void SdrTableObj::FitFrameToTextSize()
{
    // todo
}

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

FASTBOOL SdrTableObj::IsAutoGrowHeight() const
{
    return TRUE;
}

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

FASTBOOL SdrTableObj::IsAutoGrowWidth() const
{
    return TRUE;
}

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

bool SdrTableObj::HasText() const
{
    return true;
}

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

bool SdrTableObj::IsTextEditActive( const CellPos& rPos )
{
    return pEdtOutl && mpImpl && (rPos == mpImpl->maEditPos);
}

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

void SdrTableObj::onEditOutlinerStatusEvent( EditStatus* pEditStatus )
{
    if( (pEditStatus->GetStatusWord() & EE_STAT_TEXTHEIGHTCHANGED) && mpImpl && mpImpl->mpLayouter )
    {
        Rectangle aRect0( aRect );
        aRect = maLogicRect;
//      mpImpl->mpLayouter->setRowHeight( mpImpl->maEditPos.mnRow, mpImpl->mnSavedEditRowHeight );
        mpImpl->LayoutTable( aRect, false, false );
        SetRectsDirty();
        ActionChanged();
        BroadcastObjectChange();
        if( aRect0 != aRect )
            SendUserCall(SDRUSERCALL_RESIZE,aRect0);
    }
}

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

void SdrTableObj::TakeObjInfo(SdrObjTransformInfoRec& rInfo) const
{
    rInfo.bResizeFreeAllowed=TRUE;
    rInfo.bResizePropAllowed=TRUE;
    rInfo.bRotateFreeAllowed=FALSE;
    rInfo.bRotate90Allowed  =FALSE;
    rInfo.bMirrorFreeAllowed=FALSE;
    rInfo.bMirror45Allowed  =FALSE;
    rInfo.bMirror90Allowed  =FALSE;

    // allow transparence
    rInfo.bTransparenceAllowed = TRUE;

    // gradient depends on fillstyle
    XFillStyle eFillStyle = ((XFillStyleItem&)(GetObjectItem(XATTR_FILLSTYLE))).GetValue();
    rInfo.bGradientAllowed = (eFillStyle == XFILL_GRADIENT);
    rInfo.bShearAllowed     =FALSE;
    rInfo.bEdgeRadiusAllowed=FALSE;
    rInfo.bCanConvToPath    =FALSE;
    rInfo.bCanConvToPoly    =FALSE;
    rInfo.bCanConvToPathLineToArea=FALSE;
    rInfo.bCanConvToPolyLineToArea=FALSE;
    rInfo.bCanConvToContour = FALSE;
}

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

UINT16 SdrTableObj::GetObjIdentifier() const
{
    return static_cast<UINT16>(OBJ_TABLE);
}

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

void SdrTableObj::SetPage(SdrPage* pNewPage)
{
    SdrTextObj::SetPage(pNewPage);
}

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

void SdrTableObj::SetModel(SdrModel* pNewModel)
{
    SdrModel* pOldModel = GetModel();
    if( pNewModel != pOldModel )
    {
        SdrTextObj::SetModel(pNewModel);

        if( mpImpl )
        {
            mpImpl->SetModel( pOldModel, pNewModel );

            if( !maLogicRect.IsEmpty() )
            {
                aRect = maLogicRect;
                mpImpl->LayoutTable( aRect, false, false );
            }
        }
    }
}

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

void SdrTableObj::TakeTextRect( SdrOutliner& rOutliner, Rectangle& rTextRect, FASTBOOL bNoEditText, Rectangle* pAnchorRect, BOOL bLineWidth ) const
{
    if( mpImpl )
        TakeTextRect( mpImpl->maEditPos, rOutliner, rTextRect, bNoEditText, pAnchorRect, bLineWidth );
}

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

void SdrTableObj::TakeTextRect( const CellPos& rPos, SdrOutliner& rOutliner, Rectangle& rTextRect, FASTBOOL bNoEditText, Rectangle* pAnchorRect, BOOL /*bLineWidth*/ ) const
{
    if( !mpImpl )
        return;

    CellRef xCell( mpImpl->getCell( rPos ) );
    if( !xCell.is() )
        return;

    Rectangle aAnkRect;
    TakeTextAnchorRect( rPos, aAnkRect );

    SdrTextVertAdjust eVAdj=xCell->GetTextVerticalAdjust();
//  SdrTextHorzAdjust eHAdj=xCell->GetTextHorizontalAdjust();

    ULONG nStat0=rOutliner.GetControlWord();
    Size aNullSize;
    nStat0 |= EE_CNTRL_AUTOPAGESIZE;
    rOutliner.SetControlWord(nStat0);
    rOutliner.SetMinAutoPaperSize(aNullSize);
    rOutliner.SetMaxAutoPaperSize(aAnkRect.GetSize());
    rOutliner.SetPaperSize(aAnkRect.GetSize());

    // #103516# New try with _BLOCK for hor and ver after completely
    // supporting full width for vertical text.
//  if( SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting())
//  {
        rOutliner.SetMinAutoPaperSize(Size(aAnkRect.GetWidth(), 0));
//  }
//  else if(SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting())
//  {
//      rOutliner.SetMinAutoPaperSize(Size(0, aAnkRect.GetHeight()));
//  }

    // ---

    // set text at outliner, maybe from edit outliner
    OutlinerParaObject* pPara= xCell->GetOutlinerParaObject();
    if (pEdtOutl && !bNoEditText && mpImpl->mxActiveCell == xCell )
        pPara=pEdtOutl->CreateParaObject();

    if (pPara)
    {
        const bool bHitTest = pModel && (&pModel->GetHitTestOutliner() == &rOutliner);

        const SdrTextObj* pTestObj = rOutliner.GetTextObj();
        if( !pTestObj || !bHitTest || (pTestObj != this) || (pTestObj->GetOutlinerParaObject() != xCell->GetOutlinerParaObject()) )
        {
            if( bHitTest ) // #i33696# take back fix #i27510#
                rOutliner.SetTextObj( this );

            rOutliner.SetUpdateMode(TRUE);
            rOutliner.SetText(*pPara);
        }
    }
    else
    {
        rOutliner.SetTextObj( NULL );
    }

    if (pEdtOutl && !bNoEditText && pPara && mpImpl->mxActiveCell == xCell )
        delete pPara;

    rOutliner.SetUpdateMode(TRUE);
    rOutliner.SetControlWord(nStat0);

    Point aTextPos(aAnkRect.TopLeft());
    Size aTextSiz(rOutliner.GetPaperSize());
/*
    if (eHAdj==SDRTEXTHORZADJUST_CENTER || eHAdj==SDRTEXTHORZADJUST_RIGHT)
    {
        long nFreeWdt=aAnkRect.GetWidth()-aTextSiz.Width();
        if (eHAdj==SDRTEXTHORZADJUST_CENTER)
            aTextPos.X()+=nFreeWdt/2;
        if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
            aTextPos.X()+=nFreeWdt;
    }
*/
    if (eVAdj==SDRTEXTVERTADJUST_CENTER || eVAdj==SDRTEXTVERTADJUST_BOTTOM)
    {
        long nFreeHgt=aAnkRect.GetHeight()-aTextSiz.Height();
        if (eVAdj==SDRTEXTVERTADJUST_CENTER)
            aTextPos.Y()+=nFreeHgt/2;
        if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
            aTextPos.Y()+=nFreeHgt;
    }

    if (pAnchorRect)
        *pAnchorRect=aAnkRect;

    rTextRect=Rectangle(aTextPos,aTextSiz);
}

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

const CellRef& SdrTableObj::getActiveCell() const
{
    if( mpImpl )
    {
        if( !mpImpl->mxActiveCell.is() )
        {
            CellPos aPos;
            const_cast< SdrTableObj* >(this)->setActiveCell( aPos );
        }
        return mpImpl->mxActiveCell;
    }
    else
    {
        static CellRef xCell;
        return xCell;
    }
}

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

sal_Int32 SdrTableObj::getRowCount() const
{
    return mpImpl ? mpImpl->getRowCount() : 0;
}

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

sal_Int32 SdrTableObj::getColumnCount() const
{
    return mpImpl ? mpImpl->getColumnCount() : 0;
}

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

void SdrTableObj::setActiveCell( const CellPos& rPos )
{
    if( mpImpl && mpImpl->mxTable.is() ) try
    {
        mpImpl->mxActiveCell.set( dynamic_cast< Cell* >( mpImpl->mxTable->getCellByPosition( rPos.mnCol, rPos.mnRow ).get() ) );
        if( mpImpl->mxActiveCell.is() && mpImpl->mxActiveCell->isMerged() )
        {
            CellPos aOrigin;
            findMergeOrigin( mpImpl->mxTable.get(), rPos.mnCol, rPos.mnRow, aOrigin.mnCol, aOrigin.mnRow );
            mpImpl->mxActiveCell.set( dynamic_cast< Cell* >( mpImpl->mxTable->getCellByPosition( aOrigin.mnCol, aOrigin.mnRow ).get() ) );
            mpImpl->maEditPos = aOrigin;
        }
        else
        {
            mpImpl->maEditPos = rPos;
        }
    }
    catch( Exception& )
    {
        DBG_ERROR("SdrTableObj::setActiveCell(), exception caught!");
    }
}

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

void SdrTableObj::getActiveCellPos( CellPos& rPos ) const
{
    rPos = mpImpl->maEditPos;
}

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

void SdrTableObj::getCellBounds( const CellPos& rPos, ::Rectangle& rCellRect )
{
    if( mpImpl )
    {
        CellRef xCell( mpImpl->getCell( rPos ) );
        if( xCell.is() )
            rCellRect = xCell->getCellRect();
    }
}

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

void SdrTableObj::TakeTextAnchorRect(Rectangle& rAnchorRect) const
{
    if( mpImpl )
        TakeTextAnchorRect( mpImpl->maEditPos, rAnchorRect );
}

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

void SdrTableObj::TakeTextAnchorRect( const CellPos& rPos, Rectangle& rAnchorRect ) const
{
    Rectangle aAnkRect(aRect);

    if( mpImpl )
    {
        CellRef xCell( mpImpl->getCell( rPos ) );
        if( xCell.is() )
            xCell->TakeTextAnchorRect( aAnkRect );
    }

    ImpJustifyRect(aAnkRect);
    rAnchorRect=aAnkRect;
}

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

void SdrTableObj::TakeTextEditArea(Size* pPaperMin, Size* pPaperMax, Rectangle* pViewInit, Rectangle* pViewMin) const
{
    if( mpImpl )
        TakeTextEditArea( mpImpl->maEditPos, pPaperMin, pPaperMax, pViewInit, pViewMin );
}

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

void SdrTableObj::TakeTextEditArea( const CellPos& rPos, Size* pPaperMin, Size* pPaperMax, Rectangle* pViewInit, Rectangle* pViewMin ) const
{
    Size aPaperMin,aPaperMax;
    Rectangle aViewInit;
    TakeTextAnchorRect( rPos, aViewInit );

    Size aAnkSiz(aViewInit.GetSize());
    aAnkSiz.Width()--; aAnkSiz.Height()--; // weil GetSize() ein draufaddiert

    Size aMaxSiz(aAnkSiz.Width(),1000000);
    if (pModel!=NULL)
    {
        Size aTmpSiz(pModel->GetMaxObjSize());
        if (aTmpSiz.Height()!=0)
            aMaxSiz.Height()=aTmpSiz.Height();
    }

    CellRef xCell( mpImpl->getCell( rPos ) );
    SdrTextVertAdjust eVAdj = xCell.is() ? xCell->GetTextVerticalAdjust() : SDRTEXTVERTADJUST_TOP;
//  SdrTextHorzAdjust eHAdj = xCell.is() ? xCell->GetTextHorizontalAdjust() : SDRTEXTHORZADJUST_LEFT;

    aPaperMax=aMaxSiz;

//  if((SDRTEXTHORZADJUST_BLOCK == eHAdj && !IsVerticalWriting()) || (SDRTEXTVERTADJUST_BLOCK == eVAdj && IsVerticalWriting()))
        aPaperMin.Width() = aAnkSiz.Width();

    if (pViewMin!=NULL)
    {
        *pViewMin=aViewInit;
/*
        long nXFree=aAnkSiz.Width()-aPaperMin.Width();

        if (eHAdj==SDRTEXTHORZADJUST_LEFT)
        {
            pViewMin->Right()-=nXFree;
        }
        else if (eHAdj==SDRTEXTHORZADJUST_RIGHT)
        {
            pViewMin->Left()+=nXFree;
        }
        else
        {
            pViewMin->Left()+=nXFree/2;
            pViewMin->Right()=pViewMin->Left()+aPaperMin.Width();
        }
*/
        long nYFree=aAnkSiz.Height()-aPaperMin.Height();

        if (eVAdj==SDRTEXTVERTADJUST_TOP)
        {
            pViewMin->Bottom()-=nYFree;
        }
        else if (eVAdj==SDRTEXTVERTADJUST_BOTTOM)
        {
            pViewMin->Top()+=nYFree;
        }
        else
        {
            pViewMin->Top()+=nYFree/2;
            pViewMin->Bottom()=pViewMin->Top()+aPaperMin.Height();
        }
    }


    if(IsVerticalWriting())
        aPaperMin.Width() = 0;
    else
        aPaperMin.Height() = 0;

    if (pPaperMin!=NULL) *pPaperMin=aPaperMin;
    if (pPaperMax!=NULL) *pPaperMax=aPaperMax;
    if (pViewInit!=NULL) *pViewInit=aViewInit;
}

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

USHORT SdrTableObj::GetOutlinerViewAnchorMode() const
{
    EVAnchorMode eRet=ANCHOR_TOP_LEFT;
    CellRef xCell( getActiveCell() );
    if( xCell.is() )
    {
        SdrTextVertAdjust eV=xCell->GetTextVerticalAdjust();
//      SdrTextHorzAdjust eH=xCell->GetTextHorizontalAdjust();

//      if (eH==SDRTEXTHORZADJUST_LEFT)
        {
            if (eV==SDRTEXTVERTADJUST_TOP)
            {
                eRet=ANCHOR_TOP_LEFT;
            }
            else if (eV==SDRTEXTVERTADJUST_BOTTOM)
            {
                eRet=ANCHOR_BOTTOM_LEFT;
            }
            else
            {
                eRet=ANCHOR_VCENTER_LEFT;
            }
        }
/*
        else if (eH==SDRTEXTHORZADJUST_RIGHT)
        {
            if (eV==SDRTEXTVERTADJUST_TOP)
            {
                eRet=ANCHOR_TOP_RIGHT;
            }
            else if (eV==SDRTEXTVERTADJUST_BOTTOM)
            {
                eRet=ANCHOR_BOTTOM_RIGHT;
            }
            else
            {
                eRet=ANCHOR_VCENTER_RIGHT;
            }
        }
        else
        {
            if (eV==SDRTEXTVERTADJUST_TOP)
            {
                eRet=ANCHOR_TOP_HCENTER;
            }
            else if (eV==SDRTEXTVERTADJUST_BOTTOM)
            {
                eRet=ANCHOR_BOTTOM_HCENTER;
            }
            else
            {
                eRet=ANCHOR_VCENTER_HCENTER;
            }
        }
*/
    }
    return (USHORT)eRet;
}

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

OutlinerParaObject* SdrTableObj::GetEditOutlinerParaObject() const
{
    return SdrTextObj::GetEditOutlinerParaObject();
}

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

SdrOutliner* SdrTableObj::GetCellTextEditOutliner( const CellPos& rPos ) const
{
    if( pEdtOutl && mpImpl && (mpImpl->maEditPos == rPos) )
        return pEdtOutl;
    else
        return 0;
}

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

struct ImplTableShadowPaintInfo
{
    Color maShadowColor;
    sal_uInt32 mnXDistance;
    sal_uInt32 mnYDistance;
    sal_uInt16 mnShadowTransparence;

    ImplTableShadowPaintInfo( const SfxItemSet& rSet )
    {
        const SdrShadowColorItem& rShadColItem = ((const SdrShadowColorItem&)(rSet.Get(SDRATTR_SHADOWCOLOR)));
        maShadowColor = rShadColItem.GetColorValue();
        mnShadowTransparence = ((const SdrShadowTransparenceItem&)(rSet.Get(SDRATTR_SHADOWTRANSPARENCE))).GetValue();

        mnXDistance = ((SdrShadowXDistItem&)(rSet.Get(SDRATTR_SHADOWXDIST))).GetValue();
        mnYDistance = ((SdrShadowYDistItem&)(rSet.Get(SDRATTR_SHADOWYDIST))).GetValue();
    }
};

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

void lcl_VertLineEnds( OutputDevice& rDev, const Point& rTop, const Point& rBottom,
        const Color& rColor, long nXOffs, long nWidth,
        const svx::frame::Style& rTopLine, const svx::frame::Style& rBottomLine )
{
    rDev.SetLineColor(rColor);              // PEN_NULL ???
    rDev.SetFillColor(rColor);

    //  Position oben/unten muss unabhaengig von der Liniendicke sein,
    //  damit der Winkel stimmt (oder X-Position auch anpassen)
    long nTopPos = rTop.Y();
    long nBotPos = rBottom.Y();

    long nTopLeft = rTop.X() + nXOffs;
    long nTopRight = nTopLeft + nWidth - 1;

    long nBotLeft = rBottom.X() + nXOffs;
    long nBotRight = nBotLeft + nWidth - 1;

    //  oben abschliessen

    if ( rTopLine.Prim() )
    {
        long nLineW = rTopLine.GetWidth();
        if (nLineW >= 2)
        {
            Point aTriangle[3];
            aTriangle[0] = Point( nTopLeft, nTopPos );      // wie aPoints[0]
            aTriangle[1] = Point( nTopRight, nTopPos );     // wie aPoints[1]
            aTriangle[2] = Point( rTop.X(), nTopPos - (nLineW - 1) / 2 );
            Polygon aTriPoly( 3, aTriangle );
            rDev.DrawPolygon( aTriPoly );
        }
    }

    //  unten abschliessen

    if ( rBottomLine.Prim() )
    {
        long nLineW = rBottomLine.GetWidth();
        if (nLineW >= 2)
        {
            Point aTriangle[3];
            aTriangle[0] = Point( nBotLeft, nBotPos );      // wie aPoints[3]
            aTriangle[1] = Point( nBotRight, nBotPos );     // wie aPoints[2]
            aTriangle[2] = Point( rBottom.X(), nBotPos - (nLineW - 1) / 2 + nLineW - 1 );
            Polygon aTriPoly( 3, aTriangle );
            rDev.DrawPolygon( aTriPoly );
        }
    }
}

void lcl_VertLine( OutputDevice& rDev, const Point& rTop, const Point& rBottom,
                    const svx::frame::Style& rLine,
                    const svx::frame::Style& rTopLine, const svx::frame::Style& rBottomLine,
                    const Color* pForceColor )
{
    if( rLine.Prim() )
    {
        svx::frame::DrawVerFrameBorderSlanted( rDev, rTop, rBottom, rLine, pForceColor );

        svx::frame::Style aScaled( rLine );
        aScaled.ScaleSelf( 1.0 / cos( svx::frame::GetVerDiagAngle( rTop, rBottom ) ) );
        if( pForceColor )
            aScaled.SetColor( *pForceColor );

        long nXOffs = (aScaled.GetWidth() - 1) / -2L;

        lcl_VertLineEnds( rDev, rTop, rBottom, aScaled.GetColor(),
            nXOffs, aScaled.Prim(), rTopLine, rBottomLine );

        if( aScaled.Secn() )
            lcl_VertLineEnds( rDev, rTop, rBottom, aScaled.GetColor(),
                nXOffs + aScaled.Prim() + aScaled.Dist(), aScaled.Secn(), rTopLine, rBottomLine );
    }
}

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

void SdrTableObj::TakeObjNameSingul(XubString& rName) const
{
    rName = ImpGetResStr(STR_ObjNameSingulTable);

    String aName( GetName() );
    if(aName.Len())
    {
        rName += sal_Unicode(' ');
        rName += sal_Unicode('\'');
        rName += aName;
        rName += sal_Unicode('\'');
    }
}

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

void SdrTableObj::TakeObjNamePlural(XubString& rName) const
{
    rName = ImpGetResStr(STR_ObjNamePluralTable);
}

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

void SdrTableObj::operator=(const SdrObject& rObj)
{
    // call parent
    SdrObject::operator=(rObj);

    const SdrTableObj* pTableObj = dynamic_cast< const SdrTableObj* >( &rObj );
    if (pTableObj!=NULL)
    {
        TableModelNotifyGuard aGuard( mpImpl ? mpImpl->mxTable.get() : 0 );

        maLogicRect = pTableObj->maLogicRect;
        aRect = pTableObj->aRect;
        aGeo = pTableObj->aGeo;
        eTextKind = pTableObj->eTextKind;
        bTextFrame = pTableObj->bTextFrame;
        aTextSize = pTableObj->aTextSize;
        bTextSizeDirty = pTableObj->bTextSizeDirty;
        bNoShear = pTableObj->bNoShear;
        bNoRotate = pTableObj->bNoRotate;
        bNoMirror = pTableObj->bNoMirror;
        bDisableAutoWidthOnDragging = pTableObj->bDisableAutoWidthOnDragging;

        if( pTableObj->mpImpl )
            *mpImpl = *pTableObj->mpImpl;
    }
}

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

basegfx::B2DPolyPolygon SdrTableObj::TakeXorPoly() const
{
    return SdrTextObj::TakeXorPoly();
}

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

basegfx::B2DPolyPolygon SdrTableObj::TakeContour() const
{
    return SdrTextObj::TakeContour();
}

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

const Rectangle& SdrTableObj::GetSnapRect() const
{
    return aRect;
}

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

void SdrTableObj::NbcSetSnapRect(const Rectangle& rRect)
{
    NbcSetLogicRect( rRect );
}

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

const Rectangle& SdrTableObj::GetLogicRect() const
{
    return maLogicRect;
}

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

void SdrTableObj::RecalcSnapRect()
{
}

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

sal_uInt32 SdrTableObj::GetSnapPointCount() const
{
    return SdrTextObj::GetSnapPointCount();
}

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


Point SdrTableObj::GetSnapPoint(sal_uInt32 i) const
{
    return SdrTextObj::GetSnapPoint(i);
}

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

sal_Bool SdrTableObj::BegTextEdit(SdrOutliner& rOutl)
{
    if( pEdtOutl != NULL )
        return sal_False;

    pEdtOutl=&rOutl;

//  ForceOutlinerParaObject();

    mbInEditMode = TRUE;

    rOutl.Init( OUTLINERMODE_TEXTOBJECT );
    rOutl.SetRefDevice( pModel->GetRefDevice() );

// --
        FASTBOOL bUpdMerk=rOutl.GetUpdateMode();
        if (bUpdMerk) rOutl.SetUpdateMode(FALSE);
        Size aPaperMin;
        Size aPaperMax;
        Rectangle aEditArea;
        TakeTextEditArea(&aPaperMin,&aPaperMax,&aEditArea,NULL);

        rOutl.SetMinAutoPaperSize(aPaperMin);
        rOutl.SetMaxAutoPaperSize(aPaperMax);
        rOutl.SetPaperSize(aPaperMax);

        if (bUpdMerk) rOutl.SetUpdateMode(TRUE);
//---

    ULONG nStat=rOutl.GetControlWord();
//  nStat   &= ~EE_CNTRL_AUTOPAGESIZE;
    nStat   |= EE_CNTRL_AUTOPAGESIZE;
    nStat   &=~EE_CNTRL_STRETCHING;
    rOutl.SetControlWord(nStat);

    OutlinerParaObject* pPara = GetOutlinerParaObject();
    if(pPara)
        rOutl.SetText(*pPara);

    rOutl.UpdateFields();
    rOutl.ClearModifyFlag();

//  mpImpl->mnSavedEditRowHeight = mpImpl->mpLayouter->getRowHeight( mpImpl->maEditPos.mnRow );

    return sal_True;
}

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

void SdrTableObj::EndTextEdit(SdrOutliner& rOutl)
{
    if(rOutl.IsModified())
    {
        if( GetModel() && GetModel()->IsUndoEnabled() )
            GetModel()->AddUndo( GetModel()->GetSdrUndoFactory().CreateUndoGeoObject(*this) );

        OutlinerParaObject* pNewText = 0;
        Paragraph* p1stPara = rOutl.GetParagraph( 0 );
        UINT32 nParaAnz = rOutl.GetParagraphCount();

        if(p1stPara)
        {
            if(nParaAnz == 1)
            {
                // if its only one paragraph, check if it is empty
                XubString aStr(rOutl.GetText(p1stPara));

                if(!aStr.Len())
                {
                    // gotcha!
                    nParaAnz = 0;
                }
            }

            // to remove the grey field background
            rOutl.UpdateFields();

            if(nParaAnz != 0)
            {
                // create new text object
                pNewText = rOutl.CreateParaObject( 0, (sal_uInt16)nParaAnz );
            }
        }
        SetOutlinerParaObject(pNewText);
    }

    pEdtOutl = 0;
    rOutl.Clear();
    UINT32 nStat = rOutl.GetControlWord();
    nStat &= ~EE_CNTRL_AUTOPAGESIZE;
    rOutl.SetControlWord(nStat);

    mbInEditMode = FALSE;
}

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

OutlinerParaObject* SdrTableObj::GetOutlinerParaObject() const
{
    CellRef xCell( getActiveCell() );
    if( xCell.is() )
        return xCell->GetOutlinerParaObject();
    else
        return 0;
}

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

void SdrTableObj::NbcSetOutlinerParaObject( OutlinerParaObject* pTextObject)
{
    CellRef xCell( getActiveCell() );
    if( xCell.is() )
    {
        if( pModel )
        {
            // Update HitTestOutliner
            const SdrTextObj* pTestObj = pModel->GetHitTestOutliner().GetTextObj();
            if( pTestObj && pTestObj->GetOutlinerParaObject() == xCell->GetOutlinerParaObject() )
                pModel->GetHitTestOutliner().SetTextObj( NULL );
        }

        xCell->SetOutlinerParaObject( pTextObject );

        SetTextSizeDirty();
        NbcAdjustTextFrameWidthAndHeight();
//      ImpSetTextStyleSheetListeners();
//      ImpCheckMasterCachable();
    }
}

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

void SdrTableObj::NbcSetLogicRect(const Rectangle& rRect)
{
    maLogicRect=rRect;
    ImpJustifyRect(maLogicRect);
    const bool bWidth = maLogicRect.getWidth() != aRect.getWidth();
    const bool bHeight = maLogicRect.getHeight() != aRect.getHeight();
    aRect=maLogicRect;
    NbcAdjustTextFrameWidthAndHeight( !bHeight, !bWidth );
    SetRectsDirty();
}

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

void SdrTableObj::NbcMove(const Size& rSiz)
{
    MoveRect(maLogicRect,rSiz);
    SdrTextObj::NbcMove( rSiz );
    if( mpImpl )
        mpImpl->UpdateCells( aRect );
}

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

void SdrTableObj::NbcResize(const Point& rRef, const Fraction& xFact, const Fraction& yFact)
{
    Rectangle aOldRect( maLogicRect );
    ResizeRect(maLogicRect,rRef,xFact,yFact);

    aRect = maLogicRect;
    NbcAdjustTextFrameWidthAndHeight( maLogicRect.GetHeight() == aOldRect.GetHeight(), maLogicRect.GetWidth() == aOldRect.GetWidth() );
    SetRectsDirty();
}

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

FASTBOOL SdrTableObj::AdjustTextFrameWidthAndHeight(FASTBOOL bHgt, FASTBOOL bWdt)
{
    Rectangle aNeuRect(maLogicRect);
    FASTBOOL bRet=AdjustTextFrameWidthAndHeight(aNeuRect,bHgt,bWdt);
    if (bRet)
    {
        Rectangle aBoundRect0;
        if (pUserCall!=NULL)
            aBoundRect0=GetLastBoundRect();
        aRect=aNeuRect;
        SetRectsDirty();
        SetChanged();
        BroadcastObjectChange();
        SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
    }
    return bRet;
}

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

FASTBOOL SdrTableObj::AdjustTextFrameWidthAndHeight(Rectangle& rR, FASTBOOL bHeight, FASTBOOL bWidth) const
{
    if((pModel == NULL) || rR.IsEmpty() || !mpImpl || !mpImpl->mxTable.is() )
        return FALSE;

    Rectangle aRectangle( rR );
    mpImpl->LayoutTable( aRectangle, !bWidth, !bHeight );

    if( aRectangle != rR )
    {
        rR = aRectangle;
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}

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

void SdrTableObj::NbcReformatText()
{
    NbcAdjustTextFrameWidthAndHeight();
}

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

void SdrTableObj::ReformatText()
{
    Rectangle aBoundRect0;
    if (pUserCall!=NULL)
        aBoundRect0=GetLastBoundRect();
    NbcReformatText();
    SetChanged();
    BroadcastObjectChange();
    SendUserCall(SDRUSERCALL_RESIZE,aBoundRect0);
}

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

sal_Bool SdrTableObj::IsVerticalWriting() const
{
    const SvxWritingModeItem* pModeItem = dynamic_cast< const SvxWritingModeItem* >( &GetObjectItem( SDRATTR_TEXTDIRECTION ) );
    return pModeItem && pModeItem->GetValue() == com::sun::star::text::WritingMode_TB_RL;
}

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

void SdrTableObj::SetVerticalWriting(sal_Bool bVertical )
{
    if( bVertical != IsVerticalWriting() )
    {
        SvxWritingModeItem aModeItem( com::sun::star::text::WritingMode_LR_TB, SDRATTR_TEXTDIRECTION );
        SetObjectItem( aModeItem );
    }
}

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

WritingMode SdrTableObj::GetWritingMode() const
{
    WritingMode eMode = WritingMode_LR_TB;
    if( mpImpl && mpImpl->mpLayouter )
        eMode = mpImpl->mpLayouter->GetWritingMode();
    return eMode;
}

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

// gets base transformation and rectangle of object. If it's an SdrPathObj it fills the PolyPolygon
// with the base geometry and returns TRUE. Otherwise it returns FALSE.
sal_Bool SdrTableObj::TRGetBaseGeometry(basegfx::B2DHomMatrix& rMatrix, basegfx::B2DPolyPolygon& rPolyPolygon ) const
{
    return SdrTextObj::TRGetBaseGeometry( rMatrix, rPolyPolygon );
}

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

// sets the base geometry of the object using infos contained in the homogen 3x3 matrix.
// If it's an SdrPathObj it will use the provided geometry information. The Polygon has
// to use (0,0) as upper left and will be scaled to the given size in the matrix.
void SdrTableObj::TRSetBaseGeometry(const basegfx::B2DHomMatrix& rMatrix, const basegfx::B2DPolyPolygon& rPolyPolygon )
{
    SdrTextObj::TRSetBaseGeometry( rMatrix, rPolyPolygon );
}

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

bool SdrTableObj::IsRealyEdited() const
{
    return pEdtOutl && pEdtOutl->IsModified();
}

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

FASTBOOL SdrTableObj::IsFontwork() const
{
    return FALSE;
}

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

sal_uInt32 SdrTableObj::GetHdlCount() const
{
    sal_uInt32 nCount = SdrTextObj::GetHdlCount();
    const sal_Int32 nRowCount = mpImpl->getRowCount();
    const sal_Int32 nColCount = mpImpl->getColumnCount();

    if( nRowCount && nColCount )
        nCount += nRowCount + nColCount + 2 + 1;

    return nCount;
}

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

void SdrTableObj::AddToHdlList(SdrHdlList& rHdlList) const
{
    const sal_Int32 nRowCount = mpImpl->getRowCount();
    const sal_Int32 nColCount = mpImpl->getColumnCount();

    // first add row handles
    std::vector< TableEdgeHdl* > aRowEdges( nRowCount + 1 );

    for( sal_Int32 nRow = 0; nRow <= nRowCount; nRow++ )
    {
        sal_Int32 nEdgeMin, nEdgeMax;
        const sal_Int32 nEdge = mpImpl->mpLayouter->getHorizontalEdge( nRow, &nEdgeMin, &nEdgeMax );
        nEdgeMin -= nEdge;
        nEdgeMax -= nEdge;

        Point aPoint( aRect.TopLeft() );
        aPoint.Y() += nEdge;

        TableEdgeHdl* pHdl= new TableEdgeHdl(aPoint,true,nEdgeMin,nEdgeMax,nColCount+1);
        pHdl->SetPointNum( nRow );
        rHdlList.AddHdl( pHdl );
        aRowEdges[nRow] = pHdl;
    }

    // second add column handles
    std::vector< TableEdgeHdl* > aColEdges( nColCount + 1 );

    for( sal_Int32 nCol = 0; nCol <= nColCount; nCol++ )
    {
        sal_Int32 nEdgeMin, nEdgeMax;
        const sal_Int32 nEdge = mpImpl->mpLayouter->getVerticalEdge( nCol, &nEdgeMin, &nEdgeMax );
        nEdgeMin -= nEdge;
        nEdgeMax -= nEdge;

        Point aPoint( aRect.TopLeft() );
        aPoint.X() += nEdge;

        TableEdgeHdl* pHdl = new TableEdgeHdl(aPoint,false,nEdgeMin,nEdgeMax, nRowCount+1);
        pHdl->SetPointNum( nCol );
        rHdlList.AddHdl( pHdl );
        aColEdges[nCol] = pHdl;
    }

    // now add visible edges to row and column handles
    if( mpImpl && mpImpl->mpLayouter )
    {
        TableLayouter& rLayouter = *mpImpl->mpLayouter;

        sal_Int32 nY = 0;

        for( sal_Int32 nRow = 0; nRow <= nRowCount; ++nRow )
        {
            const sal_Int32 nRowHeight = (nRow == nRowCount) ? 0 : rLayouter.getRowHeight(nRow);
            sal_Int32 nX = 0;

            for( sal_Int32 nCol = 0; nCol <= nColCount; ++nCol )
            {
                const sal_Int32 nColWidth = (nCol == nColCount) ? 0 : rLayouter.getColumnWidth(nCol);

                if( nRowHeight > 0 )
                {
                    if( rLayouter.isEdgeVisible( nCol, nRow, false ) )
                        aColEdges[nCol]->SetEdge( nRow, nY, nY + nRowHeight, (rLayouter.getBorderLine( nCol, nRow, false ) == 0) ? Visible : Invisible);
                }

                if( nColWidth > 0 )
                {
                    if( rLayouter.isEdgeVisible( nCol, nRow, true ) )
                        aRowEdges[nRow]->SetEdge( nCol, nX, nX + nColWidth, (rLayouter.getBorderLine( nCol, nRow, true ) == 0) ? Visible : Invisible);
                }

                nX += nColWidth;
            }

            nY += nRowHeight;
        }
    }

    // add remaining handles
    SdrHdl* pH=0;
    rHdlList.AddHdl( pH = new TableBorderHdl( aRect ) ); pH->SetMoveOutside( true );
    rHdlList.AddHdl( pH = new SdrHdl(aRect.TopLeft(),HDL_UPLFT) ); pH->SetMoveOutside( true );
    rHdlList.AddHdl( pH = new SdrHdl(aRect.TopCenter(),HDL_UPPER) ); pH->SetMoveOutside( true );
    rHdlList.AddHdl( pH = new SdrHdl(aRect.TopRight(),HDL_UPRGT) ); pH->SetMoveOutside( true );
    rHdlList.AddHdl( pH = new SdrHdl(aRect.LeftCenter(),HDL_LEFT) ); pH->SetMoveOutside( true );
    rHdlList.AddHdl( pH = new SdrHdl(aRect.RightCenter(),HDL_RIGHT) ); pH->SetMoveOutside( true );
    rHdlList.AddHdl( pH = new SdrHdl(aRect.BottomLeft(),HDL_LWLFT) ); pH->SetMoveOutside( true );
    rHdlList.AddHdl( pH = new SdrHdl(aRect.BottomCenter(),HDL_LOWER) ); pH->SetMoveOutside( true );
    rHdlList.AddHdl( pH = new SdrHdl(aRect.BottomRight(),HDL_LWRGT) ); pH->SetMoveOutside( true );

    ULONG nHdlCount = rHdlList.GetHdlCount();
    for( ULONG nHdl = 0; nHdl < nHdlCount; nHdl++ )
        rHdlList.GetHdl(nHdl)->SetObj((SdrObject*)this);
}

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

SdrHdl* SdrTableObj::GetHdl(sal_uInt32 nHdlNum) const
{
    // #i73248#
    // Warn the user that this is ineffective and show alternatives. Should not be used at all.
    OSL_ENSURE(false, "SdrTableObj::GetHdl(): ineffective, use AddToHdlList instead (!)");

    // to have an alternative, get single handle using the ineffective way
    SdrHdl* pRetval = 0;
    SdrHdlList aLocalList(0);
    AddToHdlList(aLocalList);
    const sal_uInt32 nHdlCount(aLocalList.GetHdlCount());

    if(nHdlCount && nHdlNum < nHdlCount)
    {
        // remove and remember. The other created handles will be deleted again with the
        // destruction of the local list
        pRetval = aLocalList.RemoveHdl(nHdlNum);
    }

    return pRetval;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Draging

bool SdrTableObj::hasSpecialDrag() const
{
    return true;
}

bool SdrTableObj::beginSpecialDrag(SdrDragStat& rDrag) const
{
    const SdrHdl* pHdl = rDrag.GetHdl();
    const SdrHdlKind eHdl((pHdl == NULL) ? HDL_MOVE : pHdl->GetKind());

    switch( eHdl )
    {
        case HDL_UPLFT:
        case HDL_UPPER:
        case HDL_UPRGT:
        case HDL_LEFT:
        case HDL_RIGHT:
        case HDL_LWLFT:
        case HDL_LOWER:
        case HDL_LWRGT:
        case HDL_MOVE:
        {
            break;
        }

        case HDL_USER:
        {
            rDrag.SetEndDragChangesAttributes(false);
            rDrag.SetNoSnap(true);
            break;
        }

        default:
        {
            return false;
        }
    }

    return true;
}

bool SdrTableObj::applySpecialDrag(SdrDragStat& rDrag)
{
    bool bRet(true);
    const SdrHdl* pHdl = rDrag.GetHdl();
    const SdrHdlKind eHdl((pHdl == NULL) ? HDL_MOVE : pHdl->GetKind());

    switch( eHdl )
    {
        case HDL_UPLFT:
        case HDL_UPPER:
        case HDL_UPRGT:
        case HDL_LEFT:
        case HDL_RIGHT:
        case HDL_LWLFT:
        case HDL_LOWER:
        case HDL_LWRGT:
        {
            const Rectangle aNewRectangle(ImpDragCalcRect(rDrag));

            if(aNewRectangle != aRect)
            {
                   NbcSetLogicRect(aNewRectangle);
            }

            break;
        }

        case HDL_MOVE:
        {
               NbcMove( Size( rDrag.GetDX(), rDrag.GetDY() ) );
            break;
        }

        case HDL_USER:
        {
            rDrag.SetEndDragChangesAttributes(false);
            rDrag.SetNoSnap(true);
            const TableEdgeHdl* pEdgeHdl = dynamic_cast< const TableEdgeHdl* >( pHdl );

            if( pEdgeHdl )
            {
                if( GetModel() && IsInserted() )
                {
                    rDrag.SetEndDragChangesAttributes(true);
                }

                mpImpl->DragEdge( pEdgeHdl->IsHorizontalEdge(), pEdgeHdl->GetPointNum(), pEdgeHdl->GetValidDragOffset( rDrag ) );
            }
            break;
        }

        default:
        {
            bRet = false;
        }
    }

    return bRet;
}

String SdrTableObj::getSpecialDragComment(const SdrDragStat& rDrag) const
{
    return SdrTextObj::getSpecialDragComment( rDrag );
}

basegfx::B2DPolyPolygon SdrTableObj::getSpecialDragPoly(const SdrDragStat& rDrag) const
{
    basegfx::B2DPolyPolygon aRetval;
    const SdrHdl* pHdl = rDrag.GetHdl();

    if(HDL_USER == pHdl->GetKind())
    {
        const TableEdgeHdl* pEdgeHdl = dynamic_cast< const TableEdgeHdl* >( pHdl );

        if( pEdgeHdl )
        {
            aRetval = pEdgeHdl->getSpecialDragPoly( rDrag );
        }
    }

    return aRetval;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
// Create
// --------------------------------------------------------------------

FASTBOOL SdrTableObj::BegCreate(SdrDragStat& rStat)
{
    rStat.SetOrtho4Possible();
    Rectangle aRect1(rStat.GetStart(), rStat.GetNow());
    aRect1.Justify();
    rStat.SetActionRect(aRect1);
    aRect = aRect1;
    return TRUE;
}

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

FASTBOOL SdrTableObj::MovCreate(SdrDragStat& rStat)
{
    Rectangle aRect1;
    rStat.TakeCreateRect(aRect1);
    ImpJustifyRect(aRect1);
    rStat.SetActionRect(aRect1);
    aRect=aRect1; // fuer ObjName
    SetBoundRectDirty();
    bSnapRectDirty=TRUE;
    return TRUE;
}

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

FASTBOOL SdrTableObj::EndCreate(SdrDragStat& rStat, SdrCreateCmd eCmd)
{
    rStat.TakeCreateRect(aRect);
    ImpJustifyRect(aRect);
    return (eCmd==SDRCREATE_FORCEEND || rStat.GetPointAnz()>=2);
}

void SdrTableObj::BrkCreate(SdrDragStat& /*rStat*/)
{
}

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

FASTBOOL SdrTableObj::BckCreate(SdrDragStat& /*rStat*/)
{
    return TRUE;
}

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

basegfx::B2DPolyPolygon SdrTableObj::TakeCreatePoly(const SdrDragStat& rDrag) const
{
    Rectangle aRect1;
    rDrag.TakeCreateRect(aRect1);
    aRect1.Justify();

    basegfx::B2DPolyPolygon aRetval;
    const basegfx::B2DRange aRange(aRect1.Left(), aRect1.Top(), aRect1.Right(), aRect1.Bottom());
    aRetval.append(basegfx::tools::createPolygonFromRect(aRange));
    return aRetval;
}

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

Pointer SdrTableObj::GetCreatePointer() const
{
    return Pointer(POINTER_CROSS);
}

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

void SdrTableObj::createCell( CellRef& xNewCell )
{
    xNewCell = Cell::create( *this, 0 );
}

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

SdrObjGeoData *SdrTableObj::NewGeoData() const
{
    return new TableObjectGeoData;
}

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

void SdrTableObj::SaveGeoData(SdrObjGeoData& rGeo) const
{
    DBG_ASSERT( dynamic_cast< TableObjectGeoData* >( &rGeo ), "svx::SdrTableObj::SaveGeoData(), illegal geo data!" );
    SdrTextObj::SaveGeoData (rGeo);

    ((TableObjectGeoData &) rGeo).maLogicRect = maLogicRect;
}

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

void SdrTableObj::RestGeoData(const SdrObjGeoData& rGeo)
{
    DBG_ASSERT( dynamic_cast< const TableObjectGeoData* >( &rGeo ), "svx::SdrTableObj::SaveGeoData(), illegal geo data!" );

    maLogicRect = ((TableObjectGeoData &) rGeo).maLogicRect;

    SdrTextObj::RestGeoData (rGeo);

    if( mpImpl )
        mpImpl->LayoutTable( aRect, false, false );
    ActionChanged();
}

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

SdrTableObj* SdrTableObj::CloneRange( const CellPos& rStart, const CellPos& rEnd )
{
    const sal_Int32 nColumns = rEnd.mnCol - rStart.mnCol + 1;
    const sal_Int32 nRows = rEnd.mnRow - rStart.mnRow + 1;

    SdrTableObj* pNewTableObj = new SdrTableObj( GetModel(), GetCurrentBoundRect(), nColumns, nRows);
    pNewTableObj->setTableStyleSettings( getTableStyleSettings() );
    pNewTableObj->setTableStyle( getTableStyle() );

    Reference< XTable > xTable( getTable() );
    Reference< XTable > xNewTable( pNewTableObj->getTable() );

    if( !xTable.is() || !xNewTable.is() )
    {
        delete pNewTableObj;
        return 0;
    }

    // copy cells
    for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
    {
        for( sal_Int32 nCol = 0; nCol < nColumns; ++nCol ) try
        {
            CellRef xTargetCell( dynamic_cast< Cell* >( xNewTable->getCellByPosition( nCol, nRow ).get() ) );
            if( xTargetCell.is() )
                xTargetCell->cloneFrom( dynamic_cast< Cell* >( xTable->getCellByPosition( rStart.mnCol + nCol, rStart.mnRow + nRow ).get() ) );
        }
        catch( Exception& )
        {
            DBG_ERROR( "svx::SvxTableController::GetMarkedObjModel(), exception caught!" );
        }
    }

    // copy row heights
    Reference< XTableRows > xNewRows( xNewTable->getRows(), UNO_QUERY_THROW );
    const OUString sHeight( RTL_CONSTASCII_USTRINGPARAM( "Height" ) );
    for( sal_Int32 nRow = 0; nRow < nRows; ++nRow )
    {
        Reference< XPropertySet > xNewSet( xNewRows->getByIndex( nRow ), UNO_QUERY_THROW );
        xNewSet->setPropertyValue( sHeight, Any( mpImpl->mpLayouter->getRowHeight( rStart.mnRow + nRow ) ) );
    }

    // copy column widths
    Reference< XTableColumns > xNewColumns( xNewTable->getColumns(), UNO_QUERY_THROW );
    const OUString sWidth( RTL_CONSTASCII_USTRINGPARAM( "Width" ) );
    for( sal_Int32 nCol = 0; nCol < nColumns; ++nCol )
    {
        Reference< XPropertySet > xNewSet( xNewColumns->getByIndex( nCol ), UNO_QUERY_THROW );
        xNewSet->setPropertyValue( sWidth, Any( mpImpl->mpLayouter->getColumnWidth( rStart.mnCol + nCol ) ) );
    }

    pNewTableObj->NbcReformatText();
    pNewTableObj->SetLogicRect( pNewTableObj->GetCurrentBoundRect() );

    return pNewTableObj;
}

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

void SdrTableObj::DistributeColumns( sal_Int32 nFirstColumn, sal_Int32 nLastColumn )
{
    if( mpImpl && mpImpl->mpLayouter )
    {
        TableModelNotifyGuard aGuard( mpImpl->mxTable.get() );
        mpImpl->mpLayouter->DistributeColumns( aRect, nFirstColumn, nLastColumn );
    }
}

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

void SdrTableObj::DistributeRows( sal_Int32 nFirstRow, sal_Int32 nLastRow )
{
    if( mpImpl && mpImpl->mpLayouter )
    {
        TableModelNotifyGuard aGuard( mpImpl->mxTable.get() );
        mpImpl->mpLayouter->DistributeRows( aRect, nFirstRow, nLastRow );
    }
}

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

void SdrTableObj::SetChanged()
{
    if( mpImpl )
    {
        if( mpImpl->UpdateWritingMode() )
            mpImpl->LayoutTable( aRect, false, false );
    }

    ::SdrTextObj::SetChanged();
}

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

void SdrTableObj::uno_lock()
{
    if( mpImpl && mpImpl->mxTable.is() )
        mpImpl->mxTable->lockBroadcasts();
}

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

void SdrTableObj::uno_unlock()
{
    if( mpImpl && mpImpl->mxTable.is() )
        mpImpl->mxTable->unlockBroadcasts();
}

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



} }