/* -*- 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;
// 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 // we are trying multi-formula-group-threading, but found intergroup dependency. if (bThreadingDepEval && mxGroup &&
(mxGroup->mbPartOfCycle || !rRecursionHelper.AreGroupsIndependent()))
{ // Set itCell as dirty as itCell may be interpreted in InterpretTail()
(*itCell)->SetDirtyVar(); if (pDirtiedAddress)
pDirtiedAddress->SetRow(nRow);
bAllowThreading = false; return;
}
// Returns true if at least one FC is dirty. bool ScColumn::EnsureFormulaCellResults( SCROW nRow1, SCROW nRow2, bool bSkipRunning )
{ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2) returnfalse;
void ScColumn::StoreToCache(SvStream& rStrm) const
{
rStrm.WriteUInt64(nCol);
SCROW nLastRow = GetLastDataPos();
rStrm.WriteUInt64(nLastRow + 1); // the rows are zero based
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.