1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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>
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>
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>
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
;
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;
66 // shrink the size to avoid exceeding the specified last row position.
67 nSize
-= nLastRow
- nRow2
;
71 // this block is of size one.
73 return sc::MultiDataCellState::HasMultipleCells
;
81 // size of this block is greater than one.
84 return sc::MultiDataCellState::HasMultipleCells
;
88 nRow
+= it
->size
- nOffset
;
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
))
103 sc::ColumnBlockPosition
* pBlockPos
= rCxt
.getBlockPosition(nTab
, nCol
);
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();
145 pCondList
->DeleteArea(nCol
, aRange
.mnRow1
, nCol
, aRange
.mnRow2
);
147 else if ((nDelFlag
& InsertDeleteFlags::HARDATTR
) == InsertDeleteFlags::HARDATTR
)
148 pAttrArray
->DeleteHardAttr(aRange
.mnRow1
, aRange
.mnRow2
);
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
);
165 // All cells in the range in the clip are empty. Nothing to delete.
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;
174 for (const sc::RowSpan
& r
: aSpans
)
176 SCROW nDestRow1
= r
.mnRow1
+ nDestOffset
;
177 SCROW nDestRow2
= r
.mnRow2
+ nDestOffset
;
179 if (nDestRow1
> aRange
.mnRow2
)
186 if (nDestRow2
> aRange
.mnRow2
)
188 // Truncate this range, and set it as the last span.
189 nDestRow2
= aRange
.mnRow2
;
193 aDestSpans
.emplace_back(nDestRow1
, nDestRow2
);
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();
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
);
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())
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();
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();
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();
322 case CELLTYPE_FORMULA
:
324 std::vector
<sc::RowSpan
> aRanges
;
326 aRanges
.emplace_back(nRow1
, nRow2
);
327 CloneFormulaCell(*pBlockPos
, *rSrcCell
.getFormula(), rSrcAttr
, aRanges
);
335 ScAddress
aDestPosition(nCol
, nRow1
, nTab
);
337 duplicateSparkline(rCxt
, pBlockPos
, nColOffset
, nDestSize
, aDestPosition
);
340 const ScPostIt
* pNote
= rCxt
.getSingleCellNote(nColOffset
);
341 if (!(pNote
&& (nFlags
& (InsertDeleteFlags::NOTE
| InsertDeleteFlags::ADDNOTES
)) != InsertDeleteFlags::NONE
))
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
=
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
)
376 auto pSparkline
= rContext
.getSingleSparkline(nColOffset
);
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
))
404 SCROW nLastRow
= nRow
+ rVals
.size() - 1;
405 if (nLastRow
> GetDoc().MaxRow())
406 // Out of bound. Do nothing.
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
)
426 BroadcastCells(aRows
, SfxHintId::ScDataChanged
);
429 void ScColumn::TransferCellValuesTo( SCROW nRow
, size_t nLen
, sc::CellValues
& rDest
)
431 if (!GetDoc().ValidRow(nRow
))
434 SCROW nLastRow
= nRow
+ nLen
- 1;
435 if (nLastRow
> GetDoc().MaxRow())
436 // Out of bound. Do nothing.
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
;
448 for (SCROW i
= nRow
; i
<= nLastRow
; ++i
)
451 BroadcastCells(aRows
, SfxHintId::ScDataChanged
);
454 void ScColumn::CopyCellValuesFrom( SCROW nRow
, const sc::CellValues
& rSrc
)
456 if (!GetDoc().ValidRow(nRow
))
459 SCROW nLastRow
= nRow
+ rSrc
.size() - 1;
460 if (nLastRow
> GetDoc().MaxRow())
461 // Out of bound. Do nothing
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
)
476 BroadcastCells(aRows
, SfxHintId::ScDataChanged
);
481 class ConvertFormulaToValueHandler
483 sc::CellValues maResValues
;
488 ConvertFormulaToValueHandler(ScDocument
& rDoc
) :
492 maResValues
.reset(mrDoc
.GetSheetLimits().GetMaxRowCount());
495 void operator() ( size_t nRow
, const ScFormulaCell
* pCell
)
497 sc::FormulaResultValue aRes
= pCell
->GetResult();
500 case sc::FormulaResultValue::Value
:
501 maResValues
.setValue(nRow
, aRes
.mfValue
);
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
));
511 maResValues
.setValue(nRow
, aRes
.maString
);
514 case sc::FormulaResultValue::Error
:
515 case sc::FormulaResultValue::Invalid
:
517 maResValues
.setValue(nRow
, svl::SharedString::getEmptyString());
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
)
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.
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);
557 pUndo
->swap(nTab
, nCol
, aUndoCells
);
562 class StartListeningHandler
564 sc::StartListeningContext
& mrCxt
;
567 explicit StartListeningHandler( sc::StartListeningContext
& rCxt
) :
570 void operator() (size_t /*nRow*/, ScFormulaCell
* pCell
)
572 pCell
->StartListeningTo(mrCxt
);
576 class EndListeningHandler
578 sc::EndListeningContext
& mrCxt
;
581 explicit EndListeningHandler( sc::EndListeningContext
& 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;
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
);
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
);
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
))
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
)
740 return rCount
+ rCellNote
.size
;
746 class NoteCaptionCreator
750 NoteCaptionCreator( SCTAB nTab
, SCCOL nCol
) : maPos(nCol
,0,nTab
) {}
752 void operator() ( size_t nRow
, const ScPostIt
* p
)
755 p
->GetOrCreateCaption(maPos
);
759 class NoteCaptionCleaner
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())
784 if (!GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
))
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.
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
;
818 class NoteEntryCollector
820 std::vector
<sc::NoteEntry
>& mrNotes
;
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
)
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
);
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
);
849 ScAddress
aPos(mnCol
, nTopRow
+ nOffset
, mnTab
);
850 mrNotes
.emplace_back(aPos
, *it
);
857 void ScColumn::GetAllNoteEntries( std::vector
<sc::NoteEntry
>& rNotes
) const
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.
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.
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
)
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
));
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
))
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();
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();
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
));
945 class RecompileByOpcodeHandler
948 const formula::unordered_opcode_set
& mrOps
;
949 sc::EndListeningContext
& mrEndListenCxt
;
950 sc::CompileFormulaContext
& mrCompileFormulaCxt
;
953 RecompileByOpcodeHandler(
954 ScDocument
* pDoc
, const formula::unordered_opcode_set
& rOps
,
955 sc::EndListeningContext
& rEndListenCxt
, sc::CompileFormulaContext
& rCompileCxt
) :
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;
970 // Only inspect the code from the top cell.
971 pTop
= *rEntry
.mpCells
;
974 pTop
= rEntry
.mpCell
;
976 ScTokenArray
* pCode
= pTop
->GetCode();
977 bool bRecompile
= pCode
->HasOpCodes(mrOps
);
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);
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
);
1004 rEntry
.mpCell
->EndListeningTo(mrEndListenCxt
);
1005 mpDoc
->RemoveFromFormulaTree(rEntry
.mpCell
);
1009 pTop
->SetHybridFormula(aFormula
, mpDoc
->GetGrammar());
1013 class CompileHybridFormulaHandler
1016 sc::StartListeningContext
& mrStartListenCxt
;
1017 sc::CompileFormulaContext
& mrCompileFormulaCxt
;
1020 CompileHybridFormulaHandler(ScDocument
& rDoc
, sc::StartListeningContext
& rStartListenCxt
, sc::CompileFormulaContext
& rCompileCxt
) :
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();
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
);
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
);
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
;
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
;
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
);
1122 class ScriptTypeUpdater
1125 sc::CellTextAttrStoreType
& mrTextAttrs
;
1126 sc::CellTextAttrStoreType::iterator miPosAttr
;
1127 ScConditionalFormatList
* mpCFList
;
1128 SvNumberFormatter
* mpFormatter
;
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
)
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.
1146 const ScPatternAttr
* pPat
= mrCol
.GetPattern(nRow
);
1148 // In theory this should never return NULL. But let's be safe.
1151 const SfxItemSet
* pCondSet
= nullptr;
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
);
1169 explicit ScriptTypeUpdater( ScColumn
& 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()),
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
)
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();
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
);
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();
1267 class FormulaColPosSetter
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
);
1284 pCell
->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos
, pCell
->aPos
);
1286 pCell
->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos
, pCell
->aPos
);
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
);
1306 class RelativeRefBoundChecker
1308 std::vector
<SCROW
> maBounds
;
1309 ScRange maBoundRange
;
1312 explicit RelativeRefBoundChecker( const ScRange
& rBoundRange
) :
1313 maBoundRange(rBoundRange
) {}
1315 void operator() ( size_t /*nRow*/, ScFormulaCell
* pCell
)
1317 if (!pCell
->IsSharedTop())
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.
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
);
1348 maCells
.begin(), maCells
, rBoundRange
.aStart
.Row(), rBoundRange
.aEnd
.Row(), aFunc
);
1349 aFunc
.swapBounds(aBounds
);
1350 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells
, aBounds
);
1355 class ListenerCollector
1357 std::vector
<SvtListener
*>& mrListeners
;
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
;
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
))
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
))
1397 FormulaCellCollector
aFunc(rCells
);
1398 sc::ProcessFormula(maCells
.begin(), maCells
, nRow1
, nRow2
, aFunc
);
1401 bool ScColumn::HasFormulaCell() const
1403 return mnBlkCountFormula
!= 0;
1408 struct FindAnyFormula
1410 bool operator() ( size_t /*nRow*/, const ScFormulaCell
* /*pCell*/ ) const
1418 bool ScColumn::HasFormulaCell( SCROW nRow1
, SCROW nRow2
) const
1420 if (!mnBlkCountFormula
)
1423 if (nRow2
< nRow1
|| !GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
))
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();
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
;
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.
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
)
1483 endListening(mrEndCxt
, pp
, ppBeg
);
1484 mnStartRow
-= nBackTrackSize
;
1488 for (; pp
!= ppEnd
; ++pp
)
1492 if (!pFC
->IsSharedTop())
1494 assert(!pFC
->IsShared());
1495 pFC
->StartListeningTo(mrStartCxt
);
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.
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
;
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.
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
)
1560 mnStartRow
-= nBackTrackSize
;
1564 for (; pp
!= ppEnd
; ++pp
)
1568 if (!pFC
->IsSharedTop())
1570 assert(!pFC
->IsShared());
1571 pFC
->EndListeningTo(mrEndCxt
);
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.
1589 // Move to the last one in the group.
1590 pp
+= pFC
->GetSharedLength() - 1;
1595 SCROW
getStartRow() const
1600 SCROW
getEndRow() const
1608 void ScColumn::StartListeningFormulaCells(
1609 sc::StartListeningContext
& rStartCxt
, sc::EndListeningContext
& rEndCxt
,
1610 SCROW nRow1
, SCROW nRow2
)
1612 if (!HasFormulaCell())
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())
1626 EndListeningFormulaCellsHandler
aFunc(rCxt
);
1627 sc::ProcessBlock(maCells
.begin(), maCells
, aFunc
, nRow1
, nRow2
);
1630 *pStartRow
= aFunc
.getStartRow();
1633 *pEndRow
= aFunc
.getEndRow();
1636 void ScColumn::EndListeningIntersectedGroup(
1637 sc::EndListeningContext
& rCxt
, SCROW nRow
, std::vector
<ScAddress
>* pGroupPos
)
1639 if (!GetDoc().ValidRow(nRow
))
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.
1648 ScFormulaCell
* pFC
= sc::formula_block::at(*it
->data
, aPos
.second
);
1649 ScFormulaCellGroupRef xGroup
= pFC
->GetCellGroup();
1651 // Not a formula group.
1655 pFC
->EndListeningTo(rCxt
);
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();
1682 if (!pFC
->IsSharedTop())
1684 pFC
->EndListeningTo(rCxt
);
1687 // Record the position of the top cell of the group.
1688 pGroupPos
->push_back(xGroup
->mpTopCell
->aPos
);
1692 aPos
= maCells
.position(it
, nRow2
);
1694 if (it
->type
!= sc::element_type_formula
)
1697 ScFormulaCell
* pFC
= sc::formula_block::at(*it
->data
, aPos
.second
);
1698 ScFormulaCellGroupRef xGroup
= pFC
->GetCellGroup();
1702 if (!pFC
->IsSharedTop())
1704 pFC
->EndListeningTo(rCxt
);
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.
1722 ScFormulaCell
** pp
= &sc::formula_block::at(*aPos
.first
->data
, aPos
.second
);
1724 ScFormulaCellGroupRef xGroup
= (*pp
)->GetCellGroup();
1727 // not a formula group.
1728 (*pp
)->EndListeningTo(rCxt
);
1732 // Move back to the top cell.
1733 SCROW nTopDelta
= (*pp
)->aPos
.Row() - xGroup
->mpTopCell
->aPos
.Row();
1734 assert(nTopDelta
>= 0);
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.
1752 ScFormulaCell
** pp
= &sc::formula_block::at(*aPos
.first
->data
, aPos
.second
);
1754 ScFormulaCellGroupRef xGroup
= (*pp
)->GetCellGroup();
1757 // not a formula group.
1758 (*pp
)->SetNeedsListening(true);
1762 // Move back to the top cell.
1763 SCROW nTopDelta
= (*pp
)->aPos
.Row() - xGroup
->mpTopCell
->aPos
.Row();
1764 assert(nTopDelta
>= 0);
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
)
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
;
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.
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;
1828 if (!bGroupInterpreted
)
1830 // Evaluate from second cell in non-grouped style (no point in trying group-interpret again).
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;
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;
1854 pCellStart
= nullptr; // For next sub span start detection.
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
;
1873 for (;it
!= rCells
.end() && nRow
<= nRow2
; ++it
, nOffset
= 0)
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;
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
1903 if (bThreadingDepEval
&& pChildTopCell
->GetSeenInPath())
1905 ScFormulaGroupCycleCheckGuard
aCycleCheckGuard(rRecursionHelper
, pChildTopCell
);
1906 bAllowThreading
= false;
1910 if (bSkipRunning
&& (*itCell
)->IsRunning())
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
)
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
;
1939 nRowsToRead
-= nSpanLen
;
1943 // No formula-group here.
1944 bool bDirtyFlag
= false;
1945 if( (*itCell
)->NeedsInterpret())
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;
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.
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;
1975 // update the counters by 1.
1986 nRow
+= it
->size
- nOffset
;
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
)
2002 if (!HasFormulaCell(nRow1
, nRow2
))
2005 bool bAnyDirty
= false, bTmp
= false;
2006 lcl_EvalDirty(maCells
, nRow1
, nRow2
, GetDoc(), nullptr, false, bSkipRunning
, bAnyDirty
, bTmp
, nullptr);
2010 bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1
, SCROW nRow2
, const ScFormulaCellGroupRef
& mxGroup
, ScAddress
* pDirtiedAddress
)
2015 bool bAllowThreading
= true, bTmp
= false;
2016 lcl_EvalDirty(maCells
, nRow1
, nRow2
, GetDoc(), mxGroup
, true, false, bTmp
, bAllowThreading
, pDirtiedAddress
);
2018 return bAllowThreading
;
2023 class StoreToCacheFunc
2028 StoreToCacheFunc(SvStream
& 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
);
2040 case sc::element_type_empty
:
2042 mrStrm
.WriteUChar(0);
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
);
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
);
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;
2092 nGroupLength
= xCellGroup
->mnLength
;
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
);
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
);
2146 maCells
.set_empty(nStartRow
, nDataSize
);
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());
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());
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
);
2199 nRow
+= nFormulaGroupSize
;
2202 maCells
.set(nStartRow
, aFormulaCells
.begin(), aFormulaCells
.end());
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
; }
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
)
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
);
2277 rLisStore
.emplace_back(pFC
);
2279 rLisStore
.emplace_back(pLis
);
2285 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */