diff options
Diffstat (limited to 'sc/source/core/tool/chgtrack.cxx')
-rw-r--r-- | sc/source/core/tool/chgtrack.cxx | 4869 |
1 files changed, 4869 insertions, 0 deletions
diff --git a/sc/source/core/tool/chgtrack.cxx b/sc/source/core/tool/chgtrack.cxx new file mode 100644 index 000000000000..ba3f3e47bfb7 --- /dev/null +++ b/sc/source/core/tool/chgtrack.cxx @@ -0,0 +1,4869 @@ +/************************************************************************* + * + * 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 USHORT nMemPoolChangeActionCellListEntry = (0x2000 - 64) / sizeof(ScChangeActionCellListEntry); +IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionCellListEntry, nMemPoolChangeActionCellListEntry, nMemPoolChangeActionCellListEntry ) + +const USHORT 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 ULONG nTempAction, const 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 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(); +} + + +BOOL ScChangeAction::IsVisible() const +{ + //! sequence order of execution is significant + if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() ) + return FALSE; + if ( GetType() == SC_CAT_CONTENT ) + return ((ScChangeActionContent*)this)->IsTopContent(); + return TRUE; +} + + +BOOL ScChangeAction::IsTouchable() const +{ + //! sequence order of execution is significant + if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() ) + return FALSE; + // content may reject and be touchable if on top + if ( GetType() == SC_CAT_CONTENT ) + return ((ScChangeActionContent*)this)->IsTopContent(); + if ( IsRejecting() ) + return FALSE; + return TRUE; +} + + +BOOL ScChangeAction::IsClickable() const +{ + //! sequence order of execution is significant + if ( !IsVirgin() ) + return FALSE; + if ( IsDeletedIn() ) + return FALSE; + if ( GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContentCellType eCCT = + ScChangeActionContent::GetContentCellType( + ((ScChangeActionContent*)this)->GetNewCell() ); + if ( eCCT == SC_CACCT_MATREF ) + return 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 FALSE; + pL = pL->GetNext(); + } + } + return TRUE; // for Select() a content doesn't have to be touchable + } + return IsTouchable(); // Accept()/Reject() only on touchables +} + + +BOOL ScChangeAction::IsRejectable() const +{ + //! sequence order of execution is significant + if ( !IsClickable() ) + return FALSE; + if ( GetType() == SC_CAT_CONTENT ) + { + if ( ((ScChangeActionContent*)this)->IsOldMatrixReference() ) + return FALSE; + ScChangeActionContent* pNextContent = + ((ScChangeActionContent*)this)->GetNextContent(); + if ( pNextContent == NULL ) + return TRUE; // *this is TopContent + return pNextContent->IsRejected(); // *this is next rejectable + } + return IsTouchable(); +} + + +BOOL ScChangeAction::IsInternalRejectable() const +{ + //! sequence order of execution is significant + if ( !IsVirgin() ) + return FALSE; + if ( IsDeletedIn() ) + return FALSE; + if ( GetType() == SC_CAT_CONTENT ) + { + ScChangeActionContent* pNextContent = + ((ScChangeActionContent*)this)->GetNextContent(); + if ( pNextContent == NULL ) + return TRUE; // *this is TopContent + return pNextContent->IsRejected(); // *this is next rejectable + } + return IsTouchable(); +} + + +BOOL ScChangeAction::IsDialogRoot() const +{ + return IsInternalRejectable(); // only rejectables in root +} + + +BOOL ScChangeAction::IsDialogParent() const +{ + //! sequence order of execution is significant + if ( GetType() == SC_CAT_CONTENT ) + { + if ( !IsDialogRoot() ) + return FALSE; + if ( ((ScChangeActionContent*)this)->IsMatrixOrigin() && HasDependent() ) + return TRUE; + ScChangeActionContent* pPrevContent = + ((ScChangeActionContent*)this)->GetPrevContent(); + return pPrevContent && pPrevContent->IsVirgin(); + } + if ( HasDependent() ) + return IsDeleteType() ? TRUE : !IsDeletedIn(); + if ( HasDeleted() ) + { + if ( IsDeleteType() ) + { + if ( IsDialogRoot() ) + return TRUE; + ScChangeActionLinkEntry* pL = pLinkDeleted; + while ( pL ) + { + ScChangeAction* p = pL->GetAction(); + if ( p && p->GetType() != eType ) + return TRUE; + pL = pL->GetNext(); + } + } + else + return TRUE; + } + return FALSE; +} + + +BOOL ScChangeAction::IsMasterDelete() const +{ + if ( !IsDeleteType() ) + return 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 +} + + +BOOL ScChangeAction::RemoveDeletedIn( const ScChangeAction* p ) +{ + BOOL bRemoved = FALSE; + ScChangeActionLinkEntry* pL = GetDeletedIn(); + while ( pL ) + { + ScChangeActionLinkEntry* pNextLink = pL->GetNext(); + if ( pL->GetAction() == p ) + { + delete pL; + bRemoved = TRUE; + } + pL = pNextLink; + } + return bRemoved; +} + + +BOOL ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const +{ + ScChangeActionLinkEntry* pL = GetDeletedIn(); + while ( pL ) + { + if ( pL->GetAction() == p ) + return TRUE; + pL = pL->GetNext(); + } + return FALSE; +} + + +void ScChangeAction::RemoveAllDeletedIn() +{ + //! nicht vom evtl. TopContent sondern wirklich dieser + while ( pLinkDeletedIn ) + delete pLinkDeletedIn; // rueckt sich selbst hoch +} + + +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 TRUE; + pL = pL->GetNext(); + } + } + return 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, + INT32 nDx, INT32 nDy, INT32 nDz ) +{ + ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() ); +} + + +void ScChangeAction::GetDescription( String& rStr, ScDocument* /* pDoc */, + 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, FALSE, 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, BOOL bFlag3D ) const +{ + String aStr; + USHORT 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, + 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, 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( 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( 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, TRUE ); + + String aAuthor = GetUser(); + + DateTime aDT = GetDateTime(); + String aDate = ScGlobal::pLocaleData->getDate( aDT ); + aDate += ' '; + aDate += ScGlobal::pLocaleData->getTime( aDT, FALSE, 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 ULONG nActionNumber, const ScChangeActionState eStateP, const 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, + BOOL bSplitRange, bool bWarning ) const +{ + ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); + + USHORT 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 ); +} + + +BOOL ScChangeActionIns::Reject( ScDocument* pDoc ) +{ + if ( !aBigRange.IsValid( pDoc ) ) + return FALSE; + + ScRange aRange( aBigRange.MakeRange() ); + if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(), + aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) ) + return 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 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 ULONG nActionNumber, const ScChangeActionState eStateP, const 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 ); +} + + +BOOL ScChangeActionDel::IsBaseDelete() const +{ + return !GetDx() && !GetDy(); +} + + +BOOL ScChangeActionDel::IsTopDelete() const +{ + const ScChangeAction* p = GetNext(); + if ( !p || p->GetType() != GetType() ) + return TRUE; + return ((ScChangeActionDel*)p)->IsBaseDelete(); +} + + +BOOL ScChangeActionDel::IsMultiDelete() const +{ + if ( GetDx() || GetDy() ) + return TRUE; + const ScChangeAction* p = GetNext(); + if ( !p || p->GetType() != GetType() ) + return FALSE; + const ScChangeActionDel* pDel = (const ScChangeActionDel*) p; + if ( (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) && + pDel->GetBigRange() == aBigRange ) + return TRUE; + return FALSE; +} + + +BOOL ScChangeActionDel::IsTabDeleteCol() const +{ + if ( GetType() != SC_CAT_DELETE_COLS ) + return 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, + INT32 nDxP, INT32 nDyP, 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, + BOOL bSplitRange, bool bWarning ) const +{ + ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); + + USHORT 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 ); +} + + +BOOL ScChangeActionDel::Reject( ScDocument* pDoc ) +{ + if ( !aBigRange.IsValid( pDoc ) && GetType() != SC_CAT_DELETE_TABS ) + return FALSE; + + BOOL bOk = 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 = FALSE; + } + else + bOk = FALSE; + } + if ( bOk ) + { + ScRange aRange( aTmpRange.MakeRange() ); + // InDelete... fuer Formel UpdateReference in Document + pTrack->SetInDeleteRange( aRange ); + pTrack->SetInDeleteTop( TRUE ); + pTrack->SetInDeleteUndo( TRUE ); + pTrack->SetInDelete( 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 ) ) != FALSE ) + bOk = pDoc->InsertCol( aRange ); + } + break; + case SC_CAT_DELETE_ROWS : + if ( ( bOk = pDoc->CanInsertRow( aRange ) ) != FALSE ) + bOk = pDoc->InsertRow( aRange ); + break; + case SC_CAT_DELETE_TABS : + { +//2do: Tabellennamen merken? + String aName; + pDoc->CreateValidTabName( aName ); + if ( ( bOk = pDoc->ValidNewTabName( aName ) ) != FALSE ) + bOk = pDoc->InsertTab( aRange.aStart.Tab(), aName ); + } + break; + default: + { + // added to avoid warnings + } + } + pTrack->SetInDelete( FALSE ); + pTrack->SetInDeleteUndo( FALSE ); + } + if ( !bOk ) + { + pTrack->SetInDeleteTop( FALSE ); + return FALSE; + } + // InDeleteTop fuer UpdateReference-Undo behalten + } + + // setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries + RejectRestoreContents( pTrack, GetDx(), GetDy() ); + + pTrack->SetInDeleteTop( FALSE ); + RemoveAllLinks(); + return 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 ULONG nActionNumber, const ScChangeActionState eStateP, const 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, + INT32 nDx, INT32 nDy, INT32 nDz ) +{ + ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aFromRange ); + ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() ); +} + + +void ScChangeActionMove::GetDelta( INT32& nDx, INT32& nDy, 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, + BOOL bSplitRange, bool bWarning ) const +{ + ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); + + 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, + 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 ); +} + + +BOOL ScChangeActionMove::Reject( ScDocument* pDoc ) +{ + if ( !(aBigRange.IsValid( pDoc ) && aFromRange.IsValid( pDoc )) ) + return FALSE; + + ScRange aToRange( aBigRange.MakeRange() ); + ScRange aFrmRange( aFromRange.MakeRange() ); + + 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 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 TRUE; +} + + +// --- ScChangeActionContent ----------------------------------------------- + +const USHORT nMemPoolChangeActionContent = (0x8000 - 64) / sizeof(ScChangeActionContent); +IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionContent, nMemPoolChangeActionContent, nMemPoolChangeActionContent ) + +ScChangeActionContent::ScChangeActionContent( const ULONG nActionNumber, + const ScChangeActionState eStateP, const 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 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, 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, + ULONG nOldFormat, ScBaseCell* pNewCellP, + 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( 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, + 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, + BOOL bFlag3D ) const +{ + USHORT 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 ); +} + + +BOOL ScChangeActionContent::Reject( ScDocument* pDoc ) +{ + if ( !aBigRange.IsValid( pDoc ) ) + return FALSE; + + PutOldValueToDoc( pDoc, 0, 0 ); + + SetState( SC_CAS_REJECTED ); + RemoveAllLinks(); + + return TRUE; +} + + +BOOL ScChangeActionContent::Select( ScDocument* pDoc, ScChangeTrack* pTrack, + BOOL bOldest, Stack* pRejectActions ) +{ + if ( !aBigRange.IsValid( pDoc ) ) + return 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 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, 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 +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 ) +{ + ULONG nFormat = NeedsNumberFormat( pOrgCell ) ? pFromDoc->GetNumberFormat( rPos ) : 0; + SetValue( rStr, pCell, nFormat, pOrgCell, pFromDoc, pToDoc ); +} + + +// static +void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell, + 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( TRUE ); + break; + default: + { + // added to avoid warnings + } + } + } + else + pCell = NULL; +} + + +// static +void ScChangeActionContent::SetCell( String& rStr, ScBaseCell* pCell, + 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( 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( TRUE ); + } + if ( rPos.Row() < 0 || MAXROW < rPos.Row() ) + { + rRef1.nRow = SCROW_MAX; + rRef1.nRelRow = SCROW_MAX; + rRef1.SetRowDeleted( TRUE ); + } + if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() ) + { + rRef1.nTab = SCTAB_MAX; + rRef1.nRelTab = SCTAB_MAX; + rRef1.SetTabDeleted( 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( TRUE ); + } + if ( rPos.Row() < 0 || MAXROW < rPos.Row() ) + { + rRef2.nRow = SCROW_MAX; + rRef2.nRelRow = SCROW_MAX; + rRef2.SetRowDeleted( TRUE ); + } + if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() ) + { + rRef2.nTab = SCTAB_MAX; + rRef2.nRelTab = SCTAB_MAX; + rRef2.SetTabDeleted( TRUE ); + } + } +} + + +void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack, + UpdateRefMode eMode, const ScBigRange& rRange, + INT32 nDx, INT32 nDy, 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 + + BOOL bOldFormula = ( pOldCell && pOldCell->GetCellType() == CELLTYPE_FORMULA ); + 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 ULONG nActionNumber, const ScChangeActionState eStateP, const 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 = FALSE; + bInDelete = FALSE; + bInDeleteTop = FALSE; + bInDeleteUndo = FALSE; + bInPasteCut = FALSE; + bUseFixDateTime = FALSE; + bTime100thSeconds = 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(); + USHORT 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, + 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( 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 ) + { + BOOL bNew = FALSE; + ScChangeTrackMsgInfo* pMsg; + while ( ( pMsg = aMsgStackFinal.Pop() ) != NULL ) + { + aMsgQueue.Put( pMsg ); + bNew = TRUE; + } + if ( bNew ) + aModifiedLink.Call( this ); + } + } +} + + +void ScChangeTrack::NotifyModified( ScChangeTrackMsgType eMsgType, + ULONG nStartAction, 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, 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, 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, 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 ) + { + 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, ULONG& nStartAction, ULONG& nEndAction, SCsTAB nDz ) +{ + nStartAction = GetActionMax() + 1; + AppendDeleteRange( rRange, pRefDoc, nDz, 0 ); + nEndAction = GetActionMax(); +} + + +void ScChangeTrack::AppendDeleteRange( const ScRange& rRange, + ScDocument* pRefDoc, SCsTAB nDz, 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( 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( 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( TRUE ); + AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0, + 0, nRejectingInsert ); + } + } + else + { + DBG_ERROR( "ScChangeTrack::AppendDeleteRange: Block not supported!" ); + } + SetInDeleteTop( FALSE ); + } + } + EndBlockModify( GetActionMax() ); +} + + +void ScChangeTrack::AppendOneDeleteRange( const ScRange& rOrgRange, + ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + 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 +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, 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, ULONG& nStartAction, 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 ); + BOOL bDoContents; + if ( eClipMode == SC_CACM_PASTE && HasLastCut() ) + { + bDoContents = FALSE; + SetInPasteCut( 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 = 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 = 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 = 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( FALSE ); + } + else + { + bDoContents = 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, + ULONG& nStartAction, 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, + ULONG nOldFormat, 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 ) +{ + 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() ) + { + 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(); + BOOL bActNoInsert = !pAct->IsInsertType(); + BOOL bActColDel = ( eActType == SC_CAT_DELETE_COLS ); + BOOL bActRowDel = ( eActType == SC_CAT_DELETE_ROWS ); + 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 + 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 ) + { + 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( ULONG nStartAction, 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 ( 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( TRUE ); + SetInDeleteRange( ((ScChangeActionDel*)pAct)-> + GetOverAllRange().MakeRange() ); + } + } + UpdateReference( pAct, TRUE ); + SetInDeleteTop( FALSE ); + Remove( pAct ); + if ( IsInPasteCut() ) + aPasteCutTable.Insert( pAct->GetActionNumber(), pAct ); + else + { + if ( j == nStartAction && pAct->GetType() == SC_CAT_MOVE ) + { + ScChangeActionMove* pMove = (ScChangeActionMove*) pAct; + ULONG nStart = pMove->GetStartLastCut(); + ULONG nEnd = pMove->GetEndLastCut(); + if ( nStart && nStart <= nEnd ) + { // LastCut wiederherstellen + //! Links vor Cut-Append aufloesen + pMove->RemoveAllLinks(); + StartBlockModify( SC_CTM_APPEND, nStart ); + for ( 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 +BOOL ScChangeTrack::MergeIgnore( const ScChangeAction& rAction, ULONG nFirstMerge ) +{ + if ( rAction.IsRejected() ) + return TRUE; // da kommt noch eine passende Reject-Action + + if ( rAction.IsRejecting() && rAction.GetRejectAction() >= nFirstMerge ) + return TRUE; // da ist sie + + return FALSE; // alles andere +} + + +void ScChangeTrack::MergePrepare( ScChangeAction* pFirstMerge, bool bShared ) +{ + SetMergeState( SC_CTMS_PREPARE ); + 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( TRUE ); + SetInDeleteRange( ((ScChangeActionDel*)pAct)-> + GetOverAllRange().MakeRange() ); + } + } + UpdateReference( pAct, TRUE ); + SetInDeleteTop( 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, 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( TRUE ); + SetInDeleteRange( ((ScChangeActionDel*)pAct)-> + GetOverAllRange().MakeRange() ); + } + } + UpdateReference( pAct, FALSE ); + SetInDeleteTop( FALSE ); + SetMergeState( SC_CTMS_OTHER ); //! nachfolgende per default MergeOther + } +} + + +void ScChangeTrack::UpdateReference( ScChangeAction* pAct, BOOL bUndo ) +{ + ScChangeActionType eActType = pAct->GetType(); + if ( eActType == SC_CAT_CONTENT || eActType == SC_CAT_REJECT ) + return ; + + //! Formelzellen haengen nicht im Dokument + BOOL bOldAutoCalc = pDoc->GetAutoCalc(); + pDoc->SetAutoCalc( FALSE ); + BOOL bOldNoListening = pDoc->GetNoListening(); + pDoc->SetNoListening( TRUE ); + //! Formelzellen ExpandRefs synchronisiert zu denen im Dokument + BOOL bOldExpandRefs = pDoc->IsExpandRefs(); + if ( (!bUndo && pAct->IsInsertType()) || (bUndo && pAct->IsDeleteType()) ) + pDoc->SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() ); + + if ( pAct->IsDeleteType() ) + { + SetInDeleteUndo( bUndo ); + SetInDelete( TRUE ); + } + else if ( GetMergeState() == SC_CTMS_OWN ) + { + // Referenzen von Formelzellen wiederherstellen, + // vorheriges MergePrepare war bei einem Insert wie ein Delete + if ( pAct->IsInsertType() ) + SetInDeleteUndo( TRUE ); + } + + //! erst die generated, als waeren sie vorher getrackt worden + if ( pFirstGeneratedDelContent ) + UpdateReference( (ScChangeAction**)&pFirstGeneratedDelContent, pAct, + bUndo ); + UpdateReference( &pFirst, pAct, bUndo ); + + SetInDelete( FALSE ); + SetInDeleteUndo( FALSE ); + + pDoc->SetExpandRefs( bOldExpandRefs ); + pDoc->SetNoListening( bOldNoListening ); + pDoc->SetAutoCalc( bOldAutoCalc ); +} + + +void ScChangeTrack::UpdateReference( ScChangeAction** ppFirstAction, + ScChangeAction* pAct, BOOL bUndo ) +{ + ScChangeActionType eActType = pAct->GetType(); + BOOL bGeneratedDelContents = + ( ppFirstAction == (ScChangeAction**)&pFirstGeneratedDelContent ); + const ScBigRange& rOrgRange = pAct->GetBigRange(); + ScBigRange aRange( rOrgRange ); + ScBigRange aDelRange( rOrgRange ); + INT32 nDx, nDy, nDz; + nDx = nDy = nDz = 0; + UpdateRefMode eMode = URM_INSDEL; + BOOL bDel = 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 = 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 = 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 = 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 + BOOL bUpdate = 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 = 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 = 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 = 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 = 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 + BOOL bUpdate = 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 = 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 = FALSE; + } + if ( p->GetType() == eActType && pActDel->IsDeletedIn( p ) ) + { + pActDel->RemoveDeletedIn( p ); // "druntergerutscht" + bUpdate = 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; + 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 + 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, BOOL bListMasterDelete, BOOL bAllFlat ) const +{ + //! bAllFlat==TRUE: intern aus Accept oder Reject gerufen, + //! => Generated werden nicht aufgenommen + + BOOL bIsDelete = pAct->IsDeleteType(); + 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 ) + { + 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 + 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 ) + { + 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 ) + { + 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; +} + + +BOOL ScChangeTrack::SelectContent( ScChangeAction* pAct, BOOL bOldest ) +{ + if ( pAct->GetType() != SC_CAT_CONTENT ) + return 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 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 FALSE; + + ScRange aRange( aBigRange.MakeRange() ); + if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(), + aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) ) + return FALSE; + + if ( pContent->HasDependent() ) + { + BOOL bOk = 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(); + } +} + + +BOOL ScChangeTrack::Accept( ScChangeAction* pAct ) +{ + if ( !pAct->IsClickable() ) + return FALSE; + + if ( pAct->IsDeleteType() || pAct->GetType() == SC_CAT_CONTENT ) + { + ScChangeActionTable aActionTable; + GetDependents( pAct, aActionTable, FALSE, TRUE ); + for ( ScChangeAction* p = aActionTable.First(); p; p = aActionTable.Next() ) + { + p->Accept(); + } + } + pAct->Accept(); + return TRUE; +} + + +BOOL ScChangeTrack::RejectAll() +{ + BOOL bOk = 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; +} + + +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 FALSE; + + ScChangeActionTable* pTable = NULL; + if ( pAct->HasDependent() ) + { + pTable = new ScChangeActionTable; + GetDependents( pAct, *pTable, FALSE, TRUE ); + } + BOOL bRejected = Reject( pAct, pTable, FALSE ); + if ( pTable ) + delete pTable; + return bRejected; +} + + +BOOL ScChangeTrack::Reject( ScChangeAction* pAct, ScChangeActionTable* pTable, + BOOL bRecursion ) +{ + if ( !pAct->IsInternalRejectable() ) + return FALSE; + + BOOL bOk = TRUE; + BOOL bRejected = 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, TRUE ); //! rekursiv + } + } + if ( bOk && (bRejected = pAct->Reject( pDoc )) != 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; + ULONG nRejectAction = pAct->GetActionNumber(); + BOOL bTabDel, bTabDelOk; + if ( pAct->GetType() == SC_CAT_DELETE_TABS ) + { + bTabDel = 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 = FALSE; + ScChangeActionDel* pDel = (ScChangeActionDel*) pAct; + if ( bOk ) + { + aDelRange = pDel->GetOverAllRange(); + bOk = aDelRange.IsValid( pDoc ); + } + BOOL bOneOk = 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; + BOOL bLoop = 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 = TRUE; + } + if ( pDel->IsBaseDelete() ) + bLoop = 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, TRUE ); //! rekursiv + } + } + if ( bOk && (bRejected = pAct->Reject( pDoc )) != 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 )) != 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; +} + + +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< 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< 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 |