/* -*- 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/.
*/
sc::MultiDataCellState::StateType ScColumn::HasDataCellsInRange(
SCROW nRow1, SCROW nRow2, SCROW* pRow1 ) const
{
sc::CellStoreType::const_position_type aPos = maCells.position(nRow1);
sc::CellStoreType::const_iterator it = aPos.first;
size_t nOffset = aPos.second;
SCROW nRow = nRow1; bool bHasOne = false; // whether or not we have found a non-empty block of size one.
for (; it != maCells.end() && nRow <= nRow2; ++it)
{ if (it->type != sc::element_type_empty)
{ // non-empty block found.
assert(it->size > 0); // mtv should never contain a block of zero length.
size_t nSize = it->size - nOffset;
SCROW nLastRow = nRow + nSize - 1; if (nLastRow > nRow2) // shrink the size to avoid exceeding the specified last row position.
nSize -= nLastRow - nRow2;
if (nSize == 1)
{ // this block is of size one. if (bHasOne) return sc::MultiDataCellState::HasMultipleCells;
bHasOne = true; if (pRow1)
*pRow1 = nRow;
} else
{ // size of this block is greater than one. if (pRow1)
*pRow1 = nRow; return sc::MultiDataCellState::HasMultipleCells;
}
}
auto pDuplicatedGroup = GetDoc().SearchSparklineGroup(pSparklineGroup->getID()); if (!pDuplicatedGroup)
pDuplicatedGroup = std::make_shared<sc::SparklineGroup>(*pSparklineGroup);
std::vector<sc::SparklineCell*> aSparklines(nDestSize, nullptr);
ScAddress aCurrentPosition = aDestPosition; for (size_t i = 0; i < nDestSize; ++i)
{ auto pNewSparkline = std::make_shared<sc::Sparkline>(aCurrentPosition.Col(), aCurrentPosition.Row(), pDuplicatedGroup);
pNewSparkline->setInputRange(pSparkline->getInputRange());
aSparklines[i] = new sc::SparklineCell(std::move(pNewSparkline));
aCurrentPosition.IncRow();
}
std::vector<SCROW> aBounds { nRow1 }; if (nRow2 < GetDoc().MaxRow()-1)
aBounds.push_back(nRow2+1);
// Split formula cell groups at top and bottom boundaries (if applicable).
sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
// Parse all formulas within the range and store their results into temporary storage.
ConvertFormulaToValueHandler aFunc(GetDoc());
sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc); if (!aFunc.isModified()) // No formula cells encountered. return;
DetachFormulaCells(rCxt, nRow1, nRow2);
// Undo storage to hold static values which will get swapped to the cell storage later.
sc::CellValues aUndoCells;
aFunc.getResValues().swap(aUndoCells);
aUndoCells.swapNonEmpty(*this); if (pUndo)
pUndo->swap(nTab, nCol, aUndoCells);
}
namespace {
class StartListeningHandler
{
sc::StartListeningContext& mrCxt;
// Split formula cell groups at top and bottom boundaries (if applicable).
sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
std::vector<sc::CellValueSpan> aSpans = rValues.getNonEmptySpans(nTab, nCol);
// Detach formula cells within the spans (if any).
EndListeningHandler aEndLisFunc(rEndCxt);
sc::CellStoreType::iterator itPos = maCells.begin(); for (constauto& rSpan : aSpans)
{
SCROW nRow1 = rSpan.mnRow1;
SCROW nRow2 = rSpan.mnRow2;
itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aEndLisFunc);
}
if (nLen == 1 || !rSrc.GetCode()->IsShareable())
{ // Single, ungrouped formula cell, or create copies for // non-shareable token arrays. for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
{
ScFormulaCell* pCell = new ScFormulaCell(rSrc, rDocument, aPos);
aFormulas.push_back(pCell);
}
} else
{ // Create a group of formula cells.
ScFormulaCellGroupRef xGroup(new ScFormulaCellGroup);
xGroup->setCode(*rSrc.GetCode());
xGroup->compileCode(rDocument, aPos, rDocument.GetGrammar()); for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
{
ScFormulaCell* pCell = new ScFormulaCell(rDocument, aPos, xGroup, rDocument.GetGrammar(), nMatrixFlag); if (nMatrixFlag == ScMatrixMode::Formula)
pCell->SetMatColsRows( nMatrixCols, nMatrixRows); if (i == 0)
{
xGroup->mpTopCell = pCell;
xGroup->mnLength = nLen;
}
aFormulas.push_back(pCell);
}
}
// Join the top and bottom of the pasted formula cells as needed.
sc::CellStoreType::position_type aPosObj = maCells.position(rBlockPos.miCellPos, nRow1);
SCROW ScColumn::GetNotePosition( size_t nIndex ) const
{ // Return the row position of the nth note in the column.
size_t nCount = 0; // Number of notes encountered so far. for (constauto& rCellNote : maCellNotes)
{ if (rCellNote.type != sc::element_type_cellnote) // Skip the empty blocks. continue;
if (nIndex < nCount + rCellNote.size)
{ // Index falls within this block.
size_t nOffset = nIndex - nCount; return rCellNote.position + nOffset;
}
void ScColumn::GetUnprotectedCells( SCROW nStartRow, SCROW nEndRow, ScRangeList& rRangeList ) const
{
SCROW nTmpStartRow = nStartRow, nTmpEndRow = nEndRow; const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow); bool bProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection(); if (!bProtection)
{ // Limit the span to the range in question. if (nTmpStartRow < nStartRow)
nTmpStartRow = nStartRow; if (nTmpEndRow > nEndRow)
nTmpEndRow = nEndRow;
rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab));
} while (nEndRow > nTmpEndRow)
{
nStartRow = nTmpEndRow + 1;
pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow); bool bTmpProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection(); if (!bTmpProtection)
{ // Limit the span to the range in question. // Only end row needs to be checked as we enter here only for spans // below the original nStartRow. if (nTmpEndRow > nEndRow)
nTmpEndRow = nEndRow;
rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab));
}
}
}
// Get the formula string.
OUString aFormula = pTop->GetFormula(mrCompileFormulaCxt);
sal_Int32 n = aFormula.getLength(); if (pTop->GetMatrixFlag() != ScMatrixMode::NONE && n > 0)
{ if (aFormula[0] == '{' && aFormula[n-1] == '}')
aFormula = aFormula.copy(1, n-2);
}
if (!aFormula.isEmpty())
{ // Create a new token array from the hybrid formula string, and // set it to the group.
ScCompiler aComp(mrCompileFormulaCxt, pTop->aPos);
std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula);
ScFormulaCellGroupRef xGroup = pTop->GetCellGroup();
assert(xGroup);
xGroup->setCode(std::move(*pNewCode));
xGroup->compileCode(mrDoc, pTop->aPos, mrDoc.GetGrammar());
// Propagate the new token array to all formula cells in the group.
ScFormulaCell** pp = rEntry.mpCells;
ScFormulaCell** ppEnd = pp + rEntry.mnLength; for (; pp != ppEnd; ++pp)
{
ScFormulaCell* p = *pp;
p->SyncSharedCode();
p->StartListeningTo(mrStartListenCxt);
p->SetDirty();
}
}
} else
{
ScFormulaCell* pCell = rEntry.mpCell;
OUString aFormula = pCell->GetHybridFormula();
if (!aFormula.isEmpty())
{ // Create token array from formula string.
ScCompiler aComp(mrCompileFormulaCxt, pCell->aPos);
std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula);
voidoperator() ( size_t nRow, ScFormulaCell* pCell )
{ if (!pCell->IsShared() || pCell->IsSharedTop())
{ // Ensure that the references still point to the same locations // after the position change.
ScAddress aOldPos = pCell->aPos;
pCell->aPos.SetCol(mnCol);
pCell->aPos.SetRow(nRow); if (mbUpdateRefs)
pCell->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, pCell->aPos); else
pCell->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, pCell->aPos);
} else
{
pCell->aPos.SetCol(mnCol);
pCell->aPos.SetRow(nRow);
}
}
};
voidoperator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
{ if (node.type != sc::element_type_formula) // We are only interested in formulas. return;
// If the first formula cell belongs to a group and it's not the top // cell, move up to the top cell of the group, and have all the extra // formula cells stop listening.
ScFormulaCell* pFC = *pp; if (pFC->IsShared() && !pFC->IsSharedTop())
{
SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow(); if (nBackTrackSize > 0)
{
assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset); for (SCROW i = 0; i < nBackTrackSize; ++i)
--pp;
endListening(mrEndCxt, pp, ppBeg);
mnStartRow -= nBackTrackSize;
}
}
for (; pp != ppEnd; ++pp)
{
pFC = *pp;
if (!pFC->IsSharedTop())
{
assert(!pFC->IsShared());
pFC->StartListeningTo(mrStartCxt); continue;
}
// If This is the last group in the range, see if the group // extends beyond the range, in which case have the excess // formula cells stop listening.
size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength(); if (nEndGroupPos > nDataSize)
{
size_t nExcessSize = nEndGroupPos - nDataSize;
ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
ScFormulaCell** ppGrp = ppGrpEnd - nExcessSize;
endListening(mrEndCxt, ppGrp, ppGrpEnd);
// Register formula cells as a group.
sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
pp = ppEnd - 1; // Move to the one before the end position.
} else
{ // Register formula cells as a group.
sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
pp += pFC->GetSharedLength() - 1; // Move to the last one in the group.
}
}
}
};
class EndListeningFormulaCellsHandler
{
sc::EndListeningContext& mrEndCxt;
SCROW mnStartRow;
SCROW mnEndRow;
voidoperator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
{ if (node.type != sc::element_type_formula) // We are only interested in formulas. return;
// If the first formula cell belongs to a group and it's not the top // cell, move up to the top cell of the group.
ScFormulaCell* pFC = *pp; if (pFC->IsShared() && !pFC->IsSharedTop())
{
SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow(); if (nBackTrackSize > 0)
{
assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset); for (SCROW i = 0; i < nBackTrackSize; ++i)
--pp;
mnStartRow -= nBackTrackSize;
}
}
for (; pp != ppEnd; ++pp)
{
pFC = *pp;
if (!pFC->IsSharedTop())
{
assert(!pFC->IsShared());
pFC->EndListeningTo(mrEndCxt); continue;
}
size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
mnEndRow = node.position + nOffset + nEndGroupPos - 1; // absolute row position of the last one in the group.
if (nEndGroupPos > nDataSize)
{ // The group goes beyond the specified end row. Move to the // one before the end position to finish the loop.
pp = ppEnd - 1;
} else
{ // Move to the last one in the group.
pp += pFC->GetSharedLength() - 1;
}
}
}
sc::CellStoreType::position_type aPos = maCells.position(nRow);
sc::CellStoreType::iterator it = aPos.first; if (it->type != sc::element_type_formula) // Only interested in a formula block. return;
ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
ScFormulaCellGroupRef xGroup = pFC->GetCellGroup(); if (!xGroup) // Not a formula group. return;
// End listening.
pFC->EndListeningTo(rCxt);
if (pGroupPos)
{ if (!pFC->IsSharedTop()) // Record the position of the top cell of the group.
pGroupPos->push_back(xGroup->mpTopCell->aPos);
SCROW nGrpLastRow = pFC->GetSharedTopRow() + pFC->GetSharedLength() - 1; if (nRow < nGrpLastRow) // Record the last position of the group.
pGroupPos->push_back(ScAddress(nCol, nGrpLastRow, nTab));
}
}
void ScColumn::EndListeningIntersectedGroups(
sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, std::vector<ScAddress>* pGroupPos )
{ // Only end the intersected group.
sc::CellStoreType::position_type aPos = maCells.position(nRow1);
sc::CellStoreType::iterator it = aPos.first; if (it->type == sc::element_type_formula)
{
ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
ScFormulaCellGroupRef xGroup = pFC->GetCellGroup(); if (xGroup)
{ if (!pFC->IsSharedTop()) // End listening.
pFC->EndListeningTo(rCxt);
if (pGroupPos) // Record the position of the top cell of the group.
pGroupPos->push_back(xGroup->mpTopCell->aPos);
}
}
aPos = maCells.position(it, nRow2);
it = aPos.first; if (it->type != sc::element_type_formula) return;
if (!pFC->IsSharedTop()) // End listening.
pFC->EndListeningTo(rCxt);
if (pGroupPos)
{ // Record the position of the bottom cell of the group.
ScAddress aPosLast = xGroup->mpTopCell->aPos;
aPosLast.IncRow(xGroup->mnLength-1);
pGroupPos->push_back(aPosLast);
}
}
void ScColumn::EndListeningGroup( sc::EndListeningContext& rCxt, SCROW nRow )
{
sc::CellStoreType::position_type aPos = maCells.position(nRow); if (aPos.first->type != sc::element_type_formula) // not a formula cell. return;
ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup(); if (!xGroup)
{ // not a formula group.
(*pp)->EndListeningTo(rCxt); return;
}
// Move back to the top cell.
SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
assert(nTopDelta >= 0); if (nTopDelta > 0)
pp -= nTopDelta;
// Set the needs listening flag to all cells in the group.
assert(*pp == xGroup->mpTopCell);
ScFormulaCell** ppEnd = pp + xGroup->mnLength; for (; pp != ppEnd; ++pp)
(*pp)->EndListeningTo(rCxt);
}
void ScColumn::SetNeedsListeningGroup( SCROW nRow )
{
sc::CellStoreType::position_type aPos = maCells.position(nRow); if (aPos.first->type != sc::element_type_formula) // not a formula cell. return;
ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup(); if (!xGroup)
{ // not a formula group.
(*pp)->SetNeedsListening(true); return;
}
// Move back to the top cell.
SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
assert(nTopDelta >= 0); if (nTopDelta > 0)
pp -= nTopDelta;
// Set the needs listening flag to all cells in the group.
assert(*pp == xGroup->mpTopCell);
ScFormulaCell** ppEnd = pp + xGroup->mnLength; for (; pp != ppEnd; ++pp)
(*pp)->SetNeedsListening(true);
}
// Found a completely dirty sub span [nSpanStart, nSpanEnd] inside the required span [nStartOffset, nEndOffset] bool bGroupInterpreted = pCellStart->Interpret(nSpanStart, nSpanEnd);
if (bGroupInterpreted) for (SCROW nIdx = nSpanStart; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart)
assert(!(*itSpanStart)->NeedsInterpret());
ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper(); // child cell's Interpret could result in calling dependency calc // and that could detect a cycle involving mxGroup // and do early exit in that case. // OR // this call resulted from a dependency calculation for a multi-formula-group-threading and // if intergroup dependency is found, return early. if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent())
{
bAllowThreading = false; return bAnyDirty;
}
if (!bGroupInterpreted)
{ // Evaluate from second cell in non-grouped style (no point in trying group-interpret again).
++itSpanStart; for (SCROW nIdx = nSpanStart+1; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart)
{
(*itSpanStart)->Interpret(); // We know for sure that this cell is dirty so directly call Interpret(). if ((*itSpanStart)->NeedsInterpret())
{
SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itSpanStart)->aPos
<< " failed running Interpret(), not allowing threading");
bAllowThreading = false; return bAnyDirty;
}
// Allow early exit like above. if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent())
{ // Set this cell as dirty as this may be interpreted in InterpretTail()
pCellStart->SetDirtyVar();
bAllowThreading = false; return bAnyDirty;
}
}
}
pCellStart = nullptr; // For next sub span start detection.
}
}
for (;it != rCells.end() && nRow <= nRow2; ++it, nOffset = 0)
{ switch( it->type )
{ case sc::element_type_edittext: // These require EditEngine (in ScEditUtils::GetString()), which is probably // too complex for use in threads. if (bThreadingDepEval)
{
bAllowThreading = false; return;
} break; case sc::element_type_formula:
{
size_t nRowsToRead = nRow2 - nRow + 1; const size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1
sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
std::advance(itCell, nOffset);
// Loop inside the formula block.
size_t nCellIdx = nOffset; while (nCellIdx < nEnd)
{ const ScFormulaCellGroupRef& mxGroupChild = (*itCell)->GetCellGroup();
ScFormulaCell* pChildTopCell = mxGroupChild ? mxGroupChild->mpTopCell : *itCell;
// Check if itCell is already in path. // If yes use a cycle guard to mark all elements of the cycle // and return false if (bThreadingDepEval && pChildTopCell->GetSeenInPath())
{
ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pChildTopCell);
bAllowThreading = false; return;
}
if (mxGroupChild)
{ // It is a Formula-group, evaluate the necessary parts of it (spans). const SCROW nFGStartOffset = (*itCell)->aPos.Row() - pChildTopCell->aPos.Row(); const SCROW nFGEndOffset = std::min(nFGStartOffset + static_cast<SCROW>(nRowsToRead) - 1, mxGroupChild->mnLength - 1);
assert(nFGEndOffset >= nFGStartOffset); const SCROW nSpanLen = nFGEndOffset - nFGStartOffset + 1; // The (main) span required to be evaluated is [nFGStartOffset, nFGEndOffset], but this span may contain // non-dirty cells, so split this into sets of completely-dirty spans and try evaluate each of them in grouped-style.
bool bAnyDirtyInSpan = lcl_InterpretSpan(itCell, nFGStartOffset, nFGEndOffset, mxGroup, bAllowThreading, rDoc); if (!bAllowThreading) return; // itCell will now point to cell just after the end of span [nFGStartOffset, nFGEndOffset].
bIsDirty = bIsDirty || bAnyDirtyInSpan;
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.