Avoid potential negative array index access to cached text.
[LibreOffice.git] / sc / source / core / data / column4.cxx
blob8b73d5c21518d6b81262908a95f2b361e04c7c96
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <column.hxx>
11 #include <clipparam.hxx>
12 #include <cellvalue.hxx>
13 #include <attarray.hxx>
14 #include <document.hxx>
15 #include <cellvalues.hxx>
16 #include <columnspanset.hxx>
17 #include <columniterator.hxx>
18 #include <mtvcellfunc.hxx>
19 #include <clipcontext.hxx>
20 #include <attrib.hxx>
21 #include <patattr.hxx>
22 #include <docpool.hxx>
23 #include <conditio.hxx>
24 #include <formulagroup.hxx>
25 #include <tokenarray.hxx>
26 #include <scitems.hxx>
27 #include <cellform.hxx>
28 #include <sharedformula.hxx>
29 #include <drwlayer.hxx>
30 #include <compiler.hxx>
31 #include <recursionhelper.hxx>
32 #include <docsh.hxx>
33 #include <editutil.hxx>
34 #include <broadcast.hxx>
36 #include <SparklineGroup.hxx>
38 #include <o3tl/safeint.hxx>
39 #include <svl/sharedstringpool.hxx>
40 #include <sal/log.hxx>
41 #include <tools/stream.hxx>
43 #include <numeric>
44 #include <vector>
45 #include <cassert>
47 sc::MultiDataCellState::StateType ScColumn::HasDataCellsInRange(
48 SCROW nRow1, SCROW nRow2, SCROW* pRow1 ) const
50 sc::CellStoreType::const_position_type aPos = maCells.position(nRow1);
51 sc::CellStoreType::const_iterator it = aPos.first;
52 size_t nOffset = aPos.second;
53 SCROW nRow = nRow1;
54 bool bHasOne = false; // whether or not we have found a non-empty block of size one.
56 for (; it != maCells.end() && nRow <= nRow2; ++it)
58 if (it->type != sc::element_type_empty)
60 // non-empty block found.
61 assert(it->size > 0); // mtv should never contain a block of zero length.
62 size_t nSize = it->size - nOffset;
64 SCROW nLastRow = nRow + nSize - 1;
65 if (nLastRow > nRow2)
66 // shrink the size to avoid exceeding the specified last row position.
67 nSize -= nLastRow - nRow2;
69 if (nSize == 1)
71 // this block is of size one.
72 if (bHasOne)
73 return sc::MultiDataCellState::HasMultipleCells;
75 bHasOne = true;
76 if (pRow1)
77 *pRow1 = nRow;
79 else
81 // size of this block is greater than one.
82 if (pRow1)
83 *pRow1 = nRow;
84 return sc::MultiDataCellState::HasMultipleCells;
88 nRow += it->size - nOffset;
89 nOffset = 0;
92 return bHasOne ? sc::MultiDataCellState::HasOneCell : sc::MultiDataCellState::Empty;
95 void ScColumn::DeleteBeforeCopyFromClip(
96 sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans )
98 ScDocument& rDocument = GetDoc();
99 sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
100 if (!rDocument.ValidRow(aRange.mnRow1) || !rDocument.ValidRow(aRange.mnRow2))
101 return;
103 sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
104 if (!pBlockPos)
105 return;
107 InsertDeleteFlags nDelFlag = rCxt.getDeleteFlag();
109 if (!rCxt.isSkipEmptyCells())
111 // Delete the whole destination range.
113 if (nDelFlag & InsertDeleteFlags::CONTENTS)
115 auto xResult = DeleteCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2, nDelFlag);
116 rBroadcastSpans.set(GetDoc(), nTab, nCol, xResult->aDeletedRows, true);
118 for (const auto& rRange : xResult->aFormulaRanges)
119 rCxt.setListeningFormulaSpans(
120 nTab, nCol, rRange.first, nCol, rRange.second);
123 if (nDelFlag & InsertDeleteFlags::NOTE)
124 DeleteCellNotes(*pBlockPos, aRange.mnRow1, aRange.mnRow2, false);
126 if (nDelFlag & InsertDeleteFlags::SPARKLINES)
127 DeleteSparklineCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2);
129 if (nDelFlag & InsertDeleteFlags::EDITATTR)
130 RemoveEditAttribs(*pBlockPos, aRange.mnRow1, aRange.mnRow2);
132 if (nDelFlag & InsertDeleteFlags::ATTRIB)
134 pAttrArray->DeleteArea(aRange.mnRow1, aRange.mnRow2);
136 if (rCxt.isTableProtected())
138 ScPatternAttr aPattern(rDocument.GetPool());
139 aPattern.GetItemSet().Put(ScProtectionAttr(false));
140 ApplyPatternArea(aRange.mnRow1, aRange.mnRow2, aPattern);
143 ScConditionalFormatList* pCondList = rCxt.getCondFormatList();
144 if (pCondList)
145 pCondList->DeleteArea(nCol, aRange.mnRow1, nCol, aRange.mnRow2);
147 else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
148 pAttrArray->DeleteHardAttr(aRange.mnRow1, aRange.mnRow2);
150 return;
153 ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
154 SCROW nClipRow1 = aClipRange.aStart.Row();
155 SCROW nClipRow2 = aClipRange.aEnd.Row();
156 SCROW nClipRowLen = nClipRow2 - nClipRow1 + 1;
158 // Check for non-empty cell ranges in the clip column.
159 sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
160 aSpanSet.scan(rClipCol, nClipRow1, nClipRow2);
161 sc::SingleColumnSpanSet::SpansType aSpans;
162 aSpanSet.getSpans(aSpans);
164 if (aSpans.empty())
165 // All cells in the range in the clip are empty. Nothing to delete.
166 return;
168 // Translate the clip column spans into the destination column, and repeat as needed.
169 std::vector<sc::RowSpan> aDestSpans;
170 SCROW nDestOffset = aRange.mnRow1 - nClipRow1;
171 bool bContinue = true;
172 while (bContinue)
174 for (const sc::RowSpan& r : aSpans)
176 SCROW nDestRow1 = r.mnRow1 + nDestOffset;
177 SCROW nDestRow2 = r.mnRow2 + nDestOffset;
179 if (nDestRow1 > aRange.mnRow2)
181 // We're done.
182 bContinue = false;
183 break;
186 if (nDestRow2 > aRange.mnRow2)
188 // Truncate this range, and set it as the last span.
189 nDestRow2 = aRange.mnRow2;
190 bContinue = false;
193 aDestSpans.emplace_back(nDestRow1, nDestRow2);
195 if (!bContinue)
196 break;
199 nDestOffset += nClipRowLen;
202 for (const auto& rDestSpan : aDestSpans)
204 SCROW nRow1 = rDestSpan.mnRow1;
205 SCROW nRow2 = rDestSpan.mnRow2;
207 if (nDelFlag & InsertDeleteFlags::CONTENTS)
209 auto xResult = DeleteCells(*pBlockPos, nRow1, nRow2, nDelFlag);
210 rBroadcastSpans.set(GetDoc(), nTab, nCol, xResult->aDeletedRows, true);
212 for (const auto& rRange : xResult->aFormulaRanges)
213 rCxt.setListeningFormulaSpans(
214 nTab, nCol, rRange.first, nCol, rRange.second);
217 if (nDelFlag & InsertDeleteFlags::NOTE)
218 DeleteCellNotes(*pBlockPos, nRow1, nRow2, false);
220 if (nDelFlag & InsertDeleteFlags::SPARKLINES)
221 DeleteSparklineCells(*pBlockPos, nRow1, nRow2);
223 if (nDelFlag & InsertDeleteFlags::EDITATTR)
224 RemoveEditAttribs(*pBlockPos, nRow1, nRow2);
226 // Delete attributes just now
227 if (nDelFlag & InsertDeleteFlags::ATTRIB)
229 pAttrArray->DeleteArea(nRow1, nRow2);
231 if (rCxt.isTableProtected())
233 ScPatternAttr aPattern(rDocument.GetPool());
234 aPattern.GetItemSet().Put(ScProtectionAttr(false));
235 ApplyPatternArea(nRow1, nRow2, aPattern);
238 ScConditionalFormatList* pCondList = rCxt.getCondFormatList();
239 if (pCondList)
240 pCondList->DeleteArea(nCol, nRow1, nCol, nRow2);
242 else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
243 pAttrArray->DeleteHardAttr(nRow1, nRow2);
247 void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset )
249 assert(nRow1 <= nRow2);
251 size_t nDestSize = nRow2 - nRow1 + 1;
252 sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
253 if (!pBlockPos)
254 return;
256 ScDocument& rDocument = GetDoc();
257 bool bSameDocPool = (rCxt.getClipDoc()->GetPool() == rDocument.GetPool());
259 ScCellValue& rSrcCell = rCxt.getSingleCell(nColOffset);
260 sc::CellTextAttr& rSrcAttr = rCxt.getSingleCellAttr(nColOffset);
262 InsertDeleteFlags nFlags = rCxt.getInsertFlag();
264 if ((nFlags & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE)
266 if (!rCxt.isSkipEmptyCells() || rSrcCell.getType() != CELLTYPE_NONE)
268 const ScPatternAttr* pAttr = (bSameDocPool ? rCxt.getSingleCellPattern(nColOffset) :
269 rCxt.getSingleCellPattern(nColOffset)->PutInPool( &rDocument, rCxt.getClipDoc()));
271 pAttrArray->SetPatternArea(nRow1, nRow2, pAttr, true);
275 if ((nFlags & InsertDeleteFlags::CONTENTS) != InsertDeleteFlags::NONE)
277 std::vector<sc::CellTextAttr> aTextAttrs(nDestSize, rSrcAttr);
279 switch (rSrcCell.getType())
281 case CELLTYPE_VALUE:
283 std::vector<double> aVals(nDestSize, rSrcCell.getDouble());
284 pBlockPos->miCellPos =
285 maCells.set(pBlockPos->miCellPos, nRow1, aVals.begin(), aVals.end());
286 pBlockPos->miCellTextAttrPos =
287 maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
288 CellStorageModified();
290 break;
291 case CELLTYPE_STRING:
293 // Compare the ScDocumentPool* to determine if we are copying within the
294 // same document. If not, re-intern shared strings.
295 svl::SharedStringPool* pSharedStringPool = (bSameDocPool ? nullptr : &rDocument.GetSharedStringPool());
296 svl::SharedString aStr = (pSharedStringPool ?
297 pSharedStringPool->intern( rSrcCell.getSharedString()->getString()) :
298 *rSrcCell.getSharedString());
300 std::vector<svl::SharedString> aStrs(nDestSize, aStr);
301 pBlockPos->miCellPos =
302 maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
303 pBlockPos->miCellTextAttrPos =
304 maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
305 CellStorageModified();
307 break;
308 case CELLTYPE_EDIT:
310 std::vector<EditTextObject*> aStrs;
311 aStrs.reserve(nDestSize);
312 for (size_t i = 0; i < nDestSize; ++i)
313 aStrs.push_back(rSrcCell.getEditText()->Clone().release());
315 pBlockPos->miCellPos =
316 maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
317 pBlockPos->miCellTextAttrPos =
318 maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
319 CellStorageModified();
321 break;
322 case CELLTYPE_FORMULA:
324 std::vector<sc::RowSpan> aRanges;
325 aRanges.reserve(1);
326 aRanges.emplace_back(nRow1, nRow2);
327 CloneFormulaCell(*pBlockPos, *rSrcCell.getFormula(), rSrcAttr, aRanges);
329 break;
330 default:
335 ScAddress aDestPosition(nCol, nRow1, nTab);
337 duplicateSparkline(rCxt, pBlockPos, nColOffset, nDestSize, aDestPosition);
339 // Notes
340 const ScPostIt* pNote = rCxt.getSingleCellNote(nColOffset);
341 if (!(pNote && (nFlags & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE))
342 return;
344 // Duplicate the cell note over the whole pasted range.
346 ScDocument* pClipDoc = rCxt.getClipDoc();
347 const ScAddress aSrcPos = pClipDoc->GetClipParam().getWholeRange().aStart;
348 std::vector<ScPostIt*> aNotes;
349 aNotes.reserve(nDestSize);
350 for (size_t i = 0; i < nDestSize; ++i)
352 bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
353 aNotes.push_back(pNote->Clone(aSrcPos, rDocument, aDestPosition, bCloneCaption).release());
354 aDestPosition.IncRow();
357 pBlockPos->miCellNotePos =
358 maCellNotes.set(
359 pBlockPos->miCellNotePos, nRow1, aNotes.begin(), aNotes.end());
361 // Notify our LOK clients.
362 aDestPosition.SetRow(nRow1);
363 for (size_t i = 0; i < nDestSize; ++i)
365 ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, rDocument, aDestPosition, aNotes[i]);
366 aDestPosition.IncRow();
370 void ScColumn::duplicateSparkline(sc::CopyFromClipContext& rContext, sc::ColumnBlockPosition* pBlockPos,
371 size_t nColOffset, size_t nDestSize, ScAddress aDestPosition)
373 if ((rContext.getInsertFlag() & InsertDeleteFlags::SPARKLINES) == InsertDeleteFlags::NONE)
374 return;
376 auto pSparkline = rContext.getSingleSparkline(nColOffset);
377 if (pSparkline)
379 auto const& pSparklineGroup = pSparkline->getSparklineGroup();
381 auto pDuplicatedGroup = GetDoc().SearchSparklineGroup(pSparklineGroup->getID());
382 if (!pDuplicatedGroup)
383 pDuplicatedGroup = std::make_shared<sc::SparklineGroup>(*pSparklineGroup);
385 std::vector<sc::SparklineCell*> aSparklines(nDestSize, nullptr);
386 ScAddress aCurrentPosition = aDestPosition;
387 for (size_t i = 0; i < nDestSize; ++i)
389 auto pNewSparkline = std::make_shared<sc::Sparkline>(aCurrentPosition.Col(), aCurrentPosition.Row(), pDuplicatedGroup);
390 pNewSparkline->setInputRange(pSparkline->getInputRange());
391 aSparklines[i] = new sc::SparklineCell(pNewSparkline);
392 aCurrentPosition.IncRow();
395 pBlockPos->miSparklinePos = maSparklines.set(pBlockPos->miSparklinePos, aDestPosition.Row(), aSparklines.begin(), aSparklines.end());
399 void ScColumn::SetValues( const SCROW nRow, const std::vector<double>& rVals )
401 if (!GetDoc().ValidRow(nRow))
402 return;
404 SCROW nLastRow = nRow + rVals.size() - 1;
405 if (nLastRow > GetDoc().MaxRow())
406 // Out of bound. Do nothing.
407 return;
409 sc::CellStoreType::position_type aPos = maCells.position(nRow);
410 std::vector<SCROW> aNewSharedRows;
411 DetachFormulaCells(aPos, rVals.size(), &aNewSharedRows);
413 maCells.set(nRow, rVals.begin(), rVals.end());
414 std::vector<sc::CellTextAttr> aDefaults(rVals.size());
415 maCellTextAttrs.set(nRow, aDefaults.begin(), aDefaults.end());
417 CellStorageModified();
419 StartListeningUnshared( aNewSharedRows);
421 std::vector<SCROW> aRows;
422 aRows.reserve(rVals.size());
423 for (SCROW i = nRow; i <= nLastRow; ++i)
424 aRows.push_back(i);
426 BroadcastCells(aRows, SfxHintId::ScDataChanged);
429 void ScColumn::TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rDest )
431 if (!GetDoc().ValidRow(nRow))
432 return;
434 SCROW nLastRow = nRow + nLen - 1;
435 if (nLastRow > GetDoc().MaxRow())
436 // Out of bound. Do nothing.
437 return;
439 sc::CellStoreType::position_type aPos = maCells.position(nRow);
440 DetachFormulaCells(aPos, nLen, nullptr);
442 rDest.transferFrom(*this, nRow, nLen);
444 CellStorageModified();
446 std::vector<SCROW> aRows;
447 aRows.reserve(nLen);
448 for (SCROW i = nRow; i <= nLastRow; ++i)
449 aRows.push_back(i);
451 BroadcastCells(aRows, SfxHintId::ScDataChanged);
454 void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc )
456 if (!GetDoc().ValidRow(nRow))
457 return;
459 SCROW nLastRow = nRow + rSrc.size() - 1;
460 if (nLastRow > GetDoc().MaxRow())
461 // Out of bound. Do nothing
462 return;
464 sc::CellStoreType::position_type aPos = maCells.position(nRow);
465 DetachFormulaCells(aPos, rSrc.size(), nullptr);
467 rSrc.copyTo(*this, nRow);
469 CellStorageModified();
471 std::vector<SCROW> aRows;
472 aRows.reserve(rSrc.size());
473 for (SCROW i = nRow; i <= nLastRow; ++i)
474 aRows.push_back(i);
476 BroadcastCells(aRows, SfxHintId::ScDataChanged);
479 namespace {
481 class ConvertFormulaToValueHandler
483 sc::CellValues maResValues;
484 ScDocument& mrDoc;
485 bool mbModified;
487 public:
488 ConvertFormulaToValueHandler(ScDocument& rDoc) :
489 mrDoc(rDoc),
490 mbModified(false)
492 maResValues.reset(mrDoc.GetSheetLimits().GetMaxRowCount());
495 void operator() ( size_t nRow, const ScFormulaCell* pCell )
497 sc::FormulaResultValue aRes = pCell->GetResult();
498 switch (aRes.meType)
500 case sc::FormulaResultValue::Value:
501 maResValues.setValue(nRow, aRes.mfValue);
502 break;
503 case sc::FormulaResultValue::String:
504 if (aRes.mbMultiLine)
506 std::unique_ptr<EditTextObject> pObj(mrDoc.CreateSharedStringTextObject(aRes.maString));
507 maResValues.setValue(nRow, std::move(pObj));
509 else
511 maResValues.setValue(nRow, aRes.maString);
513 break;
514 case sc::FormulaResultValue::Error:
515 case sc::FormulaResultValue::Invalid:
516 default:
517 maResValues.setValue(nRow, svl::SharedString::getEmptyString());
520 mbModified = true;
523 bool isModified() const { return mbModified; }
525 sc::CellValues& getResValues() { return maResValues; }
530 void ScColumn::ConvertFormulaToValue(
531 sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, sc::TableValues* pUndo )
533 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
534 return;
536 std::vector<SCROW> aBounds { nRow1 };
537 if (nRow2 < GetDoc().MaxRow()-1)
538 aBounds.push_back(nRow2+1);
540 // Split formula cell groups at top and bottom boundaries (if applicable).
541 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
543 // Parse all formulas within the range and store their results into temporary storage.
544 ConvertFormulaToValueHandler aFunc(GetDoc());
545 sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
546 if (!aFunc.isModified())
547 // No formula cells encountered.
548 return;
550 DetachFormulaCells(rCxt, nRow1, nRow2);
552 // Undo storage to hold static values which will get swapped to the cell storage later.
553 sc::CellValues aUndoCells;
554 aFunc.getResValues().swap(aUndoCells);
555 aUndoCells.swapNonEmpty(*this);
556 if (pUndo)
557 pUndo->swap(nTab, nCol, aUndoCells);
560 namespace {
562 class StartListeningHandler
564 sc::StartListeningContext& mrCxt;
566 public:
567 explicit StartListeningHandler( sc::StartListeningContext& rCxt ) :
568 mrCxt(rCxt) {}
570 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
572 pCell->StartListeningTo(mrCxt);
576 class EndListeningHandler
578 sc::EndListeningContext& mrCxt;
580 public:
581 explicit EndListeningHandler( sc::EndListeningContext& rCxt ) :
582 mrCxt(rCxt) {}
584 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
586 pCell->EndListeningTo(mrCxt);
592 void ScColumn::SwapNonEmpty(
593 sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
595 const ScRange& rRange = rValues.getRange();
596 std::vector<SCROW> aBounds { rRange.aStart.Row() };
597 if (rRange.aEnd.Row() < GetDoc().MaxRow()-1)
598 aBounds.push_back(rRange.aEnd.Row()+1);
600 // Split formula cell groups at top and bottom boundaries (if applicable).
601 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
602 std::vector<sc::CellValueSpan> aSpans = rValues.getNonEmptySpans(nTab, nCol);
604 // Detach formula cells within the spans (if any).
605 EndListeningHandler aEndLisFunc(rEndCxt);
606 sc::CellStoreType::iterator itPos = maCells.begin();
607 for (const auto& rSpan : aSpans)
609 SCROW nRow1 = rSpan.mnRow1;
610 SCROW nRow2 = rSpan.mnRow2;
611 itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aEndLisFunc);
614 rValues.swapNonEmpty(nTab, nCol, *this);
615 RegroupFormulaCells();
617 // Attach formula cells within the spans (if any).
618 StartListeningHandler aStartLisFunc(rStartCxt);
619 itPos = maCells.begin();
620 for (const auto& rSpan : aSpans)
622 SCROW nRow1 = rSpan.mnRow1;
623 SCROW nRow2 = rSpan.mnRow2;
624 itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aStartLisFunc);
627 CellStorageModified();
630 void ScColumn::DeleteRanges( const std::vector<sc::RowSpan>& rRanges, InsertDeleteFlags nDelFlag )
632 for (const auto& rSpan : rRanges)
633 DeleteArea(rSpan.mnRow1, rSpan.mnRow2, nDelFlag, false/*bBroadcast*/);
636 void ScColumn::CloneFormulaCell(
637 sc::ColumnBlockPosition& rBlockPos,
638 const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr,
639 const std::vector<sc::RowSpan>& rRanges )
641 SCCOL nMatrixCols = 0;
642 SCROW nMatrixRows = 0;
643 ScMatrixMode nMatrixFlag = rSrc.GetMatrixFlag();
644 if (nMatrixFlag == ScMatrixMode::Formula)
646 rSrc.GetMatColsRows( nMatrixCols, nMatrixRows);
647 SAL_WARN_IF( nMatrixCols != 1 || nMatrixRows != 1, "sc.core",
648 "ScColumn::CloneFormulaCell - cloning array/matrix with not exactly one column or row as single cell");
651 ScDocument& rDocument = GetDoc();
652 std::vector<ScFormulaCell*> aFormulas;
653 for (const auto& rSpan : rRanges)
655 SCROW nRow1 = rSpan.mnRow1, nRow2 = rSpan.mnRow2;
656 size_t nLen = nRow2 - nRow1 + 1;
657 assert(nLen > 0);
658 aFormulas.clear();
659 aFormulas.reserve(nLen);
661 ScAddress aPos(nCol, nRow1, nTab);
663 if (nLen == 1 || !rSrc.GetCode()->IsShareable())
665 // Single, ungrouped formula cell, or create copies for
666 // non-shareable token arrays.
667 for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
669 ScFormulaCell* pCell = new ScFormulaCell(rSrc, rDocument, aPos);
670 aFormulas.push_back(pCell);
673 else
675 // Create a group of formula cells.
676 ScFormulaCellGroupRef xGroup(new ScFormulaCellGroup);
677 xGroup->setCode(*rSrc.GetCode());
678 xGroup->compileCode(rDocument, aPos, rDocument.GetGrammar());
679 for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
681 ScFormulaCell* pCell = new ScFormulaCell(rDocument, aPos, xGroup, rDocument.GetGrammar(), nMatrixFlag);
682 if (nMatrixFlag == ScMatrixMode::Formula)
683 pCell->SetMatColsRows( nMatrixCols, nMatrixRows);
684 if (i == 0)
686 xGroup->mpTopCell = pCell;
687 xGroup->mnLength = nLen;
689 aFormulas.push_back(pCell);
693 rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow1, aFormulas.begin(), aFormulas.end());
695 // Join the top and bottom of the pasted formula cells as needed.
696 sc::CellStoreType::position_type aPosObj = maCells.position(rBlockPos.miCellPos, nRow1);
698 assert(aPosObj.first->type == sc::element_type_formula);
699 ScFormulaCell* pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
700 JoinNewFormulaCell(aPosObj, *pCell);
702 aPosObj = maCells.position(aPosObj.first, nRow2);
703 assert(aPosObj.first->type == sc::element_type_formula);
704 pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
705 JoinNewFormulaCell(aPosObj, *pCell);
707 std::vector<sc::CellTextAttr> aTextAttrs(nLen, rAttr);
708 rBlockPos.miCellTextAttrPos = maCellTextAttrs.set(
709 rBlockPos.miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
712 CellStorageModified();
715 void ScColumn::CloneFormulaCell(
716 const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr,
717 const std::vector<sc::RowSpan>& rRanges )
719 sc::ColumnBlockPosition aBlockPos;
720 InitBlockPosition(aBlockPos);
721 CloneFormulaCell(aBlockPos, rSrc, rAttr, rRanges);
724 std::unique_ptr<ScPostIt> ScColumn::ReleaseNote( SCROW nRow )
726 if (!GetDoc().ValidRow(nRow))
727 return nullptr;
729 ScPostIt* p = nullptr;
730 maCellNotes.release(nRow, p);
731 return std::unique_ptr<ScPostIt>(p);
734 size_t ScColumn::GetNoteCount() const
736 return std::accumulate(maCellNotes.begin(), maCellNotes.end(), size_t(0),
737 [](const size_t& rCount, const auto& rCellNote) {
738 if (rCellNote.type != sc::element_type_cellnote)
739 return rCount;
740 return rCount + rCellNote.size;
744 namespace {
746 class NoteCaptionCreator
748 ScAddress maPos;
749 public:
750 NoteCaptionCreator( SCTAB nTab, SCCOL nCol ) : maPos(nCol,0,nTab) {}
752 void operator() ( size_t nRow, const ScPostIt* p )
754 maPos.SetRow(nRow);
755 p->GetOrCreateCaption(maPos);
759 class NoteCaptionCleaner
761 bool mbPreserveData;
762 public:
763 explicit NoteCaptionCleaner( bool bPreserveData ) : mbPreserveData(bPreserveData) {}
765 void operator() ( size_t /*nRow*/, ScPostIt* p )
767 p->ForgetCaption(mbPreserveData);
773 void ScColumn::CreateAllNoteCaptions()
775 NoteCaptionCreator aFunc(nTab, nCol);
776 sc::ProcessNote(maCellNotes, aFunc);
779 void ScColumn::ForgetNoteCaptions( SCROW nRow1, SCROW nRow2, bool bPreserveData )
781 if (maCellNotes.empty())
782 return;
784 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
785 return;
787 NoteCaptionCleaner aFunc(bPreserveData);
788 sc::CellNoteStoreType::iterator it = maCellNotes.begin();
789 sc::ProcessNote(it, maCellNotes, nRow1, nRow2, aFunc);
792 SCROW ScColumn::GetNotePosition( size_t nIndex ) const
794 // Return the row position of the nth note in the column.
796 size_t nCount = 0; // Number of notes encountered so far.
797 for (const auto& rCellNote : maCellNotes)
799 if (rCellNote.type != sc::element_type_cellnote)
800 // Skip the empty blocks.
801 continue;
803 if (nIndex < nCount + rCellNote.size)
805 // Index falls within this block.
806 size_t nOffset = nIndex - nCount;
807 return rCellNote.position + nOffset;
810 nCount += rCellNote.size;
813 return -1;
816 namespace {
818 class NoteEntryCollector
820 std::vector<sc::NoteEntry>& mrNotes;
821 SCTAB mnTab;
822 SCCOL mnCol;
823 SCROW mnStartRow;
824 SCROW mnEndRow;
825 public:
826 NoteEntryCollector( std::vector<sc::NoteEntry>& rNotes, SCTAB nTab, SCCOL nCol,
827 SCROW nStartRow, SCROW nEndRow) :
828 mrNotes(rNotes), mnTab(nTab), mnCol(nCol),
829 mnStartRow(nStartRow), mnEndRow(nEndRow) {}
831 void operator() (const sc::CellNoteStoreType::value_type& node) const
833 if (node.type != sc::element_type_cellnote)
834 return;
836 size_t nTopRow = node.position;
837 sc::cellnote_block::const_iterator it = sc::cellnote_block::begin(*node.data);
838 sc::cellnote_block::const_iterator itEnd = sc::cellnote_block::end(*node.data);
839 size_t nOffset = 0;
840 if(nTopRow < o3tl::make_unsigned(mnStartRow))
842 std::advance(it, mnStartRow - nTopRow);
843 nOffset = mnStartRow - nTopRow;
846 for (; it != itEnd && nTopRow + nOffset <= o3tl::make_unsigned(mnEndRow);
847 ++it, ++nOffset)
849 ScAddress aPos(mnCol, nTopRow + nOffset, mnTab);
850 mrNotes.emplace_back(aPos, *it);
857 void ScColumn::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
859 if (HasCellNotes())
860 std::for_each(maCellNotes.begin(), maCellNotes.end(),
861 NoteEntryCollector(rNotes, nTab, nCol, 0, GetDoc().MaxRow()));
864 void ScColumn::GetNotesInRange(SCROW nStartRow, SCROW nEndRow,
865 std::vector<sc::NoteEntry>& rNotes ) const
867 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
868 sc::CellNoteStoreType::const_iterator it = aPos.first;
869 if (it == maCellNotes.end())
870 // Invalid row number.
871 return;
873 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos =
874 maCellNotes.position(nEndRow);
875 sc::CellNoteStoreType::const_iterator itEnd = aEndPos.first;
877 std::for_each(it, ++itEnd, NoteEntryCollector(rNotes, nTab, nCol, nStartRow, nEndRow));
880 bool ScColumn::HasCellNote(SCROW nStartRow, SCROW nEndRow) const
882 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aStartPos =
883 maCellNotes.position(nStartRow);
884 if (aStartPos.first == maCellNotes.end())
885 // Invalid row number.
886 return false;
888 std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos =
889 maCellNotes.position(nEndRow);
891 for (sc::CellNoteStoreType::const_iterator it = aStartPos.first; it != aEndPos.first; ++it)
893 if (it->type != sc::element_type_cellnote)
894 continue;
895 size_t nTopRow = it->position;
896 sc::cellnote_block::const_iterator blockIt = sc::cellnote_block::begin(*(it->data));
897 sc::cellnote_block::const_iterator blockItEnd = sc::cellnote_block::end(*(it->data));
898 size_t nOffset = 0;
899 if(nTopRow < o3tl::make_unsigned(nStartRow))
901 std::advance(blockIt, nStartRow - nTopRow);
902 nOffset = nStartRow - nTopRow;
905 if (blockIt != blockItEnd && nTopRow + nOffset <= o3tl::make_unsigned(nEndRow))
906 return true;
909 return false;
912 void ScColumn::GetUnprotectedCells( SCROW nStartRow, SCROW nEndRow, ScRangeList& rRangeList ) const
914 SCROW nTmpStartRow = nStartRow, nTmpEndRow = nEndRow;
915 const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow);
916 bool bProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection();
917 if (!bProtection)
919 // Limit the span to the range in question.
920 if (nTmpStartRow < nStartRow)
921 nTmpStartRow = nStartRow;
922 if (nTmpEndRow > nEndRow)
923 nTmpEndRow = nEndRow;
924 rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab));
926 while (nEndRow > nTmpEndRow)
928 nStartRow = nTmpEndRow + 1;
929 pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow);
930 bool bTmpProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection();
931 if (!bTmpProtection)
933 // Limit the span to the range in question.
934 // Only end row needs to be checked as we enter here only for spans
935 // below the original nStartRow.
936 if (nTmpEndRow > nEndRow)
937 nTmpEndRow = nEndRow;
938 rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab));
943 namespace {
945 class RecompileByOpcodeHandler
947 ScDocument* mpDoc;
948 const formula::unordered_opcode_set& mrOps;
949 sc::EndListeningContext& mrEndListenCxt;
950 sc::CompileFormulaContext& mrCompileFormulaCxt;
952 public:
953 RecompileByOpcodeHandler(
954 ScDocument* pDoc, const formula::unordered_opcode_set& rOps,
955 sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
956 mpDoc(pDoc),
957 mrOps(rOps),
958 mrEndListenCxt(rEndListenCxt),
959 mrCompileFormulaCxt(rCompileCxt) {}
961 void operator() ( sc::FormulaGroupEntry& rEntry )
963 // Perform end listening, remove from formula tree, and set them up
964 // for re-compilation.
966 ScFormulaCell* pTop = nullptr;
968 if (rEntry.mbShared)
970 // Only inspect the code from the top cell.
971 pTop = *rEntry.mpCells;
973 else
974 pTop = rEntry.mpCell;
976 ScTokenArray* pCode = pTop->GetCode();
977 bool bRecompile = pCode->HasOpCodes(mrOps);
979 if (!bRecompile)
980 return;
982 // Get the formula string.
983 OUString aFormula = pTop->GetFormula(mrCompileFormulaCxt);
984 sal_Int32 n = aFormula.getLength();
985 if (pTop->GetMatrixFlag() != ScMatrixMode::NONE && n > 0)
987 if (aFormula[0] == '{' && aFormula[n-1] == '}')
988 aFormula = aFormula.copy(1, n-2);
991 if (rEntry.mbShared)
993 ScFormulaCell** pp = rEntry.mpCells;
994 ScFormulaCell** ppEnd = pp + rEntry.mnLength;
995 for (; pp != ppEnd; ++pp)
997 ScFormulaCell* p = *pp;
998 p->EndListeningTo(mrEndListenCxt);
999 mpDoc->RemoveFromFormulaTree(p);
1002 else
1004 rEntry.mpCell->EndListeningTo(mrEndListenCxt);
1005 mpDoc->RemoveFromFormulaTree(rEntry.mpCell);
1008 pCode->Clear();
1009 pTop->SetHybridFormula(aFormula, mpDoc->GetGrammar());
1013 class CompileHybridFormulaHandler
1015 ScDocument& mrDoc;
1016 sc::StartListeningContext& mrStartListenCxt;
1017 sc::CompileFormulaContext& mrCompileFormulaCxt;
1019 public:
1020 CompileHybridFormulaHandler(ScDocument& rDoc, sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
1021 mrDoc(rDoc),
1022 mrStartListenCxt(rStartListenCxt),
1023 mrCompileFormulaCxt(rCompileCxt) {}
1025 void operator() ( sc::FormulaGroupEntry& rEntry )
1027 if (rEntry.mbShared)
1029 ScFormulaCell* pTop = *rEntry.mpCells;
1030 OUString aFormula = pTop->GetHybridFormula();
1032 if (!aFormula.isEmpty())
1034 // Create a new token array from the hybrid formula string, and
1035 // set it to the group.
1036 ScCompiler aComp(mrCompileFormulaCxt, pTop->aPos);
1037 std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula);
1038 ScFormulaCellGroupRef xGroup = pTop->GetCellGroup();
1039 assert(xGroup);
1040 xGroup->setCode(std::move(*pNewCode));
1041 xGroup->compileCode(mrDoc, pTop->aPos, mrDoc.GetGrammar());
1043 // Propagate the new token array to all formula cells in the group.
1044 ScFormulaCell** pp = rEntry.mpCells;
1045 ScFormulaCell** ppEnd = pp + rEntry.mnLength;
1046 for (; pp != ppEnd; ++pp)
1048 ScFormulaCell* p = *pp;
1049 p->SyncSharedCode();
1050 p->StartListeningTo(mrStartListenCxt);
1051 p->SetDirty();
1055 else
1057 ScFormulaCell* pCell = rEntry.mpCell;
1058 OUString aFormula = pCell->GetHybridFormula();
1060 if (!aFormula.isEmpty())
1062 // Create token array from formula string.
1063 ScCompiler aComp(mrCompileFormulaCxt, pCell->aPos);
1064 std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula);
1066 // Generate RPN tokens.
1067 ScCompiler aComp2(mrDoc, pCell->aPos, *pNewCode, formula::FormulaGrammar::GRAM_UNSPECIFIED,
1068 true, pCell->GetMatrixFlag() != ScMatrixMode::NONE);
1069 aComp2.CompileTokenArray();
1071 pCell->SetCode(std::move(pNewCode));
1072 pCell->StartListeningTo(mrStartListenCxt);
1073 pCell->SetDirty();
1081 void ScColumn::PreprocessRangeNameUpdate(
1082 sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
1084 // Collect all formula groups.
1085 std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
1087 formula::unordered_opcode_set aOps;
1088 aOps.insert(ocBad);
1089 aOps.insert(ocColRowName);
1090 aOps.insert(ocName);
1091 RecompileByOpcodeHandler aFunc(&GetDoc(), aOps, rEndListenCxt, rCompileCxt);
1092 std::for_each(aGroups.begin(), aGroups.end(), aFunc);
1095 void ScColumn::PreprocessDBDataUpdate(
1096 sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
1098 // Collect all formula groups.
1099 std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
1101 formula::unordered_opcode_set aOps;
1102 aOps.insert(ocBad);
1103 aOps.insert(ocColRowName);
1104 aOps.insert(ocDBArea);
1105 aOps.insert(ocTableRef);
1106 RecompileByOpcodeHandler aFunc(&GetDoc(), aOps, rEndListenCxt, rCompileCxt);
1107 std::for_each(aGroups.begin(), aGroups.end(), aFunc);
1110 void ScColumn::CompileHybridFormula(
1111 sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt )
1113 // Collect all formula groups.
1114 std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
1116 CompileHybridFormulaHandler aFunc(GetDoc(), rStartListenCxt, rCompileCxt);
1117 std::for_each(aGroups.begin(), aGroups.end(), aFunc);
1120 namespace {
1122 class ScriptTypeUpdater
1124 ScColumn& mrCol;
1125 sc::CellTextAttrStoreType& mrTextAttrs;
1126 sc::CellTextAttrStoreType::iterator miPosAttr;
1127 ScConditionalFormatList* mpCFList;
1128 SvNumberFormatter* mpFormatter;
1129 ScAddress maPos;
1130 bool mbUpdated;
1132 private:
1133 void updateScriptType( size_t nRow, ScRefCellValue& rCell )
1135 sc::CellTextAttrStoreType::position_type aAttrPos = mrTextAttrs.position(miPosAttr, nRow);
1136 miPosAttr = aAttrPos.first;
1138 if (aAttrPos.first->type != sc::element_type_celltextattr)
1139 return;
1141 sc::CellTextAttr& rAttr = sc::celltextattr_block::at(*aAttrPos.first->data, aAttrPos.second);
1142 if (rAttr.mnScriptType != SvtScriptType::UNKNOWN)
1143 // Script type already determined. Skip it.
1144 return;
1146 const ScPatternAttr* pPat = mrCol.GetPattern(nRow);
1147 if (!pPat)
1148 // In theory this should never return NULL. But let's be safe.
1149 return;
1151 const SfxItemSet* pCondSet = nullptr;
1152 if (mpCFList)
1154 maPos.SetRow(nRow);
1155 const ScCondFormatItem& rItem = pPat->GetItem(ATTR_CONDITIONAL);
1156 const ScCondFormatIndexes& rData = rItem.GetCondFormatData();
1157 pCondSet = mrCol.GetDoc().GetCondResult(rCell, maPos, *mpCFList, rData);
1160 const Color* pColor;
1161 sal_uInt32 nFormat = pPat->GetNumberFormat(mpFormatter, pCondSet);
1162 OUString aStr = ScCellFormat::GetString(rCell, nFormat, &pColor, *mpFormatter, mrCol.GetDoc());
1164 rAttr.mnScriptType = mrCol.GetDoc().GetStringScriptType(aStr);
1165 mbUpdated = true;
1168 public:
1169 explicit ScriptTypeUpdater( ScColumn& rCol ) :
1170 mrCol(rCol),
1171 mrTextAttrs(rCol.GetCellAttrStore()),
1172 miPosAttr(mrTextAttrs.begin()),
1173 mpCFList(rCol.GetDoc().GetCondFormList(rCol.GetTab())),
1174 mpFormatter(rCol.GetDoc().GetFormatTable()),
1175 maPos(rCol.GetCol(), 0, rCol.GetTab()),
1176 mbUpdated(false)
1179 void operator() ( size_t nRow, double fVal )
1181 ScRefCellValue aCell(fVal);
1182 updateScriptType(nRow, aCell);
1185 void operator() ( size_t nRow, const svl::SharedString& rStr )
1187 ScRefCellValue aCell(&rStr);
1188 updateScriptType(nRow, aCell);
1191 void operator() ( size_t nRow, const EditTextObject* pText )
1193 ScRefCellValue aCell(pText);
1194 updateScriptType(nRow, aCell);
1197 void operator() ( size_t nRow, const ScFormulaCell* pCell )
1199 ScRefCellValue aCell(const_cast<ScFormulaCell*>(pCell));
1200 updateScriptType(nRow, aCell);
1203 bool isUpdated() const { return mbUpdated; }
1208 void ScColumn::UpdateScriptTypes( SCROW nRow1, SCROW nRow2 )
1210 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
1211 return;
1213 ScriptTypeUpdater aFunc(*this);
1214 sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
1215 if (aFunc.isUpdated())
1216 CellStorageModified();
1219 void ScColumn::Swap( ScColumn& rOther, SCROW nRow1, SCROW nRow2, bool bPattern )
1221 maCells.swap(nRow1, nRow2, rOther.maCells, nRow1);
1222 maCellTextAttrs.swap(nRow1, nRow2, rOther.maCellTextAttrs, nRow1);
1223 maCellNotes.swap(nRow1, nRow2, rOther.maCellNotes, nRow1);
1224 maBroadcasters.swap(nRow1, nRow2, rOther.maBroadcasters, nRow1);
1226 // Update draw object anchors
1227 ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
1228 if (pDrawLayer)
1230 std::map<SCROW, std::vector<SdrObject*>> aThisColRowDrawObjects
1231 = pDrawLayer->GetObjectsAnchoredToRange(GetTab(), GetCol(), nRow1, nRow2);
1232 std::map<SCROW, std::vector<SdrObject*>> aOtherColRowDrawObjects
1233 = pDrawLayer->GetObjectsAnchoredToRange(GetTab(), rOther.GetCol(), nRow1, nRow2);
1234 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
1236 std::vector<SdrObject*>& rThisCellDrawObjects = aThisColRowDrawObjects[nRow];
1237 if (!rThisCellDrawObjects.empty())
1238 UpdateDrawObjectsForRow(rThisCellDrawObjects, rOther.GetCol(), nRow);
1239 std::vector<SdrObject*>& rOtherCellDrawObjects = aOtherColRowDrawObjects[nRow];
1240 if (!rOtherCellDrawObjects.empty())
1241 rOther.UpdateDrawObjectsForRow(rOtherCellDrawObjects, GetCol(), nRow);
1245 if (bPattern)
1247 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
1249 const ScPatternAttr* pPat1 = GetPattern(nRow);
1250 const ScPatternAttr* pPat2 = rOther.GetPattern(nRow);
1251 if (!SfxPoolItem::areSame(pPat1, pPat2))
1253 if (pPat1->GetRefCount() == 1)
1254 pPat1 = &rOther.GetDoc().GetPool()->DirectPutItemInPool(*pPat1);
1255 SetPattern(nRow, *pPat2);
1256 rOther.SetPattern(nRow, *pPat1);
1261 CellStorageModified();
1262 rOther.CellStorageModified();
1265 namespace {
1267 class FormulaColPosSetter
1269 SCCOL mnCol;
1270 bool mbUpdateRefs;
1271 public:
1272 FormulaColPosSetter( SCCOL nCol, bool bUpdateRefs ) : mnCol(nCol), mbUpdateRefs(bUpdateRefs) {}
1274 void operator() ( size_t nRow, ScFormulaCell* pCell )
1276 if (!pCell->IsShared() || pCell->IsSharedTop())
1278 // Ensure that the references still point to the same locations
1279 // after the position change.
1280 ScAddress aOldPos = pCell->aPos;
1281 pCell->aPos.SetCol(mnCol);
1282 pCell->aPos.SetRow(nRow);
1283 if (mbUpdateRefs)
1284 pCell->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, pCell->aPos);
1285 else
1286 pCell->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, pCell->aPos);
1288 else
1290 pCell->aPos.SetCol(mnCol);
1291 pCell->aPos.SetRow(nRow);
1298 void ScColumn::ResetFormulaCellPositions( SCROW nRow1, SCROW nRow2, bool bUpdateRefs )
1300 FormulaColPosSetter aFunc(nCol, bUpdateRefs);
1301 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
1304 namespace {
1306 class RelativeRefBoundChecker
1308 std::vector<SCROW> maBounds;
1309 ScRange maBoundRange;
1311 public:
1312 explicit RelativeRefBoundChecker( const ScRange& rBoundRange ) :
1313 maBoundRange(rBoundRange) {}
1315 void operator() ( size_t /*nRow*/, ScFormulaCell* pCell )
1317 if (!pCell->IsSharedTop())
1318 return;
1320 pCell->GetCode()->CheckRelativeReferenceBounds(
1321 pCell->aPos, pCell->GetSharedLength(), maBoundRange, maBounds);
1324 void swapBounds( std::vector<SCROW>& rBounds )
1326 rBounds.swap(maBounds);
1332 void ScColumn::SplitFormulaGroupByRelativeRef( const ScRange& rBoundRange )
1334 if (rBoundRange.aStart.Row() >= GetDoc().MaxRow())
1335 // Nothing to split.
1336 return;
1338 std::vector<SCROW> aBounds;
1340 // Cut at row boundaries first.
1341 aBounds.push_back(rBoundRange.aStart.Row());
1342 if (rBoundRange.aEnd.Row() < GetDoc().MaxRow())
1343 aBounds.push_back(rBoundRange.aEnd.Row()+1);
1344 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
1346 RelativeRefBoundChecker aFunc(rBoundRange);
1347 sc::ProcessFormula(
1348 maCells.begin(), maCells, rBoundRange.aStart.Row(), rBoundRange.aEnd.Row(), aFunc);
1349 aFunc.swapBounds(aBounds);
1350 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
1353 namespace {
1355 class ListenerCollector
1357 std::vector<SvtListener*>& mrListeners;
1358 public:
1359 explicit ListenerCollector( std::vector<SvtListener*>& rListener ) :
1360 mrListeners(rListener) {}
1362 void operator() ( size_t /*nRow*/, SvtBroadcaster* p )
1364 SvtBroadcaster::ListenersType& rLis = p->GetAllListeners();
1365 mrListeners.insert(mrListeners.end(), rLis.begin(), rLis.end());
1369 class FormulaCellCollector
1371 std::vector<ScFormulaCell*>& mrCells;
1372 public:
1373 explicit FormulaCellCollector( std::vector<ScFormulaCell*>& rCells ) : mrCells(rCells) {}
1375 void operator() ( size_t /*nRow*/, ScFormulaCell* p )
1377 mrCells.push_back(p);
1383 void ScColumn::CollectListeners( std::vector<SvtListener*>& rListeners, SCROW nRow1, SCROW nRow2 )
1385 if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
1386 return;
1388 ListenerCollector aFunc(rListeners);
1389 sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aFunc);
1392 void ScColumn::CollectFormulaCells( std::vector<ScFormulaCell*>& rCells, SCROW nRow1, SCROW nRow2 )
1394 if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
1395 return;
1397 FormulaCellCollector aFunc(rCells);
1398 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
1401 bool ScColumn::HasFormulaCell() const
1403 return mnBlkCountFormula != 0;
1406 namespace {
1408 struct FindAnyFormula
1410 bool operator() ( size_t /*nRow*/, const ScFormulaCell* /*pCell*/ ) const
1412 return true;
1418 bool ScColumn::HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const
1420 if (!mnBlkCountFormula)
1421 return false;
1423 if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
1424 return false;
1426 if (nRow1 == 0 && nRow2 == GetDoc().MaxRow())
1427 return HasFormulaCell();
1429 FindAnyFormula aFunc;
1430 std::pair<sc::CellStoreType::const_iterator, size_t> aRet =
1431 sc::FindFormula(maCells, nRow1, nRow2, aFunc);
1433 return aRet.first != maCells.end();
1436 namespace {
1438 void endListening( sc::EndListeningContext& rCxt, ScFormulaCell** pp, ScFormulaCell** ppEnd )
1440 for (; pp != ppEnd; ++pp)
1442 ScFormulaCell& rFC = **pp;
1443 rFC.EndListeningTo(rCxt);
1447 class StartListeningFormulaCellsHandler
1449 sc::StartListeningContext& mrStartCxt;
1450 sc::EndListeningContext& mrEndCxt;
1451 SCROW mnStartRow;
1453 public:
1454 StartListeningFormulaCellsHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
1455 mrStartCxt(rStartCxt), mrEndCxt(rEndCxt), mnStartRow(-1) {}
1457 void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
1459 if (node.type != sc::element_type_formula)
1460 // We are only interested in formulas.
1461 return;
1463 mnStartRow = node.position + nOffset;
1465 ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
1466 ScFormulaCell** ppEnd = ppBeg + nDataSize;
1468 ScFormulaCell** pp = ppBeg;
1470 // If the first formula cell belongs to a group and it's not the top
1471 // cell, move up to the top cell of the group, and have all the extra
1472 // formula cells stop listening.
1474 ScFormulaCell* pFC = *pp;
1475 if (pFC->IsShared() && !pFC->IsSharedTop())
1477 SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
1478 if (nBackTrackSize > 0)
1480 assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset);
1481 for (SCROW i = 0; i < nBackTrackSize; ++i)
1482 --pp;
1483 endListening(mrEndCxt, pp, ppBeg);
1484 mnStartRow -= nBackTrackSize;
1488 for (; pp != ppEnd; ++pp)
1490 pFC = *pp;
1492 if (!pFC->IsSharedTop())
1494 assert(!pFC->IsShared());
1495 pFC->StartListeningTo(mrStartCxt);
1496 continue;
1499 // If This is the last group in the range, see if the group
1500 // extends beyond the range, in which case have the excess
1501 // formula cells stop listening.
1502 size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
1503 if (nEndGroupPos > nDataSize)
1505 size_t nExcessSize = nEndGroupPos - nDataSize;
1506 ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
1507 ScFormulaCell** ppGrp = ppGrpEnd - nExcessSize;
1508 endListening(mrEndCxt, ppGrp, ppGrpEnd);
1510 // Register formula cells as a group.
1511 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
1512 pp = ppEnd - 1; // Move to the one before the end position.
1514 else
1516 // Register formula cells as a group.
1517 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
1518 pp += pFC->GetSharedLength() - 1; // Move to the last one in the group.
1525 class EndListeningFormulaCellsHandler
1527 sc::EndListeningContext& mrEndCxt;
1528 SCROW mnStartRow;
1529 SCROW mnEndRow;
1531 public:
1532 explicit EndListeningFormulaCellsHandler( sc::EndListeningContext& rEndCxt ) :
1533 mrEndCxt(rEndCxt), mnStartRow(-1), mnEndRow(-1) {}
1535 void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
1537 if (node.type != sc::element_type_formula)
1538 // We are only interested in formulas.
1539 return;
1541 mnStartRow = node.position + nOffset;
1543 ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
1544 ScFormulaCell** ppEnd = ppBeg + nDataSize;
1546 ScFormulaCell** pp = ppBeg;
1548 // If the first formula cell belongs to a group and it's not the top
1549 // cell, move up to the top cell of the group.
1551 ScFormulaCell* pFC = *pp;
1552 if (pFC->IsShared() && !pFC->IsSharedTop())
1554 SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
1555 if (nBackTrackSize > 0)
1557 assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset);
1558 for (SCROW i = 0; i < nBackTrackSize; ++i)
1559 --pp;
1560 mnStartRow -= nBackTrackSize;
1564 for (; pp != ppEnd; ++pp)
1566 pFC = *pp;
1568 if (!pFC->IsSharedTop())
1570 assert(!pFC->IsShared());
1571 pFC->EndListeningTo(mrEndCxt);
1572 continue;
1575 size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
1576 mnEndRow = node.position + nOffset + nEndGroupPos - 1; // absolute row position of the last one in the group.
1578 ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
1579 endListening(mrEndCxt, pp, ppGrpEnd);
1581 if (nEndGroupPos > nDataSize)
1583 // The group goes beyond the specified end row. Move to the
1584 // one before the end position to finish the loop.
1585 pp = ppEnd - 1;
1587 else
1589 // Move to the last one in the group.
1590 pp += pFC->GetSharedLength() - 1;
1595 SCROW getStartRow() const
1597 return mnStartRow;
1600 SCROW getEndRow() const
1602 return mnEndRow;
1608 void ScColumn::StartListeningFormulaCells(
1609 sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
1610 SCROW nRow1, SCROW nRow2 )
1612 if (!HasFormulaCell())
1613 return;
1615 StartListeningFormulaCellsHandler aFunc(rStartCxt, rEndCxt);
1616 sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
1619 void ScColumn::EndListeningFormulaCells(
1620 sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2,
1621 SCROW* pStartRow, SCROW* pEndRow )
1623 if (!HasFormulaCell())
1624 return;
1626 EndListeningFormulaCellsHandler aFunc(rCxt);
1627 sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
1629 if (pStartRow)
1630 *pStartRow = aFunc.getStartRow();
1632 if (pEndRow)
1633 *pEndRow = aFunc.getEndRow();
1636 void ScColumn::EndListeningIntersectedGroup(
1637 sc::EndListeningContext& rCxt, SCROW nRow, std::vector<ScAddress>* pGroupPos )
1639 if (!GetDoc().ValidRow(nRow))
1640 return;
1642 sc::CellStoreType::position_type aPos = maCells.position(nRow);
1643 sc::CellStoreType::iterator it = aPos.first;
1644 if (it->type != sc::element_type_formula)
1645 // Only interested in a formula block.
1646 return;
1648 ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
1649 ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
1650 if (!xGroup)
1651 // Not a formula group.
1652 return;
1654 // End listening.
1655 pFC->EndListeningTo(rCxt);
1657 if (pGroupPos)
1659 if (!pFC->IsSharedTop())
1660 // Record the position of the top cell of the group.
1661 pGroupPos->push_back(xGroup->mpTopCell->aPos);
1663 SCROW nGrpLastRow = pFC->GetSharedTopRow() + pFC->GetSharedLength() - 1;
1664 if (nRow < nGrpLastRow)
1665 // Record the last position of the group.
1666 pGroupPos->push_back(ScAddress(nCol, nGrpLastRow, nTab));
1670 void ScColumn::EndListeningIntersectedGroups(
1671 sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, std::vector<ScAddress>* pGroupPos )
1673 // Only end the intersected group.
1674 sc::CellStoreType::position_type aPos = maCells.position(nRow1);
1675 sc::CellStoreType::iterator it = aPos.first;
1676 if (it->type == sc::element_type_formula)
1678 ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
1679 ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
1680 if (xGroup)
1682 if (!pFC->IsSharedTop())
1683 // End listening.
1684 pFC->EndListeningTo(rCxt);
1686 if (pGroupPos)
1687 // Record the position of the top cell of the group.
1688 pGroupPos->push_back(xGroup->mpTopCell->aPos);
1692 aPos = maCells.position(it, nRow2);
1693 it = aPos.first;
1694 if (it->type != sc::element_type_formula)
1695 return;
1697 ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
1698 ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
1699 if (!xGroup)
1700 return;
1702 if (!pFC->IsSharedTop())
1703 // End listening.
1704 pFC->EndListeningTo(rCxt);
1706 if (pGroupPos)
1708 // Record the position of the bottom cell of the group.
1709 ScAddress aPosLast = xGroup->mpTopCell->aPos;
1710 aPosLast.IncRow(xGroup->mnLength-1);
1711 pGroupPos->push_back(aPosLast);
1715 void ScColumn::EndListeningGroup( sc::EndListeningContext& rCxt, SCROW nRow )
1717 sc::CellStoreType::position_type aPos = maCells.position(nRow);
1718 if (aPos.first->type != sc::element_type_formula)
1719 // not a formula cell.
1720 return;
1722 ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
1724 ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup();
1725 if (!xGroup)
1727 // not a formula group.
1728 (*pp)->EndListeningTo(rCxt);
1729 return;
1732 // Move back to the top cell.
1733 SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
1734 assert(nTopDelta >= 0);
1735 if (nTopDelta > 0)
1736 pp -= nTopDelta;
1738 // Set the needs listening flag to all cells in the group.
1739 assert(*pp == xGroup->mpTopCell);
1740 ScFormulaCell** ppEnd = pp + xGroup->mnLength;
1741 for (; pp != ppEnd; ++pp)
1742 (*pp)->EndListeningTo(rCxt);
1745 void ScColumn::SetNeedsListeningGroup( SCROW nRow )
1747 sc::CellStoreType::position_type aPos = maCells.position(nRow);
1748 if (aPos.first->type != sc::element_type_formula)
1749 // not a formula cell.
1750 return;
1752 ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
1754 ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup();
1755 if (!xGroup)
1757 // not a formula group.
1758 (*pp)->SetNeedsListening(true);
1759 return;
1762 // Move back to the top cell.
1763 SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
1764 assert(nTopDelta >= 0);
1765 if (nTopDelta > 0)
1766 pp -= nTopDelta;
1768 // Set the needs listening flag to all cells in the group.
1769 assert(*pp == xGroup->mpTopCell);
1770 ScFormulaCell** ppEnd = pp + xGroup->mnLength;
1771 for (; pp != ppEnd; ++pp)
1772 (*pp)->SetNeedsListening(true);
1775 std::optional<sc::ColumnIterator> ScColumn::GetColumnIterator( SCROW nRow1, SCROW nRow2 ) const
1777 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
1778 return {};
1780 return sc::ColumnIterator(maCells, nRow1, nRow2);
1783 static bool lcl_InterpretSpan(sc::formula_block::const_iterator& rSpanIter, SCROW nStartOffset, SCROW nEndOffset,
1784 const ScFormulaCellGroupRef& mxParentGroup, bool& bAllowThreading, ScDocument& rDoc)
1786 bAllowThreading = true;
1787 ScFormulaCell* pCellStart = nullptr;
1788 SCROW nSpanStart = -1;
1789 SCROW nSpanEnd = -1;
1790 sc::formula_block::const_iterator itSpanStart;
1791 bool bAnyDirty = false;
1792 for (SCROW nFGOffset = nStartOffset; nFGOffset <= nEndOffset; ++rSpanIter, ++nFGOffset)
1794 bool bThisDirty = (*rSpanIter)->NeedsInterpret();
1795 if (!pCellStart && bThisDirty)
1797 pCellStart = *rSpanIter;
1798 itSpanStart = rSpanIter;
1799 nSpanStart = nFGOffset;
1800 bAnyDirty = true;
1803 if (pCellStart && (!bThisDirty || nFGOffset == nEndOffset))
1805 nSpanEnd = bThisDirty ? nFGOffset : nFGOffset - 1;
1806 assert(nSpanStart >= nStartOffset && nSpanStart <= nSpanEnd && nSpanEnd <= nEndOffset);
1808 // Found a completely dirty sub span [nSpanStart, nSpanEnd] inside the required span [nStartOffset, nEndOffset]
1809 bool bGroupInterpreted = pCellStart->Interpret(nSpanStart, nSpanEnd);
1811 if (bGroupInterpreted)
1812 for (SCROW nIdx = nSpanStart; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart)
1813 assert(!(*itSpanStart)->NeedsInterpret());
1815 ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper();
1816 // child cell's Interpret could result in calling dependency calc
1817 // and that could detect a cycle involving mxGroup
1818 // and do early exit in that case.
1819 // OR
1820 // this call resulted from a dependency calculation for a multi-formula-group-threading and
1821 // if intergroup dependency is found, return early.
1822 if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent())
1824 bAllowThreading = false;
1825 return bAnyDirty;
1828 if (!bGroupInterpreted)
1830 // Evaluate from second cell in non-grouped style (no point in trying group-interpret again).
1831 ++itSpanStart;
1832 for (SCROW nIdx = nSpanStart+1; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart)
1834 (*itSpanStart)->Interpret(); // We know for sure that this cell is dirty so directly call Interpret().
1835 if ((*itSpanStart)->NeedsInterpret())
1837 SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itSpanStart)->aPos
1838 << " failed running Interpret(), not allowing threading");
1839 bAllowThreading = false;
1840 return bAnyDirty;
1843 // Allow early exit like above.
1844 if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent())
1846 // Set this cell as dirty as this may be interpreted in InterpretTail()
1847 pCellStart->SetDirtyVar();
1848 bAllowThreading = false;
1849 return bAnyDirty;
1854 pCellStart = nullptr; // For next sub span start detection.
1858 return bAnyDirty;
1861 static void lcl_EvalDirty(sc::CellStoreType& rCells, SCROW nRow1, SCROW nRow2, ScDocument& rDoc,
1862 const ScFormulaCellGroupRef& mxGroup, bool bThreadingDepEval, bool bSkipRunning,
1863 bool& bIsDirty, bool& bAllowThreading, ScAddress* pDirtiedAddress)
1865 ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper();
1866 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = rCells.position(nRow1);
1867 sc::CellStoreType::const_iterator it = aPos.first;
1868 size_t nOffset = aPos.second;
1869 SCROW nRow = nRow1;
1871 bIsDirty = false;
1873 for (;it != rCells.end() && nRow <= nRow2; ++it, nOffset = 0)
1875 switch( it->type )
1877 case sc::element_type_edittext:
1878 // These require EditEngine (in ScEditUtils::GetString()), which is probably
1879 // too complex for use in threads.
1880 if (bThreadingDepEval)
1882 bAllowThreading = false;
1883 return;
1885 break;
1886 case sc::element_type_formula:
1888 size_t nRowsToRead = nRow2 - nRow + 1;
1889 const size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1
1890 sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
1891 std::advance(itCell, nOffset);
1893 // Loop inside the formula block.
1894 size_t nCellIdx = nOffset;
1895 while (nCellIdx < nEnd)
1897 const ScFormulaCellGroupRef& mxGroupChild = (*itCell)->GetCellGroup();
1898 ScFormulaCell* pChildTopCell = mxGroupChild ? mxGroupChild->mpTopCell : *itCell;
1900 // Check if itCell is already in path.
1901 // If yes use a cycle guard to mark all elements of the cycle
1902 // and return false
1903 if (bThreadingDepEval && pChildTopCell->GetSeenInPath())
1905 ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pChildTopCell);
1906 bAllowThreading = false;
1907 return;
1910 if (bSkipRunning && (*itCell)->IsRunning())
1912 ++itCell;
1913 nCellIdx += 1;
1914 nRow += 1;
1915 nRowsToRead -= 1;
1916 continue;
1919 if (mxGroupChild)
1921 // It is a Formula-group, evaluate the necessary parts of it (spans).
1922 const SCROW nFGStartOffset = (*itCell)->aPos.Row() - pChildTopCell->aPos.Row();
1923 const SCROW nFGEndOffset = std::min(nFGStartOffset + static_cast<SCROW>(nRowsToRead) - 1, mxGroupChild->mnLength - 1);
1924 assert(nFGEndOffset >= nFGStartOffset);
1925 const SCROW nSpanLen = nFGEndOffset - nFGStartOffset + 1;
1926 // The (main) span required to be evaluated is [nFGStartOffset, nFGEndOffset], but this span may contain
1927 // non-dirty cells, so split this into sets of completely-dirty spans and try evaluate each of them in grouped-style.
1929 bool bAnyDirtyInSpan = lcl_InterpretSpan(itCell, nFGStartOffset, nFGEndOffset, mxGroup, bAllowThreading, rDoc);
1930 if (!bAllowThreading)
1931 return;
1932 // itCell will now point to cell just after the end of span [nFGStartOffset, nFGEndOffset].
1933 bIsDirty = bIsDirty || bAnyDirtyInSpan;
1935 // update the counters by nSpanLen.
1936 // itCell already got updated.
1937 nCellIdx += nSpanLen;
1938 nRow += nSpanLen;
1939 nRowsToRead -= nSpanLen;
1941 else
1943 // No formula-group here.
1944 bool bDirtyFlag = false;
1945 if( (*itCell)->NeedsInterpret())
1947 bDirtyFlag = true;
1948 (*itCell)->Interpret();
1949 if ((*itCell)->NeedsInterpret())
1951 SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itCell)->aPos
1952 << " failed running Interpret(), not allowing threading");
1953 bAllowThreading = false;
1954 return;
1957 bIsDirty = bIsDirty || bDirtyFlag;
1959 // child cell's Interpret could result in calling dependency calc
1960 // and that could detect a cycle involving mxGroup
1961 // and do early exit in that case.
1962 // OR
1963 // we are trying multi-formula-group-threading, but found intergroup dependency.
1964 if (bThreadingDepEval && mxGroup &&
1965 (mxGroup->mbPartOfCycle || !rRecursionHelper.AreGroupsIndependent()))
1967 // Set itCell as dirty as itCell may be interpreted in InterpretTail()
1968 (*itCell)->SetDirtyVar();
1969 if (pDirtiedAddress)
1970 pDirtiedAddress->SetRow(nRow);
1971 bAllowThreading = false;
1972 return;
1975 // update the counters by 1.
1976 nCellIdx += 1;
1977 nRow += 1;
1978 nRowsToRead -= 1;
1979 ++itCell;
1982 break;
1984 default:
1985 // Skip this block.
1986 nRow += it->size - nOffset;
1987 continue;
1991 if (bThreadingDepEval)
1992 bAllowThreading = true;
1996 // Returns true if at least one FC is dirty.
1997 bool ScColumn::EnsureFormulaCellResults( SCROW nRow1, SCROW nRow2, bool bSkipRunning )
1999 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
2000 return false;
2002 if (!HasFormulaCell(nRow1, nRow2))
2003 return false;
2005 bool bAnyDirty = false, bTmp = false;
2006 lcl_EvalDirty(maCells, nRow1, nRow2, GetDoc(), nullptr, false, bSkipRunning, bAnyDirty, bTmp, nullptr);
2007 return bAnyDirty;
2010 bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1, SCROW nRow2, const ScFormulaCellGroupRef& mxGroup, ScAddress* pDirtiedAddress )
2012 if (nRow1 > nRow2)
2013 return false;
2015 bool bAllowThreading = true, bTmp = false;
2016 lcl_EvalDirty(maCells, nRow1, nRow2, GetDoc(), mxGroup, true, false, bTmp, bAllowThreading, pDirtiedAddress);
2018 return bAllowThreading;
2021 namespace {
2023 class StoreToCacheFunc
2025 SvStream& mrStrm;
2026 public:
2028 StoreToCacheFunc(SvStream& rStrm):
2029 mrStrm(rStrm)
2033 void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
2035 SCROW nStartRow = node.position + nOffset;
2036 mrStrm.WriteUInt64(nStartRow);
2037 mrStrm.WriteUInt64(nDataSize);
2038 switch (node.type)
2040 case sc::element_type_empty:
2042 mrStrm.WriteUChar(0);
2044 break;
2045 case sc::element_type_numeric:
2047 mrStrm.WriteUChar(1);
2048 sc::numeric_block::const_iterator it = sc::numeric_block::begin(*node.data);
2049 std::advance(it, nOffset);
2050 sc::numeric_block::const_iterator itEnd = it;
2051 std::advance(itEnd, nDataSize);
2053 for (; it != itEnd; ++it)
2055 mrStrm.WriteDouble(*it);
2058 break;
2059 case sc::element_type_string:
2061 mrStrm.WriteUChar(2);
2062 sc::string_block::const_iterator it = sc::string_block::begin(*node.data);
2063 std::advance(it, nOffset);
2064 sc::string_block::const_iterator itEnd = it;
2065 std::advance(itEnd, nDataSize);
2067 for (; it != itEnd; ++it)
2069 OString aStr = OUStringToOString(it->getString(), RTL_TEXTENCODING_UTF8);
2070 sal_Int32 nStrLength = aStr.getLength();
2071 mrStrm.WriteInt32(nStrLength);
2072 mrStrm.WriteOString(aStr);
2075 break;
2076 case sc::element_type_formula:
2078 mrStrm.WriteUChar(3);
2079 sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
2080 std::advance(it, nOffset);
2081 sc::formula_block::const_iterator itEnd = it;
2082 std::advance(itEnd, nDataSize);
2084 for (; it != itEnd; /* incrementing through std::advance*/)
2086 const ScFormulaCell* pCell = *it;
2087 OUString aFormula = pCell->GetFormula(formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
2088 const auto& xCellGroup = pCell->GetCellGroup();
2089 sal_uInt64 nGroupLength = 0;
2090 if (xCellGroup)
2092 nGroupLength = xCellGroup->mnLength;
2094 else
2096 nGroupLength = 1;
2098 mrStrm.WriteUInt64(nGroupLength);
2099 mrStrm.WriteInt32(aFormula.getLength());
2100 mrStrm.WriteOString(OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8));
2102 // incrementing the iterator
2103 std::advance(it, nGroupLength);
2106 break;
2113 void ScColumn::StoreToCache(SvStream& rStrm) const
2115 rStrm.WriteUInt64(nCol);
2116 SCROW nLastRow = GetLastDataPos();
2117 rStrm.WriteUInt64(nLastRow + 1); // the rows are zero based
2119 StoreToCacheFunc aFunc(rStrm);
2120 sc::ParseBlock(maCells.begin(), maCells, aFunc, SCROW(0), nLastRow);
2123 void ScColumn::RestoreFromCache(SvStream& rStrm)
2125 sal_uInt64 nStoredCol = 0;
2126 rStrm.ReadUInt64(nStoredCol);
2127 if (nStoredCol != static_cast<sal_uInt64>(nCol))
2128 throw std::exception();
2130 sal_uInt64 nLastRow = 0;
2131 rStrm.ReadUInt64(nLastRow);
2132 sal_uInt64 nReadRow = 0;
2133 ScDocument& rDocument = GetDoc();
2134 while (nReadRow < nLastRow)
2136 sal_uInt64 nStartRow = 0;
2137 sal_uInt64 nDataSize = 0;
2138 rStrm.ReadUInt64(nStartRow);
2139 rStrm.ReadUInt64(nDataSize);
2140 sal_uInt8 nType = 0;
2141 rStrm.ReadUChar(nType);
2142 switch (nType)
2144 case 0:
2145 // nothing to do
2146 maCells.set_empty(nStartRow, nDataSize);
2147 break;
2148 case 1:
2150 // nDataSize double values
2151 std::vector<double> aValues(nDataSize);
2152 for (auto& rValue : aValues)
2154 rStrm.ReadDouble(rValue);
2156 maCells.set(nStartRow, aValues.begin(), aValues.end());
2158 break;
2159 case 2:
2161 std::vector<svl::SharedString> aStrings(nDataSize);
2162 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
2163 for (auto& rString : aStrings)
2165 sal_Int32 nStrLength = 0;
2166 rStrm.ReadInt32(nStrLength);
2167 std::unique_ptr<char[]> pStr(new char[nStrLength]);
2168 rStrm.ReadBytes(pStr.get(), nStrLength);
2169 std::string_view aOStr(pStr.get(), nStrLength);
2170 OUString aStr = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8);
2171 rString = rPool.intern(aStr);
2173 maCells.set(nStartRow, aStrings.begin(), aStrings.end());
2176 break;
2177 case 3:
2179 std::vector<ScFormulaCell*> aFormulaCells(nDataSize);
2181 ScAddress aAddr(nCol, nStartRow, nTab);
2182 const formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
2183 for (SCROW nRow = 0; nRow < static_cast<SCROW>(nDataSize);)
2185 sal_uInt64 nFormulaGroupSize = 0;
2186 rStrm.ReadUInt64(nFormulaGroupSize);
2187 sal_Int32 nStrLength = 0;
2188 rStrm.ReadInt32(nStrLength);
2189 std::unique_ptr<char[]> pStr(new char[nStrLength]);
2190 rStrm.ReadBytes(pStr.get(), nStrLength);
2191 std::string_view aOStr(pStr.get(), nStrLength);
2192 OUString aStr = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8);
2193 for (sal_uInt64 i = 0; i < nFormulaGroupSize; ++i)
2195 aFormulaCells[nRow + i] = new ScFormulaCell(rDocument, aAddr, aStr, eGrammar);
2196 aAddr.IncRow();
2199 nRow += nFormulaGroupSize;
2202 maCells.set(nStartRow, aFormulaCells.begin(), aFormulaCells.end());
2204 break;
2207 nReadRow += nDataSize;
2211 void ScColumn::CheckIntegrity() const
2213 auto checkEventHandlerColumnRef = [this](const auto& rStore, std::string_view pStoreName)
2215 if (const ScColumn* pColTest = rStore.event_handler().getColumn(); pColTest != this)
2217 std::ostringstream os;
2218 os << pStoreName << "'s event handler references wrong column instance (this=" << this
2219 << "; stored=" << pColTest << ")";
2220 throw std::runtime_error(os.str());
2224 auto countBlocks = [](const auto& rStore, mdds::mtv::element_t nBlockType)
2226 std::size_t nCount = std::count_if(rStore.cbegin(), rStore.cend(),
2227 [nBlockType](const auto& blk) { return blk.type == nBlockType; }
2230 return nCount;
2233 auto checkCachedBlockCount = [countBlocks](
2234 const auto& rStore, mdds::mtv::element_t nBlockType, std::size_t nCachedBlkCount,
2235 std::string_view pName)
2237 std::size_t nCount = countBlocks(rStore, nBlockType);
2239 if (nCachedBlkCount != nCount)
2241 std::ostringstream os;
2242 os << "incorrect cached " << pName << " block count (expected=" << nCount << "; actual="
2243 << nCachedBlkCount << ")";
2244 throw std::runtime_error(os.str());
2248 checkEventHandlerColumnRef(maCells, "cell store");
2249 checkEventHandlerColumnRef(maCellNotes, "cell-note store");
2251 checkCachedBlockCount(maCells, sc::element_type_formula, mnBlkCountFormula, "formula");
2252 checkCachedBlockCount(maCellNotes, sc::element_type_cellnote, mnBlkCountCellNotes, "cell note");
2255 void ScColumn::CollectBroadcasterState(sc::BroadcasterState& rState) const
2257 for (const auto& block : maBroadcasters)
2259 if (block.type != sc::element_type_broadcaster)
2260 continue;
2262 auto itBeg = sc::broadcaster_block::begin(*block.data);
2263 auto itEnd = sc::broadcaster_block::end(*block.data);
2265 for (auto it = itBeg; it != itEnd; ++it)
2267 ScAddress aBCPos(nCol, block.position + std::distance(itBeg, it), nTab);
2269 auto aRes = rState.aCellListenerStore.try_emplace(aBCPos);
2270 auto& rLisStore = aRes.first->second;
2272 const SvtBroadcaster& rBC = **it;
2273 for (const SvtListener* pLis : rBC.GetAllListeners())
2275 const auto* pFC = dynamic_cast<const ScFormulaCell*>(pLis);
2276 if (pFC)
2277 rLisStore.emplace_back(pFC);
2278 else
2279 rLisStore.emplace_back(pLis);
2285 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */