/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* 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 .
*/
#include <chgtrack.hxx>
#include <compiler.hxx>
#include <formulacell.hxx>
#include <document.hxx>
#include <docsh.hxx>
#include <dociter.hxx>
#include <global.hxx>
#include <scmod.hxx>
#include <inputopt.hxx>
#include <patattr.hxx>
#include <hints.hxx>
#include <markdata.hxx>
#include <globstr.hrc>
#include <scresid.hxx>
#include <editutil.hxx>
#include <tokenarray.hxx>
#include <refupdatecontext.hxx>
#include <refupdat.hxx>
#include <osl/diagnose.h>
#include <svl/numformat.hxx>
#include <sfx2/objsh.hxx>
#include <unotools/useroptions.hxx>
#include <unotools/datetime.hxx>
#include <tools/json_writer.hxx>
#include <algorithm>
#include <memory>
#include <strings.hrc>
#include <utility>
ScChangeAction::ScChangeAction( ScChangeActionType eTypeP,
const ScRange& rRange )
:
aBigRange( rRange ),
aDateTime( DateTime::SYSTEM ),
pNext( nullptr ),
pPrev( nullptr ),
pLinkAny( nullptr ),
pLinkDeletedIn( nullptr ),
pLinkDeleted( nullptr ),
pLinkDependent( nullptr ),
nAction( 0 ),
nRejectAction( 0 ),
eType( eTypeP ),
eState( SC_CAS_VIRGIN )
{
aDateTime.ConvertToUTC();
}
ScChangeAction::ScChangeAction(
ScChangeActionType eTypeP, ScBigRange aRange,
const sal_uLong nTempAction,
const sal_uLong nTempRejectAction,
const ScChangeActionState eTempState,
const DateTime& aTempDateTime,
OUString aTempUser, OUString aTempComment) :
aBigRange(std::move( aRange )),
aDateTime( aTempDateTime ),
aUser(std::move( aTempUser )),
aComment(std::move( aTempComment )),
pNext( nullptr ),
pPrev( nullptr ),
pLinkAny( nullptr ),
pLinkDeletedIn( nullptr ),
pLinkDeleted( nullptr ),
pLinkDependent( nullptr ),
nAction( nTempAction ),
nRejectAction( nTempRejectAction ),
eType( eTypeP ),
eState( eTempState )
{
}
ScChangeAction::ScChangeAction( ScChangeActionType eTypeP, ScBigRange aRange,
const sal_uLong nTempAction)
:
aBigRange(std::move( aRange )),
aDateTime( DateTime::SYSTEM ),
pNext( nullptr ),
pPrev( nullptr ),
pLinkAny( nullptr ),
pLinkDeletedIn( nullptr ),
pLinkDeleted( nullptr ),
pLinkDependent( nullptr ),
nAction( nTempAction ),
nRejectAction( 0 ),
eType( eTypeP ),
eState( SC_CAS_VIRGIN )
{
aDateTime.ConvertToUTC();
}
ScChangeAction::~ScChangeAction()
{
RemoveAllLinks();
}
bool ScChangeAction::IsInsertType()
const
{
return eType == SC_CAT_INSERT_COLS || eType == SC_CAT_INSERT_ROWS || eType == SC_CAT_INSERT
_TABS;
}
bool ScChangeAction::IsDeleteType() const
{
return eType == SC_CAT_DELETE_COLS || eType == SC_CAT_DELETE_ROWS || eType == SC_CAT_DELETE_TABS;
}
bool ScChangeAction::IsVirgin() const
{
return eState == SC_CAS_VIRGIN;
}
bool ScChangeAction::IsAccepted() const
{
return eState == SC_CAS_ACCEPTED;
}
bool ScChangeAction::IsRejected() const
{
return eState == SC_CAS_REJECTED;
}
bool ScChangeAction::IsRejecting() const
{
return nRejectAction != 0;
}
bool ScChangeAction::IsVisible() const
{
// sequence order of execution is significant!
if ( IsRejected() || GetType() == SC_CAT_DELETE_TABS || IsDeletedIn() )
return false ;
if ( GetType() == SC_CAT_CONTENT )
return static_cast <const ScChangeActionContent*>(this )->IsTopContent();
return true ;
}
bool ScChangeAction::IsTouchable() const
{
// sequence order of execution is significant!
if ( IsRejected() || GetType() == SC_CAT_REJECT || IsDeletedIn() )
return false ;
// content may reject and be touchable if on top
if ( GetType() == SC_CAT_CONTENT )
return static_cast <const ScChangeActionContent*>(this )->IsTopContent();
if ( IsRejecting() )
return false ;
return true ;
}
bool ScChangeAction::IsClickable() const
{
// sequence order of execution is significant!
if ( !IsVirgin() )
return false ;
if ( IsDeletedIn() )
return false ;
if ( GetType() == SC_CAT_CONTENT )
{
ScChangeActionContentCellType eCCT =
ScChangeActionContent::GetContentCellType(
static_cast <const ScChangeActionContent*>(this )->GetNewCell() );
if ( eCCT == SC_CACCT_MATREF )
return false ;
if ( eCCT == SC_CACCT_MATORG )
{ // no Accept-Select if one of the references is in a deleted col/row
const ScChangeActionLinkEntry* pL =
static_cast <const ScChangeActionContent*>(this )->GetFirstDependentEntry();
while ( pL )
{
ScChangeAction* p = const_cast <ScChangeAction*>(pL->GetAction());
if ( p && p->IsDeletedIn() )
return false ;
pL = pL->GetNext();
}
}
return true ; // for Select() a content doesn't have to be touchable
}
return IsTouchable(); // Accept()/Reject() only on touchables
}
bool ScChangeAction::IsRejectable() const
{
// sequence order of execution is significant!
if ( !IsClickable() )
return false ;
if ( GetType() == SC_CAT_CONTENT )
{
if ( static_cast <const ScChangeActionContent*>(this )->IsOldMatrixReference() )
return false ;
ScChangeActionContent* pNextContent =
static_cast <const ScChangeActionContent*>(this )->GetNextContent();
if ( pNextContent == nullptr )
return true ; // *this is TopContent
return pNextContent->IsRejected(); // *this is next rejectable
}
return IsTouchable();
}
bool ScChangeAction::IsInternalRejectable() const
{
// sequence order of execution is significant!
if ( !IsVirgin() )
return false ;
if ( IsDeletedIn() )
return false ;
if ( GetType() == SC_CAT_CONTENT )
{
ScChangeActionContent* pNextContent =
static_cast <const ScChangeActionContent*>(this )->GetNextContent();
if ( pNextContent == nullptr )
return true ; // *this is TopContent
return pNextContent->IsRejected(); // *this is next rejectable
}
return IsTouchable();
}
bool ScChangeAction::IsDialogRoot() const
{
return IsInternalRejectable(); // only rejectables in root
}
bool ScChangeAction::IsDialogParent() const
{
// sequence order of execution is significant!
if ( GetType() == SC_CAT_CONTENT )
{
if ( !IsDialogRoot() )
return false ;
if ( static_cast <const ScChangeActionContent*>(this )->IsMatrixOrigin() && HasDependent() )
return true ;
ScChangeActionContent* pPrevContent =
static_cast <const ScChangeActionContent*>(this )->GetPrevContent();
return pPrevContent && pPrevContent->IsVirgin();
}
if ( HasDependent() )
return IsDeleteType() || !IsDeletedIn();
if ( HasDeleted() )
{
if ( IsDeleteType() )
{
if ( IsDialogRoot() )
return true ;
ScChangeActionLinkEntry* pL = pLinkDeleted;
while ( pL )
{
ScChangeAction* p = pL->GetAction();
if ( p && p->GetType() != eType )
return true ;
pL = pL->GetNext();
}
}
else
return true ;
}
return false ;
}
bool ScChangeAction::IsMasterDelete() const
{
if ( !IsDeleteType() )
return false ;
const ScChangeActionDel* pDel = static_cast <const ScChangeActionDel*>(this );
return pDel->IsMultiDelete() && (pDel->IsTopDelete() || pDel->IsRejectable());
}
void ScChangeAction::RemoveAllLinks()
{
while (pLinkAny)
{
// coverity[use_after_free] - Moves up by itself
delete pLinkAny;
}
RemoveAllDeletedIn();
while (pLinkDeleted)
{
// coverity[use_after_free] - Moves up by itself
delete pLinkDeleted;
}
RemoveAllDependent();
}
bool ScChangeAction::RemoveDeletedIn( const ScChangeAction* p )
{
bool bRemoved = false ;
ScChangeActionLinkEntry* pL = GetDeletedIn();
while ( pL )
{
ScChangeActionLinkEntry* pNextLink = pL->GetNext();
if ( pL->GetAction() == p )
{
delete pL;
bRemoved = true ;
}
pL = pNextLink;
}
return bRemoved;
}
bool ScChangeAction::IsDeletedIn() const
{
return GetDeletedIn() != nullptr;
}
bool ScChangeAction::IsDeletedIn( const ScChangeAction* p ) const
{
ScChangeActionLinkEntry* pL = GetDeletedIn();
while ( pL )
{
if ( pL->GetAction() == p )
return true ;
pL = pL->GetNext();
}
return false ;
}
void ScChangeAction::RemoveAllDeletedIn()
{
//TODO: Not from TopContent, but really this one
while (pLinkDeletedIn)
{
// coverity[use_after_free] - Moves up by itself
delete pLinkDeletedIn;
}
}
bool ScChangeAction::IsDeletedInDelType( ScChangeActionType eDelType ) const
{
ScChangeActionLinkEntry* pL = GetDeletedIn();
if ( pL )
{
// InsertType for 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 )
{
ScChangeAction* p = pL->GetAction();
if ( p != nullptr && (p->GetType() == eDelType || p->GetType() == eInsType) )
return true ;
pL = pL->GetNext();
}
}
return false ;
}
bool ScChangeAction::HasDependent() const
{
return pLinkDependent != nullptr;
}
bool ScChangeAction::HasDeleted() const
{
return pLinkDeleted != nullptr;
}
void ScChangeAction::SetDeletedIn( ScChangeAction* p )
{
ScChangeActionLinkEntry* pLink1 = new ScChangeActionLinkEntry( GetDeletedInAddress(), p );
ScChangeActionLinkEntry* pLink2;
if ( GetType() == SC_CAT_CONTENT )
pLink2 = p->AddDeleted( static_cast <ScChangeActionContent*>(this )->GetTopContent() );
else
pLink2 = p->AddDeleted( this );
pLink1->SetLink( pLink2 );
}
void ScChangeAction::RemoveAllDependent()
{
while (pLinkDependent)
{
// coverity[use_after_free] - Moves up by itself
delete pLinkDependent;
}
}
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() );
}
OUString ScChangeAction::GetDescription(
ScDocument& /* rDoc */, bool /* bSplitRange */, bool bWarning ) const
{
if (!IsRejecting() || !bWarning)
return OUString();
// 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)
{
return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " " ;
}
if (IsInsertType())
{
return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " " ;
}
const ScChangeTrack* pCT = GetChangeTrack();
if (!pCT)
return OUString();
ScChangeAction* pReject = pCT->GetActionOrGenerated(GetRejectAction());
if (!pReject)
return OUString();
if (pReject->GetType() == SC_CAT_MOVE)
{
return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " " ;
}
if (pReject->IsDeleteType())
{
return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " " ;
}
if (!pReject->HasDependent())
return OUString();
ScChangeActionMap aMap;
pCT->GetDependents( pReject, aMap, false , true );
ScChangeActionMap::iterator itChangeAction = std::find_if(aMap.begin(), aMap.end(),
[&pReject](const ScChangeActionMap::value_type& rEntry) {
return rEntry.second->GetType() == SC_CAT_MOVE || pReject->IsDeleteType(); });
if (itChangeAction == aMap.end())
return OUString();
if ( itChangeAction->second->GetType() == SC_CAT_MOVE)
return ScResId(STR_CHANGED_MOVE_REJECTION_WARNING) + " " ;
else
return ScResId(STR_CHANGED_DELETE_REJECTION_WARNING) + " " ;
}
OUString ScChangeAction::GetRefString(
const ScBigRange& rRange, const ScDocument& rDoc, bool bFlag3D ) const
{
OUStringBuffer aBuf;
ScRefFlags nFlags = ( rRange.IsValid( rDoc ) ? ScRefFlags::VALID : ScRefFlags::ZERO );
if ( nFlags == ScRefFlags::ZERO )
aBuf.append(ScCompiler::GetNativeSymbol(ocErrRef));
else
{
ScRange aTmpRange( rRange.MakeRange( rDoc ) );
switch ( GetType() )
{
case SC_CAT_INSERT_COLS :
case SC_CAT_DELETE_COLS :
if ( bFlag3D )
{
OUString aTmp;
rDoc.GetName( aTmpRange.aStart.Tab(), aTmp );
aBuf.append(aTmp + "." );
}
aBuf.append(ScColToAlpha(aTmpRange.aStart.Col())
+ ":" + ScColToAlpha(aTmpRange.aEnd.Col()));
break ;
case SC_CAT_INSERT_ROWS :
case SC_CAT_DELETE_ROWS :
if ( bFlag3D )
{
OUString aTmp;
rDoc.GetName( aTmpRange.aStart.Tab(), aTmp );
aBuf.append(aTmp + "." );
}
aBuf.append(OUString::number(static_cast <sal_Int64>(aTmpRange.aStart.Row()+1))
+ ":" + OUString::number(static_cast <sal_Int64>(aTmpRange.aEnd.Row()+1)));
break ;
default :
{
if ( bFlag3D || GetType() == SC_CAT_INSERT_TABS )
nFlags |= ScRefFlags::TAB_3D;
aBuf.append(aTmpRange.Format(rDoc, nFlags, rDoc.GetAddressConvention()));
}
}
if ( (bFlag3D && IsDeleteType()) || IsDeletedIn() )
{
aBuf.insert(0, '(' );
aBuf.append(')' );
}
}
return aBuf.makeStringAndClear();
}
void ScChangeAction::SetUser( const OUString& r )
{
aUser = r;
}
void ScChangeAction::SetComment( const OUString& rStr )
{
aComment = rStr;
}
OUString ScChangeAction::GetRefString( ScDocument& rDoc, bool bFlag3D ) const
{
return GetRefString( GetBigRange(), rDoc, 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,
SCCOL nDx, SCROW nDy )
{
// Construct list of Contents
std::vector<ScChangeActionContent*> aContentsList;
for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
{
ScChangeAction* p = pL->GetAction();
if ( p && p->GetType() == SC_CAT_CONTENT )
{
aContentsList.push_back(static_cast <ScChangeActionContent*>(p) );
}
}
SetState( SC_CAS_REJECTED ); // Before UpdateReference for Move
pTrack->UpdateReference( this , true ); // Free LinkDeleted
OSL_ENSURE( !pLinkDeleted, "ScChangeAction::RejectRestoreContents: pLinkDeleted != NULL" );
// Work through list of Contents and delete
ScDocument& rDoc = pTrack->GetDocument();
for (ScChangeActionContent* pContent : aContentsList)
{
if ( !pContent->IsDeletedIn() &&
pContent->GetBigRange().aStart.IsValid( rDoc ) )
pContent->PutNewValueToDoc( &rDoc, nDx, nDy );
}
DeleteCellEntries(); // Remove generated ones
}
void ScChangeAction::SetDeletedInThis( sal_uLong nActionNumber,
const ScChangeTrack* pTrack )
{
if ( nActionNumber )
{
ScChangeAction* pAct = pTrack->GetActionOrGenerated( nActionNumber );
OSL_ENSURE( 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 );
OSL_ENSURE( pAct, "ScChangeAction::AddDependent: missing Action" );
if ( pAct )
{
ScChangeActionLinkEntry* pLink = AddDependent( pAct );
pAct->AddLink( this , pLink );
}
}
}
// ScChangeActionIns
ScChangeActionIns::ScChangeActionIns( const ScDocument* pDoc, const ScRange& rRange, bool bEndOfList ) :
ScChangeAction(SC_CAT_NONE, rRange),
mbEndOfList(bEndOfList)
{
if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == pDoc->MaxCol() )
{
aBigRange.aStart.SetCol( ScBigRange::nRangeMin );
aBigRange.aEnd.SetCol( ScBigRange::nRangeMax );
if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
{
SetType( SC_CAT_INSERT_TABS );
aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
}
else
SetType( SC_CAT_INSERT_ROWS );
}
else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
{
SetType( SC_CAT_INSERT_COLS );
aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
}
else
{
OSL_FAIL( "ScChangeActionIns: Block not supported!" );
}
}
ScChangeActionIns::ScChangeActionIns(
const sal_uLong nActionNumber, const ScChangeActionState eStateP,
const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP,
const OUString& aUserP, const DateTime& aDateTimeP,
const OUString& sComment, const ScChangeActionType eTypeP,
bool bEndOfList ) :
ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
mbEndOfList(bEndOfList)
{
}
ScChangeActionIns::~ScChangeActionIns()
{
}
OUString ScChangeActionIns::GetDescription( ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
{
OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
TranslateId pWhatId;
switch ( GetType() )
{
case SC_CAT_INSERT_COLS :
pWhatId = STR_COLUMN;
break ;
case SC_CAT_INSERT_ROWS :
pWhatId = STR_ROW;
break ;
default :
pWhatId = STR_AREA;
}
OUString aRsc = ScResId(STR_CHANGED_INSERT);
sal_Int32 nPos = aRsc.indexOf("#1" );
if (nPos < 0)
return str;
// Construct a range string to replace '#1' first.
OUString aRangeStr = ScResId(pWhatId) +
" " +
GetRefString(GetBigRange(), rDoc);
aRsc = aRsc.replaceAt(nPos, 2, aRangeStr); // replace '#1' with the range string.
return str + aRsc;
}
bool ScChangeActionIns::IsEndOfList() const
{
return mbEndOfList;
}
bool ScChangeActionIns::Reject( ScDocument& rDoc )
{
if ( !aBigRange.IsValid( rDoc ) )
return false ;
ScRange aRange( aBigRange.MakeRange( rDoc ) );
if ( !rDoc.IsBlockEditable( aRange.aStart.Tab(), aRange.aStart.Col(),
aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row() ) )
return false ;
switch ( GetType() )
{
case SC_CAT_INSERT_COLS :
rDoc.DeleteCol( aRange );
break ;
case SC_CAT_INSERT_ROWS :
rDoc.DeleteRow( aRange );
break ;
case SC_CAT_INSERT_TABS :
rDoc.DeleteTab( aRange.aStart.Tab() );
break ;
default :
{
// added to avoid warnings
}
}
SetState( SC_CAS_REJECTED );
RemoveAllLinks();
return true ;
}
// ScChangeActionDel
ScChangeActionDel::ScChangeActionDel( const ScDocument* pDoc, const ScRange& rRange,
SCCOL nDxP, SCROW nDyP, ScChangeTrack* pTrackP )
:
ScChangeAction( SC_CAT_NONE, rRange ),
pTrack( pTrackP ),
pCutOff( nullptr ),
nCutOff( 0 ),
pLinkMove( nullptr ),
nDx( nDxP ),
nDy( nDyP )
{
if ( rRange.aStart.Col() == 0 && rRange.aEnd.Col() == pDoc->MaxCol() )
{
aBigRange.aStart.SetCol( ScBigRange::nRangeMin );
aBigRange.aEnd.SetCol( ScBigRange::nRangeMax );
if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
{
SetType( SC_CAT_DELETE_TABS );
aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
}
else
SetType( SC_CAT_DELETE_ROWS );
}
else if ( rRange.aStart.Row() == 0 && rRange.aEnd.Row() == pDoc->MaxRow() )
{
SetType( SC_CAT_DELETE_COLS );
aBigRange.aStart.SetRow( ScBigRange::nRangeMin );
aBigRange.aEnd.SetRow( ScBigRange::nRangeMax );
}
else
{
OSL_FAIL( "ScChangeActionDel: Block not supported!" );
}
}
ScChangeActionDel::ScChangeActionDel(
const sal_uLong nActionNumber, const ScChangeActionState eStateP,
const sal_uLong nRejectingNumber, const ScBigRange& aBigRangeP,
const OUString& aUserP, const DateTime& aDateTimeP, const OUString &sComment,
const ScChangeActionType eTypeP, const SCCOLROW nD, ScChangeTrack* pTrackP) : // which of nDx and nDy is set depends on the type
ScChangeAction(eTypeP, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
pTrack( pTrackP ),
pCutOff( nullptr ),
nCutOff( 0 ),
pLinkMove( nullptr ),
nDx( 0 ),
nDy( 0 )
{
if (eType == SC_CAT_DELETE_COLS)
nDx = static_cast <SCCOL>(nD);
else if (eType == SC_CAT_DELETE_ROWS)
nDy = static_cast <SCROW>(nD);
}
ScChangeActionDel::~ScChangeActionDel()
{
DeleteCellEntries();
while (pLinkMove)
{
// coverity[use_after_free] - Moves up by itself
delete pLinkMove;
}
}
void ScChangeActionDel::AddContent( ScChangeActionContent* pContent )
{
mvCells.push_back(pContent);
}
void ScChangeActionDel::DeleteCellEntries()
{
pTrack->DeleteCellEntries( mvCells, this );
}
bool ScChangeActionDel::IsBaseDelete() const
{
return !GetDx() && !GetDy();
}
bool ScChangeActionDel::IsTopDelete() const
{
const ScChangeAction* p = GetNext();
if ( !p || p->GetType() != GetType() )
return true ;
return static_cast <const ScChangeActionDel*>(p)->IsBaseDelete();
}
bool ScChangeActionDel::IsMultiDelete() const
{
if ( GetDx() || GetDy() )
return true ;
const ScChangeAction* p = GetNext();
if ( !p || p->GetType() != GetType() )
return false ;
const ScChangeActionDel* pDel = static_cast <const ScChangeActionDel*>(p);
return (pDel->GetDx() > GetDx() || pDel->GetDy() > GetDy()) &&
pDel->GetBigRange() == aBigRange;
}
bool ScChangeActionDel::IsTabDeleteCol() const
{
if ( GetType() != SC_CAT_DELETE_COLS )
return false ;
const ScChangeAction* p = this ;
while ( p && p->GetType() == SC_CAT_DELETE_COLS &&
!static_cast <const ScChangeActionDel*>(p)->IsTopDelete() )
p = p->GetNext();
return p && p->GetType() == SC_CAT_DELETE_TABS;
}
ScChangeActionDelMoveEntry* ScChangeActionDel::AddCutOffMove(
ScChangeActionMove* pMove, short nFrom, short nTo )
{
return new ScChangeActionDelMoveEntry(&pLinkMove, pMove, nFrom, nTo);
}
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 ;
// Correct in the ones who slipped through
for ( ScChangeActionLinkEntry* pL = pLinkDeleted; pL; pL = pL->GetNext() )
{
ScChangeAction* p = pL->GetAction();
if ( p && p->GetType() == SC_CAT_CONTENT &&
!GetBigRange().Contains( 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;
}
OUString ScChangeActionDel::GetDescription( ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
{
OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
TranslateId pWhatId;
switch ( GetType() )
{
case SC_CAT_DELETE_COLS :
pWhatId = STR_COLUMN;
break ;
case SC_CAT_DELETE_ROWS :
pWhatId = STR_ROW;
break ;
default :
pWhatId = 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() );
}
OUString aRsc = ScResId(STR_CHANGED_DELETE);
sal_Int32 nPos = aRsc.indexOf("#1" );
if (nPos < 0)
return str;
// Build a string to replace with.
OUString aRangeStr = ScResId(pWhatId) + " " +
GetRefString(aTmpRange, rDoc);
aRsc = aRsc.replaceAt(nPos, 2, aRangeStr); // replace '#1' with the string.
return str + aRsc; // append to the original.
}
bool ScChangeActionDel::Reject( ScDocument& rDoc )
{
if ( !aBigRange.IsValid( rDoc ) && GetType() != SC_CAT_DELETE_TABS )
return false ;
if ( IsTopDelete() )
{ // Restore whole section in one go
bool bOk = true ;
ScBigRange aTmpRange( GetOverAllRange() );
if ( !aTmpRange.IsValid( rDoc ) )
{
if ( GetType() == SC_CAT_DELETE_TABS )
{ // Do we attach a Tab?
if ( aTmpRange.aStart.Tab() > rDoc.GetMaxTableNumber() )
bOk = false ;
}
else
bOk = false ;
}
if ( bOk )
{
ScRange aRange( aTmpRange.MakeRange( rDoc ) );
// InDelete... for formula UpdateReference in Document
pTrack->SetInDeleteRange( aRange );
pTrack->SetInDeleteTop( true );
pTrack->SetInDeleteUndo( true );
pTrack->SetInDelete( true );
switch ( GetType() )
{
case SC_CAT_DELETE_COLS :
if ( aRange.aStart.Col() != 0 || aRange.aEnd.Col() != rDoc.MaxCol() )
{ // Only if not TabDelete
bOk = rDoc.CanInsertCol( aRange ) && rDoc.InsertCol( aRange );
}
break ;
case SC_CAT_DELETE_ROWS :
bOk = rDoc.CanInsertRow( aRange ) && rDoc.InsertRow( aRange );
break ;
case SC_CAT_DELETE_TABS :
{
//TODO: Remember table names?
OUString aName;
rDoc.CreateValidTabName( aName );
bOk = rDoc.ValidNewTabName( aName ) && rDoc.InsertTab( aRange.aStart.Tab(), aName );
}
break ;
default :
{
// added to avoid warnings
}
}
pTrack->SetInDelete( false );
pTrack->SetInDeleteUndo( false );
}
if ( !bOk )
{
pTrack->SetInDeleteTop( false );
return false ;
}
// Keep InDeleteTop for UpdateReference Undo
}
// Sets rejected and calls UpdateReference-Undo and DeleteCellEntries
RejectRestoreContents( pTrack, GetDx(), GetDy() );
pTrack->SetInDeleteTop( false );
RemoveAllLinks();
return true ;
}
void ScChangeActionDel::UndoCutOffMoves()
{ // Restore cut off Moves; delete Entries/Links
while ( pLinkMove )
{
// coverity[deref_arg] - the call on delete pLinkMove at the block end Moves a new entry into pLinkMode by itself
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; // Moves up by itself
}
}
void ScChangeActionDel::UndoCutOffInsert()
{ //Restore cut off Insert
if ( !pCutOff )
return ;
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( nullptr, 0 );
}
// ScChangeActionMove
ScChangeActionMove::ScChangeActionMove(
const sal_uLong nActionNumber, const ScChangeActionState eStateP,
const sal_uLong nRejectingNumber, const ScBigRange& aToBigRange,
const OUString& aUserP, const DateTime& aDateTimeP,
const OUString &sComment, ScBigRange aFromBigRange,
ScChangeTrack* pTrackP) : // which of nDx and nDy is set depends on the type
ScChangeAction(SC_CAT_MOVE, aToBigRange, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
aFromRange(std::move(aFromBigRange)),
pTrack( pTrackP ),
nStartLastCut(0),
nEndLastCut(0)
{
}
ScChangeActionMove::~ScChangeActionMove()
{
DeleteCellEntries();
}
void ScChangeActionMove::AddContent( ScChangeActionContent* pContent )
{
mvCells.push_back(pContent);
}
void ScChangeActionMove::DeleteCellEntries()
{
pTrack->DeleteCellEntries( mvCells, 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();
}
OUString ScChangeActionMove::GetDescription(
ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
{
OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
bool bFlag3D = GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab();
OUString aRsc = ScResId(STR_CHANGED_MOVE);
OUString aTmpStr = ScChangeAction::GetRefString(GetFromRange(), rDoc, bFlag3D);
sal_Int32 nPos = aRsc.indexOf("#1" );
if (nPos >= 0)
{
aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
nPos += aTmpStr.getLength();
}
aTmpStr = ScChangeAction::GetRefString(GetBigRange(), rDoc, bFlag3D);
nPos = nPos >= 0 ? aRsc.indexOf("#2" , nPos) : -1;
if (nPos >= 0)
{
aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
}
return str + aRsc; // append to the original string.
}
OUString ScChangeActionMove::GetRefString( ScDocument& rDoc, bool bFlag3D ) const
{
if ( !bFlag3D )
bFlag3D = ( GetFromRange().aStart.Tab() != GetBigRange().aStart.Tab() );
return ScChangeAction::GetRefString(GetFromRange(), rDoc, bFlag3D)
+ ", "
+ ScChangeAction::GetRefString(GetBigRange(), rDoc, bFlag3D);
}
bool ScChangeActionMove::Reject( ScDocument& rDoc )
{
if ( !(aBigRange.IsValid( rDoc ) && aFromRange.IsValid( rDoc )) )
return false ;
ScRange aToRange( aBigRange.MakeRange( rDoc ) );
ScRange aFrmRange( aFromRange.MakeRange( rDoc ) );
bool bOk = rDoc.IsBlockEditable( aToRange.aStart.Tab(),
aToRange.aStart.Col(), aToRange.aStart.Row(),
aToRange.aEnd.Col(), aToRange.aEnd.Row() );
if ( bOk )
bOk = rDoc.IsBlockEditable( aFrmRange.aStart.Tab(),
aFrmRange.aStart.Col(), aFrmRange.aStart.Row(),
aFrmRange.aEnd.Col(), aFrmRange.aEnd.Row() );
if ( !bOk )
return false ;
pTrack->LookUpContents( aToRange, &rDoc, 0, 0, 0 ); // Contents to be moved
rDoc.DeleteAreaTab( aToRange, InsertDeleteFlags::ALL );
rDoc.DeleteAreaTab( aFrmRange, InsertDeleteFlags::ALL );
// Adjust formula in the Document
sc::RefUpdateContext aCxt(rDoc);
aCxt.meMode = URM_MOVE;
aCxt.maRange = aFrmRange;
aCxt.mnColDelta = aFrmRange.aStart.Col() - aToRange.aStart.Col();
aCxt.mnRowDelta = aFrmRange.aStart.Row() - aToRange.aStart.Row();
aCxt.mnTabDelta = aFrmRange.aStart.Tab() - aToRange.aStart.Tab();
rDoc.UpdateReference(aCxt);
// Free LinkDependent, set succeeding UpdateReference Undo
// ToRange->FromRange Dependents
RemoveAllDependent();
// Sets rejected and calls UpdateReference Undo and DeleteCellEntries
RejectRestoreContents( pTrack, 0, 0 );
while ( pLinkDependent )
{
ScChangeAction* p = pLinkDependent->GetAction();
if ( p && p->GetType() == SC_CAT_CONTENT )
{
ScChangeActionContent* pContent = static_cast <ScChangeActionContent*>(p);
if ( !pContent->IsDeletedIn() &&
pContent->GetBigRange().aStart.IsValid( rDoc ) )
pContent->PutNewValueToDoc( &rDoc, 0, 0 );
// Delete the ones created in LookUpContents
if ( pTrack->IsGenerated( pContent->GetActionNumber() ) &&
!pContent->IsDeletedIn() )
{
pLinkDependent->UnLink(); // Else this one is also deleted!
pTrack->DeleteGeneratedDelContent( pContent );
}
}
delete pLinkDependent;
}
RemoveAllLinks();
return true ;
}
ScChangeActionContent::ScChangeActionContent( const ScRange& rRange ) :
ScChangeAction(SC_CAT_CONTENT, rRange),
pNextContent(nullptr),
pPrevContent(nullptr),
pNextInSlot(nullptr),
ppPrevInSlot(nullptr)
{}
ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
const ScChangeActionState eStateP, const sal_uLong nRejectingNumber,
const ScBigRange& aBigRangeP, const OUString& aUserP,
const DateTime& aDateTimeP, const OUString& sComment,
ScCellValue aOldCell, const ScDocument& rDoc, const OUString& sOldValue ) :
ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment),
maOldCell(std::move(aOldCell)),
maOldValue(sOldValue),
pNextContent(nullptr),
pPrevContent(nullptr),
pNextInSlot(nullptr),
ppPrevInSlot(nullptr)
{
if (!maOldCell.isEmpty())
SetCell(maOldValue, maOldCell, 0, rDoc);
if (!sOldValue.isEmpty()) // #i40704# don't overwrite SetCell result with empty string
maOldValue = sOldValue; // set again, because SetCell removes it
}
ScChangeActionContent::ScChangeActionContent( const sal_uLong nActionNumber,
ScCellValue aNewCell, const ScBigRange& aBigRangeP,
const ScDocument& rDoc, const OUString& sNewValue ) :
ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber),
maNewCell(std::move(aNewCell)),
maNewValue(sNewValue),
pNextContent(nullptr),
pPrevContent(nullptr),
pNextInSlot(nullptr),
ppPrevInSlot(nullptr)
{
if (!maNewCell.isEmpty())
SetCell(maNewValue, maNewCell, 0, rDoc);
if (!sNewValue.isEmpty()) // #i40704# don't overwrite SetCell result with empty string
maNewValue = 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 const_cast <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 ScCellValue& rCell, const ScDocument* pFromDoc, ScDocument* pToDoc, sal_uLong nFormat )
{
SetValue(maOldValue, maOldCell, nFormat, rCell, pFromDoc, pToDoc);
}
void ScChangeActionContent::SetOldValue(
const ScCellValue& rCell, const ScDocument* pFromDoc, ScDocument* pToDoc )
{
SetValue(maOldValue, maOldCell, aBigRange.aStart.MakeAddress(*pFromDoc), rCell, pFromDoc, pToDoc);
}
void ScChangeActionContent::SetNewValue( const ScCellValue& rCell, ScDocument* pDoc )
{
SetValue(maNewValue, maNewCell, aBigRange.aStart.MakeAddress(*pDoc), rCell, pDoc, pDoc);
}
void ScChangeActionContent::SetOldNewCells(
const ScCellValue& rOldCell, sal_uLong nOldFormat, const ScCellValue& rNewCell,
sal_uLong nNewFormat, const ScDocument& rDoc )
{
maOldCell = rOldCell;
maNewCell = rNewCell;
SetCell(maOldValue, maOldCell, nOldFormat, rDoc);
SetCell(maNewValue, maNewCell, nNewFormat, rDoc);
}
void ScChangeActionContent::SetNewCell(
const ScCellValue& rCell, const ScDocument& rDoc, const OUString& rFormatted )
{
maNewCell = rCell;
SetCell(maNewValue, maNewCell, 0, rDoc);
// #i40704# allow to set formatted text here - don't call SetNewValue with string from XML filter
if (!rFormatted.isEmpty())
maNewValue = rFormatted;
}
void ScChangeActionContent::SetValueString(
OUString& rValue, ScCellValue& rCell, const OUString& rStr, ScDocument* pDoc )
{
rCell.clear();
if ( rStr.getLength() > 1 && rStr[0] == '=' )
{
rValue.clear();
rCell.set(new ScFormulaCell(
*pDoc, aBigRange.aStart.MakeAddress(*pDoc), rStr,
pDoc->GetGrammar() ));
rCell.getFormula()->SetInChangeTrack(true );
}
else
rValue = rStr;
}
void ScChangeActionContent::SetOldValue( const OUString& rOld, ScDocument* pDoc )
{
SetValueString(maOldValue, maOldCell, rOld, pDoc);
}
OUString ScChangeActionContent::GetOldString( const ScDocument& rDoc ) const
{
return GetValueString(maOldValue, maOldCell, rDoc);
}
OUString ScChangeActionContent::GetNewString( const ScDocument& rDoc ) const
{
return GetValueString(maNewValue, maNewCell, rDoc);
}
OUString ScChangeActionContent::GetDescription(
ScDocument& rDoc, bool bSplitRange, bool bWarning ) const
{
OUString str = ScChangeAction::GetDescription( rDoc, bSplitRange, bWarning );
OUString aRsc = ScResId(STR_CHANGED_CELL);
OUString aTmpStr = GetRefString(rDoc);
sal_Int32 nPos = aRsc.indexOf("#1" , 0);
if (nPos >= 0)
{
aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
nPos += aTmpStr.getLength();
}
aTmpStr = GetOldString( rDoc );
if (aTmpStr.isEmpty())
aTmpStr = ScResId( STR_CHANGED_BLANK );
nPos = nPos >= 0 ? aRsc.indexOf("#2" , nPos) : -1;
if (nPos >= 0)
{
aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
nPos += aTmpStr.getLength();
}
aTmpStr = GetNewString( rDoc );
if (aTmpStr.isEmpty())
aTmpStr = ScResId( STR_CHANGED_BLANK );
nPos = nPos >= 0 ? aRsc.indexOf("#3" , nPos) : -1;
if (nPos >= 0)
{
aRsc = aRsc.replaceAt(nPos, 2, aTmpStr);
}
return str + aRsc; // append to the original string.
}
OUString ScChangeActionContent::GetRefString(
ScDocument& rDoc, bool bFlag3D ) const
{
ScRefFlags nFlags = ( GetBigRange().IsValid( rDoc ) ? ScRefFlags::VALID : ScRefFlags::ZERO );
if ( nFlags != ScRefFlags::ZERO )
{
const ScCellValue& rCell = GetNewCell();
if ( GetContentCellType(rCell) == SC_CACCT_MATORG )
{
ScBigRange aLocalBigRange( GetBigRange() );
SCCOL nC;
SCROW nR;
rCell.getFormula()->GetMatColsRows( nC, nR );
aLocalBigRange.aEnd.IncCol( nC-1 );
aLocalBigRange.aEnd.IncRow( nR-1 );
return ScChangeAction::GetRefString( aLocalBigRange, rDoc, bFlag3D );
}
ScAddress aTmpAddress( GetBigRange().aStart.MakeAddress( rDoc ) );
if ( bFlag3D )
nFlags |= ScRefFlags::TAB_3D;
OUString str = aTmpAddress.Format(nFlags, &rDoc, rDoc.GetAddressConvention());
if ( IsDeletedIn() )
{
// Insert the parentheses.
str = "(" + str + ")" ;
}
return str;
}
else
return ScCompiler::GetNativeSymbol(ocErrRef);
}
bool ScChangeActionContent::Reject( ScDocument& rDoc )
{
if ( !aBigRange.IsValid( rDoc ) )
return false ;
PutOldValueToDoc( &rDoc, 0, 0 );
SetState( SC_CAS_REJECTED );
RemoveAllLinks();
return true ;
}
bool ScChangeActionContent::Select( ScDocument& rDoc, ScChangeTrack* pTrack,
bool bOldest, ::std::stack<ScChangeActionContent*>* pRejectActions )
{
if ( !aBigRange.IsValid( rDoc ) )
return false ;
ScChangeActionContent* pContent = this ;
// accept previous contents
while ( ( pContent = pContent->pPrevContent ) != nullptr )
{
if ( pContent->IsVirgin() )
pContent->SetState( SC_CAS_ACCEPTED );
}
ScChangeActionContent* pEnd = pContent = this ;
// reject subsequent contents
while ( ( pContent = pContent->pNextContent ) != nullptr )
{
// MatrixOrigin may have dependents, no dependency recursion needed
const ScChangeActionLinkEntry* pL = pContent->GetFirstDependentEntry();
while ( pL )
{
ScChangeAction* p = const_cast <ScChangeAction*>(pL->GetAction());
if ( p )
p->SetRejected();
pL = pL->GetNext();
}
pContent->SetRejected();
pEnd = pContent;
}
// If not oldest: Is it anyone else than the last one?
if ( bOldest || pEnd != this )
{ ScRange aRange( aBigRange.aStart.MakeAddress( rDoc ) );
const ScAddress& rPos = aRange.aStart;
ScChangeActionContent* pNew = new ScChangeActionContent( aRange );
ScCellValue aCell;
aCell.assign(rDoc, rPos);
pNew->SetOldValue(aCell, &rDoc, &rDoc);
if ( bOldest )
PutOldValueToDoc( &rDoc, 0, 0 );
else
PutNewValueToDoc( &rDoc, 0, 0 );
pNew->SetRejectAction( bOldest ? GetActionNumber() : pEnd->GetActionNumber() );
pNew->SetState( SC_CAS_ACCEPTED );
if ( pRejectActions )
pRejectActions->push( pNew );
else
{
aCell.assign(rDoc, rPos);
pNew->SetNewValue(aCell, &rDoc);
pTrack->Append( pNew );
}
}
if ( bOldest )
SetRejected();
else
SetState( SC_CAS_ACCEPTED );
return true ;
}
OUString ScChangeActionContent::GetStringOfCell(
const ScCellValue& rCell, const ScDocument& rDoc, const ScAddress& rPos )
{
if (NeedsNumberFormat(rCell))
return GetStringOfCell(rCell, rDoc, rDoc.GetNumberFormat(ScRange(rPos)));
else
return GetStringOfCell(rCell, rDoc, 0);
}
OUString ScChangeActionContent::GetStringOfCell(
const ScCellValue& rCell, const ScDocument& rDoc, sal_uLong nFormat )
{
if (!GetContentCellType(rCell))
return OUString();
switch (rCell.getType())
{
case CELLTYPE_VALUE:
return rDoc.GetFormatTable()->GetInputLineString(rCell.getDouble(), nFormat);
case CELLTYPE_STRING:
return rCell.getSharedString()->getString();
case CELLTYPE_EDIT:
if (rCell.getEditText())
return ScEditUtil::GetString(*rCell.getEditText(), rDoc);
return OUString();
case CELLTYPE_FORMULA:
return rCell.getFormula()->GetFormula();
default :
return OUString();
}
}
ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScCellValue& rCell )
{
switch (rCell.getType())
{
case CELLTYPE_VALUE :
case CELLTYPE_STRING :
case CELLTYPE_EDIT :
return SC_CACCT_NORMAL;
case CELLTYPE_FORMULA :
switch (rCell.getFormula()->GetMatrixFlag())
{
case ScMatrixMode::NONE :
return SC_CACCT_NORMAL;
case ScMatrixMode::Formula :
return SC_CACCT_MATORG;
case ScMatrixMode::Reference :
return SC_CACCT_MATREF;
}
return SC_CACCT_NORMAL;
default :
return SC_CACCT_NONE;
}
}
ScChangeActionContentCellType ScChangeActionContent::GetContentCellType( const ScRefCellValue& rCell )
{
switch (rCell.getType())
{
case CELLTYPE_VALUE:
case CELLTYPE_STRING:
case CELLTYPE_EDIT:
return SC_CACCT_NORMAL;
case CELLTYPE_FORMULA:
{
const ScFormulaCell* pCell = rCell.getFormula();
switch (pCell->GetMatrixFlag())
{
case ScMatrixMode::NONE :
return SC_CACCT_NORMAL;
case ScMatrixMode::Formula :
return SC_CACCT_MATORG;
case ScMatrixMode::Reference :
return SC_CACCT_MATREF;
}
return SC_CACCT_NORMAL;
}
default :
;
}
return SC_CACCT_NONE;
}
bool ScChangeActionContent::NeedsNumberFormat( const ScCellValue& rVal )
{
return rVal.getType() == CELLTYPE_VALUE;
}
void ScChangeActionContent::SetValue(
OUString& rStr, ScCellValue& rCell, const ScAddress& rPos, const ScCellValue& rOrgCell,
const ScDocument* pFromDoc, ScDocument* pToDoc )
{
sal_uInt32 nFormat = NeedsNumberFormat(rOrgCell) ? pFromDoc->GetNumberFormat(ScRange(rPos)) : 0;
SetValue(rStr, rCell, nFormat, rOrgCell, pFromDoc, pToDoc);
}
void ScChangeActionContent::SetValue(
OUString& rStr, ScCellValue& rCell, sal_uLong nFormat, const ScCellValue& rOrgCell,
const ScDocument* pFromDoc, ScDocument* pToDoc )
{
rStr.clear();
if (GetContentCellType(rOrgCell))
{
rCell.assign(rOrgCell, *pToDoc);
switch (rOrgCell.getType())
{
case CELLTYPE_VALUE :
{ // E.g.: Remember date as such
rStr = pFromDoc->GetFormatTable()->GetInputLineString(
rOrgCell.getDouble(), nFormat);
}
break ;
case CELLTYPE_FORMULA :
rCell.getFormula()->SetInChangeTrack(true );
break ;
default :
{
// added to avoid warnings
}
}
}
else
rCell.clear();
}
void ScChangeActionContent::SetCell( OUString& rStr, const ScCellValue& rCell, sal_uLong nFormat, const ScDocument& rDoc )
{
rStr.clear();
if (rCell.isEmpty())
return ;
switch (rCell.getType())
{
case CELLTYPE_VALUE :
// e.g. remember date as date string
rStr = rDoc.GetFormatTable()->GetInputLineString(rCell.getDouble(), nFormat);
break ;
case CELLTYPE_FORMULA :
rCell.getFormula()->SetInChangeTrack(true );
break ;
default :
{
// added to avoid warnings
}
}
}
OUString ScChangeActionContent::GetValueString(
const OUString& rValue, const ScCellValue& rCell, const ScDocument& rDoc ) const
{
if (!rValue.isEmpty())
{
return rValue;
}
switch (rCell.getType())
{
case CELLTYPE_STRING :
return rCell.getSharedString()->getString();
case CELLTYPE_EDIT :
if (rCell.getEditText())
return ScEditUtil::GetString(*rCell.getEditText(), rDoc);
return OUString();
case CELLTYPE_VALUE : // Is always in rValue
return rValue;
case CELLTYPE_FORMULA :
return GetFormulaString(rCell.getFormula());
case CELLTYPE_NONE:
default :
return OUString();
}
}
OUString ScChangeActionContent::GetFormulaString(
const ScFormulaCell* pCell ) const
{
ScAddress aPos( aBigRange.aStart.MakeAddress( pCell->GetDocument()) );
if ( aPos == pCell->aPos || IsDeletedIn() )
return pCell->GetFormula();
else
{
OSL_FAIL( "ScChangeActionContent::GetFormulaString: aPos != pCell->aPos" );
ScFormulaCell aNew( *pCell, pCell->GetDocument(), aPos );
return aNew.GetFormula();
}
}
void ScChangeActionContent::PutOldValueToDoc( ScDocument* pDoc,
SCCOL nDx, SCROW nDy ) const
{
PutValueToDoc(maOldCell, maOldValue, pDoc, nDx, nDy);
}
void ScChangeActionContent::PutNewValueToDoc( ScDocument* pDoc,
SCCOL nDx, SCROW nDy ) const
{
PutValueToDoc(maNewCell, maNewValue, pDoc, nDx, nDy);
}
void ScChangeActionContent::PutValueToDoc(
const ScCellValue& rCell, const OUString& rValue, ScDocument* pDoc,
SCCOL nDx, SCROW nDy ) const
{
ScAddress aPos( aBigRange.aStart.MakeAddress( *pDoc ) );
if ( nDx )
aPos.IncCol( nDx );
if ( nDy )
aPos.IncRow( nDy );
if (!rValue.isEmpty())
{
pDoc->SetString(aPos, rValue);
return ;
}
if (rCell.isEmpty())
{
pDoc->SetEmptyCell(aPos);
return ;
}
if (rCell.getType() == CELLTYPE_VALUE)
{
pDoc->SetString( aPos.Col(), aPos.Row(), aPos.Tab(), rValue );
return ;
}
switch (GetContentCellType(rCell))
{
case SC_CACCT_MATORG :
{
SCCOL nC;
SCROW nR;
rCell.getFormula()->GetMatColsRows(nC, nR);
OSL_ENSURE( 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(pDoc->GetSheetLimits());
aDestMark.SelectOneTable( aPos.Tab() );
aDestMark.SetMarkArea( aRange );
pDoc->InsertMatrixFormula( aPos.Col(), aPos.Row(),
aRange.aEnd.Col(), aRange.aEnd.Row(),
aDestMark, OUString(), rCell.getFormula()->GetCode());
}
break ;
case SC_CACCT_MATREF :
// nothing
break ;
default :
rCell.commit(*pDoc, aPos);
}
}
static void lcl_InvalidateReference( const ScDocument& rDoc, formula::FormulaToken& rTok, const ScBigAddress& rPos )
{
ScSingleRefData& rRef1 = *rTok.GetSingleRef();
if ( rPos.Col() < 0 || rDoc.MaxCol() < rPos.Col() )
{
rRef1.SetColDeleted( true );
}
if ( rPos.Row() < 0 || rDoc.MaxRow() < rPos.Row() )
{
rRef1.SetRowDeleted( true );
}
if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
{
rRef1.SetTabDeleted( true );
}
if ( rTok.GetType() != formula::svDoubleRef )
return ;
ScSingleRefData& rRef2 = rTok.GetDoubleRef()->Ref2;
if ( rPos.Col() < 0 || rDoc.MaxCol() < rPos.Col() )
{
rRef2.SetColDeleted( true );
}
if ( rPos.Row() < 0 || rDoc.MaxRow() < rPos.Row() )
{
rRef2.SetRowDeleted( true );
}
if ( rPos.Tab() < 0 || MAXTAB < rPos.Tab() )
{
rRef2.SetTabDeleted( true );
}
}
void ScChangeActionContent::UpdateReference( const ScChangeTrack* pTrack,
UpdateRefMode eMode, const ScBigRange& rRange,
sal_Int32 nDx, sal_Int32 nDy, sal_Int32 nDz )
{
SCSIZE nOldSlot = pTrack->ComputeContentSlot( aBigRange.aStart.Row() );
ScRefUpdate::Update( eMode, rRange, nDx, nDy, nDz, aBigRange );
SCSIZE nNewSlot = pTrack->ComputeContentSlot( aBigRange.aStart.Row() );
if ( nNewSlot != nOldSlot )
{
RemoveFromSlot();
InsertInSlot( &(pTrack->GetContentSlots()[nNewSlot]) );
}
if ( pTrack->IsInDelete() && !pTrack->IsInDeleteTop() )
return ; // Formula only update whole range
bool bOldFormula = maOldCell.getType() == CELLTYPE_FORMULA;
bool bNewFormula = maNewCell.getType() == CELLTYPE_FORMULA;
if ( !(bOldFormula || bNewFormula) )
return ;
// Adjust UpdateReference via ScFormulaCell (there)
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 starts there after removed range
// Position is changed there
if ( nDx )
aTmpRange.aStart.IncCol( -nDx );
if ( nDy )
aTmpRange.aStart.IncRow( -nDy );
if ( nDz )
aTmpRange.aStart.IncTab( -nDz );
}
break ;
case URM_MOVE :
// Move is Source here and Target there
// Position needs to be adjusted before that
if ( bOldFormula )
maOldCell.getFormula()->aPos = aBigRange.aStart.MakeAddress(pTrack->GetDocument());
if ( bNewFormula )
maNewCell.getFormula()->aPos = aBigRange.aStart.MakeAddress(pTrack->GetDocument());
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(pTrack->GetDocument()) );
sc::RefUpdateContext aRefCxt(pTrack->GetDocument());
aRefCxt.meMode = eMode;
aRefCxt.maRange = aRange;
aRefCxt.mnColDelta = nDx;
aRefCxt.mnRowDelta = nDy;
aRefCxt.mnTabDelta = nDz;
if ( bOldFormula )
maOldCell.getFormula()->UpdateReference(aRefCxt);
if ( bNewFormula )
maNewCell.getFormula()->UpdateReference(aRefCxt);
if ( aBigRange.aStart.IsValid( pTrack->GetDocument() ) )
return ;
//FIXME:
// UpdateReference cannot handle positions outside of the Document.
// Therefore set everything to #REF!
//TODO: Remove the need for this hack! This means big changes to ScAddress etc.!
const ScBigAddress& rPos = aBigRange.aStart;
if ( bOldFormula )
{
formula::FormulaToken* t;
ScTokenArray* pArr = maOldCell.getFormula()->GetCode();
formula::FormulaTokenArrayPlainIterator aIter(*pArr);
while ( ( t = aIter.GetNextReference() ) != nullptr )
lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
aIter.Reset();
while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
}
if ( bNewFormula )
{
formula::FormulaToken* t;
ScTokenArray* pArr = maNewCell.getFormula()->GetCode();
formula::FormulaTokenArrayPlainIterator aIter(*pArr);
while ( ( t = aIter.GetNextReference() ) != nullptr )
lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
aIter.Reset();
while ( ( t = aIter.GetNextReferenceRPN() ) != nullptr )
lcl_InvalidateReference( pTrack->GetDocument(), *t, rPos );
}
}
bool ScChangeActionContent::IsMatrixOrigin() const
{
return GetContentCellType(GetNewCell()) == SC_CACCT_MATORG;
}
bool ScChangeActionContent::IsOldMatrixReference() const
{
return GetContentCellType(GetOldCell()) == SC_CACCT_MATREF;
}
// ScChangeActionReject
ScChangeActionReject::ScChangeActionReject(
const sal_uLong nActionNumber, const ScChangeActionState eStateP,
const sal_uLong nRejectingNumber,
const ScBigRange& aBigRangeP, const OUString& aUserP,
const DateTime& aDateTimeP, const OUString& sComment) :
ScChangeAction(SC_CAT_CONTENT, aBigRangeP, nActionNumber, nRejectingNumber, eStateP, aDateTimeP, aUserP, sComment)
{
}
bool ScChangeActionReject::Reject(ScDocument& /*rDoc*/)
{
return false ;
}
SCSIZE ScChangeTrack::ComputeContentSlot( sal_Int32 nRow ) const
{
if ( nRow < 0 || nRow > rDoc.GetSheetLimits().mnMaxRow )
return mnContentSlots - 1;
return static_cast < SCSIZE >( nRow / mnContentRowsPerSlot );
}
SCROW ScChangeTrack::InitContentRowsPerSlot()
{
const SCSIZE nMaxSlots = 0xffe0 / sizeof ( ScChangeActionContent* ) - 2;
SCROW nRowsPerSlot = rDoc.GetMaxRowCount() / nMaxSlots;
if ( nRowsPerSlot * nMaxSlots < sal::static_int_cast<SCSIZE>(rDoc.GetMaxRowCount()) )
++nRowsPerSlot;
return nRowsPerSlot;
}
ScChangeTrack::ScChangeTrack( ScDocument& rDocP ) :
aFixDateTime( DateTime::SYSTEM ),
rDoc( rDocP )
{
Init();
ScModule::get()->GetUserOptions().AddListener(this );
ppContentSlots.reset( new ScChangeActionContent* [ mnContentSlots ] );
memset( ppContentSlots.get(), 0, mnContentSlots * sizeof ( ScChangeActionContent* ) );
}
ScChangeTrack::ScChangeTrack( ScDocument& rDocP, std::set<OUString>&& aTempUserCollection) :
maUserCollection(std::move(aTempUserCollection)),
aFixDateTime( DateTime::SYSTEM ),
rDoc( rDocP )
{
Init();
ScModule::get()->GetUserOptions().AddListener(this );
ppContentSlots.reset( new ScChangeActionContent* [ mnContentSlots ] );
memset( ppContentSlots.get(), 0, mnContentSlots * sizeof ( ScChangeActionContent* ) );
}
ScChangeTrack::~ScChangeTrack()
{
ScModule::get()->GetUserOptions().RemoveListener(this );
DtorClear();
}
void ScChangeTrack::Init()
{
mnContentRowsPerSlot = InitContentRowsPerSlot();
mnContentSlots = rDoc.GetMaxRowCount() / InitContentRowsPerSlot() + 2;
pFirst = nullptr;
pLast = nullptr;
pFirstGeneratedDelContent = nullptr;
pLastCutMove = nullptr;
pLinkInsertCol = nullptr;
pLinkInsertRow = nullptr;
pLinkInsertTab = nullptr;
pLinkMove = nullptr;
xBlockModifyMsg.reset();
nActionMax = 0;
nGeneratedMin = SC_CHGTRACK_GENERATED_START;
nMarkLastSaved = 0;
nStartLastCut = 0;
nEndLastCut = 0;
nLastMerge = 0;
eMergeState = SC_CTMS_NONE;
bInDelete = false ;
bInDeleteTop = false ;
bInDeleteUndo = false ;
bInPasteCut = false ;
bUseFixDateTime = false ;
bTimeNanoSeconds = true ;
CreateAuthorName();
}
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 ( const auto & rEntry : aPasteCutMap )
{
delete rEntry.second;
}
pLastCutMove.reset();
ClearMsgQueue();
}
void ScChangeTrack::ClearMsgQueue()
{
xBlockModifyMsg.reset();
aMsgStackTmp.clear();
aMsgStackFinal.clear();
aMsgQueue.clear();
}
void ScChangeTrack::Clear()
{
DtorClear();
aMap.clear();
aGeneratedMap.clear();
aPasteCutMap.clear();
maUserCollection.clear();
maUser.clear();
Init();
}
bool ScChangeTrack::IsGenerated( sal_uLong nAction ) const
{
return nAction >= nGeneratedMin;
}
ScChangeAction* ScChangeTrack::GetAction( sal_uLong nAction ) const
{
ScChangeActionMap::const_iterator it = aMap.find( nAction );
if ( it != aMap.end() )
return it->second;
else
return nullptr;
}
ScChangeAction* ScChangeTrack::GetGenerated( sal_uLong nGenerated ) const
{
ScChangeActionMap::const_iterator it = aGeneratedMap.find( nGenerated );
if ( it != aGeneratedMap.end() )
return it->second;
else
return nullptr;
}
ScChangeAction* ScChangeTrack::GetActionOrGenerated( sal_uLong nAction ) const
{
return IsGenerated( nAction ) ?
--> --------------------
--> maximum size reached
--> --------------------
Messung V0.5 C=94 H=94 G=93
¤ Dauer der Verarbeitung: 0.22 Sekunden
¤
*© Formatika GbR, Deutschland