diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /sc/source/core/data/document10.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.tar.xz libreoffice-ed5640d8b587fbcfed7dd7967f3de04b37a76f26.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sc/source/core/data/document10.cxx')
-rw-r--r-- | sc/source/core/data/document10.cxx | 1106 |
1 files changed, 1106 insertions, 0 deletions
diff --git a/sc/source/core/data/document10.cxx b/sc/source/core/data/document10.cxx new file mode 100644 index 000000000..ec61fa530 --- /dev/null +++ b/sc/source/core/data/document10.cxx @@ -0,0 +1,1106 @@ +/* -*- 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/. + */ + +#include <memory> +#include <document.hxx> +#include <clipcontext.hxx> +#include <clipparam.hxx> +#include <table.hxx> +#include <tokenarray.hxx> +#include <listenercontext.hxx> +#include <tokenstringcontext.hxx> +#include <poolhelp.hxx> +#include <cellvalues.hxx> +#include <docpool.hxx> +#include <columniterator.hxx> + +#include <refupdatecontext.hxx> +#include <sal/log.hxx> +#include <editeng/colritem.hxx> +#include <scitems.hxx> +#include <datamapper.hxx> +#include <docsh.hxx> + +// Add totally brand-new methods to this source file. + +bool ScDocument::IsMerged( const ScAddress& rPos ) const +{ + const ScTable* pTab = FetchTable(rPos.Tab()); + if (!pTab) + return false; + + return pTab->IsMerged(rPos.Col(), rPos.Row()); +} + +sc::MultiDataCellState ScDocument::HasMultipleDataCells( const ScRange& rRange ) const +{ + if (rRange.aStart.Tab() != rRange.aEnd.Tab()) + // Currently we only support a single-sheet range. + return sc::MultiDataCellState(); + + const ScTable* pTab = FetchTable(rRange.aStart.Tab()); + if (!pTab) + return sc::MultiDataCellState(sc::MultiDataCellState::Empty); + + const ScAddress& s = rRange.aStart; + const ScAddress& e = rRange.aEnd; + return pTab->HasMultipleDataCells(s.Col(), s.Row(), e.Col(), e.Row()); +} + +void ScDocument::DeleteBeforeCopyFromClip( + sc::CopyFromClipContext& rCxt, const ScMarkData& rMark, sc::ColumnSpanSet& rBroadcastSpans ) +{ + SCTAB nClipTab = 0; + const TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs; + SCTAB nClipTabCount = rClipTabs.size(); + + for (SCTAB nTab = rCxt.getTabStart(); nTab <= rCxt.getTabEnd(); ++nTab) + { + ScTable* pTab = FetchTable(nTab); + if (!pTab) + continue; + + if (!rMark.GetTableSelect(nTab)) + continue; + + while (!rClipTabs[nClipTab]) + nClipTab = (nClipTab+1) % nClipTabCount; + + pTab->DeleteBeforeCopyFromClip(rCxt, *rClipTabs[nClipTab], rBroadcastSpans); + + nClipTab = (nClipTab+1) % nClipTabCount; + } +} + +bool ScDocument::CopyOneCellFromClip( + sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ + ScDocument* pClipDoc = rCxt.getClipDoc(); + if (pClipDoc->GetClipParam().mbCutMode) + // We don't handle cut and paste or moving of cells here. + return false; + + ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange(); + if (aClipRange.aStart.Row() != aClipRange.aEnd.Row()) + // The source is not really a single row. Bail out. + return false; + + SCCOL nSrcColSize = aClipRange.aEnd.Col() - aClipRange.aStart.Col() + 1; + SCCOL nDestColSize = nCol2 - nCol1 + 1; + if (nDestColSize < nSrcColSize) + return false; + + if (pClipDoc->maTabs.size() > 1) + // Copying from multiple source sheets is not handled here. + return false; + + ScAddress aSrcPos = aClipRange.aStart; + + for (SCCOL nCol = aClipRange.aStart.Col(); nCol <= aClipRange.aEnd.Col(); ++nCol) + { + ScAddress aTestPos = aSrcPos; + aTestPos.SetCol(nCol); + if (pClipDoc->IsMerged(aTestPos)) + // We don't handle merged source cell for this. + return false; + } + + ScTable* pSrcTab = pClipDoc->FetchTable(aSrcPos.Tab()); + if (!pSrcTab) + return false; + + rCxt.setSingleCellColumnSize(nSrcColSize); + + for (SCCOL nColOffset = 0; nColOffset < nSrcColSize; ++nColOffset, aSrcPos.IncCol()) + { + const ScPatternAttr* pAttr = pClipDoc->GetPattern(aSrcPos); + rCxt.setSingleCellPattern(nColOffset, pAttr); + + if ((rCxt.getInsertFlag() & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE) + rCxt.setSingleCellNote(nColOffset, pClipDoc->GetNote(aSrcPos)); + + if ((rCxt.getInsertFlag() & InsertDeleteFlags::SPARKLINES) != InsertDeleteFlags::NONE) + rCxt.setSingleSparkline(nColOffset, pClipDoc->GetSparkline(aSrcPos)); + + ScColumn* pSrcCol = pSrcTab->FetchColumn(aSrcPos.Col()); + assert(pSrcCol); + // Determine the script type of the copied single cell. + pSrcCol->UpdateScriptTypes(aSrcPos.Row(), aSrcPos.Row()); + rCxt.setSingleCell(aSrcPos, *pSrcCol); + } + + // All good. Proceed with the pasting. + + SCTAB nTabEnd = rCxt.getTabEnd(); + for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); ++i) + { + maTabs[i]->CopyOneCellFromClip(rCxt, nCol1, nRow1, nCol2, nRow2, aClipRange.aStart.Row(), pSrcTab); + } + + sc::RefUpdateContext aRefCxt(*this); + aRefCxt.maRange = ScRange(nCol1, nRow1, rCxt.getTabStart(), nCol2, nRow2, nTabEnd); + aRefCxt.mnColDelta = nCol1 - aSrcPos.Col(); + aRefCxt.mnRowDelta = nRow1 - aSrcPos.Row(); + aRefCxt.mnTabDelta = rCxt.getTabStart() - aSrcPos.Tab(); + // Only Copy&Paste, for Cut&Paste we already bailed out early. + aRefCxt.meMode = URM_COPY; + UpdateReference(aRefCxt, rCxt.getUndoDoc(), false); + + return true; +} + +void ScDocument::SetValues( const ScAddress& rPos, const std::vector<double>& rVals ) +{ + ScTable* pTab = FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->SetValues(rPos.Col(), rPos.Row(), rVals); +} + +void ScDocument::TransferCellValuesTo( const ScAddress& rTopPos, size_t nLen, sc::CellValues& rDest ) +{ + ScTable* pTab = FetchTable(rTopPos.Tab()); + if (!pTab) + return; + + pTab->TransferCellValuesTo(rTopPos.Col(), rTopPos.Row(), nLen, rDest); +} + +void ScDocument::CopyCellValuesFrom( const ScAddress& rTopPos, const sc::CellValues& rSrc ) +{ + ScTable* pTab = FetchTable(rTopPos.Tab()); + if (!pTab) + return; + + pTab->CopyCellValuesFrom(rTopPos.Col(), rTopPos.Row(), rSrc); +} + +std::set<Color> ScDocument::GetDocColors() +{ + std::set<Color> aDocColors; + ScDocumentPool *pPool = GetPool(); + const sal_uInt16 pAttribs[] = {ATTR_BACKGROUND, ATTR_FONT_COLOR}; + for (sal_uInt16 nAttrib : pAttribs) + { + for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(nAttrib)) + { + const SvxColorItem *pColorItem = static_cast<const SvxColorItem*>(pItem); + Color aColor( pColorItem->GetValue() ); + if (COL_AUTO != aColor) + aDocColors.insert(aColor); + } + } + return aDocColors; +} + +void ScDocument::SetCalcConfig( const ScCalcConfig& rConfig ) +{ + ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE); + maCalcConfig = rConfig; +} + +void ScDocument::ConvertFormulaToValue( const ScRange& rRange, sc::TableValues* pUndo ) +{ + sc::EndListeningContext aCxt(*this); + + for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab) + { + ScTable* pTab = FetchTable(nTab); + if (!pTab) + continue; + + pTab->ConvertFormulaToValue( + aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), + pUndo); + } + + aCxt.purgeEmptyBroadcasters(); +} + +void ScDocument::SwapNonEmpty( sc::TableValues& rValues ) +{ + const ScRange& rRange = rValues.getRange(); + if (!rRange.IsValid()) + return; + + auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this); + sc::StartListeningContext aStartCxt(*this, pPosSet); + sc::EndListeningContext aEndCxt(*this, pPosSet); + + for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab) + { + ScTable* pTab = FetchTable(nTab); + if (!pTab) + continue; + + pTab->SwapNonEmpty(rValues, aStartCxt, aEndCxt); + } + + aEndCxt.purgeEmptyBroadcasters(); +} + +void ScDocument::PreprocessAllRangeNamesUpdate( const std::map<OUString, std::unique_ptr<ScRangeName>>& rRangeMap ) +{ + // Update all existing names with new names. + // The prerequisites are that the name dialog preserves ScRangeData index + // for changes and does not reuse free index slots for new names. + // ScDocument::SetAllRangeNames() hereafter then will replace the + // ScRangeName containers of ScRangeData instances with empty + // ScRangeData::maNewName. + std::map<OUString, ScRangeName*> aRangeNameMap; + GetRangeNameMap( aRangeNameMap); + for (const auto& itTab : aRangeNameMap) + { + ScRangeName* pOldRangeNames = itTab.second; + if (!pOldRangeNames) + continue; + + const auto& itNewTab( rRangeMap.find( itTab.first)); + if (itNewTab == rRangeMap.end()) + continue; + + const ScRangeName* pNewRangeNames = itNewTab->second.get(); + if (!pNewRangeNames) + continue; + + for (const auto& rEntry : *pOldRangeNames) + { + ScRangeData* pOldData = rEntry.second.get(); + if (!pOldData) + continue; + + const ScRangeData* pNewData = pNewRangeNames->findByIndex( pOldData->GetIndex()); + if (pNewData) + pOldData->SetNewName( pNewData->GetName()); + } + } + + sc::EndListeningContext aEndListenCxt(*this); + sc::CompileFormulaContext aCompileCxt(*this); + + for (const auto& rxTab : maTabs) + { + ScTable* p = rxTab.get(); + p->PreprocessRangeNameUpdate(aEndListenCxt, aCompileCxt); + } +} + +void ScDocument::PreprocessRangeNameUpdate() +{ + sc::EndListeningContext aEndListenCxt(*this); + sc::CompileFormulaContext aCompileCxt(*this); + + for (const auto& rxTab : maTabs) + { + ScTable* p = rxTab.get(); + p->PreprocessRangeNameUpdate(aEndListenCxt, aCompileCxt); + } +} + +void ScDocument::PreprocessDBDataUpdate() +{ + sc::EndListeningContext aEndListenCxt(*this); + sc::CompileFormulaContext aCompileCxt(*this); + + for (const auto& rxTab : maTabs) + { + ScTable* p = rxTab.get(); + p->PreprocessDBDataUpdate(aEndListenCxt, aCompileCxt); + } +} + +void ScDocument::CompileHybridFormula() +{ + sc::StartListeningContext aStartListenCxt(*this); + sc::CompileFormulaContext aCompileCxt(*this); + for (const auto& rxTab : maTabs) + { + ScTable* p = rxTab.get(); + p->CompileHybridFormula(aStartListenCxt, aCompileCxt); + } +} + +void ScDocument::SharePooledResources( const ScDocument* pSrcDoc ) +{ + ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE); + mxPoolHelper = pSrcDoc->mxPoolHelper; + mpCellStringPool = pSrcDoc->mpCellStringPool; +} + +void ScDocument::UpdateScriptTypes( const ScAddress& rPos, SCCOL nColSize, SCROW nRowSize ) +{ + ScTable* pTab = FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->UpdateScriptTypes(rPos.Col(), rPos.Row(), rPos.Col()+nColSize-1, rPos.Row()+nRowSize-1); +} + +bool ScDocument::HasUniformRowHeight( SCTAB nTab, SCROW nRow1, SCROW nRow2 ) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return false; + + return pTab->HasUniformRowHeight(nRow1, nRow2); +} + +void ScDocument::UnshareFormulaCells( SCTAB nTab, SCCOL nCol, std::vector<SCROW>& rRows ) +{ + ScTable* pTab = FetchTable(nTab); + if (!pTab) + return; + + pTab->UnshareFormulaCells(nCol, rRows); +} + +void ScDocument::RegroupFormulaCells( SCTAB nTab, SCCOL nCol ) +{ + ScTable* pTab = FetchTable(nTab); + if (!pTab) + return; + + pTab->RegroupFormulaCells(nCol); +} + +void ScDocument::RegroupFormulaCells( const ScRange& rRange ) +{ + for( SCTAB tab = rRange.aStart.Tab(); tab <= rRange.aEnd.Tab(); ++tab ) + for( SCCOL col = rRange.aStart.Col(); col <= rRange.aEnd.Col(); ++col ) + RegroupFormulaCells( tab, col ); +} + +void ScDocument::DelayFormulaGrouping( bool delay ) +{ + if( delay ) + { + if( !pDelayedFormulaGrouping ) + pDelayedFormulaGrouping.reset( new ScRange( ScAddress::INITIALIZE_INVALID )); + } + else + { + if( pDelayedFormulaGrouping && pDelayedFormulaGrouping->IsValid()) + RegroupFormulaCells( *pDelayedFormulaGrouping ); + pDelayedFormulaGrouping.reset(); + } +} + +void ScDocument::AddDelayedFormulaGroupingCell( const ScFormulaCell* cell ) +{ + if( !pDelayedFormulaGrouping->Contains( cell->aPos )) + pDelayedFormulaGrouping->ExtendTo( cell->aPos ); +} + +void ScDocument::EnableDelayStartListeningFormulaCells( ScColumn* column, bool delay ) +{ + if( delay ) + { + if( pDelayedStartListeningFormulaCells.find( column ) == pDelayedStartListeningFormulaCells.end()) + pDelayedStartListeningFormulaCells[ column ] = std::pair<SCROW, SCROW>( -1, -1 ); + } + else + { + auto it = pDelayedStartListeningFormulaCells.find( column ); + if( it != pDelayedStartListeningFormulaCells.end()) + { + if( it->second.first != -1 ) + { + auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this); + sc::StartListeningContext aStartCxt(*this, pPosSet); + sc::EndListeningContext aEndCxt(*this, pPosSet); + column->StartListeningFormulaCells(aStartCxt, aEndCxt, it->second.first, it->second.second); + } + pDelayedStartListeningFormulaCells.erase( it ); + } + } +} + +bool ScDocument::IsEnabledDelayStartListeningFormulaCells( ScColumn* column ) const +{ + return pDelayedStartListeningFormulaCells.find( column ) != pDelayedStartListeningFormulaCells.end(); +} + +bool ScDocument::CanDelayStartListeningFormulaCells( ScColumn* column, SCROW row1, SCROW row2 ) +{ + auto it = pDelayedStartListeningFormulaCells.find( column ); + if( it == pDelayedStartListeningFormulaCells.end()) + return false; // not enabled + if( it->second.first == -1 && it->second.second == -1 ) // uninitialized + pDelayedStartListeningFormulaCells[ column ] = std::make_pair( row1, row2 ); + else + { + if( row1 > it->second.second + 1 || row2 < it->second.first - 1 ) + { // two non-adjacent ranges, just bail out + return false; + } + it->second.first = std::min( it->second.first, row1 ); + it->second.second = std::max( it->second.second, row2 ); + } + return true; +} + +void ScDocument::EnableDelayDeletingBroadcasters( bool set ) +{ + if( bDelayedDeletingBroadcasters == set ) + return; + bDelayedDeletingBroadcasters = set; + if( !bDelayedDeletingBroadcasters ) + { + for (auto& rxTab : maTabs) + if (rxTab) + rxTab->DeleteEmptyBroadcasters(); + } +} + +bool ScDocument::HasFormulaCell( const ScRange& rRange ) const +{ + if (!rRange.IsValid()) + return false; + + for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab) + { + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + continue; + + if (pTab->HasFormulaCell(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row())) + return true; + } + + return false; +} + +void ScDocument::EndListeningIntersectedGroup( + sc::EndListeningContext& rCxt, const ScAddress& rPos, std::vector<ScAddress>* pGroupPos ) +{ + ScTable* pTab = FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->EndListeningIntersectedGroup(rCxt, rPos.Col(), rPos.Row(), pGroupPos); +} + +void ScDocument::EndListeningIntersectedGroups( + sc::EndListeningContext& rCxt, const ScRange& rRange, std::vector<ScAddress>* pGroupPos ) +{ + for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab) + { + ScTable* pTab = FetchTable(nTab); + if (!pTab) + continue; + + pTab->EndListeningIntersectedGroups( + rCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), + pGroupPos); + } +} + +void ScDocument::EndListeningGroups( const std::vector<ScAddress>& rPosArray ) +{ + sc::EndListeningContext aCxt(*this); + for (const ScAddress& rPos : rPosArray) + { + ScTable* pTab = FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->EndListeningGroup(aCxt, rPos.Col(), rPos.Row()); + } + + aCxt.purgeEmptyBroadcasters(); +} + +void ScDocument::SetNeedsListeningGroups( const std::vector<ScAddress>& rPosArray ) +{ + for (const ScAddress& rPos : rPosArray) + { + ScTable* pTab = FetchTable(rPos.Tab()); + if (!pTab) + return; + + pTab->SetNeedsListeningGroup(rPos.Col(), rPos.Row()); + } +} + +namespace { + +class StartNeededListenersHandler +{ + std::shared_ptr<sc::StartListeningContext> mpCxt; +public: + explicit StartNeededListenersHandler( ScDocument& rDoc ) : mpCxt(std::make_shared<sc::StartListeningContext>(rDoc)) {} + explicit StartNeededListenersHandler( ScDocument& rDoc, const std::shared_ptr<const sc::ColumnSet>& rpColSet ) : + mpCxt(std::make_shared<sc::StartListeningContext>(rDoc)) + { + mpCxt->setColumnSet( rpColSet); + } + + void operator() (const ScTableUniquePtr & p) + { + if (p) + p->StartListeners(*mpCxt, false); + } +}; + +} + +void ScDocument::StartNeededListeners() +{ + std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this)); +} + +void ScDocument::StartNeededListeners( const std::shared_ptr<const sc::ColumnSet>& rpColSet ) +{ + std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this, rpColSet)); +} + +void ScDocument::StartAllListeners( const ScRange& rRange ) +{ + if (IsClipOrUndo() || GetNoListening()) + return; + + auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this); + sc::StartListeningContext aStartCxt(*this, pPosSet); + sc::EndListeningContext aEndCxt(*this, pPosSet); + + for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab) + { + ScTable* pTab = FetchTable(nTab); + if (!pTab) + continue; + + pTab->StartListeningFormulaCells( + aStartCxt, aEndCxt, + rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()); + } +} + +void ScDocument::finalizeOutlineImport() +{ + for (const auto& rxTab : maTabs) + { + ScTable* p = rxTab.get(); + p->finalizeOutlineImport(); + } +} + +bool ScDocument::FindRangeNamesReferencingSheet( sc::UpdatedRangeNames& rIndexes, + SCTAB nTokenTab, const sal_uInt16 nTokenIndex, + SCTAB nGlobalRefTab, SCTAB nLocalRefTab, SCTAB nOldTokenTab, SCTAB nOldTokenTabReplacement, + bool bSameDoc, int nRecursion) const +{ + if (nTokenTab < -1) + { + SAL_WARN("sc.core", "ScDocument::FindRangeNamesReferencingSheet - nTokenTab < -1 : " << + nTokenTab << ", nTokenIndex " << nTokenIndex << " Fix the creator!"); +#if OSL_DEBUG_LEVEL > 0 + const ScRangeData* pData = FindRangeNameBySheetAndIndex( nTokenTab, nTokenIndex); + SAL_WARN_IF( pData, "sc.core", "ScDocument::FindRangeNamesReferencingSheet - named expression is: " << pData->GetName()); +#endif + nTokenTab = -1; + } + SCTAB nRefTab = nGlobalRefTab; + if (nTokenTab == nOldTokenTab) + { + nTokenTab = nOldTokenTabReplacement; + nRefTab = nLocalRefTab; + } + else if (nTokenTab == nOldTokenTabReplacement) + { + nRefTab = nLocalRefTab; + } + + if (rIndexes.isNameUpdated( nTokenTab, nTokenIndex)) + return true; + + ScRangeData* pData = FindRangeNameBySheetAndIndex( nTokenTab, nTokenIndex); + if (!pData) + return false; + + ScTokenArray* pCode = pData->GetCode(); + if (!pCode) + return false; + + bool bRef = !bSameDoc; // include every name used when copying to other doc + if (nRecursion < 126) // whatever... 42*3 + { + formula::FormulaTokenArrayPlainIterator aIter(*pCode); + for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next()) + { + if (p->GetOpCode() == ocName) + { + bRef |= FindRangeNamesReferencingSheet( rIndexes, p->GetSheet(), p->GetIndex(), + nGlobalRefTab, nLocalRefTab, nOldTokenTab, nOldTokenTabReplacement, bSameDoc, nRecursion+1); + } + } + } + + if (!bRef) + { + SCTAB nPosTab = pData->GetPos().Tab(); + if (nPosTab == nOldTokenTab) + nPosTab = nOldTokenTabReplacement; + bRef = pCode->ReferencesSheet( nRefTab, nPosTab); + } + if (bRef) + rIndexes.setUpdatedName( nTokenTab, nTokenIndex); + + return bRef; +} + +namespace { + +enum MightReferenceSheet +{ + UNKNOWN, + NONE, + CODE, + NAME +}; + +MightReferenceSheet mightRangeNameReferenceSheet( ScRangeData* pData, SCTAB nRefTab) +{ + ScTokenArray* pCode = pData->GetCode(); + if (!pCode) + return MightReferenceSheet::NONE; + + formula::FormulaTokenArrayPlainIterator aIter(*pCode); + for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next()) + { + if (p->GetOpCode() == ocName) + return MightReferenceSheet::NAME; + } + + return pCode->ReferencesSheet( nRefTab, pData->GetPos().Tab()) ? + MightReferenceSheet::CODE : MightReferenceSheet::NONE; +} + +ScRangeData* copyRangeName( const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const ScDocument& rOldDoc, + const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal, + SCTAB nOldSheet, const SCTAB nNewSheet, bool bSameDoc) +{ + ScAddress aRangePos( pOldRangeData->GetPos()); + if (nNewSheet >= 0) + aRangePos.SetTab( nNewSheet); + ScRangeData* pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc, &aRangePos); + pRangeData->SetIndex(0); // needed for insert to assign a new index + ScTokenArray* pRangeNameToken = pRangeData->GetCode(); + if (bSameDoc && nNewSheet >= 0) + { + if (bGlobalNamesToLocal && nOldSheet < 0) + { + nOldSheet = rOldPos.Tab(); + if (rNewPos.Tab() <= nOldSheet) + // Sheet was inserted before and references already updated. + ++nOldSheet; + } + pRangeNameToken->AdjustSheetLocalNameReferences( nOldSheet, nNewSheet); + } + if (!bSameDoc) + { + pRangeNameToken->ReadjustAbsolute3DReferences(rOldDoc, rNewDoc, pRangeData->GetPos(), true); + pRangeNameToken->AdjustAbsoluteRefs(rOldDoc, rOldPos, rNewPos, true); + } + + bool bInserted; + if (nNewSheet < 0) + bInserted = rNewDoc.GetRangeName()->insert(pRangeData); + else + bInserted = rNewDoc.GetRangeName(nNewSheet)->insert(pRangeData); + + return bInserted ? pRangeData : nullptr; +} + +struct SheetIndex +{ + SCTAB mnSheet; + sal_uInt16 mnIndex; + + SheetIndex( SCTAB nSheet, sal_uInt16 nIndex ) : mnSheet(nSheet < -1 ? -1 : nSheet), mnIndex(nIndex) {} + bool operator<( const SheetIndex& r ) const + { + // Ascending order sheet, index + if (mnSheet < r.mnSheet) + return true; + if (mnSheet == r.mnSheet) + return mnIndex < r.mnIndex; + return false; + } +}; +typedef std::map< SheetIndex, SheetIndex > SheetIndexMap; + +ScRangeData* copyRangeNames( SheetIndexMap& rSheetIndexMap, std::vector<ScRangeData*>& rRangeDataVec, + const sc::UpdatedRangeNames& rReferencingNames, SCTAB nTab, + const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const ScDocument& rOldDoc, + const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal, + const SCTAB nOldSheet, const SCTAB nNewSheet, bool bSameDoc) +{ + ScRangeData* pRangeData = nullptr; + const ScRangeName* pOldRangeName = (nTab < 0 ? rOldDoc.GetRangeName() : rOldDoc.GetRangeName(nTab)); + if (pOldRangeName) + { + const ScRangeName* pNewRangeName = (nNewSheet < 0 ? rNewDoc.GetRangeName() : rNewDoc.GetRangeName(nNewSheet)); + sc::UpdatedRangeNames::NameIndicesType aSet( rReferencingNames.getUpdatedNames(nTab)); + for (auto const & rIndex : aSet) + { + const ScRangeData* pCopyData = pOldRangeName->findByIndex(rIndex); + if (pCopyData) + { + // Match the original pOldRangeData to adapt the current + // token's values later. For that no check for an already + // copied name is needed as we only enter here if there was + // none. + if (pCopyData == pOldRangeData) + { + pRangeData = copyRangeName( pCopyData, rNewDoc, rOldDoc, rNewPos, rOldPos, + bGlobalNamesToLocal, nOldSheet, nNewSheet, bSameDoc); + if (pRangeData) + { + rRangeDataVec.push_back(pRangeData); + rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()), + SheetIndex( nNewSheet, pRangeData->GetIndex()))); + } + } + else + { + // First check if the name is already available as copy. + const ScRangeData* pFoundData = pNewRangeName->findByUpperName( pCopyData->GetUpperName()); + if (pFoundData) + { + // Just add the resulting sheet/index mapping. + rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()), + SheetIndex( nNewSheet, pFoundData->GetIndex()))); + } + else + { + ScRangeData* pTmpData = copyRangeName( pCopyData, rNewDoc, rOldDoc, rNewPos, rOldPos, + bGlobalNamesToLocal, nOldSheet, nNewSheet, bSameDoc); + if (pTmpData) + { + rRangeDataVec.push_back(pTmpData); + rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()), + SheetIndex( nNewSheet, pTmpData->GetIndex()))); + } + } + } + } + } + } + return pRangeData; +} + +} // namespace + +bool ScDocument::CopyAdjustRangeName( SCTAB& rSheet, sal_uInt16& rIndex, ScRangeData*& rpRangeData, + ScDocument& rNewDoc, const ScAddress& rNewPos, const ScAddress& rOldPos, const bool bGlobalNamesToLocal, + const bool bUsedByFormula ) const +{ + ScDocument* pThis = const_cast<ScDocument*>(this); + const bool bSameDoc = (rNewDoc.GetPool() == pThis->GetPool()); + if (bSameDoc && ((rSheet < 0 && !bGlobalNamesToLocal) || (rSheet >= 0 + && (rSheet != rOldPos.Tab() || (IsClipboard() && pThis->IsCutMode()))))) + // Same doc and global name, if not copied to local name, or + // sheet-local name on other sheet stays the same. Sheet-local on + // same sheet also in a clipboard cut&paste / move operation. + return false; + + // Ensure we don't fiddle with the references until exit. + const SCTAB nOldSheet = rSheet; + const sal_uInt16 nOldIndex = rIndex; + + SAL_WARN_IF( !bSameDoc && nOldSheet >= 0 && nOldSheet != rOldPos.Tab(), + "sc.core", "adjustCopyRangeName - sheet-local name was on other sheet in other document"); + /* TODO: can we do something about that? e.g. loop over sheets? */ + + OUString aRangeName; + ScRangeData* pOldRangeData = nullptr; + + // XXX bGlobalNamesToLocal is also a synonym for copied sheet. + bool bInsertingBefore = (bGlobalNamesToLocal && bSameDoc && rNewPos.Tab() <= rOldPos.Tab()); + + // The Tab where an old local name is to be found or that a global name + // references. May differ below from nOldSheet if a sheet was inserted + // before the old position. Global names and local names other than on the + // old sheet or new sheet are already updated, local names on the old sheet + // or inserted sheet will be updated later. Confusing stuff. Watch out. + SCTAB nOldTab = (nOldSheet < 0 ? rOldPos.Tab() : nOldSheet); + if (bInsertingBefore) + // Sheet was already inserted before old position. + ++nOldTab; + + // Search the name of the RangeName. + if (nOldSheet >= 0) + { + const ScRangeName* pNames = GetRangeName(nOldTab); + pOldRangeData = pNames ? pNames->findByIndex(nOldIndex) : nullptr; + if (!pOldRangeData) + return false; // might be an error in the formula array + aRangeName = pOldRangeData->GetUpperName(); + } + else + { + pOldRangeData = GetRangeName()->findByIndex(nOldIndex); + if (!pOldRangeData) + return false; // might be an error in the formula array + aRangeName = pOldRangeData->GetUpperName(); + } + + // Find corresponding range name in new document. + // First search for local range name then global range names. + SCTAB nNewSheet = rNewPos.Tab(); + ScRangeName* pNewNames = rNewDoc.GetRangeName(nNewSheet); + // Search local range names. + if (pNewNames) + { + rpRangeData = pNewNames->findByUpperName(aRangeName); + } + // Search global range names. + if (!rpRangeData && !bGlobalNamesToLocal) + { + nNewSheet = -1; + pNewNames = rNewDoc.GetRangeName(); + if (pNewNames) + rpRangeData = pNewNames->findByUpperName(aRangeName); + } + // If no range name was found copy it. + if (!rpRangeData) + { + // Do not copy global name if it doesn't reference sheet or is not used + // by a formula copied to another document. + bool bEarlyBailOut = (nOldSheet < 0 && (bSameDoc || !bUsedByFormula)); + MightReferenceSheet eMightReference = mightRangeNameReferenceSheet( pOldRangeData, nOldTab); + if (bEarlyBailOut && eMightReference == MightReferenceSheet::NONE) + return false; + + if (eMightReference == MightReferenceSheet::NAME) + { + // Name these to clarify what is passed where. + const SCTAB nGlobalRefTab = nOldTab; + const SCTAB nLocalRefTab = (bInsertingBefore ? nOldTab-1 : nOldTab); + const SCTAB nOldTokenTab = (nOldSheet < 0 ? (bInsertingBefore ? nOldTab-1 : nOldTab) : nOldSheet); + const SCTAB nOldTokenTabReplacement = nOldTab; + sc::UpdatedRangeNames aReferencingNames; + FindRangeNamesReferencingSheet( aReferencingNames, nOldSheet, nOldIndex, + nGlobalRefTab, nLocalRefTab, nOldTokenTab, nOldTokenTabReplacement, bSameDoc, 0); + if (bEarlyBailOut && aReferencingNames.isEmpty(-1) && aReferencingNames.isEmpty(nOldTokenTabReplacement)) + return false; + + SheetIndexMap aSheetIndexMap; + std::vector<ScRangeData*> aRangeDataVec; + if (!aReferencingNames.isEmpty(nOldTokenTabReplacement)) + { + const SCTAB nTmpOldSheet = (nOldSheet < 0 ? nOldTab : nOldSheet); + nNewSheet = rNewPos.Tab(); + rpRangeData = copyRangeNames( aSheetIndexMap, aRangeDataVec, aReferencingNames, nOldTab, + pOldRangeData, rNewDoc, *this, rNewPos, rOldPos, + bGlobalNamesToLocal, nTmpOldSheet, nNewSheet, bSameDoc); + } + if ((bGlobalNamesToLocal || !bSameDoc) && !aReferencingNames.isEmpty(-1)) + { + const SCTAB nTmpOldSheet = -1; + const SCTAB nTmpNewSheet = (bGlobalNamesToLocal ? rNewPos.Tab() : -1); + ScRangeData* pTmpData = copyRangeNames( aSheetIndexMap, aRangeDataVec, aReferencingNames, -1, + pOldRangeData, rNewDoc, *this, rNewPos, rOldPos, + bGlobalNamesToLocal, nTmpOldSheet, nTmpNewSheet, bSameDoc); + if (!rpRangeData) + { + rpRangeData = pTmpData; + nNewSheet = nTmpNewSheet; + } + } + + // Adjust copied nested names to new sheet/index. + for (auto & iRD : aRangeDataVec) + { + ScTokenArray* pCode = iRD->GetCode(); + if (pCode) + { + formula::FormulaTokenArrayPlainIterator aIter(*pCode); + for (formula::FormulaToken* p = aIter.First(); p; p = aIter.Next()) + { + if (p->GetOpCode() == ocName) + { + auto it = aSheetIndexMap.find( SheetIndex( p->GetSheet(), p->GetIndex())); + if (it != aSheetIndexMap.end()) + { + p->SetSheet( it->second.mnSheet); + p->SetIndex( it->second.mnIndex); + } + else if (!bSameDoc) + { + SAL_WARN("sc.core","adjustCopyRangeName - mapping to new name in other doc missing"); + p->SetIndex(0); // #NAME? error instead of arbitrary name. + } + } + } + } + } + } + else + { + nNewSheet = ((nOldSheet < 0 && !bGlobalNamesToLocal) ? -1 : rNewPos.Tab()); + rpRangeData = copyRangeName( pOldRangeData, rNewDoc, *this, rNewPos, rOldPos, bGlobalNamesToLocal, + nOldSheet, nNewSheet, bSameDoc); + } + + if (rpRangeData && !rNewDoc.IsClipOrUndo()) + { + ScDocShell* pDocSh = static_cast<ScDocShell*>(rNewDoc.GetDocumentShell()); + if (pDocSh) + pDocSh->SetAreasChangedNeedBroadcast(); + } + } + + rSheet = nNewSheet; + rIndex = rpRangeData ? rpRangeData->GetIndex() : 0; // 0 means not inserted + return true; +} + +bool ScDocument::IsEditActionAllowed( + sc::ColRowEditAction eAction, SCTAB nTab, SCCOLROW nStart, SCCOLROW nEnd ) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return false; + + return pTab->IsEditActionAllowed(eAction, nStart, nEnd); +} + +bool ScDocument::IsEditActionAllowed( + sc::ColRowEditAction eAction, const ScMarkData& rMark, SCCOLROW nStart, SCCOLROW nEnd ) const +{ + return std::all_of(rMark.begin(), rMark.end(), + [this, &eAction, &nStart, &nEnd](const SCTAB& rTab) { return IsEditActionAllowed(eAction, rTab, nStart, nEnd); }); +} + +std::optional<sc::ColumnIterator> ScDocument::GetColumnIterator( SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2 ) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return {}; + + return pTab->GetColumnIterator(nCol, nRow1, nRow2); +} + +void ScDocument::CreateColumnIfNotExists( SCTAB nTab, SCCOL nCol ) +{ + ScTable* pTab = FetchTable(nTab); + if (!pTab) + return; + + pTab->CreateColumnIfNotExists(nCol); +} + +bool ScDocument::EnsureFormulaCellResults( const ScRange& rRange, bool bSkipRunning ) +{ + bool bAnyDirty = false; + for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab) + { + ScTable* pTab = FetchTable(nTab); + if (!pTab) + continue; + + bool bRet = pTab->EnsureFormulaCellResults( + rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), bSkipRunning); + bAnyDirty = bAnyDirty || bRet; + } + + return bAnyDirty; +} + +sc::ExternalDataMapper& ScDocument::GetExternalDataMapper() +{ + if (!mpDataMapper) + mpDataMapper.reset(new sc::ExternalDataMapper(*this)); + + return *mpDataMapper; +} + +void ScDocument::StoreTabToCache(SCTAB nTab, SvStream& rStrm) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return; + + pTab->StoreToCache(rStrm); +} + +void ScDocument::RestoreTabFromCache(SCTAB nTab, SvStream& rStrm) +{ + ScTable* pTab = FetchTable(nTab); + if (!pTab) + return; + + pTab->RestoreFromCache(rStrm); +} + +OString ScDocument::dumpSheetGeomData(SCTAB nTab, bool bColumns, SheetGeomType eGeomType) +{ + ScTable* pTab = FetchTable(nTab); + if (!pTab) + return ""; + + return pTab->dumpSheetGeomData(bColumns, eGeomType); +} + +SCCOL ScDocument::GetLOKFreezeCol(SCTAB nTab) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return -1; + + return pTab->GetLOKFreezeCol(); +} +SCROW ScDocument::GetLOKFreezeRow(SCTAB nTab) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return -1; + + return pTab->GetLOKFreezeRow(); +} + +bool ScDocument::SetLOKFreezeCol(SCCOL nFreezeCol, SCTAB nTab) +{ + ScTable* pTab = FetchTable(nTab); + if (!pTab) + return false; + + return pTab->SetLOKFreezeCol(nFreezeCol); +} + +bool ScDocument::SetLOKFreezeRow(SCROW nFreezeRow, SCTAB nTab) +{ + ScTable* pTab = FetchTable(nTab); + if (!pTab) + return false; + + return pTab->SetLOKFreezeRow(nFreezeRow); +} + +std::set<SCCOL> ScDocument::QueryColumnsWithFormulaCells( SCTAB nTab ) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return std::set<SCCOL>{}; + + return pTab->QueryColumnsWithFormulaCells(); +} + +void ScDocument::CheckIntegrity( SCTAB nTab ) const +{ + const ScTable* pTab = FetchTable(nTab); + if (!pTab) + return; + + pTab->CheckIntegrity(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |