/* -*- 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 .
*/
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) returntrue;
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
}
}
}
returnfalse;
}
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;
}
}
// 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();
}
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. */
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;
}
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()
{ returnconst_cast<ScMatrix*>(xMatrix.get());
}
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 ocChooseCols: case ocChooseRows: case ocDrop: case ocExpand: case ocHStack: case ocVStack: case ocTake: case ocTextSplit: case ocToCol: case ocToRow: case ocUnique: case ocWrapCols: case ocWrapRows: // 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;
}
} elseif (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:
;
}
} elseif (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.
// 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;
}
}
}
// 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;
bool ScTokenArray::IsFormulaVectorDisabled() const
{ switch (meVectorState)
{ case FormulaVectorDisabled: case FormulaVectorDisabledByOpCode: case FormulaVectorDisabledByStackVariable: case FormulaVectorDisabledNotInSubSet: returntrue; default:
;
}
returnfalse;
}
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()) returnfalse;
} break; case svDoubleRef: case svExternalDoubleRef:
{ const ScComplexRefData& rRef = *(*p)->GetDoubleRef(); if (rRef.Ref1.IsRowRel() || rRef.Ref2.IsRowRel()) returnfalse;
} break; case svIndex: returnfalse; default:
;
}
}
// Utility function to ensure that there is strict alternation of values and // separators. staticbool
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;
}
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;
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);
}
} elseif (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;
} elseif (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);
}
} elseif (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;
}
if (rCxt.mnColDelta > 0)
{ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())) // Entire rows are not affected, columns are anchored. returnfalse;
// 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. returnfalse;
if (rCxt.mrDoc.IsExpandRefs())
{ if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1) // Reference must be at least two columns wide. returnfalse;
} else
{ if (rSelectedRange.aStart.Col() <= rRefRange.aStart.Col()) // Selected range is at the left end and the edge expansion is turned off. No expansion. returnfalse;
}
if (rRefRange.IsEndColSticky(rCxt.mrDoc)) // Sticky end not affected. returnfalse;
// Move the last column position to the right.
SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta); returntrue;
} elseif (rCxt.mnRowDelta > 0)
{ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits())) // Entire columns are not affected, rows are anchored. returnfalse;
// 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. returnfalse;
if (rCxt.mrDoc.IsExpandRefs())
{ if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1) // Reference must be at least two rows tall. returnfalse;
} else
{ if (rSelectedRange.aStart.Row() <= rRefRange.aStart.Row()) // Selected range is at the top end and the edge expansion is turned off. No expansion. returnfalse;
}
if (rRefRange.IsEndRowSticky(rCxt.mrDoc)) // Sticky end not affected. returnfalse;
// Move the last row position down.
SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta); returntrue;
} returnfalse;
}
/** * 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. returnfalse;
if (rSelectedRange.aStart.Tab() > rRefRange.aStart.Tab() || rRefRange.aEnd.Tab() > rSelectedRange.aEnd.Tab()) // Sheet references not within selected range. returnfalse;
if (rCxt.mnColDelta > 0)
{ if (rRef.IsEntireRow(rCxt.mrDoc.GetSheetLimits())) // Entire rows are not affected, columns are anchored. returnfalse;
// Insert and shift right.
if (rRefRange.aEnd.Col() - rRefRange.aStart.Col() < 1) // Reference must be at least two columns wide. returnfalse;
if (rRefRange.aStart.Row() < rSelectedRange.aStart.Row() || rSelectedRange.aEnd.Row() < rRefRange.aEnd.Row()) // Selected range is only partially overlapping in vertical direction. Bail out. returnfalse;
if (rSelectedRange.aStart.Col() - rRefRange.aEnd.Col() != 1) // Selected range is not immediately adjacent. Bail out. returnfalse;
if (rRefRange.IsEndColSticky(rCxt.mrDoc)) // Sticky end not affected. returnfalse;
// Move the last column position to the right.
SCCOL nDelta = rSelectedRange.aEnd.Col() - rSelectedRange.aStart.Col() + 1;
rRefRange.IncEndColSticky(rCxt.mrDoc, nDelta); returntrue;
} elseif (rCxt.mnRowDelta > 0)
{ if (rRef.IsEntireCol(rCxt.mrDoc.GetSheetLimits())) // Entire columns are not affected, rows are anchored. returnfalse;
if (rRefRange.aEnd.Row() - rRefRange.aStart.Row() < 1) // Reference must be at least two rows tall. returnfalse;
if (rRefRange.aStart.Col() < rSelectedRange.aStart.Col() || rSelectedRange.aEnd.Col() < rRefRange.aEnd.Col()) // Selected range is only partially overlapping in horizontal direction. Bail out. returnfalse;
if (rSelectedRange.aStart.Row() - rRefRange.aEnd.Row() != 1) // Selected range is not immediately adjacent. Bail out. returnfalse;
if (rRefRange.IsEndRowSticky(rCxt.mrDoc)) // Sticky end not affected. returnfalse;
// Move the last row position down.
SCROW nDelta = rSelectedRange.aEnd.Row() - rSelectedRange.aStart.Row() + 1;
rRefRange.IncEndRowSticky(rCxt.mrDoc, nDelta); returntrue;
}
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;
}
if (rCxt.isDeleted())
{ if (aSelectedRange.Contains(aAbs))
{ // This reference is in the deleted region.
setRefDeleted(rRef, rCxt);
aRes.mbValueChanged = true; break;
} elseif (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;
} elseif (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;
}
} elseif (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
}
}
}
}
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;
// 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;
} elseif (rCxt.maRange.Contains(aAbs))
{ // Referenced cell has been overwritten.
aRes.mbValueChanged = true;
}
// 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;
} elseif (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
}
}
}
}
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. returnfalse;
}
if (!rCxt.maRange.Contains(rRef.toAbs(rCxt.mrDoc, rPos))) returnfalse;
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); returntrue;
}
}
} 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); returntrue;
}
}
}
}
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;
}
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.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;
} elseif (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;
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);
}
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;
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;
} elseif (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;
}
} elseif (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;
}
} elseif (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;
}
// 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;
}
// 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;
}
// 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;
}
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;
// 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
}
}
}
}
// 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
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;
} elseif (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;
}
// 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:
;
}
}
}
}
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;
}
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));
}
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.171Bemerkung:
(vorverarbeitet am 2026-05-06)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.