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 <conditio.hxx>
23 #include <formulagroup.hxx>
24 #include <tokenarray.hxx>
25 #include <scitems.hxx>
26 #include <cellform.hxx>
27 #include <sharedformula.hxx>
28 #include <drwlayer.hxx>
29 #include <compiler.hxx>
30 #include <recursionhelper.hxx>
32 #include <broadcast.hxx>
34 #include <SparklineGroup.hxx>
36 #include <o3tl/safeint.hxx>
37 #include <svl/sharedstringpool.hxx>
38 #include <sal/log.hxx>
39 #include <tools/stream.hxx>
45 sc::MultiDataCellState::StateType
ScColumn::HasDataCellsInRange(
46 SCROW nRow1
, SCROW nRow2
, SCROW
* pRow1
) const
48 sc::CellStoreType::const_position_type aPos
= maCells
.position(nRow1
);
49 sc::CellStoreType::const_iterator it
= aPos
.first
;
50 size_t nOffset
= aPos
.second
;
52 bool bHasOne
= false; // whether or not we have found a non-empty block of size one.
54 for (; it
!= maCells
.end() && nRow
<= nRow2
; ++it
)
56 if (it
->type
!= sc::element_type_empty
)
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
;
62 SCROW nLastRow
= nRow
+ nSize
- 1;
64 // shrink the size to avoid exceeding the specified last row position.
65 nSize
-= nLastRow
- nRow2
;
69 // this block is of size one.
71 return sc::MultiDataCellState::HasMultipleCells
;
79 // size of this block is greater than one.
82 return sc::MultiDataCellState::HasMultipleCells
;
86 nRow
+= it
->size
- nOffset
;
90 return bHasOne
? sc::MultiDataCellState::HasOneCell
: sc::MultiDataCellState::Empty
;
93 void ScColumn::DeleteBeforeCopyFromClip(
94 sc::CopyFromClipContext
& rCxt
, const ScColumn
& rClipCol
, sc::ColumnSpanSet
& rBroadcastSpans
)
96 ScDocument
& rDocument
= GetDoc();
97 sc::CopyFromClipContext::Range aRange
= rCxt
.getDestRange();
98 if (!rDocument
.ValidRow(aRange
.mnRow1
) || !rDocument
.ValidRow(aRange
.mnRow2
))
101 sc::ColumnBlockPosition
* pBlockPos
= rCxt
.getBlockPosition(nTab
, nCol
);
105 InsertDeleteFlags nDelFlag
= rCxt
.getDeleteFlag();
107 if (!rCxt
.isSkipEmptyCells())
109 // Delete the whole destination range.
111 if (nDelFlag
& InsertDeleteFlags::CONTENTS
)
113 auto xResult
= DeleteCells(*pBlockPos
, aRange
.mnRow1
, aRange
.mnRow2
, nDelFlag
);
114 rBroadcastSpans
.set(GetDoc(), nTab
, nCol
, xResult
->aDeletedRows
, true);
116 for (const auto& rRange
: xResult
->aFormulaRanges
)
117 rCxt
.setListeningFormulaSpans(
118 nTab
, nCol
, rRange
.first
, nCol
, rRange
.second
);
121 if (nDelFlag
& InsertDeleteFlags::NOTE
)
122 DeleteCellNotes(*pBlockPos
, aRange
.mnRow1
, aRange
.mnRow2
, false);
124 if (nDelFlag
& InsertDeleteFlags::SPARKLINES
)
125 DeleteSparklineCells(*pBlockPos
, aRange
.mnRow1
, aRange
.mnRow2
);
127 if (nDelFlag
& InsertDeleteFlags::EDITATTR
)
128 RemoveEditAttribs(*pBlockPos
, aRange
.mnRow1
, aRange
.mnRow2
);
130 if (nDelFlag
& InsertDeleteFlags::ATTRIB
)
132 pAttrArray
->DeleteArea(aRange
.mnRow1
, aRange
.mnRow2
);
134 if (rCxt
.isTableProtected())
136 ScPatternAttr
aPattern(rDocument
.getCellAttributeHelper());
137 aPattern
.GetItemSet().Put(ScProtectionAttr(false));
138 ApplyPatternArea(aRange
.mnRow1
, aRange
.mnRow2
, aPattern
);
141 ScConditionalFormatList
* pCondList
= rCxt
.getCondFormatList();
143 pCondList
->DeleteArea(nCol
, aRange
.mnRow1
, nCol
, aRange
.mnRow2
);
145 else if ((nDelFlag
& InsertDeleteFlags::HARDATTR
) == InsertDeleteFlags::HARDATTR
)
146 pAttrArray
->DeleteHardAttr(aRange
.mnRow1
, aRange
.mnRow2
);
151 ScRange aClipRange
= rCxt
.getClipDoc()->GetClipParam().getWholeRange();
152 SCROW nClipRow1
= aClipRange
.aStart
.Row();
153 SCROW nClipRow2
= aClipRange
.aEnd
.Row();
154 SCROW nClipRowLen
= nClipRow2
- nClipRow1
+ 1;
156 // Check for non-empty cell ranges in the clip column.
157 sc::SingleColumnSpanSet
aSpanSet(GetDoc().GetSheetLimits());
158 aSpanSet
.scan(rClipCol
, nClipRow1
, nClipRow2
);
159 sc::SingleColumnSpanSet::SpansType aSpans
;
160 aSpanSet
.getSpans(aSpans
);
163 // All cells in the range in the clip are empty. Nothing to delete.
166 // Translate the clip column spans into the destination column, and repeat as needed.
167 std::vector
<sc::RowSpan
> aDestSpans
;
168 SCROW nDestOffset
= aRange
.mnRow1
- nClipRow1
;
169 bool bContinue
= true;
172 for (const sc::RowSpan
& r
: aSpans
)
174 SCROW nDestRow1
= r
.mnRow1
+ nDestOffset
;
175 SCROW nDestRow2
= r
.mnRow2
+ nDestOffset
;
177 if (nDestRow1
> aRange
.mnRow2
)
184 if (nDestRow2
> aRange
.mnRow2
)
186 // Truncate this range, and set it as the last span.
187 nDestRow2
= aRange
.mnRow2
;
191 aDestSpans
.emplace_back(nDestRow1
, nDestRow2
);
197 nDestOffset
+= nClipRowLen
;
200 for (const auto& rDestSpan
: aDestSpans
)
202 SCROW nRow1
= rDestSpan
.mnRow1
;
203 SCROW nRow2
= rDestSpan
.mnRow2
;
205 if (nDelFlag
& InsertDeleteFlags::CONTENTS
)
207 auto xResult
= DeleteCells(*pBlockPos
, nRow1
, nRow2
, nDelFlag
);
208 rBroadcastSpans
.set(GetDoc(), nTab
, nCol
, xResult
->aDeletedRows
, true);
210 for (const auto& rRange
: xResult
->aFormulaRanges
)
211 rCxt
.setListeningFormulaSpans(
212 nTab
, nCol
, rRange
.first
, nCol
, rRange
.second
);
215 if (nDelFlag
& InsertDeleteFlags::NOTE
)
216 DeleteCellNotes(*pBlockPos
, nRow1
, nRow2
, false);
218 if (nDelFlag
& InsertDeleteFlags::SPARKLINES
)
219 DeleteSparklineCells(*pBlockPos
, nRow1
, nRow2
);
221 if (nDelFlag
& InsertDeleteFlags::EDITATTR
)
222 RemoveEditAttribs(*pBlockPos
, nRow1
, nRow2
);
224 // Delete attributes just now
225 if (nDelFlag
& InsertDeleteFlags::ATTRIB
)
227 pAttrArray
->DeleteArea(nRow1
, nRow2
);
229 if (rCxt
.isTableProtected())
231 ScPatternAttr
aPattern(rDocument
.getCellAttributeHelper());
232 aPattern
.GetItemSet().Put(ScProtectionAttr(false));
233 ApplyPatternArea(nRow1
, nRow2
, aPattern
);
236 ScConditionalFormatList
* pCondList
= rCxt
.getCondFormatList();
238 pCondList
->DeleteArea(nCol
, nRow1
, nCol
, nRow2
);
240 else if ((nDelFlag
& InsertDeleteFlags::HARDATTR
) == InsertDeleteFlags::HARDATTR
)
241 pAttrArray
->DeleteHardAttr(nRow1
, nRow2
);
245 void ScColumn::CopyOneCellFromClip( sc::CopyFromClipContext
& rCxt
, SCROW nRow1
, SCROW nRow2
, size_t nColOffset
)
247 assert(nRow1
<= nRow2
);
249 size_t nDestSize
= nRow2
- nRow1
+ 1;
250 sc::ColumnBlockPosition
* pBlockPos
= rCxt
.getBlockPosition(nTab
, nCol
);
254 ScDocument
& rDocument
= GetDoc();
255 bool bSameDocPool
= (rCxt
.getClipDoc()->GetPool() == rDocument
.GetPool());
257 ScCellValue
& rSrcCell
= rCxt
.getSingleCell(nColOffset
);
258 sc::CellTextAttr
& rSrcAttr
= rCxt
.getSingleCellAttr(nColOffset
);
260 InsertDeleteFlags nFlags
= rCxt
.getInsertFlag();
262 if ((nFlags
& InsertDeleteFlags::ATTRIB
) != InsertDeleteFlags::NONE
)
264 if (!rCxt
.isSkipEmptyCells() || rSrcCell
.getType() != CELLTYPE_NONE
)
266 CellAttributeHolder aNewPattern
;
268 aNewPattern
.setScPatternAttr(rCxt
.getSingleCellPattern(nColOffset
));
270 aNewPattern
= rCxt
.getSingleCellPattern(nColOffset
)->MigrateToDocument( &rDocument
, rCxt
.getClipDoc());
272 pAttrArray
->SetPatternArea(nRow1
, nRow2
, aNewPattern
);
276 if ((nFlags
& InsertDeleteFlags::CONTENTS
) != InsertDeleteFlags::NONE
)
278 std::vector
<sc::CellTextAttr
> aTextAttrs(nDestSize
, rSrcAttr
);
280 switch (rSrcCell
.getType())
284 std::vector
<double> aVals(nDestSize
, rSrcCell
.getDouble());
285 pBlockPos
->miCellPos
=
286 maCells
.set(pBlockPos
->miCellPos
, nRow1
, aVals
.begin(), aVals
.end());
287 pBlockPos
->miCellTextAttrPos
=
288 maCellTextAttrs
.set(pBlockPos
->miCellTextAttrPos
, nRow1
, aTextAttrs
.begin(), aTextAttrs
.end());
289 CellStorageModified();
292 case CELLTYPE_STRING
:
294 // Compare the ScDocumentPool* to determine if we are copying within the
295 // same document. If not, re-intern shared strings.
296 svl::SharedStringPool
* pSharedStringPool
= (bSameDocPool
? nullptr : &rDocument
.GetSharedStringPool());
297 svl::SharedString aStr
= (pSharedStringPool
?
298 pSharedStringPool
->intern( rSrcCell
.getSharedString()->getString()) :
299 *rSrcCell
.getSharedString());
301 std::vector
<svl::SharedString
> aStrs(nDestSize
, aStr
);
302 pBlockPos
->miCellPos
=
303 maCells
.set(pBlockPos
->miCellPos
, nRow1
, aStrs
.begin(), aStrs
.end());
304 pBlockPos
->miCellTextAttrPos
=
305 maCellTextAttrs
.set(pBlockPos
->miCellTextAttrPos
, nRow1
, aTextAttrs
.begin(), aTextAttrs
.end());
306 CellStorageModified();
311 std::vector
<EditTextObject
*> aStrs
;
312 aStrs
.reserve(nDestSize
);
313 for (size_t i
= 0; i
< nDestSize
; ++i
)
314 aStrs
.push_back(rSrcCell
.getEditText()->Clone().release());
316 pBlockPos
->miCellPos
=
317 maCells
.set(pBlockPos
->miCellPos
, nRow1
, aStrs
.begin(), aStrs
.end());
318 pBlockPos
->miCellTextAttrPos
=
319 maCellTextAttrs
.set(pBlockPos
->miCellTextAttrPos
, nRow1
, aTextAttrs
.begin(), aTextAttrs
.end());
320 CellStorageModified();
323 case CELLTYPE_FORMULA
:
325 std::vector
<sc::RowSpan
> aRanges
;
327 aRanges
.emplace_back(nRow1
, nRow2
);
328 CloneFormulaCell(*pBlockPos
, *rSrcCell
.getFormula(), rSrcAttr
, aRanges
);
336 ScAddress
aDestPosition(nCol
, nRow1
, nTab
);
338 duplicateSparkline(rCxt
, pBlockPos
, nColOffset
, nDestSize
, aDestPosition
);
341 const ScPostIt
* pNote
= rCxt
.getSingleCellNote(nColOffset
);
342 if (!(pNote
&& (nFlags
& (InsertDeleteFlags::NOTE
| InsertDeleteFlags::ADDNOTES
)) != InsertDeleteFlags::NONE
))
345 // Duplicate the cell note over the whole pasted range.
347 ScDocument
* pClipDoc
= rCxt
.getClipDoc();
348 const ScAddress aSrcPos
= pClipDoc
->GetClipParam().getWholeRange().aStart
;
349 std::vector
<ScPostIt
*> aNotes
;
350 aNotes
.reserve(nDestSize
);
351 for (size_t i
= 0; i
< nDestSize
; ++i
)
353 bool bCloneCaption
= (nFlags
& InsertDeleteFlags::NOCAPTIONS
) == InsertDeleteFlags::NONE
;
354 aNotes
.push_back(pNote
->Clone(aSrcPos
, rDocument
, aDestPosition
, bCloneCaption
).release());
355 aDestPosition
.IncRow();
358 pBlockPos
->miCellNotePos
=
360 pBlockPos
->miCellNotePos
, nRow1
, aNotes
.begin(), aNotes
.end());
362 // Notify our LOK clients.
363 aDestPosition
.SetRow(nRow1
);
364 for (size_t i
= 0; i
< nDestSize
; ++i
)
366 ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Add
, rDocument
, aDestPosition
, aNotes
[i
]);
367 aDestPosition
.IncRow();
371 void ScColumn::duplicateSparkline(sc::CopyFromClipContext
& rContext
, sc::ColumnBlockPosition
* pBlockPos
,
372 size_t nColOffset
, size_t nDestSize
, ScAddress aDestPosition
)
374 if ((rContext
.getInsertFlag() & InsertDeleteFlags::SPARKLINES
) == InsertDeleteFlags::NONE
)
377 const auto& pSparkline
= rContext
.getSingleSparkline(nColOffset
);
380 auto const& pSparklineGroup
= pSparkline
->getSparklineGroup();
382 auto pDuplicatedGroup
= GetDoc().SearchSparklineGroup(pSparklineGroup
->getID());
383 if (!pDuplicatedGroup
)
384 pDuplicatedGroup
= std::make_shared
<sc::SparklineGroup
>(*pSparklineGroup
);
386 std::vector
<sc::SparklineCell
*> aSparklines(nDestSize
, nullptr);
387 ScAddress aCurrentPosition
= aDestPosition
;
388 for (size_t i
= 0; i
< nDestSize
; ++i
)
390 auto pNewSparkline
= std::make_shared
<sc::Sparkline
>(aCurrentPosition
.Col(), aCurrentPosition
.Row(), pDuplicatedGroup
);
391 pNewSparkline
->setInputRange(pSparkline
->getInputRange());
392 aSparklines
[i
] = new sc::SparklineCell(std::move(pNewSparkline
));
393 aCurrentPosition
.IncRow();
396 pBlockPos
->miSparklinePos
= maSparklines
.set(pBlockPos
->miSparklinePos
, aDestPosition
.Row(), aSparklines
.begin(), aSparklines
.end());
400 void ScColumn::SetValues( const SCROW nRow
, const std::vector
<double>& rVals
)
402 if (!GetDoc().ValidRow(nRow
))
405 SCROW nLastRow
= nRow
+ rVals
.size() - 1;
406 if (nLastRow
> GetDoc().MaxRow())
407 // Out of bound. Do nothing.
410 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
411 std::vector
<SCROW
> aNewSharedRows
;
412 DetachFormulaCells(aPos
, rVals
.size(), &aNewSharedRows
);
414 maCells
.set(nRow
, rVals
.begin(), rVals
.end());
415 std::vector
<sc::CellTextAttr
> aDefaults(rVals
.size());
416 maCellTextAttrs
.set(nRow
, aDefaults
.begin(), aDefaults
.end());
418 CellStorageModified();
420 StartListeningUnshared( aNewSharedRows
);
422 std::vector
<SCROW
> aRows
;
423 aRows
.reserve(rVals
.size());
424 for (SCROW i
= nRow
; i
<= nLastRow
; ++i
)
427 BroadcastCells(aRows
, SfxHintId::ScDataChanged
);
430 void ScColumn::TransferCellValuesTo( SCROW nRow
, size_t nLen
, sc::CellValues
& rDest
)
432 if (!GetDoc().ValidRow(nRow
))
435 SCROW nLastRow
= nRow
+ nLen
- 1;
436 if (nLastRow
> GetDoc().MaxRow())
437 // Out of bound. Do nothing.
440 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
441 DetachFormulaCells(aPos
, nLen
, nullptr);
443 rDest
.transferFrom(*this, nRow
, nLen
);
445 CellStorageModified();
447 std::vector
<SCROW
> aRows
;
449 for (SCROW i
= nRow
; i
<= nLastRow
; ++i
)
452 BroadcastCells(aRows
, SfxHintId::ScDataChanged
);
455 void ScColumn::CopyCellValuesFrom( SCROW nRow
, const sc::CellValues
& rSrc
)
457 if (!GetDoc().ValidRow(nRow
))
460 SCROW nLastRow
= nRow
+ rSrc
.size() - 1;
461 if (nLastRow
> GetDoc().MaxRow())
462 // Out of bound. Do nothing
465 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
466 DetachFormulaCells(aPos
, rSrc
.size(), nullptr);
468 rSrc
.copyTo(*this, nRow
);
470 CellStorageModified();
472 std::vector
<SCROW
> aRows
;
473 aRows
.reserve(rSrc
.size());
474 for (SCROW i
= nRow
; i
<= nLastRow
; ++i
)
477 BroadcastCells(aRows
, SfxHintId::ScDataChanged
);
482 class ConvertFormulaToValueHandler
484 sc::CellValues maResValues
;
489 ConvertFormulaToValueHandler(ScDocument
& rDoc
) :
493 maResValues
.reset(mrDoc
.GetSheetLimits().GetMaxRowCount());
496 void operator() ( size_t nRow
, const ScFormulaCell
* pCell
)
498 sc::FormulaResultValue aRes
= pCell
->GetResult();
501 case sc::FormulaResultValue::Value
:
502 maResValues
.setValue(nRow
, aRes
.mfValue
);
504 case sc::FormulaResultValue::String
:
505 if (aRes
.mbMultiLine
)
507 std::unique_ptr
<EditTextObject
> pObj(mrDoc
.CreateSharedStringTextObject(aRes
.maString
));
508 maResValues
.setValue(nRow
, std::move(pObj
));
512 maResValues
.setValue(nRow
, aRes
.maString
);
515 case sc::FormulaResultValue::Error
:
516 case sc::FormulaResultValue::Invalid
:
518 maResValues
.setValue(nRow
, svl::SharedString::getEmptyString());
524 bool isModified() const { return mbModified
; }
526 sc::CellValues
& getResValues() { return maResValues
; }
531 void ScColumn::ConvertFormulaToValue(
532 sc::EndListeningContext
& rCxt
, SCROW nRow1
, SCROW nRow2
, sc::TableValues
* pUndo
)
534 if (!GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
) || nRow1
> nRow2
)
537 std::vector
<SCROW
> aBounds
{ nRow1
};
538 if (nRow2
< GetDoc().MaxRow()-1)
539 aBounds
.push_back(nRow2
+1);
541 // Split formula cell groups at top and bottom boundaries (if applicable).
542 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells
, aBounds
);
544 // Parse all formulas within the range and store their results into temporary storage.
545 ConvertFormulaToValueHandler
aFunc(GetDoc());
546 sc::ParseFormula(maCells
.begin(), maCells
, nRow1
, nRow2
, aFunc
);
547 if (!aFunc
.isModified())
548 // No formula cells encountered.
551 DetachFormulaCells(rCxt
, nRow1
, nRow2
);
553 // Undo storage to hold static values which will get swapped to the cell storage later.
554 sc::CellValues aUndoCells
;
555 aFunc
.getResValues().swap(aUndoCells
);
556 aUndoCells
.swapNonEmpty(*this);
558 pUndo
->swap(nTab
, nCol
, aUndoCells
);
563 class StartListeningHandler
565 sc::StartListeningContext
& mrCxt
;
568 explicit StartListeningHandler( sc::StartListeningContext
& rCxt
) :
571 void operator() (size_t /*nRow*/, ScFormulaCell
* pCell
)
573 pCell
->StartListeningTo(mrCxt
);
577 class EndListeningHandler
579 sc::EndListeningContext
& mrCxt
;
582 explicit EndListeningHandler( sc::EndListeningContext
& rCxt
) :
585 void operator() (size_t /*nRow*/, ScFormulaCell
* pCell
)
587 pCell
->EndListeningTo(mrCxt
);
593 void ScColumn::SwapNonEmpty(
594 sc::TableValues
& rValues
, sc::StartListeningContext
& rStartCxt
, sc::EndListeningContext
& rEndCxt
)
596 const ScRange
& rRange
= rValues
.getRange();
597 std::vector
<SCROW
> aBounds
{ rRange
.aStart
.Row() };
598 if (rRange
.aEnd
.Row() < GetDoc().MaxRow()-1)
599 aBounds
.push_back(rRange
.aEnd
.Row()+1);
601 // Split formula cell groups at top and bottom boundaries (if applicable).
602 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells
, aBounds
);
603 std::vector
<sc::CellValueSpan
> aSpans
= rValues
.getNonEmptySpans(nTab
, nCol
);
605 // Detach formula cells within the spans (if any).
606 EndListeningHandler
aEndLisFunc(rEndCxt
);
607 sc::CellStoreType::iterator itPos
= maCells
.begin();
608 for (const auto& rSpan
: aSpans
)
610 SCROW nRow1
= rSpan
.mnRow1
;
611 SCROW nRow2
= rSpan
.mnRow2
;
612 itPos
= sc::ProcessFormula(itPos
, maCells
, nRow1
, nRow2
, aEndLisFunc
);
615 rValues
.swapNonEmpty(nTab
, nCol
, *this);
616 RegroupFormulaCells();
618 // Attach formula cells within the spans (if any).
619 StartListeningHandler
aStartLisFunc(rStartCxt
);
620 itPos
= maCells
.begin();
621 for (const auto& rSpan
: aSpans
)
623 SCROW nRow1
= rSpan
.mnRow1
;
624 SCROW nRow2
= rSpan
.mnRow2
;
625 itPos
= sc::ProcessFormula(itPos
, maCells
, nRow1
, nRow2
, aStartLisFunc
);
628 CellStorageModified();
631 void ScColumn::DeleteRanges( const std::vector
<sc::RowSpan
>& rRanges
, InsertDeleteFlags nDelFlag
)
633 for (const auto& rSpan
: rRanges
)
634 DeleteArea(rSpan
.mnRow1
, rSpan
.mnRow2
, nDelFlag
, false/*bBroadcast*/);
637 void ScColumn::CloneFormulaCell(
638 sc::ColumnBlockPosition
& rBlockPos
,
639 const ScFormulaCell
& rSrc
, const sc::CellTextAttr
& rAttr
,
640 const std::vector
<sc::RowSpan
>& rRanges
)
642 SCCOL nMatrixCols
= 0;
643 SCROW nMatrixRows
= 0;
644 ScMatrixMode nMatrixFlag
= rSrc
.GetMatrixFlag();
645 if (nMatrixFlag
== ScMatrixMode::Formula
)
647 rSrc
.GetMatColsRows( nMatrixCols
, nMatrixRows
);
648 SAL_WARN_IF( nMatrixCols
!= 1 || nMatrixRows
!= 1, "sc.core",
649 "ScColumn::CloneFormulaCell - cloning array/matrix with not exactly one column or row as single cell");
652 ScDocument
& rDocument
= GetDoc();
653 std::vector
<ScFormulaCell
*> aFormulas
;
654 for (const auto& rSpan
: rRanges
)
656 SCROW nRow1
= rSpan
.mnRow1
, nRow2
= rSpan
.mnRow2
;
657 size_t nLen
= nRow2
- nRow1
+ 1;
660 aFormulas
.reserve(nLen
);
662 ScAddress
aPos(nCol
, nRow1
, nTab
);
664 if (nLen
== 1 || !rSrc
.GetCode()->IsShareable())
666 // Single, ungrouped formula cell, or create copies for
667 // non-shareable token arrays.
668 for (size_t i
= 0; i
< nLen
; ++i
, aPos
.IncRow())
670 ScFormulaCell
* pCell
= new ScFormulaCell(rSrc
, rDocument
, aPos
);
671 aFormulas
.push_back(pCell
);
676 // Create a group of formula cells.
677 ScFormulaCellGroupRef
xGroup(new ScFormulaCellGroup
);
678 xGroup
->setCode(*rSrc
.GetCode());
679 xGroup
->compileCode(rDocument
, aPos
, rDocument
.GetGrammar());
680 for (size_t i
= 0; i
< nLen
; ++i
, aPos
.IncRow())
682 ScFormulaCell
* pCell
= new ScFormulaCell(rDocument
, aPos
, xGroup
, rDocument
.GetGrammar(), nMatrixFlag
);
683 if (nMatrixFlag
== ScMatrixMode::Formula
)
684 pCell
->SetMatColsRows( nMatrixCols
, nMatrixRows
);
687 xGroup
->mpTopCell
= pCell
;
688 xGroup
->mnLength
= nLen
;
690 aFormulas
.push_back(pCell
);
694 rBlockPos
.miCellPos
= maCells
.set(rBlockPos
.miCellPos
, nRow1
, aFormulas
.begin(), aFormulas
.end());
696 // Join the top and bottom of the pasted formula cells as needed.
697 sc::CellStoreType::position_type aPosObj
= maCells
.position(rBlockPos
.miCellPos
, nRow1
);
699 assert(aPosObj
.first
->type
== sc::element_type_formula
);
700 ScFormulaCell
* pCell
= sc::formula_block::at(*aPosObj
.first
->data
, aPosObj
.second
);
701 JoinNewFormulaCell(aPosObj
, *pCell
);
703 aPosObj
= maCells
.position(aPosObj
.first
, nRow2
);
704 assert(aPosObj
.first
->type
== sc::element_type_formula
);
705 pCell
= sc::formula_block::at(*aPosObj
.first
->data
, aPosObj
.second
);
706 JoinNewFormulaCell(aPosObj
, *pCell
);
708 std::vector
<sc::CellTextAttr
> aTextAttrs(nLen
, rAttr
);
709 rBlockPos
.miCellTextAttrPos
= maCellTextAttrs
.set(
710 rBlockPos
.miCellTextAttrPos
, nRow1
, aTextAttrs
.begin(), aTextAttrs
.end());
713 CellStorageModified();
716 void ScColumn::CloneFormulaCell(
717 const ScFormulaCell
& rSrc
, const sc::CellTextAttr
& rAttr
,
718 const std::vector
<sc::RowSpan
>& rRanges
)
720 sc::ColumnBlockPosition aBlockPos
;
721 InitBlockPosition(aBlockPos
);
722 CloneFormulaCell(aBlockPos
, rSrc
, rAttr
, rRanges
);
725 std::unique_ptr
<ScPostIt
> ScColumn::ReleaseNote( SCROW nRow
)
727 if (!GetDoc().ValidRow(nRow
))
730 ScPostIt
* p
= nullptr;
731 maCellNotes
.release(nRow
, p
);
732 return std::unique_ptr
<ScPostIt
>(p
);
735 size_t ScColumn::GetNoteCount() const
737 return std::accumulate(maCellNotes
.begin(), maCellNotes
.end(), size_t(0),
738 [](const size_t& rCount
, const auto& rCellNote
) {
739 if (rCellNote
.type
!= sc::element_type_cellnote
)
741 return rCount
+ rCellNote
.size
;
747 class NoteCaptionCreator
751 NoteCaptionCreator( SCTAB nTab
, SCCOL nCol
) : maPos(nCol
,0,nTab
) {}
753 void operator() ( size_t nRow
, const ScPostIt
* p
)
756 p
->GetOrCreateCaption(maPos
);
760 class NoteCaptionCleaner
764 explicit NoteCaptionCleaner( bool bPreserveData
) : mbPreserveData(bPreserveData
) {}
766 void operator() ( size_t /*nRow*/, ScPostIt
* p
)
768 p
->ForgetCaption(mbPreserveData
);
774 void ScColumn::CreateAllNoteCaptions()
776 NoteCaptionCreator
aFunc(nTab
, nCol
);
777 sc::ProcessNote(maCellNotes
, aFunc
);
780 void ScColumn::ForgetNoteCaptions( SCROW nRow1
, SCROW nRow2
, bool bPreserveData
)
782 if (maCellNotes
.empty())
785 if (!GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
))
788 NoteCaptionCleaner
aFunc(bPreserveData
);
789 sc::CellNoteStoreType::iterator it
= maCellNotes
.begin();
790 sc::ProcessNote(it
, maCellNotes
, nRow1
, nRow2
, aFunc
);
793 SCROW
ScColumn::GetNotePosition( size_t nIndex
) const
795 // Return the row position of the nth note in the column.
797 size_t nCount
= 0; // Number of notes encountered so far.
798 for (const auto& rCellNote
: maCellNotes
)
800 if (rCellNote
.type
!= sc::element_type_cellnote
)
801 // Skip the empty blocks.
804 if (nIndex
< nCount
+ rCellNote
.size
)
806 // Index falls within this block.
807 size_t nOffset
= nIndex
- nCount
;
808 return rCellNote
.position
+ nOffset
;
811 nCount
+= rCellNote
.size
;
819 class NoteEntryCollector
821 std::vector
<sc::NoteEntry
>& mrNotes
;
827 NoteEntryCollector( std::vector
<sc::NoteEntry
>& rNotes
, SCTAB nTab
, SCCOL nCol
,
828 SCROW nStartRow
, SCROW nEndRow
) :
829 mrNotes(rNotes
), mnTab(nTab
), mnCol(nCol
),
830 mnStartRow(nStartRow
), mnEndRow(nEndRow
) {}
832 void operator() (const sc::CellNoteStoreType::value_type
& node
) const
834 if (node
.type
!= sc::element_type_cellnote
)
837 size_t nTopRow
= node
.position
;
838 sc::cellnote_block::const_iterator it
= sc::cellnote_block::begin(*node
.data
);
839 sc::cellnote_block::const_iterator itEnd
= sc::cellnote_block::end(*node
.data
);
841 if(nTopRow
< o3tl::make_unsigned(mnStartRow
))
843 std::advance(it
, mnStartRow
- nTopRow
);
844 nOffset
= mnStartRow
- nTopRow
;
847 for (; it
!= itEnd
&& nTopRow
+ nOffset
<= o3tl::make_unsigned(mnEndRow
);
850 ScAddress
aPos(mnCol
, nTopRow
+ nOffset
, mnTab
);
851 mrNotes
.emplace_back(aPos
, *it
);
858 void ScColumn::GetAllNoteEntries( std::vector
<sc::NoteEntry
>& rNotes
) const
861 std::for_each(maCellNotes
.begin(), maCellNotes
.end(),
862 NoteEntryCollector(rNotes
, nTab
, nCol
, 0, GetDoc().MaxRow()));
865 void ScColumn::GetNotesInRange(SCROW nStartRow
, SCROW nEndRow
,
866 std::vector
<sc::NoteEntry
>& rNotes
) const
868 std::pair
<sc::CellNoteStoreType::const_iterator
,size_t> aPos
= maCellNotes
.position(nStartRow
);
869 if (aPos
.first
== 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(aPos
.first
, ++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 ScInterpreterContext
& mrContext
;
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(mrContext
, pCondSet
);
1162 OUString aStr
= ScCellFormat::GetString(rCell
, nFormat
, &pColor
, &mrContext
, 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 mrContext(rCol
.GetDoc().GetNonThreadedContext()),
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 (!ScPatternAttr::areSame(pPat1
, pPat2
))
1253 CellAttributeHolder
aTemp(pPat1
);
1254 SetPattern(nRow
, *pPat2
);
1255 rOther
.SetPattern(nRow
, aTemp
);
1260 CellStorageModified();
1261 rOther
.CellStorageModified();
1266 class FormulaColPosSetter
1271 FormulaColPosSetter( SCCOL nCol
, bool bUpdateRefs
) : mnCol(nCol
), mbUpdateRefs(bUpdateRefs
) {}
1273 void operator() ( size_t nRow
, ScFormulaCell
* pCell
)
1275 if (!pCell
->IsShared() || pCell
->IsSharedTop())
1277 // Ensure that the references still point to the same locations
1278 // after the position change.
1279 ScAddress aOldPos
= pCell
->aPos
;
1280 pCell
->aPos
.SetCol(mnCol
);
1281 pCell
->aPos
.SetRow(nRow
);
1283 pCell
->GetCode()->AdjustReferenceOnMovedOrigin(aOldPos
, pCell
->aPos
);
1285 pCell
->GetCode()->AdjustReferenceOnMovedOriginIfOtherSheet(aOldPos
, pCell
->aPos
);
1289 pCell
->aPos
.SetCol(mnCol
);
1290 pCell
->aPos
.SetRow(nRow
);
1297 void ScColumn::ResetFormulaCellPositions( SCROW nRow1
, SCROW nRow2
, bool bUpdateRefs
)
1299 FormulaColPosSetter
aFunc(nCol
, bUpdateRefs
);
1300 sc::ProcessFormula(maCells
.begin(), maCells
, nRow1
, nRow2
, aFunc
);
1305 class RelativeRefBoundChecker
1307 std::vector
<SCROW
> maBounds
;
1308 ScRange maBoundRange
;
1311 explicit RelativeRefBoundChecker( const ScRange
& rBoundRange
) :
1312 maBoundRange(rBoundRange
) {}
1314 void operator() ( size_t /*nRow*/, ScFormulaCell
* pCell
)
1316 if (!pCell
->IsSharedTop())
1319 pCell
->GetCode()->CheckRelativeReferenceBounds(
1320 pCell
->aPos
, pCell
->GetSharedLength(), maBoundRange
, maBounds
);
1323 void swapBounds( std::vector
<SCROW
>& rBounds
)
1325 rBounds
.swap(maBounds
);
1331 void ScColumn::SplitFormulaGroupByRelativeRef( const ScRange
& rBoundRange
)
1333 if (rBoundRange
.aStart
.Row() >= GetDoc().MaxRow())
1334 // Nothing to split.
1337 std::vector
<SCROW
> aBounds
;
1339 // Cut at row boundaries first.
1340 aBounds
.push_back(rBoundRange
.aStart
.Row());
1341 if (rBoundRange
.aEnd
.Row() < GetDoc().MaxRow())
1342 aBounds
.push_back(rBoundRange
.aEnd
.Row()+1);
1343 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells
, aBounds
);
1345 RelativeRefBoundChecker
aFunc(rBoundRange
);
1347 maCells
.begin(), maCells
, rBoundRange
.aStart
.Row(), rBoundRange
.aEnd
.Row(), aFunc
);
1348 aFunc
.swapBounds(aBounds
);
1349 sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells
, aBounds
);
1354 class ListenerCollector
1356 std::vector
<SvtListener
*>& mrListeners
;
1358 explicit ListenerCollector( std::vector
<SvtListener
*>& rListener
) :
1359 mrListeners(rListener
) {}
1361 void operator() ( size_t /*nRow*/, SvtBroadcaster
* p
)
1363 SvtBroadcaster::ListenersType
& rLis
= p
->GetAllListeners();
1364 mrListeners
.insert(mrListeners
.end(), rLis
.begin(), rLis
.end());
1368 class FormulaCellCollector
1370 std::vector
<ScFormulaCell
*>& mrCells
;
1372 explicit FormulaCellCollector( std::vector
<ScFormulaCell
*>& rCells
) : mrCells(rCells
) {}
1374 void operator() ( size_t /*nRow*/, ScFormulaCell
* p
)
1376 mrCells
.push_back(p
);
1382 void ScColumn::CollectListeners( std::vector
<SvtListener
*>& rListeners
, SCROW nRow1
, SCROW nRow2
)
1384 if (nRow2
< nRow1
|| !GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
))
1387 ListenerCollector
aFunc(rListeners
);
1388 sc::ProcessBroadcaster(maBroadcasters
.begin(), maBroadcasters
, nRow1
, nRow2
, aFunc
);
1391 void ScColumn::CollectFormulaCells( std::vector
<ScFormulaCell
*>& rCells
, SCROW nRow1
, SCROW nRow2
)
1393 if (nRow2
< nRow1
|| !GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
))
1396 FormulaCellCollector
aFunc(rCells
);
1397 sc::ProcessFormula(maCells
.begin(), maCells
, nRow1
, nRow2
, aFunc
);
1400 bool ScColumn::HasFormulaCell() const
1402 return mnBlkCountFormula
!= 0;
1407 struct FindAnyFormula
1409 bool operator() ( size_t /*nRow*/, const ScFormulaCell
* /*pCell*/ ) const
1417 bool ScColumn::HasFormulaCell( SCROW nRow1
, SCROW nRow2
) const
1419 if (!mnBlkCountFormula
)
1422 if (nRow2
< nRow1
|| !GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
))
1425 if (nRow1
== 0 && nRow2
== GetDoc().MaxRow())
1426 return HasFormulaCell();
1428 FindAnyFormula aFunc
;
1429 std::pair
<sc::CellStoreType::const_iterator
, size_t> aRet
=
1430 sc::FindFormula(maCells
, nRow1
, nRow2
, aFunc
);
1432 return aRet
.first
!= maCells
.end();
1437 void endListening( sc::EndListeningContext
& rCxt
, ScFormulaCell
** pp
, ScFormulaCell
** ppEnd
)
1439 for (; pp
!= ppEnd
; ++pp
)
1441 ScFormulaCell
& rFC
= **pp
;
1442 rFC
.EndListeningTo(rCxt
);
1446 class StartListeningFormulaCellsHandler
1448 sc::StartListeningContext
& mrStartCxt
;
1449 sc::EndListeningContext
& mrEndCxt
;
1453 StartListeningFormulaCellsHandler( sc::StartListeningContext
& rStartCxt
, sc::EndListeningContext
& rEndCxt
) :
1454 mrStartCxt(rStartCxt
), mrEndCxt(rEndCxt
), mnStartRow(-1) {}
1456 void operator() ( const sc::CellStoreType::value_type
& node
, size_t nOffset
, size_t nDataSize
)
1458 if (node
.type
!= sc::element_type_formula
)
1459 // We are only interested in formulas.
1462 mnStartRow
= node
.position
+ nOffset
;
1464 ScFormulaCell
** ppBeg
= &sc::formula_block::at(*node
.data
, nOffset
);
1465 ScFormulaCell
** ppEnd
= ppBeg
+ nDataSize
;
1467 ScFormulaCell
** pp
= ppBeg
;
1469 // If the first formula cell belongs to a group and it's not the top
1470 // cell, move up to the top cell of the group, and have all the extra
1471 // formula cells stop listening.
1473 ScFormulaCell
* pFC
= *pp
;
1474 if (pFC
->IsShared() && !pFC
->IsSharedTop())
1476 SCROW nBackTrackSize
= pFC
->aPos
.Row() - pFC
->GetSharedTopRow();
1477 if (nBackTrackSize
> 0)
1479 assert(o3tl::make_unsigned(nBackTrackSize
) <= nOffset
);
1480 for (SCROW i
= 0; i
< nBackTrackSize
; ++i
)
1482 endListening(mrEndCxt
, pp
, ppBeg
);
1483 mnStartRow
-= nBackTrackSize
;
1487 for (; pp
!= ppEnd
; ++pp
)
1491 if (!pFC
->IsSharedTop())
1493 assert(!pFC
->IsShared());
1494 pFC
->StartListeningTo(mrStartCxt
);
1498 // If This is the last group in the range, see if the group
1499 // extends beyond the range, in which case have the excess
1500 // formula cells stop listening.
1501 size_t nEndGroupPos
= (pp
- ppBeg
) + pFC
->GetSharedLength();
1502 if (nEndGroupPos
> nDataSize
)
1504 size_t nExcessSize
= nEndGroupPos
- nDataSize
;
1505 ScFormulaCell
** ppGrpEnd
= pp
+ pFC
->GetSharedLength();
1506 ScFormulaCell
** ppGrp
= ppGrpEnd
- nExcessSize
;
1507 endListening(mrEndCxt
, ppGrp
, ppGrpEnd
);
1509 // Register formula cells as a group.
1510 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt
, pp
);
1511 pp
= ppEnd
- 1; // Move to the one before the end position.
1515 // Register formula cells as a group.
1516 sc::SharedFormulaUtil::startListeningAsGroup(mrStartCxt
, pp
);
1517 pp
+= pFC
->GetSharedLength() - 1; // Move to the last one in the group.
1524 class EndListeningFormulaCellsHandler
1526 sc::EndListeningContext
& mrEndCxt
;
1531 explicit EndListeningFormulaCellsHandler( sc::EndListeningContext
& rEndCxt
) :
1532 mrEndCxt(rEndCxt
), mnStartRow(-1), mnEndRow(-1) {}
1534 void operator() ( const sc::CellStoreType::value_type
& node
, size_t nOffset
, size_t nDataSize
)
1536 if (node
.type
!= sc::element_type_formula
)
1537 // We are only interested in formulas.
1540 mnStartRow
= node
.position
+ nOffset
;
1542 ScFormulaCell
** ppBeg
= &sc::formula_block::at(*node
.data
, nOffset
);
1543 ScFormulaCell
** ppEnd
= ppBeg
+ nDataSize
;
1545 ScFormulaCell
** pp
= ppBeg
;
1547 // If the first formula cell belongs to a group and it's not the top
1548 // cell, move up to the top cell of the group.
1550 ScFormulaCell
* pFC
= *pp
;
1551 if (pFC
->IsShared() && !pFC
->IsSharedTop())
1553 SCROW nBackTrackSize
= pFC
->aPos
.Row() - pFC
->GetSharedTopRow();
1554 if (nBackTrackSize
> 0)
1556 assert(o3tl::make_unsigned(nBackTrackSize
) <= nOffset
);
1557 for (SCROW i
= 0; i
< nBackTrackSize
; ++i
)
1559 mnStartRow
-= nBackTrackSize
;
1563 for (; pp
!= ppEnd
; ++pp
)
1567 if (!pFC
->IsSharedTop())
1569 assert(!pFC
->IsShared());
1570 pFC
->EndListeningTo(mrEndCxt
);
1574 size_t nEndGroupPos
= (pp
- ppBeg
) + pFC
->GetSharedLength();
1575 mnEndRow
= node
.position
+ nOffset
+ nEndGroupPos
- 1; // absolute row position of the last one in the group.
1577 ScFormulaCell
** ppGrpEnd
= pp
+ pFC
->GetSharedLength();
1578 endListening(mrEndCxt
, pp
, ppGrpEnd
);
1580 if (nEndGroupPos
> nDataSize
)
1582 // The group goes beyond the specified end row. Move to the
1583 // one before the end position to finish the loop.
1588 // Move to the last one in the group.
1589 pp
+= pFC
->GetSharedLength() - 1;
1594 SCROW
getStartRow() const
1599 SCROW
getEndRow() const
1607 void ScColumn::StartListeningFormulaCells(
1608 sc::StartListeningContext
& rStartCxt
, sc::EndListeningContext
& rEndCxt
,
1609 SCROW nRow1
, SCROW nRow2
)
1611 if (!HasFormulaCell())
1614 StartListeningFormulaCellsHandler
aFunc(rStartCxt
, rEndCxt
);
1615 sc::ProcessBlock(maCells
.begin(), maCells
, aFunc
, nRow1
, nRow2
);
1618 void ScColumn::EndListeningFormulaCells(
1619 sc::EndListeningContext
& rCxt
, SCROW nRow1
, SCROW nRow2
,
1620 SCROW
* pStartRow
, SCROW
* pEndRow
)
1622 if (!HasFormulaCell())
1625 EndListeningFormulaCellsHandler
aFunc(rCxt
);
1626 sc::ProcessBlock(maCells
.begin(), maCells
, aFunc
, nRow1
, nRow2
);
1629 *pStartRow
= aFunc
.getStartRow();
1632 *pEndRow
= aFunc
.getEndRow();
1635 void ScColumn::EndListeningIntersectedGroup(
1636 sc::EndListeningContext
& rCxt
, SCROW nRow
, std::vector
<ScAddress
>* pGroupPos
)
1638 if (!GetDoc().ValidRow(nRow
))
1641 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
1642 sc::CellStoreType::iterator it
= aPos
.first
;
1643 if (it
->type
!= sc::element_type_formula
)
1644 // Only interested in a formula block.
1647 ScFormulaCell
* pFC
= sc::formula_block::at(*it
->data
, aPos
.second
);
1648 ScFormulaCellGroupRef xGroup
= pFC
->GetCellGroup();
1650 // Not a formula group.
1654 pFC
->EndListeningTo(rCxt
);
1658 if (!pFC
->IsSharedTop())
1659 // Record the position of the top cell of the group.
1660 pGroupPos
->push_back(xGroup
->mpTopCell
->aPos
);
1662 SCROW nGrpLastRow
= pFC
->GetSharedTopRow() + pFC
->GetSharedLength() - 1;
1663 if (nRow
< nGrpLastRow
)
1664 // Record the last position of the group.
1665 pGroupPos
->push_back(ScAddress(nCol
, nGrpLastRow
, nTab
));
1669 void ScColumn::EndListeningIntersectedGroups(
1670 sc::EndListeningContext
& rCxt
, SCROW nRow1
, SCROW nRow2
, std::vector
<ScAddress
>* pGroupPos
)
1672 // Only end the intersected group.
1673 sc::CellStoreType::position_type aPos
= maCells
.position(nRow1
);
1674 sc::CellStoreType::iterator it
= aPos
.first
;
1675 if (it
->type
== sc::element_type_formula
)
1677 ScFormulaCell
* pFC
= sc::formula_block::at(*it
->data
, aPos
.second
);
1678 ScFormulaCellGroupRef xGroup
= pFC
->GetCellGroup();
1681 if (!pFC
->IsSharedTop())
1683 pFC
->EndListeningTo(rCxt
);
1686 // Record the position of the top cell of the group.
1687 pGroupPos
->push_back(xGroup
->mpTopCell
->aPos
);
1691 aPos
= maCells
.position(it
, nRow2
);
1693 if (it
->type
!= sc::element_type_formula
)
1696 ScFormulaCell
* pFC
= sc::formula_block::at(*it
->data
, aPos
.second
);
1697 ScFormulaCellGroupRef xGroup
= pFC
->GetCellGroup();
1701 if (!pFC
->IsSharedTop())
1703 pFC
->EndListeningTo(rCxt
);
1707 // Record the position of the bottom cell of the group.
1708 ScAddress aPosLast
= xGroup
->mpTopCell
->aPos
;
1709 aPosLast
.IncRow(xGroup
->mnLength
-1);
1710 pGroupPos
->push_back(aPosLast
);
1714 void ScColumn::EndListeningGroup( sc::EndListeningContext
& rCxt
, SCROW nRow
)
1716 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
1717 if (aPos
.first
->type
!= sc::element_type_formula
)
1718 // not a formula cell.
1721 ScFormulaCell
** pp
= &sc::formula_block::at(*aPos
.first
->data
, aPos
.second
);
1723 ScFormulaCellGroupRef xGroup
= (*pp
)->GetCellGroup();
1726 // not a formula group.
1727 (*pp
)->EndListeningTo(rCxt
);
1731 // Move back to the top cell.
1732 SCROW nTopDelta
= (*pp
)->aPos
.Row() - xGroup
->mpTopCell
->aPos
.Row();
1733 assert(nTopDelta
>= 0);
1737 // Set the needs listening flag to all cells in the group.
1738 assert(*pp
== xGroup
->mpTopCell
);
1739 ScFormulaCell
** ppEnd
= pp
+ xGroup
->mnLength
;
1740 for (; pp
!= ppEnd
; ++pp
)
1741 (*pp
)->EndListeningTo(rCxt
);
1744 void ScColumn::SetNeedsListeningGroup( SCROW nRow
)
1746 sc::CellStoreType::position_type aPos
= maCells
.position(nRow
);
1747 if (aPos
.first
->type
!= sc::element_type_formula
)
1748 // not a formula cell.
1751 ScFormulaCell
** pp
= &sc::formula_block::at(*aPos
.first
->data
, aPos
.second
);
1753 ScFormulaCellGroupRef xGroup
= (*pp
)->GetCellGroup();
1756 // not a formula group.
1757 (*pp
)->SetNeedsListening(true);
1761 // Move back to the top cell.
1762 SCROW nTopDelta
= (*pp
)->aPos
.Row() - xGroup
->mpTopCell
->aPos
.Row();
1763 assert(nTopDelta
>= 0);
1767 // Set the needs listening flag to all cells in the group.
1768 assert(*pp
== xGroup
->mpTopCell
);
1769 ScFormulaCell
** ppEnd
= pp
+ xGroup
->mnLength
;
1770 for (; pp
!= ppEnd
; ++pp
)
1771 (*pp
)->SetNeedsListening(true);
1774 std::optional
<sc::ColumnIterator
> ScColumn::GetColumnIterator( SCROW nRow1
, SCROW nRow2
) const
1776 if (!GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
) || nRow1
> nRow2
)
1779 return sc::ColumnIterator(maCells
, nRow1
, nRow2
);
1782 static bool lcl_InterpretSpan(sc::formula_block::const_iterator
& rSpanIter
, SCROW nStartOffset
, SCROW nEndOffset
,
1783 const ScFormulaCellGroupRef
& mxParentGroup
, bool& bAllowThreading
, ScDocument
& rDoc
)
1785 bAllowThreading
= true;
1786 ScFormulaCell
* pCellStart
= nullptr;
1787 SCROW nSpanStart
= -1;
1788 SCROW nSpanEnd
= -1;
1789 sc::formula_block::const_iterator itSpanStart
;
1790 bool bAnyDirty
= false;
1791 for (SCROW nFGOffset
= nStartOffset
; nFGOffset
<= nEndOffset
; ++rSpanIter
, ++nFGOffset
)
1793 bool bThisDirty
= (*rSpanIter
)->NeedsInterpret();
1794 if (!pCellStart
&& bThisDirty
)
1796 pCellStart
= *rSpanIter
;
1797 itSpanStart
= rSpanIter
;
1798 nSpanStart
= nFGOffset
;
1802 if (pCellStart
&& (!bThisDirty
|| nFGOffset
== nEndOffset
))
1804 nSpanEnd
= bThisDirty
? nFGOffset
: nFGOffset
- 1;
1805 assert(nSpanStart
>= nStartOffset
&& nSpanStart
<= nSpanEnd
&& nSpanEnd
<= nEndOffset
);
1807 // Found a completely dirty sub span [nSpanStart, nSpanEnd] inside the required span [nStartOffset, nEndOffset]
1808 bool bGroupInterpreted
= pCellStart
->Interpret(nSpanStart
, nSpanEnd
);
1810 if (bGroupInterpreted
)
1811 for (SCROW nIdx
= nSpanStart
; nIdx
<= nSpanEnd
; ++nIdx
, ++itSpanStart
)
1812 assert(!(*itSpanStart
)->NeedsInterpret());
1814 ScRecursionHelper
& rRecursionHelper
= rDoc
.GetRecursionHelper();
1815 // child cell's Interpret could result in calling dependency calc
1816 // and that could detect a cycle involving mxGroup
1817 // and do early exit in that case.
1819 // this call resulted from a dependency calculation for a multi-formula-group-threading and
1820 // if intergroup dependency is found, return early.
1821 if ((mxParentGroup
&& mxParentGroup
->mbPartOfCycle
) || !rRecursionHelper
.AreGroupsIndependent())
1823 bAllowThreading
= false;
1827 if (!bGroupInterpreted
)
1829 // Evaluate from second cell in non-grouped style (no point in trying group-interpret again).
1831 for (SCROW nIdx
= nSpanStart
+1; nIdx
<= nSpanEnd
; ++nIdx
, ++itSpanStart
)
1833 (*itSpanStart
)->Interpret(); // We know for sure that this cell is dirty so directly call Interpret().
1834 if ((*itSpanStart
)->NeedsInterpret())
1836 SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itSpanStart
)->aPos
1837 << " failed running Interpret(), not allowing threading");
1838 bAllowThreading
= false;
1842 // Allow early exit like above.
1843 if ((mxParentGroup
&& mxParentGroup
->mbPartOfCycle
) || !rRecursionHelper
.AreGroupsIndependent())
1845 // Set this cell as dirty as this may be interpreted in InterpretTail()
1846 pCellStart
->SetDirtyVar();
1847 bAllowThreading
= false;
1853 pCellStart
= nullptr; // For next sub span start detection.
1860 static void lcl_EvalDirty(sc::CellStoreType
& rCells
, SCROW nRow1
, SCROW nRow2
, ScDocument
& rDoc
,
1861 const ScFormulaCellGroupRef
& mxGroup
, bool bThreadingDepEval
, bool bSkipRunning
,
1862 bool& bIsDirty
, bool& bAllowThreading
, ScAddress
* pDirtiedAddress
)
1864 ScRecursionHelper
& rRecursionHelper
= rDoc
.GetRecursionHelper();
1865 std::pair
<sc::CellStoreType::const_iterator
,size_t> aPos
= rCells
.position(nRow1
);
1866 sc::CellStoreType::const_iterator it
= aPos
.first
;
1867 size_t nOffset
= aPos
.second
;
1872 for (;it
!= rCells
.end() && nRow
<= nRow2
; ++it
, nOffset
= 0)
1876 case sc::element_type_edittext
:
1877 // These require EditEngine (in ScEditUtils::GetString()), which is probably
1878 // too complex for use in threads.
1879 if (bThreadingDepEval
)
1881 bAllowThreading
= false;
1885 case sc::element_type_formula
:
1887 size_t nRowsToRead
= nRow2
- nRow
+ 1;
1888 const size_t nEnd
= std::min(it
->size
, nOffset
+nRowsToRead
); // last row + 1
1889 sc::formula_block::const_iterator itCell
= sc::formula_block::begin(*it
->data
);
1890 std::advance(itCell
, nOffset
);
1892 // Loop inside the formula block.
1893 size_t nCellIdx
= nOffset
;
1894 while (nCellIdx
< nEnd
)
1896 const ScFormulaCellGroupRef
& mxGroupChild
= (*itCell
)->GetCellGroup();
1897 ScFormulaCell
* pChildTopCell
= mxGroupChild
? mxGroupChild
->mpTopCell
: *itCell
;
1899 // Check if itCell is already in path.
1900 // If yes use a cycle guard to mark all elements of the cycle
1902 if (bThreadingDepEval
&& pChildTopCell
->GetSeenInPath())
1904 ScFormulaGroupCycleCheckGuard
aCycleCheckGuard(rRecursionHelper
, pChildTopCell
);
1905 bAllowThreading
= false;
1909 if (bSkipRunning
&& (*itCell
)->IsRunning())
1920 // It is a Formula-group, evaluate the necessary parts of it (spans).
1921 const SCROW nFGStartOffset
= (*itCell
)->aPos
.Row() - pChildTopCell
->aPos
.Row();
1922 const SCROW nFGEndOffset
= std::min(nFGStartOffset
+ static_cast<SCROW
>(nRowsToRead
) - 1, mxGroupChild
->mnLength
- 1);
1923 assert(nFGEndOffset
>= nFGStartOffset
);
1924 const SCROW nSpanLen
= nFGEndOffset
- nFGStartOffset
+ 1;
1925 // The (main) span required to be evaluated is [nFGStartOffset, nFGEndOffset], but this span may contain
1926 // non-dirty cells, so split this into sets of completely-dirty spans and try evaluate each of them in grouped-style.
1928 bool bAnyDirtyInSpan
= lcl_InterpretSpan(itCell
, nFGStartOffset
, nFGEndOffset
, mxGroup
, bAllowThreading
, rDoc
);
1929 if (!bAllowThreading
)
1931 // itCell will now point to cell just after the end of span [nFGStartOffset, nFGEndOffset].
1932 bIsDirty
= bIsDirty
|| bAnyDirtyInSpan
;
1934 // update the counters by nSpanLen.
1935 // itCell already got updated.
1936 nCellIdx
+= nSpanLen
;
1938 nRowsToRead
-= nSpanLen
;
1942 // No formula-group here.
1943 bool bDirtyFlag
= false;
1944 if( (*itCell
)->NeedsInterpret())
1947 (*itCell
)->Interpret();
1948 if ((*itCell
)->NeedsInterpret())
1950 SAL_WARN("sc.core.formulagroup", "Internal error, cell " << (*itCell
)->aPos
1951 << " failed running Interpret(), not allowing threading");
1952 bAllowThreading
= false;
1956 bIsDirty
= bIsDirty
|| bDirtyFlag
;
1958 // child cell's Interpret could result in calling dependency calc
1959 // and that could detect a cycle involving mxGroup
1960 // and do early exit in that case.
1962 // we are trying multi-formula-group-threading, but found intergroup dependency.
1963 if (bThreadingDepEval
&& mxGroup
&&
1964 (mxGroup
->mbPartOfCycle
|| !rRecursionHelper
.AreGroupsIndependent()))
1966 // Set itCell as dirty as itCell may be interpreted in InterpretTail()
1967 (*itCell
)->SetDirtyVar();
1968 if (pDirtiedAddress
)
1969 pDirtiedAddress
->SetRow(nRow
);
1970 bAllowThreading
= false;
1974 // update the counters by 1.
1985 nRow
+= it
->size
- nOffset
;
1990 if (bThreadingDepEval
)
1991 bAllowThreading
= true;
1995 // Returns true if at least one FC is dirty.
1996 bool ScColumn::EnsureFormulaCellResults( SCROW nRow1
, SCROW nRow2
, bool bSkipRunning
)
1998 if (!GetDoc().ValidRow(nRow1
) || !GetDoc().ValidRow(nRow2
) || nRow1
> nRow2
)
2001 if (!HasFormulaCell(nRow1
, nRow2
))
2004 bool bAnyDirty
= false, bTmp
= false;
2005 lcl_EvalDirty(maCells
, nRow1
, nRow2
, GetDoc(), nullptr, false, bSkipRunning
, bAnyDirty
, bTmp
, nullptr);
2009 bool ScColumn::HandleRefArrayForParallelism( SCROW nRow1
, SCROW nRow2
, const ScFormulaCellGroupRef
& mxGroup
, ScAddress
* pDirtiedAddress
)
2014 bool bAllowThreading
= true, bTmp
= false;
2015 lcl_EvalDirty(maCells
, nRow1
, nRow2
, GetDoc(), mxGroup
, true, false, bTmp
, bAllowThreading
, pDirtiedAddress
);
2017 return bAllowThreading
;
2022 class StoreToCacheFunc
2027 StoreToCacheFunc(SvStream
& rStrm
):
2032 void operator() ( const sc::CellStoreType::value_type
& node
, size_t nOffset
, size_t nDataSize
)
2034 SCROW nStartRow
= node
.position
+ nOffset
;
2035 mrStrm
.WriteUInt64(nStartRow
);
2036 mrStrm
.WriteUInt64(nDataSize
);
2039 case sc::element_type_empty
:
2041 mrStrm
.WriteUChar(0);
2044 case sc::element_type_numeric
:
2046 mrStrm
.WriteUChar(1);
2047 sc::numeric_block::const_iterator it
= sc::numeric_block::begin(*node
.data
);
2048 std::advance(it
, nOffset
);
2049 sc::numeric_block::const_iterator itEnd
= it
;
2050 std::advance(itEnd
, nDataSize
);
2052 for (; it
!= itEnd
; ++it
)
2054 mrStrm
.WriteDouble(*it
);
2058 case sc::element_type_string
:
2060 mrStrm
.WriteUChar(2);
2061 sc::string_block::const_iterator it
= sc::string_block::begin(*node
.data
);
2062 std::advance(it
, nOffset
);
2063 sc::string_block::const_iterator itEnd
= it
;
2064 std::advance(itEnd
, nDataSize
);
2066 for (; it
!= itEnd
; ++it
)
2068 OString aStr
= OUStringToOString(it
->getString(), RTL_TEXTENCODING_UTF8
);
2069 sal_Int32 nStrLength
= aStr
.getLength();
2070 mrStrm
.WriteInt32(nStrLength
);
2071 mrStrm
.WriteOString(aStr
);
2075 case sc::element_type_formula
:
2077 mrStrm
.WriteUChar(3);
2078 sc::formula_block::const_iterator it
= sc::formula_block::begin(*node
.data
);
2079 std::advance(it
, nOffset
);
2080 sc::formula_block::const_iterator itEnd
= it
;
2081 std::advance(itEnd
, nDataSize
);
2083 for (; it
!= itEnd
; /* incrementing through std::advance*/)
2085 const ScFormulaCell
* pCell
= *it
;
2086 OUString aFormula
= pCell
->GetFormula(formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
);
2087 const auto& xCellGroup
= pCell
->GetCellGroup();
2088 sal_uInt64 nGroupLength
= 0;
2091 nGroupLength
= xCellGroup
->mnLength
;
2097 mrStrm
.WriteUInt64(nGroupLength
);
2098 mrStrm
.WriteInt32(aFormula
.getLength());
2099 mrStrm
.WriteOString(OUStringToOString(aFormula
, RTL_TEXTENCODING_UTF8
));
2101 // incrementing the iterator
2102 std::advance(it
, nGroupLength
);
2112 void ScColumn::StoreToCache(SvStream
& rStrm
) const
2114 rStrm
.WriteUInt64(nCol
);
2115 SCROW nLastRow
= GetLastDataPos();
2116 rStrm
.WriteUInt64(nLastRow
+ 1); // the rows are zero based
2118 StoreToCacheFunc
aFunc(rStrm
);
2119 sc::ParseBlock(maCells
.begin(), maCells
, aFunc
, SCROW(0), nLastRow
);
2122 void ScColumn::RestoreFromCache(SvStream
& rStrm
)
2124 sal_uInt64 nStoredCol
= 0;
2125 rStrm
.ReadUInt64(nStoredCol
);
2126 if (nStoredCol
!= static_cast<sal_uInt64
>(nCol
))
2127 throw std::exception();
2129 sal_uInt64 nLastRow
= 0;
2130 rStrm
.ReadUInt64(nLastRow
);
2131 sal_uInt64 nReadRow
= 0;
2132 ScDocument
& rDocument
= GetDoc();
2133 while (nReadRow
< nLastRow
)
2135 sal_uInt64 nStartRow
= 0;
2136 sal_uInt64 nDataSize
= 0;
2137 rStrm
.ReadUInt64(nStartRow
);
2138 rStrm
.ReadUInt64(nDataSize
);
2139 sal_uInt8 nType
= 0;
2140 rStrm
.ReadUChar(nType
);
2145 maCells
.set_empty(nStartRow
, nDataSize
);
2149 // nDataSize double values
2150 std::vector
<double> aValues(nDataSize
);
2151 for (auto& rValue
: aValues
)
2153 rStrm
.ReadDouble(rValue
);
2155 maCells
.set(nStartRow
, aValues
.begin(), aValues
.end());
2160 std::vector
<svl::SharedString
> aStrings(nDataSize
);
2161 svl::SharedStringPool
& rPool
= rDocument
.GetSharedStringPool();
2162 for (auto& rString
: aStrings
)
2164 sal_Int32 nStrLength
= 0;
2165 rStrm
.ReadInt32(nStrLength
);
2166 std::unique_ptr
<char[]> pStr(new char[nStrLength
]);
2167 rStrm
.ReadBytes(pStr
.get(), nStrLength
);
2168 std::string_view
aOStr(pStr
.get(), nStrLength
);
2169 OUString aStr
= OStringToOUString(aOStr
, RTL_TEXTENCODING_UTF8
);
2170 rString
= rPool
.intern(aStr
);
2172 maCells
.set(nStartRow
, aStrings
.begin(), aStrings
.end());
2178 std::vector
<ScFormulaCell
*> aFormulaCells(nDataSize
);
2180 ScAddress
aAddr(nCol
, nStartRow
, nTab
);
2181 const formula::FormulaGrammar::Grammar eGrammar
= formula::FormulaGrammar::GRAM_ENGLISH_XL_R1C1
;
2182 for (SCROW nRow
= 0; nRow
< static_cast<SCROW
>(nDataSize
);)
2184 sal_uInt64 nFormulaGroupSize
= 0;
2185 rStrm
.ReadUInt64(nFormulaGroupSize
);
2186 sal_Int32 nStrLength
= 0;
2187 rStrm
.ReadInt32(nStrLength
);
2188 std::unique_ptr
<char[]> pStr(new char[nStrLength
]);
2189 rStrm
.ReadBytes(pStr
.get(), nStrLength
);
2190 std::string_view
aOStr(pStr
.get(), nStrLength
);
2191 OUString aStr
= OStringToOUString(aOStr
, RTL_TEXTENCODING_UTF8
);
2192 for (sal_uInt64 i
= 0; i
< nFormulaGroupSize
; ++i
)
2194 aFormulaCells
[nRow
+ i
] = new ScFormulaCell(rDocument
, aAddr
, aStr
, eGrammar
);
2198 nRow
+= nFormulaGroupSize
;
2201 maCells
.set(nStartRow
, aFormulaCells
.begin(), aFormulaCells
.end());
2206 nReadRow
+= nDataSize
;
2210 void ScColumn::CheckIntegrity() const
2212 auto checkEventHandlerColumnRef
= [this](const auto& rStore
, std::string_view pStoreName
)
2214 if (const ScColumn
* pColTest
= rStore
.event_handler().getColumn(); pColTest
!= this)
2216 std::ostringstream os
;
2217 os
<< pStoreName
<< "'s event handler references wrong column instance (this=" << this
2218 << "; stored=" << pColTest
<< ")";
2219 throw std::runtime_error(os
.str());
2223 auto countBlocks
= [](const auto& rStore
, mdds::mtv::element_t nBlockType
)
2225 std::size_t nCount
= std::count_if(rStore
.cbegin(), rStore
.cend(),
2226 [nBlockType
](const auto& blk
) { return blk
.type
== nBlockType
; }
2232 auto checkCachedBlockCount
= [countBlocks
](
2233 const auto& rStore
, mdds::mtv::element_t nBlockType
, std::size_t nCachedBlkCount
,
2234 std::string_view pName
)
2236 std::size_t nCount
= countBlocks(rStore
, nBlockType
);
2238 if (nCachedBlkCount
!= nCount
)
2240 std::ostringstream os
;
2241 os
<< "incorrect cached " << pName
<< " block count (expected=" << nCount
<< "; actual="
2242 << nCachedBlkCount
<< ")";
2243 throw std::runtime_error(os
.str());
2247 checkEventHandlerColumnRef(maCells
, "cell store");
2248 checkEventHandlerColumnRef(maCellNotes
, "cell-note store");
2250 checkCachedBlockCount(maCells
, sc::element_type_formula
, mnBlkCountFormula
, "formula");
2251 checkCachedBlockCount(maCellNotes
, sc::element_type_cellnote
, mnBlkCountCellNotes
, "cell note");
2254 void ScColumn::CollectBroadcasterState(sc::BroadcasterState
& rState
) const
2256 for (const auto& block
: maBroadcasters
)
2258 if (block
.type
!= sc::element_type_broadcaster
)
2261 auto itBeg
= sc::broadcaster_block::begin(*block
.data
);
2262 auto itEnd
= sc::broadcaster_block::end(*block
.data
);
2264 for (auto it
= itBeg
; it
!= itEnd
; ++it
)
2266 ScAddress
aBCPos(nCol
, block
.position
+ std::distance(itBeg
, it
), nTab
);
2268 auto aRes
= rState
.aCellListenerStore
.try_emplace(aBCPos
);
2269 auto& rLisStore
= aRes
.first
->second
;
2271 const SvtBroadcaster
& rBC
= **it
;
2272 for (const SvtListener
* pLis
: rBC
.GetAllListeners())
2274 const auto* pFC
= dynamic_cast<const ScFormulaCell
*>(pLis
);
2276 rLisStore
.emplace_back(pFC
);
2278 rLisStore
.emplace_back(pLis
);
2284 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */