/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 * 
 * Copyright 2000, 2010 Oracle and/or its affiliates.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * 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_sw.hxx"

#include <UndoTable.hxx>

#include <UndoRedline.hxx>
#include <UndoDelete.hxx>
#include <UndoSplitMove.hxx>
#include <UndoCore.hxx>
#include <hintids.hxx>
#include <hints.hxx>
#include <editeng/brkitem.hxx>
#include <fmtornt.hxx>
#include <fmtpdsc.hxx>
#include <doc.hxx>
#include <IDocumentUndoRedo.hxx>
#include <editsh.hxx>
#include <docary.hxx>
#include <ndtxt.hxx>
#include <swtable.hxx>
#include <pam.hxx>
#include <cntfrm.hxx>
#include <tblsel.hxx>
#include <swundo.hxx>			// fuer die UndoIds
#include <rolbck.hxx>
#include <ddefld.hxx>
#include <tabcol.hxx>
#include <tabfrm.hxx>
#include <rowfrm.hxx>
#include <cellfrm.hxx>
#include <swcache.hxx>
#include <tblafmt.hxx>
#include <poolfmt.hxx>
#include <mvsave.hxx>
#include <cellatr.hxx>
#include <swtblfmt.hxx>
#include <swddetbl.hxx>
#include <redline.hxx>
#include <node2lay.hxx>
#include <tblrwcl.hxx>
#include <fmtanchr.hxx>
#include <comcore.hrc>
#include <unochart.hxx>
#include <switerator.hxx>

#if OSL_DEBUG_LEVEL > 1
#define CHECK_TABLE(t) (t).CheckConsistency();
#else
#define CHECK_TABLE(t)
#endif

#if OSL_DEBUG_LEVEL > 1
    void lcl_DebugRedline( const SwDoc* pDoc );
    #define _DEBUG_REDLINE( pDoc ) lcl_DebugRedline( pDoc );
#else
    #define _DEBUG_REDLINE( pDoc )
#endif

extern void ClearFEShellTabCols();

typedef SfxItemSet* SfxItemSetPtr;
SV_DECL_PTRARR_DEL( SfxItemSets, SfxItemSetPtr, 10, 5 )

typedef SwUndoSaveSection* SwUndoSaveSectionPtr;
SV_DECL_PTRARR_DEL( SwUndoSaveSections, SwUndoSaveSectionPtr, 0, 10 )

typedef SwUndoMove* SwUndoMovePtr;
SV_DECL_PTRARR_DEL( SwUndoMoves, SwUndoMovePtr, 0, 10 )

struct SwTblToTxtSave;
typedef SwTblToTxtSave* SwTblToTxtSavePtr;
SV_DECL_PTRARR_DEL( SwTblToTxtSaves, SwTblToTxtSavePtr, 0, 10 )

struct _UndoTblCpyTbl_Entry
{
    sal_uLong nBoxIdx, nOffset;
    SfxItemSet* pBoxNumAttr;
    SwUndo* pUndo;

    // Was the last paragraph of the new and the first paragraph of the old content joined?
    bool bJoin; // For redlining only

    _UndoTblCpyTbl_Entry( const SwTableBox& rBox );
    ~_UndoTblCpyTbl_Entry();
};
typedef _UndoTblCpyTbl_Entry* _UndoTblCpyTbl_EntryPtr;
SV_DECL_PTRARR_DEL( _UndoTblCpyTbl_Entries, _UndoTblCpyTbl_EntryPtr, 0, 10 )

class _SaveBox;
class _SaveLine;

class _SaveTable
{
    friend class _SaveBox;
    friend class _SaveLine;
    SfxItemSet aTblSet;
    _SaveLine* pLine;
    const SwTable* pSwTable;
    SfxItemSets aSets;
    SwFrmFmts aFrmFmts;
    sal_uInt16 nLineCount;
    sal_Bool bModifyBox : 1;
    sal_Bool bSaveFormula : 1;
    sal_Bool bNewModel : 1;

public:
    _SaveTable( const SwTable& rTbl, sal_uInt16 nLnCnt = USHRT_MAX,
                sal_Bool bSaveFml = sal_True );
    ~_SaveTable();

    sal_uInt16 AddFmt( SwFrmFmt* pFmt, bool bIsLine );
    void NewFrmFmt( const SwTableLine* , const SwTableBox*, sal_uInt16 nFmtPos,
                    SwFrmFmt* pOldFmt );

    void RestoreAttr( SwTable& rTbl, sal_Bool bModifyBox = sal_False );
    void SaveCntntAttrs( SwDoc* pDoc );
    void CreateNew( SwTable& rTbl, sal_Bool bCreateFrms = sal_True,
                    sal_Bool bRestoreChart = sal_True );
    sal_Bool IsNewModel() const { return bNewModel; }
};

class _SaveLine
{
    friend class _SaveTable;
    friend class _SaveBox;

    _SaveLine* pNext;
    _SaveBox* pBox;
    sal_uInt16 nItemSet;

public:

    _SaveLine( _SaveLine* pPrev, const SwTableLine& rLine, _SaveTable& rSTbl );
    ~_SaveLine();

    void RestoreAttr( SwTableLine& rLine, _SaveTable& rSTbl );
    void SaveCntntAttrs( SwDoc* pDoc );

    void CreateNew( SwTable& rTbl, SwTableBox& rParent, _SaveTable& rSTbl  );
};

class _SaveBox
{
    friend class _SaveLine;

    _SaveBox* pNext;
    sal_uLong nSttNode;
    long nRowSpan;
    sal_uInt16 nItemSet;
    union
    {
        SfxItemSets* pCntntAttrs;
        _SaveLine* pLine;
    } Ptrs;

public:
    _SaveBox( _SaveBox* pPrev, const SwTableBox& rBox, _SaveTable& rSTbl );
    ~_SaveBox();

    void RestoreAttr( SwTableBox& rBox, _SaveTable& rSTbl );
    void SaveCntntAttrs( SwDoc* pDoc );

    void CreateNew( SwTable& rTbl, SwTableLine& rParent, _SaveTable& rSTbl );
};

void InsertSort( SvUShorts& rArr, sal_uInt16 nIdx, sal_uInt16* pInsPos = 0 );
void InsertSort( SvULongs& rArr, sal_uLong nIdx, sal_uInt16* pInsPos = 0 );

#if OSL_DEBUG_LEVEL > 1
#include "shellio.hxx"
void CheckTable( const SwTable& );
#define CHECKTABLE(t) CheckTable( t );
#else
#define CHECKTABLE(t)
#endif

/* #130880: Crash in undo of table to text when the table has (freshly) merged cells
The order of cell content nodes in the nodes array is not given by the recursive table structure.
The algorithmn must not rely on this even it holds for a fresh loaded table in odt file format.
So we need to remember not only the start node position but the end node position as well.
*/

struct SwTblToTxtSave
{
    sal_uLong m_nSttNd;
    sal_uLong m_nEndNd;
    xub_StrLen m_nCntnt;
    SwHistory* m_pHstry;
    // metadata references for first and last paragraph in cell
    ::boost::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoStart;
    ::boost::shared_ptr< ::sfx2::MetadatableUndo > m_pMetadataUndoEnd;

    SwTblToTxtSave( SwDoc& rDoc, sal_uLong nNd, sal_uLong nEndIdx, xub_StrLen nCntnt );
    ~SwTblToTxtSave() { delete m_pHstry; }
};

SV_IMPL_PTRARR( SfxItemSets, SfxItemSetPtr )
SV_IMPL_PTRARR( SwUndoSaveSections, SwUndoSaveSectionPtr )
SV_IMPL_PTRARR( SwUndoMoves, SwUndoMovePtr )
SV_IMPL_PTRARR( SwTblToTxtSaves, SwTblToTxtSavePtr )
SV_IMPL_PTRARR( _UndoTblCpyTbl_Entries, _UndoTblCpyTbl_EntryPtr )

sal_uInt16 aSave_BoxCntntSet[] = {
    RES_CHRATR_COLOR, RES_CHRATR_CROSSEDOUT,
    RES_CHRATR_FONT, RES_CHRATR_FONTSIZE,
    RES_CHRATR_POSTURE,	RES_CHRATR_POSTURE,
    RES_CHRATR_SHADOWED, RES_CHRATR_WEIGHT,
    RES_PARATR_ADJUST, RES_PARATR_ADJUST,
    0 };



SwUndoInsTbl::SwUndoInsTbl( const SwPosition& rPos, sal_uInt16 nCl, sal_uInt16 nRw,
                            sal_uInt16 nAdj, const SwInsertTableOptions& rInsTblOpts,
                            const SwTableAutoFmt* pTAFmt,
                            const SvUShorts* pColArr,
                            const String & rName)
    : SwUndo( UNDO_INSTABLE ),
    aInsTblOpts( rInsTblOpts ), pDDEFldType( 0 ), pColWidth( 0 ), pRedlData( 0 ), pAutoFmt( 0 ),
    nSttNode( rPos.nNode.GetIndex() ), nRows( nRw ), nCols( nCl ), nAdjust( nAdj )
{
    if( pColArr )
    {
        pColWidth = new SvUShorts( 0, 1 );
        pColWidth->Insert( pColArr, 0 );
    }
    if( pTAFmt )
        pAutoFmt = new SwTableAutoFmt( *pTAFmt );

    // Redline beachten
    SwDoc& rDoc = *rPos.nNode.GetNode().GetDoc();
    if( rDoc.IsRedlineOn() )
    {
        pRedlData = new SwRedlineData( nsRedlineType_t::REDLINE_INSERT, rDoc.GetRedlineAuthor() );
        SetRedlineMode( rDoc.GetRedlineMode() );
    }

    sTblNm = rName;
}


SwUndoInsTbl::~SwUndoInsTbl()
{
    delete pDDEFldType;
    delete pColWidth;
    delete pRedlData;
    delete pAutoFmt;
}

void SwUndoInsTbl::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwNodeIndex aIdx( rDoc.GetNodes(), nSttNode );

    SwTableNode* pTblNd = aIdx.GetNode().GetTableNode();
    OSL_ENSURE( pTblNd, "kein TabellenNode" );
    pTblNd->DelFrms();

    if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineMode() ))
        rDoc.DeleteRedline( *pTblNd, true, USHRT_MAX );
    RemoveIdxFromSection( rDoc, nSttNode );

    // harte SeitenUmbrueche am nachfolgenden Node verschieben
    SwCntntNode* pNextNd = rDoc.GetNodes()[ pTblNd->EndOfSectionIndex()+1 ]->GetCntntNode();
    if( pNextNd )
    {
        SwFrmFmt* pTableFmt = pTblNd->GetTable().GetFrmFmt();
        const SfxPoolItem *pItem;

        if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_PAGEDESC,
            sal_False, &pItem ) )
            pNextNd->SetAttr( *pItem );

        if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_BREAK,
            sal_False, &pItem ) )
            pNextNd->SetAttr( *pItem );
    }


    sTblNm = pTblNd->GetTable().GetFrmFmt()->GetName();
    if( pTblNd->GetTable().IsA( TYPE( SwDDETable )) )
        pDDEFldType = (SwDDEFieldType*)((SwDDETable&)pTblNd->GetTable()).
                                        GetDDEFldType()->Copy();

    rDoc.GetNodes().Delete( aIdx, pTblNd->EndOfSectionIndex() -
                                aIdx.GetIndex() + 1 );

    SwPaM & rPam( rContext.GetCursorSupplier().CreateNewShellCursor() );
    rPam.DeleteMark();
    rPam.GetPoint()->nNode = aIdx;
    rPam.GetPoint()->nContent.Assign( rPam.GetCntntNode(), 0 );
}


void SwUndoInsTbl::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();

    SwPosition const aPos(SwNodeIndex(rDoc.GetNodes(), nSttNode));
    const SwTable* pTbl = rDoc.InsertTable( aInsTblOpts, aPos, nRows, nCols,
                                            nAdjust,
                                            pAutoFmt, pColWidth );
    ((SwFrmFmt*)pTbl->GetFrmFmt())->SetName( sTblNm );
    SwTableNode* pTblNode = (SwTableNode*)rDoc.GetNodes()[nSttNode]->GetTableNode();

    if( pDDEFldType )
    {
        SwDDEFieldType* pNewType = (SwDDEFieldType*)rDoc.InsertFldType(
                                                            *pDDEFldType);
        SwDDETable* pDDETbl = new SwDDETable( pTblNode->GetTable(), pNewType );
        pTblNode->SetNewTable( pDDETbl );		// setze die DDE-Tabelle
        delete pDDEFldType, pDDEFldType = 0;
    }

    if( (pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineMode() )) ||
        ( !( nsRedlineMode_t::REDLINE_IGNORE & GetRedlineMode() ) &&
            rDoc.GetRedlineTbl().Count() ))
    {
        SwPaM aPam( *pTblNode->EndOfSectionNode(), *pTblNode, 1 );
        SwCntntNode* pCNd = aPam.GetCntntNode( sal_False );
        if( pCNd )
            aPam.GetMark()->nContent.Assign( pCNd, 0 );

        if( pRedlData && IDocumentRedlineAccess::IsRedlineOn( GetRedlineMode() ) )
        {
            RedlineMode_t eOld = rDoc.GetRedlineMode();
            rDoc.SetRedlineMode_intern((RedlineMode_t)(eOld & ~nsRedlineMode_t::REDLINE_IGNORE));

            rDoc.AppendRedline( new SwRedline( *pRedlData, aPam ), true);
            rDoc.SetRedlineMode_intern( eOld );
        }
        else
            rDoc.SplitRedline( aPam );
    }
}


void SwUndoInsTbl::RepeatImpl(::sw::RepeatContext & rContext)
{
    rContext.GetDoc().InsertTable(
            aInsTblOpts, *rContext.GetRepeatPaM().GetPoint(),
            nRows, nCols, nAdjust, pAutoFmt, pColWidth );
}

SwRewriter SwUndoInsTbl::GetRewriter() const
{
    SwRewriter aRewriter;

    aRewriter.AddRule(UNDO_ARG1, SW_RES(STR_START_QUOTE));
    aRewriter.AddRule(UNDO_ARG2, sTblNm);
    aRewriter.AddRule(UNDO_ARG3, SW_RES(STR_END_QUOTE));

    return aRewriter;
}

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

SwTblToTxtSave::SwTblToTxtSave( SwDoc& rDoc, sal_uLong nNd, sal_uLong nEndIdx, xub_StrLen nCnt )
    : m_nSttNd( nNd ), m_nEndNd( nEndIdx), m_nCntnt( nCnt ), m_pHstry( 0 )
{
    // Attributierung des gejointen Node merken.
    SwTxtNode* pNd = rDoc.GetNodes()[ nNd ]->GetTxtNode();
    if( pNd )
    {
        m_pHstry = new SwHistory;

        m_pHstry->Add( pNd->GetTxtColl(), nNd, ND_TEXTNODE );
        if ( pNd->GetpSwpHints() )
        {
            m_pHstry->CopyAttr( pNd->GetpSwpHints(), nNd, 0,
                        pNd->GetTxt().Len(), false );
        }
        if( pNd->HasSwAttrSet() )
            m_pHstry->CopyFmtAttr( *pNd->GetpSwAttrSet(), nNd );

        if( !m_pHstry->Count() )
            delete m_pHstry, m_pHstry = 0;

        // METADATA: store
        m_pMetadataUndoStart = pNd->CreateUndo();
    }

    // we also need to store the metadata reference of the _last_ paragraph
    // we subtract 1 to account for the removed cell start/end node pair
    // (after SectionUp, the end of the range points to the node after the cell)
    if ( nEndIdx - 1 > nNd )
    {
        SwTxtNode* pLastNode( rDoc.GetNodes()[ nEndIdx - 1 ]->GetTxtNode() );
        if( pLastNode )
        {
            // METADATA: store
            m_pMetadataUndoEnd = pLastNode->CreateUndo();
        }
    }
}

SwUndoTblToTxt::SwUndoTblToTxt( const SwTable& rTbl, sal_Unicode cCh )
    : SwUndo( UNDO_TABLETOTEXT ),
    sTblNm( rTbl.GetFrmFmt()->GetName() ), pDDEFldType( 0 ), pHistory( 0 ),
    nSttNd( 0 ), nEndNd( 0 ),
    nAdjust( static_cast<sal_uInt16>(rTbl.GetFrmFmt()->GetHoriOrient().GetHoriOrient()) ),
    cTrenner( cCh ), nHdlnRpt( rTbl.GetRowsToRepeat() )
{
    pTblSave = new _SaveTable( rTbl );
    pBoxSaves = new SwTblToTxtSaves( (sal_uInt8)rTbl.GetTabSortBoxes().Count() );

    if( rTbl.IsA( TYPE( SwDDETable ) ) )
        pDDEFldType = (SwDDEFieldType*)((SwDDETable&)rTbl).GetDDEFldType()->Copy();

    bCheckNumFmt = rTbl.GetFrmFmt()->GetDoc()->IsInsTblFormatNum();

    pHistory = new SwHistory;
    const SwTableNode* pTblNd = rTbl.GetTableNode();
    sal_uLong nTblStt = pTblNd->GetIndex(), nTblEnd = pTblNd->EndOfSectionIndex();

    const SwSpzFrmFmts& rFrmFmtTbl = *pTblNd->GetDoc()->GetSpzFrmFmts();
    for( sal_uInt16 n = 0; n < rFrmFmtTbl.Count(); ++n )
    {
        SwFrmFmt* pFmt = rFrmFmtTbl[ n ];
        SwFmtAnchor const*const pAnchor = &pFmt->GetAnchor();
        SwPosition const*const pAPos = pAnchor->GetCntntAnchor();
        if (pAPos &&
            ((FLY_AT_CHAR == pAnchor->GetAnchorId()) ||
             (FLY_AT_PARA == pAnchor->GetAnchorId())) &&
            nTblStt <= pAPos->nNode.GetIndex() &&
            pAPos->nNode.GetIndex() < nTblEnd )
        {
            pHistory->Add( *pFmt );
        }
    }

    if( !pHistory->Count() )
        delete pHistory, pHistory = 0;
}


SwUndoTblToTxt::~SwUndoTblToTxt()
{
    delete pDDEFldType;
    delete pTblSave;
    delete pBoxSaves;
    delete pHistory;
}



void SwUndoTblToTxt::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());

    SwNodeIndex aFrmIdx( rDoc.GetNodes(), nSttNd );
    SwNodeIndex aEndIdx( rDoc.GetNodes(), nEndNd );

    pPam->GetPoint()->nNode = aFrmIdx;
    pPam->SetMark();
    pPam->GetPoint()->nNode = aEndIdx;
    rDoc.DelNumRules( *pPam );
    pPam->DeleteMark();

    // dann sammel mal alle Uppers ein
    SwNode2Layout aNode2Layout( aFrmIdx.GetNode() );

    // erzeuge die TabelleNode Structur
    SwTableNode* pTblNd = rDoc.GetNodes().UndoTableToText( nSttNd, nEndNd, *pBoxSaves );
    pTblNd->GetTable().SetTableModel( pTblSave->IsNewModel() );
    SwTableFmt* pTableFmt = rDoc.MakeTblFrmFmt( sTblNm, rDoc.GetDfltFrmFmt() );
    pTblNd->GetTable().RegisterToFormat( *pTableFmt );
    pTblNd->GetTable().SetRowsToRepeat( nHdlnRpt );

    // erzeuge die alte Tabellen Struktur
    pTblSave->CreateNew( pTblNd->GetTable() );

    if( pDDEFldType )
    {
        SwDDEFieldType* pNewType = (SwDDEFieldType*)rDoc.InsertFldType(
                                                            *pDDEFldType);
        SwDDETable* pDDETbl = new SwDDETable( pTblNd->GetTable(), pNewType );
        pTblNd->SetNewTable( pDDETbl, sal_False );		// setze die DDE-Tabelle
        delete pDDEFldType, pDDEFldType = 0;
    }

    if( bCheckNumFmt )
    {
        SwTableSortBoxes& rBxs = pTblNd->GetTable().GetTabSortBoxes();
        for( sal_uInt16 nBoxes = rBxs.Count(); nBoxes; )
            rDoc.ChkBoxNumFmt( *rBxs[ --nBoxes ], sal_False );
    }

    if( pHistory )
    {
        sal_uInt16 nTmpEnd = pHistory->GetTmpEnd();
        pHistory->TmpRollback( &rDoc, 0 );
        pHistory->SetTmpEnd( nTmpEnd );
    }

    aNode2Layout.RestoreUpperFrms( rDoc.GetNodes(),
                                   pTblNd->GetIndex(), pTblNd->GetIndex()+1 );

    // will man eine TabellenSelektion ??
    pPam->DeleteMark();
    pPam->GetPoint()->nNode = *pTblNd->EndOfSectionNode();
    pPam->SetMark();
    pPam->GetPoint()->nNode = *pPam->GetNode()->StartOfSectionNode();
    pPam->Move( fnMoveForward, fnGoCntnt );
    pPam->Exchange();
    pPam->Move( fnMoveBackward, fnGoCntnt );

    ClearFEShellTabCols();
}

    // steht im untbl.cxx und darf nur vom Undoobject gerufen werden
SwTableNode* SwNodes::UndoTableToText( sal_uLong nSttNd, sal_uLong nEndNd,
                                const SwTblToTxtSaves& rSavedData )
{
    SwNodeIndex aSttIdx( *this, nSttNd );
    SwNodeIndex aEndIdx( *this, nEndNd+1 );

    SwTableNode * pTblNd = new SwTableNode( aSttIdx );
    SwEndNode* pEndNd = new SwEndNode( aEndIdx, *pTblNd  );

    aEndIdx = *pEndNd;

    /* Set pTblNd as start of section for all nodes in [nSttNd, nEndNd].
       Delete all Frames attached to the nodes in that range. */
    SwNode* pNd;
    {
        sal_uLong n, nTmpEnd = aEndIdx.GetIndex();
        for( n = pTblNd->GetIndex() + 1; n < nTmpEnd; ++n )
        {
            if( ( pNd = (*this)[ n ] )->IsCntntNode() )
                ((SwCntntNode*)pNd)->DelFrms();
            pNd->pStartOfSection = pTblNd;
        }
    }

    // dann die Tabellen Struktur teilweise aufbauen. Erstmal eine Line
    // in der alle Boxen stehen! Die korrekte Struktur kommt dann aus der
    // SaveStruct
    SwTableBoxFmt* pBoxFmt = GetDoc()->MakeTableBoxFmt();
    SwTableLineFmt* pLineFmt = GetDoc()->MakeTableLineFmt();
    SwTableLine* pLine = new SwTableLine( pLineFmt, rSavedData.Count(), 0 );
    pTblNd->GetTable().GetTabLines().C40_INSERT( SwTableLine, pLine, 0 );

    SvULongs aBkmkArr( 0, 4 );
    for( sal_uInt16 n = rSavedData.Count(); n; )
    {
        SwTblToTxtSave* pSave = rSavedData[ --n ];
        // if the start node was merged with last from prev. cell,
        // subtract 1 from index to get the merged paragraph, and split that
        aSttIdx = pSave->m_nSttNd - ( ( USHRT_MAX != pSave->m_nCntnt ) ? 1 : 0);
        SwTxtNode* pTxtNd = aSttIdx.GetNode().GetTxtNode();

        if( USHRT_MAX != pSave->m_nCntnt )
        {
            // an der ContentPosition splitten, das vorherige Zeichen
            // loeschen (ist der Trenner!)
            OSL_ENSURE( pTxtNd, "Wo ist der TextNode geblieben?" );
            SwIndex aCntPos( pTxtNd, pSave->m_nCntnt - 1 );

            pTxtNd->EraseText( aCntPos, 1 );
            SwCntntNode* pNewNd = pTxtNd->SplitCntntNode(
                                        SwPosition( aSttIdx, aCntPos ));
            if( aBkmkArr.Count() )
                _RestoreCntntIdx( aBkmkArr, *pNewNd, pSave->m_nCntnt,
                                                     pSave->m_nCntnt + 1 );
        }
        else
        {
            if( aBkmkArr.Count() )
                aBkmkArr.Remove( 0, aBkmkArr.Count() );
            if( pTxtNd )
                _SaveCntntIdx( GetDoc(), aSttIdx.GetIndex(),
                                pTxtNd->GetTxt().Len(), aBkmkArr );
        }

        if( pTxtNd )
        {
            // METADATA: restore
            pTxtNd->GetTxtNode()->RestoreMetadata(pSave->m_pMetadataUndoStart);
            if( pTxtNd->HasSwAttrSet() )
                pTxtNd->ResetAllAttr();

            if( pTxtNd->GetpSwpHints() )
                pTxtNd->ClearSwpHintsArr( false );
        }

        if( pSave->m_pHstry )
        {
            sal_uInt16 nTmpEnd = pSave->m_pHstry->GetTmpEnd();
            pSave->m_pHstry->TmpRollback( GetDoc(), 0 );
            pSave->m_pHstry->SetTmpEnd( nTmpEnd );
        }

        // METADATA: restore
        // end points to node after cell
        if ( pSave->m_nEndNd - 1 > pSave->m_nSttNd )
        {
            SwTxtNode* pLastNode = (*this)[ pSave->m_nEndNd - 1 ]->GetTxtNode();
            if (pLastNode)
            {
                pLastNode->RestoreMetadata(pSave->m_pMetadataUndoEnd);
            }
        }

        aEndIdx = pSave->m_nEndNd;
        SwStartNode* pSttNd = new SwStartNode( aSttIdx, ND_STARTNODE,
                                                SwTableBoxStartNode );
        pSttNd->pStartOfSection = pTblNd;
        new SwEndNode( aEndIdx, *pSttNd );

        for( sal_uLong i = aSttIdx.GetIndex(); i < aEndIdx.GetIndex()-1; ++i )
        {
            pNd = (*this)[ i ];
            pNd->pStartOfSection = pSttNd;
            if( pNd->IsStartNode() )
                i = pNd->EndOfSectionIndex();
        }

        SwTableBox* pBox = new SwTableBox( pBoxFmt, *pSttNd, pLine );
        pLine->GetTabBoxes().C40_INSERT( SwTableBox, pBox, 0 );
    }
    return pTblNd;
}


void SwUndoTblToTxt::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());

    pPam->GetPoint()->nNode = nSttNd;
    pPam->GetPoint()->nContent.Assign( 0, 0 );
    SwNodeIndex aSaveIdx( pPam->GetPoint()->nNode, -1 );

    pPam->SetMark();            // alle Indizies abmelden
    pPam->DeleteMark();

    SwTableNode* pTblNd = pPam->GetNode()->GetTableNode();
    OSL_ENSURE( pTblNd, "keinen TableNode gefunden" );

    if( pTblNd->GetTable().IsA( TYPE( SwDDETable )) )
        pDDEFldType = (SwDDEFieldType*)((SwDDETable&)pTblNd->GetTable()).
                                                GetDDEFldType()->Copy();

    rDoc.TableToText( pTblNd, cTrenner );

    aSaveIdx++;
    SwCntntNode* pCNd = aSaveIdx.GetNode().GetCntntNode();
    if( !pCNd && 0 == ( pCNd = rDoc.GetNodes().GoNext( &aSaveIdx ) ) &&
        0 == ( pCNd = rDoc.GetNodes().GoPrevious( &aSaveIdx )) )
    {
        OSL_FAIL( "wo steht denn nun der TextNode" );
    }

    pPam->GetPoint()->nNode = aSaveIdx;
    pPam->GetPoint()->nContent.Assign( pCNd, 0 );

    pPam->SetMark();            // alle Indizies abmelden
    pPam->DeleteMark();
}


void SwUndoTblToTxt::RepeatImpl(::sw::RepeatContext & rContext)
{
    SwPaM *const pPam = & rContext.GetRepeatPaM();
    SwTableNode *const pTblNd = pPam->GetNode()->FindTableNode();
    if( pTblNd )
    {
        // move cursor out of table
        pPam->GetPoint()->nNode = *pTblNd->EndOfSectionNode();
        pPam->Move( fnMoveForward, fnGoCntnt );
        pPam->SetMark();
        pPam->DeleteMark();

        rContext.GetDoc().TableToText( pTblNd, cTrenner );
    }
}

void SwUndoTblToTxt::SetRange( const SwNodeRange& rRg )
{
    nSttNd = rRg.aStart.GetIndex();
    nEndNd = rRg.aEnd.GetIndex();
}

void SwUndoTblToTxt::AddBoxPos( SwDoc& rDoc, sal_uLong nNdIdx, sal_uLong nEndIdx, xub_StrLen nCntntIdx )
{
    SwTblToTxtSave* pNew = new SwTblToTxtSave( rDoc, nNdIdx, nEndIdx, nCntntIdx );
    pBoxSaves->Insert( pNew, pBoxSaves->Count() );
}

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

SwUndoTxtToTbl::SwUndoTxtToTbl( const SwPaM& rRg,
                                const SwInsertTableOptions& rInsTblOpts,
                                sal_Unicode cCh, sal_uInt16 nAdj,
                                const SwTableAutoFmt* pAFmt )
    : SwUndo( UNDO_TEXTTOTABLE ), SwUndRng( rRg ), aInsTblOpts( rInsTblOpts ),
      pDelBoxes( 0 ), pAutoFmt( 0 ),
      pHistory( 0 ), cTrenner( cCh ), nAdjust( nAdj )
{
    if( pAFmt )
        pAutoFmt = new SwTableAutoFmt( *pAFmt );

    const SwPosition* pEnd = rRg.End();
    SwNodes& rNds = rRg.GetDoc()->GetNodes();
    bSplitEnd = pEnd->nContent.GetIndex() && ( pEnd->nContent.GetIndex()
                        != pEnd->nNode.GetNode().GetCntntNode()->Len() ||
                pEnd->nNode.GetIndex() >= rNds.GetEndOfContent().GetIndex()-1 );
}

SwUndoTxtToTbl::~SwUndoTxtToTbl()
{
    delete pDelBoxes;
    delete pAutoFmt;
}

void SwUndoTxtToTbl::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();

    sal_uLong nTblNd = nSttNode;
    if( nSttCntnt )
        ++nTblNd;		// Node wurde vorher gesplittet
    SwNodeIndex aIdx( rDoc.GetNodes(), nTblNd );
    SwTableNode *const pTNd = aIdx.GetNode().GetTableNode();
    OSL_ENSURE( pTNd, "keinen Tabellen-Node gefunden" );

    RemoveIdxFromSection( rDoc, nTblNd );

    sTblNm = pTNd->GetTable().GetFrmFmt()->GetName();

    if( pHistory )
    {
        pHistory->TmpRollback( &rDoc, 0 );
        pHistory->SetTmpEnd( pHistory->Count() );
    }

    if( pDelBoxes )
    {
        SwTable& rTbl = pTNd->GetTable();
        for( sal_uInt16 n = pDelBoxes->Count(); n; )
        {
            SwTableBox* pBox = rTbl.GetTblBox( (*pDelBoxes)[ --n ] );
            if( pBox )
                ::_DeleteBox( rTbl, pBox, 0, sal_False, sal_False );
            else {
                OSL_ENSURE( !this, "Wo ist die Box geblieben?" );
            }
        }
    }

    SwNodeIndex aEndIdx( *pTNd->EndOfSectionNode() );
    rDoc.TableToText( pTNd, 0x0b == cTrenner ? 0x09 : cTrenner );

    // join again at start?
    SwPaM aPam(rDoc.GetNodes().GetEndOfContent());
    SwPosition *const pPos = aPam.GetPoint();
    if( nSttCntnt )
    {
        pPos->nNode = nTblNd;
        pPos->nContent.Assign(pPos->nNode.GetNode().GetCntntNode(), 0);
        if (aPam.Move(fnMoveBackward, fnGoCntnt))
        {
            SwNodeIndex & rIdx = aPam.GetPoint()->nNode;

            // dann die Crsr/etc. nochmal relativ verschieben
            RemoveIdxRel( rIdx.GetIndex()+1, *pPos );

            rIdx.GetNode().GetCntntNode()->JoinNext();
        }
    }

    // join again at end?
    if( bSplitEnd )
    {
        SwNodeIndex& rIdx = pPos->nNode;
        rIdx = nEndNode;
        SwTxtNode* pTxtNd = rIdx.GetNode().GetTxtNode();
        if( pTxtNd && pTxtNd->CanJoinNext() )
        {
            aPam.GetMark()->nContent.Assign( 0, 0 );
            aPam.GetPoint()->nContent.Assign( 0, 0 );

            // dann die Crsr/etc. nochmal relativ verschieben
            pPos->nContent.Assign( pTxtNd, pTxtNd->GetTxt().Len() );
            RemoveIdxRel( nEndNode + 1, *pPos );

            pTxtNd->JoinNext();
        }
    }

    AddUndoRedoPaM(rContext);
}


void SwUndoTxtToTbl::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwPaM & rPam( AddUndoRedoPaM(rContext) );
    RemoveIdxFromRange(rPam, false);
    SetPaM(rPam);

    SwTable const*const pTable = rContext.GetDoc().TextToTable(
                aInsTblOpts, rPam, cTrenner, nAdjust, pAutoFmt );
    ((SwFrmFmt*)pTable->GetFrmFmt())->SetName( sTblNm );
}


void SwUndoTxtToTbl::RepeatImpl(::sw::RepeatContext & rContext)
{
    // no Table In Table
    if (!rContext.GetRepeatPaM().GetNode()->FindTableNode())
    {
        rContext.GetDoc().TextToTable( aInsTblOpts, rContext.GetRepeatPaM(),
                                        cTrenner, nAdjust,
                                        pAutoFmt );
    }
}

void SwUndoTxtToTbl::AddFillBox( const SwTableBox& rBox )
{
    if( !pDelBoxes )
        pDelBoxes = new SvULongs;
    pDelBoxes->Insert( rBox.GetSttIdx(), pDelBoxes->Count() );
}

SwHistory& SwUndoTxtToTbl::GetHistory()
{
    if( !pHistory )
        pHistory = new SwHistory;
    return *pHistory;
}

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

SwUndoTblHeadline::SwUndoTblHeadline( const SwTable& rTbl, sal_uInt16 nOldHdl,
                                      sal_uInt16 nNewHdl )
    : SwUndo( UNDO_TABLEHEADLINE ),
    nOldHeadline( nOldHdl ),
    nNewHeadline( nNewHdl )
{
    OSL_ENSURE( rTbl.GetTabSortBoxes().Count(), "Tabelle ohne Inhalt" );
    const SwStartNode *pSttNd = rTbl.GetTabSortBoxes()[ 0 ]->GetSttNd();
    OSL_ENSURE( pSttNd, "Box ohne Inhalt" );

    nTblNd = pSttNd->StartOfSectionIndex();
}

void SwUndoTblHeadline::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwTableNode* pTNd = rDoc.GetNodes()[ nTblNd ]->GetTableNode();
    OSL_ENSURE( pTNd, "keinen Tabellen-Node gefunden" );

    rDoc.SetRowsToRepeat( pTNd->GetTable(), nOldHeadline );
}

void SwUndoTblHeadline::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();

    SwTableNode* pTNd = rDoc.GetNodes()[ nTblNd ]->GetTableNode();
    OSL_ENSURE( pTNd, "keinen Tabellen-Node gefunden" );

    rDoc.SetRowsToRepeat( pTNd->GetTable(), nNewHeadline );
}

void SwUndoTblHeadline::RepeatImpl(::sw::RepeatContext & rContext)
{
    SwTableNode *const pTblNd =
        rContext.GetRepeatPaM().GetNode()->FindTableNode();
    if( pTblNd )
    {
        rContext.GetDoc().SetRowsToRepeat( pTblNd->GetTable(), nNewHeadline );
    }
}


//////////////////////////////////////////////////////////////////////////


_SaveTable::_SaveTable( const SwTable& rTbl, sal_uInt16 nLnCnt, sal_Bool bSaveFml )
    : aTblSet( *rTbl.GetFrmFmt()->GetAttrSet().GetPool(), aTableSetRange ),
    pSwTable( &rTbl ), nLineCount( nLnCnt ), bSaveFormula( bSaveFml )
{
    bModifyBox = sal_False;
    bNewModel = rTbl.IsNewModel();
    aTblSet.Put( rTbl.GetFrmFmt()->GetAttrSet() );
    pLine = new _SaveLine( 0, *rTbl.GetTabLines()[ 0 ], *this );

    _SaveLine* pLn = pLine;
    if( USHRT_MAX == nLnCnt )
        nLnCnt = rTbl.GetTabLines().Count();
    for( sal_uInt16 n = 1; n < nLnCnt; ++n )
        pLn = new _SaveLine( pLn, *rTbl.GetTabLines()[ n ], *this );

    aFrmFmts.Remove( 0, aFrmFmts.Count() );
    pSwTable = 0;
}


_SaveTable::~_SaveTable()
{
    delete pLine;
}


sal_uInt16 _SaveTable::AddFmt( SwFrmFmt* pFmt, bool bIsLine )
{
    sal_uInt16 nRet = aFrmFmts.GetPos( pFmt );
    if( USHRT_MAX == nRet )
    {
        // Kopie vom ItemSet anlegen
        SfxItemSet* pSet = new SfxItemSet( *pFmt->GetAttrSet().GetPool(),
            bIsLine ? aTableLineSetRange : aTableBoxSetRange );
        pSet->Put( pFmt->GetAttrSet() );
        //JP 20.04.98: Bug 49502 - wenn eine Formel gesetzt ist, nie den
        //				Value mit sichern. Der muss gegebenfalls neu
        //				errechnet werden!
        //JP 30.07.98: Bug 54295 - Formeln immer im Klartext speichern
        const SfxPoolItem* pItem;
        if( SFX_ITEM_SET == pSet->GetItemState( RES_BOXATR_FORMULA, sal_True, &pItem ))
        {
            pSet->ClearItem( RES_BOXATR_VALUE );
            if( pSwTable && bSaveFormula )
            {
                SwTableFmlUpdate aMsgHnt( pSwTable );
                aMsgHnt.eFlags = TBL_BOXNAME;
                ((SwTblBoxFormula*)pItem)->ChgDefinedIn( pFmt );
                ((SwTblBoxFormula*)pItem)->ChangeState( &aMsgHnt );
                ((SwTblBoxFormula*)pItem)->ChgDefinedIn( 0 );
            }
        }
        aSets.Insert( pSet, (nRet = aSets.Count() ) );
        aFrmFmts.Insert( pFmt, nRet );
    }
    return nRet;
}


void _SaveTable::RestoreAttr( SwTable& rTbl, sal_Bool bMdfyBox )
{
    sal_uInt16 n;

    bModifyBox = bMdfyBox;

    // zuerst die Attribute des TabellenFrmFormates zurueck holen
    SwFrmFmt* pFmt = rTbl.GetFrmFmt();
    SfxItemSet& rFmtSet  = (SfxItemSet&)pFmt->GetAttrSet();
    rFmtSet.ClearItem();
    rFmtSet.Put( aTblSet );

    if( pFmt->IsInCache() )
    {
        SwFrm::GetCache().Delete( pFmt );
        pFmt->SetInCache( sal_False );
    }

    // zur Sicherheit alle Tableframes invalidieren
    SwIterator<SwTabFrm,SwFmt> aIter( *pFmt );
    for( SwTabFrm* pLast = aIter.First(); pLast; pLast = aIter.Next() )
        if( pLast->GetTable() == &rTbl )
        {
            pLast->InvalidateAll();
            pLast->SetCompletePaint();
        }

    // FrmFmts mit Defaults (0) fuellen
    pFmt = 0;
    for( n = aSets.Count(); n; --n )
        aFrmFmts.Insert( pFmt, aFrmFmts.Count() );

    sal_uInt16 nLnCnt = nLineCount;
    if( USHRT_MAX == nLnCnt )
        nLnCnt = rTbl.GetTabLines().Count();

    _SaveLine* pLn = pLine;
    for( n = 0; n < nLnCnt; ++n, pLn = pLn->pNext )
    {
        if( !pLn )
        {
            OSL_ENSURE( !this, "Anzahl der Lines hat sich veraendert" );
            break;
        }

        pLn->RestoreAttr( *rTbl.GetTabLines()[ n ], *this );
    }

    aFrmFmts.Remove( 0, aFrmFmts.Count() );
    bModifyBox = sal_False;
}


void _SaveTable::SaveCntntAttrs( SwDoc* pDoc )
{
    pLine->SaveCntntAttrs( pDoc );
}


void _SaveTable::CreateNew( SwTable& rTbl, sal_Bool bCreateFrms,
                            sal_Bool bRestoreChart )
{
    sal_uInt16 n;

    _FndBox aTmpBox( 0, 0 );
    //if( bRestoreChart )
    //    // ? TL_CHART2: notification or locking of controller required ?
    aTmpBox.DelFrms( rTbl );

    // zuerst die Attribute des TabellenFrmFormates zurueck holen
    SwFrmFmt* pFmt = rTbl.GetFrmFmt();
    SfxItemSet& rFmtSet  = (SfxItemSet&)pFmt->GetAttrSet();
    rFmtSet.ClearItem();
    rFmtSet.Put( aTblSet );

    if( pFmt->IsInCache() )
    {
        SwFrm::GetCache().Delete( pFmt );
        pFmt->SetInCache( sal_False );
    }

    // SwTableBox muss ein Format haben!!
    SwTableBox aParent( (SwTableBoxFmt*)pFmt, rTbl.GetTabLines().Count(), 0 );

    // FrmFmts mit Defaults (0) fuellen
    pFmt = 0;
    for( n = aSets.Count(); n; --n )
        aFrmFmts.Insert( pFmt, aFrmFmts.Count() );

    pLine->CreateNew( rTbl, aParent, *this );
    aFrmFmts.Remove( 0, aFrmFmts.Count() );

    // die neuen Lines eintragen, die alten loeschen
    sal_uInt16 nOldLines = nLineCount;
    if( USHRT_MAX == nLineCount )
        nOldLines = rTbl.GetTabLines().Count();

    SwDoc *pDoc = rTbl.GetFrmFmt()->GetDoc();
    SwChartDataProvider *pPCD = pDoc->GetChartDataProvider();
    for( n = 0; n < aParent.GetTabLines().Count(); ++n )
    {
        SwTableLine* pLn = aParent.GetTabLines()[ n ];
        pLn->SetUpper( 0 );
        if( n < nOldLines )
        {
            SwTableLine* pOld = rTbl.GetTabLines()[ n ];

            // TL_CHART2: notify chart about boxes to be removed
            const SwTableBoxes &rBoxes = pOld->GetTabBoxes();
            sal_uInt16 nBoxes = rBoxes.Count();
            for (sal_uInt16 k = 0;  k < nBoxes;  ++k)
            {
                SwTableBox *pBox = rBoxes[k];
                if (pPCD)
                    pPCD->DeleteBox( &rTbl, *pBox );
            }

            rTbl.GetTabLines().C40_REPLACE( SwTableLine, pLn, n );
            delete pOld;
        }
        else
            rTbl.GetTabLines().C40_INSERT( SwTableLine, pLn, n );
    }

    if( n < nOldLines )
    {
        // remove remaining lines...

        for (sal_uInt16 k1 = 0; k1 < nOldLines - n;  ++k1)
        {
            const SwTableBoxes &rBoxes = rTbl.GetTabLines()[n + k1]->GetTabBoxes();
            sal_uInt16 nBoxes = rBoxes.Count();
            for (sal_uInt16 k2 = 0;  k2 < nBoxes;  ++k2)
            {
                SwTableBox *pBox = rBoxes[k2];
                // TL_CHART2: notify chart about boxes to be removed
                if (pPCD)
                    pPCD->DeleteBox( &rTbl, *pBox );
            }
        }

        rTbl.GetTabLines().DeleteAndDestroy( n, nOldLines - n );
    }

    aParent.GetTabLines().Remove( 0, n );

    if( bCreateFrms )
        aTmpBox.MakeFrms( rTbl );
    if( bRestoreChart )
    {
        // TL_CHART2: need to inform chart of probably changed cell names
        pDoc->UpdateCharts( rTbl.GetFrmFmt()->GetName() );
    }
}


void _SaveTable::NewFrmFmt( const SwTableLine* pTblLn, const SwTableBox* pTblBx,
                            sal_uInt16 nFmtPos, SwFrmFmt* pOldFmt )
{
    SwDoc* pDoc = pOldFmt->GetDoc();

    SwFrmFmt* pFmt = aFrmFmts[ nFmtPos ];
    if( !pFmt )
    {
        if( pTblLn )
            pFmt = pDoc->MakeTableLineFmt();
        else
            pFmt = pDoc->MakeTableBoxFmt();
        pFmt->SetFmtAttr( *aSets[ nFmtPos ] );
        aFrmFmts.Replace( pFmt, nFmtPos );
    }

    //Erstmal die Frms ummelden.
    SwIterator<SwTabFrm,SwFmt> aIter( *pOldFmt );
    for( SwFrm* pLast = aIter.First(); pLast; pLast = aIter.Next() )
    {
        if( pTblLn ? ((SwRowFrm*)pLast)->GetTabLine() == pTblLn
                    : ((SwCellFrm*)pLast)->GetTabBox() == pTblBx )
        {
            pLast->RegisterToFormat(*pFmt);
            pLast->InvalidateAll();
            pLast->ReinitializeFrmSizeAttrFlags();
            if ( !pTblLn )
            {
                ((SwCellFrm*)pLast)->SetDerivedVert( sal_False );
                ((SwCellFrm*)pLast)->CheckDirChange();
            }
        }
    }

    //Jetzt noch mich selbst ummelden.
    if ( pTblLn )
        const_cast<SwTableLine*>(pTblLn)->RegisterToFormat( *pFmt );
    else if ( pTblBx )
        const_cast<SwTableBox*>(pTblBx)->RegisterToFormat( *pFmt );

    if( bModifyBox && !pTblLn )
    {
        const SfxPoolItem& rOld = pOldFmt->GetFmtAttr( RES_BOXATR_FORMAT ),
                         & rNew = pFmt->GetFmtAttr( RES_BOXATR_FORMAT );
        if( rOld != rNew )
            pFmt->ModifyNotification( (SfxPoolItem*)&rOld, (SfxPoolItem*)&rNew );
    }

    if( !pOldFmt->GetDepends() )
        delete pOldFmt;

}


_SaveLine::_SaveLine( _SaveLine* pPrev, const SwTableLine& rLine, _SaveTable& rSTbl )
    : pNext( 0 )
{
    if( pPrev )
        pPrev->pNext = this;

    nItemSet = rSTbl.AddFmt( rLine.GetFrmFmt(), true );

    pBox = new _SaveBox( 0, *rLine.GetTabBoxes()[ 0 ], rSTbl );
    _SaveBox* pBx = pBox;
    for( sal_uInt16 n = 1; n < rLine.GetTabBoxes().Count(); ++n )
        pBx = new _SaveBox( pBx, *rLine.GetTabBoxes()[ n ], rSTbl );
}


_SaveLine::~_SaveLine()
{
    delete pBox;
    delete pNext;
}


void _SaveLine::RestoreAttr( SwTableLine& rLine, _SaveTable& rSTbl )
{
    rSTbl.NewFrmFmt( &rLine, 0, nItemSet, rLine.GetFrmFmt() );

    _SaveBox* pBx = pBox;
    for( sal_uInt16 n = 0; n < rLine.GetTabBoxes().Count(); ++n, pBx = pBx->pNext )
    {
        if( !pBx )
        {
            OSL_ENSURE( !this, "Anzahl der Boxen hat sich veraendert" );
            break;
        }
        pBx->RestoreAttr( *rLine.GetTabBoxes()[ n ], rSTbl );
    }
}


void _SaveLine::SaveCntntAttrs( SwDoc* pDoc )
{
    pBox->SaveCntntAttrs( pDoc );
    if( pNext )
        pNext->SaveCntntAttrs( pDoc );
}


void _SaveLine::CreateNew( SwTable& rTbl, SwTableBox& rParent, _SaveTable& rSTbl )
{
    SwTableLineFmt* pFmt = (SwTableLineFmt*)rSTbl.aFrmFmts[ nItemSet ];
    if( !pFmt )
    {
        SwDoc* pDoc = rTbl.GetFrmFmt()->GetDoc();
        pFmt = pDoc->MakeTableLineFmt();
        pFmt->SetFmtAttr( *rSTbl.aSets[ nItemSet ] );
        rSTbl.aFrmFmts.Replace( pFmt, nItemSet );
    }
    SwTableLine* pNew = new SwTableLine( pFmt, 1, &rParent );

    rParent.GetTabLines().C40_INSERT( SwTableLine, pNew, rParent.GetTabLines().Count() );

    // HB, #127868# robustness: in some cases - which I
    // cannot reproduce nor see from the code - pNew seems
    // to be set to NULL in C40_INSERT.
    OSL_ENSURE(pNew, "Table line just created set to NULL in C40_INSERT");

    if (pNew)
    {
        pBox->CreateNew( rTbl, *pNew, rSTbl );
    }

    if( pNext )
        pNext->CreateNew( rTbl, rParent, rSTbl );
}


_SaveBox::_SaveBox( _SaveBox* pPrev, const SwTableBox& rBox, _SaveTable& rSTbl )
    : pNext( 0 ), nSttNode( ULONG_MAX ), nRowSpan(0)
{
    Ptrs.pLine = 0;

    if( pPrev )
        pPrev->pNext = this;

    nItemSet = rSTbl.AddFmt( rBox.GetFrmFmt(), false );

    if( rBox.GetSttNd() )
    {
        nSttNode = rBox.GetSttIdx();
        nRowSpan = rBox.getRowSpan();
    }
    else
    {
        Ptrs.pLine = new _SaveLine( 0, *rBox.GetTabLines()[ 0 ], rSTbl );

        _SaveLine* pLn = Ptrs.pLine;
        for( sal_uInt16 n = 1; n < rBox.GetTabLines().Count(); ++n )
            pLn = new _SaveLine( pLn, *rBox.GetTabLines()[ n ], rSTbl );
    }
}


_SaveBox::~_SaveBox()
{
    if( ULONG_MAX == nSttNode )		// keine EndBox
        delete Ptrs.pLine;
    else
        delete Ptrs.pCntntAttrs;
    delete pNext;
}


void _SaveBox::RestoreAttr( SwTableBox& rBox, _SaveTable& rSTbl )
{
    rSTbl.NewFrmFmt( 0, &rBox, nItemSet, rBox.GetFrmFmt() );

    if( ULONG_MAX == nSttNode )		// keine EndBox
    {
        if( !rBox.GetTabLines().Count() )
        {
            OSL_ENSURE( !this, "Anzahl der Lines hat sich veraendert" );
        }
        else
        {
            _SaveLine* pLn = Ptrs.pLine;
            for( sal_uInt16 n = 0; n < rBox.GetTabLines().Count(); ++n, pLn = pLn->pNext )
            {
                if( !pLn )
                {
                    OSL_ENSURE( !this, "Anzahl der Lines hat sich veraendert" );
                    break;
                }

                pLn->RestoreAttr( *rBox.GetTabLines()[ n ], rSTbl );
            }
        }
    }
    else if( rBox.GetSttNd() && rBox.GetSttIdx() == nSttNode )
    {
        if( Ptrs.pCntntAttrs )
        {
            SwNodes& rNds = rBox.GetFrmFmt()->GetDoc()->GetNodes();
            sal_uInt16 nSet = 0;
            sal_uLong nEnd = rBox.GetSttNd()->EndOfSectionIndex();
            for( sal_uLong n = nSttNode + 1; n < nEnd; ++n )
            {
                SwCntntNode* pCNd = rNds[ n ]->GetCntntNode();
                if( pCNd )
                {
                    SfxItemSet* pSet = (*Ptrs.pCntntAttrs)[ nSet++ ];
                    if( pSet )
                    {
                        sal_uInt16 *pRstAttr = aSave_BoxCntntSet;
                        while( *pRstAttr )
                        {
                            pCNd->ResetAttr( *pRstAttr, *(pRstAttr+1) );
                            pRstAttr += 2;
                        }
                        pCNd->SetAttr( *pSet );
                    }
                    else
                        pCNd->ResetAllAttr();
                }
            }
        }
    }
    else
    {
        OSL_ENSURE( !this, "Box nicht mehr am gleichen Node" );
    }
}


void _SaveBox::SaveCntntAttrs( SwDoc* pDoc )
{
    if( ULONG_MAX == nSttNode )		// keine EndBox
    {
        // weiter in der Line
        Ptrs.pLine->SaveCntntAttrs( pDoc );
    }
    else
    {
        sal_uLong nEnd = pDoc->GetNodes()[ nSttNode ]->EndOfSectionIndex();
        Ptrs.pCntntAttrs = new SfxItemSets( (sal_uInt8)(nEnd - nSttNode - 1 ), 5 );
        for( sal_uLong n = nSttNode + 1; n < nEnd; ++n )
        {
            SwCntntNode* pCNd = pDoc->GetNodes()[ n ]->GetCntntNode();
            if( pCNd )
            {
                SfxItemSet* pSet = 0;
                if( pCNd->HasSwAttrSet() )
                {
                    pSet = new SfxItemSet( pDoc->GetAttrPool(),
                                            aSave_BoxCntntSet );
                    pSet->Put( *pCNd->GetpSwAttrSet() );
                }

                Ptrs.pCntntAttrs->Insert( pSet, Ptrs.pCntntAttrs->Count() );
            }
        }
    }
    if( pNext )
        pNext->SaveCntntAttrs( pDoc );
}


void _SaveBox::CreateNew( SwTable& rTbl, SwTableLine& rParent, _SaveTable& rSTbl )
{
    SwTableBoxFmt* pFmt = (SwTableBoxFmt*)rSTbl.aFrmFmts[ nItemSet ];
    if( !pFmt )
    {
        SwDoc* pDoc = rTbl.GetFrmFmt()->GetDoc();
        pFmt = pDoc->MakeTableBoxFmt();
        pFmt->SetFmtAttr( *rSTbl.aSets[ nItemSet ] );
        rSTbl.aFrmFmts.Replace( pFmt, nItemSet );
    }

    if( ULONG_MAX == nSttNode )		// keine EndBox
    {
        SwTableBox* pNew = new SwTableBox( pFmt, 1, &rParent );
        rParent.GetTabBoxes().C40_INSERT( SwTableBox, pNew, rParent.GetTabBoxes().Count() );

        Ptrs.pLine->CreateNew( rTbl, *pNew, rSTbl );
    }
    else
    {
        // Box zum StartNode in der alten Tabelle suchen
        SwTableBox* pBox = rTbl.GetTblBox( nSttNode );
        OSL_ENSURE( pBox, "Wo ist meine TabellenBox geblieben?" );

        SwFrmFmt* pOld = pBox->GetFrmFmt();
        pBox->RegisterToFormat( *pFmt );
        if( !pOld->GetDepends() )
            delete pOld;

        pBox->setRowSpan( nRowSpan );

        SwTableBoxes* pTBoxes = &pBox->GetUpper()->GetTabBoxes();
        pTBoxes->Remove( pTBoxes->C40_GETPOS( SwTableBox, pBox ) );

        pBox->SetUpper( &rParent );
        pTBoxes = &rParent.GetTabBoxes();
        pTBoxes->C40_INSERT( SwTableBox, pBox, pTBoxes->Count() );
    }

    if( pNext )
        pNext->CreateNew( rTbl, rParent, rSTbl );
}


//////////////////////////////////////////////////////////////////////////

// UndoObject fuer Attribut Aenderung an der Tabelle


SwUndoAttrTbl::SwUndoAttrTbl( const SwTableNode& rTblNd, sal_Bool bClearTabCols )
    : SwUndo( UNDO_TABLE_ATTR ),
    nSttNode( rTblNd.GetIndex() )
{
    bClearTabCol = bClearTabCols;
    pSaveTbl = new _SaveTable( rTblNd.GetTable() );
}

SwUndoAttrTbl::~SwUndoAttrTbl()
{
    delete pSaveTbl;
}

void SwUndoAttrTbl::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwTableNode* pTblNd = rDoc.GetNodes()[ nSttNode ]->GetTableNode();
    OSL_ENSURE( pTblNd, "kein TabellenNode" );

    if (pTblNd)
    {
        _SaveTable* pOrig = new _SaveTable( pTblNd->GetTable() );
        pSaveTbl->RestoreAttr( pTblNd->GetTable() );
        delete pSaveTbl;
        pSaveTbl = pOrig;
    }

    if( bClearTabCol )
        ClearFEShellTabCols();
}

void SwUndoAttrTbl::RedoImpl(::sw::UndoRedoContext & rContext)
{
    UndoImpl(rContext);
}


//////////////////////////////////////////////////////////////////////////

// UndoObject fuer AutoFormat an der Tabelle


SwUndoTblAutoFmt::SwUndoTblAutoFmt( const SwTableNode& rTblNd,
                                    const SwTableAutoFmt& rAFmt )
    : SwUndo( UNDO_TABLE_AUTOFMT ),
    nSttNode( rTblNd.GetIndex() ),
    bSaveCntntAttr( sal_False )
{
    pSaveTbl = new _SaveTable( rTblNd.GetTable() );

    if( rAFmt.IsFont() || rAFmt.IsJustify() )
    {
        // dann auch noch ueber die ContentNodes der EndBoxen und
        // und alle Absatz-Attribute zusammen sammeln
        pSaveTbl->SaveCntntAttrs( (SwDoc*)rTblNd.GetDoc() );
        bSaveCntntAttr = sal_True;
    }
}

SwUndoTblAutoFmt::~SwUndoTblAutoFmt()
{
    delete pSaveTbl;
}

void SwUndoTblAutoFmt::SaveBoxCntnt( const SwTableBox& rBox )
{
    ::boost::shared_ptr<SwUndoTblNumFmt> const p(new SwUndoTblNumFmt(rBox));
    m_Undos.push_back(p);
}


void
SwUndoTblAutoFmt::UndoRedo(bool const bUndo, ::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwTableNode* pTblNd = rDoc.GetNodes()[ nSttNode ]->GetTableNode();
    OSL_ENSURE( pTblNd, "kein TabellenNode" );

    _SaveTable* pOrig = new _SaveTable( pTblNd->GetTable() );
        // dann auch noch ueber die ContentNodes der EndBoxen und
        // und alle Absatz-Attribute zusammen sammeln
    if( bSaveCntntAttr )
        pOrig->SaveCntntAttrs( &rDoc );

    if (bUndo)
    {
        for (size_t n = m_Undos.size(); 0 < n; --n)
        {
            m_Undos.at(n-1)->UndoImpl(rContext);
        }
    }

    pSaveTbl->RestoreAttr( pTblNd->GetTable(), !bUndo );
    delete pSaveTbl;
    pSaveTbl = pOrig;
}

void SwUndoTblAutoFmt::UndoImpl(::sw::UndoRedoContext & rContext)
{
    UndoRedo(true, rContext);
}

void SwUndoTblAutoFmt::RedoImpl(::sw::UndoRedoContext & rContext)
{
    UndoRedo(false, rContext);
}


//////////////////////////////////////////////////////////////////////////

SwUndoTblNdsChg::SwUndoTblNdsChg( SwUndoId nAction,
                                    const SwSelBoxes& rBoxes,
                                    const SwTableNode& rTblNd,
                                    long nMn, long nMx,
                                    sal_uInt16 nCnt, sal_Bool bFlg, sal_Bool bSmHght )
    : SwUndo( nAction ),
    aBoxes( rBoxes.Count() < 255 ? (sal_uInt8)rBoxes.Count() : 255, 10 ),
    nMin( nMn ), nMax( nMx ),
    nSttNode( rTblNd.GetIndex() ), nCurrBox( 0 ),
    nCount( nCnt ), nRelDiff( 0 ), nAbsDiff( 0 ),
    nSetColType( USHRT_MAX ),
    bFlag( bFlg ),
    bSameHeight( bSmHght )
{
    Ptrs.pNewSttNds = 0;

    const SwTable& rTbl = rTblNd.GetTable();
    pSaveTbl = new _SaveTable( rTbl );

    // und die Selektion merken
    for( sal_uInt16 n = 0; n < rBoxes.Count(); ++n )
        aBoxes.Insert( rBoxes[n]->GetSttIdx(), n );
}


SwUndoTblNdsChg::SwUndoTblNdsChg( SwUndoId nAction,
                                    const SwSelBoxes& rBoxes,
                                    const SwTableNode& rTblNd )
    : SwUndo( nAction ),
    aBoxes( rBoxes.Count() < 255 ? (sal_uInt8)rBoxes.Count() : 255, 10 ),
    nMin( 0 ), nMax( 0 ),
    nSttNode( rTblNd.GetIndex() ), nCurrBox( 0 ),
    nCount( 0 ), nRelDiff( 0 ), nAbsDiff( 0 ),
    nSetColType( USHRT_MAX ),
    bFlag( sal_False ),
    bSameHeight( sal_False )
{
    Ptrs.pNewSttNds = 0;

    const SwTable& rTbl = rTblNd.GetTable();
    pSaveTbl = new _SaveTable( rTbl );

    // und die Selektion merken
    for( sal_uInt16 n = 0; n < rBoxes.Count(); ++n )
        aBoxes.Insert( rBoxes[n]->GetSttIdx(), n );
}

void SwUndoTblNdsChg::ReNewBoxes( const SwSelBoxes& rBoxes )
{
    if( rBoxes.Count() != aBoxes.Count() )
    {
        aBoxes.Remove( 0, aBoxes.Count() );
        for( sal_uInt16 n = 0; n < rBoxes.Count(); ++n )
            aBoxes.Insert( rBoxes[n]->GetSttIdx(), n );
    }
}

SwUndoTblNdsChg::~SwUndoTblNdsChg()
{
    delete pSaveTbl;

    if( IsDelBox() )
        delete Ptrs.pDelSects;
    else
        delete Ptrs.pNewSttNds;
}

void SwUndoTblNdsChg::SaveNewBoxes( const SwTableNode& rTblNd,
                                    const SwTableSortBoxes& rOld )
{
    const SwTable& rTbl = rTblNd.GetTable();
    const SwTableSortBoxes& rTblBoxes = rTbl.GetTabSortBoxes();
    sal_uInt16 n;
    sal_uInt16 i;

    OSL_ENSURE( ! IsDelBox(), "falsche Action" );
    Ptrs.pNewSttNds = new SvULongs( (sal_uInt8)(rTblBoxes.Count() - rOld.Count()), 5 );

    for( n = 0, i = 0; n < rOld.Count(); ++i )
    {
        if( rOld[ n ] == rTblBoxes[ i ] )
            ++n;
        else
            // neue Box: sortiert einfuegen!!
            InsertSort( *Ptrs.pNewSttNds, rTblBoxes[ i ]->GetSttIdx() );
    }

    for( ; i < rTblBoxes.Count(); ++i )
        // neue Box: sortiert einfuegen!!
        InsertSort( *Ptrs.pNewSttNds, rTblBoxes[ i ]->GetSttIdx() );
}


SwTableLine* lcl_FindTableLine( const SwTable& rTable,
                                const SwTableBox& rBox )
{
    SwTableLine* pRet = NULL;
    // i63949: For nested cells we have to take nLineNo - 1, too, not 0!
    const SwTableLines &rTableLines = ( rBox.GetUpper()->GetUpper() != NULL ) ?
                                  rBox.GetUpper()->GetUpper()->GetTabLines()
                                : rTable.GetTabLines();
    const SwTableLine* pLine = rBox.GetUpper();
    sal_uInt16 nLineNo = rTableLines.C40_GETPOS( SwTableLine, pLine );
    pRet = rTableLines[nLineNo - 1];

    return pRet;
}

const SwTableLines& lcl_FindParentLines( const SwTable& rTable,
                                       const SwTableBox& rBox )
{
    const SwTableLines& rRet =
        ( rBox.GetUpper()->GetUpper() != NULL ) ?
            rBox.GetUpper()->GetUpper()->GetTabLines() :
            rTable.GetTabLines();

    return rRet;
}


void SwUndoTblNdsChg::SaveNewBoxes( const SwTableNode& rTblNd,
                                    const SwTableSortBoxes& rOld,
                                    const SwSelBoxes& rBoxes,
                                    const SvULongs& rNodeCnts )
{
    const SwTable& rTbl = rTblNd.GetTable();
    const SwTableSortBoxes& rTblBoxes = rTbl.GetTabSortBoxes();

    OSL_ENSURE( ! IsDelBox(), "falsche Action" );
    Ptrs.pNewSttNds = new SvULongs( (sal_uInt8)(rTblBoxes.Count() - rOld.Count()), 5 );

    OSL_ENSURE( rTbl.IsNewModel() || rOld.Count() + nCount * rBoxes.Count() == rTblBoxes.Count(),
        "unexpected boxes" );
    OSL_ENSURE( rOld.Count() <= rTblBoxes.Count(), "more unexpected boxes" );
    for( sal_uInt16 n = 0, i = 0; i < rTblBoxes.Count(); ++i )
    {
        if( ( n < rOld.Count() ) &&
            ( rOld[ n ] == rTblBoxes[ i ] ) )
        {
            // box already known? Then nothing to be done.
            ++n;
        }
        else
        {
            // new box found: insert (obey sort order)
            sal_uInt16 nInsPos;
            const SwTableBox* pBox = rTblBoxes[ i ];
            InsertSort( *Ptrs.pNewSttNds, pBox->GetSttIdx(), &nInsPos );

            // find the source box. It must be one in rBoxes.
            // We found the right one if it's in the same column as pBox.
            // No, if more than one selected cell in the same column has been splitted,
            // we have to look for the nearest one (i65201)!
            const SwTableBox* pSourceBox = NULL;
            const SwTableBox* pCheckBox = NULL;
            const SwTableLine* pBoxLine = pBox->GetUpper();
            sal_uInt16 nLineDiff = lcl_FindParentLines(rTbl,*pBox).C40_GETPOS(SwTableLine,pBoxLine);
            sal_uInt16 nLineNo = 0;
            for( sal_uInt16 j = 0; j < rBoxes.Count(); ++j )
            {
                pCheckBox = rBoxes[j];
                if( pCheckBox->GetUpper()->GetUpper() == pBox->GetUpper()->GetUpper() )
                {
                    const SwTableLine* pCheckLine = pCheckBox->GetUpper();
                    sal_uInt16 nCheckLine = lcl_FindParentLines( rTbl, *pCheckBox ).
                    C40_GETPOS( SwTableLine, pCheckLine );
                    if( ( !pSourceBox || nCheckLine > nLineNo ) && nCheckLine < nLineDiff )
                    {
                        nLineNo = nCheckLine;
                        pSourceBox = pCheckBox;
                    }
                }
            }

            // find the line number difference
            // (to help determine bNodesMoved flag below)
            nLineDiff = nLineDiff - nLineNo;
            OSL_ENSURE( pSourceBox, "Splitted source box not found!" );
            // find out how many nodes the source box used to have
            // (to help determine bNodesMoved flag below)
            sal_uInt16 nNdsPos = 0;
            while( rBoxes[ nNdsPos ] != pSourceBox )
                ++nNdsPos;
            sal_uLong nNodes = rNodeCnts[ nNdsPos ];

            // When a new table cell is created, it either gets a new
            // node, or it gets node(s) from elsewhere. The undo must
            // know, of course, and thus we must determine here just
            // where pBox's nodes are from:
            // If 1) the source box has lost nodes, and
            //    2) we're in the node range that got nodes
            // then pBox received nodes from elsewhere.
            // If bNodesMoved is set for pBox the undo must move the
            // boxes back, otherwise it must delete them.
            // The bNodesMoved flag is stored in a seperate array
            // which mirrors Ptrs.pNewSttNds, i.e. Ptrs.pNewSttNds[i]
            // and aMvBoxes[i] belong together.
            sal_Bool bNodesMoved =
                ( nNodes != ( pSourceBox->GetSttNd()->EndOfSectionIndex() -
                              pSourceBox->GetSttIdx() ) )
                && ( nNodes - 1 > nLineDiff );
            aMvBoxes.insert( aMvBoxes.begin() + nInsPos, bNodesMoved );
        }
    }
}


void SwUndoTblNdsChg::SaveSection( SwStartNode* pSttNd )
{
    OSL_ENSURE( IsDelBox(), "falsche Action" );
    if( !Ptrs.pDelSects )
        Ptrs.pDelSects = new SwUndoSaveSections( 10, 5 );

    SwTableNode* pTblNd = pSttNd->FindTableNode();
    SwUndoSaveSection* pSave = new SwUndoSaveSection;
    pSave->SaveSection( pSttNd->GetDoc(), SwNodeIndex( *pSttNd ));

    Ptrs.pDelSects->Insert( pSave, Ptrs.pDelSects->Count() );
    nSttNode = pTblNd->GetIndex();
}


void SwUndoTblNdsChg::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwNodeIndex aIdx( rDoc.GetNodes(), nSttNode );

    SwTableNode *const pTblNd = aIdx.GetNode().GetTableNode();
    OSL_ENSURE( pTblNd, "kein TabellenNode" );

    SwTableFmlUpdate aMsgHnt( &pTblNd->GetTable() );
    aMsgHnt.eFlags = TBL_BOXPTR;
    rDoc.UpdateTblFlds( &aMsgHnt );

    CHECK_TABLE( pTblNd->GetTable() )

    _FndBox aTmpBox( 0, 0 );
    // ? TL_CHART2: notification or locking of controller required ?

    SwChartDataProvider *pPCD = rDoc.GetChartDataProvider();
    std::vector< SwTableBox* > aDelBoxes;
    if( IsDelBox() )
    {
        // Trick: die fehlenden Boxen in irgendeine Line einfuegen, beim
        // CreateNew werden sie korrekt verbunden.
        SwTableBox* pCpyBox = pTblNd->GetTable().GetTabSortBoxes()[0];
        SwTableBoxes& rLnBoxes = pCpyBox->GetUpper()->GetTabBoxes();

        // die Sections wieder herstellen
        for( sal_uInt16 n = Ptrs.pDelSects->Count(); n; )
        {
            SwUndoSaveSection* pSave = (*Ptrs.pDelSects)[ --n ];
            pSave->RestoreSection( &rDoc, &aIdx, SwTableBoxStartNode );
            if( pSave->GetHistory() )
                pSave->GetHistory()->Rollback( &rDoc );
            SwTableBox* pBox = new SwTableBox( (SwTableBoxFmt*)pCpyBox->GetFrmFmt(), aIdx,
                                                pCpyBox->GetUpper() );
            rLnBoxes.C40_INSERT( SwTableBox, pBox, rLnBoxes.Count() );
        }
        Ptrs.pDelSects->DeleteAndDestroy( 0, Ptrs.pDelSects->Count() );
    }
    else if( !aMvBoxes.empty() )
    {
        // dann muessen Nodes verschoben und nicht geloescht werden!
        // Dafuer brauchen wir aber ein temp Array
        SvULongs aTmp( 0, 5);
        aTmp.Insert( Ptrs.pNewSttNds, 0 );

        // von hinten anfangen
        for( sal_uInt16 n = aTmp.Count(); n; )
        {
            // Box aus der Tabellen-Struktur entfernen
            sal_uLong nIdx = aTmp[ --n ];
            SwTableBox* pBox = pTblNd->GetTable().GetTblBox( nIdx );
            OSL_ENSURE( pBox, "Wo ist meine TabellenBox geblieben?" );

            // TL_CHART2: notify chart about box to be removed
            if (pPCD)
                pPCD->DeleteBox( &pTblNd->GetTable(), *pBox );

            if( aMvBoxes[ n ] )
            {
                SwNodeRange aRg( *pBox->GetSttNd(), 1,
                            *pBox->GetSttNd()->EndOfSectionNode() );

                SwTableLine* pLine = lcl_FindTableLine( pTblNd->GetTable(), *pBox );
                SwNodeIndex aInsPos( *(pLine->GetTabBoxes()[0]->GetSttNd()), 2 );

                // alle StartNode Indizies anpassen
                sal_uInt16 i = n;
                sal_uLong nSttIdx = aInsPos.GetIndex() - 2,
                       nNdCnt = aRg.aEnd.GetIndex() - aRg.aStart.GetIndex();
                while( i && aTmp[ --i ] > nSttIdx )
                    aTmp[ i ] += nNdCnt;

                // erst die Box loeschen
                delete pBox;
                // dann die Nodes verschieben,
                rDoc.GetNodes()._MoveNodes( aRg, rDoc.GetNodes(), aInsPos, sal_False );
            }
            else
                rDoc.DeleteSection( rDoc.GetNodes()[ nIdx ] );
            aDelBoxes.insert( aDelBoxes.end(), pBox );
        }
    }
    else
    {
        // Remove nodes from nodes array (backwards!)
        for( sal_uInt16 n = Ptrs.pNewSttNds->Count(); n; )
        {
            sal_uLong nIdx = (*Ptrs.pNewSttNds)[ --n ];
            SwTableBox* pBox = pTblNd->GetTable().GetTblBox( nIdx );
            OSL_ENSURE( pBox, "Where's my table box?" );
            // TL_CHART2: notify chart about box to be removed
            if (pPCD)
                pPCD->DeleteBox( &pTblNd->GetTable(), *pBox );
            aDelBoxes.insert( aDelBoxes.end(), pBox );
            rDoc.DeleteSection( rDoc.GetNodes()[ nIdx ] );
        }
    }
    // Remove boxes from table structure
    for( sal_uInt16 n = 0; n < aDelBoxes.size(); ++n )
    {
        SwTableBox* pCurrBox = aDelBoxes[n];
        SwTableBoxes* pTBoxes = &pCurrBox->GetUpper()->GetTabBoxes();
        pTBoxes->Remove( pTBoxes->C40_GETPOS( SwTableBox, pCurrBox ) );
        delete pCurrBox;
    }

    pSaveTbl->CreateNew( pTblNd->GetTable(), sal_True, sal_False );

    // TL_CHART2: need to inform chart of probably changed cell names
    rDoc.UpdateCharts( pTblNd->GetTable().GetFrmFmt()->GetName() );

    if( IsDelBox() )
        nSttNode = pTblNd->GetIndex();
    ClearFEShellTabCols();
    CHECK_TABLE( pTblNd->GetTable() )
}


void SwUndoTblNdsChg::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();

    SwTableNode* pTblNd = rDoc.GetNodes()[ nSttNode ]->GetTableNode();
    OSL_ENSURE( pTblNd, "kein TabellenNode" );
    CHECK_TABLE( pTblNd->GetTable() )

    SwSelBoxes aSelBoxes;
    for( sal_uInt16 n = 0; n < aBoxes.Count(); ++n )
    {
        SwTableBox* pBox = pTblNd->GetTable().GetTblBox( aBoxes[ n ] );
        aSelBoxes.Insert( pBox );
    }

    // SelBoxes erzeugen und InsertCell/-Row/SplitTbl aufrufen
    switch( GetId() )
    {
    case UNDO_TABLE_INSCOL:
        if( USHRT_MAX == nSetColType )
            rDoc.InsertCol( aSelBoxes, nCount, bFlag );
        else
        {
            SwTableBox* pBox = pTblNd->GetTable().GetTblBox( nCurrBox );
            rDoc.SetColRowWidthHeight( *pBox, nSetColType, nAbsDiff,
                                        nRelDiff );
        }
        break;

    case UNDO_TABLE_INSROW:
        if( USHRT_MAX == nSetColType )
            rDoc.InsertRow( aSelBoxes, nCount, bFlag );
        else
        {
            SwTable& rTbl = pTblNd->GetTable();
            SwTableBox* pBox = rTbl.GetTblBox( nCurrBox );
            TblChgMode eOldMode = rTbl.GetTblChgMode();
            rTbl.SetTblChgMode( (TblChgMode)nCount );
            rDoc.SetColRowWidthHeight( *pBox, nSetColType, nAbsDiff, nRelDiff );
            rTbl.SetTblChgMode( eOldMode );
        }
        break;

    case UNDO_TABLE_SPLIT:
        rDoc.SplitTbl( aSelBoxes, bFlag, nCount, bSameHeight );
        break;
    case UNDO_TABLE_DELBOX:
    case UNDO_ROW_DELETE:
    case UNDO_COL_DELETE:
        if( USHRT_MAX == nSetColType )
        {
            SwTableFmlUpdate aMsgHnt( &pTblNd->GetTable() );
            aMsgHnt.eFlags = TBL_BOXPTR;
            rDoc.UpdateTblFlds( &aMsgHnt );
            SwTable &rTable = pTblNd->GetTable();
            if( nMax > nMin && rTable.IsNewModel() )
                rTable.PrepareDeleteCol( nMin, nMax );
            rTable.DeleteSel( &rDoc, aSelBoxes, 0, this, sal_True, sal_True );
        }
        else
        {
            SwTable& rTbl = pTblNd->GetTable();

            SwTableFmlUpdate aMsgHnt( &rTbl );
            aMsgHnt.eFlags = TBL_BOXPTR;
            rDoc.UpdateTblFlds( &aMsgHnt );

            SwTableBox* pBox = rTbl.GetTblBox( nCurrBox );
            TblChgMode eOldMode = rTbl.GetTblChgMode();
            rTbl.SetTblChgMode( (TblChgMode)nCount );

            // need the SaveSections!
            rDoc.GetIDocumentUndoRedo().DoUndo( true );
            SwUndoTblNdsChg* pUndo = 0;

            switch( nSetColType & 0xff )
            {
            case nsTblChgWidthHeightType::WH_COL_LEFT:
            case nsTblChgWidthHeightType::WH_COL_RIGHT:
            case nsTblChgWidthHeightType::WH_CELL_LEFT:
            case nsTblChgWidthHeightType::WH_CELL_RIGHT:
                 rTbl.SetColWidth( *pBox, nSetColType, nAbsDiff,
                                    nRelDiff, (SwUndo**)&pUndo );
                break;
            case nsTblChgWidthHeightType::WH_ROW_TOP:
            case nsTblChgWidthHeightType::WH_ROW_BOTTOM:
            case nsTblChgWidthHeightType::WH_CELL_TOP:
            case nsTblChgWidthHeightType::WH_CELL_BOTTOM:
                rTbl.SetRowHeight( *pBox, nSetColType, nAbsDiff,
                                    nRelDiff, (SwUndo**)&pUndo );
                break;
            }

            if( pUndo )
            {
                Ptrs.pDelSects->Insert( pUndo->Ptrs.pDelSects, 0 );
                pUndo->Ptrs.pDelSects->Remove( 0, pUndo->Ptrs.pDelSects->Count() );

                delete pUndo;
            }
            rDoc.GetIDocumentUndoRedo().DoUndo( false );

            rTbl.SetTblChgMode( eOldMode );
        }
        nSttNode = pTblNd->GetIndex();
        break;
    default:
        ;
    }
    ClearFEShellTabCols();
    CHECK_TABLE( pTblNd->GetTable() )
}


//////////////////////////////////////////////////////////////////////////

SwUndoTblMerge::SwUndoTblMerge( const SwPaM& rTblSel )
    : SwUndo( UNDO_TABLE_MERGE ), SwUndRng( rTblSel ), pHistory( 0 )
{
    const SwTableNode* pTblNd = rTblSel.GetNode()->FindTableNode();
    OSL_ENSURE( pTblNd, "Wo ist TabllenNode" );
    pSaveTbl = new _SaveTable( pTblNd->GetTable() );
    pMoves = new SwUndoMoves;
    nTblNode = pTblNd->GetIndex();
}

SwUndoTblMerge::~SwUndoTblMerge()
{
    delete pSaveTbl;
    delete pMoves;
    delete pHistory;
}

void SwUndoTblMerge::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwNodeIndex aIdx( rDoc.GetNodes(), nTblNode );

    SwTableNode *const pTblNd = aIdx.GetNode().GetTableNode();
    OSL_ENSURE( pTblNd, "kein TabellenNode" );

    SwTableFmlUpdate aMsgHnt( &pTblNd->GetTable() );
    aMsgHnt.eFlags = TBL_BOXPTR;
    rDoc.UpdateTblFlds( &aMsgHnt );

    _FndBox aTmpBox( 0, 0 );
    // ? TL_CHART2: notification or locking of controller required ?


    // 1. die geloeschten Boxen wiederherstellen:

    // Trick: die fehlenden Boxen in irgendeine Line einfuegen, beim
    // CreateNew werden sie korrekt verbunden.
    SwTableBox *pBox, *pCpyBox = pTblNd->GetTable().GetTabSortBoxes()[0];
    SwTableBoxes& rLnBoxes = pCpyBox->GetUpper()->GetTabBoxes();

CHECKTABLE(pTblNd->GetTable())

    SwSelBoxes aSelBoxes;
    SwTxtFmtColl* pColl = rDoc.GetTxtCollFromPool( RES_POOLCOLL_STANDARD );
    sal_uInt16 n;

    for( n = 0; n < aBoxes.Count(); ++n )
    {
        aIdx = aBoxes[ n ];
        SwStartNode* pSttNd = rDoc.GetNodes().MakeTextSection( aIdx,
                                            SwTableBoxStartNode, pColl );
        pBox = new SwTableBox( (SwTableBoxFmt*)pCpyBox->GetFrmFmt(), *pSttNd,
                                pCpyBox->GetUpper() );
        rLnBoxes.C40_INSERT( SwTableBox, pBox, rLnBoxes.Count() );

        aSelBoxes.Insert( pBox );
    }

CHECKTABLE(pTblNd->GetTable())

    SwChartDataProvider *pPCD = rDoc.GetChartDataProvider();
    // 2. die eingefuegten Boxen loeschen
    // die Nodes loeschen (von Hinten!!)
    for( n = aNewSttNds.Count(); n; )
    {
        // Box aus der Tabellen-Struktur entfernen
        sal_uLong nIdx = aNewSttNds[ --n ];

        if( !nIdx && n )
        {
            nIdx = aNewSttNds[ --n ];
            pBox = pTblNd->GetTable().GetTblBox( nIdx );
            OSL_ENSURE( pBox, "Wo ist meine TabellenBox geblieben?" );

            if( !pSaveTbl->IsNewModel() )
                rDoc.GetNodes().MakeTxtNode( SwNodeIndex(
                    *pBox->GetSttNd()->EndOfSectionNode() ), pColl );

            // das war der Trenner, -> die verschobenen herstellen
            for( sal_uInt16 i = pMoves->Count(); i; )
            {
                SwTxtNode* pTxtNd = 0;
                sal_uInt16 nDelPos = 0;
                SwUndoMove* pUndo = (*pMoves)[ --i ];
                if( !pUndo->IsMoveRange() )
                {
                    pTxtNd = rDoc.GetNodes()[ pUndo->GetDestSttNode() ]->GetTxtNode();
                    nDelPos = pUndo->GetDestSttCntnt() - 1;
                }
                pUndo->UndoImpl(rContext);
                if( pUndo->IsMoveRange() )
                {
                    // den ueberfluessigen Node loeschen
                    aIdx = pUndo->GetEndNode();
                    SwCntntNode *pCNd = aIdx.GetNode().GetCntntNode();
                    if( pCNd )
                    {
                        SwNodeIndex aTmp( aIdx, -1 );
                        SwCntntNode *pMove = aTmp.GetNode().GetCntntNode();
                        if( pMove )
                            pCNd->MoveTo( *pMove );
                    }
                    rDoc.GetNodes().Delete( aIdx, 1 );
                }
                else if( pTxtNd )
                {
                    // evt. noch ueberflussige Attribute loeschen
                    SwIndex aTmpIdx( pTxtNd, nDelPos );
                    if( pTxtNd->GetpSwpHints() && pTxtNd->GetpSwpHints()->Count() )
                        pTxtNd->RstAttr( aTmpIdx, pTxtNd->GetTxt().Len() -
                                                            nDelPos + 1 );
                    // das Trennzeichen loeschen
                    pTxtNd->EraseText( aTmpIdx, 1 );
                }
            }
            nIdx = pBox->GetSttIdx();
        }
        else
            pBox = pTblNd->GetTable().GetTblBox( nIdx );

        if( !pSaveTbl->IsNewModel() )
        {
            // TL_CHART2: notify chart about box to be removed
            if (pPCD)
                pPCD->DeleteBox( &pTblNd->GetTable(), *pBox );

            SwTableBoxes* pTBoxes = &pBox->GetUpper()->GetTabBoxes();
            pTBoxes->Remove( pTBoxes->C40_GETPOS( SwTableBox, pBox ) );


            // Indizies aus dem Bereich loeschen
            {
                SwNodeIndex aTmpIdx( *pBox->GetSttNd() );
                rDoc.CorrAbs( SwNodeIndex( aTmpIdx, 1 ),
                            SwNodeIndex( *aTmpIdx.GetNode().EndOfSectionNode() ),
                            SwPosition( aTmpIdx, SwIndex( 0, 0 )), sal_True );
            }

            delete pBox;
            rDoc.DeleteSection( rDoc.GetNodes()[ nIdx ] );
        }
    }
CHECKTABLE(pTblNd->GetTable())


    pSaveTbl->CreateNew( pTblNd->GetTable(), sal_True, sal_False );

    // TL_CHART2: need to inform chart of probably changed cell names
    rDoc.UpdateCharts( pTblNd->GetTable().GetFrmFmt()->GetName() );

    if( pHistory )
    {
        pHistory->TmpRollback( &rDoc, 0 );
        pHistory->SetTmpEnd( pHistory->Count() );
    }
//	nTblNode = pTblNd->GetIndex();

    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
    pPam->DeleteMark();
    pPam->GetPoint()->nNode = nSttNode;
    pPam->GetPoint()->nContent.Assign( pPam->GetCntntNode(), nSttCntnt );
    pPam->SetMark();
    pPam->DeleteMark();

CHECKTABLE(pTblNd->GetTable())
    ClearFEShellTabCols();
}

void SwUndoTblMerge::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwPaM & rPam( AddUndoRedoPaM(rContext) );
    rDoc.MergeTbl(rPam);
}

void SwUndoTblMerge::MoveBoxCntnt( SwDoc* pDoc, SwNodeRange& rRg, SwNodeIndex& rPos )
{
    SwNodeIndex aTmp( rRg.aStart, -1 ), aTmp2( rPos, -1 );
    SwUndoMove* pUndo = new SwUndoMove( pDoc, rRg, rPos );
    ::sw::UndoGuard const undoGuard(pDoc->GetIDocumentUndoRedo());
    pDoc->MoveNodeRange( rRg, rPos, (pSaveTbl->IsNewModel()) ?
        IDocumentContentOperations::DOC_NO_DELFRMS :
        IDocumentContentOperations::DOC_MOVEDEFAULT );
    aTmp++;
    aTmp2++;
    pUndo->SetDestRange( aTmp2, rPos, aTmp );

    pMoves->Insert( pUndo, pMoves->Count() );
}

void SwUndoTblMerge::SetSelBoxes( const SwSelBoxes& rBoxes )
{
    // die Selektion merken
    for( sal_uInt16 n = 0; n < rBoxes.Count(); ++n )
        InsertSort( aBoxes, rBoxes[n]->GetSttIdx() );

    // als Trennung fuers einfuegen neuer Boxen nach dem Verschieben!
    aNewSttNds.Insert( (sal_uLong)0, aNewSttNds.Count() );

     // The new table model does not delete overlapped cells (by row span),
     // so the rBoxes array might be empty even some cells have been merged.
    if( rBoxes.Count() )
        nTblNode = rBoxes[ 0 ]->GetSttNd()->FindTableNode()->GetIndex();
}

void SwUndoTblMerge::SaveCollection( const SwTableBox& rBox )
{
    if( !pHistory )
        pHistory = new SwHistory;

    SwNodeIndex aIdx( *rBox.GetSttNd(), 1 );
    SwCntntNode* pCNd = aIdx.GetNode().GetCntntNode();
    if( !pCNd )
        pCNd = aIdx.GetNodes().GoNext( &aIdx );

    pHistory->Add( pCNd->GetFmtColl(), aIdx.GetIndex(), pCNd->GetNodeType());
    if( pCNd->HasSwAttrSet() )
        pHistory->CopyFmtAttr( *pCNd->GetpSwAttrSet(), aIdx.GetIndex() );
}


//////////////////////////////////////////////////////////////////////////

SwUndoTblNumFmt::SwUndoTblNumFmt( const SwTableBox& rBox,
                                    const SfxItemSet* pNewSet )
    : SwUndo( UNDO_TBLNUMFMT ),
    pBoxSet( 0 ), pHistory( 0 ), nFmtIdx( NUMBERFORMAT_TEXT )
{
    bNewFmt = bNewFml = bNewValue = sal_False;
    nNode = rBox.GetSttIdx();

    nNdPos = rBox.IsValidNumTxtNd( 0 == pNewSet );
    SwDoc* pDoc = rBox.GetFrmFmt()->GetDoc();

    if( ULONG_MAX != nNdPos )
    {
        SwTxtNode* pTNd = pDoc->GetNodes()[ nNdPos ]->GetTxtNode();

        pHistory = new SwHistory;
        SwRegHistory aRHst( *rBox.GetSttNd(), pHistory );
        // always save all text atttibutes because of possibly overlapping
        // areas of on/off
        pHistory->CopyAttr( pTNd->GetpSwpHints(), nNdPos, 0,
                            pTNd->GetTxt().Len(), true );

        if( pTNd->HasSwAttrSet() )
            pHistory->CopyFmtAttr( *pTNd->GetpSwAttrSet(), nNdPos );

        aStr = pTNd->GetTxt();
        if( pTNd->GetpSwpHints() )
            pTNd->GetpSwpHints()->DeRegister();
    }

    pBoxSet = new SfxItemSet( pDoc->GetAttrPool(), aTableBoxSetRange );
    pBoxSet->Put( rBox.GetFrmFmt()->GetAttrSet() );

    if( pNewSet )
    {
        const SfxPoolItem* pItem;
        if( SFX_ITEM_SET == pNewSet->GetItemState( RES_BOXATR_FORMAT,
                sal_False, &pItem ))
        {
            bNewFmt = sal_True;
            nNewFmtIdx = ((SwTblBoxNumFormat*)pItem)->GetValue();
        }
        if( SFX_ITEM_SET == pNewSet->GetItemState( RES_BOXATR_FORMULA,
                sal_False, &pItem ))
        {
            bNewFml = sal_True;
            aNewFml = ((SwTblBoxFormula*)pItem)->GetFormula();
        }
        if( SFX_ITEM_SET == pNewSet->GetItemState( RES_BOXATR_VALUE,
                sal_False, &pItem ))
        {
            bNewValue = sal_True;
            fNewNum = ((SwTblBoxValue*)pItem)->GetValue();
        }
    }

    // wird die History ueberhaupt benoetigt ??
    if( pHistory && !pHistory->Count() )
        DELETEZ( pHistory );
}

SwUndoTblNumFmt::~SwUndoTblNumFmt()
{
    delete pHistory;
    delete pBoxSet;
}

void SwUndoTblNumFmt::UndoImpl(::sw::UndoRedoContext & rContext)
{
    OSL_ENSURE( pBoxSet, "Where's the stored item set?" );

    SwDoc & rDoc = rContext.GetDoc();
    SwStartNode* pSttNd = rDoc.GetNodes()[ nNode ]->
                            FindSttNodeByType( SwTableBoxStartNode );
    OSL_ENSURE( pSttNd, "ohne StartNode kein TabellenBox" );
    SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().GetTblBox(
                                    pSttNd->GetIndex() );
    OSL_ENSURE( pBox, "keine TabellenBox gefunden" );

    SwTableBoxFmt* pFmt = rDoc.MakeTableBoxFmt();
    pFmt->SetFmtAttr( *pBoxSet );
    pBox->ChgFrmFmt( pFmt );

    if( ULONG_MAX == nNdPos )
        return;

    SwTxtNode* pTxtNd = rDoc.GetNodes()[ nNdPos ]->GetTxtNode();
    // wenn mehr als ein Node geloescht wurde, dann wurden auch
    // alle "Node"-Attribute gespeichert
    if( pTxtNd->HasSwAttrSet() )
        pTxtNd->ResetAllAttr();

    if( pTxtNd->GetpSwpHints() && aStr.Len() )
        pTxtNd->ClearSwpHintsArr( true );

    // ChgTextToNum(..) only acts when the strings are different. We
    // need to do the same here.
    if( pTxtNd->GetTxt() != aStr )
    {
        rDoc.DeleteRedline( *( pBox->GetSttNd() ), false, USHRT_MAX );

        SwIndex aIdx( pTxtNd, 0 );
        if( aStr.Len() )
        {
            pTxtNd->EraseText( aIdx );
            pTxtNd->InsertText( aStr, aIdx,
                IDocumentContentOperations::INS_NOHINTEXPAND );
        }
    }

    if( pHistory )
    {
        sal_uInt16 nTmpEnd = pHistory->GetTmpEnd();
        pHistory->TmpRollback( &rDoc, 0 );
        pHistory->SetTmpEnd( nTmpEnd );
    }

    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());
    pPam->DeleteMark();
    pPam->GetPoint()->nNode = nNode + 1;
    pPam->GetPoint()->nContent.Assign( pTxtNd, 0 );
}

/** switch the RedlineMode on the given document, using
 * SetRedlineMode_intern. This class set the mode in the constructor,
 * and changes it back in the destructor, i.e. it uses the
 * initialization-is-resource-acquisition idiom.
 */
class RedlineModeInternGuard
{
    SwDoc& mrDoc;
    RedlineMode_t meOldRedlineMode;

public:
    RedlineModeInternGuard(
        SwDoc& rDoc,                      /// change mode of this document
        RedlineMode_t eNewRedlineMode,    /// new redline mode
        RedlineMode_t eRedlineModeMask  = (RedlineMode_t)(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_IGNORE /*change only bits set in this mask*/));

    ~RedlineModeInternGuard();
};

RedlineModeInternGuard::RedlineModeInternGuard(
    SwDoc& rDoc,
    RedlineMode_t eNewRedlineMode,
    RedlineMode_t eRedlineModeMask )
    : mrDoc( rDoc ),
      meOldRedlineMode( rDoc.GetRedlineMode() )
{
    mrDoc.SetRedlineMode_intern((RedlineMode_t)( ( meOldRedlineMode & ~eRedlineModeMask ) |
                                     ( eNewRedlineMode & eRedlineModeMask ) ));
}

RedlineModeInternGuard::~RedlineModeInternGuard()
{
    mrDoc.SetRedlineMode_intern( meOldRedlineMode );
}



void SwUndoTblNumFmt::RedoImpl(::sw::UndoRedoContext & rContext)
{
    // konnte die Box veraendert werden ?
    if( !pBoxSet )
        return ;

    SwDoc & rDoc = rContext.GetDoc();
    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());

    pPam->DeleteMark();
    pPam->GetPoint()->nNode = nNode;

    SwNode * pNd = & pPam->GetPoint()->nNode.GetNode();
    SwStartNode* pSttNd = pNd->FindSttNodeByType( SwTableBoxStartNode );
    OSL_ENSURE( pSttNd, "ohne StartNode kein TabellenBox" );
    SwTableBox* pBox = pSttNd->FindTableNode()->GetTable().GetTblBox(
                                    pSttNd->GetIndex() );
    OSL_ENSURE( pBox, "keine TabellenBox gefunden" );

    SwFrmFmt* pBoxFmt = pBox->ClaimFrmFmt();
    if(	bNewFmt || bNewFml || bNewValue )
    {
        SfxItemSet aBoxSet( rDoc.GetAttrPool(),
                                RES_BOXATR_FORMAT, RES_BOXATR_VALUE );

        // JP 15.01.99: Nur Attribute zuruecksetzen reicht nicht.
        //				Sorge dafuer, das der Text auch entsprechend
        //				formatiert wird!
        pBoxFmt->LockModify();

        if( bNewFml )
            aBoxSet.Put( SwTblBoxFormula( aNewFml ));
        else
            pBoxFmt->ResetFmtAttr( RES_BOXATR_FORMULA );
        if( bNewFmt )
            aBoxSet.Put( SwTblBoxNumFormat( nNewFmtIdx ));
        else
            pBoxFmt->ResetFmtAttr( RES_BOXATR_FORMAT );
        if( bNewValue )
            aBoxSet.Put( SwTblBoxValue( fNewNum ));
        else
            pBoxFmt->ResetFmtAttr( RES_BOXATR_VALUE );
        pBoxFmt->UnlockModify();

        // dvo: When redlining is (was) enabled, setting the attribute
        // will also change the cell content. To allow this, the
        // REDLINE_IGNORE flag must be removed during Redo. #108450#
        RedlineModeInternGuard aGuard( rDoc, nsRedlineMode_t::REDLINE_NONE, nsRedlineMode_t::REDLINE_IGNORE );
        pBoxFmt->SetFmtAttr( aBoxSet );
    }
    else if( NUMBERFORMAT_TEXT != nFmtIdx )
    {
        SfxItemSet aBoxSet( rDoc.GetAttrPool(),
                            RES_BOXATR_FORMAT, RES_BOXATR_VALUE );

        aBoxSet.Put( SwTblBoxNumFormat( nFmtIdx ));
        aBoxSet.Put( SwTblBoxValue( fNum ));

        // JP 15.01.99: Nur Attribute zuruecksetzen reicht nicht.
        //				Sorge dafuer, das der Text auch entsprechend
        //				formatiert wird!
        pBoxFmt->LockModify();
        pBoxFmt->ResetFmtAttr( RES_BOXATR_FORMULA );
        pBoxFmt->UnlockModify();

        // dvo: When redlining is (was) enabled, setting the attribute
        // will also change the cell content. To allow this, the
        // REDLINE_IGNORE flag must be removed during Redo. #108450#
        RedlineModeInternGuard aGuard( rDoc, nsRedlineMode_t::REDLINE_NONE, nsRedlineMode_t::REDLINE_IGNORE );
        pBoxFmt->SetFmtAttr( aBoxSet );
    }
    else
    {
        // es ist keine Zahl

        // JP 15.01.99: Nur Attribute zuruecksetzen reicht nicht.
        //				Sorge dafuer, das der Text auch entsprechend
        //				formatiert wird!
        pBoxFmt->SetFmtAttr( *GetDfltAttr( RES_BOXATR_FORMAT ));

        pBoxFmt->ResetFmtAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
    }

    if( bNewFml )
    {
        // egal was gesetzt wurde, ein Update der Tabelle macht sich immer gut
        SwTableFmlUpdate aTblUpdate( &pSttNd->FindTableNode()->GetTable() );
        rDoc.UpdateTblFlds( &aTblUpdate );
    }

    if( !pNd->IsCntntNode() )
        pNd = rDoc.GetNodes().GoNext( &pPam->GetPoint()->nNode );
    pPam->GetPoint()->nContent.Assign( (SwCntntNode*)pNd, 0 );
}

void SwUndoTblNumFmt::SetBox( const SwTableBox& rBox )
{
    nNode = rBox.GetSttIdx();
}


//////////////////////////////////////////////////////////////////////////

_UndoTblCpyTbl_Entry::_UndoTblCpyTbl_Entry( const SwTableBox& rBox )
    : nBoxIdx( rBox.GetSttIdx() ), nOffset( 0 ),
    pBoxNumAttr( 0 ), pUndo( 0 ), bJoin( false )
{
}

_UndoTblCpyTbl_Entry::~_UndoTblCpyTbl_Entry()
{
    delete pUndo;
    delete pBoxNumAttr;
}


SwUndoTblCpyTbl::SwUndoTblCpyTbl()
    : SwUndo( UNDO_TBLCPYTBL ), pInsRowUndo( 0 )
{
    pArr = new _UndoTblCpyTbl_Entries;
}

SwUndoTblCpyTbl::~SwUndoTblCpyTbl()
{
    delete pArr;
    delete pInsRowUndo;
}

void SwUndoTblCpyTbl::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    _DEBUG_REDLINE( &rDoc )

    SwTableNode* pTblNd = 0;
    for( sal_uInt16 n = pArr->Count(); n; )
    {
        _UndoTblCpyTbl_Entry* pEntry = (*pArr)[ --n ];
        sal_uLong nSttPos = pEntry->nBoxIdx + pEntry->nOffset;
        SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode();
        if( !pTblNd )
            pTblNd = pSNd->FindTableNode();

        SwTableBox& rBox = *pTblNd->GetTable().GetTblBox( nSttPos );

        SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
        rDoc.GetNodes().MakeTxtNode( aInsIdx, (SwTxtFmtColl*)rDoc.GetDfltTxtFmtColl() );

        // b62341295: Redline for copying tables
        const SwNode *pEndNode = rBox.GetSttNd()->EndOfSectionNode();
        SwPaM aPam( aInsIdx.GetNode(), *pEndNode );
        SwUndoDelete* pUndo = 0;

        if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineMode() ) )
        {
            bool bDeleteCompleteParagraph = false;
            bool bShiftPam = false;
            // There are a couple of different situations to consider during redlining
            if( pEntry->pUndo )
            {
                SwUndoDelete *const pUndoDelete =
                    dynamic_cast<SwUndoDelete*>(pEntry->pUndo);
                SwUndoRedlineDelete *const pUndoRedlineDelete =
                    dynamic_cast<SwUndoRedlineDelete*>(pEntry->pUndo);
                OSL_ASSERT(pUndoDelete || pUndoRedlineDelete);
                if (pUndoRedlineDelete)
                {
                    // The old content was not empty or he has been merged with the new content
                    bDeleteCompleteParagraph = !pEntry->bJoin; // bJoin is set when merged
                    // Set aTmpIdx to the beginning fo the old content
                    SwNodeIndex aTmpIdx( *pEndNode,
                            pUndoRedlineDelete->NodeDiff()-1 );
                    SwTxtNode *pTxt = aTmpIdx.GetNode().GetTxtNode();
                    if( pTxt )
                    {
                        aPam.GetPoint()->nNode = *pTxt;
                        aPam.GetPoint()->nContent.Assign( pTxt,
                                pUndoRedlineDelete->ContentStart() );
                    }
                    else
                        *aPam.GetPoint() = SwPosition( aTmpIdx );
                }
                else if (pUndoDelete && pUndoDelete->IsDelFullPara())
                {
                    // When the old content was an empty paragraph, but could not be joined
                    // with the new content (e.g. because of a section or table)
                    // We "save" the aPam.Point, we go one step backwards (because later on the
                    // empty paragraph will be inserted by the undo) and set the "ShiftPam-flag
                    // for step forward later on.
                    bDeleteCompleteParagraph = true;
                    bShiftPam = true;
                    SwNodeIndex aTmpIdx( *pEndNode, -1 );
                    SwTxtNode *pTxt = aTmpIdx.GetNode().GetTxtNode();
                    if( pTxt )
                    {
                        aPam.GetPoint()->nNode = *pTxt;
                        aPam.GetPoint()->nContent.Assign( pTxt, 0 );
                    }
                    else
                        *aPam.GetPoint() = SwPosition( aTmpIdx );
                }
            }
            rDoc.DeleteRedline( aPam, true, USHRT_MAX );

            if( pEntry->pUndo )
            {
                pEntry->pUndo->UndoImpl(rContext);
                delete pEntry->pUndo;
                pEntry->pUndo = 0;
            }
            if( bShiftPam )
            {
                // The aPam.Point is at the moment at the last position of the new content and has to be
                // moved to the first postion of the old content for the SwUndoDelete operation
                SwNodeIndex aTmpIdx( aPam.GetPoint()->nNode, 1 );
                SwTxtNode *pTxt = aTmpIdx.GetNode().GetTxtNode();
                if( pTxt )
                {
                    aPam.GetPoint()->nNode = *pTxt;
                    aPam.GetPoint()->nContent.Assign( pTxt, 0 );
                }
                else
                    *aPam.GetPoint() = SwPosition( aTmpIdx );
            }
            pUndo = new SwUndoDelete( aPam, bDeleteCompleteParagraph, sal_True );
        }
        else
        {
            pUndo = new SwUndoDelete( aPam, true );
            if( pEntry->pUndo )
            {
                pEntry->pUndo->UndoImpl(rContext);
                delete pEntry->pUndo;
                pEntry->pUndo = 0;
            }
        }
        pEntry->pUndo = pUndo;

        aInsIdx = rBox.GetSttIdx() + 1;
        rDoc.GetNodes().Delete( aInsIdx, 1 );

        SfxItemSet aTmpSet( rDoc.GetAttrPool(), RES_BOXATR_FORMAT, RES_BOXATR_VALUE,
                                                RES_VERT_ORIENT, RES_VERT_ORIENT, 0 );
        aTmpSet.Put( rBox.GetFrmFmt()->GetAttrSet() );
        if( aTmpSet.Count() )
        {
            SwFrmFmt* pBoxFmt = rBox.ClaimFrmFmt();
            pBoxFmt->ResetFmtAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
            pBoxFmt->ResetFmtAttr( RES_VERT_ORIENT );
        }

        if( pEntry->pBoxNumAttr )
        {
            rBox.ClaimFrmFmt()->SetFmtAttr( *pEntry->pBoxNumAttr );
            delete pEntry->pBoxNumAttr, pEntry->pBoxNumAttr = 0;
        }

        if( aTmpSet.Count() )
        {
            pEntry->pBoxNumAttr = new SfxItemSet( rDoc.GetAttrPool(),
                                    RES_BOXATR_FORMAT, RES_BOXATR_VALUE,
                                    RES_VERT_ORIENT, RES_VERT_ORIENT, 0 );
            pEntry->pBoxNumAttr->Put( aTmpSet );
        }

        pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
    }

    if( pInsRowUndo )
    {
        pInsRowUndo->UndoImpl(rContext);
    }
    _DEBUG_REDLINE( &rDoc )
}

void SwUndoTblCpyTbl::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    _DEBUG_REDLINE( &rDoc )

    if( pInsRowUndo )
    {
        pInsRowUndo->RedoImpl(rContext);
    }

    SwTableNode* pTblNd = 0;
    for( sal_uInt16 n = 0; n < pArr->Count(); ++n )
    {
        _UndoTblCpyTbl_Entry* pEntry = (*pArr)[ n ];
        sal_uLong nSttPos = pEntry->nBoxIdx + pEntry->nOffset;
        SwStartNode* pSNd = rDoc.GetNodes()[ nSttPos ]->StartOfSectionNode();
        if( !pTblNd )
            pTblNd = pSNd->FindTableNode();

        SwTableBox& rBox = *pTblNd->GetTable().GetTblBox( nSttPos );

        SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );

        // b62341295: Redline for copying tables - Start.
        rDoc.GetNodes().MakeTxtNode( aInsIdx, (SwTxtFmtColl*)rDoc.GetDfltTxtFmtColl() );
        SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode());
        SwUndo* pUndo = IDocumentRedlineAccess::IsRedlineOn( GetRedlineMode() ) ? 0 : new SwUndoDelete( aPam, sal_True );
        if( pEntry->pUndo )
        {
            pEntry->pUndo->UndoImpl(rContext);
            if( IDocumentRedlineAccess::IsRedlineOn( GetRedlineMode() ) )
            {
                // PrepareRedline has to be called with the beginning of the old content
                // When new and old content has been joined, the rIter.pAktPam has been set
                // by the Undo operation to this point.
                // Otherwise aInsIdx has been moved during the Undo operation
                if( pEntry->bJoin )
                {
                    SwPaM const& rLastPam =
                        rContext.GetCursorSupplier().GetCurrentShellCursor();
                    pUndo = PrepareRedline( &rDoc, rBox, *rLastPam.GetPoint(),
                                            pEntry->bJoin, true );
                }
                else
                {
                    SwPosition aTmpPos( aInsIdx );
                    pUndo = PrepareRedline( &rDoc, rBox, aTmpPos, pEntry->bJoin, true );
                }
            }
            delete pEntry->pUndo;
            pEntry->pUndo = 0;
        }
        pEntry->pUndo = pUndo;
        // b62341295: Redline for copying tables - End.

        aInsIdx = rBox.GetSttIdx() + 1;
        rDoc.GetNodes().Delete( aInsIdx, 1 );

        SfxItemSet aTmpSet( rDoc.GetAttrPool(), RES_BOXATR_FORMAT, RES_BOXATR_VALUE,
                                                RES_VERT_ORIENT, RES_VERT_ORIENT, 0 );
        aTmpSet.Put( rBox.GetFrmFmt()->GetAttrSet() );
        if( aTmpSet.Count() )
        {
            SwFrmFmt* pBoxFmt = rBox.ClaimFrmFmt();
            pBoxFmt->ResetFmtAttr( RES_BOXATR_FORMAT, RES_BOXATR_VALUE );
            pBoxFmt->ResetFmtAttr( RES_VERT_ORIENT );
        }
        if( pEntry->pBoxNumAttr )
        {
            rBox.ClaimFrmFmt()->SetFmtAttr( *pEntry->pBoxNumAttr );
            delete pEntry->pBoxNumAttr, pEntry->pBoxNumAttr = 0;
        }

        if( aTmpSet.Count() )
        {
            pEntry->pBoxNumAttr = new SfxItemSet( rDoc.GetAttrPool(),
                                    RES_BOXATR_FORMAT, RES_BOXATR_VALUE,
                                    RES_VERT_ORIENT, RES_VERT_ORIENT, 0 );
            pEntry->pBoxNumAttr->Put( aTmpSet );
        }

        pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
    }
    _DEBUG_REDLINE( &rDoc )
}

void SwUndoTblCpyTbl::AddBoxBefore( const SwTableBox& rBox, sal_Bool bDelCntnt )
{
    if( pArr->Count() && !bDelCntnt )
        return;

    _UndoTblCpyTbl_Entry* pEntry = new _UndoTblCpyTbl_Entry( rBox );
    pArr->Insert( pEntry, pArr->Count() );

    SwDoc* pDoc = rBox.GetFrmFmt()->GetDoc();
    _DEBUG_REDLINE( pDoc )
    if( bDelCntnt )
    {
        SwNodeIndex aInsIdx( *rBox.GetSttNd(), 1 );
        pDoc->GetNodes().MakeTxtNode( aInsIdx, (SwTxtFmtColl*)pDoc->GetDfltTxtFmtColl() );
        SwPaM aPam( aInsIdx.GetNode(), *rBox.GetSttNd()->EndOfSectionNode() );

        if( !pDoc->IsRedlineOn() )
            pEntry->pUndo = new SwUndoDelete( aPam, sal_True );
    }

    pEntry->pBoxNumAttr = new SfxItemSet( pDoc->GetAttrPool(),
                                    RES_BOXATR_FORMAT, RES_BOXATR_VALUE,
                                    RES_VERT_ORIENT, RES_VERT_ORIENT, 0 );
    pEntry->pBoxNumAttr->Put( rBox.GetFrmFmt()->GetAttrSet() );
    if( !pEntry->pBoxNumAttr->Count() )
        delete pEntry->pBoxNumAttr, pEntry->pBoxNumAttr = 0;
    _DEBUG_REDLINE( pDoc )
}

void SwUndoTblCpyTbl::AddBoxAfter( const SwTableBox& rBox, const SwNodeIndex& rIdx, sal_Bool bDelCntnt )
{
    _UndoTblCpyTbl_Entry* pEntry = (*pArr)[ pArr->Count() - 1 ];

    // wurde der Inhalt geloescht, so loesche jetzt auch noch den temp.
    // erzeugten Node
    if( bDelCntnt )
    {
        SwDoc* pDoc = rBox.GetFrmFmt()->GetDoc();
        _DEBUG_REDLINE( pDoc )

        if( pDoc->IsRedlineOn() )
        {
            SwPosition aTmpPos( rIdx );
            pEntry->pUndo = PrepareRedline( pDoc, rBox, aTmpPos, pEntry->bJoin, false );
        }
        SwNodeIndex aDelIdx( *rBox.GetSttNd(), 1 );
        rBox.GetFrmFmt()->GetDoc()->GetNodes().Delete( aDelIdx, 1 );
        _DEBUG_REDLINE( pDoc )
    }

    pEntry->nOffset = rBox.GetSttIdx() - pEntry->nBoxIdx;
}

// PrepareRedline is called from AddBoxAfter() and from Redo() in slightly different situations.
// bRedo is set by calling from Redo()
// rJoin is false by calling from AddBoxAfter() and will be set if the old and new content has
// been merged.
// rJoin is true if Redo() is calling and the content has already been merged

SwUndo* SwUndoTblCpyTbl::PrepareRedline( SwDoc* pDoc, const SwTableBox& rBox,
    const SwPosition& rPos, bool& rJoin, bool bRedo )
{
    SwUndo *pUndo = 0;
    // b62341295: Redline for copying tables
    // What's to do?
    // Mark the cell content before rIdx as insertion,
    // mark the cell content behind rIdx as deletion
    // merge text nodes at rIdx if possible
    RedlineMode_t eOld = pDoc->GetRedlineMode();
    pDoc->SetRedlineMode_intern((RedlineMode_t)( ( eOld | nsRedlineMode_t::REDLINE_DONTCOMBINE_REDLINES ) &
                                     ~nsRedlineMode_t::REDLINE_IGNORE ));
    SwPosition aInsertEnd( rPos );
    SwTxtNode* pTxt;
    if( !rJoin )
    {
        // If the content is not merged, the end of the insertion is at the end of the node
        // _before_ the given position rPos
        --aInsertEnd.nNode;
        pTxt = aInsertEnd.nNode.GetNode().GetTxtNode();
        if( pTxt )
        {
            aInsertEnd.nContent.Assign( pTxt, pTxt->GetTxt().Len() );
            if( !bRedo && rPos.nNode.GetNode().GetTxtNode() )
            {   // Try to merge, if not called by Redo()
                rJoin = true;
                pTxt->JoinNext();
            }
        }
        else
            aInsertEnd.nContent = SwIndex( 0 );
    }
    // For joined (merged) contents the start of deletionm and end of insertion are identical
    // otherwise adjacent nodes.
    SwPosition aDeleteStart( rJoin ? aInsertEnd : rPos );
    if( !rJoin )
    {
        pTxt = aDeleteStart.nNode.GetNode().GetTxtNode();
        if( pTxt )
            aDeleteStart.nContent.Assign( pTxt, 0 );
    }
    SwPosition aCellEnd( SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode(), -1 ) );
    pTxt = aCellEnd.nNode.GetNode().GetTxtNode();
    if( pTxt )
        aCellEnd.nContent.Assign( pTxt, pTxt->GetTxt().Len() );
    if( aDeleteStart != aCellEnd )
    {   // If the old (deleted) part is not empty, here we are...
        SwPaM aDeletePam( aDeleteStart, aCellEnd );
        pUndo = new SwUndoRedlineDelete( aDeletePam, UNDO_DELETE );
        pDoc->AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_DELETE, aDeletePam ), true );
    }
    else if( !rJoin ) // If the old part is empty and joined, we are finished
    {   // if it is not joined, we have to delete this empty paragraph
        aCellEnd = SwPosition(
            SwNodeIndex( *rBox.GetSttNd()->EndOfSectionNode() ));
        SwPaM aTmpPam( aDeleteStart, aCellEnd );
        pUndo = new SwUndoDelete( aTmpPam, sal_True );
    }
    SwPosition aCellStart( SwNodeIndex( *rBox.GetSttNd(), 2 ) );
    pTxt = aCellStart.nNode.GetNode().GetTxtNode();
    if( pTxt )
        aCellStart.nContent.Assign( pTxt, 0 );
    if( aCellStart != aInsertEnd ) // An empty insertion will not been marked
    {
        SwPaM aTmpPam( aCellStart, aInsertEnd );
        pDoc->AppendRedline( new SwRedline( nsRedlineType_t::REDLINE_INSERT, aTmpPam ), true );
    }

    pDoc->SetRedlineMode_intern( eOld );
    return pUndo;
}


sal_Bool SwUndoTblCpyTbl::InsertRow( SwTable& rTbl, const SwSelBoxes& rBoxes,
                                sal_uInt16 nCnt )
{
    SwTableNode* pTblNd = (SwTableNode*)rTbl.GetTabSortBoxes()[0]->
                                GetSttNd()->FindTableNode();

    SwTableSortBoxes aTmpLst( 0, 5 );
    pInsRowUndo = new SwUndoTblNdsChg( UNDO_TABLE_INSROW, rBoxes, *pTblNd,
                                       0, 0, nCnt, sal_True, sal_False );
    aTmpLst.Insert( &rTbl.GetTabSortBoxes(), 0, rTbl.GetTabSortBoxes().Count() );

    sal_Bool bRet = rTbl.InsertRow( rTbl.GetFrmFmt()->GetDoc(), rBoxes, nCnt, sal_True );
    if( bRet )
        pInsRowUndo->SaveNewBoxes( *pTblNd, aTmpLst );
    else
        delete pInsRowUndo, pInsRowUndo = 0;
    return bRet;
}

sal_Bool SwUndoTblCpyTbl::IsEmpty() const
{
    return !pInsRowUndo && !pArr->Count();
}


//////////////////////////////////////////////////////////////////////////

SwUndoCpyTbl::SwUndoCpyTbl()
    : SwUndo( UNDO_CPYTBL ), pDel( 0 ), nTblNode( 0 )
{
}

SwUndoCpyTbl::~SwUndoCpyTbl()
{
    delete pDel;
}

void SwUndoCpyTbl::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc & rDoc = rContext.GetDoc();
    SwTableNode* pTNd = rDoc.GetNodes()[ nTblNode ]->GetTableNode();

    // harte SeitenUmbrueche am nachfolgenden Node verschieben
    SwCntntNode* pNextNd = rDoc.GetNodes()[ pTNd->EndOfSectionIndex()+1 ]->GetCntntNode();
    if( pNextNd )
    {
        SwFrmFmt* pTableFmt = pTNd->GetTable().GetFrmFmt();
        const SfxPoolItem *pItem;

        if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_PAGEDESC,
            sal_False, &pItem ) )
            pNextNd->SetAttr( *pItem );

        if( SFX_ITEM_SET == pTableFmt->GetItemState( RES_BREAK,
            sal_False, &pItem ) )
            pNextNd->SetAttr( *pItem );
    }

    SwPaM aPam( *pTNd, *pTNd->EndOfSectionNode(), 0 , 1 );
    pDel = new SwUndoDelete( aPam, sal_True );
}

void SwUndoCpyTbl::RedoImpl(::sw::UndoRedoContext & rContext)
{
    pDel->UndoImpl(rContext);
    delete pDel, pDel = 0;
}


//////////////////////////////////////////////////////////////////////////

SwUndoSplitTbl::SwUndoSplitTbl( const SwTableNode& rTblNd,
    SwSaveRowSpan* pRowSp, sal_uInt16 eMode, sal_Bool bNewSize )
    : SwUndo( UNDO_SPLIT_TABLE ),
    nTblNode( rTblNd.GetIndex() ), nOffset( 0 ), mpSaveRowSpan( pRowSp ), pSavTbl( 0 ),
    pHistory( 0 ), nMode( eMode ), nFmlEnd( 0 ), bCalcNewSize( bNewSize )
{
    switch( nMode )
    {
    case HEADLINE_BOXATRCOLLCOPY:
            pHistory = new SwHistory;
            // kein break;
    case HEADLINE_BORDERCOPY:
    case HEADLINE_BOXATTRCOPY:
        pSavTbl = new _SaveTable( rTblNd.GetTable(), 1, sal_False );
        break;
    }
}

SwUndoSplitTbl::~SwUndoSplitTbl()
{
    delete pSavTbl;
    delete pHistory;
    delete mpSaveRowSpan;
}

void SwUndoSplitTbl::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc *const pDoc = & rContext.GetDoc();
    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());

    pPam->DeleteMark();
    SwNodeIndex& rIdx = pPam->GetPoint()->nNode;
    rIdx = nTblNode + nOffset;

    //Den implizit erzeugten Absatz wieder entfernen.
    pDoc->GetNodes().Delete( rIdx, 1 );

    rIdx = nTblNode + nOffset;
    SwTableNode* pTblNd = rIdx.GetNode().GetTableNode();
    SwTable& rTbl = pTblNd->GetTable();

    SwTableFmlUpdate aMsgHnt( &rTbl );
    aMsgHnt.eFlags = TBL_BOXPTR;
    pDoc->UpdateTblFlds( &aMsgHnt );

    switch( nMode )
    {
    case HEADLINE_BOXATRCOLLCOPY:
        if( pHistory )
            pHistory->TmpRollback( pDoc, nFmlEnd );

        // kein break
    case HEADLINE_BOXATTRCOPY:
    case HEADLINE_BORDERCOPY:
        {
            pSavTbl->CreateNew( rTbl, sal_False );
            pSavTbl->RestoreAttr( rTbl );
        }
        break;

    case HEADLINE_CNTNTCOPY:
        // die erzeugte 1. Line muss wieder entfernt werden
        {
            SwSelBoxes aSelBoxes;
            SwTableBox* pBox = rTbl.GetTblBox( nTblNode + nOffset + 1 );
            rTbl.SelLineFromBox( pBox, aSelBoxes, sal_True );
            _FndBox aTmpBox( 0, 0 );
            aTmpBox.SetTableLines( aSelBoxes, rTbl );
            aTmpBox.DelFrms( rTbl );
            rTbl.DeleteSel( pDoc, aSelBoxes, 0, 0, sal_False, sal_False );
        }
        break;
    }

    pDoc->GetNodes().MergeTable( rIdx );

    if( pHistory )
    {
        pHistory->TmpRollback( pDoc, 0 );
        pHistory->SetTmpEnd( pHistory->Count() );
    }
    if( mpSaveRowSpan )
    {
        pTblNd = rIdx.GetNode().FindTableNode();
        if( pTblNd )
            pTblNd->GetTable().RestoreRowSpan( *mpSaveRowSpan );
    }
    ClearFEShellTabCols();
}

void SwUndoSplitTbl::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc *const pDoc = & rContext.GetDoc();
    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());

    pPam->DeleteMark();
    pPam->GetPoint()->nNode = nTblNode;
    pDoc->SplitTable( *pPam->GetPoint(), nMode, bCalcNewSize );

    ClearFEShellTabCols();
}

void SwUndoSplitTbl::RepeatImpl(::sw::RepeatContext & rContext)
{
    SwPaM *const pPam = & rContext.GetRepeatPaM();
    SwDoc *const pDoc = & rContext.GetDoc();

    pDoc->SplitTable( *pPam->GetPoint(), nMode, bCalcNewSize );
    ClearFEShellTabCols();
}

void SwUndoSplitTbl::SaveFormula( SwHistory& rHistory )
{
    if( !pHistory )
        pHistory = new SwHistory;

    nFmlEnd = rHistory.Count();
    pHistory->Move( 0, &rHistory );
}


//////////////////////////////////////////////////////////////////////////

SwUndoMergeTbl::SwUndoMergeTbl( const SwTableNode& rTblNd,
                                const SwTableNode& rDelTblNd,
                                sal_Bool bWithPrv, sal_uInt16 nMd )
    : SwUndo( UNDO_MERGE_TABLE ), pSavTbl( 0 ),
    pHistory( 0 ), nMode( nMd ), bWithPrev( bWithPrv )
{
    // Endnode der letzen Tabellenzelle merken, die auf der Position verbleibt
    if( bWithPrev )
        nTblNode = rDelTblNd.EndOfSectionIndex() - 1;
    else
        nTblNode = rTblNd.EndOfSectionIndex() - 1;

    aName = rDelTblNd.GetTable().GetFrmFmt()->GetName();
    pSavTbl = new _SaveTable( rDelTblNd.GetTable() );

    pSavHdl = bWithPrev ? new _SaveTable( rTblNd.GetTable(), 1 ) : 0;
}

SwUndoMergeTbl::~SwUndoMergeTbl()
{
    delete pSavTbl;
    delete pSavHdl;
    delete pHistory;
}

void SwUndoMergeTbl::UndoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc *const pDoc = & rContext.GetDoc();
    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());

    pPam->DeleteMark();
    SwNodeIndex& rIdx = pPam->GetPoint()->nNode;
    rIdx = nTblNode;

    SwTableNode* pTblNd = rIdx.GetNode().FindTableNode();
    SwTable* pTbl = &pTblNd->GetTable();

    SwTableFmlUpdate aMsgHnt( pTbl );
    aMsgHnt.eFlags = TBL_BOXPTR;
    pDoc->UpdateTblFlds( &aMsgHnt );

    //Lines fuer das Layout-Update herausuchen.
    _FndBox aFndBox( 0, 0 );
    aFndBox.SetTableLines( *pTbl );
    aFndBox.DelFrms( *pTbl );
    // ? TL_CHART2: notification or locking of controller required ?

    SwTableNode* pNew = pDoc->GetNodes().SplitTable( rIdx, sal_True, sal_False );

    //Layout updaten
    aFndBox.MakeFrms( *pTbl );
    // ? TL_CHART2: notification or locking of controller required ?

    if( bWithPrev )
    {
        // den Namen umsetzen
        pNew->GetTable().GetFrmFmt()->SetName( pTbl->GetFrmFmt()->GetName() );
        pSavHdl->RestoreAttr( pNew->GetTable() );
    }
    else
        pTbl = &pNew->GetTable();
    pTbl->GetFrmFmt()->SetName( aName );

//	pSavTbl->CreateNew( *pTbl, sal_False );
    pSavTbl->RestoreAttr( *pTbl );


    if( pHistory )
    {
        pHistory->TmpRollback( pDoc, 0 );
        pHistory->SetTmpEnd( pHistory->Count() );
    }

    // fuer die neue Tabelle die Frames anlegen
    SwNodeIndex aTmpIdx( *pNew );
    pNew->MakeFrms( &aTmpIdx );

    // Cursor  irgendwo in den Content stellen
    SwCntntNode* pCNd = pDoc->GetNodes().GoNext( &rIdx );
    pPam->GetPoint()->nContent.Assign( pCNd, 0 );

    ClearFEShellTabCols();

    // TL_CHART2: need to inform chart of probably changed cell names
    SwChartDataProvider *pPCD = pDoc->GetChartDataProvider();
    if (pPCD)
    {
        pDoc->UpdateCharts( pTbl->GetFrmFmt()->GetName() );
        pDoc->UpdateCharts( pNew->GetTable().GetFrmFmt()->GetName() );
    }
}

void SwUndoMergeTbl::RedoImpl(::sw::UndoRedoContext & rContext)
{
    SwDoc *const pDoc = & rContext.GetDoc();
    SwPaM *const pPam(& rContext.GetCursorSupplier().CreateNewShellCursor());

    pPam->DeleteMark();
    pPam->GetPoint()->nNode = nTblNode;
    if( bWithPrev )
        pPam->GetPoint()->nNode = nTblNode + 3;
    else
        pPam->GetPoint()->nNode = nTblNode;

    pDoc->MergeTable( *pPam->GetPoint(), bWithPrev, nMode );

    ClearFEShellTabCols();
}

void SwUndoMergeTbl::RepeatImpl(::sw::RepeatContext & rContext)
{
    SwDoc *const pDoc = & rContext.GetDoc();
    SwPaM *const pPam = & rContext.GetRepeatPaM();

    pDoc->MergeTable( *pPam->GetPoint(), bWithPrev, nMode );
    ClearFEShellTabCols();
}

void SwUndoMergeTbl::SaveFormula( SwHistory& rHistory )
{
    if( !pHistory )
        pHistory = new SwHistory;
    pHistory->Move( 0, &rHistory );
}


//////////////////////////////////////////////////////////////////////////

void InsertSort( SvUShorts& rArr, sal_uInt16 nIdx, sal_uInt16* pInsPos )
{
    sal_uInt16 nO	= rArr.Count(), nM, nU = 0;
    if( nO > 0 )
    {
        nO--;
        while( nU <= nO )
        {
            nM = nU + ( nO - nU ) / 2;
            if( *(rArr.GetData() + nM) == nIdx )
            {
                OSL_FAIL( "Index already exists. This should never happen." );
                return;
            }
            if( *(rArr.GetData() + nM) < nIdx )
                nU = nM + 1;
            else if( nM == 0 )
                break;
            else
                nO = nM - 1;
        }
    }
    rArr.Insert( nIdx, nU );
    if( pInsPos )
        *pInsPos = nU;
}

void InsertSort( SvULongs& rArr, sal_uLong nIdx, sal_uInt16* pInsPos )
{
    sal_uInt16 nO	= rArr.Count(), nM, nU = 0;
    if( nO > 0 )
    {
        nO--;
        while( nU <= nO )
        {
            nM = nU + ( nO - nU ) / 2;
            if( *(rArr.GetData() + nM) == nIdx )
            {
                OSL_FAIL( "Index ist schon vorhanden, darf nie sein!" );
                return;
            }
            if( *(rArr.GetData() + nM) < nIdx )
                nU = nM + 1;
            else if( nM == 0 )
                break;
            else
                nO = nM - 1;
        }
    }
    rArr.Insert( nIdx, nU );
    if( pInsPos )
        *pInsPos = nU;
}

#if OSL_DEBUG_LEVEL > 1


void CheckTable( const SwTable& rTbl )
{
    const SwNodes& rNds = rTbl.GetFrmFmt()->GetDoc()->GetNodes();
    const SwTableSortBoxes& rSrtArr = rTbl.GetTabSortBoxes();
    for( sal_uInt16 n = 0; n < rSrtArr.Count(); ++n )
    {
        const SwTableBox* pBox = rSrtArr[ n ];
        const SwNode* pNd = pBox->GetSttNd();
        OSL_ENSURE( rNds[ pBox->GetSttIdx() ] == pNd, "Box mit falchem StartNode"  );
    }
}
#endif


/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
