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