xref: /core/sc/source/core/data/column4.cxx (revision 1a7913c1)
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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  */
9 
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 
34 #include <SparklineGroup.hxx>
35 
36 #include <o3tl/safeint.hxx>
37 #include <svl/sharedstringpool.hxx>
38 #include <sal/log.hxx>
39 #include <tools/stream.hxx>
40 
41 #include <numeric>
42 #include <vector>
43 #include <cassert>
44 
45 sc::MultiDataCellState::StateType ScColumn::HasDataCellsInRange(
46     SCROW nRow1, SCROW nRow2, SCROW* pRow1 ) const
47 {
48     sc::CellStoreType::const_position_type aPos = maCells.position(nRow1);
49     sc::CellStoreType::const_iterator it = aPos.first;
50     size_t nOffset = aPos.second;
51     SCROW nRow = nRow1;
52     bool bHasOne = false; // whether or not we have found a non-empty block of size one.
53 
54     for (; it != maCells.end() && nRow <= nRow2; ++it)
55     {
56         if (it->type != sc::element_type_empty)
57         {
58             // non-empty block found.
59             assert(it->size > 0); // mtv should never contain a block of zero length.
60             size_t nSize = it->size - nOffset;
61 
62             SCROW nLastRow = nRow + nSize - 1;
63             if (nLastRow > nRow2)
64                 // shrink the size to avoid exceeding the specified last row position.
65                 nSize -= nLastRow - nRow2;
66 
67             if (nSize == 1)
68             {
69                 // this block is of size one.
70                 if (bHasOne)
71                     return sc::MultiDataCellState::HasMultipleCells;
72 
73                 bHasOne = true;
74                 if (pRow1)
75                     *pRow1 = nRow;
76             }
77             else
78             {
79                 // size of this block is greater than one.
80                 if (pRow1)
81                     *pRow1 = nRow;
82                 return sc::MultiDataCellState::HasMultipleCells;
83             }
84         }
85 
86         nRow += it->size - nOffset;
87         nOffset = 0;
88     }
89 
90     return bHasOne ? sc::MultiDataCellState::HasOneCell : sc::MultiDataCellState::Empty;
91 }
92 
93 void ScColumn::DeleteBeforeCopyFromClip(
94     sc::CopyFromClipContext& rCxt, const ScColumn& rClipCol, sc::ColumnSpanSet& rBroadcastSpans )
95 {
96     ScDocument& rDocument = GetDoc();
97     sc::CopyFromClipContext::Range aRange = rCxt.getDestRange();
98     if (!rDocument.ValidRow(aRange.mnRow1) || !rDocument.ValidRow(aRange.mnRow2))
99         return;
100 
101     sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
102     if (!pBlockPos)
103         return;
104 
105     InsertDeleteFlags nDelFlag = rCxt.getDeleteFlag();
106 
107     if (!rCxt.isSkipEmptyCells())
108     {
109         // Delete the whole destination range.
110 
111         if (nDelFlag & InsertDeleteFlags::CONTENTS)
112         {
113             sc::SingleColumnSpanSet aDeletedRows(GetDoc().GetSheetLimits());
114             DeleteCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2, nDelFlag, aDeletedRows);
115             rBroadcastSpans.set(GetDoc(), nTab, nCol, aDeletedRows, true);
116         }
117 
118         if (nDelFlag & InsertDeleteFlags::NOTE)
119             DeleteCellNotes(*pBlockPos, aRange.mnRow1, aRange.mnRow2, false);
120 
121         if (nDelFlag & InsertDeleteFlags::SPARKLINES)
122             DeleteSparklineCells(*pBlockPos, aRange.mnRow1, aRange.mnRow2);
123 
124         if (nDelFlag & InsertDeleteFlags::EDITATTR)
125             RemoveEditAttribs(*pBlockPos, aRange.mnRow1, aRange.mnRow2);
126 
127         if (nDelFlag & InsertDeleteFlags::ATTRIB)
128         {
129             pAttrArray->DeleteArea(aRange.mnRow1, aRange.mnRow2);
130 
131             if (rCxt.isTableProtected())
132             {
133                 ScPatternAttr aPattern(rDocument.GetPool());
134                 aPattern.GetItemSet().Put(ScProtectionAttr(false));
135                 ApplyPatternArea(aRange.mnRow1, aRange.mnRow2, aPattern);
136             }
137 
138             ScConditionalFormatList* pCondList = rCxt.getCondFormatList();
139             if (pCondList)
140                 pCondList->DeleteArea(nCol, aRange.mnRow1, nCol, aRange.mnRow2);
141         }
142         else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
143             pAttrArray->DeleteHardAttr(aRange.mnRow1, aRange.mnRow2);
144 
145         return;
146     }
147 
148     ScRange aClipRange = rCxt.getClipDoc()->GetClipParam().getWholeRange();
149     SCROW nClipRow1 = aClipRange.aStart.Row();
150     SCROW nClipRow2 = aClipRange.aEnd.Row();
151     SCROW nClipRowLen = nClipRow2 - nClipRow1 + 1;
152 
153     // Check for non-empty cell ranges in the clip column.
154     sc::SingleColumnSpanSet aSpanSet(GetDoc().GetSheetLimits());
155     aSpanSet.scan(rClipCol, nClipRow1, nClipRow2);
156     sc::SingleColumnSpanSet::SpansType aSpans;
157     aSpanSet.getSpans(aSpans);
158 
159     if (aSpans.empty())
160         // All cells in the range in the clip are empty.  Nothing to delete.
161         return;
162 
163     // Translate the clip column spans into the destination column, and repeat as needed.
164     std::vector<sc::RowSpan> aDestSpans;
165     SCROW nDestOffset = aRange.mnRow1 - nClipRow1;
166     bool bContinue = true;
167     while (bContinue)
168     {
169         for (const sc::RowSpan& r : aSpans)
170         {
171             SCROW nDestRow1 = r.mnRow1 + nDestOffset;
172             SCROW nDestRow2 = r.mnRow2 + nDestOffset;
173 
174             if (nDestRow1 > aRange.mnRow2)
175             {
176                 // We're done.
177                 bContinue = false;
178                 break;
179             }
180 
181             if (nDestRow2 > aRange.mnRow2)
182             {
183                 // Truncate this range, and set it as the last span.
184                 nDestRow2 = aRange.mnRow2;
185                 bContinue = false;
186             }
187 
188             aDestSpans.emplace_back(nDestRow1, nDestRow2);
189 
190             if (!bContinue)
191                 break;
192         }
193 
194         nDestOffset += nClipRowLen;
195     }
196 
197     for (const auto& rDestSpan : aDestSpans)
198     {
199         SCROW nRow1 = rDestSpan.mnRow1;
200         SCROW nRow2 = rDestSpan.mnRow2;
201 
202         if (nDelFlag & InsertDeleteFlags::CONTENTS)
203         {
204             sc::SingleColumnSpanSet aDeletedRows(GetDoc().GetSheetLimits());
205             DeleteCells(*pBlockPos, nRow1, nRow2, nDelFlag, aDeletedRows);
206             rBroadcastSpans.set(GetDoc(), nTab, nCol, aDeletedRows, true);
207         }
208 
209         if (nDelFlag & InsertDeleteFlags::NOTE)
210             DeleteCellNotes(*pBlockPos, nRow1, nRow2, false);
211 
212         if (nDelFlag & InsertDeleteFlags::SPARKLINES)
213             DeleteSparklineCells(*pBlockPos, nRow1, nRow2);
214 
215         if (nDelFlag & InsertDeleteFlags::EDITATTR)
216             RemoveEditAttribs(*pBlockPos, nRow1, nRow2);
217 
218         // Delete attributes just now
219         if (nDelFlag & InsertDeleteFlags::ATTRIB)
220         {
221             pAttrArray->DeleteArea(nRow1, nRow2);
222 
223             if (rCxt.isTableProtected())
224             {
225                 ScPatternAttr aPattern(rDocument.GetPool());
226                 aPattern.GetItemSet().Put(ScProtectionAttr(false));
227                 ApplyPatternArea(nRow1, nRow2, aPattern);
228             }
229 
230             ScConditionalFormatList* pCondList = rCxt.getCondFormatList();
231             if (pCondList)
232                 pCondList->DeleteArea(nCol, nRow1, nCol, nRow2);
233         }
234         else if ((nDelFlag & InsertDeleteFlags::HARDATTR) == InsertDeleteFlags::HARDATTR)
235             pAttrArray->DeleteHardAttr(nRow1, nRow2);
236     }
237 }
238 
239 void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext& rCxt, SCROW nRow1, SCROW nRow2, size_t nColOffset )
240 {
241     assert(nRow1 <= nRow2);
242 
243     size_t nDestSize = nRow2 - nRow1 + 1;
244     sc::ColumnBlockPosition* pBlockPos = rCxt.getBlockPosition(nTab, nCol);
245     if (!pBlockPos)
246         return;
247 
248     ScDocument& rDocument = GetDoc();
249     bool bSameDocPool = (rCxt.getClipDoc()->GetPool() == rDocument.GetPool());
250 
251     ScCellValue& rSrcCell = rCxt.getSingleCell(nColOffset);
252     sc::CellTextAttr& rSrcAttr = rCxt.getSingleCellAttr(nColOffset);
253 
254     InsertDeleteFlags nFlags = rCxt.getInsertFlag();
255 
256     if ((nFlags & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE)
257     {
258         if (!rCxt.isSkipEmptyCells() || rSrcCell.getType() != CELLTYPE_NONE)
259         {
260             const ScPatternAttr* pAttr = (bSameDocPool ? rCxt.getSingleCellPattern(nColOffset) :
261                     rCxt.getSingleCellPattern(nColOffset)->PutInPool( &rDocument, rCxt.getClipDoc()));
262 
263             auto pNewPattern = std::make_unique<ScPatternAttr>(*pAttr);
264             sal_uInt16 pItems[2];
265             pItems[0] = ATTR_CONDITIONAL;
266             pItems[1] = 0;
267             pNewPattern->ClearItems(pItems);
268             pAttrArray->SetPatternArea(nRow1, nRow2, std::move(pNewPattern), true);
269         }
270     }
271 
272     if ((nFlags & InsertDeleteFlags::CONTENTS) != InsertDeleteFlags::NONE)
273     {
274         std::vector<sc::CellTextAttr> aTextAttrs(nDestSize, rSrcAttr);
275 
276         switch (rSrcCell.getType())
277         {
278             case CELLTYPE_VALUE:
279             {
280                 std::vector<double> aVals(nDestSize, rSrcCell.getDouble());
281                 pBlockPos->miCellPos =
282                     maCells.set(pBlockPos->miCellPos, nRow1, aVals.begin(), aVals.end());
283                 pBlockPos->miCellTextAttrPos =
284                     maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
285                 CellStorageModified();
286             }
287             break;
288             case CELLTYPE_STRING:
289             {
290                 // Compare the ScDocumentPool* to determine if we are copying within the
291                 // same document. If not, re-intern shared strings.
292                 svl::SharedStringPool* pSharedStringPool = (bSameDocPool ? nullptr : &rDocument.GetSharedStringPool());
293                 svl::SharedString aStr = (pSharedStringPool ?
294                         pSharedStringPool->intern( rSrcCell.getSharedString()->getString()) :
295                         *rSrcCell.getSharedString());
296 
297                 std::vector<svl::SharedString> aStrs(nDestSize, aStr);
298                 pBlockPos->miCellPos =
299                     maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
300                 pBlockPos->miCellTextAttrPos =
301                     maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
302                 CellStorageModified();
303             }
304             break;
305             case CELLTYPE_EDIT:
306             {
307                 std::vector<EditTextObject*> aStrs;
308                 aStrs.reserve(nDestSize);
309                 for (size_t i = 0; i < nDestSize; ++i)
310                     aStrs.push_back(rSrcCell.getEditText()->Clone().release());
311 
312                 pBlockPos->miCellPos =
313                     maCells.set(pBlockPos->miCellPos, nRow1, aStrs.begin(), aStrs.end());
314                 pBlockPos->miCellTextAttrPos =
315                     maCellTextAttrs.set(pBlockPos->miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
316                 CellStorageModified();
317             }
318             break;
319             case CELLTYPE_FORMULA:
320             {
321                 std::vector<sc::RowSpan> aRanges;
322                 aRanges.reserve(1);
323                 aRanges.emplace_back(nRow1, nRow2);
324                 CloneFormulaCell(*pBlockPos, *rSrcCell.getFormula(), rSrcAttr, aRanges);
325             }
326             break;
327             default:
328                 ;
329         }
330     }
331 
332     ScAddress aDestPosition(nCol, nRow1, nTab);
333 
334     duplicateSparkline(rCxt, pBlockPos, nColOffset, nDestSize, aDestPosition);
335 
336     // Notes
337     const ScPostIt* pNote = rCxt.getSingleCellNote(nColOffset);
338     if (!(pNote && (nFlags & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE))
339         return;
340 
341     // Duplicate the cell note over the whole pasted range.
342 
343     ScDocument* pClipDoc = rCxt.getClipDoc();
344     const ScAddress aSrcPos = pClipDoc->GetClipParam().getWholeRange().aStart;
345     std::vector<ScPostIt*> aNotes;
346     aNotes.reserve(nDestSize);
347     for (size_t i = 0; i < nDestSize; ++i)
348     {
349         bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
350         aNotes.push_back(pNote->Clone(aSrcPos, rDocument, aDestPosition, bCloneCaption).release());
351         aDestPosition.IncRow();
352     }
353 
354     pBlockPos->miCellNotePos =
355         maCellNotes.set(
356             pBlockPos->miCellNotePos, nRow1, aNotes.begin(), aNotes.end());
357 
358     // Notify our LOK clients.
359     aDestPosition.SetRow(nRow1);
360     for (size_t i = 0; i < nDestSize; ++i)
361     {
362         ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add, &rDocument, aDestPosition, aNotes[i]);
363         aDestPosition.IncRow();
364     }
365 }
366 
367 void ScColumn::duplicateSparkline(sc::CopyFromClipContext& rContext, sc::ColumnBlockPosition* pBlockPos,
368                                   size_t nColOffset, size_t nDestSize, ScAddress aDestPosition)
369 {
370     if ((rContext.getInsertFlag() & InsertDeleteFlags::SPARKLINES) == InsertDeleteFlags::NONE)
371         return;
372 
373     auto pSparkline = rContext.getSingleSparkline(nColOffset);
374     if (pSparkline)
375     {
376         auto const& pSparklineGroup = pSparkline->getSparklineGroup();
377 
378         auto pDuplicatedGroup = GetDoc().SearchSparklineGroup(pSparklineGroup->getID());
379         if (!pDuplicatedGroup)
380             pDuplicatedGroup = std::make_shared<sc::SparklineGroup>(*pSparklineGroup);
381 
382         std::vector<sc::SparklineCell*> aSparklines(nDestSize, nullptr);
383         ScAddress aCurrentPosition = aDestPosition;
384         for (size_t i = 0; i < nDestSize; ++i)
385         {
386             auto pNewSparkline = std::make_shared<sc::Sparkline>(aCurrentPosition.Col(), aCurrentPosition.Row(), pDuplicatedGroup);
387             pNewSparkline->setInputRange(pSparkline->getInputRange());
388             aSparklines[i] = new sc::SparklineCell(pNewSparkline);
389             aCurrentPosition.IncRow();
390         }
391 
392         pBlockPos->miSparklinePos = maSparklines.set(pBlockPos->miSparklinePos, aDestPosition.Row(), aSparklines.begin(), aSparklines.end());
393     }
394 }
395 
396 void ScColumn::SetValues( const SCROW nRow, const std::vector<double>& rVals )
397 {
398     if (!GetDoc().ValidRow(nRow))
399         return;
400 
401     SCROW nLastRow = nRow + rVals.size() - 1;
402     if (nLastRow > GetDoc().MaxRow())
403         // Out of bound. Do nothing.
404         return;
405 
406     sc::CellStoreType::position_type aPos = maCells.position(nRow);
407     std::vector<SCROW> aNewSharedRows;
408     DetachFormulaCells(aPos, rVals.size(), &aNewSharedRows);
409 
410     maCells.set(nRow, rVals.begin(), rVals.end());
411     std::vector<sc::CellTextAttr> aDefaults(rVals.size());
412     maCellTextAttrs.set(nRow, aDefaults.begin(), aDefaults.end());
413 
414     CellStorageModified();
415 
416     StartListeningUnshared( aNewSharedRows);
417 
418     std::vector<SCROW> aRows;
419     aRows.reserve(rVals.size());
420     for (SCROW i = nRow; i <= nLastRow; ++i)
421         aRows.push_back(i);
422 
423     BroadcastCells(aRows, SfxHintId::ScDataChanged);
424 }
425 
426 void ScColumn::TransferCellValuesTo( SCROW nRow, size_t nLen, sc::CellValues& rDest )
427 {
428     if (!GetDoc().ValidRow(nRow))
429         return;
430 
431     SCROW nLastRow = nRow + nLen - 1;
432     if (nLastRow > GetDoc().MaxRow())
433         // Out of bound. Do nothing.
434         return;
435 
436     sc::CellStoreType::position_type aPos = maCells.position(nRow);
437     DetachFormulaCells(aPos, nLen, nullptr);
438 
439     rDest.transferFrom(*this, nRow, nLen);
440 
441     CellStorageModified();
442 
443     std::vector<SCROW> aRows;
444     aRows.reserve(nLen);
445     for (SCROW i = nRow; i <= nLastRow; ++i)
446         aRows.push_back(i);
447 
448     BroadcastCells(aRows, SfxHintId::ScDataChanged);
449 }
450 
451 void ScColumn::CopyCellValuesFrom( SCROW nRow, const sc::CellValues& rSrc )
452 {
453     if (!GetDoc().ValidRow(nRow))
454         return;
455 
456     SCROW nLastRow = nRow + rSrc.size() - 1;
457     if (nLastRow > GetDoc().MaxRow())
458         // Out of bound. Do nothing
459         return;
460 
461     sc::CellStoreType::position_type aPos = maCells.position(nRow);
462     DetachFormulaCells(aPos, rSrc.size(), nullptr);
463 
464     rSrc.copyTo(*this, nRow);
465 
466     CellStorageModified();
467 
468     std::vector<SCROW> aRows;
469     aRows.reserve(rSrc.size());
470     for (SCROW i = nRow; i <= nLastRow; ++i)
471         aRows.push_back(i);
472 
473     BroadcastCells(aRows, SfxHintId::ScDataChanged);
474 }
475 
476 namespace {
477 
478 class ConvertFormulaToValueHandler
479 {
480     sc::CellValues maResValues;
481     bool mbModified;
482 
483 public:
484     ConvertFormulaToValueHandler(ScSheetLimits const & rSheetLimits) :
485         mbModified(false)
486     {
487         maResValues.reset(rSheetLimits.GetMaxRowCount());
488     }
489 
490     void operator() ( size_t nRow, const ScFormulaCell* pCell )
491     {
492         sc::FormulaResultValue aRes = pCell->GetResult();
493         switch (aRes.meType)
494         {
495             case sc::FormulaResultValue::Value:
496                 maResValues.setValue(nRow, aRes.mfValue);
497             break;
498             case sc::FormulaResultValue::String:
499                 maResValues.setValue(nRow, aRes.maString);
500             break;
501             case sc::FormulaResultValue::Error:
502             case sc::FormulaResultValue::Invalid:
503             default:
504                 maResValues.setValue(nRow, svl::SharedString::getEmptyString());
505         }
506 
507         mbModified = true;
508     }
509 
510     bool isModified() const { return mbModified; }
511 
512     sc::CellValues& getResValues() { return maResValues; }
513 };
514 
515 }
516 
517 void ScColumn::ConvertFormulaToValue(
518     sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, sc::TableValues* pUndo )
519 {
520     if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
521         return;
522 
523     std::vector<SCROW> aBounds { nRow1 };
524     if (nRow2 < GetDoc().MaxRow()-1)
525         aBounds.push_back(nRow2+1);
526 
527     // Split formula cell groups at top and bottom boundaries (if applicable).
528     sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
529 
530     // Parse all formulas within the range and store their results into temporary storage.
531     ConvertFormulaToValueHandler aFunc(GetDoc().GetSheetLimits());
532     sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
533     if (!aFunc.isModified())
534         // No formula cells encountered.
535         return;
536 
537     DetachFormulaCells(rCxt, nRow1, nRow2);
538 
539     // Undo storage to hold static values which will get swapped to the cell storage later.
540     sc::CellValues aUndoCells;
541     aFunc.getResValues().swap(aUndoCells);
542     aUndoCells.swapNonEmpty(*this);
543     if (pUndo)
544         pUndo->swap(nTab, nCol, aUndoCells);
545 }
546 
547 namespace {
548 
549 class StartListeningHandler
550 {
551     sc::StartListeningContext& mrCxt;
552 
553 public:
554     explicit StartListeningHandler( sc::StartListeningContext& rCxt ) :
555         mrCxt(rCxt) {}
556 
557     void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
558     {
559         pCell->StartListeningTo(mrCxt);
560     }
561 };
562 
563 class EndListeningHandler
564 {
565     sc::EndListeningContext& mrCxt;
566 
567 public:
568     explicit EndListeningHandler( sc::EndListeningContext& rCxt ) :
569         mrCxt(rCxt) {}
570 
571     void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
572     {
573         pCell->EndListeningTo(mrCxt);
574     }
575 };
576 
577 }
578 
579 void ScColumn::SwapNonEmpty(
580     sc::TableValues& rValues, sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt )
581 {
582     const ScRange& rRange = rValues.getRange();
583     std::vector<SCROW> aBounds { rRange.aStart.Row() };
584     if (rRange.aEnd.Row() < GetDoc().MaxRow()-1)
585         aBounds.push_back(rRange.aEnd.Row()+1);
586 
587     // Split formula cell groups at top and bottom boundaries (if applicable).
588     sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
589     std::vector<sc::CellValueSpan> aSpans = rValues.getNonEmptySpans(nTab, nCol);
590 
591     // Detach formula cells within the spans (if any).
592     EndListeningHandler aEndLisFunc(rEndCxt);
593     sc::CellStoreType::iterator itPos = maCells.begin();
594     for (const auto& rSpan : aSpans)
595     {
596         SCROW nRow1 = rSpan.mnRow1;
597         SCROW nRow2 = rSpan.mnRow2;
598         itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aEndLisFunc);
599     }
600 
601     rValues.swapNonEmpty(nTab, nCol, *this);
602     RegroupFormulaCells();
603 
604     // Attach formula cells within the spans (if any).
605     StartListeningHandler aStartLisFunc(rStartCxt);
606     itPos = maCells.begin();
607     for (const auto& rSpan : aSpans)
608     {
609         SCROW nRow1 = rSpan.mnRow1;
610         SCROW nRow2 = rSpan.mnRow2;
611         itPos = sc::ProcessFormula(itPos, maCells, nRow1, nRow2, aStartLisFunc);
612     }
613 
614     CellStorageModified();
615 }
616 
617 void ScColumn::DeleteRanges( const std::vector<sc::RowSpan>& rRanges, InsertDeleteFlags nDelFlag )
618 {
619     for (const auto& rSpan : rRanges)
620         DeleteArea(rSpan.mnRow1, rSpan.mnRow2, nDelFlag, false/*bBroadcast*/);
621 }
622 
623 void ScColumn::CloneFormulaCell(
624     sc::ColumnBlockPosition& rBlockPos,
625     const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr,
626     const std::vector<sc::RowSpan>& rRanges )
627 {
628     SCCOL nMatrixCols = 0;
629     SCROW nMatrixRows = 0;
630     ScMatrixMode nMatrixFlag = rSrc.GetMatrixFlag();
631     if (nMatrixFlag == ScMatrixMode::Formula)
632     {
633         rSrc.GetMatColsRows( nMatrixCols, nMatrixRows);
634         SAL_WARN_IF( nMatrixCols != 1 || nMatrixRows != 1, "sc.core",
635                 "ScColumn::CloneFormulaCell - cloning array/matrix with not exactly one column or row as single cell");
636     }
637 
638     ScDocument& rDocument = GetDoc();
639     std::vector<ScFormulaCell*> aFormulas;
640     for (const auto& rSpan : rRanges)
641     {
642         SCROW nRow1 = rSpan.mnRow1, nRow2 = rSpan.mnRow2;
643         size_t nLen = nRow2 - nRow1 + 1;
644         assert(nLen > 0);
645         aFormulas.clear();
646         aFormulas.reserve(nLen);
647 
648         ScAddress aPos(nCol, nRow1, nTab);
649 
650         if (nLen == 1 || !rSrc.GetCode()->IsShareable())
651         {
652             // Single, ungrouped formula cell, or create copies for
653             // non-shareable token arrays.
654             for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
655             {
656                 ScFormulaCell* pCell = new ScFormulaCell(rSrc, rDocument, aPos);
657                 aFormulas.push_back(pCell);
658             }
659         }
660         else
661         {
662             // Create a group of formula cells.
663             ScFormulaCellGroupRef xGroup(new ScFormulaCellGroup);
664             xGroup->setCode(*rSrc.GetCode());
665             xGroup->compileCode(rDocument, aPos, rDocument.GetGrammar());
666             for (size_t i = 0; i < nLen; ++i, aPos.IncRow())
667             {
668                 ScFormulaCell* pCell = new ScFormulaCell(rDocument, aPos, xGroup, rDocument.GetGrammar(), nMatrixFlag);
669                 if (nMatrixFlag == ScMatrixMode::Formula)
670                     pCell->SetMatColsRows( nMatrixCols, nMatrixRows);
671                 if (i == 0)
672                 {
673                     xGroup->mpTopCell = pCell;
674                     xGroup->mnLength = nLen;
675                 }
676                 aFormulas.push_back(pCell);
677             }
678         }
679 
680         rBlockPos.miCellPos = maCells.set(rBlockPos.miCellPos, nRow1, aFormulas.begin(), aFormulas.end());
681 
682         // Join the top and bottom of the pasted formula cells as needed.
683         sc::CellStoreType::position_type aPosObj = maCells.position(rBlockPos.miCellPos, nRow1);
684 
685         assert(aPosObj.first->type == sc::element_type_formula);
686         ScFormulaCell* pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
687         JoinNewFormulaCell(aPosObj, *pCell);
688 
689         aPosObj = maCells.position(aPosObj.first, nRow2);
690         assert(aPosObj.first->type == sc::element_type_formula);
691         pCell = sc::formula_block::at(*aPosObj.first->data, aPosObj.second);
692         JoinNewFormulaCell(aPosObj, *pCell);
693 
694         std::vector<sc::CellTextAttr> aTextAttrs(nLen, rAttr);
695         rBlockPos.miCellTextAttrPos = maCellTextAttrs.set(
696             rBlockPos.miCellTextAttrPos, nRow1, aTextAttrs.begin(), aTextAttrs.end());
697     }
698 
699     CellStorageModified();
700 }
701 
702 void ScColumn::CloneFormulaCell(
703     const ScFormulaCell& rSrc, const sc::CellTextAttr& rAttr,
704     const std::vector<sc::RowSpan>& rRanges )
705 {
706     sc::ColumnBlockPosition aBlockPos;
707     InitBlockPosition(aBlockPos);
708     CloneFormulaCell(aBlockPos, rSrc, rAttr, rRanges);
709 }
710 
711 std::unique_ptr<ScPostIt> ScColumn::ReleaseNote( SCROW nRow )
712 {
713     if (!GetDoc().ValidRow(nRow))
714         return nullptr;
715 
716     ScPostIt* p = nullptr;
717     maCellNotes.release(nRow, p);
718     return std::unique_ptr<ScPostIt>(p);
719 }
720 
721 size_t ScColumn::GetNoteCount() const
722 {
723     return std::accumulate(maCellNotes.begin(), maCellNotes.end(), size_t(0),
724         [](const size_t& rCount, const auto& rCellNote) {
725             if (rCellNote.type != sc::element_type_cellnote)
726                 return rCount;
727             return rCount + rCellNote.size;
728         });
729 }
730 
731 namespace {
732 
733 class NoteCaptionCreator
734 {
735     ScAddress maPos;
736 public:
737     NoteCaptionCreator( SCTAB nTab, SCCOL nCol ) : maPos(nCol,0,nTab) {}
738 
739     void operator() ( size_t nRow, const ScPostIt* p )
740     {
741         maPos.SetRow(nRow);
742         p->GetOrCreateCaption(maPos);
743     }
744 };
745 
746 class NoteCaptionCleaner
747 {
748     bool mbPreserveData;
749 public:
750     explicit NoteCaptionCleaner( bool bPreserveData ) : mbPreserveData(bPreserveData) {}
751 
752     void operator() ( size_t /*nRow*/, ScPostIt* p )
753     {
754         p->ForgetCaption(mbPreserveData);
755     }
756 };
757 
758 }
759 
760 void ScColumn::CreateAllNoteCaptions()
761 {
762     NoteCaptionCreator aFunc(nTab, nCol);
763     sc::ProcessNote(maCellNotes, aFunc);
764 }
765 
766 void ScColumn::ForgetNoteCaptions( SCROW nRow1, SCROW nRow2, bool bPreserveData )
767 {
768     if (maCellNotes.empty())
769         return;
770 
771     if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
772         return;
773 
774     NoteCaptionCleaner aFunc(bPreserveData);
775     sc::CellNoteStoreType::iterator it = maCellNotes.begin();
776     sc::ProcessNote(it, maCellNotes, nRow1, nRow2, aFunc);
777 }
778 
779 SCROW ScColumn::GetNotePosition( size_t nIndex ) const
780 {
781     // Return the row position of the nth note in the column.
782 
783     size_t nCount = 0; // Number of notes encountered so far.
784     for (const auto& rCellNote : maCellNotes)
785     {
786         if (rCellNote.type != sc::element_type_cellnote)
787             // Skip the empty blocks.
788             continue;
789 
790         if (nIndex < nCount + rCellNote.size)
791         {
792             // Index falls within this block.
793             size_t nOffset = nIndex - nCount;
794             return rCellNote.position + nOffset;
795         }
796 
797         nCount += rCellNote.size;
798     }
799 
800     return -1;
801 }
802 
803 namespace {
804 
805 class NoteEntryCollector
806 {
807     std::vector<sc::NoteEntry>& mrNotes;
808     SCTAB mnTab;
809     SCCOL mnCol;
810     SCROW mnStartRow;
811     SCROW mnEndRow;
812 public:
813     NoteEntryCollector( std::vector<sc::NoteEntry>& rNotes, SCTAB nTab, SCCOL nCol,
814             SCROW nStartRow, SCROW nEndRow) :
815         mrNotes(rNotes), mnTab(nTab), mnCol(nCol),
816         mnStartRow(nStartRow), mnEndRow(nEndRow) {}
817 
818     void operator() (const sc::CellNoteStoreType::value_type& node) const
819     {
820         if (node.type != sc::element_type_cellnote)
821             return;
822 
823         size_t nTopRow = node.position;
824         sc::cellnote_block::const_iterator it = sc::cellnote_block::begin(*node.data);
825         sc::cellnote_block::const_iterator itEnd = sc::cellnote_block::end(*node.data);
826         size_t nOffset = 0;
827         if(nTopRow < o3tl::make_unsigned(mnStartRow))
828         {
829             std::advance(it, mnStartRow - nTopRow);
830             nOffset = mnStartRow - nTopRow;
831         }
832 
833         for (; it != itEnd && nTopRow + nOffset <= o3tl::make_unsigned(mnEndRow);
834                 ++it, ++nOffset)
835         {
836             ScAddress aPos(mnCol, nTopRow + nOffset, mnTab);
837             mrNotes.emplace_back(aPos, *it);
838         }
839     }
840 };
841 
842 }
843 
844 void ScColumn::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
845 {
846     std::for_each(maCellNotes.begin(), maCellNotes.end(), NoteEntryCollector(rNotes, nTab, nCol, 0, GetDoc().MaxRow()));
847 }
848 
849 void ScColumn::GetNotesInRange(SCROW nStartRow, SCROW nEndRow,
850         std::vector<sc::NoteEntry>& rNotes ) const
851 {
852     std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
853     sc::CellNoteStoreType::const_iterator it = aPos.first;
854     if (it == maCellNotes.end())
855         // Invalid row number.
856         return;
857 
858     std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos =
859         maCellNotes.position(nEndRow);
860     sc::CellNoteStoreType::const_iterator itEnd = aEndPos.first;
861 
862     std::for_each(it, ++itEnd, NoteEntryCollector(rNotes, nTab, nCol, nStartRow, nEndRow));
863 }
864 
865 bool ScColumn::HasCellNote(SCROW nStartRow, SCROW nEndRow) const
866 {
867     std::pair<sc::CellNoteStoreType::const_iterator,size_t> aStartPos =
868         maCellNotes.position(nStartRow);
869     if (aStartPos.first == maCellNotes.end())
870         // Invalid row number.
871         return false;
872 
873     std::pair<sc::CellNoteStoreType::const_iterator,size_t> aEndPos =
874         maCellNotes.position(nEndRow);
875 
876     for (sc::CellNoteStoreType::const_iterator it = aStartPos.first; it != aEndPos.first; ++it)
877     {
878         if (it->type != sc::element_type_cellnote)
879             continue;
880         size_t nTopRow = it->position;
881         sc::cellnote_block::const_iterator blockIt = sc::cellnote_block::begin(*(it->data));
882         sc::cellnote_block::const_iterator blockItEnd = sc::cellnote_block::end(*(it->data));
883         size_t nOffset = 0;
884         if(nTopRow < o3tl::make_unsigned(nStartRow))
885         {
886             std::advance(blockIt, nStartRow - nTopRow);
887             nOffset = nStartRow - nTopRow;
888         }
889 
890         if (blockIt != blockItEnd && nTopRow + nOffset <= o3tl::make_unsigned(nEndRow))
891             return true;
892     }
893 
894     return false;
895 }
896 
897 void ScColumn::GetUnprotectedCells( SCROW nStartRow, SCROW nEndRow, ScRangeList& rRangeList ) const
898 {
899     SCROW nTmpStartRow = nStartRow, nTmpEndRow = nEndRow;
900     const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow);
901     bool bProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection();
902     if (!bProtection)
903     {
904         // Limit the span to the range in question.
905         if (nTmpStartRow < nStartRow)
906             nTmpStartRow = nStartRow;
907         if (nTmpEndRow > nEndRow)
908             nTmpEndRow = nEndRow;
909         rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab));
910     }
911     while (nEndRow > nTmpEndRow)
912     {
913         nStartRow = nTmpEndRow + 1;
914         pPattern = pAttrArray->GetPatternRange(nTmpStartRow, nTmpEndRow, nStartRow);
915         bool bTmpProtection = pPattern->GetItem(ATTR_PROTECTION).GetProtection();
916         if (!bTmpProtection)
917         {
918             // Limit the span to the range in question.
919             // Only end row needs to be checked as we enter here only for spans
920             // below the original nStartRow.
921             if (nTmpEndRow > nEndRow)
922                 nTmpEndRow = nEndRow;
923             rRangeList.Join( ScRange( nCol, nTmpStartRow, nTab, nCol, nTmpEndRow, nTab));
924         }
925     }
926 }
927 
928 namespace {
929 
930 class RecompileByOpcodeHandler
931 {
932     ScDocument* mpDoc;
933     const formula::unordered_opcode_set& mrOps;
934     sc::EndListeningContext& mrEndListenCxt;
935     sc::CompileFormulaContext& mrCompileFormulaCxt;
936 
937 public:
938     RecompileByOpcodeHandler(
939         ScDocument* pDoc, const formula::unordered_opcode_set& rOps,
940         sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
941         mpDoc(pDoc),
942         mrOps(rOps),
943         mrEndListenCxt(rEndListenCxt),
944         mrCompileFormulaCxt(rCompileCxt) {}
945 
946     void operator() ( sc::FormulaGroupEntry& rEntry )
947     {
948         // Perform end listening, remove from formula tree, and set them up
949         // for re-compilation.
950 
951         ScFormulaCell* pTop = nullptr;
952 
953         if (rEntry.mbShared)
954         {
955             // Only inspect the code from the top cell.
956             pTop = *rEntry.mpCells;
957         }
958         else
959             pTop = rEntry.mpCell;
960 
961         ScTokenArray* pCode = pTop->GetCode();
962         bool bRecompile = pCode->HasOpCodes(mrOps);
963 
964         if (!bRecompile)
965             return;
966 
967         // Get the formula string.
968         OUString aFormula = pTop->GetFormula(mrCompileFormulaCxt);
969         sal_Int32 n = aFormula.getLength();
970         if (pTop->GetMatrixFlag() != ScMatrixMode::NONE && n > 0)
971         {
972             if (aFormula[0] == '{' && aFormula[n-1] == '}')
973                 aFormula = aFormula.copy(1, n-2);
974         }
975 
976         if (rEntry.mbShared)
977         {
978             ScFormulaCell** pp = rEntry.mpCells;
979             ScFormulaCell** ppEnd = pp + rEntry.mnLength;
980             for (; pp != ppEnd; ++pp)
981             {
982                 ScFormulaCell* p = *pp;
983                 p->EndListeningTo(mrEndListenCxt);
984                 mpDoc->RemoveFromFormulaTree(p);
985             }
986         }
987         else
988         {
989             rEntry.mpCell->EndListeningTo(mrEndListenCxt);
990             mpDoc->RemoveFromFormulaTree(rEntry.mpCell);
991         }
992 
993         pCode->Clear();
994         pTop->SetHybridFormula(aFormula, mpDoc->GetGrammar());
995     }
996 };
997 
998 class CompileHybridFormulaHandler
999 {
1000     ScDocument& mrDoc;
1001     sc::StartListeningContext& mrStartListenCxt;
1002     sc::CompileFormulaContext& mrCompileFormulaCxt;
1003 
1004 public:
1005     CompileHybridFormulaHandler(ScDocument& rDoc, sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt ) :
1006         mrDoc(rDoc),
1007         mrStartListenCxt(rStartListenCxt),
1008         mrCompileFormulaCxt(rCompileCxt) {}
1009 
1010     void operator() ( sc::FormulaGroupEntry& rEntry )
1011     {
1012         if (rEntry.mbShared)
1013         {
1014             ScFormulaCell* pTop = *rEntry.mpCells;
1015             OUString aFormula = pTop->GetHybridFormula();
1016 
1017             if (!aFormula.isEmpty())
1018             {
1019                 // Create a new token array from the hybrid formula string, and
1020                 // set it to the group.
1021                 ScCompiler aComp(mrCompileFormulaCxt, pTop->aPos);
1022                 std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula);
1023                 ScFormulaCellGroupRef xGroup = pTop->GetCellGroup();
1024                 assert(xGroup);
1025                 xGroup->setCode(std::move(*pNewCode));
1026                 xGroup->compileCode(mrDoc, pTop->aPos, mrDoc.GetGrammar());
1027 
1028                 // Propagate the new token array to all formula cells in the group.
1029                 ScFormulaCell** pp = rEntry.mpCells;
1030                 ScFormulaCell** ppEnd = pp + rEntry.mnLength;
1031                 for (; pp != ppEnd; ++pp)
1032                 {
1033                     ScFormulaCell* p = *pp;
1034                     p->SyncSharedCode();
1035                     p->StartListeningTo(mrStartListenCxt);
1036                     p->SetDirty();
1037                 }
1038             }
1039         }
1040         else
1041         {
1042             ScFormulaCell* pCell = rEntry.mpCell;
1043             OUString aFormula = pCell->GetHybridFormula();
1044 
1045             if (!aFormula.isEmpty())
1046             {
1047                 // Create token array from formula string.
1048                 ScCompiler aComp(mrCompileFormulaCxt, pCell->aPos);
1049                 std::unique_ptr<ScTokenArray> pNewCode = aComp.CompileString(aFormula);
1050 
1051                 // Generate RPN tokens.
1052                 ScCompiler aComp2(mrDoc, pCell->aPos, *pNewCode, formula::FormulaGrammar::GRAM_UNSPECIFIED,
1053                                   true, pCell->GetMatrixFlag() != ScMatrixMode::NONE);
1054                 aComp2.CompileTokenArray();
1055 
1056                 pCell->SetCode(std::move(pNewCode));
1057                 pCell->StartListeningTo(mrStartListenCxt);
1058                 pCell->SetDirty();
1059             }
1060         }
1061     }
1062 };
1063 
1064 }
1065 
1066 void ScColumn::PreprocessRangeNameUpdate(
1067     sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
1068 {
1069     // Collect all formula groups.
1070     std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
1071 
1072     formula::unordered_opcode_set aOps;
1073     aOps.insert(ocBad);
1074     aOps.insert(ocColRowName);
1075     aOps.insert(ocName);
1076     RecompileByOpcodeHandler aFunc(&GetDoc(), aOps, rEndListenCxt, rCompileCxt);
1077     std::for_each(aGroups.begin(), aGroups.end(), aFunc);
1078 }
1079 
1080 void ScColumn::PreprocessDBDataUpdate(
1081     sc::EndListeningContext& rEndListenCxt, sc::CompileFormulaContext& rCompileCxt )
1082 {
1083     // Collect all formula groups.
1084     std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
1085 
1086     formula::unordered_opcode_set aOps;
1087     aOps.insert(ocBad);
1088     aOps.insert(ocColRowName);
1089     aOps.insert(ocDBArea);
1090     aOps.insert(ocTableRef);
1091     RecompileByOpcodeHandler aFunc(&GetDoc(), aOps, rEndListenCxt, rCompileCxt);
1092     std::for_each(aGroups.begin(), aGroups.end(), aFunc);
1093 }
1094 
1095 void ScColumn::CompileHybridFormula(
1096     sc::StartListeningContext& rStartListenCxt, sc::CompileFormulaContext& rCompileCxt )
1097 {
1098     // Collect all formula groups.
1099     std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
1100 
1101     CompileHybridFormulaHandler aFunc(GetDoc(), rStartListenCxt, rCompileCxt);
1102     std::for_each(aGroups.begin(), aGroups.end(), aFunc);
1103 }
1104 
1105 namespace {
1106 
1107 class ScriptTypeUpdater
1108 {
1109     ScColumn& mrCol;
1110     sc::CellTextAttrStoreType& mrTextAttrs;
1111     sc::CellTextAttrStoreType::iterator miPosAttr;
1112     ScConditionalFormatList* mpCFList;
1113     SvNumberFormatter* mpFormatter;
1114     ScAddress maPos;
1115     bool mbUpdated;
1116 
1117 private:
1118     void updateScriptType( size_t nRow, ScRefCellValue& rCell )
1119     {
1120         sc::CellTextAttrStoreType::position_type aAttrPos = mrTextAttrs.position(miPosAttr, nRow);
1121         miPosAttr = aAttrPos.first;
1122 
1123         if (aAttrPos.first->type != sc::element_type_celltextattr)
1124             return;
1125 
1126         sc::CellTextAttr& rAttr = sc::celltextattr_block::at(*aAttrPos.first->data, aAttrPos.second);
1127         if (rAttr.mnScriptType != SvtScriptType::UNKNOWN)
1128             // Script type already determined.  Skip it.
1129             return;
1130 
1131         const ScPatternAttr* pPat = mrCol.GetPattern(nRow);
1132         if (!pPat)
1133             // In theory this should never return NULL. But let's be safe.
1134             return;
1135 
1136         const SfxItemSet* pCondSet = nullptr;
1137         if (mpCFList)
1138         {
1139             maPos.SetRow(nRow);
1140             const ScCondFormatItem& rItem = pPat->GetItem(ATTR_CONDITIONAL);
1141             const ScCondFormatIndexes& rData = rItem.GetCondFormatData();
1142             pCondSet = mrCol.GetDoc().GetCondResult(rCell, maPos, *mpCFList, rData);
1143         }
1144 
1145         const Color* pColor;
1146         sal_uInt32 nFormat = pPat->GetNumberFormat(mpFormatter, pCondSet);
1147         OUString aStr = ScCellFormat::GetString(rCell, nFormat, &pColor, *mpFormatter, mrCol.GetDoc());
1148 
1149         rAttr.mnScriptType = mrCol.GetDoc().GetStringScriptType(aStr);
1150         mbUpdated = true;
1151     }
1152 
1153 public:
1154     explicit ScriptTypeUpdater( ScColumn& rCol ) :
1155         mrCol(rCol),
1156         mrTextAttrs(rCol.GetCellAttrStore()),
1157         miPosAttr(mrTextAttrs.begin()),
1158         mpCFList(rCol.GetDoc().GetCondFormList(rCol.GetTab())),
1159         mpFormatter(rCol.GetDoc().GetFormatTable()),
1160         maPos(rCol.GetCol(), 0, rCol.GetTab()),
1161         mbUpdated(false)
1162     {}
1163 
1164     void operator() ( size_t nRow, double fVal )
1165     {
1166         ScRefCellValue aCell(fVal);
1167         updateScriptType(nRow, aCell);
1168     }
1169 
1170     void operator() ( size_t nRow, const svl::SharedString& rStr )
1171     {
1172         ScRefCellValue aCell(&rStr);
1173         updateScriptType(nRow, aCell);
1174     }
1175 
1176     void operator() ( size_t nRow, const EditTextObject* pText )
1177     {
1178         ScRefCellValue aCell(pText);
1179         updateScriptType(nRow, aCell);
1180     }
1181 
1182     void operator() ( size_t nRow, const ScFormulaCell* pCell )
1183     {
1184         ScRefCellValue aCell(const_cast<ScFormulaCell*>(pCell));
1185         updateScriptType(nRow, aCell);
1186     }
1187 
1188     bool isUpdated() const { return mbUpdated; }
1189 };
1190 
1191 }
1192 
1193 void ScColumn::UpdateScriptTypes( SCROW nRow1, SCROW nRow2 )
1194 {
1195     if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
1196         return;
1197 
1198     ScriptTypeUpdater aFunc(*this);
1199     sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
1200     if (aFunc.isUpdated())
1201         CellStorageModified();
1202 }
1203 
1204 void ScColumn::Swap( ScColumn& rOther, SCROW nRow1, SCROW nRow2, bool bPattern )
1205 {
1206     maCells.swap(nRow1, nRow2, rOther.maCells, nRow1);
1207     maCellTextAttrs.swap(nRow1, nRow2, rOther.maCellTextAttrs, nRow1);
1208     maCellNotes.swap(nRow1, nRow2, rOther.maCellNotes, nRow1);
1209     maBroadcasters.swap(nRow1, nRow2, rOther.maBroadcasters, nRow1);
1210 
1211     // Update draw object anchors
1212     ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
1213     if (pDrawLayer)
1214     {
1215         std::map<SCROW, std::vector<SdrObject*>> aThisColRowDrawObjects
1216             = pDrawLayer->GetObjectsAnchoredToRange(GetTab(), GetCol(), nRow1, nRow2);
1217         std::map<SCROW, std::vector<SdrObject*>> aOtherColRowDrawObjects
1218             = pDrawLayer->GetObjectsAnchoredToRange(GetTab(), rOther.GetCol(), nRow1, nRow2);
1219         for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
1220         {
1221             std::vector<SdrObject*>& rThisCellDrawObjects = aThisColRowDrawObjects[nRow];
1222             if (!rThisCellDrawObjects.empty())
1223                 UpdateDrawObjectsForRow(rThisCellDrawObjects, rOther.GetCol(), nRow);
1224             std::vector<SdrObject*>& rOtherCellDrawObjects = aOtherColRowDrawObjects[nRow];
1225             if (!rOtherCellDrawObjects.empty())
1226                 rOther.UpdateDrawObjectsForRow(rOtherCellDrawObjects, GetCol(), nRow);
1227         }
1228     }
1229 
1230     if (bPattern)
1231     {
1232         for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
1233         {
1234             const ScPatternAttr* pPat1 = GetPattern(nRow);
1235             const ScPatternAttr* pPat2 = rOther.GetPattern(nRow);
1236             if (pPat1 != pPat2)
1237             {
1238                 if (pPat1->GetRefCount() == 1)
1239                     pPat1 = &rOther.GetDoc().GetPool()->Put(*pPat1);
1240                 SetPattern(nRow, *pPat2);
1241                 rOther.SetPattern(nRow, *pPat1);
1242             }
1243         }
1244     }
1245 
1246     CellStorageModified();
1247     rOther.CellStorageModified();
1248 }
1249 
1250 namespace {
1251 
1252 class FormulaColPosSetter
1253 {
1254     SCCOL mnCol;
1255     bool  mbUpdateRefs;
1256 public:
1257     FormulaColPosSetter( SCCOL nCol, bool bUpdateRefs ) : mnCol(nCol), mbUpdateRefs(bUpdateRefs) {}
1258 
1259     void operator() ( size_t nRow, ScFormulaCell* pCell )
1260     {
1261         if (!pCell->IsShared() || pCell->IsSharedTop())
1262         {
1263             // Ensure that the references still point to the same locations
1264             // after the position change.
1265             ScAddress aOldPos = pCell->aPos;
1266             pCell->aPos.SetCol(mnCol);
1267             pCell->aPos.SetRow(nRow);
1268             if (mbUpdateRefs)
1269                 pCell->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos, pCell->aPos);
1270             else
1271                 pCell->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos, pCell->aPos);
1272         }
1273         else
1274         {
1275             pCell->aPos.SetCol(mnCol);
1276             pCell->aPos.SetRow(nRow);
1277         }
1278     }
1279 };
1280 
1281 }
1282 
1283 void ScColumn::ResetFormulaCellPositions( SCROW nRow1, SCROW nRow2, bool bUpdateRefs )
1284 {
1285     FormulaColPosSetter aFunc(nCol, bUpdateRefs);
1286     sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
1287 }
1288 
1289 namespace {
1290 
1291 class RelativeRefBoundChecker
1292 {
1293     std::vector<SCROW> maBounds;
1294     ScRange maBoundRange;
1295 
1296 public:
1297     explicit RelativeRefBoundChecker( const ScRange& rBoundRange ) :
1298         maBoundRange(rBoundRange) {}
1299 
1300     void operator() ( size_t /*nRow*/, ScFormulaCell* pCell )
1301     {
1302         if (!pCell->IsSharedTop())
1303             return;
1304 
1305         pCell->GetCode()->CheckRelativeReferenceBounds(
1306             pCell->aPos, pCell->GetSharedLength(), maBoundRange, maBounds);
1307     }
1308 
1309     void swapBounds( std::vector<SCROW>& rBounds )
1310     {
1311         rBounds.swap(maBounds);
1312     }
1313 };
1314 
1315 }
1316 
1317 void ScColumn::SplitFormulaGroupByRelativeRef( const ScRange& rBoundRange )
1318 {
1319     if (rBoundRange.aStart.Row() >= GetDoc().MaxRow())
1320         // Nothing to split.
1321         return;
1322 
1323     std::vector<SCROW> aBounds;
1324 
1325     // Cut at row boundaries first.
1326     aBounds.push_back(rBoundRange.aStart.Row());
1327     if (rBoundRange.aEnd.Row() < GetDoc().MaxRow())
1328         aBounds.push_back(rBoundRange.aEnd.Row()+1);
1329     sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
1330 
1331     RelativeRefBoundChecker aFunc(rBoundRange);
1332     sc::ProcessFormula(
1333         maCells.begin(), maCells, rBoundRange.aStart.Row(), rBoundRange.aEnd.Row(), aFunc);
1334     aFunc.swapBounds(aBounds);
1335     sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
1336 }
1337 
1338 namespace {
1339 
1340 class ListenerCollector
1341 {
1342     std::vector<SvtListener*>& mrListeners;
1343 public:
1344     explicit ListenerCollector( std::vector<SvtListener*>& rListener ) :
1345         mrListeners(rListener) {}
1346 
1347     void operator() ( size_t /*nRow*/, SvtBroadcaster* p )
1348     {
1349         SvtBroadcaster::ListenersType& rLis = p->GetAllListeners();
1350         mrListeners.insert(mrListeners.end(), rLis.begin(), rLis.end());
1351     }
1352 };
1353 
1354 class FormulaCellCollector
1355 {
1356     std::vector<ScFormulaCell*>& mrCells;
1357 public:
1358     explicit FormulaCellCollector( std::vector<ScFormulaCell*>& rCells ) : mrCells(rCells) {}
1359 
1360     void operator() ( size_t /*nRow*/, ScFormulaCell* p )
1361     {
1362         mrCells.push_back(p);
1363     }
1364 };
1365 
1366 }
1367 
1368 void ScColumn::CollectListeners( std::vector<SvtListener*>& rListeners, SCROW nRow1, SCROW nRow2 )
1369 {
1370     if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
1371         return;
1372 
1373     ListenerCollector aFunc(rListeners);
1374     sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aFunc);
1375 }
1376 
1377 void ScColumn::CollectFormulaCells( std::vector<ScFormulaCell*>& rCells, SCROW nRow1, SCROW nRow2 )
1378 {
1379     if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
1380         return;
1381 
1382     FormulaCellCollector aFunc(rCells);
1383     sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
1384 }
1385 
1386 bool ScColumn::HasFormulaCell() const
1387 {
1388     return mnBlkCountFormula != 0;
1389 }
1390 
1391 namespace {
1392 
1393 struct FindAnyFormula
1394 {
1395     bool operator() ( size_t /*nRow*/, const ScFormulaCell* /*pCell*/ ) const
1396     {
1397         return true;
1398     }
1399 };
1400 
1401 }
1402 
1403 bool ScColumn::HasFormulaCell( SCROW nRow1, SCROW nRow2 ) const
1404 {
1405     if (!mnBlkCountFormula)
1406         return false;
1407 
1408     if (nRow2 < nRow1 || !GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2))
1409         return false;
1410 
1411     if (nRow1 == 0 && nRow2 == GetDoc().MaxRow())
1412         return HasFormulaCell();
1413 
1414     FindAnyFormula aFunc;
1415     std::pair<sc::CellStoreType::const_iterator, size_t> aRet =
1416         sc::FindFormula(maCells, nRow1, nRow2, aFunc);
1417 
1418     return aRet.first != maCells.end();
1419 }
1420 
1421 namespace {
1422 
1423 void endListening( sc::EndListeningContext& rCxt, ScFormulaCell** pp, ScFormulaCell** ppEnd )
1424 {
1425     for (; pp != ppEnd; ++pp)
1426     {
1427         ScFormulaCell& rFC = **pp;
1428         rFC.EndListeningTo(rCxt);
1429     }
1430 }
1431 
1432 class StartListeningFormulaCellsHandler
1433 {
1434     sc::StartListeningContext& mrStartCxt;
1435     sc::EndListeningContext& mrEndCxt;
1436     SCROW mnStartRow;
1437 
1438 public:
1439     StartListeningFormulaCellsHandler( sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt ) :
1440         mrStartCxt(rStartCxt), mrEndCxt(rEndCxt), mnStartRow(-1) {}
1441 
1442     void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
1443     {
1444         if (node.type != sc::element_type_formula)
1445             // We are only interested in formulas.
1446             return;
1447 
1448         mnStartRow = node.position + nOffset;
1449 
1450         ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
1451         ScFormulaCell** ppEnd = ppBeg + nDataSize;
1452 
1453         ScFormulaCell** pp = ppBeg;
1454 
1455         // If the first formula cell belongs to a group and it's not the top
1456         // cell, move up to the top cell of the group, and have all the extra
1457         // formula cells stop listening.
1458 
1459         ScFormulaCell* pFC = *pp;
1460         if (pFC->IsShared() && !pFC->IsSharedTop())
1461         {
1462             SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
1463             if (nBackTrackSize > 0)
1464             {
1465                 assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset);
1466                 for (SCROW i = 0; i < nBackTrackSize; ++i)
1467                     --pp;
1468                 endListening(mrEndCxt, pp, ppBeg);
1469                 mnStartRow -= nBackTrackSize;
1470             }
1471         }
1472 
1473         for (; pp != ppEnd; ++pp)
1474         {
1475             pFC = *pp;
1476 
1477             if (!pFC->IsSharedTop())
1478             {
1479                 assert(!pFC->IsShared());
1480                 pFC->StartListeningTo(mrStartCxt);
1481                 continue;
1482             }
1483 
1484             // If This is the last group in the range, see if the group
1485             // extends beyond the range, in which case have the excess
1486             // formula cells stop listening.
1487             size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
1488             if (nEndGroupPos > nDataSize)
1489             {
1490                 size_t nExcessSize = nEndGroupPos - nDataSize;
1491                 ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
1492                 ScFormulaCell** ppGrp = ppGrpEnd - nExcessSize;
1493                 endListening(mrEndCxt, ppGrp, ppGrpEnd);
1494 
1495                 // Register formula cells as a group.
1496                 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
1497                 pp = ppEnd - 1; // Move to the one before the end position.
1498             }
1499             else
1500             {
1501                 // Register formula cells as a group.
1502                 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt, pp);
1503                 pp += pFC->GetSharedLength() - 1; // Move to the last one in the group.
1504             }
1505         }
1506     }
1507 
1508 };
1509 
1510 class EndListeningFormulaCellsHandler
1511 {
1512     sc::EndListeningContext& mrEndCxt;
1513     SCROW mnStartRow;
1514     SCROW mnEndRow;
1515 
1516 public:
1517     explicit EndListeningFormulaCellsHandler( sc::EndListeningContext& rEndCxt ) :
1518         mrEndCxt(rEndCxt), mnStartRow(-1), mnEndRow(-1) {}
1519 
1520     void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
1521     {
1522         if (node.type != sc::element_type_formula)
1523             // We are only interested in formulas.
1524             return;
1525 
1526         mnStartRow = node.position + nOffset;
1527 
1528         ScFormulaCell** ppBeg = &sc::formula_block::at(*node.data, nOffset);
1529         ScFormulaCell** ppEnd = ppBeg + nDataSize;
1530 
1531         ScFormulaCell** pp = ppBeg;
1532 
1533         // If the first formula cell belongs to a group and it's not the top
1534         // cell, move up to the top cell of the group.
1535 
1536         ScFormulaCell* pFC = *pp;
1537         if (pFC->IsShared() && !pFC->IsSharedTop())
1538         {
1539             SCROW nBackTrackSize = pFC->aPos.Row() - pFC->GetSharedTopRow();
1540             if (nBackTrackSize > 0)
1541             {
1542                 assert(o3tl::make_unsigned(nBackTrackSize) <= nOffset);
1543                 for (SCROW i = 0; i < nBackTrackSize; ++i)
1544                     --pp;
1545                 mnStartRow -= nBackTrackSize;
1546             }
1547         }
1548 
1549         for (; pp != ppEnd; ++pp)
1550         {
1551             pFC = *pp;
1552 
1553             if (!pFC->IsSharedTop())
1554             {
1555                 assert(!pFC->IsShared());
1556                 pFC->EndListeningTo(mrEndCxt);
1557                 continue;
1558             }
1559 
1560             size_t nEndGroupPos = (pp - ppBeg) + pFC->GetSharedLength();
1561             mnEndRow = node.position + nOffset + nEndGroupPos - 1; // absolute row position of the last one in the group.
1562 
1563             ScFormulaCell** ppGrpEnd = pp + pFC->GetSharedLength();
1564             endListening(mrEndCxt, pp, ppGrpEnd);
1565 
1566             if (nEndGroupPos > nDataSize)
1567             {
1568                 // The group goes beyond the specified end row.  Move to the
1569                 // one before the end position to finish the loop.
1570                 pp = ppEnd - 1;
1571             }
1572             else
1573             {
1574                 // Move to the last one in the group.
1575                 pp += pFC->GetSharedLength() - 1;
1576             }
1577         }
1578     }
1579 
1580     SCROW getStartRow() const
1581     {
1582         return mnStartRow;
1583     }
1584 
1585     SCROW getEndRow() const
1586     {
1587         return mnEndRow;
1588     }
1589 };
1590 
1591 }
1592 
1593 void ScColumn::StartListeningFormulaCells(
1594     sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
1595     SCROW nRow1, SCROW nRow2 )
1596 {
1597     if (!HasFormulaCell())
1598         return;
1599 
1600     StartListeningFormulaCellsHandler aFunc(rStartCxt, rEndCxt);
1601     sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
1602 }
1603 
1604 void ScColumn::EndListeningFormulaCells(
1605     sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2,
1606     SCROW* pStartRow, SCROW* pEndRow )
1607 {
1608     if (!HasFormulaCell())
1609         return;
1610 
1611     EndListeningFormulaCellsHandler aFunc(rCxt);
1612     sc::ProcessBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
1613 
1614     if (pStartRow)
1615         *pStartRow = aFunc.getStartRow();
1616 
1617     if (pEndRow)
1618         *pEndRow = aFunc.getEndRow();
1619 }
1620 
1621 void ScColumn::EndListeningIntersectedGroup(
1622     sc::EndListeningContext& rCxt, SCROW nRow, std::vector<ScAddress>* pGroupPos )
1623 {
1624     if (!GetDoc().ValidRow(nRow))
1625         return;
1626 
1627     sc::CellStoreType::position_type aPos = maCells.position(nRow);
1628     sc::CellStoreType::iterator it = aPos.first;
1629     if (it->type != sc::element_type_formula)
1630         // Only interested in a formula block.
1631         return;
1632 
1633     ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
1634     ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
1635     if (!xGroup)
1636         // Not a formula group.
1637         return;
1638 
1639     // End listening.
1640     pFC->EndListeningTo(rCxt);
1641 
1642     if (pGroupPos)
1643     {
1644         if (!pFC->IsSharedTop())
1645             // Record the position of the top cell of the group.
1646             pGroupPos->push_back(xGroup->mpTopCell->aPos);
1647 
1648         SCROW nGrpLastRow = pFC->GetSharedTopRow() + pFC->GetSharedLength() - 1;
1649         if (nRow < nGrpLastRow)
1650             // Record the last position of the group.
1651             pGroupPos->push_back(ScAddress(nCol, nGrpLastRow, nTab));
1652     }
1653 }
1654 
1655 void ScColumn::EndListeningIntersectedGroups(
1656     sc::EndListeningContext& rCxt, SCROW nRow1, SCROW nRow2, std::vector<ScAddress>* pGroupPos )
1657 {
1658     // Only end the intersected group.
1659     sc::CellStoreType::position_type aPos = maCells.position(nRow1);
1660     sc::CellStoreType::iterator it = aPos.first;
1661     if (it->type == sc::element_type_formula)
1662     {
1663         ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
1664         ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
1665         if (xGroup)
1666         {
1667             if (!pFC->IsSharedTop())
1668                 // End listening.
1669                 pFC->EndListeningTo(rCxt);
1670 
1671             if (pGroupPos)
1672                 // Record the position of the top cell of the group.
1673                 pGroupPos->push_back(xGroup->mpTopCell->aPos);
1674         }
1675     }
1676 
1677     aPos = maCells.position(it, nRow2);
1678     it = aPos.first;
1679     if (it->type != sc::element_type_formula)
1680         return;
1681 
1682     ScFormulaCell* pFC = sc::formula_block::at(*it->data, aPos.second);
1683     ScFormulaCellGroupRef xGroup = pFC->GetCellGroup();
1684     if (!xGroup)
1685         return;
1686 
1687     if (!pFC->IsSharedTop())
1688         // End listening.
1689         pFC->EndListeningTo(rCxt);
1690 
1691     if (pGroupPos)
1692     {
1693         // Record the position of the bottom cell of the group.
1694         ScAddress aPosLast = xGroup->mpTopCell->aPos;
1695         aPosLast.IncRow(xGroup->mnLength-1);
1696         pGroupPos->push_back(aPosLast);
1697     }
1698 }
1699 
1700 void ScColumn::EndListeningGroup( sc::EndListeningContext& rCxt, SCROW nRow )
1701 {
1702     sc::CellStoreType::position_type aPos = maCells.position(nRow);
1703     if (aPos.first->type != sc::element_type_formula)
1704         // not a formula cell.
1705         return;
1706 
1707     ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
1708 
1709     ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup();
1710     if (!xGroup)
1711     {
1712         // not a formula group.
1713         (*pp)->EndListeningTo(rCxt);
1714         return;
1715     }
1716 
1717     // Move back to the top cell.
1718     SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
1719     assert(nTopDelta >= 0);
1720     if (nTopDelta > 0)
1721         pp -= nTopDelta;
1722 
1723     // Set the needs listening flag to all cells in the group.
1724     assert(*pp == xGroup->mpTopCell);
1725     ScFormulaCell** ppEnd = pp + xGroup->mnLength;
1726     for (; pp != ppEnd; ++pp)
1727         (*pp)->EndListeningTo(rCxt);
1728 }
1729 
1730 void ScColumn::SetNeedsListeningGroup( SCROW nRow )
1731 {
1732     sc::CellStoreType::position_type aPos = maCells.position(nRow);
1733     if (aPos.first->type != sc::element_type_formula)
1734         // not a formula cell.
1735         return;
1736 
1737     ScFormulaCell** pp = &sc::formula_block::at(*aPos.first->data, aPos.second);
1738 
1739     ScFormulaCellGroupRef xGroup = (*pp)->GetCellGroup();
1740     if (!xGroup)
1741     {
1742         // not a formula group.
1743         (*pp)->SetNeedsListening(true);
1744         return;
1745     }
1746 
1747     // Move back to the top cell.
1748     SCROW nTopDelta = (*pp)->aPos.Row() - xGroup->mpTopCell->aPos.Row();
1749     assert(nTopDelta >= 0);
1750     if (nTopDelta > 0)
1751         pp -= nTopDelta;
1752 
1753     // Set the needs listening flag to all cells in the group.
1754     assert(*pp == xGroup->mpTopCell);
1755     ScFormulaCell** ppEnd = pp + xGroup->mnLength;
1756     for (; pp != ppEnd; ++pp)
1757         (*pp)->SetNeedsListening(true);
1758 }
1759 
1760 std::optional<sc::ColumnIterator> ScColumn::GetColumnIterator( SCROW nRow1, SCROW nRow2 ) const
1761 {
1762     if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
1763         return {};
1764 
1765     return sc::ColumnIterator(maCells, nRow1, nRow2);
1766 }
1767 
1768 static bool lcl_InterpretSpan(sc::formula_block::const_iterator& rSpanIter, SCROW nStartOffset, SCROW nEndOffset,
1769                               const ScFormulaCellGroupRef& mxParentGroup, bool& bAllowThreading, ScDocument& rDoc)
1770 {
1771     bAllowThreading = true;
1772     ScFormulaCell* pCellStart = nullptr;
1773     SCROW nSpanStart = -1;
1774     SCROW nSpanEnd = -1;
1775     sc::formula_block::const_iterator itSpanStart;
1776     bool bAnyDirty = false;
1777     for (SCROW nFGOffset = nStartOffset; nFGOffset <= nEndOffset; ++rSpanIter, ++nFGOffset)
1778     {
1779         bool bThisDirty = (*rSpanIter)->NeedsInterpret();
1780         if (!pCellStart && bThisDirty)
1781         {
1782             pCellStart = *rSpanIter;
1783             itSpanStart = rSpanIter;
1784             nSpanStart = nFGOffset;
1785             bAnyDirty = true;
1786         }
1787 
1788         if (pCellStart && (!bThisDirty || nFGOffset == nEndOffset))
1789         {
1790             nSpanEnd = bThisDirty ? nFGOffset : nFGOffset - 1;
1791             assert(nSpanStart >= nStartOffset && nSpanStart <= nSpanEnd && nSpanEnd <= nEndOffset);
1792 
1793             // Found a completely dirty sub span [nSpanStart, nSpanEnd] inside the required span [nStartOffset, nEndOffset]
1794             bool bGroupInterpreted = pCellStart->Interpret(nSpanStart, nSpanEnd);
1795 
1796             if (bGroupInterpreted)
1797                 for (SCROW nIdx = nSpanStart; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart)
1798                     assert(!(*itSpanStart)->NeedsInterpret());
1799 
1800             ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper();
1801             // child cell's Interpret could result in calling dependency calc
1802             // and that could detect a cycle involving mxGroup
1803             // and do early exit in that case.
1804             // OR
1805             // this call resulted from a dependency calculation for a multi-formula-group-threading and
1806             // if intergroup dependency is found, return early.
1807             if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent())
1808             {
1809                 bAllowThreading = false;
1810                 return bAnyDirty;
1811             }
1812 
1813             if (!bGroupInterpreted)
1814             {
1815                 // Evaluate from second cell in non-grouped style (no point in trying group-interpret again).
1816                 ++itSpanStart;
1817                 for (SCROW nIdx = nSpanStart+1; nIdx <= nSpanEnd; ++nIdx, ++itSpanStart)
1818                 {
1819                     (*itSpanStart)->Interpret(); // We know for sure that this cell is dirty so directly call Interpret().
1820                     if ((*itSpanStart)->NeedsInterpret())
1821                     {
1822                         SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itSpanStart)->aPos
1823                             << " failed running Interpret(), not allowing threading");
1824                         bAllowThreading = false;
1825                         return bAnyDirty;
1826                     }
1827 
1828                     // Allow early exit like above.
1829                     if ((mxParentGroup && mxParentGroup->mbPartOfCycle) || !rRecursionHelper.AreGroupsIndependent())
1830                     {
1831                         // Set this cell as dirty as this may be interpreted in InterpretTail()
1832                         pCellStart->SetDirtyVar();
1833                         bAllowThreading = false;
1834                         return bAnyDirty;
1835                     }
1836                 }
1837             }
1838 
1839             pCellStart = nullptr; // For next sub span start detection.
1840         }
1841     }
1842 
1843     return bAnyDirty;
1844 }
1845 
1846 static void lcl_EvalDirty(sc::CellStoreType& rCells, SCROW nRow1, SCROW nRow2, ScDocument& rDoc,
1847                           const ScFormulaCellGroupRef& mxGroup, bool bThreadingDepEval, bool bSkipRunning,
1848                           bool& bIsDirty, bool& bAllowThreading)
1849 {
1850     ScRecursionHelper& rRecursionHelper = rDoc.GetRecursionHelper();
1851     std::pair<sc::CellStoreType::const_iterator,size_t> aPos = rCells.position(nRow1);
1852     sc::CellStoreType::const_iterator it = aPos.first;
1853     size_t nOffset = aPos.second;
1854     SCROW nRow = nRow1;
1855 
1856     bIsDirty = false;
1857 
1858     for (;it != rCells.end() && nRow <= nRow2; ++it, nOffset = 0)
1859     {
1860         switch( it->type )
1861         {
1862             case sc::element_type_edittext:
1863                 // These require EditEngine (in ScEditUtils::GetString()), which is probably
1864                 // too complex for use in threads.
1865                 if (bThreadingDepEval)
1866                 {
1867                     bAllowThreading = false;
1868                     return;
1869                 }
1870                 break;
1871             case sc::element_type_formula:
1872             {
1873                 size_t nRowsToRead = nRow2 - nRow + 1;
1874                 const size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1
1875                 sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
1876                 std::advance(itCell, nOffset);
1877 
1878                 // Loop inside the formula block.
1879                 size_t nCellIdx = nOffset;
1880                 while (nCellIdx < nEnd)
1881                 {
1882                     const ScFormulaCellGroupRef& mxGroupChild = (*itCell)->GetCellGroup();
1883                     ScFormulaCell* pChildTopCell = mxGroupChild ? mxGroupChild->mpTopCell : *itCell;
1884 
1885                     // Check if itCell is already in path.
1886                     // If yes use a cycle guard to mark all elements of the cycle
1887                     // and return false
1888                     if (bThreadingDepEval && pChildTopCell->GetSeenInPath())
1889                     {
1890                         ScFormulaGroupCycleCheckGuard aCycleCheckGuard(rRecursionHelper, pChildTopCell);
1891                         bAllowThreading = false;
1892                         return;
1893                     }
1894 
1895                     if (bSkipRunning && (*itCell)->IsRunning())
1896                     {
1897                         ++itCell;
1898                         nCellIdx += 1;
1899                         nRow += 1;
1900                         nRowsToRead -= 1;
1901                         continue;
1902                     }
1903 
1904                     if (mxGroupChild)
1905                     {
1906                         // It is a Formula-group, evaluate the necessary parts of it (spans).
1907                         const SCROW nFGStartOffset = (*itCell)->aPos.Row() - pChildTopCell->aPos.Row();
1908                         const SCROW nFGEndOffset = std::min(nFGStartOffset + static_cast<SCROW>(nRowsToRead) - 1, mxGroupChild->mnLength - 1);
1909                         assert(nFGEndOffset >= nFGStartOffset);
1910                         const SCROW nSpanLen = nFGEndOffset - nFGStartOffset + 1;
1911                         // The (main) span required to be evaluated is [nFGStartOffset, nFGEndOffset], but this span may contain
1912                         // non-dirty cells, so split this into sets of completely-dirty spans and try evaluate each of them in grouped-style.
1913 
1914                         bool bAnyDirtyInSpan = lcl_InterpretSpan(itCell, nFGStartOffset, nFGEndOffset, mxGroup, bAllowThreading, rDoc);
1915                         if (!bAllowThreading)
1916                             return;
1917                         // itCell will now point to cell just after the end of span [nFGStartOffset, nFGEndOffset].
1918                         bIsDirty = bIsDirty || bAnyDirtyInSpan;
1919 
1920                         // update the counters by nSpanLen.
1921                         // itCell already got updated.
1922                         nCellIdx += nSpanLen;
1923                         nRow += nSpanLen;
1924                         nRowsToRead -= nSpanLen;
1925                     }
1926                     else
1927                     {
1928                         // No formula-group here.
1929                         bool bDirtyFlag = false;
1930                         if( (*itCell)->NeedsInterpret())
1931                         {
1932                             bDirtyFlag = true;
1933                             (*itCell)->Interpret();
1934                             if ((*itCell)->NeedsInterpret())
1935                             {
1936                                 SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itCell)->aPos
1937                                     << " failed running Interpret(), not allowing threading");
1938                                 bAllowThreading = false;
1939                                 return;
1940                             }
1941                         }
1942                         bIsDirty = bIsDirty || bDirtyFlag;
1943 
1944                         // child cell's Interpret could result in calling dependency calc
1945                         // and that could detect a cycle involving mxGroup
1946                         // and do early exit in that case.
1947                         // OR
1948                         // we are trying multi-formula-group-threading, but found intergroup dependency.
1949                         if (bThreadingDepEval && mxGroup &&
1950                             (mxGroup->mbPartOfCycle || !rRecursionHelper.AreGroupsIndependent()))
1951                         {
1952                             // Set itCell as dirty as itCell may be interpreted in InterpretTail()
1953                             (*itCell)->SetDirtyVar();
1954                             bAllowThreading = false;
1955                             return;
1956                         }
1957 
1958                         // update the counters by 1.
1959                         nCellIdx += 1;
1960                         nRow += 1;
1961                         nRowsToRead -= 1;
1962                         ++itCell;
1963                     }
1964                 }
1965                 break;
1966             }
1967             default:
1968                 // Skip this block.
1969                 nRow += it->size - nOffset;
1970                 continue;
1971         }
1972     }
1973 
1974     if (bThreadingDepEval)
1975         bAllowThreading = true;
1976 
1977 }
1978 
1979 // Returns true if at least one FC is dirty.
1980 bool ScColumn::EnsureFormulaCellResults( SCROW nRow1, SCROW nRow2, bool bSkipRunning )
1981 {
1982     if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
1983         return false;
1984 
1985     if (!HasFormulaCell(nRow1, nRow2))
1986         return false;
1987 
1988     bool bAnyDirty = false, bTmp = false;
1989     lcl_EvalDirty(maCells, nRow1, nRow2, GetDoc(), nullptr, false, bSkipRunning, bAnyDirty, bTmp);
1990     return bAnyDirty;
1991 }
1992 
1993 bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1, SCROW nRow2, const ScFormulaCellGroupRef& mxGroup )
1994 {
1995     if (nRow1 > nRow2)
1996         return false;
1997 
1998     bool bAllowThreading = true, bTmp = false;
1999     lcl_EvalDirty(maCells, nRow1, nRow2, GetDoc(), mxGroup, true, false, bTmp, bAllowThreading);
2000 
2001     return bAllowThreading;
2002 }
2003 
2004 namespace {
2005 
2006 class StoreToCacheFunc
2007 {
2008     SvStream& mrStrm;
2009 public:
2010 
2011     StoreToCacheFunc(SvStream& rStrm):
2012         mrStrm(rStrm)
2013     {
2014     }
2015 
2016     void operator() ( const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize )
2017     {
2018         SCROW nStartRow = node.position + nOffset;
2019         mrStrm.WriteUInt64(nStartRow);
2020         mrStrm.WriteUInt64(nDataSize);
2021         switch (node.type)
2022         {
2023             case sc::element_type_empty:
2024             {
2025                 mrStrm.WriteUChar(0);
2026             }
2027             break;
2028             case sc::element_type_numeric:
2029             {
2030                 mrStrm.WriteUChar(1);
2031                 sc::numeric_block::const_iterator it = sc::numeric_block::begin(*node.data);
2032                 std::advance(it, nOffset);
2033                 sc::numeric_block::const_iterator itEnd = it;
2034                 std::advance(itEnd, nDataSize);
2035 
2036                 for (; it != itEnd; ++it)
2037                 {
2038                     mrStrm.WriteDouble(*it);
2039                 }
2040             }
2041             break;
2042             case sc::element_type_string:
2043             {
2044                 mrStrm.WriteUChar(2);
2045                 sc::string_block::const_iterator it = sc::string_block::begin(*node.data);
2046                 std::advance(it, nOffset);
2047                 sc::string_block::const_iterator itEnd = it;
2048                 std::advance(itEnd, nDataSize);
2049 
2050                 for (; it != itEnd; ++it)
2051                 {
2052                     OString aStr = OUStringToOString(it->getString(), RTL_TEXTENCODING_UTF8);
2053                     sal_Int32 nStrLength = aStr.getLength();
2054                     mrStrm.WriteInt32(nStrLength);
2055                     mrStrm.WriteOString(aStr);
2056                 }
2057             }
2058             break;
2059             case sc::element_type_formula:
2060             {
2061                 mrStrm.WriteUChar(3);
2062                 sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
2063                 std::advance(it, nOffset);
2064                 sc::formula_block::const_iterator itEnd = it;
2065                 std::advance(itEnd, nDataSize);
2066 
2067                 for (; it != itEnd; /* incrementing through std::advance*/)
2068                 {
2069                     const ScFormulaCell* pCell = *it;
2070                     OUString aFormula = pCell->GetFormula(formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1);
2071                     const auto& xCellGroup = pCell->GetCellGroup();
2072                     sal_uInt64 nGroupLength = 0;
2073                     if (xCellGroup)
2074                     {
2075                         nGroupLength = xCellGroup->mnLength;
2076                     }
2077                     else
2078                     {
2079                         nGroupLength = 1;
2080                     }
2081                     mrStrm.WriteUInt64(nGroupLength);
2082                     mrStrm.WriteInt32(aFormula.getLength());
2083                     mrStrm.WriteOString(OUStringToOString(aFormula, RTL_TEXTENCODING_UTF8));
2084 
2085                     // incrementing the iterator
2086                     std::advance(it, nGroupLength);
2087                 }
2088             }
2089             break;
2090         }
2091     }
2092 };
2093 
2094 }
2095 
2096 void ScColumn::StoreToCache(SvStream& rStrm) const
2097 {
2098     rStrm.WriteUInt64(nCol);
2099     SCROW nLastRow = GetLastDataPos();
2100     rStrm.WriteUInt64(nLastRow + 1); // the rows are zero based
2101 
2102     StoreToCacheFunc aFunc(rStrm);
2103     sc::ParseBlock(maCells.begin(), maCells, aFunc, SCROW(0), nLastRow);
2104 }
2105 
2106 void ScColumn::RestoreFromCache(SvStream& rStrm)
2107 {
2108     sal_uInt64 nStoredCol = 0;
2109     rStrm.ReadUInt64(nStoredCol);
2110     if (nStoredCol != static_cast<sal_uInt64>(nCol))
2111         throw std::exception();
2112 
2113     sal_uInt64 nLastRow = 0;
2114     rStrm.ReadUInt64(nLastRow);
2115     sal_uInt64 nReadRow = 0;
2116     ScDocument& rDocument = GetDoc();
2117     while (nReadRow < nLastRow)
2118     {
2119         sal_uInt64 nStartRow = 0;
2120         sal_uInt64 nDataSize = 0;
2121         rStrm.ReadUInt64(nStartRow);
2122         rStrm.ReadUInt64(nDataSize);
2123         sal_uInt8 nType = 0;
2124         rStrm.ReadUChar(nType);
2125         switch (nType)
2126         {
2127             case 0:
2128                 // nothing to do
2129                 maCells.set_empty(nStartRow, nDataSize);
2130             break;
2131             case 1:
2132             {
2133                 // nDataSize double values
2134                 std::vector<double> aValues(nDataSize);
2135                 for (auto& rValue : aValues)
2136                 {
2137                     rStrm.ReadDouble(rValue);
2138                 }
2139                 maCells.set(nStartRow, aValues.begin(), aValues.end());
2140             }
2141             break;
2142             case 2:
2143             {
2144                 std::vector<svl::SharedString> aStrings(nDataSize);
2145                 svl::SharedStringPool& rPool = rDocument.GetSharedStringPool();
2146                 for (auto& rString : aStrings)
2147                 {
2148                     sal_Int32 nStrLength = 0;
2149                     rStrm.ReadInt32(nStrLength);
2150                     std::unique_ptr<char[]> pStr(new char[nStrLength]);
2151                     rStrm.ReadBytes(pStr.get(), nStrLength);
2152                     OString aOStr(pStr.get(), nStrLength);
2153                     OUString aStr = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8);
2154                     rString = rPool.intern(aStr);
2155                 }
2156                 maCells.set(nStartRow, aStrings.begin(), aStrings.end());
2157 
2158             }
2159             break;
2160             case 3:
2161             {
2162                 std::vector<ScFormulaCell*> aFormulaCells(nDataSize);
2163 
2164                 ScAddress aAddr(nCol, nStartRow, nTab);
2165                 const formula::FormulaGrammar::Grammar eGrammar = formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1;
2166                 for (SCROW nRow = 0; nRow < static_cast<SCROW>(nDataSize);)
2167                 {
2168                     sal_uInt64 nFormulaGroupSize = 0;
2169                     rStrm.ReadUInt64(nFormulaGroupSize);
2170                     sal_Int32 nStrLength = 0;
2171                     rStrm.ReadInt32(nStrLength);
2172                     std::unique_ptr<char[]> pStr(new char[nStrLength]);
2173                     rStrm.ReadBytes(pStr.get(), nStrLength);
2174                     OString aOStr(pStr.get(), nStrLength);
2175                     OUString aStr = OStringToOUString(aOStr, RTL_TEXTENCODING_UTF8);
2176                     for (sal_uInt64 i = 0; i < nFormulaGroupSize; ++i)
2177                     {
2178                         aFormulaCells[nRow + i] = new ScFormulaCell(rDocument, aAddr, aStr, eGrammar);
2179                         aAddr.IncRow();
2180                     }
2181 
2182                     nRow += nFormulaGroupSize;
2183                 }
2184 
2185                 maCells.set(nStartRow, aFormulaCells.begin(), aFormulaCells.end());
2186             }
2187             break;
2188         }
2189 
2190         nReadRow += nDataSize;
2191     }
2192 }
2193 
2194 void ScColumn::CheckIntegrity() const
2195 {
2196     const ScColumn* pColTest = maCells.event_handler().getColumn();
2197 
2198     if (pColTest != this)
2199     {
2200         std::ostringstream os;
2201         os << "cell store's event handler references wrong column instance (this=" << this
2202             << "; stored=" << pColTest << ")";
2203         throw std::runtime_error(os.str());
2204     }
2205 
2206     size_t nCount = std::count_if(maCells.cbegin(), maCells.cend(),
2207         [](const auto& blk) { return blk.type == sc::element_type_formula; }
2208     );
2209 
2210     if (mnBlkCountFormula != nCount)
2211     {
2212         std::ostringstream os;
2213         os << "incorrect cached formula block count (expected=" << nCount << "; actual="
2214             << mnBlkCountFormula << ")";
2215         throw std::runtime_error(os.str());
2216     }
2217 }
2218 
2219 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2220