Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / sc / source / core / data / column.cxx
blob15060d98c1b321f393d2aa565482d8d4c8f00a8c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <column.hxx>
21 #include <scitems.hxx>
22 #include <formulacell.hxx>
23 #include <docsh.hxx>
24 #include <document.hxx>
25 #include <table.hxx>
26 #include <attarray.hxx>
27 #include <patattr.hxx>
28 #include <compiler.hxx>
29 #include <brdcst.hxx>
30 #include <markdata.hxx>
31 #include <postit.hxx>
32 #include <cellvalue.hxx>
33 #include <tokenarray.hxx>
34 #include <clipcontext.hxx>
35 #include <types.hxx>
36 #include <editutil.hxx>
37 #include <mtvcellfunc.hxx>
38 #include <columnspanset.hxx>
39 #include <scopetools.hxx>
40 #include <sharedformula.hxx>
41 #include <refupdatecontext.hxx>
42 #include <listenercontext.hxx>
43 #include <formulagroup.hxx>
44 #include <drwlayer.hxx>
45 #include <mtvelements.hxx>
46 #include <bcaslot.hxx>
48 #include <svl/numformat.hxx>
49 #include <poolcach.hxx>
50 #include <svl/zforlist.hxx>
51 #include <svl/sharedstringpool.hxx>
52 #include <editeng/fieldupdater.hxx>
53 #include <formula/errorcodes.hxx>
54 #include <o3tl/safeint.hxx>
55 #include <osl/diagnose.h>
57 #include <map>
58 #include <cstdio>
59 #include <memory>
61 using ::editeng::SvxBorderLine;
62 using namespace formula;
64 namespace {
66 bool IsAmbiguousScriptNonZero( SvtScriptType nScript )
68 //TODO: move to a header file
69 return ( nScript != SvtScriptType::LATIN &&
70 nScript != SvtScriptType::ASIAN &&
71 nScript != SvtScriptType::COMPLEX &&
72 nScript != SvtScriptType::NONE );
77 ScNeededSizeOptions::ScNeededSizeOptions() :
78 aPattern(), bFormula(false), bSkipMerged(true), bGetFont(true), bTotalSize(false)
82 ScColumn::ScColumn(ScSheetLimits const & rSheetLimits) :
83 maCellTextAttrs(rSheetLimits.GetMaxRowCount()),
84 maCellNotes(sc::CellStoreEvent(this)),
85 maBroadcasters(rSheetLimits.GetMaxRowCount()),
86 maCells(sc::CellStoreEvent(this)),
87 maSparklines(rSheetLimits.GetMaxRowCount()),
88 mnBlkCountFormula(0),
89 mnBlkCountCellNotes(0),
90 nCol( 0 ),
91 nTab( 0 ),
92 mbEmptyBroadcastersPending( false )
94 maCellNotes.resize(rSheetLimits.GetMaxRowCount());
95 maCells.resize(rSheetLimits.GetMaxRowCount());
98 ScColumn::~ScColumn() COVERITY_NOEXCEPT_FALSE
100 FreeAll();
103 void ScColumn::Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, bool bEmptyAttrArray)
105 nCol = nNewCol;
106 nTab = nNewTab;
107 if ( bEmptyAttrArray )
108 InitAttrArray(new ScAttrArray( nCol, nTab, rDoc, nullptr ));
109 else
110 InitAttrArray(new ScAttrArray( nCol, nTab, rDoc, &rDoc.maTabs[nTab]->aDefaultColData.AttrArray()));
113 sc::MatrixEdge ScColumn::GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, sc::MatrixEdge nMask,
114 bool bNoMatrixAtAll ) const
116 using namespace sc;
118 if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
119 return MatrixEdge::Nothing;
121 ScAddress aOrigin(ScAddress::INITIALIZE_INVALID);
123 if (nRow1 == nRow2)
125 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
126 if (aPos.first->type != sc::element_type_formula)
127 return MatrixEdge::Nothing;
129 const ScFormulaCell* pCell = sc::formula_block::at(*aPos.first->data, aPos.second);
130 if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
131 return MatrixEdge::Nothing;
133 return pCell->GetMatrixEdge(GetDoc(), aOrigin);
136 bool bOpen = false;
137 MatrixEdge nEdges = MatrixEdge::Nothing;
139 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
140 sc::CellStoreType::const_iterator it = aPos.first;
141 size_t nOffset = aPos.second;
142 SCROW nRow = nRow1;
143 for (;it != maCells.end() && nRow <= nRow2; ++it, nOffset = 0)
145 if (it->type != sc::element_type_formula)
147 // Skip this block.
148 nRow += it->size - nOffset;
149 continue;
152 size_t nRowsToRead = nRow2 - nRow + 1;
153 size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1
154 sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
155 std::advance(itCell, nOffset);
156 for (size_t i = nOffset; i < nEnd; ++itCell, ++i)
158 // Loop inside the formula block.
159 const ScFormulaCell* pCell = *itCell;
160 if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
161 continue;
163 nEdges = pCell->GetMatrixEdge(GetDoc(), aOrigin);
164 if (nEdges == MatrixEdge::Nothing)
165 continue;
167 // A 1x1 matrix array formula is OK even for no matrix at all.
168 if (bNoMatrixAtAll
169 && (nEdges != (MatrixEdge::Top | MatrixEdge::Left | MatrixEdge::Bottom | MatrixEdge::Right)))
170 return MatrixEdge::Inside; // per convention Inside
172 if (nEdges & MatrixEdge::Top)
173 bOpen = true; // top edge opens, keep on looking
174 else if (!bOpen)
175 return nEdges | MatrixEdge::Open; // there's something that wasn't opened
176 else if (nEdges & MatrixEdge::Inside)
177 return nEdges; // inside
178 if (((nMask & MatrixEdge::Right) && (nEdges & MatrixEdge::Left) && !(nEdges & MatrixEdge::Right)) ||
179 ((nMask & MatrixEdge::Left) && (nEdges & MatrixEdge::Right) && !(nEdges & MatrixEdge::Left)))
180 return nEdges; // only left/right edge
182 if (nEdges & MatrixEdge::Bottom)
183 bOpen = false; // bottom edge closes
186 nRow += nEnd - nOffset;
188 if (bOpen)
189 nEdges |= MatrixEdge::Open; // not closed, matrix continues
191 return nEdges;
194 bool ScColumn::HasSelectionMatrixFragment(const ScMarkData& rMark, const ScRangeList& rRangeList) const
196 using namespace sc;
198 if (!rMark.IsMultiMarked())
199 return false;
201 ScAddress aOrigin(ScAddress::INITIALIZE_INVALID);
202 ScAddress aCurOrigin = aOrigin;
204 bool bOpen = false;
205 for (size_t i = 0, n = rRangeList.size(); i < n; ++i)
207 const ScRange& r = rRangeList[i];
208 if (nTab < r.aStart.Tab() || r.aEnd.Tab() < nTab)
209 continue;
211 if (nCol < r.aStart.Col() || r.aEnd.Col() < nCol)
212 continue;
214 SCROW nTop = r.aStart.Row(), nBottom = r.aEnd.Row();
215 SCROW nRow = nTop;
216 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
217 sc::CellStoreType::const_iterator it = aPos.first;
218 size_t nOffset = aPos.second;
220 for (;it != maCells.end() && nRow <= nBottom; ++it, nOffset = 0)
222 if (it->type != sc::element_type_formula)
224 // Skip this block.
225 nRow += it->size - nOffset;
226 continue;
229 // This is a formula cell block.
230 size_t nRowsToRead = nBottom - nRow + 1;
231 size_t nEnd = std::min(it->size, nRowsToRead);
232 sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
233 std::advance(itCell, nOffset);
234 for (size_t j = nOffset; j < nEnd; ++itCell, ++j)
236 // Loop inside the formula block.
237 const ScFormulaCell* pCell = *itCell;
238 if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
239 // cell is not a part of a matrix.
240 continue;
242 MatrixEdge nEdges = pCell->GetMatrixEdge(GetDoc(), aOrigin);
243 if (nEdges == MatrixEdge::Nothing)
244 continue;
246 bool bFound = false;
248 if (nEdges & MatrixEdge::Top)
249 bOpen = true; // top edge opens, keep on looking
250 else if (!bOpen)
251 return true; // there's something that wasn't opened
252 else if (nEdges & MatrixEdge::Inside)
253 bFound = true; // inside, all selected?
255 if (((nEdges & MatrixEdge::Left) | MatrixEdge::Right) ^ ((nEdges & MatrixEdge::Right) | MatrixEdge::Left))
256 // either left or right, but not both.
257 bFound = true; // only left/right edge, all selected?
259 if (nEdges & MatrixEdge::Bottom)
260 bOpen = false; // bottom edge closes
262 if (bFound)
264 // Check if the matrix is inside the selection in its entirety.
266 // TODO: It's more efficient to skip the matrix range if
267 // it's within selection, to avoid checking it again and
268 // again.
270 if (aCurOrigin != aOrigin)
271 { // new matrix to check?
272 aCurOrigin = aOrigin;
273 const ScFormulaCell* pFCell;
274 if (pCell->GetMatrixFlag() == ScMatrixMode::Reference)
275 pFCell = GetDoc().GetFormulaCell(aOrigin);
276 else
277 pFCell = pCell;
279 SCCOL nC;
280 SCROW nR;
281 pFCell->GetMatColsRows(nC, nR);
282 ScRange aRange(aOrigin, ScAddress(aOrigin.Col()+nC-1, aOrigin.Row()+nR-1, aOrigin.Tab()));
283 if (rMark.IsAllMarked(aRange))
284 bFound = false;
286 else
287 bFound = false; // done already
290 if (bFound)
291 return true;
294 nRow += nEnd;
298 return bOpen;
301 bool ScColumn::HasAttribSelection( const ScMarkData& rMark, HasAttrFlags nMask ) const
303 bool bFound = false;
305 SCROW nTop;
306 SCROW nBottom;
308 if (rMark.IsMultiMarked())
310 ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
311 while (aMultiIter.Next( nTop, nBottom ) && !bFound)
313 if (pAttrArray->HasAttrib( nTop, nBottom, nMask ))
314 bFound = true;
318 return bFound;
321 void ScColumn::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, bool bDeep ) const
323 SCROW nTop;
324 SCROW nBottom;
326 if ( rMark.IsMultiMarked() )
328 const ScMultiSel& rMultiSel = rMark.GetMultiSelData();
329 if ( rMultiSel.HasMarks( nCol ) )
331 ScMultiSelIter aMultiIter( rMultiSel, nCol );
332 while (aMultiIter.Next( nTop, nBottom ))
333 pAttrArray->MergePatternArea( nTop, nBottom, rState, bDeep );
338 const ScPatternAttr* ScColumnData::GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow ) const
340 ::std::map< const ScPatternAttr*, size_t > aAttrMap;
341 const ScPatternAttr* pMaxPattern = nullptr;
342 size_t nMaxCount = 0;
344 ScAttrIterator aAttrIter( pAttrArray.get(), nStartRow, nEndRow, &GetDoc().getCellAttributeHelper().getDefaultCellAttribute() );
345 const ScPatternAttr* pPattern;
346 SCROW nAttrRow1 = 0, nAttrRow2 = 0;
348 while( (pPattern = aAttrIter.Next( nAttrRow1, nAttrRow2 )) != nullptr )
350 size_t& rnCount = aAttrMap[ pPattern ];
351 rnCount += (nAttrRow2 - nAttrRow1 + 1);
352 if( rnCount > nMaxCount )
354 pMaxPattern = pPattern;
355 nMaxCount = rnCount;
359 return pMaxPattern;
362 sal_uInt32 ScColumnData::GetNumberFormat( SCROW nStartRow, SCROW nEndRow ) const
364 SCROW nPatStartRow, nPatEndRow;
365 const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nPatStartRow, nPatEndRow, nStartRow);
366 sal_uInt32 nFormat = pPattern->GetNumberFormat(GetDoc().GetFormatTable());
367 while (nEndRow > nPatEndRow)
369 nStartRow = nPatEndRow + 1;
370 pPattern = pAttrArray->GetPatternRange(nPatStartRow, nPatEndRow, nStartRow);
371 sal_uInt32 nTmpFormat = pPattern->GetNumberFormat(GetDoc().GetFormatTable());
372 if (nFormat != nTmpFormat)
373 return 0;
375 return nFormat;
378 void ScColumnData::ApplySelectionCache(ScItemPoolCache& rCache, SCROW nStartRow, SCROW nEndRow,
379 ScEditDataArray* pDataArray, bool* pIsChanged)
381 pAttrArray->ApplyCacheArea(nStartRow, nEndRow, rCache, pDataArray, pIsChanged);
384 void ScColumnData::ChangeSelectionIndent(bool bIncrement, SCROW nStartRow, SCROW nEndRow)
386 pAttrArray->ChangeIndent(nStartRow, nEndRow, bIncrement);
389 void ScColumnData::ClearSelectionItems(const sal_uInt16* pWhich, SCROW nStartRow, SCROW nEndRow)
391 if (!pAttrArray)
392 return;
394 pAttrArray->ClearItems(nStartRow, nEndRow, pWhich);
397 void ScColumn::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
399 SCROW nTop;
400 SCROW nBottom;
402 if ( rMark.IsMultiMarked() )
404 ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
405 while (aMultiIter.Next( nTop, nBottom ))
406 DeleteArea(nTop, nBottom, nDelFlag, bBroadcast);
410 void ScColumn::ApplyPattern( SCROW nRow, const ScPatternAttr& rPatAttr )
412 const SfxItemSet* pSet = &rPatAttr.GetItemSet();
413 ScItemPoolCache aCache( GetDoc().getCellAttributeHelper(), *pSet );
415 const CellAttributeHolder aPattern(pAttrArray->GetPattern( nRow ));
417 // true = keep old content
419 const CellAttributeHolder& rNewPattern = aCache.ApplyTo( aPattern );
421 if (!CellAttributeHolder::areSame(&rNewPattern, &aPattern))
422 pAttrArray->SetPattern( nRow, rNewPattern );
425 void ScColumnData::ApplyPatternArea( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr& rPatAttr,
426 ScEditDataArray* pDataArray, bool* const pIsChanged )
428 const SfxItemSet* pSet = &rPatAttr.GetItemSet();
429 ScItemPoolCache aCache( GetDoc().getCellAttributeHelper(), *pSet );
430 pAttrArray->ApplyCacheArea( nStartRow, nEndRow, aCache, pDataArray, pIsChanged );
433 void ScColumn::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
434 const ScPatternAttr& rPattern, SvNumFormatType nNewType )
436 const SfxItemSet* pSet = &rPattern.GetItemSet();
437 ScItemPoolCache aCache( GetDoc().getCellAttributeHelper(), *pSet );
438 SvNumberFormatter* pFormatter = GetDoc().GetFormatTable();
439 SCROW nEndRow = rRange.aEnd.Row();
440 for ( SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; nRow++ )
442 SCROW nRow1, nRow2;
443 const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(
444 nRow1, nRow2, nRow );
445 sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
446 SvNumFormatType nOldType = pFormatter->GetType( nFormat );
447 if ( nOldType == nNewType || SvNumberFormatter::IsCompatible( nOldType, nNewType ) )
448 nRow = nRow2;
449 else
451 SCROW nNewRow1 = std::max( nRow1, nRow );
452 SCROW nNewRow2 = std::min( nRow2, nEndRow );
453 pAttrArray->ApplyCacheArea( nNewRow1, nNewRow2, aCache );
454 nRow = nNewRow2;
459 void ScColumn::ApplyStyle( SCROW nRow, const ScStyleSheet* rStyle )
461 const ScPatternAttr* pPattern = pAttrArray->GetPattern(nRow);
462 ScPatternAttr* pNewPattern(new ScPatternAttr(*pPattern));
463 pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(rStyle));
464 pAttrArray->SetPattern(nRow, CellAttributeHolder(pNewPattern, true));
467 void ScColumnData::ApplySelectionStyle(const ScStyleSheet& rStyle, SCROW nTop, SCROW nBottom)
469 pAttrArray->ApplyStyleArea(nTop, nBottom, rStyle);
472 void ScColumn::ApplySelectionLineStyle( const ScMarkData& rMark,
473 const SvxBorderLine* pLine, bool bColorOnly )
475 if ( bColorOnly && !pLine )
476 return;
478 SCROW nTop;
479 SCROW nBottom;
481 if (rMark.IsMultiMarked())
483 ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
484 while (aMultiIter.Next( nTop, nBottom ))
485 pAttrArray->ApplyLineStyleArea(nTop, nBottom, pLine, bColorOnly );
489 const ScStyleSheet* ScColumn::GetSelectionStyle( const ScMarkData& rMark, bool& rFound ) const
491 rFound = false;
492 if (!rMark.IsMultiMarked())
494 OSL_FAIL("No selection in ScColumn::GetSelectionStyle");
495 return nullptr;
498 bool bEqual = true;
500 const ScStyleSheet* pStyle = nullptr;
501 const ScStyleSheet* pNewStyle;
503 ScDocument& rDocument = GetDoc();
504 ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
505 SCROW nTop;
506 SCROW nBottom;
507 while (bEqual && aMultiIter.Next( nTop, nBottom ))
509 ScAttrIterator aAttrIter( pAttrArray.get(), nTop, nBottom, &rDocument.getCellAttributeHelper().getDefaultCellAttribute() );
510 SCROW nRow;
511 SCROW nDummy;
512 while (bEqual)
514 const ScPatternAttr* pPattern = aAttrIter.Next( nRow, nDummy );
515 if (!pPattern)
516 break;
517 pNewStyle = pPattern->GetStyleSheet();
518 rFound = true;
519 if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
520 bEqual = false; // difference
521 pStyle = pNewStyle;
525 return bEqual ? pStyle : nullptr;
528 const ScStyleSheet* ScColumn::GetAreaStyle( bool& rFound, SCROW nRow1, SCROW nRow2 ) const
530 rFound = false;
532 bool bEqual = true;
534 const ScStyleSheet* pStyle = nullptr;
535 const ScStyleSheet* pNewStyle;
537 ScAttrIterator aAttrIter( pAttrArray.get(), nRow1, nRow2, &GetDoc().getCellAttributeHelper().getDefaultCellAttribute() );
538 SCROW nRow;
539 SCROW nDummy;
540 while (bEqual)
542 const ScPatternAttr* pPattern = aAttrIter.Next( nRow, nDummy );
543 if (!pPattern)
544 break;
545 pNewStyle = pPattern->GetStyleSheet();
546 rFound = true;
547 if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
548 bEqual = false; // difference
549 pStyle = pNewStyle;
552 return bEqual ? pStyle : nullptr;
555 void ScColumn::ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr )
557 // in order to only create a new SetItem, we don't need SfxItemPoolCache.
558 //TODO: Warning: ScItemPoolCache seems to create too many Refs for the new SetItem ??
560 const ScPatternAttr* pOldPattern(pAttrArray->GetPattern(nRow));
561 ScPatternAttr* pNewPattern(new ScPatternAttr(*pOldPattern));
562 pNewPattern->GetItemSet().Put(rAttr);
564 if (!ScPatternAttr::areSame( pNewPattern, pOldPattern ))
565 pAttrArray->SetPattern( nRow, CellAttributeHolder(pNewPattern, true) );
566 else
567 delete pNewPattern;
570 ScRefCellValue ScColumn::GetCellValue( SCROW nRow ) const
572 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
573 if (aPos.first == maCells.end())
574 return ScRefCellValue();
576 return GetCellValue(aPos.first, aPos.second);
579 ScRefCellValue ScColumn::GetCellValue( sc::ColumnBlockPosition& rBlockPos, SCROW nRow )
581 std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
582 if (aPos.first == maCells.end())
583 return ScRefCellValue();
585 rBlockPos.miCellPos = aPos.first; // Store this for next call.
586 return GetCellValue(aPos.first, aPos.second);
589 ScRefCellValue ScColumn::GetCellValue( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
591 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
592 if (aPos.first == maCells.end())
593 return ScRefCellValue();
595 rBlockPos.miCellPos = aPos.first; // Store this for next call.
596 return GetCellValue(aPos.first, aPos.second);
599 ScRefCellValue ScColumn::GetCellValue( const sc::CellStoreType::const_iterator& itPos, size_t nOffset )
601 switch (itPos->type)
603 case sc::element_type_numeric:
604 // Numeric cell
605 return ScRefCellValue(sc::numeric_block::at(*itPos->data, nOffset));
606 case sc::element_type_string:
607 // String cell
608 return ScRefCellValue(&sc::string_block::at(*itPos->data, nOffset));
609 case sc::element_type_edittext:
610 // Edit cell
611 return ScRefCellValue(sc::edittext_block::at(*itPos->data, nOffset));
612 case sc::element_type_formula:
613 // Formula cell
614 return ScRefCellValue(sc::formula_block::at(*itPos->data, nOffset));
615 default:
616 return ScRefCellValue(); // empty cell
620 const sc::CellTextAttr* ScColumn::GetCellTextAttr( SCROW nRow ) const
622 sc::ColumnBlockConstPosition aBlockPos;
623 aBlockPos.miCellTextAttrPos = maCellTextAttrs.begin();
624 return GetCellTextAttr(aBlockPos, nRow);
627 const sc::CellTextAttr* ScColumn::GetCellTextAttr( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
629 sc::CellTextAttrStoreType::const_position_type aPos = maCellTextAttrs.position(rBlockPos.miCellTextAttrPos, nRow);
630 if (aPos.first == maCellTextAttrs.end())
631 return nullptr;
633 rBlockPos.miCellTextAttrPos = aPos.first;
635 if (aPos.first->type != sc::element_type_celltextattr)
636 return nullptr;
638 return &sc::celltextattr_block::at(*aPos.first->data, aPos.second);
641 bool ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
643 if (IsEmptyData() && IsEmptyAttr())
644 return true;
646 // Return false if we have any non-empty cells between nStartRow and nEndRow inclusive.
647 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
648 sc::CellStoreType::const_iterator it = aPos.first;
649 if (it->type != sc::element_type_empty)
650 return false;
652 // Get the length of the remaining empty segment.
653 size_t nLen = it->size - aPos.second;
654 SCROW nNextNonEmptyRow = nStartRow + nLen;
655 if (nNextNonEmptyRow <= nEndRow)
656 return false;
658 // AttrArray only looks for merged cells
660 return pAttrArray == nullptr || pAttrArray->TestInsertCol(nStartRow, nEndRow);
663 bool ScColumn::TestInsertRow( SCROW nStartRow, SCSIZE nSize ) const
665 // AttrArray only looks for merged cells
667 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
668 sc::CellStoreType::const_iterator it = aPos.first;
669 if (it->type == sc::element_type_empty && maCells.block_size() == 1)
670 // The entire cell array is empty.
671 return pAttrArray->TestInsertRow(nSize);
674 // See if there would be any non-empty cell that gets pushed out.
676 // Find the position of the last non-empty cell below nStartRow.
677 size_t nLastNonEmptyRow = GetDoc().MaxRow();
678 sc::CellStoreType::const_reverse_iterator it = maCells.rbegin();
679 if (it->type == sc::element_type_empty)
680 nLastNonEmptyRow -= it->size;
682 if (nLastNonEmptyRow < o3tl::make_unsigned(nStartRow))
683 // No cells would get pushed out.
684 return pAttrArray->TestInsertRow(nSize);
686 if (nLastNonEmptyRow + nSize > o3tl::make_unsigned(GetDoc().MaxRow()))
687 // At least one cell would get pushed out. Not good.
688 return false;
690 return pAttrArray->TestInsertRow(nSize);
693 void ScColumn::InsertRow( SCROW nStartRow, SCSIZE nSize )
695 pAttrArray->InsertRow( nStartRow, nSize );
697 maCellNotes.insert_empty(nStartRow, nSize);
698 maCellNotes.resize(GetDoc().GetMaxRowCount());
700 maSparklines.insert_empty(nStartRow, nSize);
701 maSparklines.resize(GetDoc().GetSheetLimits().GetMaxRowCount());
703 maBroadcasters.insert_empty(nStartRow, nSize);
704 maBroadcasters.resize(GetDoc().GetMaxRowCount());
706 maCellTextAttrs.insert_empty(nStartRow, nSize);
707 maCellTextAttrs.resize(GetDoc().GetMaxRowCount());
709 maCells.insert_empty(nStartRow, nSize);
710 maCells.resize(GetDoc().GetMaxRowCount());
712 CellStorageModified();
714 // We *probably* don't need to broadcast here since the parent call seems
715 // to take care of it.
718 namespace {
720 class CopyToClipHandler
722 const ScDocument& mrSrcDoc;
723 const ScColumn& mrSrcCol;
724 ScColumn& mrDestCol;
725 sc::ColumnBlockPosition maDestPos;
726 sc::ColumnBlockPosition* mpDestPos;
728 void setDefaultAttrsToDest(size_t nRow, size_t nSize)
730 std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
731 maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
732 maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
735 public:
736 CopyToClipHandler(const ScDocument& rSrcDoc, const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos) :
737 mrSrcDoc(rSrcDoc), mrSrcCol(rSrcCol), mrDestCol(rDestCol), mpDestPos(pDestPos)
739 if (mpDestPos)
740 maDestPos = *mpDestPos;
741 else
742 mrDestCol.InitBlockPosition(maDestPos);
745 ~CopyToClipHandler()
747 if (mpDestPos)
748 *mpDestPos = maDestPos;
751 void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
753 size_t nTopRow = aNode.position + nOffset;
755 bool bSet = true;
757 switch (aNode.type)
759 case sc::element_type_numeric:
761 sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
762 std::advance(it, nOffset);
763 sc::numeric_block::const_iterator itEnd = it;
764 std::advance(itEnd, nDataSize);
765 maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nTopRow, it, itEnd);
767 break;
768 case sc::element_type_string:
770 sc::string_block::const_iterator it = sc::string_block::begin(*aNode.data);
771 std::advance(it, nOffset);
772 sc::string_block::const_iterator itEnd = it;
773 std::advance(itEnd, nDataSize);
774 maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nTopRow, it, itEnd);
777 break;
778 case sc::element_type_edittext:
780 sc::edittext_block::const_iterator it = sc::edittext_block::begin(*aNode.data);
781 std::advance(it, nOffset);
782 sc::edittext_block::const_iterator itEnd = it;
783 std::advance(itEnd, nDataSize);
785 std::vector<EditTextObject*> aCloned;
786 aCloned.reserve(nDataSize);
787 for (; it != itEnd; ++it)
788 aCloned.push_back(ScEditUtil::Clone(**it, mrDestCol.GetDoc()).release());
790 maDestPos.miCellPos = mrDestCol.GetCellStore().set(
791 maDestPos.miCellPos, nTopRow, aCloned.begin(), aCloned.end());
793 break;
794 case sc::element_type_formula:
796 sc::formula_block::const_iterator it = sc::formula_block::begin(*aNode.data);
797 std::advance(it, nOffset);
798 sc::formula_block::const_iterator itEnd = it;
799 std::advance(itEnd, nDataSize);
801 std::vector<ScFormulaCell*> aCloned;
802 aCloned.reserve(nDataSize);
803 ScAddress aDestPos(mrDestCol.GetCol(), nTopRow, mrDestCol.GetTab());
804 for (; it != itEnd; ++it, aDestPos.IncRow())
806 const ScFormulaCell& rOld = **it;
807 if (rOld.GetDirty() && mrSrcCol.GetDoc().GetAutoCalc())
808 const_cast<ScFormulaCell&>(rOld).Interpret();
810 aCloned.push_back(new ScFormulaCell(rOld, mrDestCol.GetDoc(), aDestPos));
813 // Group the cloned formula cells.
814 if (!aCloned.empty())
815 sc::SharedFormulaUtil::groupFormulaCells(aCloned.begin(), aCloned.end());
817 sc::CellStoreType& rDestCells = mrDestCol.GetCellStore();
818 maDestPos.miCellPos = rDestCells.set(
819 maDestPos.miCellPos, nTopRow, aCloned.begin(), aCloned.end());
821 // Merge adjacent formula cell groups (if applicable).
822 sc::CellStoreType::position_type aPos =
823 rDestCells.position(maDestPos.miCellPos, nTopRow);
824 maDestPos.miCellPos = aPos.first;
825 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
826 size_t nLastRow = nTopRow + nDataSize;
827 if (nLastRow < o3tl::make_unsigned(mrSrcDoc.MaxRow()))
829 aPos = rDestCells.position(maDestPos.miCellPos, nLastRow+1);
830 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
833 break;
834 default:
835 bSet = false;
838 if (bSet)
839 setDefaultAttrsToDest(nTopRow, nDataSize);
841 mrSrcCol.DuplicateNotes(nTopRow, nDataSize, mrDestCol, maDestPos, false);
842 mrSrcCol.DuplicateSparklines(nTopRow, nDataSize, mrDestCol, maDestPos);
846 class CopyTextAttrToClipHandler
848 sc::CellTextAttrStoreType& mrDestAttrs;
849 sc::CellTextAttrStoreType::iterator miPos;
851 public:
852 explicit CopyTextAttrToClipHandler( sc::CellTextAttrStoreType& rAttrs ) :
853 mrDestAttrs(rAttrs), miPos(mrDestAttrs.begin()) {}
855 void operator() ( const sc::CellTextAttrStoreType::value_type& aNode, size_t nOffset, size_t nDataSize )
857 if (aNode.type != sc::element_type_celltextattr)
858 return;
860 sc::celltextattr_block::const_iterator it = sc::celltextattr_block::begin(*aNode.data);
861 std::advance(it, nOffset);
862 sc::celltextattr_block::const_iterator itEnd = it;
863 std::advance(itEnd, nDataSize);
865 size_t nPos = aNode.position + nOffset;
866 miPos = mrDestAttrs.set(miPos, nPos, it, itEnd);
873 void ScColumn::CopyToClip(
874 sc::CopyToClipContext& rCxt, SCROW nRow1, SCROW nRow2, ScColumn& rColumn ) const
876 if (!rCxt.isCopyChartRanges()) // No need to copy attributes for chart ranges
877 pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray,
878 rCxt.isKeepScenarioFlags() ? (ScMF::All & ~ScMF::Scenario) : ScMF::All );
881 CopyToClipHandler aFunc(GetDoc(), *this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol));
882 sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
885 if (!rCxt.isCopyChartRanges()) // No need to copy attributes for chart ranges
887 CopyTextAttrToClipHandler aFunc(rColumn.maCellTextAttrs);
888 sc::ParseBlock(maCellTextAttrs.begin(), maCellTextAttrs, aFunc, nRow1, nRow2);
891 rColumn.CellStorageModified();
894 void ScColumn::CopyStaticToDocument(
895 SCROW nRow1, SCROW nRow2, const SvNumberFormatterMergeMap& rMap, ScColumn& rDestCol )
897 if (nRow1 > nRow2)
898 return;
900 sc::ColumnBlockPosition aDestPos;
901 CopyCellTextAttrsToDocument(nRow1, nRow2, rDestCol);
902 CopyCellNotesToDocument(nRow1, nRow2, rDestCol);
904 // First, clear the destination column for the specified row range.
905 rDestCol.maCells.set_empty(nRow1, nRow2);
907 aDestPos.miCellPos = rDestCol.maCells.begin();
909 ScDocument& rDocument = GetDoc();
910 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
911 sc::CellStoreType::const_iterator it = aPos.first;
912 size_t nOffset = aPos.second;
913 size_t nDataSize = 0;
914 size_t nCurRow = nRow1;
916 for (; it != maCells.end() && nCurRow <= o3tl::make_unsigned(nRow2); ++it, nOffset = 0, nCurRow += nDataSize)
918 bool bLastBlock = false;
919 nDataSize = it->size - nOffset;
920 if (nCurRow + nDataSize - 1 > o3tl::make_unsigned(nRow2))
922 // Truncate the block to copy to clipboard.
923 nDataSize = nRow2 - nCurRow + 1;
924 bLastBlock = true;
927 switch (it->type)
929 case sc::element_type_numeric:
931 sc::numeric_block::const_iterator itData = sc::numeric_block::begin(*it->data);
932 std::advance(itData, nOffset);
933 sc::numeric_block::const_iterator itDataEnd = itData;
934 std::advance(itDataEnd, nDataSize);
935 aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd);
937 break;
938 case sc::element_type_string:
940 sc::string_block::const_iterator itData = sc::string_block::begin(*it->data);
941 std::advance(itData, nOffset);
942 sc::string_block::const_iterator itDataEnd = itData;
943 std::advance(itDataEnd, nDataSize);
944 aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd);
946 break;
947 case sc::element_type_edittext:
949 sc::edittext_block::const_iterator itData = sc::edittext_block::begin(*it->data);
950 std::advance(itData, nOffset);
951 sc::edittext_block::const_iterator itDataEnd = itData;
952 std::advance(itDataEnd, nDataSize);
954 // Convert to simple strings.
955 std::vector<svl::SharedString> aConverted;
956 aConverted.reserve(nDataSize);
957 for (; itData != itDataEnd; ++itData)
959 const EditTextObject& rObj = **itData;
960 svl::SharedString aSS = rDocument.GetSharedStringPool().intern(ScEditUtil::GetString(rObj, &rDocument));
961 aConverted.push_back(aSS);
963 aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, aConverted.begin(), aConverted.end());
965 break;
966 case sc::element_type_formula:
968 sc::formula_block::const_iterator itData = sc::formula_block::begin(*it->data);
969 std::advance(itData, nOffset);
970 sc::formula_block::const_iterator itDataEnd = itData;
971 std::advance(itDataEnd, nDataSize);
973 // Interpret and convert to raw values.
974 for (SCROW i = 0; itData != itDataEnd; ++itData, ++i)
976 SCROW nRow = nCurRow + i;
978 ScFormulaCell& rFC = **itData;
979 if (rFC.GetDirty() && rDocument.GetAutoCalc())
980 rFC.Interpret();
982 if (rFC.GetErrCode() != FormulaError::NONE)
983 // Skip cells with error.
984 continue;
986 if (rFC.IsValue())
987 aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, rFC.GetValue());
988 else
990 svl::SharedString aSS = rFC.GetString();
991 if (aSS.isValid())
992 aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, aSS);
996 break;
997 default:
1001 if (bLastBlock)
1002 break;
1005 // Don't forget to copy the number formats over. Charts may reference them.
1006 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
1008 sal_uInt32 nNumFmt = GetNumberFormat(rDocument.GetNonThreadedContext(), nRow);
1009 SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt);
1010 if (itNum != rMap.end())
1011 nNumFmt = itNum->second;
1013 rDestCol.SetNumberFormat(nRow, nNumFmt);
1016 rDestCol.CellStorageModified();
1019 void ScColumn::CopyCellToDocument( SCROW nSrcRow, SCROW nDestRow, ScColumn& rDestCol )
1021 ScDocument& rDocument = GetDoc();
1022 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nSrcRow);
1023 sc::CellStoreType::const_iterator it = aPos.first;
1024 bool bSet = true;
1025 switch (it->type)
1027 case sc::element_type_numeric:
1028 rDestCol.maCells.set(nDestRow, sc::numeric_block::at(*it->data, aPos.second));
1029 break;
1030 case sc::element_type_string:
1031 rDestCol.maCells.set(nDestRow, sc::string_block::at(*it->data, aPos.second));
1032 break;
1033 case sc::element_type_edittext:
1035 EditTextObject* p = sc::edittext_block::at(*it->data, aPos.second);
1036 if (&rDocument == &rDestCol.GetDoc())
1037 rDestCol.maCells.set(nDestRow, p->Clone().release());
1038 else
1039 rDestCol.maCells.set(nDestRow, ScEditUtil::Clone(*p, rDestCol.GetDoc()).release());
1041 break;
1042 case sc::element_type_formula:
1044 ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
1045 if (p->GetDirty() && rDocument.GetAutoCalc())
1046 p->Interpret();
1048 ScAddress aDestPos = p->aPos;
1049 aDestPos.SetRow(nDestRow);
1050 ScFormulaCell* pNew = new ScFormulaCell(*p, rDestCol.GetDoc(), aDestPos);
1051 rDestCol.SetFormulaCell(nDestRow, pNew);
1053 break;
1054 case sc::element_type_empty:
1055 default:
1056 // empty
1057 rDestCol.maCells.set_empty(nDestRow, nDestRow);
1058 bSet = false;
1061 if (bSet)
1063 rDestCol.maCellTextAttrs.set(nDestRow, maCellTextAttrs.get<sc::CellTextAttr>(nSrcRow));
1064 ScPostIt* pNote = maCellNotes.get<ScPostIt*>(nSrcRow);
1065 if (pNote)
1067 pNote = pNote->Clone(ScAddress(nCol, nSrcRow, nTab),
1068 rDestCol.GetDoc(),
1069 ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab),
1070 false).release();
1071 rDestCol.maCellNotes.set(nDestRow, pNote);
1072 pNote->UpdateCaptionPos(ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab));
1074 else
1075 rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
1077 else
1079 rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow);
1080 rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
1083 rDestCol.CellStorageModified();
1086 namespace {
1088 bool canCopyValue(const ScDocument& rDoc, const ScAddress& rPos, InsertDeleteFlags nFlags)
1090 sal_uInt32 nNumIndex = rDoc.GetAttr(rPos, ATTR_VALUE_FORMAT)->GetValue();
1091 SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nNumIndex);
1092 if ((nType == SvNumFormatType::DATE) || (nType == SvNumFormatType::TIME) || (nType == SvNumFormatType::DATETIME))
1093 return ((nFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE);
1095 return (nFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
1098 class CopyAsLinkHandler
1100 const ScColumn& mrSrcCol;
1101 ScColumn& mrDestCol;
1102 sc::ColumnBlockPosition maDestPos;
1103 sc::ColumnBlockPosition* mpDestPos;
1104 InsertDeleteFlags mnCopyFlags;
1106 sc::StartListeningType meListenType;
1108 void setDefaultAttrToDest(size_t nRow)
1110 maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
1111 maDestPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
1114 void setDefaultAttrsToDest(size_t nRow, size_t nSize)
1116 std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
1117 maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
1118 maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
1121 ScFormulaCell* createRefCell(size_t nRow)
1123 ScSingleRefData aRef;
1124 aRef.InitAddress(ScAddress(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab())); // Absolute reference.
1125 aRef.SetFlag3D(true);
1127 ScTokenArray aArr(mrDestCol.GetDoc());
1128 aArr.AddSingleReference(aRef);
1129 return new ScFormulaCell(mrDestCol.GetDoc(), ScAddress(mrDestCol.GetCol(), nRow, mrDestCol.GetTab()), aArr);
1132 void createRefBlock(const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
1134 size_t nTopRow = aNode.position + nOffset;
1136 for (size_t i = 0; i < nDataSize; ++i)
1138 SCROW nRow = nTopRow + i;
1139 mrDestCol.SetFormulaCell(maDestPos, nRow, createRefCell(nRow), meListenType);
1142 setDefaultAttrsToDest(nTopRow, nDataSize);
1145 public:
1146 CopyAsLinkHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos, InsertDeleteFlags nCopyFlags) :
1147 mrSrcCol(rSrcCol),
1148 mrDestCol(rDestCol),
1149 mpDestPos(pDestPos),
1150 mnCopyFlags(nCopyFlags),
1151 meListenType(sc::SingleCellListening)
1153 if (mpDestPos)
1154 maDestPos = *mpDestPos;
1157 ~CopyAsLinkHandler()
1159 if (mpDestPos)
1161 // Similar to CopyByCloneHandler, don't copy a singular iterator.
1163 sc::ColumnBlockPosition aTempBlock;
1164 mrDestCol.InitBlockPosition(aTempBlock);
1165 maDestPos.miBroadcasterPos = aTempBlock.miBroadcasterPos;
1168 *mpDestPos = maDestPos;
1172 void setStartListening( bool b )
1174 meListenType = b ? sc::SingleCellListening : sc::NoListening;
1177 void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
1179 size_t nRow = aNode.position + nOffset;
1181 if (mnCopyFlags & (InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES))
1183 bool bCloneCaption = (mnCopyFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
1184 mrSrcCol.DuplicateNotes(nRow, nDataSize, mrDestCol, maDestPos, bCloneCaption);
1187 switch (aNode.type)
1189 case sc::element_type_numeric:
1191 if ((mnCopyFlags & (InsertDeleteFlags::DATETIME|InsertDeleteFlags::VALUE)) == InsertDeleteFlags::NONE)
1192 return;
1194 sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
1195 std::advance(it, nOffset);
1196 sc::numeric_block::const_iterator itEnd = it;
1197 std::advance(itEnd, nDataSize);
1199 ScAddress aSrcPos(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab());
1200 for (; it != itEnd; ++it, aSrcPos.IncRow(), ++nRow)
1202 if (!canCopyValue(mrSrcCol.GetDoc(), aSrcPos, mnCopyFlags))
1203 continue;
1205 maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, createRefCell(nRow));
1206 setDefaultAttrToDest(nRow);
1209 break;
1210 case sc::element_type_string:
1211 case sc::element_type_edittext:
1213 if (!(mnCopyFlags & InsertDeleteFlags::STRING))
1214 return;
1216 createRefBlock(aNode, nOffset, nDataSize);
1218 break;
1219 case sc::element_type_formula:
1221 if (!(mnCopyFlags & InsertDeleteFlags::FORMULA))
1222 return;
1224 createRefBlock(aNode, nOffset, nDataSize);
1226 break;
1227 default:
1233 class CopyByCloneHandler
1235 const ScColumn& mrSrcCol;
1236 ScColumn& mrDestCol;
1237 sc::ColumnBlockPosition maDestPos;
1238 sc::ColumnBlockPosition* mpDestPos;
1239 svl::SharedStringPool* mpSharedStringPool;
1240 InsertDeleteFlags mnCopyFlags;
1242 sc::StartListeningType meListenType;
1243 ScCloneFlags mnFormulaCellCloneFlags;
1245 void setDefaultAttrToDest(size_t nRow)
1247 maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
1248 maDestPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
1251 void setDefaultAttrsToDest(size_t nRow, size_t nSize)
1253 std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
1254 maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
1255 maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
1258 void cloneFormulaCell(size_t nRow, ScFormulaCell& rSrcCell)
1260 ScAddress aDestPos(mrDestCol.GetCol(), nRow, mrDestCol.GetTab());
1262 bool bCloneValue = (mnCopyFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
1263 bool bCloneDateTime = (mnCopyFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE;
1264 bool bCloneString = (mnCopyFlags & InsertDeleteFlags::STRING) != InsertDeleteFlags::NONE;
1265 bool bCloneSpecialBoolean = (mnCopyFlags & InsertDeleteFlags::SPECIAL_BOOLEAN) != InsertDeleteFlags::NONE;
1266 bool bCloneFormula = (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE;
1268 bool bForceFormula = false;
1270 if (bCloneSpecialBoolean)
1272 // See if the formula consists of =TRUE() or =FALSE().
1273 const ScTokenArray* pCode = rSrcCell.GetCode();
1274 if (pCode && pCode->GetLen() == 1)
1276 const formula::FormulaToken* p = pCode->FirstToken();
1277 if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse)
1278 // This is a boolean formula.
1279 bForceFormula = true;
1283 if (bForceFormula || bCloneFormula)
1285 // Clone as formula cell.
1286 ScFormulaCell* pCell = new ScFormulaCell(rSrcCell, mrDestCol.GetDoc(), aDestPos, mnFormulaCellCloneFlags);
1287 pCell->SetDirtyVar();
1288 mrDestCol.SetFormulaCell(maDestPos, nRow, pCell, meListenType, rSrcCell.NeedsNumberFormat());
1289 setDefaultAttrToDest(nRow);
1290 return;
1293 if (mrDestCol.GetDoc().IsUndo())
1294 return;
1296 if (bCloneValue)
1298 FormulaError nErr = rSrcCell.GetErrCode();
1299 if (nErr != FormulaError::NONE)
1301 // error codes are cloned with values
1302 ScFormulaCell* pErrCell = new ScFormulaCell(mrDestCol.GetDoc(), aDestPos);
1303 pErrCell->SetErrCode(nErr);
1304 mrDestCol.SetFormulaCell(maDestPos, nRow, pErrCell, meListenType);
1305 setDefaultAttrToDest(nRow);
1306 return;
1310 if (bCloneValue || bCloneDateTime)
1312 if (rSrcCell.IsValue())
1314 if (canCopyValue(mrSrcCol.GetDoc(), ScAddress(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab()), mnCopyFlags))
1316 maDestPos.miCellPos = mrDestCol.GetCellStore().set(
1317 maDestPos.miCellPos, nRow, rSrcCell.GetValue());
1318 setDefaultAttrToDest(nRow);
1321 return;
1325 if (!bCloneString)
1326 return;
1328 svl::SharedString aStr = rSrcCell.GetString();
1329 if (aStr.isEmpty())
1330 // Don't create empty string cells.
1331 return;
1333 if (rSrcCell.IsMultilineResult())
1335 // Clone as an edit text object.
1336 EditEngine& rEngine = mrDestCol.GetDoc().GetEditEngine();
1337 rEngine.SetText(aStr.getString());
1338 maDestPos.miCellPos =
1339 mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, rEngine.CreateTextObject().release());
1341 else
1343 maDestPos.miCellPos =
1344 mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, aStr);
1347 setDefaultAttrToDest(nRow);
1350 public:
1351 CopyByCloneHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos,
1352 InsertDeleteFlags nCopyFlags, svl::SharedStringPool* pSharedStringPool, bool bGlobalNamesToLocal) :
1353 mrSrcCol(rSrcCol),
1354 mrDestCol(rDestCol),
1355 mpDestPos(pDestPos),
1356 mpSharedStringPool(pSharedStringPool),
1357 mnCopyFlags(nCopyFlags),
1358 meListenType(sc::SingleCellListening),
1359 mnFormulaCellCloneFlags(bGlobalNamesToLocal ? ScCloneFlags::NamesToLocal : ScCloneFlags::Default)
1361 if (mpDestPos)
1362 maDestPos = *mpDestPos;
1365 ~CopyByCloneHandler()
1367 if (!mpDestPos)
1368 return;
1370 // If broadcasters were setup in the same column,
1371 // maDestPos.miBroadcasterPos doesn't match
1372 // mrDestCol.maBroadcasters because it is never passed anywhere.
1373 // Assign a corresponding iterator before copying all over.
1374 // Otherwise this may result in wrongly copying a singular
1375 // iterator.
1378 /* XXX Using a temporary ColumnBlockPosition just for
1379 * initializing from ScColumn::maBroadcasters.begin() is ugly,
1380 * on the other hand we don't want to expose
1381 * ScColumn::maBroadcasters to the outer world and have a
1382 * getter. */
1383 sc::ColumnBlockPosition aTempBlock;
1384 mrDestCol.InitBlockPosition(aTempBlock);
1385 maDestPos.miBroadcasterPos = aTempBlock.miBroadcasterPos;
1388 *mpDestPos = maDestPos;
1391 void setStartListening( bool b )
1393 meListenType = b ? sc::SingleCellListening : sc::NoListening;
1396 void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
1398 size_t nRow = aNode.position + nOffset;
1400 if (mnCopyFlags & (InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES))
1402 bool bCloneCaption = (mnCopyFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
1403 mrSrcCol.DuplicateNotes(nRow, nDataSize, mrDestCol, maDestPos, bCloneCaption);
1406 switch (aNode.type)
1408 case sc::element_type_numeric:
1410 if ((mnCopyFlags & (InsertDeleteFlags::DATETIME|InsertDeleteFlags::VALUE)) == InsertDeleteFlags::NONE)
1411 return;
1413 sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
1414 std::advance(it, nOffset);
1415 sc::numeric_block::const_iterator itEnd = it;
1416 std::advance(itEnd, nDataSize);
1418 ScAddress aSrcPos(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab());
1419 for (; it != itEnd; ++it, aSrcPos.IncRow(), ++nRow)
1421 if (!canCopyValue(mrSrcCol.GetDoc(), aSrcPos, mnCopyFlags))
1422 continue;
1424 maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, *it);
1425 setDefaultAttrToDest(nRow);
1428 break;
1429 case sc::element_type_string:
1431 if (!(mnCopyFlags & InsertDeleteFlags::STRING))
1432 return;
1434 sc::string_block::const_iterator it = sc::string_block::begin(*aNode.data);
1435 std::advance(it, nOffset);
1436 sc::string_block::const_iterator itEnd = it;
1437 std::advance(itEnd, nDataSize);
1439 for (; it != itEnd; ++it, ++nRow)
1441 const svl::SharedString& rStr = *it;
1442 if (rStr.isEmpty())
1444 // String cell with empty value is used to special-case cell value removal.
1445 maDestPos.miCellPos = mrDestCol.GetCellStore().set_empty(
1446 maDestPos.miCellPos, nRow, nRow);
1447 maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set_empty(
1448 maDestPos.miCellTextAttrPos, nRow, nRow);
1450 else
1452 if (mpSharedStringPool)
1454 // Re-intern the string if source is a different document.
1455 svl::SharedString aInterned = mpSharedStringPool->intern( rStr.getString());
1456 maDestPos.miCellPos =
1457 mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, aInterned);
1459 else
1461 maDestPos.miCellPos =
1462 mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, rStr);
1464 setDefaultAttrToDest(nRow);
1468 break;
1469 case sc::element_type_edittext:
1471 if (!(mnCopyFlags & InsertDeleteFlags::STRING))
1472 return;
1474 sc::edittext_block::const_iterator it = sc::edittext_block::begin(*aNode.data);
1475 std::advance(it, nOffset);
1476 sc::edittext_block::const_iterator itEnd = it;
1477 std::advance(itEnd, nDataSize);
1479 std::vector<EditTextObject*> aCloned;
1480 aCloned.reserve(nDataSize);
1481 for (; it != itEnd; ++it)
1482 aCloned.push_back(ScEditUtil::Clone(**it, mrDestCol.GetDoc()).release());
1484 maDestPos.miCellPos = mrDestCol.GetCellStore().set(
1485 maDestPos.miCellPos, nRow, aCloned.begin(), aCloned.end());
1487 setDefaultAttrsToDest(nRow, nDataSize);
1489 break;
1490 case sc::element_type_formula:
1492 sc::formula_block::const_iterator it = sc::formula_block::begin(*aNode.data);
1493 std::advance(it, nOffset);
1494 sc::formula_block::const_iterator itEnd = it;
1495 std::advance(itEnd, nDataSize);
1497 sc::DelayStartListeningFormulaCells startDelay(mrDestCol); // disabled
1498 if(nDataSize > 1024 && (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE)
1500 // If the column to be replaced contains a long formula group (tdf#102364), there can
1501 // be so many listeners in a single vector that the quadratic cost of repeatedly removing
1502 // the first element becomes very high. Optimize this by removing them in one go.
1503 sc::EndListeningContext context(mrDestCol.GetDoc());
1504 mrDestCol.EndListeningFormulaCells( context, nRow, nRow + nDataSize - 1, nullptr, nullptr );
1505 // There can be a similar problem with starting to listen to cells repeatedly (tdf#133302).
1506 // Delay it.
1507 startDelay.set();
1510 for (; it != itEnd; ++it, ++nRow)
1511 cloneFormulaCell(nRow, **it);
1513 break;
1514 default:
1522 void ScColumn::CopyToColumn(
1523 sc::CopyToDocContext& rCxt,
1524 SCROW nRow1, SCROW nRow2, InsertDeleteFlags nFlags, bool bMarked, ScColumn& rColumn,
1525 const ScMarkData* pMarkData, bool bAsLink, bool bGlobalNamesToLocal) const
1527 if (bMarked)
1529 SCROW nStart, nEnd;
1530 if (pMarkData && pMarkData->IsMultiMarked())
1532 ScMultiSelIter aIter( pMarkData->GetMultiSelData(), nCol );
1534 while ( aIter.Next( nStart, nEnd ) && nStart <= nRow2 )
1536 if ( nEnd >= nRow1 )
1537 CopyToColumn(rCxt, std::max(nRow1,nStart), std::min(nRow2,nEnd),
1538 nFlags, false, rColumn, pMarkData, bAsLink );
1541 else
1543 OSL_FAIL("CopyToColumn: bMarked, but no mark");
1545 return;
1548 if ( (nFlags & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE )
1550 if ( (nFlags & InsertDeleteFlags::STYLES) != InsertDeleteFlags::STYLES )
1551 { // keep the StyleSheets in the target document
1552 // e.g. DIF and RTF Clipboard-Import
1553 for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
1555 const ScStyleSheet* pStyle(rColumn.pAttrArray->GetPattern( nRow )->GetStyleSheet());
1556 ScPatternAttr* pNewPattern(new ScPatternAttr(*pAttrArray->GetPattern(nRow)));
1557 pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(pStyle));
1558 rColumn.pAttrArray->SetPattern(nRow, CellAttributeHolder(pNewPattern, true));
1561 else
1562 pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray);
1565 if ((nFlags & InsertDeleteFlags::CONTENTS) == InsertDeleteFlags::NONE)
1566 return;
1568 if (bAsLink)
1570 CopyAsLinkHandler aFunc(*this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), nFlags);
1571 aFunc.setStartListening(rCxt.isStartListening());
1572 sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
1574 else
1576 // Compare the ScDocumentPool* to determine if we are copying
1577 // within the same document. If not, re-intern shared strings.
1578 svl::SharedStringPool* pSharedStringPool =
1579 (GetDoc().GetPool() != rColumn.GetDoc().GetPool()) ?
1580 &rColumn.GetDoc().GetSharedStringPool() : nullptr;
1581 CopyByCloneHandler aFunc(*this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), nFlags,
1582 pSharedStringPool, bGlobalNamesToLocal);
1583 aFunc.setStartListening(rCxt.isStartListening());
1584 sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
1587 rColumn.CellStorageModified();
1590 void ScColumn::UndoToColumn(
1591 sc::CopyToDocContext& rCxt, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nFlags, bool bMarked,
1592 ScColumn& rColumn ) const
1594 if (nRow1 > 0)
1595 CopyToColumn(rCxt, 0, nRow1-1, InsertDeleteFlags::FORMULA, false, rColumn);
1597 CopyToColumn(rCxt, nRow1, nRow2, nFlags, bMarked, rColumn); //TODO: bMarked ????
1599 if (nRow2 < GetDoc().MaxRow())
1600 CopyToColumn(rCxt, nRow2+1, GetDoc().MaxRow(), InsertDeleteFlags::FORMULA, false, rColumn);
1603 void ScColumn::CopyUpdated( const ScColumn* pPosCol, ScColumn& rDestCol ) const
1605 // Copy cells from this column to the destination column only for those
1606 // rows that are present in the position column (pPosCol).
1608 // First, mark all the non-empty cell ranges from the position column.
1609 sc::SingleColumnSpanSet aRangeSet(GetDoc().GetSheetLimits());
1610 if(pPosCol)
1611 aRangeSet.scan(*pPosCol);
1613 // Now, copy cells from this column to the destination column for those
1614 // marked row ranges.
1615 sc::SingleColumnSpanSet::SpansType aRanges;
1616 aRangeSet.getSpans(aRanges);
1618 CopyToClipHandler aFunc(GetDoc(), *this, rDestCol, nullptr);
1619 sc::CellStoreType::const_iterator itPos = maCells.begin();
1620 for (const auto& rRange : aRanges)
1621 itPos = sc::ParseBlock(itPos, maCells, aFunc, rRange.mnRow1, rRange.mnRow2);
1623 rDestCol.CellStorageModified();
1626 void ScColumn::CopyScenarioFrom( const ScColumn& rSrcCol )
1628 // This is the scenario table, the data is copied into it
1629 ScDocument& rDocument = GetDoc();
1630 ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), &rDocument.getCellAttributeHelper().getDefaultCellAttribute() );
1631 SCROW nStart = -1, nEnd = -1;
1632 const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
1633 while (pPattern)
1635 if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
1637 DeleteArea( nStart, nEnd, InsertDeleteFlags::CONTENTS );
1638 sc::CopyToDocContext aCxt(rDocument);
1639 rSrcCol.
1640 CopyToColumn(aCxt, nStart, nEnd, InsertDeleteFlags::CONTENTS, false, *this);
1642 // UpdateUsed not needed, already done in TestCopyScenario (obsolete comment ?)
1644 sc::RefUpdateContext aRefCxt(rDocument);
1645 aRefCxt.meMode = URM_COPY;
1646 aRefCxt.maRange = ScRange(nCol, nStart, nTab, nCol, nEnd, nTab);
1647 aRefCxt.mnTabDelta = nTab - rSrcCol.nTab;
1648 UpdateReferenceOnCopy(aRefCxt);
1649 UpdateCompile();
1651 pPattern = aAttrIter.Next( nStart, nEnd );
1655 void ScColumn::CopyScenarioTo( ScColumn& rDestCol ) const
1657 // This is the scenario table, the data is copied to the other
1658 ScDocument& rDocument = GetDoc();
1659 ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), &rDocument.getCellAttributeHelper().getDefaultCellAttribute() );
1660 SCROW nStart = -1, nEnd = -1;
1661 const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
1662 while (pPattern)
1664 if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
1666 rDestCol.DeleteArea( nStart, nEnd, InsertDeleteFlags::CONTENTS );
1667 sc::CopyToDocContext aCxt(rDestCol.GetDoc());
1668 CopyToColumn(aCxt, nStart, nEnd, InsertDeleteFlags::CONTENTS, false, rDestCol);
1670 sc::RefUpdateContext aRefCxt(rDocument);
1671 aRefCxt.meMode = URM_COPY;
1672 aRefCxt.maRange = ScRange(rDestCol.nCol, nStart, rDestCol.nTab, rDestCol.nCol, nEnd, rDestCol.nTab);
1673 aRefCxt.mnTabDelta = rDestCol.nTab - nTab;
1674 rDestCol.UpdateReferenceOnCopy(aRefCxt);
1675 rDestCol.UpdateCompile();
1677 pPattern = aAttrIter.Next( nStart, nEnd );
1681 bool ScColumn::TestCopyScenarioTo( const ScColumn& rDestCol ) const
1683 bool bOk = true;
1684 ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), &GetDoc().getCellAttributeHelper().getDefaultCellAttribute() );
1685 SCROW nStart = 0, nEnd = 0;
1686 const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
1687 while (pPattern && bOk)
1689 if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
1690 if ( rDestCol.pAttrArray->HasAttrib( nStart, nEnd, HasAttrFlags::Protected ) )
1691 bOk = false;
1693 pPattern = aAttrIter.Next( nStart, nEnd );
1695 return bOk;
1698 void ScColumn::MarkScenarioIn( ScMarkData& rDestMark ) const
1700 ScRange aRange( nCol, 0, nTab );
1702 ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), &GetDoc().getCellAttributeHelper().getDefaultCellAttribute() );
1703 SCROW nStart = -1, nEnd = -1;
1704 const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
1705 while (pPattern)
1707 if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
1709 aRange.aStart.SetRow( nStart );
1710 aRange.aEnd.SetRow( nEnd );
1711 rDestMark.SetMultiMarkArea( aRange );
1714 pPattern = aAttrIter.Next( nStart, nEnd );
1718 namespace {
1720 void resetColumnPosition(sc::CellStoreType& rCells, SCCOL nCol)
1722 for (auto& rCellItem : rCells)
1724 if (rCellItem.type != sc::element_type_formula)
1725 continue;
1727 sc::formula_block::iterator itCell = sc::formula_block::begin(*rCellItem.data);
1728 sc::formula_block::iterator itCellEnd = sc::formula_block::end(*rCellItem.data);
1729 for (; itCell != itCellEnd; ++itCell)
1731 ScFormulaCell& rCell = **itCell;
1732 rCell.aPos.SetCol(nCol);
1737 class NoteCaptionUpdater
1739 const ScDocument& m_rDocument;
1740 const ScAddress m_aAddress; // 'incomplete' address consisting of tab, column
1741 bool m_bUpdateCaptionPos; // false if we want to skip updating the caption pos, only useful in kit mode
1742 bool m_bAddressChanged; // false if the cell anchor address is unchanged
1743 public:
1744 NoteCaptionUpdater(const ScDocument& rDocument, const ScAddress& rPos, bool bUpdateCaptionPos, bool bAddressChanged)
1745 : m_rDocument(rDocument)
1746 , m_aAddress(rPos)
1747 , m_bUpdateCaptionPos(bUpdateCaptionPos)
1748 , m_bAddressChanged(bAddressChanged)
1752 void operator() ( size_t nRow, ScPostIt* p )
1754 // Create a 'complete' address object
1755 ScAddress aAddr(m_aAddress);
1756 aAddr.SetRow(nRow);
1758 if (m_bUpdateCaptionPos)
1759 p->UpdateCaptionPos(aAddr);
1761 // Notify our LOK clients
1762 if (m_bAddressChanged)
1763 ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Modify, m_rDocument, aAddr, p);
1769 void ScColumn::UpdateNoteCaptions( SCROW nRow1, SCROW nRow2, bool bAddressChanged )
1771 ScAddress aAddr(nCol, 0, nTab);
1772 NoteCaptionUpdater aFunc(GetDoc(), aAddr, true, bAddressChanged);
1773 sc::ProcessNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
1776 void ScColumn::CommentNotifyAddressChange( SCROW nRow1, SCROW nRow2 )
1778 ScAddress aAddr(nCol, 0, nTab);
1779 NoteCaptionUpdater aFunc(GetDoc(), aAddr, false, true);
1780 sc::ProcessNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
1783 void ScColumn::UpdateDrawObjects(std::vector<std::vector<SdrObject*>>& pObjects, SCROW nRowStart, SCROW nRowEnd)
1785 assert(static_cast<int>(pObjects.size()) >= nRowEnd - nRowStart + 1);
1787 int nObj = 0;
1788 for (SCROW nCurrentRow = nRowStart; nCurrentRow <= nRowEnd; nCurrentRow++, nObj++)
1790 if (pObjects[nObj].empty())
1791 continue; // No draw objects in this row
1793 UpdateDrawObjectsForRow(pObjects[nObj], nCol, nCurrentRow);
1797 void ScColumn::UpdateDrawObjectsForRow( std::vector<SdrObject*>& pObjects, SCCOL nTargetCol, SCROW nTargetRow )
1799 for (auto &pObject : pObjects)
1801 ScAddress aNewAddress(nTargetCol, nTargetRow, nTab);
1803 // Update draw object according to new anchor
1804 ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
1805 if (pDrawLayer)
1806 pDrawLayer->MoveObject(pObject, aNewAddress);
1810 bool ScColumn::IsDrawObjectsEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
1812 ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
1813 if (!pDrawLayer)
1814 return true;
1816 ScRange aRange(nCol, nStartRow, nTab, nCol, nEndRow, nTab);
1817 return !pDrawLayer->HasObjectsAnchoredInRange(aRange);
1820 void ScColumn::SwapCol(ScColumn& rCol)
1822 maBroadcasters.swap(rCol.maBroadcasters);
1823 maCells.swap(rCol.maCells);
1824 maCellTextAttrs.swap(rCol.maCellTextAttrs);
1825 maCellNotes.swap(rCol.maCellNotes);
1826 maSparklines.swap(rCol.maSparklines);
1828 // Swap all CellStoreEvent mdds event_func related.
1829 maCells.event_handler().swap(rCol.maCells.event_handler());
1830 maCellNotes.event_handler().swap(rCol.maCellNotes.event_handler());
1831 std::swap( mnBlkCountFormula, rCol.mnBlkCountFormula);
1832 std::swap(mnBlkCountCellNotes, rCol.mnBlkCountCellNotes);
1834 // notes update caption
1835 UpdateNoteCaptions(0, GetDoc().MaxRow());
1836 rCol.UpdateNoteCaptions(0, GetDoc().MaxRow());
1838 std::swap(pAttrArray, rCol.pAttrArray);
1840 // AttrArray needs to have the right column number
1841 pAttrArray->SetCol(nCol);
1842 rCol.pAttrArray->SetCol(rCol.nCol);
1844 // Reset column positions in formula cells.
1845 resetColumnPosition(maCells, nCol);
1846 resetColumnPosition(rCol.maCells, rCol.nCol);
1848 CellStorageModified();
1849 rCol.CellStorageModified();
1852 void ScColumn::MoveTo(SCROW nStartRow, SCROW nEndRow, ScColumn& rCol)
1854 pAttrArray->MoveTo(nStartRow, nEndRow, *rCol.pAttrArray);
1856 // Mark the non-empty cells within the specified range, for later broadcasting.
1857 sc::SingleColumnSpanSet aNonEmpties(GetDoc().GetSheetLimits());
1858 aNonEmpties.scan(*this, nStartRow, nEndRow);
1859 sc::SingleColumnSpanSet::SpansType aRanges;
1860 aNonEmpties.getSpans(aRanges);
1862 // Split the formula grouping at the top and bottom boundaries.
1863 sc::CellStoreType::position_type aPos = maCells.position(nStartRow);
1864 sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
1865 if (GetDoc().ValidRow(nEndRow+1))
1867 aPos = maCells.position(aPos.first, nEndRow+1);
1868 sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
1871 // Do the same with the destination column.
1872 aPos = rCol.maCells.position(nStartRow);
1873 sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
1874 if (GetDoc().ValidRow(nEndRow+1))
1876 aPos = rCol.maCells.position(aPos.first, nEndRow+1);
1877 sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
1880 // Move the broadcasters to the destination column.
1881 maBroadcasters.transfer(nStartRow, nEndRow, rCol.maBroadcasters, nStartRow);
1882 maCells.transfer(nStartRow, nEndRow, rCol.maCells, nStartRow);
1883 maCellTextAttrs.transfer(nStartRow, nEndRow, rCol.maCellTextAttrs, nStartRow);
1885 // move the notes to the destination column
1886 maCellNotes.transfer(nStartRow, nEndRow, rCol.maCellNotes, nStartRow);
1887 UpdateNoteCaptions(0, GetDoc().MaxRow());
1889 // Re-group transferred formula cells.
1890 aPos = rCol.maCells.position(nStartRow);
1891 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1892 if (GetDoc().ValidRow(nEndRow+1))
1894 aPos = rCol.maCells.position(aPos.first, nEndRow+1);
1895 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
1898 CellStorageModified();
1899 rCol.CellStorageModified();
1901 // Broadcast on moved ranges. Area-broadcast only.
1902 ScDocument& rDocument = GetDoc();
1903 ScHint aHint(SfxHintId::ScDataChanged, ScAddress(nCol, 0, nTab));
1904 for (const auto& rRange : aRanges)
1906 for (SCROW nRow = rRange.mnRow1; nRow <= rRange.mnRow2; ++nRow)
1908 aHint.SetAddressRow(nRow);
1909 rDocument.AreaBroadcast(aHint);
1914 namespace {
1916 class SharedTopFormulaCellPicker
1918 public:
1919 SharedTopFormulaCellPicker() = default;
1920 SharedTopFormulaCellPicker(SharedTopFormulaCellPicker const &) = default;
1921 SharedTopFormulaCellPicker(SharedTopFormulaCellPicker &&) = default;
1922 SharedTopFormulaCellPicker & operator =(SharedTopFormulaCellPicker const &) = default;
1923 SharedTopFormulaCellPicker & operator =(SharedTopFormulaCellPicker &&) = default;
1925 virtual ~SharedTopFormulaCellPicker() {}
1927 void operator() ( sc::CellStoreType::value_type& node )
1929 if (node.type != sc::element_type_formula)
1930 return;
1932 size_t nTopRow = node.position;
1934 sc::formula_block::iterator itBeg = sc::formula_block::begin(*node.data);
1935 sc::formula_block::iterator itEnd = sc::formula_block::end(*node.data);
1937 // Only pick shared formula cells that are the top cells of their
1938 // respective shared ranges.
1939 for (sc::formula_block::iterator it = itBeg; it != itEnd; ++it)
1941 ScFormulaCell* pCell = *it;
1942 size_t nRow = nTopRow + std::distance(itBeg, it);
1943 if (!pCell->IsShared())
1945 processNonShared(pCell, nRow);
1946 continue;
1949 if (pCell->IsSharedTop())
1951 ScFormulaCell** pp = &(*it);
1952 SCROW nCellLen = pCell->GetSharedLength();
1953 assert(nCellLen > 0);
1954 processSharedTop(pp, nRow, nCellLen);
1956 // Move to the last cell in the group, to get incremented to
1957 // the next cell in the next iteration.
1958 size_t nOffsetToLast = nCellLen - 1;
1959 std::advance(it, nOffsetToLast);
1964 virtual void processNonShared( ScFormulaCell* /*pCell*/, size_t /*nRow*/ ) {}
1965 virtual void processSharedTop( ScFormulaCell** /*ppCells*/, size_t /*nRow*/, size_t /*nLength*/ ) {}
1968 class UpdateRefOnCopy
1970 const sc::RefUpdateContext& mrCxt;
1971 ScDocument* mpUndoDoc;
1972 bool mbUpdated;
1974 public:
1975 UpdateRefOnCopy(const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc) :
1976 mrCxt(rCxt), mpUndoDoc(pUndoDoc), mbUpdated(false) {}
1978 bool isUpdated() const { return mbUpdated; }
1980 void operator() (sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
1982 if (node.type != sc::element_type_formula)
1983 return;
1985 sc::formula_block::iterator it = sc::formula_block::begin(*node.data);
1986 std::advance(it, nOffset);
1987 sc::formula_block::iterator itEnd = it;
1988 std::advance(itEnd, nDataSize);
1990 for (; it != itEnd; ++it)
1992 ScFormulaCell& rCell = **it;
1993 mbUpdated |= rCell.UpdateReference(mrCxt, mpUndoDoc);
1998 class UpdateRefOnNonCopy
2000 SCCOL mnCol;
2001 SCROW mnTab;
2002 const sc::RefUpdateContext* mpCxt;
2003 ScDocument* mpUndoDoc;
2004 bool mbUpdated;
2005 bool mbClipboardSource;
2007 void recompileTokenArray( ScFormulaCell& rTopCell )
2009 // We need to re-compile the token array when a range name is
2010 // modified, to correctly reflect the new references in the
2011 // name.
2012 ScCompiler aComp(mpCxt->mrDoc, rTopCell.aPos, *rTopCell.GetCode(), mpCxt->mrDoc.GetGrammar(),
2013 true, rTopCell.GetMatrixFlag() != ScMatrixMode::NONE);
2014 aComp.CompileTokenArray();
2017 void updateRefOnShift( sc::FormulaGroupEntry& rGroup )
2019 if (!rGroup.mbShared)
2021 ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
2022 mbUpdated |= rGroup.mpCell->UpdateReferenceOnShift(*mpCxt, mpUndoDoc, &aUndoPos);
2023 return;
2026 // Update references of a formula group.
2027 ScFormulaCell** pp = rGroup.mpCells;
2028 ScFormulaCell** ppEnd = pp + rGroup.mnLength;
2029 ScFormulaCell* pTop = *pp;
2030 ScTokenArray* pCode = pTop->GetCode();
2031 ScTokenArray aOldCode(pCode->CloneValue());
2032 ScAddress aOldPos = pTop->aPos;
2034 // Run this before the position gets updated.
2035 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(*mpCxt, aOldPos);
2037 bool bGroupShifted = false;
2038 if (pTop->UpdatePosOnShift(*mpCxt))
2040 ScAddress aErrorPos( ScAddress::UNINITIALIZED );
2041 // Update the positions of all formula cells.
2042 for (++pp; pp != ppEnd; ++pp) // skip the top cell.
2044 ScFormulaCell* pFC = *pp;
2045 if (!pFC->aPos.Move(mpCxt->mnColDelta, mpCxt->mnRowDelta, mpCxt->mnTabDelta,
2046 aErrorPos, mpCxt->mrDoc))
2048 assert(!"can't move formula cell");
2052 if (pCode->IsRecalcModeOnRefMove())
2053 aRes.mbValueChanged = true;
2055 // FormulaGroupAreaListener (contrary to ScBroadcastArea) is not
2056 // updated but needs to be re-setup, else at least its mpColumn
2057 // would indicate the old column to collect cells from. tdf#129396
2058 /* TODO: investigate if that could be short-cut to avoid all the
2059 * EndListeningTo() / StartListeningTo() overhead and is really
2060 * only necessary when shifting the column, not also when shifting
2061 * rows. */
2062 bGroupShifted = true;
2064 else if (aRes.mbReferenceModified && pCode->IsRecalcModeOnRefMove())
2066 // The cell itself hasn't shifted. But it may have ROW or COLUMN
2067 // referencing another cell that has.
2068 aRes.mbValueChanged = true;
2071 if (aRes.mbNameModified)
2072 recompileTokenArray(*pTop);
2074 if (aRes.mbReferenceModified || aRes.mbNameModified || bGroupShifted)
2076 sc::EndListeningContext aEndCxt(mpCxt->mrDoc, &aOldCode);
2077 aEndCxt.setPositionDelta(
2078 ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));
2080 for (pp = rGroup.mpCells; pp != ppEnd; ++pp)
2082 ScFormulaCell* p = *pp;
2083 p->EndListeningTo(aEndCxt);
2084 p->SetNeedsListening(true);
2087 mbUpdated = true;
2089 fillUndoDoc(aOldPos, rGroup.mnLength, aOldCode);
2092 if (aRes.mbValueChanged)
2094 for (pp = rGroup.mpCells; pp != ppEnd; ++pp)
2096 ScFormulaCell* p = *pp;
2097 p->SetNeedsDirty(true);
2102 void updateRefOnMove( sc::FormulaGroupEntry& rGroup )
2104 if (!rGroup.mbShared)
2106 ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
2107 mbUpdated |= rGroup.mpCell->UpdateReferenceOnMove(*mpCxt, mpUndoDoc, &aUndoPos);
2108 return;
2111 // Update references of a formula group.
2112 ScFormulaCell** pp = rGroup.mpCells;
2113 ScFormulaCell** ppEnd = pp + rGroup.mnLength;
2114 ScFormulaCell* pTop = *pp;
2115 ScTokenArray* pCode = pTop->GetCode();
2116 ScTokenArray aOldCode(pCode->CloneValue());
2118 ScAddress aPos = pTop->aPos;
2119 ScAddress aOldPos = aPos;
2121 bool bCellMoved;
2122 if (mpCxt->maRange.Contains(aPos))
2124 bCellMoved = true;
2126 // The cell is being moved or copied to a new position. The
2127 // position has already been updated prior to this call.
2128 // Determine its original position before the move which will be
2129 // used to adjust relative references later.
2131 aOldPos.Set(
2132 aPos.Col() - mpCxt->mnColDelta,
2133 aPos.Row() - mpCxt->mnRowDelta,
2134 aPos.Tab() - mpCxt->mnTabDelta);
2136 else
2138 bCellMoved = false;
2141 bool bRecalcOnMove = pCode->IsRecalcModeOnRefMove();
2142 if (bRecalcOnMove)
2143 bRecalcOnMove = aPos != aOldPos;
2145 sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(*mpCxt, aOldPos, aPos);
2147 if (!(aRes.mbReferenceModified || aRes.mbNameModified || bRecalcOnMove))
2148 return;
2150 sc::AutoCalcSwitch aACSwitch(mpCxt->mrDoc, false);
2152 if (aRes.mbNameModified)
2153 recompileTokenArray(*pTop);
2155 // Perform end-listening, start-listening, and dirtying on all
2156 // formula cells in the group.
2158 // Make sure that the start and end listening contexts share the
2159 // same block position set, else an invalid iterator may ensue.
2160 const auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(mpCxt->mrDoc);
2161 sc::StartListeningContext aStartCxt(mpCxt->mrDoc, pPosSet);
2162 sc::EndListeningContext aEndCxt(mpCxt->mrDoc, pPosSet, &aOldCode);
2164 aEndCxt.setPositionDelta(
2165 ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));
2167 for (; pp != ppEnd; ++pp)
2169 ScFormulaCell* p = *pp;
2170 p->EndListeningTo(aEndCxt);
2171 p->StartListeningTo(aStartCxt);
2172 p->SetDirty();
2175 mbUpdated = true;
2177 // Move from clipboard is Cut&Paste, then do not copy the original
2178 // positions' formula cells to the Undo document.
2179 if (!mbClipboardSource || !bCellMoved)
2180 fillUndoDoc(aOldPos, rGroup.mnLength, aOldCode);
2183 void fillUndoDoc( const ScAddress& rOldPos, SCROW nLength, const ScTokenArray& rOldCode )
2185 if (!mpUndoDoc || nLength <= 0)
2186 return;
2188 // Insert the old formula group into the undo document.
2189 ScAddress aUndoPos = rOldPos;
2190 ScFormulaCell* pFC = new ScFormulaCell(*mpUndoDoc, aUndoPos, rOldCode.Clone());
2192 if (nLength == 1)
2194 mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
2195 return;
2198 std::vector<ScFormulaCell*> aCells;
2199 aCells.reserve(nLength);
2200 ScFormulaCellGroupRef xGroup = pFC->CreateCellGroup(nLength, false);
2201 aCells.push_back(pFC);
2202 aUndoPos.IncRow();
2203 for (SCROW i = 1; i < nLength; ++i, aUndoPos.IncRow())
2205 pFC = new ScFormulaCell(*mpUndoDoc, aUndoPos, xGroup);
2206 aCells.push_back(pFC);
2209 if (!mpUndoDoc->SetFormulaCells(rOldPos, aCells))
2210 // Insertion failed. Delete all formula cells.
2211 std::for_each(aCells.begin(), aCells.end(), std::default_delete<ScFormulaCell>());
2214 public:
2215 UpdateRefOnNonCopy(
2216 SCCOL nCol, SCTAB nTab, const sc::RefUpdateContext* pCxt,
2217 ScDocument* pUndoDoc) :
2218 mnCol(nCol), mnTab(nTab), mpCxt(pCxt),
2219 mpUndoDoc(pUndoDoc), mbUpdated(false),
2220 mbClipboardSource(pCxt->mrDoc.IsClipboardSource()){}
2222 void operator() ( sc::FormulaGroupEntry& rGroup )
2224 switch (mpCxt->meMode)
2226 case URM_INSDEL:
2227 updateRefOnShift(rGroup);
2228 return;
2229 case URM_MOVE:
2230 updateRefOnMove(rGroup);
2231 return;
2232 default:
2236 if (rGroup.mbShared)
2238 ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
2239 ScFormulaCell** pp = rGroup.mpCells;
2240 ScFormulaCell** ppEnd = pp + rGroup.mnLength;
2241 for (; pp != ppEnd; ++pp, aUndoPos.IncRow())
2243 ScFormulaCell* p = *pp;
2244 mbUpdated |= p->UpdateReference(*mpCxt, mpUndoDoc, &aUndoPos);
2247 else
2249 ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
2250 mbUpdated |= rGroup.mpCell->UpdateReference(*mpCxt, mpUndoDoc, &aUndoPos);
2254 bool isUpdated() const { return mbUpdated; }
2257 class UpdateRefGroupBoundChecker : public SharedTopFormulaCellPicker
2259 const sc::RefUpdateContext& mrCxt;
2260 std::vector<SCROW>& mrBounds;
2262 public:
2263 UpdateRefGroupBoundChecker(const sc::RefUpdateContext& rCxt, std::vector<SCROW>& rBounds) :
2264 mrCxt(rCxt), mrBounds(rBounds) {}
2266 virtual void processSharedTop( ScFormulaCell** ppCells, size_t /*nRow*/, size_t /*nLength*/ ) override
2268 // Check its tokens and record its reference boundaries.
2269 ScFormulaCell& rCell = **ppCells;
2270 const ScTokenArray& rCode = *rCell.GetCode();
2271 rCode.CheckRelativeReferenceBounds(
2272 mrCxt, rCell.aPos, rCell.GetSharedLength(), mrBounds);
2276 class UpdateRefExpandGroupBoundChecker : public SharedTopFormulaCellPicker
2278 const sc::RefUpdateContext& mrCxt;
2279 std::vector<SCROW>& mrBounds;
2281 public:
2282 UpdateRefExpandGroupBoundChecker(const sc::RefUpdateContext& rCxt, std::vector<SCROW>& rBounds) :
2283 mrCxt(rCxt), mrBounds(rBounds) {}
2285 virtual void processSharedTop( ScFormulaCell** ppCells, size_t /*nRow*/, size_t /*nLength*/ ) override
2287 // Check its tokens and record its reference boundaries.
2288 ScFormulaCell& rCell = **ppCells;
2289 const ScTokenArray& rCode = *rCell.GetCode();
2290 rCode.CheckExpandReferenceBounds(
2291 mrCxt, rCell.aPos, rCell.GetSharedLength(), mrBounds);
2295 class FormulaGroupPicker : public SharedTopFormulaCellPicker
2297 std::vector<sc::FormulaGroupEntry>& mrGroups;
2299 public:
2300 explicit FormulaGroupPicker( std::vector<sc::FormulaGroupEntry>& rGroups ) : mrGroups(rGroups) {}
2302 virtual void processNonShared( ScFormulaCell* pCell, size_t nRow ) override
2304 mrGroups.emplace_back(pCell, nRow);
2307 virtual void processSharedTop( ScFormulaCell** ppCells, size_t nRow, size_t nLength ) override
2309 mrGroups.emplace_back(ppCells, nRow, nLength);
2315 bool ScColumn::UpdateReferenceOnCopy( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc )
2317 // When copying, the range equals the destination range where cells
2318 // are pasted, and the dx, dy, dz refer to the distance from the
2319 // source range.
2321 UpdateRefOnCopy aHandler(rCxt, pUndoDoc);
2322 sc::ColumnBlockPosition* blockPos = rCxt.getBlockPosition(nTab, nCol);
2323 sc::CellStoreType::position_type aPos = blockPos
2324 ? maCells.position(blockPos->miCellPos, rCxt.maRange.aStart.Row())
2325 : maCells.position(rCxt.maRange.aStart.Row());
2326 sc::ProcessBlock(aPos.first, maCells, aHandler, rCxt.maRange.aStart.Row(), rCxt.maRange.aEnd.Row());
2328 // The formula groups at the top and bottom boundaries are expected to
2329 // have been split prior to this call. Here, we only do the joining.
2330 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
2331 if (rCxt.maRange.aEnd.Row() < GetDoc().MaxRow())
2333 aPos = maCells.position(aPos.first, rCxt.maRange.aEnd.Row()+1);
2334 sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
2337 return aHandler.isUpdated();
2340 bool ScColumn::UpdateReference( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc )
2342 if (IsEmptyData() || GetDoc().IsClipOrUndo())
2343 // Cells in this column are all empty, or clip or undo doc. No update needed.
2344 return false;
2346 if (rCxt.meMode == URM_COPY)
2347 return UpdateReferenceOnCopy(rCxt, pUndoDoc);
2349 std::vector<SCROW> aBounds;
2351 bool bThisColShifted = (rCxt.maRange.aStart.Tab() <= nTab && nTab <= rCxt.maRange.aEnd.Tab() &&
2352 rCxt.maRange.aStart.Col() <= nCol && nCol <= rCxt.maRange.aEnd.Col());
2353 if (bThisColShifted)
2355 // Cells in this column is being shifted. Split formula grouping at
2356 // the top and bottom boundaries before they get shifted.
2357 // Also, for deleted rows split at the top of the deleted area to adapt
2358 // the affected group length.
2359 SCROW nSplitPos;
2360 if (rCxt.mnRowDelta < 0)
2362 nSplitPos = rCxt.maRange.aStart.Row() + rCxt.mnRowDelta;
2363 if (GetDoc().ValidRow(nSplitPos))
2364 aBounds.push_back(nSplitPos);
2366 nSplitPos = rCxt.maRange.aStart.Row();
2367 if (GetDoc().ValidRow(nSplitPos))
2369 aBounds.push_back(nSplitPos);
2370 nSplitPos = rCxt.maRange.aEnd.Row() + 1;
2371 if (GetDoc().ValidRow(nSplitPos))
2372 aBounds.push_back(nSplitPos);
2376 // Check the row positions at which the group must be split per relative
2377 // references.
2379 UpdateRefGroupBoundChecker aBoundChecker(rCxt, aBounds);
2380 std::for_each(maCells.begin(), maCells.end(), std::move(aBoundChecker));
2383 // If expand reference edges is on, splitting groups may happen anywhere
2384 // where a reference points to an adjacent row of the insertion.
2385 if (rCxt.mnRowDelta > 0 && rCxt.mrDoc.IsExpandRefs())
2387 UpdateRefExpandGroupBoundChecker aExpandChecker(rCxt, aBounds);
2388 std::for_each(maCells.begin(), maCells.end(), std::move(aExpandChecker));
2391 // Do the actual splitting.
2392 const bool bSplit = sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
2394 // Collect all formula groups.
2395 std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
2397 // Process all collected formula groups.
2398 UpdateRefOnNonCopy aHandler(nCol, nTab, &rCxt, pUndoDoc);
2399 aHandler = std::for_each(aGroups.begin(), aGroups.end(), aHandler);
2400 if (bSplit || aHandler.isUpdated())
2401 rCxt.maRegroupCols.set(nTab, nCol);
2403 return aHandler.isUpdated();
2406 std::vector<sc::FormulaGroupEntry> ScColumn::GetFormulaGroupEntries()
2408 std::vector<sc::FormulaGroupEntry> aGroups;
2409 std::for_each(maCells.begin(), maCells.end(), FormulaGroupPicker(aGroups));
2410 return aGroups;
2413 namespace {
2415 class UpdateTransHandler
2417 ScColumn& mrColumn;
2418 sc::CellStoreType::iterator miPos;
2419 ScRange maSource;
2420 ScAddress maDest;
2421 ScDocument* mpUndoDoc;
2422 public:
2423 UpdateTransHandler(ScColumn& rColumn, const ScRange& rSource, const ScAddress& rDest, ScDocument* pUndoDoc) :
2424 mrColumn(rColumn),
2425 miPos(rColumn.GetCellStore().begin()),
2426 maSource(rSource), maDest(rDest), mpUndoDoc(pUndoDoc) {}
2428 void operator() (size_t nRow, ScFormulaCell* pCell)
2430 sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
2431 miPos = aPos.first;
2432 sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
2433 pCell->UpdateTranspose(maSource, maDest, mpUndoDoc);
2434 ScColumn::JoinNewFormulaCell(aPos, *pCell);
2438 class UpdateGrowHandler
2440 ScColumn& mrColumn;
2441 sc::CellStoreType::iterator miPos;
2442 ScRange maArea;
2443 SCCOL mnGrowX;
2444 SCROW mnGrowY;
2445 public:
2446 UpdateGrowHandler(ScColumn& rColumn, const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY) :
2447 mrColumn(rColumn),
2448 miPos(rColumn.GetCellStore().begin()),
2449 maArea(rArea), mnGrowX(nGrowX), mnGrowY(nGrowY) {}
2451 void operator() (size_t nRow, ScFormulaCell* pCell)
2453 sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
2454 miPos = aPos.first;
2455 sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
2456 pCell->UpdateGrow(maArea, mnGrowX, mnGrowY);
2457 ScColumn::JoinNewFormulaCell(aPos, *pCell);
2461 class InsertTabUpdater
2463 sc::RefUpdateInsertTabContext& mrCxt;
2464 sc::CellTextAttrStoreType& mrTextAttrs;
2465 sc::CellTextAttrStoreType::iterator miAttrPos;
2466 SCTAB mnTab;
2467 bool mbModified;
2469 public:
2470 InsertTabUpdater(sc::RefUpdateInsertTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
2471 mrCxt(rCxt),
2472 mrTextAttrs(rTextAttrs),
2473 miAttrPos(rTextAttrs.begin()),
2474 mnTab(nTab),
2475 mbModified(false) {}
2477 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2479 pCell->UpdateInsertTab(mrCxt);
2480 mbModified = true;
2483 void operator() (size_t nRow, EditTextObject* pCell)
2485 editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
2486 aUpdater.updateTableFields(mnTab);
2487 miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
2488 mbModified = true;
2491 bool isModified() const { return mbModified; }
2494 class DeleteTabUpdater
2496 sc::RefUpdateDeleteTabContext& mrCxt;
2497 sc::CellTextAttrStoreType& mrTextAttrs;
2498 sc::CellTextAttrStoreType::iterator miAttrPos;
2499 SCTAB mnTab;
2500 bool mbModified;
2501 public:
2502 DeleteTabUpdater(sc::RefUpdateDeleteTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
2503 mrCxt(rCxt),
2504 mrTextAttrs(rTextAttrs),
2505 miAttrPos(rTextAttrs.begin()),
2506 mnTab(nTab),
2507 mbModified(false) {}
2509 void operator() (size_t, ScFormulaCell* pCell)
2511 pCell->UpdateDeleteTab(mrCxt);
2512 mbModified = true;
2515 void operator() (size_t nRow, EditTextObject* pCell)
2517 editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
2518 aUpdater.updateTableFields(mnTab);
2519 miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
2520 mbModified = true;
2523 bool isModified() const { return mbModified; }
2526 class InsertAbsTabUpdater
2528 sc::CellTextAttrStoreType& mrTextAttrs;
2529 sc::CellTextAttrStoreType::iterator miAttrPos;
2530 SCTAB mnTab;
2531 SCTAB mnNewPos;
2532 bool mbModified;
2533 public:
2534 InsertAbsTabUpdater(sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab, SCTAB nNewPos) :
2535 mrTextAttrs(rTextAttrs),
2536 miAttrPos(rTextAttrs.begin()),
2537 mnTab(nTab),
2538 mnNewPos(nNewPos),
2539 mbModified(false) {}
2541 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2543 pCell->UpdateInsertTabAbs(mnNewPos);
2544 mbModified = true;
2547 void operator() (size_t nRow, EditTextObject* pCell)
2549 editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
2550 aUpdater.updateTableFields(mnTab);
2551 miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
2552 mbModified = true;
2555 bool isModified() const { return mbModified; }
2558 class MoveTabUpdater
2560 sc::RefUpdateMoveTabContext& mrCxt;
2561 sc::CellTextAttrStoreType& mrTextAttrs;
2562 sc::CellTextAttrStoreType::iterator miAttrPos;
2563 SCTAB mnTab;
2564 bool mbModified;
2565 public:
2566 MoveTabUpdater(sc::RefUpdateMoveTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
2567 mrCxt(rCxt),
2568 mrTextAttrs(rTextAttrs),
2569 miAttrPos(rTextAttrs.begin()),
2570 mnTab(nTab),
2571 mbModified(false) {}
2573 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2575 pCell->UpdateMoveTab(mrCxt, mnTab);
2576 mbModified = true;
2579 void operator() (size_t nRow, EditTextObject* pCell)
2581 editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
2582 aUpdater.updateTableFields(mnTab);
2583 miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
2584 mbModified = true;
2587 bool isModified() const { return mbModified; }
2590 class UpdateCompileHandler
2592 bool mbForceIfNameInUse:1;
2593 public:
2594 explicit UpdateCompileHandler(bool bForceIfNameInUse) :
2595 mbForceIfNameInUse(bForceIfNameInUse) {}
2597 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2599 pCell->UpdateCompile(mbForceIfNameInUse);
2603 class TabNoSetter
2605 SCTAB mnTab;
2606 public:
2607 explicit TabNoSetter(SCTAB nTab) : mnTab(nTab) {}
2609 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2611 pCell->aPos.SetTab(mnTab);
2615 class UsedRangeNameFinder
2617 sc::UpdatedRangeNames& mrIndexes;
2618 public:
2619 explicit UsedRangeNameFinder(sc::UpdatedRangeNames& rIndexes) : mrIndexes(rIndexes) {}
2621 void operator() (size_t /*nRow*/, const ScFormulaCell* pCell)
2623 pCell->FindRangeNamesInUse(mrIndexes);
2627 class CheckVectorizationHandler
2629 public:
2630 CheckVectorizationHandler()
2633 void operator() (size_t /*nRow*/, ScFormulaCell* p)
2635 ScTokenArray* pCode = p->GetCode();
2636 if (pCode && pCode->IsFormulaVectorDisabled())
2638 pCode->ResetVectorState();
2639 FormulaTokenArrayPlainIterator aIter(*pCode);
2640 FormulaToken* pFT = aIter.First();
2641 while (pFT)
2643 pCode->CheckToken(*pFT);
2644 pFT = aIter.Next();
2650 struct SetDirtyVarHandler
2652 void operator() (size_t /*nRow*/, ScFormulaCell* p)
2654 p->SetDirtyVar();
2658 class SetDirtyHandler
2660 ScDocument& mrDoc;
2661 const sc::SetFormulaDirtyContext& mrCxt;
2662 public:
2663 SetDirtyHandler( ScDocument& rDoc, const sc::SetFormulaDirtyContext& rCxt ) :
2664 mrDoc(rDoc), mrCxt(rCxt) {}
2666 void operator() (size_t /*nRow*/, ScFormulaCell* p)
2668 if (mrCxt.mbClearTabDeletedFlag)
2670 if (!p->IsShared() || p->IsSharedTop())
2672 ScTokenArray* pCode = p->GetCode();
2673 pCode->ClearTabDeleted(
2674 p->aPos, mrCxt.mnTabDeletedStart, mrCxt.mnTabDeletedEnd);
2678 p->SetDirtyVar();
2679 if (!mrDoc.IsInFormulaTree(p))
2680 mrDoc.PutInFormulaTree(p);
2684 class SetDirtyOnRangeHandler
2686 sc::SingleColumnSpanSet maValueRanges;
2687 ScColumn& mrColumn;
2688 public:
2689 explicit SetDirtyOnRangeHandler(ScColumn& rColumn)
2690 : maValueRanges(rColumn.GetDoc().GetSheetLimits()),
2691 mrColumn(rColumn) {}
2693 void operator() (size_t /*nRow*/, ScFormulaCell* p)
2695 p->SetDirty();
2698 void operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize)
2700 if (type == sc::element_type_empty)
2701 // Ignore empty blocks.
2702 return;
2704 // Non-formula cells.
2705 SCROW nRow1 = nTopRow;
2706 SCROW nRow2 = nTopRow + nDataSize - 1;
2707 maValueRanges.set(nRow1, nRow2, true);
2710 void broadcast()
2712 std::vector<SCROW> aRows;
2713 maValueRanges.getRows(aRows);
2714 mrColumn.BroadcastCells(aRows, SfxHintId::ScDataChanged);
2717 void fillBroadcastSpans( sc::ColumnSpanSet& rBroadcastSpans ) const
2719 SCCOL nCol = mrColumn.GetCol();
2720 SCTAB nTab = mrColumn.GetTab();
2721 sc::SingleColumnSpanSet::SpansType aSpans;
2722 maValueRanges.getSpans(aSpans);
2724 for (const auto& rSpan : aSpans)
2725 rBroadcastSpans.set(mrColumn.GetDoc(), nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, true);
2729 class SetTableOpDirtyOnRangeHandler
2731 sc::SingleColumnSpanSet maValueRanges;
2732 ScColumn& mrColumn;
2733 public:
2734 explicit SetTableOpDirtyOnRangeHandler(ScColumn& rColumn)
2735 : maValueRanges(rColumn.GetDoc().GetSheetLimits()),
2736 mrColumn(rColumn) {}
2738 void operator() (size_t /*nRow*/, ScFormulaCell* p)
2740 p->SetTableOpDirty();
2743 void operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize)
2745 if (type == sc::element_type_empty)
2746 // Ignore empty blocks.
2747 return;
2749 // Non-formula cells.
2750 SCROW nRow1 = nTopRow;
2751 SCROW nRow2 = nTopRow + nDataSize - 1;
2752 maValueRanges.set(nRow1, nRow2, true);
2755 void broadcast()
2757 std::vector<SCROW> aRows;
2758 maValueRanges.getRows(aRows);
2759 mrColumn.BroadcastCells(aRows, SfxHintId::ScTableOpDirty);
2763 struct SetDirtyAfterLoadHandler
2765 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2767 #if 1
2768 // Simply set dirty and append to FormulaTree, without broadcasting,
2769 // which is a magnitude faster. This is used to calculate the entire
2770 // document, e.g. when loading alien file formats.
2771 pCell->SetDirtyAfterLoad();
2772 #else
2773 /* This was used with the binary file format that stored results, where only
2774 * newly compiled and volatile functions and their dependents had to be
2775 * recalculated, which was faster then. Since that was moved to 'binfilter' to
2776 * convert to an XML file this isn't needed anymore, and not used for other
2777 * file formats. Kept for reference in case mechanism needs to be reactivated
2778 * for some file formats, we'd have to introduce a controlling parameter to
2779 * this method here then.
2782 // If the cell was already dirty because of CalcAfterLoad,
2783 // FormulaTracking has to take place.
2784 if (pCell->GetDirty())
2785 pCell->SetDirty();
2786 #endif
2790 struct SetDirtyIfPostponedHandler
2792 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2794 if (pCell->IsPostponedDirty() || (pCell->HasRelNameReference() != ScFormulaCell::RelNameRef::NONE))
2795 pCell->SetDirty();
2799 struct CalcAllHandler
2801 #define DEBUG_SC_CHECK_FORMULATREE_CALCULATION 0
2802 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2804 #if DEBUG_SC_CHECK_FORMULATREE_CALCULATION
2805 // after F9 ctrl-F9: check the calculation for each FormulaTree
2806 double nOldVal, nNewVal;
2807 nOldVal = pCell->GetValue();
2808 #endif
2809 pCell->Interpret();
2810 #if DEBUG_SC_CHECK_FORMULATREE_CALCULATION
2811 if (pCell->GetCode()->IsRecalcModeNormal())
2812 nNewVal = pCell->GetValue();
2813 else
2814 nNewVal = nOldVal; // random(), jetzt() etc.
2816 assert(nOldVal == nNewVal);
2817 #endif
2819 #undef DEBUG_SC_CHECK_FORMULATREE_CALCULATION
2822 class CompileAllHandler
2824 sc::CompileFormulaContext& mrCxt;
2825 public:
2826 explicit CompileAllHandler( sc::CompileFormulaContext& rCxt ) : mrCxt(rCxt) {}
2828 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2830 // for unconditional compilation
2831 // bCompile=true and pCode->nError=0
2832 pCell->GetCode()->SetCodeError(FormulaError::NONE);
2833 pCell->SetCompile(true);
2834 pCell->CompileTokenArray(mrCxt);
2838 class CompileXMLHandler
2840 sc::CompileFormulaContext& mrCxt;
2841 ScProgress& mrProgress;
2842 const ScColumn& mrCol;
2843 public:
2844 CompileXMLHandler( sc::CompileFormulaContext& rCxt, ScProgress& rProgress, const ScColumn& rCol) :
2845 mrCxt(rCxt),
2846 mrProgress(rProgress),
2847 mrCol(rCol) {}
2849 void operator() (size_t nRow, ScFormulaCell* pCell)
2851 sal_uInt32 nFormat = mrCol.GetNumberFormat(mrCol.GetDoc().GetNonThreadedContext(), nRow);
2852 if( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
2853 // Non-default number format is set.
2854 pCell->SetNeedNumberFormat(false);
2855 else if (pCell->NeedsNumberFormat())
2856 pCell->SetDirtyVar();
2858 if (pCell->GetMatrixFlag() != ScMatrixMode::NONE)
2859 pCell->SetDirtyVar();
2861 pCell->CompileXML(mrCxt, mrProgress);
2865 class CompileErrorCellsHandler
2867 sc::CompileFormulaContext& mrCxt;
2868 ScColumn& mrColumn;
2869 sc::CellStoreType::iterator miPos;
2870 FormulaError mnErrCode;
2871 bool mbCompiled;
2872 public:
2873 CompileErrorCellsHandler( sc::CompileFormulaContext& rCxt, ScColumn& rColumn, FormulaError nErrCode ) :
2874 mrCxt(rCxt),
2875 mrColumn(rColumn),
2876 miPos(mrColumn.GetCellStore().begin()),
2877 mnErrCode(nErrCode),
2878 mbCompiled(false)
2882 void operator() (size_t nRow, ScFormulaCell* pCell)
2884 FormulaError nCurError = pCell->GetRawError();
2885 if (nCurError == FormulaError::NONE)
2886 // It's not an error cell. Skip it.
2887 return;
2889 if (mnErrCode != FormulaError::NONE && nCurError != mnErrCode)
2890 // Error code is specified, and it doesn't match. Skip it.
2891 return;
2893 sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
2894 miPos = aPos.first;
2895 sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
2896 pCell->GetCode()->SetCodeError(FormulaError::NONE);
2897 OUString aFormula = pCell->GetFormula(mrCxt);
2898 pCell->Compile(mrCxt, aFormula);
2899 ScColumn::JoinNewFormulaCell(aPos, *pCell);
2901 mbCompiled = true;
2904 bool isCompiled() const { return mbCompiled; }
2907 class CalcAfterLoadHandler
2909 sc::CompileFormulaContext& mrCxt;
2910 bool mbStartListening;
2912 public:
2913 CalcAfterLoadHandler( sc::CompileFormulaContext& rCxt, bool bStartListening ) :
2914 mrCxt(rCxt), mbStartListening(bStartListening) {}
2916 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2918 pCell->CalcAfterLoad(mrCxt, mbStartListening);
2922 struct ResetChangedHandler
2924 void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
2926 pCell->SetChanged(false);
2931 * Ambiguous script type counts as edit cell.
2933 class FindEditCellsHandler
2935 ScColumn& mrColumn;
2936 sc::CellTextAttrStoreType::iterator miAttrPos;
2937 sc::CellStoreType::iterator miCellPos;
2939 public:
2940 explicit FindEditCellsHandler(ScColumn& rCol) :
2941 mrColumn(rCol),
2942 miAttrPos(rCol.GetCellAttrStore().begin()),
2943 miCellPos(rCol.GetCellStore().begin()) {}
2945 bool operator() (size_t, const EditTextObject*)
2947 // This is definitely an edit text cell.
2948 return true;
2951 bool operator() (size_t nRow, const ScFormulaCell* p)
2953 // With a formula cell, it's considered an edit text cell when either
2954 // the result is multi-line or it has more than one script types.
2955 SvtScriptType nScriptType = mrColumn.GetRangeScriptType(miAttrPos, nRow, nRow, miCellPos);
2956 if (IsAmbiguousScriptNonZero(nScriptType))
2957 return true;
2959 return const_cast<ScFormulaCell*>(p)->IsMultilineResult();
2963 * Callback for a block of other types.
2965 std::pair<size_t,bool> operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
2967 typedef std::pair<size_t,bool> RetType;
2969 if (node.type == sc::element_type_empty)
2970 // Ignore empty blocks.
2971 return RetType(0, false);
2973 // Check the script type of a non-empty element and see if it has
2974 // multiple script types.
2975 for (size_t i = 0; i < nDataSize; ++i)
2977 SCROW nRow = node.position + i + nOffset;
2978 SvtScriptType nScriptType = mrColumn.GetRangeScriptType(miAttrPos, nRow, nRow, miCellPos);
2979 if (IsAmbiguousScriptNonZero(nScriptType))
2980 // Return the offset from the first row.
2981 return RetType(i+nOffset, true);
2984 // No edit text cell found.
2985 return RetType(0, false);
2991 void ScColumn::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
2992 ScDocument* pUndoDoc )
2994 UpdateTransHandler aFunc(*this, rSource, rDest, pUndoDoc);
2995 sc::ProcessFormula(maCells, aFunc);
2998 void ScColumn::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
3000 UpdateGrowHandler aFunc(*this, rArea, nGrowX, nGrowY);
3001 sc::ProcessFormula(maCells, aFunc);
3004 void ScColumn::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
3006 if (nTab >= rCxt.mnInsertPos)
3008 nTab += rCxt.mnSheets;
3009 pAttrArray->SetTab(nTab);
3012 UpdateInsertTabOnlyCells(rCxt);
3015 void ScColumn::UpdateInsertTabOnlyCells( sc::RefUpdateInsertTabContext& rCxt )
3017 InsertTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
3018 sc::ProcessFormulaEditText(maCells, aFunc);
3019 if (aFunc.isModified())
3020 CellStorageModified();
3023 void ScColumn::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
3025 if (nTab > rCxt.mnDeletePos)
3027 nTab -= rCxt.mnSheets;
3028 pAttrArray->SetTab(nTab);
3031 DeleteTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
3032 sc::ProcessFormulaEditText(maCells, aFunc);
3033 if (aFunc.isModified())
3034 CellStorageModified();
3037 void ScColumn::UpdateInsertTabAbs(SCTAB nNewPos)
3039 InsertAbsTabUpdater aFunc(maCellTextAttrs, nTab, nNewPos);
3040 sc::ProcessFormulaEditText(maCells, aFunc);
3041 if (aFunc.isModified())
3042 CellStorageModified();
3045 void ScColumn::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo )
3047 nTab = nTabNo;
3048 pAttrArray->SetTab( nTabNo );
3050 MoveTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
3051 sc::ProcessFormulaEditText(maCells, aFunc);
3052 if (aFunc.isModified())
3053 CellStorageModified();
3056 void ScColumn::UpdateCompile( bool bForceIfNameInUse )
3058 UpdateCompileHandler aFunc(bForceIfNameInUse);
3059 sc::ProcessFormula(maCells, aFunc);
3062 void ScColumn::SetTabNo(SCTAB nNewTab)
3064 nTab = nNewTab;
3065 pAttrArray->SetTab( nNewTab );
3067 TabNoSetter aFunc(nTab);
3068 sc::ProcessFormula(maCells, aFunc);
3071 void ScColumn::FindRangeNamesInUse(SCROW nRow1, SCROW nRow2, sc::UpdatedRangeNames& rIndexes) const
3073 UsedRangeNameFinder aFunc(rIndexes);
3074 sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
3077 void ScColumn::SetDirtyVar()
3079 SetDirtyVarHandler aFunc;
3080 sc::ProcessFormula(maCells, aFunc);
3083 bool ScColumn::IsFormulaDirty( SCROW nRow ) const
3085 if (!GetDoc().ValidRow(nRow))
3086 return false;
3088 std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
3089 sc::CellStoreType::const_iterator it = aPos.first;
3090 if (it->type != sc::element_type_formula)
3091 // This is not a formula cell block.
3092 return false;
3094 const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
3095 return p->GetDirty();
3098 void ScColumn::CheckVectorizationState()
3100 sc::AutoCalcSwitch aSwitch(GetDoc(), false);
3101 CheckVectorizationHandler aFunc;
3102 sc::ProcessFormula(maCells, aFunc);
3105 void ScColumn::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
3107 // is only done documentwide, no FormulaTracking
3108 sc::AutoCalcSwitch aSwitch(GetDoc(), false);
3109 SetDirtyHandler aFunc(GetDoc(), rCxt);
3110 sc::ProcessFormula(maCells, aFunc);
3113 void ScColumn::SetDirtyFromClip( SCROW nRow1, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
3115 // Set all formula cells in the range dirty, and pick up all non-formula
3116 // cells for later broadcasting. We don't broadcast here.
3117 sc::AutoCalcSwitch aSwitch(GetDoc(), false);
3119 SetDirtyOnRangeHandler aHdl(*this);
3120 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
3121 aHdl.fillBroadcastSpans(rBroadcastSpans);
3124 namespace {
3126 class BroadcastBroadcastersHandler
3128 ScHint maHint;
3129 bool mbBroadcasted;
3131 public:
3132 explicit BroadcastBroadcastersHandler( SfxHintId nHint, SCTAB nTab, SCCOL nCol )
3133 : maHint(nHint, ScAddress(nCol, 0, nTab))
3134 , mbBroadcasted(false)
3138 void operator() ( size_t nRow, SvtBroadcaster* pBroadcaster )
3140 maHint.SetAddressRow(nRow);
3141 pBroadcaster->Broadcast(maHint);
3142 mbBroadcasted = true;
3145 bool wasBroadcasted() { return mbBroadcasted; }
3150 bool ScColumn::BroadcastBroadcasters( SCROW nRow1, SCROW nRow2, SfxHintId nHint )
3152 BroadcastBroadcastersHandler aBroadcasterHdl(nHint, nTab, nCol);
3153 sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aBroadcasterHdl);
3154 return aBroadcasterHdl.wasBroadcasted();
3157 void ScColumn::SetDirty( SCROW nRow1, SCROW nRow2, BroadcastMode eMode )
3159 // broadcasts everything within the range, with FormulaTracking
3160 sc::AutoCalcSwitch aSwitch(GetDoc(), false);
3162 switch (eMode)
3164 case BROADCAST_NONE:
3166 // Handler only used with formula cells.
3167 SetDirtyOnRangeHandler aHdl(*this);
3168 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl);
3170 break;
3171 case BROADCAST_DATA_POSITIONS:
3173 // Handler used with both, formula and non-formula cells.
3174 SetDirtyOnRangeHandler aHdl(*this);
3175 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
3176 aHdl.broadcast();
3178 break;
3179 case BROADCAST_BROADCASTERS:
3181 // Handler only used with formula cells.
3182 SetDirtyOnRangeHandler aHdl(*this);
3183 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl);
3184 // Broadcast all broadcasters in range.
3185 if (BroadcastBroadcasters( nRow1, nRow2, SfxHintId::ScDataChanged))
3187 // SetDirtyOnRangeHandler implicitly tracks notified
3188 // formulas via ScDocument::Broadcast(), which
3189 // BroadcastBroadcastersHandler doesn't, so explicitly
3190 // track them here.
3191 GetDoc().TrackFormulas();
3194 break;
3198 void ScColumn::SetTableOpDirty( const ScRange& rRange )
3200 sc::AutoCalcSwitch aSwitch(GetDoc(), false);
3202 SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
3203 SetTableOpDirtyOnRangeHandler aHdl(*this);
3204 sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
3205 aHdl.broadcast();
3208 void ScColumn::SetDirtyAfterLoad()
3210 sc::AutoCalcSwitch aSwitch(GetDoc(), false);
3211 SetDirtyAfterLoadHandler aFunc;
3212 sc::ProcessFormula(maCells, aFunc);
3215 namespace {
3217 class RecalcOnRefMoveCollector
3219 std::vector<SCROW> maDirtyRows;
3220 public:
3221 void operator() (size_t nRow, ScFormulaCell* pCell)
3223 if (pCell->GetDirty() && pCell->GetCode()->IsRecalcModeOnRefMove())
3224 maDirtyRows.push_back(nRow);
3227 const std::vector<SCROW>& getDirtyRows() const
3229 return maDirtyRows;
3235 void ScColumn::SetDirtyIfPostponed()
3237 sc::AutoCalcSwitch aSwitch(GetDoc(), false);
3238 SetDirtyIfPostponedHandler aFunc;
3239 ScBulkBroadcast aBulkBroadcast( GetDoc().GetBASM(), SfxHintId::ScDataChanged);
3240 sc::ProcessFormula(maCells, aFunc);
3243 void ScColumn::BroadcastRecalcOnRefMove()
3245 sc::AutoCalcSwitch aSwitch(GetDoc(), false);
3246 RecalcOnRefMoveCollector aFunc;
3247 sc::ProcessFormula(maCells, aFunc);
3248 BroadcastCells(aFunc.getDirtyRows(), SfxHintId::ScDataChanged);
3251 void ScColumn::CalcAll()
3253 CalcAllHandler aFunc;
3254 sc::ProcessFormula(maCells, aFunc);
3257 void ScColumn::CompileAll( sc::CompileFormulaContext& rCxt )
3259 CompileAllHandler aFunc(rCxt);
3260 sc::ProcessFormula(maCells, aFunc);
3263 void ScColumn::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
3265 CompileXMLHandler aFunc(rCxt, rProgress, *this);
3266 sc::ProcessFormula(maCells, aFunc);
3267 RegroupFormulaCells();
3270 bool ScColumn::CompileErrorCells( sc::CompileFormulaContext& rCxt, FormulaError nErrCode )
3272 CompileErrorCellsHandler aHdl(rCxt, *this, nErrCode);
3273 sc::ProcessFormula(maCells, aHdl);
3274 return aHdl.isCompiled();
3277 void ScColumn::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
3279 CalcAfterLoadHandler aFunc(rCxt, bStartListening);
3280 sc::ProcessFormula(maCells, aFunc);
3283 void ScColumn::ResetChanged( SCROW nStartRow, SCROW nEndRow )
3285 ResetChangedHandler aFunc;
3286 sc::ProcessFormula(maCells.begin(), maCells, nStartRow, nEndRow, aFunc);
3289 bool ScColumn::HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW& rFirst)
3291 // used in GetOptimalHeight - ambiguous script type counts as edit cell
3293 FindEditCellsHandler aFunc(*this);
3294 std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
3295 sc::FindFormulaEditText(maCells, nStartRow, nEndRow, aFunc);
3297 if (aPos.first == maCells.end())
3298 return false;
3300 rFirst = aPos.first->position + aPos.second;
3301 return true;
3304 SCROW ScColumn::SearchStyle(
3305 SCROW nRow, const ScStyleSheet* pSearchStyle, bool bUp, bool bInSelection,
3306 const ScMarkData& rMark) const
3308 if (bInSelection)
3310 if (rMark.IsMultiMarked())
3312 ScMarkArray aArray(rMark.GetMarkArray(nCol));
3313 return pAttrArray->SearchStyle(nRow, pSearchStyle, bUp, &aArray);
3315 else
3316 return -1;
3318 else
3319 return pAttrArray->SearchStyle( nRow, pSearchStyle, bUp );
3322 bool ScColumn::SearchStyleRange(
3323 SCROW& rRow, SCROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
3324 bool bInSelection, const ScMarkData& rMark) const
3326 if (bInSelection)
3328 if (rMark.IsMultiMarked())
3330 ScMarkArray aArray(rMark.GetMarkArray(nCol));
3331 return pAttrArray->SearchStyleRange(
3332 rRow, rEndRow, pSearchStyle, bUp, &aArray);
3334 else
3335 return false;
3337 else
3338 return pAttrArray->SearchStyleRange( rRow, rEndRow, pSearchStyle, bUp );
3341 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */