/* -*- 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 <svx/DocumentColorHelper.hxx>
#include <scitems.hxx>
#include <datamapper.hxx>
#include <docsh.hxx>
#include <bcaslot.hxx>
#include <broadcast.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 < GetTableCount(); ++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();
svx::DocumentColorHelper::queryColors<SvxBrushItem>(ATTR_BACKGROUND, pPool, aDocColors);
svx::DocumentColorHelper::queryColors<SvxColorItem>(ATTR_FONT_COLOR, pPool, aDocColors);
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;
const 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, 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& rNewRangeNames = itNewTab->second;
for (const auto& rEntry : *pOldRangeNames)
{
ScRangeData* pOldData = rEntry.second.get();
if (!pOldData)
continue;
const ScRangeData* pNewData = rNewRangeNames.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;
// force lazy creation/existence in source document *before* sharing
pSrcDoc->getCellAttributeHelper();
mpCellAttributeHelper = pSrcDoc->mpCellAttributeHelper;
}
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( ScRange(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 )
{
const 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;
const 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 = 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 ""_ostr;
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();
}
sc::BroadcasterState ScDocument::GetBroadcasterState() const
{
sc::BroadcasterState aState;
for (const auto& xTab : maTabs)
xTab->CollectBroadcasterState(aState);
if (pBASM)
pBASM->CollectBroadcasterState(aState);
return aState;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V1029 Numeric Truncation Error. Return value of the 'size' function is written to the 16-bit variable.