summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/column.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/column.cxx')
-rw-r--r--sc/source/core/data/column.cxx3391
1 files changed, 3391 insertions, 0 deletions
diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx
new file mode 100644
index 000000000..06bbe0cf3
--- /dev/null
+++ b/sc/source/core/data/column.cxx
@@ -0,0 +1,3391 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <column.hxx>
+#include <scitems.hxx>
+#include <formulacell.hxx>
+#include <document.hxx>
+#include <table.hxx>
+#include <docpool.hxx>
+#include <attarray.hxx>
+#include <patattr.hxx>
+#include <compiler.hxx>
+#include <brdcst.hxx>
+#include <markdata.hxx>
+#include <postit.hxx>
+#include <cellvalue.hxx>
+#include <tokenarray.hxx>
+#include <clipcontext.hxx>
+#include <types.hxx>
+#include <editutil.hxx>
+#include <mtvcellfunc.hxx>
+#include <columnspanset.hxx>
+#include <scopetools.hxx>
+#include <sharedformula.hxx>
+#include <refupdatecontext.hxx>
+#include <listenercontext.hxx>
+#include <formulagroup.hxx>
+#include <drwlayer.hxx>
+#include <mtvelements.hxx>
+#include <bcaslot.hxx>
+
+#include <svl/numformat.hxx>
+#include <svl/poolcach.hxx>
+#include <svl/zforlist.hxx>
+#include <svl/sharedstringpool.hxx>
+#include <editeng/fieldupdater.hxx>
+#include <formula/errorcodes.hxx>
+#include <o3tl/safeint.hxx>
+#include <osl/diagnose.h>
+
+#include <map>
+#include <cstdio>
+#include <memory>
+
+using ::editeng::SvxBorderLine;
+using namespace formula;
+
+namespace {
+
+bool IsAmbiguousScriptNonZero( SvtScriptType nScript )
+{
+ //TODO: move to a header file
+ return ( nScript != SvtScriptType::LATIN &&
+ nScript != SvtScriptType::ASIAN &&
+ nScript != SvtScriptType::COMPLEX &&
+ nScript != SvtScriptType::NONE );
+}
+
+}
+
+ScNeededSizeOptions::ScNeededSizeOptions() :
+ pPattern(nullptr), bFormula(false), bSkipMerged(true), bGetFont(true), bTotalSize(false)
+{
+}
+
+ScColumn::ScColumn(ScSheetLimits const & rSheetLimits) :
+ maCellTextAttrs(rSheetLimits.GetMaxRowCount()),
+ maCellNotes(rSheetLimits.GetMaxRowCount()),
+ maBroadcasters(rSheetLimits.GetMaxRowCount()),
+ maCells(sc::CellStoreEvent(this)),
+ maSparklines(rSheetLimits.GetMaxRowCount()),
+ mnBlkCountFormula(0),
+ nCol( 0 ),
+ nTab( 0 ),
+ mbFiltering( false ),
+ mbEmptyBroadcastersPending( false )
+{
+ maCells.resize(rSheetLimits.GetMaxRowCount());
+}
+
+ScColumn::~ScColumn() COVERITY_NOEXCEPT_FALSE
+{
+ FreeAll();
+}
+
+void ScColumn::Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, bool bEmptyAttrArray)
+{
+ nCol = nNewCol;
+ nTab = nNewTab;
+ if ( bEmptyAttrArray )
+ InitAttrArray(new ScAttrArray( nCol, nTab, rDoc, nullptr ));
+ else
+ InitAttrArray(new ScAttrArray( nCol, nTab, rDoc, &rDoc.maTabs[nTab]->aDefaultColData.AttrArray()));
+}
+
+sc::MatrixEdge ScColumn::GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, sc::MatrixEdge nMask,
+ bool bNoMatrixAtAll ) const
+{
+ using namespace sc;
+
+ if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
+ return MatrixEdge::Nothing;
+
+ ScAddress aOrigin(ScAddress::INITIALIZE_INVALID);
+
+ if (nRow1 == nRow2)
+ {
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
+ if (aPos.first->type != sc::element_type_formula)
+ return MatrixEdge::Nothing;
+
+ const ScFormulaCell* pCell = sc::formula_block::at(*aPos.first->data, aPos.second);
+ if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
+ return MatrixEdge::Nothing;
+
+ return pCell->GetMatrixEdge(GetDoc(), aOrigin);
+ }
+
+ bool bOpen = false;
+ MatrixEdge nEdges = MatrixEdge::Nothing;
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+ SCROW nRow = nRow1;
+ for (;it != maCells.end() && nRow <= nRow2; ++it, nOffset = 0)
+ {
+ if (it->type != sc::element_type_formula)
+ {
+ // Skip this block.
+ nRow += it->size - nOffset;
+ continue;
+ }
+
+ size_t nRowsToRead = nRow2 - nRow + 1;
+ size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1
+ sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, nOffset);
+ for (size_t i = nOffset; i < nEnd; ++itCell, ++i)
+ {
+ // Loop inside the formula block.
+ const ScFormulaCell* pCell = *itCell;
+ if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
+ continue;
+
+ nEdges = pCell->GetMatrixEdge(GetDoc(), aOrigin);
+ if (nEdges == MatrixEdge::Nothing)
+ continue;
+
+ // A 1x1 matrix array formula is OK even for no matrix at all.
+ if (bNoMatrixAtAll
+ && (nEdges != (MatrixEdge::Top | MatrixEdge::Left | MatrixEdge::Bottom | MatrixEdge::Right)))
+ return MatrixEdge::Inside; // per convention Inside
+
+ if (nEdges & MatrixEdge::Top)
+ bOpen = true; // top edge opens, keep on looking
+ else if (!bOpen)
+ return nEdges | MatrixEdge::Open; // there's something that wasn't opened
+ else if (nEdges & MatrixEdge::Inside)
+ return nEdges; // inside
+ if (((nMask & MatrixEdge::Right) && (nEdges & MatrixEdge::Left) && !(nEdges & MatrixEdge::Right)) ||
+ ((nMask & MatrixEdge::Left) && (nEdges & MatrixEdge::Right) && !(nEdges & MatrixEdge::Left)))
+ return nEdges; // only left/right edge
+
+ if (nEdges & MatrixEdge::Bottom)
+ bOpen = false; // bottom edge closes
+ }
+
+ nRow += nEnd - nOffset;
+ }
+ if (bOpen)
+ nEdges |= MatrixEdge::Open; // not closed, matrix continues
+
+ return nEdges;
+}
+
+bool ScColumn::HasSelectionMatrixFragment(const ScMarkData& rMark, const ScRangeList& rRangeList) const
+{
+ using namespace sc;
+
+ if (!rMark.IsMultiMarked())
+ return false;
+
+ ScAddress aOrigin(ScAddress::INITIALIZE_INVALID);
+ ScAddress aCurOrigin = aOrigin;
+
+ bool bOpen = false;
+ ScRangeList aRanges = rRangeList; // cached rMark.GetMarkedRanges(), for performance reasons (tdf#148147)
+ for (size_t i = 0, n = aRanges.size(); i < n; ++i)
+ {
+ const ScRange& r = aRanges[i];
+ if (nTab < r.aStart.Tab() || r.aEnd.Tab() < nTab)
+ continue;
+
+ if (nCol < r.aStart.Col() || r.aEnd.Col() < nCol)
+ continue;
+
+ SCROW nTop = r.aStart.Row(), nBottom = r.aEnd.Row();
+ SCROW nRow = nTop;
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+
+ for (;it != maCells.end() && nRow <= nBottom; ++it, nOffset = 0)
+ {
+ if (it->type != sc::element_type_formula)
+ {
+ // Skip this block.
+ nRow += it->size - nOffset;
+ continue;
+ }
+
+ // This is a formula cell block.
+ size_t nRowsToRead = nBottom - nRow + 1;
+ size_t nEnd = std::min(it->size, nRowsToRead);
+ sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
+ std::advance(itCell, nOffset);
+ for (size_t j = nOffset; j < nEnd; ++itCell, ++j)
+ {
+ // Loop inside the formula block.
+ const ScFormulaCell* pCell = *itCell;
+ if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
+ // cell is not a part of a matrix.
+ continue;
+
+ MatrixEdge nEdges = pCell->GetMatrixEdge(GetDoc(), aOrigin);
+ if (nEdges == MatrixEdge::Nothing)
+ continue;
+
+ bool bFound = false;
+
+ if (nEdges & MatrixEdge::Top)
+ bOpen = true; // top edge opens, keep on looking
+ else if (!bOpen)
+ return true; // there's something that wasn't opened
+ else if (nEdges & MatrixEdge::Inside)
+ bFound = true; // inside, all selected?
+
+ if (((nEdges & MatrixEdge::Left) | MatrixEdge::Right) ^ ((nEdges & MatrixEdge::Right) | MatrixEdge::Left))
+ // either left or right, but not both.
+ bFound = true; // only left/right edge, all selected?
+
+ if (nEdges & MatrixEdge::Bottom)
+ bOpen = false; // bottom edge closes
+
+ if (bFound)
+ {
+ // Check if the matrix is inside the selection in its entirety.
+ //
+ // TODO: It's more efficient to skip the matrix range if
+ // it's within selection, to avoid checking it again and
+ // again.
+
+ if (aCurOrigin != aOrigin)
+ { // new matrix to check?
+ aCurOrigin = aOrigin;
+ const ScFormulaCell* pFCell;
+ if (pCell->GetMatrixFlag() == ScMatrixMode::Reference)
+ pFCell = GetDoc().GetFormulaCell(aOrigin);
+ else
+ pFCell = pCell;
+
+ SCCOL nC;
+ SCROW nR;
+ pFCell->GetMatColsRows(nC, nR);
+ ScRange aRange(aOrigin, ScAddress(aOrigin.Col()+nC-1, aOrigin.Row()+nR-1, aOrigin.Tab()));
+ if (rMark.IsAllMarked(aRange))
+ bFound = false;
+ }
+ else
+ bFound = false; // done already
+ }
+
+ if (bFound)
+ return true;
+ }
+
+ nRow += nEnd;
+ }
+ }
+
+ return bOpen;
+}
+
+bool ScColumn::HasAttribSelection( const ScMarkData& rMark, HasAttrFlags nMask ) const
+{
+ bool bFound = false;
+
+ SCROW nTop;
+ SCROW nBottom;
+
+ if (rMark.IsMultiMarked())
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ while (aMultiIter.Next( nTop, nBottom ) && !bFound)
+ {
+ if (pAttrArray->HasAttrib( nTop, nBottom, nMask ))
+ bFound = true;
+ }
+ }
+
+ return bFound;
+}
+
+void ScColumn::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, bool bDeep ) const
+{
+ SCROW nTop;
+ SCROW nBottom;
+
+ if ( rMark.IsMultiMarked() )
+ {
+ const ScMultiSel& rMultiSel = rMark.GetMultiSelData();
+ if ( rMultiSel.HasMarks( nCol ) )
+ {
+ ScMultiSelIter aMultiIter( rMultiSel, nCol );
+ while (aMultiIter.Next( nTop, nBottom ))
+ pAttrArray->MergePatternArea( nTop, nBottom, rState, bDeep );
+ }
+ }
+}
+
+const ScPatternAttr* ScColumnData::GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow ) const
+{
+ ::std::map< const ScPatternAttr*, size_t > aAttrMap;
+ const ScPatternAttr* pMaxPattern = nullptr;
+ size_t nMaxCount = 0;
+
+ ScAttrIterator aAttrIter( pAttrArray.get(), nStartRow, nEndRow, GetDoc().GetDefPattern() );
+ const ScPatternAttr* pPattern;
+ SCROW nAttrRow1 = 0, nAttrRow2 = 0;
+
+ while( (pPattern = aAttrIter.Next( nAttrRow1, nAttrRow2 )) != nullptr )
+ {
+ size_t& rnCount = aAttrMap[ pPattern ];
+ rnCount += (nAttrRow2 - nAttrRow1 + 1);
+ if( rnCount > nMaxCount )
+ {
+ pMaxPattern = pPattern;
+ nMaxCount = rnCount;
+ }
+ }
+
+ return pMaxPattern;
+}
+
+sal_uInt32 ScColumnData::GetNumberFormat( SCROW nStartRow, SCROW nEndRow ) const
+{
+ SCROW nPatStartRow, nPatEndRow;
+ const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nPatStartRow, nPatEndRow, nStartRow);
+ sal_uInt32 nFormat = pPattern->GetNumberFormat(GetDoc().GetFormatTable());
+ while (nEndRow > nPatEndRow)
+ {
+ nStartRow = nPatEndRow + 1;
+ pPattern = pAttrArray->GetPatternRange(nPatStartRow, nPatEndRow, nStartRow);
+ sal_uInt32 nTmpFormat = pPattern->GetNumberFormat(GetDoc().GetFormatTable());
+ if (nFormat != nTmpFormat)
+ return 0;
+ }
+ return nFormat;
+}
+
+SCROW ScColumn::ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark, ScEditDataArray* pDataArray,
+ bool* const pIsChanged )
+{
+ return ScColumnData::ApplySelectionCache( pCache, rMark, pDataArray, pIsChanged, nCol );
+}
+
+SCROW ScColumnData::ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark, ScEditDataArray* pDataArray,
+ bool* const pIsChanged, SCCOL nCol )
+{
+ SCROW nTop = 0;
+ SCROW nBottom = 0;
+ bool bFound = false;
+
+ if ( rMark.IsMultiMarked() )
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ while (aMultiIter.Next( nTop, nBottom ))
+ {
+ pAttrArray->ApplyCacheArea( nTop, nBottom, pCache, pDataArray, pIsChanged );
+ bFound = true;
+ }
+ }
+
+ if (!bFound)
+ return -1;
+ else if (nTop==0 && nBottom==GetDoc().MaxRow())
+ return 0;
+ else
+ return nBottom;
+}
+
+void ScColumnData::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark, SCCOL nCol )
+{
+ assert(rMark.IsMultiMarked());
+ if ( pAttrArray && rMark.IsMultiMarked() )
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ SCROW nTop;
+ SCROW nBottom;
+ while (aMultiIter.Next( nTop, nBottom ))
+ pAttrArray->ChangeIndent(nTop, nBottom, bIncrement);
+ }
+}
+
+void ScColumn::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark )
+{
+ return ScColumnData::ChangeSelectionIndent( bIncrement, rMark, nCol );
+}
+
+void ScColumnData::ClearSelectionItems( const sal_uInt16* pWhich,const ScMarkData& rMark, SCCOL nCol )
+{
+ if (!pAttrArray)
+ return;
+
+ if (rMark.IsMultiMarked() )
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ SCROW nTop;
+ SCROW nBottom;
+ while (aMultiIter.Next( nTop, nBottom ))
+ pAttrArray->ClearItems(nTop, nBottom, pWhich);
+ }
+ else if (rMark.IsMarked())
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ if (aRange.aStart.Col() <= nCol && nCol <= aRange.aEnd.Col())
+ {
+ pAttrArray->ClearItems(aRange.aStart.Row(), aRange.aEnd.Row(), pWhich);
+ }
+ }
+}
+
+void ScColumn::ClearSelectionItems( const sal_uInt16* pWhich,const ScMarkData& rMark )
+{
+ ScColumnData::ClearSelectionItems( pWhich, rMark, nCol );
+}
+
+void ScColumn::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
+{
+ SCROW nTop;
+ SCROW nBottom;
+
+ if ( rMark.IsMultiMarked() )
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ while (aMultiIter.Next( nTop, nBottom ))
+ DeleteArea(nTop, nBottom, nDelFlag, bBroadcast);
+ }
+}
+
+void ScColumn::ApplyPattern( SCROW nRow, const ScPatternAttr& rPatAttr )
+{
+ const SfxItemSet* pSet = &rPatAttr.GetItemSet();
+ SfxItemPoolCache aCache( GetDoc().GetPool(), pSet );
+
+ const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow );
+
+ // true = keep old content
+
+ const ScPatternAttr* pNewPattern = static_cast<const ScPatternAttr*>( &aCache.ApplyTo( *pPattern ) );
+
+ if (pNewPattern != pPattern)
+ pAttrArray->SetPattern( nRow, pNewPattern );
+}
+
+void ScColumnData::ApplyPatternArea( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr& rPatAttr,
+ ScEditDataArray* pDataArray, bool* const pIsChanged )
+{
+ const SfxItemSet* pSet = &rPatAttr.GetItemSet();
+ SfxItemPoolCache aCache( GetDoc().GetPool(), pSet );
+ pAttrArray->ApplyCacheArea( nStartRow, nEndRow, &aCache, pDataArray, pIsChanged );
+}
+
+void ScColumn::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
+ const ScPatternAttr& rPattern, SvNumFormatType nNewType )
+{
+ const SfxItemSet* pSet = &rPattern.GetItemSet();
+ SfxItemPoolCache aCache( GetDoc().GetPool(), pSet );
+ SvNumberFormatter* pFormatter = GetDoc().GetFormatTable();
+ SCROW nEndRow = rRange.aEnd.Row();
+ for ( SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; nRow++ )
+ {
+ SCROW nRow1, nRow2;
+ const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(
+ nRow1, nRow2, nRow );
+ sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
+ SvNumFormatType nOldType = pFormatter->GetType( nFormat );
+ if ( nOldType == nNewType || SvNumberFormatter::IsCompatible( nOldType, nNewType ) )
+ nRow = nRow2;
+ else
+ {
+ SCROW nNewRow1 = std::max( nRow1, nRow );
+ SCROW nNewRow2 = std::min( nRow2, nEndRow );
+ pAttrArray->ApplyCacheArea( nNewRow1, nNewRow2, &aCache );
+ nRow = nNewRow2;
+ }
+ }
+}
+
+void ScColumn::ApplyStyle( SCROW nRow, const ScStyleSheet* rStyle )
+{
+ const ScPatternAttr* pPattern = pAttrArray->GetPattern(nRow);
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr(*pPattern));
+ pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(rStyle));
+ pAttrArray->SetPattern(nRow, std::move(pNewPattern), true);
+}
+
+void ScColumn::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark)
+{
+ SCROW nTop;
+ SCROW nBottom;
+
+ if ( rMark.IsMultiMarked() )
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ while (aMultiIter.Next( nTop, nBottom ))
+ pAttrArray->ApplyStyleArea(nTop, nBottom, rStyle);
+ }
+}
+
+void ScColumn::ApplySelectionLineStyle( const ScMarkData& rMark,
+ const SvxBorderLine* pLine, bool bColorOnly )
+{
+ if ( bColorOnly && !pLine )
+ return;
+
+ SCROW nTop;
+ SCROW nBottom;
+
+ if (rMark.IsMultiMarked())
+ {
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ while (aMultiIter.Next( nTop, nBottom ))
+ pAttrArray->ApplyLineStyleArea(nTop, nBottom, pLine, bColorOnly );
+ }
+}
+
+const ScStyleSheet* ScColumn::GetSelectionStyle( const ScMarkData& rMark, bool& rFound ) const
+{
+ rFound = false;
+ if (!rMark.IsMultiMarked())
+ {
+ OSL_FAIL("No selection in ScColumn::GetSelectionStyle");
+ return nullptr;
+ }
+
+ bool bEqual = true;
+
+ const ScStyleSheet* pStyle = nullptr;
+ const ScStyleSheet* pNewStyle;
+
+ ScDocument& rDocument = GetDoc();
+ ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
+ SCROW nTop;
+ SCROW nBottom;
+ while (bEqual && aMultiIter.Next( nTop, nBottom ))
+ {
+ ScAttrIterator aAttrIter( pAttrArray.get(), nTop, nBottom, rDocument.GetDefPattern() );
+ SCROW nRow;
+ SCROW nDummy;
+ while (bEqual)
+ {
+ const ScPatternAttr* pPattern = aAttrIter.Next( nRow, nDummy );
+ if (!pPattern)
+ break;
+ pNewStyle = pPattern->GetStyleSheet();
+ rFound = true;
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false; // difference
+ pStyle = pNewStyle;
+ }
+ }
+
+ return bEqual ? pStyle : nullptr;
+}
+
+const ScStyleSheet* ScColumn::GetAreaStyle( bool& rFound, SCROW nRow1, SCROW nRow2 ) const
+{
+ rFound = false;
+
+ bool bEqual = true;
+
+ const ScStyleSheet* pStyle = nullptr;
+ const ScStyleSheet* pNewStyle;
+
+ ScAttrIterator aAttrIter( pAttrArray.get(), nRow1, nRow2, GetDoc().GetDefPattern() );
+ SCROW nRow;
+ SCROW nDummy;
+ while (bEqual)
+ {
+ const ScPatternAttr* pPattern = aAttrIter.Next( nRow, nDummy );
+ if (!pPattern)
+ break;
+ pNewStyle = pPattern->GetStyleSheet();
+ rFound = true;
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false; // difference
+ pStyle = pNewStyle;
+ }
+
+ return bEqual ? pStyle : nullptr;
+}
+
+void ScColumn::ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr )
+{
+ // in order to only create a new SetItem, we don't need SfxItemPoolCache.
+ //TODO: Warning: SfxItemPoolCache seems to create too many Refs for the new SetItem ??
+
+ ScDocumentPool* pDocPool = GetDoc().GetPool();
+
+ const ScPatternAttr* pOldPattern = pAttrArray->GetPattern( nRow );
+ ScPatternAttr aTemp(*pOldPattern);
+ aTemp.GetItemSet().Put(rAttr);
+ const ScPatternAttr* pNewPattern = &pDocPool->Put( aTemp );
+
+ if ( pNewPattern != pOldPattern )
+ pAttrArray->SetPattern( nRow, pNewPattern );
+ else
+ pDocPool->Remove( *pNewPattern ); // free up resources
+}
+
+ScRefCellValue ScColumn::GetCellValue( SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ if (aPos.first == maCells.end())
+ return ScRefCellValue();
+
+ return GetCellValue(aPos.first, aPos.second);
+}
+
+ScRefCellValue ScColumn::GetCellValue( sc::ColumnBlockPosition& rBlockPos, SCROW nRow )
+{
+ std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
+ if (aPos.first == maCells.end())
+ return ScRefCellValue();
+
+ rBlockPos.miCellPos = aPos.first; // Store this for next call.
+ return GetCellValue(aPos.first, aPos.second);
+}
+
+ScRefCellValue ScColumn::GetCellValue( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
+{
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
+ if (aPos.first == maCells.end())
+ return ScRefCellValue();
+
+ rBlockPos.miCellPos = aPos.first; // Store this for next call.
+ return GetCellValue(aPos.first, aPos.second);
+}
+
+ScRefCellValue ScColumn::GetCellValue( const sc::CellStoreType::const_iterator& itPos, size_t nOffset )
+{
+ ScRefCellValue aVal; // Defaults to empty cell.
+ switch (itPos->type)
+ {
+ case sc::element_type_numeric:
+ // Numeric cell
+ aVal.mfValue = sc::numeric_block::at(*itPos->data, nOffset);
+ aVal.meType = CELLTYPE_VALUE;
+ break;
+ case sc::element_type_string:
+ // String cell
+ aVal.mpString = &sc::string_block::at(*itPos->data, nOffset);
+ aVal.meType = CELLTYPE_STRING;
+ break;
+ case sc::element_type_edittext:
+ // Edit cell
+ aVal.mpEditText = sc::edittext_block::at(*itPos->data, nOffset);
+ aVal.meType = CELLTYPE_EDIT;
+ break;
+ case sc::element_type_formula:
+ // Formula cell
+ aVal.mpFormula = sc::formula_block::at(*itPos->data, nOffset);
+ aVal.meType = CELLTYPE_FORMULA;
+ break;
+ default:
+ ;
+ }
+
+ return aVal;
+}
+
+const sc::CellTextAttr* ScColumn::GetCellTextAttr( SCROW nRow ) const
+{
+ sc::ColumnBlockConstPosition aBlockPos;
+ aBlockPos.miCellTextAttrPos = maCellTextAttrs.begin();
+ return GetCellTextAttr(aBlockPos, nRow);
+}
+
+const sc::CellTextAttr* ScColumn::GetCellTextAttr( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
+{
+ sc::CellTextAttrStoreType::const_position_type aPos = maCellTextAttrs.position(rBlockPos.miCellTextAttrPos, nRow);
+ if (aPos.first == maCellTextAttrs.end())
+ return nullptr;
+
+ rBlockPos.miCellTextAttrPos = aPos.first;
+
+ if (aPos.first->type != sc::element_type_celltextattr)
+ return nullptr;
+
+ return &sc::celltextattr_block::at(*aPos.first->data, aPos.second);
+}
+
+bool ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
+{
+ if (IsEmptyData() && IsEmptyAttr())
+ return true;
+
+ // Return false if we have any non-empty cells between nStartRow and nEndRow inclusive.
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it->type != sc::element_type_empty)
+ return false;
+
+ // Get the length of the remaining empty segment.
+ size_t nLen = it->size - aPos.second;
+ SCROW nNextNonEmptyRow = nStartRow + nLen;
+ if (nNextNonEmptyRow <= nEndRow)
+ return false;
+
+ // AttrArray only looks for merged cells
+
+ return pAttrArray == nullptr || pAttrArray->TestInsertCol(nStartRow, nEndRow);
+}
+
+bool ScColumn::TestInsertRow( SCROW nStartRow, SCSIZE nSize ) const
+{
+ // AttrArray only looks for merged cells
+ {
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it->type == sc::element_type_empty && maCells.block_size() == 1)
+ // The entire cell array is empty.
+ return pAttrArray->TestInsertRow(nSize);
+ }
+
+ // See if there would be any non-empty cell that gets pushed out.
+
+ // Find the position of the last non-empty cell below nStartRow.
+ size_t nLastNonEmptyRow = GetDoc().MaxRow();
+ sc::CellStoreType::const_reverse_iterator it = maCells.rbegin();
+ if (it->type == sc::element_type_empty)
+ nLastNonEmptyRow -= it->size;
+
+ if (nLastNonEmptyRow < o3tl::make_unsigned(nStartRow))
+ // No cells would get pushed out.
+ return pAttrArray->TestInsertRow(nSize);
+
+ if (nLastNonEmptyRow + nSize > o3tl::make_unsigned(GetDoc().MaxRow()))
+ // At least one cell would get pushed out. Not good.
+ return false;
+
+ return pAttrArray->TestInsertRow(nSize);
+}
+
+void ScColumn::InsertRow( SCROW nStartRow, SCSIZE nSize )
+{
+ pAttrArray->InsertRow( nStartRow, nSize );
+
+ maCellNotes.insert_empty(nStartRow, nSize);
+ maCellNotes.resize(GetDoc().GetMaxRowCount());
+
+ maSparklines.insert_empty(nStartRow, nSize);
+ maSparklines.resize(GetDoc().GetSheetLimits().GetMaxRowCount());
+
+ maBroadcasters.insert_empty(nStartRow, nSize);
+ maBroadcasters.resize(GetDoc().GetMaxRowCount());
+
+ maCellTextAttrs.insert_empty(nStartRow, nSize);
+ maCellTextAttrs.resize(GetDoc().GetMaxRowCount());
+
+ maCells.insert_empty(nStartRow, nSize);
+ maCells.resize(GetDoc().GetMaxRowCount());
+
+ CellStorageModified();
+
+ // We *probably* don't need to broadcast here since the parent call seems
+ // to take care of it.
+}
+
+namespace {
+
+class CopyToClipHandler
+{
+ const ScDocument& mrSrcDoc;
+ const ScColumn& mrSrcCol;
+ ScColumn& mrDestCol;
+ sc::ColumnBlockPosition maDestPos;
+ sc::ColumnBlockPosition* mpDestPos;
+
+ void setDefaultAttrsToDest(size_t nRow, size_t nSize)
+ {
+ std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
+ }
+
+public:
+ CopyToClipHandler(const ScDocument& rSrcDoc, const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos) :
+ mrSrcDoc(rSrcDoc), mrSrcCol(rSrcCol), mrDestCol(rDestCol), mpDestPos(pDestPos)
+ {
+ if (mpDestPos)
+ maDestPos = *mpDestPos;
+ else
+ mrDestCol.InitBlockPosition(maDestPos);
+ }
+
+ ~CopyToClipHandler()
+ {
+ if (mpDestPos)
+ *mpDestPos = maDestPos;
+ }
+
+ void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
+ {
+ size_t nTopRow = aNode.position + nOffset;
+
+ bool bSet = true;
+
+ switch (aNode.type)
+ {
+ case sc::element_type_numeric:
+ {
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::numeric_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nTopRow, it, itEnd);
+ }
+ break;
+ case sc::element_type_string:
+ {
+ sc::string_block::const_iterator it = sc::string_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::string_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nTopRow, it, itEnd);
+
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ sc::edittext_block::const_iterator it = sc::edittext_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::edittext_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ std::vector<EditTextObject*> aCloned;
+ aCloned.reserve(nDataSize);
+ for (; it != itEnd; ++it)
+ aCloned.push_back(ScEditUtil::Clone(**it, mrDestCol.GetDoc()).release());
+
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(
+ maDestPos.miCellPos, nTopRow, aCloned.begin(), aCloned.end());
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ std::vector<ScFormulaCell*> aCloned;
+ aCloned.reserve(nDataSize);
+ ScAddress aDestPos(mrDestCol.GetCol(), nTopRow, mrDestCol.GetTab());
+ for (; it != itEnd; ++it, aDestPos.IncRow())
+ {
+ const ScFormulaCell& rOld = **it;
+ if (rOld.GetDirty() && mrSrcCol.GetDoc().GetAutoCalc())
+ const_cast<ScFormulaCell&>(rOld).Interpret();
+
+ aCloned.push_back(new ScFormulaCell(rOld, mrDestCol.GetDoc(), aDestPos));
+ }
+
+ // Group the cloned formula cells.
+ if (!aCloned.empty())
+ sc::SharedFormulaUtil::groupFormulaCells(aCloned.begin(), aCloned.end());
+
+ sc::CellStoreType& rDestCells = mrDestCol.GetCellStore();
+ maDestPos.miCellPos = rDestCells.set(
+ maDestPos.miCellPos, nTopRow, aCloned.begin(), aCloned.end());
+
+ // Merge adjacent formula cell groups (if applicable).
+ sc::CellStoreType::position_type aPos =
+ rDestCells.position(maDestPos.miCellPos, nTopRow);
+ maDestPos.miCellPos = aPos.first;
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ size_t nLastRow = nTopRow + nDataSize;
+ if (nLastRow < o3tl::make_unsigned(mrSrcDoc.MaxRow()))
+ {
+ aPos = rDestCells.position(maDestPos.miCellPos, nLastRow+1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+ }
+ break;
+ default:
+ bSet = false;
+ }
+
+ if (bSet)
+ setDefaultAttrsToDest(nTopRow, nDataSize);
+
+ mrSrcCol.DuplicateNotes(nTopRow, nDataSize, mrDestCol, maDestPos, false);
+ mrSrcCol.DuplicateSparklines(nTopRow, nDataSize, mrDestCol, maDestPos);
+ }
+};
+
+class CopyTextAttrToClipHandler
+{
+ sc::CellTextAttrStoreType& mrDestAttrs;
+ sc::CellTextAttrStoreType::iterator miPos;
+
+public:
+ explicit CopyTextAttrToClipHandler( sc::CellTextAttrStoreType& rAttrs ) :
+ mrDestAttrs(rAttrs), miPos(mrDestAttrs.begin()) {}
+
+ void operator() ( const sc::CellTextAttrStoreType::value_type& aNode, size_t nOffset, size_t nDataSize )
+ {
+ if (aNode.type != sc::element_type_celltextattr)
+ return;
+
+ sc::celltextattr_block::const_iterator it = sc::celltextattr_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::celltextattr_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ size_t nPos = aNode.position + nOffset;
+ miPos = mrDestAttrs.set(miPos, nPos, it, itEnd);
+ }
+};
+
+
+}
+
+void ScColumn::CopyToClip(
+ sc::CopyToClipContext& rCxt, SCROW nRow1, SCROW nRow2, ScColumn& rColumn ) const
+{
+ pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray,
+ rCxt.isKeepScenarioFlags() ? (ScMF::All & ~ScMF::Scenario) : ScMF::All );
+
+ {
+ CopyToClipHandler aFunc(GetDoc(), *this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol));
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+ }
+
+ {
+ CopyTextAttrToClipHandler aFunc(rColumn.maCellTextAttrs);
+ sc::ParseBlock(maCellTextAttrs.begin(), maCellTextAttrs, aFunc, nRow1, nRow2);
+ }
+
+ rColumn.CellStorageModified();
+}
+
+void ScColumn::CopyStaticToDocument(
+ SCROW nRow1, SCROW nRow2, const SvNumberFormatterMergeMap& rMap, ScColumn& rDestCol )
+{
+ if (nRow1 > nRow2)
+ return;
+
+ sc::ColumnBlockPosition aDestPos;
+ CopyCellTextAttrsToDocument(nRow1, nRow2, rDestCol);
+ CopyCellNotesToDocument(nRow1, nRow2, rDestCol);
+
+ // First, clear the destination column for the specified row range.
+ rDestCol.maCells.set_empty(nRow1, nRow2);
+
+ aDestPos.miCellPos = rDestCol.maCells.begin();
+
+ ScDocument& rDocument = GetDoc();
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ size_t nOffset = aPos.second;
+ size_t nDataSize = 0;
+ size_t nCurRow = nRow1;
+
+ for (; it != maCells.end() && nCurRow <= o3tl::make_unsigned(nRow2); ++it, nOffset = 0, nCurRow += nDataSize)
+ {
+ bool bLastBlock = false;
+ nDataSize = it->size - nOffset;
+ if (nCurRow + nDataSize - 1 > o3tl::make_unsigned(nRow2))
+ {
+ // Truncate the block to copy to clipboard.
+ nDataSize = nRow2 - nCurRow + 1;
+ bLastBlock = true;
+ }
+
+ switch (it->type)
+ {
+ case sc::element_type_numeric:
+ {
+ sc::numeric_block::const_iterator itData = sc::numeric_block::begin(*it->data);
+ std::advance(itData, nOffset);
+ sc::numeric_block::const_iterator itDataEnd = itData;
+ std::advance(itDataEnd, nDataSize);
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd);
+ }
+ break;
+ case sc::element_type_string:
+ {
+ sc::string_block::const_iterator itData = sc::string_block::begin(*it->data);
+ std::advance(itData, nOffset);
+ sc::string_block::const_iterator itDataEnd = itData;
+ std::advance(itDataEnd, nDataSize);
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd);
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ sc::edittext_block::const_iterator itData = sc::edittext_block::begin(*it->data);
+ std::advance(itData, nOffset);
+ sc::edittext_block::const_iterator itDataEnd = itData;
+ std::advance(itDataEnd, nDataSize);
+
+ // Convert to simple strings.
+ std::vector<svl::SharedString> aConverted;
+ aConverted.reserve(nDataSize);
+ for (; itData != itDataEnd; ++itData)
+ {
+ const EditTextObject& rObj = **itData;
+ svl::SharedString aSS = rDocument.GetSharedStringPool().intern(ScEditUtil::GetString(rObj, &rDocument));
+ aConverted.push_back(aSS);
+ }
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, aConverted.begin(), aConverted.end());
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator itData = sc::formula_block::begin(*it->data);
+ std::advance(itData, nOffset);
+ sc::formula_block::const_iterator itDataEnd = itData;
+ std::advance(itDataEnd, nDataSize);
+
+ // Interpret and convert to raw values.
+ for (SCROW i = 0; itData != itDataEnd; ++itData, ++i)
+ {
+ SCROW nRow = nCurRow + i;
+
+ ScFormulaCell& rFC = **itData;
+ if (rFC.GetDirty() && rDocument.GetAutoCalc())
+ rFC.Interpret();
+
+ if (rFC.GetErrCode() != FormulaError::NONE)
+ // Skip cells with error.
+ continue;
+
+ if (rFC.IsValue())
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, rFC.GetValue());
+ else
+ {
+ svl::SharedString aSS = rFC.GetString();
+ if (aSS.isValid())
+ aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, aSS);
+ }
+ }
+ }
+ break;
+ default:
+ ;
+ }
+
+ if (bLastBlock)
+ break;
+ }
+
+ // Don't forget to copy the number formats over. Charts may reference them.
+ for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
+ {
+ sal_uInt32 nNumFmt = GetNumberFormat(rDocument.GetNonThreadedContext(), nRow);
+ SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt);
+ if (itNum != rMap.end())
+ nNumFmt = itNum->second;
+
+ rDestCol.SetNumberFormat(nRow, nNumFmt);
+ }
+
+ rDestCol.CellStorageModified();
+}
+
+void ScColumn::CopyCellToDocument( SCROW nSrcRow, SCROW nDestRow, ScColumn& rDestCol )
+{
+ ScDocument& rDocument = GetDoc();
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nSrcRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ bool bSet = true;
+ switch (it->type)
+ {
+ case sc::element_type_numeric:
+ rDestCol.maCells.set(nDestRow, sc::numeric_block::at(*it->data, aPos.second));
+ break;
+ case sc::element_type_string:
+ rDestCol.maCells.set(nDestRow, sc::string_block::at(*it->data, aPos.second));
+ break;
+ case sc::element_type_edittext:
+ {
+ EditTextObject* p = sc::edittext_block::at(*it->data, aPos.second);
+ if (&rDocument == &rDestCol.GetDoc())
+ rDestCol.maCells.set(nDestRow, p->Clone().release());
+ else
+ rDestCol.maCells.set(nDestRow, ScEditUtil::Clone(*p, rDestCol.GetDoc()).release());
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
+ if (p->GetDirty() && rDocument.GetAutoCalc())
+ p->Interpret();
+
+ ScAddress aDestPos = p->aPos;
+ aDestPos.SetRow(nDestRow);
+ ScFormulaCell* pNew = new ScFormulaCell(*p, rDestCol.GetDoc(), aDestPos);
+ rDestCol.SetFormulaCell(nDestRow, pNew);
+ }
+ break;
+ case sc::element_type_empty:
+ default:
+ // empty
+ rDestCol.maCells.set_empty(nDestRow, nDestRow);
+ bSet = false;
+ }
+
+ if (bSet)
+ {
+ rDestCol.maCellTextAttrs.set(nDestRow, maCellTextAttrs.get<sc::CellTextAttr>(nSrcRow));
+ ScPostIt* pNote = maCellNotes.get<ScPostIt*>(nSrcRow);
+ if (pNote)
+ {
+ pNote = pNote->Clone(ScAddress(nCol, nSrcRow, nTab),
+ rDestCol.GetDoc(),
+ ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab),
+ false).release();
+ rDestCol.maCellNotes.set(nDestRow, pNote);
+ pNote->UpdateCaptionPos(ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab));
+ }
+ else
+ rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
+ }
+ else
+ {
+ rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow);
+ rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
+ }
+
+ rDestCol.CellStorageModified();
+}
+
+namespace {
+
+bool canCopyValue(const ScDocument& rDoc, const ScAddress& rPos, InsertDeleteFlags nFlags)
+{
+ sal_uInt32 nNumIndex = rDoc.GetAttr(rPos, ATTR_VALUE_FORMAT)->GetValue();
+ SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nNumIndex);
+ if ((nType == SvNumFormatType::DATE) || (nType == SvNumFormatType::TIME) || (nType == SvNumFormatType::DATETIME))
+ return ((nFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE);
+
+ return (nFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
+}
+
+class CopyAsLinkHandler
+{
+ const ScColumn& mrSrcCol;
+ ScColumn& mrDestCol;
+ sc::ColumnBlockPosition maDestPos;
+ sc::ColumnBlockPosition* mpDestPos;
+ InsertDeleteFlags mnCopyFlags;
+
+ sc::StartListeningType meListenType;
+
+ void setDefaultAttrToDest(size_t nRow)
+ {
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
+ }
+
+ void setDefaultAttrsToDest(size_t nRow, size_t nSize)
+ {
+ std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
+ }
+
+ ScFormulaCell* createRefCell(size_t nRow)
+ {
+ ScSingleRefData aRef;
+ aRef.InitAddress(ScAddress(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab())); // Absolute reference.
+ aRef.SetFlag3D(true);
+
+ ScTokenArray aArr(mrDestCol.GetDoc());
+ aArr.AddSingleReference(aRef);
+ return new ScFormulaCell(mrDestCol.GetDoc(), ScAddress(mrDestCol.GetCol(), nRow, mrDestCol.GetTab()), aArr);
+ }
+
+ void createRefBlock(const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
+ {
+ size_t nTopRow = aNode.position + nOffset;
+
+ for (size_t i = 0; i < nDataSize; ++i)
+ {
+ SCROW nRow = nTopRow + i;
+ mrDestCol.SetFormulaCell(maDestPos, nRow, createRefCell(nRow), meListenType);
+ }
+
+ setDefaultAttrsToDest(nTopRow, nDataSize);
+ }
+
+public:
+ CopyAsLinkHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos, InsertDeleteFlags nCopyFlags) :
+ mrSrcCol(rSrcCol),
+ mrDestCol(rDestCol),
+ mpDestPos(pDestPos),
+ mnCopyFlags(nCopyFlags),
+ meListenType(sc::SingleCellListening)
+ {
+ if (mpDestPos)
+ maDestPos = *mpDestPos;
+ }
+
+ ~CopyAsLinkHandler()
+ {
+ if (mpDestPos)
+ {
+ // Similar to CopyByCloneHandler, don't copy a singular iterator.
+ {
+ sc::ColumnBlockPosition aTempBlock;
+ mrDestCol.InitBlockPosition(aTempBlock);
+ maDestPos.miBroadcasterPos = aTempBlock.miBroadcasterPos;
+ }
+
+ *mpDestPos = maDestPos;
+ }
+ }
+
+ void setStartListening( bool b )
+ {
+ meListenType = b ? sc::SingleCellListening : sc::NoListening;
+ }
+
+ void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
+ {
+ size_t nRow = aNode.position + nOffset;
+
+ if (mnCopyFlags & (InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES))
+ {
+ bool bCloneCaption = (mnCopyFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
+ mrSrcCol.DuplicateNotes(nRow, nDataSize, mrDestCol, maDestPos, bCloneCaption);
+ }
+
+ switch (aNode.type)
+ {
+ case sc::element_type_numeric:
+ {
+ if ((mnCopyFlags & (InsertDeleteFlags::DATETIME|InsertDeleteFlags::VALUE)) == InsertDeleteFlags::NONE)
+ return;
+
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::numeric_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ ScAddress aSrcPos(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab());
+ for (; it != itEnd; ++it, aSrcPos.IncRow(), ++nRow)
+ {
+ if (!canCopyValue(mrSrcCol.GetDoc(), aSrcPos, mnCopyFlags))
+ continue;
+
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, createRefCell(nRow));
+ setDefaultAttrToDest(nRow);
+ }
+ }
+ break;
+ case sc::element_type_string:
+ case sc::element_type_edittext:
+ {
+ if (!(mnCopyFlags & InsertDeleteFlags::STRING))
+ return;
+
+ createRefBlock(aNode, nOffset, nDataSize);
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ if (!(mnCopyFlags & InsertDeleteFlags::FORMULA))
+ return;
+
+ createRefBlock(aNode, nOffset, nDataSize);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+};
+
+class CopyByCloneHandler
+{
+ const ScColumn& mrSrcCol;
+ ScColumn& mrDestCol;
+ sc::ColumnBlockPosition maDestPos;
+ sc::ColumnBlockPosition* mpDestPos;
+ svl::SharedStringPool* mpSharedStringPool;
+ InsertDeleteFlags mnCopyFlags;
+
+ sc::StartListeningType meListenType;
+ ScCloneFlags mnFormulaCellCloneFlags;
+
+ void setDefaultAttrToDest(size_t nRow)
+ {
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
+ }
+
+ void setDefaultAttrsToDest(size_t nRow, size_t nSize)
+ {
+ std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
+ maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
+ }
+
+ void cloneFormulaCell(size_t nRow, ScFormulaCell& rSrcCell)
+ {
+ ScAddress aDestPos(mrDestCol.GetCol(), nRow, mrDestCol.GetTab());
+
+ bool bCloneValue = (mnCopyFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
+ bool bCloneDateTime = (mnCopyFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE;
+ bool bCloneString = (mnCopyFlags & InsertDeleteFlags::STRING) != InsertDeleteFlags::NONE;
+ bool bCloneSpecialBoolean = (mnCopyFlags & InsertDeleteFlags::SPECIAL_BOOLEAN) != InsertDeleteFlags::NONE;
+ bool bCloneFormula = (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE;
+
+ bool bForceFormula = false;
+
+ if (bCloneSpecialBoolean)
+ {
+ // See if the formula consists of =TRUE() or =FALSE().
+ const ScTokenArray* pCode = rSrcCell.GetCode();
+ if (pCode && pCode->GetLen() == 1)
+ {
+ const formula::FormulaToken* p = pCode->FirstToken();
+ if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse)
+ // This is a boolean formula.
+ bForceFormula = true;
+ }
+ }
+
+ if (bForceFormula || bCloneFormula)
+ {
+ // Clone as formula cell.
+ ScFormulaCell* pCell = new ScFormulaCell(rSrcCell, mrDestCol.GetDoc(), aDestPos, mnFormulaCellCloneFlags);
+ pCell->SetDirtyVar();
+ mrDestCol.SetFormulaCell(maDestPos, nRow, pCell, meListenType, rSrcCell.NeedsNumberFormat());
+ setDefaultAttrToDest(nRow);
+ return;
+ }
+
+ if (mrDestCol.GetDoc().IsUndo())
+ return;
+
+ if (bCloneValue)
+ {
+ FormulaError nErr = rSrcCell.GetErrCode();
+ if (nErr != FormulaError::NONE)
+ {
+ // error codes are cloned with values
+ ScFormulaCell* pErrCell = new ScFormulaCell(mrDestCol.GetDoc(), aDestPos);
+ pErrCell->SetErrCode(nErr);
+ mrDestCol.SetFormulaCell(maDestPos, nRow, pErrCell, meListenType);
+ setDefaultAttrToDest(nRow);
+ return;
+ }
+ }
+
+ if (bCloneValue || bCloneDateTime)
+ {
+ if (rSrcCell.IsValue())
+ {
+ if (canCopyValue(mrSrcCol.GetDoc(), ScAddress(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab()), mnCopyFlags))
+ {
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(
+ maDestPos.miCellPos, nRow, rSrcCell.GetValue());
+ setDefaultAttrToDest(nRow);
+ }
+
+ return;
+ }
+ }
+
+ if (!bCloneString)
+ return;
+
+ svl::SharedString aStr = rSrcCell.GetString();
+ if (aStr.isEmpty())
+ // Don't create empty string cells.
+ return;
+
+ if (rSrcCell.IsMultilineResult())
+ {
+ // Clone as an edit text object.
+ EditEngine& rEngine = mrDestCol.GetDoc().GetEditEngine();
+ rEngine.SetText(aStr.getString());
+ maDestPos.miCellPos =
+ mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, rEngine.CreateTextObject().release());
+ }
+ else
+ {
+ maDestPos.miCellPos =
+ mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, aStr);
+ }
+
+ setDefaultAttrToDest(nRow);
+ }
+
+public:
+ CopyByCloneHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos,
+ InsertDeleteFlags nCopyFlags, svl::SharedStringPool* pSharedStringPool, bool bGlobalNamesToLocal) :
+ mrSrcCol(rSrcCol),
+ mrDestCol(rDestCol),
+ mpDestPos(pDestPos),
+ mpSharedStringPool(pSharedStringPool),
+ mnCopyFlags(nCopyFlags),
+ meListenType(sc::SingleCellListening),
+ mnFormulaCellCloneFlags(bGlobalNamesToLocal ? ScCloneFlags::NamesToLocal : ScCloneFlags::Default)
+ {
+ if (mpDestPos)
+ maDestPos = *mpDestPos;
+ }
+
+ ~CopyByCloneHandler()
+ {
+ if (!mpDestPos)
+ return;
+
+ // If broadcasters were setup in the same column,
+ // maDestPos.miBroadcasterPos doesn't match
+ // mrDestCol.maBroadcasters because it is never passed anywhere.
+ // Assign a corresponding iterator before copying all over.
+ // Otherwise this may result in wrongly copying a singular
+ // iterator.
+
+ {
+ /* XXX Using a temporary ColumnBlockPosition just for
+ * initializing from ScColumn::maBroadcasters.begin() is ugly,
+ * on the other hand we don't want to expose
+ * ScColumn::maBroadcasters to the outer world and have a
+ * getter. */
+ sc::ColumnBlockPosition aTempBlock;
+ mrDestCol.InitBlockPosition(aTempBlock);
+ maDestPos.miBroadcasterPos = aTempBlock.miBroadcasterPos;
+ }
+
+ *mpDestPos = maDestPos;
+ }
+
+ void setStartListening( bool b )
+ {
+ meListenType = b ? sc::SingleCellListening : sc::NoListening;
+ }
+
+ void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
+ {
+ size_t nRow = aNode.position + nOffset;
+
+ if (mnCopyFlags & (InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES))
+ {
+ bool bCloneCaption = (mnCopyFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
+ mrSrcCol.DuplicateNotes(nRow, nDataSize, mrDestCol, maDestPos, bCloneCaption);
+ }
+
+ switch (aNode.type)
+ {
+ case sc::element_type_numeric:
+ {
+ if ((mnCopyFlags & (InsertDeleteFlags::DATETIME|InsertDeleteFlags::VALUE)) == InsertDeleteFlags::NONE)
+ return;
+
+ sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::numeric_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ ScAddress aSrcPos(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab());
+ for (; it != itEnd; ++it, aSrcPos.IncRow(), ++nRow)
+ {
+ if (!canCopyValue(mrSrcCol.GetDoc(), aSrcPos, mnCopyFlags))
+ continue;
+
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, *it);
+ setDefaultAttrToDest(nRow);
+ }
+ }
+ break;
+ case sc::element_type_string:
+ {
+ if (!(mnCopyFlags & InsertDeleteFlags::STRING))
+ return;
+
+ sc::string_block::const_iterator it = sc::string_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::string_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ for (; it != itEnd; ++it, ++nRow)
+ {
+ const svl::SharedString& rStr = *it;
+ if (rStr.isEmpty())
+ {
+ // String cell with empty value is used to special-case cell value removal.
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set_empty(
+ maDestPos.miCellPos, nRow, nRow);
+ maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set_empty(
+ maDestPos.miCellTextAttrPos, nRow, nRow);
+ }
+ else
+ {
+ if (mpSharedStringPool)
+ {
+ // Re-intern the string if source is a different document.
+ svl::SharedString aInterned = mpSharedStringPool->intern( rStr.getString());
+ maDestPos.miCellPos =
+ mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, aInterned);
+ }
+ else
+ {
+ maDestPos.miCellPos =
+ mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, rStr);
+ }
+ setDefaultAttrToDest(nRow);
+ }
+ }
+ }
+ break;
+ case sc::element_type_edittext:
+ {
+ if (!(mnCopyFlags & InsertDeleteFlags::STRING))
+ return;
+
+ sc::edittext_block::const_iterator it = sc::edittext_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::edittext_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ std::vector<EditTextObject*> aCloned;
+ aCloned.reserve(nDataSize);
+ for (; it != itEnd; ++it)
+ aCloned.push_back(ScEditUtil::Clone(**it, mrDestCol.GetDoc()).release());
+
+ maDestPos.miCellPos = mrDestCol.GetCellStore().set(
+ maDestPos.miCellPos, nRow, aCloned.begin(), aCloned.end());
+
+ setDefaultAttrsToDest(nRow, nDataSize);
+ }
+ break;
+ case sc::element_type_formula:
+ {
+ sc::formula_block::const_iterator it = sc::formula_block::begin(*aNode.data);
+ std::advance(it, nOffset);
+ sc::formula_block::const_iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ sc::DelayStartListeningFormulaCells startDelay(mrDestCol); // disabled
+ if(nDataSize > 1024 && (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE)
+ {
+ // If the column to be replaced contains a long formula group (tdf#102364), there can
+ // be so many listeners in a single vector that the quadratic cost of repeatedly removing
+ // the first element becomes very high. Optimize this by removing them in one go.
+ sc::EndListeningContext context(mrDestCol.GetDoc());
+ mrDestCol.EndListeningFormulaCells( context, nRow, nRow + nDataSize - 1, nullptr, nullptr );
+ // There can be a similar problem with starting to listen to cells repeatedly (tdf#133302).
+ // Delay it.
+ startDelay.set();
+ }
+
+ for (; it != itEnd; ++it, ++nRow)
+ cloneFormulaCell(nRow, **it);
+ }
+ break;
+ default:
+ ;
+ }
+ }
+};
+
+}
+
+void ScColumn::CopyToColumn(
+ sc::CopyToDocContext& rCxt,
+ SCROW nRow1, SCROW nRow2, InsertDeleteFlags nFlags, bool bMarked, ScColumn& rColumn,
+ const ScMarkData* pMarkData, bool bAsLink, bool bGlobalNamesToLocal) const
+{
+ if (bMarked)
+ {
+ SCROW nStart, nEnd;
+ if (pMarkData && pMarkData->IsMultiMarked())
+ {
+ ScMultiSelIter aIter( pMarkData->GetMultiSelData(), nCol );
+
+ while ( aIter.Next( nStart, nEnd ) && nStart <= nRow2 )
+ {
+ if ( nEnd >= nRow1 )
+ CopyToColumn(rCxt, std::max(nRow1,nStart), std::min(nRow2,nEnd),
+ nFlags, false, rColumn, pMarkData, bAsLink );
+ }
+ }
+ else
+ {
+ OSL_FAIL("CopyToColumn: bMarked, but no mark");
+ }
+ return;
+ }
+
+ if ( (nFlags & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE )
+ {
+ if ( (nFlags & InsertDeleteFlags::STYLES) != InsertDeleteFlags::STYLES )
+ { // keep the StyleSheets in the target document
+ // e.g. DIF and RTF Clipboard-Import
+ for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
+ {
+ const ScStyleSheet* pStyle =
+ rColumn.pAttrArray->GetPattern( nRow )->GetStyleSheet();
+ const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow );
+ std::unique_ptr<ScPatternAttr> pNewPattern(new ScPatternAttr( *pPattern ));
+ pNewPattern->SetStyleSheet( const_cast<ScStyleSheet*>(pStyle) );
+ rColumn.pAttrArray->SetPattern( nRow, std::move(pNewPattern), true );
+ }
+ }
+ else
+ pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray);
+ }
+
+ if ((nFlags & InsertDeleteFlags::CONTENTS) == InsertDeleteFlags::NONE)
+ return;
+
+ if (bAsLink)
+ {
+ CopyAsLinkHandler aFunc(*this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), nFlags);
+ aFunc.setStartListening(rCxt.isStartListening());
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+ }
+ else
+ {
+ // Compare the ScDocumentPool* to determine if we are copying
+ // within the same document. If not, re-intern shared strings.
+ svl::SharedStringPool* pSharedStringPool =
+ (GetDoc().GetPool() != rColumn.GetDoc().GetPool()) ?
+ &rColumn.GetDoc().GetSharedStringPool() : nullptr;
+ CopyByCloneHandler aFunc(*this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), nFlags,
+ pSharedStringPool, bGlobalNamesToLocal);
+ aFunc.setStartListening(rCxt.isStartListening());
+ sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
+ }
+
+ rColumn.CellStorageModified();
+}
+
+void ScColumn::UndoToColumn(
+ sc::CopyToDocContext& rCxt, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nFlags, bool bMarked,
+ ScColumn& rColumn ) const
+{
+ if (nRow1 > 0)
+ CopyToColumn(rCxt, 0, nRow1-1, InsertDeleteFlags::FORMULA, false, rColumn);
+
+ CopyToColumn(rCxt, nRow1, nRow2, nFlags, bMarked, rColumn); //TODO: bMarked ????
+
+ if (nRow2 < GetDoc().MaxRow())
+ CopyToColumn(rCxt, nRow2+1, GetDoc().MaxRow(), InsertDeleteFlags::FORMULA, false, rColumn);
+}
+
+void ScColumn::CopyUpdated( const ScColumn* pPosCol, ScColumn& rDestCol ) const
+{
+ // Copy cells from this column to the destination column only for those
+ // rows that are present in the position column (pPosCol).
+
+ // First, mark all the non-empty cell ranges from the position column.
+ sc::SingleColumnSpanSet aRangeSet(GetDoc().GetSheetLimits());
+ if(pPosCol)
+ aRangeSet.scan(*pPosCol);
+
+ // Now, copy cells from this column to the destination column for those
+ // marked row ranges.
+ sc::SingleColumnSpanSet::SpansType aRanges;
+ aRangeSet.getSpans(aRanges);
+
+ CopyToClipHandler aFunc(GetDoc(), *this, rDestCol, nullptr);
+ sc::CellStoreType::const_iterator itPos = maCells.begin();
+ for (const auto& rRange : aRanges)
+ itPos = sc::ParseBlock(itPos, maCells, aFunc, rRange.mnRow1, rRange.mnRow2);
+
+ rDestCol.CellStorageModified();
+}
+
+void ScColumn::CopyScenarioFrom( const ScColumn& rSrcCol )
+{
+ // This is the scenario table, the data is copied into it
+ ScDocument& rDocument = GetDoc();
+ ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), rDocument.GetDefPattern() );
+ SCROW nStart = -1, nEnd = -1;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
+ while (pPattern)
+ {
+ if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
+ {
+ DeleteArea( nStart, nEnd, InsertDeleteFlags::CONTENTS );
+ sc::CopyToDocContext aCxt(rDocument);
+ rSrcCol.
+ CopyToColumn(aCxt, nStart, nEnd, InsertDeleteFlags::CONTENTS, false, *this);
+
+ // UpdateUsed not needed, already done in TestCopyScenario (obsolete comment ?)
+
+ sc::RefUpdateContext aRefCxt(rDocument);
+ aRefCxt.meMode = URM_COPY;
+ aRefCxt.maRange = ScRange(nCol, nStart, nTab, nCol, nEnd, nTab);
+ aRefCxt.mnTabDelta = nTab - rSrcCol.nTab;
+ UpdateReferenceOnCopy(aRefCxt);
+ UpdateCompile();
+ }
+ pPattern = aAttrIter.Next( nStart, nEnd );
+ }
+}
+
+void ScColumn::CopyScenarioTo( ScColumn& rDestCol ) const
+{
+ // This is the scenario table, the data is copied to the other
+ ScDocument& rDocument = GetDoc();
+ ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), rDocument.GetDefPattern() );
+ SCROW nStart = -1, nEnd = -1;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
+ while (pPattern)
+ {
+ if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
+ {
+ rDestCol.DeleteArea( nStart, nEnd, InsertDeleteFlags::CONTENTS );
+ sc::CopyToDocContext aCxt(rDestCol.GetDoc());
+ CopyToColumn(aCxt, nStart, nEnd, InsertDeleteFlags::CONTENTS, false, rDestCol);
+
+ sc::RefUpdateContext aRefCxt(rDocument);
+ aRefCxt.meMode = URM_COPY;
+ aRefCxt.maRange = ScRange(rDestCol.nCol, nStart, rDestCol.nTab, rDestCol.nCol, nEnd, rDestCol.nTab);
+ aRefCxt.mnTabDelta = rDestCol.nTab - nTab;
+ rDestCol.UpdateReferenceOnCopy(aRefCxt);
+ rDestCol.UpdateCompile();
+ }
+ pPattern = aAttrIter.Next( nStart, nEnd );
+ }
+}
+
+bool ScColumn::TestCopyScenarioTo( const ScColumn& rDestCol ) const
+{
+ bool bOk = true;
+ ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), GetDoc().GetDefPattern() );
+ SCROW nStart = 0, nEnd = 0;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
+ while (pPattern && bOk)
+ {
+ if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
+ if ( rDestCol.pAttrArray->HasAttrib( nStart, nEnd, HasAttrFlags::Protected ) )
+ bOk = false;
+
+ pPattern = aAttrIter.Next( nStart, nEnd );
+ }
+ return bOk;
+}
+
+void ScColumn::MarkScenarioIn( ScMarkData& rDestMark ) const
+{
+ ScRange aRange( nCol, 0, nTab );
+
+ ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), GetDoc().GetDefPattern() );
+ SCROW nStart = -1, nEnd = -1;
+ const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
+ while (pPattern)
+ {
+ if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
+ {
+ aRange.aStart.SetRow( nStart );
+ aRange.aEnd.SetRow( nEnd );
+ rDestMark.SetMultiMarkArea( aRange );
+ }
+
+ pPattern = aAttrIter.Next( nStart, nEnd );
+ }
+}
+
+namespace {
+
+void resetColumnPosition(sc::CellStoreType& rCells, SCCOL nCol)
+{
+ for (auto& rCellItem : rCells)
+ {
+ if (rCellItem.type != sc::element_type_formula)
+ continue;
+
+ sc::formula_block::iterator itCell = sc::formula_block::begin(*rCellItem.data);
+ sc::formula_block::iterator itCellEnd = sc::formula_block::end(*rCellItem.data);
+ for (; itCell != itCellEnd; ++itCell)
+ {
+ ScFormulaCell& rCell = **itCell;
+ rCell.aPos.SetCol(nCol);
+ }
+ }
+}
+
+class NoteCaptionUpdater
+{
+ SCCOL mnCol;
+ SCTAB mnTab;
+public:
+ NoteCaptionUpdater( SCCOL nCol, SCTAB nTab ) : mnCol(nCol), mnTab(nTab) {}
+
+ void operator() ( size_t nRow, ScPostIt* p )
+ {
+ p->UpdateCaptionPos(ScAddress(mnCol,nRow,mnTab));
+ }
+};
+
+}
+
+void ScColumn::UpdateNoteCaptions( SCROW nRow1, SCROW nRow2 )
+{
+ NoteCaptionUpdater aFunc(nCol, nTab);
+ sc::ProcessNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
+}
+
+void ScColumn::UpdateDrawObjects(std::vector<std::vector<SdrObject*>>& pObjects, SCROW nRowStart, SCROW nRowEnd)
+{
+ assert(static_cast<int>(pObjects.size()) >= nRowEnd - nRowStart + 1);
+
+ int nObj = 0;
+ for (SCROW nCurrentRow = nRowStart; nCurrentRow <= nRowEnd; nCurrentRow++, nObj++)
+ {
+ if (pObjects[nObj].empty())
+ continue; // No draw objects in this row
+
+ UpdateDrawObjectsForRow(pObjects[nObj], nCol, nCurrentRow);
+ }
+}
+
+void ScColumn::UpdateDrawObjectsForRow( std::vector<SdrObject*>& pObjects, SCCOL nTargetCol, SCROW nTargetRow )
+{
+ for (auto &pObject : pObjects)
+ {
+ ScAddress aNewAddress(nTargetCol, nTargetRow, nTab);
+
+ // Update draw object according to new anchor
+ ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
+ if (pDrawLayer)
+ pDrawLayer->MoveObject(pObject, aNewAddress);
+ }
+}
+
+bool ScColumn::IsDrawObjectsEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
+{
+ ScDrawLayer* pDrawLayer = GetDoc().GetDrawLayer();
+ if (!pDrawLayer)
+ return true;
+
+ ScRange aRange(nCol, nStartRow, nTab, nCol, nEndRow, nTab);
+ return !pDrawLayer->HasObjectsAnchoredInRange(aRange);
+}
+
+void ScColumn::SwapCol(ScColumn& rCol)
+{
+ maBroadcasters.swap(rCol.maBroadcasters);
+ maCells.swap(rCol.maCells);
+ maCellTextAttrs.swap(rCol.maCellTextAttrs);
+ maCellNotes.swap(rCol.maCellNotes);
+ maSparklines.swap(rCol.maSparklines);
+
+ // Swap all CellStoreEvent mdds event_func related.
+ maCells.event_handler().swap(rCol.maCells.event_handler());
+ std::swap( mnBlkCountFormula, rCol.mnBlkCountFormula);
+
+ // notes update caption
+ UpdateNoteCaptions(0, GetDoc().MaxRow());
+ rCol.UpdateNoteCaptions(0, GetDoc().MaxRow());
+
+ std::swap(pAttrArray, rCol.pAttrArray);
+
+ // AttrArray needs to have the right column number
+ pAttrArray->SetCol(nCol);
+ rCol.pAttrArray->SetCol(rCol.nCol);
+
+ // Reset column positions in formula cells.
+ resetColumnPosition(maCells, nCol);
+ resetColumnPosition(rCol.maCells, rCol.nCol);
+
+ CellStorageModified();
+ rCol.CellStorageModified();
+}
+
+void ScColumn::MoveTo(SCROW nStartRow, SCROW nEndRow, ScColumn& rCol)
+{
+ pAttrArray->MoveTo(nStartRow, nEndRow, *rCol.pAttrArray);
+
+ // Mark the non-empty cells within the specified range, for later broadcasting.
+ sc::SingleColumnSpanSet aNonEmpties(GetDoc().GetSheetLimits());
+ aNonEmpties.scan(*this, nStartRow, nEndRow);
+ sc::SingleColumnSpanSet::SpansType aRanges;
+ aNonEmpties.getSpans(aRanges);
+
+ // Split the formula grouping at the top and bottom boundaries.
+ sc::CellStoreType::position_type aPos = maCells.position(nStartRow);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+ if (GetDoc().ValidRow(nEndRow+1))
+ {
+ aPos = maCells.position(aPos.first, nEndRow+1);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+ }
+
+ // Do the same with the destination column.
+ aPos = rCol.maCells.position(nStartRow);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+ if (GetDoc().ValidRow(nEndRow+1))
+ {
+ aPos = rCol.maCells.position(aPos.first, nEndRow+1);
+ sc::SharedFormulaUtil::splitFormulaCellGroup(aPos, nullptr);
+ }
+
+ // Move the broadcasters to the destination column.
+ maBroadcasters.transfer(nStartRow, nEndRow, rCol.maBroadcasters, nStartRow);
+ maCells.transfer(nStartRow, nEndRow, rCol.maCells, nStartRow);
+ maCellTextAttrs.transfer(nStartRow, nEndRow, rCol.maCellTextAttrs, nStartRow);
+
+ // move the notes to the destination column
+ maCellNotes.transfer(nStartRow, nEndRow, rCol.maCellNotes, nStartRow);
+ UpdateNoteCaptions(0, GetDoc().MaxRow());
+
+ // Re-group transferred formula cells.
+ aPos = rCol.maCells.position(nStartRow);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ if (GetDoc().ValidRow(nEndRow+1))
+ {
+ aPos = rCol.maCells.position(aPos.first, nEndRow+1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+
+ CellStorageModified();
+ rCol.CellStorageModified();
+
+ // Broadcast on moved ranges. Area-broadcast only.
+ ScDocument& rDocument = GetDoc();
+ ScHint aHint(SfxHintId::ScDataChanged, ScAddress(nCol, 0, nTab));
+ for (const auto& rRange : aRanges)
+ {
+ for (SCROW nRow = rRange.mnRow1; nRow <= rRange.mnRow2; ++nRow)
+ {
+ aHint.SetAddressRow(nRow);
+ rDocument.AreaBroadcast(aHint);
+ }
+ }
+}
+
+namespace {
+
+class SharedTopFormulaCellPicker
+{
+public:
+ SharedTopFormulaCellPicker() = default;
+ SharedTopFormulaCellPicker(SharedTopFormulaCellPicker const &) = default;
+ SharedTopFormulaCellPicker(SharedTopFormulaCellPicker &&) = default;
+ SharedTopFormulaCellPicker & operator =(SharedTopFormulaCellPicker const &) = default;
+ SharedTopFormulaCellPicker & operator =(SharedTopFormulaCellPicker &&) = default;
+
+ virtual ~SharedTopFormulaCellPicker() {}
+
+ void operator() ( sc::CellStoreType::value_type& node )
+ {
+ if (node.type != sc::element_type_formula)
+ return;
+
+ size_t nTopRow = node.position;
+
+ sc::formula_block::iterator itBeg = sc::formula_block::begin(*node.data);
+ sc::formula_block::iterator itEnd = sc::formula_block::end(*node.data);
+
+ // Only pick shared formula cells that are the top cells of their
+ // respective shared ranges.
+ for (sc::formula_block::iterator it = itBeg; it != itEnd; ++it)
+ {
+ ScFormulaCell* pCell = *it;
+ size_t nRow = nTopRow + std::distance(itBeg, it);
+ if (!pCell->IsShared())
+ {
+ processNonShared(pCell, nRow);
+ continue;
+ }
+
+ if (pCell->IsSharedTop())
+ {
+ ScFormulaCell** pp = &(*it);
+ processSharedTop(pp, nRow, pCell->GetSharedLength());
+
+ // Move to the last cell in the group, to get incremented to
+ // the next cell in the next iteration.
+ size_t nOffsetToLast = pCell->GetSharedLength() - 1;
+ std::advance(it, nOffsetToLast);
+ }
+ }
+ }
+
+ virtual void processNonShared( ScFormulaCell* /*pCell*/, size_t /*nRow*/ ) {}
+ virtual void processSharedTop( ScFormulaCell** /*ppCells*/, size_t /*nRow*/, size_t /*nLength*/ ) {}
+};
+
+class UpdateRefOnCopy
+{
+ const sc::RefUpdateContext& mrCxt;
+ ScDocument* mpUndoDoc;
+ bool mbUpdated;
+
+public:
+ UpdateRefOnCopy(const sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc) :
+ mrCxt(rCxt), mpUndoDoc(pUndoDoc), mbUpdated(false) {}
+
+ bool isUpdated() const { return mbUpdated; }
+
+ void operator() (sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ if (node.type != sc::element_type_formula)
+ return;
+
+ sc::formula_block::iterator it = sc::formula_block::begin(*node.data);
+ std::advance(it, nOffset);
+ sc::formula_block::iterator itEnd = it;
+ std::advance(itEnd, nDataSize);
+
+ for (; it != itEnd; ++it)
+ {
+ ScFormulaCell& rCell = **it;
+ mbUpdated |= rCell.UpdateReference(mrCxt, mpUndoDoc);
+ }
+ }
+};
+
+class UpdateRefOnNonCopy
+{
+ SCCOL mnCol;
+ SCROW mnTab;
+ const sc::RefUpdateContext* mpCxt;
+ ScDocument* mpUndoDoc;
+ bool mbUpdated;
+ bool mbClipboardSource;
+
+ void recompileTokenArray( ScFormulaCell& rTopCell )
+ {
+ // We need to re-compile the token array when a range name is
+ // modified, to correctly reflect the new references in the
+ // name.
+ ScCompiler aComp(mpCxt->mrDoc, rTopCell.aPos, *rTopCell.GetCode(), mpCxt->mrDoc.GetGrammar(),
+ true, rTopCell.GetMatrixFlag() != ScMatrixMode::NONE);
+ aComp.CompileTokenArray();
+ }
+
+ void updateRefOnShift( sc::FormulaGroupEntry& rGroup )
+ {
+ if (!rGroup.mbShared)
+ {
+ ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+ mbUpdated |= rGroup.mpCell->UpdateReferenceOnShift(*mpCxt, mpUndoDoc, &aUndoPos);
+ return;
+ }
+
+ // Update references of a formula group.
+ ScFormulaCell** pp = rGroup.mpCells;
+ ScFormulaCell** ppEnd = pp + rGroup.mnLength;
+ ScFormulaCell* pTop = *pp;
+ ScTokenArray* pCode = pTop->GetCode();
+ ScTokenArray aOldCode(pCode->CloneValue());
+ ScAddress aOldPos = pTop->aPos;
+
+ // Run this before the position gets updated.
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnShift(*mpCxt, aOldPos);
+
+ bool bGroupShifted = false;
+ if (pTop->UpdatePosOnShift(*mpCxt))
+ {
+ ScAddress aErrorPos( ScAddress::UNINITIALIZED );
+ // Update the positions of all formula cells.
+ for (++pp; pp != ppEnd; ++pp) // skip the top cell.
+ {
+ ScFormulaCell* pFC = *pp;
+ if (!pFC->aPos.Move(mpCxt->mnColDelta, mpCxt->mnRowDelta, mpCxt->mnTabDelta,
+ aErrorPos, mpCxt->mrDoc))
+ {
+ assert(!"can't move formula cell");
+ }
+ }
+
+ if (pCode->IsRecalcModeOnRefMove())
+ aRes.mbValueChanged = true;
+
+ // FormulaGroupAreaListener (contrary to ScBroadcastArea) is not
+ // updated but needs to be re-setup, else at least its mpColumn
+ // would indicate the old column to collect cells from. tdf#129396
+ /* TODO: investigate if that could be short-cut to avoid all the
+ * EndListeningTo() / StartListeningTo() overhead and is really
+ * only necessary when shifting the column, not also when shifting
+ * rows. */
+ bGroupShifted = true;
+ }
+ else if (aRes.mbReferenceModified && pCode->IsRecalcModeOnRefMove())
+ {
+ // The cell itself hasn't shifted. But it may have ROW or COLUMN
+ // referencing another cell that has.
+ aRes.mbValueChanged = true;
+ }
+
+ if (aRes.mbNameModified)
+ recompileTokenArray(*pTop);
+
+ if (aRes.mbReferenceModified || aRes.mbNameModified || bGroupShifted)
+ {
+ sc::EndListeningContext aEndCxt(mpCxt->mrDoc, &aOldCode);
+ aEndCxt.setPositionDelta(
+ ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));
+
+ for (pp = rGroup.mpCells; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell* p = *pp;
+ p->EndListeningTo(aEndCxt);
+ p->SetNeedsListening(true);
+ }
+
+ mbUpdated = true;
+
+ fillUndoDoc(aOldPos, rGroup.mnLength, aOldCode);
+ }
+
+ if (aRes.mbValueChanged)
+ {
+ for (pp = rGroup.mpCells; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell* p = *pp;
+ p->SetNeedsDirty(true);
+ }
+ }
+ }
+
+ void updateRefOnMove( sc::FormulaGroupEntry& rGroup )
+ {
+ if (!rGroup.mbShared)
+ {
+ ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+ mbUpdated |= rGroup.mpCell->UpdateReferenceOnMove(*mpCxt, mpUndoDoc, &aUndoPos);
+ return;
+ }
+
+ // Update references of a formula group.
+ ScFormulaCell** pp = rGroup.mpCells;
+ ScFormulaCell** ppEnd = pp + rGroup.mnLength;
+ ScFormulaCell* pTop = *pp;
+ ScTokenArray* pCode = pTop->GetCode();
+ ScTokenArray aOldCode(pCode->CloneValue());
+
+ ScAddress aPos = pTop->aPos;
+ ScAddress aOldPos = aPos;
+
+ bool bCellMoved;
+ if (mpCxt->maRange.Contains(aPos))
+ {
+ bCellMoved = true;
+
+ // The cell is being moved or copied to a new position. The
+ // position has already been updated prior to this call.
+ // Determine its original position before the move which will be
+ // used to adjust relative references later.
+
+ aOldPos.Set(
+ aPos.Col() - mpCxt->mnColDelta,
+ aPos.Row() - mpCxt->mnRowDelta,
+ aPos.Tab() - mpCxt->mnTabDelta);
+ }
+ else
+ {
+ bCellMoved = false;
+ }
+
+ bool bRecalcOnMove = pCode->IsRecalcModeOnRefMove();
+ if (bRecalcOnMove)
+ bRecalcOnMove = aPos != aOldPos;
+
+ sc::RefUpdateResult aRes = pCode->AdjustReferenceOnMove(*mpCxt, aOldPos, aPos);
+
+ if (!(aRes.mbReferenceModified || aRes.mbNameModified || bRecalcOnMove))
+ return;
+
+ sc::AutoCalcSwitch aACSwitch(mpCxt->mrDoc, false);
+
+ if (aRes.mbNameModified)
+ recompileTokenArray(*pTop);
+
+ // Perform end-listening, start-listening, and dirtying on all
+ // formula cells in the group.
+
+ // Make sure that the start and end listening contexts share the
+ // same block position set, else an invalid iterator may ensue.
+ auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(mpCxt->mrDoc);
+
+ sc::StartListeningContext aStartCxt(mpCxt->mrDoc, pPosSet);
+ sc::EndListeningContext aEndCxt(mpCxt->mrDoc, pPosSet, &aOldCode);
+
+ aEndCxt.setPositionDelta(
+ ScAddress(-mpCxt->mnColDelta, -mpCxt->mnRowDelta, -mpCxt->mnTabDelta));
+
+ for (; pp != ppEnd; ++pp)
+ {
+ ScFormulaCell* p = *pp;
+ p->EndListeningTo(aEndCxt);
+ p->StartListeningTo(aStartCxt);
+ p->SetDirty();
+ }
+
+ mbUpdated = true;
+
+ // Move from clipboard is Cut&Paste, then do not copy the original
+ // positions' formula cells to the Undo document.
+ if (!mbClipboardSource || !bCellMoved)
+ fillUndoDoc(aOldPos, rGroup.mnLength, aOldCode);
+ }
+
+ void fillUndoDoc( const ScAddress& rOldPos, SCROW nLength, const ScTokenArray& rOldCode )
+ {
+ if (!mpUndoDoc || nLength <= 0)
+ return;
+
+ // Insert the old formula group into the undo document.
+ ScAddress aUndoPos = rOldPos;
+ ScFormulaCell* pFC = new ScFormulaCell(*mpUndoDoc, aUndoPos, rOldCode.Clone());
+
+ if (nLength == 1)
+ {
+ mpUndoDoc->SetFormulaCell(aUndoPos, pFC);
+ return;
+ }
+
+ std::vector<ScFormulaCell*> aCells;
+ aCells.reserve(nLength);
+ ScFormulaCellGroupRef xGroup = pFC->CreateCellGroup(nLength, false);
+ aCells.push_back(pFC);
+ aUndoPos.IncRow();
+ for (SCROW i = 1; i < nLength; ++i, aUndoPos.IncRow())
+ {
+ pFC = new ScFormulaCell(*mpUndoDoc, aUndoPos, xGroup);
+ aCells.push_back(pFC);
+ }
+
+ if (!mpUndoDoc->SetFormulaCells(rOldPos, aCells))
+ // Insertion failed. Delete all formula cells.
+ std::for_each(aCells.begin(), aCells.end(), std::default_delete<ScFormulaCell>());
+ }
+
+public:
+ UpdateRefOnNonCopy(
+ SCCOL nCol, SCTAB nTab, const sc::RefUpdateContext* pCxt,
+ ScDocument* pUndoDoc) :
+ mnCol(nCol), mnTab(nTab), mpCxt(pCxt),
+ mpUndoDoc(pUndoDoc), mbUpdated(false),
+ mbClipboardSource(pCxt->mrDoc.IsClipboardSource()){}
+
+ void operator() ( sc::FormulaGroupEntry& rGroup )
+ {
+ switch (mpCxt->meMode)
+ {
+ case URM_INSDEL:
+ updateRefOnShift(rGroup);
+ return;
+ case URM_MOVE:
+ updateRefOnMove(rGroup);
+ return;
+ default:
+ ;
+ }
+
+ if (rGroup.mbShared)
+ {
+ ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+ ScFormulaCell** pp = rGroup.mpCells;
+ ScFormulaCell** ppEnd = pp + rGroup.mnLength;
+ for (; pp != ppEnd; ++pp, aUndoPos.IncRow())
+ {
+ ScFormulaCell* p = *pp;
+ mbUpdated |= p->UpdateReference(*mpCxt, mpUndoDoc, &aUndoPos);
+ }
+ }
+ else
+ {
+ ScAddress aUndoPos(mnCol, rGroup.mnRow, mnTab);
+ mbUpdated |= rGroup.mpCell->UpdateReference(*mpCxt, mpUndoDoc, &aUndoPos);
+ }
+ }
+
+ bool isUpdated() const { return mbUpdated; }
+};
+
+class UpdateRefGroupBoundChecker : public SharedTopFormulaCellPicker
+{
+ const sc::RefUpdateContext& mrCxt;
+ std::vector<SCROW>& mrBounds;
+
+public:
+ UpdateRefGroupBoundChecker(const sc::RefUpdateContext& rCxt, std::vector<SCROW>& rBounds) :
+ mrCxt(rCxt), mrBounds(rBounds) {}
+
+ virtual void processSharedTop( ScFormulaCell** ppCells, size_t /*nRow*/, size_t /*nLength*/ ) override
+ {
+ // Check its tokens and record its reference boundaries.
+ ScFormulaCell& rCell = **ppCells;
+ const ScTokenArray& rCode = *rCell.GetCode();
+ rCode.CheckRelativeReferenceBounds(
+ mrCxt, rCell.aPos, rCell.GetSharedLength(), mrBounds);
+ }
+};
+
+class UpdateRefExpandGroupBoundChecker : public SharedTopFormulaCellPicker
+{
+ const sc::RefUpdateContext& mrCxt;
+ std::vector<SCROW>& mrBounds;
+
+public:
+ UpdateRefExpandGroupBoundChecker(const sc::RefUpdateContext& rCxt, std::vector<SCROW>& rBounds) :
+ mrCxt(rCxt), mrBounds(rBounds) {}
+
+ virtual void processSharedTop( ScFormulaCell** ppCells, size_t /*nRow*/, size_t /*nLength*/ ) override
+ {
+ // Check its tokens and record its reference boundaries.
+ ScFormulaCell& rCell = **ppCells;
+ const ScTokenArray& rCode = *rCell.GetCode();
+ rCode.CheckExpandReferenceBounds(
+ mrCxt, rCell.aPos, rCell.GetSharedLength(), mrBounds);
+ }
+};
+
+class FormulaGroupPicker : public SharedTopFormulaCellPicker
+{
+ std::vector<sc::FormulaGroupEntry>& mrGroups;
+
+public:
+ explicit FormulaGroupPicker( std::vector<sc::FormulaGroupEntry>& rGroups ) : mrGroups(rGroups) {}
+
+ virtual void processNonShared( ScFormulaCell* pCell, size_t nRow ) override
+ {
+ mrGroups.emplace_back(pCell, nRow);
+ }
+
+ virtual void processSharedTop( ScFormulaCell** ppCells, size_t nRow, size_t nLength ) override
+ {
+ mrGroups.emplace_back(ppCells, nRow, nLength);
+ }
+};
+
+}
+
+bool ScColumn::UpdateReferenceOnCopy( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc )
+{
+ // When copying, the range equals the destination range where cells
+ // are pasted, and the dx, dy, dz refer to the distance from the
+ // source range.
+
+ UpdateRefOnCopy aHandler(rCxt, pUndoDoc);
+ sc::ColumnBlockPosition* blockPos = rCxt.getBlockPosition(nTab, nCol);
+ sc::CellStoreType::position_type aPos = blockPos
+ ? maCells.position(blockPos->miCellPos, rCxt.maRange.aStart.Row())
+ : maCells.position(rCxt.maRange.aStart.Row());
+ sc::ProcessBlock(aPos.first, maCells, aHandler, rCxt.maRange.aStart.Row(), rCxt.maRange.aEnd.Row());
+
+ // The formula groups at the top and bottom boundaries are expected to
+ // have been split prior to this call. Here, we only do the joining.
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ if (rCxt.maRange.aEnd.Row() < GetDoc().MaxRow())
+ {
+ aPos = maCells.position(aPos.first, rCxt.maRange.aEnd.Row()+1);
+ sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
+ }
+
+ return aHandler.isUpdated();
+}
+
+bool ScColumn::UpdateReference( sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc )
+{
+ if (IsEmptyData() || GetDoc().IsClipOrUndo())
+ // Cells in this column are all empty, or clip or undo doc. No update needed.
+ return false;
+
+ if (rCxt.meMode == URM_COPY)
+ return UpdateReferenceOnCopy(rCxt, pUndoDoc);
+
+ std::vector<SCROW> aBounds;
+
+ bool bThisColShifted = (rCxt.maRange.aStart.Tab() <= nTab && nTab <= rCxt.maRange.aEnd.Tab() &&
+ rCxt.maRange.aStart.Col() <= nCol && nCol <= rCxt.maRange.aEnd.Col());
+ if (bThisColShifted)
+ {
+ // Cells in this column is being shifted. Split formula grouping at
+ // the top and bottom boundaries before they get shifted.
+ // Also, for deleted rows split at the top of the deleted area to adapt
+ // the affected group length.
+ SCROW nSplitPos;
+ if (rCxt.mnRowDelta < 0)
+ {
+ nSplitPos = rCxt.maRange.aStart.Row() + rCxt.mnRowDelta;
+ if (GetDoc().ValidRow(nSplitPos))
+ aBounds.push_back(nSplitPos);
+ }
+ nSplitPos = rCxt.maRange.aStart.Row();
+ if (GetDoc().ValidRow(nSplitPos))
+ {
+ aBounds.push_back(nSplitPos);
+ nSplitPos = rCxt.maRange.aEnd.Row() + 1;
+ if (GetDoc().ValidRow(nSplitPos))
+ aBounds.push_back(nSplitPos);
+ }
+ }
+
+ // Check the row positions at which the group must be split per relative
+ // references.
+ UpdateRefGroupBoundChecker aBoundChecker(rCxt, aBounds);
+ std::for_each(maCells.begin(), maCells.end(), aBoundChecker);
+
+ // If expand reference edges is on, splitting groups may happen anywhere
+ // where a reference points to an adjacent row of the insertion.
+ if (rCxt.mnRowDelta > 0 && rCxt.mrDoc.IsExpandRefs())
+ {
+ UpdateRefExpandGroupBoundChecker aExpandChecker(rCxt, aBounds);
+ std::for_each(maCells.begin(), maCells.end(), aExpandChecker);
+ }
+
+ // Do the actual splitting.
+ const bool bSplit = sc::SharedFormulaUtil::splitFormulaCellGroups(GetDoc(), maCells, aBounds);
+
+ // Collect all formula groups.
+ std::vector<sc::FormulaGroupEntry> aGroups = GetFormulaGroupEntries();
+
+ // Process all collected formula groups.
+ UpdateRefOnNonCopy aHandler(nCol, nTab, &rCxt, pUndoDoc);
+ aHandler = std::for_each(aGroups.begin(), aGroups.end(), aHandler);
+ if (bSplit || aHandler.isUpdated())
+ rCxt.maRegroupCols.set(nTab, nCol);
+
+ return aHandler.isUpdated();
+}
+
+std::vector<sc::FormulaGroupEntry> ScColumn::GetFormulaGroupEntries()
+{
+ std::vector<sc::FormulaGroupEntry> aGroups;
+ std::for_each(maCells.begin(), maCells.end(), FormulaGroupPicker(aGroups));
+ return aGroups;
+}
+
+namespace {
+
+class UpdateTransHandler
+{
+ ScColumn& mrColumn;
+ sc::CellStoreType::iterator miPos;
+ ScRange maSource;
+ ScAddress maDest;
+ ScDocument* mpUndoDoc;
+public:
+ UpdateTransHandler(ScColumn& rColumn, const ScRange& rSource, const ScAddress& rDest, ScDocument* pUndoDoc) :
+ mrColumn(rColumn),
+ miPos(rColumn.GetCellStore().begin()),
+ maSource(rSource), maDest(rDest), mpUndoDoc(pUndoDoc) {}
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
+ miPos = aPos.first;
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
+ pCell->UpdateTranspose(maSource, maDest, mpUndoDoc);
+ ScColumn::JoinNewFormulaCell(aPos, *pCell);
+ }
+};
+
+class UpdateGrowHandler
+{
+ ScColumn& mrColumn;
+ sc::CellStoreType::iterator miPos;
+ ScRange maArea;
+ SCCOL mnGrowX;
+ SCROW mnGrowY;
+public:
+ UpdateGrowHandler(ScColumn& rColumn, const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY) :
+ mrColumn(rColumn),
+ miPos(rColumn.GetCellStore().begin()),
+ maArea(rArea), mnGrowX(nGrowX), mnGrowY(nGrowY) {}
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
+ miPos = aPos.first;
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
+ pCell->UpdateGrow(maArea, mnGrowX, mnGrowY);
+ ScColumn::JoinNewFormulaCell(aPos, *pCell);
+ }
+};
+
+class InsertTabUpdater
+{
+ sc::RefUpdateInsertTabContext& mrCxt;
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ SCTAB mnTab;
+ bool mbModified;
+
+public:
+ InsertTabUpdater(sc::RefUpdateInsertTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
+ mrCxt(rCxt),
+ mrTextAttrs(rTextAttrs),
+ miAttrPos(rTextAttrs.begin()),
+ mnTab(nTab),
+ mbModified(false) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->UpdateInsertTab(mrCxt);
+ mbModified = true;
+ }
+
+ void operator() (size_t nRow, EditTextObject* pCell)
+ {
+ editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
+ aUpdater.updateTableFields(mnTab);
+ miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+};
+
+class DeleteTabUpdater
+{
+ sc::RefUpdateDeleteTabContext& mrCxt;
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ SCTAB mnTab;
+ bool mbModified;
+public:
+ DeleteTabUpdater(sc::RefUpdateDeleteTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
+ mrCxt(rCxt),
+ mrTextAttrs(rTextAttrs),
+ miAttrPos(rTextAttrs.begin()),
+ mnTab(nTab),
+ mbModified(false) {}
+
+ void operator() (size_t, ScFormulaCell* pCell)
+ {
+ pCell->UpdateDeleteTab(mrCxt);
+ mbModified = true;
+ }
+
+ void operator() (size_t nRow, EditTextObject* pCell)
+ {
+ editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
+ aUpdater.updateTableFields(mnTab);
+ miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+};
+
+class InsertAbsTabUpdater
+{
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ SCTAB mnTab;
+ SCTAB mnNewPos;
+ bool mbModified;
+public:
+ InsertAbsTabUpdater(sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab, SCTAB nNewPos) :
+ mrTextAttrs(rTextAttrs),
+ miAttrPos(rTextAttrs.begin()),
+ mnTab(nTab),
+ mnNewPos(nNewPos),
+ mbModified(false) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->UpdateInsertTabAbs(mnNewPos);
+ mbModified = true;
+ }
+
+ void operator() (size_t nRow, EditTextObject* pCell)
+ {
+ editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
+ aUpdater.updateTableFields(mnTab);
+ miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+};
+
+class MoveTabUpdater
+{
+ sc::RefUpdateMoveTabContext& mrCxt;
+ sc::CellTextAttrStoreType& mrTextAttrs;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ SCTAB mnTab;
+ bool mbModified;
+public:
+ MoveTabUpdater(sc::RefUpdateMoveTabContext& rCxt, sc::CellTextAttrStoreType& rTextAttrs, SCTAB nTab) :
+ mrCxt(rCxt),
+ mrTextAttrs(rTextAttrs),
+ miAttrPos(rTextAttrs.begin()),
+ mnTab(nTab),
+ mbModified(false) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->UpdateMoveTab(mrCxt, mnTab);
+ mbModified = true;
+ }
+
+ void operator() (size_t nRow, EditTextObject* pCell)
+ {
+ editeng::FieldUpdater aUpdater = pCell->GetFieldUpdater();
+ aUpdater.updateTableFields(mnTab);
+ miAttrPos = mrTextAttrs.set(miAttrPos, nRow, sc::CellTextAttr());
+ mbModified = true;
+ }
+
+ bool isModified() const { return mbModified; }
+};
+
+class UpdateCompileHandler
+{
+ bool mbForceIfNameInUse:1;
+public:
+ explicit UpdateCompileHandler(bool bForceIfNameInUse) :
+ mbForceIfNameInUse(bForceIfNameInUse) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->UpdateCompile(mbForceIfNameInUse);
+ }
+};
+
+class TabNoSetter
+{
+ SCTAB mnTab;
+public:
+ explicit TabNoSetter(SCTAB nTab) : mnTab(nTab) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->aPos.SetTab(mnTab);
+ }
+};
+
+class UsedRangeNameFinder
+{
+ sc::UpdatedRangeNames& mrIndexes;
+public:
+ explicit UsedRangeNameFinder(sc::UpdatedRangeNames& rIndexes) : mrIndexes(rIndexes) {}
+
+ void operator() (size_t /*nRow*/, const ScFormulaCell* pCell)
+ {
+ pCell->FindRangeNamesInUse(mrIndexes);
+ }
+};
+
+class CheckVectorizationHandler
+{
+public:
+ CheckVectorizationHandler()
+ {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ ScTokenArray* pCode = p->GetCode();
+ if (pCode && pCode->IsFormulaVectorDisabled())
+ {
+ pCode->ResetVectorState();
+ FormulaTokenArrayPlainIterator aIter(*pCode);
+ FormulaToken* pFT = aIter.First();
+ while (pFT)
+ {
+ pCode->CheckToken(*pFT);
+ pFT = aIter.Next();
+ }
+ }
+ }
+};
+
+struct SetDirtyVarHandler
+{
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ p->SetDirtyVar();
+ }
+};
+
+class SetDirtyHandler
+{
+ ScDocument& mrDoc;
+ const sc::SetFormulaDirtyContext& mrCxt;
+public:
+ SetDirtyHandler( ScDocument& rDoc, const sc::SetFormulaDirtyContext& rCxt ) :
+ mrDoc(rDoc), mrCxt(rCxt) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ if (mrCxt.mbClearTabDeletedFlag)
+ {
+ if (!p->IsShared() || p->IsSharedTop())
+ {
+ ScTokenArray* pCode = p->GetCode();
+ pCode->ClearTabDeleted(
+ p->aPos, mrCxt.mnTabDeletedStart, mrCxt.mnTabDeletedEnd);
+ }
+ }
+
+ p->SetDirtyVar();
+ if (!mrDoc.IsInFormulaTree(p))
+ mrDoc.PutInFormulaTree(p);
+ }
+};
+
+class SetDirtyOnRangeHandler
+{
+ sc::SingleColumnSpanSet maValueRanges;
+ ScColumn& mrColumn;
+public:
+ explicit SetDirtyOnRangeHandler(ScColumn& rColumn)
+ : maValueRanges(rColumn.GetDoc().GetSheetLimits()),
+ mrColumn(rColumn) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ p->SetDirty();
+ }
+
+ void operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize)
+ {
+ if (type == sc::element_type_empty)
+ // Ignore empty blocks.
+ return;
+
+ // Non-formula cells.
+ SCROW nRow1 = nTopRow;
+ SCROW nRow2 = nTopRow + nDataSize - 1;
+ maValueRanges.set(nRow1, nRow2, true);
+ }
+
+ void broadcast()
+ {
+ std::vector<SCROW> aRows;
+ maValueRanges.getRows(aRows);
+ mrColumn.BroadcastCells(aRows, SfxHintId::ScDataChanged);
+ }
+
+ void fillBroadcastSpans( sc::ColumnSpanSet& rBroadcastSpans ) const
+ {
+ SCCOL nCol = mrColumn.GetCol();
+ SCTAB nTab = mrColumn.GetTab();
+ sc::SingleColumnSpanSet::SpansType aSpans;
+ maValueRanges.getSpans(aSpans);
+
+ for (const auto& rSpan : aSpans)
+ rBroadcastSpans.set(mrColumn.GetDoc(), nTab, nCol, rSpan.mnRow1, rSpan.mnRow2, true);
+ }
+};
+
+class SetTableOpDirtyOnRangeHandler
+{
+ sc::SingleColumnSpanSet maValueRanges;
+ ScColumn& mrColumn;
+public:
+ explicit SetTableOpDirtyOnRangeHandler(ScColumn& rColumn)
+ : maValueRanges(rColumn.GetDoc().GetSheetLimits()),
+ mrColumn(rColumn) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* p)
+ {
+ p->SetTableOpDirty();
+ }
+
+ void operator() (mdds::mtv::element_t type, size_t nTopRow, size_t nDataSize)
+ {
+ if (type == sc::element_type_empty)
+ // Ignore empty blocks.
+ return;
+
+ // Non-formula cells.
+ SCROW nRow1 = nTopRow;
+ SCROW nRow2 = nTopRow + nDataSize - 1;
+ maValueRanges.set(nRow1, nRow2, true);
+ }
+
+ void broadcast()
+ {
+ std::vector<SCROW> aRows;
+ maValueRanges.getRows(aRows);
+ mrColumn.BroadcastCells(aRows, SfxHintId::ScTableOpDirty);
+ }
+};
+
+struct SetDirtyAfterLoadHandler
+{
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+#if 1
+ // Simply set dirty and append to FormulaTree, without broadcasting,
+ // which is a magnitude faster. This is used to calculate the entire
+ // document, e.g. when loading alien file formats.
+ pCell->SetDirtyAfterLoad();
+#else
+/* This was used with the binary file format that stored results, where only
+ * newly compiled and volatile functions and their dependents had to be
+ * recalculated, which was faster then. Since that was moved to 'binfilter' to
+ * convert to an XML file this isn't needed anymore, and not used for other
+ * file formats. Kept for reference in case mechanism needs to be reactivated
+ * for some file formats, we'd have to introduce a controlling parameter to
+ * this method here then.
+*/
+
+ // If the cell was already dirty because of CalcAfterLoad,
+ // FormulaTracking has to take place.
+ if (pCell->GetDirty())
+ pCell->SetDirty();
+#endif
+ }
+};
+
+struct SetDirtyIfPostponedHandler
+{
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ if (pCell->IsPostponedDirty() || (pCell->HasRelNameReference() != ScFormulaCell::RelNameRef::NONE))
+ pCell->SetDirty();
+ }
+};
+
+struct CalcAllHandler
+{
+#define DEBUG_SC_CHECK_FORMULATREE_CALCULATION 0
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+#if DEBUG_SC_CHECK_FORMULATREE_CALCULATION
+ // after F9 ctrl-F9: check the calculation for each FormulaTree
+ double nOldVal, nNewVal;
+ nOldVal = pCell->GetValue();
+#endif
+ pCell->Interpret();
+#if DEBUG_SC_CHECK_FORMULATREE_CALCULATION
+ if (pCell->GetCode()->IsRecalcModeNormal())
+ nNewVal = pCell->GetValue();
+ else
+ nNewVal = nOldVal; // random(), jetzt() etc.
+
+ assert(nOldVal == nNewVal);
+#endif
+ }
+#undef DEBUG_SC_CHECK_FORMULATREE_CALCULATION
+};
+
+class CompileAllHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+public:
+ explicit CompileAllHandler( sc::CompileFormulaContext& rCxt ) : mrCxt(rCxt) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ // for unconditional compilation
+ // bCompile=true and pCode->nError=0
+ pCell->GetCode()->SetCodeError(FormulaError::NONE);
+ pCell->SetCompile(true);
+ pCell->CompileTokenArray(mrCxt);
+ }
+};
+
+class CompileXMLHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+ ScProgress& mrProgress;
+ const ScColumn& mrCol;
+public:
+ CompileXMLHandler( sc::CompileFormulaContext& rCxt, ScProgress& rProgress, const ScColumn& rCol) :
+ mrCxt(rCxt),
+ mrProgress(rProgress),
+ mrCol(rCol) {}
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ sal_uInt32 nFormat = mrCol.GetNumberFormat(mrCol.GetDoc().GetNonThreadedContext(), nRow);
+ if( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) != 0)
+ // Non-default number format is set.
+ pCell->SetNeedNumberFormat(false);
+ else if (pCell->NeedsNumberFormat())
+ pCell->SetDirtyVar();
+
+ if (pCell->GetMatrixFlag() != ScMatrixMode::NONE)
+ pCell->SetDirtyVar();
+
+ pCell->CompileXML(mrCxt, mrProgress);
+ }
+};
+
+class CompileErrorCellsHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+ ScColumn& mrColumn;
+ sc::CellStoreType::iterator miPos;
+ FormulaError mnErrCode;
+ bool mbCompiled;
+public:
+ CompileErrorCellsHandler( sc::CompileFormulaContext& rCxt, ScColumn& rColumn, FormulaError nErrCode ) :
+ mrCxt(rCxt),
+ mrColumn(rColumn),
+ miPos(mrColumn.GetCellStore().begin()),
+ mnErrCode(nErrCode),
+ mbCompiled(false)
+ {
+ }
+
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ FormulaError nCurError = pCell->GetRawError();
+ if (nCurError == FormulaError::NONE)
+ // It's not an error cell. Skip it.
+ return;
+
+ if (mnErrCode != FormulaError::NONE && nCurError != mnErrCode)
+ // Error code is specified, and it doesn't match. Skip it.
+ return;
+
+ sc::CellStoreType::position_type aPos = mrColumn.GetCellStore().position(miPos, nRow);
+ miPos = aPos.first;
+ sc::SharedFormulaUtil::unshareFormulaCell(aPos, *pCell);
+ pCell->GetCode()->SetCodeError(FormulaError::NONE);
+ OUString aFormula = pCell->GetFormula(mrCxt);
+ pCell->Compile(mrCxt, aFormula);
+ ScColumn::JoinNewFormulaCell(aPos, *pCell);
+
+ mbCompiled = true;
+ }
+
+ bool isCompiled() const { return mbCompiled; }
+};
+
+class CalcAfterLoadHandler
+{
+ sc::CompileFormulaContext& mrCxt;
+ bool mbStartListening;
+
+public:
+ CalcAfterLoadHandler( sc::CompileFormulaContext& rCxt, bool bStartListening ) :
+ mrCxt(rCxt), mbStartListening(bStartListening) {}
+
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->CalcAfterLoad(mrCxt, mbStartListening);
+ }
+};
+
+struct ResetChangedHandler
+{
+ void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
+ {
+ pCell->SetChanged(false);
+ }
+};
+
+/**
+ * Ambiguous script type counts as edit cell.
+ */
+class FindEditCellsHandler
+{
+ ScColumn& mrColumn;
+ sc::CellTextAttrStoreType::iterator miAttrPos;
+ sc::CellStoreType::iterator miCellPos;
+
+public:
+ explicit FindEditCellsHandler(ScColumn& rCol) :
+ mrColumn(rCol),
+ miAttrPos(rCol.GetCellAttrStore().begin()),
+ miCellPos(rCol.GetCellStore().begin()) {}
+
+ bool operator() (size_t, const EditTextObject*)
+ {
+ // This is definitely an edit text cell.
+ return true;
+ }
+
+ bool operator() (size_t nRow, const ScFormulaCell* p)
+ {
+ // With a formula cell, it's considered an edit text cell when either
+ // the result is multi-line or it has more than one script types.
+ SvtScriptType nScriptType = mrColumn.GetRangeScriptType(miAttrPos, nRow, nRow, miCellPos);
+ if (IsAmbiguousScriptNonZero(nScriptType))
+ return true;
+
+ return const_cast<ScFormulaCell*>(p)->IsMultilineResult();
+ }
+
+ /**
+ * Callback for a block of other types.
+ */
+ std::pair<size_t,bool> operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
+ {
+ typedef std::pair<size_t,bool> RetType;
+
+ if (node.type == sc::element_type_empty)
+ // Ignore empty blocks.
+ return RetType(0, false);
+
+ // Check the script type of a non-empty element and see if it has
+ // multiple script types.
+ for (size_t i = 0; i < nDataSize; ++i)
+ {
+ SCROW nRow = node.position + i + nOffset;
+ SvtScriptType nScriptType = mrColumn.GetRangeScriptType(miAttrPos, nRow, nRow, miCellPos);
+ if (IsAmbiguousScriptNonZero(nScriptType))
+ // Return the offset from the first row.
+ return RetType(i+nOffset, true);
+ }
+
+ // No edit text cell found.
+ return RetType(0, false);
+ }
+};
+
+}
+
+void ScColumn::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest,
+ ScDocument* pUndoDoc )
+{
+ UpdateTransHandler aFunc(*this, rSource, rDest, pUndoDoc);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ UpdateGrowHandler aFunc(*this, rArea, nGrowX, nGrowY);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::UpdateInsertTab( sc::RefUpdateInsertTabContext& rCxt )
+{
+ if (nTab >= rCxt.mnInsertPos)
+ {
+ nTab += rCxt.mnSheets;
+ pAttrArray->SetTab(nTab);
+ }
+
+ UpdateInsertTabOnlyCells(rCxt);
+}
+
+void ScColumn::UpdateInsertTabOnlyCells( sc::RefUpdateInsertTabContext& rCxt )
+{
+ InsertTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
+ sc::ProcessFormulaEditText(maCells, aFunc);
+ if (aFunc.isModified())
+ CellStorageModified();
+}
+
+void ScColumn::UpdateDeleteTab( sc::RefUpdateDeleteTabContext& rCxt )
+{
+ if (nTab > rCxt.mnDeletePos)
+ {
+ nTab -= rCxt.mnSheets;
+ pAttrArray->SetTab(nTab);
+ }
+
+ DeleteTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
+ sc::ProcessFormulaEditText(maCells, aFunc);
+ if (aFunc.isModified())
+ CellStorageModified();
+}
+
+void ScColumn::UpdateInsertTabAbs(SCTAB nNewPos)
+{
+ InsertAbsTabUpdater aFunc(maCellTextAttrs, nTab, nNewPos);
+ sc::ProcessFormulaEditText(maCells, aFunc);
+ if (aFunc.isModified())
+ CellStorageModified();
+}
+
+void ScColumn::UpdateMoveTab( sc::RefUpdateMoveTabContext& rCxt, SCTAB nTabNo )
+{
+ nTab = nTabNo;
+ pAttrArray->SetTab( nTabNo );
+
+ MoveTabUpdater aFunc(rCxt, maCellTextAttrs, nTab);
+ sc::ProcessFormulaEditText(maCells, aFunc);
+ if (aFunc.isModified())
+ CellStorageModified();
+}
+
+void ScColumn::UpdateCompile( bool bForceIfNameInUse )
+{
+ UpdateCompileHandler aFunc(bForceIfNameInUse);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::SetTabNo(SCTAB nNewTab)
+{
+ nTab = nNewTab;
+ pAttrArray->SetTab( nNewTab );
+
+ TabNoSetter aFunc(nTab);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::FindRangeNamesInUse(SCROW nRow1, SCROW nRow2, sc::UpdatedRangeNames& rIndexes) const
+{
+ UsedRangeNameFinder aFunc(rIndexes);
+ sc::ParseFormula(maCells.begin(), maCells, nRow1, nRow2, aFunc);
+}
+
+void ScColumn::SetDirtyVar()
+{
+ SetDirtyVarHandler aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+bool ScColumn::IsFormulaDirty( SCROW nRow ) const
+{
+ if (!GetDoc().ValidRow(nRow))
+ return false;
+
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
+ sc::CellStoreType::const_iterator it = aPos.first;
+ if (it->type != sc::element_type_formula)
+ // This is not a formula cell block.
+ return false;
+
+ const ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
+ return p->GetDirty();
+}
+
+void ScColumn::CheckVectorizationState()
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ CheckVectorizationHandler aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
+{
+ // is only done documentwide, no FormulaTracking
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ SetDirtyHandler aFunc(GetDoc(), rCxt);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::SetDirtyFromClip( SCROW nRow1, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
+{
+ // Set all formula cells in the range dirty, and pick up all non-formula
+ // cells for later broadcasting. We don't broadcast here.
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+
+ SetDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
+ aHdl.fillBroadcastSpans(rBroadcastSpans);
+}
+
+namespace {
+
+class BroadcastBroadcastersHandler
+{
+ ScHint maHint;
+ bool mbBroadcasted;
+
+public:
+ explicit BroadcastBroadcastersHandler( SfxHintId nHint, SCTAB nTab, SCCOL nCol )
+ : maHint(nHint, ScAddress(nCol, 0, nTab))
+ , mbBroadcasted(false)
+ {
+ }
+
+ void operator() ( size_t nRow, SvtBroadcaster* pBroadcaster )
+ {
+ maHint.SetAddressRow(nRow);
+ pBroadcaster->Broadcast(maHint);
+ mbBroadcasted = true;
+ }
+
+ bool wasBroadcasted() { return mbBroadcasted; }
+};
+
+}
+
+bool ScColumn::BroadcastBroadcasters( SCROW nRow1, SCROW nRow2, SfxHintId nHint )
+{
+ BroadcastBroadcastersHandler aBroadcasterHdl(nHint, nTab, nCol);
+ sc::ProcessBroadcaster(maBroadcasters.begin(), maBroadcasters, nRow1, nRow2, aBroadcasterHdl);
+ return aBroadcasterHdl.wasBroadcasted();
+}
+
+void ScColumn::SetDirty( SCROW nRow1, SCROW nRow2, BroadcastMode eMode )
+{
+ // broadcasts everything within the range, with FormulaTracking
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+
+ switch (eMode)
+ {
+ case BROADCAST_NONE:
+ {
+ // Handler only used with formula cells.
+ SetDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl);
+ }
+ break;
+ case BROADCAST_DATA_POSITIONS:
+ {
+ // Handler used with both, formula and non-formula cells.
+ SetDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
+ aHdl.broadcast();
+ }
+ break;
+ case BROADCAST_BROADCASTERS:
+ {
+ // Handler only used with formula cells.
+ SetDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl);
+ // Broadcast all broadcasters in range.
+ if (BroadcastBroadcasters( nRow1, nRow2, SfxHintId::ScDataChanged))
+ {
+ // SetDirtyOnRangeHandler implicitly tracks notified
+ // formulas via ScDocument::Broadcast(), which
+ // BroadcastBroadcastersHandler doesn't, so explicitly
+ // track them here.
+ GetDoc().TrackFormulas();
+ }
+ }
+ break;
+ }
+}
+
+void ScColumn::SetTableOpDirty( const ScRange& rRange )
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+ SetTableOpDirtyOnRangeHandler aHdl(*this);
+ sc::ProcessFormula(maCells.begin(), maCells, nRow1, nRow2, aHdl, aHdl);
+ aHdl.broadcast();
+}
+
+void ScColumn::SetDirtyAfterLoad()
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ SetDirtyAfterLoadHandler aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+namespace {
+
+class RecalcOnRefMoveCollector
+{
+ std::vector<SCROW> maDirtyRows;
+public:
+ void operator() (size_t nRow, ScFormulaCell* pCell)
+ {
+ if (pCell->GetDirty() && pCell->GetCode()->IsRecalcModeOnRefMove())
+ maDirtyRows.push_back(nRow);
+ }
+
+ const std::vector<SCROW>& getDirtyRows() const
+ {
+ return maDirtyRows;
+ }
+};
+
+}
+
+void ScColumn::SetDirtyIfPostponed()
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ SetDirtyIfPostponedHandler aFunc;
+ ScBulkBroadcast aBulkBroadcast( GetDoc().GetBASM(), SfxHintId::ScDataChanged);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::BroadcastRecalcOnRefMove()
+{
+ sc::AutoCalcSwitch aSwitch(GetDoc(), false);
+ RecalcOnRefMoveCollector aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+ BroadcastCells(aFunc.getDirtyRows(), SfxHintId::ScDataChanged);
+}
+
+void ScColumn::CalcAll()
+{
+ CalcAllHandler aFunc;
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::CompileAll( sc::CompileFormulaContext& rCxt )
+{
+ CompileAllHandler aFunc(rCxt);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::CompileXML( sc::CompileFormulaContext& rCxt, ScProgress& rProgress )
+{
+ CompileXMLHandler aFunc(rCxt, rProgress, *this);
+ sc::ProcessFormula(maCells, aFunc);
+ RegroupFormulaCells();
+}
+
+bool ScColumn::CompileErrorCells( sc::CompileFormulaContext& rCxt, FormulaError nErrCode )
+{
+ CompileErrorCellsHandler aHdl(rCxt, *this, nErrCode);
+ sc::ProcessFormula(maCells, aHdl);
+ return aHdl.isCompiled();
+}
+
+void ScColumn::CalcAfterLoad( sc::CompileFormulaContext& rCxt, bool bStartListening )
+{
+ CalcAfterLoadHandler aFunc(rCxt, bStartListening);
+ sc::ProcessFormula(maCells, aFunc);
+}
+
+void ScColumn::ResetChanged( SCROW nStartRow, SCROW nEndRow )
+{
+ ResetChangedHandler aFunc;
+ sc::ProcessFormula(maCells.begin(), maCells, nStartRow, nEndRow, aFunc);
+}
+
+bool ScColumn::HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW& rFirst)
+{
+ // used in GetOptimalHeight - ambiguous script type counts as edit cell
+
+ FindEditCellsHandler aFunc(*this);
+ std::pair<sc::CellStoreType::const_iterator,size_t> aPos =
+ sc::FindFormulaEditText(maCells, nStartRow, nEndRow, aFunc);
+
+ if (aPos.first == maCells.end())
+ return false;
+
+ rFirst = aPos.first->position + aPos.second;
+ return true;
+}
+
+SCROW ScColumn::SearchStyle(
+ SCROW nRow, const ScStyleSheet* pSearchStyle, bool bUp, bool bInSelection,
+ const ScMarkData& rMark) const
+{
+ if (bInSelection)
+ {
+ if (rMark.IsMultiMarked())
+ {
+ ScMarkArray aArray(rMark.GetMarkArray(nCol));
+ return pAttrArray->SearchStyle(nRow, pSearchStyle, bUp, &aArray);
+ }
+ else
+ return -1;
+ }
+ else
+ return pAttrArray->SearchStyle( nRow, pSearchStyle, bUp );
+}
+
+bool ScColumn::SearchStyleRange(
+ SCROW& rRow, SCROW& rEndRow, const ScStyleSheet* pSearchStyle, bool bUp,
+ bool bInSelection, const ScMarkData& rMark) const
+{
+ if (bInSelection)
+ {
+ if (rMark.IsMultiMarked())
+ {
+ ScMarkArray aArray(rMark.GetMarkArray(nCol));
+ return pAttrArray->SearchStyleRange(
+ rRow, rEndRow, pSearchStyle, bUp, &aArray);
+ }
+ else
+ return false;
+ }
+ else
+ return pAttrArray->SearchStyleRange( rRow, rEndRow, pSearchStyle, bUp );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */