5457 lines
190 KiB
C++
5457 lines
190 KiB
C++
/* -*- 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 <functional>
|
|
|
|
#include <string.h>
|
|
#include <osl/diagnose.h>
|
|
#include <sal/log.hxx>
|
|
|
|
#include <token.hxx>
|
|
#include <tokenarray.hxx>
|
|
#include <reftokenhelper.hxx>
|
|
#include <clipparam.hxx>
|
|
#include <compiler.hxx>
|
|
#include <interpre.hxx>
|
|
#include <formula/FormulaCompiler.hxx>
|
|
#include <formula/compiler.hxx>
|
|
#include <formula/opcode.hxx>
|
|
#include <jumpmatrix.hxx>
|
|
#include <rangeseq.hxx>
|
|
#include <rangeutl.hxx>
|
|
#include <externalrefmgr.hxx>
|
|
#include <document.hxx>
|
|
#include <refupdatecontext.hxx>
|
|
#include <tokenstringcontext.hxx>
|
|
#include <types.hxx>
|
|
#include <addincol.hxx>
|
|
#include <dbdata.hxx>
|
|
#include <reordermap.hxx>
|
|
#include <svl/sharedstring.hxx>
|
|
#include <scmatrix.hxx>
|
|
|
|
#include <com/sun/star/sheet/ComplexReference.hpp>
|
|
#include <com/sun/star/sheet/ExternalReference.hpp>
|
|
#include <com/sun/star/sheet/FormulaToken.hpp>
|
|
#include <com/sun/star/sheet/ReferenceFlags.hpp>
|
|
#include <com/sun/star/sheet/NameToken.hpp>
|
|
#include <com/sun/star/sheet/TableRefToken.hpp>
|
|
#include <utility>
|
|
#include <o3tl/safeint.hxx>
|
|
#include <o3tl/sorted_vector.hxx>
|
|
|
|
using ::std::vector;
|
|
using namespace formula;
|
|
using namespace com::sun::star;
|
|
|
|
namespace
|
|
{
|
|
void lcl_SingleRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI )
|
|
{
|
|
rRef.InitFlags();
|
|
|
|
rRef.SetColRel( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 );
|
|
rRef.SetRowRel( ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE ) != 0 );
|
|
rRef.SetTabRel( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_RELATIVE ) != 0 );
|
|
rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED ) != 0 );
|
|
rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED ) != 0 );
|
|
rRef.SetTabDeleted( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_DELETED ) != 0 );
|
|
rRef.SetFlag3D( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D ) != 0 );
|
|
rRef.SetRelName( ( rAPI.Flags & sheet::ReferenceFlags::RELATIVE_NAME ) != 0 );
|
|
|
|
if (rRef.IsColRel())
|
|
rRef.SetRelCol(static_cast<SCCOL>(rAPI.RelativeColumn));
|
|
else
|
|
rRef.SetAbsCol(static_cast<SCCOL>(rAPI.Column));
|
|
|
|
if (rRef.IsRowRel())
|
|
rRef.SetRelRow(static_cast<SCROW>(rAPI.RelativeRow));
|
|
else
|
|
rRef.SetAbsRow(static_cast<SCROW>(rAPI.Row));
|
|
|
|
if (rRef.IsTabRel())
|
|
rRef.SetRelTab(static_cast<SCTAB>(rAPI.RelativeSheet));
|
|
else
|
|
rRef.SetAbsTab(static_cast<SCTAB>(rAPI.Sheet));
|
|
}
|
|
|
|
void lcl_ExternalRefToCalc( ScSingleRefData& rRef, const sheet::SingleReference& rAPI )
|
|
{
|
|
rRef.InitFlags();
|
|
|
|
rRef.SetColRel( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_RELATIVE ) != 0 );
|
|
rRef.SetRowRel( ( rAPI.Flags & sheet::ReferenceFlags::ROW_RELATIVE ) != 0 );
|
|
rRef.SetColDeleted( ( rAPI.Flags & sheet::ReferenceFlags::COLUMN_DELETED ) != 0 );
|
|
rRef.SetRowDeleted( ( rAPI.Flags & sheet::ReferenceFlags::ROW_DELETED ) != 0 );
|
|
rRef.SetTabDeleted( false ); // sheet must not be deleted for external refs
|
|
rRef.SetFlag3D( ( rAPI.Flags & sheet::ReferenceFlags::SHEET_3D ) != 0 );
|
|
rRef.SetRelName( false );
|
|
|
|
if (rRef.IsColRel())
|
|
rRef.SetRelCol(static_cast<SCCOL>(rAPI.RelativeColumn));
|
|
else
|
|
rRef.SetAbsCol(static_cast<SCCOL>(rAPI.Column));
|
|
|
|
if (rRef.IsRowRel())
|
|
rRef.SetRelRow(static_cast<SCROW>(rAPI.RelativeRow));
|
|
else
|
|
rRef.SetAbsRow(static_cast<SCROW>(rAPI.Row));
|
|
|
|
// sheet index must be absolute for external refs
|
|
rRef.SetAbsTab(0);
|
|
}
|
|
|
|
struct TokenPointerRange
|
|
{
|
|
FormulaToken** mpStart;
|
|
FormulaToken** mpStop;
|
|
|
|
TokenPointerRange() : mpStart(nullptr), mpStop(nullptr) {}
|
|
TokenPointerRange( FormulaToken** p, sal_uInt16 n ) :
|
|
mpStart(p), mpStop( p + static_cast<size_t>(n)) {}
|
|
};
|
|
struct TokenPointers
|
|
{
|
|
TokenPointerRange maPointerRange[2];
|
|
bool mbSkipRelName;
|
|
|
|
TokenPointers( FormulaToken** pCode, sal_uInt16 nLen, FormulaToken** pRPN, sal_uInt16 nRPN,
|
|
bool bSkipRelName = true ) :
|
|
mbSkipRelName(bSkipRelName)
|
|
{
|
|
maPointerRange[0] = TokenPointerRange( pCode, nLen);
|
|
maPointerRange[1] = TokenPointerRange( pRPN, nRPN);
|
|
}
|
|
|
|
bool skipToken( size_t i, const FormulaToken* const * pp )
|
|
{
|
|
// Handle all code tokens, and tokens in RPN only if they have a
|
|
// reference count of 1, which means they are not referenced in the
|
|
// code array. Doing it the other way would skip code tokens that
|
|
// are held by flat copied token arrays and thus are shared. For
|
|
// flat copy arrays the caller has to know what it does and should
|
|
// discard all RPN, update only one array and regenerate all RPN.
|
|
if (i == 1)
|
|
{
|
|
if ((*pp)->GetRef() > 1)
|
|
return true;
|
|
|
|
if (mbSkipRelName)
|
|
{
|
|
// Skip (do not adjust) relative references resulting from
|
|
// named expressions. Resolved expressions are only in RPN.
|
|
switch ((*pp)->GetType())
|
|
{
|
|
case svSingleRef:
|
|
return (*pp)->GetSingleRef()->IsRelName();
|
|
case svDoubleRef:
|
|
{
|
|
const ScComplexRefData& rRef = *(*pp)->GetDoubleRef();
|
|
return rRef.Ref1.IsRelName() || rRef.Ref2.IsRelName();
|
|
}
|
|
default:
|
|
; // nothing
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
FormulaToken* getHandledToken( size_t i, FormulaToken* const * pp )
|
|
{
|
|
if (skipToken( i, pp))
|
|
return nullptr;
|
|
|
|
FormulaToken* p = *pp;
|
|
if (p->GetOpCode() == ocTableRef)
|
|
{
|
|
// Return the inner reference token if it is not in RPN.
|
|
ScTableRefToken* pTR = dynamic_cast<ScTableRefToken*>(p);
|
|
if (!pTR)
|
|
return p;
|
|
p = pTR->GetAreaRefRPN();
|
|
if (!p)
|
|
return pTR;
|
|
if (p->GetRef() > 1)
|
|
// Reference handled in RPN, but do not return nullptr so
|
|
// loops will process ocTableRef via pp instead of issuing
|
|
// a continue.
|
|
return pTR;
|
|
}
|
|
return p;
|
|
}
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
// --- class ScRawToken -----------------------------------------------------
|
|
|
|
void ScRawToken::SetOpCode( OpCode e )
|
|
{
|
|
eOp = e;
|
|
switch (eOp)
|
|
{
|
|
case ocIf:
|
|
eType = svJump;
|
|
nJump[ 0 ] = 3; // If, Else, Behind
|
|
break;
|
|
case ocIfError:
|
|
case ocIfNA:
|
|
eType = svJump;
|
|
nJump[ 0 ] = 2; // If, Behind
|
|
break;
|
|
case ocChoose:
|
|
eType = svJump;
|
|
nJump[ 0 ] = FORMULA_MAXJUMPCOUNT + 1;
|
|
break;
|
|
case ocLet:
|
|
eType = svJump;
|
|
nJump[ 0 ] = FORMULA_MAXPARAMS + 1;
|
|
break;
|
|
case ocMissing:
|
|
eType = svMissing;
|
|
break;
|
|
case ocSep:
|
|
case ocOpen:
|
|
case ocClose:
|
|
case ocArrayRowSep:
|
|
case ocArrayColSep:
|
|
case ocArrayOpen:
|
|
case ocArrayClose:
|
|
case ocTableRefOpen:
|
|
case ocTableRefClose:
|
|
eType = svSep;
|
|
break;
|
|
case ocWhitespace:
|
|
eType = svByte;
|
|
whitespace.nCount = 1;
|
|
whitespace.cChar = 0x20;
|
|
break;
|
|
default:
|
|
eType = svByte;
|
|
sbyte.cByte = 0;
|
|
sbyte.eInForceArray = ParamClass::Unknown;
|
|
}
|
|
}
|
|
|
|
void ScRawToken::SetString( rtl_uString* pData, rtl_uString* pDataIgnoreCase )
|
|
{
|
|
eOp = ocPush;
|
|
eType = svString;
|
|
|
|
sharedstring.mpData = pData;
|
|
sharedstring.mpDataIgnoreCase = pDataIgnoreCase;
|
|
}
|
|
|
|
void ScRawToken::SetStringName( rtl_uString* pData, rtl_uString* pDataIgnoreCase )
|
|
{
|
|
eOp = ocStringName;
|
|
eType = svString;
|
|
|
|
sharedstring.mpData = pData;
|
|
sharedstring.mpDataIgnoreCase = pDataIgnoreCase;
|
|
}
|
|
|
|
void ScRawToken::SetSingleReference( const ScSingleRefData& rRef )
|
|
{
|
|
eOp = ocPush;
|
|
eType = svSingleRef;
|
|
aRef.Ref1 =
|
|
aRef.Ref2 = rRef;
|
|
}
|
|
|
|
void ScRawToken::SetDoubleReference( const ScComplexRefData& rRef )
|
|
{
|
|
eOp = ocPush;
|
|
eType = svDoubleRef;
|
|
aRef = rRef;
|
|
}
|
|
|
|
void ScRawToken::SetDouble(double rVal)
|
|
{
|
|
eOp = ocPush;
|
|
eType = svDouble;
|
|
nValue = rVal;
|
|
}
|
|
|
|
void ScRawToken::SetErrorConstant( FormulaError nErr )
|
|
{
|
|
eOp = ocPush;
|
|
eType = svError;
|
|
nError = nErr;
|
|
}
|
|
|
|
void ScRawToken::SetName(sal_Int16 nSheet, sal_uInt16 nIndex)
|
|
{
|
|
eOp = ocName;
|
|
eType = svIndex;
|
|
|
|
name.nSheet = nSheet;
|
|
name.nIndex = nIndex;
|
|
}
|
|
|
|
void ScRawToken::SetExternalSingleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScSingleRefData& rRef )
|
|
{
|
|
eOp = ocPush;
|
|
eType = svExternalSingleRef;
|
|
|
|
extref.nFileId = nFileId;
|
|
extref.aRef.Ref1 =
|
|
extref.aRef.Ref2 = rRef;
|
|
maExternalName = rTabName;
|
|
}
|
|
|
|
void ScRawToken::SetExternalDoubleRef( sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rRef )
|
|
{
|
|
eOp = ocPush;
|
|
eType = svExternalDoubleRef;
|
|
|
|
extref.nFileId = nFileId;
|
|
extref.aRef = rRef;
|
|
maExternalName = rTabName;
|
|
}
|
|
|
|
void ScRawToken::SetExternalName( sal_uInt16 nFileId, const OUString& rName )
|
|
{
|
|
eOp = ocPush;
|
|
eType = svExternalName;
|
|
|
|
extname.nFileId = nFileId;
|
|
maExternalName = rName;
|
|
}
|
|
|
|
void ScRawToken::SetExternal( const OUString& rStr )
|
|
{
|
|
eOp = ocExternal;
|
|
eType = svExternal;
|
|
maExternalName = rStr;
|
|
}
|
|
|
|
bool ScRawToken::IsValidReference(const ScDocument& rDoc) const
|
|
{
|
|
switch (eType)
|
|
{
|
|
case svSingleRef:
|
|
return aRef.Ref1.Valid(rDoc);
|
|
case svDoubleRef:
|
|
return aRef.Valid(rDoc);
|
|
case svExternalSingleRef:
|
|
case svExternalDoubleRef:
|
|
return true;
|
|
default:
|
|
; // nothing
|
|
}
|
|
return false;
|
|
}
|
|
|
|
FormulaToken* ScRawToken::CreateToken(ScSheetLimits& rLimits) const
|
|
{
|
|
#define IF_NOT_OPCODE_ERROR(o,c) SAL_WARN_IF((eOp!=o), "sc.core", #c "::ctor: OpCode " << static_cast<int>(eOp) << " lost, converted to " #o "; maybe inherit from FormulaToken instead!")
|
|
switch ( GetType() )
|
|
{
|
|
case svByte :
|
|
if (eOp == ocWhitespace)
|
|
return new FormulaSpaceToken( whitespace.nCount, whitespace.cChar );
|
|
else
|
|
return new FormulaByteToken( eOp, sbyte.cByte, sbyte.eInForceArray );
|
|
case svDouble :
|
|
IF_NOT_OPCODE_ERROR( ocPush, FormulaDoubleToken);
|
|
return new FormulaDoubleToken( nValue );
|
|
case svString :
|
|
{
|
|
svl::SharedString aSS(sharedstring.mpData, sharedstring.mpDataIgnoreCase);
|
|
if (eOp == ocPush)
|
|
return new FormulaStringToken(std::move(aSS));
|
|
else
|
|
return new FormulaStringOpToken(eOp, std::move(aSS));
|
|
}
|
|
case svSingleRef :
|
|
return new ScSingleRefToken(rLimits, aRef.Ref1, eOp);
|
|
case svDoubleRef :
|
|
return new ScDoubleRefToken(rLimits, aRef, eOp);
|
|
case svMatrix :
|
|
IF_NOT_OPCODE_ERROR( ocPush, ScMatrixToken);
|
|
return new ScMatrixToken( pMat );
|
|
case svIndex :
|
|
if (eOp == ocTableRef)
|
|
return new ScTableRefToken( table.nIndex, table.eItem);
|
|
else
|
|
return new FormulaIndexToken( eOp, name.nIndex, name.nSheet);
|
|
case svExternalSingleRef:
|
|
{
|
|
svl::SharedString aTabName(maExternalName); // string not interned
|
|
return new ScExternalSingleRefToken(extref.nFileId, std::move(aTabName), extref.aRef.Ref1);
|
|
}
|
|
case svExternalDoubleRef:
|
|
{
|
|
svl::SharedString aTabName(maExternalName); // string not interned
|
|
return new ScExternalDoubleRefToken(extref.nFileId, std::move(aTabName), extref.aRef);
|
|
}
|
|
case svExternalName:
|
|
{
|
|
svl::SharedString aName(maExternalName); // string not interned
|
|
return new ScExternalNameToken( extname.nFileId, std::move(aName) );
|
|
}
|
|
case svJump :
|
|
return new FormulaJumpToken( eOp, nJump );
|
|
case svExternal :
|
|
return new FormulaExternalToken( eOp, sbyte.cByte, maExternalName );
|
|
case svFAP :
|
|
return new FormulaFAPToken( eOp, sbyte.cByte, nullptr );
|
|
case svMissing :
|
|
IF_NOT_OPCODE_ERROR( ocMissing, FormulaMissingToken);
|
|
return new FormulaMissingToken;
|
|
case svSep :
|
|
return new FormulaToken( svSep,eOp );
|
|
case svError :
|
|
return new FormulaErrorToken( nError );
|
|
case svUnknown :
|
|
return new FormulaUnknownToken( eOp );
|
|
default:
|
|
{
|
|
SAL_WARN("sc.core", "unknown ScRawToken::CreateToken() type " << int(GetType()));
|
|
return new FormulaUnknownToken( ocBad );
|
|
}
|
|
}
|
|
#undef IF_NOT_OPCODE_ERROR
|
|
}
|
|
|
|
namespace {
|
|
|
|
// TextEqual: if same formula entered (for optimization in sort)
|
|
bool checkTextEqual( const ScSheetLimits& rLimits, const FormulaToken& _rToken1, const FormulaToken& _rToken2 )
|
|
{
|
|
assert(
|
|
(_rToken1.GetType() == svSingleRef || _rToken1.GetType() == svDoubleRef)
|
|
&& _rToken1.FormulaToken::operator ==(_rToken2));
|
|
|
|
// in relative Refs only compare relative parts
|
|
|
|
ScComplexRefData aTemp1;
|
|
if ( _rToken1.GetType() == svSingleRef )
|
|
{
|
|
aTemp1.Ref1 = *_rToken1.GetSingleRef();
|
|
aTemp1.Ref2 = aTemp1.Ref1;
|
|
}
|
|
else
|
|
aTemp1 = *_rToken1.GetDoubleRef();
|
|
|
|
ScComplexRefData aTemp2;
|
|
if ( _rToken2.GetType() == svSingleRef )
|
|
{
|
|
aTemp2.Ref1 = *_rToken2.GetSingleRef();
|
|
aTemp2.Ref2 = aTemp2.Ref1;
|
|
}
|
|
else
|
|
aTemp2 = *_rToken2.GetDoubleRef();
|
|
|
|
ScAddress aPos;
|
|
ScRange aRange1 = aTemp1.toAbs(rLimits, aPos), aRange2 = aTemp2.toAbs(rLimits, aPos);
|
|
|
|
// memcmp doesn't work because of the alignment byte after bFlags.
|
|
// After SmartRelAbs only absolute parts have to be compared.
|
|
return aRange1 == aRange2 && aTemp1.Ref1.FlagValue() == aTemp2.Ref1.FlagValue() && aTemp1.Ref2.FlagValue() == aTemp2.Ref2.FlagValue();
|
|
}
|
|
|
|
}
|
|
|
|
#if DEBUG_FORMULA_COMPILER
|
|
void DumpToken(formula::FormulaToken const & rToken)
|
|
{
|
|
switch (rToken.GetType()) {
|
|
case svSingleRef:
|
|
cout << "-- ScSingleRefToken" << endl;
|
|
rToken.GetSingleRef()->Dump(1);
|
|
break;
|
|
case svDoubleRef:
|
|
cout << "-- ScDoubleRefToken" << endl;
|
|
rToken.GetDoubleRef()->Dump(1);
|
|
break;
|
|
default:
|
|
cout << "-- FormulaToken" << endl;
|
|
cout << " opcode: " << int(rToken.GetOpCode()) << " " <<
|
|
formula::FormulaCompiler::GetNativeSymbol( rToken.GetOpCode()).toUtf8().getStr() << endl;
|
|
cout << " type: " << static_cast<int>(rToken.GetType()) << endl;
|
|
switch (rToken.GetType())
|
|
{
|
|
case svDouble:
|
|
cout << " value: " << rToken.GetDouble() << endl;
|
|
break;
|
|
case svString:
|
|
cout << " string: "
|
|
<< OUStringToOString(rToken.GetString().getString(), RTL_TEXTENCODING_UTF8).getStr()
|
|
<< endl;
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FormulaTokenRef extendRangeReference( ScSheetLimits& rLimits, FormulaToken & rTok1, FormulaToken & rTok2,
|
|
const ScAddress & rPos, bool bReuseDoubleRef )
|
|
{
|
|
|
|
StackVar sv1 = rTok1.GetType();
|
|
// Doing a RangeOp with RefList is probably utter nonsense, but Xcl
|
|
// supports it, so do we.
|
|
if (sv1 != svSingleRef && sv1 != svDoubleRef && sv1 != svRefList
|
|
&& sv1 != svExternalSingleRef && sv1 != svExternalDoubleRef)
|
|
return nullptr;
|
|
StackVar sv2 = rTok2.GetType();
|
|
if (sv2 != svSingleRef && sv2 != svDoubleRef && sv2 != svRefList)
|
|
return nullptr;
|
|
|
|
ScTokenRef xRes;
|
|
bool bExternal = (sv1 == svExternalSingleRef);
|
|
if ((sv1 == svSingleRef || bExternal) && sv2 == svSingleRef)
|
|
{
|
|
// Range references like Sheet1.A1:A2 are generalized and built by
|
|
// first creating a DoubleRef from the first SingleRef, effectively
|
|
// generating Sheet1.A1:A1, and then extending that with A2 as if
|
|
// Sheet1.A1:A1:A2 was encountered, so the mechanisms to adjust the
|
|
// references apply as well.
|
|
|
|
/* Given the current structure of external references an external
|
|
* reference can only be extended if the second reference does not
|
|
* point to a different sheet. 'file'#Sheet1.A1:A2 is ok,
|
|
* 'file'#Sheet1.A1:Sheet2.A2 is not. Since we can't determine from a
|
|
* svSingleRef whether the sheet would be different from the one given
|
|
* in the external reference, we have to bail out if there is any sheet
|
|
* specified. NOTE: Xcl does handle external 3D references as in
|
|
* '[file]Sheet1:Sheet2'!A1:A2
|
|
*
|
|
* FIXME: For OOo syntax be smart and remember an external singleref
|
|
* encountered and if followed by ocRange and singleref, create an
|
|
* external singleref for the second singleref. Both could then be
|
|
* merged here. For Xcl syntax already parse an external range
|
|
* reference entirely, cumbersome. */
|
|
|
|
const ScSingleRefData& rRef2 = *rTok2.GetSingleRef();
|
|
if (bExternal && rRef2.IsFlag3D())
|
|
return nullptr;
|
|
|
|
ScComplexRefData aRef;
|
|
aRef.Ref1 = aRef.Ref2 = *rTok1.GetSingleRef();
|
|
aRef.Ref2.SetFlag3D( false);
|
|
aRef.Extend(rLimits, rRef2, rPos);
|
|
if (bExternal)
|
|
xRes = new ScExternalDoubleRefToken( rTok1.GetIndex(), rTok1.GetString(), aRef);
|
|
else
|
|
xRes = new ScDoubleRefToken(rLimits, aRef);
|
|
}
|
|
else
|
|
{
|
|
bExternal |= (sv1 == svExternalDoubleRef);
|
|
const ScRefList* pRefList = nullptr;
|
|
if (sv1 == svDoubleRef)
|
|
{
|
|
xRes = (bReuseDoubleRef && rTok1.GetRef() == 1 ? &rTok1 : rTok1.Clone());
|
|
sv1 = svUnknown; // mark as handled
|
|
}
|
|
else if (sv2 == svDoubleRef)
|
|
{
|
|
xRes = (bReuseDoubleRef && rTok2.GetRef() == 1 ? &rTok2 : rTok2.Clone());
|
|
sv2 = svUnknown; // mark as handled
|
|
}
|
|
else if (sv1 == svRefList)
|
|
pRefList = rTok1.GetRefList();
|
|
else if (sv2 == svRefList)
|
|
pRefList = rTok2.GetRefList();
|
|
if (pRefList)
|
|
{
|
|
if (pRefList->empty())
|
|
return nullptr;
|
|
if (bExternal)
|
|
return nullptr; // external reference list not possible
|
|
xRes = new ScDoubleRefToken(rLimits, (*pRefList)[0] );
|
|
}
|
|
if (!xRes)
|
|
return nullptr; // shouldn't happen...
|
|
StackVar sv[2] = { sv1, sv2 };
|
|
formula::FormulaToken* pt[2] = { &rTok1, &rTok2 };
|
|
ScComplexRefData& rRef = *xRes->GetDoubleRef();
|
|
for (size_t i=0; i<2; ++i)
|
|
{
|
|
switch (sv[i])
|
|
{
|
|
case svSingleRef:
|
|
rRef.Extend(rLimits, *pt[i]->GetSingleRef(), rPos);
|
|
break;
|
|
case svDoubleRef:
|
|
rRef.Extend(rLimits, *pt[i]->GetDoubleRef(), rPos);
|
|
break;
|
|
case svRefList:
|
|
{
|
|
const ScRefList* p = pt[i]->GetRefList();
|
|
if (p->empty())
|
|
return nullptr;
|
|
for (const auto& rRefData : *p)
|
|
{
|
|
rRef.Extend(rLimits, rRefData, rPos);
|
|
}
|
|
}
|
|
break;
|
|
case svExternalSingleRef:
|
|
if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D())
|
|
return nullptr; // no other sheets with external refs
|
|
else
|
|
rRef.Extend(rLimits, *pt[i]->GetSingleRef(), rPos);
|
|
break;
|
|
case svExternalDoubleRef:
|
|
if (rRef.Ref1.IsFlag3D() || rRef.Ref2.IsFlag3D())
|
|
return nullptr; // no other sheets with external refs
|
|
else
|
|
rRef.Extend(rLimits, *pt[i]->GetDoubleRef(), rPos);
|
|
break;
|
|
default:
|
|
; // nothing, prevent compiler warning
|
|
}
|
|
}
|
|
}
|
|
return FormulaTokenRef(xRes.get());
|
|
}
|
|
|
|
// real implementations of virtual functions
|
|
|
|
const ScSingleRefData* ScSingleRefToken::GetSingleRef() const { return &aSingleRef; }
|
|
ScSingleRefData* ScSingleRefToken::GetSingleRef() { return &aSingleRef; }
|
|
bool ScSingleRefToken::TextEqual( const FormulaToken& _rToken ) const
|
|
{
|
|
return FormulaToken::operator ==(_rToken) && checkTextEqual(mrSheetLimits, *this, _rToken);
|
|
}
|
|
bool ScSingleRefToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
return FormulaToken::operator==( r ) && aSingleRef == *r.GetSingleRef();
|
|
}
|
|
|
|
const ScSingleRefData* ScDoubleRefToken::GetSingleRef() const { return &aDoubleRef.Ref1; }
|
|
ScSingleRefData* ScDoubleRefToken::GetSingleRef() { return &aDoubleRef.Ref1; }
|
|
const ScComplexRefData* ScDoubleRefToken::GetDoubleRef() const { return &aDoubleRef; }
|
|
ScComplexRefData* ScDoubleRefToken::GetDoubleRef() { return &aDoubleRef; }
|
|
const ScSingleRefData* ScDoubleRefToken::GetSingleRef2() const { return &aDoubleRef.Ref2; }
|
|
ScSingleRefData* ScDoubleRefToken::GetSingleRef2() { return &aDoubleRef.Ref2; }
|
|
bool ScDoubleRefToken::TextEqual( const FormulaToken& _rToken ) const
|
|
{
|
|
return FormulaToken::operator ==(_rToken) && checkTextEqual(mrSheetLimits, *this, _rToken);
|
|
}
|
|
bool ScDoubleRefToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
return FormulaToken::operator==( r ) && aDoubleRef == *r.GetDoubleRef();
|
|
}
|
|
|
|
const ScRefList* ScRefListToken::GetRefList() const { return &aRefList; }
|
|
ScRefList* ScRefListToken::GetRefList() { return &aRefList; }
|
|
bool ScRefListToken::IsArrayResult() const { return mbArrayResult; }
|
|
bool ScRefListToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
if (!FormulaToken::operator==( r ) || &aRefList != r.GetRefList())
|
|
return false;
|
|
const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(&r);
|
|
return p && mbArrayResult == p->IsArrayResult();
|
|
}
|
|
|
|
ScMatrixToken::ScMatrixToken( ScMatrixRef p ) :
|
|
FormulaToken(formula::svMatrix), pMatrix(std::move(p)) {}
|
|
|
|
ScMatrixToken::ScMatrixToken( const ScMatrixToken& ) = default;
|
|
|
|
const ScMatrix* ScMatrixToken::GetMatrix() const { return pMatrix.get(); }
|
|
ScMatrix* ScMatrixToken::GetMatrix() { return pMatrix.get(); }
|
|
bool ScMatrixToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
return FormulaToken::operator==( r ) && pMatrix == r.GetMatrix();
|
|
}
|
|
|
|
ScMatrixRangeToken::ScMatrixRangeToken( const sc::RangeMatrix& rMat ) :
|
|
FormulaToken(formula::svMatrix), mpMatrix(rMat.mpMat)
|
|
{
|
|
maRef.InitRange(rMat.mnCol1, rMat.mnRow1, rMat.mnTab1, rMat.mnCol2, rMat.mnRow2, rMat.mnTab2);
|
|
}
|
|
|
|
ScMatrixRangeToken::ScMatrixRangeToken( const ScMatrixRangeToken& ) = default;
|
|
|
|
sal_uInt8 ScMatrixRangeToken::GetByte() const
|
|
{
|
|
return MATRIX_TOKEN_HAS_RANGE;
|
|
}
|
|
|
|
const ScMatrix* ScMatrixRangeToken::GetMatrix() const
|
|
{
|
|
return mpMatrix.get();
|
|
}
|
|
|
|
ScMatrix* ScMatrixRangeToken::GetMatrix()
|
|
{
|
|
return mpMatrix.get();
|
|
}
|
|
|
|
const ScComplexRefData* ScMatrixRangeToken::GetDoubleRef() const
|
|
{
|
|
return &maRef;
|
|
}
|
|
|
|
ScComplexRefData* ScMatrixRangeToken::GetDoubleRef()
|
|
{
|
|
return &maRef;
|
|
}
|
|
|
|
bool ScMatrixRangeToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
return FormulaToken::operator==(r) && mpMatrix == r.GetMatrix();
|
|
}
|
|
|
|
FormulaToken* ScMatrixRangeToken::Clone() const
|
|
{
|
|
return new ScMatrixRangeToken(*this);
|
|
}
|
|
|
|
ScExternalSingleRefToken::ScExternalSingleRefToken( sal_uInt16 nFileId, svl::SharedString aTabName, const ScSingleRefData& r ) :
|
|
FormulaToken( svExternalSingleRef, ocPush),
|
|
mnFileId(nFileId),
|
|
maTabName(std::move(aTabName)),
|
|
maSingleRef(r)
|
|
{
|
|
}
|
|
|
|
ScExternalSingleRefToken::~ScExternalSingleRefToken()
|
|
{
|
|
}
|
|
|
|
sal_uInt16 ScExternalSingleRefToken::GetIndex() const
|
|
{
|
|
return mnFileId;
|
|
}
|
|
|
|
const svl::SharedString & ScExternalSingleRefToken::GetString() const
|
|
{
|
|
return maTabName;
|
|
}
|
|
|
|
const ScSingleRefData* ScExternalSingleRefToken::GetSingleRef() const
|
|
{
|
|
return &maSingleRef;
|
|
}
|
|
|
|
ScSingleRefData* ScExternalSingleRefToken::GetSingleRef()
|
|
{
|
|
return &maSingleRef;
|
|
}
|
|
|
|
bool ScExternalSingleRefToken::operator ==( const FormulaToken& r ) const
|
|
{
|
|
if (!FormulaToken::operator==(r))
|
|
return false;
|
|
|
|
if (mnFileId != r.GetIndex())
|
|
return false;
|
|
|
|
if (maTabName != r.GetString())
|
|
return false;
|
|
|
|
return maSingleRef == *r.GetSingleRef();
|
|
}
|
|
|
|
ScExternalDoubleRefToken::ScExternalDoubleRefToken( sal_uInt16 nFileId, svl::SharedString aTabName, const ScComplexRefData& r ) :
|
|
FormulaToken( svExternalDoubleRef, ocPush),
|
|
mnFileId(nFileId),
|
|
maTabName(std::move(aTabName)),
|
|
maDoubleRef(r)
|
|
{
|
|
}
|
|
|
|
ScExternalDoubleRefToken::~ScExternalDoubleRefToken()
|
|
{
|
|
}
|
|
|
|
sal_uInt16 ScExternalDoubleRefToken::GetIndex() const
|
|
{
|
|
return mnFileId;
|
|
}
|
|
|
|
const svl::SharedString & ScExternalDoubleRefToken::GetString() const
|
|
{
|
|
return maTabName;
|
|
}
|
|
|
|
const ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef() const
|
|
{
|
|
return &maDoubleRef.Ref1;
|
|
}
|
|
|
|
ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef()
|
|
{
|
|
return &maDoubleRef.Ref1;
|
|
}
|
|
|
|
const ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef2() const
|
|
{
|
|
return &maDoubleRef.Ref2;
|
|
}
|
|
|
|
ScSingleRefData* ScExternalDoubleRefToken::GetSingleRef2()
|
|
{
|
|
return &maDoubleRef.Ref2;
|
|
}
|
|
|
|
const ScComplexRefData* ScExternalDoubleRefToken::GetDoubleRef() const
|
|
{
|
|
return &maDoubleRef;
|
|
}
|
|
|
|
ScComplexRefData* ScExternalDoubleRefToken::GetDoubleRef()
|
|
{
|
|
return &maDoubleRef;
|
|
}
|
|
|
|
bool ScExternalDoubleRefToken::operator ==( const FormulaToken& r ) const
|
|
{
|
|
if (!FormulaToken::operator==(r))
|
|
return false;
|
|
|
|
if (mnFileId != r.GetIndex())
|
|
return false;
|
|
|
|
if (maTabName != r.GetString())
|
|
return false;
|
|
|
|
return maDoubleRef == *r.GetDoubleRef();
|
|
}
|
|
|
|
ScExternalNameToken::ScExternalNameToken( sal_uInt16 nFileId, svl::SharedString aName ) :
|
|
FormulaToken( svExternalName, ocPush),
|
|
mnFileId(nFileId),
|
|
maName(std::move(aName))
|
|
{
|
|
}
|
|
|
|
ScExternalNameToken::~ScExternalNameToken() {}
|
|
|
|
sal_uInt16 ScExternalNameToken::GetIndex() const
|
|
{
|
|
return mnFileId;
|
|
}
|
|
|
|
const svl::SharedString & ScExternalNameToken::GetString() const
|
|
{
|
|
return maName;
|
|
}
|
|
|
|
bool ScExternalNameToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
if ( !FormulaToken::operator==(r) )
|
|
return false;
|
|
|
|
if (mnFileId != r.GetIndex())
|
|
return false;
|
|
|
|
return maName == r.GetString();
|
|
}
|
|
|
|
ScTableRefToken::ScTableRefToken( sal_uInt16 nIndex, ScTableRefToken::Item eItem ) :
|
|
FormulaToken( svIndex, ocTableRef),
|
|
mnIndex(nIndex),
|
|
meItem(eItem)
|
|
{
|
|
}
|
|
|
|
ScTableRefToken::ScTableRefToken( const ScTableRefToken& r ) :
|
|
FormulaToken(r),
|
|
mxAreaRefRPN( r.mxAreaRefRPN ? r.mxAreaRefRPN->Clone() : nullptr),
|
|
mnIndex(r.mnIndex),
|
|
meItem(r.meItem)
|
|
{
|
|
}
|
|
|
|
ScTableRefToken::~ScTableRefToken() {}
|
|
|
|
sal_uInt16 ScTableRefToken::GetIndex() const
|
|
{
|
|
return mnIndex;
|
|
}
|
|
|
|
void ScTableRefToken::SetIndex( sal_uInt16 n )
|
|
{
|
|
mnIndex = n;
|
|
}
|
|
|
|
sal_Int16 ScTableRefToken::GetSheet() const
|
|
{
|
|
// Code asking for this may have to be adapted as it might assume an
|
|
// svIndex token would always be ocName or ocDBArea.
|
|
SAL_WARN("sc.core","ScTableRefToken::GetSheet - maybe adapt caller to know about TableRef?");
|
|
// Database range is always global.
|
|
return -1;
|
|
}
|
|
|
|
ScTableRefToken::Item ScTableRefToken::GetItem() const
|
|
{
|
|
return meItem;
|
|
}
|
|
|
|
void ScTableRefToken::AddItem( ScTableRefToken::Item eItem )
|
|
{
|
|
meItem = static_cast<ScTableRefToken::Item>(meItem | eItem);
|
|
}
|
|
|
|
void ScTableRefToken::SetAreaRefRPN( formula::FormulaToken* pToken )
|
|
{
|
|
mxAreaRefRPN = pToken;
|
|
}
|
|
|
|
formula::FormulaToken* ScTableRefToken::GetAreaRefRPN() const
|
|
{
|
|
return mxAreaRefRPN.get();
|
|
}
|
|
|
|
bool ScTableRefToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
if ( !FormulaToken::operator==(r) )
|
|
return false;
|
|
|
|
if (mnIndex != r.GetIndex())
|
|
return false;
|
|
|
|
const ScTableRefToken* p = dynamic_cast<const ScTableRefToken*>(&r);
|
|
if (!p)
|
|
return false;
|
|
|
|
if (meItem != p->GetItem())
|
|
return false;
|
|
|
|
if (!mxAreaRefRPN && !p->mxAreaRefRPN)
|
|
; // nothing
|
|
else if (!mxAreaRefRPN || !p->mxAreaRefRPN)
|
|
return false;
|
|
else if (!(*mxAreaRefRPN == *(p->mxAreaRefRPN)))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
ScJumpMatrixToken::ScJumpMatrixToken(std::shared_ptr<ScJumpMatrix> p)
|
|
: FormulaToken(formula::svJumpMatrix)
|
|
, mpJumpMatrix(std::move(p))
|
|
{}
|
|
|
|
ScJumpMatrixToken::ScJumpMatrixToken( const ScJumpMatrixToken & ) = default;
|
|
|
|
ScJumpMatrix* ScJumpMatrixToken::GetJumpMatrix() const
|
|
{
|
|
return mpJumpMatrix.get();
|
|
}
|
|
|
|
bool ScJumpMatrixToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
return FormulaToken::operator==( r ) && mpJumpMatrix.get() == r.GetJumpMatrix();
|
|
}
|
|
|
|
ScJumpMatrixToken::~ScJumpMatrixToken()
|
|
{
|
|
}
|
|
|
|
double ScEmptyCellToken::GetDouble() const { return 0.0; }
|
|
|
|
const svl::SharedString & ScEmptyCellToken::GetString() const
|
|
{
|
|
return svl::SharedString::getEmptyString();
|
|
}
|
|
|
|
bool ScEmptyCellToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
return FormulaToken::operator==( r ) &&
|
|
bInherited == static_cast< const ScEmptyCellToken & >(r).IsInherited() &&
|
|
bDisplayedAsString == static_cast< const ScEmptyCellToken & >(r).IsDisplayedAsString();
|
|
}
|
|
|
|
ScMatrixCellResultToken::ScMatrixCellResultToken( ScConstMatrixRef pMat, const formula::FormulaToken* pUL ) :
|
|
FormulaToken(formula::svMatrixCell), xMatrix(std::move(pMat)), xUpperLeft(pUL) {}
|
|
|
|
ScMatrixCellResultToken::ScMatrixCellResultToken( const ScMatrixCellResultToken& ) = default;
|
|
|
|
double ScMatrixCellResultToken::GetDouble() const { return xUpperLeft->GetDouble(); }
|
|
|
|
ScMatrixCellResultToken::~ScMatrixCellResultToken() {}
|
|
|
|
const svl::SharedString & ScMatrixCellResultToken::GetString() const
|
|
{
|
|
return xUpperLeft->GetString();
|
|
}
|
|
|
|
const ScMatrix* ScMatrixCellResultToken::GetMatrix() const { return xMatrix.get(); }
|
|
// Non-const GetMatrix() is private and unused but must be implemented to
|
|
// satisfy vtable linkage.
|
|
ScMatrix* ScMatrixCellResultToken::GetMatrix()
|
|
{
|
|
return const_cast<ScMatrix*>(xMatrix.get());
|
|
}
|
|
|
|
bool ScMatrixCellResultToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
return FormulaToken::operator==( r ) &&
|
|
xUpperLeft == static_cast<const ScMatrixCellResultToken &>(r).xUpperLeft &&
|
|
xMatrix == static_cast<const ScMatrixCellResultToken &>(r).xMatrix;
|
|
}
|
|
|
|
FormulaToken* ScMatrixCellResultToken::Clone() const
|
|
{
|
|
return new ScMatrixCellResultToken(*this);
|
|
}
|
|
|
|
void ScMatrixCellResultToken::Assign( const ScMatrixCellResultToken & r )
|
|
{
|
|
xMatrix = r.xMatrix;
|
|
xUpperLeft = r.xUpperLeft;
|
|
}
|
|
|
|
ScMatrixFormulaCellToken::ScMatrixFormulaCellToken(
|
|
SCCOL nC, SCROW nR, const ScConstMatrixRef& pMat, const formula::FormulaToken* pUL ) :
|
|
ScMatrixCellResultToken(pMat, pUL), nRows(nR), nCols(nC)
|
|
{
|
|
CloneUpperLeftIfNecessary();
|
|
}
|
|
|
|
ScMatrixFormulaCellToken::ScMatrixFormulaCellToken( SCCOL nC, SCROW nR ) :
|
|
ScMatrixCellResultToken(nullptr, nullptr), nRows(nR), nCols(nC) {}
|
|
|
|
ScMatrixFormulaCellToken::ScMatrixFormulaCellToken( const ScMatrixFormulaCellToken& r ) :
|
|
ScMatrixCellResultToken(r), nRows(r.nRows), nCols(r.nCols)
|
|
{
|
|
CloneUpperLeftIfNecessary();
|
|
}
|
|
|
|
ScMatrixFormulaCellToken::~ScMatrixFormulaCellToken() {}
|
|
|
|
bool ScMatrixFormulaCellToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
const ScMatrixFormulaCellToken* p = dynamic_cast<const ScMatrixFormulaCellToken*>(&r);
|
|
return p && ScMatrixCellResultToken::operator==( r ) &&
|
|
nCols == p->nCols && nRows == p->nRows;
|
|
}
|
|
|
|
void ScMatrixFormulaCellToken::CloneUpperLeftIfNecessary()
|
|
{
|
|
if (xUpperLeft && xUpperLeft->GetType() == svDouble)
|
|
xUpperLeft = xUpperLeft->Clone();
|
|
}
|
|
|
|
void ScMatrixFormulaCellToken::Assign( const ScMatrixCellResultToken & r )
|
|
{
|
|
ScMatrixCellResultToken::Assign( r);
|
|
|
|
CloneUpperLeftIfNecessary();
|
|
}
|
|
|
|
void ScMatrixFormulaCellToken::Assign( const formula::FormulaToken& r )
|
|
{
|
|
if (this == &r)
|
|
return;
|
|
const ScMatrixCellResultToken* p = dynamic_cast<const ScMatrixCellResultToken*>(&r);
|
|
if (p)
|
|
ScMatrixCellResultToken::Assign( *p);
|
|
else
|
|
{
|
|
OSL_ENSURE( r.GetType() != svMatrix, "ScMatrixFormulaCellToken::operator=: assigning ScMatrixToken to ScMatrixFormulaCellToken is not proper, use ScMatrixCellResultToken instead");
|
|
if (r.GetType() == svMatrix)
|
|
{
|
|
xUpperLeft = nullptr;
|
|
xMatrix = r.GetMatrix();
|
|
}
|
|
else
|
|
{
|
|
xUpperLeft = &r;
|
|
xMatrix = nullptr;
|
|
CloneUpperLeftIfNecessary();
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScMatrixFormulaCellToken::SetUpperLeftDouble( double f )
|
|
{
|
|
switch (GetUpperLeftType())
|
|
{
|
|
case svDouble:
|
|
const_cast<FormulaToken*>(xUpperLeft.get())->SetDouble(f);
|
|
break;
|
|
case svString:
|
|
xUpperLeft = new FormulaDoubleToken( f);
|
|
break;
|
|
case svUnknown:
|
|
if (!xUpperLeft)
|
|
{
|
|
xUpperLeft = new FormulaDoubleToken( f);
|
|
break;
|
|
}
|
|
[[fallthrough]];
|
|
default:
|
|
{
|
|
OSL_FAIL("ScMatrixFormulaCellToken::SetUpperLeftDouble: not modifying unhandled token type");
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScMatrixFormulaCellToken::ResetResult()
|
|
{
|
|
xMatrix = nullptr;
|
|
xUpperLeft = nullptr;
|
|
}
|
|
|
|
ScHybridCellToken::ScHybridCellToken(
|
|
double f, const svl::SharedString & rStr, OUString aFormula, bool bEmptyDisplayedAsString ) :
|
|
FormulaToken( formula::svHybridCell ),
|
|
mfDouble( f ), maString( rStr ),
|
|
maFormula(std::move( aFormula )),
|
|
mbEmptyDisplayedAsString( bEmptyDisplayedAsString)
|
|
{
|
|
// caller, make up your mind...
|
|
assert( !bEmptyDisplayedAsString || (f == 0.0 && rStr.getString().isEmpty()));
|
|
}
|
|
|
|
double ScHybridCellToken::GetDouble() const { return mfDouble; }
|
|
|
|
const svl::SharedString & ScHybridCellToken::GetString() const
|
|
{
|
|
return maString;
|
|
}
|
|
|
|
bool ScHybridCellToken::operator==( const FormulaToken& r ) const
|
|
{
|
|
return FormulaToken::operator==( r ) &&
|
|
mfDouble == r.GetDouble() && maString == r.GetString() &&
|
|
maFormula == static_cast<const ScHybridCellToken &>(r).GetFormula();
|
|
}
|
|
|
|
bool ScTokenArray::AddFormulaToken(
|
|
const css::sheet::FormulaToken& rToken, svl::SharedStringPool& rSPool, formula::ExternalReferenceHelper* pExtRef)
|
|
{
|
|
bool bError = FormulaTokenArray::AddFormulaToken(rToken, rSPool, pExtRef);
|
|
if ( bError )
|
|
{
|
|
bError = false;
|
|
const OpCode eOpCode = static_cast<OpCode>(rToken.OpCode); // assuming equal values for the moment
|
|
|
|
const uno::TypeClass eClass = rToken.Data.getValueTypeClass();
|
|
switch ( eClass )
|
|
{
|
|
case uno::TypeClass_STRUCT:
|
|
{
|
|
uno::Type aType = rToken.Data.getValueType();
|
|
if ( aType.equals( cppu::UnoType<sheet::SingleReference>::get() ) )
|
|
{
|
|
ScSingleRefData aSingleRef;
|
|
sheet::SingleReference aApiRef;
|
|
rToken.Data >>= aApiRef;
|
|
lcl_SingleRefToCalc( aSingleRef, aApiRef );
|
|
if ( eOpCode == ocPush )
|
|
AddSingleReference( aSingleRef );
|
|
else if ( eOpCode == ocColRowName )
|
|
AddColRowName( aSingleRef );
|
|
else
|
|
bError = true;
|
|
}
|
|
else if ( aType.equals( cppu::UnoType<sheet::ComplexReference>::get() ) )
|
|
{
|
|
ScComplexRefData aComplRef;
|
|
sheet::ComplexReference aApiRef;
|
|
rToken.Data >>= aApiRef;
|
|
lcl_SingleRefToCalc( aComplRef.Ref1, aApiRef.Reference1 );
|
|
lcl_SingleRefToCalc( aComplRef.Ref2, aApiRef.Reference2 );
|
|
|
|
if ( eOpCode == ocPush )
|
|
AddDoubleReference( aComplRef );
|
|
else
|
|
bError = true;
|
|
}
|
|
else if ( aType.equals( cppu::UnoType<sheet::NameToken>::get() ) )
|
|
{
|
|
sheet::NameToken aTokenData;
|
|
rToken.Data >>= aTokenData;
|
|
if ( eOpCode == ocName )
|
|
{
|
|
SAL_WARN_IF( aTokenData.Sheet < -1 || std::numeric_limits<sal_Int16>::max() < aTokenData.Sheet,
|
|
"sc.core",
|
|
"ScTokenArray::AddFormulaToken - NameToken.Sheet out of limits: " << aTokenData.Sheet);
|
|
sal_Int16 nSheet = static_cast<sal_Int16>(aTokenData.Sheet);
|
|
AddRangeName(aTokenData.Index, nSheet);
|
|
}
|
|
else if (eOpCode == ocDBArea)
|
|
AddDBRange(aTokenData.Index);
|
|
else
|
|
bError = true;
|
|
}
|
|
else if ( aType.equals( cppu::UnoType<sheet::TableRefToken>::get() ) )
|
|
{
|
|
if (eOpCode == ocTableRef)
|
|
{
|
|
sheet::TableRefToken aTokenData;
|
|
rToken.Data >>= aTokenData;
|
|
ScTableRefToken* pToken = new ScTableRefToken( aTokenData.Index,
|
|
static_cast<ScTableRefToken::Item>(aTokenData.Item));
|
|
if (Add(pToken)) // else pToken is deleted
|
|
{
|
|
if (aTokenData.Reference.Reference1 == aTokenData.Reference.Reference2)
|
|
{
|
|
ScSingleRefData aRefData;
|
|
lcl_SingleRefToCalc( aRefData, aTokenData.Reference.Reference1 );
|
|
pToken->SetAreaRefRPN( new ScSingleRefToken( *mxSheetLimits, aRefData));
|
|
}
|
|
else
|
|
{
|
|
ScComplexRefData aRefData;
|
|
lcl_SingleRefToCalc( aRefData.Ref1, aTokenData.Reference.Reference1 );
|
|
lcl_SingleRefToCalc( aRefData.Ref2, aTokenData.Reference.Reference2 );
|
|
pToken->SetAreaRefRPN( new ScDoubleRefToken( *mxSheetLimits, aRefData));
|
|
}
|
|
}
|
|
else
|
|
bError = true;
|
|
}
|
|
else
|
|
bError = true;
|
|
}
|
|
else if ( aType.equals( cppu::UnoType<sheet::ExternalReference>::get() ) )
|
|
{
|
|
sheet::ExternalReference aApiExtRef;
|
|
if( (eOpCode == ocPush) && (rToken.Data >>= aApiExtRef) && (0 <= aApiExtRef.Index) && (aApiExtRef.Index <= SAL_MAX_UINT16) )
|
|
{
|
|
sal_uInt16 nFileId = static_cast< sal_uInt16 >( aApiExtRef.Index );
|
|
sheet::SingleReference aApiSRef;
|
|
sheet::ComplexReference aApiCRef;
|
|
OUString aName;
|
|
if( aApiExtRef.Reference >>= aApiSRef )
|
|
{
|
|
// try to resolve cache index to sheet name
|
|
size_t nCacheId = static_cast< size_t >( aApiSRef.Sheet );
|
|
OUString aTabName = pExtRef->getCacheTableName( nFileId, nCacheId );
|
|
if( !aTabName.isEmpty() )
|
|
{
|
|
ScSingleRefData aSingleRef;
|
|
// convert column/row settings, set sheet index to absolute
|
|
lcl_ExternalRefToCalc( aSingleRef, aApiSRef );
|
|
AddExternalSingleReference( nFileId, rSPool.intern( aTabName), aSingleRef );
|
|
}
|
|
else
|
|
bError = true;
|
|
}
|
|
else if( aApiExtRef.Reference >>= aApiCRef )
|
|
{
|
|
// try to resolve cache index to sheet name.
|
|
size_t nCacheId = static_cast< size_t >( aApiCRef.Reference1.Sheet );
|
|
OUString aTabName = pExtRef->getCacheTableName( nFileId, nCacheId );
|
|
if( !aTabName.isEmpty() )
|
|
{
|
|
ScComplexRefData aComplRef;
|
|
// convert column/row settings, set sheet index to absolute
|
|
lcl_ExternalRefToCalc( aComplRef.Ref1, aApiCRef.Reference1 );
|
|
lcl_ExternalRefToCalc( aComplRef.Ref2, aApiCRef.Reference2 );
|
|
// NOTE: This assumes that cached sheets are in consecutive order!
|
|
aComplRef.Ref2.SetAbsTab(
|
|
aComplRef.Ref1.Tab() + static_cast<SCTAB>(aApiCRef.Reference2.Sheet - aApiCRef.Reference1.Sheet));
|
|
AddExternalDoubleReference( nFileId, rSPool.intern( aTabName), aComplRef );
|
|
}
|
|
else
|
|
bError = true;
|
|
}
|
|
else if( aApiExtRef.Reference >>= aName )
|
|
{
|
|
if( !aName.isEmpty() )
|
|
AddExternalName( nFileId, rSPool.intern( aName) );
|
|
else
|
|
bError = true;
|
|
}
|
|
else
|
|
bError = true;
|
|
}
|
|
else
|
|
bError = true;
|
|
}
|
|
else
|
|
bError = true; // unknown struct
|
|
}
|
|
break;
|
|
case uno::TypeClass_SEQUENCE:
|
|
{
|
|
if ( eOpCode != ocPush )
|
|
bError = true; // not an inline array
|
|
else if (!rToken.Data.getValueType().equals( cppu::UnoType<
|
|
uno::Sequence< uno::Sequence< uno::Any >>>::get()))
|
|
bError = true; // unexpected sequence type
|
|
else
|
|
{
|
|
ScMatrixRef xMat = ScSequenceToMatrix::CreateMixedMatrix( rToken.Data);
|
|
if (xMat)
|
|
AddMatrix( xMat);
|
|
else
|
|
bError = true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
bError = true;
|
|
}
|
|
}
|
|
return bError;
|
|
}
|
|
|
|
void ScTokenArray::CheckForThreading( const FormulaToken& r )
|
|
{
|
|
#if HAVE_CPP_CONSTINIT_SORTED_VECTOR
|
|
constinit
|
|
#endif
|
|
static const o3tl::sorted_vector<OpCode> aThreadedCalcDenyList({
|
|
ocIndirect,
|
|
ocMacro,
|
|
ocOffset,
|
|
ocTableOp,
|
|
ocCell,
|
|
ocMatch,
|
|
ocInfo,
|
|
ocStyle,
|
|
ocDBAverage,
|
|
ocDBCount,
|
|
ocDBCount2,
|
|
ocDBGet,
|
|
ocDBMax,
|
|
ocDBMin,
|
|
ocDBProduct,
|
|
ocDBStdDev,
|
|
ocDBStdDevP,
|
|
ocDBSum,
|
|
ocDBVar,
|
|
ocDBVarP,
|
|
ocText,
|
|
ocSheet,
|
|
ocExternal,
|
|
ocDde,
|
|
ocWebservice,
|
|
ocGetPivotData
|
|
});
|
|
|
|
// Don't enable threading once we decided to disable it.
|
|
if (!mbThreadingEnabled)
|
|
return;
|
|
|
|
static const bool bThreadingProhibited = std::getenv("SC_NO_THREADED_CALCULATION");
|
|
|
|
if (bThreadingProhibited)
|
|
{
|
|
mbThreadingEnabled = false;
|
|
return;
|
|
}
|
|
|
|
OpCode eOp = r.GetOpCode();
|
|
|
|
if (aThreadedCalcDenyList.find(eOp) != aThreadedCalcDenyList.end())
|
|
{
|
|
SAL_INFO("sc.core.formulagroup", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
|
|
<< "(" << int(eOp) << ") disables threaded calculation of formula group");
|
|
mbThreadingEnabled = false;
|
|
return;
|
|
}
|
|
|
|
if (eOp != ocPush)
|
|
return;
|
|
|
|
switch (r.GetType())
|
|
{
|
|
case svExternalDoubleRef:
|
|
case svExternalSingleRef:
|
|
case svExternalName:
|
|
case svMatrix:
|
|
SAL_INFO("sc.core.formulagroup", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType())
|
|
<< " disables threaded calculation of formula group");
|
|
mbThreadingEnabled = false;
|
|
return;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ScTokenArray::CheckToken( const FormulaToken& r )
|
|
{
|
|
if (mbThreadingEnabled)
|
|
CheckForThreading(r);
|
|
|
|
if (IsFormulaVectorDisabled())
|
|
return; // It's already disabled. No more checking needed.
|
|
|
|
OpCode eOp = r.GetOpCode();
|
|
|
|
if (SC_OPCODE_START_FUNCTION <= eOp && eOp < SC_OPCODE_STOP_FUNCTION)
|
|
{
|
|
if (ScInterpreter::GetGlobalConfig().mbOpenCLSubsetOnly &&
|
|
ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->find(eOp) == ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->end())
|
|
{
|
|
SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
|
|
<< "(" << int(eOp) << ") disables vectorisation for formula group");
|
|
meVectorState = FormulaVectorDisabledNotInSubSet;
|
|
mbOpenCLEnabled = false;
|
|
return;
|
|
}
|
|
|
|
// We support vectorization for the following opcodes.
|
|
switch (eOp)
|
|
{
|
|
case ocAverage:
|
|
case ocMin:
|
|
case ocMinA:
|
|
case ocMax:
|
|
case ocMaxA:
|
|
case ocSum:
|
|
case ocSumIfs:
|
|
case ocSumProduct:
|
|
case ocCount:
|
|
case ocCount2:
|
|
case ocVLookup:
|
|
case ocXLookup:
|
|
case ocXMatch:
|
|
case ocFilter:
|
|
case ocSort:
|
|
case ocSortBy:
|
|
case ocSLN:
|
|
case ocIRR:
|
|
case ocMIRR:
|
|
case ocPMT:
|
|
case ocRate:
|
|
case ocRRI:
|
|
case ocPpmt:
|
|
case ocFisher:
|
|
case ocFisherInv:
|
|
case ocGamma:
|
|
case ocGammaLn:
|
|
case ocNotAvail:
|
|
case ocGauss:
|
|
case ocGeoMean:
|
|
case ocHarMean:
|
|
case ocSYD:
|
|
case ocCorrel:
|
|
case ocNegBinomVert:
|
|
case ocPearson:
|
|
case ocRSQ:
|
|
case ocCos:
|
|
case ocCosecant:
|
|
case ocCosecantHyp:
|
|
case ocISPMT:
|
|
case ocPDuration:
|
|
case ocSinHyp:
|
|
case ocAbs:
|
|
case ocPV:
|
|
case ocSin:
|
|
case ocTan:
|
|
case ocTanHyp:
|
|
case ocStandard:
|
|
case ocWeibull:
|
|
case ocMedian:
|
|
case ocDDB:
|
|
case ocFV:
|
|
case ocVBD:
|
|
case ocKurt:
|
|
case ocNper:
|
|
case ocNormDist:
|
|
case ocArcCos:
|
|
case ocSqrt:
|
|
case ocArcCosHyp:
|
|
case ocNPV:
|
|
case ocStdNormDist:
|
|
case ocNormInv:
|
|
case ocSNormInv:
|
|
case ocPermut:
|
|
case ocPermutationA:
|
|
case ocPhi:
|
|
case ocIpmt:
|
|
case ocConfidence:
|
|
case ocIntercept:
|
|
case ocDB:
|
|
case ocLogInv:
|
|
case ocArcCot:
|
|
case ocCosHyp:
|
|
case ocCritBinom:
|
|
case ocArcCotHyp:
|
|
case ocArcSin:
|
|
case ocArcSinHyp:
|
|
case ocArcTan:
|
|
case ocArcTanHyp:
|
|
case ocBitAnd:
|
|
case ocForecast:
|
|
case ocLogNormDist:
|
|
case ocGammaDist:
|
|
case ocLn:
|
|
case ocRound:
|
|
case ocCot:
|
|
case ocCotHyp:
|
|
case ocFDist:
|
|
case ocVar:
|
|
case ocChiDist:
|
|
case ocPower:
|
|
case ocOdd:
|
|
case ocChiSqDist:
|
|
case ocChiSqInv:
|
|
case ocGammaInv:
|
|
case ocFloor:
|
|
case ocFInv:
|
|
case ocFTest:
|
|
case ocB:
|
|
case ocBetaDist:
|
|
case ocExp:
|
|
case ocLog10:
|
|
case ocExpDist:
|
|
case ocAverageIfs:
|
|
case ocCountIfs:
|
|
case ocCombinA:
|
|
case ocEven:
|
|
case ocLog:
|
|
case ocMod:
|
|
case ocTrunc:
|
|
case ocSkew:
|
|
case ocArcTan2:
|
|
case ocBitOr:
|
|
case ocBitLshift:
|
|
case ocBitRshift:
|
|
case ocBitXor:
|
|
case ocChiInv:
|
|
case ocPoissonDist:
|
|
case ocSumSQ:
|
|
case ocSkewp:
|
|
case ocBinomDist:
|
|
case ocVarP:
|
|
case ocCeil:
|
|
case ocCombin:
|
|
case ocDevSq:
|
|
case ocStDev:
|
|
case ocSlope:
|
|
case ocSTEYX:
|
|
case ocZTest:
|
|
case ocPi:
|
|
case ocRandom:
|
|
case ocProduct:
|
|
case ocHypGeomDist:
|
|
case ocSumX2MY2:
|
|
case ocSumX2DY2:
|
|
case ocBetaInv:
|
|
case ocTTest:
|
|
case ocTDist:
|
|
case ocTInv:
|
|
case ocSumXMY2:
|
|
case ocStDevP:
|
|
case ocCovar:
|
|
case ocAnd:
|
|
case ocOr:
|
|
case ocNot:
|
|
case ocXor:
|
|
case ocDBMax:
|
|
case ocDBMin:
|
|
case ocDBProduct:
|
|
case ocDBAverage:
|
|
case ocDBStdDev:
|
|
case ocDBStdDevP:
|
|
case ocDBSum:
|
|
case ocDBVar:
|
|
case ocDBVarP:
|
|
case ocAverageIf:
|
|
case ocDBCount:
|
|
case ocDBCount2:
|
|
case ocDeg:
|
|
case ocRoundUp:
|
|
case ocRoundDown:
|
|
case ocInt:
|
|
case ocRad:
|
|
case ocCountIf:
|
|
case ocIsEven:
|
|
case ocIsOdd:
|
|
case ocFact:
|
|
case ocAverageA:
|
|
case ocVarA:
|
|
case ocVarPA:
|
|
case ocStDevA:
|
|
case ocStDevPA:
|
|
case ocSecant:
|
|
case ocSecantHyp:
|
|
case ocSumIf:
|
|
case ocNegSub:
|
|
case ocAveDev:
|
|
case ocMatSequence:
|
|
case ocRandArray:
|
|
case ocUnique:
|
|
// Don't change the state.
|
|
break;
|
|
default:
|
|
SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
|
|
<< "(" << int(eOp) << ") disables vectorisation for formula group");
|
|
meVectorState = FormulaVectorDisabledByOpCode;
|
|
mbOpenCLEnabled = false;
|
|
return;
|
|
}
|
|
}
|
|
else if (eOp == ocPush)
|
|
{
|
|
// This is a stack variable. See if this is a reference.
|
|
|
|
switch (r.GetType())
|
|
{
|
|
case svByte:
|
|
case svDouble:
|
|
case svString:
|
|
// Don't change the state.
|
|
break;
|
|
case svSingleRef:
|
|
case svDoubleRef:
|
|
// Depends on the reference state.
|
|
meVectorState = FormulaVectorCheckReference;
|
|
break;
|
|
case svError:
|
|
case svEmptyCell:
|
|
case svExternal:
|
|
case svExternalDoubleRef:
|
|
case svExternalName:
|
|
case svExternalSingleRef:
|
|
case svFAP:
|
|
case svHybridCell:
|
|
case svIndex:
|
|
case svJump:
|
|
case svJumpMatrix:
|
|
case svMatrix:
|
|
case svMatrixCell:
|
|
case svMissing:
|
|
case svRefList:
|
|
case svSep:
|
|
case svUnknown:
|
|
// We don't support vectorization on these.
|
|
SAL_INFO("sc.opencl", "opcode ocPush: variable type " << StackVarEnumToString(r.GetType()) << " disables vectorisation for formula group");
|
|
meVectorState = FormulaVectorDisabledByStackVariable;
|
|
mbOpenCLEnabled = false;
|
|
return;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
else if (SC_OPCODE_START_BIN_OP <= eOp && eOp < SC_OPCODE_STOP_UN_OP)
|
|
{
|
|
if (ScInterpreter::GetGlobalConfig().mbOpenCLSubsetOnly &&
|
|
ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->find(eOp) == ScInterpreter::GetGlobalConfig().mpOpenCLSubsetOpCodes->end())
|
|
{
|
|
SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
|
|
<< "(" << int(eOp) << ") disables vectorisation for formula group");
|
|
meVectorState = FormulaVectorDisabledNotInSubSet;
|
|
mbOpenCLEnabled = false;
|
|
return;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// All the rest, special commands, separators, error codes, ...
|
|
switch (eOp)
|
|
{
|
|
default:
|
|
// Default is off, no vectorization.
|
|
// Mentioning some specific values below to indicate why.
|
|
|
|
case ocName:
|
|
// Named expression would need "recursive" handling of its
|
|
// token array for vector state in
|
|
// ScFormulaCell::InterpretFormulaGroup() and below.
|
|
|
|
case ocDBArea:
|
|
// Certainly not a vectorization of the entire area...
|
|
|
|
case ocTableRef:
|
|
// May result in a single cell or range reference, depending on
|
|
// context.
|
|
|
|
case ocColRowName:
|
|
// The associated reference is the name cell with which to
|
|
// create the implicit intersection.
|
|
|
|
case ocColRowNameAuto:
|
|
// Auto column/row names lead to references computed in
|
|
// interpreter.
|
|
|
|
SAL_INFO("sc.opencl", "opcode " << formula::FormulaCompiler().GetOpCodeMap(sheet::FormulaLanguage::ENGLISH)->getSymbol(eOp)
|
|
<< "(" << int(eOp) << ") disables vectorisation for formula group");
|
|
meVectorState = FormulaVectorDisabledByOpCode;
|
|
mbOpenCLEnabled = false;
|
|
return;
|
|
|
|
// Known good, don't change state.
|
|
case ocStop:
|
|
case ocExternal:
|
|
case ocOpen:
|
|
case ocClose:
|
|
case ocSep:
|
|
case ocArrayOpen:
|
|
case ocArrayRowSep:
|
|
case ocArrayColSep:
|
|
case ocArrayClose:
|
|
case ocMissing:
|
|
case ocBad:
|
|
case ocSpaces:
|
|
case ocWhitespace:
|
|
case ocSkip:
|
|
case ocPercentSign:
|
|
case ocErrNull:
|
|
case ocErrDivZero:
|
|
case ocErrValue:
|
|
case ocErrRef:
|
|
case ocErrName:
|
|
case ocErrNum:
|
|
case ocErrNA:
|
|
break;
|
|
case ocIf:
|
|
case ocIfError:
|
|
case ocIfNA:
|
|
case ocChoose:
|
|
case ocLet:
|
|
// Jump commands are now supported.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ScTokenArray::ImplGetReference( ScRange& rRange, const ScAddress& rPos, bool bValidOnly ) const
|
|
{
|
|
bool bIs = false;
|
|
if ( pCode && nLen == 1 )
|
|
{
|
|
const FormulaToken* pToken = pCode[0];
|
|
if ( pToken )
|
|
{
|
|
if ( pToken->GetType() == svSingleRef )
|
|
{
|
|
const ScSingleRefData& rRef = *static_cast<const ScSingleRefToken*>(pToken)->GetSingleRef();
|
|
rRange.aStart = rRange.aEnd = rRef.toAbs(*mxSheetLimits, rPos);
|
|
bIs = !bValidOnly || mxSheetLimits->ValidAddress(rRange.aStart);
|
|
}
|
|
else if ( pToken->GetType() == svDoubleRef )
|
|
{
|
|
const ScComplexRefData& rCompl = *static_cast<const ScDoubleRefToken*>(pToken)->GetDoubleRef();
|
|
const ScSingleRefData& rRef1 = rCompl.Ref1;
|
|
const ScSingleRefData& rRef2 = rCompl.Ref2;
|
|
rRange.aStart = rRef1.toAbs(*mxSheetLimits, rPos);
|
|
rRange.aEnd = rRef2.toAbs(*mxSheetLimits, rPos);
|
|
bIs = !bValidOnly || mxSheetLimits->ValidRange(rRange);
|
|
}
|
|
}
|
|
}
|
|
return bIs;
|
|
}
|
|
|
|
namespace {
|
|
|
|
// we want to compare for similar not identical formulae
|
|
// so we can't use actual row & column indices.
|
|
size_t HashSingleRef( const ScSingleRefData& rRef )
|
|
{
|
|
size_t nVal = 0;
|
|
|
|
nVal += size_t(rRef.IsColRel());
|
|
nVal += (size_t(rRef.IsRowRel()) << 1);
|
|
nVal += (size_t(rRef.IsTabRel()) << 2);
|
|
|
|
return nVal;
|
|
}
|
|
|
|
}
|
|
|
|
void ScTokenArray::GenHash()
|
|
{
|
|
static const OUStringHash aHasher;
|
|
|
|
size_t nHash = 1;
|
|
OpCode eOp;
|
|
StackVar eType;
|
|
const formula::FormulaToken* p;
|
|
sal_uInt16 n = std::min<sal_uInt16>(nLen, 20);
|
|
for (sal_uInt16 i = 0; i < n; ++i)
|
|
{
|
|
p = pCode[i];
|
|
eOp = p->GetOpCode();
|
|
if (eOp == ocPush)
|
|
{
|
|
// This is stack variable. Do additional differentiation.
|
|
eType = p->GetType();
|
|
switch (eType)
|
|
{
|
|
case svByte:
|
|
{
|
|
// Constant value.
|
|
sal_uInt8 nVal = p->GetByte();
|
|
nHash += static_cast<size_t>(nVal);
|
|
}
|
|
break;
|
|
case svDouble:
|
|
{
|
|
// Constant value.
|
|
double fVal = p->GetDouble();
|
|
nHash += std::hash<double>()(fVal);
|
|
}
|
|
break;
|
|
case svString:
|
|
{
|
|
// Constant string.
|
|
OUString aStr = p->GetString().getString();
|
|
nHash += aHasher(aStr);
|
|
}
|
|
break;
|
|
case svSingleRef:
|
|
{
|
|
size_t nVal = HashSingleRef(*p->GetSingleRef());
|
|
nHash += nVal;
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
const ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
size_t nVal1 = HashSingleRef(rRef.Ref1);
|
|
size_t nVal2 = HashSingleRef(rRef.Ref2);
|
|
nHash += nVal1;
|
|
nHash += nVal2;
|
|
}
|
|
break;
|
|
default:
|
|
// Use the opcode value in all the other cases.
|
|
nHash += static_cast<size_t>(eOp);
|
|
}
|
|
}
|
|
else
|
|
// Use the opcode value in all the other cases.
|
|
nHash += static_cast<size_t>(eOp);
|
|
|
|
nHash = (nHash << 4) - nHash;
|
|
}
|
|
|
|
mnHashValue = nHash;
|
|
}
|
|
|
|
void ScTokenArray::ResetVectorState()
|
|
{
|
|
mbOpenCLEnabled = ScCalcConfig::isOpenCLEnabled();
|
|
meVectorState = mbOpenCLEnabled ? FormulaVectorEnabled : FormulaVectorDisabled;
|
|
mbThreadingEnabled = ScCalcConfig::isThreadingEnabled();
|
|
}
|
|
|
|
bool ScTokenArray::IsFormulaVectorDisabled() const
|
|
{
|
|
switch (meVectorState)
|
|
{
|
|
case FormulaVectorDisabled:
|
|
case FormulaVectorDisabledByOpCode:
|
|
case FormulaVectorDisabledByStackVariable:
|
|
case FormulaVectorDisabledNotInSubSet:
|
|
return true;
|
|
default:
|
|
;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ScTokenArray::IsInvariant() const
|
|
{
|
|
FormulaToken** p = pCode.get();
|
|
FormulaToken** pEnd = p + static_cast<size_t>(nLen);
|
|
for (; p != pEnd; ++p)
|
|
{
|
|
switch ((*p)->GetType())
|
|
{
|
|
case svSingleRef:
|
|
case svExternalSingleRef:
|
|
{
|
|
const ScSingleRefData& rRef = *(*p)->GetSingleRef();
|
|
if (rRef.IsRowRel())
|
|
return false;
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
case svExternalDoubleRef:
|
|
{
|
|
const ScComplexRefData& rRef = *(*p)->GetDoubleRef();
|
|
if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
|
|
return false;
|
|
}
|
|
break;
|
|
case svIndex:
|
|
return false;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool ScTokenArray::IsReference( ScRange& rRange, const ScAddress& rPos ) const
|
|
{
|
|
return ImplGetReference(rRange, rPos, false);
|
|
}
|
|
|
|
bool ScTokenArray::IsValidReference( ScRange& rRange, const ScAddress& rPos ) const
|
|
{
|
|
return ImplGetReference(rRange, rPos, true);
|
|
}
|
|
|
|
ScTokenArray::ScTokenArray(const ScDocument& rDoc) :
|
|
mxSheetLimits(&rDoc.GetSheetLimits()),
|
|
mnHashValue(0)
|
|
{
|
|
ResetVectorState();
|
|
}
|
|
|
|
ScTokenArray::ScTokenArray(ScSheetLimits& rLimits) :
|
|
mxSheetLimits(&rLimits),
|
|
mnHashValue(0)
|
|
{
|
|
ResetVectorState();
|
|
}
|
|
|
|
ScTokenArray::~ScTokenArray()
|
|
{
|
|
}
|
|
|
|
ScTokenArray& ScTokenArray::operator=( const ScTokenArray& rArr )
|
|
{
|
|
Clear();
|
|
Assign( rArr );
|
|
mnHashValue = rArr.mnHashValue;
|
|
meVectorState = rArr.meVectorState;
|
|
mbOpenCLEnabled = rArr.mbOpenCLEnabled;
|
|
mbThreadingEnabled = rArr.mbThreadingEnabled;
|
|
return *this;
|
|
}
|
|
|
|
ScTokenArray& ScTokenArray::operator=( ScTokenArray&& rArr )
|
|
{
|
|
mxSheetLimits = std::move(rArr.mxSheetLimits);
|
|
mnHashValue = rArr.mnHashValue;
|
|
meVectorState = rArr.meVectorState;
|
|
mbOpenCLEnabled = rArr.mbOpenCLEnabled;
|
|
mbThreadingEnabled = rArr.mbThreadingEnabled;
|
|
Move(std::move(rArr));
|
|
return *this;
|
|
}
|
|
|
|
bool ScTokenArray::EqualTokens( const ScTokenArray* pArr2) const
|
|
{
|
|
// We only compare the non-RPN array
|
|
if ( pArr2->nLen != nLen )
|
|
return false;
|
|
|
|
FormulaToken** ppToken1 = GetArray();
|
|
FormulaToken** ppToken2 = pArr2->GetArray();
|
|
for (sal_uInt16 i=0; i<nLen; i++)
|
|
{
|
|
if ( ppToken1[i] != ppToken2[i] &&
|
|
!(*ppToken1[i] == *ppToken2[i]) )
|
|
return false; // Difference
|
|
}
|
|
return true; // All entries are the same
|
|
}
|
|
|
|
void ScTokenArray::Clear()
|
|
{
|
|
mnHashValue = 0;
|
|
ResetVectorState();
|
|
FormulaTokenArray::Clear();
|
|
}
|
|
|
|
std::unique_ptr<ScTokenArray> ScTokenArray::Clone() const
|
|
{
|
|
std::unique_ptr<ScTokenArray> p(new ScTokenArray(*mxSheetLimits));
|
|
p->nLen = nLen;
|
|
p->nRPN = nRPN;
|
|
p->nMode = nMode;
|
|
p->nError = nError;
|
|
p->bHyperLink = bHyperLink;
|
|
p->mnHashValue = mnHashValue;
|
|
p->meVectorState = meVectorState;
|
|
p->mbOpenCLEnabled = mbOpenCLEnabled;
|
|
p->mbThreadingEnabled = mbThreadingEnabled;
|
|
p->mbFromRangeName = mbFromRangeName;
|
|
p->mbShareable = mbShareable;
|
|
|
|
FormulaToken** pp;
|
|
if( nLen )
|
|
{
|
|
p->pCode.reset(new FormulaToken*[ nLen ]);
|
|
pp = p->pCode.get();
|
|
memcpy( pp, pCode.get(), nLen * sizeof( formula::FormulaToken* ) );
|
|
for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
|
|
{
|
|
*pp = (*pp)->Clone();
|
|
(*pp)->IncRef();
|
|
}
|
|
}
|
|
if( nRPN )
|
|
{
|
|
pp = p->pRPN = new FormulaToken*[ nRPN ];
|
|
memcpy( pp, pRPN, nRPN * sizeof( formula::FormulaToken* ) );
|
|
for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
|
|
{
|
|
FormulaToken* t = *pp;
|
|
if( t->GetRef() > 1 )
|
|
{
|
|
FormulaToken** p2 = pCode.get();
|
|
sal_uInt16 nIdx = 0xFFFF;
|
|
for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
|
|
{
|
|
if( *p2 == t )
|
|
{
|
|
nIdx = j; break;
|
|
}
|
|
}
|
|
if( nIdx == 0xFFFF )
|
|
*pp = t->Clone();
|
|
else
|
|
*pp = p->pCode[ nIdx ];
|
|
}
|
|
else
|
|
*pp = t->Clone();
|
|
(*pp)->IncRef();
|
|
}
|
|
}
|
|
return p;
|
|
}
|
|
|
|
ScTokenArray ScTokenArray::CloneValue() const
|
|
{
|
|
ScTokenArray aNew(*mxSheetLimits);
|
|
aNew.nLen = nLen;
|
|
aNew.nRPN = nRPN;
|
|
aNew.nMode = nMode;
|
|
aNew.nError = nError;
|
|
aNew.bHyperLink = bHyperLink;
|
|
aNew.mnHashValue = mnHashValue;
|
|
aNew.meVectorState = meVectorState;
|
|
aNew.mbOpenCLEnabled = mbOpenCLEnabled;
|
|
aNew.mbThreadingEnabled = mbThreadingEnabled;
|
|
aNew.mbFromRangeName = mbFromRangeName;
|
|
aNew.mbShareable = mbShareable;
|
|
|
|
FormulaToken** pp;
|
|
if( nLen )
|
|
{
|
|
aNew.pCode.reset(new FormulaToken*[ nLen ]);
|
|
pp = aNew.pCode.get();
|
|
memcpy( pp, pCode.get(), nLen * sizeof( formula::FormulaToken* ) );
|
|
for( sal_uInt16 i = 0; i < nLen; i++, pp++ )
|
|
{
|
|
*pp = (*pp)->Clone();
|
|
(*pp)->IncRef();
|
|
}
|
|
}
|
|
if( nRPN )
|
|
{
|
|
pp = aNew.pRPN = new FormulaToken*[ nRPN ];
|
|
memcpy( pp, pRPN, nRPN * sizeof( formula::FormulaToken* ) );
|
|
for( sal_uInt16 i = 0; i < nRPN; i++, pp++ )
|
|
{
|
|
FormulaToken* t = *pp;
|
|
if( t->GetRef() > 1 )
|
|
{
|
|
FormulaToken** p2 = pCode.get();
|
|
sal_uInt16 nIdx = 0xFFFF;
|
|
for( sal_uInt16 j = 0; j < nLen; j++, p2++ )
|
|
{
|
|
if( *p2 == t )
|
|
{
|
|
nIdx = j; break;
|
|
}
|
|
}
|
|
if( nIdx == 0xFFFF )
|
|
*pp = t->Clone();
|
|
else
|
|
*pp = aNew.pCode[ nIdx ];
|
|
}
|
|
else
|
|
*pp = t->Clone();
|
|
(*pp)->IncRef();
|
|
}
|
|
}
|
|
return aNew;
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddRawToken( const ScRawToken& r )
|
|
{
|
|
return Add( r.CreateToken(*mxSheetLimits) );
|
|
}
|
|
|
|
// Utility function to ensure that there is strict alternation of values and
|
|
// separators.
|
|
static bool
|
|
checkArraySep( bool & bPrevWasSep, bool bNewVal )
|
|
{
|
|
bool bResult = (bPrevWasSep == bNewVal);
|
|
bPrevWasSep = bNewVal;
|
|
return bResult;
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::MergeArray( )
|
|
{
|
|
int nCol = -1, nRow = 0;
|
|
int i, nPrevRowSep = -1, nStart = 0;
|
|
bool bPrevWasSep = false; // top of stack is ocArrayClose
|
|
FormulaToken* t;
|
|
bool bNumeric = false; // numeric value encountered in current element
|
|
|
|
// (1) Iterate from the end to the start to find matrix dims
|
|
// and do basic validation.
|
|
for ( i = nLen ; i-- > nStart ; )
|
|
{
|
|
t = pCode[i];
|
|
switch ( t->GetOpCode() )
|
|
{
|
|
case ocPush :
|
|
if( checkArraySep( bPrevWasSep, false ) )
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// no references or nested arrays
|
|
if ( t->GetType() != svDouble && t->GetType() != svString )
|
|
{
|
|
return nullptr;
|
|
}
|
|
bNumeric = (t->GetType() == svDouble);
|
|
break;
|
|
|
|
case ocMissing :
|
|
case ocTrue :
|
|
case ocFalse :
|
|
if( checkArraySep( bPrevWasSep, false ) )
|
|
{
|
|
return nullptr;
|
|
}
|
|
bNumeric = false;
|
|
break;
|
|
|
|
case ocArrayColSep :
|
|
case ocSep :
|
|
if( checkArraySep( bPrevWasSep, true ) )
|
|
{
|
|
return nullptr;
|
|
}
|
|
bNumeric = false;
|
|
break;
|
|
|
|
case ocArrayClose :
|
|
// not possible with the , but check just in case
|
|
// something changes in the future
|
|
if( i != (nLen-1))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if( checkArraySep( bPrevWasSep, true ) )
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
nPrevRowSep = i;
|
|
bNumeric = false;
|
|
break;
|
|
|
|
case ocArrayOpen :
|
|
nStart = i; // stop iteration
|
|
[[fallthrough]]; // to ArrayRowSep
|
|
|
|
case ocArrayRowSep :
|
|
if( checkArraySep( bPrevWasSep, true ) )
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if( nPrevRowSep < 0 || // missing ocArrayClose
|
|
((nPrevRowSep - i) % 2) == 1) // no complex elements
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
if( nCol < 0 )
|
|
{
|
|
nCol = (nPrevRowSep - i) / 2;
|
|
}
|
|
else if( (nPrevRowSep - i)/2 != nCol) // irregular array
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
nPrevRowSep = i;
|
|
nRow++;
|
|
bNumeric = false;
|
|
break;
|
|
|
|
case ocNegSub :
|
|
case ocAdd :
|
|
// negation or unary plus must precede numeric value
|
|
if( !bNumeric )
|
|
{
|
|
return nullptr;
|
|
}
|
|
--nPrevRowSep; // shorten this row by 1
|
|
bNumeric = false; // one level only, no --42
|
|
break;
|
|
|
|
case ocSpaces :
|
|
case ocWhitespace :
|
|
// ignore spaces
|
|
--nPrevRowSep; // shorten this row by 1
|
|
break;
|
|
|
|
default :
|
|
// no functions or operators
|
|
return nullptr;
|
|
}
|
|
}
|
|
if( nCol <= 0 || nRow <= 0 )
|
|
return nullptr;
|
|
|
|
int nSign = 1;
|
|
ScMatrix* pArray = new ScMatrix(nCol, nRow, 0.0);
|
|
for ( i = nStart, nCol = 0, nRow = 0 ; i < nLen ; i++ )
|
|
{
|
|
t = pCode[i];
|
|
|
|
switch ( t->GetOpCode() )
|
|
{
|
|
case ocPush :
|
|
if ( t->GetType() == svDouble )
|
|
{
|
|
pArray->PutDouble( t->GetDouble() * nSign, nCol, nRow );
|
|
nSign = 1;
|
|
}
|
|
else if ( t->GetType() == svString )
|
|
{
|
|
pArray->PutString(t->GetString(), nCol, nRow);
|
|
}
|
|
break;
|
|
|
|
case ocMissing :
|
|
pArray->PutEmpty( nCol, nRow );
|
|
break;
|
|
|
|
case ocTrue :
|
|
pArray->PutBoolean( true, nCol, nRow );
|
|
break;
|
|
|
|
case ocFalse :
|
|
pArray->PutBoolean( false, nCol, nRow );
|
|
break;
|
|
|
|
case ocArrayColSep :
|
|
case ocSep :
|
|
nCol++;
|
|
break;
|
|
|
|
case ocArrayRowSep :
|
|
nRow++; nCol = 0;
|
|
break;
|
|
|
|
case ocNegSub :
|
|
nSign = -nSign;
|
|
break;
|
|
|
|
default :
|
|
break;
|
|
}
|
|
pCode[i] = nullptr;
|
|
t->DecRef();
|
|
}
|
|
nLen = sal_uInt16( nStart );
|
|
return AddMatrix( pArray );
|
|
}
|
|
|
|
void ScTokenArray::MergeRangeReference( const ScAddress & rPos )
|
|
{
|
|
if (!pCode || !nLen)
|
|
return;
|
|
sal_uInt16 nIdx = nLen;
|
|
|
|
// The actual types are checked in extendRangeReference().
|
|
FormulaToken *p3 = PeekPrev(nIdx); // ref
|
|
if (!p3)
|
|
return;
|
|
FormulaToken *p2 = PeekPrev(nIdx); // ocRange
|
|
if (!p2 || p2->GetOpCode() != ocRange)
|
|
return;
|
|
FormulaToken *p1 = PeekPrev(nIdx); // ref
|
|
if (!p1)
|
|
return;
|
|
FormulaTokenRef p = extendRangeReference( *mxSheetLimits, *p1, *p3, rPos, true);
|
|
if (p)
|
|
{
|
|
p->IncRef();
|
|
p1->DecRef();
|
|
p2->DecRef();
|
|
p3->DecRef();
|
|
nLen -= 2;
|
|
pCode[ nLen-1 ] = p.get();
|
|
}
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddOpCode( OpCode e )
|
|
{
|
|
ScRawToken t;
|
|
t.SetOpCode( e );
|
|
return AddRawToken( t );
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddSingleReference( const ScSingleRefData& rRef )
|
|
{
|
|
return Add( new ScSingleRefToken( *mxSheetLimits, rRef ) );
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddMatrixSingleReference( const ScSingleRefData& rRef )
|
|
{
|
|
return Add( new ScSingleRefToken(*mxSheetLimits, rRef, ocMatRef ) );
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddDoubleReference( const ScComplexRefData& rRef )
|
|
{
|
|
return Add( new ScDoubleRefToken(*mxSheetLimits, rRef ) );
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddMatrix( const ScMatrixRef& p )
|
|
{
|
|
return Add( new ScMatrixToken( p ) );
|
|
}
|
|
|
|
void ScTokenArray::AddRangeName( sal_uInt16 n, sal_Int16 nSheet )
|
|
{
|
|
Add( new FormulaIndexToken( ocName, n, nSheet));
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddDBRange( sal_uInt16 n )
|
|
{
|
|
return Add( new FormulaIndexToken( ocDBArea, n));
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddExternalName( sal_uInt16 nFileId, const svl::SharedString& rName )
|
|
{
|
|
return Add( new ScExternalNameToken(nFileId, rName) );
|
|
}
|
|
|
|
void ScTokenArray::AddExternalSingleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName,
|
|
const ScSingleRefData& rRef )
|
|
{
|
|
Add( new ScExternalSingleRefToken(nFileId, rTabName, rRef) );
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddExternalDoubleReference( sal_uInt16 nFileId, const svl::SharedString& rTabName,
|
|
const ScComplexRefData& rRef )
|
|
{
|
|
return Add( new ScExternalDoubleRefToken(nFileId, rTabName, rRef) );
|
|
}
|
|
|
|
FormulaToken* ScTokenArray::AddColRowName( const ScSingleRefData& rRef )
|
|
{
|
|
return Add( new ScSingleRefToken(*mxSheetLimits, rRef, ocColRowName ) );
|
|
}
|
|
|
|
void ScTokenArray::AssignXMLString( const OUString &rText, const OUString &rFormulaNmsp )
|
|
{
|
|
sal_uInt16 nTokens = 1;
|
|
FormulaToken *aTokens[2];
|
|
|
|
aTokens[0] = new FormulaStringOpToken( ocStringXML, svl::SharedString( rText) ); // string not interned
|
|
if( !rFormulaNmsp.isEmpty() )
|
|
aTokens[ nTokens++ ] = new FormulaStringOpToken( ocStringXML,
|
|
svl::SharedString( rFormulaNmsp) ); // string not interned
|
|
|
|
Assign( nTokens, aTokens );
|
|
}
|
|
|
|
bool ScTokenArray::GetAdjacentExtendOfOuterFuncRefs( SCCOLROW& nExtend,
|
|
const ScAddress& rPos, ScDirection eDir )
|
|
{
|
|
SCCOL nCol = 0;
|
|
SCROW nRow = 0;
|
|
switch ( eDir )
|
|
{
|
|
case DIR_BOTTOM :
|
|
if ( rPos.Row() >= mxSheetLimits->mnMaxRow )
|
|
return false;
|
|
nExtend = rPos.Row();
|
|
nRow = nExtend + 1;
|
|
break;
|
|
case DIR_RIGHT :
|
|
if ( rPos.Col() >= mxSheetLimits->mnMaxCol )
|
|
return false;
|
|
nExtend = rPos.Col();
|
|
nCol = static_cast<SCCOL>(nExtend) + 1;
|
|
break;
|
|
case DIR_TOP :
|
|
if ( rPos.Row() <= 0 )
|
|
return false;
|
|
nExtend = rPos.Row();
|
|
nRow = nExtend - 1;
|
|
break;
|
|
case DIR_LEFT :
|
|
if ( rPos.Col() <= 0 )
|
|
return false;
|
|
nExtend = rPos.Col();
|
|
nCol = static_cast<SCCOL>(nExtend) - 1;
|
|
break;
|
|
default:
|
|
OSL_FAIL( "unknown Direction" );
|
|
return false;
|
|
}
|
|
if ( pRPN && nRPN )
|
|
{
|
|
FormulaToken* t = pRPN[nRPN-1];
|
|
if ( t->GetType() == svByte )
|
|
{
|
|
sal_uInt8 nParamCount = t->GetByte();
|
|
if ( nParamCount && nRPN > nParamCount )
|
|
{
|
|
bool bRet = false;
|
|
sal_uInt16 nParam = nRPN - nParamCount - 1;
|
|
for ( ; nParam < nRPN-1; nParam++ )
|
|
{
|
|
FormulaToken* p = pRPN[nParam];
|
|
switch ( p->GetType() )
|
|
{
|
|
case svSingleRef :
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
|
|
switch ( eDir )
|
|
{
|
|
case DIR_BOTTOM :
|
|
if (aAbs.Row() == nRow && aAbs.Row() > nExtend)
|
|
{
|
|
nExtend = aAbs.Row();
|
|
bRet = true;
|
|
}
|
|
break;
|
|
case DIR_RIGHT :
|
|
if (aAbs.Col() == nCol && static_cast<SCCOLROW>(aAbs.Col()) > nExtend)
|
|
{
|
|
nExtend = aAbs.Col();
|
|
bRet = true;
|
|
}
|
|
break;
|
|
case DIR_TOP :
|
|
if (aAbs.Row() == nRow && aAbs.Row() < nExtend)
|
|
{
|
|
nExtend = aAbs.Row();
|
|
bRet = true;
|
|
}
|
|
break;
|
|
case DIR_LEFT :
|
|
if (aAbs.Col() == nCol && static_cast<SCCOLROW>(aAbs.Col()) < nExtend)
|
|
{
|
|
nExtend = aAbs.Col();
|
|
bRet = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef :
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
|
|
switch ( eDir )
|
|
{
|
|
case DIR_BOTTOM :
|
|
if (aAbs.aStart.Row() == nRow && aAbs.aEnd.Row() > nExtend)
|
|
{
|
|
nExtend = aAbs.aEnd.Row();
|
|
bRet = true;
|
|
}
|
|
break;
|
|
case DIR_RIGHT :
|
|
if (aAbs.aStart.Col() == nCol && static_cast<SCCOLROW>(aAbs.aEnd.Col()) > nExtend)
|
|
{
|
|
nExtend = aAbs.aEnd.Col();
|
|
bRet = true;
|
|
}
|
|
break;
|
|
case DIR_TOP :
|
|
if (aAbs.aEnd.Row() == nRow && aAbs.aStart.Row() < nExtend)
|
|
{
|
|
nExtend = aAbs.aStart.Row();
|
|
bRet = true;
|
|
}
|
|
break;
|
|
case DIR_LEFT :
|
|
if (aAbs.aEnd.Col() == nCol && static_cast<SCCOLROW>(aAbs.aStart.Col()) < nExtend)
|
|
{
|
|
nExtend = aAbs.aStart.Col();
|
|
bRet = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// added to avoid warnings
|
|
}
|
|
} // switch
|
|
} // for
|
|
return bRet;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
|
|
void GetExternalTableData(const ScDocument* pOldDoc, const ScDocument* pNewDoc, const SCTAB nTab, OUString& rTabName, sal_uInt16& rFileId)
|
|
{
|
|
const OUString& aFileName = pOldDoc->GetFileURL();
|
|
rFileId = pNewDoc->GetExternalRefManager()->getExternalFileId(aFileName);
|
|
rTabName = pOldDoc->GetCopyTabName(nTab);
|
|
if (rTabName.isEmpty())
|
|
pOldDoc->GetName(nTab, rTabName);
|
|
}
|
|
|
|
bool IsInCopyRange( const ScRange& rRange, const ScDocument* pClipDoc )
|
|
{
|
|
ScClipParam& rClipParam = const_cast<ScDocument*>(pClipDoc)->GetClipParam();
|
|
return rClipParam.maRanges.Contains(rRange);
|
|
}
|
|
|
|
bool SkipReference(formula::FormulaToken* pToken, const ScAddress& rPos, const ScDocument& rOldDoc, bool bRangeName, bool bCheckCopyArea)
|
|
{
|
|
ScRange aRange;
|
|
|
|
if (!ScRefTokenHelper::getRangeFromToken(&rOldDoc, aRange, pToken, rPos))
|
|
return true;
|
|
|
|
if (bRangeName && aRange.aStart.Tab() == rPos.Tab())
|
|
{
|
|
switch (pToken->GetType())
|
|
{
|
|
case svDoubleRef:
|
|
{
|
|
ScSingleRefData& rRef = *pToken->GetSingleRef2();
|
|
if (rRef.IsColRel() || rRef.IsRowRel())
|
|
return true;
|
|
}
|
|
[[fallthrough]];
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *pToken->GetSingleRef();
|
|
if (rRef.IsColRel() || rRef.IsRowRel())
|
|
return true;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bCheckCopyArea && IsInCopyRange(aRange, &rOldDoc))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
void AdjustSingleRefData( ScSingleRefData& rRef, const ScAddress& rOldPos, const ScAddress& rNewPos)
|
|
{
|
|
SCCOL nCols = rNewPos.Col() - rOldPos.Col();
|
|
SCROW nRows = rNewPos.Row() - rOldPos.Row();
|
|
SCTAB nTabs = rNewPos.Tab() - rOldPos.Tab();
|
|
|
|
if (!rRef.IsColRel())
|
|
rRef.IncCol(nCols);
|
|
|
|
if (!rRef.IsRowRel())
|
|
rRef.IncRow(nRows);
|
|
|
|
if (!rRef.IsTabRel())
|
|
rRef.IncTab(nTabs);
|
|
}
|
|
|
|
}
|
|
|
|
void ScTokenArray::ReadjustAbsolute3DReferences( const ScDocument& rOldDoc, ScDocument& rNewDoc, const ScAddress& rPos, bool bRangeName )
|
|
{
|
|
for ( sal_uInt16 j=0; j<nLen; ++j )
|
|
{
|
|
switch ( pCode[j]->GetType() )
|
|
{
|
|
case svDoubleRef :
|
|
{
|
|
if (SkipReference(pCode[j], rPos, rOldDoc, bRangeName, true))
|
|
continue;
|
|
|
|
ScComplexRefData& rRef = *pCode[j]->GetDoubleRef();
|
|
ScSingleRefData& rRef2 = rRef.Ref2;
|
|
ScSingleRefData& rRef1 = rRef.Ref1;
|
|
|
|
if ( (rRef2.IsFlag3D() && !rRef2.IsTabRel()) || (rRef1.IsFlag3D() && !rRef1.IsTabRel()) )
|
|
{
|
|
OUString aTabName;
|
|
sal_uInt16 nFileId;
|
|
GetExternalTableData(&rOldDoc, &rNewDoc, rRef1.Tab(), aTabName, nFileId);
|
|
ReplaceToken( j, new ScExternalDoubleRefToken( nFileId,
|
|
rNewDoc.GetSharedStringPool().intern( aTabName), rRef), CODE_AND_RPN);
|
|
// ATTENTION: rRef can't be used after this point
|
|
}
|
|
}
|
|
break;
|
|
case svSingleRef :
|
|
{
|
|
if (SkipReference(pCode[j], rPos, rOldDoc, bRangeName, true))
|
|
continue;
|
|
|
|
ScSingleRefData& rRef = *pCode[j]->GetSingleRef();
|
|
|
|
if ( rRef.IsFlag3D() && !rRef.IsTabRel() )
|
|
{
|
|
OUString aTabName;
|
|
sal_uInt16 nFileId;
|
|
GetExternalTableData(&rOldDoc, &rNewDoc, rRef.Tab(), aTabName, nFileId);
|
|
ReplaceToken( j, new ScExternalSingleRefToken( nFileId,
|
|
rNewDoc.GetSharedStringPool().intern( aTabName), rRef), CODE_AND_RPN);
|
|
// ATTENTION: rRef can't be used after this point
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// added to avoid warnings
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScTokenArray::AdjustAbsoluteRefs( const ScDocument& rOldDoc, const ScAddress& rOldPos, const ScAddress& rNewPos,
|
|
bool bCheckCopyRange)
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, true);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch ( p->GetType() )
|
|
{
|
|
case svDoubleRef :
|
|
{
|
|
if (!SkipReference(p, rOldPos, rOldDoc, false, bCheckCopyRange))
|
|
continue;
|
|
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScSingleRefData& rRef2 = rRef.Ref2;
|
|
ScSingleRefData& rRef1 = rRef.Ref1;
|
|
|
|
AdjustSingleRefData( rRef1, rOldPos, rNewPos );
|
|
AdjustSingleRefData( rRef2, rOldPos, rNewPos );
|
|
}
|
|
break;
|
|
case svSingleRef :
|
|
{
|
|
if (!SkipReference(p, rOldPos, rOldDoc, false, bCheckCopyRange))
|
|
continue;
|
|
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
|
|
AdjustSingleRefData( rRef, rOldPos, rNewPos );
|
|
}
|
|
break;
|
|
default:
|
|
{
|
|
// added to avoid warnings
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScTokenArray::AdjustSheetLocalNameReferences( SCTAB nOldTab, SCTAB nNewTab )
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch ( p->GetType() )
|
|
{
|
|
case svDoubleRef :
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScSingleRefData& rRef2 = rRef.Ref2;
|
|
ScSingleRefData& rRef1 = rRef.Ref1;
|
|
|
|
if (!rRef1.IsTabRel() && rRef1.Tab() == nOldTab)
|
|
rRef1.SetAbsTab( nNewTab);
|
|
if (!rRef2.IsTabRel() && rRef2.Tab() == nOldTab)
|
|
rRef2.SetAbsTab( nNewTab);
|
|
if (!rRef1.IsTabRel() && !rRef2.IsTabRel() && rRef1.Tab() > rRef2.Tab())
|
|
{
|
|
SCTAB nTab = rRef1.Tab();
|
|
rRef1.SetAbsTab( rRef2.Tab());
|
|
rRef2.SetAbsTab( nTab);
|
|
}
|
|
}
|
|
break;
|
|
case svSingleRef :
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
|
|
if (!rRef.IsTabRel() && rRef.Tab() == nOldTab)
|
|
rRef.SetAbsTab( nNewTab);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ScTokenArray::ReferencesSheet( SCTAB nTab, SCTAB nPosTab ) const
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken* const * pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken* const * const pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
const FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch ( p->GetType() )
|
|
{
|
|
case svDoubleRef :
|
|
{
|
|
const ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
const ScSingleRefData& rRef2 = rRef.Ref2;
|
|
const ScSingleRefData& rRef1 = rRef.Ref1;
|
|
|
|
SCTAB nTab1 = (rRef1.IsTabRel() ? rRef1.Tab() + nPosTab : rRef1.Tab());
|
|
SCTAB nTab2 = (rRef2.IsTabRel() ? rRef2.Tab() + nPosTab : rRef2.Tab());
|
|
if (nTab1 <= nTab && nTab <= nTab2)
|
|
return true;
|
|
}
|
|
break;
|
|
case svSingleRef :
|
|
{
|
|
const ScSingleRefData& rRef = *p->GetSingleRef();
|
|
if (rRef.IsTabRel())
|
|
{
|
|
if (rRef.Tab() + nPosTab == nTab)
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if (rRef.Tab() == nTab)
|
|
return true;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace {
|
|
|
|
ScRange getSelectedRange( const sc::RefUpdateContext& rCxt )
|
|
{
|
|
ScRange aSelectedRange(ScAddress::INITIALIZE_INVALID);
|
|
if (rCxt.mnColDelta < 0)
|
|
{
|
|
// Delete and shift to left.
|
|
aSelectedRange.aStart = ScAddress(rCxt.maRange.aStart.Col()+rCxt.mnColDelta, rCxt.maRange.aStart.Row(), rCxt.maRange.aStart.Tab());
|
|
aSelectedRange.aEnd = ScAddress(rCxt.maRange.aStart.Col()-1, rCxt.maRange.aEnd.Row(), rCxt.maRange.aEnd.Tab());
|
|
}
|
|
else if (rCxt.mnRowDelta < 0)
|
|
{
|
|
// Delete and shift up.
|
|
aSelectedRange.aStart = ScAddress(rCxt.maRange.aStart.Col(), rCxt.maRange.aStart.Row()+rCxt.mnRowDelta, rCxt.maRange.aStart.Tab());
|
|
aSelectedRange.aEnd = ScAddress(rCxt.maRange.aEnd.Col(), rCxt.maRange.aStart.Row()-1, rCxt.maRange.aEnd.Tab());
|
|
}
|
|
else if (rCxt.mnTabDelta < 0)
|
|
{
|
|
// Deleting sheets.
|
|
// TODO : Figure out what to do here.
|
|
}
|
|
else if (rCxt.mnColDelta > 0)
|
|
{
|
|
// Insert and shift to the right.
|
|
aSelectedRange.aStart = rCxt.maRange.aStart;
|
|
aSelectedRange.aEnd = ScAddress(rCxt.maRange.aStart.Col()+rCxt.mnColDelta-1, rCxt.maRange.aEnd.Row(), rCxt.maRange.aEnd.Tab());
|
|
}
|
|
else if (rCxt.mnRowDelta > 0)
|
|
{
|
|
// Insert and shift down.
|
|
aSelectedRange.aStart = rCxt.maRange.aStart;
|
|
aSelectedRange.aEnd = ScAddress(rCxt.maRange.aEnd.Col(), rCxt.maRange.aStart.Row()+rCxt.mnRowDelta-1, rCxt.maRange.aEnd.Tab());
|
|
}
|
|
else if (rCxt.mnTabDelta > 0)
|
|
{
|
|
// Inserting sheets.
|
|
// TODO : Figure out what to do here.
|
|
}
|
|
|
|
return aSelectedRange;
|
|
}
|
|
|
|
void setRefDeleted( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
|
|
{
|
|
if (rCxt.mnColDelta < 0)
|
|
rRef.SetColDeleted(true);
|
|
else if (rCxt.mnRowDelta < 0)
|
|
rRef.SetRowDeleted(true);
|
|
else if (rCxt.mnTabDelta < 0)
|
|
rRef.SetTabDeleted(true);
|
|
}
|
|
|
|
void restoreDeletedRef( ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt )
|
|
{
|
|
if (rCxt.mnColDelta)
|
|
{
|
|
if (rRef.IsColDeleted())
|
|
rRef.SetColDeleted(false);
|
|
}
|
|
else if (rCxt.mnRowDelta)
|
|
{
|
|
if (rRef.IsRowDeleted())
|
|
rRef.SetRowDeleted(false);
|
|
}
|
|
else if (rCxt.mnTabDelta)
|
|
{
|
|
if (rRef.IsTabDeleted())
|
|
rRef.SetTabDeleted(false);
|
|
}
|
|
}
|
|
|
|
void setRefDeleted( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
|
|
{
|
|
if (rCxt.mnColDelta < 0)
|
|
{
|
|
rRef.Ref1.SetColDeleted(true);
|
|
rRef.Ref2.SetColDeleted(true);
|
|
}
|
|
else if (rCxt.mnRowDelta < 0)
|
|
{
|
|
rRef.Ref1.SetRowDeleted(true);
|
|
rRef.Ref2.SetRowDeleted(true);
|
|
}
|
|
else if (rCxt.mnTabDelta < 0)
|
|
{
|
|
rRef.Ref1.SetTabDeleted(true);
|
|
rRef.Ref2.SetTabDeleted(true);
|
|
}
|
|
}
|
|
|
|
void restoreDeletedRef( ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt )
|
|
{
|
|
restoreDeletedRef(rRef.Ref1, rCxt);
|
|
restoreDeletedRef(rRef.Ref2, rCxt);
|
|
}
|
|
|
|
enum ShrinkResult
|
|
{
|
|
UNMODIFIED,
|
|
SHRUNK,
|
|
STICKY
|
|
};
|
|
|
|
ShrinkResult shrinkRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rDeletedRange,
|
|
const ScComplexRefData& rRef )
|
|
{
|
|
if (!rDeletedRange.Intersects(rRefRange))
|
|
return UNMODIFIED;
|
|
|
|
if (rCxt.mnColDelta < 0)
|
|
{
|
|
if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
|
|
// Entire rows are not affected, columns are anchored.
|
|
return STICKY;
|
|
|
|
// Shifting left.
|
|
if (rRefRange.aStart.Row() < rDeletedRange.aStart.Row() || rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row())
|
|
// Deleted range is only partially overlapping in vertical direction. Bail out.
|
|
return UNMODIFIED;
|
|
|
|
if (rDeletedRange.aStart.Col() <= rRefRange.aStart.Col())
|
|
{
|
|
if (rRefRange.aEnd.Col() <= rDeletedRange.aEnd.Col())
|
|
{
|
|
// Reference is entirely deleted.
|
|
rRefRange.SetInvalid();
|
|
}
|
|
else
|
|
{
|
|
// The reference range is truncated on the left.
|
|
SCCOL nOffset = rDeletedRange.aStart.Col() - rRefRange.aStart.Col();
|
|
SCCOL nDelta = rRefRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1;
|
|
rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta+nOffset);
|
|
rRefRange.aStart.IncCol(nOffset);
|
|
}
|
|
}
|
|
else if (rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col())
|
|
{
|
|
if (rRefRange.IsEndColSticky(rCxt.mrDoc))
|
|
// Sticky end not affected.
|
|
return STICKY;
|
|
|
|
// Reference is deleted in the middle. Move the last column
|
|
// position to the left.
|
|
SCCOL nDelta = rDeletedRange.aStart.Col() - rDeletedRange.aEnd.Col() - 1;
|
|
rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
|
|
}
|
|
else
|
|
{
|
|
if (rRefRange.IsEndColSticky(rCxt.mrDoc))
|
|
// Sticky end not affected.
|
|
return STICKY;
|
|
|
|
// The reference range is truncated on the right.
|
|
SCCOL nDelta = rDeletedRange.aStart.Col() - rRefRange.aEnd.Col() - 1;
|
|
rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
|
|
}
|
|
return SHRUNK;
|
|
}
|
|
else if (rCxt.mnRowDelta < 0)
|
|
{
|
|
if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
|
|
// Entire columns are not affected, rows are anchored.
|
|
return STICKY;
|
|
|
|
// Shifting up.
|
|
|
|
if (rRefRange.aStart.Col() < rDeletedRange.aStart.Col() || rDeletedRange.aEnd.Col() < rRefRange.aEnd.Col())
|
|
// Deleted range is only partially overlapping in horizontal direction. Bail out.
|
|
return UNMODIFIED;
|
|
|
|
if (rDeletedRange.aStart.Row() <= rRefRange.aStart.Row())
|
|
{
|
|
if (rRefRange.aEnd.Row() <= rDeletedRange.aEnd.Row())
|
|
{
|
|
// Reference is entirely deleted.
|
|
rRefRange.SetInvalid();
|
|
}
|
|
else
|
|
{
|
|
// The reference range is truncated on the top.
|
|
SCROW nOffset = rDeletedRange.aStart.Row() - rRefRange.aStart.Row();
|
|
SCROW nDelta = rRefRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1;
|
|
rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta+nOffset);
|
|
rRefRange.aStart.IncRow(nOffset);
|
|
}
|
|
}
|
|
else if (rDeletedRange.aEnd.Row() < rRefRange.aEnd.Row())
|
|
{
|
|
if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
|
|
// Sticky end not affected.
|
|
return STICKY;
|
|
|
|
// Reference is deleted in the middle. Move the last row
|
|
// position upward.
|
|
SCROW nDelta = rDeletedRange.aStart.Row() - rDeletedRange.aEnd.Row() - 1;
|
|
rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
|
|
}
|
|
else
|
|
{
|
|
if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
|
|
// Sticky end not affected.
|
|
return STICKY;
|
|
|
|
// The reference range is truncated on the bottom.
|
|
SCROW nDelta = rDeletedRange.aStart.Row() - rRefRange.aEnd.Row() - 1;
|
|
rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
|
|
}
|
|
return SHRUNK;
|
|
}
|
|
|
|
return UNMODIFIED;
|
|
}
|
|
|
|
bool expandRange( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rSelectedRange,
|
|
const ScComplexRefData& rRef )
|
|
{
|
|
if (!rSelectedRange.Intersects(rRefRange))
|
|
return false;
|
|
|
|
if (rCxt.mnColDelta > 0)
|
|
{
|
|
if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
|
|
// Entire rows are not affected, columns are anchored.
|
|
return false;
|
|
|
|
// Insert and shifting right.
|
|
if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row())
|
|
// Selected range is only partially overlapping in vertical direction. Bail out.
|
|
return false;
|
|
|
|
if (rCxt.mrDoc.IsExpandRefs())
|
|
{
|
|
if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1)
|
|
// Reference must be at least two columns wide.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (rSelectedRange.aStart.Col() <= rRefRange.aStart.Col())
|
|
// Selected range is at the left end and the edge expansion is turned off. No expansion.
|
|
return false;
|
|
}
|
|
|
|
if (rRefRange.IsEndColSticky(rCxt.mrDoc))
|
|
// Sticky end not affected.
|
|
return false;
|
|
|
|
// Move the last column position to the right.
|
|
SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
|
|
rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
|
|
return true;
|
|
}
|
|
else if (rCxt.mnRowDelta > 0)
|
|
{
|
|
if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
|
|
// Entire columns are not affected, rows are anchored.
|
|
return false;
|
|
|
|
// Insert and shifting down.
|
|
if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col())
|
|
// Selected range is only partially overlapping in horizontal direction. Bail out.
|
|
return false;
|
|
|
|
if (rCxt.mrDoc.IsExpandRefs())
|
|
{
|
|
if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1)
|
|
// Reference must be at least two rows tall.
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
if (rSelectedRange.aStart.Row() <= rRefRange.aStart.Row())
|
|
// Selected range is at the top end and the edge expansion is turned off. No expansion.
|
|
return false;
|
|
}
|
|
|
|
if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
|
|
// Sticky end not affected.
|
|
return false;
|
|
|
|
// Move the last row position down.
|
|
SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
|
|
rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check if the referenced range is expandable when the selected range is
|
|
* not overlapping the referenced range.
|
|
*/
|
|
bool expandRangeByEdge( const sc::RefUpdateContext& rCxt, ScRange& rRefRange, const ScRange& rSelectedRange,
|
|
const ScComplexRefData& rRef )
|
|
{
|
|
if (!rCxt.mrDoc.IsExpandRefs())
|
|
// Edge-expansion is turned off.
|
|
return false;
|
|
|
|
if (rSelectedRange.aStart.Tab() > rRefRange.aStart.Tab() || rRefRange.aEnd.Tab() > rSelectedRange.aEnd.Tab())
|
|
// Sheet references not within selected range.
|
|
return false;
|
|
|
|
if (rCxt.mnColDelta > 0)
|
|
{
|
|
if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
|
|
// Entire rows are not affected, columns are anchored.
|
|
return false;
|
|
|
|
// Insert and shift right.
|
|
|
|
if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1)
|
|
// Reference must be at least two columns wide.
|
|
return false;
|
|
|
|
if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row())
|
|
// Selected range is only partially overlapping in vertical direction. Bail out.
|
|
return false;
|
|
|
|
if (rSelectedRange.aStart.Col() - rRefRange.aEnd.Col() != 1)
|
|
// Selected range is not immediately adjacent. Bail out.
|
|
return false;
|
|
|
|
if (rRefRange.IsEndColSticky(rCxt.mrDoc))
|
|
// Sticky end not affected.
|
|
return false;
|
|
|
|
// Move the last column position to the right.
|
|
SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
|
|
rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta);
|
|
return true;
|
|
}
|
|
else if (rCxt.mnRowDelta > 0)
|
|
{
|
|
if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
|
|
// Entire columns are not affected, rows are anchored.
|
|
return false;
|
|
|
|
if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1)
|
|
// Reference must be at least two rows tall.
|
|
return false;
|
|
|
|
if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col())
|
|
// Selected range is only partially overlapping in horizontal direction. Bail out.
|
|
return false;
|
|
|
|
if (rSelectedRange.aStart.Row() - rRefRange.aEnd.Row() != 1)
|
|
// Selected range is not immediately adjacent. Bail out.
|
|
return false;
|
|
|
|
if (rRefRange.IsEndRowSticky(rCxt.mrDoc))
|
|
// Sticky end not affected.
|
|
return false;
|
|
|
|
// Move the last row position down.
|
|
SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
|
|
rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool isNameModified( const sc::UpdatedRangeNames& rUpdatedNames, SCTAB nOldTab, const formula::FormulaToken& rToken )
|
|
{
|
|
SCTAB nTab = -1;
|
|
if (rToken.GetSheet() >= 0)
|
|
nTab = nOldTab;
|
|
|
|
// Check if this named expression has been modified.
|
|
return rUpdatedNames.isNameUpdated(nTab, rToken.GetIndex());
|
|
}
|
|
|
|
bool isDBDataModified( const ScDocument& rDoc, const formula::FormulaToken& rToken )
|
|
{
|
|
// Check if this DBData has been modified.
|
|
const ScDBData* pDBData = rDoc.GetDBCollection()->getNamedDBs().findByIndex( rToken.GetIndex());
|
|
if (!pDBData)
|
|
return true;
|
|
|
|
return pDBData->IsModified();
|
|
}
|
|
|
|
}
|
|
|
|
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnShift( const sc::RefUpdateContext& rCxt, const ScAddress& rOldPos )
|
|
{
|
|
ScRange aSelectedRange = getSelectedRange(rCxt);
|
|
|
|
sc::RefUpdateResult aRes;
|
|
ScAddress aNewPos = rOldPos;
|
|
bool bCellShifted = rCxt.maRange.Contains(rOldPos);
|
|
if (bCellShifted)
|
|
{
|
|
ScAddress aErrorPos( ScAddress::UNINITIALIZED );
|
|
if (!aNewPos.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
|
|
{
|
|
assert(!"can't move");
|
|
}
|
|
}
|
|
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
|
|
if (rCxt.isDeleted() && aSelectedRange.Contains(aAbs))
|
|
{
|
|
// This reference is in the deleted region.
|
|
setRefDeleted(rRef, rCxt);
|
|
aRes.mbValueChanged = true;
|
|
break;
|
|
}
|
|
|
|
if (!rCxt.isDeleted() && rRef.IsDeleted())
|
|
{
|
|
// Check if the token has reference to previously deleted region.
|
|
ScAddress aCheckPos = rRef.toAbs(*mxSheetLimits, aNewPos);
|
|
if (rCxt.maRange.Contains(aCheckPos))
|
|
{
|
|
restoreDeletedRef(rRef, rCxt);
|
|
aRes.mbValueChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rCxt.maRange.Contains(aAbs))
|
|
{
|
|
ScAddress aErrorPos( ScAddress::UNINITIALIZED );
|
|
if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
|
|
aAbs = aErrorPos;
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
|
|
if (rCxt.isDeleted())
|
|
{
|
|
if (aSelectedRange.Contains(aAbs))
|
|
{
|
|
// This reference is in the deleted region.
|
|
setRefDeleted(rRef, rCxt);
|
|
aRes.mbValueChanged = true;
|
|
break;
|
|
}
|
|
else if (aSelectedRange.Intersects(aAbs))
|
|
{
|
|
const ShrinkResult eSR = shrinkRange(rCxt, aAbs, aSelectedRange, rRef);
|
|
if (eSR == SHRUNK)
|
|
{
|
|
// The reference range has been shrunk.
|
|
rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
|
|
aRes.mbValueChanged = true;
|
|
aRes.mbReferenceModified = true;
|
|
break;
|
|
}
|
|
else if (eSR == STICKY)
|
|
{
|
|
// The reference range stays the same but a
|
|
// new (empty) cell range is shifted in and
|
|
// may change the calculation result.
|
|
aRes.mbValueChanged = true;
|
|
// Sticky when intersecting the selected
|
|
// range means also that the other
|
|
// conditions below are not met,
|
|
// specifically not the
|
|
// if (rCxt.maRange.Contains(aAbs))
|
|
// that is able to update the reference,
|
|
// but aSelectedRange does not intersect
|
|
// with rCxt.maRange so that can't happen
|
|
// and we can bail out early without
|
|
// updating the reference.
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rCxt.isDeleted() && rRef.IsDeleted())
|
|
{
|
|
// Check if the token has reference to previously deleted region.
|
|
ScRange aCheckRange = rRef.toAbs(*mxSheetLimits, aNewPos);
|
|
if (aSelectedRange.Contains(aCheckRange))
|
|
{
|
|
// This reference was previously in the deleted region. Restore it.
|
|
restoreDeletedRef(rRef, rCxt);
|
|
aRes.mbValueChanged = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rCxt.isInserted())
|
|
{
|
|
if (expandRange(rCxt, aAbs, aSelectedRange, rRef))
|
|
{
|
|
// The reference range has been expanded.
|
|
rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
|
|
aRes.mbValueChanged = true;
|
|
aRes.mbReferenceModified = true;
|
|
break;
|
|
}
|
|
|
|
if (expandRangeByEdge(rCxt, aAbs, aSelectedRange, rRef))
|
|
{
|
|
// The reference range has been expanded on the edge.
|
|
rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
|
|
aRes.mbValueChanged = true;
|
|
aRes.mbReferenceModified = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (rCxt.maRange.Contains(aAbs))
|
|
{
|
|
// We shift either by column or by row, not both,
|
|
// so moving the reference has only to be done in
|
|
// the non-sticky case.
|
|
if ((rCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
|
|
|| (rCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())))
|
|
{
|
|
// In entire col/row, values are shifted within
|
|
// the reference, which affects all positional
|
|
// results like in MATCH or matrix positions.
|
|
aRes.mbValueChanged = true;
|
|
}
|
|
else
|
|
{
|
|
ScRange aErrorRange( ScAddress::UNINITIALIZED );
|
|
if (!aAbs.MoveSticky(rCxt.mrDoc, rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange))
|
|
aAbs = aErrorRange;
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
}
|
|
else if (rCxt.maRange.Intersects(aAbs))
|
|
{
|
|
// Part of the referenced range is being shifted. This
|
|
// will change the values of the range.
|
|
aRes.mbValueChanged = true;
|
|
}
|
|
|
|
rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
|
|
}
|
|
break;
|
|
case svExternalSingleRef:
|
|
{
|
|
// For external reference, just reset the reference with
|
|
// respect to the new cell position.
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
// Same as above.
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
// For ocTableRef p is the inner token of *pp, so have a separate
|
|
// condition here.
|
|
if ((*pp)->GetType() == svIndex)
|
|
{
|
|
switch ((*pp)->GetOpCode())
|
|
{
|
|
case ocName:
|
|
{
|
|
SCTAB nOldTab = (*pp)->GetSheet();
|
|
if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
|
|
aRes.mbNameModified = true;
|
|
if (rCxt.mnTabDelta &&
|
|
rCxt.maRange.aStart.Tab() <= nOldTab && nOldTab <= rCxt.maRange.aEnd.Tab())
|
|
{
|
|
aRes.mbNameModified = true;
|
|
(*pp)->SetSheet( nOldTab + rCxt.mnTabDelta);
|
|
}
|
|
}
|
|
break;
|
|
case ocDBArea:
|
|
case ocTableRef:
|
|
if (isDBDataModified(rCxt.mrDoc, **pp))
|
|
aRes.mbNameModified = true;
|
|
break;
|
|
default:
|
|
; // nothing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return aRes;
|
|
}
|
|
|
|
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMove(
|
|
const sc::RefUpdateContext& rCxt, const ScAddress& rOldPos, const ScAddress& rNewPos )
|
|
{
|
|
sc::RefUpdateResult aRes;
|
|
|
|
if (!rCxt.mnColDelta && !rCxt.mnRowDelta && !rCxt.mnTabDelta)
|
|
// The cell hasn't moved at all.
|
|
return aRes;
|
|
|
|
// When moving, the range in the context is the destination range. We need
|
|
// to use the old range prior to the move for hit analysis.
|
|
ScRange aOldRange = rCxt.maRange;
|
|
ScRange aErrorMoveRange( ScAddress::UNINITIALIZED );
|
|
if (!aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorMoveRange, rCxt.mrDoc))
|
|
{
|
|
assert(!"can't move");
|
|
}
|
|
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
|
|
// Do not update the reference in transposed case (cut paste transposed).
|
|
// The reference will be updated in UpdateTranspose().
|
|
// Additionally, do not update the references from cells within the moved
|
|
// range as they lead to #REF! errors here. These #REF! cannot by fixed
|
|
// later in UpdateTranspose().
|
|
if (rCxt.mbTransposed && (aOldRange.Contains(rOldPos) || aOldRange.Contains(aAbs)))
|
|
break;
|
|
|
|
if (aOldRange.Contains(aAbs))
|
|
{
|
|
ScAddress aErrorPos( ScAddress::UNINITIALIZED );
|
|
if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
|
|
aAbs = aErrorPos;
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
else if (rCxt.maRange.Contains(aAbs))
|
|
{
|
|
// Referenced cell has been overwritten.
|
|
aRes.mbValueChanged = true;
|
|
}
|
|
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
|
|
rRef.SetFlag3D(rRef.IsFlag3D() || !rRef.IsTabRel() || aAbs.Tab() != rNewPos.Tab());
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
|
|
// Do not update the reference in transposed case (cut paste transposed).
|
|
// The reference will be updated in UpdateTranspose().
|
|
// Additionally, do not update the references from cells within the moved
|
|
// range as they lead to #REF! errors here. These #REF! cannot by fixed
|
|
// later in UpdateTranspose().
|
|
if (rCxt.mbTransposed && (aOldRange.Contains(rOldPos) || aOldRange.Contains(aAbs)))
|
|
break;
|
|
|
|
if (aOldRange.Contains(aAbs))
|
|
{
|
|
ScRange aErrorRange( ScAddress::UNINITIALIZED );
|
|
if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
|
|
aAbs = aErrorRange;
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
else if (rCxt.maRange.Contains(aAbs))
|
|
{
|
|
// Referenced range has been entirely overwritten.
|
|
aRes.mbValueChanged = true;
|
|
}
|
|
|
|
rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
|
|
bool b1, b2;
|
|
if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
|
|
{
|
|
// More than one sheet referenced => has to have
|
|
// both 3D flags.
|
|
b1 = b2 = true;
|
|
}
|
|
else
|
|
{
|
|
// Keep given 3D flag even for relative sheet
|
|
// reference to same sheet.
|
|
// Absolute sheet reference => set 3D flag.
|
|
// Reference to another sheet => set 3D flag.
|
|
b1 = rRef.Ref1.IsFlag3D() || !rRef.Ref1.IsTabRel() || rNewPos.Tab() != aAbs.aStart.Tab();
|
|
b2 = rRef.Ref2.IsFlag3D() || !rRef.Ref2.IsTabRel() || rNewPos.Tab() != aAbs.aEnd.Tab();
|
|
// End part has 3D flag => start part must have it too.
|
|
if (b2)
|
|
b1 = true;
|
|
// End part sheet reference is identical to start
|
|
// part sheet reference and end part sheet
|
|
// reference was not explicitly given => clear end
|
|
// part 3D flag.
|
|
if (b1 && b2 && rRef.Ref1.IsTabRel() == rRef.Ref2.IsTabRel() && !rRef.Ref2.IsFlag3D())
|
|
b2 = false;
|
|
}
|
|
rRef.Ref1.SetFlag3D(b1);
|
|
rRef.Ref2.SetFlag3D(b2);
|
|
}
|
|
break;
|
|
case svExternalSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
// For ocTableRef p is the inner token of *pp, so have a separate
|
|
// condition here.
|
|
if ((*pp)->GetType() == svIndex)
|
|
{
|
|
switch ((*pp)->GetOpCode())
|
|
{
|
|
case ocName:
|
|
{
|
|
SCTAB nOldTab = (*pp)->GetSheet();
|
|
if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
|
|
aRes.mbNameModified = true;
|
|
}
|
|
break;
|
|
case ocDBArea:
|
|
case ocTableRef:
|
|
if (isDBDataModified(rCxt.mrDoc, **pp))
|
|
aRes.mbNameModified = true;
|
|
break;
|
|
default:
|
|
; // nothing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return aRes;
|
|
}
|
|
|
|
void ScTokenArray::MoveReferenceColReorder(
|
|
const ScAddress& rPos, SCTAB nTab, SCROW nRow1, SCROW nRow2, const sc::ColRowReorderMapType& rColMap )
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
|
|
|
|
if (aAbs.Tab() == nTab && nRow1 <= aAbs.Row() && aAbs.Row() <= nRow2)
|
|
{
|
|
// Inside reordered row range.
|
|
sc::ColRowReorderMapType::const_iterator it = rColMap.find(aAbs.Col());
|
|
if (it != rColMap.end())
|
|
{
|
|
// This column is reordered.
|
|
SCCOL nNewCol = it->second;
|
|
aAbs.SetCol(nNewCol);
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
|
|
|
|
if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
|
|
// Must be a single-sheet reference.
|
|
break;
|
|
|
|
if (aAbs.aStart.Col() != aAbs.aEnd.Col())
|
|
// Whole range must fit in a single column.
|
|
break;
|
|
|
|
if (aAbs.aStart.Tab() == nTab && nRow1 <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= nRow2)
|
|
{
|
|
// Inside reordered row range.
|
|
sc::ColRowReorderMapType::const_iterator it = rColMap.find(aAbs.aStart.Col());
|
|
if (it != rColMap.end())
|
|
{
|
|
// This column is reordered.
|
|
SCCOL nNewCol = it->second;
|
|
aAbs.aStart.SetCol(nNewCol);
|
|
aAbs.aEnd.SetCol(nNewCol);
|
|
rRef.SetRange(*mxSheetLimits, aAbs, rPos);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScTokenArray::MoveReferenceRowReorder( const ScAddress& rPos, SCTAB nTab, SCCOL nCol1, SCCOL nCol2, const sc::ColRowReorderMapType& rRowMap )
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
|
|
|
|
if (aAbs.Tab() == nTab && nCol1 <= aAbs.Col() && aAbs.Col() <= nCol2)
|
|
{
|
|
// Inside reordered column range.
|
|
sc::ColRowReorderMapType::const_iterator it = rRowMap.find(aAbs.Row());
|
|
if (it != rRowMap.end())
|
|
{
|
|
// This column is reordered.
|
|
SCROW nNewRow = it->second;
|
|
aAbs.SetRow(nNewRow);
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
|
|
|
|
if (aAbs.aStart.Tab() != aAbs.aEnd.Tab())
|
|
// Must be a single-sheet reference.
|
|
break;
|
|
|
|
if (aAbs.aStart.Row() != aAbs.aEnd.Row())
|
|
// Whole range must fit in a single row.
|
|
break;
|
|
|
|
if (aAbs.aStart.Tab() == nTab && nCol1 <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= nCol2)
|
|
{
|
|
// Inside reordered column range.
|
|
sc::ColRowReorderMapType::const_iterator it = rRowMap.find(aAbs.aStart.Row());
|
|
if (it != rRowMap.end())
|
|
{
|
|
// This row is reordered.
|
|
SCROW nNewRow = it->second;
|
|
aAbs.aStart.SetRow(nNewRow);
|
|
aAbs.aEnd.SetRow(nNewRow);
|
|
rRef.SetRange(*mxSheetLimits, aAbs, rPos);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool adjustSingleRefInName(
|
|
ScSingleRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos,
|
|
ScComplexRefData* pEndOfComplex )
|
|
{
|
|
ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
|
|
|
|
if (aAbs.Tab() < rCxt.maRange.aStart.Tab() || rCxt.maRange.aEnd.Tab() < aAbs.Tab())
|
|
{
|
|
// This references a sheet that has not shifted. Don't change it.
|
|
return false;
|
|
}
|
|
|
|
if (!rCxt.maRange.Contains(rRef.toAbs(rCxt.mrDoc, rPos)))
|
|
return false;
|
|
|
|
bool bChanged = false;
|
|
|
|
if (rCxt.mnColDelta && !rRef.IsColRel())
|
|
{
|
|
// Adjust absolute column reference.
|
|
if (rCxt.maRange.aStart.Col() <= rRef.Col() && rRef.Col() <= rCxt.maRange.aEnd.Col())
|
|
{
|
|
if (pEndOfComplex)
|
|
{
|
|
if (pEndOfComplex->IncEndColSticky(rCxt.mrDoc, rCxt.mnColDelta, rPos))
|
|
bChanged = true;
|
|
}
|
|
else
|
|
{
|
|
rRef.IncCol(rCxt.mnColDelta);
|
|
bChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rCxt.mnRowDelta && !rRef.IsRowRel())
|
|
{
|
|
// Adjust absolute row reference.
|
|
if (rCxt.maRange.aStart.Row() <= rRef.Row() && rRef.Row() <= rCxt.maRange.aEnd.Row())
|
|
{
|
|
if (pEndOfComplex)
|
|
{
|
|
if (pEndOfComplex->IncEndRowSticky(rCxt.mrDoc, rCxt.mnRowDelta, rPos))
|
|
bChanged = true;
|
|
}
|
|
else
|
|
{
|
|
rRef.IncRow(rCxt.mnRowDelta);
|
|
bChanged = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!rRef.IsTabRel() && rCxt.mnTabDelta)
|
|
{
|
|
// Sheet range has already been checked above.
|
|
rRef.IncTab(rCxt.mnTabDelta);
|
|
bChanged = true;
|
|
}
|
|
|
|
return bChanged;
|
|
}
|
|
|
|
bool adjustDoubleRefInName(
|
|
ScComplexRefData& rRef, const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
|
|
{
|
|
bool bRefChanged = false;
|
|
if (rCxt.mrDoc.IsExpandRefs())
|
|
{
|
|
if (rCxt.mnRowDelta > 0 && !rRef.Ref1.IsRowRel() && !rRef.Ref2.IsRowRel())
|
|
{
|
|
ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
|
|
// Expand only if at least two rows tall.
|
|
if (aAbs.aStart.Row() < aAbs.aEnd.Row())
|
|
{
|
|
// Check and see if we should expand the range at the top.
|
|
ScRange aSelectedRange = getSelectedRange(rCxt);
|
|
if (aSelectedRange.Intersects(aAbs))
|
|
{
|
|
// Selection intersects the referenced range. Only expand the
|
|
// bottom position.
|
|
rRef.IncEndRowSticky(rCxt.mrDoc, rCxt.mnRowDelta, rPos);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
if (rCxt.mnColDelta > 0 && !rRef.Ref1.IsColRel() && !rRef.Ref2.IsColRel())
|
|
{
|
|
ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
|
|
// Expand only if at least two columns wide.
|
|
if (aAbs.aStart.Col() < aAbs.aEnd.Col())
|
|
{
|
|
// Check and see if we should expand the range at the left.
|
|
ScRange aSelectedRange = getSelectedRange(rCxt);
|
|
if (aSelectedRange.Intersects(aAbs))
|
|
{
|
|
// Selection intersects the referenced range. Only expand the
|
|
// right position.
|
|
rRef.IncEndColSticky(rCxt.mrDoc, rCxt.mnColDelta, rPos);
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((rCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
|
|
|| (rCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())))
|
|
{
|
|
sc::RefUpdateContext aCxt( rCxt.mrDoc);
|
|
// We only need a few parameters of RefUpdateContext.
|
|
aCxt.maRange = rCxt.maRange;
|
|
aCxt.mnColDelta = rCxt.mnColDelta;
|
|
aCxt.mnRowDelta = rCxt.mnRowDelta;
|
|
aCxt.mnTabDelta = rCxt.mnTabDelta;
|
|
|
|
// References to entire col/row are not to be adjusted in the other axis.
|
|
if (aCxt.mnRowDelta && rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
|
|
aCxt.mnRowDelta = 0;
|
|
if (aCxt.mnColDelta && rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
|
|
aCxt.mnColDelta = 0;
|
|
if (!aCxt.mnColDelta && !aCxt.mnRowDelta && !aCxt.mnTabDelta)
|
|
// early bailout
|
|
return bRefChanged;
|
|
|
|
// Ref2 before Ref1 for sticky ends.
|
|
if (adjustSingleRefInName(rRef.Ref2, aCxt, rPos, &rRef))
|
|
bRefChanged = true;
|
|
|
|
if (adjustSingleRefInName(rRef.Ref1, aCxt, rPos, nullptr))
|
|
bRefChanged = true;
|
|
}
|
|
else
|
|
{
|
|
// Ref2 before Ref1 for sticky ends.
|
|
if (adjustSingleRefInName(rRef.Ref2, rCxt, rPos, &rRef))
|
|
bRefChanged = true;
|
|
|
|
if (adjustSingleRefInName(rRef.Ref1, rCxt, rPos, nullptr))
|
|
bRefChanged = true;
|
|
}
|
|
|
|
return bRefChanged;
|
|
}
|
|
|
|
}
|
|
|
|
sc::RefUpdateResult ScTokenArray::AdjustReferenceInName(
|
|
const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
|
|
{
|
|
if (rCxt.meMode == URM_MOVE)
|
|
return AdjustReferenceInMovedName(rCxt, rPos);
|
|
|
|
sc::RefUpdateResult aRes;
|
|
|
|
if (rCxt.meMode == URM_COPY)
|
|
// Copying cells does not modify named expressions.
|
|
return aRes;
|
|
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
if (rCxt.mnRowDelta < 0)
|
|
{
|
|
// row(s) deleted.
|
|
|
|
if (rRef.IsRowRel())
|
|
// Don't modify relative references in names.
|
|
break;
|
|
|
|
ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
|
|
|
|
if (aAbs.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.Col())
|
|
// column of the reference is not in the deleted column range.
|
|
break;
|
|
|
|
if (aAbs.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.Tab() < rCxt.maRange.aStart.Tab())
|
|
// wrong tables
|
|
break;
|
|
|
|
const SCROW nDelStartRow = rCxt.maRange.aStart.Row() + rCxt.mnRowDelta;
|
|
const SCROW nDelEndRow = nDelStartRow - rCxt.mnRowDelta - 1;
|
|
|
|
if (nDelStartRow <= aAbs.Row() && aAbs.Row() <= nDelEndRow)
|
|
{
|
|
// This reference is deleted.
|
|
rRef.SetRowDeleted(true);
|
|
aRes.mbReferenceModified = true;
|
|
break;
|
|
}
|
|
}
|
|
else if (rCxt.mnColDelta < 0)
|
|
{
|
|
// column(s) deleted.
|
|
|
|
if (rRef.IsColRel())
|
|
// Don't modify relative references in names.
|
|
break;
|
|
|
|
ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
|
|
|
|
if (aAbs.Row() < rCxt.maRange.aStart.Row() || rCxt.maRange.aEnd.Row() < aAbs.Row())
|
|
// row of the reference is not in the deleted row range.
|
|
break;
|
|
|
|
if (aAbs.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.Tab() < rCxt.maRange.aStart.Tab())
|
|
// wrong tables
|
|
break;
|
|
|
|
const SCCOL nDelStartCol = rCxt.maRange.aStart.Col() + rCxt.mnColDelta;
|
|
const SCCOL nDelEndCol = nDelStartCol - rCxt.mnColDelta - 1;
|
|
|
|
if (nDelStartCol <= aAbs.Col() && aAbs.Col() <= nDelEndCol)
|
|
{
|
|
// This reference is deleted.
|
|
rRef.SetColDeleted(true);
|
|
aRes.mbReferenceModified = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (adjustSingleRefInName(rRef, rCxt, rPos, nullptr))
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
|
|
|
|
if (aAbs.aStart.Tab() > rCxt.maRange.aEnd.Tab() || aAbs.aEnd.Tab() < rCxt.maRange.aStart.Tab())
|
|
// Sheet references not affected.
|
|
break;
|
|
|
|
if (rCxt.maRange.Contains(aAbs))
|
|
{
|
|
// This range is entirely within the shifted region.
|
|
if (adjustDoubleRefInName(rRef, rCxt, rPos))
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
else if (rCxt.mnRowDelta < 0)
|
|
{
|
|
// row(s) deleted.
|
|
|
|
if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits()))
|
|
// Rows of entire columns are not affected.
|
|
break;
|
|
|
|
if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel())
|
|
// Don't modify relative references in names.
|
|
break;
|
|
|
|
if (aAbs.aStart.Col() < rCxt.maRange.aStart.Col() || rCxt.maRange.aEnd.Col() < aAbs.aEnd.Col())
|
|
// column range of the reference is not entirely in the deleted column range.
|
|
break;
|
|
|
|
ScRange aDeleted = rCxt.maRange;
|
|
aDeleted.aStart.IncRow(rCxt.mnRowDelta);
|
|
aDeleted.aEnd.SetRow(aDeleted.aStart.Row()-rCxt.mnRowDelta-1);
|
|
|
|
if (aAbs.aEnd.Row() < aDeleted.aStart.Row() || aDeleted.aEnd.Row() < aAbs.aStart.Row())
|
|
// reference range doesn't intersect with the deleted range.
|
|
break;
|
|
|
|
if (aDeleted.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= aDeleted.aEnd.Row())
|
|
{
|
|
// This reference is entirely deleted.
|
|
rRef.Ref1.SetRowDeleted(true);
|
|
rRef.Ref2.SetRowDeleted(true);
|
|
aRes.mbReferenceModified = true;
|
|
break;
|
|
}
|
|
|
|
if (aAbs.aStart.Row() < aDeleted.aStart.Row())
|
|
{
|
|
if (!aAbs.IsEndRowSticky(rCxt.mrDoc))
|
|
{
|
|
if (aDeleted.aEnd.Row() < aAbs.aEnd.Row())
|
|
// Deleted in the middle. Make the reference shorter.
|
|
rRef.Ref2.IncRow(rCxt.mnRowDelta);
|
|
else
|
|
// Deleted at tail end. Cut off the lower part.
|
|
rRef.Ref2.SetAbsRow(aDeleted.aStart.Row()-1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Deleted at the top. Cut the top off and shift up.
|
|
rRef.Ref1.SetAbsRow(aDeleted.aEnd.Row()+1);
|
|
rRef.Ref1.IncRow(rCxt.mnRowDelta);
|
|
if (!aAbs.IsEndRowSticky(rCxt.mrDoc))
|
|
rRef.Ref2.IncRow(rCxt.mnRowDelta);
|
|
}
|
|
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
else if (rCxt.mnColDelta < 0)
|
|
{
|
|
// column(s) deleted.
|
|
|
|
if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits()))
|
|
// Rows of entire rows are not affected.
|
|
break;
|
|
|
|
if (rRef.Ref1.IsColRel() || rRef.Ref2.IsColRel())
|
|
// Don't modify relative references in names.
|
|
break;
|
|
|
|
if (aAbs.aStart.Row() < rCxt.maRange.aStart.Row() || rCxt.maRange.aEnd.Row() < aAbs.aEnd.Row())
|
|
// row range of the reference is not entirely in the deleted row range.
|
|
break;
|
|
|
|
ScRange aDeleted = rCxt.maRange;
|
|
aDeleted.aStart.IncCol(rCxt.mnColDelta);
|
|
aDeleted.aEnd.SetCol(aDeleted.aStart.Col()-rCxt.mnColDelta-1);
|
|
|
|
if (aAbs.aEnd.Col() < aDeleted.aStart.Col() || aDeleted.aEnd.Col() < aAbs.aStart.Col())
|
|
// reference range doesn't intersect with the deleted range.
|
|
break;
|
|
|
|
if (aDeleted.aStart.Col() <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= aDeleted.aEnd.Col())
|
|
{
|
|
// This reference is entirely deleted.
|
|
rRef.Ref1.SetColDeleted(true);
|
|
rRef.Ref2.SetColDeleted(true);
|
|
aRes.mbReferenceModified = true;
|
|
break;
|
|
}
|
|
|
|
if (aAbs.aStart.Col() < aDeleted.aStart.Col())
|
|
{
|
|
if (!aAbs.IsEndColSticky(rCxt.mrDoc))
|
|
{
|
|
if (aDeleted.aEnd.Col() < aAbs.aEnd.Col())
|
|
// Deleted in the middle. Make the reference shorter.
|
|
rRef.Ref2.IncCol(rCxt.mnColDelta);
|
|
else
|
|
// Deleted at tail end. Cut off the right part.
|
|
rRef.Ref2.SetAbsCol(aDeleted.aStart.Col()-1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Deleted at the left. Cut the left off and shift left.
|
|
rRef.Ref1.SetAbsCol(aDeleted.aEnd.Col()+1);
|
|
rRef.Ref1.IncCol(rCxt.mnColDelta);
|
|
if (!aAbs.IsEndColSticky(rCxt.mrDoc))
|
|
rRef.Ref2.IncCol(rCxt.mnColDelta);
|
|
}
|
|
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
else if (rCxt.maRange.Intersects(aAbs))
|
|
{
|
|
if (rCxt.mnColDelta && rCxt.maRange.aStart.Row() <= aAbs.aStart.Row() && aAbs.aEnd.Row() <= rCxt.maRange.aEnd.Row())
|
|
{
|
|
if (adjustDoubleRefInName(rRef, rCxt, rPos))
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
if (rCxt.mnRowDelta && rCxt.maRange.aStart.Col() <= aAbs.aStart.Col() && aAbs.aEnd.Col() <= rCxt.maRange.aEnd.Col())
|
|
{
|
|
if (adjustDoubleRefInName(rRef, rCxt, rPos))
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
}
|
|
else if (rCxt.mnRowDelta > 0 && rCxt.mrDoc.IsExpandRefs())
|
|
{
|
|
// Check if we could expand range reference by the bottom
|
|
// edge. For named expressions, we only expand absolute
|
|
// references. Reference must be at least two rows
|
|
// tall.
|
|
if (!rRef.Ref1.IsRowRel() && !rRef.Ref2.IsRowRel() &&
|
|
aAbs.aStart.Row() < aAbs.aEnd.Row() &&
|
|
aAbs.aEnd.Row()+1 == rCxt.maRange.aStart.Row())
|
|
{
|
|
// Expand by the bottom edge.
|
|
rRef.Ref2.IncRow(rCxt.mnRowDelta);
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
}
|
|
else if (rCxt.mnColDelta > 0 && rCxt.mrDoc.IsExpandRefs())
|
|
{
|
|
// Check if we could expand range reference by the right
|
|
// edge. For named expressions, we only expand absolute
|
|
// references. Reference must be at least two
|
|
// columns wide.
|
|
if (!rRef.Ref1.IsColRel() && !rRef.Ref2.IsColRel() &&
|
|
aAbs.aStart.Col() < aAbs.aEnd.Col() &&
|
|
aAbs.aEnd.Col()+1 == rCxt.maRange.aStart.Col())
|
|
{
|
|
// Expand by the right edge.
|
|
rRef.Ref2.IncCol(rCxt.mnColDelta);
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
return aRes;
|
|
}
|
|
|
|
sc::RefUpdateResult ScTokenArray::AdjustReferenceInMovedName( const sc::RefUpdateContext& rCxt, const ScAddress& rPos )
|
|
{
|
|
// When moving, the range is the destination range.
|
|
ScRange aOldRange = rCxt.maRange;
|
|
ScRange aErrorMoveRange( ScAddress::UNINITIALIZED );
|
|
if (!aOldRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorMoveRange, rCxt.mrDoc))
|
|
{
|
|
assert(!"can't move");
|
|
}
|
|
|
|
// In a named expression, we'll move the reference only when the reference
|
|
// is entirely absolute.
|
|
|
|
sc::RefUpdateResult aRes;
|
|
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
if (rRef.IsColRel() || rRef.IsRowRel() || rRef.IsTabRel())
|
|
continue;
|
|
|
|
ScAddress aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
|
|
|
|
// Do not update the reference in transposed case (cut paste transposed).
|
|
// The reference will be updated in UpdateTranspose().
|
|
if (rCxt.mbTransposed && aOldRange.Contains(aAbs))
|
|
break;
|
|
|
|
if (aOldRange.Contains(aAbs))
|
|
{
|
|
ScAddress aErrorPos( ScAddress::UNINITIALIZED );
|
|
if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorPos, rCxt.mrDoc))
|
|
aAbs = aErrorPos;
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
|
|
rRef.SetAddress(rCxt.mrDoc.GetSheetLimits(), aAbs, rPos);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
if (rRef.Ref1.IsColRel() || rRef.Ref1.IsRowRel() || rRef.Ref1.IsTabRel() ||
|
|
rRef.Ref2.IsColRel() || rRef.Ref2.IsRowRel() || rRef.Ref2.IsTabRel())
|
|
continue;
|
|
|
|
ScRange aAbs = rRef.toAbs(rCxt.mrDoc, rPos);
|
|
|
|
// Do not update the reference in transposed case (cut paste transposed).
|
|
// The reference will be updated in UpdateTranspose().
|
|
if (rCxt.mbTransposed && aOldRange.Contains(aAbs))
|
|
break;
|
|
|
|
if (aOldRange.Contains(aAbs))
|
|
{
|
|
ScRange aErrorRange( ScAddress::UNINITIALIZED );
|
|
if (!aAbs.Move(rCxt.mnColDelta, rCxt.mnRowDelta, rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
|
|
aAbs = aErrorRange;
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
|
|
rRef.SetRange(rCxt.mrDoc.GetSheetLimits(), aAbs, rPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
return aRes;
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool adjustSingleRefOnDeletedTab( const ScSheetLimits& rLimits, ScSingleRefData& rRef, SCTAB nDelPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos )
|
|
{
|
|
ScAddress aAbs = rRef.toAbs(rLimits, rOldPos);
|
|
if (nDelPos <= aAbs.Tab() && aAbs.Tab() < nDelPos + nSheets)
|
|
{
|
|
rRef.SetTabDeleted(true);
|
|
return true;
|
|
}
|
|
|
|
if (nDelPos < aAbs.Tab())
|
|
{
|
|
// Reference sheet needs to be adjusted.
|
|
aAbs.IncTab(-1*nSheets);
|
|
rRef.SetAddress(rLimits, aAbs, rNewPos);
|
|
return true;
|
|
}
|
|
else if (rOldPos.Tab() != rNewPos.Tab())
|
|
{
|
|
// Cell itself has moved.
|
|
rRef.SetAddress(rLimits, aAbs, rNewPos);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool adjustSingleRefOnInsertedTab( const ScSheetLimits& rLimits, ScSingleRefData& rRef, SCTAB nInsPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos )
|
|
{
|
|
ScAddress aAbs = rRef.toAbs(rLimits, rOldPos);
|
|
if (nInsPos <= aAbs.Tab())
|
|
{
|
|
// Reference sheet needs to be adjusted.
|
|
aAbs.IncTab(nSheets);
|
|
rRef.SetAddress(rLimits, aAbs, rNewPos);
|
|
return true;
|
|
}
|
|
else if (rOldPos.Tab() != rNewPos.Tab())
|
|
{
|
|
// Cell itself has moved.
|
|
rRef.SetAddress(rLimits, aAbs, rNewPos);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool adjustDoubleRefOnDeleteTab(const ScSheetLimits& rLimits, ScComplexRefData& rRef, SCTAB nDelPos, SCTAB nSheets, const ScAddress& rOldPos, const ScAddress& rNewPos)
|
|
{
|
|
ScSingleRefData& rRef1 = rRef.Ref1;
|
|
ScSingleRefData& rRef2 = rRef.Ref2;
|
|
ScAddress aStartPos = rRef1.toAbs(rLimits, rOldPos);
|
|
ScAddress aEndPos = rRef2.toAbs(rLimits, rOldPos);
|
|
bool bMoreThanOneTab = aStartPos.Tab() != aEndPos.Tab();
|
|
bool bModified = false;
|
|
if (bMoreThanOneTab && aStartPos.Tab() == nDelPos && nDelPos + nSheets <= aEndPos.Tab())
|
|
{
|
|
if (rRef1.IsTabRel() && aStartPos.Tab() < rOldPos.Tab())
|
|
{
|
|
rRef1.IncTab(nSheets);
|
|
bModified = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bModified = adjustSingleRefOnDeletedTab(rLimits, rRef1, nDelPos, nSheets, rOldPos, rNewPos);
|
|
}
|
|
|
|
if (bMoreThanOneTab && aEndPos.Tab() == nDelPos && aStartPos.Tab() <= nDelPos - nSheets)
|
|
{
|
|
if (!rRef2.IsTabRel() || rOldPos.Tab() < aEndPos.Tab())
|
|
{
|
|
rRef2.IncTab(-nSheets);
|
|
bModified = true;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bModified |= adjustSingleRefOnDeletedTab(rLimits, rRef2, nDelPos, nSheets, rOldPos, rNewPos);
|
|
}
|
|
return bModified;
|
|
}
|
|
|
|
}
|
|
|
|
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnDeletedTab( const sc::RefUpdateDeleteTabContext& rCxt, const ScAddress& rOldPos )
|
|
{
|
|
sc::RefUpdateResult aRes;
|
|
ScAddress aNewPos = rOldPos;
|
|
ScRangeUpdater::UpdateDeleteTab( aNewPos, rCxt);
|
|
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
if (adjustSingleRefOnDeletedTab(*mxSheetLimits, rRef, rCxt.mnDeletePos, rCxt.mnSheets, rOldPos, aNewPos))
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
aRes.mbReferenceModified |= adjustDoubleRefOnDeleteTab(*mxSheetLimits, rRef, rCxt.mnDeletePos, rCxt.mnSheets, rOldPos, aNewPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
// For ocTableRef p is the inner token of *pp, so have a separate
|
|
// condition here.
|
|
if ((*pp)->GetType() == svIndex)
|
|
{
|
|
switch ((*pp)->GetOpCode())
|
|
{
|
|
case ocName:
|
|
{
|
|
SCTAB nOldTab = (*pp)->GetSheet();
|
|
if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
|
|
aRes.mbNameModified = true;
|
|
if (rCxt.mnDeletePos <= nOldTab)
|
|
{
|
|
aRes.mbNameModified = true;
|
|
if (rCxt.mnDeletePos + rCxt.mnSheets <= nOldTab)
|
|
(*pp)->SetSheet( nOldTab - rCxt.mnSheets);
|
|
else
|
|
// Would point to a deleted sheet. Invalidate.
|
|
(*pp)->SetSheet( SCTAB_MAX);
|
|
}
|
|
}
|
|
break;
|
|
case ocDBArea:
|
|
case ocTableRef:
|
|
if (isDBDataModified(rCxt.mrDoc, **pp))
|
|
aRes.mbNameModified = true;
|
|
break;
|
|
default:
|
|
; // nothing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return aRes;
|
|
}
|
|
|
|
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnInsertedTab( const sc::RefUpdateInsertTabContext& rCxt, const ScAddress& rOldPos )
|
|
{
|
|
sc::RefUpdateResult aRes;
|
|
ScAddress aNewPos = rOldPos;
|
|
if (rCxt.mnInsertPos <= rOldPos.Tab())
|
|
aNewPos.IncTab(rCxt.mnSheets);
|
|
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef.Ref1, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
|
|
aRes.mbReferenceModified = true;
|
|
if (adjustSingleRefOnInsertedTab(*mxSheetLimits, rRef.Ref2, rCxt.mnInsertPos, rCxt.mnSheets, rOldPos, aNewPos))
|
|
aRes.mbReferenceModified = true;
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
// For ocTableRef p is the inner token of *pp, so have a separate
|
|
// condition here.
|
|
if ((*pp)->GetType() == svIndex)
|
|
{
|
|
switch ((*pp)->GetOpCode())
|
|
{
|
|
case ocName:
|
|
{
|
|
SCTAB nOldTab = (*pp)->GetSheet();
|
|
if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
|
|
aRes.mbNameModified = true;
|
|
if (rCxt.mnInsertPos <= nOldTab)
|
|
{
|
|
aRes.mbNameModified = true;
|
|
(*pp)->SetSheet( nOldTab + rCxt.mnSheets);
|
|
}
|
|
}
|
|
break;
|
|
case ocDBArea:
|
|
case ocTableRef:
|
|
if (isDBDataModified(rCxt.mrDoc, **pp))
|
|
aRes.mbNameModified = true;
|
|
break;
|
|
default:
|
|
; // nothing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return aRes;
|
|
}
|
|
|
|
namespace {
|
|
|
|
bool adjustTabOnMove( ScAddress& rPos, const sc::RefUpdateMoveTabContext& rCxt )
|
|
{
|
|
SCTAB nNewTab = rCxt.getNewTab(rPos.Tab());
|
|
if (nNewTab == rPos.Tab())
|
|
return false;
|
|
|
|
rPos.SetTab(nNewTab);
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
sc::RefUpdateResult ScTokenArray::AdjustReferenceOnMovedTab( const sc::RefUpdateMoveTabContext& rCxt, const ScAddress& rOldPos )
|
|
{
|
|
sc::RefUpdateResult aRes;
|
|
if (rCxt.mnOldPos == rCxt.mnNewPos)
|
|
return aRes;
|
|
|
|
ScAddress aNewPos = rOldPos;
|
|
if (adjustTabOnMove(aNewPos, rCxt))
|
|
{
|
|
aRes.mbReferenceModified = true;
|
|
aRes.mbValueChanged = true;
|
|
aRes.mnTab = aNewPos.Tab(); // this sets the new tab position used when deleting
|
|
}
|
|
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
if (adjustTabOnMove(aAbs, rCxt))
|
|
aRes.mbReferenceModified = true;
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, aNewPos);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
if (adjustTabOnMove(aAbs.aStart, rCxt))
|
|
aRes.mbReferenceModified = true;
|
|
if (adjustTabOnMove(aAbs.aEnd, rCxt))
|
|
aRes.mbReferenceModified = true;
|
|
rRef.SetRange(*mxSheetLimits, aAbs, aNewPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
|
|
// For ocTableRef p is the inner token of *pp, so have a separate
|
|
// condition here.
|
|
if ((*pp)->GetType() == svIndex)
|
|
{
|
|
switch ((*pp)->GetOpCode())
|
|
{
|
|
case ocName:
|
|
{
|
|
SCTAB nOldTab = (*pp)->GetSheet();
|
|
if (isNameModified(rCxt.maUpdatedNames, nOldTab, **pp))
|
|
aRes.mbNameModified = true;
|
|
SCTAB nNewTab = rCxt.getNewTab( nOldTab);
|
|
if (nNewTab != nOldTab)
|
|
{
|
|
aRes.mbNameModified = true;
|
|
(*pp)->SetSheet( nNewTab);
|
|
}
|
|
}
|
|
break;
|
|
case ocDBArea:
|
|
case ocTableRef:
|
|
if (isDBDataModified(rCxt.mrDoc, **pp))
|
|
aRes.mbNameModified = true;
|
|
break;
|
|
default:
|
|
; // nothing
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return aRes;
|
|
}
|
|
|
|
void ScTokenArray::AdjustReferenceOnMovedOrigin( const ScAddress& rOldPos, const ScAddress& rNewPos )
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
case svExternalSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScTokenArray::AdjustReferenceOnMovedOriginIfOtherSheet( const ScAddress& rOldPos, const ScAddress& rNewPos )
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
bool bAdjust = false;
|
|
switch (p->GetType())
|
|
{
|
|
case svExternalSingleRef:
|
|
bAdjust = true; // always
|
|
[[fallthrough]];
|
|
case svSingleRef:
|
|
{
|
|
ScSingleRefData& rRef = *p->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
if (!bAdjust)
|
|
bAdjust = (aAbs.Tab() != rOldPos.Tab());
|
|
if (bAdjust)
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, rNewPos);
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
bAdjust = true; // always
|
|
[[fallthrough]];
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rOldPos);
|
|
if (!bAdjust)
|
|
bAdjust = (rOldPos.Tab() < aAbs.aStart.Tab() || aAbs.aEnd.Tab() < rOldPos.Tab());
|
|
if (bAdjust)
|
|
rRef.SetRange(*mxSheetLimits, aAbs, rNewPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScTokenArray::AdjustReferenceOnCopy( const ScAddress& rNewPos )
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN, false);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svDoubleRef:
|
|
{
|
|
ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
rRef.PutInOrder( rNewPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
void clearTabDeletedFlag( const ScSheetLimits& rLimits, ScSingleRefData& rRef, const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
|
|
{
|
|
if (!rRef.IsTabDeleted())
|
|
return;
|
|
|
|
ScAddress aAbs = rRef.toAbs(rLimits, rPos);
|
|
if (nStartTab <= aAbs.Tab() && aAbs.Tab() <= nEndTab)
|
|
rRef.SetTabDeleted(false);
|
|
}
|
|
|
|
}
|
|
|
|
void ScTokenArray::ClearTabDeleted( const ScAddress& rPos, SCTAB nStartTab, SCTAB nEndTab )
|
|
{
|
|
if (nEndTab < nStartTab)
|
|
return;
|
|
|
|
FormulaToken** p = pCode.get();
|
|
FormulaToken** pEnd = p + static_cast<size_t>(nLen);
|
|
for (; p != pEnd; ++p)
|
|
{
|
|
switch ((*p)->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
formula::FormulaToken* pToken = *p;
|
|
ScSingleRefData& rRef = *pToken->GetSingleRef();
|
|
clearTabDeletedFlag(*mxSheetLimits, rRef, rPos, nStartTab, nEndTab);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
formula::FormulaToken* pToken = *p;
|
|
ScComplexRefData& rRef = *pToken->GetDoubleRef();
|
|
clearTabDeletedFlag(*mxSheetLimits, rRef.Ref1, rPos, nStartTab, nEndTab);
|
|
clearTabDeletedFlag(*mxSheetLimits, rRef.Ref2, rPos, nStartTab, nEndTab);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
void checkBounds(
|
|
const ScSheetLimits& rLimits,
|
|
const ScAddress& rPos, SCROW nGroupLen, const ScRange& rCheckRange,
|
|
const ScSingleRefData& rRef, std::vector<SCROW>& rBounds, const ScRange* pDeletedRange )
|
|
{
|
|
if (!rRef.IsRowRel())
|
|
return;
|
|
|
|
ScRange aAbs(rRef.toAbs(rLimits, rPos));
|
|
aAbs.aEnd.IncRow(nGroupLen-1);
|
|
if (!rCheckRange.Intersects(aAbs) && (!pDeletedRange || !pDeletedRange->Intersects(aAbs)))
|
|
return;
|
|
|
|
// Get the boundary row positions.
|
|
if (aAbs.aEnd.Row() < rCheckRange.aStart.Row() && (!pDeletedRange || aAbs.aEnd.Row() < pDeletedRange->aStart.Row()))
|
|
// No intersections.
|
|
return;
|
|
|
|
// rCheckRange may be a virtual non-existent row being shifted in.
|
|
if (aAbs.aStart.Row() <= rCheckRange.aStart.Row() && rCheckRange.aStart.Row() < rLimits.GetMaxRowCount())
|
|
{
|
|
// +-+ <---- top
|
|
// | |
|
|
// +--+-+--+ <---- boundary row position
|
|
// | | | |
|
|
// | |
|
|
// +-------+
|
|
|
|
// Add offset from the reference top to the cell position.
|
|
SCROW nOffset = rCheckRange.aStart.Row() - aAbs.aStart.Row();
|
|
rBounds.push_back(rPos.Row()+nOffset);
|
|
}
|
|
// Same for deleted range.
|
|
if (pDeletedRange && aAbs.aStart.Row() <= pDeletedRange->aStart.Row())
|
|
{
|
|
SCROW nOffset = pDeletedRange->aStart.Row() - aAbs.aStart.Row();
|
|
SCROW nRow = rPos.Row() + nOffset;
|
|
// Unlike for rCheckRange, for pDeletedRange nRow can be anywhere>=0.
|
|
if (rLimits.ValidRow(nRow))
|
|
rBounds.push_back(nRow);
|
|
}
|
|
|
|
if (aAbs.aEnd.Row() >= rCheckRange.aEnd.Row())
|
|
{
|
|
// only check for end range
|
|
|
|
// +-------+
|
|
// | |
|
|
// | | | |
|
|
// +--+-+--+ <---- boundary row position
|
|
// | |
|
|
// +-+
|
|
|
|
// Ditto.
|
|
SCROW nOffset = rCheckRange.aEnd.Row() + 1 - aAbs.aStart.Row();
|
|
rBounds.push_back(rPos.Row()+nOffset);
|
|
}
|
|
// Same for deleted range.
|
|
if (pDeletedRange && aAbs.aEnd.Row() >= pDeletedRange->aEnd.Row())
|
|
{
|
|
SCROW nOffset = pDeletedRange->aEnd.Row() + 1 - aAbs.aStart.Row();
|
|
SCROW nRow = rPos.Row() + nOffset;
|
|
// Unlike for rCheckRange, for pDeletedRange nRow can be ~anywhere.
|
|
if (rLimits.ValidRow(nRow))
|
|
rBounds.push_back(nRow);
|
|
}
|
|
}
|
|
|
|
void checkBounds(
|
|
const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen,
|
|
const ScSingleRefData& rRef, std::vector<SCROW>& rBounds)
|
|
{
|
|
if (!rRef.IsRowRel())
|
|
return;
|
|
|
|
ScRange aDeletedRange( ScAddress::UNINITIALIZED );
|
|
const ScRange* pDeletedRange = nullptr;
|
|
|
|
ScRange aCheckRange = rCxt.maRange;
|
|
if (rCxt.meMode == URM_MOVE)
|
|
{
|
|
// Check bounds against the old range prior to the move.
|
|
ScRange aErrorRange( ScAddress::UNINITIALIZED );
|
|
if (!aCheckRange.Move(-rCxt.mnColDelta, -rCxt.mnRowDelta, -rCxt.mnTabDelta, aErrorRange, rCxt.mrDoc))
|
|
{
|
|
assert(!"can't move");
|
|
}
|
|
|
|
// Check bounds also against the range moved into.
|
|
pDeletedRange = &rCxt.maRange;
|
|
}
|
|
else if (rCxt.meMode == URM_INSDEL &&
|
|
((rCxt.mnColDelta < 0 && rCxt.maRange.aStart.Col() > 0) ||
|
|
(rCxt.mnRowDelta < 0 && rCxt.maRange.aStart.Row() > 0)))
|
|
{
|
|
// Check bounds also against deleted range where cells are shifted
|
|
// into and references need to be invalidated.
|
|
aDeletedRange = getSelectedRange( rCxt);
|
|
pDeletedRange = &aDeletedRange;
|
|
}
|
|
|
|
checkBounds(rCxt.mrDoc.GetSheetLimits(), rPos, nGroupLen, aCheckRange, rRef, rBounds, pDeletedRange);
|
|
}
|
|
|
|
}
|
|
|
|
void ScTokenArray::CheckRelativeReferenceBounds(
|
|
const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
checkBounds(rCxt, rPos, nGroupLen, *p->GetSingleRef(), rBounds);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
const ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
checkBounds(rCxt, rPos, nGroupLen, rRef.Ref1, rBounds);
|
|
checkBounds(rCxt, rPos, nGroupLen, rRef.Ref2, rBounds);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScTokenArray::CheckRelativeReferenceBounds(
|
|
const ScAddress& rPos, SCROW nGroupLen, const ScRange& rRange, std::vector<SCROW>& rBounds ) const
|
|
{
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken** pp = aPtrs.maPointerRange[j].mpStart;
|
|
FormulaToken** pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
const ScSingleRefData& rRef = *p->GetSingleRef();
|
|
checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef, rBounds, nullptr);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
const ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef.Ref1, rBounds, nullptr);
|
|
checkBounds(*mxSheetLimits, rPos, nGroupLen, rRange, rRef.Ref2, rBounds, nullptr);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScTokenArray::CheckExpandReferenceBounds(
|
|
const sc::RefUpdateContext& rCxt, const ScAddress& rPos, SCROW nGroupLen, std::vector<SCROW>& rBounds ) const
|
|
{
|
|
const SCROW nInsRow = rCxt.maRange.aStart.Row();
|
|
TokenPointers aPtrs( pCode.get(), nLen, pRPN, nRPN);
|
|
for (size_t j=0; j<2; ++j)
|
|
{
|
|
FormulaToken* const * pp = aPtrs.maPointerRange[j].mpStart;
|
|
const FormulaToken* const * pEnd = aPtrs.maPointerRange[j].mpStop;
|
|
for (; pp != pEnd; ++pp)
|
|
{
|
|
const FormulaToken* p = aPtrs.getHandledToken(j,pp);
|
|
if (!p)
|
|
continue;
|
|
|
|
switch (p->GetType())
|
|
{
|
|
case svDoubleRef:
|
|
{
|
|
const ScComplexRefData& rRef = *p->GetDoubleRef();
|
|
bool bStartRowRelative = rRef.Ref1.IsRowRel();
|
|
bool bEndRowRelative = rRef.Ref2.IsRowRel();
|
|
|
|
// For absolute references nothing needs to be done, they stay
|
|
// the same for all and if to be expanded the group will be
|
|
// adjusted later.
|
|
if (!bStartRowRelative && !bEndRowRelative)
|
|
break; // switch
|
|
|
|
ScRange aAbsStart(rRef.toAbs(*mxSheetLimits, rPos));
|
|
ScAddress aPos(rPos);
|
|
aPos.IncRow(nGroupLen);
|
|
ScRange aAbsEnd(rRef.toAbs(*mxSheetLimits, aPos));
|
|
// References must be at least two rows to be expandable.
|
|
if ((aAbsStart.aEnd.Row() - aAbsStart.aStart.Row() < 1) &&
|
|
(aAbsEnd.aEnd.Row() - aAbsEnd.aStart.Row() < 1))
|
|
break; // switch
|
|
|
|
// Only need to process if an edge may be touching the
|
|
// insertion row anywhere within the run of the group.
|
|
if (!((aAbsStart.aStart.Row() <= nInsRow && nInsRow <= aAbsEnd.aStart.Row()) ||
|
|
(aAbsStart.aEnd.Row() <= nInsRow && nInsRow <= aAbsEnd.aEnd.Row())))
|
|
break; // switch
|
|
|
|
SCROW nStartRow = aAbsStart.aStart.Row();
|
|
SCROW nEndRow = aAbsStart.aEnd.Row();
|
|
// Position on first relevant range.
|
|
SCROW nOffset = 0;
|
|
if (nEndRow + 1 < nInsRow)
|
|
{
|
|
if (bEndRowRelative)
|
|
{
|
|
nOffset = nInsRow - nEndRow - 1;
|
|
nEndRow += nOffset;
|
|
if (bStartRowRelative)
|
|
nStartRow += nOffset;
|
|
}
|
|
else // bStartRowRelative==true
|
|
{
|
|
nOffset = nInsRow - nStartRow;
|
|
nStartRow += nOffset;
|
|
// Start is overtaking End, swap.
|
|
bStartRowRelative = false;
|
|
bEndRowRelative = true;
|
|
}
|
|
}
|
|
for (SCROW i = nOffset; i < nGroupLen; ++i)
|
|
{
|
|
bool bSplit = (nStartRow == nInsRow || nEndRow + 1 == nInsRow);
|
|
if (bSplit)
|
|
rBounds.push_back( rPos.Row() + i);
|
|
|
|
if (bEndRowRelative)
|
|
++nEndRow;
|
|
if (bStartRowRelative)
|
|
{
|
|
++nStartRow;
|
|
if (!bEndRowRelative && nStartRow == nEndRow)
|
|
{
|
|
// Start is overtaking End, swap.
|
|
bStartRowRelative = false;
|
|
bEndRowRelative = true;
|
|
}
|
|
}
|
|
if (nInsRow < nStartRow || (!bStartRowRelative && nInsRow <= nEndRow))
|
|
{
|
|
if (bSplit && (++i < nGroupLen))
|
|
rBounds.push_back( rPos.Row() + i);
|
|
break; // for, out of range now
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
namespace {
|
|
|
|
void appendDouble( const sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, double fVal )
|
|
{
|
|
if (rCxt.mxOpCodeMap->isEnglish())
|
|
{
|
|
rtl::math::doubleToUStringBuffer(
|
|
rBuf, fVal, rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max, '.', true);
|
|
}
|
|
else
|
|
{
|
|
SvtSysLocale aSysLocale;
|
|
rtl::math::doubleToUStringBuffer(
|
|
rBuf, fVal,
|
|
rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
|
|
aSysLocale.GetLocaleData().getNumDecimalSep()[0], true);
|
|
}
|
|
}
|
|
|
|
void appendString( OUStringBuffer& rBuf, const OUString& rStr )
|
|
{
|
|
rBuf.append('"');
|
|
rBuf.append(rStr.replaceAll("\"", "\"\""));
|
|
rBuf.append('"');
|
|
}
|
|
|
|
void appendTokenByType( ScSheetLimits& rLimits, sc::TokenStringContext& rCxt, OUStringBuffer& rBuf, const FormulaToken& rToken,
|
|
const ScAddress& rPos, bool bFromRangeName )
|
|
{
|
|
if (rToken.IsExternalRef())
|
|
{
|
|
size_t nFileId = rToken.GetIndex();
|
|
OUString aTabName = rToken.GetString().getString();
|
|
if (nFileId >= rCxt.maExternalFileNames.size())
|
|
// out of bound
|
|
return;
|
|
|
|
OUString aFileName = rCxt.maExternalFileNames[nFileId];
|
|
|
|
switch (rToken.GetType())
|
|
{
|
|
case svExternalName:
|
|
rBuf.append(rCxt.mpRefConv->makeExternalNameStr(nFileId, aFileName, aTabName));
|
|
break;
|
|
case svExternalSingleRef:
|
|
rCxt.mpRefConv->makeExternalRefStr(
|
|
rLimits, rBuf, rPos, nFileId, aFileName, aTabName, *rToken.GetSingleRef());
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
sc::TokenStringContext::IndexNamesMapType::const_iterator it =
|
|
rCxt.maExternalCachedTabNames.find(nFileId);
|
|
|
|
if (it == rCxt.maExternalCachedTabNames.end())
|
|
return;
|
|
|
|
rCxt.mpRefConv->makeExternalRefStr(
|
|
rLimits, rBuf, rPos, nFileId, aFileName, it->second, aTabName,
|
|
*rToken.GetDoubleRef());
|
|
}
|
|
break;
|
|
default:
|
|
// warning, not error, otherwise we may end up with a never
|
|
// ending message box loop if this was the cursor cell to be redrawn.
|
|
OSL_FAIL("appendTokenByType: unknown type of ocExternalRef");
|
|
}
|
|
return;
|
|
}
|
|
|
|
OpCode eOp = rToken.GetOpCode();
|
|
switch (rToken.GetType())
|
|
{
|
|
case svDouble:
|
|
appendDouble(rCxt, rBuf, rToken.GetDouble());
|
|
break;
|
|
case svString:
|
|
{
|
|
OUString aStr = rToken.GetString().getString();
|
|
if (eOp == ocBad || eOp == ocStringXML || eOp == ocStringName)
|
|
{
|
|
rBuf.append(aStr);
|
|
return;
|
|
}
|
|
|
|
appendString(rBuf, aStr);
|
|
}
|
|
break;
|
|
case svSingleRef:
|
|
{
|
|
if (rCxt.mpRefConv)
|
|
{
|
|
const ScSingleRefData& rRef = *rToken.GetSingleRef();
|
|
ScComplexRefData aRef;
|
|
aRef.Ref1 = rRef;
|
|
aRef.Ref2 = rRef;
|
|
rCxt.mpRefConv->makeRefStr(rLimits, rBuf, rCxt.meGram, rPos, rCxt.maErrRef, rCxt.maTabNames, aRef, true,
|
|
bFromRangeName);
|
|
}
|
|
else
|
|
rBuf.append(rCxt.maErrRef);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
if (rCxt.mpRefConv)
|
|
{
|
|
const ScComplexRefData& rRef = *rToken.GetDoubleRef();
|
|
rCxt.mpRefConv->makeRefStr(rLimits, rBuf, rCxt.meGram, rPos, rCxt.maErrRef, rCxt.maTabNames, rRef, false,
|
|
bFromRangeName);
|
|
}
|
|
else
|
|
rBuf.append(rCxt.maErrRef);
|
|
}
|
|
break;
|
|
case svMatrix:
|
|
{
|
|
const ScMatrix* pMat = rToken.GetMatrix();
|
|
if (!pMat)
|
|
return;
|
|
|
|
size_t nC, nMaxC, nR, nMaxR;
|
|
pMat->GetDimensions(nMaxC, nMaxR);
|
|
|
|
rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayOpen));
|
|
for (nR = 0 ; nR < nMaxR ; ++nR)
|
|
{
|
|
if (nR > 0)
|
|
{
|
|
rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayRowSep));
|
|
}
|
|
|
|
for (nC = 0 ; nC < nMaxC ; ++nC)
|
|
{
|
|
if (nC > 0)
|
|
{
|
|
rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayColSep));
|
|
}
|
|
|
|
if (pMat->IsValue(nC, nR))
|
|
{
|
|
if (pMat->IsBoolean(nC, nR))
|
|
{
|
|
bool bVal = pMat->GetDouble(nC, nR) != 0.0;
|
|
rBuf.append(rCxt.mxOpCodeMap->getSymbol(bVal ? ocTrue : ocFalse));
|
|
}
|
|
else
|
|
{
|
|
FormulaError nErr = pMat->GetError(nC, nR);
|
|
if (nErr != FormulaError::NONE)
|
|
rBuf.append(ScGlobal::GetErrorString(nErr));
|
|
else
|
|
appendDouble(rCxt, rBuf, pMat->GetDouble(nC, nR));
|
|
}
|
|
}
|
|
else if (pMat->IsEmpty(nC, nR))
|
|
{
|
|
// Skip it.
|
|
}
|
|
else if (pMat->IsStringOrEmpty(nC, nR))
|
|
appendString(rBuf, pMat->GetString(nC, nR).getString());
|
|
}
|
|
}
|
|
rBuf.append(rCxt.mxOpCodeMap->getSymbol(ocArrayClose));
|
|
}
|
|
break;
|
|
case svIndex:
|
|
{
|
|
typedef sc::TokenStringContext::IndexNameMapType NameType;
|
|
|
|
sal_uInt16 nIndex = rToken.GetIndex();
|
|
switch (eOp)
|
|
{
|
|
case ocName:
|
|
{
|
|
SCTAB nTab = rToken.GetSheet();
|
|
if (nTab < 0)
|
|
{
|
|
// global named range
|
|
NameType::const_iterator it = rCxt.maGlobalRangeNames.find(nIndex);
|
|
if (it == rCxt.maGlobalRangeNames.end())
|
|
{
|
|
rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
|
|
break;
|
|
}
|
|
|
|
rBuf.append(it->second);
|
|
}
|
|
else
|
|
{
|
|
// sheet-local named range
|
|
if (nTab != rPos.Tab())
|
|
{
|
|
// On other sheet.
|
|
OUString aName;
|
|
if (o3tl::make_unsigned(nTab) < rCxt.maTabNames.size())
|
|
aName = rCxt.maTabNames[nTab];
|
|
if (!aName.isEmpty())
|
|
{
|
|
ScCompiler::CheckTabQuotes( aName, rCxt.mpRefConv->meConv);
|
|
rBuf.append( aName);
|
|
}
|
|
else
|
|
rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
|
|
rBuf.append( rCxt.mpRefConv->getSpecialSymbol( ScCompiler::Convention::SHEET_SEPARATOR));
|
|
}
|
|
|
|
sc::TokenStringContext::TabIndexMapType::const_iterator itTab = rCxt.maSheetRangeNames.find(nTab);
|
|
if (itTab == rCxt.maSheetRangeNames.end())
|
|
{
|
|
rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
|
|
break;
|
|
}
|
|
|
|
const NameType& rNames = itTab->second;
|
|
NameType::const_iterator it = rNames.find(nIndex);
|
|
if (it == rNames.end())
|
|
{
|
|
rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
|
|
break;
|
|
}
|
|
|
|
rBuf.append(it->second);
|
|
}
|
|
}
|
|
break;
|
|
case ocDBArea:
|
|
case ocTableRef:
|
|
{
|
|
NameType::const_iterator it = rCxt.maNamedDBs.find(nIndex);
|
|
if (it != rCxt.maNamedDBs.end())
|
|
rBuf.append(it->second);
|
|
}
|
|
break;
|
|
default:
|
|
rBuf.append(ScCompiler::GetNativeSymbol(ocErrName));
|
|
}
|
|
}
|
|
break;
|
|
case svExternal:
|
|
{
|
|
// mapped or translated name of AddIns
|
|
OUString aAddIn = rToken.GetExternal();
|
|
bool bMapped = rCxt.mxOpCodeMap->isPODF(); // ODF 1.1 directly uses programmatical name
|
|
if (!bMapped && rCxt.mxOpCodeMap->hasExternals())
|
|
{
|
|
const ExternalHashMap& rExtMap = rCxt.mxOpCodeMap->getReverseExternalHashMap();
|
|
ExternalHashMap::const_iterator it = rExtMap.find(aAddIn);
|
|
if (it != rExtMap.end())
|
|
{
|
|
aAddIn = it->second;
|
|
bMapped = true;
|
|
}
|
|
}
|
|
|
|
if (!bMapped && !rCxt.mxOpCodeMap->isEnglish())
|
|
ScGlobal::GetAddInCollection()->LocalizeString(aAddIn);
|
|
|
|
rBuf.append(aAddIn);
|
|
}
|
|
break;
|
|
case svError:
|
|
{
|
|
FormulaError nErr = rToken.GetError();
|
|
OpCode eOpErr;
|
|
switch (nErr)
|
|
{
|
|
case FormulaError::DivisionByZero:
|
|
eOpErr = ocErrDivZero;
|
|
break;
|
|
case FormulaError::NoValue:
|
|
eOpErr = ocErrValue;
|
|
break;
|
|
case FormulaError::NoRef:
|
|
eOpErr = ocErrRef;
|
|
break;
|
|
case FormulaError::NoName:
|
|
eOpErr = ocErrName;
|
|
break;
|
|
case FormulaError::IllegalFPOperation:
|
|
eOpErr = ocErrNum;
|
|
break;
|
|
case FormulaError::NotAvailable:
|
|
eOpErr = ocErrNA;
|
|
break;
|
|
case FormulaError::NoCode:
|
|
default:
|
|
eOpErr = ocErrNull;
|
|
}
|
|
rBuf.append(rCxt.mxOpCodeMap->getSymbol(eOpErr));
|
|
}
|
|
break;
|
|
case svByte:
|
|
case svJump:
|
|
case svFAP:
|
|
case svMissing:
|
|
case svSep:
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
OUString ScTokenArray::CreateString( sc::TokenStringContext& rCxt, const ScAddress& rPos ) const
|
|
{
|
|
if (!nLen)
|
|
return OUString();
|
|
|
|
OUStringBuffer aBuf;
|
|
|
|
FormulaToken** p = pCode.get();
|
|
FormulaToken** pEnd = p + static_cast<size_t>(nLen);
|
|
for (; p != pEnd; ++p)
|
|
{
|
|
const FormulaToken* pToken = *p;
|
|
OpCode eOp = pToken->GetOpCode();
|
|
/* FIXME: why does this ignore the count of spaces? */
|
|
if (eOp == ocSpaces)
|
|
{
|
|
// TODO : Handle intersection operator '!!'.
|
|
aBuf.append(' ');
|
|
continue;
|
|
}
|
|
else if (eOp == ocWhitespace)
|
|
{
|
|
aBuf.append( pToken->GetChar());
|
|
continue;
|
|
}
|
|
|
|
if (eOp < rCxt.mxOpCodeMap->getSymbolCount())
|
|
aBuf.append(rCxt.mxOpCodeMap->getSymbol(eOp));
|
|
|
|
appendTokenByType(*mxSheetLimits, rCxt, aBuf, *pToken, rPos, IsFromRangeName());
|
|
}
|
|
|
|
return aBuf.makeStringAndClear();
|
|
}
|
|
|
|
namespace {
|
|
|
|
void wrapAddress( ScAddress& rPos, SCCOL nMaxCol, SCROW nMaxRow )
|
|
{
|
|
if (rPos.Col() > nMaxCol)
|
|
rPos.SetCol(rPos.Col() % (nMaxCol+1));
|
|
if (rPos.Row() > nMaxRow)
|
|
rPos.SetRow(rPos.Row() % (nMaxRow+1));
|
|
}
|
|
|
|
template<typename T> void wrapRange( T& n1, T& n2, T nMax )
|
|
{
|
|
if (n2 > nMax)
|
|
{
|
|
if (n1 == 0)
|
|
n2 = nMax; // Truncate to full range instead of wrapping to a weird range.
|
|
else
|
|
n2 = n2 % (nMax+1);
|
|
}
|
|
if (n1 > nMax)
|
|
n1 = n1 % (nMax+1);
|
|
}
|
|
|
|
void wrapColRange( ScRange& rRange, SCCOL nMaxCol )
|
|
{
|
|
SCCOL nCol1 = rRange.aStart.Col();
|
|
SCCOL nCol2 = rRange.aEnd.Col();
|
|
wrapRange( nCol1, nCol2, nMaxCol);
|
|
rRange.aStart.SetCol( nCol1);
|
|
rRange.aEnd.SetCol( nCol2);
|
|
}
|
|
|
|
void wrapRowRange( ScRange& rRange, SCROW nMaxRow )
|
|
{
|
|
SCROW nRow1 = rRange.aStart.Row();
|
|
SCROW nRow2 = rRange.aEnd.Row();
|
|
wrapRange( nRow1, nRow2, nMaxRow);
|
|
rRange.aStart.SetRow( nRow1);
|
|
rRange.aEnd.SetRow( nRow2);
|
|
}
|
|
|
|
}
|
|
|
|
void ScTokenArray::WrapReference( const ScAddress& rPos, SCCOL nMaxCol, SCROW nMaxRow )
|
|
{
|
|
FormulaToken** p = pCode.get();
|
|
FormulaToken** pEnd = p + static_cast<size_t>(nLen);
|
|
for (; p != pEnd; ++p)
|
|
{
|
|
switch ((*p)->GetType())
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
formula::FormulaToken* pToken = *p;
|
|
ScSingleRefData& rRef = *pToken->GetSingleRef();
|
|
ScAddress aAbs = rRef.toAbs(*mxSheetLimits, rPos);
|
|
wrapAddress(aAbs, nMaxCol, nMaxRow);
|
|
rRef.SetAddress(*mxSheetLimits, aAbs, rPos);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
formula::FormulaToken* pToken = *p;
|
|
ScComplexRefData& rRef = *pToken->GetDoubleRef();
|
|
ScRange aAbs = rRef.toAbs(*mxSheetLimits, rPos);
|
|
// Entire columns/rows are sticky.
|
|
if (!rRef.IsEntireCol(*mxSheetLimits) && !rRef.IsEntireRow(*mxSheetLimits))
|
|
{
|
|
wrapColRange( aAbs, nMaxCol);
|
|
wrapRowRange( aAbs, nMaxRow);
|
|
}
|
|
else if (rRef.IsEntireCol(*mxSheetLimits) && !rRef.IsEntireRow(*mxSheetLimits))
|
|
wrapColRange( aAbs, nMaxCol);
|
|
else if (!rRef.IsEntireCol(*mxSheetLimits) && rRef.IsEntireRow(*mxSheetLimits))
|
|
wrapRowRange( aAbs, nMaxRow);
|
|
// else nothing if both, column and row, are entire.
|
|
aAbs.PutInOrder();
|
|
rRef.SetRange(*mxSheetLimits, aAbs, rPos);
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
}
|
|
|
|
sal_Int32 ScTokenArray::GetWeight() const
|
|
{
|
|
sal_Int32 nResult = 0;
|
|
for (auto i = 0; i < nRPN; ++i)
|
|
{
|
|
switch ((*pRPN[i]).GetType())
|
|
{
|
|
case svDoubleRef:
|
|
{
|
|
const auto pComplexRef = (*pRPN[i]).GetDoubleRef();
|
|
|
|
// Number of cells referenced divided by 10.
|
|
const double nRows = 1 + (pComplexRef->Ref2.Row() - pComplexRef->Ref1.Row());
|
|
const double nCols = 1 + (pComplexRef->Ref2.Col() - pComplexRef->Ref1.Col());
|
|
const double nNumCellsTerm = nRows * nCols / 10.0;
|
|
|
|
if (nNumCellsTerm + nResult < SAL_MAX_INT32)
|
|
nResult += nNumCellsTerm;
|
|
else
|
|
nResult = SAL_MAX_INT32;
|
|
}
|
|
break;
|
|
default:
|
|
;
|
|
}
|
|
}
|
|
|
|
if (nResult == 0)
|
|
nResult = 1;
|
|
|
|
return nResult;
|
|
}
|
|
|
|
#if DEBUG_FORMULA_COMPILER
|
|
|
|
void ScTokenArray::Dump() const
|
|
{
|
|
cout << "+++ Normal Tokens +++" << endl;
|
|
for (sal_uInt16 i = 0; i < nLen; ++i)
|
|
{
|
|
DumpToken(*pCode[i]);
|
|
}
|
|
|
|
cout << "+++ RPN Tokens +++" << endl;
|
|
for (sal_uInt16 i = 0; i < nRPN; ++i)
|
|
{
|
|
DumpToken(*pRPN[i]);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|