/************************************************************** * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. * *************************************************************/ // MARKER(update_precomp.py): autogen include statement, do not remove #include "precompiled_sc.hxx" #include #include // SHL_CALC #include #include #include #include #include #include #include #include #include #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 #define SC_CHGTRACK_CXX #include "chgtrack.hxx" DECLARE_STACK( ScChangeActionStack, ScChangeAction* ) const sal_uInt16 nMemPoolChangeActionCellListEntry = (0x2000 - 64) / sizeof(ScChangeActionCellListEntry); IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionCellListEntry, nMemPoolChangeActionCellListEntry, nMemPoolChangeActionCellListEntry ) const sal_uInt16 nMemPoolChangeActionLinkEntry = (0x8000 - 64) / sizeof(ScChangeActionLinkEntry); IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionLinkEntry, nMemPoolChangeActionLinkEntry, nMemPoolChangeActionLinkEntry ) // loaded MSB > eigenes => inkompatibel #define SC_CHGTRACK_FILEFORMAT_FIRST 0x0001 #define SC_CHGTRACK_FILEFORMAT 0x0001 // --- ScChangeActionLinkEntry --------------------------------------------- #if DEBUG_CHANGETRACK String ScChangeActionLinkEntry::ToString() const { String aReturn; if ( pAction ) { aReturn = String::CreateFromInt64( static_cast< sal_Int64 >( pAction->GetActionNumber() ) ); } else if ( pLink && pLink->pAction ) { aReturn = String::CreateFromAscii( "*" ); aReturn += String::CreateFromInt64( static_cast< sal_Int64 >( pLink->pAction->GetActionNumber() ) ); } else { aReturn = String::CreateFromAscii( "-" ); } return aReturn; } #endif // DEBUG_CHANGETRACK // --- ScChangeAction ------------------------------------------------------ ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScRange& rRange ) : aBigRange( rRange ), pNext( NULL ), pPrev( NULL ), pLinkAny( NULL ), pLinkDeletedIn( NULL ), pLinkDeleted( NULL ), pLinkDependent( NULL ), nAction( 0 ), nRejectAction( 0 ), eType( eTypeP ), eState( SC_CAS_VIRGIN ) { aDateTime.ConvertToUTC(); } ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange, const sal_uLong nTempAction, const sal_uLong nTempRejectAction, const ScChangeActionState eTempState, const DateTime& aTempDateTime, const String& aTempUser, const String& aTempComment) : aBigRange( rRange ), aDateTime( aTempDateTime ), aUser( aTempUser ), aComment( aTempComment ), pNext( NULL ), pPrev( NULL ), pLinkAny( NULL ), pLinkDeletedIn( NULL ), pLinkDeleted( NULL ), pLinkDependent( NULL ), nAction( nTempAction ), nRejectAction( nTempRejectAction ), eType( eTypeP ), eState( eTempState ) { } ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, const ScBigRange& rRange, const sal_uLong nTempAction) : aBigRange( rRange ), pNext( NULL ), pPrev( NULL ), pLinkAny( NULL ), pLinkDeletedIn( NULL ), pLinkDeleted( NULL ), pLinkDependent( NULL ), nAction( nTempAction ), nRejectAction( 0 ), eType( eTypeP ), eState( SC_CAS_VIRGIN ) { aDateTime.ConvertToUTC(); } ScChangeAction::~ScChangeAction() { RemoveAllLinks(); } sal_Bool ScChangeAction::IsVisible() const { //! sequence order of execution is significant if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() ) return sal_False; if ( GetType() == SC_CAT_CONTENT ) return ((ScChangeActionContent*)this)->IsTopContent(); return sal_True; } sal_Bool ScChangeAction::IsTouchable() const { //! sequence order of execution is significant if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() ) return sal_False; // content may reject and be touchable if on top if ( GetType() == SC_CAT_CONTENT ) return ((ScChangeActionContent*)this)->IsTopContent(); if ( IsRejecting() ) return sal_False; return sal_True; } sal_Bool ScChangeAction::IsClickable() const { //! sequence order of execution is significant if ( !IsVirgin() ) return sal_False; if ( IsDeletedIn() ) return sal_False; if ( GetType() == SC_CAT_CONTENT ) { ScChangeActionContentCellType eCCT = ScChangeActionContent::GetContentCellType( ((ScChangeActionContent*)this)->GetNewCell() ); if ( eCCT == SC_CACCT_MATREF ) return sal_False; if ( eCCT == SC_CACCT_MATORG ) { // no Accept-Select if one of the references is in a deleted col/row const ScChangeActionLinkEntry* pL = ((ScChangeActionContent*)this)->GetFirstDependentEntry(); while ( pL ) { ScChangeAction* p = (ScChangeAction*) pL->GetAction(); if ( p && p->IsDeletedIn() ) return sal_False; pL = pL->GetNext(); } } return sal_True; // for Select() a content doesn't have to be touchable } return IsTouchable(); // Accept()/Reject() only on touchables } sal_Bool ScChangeAction::IsRejectable() const { //! sequence order of execution is significant if ( !IsClickable() ) return sal_False; if ( GetType() == SC_CAT_CONTENT ) { if ( ((ScChangeActionContent*)this)->IsOldMatrixReference() ) return sal_False; ScChangeActionContent* pNextContent = ((ScChangeActionContent*)this)->GetNextContent(); if ( pNextContent == NULL ) return sal_True; // *this is TopContent return pNextContent->IsRejected(); // *this is next rejectable } return IsTouchable(); } sal_Bool ScChangeAction::IsInternalRejectable() const { //! sequence order of execution is significant if ( !IsVirgin() ) return sal_False; if ( IsDeletedIn() ) return sal_False; if ( GetType() == SC_CAT_CONTENT ) { ScChangeActionContent* pNextContent = ((ScChangeActionContent*)this)->GetNextContent(); if ( pNextContent == NULL ) return sal_True; // *this is TopContent return pNextContent->IsRejected(); // *this is next rejectable } return IsTouchable(); } sal_Bool ScChangeAction::IsDialogRoot() const { return IsInternalRejectable(); // only rejectables in root } sal_Bool ScChangeAction::IsDialogParent() const { //! sequence order of execution is significant if ( GetType() == SC_CAT_CONTENT ) { if ( !IsDialogRoot() ) return sal_False; if ( ((ScChangeActionContent*)this)->IsMatrixOrigin() && HasDependent() ) return sal_True; ScChangeActionContent* pPrevContent = ((ScChangeActionContent*)this)->GetPrevContent(); return pPrevContent && pPrevContent->IsVirgin(); } if ( HasDependent() ) return IsDeleteType() ? sal_True : !IsDeletedIn(); if ( HasDeleted() ) { if ( IsDeleteType() ) { if ( IsDialogRoot() ) return sal_True; ScChangeActionLinkEntry* pL = pLinkDeleted; while ( pL ) { ScChangeAction* p = pL->GetAction(); if ( p && p->GetType() != eType ) return sal_True; pL = pL->GetNext(); } } else return sal_True; } return sal_False; } sal_Bool ScChangeAction::IsMasterDelete() const { if ( !IsDeleteType() ) return sal_False; ScChangeActionDel* pDel = (ScChangeActionDel*) this; return pDel->IsMultiDelete() && (pDel->IsTopDelete() || pDel->IsRejectable()); } void ScChangeAction::RemoveAllLinks() { RemoveAllAnyLinks(); RemoveAllDeletedIn(); RemoveAllDeleted(); RemoveAllDependent(); } void ScChangeAction::RemoveAllAnyLinks() { while ( pLinkAny ) delete pLinkAny; // rueckt sich selbst hoch } sal_Bool ScChangeAction::RemoveDeletedIn( const ScChangeAction* p ) { sal_Bool bRemoved = sal_False; ScChangeActionLinkEntry* pL = GetDeletedIn(); while ( pL ) { ScChangeActionLinkEntry* pNextLink = pL->GetNext(); if ( pL->GetAction() == p ) { delete pL; bRemoved = sal_True; } pL = pNextLink; } return bRemoved; } sal_Bool ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const { ScChangeActionLinkEntry* pL = GetDeletedIn(); while ( pL ) { if ( pL->GetAction() == p ) return sal_True; pL = pL->GetNext(); } return sal_False; } void ScChangeAction::RemoveAllDeletedIn() { //! nicht vom evtl. TopContent sondern wirklich dieser while ( pLinkDeletedIn ) delete pLinkDeletedIn; // rueckt sich selbst hoch } sal_Bool ScChangeAction::IsDeletedInDelType( ScChangeActionType eDelType ) const { ScChangeAction* p; ScChangeActionLinkEntry* pL = GetDeletedIn(); if ( pL ) { // InsertType fuer MergePrepare/MergeOwn ScChangeActionType eInsType; switch ( eDelType ) { case SC_CAT_DELETE_COLS : eInsType = SC_CAT_INSERT_COLS; break; case SC_CAT_DELETE_ROWS : eInsType = SC_CAT_INSERT_ROWS; break; case SC_CAT_DELETE_TABS : eInsType = SC_CAT_INSERT_TABS; break; default: eInsType = SC_CAT_NONE; } while ( pL ) { if ( (p = pL->GetAction()) != NULL && (p->GetType() == eDelType || p->GetType() == eInsType) ) return sal_True; pL = pL->GetNext(); } } return sal_False; } void ScChangeAction::SetDeletedIn( ScChangeAction* p ) { ScChangeActionLinkEntry* pLink1 = AddDeletedIn( p ); ScChangeActionLinkEntry* pLink2; if ( GetType() == SC_CAT_CONTENT ) pLink2 = p->AddDeleted( ((ScChangeActionContent*)this)->GetTopContent() ); else pLink2 = p->AddDeleted( this ); pLink1->SetLink( pLink2 ); } void ScChangeAction::RemoveAllDeleted() { while ( pLinkDeleted ) delete pLinkDeleted; // rueckt sich selbst hoch } void ScChangeAction::RemoveAllDependent() { while ( pLinkDependent ) delete pLinkDependent; // rueckt sich selbst hoch } DateTime ScChangeAction::GetDateTime() const { DateTime aDT( aDateTime ); aDT.ConvertToLocalTime(); return aDT; } void ScChangeAction::UpdateReference( const ScChangeTrack* /* pTrack */, UpdateRefMode eMode, const ScBigRange& rRange, sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz ) { ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() ); } void ScChangeAction::GetDescription( String& rStr, ScDocument* /* pDoc */, sal_Bool /* bSplitRange */, bool bWarning ) const { if ( IsRejecting() && bWarning ) { // #112261# Add comment if rejection may have resulted in references // not properly restored in formulas. See specification at // http://specs.openoffice.org/calc/ease-of-use/redlining_comment.sxw if (GetType() == SC_CAT_MOVE) { rStr += ScGlobal::GetRscString( STR_CHANGED_MOVE_REJECTION_WARNING); rStr += ' '; } else if (IsInsertType()) { rStr += ScGlobal::GetRscString( STR_CHANGED_DELETE_REJECTION_WARNING); rStr += ' '; } else { const ScChangeTrack* pCT = GetChangeTrack(); if (pCT) { ScChangeAction* pReject = pCT->GetActionOrGenerated( GetRejectAction()); if (pReject) { if (pReject->GetType() == SC_CAT_MOVE) { rStr += ScGlobal::GetRscString( STR_CHANGED_MOVE_REJECTION_WARNING); rStr += ' '; } else if (pReject->IsDeleteType()) { rStr += ScGlobal::GetRscString( STR_CHANGED_DELETE_REJECTION_WARNING); rStr += ' '; } else if (pReject->HasDependent()) { ScChangeActionTable aTable; pCT->GetDependents( pReject, aTable, sal_False, sal_True ); for ( const ScChangeAction* p = aTable.First(); p; p = aTable.Next() ) { if (p->GetType() == SC_CAT_MOVE) { rStr += ScGlobal::GetRscString( STR_CHANGED_MOVE_REJECTION_WARNING); rStr += ' '; break; // for } else if (pReject->IsDeleteType()) { rStr += ScGlobal::GetRscString( STR_CHANGED_DELETE_REJECTION_WARNING); rStr += ' '; break; // for } } } } } } } } String ScChangeAction::GetRefString( const ScBigRange& rRange, ScDocument* pDoc, sal_Bool bFlag3D ) const { String aStr; sal_uInt16 nFlags = ( rRange.IsValid( pDoc ) ? SCA_VALID : 0 ); if ( !nFlags ) aStr = ScGlobal::GetRscString( STR_NOREF_STR ); else { ScRange aTmpRange( rRange.MakeRange() ); switch ( GetType() ) { case SC_CAT_INSERT_COLS : case SC_CAT_DELETE_COLS : if ( bFlag3D ) { pDoc->GetName( aTmpRange.aStart.Tab(), aStr ); aStr += '.'; } aStr += ::ScColToAlpha( aTmpRange.aStart.Col() ); aStr += ':'; aStr += ::ScColToAlpha( aTmpRange.aEnd.Col() ); break; case SC_CAT_INSERT_ROWS : case SC_CAT_DELETE_ROWS : if ( bFlag3D ) { pDoc->GetName( aTmpRange.aStart.Tab(), aStr ); aStr += '.'; } aStr += String::CreateFromInt32( aTmpRange.aStart.Row() + 1 ); aStr += ':'; aStr += String::CreateFromInt32( aTmpRange.aEnd.Row() + 1 ); break; default: if ( bFlag3D || GetType() == SC_CAT_INSERT_TABS ) nFlags |= SCA_TAB_3D; aTmpRange.Format( aStr, nFlags, pDoc, pDoc->GetAddressConvention() ); } if ( (bFlag3D && IsDeleteType()) || IsDeletedIn() ) { aStr.Insert( '(', 0 ); aStr += ')'; } } return aStr; } void ScChangeAction::GetRefString( String& rStr, ScDocument* pDoc, sal_Bool bFlag3D ) const { rStr = GetRefString( GetBigRange(), pDoc, bFlag3D ); } void ScChangeAction::Accept() { if ( IsVirgin() ) { SetState( SC_CAS_ACCEPTED ); DeleteCellEntries(); } } void ScChangeAction::SetRejected() { if ( IsVirgin() ) { SetState( SC_CAS_REJECTED ); RemoveAllLinks(); DeleteCellEntries(); } } void ScChangeAction::RejectRestoreContents( ScChangeTrack* pTrack, SCsCOL nDx, SCsROW nDy ) { // Liste der Contents aufbauen ScChangeActionCellListEntry* pListContents = NULL; for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() ) { ScChangeAction* p = pL->GetAction(); if ( p && p->GetType() == SC_CAT_CONTENT ) { ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry( (ScChangeActionContent*) p, pListContents ); pListContents = pE; } } SetState( SC_CAS_REJECTED ); // vor UpdateReference fuer Move pTrack->UpdateReference( this, sal_True ); // LinkDeleted freigeben DBG_ASSERT( !pLinkDeleted, "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL" ); // Liste der Contents abarbeiten und loeschen ScDocument* pDoc = pTrack->GetDocument(); ScChangeActionCellListEntry* pE = pListContents; while ( pE ) { if ( !pE->pContent->IsDeletedIn() && pE->pContent->GetBigRange().aStart.IsValid( pDoc ) ) pE->pContent->PutNewValueToDoc( pDoc, nDx, nDy ); ScChangeActionCellListEntry* pNextEntry; pNextEntry = pE->pNext; delete pE; pE = pNextEntry; } DeleteCellEntries(); // weg mit den generierten } void ScChangeAction::SetDeletedInThis( sal_uLong nActionNumber, const ScChangeTrack* pTrack ) { if ( nActionNumber ) { ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber ); DBG_ASSERT( pAct, "ScChangeAction::SetDeletedInThis: missing Action" ); if ( pAct ) pAct->SetDeletedIn( this ); } } void ScChangeAction::AddDependent( sal_uLong nActionNumber, const ScChangeTrack* pTrack ) { if ( nActionNumber ) { ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber ); DBG_ASSERT( pAct, "ScChangeAction::AddDependent: missing Action" ); if ( pAct ) { ScChangeActionLinkEntry* pLink = AddDependent( pAct ); pAct->AddLink( this, pLink ); } } } #if DEBUG_CHANGETRACK String ScChangeAction::ToString( ScDocument* pDoc ) const { String aReturn; String aNumber = String::CreateFromInt64( static_cast< sal_Int64 >( GetActionNumber() ) ); String aActionState; ScChangeActionState eActionState = GetState(); switch ( eActionState ) { case SC_CAS_VIRGIN: { aActionState = String::CreateFromAscii( " " ); } break; case SC_CAS_ACCEPTED: { aActionState = String::CreateFromAscii( "+" ); } break; case SC_CAS_REJECTED: { aActionState = String::CreateFromAscii( "-" ); } break; } String aRejectAction; if ( IsRejecting() ) { aRejectAction += 'r'; aRejectAction += String::CreateFromInt64( static_cast< sal_Int64 >( GetRejectAction() ) ); } String aReference; GetRefString( aReference, pDoc, sal_True ); String aAuthor = GetUser(); DateTime aDT = GetDateTime(); String aDate = ScGlobal::pLocaleData->getDate( aDT ); aDate += ' '; aDate += ScGlobal::pLocaleData->getTime( aDT, sal_False, sal_False ); String aDescription; GetDescription( aDescription, pDoc ); String aLinkAny; const ScChangeActionLinkEntry* pLinkA = pLinkAny; while ( pLinkA ) { if ( !aLinkAny.Len() ) { aLinkAny = String::CreateFromAscii( "(Any:" ); } aLinkAny += String::CreateFromAscii( " ->" ); aLinkAny += pLinkA->ToString(); pLinkA = pLinkA->GetNext(); } if ( aLinkAny.Len() ) { aLinkAny += ')'; } String aLinkDeletedIn; const ScChangeActionLinkEntry* pLinkDI = pLinkDeletedIn; while ( pLinkDI ) { if ( !aLinkDeletedIn.Len() ) { aLinkDeletedIn = String::CreateFromAscii( "(DeletedIn:" ); } aLinkDeletedIn += String::CreateFromAscii( " ->" ); aLinkDeletedIn += pLinkDI->ToString(); pLinkDI = pLinkDI->GetNext(); } if ( aLinkDeletedIn.Len() ) { aLinkDeletedIn += ')'; } String aLinkDeleted; const ScChangeActionLinkEntry* pLinkD = pLinkDeleted; while ( pLinkD ) { if ( !aLinkDeleted.Len() ) { aLinkDeleted = String::CreateFromAscii( "(Deleted:" ); } aLinkDeleted += String::CreateFromAscii( " ->" ); aLinkDeleted += pLinkD->ToString(); pLinkD = pLinkD->GetNext(); } if ( aLinkDeleted.Len() ) { aLinkDeleted += ')'; } String aLinkDependent; const ScChangeActionLinkEntry* pLinkDp = pLinkDependent; while ( pLinkDp ) { if ( !aLinkDependent.Len() ) { aLinkDependent = String::CreateFromAscii( "(Dependent:" ); } aLinkDependent += String::CreateFromAscii( " ->" ); aLinkDependent += pLinkDp->ToString(); pLinkDp = pLinkDp->GetNext(); } if ( aLinkDependent.Len() ) { aLinkDependent += ')'; } aReturn += aNumber; aReturn += aActionState; aReturn += aRejectAction; aReturn += String::CreateFromAscii( ": " ); aReturn += aReference; aReturn += ' '; aReturn += aAuthor; aReturn += ' '; aReturn += aDate; aReturn += ' '; aReturn += aDescription; aReturn += ' '; aReturn += aLinkAny; aReturn += ' '; aReturn += aLinkDeletedIn; aReturn += ' '; aReturn += aLinkDeleted; aReturn += ' '; aReturn += aLinkDependent; return aReturn; } #endif // DEBUG_CHANGETRACK // --- ScChangeActionIns --------------------------------------------------- ScChangeActionIns::ScChangeActionIns( const ScRange& rRange ) : ScChangeAction( SC_CAT_NONE, rRange ) { if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL ) { aBigRange.aStart.SetCol( nInt32Min ); aBigRange.aEnd.SetCol( nInt32Max ); if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW ) { SetType( SC_CAT_INSERT_TABS ); aBigRange.aStart.SetRow( nInt32Min ); aBigRange.aEnd.SetRow( nInt32Max ); } else SetType( SC_CAT_INSERT_ROWS ); } else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW ) { SetType( SC_CAT_INSERT_COLS ); aBigRange.aStart.SetRow( nInt32Min ); aBigRange.aEnd.SetRow( nInt32Max ); } else { DBG_ERROR( "ScChangeActionIns: Block not supported!" ); } } ScChangeActionIns::ScChangeActionIns(const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment, const ScChangeActionType eTypeP) : ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment) { } ScChangeActionIns::~ScChangeActionIns() { } void ScChangeActionIns::GetDescription( String& rStr, ScDocument* pDoc, sal_Bool bSplitRange, bool bWarning ) const { ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); sal_uInt16 nWhatId; switch ( GetType() ) { case SC_CAT_INSERT_COLS : nWhatId = STR_COLUMN; break; case SC_CAT_INSERT_ROWS : nWhatId = STR_ROW; break; default: nWhatId = STR_AREA; } String aRsc( ScGlobal::GetRscString( STR_CHANGED_INSERT ) ); xub_StrLen nPos = aRsc.SearchAscii( "#1" ); rStr += aRsc.Copy( 0, nPos ); rStr += ScGlobal::GetRscString( nWhatId ); rStr += ' '; rStr += GetRefString( GetBigRange(), pDoc ); rStr += aRsc.Copy( nPos+2 ); } sal_Bool ScChangeActionIns::Reject( ScDocument* pDoc ) { if ( !aBigRange.IsValid( pDoc ) ) return sal_False; ScRange aRange( aBigRange.MakeRange() ); if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) ) return sal_False; switch ( GetType() ) { case SC_CAT_INSERT_COLS : pDoc->DeleteCol( aRange ); break; case SC_CAT_INSERT_ROWS : pDoc->DeleteRow( aRange ); break; case SC_CAT_INSERT_TABS : pDoc->DeleteTab( aRange.aStart.Tab() ); break; default: { // added to avoid warnings } } SetState( SC_CAS_REJECTED ); RemoveAllLinks(); return sal_True; } // --- ScChangeActionDel --------------------------------------------------- ScChangeActionDel::ScChangeActionDel( const ScRange& rRange, SCsCOL nDxP, SCsROW nDyP, ScChangeTrack* pTrackP ) : ScChangeAction( SC_CAT_NONE, rRange ), pTrack( pTrackP ), pFirstCell( NULL ), pCutOff( NULL ), nCutOff( 0 ), pLinkMove( NULL ), nDx( nDxP ), nDy( nDyP ) { if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == MAXCOL ) { aBigRange.aStart.SetCol( nInt32Min ); aBigRange.aEnd.SetCol( nInt32Max ); if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW ) { SetType( SC_CAT_DELETE_TABS ); aBigRange.aStart.SetRow( nInt32Min ); aBigRange.aEnd.SetRow( nInt32Max ); } else SetType( SC_CAT_DELETE_ROWS ); } else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == MAXROW ) { SetType( SC_CAT_DELETE_COLS ); aBigRange.aStart.SetRow( nInt32Min ); aBigRange.aEnd.SetRow( nInt32Max ); } else { DBG_ERROR( "ScChangeActionDel: Block not supported!" ); } } ScChangeActionDel::ScChangeActionDel(const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String &sComment, const ScChangeActionType eTypeP, const SCsCOLROW nD, ScChangeTrack* pTrackP) // which 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(nD); else if (eType == SC_CAT_DELETE_ROWS) nDy = static_cast(nD); } ScChangeActionDel::~ScChangeActionDel() { DeleteCellEntries(); while ( pLinkMove ) delete pLinkMove; } void ScChangeActionDel::AddContent( ScChangeActionContent* pContent ) { ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry( pContent, pFirstCell ); pFirstCell = pE; } void ScChangeActionDel::DeleteCellEntries() { pTrack->DeleteCellEntries( pFirstCell, this ); } sal_Bool ScChangeActionDel::IsBaseDelete() const { return !GetDx() && !GetDy(); } sal_Bool ScChangeActionDel::IsTopDelete() const { const ScChangeAction* p = GetNext(); if ( !p || p->GetType() != GetType() ) return sal_True; return ((ScChangeActionDel*)p)->IsBaseDelete(); } sal_Bool ScChangeActionDel::IsMultiDelete() const { if ( GetDx() || GetDy() ) return sal_True; const ScChangeAction* p = GetNext(); if ( !p || p->GetType() != GetType() ) return sal_False; const ScChangeActionDel* pDel = (const ScChangeActionDel*) p; if ( (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) && pDel->GetBigRange() == aBigRange ) return sal_True; return sal_False; } sal_Bool ScChangeActionDel::IsTabDeleteCol() const { if ( GetType() != SC_CAT_DELETE_COLS ) return sal_False; const ScChangeAction* p = this; while ( p && p->GetType() == SC_CAT_DELETE_COLS && !((const ScChangeActionDel*)p)->IsTopDelete() ) p = p->GetNext(); return p && p->GetType() == SC_CAT_DELETE_TABS; } void ScChangeActionDel::UpdateReference( const ScChangeTrack* /* pTrack */, UpdateRefMode eMode, const ScBigRange& rRange, sal_Int32 nDxP, sal_Int32 nDyP, sal_Int32 nDz ) { ScRefUpdate::Update( eMode, rRange, nDxP, nDyP, nDz, GetBigRange() ); if ( !IsDeletedIn() ) return ; // evtl. in "druntergerutschten" anpassen for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() ) { ScChangeAction* p = pL->GetAction(); if ( p && p->GetType() == SC_CAT_CONTENT && !GetBigRange().In( p->GetBigRange() ) ) { switch ( GetType() ) { case SC_CAT_DELETE_COLS : p->GetBigRange().aStart.SetCol( GetBigRange().aStart.Col() ); p->GetBigRange().aEnd.SetCol( GetBigRange().aStart.Col() ); break; case SC_CAT_DELETE_ROWS : p->GetBigRange().aStart.SetRow( GetBigRange().aStart.Row() ); p->GetBigRange().aEnd.SetRow( GetBigRange().aStart.Row() ); break; case SC_CAT_DELETE_TABS : p->GetBigRange().aStart.SetTab( GetBigRange().aStart.Tab() ); p->GetBigRange().aEnd.SetTab( GetBigRange().aStart.Tab() ); break; default: { // added to avoid warnings } } } } } ScBigRange ScChangeActionDel::GetOverAllRange() const { ScBigRange aTmpRange( GetBigRange() ); aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() ); aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() ); return aTmpRange; } void ScChangeActionDel::GetDescription( String& rStr, ScDocument* pDoc, sal_Bool bSplitRange, bool bWarning ) const { ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); sal_uInt16 nWhatId; switch ( GetType() ) { case SC_CAT_DELETE_COLS : nWhatId = STR_COLUMN; break; case SC_CAT_DELETE_ROWS : nWhatId = STR_ROW; break; default: nWhatId = STR_AREA; } ScBigRange aTmpRange( GetBigRange() ); if ( !IsRejected() ) { if ( bSplitRange ) { aTmpRange.aStart.SetCol( aTmpRange.aStart.Col() + GetDx() ); aTmpRange.aStart.SetRow( aTmpRange.aStart.Row() + GetDy() ); } aTmpRange.aEnd.SetCol( aTmpRange.aEnd.Col() + GetDx() ); aTmpRange.aEnd.SetRow( aTmpRange.aEnd.Row() + GetDy() ); } String aRsc( ScGlobal::GetRscString( STR_CHANGED_DELETE ) ); xub_StrLen nPos = aRsc.SearchAscii( "#1" ); rStr += aRsc.Copy( 0, nPos ); rStr += ScGlobal::GetRscString( nWhatId ); rStr += ' '; rStr += GetRefString( aTmpRange, pDoc ); rStr += aRsc.Copy( nPos+2 ); } sal_Bool ScChangeActionDel::Reject( ScDocument* pDoc ) { if ( !aBigRange.IsValid( pDoc ) && GetType() != SC_CAT_DELETE_TABS ) return sal_False; sal_Bool bOk = sal_True; if ( IsTopDelete() ) { // den kompletten Bereich in einem Rutsch restaurieren ScBigRange aTmpRange( GetOverAllRange() ); if ( !aTmpRange.IsValid( pDoc ) ) { if ( GetType() == SC_CAT_DELETE_TABS ) { // wird Tab angehaengt? if ( aTmpRange.aStart.Tab() > pDoc->GetMaxTableNumber() ) bOk = sal_False; } else bOk = sal_False; } if ( bOk ) { ScRange aRange( aTmpRange.MakeRange() ); // InDelete... fuer Formel UpdateReference in Document pTrack->SetInDeleteRange( aRange ); pTrack->SetInDeleteTop( sal_True ); pTrack->SetInDeleteUndo( sal_True ); pTrack->SetInDelete( sal_True ); switch ( GetType() ) { case SC_CAT_DELETE_COLS : if ( !(aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MAXCOL) ) { // nur wenn nicht TabDelete if ( ( bOk = pDoc->CanInsertCol( aRange ) ) != sal_False ) bOk = pDoc->InsertCol( aRange ); } break; case SC_CAT_DELETE_ROWS : if ( ( bOk = pDoc->CanInsertRow( aRange ) ) != sal_False ) bOk = pDoc->InsertRow( aRange ); break; case SC_CAT_DELETE_TABS : { //2do: Tabellennamen merken? String aName; pDoc->CreateValidTabName( aName ); if ( ( bOk = pDoc->ValidNewTabName( aName ) ) != sal_False ) bOk = pDoc->InsertTab( aRange.aStart.Tab(), aName ); } break; default: { // added to avoid warnings } } pTrack->SetInDelete( sal_False ); pTrack->SetInDeleteUndo( sal_False ); } if ( !bOk ) { pTrack->SetInDeleteTop( sal_False ); return sal_False; } // InDeleteTop fuer UpdateReference-Undo behalten } // setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries RejectRestoreContents( pTrack, GetDx(), GetDy() ); pTrack->SetInDeleteTop( sal_False ); RemoveAllLinks(); return sal_True; } void ScChangeActionDel::UndoCutOffMoves() { // abgeschnittene Moves wiederherstellen, Entries/Links deleten while ( pLinkMove ) { ScChangeActionMove* pMove = pLinkMove->GetMove(); short nFrom = pLinkMove->GetCutOffFrom(); short nTo = pLinkMove->GetCutOffTo(); switch ( GetType() ) { case SC_CAT_DELETE_COLS : if ( nFrom > 0 ) pMove->GetFromRange().aStart.IncCol( -nFrom ); else if ( nFrom < 0 ) pMove->GetFromRange().aEnd.IncCol( -nFrom ); if ( nTo > 0 ) pMove->GetBigRange().aStart.IncCol( -nTo ); else if ( nTo < 0 ) pMove->GetBigRange().aEnd.IncCol( -nTo ); break; case SC_CAT_DELETE_ROWS : if ( nFrom > 0 ) pMove->GetFromRange().aStart.IncRow( -nFrom ); else if ( nFrom < 0 ) pMove->GetFromRange().aEnd.IncRow( -nFrom ); if ( nTo > 0 ) pMove->GetBigRange().aStart.IncRow( -nTo ); else if ( nTo < 0 ) pMove->GetBigRange().aEnd.IncRow( -nTo ); break; case SC_CAT_DELETE_TABS : if ( nFrom > 0 ) pMove->GetFromRange().aStart.IncTab( -nFrom ); else if ( nFrom < 0 ) pMove->GetFromRange().aEnd.IncTab( -nFrom ); if ( nTo > 0 ) pMove->GetBigRange().aStart.IncTab( -nTo ); else if ( nTo < 0 ) pMove->GetBigRange().aEnd.IncTab( -nTo ); break; default: { // added to avoid warnings } } delete pLinkMove; // rueckt sich selbst hoch } } void ScChangeActionDel::UndoCutOffInsert() { // abgeschnittenes Insert wiederherstellen if ( pCutOff ) { switch ( pCutOff->GetType() ) { case SC_CAT_INSERT_COLS : if ( nCutOff < 0 ) pCutOff->GetBigRange().aEnd.IncCol( -nCutOff ); else pCutOff->GetBigRange().aStart.IncCol( -nCutOff ); break; case SC_CAT_INSERT_ROWS : if ( nCutOff < 0 ) pCutOff->GetBigRange().aEnd.IncRow( -nCutOff ); else pCutOff->GetBigRange().aStart.IncRow( -nCutOff ); break; case SC_CAT_INSERT_TABS : if ( nCutOff < 0 ) pCutOff->GetBigRange().aEnd.IncTab( -nCutOff ); else pCutOff->GetBigRange().aStart.IncTab( -nCutOff ); break; default: { // added to avoid warnings } } SetCutOffInsert( NULL, 0 ); } } // --- ScChangeActionMove -------------------------------------------------- ScChangeActionMove::ScChangeActionMove(const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber, const ScBigRange& aToBigRange, const String& aUserP, const DateTime& aDateTimeP, const String &sComment, const ScBigRange& aFromBigRange, ScChangeTrack* pTrackP) // which of nDx and nDy is set is depend on the type : ScChangeAction(SC_CAT_MOVE, aToBigRange, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment), aFromRange(aFromBigRange), pTrack( pTrackP ), pFirstCell( NULL ), nStartLastCut(0), nEndLastCut(0) { } ScChangeActionMove::~ScChangeActionMove() { DeleteCellEntries(); } void ScChangeActionMove::AddContent( ScChangeActionContent* pContent ) { ScChangeActionCellListEntry* pE = new ScChangeActionCellListEntry( pContent, pFirstCell ); pFirstCell = pE; } void ScChangeActionMove::DeleteCellEntries() { pTrack->DeleteCellEntries( pFirstCell, this ); } void ScChangeActionMove::UpdateReference( const ScChangeTrack* /* pTrack */, UpdateRefMode eMode, const ScBigRange& rRange, sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz ) { ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aFromRange ); ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, GetBigRange() ); } void ScChangeActionMove::GetDelta( sal_Int32& nDx, sal_Int32& nDy, sal_Int32& nDz ) const { const ScBigAddress& rToPos = GetBigRange().aStart; const ScBigAddress& rFromPos = GetFromRange().aStart; nDx = rToPos.Col() - rFromPos.Col(); nDy = rToPos.Row() - rFromPos.Row(); nDz = rToPos.Tab() - rFromPos.Tab(); } void ScChangeActionMove::GetDescription( String& rStr, ScDocument* pDoc, sal_Bool bSplitRange, bool bWarning ) const { ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); sal_Bool bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() ); String aRsc( ScGlobal::GetRscString( STR_CHANGED_MOVE ) ); xub_StrLen nPos = 0; String aTmpStr = ScChangeAction::GetRefString( GetFromRange(), pDoc, bFlag3D ); nPos = aRsc.SearchAscii( "#1", nPos ); aRsc.Erase( nPos, 2 ); aRsc.Insert( aTmpStr, nPos ); nPos = sal::static_int_cast( 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( nPos + aTmpStr.Len() ); rStr += aRsc; } void ScChangeActionMove::GetRefString( String& rStr, ScDocument* pDoc, sal_Bool bFlag3D ) const { if ( !bFlag3D ) bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() ); rStr = ScChangeAction::GetRefString( GetFromRange(), pDoc, bFlag3D ); rStr += ','; rStr += ' '; rStr += ScChangeAction::GetRefString( GetBigRange(), pDoc, bFlag3D ); } sal_Bool ScChangeActionMove::Reject( ScDocument* pDoc ) { if ( !(aBigRange.IsValid( pDoc ) && aFromRange.IsValid( pDoc )) ) return sal_False; ScRange aToRange( aBigRange.MakeRange() ); ScRange aFrmRange( aFromRange.MakeRange() ); sal_Bool bOk = pDoc->IsBlockEditable( aToRange.aStart.Tab(), aToRange.aStart.Col(), aToRange.aStart.Row(), aToRange.aEnd.Col(), aToRange.aEnd.Row() ); if ( bOk ) bOk = pDoc->IsBlockEditable( aFrmRange.aStart.Tab(), aFrmRange.aStart.Col(), aFrmRange.aStart.Row(), aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row() ); if ( !bOk ) return sal_False; pTrack->LookUpContents( aToRange, pDoc, 0, 0, 0 ); // zu movende Contents pDoc->DeleteAreaTab( aToRange, IDF_ALL ); pDoc->DeleteAreaTab( aFrmRange, IDF_ALL ); // Formeln im Dokument anpassen pDoc->UpdateReference( URM_MOVE, aFrmRange.aStart.Col(), aFrmRange.aStart.Row(), aFrmRange.aStart.Tab(), aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row(), aFrmRange.aEnd.Tab(), (SCsCOL) aFrmRange.aStart.Col() - aToRange.aStart.Col(), (SCsROW) aFrmRange.aStart.Row() - aToRange.aStart.Row(), (SCsTAB) aFrmRange.aStart.Tab() - aToRange.aStart.Tab(), NULL ); // LinkDependent freigeben, nachfolgendes UpdateReference-Undo setzt // ToRange->FromRange Dependents RemoveAllDependent(); // setzt rejected und ruft UpdateReference-Undo und DeleteCellEntries RejectRestoreContents( pTrack, 0, 0 ); while ( pLinkDependent ) { ScChangeAction* p = pLinkDependent->GetAction(); if ( p && p->GetType() == SC_CAT_CONTENT ) { ScChangeActionContent* pContent = (ScChangeActionContent*) p; if ( !pContent->IsDeletedIn() && pContent->GetBigRange().aStart.IsValid( pDoc ) ) pContent->PutNewValueToDoc( pDoc, 0, 0 ); // in LookUpContents generierte loeschen if ( pTrack->IsGenerated( pContent->GetActionNumber() ) && !pContent->IsDeletedIn() ) { pLinkDependent->UnLink(); //! sonst wird der mitgeloescht pTrack->DeleteGeneratedDelContent( pContent ); } } delete pLinkDependent; } RemoveAllLinks(); return sal_True; } // --- ScChangeActionContent ----------------------------------------------- const sal_uInt16 nMemPoolChangeActionContent = (0x8000 - 64) / sizeof(ScChangeActionContent); IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeActionContent, nMemPoolChangeActionContent, nMemPoolChangeActionContent ) ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment, ScBaseCell* pTempOldCell, ScDocument* pDoc, const String& sOldValue ) : ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment), aOldValue(sOldValue), pOldCell(pTempOldCell), pNewCell(NULL), pNextContent(NULL), pPrevContent(NULL), pNextInSlot(NULL), ppPrevInSlot(NULL) { if (pOldCell) ScChangeActionContent::SetCell( aOldValue, pOldCell, 0, pDoc ); if ( sOldValue.Len() ) // #i40704# don't overwrite SetCell result with empty string aOldValue = sOldValue; // set again, because SetCell removes it } ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber, ScBaseCell* pTempNewCell, const ScBigRange& aBigRangeP, ScDocument* pDoc, const String& sNewValue ) : ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber), aNewValue(sNewValue), pOldCell(NULL), pNewCell(pTempNewCell), pNextContent(NULL), pPrevContent(NULL), pNextInSlot(NULL), ppPrevInSlot(NULL) { if (pNewCell) ScChangeActionContent::SetCell( aNewValue, pNewCell, 0, pDoc ); if ( sNewValue.Len() ) // #i40704# don't overwrite SetCell result with empty string aNewValue = sNewValue; // set again, because SetCell removes it } ScChangeActionContent::~ScChangeActionContent() { ClearTrack(); } void ScChangeActionContent::ClearTrack() { RemoveFromSlot(); if ( pPrevContent ) pPrevContent->pNextContent = pNextContent; if ( pNextContent ) pNextContent->pPrevContent = pPrevContent; } ScChangeActionContent* ScChangeActionContent::GetTopContent() const { if ( pNextContent ) { ScChangeActionContent* pContent = pNextContent; while ( pContent->pNextContent && pContent != pContent->pNextContent ) pContent = pContent->pNextContent; return pContent; } return (ScChangeActionContent*) this; } ScChangeActionLinkEntry* ScChangeActionContent::GetDeletedIn() const { if ( pNextContent ) return GetTopContent()->pLinkDeletedIn; return pLinkDeletedIn; } ScChangeActionLinkEntry** ScChangeActionContent::GetDeletedInAddress() { if ( pNextContent ) return GetTopContent()->GetDeletedInAddress(); return &pLinkDeletedIn; } void ScChangeActionContent::SetOldValue( const ScBaseCell* pCell, const ScDocument* pFromDoc, ScDocument* pToDoc, sal_uLong nFormat ) { ScChangeActionContent::SetValue( aOldValue, pOldCell, nFormat, pCell, pFromDoc, pToDoc ); } void ScChangeActionContent::SetOldValue( const ScBaseCell* pCell, const ScDocument* pFromDoc, ScDocument* pToDoc ) { ScChangeActionContent::SetValue( aOldValue, pOldCell, aBigRange.aStart.MakeAddress(), pCell, pFromDoc, pToDoc ); } void ScChangeActionContent::SetNewValue( const ScBaseCell* pCell, ScDocument* pDoc ) { ScChangeActionContent::SetValue( aNewValue, pNewCell, aBigRange.aStart.MakeAddress(), pCell, pDoc, pDoc ); } void ScChangeActionContent::SetOldNewCells( ScBaseCell* pOldCellP, sal_uLong nOldFormat, ScBaseCell* pNewCellP, sal_uLong nNewFormat, ScDocument* pDoc ) { pOldCell = pOldCellP; pNewCell = pNewCellP; ScChangeActionContent::SetCell( aOldValue, pOldCell, nOldFormat, pDoc ); ScChangeActionContent::SetCell( aNewValue, pNewCell, nNewFormat, pDoc ); } void ScChangeActionContent::SetNewCell( ScBaseCell* pCell, ScDocument* pDoc, const String& rFormatted ) { DBG_ASSERT( !pNewCell, "ScChangeActionContent::SetNewCell: overwriting existing cell" ); pNewCell = pCell; ScChangeActionContent::SetCell( aNewValue, pNewCell, 0, pDoc ); // #i40704# allow to set formatted text here - don't call SetNewValue with String from XML filter if ( rFormatted.Len() ) aNewValue = rFormatted; } void ScChangeActionContent::SetValueString( String& rValue, ScBaseCell*& pCell, const String& rStr, ScDocument* pDoc ) { if ( pCell ) { pCell->Delete(); pCell = NULL; } if ( rStr.Len() > 1 && rStr.GetChar(0) == '=' ) { rValue.Erase(); pCell = new ScFormulaCell( pDoc, aBigRange.aStart.MakeAddress(), rStr, formula::FormulaGrammar::GRAM_DEFAULT, formula::FormulaGrammar::CONV_OOO ); ((ScFormulaCell*)pCell)->SetInChangeTrack( sal_True ); } else rValue = rStr; } void ScChangeActionContent::SetOldValue( const String& rOld, ScDocument* pDoc ) { SetValueString( aOldValue, pOldCell, rOld, pDoc ); } void ScChangeActionContent::SetNewValue( const String& rNew, ScDocument* pDoc ) { SetValueString( aNewValue, pNewCell, rNew, pDoc ); } void ScChangeActionContent::GetOldString( String& rStr ) const { GetValueString( rStr, aOldValue, pOldCell ); } void ScChangeActionContent::GetNewString( String& rStr ) const { GetValueString( rStr, aNewValue, pNewCell ); } void ScChangeActionContent::GetDescription( String& rStr, ScDocument* pDoc, sal_Bool bSplitRange, bool bWarning ) const { ScChangeAction::GetDescription( rStr, pDoc, bSplitRange, bWarning ); String aRsc( ScGlobal::GetRscString( STR_CHANGED_CELL ) ); String aTmpStr; GetRefString( aTmpStr, pDoc ); xub_StrLen nPos = 0; nPos = aRsc.SearchAscii( "#1", nPos ); aRsc.Erase( nPos, 2 ); aRsc.Insert( aTmpStr, nPos ); nPos = sal::static_int_cast( 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( nPos + aTmpStr.Len() ); GetNewString( aTmpStr ); if ( !aTmpStr.Len() ) aTmpStr = ScGlobal::GetRscString( STR_CHANGED_BLANK ); nPos = aRsc.SearchAscii( "#3", nPos ); aRsc.Erase( nPos, 2 ); aRsc.Insert( aTmpStr, nPos ); rStr += aRsc; } void ScChangeActionContent::GetRefString( String& rStr, ScDocument* pDoc, sal_Bool bFlag3D ) const { sal_uInt16 nFlags = ( GetBigRange().IsValid( pDoc ) ? SCA_VALID : 0 ); if ( nFlags ) { const ScBaseCell* pCell = GetNewCell(); if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATORG ) { ScBigRange aLocalBigRange( GetBigRange() ); SCCOL nC; SCROW nR; ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR ); aLocalBigRange.aEnd.IncCol( nC-1 ); aLocalBigRange.aEnd.IncRow( nR-1 ); rStr = ScChangeAction::GetRefString( aLocalBigRange, pDoc, bFlag3D ); return ; } ScAddress aTmpAddress( GetBigRange().aStart.MakeAddress() ); if ( bFlag3D ) nFlags |= SCA_TAB_3D; aTmpAddress.Format( rStr, nFlags, pDoc, pDoc->GetAddressConvention() ); if ( IsDeletedIn() ) { rStr.Insert( '(', 0 ); rStr += ')'; } } else rStr = ScGlobal::GetRscString( STR_NOREF_STR ); } sal_Bool ScChangeActionContent::Reject( ScDocument* pDoc ) { if ( !aBigRange.IsValid( pDoc ) ) return sal_False; PutOldValueToDoc( pDoc, 0, 0 ); SetState( SC_CAS_REJECTED ); RemoveAllLinks(); return sal_True; } sal_Bool ScChangeActionContent::Select( ScDocument* pDoc, ScChangeTrack* pTrack, sal_Bool bOldest, Stack* pRejectActions ) { if ( !aBigRange.IsValid( pDoc ) ) return sal_False; ScChangeActionContent* pContent = this; // accept previous contents while ( ( pContent = pContent->pPrevContent ) != NULL ) { if ( pContent->IsVirgin() ) pContent->SetState( SC_CAS_ACCEPTED ); } ScChangeActionContent* pEnd = pContent = this; // reject subsequent contents while ( ( pContent = pContent->pNextContent ) != NULL ) { // MatrixOrigin may have dependents, no dependency recursion needed const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry(); while ( pL ) { ScChangeAction* p = (ScChangeAction*) pL->GetAction(); if ( p ) p->SetRejected(); pL = pL->GetNext(); } pContent->SetRejected(); pEnd = pContent; } if ( bOldest || pEnd != this ) { // wenn nicht aeltester: ist es ueberhaupt ein anderer als der letzte? ScRange aRange( aBigRange.aStart.MakeAddress() ); const ScAddress& rPos = aRange.aStart; ScChangeActionContent* pNew = new ScChangeActionContent( aRange ); pNew->SetOldValue( pDoc->GetCell( rPos ), pDoc, pDoc ); if ( bOldest ) PutOldValueToDoc( pDoc, 0, 0 ); else PutNewValueToDoc( pDoc, 0, 0 ); pNew->SetRejectAction( bOldest ? GetActionNumber() : pEnd->GetActionNumber() ); pNew->SetState( SC_CAS_ACCEPTED ); if ( pRejectActions ) pRejectActions->Push( pNew ); else { pNew->SetNewValue( pDoc->GetCell( rPos ), pDoc ); pTrack->Append( pNew ); } } if ( bOldest ) SetRejected(); else SetState( SC_CAS_ACCEPTED ); return sal_True; } // static void ScChangeActionContent::GetStringOfCell( String& rStr, const ScBaseCell* pCell, const ScDocument* pDoc, const ScAddress& rPos ) { if ( pCell ) { if ( ScChangeActionContent::NeedsNumberFormat( pCell ) ) GetStringOfCell( rStr, pCell, pDoc, pDoc->GetNumberFormat( rPos ) ); else GetStringOfCell( rStr, pCell, pDoc, 0 ); } else rStr.Erase(); } // static void ScChangeActionContent::GetStringOfCell( String& rStr, const ScBaseCell* pCell, const ScDocument* pDoc, sal_uLong nFormat ) { if ( ScChangeActionContent::GetContentCellType( pCell ) ) { switch ( pCell->GetCellType() ) { case CELLTYPE_VALUE : { double nValue = ((ScValueCell*)pCell)->GetValue(); pDoc->GetFormatTable()->GetInputLineString( nValue, nFormat, rStr ); } break; case CELLTYPE_STRING : ((ScStringCell*)pCell)->GetString( rStr ); break; case CELLTYPE_EDIT : ((ScEditCell*)pCell)->GetString( rStr ); break; case CELLTYPE_FORMULA : ((ScFormulaCell*)pCell)->GetFormula( rStr ); break; default: rStr.Erase(); } } else rStr.Erase(); } // static ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScBaseCell* pCell ) { if ( pCell ) { switch ( pCell->GetCellType() ) { case CELLTYPE_VALUE : case CELLTYPE_STRING : case CELLTYPE_EDIT : return SC_CACCT_NORMAL; //break; case CELLTYPE_FORMULA : switch ( ((const ScFormulaCell*)pCell)->GetMatrixFlag() ) { case MM_NONE : return SC_CACCT_NORMAL; //break; case MM_FORMULA : case MM_FAKE : return SC_CACCT_MATORG; //break; case MM_REFERENCE : return SC_CACCT_MATREF; //break; } return SC_CACCT_NORMAL; //break; default: return SC_CACCT_NONE; } } return SC_CACCT_NONE; } // static sal_Bool ScChangeActionContent::NeedsNumberFormat( const ScBaseCell* pCell ) { return pCell && pCell->GetCellType() == CELLTYPE_VALUE; } // static void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell, const ScAddress& rPos, const ScBaseCell* pOrgCell, const ScDocument* pFromDoc, ScDocument* pToDoc ) { sal_uLong nFormat = NeedsNumberFormat( pOrgCell ) ? pFromDoc->GetNumberFormat( rPos ) : 0; SetValue( rStr, pCell, nFormat, pOrgCell, pFromDoc, pToDoc ); } // static void ScChangeActionContent::SetValue( String& rStr, ScBaseCell*& pCell, sal_uLong nFormat, const ScBaseCell* pOrgCell, const ScDocument* pFromDoc, ScDocument* pToDoc ) { rStr.Erase(); if ( pCell ) pCell->Delete(); if ( ScChangeActionContent::GetContentCellType( pOrgCell ) ) { pCell = pOrgCell->CloneWithoutNote( *pToDoc ); switch ( pOrgCell->GetCellType() ) { case CELLTYPE_VALUE : { // z.B. Datum auch als solches merken double nValue = ((ScValueCell*)pOrgCell)->GetValue(); pFromDoc->GetFormatTable()->GetInputLineString( nValue, nFormat, rStr ); } break; case CELLTYPE_FORMULA : ((ScFormulaCell*)pCell)->SetInChangeTrack( sal_True ); break; default: { // added to avoid warnings } } } else pCell = NULL; } // static void ScChangeActionContent::SetCell( String& rStr, ScBaseCell* pCell, sal_uLong nFormat, const ScDocument* pDoc ) { rStr.Erase(); if ( pCell ) { switch ( pCell->GetCellType() ) { case CELLTYPE_VALUE : { // e.g. remember date as date string double nValue = ((ScValueCell*)pCell)->GetValue(); pDoc->GetFormatTable()->GetInputLineString( nValue, nFormat, rStr ); } break; case CELLTYPE_FORMULA : ((ScFormulaCell*)pCell)->SetInChangeTrack( sal_True ); break; default: { // added to avoid warnings } } } } void ScChangeActionContent::GetValueString( String& rStr, const String& rValue, const ScBaseCell* pCell ) const { if ( !rValue.Len() ) { if ( pCell ) { switch ( pCell->GetCellType() ) { case CELLTYPE_STRING : ((ScStringCell*)pCell)->GetString( rStr ); break; case CELLTYPE_EDIT : ((ScEditCell*)pCell)->GetString( rStr ); break; case CELLTYPE_VALUE : // ist immer in rValue rStr = rValue; break; case CELLTYPE_FORMULA : GetFormulaString( rStr, (ScFormulaCell*) pCell ); break; default: { // added to avoid warnings } } } else rStr.Erase(); } else rStr = rValue; } void ScChangeActionContent::GetFormulaString( String& rStr, const ScFormulaCell* pCell ) const { ScAddress aPos( aBigRange.aStart.MakeAddress() ); if ( aPos == pCell->aPos || IsDeletedIn() ) pCell->GetFormula( rStr ); else { DBG_ERROR( "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos" ); ScFormulaCell* pNew = new ScFormulaCell( *pCell, *pCell->GetDocument(), aPos ); pNew->GetFormula( rStr ); delete pNew; } } void ScChangeActionContent::PutOldValueToDoc( ScDocument* pDoc, SCsCOL nDx, SCsROW nDy ) const { PutValueToDoc( pOldCell, aOldValue, pDoc, nDx, nDy ); } void ScChangeActionContent::PutNewValueToDoc( ScDocument* pDoc, SCsCOL nDx, SCsROW nDy ) const { PutValueToDoc( pNewCell, aNewValue, pDoc, nDx, nDy ); } void ScChangeActionContent::PutValueToDoc( ScBaseCell* pCell, const String& rValue, ScDocument* pDoc, SCsCOL nDx, SCsROW nDy ) const { ScAddress aPos( aBigRange.aStart.MakeAddress() ); if ( nDx ) aPos.IncCol( nDx ); if ( nDy ) aPos.IncRow( nDy ); if ( !rValue.Len() ) { if ( pCell ) { switch ( pCell->GetCellType() ) { case CELLTYPE_VALUE : // ist immer in rValue pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue ); break; default: switch ( ScChangeActionContent::GetContentCellType( pCell ) ) { case SC_CACCT_MATORG : { SCCOL nC; SCROW nR; ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR ); DBG_ASSERT( nC>0 && nR>0, "ScChangeActionContent::PutValueToDoc: MatColsRows?" ); ScRange aRange( aPos ); if ( nC > 1 ) aRange.aEnd.IncCol( nC-1 ); if ( nR > 1 ) aRange.aEnd.IncRow( nR-1 ); ScMarkData aDestMark; aDestMark.SelectOneTable( aPos.Tab() ); aDestMark.SetMarkArea( aRange ); pDoc->InsertMatrixFormula( aPos.Col(), aPos.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), aDestMark, EMPTY_STRING, ((const ScFormulaCell*)pCell)->GetCode() ); } break; case SC_CACCT_MATREF : // nothing break; default: pDoc->PutCell( aPos, pCell->CloneWithoutNote( *pDoc ) ); } } } else pDoc->PutCell( aPos, NULL ); } else pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue ); } void lcl_InvalidateReference( ScToken& rTok, const ScBigAddress& rPos ) { ScSingleRefData& rRef1 = rTok.GetSingleRef(); if ( rPos.Col() < 0 || MAXCOL < rPos.Col() ) { rRef1.nCol = SCCOL_MAX; rRef1.nRelCol = SCCOL_MAX; rRef1.SetColDeleted( sal_True ); } if ( rPos.Row() < 0 || MAXROW < rPos.Row() ) { rRef1.nRow = SCROW_MAX; rRef1.nRelRow = SCROW_MAX; rRef1.SetRowDeleted( sal_True ); } if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() ) { rRef1.nTab = SCTAB_MAX; rRef1.nRelTab = SCTAB_MAX; rRef1.SetTabDeleted( sal_True ); } if ( rTok.GetType() == formula::svDoubleRef ) { ScSingleRefData& rRef2 = rTok.GetDoubleRef().Ref2; if ( rPos.Col() < 0 || MAXCOL < rPos.Col() ) { rRef2.nCol = SCCOL_MAX; rRef2.nRelCol = SCCOL_MAX; rRef2.SetColDeleted( sal_True ); } if ( rPos.Row() < 0 || MAXROW < rPos.Row() ) { rRef2.nRow = SCROW_MAX; rRef2.nRelRow = SCROW_MAX; rRef2.SetRowDeleted( sal_True ); } if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() ) { rRef2.nTab = SCTAB_MAX; rRef2.nRelTab = SCTAB_MAX; rRef2.SetTabDeleted( sal_True ); } } } void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack, UpdateRefMode eMode, const ScBigRange& rRange, sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz ) { SCSIZE nOldSlot = ScChangeTrack::ComputeContentSlot( aBigRange.aStart.Row() ); ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aBigRange ); SCSIZE nNewSlot = ScChangeTrack::ComputeContentSlot( aBigRange.aStart.Row() ); if ( nNewSlot != nOldSlot ) { RemoveFromSlot(); InsertInSlot( &(pTrack->GetContentSlots()[nNewSlot]) ); } if ( pTrack->IsInDelete() && !pTrack->IsInDeleteTop() ) return ; // Formeln nur kompletten Bereich updaten sal_Bool bOldFormula = ( pOldCell && pOldCell->GetCellType() == CELLTYPE_FORMULA ); sal_Bool bNewFormula = ( pNewCell && pNewCell->GetCellType() == CELLTYPE_FORMULA ); if ( bOldFormula || bNewFormula ) { // via ScFormulaCell UpdateReference anpassen (dort) if ( pTrack->IsInDelete() ) { const ScRange& rDelRange = pTrack->GetInDeleteRange(); if ( nDx > 0 ) nDx = rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1; else if ( nDx < 0 ) nDx = -(rDelRange.aEnd.Col() - rDelRange.aStart.Col() + 1); if ( nDy > 0 ) nDy = rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1; else if ( nDy < 0 ) nDy = -(rDelRange.aEnd.Row() - rDelRange.aStart.Row() + 1); if ( nDz > 0 ) nDz = rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1; else if ( nDz < 0 ) nDz = -(rDelRange.aEnd.Tab() - rDelRange.aStart.Tab() + 1); } ScBigRange aTmpRange( rRange ); switch ( eMode ) { case URM_INSDEL : if ( nDx < 0 || nDy < 0 || nDz < 0 ) { // Delete startet dort hinter geloeschtem Bereich, // Position wird dort angepasst. if ( nDx ) aTmpRange.aStart.IncCol( -nDx ); if ( nDy ) aTmpRange.aStart.IncRow( -nDy ); if ( nDz ) aTmpRange.aStart.IncTab( -nDz ); } break; case URM_MOVE : // Move ist hier Quelle, dort Ziel, // Position muss vorher angepasst sein. if ( bOldFormula ) ((ScFormulaCell*)pOldCell)->aPos = aBigRange.aStart.MakeAddress(); if ( bNewFormula ) ((ScFormulaCell*)pNewCell)->aPos = aBigRange.aStart.MakeAddress(); if ( nDx ) { aTmpRange.aStart.IncCol( nDx ); aTmpRange.aEnd.IncCol( nDx ); } if ( nDy ) { aTmpRange.aStart.IncRow( nDy ); aTmpRange.aEnd.IncRow( nDy ); } if ( nDz ) { aTmpRange.aStart.IncTab( nDz ); aTmpRange.aEnd.IncTab( nDz ); } break; default: { // added to avoid warnings } } ScRange aRange( aTmpRange.MakeRange() ); if ( bOldFormula ) ((ScFormulaCell*)pOldCell)->UpdateReference( eMode, aRange, (SCsCOL) nDx, (SCsROW) nDy, (SCsTAB) nDz, NULL ); if ( bNewFormula ) ((ScFormulaCell*)pNewCell)->UpdateReference( eMode, aRange, (SCsCOL) nDx, (SCsROW) nDy, (SCsTAB) nDz, NULL ); if ( !aBigRange.aStart.IsValid( pTrack->GetDocument() ) ) { //! HACK! //! UpdateReference kann nicht mit Positionen ausserhalb des //! Dokuments umgehen, deswegen alles auf #REF! setzen //2do: make it possible! das bedeutet grossen Umbau von ScAddress etc.! const ScBigAddress& rPos = aBigRange.aStart; if ( bOldFormula ) { ScToken* t; ScTokenArray* pArr = ((ScFormulaCell*)pOldCell)->GetCode(); pArr->Reset(); while ( ( t = static_cast(pArr->GetNextReference()) ) != NULL ) lcl_InvalidateReference( *t, rPos ); pArr->Reset(); while ( ( t = static_cast(pArr->GetNextReferenceRPN()) ) != NULL ) lcl_InvalidateReference( *t, rPos ); } if ( bNewFormula ) { ScToken* t; ScTokenArray* pArr = ((ScFormulaCell*)pNewCell)->GetCode(); pArr->Reset(); while ( ( t = static_cast(pArr->GetNextReference()) ) != NULL ) lcl_InvalidateReference( *t, rPos ); pArr->Reset(); while ( ( t = static_cast(pArr->GetNextReferenceRPN()) ) != NULL ) lcl_InvalidateReference( *t, rPos ); } } } } // --- ScChangeActionReject ------------------------------------------------ ScChangeActionReject::ScChangeActionReject(const sal_uLong nActionNumber, const ScChangeActionState eStateP, const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP, const String& aUserP, const DateTime& aDateTimeP, const String& sComment) : ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment) { } // --- ScChangeTrack ------------------------------------------------------- IMPL_FIXEDMEMPOOL_NEWDEL( ScChangeTrackMsgInfo, 16, 16 ) const SCROW ScChangeTrack::nContentRowsPerSlot = InitContentRowsPerSlot(); const SCSIZE ScChangeTrack::nContentSlots = (MAXROWCOUNT) / InitContentRowsPerSlot() + 2; // static SCROW ScChangeTrack::InitContentRowsPerSlot() { const SCSIZE nMaxSlots = 0xffe0 / sizeof( ScChangeActionContent* ) - 2; SCROW nRowsPerSlot = (MAXROWCOUNT) / nMaxSlots; if ( nRowsPerSlot * nMaxSlots < sal::static_int_cast(MAXROWCOUNT) ) ++nRowsPerSlot; return nRowsPerSlot; } ScChangeTrack::ScChangeTrack( ScDocument* pDocP ) : pDoc( pDocP ) { Init(); SC_MOD()->GetUserOptions().AddListener(this); ppContentSlots = new ScChangeActionContent* [ nContentSlots ]; memset( ppContentSlots, 0, nContentSlots * sizeof( ScChangeActionContent* ) ); } ScChangeTrack::ScChangeTrack( ScDocument* pDocP, const ScStrCollection& aTempUserCollection) : aUserCollection(aTempUserCollection), pDoc( pDocP ) { Init(); SC_MOD()->GetUserOptions().AddListener(this); ppContentSlots = new ScChangeActionContent* [ nContentSlots ]; memset( ppContentSlots, 0, nContentSlots * sizeof( ScChangeActionContent* ) ); } ScChangeTrack::~ScChangeTrack() { SC_MOD()->GetUserOptions().RemoveListener(this); DtorClear(); delete [] ppContentSlots; } void ScChangeTrack::Init() { pFirst = NULL; pLast = NULL; pFirstGeneratedDelContent = NULL; pLastCutMove = NULL; pLinkInsertCol = NULL; pLinkInsertRow = NULL; pLinkInsertTab = NULL; pLinkMove = NULL; pBlockModifyMsg = NULL; nActionMax = 0; nGeneratedMin = SC_CHGTRACK_GENERATED_START; nMarkLastSaved = 0; nStartLastCut = 0; nEndLastCut = 0; nLastMerge = 0; eMergeState = SC_CTMS_NONE; nLoadedFileFormatVersion = SC_CHGTRACK_FILEFORMAT; bLoadSave = sal_False; bInDelete = sal_False; bInDeleteTop = sal_False; bInDeleteUndo = sal_False; bInPasteCut = sal_False; bUseFixDateTime = sal_False; bTime100thSeconds = sal_True; const SvtUserOptions& rUserOpt = SC_MOD()->GetUserOptions(); aUser = rUserOpt.GetFirstName(); aUser += ' '; aUser += (String)rUserOpt.GetLastName(); aUserCollection.Insert( new StrData( aUser ) ); } void ScChangeTrack::DtorClear() { ScChangeAction* p; ScChangeAction* pNext; for ( p = GetFirst(); p; p = pNext ) { pNext = p->GetNext(); delete p; } for ( p = pFirstGeneratedDelContent; p; p = pNext ) { pNext = p->GetNext(); delete p; } for ( p = aPasteCutTable.First(); p; p = aPasteCutTable.Next() ) { delete p; } delete pLastCutMove; ClearMsgQueue(); } void ScChangeTrack::ClearMsgQueue() { if ( pBlockModifyMsg ) { delete pBlockModifyMsg; pBlockModifyMsg = NULL; } ScChangeTrackMsgInfo* pMsgInfo; while ( ( pMsgInfo = aMsgStackTmp.Pop() ) != NULL ) delete pMsgInfo; while ( ( pMsgInfo = aMsgStackFinal.Pop() ) != NULL ) delete pMsgInfo; while ( ( pMsgInfo = aMsgQueue.Get() ) != NULL ) delete pMsgInfo; } void ScChangeTrack::Clear() { DtorClear(); aTable.Clear(); aGeneratedTable.Clear(); aPasteCutTable.Clear(); aUserCollection.FreeAll(); aUser.Erase(); Init(); } void __EXPORT ScChangeTrack::ConfigurationChanged( utl::ConfigurationBroadcaster*, sal_uInt32 ) { if ( !pDoc->IsInDtorClear() ) { const SvtUserOptions& rUserOptions = SC_MOD()->GetUserOptions(); sal_uInt16 nOldCount = aUserCollection.GetCount(); String aStr( rUserOptions.GetFirstName() ); aStr += ' '; aStr += (String)rUserOptions.GetLastName(); SetUser( aStr ); if ( aUserCollection.GetCount() != nOldCount ) { // New user in collection -> have to repaint because // colors may be different now (#106697#). // (Has to be done in the Notify handler, to be sure // the user collection has already been updated) SfxObjectShell* pDocSh = pDoc->GetDocumentShell(); if (pDocSh) pDocSh->Broadcast( ScPaintHint( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB), PAINT_GRID ) ); } } } void ScChangeTrack::SetUser( const String& rUser ) { if ( IsLoadSave() ) return ; // nicht die Collection zerschiessen aUser = rUser; StrData* pStrData = new StrData( aUser ); if ( !aUserCollection.Insert( pStrData ) ) delete pStrData; } void ScChangeTrack::StartBlockModify( ScChangeTrackMsgType eMsgType, sal_uLong nStartAction ) { if ( aModifiedLink.IsSet() ) { if ( pBlockModifyMsg ) aMsgStackTmp.Push( pBlockModifyMsg ); // Block im Block pBlockModifyMsg = new ScChangeTrackMsgInfo; pBlockModifyMsg->eMsgType = eMsgType; pBlockModifyMsg->nStartAction = nStartAction; } } void ScChangeTrack::EndBlockModify( sal_uLong nEndAction ) { if ( aModifiedLink.IsSet() ) { if ( pBlockModifyMsg ) { if ( pBlockModifyMsg->nStartAction <= nEndAction ) { pBlockModifyMsg->nEndAction = nEndAction; // Blocks in Blocks aufgeloest aMsgStackFinal.Push( pBlockModifyMsg ); } else delete pBlockModifyMsg; pBlockModifyMsg = aMsgStackTmp.Pop(); // evtl. Block im Block } if ( !pBlockModifyMsg ) { sal_Bool bNew = sal_False; ScChangeTrackMsgInfo* pMsg; while ( ( pMsg = aMsgStackFinal.Pop() ) != NULL ) { aMsgQueue.Put( pMsg ); bNew = sal_True; } if ( bNew ) aModifiedLink.Call( this ); } } } void ScChangeTrack::NotifyModified( ScChangeTrackMsgType eMsgType, sal_uLong nStartAction, sal_uLong nEndAction ) { if ( aModifiedLink.IsSet() ) { if ( !pBlockModifyMsg || pBlockModifyMsg->eMsgType != eMsgType || (IsGenerated( nStartAction ) && (eMsgType == SC_CTM_APPEND || eMsgType == SC_CTM_REMOVE)) ) { // Append innerhalb von Append z.B. nicht StartBlockModify( eMsgType, nStartAction ); EndBlockModify( nEndAction ); } } } void ScChangeTrack::MasterLinks( ScChangeAction* pAppend ) { ScChangeActionType eType = pAppend->GetType(); if ( eType == SC_CAT_CONTENT ) { if ( !IsGenerated( pAppend->GetActionNumber() ) ) { SCSIZE nSlot = ComputeContentSlot( pAppend->GetBigRange().aStart.Row() ); ((ScChangeActionContent*)pAppend)->InsertInSlot( &ppContentSlots[nSlot] ); } return ; } if ( pAppend->IsRejecting() ) return ; // Rejects haben keine Abhaengigkeiten switch ( eType ) { case SC_CAT_INSERT_COLS : { ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry( &pLinkInsertCol, pAppend ); pAppend->AddLink( NULL, pLink ); } break; case SC_CAT_INSERT_ROWS : { ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry( &pLinkInsertRow, pAppend ); pAppend->AddLink( NULL, pLink ); } break; case SC_CAT_INSERT_TABS : { ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry( &pLinkInsertTab, pAppend ); pAppend->AddLink( NULL, pLink ); } break; case SC_CAT_MOVE : { ScChangeActionLinkEntry* pLink = new ScChangeActionLinkEntry( &pLinkMove, pAppend ); pAppend->AddLink( NULL, pLink ); } break; default: { // added to avoid warnings } } } void ScChangeTrack::AppendLoaded( ScChangeAction* pAppend ) { aTable.Insert( pAppend->GetActionNumber(), pAppend ); if ( !pLast ) pFirst = pLast = pAppend; else { pLast->pNext = pAppend; pAppend->pPrev = pLast; pLast = pAppend; } MasterLinks( pAppend ); } void ScChangeTrack::Append( ScChangeAction* pAppend, sal_uLong nAction ) { if ( nActionMax < nAction ) nActionMax = nAction; pAppend->SetUser( aUser ); if ( bUseFixDateTime ) pAppend->SetDateTimeUTC( aFixDateTime ); pAppend->SetActionNumber( nAction ); aTable.Insert( nAction, pAppend ); // UpdateReference Inserts vor Dependencies. // Delete rejectendes Insert hatte UpdateReference mit Delete-Undo. // UpdateReference auch wenn pLast==NULL, weil pAppend ein Delete sein // kann, dass DelContents generiert haben kann if ( pAppend->IsInsertType() && !pAppend->IsRejecting() ) UpdateReference( pAppend, sal_False ); if ( !pLast ) pFirst = pLast = pAppend; else { pLast->pNext = pAppend; pAppend->pPrev = pLast; pLast = pAppend; Dependencies( pAppend ); } // UpdateReference Inserts nicht nach Dependencies. // Move rejectendes Move hatte UpdateReference mit Move-Undo, Inhalt in // ToRange nicht deleten. if ( !pAppend->IsInsertType() && !(pAppend->GetType() == SC_CAT_MOVE && pAppend->IsRejecting()) ) UpdateReference( pAppend, sal_False ); MasterLinks( pAppend ); if ( aModifiedLink.IsSet() ) { NotifyModified( SC_CTM_APPEND, nAction, nAction ); if ( pAppend->GetType() == SC_CAT_CONTENT ) { ScChangeActionContent* pContent = (ScChangeActionContent*) pAppend; if ( ( pContent = pContent->GetPrevContent() ) != NULL ) { sal_uLong nMod = pContent->GetActionNumber(); NotifyModified( SC_CTM_CHANGE, nMod, nMod ); } } else NotifyModified( SC_CTM_CHANGE, pFirst->GetActionNumber(), pLast->GetActionNumber() ); } } void ScChangeTrack::Append( ScChangeAction* pAppend ) { Append( pAppend, ++nActionMax ); } void ScChangeTrack::AppendDeleteRange( const ScRange& rRange, ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction, SCsTAB nDz ) { nStartAction = GetActionMax() + 1; AppendDeleteRange( rRange, pRefDoc, nDz, 0 ); nEndAction = GetActionMax(); } void ScChangeTrack::AppendDeleteRange( const ScRange& rRange, ScDocument* pRefDoc, SCsTAB nDz, sal_uLong nRejectingInsert ) { SetInDeleteRange( rRange ); StartBlockModify( SC_CTM_APPEND, GetActionMax() + 1 ); SCCOL nCol1; SCROW nRow1; SCTAB nTab1; SCCOL nCol2; SCROW nRow2; SCTAB nTab2; rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ ) { if ( !pRefDoc || nTab < pRefDoc->GetTableCount() ) { if ( nCol1 == 0 && nCol2 == MAXCOL ) { // ganze Zeilen und/oder Tabellen if ( nRow1 == 0 && nRow2 == MAXROW ) { // ganze Tabellen //2do: geht nicht auch komplette Tabelle als ganzes? ScRange aRange( 0, 0, nTab, 0, MAXROW, nTab ); for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) { // spaltenweise ist weniger als zeilenweise aRange.aStart.SetCol( nCol ); aRange.aEnd.SetCol( nCol ); if ( nCol == nCol2 ) SetInDeleteTop( sal_True ); AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0, nTab-nTab1 + nDz, nRejectingInsert ); } //! immer noch InDeleteTop AppendOneDeleteRange( rRange, pRefDoc, 0, 0, nTab-nTab1 + nDz, nRejectingInsert ); } else { // ganze Zeilen ScRange aRange( 0, 0, nTab, MAXCOL, 0, nTab ); for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ ) { aRange.aStart.SetRow( nRow ); aRange.aEnd.SetRow( nRow ); if ( nRow == nRow2 ) SetInDeleteTop( sal_True ); AppendOneDeleteRange( aRange, pRefDoc, 0, nRow-nRow1, 0, nRejectingInsert ); } } } else if ( nRow1 == 0 && nRow2 == MAXROW ) { // ganze Spalten ScRange aRange( 0, 0, nTab, 0, MAXROW, nTab ); for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) { aRange.aStart.SetCol( nCol ); aRange.aEnd.SetCol( nCol ); if ( nCol == nCol2 ) SetInDeleteTop( sal_True ); AppendOneDeleteRange( aRange, pRefDoc, nCol-nCol1, 0, 0, nRejectingInsert ); } } else { DBG_ERROR( "ScChangeTrack::AppendDeleteRange: Block not supported!" ); } SetInDeleteTop( sal_False ); } } EndBlockModify( GetActionMax() ); } void ScChangeTrack::AppendOneDeleteRange( const ScRange& rOrgRange, ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz, sal_uLong nRejectingInsert ) { ScRange aTrackRange( rOrgRange ); if ( nDx ) { aTrackRange.aStart.IncCol( -nDx ); aTrackRange.aEnd.IncCol( -nDx ); } if ( nDy ) { aTrackRange.aStart.IncRow( -nDy ); aTrackRange.aEnd.IncRow( -nDy ); } if ( nDz ) { aTrackRange.aStart.IncTab( -nDz ); aTrackRange.aEnd.IncTab( -nDz ); } ScChangeActionDel* pAct = new ScChangeActionDel( aTrackRange, nDx, nDy, this ); // TabDelete keine Contents, sind in einzelnen Spalten if ( !(rOrgRange.aStart.Col() == 0 && rOrgRange.aStart.Row() == 0 && rOrgRange.aEnd.Col() == MAXCOL && rOrgRange.aEnd.Row() == MAXROW) ) LookUpContents( rOrgRange, pRefDoc, -nDx, -nDy, -nDz ); if ( nRejectingInsert ) { pAct->SetRejectAction( nRejectingInsert ); pAct->SetState( SC_CAS_ACCEPTED ); } Append( pAct ); } void ScChangeTrack::LookUpContents( const ScRange& rOrgRange, ScDocument* pRefDoc, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) { if ( pRefDoc ) { ScAddress aPos; ScBigAddress aBigPos; ScCellIterator aIter( pRefDoc, rOrgRange ); ScBaseCell* pCell = aIter.GetFirst(); while ( pCell ) { if ( ScChangeActionContent::GetContentCellType( pCell ) ) { aBigPos.Set( aIter.GetCol() + nDx, aIter.GetRow() + nDy, aIter.GetTab() + nDz ); ScChangeActionContent* pContent = SearchContentAt( aBigPos, NULL ); if ( !pContent ) { // nicht getrackte Contents aPos.Set( aIter.GetCol() + nDx, aIter.GetRow() + nDy, aIter.GetTab() + nDz ); GenerateDelContent( aPos, pCell, pRefDoc ); //! der Content wird hier _nicht_ per AddContent hinzugefuegt, //! sondern in UpdateReference, um z.B. auch kreuzende Deletes //! korrekt zu erfassen } } pCell = aIter.GetNext(); } } } void ScChangeTrack::AppendMove( const ScRange& rFromRange, const ScRange& rToRange, ScDocument* pRefDoc ) { ScChangeActionMove* pAct = new ScChangeActionMove( rFromRange, rToRange, this ); LookUpContents( rToRange, pRefDoc, 0, 0, 0 ); // ueberschriebene Contents Append( pAct ); } // static sal_Bool ScChangeTrack::IsMatrixFormulaRangeDifferent( const ScBaseCell* pOldCell, const ScBaseCell* pNewCell ) { SCCOL nC1, nC2; SCROW nR1, nR2; nC1 = nC2 = 0; nR1 = nR2 = 0; if ( pOldCell && (pOldCell->GetCellType() == CELLTYPE_FORMULA) && ((const ScFormulaCell*)pOldCell)->GetMatrixFlag() == MM_FORMULA ) ((const ScFormulaCell*)pOldCell)->GetMatColsRows( nC1, nR1 ); if ( pNewCell && (pNewCell->GetCellType() == CELLTYPE_FORMULA) && ((const ScFormulaCell*)pNewCell)->GetMatrixFlag() == MM_FORMULA ) ((const ScFormulaCell*)pNewCell)->GetMatColsRows( nC1, nR1 ); return nC1 != nC2 || nR1 != nR2; } void ScChangeTrack::AppendContent( const ScAddress& rPos, const String& rNewValue, ScBaseCell* pOldCell ) { String aOldValue; ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pDoc, rPos ); if ( aOldValue != rNewValue || IsMatrixFormulaRangeDifferent( pOldCell, NULL ) ) { // nur wirkliche Aenderung tracken ScRange aRange( rPos ); ScChangeActionContent* pAct = new ScChangeActionContent( aRange ); pAct->SetOldValue( pOldCell, pDoc, pDoc ); pAct->SetNewValue( rNewValue, pDoc ); Append( pAct ); } } void ScChangeTrack::AppendContent( const ScAddress& rPos, const ScBaseCell* pOldCell, sal_uLong nOldFormat, ScDocument* pRefDoc ) { if ( !pRefDoc ) pRefDoc = pDoc; String aOldValue; ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pRefDoc, nOldFormat ); String aNewValue; ScBaseCell* pNewCell = pDoc->GetCell( rPos ); ScChangeActionContent::GetStringOfCell( aNewValue, pNewCell, pDoc, rPos ); if ( aOldValue != aNewValue || IsMatrixFormulaRangeDifferent( pOldCell, pNewCell ) ) { // nur wirkliche Aenderung tracken ScRange aRange( rPos ); ScChangeActionContent* pAct = new ScChangeActionContent( aRange ); pAct->SetOldValue( pOldCell, pRefDoc, pDoc, nOldFormat ); pAct->SetNewValue( pNewCell, pDoc ); Append( pAct ); } } void ScChangeTrack::AppendContent( const ScAddress& rPos, ScDocument* pRefDoc ) { String aOldValue; ScBaseCell* pOldCell = pRefDoc->GetCell( rPos ); ScChangeActionContent::GetStringOfCell( aOldValue, pOldCell, pRefDoc, rPos ); String aNewValue; ScBaseCell* pNewCell = pDoc->GetCell( rPos ); ScChangeActionContent::GetStringOfCell( aNewValue, pNewCell, pDoc, rPos ); if ( aOldValue != aNewValue || IsMatrixFormulaRangeDifferent( pOldCell, pNewCell ) ) { // nur wirkliche Aenderung tracken ScRange aRange( rPos ); ScChangeActionContent* pAct = new ScChangeActionContent( aRange ); pAct->SetOldValue( pOldCell, pRefDoc, pDoc ); pAct->SetNewValue( pNewCell, pDoc ); Append( pAct ); } } void ScChangeTrack::AppendContent( const ScAddress& rPos, const ScBaseCell* pOldCell ) { if ( ScChangeActionContent::NeedsNumberFormat( pOldCell ) ) AppendContent( rPos, pOldCell, pDoc->GetNumberFormat( rPos ), pDoc ); else AppendContent( rPos, pOldCell, 0, pDoc ); } void ScChangeTrack::SetLastCutMoveRange( const ScRange& rRange, ScDocument* pRefDoc ) { if ( pLastCutMove ) { // ToRange nicht mit Deletes linken und nicht in der Groesse aendern, // eigentlich unnoetig, da ein Delete vorher in // ScViewFunc::PasteFromClip ein ResetLastCut ausloest ScBigRange& r = pLastCutMove->GetBigRange(); r.aEnd.SetCol( -1 ); r.aEnd.SetRow( -1 ); r.aEnd.SetTab( -1 ); r.aStart.SetCol( -1 - (rRange.aEnd.Col() - rRange.aStart.Col()) ); r.aStart.SetRow( -1 - (rRange.aEnd.Row() - rRange.aStart.Row()) ); r.aStart.SetTab( -1 - (rRange.aEnd.Tab() - rRange.aStart.Tab()) ); // zu ueberschreibende Contents im FromRange LookUpContents( rRange, pRefDoc, 0, 0, 0 ); } } void ScChangeTrack::AppendContentRange( const ScRange& rRange, ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction, ScChangeActionClipMode eClipMode ) { if ( eClipMode == SC_CACM_CUT ) { ResetLastCut(); pLastCutMove = new ScChangeActionMove( rRange, rRange, this ); SetLastCutMoveRange( rRange, pRefDoc ); } SCCOL nCol1; SCROW nRow1; SCTAB nTab1; SCCOL nCol2; SCROW nRow2; SCTAB nTab2; rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); sal_Bool bDoContents; if ( eClipMode == SC_CACM_PASTE && HasLastCut() ) { bDoContents = sal_False; SetInPasteCut( sal_True ); // Paste und Cut abstimmen, Paste kann groesserer Range sein ScRange aRange( rRange ); ScBigRange& r = pLastCutMove->GetBigRange(); SCCOL nTmpCol; if ( (nTmpCol = (SCCOL) (r.aEnd.Col() - r.aStart.Col())) != (nCol2 - nCol1) ) { aRange.aEnd.SetCol( aRange.aStart.Col() + nTmpCol ); nCol1 += nTmpCol + 1; bDoContents = sal_True; } SCROW nTmpRow; if ( (nTmpRow = (SCROW) (r.aEnd.Row() - r.aStart.Row())) != (nRow2 - nRow1) ) { aRange.aEnd.SetRow( aRange.aStart.Row() + nTmpRow ); nRow1 += nTmpRow + 1; bDoContents = sal_True; } SCTAB nTmpTab; if ( (nTmpTab = (SCTAB) (r.aEnd.Tab() - r.aStart.Tab())) != (nTab2 - nTab1) ) { aRange.aEnd.SetTab( aRange.aStart.Tab() + nTmpTab ); nTab1 += nTmpTab + 1; bDoContents = sal_True; } r = aRange; Undo( nStartLastCut, nEndLastCut ); // hier werden sich die Cuts gemerkt //! StartAction erst nach Undo nStartAction = GetActionMax() + 1; StartBlockModify( SC_CTM_APPEND, nStartAction ); // zu ueberschreibende Contents im ToRange LookUpContents( aRange, pRefDoc, 0, 0, 0 ); pLastCutMove->SetStartLastCut( nStartLastCut ); pLastCutMove->SetEndLastCut( nEndLastCut ); Append( pLastCutMove ); pLastCutMove = NULL; ResetLastCut(); SetInPasteCut( sal_False ); } else { bDoContents = sal_True; nStartAction = GetActionMax() + 1; StartBlockModify( SC_CTM_APPEND, nStartAction ); } if ( bDoContents ) { ScAddress aPos; for ( SCTAB nTab = nTab1; nTab <= nTab2; nTab++ ) { aPos.SetTab( nTab ); for ( SCCOL nCol = nCol1; nCol <= nCol2; nCol++ ) { aPos.SetCol( nCol ); for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ ) { aPos.SetRow( nRow ); AppendContent( aPos, pRefDoc ); } } } } nEndAction = GetActionMax(); EndBlockModify( nEndAction ); if ( eClipMode == SC_CACM_CUT ) { nStartLastCut = nStartAction; nEndLastCut = nEndAction; } } void ScChangeTrack::AppendContentsIfInRefDoc( ScDocument* pRefDoc, sal_uLong& nStartAction, sal_uLong& nEndAction ) { ScDocumentIterator aIter( pRefDoc, 0, MAXTAB ); if ( aIter.GetFirst() ) { nStartAction = GetActionMax() + 1; StartBlockModify( SC_CTM_APPEND, nStartAction ); SvNumberFormatter* pFormatter = pRefDoc->GetFormatTable(); do { SCCOL nCol; SCROW nRow; SCTAB nTab; aIter.GetPos( nCol, nRow, nTab ); ScAddress aPos( nCol, nRow, nTab ); AppendContent( aPos, aIter.GetCell(), aIter.GetPattern()->GetNumberFormat( pFormatter ), pRefDoc ); } while ( aIter.GetNext() ); nEndAction = GetActionMax(); EndBlockModify( nEndAction ); } else nStartAction = nEndAction = 0; } ScChangeActionContent* ScChangeTrack::AppendContentOnTheFly( const ScAddress& rPos, ScBaseCell* pOldCell, ScBaseCell* pNewCell, sal_uLong nOldFormat, sal_uLong nNewFormat ) { ScRange aRange( rPos ); ScChangeActionContent* pAct = new ScChangeActionContent( aRange ); pAct->SetOldNewCells( pOldCell, nOldFormat, pNewCell, nNewFormat, pDoc ); Append( pAct ); return pAct; } void ScChangeTrack::AppendInsert( const ScRange& rRange ) { ScChangeActionIns* pAct = new ScChangeActionIns( rRange ); Append( pAct ); } void ScChangeTrack::DeleteCellEntries( ScChangeActionCellListEntry*& pCellList, ScChangeAction* pDeletor ) { ScChangeActionCellListEntry* pE = pCellList; while ( pE ) { ScChangeActionCellListEntry* pNext = pE->pNext; pE->pContent->RemoveDeletedIn( pDeletor ); if ( IsGenerated( pE->pContent->GetActionNumber() ) && !pE->pContent->IsDeletedIn() ) DeleteGeneratedDelContent( pE->pContent ); delete pE; pE = pNext; } pCellList = NULL; } ScChangeActionContent* ScChangeTrack::GenerateDelContent( const ScAddress& rPos, const ScBaseCell* pCell, const ScDocument* pFromDoc ) { ScChangeActionContent* pContent = new ScChangeActionContent( ScRange( rPos ) ); pContent->SetActionNumber( --nGeneratedMin ); // nur NewValue ScChangeActionContent::SetValue( pContent->aNewValue, pContent->pNewCell, rPos, pCell, pFromDoc, pDoc ); // pNextContent und pPrevContent werden nicht gesetzt if ( pFirstGeneratedDelContent ) { // vorne reinhaengen pFirstGeneratedDelContent->pPrev = pContent; pContent->pNext = pFirstGeneratedDelContent; } pFirstGeneratedDelContent = pContent; aGeneratedTable.Insert( nGeneratedMin, pContent ); NotifyModified( SC_CTM_APPEND, nGeneratedMin, nGeneratedMin ); return pContent; } void ScChangeTrack::DeleteGeneratedDelContent( ScChangeActionContent* pContent ) { sal_uLong nAct = pContent->GetActionNumber(); aGeneratedTable.Remove( nAct ); if ( pFirstGeneratedDelContent == pContent ) pFirstGeneratedDelContent = (ScChangeActionContent*) pContent->pNext; if ( pContent->pNext ) pContent->pNext->pPrev = pContent->pPrev; if ( pContent->pPrev ) pContent->pPrev->pNext = pContent->pNext; delete pContent; NotifyModified( SC_CTM_REMOVE, nAct, nAct ); if ( nAct == nGeneratedMin ) ++nGeneratedMin; //! erst nach NotifyModified wg. IsGenerated } ScChangeActionContent* ScChangeTrack::SearchContentAt( const ScBigAddress& rPos, ScChangeAction* pButNotThis ) const { SCSIZE nSlot = ComputeContentSlot( rPos.Row() ); for ( ScChangeActionContent* p = ppContentSlots[nSlot]; p; p = p->GetNextInSlot() ) { if ( p != pButNotThis && !p->IsDeletedIn() && p->GetBigRange().aStart == rPos ) { ScChangeActionContent* pContent = p->GetTopContent(); if ( !pContent->IsDeletedIn() ) return pContent; } } return NULL; } void ScChangeTrack::AddDependentWithNotify( ScChangeAction* pParent, ScChangeAction* pDependent ) { ScChangeActionLinkEntry* pLink = pParent->AddDependent( pDependent ); pDependent->AddLink( pParent, pLink ); if ( aModifiedLink.IsSet() ) { sal_uLong nMod = pParent->GetActionNumber(); NotifyModified( SC_CTM_PARENT, nMod, nMod ); } } void ScChangeTrack::Dependencies( ScChangeAction* pAct ) { // Finde die letzte Abhaengigkeit fuer jeweils Col/Row/Tab. // Content an gleicher Position verketten. // Move Abhaengigkeiten. ScChangeActionType eActType = pAct->GetType(); if ( eActType == SC_CAT_REJECT || (eActType == SC_CAT_MOVE && pAct->IsRejecting()) ) return ; // diese Rejects sind nicht abhaengig if ( eActType == SC_CAT_CONTENT ) { if ( !(((ScChangeActionContent*)pAct)->GetNextContent() || ((ScChangeActionContent*)pAct)->GetPrevContent()) ) { // Contents an gleicher Position verketten ScChangeActionContent* pContent = SearchContentAt( pAct->GetBigRange().aStart, pAct ); if ( pContent ) { pContent->SetNextContent( (ScChangeActionContent*) pAct ); ((ScChangeActionContent*)pAct)->SetPrevContent( pContent ); } } const ScBaseCell* pCell = ((ScChangeActionContent*)pAct)->GetNewCell(); if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATREF ) { ScAddress aOrg; ((const ScFormulaCell*)pCell)->GetMatrixOrigin( aOrg ); ScChangeActionContent* pContent = SearchContentAt( aOrg, pAct ); if ( pContent && pContent->IsMatrixOrigin() ) { AddDependentWithNotify( pContent, pAct ); } else { DBG_ERRORFILE( "ScChangeTrack::Dependencies: MatOrg not found" ); } } } if ( !(pLinkInsertCol || pLinkInsertRow || pLinkInsertTab || pLinkMove) ) return ; // keine Dependencies if ( pAct->IsRejecting() ) return ; // ausser Content keine Dependencies // Insert in einem entsprechenden Insert haengt davon ab, sonst muesste // der vorherige Insert gesplittet werden. // Sich kreuzende Inserts und Deletes sind nicht abhaengig. // Alles andere ist abhaengig. // Der zuletzt eingelinkte Insert steht am Anfang einer Kette, // also genau richtig const ScBigRange& rRange = pAct->GetBigRange(); sal_Bool bActNoInsert = !pAct->IsInsertType(); sal_Bool bActColDel = ( eActType == SC_CAT_DELETE_COLS ); sal_Bool bActRowDel = ( eActType == SC_CAT_DELETE_ROWS ); sal_Bool bActTabDel = ( eActType == SC_CAT_DELETE_TABS ); if ( pLinkInsertCol && (eActType == SC_CAT_INSERT_COLS || (bActNoInsert && !bActRowDel && !bActTabDel)) ) { for ( ScChangeActionLinkEntry* pL = pLinkInsertCol; pL; pL = pL->GetNext() ) { ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction(); if ( !pTest->IsRejected() && pTest->GetBigRange().Intersects( rRange ) ) { AddDependentWithNotify( pTest, pAct ); break; // for } } } if ( pLinkInsertRow && (eActType == SC_CAT_INSERT_ROWS || (bActNoInsert && !bActColDel && !bActTabDel)) ) { for ( ScChangeActionLinkEntry* pL = pLinkInsertRow; pL; pL = pL->GetNext() ) { ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction(); if ( !pTest->IsRejected() && pTest->GetBigRange().Intersects( rRange ) ) { AddDependentWithNotify( pTest, pAct ); break; // for } } } if ( pLinkInsertTab && (eActType == SC_CAT_INSERT_TABS || (bActNoInsert && !bActColDel && !bActRowDel)) ) { for ( ScChangeActionLinkEntry* pL = pLinkInsertTab; pL; pL = pL->GetNext() ) { ScChangeActionIns* pTest = (ScChangeActionIns*) pL->GetAction(); if ( !pTest->IsRejected() && pTest->GetBigRange().Intersects( rRange ) ) { AddDependentWithNotify( pTest, pAct ); break; // for } } } if ( pLinkMove ) { if ( eActType == SC_CAT_CONTENT ) { // Content ist von FromRange abhaengig const ScBigAddress& rPos = rRange.aStart; for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() ) { ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction(); if ( !pTest->IsRejected() && pTest->GetFromRange().In( rPos ) ) { AddDependentWithNotify( pTest, pAct ); } } } else if ( eActType == SC_CAT_MOVE ) { // Move FromRange ist von ToRange abhaengig const ScBigRange& rFromRange = ((ScChangeActionMove*)pAct)->GetFromRange(); for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() ) { ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction(); if ( !pTest->IsRejected() && pTest->GetBigRange().Intersects( rFromRange ) ) { AddDependentWithNotify( pTest, pAct ); } } } else { // Inserts und Deletes sind abhaengig, sobald sie FromRange oder // ToRange kreuzen for ( ScChangeActionLinkEntry* pL = pLinkMove; pL; pL = pL->GetNext() ) { ScChangeActionMove* pTest = (ScChangeActionMove*) pL->GetAction(); if ( !pTest->IsRejected() && (pTest->GetFromRange().Intersects( rRange ) || pTest->GetBigRange().Intersects( rRange )) ) { AddDependentWithNotify( pTest, pAct ); } } } } } void ScChangeTrack::Remove( ScChangeAction* pRemove ) { // aus Track ausklinken sal_uLong nAct = pRemove->GetActionNumber(); aTable.Remove( nAct ); if ( nAct == nActionMax ) --nActionMax; if ( pRemove == pLast ) pLast = pRemove->pPrev; if ( pRemove == pFirst ) pFirst = pRemove->pNext; if ( nAct == nMarkLastSaved ) nMarkLastSaved = ( pRemove->pPrev ? pRemove->pPrev->GetActionNumber() : 0 ); // aus der globalen Kette ausklinken if ( pRemove->pNext ) pRemove->pNext->pPrev = pRemove->pPrev; if ( pRemove->pPrev ) pRemove->pPrev->pNext = pRemove->pNext; // Dependencies nicht loeschen, passiert on delete automatisch durch // LinkEntry, ohne Listen abzuklappern if ( aModifiedLink.IsSet() ) { NotifyModified( SC_CTM_REMOVE, nAct, nAct ); if ( pRemove->GetType() == SC_CAT_CONTENT ) { ScChangeActionContent* pContent = (ScChangeActionContent*) pRemove; if ( ( pContent = pContent->GetPrevContent() ) != NULL ) { sal_uLong nMod = pContent->GetActionNumber(); NotifyModified( SC_CTM_CHANGE, nMod, nMod ); } } else if ( pLast ) NotifyModified( SC_CTM_CHANGE, pFirst->GetActionNumber(), pLast->GetActionNumber() ); } if ( IsInPasteCut() && pRemove->GetType() == SC_CAT_CONTENT ) { //! Content wird wiederverwertet ScChangeActionContent* pContent = (ScChangeActionContent*) pRemove; pContent->RemoveAllLinks(); pContent->ClearTrack(); pContent->pNext = pContent->pPrev = NULL; pContent->pNextContent = pContent->pPrevContent = NULL; } } void ScChangeTrack::Undo( sal_uLong nStartAction, sal_uLong nEndAction, bool bMerge ) { // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong if ( bMerge ) { SetMergeState( SC_CTMS_UNDO ); } if ( nStartAction == 0 ) ++nStartAction; if ( nEndAction > nActionMax ) nEndAction = nActionMax; if ( nEndAction && nStartAction <= nEndAction ) { if ( nStartAction == nStartLastCut && nEndAction == nEndLastCut && !IsInPasteCut() ) ResetLastCut(); StartBlockModify( SC_CTM_REMOVE, nStartAction ); for ( sal_uLong j = nEndAction; j >= nStartAction; --j ) { // rueckwaerts um evtl. nActionMax zu recyclen und schnelleren // Zugriff via pLast, Deletes in richtiger Reihenfolge ScChangeAction* pAct = ( (j == nActionMax && pLast && pLast->GetActionNumber() == j) ? pLast : GetAction( j ) ); if ( pAct ) { if ( pAct->IsDeleteType() ) { if ( j == nEndAction || (pAct != pLast && ((ScChangeActionDel*)pAct)->IsTopDelete()) ) { SetInDeleteTop( sal_True ); SetInDeleteRange( ((ScChangeActionDel*)pAct)-> GetOverAllRange().MakeRange() ); } } UpdateReference( pAct, sal_True ); SetInDeleteTop( sal_False ); Remove( pAct ); if ( IsInPasteCut() ) aPasteCutTable.Insert( pAct->GetActionNumber(), pAct ); else { if ( j == nStartAction && pAct->GetType() == SC_CAT_MOVE ) { ScChangeActionMove* pMove = (ScChangeActionMove*) pAct; sal_uLong nStart = pMove->GetStartLastCut(); sal_uLong nEnd = pMove->GetEndLastCut(); if ( nStart && nStart <= nEnd ) { // LastCut wiederherstellen //! Links vor Cut-Append aufloesen pMove->RemoveAllLinks(); StartBlockModify( SC_CTM_APPEND, nStart ); for ( sal_uLong nCut = nStart; nCut <= nEnd; nCut++ ) { ScChangeAction* pCut = aPasteCutTable.Remove( nCut ); if ( pCut ) { DBG_ASSERT( !aTable.Get( nCut ), "ScChangeTrack::Undo: nCut dup" ); Append( pCut, nCut ); } else { DBG_ERROR( "ScChangeTrack::Undo: nCut not found" ); } } EndBlockModify( nEnd ); ResetLastCut(); nStartLastCut = nStart; nEndLastCut = nEnd; pLastCutMove = pMove; SetLastCutMoveRange( pMove->GetFromRange().MakeRange(), pDoc ); } else delete pMove; } else delete pAct; } } } EndBlockModify( nEndAction ); } // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong if ( bMerge ) { SetMergeState( SC_CTMS_OTHER ); } } // static sal_Bool ScChangeTrack::MergeIgnore( const ScChangeAction& rAction, sal_uLong nFirstMerge ) { if ( rAction.IsRejected() ) return sal_True; // da kommt noch eine passende Reject-Action if ( rAction.IsRejecting() && rAction.GetRejectAction() >= nFirstMerge ) return sal_True; // da ist sie return sal_False; // alles andere } void ScChangeTrack::MergePrepare( ScChangeAction* pFirstMerge, bool bShared ) { SetMergeState( SC_CTMS_PREPARE ); sal_uLong nFirstMerge = pFirstMerge->GetActionNumber(); ScChangeAction* pAct = GetLast(); if ( pAct ) { SetLastMerge( pAct->GetActionNumber() ); while ( pAct ) { // rueckwaerts, Deletes in richtiger Reihenfolge // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) ) { if ( pAct->IsDeleteType() ) { if ( ((ScChangeActionDel*)pAct)->IsTopDelete() ) { SetInDeleteTop( sal_True ); SetInDeleteRange( ((ScChangeActionDel*)pAct)-> GetOverAllRange().MakeRange() ); } } UpdateReference( pAct, sal_True ); SetInDeleteTop( sal_False ); pAct->DeleteCellEntries(); // sonst GPF bei Track Clear() } pAct = ( pAct == pFirstMerge ? NULL : pAct->GetPrev() ); } } SetMergeState( SC_CTMS_OTHER ); //! nachfolgende per default MergeOther } void ScChangeTrack::MergeOwn( ScChangeAction* pAct, sal_uLong nFirstMerge, bool bShared ) { // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong if ( bShared || !ScChangeTrack::MergeIgnore( *pAct, nFirstMerge ) ) { SetMergeState( SC_CTMS_OWN ); if ( pAct->IsDeleteType() ) { if ( ((ScChangeActionDel*)pAct)->IsTopDelete() ) { SetInDeleteTop( sal_True ); SetInDeleteRange( ((ScChangeActionDel*)pAct)-> GetOverAllRange().MakeRange() ); } } UpdateReference( pAct, sal_False ); SetInDeleteTop( sal_False ); SetMergeState( SC_CTMS_OTHER ); //! nachfolgende per default MergeOther } } void ScChangeTrack::UpdateReference( ScChangeAction* pAct, sal_Bool bUndo ) { ScChangeActionType eActType = pAct->GetType(); if ( eActType == SC_CAT_CONTENT || eActType == SC_CAT_REJECT ) return ; //! Formelzellen haengen nicht im Dokument sal_Bool bOldAutoCalc = pDoc->GetAutoCalc(); pDoc->SetAutoCalc( sal_False ); sal_Bool bOldNoListening = pDoc->GetNoListening(); pDoc->SetNoListening( sal_True ); //! Formelzellen ExpandRefs synchronisiert zu denen im Dokument sal_Bool bOldExpandRefs = pDoc->IsExpandRefs(); if ( (!bUndo && pAct->IsInsertType()) || (bUndo && pAct->IsDeleteType()) ) pDoc->SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() ); if ( pAct->IsDeleteType() ) { SetInDeleteUndo( bUndo ); SetInDelete( sal_True ); } else if ( GetMergeState() == SC_CTMS_OWN ) { // Referenzen von Formelzellen wiederherstellen, // vorheriges MergePrepare war bei einem Insert wie ein Delete if ( pAct->IsInsertType() ) SetInDeleteUndo( sal_True ); } //! erst die generated, als waeren sie vorher getrackt worden if ( pFirstGeneratedDelContent ) UpdateReference( (ScChangeAction**)&pFirstGeneratedDelContent, pAct, bUndo ); UpdateReference( &pFirst, pAct, bUndo ); SetInDelete( sal_False ); SetInDeleteUndo( sal_False ); pDoc->SetExpandRefs( bOldExpandRefs ); pDoc->SetNoListening( bOldNoListening ); pDoc->SetAutoCalc( bOldAutoCalc ); } void ScChangeTrack::UpdateReference( ScChangeAction** ppFirstAction, ScChangeAction* pAct, sal_Bool bUndo ) { ScChangeActionType eActType = pAct->GetType(); sal_Bool bGeneratedDelContents = ( ppFirstAction == (ScChangeAction**)&pFirstGeneratedDelContent ); const ScBigRange& rOrgRange = pAct->GetBigRange(); ScBigRange aRange( rOrgRange ); ScBigRange aDelRange( rOrgRange ); sal_Int32 nDx, nDy, nDz; nDx = nDy = nDz = 0; UpdateRefMode eMode = URM_INSDEL; sal_Bool bDel = sal_False; switch ( eActType ) { case SC_CAT_INSERT_COLS : aRange.aEnd.SetCol( nInt32Max ); nDx = rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1; break; case SC_CAT_INSERT_ROWS : aRange.aEnd.SetRow( nInt32Max ); nDy = rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1; break; case SC_CAT_INSERT_TABS : aRange.aEnd.SetTab( nInt32Max ); nDz = rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1; break; case SC_CAT_DELETE_COLS : aRange.aEnd.SetCol( nInt32Max ); nDx = -(rOrgRange.aEnd.Col() - rOrgRange.aStart.Col() + 1); aDelRange.aEnd.SetCol( aDelRange.aStart.Col() - nDx - 1 ); bDel = sal_True; break; case SC_CAT_DELETE_ROWS : aRange.aEnd.SetRow( nInt32Max ); nDy = -(rOrgRange.aEnd.Row() - rOrgRange.aStart.Row() + 1); aDelRange.aEnd.SetRow( aDelRange.aStart.Row() - nDy - 1 ); bDel = sal_True; break; case SC_CAT_DELETE_TABS : aRange.aEnd.SetTab( nInt32Max ); nDz = -(rOrgRange.aEnd.Tab() - rOrgRange.aStart.Tab() + 1); aDelRange.aEnd.SetTab( aDelRange.aStart.Tab() - nDz - 1 ); bDel = sal_True; break; case SC_CAT_MOVE : eMode = URM_MOVE; ((ScChangeActionMove*)pAct)->GetDelta( nDx, nDy, nDz ); break; default: DBG_ERROR( "ScChangeTrack::UpdateReference: unknown Type" ); } if ( bUndo ) { nDx = -nDx; nDy = -nDy; nDz = -nDz; } if ( bDel ) { //! fuer diesen Mechanismus gilt: //! es gibt nur ganze, einfache geloeschte Spalten/Zeilen ScChangeActionDel* pActDel = (ScChangeActionDel*) pAct; if ( !bUndo ) { // Delete ScChangeActionType eInsType = SC_CAT_NONE; // for Insert-Undo-"Deletes" switch ( eActType ) { case SC_CAT_DELETE_COLS : eInsType = SC_CAT_INSERT_COLS; break; case SC_CAT_DELETE_ROWS : eInsType = SC_CAT_INSERT_ROWS; break; case SC_CAT_DELETE_TABS : eInsType = SC_CAT_INSERT_TABS; break; default: { // added to avoid warnings } } for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p == pAct ) continue; // for sal_Bool bUpdate = sal_True; if ( GetMergeState() == SC_CTMS_OTHER && p->GetActionNumber() <= GetLastMerge() ) { // Delete in mergendem Dokument, Action im zu mergenden if ( p->IsInsertType() ) { // Bei Insert Referenzen nur anpassen, wenn das Delete // das Insert nicht schneidet. if ( !aDelRange.Intersects( p->GetBigRange() ) ) p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); bUpdate = sal_False; } else if ( p->GetType() == SC_CAT_CONTENT && p->IsDeletedInDelType( eInsType ) ) { // Content in Insert-Undo-"Delete" // Nicht anpassen, wenn dieses Delete in dem // Insert-"Delete" sein wuerde (ist nur verschoben). if ( aDelRange.In( p->GetBigRange().aStart ) ) bUpdate = sal_False; else { const ScChangeActionLinkEntry* pLink = p->GetDeletedIn(); while ( pLink && bUpdate ) { const ScChangeAction* pDel = pLink->GetAction(); if ( pDel && pDel->GetType() == eInsType && pDel->GetBigRange().In( aDelRange ) ) bUpdate = sal_False; pLink = pLink->GetNext(); } } } if ( !bUpdate ) continue; // for } if ( aDelRange.In( p->GetBigRange() ) ) { // Innerhalb eines gerade geloeschten Bereiches nicht // anpassen, stattdessen dem Bereich zuordnen. // Mehrfache geloeschte Bereiche "stapeln". // Kreuzende Deletes setzen mehrfach geloescht. if ( !p->IsDeletedInDelType( eActType ) ) { p->SetDeletedIn( pActDel ); // GeneratedDelContent in zu loeschende Liste aufnehmen if ( bGeneratedDelContents ) pActDel->AddContent( (ScChangeActionContent*) p ); } bUpdate = sal_False; } else { // Eingefuegte Bereiche abschneiden, wenn Start/End im // Delete liegt, aber das Insert nicht komplett innerhalb // des Delete liegt bzw. das Delete nicht komplett im // Insert. Das Delete merkt sich, welchem Insert es was // abgeschnitten hat, es kann auch nur ein einziges Insert // sein (weil Delete einspaltig/einzeilig ist). // Abgeschnittene Moves kann es viele geben. //! Ein Delete ist immer einspaltig/einzeilig, deswegen 1 //! ohne die Ueberlappung auszurechnen. switch ( p->GetType() ) { case SC_CAT_INSERT_COLS : if ( eActType == SC_CAT_DELETE_COLS ) { if ( aDelRange.In( p->GetBigRange().aStart ) ) { pActDel->SetCutOffInsert( (ScChangeActionIns*) p, 1 ); p->GetBigRange().aStart.IncCol( 1 ); } else if ( aDelRange.In( p->GetBigRange().aEnd ) ) { pActDel->SetCutOffInsert( (ScChangeActionIns*) p, -1 ); p->GetBigRange().aEnd.IncCol( -1 ); } } break; case SC_CAT_INSERT_ROWS : if ( eActType == SC_CAT_DELETE_ROWS ) { if ( aDelRange.In( p->GetBigRange().aStart ) ) { pActDel->SetCutOffInsert( (ScChangeActionIns*) p, 1 ); p->GetBigRange().aStart.IncRow( 1 ); } else if ( aDelRange.In( p->GetBigRange().aEnd ) ) { pActDel->SetCutOffInsert( (ScChangeActionIns*) p, -1 ); p->GetBigRange().aEnd.IncRow( -1 ); } } break; case SC_CAT_INSERT_TABS : if ( eActType == SC_CAT_DELETE_TABS ) { if ( aDelRange.In( p->GetBigRange().aStart ) ) { pActDel->SetCutOffInsert( (ScChangeActionIns*) p, 1 ); p->GetBigRange().aStart.IncTab( 1 ); } else if ( aDelRange.In( p->GetBigRange().aEnd ) ) { pActDel->SetCutOffInsert( (ScChangeActionIns*) p, -1 ); p->GetBigRange().aEnd.IncTab( -1 ); } } break; case SC_CAT_MOVE : { ScChangeActionMove* pMove = (ScChangeActionMove*) p; short nFrom = 0; short nTo = 0; if ( aDelRange.In( pMove->GetBigRange().aStart ) ) nTo = 1; else if ( aDelRange.In( pMove->GetBigRange().aEnd ) ) nTo = -1; if ( aDelRange.In( pMove->GetFromRange().aStart ) ) nFrom = 1; else if ( aDelRange.In( pMove->GetFromRange().aEnd ) ) nFrom = -1; if ( nFrom ) { switch ( eActType ) { case SC_CAT_DELETE_COLS : if ( nFrom > 0 ) pMove->GetFromRange().aStart.IncCol( nFrom ); else pMove->GetFromRange().aEnd.IncCol( nFrom ); break; case SC_CAT_DELETE_ROWS : if ( nFrom > 0 ) pMove->GetFromRange().aStart.IncRow( nFrom ); else pMove->GetFromRange().aEnd.IncRow( nFrom ); break; case SC_CAT_DELETE_TABS : if ( nFrom > 0 ) pMove->GetFromRange().aStart.IncTab( nFrom ); else pMove->GetFromRange().aEnd.IncTab( nFrom ); break; default: { // added to avoid warnings } } } if ( nTo ) { switch ( eActType ) { case SC_CAT_DELETE_COLS : if ( nTo > 0 ) pMove->GetBigRange().aStart.IncCol( nTo ); else pMove->GetBigRange().aEnd.IncCol( nTo ); break; case SC_CAT_DELETE_ROWS : if ( nTo > 0 ) pMove->GetBigRange().aStart.IncRow( nTo ); else pMove->GetBigRange().aEnd.IncRow( nTo ); break; case SC_CAT_DELETE_TABS : if ( nTo > 0 ) pMove->GetBigRange().aStart.IncTab( nTo ); else pMove->GetBigRange().aEnd.IncTab( nTo ); break; default: { // added to avoid warnings } } } if ( nFrom || nTo ) { ScChangeActionDelMoveEntry* pLink = pActDel->AddCutOffMove( pMove, nFrom, nTo ); pMove->AddLink( pActDel, pLink ); } } break; default: { // added to avoid warnings } } } if ( bUpdate ) { p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); if ( p->GetType() == eActType && !p->IsRejected() && !pActDel->IsDeletedIn() && p->GetBigRange().In( aDelRange ) ) pActDel->SetDeletedIn( p ); // "druntergerutscht" } } } else { // Undo Delete for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p == pAct ) continue; // for sal_Bool bUpdate = sal_True; if ( aDelRange.In( p->GetBigRange() ) ) { // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong if ( GetMergeState() == SC_CTMS_UNDO && !p->IsDeletedIn( pAct ) && pAct->IsDeleteType() && ( p->GetType() == SC_CAT_CONTENT || p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS || p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) ) { p->SetDeletedIn( pAct ); } if ( p->IsDeletedInDelType( eActType ) ) { if ( p->IsDeletedIn( pActDel ) ) { if ( p->GetType() != SC_CAT_CONTENT || ((ScChangeActionContent*)p)->IsTopContent() ) { // erst der TopContent wird wirklich entfernt p->RemoveDeletedIn( pActDel ); // GeneratedDelContent _nicht_ aus Liste loeschen, // wir brauchen ihn evtl. noch fuer Reject, // geloescht wird in DeleteCellEntries } } bUpdate = sal_False; } else if ( eActType != SC_CAT_DELETE_TABS && p->IsDeletedInDelType( SC_CAT_DELETE_TABS ) ) { // in geloeschten Tabellen nicht updaten, // ausser wenn Tabelle verschoben wird bUpdate = sal_False; } if ( p->GetType() == eActType && pActDel->IsDeletedIn( p ) ) { pActDel->RemoveDeletedIn( p ); // "druntergerutscht" bUpdate = sal_True; } } if ( bUpdate ) p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); } if ( !bGeneratedDelContents ) { // die werden sonst noch fuer das echte Undo gebraucht pActDel->UndoCutOffInsert(); pActDel->UndoCutOffMoves(); } } } else if ( eActType == SC_CAT_MOVE ) { ScChangeActionMove* pActMove = (ScChangeActionMove*) pAct; sal_Bool bLastCutMove = ( pActMove == pLastCutMove ); const ScBigRange& rTo = pActMove->GetBigRange(); const ScBigRange& rFrom = pActMove->GetFromRange(); if ( !bUndo ) { // Move for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p == pAct ) continue; // for if ( p->GetType() == SC_CAT_CONTENT ) { // Inhalt in Ziel deleten (Inhalt in Quelle moven) if ( rTo.In( p->GetBigRange() ) ) { if ( !p->IsDeletedIn( pActMove ) ) { p->SetDeletedIn( pActMove ); // GeneratedDelContent in zu loeschende Liste aufnehmen if ( bGeneratedDelContents ) pActMove->AddContent( (ScChangeActionContent*) p ); } } else if ( bLastCutMove && p->GetActionNumber() > nEndLastCut && rFrom.In( p->GetBigRange() ) ) { // Paste Cut: neuer Content nach Cut eingefuegt, bleibt. // Aufsplitten der ContentChain ScChangeActionContent *pHere, *pTmp; pHere = (ScChangeActionContent*) p; while ( (pTmp = pHere->GetPrevContent()) != NULL && pTmp->GetActionNumber() > nEndLastCut ) pHere = pTmp; if ( pTmp ) { // wird TopContent des Move pTmp->SetNextContent( NULL ); pHere->SetPrevContent( NULL ); } do { // Abhaengigkeit vom FromRange herstellen AddDependentWithNotify( pActMove, pHere ); } while ( ( pHere = pHere->GetNextContent() ) != NULL ); } // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() ) p->UpdateReference( this, eMode, rFrom, nDx, nDy, nDz ); } } } else { // Undo Move sal_Bool bActRejected = pActMove->IsRejected(); for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p == pAct ) continue; // for if ( p->GetType() == SC_CAT_CONTENT ) { // Inhalt in Ziel moven, wenn nicht deleted, sonst undelete if ( p->IsDeletedIn( pActMove ) ) { if ( ((ScChangeActionContent*)p)->IsTopContent() ) { // erst der TopContent wird wirklich entfernt p->RemoveDeletedIn( pActMove ); // GeneratedDelContent _nicht_ aus Liste loeschen, // wir brauchen ihn evtl. noch fuer Reject, // geloescht wird in DeleteCellEntries } } // #i87003# [Collaboration] Move range and insert content in FromRange is not merged correctly else if ( ( GetMergeState() != SC_CTMS_PREPARE && GetMergeState() != SC_CTMS_OWN ) || p->GetActionNumber() <= pAct->GetActionNumber() ) p->UpdateReference( this, eMode, rTo, nDx, nDy, nDz ); if ( bActRejected && ((ScChangeActionContent*)p)->IsTopContent() && rFrom.In( p->GetBigRange() ) ) { // Abhaengigkeit herstellen, um Content zu schreiben ScChangeActionLinkEntry* pLink = pActMove->AddDependent( p ); p->AddLink( pActMove, pLink ); } } } } } else { // Insert / Undo Insert switch ( GetMergeState() ) { case SC_CTMS_NONE : case SC_CTMS_OTHER : { for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p == pAct ) continue; // for p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); } } break; case SC_CTMS_PREPARE : { // in Insert-Undo "Deleten" const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry(); while ( pLink ) { ScChangeAction* p = (ScChangeAction*) pLink->GetAction(); if ( p ) p->SetDeletedIn( pAct ); pLink = pLink->GetNext(); } // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() && // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong ( p->GetType() == SC_CAT_CONTENT || p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS || p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) && pAct->GetBigRange().Intersects( p->GetBigRange() ) ) { p->SetDeletedIn( pAct ); } } for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p == pAct ) continue; // for if ( !p->IsDeletedIn( pAct ) // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet && p->GetActionNumber() <= pAct->GetActionNumber() ) { p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); } } } break; case SC_CTMS_OWN : { for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p == pAct ) continue; // for if ( !p->IsDeletedIn( pAct ) // #i95212# [Collaboration] Bad handling of row insertion in shared spreadsheet && p->GetActionNumber() <= pAct->GetActionNumber() ) { p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); } } // in Insert-Undo "Delete" rueckgaengig const ScChangeActionLinkEntry* pLink = pAct->GetFirstDependentEntry(); while ( pLink ) { ScChangeAction* p = (ScChangeAction*) pLink->GetAction(); if ( p ) p->RemoveDeletedIn( pAct ); pLink = pLink->GetNext(); } // #i87049# [Collaboration] Conflict between delete row and insert content is not merged correctly for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p->IsDeletedIn( pAct ) && pAct->IsInsertType() && // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong ( p->GetType() == SC_CAT_CONTENT || p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS || p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) && pAct->GetBigRange().Intersects( p->GetBigRange() ) ) { p->RemoveDeletedIn( pAct ); } } } break; // #i94841# [Collaboration] When deleting rows is rejected, the content is sometimes wrong case SC_CTMS_UNDO : { for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( !p->IsDeletedIn( pAct ) && pAct->IsInsertType() && ( p->GetType() == SC_CAT_CONTENT || p->GetType() == SC_CAT_DELETE_ROWS || p->GetType() == SC_CAT_DELETE_COLS || p->GetType() == SC_CAT_INSERT_ROWS || p->GetType() == SC_CAT_INSERT_COLS ) && pAct->GetBigRange().Intersects( p->GetBigRange() ) ) { p->SetDeletedIn( pAct ); } } for ( ScChangeAction* p = *ppFirstAction; p; p = p->GetNext() ) { if ( p == pAct ) { continue; } if ( !p->IsDeletedIn( pAct ) && p->GetActionNumber() <= pAct->GetActionNumber() ) { p->UpdateReference( this, eMode, aRange, nDx, nDy, nDz ); } } } break; } } } void ScChangeTrack::GetDependents( ScChangeAction* pAct, ScChangeActionTable& rTable, sal_Bool bListMasterDelete, sal_Bool bAllFlat ) const { //! bAllFlat==TRUE: intern aus Accept oder Reject gerufen, //! => Generated werden nicht aufgenommen sal_Bool bIsDelete = pAct->IsDeleteType(); sal_Bool bIsMasterDelete = ( bListMasterDelete && pAct->IsMasterDelete() ); const ScChangeAction* pCur = pAct; ScChangeActionStack* pStack = new ScChangeActionStack; do { if ( pCur->IsInsertType() ) { const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry(); while ( pL ) { ScChangeAction* p = (ScChangeAction*) pL->GetAction(); if ( p != pAct ) { if ( bAllFlat ) { sal_uLong n = p->GetActionNumber(); if ( !IsGenerated( n ) && rTable.Insert( n, p ) ) if ( p->HasDependent() ) pStack->Push( p ); } else { if ( p->GetType() == SC_CAT_CONTENT ) { if ( ((ScChangeActionContent*)p)->IsTopContent() ) rTable.Insert( p->GetActionNumber(), p ); } else rTable.Insert( p->GetActionNumber(), p ); } } pL = pL->GetNext(); } } else if ( pCur->IsDeleteType() ) { if ( bIsDelete ) { // Inhalte geloeschter Bereiche interessieren nur bei Delete ScChangeActionDel* pDel = (ScChangeActionDel*) pCur; if ( !bAllFlat && bIsMasterDelete && pCur == pAct ) { // zu diesem Delete gehoerende Deletes in gleiche Ebene, // wenn dieses Delete das momentan oberste einer Reihe ist, ScChangeActionType eType = pDel->GetType(); ScChangeAction* p = pDel; while ( (p = p->GetPrev()) != NULL && p->GetType() == eType && !((ScChangeActionDel*)p)->IsTopDelete() ) rTable.Insert( p->GetActionNumber(), p ); // dieses Delete auch in Table! rTable.Insert( pAct->GetActionNumber(), pAct ); } else { const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry(); while ( pL ) { ScChangeAction* p = (ScChangeAction*) pL->GetAction(); if ( p != pAct ) { if ( bAllFlat ) { // nur ein TopContent einer Kette ist in LinkDeleted sal_uLong n = p->GetActionNumber(); if ( !IsGenerated( n ) && rTable.Insert( n, p ) ) if ( p->HasDeleted() || p->GetType() == SC_CAT_CONTENT ) pStack->Push( p ); } else { if ( p->IsDeleteType() ) { // weiteres TopDelete in gleiche Ebene, // es ist nicht rejectable if ( ((ScChangeActionDel*)p)->IsTopDelete() ) rTable.Insert( p->GetActionNumber(), p ); } else rTable.Insert( p->GetActionNumber(), p ); } } pL = pL->GetNext(); } } } } else if ( pCur->GetType() == SC_CAT_MOVE ) { // geloeschte Contents im ToRange const ScChangeActionLinkEntry* pL = pCur->GetFirstDeletedEntry(); while ( pL ) { ScChangeAction* p = (ScChangeAction*) pL->GetAction(); if ( p != pAct && rTable.Insert( p->GetActionNumber(), p ) ) { // nur ein TopContent einer Kette ist in LinkDeleted if ( bAllFlat && (p->HasDeleted() || p->GetType() == SC_CAT_CONTENT) ) pStack->Push( p ); } pL = pL->GetNext(); } // neue Contents im FromRange oder neuer FromRange im ToRange // oder Inserts/Deletes in FromRange/ToRange pL = pCur->GetFirstDependentEntry(); while ( pL ) { ScChangeAction* p = (ScChangeAction*) pL->GetAction(); if ( p != pAct ) { if ( bAllFlat ) { sal_uLong n = p->GetActionNumber(); if ( !IsGenerated( n ) && rTable.Insert( n, p ) ) if ( p->HasDependent() || p->HasDeleted() ) pStack->Push( p ); } else { if ( p->GetType() == SC_CAT_CONTENT ) { if ( ((ScChangeActionContent*)p)->IsTopContent() ) rTable.Insert( p->GetActionNumber(), p ); } else rTable.Insert( p->GetActionNumber(), p ); } } pL = pL->GetNext(); } } else if ( pCur->GetType() == SC_CAT_CONTENT ) { // alle Aenderungen an gleicher Position ScChangeActionContent* pContent = (ScChangeActionContent*) pCur; // alle vorherigen while ( ( pContent = pContent->GetPrevContent() ) != NULL ) { if ( !pContent->IsRejected() ) rTable.Insert( pContent->GetActionNumber(), pContent ); } pContent = (ScChangeActionContent*) pCur; // alle nachfolgenden while ( ( pContent = pContent->GetNextContent() ) != NULL ) { if ( !pContent->IsRejected() ) rTable.Insert( pContent->GetActionNumber(), pContent ); } // all MatrixReferences of a MatrixOrigin const ScChangeActionLinkEntry* pL = pCur->GetFirstDependentEntry(); while ( pL ) { ScChangeAction* p = (ScChangeAction*) pL->GetAction(); if ( p != pAct ) { if ( bAllFlat ) { sal_uLong n = p->GetActionNumber(); if ( !IsGenerated( n ) && rTable.Insert( n, p ) ) if ( p->HasDependent() ) pStack->Push( p ); } else rTable.Insert( p->GetActionNumber(), p ); } pL = pL->GetNext(); } } else if ( pCur->GetType() == SC_CAT_REJECT ) { if ( bAllFlat ) { ScChangeAction* p = GetAction( ((ScChangeActionReject*)pCur)->GetRejectAction() ); if ( p != pAct && !rTable.Get( p->GetActionNumber() ) ) pStack->Push( p ); } } } while ( ( pCur = pStack->Pop() ) != NULL ); delete pStack; } sal_Bool ScChangeTrack::SelectContent( ScChangeAction* pAct, sal_Bool bOldest ) { if ( pAct->GetType() != SC_CAT_CONTENT ) return sal_False; ScChangeActionContent* pContent = (ScChangeActionContent*) pAct; if ( bOldest ) { pContent = pContent->GetTopContent(); ScChangeActionContent* pPrevContent; while ( (pPrevContent = pContent->GetPrevContent()) != NULL && pPrevContent->IsVirgin() ) pContent = pPrevContent; } if ( !pContent->IsClickable() ) return sal_False; ScBigRange aBigRange( pContent->GetBigRange() ); const ScBaseCell* pCell = (bOldest ? pContent->GetOldCell() : pContent->GetNewCell()); if ( ScChangeActionContent::GetContentCellType( pCell ) == SC_CACCT_MATORG ) { SCCOL nC; SCROW nR; ((const ScFormulaCell*)pCell)->GetMatColsRows( nC, nR ); aBigRange.aEnd.IncCol( nC-1 ); aBigRange.aEnd.IncRow( nR-1 ); } if ( !aBigRange.IsValid( pDoc ) ) return sal_False; ScRange aRange( aBigRange.MakeRange() ); if ( !pDoc->IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) ) return sal_False; if ( pContent->HasDependent() ) { sal_Bool bOk = sal_True; Stack aRejectActions; const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry(); while ( pL ) { ScChangeAction* p = (ScChangeAction*) pL->GetAction(); if ( p != pContent ) { if ( p->GetType() == SC_CAT_CONTENT ) { // we don't need no recursion here, do we? bOk &= ((ScChangeActionContent*)p)->Select( pDoc, this, bOldest, &aRejectActions ); } else { DBG_ERRORFILE( "ScChangeTrack::SelectContent: content dependent no content" ); } } pL = pL->GetNext(); } bOk &= pContent->Select( pDoc, this, bOldest, NULL ); // now the matrix is inserted and new content values are ready ScChangeActionContent* pNew; while ( ( pNew = (ScChangeActionContent*) aRejectActions.Pop() ) != NULL ) { ScAddress aPos( pNew->GetBigRange().aStart.MakeAddress() ); pNew->SetNewValue( pDoc->GetCell( aPos ), pDoc ); Append( pNew ); } return bOk; } else return pContent->Select( pDoc, this, bOldest, NULL ); } void ScChangeTrack::AcceptAll() { for ( ScChangeAction* p = GetFirst(); p; p = p->GetNext() ) { p->Accept(); } } sal_Bool ScChangeTrack::Accept( ScChangeAction* pAct ) { if ( !pAct->IsClickable() ) return sal_False; if ( pAct->IsDeleteType() || pAct->GetType() == SC_CAT_CONTENT ) { ScChangeActionTable aActionTable; GetDependents( pAct, aActionTable, sal_False, sal_True ); for ( ScChangeAction* p = aActionTable.First(); p; p = aActionTable.Next() ) { p->Accept(); } } pAct->Accept(); return sal_True; } sal_Bool ScChangeTrack::RejectAll() { sal_Bool bOk = sal_True; for ( ScChangeAction* p = GetLast(); p && bOk; p = p->GetPrev() ) { //! rueckwaerts, weil abhaengige hinten und RejectActions angehaengt if ( p->IsInternalRejectable() ) bOk = Reject( p ); } return bOk; } sal_Bool ScChangeTrack::Reject( ScChangeAction* pAct, bool bShared ) { // #i100895# When collaboration changes are reversed, it must be possible // to reject a deleted row above another deleted row. if ( bShared && pAct->IsDeletedIn() ) pAct->RemoveAllDeletedIn(); if ( !pAct->IsRejectable() ) return sal_False; ScChangeActionTable* pTable = NULL; if ( pAct->HasDependent() ) { pTable = new ScChangeActionTable; GetDependents( pAct, *pTable, sal_False, sal_True ); } sal_Bool bRejected = Reject( pAct, pTable, sal_False ); if ( pTable ) delete pTable; return bRejected; } sal_Bool ScChangeTrack::Reject( ScChangeAction* pAct, ScChangeActionTable* pTable, sal_Bool bRecursion ) { if ( !pAct->IsInternalRejectable() ) return sal_False; sal_Bool bOk = sal_True; sal_Bool bRejected = sal_False; if ( pAct->IsInsertType() ) { if ( pAct->HasDependent() && !bRecursion ) { DBG_ASSERT( pTable, "ScChangeTrack::Reject: Insert ohne Table" ); for ( ScChangeAction* p = pTable->Last(); p && bOk; p = pTable->Prev() ) { // keine Contents restoren, die eh geloescht werden wuerden if ( p->GetType() == SC_CAT_CONTENT ) p->SetRejected(); else if ( p->IsDeleteType() ) p->Accept(); // geloeschtes ins Nirvana else bOk = Reject( p, NULL, sal_True ); //! rekursiv } } if ( bOk && (bRejected = pAct->Reject( pDoc )) != sal_False ) { // pRefDoc NULL := geloeschte Zellen nicht speichern AppendDeleteRange( pAct->GetBigRange().MakeRange(), NULL, (short) 0, pAct->GetActionNumber() ); } } else if ( pAct->IsDeleteType() ) { DBG_ASSERT( !pTable, "ScChangeTrack::Reject: Delete mit Table" ); ScBigRange aDelRange; sal_uLong nRejectAction = pAct->GetActionNumber(); sal_Bool bTabDel, bTabDelOk; if ( pAct->GetType() == SC_CAT_DELETE_TABS ) { bTabDel = sal_True; aDelRange = pAct->GetBigRange(); bOk = bTabDelOk = pAct->Reject( pDoc ); if ( bOk ) { pAct = pAct->GetPrev(); bOk = ( pAct && pAct->GetType() == SC_CAT_DELETE_COLS ); } } else bTabDel = bTabDelOk = sal_False; ScChangeActionDel* pDel = (ScChangeActionDel*) pAct; if ( bOk ) { aDelRange = pDel->GetOverAllRange(); bOk = aDelRange.IsValid( pDoc ); } sal_Bool bOneOk = sal_False; if ( bOk ) { ScChangeActionType eActType = pAct->GetType(); switch ( eActType ) { case SC_CAT_DELETE_COLS : aDelRange.aStart.SetCol( aDelRange.aEnd.Col() ); break; case SC_CAT_DELETE_ROWS : aDelRange.aStart.SetRow( aDelRange.aEnd.Row() ); break; case SC_CAT_DELETE_TABS : aDelRange.aStart.SetTab( aDelRange.aEnd.Tab() ); break; default: { // added to avoid warnings } } ScChangeAction* p = pAct; sal_Bool bLoop = sal_True; do { pDel = (ScChangeActionDel*) p; bOk = pDel->Reject( pDoc ); if ( bOk ) { if ( bOneOk ) { switch ( pDel->GetType() ) { case SC_CAT_DELETE_COLS : aDelRange.aStart.IncCol( -1 ); break; case SC_CAT_DELETE_ROWS : aDelRange.aStart.IncRow( -1 ); break; case SC_CAT_DELETE_TABS : aDelRange.aStart.IncTab( -1 ); break; default: { // added to avoid warnings } } } else bOneOk = sal_True; } if ( pDel->IsBaseDelete() ) bLoop = sal_False; else p = p->GetPrev(); } while ( bOk && bLoop && p && p->GetType() == eActType && !((ScChangeActionDel*)p)->IsTopDelete() ); } bRejected = bOk; if ( bOneOk || (bTabDel && bTabDelOk) ) { // Delete-Reject machte UpdateReference Undo ScChangeActionIns* pReject = new ScChangeActionIns( aDelRange.MakeRange() ); pReject->SetRejectAction( nRejectAction ); pReject->SetState( SC_CAS_ACCEPTED ); Append( pReject ); } } else if ( pAct->GetType() == SC_CAT_MOVE ) { if ( pAct->HasDependent() && !bRecursion ) { DBG_ASSERT( pTable, "ScChangeTrack::Reject: Move ohne Table" ); for ( ScChangeAction* p = pTable->Last(); p && bOk; p = pTable->Prev() ) { bOk = Reject( p, NULL, sal_True ); //! rekursiv } } if ( bOk && (bRejected = pAct->Reject( pDoc )) != sal_False ) { ScChangeActionMove* pReject = new ScChangeActionMove( pAct->GetBigRange().MakeRange(), ((ScChangeActionMove*)pAct)->GetFromRange().MakeRange(), this ); pReject->SetRejectAction( pAct->GetActionNumber() ); pReject->SetState( SC_CAS_ACCEPTED ); Append( pReject ); } } else if ( pAct->GetType() == SC_CAT_CONTENT ) { ScRange aRange; ScChangeActionContent* pReject; if ( bRecursion ) pReject = NULL; else { aRange = pAct->GetBigRange().aStart.MakeAddress(); pReject = new ScChangeActionContent( aRange ); pReject->SetOldValue( pDoc->GetCell( aRange.aStart ), pDoc, pDoc ); } if ( (bRejected = pAct->Reject( pDoc )) != sal_False && !bRecursion ) { pReject->SetNewValue( pDoc->GetCell( aRange.aStart ), pDoc ); pReject->SetRejectAction( pAct->GetActionNumber() ); pReject->SetState( SC_CAS_ACCEPTED ); Append( pReject ); } else if ( pReject ) delete pReject; } else { DBG_ERROR( "ScChangeTrack::Reject: say what?" ); } return bRejected; } sal_uLong ScChangeTrack::AddLoadedGenerated(ScBaseCell* pNewCell, const ScBigRange& aBigRange, const String& sNewValue ) { ScChangeActionContent* pAct = new ScChangeActionContent( --nGeneratedMin, pNewCell, aBigRange, pDoc, sNewValue ); if ( pAct ) { if ( pFirstGeneratedDelContent ) pFirstGeneratedDelContent->pPrev = pAct; pAct->pNext = pFirstGeneratedDelContent; pFirstGeneratedDelContent = pAct; aGeneratedTable.Insert( pAct->GetActionNumber(), pAct ); return pAct->GetActionNumber(); } return 0; } void ScChangeTrack::AppendCloned( ScChangeAction* pAppend ) { aTable.Insert( pAppend->GetActionNumber(), pAppend ); if ( !pLast ) pFirst = pLast = pAppend; else { pLast->pNext = pAppend; pAppend->pPrev = pLast; pLast = pAppend; } } ScChangeTrack* ScChangeTrack::Clone( ScDocument* pDocument ) const { if ( !pDocument ) { return NULL; } ScChangeTrack* pClonedTrack = new ScChangeTrack( pDocument ); pClonedTrack->SetTime100thSeconds( IsTime100thSeconds() ); // clone generated actions ::std::stack< const ScChangeAction* > aGeneratedStack; const ScChangeAction* pGenerated = GetFirstGenerated(); while ( pGenerated ) { aGeneratedStack.push( pGenerated ); pGenerated = pGenerated->GetNext(); } while ( !aGeneratedStack.empty() ) { pGenerated = aGeneratedStack.top(); aGeneratedStack.pop(); const ScChangeActionContent* pContent = dynamic_cast< const ScChangeActionContent* >( pGenerated ); DBG_ASSERT( pContent, "ScChangeTrack::Clone: pContent is null!" ); const ScBaseCell* pNewCell = pContent->GetNewCell(); if ( pNewCell ) { ScBaseCell* pClonedNewCell = pNewCell->CloneWithoutNote( *pDocument ); String aNewValue; pContent->GetNewString( aNewValue ); pClonedTrack->nGeneratedMin = pGenerated->GetActionNumber() + 1; pClonedTrack->AddLoadedGenerated( pClonedNewCell, pGenerated->GetBigRange(), aNewValue ); } } // clone actions const ScChangeAction* pAction = GetFirst(); while ( pAction ) { ScChangeAction* pClonedAction = NULL; switch ( pAction->GetType() ) { case SC_CAT_INSERT_COLS: case SC_CAT_INSERT_ROWS: case SC_CAT_INSERT_TABS: { pClonedAction = new ScChangeActionIns( pAction->GetActionNumber(), pAction->GetState(), pAction->GetRejectAction(), pAction->GetBigRange(), pAction->GetUser(), pAction->GetDateTimeUTC(), pAction->GetComment(), pAction->GetType() ); } break; case SC_CAT_DELETE_COLS: case SC_CAT_DELETE_ROWS: case SC_CAT_DELETE_TABS: { const ScChangeActionDel* pDelete = dynamic_cast< const ScChangeActionDel* >( pAction ); DBG_ASSERT( pDelete, "ScChangeTrack::Clone: pDelete is null!" ); SCsCOLROW nD = 0; ScChangeActionType eType = pAction->GetType(); if ( eType == SC_CAT_DELETE_COLS ) { nD = static_cast< SCsCOLROW >( pDelete->GetDx() ); } else if ( eType == SC_CAT_DELETE_ROWS ) { nD = static_cast< SCsCOLROW >( pDelete->GetDy() ); } pClonedAction = new ScChangeActionDel( pAction->GetActionNumber(), pAction->GetState(), pAction->GetRejectAction(), pAction->GetBigRange(), pAction->GetUser(), pAction->GetDateTimeUTC(), pAction->GetComment(), eType, nD, pClonedTrack ); } break; case SC_CAT_MOVE: { const ScChangeActionMove* pMove = dynamic_cast< const ScChangeActionMove* >( pAction ); DBG_ASSERT( pMove, "ScChangeTrack::Clone: pMove is null!" ); pClonedAction = new ScChangeActionMove( pAction->GetActionNumber(), pAction->GetState(), pAction->GetRejectAction(), pAction->GetBigRange(), pAction->GetUser(), pAction->GetDateTimeUTC(), pAction->GetComment(), pMove->GetFromRange(), pClonedTrack ); } break; case SC_CAT_CONTENT: { const ScChangeActionContent* pContent = dynamic_cast< const ScChangeActionContent* >( pAction ); DBG_ASSERT( pContent, "ScChangeTrack::Clone: pContent is null!" ); const ScBaseCell* pOldCell = pContent->GetOldCell(); ScBaseCell* pClonedOldCell = pOldCell ? pOldCell->CloneWithoutNote( *pDocument ) : 0; String aOldValue; pContent->GetOldString( aOldValue ); ScChangeActionContent* pClonedContent = new ScChangeActionContent( pAction->GetActionNumber(), pAction->GetState(), pAction->GetRejectAction(), pAction->GetBigRange(), pAction->GetUser(), pAction->GetDateTimeUTC(), pAction->GetComment(), pClonedOldCell, pDocument, aOldValue ); const ScBaseCell* pNewCell = pContent->GetNewCell(); if ( pNewCell ) { ScBaseCell* pClonedNewCell = pNewCell->CloneWithoutNote( *pDocument ); pClonedContent->SetNewValue( pClonedNewCell, pDocument ); } pClonedAction = pClonedContent; } break; case SC_CAT_REJECT: { pClonedAction = new ScChangeActionReject( pAction->GetActionNumber(), pAction->GetState(), pAction->GetRejectAction(), pAction->GetBigRange(), pAction->GetUser(), pAction->GetDateTimeUTC(), pAction->GetComment() ); } break; default: { } break; } if ( pClonedAction ) { pClonedTrack->AppendCloned( pClonedAction ); } pAction = pAction->GetNext(); } if ( pClonedTrack->GetLast() ) { pClonedTrack->SetActionMax( pClonedTrack->GetLast()->GetActionNumber() ); } // set dependencies for Deleted/DeletedIn pAction = GetFirst(); while ( pAction ) { if ( pAction->HasDeleted() ) { ::std::stack< sal_uLong > aStack; const ScChangeActionLinkEntry* pL = pAction->GetFirstDeletedEntry(); while ( pL ) { const ScChangeAction* pDeleted = pL->GetAction(); if ( pDeleted ) { aStack.push( pDeleted->GetActionNumber() ); } pL = pL->GetNext(); } ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() ); if ( pClonedAction ) { while ( !aStack.empty() ) { ScChangeAction* pClonedDeleted = pClonedTrack->GetActionOrGenerated( aStack.top() ); aStack.pop(); if ( pClonedDeleted ) { pClonedDeleted->SetDeletedIn( pClonedAction ); } } } } pAction = pAction->GetNext(); } // set dependencies for Dependent/Any pAction = GetLast(); while ( pAction ) { if ( pAction->HasDependent() ) { ::std::stack< sal_uLong > aStack; const ScChangeActionLinkEntry* pL = pAction->GetFirstDependentEntry(); while ( pL ) { const ScChangeAction* pDependent = pL->GetAction(); if ( pDependent ) { aStack.push( pDependent->GetActionNumber() ); } pL = pL->GetNext(); } ScChangeAction* pClonedAction = pClonedTrack->GetAction( pAction->GetActionNumber() ); if ( pClonedAction ) { while ( !aStack.empty() ) { ScChangeAction* pClonedDependent = pClonedTrack->GetActionOrGenerated( aStack.top() ); aStack.pop(); if ( pClonedDependent ) { ScChangeActionLinkEntry* pLink = pClonedAction->AddDependent( pClonedDependent ); pClonedDependent->AddLink( pClonedAction, pLink ); } } } } pAction = pAction->GetPrev(); } // masterlinks ScChangeAction* pClonedAction = pClonedTrack->GetFirst(); while ( pClonedAction ) { pClonedTrack->MasterLinks( pClonedAction ); pClonedAction = pClonedAction->GetNext(); } if ( IsProtected() ) { pClonedTrack->SetProtection( GetProtection() ); } if ( pClonedTrack->GetLast() ) { pClonedTrack->SetLastSavedActionNumber( pClonedTrack->GetLast()->GetActionNumber() ); } pDocument->SetChangeTrack( pClonedTrack ); return pClonedTrack; } void ScChangeTrack::MergeActionState( ScChangeAction* pAct, const ScChangeAction* pOtherAct ) { if ( pAct->IsVirgin() ) { if ( pOtherAct->IsAccepted() ) { pAct->Accept(); if ( pOtherAct->IsRejecting() ) { pAct->SetRejectAction( pOtherAct->GetRejectAction() ); } } else if ( pOtherAct->IsRejected() ) { pAct->SetRejected(); } } } #if DEBUG_CHANGETRACK String ScChangeTrack::ToString() const { String aReturn; aReturn += String::CreateFromAscii( "============================================================\n" ); const ScChangeAction* pGenerated = GetFirstGenerated(); while ( pGenerated ) { aReturn += pGenerated->ToString( pDoc ); aReturn += '\n'; pGenerated = pGenerated->GetNext(); } aReturn += String::CreateFromAscii( "------------------------------------------------------------\n" ); const ScChangeAction* pAction = GetFirst(); while ( pAction ) { aReturn += pAction->ToString( pDoc ); aReturn += '\n'; pAction = pAction->GetNext(); } aReturn += String::CreateFromAscii( "============================================================\n" ); return aReturn; } #endif // DEBUG_CHANGETRACK