tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / sc / source / core / data / document10.cxx
blob4c6527304a7ea6c5cc02e8d0dab2ad08c2b26e95
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <memory>
11 #include <document.hxx>
12 #include <clipcontext.hxx>
13 #include <clipparam.hxx>
14 #include <table.hxx>
15 #include <tokenarray.hxx>
16 #include <listenercontext.hxx>
17 #include <tokenstringcontext.hxx>
18 #include <poolhelp.hxx>
19 #include <cellvalues.hxx>
20 #include <docpool.hxx>
21 #include <columniterator.hxx>
23 #include <refupdatecontext.hxx>
24 #include <sal/log.hxx>
25 #include <svx/DocumentColorHelper.hxx>
26 #include <scitems.hxx>
27 #include <datamapper.hxx>
28 #include <docsh.hxx>
29 #include <bcaslot.hxx>
30 #include <broadcast.hxx>
32 // Add totally brand-new methods to this source file.
34 bool ScDocument::IsMerged( const ScAddress& rPos ) const
36 const ScTable* pTab = FetchTable(rPos.Tab());
37 if (!pTab)
38 return false;
40 return pTab->IsMerged(rPos.Col(), rPos.Row());
43 sc::MultiDataCellState ScDocument::HasMultipleDataCells( const ScRange& rRange ) const
45 if (rRange.aStart.Tab() != rRange.aEnd.Tab())
46 // Currently we only support a single-sheet range.
47 return sc::MultiDataCellState();
49 const ScTable* pTab = FetchTable(rRange.aStart.Tab());
50 if (!pTab)
51 return sc::MultiDataCellState(sc::MultiDataCellState::Empty);
53 const ScAddress& s = rRange.aStart;
54 const ScAddress& e = rRange.aEnd;
55 return pTab->HasMultipleDataCells(s.Col(), s.Row(), e.Col(), e.Row());
58 void ScDocument::DeleteBeforeCopyFromClip(
59 sc::CopyFromClipContext& rCxt, const ScMarkData& rMark, sc::ColumnSpanSet& rBroadcastSpans )
61 SCTAB nClipTab = 0;
62 const TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
63 SCTAB nClipTabCount = rClipTabs.size();
65 for (SCTAB nTab = rCxt.getTabStart(); nTab <= rCxt.getTabEnd(); ++nTab)
67 ScTable* pTab = FetchTable(nTab);
68 if (!pTab)
69 continue;
71 if (!rMark.GetTableSelect(nTab))
72 continue;
74 while (!rClipTabs[nClipTab])
75 nClipTab = (nClipTab+1) % nClipTabCount;
77 pTab->DeleteBeforeCopyFromClip(rCxt, *rClipTabs[nClipTab], rBroadcastSpans);
79 nClipTab = (nClipTab+1) % nClipTabCount;
83 bool ScDocument::CopyOneCellFromClip(
84 sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
86 ScDocument* pClipDoc = rCxt.getClipDoc();
87 if (pClipDoc->GetClipParam().mbCutMode)
88 // We don't handle cut and paste or moving of cells here.
89 return false;
91 ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange();
92 if (aClipRange.aStart.Row() != aClipRange.aEnd.Row())
93 // The source is not really a single row. Bail out.
94 return false;
96 SCCOL nSrcColSize = aClipRange.aEnd.Col() - aClipRange.aStart.Col() + 1;
97 SCCOL nDestColSize = nCol2 - nCol1 + 1;
98 if (nDestColSize < nSrcColSize)
99 return false;
101 if (pClipDoc->maTabs.size() > 1)
102 // Copying from multiple source sheets is not handled here.
103 return false;
105 ScAddress aSrcPos = aClipRange.aStart;
107 for (SCCOL nCol = aClipRange.aStart.Col(); nCol <= aClipRange.aEnd.Col(); ++nCol)
109 ScAddress aTestPos = aSrcPos;
110 aTestPos.SetCol(nCol);
111 if (pClipDoc->IsMerged(aTestPos))
112 // We don't handle merged source cell for this.
113 return false;
116 ScTable* pSrcTab = pClipDoc->FetchTable(aSrcPos.Tab());
117 if (!pSrcTab)
118 return false;
120 rCxt.setSingleCellColumnSize(nSrcColSize);
122 for (SCCOL nColOffset = 0; nColOffset < nSrcColSize; ++nColOffset, aSrcPos.IncCol())
124 const ScPatternAttr* pAttr = pClipDoc->GetPattern(aSrcPos);
125 rCxt.setSingleCellPattern(nColOffset, pAttr);
127 if ((rCxt.getInsertFlag() & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)) != InsertDeleteFlags::NONE)
128 rCxt.setSingleCellNote(nColOffset, pClipDoc->GetNote(aSrcPos));
130 if ((rCxt.getInsertFlag() & InsertDeleteFlags::SPARKLINES) != InsertDeleteFlags::NONE)
131 rCxt.setSingleSparkline(nColOffset, pClipDoc->GetSparkline(aSrcPos));
133 ScColumn* pSrcCol = pSrcTab->FetchColumn(aSrcPos.Col());
134 assert(pSrcCol);
135 // Determine the script type of the copied single cell.
136 pSrcCol->UpdateScriptTypes(aSrcPos.Row(), aSrcPos.Row());
137 rCxt.setSingleCell(aSrcPos, *pSrcCol);
140 // All good. Proceed with the pasting.
142 SCTAB nTabEnd = rCxt.getTabEnd();
143 for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < GetTableCount(); ++i)
145 maTabs[i]->CopyOneCellFromClip(rCxt, nCol1, nRow1, nCol2, nRow2, aClipRange.aStart.Row(), pSrcTab);
148 sc::RefUpdateContext aRefCxt(*this);
149 aRefCxt.maRange = ScRange(nCol1, nRow1, rCxt.getTabStart(), nCol2, nRow2, nTabEnd);
150 aRefCxt.mnColDelta = nCol1 - aSrcPos.Col();
151 aRefCxt.mnRowDelta = nRow1 - aSrcPos.Row();
152 aRefCxt.mnTabDelta = rCxt.getTabStart() - aSrcPos.Tab();
153 // Only Copy&Paste, for Cut&Paste we already bailed out early.
154 aRefCxt.meMode = URM_COPY;
155 UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
157 return true;
160 void ScDocument::SetValues( const ScAddress& rPos, const std::vector<double>& rVals )
162 ScTable* pTab = FetchTable(rPos.Tab());
163 if (!pTab)
164 return;
166 pTab->SetValues(rPos.Col(), rPos.Row(), rVals);
169 void ScDocument::TransferCellValuesTo( const ScAddress& rTopPos, size_t nLen, sc::CellValues& rDest )
171 ScTable* pTab = FetchTable(rTopPos.Tab());
172 if (!pTab)
173 return;
175 pTab->TransferCellValuesTo(rTopPos.Col(), rTopPos.Row(), nLen, rDest);
178 void ScDocument::CopyCellValuesFrom( const ScAddress& rTopPos, const sc::CellValues& rSrc )
180 ScTable* pTab = FetchTable(rTopPos.Tab());
181 if (!pTab)
182 return;
184 pTab->CopyCellValuesFrom(rTopPos.Col(), rTopPos.Row(), rSrc);
187 std::set<Color> ScDocument::GetDocColors()
189 std::set<Color> aDocColors;
190 ScDocumentPool *pPool = GetPool();
192 svx::DocumentColorHelper::queryColors<SvxBrushItem>(ATTR_BACKGROUND, pPool, aDocColors);
193 svx::DocumentColorHelper::queryColors<SvxColorItem>(ATTR_FONT_COLOR, pPool, aDocColors);
195 return aDocColors;
198 void ScDocument::SetCalcConfig( const ScCalcConfig& rConfig )
200 ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
201 maCalcConfig = rConfig;
204 void ScDocument::ConvertFormulaToValue( const ScRange& rRange, sc::TableValues* pUndo )
206 sc::EndListeningContext aCxt(*this);
208 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
210 ScTable* pTab = FetchTable(nTab);
211 if (!pTab)
212 continue;
214 pTab->ConvertFormulaToValue(
215 aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
216 pUndo);
219 aCxt.purgeEmptyBroadcasters();
222 void ScDocument::SwapNonEmpty( sc::TableValues& rValues )
224 const ScRange& rRange = rValues.getRange();
225 if (!rRange.IsValid())
226 return;
228 const auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
229 sc::StartListeningContext aStartCxt(*this, pPosSet);
230 sc::EndListeningContext aEndCxt(*this, pPosSet);
232 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
234 ScTable* pTab = FetchTable(nTab);
235 if (!pTab)
236 continue;
238 pTab->SwapNonEmpty(rValues, aStartCxt, aEndCxt);
241 aEndCxt.purgeEmptyBroadcasters();
244 void ScDocument::PreprocessAllRangeNamesUpdate( const std::map<OUString, ScRangeName>& rRangeMap )
246 // Update all existing names with new names.
247 // The prerequisites are that the name dialog preserves ScRangeData index
248 // for changes and does not reuse free index slots for new names.
249 // ScDocument::SetAllRangeNames() hereafter then will replace the
250 // ScRangeName containers of ScRangeData instances with empty
251 // ScRangeData::maNewName.
252 std::map<OUString, ScRangeName*> aRangeNameMap;
253 GetRangeNameMap( aRangeNameMap);
254 for (const auto& itTab : aRangeNameMap)
256 ScRangeName* pOldRangeNames = itTab.second;
257 if (!pOldRangeNames)
258 continue;
260 const auto itNewTab( rRangeMap.find( itTab.first));
261 if (itNewTab == rRangeMap.end())
262 continue;
264 const ScRangeName& rNewRangeNames = itNewTab->second;
266 for (const auto& rEntry : *pOldRangeNames)
268 ScRangeData* pOldData = rEntry.second.get();
269 if (!pOldData)
270 continue;
272 const ScRangeData* pNewData = rNewRangeNames.findByIndex( pOldData->GetIndex());
273 if (pNewData)
274 pOldData->SetNewName( pNewData->GetName());
278 sc::EndListeningContext aEndListenCxt(*this);
279 sc::CompileFormulaContext aCompileCxt(*this);
281 for (const auto& rxTab : maTabs)
283 ScTable* p = rxTab.get();
284 p->PreprocessRangeNameUpdate(aEndListenCxt, aCompileCxt);
288 void ScDocument::PreprocessRangeNameUpdate()
290 sc::EndListeningContext aEndListenCxt(*this);
291 sc::CompileFormulaContext aCompileCxt(*this);
293 for (const auto& rxTab : maTabs)
295 ScTable* p = rxTab.get();
296 p->PreprocessRangeNameUpdate(aEndListenCxt, aCompileCxt);
300 void ScDocument::PreprocessDBDataUpdate()
302 sc::EndListeningContext aEndListenCxt(*this);
303 sc::CompileFormulaContext aCompileCxt(*this);
305 for (const auto& rxTab : maTabs)
307 ScTable* p = rxTab.get();
308 p->PreprocessDBDataUpdate(aEndListenCxt, aCompileCxt);
312 void ScDocument::CompileHybridFormula()
314 sc::StartListeningContext aStartListenCxt(*this);
315 sc::CompileFormulaContext aCompileCxt(*this);
316 for (const auto& rxTab : maTabs)
318 ScTable* p = rxTab.get();
319 p->CompileHybridFormula(aStartListenCxt, aCompileCxt);
323 void ScDocument::SharePooledResources( const ScDocument* pSrcDoc )
325 ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
326 mxPoolHelper = pSrcDoc->mxPoolHelper;
327 mpCellStringPool = pSrcDoc->mpCellStringPool;
329 // force lazy creation/existence in source document *before* sharing
330 pSrcDoc->getCellAttributeHelper();
331 mpCellAttributeHelper = pSrcDoc->mpCellAttributeHelper;
334 void ScDocument::UpdateScriptTypes( const ScAddress& rPos, SCCOL nColSize, SCROW nRowSize )
336 ScTable* pTab = FetchTable(rPos.Tab());
337 if (!pTab)
338 return;
340 pTab->UpdateScriptTypes(rPos.Col(), rPos.Row(), rPos.Col()+nColSize-1, rPos.Row()+nRowSize-1);
343 bool ScDocument::HasUniformRowHeight( SCTAB nTab, SCROW nRow1, SCROW nRow2 ) const
345 const ScTable* pTab = FetchTable(nTab);
346 if (!pTab)
347 return false;
349 return pTab->HasUniformRowHeight(nRow1, nRow2);
352 void ScDocument::UnshareFormulaCells( SCTAB nTab, SCCOL nCol, std::vector<SCROW>& rRows )
354 ScTable* pTab = FetchTable(nTab);
355 if (!pTab)
356 return;
358 pTab->UnshareFormulaCells(nCol, rRows);
361 void ScDocument::RegroupFormulaCells( SCTAB nTab, SCCOL nCol )
363 ScTable* pTab = FetchTable(nTab);
364 if (!pTab)
365 return;
367 pTab->RegroupFormulaCells(nCol);
370 void ScDocument::RegroupFormulaCells( const ScRange& rRange )
372 for( SCTAB tab = rRange.aStart.Tab(); tab <= rRange.aEnd.Tab(); ++tab )
373 for( SCCOL col = rRange.aStart.Col(); col <= rRange.aEnd.Col(); ++col )
374 RegroupFormulaCells( tab, col );
377 void ScDocument::DelayFormulaGrouping( bool delay )
379 if( delay )
381 if( !pDelayedFormulaGrouping )
382 pDelayedFormulaGrouping.reset( new ScRange( ScAddress::INITIALIZE_INVALID ));
384 else
386 if( pDelayedFormulaGrouping && pDelayedFormulaGrouping->IsValid())
387 RegroupFormulaCells( *pDelayedFormulaGrouping );
388 pDelayedFormulaGrouping.reset();
392 void ScDocument::AddDelayedFormulaGroupingCell( const ScFormulaCell* cell )
394 if( !pDelayedFormulaGrouping->Contains( cell->aPos ))
395 pDelayedFormulaGrouping->ExtendTo( ScRange(cell->aPos) );
398 void ScDocument::EnableDelayStartListeningFormulaCells( ScColumn* column, bool delay )
400 if( delay )
402 if( pDelayedStartListeningFormulaCells.find( column ) == pDelayedStartListeningFormulaCells.end())
403 pDelayedStartListeningFormulaCells[ column ] = std::pair<SCROW, SCROW>( -1, -1 );
405 else
407 auto it = pDelayedStartListeningFormulaCells.find( column );
408 if( it != pDelayedStartListeningFormulaCells.end())
410 if( it->second.first != -1 )
412 const auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
413 sc::StartListeningContext aStartCxt(*this, pPosSet);
414 sc::EndListeningContext aEndCxt(*this, pPosSet);
415 column->StartListeningFormulaCells(aStartCxt, aEndCxt, it->second.first, it->second.second);
417 pDelayedStartListeningFormulaCells.erase( it );
422 bool ScDocument::IsEnabledDelayStartListeningFormulaCells( ScColumn* column ) const
424 return pDelayedStartListeningFormulaCells.find( column ) != pDelayedStartListeningFormulaCells.end();
427 bool ScDocument::CanDelayStartListeningFormulaCells( ScColumn* column, SCROW row1, SCROW row2 )
429 auto it = pDelayedStartListeningFormulaCells.find( column );
430 if( it == pDelayedStartListeningFormulaCells.end())
431 return false; // not enabled
432 if( it->second.first == -1 && it->second.second == -1 ) // uninitialized
433 pDelayedStartListeningFormulaCells[ column ] = std::make_pair( row1, row2 );
434 else
436 if( row1 > it->second.second + 1 || row2 < it->second.first - 1 )
437 { // two non-adjacent ranges, just bail out
438 return false;
440 it->second.first = std::min( it->second.first, row1 );
441 it->second.second = std::max( it->second.second, row2 );
443 return true;
446 void ScDocument::EnableDelayDeletingBroadcasters( bool set )
448 if( bDelayedDeletingBroadcasters == set )
449 return;
450 bDelayedDeletingBroadcasters = set;
451 if( !bDelayedDeletingBroadcasters )
453 for (auto& rxTab : maTabs)
454 if (rxTab)
455 rxTab->DeleteEmptyBroadcasters();
459 bool ScDocument::HasFormulaCell( const ScRange& rRange ) const
461 if (!rRange.IsValid())
462 return false;
464 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
466 const ScTable* pTab = FetchTable(nTab);
467 if (!pTab)
468 continue;
470 if (pTab->HasFormulaCell(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()))
471 return true;
474 return false;
477 void ScDocument::EndListeningIntersectedGroup(
478 sc::EndListeningContext& rCxt, const ScAddress& rPos, std::vector<ScAddress>* pGroupPos )
480 ScTable* pTab = FetchTable(rPos.Tab());
481 if (!pTab)
482 return;
484 pTab->EndListeningIntersectedGroup(rCxt, rPos.Col(), rPos.Row(), pGroupPos);
487 void ScDocument::EndListeningIntersectedGroups(
488 sc::EndListeningContext& rCxt, const ScRange& rRange, std::vector<ScAddress>* pGroupPos )
490 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
492 ScTable* pTab = FetchTable(nTab);
493 if (!pTab)
494 continue;
496 pTab->EndListeningIntersectedGroups(
497 rCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
498 pGroupPos);
502 void ScDocument::EndListeningGroups( const std::vector<ScAddress>& rPosArray )
504 sc::EndListeningContext aCxt(*this);
505 for (const ScAddress& rPos : rPosArray)
507 ScTable* pTab = FetchTable(rPos.Tab());
508 if (!pTab)
509 return;
511 pTab->EndListeningGroup(aCxt, rPos.Col(), rPos.Row());
514 aCxt.purgeEmptyBroadcasters();
517 void ScDocument::SetNeedsListeningGroups( const std::vector<ScAddress>& rPosArray )
519 for (const ScAddress& rPos : rPosArray)
521 ScTable* pTab = FetchTable(rPos.Tab());
522 if (!pTab)
523 return;
525 pTab->SetNeedsListeningGroup(rPos.Col(), rPos.Row());
529 namespace {
531 class StartNeededListenersHandler
533 std::shared_ptr<sc::StartListeningContext> mpCxt;
534 public:
535 explicit StartNeededListenersHandler( ScDocument& rDoc ) : mpCxt(std::make_shared<sc::StartListeningContext>(rDoc)) {}
536 explicit StartNeededListenersHandler( ScDocument& rDoc, const std::shared_ptr<const sc::ColumnSet>& rpColSet ) :
537 mpCxt(std::make_shared<sc::StartListeningContext>(rDoc))
539 mpCxt->setColumnSet( rpColSet);
542 void operator() (const ScTableUniquePtr & p)
544 if (p)
545 p->StartListeners(*mpCxt, false);
551 void ScDocument::StartNeededListeners()
553 std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this));
556 void ScDocument::StartNeededListeners( const std::shared_ptr<const sc::ColumnSet>& rpColSet )
558 std::for_each(maTabs.begin(), maTabs.end(), StartNeededListenersHandler(*this, rpColSet));
561 void ScDocument::StartAllListeners( const ScRange& rRange )
563 if (IsClipOrUndo() || GetNoListening())
564 return;
566 const auto pPosSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
567 sc::StartListeningContext aStartCxt(*this, pPosSet);
568 sc::EndListeningContext aEndCxt(*this, pPosSet);
570 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
572 ScTable* pTab = FetchTable(nTab);
573 if (!pTab)
574 continue;
576 pTab->StartListeningFormulaCells(
577 aStartCxt, aEndCxt,
578 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
582 void ScDocument::finalizeOutlineImport()
584 for (const auto& rxTab : maTabs)
586 ScTable* p = rxTab.get();
587 p->finalizeOutlineImport();
591 bool ScDocument::FindRangeNamesReferencingSheet( sc::UpdatedRangeNames& rIndexes,
592 SCTAB nTokenTab, const sal_uInt16 nTokenIndex,
593 SCTAB nGlobalRefTab, SCTAB nLocalRefTab, SCTAB nOldTokenTab, SCTAB nOldTokenTabReplacement,
594 bool bSameDoc, int nRecursion) const
596 if (nTokenTab < -1)
598 SAL_WARN("sc.core", "ScDocument::FindRangeNamesReferencingSheet - nTokenTab < -1 : " <<
599 nTokenTab << ", nTokenIndex " << nTokenIndex << " Fix the creator!");
600 #if OSL_DEBUG_LEVEL > 0
601 const ScRangeData* pData = FindRangeNameBySheetAndIndex( nTokenTab, nTokenIndex);
602 SAL_WARN_IF( pData, "sc.core", "ScDocument::FindRangeNamesReferencingSheet - named expression is: " << pData->GetName());
603 #endif
604 nTokenTab = -1;
606 SCTAB nRefTab = nGlobalRefTab;
607 if (nTokenTab == nOldTokenTab)
609 nTokenTab = nOldTokenTabReplacement;
610 nRefTab = nLocalRefTab;
612 else if (nTokenTab == nOldTokenTabReplacement)
614 nRefTab = nLocalRefTab;
617 if (rIndexes.isNameUpdated( nTokenTab, nTokenIndex))
618 return true;
620 ScRangeData* pData = FindRangeNameBySheetAndIndex( nTokenTab, nTokenIndex);
621 if (!pData)
622 return false;
624 ScTokenArray* pCode = pData->GetCode();
625 if (!pCode)
626 return false;
628 bool bRef = !bSameDoc; // include every name used when copying to other doc
629 if (nRecursion < 126) // whatever... 42*3
631 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
632 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
634 if (p->GetOpCode() == ocName)
636 bRef |= FindRangeNamesReferencingSheet( rIndexes, p->GetSheet(), p->GetIndex(),
637 nGlobalRefTab, nLocalRefTab, nOldTokenTab, nOldTokenTabReplacement, bSameDoc, nRecursion+1);
642 if (!bRef)
644 SCTAB nPosTab = pData->GetPos().Tab();
645 if (nPosTab == nOldTokenTab)
646 nPosTab = nOldTokenTabReplacement;
647 bRef = pCode->ReferencesSheet( nRefTab, nPosTab);
649 if (bRef)
650 rIndexes.setUpdatedName( nTokenTab, nTokenIndex);
652 return bRef;
655 namespace {
657 enum MightReferenceSheet
659 UNKNOWN,
660 NONE,
661 CODE,
662 NAME
665 MightReferenceSheet mightRangeNameReferenceSheet( ScRangeData* pData, SCTAB nRefTab)
667 ScTokenArray* pCode = pData->GetCode();
668 if (!pCode)
669 return MightReferenceSheet::NONE;
671 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
672 for (const formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
674 if (p->GetOpCode() == ocName)
675 return MightReferenceSheet::NAME;
678 return pCode->ReferencesSheet( nRefTab, pData->GetPos().Tab()) ?
679 MightReferenceSheet::CODE : MightReferenceSheet::NONE;
682 ScRangeData* copyRangeName( const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const ScDocument& rOldDoc,
683 const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal,
684 SCTAB nOldSheet, const SCTAB nNewSheet, bool bSameDoc)
686 ScAddress aRangePos( pOldRangeData->GetPos());
687 if (nNewSheet >= 0)
688 aRangePos.SetTab( nNewSheet);
689 ScRangeData* pRangeData = new ScRangeData(*pOldRangeData, &rNewDoc, &aRangePos);
690 pRangeData->SetIndex(0); // needed for insert to assign a new index
691 ScTokenArray* pRangeNameToken = pRangeData->GetCode();
692 if (bSameDoc && nNewSheet >= 0)
694 if (bGlobalNamesToLocal && nOldSheet < 0)
696 nOldSheet = rOldPos.Tab();
697 if (rNewPos.Tab() <= nOldSheet)
698 // Sheet was inserted before and references already updated.
699 ++nOldSheet;
701 pRangeNameToken->AdjustSheetLocalNameReferences( nOldSheet, nNewSheet);
703 if (!bSameDoc)
705 pRangeNameToken->ReadjustAbsolute3DReferences(rOldDoc, rNewDoc, pRangeData->GetPos(), true);
706 pRangeNameToken->AdjustAbsoluteRefs(rOldDoc, rOldPos, rNewPos, true);
709 bool bInserted;
710 if (nNewSheet < 0)
711 bInserted = rNewDoc.GetRangeName()->insert(pRangeData);
712 else
713 bInserted = rNewDoc.GetRangeName(nNewSheet)->insert(pRangeData);
715 return bInserted ? pRangeData : nullptr;
718 struct SheetIndex
720 SCTAB mnSheet;
721 sal_uInt16 mnIndex;
723 SheetIndex( SCTAB nSheet, sal_uInt16 nIndex ) : mnSheet(nSheet < -1 ? -1 : nSheet), mnIndex(nIndex) {}
724 bool operator<( const SheetIndex& r ) const
726 // Ascending order sheet, index
727 if (mnSheet < r.mnSheet)
728 return true;
729 if (mnSheet == r.mnSheet)
730 return mnIndex < r.mnIndex;
731 return false;
734 typedef std::map< SheetIndex, SheetIndex > SheetIndexMap;
736 ScRangeData* copyRangeNames( SheetIndexMap& rSheetIndexMap, std::vector<ScRangeData*>& rRangeDataVec,
737 const sc::UpdatedRangeNames& rReferencingNames, SCTAB nTab,
738 const ScRangeData* pOldRangeData, ScDocument& rNewDoc, const ScDocument& rOldDoc,
739 const ScAddress& rNewPos, const ScAddress& rOldPos, bool bGlobalNamesToLocal,
740 const SCTAB nOldSheet, const SCTAB nNewSheet, bool bSameDoc)
742 ScRangeData* pRangeData = nullptr;
743 const ScRangeName* pOldRangeName = (nTab < 0 ? rOldDoc.GetRangeName() : rOldDoc.GetRangeName(nTab));
744 if (pOldRangeName)
746 const ScRangeName* pNewRangeName = (nNewSheet < 0 ? rNewDoc.GetRangeName() : rNewDoc.GetRangeName(nNewSheet));
747 sc::UpdatedRangeNames::NameIndicesType aSet( rReferencingNames.getUpdatedNames(nTab));
748 for (auto const & rIndex : aSet)
750 const ScRangeData* pCopyData = pOldRangeName->findByIndex(rIndex);
751 if (pCopyData)
753 // Match the original pOldRangeData to adapt the current
754 // token's values later. For that no check for an already
755 // copied name is needed as we only enter here if there was
756 // none.
757 if (pCopyData == pOldRangeData)
759 pRangeData = copyRangeName( pCopyData, rNewDoc, rOldDoc, rNewPos, rOldPos,
760 bGlobalNamesToLocal, nOldSheet, nNewSheet, bSameDoc);
761 if (pRangeData)
763 rRangeDataVec.push_back(pRangeData);
764 rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
765 SheetIndex( nNewSheet, pRangeData->GetIndex())));
768 else
770 // First check if the name is already available as copy.
771 const ScRangeData* pFoundData = pNewRangeName->findByUpperName( pCopyData->GetUpperName());
772 if (pFoundData)
774 // Just add the resulting sheet/index mapping.
775 rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
776 SheetIndex( nNewSheet, pFoundData->GetIndex())));
778 else
780 ScRangeData* pTmpData = copyRangeName( pCopyData, rNewDoc, rOldDoc, rNewPos, rOldPos,
781 bGlobalNamesToLocal, nOldSheet, nNewSheet, bSameDoc);
782 if (pTmpData)
784 rRangeDataVec.push_back(pTmpData);
785 rSheetIndexMap.insert( std::make_pair( SheetIndex( nOldSheet, pCopyData->GetIndex()),
786 SheetIndex( nNewSheet, pTmpData->GetIndex())));
793 return pRangeData;
796 } // namespace
798 bool ScDocument::CopyAdjustRangeName( SCTAB& rSheet, sal_uInt16& rIndex, ScRangeData*& rpRangeData,
799 ScDocument& rNewDoc, const ScAddress& rNewPos, const ScAddress& rOldPos, const bool bGlobalNamesToLocal,
800 const bool bUsedByFormula ) const
802 ScDocument* pThis = const_cast<ScDocument*>(this);
803 const bool bSameDoc = (rNewDoc.GetPool() == pThis->GetPool());
804 if (bSameDoc && ((rSheet < 0 && !bGlobalNamesToLocal) || (rSheet >= 0
805 && (rSheet != rOldPos.Tab() || (IsClipboard() && pThis->IsCutMode())))))
806 // Same doc and global name, if not copied to local name, or
807 // sheet-local name on other sheet stays the same. Sheet-local on
808 // same sheet also in a clipboard cut&paste / move operation.
809 return false;
811 // Ensure we don't fiddle with the references until exit.
812 const SCTAB nOldSheet = rSheet;
813 const sal_uInt16 nOldIndex = rIndex;
815 SAL_WARN_IF( !bSameDoc && nOldSheet >= 0 && nOldSheet != rOldPos.Tab(),
816 "sc.core", "adjustCopyRangeName - sheet-local name was on other sheet in other document");
817 /* TODO: can we do something about that? e.g. loop over sheets? */
819 OUString aRangeName;
820 ScRangeData* pOldRangeData = nullptr;
822 // XXX bGlobalNamesToLocal is also a synonym for copied sheet.
823 bool bInsertingBefore = (bGlobalNamesToLocal && bSameDoc && rNewPos.Tab() <= rOldPos.Tab());
825 // The Tab where an old local name is to be found or that a global name
826 // references. May differ below from nOldSheet if a sheet was inserted
827 // before the old position. Global names and local names other than on the
828 // old sheet or new sheet are already updated, local names on the old sheet
829 // or inserted sheet will be updated later. Confusing stuff. Watch out.
830 SCTAB nOldTab = (nOldSheet < 0 ? rOldPos.Tab() : nOldSheet);
831 if (bInsertingBefore)
832 // Sheet was already inserted before old position.
833 ++nOldTab;
835 // Search the name of the RangeName.
836 if (nOldSheet >= 0)
838 const ScRangeName* pNames = GetRangeName(nOldTab);
839 pOldRangeData = pNames ? pNames->findByIndex(nOldIndex) : nullptr;
840 if (!pOldRangeData)
841 return false; // might be an error in the formula array
842 aRangeName = pOldRangeData->GetUpperName();
844 else
846 pOldRangeData = GetRangeName()->findByIndex(nOldIndex);
847 if (!pOldRangeData)
848 return false; // might be an error in the formula array
849 aRangeName = pOldRangeData->GetUpperName();
852 // Find corresponding range name in new document.
853 // First search for local range name then global range names.
854 SCTAB nNewSheet = rNewPos.Tab();
855 ScRangeName* pNewNames = rNewDoc.GetRangeName(nNewSheet);
856 // Search local range names.
857 if (pNewNames)
859 rpRangeData = pNewNames->findByUpperName(aRangeName);
861 // Search global range names.
862 if (!rpRangeData && !bGlobalNamesToLocal)
864 nNewSheet = -1;
865 pNewNames = rNewDoc.GetRangeName();
866 if (pNewNames)
867 rpRangeData = pNewNames->findByUpperName(aRangeName);
869 // If no range name was found copy it.
870 if (!rpRangeData)
872 // Do not copy global name if it doesn't reference sheet or is not used
873 // by a formula copied to another document.
874 bool bEarlyBailOut = (nOldSheet < 0 && (bSameDoc || !bUsedByFormula));
875 MightReferenceSheet eMightReference = mightRangeNameReferenceSheet( pOldRangeData, nOldTab);
876 if (bEarlyBailOut && eMightReference == MightReferenceSheet::NONE)
877 return false;
879 if (eMightReference == MightReferenceSheet::NAME)
881 // Name these to clarify what is passed where.
882 const SCTAB nGlobalRefTab = nOldTab;
883 const SCTAB nLocalRefTab = (bInsertingBefore ? nOldTab-1 : nOldTab);
884 const SCTAB nOldTokenTab = (nOldSheet < 0 ? (bInsertingBefore ? nOldTab-1 : nOldTab) : nOldSheet);
885 const SCTAB nOldTokenTabReplacement = nOldTab;
886 sc::UpdatedRangeNames aReferencingNames;
887 FindRangeNamesReferencingSheet( aReferencingNames, nOldSheet, nOldIndex,
888 nGlobalRefTab, nLocalRefTab, nOldTokenTab, nOldTokenTabReplacement, bSameDoc, 0);
889 if (bEarlyBailOut && aReferencingNames.isEmpty(-1) && aReferencingNames.isEmpty(nOldTokenTabReplacement))
890 return false;
892 SheetIndexMap aSheetIndexMap;
893 std::vector<ScRangeData*> aRangeDataVec;
894 if (!aReferencingNames.isEmpty(nOldTokenTabReplacement))
896 const SCTAB nTmpOldSheet = (nOldSheet < 0 ? nOldTab : nOldSheet);
897 nNewSheet = rNewPos.Tab();
898 rpRangeData = copyRangeNames( aSheetIndexMap, aRangeDataVec, aReferencingNames, nOldTab,
899 pOldRangeData, rNewDoc, *this, rNewPos, rOldPos,
900 bGlobalNamesToLocal, nTmpOldSheet, nNewSheet, bSameDoc);
902 if ((bGlobalNamesToLocal || !bSameDoc) && !aReferencingNames.isEmpty(-1))
904 const SCTAB nTmpOldSheet = -1;
905 const SCTAB nTmpNewSheet = (bGlobalNamesToLocal ? rNewPos.Tab() : -1);
906 ScRangeData* pTmpData = copyRangeNames( aSheetIndexMap, aRangeDataVec, aReferencingNames, -1,
907 pOldRangeData, rNewDoc, *this, rNewPos, rOldPos,
908 bGlobalNamesToLocal, nTmpOldSheet, nTmpNewSheet, bSameDoc);
909 if (!rpRangeData)
911 rpRangeData = pTmpData;
912 nNewSheet = nTmpNewSheet;
916 // Adjust copied nested names to new sheet/index.
917 for (auto & iRD : aRangeDataVec)
919 ScTokenArray* pCode = iRD->GetCode();
920 if (pCode)
922 formula::FormulaTokenArrayPlainIterator aIter(*pCode);
923 for (formula::FormulaToken* p = aIter.First(); p; p = aIter.Next())
925 if (p->GetOpCode() == ocName)
927 auto it = aSheetIndexMap.find( SheetIndex( p->GetSheet(), p->GetIndex()));
928 if (it != aSheetIndexMap.end())
930 p->SetSheet( it->second.mnSheet);
931 p->SetIndex( it->second.mnIndex);
933 else if (!bSameDoc)
935 SAL_WARN("sc.core","adjustCopyRangeName - mapping to new name in other doc missing");
936 p->SetIndex(0); // #NAME? error instead of arbitrary name.
943 else
945 nNewSheet = ((nOldSheet < 0 && !bGlobalNamesToLocal) ? -1 : rNewPos.Tab());
946 rpRangeData = copyRangeName( pOldRangeData, rNewDoc, *this, rNewPos, rOldPos, bGlobalNamesToLocal,
947 nOldSheet, nNewSheet, bSameDoc);
950 if (rpRangeData && !rNewDoc.IsClipOrUndo())
952 ScDocShell* pDocSh = rNewDoc.GetDocumentShell();
953 if (pDocSh)
954 pDocSh->SetAreasChangedNeedBroadcast();
958 rSheet = nNewSheet;
959 rIndex = rpRangeData ? rpRangeData->GetIndex() : 0; // 0 means not inserted
960 return true;
963 bool ScDocument::IsEditActionAllowed(
964 sc::EditAction eAction, SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
966 const ScTable* pTab = FetchTable(nTab);
967 if (!pTab)
968 return false;
970 return pTab->IsEditActionAllowed(eAction, nStartCol, nStartRow, nEndCol, nEndRow);
973 bool ScDocument::IsEditActionAllowed(
974 sc::EditAction eAction, const ScMarkData& rMark, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const
976 return std::all_of(rMark.begin(), rMark.end(),
977 [this, &eAction, &nStartCol, &nStartRow, &nEndCol, &nEndRow](const SCTAB& rTab)
978 { return IsEditActionAllowed(eAction, rTab, nStartCol, nStartRow, nEndCol, nEndRow); });
981 std::optional<sc::ColumnIterator> ScDocument::GetColumnIterator( SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2 ) const
983 const ScTable* pTab = FetchTable(nTab);
984 if (!pTab)
985 return {};
987 return pTab->GetColumnIterator(nCol, nRow1, nRow2);
990 void ScDocument::CreateColumnIfNotExists( SCTAB nTab, SCCOL nCol )
992 ScTable* pTab = FetchTable(nTab);
993 if (!pTab)
994 return;
996 pTab->CreateColumnIfNotExists(nCol);
999 bool ScDocument::EnsureFormulaCellResults( const ScRange& rRange, bool bSkipRunning )
1001 bool bAnyDirty = false;
1002 for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
1004 ScTable* pTab = FetchTable(nTab);
1005 if (!pTab)
1006 continue;
1008 bool bRet = pTab->EnsureFormulaCellResults(
1009 rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), bSkipRunning);
1010 bAnyDirty = bAnyDirty || bRet;
1013 return bAnyDirty;
1016 sc::ExternalDataMapper& ScDocument::GetExternalDataMapper()
1018 if (!mpDataMapper)
1019 mpDataMapper.reset(new sc::ExternalDataMapper(*this));
1021 return *mpDataMapper;
1024 void ScDocument::StoreTabToCache(SCTAB nTab, SvStream& rStrm) const
1026 const ScTable* pTab = FetchTable(nTab);
1027 if (!pTab)
1028 return;
1030 pTab->StoreToCache(rStrm);
1033 void ScDocument::RestoreTabFromCache(SCTAB nTab, SvStream& rStrm)
1035 ScTable* pTab = FetchTable(nTab);
1036 if (!pTab)
1037 return;
1039 pTab->RestoreFromCache(rStrm);
1042 OString ScDocument::dumpSheetGeomData(SCTAB nTab, bool bColumns, SheetGeomType eGeomType)
1044 ScTable* pTab = FetchTable(nTab);
1045 if (!pTab)
1046 return ""_ostr;
1048 return pTab->dumpSheetGeomData(bColumns, eGeomType);
1051 SCCOL ScDocument::GetLOKFreezeCol(SCTAB nTab) const
1053 const ScTable* pTab = FetchTable(nTab);
1054 if (!pTab)
1055 return -1;
1057 return pTab->GetLOKFreezeCol();
1059 SCROW ScDocument::GetLOKFreezeRow(SCTAB nTab) const
1061 const ScTable* pTab = FetchTable(nTab);
1062 if (!pTab)
1063 return -1;
1065 return pTab->GetLOKFreezeRow();
1068 bool ScDocument::SetLOKFreezeCol(SCCOL nFreezeCol, SCTAB nTab)
1070 ScTable* pTab = FetchTable(nTab);
1071 if (!pTab)
1072 return false;
1074 return pTab->SetLOKFreezeCol(nFreezeCol);
1077 bool ScDocument::SetLOKFreezeRow(SCROW nFreezeRow, SCTAB nTab)
1079 ScTable* pTab = FetchTable(nTab);
1080 if (!pTab)
1081 return false;
1083 return pTab->SetLOKFreezeRow(nFreezeRow);
1086 std::set<SCCOL> ScDocument::QueryColumnsWithFormulaCells( SCTAB nTab ) const
1088 const ScTable* pTab = FetchTable(nTab);
1089 if (!pTab)
1090 return std::set<SCCOL>{};
1092 return pTab->QueryColumnsWithFormulaCells();
1095 void ScDocument::CheckIntegrity( SCTAB nTab ) const
1097 const ScTable* pTab = FetchTable(nTab);
1098 if (!pTab)
1099 return;
1101 pTab->CheckIntegrity();
1104 sc::BroadcasterState ScDocument::GetBroadcasterState() const
1106 sc::BroadcasterState aState;
1108 for (const auto& xTab : maTabs)
1109 xTab->CollectBroadcasterState(aState);
1111 if (pBASM)
1112 pBASM->CollectBroadcasterState(aState);
1114 return aState;
1117 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */