/*************************************************************************
 *
 * 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_sc.hxx"


#include <tools/debug.hxx>
#include <tools/shl.hxx>		// SHL_CALC
#include <tools/stack.hxx>
#include <tools/rtti.hxx>
#include <svl/zforlist.hxx>
#include <svl/itemset.hxx>
#include <svl/isethint.hxx>
#include <svl/itempool.hxx>
#include <sfx2/app.hxx>
#include <unotools/useroptions.hxx>
#include <sfx2/sfxsids.hrc>

#include "cell.hxx"
#include "document.hxx"
#include "dociter.hxx"
#include "global.hxx"
#include "rechead.hxx"
#include "scerrors.hxx"
#include "scmod.hxx"   		// SC_MOD
#include "inputopt.hxx" 	// GetExpandRefs
#include "patattr.hxx"
#include "hints.hxx"

#include "globstr.hrc"

#include <stack>

#define SC_CHGTRACK_CXX
#include "chgtrack.hxx"

DECLARE_STACK( ScChangeActionStack, ScChangeAction* )

const sal_uInt16 nMemPoolChangeActionCellListEntry = (0x2000 - 64) / sizeof(ScChangeActionCellListEntry);
IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionCellListEntry, nMemPoolChangeActionCellListEntry, nMemPoolChangeActionCellListEntry )

const sal_uInt16 nMemPoolChangeActionLinkEntry = (0x8000 - 64) / sizeof(ScChangeActionLinkEntry);
IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionLinkEntry, nMemPoolChangeActionLinkEntry, nMemPoolChangeActionLinkEntry )

// loaded MSB > eigenes => inkompatibel
#define SC_CHGTRACK_FILEFORMAT_FIRST	0x0001
#define SC_CHGTRACK_FILEFORMAT	0x0001

// --- ScChangeActionLinkEntry ---------------------------------------------

#if DEBUG_CHANGETRACK
String ScChangeActionLinkEntry::ToString() const
{
    String aReturn;
    if ( pAction )
    {
        aReturn = String::CreateFromInt64( static_cast< sal_Int64 >( pAction->GetActionNumber() ) );
    }
    else if ( pLink && pLink->pAction )
    {
        aReturn = String::CreateFromAscii( "*" );
        aReturn += String::CreateFromInt64( static_cast< sal_Int64 >( pLink->pAction->GetActionNumber() ) );
    }
    else
    {
        aReturn = String::CreateFromAscii( "-" );
    }

    return aReturn;
}
#endif // DEBUG_CHANGETRACK

// --- ScChangeAction ------------------------------------------------------

ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScRange& rRange )
		:
		aBigRange( rRange ),
		pNext( NULL ),
		pPrev( NULL ),
		pLinkAny( NULL ),
		pLinkDeletedIn( NULL ),
		pLinkDeleted( NULL ),
		pLinkDependent( NULL ),
		nAction( 0 ),
		nRejectAction( 0 ),
		eType( eTypeP ),
		eState( SC_CAS_VIRGIN )
{
	aDateTime.ConvertToUTC();
}

ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange,
						const sal_uLong nTempAction, const sal_uLong nTempRejectAction,
						const ScChangeActionState eTempState, const DateTime& aTempDateTime,
						const String& aTempUser,  const String& aTempComment)
		:
		aBigRange( rRange ),
		aDateTime( aTempDateTime ),
		aUser( aTempUser ),
		aComment( aTempComment ),
		pNext( NULL ),
		pPrev( NULL ),
		pLinkAny( NULL ),
		pLinkDeletedIn( NULL ),
		pLinkDeleted( NULL ),
		pLinkDependent( NULL ),
		nAction( nTempAction ),
		nRejectAction( nTempRejectAction ),
		eType( eTypeP ),
		eState( eTempState )
{
}

ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange,
						const sal_uLong nTempAction)
		:
		aBigRange( rRange ),
		pNext( NULL ),
		pPrev( NULL ),
		pLinkAny( NULL ),
		pLinkDeletedIn( NULL ),
		pLinkDeleted( NULL ),
		pLinkDependent( NULL ),
		nAction( nTempAction ),
		nRejectAction( 0 ),
		eType( eTypeP ),
		eState( SC_CAS_VIRGIN )
{
	aDateTime.ConvertToUTC();
}


ScChangeAction::~ScChangeAction()
{
	RemoveAllLinks();
}


sal_Bool ScChangeAction::IsVisible() const
{
	//! sequence order of execution is significant
	if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() )
		return sal_False;
	if ( GetType() == SC_CAT_CONTENT )
		return ((ScChangeActionContent*)this)->IsTopContent();
	return sal_True;
}


sal_Bool ScChangeAction::IsTouchable() const
{
	//! sequence order of execution is significant
	if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() )
		return sal_False;
	// content may reject and be touchable if on top
	if ( GetType() == SC_CAT_CONTENT )
		return ((ScChangeActionContent*)this)->IsTopContent();
	if ( IsRejecting() )
		return sal_False;
	return sal_True;
}


sal_Bool ScChangeAction::IsClickable() const
{
	//! sequence order of execution is significant
	if ( !IsVirgin() )
		return sal_False;
	if ( IsDeletedIn() )
		return sal_False;
	if ( GetType() == SC_CAT_CONTENT )
	{
		ScChangeActionContentCellType eCCT =
			ScChangeActionContent::GetContentCellType(
			((ScChangeActionContent*)this)->GetNewCell() );
		if ( eCCT == SC_CACCT_MATREF )
			return sal_False;
		if ( eCCT == SC_CACCT_MATORG )
		{	// no Accept-Select if one of the references is in a deleted col/row
			const ScChangeActionLinkEntry* pL =
				((ScChangeActionContent*)this)->GetFirstDependentEntry();
			while ( pL )
			{
				ScChangeAction* p = (ScChangeAction*) pL->GetAction();
				if ( p && p->IsDeletedIn() )
					return sal_False;
				pL = pL->GetNext();
			}
		}
		return sal_True;	// for Select() a content doesn't have to be touchable
	}
	return IsTouchable();	// Accept()/Reject() only on touchables
}


sal_Bool ScChangeAction::IsRejectable() const
{
	//! sequence order of execution is significant
	if ( !IsClickable() )
		return sal_False;
	if ( GetType() == SC_CAT_CONTENT )
	{
		if ( ((ScChangeActionContent*)this)->IsOldMatrixReference() )
			return sal_False;
		ScChangeActionContent* pNextContent =
			((ScChangeActionContent*)this)->GetNextContent();
		if ( pNextContent == NULL )
			return sal_True;		// *this is TopContent
		return pNextContent->IsRejected();		// *this is next rejectable
	}
	return IsTouchable();
}


sal_Bool ScChangeAction::IsInternalRejectable() const
{
	//! sequence order of execution is significant
	if ( !IsVirgin() )
		return sal_False;
	if ( IsDeletedIn() )
		return sal_False;
	if ( GetType() == SC_CAT_CONTENT )
	{
		ScChangeActionContent* pNextContent =
			((ScChangeActionContent*)this)->GetNextContent();
		if ( pNextContent == NULL )
			return sal_True;		// *this is TopContent
		return pNextContent->IsRejected();		// *this is next rejectable
	}
	return IsTouchable();
}


sal_Bool ScChangeAction::IsDialogRoot() const
{
	return IsInternalRejectable();		// only rejectables in root
}


sal_Bool ScChangeAction::IsDialogParent() const
{
	//! sequence order of execution is significant
	if ( GetType() == SC_CAT_CONTENT )
	{
		if ( !IsDialogRoot() )
			return sal_False;
		if ( ((ScChangeActionContent*)this)->IsMatrixOrigin() && HasDependent() )
			return sal_True;
		ScChangeActionContent* pPrevContent =
			((ScChangeActionContent*)this)->GetPrevContent();
		return pPrevContent && pPrevContent->IsVirgin();
	}
	if ( HasDependent() )
		return IsDeleteType() ? sal_True : !IsDeletedIn();
	if ( HasDeleted() )
	{
		if ( IsDeleteType() )
		{
			if ( IsDialogRoot() )
				return sal_True;
			ScChangeActionLinkEntry* pL = pLinkDeleted;
			while ( pL )
			{
				ScChangeAction* p = pL->GetAction();
				if ( p && p->GetType() != eType )
					return sal_True;
				pL = pL->GetNext();
			}
		}
		else
			return sal_True;
	}
	return sal_False;
}


sal_Bool ScChangeAction::IsMasterDelete() const
{
	if ( !IsDeleteType() )
		return sal_False;
	ScChangeActionDel* pDel = (ScChangeActionDel*) this;
	return pDel->IsMultiDelete() && (pDel->IsTopDelete() || pDel->IsRejectable());
}


void ScChangeAction::RemoveAllLinks()
{
	RemoveAllAnyLinks();
	RemoveAllDeletedIn();
	RemoveAllDeleted();
	RemoveAllDependent();
}


void ScChangeAction::RemoveAllAnyLinks()
{
	while ( pLinkAny )
		delete pLinkAny;		// rueckt sich selbst hoch
}


sal_Bool ScChangeAction::RemoveDeletedIn( const ScChangeAction* p )
{
	sal_Bool bRemoved = sal_False;
	ScChangeActionLinkEntry* pL = GetDeletedIn();
	while ( pL )
	{
		ScChangeActionLinkEntry* pNextLink = pL->GetNext();
		if ( pL->GetAction() == p )
		{
			delete pL;
			bRemoved = sal_True;
		}
		pL = pNextLink;
	}
	return bRemoved;
}


sal_Bool ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const
{
	ScChangeActionLinkEntry* pL = GetDeletedIn();
	while ( pL )
	{
		if ( pL->GetAction() == p )
			return sal_True;
		pL = pL->GetNext();
	}
	return sal_False;
}


void ScChangeAction::RemoveAllDeletedIn()
{
	//! nicht vom evtl. TopContent sondern wirklich dieser
	while ( pLinkDeletedIn )
		delete pLinkDeletedIn;		// rueckt sich selbst hoch
}


sal_Bool ScChangeAction::IsDeletedInDelType( ScChangeActionType eDelType ) const
{
	ScChangeAction* p;
	ScChangeActionLinkEntry* pL = GetDeletedIn();
	if ( pL )
	{
		// InsertType fuer MergePrepare/MergeOwn
		ScChangeActionType eInsType;
		switch ( eDelType )
		{
			case SC_CAT_DELETE_COLS :
				eInsType = SC_CAT_INSERT_COLS;
			break;
			case SC_CAT_DELETE_ROWS :
				eInsType = SC_CAT_INSERT_ROWS;
			break;
			case SC_CAT_DELETE_TABS :
				eInsType = SC_CAT_INSERT_TABS;
			break;
			default:
				eInsType = SC_CAT_NONE;
		}
		while ( pL )
		{
            if ( (p = pL->GetAction()) != NULL &&
					(p->GetType() == eDelType || p->GetType() == eInsType) )
				return sal_True;
			pL = pL->GetNext();
		}
	}
	return sal_False;
}


void ScChangeAction::SetDeletedIn( ScChangeAction* p )
{
	ScChangeActionLinkEntry* pLink1 = AddDeletedIn( p );
	ScChangeActionLinkEntry* pLink2;
	if ( GetType() == SC_CAT_CONTENT )
		pLink2 = p->AddDeleted( ((ScChangeActionContent*)this)->GetTopContent() );
	else
		pLink2 = p->AddDeleted( this );
	pLink1->SetLink( pLink2 );
}


void ScChangeAction::RemoveAllDeleted()
{
	while ( pLinkDeleted )
		delete pLinkDeleted;		// rueckt sich selbst hoch
}


void ScChangeAction::RemoveAllDependent()
{
	while ( pLinkDependent )
		delete pLinkDependent;		// rueckt sich selbst hoch
}


DateTime ScChangeAction::GetDateTime() const
{
	DateTime aDT( aDateTime );
	aDT.ConvertToLocalTime();
	return aDT;
}


void ScChangeAction::UpdateReference( const ScChangeTrack* /* pTrack */,
		UpdateRefMode eMode, const ScBigRange& rRange,
		sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
{
	ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
}


void ScChangeAction::GetDescription( String& rStr, ScDocument* /* pDoc */,
        sal_Bool /* bSplitRange */, bool bWarning ) const
{
    if ( IsRejecting() && bWarning )
    {
        // #112261# Add comment if rejection may have resulted in references
        // not properly restored in formulas. See specification at
        // http://specs.openoffice.org/calc/ease-of-use/redlining_comment.sxw
        if (GetType() == SC_CAT_MOVE)
        {
            rStr += ScGlobal::GetRscString(
                    STR_CHANGED_MOVE_REJECTION_WARNING);
            rStr += ' ';
        }
        else if (IsInsertType())
        {
            rStr += ScGlobal::GetRscString(
                    STR_CHANGED_DELETE_REJECTION_WARNING);
            rStr += ' ';
        }
        else
        {
            const ScChangeTrack* pCT = GetChangeTrack();
            if (pCT)
            {
                ScChangeAction* pReject = pCT->GetActionOrGenerated(
                        GetRejectAction());
                if (pReject)
                {
                    if (pReject->GetType() == SC_CAT_MOVE)
                    {
                        rStr += ScGlobal::GetRscString(
                                STR_CHANGED_MOVE_REJECTION_WARNING);
                        rStr += ' ';
                    }
                    else if (pReject->IsDeleteType())
                    {
                        rStr += ScGlobal::GetRscString(
                                STR_CHANGED_DELETE_REJECTION_WARNING);
                        rStr += ' ';
                    }
                    else if (pReject->HasDependent())
                    {
                        ScChangeActionTable aTable;
                        pCT->GetDependents( pReject, aTable, sal_False, sal_True );
                        for ( const ScChangeAction* p = aTable.First(); p;
                                p = aTable.Next() )
                        {
                            if (p->GetType() == SC_CAT_MOVE)
                            {
                                rStr += ScGlobal::GetRscString(
                                        STR_CHANGED_MOVE_REJECTION_WARNING);
                                rStr += ' ';
                                break;  // for
                            }
                            else if (pReject->IsDeleteType())
                            {
                                rStr += ScGlobal::GetRscString(
                                        STR_CHANGED_DELETE_REJECTION_WARNING);
                                rStr += ' ';
                                break;  // for
                            }
                        }
                    }
                }
            }
        }
    }
}


String ScChangeAction::GetRefString( const ScBigRange& rRange,
		ScDocument* pDoc, sal_Bool bFlag3D ) const
{
	String aStr;
	sal_uInt16 nFlags = ( rRange.IsValid( pDoc ) ? SCA_VALID : 0 );
	if ( !nFlags )
		aStr = ScGlobal::GetRscString( STR_NOREF_STR );
	else
	{
		ScRange aTmpRange( rRange.MakeRange() );
		switch ( GetType() )
		{
			case SC_CAT_INSERT_COLS :
			case SC_CAT_DELETE_COLS :
				if ( bFlag3D )
				{
					pDoc->GetName( aTmpRange.aStart.Tab(), aStr );
					aStr += '.';
				}
				aStr += ::ScColToAlpha( aTmpRange.aStart.Col() );
				aStr += ':';
				aStr += ::ScColToAlpha( aTmpRange.aEnd.Col() );
			break;
			case SC_CAT_INSERT_ROWS :
			case SC_CAT_DELETE_ROWS :
				if ( bFlag3D )
				{
					pDoc->GetName( aTmpRange.aStart.Tab(), aStr );
					aStr += '.';
				}
				aStr += String::CreateFromInt32( aTmpRange.aStart.Row() + 1 );
				aStr += ':';
				aStr += String::CreateFromInt32( aTmpRange.aEnd.Row() + 1 );
			break;
			default:
				if ( bFlag3D || GetType() == SC_CAT_INSERT_TABS )
					nFlags |= SCA_TAB_3D;
				aTmpRange.Format( aStr, nFlags, pDoc, pDoc->GetAddressConvention() );
		}
		if ( (bFlag3D && IsDeleteType()) || IsDeletedIn() )
		{
			aStr.Insert( '(', 0 );
			aStr += ')';
		}
	}
	return aStr;
}


void ScChangeAction::GetRefString( String& rStr, ScDocument* pDoc,
		sal_Bool bFlag3D ) const
{
	rStr = GetRefString( GetBigRange(), pDoc, bFlag3D );
}


void ScChangeAction::Accept()
{
	if ( IsVirgin() )
	{
		SetState( SC_CAS_ACCEPTED );
		DeleteCellEntries();
	}
}


void ScChangeAction::SetRejected()
{
	if ( IsVirgin() )
	{
		SetState( SC_CAS_REJECTED );
		RemoveAllLinks();
		DeleteCellEntries();
	}
}


void ScChangeAction::RejectRestoreContents( ScChangeTrack* pTrack,
		SCsCOL nDx, SCsROW nDy )
{
	// Liste der Contents aufbauen
	ScChangeActionCellListEntry* pListContents = NULL;
	for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
	{
		ScChangeAction* p = pL->GetAction();
		if ( p && p->GetType() == SC_CAT_CONTENT )
		{
			ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry(
				(ScChangeActionContent*) p, pListContents );
			pListContents = pE;
		}
	}
	SetState( SC_CAS_REJECTED );		// vor UpdateReference fuer Move
	pTrack->UpdateReference( this, sal_True );		// LinkDeleted freigeben
	DBG_ASSERT( !pLinkDeleted, "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL" );
	// Liste der Contents abarbeiten und loeschen
	ScDocument* pDoc = pTrack->GetDocument();
	ScChangeActionCellListEntry* pE = pListContents;
	while ( pE )
	{
		if ( !pE->pContent->IsDeletedIn() &&
				pE->pContent->GetBigRange().aStart.IsValid( pDoc ) )
			pE->pContent->PutNewValueToDoc( pDoc, nDx, nDy );
        ScChangeActionCellListEntry* pNextEntry;
        pNextEntry = pE->pNext;
		delete pE;
        pE = pNextEntry;
	}
	DeleteCellEntries();		// weg mit den generierten
}


void ScChangeAction::SetDeletedInThis( sal_uLong nActionNumber,
		const ScChangeTrack* pTrack )
{
	if ( nActionNumber )
	{
		ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
		DBG_ASSERT( pAct, "ScChangeAction::SetDeletedInThis: missing Action" );
		if ( pAct )
			pAct->SetDeletedIn( this );
	}
}


void ScChangeAction::AddDependent( sal_uLong nActionNumber,
		const ScChangeTrack* pTrack )
{
	if ( nActionNumber )
	{
		ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
		DBG_ASSERT( pAct, "ScChangeAction::AddDependent: missing Action" );
		if ( pAct )
		{
			ScChangeActionLinkEntry* pLink = AddDependent( pAct );
			pAct->AddLink( this, pLink );
		}
	}
}


#if DEBUG_CHANGETRACK
String ScChangeAction::ToString( ScDocument* pDoc ) const
{
    String aReturn;

    String aNumber = String::CreateFromInt64( static_cast< sal_Int64 >( GetActionNumber() ) );

    String aActionState;
    ScChangeActionState eActionState = GetState();
    switch ( eActionState )
    {
        case SC_CAS_VIRGIN:
            {
                aActionState = String::CreateFromAscii( " " );
            }
            break;
        case SC_CAS_ACCEPTED:
            {
                aActionState = String::CreateFromAscii( "+" );
            }
            break;
        case SC_CAS_REJECTED:
            {
                aActionState = String::CreateFromAscii( "-" );
            }
            break;
    }

    String aRejectAction;
    if ( IsRejecting() )
    {
        aRejectAction += 'r';
        aRejectAction += String::CreateFromInt64( static_cast< sal_Int64 >( GetRejectAction() ) );
    }

    String aReference;
    GetRefString( aReference, pDoc, sal_True );

    String aAuthor = GetUser();

    DateTime aDT = GetDateTime();
    String aDate = ScGlobal::pLocaleData->getDate( aDT );
    aDate += ' ';
    aDate += ScGlobal::pLocaleData->getTime( aDT, sal_False, sal_False );

    String aDescription;
    GetDescription( aDescription, pDoc );

    String aLinkAny;
    const ScChangeActionLinkEntry* pLinkA = pLinkAny;
    while ( pLinkA )
    {
        if ( !aLinkAny.Len() )
        {
            aLinkAny = String::CreateFromAscii( "(Any:" );
        }
        aLinkAny += String::CreateFromAscii( " ->" );
        aLinkAny += pLinkA->ToString();
        pLinkA = pLinkA->GetNext();
    }
    if ( aLinkAny.Len() )
    {
        aLinkAny += ')';
    }

    String aLinkDeletedIn;
    const ScChangeActionLinkEntry* pLinkDI = pLinkDeletedIn;
    while ( pLinkDI )
    {
        if ( !aLinkDeletedIn.Len() )
        {
            aLinkDeletedIn = String::CreateFromAscii( "(DeletedIn:" );
        }
        aLinkDeletedIn += String::CreateFromAscii( " ->" );
        aLinkDeletedIn += pLinkDI->ToString();
        pLinkDI = pLinkDI->GetNext();
    }
    if ( aLinkDeletedIn.Len() )
    {
        aLinkDeletedIn += ')';
    }

    String aLinkDeleted;
    const ScChangeActionLinkEntry* pLinkD = pLinkDeleted;
    while ( pLinkD )
    {
        if ( !aLinkDeleted.Len() )
        {
            aLinkDeleted = String::CreateFromAscii( "(Deleted:" );
        }
        aLinkDeleted += String::CreateFromAscii( " ->" );
        aLinkDeleted += pLinkD->ToString();
        pLinkD = pLinkD->GetNext();
    }
    if ( aLinkDeleted.Len() )
    {
        aLinkDeleted += ')';
    }

    String aLinkDependent;
    const ScChangeActionLinkEntry* pLinkDp = pLinkDependent;
    while ( pLinkDp )
    {
        if ( !aLinkDependent.Len() )
        {
            aLinkDependent = String::CreateFromAscii( "(Dependent:" );
        }
        aLinkDependent += String::CreateFromAscii( " ->" );
        aLinkDependent += pLinkDp->ToString();
        pLinkDp = pLinkDp->GetNext();
    }
    if ( aLinkDependent.Len() )
    {
        aLinkDependent += ')';
    }

    aReturn += aNumber;
    aReturn += aActionState;
    aReturn += aRejectAction;
    aReturn += String::CreateFromAscii( ": " );
    aReturn += aReference;
    aReturn += ' ';
    aReturn += aAuthor;
    aReturn += ' ';
    aReturn += aDate;
    aReturn += ' ';
    aReturn += aDescription;
    aReturn += ' ';
    aReturn += aLinkAny;
    aReturn += ' ';
    aReturn += aLinkDeletedIn;
    aReturn += ' ';
    aReturn += aLinkDeleted;
    aReturn += ' ';
    aReturn += aLinkDependent;

    return aReturn;
}
#endif // DEBUG_CHANGETRACK


// --- ScChangeActionIns ---------------------------------------------------

ScChangeActionIns::ScChangeActionIns( const ScRange& rRange )
		: ScChangeAction( SC_CAT_NONE, rRange )
{
	if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL )
	{
		aBigRange.aStart.SetCol( nInt32Min );
		aBigRange.aEnd.SetCol( nInt32Max );
		if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW )
		{
			SetType( SC_CAT_INSERT_TABS );
			aBigRange.aStart.SetRow( nInt32Min );
			aBigRange.aEnd.SetRow( nInt32Max );
		}
		else
			SetType( SC_CAT_INSERT_ROWS );
	}
	else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW )
	{
		SetType( SC_CAT_INSERT_COLS );
		aBigRange.aStart.SetRow( nInt32Min );
		aBigRange.aEnd.SetRow( nInt32Max );
	}
	else
	{
		DBG_ERROR( "ScChangeActionIns: Block not supported!" );
	}
}


ScChangeActionIns::ScChangeActionIns(const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
                                                const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment,
                                                const ScChangeActionType eTypeP)
		:
        ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment)
{
}

ScChangeActionIns::~ScChangeActionIns()
{
}


void ScChangeActionIns::GetDescription( String& rStr, ScDocument* pDoc,
		sal_Bool bSplitRange, bool bWarning ) const
{
    ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning );

	sal_uInt16 nWhatId;
	switch ( GetType() )
	{
		case SC_CAT_INSERT_COLS :
			nWhatId = STR_COLUMN;
		break;
		case SC_CAT_INSERT_ROWS :
			nWhatId = STR_ROW;
		break;
		default:
			nWhatId = STR_AREA;
	}

	String aRsc( ScGlobal::GetRscString( STR_CHANGED_INSERT ) );
	xub_StrLen nPos = aRsc.SearchAscii( "#1" );
	rStr += aRsc.Copy( 0, nPos );
	rStr += ScGlobal::GetRscString( nWhatId );
	rStr += ' ';
	rStr += GetRefString( GetBigRange(), pDoc );
	rStr += aRsc.Copy( nPos+2 );
}


sal_Bool ScChangeActionIns::Reject( ScDocument* pDoc )
{
	if ( !aBigRange.IsValid( pDoc ) )
		return sal_False;

	ScRange aRange( aBigRange.MakeRange() );
	if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
			aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
		return sal_False;

	switch ( GetType() )
	{
		case SC_CAT_INSERT_COLS :
			pDoc->DeleteCol( aRange );
		break;
		case SC_CAT_INSERT_ROWS :
			pDoc->DeleteRow( aRange );
		break;
		case SC_CAT_INSERT_TABS :
			pDoc->DeleteTab( aRange.aStart.Tab() );
		break;
        default:
        {
            // added to avoid warnings
        }
	}
	SetState( SC_CAS_REJECTED );
	RemoveAllLinks();
	return sal_True;
}


// --- ScChangeActionDel ---------------------------------------------------

ScChangeActionDel::ScChangeActionDel( const ScRange& rRange,
			SCsCOL nDxP, SCsROW nDyP, ScChangeTrack* pTrackP )
		:
		ScChangeAction( SC_CAT_NONE, rRange ),
		pTrack( pTrackP ),
		pFirstCell( NULL ),
		pCutOff( NULL ),
		nCutOff( 0 ),
		pLinkMove( NULL ),
		nDx( nDxP ),
		nDy( nDyP )
{
	if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL )
	{
		aBigRange.aStart.SetCol( nInt32Min );
		aBigRange.aEnd.SetCol( nInt32Max );
		if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW )
		{
			SetType( SC_CAT_DELETE_TABS );
			aBigRange.aStart.SetRow( nInt32Min );
			aBigRange.aEnd.SetRow( nInt32Max );
		}
		else
			SetType( SC_CAT_DELETE_ROWS );
	}
	else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW )
	{
		SetType( SC_CAT_DELETE_COLS );
		aBigRange.aStart.SetRow( nInt32Min );
		aBigRange.aEnd.SetRow( nInt32Max );
	}
	else
	{
		DBG_ERROR( "ScChangeActionDel: Block not supported!" );
	}
}


ScChangeActionDel::ScChangeActionDel(const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
                                    const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String &sComment,
                                    const ScChangeActionType eTypeP, const SCsCOLROW nD, ScChangeTrack* pTrackP) // wich of nDx and nDy is set is depend on the type
		:
        ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
		pTrack( pTrackP ),
		pFirstCell( NULL ),
		pCutOff( NULL ),
		nCutOff( 0 ),
		pLinkMove( NULL ),
		nDx( 0 ),
		nDy( 0 )
{
	if (eType == SC_CAT_DELETE_COLS)
		nDx = static_cast<SCsCOL>(nD);
	else if (eType == SC_CAT_DELETE_ROWS)
		nDy = static_cast<SCsROW>(nD);
}

ScChangeActionDel::~ScChangeActionDel()
{
	DeleteCellEntries();
	while ( pLinkMove )
		delete pLinkMove;
}

void ScChangeActionDel::AddContent( ScChangeActionContent* pContent )
{
	ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry(
		pContent, pFirstCell );
	pFirstCell = pE;
}


void ScChangeActionDel::DeleteCellEntries()
{
	pTrack->DeleteCellEntries( pFirstCell, this );
}


sal_Bool ScChangeActionDel::IsBaseDelete() const
{
	return !GetDx() && !GetDy();
}


sal_Bool ScChangeActionDel::IsTopDelete() const
{
	const ScChangeAction* p = GetNext();
	if ( !p || p->GetType() != GetType() )
		return sal_True;
	return ((ScChangeActionDel*)p)->IsBaseDelete();
}


sal_Bool ScChangeActionDel::IsMultiDelete() const
{
	if ( GetDx() || GetDy() )
		return sal_True;
	const ScChangeAction* p = GetNext();
	if ( !p || p->GetType() != GetType() )
		return sal_False;
	const ScChangeActionDel* pDel = (const ScChangeActionDel*) p;
	if ( (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) &&
			pDel->GetBigRange() == aBigRange )
		return sal_True;
	return sal_False;
}


sal_Bool ScChangeActionDel::IsTabDeleteCol() const
{
	if ( GetType() != SC_CAT_DELETE_COLS )
		return sal_False;
	const ScChangeAction* p = this;
	while ( p && p->GetType() == SC_CAT_DELETE_COLS &&
			!((const ScChangeActionDel*)p)->IsTopDelete() )
		p = p->GetNext();
	return p && p->GetType() == SC_CAT_DELETE_TABS;
}


void ScChangeActionDel::UpdateReference( const ScChangeTrack* /* pTrack */,
		UpdateRefMode eMode, const ScBigRange& rRange,
        sal_Int32 nDxP, sal_Int32 nDyP, sal_Int32 nDz )
{
    ScRefUpdate::Update( eMode, rRange, nDxP, nDyP, nDz, GetBigRange() );
	if ( !IsDeletedIn() )
		return ;
	// evtl. in "druntergerutschten" anpassen
	for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
	{
		ScChangeAction* p = pL->GetAction();
		if ( p && p->GetType() == SC_CAT_CONTENT &&
				!GetBigRange().In( p->GetBigRange() ) )
		{
			switch ( GetType() )
			{
				case SC_CAT_DELETE_COLS :
					p->GetBigRange().aStart.SetCol( GetBigRange().aStart.Col() );
					p->GetBigRange().aEnd.SetCol( GetBigRange().aStart.Col() );
				break;
				case SC_CAT_DELETE_ROWS :
					p->GetBigRange().aStart.SetRow( GetBigRange().aStart.Row() );
					p->GetBigRange().aEnd.SetRow( GetBigRange().aStart.Row() );
				break;
				case SC_CAT_DELETE_TABS :
					p->GetBigRange().aStart.SetTab( GetBigRange().aStart.Tab() );
					p->GetBigRange().aEnd.SetTab( GetBigRange().aStart.Tab() );
				break;
                default:
                {
                    // added to avoid warnings
                }
			}
		}
	}
}


ScBigRange ScChangeActionDel::GetOverAllRange() const
{
	ScBigRange aTmpRange( GetBigRange() );
	aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
	aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
	return aTmpRange;
}


void ScChangeActionDel::GetDescription( String& rStr, ScDocument* pDoc,
		sal_Bool bSplitRange, bool bWarning ) const
{
    ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning );

	sal_uInt16 nWhatId;
	switch ( GetType() )
	{
		case SC_CAT_DELETE_COLS :
			nWhatId = STR_COLUMN;
		break;
		case SC_CAT_DELETE_ROWS :
			nWhatId = STR_ROW;
		break;
		default:
			nWhatId = STR_AREA;
	}

	ScBigRange aTmpRange( GetBigRange() );
	if ( !IsRejected() )
	{
		if ( bSplitRange )
		{
			aTmpRange.aStart.SetCol( aTmpRange.aStart.Col() + GetDx() );
			aTmpRange.aStart.SetRow( aTmpRange.aStart.Row() + GetDy() );
		}
		aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() );
		aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() );
	}

	String aRsc( ScGlobal::GetRscString( STR_CHANGED_DELETE ) );
	xub_StrLen nPos = aRsc.SearchAscii( "#1" );
	rStr += aRsc.Copy( 0, nPos );
	rStr += ScGlobal::GetRscString( nWhatId );
	rStr += ' ';
	rStr += GetRefString( aTmpRange, pDoc );
	rStr += aRsc.Copy( nPos+2 );
}


sal_Bool ScChangeActionDel::Reject( ScDocument* pDoc )
{
	if ( !aBigRange.IsValid( pDoc ) && GetType() != SC_CAT_DELETE_TABS )
		return sal_False;

	sal_Bool bOk = sal_True;

	if ( IsTopDelete() )
	{	// den kompletten Bereich in einem Rutsch restaurieren
		ScBigRange aTmpRange( GetOverAllRange() );
		if ( !aTmpRange.IsValid( pDoc ) )
		{
			if ( GetType() == SC_CAT_DELETE_TABS )
			{	// wird Tab angehaengt?
				if ( aTmpRange.aStart.Tab() > pDoc->GetMaxTableNumber() )
					bOk = sal_False;
			}
			else
				bOk = sal_False;
		}
		if ( bOk )
		{
			ScRange aRange( aTmpRange.MakeRange() );
			// InDelete... fuer Formel UpdateReference in Document
			pTrack->SetInDeleteRange( aRange );
			pTrack->SetInDeleteTop( sal_True );
			pTrack->SetInDeleteUndo( sal_True );
			pTrack->SetInDelete( sal_True );
			switch ( GetType() )
			{
				case SC_CAT_DELETE_COLS :
					if ( !(aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MAXCOL) )
					{	// nur wenn nicht TabDelete
                        if ( ( bOk = pDoc->CanInsertCol( aRange ) ) != sal_False )
							bOk = pDoc->InsertCol( aRange );
					}
				break;
				case SC_CAT_DELETE_ROWS :
                    if ( ( bOk = pDoc->CanInsertRow( aRange ) ) != sal_False )
						bOk = pDoc->InsertRow( aRange );
				break;
				case SC_CAT_DELETE_TABS :
				{
//2do: Tabellennamen merken?
					String aName;
					pDoc->CreateValidTabName( aName );
                    if ( ( bOk = pDoc->ValidNewTabName( aName ) ) != sal_False )
						bOk = pDoc->InsertTab( aRange.aStart.Tab(), aName );
				}
				break;
                default:
                {
                    // added to avoid warnings
                }
			}
			pTrack->SetInDelete( sal_False );
			pTrack->SetInDeleteUndo( sal_False );
		}
		if ( !bOk )
		{
			pTrack->SetInDeleteTop( sal_False );
			return sal_False;
		}
		// InDeleteTop fuer UpdateReference-Undo behalten
	}

	// setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries
	RejectRestoreContents( pTrack, GetDx(), GetDy() );

	pTrack->SetInDeleteTop( sal_False );
	RemoveAllLinks();
	return sal_True;
}


void ScChangeActionDel::UndoCutOffMoves()
{	// abgeschnittene Moves wiederherstellen, Entries/Links deleten
	while ( pLinkMove )
	{
		ScChangeActionMove* pMove = pLinkMove->GetMove();
		short nFrom = pLinkMove->GetCutOffFrom();
		short nTo = pLinkMove->GetCutOffTo();
		switch ( GetType() )
		{
			case SC_CAT_DELETE_COLS :
				if ( nFrom > 0 )
					pMove->GetFromRange().aStart.IncCol( -nFrom );
				else if ( nFrom < 0 )
					pMove->GetFromRange().aEnd.IncCol( -nFrom );
				if ( nTo > 0 )
					pMove->GetBigRange().aStart.IncCol( -nTo );
				else if ( nTo < 0 )
					pMove->GetBigRange().aEnd.IncCol( -nTo );
			break;
			case SC_CAT_DELETE_ROWS :
				if ( nFrom > 0 )
					pMove->GetFromRange().aStart.IncRow( -nFrom );
				else if ( nFrom < 0 )
					pMove->GetFromRange().aEnd.IncRow( -nFrom );
				if ( nTo > 0 )
					pMove->GetBigRange().aStart.IncRow( -nTo );
				else if ( nTo < 0 )
					pMove->GetBigRange().aEnd.IncRow( -nTo );
			break;
			case SC_CAT_DELETE_TABS :
				if ( nFrom > 0 )
					pMove->GetFromRange().aStart.IncTab( -nFrom );
				else if ( nFrom < 0 )
					pMove->GetFromRange().aEnd.IncTab( -nFrom );
				if ( nTo > 0 )
					pMove->GetBigRange().aStart.IncTab( -nTo );
				else if ( nTo < 0 )
					pMove->GetBigRange().aEnd.IncTab( -nTo );
			break;
            default:
            {
                // added to avoid warnings
            }
		}
		delete pLinkMove;		// rueckt sich selbst hoch
	}
}

void ScChangeActionDel::UndoCutOffInsert()
{	// abgeschnittenes Insert wiederherstellen
	if ( pCutOff )
	{
		switch ( pCutOff->GetType() )
		{
			case SC_CAT_INSERT_COLS :
				if ( nCutOff < 0 )
					pCutOff->GetBigRange().aEnd.IncCol( -nCutOff );
				else
					pCutOff->GetBigRange().aStart.IncCol( -nCutOff );
			break;
			case SC_CAT_INSERT_ROWS :
				if ( nCutOff < 0 )
					pCutOff->GetBigRange().aEnd.IncRow( -nCutOff );
				else
					pCutOff->GetBigRange().aStart.IncRow( -nCutOff );
			break;
			case SC_CAT_INSERT_TABS :
				if ( nCutOff < 0 )
					pCutOff->GetBigRange().aEnd.IncTab( -nCutOff );
				else
					pCutOff->GetBigRange().aStart.IncTab( -nCutOff );
			break;
            default:
            {
                // added to avoid warnings
            }
		}
		SetCutOffInsert( NULL, 0 );
	}
}


// --- ScChangeActionMove --------------------------------------------------

ScChangeActionMove::ScChangeActionMove(const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
                                    const ScBigRange& aToBigRange, const String& aUserP, const DateTime& aDateTimeP, const String &sComment,
									const ScBigRange& aFromBigRange, ScChangeTrack* pTrackP) // wich of nDx and nDy is set is depend on the type
		:
        ScChangeAction(SC_CAT_MOVE, aToBigRange, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
		aFromRange(aFromBigRange),
		pTrack( pTrackP ),
		pFirstCell( NULL ),
		nStartLastCut(0),
		nEndLastCut(0)
{
}

ScChangeActionMove::~ScChangeActionMove()
{
	DeleteCellEntries();
}


void ScChangeActionMove::AddContent( ScChangeActionContent* pContent )
{
	ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry(
		pContent, pFirstCell );
	pFirstCell = pE;
}


void ScChangeActionMove::DeleteCellEntries()
{
	pTrack->DeleteCellEntries( pFirstCell, this );
}


void ScChangeActionMove::UpdateReference( const ScChangeTrack* /* pTrack */,
		UpdateRefMode eMode, const ScBigRange& rRange,
		sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
{
	ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aFromRange );
	ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() );
}


void ScChangeActionMove::GetDelta( sal_Int32& nDx, sal_Int32& nDy, sal_Int32& nDz ) const
{
	const ScBigAddress& rToPos = GetBigRange().aStart;
	const ScBigAddress& rFromPos = GetFromRange().aStart;
	nDx = rToPos.Col() - rFromPos.Col();
	nDy = rToPos.Row() - rFromPos.Row();
	nDz = rToPos.Tab() - rFromPos.Tab();
}


void ScChangeActionMove::GetDescription( String& rStr, ScDocument* pDoc,
		sal_Bool bSplitRange, bool bWarning ) const
{
    ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning );

	sal_Bool bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() );

	String aRsc( ScGlobal::GetRscString( STR_CHANGED_MOVE ) );

	xub_StrLen nPos = 0;
	String aTmpStr = ScChangeAction::GetRefString( GetFromRange(), pDoc, bFlag3D );
	nPos = aRsc.SearchAscii( "#1", nPos );
	aRsc.Erase( nPos, 2 );
	aRsc.Insert( aTmpStr, nPos );
    nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() );

	aTmpStr = ScChangeAction::GetRefString( GetBigRange(), pDoc, bFlag3D );
	nPos = aRsc.SearchAscii( "#2", nPos );
	aRsc.Erase( nPos, 2 );
	aRsc.Insert( aTmpStr, nPos );
	nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() );

	rStr += aRsc;
}


void ScChangeActionMove::GetRefString( String& rStr, ScDocument* pDoc,
		sal_Bool bFlag3D ) const
{
	if ( !bFlag3D )
		bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() );
	rStr = ScChangeAction::GetRefString( GetFromRange(), pDoc, bFlag3D );
	rStr += ',';
	rStr += ' ';
	rStr += ScChangeAction::GetRefString( GetBigRange(), pDoc, bFlag3D );
}


sal_Bool ScChangeActionMove::Reject( ScDocument* pDoc )
{
	if ( !(aBigRange.IsValid( pDoc ) && aFromRange.IsValid( pDoc )) )
		return sal_False;

	ScRange aToRange( aBigRange.MakeRange() );
	ScRange aFrmRange( aFromRange.MakeRange() );

	sal_Bool bOk = pDoc->IsBlockEditable( aToRange.aStart.Tab(),
		aToRange.aStart.Col(), aToRange.aStart.Row(),
		aToRange.aEnd.Col(), aToRange.aEnd.Row() );
	if ( bOk )
		bOk = pDoc->IsBlockEditable( aFrmRange.aStart.Tab(),
			aFrmRange.aStart.Col(), aFrmRange.aStart.Row(),
			aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row() );
	if ( !bOk )
		return sal_False;

	pTrack->LookUpContents( aToRange, pDoc, 0, 0, 0 );	// zu movende Contents

	pDoc->DeleteAreaTab( aToRange, IDF_ALL );
	pDoc->DeleteAreaTab( aFrmRange, IDF_ALL );
	// Formeln im Dokument anpassen
	pDoc->UpdateReference( URM_MOVE,
		aFrmRange.aStart.Col(), aFrmRange.aStart.Row(), aFrmRange.aStart.Tab(),
		aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row(), aFrmRange.aEnd.Tab(),
		(SCsCOL) aFrmRange.aStart.Col() - aToRange.aStart.Col(),
		(SCsROW) aFrmRange.aStart.Row() - aToRange.aStart.Row(),
		(SCsTAB) aFrmRange.aStart.Tab() - aToRange.aStart.Tab(), NULL );

	// LinkDependent freigeben, nachfolgendes UpdateReference-Undo setzt
	// ToRange->FromRange Dependents
	RemoveAllDependent();

	// setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries
	RejectRestoreContents( pTrack, 0, 0 );

	while ( pLinkDependent )
	{
		ScChangeAction* p = pLinkDependent->GetAction();
		if ( p && p->GetType() == SC_CAT_CONTENT )
		{
			ScChangeActionContent* pContent = (ScChangeActionContent*) p;
			if ( !pContent->IsDeletedIn() &&
					pContent->GetBigRange().aStart.IsValid( pDoc ) )
				pContent->PutNewValueToDoc( pDoc, 0, 0 );
			// in LookUpContents generierte loeschen
			if ( pTrack->IsGenerated( pContent->GetActionNumber() ) &&
					!pContent->IsDeletedIn() )
			{
				pLinkDependent->UnLink();		//! sonst wird der mitgeloescht
				pTrack->DeleteGeneratedDelContent( pContent );
			}
		}
		delete pLinkDependent;
	}

	RemoveAllLinks();
	return sal_True;
}


// --- ScChangeActionContent -----------------------------------------------

const sal_uInt16 nMemPoolChangeActionContent = (0x8000 - 64) / sizeof(ScChangeActionContent);
IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionContent, nMemPoolChangeActionContent, nMemPoolChangeActionContent )

ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
            const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
            const ScBigRange& aBigRangeP, const String& aUserP,
            const DateTime& aDateTimeP, const String& sComment,
			ScBaseCell* pTempOldCell, ScDocument* pDoc, const String& sOldValue )
		:
        ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
		aOldValue(sOldValue),
		pOldCell(pTempOldCell),
		pNewCell(NULL),
		pNextContent(NULL),
		pPrevContent(NULL),
		pNextInSlot(NULL),
		ppPrevInSlot(NULL)

{
	if (pOldCell)
		ScChangeActionContent::SetCell( aOldValue, pOldCell, 0, pDoc );
    if ( sOldValue.Len() )     // #i40704# don't overwrite SetCell result with empty string
        aOldValue = sOldValue; // set again, because SetCell removes it
}

ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
            ScBaseCell* pTempNewCell, const ScBigRange& aBigRangeP,
			ScDocument* pDoc, const String& sNewValue )
		:
        ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber),
        aNewValue(sNewValue),
		pOldCell(NULL),
		pNewCell(pTempNewCell),
		pNextContent(NULL),
		pPrevContent(NULL),
		pNextInSlot(NULL),
		ppPrevInSlot(NULL)
{
	if (pNewCell)
		ScChangeActionContent::SetCell( aNewValue, pNewCell, 0, pDoc );
    if ( sNewValue.Len() )     // #i40704# don't overwrite SetCell result with empty string
        aNewValue = sNewValue; // set again, because SetCell removes it
}

ScChangeActionContent::~ScChangeActionContent()
{
	ClearTrack();
}


void ScChangeActionContent::ClearTrack()
{
	RemoveFromSlot();
	if ( pPrevContent )
		pPrevContent->pNextContent = pNextContent;
	if ( pNextContent )
		pNextContent->pPrevContent = pPrevContent;
}


ScChangeActionContent* ScChangeActionContent::GetTopContent() const
{
	if ( pNextContent )
	{
		ScChangeActionContent* pContent = pNextContent;
		while ( pContent->pNextContent && pContent != pContent->pNextContent )
			pContent = pContent->pNextContent;
		return pContent;
	}
	return (ScChangeActionContent*) this;
}


ScChangeActionLinkEntry* ScChangeActionContent::GetDeletedIn() const
{
	if ( pNextContent )
		return GetTopContent()->pLinkDeletedIn;
	return pLinkDeletedIn;
}


ScChangeActionLinkEntry** ScChangeActionContent::GetDeletedInAddress()
{
	if ( pNextContent )
		return GetTopContent()->GetDeletedInAddress();
	return &pLinkDeletedIn;
}


void ScChangeActionContent::SetOldValue( const ScBaseCell* pCell,
		const ScDocument* pFromDoc, ScDocument* pToDoc, sal_uLong nFormat )
{
	ScChangeActionContent::SetValue( aOldValue, pOldCell,
		nFormat, pCell, pFromDoc, pToDoc );
}


void ScChangeActionContent::SetOldValue( const ScBaseCell* pCell,
		const ScDocument* pFromDoc, ScDocument* pToDoc )
{
	ScChangeActionContent::SetValue( aOldValue, pOldCell,
		aBigRange.aStart.MakeAddress(), pCell, pFromDoc, pToDoc );
}


void ScChangeActionContent::SetNewValue( const ScBaseCell* pCell,
		ScDocument* pDoc )
{
	ScChangeActionContent::SetValue( aNewValue, pNewCell,
		aBigRange.aStart.MakeAddress(), pCell, pDoc, pDoc );
}


void ScChangeActionContent::SetOldNewCells( ScBaseCell* pOldCellP,
						sal_uLong nOldFormat, ScBaseCell* pNewCellP,
						sal_uLong nNewFormat, ScDocument* pDoc )
{
	pOldCell = pOldCellP;
	pNewCell = pNewCellP;
	ScChangeActionContent::SetCell( aOldValue, pOldCell, nOldFormat, pDoc );
	ScChangeActionContent::SetCell( aNewValue, pNewCell, nNewFormat, pDoc );
}

void ScChangeActionContent::SetNewCell( ScBaseCell* pCell, ScDocument* pDoc, const String& rFormatted )
{
	DBG_ASSERT( !pNewCell, "ScChangeActionContent::SetNewCell: overwriting existing cell" );
	pNewCell = pCell;
	ScChangeActionContent::SetCell( aNewValue, pNewCell, 0, pDoc );

    // #i40704# allow to set formatted text here - don't call SetNewValue with String from XML filter
    if ( rFormatted.Len() )
        aNewValue = rFormatted;
}

void ScChangeActionContent::SetValueString( String& rValue, ScBaseCell*& pCell,
		const String& rStr, ScDocument* pDoc )
{
	if ( pCell )
	{
		pCell->Delete();
		pCell = NULL;
	}
	if ( rStr.Len() > 1 && rStr.GetChar(0) == '=' )
	{
		rValue.Erase();
		pCell = new ScFormulaCell(
            pDoc, aBigRange.aStart.MakeAddress(), rStr, formula::FormulaGrammar::GRAM_DEFAULT, formula::FormulaGrammar::CONV_OOO );
		((ScFormulaCell*)pCell)->SetInChangeTrack( sal_True );
	}
	else
		rValue = rStr;
}


void ScChangeActionContent::SetOldValue( const String& rOld, ScDocument* pDoc )
{
	SetValueString( aOldValue, pOldCell, rOld, pDoc );
}


void ScChangeActionContent::SetNewValue( const String& rNew, ScDocument* pDoc )
{
	SetValueString( aNewValue, pNewCell, rNew, pDoc );
}


void ScChangeActionContent::GetOldString( String& rStr ) const
{
	GetValueString( rStr, aOldValue, pOldCell );
}


void ScChangeActionContent::GetNewString( String& rStr ) const
{
	GetValueString( rStr, aNewValue, pNewCell );
}


void ScChangeActionContent::GetDescription( String& rStr, ScDocument* pDoc,
		sal_Bool bSplitRange, bool bWarning ) const
{
    ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning );

	String aRsc( ScGlobal::GetRscString( STR_CHANGED_CELL ) );

	String aTmpStr;
	GetRefString( aTmpStr, pDoc );

	xub_StrLen nPos = 0;
	nPos = aRsc.SearchAscii( "#1", nPos );
	aRsc.Erase( nPos, 2 );
	aRsc.Insert( aTmpStr, nPos );
    nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() );

	GetOldString( aTmpStr );
	if ( !aTmpStr.Len() )
		aTmpStr = ScGlobal::GetRscString( STR_CHANGED_BLANK );
	nPos = aRsc.SearchAscii( "#2", nPos );
	aRsc.Erase( nPos, 2 );
	aRsc.Insert( aTmpStr, nPos );
	nPos = sal::static_int_cast<xub_StrLen>( nPos + aTmpStr.Len() );

	GetNewString( aTmpStr );
	if ( !aTmpStr.Len() )
		aTmpStr = ScGlobal::GetRscString( STR_CHANGED_BLANK );
	nPos = aRsc.SearchAscii( "#3", nPos );
	aRsc.Erase( nPos, 2 );
	aRsc.Insert( aTmpStr, nPos );

	rStr += aRsc;
}


void ScChangeActionContent::GetRefString( String& rStr, ScDocument* pDoc,
		sal_Bool bFlag3D ) const
{
	sal_uInt16 nFlags = ( GetBigRange().IsValid( pDoc ) ? SCA_VALID : 0 );
	if ( nFlags )
	{
		const ScBaseCell* pCell = GetNewCell();
		if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATORG )
		{
            ScBigRange aLocalBigRange( GetBigRange() );
            SCCOL nC;
            SCROW nR;
            ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR );
            aLocalBigRange.aEnd.IncCol( nC-1 );
            aLocalBigRange.aEnd.IncRow( nR-1 );
            rStr = ScChangeAction::GetRefString( aLocalBigRange, pDoc, bFlag3D );

			return ;
		}

		ScAddress aTmpAddress( GetBigRange().aStart.MakeAddress() );
		if ( bFlag3D )
			nFlags |= SCA_TAB_3D;
		aTmpAddress.Format( rStr, nFlags, pDoc, pDoc->GetAddressConvention() );
		if ( IsDeletedIn() )
		{
			rStr.Insert( '(', 0 );
			rStr += ')';
		}
	}
	else
		rStr = ScGlobal::GetRscString( STR_NOREF_STR );
}


sal_Bool ScChangeActionContent::Reject( ScDocument* pDoc )
{
	if ( !aBigRange.IsValid( pDoc ) )
		return sal_False;

	PutOldValueToDoc( pDoc, 0, 0 );

	SetState( SC_CAS_REJECTED );
	RemoveAllLinks();

	return sal_True;
}


sal_Bool ScChangeActionContent::Select( ScDocument* pDoc, ScChangeTrack* pTrack,
		sal_Bool bOldest, Stack* pRejectActions )
{
	if ( !aBigRange.IsValid( pDoc ) )
		return sal_False;

	ScChangeActionContent* pContent = this;
	// accept previous contents
    while ( ( pContent = pContent->pPrevContent ) != NULL )
	{
		if ( pContent->IsVirgin() )
			pContent->SetState( SC_CAS_ACCEPTED );
	}
	ScChangeActionContent* pEnd = pContent = this;
	// reject subsequent contents
    while ( ( pContent = pContent->pNextContent ) != NULL )
	{
		// MatrixOrigin may have dependents, no dependency recursion needed
		const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
		while ( pL )
		{
			ScChangeAction* p = (ScChangeAction*) pL->GetAction();
			if ( p )
				p->SetRejected();
			pL = pL->GetNext();
		}
		pContent->SetRejected();
		pEnd = pContent;
	}

	if ( bOldest || pEnd != this )
	{	// wenn nicht aeltester: ist es ueberhaupt ein anderer als der letzte?
		ScRange aRange( aBigRange.aStart.MakeAddress() );
		const ScAddress& rPos = aRange.aStart;

		ScChangeActionContent* pNew = new ScChangeActionContent( aRange );
		pNew->SetOldValue( pDoc->GetCell( rPos ), pDoc, pDoc );

		if ( bOldest )
			PutOldValueToDoc( pDoc, 0, 0 );
		else
			PutNewValueToDoc( pDoc, 0, 0 );

		pNew->SetRejectAction( bOldest ? GetActionNumber() : pEnd->GetActionNumber() );
		pNew->SetState( SC_CAS_ACCEPTED );
		if ( pRejectActions )
			pRejectActions->Push( pNew );
		else
		{
			pNew->SetNewValue( pDoc->GetCell( rPos ), pDoc );
			pTrack->Append( pNew );
		}
	}

	if ( bOldest )
		SetRejected();
	else
		SetState( SC_CAS_ACCEPTED );

	return sal_True;
}


// static
void ScChangeActionContent::GetStringOfCell( String& rStr,
		const ScBaseCell* pCell, const ScDocument* pDoc, const ScAddress& rPos )
{
	if ( pCell )
	{
		if ( ScChangeActionContent::NeedsNumberFormat( pCell ) )
			GetStringOfCell( rStr, pCell, pDoc, pDoc->GetNumberFormat( rPos ) );
		else
			GetStringOfCell( rStr, pCell, pDoc, 0 );
	}
	else
		rStr.Erase();
}


// static
void ScChangeActionContent::GetStringOfCell( String& rStr,
		const ScBaseCell* pCell, const ScDocument* pDoc, sal_uLong nFormat )
{
	if ( ScChangeActionContent::GetContentCellType( pCell ) )
	{
		switch ( pCell->GetCellType() )
		{
			case CELLTYPE_VALUE :
			{
				double nValue = ((ScValueCell*)pCell)->GetValue();
				pDoc->GetFormatTable()->GetInputLineString( nValue,	nFormat,
					rStr );
			}
			break;
			case CELLTYPE_STRING :
				((ScStringCell*)pCell)->GetString( rStr );
			break;
			case CELLTYPE_EDIT :
				((ScEditCell*)pCell)->GetString( rStr );
			break;
			case CELLTYPE_FORMULA :
				((ScFormulaCell*)pCell)->GetFormula( rStr );
			break;
			default:
				rStr.Erase();
		}
	}
	else
		rStr.Erase();
}


// static
ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScBaseCell* pCell )
{
	if ( pCell )
	{
		switch ( pCell->GetCellType() )
		{
			case CELLTYPE_VALUE :
			case CELLTYPE_STRING :
			case CELLTYPE_EDIT :
				return SC_CACCT_NORMAL;
            //break;
			case CELLTYPE_FORMULA :
				switch ( ((const ScFormulaCell*)pCell)->GetMatrixFlag() )
				{
					case MM_NONE :
						return SC_CACCT_NORMAL;
                    //break;
					case MM_FORMULA :
					case MM_FAKE :
						return SC_CACCT_MATORG;
                    //break;
					case MM_REFERENCE :
						return SC_CACCT_MATREF;
                    //break;
				}
				return SC_CACCT_NORMAL;
            //break;
			default:
				return SC_CACCT_NONE;
		}
	}
	return SC_CACCT_NONE;
}


// static
sal_Bool ScChangeActionContent::NeedsNumberFormat( const ScBaseCell* pCell )
{
	return pCell && pCell->GetCellType() == CELLTYPE_VALUE;
}


// static
void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell,
		const ScAddress& rPos, const ScBaseCell* pOrgCell,
		const ScDocument* pFromDoc, ScDocument* pToDoc )
{
	sal_uLong nFormat = NeedsNumberFormat( pOrgCell ) ? pFromDoc->GetNumberFormat( rPos ) : 0;
    SetValue( rStr, pCell, nFormat, pOrgCell, pFromDoc, pToDoc );
}


// static
void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell,
		sal_uLong nFormat, const ScBaseCell* pOrgCell,
		const ScDocument* pFromDoc, ScDocument* pToDoc )
{
	rStr.Erase();
	if ( pCell )
		pCell->Delete();
	if ( ScChangeActionContent::GetContentCellType( pOrgCell ) )
	{
        pCell = pOrgCell->CloneWithoutNote( *pToDoc );
		switch ( pOrgCell->GetCellType() )
		{
			case CELLTYPE_VALUE :
			{	// z.B. Datum auch als solches merken
				double nValue = ((ScValueCell*)pOrgCell)->GetValue();
				pFromDoc->GetFormatTable()->GetInputLineString( nValue,
					nFormat, rStr );
			}
			break;
			case CELLTYPE_FORMULA :
				((ScFormulaCell*)pCell)->SetInChangeTrack( sal_True );
			break;
            default:
            {
                // added to avoid warnings
            }
		}
	}
	else
		pCell = NULL;
}


// static
void ScChangeActionContent::SetCell( String& rStr, ScBaseCell* pCell,
		sal_uLong nFormat, const ScDocument* pDoc )
{
	rStr.Erase();
	if ( pCell )
	{
		switch ( pCell->GetCellType() )
		{
			case CELLTYPE_VALUE :
			{	// e.g. remember date as date string
				double nValue = ((ScValueCell*)pCell)->GetValue();
				pDoc->GetFormatTable()->GetInputLineString( nValue,
					nFormat, rStr );
			}
			break;
			case CELLTYPE_FORMULA :
				((ScFormulaCell*)pCell)->SetInChangeTrack( sal_True );
			break;
            default:
            {
                // added to avoid warnings
            }
		}
	}
}


void ScChangeActionContent::GetValueString( String& rStr,
		const String& rValue, const ScBaseCell* pCell ) const
{
	if ( !rValue.Len() )
	{
		if ( pCell )
		{
			switch ( pCell->GetCellType() )
			{
				case CELLTYPE_STRING :
					((ScStringCell*)pCell)->GetString( rStr );
				break;
				case CELLTYPE_EDIT :
					((ScEditCell*)pCell)->GetString( rStr );
				break;
				case CELLTYPE_VALUE :	// ist immer in rValue
					rStr = rValue;
				break;
				case CELLTYPE_FORMULA :
					GetFormulaString( rStr, (ScFormulaCell*) pCell );
				break;
                default:
                {
                    // added to avoid warnings
                }
			}
		}
		else
			rStr.Erase();
	}
	else
		rStr = rValue;
}


void ScChangeActionContent::GetFormulaString( String& rStr,
		const ScFormulaCell* pCell ) const
{
	ScAddress aPos( aBigRange.aStart.MakeAddress() );
	if ( aPos == pCell->aPos || IsDeletedIn() )
		pCell->GetFormula( rStr );
	else
	{
		DBG_ERROR( "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos" );
        ScFormulaCell* pNew = new ScFormulaCell( *pCell, *pCell->GetDocument(), aPos );
		pNew->GetFormula( rStr );
		delete pNew;
	}
}


void ScChangeActionContent::PutOldValueToDoc( ScDocument* pDoc,
		SCsCOL nDx, SCsROW nDy ) const
{
	PutValueToDoc( pOldCell, aOldValue, pDoc, nDx, nDy );
}


void ScChangeActionContent::PutNewValueToDoc( ScDocument* pDoc,
		SCsCOL nDx, SCsROW nDy ) const
{
	PutValueToDoc( pNewCell, aNewValue, pDoc, nDx, nDy );
}


void ScChangeActionContent::PutValueToDoc( ScBaseCell* pCell,
		const String& rValue, ScDocument* pDoc, SCsCOL nDx, SCsROW nDy ) const
{
	ScAddress aPos( aBigRange.aStart.MakeAddress() );
	if ( nDx )
		aPos.IncCol( nDx );
	if ( nDy )
		aPos.IncRow( nDy );
	if ( !rValue.Len() )
	{
		if ( pCell )
		{
			switch ( pCell->GetCellType() )
			{
				case CELLTYPE_VALUE :	// ist immer in rValue
					pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue );
				break;
				default:
					switch ( ScChangeActionContent::GetContentCellType( pCell ) )
					{
						case SC_CACCT_MATORG :
						{
                            SCCOL nC;
                            SCROW nR;
                            ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR );
							DBG_ASSERT( nC>0 && nR>0, "ScChangeActionContent::PutValueToDoc: MatColsRows?" );
							ScRange aRange( aPos );
							if ( nC > 1 )
								aRange.aEnd.IncCol( nC-1 );
							if ( nR > 1 )
								aRange.aEnd.IncRow( nR-1 );
							ScMarkData aDestMark;
							aDestMark.SelectOneTable( aPos.Tab() );
							aDestMark.SetMarkArea( aRange );
							pDoc->InsertMatrixFormula( aPos.Col(), aPos.Row(),
								aRange.aEnd.Col(), aRange.aEnd.Row(),
								aDestMark, EMPTY_STRING,
								((const ScFormulaCell*)pCell)->GetCode() );
						}
						break;
						case SC_CACCT_MATREF :
							// nothing
						break;
						default:
                            pDoc->PutCell( aPos, pCell->CloneWithoutNote( *pDoc ) );
					}
			}
		}
		else
			pDoc->PutCell( aPos, NULL );
	}
	else
		pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue );
}


void lcl_InvalidateReference( ScToken& rTok, const ScBigAddress& rPos )
{
	ScSingleRefData& rRef1 = rTok.GetSingleRef();
	if ( rPos.Col() < 0 || MAXCOL < rPos.Col() )
	{
		rRef1.nCol = SCCOL_MAX;
		rRef1.nRelCol = SCCOL_MAX;
		rRef1.SetColDeleted( sal_True );
	}
	if ( rPos.Row() < 0 || MAXROW < rPos.Row() )
	{
		rRef1.nRow = SCROW_MAX;
		rRef1.nRelRow = SCROW_MAX;
		rRef1.SetRowDeleted( sal_True );
	}
	if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
	{
		rRef1.nTab = SCTAB_MAX;
		rRef1.nRelTab = SCTAB_MAX;
		rRef1.SetTabDeleted( sal_True );
	}
	if ( rTok.GetType() == formula::svDoubleRef )
	{
		ScSingleRefData& rRef2 = rTok.GetDoubleRef().Ref2;
		if ( rPos.Col() < 0 || MAXCOL < rPos.Col() )
		{
			rRef2.nCol = SCCOL_MAX;
			rRef2.nRelCol = SCCOL_MAX;
			rRef2.SetColDeleted( sal_True );
		}
		if ( rPos.Row() < 0 || MAXROW < rPos.Row() )
		{
			rRef2.nRow = SCROW_MAX;
			rRef2.nRelRow = SCROW_MAX;
			rRef2.SetRowDeleted( sal_True );
		}
		if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
		{
			rRef2.nTab = SCTAB_MAX;
			rRef2.nRelTab = SCTAB_MAX;
			rRef2.SetTabDeleted( sal_True );
		}
	}
}


void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack,
		UpdateRefMode eMode, const ScBigRange& rRange,
		sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
{
	SCSIZE nOldSlot = ScChangeTrack::ComputeContentSlot( aBigRange.aStart.Row() );
	ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aBigRange );
	SCSIZE nNewSlot = ScChangeTrack::ComputeContentSlot( aBigRange.aStart.Row() );
	if ( nNewSlot != nOldSlot )
	{
		RemoveFromSlot();
		InsertInSlot( &(pTrack->GetContentSlots()[nNewSlot]) );
	}

	if ( pTrack->IsInDelete() && !pTrack->IsInDeleteTop() )
		return ;		// Formeln nur kompletten Bereich updaten

	sal_Bool bOldFormula = ( pOldCell && pOldCell->GetCellType() == CELLTYPE_FORMULA );
	sal_Bool bNewFormula = ( pNewCell && pNewCell->GetCellType() == CELLTYPE_FORMULA );
	if ( bOldFormula || bNewFormula )
	{	// via ScFormulaCell UpdateReference anpassen (dort)
		if ( pTrack->IsInDelete() )
		{
			const ScRange& rDelRange = pTrack->GetInDeleteRange();
			if ( nDx > 0 )
				nDx = rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1;
			else if ( nDx < 0 )
				nDx = -(rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1);
			if ( nDy > 0 )
				nDy = rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1;
			else if ( nDy < 0 )
				nDy = -(rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1);
			if ( nDz > 0 )
				nDz = rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1;
			else if ( nDz < 0 )
				nDz = -(rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1);
		}
		ScBigRange aTmpRange( rRange );
		switch ( eMode )
		{
			case URM_INSDEL :
				if ( nDx < 0 || nDy < 0 || nDz < 0 )
				{	// Delete startet dort hinter geloeschtem Bereich,
					// Position wird dort angepasst.
					if ( nDx )
						aTmpRange.aStart.IncCol( -nDx );
					if ( nDy )
						aTmpRange.aStart.IncRow( -nDy );
					if ( nDz )
						aTmpRange.aStart.IncTab( -nDz );
				}
			break;
			case URM_MOVE :
				// Move ist hier Quelle, dort Ziel,
				// Position muss vorher angepasst sein.
				if ( bOldFormula )
					((ScFormulaCell*)pOldCell)->aPos = aBigRange.aStart.MakeAddress();
				if ( bNewFormula )
					((ScFormulaCell*)pNewCell)->aPos = aBigRange.aStart.MakeAddress();
				if ( nDx )
				{
					aTmpRange.aStart.IncCol( nDx );
					aTmpRange.aEnd.IncCol( nDx );
				}
				if ( nDy )
				{
					aTmpRange.aStart.IncRow( nDy );
					aTmpRange.aEnd.IncRow( nDy );
				}
				if ( nDz )
				{
					aTmpRange.aStart.IncTab( nDz );
					aTmpRange.aEnd.IncTab( nDz );
				}
			break;
            default:
            {
                // added to avoid warnings
            }
		}
		ScRange aRange( aTmpRange.MakeRange() );
		if ( bOldFormula )
			((ScFormulaCell*)pOldCell)->UpdateReference( eMode, aRange,
				(SCsCOL) nDx, (SCsROW) nDy, (SCsTAB) nDz, NULL );
		if ( bNewFormula )
			((ScFormulaCell*)pNewCell)->UpdateReference( eMode, aRange,
				(SCsCOL) nDx, (SCsROW) nDy, (SCsTAB) nDz, NULL );
		if ( !aBigRange.aStart.IsValid( pTrack->GetDocument() ) )
		{	//! HACK!
			//! UpdateReference kann nicht mit Positionen ausserhalb des
			//! Dokuments umgehen, deswegen alles auf #REF! setzen
//2do: make it possible! das bedeutet grossen Umbau von ScAddress etc.!
			const ScBigAddress& rPos = aBigRange.aStart;
			if ( bOldFormula )
			{
				ScToken* t;
				ScTokenArray* pArr = ((ScFormulaCell*)pOldCell)->GetCode();
				pArr->Reset();
                while ( ( t = static_cast<ScToken*>(pArr->GetNextReference()) ) != NULL )
					lcl_InvalidateReference( *t, rPos );
				pArr->Reset();
                while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
					lcl_InvalidateReference( *t, rPos );
			}
			if ( bNewFormula )
			{
				ScToken* t;
				ScTokenArray* pArr = ((ScFormulaCell*)pNewCell)->GetCode();
				pArr->Reset();
                while ( ( t = static_cast<ScToken*>(pArr->GetNextReference()) ) != NULL )
					lcl_InvalidateReference( *t, rPos );
				pArr->Reset();
                while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL )
					lcl_InvalidateReference( *t, rPos );
			}
		}
	}
}


// --- ScChangeActionReject ------------------------------------------------

ScChangeActionReject::ScChangeActionReject(const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
                                                const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment)
		:
        ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment)
{
}


// --- ScChangeTrack -------------------------------------------------------

IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeTrackMsgInfo, 16, 16 )

const SCROW ScChangeTrack::nContentRowsPerSlot = InitContentRowsPerSlot();
const SCSIZE ScChangeTrack::nContentSlots =
	(MAXROWCOUNT) / InitContentRowsPerSlot() + 2;

// static
SCROW ScChangeTrack::InitContentRowsPerSlot()
{
	const SCSIZE nMaxSlots = 0xffe0 / sizeof( ScChangeActionContent* ) - 2;
	SCROW nRowsPerSlot = (MAXROWCOUNT) / nMaxSlots;
    if ( nRowsPerSlot * nMaxSlots < sal::static_int_cast<SCSIZE>(MAXROWCOUNT) )
		++nRowsPerSlot;
	return nRowsPerSlot;
}


ScChangeTrack::ScChangeTrack( ScDocument* pDocP ) :
		pDoc( pDocP )
{
	Init();
    SC_MOD()->GetUserOptions().AddListener(this);

	ppContentSlots = new ScChangeActionContent* [ nContentSlots ];
	memset( ppContentSlots, 0, nContentSlots * sizeof( ScChangeActionContent* ) );
}

ScChangeTrack::ScChangeTrack( ScDocument* pDocP, const ScStrCollection& aTempUserCollection) :
		aUserCollection(aTempUserCollection),
		pDoc( pDocP )
{
	Init();
    SC_MOD()->GetUserOptions().AddListener(this);
    ppContentSlots = new ScChangeActionContent* [ nContentSlots ];
	memset( ppContentSlots, 0, nContentSlots * sizeof( ScChangeActionContent* ) );
}

ScChangeTrack::~ScChangeTrack()
{
    SC_MOD()->GetUserOptions().RemoveListener(this);
	DtorClear();
	delete [] ppContentSlots;
}


void ScChangeTrack::Init()
{
	pFirst = NULL;
	pLast = NULL;
	pFirstGeneratedDelContent = NULL;
	pLastCutMove = NULL;
	pLinkInsertCol = NULL;
	pLinkInsertRow = NULL;
	pLinkInsertTab = NULL;
	pLinkMove = NULL;
	pBlockModifyMsg = NULL;
	nActionMax = 0;
	nGeneratedMin = SC_CHGTRACK_GENERATED_START;
	nMarkLastSaved = 0;
	nStartLastCut = 0;
	nEndLastCut = 0;
	nLastMerge = 0;
	eMergeState = SC_CTMS_NONE;
	nLoadedFileFormatVersion = SC_CHGTRACK_FILEFORMAT;
	bLoadSave = sal_False;
	bInDelete = sal_False;
	bInDeleteTop = sal_False;
	bInDeleteUndo = sal_False;
	bInPasteCut = sal_False;
	bUseFixDateTime = sal_False;
    bTime100thSeconds = sal_True;

    const SvtUserOptions& rUserOpt = SC_MOD()->GetUserOptions();
    aUser = rUserOpt.GetFirstName();
	aUser += ' ';
    aUser += (String)rUserOpt.GetLastName();
	aUserCollection.Insert( new StrData( aUser ) );
}


void ScChangeTrack::DtorClear()
{
	ScChangeAction* p;
	ScChangeAction* pNext;
	for ( p = GetFirst(); p; p = pNext )
	{
		pNext = p->GetNext();
		delete p;
	}
	for ( p = pFirstGeneratedDelContent; p; p = pNext )
	{
		pNext = p->GetNext();
		delete p;
	}
	for ( p = aPasteCutTable.First(); p; p = aPasteCutTable.Next() )
	{
		delete p;
	}
	delete pLastCutMove;
	ClearMsgQueue();
}


void ScChangeTrack::ClearMsgQueue()
{
	if ( pBlockModifyMsg )
	{
		delete pBlockModifyMsg;
		pBlockModifyMsg = NULL;
	}
	ScChangeTrackMsgInfo* pMsgInfo;
    while ( ( pMsgInfo = aMsgStackTmp.Pop() ) != NULL )
		delete pMsgInfo;
    while ( ( pMsgInfo = aMsgStackFinal.Pop() ) != NULL )
		delete pMsgInfo;
    while ( ( pMsgInfo = aMsgQueue.Get() ) != NULL )
		delete pMsgInfo;
}


void ScChangeTrack::Clear()
{
	DtorClear();
	aTable.Clear();
	aGeneratedTable.Clear();
	aPasteCutTable.Clear();
	aUserCollection.FreeAll();
	aUser.Erase();
	Init();
}


void __EXPORT ScChangeTrack::ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 )
{
    if ( !pDoc->IsInDtorClear() )
	{
        const SvtUserOptions& rUserOptions = SC_MOD()->GetUserOptions();
        sal_uInt16 nOldCount = aUserCollection.GetCount();

        String aStr( rUserOptions.GetFirstName() );
        aStr += ' ';
        aStr += (String)rUserOptions.GetLastName();
        SetUser( aStr );

        if ( aUserCollection.GetCount() != nOldCount )
        {
            //  New user in collection -> have to repaint because
            //  colors may be different now (#106697#).
            //  (Has to be done in the Notify handler, to be sure
            //  the user collection has already been updated)

            SfxObjectShell* pDocSh = pDoc->GetDocumentShell();
            if (pDocSh)
                pDocSh->Broadcast( ScPaintHint( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB), PAINT_GRID ) );
        }
	}
}


void ScChangeTrack::SetUser( const String& rUser )
{
	if ( IsLoadSave() )
		return ;		// nicht die Collection zerschiessen

	aUser = rUser;
	StrData* pStrData = new StrData( aUser );
	if ( !aUserCollection.Insert( pStrData ) )
		delete pStrData;
}


void ScChangeTrack::StartBlockModify( ScChangeTrackMsgType eMsgType,
		sal_uLong nStartAction )
{
	if ( aModifiedLink.IsSet() )
	{
		if ( pBlockModifyMsg )
			aMsgStackTmp.Push( pBlockModifyMsg );	// Block im Block
		pBlockModifyMsg = new ScChangeTrackMsgInfo;
		pBlockModifyMsg->eMsgType = eMsgType;
		pBlockModifyMsg->nStartAction = nStartAction;
	}
}


void ScChangeTrack::EndBlockModify( sal_uLong nEndAction )
{
	if ( aModifiedLink.IsSet() )
	{
		if ( pBlockModifyMsg )
		{
			if ( pBlockModifyMsg->nStartAction <= nEndAction )
			{
				pBlockModifyMsg->nEndAction = nEndAction;
				// Blocks in Blocks aufgeloest
				aMsgStackFinal.Push( pBlockModifyMsg );
			}
			else
				delete pBlockModifyMsg;
			pBlockModifyMsg = aMsgStackTmp.Pop();	// evtl. Block im Block
		}
		if ( !pBlockModifyMsg )
		{
			sal_Bool bNew = sal_False;
			ScChangeTrackMsgInfo* pMsg;
            while ( ( pMsg = aMsgStackFinal.Pop() ) != NULL )
			{
				aMsgQueue.Put( pMsg );
				bNew = sal_True;
			}
			if ( bNew )
				aModifiedLink.Call( this );
		}
	}
}


void ScChangeTrack::NotifyModified( ScChangeTrackMsgType eMsgType,
		sal_uLong nStartAction, sal_uLong nEndAction )
{
	if ( aModifiedLink.IsSet() )
	{
		if ( !pBlockModifyMsg || pBlockModifyMsg->eMsgType != eMsgType ||
				(IsGenerated( nStartAction ) &&
				(eMsgType == SC_CTM_APPEND || eMsgType == SC_CTM_REMOVE)) )
		{	// Append innerhalb von Append z.B. nicht
			StartBlockModify( eMsgType, nStartAction );
			EndBlockModify( nEndAction );
		}
	}
}


void ScChangeTrack::MasterLinks( ScChangeAction* pAppend )
{
	ScChangeActionType eType = pAppend->GetType();

	if ( eType == SC_CAT_CONTENT )
	{
		if ( !IsGenerated( pAppend->GetActionNumber() ) )
		{
			SCSIZE nSlot = ComputeContentSlot(
				pAppend->GetBigRange().aStart.Row() );
			((ScChangeActionContent*)pAppend)->InsertInSlot(
				&ppContentSlots[nSlot] );
		}
		return ;
	}

	if ( pAppend->IsRejecting() )
		return ;		// Rejects haben keine Abhaengigkeiten

	switch ( eType )
	{
		case SC_CAT_INSERT_COLS :
		{
			ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
				&pLinkInsertCol, pAppend );
			pAppend->AddLink( NULL, pLink );
		}
		break;
		case SC_CAT_INSERT_ROWS :
		{
			ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
				&pLinkInsertRow, pAppend );
			pAppend->AddLink( NULL, pLink );
		}
		break;
		case SC_CAT_INSERT_TABS :
		{
			ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
				&pLinkInsertTab, pAppend );
			pAppend->AddLink( NULL, pLink );
		}
		break;
		case SC_CAT_MOVE :
		{
			ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry(
				&pLinkMove, pAppend );
			pAppend->AddLink( NULL, pLink );
		}
		break;
        default:
        {
            // added to avoid warnings
        }
	}
}


void ScChangeTrack::AppendLoaded( ScChangeAction* pAppend )
{
	aTable.Insert( pAppend->GetActionNumber(), pAppend );
	if ( !pLast )
		pFirst = pLast = pAppend;
	else
	{
		pLast->pNext = pAppend;
		pAppend->pPrev = pLast;
		pLast = pAppend;
	}
	MasterLinks( pAppend );
}


void ScChangeTrack::Append( ScChangeAction* pAppend, sal_uLong nAction )
{
	if ( nActionMax < nAction )
		nActionMax = nAction;
	pAppend->SetUser( aUser );
	if ( bUseFixDateTime )
		pAppend->SetDateTimeUTC( aFixDateTime );
	pAppend->SetActionNumber( nAction );
	aTable.Insert( nAction, pAppend );
	// UpdateReference Inserts vor Dependencies.
	// Delete rejectendes Insert hatte UpdateReference mit Delete-Undo.
	// UpdateReference auch wenn pLast==NULL, weil pAppend ein Delete sein
	// kann, dass DelContents generiert haben kann
	if ( pAppend->IsInsertType() && !pAppend->IsRejecting() )
		UpdateReference( pAppend, sal_False );
	if ( !pLast )
		pFirst = pLast = pAppend;
	else
	{
		pLast->pNext = pAppend;
		pAppend->pPrev = pLast;
		pLast = pAppend;
		Dependencies( pAppend );
	}
	// UpdateReference Inserts nicht nach Dependencies.
	// Move rejectendes Move hatte UpdateReference mit Move-Undo, Inhalt in
	// ToRange nicht deleten.
	if ( !pAppend->IsInsertType() &&
			!(pAppend->GetType() == SC_CAT_MOVE && pAppend->IsRejecting()) )
		UpdateReference( pAppend, sal_False );
	MasterLinks( pAppend );

	if ( aModifiedLink.IsSet() )
	{
		NotifyModified( SC_CTM_APPEND, nAction, nAction );
		if ( pAppend->GetType() == SC_CAT_CONTENT )
		{
			ScChangeActionContent* pContent = (ScChangeActionContent*) pAppend;
            if ( ( pContent = pContent->GetPrevContent() ) != NULL )
			{
				sal_uLong nMod = pContent->GetActionNumber();
				NotifyModified( SC_CTM_CHANGE, nMod, nMod );
			}
		}
		else
			NotifyModified( SC_CTM_CHANGE, pFirst->GetActionNumber(),
				pLast->GetActionNumber() );
	}
}


void ScChangeTrack::Append( ScChangeAction* pAppend )
{
	Append( pAppend, ++nActionMax );
}


void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
		ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction, SCsTAB nDz )
{
	nStartAction = GetActionMax() + 1;
	AppendDeleteRange( rRange, pRefDoc, nDz, 0 );
	nEndAction = GetActionMax();
}


void ScChangeTrack::AppendDeleteRange( const ScRange& rRange,
		ScDocument* pRefDoc, SCsTAB nDz, sal_uLong nRejectingInsert )
{
	SetInDeleteRange( rRange );
	StartBlockModify( SC_CTM_APPEND, GetActionMax() + 1 );
	SCCOL nCol1;
	SCROW nRow1;
	SCTAB nTab1;
	SCCOL nCol2;
	SCROW nRow2;
	SCTAB nTab2;
	rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
	for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
	{
		if ( !pRefDoc || nTab < pRefDoc->GetTableCount() )
		{
			if ( nCol1 == 0 && nCol2 == MAXCOL )
			{	// ganze Zeilen und/oder Tabellen
				if ( nRow1 == 0 && nRow2 == MAXROW )
				{	// ganze Tabellen
//2do: geht nicht auch komplette Tabelle als ganzes?
					ScRange aRange( 0, 0, nTab, 0, MAXROW, nTab );
					for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
					{	// spaltenweise ist weniger als zeilenweise
						aRange.aStart.SetCol( nCol );
						aRange.aEnd.SetCol( nCol );
						if ( nCol == nCol2 )
							SetInDeleteTop( sal_True );
						AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
							nTab-nTab1 + nDz, nRejectingInsert );
					}
					//! immer noch InDeleteTop
					AppendOneDeleteRange( rRange, pRefDoc, 0, 0,
						nTab-nTab1 + nDz, nRejectingInsert );
				}
				else
				{	// ganze Zeilen
					ScRange aRange( 0, 0, nTab, MAXCOL, 0, nTab );
					for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
					{
						aRange.aStart.SetRow( nRow );
						aRange.aEnd.SetRow( nRow );
						if ( nRow == nRow2 )
							SetInDeleteTop( sal_True );
						AppendOneDeleteRange( aRange, pRefDoc, 0, nRow-nRow1,
							0, nRejectingInsert );
					}
				}
			}
			else if ( nRow1 == 0 && nRow2 == MAXROW )
			{	// ganze Spalten
				ScRange aRange( 0, 0, nTab, 0, MAXROW, nTab );
				for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
				{
					aRange.aStart.SetCol( nCol );
					aRange.aEnd.SetCol( nCol );
					if ( nCol == nCol2 )
						SetInDeleteTop( sal_True );
					AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0,
						0, nRejectingInsert );
				}
			}
			else
			{
				DBG_ERROR( "ScChangeTrack::AppendDeleteRange: Block not supported!" );
			}
			SetInDeleteTop( sal_False );
		}
	}
	EndBlockModify( GetActionMax() );
}


void ScChangeTrack::AppendOneDeleteRange( const ScRange& rOrgRange,
		ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz,
		sal_uLong nRejectingInsert )
{
	ScRange aTrackRange( rOrgRange );
	if ( nDx )
	{
		aTrackRange.aStart.IncCol( -nDx );
		aTrackRange.aEnd.IncCol( -nDx );
	}
	if ( nDy )
	{
		aTrackRange.aStart.IncRow( -nDy );
		aTrackRange.aEnd.IncRow( -nDy );
	}
	if ( nDz )
	{
		aTrackRange.aStart.IncTab( -nDz );
		aTrackRange.aEnd.IncTab( -nDz );
	}
	ScChangeActionDel* pAct = new ScChangeActionDel( aTrackRange, nDx, nDy,
		this );
	// TabDelete keine Contents, sind in einzelnen Spalten
	if ( !(rOrgRange.aStart.Col() == 0 && rOrgRange.aStart.Row() == 0 &&
			rOrgRange.aEnd.Col() == MAXCOL && rOrgRange.aEnd.Row() == MAXROW) )
		LookUpContents( rOrgRange, pRefDoc, -nDx, -nDy, -nDz );
	if ( nRejectingInsert )
	{
		pAct->SetRejectAction( nRejectingInsert );
		pAct->SetState( SC_CAS_ACCEPTED );
	}
	Append( pAct );
}


void ScChangeTrack::LookUpContents( const ScRange& rOrgRange,
		ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz )
{
	if ( pRefDoc )
	{
		ScAddress aPos;
		ScBigAddress aBigPos;
		ScCellIterator aIter( pRefDoc, rOrgRange );
		ScBaseCell* pCell = aIter.GetFirst();
		while ( pCell )
		{
			if ( ScChangeActionContent::GetContentCellType( pCell ) )
			{
				aBigPos.Set( aIter.GetCol() + nDx, aIter.GetRow() + nDy,
					aIter.GetTab() + nDz );
				ScChangeActionContent* pContent = SearchContentAt( aBigPos, NULL );
				if ( !pContent )
				{	// nicht getrackte Contents
					aPos.Set( aIter.GetCol() + nDx, aIter.GetRow() + nDy,
						aIter.GetTab() + nDz );
					GenerateDelContent( aPos, pCell, pRefDoc );
					//! der Content wird hier _nicht_ per AddContent hinzugefuegt,
					//! sondern in UpdateReference, um z.B. auch kreuzende Deletes
					//! korrekt zu erfassen
				}
			}
			pCell = aIter.GetNext();
		}
	}
}


void ScChangeTrack::AppendMove( const ScRange& rFromRange,
		const ScRange& rToRange, ScDocument* pRefDoc )
{
	ScChangeActionMove* pAct = new ScChangeActionMove( rFromRange, rToRange, this );
	LookUpContents( rToRange, pRefDoc, 0, 0, 0 );	// ueberschriebene Contents
	Append( pAct );
}


// static
sal_Bool ScChangeTrack::IsMatrixFormulaRangeDifferent( const ScBaseCell* pOldCell,
		const ScBaseCell* pNewCell )
{
	SCCOL nC1, nC2;
	SCROW nR1, nR2;
	nC1 = nC2 = 0;
    nR1 = nR2 = 0;
	if ( pOldCell && (pOldCell->GetCellType() == CELLTYPE_FORMULA) &&
			((const ScFormulaCell*)pOldCell)->GetMatrixFlag() == MM_FORMULA )
		((const ScFormulaCell*)pOldCell)->GetMatColsRows( nC1, nR1 );
	if ( pNewCell && (pNewCell->GetCellType() == CELLTYPE_FORMULA) &&
			((const ScFormulaCell*)pNewCell)->GetMatrixFlag() == MM_FORMULA )
		((const ScFormulaCell*)pNewCell)->GetMatColsRows( nC1, nR1 );
	return nC1 != nC2 || nR1 != nR2;
}


void ScChangeTrack::AppendContent( const ScAddress& rPos,
		const String& rNewValue, ScBaseCell* pOldCell )
{
	String aOldValue;
	ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pDoc, rPos );
	if ( aOldValue != rNewValue ||
			IsMatrixFormulaRangeDifferent( pOldCell, NULL ) )
	{	// nur wirkliche Aenderung tracken
		ScRange aRange( rPos );
		ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
		pAct->SetOldValue( pOldCell, pDoc, pDoc );
		pAct->SetNewValue( rNewValue, pDoc );
		Append( pAct );
	}
}


void ScChangeTrack::AppendContent( const ScAddress& rPos,
		const ScBaseCell* pOldCell, sal_uLong nOldFormat, ScDocument* pRefDoc )
{
	if ( !pRefDoc )
		pRefDoc = pDoc;
	String aOldValue;
	ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pRefDoc, nOldFormat );
	String aNewValue;
	ScBaseCell* pNewCell = pDoc->GetCell( rPos );
	ScChangeActionContent::GetStringOfCell( aNewValue, pNewCell, pDoc, rPos );
	if ( aOldValue != aNewValue ||
			IsMatrixFormulaRangeDifferent( pOldCell, pNewCell ) )
	{	// nur wirkliche Aenderung tracken
		ScRange aRange( rPos );
		ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
		pAct->SetOldValue( pOldCell, pRefDoc, pDoc, nOldFormat );
		pAct->SetNewValue( pNewCell, pDoc );
		Append( pAct );
	}
}


void ScChangeTrack::AppendContent( const ScAddress& rPos,
		ScDocument* pRefDoc )
{
	String aOldValue;
	ScBaseCell* pOldCell = pRefDoc->GetCell( rPos );
	ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pRefDoc, rPos );
	String aNewValue;
	ScBaseCell* pNewCell = pDoc->GetCell( rPos );
	ScChangeActionContent::GetStringOfCell( aNewValue, pNewCell, pDoc, rPos );
	if ( aOldValue != aNewValue ||
			IsMatrixFormulaRangeDifferent( pOldCell, pNewCell ) )
	{	// nur wirkliche Aenderung tracken
		ScRange aRange( rPos );
		ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
		pAct->SetOldValue( pOldCell, pRefDoc, pDoc );
		pAct->SetNewValue( pNewCell, pDoc );
		Append( pAct );
	}
}


void ScChangeTrack::AppendContent( const ScAddress& rPos,
		const ScBaseCell* pOldCell )
{
	if ( ScChangeActionContent::NeedsNumberFormat( pOldCell ) )
		AppendContent( rPos, pOldCell, pDoc->GetNumberFormat( rPos ), pDoc );
	else
		AppendContent( rPos, pOldCell, 0, pDoc );
}


void ScChangeTrack::SetLastCutMoveRange( const ScRange& rRange,
		ScDocument* pRefDoc )
{
	if ( pLastCutMove )
	{
		// ToRange nicht mit Deletes linken und nicht in der Groesse aendern,
		// eigentlich unnoetig, da ein Delete vorher in
		// ScViewFunc::PasteFromClip ein ResetLastCut ausloest
		ScBigRange& r = pLastCutMove->GetBigRange();
		r.aEnd.SetCol( -1 );
		r.aEnd.SetRow( -1 );
		r.aEnd.SetTab( -1 );
		r.aStart.SetCol( -1 - (rRange.aEnd.Col() - rRange.aStart.Col()) );
		r.aStart.SetRow( -1 - (rRange.aEnd.Row() - rRange.aStart.Row()) );
		r.aStart.SetTab( -1 - (rRange.aEnd.Tab() - rRange.aStart.Tab()) );
		// zu ueberschreibende Contents im FromRange
		LookUpContents( rRange, pRefDoc, 0, 0, 0 );
	}
}


void ScChangeTrack::AppendContentRange( const ScRange& rRange,
		ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction,
		ScChangeActionClipMode eClipMode )
{
	if ( eClipMode == SC_CACM_CUT )
	{
		ResetLastCut();
		pLastCutMove = new ScChangeActionMove( rRange, rRange, this );
		SetLastCutMoveRange( rRange, pRefDoc );
	}
	SCCOL nCol1;
	SCROW nRow1;
	SCTAB nTab1;
	SCCOL nCol2;
	SCROW nRow2;
	SCTAB nTab2;
	rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
	sal_Bool bDoContents;
	if ( eClipMode == SC_CACM_PASTE && HasLastCut() )
	{
		bDoContents = sal_False;
		SetInPasteCut( sal_True );
		// Paste und Cut abstimmen, Paste kann groesserer Range sein
		ScRange aRange( rRange );
		ScBigRange& r = pLastCutMove->GetBigRange();
        SCCOL nTmpCol;
		if ( (nTmpCol = (SCCOL) (r.aEnd.Col() - r.aStart.Col())) != (nCol2 - nCol1) )
		{
			aRange.aEnd.SetCol( aRange.aStart.Col() + nTmpCol );
			nCol1 += nTmpCol + 1;
			bDoContents = sal_True;
		}
        SCROW nTmpRow;
		if ( (nTmpRow = (SCROW) (r.aEnd.Row() - r.aStart.Row())) != (nRow2 - nRow1) )
		{
			aRange.aEnd.SetRow( aRange.aStart.Row() + nTmpRow );
			nRow1 += nTmpRow + 1;
			bDoContents = sal_True;
		}
        SCTAB nTmpTab;
		if ( (nTmpTab = (SCTAB) (r.aEnd.Tab() - r.aStart.Tab())) != (nTab2 - nTab1) )
		{
			aRange.aEnd.SetTab( aRange.aStart.Tab() + nTmpTab );
			nTab1 += nTmpTab + 1;
			bDoContents = sal_True;
		}
		r = aRange;
		Undo( nStartLastCut, nEndLastCut );	// hier werden sich die Cuts gemerkt
		//! StartAction erst nach Undo
		nStartAction = GetActionMax() + 1;
		StartBlockModify( SC_CTM_APPEND, nStartAction );
		// zu ueberschreibende Contents im ToRange
		LookUpContents( aRange, pRefDoc, 0, 0, 0 );
		pLastCutMove->SetStartLastCut( nStartLastCut );
		pLastCutMove->SetEndLastCut( nEndLastCut );
		Append( pLastCutMove );
		pLastCutMove = NULL;
		ResetLastCut();
		SetInPasteCut( sal_False );
	}
	else
	{
		bDoContents = sal_True;
		nStartAction = GetActionMax() + 1;
		StartBlockModify( SC_CTM_APPEND, nStartAction );
	}
	if ( bDoContents )
	{
		ScAddress aPos;
		for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ )
		{
			aPos.SetTab( nTab );
			for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ )
			{
				aPos.SetCol( nCol );
				for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
				{
					aPos.SetRow( nRow );
					AppendContent( aPos, pRefDoc );
				}
			}
		}
	}
	nEndAction = GetActionMax();
	EndBlockModify( nEndAction );
	if ( eClipMode == SC_CACM_CUT )
	{
		nStartLastCut = nStartAction;
		nEndLastCut = nEndAction;
	}
}


void ScChangeTrack::AppendContentsIfInRefDoc( ScDocument* pRefDoc,
			sal_uLong& nStartAction, sal_uLong& nEndAction )
{
	ScDocumentIterator aIter( pRefDoc, 0, MAXTAB );
	if ( aIter.GetFirst() )
	{
		nStartAction = GetActionMax() + 1;
		StartBlockModify( SC_CTM_APPEND, nStartAction );
		SvNumberFormatter* pFormatter = pRefDoc->GetFormatTable();
		do
		{
            SCCOL nCol;
            SCROW nRow;
            SCTAB nTab;
			aIter.GetPos( nCol, nRow, nTab );
			ScAddress aPos( nCol, nRow, nTab );
			AppendContent( aPos, aIter.GetCell(),
				aIter.GetPattern()->GetNumberFormat( pFormatter ), pRefDoc );
		} while ( aIter.GetNext() );
		nEndAction = GetActionMax();
		EndBlockModify( nEndAction );
	}
	else
		nStartAction = nEndAction = 0;
}


ScChangeActionContent* ScChangeTrack::AppendContentOnTheFly(
		const ScAddress& rPos, ScBaseCell* pOldCell, ScBaseCell* pNewCell,
		sal_uLong nOldFormat, sal_uLong nNewFormat )
{
	ScRange aRange( rPos );
	ScChangeActionContent* pAct = new ScChangeActionContent( aRange );
	pAct->SetOldNewCells( pOldCell, nOldFormat, pNewCell, nNewFormat, pDoc );
	Append( pAct );
	return pAct;
}


void ScChangeTrack::AppendInsert( const ScRange& rRange )
{
	ScChangeActionIns* pAct = new ScChangeActionIns( rRange );
	Append( pAct );
}


void ScChangeTrack::DeleteCellEntries( ScChangeActionCellListEntry*& pCellList,
		ScChangeAction* pDeletor )
{
	ScChangeActionCellListEntry* pE = pCellList;
	while ( pE )
	{
		ScChangeActionCellListEntry* pNext = pE->pNext;
		pE->pContent->RemoveDeletedIn( pDeletor );
		if ( IsGenerated( pE->pContent->GetActionNumber() ) &&
				!pE->pContent->IsDeletedIn() )
			DeleteGeneratedDelContent( pE->pContent );
		delete pE;
		pE = pNext;
	}
	pCellList = NULL;
}


ScChangeActionContent* ScChangeTrack::GenerateDelContent(
		const ScAddress& rPos, const ScBaseCell* pCell,
		const ScDocument* pFromDoc )
{
	ScChangeActionContent* pContent = new ScChangeActionContent(
		ScRange( rPos ) );
	pContent->SetActionNumber( --nGeneratedMin );
	// nur NewValue
	ScChangeActionContent::SetValue( pContent->aNewValue, pContent->pNewCell,
		rPos, pCell, pFromDoc, pDoc );
	// pNextContent und pPrevContent werden nicht gesetzt
	if ( pFirstGeneratedDelContent )
	{	// vorne reinhaengen
		pFirstGeneratedDelContent->pPrev = pContent;
		pContent->pNext = pFirstGeneratedDelContent;
	}
	pFirstGeneratedDelContent = pContent;
	aGeneratedTable.Insert( nGeneratedMin, pContent );
	NotifyModified( SC_CTM_APPEND, nGeneratedMin, nGeneratedMin );
	return pContent;
}


void ScChangeTrack::DeleteGeneratedDelContent( ScChangeActionContent* pContent )
{
	sal_uLong nAct = pContent->GetActionNumber();
	aGeneratedTable.Remove( nAct );
	if ( pFirstGeneratedDelContent == pContent )
		pFirstGeneratedDelContent = (ScChangeActionContent*) pContent->pNext;
	if ( pContent->pNext )
		pContent->pNext->pPrev = pContent->pPrev;
	if ( pContent->pPrev )
		pContent->pPrev->pNext = pContent->pNext;
	delete pContent;
	NotifyModified( SC_CTM_REMOVE, nAct, nAct );
	if ( nAct == nGeneratedMin )
		++nGeneratedMin;		//! erst nach NotifyModified wg. IsGenerated
}


ScChangeActionContent* ScChangeTrack::SearchContentAt(
		const ScBigAddress& rPos, ScChangeAction* pButNotThis ) const
{
	SCSIZE nSlot = ComputeContentSlot( rPos.Row() );
	for ( ScChangeActionContent* p = ppContentSlots[nSlot]; p;
			p = p->GetNextInSlot() )
	{
		if ( p != pButNotThis && !p->IsDeletedIn() &&
				p->GetBigRange().aStart == rPos )
		{
			ScChangeActionContent* pContent = p->GetTopContent();
			if ( !pContent->IsDeletedIn() )
				return pContent;
		}
	}
	return NULL;
}


void ScChangeTrack::AddDependentWithNotify( ScChangeAction* pParent,
		ScChangeAction* pDependent )
{
	ScChangeActionLinkEntry* pLink = pParent->AddDependent( pDependent );
	pDependent->AddLink( pParent, pLink );
	if ( aModifiedLink.IsSet() )
	{
		sal_uLong nMod = pParent->GetActionNumber();
		NotifyModified( SC_CTM_PARENT, nMod, nMod );
	}
}


void ScChangeTrack::Dependencies( ScChangeAction* pAct )
{
	// Finde die letzte Abhaengigkeit fuer jeweils Col/Row/Tab.
	// Content an gleicher Position verketten.
	// Move Abhaengigkeiten.
	ScChangeActionType eActType = pAct->GetType();
	if ( eActType == SC_CAT_REJECT ||
			(eActType == SC_CAT_MOVE && pAct->IsRejecting()) )
		return ;		// diese Rejects sind nicht abhaengig

	if ( eActType == SC_CAT_CONTENT )
	{
		if ( !(((ScChangeActionContent*)pAct)->GetNextContent() ||
			((ScChangeActionContent*)pAct)->GetPrevContent()) )
		{	// Contents an gleicher Position verketten
			ScChangeActionContent* pContent = SearchContentAt(
				pAct->GetBigRange().aStart, pAct );
			if ( pContent )
			{
				pContent->SetNextContent( (ScChangeActionContent*) pAct );
				((ScChangeActionContent*)pAct)->SetPrevContent( pContent );
			}
		}
		const ScBaseCell* pCell = ((ScChangeActionContent*)pAct)->GetNewCell();
		if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATREF )
		{
			ScAddress aOrg;
			((const ScFormulaCell*)pCell)->GetMatrixOrigin( aOrg );
			ScChangeActionContent* pContent = SearchContentAt( aOrg, pAct );
			if ( pContent && pContent->IsMatrixOrigin() )
			{
				AddDependentWithNotify( pContent, pAct );
			}
			else
			{
				DBG_ERRORFILE( "ScChangeTrack::Dependencies: MatOrg not found" );
			}
		}
	}

	if ( !(pLinkInsertCol || pLinkInsertRow || pLinkInsertTab || pLinkMove) )
		return ;		// keine Dependencies
	if ( pAct->IsRejecting() )
		return ;		// ausser Content keine Dependencies

	// Insert in einem entsprechenden Insert haengt davon ab, sonst muesste
	// der vorherige Insert gesplittet werden.
	// Sich kreuzende Inserts und Deletes sind nicht abhaengig.
	// Alles andere ist abhaengig.

	// Der zuletzt eingelinkte Insert steht am Anfang einer Kette,
	// also genau richtig

	const ScBigRange& rRange = pAct->GetBigRange();
	sal_Bool bActNoInsert = !pAct->IsInsertType();
	sal_Bool bActColDel = ( eActType == SC_CAT_DELETE_COLS );
	sal_Bool bActRowDel = ( eActType == SC_CAT_DELETE_ROWS );
	sal_Bool bActTabDel = ( eActType == SC_CAT_DELETE_TABS );

	if ( pLinkInsertCol && (eActType == SC_CAT_INSERT_COLS ||
			(bActNoInsert && !bActRowDel && !bActTabDel)) )
	{
		for ( ScChangeActionLinkEntry* pL = pLinkInsertCol; pL; pL = pL->GetNext() )
		{
			ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction();
			if ( !pTest->IsRejected() &&
					pTest->GetBigRange().Intersects( rRange ) )
			{
				AddDependentWithNotify( pTest, pAct );
				break;	// for
			}
		}
	}
	if ( pLinkInsertRow && (eActType == SC_CAT_INSERT_ROWS ||
			(bActNoInsert && !bActColDel && !bActTabDel)) )
	{
		for ( ScChangeActionLinkEntry* pL = pLinkInsertRow; pL; pL = pL->GetNext() )
		{
			ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction();
			if ( !pTest->IsRejected() &&
					pTest->GetBigRange().Intersects( rRange ) )
			{
				AddDependentWithNotify( pTest, pAct );
				break;	// for
			}
		}
	}
	if ( pLinkInsertTab && (eActType == SC_CAT_INSERT_TABS ||
			(bActNoInsert && !bActColDel &&  !bActRowDel)) )
	{
		for ( ScChangeActionLinkEntry* pL = pLinkInsertTab; pL; pL = pL->GetNext() )
		{
			ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction();
			if ( !pTest->IsRejected() &&
					pTest->GetBigRange().Intersects( rRange ) )
			{
				AddDependentWithNotify( pTest, pAct );
				break;	// for
			}
		}
	}

	if ( pLinkMove )
	{
		if ( eActType == SC_CAT_CONTENT )
		{	// Content ist von FromRange abhaengig
			const ScBigAddress& rPos = rRange.aStart;
			for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
			{
				ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction();
				if ( !pTest->IsRejected() &&
						pTest->GetFromRange().In( rPos ) )
				{
					AddDependentWithNotify( pTest, pAct );
				}
			}
		}
		else if ( eActType == SC_CAT_MOVE )
		{	// Move FromRange ist von ToRange abhaengig
			const ScBigRange& rFromRange = ((ScChangeActionMove*)pAct)->GetFromRange();
			for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
			{
				ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction();
				if ( !pTest->IsRejected() &&
						pTest->GetBigRange().Intersects( rFromRange ) )
				{
					AddDependentWithNotify( pTest, pAct );
				}
			}
		}
		else
		{	// Inserts und Deletes sind abhaengig, sobald sie FromRange oder
			// ToRange kreuzen
			for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() )
			{
				ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction();
				if ( !pTest->IsRejected() &&
						(pTest->GetFromRange().Intersects( rRange ) ||
						pTest->GetBigRange().Intersects( rRange )) )
				{
					AddDependentWithNotify( pTest, pAct );
				}
			}
		}
	}
}


void ScChangeTrack::Remove( ScChangeAction* pRemove )
{
	// aus Track ausklinken
	sal_uLong nAct = pRemove->GetActionNumber();
	aTable.Remove( nAct );
	if ( nAct == nActionMax )
		--nActionMax;
	if ( pRemove == pLast )
		pLast = pRemove->pPrev;
	if ( pRemove == pFirst )
		pFirst = pRemove->pNext;
	if ( nAct == nMarkLastSaved )
		nMarkLastSaved =
			( pRemove->pPrev ? pRemove->pPrev->GetActionNumber() : 0 );

	// aus der globalen Kette ausklinken
	if ( pRemove->pNext )
		pRemove->pNext->pPrev = pRemove->pPrev;
	if ( pRemove->pPrev )
		pRemove->pPrev->pNext = pRemove->pNext;

	// Dependencies nicht loeschen, passiert on delete automatisch durch
	// LinkEntry, ohne Listen abzuklappern

	if ( aModifiedLink.IsSet() )
	{
		NotifyModified( SC_CTM_REMOVE, nAct, nAct );
		if ( pRemove->GetType() == SC_CAT_CONTENT )
		{
			ScChangeActionContent* pContent = (ScChangeActionContent*) pRemove;
            if ( ( pContent = pContent->GetPrevContent() ) != NULL )
			{
				sal_uLong nMod = pContent->GetActionNumber();
				NotifyModified( SC_CTM_CHANGE, nMod, nMod );
			}
		}
		else if ( pLast )
			NotifyModified( SC_CTM_CHANGE, pFirst->GetActionNumber(),
				pLast->GetActionNumber() );
	}

	if ( IsInPasteCut() && pRemove->GetType() == SC_CAT_CONTENT )
	{	//! Content wird wiederverwertet
		ScChangeActionContent* pContent = (ScChangeActionContent*) pRemove;
		pContent->RemoveAllLinks();
		pContent->ClearTrack();
		pContent->pNext = pContent->pPrev = NULL;
		pContent->pNextContent = pContent->pPrevContent = NULL;
	}
}


void ScChangeTrack::Undo( sal_uLong nStartAction, sal_uLong nEndAction, bool bMerge )
{
    // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
    if ( bMerge )
    {
        SetMergeState( SC_CTMS_UNDO );
    }

	if ( nStartAction == 0 )
		++nStartAction;
	if ( nEndAction > nActionMax )
		nEndAction = nActionMax;
	if ( nEndAction && nStartAction <= nEndAction )
	{
		if ( nStartAction == nStartLastCut && nEndAction == nEndLastCut &&
				!IsInPasteCut() )
			ResetLastCut();
		StartBlockModify( SC_CTM_REMOVE, nStartAction );
		for ( sal_uLong j = nEndAction; j >= nStartAction; --j )
		{	// rueckwaerts um evtl. nActionMax zu recyclen und schnelleren
			// Zugriff via pLast, Deletes in richtiger Reihenfolge
			ScChangeAction* pAct = ( (j == nActionMax && pLast &&
				pLast->GetActionNumber() == j) ? pLast : GetAction( j ) );
			if ( pAct )
			{
				if ( pAct->IsDeleteType() )
				{
					if ( j == nEndAction || (pAct != pLast &&
							((ScChangeActionDel*)pAct)->IsTopDelete()) )
					{
						SetInDeleteTop( sal_True );
						SetInDeleteRange( ((ScChangeActionDel*)pAct)->
							GetOverAllRange().MakeRange() );
					}
				}
				UpdateReference( pAct, sal_True );
				SetInDeleteTop( sal_False );
				Remove( pAct );
				if ( IsInPasteCut() )
					aPasteCutTable.Insert( pAct->GetActionNumber(), pAct );
				else
				{
					if ( j == nStartAction && pAct->GetType() == SC_CAT_MOVE )
					{
						ScChangeActionMove* pMove = (ScChangeActionMove*) pAct;
						sal_uLong nStart = pMove->GetStartLastCut();
						sal_uLong nEnd = pMove->GetEndLastCut();
						if ( nStart && nStart <= nEnd )
						{	// LastCut wiederherstellen
							//! Links vor Cut-Append aufloesen
							pMove->RemoveAllLinks();
							StartBlockModify( SC_CTM_APPEND, nStart );
							for ( sal_uLong nCut = nStart; nCut <= nEnd; nCut++ )
							{
								ScChangeAction* pCut = aPasteCutTable.Remove( nCut );
								if ( pCut )
								{
									DBG_ASSERT( !aTable.Get( nCut ), "ScChangeTrack::Undo: nCut dup" );
									Append( pCut, nCut );
								}
								else
								{
									DBG_ERROR( "ScChangeTrack::Undo: nCut not found" );
								}
							}
							EndBlockModify( nEnd );
							ResetLastCut();
							nStartLastCut = nStart;
							nEndLastCut = nEnd;
							pLastCutMove = pMove;
							SetLastCutMoveRange(
								pMove->GetFromRange().MakeRange(), pDoc );
						}
						else
							delete pMove;
					}
					else
						delete pAct;
				}
			}
		}
		EndBlockModify( nEndAction );
	}

    // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
    if ( bMerge )
    {
        SetMergeState( SC_CTMS_OTHER );
    }
}


// static
sal_Bool ScChangeTrack::MergeIgnore( const ScChangeAction& rAction, sal_uLong nFirstMerge )
{
	if ( rAction.IsRejected() )
		return sal_True;				// da kommt noch eine passende Reject-Action

	if ( rAction.IsRejecting() && rAction.GetRejectAction() >= nFirstMerge )
		return sal_True;				// da ist sie

	return sal_False;					// alles andere
}


void ScChangeTrack::MergePrepare( ScChangeAction* pFirstMerge, bool bShared )
{
	SetMergeState( SC_CTMS_PREPARE );
	sal_uLong nFirstMerge = pFirstMerge->GetActionNumber();
	ScChangeAction* pAct = GetLast();
	if ( pAct )
	{
		SetLastMerge( pAct->GetActionNumber() );
		while ( pAct )
		{	// rueckwaerts, Deletes in richtiger Reihenfolge
            // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
            if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
			{
				if ( pAct->IsDeleteType() )
				{
					if ( ((ScChangeActionDel*)pAct)->IsTopDelete() )
					{
						SetInDeleteTop( sal_True );
						SetInDeleteRange( ((ScChangeActionDel*)pAct)->
							GetOverAllRange().MakeRange() );
					}
				}
				UpdateReference( pAct, sal_True );
				SetInDeleteTop( sal_False );
				pAct->DeleteCellEntries();		// sonst GPF bei Track Clear()
			}
			pAct = ( pAct == pFirstMerge ? NULL : pAct->GetPrev() );
		}
	}
	SetMergeState( SC_CTMS_OTHER );		//! nachfolgende per default MergeOther
}


void ScChangeTrack::MergeOwn( ScChangeAction* pAct, sal_uLong nFirstMerge, bool bShared )
{
    // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
    if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) )
	{
		SetMergeState( SC_CTMS_OWN );
		if ( pAct->IsDeleteType() )
		{
			if ( ((ScChangeActionDel*)pAct)->IsTopDelete() )
			{
				SetInDeleteTop( sal_True );
				SetInDeleteRange( ((ScChangeActionDel*)pAct)->
					GetOverAllRange().MakeRange() );
			}
		}
		UpdateReference( pAct, sal_False );
		SetInDeleteTop( sal_False );
		SetMergeState( SC_CTMS_OTHER );		//! nachfolgende per default MergeOther
	}
}


void ScChangeTrack::UpdateReference( ScChangeAction* pAct, sal_Bool bUndo )
{
	ScChangeActionType eActType = pAct->GetType();
	if ( eActType == SC_CAT_CONTENT || eActType == SC_CAT_REJECT )
		return ;

	//! Formelzellen haengen nicht im Dokument
	sal_Bool bOldAutoCalc = pDoc->GetAutoCalc();
	pDoc->SetAutoCalc( sal_False );
	sal_Bool bOldNoListening = pDoc->GetNoListening();
	pDoc->SetNoListening( sal_True );
	//! Formelzellen ExpandRefs synchronisiert zu denen im Dokument
	sal_Bool bOldExpandRefs = pDoc->IsExpandRefs();
	if ( (!bUndo && pAct->IsInsertType()) || (bUndo && pAct->IsDeleteType()) )
		pDoc->SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() );

	if ( pAct->IsDeleteType() )
	{
		SetInDeleteUndo( bUndo );
		SetInDelete( sal_True );
	}
	else if ( GetMergeState() == SC_CTMS_OWN )
	{
		// Referenzen von Formelzellen wiederherstellen,
		// vorheriges MergePrepare war bei einem Insert wie ein Delete
		if ( pAct->IsInsertType() )
			SetInDeleteUndo( sal_True );
	}

	//! erst die generated, als waeren sie vorher getrackt worden
	if ( pFirstGeneratedDelContent )
		UpdateReference( (ScChangeAction**)&pFirstGeneratedDelContent, pAct,
			bUndo );
	UpdateReference( &pFirst, pAct, bUndo );

	SetInDelete( sal_False );
	SetInDeleteUndo( sal_False );

	pDoc->SetExpandRefs( bOldExpandRefs );
	pDoc->SetNoListening( bOldNoListening );
	pDoc->SetAutoCalc( bOldAutoCalc );
}


void ScChangeTrack::UpdateReference( ScChangeAction** ppFirstAction,
		ScChangeAction* pAct, sal_Bool bUndo )
{
	ScChangeActionType eActType = pAct->GetType();
	sal_Bool bGeneratedDelContents =
		( ppFirstAction == (ScChangeAction**)&pFirstGeneratedDelContent );
	const ScBigRange& rOrgRange = pAct->GetBigRange();
	ScBigRange aRange( rOrgRange );
	ScBigRange aDelRange( rOrgRange );
	sal_Int32 nDx, nDy, nDz;
	nDx = nDy = nDz = 0;
	UpdateRefMode eMode = URM_INSDEL;
	sal_Bool bDel = sal_False;
	switch ( eActType )
	{
		case SC_CAT_INSERT_COLS :
			aRange.aEnd.SetCol( nInt32Max );
			nDx = rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1;
		break;
		case SC_CAT_INSERT_ROWS :
			aRange.aEnd.SetRow( nInt32Max );
			nDy = rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1;
		break;
		case SC_CAT_INSERT_TABS :
			aRange.aEnd.SetTab( nInt32Max );
			nDz = rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1;
		break;
		case SC_CAT_DELETE_COLS :
			aRange.aEnd.SetCol( nInt32Max );
			nDx = -(rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1);
			aDelRange.aEnd.SetCol( aDelRange.aStart.Col() - nDx - 1 );
			bDel = sal_True;
		break;
		case SC_CAT_DELETE_ROWS :
			aRange.aEnd.SetRow( nInt32Max );
			nDy = -(rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1);
			aDelRange.aEnd.SetRow( aDelRange.aStart.Row() - nDy - 1 );
			bDel = sal_True;
		break;
		case SC_CAT_DELETE_TABS :
			aRange.aEnd.SetTab( nInt32Max );
			nDz = -(rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1);
			aDelRange.aEnd.SetTab( aDelRange.aStart.Tab() - nDz - 1 );
			bDel = sal_True;
		break;
		case SC_CAT_MOVE :
			eMode = URM_MOVE;
			((ScChangeActionMove*)pAct)->GetDelta( nDx, nDy, nDz );
		break;
		default:
			DBG_ERROR( "ScChangeTrack::UpdateReference: unknown Type" );
	}
	if ( bUndo )
	{
		nDx = -nDx;
		nDy = -nDy;
		nDz = -nDz;
	}
	if ( bDel )
	{	//! fuer diesen Mechanismus gilt:
		//! es gibt nur ganze, einfache geloeschte Spalten/Zeilen
		ScChangeActionDel* pActDel = (ScChangeActionDel*) pAct;
		if ( !bUndo )
		{	// Delete
            ScChangeActionType eInsType = SC_CAT_NONE;      // for Insert-Undo-"Deletes"
			switch ( eActType )
			{
				case SC_CAT_DELETE_COLS :
					eInsType = SC_CAT_INSERT_COLS;
				break;
				case SC_CAT_DELETE_ROWS :
					eInsType = SC_CAT_INSERT_ROWS;
				break;
				case SC_CAT_DELETE_TABS :
					eInsType = SC_CAT_INSERT_TABS;
				break;
                default:
                {
                    // added to avoid warnings
                }
			}
			for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
			{
				if ( p == pAct )
					continue;	// for
				sal_Bool bUpdate = sal_True;
				if ( GetMergeState() == SC_CTMS_OTHER &&
						p->GetActionNumber() <= GetLastMerge() )
				{	// Delete in mergendem Dokument, Action im zu mergenden
					if ( p->IsInsertType() )
					{
						// Bei Insert Referenzen nur anpassen, wenn das Delete
						// das Insert nicht schneidet.
						if ( !aDelRange.Intersects( p->GetBigRange() ) )
							p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
						bUpdate = sal_False;
					}
					else if ( p->GetType() == SC_CAT_CONTENT &&
							p->IsDeletedInDelType( eInsType ) )
					{	// Content in Insert-Undo-"Delete"
						// Nicht anpassen, wenn dieses Delete in dem
						// Insert-"Delete" sein wuerde (ist nur verschoben).
						if ( aDelRange.In( p->GetBigRange().aStart ) )
							bUpdate = sal_False;
						else
						{
							const ScChangeActionLinkEntry* pLink = p->GetDeletedIn();
							while ( pLink && bUpdate )
							{
								const ScChangeAction* pDel = pLink->GetAction();
								if ( pDel && pDel->GetType() == eInsType &&
										pDel->GetBigRange().In( aDelRange ) )
									bUpdate = sal_False;
								pLink = pLink->GetNext();
							}
						}
					}
					if ( !bUpdate )
						continue;	// for
				}
				if ( aDelRange.In( p->GetBigRange() ) )
				{
					// Innerhalb eines gerade geloeschten Bereiches nicht
					// anpassen, stattdessen dem Bereich zuordnen.
					// Mehrfache geloeschte Bereiche "stapeln".
					// Kreuzende Deletes setzen mehrfach geloescht.
					if ( !p->IsDeletedInDelType( eActType ) )
					{
						p->SetDeletedIn( pActDel );
						// GeneratedDelContent in zu loeschende Liste aufnehmen
						if ( bGeneratedDelContents )
							pActDel->AddContent( (ScChangeActionContent*) p );
					}
					bUpdate = sal_False;
				}
				else
				{
					// Eingefuegte Bereiche abschneiden, wenn Start/End im
					// Delete liegt, aber das Insert nicht komplett innerhalb
					// des Delete liegt bzw. das Delete nicht komplett im
					// Insert. Das Delete merkt sich, welchem Insert es was
					// abgeschnitten hat, es kann auch nur ein einziges Insert
					// sein (weil Delete einspaltig/einzeilig ist).
					// Abgeschnittene Moves kann es viele geben.
					//! Ein Delete ist immer einspaltig/einzeilig, deswegen 1
					//! ohne die Ueberlappung auszurechnen.
					switch ( p->GetType() )
					{
						case SC_CAT_INSERT_COLS :
							if ( eActType == SC_CAT_DELETE_COLS )
							{
								if ( aDelRange.In( p->GetBigRange().aStart ) )
								{
									pActDel->SetCutOffInsert(
										(ScChangeActionIns*) p, 1 );
									p->GetBigRange().aStart.IncCol( 1 );
								}
								else if ( aDelRange.In( p->GetBigRange().aEnd ) )
								{
									pActDel->SetCutOffInsert(
										(ScChangeActionIns*) p, -1 );
									p->GetBigRange().aEnd.IncCol( -1 );
								}
							}
						break;
						case SC_CAT_INSERT_ROWS :
							if ( eActType == SC_CAT_DELETE_ROWS )
							{
								if ( aDelRange.In( p->GetBigRange().aStart ) )
								{
									pActDel->SetCutOffInsert(
										(ScChangeActionIns*) p, 1 );
									p->GetBigRange().aStart.IncRow( 1 );
								}
								else if ( aDelRange.In( p->GetBigRange().aEnd ) )
								{
									pActDel->SetCutOffInsert(
										(ScChangeActionIns*) p, -1 );
									p->GetBigRange().aEnd.IncRow( -1 );
								}
							}
						break;
						case SC_CAT_INSERT_TABS :
							if ( eActType == SC_CAT_DELETE_TABS )
							{
								if ( aDelRange.In( p->GetBigRange().aStart ) )
								{
									pActDel->SetCutOffInsert(
										(ScChangeActionIns*) p, 1 );
									p->GetBigRange().aStart.IncTab( 1 );
								}
								else if ( aDelRange.In( p->GetBigRange().aEnd ) )
								{
									pActDel->SetCutOffInsert(
										(ScChangeActionIns*) p, -1 );
									p->GetBigRange().aEnd.IncTab( -1 );
								}
							}
						break;
						case SC_CAT_MOVE :
						{
							ScChangeActionMove* pMove = (ScChangeActionMove*) p;
							short nFrom = 0;
							short nTo = 0;
							if ( aDelRange.In( pMove->GetBigRange().aStart ) )
								nTo = 1;
							else if ( aDelRange.In( pMove->GetBigRange().aEnd ) )
								nTo = -1;
							if ( aDelRange.In( pMove->GetFromRange().aStart ) )
								nFrom = 1;
							else if ( aDelRange.In( pMove->GetFromRange().aEnd ) )
								nFrom = -1;
							if ( nFrom )
							{
								switch ( eActType )
								{
									case SC_CAT_DELETE_COLS :
										if ( nFrom > 0 )
											pMove->GetFromRange().aStart.IncCol( nFrom );
										else
											pMove->GetFromRange().aEnd.IncCol( nFrom );
									break;
									case SC_CAT_DELETE_ROWS :
										if ( nFrom > 0 )
											pMove->GetFromRange().aStart.IncRow( nFrom );
										else
											pMove->GetFromRange().aEnd.IncRow( nFrom );
									break;
									case SC_CAT_DELETE_TABS :
										if ( nFrom > 0 )
											pMove->GetFromRange().aStart.IncTab( nFrom );
										else
											pMove->GetFromRange().aEnd.IncTab( nFrom );
									break;
                                    default:
                                    {
                                        // added to avoid warnings
                                    }
								}
							}
							if ( nTo )
							{
								switch ( eActType )
								{
									case SC_CAT_DELETE_COLS :
										if ( nTo > 0 )
											pMove->GetBigRange().aStart.IncCol( nTo );
										else
											pMove->GetBigRange().aEnd.IncCol( nTo );
									break;
									case SC_CAT_DELETE_ROWS :
										if ( nTo > 0 )
											pMove->GetBigRange().aStart.IncRow( nTo );
										else
											pMove->GetBigRange().aEnd.IncRow( nTo );
									break;
									case SC_CAT_DELETE_TABS :
										if ( nTo > 0 )
											pMove->GetBigRange().aStart.IncTab( nTo );
										else
											pMove->GetBigRange().aEnd.IncTab( nTo );
									break;
                                    default:
                                    {
                                        // added to avoid warnings
                                    }
								}
							}
							if ( nFrom || nTo )
							{
								ScChangeActionDelMoveEntry* pLink =
									pActDel->AddCutOffMove( pMove, nFrom, nTo );
								pMove->AddLink( pActDel, pLink );
							}
						}
						break;
                        default:
                        {
                            // added to avoid warnings
                        }
					}
				}
				if ( bUpdate )
				{
					p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
					if ( p->GetType() == eActType && !p->IsRejected() &&
							!pActDel->IsDeletedIn() &&
							p->GetBigRange().In( aDelRange ) )
						pActDel->SetDeletedIn( p );		// "druntergerutscht"
				}
			}
		}
		else
		{	// Undo Delete
			for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
			{
				if ( p == pAct )
					continue;	// for
				sal_Bool bUpdate = sal_True;
				if ( aDelRange.In( p->GetBigRange() ) )
				{
                    // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
                    if ( GetMergeState() == SC_CTMS_UNDO && !p->IsDeletedIn( pAct ) && pAct->IsDeleteType() &&
                         ( p->GetType() == SC_CAT_CONTENT ||
                           p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
                           p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) )
                    {
                        p->SetDeletedIn( pAct );
                    }

					if ( p->IsDeletedInDelType( eActType ) )
					{
						if ( p->IsDeletedIn( pActDel ) )
						{
							if ( p->GetType() != SC_CAT_CONTENT ||
									((ScChangeActionContent*)p)->IsTopContent() )
							{	// erst der TopContent wird wirklich entfernt
								p->RemoveDeletedIn( pActDel );
								// GeneratedDelContent _nicht_ aus Liste loeschen,
								// wir brauchen ihn evtl. noch fuer Reject,
								// geloescht wird in DeleteCellEntries
							}
						}
						bUpdate = sal_False;
					}
					else if ( eActType != SC_CAT_DELETE_TABS &&
							p->IsDeletedInDelType( SC_CAT_DELETE_TABS ) )
					{	// in geloeschten Tabellen nicht updaten,
						// ausser wenn Tabelle verschoben wird
						bUpdate = sal_False;
					}
					if ( p->GetType() == eActType && pActDel->IsDeletedIn( p ) )
					{
						pActDel->RemoveDeletedIn( p );	// "druntergerutscht"
						bUpdate = sal_True;
					}
				}
				if ( bUpdate )
					p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
			}
			if ( !bGeneratedDelContents )
			{	// die werden sonst noch fuer das echte Undo gebraucht
				pActDel->UndoCutOffInsert();
				pActDel->UndoCutOffMoves();
			}
		}
	}
	else if ( eActType == SC_CAT_MOVE )
	{
		ScChangeActionMove* pActMove = (ScChangeActionMove*) pAct;
		sal_Bool bLastCutMove = ( pActMove == pLastCutMove );
		const ScBigRange& rTo = pActMove->GetBigRange();
		const ScBigRange& rFrom = pActMove->GetFromRange();
		if ( !bUndo )
		{	// Move
			for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
			{
				if ( p == pAct )
					continue;	// for
				if ( p->GetType() == SC_CAT_CONTENT )
				{
					// Inhalt in Ziel deleten (Inhalt in Quelle moven)
					if ( rTo.In( p->GetBigRange() ) )
					{
						if ( !p->IsDeletedIn( pActMove ) )
						{
							p->SetDeletedIn( pActMove );
							// GeneratedDelContent in zu loeschende Liste aufnehmen
							if ( bGeneratedDelContents )
								pActMove->AddContent( (ScChangeActionContent*) p );
						}
					}
					else if ( bLastCutMove &&
							p->GetActionNumber() > nEndLastCut &&
							rFrom.In( p->GetBigRange() ) )
					{	// Paste Cut: neuer Content nach Cut eingefuegt, bleibt.
						// Aufsplitten der ContentChain
						ScChangeActionContent *pHere, *pTmp;
						pHere = (ScChangeActionContent*) p;
                        while ( (pTmp = pHere->GetPrevContent()) != NULL &&
								pTmp->GetActionNumber() > nEndLastCut )
							pHere = pTmp;
						if ( pTmp )
						{	// wird TopContent des Move
							pTmp->SetNextContent( NULL );
							pHere->SetPrevContent( NULL );
						}
						do
						{	// Abhaengigkeit vom FromRange herstellen
							AddDependentWithNotify( pActMove, pHere );
                        } while ( ( pHere = pHere->GetNextContent() ) != NULL );
					}
                    // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
                    else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
						p->UpdateReference( this, eMode, rFrom, nDx, nDy, nDz );
				}
			}
		}
		else
		{	// Undo Move
			sal_Bool bActRejected = pActMove->IsRejected();
			for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
			{
				if ( p == pAct )
					continue;	// for
				if ( p->GetType() == SC_CAT_CONTENT )
				{
					// Inhalt in Ziel moven, wenn nicht deleted, sonst undelete
					if ( p->IsDeletedIn( pActMove ) )
					{
						if ( ((ScChangeActionContent*)p)->IsTopContent() )
						{	// erst der TopContent wird wirklich entfernt
							p->RemoveDeletedIn( pActMove );
							// GeneratedDelContent _nicht_ aus Liste loeschen,
							// wir brauchen ihn evtl. noch fuer Reject,
							// geloescht wird in DeleteCellEntries
						}
					}
                    // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly
                    else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() )
						p->UpdateReference( this, eMode, rTo, nDx, nDy, nDz );
					if ( bActRejected &&
							((ScChangeActionContent*)p)->IsTopContent() &&
							rFrom.In( p->GetBigRange() ) )
					{	// Abhaengigkeit herstellen, um Content zu schreiben
						ScChangeActionLinkEntry* pLink =
							pActMove->AddDependent( p );
						p->AddLink( pActMove, pLink );
					}
				}
			}
		}
	}
	else
	{	// Insert / Undo Insert
		switch ( GetMergeState() )
		{
			case SC_CTMS_NONE :
			case SC_CTMS_OTHER :
			{
				for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
				{
					if ( p == pAct )
						continue;	// for
					p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
				}
			}
			break;
			case SC_CTMS_PREPARE :
			{
				// in Insert-Undo "Deleten"
				const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
				while ( pLink )
				{
					ScChangeAction* p = (ScChangeAction*) pLink->GetAction();
					if ( p )
						p->SetDeletedIn( pAct );
					pLink = pLink->GetNext();
				}

                // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
                for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
                {
                    if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
                         // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
                         ( p->GetType() == SC_CAT_CONTENT ||
                           p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
                           p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
                         pAct->GetBigRange().Intersects( p->GetBigRange() ) )
                    {
                        p->SetDeletedIn( pAct );
                    }
                }

                for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
				{
					if ( p == pAct )
						continue;	// for
                    if ( !p->IsDeletedIn( pAct )
                         // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
                         && p->GetActionNumber() <= pAct->GetActionNumber() )
                    {
						p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
                    }
				}
			}
			break;
			case SC_CTMS_OWN :
			{
				for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
				{
					if ( p == pAct )
						continue;	// for
                    if ( !p->IsDeletedIn( pAct )
                         // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet
                         && p->GetActionNumber() <= pAct->GetActionNumber() )
                    {
						p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
                    }
				}
				// in Insert-Undo "Delete" rueckgaengig
				const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry();
				while ( pLink )
				{
					ScChangeAction* p = (ScChangeAction*) pLink->GetAction();
					if ( p )
						p->RemoveDeletedIn( pAct );
					pLink = pLink->GetNext();
				}

                // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly
                for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
                {
                    if ( p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
                         // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
                         ( p->GetType() == SC_CAT_CONTENT ||
                           p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
                           p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
                         pAct->GetBigRange().Intersects( p->GetBigRange() ) )
                    {
                        p->RemoveDeletedIn( pAct );
                    }
                }
			}
			break;
            // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong
            case SC_CTMS_UNDO :
            {
                for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
                {
                    if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() &&
                         ( p->GetType() == SC_CAT_CONTENT ||
                           p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS ||
                           p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) &&
                         pAct->GetBigRange().Intersects( p->GetBigRange() ) )
                    {
                        p->SetDeletedIn( pAct );
                    }
                }

                for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() )
                {
                    if ( p == pAct )
                    {
                        continue;
                    }
                    if ( !p->IsDeletedIn( pAct ) && p->GetActionNumber() <= pAct->GetActionNumber() )
                    {
                        p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz );
                    }
                }
            }
            break;
		}
	}
}


void ScChangeTrack::GetDependents( ScChangeAction* pAct,
		ScChangeActionTable& rTable, sal_Bool bListMasterDelete, sal_Bool bAllFlat ) const
{
	//! bAllFlat==TRUE: intern aus Accept oder Reject gerufen,
	//! => Generated werden nicht aufgenommen

	sal_Bool bIsDelete = pAct->IsDeleteType();
	sal_Bool bIsMasterDelete = ( bListMasterDelete && pAct->IsMasterDelete() );

	const ScChangeAction* pCur = pAct;
	ScChangeActionStack* pStack = new ScChangeActionStack;
	do
	{
		if ( pCur->IsInsertType() )
		{
			const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
			while ( pL )
			{
				ScChangeAction* p = (ScChangeAction*) pL->GetAction();
				if ( p != pAct )
				{
					if ( bAllFlat )
					{
						sal_uLong n = p->GetActionNumber();
						if ( !IsGenerated( n ) && rTable.Insert( n, p ) )
							if ( p->HasDependent() )
								pStack->Push( p );
					}
					else
					{
						if ( p->GetType() == SC_CAT_CONTENT )
						{
							if ( ((ScChangeActionContent*)p)->IsTopContent() )
								rTable.Insert( p->GetActionNumber(), p );
						}
						else
							rTable.Insert( p->GetActionNumber(), p );
					}
				}
				pL = pL->GetNext();
			}
		}
		else if ( pCur->IsDeleteType() )
		{
			if ( bIsDelete )
			{	// Inhalte geloeschter Bereiche interessieren nur bei Delete
				ScChangeActionDel* pDel = (ScChangeActionDel*) pCur;
				if ( !bAllFlat && bIsMasterDelete && pCur == pAct )
				{
					// zu diesem Delete gehoerende Deletes in gleiche Ebene,
					// wenn dieses Delete das momentan oberste einer Reihe ist,
					ScChangeActionType eType = pDel->GetType();
					ScChangeAction* p = pDel;
                    while ( (p = p->GetPrev()) != NULL && p->GetType() == eType &&
							!((ScChangeActionDel*)p)->IsTopDelete() )
						rTable.Insert( p->GetActionNumber(), p );
					// dieses Delete auch in Table!
					rTable.Insert( pAct->GetActionNumber(), pAct );
				}
				else
				{
					const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
					while ( pL )
					{
						ScChangeAction* p = (ScChangeAction*) pL->GetAction();
						if ( p != pAct )
						{
							if ( bAllFlat )
							{
								// nur ein TopContent einer Kette ist in LinkDeleted
								sal_uLong n = p->GetActionNumber();
								if ( !IsGenerated( n ) && rTable.Insert( n, p ) )
									if ( p->HasDeleted() ||
											p->GetType() == SC_CAT_CONTENT )
										pStack->Push( p );
							}
							else
							{
								if ( p->IsDeleteType() )
								{	// weiteres TopDelete in gleiche Ebene,
									// es ist nicht rejectable
									if ( ((ScChangeActionDel*)p)->IsTopDelete() )
										rTable.Insert( p->GetActionNumber(), p );
								}
								else
									rTable.Insert( p->GetActionNumber(), p );
							}
						}
						pL = pL->GetNext();
					}
				}
			}
		}
		else if ( pCur->GetType() == SC_CAT_MOVE )
		{
			// geloeschte Contents im ToRange
			const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry();
			while ( pL )
			{
				ScChangeAction* p = (ScChangeAction*) pL->GetAction();
				if ( p != pAct && rTable.Insert( p->GetActionNumber(), p ) )
				{
					// nur ein TopContent einer Kette ist in LinkDeleted
					if ( bAllFlat && (p->HasDeleted() ||
							p->GetType() == SC_CAT_CONTENT) )
						pStack->Push( p );
				}
				pL = pL->GetNext();
			}
			// neue Contents im FromRange oder neuer FromRange im ToRange
			// oder Inserts/Deletes in FromRange/ToRange
			pL = pCur->GetFirstDependentEntry();
			while ( pL )
			{
				ScChangeAction* p = (ScChangeAction*) pL->GetAction();
				if ( p != pAct )
				{
					if ( bAllFlat )
					{
						sal_uLong n = p->GetActionNumber();
						if ( !IsGenerated( n ) && rTable.Insert( n, p ) )
							if ( p->HasDependent() || p->HasDeleted() )
								pStack->Push( p );
					}
					else
					{
						if ( p->GetType() == SC_CAT_CONTENT )
						{
							if ( ((ScChangeActionContent*)p)->IsTopContent() )
								rTable.Insert( p->GetActionNumber(), p );
						}
						else
							rTable.Insert( p->GetActionNumber(), p );
					}
				}
				pL = pL->GetNext();
			}
		}
		else if ( pCur->GetType() == SC_CAT_CONTENT )
		{	// alle Aenderungen an gleicher Position
			ScChangeActionContent* pContent = (ScChangeActionContent*) pCur;
			// alle vorherigen
            while ( ( pContent = pContent->GetPrevContent() ) != NULL )
			{
				if ( !pContent->IsRejected() )
					rTable.Insert( pContent->GetActionNumber(), pContent );
			}
			pContent = (ScChangeActionContent*) pCur;
			// alle nachfolgenden
            while ( ( pContent = pContent->GetNextContent() ) != NULL )
			{
				if ( !pContent->IsRejected() )
					rTable.Insert( pContent->GetActionNumber(), pContent );
			}
			// all MatrixReferences of a MatrixOrigin
			const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry();
			while ( pL )
			{
				ScChangeAction* p = (ScChangeAction*) pL->GetAction();
				if ( p != pAct )
				{
					if ( bAllFlat )
					{
						sal_uLong n = p->GetActionNumber();
						if ( !IsGenerated( n ) && rTable.Insert( n, p ) )
							if ( p->HasDependent() )
								pStack->Push( p );
					}
					else
						rTable.Insert( p->GetActionNumber(), p );
				}
				pL = pL->GetNext();
			}
		}
		else if ( pCur->GetType() == SC_CAT_REJECT )
		{
			if ( bAllFlat )
			{
				ScChangeAction* p = GetAction(
						((ScChangeActionReject*)pCur)->GetRejectAction() );
				if ( p != pAct && !rTable.Get( p->GetActionNumber() ) )
					pStack->Push( p );
			}
		}
    } while ( ( pCur = pStack->Pop() ) != NULL );
	delete pStack;
}


sal_Bool ScChangeTrack::SelectContent( ScChangeAction* pAct, sal_Bool bOldest )
{
	if ( pAct->GetType() != SC_CAT_CONTENT )
		return sal_False;

	ScChangeActionContent* pContent = (ScChangeActionContent*) pAct;
	if ( bOldest )
	{
		pContent = pContent->GetTopContent();
		ScChangeActionContent* pPrevContent;
        while ( (pPrevContent = pContent->GetPrevContent()) != NULL &&
				pPrevContent->IsVirgin() )
			pContent = pPrevContent;
	}

	if ( !pContent->IsClickable() )
		return sal_False;

	ScBigRange aBigRange( pContent->GetBigRange() );
	const ScBaseCell* pCell = (bOldest ? pContent->GetOldCell() :
		pContent->GetNewCell());
	if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATORG )
	{
        SCCOL nC;
        SCROW nR;
        ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR );
		aBigRange.aEnd.IncCol( nC-1 );
		aBigRange.aEnd.IncRow( nR-1 );
	}

	if ( !aBigRange.IsValid( pDoc ) )
		return sal_False;

	ScRange aRange( aBigRange.MakeRange() );
	if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
			aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
		return sal_False;

	if ( pContent->HasDependent() )
	{
		sal_Bool bOk = sal_True;
		Stack aRejectActions;
		const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
		while ( pL )
		{
			ScChangeAction* p = (ScChangeAction*) pL->GetAction();
			if ( p != pContent )
			{
				if ( p->GetType() == SC_CAT_CONTENT )
				{
					// we don't need no recursion here, do we?
					bOk &= ((ScChangeActionContent*)p)->Select( pDoc, this,
						bOldest, &aRejectActions );
				}
				else
				{
					DBG_ERRORFILE( "ScChangeTrack::SelectContent: content dependent no content" );
				}
			}
			pL = pL->GetNext();
		}

		bOk &= pContent->Select( pDoc, this, bOldest, NULL );
		// now the matrix is inserted and new content values are ready

		ScChangeActionContent* pNew;
        while ( ( pNew = (ScChangeActionContent*) aRejectActions.Pop() ) != NULL )
		{
			ScAddress aPos( pNew->GetBigRange().aStart.MakeAddress() );
			pNew->SetNewValue( pDoc->GetCell( aPos ), pDoc );
			Append( pNew );
		}
		return bOk;
	}
	else
		return pContent->Select( pDoc, this, bOldest, NULL );
}


void ScChangeTrack::AcceptAll()
{
	for ( ScChangeAction* p = GetFirst(); p; p = p->GetNext() )
	{
		p->Accept();
	}
}


sal_Bool ScChangeTrack::Accept( ScChangeAction* pAct )
{
	if ( !pAct->IsClickable() )
		return sal_False;

	if ( pAct->IsDeleteType() || pAct->GetType() == SC_CAT_CONTENT )
	{
        ScChangeActionTable aActionTable;
        GetDependents( pAct, aActionTable, sal_False, sal_True );
        for ( ScChangeAction* p = aActionTable.First(); p; p = aActionTable.Next() )
		{
			p->Accept();
		}
	}
	pAct->Accept();
	return sal_True;
}


sal_Bool ScChangeTrack::RejectAll()
{
	sal_Bool bOk = sal_True;
	for ( ScChangeAction* p = GetLast(); p && bOk; p = p->GetPrev() )
	{	//! rueckwaerts, weil abhaengige hinten und RejectActions angehaengt
		if ( p->IsInternalRejectable() )
			bOk = Reject( p );
	}
	return bOk;
}


sal_Bool ScChangeTrack::Reject( ScChangeAction* pAct, bool bShared )
{
    // #i100895# When collaboration changes are reversed, it must be possible
    // to reject a deleted row above another deleted row.
    if ( bShared && pAct->IsDeletedIn() )
        pAct->RemoveAllDeletedIn();

	if ( !pAct->IsRejectable() )
		return sal_False;

	ScChangeActionTable* pTable = NULL;
	if ( pAct->HasDependent() )
	{
		pTable = new ScChangeActionTable;
		GetDependents( pAct, *pTable, sal_False, sal_True );
	}
	sal_Bool bRejected = Reject( pAct, pTable, sal_False );
	if ( pTable )
		delete pTable;
	return bRejected;
}


sal_Bool ScChangeTrack::Reject( ScChangeAction* pAct, ScChangeActionTable* pTable,
		sal_Bool bRecursion )
{
	if ( !pAct->IsInternalRejectable() )
		return sal_False;

	sal_Bool bOk = sal_True;
	sal_Bool bRejected = sal_False;
	if ( pAct->IsInsertType() )
	{
		if ( pAct->HasDependent() && !bRecursion )
		{
			DBG_ASSERT( pTable, "ScChangeTrack::Reject: Insert ohne Table" );
			for ( ScChangeAction* p = pTable->Last(); p && bOk; p = pTable->Prev() )
			{
				// keine Contents restoren, die eh geloescht werden wuerden
				if ( p->GetType() == SC_CAT_CONTENT )
					p->SetRejected();
				else if ( p->IsDeleteType() )
					p->Accept();		// geloeschtes ins Nirvana
				else
					bOk = Reject( p, NULL, sal_True );		//! rekursiv
			}
		}
        if ( bOk && (bRejected = pAct->Reject( pDoc )) != sal_False )
		{
			// pRefDoc NULL := geloeschte Zellen nicht speichern
			AppendDeleteRange( pAct->GetBigRange().MakeRange(), NULL, (short) 0,
				pAct->GetActionNumber() );
		}
	}
	else if ( pAct->IsDeleteType() )
	{
		DBG_ASSERT( !pTable, "ScChangeTrack::Reject: Delete mit Table" );
		ScBigRange aDelRange;
		sal_uLong nRejectAction = pAct->GetActionNumber();
		sal_Bool bTabDel, bTabDelOk;
		if ( pAct->GetType() == SC_CAT_DELETE_TABS )
		{
			bTabDel = sal_True;
			aDelRange = pAct->GetBigRange();
			bOk = bTabDelOk = pAct->Reject( pDoc );
			if ( bOk )
			{
				pAct = pAct->GetPrev();
				bOk = ( pAct && pAct->GetType() == SC_CAT_DELETE_COLS );
			}
		}
		else
			bTabDel = bTabDelOk = sal_False;
		ScChangeActionDel* pDel = (ScChangeActionDel*) pAct;
		if ( bOk )
		{
			aDelRange = pDel->GetOverAllRange();
			bOk = aDelRange.IsValid( pDoc );
		}
		sal_Bool bOneOk = sal_False;
		if ( bOk )
		{
			ScChangeActionType eActType = pAct->GetType();
			switch ( eActType )
			{
				case SC_CAT_DELETE_COLS :
					aDelRange.aStart.SetCol( aDelRange.aEnd.Col() );
				break;
				case SC_CAT_DELETE_ROWS :
					aDelRange.aStart.SetRow( aDelRange.aEnd.Row() );
				break;
				case SC_CAT_DELETE_TABS :
					aDelRange.aStart.SetTab( aDelRange.aEnd.Tab() );
				break;
                default:
                {
                    // added to avoid warnings
                }
			}
			ScChangeAction* p = pAct;
			sal_Bool bLoop = sal_True;
			do
			{
				pDel = (ScChangeActionDel*) p;
				bOk = pDel->Reject( pDoc );
				if ( bOk )
				{
					if ( bOneOk )
					{
						switch ( pDel->GetType() )
						{
							case SC_CAT_DELETE_COLS :
								aDelRange.aStart.IncCol( -1 );
							break;
							case SC_CAT_DELETE_ROWS :
								aDelRange.aStart.IncRow( -1 );
							break;
							case SC_CAT_DELETE_TABS :
								aDelRange.aStart.IncTab( -1 );
							break;
                            default:
                            {
                                // added to avoid warnings
                            }
						}
					}
					else
						bOneOk = sal_True;
				}
				if ( pDel->IsBaseDelete() )
					bLoop = sal_False;
				else
					p = p->GetPrev();
			} while ( bOk && bLoop && p && p->GetType() == eActType &&
				!((ScChangeActionDel*)p)->IsTopDelete() );
		}
		bRejected = bOk;
		if ( bOneOk || (bTabDel && bTabDelOk) )
		{
			// Delete-Reject machte UpdateReference Undo
			ScChangeActionIns* pReject = new ScChangeActionIns(
				aDelRange.MakeRange() );
			pReject->SetRejectAction( nRejectAction );
			pReject->SetState( SC_CAS_ACCEPTED );
			Append( pReject );
		}
	}
	else if ( pAct->GetType() == SC_CAT_MOVE )
	{
		if ( pAct->HasDependent() && !bRecursion )
		{
			DBG_ASSERT( pTable, "ScChangeTrack::Reject: Move ohne Table" );
			for ( ScChangeAction* p = pTable->Last(); p && bOk; p = pTable->Prev() )
			{
				bOk = Reject( p, NULL, sal_True );		//! rekursiv
			}
		}
        if ( bOk && (bRejected = pAct->Reject( pDoc )) != sal_False )
		{
			ScChangeActionMove* pReject = new ScChangeActionMove(
				pAct->GetBigRange().MakeRange(),
				((ScChangeActionMove*)pAct)->GetFromRange().MakeRange(), this );
			pReject->SetRejectAction( pAct->GetActionNumber() );
			pReject->SetState( SC_CAS_ACCEPTED );
			Append( pReject );
		}
	}
	else if ( pAct->GetType() == SC_CAT_CONTENT )
	{
		ScRange aRange;
		ScChangeActionContent* pReject;
		if ( bRecursion )
			pReject = NULL;
		else
		{
			aRange = pAct->GetBigRange().aStart.MakeAddress();
			pReject = new ScChangeActionContent( aRange );
			pReject->SetOldValue( pDoc->GetCell( aRange.aStart ), pDoc, pDoc );
		}
        if ( (bRejected = pAct->Reject( pDoc )) != sal_False && !bRecursion )
		{
			pReject->SetNewValue( pDoc->GetCell( aRange.aStart ), pDoc );
			pReject->SetRejectAction( pAct->GetActionNumber() );
			pReject->SetState( SC_CAS_ACCEPTED );
			Append( pReject );
		}
		else if ( pReject )
			delete pReject;
	}
	else
	{
		DBG_ERROR( "ScChangeTrack::Reject: say what?" );
	}

	return bRejected;
}


sal_uLong ScChangeTrack::AddLoadedGenerated(ScBaseCell* pNewCell, const ScBigRange& aBigRange, const String& sNewValue )
{
	ScChangeActionContent* pAct = new ScChangeActionContent( --nGeneratedMin, pNewCell, aBigRange, pDoc, sNewValue );
	if ( pAct )
	{
		if ( pFirstGeneratedDelContent )
			pFirstGeneratedDelContent->pPrev = pAct;
		pAct->pNext = pFirstGeneratedDelContent;
		pFirstGeneratedDelContent = pAct;
		aGeneratedTable.Insert( pAct->GetActionNumber(), pAct );
		return pAct->GetActionNumber();
	}
	return 0;
}

void ScChangeTrack::AppendCloned( ScChangeAction* pAppend )
{
    aTable.Insert( pAppend->GetActionNumber(), pAppend );
    if ( !pLast )
        pFirst = pLast = pAppend;
    else
    {
        pLast->pNext = pAppend;
        pAppend->pPrev = pLast;
        pLast = pAppend;
    }
}

ScChangeTrack* ScChangeTrack::Clone( ScDocument* pDocument ) const
{
    if ( !pDocument )
    {
        return NULL;
    }

    ScChangeTrack* pClonedTrack = new ScChangeTrack( pDocument );
    pClonedTrack->SetTime100thSeconds( IsTime100thSeconds() );

    // clone generated actions
    ::std::stack< const ScChangeAction* > aGeneratedStack;
    const ScChangeAction* pGenerated = GetFirstGenerated();
    while ( pGenerated )
    {
        aGeneratedStack.push( pGenerated );
        pGenerated = pGenerated->GetNext();
    }
    while ( !aGeneratedStack.empty() )
    {
        pGenerated = aGeneratedStack.top();
        aGeneratedStack.pop();
        const ScChangeActionContent* pContent = dynamic_cast< const ScChangeActionContent* >( pGenerated );
        DBG_ASSERT( pContent, "ScChangeTrack::Clone: pContent is null!" );
        const ScBaseCell* pNewCell = pContent->GetNewCell();
        if ( pNewCell )
        {
            ScBaseCell* pClonedNewCell = pNewCell->CloneWithoutNote( *pDocument );
            String aNewValue;
            pContent->GetNewString( aNewValue );
            pClonedTrack->nGeneratedMin = pGenerated->GetActionNumber() + 1;
            pClonedTrack->AddLoadedGenerated( pClonedNewCell, pGenerated->GetBigRange(), aNewValue );
        }
    }

    // clone actions
    const ScChangeAction* pAction = GetFirst();
    while ( pAction )
    {
        ScChangeAction* pClonedAction = NULL;

        switch ( pAction->GetType() )
        {
            case SC_CAT_INSERT_COLS:
            case SC_CAT_INSERT_ROWS:
            case SC_CAT_INSERT_TABS:
                {
                    pClonedAction = new ScChangeActionIns(
                        pAction->GetActionNumber(),
                        pAction->GetState(),
                        pAction->GetRejectAction(),
		                pAction->GetBigRange(),
                        pAction->GetUser(),
                        pAction->GetDateTimeUTC(),
                        pAction->GetComment(),
                        pAction->GetType() );
                }
                break;
            case SC_CAT_DELETE_COLS:
            case SC_CAT_DELETE_ROWS:
            case SC_CAT_DELETE_TABS:
                {
                    const ScChangeActionDel* pDelete = dynamic_cast< const ScChangeActionDel* >( pAction );
                    DBG_ASSERT( pDelete, "ScChangeTrack::Clone: pDelete is null!" );

                    SCsCOLROW nD = 0;
                    ScChangeActionType eType = pAction->GetType();
                    if ( eType == SC_CAT_DELETE_COLS )
                    {
                        nD = static_cast< SCsCOLROW >( pDelete->GetDx() );
                    }
                    else if ( eType == SC_CAT_DELETE_ROWS )
                    {
                        nD = static_cast< SCsCOLROW >( pDelete->GetDy() );
                    }

                    pClonedAction = new ScChangeActionDel(
                        pAction->GetActionNumber(),
                        pAction->GetState(),
                        pAction->GetRejectAction(),
		                pAction->GetBigRange(),
                        pAction->GetUser(),
                        pAction->GetDateTimeUTC(),
                        pAction->GetComment(),
                        eType,
                        nD,
                        pClonedTrack );
                }
                break;
            case SC_CAT_MOVE:
                {
                    const ScChangeActionMove* pMove = dynamic_cast< const ScChangeActionMove* >( pAction );
                    DBG_ASSERT( pMove, "ScChangeTrack::Clone: pMove is null!" );

                    pClonedAction = new ScChangeActionMove(
                        pAction->GetActionNumber(),
                        pAction->GetState(),
                        pAction->GetRejectAction(),
		                pAction->GetBigRange(),
                        pAction->GetUser(),
                        pAction->GetDateTimeUTC(),
                        pAction->GetComment(),
                        pMove->GetFromRange(),
                        pClonedTrack );
                }
                break;
            case SC_CAT_CONTENT:
                {
                    const ScChangeActionContent* pContent = dynamic_cast< const ScChangeActionContent* >( pAction );
                    DBG_ASSERT( pContent, "ScChangeTrack::Clone: pContent is null!" );
                    const ScBaseCell* pOldCell = pContent->GetOldCell();
                    ScBaseCell* pClonedOldCell = pOldCell ? pOldCell->CloneWithoutNote( *pDocument ) : 0;
                    String aOldValue;
                    pContent->GetOldString( aOldValue );

                    ScChangeActionContent* pClonedContent = new ScChangeActionContent(
                        pAction->GetActionNumber(),
                        pAction->GetState(),
                        pAction->GetRejectAction(),
		                pAction->GetBigRange(),
                        pAction->GetUser(),
                        pAction->GetDateTimeUTC(),
                        pAction->GetComment(),
                        pClonedOldCell,
                        pDocument,
                        aOldValue );

                    const ScBaseCell* pNewCell = pContent->GetNewCell();
                    if ( pNewCell )
                    {
                        ScBaseCell* pClonedNewCell = pNewCell->CloneWithoutNote( *pDocument );
                        pClonedContent->SetNewValue( pClonedNewCell, pDocument );
                    }

                    pClonedAction = pClonedContent;
                }
                break;
            case SC_CAT_REJECT:
                {
                    pClonedAction = new ScChangeActionReject(
                        pAction->GetActionNumber(),
                        pAction->GetState(),
                        pAction->GetRejectAction(),
		                pAction->GetBigRange(),
                        pAction->GetUser(),
                        pAction->GetDateTimeUTC(),
                        pAction->GetComment() );
                }
                break;
            default:
                {
                }
                break;
        }

        if ( pClonedAction )
        {
            pClonedTrack->AppendCloned( pClonedAction );
        }

        pAction = pAction->GetNext();
    }

    if ( pClonedTrack->GetLast() )
    {
        pClonedTrack->SetActionMax( pClonedTrack->GetLast()->GetActionNumber() );
    }

    // set dependencies for Deleted/DeletedIn
    pAction = GetFirst();
    while ( pAction )
    {
        if ( pAction->HasDeleted() )
        {
            ::std::stack< sal_uLong > aStack;
            const ScChangeActionLinkEntry* pL = pAction->GetFirstDeletedEntry();
            while ( pL )
            {
                const ScChangeAction* pDeleted = pL->GetAction();
                if ( pDeleted )
                {
                    aStack.push( pDeleted->GetActionNumber() );
                }
                pL = pL->GetNext();
            }
            ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
            if ( pClonedAction )
            {
                while ( !aStack.empty() )
                {
                    ScChangeAction* pClonedDeleted = pClonedTrack->GetActionOrGenerated( aStack.top() );
                    aStack.pop();
                    if ( pClonedDeleted )
                    {
                        pClonedDeleted->SetDeletedIn( pClonedAction );
                    }
                }
            }
        }
        pAction = pAction->GetNext();
    }

    // set dependencies for Dependent/Any
    pAction = GetLast();
    while ( pAction )
    {
        if ( pAction->HasDependent() )
        {
            ::std::stack< sal_uLong > aStack;
            const ScChangeActionLinkEntry* pL = pAction->GetFirstDependentEntry();
            while ( pL )
            {
                const ScChangeAction* pDependent = pL->GetAction();
                if ( pDependent )
                {
                    aStack.push( pDependent->GetActionNumber() );
                }
                pL = pL->GetNext();
            }
            ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() );
            if ( pClonedAction )
            {
                while ( !aStack.empty() )
                {
                    ScChangeAction* pClonedDependent = pClonedTrack->GetActionOrGenerated( aStack.top() );
                    aStack.pop();
                    if ( pClonedDependent )
                    {
                        ScChangeActionLinkEntry* pLink = pClonedAction->AddDependent( pClonedDependent );
                        pClonedDependent->AddLink( pClonedAction, pLink );
                    }
                }
            }
        }
        pAction = pAction->GetPrev();
    }

    // masterlinks
    ScChangeAction* pClonedAction = pClonedTrack->GetFirst();
    while ( pClonedAction )
    {
        pClonedTrack->MasterLinks( pClonedAction );
        pClonedAction = pClonedAction->GetNext();
    }

    if ( IsProtected() )
    {
        pClonedTrack->SetProtection( GetProtection() );
    }

    if ( pClonedTrack->GetLast() )
    {
        pClonedTrack->SetLastSavedActionNumber( pClonedTrack->GetLast()->GetActionNumber() );
    }

    pDocument->SetChangeTrack( pClonedTrack );

    return pClonedTrack;
}

void ScChangeTrack::MergeActionState( ScChangeAction* pAct, const ScChangeAction* pOtherAct )
{
    if ( pAct->IsVirgin() )
    {
        if ( pOtherAct->IsAccepted() )
        {
            pAct->Accept();
            if ( pOtherAct->IsRejecting() )
            {
                pAct->SetRejectAction( pOtherAct->GetRejectAction() );
            }
        }
        else if ( pOtherAct->IsRejected() )
        {
            pAct->SetRejected();
        }
    }
}

#if DEBUG_CHANGETRACK
String ScChangeTrack::ToString() const
{
    String aReturn;

    aReturn += String::CreateFromAscii( "============================================================\n" );

    const ScChangeAction* pGenerated = GetFirstGenerated();
    while ( pGenerated )
    {
        aReturn += pGenerated->ToString( pDoc );
        aReturn += '\n';
        pGenerated = pGenerated->GetNext();
    }

    aReturn += String::CreateFromAscii( "------------------------------------------------------------\n" );

    const ScChangeAction* pAction = GetFirst();
    while ( pAction )
    {
        aReturn += pAction->ToString( pDoc );
        aReturn += '\n';
        pAction = pAction->GetNext();
    }
    aReturn += String::CreateFromAscii( "============================================================\n" );

    return aReturn;
}
#endif // DEBUG_CHANGETRACK
