summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/document.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/document.cxx')
-rw-r--r--sc/source/core/data/document.cxx7089
1 files changed, 7089 insertions, 0 deletions
diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx
new file mode 100644
index 000000000..9753a16e8
--- /dev/null
+++ b/sc/source/core/data/document.cxx
@@ -0,0 +1,7089 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <scitems.hxx>
+
+#include <editeng/boxitem.hxx>
+#include <editeng/editobj.hxx>
+#include <o3tl/safeint.hxx>
+#include <svx/sdrundomanager.hxx>
+#include <svx/svditer.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/docfile.hxx>
+#include <svl/numformat.hxx>
+#include <svl/poolcach.hxx>
+#include <svl/zforlist.hxx>
+#include <unotools/charclass.hxx>
+#include <unotools/transliterationwrapper.hxx>
+#include <tools/urlobj.hxx>
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+
+#include <com/sun/star/text/WritingMode2.hpp>
+#include <com/sun/star/script/vba/XVBACompatibility.hpp>
+#include <com/sun/star/sheet/TablePageBreakData.hpp>
+#include <com/sun/star/lang/NotInitializedException.hpp>
+
+#include <document.hxx>
+#include <docuno.hxx>
+#include <table.hxx>
+#include <column.hxx>
+#include <attrib.hxx>
+#include <attarray.hxx>
+#include <patattr.hxx>
+#include <rangenam.hxx>
+#include <poolhelp.hxx>
+#include <docpool.hxx>
+#include <stlpool.hxx>
+#include <stlsheet.hxx>
+#include <globstr.hrc>
+#include <scresid.hxx>
+#include <dbdata.hxx>
+#include <chartlis.hxx>
+#include <rangelst.hxx>
+#include <markdata.hxx>
+#include <drwlayer.hxx>
+#include <validat.hxx>
+#include <prnsave.hxx>
+#include <chgtrack.hxx>
+#include <hints.hxx>
+#include <detdata.hxx>
+#include <dpobject.hxx>
+#include <detfunc.hxx>
+#include <scmod.hxx>
+#include <dociter.hxx>
+#include <progress.hxx>
+#include <autonamecache.hxx>
+#include <bcaslot.hxx>
+#include <postit.hxx>
+#include <clipparam.hxx>
+#include <defaultsoptions.hxx>
+#include <editutil.hxx>
+#include <stringutil.hxx>
+#include <formulaiter.hxx>
+#include <formulacell.hxx>
+#include <clipcontext.hxx>
+#include <listenercontext.hxx>
+#include <scopetools.hxx>
+#include <refupdatecontext.hxx>
+#include <formulagroup.hxx>
+#include <tokenstringcontext.hxx>
+#include <compressedarray.hxx>
+#include <recursionhelper.hxx>
+#include <SparklineGroup.hxx>
+#include <SparklineList.hxx>
+
+#include <formula/vectortoken.hxx>
+
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include <comphelper/lok.hxx>
+#include <comphelper/servicehelper.hxx>
+
+#include <vcl/uitest/logger.hxx>
+#include <vcl/uitest/eventdescription.hxx>
+
+#include <mtvelements.hxx>
+#include <sfx2/lokhelper.hxx>
+
+using ::editeng::SvxBorderLine;
+using namespace ::com::sun::star;
+
+namespace WritingMode2 = ::com::sun::star::text::WritingMode2;
+using ::com::sun::star::uno::Sequence;
+using ::com::sun::star::sheet::TablePageBreakData;
+using ::std::set;
+
+namespace {
+
+std::pair<SCTAB,SCTAB> getMarkedTableRange(const std::vector<ScTableUniquePtr>& rTables, const ScMarkData& rMark)
+{
+ SCTAB nTabStart = MAXTAB;
+ SCTAB nTabEnd = 0;
+ SCTAB nMax = static_cast<SCTAB>(rTables.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (!rTables[rTab])
+ continue;
+
+ if (rTab < nTabStart)
+ nTabStart = rTab;
+ nTabEnd = rTab;
+ }
+
+ return std::pair<SCTAB,SCTAB>(nTabStart,nTabEnd);
+}
+
+void collectUIInformation(std::map<OUString, OUString>&& aParameters, const OUString& rAction)
+{
+ EventDescription aDescription;
+ aDescription.aID = "grid_window";
+ aDescription.aAction = rAction;
+ aDescription.aParameters = std::move(aParameters);
+ aDescription.aParent = "MainWindow";
+ aDescription.aKeyWord = "ScGridWinUIObject";
+
+ UITestLogger::getInstance().logEvent(aDescription);
+}
+
+struct ScDefaultAttr
+{
+ const ScPatternAttr* pAttr;
+ SCROW nFirst;
+ SCSIZE nCount;
+ explicit ScDefaultAttr(const ScPatternAttr* pPatAttr) : pAttr(pPatAttr), nFirst(0), nCount(0) {}
+};
+
+struct ScLessDefaultAttr
+{
+ bool operator() (const ScDefaultAttr& rValue1, const ScDefaultAttr& rValue2) const
+ {
+ return rValue1.pAttr < rValue2.pAttr;
+ }
+};
+
+}
+
+typedef std::set<ScDefaultAttr, ScLessDefaultAttr> ScDefaultAttrSet;
+
+void ScDocument::MakeTable( SCTAB nTab,bool _bNeedsNameCheck )
+{
+ if ( !(ValidTab(nTab) && ( nTab >= static_cast<SCTAB>(maTabs.size()) ||!maTabs[nTab])) )
+ return;
+
+ // Get Custom prefix
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ OUString aString = rOpt.GetInitTabPrefix() + OUString::number(nTab+1);
+ if ( _bNeedsNameCheck )
+ CreateValidTabName( aString ); // no doubles
+ if (nTab < static_cast<SCTAB>(maTabs.size()))
+ {
+ maTabs[nTab].reset( new ScTable(*this, nTab, aString) );
+ }
+ else
+ {
+ while(nTab > static_cast<SCTAB>(maTabs.size()))
+ maTabs.push_back(nullptr);
+ maTabs.emplace_back( new ScTable(*this, nTab, aString) );
+ }
+ maTabs[nTab]->SetLoadingMedium(bLoadingMedium);
+}
+
+bool ScDocument::HasTable( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ return true;
+
+ return false;
+}
+
+bool ScDocument::GetHashCode( SCTAB nTab, sal_Int64& rHashCode ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ {
+ if (maTabs[nTab])
+ {
+ rHashCode = maTabs[nTab]->GetHashCode();
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScDocument::GetName( SCTAB nTab, OUString& rName ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ {
+ if (maTabs[nTab])
+ {
+ rName = maTabs[nTab]->GetName();
+ return true;
+ }
+ }
+ rName.clear();
+ return false;
+}
+
+OUString ScDocument::GetCopyTabName( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabNames.size()))
+ return maTabNames[nTab];
+ return OUString();
+}
+
+bool ScDocument::SetCodeName( SCTAB nTab, const OUString& rName )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ {
+ if (maTabs[nTab])
+ {
+ maTabs[nTab]->SetCodeName( rName );
+ return true;
+ }
+ }
+ SAL_WARN("sc", "can't set code name " << rName );
+ return false;
+}
+
+bool ScDocument::GetCodeName( SCTAB nTab, OUString& rName ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ {
+ rName = maTabs[nTab]->GetCodeName();
+ return true;
+ }
+ rName.clear();
+ return false;
+}
+
+bool ScDocument::GetTable( const OUString& rName, SCTAB& rTab ) const
+{
+ static OUString aCacheName, aCacheUpperName;
+
+ assert(!IsThreadedGroupCalcInProgress());
+ if (aCacheName != rName)
+ {
+ aCacheName = rName;
+ // surprisingly slow ...
+ aCacheUpperName = ScGlobal::getCharClass().uppercase(rName);
+ }
+ const OUString aUpperName = aCacheUpperName;
+
+ for (SCTAB i=0; i< static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i])
+ {
+ if (aUpperName == maTabs[i]->GetUpperName())
+ {
+ rTab = i;
+ return true;
+ }
+ }
+ rTab = 0;
+ return false;
+}
+
+std::vector<OUString> ScDocument::GetAllTableNames() const
+{
+ std::vector<OUString> aNames;
+ aNames.reserve(maTabs.size());
+ for (const auto& a : maTabs)
+ {
+ // Positions need to be preserved for ScCompiler and address convention
+ // context, so still push an empty string for NULL tabs.
+ OUString aName;
+ if (a)
+ {
+ const ScTable& rTab = *a;
+ aName = rTab.GetName();
+ }
+ aNames.push_back(aName);
+ }
+
+ return aNames;
+}
+
+ScDBData* ScDocument::GetAnonymousDBData(SCTAB nTab)
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetAnonymousDBData();
+ return nullptr;
+}
+
+SCTAB ScDocument::GetTableCount() const
+{
+ return static_cast<SCTAB>(maTabs.size());
+}
+
+void ScDocument::SetAnonymousDBData(SCTAB nTab, std::unique_ptr<ScDBData> pDBData)
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetAnonymousDBData(std::move(pDBData));
+}
+
+void ScDocument::SetAnonymousDBData( std::unique_ptr<ScDBData> pDBData )
+{
+ mpAnonymousDBData = std::move(pDBData);
+}
+
+ScDBData* ScDocument::GetAnonymousDBData()
+{
+ return mpAnonymousDBData.get();
+}
+
+bool ScDocument::ValidTabName( const OUString& rName )
+{
+ if (rName.isEmpty())
+ return false;
+ sal_Int32 nLen = rName.getLength();
+
+#if 1
+ // Restrict sheet names to what Excel accepts.
+ /* TODO: We may want to remove this restriction for full ODFF compliance.
+ * Merely loading and calculating ODF documents using these characters in
+ * sheet names is not affected by this, but all sheet name editing and
+ * copying functionality is, maybe falling back to "Sheet4" or similar. */
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ const sal_Unicode c = rName[i];
+ switch (c)
+ {
+ case ':':
+ case '\\':
+ case '/':
+ case '?':
+ case '*':
+ case '[':
+ case ']':
+ // these characters are not allowed to match XL's convention.
+ return false;
+ case '\'':
+ if (i == 0 || i == nLen - 1)
+ // single quote is not allowed at the first or last
+ // character position.
+ return false;
+ break;
+ }
+ }
+#endif
+
+ return true;
+}
+
+bool ScDocument::ValidNewTabName( const OUString& rName ) const
+{
+ bool bValid = ValidTabName(rName);
+ if (!bValid)
+ return false;
+ OUString aUpperName = ScGlobal::getCharClass().uppercase(rName);
+ for (const auto& a : maTabs)
+ {
+ if (!a)
+ continue;
+ const OUString& rOldName = a->GetUpperName();
+ bValid = rOldName != aUpperName;
+ if (!bValid)
+ break;
+ }
+ return bValid;
+}
+
+void ScDocument::CreateValidTabName(OUString& rName) const
+{
+ if ( !ValidTabName(rName) )
+ {
+ // Find new one
+
+ // Get Custom prefix
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ const OUString& aStrTable = rOpt.GetInitTabPrefix();
+
+ bool bOk = false;
+
+ // First test if the prefix is valid, if so only avoid doubles
+ bool bPrefix = ValidTabName( aStrTable );
+ OSL_ENSURE(bPrefix, "Invalid Table Name");
+ SCTAB nDummy;
+
+ for ( SCTAB i = static_cast<SCTAB>(maTabs.size())+1; !bOk ; i++ )
+ {
+ rName = aStrTable + OUString::number(static_cast<sal_Int32>(i));
+ if (bPrefix)
+ bOk = ValidNewTabName( rName );
+ else
+ bOk = !GetTable( rName, nDummy );
+ }
+ }
+ else
+ {
+ // testing the supplied Name
+
+ if ( !ValidNewTabName(rName) )
+ {
+ SCTAB i = 1;
+ OUStringBuffer aName;
+ do
+ {
+ i++;
+ aName = rName;
+ aName.append('_');
+ aName.append(static_cast<sal_Int32>(i));
+ }
+ while (!ValidNewTabName(aName.toString()) && (i < MAXTAB+1));
+ rName = aName.makeStringAndClear();
+ }
+ }
+}
+
+void ScDocument::CreateValidTabNames(std::vector<OUString>& aNames, SCTAB nCount) const
+{
+ aNames.clear();//ensure that the vector is empty
+
+ // Get Custom prefix
+ const ScDefaultsOptions& rOpt = SC_MOD()->GetDefaultsOptions();
+ const OUString& aStrTable = rOpt.GetInitTabPrefix();
+
+ OUStringBuffer rName;
+
+ // First test if the prefix is valid, if so only avoid doubles
+ bool bPrefix = ValidTabName( aStrTable );
+ OSL_ENSURE(bPrefix, "Invalid Table Name");
+ SCTAB nDummy;
+ SCTAB i = static_cast<SCTAB>(maTabs.size())+1;
+
+ for (SCTAB j = 0; j < nCount; ++j)
+ {
+ bool bOk = false;
+ while(!bOk)
+ {
+ rName = aStrTable;
+ rName.append(static_cast<sal_Int32>(i));
+ if (bPrefix)
+ bOk = ValidNewTabName( rName.toString() );
+ else
+ bOk = !GetTable( rName.toString(), nDummy );
+ i++;
+ }
+ aNames.push_back(rName.makeStringAndClear());
+ }
+}
+
+void ScDocument::AppendTabOnLoad(const OUString& rName)
+{
+ SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
+ if (!ValidTab(nTabCount))
+ // max table count reached. No more tables.
+ return;
+
+ OUString aName = rName;
+ CreateValidTabName(aName);
+ maTabs.emplace_back( new ScTable(*this, nTabCount, aName) );
+}
+
+void ScDocument::SetTabNameOnLoad(SCTAB nTab, const OUString& rName)
+{
+ if (!ValidTab(nTab) || static_cast<SCTAB>(maTabs.size()) <= nTab)
+ return;
+
+ if (!ValidTabName(rName))
+ return;
+
+ maTabs[nTab]->SetName(rName);
+}
+
+void ScDocument::InvalidateStreamOnSave()
+{
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetStreamValid(false);
+ }
+}
+
+bool ScDocument::InsertTab(
+ SCTAB nPos, const OUString& rName, bool bExternalDocument, bool bUndoDeleteTab )
+{
+ SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
+ bool bValid = ValidTab(nTabCount);
+ if ( !bExternalDocument ) // else test rName == "'Doc'!Tab" first
+ bValid = (bValid && ValidNewTabName(rName));
+ if (bValid)
+ {
+ if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
+ {
+ nPos = maTabs.size();
+ maTabs.emplace_back( new ScTable(*this, nTabCount, rName) );
+ if ( bExternalDocument )
+ maTabs[nTabCount]->SetVisible( false );
+ }
+ else
+ {
+ if (ValidTab(nPos) && (nPos < nTabCount))
+ {
+ sc::RefUpdateInsertTabContext aCxt( *this, nPos, 1);
+
+ ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 );
+ if (pRangeName)
+ pRangeName->UpdateInsertTab(aCxt);
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 );
+ UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,1 );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 );
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) );
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateInsertTab(aCxt);
+ }
+ maTabs.emplace(maTabs.begin() + nPos, new ScTable(*this, nPos, rName));
+
+ // UpdateBroadcastAreas must be called between UpdateInsertTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateCompile();
+ }
+
+ StartAllListeners();
+
+ if (pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateInsertTab(aCxt);
+ }
+
+ bValid = true;
+ }
+ else
+ bValid = false;
+ }
+ }
+
+ if (bValid)
+ {
+ sc::SetFormulaDirtyContext aCxt;
+ aCxt.mbClearTabDeletedFlag = bUndoDeleteTab;
+ aCxt.mnTabDeletedStart = nPos;
+ aCxt.mnTabDeletedEnd = nPos;
+ SetAllFormulasDirty(aCxt);
+
+ if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer())
+ {
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
+ SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
+ }
+ }
+
+ return bValid;
+}
+
+bool ScDocument::InsertTabs( SCTAB nPos, const std::vector<OUString>& rNames,
+ bool bNamesValid )
+{
+ SCTAB nNewSheets = static_cast<SCTAB>(rNames.size());
+ SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
+ bool bValid = bNamesValid || ValidTab(nTabCount+nNewSheets);
+
+ if (bValid)
+ {
+ if (nPos == SC_TAB_APPEND || nPos >= nTabCount)
+ {
+ for ( SCTAB i = 0; i < nNewSheets; ++i )
+ {
+ maTabs.emplace_back( new ScTable(*this, nTabCount + i, rNames.at(i)) );
+ }
+ }
+ else
+ {
+ if (ValidTab(nPos) && (nPos < nTabCount))
+ {
+ sc::RefUpdateInsertTabContext aCxt( *this, nPos, nNewSheets);
+ ScRange aRange( 0,0,nPos, MaxCol(),MaxRow(),MAXTAB );
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,nNewSheets );
+ if (pRangeName)
+ pRangeName->UpdateInsertTab(aCxt);
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,nNewSheets );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,nNewSheets );
+ UpdateChartRef( URM_INSDEL, 0,0,nPos, MaxCol(),MaxRow(),MAXTAB, 0,0,nNewSheets );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0, nNewSheets );
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,nNewSheets ) );
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateInsertTab(aCxt);
+ }
+ for (SCTAB i = 0; i < nNewSheets; ++i)
+ {
+ maTabs.emplace(maTabs.begin() + nPos + i, new ScTable(*this, nPos + i, rNames.at(i)) );
+ }
+
+ // UpdateBroadcastAreas must be called between UpdateInsertTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,nNewSheets);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateCompile();
+ }
+
+ StartAllListeners();
+
+ if (pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateInsertTab(aCxt);
+ }
+
+ bValid = true;
+ }
+ else
+ bValid = false;
+ }
+ }
+
+ if (bValid)
+ {
+ sc::SetFormulaDirtyContext aCxt;
+ SetAllFormulasDirty(aCxt);
+ }
+
+ return bValid;
+}
+
+bool ScDocument::DeleteTab( SCTAB nTab )
+{
+ bool bValid = false;
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ {
+ if (maTabs[nTab])
+ {
+ SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
+ if (nTabCount > 1)
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+ sc::RefUpdateDeleteTabContext aCxt( *this, nTab, 1);
+ sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
+
+ ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab );
+ DelBroadcastAreasInRange( aRange );
+
+ // #i8180# remove database ranges etc. that are on the deleted tab
+ // (restored in undo with ScRefUndoData)
+
+ xColNameRanges->DeleteOnTab( nTab );
+ xRowNameRanges->DeleteOnTab( nTab );
+ pDBCollection->DeleteOnTab( nTab );
+ if (pDPCollection)
+ pDPCollection->DeleteOnTab( nTab );
+ if (pDetOpList)
+ pDetOpList->DeleteOnTab( nTab );
+ DeleteAreaLinksOnTab( nTab );
+
+ // normal reference update
+
+ aRange.aEnd.SetTab( static_cast<SCTAB>(maTabs.size())-1 );
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 );
+ if (pRangeName)
+ pRangeName->UpdateDeleteTab(aCxt);
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1 );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1 );
+ UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1 );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1 );
+ if (pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateDeleteTab(aCxt);
+ }
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1 ) );
+
+ for (auto & pTab : maTabs)
+ if (pTab)
+ pTab->UpdateDeleteTab(aCxt);
+
+ // tdf#149502 make sure ScTable destructor called after the erase is finished, when
+ // maTabs[x].nTab==x is true again, as it should be always true.
+ // In the end of maTabs.erase, maTabs indexes change, but nTab updated before erase.
+ // ~ScTable expect that maTabs[x].nTab==x so it shouldn't be called during erase.
+ ScTableUniquePtr pErasedTab = std::move(maTabs[nTab]);
+ maTabs.erase(maTabs.begin() + nTab);
+ delete pErasedTab.release();
+
+ // UpdateBroadcastAreas must be called between UpdateDeleteTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateCompile();
+ }
+ // Excel-Filter deletes some Tables while loading, Listeners will
+ // only be triggered after the loading is done.
+ if ( !bInsertingFromOtherDoc )
+ {
+ StartAllListeners();
+
+ sc::SetFormulaDirtyContext aFormulaDirtyCxt;
+ SetAllFormulasDirty(aFormulaDirtyCxt);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
+ SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
+ }
+
+ bValid = true;
+ }
+ }
+ }
+ return bValid;
+}
+
+bool ScDocument::DeleteTabs( SCTAB nTab, SCTAB nSheets )
+{
+ bool bValid = false;
+ if (ValidTab(nTab) && (nTab + nSheets) <= static_cast<SCTAB>(maTabs.size()))
+ {
+ if (maTabs[nTab])
+ {
+ SCTAB nTabCount = static_cast<SCTAB>(maTabs.size());
+ if (nTabCount > nSheets)
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+ sc::RefUpdateDeleteTabContext aCxt( *this, nTab, nSheets);
+ sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
+
+ for (SCTAB aTab = 0; aTab < nSheets; ++aTab)
+ {
+ ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTab + aTab );
+ DelBroadcastAreasInRange( aRange );
+
+ // #i8180# remove database ranges etc. that are on the deleted tab
+ // (restored in undo with ScRefUndoData)
+
+ xColNameRanges->DeleteOnTab( nTab + aTab );
+ xRowNameRanges->DeleteOnTab( nTab + aTab );
+ pDBCollection->DeleteOnTab( nTab + aTab );
+ if (pDPCollection)
+ pDPCollection->DeleteOnTab( nTab + aTab );
+ if (pDetOpList)
+ pDetOpList->DeleteOnTab( nTab + aTab );
+ DeleteAreaLinksOnTab( nTab + aTab );
+ }
+
+ if (pRangeName)
+ pRangeName->UpdateDeleteTab(aCxt);
+
+ // normal reference update
+
+ ScRange aRange( 0, 0, nTab, MaxCol(), MaxRow(), nTabCount - 1 );
+ xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
+ xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1*nSheets );
+ pDBCollection->UpdateReference(
+ URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
+ if (pDPCollection)
+ pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1*nSheets );
+ if (pDetOpList)
+ pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1*nSheets );
+ UpdateChartRef( URM_INSDEL, 0,0,nTab, MaxCol(),MaxRow(),MAXTAB, 0,0,-1*nSheets );
+ UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1*nSheets );
+ if (pValidationList)
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateDeleteTab(aCxt);
+ }
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1*nSheets ) );
+
+ for (auto & pTab : maTabs)
+ if (pTab)
+ pTab->UpdateDeleteTab(aCxt);
+
+ maTabs.erase(maTabs.begin() + nTab, maTabs.begin() + nTab + nSheets);
+ // UpdateBroadcastAreas must be called between UpdateDeleteTab,
+ // which ends listening, and StartAllListeners, to not modify
+ // areas that are to be inserted by starting listeners.
+ UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1*nSheets);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->UpdateCompile();
+ }
+ // Excel-Filter deletes some Tables while loading, Listeners will
+ // only be triggered after the loading is done.
+ if ( !bInsertingFromOtherDoc )
+ {
+ StartAllListeners();
+
+ sc::SetFormulaDirtyContext aFormulaDirtyCxt;
+ SetAllFormulasDirty(aFormulaDirtyCxt);
+ }
+
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
+ SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
+ }
+
+ bValid = true;
+ }
+ }
+ }
+ return bValid;
+}
+
+bool ScDocument::RenameTab( SCTAB nTab, const OUString& rName, bool bExternalDocument )
+{
+ bool bValid = false;
+ SCTAB i;
+ if (ValidTab(nTab))
+ {
+ if (maTabs[nTab])
+ {
+ if ( bExternalDocument )
+ bValid = true; // composed name
+ else
+ bValid = ValidTabName(rName);
+ for (i=0; (i< static_cast<SCTAB>(maTabs.size())) && bValid; i++)
+ if (maTabs[i] && (i != nTab))
+ {
+ OUString aOldName = maTabs[i]->GetName();
+ bValid = !ScGlobal::GetTransliteration().isEqual( rName, aOldName );
+ }
+ if (bValid)
+ {
+ // #i75258# update charts before renaming, so they can get their live data objects.
+ // Once the charts are live, the sheet can be renamed without problems.
+ if ( pChartListenerCollection )
+ pChartListenerCollection->UpdateChartsContainingTab( nTab );
+ maTabs[nTab]->SetName(rName);
+
+ // If formulas refer to the renamed sheet, the TokenArray remains valid,
+ // but the XML stream must be re-generated.
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetStreamValid( false );
+ }
+
+ if (comphelper::LibreOfficeKit::isActive() && GetDrawLayer())
+ {
+ ScModelObj* pModel = comphelper::getFromUnoTunnel<ScModelObj>(this->GetDocumentShell()->GetModel());
+ SfxLokHelper::notifyDocumentSizeChangedAllViews(pModel);
+ }
+ }
+ }
+ }
+
+ collectUIInformation({{"NewName", rName}}, "Rename_Sheet");
+
+ return bValid;
+}
+
+void ScDocument::SetVisible( SCTAB nTab, bool bVisible )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
+ if (maTabs[nTab])
+ maTabs[nTab]->SetVisible(bVisible);
+}
+
+bool ScDocument::IsVisible( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
+ if (maTabs[nTab])
+ return maTabs[nTab]->IsVisible();
+
+ return false;
+}
+
+bool ScDocument::IsStreamValid( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->IsStreamValid();
+
+ return false;
+}
+
+void ScDocument::SetStreamValid( SCTAB nTab, bool bSet, bool bIgnoreLock )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetStreamValid( bSet, bIgnoreLock );
+}
+
+void ScDocument::LockStreamValid( bool bLock )
+{
+ mbStreamValidLocked = bLock;
+}
+
+bool ScDocument::IsPendingRowHeights( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->IsPendingRowHeights();
+
+ return false;
+}
+
+void ScDocument::SetPendingRowHeights( SCTAB nTab, bool bSet )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetPendingRowHeights( bSet );
+}
+
+void ScDocument::SetLayoutRTL( SCTAB nTab, bool bRTL, ScObjectHandling eObjectHandling)
+{
+ if ( !(ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab]) )
+ return;
+
+ if ( bImportingXML )
+ {
+ // #i57869# only set the LoadingRTL flag, the real setting (including mirroring)
+ // is applied in SetImportingXML(false). This is so the shapes can be loaded in
+ // normal LTR mode.
+
+ maTabs[nTab]->SetLoadingRTL( bRTL );
+ return;
+ }
+
+ maTabs[nTab]->SetLayoutRTL( bRTL ); // only sets the flag
+ maTabs[nTab]->SetDrawPageSize(true, true, eObjectHandling);
+
+ // objects are already repositioned via SetDrawPageSize, only writing mode is missing
+ if (!mpDrawLayer)
+ return;
+
+ SdrPage* pPage = mpDrawLayer->GetPage(static_cast<sal_uInt16>(nTab));
+ OSL_ENSURE(pPage,"Page ?");
+ if (!pPage)
+ return;
+
+ SdrObjListIter aIter( pPage, SdrIterMode::DeepNoGroups );
+ SdrObject* pObject = aIter.Next();
+ while (pObject)
+ {
+ pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB );
+ pObject = aIter.Next();
+ }
+}
+
+bool ScDocument::IsLayoutRTL( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->IsLayoutRTL();
+
+ return false;
+}
+
+bool ScDocument::IsNegativePage( SCTAB nTab ) const
+{
+ // Negative page area is always used for RTL layout.
+ // The separate method is used to find all RTL handling of drawing objects.
+ return IsLayoutRTL( nTab );
+}
+
+/* ----------------------------------------------------------------------------
+ used search area:
+
+ GetCellArea - Only Data
+ GetTableArea - Data / Attributes
+ GetPrintArea - intended for character objects,
+ sweeps attributes all the way to bottom / right
+---------------------------------------------------------------------------- */
+
+bool ScDocument::GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
+ if (maTabs[nTab])
+ return maTabs[nTab]->GetCellArea( rEndCol, rEndRow );
+
+ rEndCol = 0;
+ rEndRow = 0;
+ return false;
+}
+
+bool ScDocument::GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow, bool bCalcHiddens) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
+ if (maTabs[nTab])
+ return maTabs[nTab]->GetTableArea( rEndCol, rEndRow, bCalcHiddens);
+
+ rEndCol = 0;
+ rEndRow = 0;
+ return false;
+}
+
+bool ScDocument::ShrinkToDataArea(SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB> (maTabs.size()) || !maTabs[nTab])
+ return false;
+
+ SCCOL nCol1, nCol2;
+ SCROW nRow1, nRow2;
+ maTabs[nTab]->GetFirstDataPos(nCol1, nRow1);
+ maTabs[nTab]->GetLastDataPos(nCol2, nRow2);
+
+ if (nCol1 > nCol2 || nRow1 > nRow2)
+ // invalid range.
+ return false;
+
+ // Make sure the area only shrinks, and doesn't grow.
+ if (rStartCol < nCol1)
+ rStartCol = nCol1;
+ if (nCol2 < rEndCol)
+ rEndCol = nCol2;
+ if (rStartRow < nRow1)
+ rStartRow = nRow1;
+ if (nRow2 < rEndRow)
+ rEndRow = nRow2;
+
+ if (rStartCol > rEndCol || rStartRow > rEndRow)
+ // invalid range.
+ return false;
+
+ return true; // success!
+}
+
+bool ScDocument::ShrinkToUsedDataArea( bool& o_bShrunk, SCTAB nTab, SCCOL& rStartCol,
+ SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly,
+ bool bStickyTopRow, bool bStickyLeftCol, ScDataAreaExtras* pDataAreaExtras ) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB> (maTabs.size()) || !maTabs[nTab])
+ {
+ o_bShrunk = false;
+ return false;
+ }
+ return maTabs[nTab]->ShrinkToUsedDataArea(
+ o_bShrunk, rStartCol, rStartRow, rEndCol, rEndRow, bColumnsOnly, bStickyTopRow,
+ bStickyLeftCol, pDataAreaExtras);
+}
+
+SCROW ScDocument::GetLastDataRow( SCTAB nTab, SCCOL nCol1, SCCOL nCol2, SCROW nLastRow ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return -1;
+
+ return pTab->GetLastDataRow(nCol1, nCol2, nLastRow);
+}
+
+// connected area
+
+void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow, bool bIncludeOld, bool bOnlyDown ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld, bOnlyDown );
+}
+
+bool ScDocument::GetDataAreaSubrange(ScRange& rRange) const
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ if (nTab != rRange.aEnd.Tab())
+ return true;
+
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetDataAreaSubrange(rRange);
+
+ return true;
+}
+
+void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB> (maTabs.size()))
+ if (maTabs[nTab])
+ maTabs[nTab]->LimitChartArea( rStartCol, rStartRow, rEndCol, rEndRow );
+}
+
+void ScDocument::LimitChartIfAll( ScRangeListRef& rRangeList )
+{
+ ScRangeListRef aNew = new ScRangeList;
+ if (rRangeList.is())
+ {
+ for ( size_t i = 0, nCount = rRangeList->size(); i < nCount; i++ )
+ {
+ ScRange aRange( (*rRangeList)[i] );
+ if ( ( aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MaxCol() ) ||
+ ( aRange.aStart.Row() == 0 && aRange.aEnd.Row() == MaxRow() ) )
+ {
+ SCCOL nStartCol = aRange.aStart.Col();
+ SCROW nStartRow = aRange.aStart.Row();
+ SCCOL nEndCol = aRange.aEnd.Col();
+ SCROW nEndRow = aRange.aEnd.Row();
+ SCTAB nTab = aRange.aStart.Tab();
+ if ( nTab < static_cast<SCTAB> (maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->LimitChartArea(nStartCol, nStartRow, nEndCol, nEndRow);
+ aRange.aStart.SetCol( nStartCol );
+ aRange.aStart.SetRow( nStartRow );
+ aRange.aEnd.SetCol( nEndCol );
+ aRange.aEnd.SetRow( nEndRow );
+ }
+ aNew->push_back(aRange);
+ }
+ }
+ else
+ {
+ OSL_FAIL("LimitChartIfAll: Ref==0");
+ }
+ rRangeList = aNew;
+}
+
+static void lcl_GetFirstTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
+{
+ // without ScMarkData, leave start/end unchanged
+ if ( !pTabMark )
+ return;
+
+ for (SCTAB nTab=0; nTab< aMaxTab; ++nTab)
+ if (pTabMark->GetTableSelect(nTab))
+ {
+ // find first range of consecutive selected sheets
+ rTabRangeStart = pTabMark->GetFirstSelected();
+ while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
+ ++nTab;
+ rTabRangeEnd = nTab;
+ return;
+ }
+}
+
+static bool lcl_GetNextTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark, SCTAB aMaxTab )
+{
+ if ( pTabMark )
+ {
+ // find next range of consecutive selected sheets after rTabRangeEnd
+ for (SCTAB nTab=rTabRangeEnd+1; nTab< aMaxTab; ++nTab)
+ if (pTabMark->GetTableSelect(nTab))
+ {
+ rTabRangeStart = nTab;
+ while ( nTab+1 < aMaxTab && pTabMark->GetTableSelect(nTab+1) )
+ ++nTab;
+ rTabRangeEnd = nTab;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool ScDocument::CanInsertRow( const ScRange& rRange ) const
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+ SCSIZE nSize = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
+
+ bool bTest = true;
+ for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i])
+ bTest &= maTabs[i]->TestInsertRow( nStartCol, nEndCol, nStartRow, nSize );
+
+ return bTest;
+}
+
+namespace {
+
+struct SetDirtyIfPostponedHandler
+{
+ void operator() (const ScTableUniquePtr & p)
+ {
+ if (p)
+ p->SetDirtyIfPostponed();
+ }
+};
+
+struct BroadcastRecalcOnRefMoveHandler
+{
+ void operator() (const ScTableUniquePtr & p)
+ {
+ if (p)
+ p->BroadcastRecalcOnRefMove();
+ }
+};
+
+struct BroadcastRecalcOnRefMoveGuard
+{
+ explicit BroadcastRecalcOnRefMoveGuard( ScDocument* pDoc ) :
+ aSwitch( *pDoc, false),
+ aBulk( pDoc->GetBASM(), SfxHintId::ScDataChanged)
+ {
+ }
+
+private:
+ sc::AutoCalcSwitch aSwitch; // first for ctor/dtor order, destroy second
+ ScBulkBroadcast aBulk; // second for ctor/dtor order, destroy first
+};
+
+}
+
+bool ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab,
+ SCCOL nEndCol, SCTAB nEndTab,
+ SCROW nStartRow, SCSIZE nSize, ScDocument* pRefUndoDoc,
+ const ScMarkData* pTabMark )
+{
+ SCTAB i;
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartTab, nEndTab );
+ if ( pTabMark )
+ {
+ nStartTab = 0;
+ nEndTab = static_cast<SCTAB>(maTabs.size()) -1;
+ }
+
+ bool bTest = true;
+ bool bRet = false;
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+ bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
+ EnableDelayDeletingBroadcasters( true );
+ for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ bTest &= maTabs[i]->TestInsertRow(nStartCol, nEndCol, nStartRow, nSize);
+ if (bTest)
+ {
+ // UpdateBroadcastAreas have to be called before UpdateReference, so that entries
+ // aren't shifted that would be rebuild at UpdateReference
+
+ // handle chunks of consecutive selected sheets together
+ SCTAB nTabRangeStart = nStartTab;
+ SCTAB nTabRangeEnd = nEndTab;
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
+ ScRange aShiftedRange(nStartCol, nStartRow, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
+ sc::EndListeningContext aEndListenCxt(*this);
+
+ std::vector<ScAddress> aGroupPos;
+ do
+ {
+ aShiftedRange.aStart.SetTab(nTabRangeStart);
+ aShiftedRange.aEnd.SetTab(nTabRangeEnd);
+
+ // Collect all formula groups that will get split by the shifting,
+ // and end all their listening. Record the position of the top
+ // cell of the topmost group, and the position of the bottom cell
+ // of the bottommost group.
+ EndListeningIntersectedGroups(aEndListenCxt, aShiftedRange, &aGroupPos);
+
+ UpdateBroadcastAreas(URM_INSDEL, aShiftedRange, 0, static_cast<SCROW>(nSize), 0);
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
+
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
+
+ sc::RefUpdateContext aCxt(*this);
+ aCxt.meMode = URM_INSDEL;
+ aCxt.maRange = aShiftedRange;
+ aCxt.mnRowDelta = nSize;
+ do
+ {
+ aCxt.maRange.aStart.SetTab(nTabRangeStart);
+ aCxt.maRange.aEnd.SetTab(nTabRangeEnd);
+ UpdateReference(aCxt, pRefUndoDoc, false); // without drawing objects
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
+
+ // UpdateReference should have set "needs listening" flags to those
+ // whose references have been modified. We also need to set this flag
+ // to those that were in the groups that got split by shifting.
+ SetNeedsListeningGroups(aGroupPos);
+
+ for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ maTabs[i]->InsertRow( nStartCol, nEndCol, nStartRow, nSize );
+
+ // UpdateRef for drawing layer must be after inserting,
+ // when the new row heights are known.
+ for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ maTabs[i]->UpdateDrawRef( URM_INSDEL,
+ nStartCol, nStartRow, nStartTab, nEndCol, MaxRow(), nEndTab,
+ 0, static_cast<SCROW>(nSize), 0 );
+
+ if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
+ { // A new Listening is needed when references to deleted ranges are restored,
+ // previous Listeners were removed in FormulaCell UpdateReference.
+ StartAllListeners();
+ }
+ else
+ { // Listeners have been removed in UpdateReference
+ StartNeededListeners();
+
+ // At least all cells using range names pointing relative to the
+ // moved range must be recalculated, and all cells marked postponed
+ // dirty.
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyIfPostponed();
+ }
+
+ {
+ BroadcastRecalcOnRefMoveGuard g(this);
+ std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
+ }
+ }
+ bRet = true;
+ }
+ EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
+ SetAutoCalc( bOldAutoCalc );
+ if ( bRet && pChartListenerCollection )
+ pChartListenerCollection->UpdateDirtyCharts();
+ return bRet;
+}
+
+bool ScDocument::InsertRow( const ScRange& rRange )
+{
+ return InsertRow( rRange.aStart.Col(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Tab(),
+ rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
+}
+
+void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab,
+ SCCOL nEndCol, SCTAB nEndTab,
+ SCROW nStartRow, SCSIZE nSize,
+ ScDocument* pRefUndoDoc, bool* pUndoOutline,
+ const ScMarkData* pTabMark )
+{
+ SCTAB i;
+
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartTab, nEndTab );
+ if ( pTabMark )
+ {
+ nStartTab = 0;
+ nEndTab = static_cast<SCTAB>(maTabs.size())-1;
+ }
+
+ sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
+
+ // handle chunks of consecutive selected sheets together
+ SCTAB nTabRangeStart = nStartTab;
+ SCTAB nTabRangeEnd = nEndTab;
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
+ do
+ {
+ if ( ValidRow(nStartRow+nSize) )
+ {
+ DelBroadcastAreasInRange( ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( nEndCol, nStartRow+nSize-1, nTabRangeEnd ) ) );
+ UpdateBroadcastAreas( URM_INSDEL, ScRange(
+ ScAddress( nStartCol, nStartRow+nSize, nTabRangeStart ),
+ ScAddress( nEndCol, MaxRow(), nTabRangeEnd )), 0, -static_cast<SCROW>(nSize), 0 );
+ }
+ else
+ DelBroadcastAreasInRange( ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( nEndCol, MaxRow(), nTabRangeEnd ) ) );
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
+
+ sc::RefUpdateContext aCxt(*this);
+ const bool bLastRowIncluded = (static_cast<SCROW>(nStartRow + nSize) == GetMaxRowCount() && ValidRow(nStartRow));
+ if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
+ {
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
+ aCxt.meMode = URM_INSDEL;
+ aCxt.mnRowDelta = -static_cast<SCROW>(nSize);
+ if (bLastRowIncluded)
+ {
+ // Last row is included, shift a virtually non-existent row in.
+ aCxt.maRange = ScRange( nStartCol, GetMaxRowCount(), nTabRangeStart, nEndCol, GetMaxRowCount(), nTabRangeEnd);
+ }
+ else
+ {
+ aCxt.maRange = ScRange( nStartCol, nStartRow+nSize, nTabRangeStart, nEndCol, MaxRow(), nTabRangeEnd);
+ }
+ do
+ {
+ UpdateReference(aCxt, pRefUndoDoc, true, false);
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
+ }
+
+ if (pUndoOutline)
+ *pUndoOutline = false;
+
+ // Keep track of the positions of all formula groups that have been joined
+ // during row deletion.
+ std::vector<ScAddress> aGroupPos;
+
+ for ( i = nStartTab; i <= nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ maTabs[i]->DeleteRow(aCxt.maRegroupCols, nStartCol, nEndCol, nStartRow, nSize, pUndoOutline, &aGroupPos);
+
+ // Newly joined groups have some of their members still listening. We
+ // need to make sure none of them are listening.
+ EndListeningGroups(aGroupPos);
+
+ // Mark all joined groups for group listening.
+ SetNeedsListeningGroups(aGroupPos);
+
+ if ( ValidRow(nStartRow+nSize) || bLastRowIncluded )
+ {
+ // Listeners have been removed in UpdateReference
+ StartNeededListeners();
+
+ // At least all cells using range names pointing relative to the moved
+ // range must be recalculated, and all cells marked postponed dirty.
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyIfPostponed();
+ }
+
+ {
+ BroadcastRecalcOnRefMoveGuard g(this);
+ std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
+ }
+ }
+
+ if (pChartListenerCollection)
+ pChartListenerCollection->UpdateDirtyCharts();
+}
+
+void ScDocument::DeleteRow( const ScRange& rRange )
+{
+ DeleteRow( rRange.aStart.Col(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Tab(),
+ rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1) );
+}
+
+bool ScDocument::CanInsertCol( const ScRange& rRange ) const
+{
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+ SCSIZE nSize = static_cast<SCSIZE>(nEndCol - nStartCol + 1);
+
+ bool bTest = true;
+ for (SCTAB i=nStartTab; i<=nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i])
+ bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
+
+ return bTest;
+}
+
+bool ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab,
+ SCROW nEndRow, SCTAB nEndTab,
+ SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
+ const ScMarkData* pTabMark )
+{
+ SCTAB i;
+
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+ if ( pTabMark )
+ {
+ nStartTab = 0;
+ nEndTab = static_cast<SCTAB>(maTabs.size())-1;
+ }
+
+ bool bTest = true;
+ bool bRet = false;
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+ bool oldDelayedDeleteBroadcasters = IsDelayedDeletingBroadcasters();
+ EnableDelayDeletingBroadcasters( true );
+ for ( i = nStartTab; i <= nEndTab && bTest && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ bTest &= maTabs[i]->TestInsertCol( nStartRow, nEndRow, nSize );
+ if (bTest)
+ {
+ // handle chunks of consecutive selected sheets together
+ SCTAB nTabRangeStart = nStartTab;
+ SCTAB nTabRangeEnd = nEndTab;
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
+ do
+ {
+ UpdateBroadcastAreas( URM_INSDEL, ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), static_cast<SCCOL>(nSize), 0, 0 );
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
+
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
+
+ sc::RefUpdateContext aCxt(*this);
+ aCxt.meMode = URM_INSDEL;
+ aCxt.maRange = ScRange(nStartCol, nStartRow, nTabRangeStart, MaxCol(), nEndRow, nTabRangeEnd);
+ aCxt.mnColDelta = nSize;
+ do
+ {
+ UpdateReference(aCxt, pRefUndoDoc, true, false);
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
+
+ for (i=nStartTab; i<=nEndTab && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ maTabs[i]->InsertCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize);
+
+ if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() )
+ { // A new Listening is needed when references to deleted ranges are restored,
+ // previous Listeners were removed in FormulaCell UpdateReference.
+ StartAllListeners();
+ }
+ else
+ {
+ // Listeners have been removed in UpdateReference
+ StartNeededListeners();
+ // At least all cells using range names pointing relative to the
+ // moved range must be recalculated, and all cells marked postponed
+ // dirty.
+ {
+ ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
+ std::for_each(maTabs.begin(), maTabs.end(), SetDirtyIfPostponedHandler());
+ }
+ // Cells containing functions such as CELL, COLUMN or ROW may have
+ // changed their values on relocation. Broadcast them.
+ {
+ BroadcastRecalcOnRefMoveGuard g(this);
+ std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
+ }
+ }
+ bRet = true;
+ }
+ EnableDelayDeletingBroadcasters( oldDelayedDeleteBroadcasters );
+ SetAutoCalc( bOldAutoCalc );
+ if ( bRet && pChartListenerCollection )
+ pChartListenerCollection->UpdateDirtyCharts();
+ return bRet;
+}
+
+bool ScDocument::InsertCol( const ScRange& rRange )
+{
+ return InsertCol( rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Row(), rRange.aEnd.Tab(),
+ rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
+}
+
+void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTAB nEndTab,
+ SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc,
+ bool* pUndoOutline, const ScMarkData* pTabMark )
+{
+ SCTAB i;
+
+ PutInOrder( nStartRow, nEndRow );
+ PutInOrder( nStartTab, nEndTab );
+ if ( pTabMark )
+ {
+ nStartTab = 0;
+ nEndTab = static_cast<SCTAB>(maTabs.size())-1;
+ }
+
+ sc::AutoCalcSwitch aACSwitch(*this, false); // avoid multiple calculations
+ ScBulkBroadcast aBulkBroadcast(GetBASM(), SfxHintId::ScDataChanged);
+
+ // handle chunks of consecutive selected sheets together
+ SCTAB nTabRangeStart = nStartTab;
+ SCTAB nTabRangeEnd = nEndTab;
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
+ do
+ {
+ if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) )
+ {
+ DelBroadcastAreasInRange( ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize-1), nEndRow, nTabRangeEnd ) ) );
+ UpdateBroadcastAreas( URM_INSDEL, ScRange(
+ ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart ),
+ ScAddress( MaxCol(), nEndRow, nTabRangeEnd )), -static_cast<SCCOL>(nSize), 0, 0 );
+ }
+ else
+ DelBroadcastAreasInRange( ScRange(
+ ScAddress( nStartCol, nStartRow, nTabRangeStart ),
+ ScAddress( MaxCol(), nEndRow, nTabRangeEnd ) ) );
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
+
+ sc::RefUpdateContext aCxt(*this);
+ const bool bLastColIncluded = (static_cast<SCCOL>(nStartCol + nSize) == GetMaxColCount() && ValidCol(nStartCol));
+ if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
+ {
+ lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) );
+ aCxt.meMode = URM_INSDEL;
+ aCxt.mnColDelta = -static_cast<SCCOL>(nSize);
+ if (bLastColIncluded)
+ {
+ // Last column is included, shift a virtually non-existent column in.
+ aCxt.maRange = ScRange( GetMaxColCount(), nStartRow, nTabRangeStart, GetMaxColCount(), nEndRow, nTabRangeEnd);
+ }
+ else
+ {
+ aCxt.maRange = ScRange( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart,
+ MaxCol(), nEndRow, nTabRangeEnd);
+ }
+ do
+ {
+ UpdateReference(aCxt, pRefUndoDoc, true, false);
+ }
+ while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark, static_cast<SCTAB>(maTabs.size()) ) );
+ }
+
+ if (pUndoOutline)
+ *pUndoOutline = false;
+
+ for (i = nStartTab; i <= nEndTab && i < static_cast<SCTAB>(maTabs.size()); ++i)
+ {
+ if (maTabs[i] && (!pTabMark || pTabMark->GetTableSelect(i)))
+ maTabs[i]->DeleteCol(aCxt.maRegroupCols, nStartCol, nStartRow, nEndRow, nSize, pUndoOutline);
+ }
+
+ if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) || bLastColIncluded )
+ {
+ // Listeners have been removed in UpdateReference
+ StartNeededListeners();
+
+ // At least all cells using range names pointing relative to the moved
+ // range must be recalculated, and all cells marked postponed dirty.
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyIfPostponed();
+ }
+
+ {
+ BroadcastRecalcOnRefMoveGuard g(this);
+ std::for_each(maTabs.begin(), maTabs.end(), BroadcastRecalcOnRefMoveHandler());
+ }
+ }
+
+ if (pChartListenerCollection)
+ pChartListenerCollection->UpdateDirtyCharts();
+}
+
+void ScDocument::DeleteCol( const ScRange& rRange )
+{
+ DeleteCol( rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Row(), rRange.aEnd.Tab(),
+ rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1) );
+}
+
+// for Area-Links: Insert/delete cells, when the range is changed.
+// (without Paint)
+
+static void lcl_GetInsDelRanges( const ScRange& rOld, const ScRange& rNew,
+ ScRange& rColRange, bool& rInsCol, bool& rDelCol,
+ ScRange& rRowRange, bool& rInsRow, bool& rDelRow )
+{
+ OSL_ENSURE( rOld.aStart == rNew.aStart, "FitBlock: Beginning is different" );
+
+ rInsCol = rDelCol = rInsRow = rDelRow = false;
+
+ SCCOL nStartX = rOld.aStart.Col();
+ SCROW nStartY = rOld.aStart.Row();
+ SCCOL nOldEndX = rOld.aEnd.Col();
+ SCROW nOldEndY = rOld.aEnd.Row();
+ SCCOL nNewEndX = rNew.aEnd.Col();
+ SCROW nNewEndY = rNew.aEnd.Row();
+ SCTAB nTab = rOld.aStart.Tab();
+
+ // if more rows, columns are inserted/deleted at the old height.
+ bool bGrowY = ( nNewEndY > nOldEndY );
+ SCROW nColEndY = bGrowY ? nOldEndY : nNewEndY;
+ SCCOL nRowEndX = bGrowY ? nNewEndX : nOldEndX;
+
+ // Columns
+
+ if ( nNewEndX > nOldEndX ) // Insert columns
+ {
+ rColRange = ScRange( nOldEndX+1, nStartY, nTab, nNewEndX, nColEndY, nTab );
+ rInsCol = true;
+ }
+ else if ( nNewEndX < nOldEndX ) // Delete columns
+ {
+ rColRange = ScRange( nNewEndX+1, nStartY, nTab, nOldEndX, nColEndY, nTab );
+ rDelCol = true;
+ }
+
+ // Rows
+
+ if ( nNewEndY > nOldEndY ) // Insert rows
+ {
+ rRowRange = ScRange( nStartX, nOldEndY+1, nTab, nRowEndX, nNewEndY, nTab );
+ rInsRow = true;
+ }
+ else if ( nNewEndY < nOldEndY ) // Delete rows
+ {
+ rRowRange = ScRange( nStartX, nNewEndY+1, nTab, nRowEndX, nOldEndY, nTab );
+ rDelRow = true;
+ }
+}
+
+bool ScDocument::HasPartOfMerged( const ScRange& rRange )
+{
+ bool bPart = false;
+ SCTAB nTab = rRange.aStart.Tab();
+
+ SCCOL nStartX = rRange.aStart.Col();
+ SCROW nStartY = rRange.aStart.Row();
+ SCCOL nEndX = rRange.aEnd.Col();
+ SCROW nEndY = rRange.aEnd.Row();
+
+ if (HasAttrib( nStartX, nStartY, nTab, nEndX, nEndY, nTab,
+ HasAttrFlags::Merged | HasAttrFlags::Overlapped ))
+ {
+ ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab );
+ ExtendOverlapped( nStartX, nStartY, nEndX, nEndY, nTab );
+
+ bPart = ( nStartX != rRange.aStart.Col() || nEndX != rRange.aEnd.Col() ||
+ nStartY != rRange.aStart.Row() || nEndY != rRange.aEnd.Row() );
+ }
+ return bPart;
+}
+
+formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScAddress& rPos )
+{
+ SCTAB nTab = rPos.Tab();
+ if (!TableExists(nTab))
+ return formula::FormulaTokenRef();
+
+ return maTabs[nTab]->ResolveStaticReference(rPos.Col(), rPos.Row());
+}
+
+formula::FormulaTokenRef ScDocument::ResolveStaticReference( const ScRange& rRange )
+{
+ SCTAB nTab = rRange.aStart.Tab();
+ if (nTab != rRange.aEnd.Tab() || !TableExists(nTab))
+ return formula::FormulaTokenRef();
+
+ return maTabs[nTab]->ResolveStaticReference(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+}
+
+formula::VectorRefArray ScDocument::FetchVectorRefArray( const ScAddress& rPos, SCROW nLength )
+{
+ SCTAB nTab = rPos.Tab();
+ if (!TableExists(nTab))
+ return formula::VectorRefArray();
+
+ return maTabs[nTab]->FetchVectorRefArray(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
+}
+
+#ifdef DBG_UTIL
+void ScDocument::AssertNoInterpretNeeded( const ScAddress& rPos, SCROW nLength )
+{
+ SCTAB nTab = rPos.Tab();
+ assert(TableExists(nTab));
+ return maTabs[nTab]->AssertNoInterpretNeeded(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1);
+}
+#endif
+
+void ScDocument::UnlockAdjustHeight()
+{
+ assert(nAdjustHeightLock > 0);
+ if(nAdjustHeightLock > 0)
+ --nAdjustHeightLock;
+}
+
+bool ScDocument::HandleRefArrayForParallelism( const ScAddress& rPos, SCROW nLength, const ScFormulaCellGroupRef& mxGroup )
+{
+ SCTAB nTab = rPos.Tab();
+ if (!TableExists(nTab))
+ return false;
+
+ return maTabs[nTab]->HandleRefArrayForParallelism(rPos.Col(), rPos.Row(), rPos.Row()+nLength-1, mxGroup);
+}
+
+bool ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew )
+{
+ if ( rOld == rNew )
+ return true;
+
+ bool bOk = true;
+ bool bInsCol,bDelCol,bInsRow,bDelRow;
+ ScRange aColRange,aRowRange;
+ lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
+
+ if ( bInsCol && !CanInsertCol( aColRange ) ) // Cells at the edge ?
+ bOk = false;
+ if ( bInsRow && !CanInsertRow( aRowRange ) ) // Cells at the edge ?
+ bOk = false;
+
+ if ( bInsCol || bDelCol )
+ {
+ aColRange.aEnd.SetCol(MaxCol());
+ if ( HasPartOfMerged(aColRange) )
+ bOk = false;
+ }
+ if ( bInsRow || bDelRow )
+ {
+ aRowRange.aEnd.SetRow(MaxRow());
+ if ( HasPartOfMerged(aRowRange) )
+ bOk = false;
+ }
+
+ return bOk;
+}
+
+void ScDocument::FitBlock( const ScRange& rOld, const ScRange& rNew, bool bClear )
+{
+ if (bClear)
+ DeleteAreaTab( rOld, InsertDeleteFlags::ALL );
+
+ bool bInsCol,bDelCol,bInsRow,bDelRow;
+ ScRange aColRange,aRowRange;
+ lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow );
+
+ if ( bInsCol )
+ InsertCol( aColRange ); // First insert columns
+ if ( bInsRow )
+ InsertRow( aRowRange );
+
+ if ( bDelRow )
+ DeleteRow( aRowRange ); // First delete rows
+ if ( bDelCol )
+ DeleteCol( aColRange );
+
+ // Expand references to inserted rows
+
+ if ( bInsCol || bInsRow )
+ {
+ ScRange aGrowSource = rOld;
+ aGrowSource.aEnd.SetCol(std::min( rOld.aEnd.Col(), rNew.aEnd.Col() ));
+ aGrowSource.aEnd.SetRow(std::min( rOld.aEnd.Row(), rNew.aEnd.Row() ));
+ SCCOL nGrowX = bInsCol ? ( rNew.aEnd.Col() - rOld.aEnd.Col() ) : 0;
+ SCROW nGrowY = bInsRow ? ( rNew.aEnd.Row() - rOld.aEnd.Row() ) : 0;
+ UpdateGrow( aGrowSource, nGrowX, nGrowY );
+ }
+}
+
+void ScDocument::DeleteArea(
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
+ InsertDeleteFlags nDelFlag, bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
+{
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+
+ std::vector<ScAddress> aGroupPos;
+ // Destroy and reconstruct listeners only if content is affected.
+ bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
+ if (bDelContent)
+ {
+ // Record the positions of top and/or bottom formula groups that intersect
+ // the area borders.
+ sc::EndListeningContext aCxt(*this);
+ ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
+ for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
+ {
+ if (rMark.GetTableSelect(i))
+ {
+ aRange.aStart.SetTab(i);
+ aRange.aEnd.SetTab(i);
+
+ EndListeningIntersectedGroups(aCxt, aRange, &aGroupPos);
+ }
+ }
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i])
+ if ( rMark.GetTableSelect(i) || bIsUndo )
+ maTabs[i]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);
+
+ if (!bDelContent)
+ return;
+
+ // Re-start listeners on those top bottom groups that have been split.
+ SetNeedsListeningGroups(aGroupPos);
+ StartNeededListeners();
+
+ // If formula groups were split their listeners were destroyed and may
+ // need to be notified now that they're restored, ScTable::DeleteArea()
+ // couldn't do that.
+ if (aGroupPos.empty())
+ return;
+
+ ScRange aRange(nCol1, nRow1, 0, nCol2, nRow2, 0);
+ for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
+ {
+ if (rMark.GetTableSelect(i))
+ {
+ aRange.aStart.SetTab(i);
+ aRange.aEnd.SetTab(i);
+ SetDirty( aRange, true);
+ }
+ }
+}
+
+void ScDocument::DeleteAreaTab(SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2,
+ SCTAB nTab, InsertDeleteFlags nDelFlag)
+{
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ {
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+ maTabs[nTab]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag);
+ SetAutoCalc( bOldAutoCalc );
+ }
+}
+
+void ScDocument::DeleteAreaTab( const ScRange& rRange, InsertDeleteFlags nDelFlag )
+{
+ for ( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); nTab++ )
+ DeleteAreaTab( rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(),
+ nTab, nDelFlag );
+}
+
+void ScDocument::InitUndoSelected(const ScDocument& rSrcDoc, const ScMarkData& rTabSelection,
+ bool bColInfo, bool bRowInfo )
+{
+ if (bIsUndo)
+ {
+ Clear();
+
+ SharePooledResources(&rSrcDoc);
+
+ for (SCTAB nTab = 0; nTab <= rTabSelection.GetLastSelected(); nTab++)
+ if ( rTabSelection.GetTableSelect( nTab ) )
+ {
+ ScTableUniquePtr pTable(new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo));
+ if (nTab < static_cast<SCTAB>(maTabs.size()))
+ maTabs[nTab] = std::move(pTable);
+ else
+ maTabs.push_back(std::move(pTable));
+ }
+ else
+ {
+ if (nTab < static_cast<SCTAB>(maTabs.size()))
+ maTabs[nTab]=nullptr;
+ else
+ maTabs.push_back(nullptr);
+ }
+ }
+ else
+ {
+ OSL_FAIL("InitUndo");
+ }
+}
+
+void ScDocument::InitUndo( const ScDocument& rSrcDoc, SCTAB nTab1, SCTAB nTab2,
+ bool bColInfo, bool bRowInfo )
+{
+ if (!bIsUndo)
+ {
+ OSL_FAIL("InitUndo");
+ return;
+ }
+
+ Clear();
+
+ // Undo document shares its pooled resources with the source document.
+ SharePooledResources(&rSrcDoc);
+
+ if (rSrcDoc.mpShell->GetMedium())
+ maFileURL = rSrcDoc.mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+
+ if ( nTab2 >= static_cast<SCTAB>(maTabs.size()))
+ maTabs.resize(nTab2 + 1);
+ for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
+ {
+ maTabs[nTab].reset(new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo));
+ }
+}
+
+void ScDocument::AddUndoTab( SCTAB nTab1, SCTAB nTab2, bool bColInfo, bool bRowInfo )
+{
+ if (!bIsUndo)
+ {
+ OSL_FAIL("AddUndoTab");
+ return;
+ }
+
+ if (nTab2 >= static_cast<SCTAB>(maTabs.size()))
+ {
+ maTabs.resize(nTab2+1);
+ }
+
+ for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++)
+ if (!maTabs[nTab])
+ {
+ maTabs[nTab].reset( new ScTable(*this, nTab, OUString(), bColInfo, bRowInfo) );
+ }
+}
+
+void ScDocument::SetCutMode( bool bVal )
+{
+ if (bIsClip)
+ GetClipParam().mbCutMode = bVal;
+ else
+ {
+ OSL_FAIL("SetCutMode without bIsClip");
+ }
+}
+
+bool ScDocument::IsCutMode()
+{
+ if (bIsClip)
+ return GetClipParam().mbCutMode;
+ else
+ {
+ OSL_FAIL("IsCutMode without bIsClip");
+ return false;
+ }
+}
+
+void ScDocument::CopyToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
+ const ScMarkData* pMarks, bool bColRowFlags )
+{
+ if (ValidTab(nTab1) && ValidTab(nTab2))
+ {
+ ScRange aThisRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
+ CopyToDocument(aThisRange, nFlags, bOnlyMarked, rDestDoc, pMarks, bColRowFlags);
+ }
+}
+
+void ScDocument::UndoToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
+ InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
+{
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+ PutInOrder( nTab1, nTab2 );
+ if (!(ValidTab(nTab1) && ValidTab(nTab2)))
+ return;
+
+ sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
+
+ if (nTab1 > 0)
+ CopyToDocument(0, 0, 0, MaxCol(), MaxRow(), nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
+
+ sc::CopyToDocContext aCxt(rDestDoc);
+ assert( nTab2 < static_cast<SCTAB>(maTabs.size()) && nTab2 < static_cast<SCTAB>(rDestDoc.maTabs.size()));
+ for (SCTAB i = nTab1; i <= nTab2; i++)
+ {
+ if (maTabs[i] && rDestDoc.maTabs[i])
+ maTabs[i]->UndoToTable(aCxt, nCol1, nRow1, nCol2, nRow2, nFlags,
+ bOnlyMarked, rDestDoc.maTabs[i].get());
+ }
+
+ if (nTab2 < MAXTAB)
+ CopyToDocument(0, 0, nTab2+1, MaxCol(), MaxRow(), MAXTAB, InsertDeleteFlags::FORMULA, false, rDestDoc);
+}
+
+void ScDocument::CopyToDocument(const ScRange& rRange,
+ InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc,
+ const ScMarkData* pMarks, bool bColRowFlags)
+{
+ ScRange aNewRange = rRange;
+ aNewRange.PutInOrder();
+
+ if (rDestDoc.aDocName.isEmpty())
+ rDestDoc.aDocName = aDocName;
+
+ sc::AutoCalcSwitch aACSwitch(rDestDoc, false); // avoid multiple calculations
+ ScBulkBroadcast aBulkBroadcast(rDestDoc.GetBASM(), SfxHintId::ScDataChanged);
+ sc::DelayDeletingBroadcasters delayDeletingBroadcasters(*this);
+
+ sc::CopyToDocContext aCxt(rDestDoc);
+ aCxt.setStartListening(false);
+
+ SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size()));
+ for (SCTAB i = aNewRange.aStart.Tab(); i <= aNewRange.aEnd.Tab() && i < nMinSizeBothTabs; i++)
+ {
+ ScTable* pTab = FetchTable(i);
+ ScTable* pDestTab = rDestDoc.FetchTable(i);
+ if (!pTab || !pDestTab)
+ continue;
+
+ pTab->CopyToTable(
+ aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(), aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
+ nFlags, bOnlyMarked, pDestTab, pMarks, false, bColRowFlags,
+ /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true);
+ }
+
+ rDestDoc.StartAllListeners(aNewRange);
+}
+
+void ScDocument::UndoToDocument(const ScRange& rRange,
+ InsertDeleteFlags nFlags, bool bOnlyMarked, ScDocument& rDestDoc)
+{
+ sc::AutoCalcSwitch aAutoCalcSwitch(*this, false);
+
+ ScRange aNewRange = rRange;
+ aNewRange.PutInOrder();
+ SCTAB nTab1 = aNewRange.aStart.Tab();
+ SCTAB nTab2 = aNewRange.aEnd.Tab();
+
+ sc::CopyToDocContext aCxt(rDestDoc);
+ if (nTab1 > 0)
+ CopyToDocument(0, 0, 0, MaxCol(), MaxRow(), nTab1-1, InsertDeleteFlags::FORMULA, false, rDestDoc);
+
+ SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rDestDoc.maTabs.size()));
+ for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
+ {
+ if (maTabs[i] && rDestDoc.maTabs[i])
+ maTabs[i]->UndoToTable(aCxt, aNewRange.aStart.Col(), aNewRange.aStart.Row(),
+ aNewRange.aEnd.Col(), aNewRange.aEnd.Row(),
+ nFlags, bOnlyMarked, rDestDoc.maTabs[i].get());
+ }
+
+ if (nTab2 < static_cast<SCTAB>(maTabs.size()))
+ CopyToDocument(0, 0 , nTab2+1, MaxCol(), MaxRow(), maTabs.size(), InsertDeleteFlags::FORMULA, false, rDestDoc);
+}
+
+void ScDocument::CopyToClip(const ScClipParam& rClipParam,
+ ScDocument* pClipDoc, const ScMarkData* pMarks,
+ bool bKeepScenarioFlags, bool bIncludeObjects )
+{
+ OSL_ENSURE( pMarks, "CopyToClip: ScMarkData fails" );
+
+ if (bIsClip)
+ return;
+
+ if (!pClipDoc)
+ {
+ SAL_WARN("sc", "CopyToClip: no ClipDoc");
+ pClipDoc = ScModule::GetClipDoc();
+ }
+
+ if (mpShell->GetMedium())
+ {
+ pClipDoc->maFileURL = mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ // for unsaved files use the title name and adjust during save of file
+ if (pClipDoc->maFileURL.isEmpty())
+ pClipDoc->maFileURL = mpShell->GetName();
+ }
+ else
+ {
+ pClipDoc->maFileURL = mpShell->GetName();
+ }
+
+ //init maTabNames
+ for (const auto& rxTab : maTabs)
+ {
+ if( rxTab )
+ {
+ OUString aTabName = rxTab->GetName();
+ pClipDoc->maTabNames.push_back(aTabName);
+ }
+ else
+ pClipDoc->maTabNames.emplace_back();
+ }
+
+ pClipDoc->aDocName = aDocName;
+ pClipDoc->SetClipParam(rClipParam);
+ ScRange aClipRange = rClipParam.getWholeRange();
+ SCTAB nEndTab = static_cast<SCTAB>(maTabs.size());
+
+ pClipDoc->ResetClip(this, pMarks);
+
+ sc::CopyToClipContext aCxt(*pClipDoc, bKeepScenarioFlags);
+ CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks);
+
+ for (SCTAB i = 0; i < nEndTab; ++i)
+ {
+ if (!maTabs[i] || i >= static_cast<SCTAB>(pClipDoc->maTabs.size()) || !pClipDoc->maTabs[i])
+ continue;
+
+ if ( pMarks && !pMarks->GetTableSelect(i) )
+ continue;
+
+ maTabs[i]->CopyToClip(aCxt, rClipParam.maRanges, pClipDoc->maTabs[i].get());
+
+ if (mpDrawLayer && bIncludeObjects)
+ {
+ // also copy drawing objects
+ tools::Rectangle aObjRect = GetMMRect(
+ aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i);
+ mpDrawLayer->CopyToClip(pClipDoc, i, aObjRect);
+ }
+ }
+
+ // Make sure to mark overlapped cells.
+ pClipDoc->ExtendMerge(aClipRange, true);
+}
+
+void ScDocument::CopyStaticToDocument(const ScRange& rSrcRange, SCTAB nDestTab, ScDocument& rDestDoc)
+{
+ ScTable* pSrcTab = rSrcRange.aStart.Tab() < static_cast<SCTAB>(maTabs.size()) ? maTabs[rSrcRange.aStart.Tab()].get() : nullptr;
+ ScTable* pDestTab = nDestTab < static_cast<SCTAB>(rDestDoc.maTabs.size()) ? rDestDoc.maTabs[nDestTab].get() : nullptr;
+
+ if (!pSrcTab || !pDestTab)
+ return;
+
+ rDestDoc.GetFormatTable()->MergeFormatter(*GetFormatTable());
+ SvNumberFormatterMergeMap aMap = rDestDoc.GetFormatTable()->ConvertMergeTableToMap();
+
+ pSrcTab->CopyStaticToDocument(
+ rSrcRange.aStart.Col(), rSrcRange.aStart.Row(), rSrcRange.aEnd.Col(), rSrcRange.aEnd.Row(),
+ aMap, pDestTab);
+}
+
+void ScDocument::CopyCellToDocument( const ScAddress& rSrcPos, const ScAddress& rDestPos, ScDocument& rDestDoc )
+{
+ if (!TableExists(rSrcPos.Tab()) || !rDestDoc.TableExists(rDestPos.Tab()))
+ return;
+
+ ScTable& rSrcTab = *maTabs[rSrcPos.Tab()];
+ ScTable& rDestTab = *rDestDoc.maTabs[rDestPos.Tab()];
+
+ rSrcTab.CopyCellToDocument(rSrcPos.Col(), rSrcPos.Row(), rDestPos.Col(), rDestPos.Row(), rDestTab);
+}
+
+void ScDocument::CopyTabToClip(SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2,
+ SCTAB nTab, ScDocument* pClipDoc)
+{
+ if (bIsClip)
+ return;
+
+ if (!pClipDoc)
+ {
+ SAL_WARN("sc", "CopyTabToClip: no ClipDoc");
+ pClipDoc = ScModule::GetClipDoc();
+ }
+
+ if (mpShell->GetMedium())
+ {
+ pClipDoc->maFileURL = mpShell->GetMedium()->GetURLObject().GetMainURL(INetURLObject::DecodeMechanism::ToIUri);
+ // for unsaved files use the title name and adjust during save of file
+ if (pClipDoc->maFileURL.isEmpty())
+ pClipDoc->maFileURL = mpShell->GetName();
+ }
+ else
+ {
+ pClipDoc->maFileURL = mpShell->GetName();
+ }
+
+ //init maTabNames
+ for (const auto& rxTab : maTabs)
+ {
+ if( rxTab )
+ {
+ OUString aTabName = rxTab->GetName();
+ pClipDoc->maTabNames.push_back(aTabName);
+ }
+ else
+ pClipDoc->maTabNames.emplace_back();
+ }
+
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ pClipDoc->aDocName = aDocName;
+ rClipParam.maRanges.RemoveAll();
+ rClipParam.maRanges.push_back(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0));
+ pClipDoc->ResetClip( this, nTab );
+
+ sc::CopyToClipContext aCxt(*pClipDoc, false);
+ if (nTab < static_cast<SCTAB>(maTabs.size()) && nTab < static_cast<SCTAB>(pClipDoc->maTabs.size()))
+ if (maTabs[nTab] && pClipDoc->maTabs[nTab])
+ maTabs[nTab]->CopyToClip(aCxt, nCol1, nRow1, nCol2, nRow2, pClipDoc->maTabs[nTab].get());
+
+ pClipDoc->GetClipParam().mbCutMode = false;
+}
+
+void ScDocument::TransposeClip(ScDocument* pTransClip, InsertDeleteFlags nFlags, bool bAsLink,
+ bool bIncludeFiltered)
+{
+ OSL_ENSURE( bIsClip && pTransClip && pTransClip->bIsClip,
+ "TransposeClip with wrong Document" );
+
+ // initialize
+ // -> pTransClip has to be deleted before the original document!
+
+ pTransClip->ResetClip(this, nullptr); // all
+
+ // Take over range
+
+ if (pRangeName)
+ {
+ pTransClip->GetRangeName()->clear();
+ for (const auto& rEntry : *pRangeName)
+ {
+ sal_uInt16 nIndex = rEntry.second->GetIndex();
+ ScRangeData* pData = new ScRangeData(*rEntry.second);
+ if (pTransClip->pRangeName->insert(pData))
+ pData->SetIndex(nIndex);
+ }
+ }
+
+ ScRange aCombinedClipRange = GetClipParam().getWholeRange();
+
+ if (!ValidRow(aCombinedClipRange.aEnd.Row() - aCombinedClipRange.aStart.Row()))
+ {
+ SAL_WARN("sc", "TransposeClip: Too big");
+ return;
+ }
+
+ // Transpose of filtered multi range row selection is a special case since filtering
+ // and selection are in the same dimension (i.e. row).
+ // The filtered row status and the selection ranges are not available at the same time,
+ // handle this case specially, do not use GetClipParam().getWholeRange(),
+ // instead loop through the ranges, calculate the row offset and handle filtered rows and
+ // create in ScClipParam::transpose() a unified range.
+ const bool bIsMultiRangeRowFilteredTranspose
+ = !bIncludeFiltered && GetClipParam().isMultiRange()
+ && HasFilteredRows(aCombinedClipRange.aStart.Row(), aCombinedClipRange.aEnd.Row(),
+ aCombinedClipRange.aStart.Tab())
+ && GetClipParam().meDirection == ScClipParam::Row;
+
+ ScRangeList aClipRanges;
+ if (bIsMultiRangeRowFilteredTranspose)
+ aClipRanges = GetClipParam().maRanges;
+ else
+ aClipRanges = ScRangeList(aCombinedClipRange);
+
+ // The data
+ ScRange aClipRange;
+ SCROW nRowCount = 0; // next consecutive row
+ for (size_t j = 0, n = aClipRanges.size(); j < n; ++j)
+ {
+ aClipRange = aClipRanges[j];
+
+ SCROW nRowOffset = 0;
+ if (bIsMultiRangeRowFilteredTranspose)
+ {
+ // adjust for the rows that are filtered
+ nRowOffset = nRowCount;
+
+ // calculate filtered rows of current clip range
+ SCROW nRowCountNonFiltered = CountNonFilteredRows(
+ aClipRange.aStart.Row(), aClipRange.aEnd.Row(), aClipRange.aStart.Tab());
+ assert(!bIncludeFiltered && "bIsMultiRangeRowFilteredTranspose can only be true if bIncludeFiltered is false");
+ nRowCount += nRowCountNonFiltered; // for next iteration
+ }
+
+ for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); i++)
+ {
+ if (maTabs[i])
+ {
+ OSL_ENSURE(pTransClip->maTabs[i], "TransposeClip: Table not there");
+ maTabs[i]->TransposeClip(
+ aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(),
+ aClipRange.aEnd.Row(), aCombinedClipRange.aStart.Row(), nRowOffset,
+ pTransClip->maTabs[i].get(), nFlags, bAsLink, bIncludeFiltered);
+
+ if ( mpDrawLayer && ( nFlags & InsertDeleteFlags::OBJECTS ) )
+ {
+ // Drawing objects are copied to the new area without transposing.
+ // CopyFromClip is used to adjust the objects to the transposed block's
+ // cell range area.
+ // (mpDrawLayer in the original clipboard document is set only if there
+ // are drawing objects to copy)
+
+ pTransClip->InitDrawLayer();
+ tools::Rectangle aSourceRect = GetMMRect( aClipRange.aStart.Col(), aClipRange.aStart.Row(),
+ aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i );
+ tools::Rectangle aDestRect = pTransClip->GetMMRect( 0, 0,
+ static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row()),
+ static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col()), i );
+ pTransClip->mpDrawLayer->CopyFromClip( mpDrawLayer.get(), i, aSourceRect, ScAddress(0,0,i), aDestRect );
+ }
+ }
+ }
+ }
+
+ pTransClip->SetClipParam(GetClipParam());
+ pTransClip->GetClipParam().transpose(*this, bIncludeFiltered,
+ bIsMultiRangeRowFilteredTranspose);
+
+ // This happens only when inserting...
+
+ GetClipParam().mbCutMode = false;
+}
+
+namespace {
+
+void copyUsedNamesToClip(ScRangeName* pClipRangeName, ScRangeName* pRangeName,
+ const sc::UpdatedRangeNames::NameIndicesType& rUsedNames)
+{
+ pClipRangeName->clear();
+ for (const auto& rEntry : *pRangeName) //TODO: also DB and Pivot regions!!!
+ {
+ sal_uInt16 nIndex = rEntry.second->GetIndex();
+ bool bInUse = (rUsedNames.count(nIndex) > 0);
+ if (!bInUse)
+ continue;
+
+ ScRangeData* pData = new ScRangeData(*rEntry.second);
+ if (pClipRangeName->insert(pData))
+ pData->SetIndex(nIndex);
+ }
+}
+
+}
+
+void ScDocument::CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks)
+{
+ if (!pRangeName || pRangeName->empty())
+ return;
+
+ sc::UpdatedRangeNames aUsedNames; // indexes of named ranges that are used in the copied cells
+ SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), pClipDoc->maTabs.size()));
+ for (SCTAB i = 0; i < nMinSizeBothTabs; ++i)
+ if (maTabs[i] && pClipDoc->maTabs[i])
+ if ( !pMarks || pMarks->GetTableSelect(i) )
+ maTabs[i]->FindRangeNamesInUse(
+ rClipRange.aStart.Col(), rClipRange.aStart.Row(),
+ rClipRange.aEnd.Col(), rClipRange.aEnd.Row(), aUsedNames);
+
+ /* TODO: handle also sheet-local names */
+ sc::UpdatedRangeNames::NameIndicesType aUsedGlobalNames( aUsedNames.getUpdatedNames(-1));
+ copyUsedNamesToClip(pClipDoc->GetRangeName(), pRangeName.get(), aUsedGlobalNames);
+}
+
+ScDocument::NumFmtMergeHandler::NumFmtMergeHandler(ScDocument& rDoc, const ScDocument& rSrcDoc)
+ : mrDoc(rDoc)
+{
+ mrDoc.MergeNumberFormatter(rSrcDoc);
+}
+
+ScDocument::NumFmtMergeHandler::~NumFmtMergeHandler()
+{
+ ScMutationGuard aGuard(mrDoc, ScMutationGuardFlags::CORE);
+ mrDoc.pFormatExchangeList = nullptr;
+}
+
+void ScDocument::PrepareFormulaCalc()
+{
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ mpFormulaGroupCxt.reset();
+}
+
+SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos )
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return nullptr;
+
+ return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
+}
+
+const SvtBroadcaster* ScDocument::GetBroadcaster( const ScAddress& rPos ) const
+{
+ const ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return nullptr;
+
+ return pTab->GetBroadcaster(rPos.Col(), rPos.Row());
+}
+
+void ScDocument::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, const ScAddress& rTopPos, SCROW nLength )
+{
+ ScTable* pTab = FetchTable(rTopPos.Tab());
+ if (!pTab || nLength <= 0)
+ return;
+
+ pTab->DeleteBroadcasters(rBlockPos, rTopPos.Col(), rTopPos.Row(), rTopPos.Row()+nLength-1);
+}
+
+#if DUMP_COLUMN_STORAGE
+void ScDocument::DumpColumnStorage( SCTAB nTab, SCCOL nCol ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->DumpColumnStorage(nCol);
+}
+#endif
+
+#if DEBUG_AREA_BROADCASTER
+void ScDocument::DumpAreaBroadcasters() const
+{
+ if (pBASM)
+ pBASM->Dump();
+}
+#endif
+
+bool ScDocument::TableExists( SCTAB nTab ) const
+{
+ return ValidTab(nTab) && o3tl::make_unsigned(nTab) < maTabs.size() && maTabs[nTab];
+}
+
+ScTable* ScDocument::FetchTable( SCTAB nTab )
+{
+ if (!TableExists(nTab))
+ return nullptr;
+
+ return maTabs[nTab].get();
+}
+
+const ScTable* ScDocument::FetchTable( SCTAB nTab ) const
+{
+ if (!TableExists(nTab))
+ return nullptr;
+
+ return maTabs[nTab].get();
+}
+
+ScColumnsRange ScDocument::GetWritableColumnsRange( SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd)
+{
+ if (!TableExists(nTab))
+ {
+ SAL_WARN("sc", "GetWritableColumnsRange() called for non-existent table");
+ return ScColumnsRange(-1, -1);
+ }
+ return maTabs[nTab]->GetWritableColumnsRange(nColBegin, nColEnd);
+}
+
+ScColumnsRange ScDocument::GetAllocatedColumnsRange( SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd) const
+{
+ if (!TableExists(nTab))
+ return ScColumnsRange(-1, -1);
+ return maTabs[nTab]->GetAllocatedColumnsRange(nColBegin, nColEnd);
+}
+
+ScColumnsRange ScDocument::GetColumnsRange( SCTAB nTab, SCCOL nColBegin, SCCOL nColEnd) const
+{
+ if (!TableExists(nTab))
+ return ScColumnsRange(-1, -1);
+ return maTabs[nTab]->GetColumnsRange(nColBegin, nColEnd);
+}
+
+void ScDocument::MergeNumberFormatter(const ScDocument& rSrcDoc)
+{
+ SvNumberFormatter* pThisFormatter = mxPoolHelper->GetFormTable();
+ SvNumberFormatter* pOtherFormatter = rSrcDoc.mxPoolHelper->GetFormTable();
+ if (pOtherFormatter && pOtherFormatter != pThisFormatter)
+ {
+ SvNumberFormatterIndexTable* pExchangeList =
+ pThisFormatter->MergeFormatter(*pOtherFormatter);
+ if (!pExchangeList->empty())
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pFormatExchangeList = pExchangeList;
+ }
+ }
+}
+
+ScClipParam& ScDocument::GetClipParam()
+{
+ if (!mpClipParam)
+ mpClipParam.reset(new ScClipParam);
+
+ return *mpClipParam;
+}
+
+void ScDocument::SetClipParam(const ScClipParam& rParam)
+{
+ mpClipParam.reset(new ScClipParam(rParam));
+}
+
+bool ScDocument::IsClipboardSource() const
+{
+ if (bIsClip || mpShell == nullptr || mpShell->IsLoading())
+ return false;
+
+ ScDocument* pClipDoc = ScModule::GetClipDoc();
+ return pClipDoc && pClipDoc->bIsClip && pClipDoc->mxPoolHelper.is() && mxPoolHelper.is() &&
+ mxPoolHelper->GetDocPool() == pClipDoc->mxPoolHelper->GetDocPool();
+}
+
+void ScDocument::StartListeningFromClip(
+ sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
+ SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->StartListeningFormulaCells(rStartCxt, rEndCxt, nCol1, nRow1, nCol2, nRow2);
+}
+
+void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark, InsertDeleteFlags nInsFlag )
+{
+ if (!(nInsFlag & InsertDeleteFlags::CONTENTS))
+ return;
+
+ auto pSet = std::make_shared<sc::ColumnBlockPositionSet>(*this);
+
+ sc::StartListeningContext aStartCxt(*this, pSet);
+ sc::EndListeningContext aEndCxt(*this, pSet, nullptr);
+
+ for (SCTAB nTab : rMark)
+ StartListeningFromClip(aStartCxt, aEndCxt, nTab, nCol1, nRow1, nCol2, nRow2);
+}
+
+void ScDocument::SetDirtyFromClip(
+ SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
+ InsertDeleteFlags nInsFlag, sc::ColumnSpanSet& rBroadcastSpans )
+{
+ if (nInsFlag & InsertDeleteFlags::CONTENTS)
+ {
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->SetDirtyFromClip(nCol1, nRow1, nCol2, nRow2, rBroadcastSpans);
+ }
+ }
+}
+
+bool ScDocument::InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCTAB nTab, SCCOL nCol )
+{
+ if (!TableExists(nTab))
+ return false;
+
+ return maTabs[nTab]->InitColumnBlockPosition(rBlockPos, nCol);
+}
+
+void ScDocument::CopyBlockFromClip(
+ sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
+ const ScMarkData& rMark, SCCOL nDx, SCROW nDy )
+{
+ TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
+ SCTAB nTabEnd = rCxt.getTabEnd();
+ SCTAB nClipTab = 0;
+ for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); i++)
+ {
+ if (maTabs[i] && rMark.GetTableSelect(i) )
+ {
+ while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
+
+ maTabs[i]->CopyFromClip(
+ rCxt, nCol1, nRow1, nCol2, nRow2, nDx, nDy, rClipTabs[nClipTab].get());
+
+ if (rCxt.getClipDoc()->mpDrawLayer && (rCxt.getInsertFlag() & InsertDeleteFlags::OBJECTS))
+ {
+ // also copy drawing objects
+
+ // drawing layer must be created before calling CopyFromClip
+ // (ScDocShell::MakeDrawLayer also does InitItems etc.)
+ OSL_ENSURE( mpDrawLayer, "CopyBlockFromClip: No drawing layer" );
+ if ( mpDrawLayer )
+ {
+ // For GetMMRect, the row heights in the target document must already be valid
+ // (copied in an extra step before pasting, or updated after pasting cells, but
+ // before pasting objects).
+
+ tools::Rectangle aSourceRect = rCxt.getClipDoc()->GetMMRect(
+ nCol1-nDx, nRow1-nDy, nCol2-nDx, nRow2-nDy, nClipTab );
+ tools::Rectangle aDestRect = GetMMRect( nCol1, nRow1, nCol2, nRow2, i );
+ mpDrawLayer->CopyFromClip(rCxt.getClipDoc()->mpDrawLayer.get(), nClipTab, aSourceRect,
+ ScAddress( nCol1, nRow1, i ), aDestRect );
+ }
+ }
+
+ nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
+ }
+ }
+ if (!(rCxt.getInsertFlag() & InsertDeleteFlags::CONTENTS))
+ return;
+
+ nClipTab = 0;
+ for (SCTAB i = rCxt.getTabStart(); i <= nTabEnd && i < static_cast<SCTAB>(maTabs.size()); i++)
+ {
+ if (maTabs[i] && rMark.GetTableSelect(i) )
+ {
+ while (!rClipTabs[nClipTab]) nClipTab = (nClipTab+1) % static_cast<SCTAB>(rClipTabs.size());
+ SCTAB nDz = i - nClipTab;
+
+ // ranges of consecutive selected tables (in clipboard and dest. doc)
+ // must be handled in one UpdateReference call
+ SCTAB nFollow = 0;
+ while ( i + nFollow < nTabEnd
+ && rMark.GetTableSelect( i + nFollow + 1 )
+ && nClipTab + nFollow < MAXTAB
+ && rClipTabs[(nClipTab + nFollow + 1) % static_cast<SCTAB>(rClipTabs.size())] )
+ ++nFollow;
+
+ sc::RefUpdateContext aRefCxt(*this, rCxt.getClipDoc());
+ aRefCxt.maRange = ScRange(nCol1, nRow1, i, nCol2, nRow2, i+nFollow);
+ aRefCxt.mnColDelta = nDx;
+ aRefCxt.mnRowDelta = nDy;
+ aRefCxt.mnTabDelta = nDz;
+ aRefCxt.setBlockPositionReference(rCxt.getBlockPositionSet()); // share mdds position caching
+ if (rCxt.getClipDoc()->GetClipParam().mbCutMode)
+ {
+ // Update references only if cut originates from the same
+ // document we are pasting into.
+ if (rCxt.getClipDoc()->GetPool() == GetPool())
+ {
+ bool bOldInserting = IsInsertingFromOtherDoc();
+ SetInsertingFromOtherDoc( true);
+ aRefCxt.meMode = URM_MOVE;
+ UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
+
+ // For URM_MOVE group listeners may have been removed,
+ // re-establish them.
+ if (!aRefCxt.maRegroupCols.empty())
+ {
+ /* TODO: holding the ColumnSet in a shared_ptr at
+ * RefUpdateContext would eliminate the need of
+ * copying it here. */
+ auto pColSet = std::make_shared<sc::ColumnSet>( aRefCxt.maRegroupCols);
+ StartNeededListeners( pColSet);
+ }
+
+ SetInsertingFromOtherDoc( bOldInserting);
+ }
+ }
+ else
+ {
+ aRefCxt.meMode = URM_COPY;
+ UpdateReference(aRefCxt, rCxt.getUndoDoc(), false);
+ }
+
+ nClipTab = (nClipTab+nFollow+1) % static_cast<SCTAB>(rClipTabs.size());
+ i = sal::static_int_cast<SCTAB>( i + nFollow );
+ }
+ }
+}
+
+SCROW ScDocument::CopyNonFilteredFromClip(sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1,
+ SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
+ SCCOL nDx, SCROW& rClipStartRow, SCROW nClipEndRow)
+{
+ // call CopyBlockFromClip for ranges of consecutive non-filtered rows
+ // nCol1/nRow1 etc. is in target doc
+
+ // filtered state is taken from first used table in clipboard (as in GetClipArea)
+ SCTAB nFlagTab = 0;
+ TableContainer& rClipTabs = rCxt.getClipDoc()->maTabs;
+ while ( nFlagTab < static_cast<SCTAB>(rClipTabs.size()) && !rClipTabs[nFlagTab] )
+ ++nFlagTab;
+
+ SCROW nSourceRow = rClipStartRow;
+ SCROW nSourceEnd = nClipEndRow;
+ SCROW nDestRow = nRow1;
+ SCROW nFilteredRows = 0;
+
+ while ( nSourceRow <= nSourceEnd && nDestRow <= nRow2 )
+ {
+ // skip filtered rows
+ SCROW nSourceRowOriginal = nSourceRow;
+ nSourceRow = rCxt.getClipDoc()->FirstNonFilteredRow(nSourceRow, nSourceEnd, nFlagTab);
+ nFilteredRows += nSourceRow - nSourceRowOriginal;
+
+ if ( nSourceRow <= nSourceEnd )
+ {
+ // look for more non-filtered rows following
+ SCROW nLastRow = nSourceRow;
+ (void)rCxt.getClipDoc()->RowFiltered(nSourceRow, nFlagTab, nullptr, &nLastRow);
+ SCROW nFollow = nLastRow - nSourceRow;
+
+ if (nFollow > nSourceEnd - nSourceRow)
+ nFollow = nSourceEnd - nSourceRow;
+ if (nFollow > nRow2 - nDestRow)
+ nFollow = nRow2 - nDestRow;
+
+ SCROW nNewDy = nDestRow - nSourceRow;
+ CopyBlockFromClip(
+ rCxt, nCol1, nDestRow, nCol2, nDestRow + nFollow, rMark, nDx, nNewDy);
+
+ nSourceRow += nFollow + 1;
+ nDestRow += nFollow + 1;
+ }
+ }
+ rClipStartRow = nSourceRow;
+ return nFilteredRows;
+}
+
+namespace {
+
+class BroadcastAction : public sc::ColumnSpanSet::ColumnAction
+{
+ ScDocument& mrDoc;
+ ScColumn* mpCol;
+
+public:
+ explicit BroadcastAction( ScDocument& rDoc ) : mrDoc(rDoc), mpCol(nullptr) {}
+
+ virtual void startColumn( ScColumn* pCol ) override
+ {
+ mpCol = pCol;
+ }
+
+ virtual void execute( SCROW nRow1, SCROW nRow2, bool bVal ) override
+ {
+ if (!bVal)
+ return;
+
+ assert(mpCol);
+ ScRange aRange(mpCol->GetCol(), nRow1, mpCol->GetTab());
+ aRange.aEnd.SetRow(nRow2);
+ mrDoc.BroadcastCells(aRange, SfxHintId::ScDataChanged);
+ };
+};
+
+}
+
+void ScDocument::CopyFromClip(
+ const ScRange& rDestRange, const ScMarkData& rMark, InsertDeleteFlags nInsFlag,
+ ScDocument* pRefUndoDoc, ScDocument* pClipDoc, bool bResetCut,
+ bool bAsLink, bool bIncludeFiltered, bool bSkipEmptyCells,
+ const ScRangeList * pDestRanges )
+{
+ if (bIsClip)
+ return;
+
+ if (!pClipDoc)
+ {
+ OSL_FAIL("CopyFromClip: no ClipDoc");
+ pClipDoc = ScModule::GetClipDoc();
+ }
+
+ if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
+ return;
+
+ sc::AutoCalcSwitch aACSwitch(*this, false); // temporarily turn off auto calc.
+
+ NumFmtMergeHandler aNumFmtMergeHdl(*this, *pClipDoc);
+
+ SCCOL nAllCol1 = rDestRange.aStart.Col();
+ SCROW nAllRow1 = rDestRange.aStart.Row();
+ SCCOL nAllCol2 = rDestRange.aEnd.Col();
+ SCROW nAllRow2 = rDestRange.aEnd.Row();
+
+ SCCOL nXw = 0;
+ SCROW nYw = 0;
+ ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange();
+ for (SCTAB nTab = 0; nTab < static_cast<SCTAB>(pClipDoc->maTabs.size()); nTab++) // find largest merge overlap
+ if (pClipDoc->maTabs[nTab]) // all sheets of the clipboard content
+ {
+ SCCOL nThisEndX = aClipRange.aEnd.Col();
+ SCROW nThisEndY = aClipRange.aEnd.Row();
+ pClipDoc->ExtendMerge( aClipRange.aStart.Col(),
+ aClipRange.aStart.Row(),
+ nThisEndX, nThisEndY, nTab );
+ // only extra value from ExtendMerge
+ nThisEndX = sal::static_int_cast<SCCOL>( nThisEndX - aClipRange.aEnd.Col() );
+ nThisEndY = sal::static_int_cast<SCROW>( nThisEndY - aClipRange.aEnd.Row() );
+ if ( nThisEndX > nXw )
+ nXw = nThisEndX;
+ if ( nThisEndY > nYw )
+ nYw = nThisEndY;
+ }
+
+ SCCOL nDestAddX;
+ SCROW nDestAddY;
+ pClipDoc->GetClipArea( nDestAddX, nDestAddY, bIncludeFiltered );
+ nXw = sal::static_int_cast<SCCOL>( nXw + nDestAddX );
+ nYw = sal::static_int_cast<SCROW>( nYw + nDestAddY ); // ClipArea, plus ExtendMerge value
+
+ /* Decide which contents to delete before copying. Delete all
+ contents if nInsFlag contains any real content flag.
+ #i102056# Notes are pasted from clipboard in a second pass,
+ together with the special flag InsertDeleteFlags::ADDNOTES that states to not
+ overwrite/delete existing cells but to insert the notes into
+ these cells. In this case, just delete old notes from the
+ destination area. */
+ InsertDeleteFlags nDelFlag = InsertDeleteFlags::NONE;
+ if ( (nInsFlag & (InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ADDNOTES)) == (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES) )
+ nDelFlag |= InsertDeleteFlags::NOTE;
+ else if ( nInsFlag & InsertDeleteFlags::CONTENTS )
+ nDelFlag |= InsertDeleteFlags::CONTENTS;
+
+ if (nInsFlag & InsertDeleteFlags::ATTRIB)
+ nDelFlag |= InsertDeleteFlags::ATTRIB;
+
+ sc::CopyFromClipContext aCxt(*this, pRefUndoDoc, pClipDoc, nInsFlag, bAsLink, bSkipEmptyCells);
+ std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
+ aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
+ aCxt.setDeleteFlag(nDelFlag);
+
+ ScRangeList aLocalRangeList;
+ if (!pDestRanges)
+ {
+ aLocalRangeList.push_back( rDestRange);
+ pDestRanges = &aLocalRangeList;
+ }
+
+ bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert
+
+ sc::ColumnSpanSet aBroadcastSpans;
+
+ SCCOL nClipStartCol = aClipRange.aStart.Col();
+ SCROW nClipStartRow = aClipRange.aStart.Row();
+ SCROW nClipEndRow = aClipRange.aEnd.Row();
+ for ( size_t nRange = 0; nRange < pDestRanges->size(); ++nRange )
+ {
+ const ScRange & rRange = (*pDestRanges)[nRange];
+ SCCOL nCol1 = rRange.aStart.Col();
+ SCROW nRow1 = rRange.aStart.Row();
+ SCCOL nCol2 = rRange.aEnd.Col();
+ SCROW nRow2 = rRange.aEnd.Row();
+
+ aCxt.setDestRange(nCol1, nRow1, nCol2, nRow2);
+ DeleteBeforeCopyFromClip(aCxt, rMark, aBroadcastSpans); // <- this removes existing formula listeners
+
+ if (CopyOneCellFromClip(aCxt, nCol1, nRow1, nCol2, nRow2))
+ continue;
+
+ SCCOL nC1 = nCol1;
+ SCROW nR1 = nRow1;
+ SCCOL nC2 = nC1 + nXw;
+ if (nC2 > nCol2)
+ nC2 = nCol2;
+ SCROW nR2 = nR1 + nYw;
+ if (nR2 > nRow2)
+ nR2 = nRow2;
+
+ const SCCOLROW nThreshold = 8192;
+ bool bPreallocatePattern = ((nInsFlag & InsertDeleteFlags::ATTRIB) && (nRow2 - nRow1 > nThreshold));
+ std::vector< SCTAB > vTables;
+
+ if (bPreallocatePattern)
+ {
+ for (SCTAB i = aCxt.getTabStart(); i <= aCxt.getTabEnd(); ++i)
+ if (maTabs[i] && rMark.GetTableSelect( i ) )
+ vTables.push_back( i );
+ }
+
+ do
+ {
+ // Pasting is done column-wise, when pasting to a filtered
+ // area this results in partitioning and we have to
+ // remember and reset the start row for each column until
+ // it can be advanced for the next chunk of unfiltered
+ // rows.
+ SCROW nSaveClipStartRow = nClipStartRow;
+ do
+ {
+ nClipStartRow = nSaveClipStartRow;
+ SCCOL nDx = nC1 - nClipStartCol;
+ SCROW nDy = nR1 - nClipStartRow;
+ if ( bIncludeFiltered )
+ {
+ CopyBlockFromClip(
+ aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nDy);
+ nClipStartRow += nR2 - nR1 + 1;
+ }
+ else
+ {
+ CopyNonFilteredFromClip(aCxt, nC1, nR1, nC2, nR2, rMark, nDx, nClipStartRow,
+ nClipEndRow);
+ }
+ nC1 = nC2 + 1;
+ nC2 = std::min(static_cast<SCCOL>(nC1 + nXw), nCol2);
+ } while (nC1 <= nCol2);
+ if (nClipStartRow > nClipEndRow)
+ nClipStartRow = aClipRange.aStart.Row();
+ nC1 = nCol1;
+ nC2 = nC1 + nXw;
+ if (nC2 > nCol2)
+ nC2 = nCol2;
+
+ // Preallocate pattern memory once if further chunks are to be pasted.
+ if (bPreallocatePattern && (nR2+1) <= nRow2)
+ {
+ SCROW nR3 = nR2 + 1;
+ for (SCTAB nTab : vTables)
+ {
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ // Pattern count of the first chunk pasted.
+ SCSIZE nChunk = GetPatternCount( nTab, nCol, nR1, nR2);
+ // If it is only one pattern per chunk and chunks are
+ // pasted consecutively then it will get its range
+ // enlarged for each chunk and no further allocation
+ // happens. For non-consecutive chunks we're out of
+ // luck in this case.
+ if (nChunk > 1)
+ {
+ SCSIZE nNeeded = nChunk * (nRow2 - nR3 + 1) / (nYw + 1);
+ SCSIZE nRemain = GetPatternCount( nTab, nCol, nR3, nRow2);
+ if (nNeeded > nRemain)
+ {
+ SCSIZE nCurr = GetPatternCount( nTab, nCol);
+ ReservePatternCount( nTab, nCol, nCurr + nNeeded);
+ }
+ }
+ }
+ }
+ bPreallocatePattern = false;
+ }
+
+ nR1 = nR2 + 1;
+ nR2 = std::min(static_cast<SCROW>(nR1 + nYw), nRow2);
+ } while (nR1 <= nRow2);
+ }
+
+ bInsertingFromOtherDoc = false;
+
+ if (nInsFlag & InsertDeleteFlags::CONTENTS)
+ {
+ for (SCTAB nTab : rMark)
+ aCxt.setListeningFormulaSpans(nTab, nAllCol1, nAllRow1, nAllCol2, nAllRow2);
+ }
+
+ // Create Listener after everything has been inserted
+ aCxt.startListeningFormulas();
+
+ {
+ ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
+
+ // Set all formula cells dirty, and collect non-empty non-formula cell
+ // positions so that we can broadcast on them below.
+ SetDirtyFromClip(nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag, aBroadcastSpans);
+
+ BroadcastAction aAction(*this);
+ aBroadcastSpans.executeColumnAction(*this, aAction);
+ }
+
+ if (bResetCut)
+ pClipDoc->GetClipParam().mbCutMode = false;
+}
+
+void ScDocument::CopyMultiRangeFromClip(const ScAddress& rDestPos, const ScMarkData& rMark,
+ InsertDeleteFlags nInsFlag, ScDocument* pClipDoc,
+ bool bResetCut, bool bAsLink, bool bIncludeFiltered,
+ bool bSkipAttrForEmpty)
+{
+ if (bIsClip)
+ return;
+
+ if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount())
+ // There is nothing in the clip doc to copy.
+ return;
+
+ // Right now, we don't allow pasting into filtered rows, so we don't even handle it here.
+
+ sc::AutoCalcSwitch aACSwitch(*this, false); // turn of auto calc temporarily.
+ NumFmtMergeHandler aNumFmtMergeHdl(*this, *pClipDoc);
+
+ const ScRange& aDestRange = rMark.GetMarkArea();
+
+ bInsertingFromOtherDoc = true; // No Broadcast/Listener created at Insert
+
+ SCCOL nCol1 = rDestPos.Col();
+ SCROW nRow1 = rDestPos.Row();
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+
+ sc::ColumnSpanSet aBroadcastSpans;
+
+ if (!bSkipAttrForEmpty)
+ {
+ // Do the deletion first.
+ SCCOL nColSize = rClipParam.getPasteColSize();
+ SCROW nRowSize = rClipParam.getPasteRowSize(*pClipDoc, bIncludeFiltered);
+
+ DeleteArea(nCol1, nRow1, nCol1+nColSize-1, nRow1+nRowSize-1, rMark, InsertDeleteFlags::CONTENTS, false, &aBroadcastSpans);
+ }
+
+ sc::CopyFromClipContext aCxt(*this, nullptr, pClipDoc, nInsFlag, bAsLink, bSkipAttrForEmpty);
+ std::pair<SCTAB,SCTAB> aTabRanges = getMarkedTableRange(maTabs, rMark);
+ aCxt.setTabRange(aTabRanges.first, aTabRanges.second);
+
+ for (size_t i = 0, n = rClipParam.maRanges.size(); i < n; ++i)
+ {
+ const ScRange & rRange = rClipParam.maRanges[i];
+
+ SCROW nRowCount = rRange.aEnd.Row() - rRange.aStart.Row() + 1;
+ SCCOL nDx = static_cast<SCCOL>(nCol1 - rRange.aStart.Col());
+ SCROW nDy = static_cast<SCROW>(nRow1 - rRange.aStart.Row());
+ SCCOL nCol2 = nCol1 + rRange.aEnd.Col() - rRange.aStart.Col();
+ SCROW nEndRow = nRow1 + nRowCount - 1;
+ SCROW nFilteredRows = 0;
+
+ if (bIncludeFiltered)
+ {
+ CopyBlockFromClip(aCxt, nCol1, nRow1, nCol2, nEndRow, rMark, nDx, nDy);
+ }
+ else
+ {
+ SCROW nClipStartRow = rRange.aStart.Row();
+ SCROW nClipEndRow = rRange.aEnd.Row();
+ nFilteredRows += CopyNonFilteredFromClip(aCxt, nCol1, nRow1, nCol2, nEndRow, rMark, nDx,
+ nClipStartRow, nClipEndRow);
+ nRowCount -= nFilteredRows;
+ }
+
+ switch (rClipParam.meDirection)
+ {
+ case ScClipParam::Row:
+ // Begin row for the next range being pasted.
+ nRow1 += nRowCount;
+ break;
+ case ScClipParam::Column:
+ nCol1 += rRange.aEnd.Col() - rRange.aStart.Col() + 1;
+ break;
+ default:
+ ;
+ }
+ }
+
+ bInsertingFromOtherDoc = false;
+
+ // Create Listener after everything has been inserted
+ StartListeningFromClip(aDestRange.aStart.Col(), aDestRange.aStart.Row(),
+ aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), rMark, nInsFlag );
+
+ {
+ ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
+
+ // Set formula cells dirty and collect non-formula cells.
+ SetDirtyFromClip(
+ aDestRange.aStart.Col(), aDestRange.aStart.Row(), aDestRange.aEnd.Col(), aDestRange.aEnd.Row(),
+ rMark, nInsFlag, aBroadcastSpans);
+
+ BroadcastAction aAction(*this);
+ aBroadcastSpans.executeColumnAction(*this, aAction);
+ }
+
+ if (bResetCut)
+ pClipDoc->GetClipParam().mbCutMode = false;
+}
+
+void ScDocument::SetClipArea( const ScRange& rArea, bool bCut )
+{
+ if (bIsClip)
+ {
+ ScClipParam& rClipParam = GetClipParam();
+ rClipParam.maRanges.RemoveAll();
+ rClipParam.maRanges.push_back(rArea);
+ rClipParam.mbCutMode = bCut;
+ }
+ else
+ {
+ OSL_FAIL("SetClipArea: No Clip");
+ }
+}
+
+void ScDocument::GetClipArea(SCCOL& nClipX, SCROW& nClipY, bool bIncludeFiltered)
+{
+ if (!bIsClip)
+ {
+ OSL_FAIL("GetClipArea: No Clip");
+ return;
+ }
+
+ ScRangeList& rClipRanges = GetClipParam().maRanges;
+ if (rClipRanges.empty())
+ // No clip range. Bail out.
+ return;
+
+ ScRange const & rRange = rClipRanges.front();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCROW nEndRow = rRange.aEnd.Row();
+ for ( size_t i = 1, n = rClipRanges.size(); i < n; ++i )
+ {
+ ScRange const & rRange2 = rClipRanges[ i ];
+ if (rRange2.aStart.Col() < nStartCol)
+ nStartCol = rRange2.aStart.Col();
+ if (rRange2.aStart.Row() < nStartRow)
+ nStartRow = rRange2.aStart.Row();
+ if (rRange2.aEnd.Col() > nEndCol)
+ nEndCol = rRange2.aEnd.Col();
+ if (rRange2.aEnd.Row() > nEndRow)
+ nEndRow = rRange2.aEnd.Row();
+ }
+
+ nClipX = nEndCol - nStartCol;
+
+ if ( bIncludeFiltered )
+ nClipY = nEndRow - nStartRow;
+ else
+ {
+ // count non-filtered rows
+ // count on first used table in clipboard
+ SCTAB nCountTab = 0;
+ while ( nCountTab < static_cast<SCTAB>(maTabs.size()) && !maTabs[nCountTab] )
+ ++nCountTab;
+
+ SCROW nResult = CountNonFilteredRows(nStartRow, nEndRow, nCountTab);
+
+ if ( nResult > 0 )
+ nClipY = nResult - 1;
+ else
+ nClipY = 0; // always return at least 1 row
+ }
+}
+
+void ScDocument::GetClipStart(SCCOL& nClipX, SCROW& nClipY)
+{
+ if (bIsClip)
+ {
+ ScRangeList& rClipRanges = GetClipParam().maRanges;
+ if ( !rClipRanges.empty() )
+ {
+ nClipX = rClipRanges.front().aStart.Col();
+ nClipY = rClipRanges.front().aStart.Row();
+ }
+ }
+ else
+ {
+ OSL_FAIL("GetClipStart: No Clip");
+ }
+}
+
+bool ScDocument::HasClipFilteredRows()
+{
+ // count on first used table in clipboard
+ SCTAB nCountTab = 0;
+ while ( nCountTab < static_cast<SCTAB>(maTabs.size()) && !maTabs[nCountTab] )
+ ++nCountTab;
+
+ ScRangeList& rClipRanges = GetClipParam().maRanges;
+ if ( rClipRanges.empty() )
+ return false;
+
+ for ( size_t i = 0, n = rClipRanges.size(); i < n; ++i )
+ {
+ ScRange & rRange = rClipRanges[ i ];
+ bool bAnswer = maTabs[nCountTab]->HasFilteredRows(rRange.aStart.Row(), rRange.aEnd.Row());
+ if (bAnswer)
+ return true;
+ }
+ return false;
+}
+
+void ScDocument::MixDocument( const ScRange& rRange, ScPasteFunc nFunction, bool bSkipEmpty,
+ ScDocument& rSrcDoc )
+{
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ sc::MixDocContext aCxt(*this);
+ SCTAB nMinSizeBothTabs = static_cast<SCTAB>(std::min(maTabs.size(), rSrcDoc.maTabs.size()));
+ for (SCTAB i = nTab1; i <= nTab2 && i < nMinSizeBothTabs; i++)
+ {
+ ScTable* pTab = FetchTable(i);
+ const ScTable* pSrcTab = rSrcDoc.FetchTable(i);
+ if (!pTab || !pSrcTab)
+ continue;
+
+ pTab->MixData(
+ aCxt, rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(),
+ nFunction, bSkipEmpty, pSrcTab);
+ }
+}
+
+void ScDocument::FillTab( const ScRange& rSrcArea, const ScMarkData& rMark,
+ InsertDeleteFlags nFlags, ScPasteFunc nFunction,
+ bool bSkipEmpty, bool bAsLink )
+{
+ InsertDeleteFlags nDelFlags = nFlags;
+ if (nDelFlags & InsertDeleteFlags::CONTENTS)
+ nDelFlags |= InsertDeleteFlags::CONTENTS; // Either all contents or delete nothing!
+
+ SCTAB nSrcTab = rSrcArea.aStart.Tab();
+
+ if (ValidTab(nSrcTab) && nSrcTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nSrcTab])
+ {
+ SCCOL nStartCol = rSrcArea.aStart.Col();
+ SCROW nStartRow = rSrcArea.aStart.Row();
+ SCCOL nEndCol = rSrcArea.aEnd.Col();
+ SCROW nEndRow = rSrcArea.aEnd.Row();
+ ScDocumentUniquePtr pMixDoc;
+ bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS );
+
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+
+ sc::CopyToDocContext aCxt(*this);
+ sc::MixDocContext aMixDocCxt(*this);
+
+ SCTAB nCount = static_cast<SCTAB>(maTabs.size());
+ for (const SCTAB& i : rMark)
+ {
+ if (i >= nCount)
+ break;
+ if (i != nSrcTab && maTabs[i])
+ {
+ if (bDoMix)
+ {
+ if (!pMixDoc)
+ {
+ pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pMixDoc->InitUndo( *this, i, i );
+ }
+ else
+ pMixDoc->AddUndoTab( i, i );
+
+ // context used for copying content to the temporary mix document.
+ sc::CopyToDocContext aMixCxt(*pMixDoc);
+ maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ InsertDeleteFlags::CONTENTS, false, pMixDoc->maTabs[i].get(),
+ /*pMarkData*/nullptr, /*bAsLink*/false, /*bColRowFlags*/true,
+ /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
+ }
+ maTabs[i]->DeleteArea( nStartCol,nStartRow, nEndCol,nEndRow, nDelFlags);
+ maTabs[nSrcTab]->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ nFlags, false, maTabs[i].get(), nullptr, bAsLink,
+ /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
+
+ if (bDoMix)
+ maTabs[i]->MixData(aMixDocCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ nFunction, bSkipEmpty, pMixDoc->maTabs[i].get() );
+ }
+ }
+
+ SetAutoCalc( bOldAutoCalc );
+ }
+ else
+ {
+ OSL_FAIL("wrong table");
+ }
+}
+
+void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark,
+ InsertDeleteFlags nFlags, ScPasteFunc nFunction,
+ bool bSkipEmpty, bool bAsLink )
+{
+ InsertDeleteFlags nDelFlags = nFlags;
+ if (nDelFlags & InsertDeleteFlags::CONTENTS)
+ nDelFlags |= InsertDeleteFlags::CONTENTS; // Either all contents or delete nothing!
+
+ if (ValidTab(nSrcTab) && nSrcTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nSrcTab])
+ {
+ ScDocumentUniquePtr pMixDoc;
+ bool bDoMix = ( bSkipEmpty || nFunction != ScPasteFunc::NONE ) && ( nFlags & InsertDeleteFlags::CONTENTS );
+
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false ); // avoid multiple calculations
+
+ const ScRange& aArea = rMark.GetMultiMarkArea();
+ SCCOL nStartCol = aArea.aStart.Col();
+ SCROW nStartRow = aArea.aStart.Row();
+ SCCOL nEndCol = aArea.aEnd.Col();
+ SCROW nEndRow = aArea.aEnd.Row();
+
+ sc::CopyToDocContext aCxt(*this);
+ sc::MixDocContext aMixDocCxt(*this);
+ SCTAB nCount = static_cast<SCTAB>(maTabs.size());
+ for (const SCTAB& i : rMark)
+ {
+ if (i >= nCount)
+ break;
+ if ( i != nSrcTab && maTabs[i] )
+ {
+ if (bDoMix)
+ {
+ if (!pMixDoc)
+ {
+ pMixDoc.reset(new ScDocument(SCDOCMODE_UNDO));
+ pMixDoc->InitUndo( *this, i, i );
+ }
+ else
+ pMixDoc->AddUndoTab( i, i );
+
+ sc::CopyToDocContext aMixCxt(*pMixDoc);
+ maTabs[i]->CopyToTable(aMixCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ InsertDeleteFlags::CONTENTS, true, pMixDoc->maTabs[i].get(), &rMark,
+ /*bAsLink*/false, /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false,
+ /*bCopyCaptions*/true );
+ }
+
+ maTabs[i]->DeleteSelection( nDelFlags, rMark );
+ maTabs[nSrcTab]->CopyToTable(aCxt, nStartCol,nStartRow, nEndCol,nEndRow,
+ nFlags, true, maTabs[i].get(), &rMark, bAsLink,
+ /*bColRowFlags*/true, /*bGlobalNamesToLocal*/false, /*bCopyCaptions*/true );
+
+ if (bDoMix)
+ maTabs[i]->MixMarked(aMixDocCxt, rMark, nFunction, bSkipEmpty, pMixDoc->maTabs[i].get());
+ }
+ }
+
+ SetAutoCalc( bOldAutoCalc );
+ }
+ else
+ {
+ OSL_FAIL("wrong table");
+ }
+}
+
+bool ScDocument::SetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& rString,
+ const ScSetStringParam* pParam )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(nCol, nRow);
+ if (pCurCellFormula && pCurCellFormula->IsShared())
+ {
+ // In case setting this string affects an existing formula group, end
+ // its listening to purge then empty cell broadcasters. Affected
+ // remaining split group listeners will be set up again via
+ // ScColumn::DetachFormulaCell() and
+ // ScColumn::StartListeningUnshared().
+
+ sc::EndListeningContext aCxt(*this);
+ ScAddress aPos(nCol, nRow, nTab);
+ EndListeningIntersectedGroup(aCxt, aPos, nullptr);
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ return pTab->SetString(nCol, nRow, nTab, rString, pParam);
+}
+
+bool ScDocument::SetString(
+ const ScAddress& rPos, const OUString& rString, const ScSetStringParam* pParam )
+{
+ return SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rString, pParam);
+}
+
+bool ScDocument::SetEditText( const ScAddress& rPos, std::unique_ptr<EditTextObject> pEditText )
+{
+ if (!TableExists(rPos.Tab()))
+ {
+ return false;
+ }
+
+ return maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), std::move(pEditText));
+}
+
+void ScDocument::SetEditText( const ScAddress& rPos, const EditTextObject& rEditText, const SfxItemPool* pEditPool )
+{
+ if (!TableExists(rPos.Tab()))
+ return;
+
+ maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEditText, pEditPool);
+}
+
+void ScDocument::SetEditText( const ScAddress& rPos, const OUString& rStr )
+{
+ if (!TableExists(rPos.Tab()))
+ return;
+
+ ScFieldEditEngine& rEngine = GetEditEngine();
+ rEngine.SetTextCurrentDefaults(rStr);
+ maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject());
+}
+
+SCROW ScDocument::GetFirstEditTextRow( const ScRange& rRange ) const
+{
+ const ScTable* pTab = FetchTable(rRange.aStart.Tab());
+ if (!pTab)
+ return -1;
+
+ return pTab->GetFirstEditTextRow(rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+}
+
+void ScDocument::SetTextCell( const ScAddress& rPos, const OUString& rStr )
+{
+ if (!TableExists(rPos.Tab()))
+ return;
+
+ if (ScStringUtil::isMultiline(rStr))
+ {
+ ScFieldEditEngine& rEngine = GetEditEngine();
+ rEngine.SetTextCurrentDefaults(rStr);
+ maTabs[rPos.Tab()]->SetEditText(rPos.Col(), rPos.Row(), rEngine.CreateTextObject());
+ }
+ else
+ {
+ ScSetStringParam aParam;
+ aParam.setTextInput();
+ maTabs[rPos.Tab()]->SetString(rPos.Col(), rPos.Row(), rPos.Tab(), rStr, &aParam);
+ }
+}
+
+void ScDocument::SetEmptyCell( const ScAddress& rPos )
+{
+ if (!TableExists(rPos.Tab()))
+ return;
+
+ maTabs[rPos.Tab()]->SetEmptyCell(rPos.Col(), rPos.Row());
+}
+
+void ScDocument::SetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rVal )
+{
+ SetValue(ScAddress(nCol, nRow, nTab), rVal);
+}
+
+void ScDocument::SetValue( const ScAddress& rPos, double fVal )
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return;
+
+ const ScFormulaCell* pCurCellFormula = pTab->GetFormulaCell(rPos.Col(), rPos.Row());
+ if (pCurCellFormula && pCurCellFormula->IsShared())
+ {
+ // In case setting this value affects an existing formula group, end
+ // its listening to purge then empty cell broadcasters. Affected
+ // remaining split group listeners will be set up again via
+ // ScColumn::DetachFormulaCell() and
+ // ScColumn::StartListeningUnshared().
+
+ sc::EndListeningContext aCxt(*this);
+ EndListeningIntersectedGroup(aCxt, rPos, nullptr);
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ pTab->SetValue(rPos.Col(), rPos.Row(), fVal);
+}
+
+OUString ScDocument::GetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScInterpreterContext* pContext ) const
+{
+ if (TableExists(nTab))
+ return maTabs[nTab]->GetString(nCol, nRow, pContext);
+ return OUString();
+}
+
+OUString ScDocument::GetString( const ScAddress& rPos, const ScInterpreterContext* pContext ) const
+{
+ if (!TableExists(rPos.Tab()))
+ return OUString();
+
+ return maTabs[rPos.Tab()]->GetString(rPos.Col(), rPos.Row(), pContext);
+}
+
+double* ScDocument::GetValueCell( const ScAddress& rPos )
+{
+ if (!TableExists(rPos.Tab()))
+ return nullptr;
+
+ return maTabs[rPos.Tab()]->GetValueCell(rPos.Col(), rPos.Row());
+}
+
+svl::SharedString ScDocument::GetSharedString( const ScAddress& rPos ) const
+{
+ if (!TableExists(rPos.Tab()))
+ return svl::SharedString();
+
+ return maTabs[rPos.Tab()]->GetSharedString(rPos.Col(), rPos.Row());
+}
+
+std::shared_ptr<sc::FormulaGroupContext>& ScDocument::GetFormulaGroupContext()
+{
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ if (!mpFormulaGroupCxt)
+ mpFormulaGroupCxt = std::make_shared<sc::FormulaGroupContext>();
+
+ return mpFormulaGroupCxt;
+}
+
+void ScDocument::DiscardFormulaGroupContext()
+{
+ assert(!IsThreadedGroupCalcInProgress());
+ if( !mbFormulaGroupCxtBlockDiscard )
+ mpFormulaGroupCxt.reset();
+}
+
+OUString ScDocument::GetInputString(SCCOL nCol, SCROW nRow, SCTAB nTab, bool bForceSystemLocale ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetInputString( nCol, nRow, nullptr, bForceSystemLocale );
+ else
+ return OUString();
+}
+
+FormulaError ScDocument::GetStringForFormula( const ScAddress& rPos, OUString& rString )
+{
+ // Used in formulas (add-in parameters etc), so it must use the same semantics as
+ // ScInterpreter::GetCellString: always format values as numbers.
+ // The return value is the error code.
+
+ ScRefCellValue aCell(*this, rPos);
+ if (aCell.isEmpty())
+ {
+ rString.clear();
+ return FormulaError::NONE;
+ }
+
+ FormulaError nErr = FormulaError::NONE;
+ OUString aStr;
+ SvNumberFormatter* pFormatter = GetFormatTable();
+ switch (aCell.meType)
+ {
+ case CELLTYPE_STRING:
+ case CELLTYPE_EDIT:
+ aStr = aCell.getString(this);
+ break;
+ case CELLTYPE_FORMULA:
+ {
+ ScFormulaCell* pFCell = aCell.mpFormula;
+ nErr = pFCell->GetErrCode();
+ if (pFCell->IsValue())
+ {
+ double fVal = pFCell->GetValue();
+ sal_uInt32 nIndex = pFormatter->GetStandardFormat(
+ SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+ pFormatter->GetInputLineString(fVal, nIndex, aStr);
+ }
+ else
+ aStr = pFCell->GetString().getString();
+ }
+ break;
+ case CELLTYPE_VALUE:
+ {
+ double fVal = aCell.mfValue;
+ sal_uInt32 nIndex = pFormatter->GetStandardFormat(
+ SvNumFormatType::NUMBER,
+ ScGlobal::eLnge);
+ pFormatter->GetInputLineString(fVal, nIndex, aStr);
+ }
+ break;
+ default:
+ ;
+ }
+
+ rString = aStr;
+ return nErr;
+}
+
+const EditTextObject* ScDocument::GetEditText( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (!TableExists(nTab))
+ return nullptr;
+
+ return maTabs[nTab]->GetEditText(rPos.Col(), rPos.Row());
+}
+
+void ScDocument::RemoveEditTextCharAttribs( const ScAddress& rPos, const ScPatternAttr& rAttr )
+{
+ if (!TableExists(rPos.Tab()))
+ return;
+
+ return maTabs[rPos.Tab()]->RemoveEditTextCharAttribs(rPos.Col(), rPos.Row(), rAttr);
+}
+
+double ScDocument::GetValue( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetValue(rPos.Col(), rPos.Row());
+ return 0.0;
+}
+
+double ScDocument::GetValue( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ ScAddress aAdr(nCol, nRow, nTab);
+ return GetValue(aAdr);
+}
+
+sal_uInt32 ScDocument::GetNumberFormat( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ {
+ return maTabs[nTab]->GetNumberFormat( nCol, nRow );
+ }
+ return 0;
+}
+
+sal_uInt32 ScDocument::GetNumberFormat( const ScRange& rRange ) const
+{
+ SCTAB nTab1 = rRange.aStart.Tab(), nTab2 = rRange.aEnd.Tab();
+ SCCOL nCol1 = rRange.aStart.Col(), nCol2 = rRange.aEnd.Col();
+ SCROW nRow1 = rRange.aStart.Row(), nRow2 = rRange.aEnd.Row();
+
+ if (!TableExists(nTab1) || !TableExists(nTab2))
+ return 0;
+
+ sal_uInt32 nFormat = 0;
+ bool bFirstItem = true;
+ for (SCTAB nTab = nTab1; nTab <= nTab2 && nTab < static_cast<SCTAB>(maTabs.size()) ; ++nTab)
+ for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
+ {
+ sal_uInt32 nThisFormat = maTabs[nTab]->GetNumberFormat(nCol, nRow1, nRow2);
+ if (bFirstItem)
+ {
+ nFormat = nThisFormat;
+ bFirstItem = false;
+ }
+ else if (nThisFormat != nFormat)
+ return 0;
+ }
+
+ return nFormat;
+}
+
+sal_uInt32 ScDocument::GetNumberFormat( const ScInterpreterContext& rContext, const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (!TableExists(nTab))
+ return 0;
+
+ return maTabs[nTab]->GetNumberFormat( rContext, rPos );
+}
+
+void ScDocument::SetNumberFormat( const ScAddress& rPos, sal_uInt32 nNumberFormat )
+{
+ assert(!IsThreadedGroupCalcInProgress());
+ SCTAB nTab = rPos.Tab();
+ if (!TableExists(nTab))
+ return;
+
+ maTabs[nTab]->SetNumberFormat(rPos.Col(), rPos.Row(), nNumberFormat);
+}
+
+void ScDocument::GetNumberFormatInfo( const ScInterpreterContext& rContext, SvNumFormatType& nType, sal_uInt32& nIndex,
+ const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ {
+ nIndex = maTabs[nTab]->GetNumberFormat( rContext, rPos );
+ nType = rContext.GetNumberFormatType( nIndex );
+ }
+ else
+ {
+ nType = SvNumFormatType::UNDEFINED;
+ nIndex = 0;
+ }
+}
+
+OUString ScDocument::GetFormula( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetFormula( nCol, nRow );
+
+ return OUString();
+}
+
+const ScFormulaCell* ScDocument::GetFormulaCell( const ScAddress& rPos ) const
+{
+ if (!TableExists(rPos.Tab()))
+ return nullptr;
+
+ return maTabs[rPos.Tab()]->GetFormulaCell(rPos.Col(), rPos.Row());
+}
+
+ScFormulaCell* ScDocument::GetFormulaCell( const ScAddress& rPos )
+{
+ if (!TableExists(rPos.Tab()))
+ return nullptr;
+
+ return maTabs[rPos.Tab()]->GetFormulaCell(rPos.Col(), rPos.Row());
+}
+
+CellType ScDocument::GetCellType( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetCellType( rPos );
+ return CELLTYPE_NONE;
+}
+
+CellType ScDocument::GetCellType( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetCellType( nCol, nRow );
+
+ return CELLTYPE_NONE;
+}
+
+bool ScDocument::HasStringData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]
+ && nCol < maTabs[nTab]->GetAllocatedColumnsCount())
+ return maTabs[nTab]->HasStringData( nCol, nRow );
+ else
+ return false;
+}
+
+bool ScDocument::HasValueData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab]
+ && nCol < maTabs[nTab]->GetAllocatedColumnsCount())
+ return maTabs[nTab]->HasValueData( nCol, nRow );
+ else
+ return false;
+}
+
+bool ScDocument::HasValueData( const ScAddress& rPos ) const
+{
+ return HasValueData(rPos.Col(), rPos.Row(), rPos.Tab());
+}
+
+bool ScDocument::HasStringCells( const ScRange& rRange ) const
+{
+ // true, if String- or Edit cells in range
+
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+
+ for ( SCTAB nTab=nStartTab; nTab<=nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
+ if ( maTabs[nTab] && maTabs[nTab]->HasStringCells( nStartCol, nStartRow, nEndCol, nEndRow ) )
+ return true;
+
+ return false;
+}
+
+bool ScDocument::HasSelectionData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
+ if( nValidation )
+ {
+ const ScValidationData* pData = GetValidationEntry( nValidation );
+ if( pData && pData->HasSelectionList() )
+ return true;
+ }
+ return HasStringCells( ScRange( nCol, 0, nTab, nCol, MaxRow(), nTab ) );
+}
+
+bool ScDocument::HasValidationData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
+ if( nValidation )
+ {
+ const ScValidationData* pData = GetValidationEntry( nValidation );
+ if( pData && pData->GetDataMode() != ScValidationMode::SC_VALID_ANY )
+ return true;
+ }
+ return false;
+}
+
+void ScDocument::CheckVectorizationState()
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ bAutoCalc = false; // no multiple calculations
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->CheckVectorizationState();
+ }
+
+ SetAutoCalc(bOldAutoCalc);
+}
+
+void ScDocument::SetAllFormulasDirty( const sc::SetFormulaDirtyContext& rCxt )
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ bAutoCalc = false; // no multiple calculations
+ { // scope for bulk broadcast
+ ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetAllFormulasDirty(rCxt);
+ }
+ }
+
+ // Although Charts are also set to dirty in Tracking without AutoCalc
+ // if all formulas are dirty, the charts can no longer be caught
+ // (#45205#) - that is why all Charts have to be explicitly handled again
+ if (pChartListenerCollection)
+ pChartListenerCollection->SetDirty();
+
+ SetAutoCalc( bOldAutoCalc );
+}
+
+void ScDocument::SetDirty( const ScRange& rRange, bool bIncludeEmptyCells )
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ bAutoCalc = false; // no multiple calculations
+ { // scope for bulk broadcast
+ ScBulkBroadcast aBulkBroadcast( GetBASM(), SfxHintId::ScDataChanged);
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ for (SCTAB i=rRange.aStart.Tab(); i<=nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i]) maTabs[i]->SetDirty( rRange,
+ (bIncludeEmptyCells ? ScColumn::BROADCAST_BROADCASTERS : ScColumn::BROADCAST_DATA_POSITIONS));
+
+ /* TODO: this now also notifies conditional formatting and does a UNO
+ * broadcast, which wasn't done here before. Is that an actually
+ * desired side effect, or should we come up with a method that
+ * doesn't? */
+ if (bIncludeEmptyCells)
+ BroadcastCells( rRange, SfxHintId::ScDataChanged, false);
+ }
+ SetAutoCalc( bOldAutoCalc );
+}
+
+void ScDocument::SetTableOpDirty( const ScRange& rRange )
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ bAutoCalc = false; // no multiple recalculation
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ for (SCTAB i=rRange.aStart.Tab(); i<=nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i]) maTabs[i]->SetTableOpDirty( rRange );
+ SetAutoCalc( bOldAutoCalc );
+}
+
+void ScDocument::InterpretDirtyCells( const ScRangeList& rRanges )
+{
+ if (!GetAutoCalc())
+ return;
+
+ PrepareFormulaCalc();
+
+ for (size_t nPos=0, nRangeCount = rRanges.size(); nPos < nRangeCount; nPos++)
+ {
+ const ScRange& rRange = rRanges[nPos];
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->InterpretDirtyCells(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row());
+ }
+ }
+
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ mpFormulaGroupCxt.reset();
+}
+
+bool ScDocument::InterpretCellsIfNeeded( const ScRangeList& rRanges )
+{
+ bool allInterpreted = true;
+ for (size_t nPos=0, nRangeCount = rRanges.size(); nPos < nRangeCount; nPos++)
+ {
+ const ScRange& rRange = rRanges[nPos];
+ for (SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ break;
+
+ if( !pTab->InterpretCellsIfNeeded(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row()))
+ {
+ allInterpreted = false;
+ }
+ }
+ }
+ return allInterpreted;
+}
+
+void ScDocument::AddTableOpFormulaCell( ScFormulaCell* pCell )
+{
+ if (m_TableOpList.empty())
+ return;
+
+ ScInterpreterTableOpParams *const p = m_TableOpList.back();
+ if ( p->bCollectNotifications )
+ {
+ if ( p->bRefresh )
+ { // refresh pointers only
+ p->aNotifiedFormulaCells.push_back( pCell );
+ }
+ else
+ { // init both, address and pointer
+ p->aNotifiedFormulaCells.push_back( pCell );
+ p->aNotifiedFormulaPos.push_back( pCell->aPos );
+ }
+ }
+}
+
+void ScDocument::CalcAll()
+{
+ PrepareFormulaCalc();
+ ClearLookupCaches(); // Ensure we don't deliver zombie data.
+ sc::AutoCalcSwitch aSwitch(*this, true);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyVar();
+ }
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->CalcAll();
+ }
+ ClearFormulaTree();
+
+ // In eternal hard recalc state caches were not added as listeners,
+ // invalidate them so the next non-CalcAll() normal lookup will not be
+ // presented with outdated data.
+ if (GetHardRecalcState() == HardRecalcState::ETERNAL)
+ ClearLookupCaches();
+}
+
+void ScDocument::CompileAll()
+{
+ sc::CompileFormulaContext aCxt(*this);
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->CompileAll(aCxt);
+ }
+
+ sc::SetFormulaDirtyContext aFormulaDirtyCxt;
+ SetAllFormulasDirty(aFormulaDirtyCxt);
+}
+
+void ScDocument::CompileXML()
+{
+ bool bOldAutoCalc = GetAutoCalc();
+ SetAutoCalc( false );
+ ScProgress aProgress( GetDocumentShell(), ScResId(
+ STR_PROGRESS_CALCULATING ), GetXMLImportedFormulaCount(), true );
+
+ sc::CompileFormulaContext aCxt(*this);
+
+ // set AutoNameCache to speed up automatic name lookup
+ OSL_ENSURE( !pAutoNameCache, "AutoNameCache already set" );
+ pAutoNameCache.reset( new ScAutoNameCache( *this ) );
+
+ if (pRangeName)
+ pRangeName->CompileUnresolvedXML(aCxt);
+
+ std::for_each(maTabs.begin(), maTabs.end(),
+ [&](ScTableUniquePtr & pTab)
+ {
+ if (pTab)
+ pTab->CompileXML(aCxt, aProgress);
+ }
+ );
+ StartAllListeners();
+
+ pAutoNameCache.reset(); // valid only during CompileXML, where cell contents don't change
+
+ if ( pValidationList )
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->CompileXML();
+ }
+
+ // Track all formula cells that were appended to the FormulaTrack during
+ // import or CompileXML().
+ TrackFormulas();
+
+ SetAutoCalc( bOldAutoCalc );
+}
+
+bool ScDocument::CompileErrorCells(FormulaError nErrCode)
+{
+ bool bCompiled = false;
+ sc::CompileFormulaContext aCxt(*this);
+ for (const auto& a : maTabs)
+ {
+ if (!a)
+ continue;
+
+ if (a->CompileErrorCells(aCxt, nErrCode))
+ bCompiled = true;
+ }
+
+ return bCompiled;
+}
+
+void ScDocument::CalcAfterLoad( bool bStartListening )
+{
+ if (bIsClip) // Excel data is loaded from the Clipboard to a Clip-Doc
+ return; // the calculation is then only performed when inserting into the real document
+
+ bCalcingAfterLoad = true;
+ sc::CompileFormulaContext aCxt(*this);
+ {
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->CalcAfterLoad(aCxt, bStartListening);
+ }
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SetDirtyAfterLoad();
+ }
+ }
+ bCalcingAfterLoad = false;
+
+ SetDetectiveDirty(false); // No real changes yet
+
+ // #i112436# If formula cells are already dirty, they don't broadcast further changes.
+ // So the source ranges of charts must be interpreted even if they are not visible,
+ // similar to ScMyShapeResizer::CreateChartListener for loading own files (i104899).
+ if (pChartListenerCollection)
+ {
+ const ScChartListenerCollection::ListenersType& rListeners = pChartListenerCollection->getListeners();
+ for (auto const& it : rListeners)
+ {
+ const ScChartListener *const p = it.second.get();
+ InterpretDirtyCells(*p->GetRangeList());
+ }
+ }
+}
+
+FormulaError ScDocument::GetErrCode( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetErrCode( rPos );
+ return FormulaError::NONE;
+}
+
+void ScDocument::ResetChanged( const ScRange& rRange )
+{
+ SCTAB nTabSize = static_cast<SCTAB>(maTabs.size());
+ SCTAB nTab1 = rRange.aStart.Tab();
+ SCTAB nTab2 = rRange.aEnd.Tab();
+ for (SCTAB nTab = nTab1; nTab1 <= nTab2 && nTab < nTabSize; ++nTab)
+ if (maTabs[nTab])
+ maTabs[nTab]->ResetChanged(rRange);
+}
+
+// Column widths / Row heights --------------------------------------
+
+void ScDocument::SetColWidth( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetColWidth( nCol, nNewWidth );
+}
+
+void ScDocument::SetColWidthOnly( SCCOL nCol, SCTAB nTab, sal_uInt16 nNewWidth )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetColWidthOnly( nCol, nNewWidth );
+}
+
+void ScDocument::SetRowHeight( SCROW nRow, SCTAB nTab, sal_uInt16 nNewHeight )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetRowHeight( nRow, nNewHeight );
+}
+
+void ScDocument::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetRowHeightRange
+ ( nStartRow, nEndRow, nNewHeight, 1.0, true );
+}
+
+void ScDocument::SetRowHeightOnly( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, sal_uInt16 nNewHeight )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetRowHeightOnly( nStartRow, nEndRow, nNewHeight );
+}
+
+void ScDocument::SetManualHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bManual )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetManualHeight( nStartRow, nEndRow, bManual );
+}
+
+sal_uInt16 ScDocument::GetColWidth( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetColWidth( nCol, bHiddenAsZero );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+tools::Long ScDocument::GetColWidth( SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return 0;
+
+ return pTab->GetColWidth(nStartCol, nEndCol);
+}
+
+sal_uInt16 ScDocument::GetOriginalWidth( SCCOL nCol, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetOriginalWidth( nCol );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetCommonWidth( SCCOL nEndCol, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetCommonWidth( nEndCol );
+ OSL_FAIL("Wrong table number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetOriginalHeight( SCROW nRow, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetOriginalHeight( nRow );
+ OSL_FAIL("Wrong table number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetRowHeight( nRow, nullptr, nullptr, bHiddenAsZero );
+ OSL_FAIL("Wrong sheet number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab, SCROW* pStartRow, SCROW* pEndRow, bool bHiddenAsZero ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetRowHeight( nRow, pStartRow, pEndRow, bHiddenAsZero );
+ OSL_FAIL("Wrong sheet number");
+ return 0;
+}
+
+tools::Long ScDocument::GetRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if (nStartRow == nEndRow)
+ return GetRowHeight( nStartRow, nTab, bHiddenAsZero ); // faster for a single row
+
+ // check bounds because this method replaces former for(i=start;i<=end;++i) loops
+ if (nStartRow > nEndRow)
+ return 0;
+
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetRowHeight( nStartRow, nEndRow, bHiddenAsZero );
+
+ OSL_FAIL("wrong sheet number");
+ return 0;
+}
+
+SCROW ScDocument::GetRowForHeight( SCTAB nTab, tools::Long nHeight ) const
+{
+ return maTabs[nTab]->GetRowForHeight(nHeight);
+}
+
+tools::Long ScDocument::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow,
+ SCTAB nTab, double fScale ) const
+{
+ // faster for a single row
+ if (nStartRow == nEndRow)
+ return static_cast<tools::Long>(GetRowHeight( nStartRow, nTab) * fScale);
+
+ // check bounds because this method replaces former for(i=start;i<=end;++i) loops
+ if (nStartRow > nEndRow)
+ return 0;
+
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetScaledRowHeight( nStartRow, nEndRow, fScale);
+
+ OSL_FAIL("wrong sheet number");
+ return 0;
+}
+
+SCROW ScDocument::GetHiddenRowCount( SCROW nRow, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetHiddenRowCount( nRow );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+tools::Long ScDocument::GetColOffset( SCCOL nCol, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetColOffset( nCol, bHiddenAsZero );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+tools::Long ScDocument::GetRowOffset( SCROW nRow, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetRowOffset( nRow, bHiddenAsZero );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+sal_uInt16 ScDocument::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ bool bFormula, const ScMarkData* pMarkData,
+ const ScColWidthParam* pParam )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetOptimalColWidth( nCol, pDev, nPPTX, nPPTY,
+ rZoomX, rZoomY, bFormula, pMarkData, pParam );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+tools::Long ScDocument::GetNeededSize( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY,
+ bool bWidth, bool bTotalSize, bool bInPrintTwips )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetNeededSize
+ ( nCol, nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bWidth, bTotalSize, bInPrintTwips );
+ OSL_FAIL("wrong table number");
+ return 0;
+}
+
+bool ScDocument::SetOptimalHeight( sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bApi )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ return pTab->SetOptimalHeight(rCxt, nStartRow, nEndRow, bApi);
+}
+
+void ScDocument::UpdateAllRowHeights( sc::RowHeightContext& rCxt, const ScMarkData* pTabMark )
+{
+ // one progress across all (selected) sheets
+
+ sal_uInt64 nCellCount = 0;
+ for ( SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()); nTab++ )
+ if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) )
+ nCellCount += maTabs[nTab]->GetWeightedCount();
+
+ ScProgress aProgress( GetDocumentShell(), ScResId(STR_PROGRESS_HEIGHTING), nCellCount, true );
+
+ sal_uInt64 nProgressStart = 0;
+ for ( SCTAB nTab=0; nTab< static_cast<SCTAB>(maTabs.size()); nTab++ )
+ if ( maTabs[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) )
+ {
+ maTabs[nTab]->SetOptimalHeightOnly(rCxt, 0, MaxRow(), &aProgress, nProgressStart);
+ maTabs[nTab]->SetDrawPageSize();
+ nProgressStart += maTabs[nTab]->GetWeightedCount();
+ }
+}
+
+// Column/Row - Flags ----------------------------------------------
+
+void ScDocument::ShowCol(SCCOL nCol, SCTAB nTab, bool bShow)
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->ShowCol( nCol, bShow );
+}
+
+void ScDocument::ShowRow(SCROW nRow, SCTAB nTab, bool bShow)
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->ShowRow( nRow, bShow );
+}
+
+void ScDocument::ShowRows(SCROW nRow1, SCROW nRow2, SCTAB nTab, bool bShow)
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->ShowRows( nRow1, nRow2, bShow );
+}
+
+void ScDocument::SetRowFlags( SCROW nRow, SCTAB nTab, CRFlags nNewFlags )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetRowFlags( nRow, nNewFlags );
+}
+
+void ScDocument::SetRowFlags( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, CRFlags nNewFlags )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetRowFlags( nStartRow, nEndRow, nNewFlags );
+}
+
+CRFlags ScDocument::GetColFlags( SCCOL nCol, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetColFlags( nCol );
+ OSL_FAIL("wrong table number");
+ return CRFlags::NONE;
+}
+
+CRFlags ScDocument::GetRowFlags( SCROW nRow, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetRowFlags( nRow );
+ OSL_FAIL("wrong table number");
+ return CRFlags::NONE;
+}
+
+void ScDocument::GetAllRowBreaks(set<SCROW>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return;
+ maTabs[nTab]->GetAllRowBreaks(rBreaks, bPage, bManual);
+}
+
+void ScDocument::GetAllColBreaks(set<SCCOL>& rBreaks, SCTAB nTab, bool bPage, bool bManual) const
+{
+ if (!ValidTab(nTab) || !maTabs[nTab])
+ return;
+
+ maTabs[nTab]->GetAllColBreaks(rBreaks, bPage, bManual);
+}
+
+ScBreakType ScDocument::HasRowBreak(SCROW nRow, SCTAB nTab) const
+{
+ ScBreakType nType = ScBreakType::NONE;
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow))
+ return nType;
+
+ if (maTabs[nTab]->HasRowPageBreak(nRow))
+ nType |= ScBreakType::Page;
+
+ if (maTabs[nTab]->HasRowManualBreak(nRow))
+ nType |= ScBreakType::Manual;
+
+ return nType;
+}
+
+ScBreakType ScDocument::HasColBreak(SCCOL nCol, SCTAB nTab) const
+{
+ ScBreakType nType = ScBreakType::NONE;
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol))
+ return nType;
+
+ if (maTabs[nTab]->HasColPageBreak(nCol))
+ nType |= ScBreakType::Page;
+
+ if (maTabs[nTab]->HasColManualBreak(nCol))
+ nType |= ScBreakType::Manual;
+
+ return nType;
+}
+
+void ScDocument::SetRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow))
+ return;
+
+ maTabs[nTab]->SetRowBreak(nRow, bPage, bManual);
+}
+
+void ScDocument::SetColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol))
+ return;
+
+ maTabs[nTab]->SetColBreak(nCol, bPage, bManual);
+}
+
+void ScDocument::RemoveRowBreak(SCROW nRow, SCTAB nTab, bool bPage, bool bManual)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidRow(nRow))
+ return;
+
+ maTabs[nTab]->RemoveRowBreak(nRow, bPage, bManual);
+}
+
+void ScDocument::RemoveColBreak(SCCOL nCol, SCTAB nTab, bool bPage, bool bManual)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab] || !ValidCol(nCol))
+ return;
+
+ maTabs[nTab]->RemoveColBreak(nCol, bPage, bManual);
+}
+
+Sequence<TablePageBreakData> ScDocument::GetRowBreakData(SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return Sequence<TablePageBreakData>();
+
+ return maTabs[nTab]->GetRowBreakData();
+}
+
+bool ScDocument::RowHidden(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return false;
+
+ return maTabs[nTab]->RowHidden(nRow, pFirstRow, pLastRow);
+}
+
+bool ScDocument::HasHiddenRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return false;
+
+ return maTabs[nTab]->HasHiddenRows(nStartRow, nEndRow);
+}
+
+bool ScDocument::ColHidden(SCCOL nCol, SCTAB nTab, SCCOL* pFirstCol, SCCOL* pLastCol) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ {
+ if (pFirstCol)
+ *pFirstCol = nCol;
+ if (pLastCol)
+ *pLastCol = nCol;
+ return false;
+ }
+
+ return maTabs[nTab]->ColHidden(nCol, pFirstCol, pLastCol);
+}
+
+void ScDocument::SetRowHidden(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bHidden)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return;
+
+ maTabs[nTab]->SetRowHidden(nStartRow, nEndRow, bHidden);
+}
+
+void ScDocument::SetColHidden(SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab, bool bHidden)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return;
+
+ maTabs[nTab]->SetColHidden(nStartCol, nEndCol, bHidden);
+}
+
+SCROW ScDocument::FirstVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return ::std::numeric_limits<SCROW>::max();
+
+ return maTabs[nTab]->FirstVisibleRow(nStartRow, nEndRow);
+}
+
+SCROW ScDocument::LastVisibleRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return ::std::numeric_limits<SCROW>::max();
+
+ return maTabs[nTab]->LastVisibleRow(nStartRow, nEndRow);
+}
+
+SCROW ScDocument::CountVisibleRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return 0;
+
+ return maTabs[nTab]->CountVisibleRows(nStartRow, nEndRow);
+}
+
+bool ScDocument::RowFiltered(SCROW nRow, SCTAB nTab, SCROW* pFirstRow, SCROW* pLastRow) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return false;
+
+ return maTabs[nTab]->RowFiltered(nRow, pFirstRow, pLastRow);
+}
+
+bool ScDocument::HasFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return false;
+
+ return maTabs[nTab]->HasFilteredRows(nStartRow, nEndRow);
+}
+
+bool ScDocument::ColFiltered(SCCOL nCol, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return false;
+
+ return maTabs[nTab]->ColFiltered(nCol);
+}
+
+void ScDocument::SetRowFiltered(SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bFiltered)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return;
+
+ maTabs[nTab]->SetRowFiltered(nStartRow, nEndRow, bFiltered);
+}
+
+SCROW ScDocument::FirstNonFilteredRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return ::std::numeric_limits<SCROW>::max();
+
+ return maTabs[nTab]->FirstNonFilteredRow(nStartRow, nEndRow);
+}
+
+SCROW ScDocument::LastNonFilteredRow(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return ::std::numeric_limits<SCROW>::max();
+
+ return maTabs[nTab]->LastNonFilteredRow(nStartRow, nEndRow);
+}
+
+SCROW ScDocument::CountNonFilteredRows(SCROW nStartRow, SCROW nEndRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return 0;
+
+ return maTabs[nTab]->CountNonFilteredRows(nStartRow, nEndRow);
+}
+
+bool ScDocument::IsManualRowHeight(SCROW nRow, SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return false;
+
+ return maTabs[nTab]->IsManualRowHeight(nRow);
+}
+
+void ScDocument::SyncColRowFlags()
+{
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->SyncColRowFlags();
+ }
+}
+
+SCROW ScDocument::GetLastFlaggedRow( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetLastFlaggedRow();
+ return 0;
+}
+
+SCCOL ScDocument::GetLastChangedColFlagsWidth( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetLastChangedColFlagsWidth();
+ return 0;
+}
+
+SCROW ScDocument::GetLastChangedRowFlagsWidth( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetLastChangedRowFlagsWidth();
+ return 0;
+}
+
+SCCOL ScDocument::GetNextDifferentChangedColFlagsWidth( SCTAB nTab, SCCOL nStart) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ {
+ CRFlags nStartFlags = maTabs[nTab]->GetColFlags(nStart);
+ sal_uInt16 nStartWidth = maTabs[nTab]->GetOriginalWidth(nStart);
+ for (SCCOL nCol : maTabs[nTab]->GetColumnsRange( nStart + 1, MaxCol()))
+ {
+ if (((nStartFlags & CRFlags::ManualBreak) != (maTabs[nTab]->GetColFlags(nCol) & CRFlags::ManualBreak)) ||
+ (nStartWidth != maTabs[nTab]->GetOriginalWidth(nCol)) ||
+ ((nStartFlags & CRFlags::Hidden) != (maTabs[nTab]->GetColFlags(nCol) & CRFlags::Hidden)) )
+ return nCol;
+ }
+ return MaxCol()+1;
+ }
+ return 0;
+}
+
+SCROW ScDocument::GetNextDifferentChangedRowFlagsWidth( SCTAB nTab, SCROW nStart) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return 0;
+
+ const ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlagsArray = maTabs[nTab]->GetRowFlagsArray();
+ if (!pRowFlagsArray)
+ return 0;
+
+ if (!maTabs[nTab]->mpRowHeights || !maTabs[nTab]->mpHiddenRows)
+ return 0;
+
+ size_t nIndex; // ignored
+ SCROW nFlagsEndRow;
+ SCROW nHiddenEndRow;
+ SCROW nHeightEndRow;
+ CRFlags nFlags;
+ bool bHidden;
+ sal_uInt16 nHeight;
+ CRFlags nStartFlags = nFlags = pRowFlagsArray->GetValue( nStart, nIndex, nFlagsEndRow);
+ bool bStartHidden = bHidden = maTabs[nTab]->RowHidden( nStart, nullptr, &nHiddenEndRow);
+ sal_uInt16 nStartHeight = nHeight = maTabs[nTab]->GetRowHeight( nStart, nullptr, &nHeightEndRow, false);
+ SCROW nRow;
+ while ((nRow = std::min( nHiddenEndRow, std::min( nFlagsEndRow, nHeightEndRow)) + 1) <= MaxRow())
+ {
+ if (nFlagsEndRow < nRow)
+ nFlags = pRowFlagsArray->GetValue( nRow, nIndex, nFlagsEndRow);
+ if (nHiddenEndRow < nRow)
+ bHidden = maTabs[nTab]->RowHidden( nRow, nullptr, &nHiddenEndRow);
+ if (nHeightEndRow < nRow)
+ nHeight = maTabs[nTab]->GetRowHeight( nRow, nullptr, &nHeightEndRow, false);
+
+ if (((nStartFlags & CRFlags::ManualBreak) != (nFlags & CRFlags::ManualBreak)) ||
+ ((nStartFlags & CRFlags::ManualSize) != (nFlags & CRFlags::ManualSize)) ||
+ (bStartHidden != bHidden) ||
+ (nStartHeight != nHeight))
+ return nRow;
+ }
+
+ return MaxRow()+1;
+}
+
+void ScDocument::GetColDefault( SCTAB nTab, SCCOL nCol, SCROW nLastRow, SCROW& nDefault)
+{
+ nDefault = 0;
+ ScDocAttrIterator aDocAttrItr(*this, nTab, nCol, 0, nCol, nLastRow);
+ SCCOL nColumn;
+ SCROW nStartRow;
+ SCROW nEndRow;
+ const ScPatternAttr* pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow);
+ if (nEndRow >= nLastRow)
+ return;
+
+ ScDefaultAttrSet aSet;
+ ScDefaultAttrSet::iterator aItr = aSet.end();
+ while (pAttr)
+ {
+ ScDefaultAttr aAttr(pAttr);
+ aItr = aSet.find(aAttr);
+ if (aItr == aSet.end())
+ {
+ aAttr.nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1);
+ aAttr.nFirst = nStartRow;
+ aSet.insert(aAttr);
+ }
+ else
+ {
+ aAttr.nCount = aItr->nCount + static_cast<SCSIZE>(nEndRow - nStartRow + 1);
+ aAttr.nFirst = aItr->nFirst;
+ aSet.erase(aItr);
+ aSet.insert(aAttr);
+ }
+ pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow);
+ }
+ ScDefaultAttrSet::iterator aDefaultItr = aSet.begin();
+ aItr = aDefaultItr;
+ ++aItr;
+ while (aItr != aSet.end())
+ {
+ // for entries with equal count, use the one with the lowest start row,
+ // don't use the random order of pointer comparisons
+ if ( aItr->nCount > aDefaultItr->nCount ||
+ ( aItr->nCount == aDefaultItr->nCount && aItr->nFirst < aDefaultItr->nFirst ) )
+ aDefaultItr = aItr;
+ ++aItr;
+ }
+ nDefault = aDefaultItr->nFirst;
+}
+
+void ScDocument::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->StripHidden( rX1, rY1, rX2, rY2 );
+}
+
+void ScDocument::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab )
+{
+ if ( ValidTab(nTab) && maTabs[nTab] )
+ maTabs[nTab]->ExtendHidden( rX1, rY1, rX2, rY2 );
+}
+
+// Attribute ----------------------------------------------------------
+
+const SfxPoolItem* ScDocument::GetAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ {
+ const SfxPoolItem* pTemp = maTabs[nTab]->GetAttr( nCol, nRow, nWhich );
+ if (pTemp)
+ return pTemp;
+ else
+ {
+ OSL_FAIL( "Attribute Null" );
+ }
+ }
+ return &mxPoolHelper->GetDocPool()->GetDefaultItem( nWhich );
+}
+
+const SfxPoolItem* ScDocument::GetAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, sal_uInt16 nWhich, SCROW& nStartRow, SCROW& nEndRow ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ {
+ const SfxPoolItem* pTemp = maTabs[nTab]->GetAttr( nCol, nRow, nWhich, nStartRow, nEndRow );
+ if (pTemp)
+ return pTemp;
+ else
+ {
+ OSL_FAIL( "Attribute Null" );
+ }
+ }
+ return &mxPoolHelper->GetDocPool()->GetDefaultItem( nWhich );
+}
+
+const SfxPoolItem* ScDocument::GetAttr( const ScAddress& rPos, sal_uInt16 nWhich ) const
+{
+ return GetAttr(rPos.Col(), rPos.Row(), rPos.Tab(), nWhich);
+}
+
+const ScPatternAttr* ScDocument::GetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if (TableExists(nTab))
+ return maTabs[nTab]->GetPattern( nCol, nRow );
+ return nullptr;
+}
+
+const ScPatternAttr* ScDocument::GetPattern( const ScAddress& rPos ) const
+{
+ if (TableExists(rPos.Tab()))
+ return maTabs[rPos.Tab()]->GetPattern(rPos.Col(), rPos.Row());
+
+ return nullptr;
+}
+
+const ScPatternAttr* ScDocument::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetMostUsedPattern( nCol, nStartRow, nEndRow );
+ return nullptr;
+}
+
+void ScDocument::ApplyAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem& rAttr )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->ApplyAttr( nCol, nRow, rAttr );
+}
+
+void ScDocument::ApplyPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->ApplyPattern( nCol, nRow, rAttr );
+}
+
+void ScDocument::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark,
+ const ScPatternAttr& rAttr,
+ ScEditDataArray* pDataArray,
+ bool* const pIsChanged )
+{
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr, pDataArray, pIsChanged );
+ }
+}
+
+void ScDocument::ApplyPatternAreaTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScPatternAttr& rAttr )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ maTabs[nTab]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr );
+}
+
+void ScDocument::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
+ const ScMarkData& rMark, const ScPatternAttr& rPattern, SvNumFormatType nNewType )
+{
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType );
+ }
+}
+
+void ScDocument::AddCondFormatData( const ScRangeList& rRange, SCTAB nTab, sal_uInt32 nIndex )
+{
+ if(o3tl::make_unsigned(nTab) >= maTabs.size())
+ return;
+
+ if(!maTabs[nTab])
+ return;
+
+ maTabs[nTab]->AddCondFormatData(rRange, nIndex);
+}
+
+void ScDocument::RemoveCondFormatData( const ScRangeList& rRange, SCTAB nTab, sal_uInt32 nIndex )
+{
+ if(o3tl::make_unsigned(nTab) >= maTabs.size())
+ return;
+
+ if(!maTabs[nTab])
+ return;
+
+ maTabs[nTab]->RemoveCondFormatData(rRange, nIndex);
+}
+
+void ScDocument::ApplyStyle( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScStyleSheet& rStyle)
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ maTabs[nTab]->ApplyStyle( nCol, nRow, &rStyle );
+}
+
+void ScDocument::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark,
+ const ScStyleSheet& rStyle)
+{
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle );
+ }
+}
+
+void ScDocument::ApplyStyleAreaTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScStyleSheet& rStyle)
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ maTabs[nTab]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle );
+}
+
+void ScDocument::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark)
+{
+ // ApplySelectionStyle needs multi mark
+ if ( rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ ApplyStyleArea( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rStyle );
+ }
+ else
+ {
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if ( maTabs[rTab] )
+ maTabs[rTab]->ApplySelectionStyle( rStyle, rMark );
+ }
+ }
+}
+
+void ScDocument::ApplySelectionLineStyle( const ScMarkData& rMark,
+ const SvxBorderLine* pLine, bool bColorOnly )
+{
+ if ( bColorOnly && !pLine )
+ return;
+
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplySelectionLineStyle( rMark, pLine, bColorOnly );
+ }
+}
+
+const ScStyleSheet* ScDocument::GetStyle( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetStyle(nCol, nRow);
+ else
+ return nullptr;
+}
+
+const ScStyleSheet* ScDocument::GetSelectionStyle( const ScMarkData& rMark ) const
+{
+ bool bEqual = true;
+ bool bFound;
+
+ const ScStyleSheet* pStyle = nullptr;
+ const ScStyleSheet* pNewStyle;
+
+ if ( rMark.IsMultiMarked() )
+ {
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ {
+ pNewStyle = maTabs[rTab]->GetSelectionStyle( rMark, bFound );
+ if (bFound)
+ {
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false; // different
+ pStyle = pNewStyle;
+ }
+ }
+ }
+ }
+ if ( rMark.IsMarked() )
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ for (SCTAB i=aRange.aStart.Tab(); i<=aRange.aEnd.Tab() && bEqual && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i] && rMark.GetTableSelect(i))
+ {
+ pNewStyle = maTabs[i]->GetAreaStyle( bFound,
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ if (bFound)
+ {
+ if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
+ bEqual = false; // different
+ pStyle = pNewStyle;
+ }
+ }
+ }
+
+ return bEqual ? pStyle : nullptr;
+}
+
+void ScDocument::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, bool bRemoved,
+ OutputDevice* pDev,
+ double nPPTX, double nPPTY,
+ const Fraction& rZoomX, const Fraction& rZoomY )
+{
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->StyleSheetChanged
+ ( pStyleSheet, bRemoved, pDev, nPPTX, nPPTY, rZoomX, rZoomY );
+ }
+
+ if ( pStyleSheet && pStyleSheet->GetName() == ScResId(STR_STYLENAME_STANDARD) )
+ {
+ // update attributes for all note objects
+ ScDetectiveFunc::UpdateAllComments( *this );
+ }
+}
+
+bool ScDocument::IsStyleSheetUsed( const ScStyleSheet& rStyle ) const
+{
+ if ( bStyleSheetUsageInvalid || rStyle.GetUsage() == ScStyleSheet::Usage::UNKNOWN )
+ {
+ SfxStyleSheetIterator aIter( mxPoolHelper->GetStylePool(),
+ SfxStyleFamily::Para );
+ for ( const SfxStyleSheetBase* pStyle = aIter.First(); pStyle;
+ pStyle = aIter.Next() )
+ {
+ if (pStyle->isScStyleSheet())
+ {
+ const ScStyleSheet* pScStyle = static_cast<const ScStyleSheet*>( pStyle );
+ pScStyle->SetUsage( ScStyleSheet::Usage::NOTUSED );
+ }
+ }
+
+ bool bIsUsed = false;
+
+ for (const auto& a : maTabs)
+ {
+ if (a && a->IsStyleSheetUsed( rStyle ) )
+ bIsUsed = true;
+ }
+
+ bStyleSheetUsageInvalid = false;
+
+ return bIsUsed;
+ }
+
+ return rStyle.GetUsage() == ScStyleSheet::Usage::USED;
+}
+
+bool ScDocument::ApplyFlagsTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, ScMF nFlags )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ return maTabs[nTab]->ApplyFlags( nStartCol, nStartRow, nEndCol, nEndRow, nFlags );
+
+ OSL_FAIL("ApplyFlags: wrong table");
+ return false;
+}
+
+bool ScDocument::RemoveFlagsTab( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, ScMF nFlags )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ return maTabs[nTab]->RemoveFlags( nStartCol, nStartRow, nEndCol, nEndRow, nFlags );
+
+ OSL_FAIL("RemoveFlags: wrong table");
+ return false;
+}
+
+const ScPatternAttr* ScDocument::SetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, std::unique_ptr<ScPatternAttr> pAttr )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ return maTabs[nTab]->SetPattern( nCol, nRow, std::move(pAttr) );
+ return nullptr;
+}
+
+const ScPatternAttr* ScDocument::SetPattern( const ScAddress& rPos, std::unique_ptr<ScPatternAttr> pAttr )
+{
+ return SetPattern(rPos.Col(), rPos.Row(), rPos.Tab(), std::move(pAttr));
+}
+
+void ScDocument::SetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ maTabs[nTab]->SetPattern( nCol, nRow, rAttr );
+}
+
+void ScDocument::SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr )
+{
+ SCTAB nTab = rPos.Tab();
+ if ( nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetPattern( rPos, rAttr );
+}
+
+std::unique_ptr<ScPatternAttr> ScDocument::CreateSelectionPattern( const ScMarkData& rMark, bool bDeep )
+{
+ ScMergePatternState aState;
+
+ if ( rMark.IsMultiMarked() ) // multi selection
+ {
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->MergeSelectionPattern( aState, rMark, bDeep );
+ }
+ }
+ if ( rMark.IsMarked() ) // single selection
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->MergePatternArea( aState,
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), bDeep );
+ }
+ }
+
+ OSL_ENSURE( aState.pItemSet, "SelectionPattern Null" );
+ if (aState.pItemSet)
+ {
+ std::unique_ptr<ScPatternAttr> pPattern(new ScPatternAttr( std::move(*aState.pItemSet) ));
+ if (aState.mbValidPatternId)
+ pPattern->SetKey(aState.mnPatternId);
+
+ return pPattern;
+ }
+ else
+ return std::unique_ptr<ScPatternAttr>(new ScPatternAttr( GetPool() )); // empty
+}
+
+const ScPatternAttr* ScDocument::GetSelectionPattern( const ScMarkData& rMark )
+{
+ pSelectionAttr = CreateSelectionPattern( rMark );
+ return pSelectionAttr.get();
+}
+
+void ScDocument::GetSelectionFrame( const ScMarkData& rMark,
+ SvxBoxItem& rLineOuter,
+ SvxBoxInfoItem& rLineInner )
+{
+ rLineOuter.SetLine(nullptr, SvxBoxItemLine::TOP);
+ rLineOuter.SetLine(nullptr, SvxBoxItemLine::BOTTOM);
+ rLineOuter.SetLine(nullptr, SvxBoxItemLine::LEFT);
+ rLineOuter.SetLine(nullptr, SvxBoxItemLine::RIGHT);
+ rLineOuter.SetAllDistances(0);
+
+ rLineInner.SetLine(nullptr, SvxBoxInfoItemLine::HORI);
+ rLineInner.SetLine(nullptr, SvxBoxInfoItemLine::VERT);
+ rLineInner.SetTable(true);
+ rLineInner.SetDist(true);
+ rLineInner.SetMinDist(false);
+
+ ScLineFlags aFlags;
+
+ if( rMark.IsMultiMarked() )
+ {
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false );
+ size_t nRangeCount = aRangeList.size();
+ bool bMultipleRows = false, bMultipleCols = false;
+ for( size_t nRangeIdx = 0; nRangeIdx < nRangeCount; ++nRangeIdx )
+ {
+ const ScRange & rRange = aRangeList[ nRangeIdx ];
+ bMultipleRows = ( bMultipleRows || ( rRange.aStart.Row() != rRange.aEnd.Row() ) );
+ bMultipleCols = ( bMultipleCols || ( rRange.aStart.Col() != rRange.aEnd.Col() ) );
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ maTabs[rTab]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ }
+ rLineInner.EnableHor( bMultipleRows );
+ rLineInner.EnableVer( bMultipleCols );
+ }
+ else if( rMark.IsMarked() )
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ rLineInner.EnableHor( aRange.aStart.Row() != aRange.aEnd.Row() );
+ rLineInner.EnableVer( aRange.aStart.Col() != aRange.aEnd.Col() );
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ maTabs[rTab]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags,
+ aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row() );
+ }
+ }
+
+ // Evaluate don't care Status
+
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::LEFT, ( aFlags.nLeft != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::RIGHT, ( aFlags.nRight != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::TOP, ( aFlags.nTop != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::BOTTOM, ( aFlags.nBottom != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::HORI, ( aFlags.nHori != SC_LINE_DONTCARE ) );
+ rLineInner.SetValid( SvxBoxInfoItemValidFlags::VERT, ( aFlags.nVert != SC_LINE_DONTCARE ) );
+}
+
+static HasAttrFlags OptimizeHasAttrib( HasAttrFlags nMask, const ScDocumentPool* pPool )
+{
+ if ( nMask & HasAttrFlags::Rotate )
+ {
+ // Is attribute used in document?
+ // (as in fillinfo)
+
+ bool bAnyItem = false;
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_ROTATE_VALUE))
+ {
+ // 90 or 270 degrees is former SvxOrientationItem - only look for other values
+ // (see ScPatternAttr::GetCellOrientation)
+ Degree100 nAngle = static_cast<const ScRotateValueItem*>(pItem)->GetValue();
+ if ( nAngle && nAngle != 9000_deg100 && nAngle != 27000_deg100 )
+ {
+ bAnyItem = true;
+ break;
+ }
+ }
+ if (!bAnyItem)
+ nMask &= ~HasAttrFlags::Rotate;
+ }
+ return nMask;
+}
+
+bool ScDocument::HasAttrib( SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
+ SCCOL nCol2, SCROW nRow2, SCTAB nTab2, HasAttrFlags nMask ) const
+{
+ nMask = OptimizeHasAttrib( nMask, mxPoolHelper->GetDocPool());
+
+ if (nMask == HasAttrFlags::NONE)
+ return false;
+
+ for (SCTAB i=nTab1; i<=nTab2 && i < static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i])
+ {
+ if ( nMask & HasAttrFlags::RightOrCenter )
+ {
+ // On a RTL sheet, don't start to look for the default left value
+ // (which is then logically right), instead always assume true.
+ // That way, ScAttrArray::HasAttrib doesn't have to handle RTL sheets.
+
+ if ( IsLayoutRTL(i) )
+ return true;
+ }
+
+ if( maTabs[i]->HasAttrib( nCol1, nRow1, nCol2, nRow2, nMask ))
+ return true;
+ }
+
+ return false;
+}
+
+bool ScDocument::HasAttrib( SCCOL nCol, SCROW nRow, SCTAB nTab, HasAttrFlags nMask, SCROW* nStartRow, SCROW* nEndRow ) const
+{
+ nMask = OptimizeHasAttrib( nMask, mxPoolHelper->GetDocPool());
+
+ if (nMask == HasAttrFlags::NONE || nTab >= static_cast<SCTAB>(maTabs.size()))
+ {
+ if( nStartRow )
+ *nStartRow = 0;
+ if( nEndRow )
+ *nEndRow = MaxRow();
+ return false;
+ }
+
+ if ( nMask & HasAttrFlags::RightOrCenter )
+ {
+ // On a RTL sheet, don't start to look for the default left value
+ // (which is then logically right), instead always assume true.
+ // That way, ScAttrArray::HasAttrib doesn't have to handle RTL sheets.
+
+ if ( IsLayoutRTL(nTab) )
+ {
+ if( nStartRow )
+ *nStartRow = 0;
+ if( nEndRow )
+ *nEndRow = MaxRow();
+ return true;
+ }
+ }
+
+ return maTabs[nTab]->HasAttrib( nCol, nRow, nMask, nStartRow, nEndRow );
+}
+
+bool ScDocument::HasAttrib( const ScRange& rRange, HasAttrFlags nMask ) const
+{
+ return HasAttrib( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(),
+ rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(),
+ nMask );
+}
+
+void ScDocument::FindMaxRotCol( SCTAB nTab, RowInfo* pRowInfo, SCSIZE nArrCount,
+ SCCOL nX1, SCCOL nX2 ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->FindMaxRotCol( pRowInfo, nArrCount, nX1, nX2 );
+ else
+ {
+ OSL_FAIL("FindMaxRotCol: wrong table");
+ }
+}
+
+void ScDocument::GetBorderLines( SCCOL nCol, SCROW nRow, SCTAB nTab,
+ const SvxBorderLine** ppLeft, const SvxBorderLine** ppTop,
+ const SvxBorderLine** ppRight, const SvxBorderLine** ppBottom ) const
+{
+ //TODO: consider page limits for printing !!!!!
+
+ const SvxBoxItem* pThisAttr = GetEffItem( nCol, nRow, nTab, ATTR_BORDER );
+ OSL_ENSURE(pThisAttr,"where is the attribute?");
+
+ const SvxBorderLine* pLeftLine = pThisAttr->GetLeft();
+ const SvxBorderLine* pTopLine = pThisAttr->GetTop();
+ const SvxBorderLine* pRightLine = pThisAttr->GetRight();
+ const SvxBorderLine* pBottomLine = pThisAttr->GetBottom();
+
+ if ( nCol > 0 )
+ {
+ const SvxBorderLine* pOther = GetEffItem( nCol-1, nRow, nTab, ATTR_BORDER )->GetRight();
+ if ( ScHasPriority( pOther, pLeftLine ) )
+ pLeftLine = pOther;
+ }
+ if ( nRow > 0 )
+ {
+ const SvxBorderLine* pOther = GetEffItem( nCol, nRow-1, nTab, ATTR_BORDER )->GetBottom();
+ if ( ScHasPriority( pOther, pTopLine ) )
+ pTopLine = pOther;
+ }
+ if ( nCol < MaxCol() )
+ {
+ const SvxBorderLine* pOther = GetEffItem( nCol+1, nRow, nTab, ATTR_BORDER )->GetLeft();
+ if ( ScHasPriority( pOther, pRightLine ) )
+ pRightLine = pOther;
+ }
+ if ( nRow < MaxRow() )
+ {
+ const SvxBorderLine* pOther = GetEffItem( nCol, nRow+1, nTab, ATTR_BORDER )->GetTop();
+ if ( ScHasPriority( pOther, pBottomLine ) )
+ pBottomLine = pOther;
+ }
+
+ if (ppLeft)
+ *ppLeft = pLeftLine;
+ if (ppTop)
+ *ppTop = pTopLine;
+ if (ppRight)
+ *ppRight = pRightLine;
+ if (ppBottom)
+ *ppBottom = pBottomLine;
+}
+
+bool ScDocument::IsBlockEmpty(SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ return maTabs[nTab]->IsBlockEmpty( nStartCol, nStartRow, nEndCol, nEndRow );
+
+ OSL_FAIL("wrong table number");
+ return false;
+}
+
+void ScDocument::LockTable(SCTAB nTab)
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->LockTable();
+ else
+ {
+ OSL_FAIL("wrong table number");
+ }
+}
+
+void ScDocument::UnlockTable(SCTAB nTab)
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->UnlockTable();
+ else
+ {
+ OSL_FAIL("wrong table number");
+ }
+}
+
+bool ScDocument::IsBlockEditable( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ bool* pOnlyNotBecauseOfMatrix /* = NULL */,
+ bool bNoMatrixAtAll ) const
+{
+ // import into read-only document is possible
+ if (!bImportingXML && !mbChangeReadOnlyEnabled && mpShell && mpShell->IsReadOnly())
+ {
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ return false;
+ }
+
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ return maTabs[nTab]->IsBlockEditable( nStartCol, nStartRow, nEndCol,
+ nEndRow, pOnlyNotBecauseOfMatrix, bNoMatrixAtAll );
+
+ OSL_FAIL("wrong table number");
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ return false;
+}
+
+bool ScDocument::IsSelectionEditable( const ScMarkData& rMark,
+ bool* pOnlyNotBecauseOfMatrix /* = NULL */ ) const
+{
+ // import into read-only document is possible
+ if ( !bImportingXML && !mbChangeReadOnlyEnabled && mpShell && mpShell->IsReadOnly() )
+ {
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = false;
+ return false;
+ }
+
+ const ScRange& aRange = rMark.GetMarkArea();
+
+ bool bOk = true;
+ bool bMatrix = ( pOnlyNotBecauseOfMatrix != nullptr );
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if ( maTabs[rTab] )
+ {
+ if (rMark.IsMarked())
+ {
+ if ( !maTabs[rTab]->IsBlockEditable( aRange.aStart.Col(),
+ aRange.aStart.Row(), aRange.aEnd.Col(),
+ aRange.aEnd.Row(), pOnlyNotBecauseOfMatrix ) )
+ {
+ bOk = false;
+ if ( pOnlyNotBecauseOfMatrix )
+ bMatrix = *pOnlyNotBecauseOfMatrix;
+ }
+ }
+ if (rMark.IsMultiMarked())
+ {
+ if ( !maTabs[rTab]->IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix ) )
+ {
+ bOk = false;
+ if ( pOnlyNotBecauseOfMatrix )
+ bMatrix = *pOnlyNotBecauseOfMatrix;
+ }
+ }
+ }
+
+ if (!bOk && !bMatrix)
+ break;
+ }
+
+ if ( pOnlyNotBecauseOfMatrix )
+ *pOnlyNotBecauseOfMatrix = ( !bOk && bMatrix );
+
+ return bOk;
+}
+
+bool ScDocument::HasSelectedBlockMatrixFragment( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow,
+ const ScMarkData& rMark ) const
+{
+ bool bOk = true;
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab] && maTabs[rTab]->HasBlockMatrixFragment( nStartCol, nStartRow, nEndCol, nEndRow ))
+ bOk = false;
+
+ if (!bOk)
+ break;
+ }
+
+ return !bOk;
+}
+
+bool ScDocument::GetMatrixFormulaRange( const ScAddress& rCellPos, ScRange& rMatrix )
+{
+ // if rCell is part of a matrix formula, return its complete range
+
+ ScFormulaCell* pFCell = GetFormulaCell(rCellPos);
+ if (!pFCell)
+ // not a formula cell. Bail out.
+ return false;
+
+ ScAddress aOrigin = rCellPos;
+ if (!pFCell->GetMatrixOrigin(*this, aOrigin))
+ // Failed to get the address of the matrix origin.
+ return false;
+
+ if (aOrigin != rCellPos)
+ {
+ pFCell = GetFormulaCell(aOrigin);
+ if (!pFCell)
+ // The matrix origin cell is not a formula cell !? Something is up...
+ return false;
+ }
+
+ SCCOL nSizeX;
+ SCROW nSizeY;
+ pFCell->GetMatColsRows(nSizeX, nSizeY);
+ if (nSizeX <= 0 || nSizeY <= 0)
+ {
+ // GetMatrixEdge computes also dimensions of the matrix
+ // if not already done (may occur if document is loaded
+ // from old file format).
+ // Needs an "invalid" initialized address.
+ aOrigin.SetInvalid();
+ pFCell->GetMatrixEdge(*this, aOrigin);
+ pFCell->GetMatColsRows(nSizeX, nSizeY);
+ }
+
+ if (nSizeX <= 0 || nSizeY <= 0)
+ // Matrix size is still invalid. Give up.
+ return false;
+
+ ScAddress aEnd( aOrigin.Col() + nSizeX - 1,
+ aOrigin.Row() + nSizeY - 1,
+ aOrigin.Tab() );
+
+ rMatrix.aStart = aOrigin;
+ rMatrix.aEnd = aEnd;
+
+ return true;
+}
+
+void ScDocument::ExtendOverlapped( SCCOL& rStartCol, SCROW& rStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) const
+{
+ if ( ValidColRow(rStartCol,rStartRow) && ValidColRow(nEndCol,nEndRow) && ValidTab(nTab) )
+ {
+ if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ {
+ SCCOL nCol;
+ SCCOL nOldCol = rStartCol;
+ SCROW nOldRow = rStartRow;
+ for (nCol=nOldCol; nCol<=nEndCol; nCol++)
+ while (GetAttr(nCol,rStartRow,nTab,ATTR_MERGE_FLAG)->IsVerOverlapped())
+ --rStartRow;
+
+ //TODO: pass on ?
+
+ const ScAttrArray& pAttrArray = maTabs[nTab]->ColumnData(nOldCol).AttrArray();
+ SCSIZE nIndex;
+ if ( pAttrArray.Count() )
+ pAttrArray.Search( nOldRow, nIndex );
+ else
+ nIndex = 0;
+ SCROW nAttrPos = nOldRow;
+ while (nAttrPos<=nEndRow)
+ {
+ OSL_ENSURE( nIndex < pAttrArray.Count(), "Wrong index in AttrArray" );
+
+ bool bHorOverlapped;
+ if ( pAttrArray.Count() )
+ bHorOverlapped = pAttrArray.mvData[nIndex].pPattern->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped();
+ else
+ bHorOverlapped = GetDefPattern()->GetItem(ATTR_MERGE_FLAG).IsHorOverlapped();
+ if ( bHorOverlapped )
+ {
+ SCROW nEndRowSeg = (pAttrArray.Count()) ? pAttrArray.mvData[nIndex].nEndRow : MaxRow();
+ SCROW nLoopEndRow = std::min( nEndRow, nEndRowSeg );
+ for (SCROW nAttrRow = nAttrPos; nAttrRow <= nLoopEndRow; nAttrRow++)
+ {
+ SCCOL nTempCol = nOldCol;
+ do
+ --nTempCol;
+ while (GetAttr(nTempCol,nAttrRow,nTab,ATTR_MERGE_FLAG)->IsHorOverlapped());
+ if (nTempCol < rStartCol)
+ rStartCol = nTempCol;
+ }
+ }
+ if ( pAttrArray.Count() )
+ {
+ nAttrPos = pAttrArray.mvData[nIndex].nEndRow + 1;
+ ++nIndex;
+ }
+ else
+ nAttrPos = MaxRow() + 1;
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("ExtendOverlapped: invalid range");
+ }
+}
+
+void ScDocument::ExtendMergeSel( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow,
+ const ScMarkData& rMark, bool bRefresh )
+{
+ // use all selected sheets from rMark
+
+ SCCOL nOldEndCol = rEndCol;
+ SCROW nOldEndRow = rEndRow;
+
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if ( maTabs[rTab] )
+ {
+ SCCOL nThisEndCol = nOldEndCol;
+ SCROW nThisEndRow = nOldEndRow;
+ ExtendMerge( nStartCol, nStartRow, nThisEndCol, nThisEndRow, rTab, bRefresh );
+ if ( nThisEndCol > rEndCol )
+ rEndCol = nThisEndCol;
+ if ( nThisEndRow > rEndRow )
+ rEndRow = nThisEndRow;
+ }
+ }
+}
+
+bool ScDocument::ExtendMerge( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL& rEndCol, SCROW& rEndRow,
+ SCTAB nTab, bool bRefresh )
+{
+ bool bFound = false;
+ if ( ValidColRow(nStartCol,nStartRow) && ValidColRow(rEndCol,rEndRow) && ValidTab(nTab) )
+ {
+ if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ bFound = maTabs[nTab]->ExtendMerge( nStartCol, nStartRow, rEndCol, rEndRow, bRefresh );
+
+ if (bRefresh)
+ RefreshAutoFilter( nStartCol, nStartRow, rEndCol, rEndRow, nTab );
+ }
+ else
+ {
+ OSL_FAIL("ExtendMerge: invalid range");
+ }
+
+ return bFound;
+}
+
+bool ScDocument::ExtendMerge( ScRange& rRange, bool bRefresh )
+{
+ bool bFound = false;
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ SCCOL nEndCol = rRange.aEnd.Col();
+ SCROW nEndRow = rRange.aEnd.Row();
+
+ PutInOrder( nStartTab, nEndTab );
+ for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
+ {
+ SCCOL nExtendCol = rRange.aEnd.Col();
+ SCROW nExtendRow = rRange.aEnd.Row();
+ if (ExtendMerge( rRange.aStart.Col(), rRange.aStart.Row(),
+ nExtendCol, nExtendRow,
+ nTab, bRefresh ) )
+ {
+ bFound = true;
+ if (nExtendCol > nEndCol) nEndCol = nExtendCol;
+ if (nExtendRow > nEndRow) nEndRow = nExtendRow;
+ }
+ }
+
+ rRange.aEnd.SetCol(nEndCol);
+ rRange.aEnd.SetRow(nEndRow);
+
+ return bFound;
+}
+
+void ScDocument::ExtendTotalMerge( ScRange& rRange ) const
+{
+ // Extend range to merged cells without including any new non-overlapped cells
+ ScRange aExt = rRange;
+ // ExtendMerge() is non-const, but called without refresh.
+ if (!const_cast<ScDocument*>(this)->ExtendMerge( aExt ))
+ return;
+
+ if ( aExt.aEnd.Row() > rRange.aEnd.Row() )
+ {
+ ScRange aTest = aExt;
+ aTest.aStart.SetRow( rRange.aEnd.Row() + 1 );
+ if ( HasAttrib( aTest, HasAttrFlags::NotOverlapped ) )
+ aExt.aEnd.SetRow(rRange.aEnd.Row());
+ }
+ if ( aExt.aEnd.Col() > rRange.aEnd.Col() )
+ {
+ ScRange aTest = aExt;
+ aTest.aStart.SetCol( rRange.aEnd.Col() + 1 );
+ if ( HasAttrib( aTest, HasAttrFlags::NotOverlapped ) )
+ aExt.aEnd.SetCol(rRange.aEnd.Col());
+ }
+
+ rRange = aExt;
+}
+
+void ScDocument::ExtendOverlapped( ScRange& rRange ) const
+{
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCTAB nEndTab = rRange.aEnd.Tab();
+ SCCOL nStartCol = rRange.aStart.Col();
+ SCROW nStartRow = rRange.aStart.Row();
+
+ PutInOrder( nStartTab, nEndTab );
+ for (SCTAB nTab = nStartTab; nTab <= nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++ )
+ {
+ SCCOL nExtendCol = rRange.aStart.Col();
+ SCROW nExtendRow = rRange.aStart.Row();
+ ExtendOverlapped( nExtendCol, nExtendRow,
+ rRange.aEnd.Col(), rRange.aEnd.Row(), nTab );
+ if (nExtendCol < nStartCol)
+ {
+ nStartCol = nExtendCol;
+ }
+ if (nExtendRow < nStartRow)
+ {
+ nStartRow = nExtendRow;
+ }
+ }
+
+ rRange.aStart.SetCol(nStartCol);
+ rRange.aStart.SetRow(nStartRow);
+}
+
+bool ScDocument::RefreshAutoFilter( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
+{
+ SCTAB nDBTab;
+ SCCOL nDBStartCol;
+ SCROW nDBStartRow;
+ SCCOL nDBEndCol;
+ SCROW nDBEndRow;
+
+ // Delete Autofilter
+
+ bool bChange = RemoveFlagsTab( nStartCol,nStartRow, nEndCol,nEndRow, nTab, ScMF::Auto );
+
+ // Set Autofilter
+
+ const ScDBData* pData = nullptr;
+ ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ {
+ if (rxDB->HasAutoFilter())
+ {
+ rxDB->GetArea(nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow);
+ if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow &&
+ nDBStartCol<=nEndCol && nDBEndCol>=nStartCol )
+ {
+ if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow,
+ nDBTab, ScMF::Auto ))
+ bChange = true;
+ }
+ }
+ }
+ if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ pData = maTabs[nTab]->GetAnonymousDBData();
+ else
+ pData=nullptr;
+ if (pData && pData->HasAutoFilter())
+ {
+ pData->GetArea( nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow );
+ if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow &&
+ nDBStartCol<=nEndCol && nDBEndCol>=nStartCol )
+ {
+ if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow,
+ nDBTab, ScMF::Auto ))
+ bChange = true;
+ }
+ }
+ return bChange;
+}
+
+void ScDocument::SkipOverlapped( SCCOL& rCol, SCROW& rRow, SCTAB nTab ) const
+{
+ while (IsHorOverlapped(rCol, rRow, nTab))
+ --rCol;
+ while (IsVerOverlapped(rCol, rRow, nTab))
+ --rRow;
+}
+
+bool ScDocument::IsHorOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab ) const
+{
+ const ScMergeFlagAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG );
+ if (pAttr)
+ return pAttr->IsHorOverlapped();
+ else
+ {
+ OSL_FAIL("Overlapped: Attr==0");
+ return false;
+ }
+}
+
+bool ScDocument::IsVerOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab, SCROW* nStartRow, SCROW* nEndRow ) const
+{
+ SCROW dummy;
+ const ScMergeFlagAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG,
+ nStartRow ? *nStartRow : dummy, nEndRow ? *nEndRow : dummy );
+ if (pAttr)
+ return pAttr->IsVerOverlapped();
+ else
+ {
+ OSL_FAIL("Overlapped: Attr==0");
+ return false;
+ }
+}
+
+void ScDocument::ApplySelectionFrame( const ScMarkData& rMark,
+ const SvxBoxItem& rLineOuter,
+ const SvxBoxInfoItem* pLineInner )
+{
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false );
+ size_t nRangeCount = aRangeList.size();
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if (maTabs[rTab])
+ {
+ for ( size_t j=0; j < nRangeCount; j++ )
+ {
+ const ScRange & rRange = aRangeList[ j ];
+ maTabs[rTab]->ApplyBlockFrame( rLineOuter, pLineInner,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ }
+ }
+ if (!rLineOuter.IsRemoveAdjacentCellBorder())
+ return;
+
+ SvxBoxItem aTmp0(rLineOuter);
+ aTmp0.SetLine( nullptr, SvxBoxItemLine::TOP );
+ aTmp0.SetLine( nullptr, SvxBoxItemLine::BOTTOM );
+ aTmp0.SetLine( nullptr, SvxBoxItemLine::LEFT );
+ aTmp0.SetLine( nullptr, SvxBoxItemLine::RIGHT );
+ SvxBoxItem aLeft( aTmp0 );
+ SvxBoxItem aRight( aTmp0 );
+ SvxBoxItem aTop( aTmp0 );
+ SvxBoxItem aBottom( aTmp0 );
+
+ SvxBoxInfoItem aTmp1( *pLineInner );
+ aTmp1.SetTable( false );
+ aTmp1.SetLine( nullptr, SvxBoxInfoItemLine::HORI );
+ aTmp1.SetLine( nullptr, SvxBoxInfoItemLine::VERT );
+ aTmp1.SetValid( SvxBoxInfoItemValidFlags::ALL, false );
+ aTmp1.SetValid( SvxBoxInfoItemValidFlags::DISTANCE );
+ SvxBoxInfoItem aLeftInfo( aTmp1 );
+ SvxBoxInfoItem aRightInfo( aTmp1 );
+ SvxBoxInfoItem aTopInfo( aTmp1 );
+ SvxBoxInfoItem aBottomInfo( aTmp1 );
+
+ if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::TOP ) && !rLineOuter.GetTop())
+ aTopInfo.SetValid( SvxBoxInfoItemValidFlags::BOTTOM );
+
+ if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::BOTTOM ) && !rLineOuter.GetBottom())
+ aBottomInfo.SetValid( SvxBoxInfoItemValidFlags::TOP );
+
+ if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::LEFT ) && !rLineOuter.GetLeft())
+ aLeftInfo.SetValid( SvxBoxInfoItemValidFlags::RIGHT );
+
+ if (pLineInner->IsValid( SvxBoxInfoItemValidFlags::RIGHT ) && !rLineOuter.GetRight())
+ aRightInfo.SetValid( SvxBoxInfoItemValidFlags::LEFT );
+
+ const ScRangeList& rRangeListTopEnvelope = rMark.GetTopEnvelope();
+ const ScRangeList& rRangeListBottomEnvelope = rMark.GetBottomEnvelope();
+ const ScRangeList& rRangeListLeftEnvelope = rMark.GetLeftEnvelope();
+ const ScRangeList& rRangeListRightEnvelope = rMark.GetRightEnvelope();
+
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+
+ if ( maTabs[rTab] )
+ {
+ size_t nEnvelopeRangeCount = rRangeListTopEnvelope.size();
+ for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
+ {
+ const ScRange & rRange = rRangeListTopEnvelope[ j ];
+ maTabs[rTab]->ApplyBlockFrame( aTop, &aTopInfo,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ nEnvelopeRangeCount = rRangeListBottomEnvelope.size();
+ for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
+ {
+ const ScRange & rRange = rRangeListBottomEnvelope[ j ];
+ maTabs[rTab]->ApplyBlockFrame( aBottom, &aBottomInfo,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ nEnvelopeRangeCount = rRangeListLeftEnvelope.size();
+ for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
+ {
+ const ScRange & rRange = rRangeListLeftEnvelope[ j ];
+ maTabs[rTab]->ApplyBlockFrame( aLeft, &aLeftInfo,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ nEnvelopeRangeCount = rRangeListRightEnvelope.size();
+ for ( size_t j=0; j < nEnvelopeRangeCount; j++ )
+ {
+ const ScRange & rRange = rRangeListRightEnvelope[ j ];
+ maTabs[rTab]->ApplyBlockFrame( aRight, &aRightInfo,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row() );
+ }
+ }
+ }
+}
+
+void ScDocument::ApplyFrameAreaTab(const ScRange& rRange,
+ const SvxBoxItem& rLineOuter,
+ const SvxBoxInfoItem& rLineInner)
+{
+ SCTAB nStartTab = rRange.aStart.Tab();
+ SCTAB nEndTab = rRange.aStart.Tab();
+ for (SCTAB nTab=nStartTab; nTab<=nEndTab && nTab < static_cast<SCTAB>(maTabs.size()); nTab++)
+ if (maTabs[nTab])
+ maTabs[nTab]->ApplyBlockFrame(rLineOuter, &rLineInner,
+ rRange.aStart.Col(), rRange.aStart.Row(),
+ rRange.aEnd.Col(), rRange.aEnd.Row());
+}
+
+void ScDocument::ApplySelectionPattern( const ScPatternAttr& rAttr, const ScMarkData& rMark, ScEditDataArray* pDataArray, bool* const pIsChanged )
+{
+ const SfxItemSet* pSet = &rAttr.GetItemSet();
+ bool bSet = false;
+ sal_uInt16 i;
+ for (i=ATTR_PATTERN_START; i<=ATTR_PATTERN_END && !bSet; i++)
+ if (pSet->GetItemState(i) == SfxItemState::SET)
+ bSet = true;
+
+ if (!bSet)
+ return;
+
+ // ApplySelectionCache needs multi mark
+ if ( rMark.IsMarked() && !rMark.IsMultiMarked() )
+ {
+ const ScRange& aRange = rMark.GetMarkArea();
+ ApplyPatternArea( aRange.aStart.Col(), aRange.aStart.Row(),
+ aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rAttr, pDataArray, pIsChanged );
+ }
+ else
+ {
+ SfxItemPoolCache aCache( mxPoolHelper->GetDocPool(), pSet );
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ApplySelectionCache( &aCache, rMark, pDataArray, pIsChanged );
+ }
+ }
+}
+
+void ScDocument::ChangeSelectionIndent( bool bIncrement, const ScMarkData& rMark )
+{
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ChangeSelectionIndent( bIncrement, rMark );
+ }
+}
+
+void ScDocument::ClearSelectionItems( const sal_uInt16* pWhich, const ScMarkData& rMark )
+{
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->ClearSelectionItems( pWhich, rMark );
+ }
+}
+
+void ScDocument::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
+{
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+
+ std::vector<ScAddress> aGroupPos;
+ // Destroy and reconstruct listeners only if content is affected.
+ bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
+ if (bDelContent)
+ {
+ // Record the positions of top and/or bottom formula groups that
+ // intersect the area borders.
+ sc::EndListeningContext aCxt(*this);
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false);
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ EndListeningIntersectedGroups( aCxt, rRange, &aGroupPos);
+ }
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ SCTAB nMax = static_cast<SCTAB>(maTabs.size());
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->DeleteSelection(nDelFlag, rMark, bBroadcast);
+ }
+
+ if (!bDelContent)
+ return;
+
+ // Re-start listeners on those top bottom groups that have been split.
+ SetNeedsListeningGroups(aGroupPos);
+ StartNeededListeners();
+
+ // If formula groups were split their listeners were destroyed and may
+ // need to be notified now that they're restored,
+ // ScTable::DeleteSelection() couldn't do that.
+ if (aGroupPos.empty())
+ return;
+
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false);
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ SetDirty( aRangeList[i], true);
+ }
+ //Notify listeners on top and bottom of the group that has been split
+ for (size_t i = 0; i < aGroupPos.size(); ++i) {
+ ScFormulaCell *pFormulaCell = GetFormulaCell(aGroupPos[i]);
+ if (pFormulaCell)
+ pFormulaCell->SetDirty(true);
+ }
+}
+
+void ScDocument::DeleteSelectionTab(
+ SCTAB nTab, InsertDeleteFlags nDelFlag, const ScMarkData& rMark )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+
+ std::vector<ScAddress> aGroupPos;
+ // Destroy and reconstruct listeners only if content is affected.
+ bool bDelContent = ((nDelFlag & ~InsertDeleteFlags::CONTENTS) != nDelFlag);
+ if (bDelContent)
+ {
+ // Record the positions of top and/or bottom formula groups that
+ // intersect the area borders.
+ sc::EndListeningContext aCxt(*this);
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false);
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ if (rRange.aStart.Tab() <= nTab && nTab <= rRange.aEnd.Tab())
+ {
+ ScRange aRange( rRange);
+ aRange.aStart.SetTab( nTab);
+ aRange.aEnd.SetTab( nTab);
+ EndListeningIntersectedGroups( aCxt, aRange, &aGroupPos);
+ }
+ }
+ aCxt.purgeEmptyBroadcasters();
+ }
+
+ maTabs[nTab]->DeleteSelection(nDelFlag, rMark);
+
+ if (bDelContent)
+ {
+ // Re-start listeners on those top bottom groups that have been split.
+ SetNeedsListeningGroups(aGroupPos);
+ StartNeededListeners();
+
+ // If formula groups were split their listeners were destroyed and may
+ // need to be notified now that they're restored,
+ // ScTable::DeleteSelection() couldn't do that.
+ if (!aGroupPos.empty())
+ {
+ ScRangeList aRangeList;
+ rMark.FillRangeListWithMarks( &aRangeList, false);
+ for (size_t i = 0; i < aRangeList.size(); ++i)
+ {
+ const ScRange & rRange = aRangeList[i];
+ if (rRange.aStart.Tab() <= nTab && nTab <= rRange.aEnd.Tab())
+ {
+ ScRange aRange( rRange);
+ aRange.aStart.SetTab( nTab);
+ aRange.aEnd.SetTab( nTab);
+ SetDirty( aRange, true);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("wrong table");
+ }
+}
+
+ScPatternAttr* ScDocument::GetDefPattern() const
+{
+ return const_cast<ScPatternAttr*>(&mxPoolHelper->GetDocPool()->GetDefaultItem(ATTR_PATTERN));
+}
+
+ScDocumentPool* ScDocument::GetPool()
+{
+ return mxPoolHelper ? mxPoolHelper->GetDocPool() : nullptr;
+}
+
+ScStyleSheetPool* ScDocument::GetStyleSheetPool() const
+{
+ return mxPoolHelper ? mxPoolHelper->GetStylePool() : nullptr;
+}
+
+bool ScDocument::IsEmptyData(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, SCTAB nTab) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->IsEmptyData(nStartCol, nStartRow, nEndCol, nEndRow);
+ return true;
+}
+
+SCSIZE ScDocument::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, ScDirection eDir )
+{
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ PutInOrder(nStartTab, nEndTab);
+ if (ValidTab(nStartTab) && nStartTab < static_cast<SCTAB>(maTabs.size()))
+ {
+ if (maTabs[nStartTab])
+ return maTabs[nStartTab]->GetEmptyLinesInBlock(nStartCol, nStartRow, nEndCol, nEndRow, eDir);
+ else
+ return 0;
+ }
+ else
+ return 0;
+}
+
+void ScDocument::FindAreaPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, ScMoveDirection eDirection ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->FindAreaPos( rCol, rRow, eDirection );
+}
+
+void ScDocument::GetNextPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, SCCOL nMovX, SCROW nMovY,
+ bool bMarked, bool bUnprotected, const ScMarkData& rMark, SCCOL nTabStartCol ) const
+{
+ OSL_ENSURE( !nMovX || !nMovY, "GetNextPos: only X or Y" );
+
+ ScMarkData aCopyMark = rMark;
+ aCopyMark.SetMarking(false);
+ aCopyMark.MarkToMulti();
+
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->GetNextPos( rCol, rRow, nMovX, nMovY, bMarked, bUnprotected, aCopyMark, nTabStartCol );
+}
+
+// Data operations
+
+void ScDocument::UpdStlShtPtrsFrmNms()
+{
+ ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
+
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_PATTERN))
+ {
+ auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem));
+ if (pPattern)
+ pPattern->UpdateStyleSheet(*this);
+ }
+ const_cast<ScPatternAttr&>(pPool->GetDefaultItem(ATTR_PATTERN)).UpdateStyleSheet(*this);
+}
+
+void ScDocument::StylesToNames()
+{
+ ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
+
+ for (const SfxPoolItem* pItem : pPool->GetItemSurrogates(ATTR_PATTERN))
+ {
+ auto pPattern = const_cast<ScPatternAttr*>(dynamic_cast<const ScPatternAttr*>(pItem));
+ if (pPattern)
+ pPattern->StyleToName();
+ }
+ const_cast<ScPatternAttr&>(pPool->GetDefaultItem(ATTR_PATTERN)).StyleToName();
+}
+
+sal_uInt64 ScDocument::GetCellCount() const
+{
+ sal_uInt64 nCellCount = 0;
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ nCellCount += a->GetCellCount();
+ }
+
+ return nCellCount;
+}
+
+sal_uInt64 ScDocument::GetFormulaGroupCount() const
+{
+ sal_uInt64 nFormulaGroupCount = 0;
+
+ ScFormulaGroupIterator aIter( *const_cast<ScDocument*>(this) );
+ for ( sc::FormulaGroupEntry* ptr = aIter.first(); ptr; ptr = aIter.next())
+ {
+ nFormulaGroupCount++;
+ }
+
+ return nFormulaGroupCount;
+}
+
+sal_uInt64 ScDocument::GetCodeCount() const
+{
+ sal_uInt64 nCodeCount = 0;
+
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ nCodeCount += a->GetCodeCount();
+ }
+
+ return nCodeCount;
+}
+
+void ScDocument::PageStyleModified( SCTAB nTab, const OUString& rNewName )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->PageStyleModified( rNewName );
+}
+
+void ScDocument::SetPageStyle( SCTAB nTab, const OUString& rName )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetPageStyle( rName );
+}
+
+OUString ScDocument::GetPageStyle( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetPageStyle();
+
+ return OUString();
+}
+
+void ScDocument::SetPageSize( SCTAB nTab, const Size& rSize )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetPageSize( rSize );
+}
+
+Size ScDocument::GetPageSize( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetPageSize();
+
+ OSL_FAIL("invalid tab");
+ return Size();
+}
+
+void ScDocument::SetRepeatArea( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->SetRepeatArea( nStartCol, nEndCol, nStartRow, nEndRow );
+}
+
+void ScDocument::InvalidatePageBreaks(SCTAB nTab)
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->InvalidatePageBreaks();
+}
+
+void ScDocument::UpdatePageBreaks( SCTAB nTab, const ScRange* pUserArea )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->UpdatePageBreaks( pUserArea );
+}
+
+void ScDocument::RemoveManualBreaks( SCTAB nTab )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->RemoveManualBreaks();
+}
+
+bool ScDocument::HasManualBreaks( SCTAB nTab ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->HasManualBreaks();
+
+ OSL_FAIL("invalid tab");
+ return false;
+}
+
+void ScDocument::GetDocStat( ScDocStat& rDocStat )
+{
+ rDocStat.nTableCount = GetTableCount();
+ rDocStat.aDocName = aDocName;
+ rDocStat.nFormulaCount = GetFormulaGroupCount();
+ rDocStat.nCellCount = GetCellCount();
+}
+
+bool ScDocument::HasPrintRange()
+{
+ bool bResult = false;
+
+ for (const auto& a : maTabs)
+ {
+ if (!a)
+ continue;
+ bResult = a->IsPrintEntireSheet() || (a->GetPrintRangeCount() > 0);
+ if (bResult)
+ break;
+ }
+
+ return bResult;
+}
+
+bool ScDocument::IsPrintEntireSheet( SCTAB nTab ) const
+{
+ return (ValidTab(nTab) ) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsPrintEntireSheet();
+}
+
+sal_uInt16 ScDocument::GetPrintRangeCount( SCTAB nTab )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetPrintRangeCount();
+
+ return 0;
+}
+
+const ScRange* ScDocument::GetPrintRange( SCTAB nTab, sal_uInt16 nPos )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetPrintRange(nPos);
+
+ return nullptr;
+}
+
+std::optional<ScRange> ScDocument::GetRepeatColRange( SCTAB nTab )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetRepeatColRange();
+
+ return std::nullopt;
+}
+
+std::optional<ScRange> ScDocument::GetRepeatRowRange( SCTAB nTab )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetRepeatRowRange();
+
+ return std::nullopt;
+}
+
+void ScDocument::ClearPrintRanges( SCTAB nTab )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->ClearPrintRanges();
+}
+
+void ScDocument::AddPrintRange( SCTAB nTab, const ScRange& rNew )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->AddPrintRange( rNew );
+}
+
+void ScDocument::SetPrintEntireSheet( SCTAB nTab )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetPrintEntireSheet();
+}
+
+void ScDocument::SetRepeatColRange( SCTAB nTab, std::optional<ScRange> oNew )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetRepeatColRange( std::move(oNew) );
+}
+
+void ScDocument::SetRepeatRowRange( SCTAB nTab, std::optional<ScRange> oNew )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetRepeatRowRange( std::move(oNew) );
+}
+
+std::unique_ptr<ScPrintRangeSaver> ScDocument::CreatePrintRangeSaver() const
+{
+ const SCTAB nCount = static_cast<SCTAB>(maTabs.size());
+ std::unique_ptr<ScPrintRangeSaver> pNew(new ScPrintRangeSaver( nCount ));
+ for (SCTAB i=0; i<nCount; i++)
+ if (maTabs[i])
+ maTabs[i]->FillPrintSaver( pNew->GetTabData(i) );
+ return pNew;
+}
+
+void ScDocument::RestorePrintRanges( const ScPrintRangeSaver& rSaver )
+{
+ const SCTAB nCount = rSaver.GetTabCount();
+ const SCTAB maxIndex = std::min(nCount, static_cast<SCTAB>(maTabs.size()));
+ for (SCTAB i=0; i<maxIndex; i++)
+ if (maTabs[i])
+ maTabs[i]->RestorePrintRanges( rSaver.GetTabData(i) );
+}
+
+bool ScDocument::NeedPageResetAfterTab( SCTAB nTab ) const
+{
+ // The page number count restarts at a sheet, if another template is set at
+ // the preceding one (only compare names) and if a pagenumber is specified (not 0)
+
+ if ( nTab + 1 < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab+1] )
+ {
+ const OUString & rNew = maTabs[nTab+1]->GetPageStyle();
+ if ( rNew != maTabs[nTab]->GetPageStyle() )
+ {
+ SfxStyleSheetBase* pStyle = mxPoolHelper->GetStylePool()->Find( rNew, SfxStyleFamily::Page );
+ if ( pStyle )
+ {
+ const SfxItemSet& rSet = pStyle->GetItemSet();
+ sal_uInt16 nFirst = rSet.Get(ATTR_PAGE_FIRSTPAGENO).GetValue();
+ if ( nFirst != 0 )
+ return true; // Specify page number in new template
+ }
+ }
+ }
+
+ return false; // otherwise not
+}
+
+SfxUndoManager* ScDocument::GetUndoManager()
+{
+ if (!mpUndoManager)
+ {
+ // to support enhanced text edit for draw objects, use an SdrUndoManager
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+
+ SdrUndoManager* pUndoManager = new SdrUndoManager;
+ pUndoManager->SetDocShell(GetDocumentShell());
+ mpUndoManager = pUndoManager;
+ }
+
+ return mpUndoManager;
+}
+
+ScRowBreakIterator* ScDocument::GetRowBreakIterator(SCTAB nTab) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return new ScRowBreakIterator(maTabs[nTab]->maRowPageBreaks);
+ return nullptr;
+}
+
+void ScDocument::AddSubTotalCell(ScFormulaCell* pCell)
+{
+ maSubTotalCells.insert(pCell);
+}
+
+void ScDocument::RemoveSubTotalCell(ScFormulaCell* pCell)
+{
+ maSubTotalCells.erase(pCell);
+}
+
+namespace {
+
+bool lcl_hasDirtyRange(const ScDocument& rDoc, ScFormulaCell* pCell, const ScRange& rDirtyRange)
+{
+ ScDetectiveRefIter aRefIter(rDoc, pCell);
+ ScRange aRange;
+ while (aRefIter.GetNextRef(aRange))
+ {
+ if (aRange.Intersects(rDirtyRange))
+ return true;
+ }
+ return false;
+}
+
+}
+
+void ScDocument::SetSubTotalCellsDirty(const ScRange& rDirtyRange)
+{
+ // to update the list by skipping cells that no longer contain subtotal function.
+ set<ScFormulaCell*> aNewSet;
+
+ bool bOldRecalc = GetAutoCalc();
+ SetAutoCalc(false);
+ for (ScFormulaCell* pCell : maSubTotalCells)
+ {
+ if (pCell->IsSubTotal())
+ {
+ aNewSet.insert(pCell);
+ if (lcl_hasDirtyRange(*this, pCell, rDirtyRange))
+ pCell->SetDirty();
+ }
+ }
+
+ SetAutoCalc(bOldRecalc);
+ maSubTotalCells.swap(aNewSet); // update the list.
+}
+
+sal_uInt16 ScDocument::GetTextWidth( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetTextWidth(rPos.Col(), rPos.Row());
+
+ return 0;
+}
+
+SvtScriptType ScDocument::GetScriptType( const ScAddress& rPos ) const
+{
+ SCTAB nTab = rPos.Tab();
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetScriptType(rPos.Col(), rPos.Row());
+
+ return SvtScriptType::NONE;
+}
+
+void ScDocument::SetScriptType( const ScAddress& rPos, SvtScriptType nType )
+{
+ SCTAB nTab = rPos.Tab();
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetScriptType(rPos.Col(), rPos.Row(), nType);
+}
+
+void ScDocument::EnableUndo( bool bVal )
+{
+ // The undo manager increases lock count every time undo is disabled.
+ // Because of this, we shouldn't disable undo unless it's currently
+ // enabled, or else re-enabling it may not actually re-enable undo unless
+ // the lock count becomes zero.
+
+ if (bVal != GetUndoManager()->IsUndoEnabled())
+ {
+ GetUndoManager()->EnableUndo(bVal);
+ if( mpDrawLayer ) mpDrawLayer->EnableUndo(bVal);
+ }
+
+ mbUndoEnabled = bVal;
+}
+
+void ScDocument::EnableUserInteraction( bool bVal )
+{
+ mbUserInteractionEnabled = bVal;
+}
+
+bool ScDocument::IsInVBAMode() const
+{
+ if (!mpShell)
+ return false;
+
+ try
+ {
+ uno::Reference<script::vba::XVBACompatibility> xVBA(
+ mpShell->GetBasicContainer(), uno::UNO_QUERY);
+
+ return xVBA.is() && xVBA->getVBACompatibilityMode();
+ }
+ catch (const lang::NotInitializedException&) {}
+
+ return false;
+}
+
+// Sparklines
+std::shared_ptr<sc::Sparkline> ScDocument::GetSparkline(ScAddress const& rPosition)
+{
+ SCTAB nTab = rPosition.Tab();
+
+ if (ValidTab(nTab) && nTab < SCTAB(maTabs.size()))
+ {
+ return maTabs[nTab]->GetSparkline(rPosition.Col(), rPosition.Row());
+ }
+ return std::shared_ptr<sc::Sparkline>();
+}
+
+bool ScDocument::HasSparkline(ScAddress const & rPosition)
+{
+ return bool(GetSparkline(rPosition));
+}
+
+sc::Sparkline* ScDocument::CreateSparkline(ScAddress const& rPosition, std::shared_ptr<sc::SparklineGroup> const& pSparklineGroup)
+{
+ SCTAB nTab = rPosition.Tab();
+
+ if (ValidTab(nTab) && nTab < SCTAB(maTabs.size()))
+ {
+ return maTabs[nTab]->CreateSparkline(rPosition.Col(), rPosition.Row(), pSparklineGroup);
+ }
+
+ return nullptr;
+}
+
+bool ScDocument::DeleteSparkline(ScAddress const & rPosition)
+{
+ SCTAB nTab = rPosition.Tab();
+
+ if (TableExists(nTab))
+ {
+ return maTabs[nTab]->DeleteSparkline(rPosition.Col(), rPosition.Row());
+ }
+
+ return false;
+}
+
+sc::SparklineList* ScDocument::GetSparklineList(SCTAB nTab)
+{
+ if (TableExists(nTab))
+ {
+ return &maTabs[nTab]->GetSparklineList();
+ }
+ return nullptr;
+}
+
+bool ScDocument::HasOneSparklineGroup(ScRange const& rRange)
+{
+ std::shared_ptr<sc::SparklineGroup> pSparklineGroup;
+ return GetSparklineGroupInRange(rRange, pSparklineGroup);
+}
+
+bool ScDocument::GetSparklineGroupInRange(ScRange const& rRange, std::shared_ptr<sc::SparklineGroup>& rGroup)
+{
+ std::shared_ptr<sc::SparklineGroup> pFoundGroup;
+ SCTAB nTab = rRange.aStart.Tab();
+
+ for (SCCOL nX = rRange.aStart.Col(); nX <= rRange.aEnd.Col(); nX++)
+ {
+ for (SCROW nY = rRange.aStart.Row(); nY <= rRange.aEnd.Row(); nY++)
+ {
+ auto pSparkline = GetSparkline(ScAddress(nX, nY, nTab));
+ if (!pSparkline)
+ {
+ return false;
+ }
+ else if (!pFoundGroup)
+ {
+ pFoundGroup = pSparkline->getSparklineGroup();
+ }
+ else if (pFoundGroup != pSparkline->getSparklineGroup())
+ {
+ return false;
+ }
+ }
+ }
+
+ rGroup = pFoundGroup;
+ return true;
+}
+
+std::shared_ptr<sc::SparklineGroup> ScDocument::SearchSparklineGroup(tools::Guid const& rGuid)
+{
+ for (auto const& rTable : maTabs)
+ {
+ if (!rTable)
+ continue;
+
+ auto& rSparklineList = rTable->GetSparklineList();
+
+ for (auto const& pSparklineGroup : rSparklineList.getSparklineGroups())
+ {
+ if (pSparklineGroup->getID() == rGuid)
+ return pSparklineGroup;
+ }
+ }
+
+ return std::shared_ptr<sc::SparklineGroup>();
+}
+
+// Notes
+
+ScPostIt* ScDocument::GetNote(const ScAddress& rPos)
+{
+ return GetNote(rPos.Col(), rPos.Row(), rPos.Tab());
+}
+
+ScPostIt* ScDocument::GetNote(SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ return maTabs[nTab]->GetNote(nCol, nRow);
+ else
+ return nullptr;
+
+}
+
+void ScDocument::SetNote(const ScAddress& rPos, std::unique_ptr<ScPostIt> pNote)
+{
+ return SetNote(rPos.Col(), rPos.Row(), rPos.Tab(), std::move(pNote));
+}
+
+void ScDocument::SetNote(SCCOL nCol, SCROW nRow, SCTAB nTab, std::unique_ptr<ScPostIt> pNote)
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ maTabs[nTab]->SetNote(nCol, nRow, std::move(pNote));
+}
+
+bool ScDocument::HasNote(const ScAddress& rPos) const
+{
+ return HasNote(rPos.Col(), rPos.Row(), rPos.Tab());
+}
+
+bool ScDocument::HasNote(SCCOL nCol, SCROW nRow, SCTAB nTab) const
+{
+ if (!ValidColRow(nCol, nRow))
+ return false;
+
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ if (nCol >= pTab->GetAllocatedColumnsCount())
+ return false;
+
+ const ScPostIt* pNote = pTab->aCol[nCol].GetCellNote(nRow);
+ return pNote != nullptr;
+}
+
+bool ScDocument::HasNote(SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ nStartCol = pTab->ClampToAllocatedColumns(nStartCol);
+ nEndCol = pTab->ClampToAllocatedColumns(nEndCol);
+ for (SCCOL nCol = nStartCol; nCol < nEndCol; ++nCol)
+ if (pTab->aCol[nCol].HasCellNote(nStartRow, nEndRow))
+ return true;
+ return false;
+}
+
+bool ScDocument::HasColNotes(SCCOL nCol, SCTAB nTab) const
+{
+ if (!ValidCol(nCol))
+ return false;
+
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return false;
+
+ if (nCol >= pTab->GetAllocatedColumnsCount())
+ return false;
+
+ return pTab->aCol[nCol].HasCellNotes();
+}
+
+bool ScDocument::HasTabNotes(SCTAB nTab) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+
+ if ( !pTab )
+ return false;
+
+ for (SCCOL nCol=0, nColSize = pTab->aCol.size(); nCol < nColSize; ++nCol)
+ if ( HasColNotes(nCol, nTab) )
+ return true;
+
+ return false;
+}
+
+bool ScDocument::HasNotes() const
+{
+ for (SCTAB i = 0; i <= MAXTAB; ++i)
+ {
+ if (HasTabNotes(i))
+ return true;
+ }
+ return false;
+}
+
+std::unique_ptr<ScPostIt> ScDocument::ReleaseNote(const ScAddress& rPos)
+{
+ ScTable* pTab = FetchTable(rPos.Tab());
+ if (!pTab)
+ return nullptr;
+
+ return pTab->ReleaseNote(rPos.Col(), rPos.Row());
+}
+
+ScPostIt* ScDocument::GetOrCreateNote(const ScAddress& rPos)
+{
+ if (HasNote(rPos))
+ return GetNote(rPos);
+ else
+ return CreateNote(rPos);
+}
+
+ScPostIt* ScDocument::CreateNote(const ScAddress& rPos)
+{
+ ScPostIt* pPostIt = new ScPostIt(*this, rPos);
+ SetNote(rPos, std::unique_ptr<ScPostIt>(pPostIt));
+ return pPostIt;
+}
+
+size_t ScDocument::GetNoteCount( SCTAB nTab, SCCOL nCol ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return 0;
+
+ return pTab->GetNoteCount(nCol);
+}
+
+void ScDocument::CreateAllNoteCaptions()
+{
+ for (const auto& a : maTabs)
+ {
+ if (a)
+ a->CreateAllNoteCaptions();
+ }
+}
+
+void ScDocument::ForgetNoteCaptions( const ScRangeList& rRanges, bool bPreserveData )
+{
+ for (size_t i = 0, n = rRanges.size(); i < n; ++i)
+ {
+ const ScRange & rRange = rRanges[i];
+ const ScAddress& s = rRange.aStart;
+ const ScAddress& e = rRange.aEnd;
+ for (SCTAB nTab = s.Tab(); nTab <= e.Tab(); ++nTab)
+ {
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ continue;
+
+ pTab->ForgetNoteCaptions(s.Col(), s.Row(), e.Col(), e.Row(), bPreserveData);
+ }
+ }
+}
+
+CommentCaptionState ScDocument::GetAllNoteCaptionsState( const ScRangeList& rRanges )
+{
+ CommentCaptionState aTmpState = CommentCaptionState::ALLHIDDEN;
+ CommentCaptionState aState = CommentCaptionState::ALLHIDDEN;
+ bool bFirstControl = true;
+ std::vector<sc::NoteEntry> aNotes;
+
+ for (size_t i = 0, n = rRanges.size(); i < n; ++i)
+ {
+ const ScRange & rRange = rRanges[i];
+
+ for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
+ {
+ aState = maTabs[nTab]->GetAllNoteCaptionsState( rRange, aNotes );
+
+ if (aState == CommentCaptionState::MIXED)
+ return aState;
+
+ if (bFirstControl) // it is possible that a range is ALLSHOWN, another range is ALLHIDDEN,
+ { // we have to detect that situation as mixed.
+ aTmpState = aState;
+ bFirstControl = false;
+ }
+ else if(aTmpState != aState)
+ {
+ aState = CommentCaptionState::MIXED;
+ return aState;
+ }
+ }
+ }
+ return aState;
+}
+
+ScAddress ScDocument::GetNotePosition( size_t nIndex ) const
+{
+ for (size_t nTab = 0; nTab < maTabs.size(); ++nTab)
+ {
+ for (SCCOL nCol : GetAllocatedColumnsRange(nTab, 0, MaxCol()))
+ {
+ size_t nColNoteCount = GetNoteCount(nTab, nCol);
+ if (!nColNoteCount)
+ continue;
+
+ if (nIndex >= nColNoteCount)
+ {
+ nIndex -= nColNoteCount;
+ continue;
+ }
+
+ SCROW nRow = GetNotePosition(nTab, nCol, nIndex);
+ if (nRow >= 0)
+ return ScAddress(nCol, nRow, nTab);
+
+ OSL_FAIL("note not found");
+ return ScAddress::INITIALIZE_INVALID;
+ }
+ }
+
+ OSL_FAIL("note not found");
+ return ScAddress::INITIALIZE_INVALID;
+}
+
+ScAddress ScDocument::GetNotePosition( size_t nIndex, SCTAB nTab ) const
+{
+ for (SCCOL nCol : GetAllocatedColumnsRange(nTab, 0, MaxCol()))
+ {
+ size_t nColNoteCount = GetNoteCount(nTab, nCol);
+ if (!nColNoteCount)
+ continue;
+
+ if (nIndex >= nColNoteCount)
+ {
+ nIndex -= nColNoteCount;
+ continue;
+ }
+
+ SCROW nRow = GetNotePosition(nTab, nCol, nIndex);
+ if (nRow >= 0)
+ return ScAddress(nCol, nRow, nTab);
+
+ OSL_FAIL("note not found");
+ return ScAddress::INITIALIZE_INVALID;
+ }
+
+ OSL_FAIL("note not found");
+ return ScAddress::INITIALIZE_INVALID;
+}
+
+SCROW ScDocument::GetNotePosition( SCTAB nTab, SCCOL nCol, size_t nIndex ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return -1;
+
+ return pTab->GetNotePosition(nCol, nIndex);
+}
+
+void ScDocument::GetAllNoteEntries( std::vector<sc::NoteEntry>& rNotes ) const
+{
+ for (const auto & pTab : maTabs)
+ {
+ if (!pTab)
+ continue;
+
+ pTab->GetAllNoteEntries(rNotes);
+ }
+}
+
+void ScDocument::GetAllNoteEntries( SCTAB nTab, std::vector<sc::NoteEntry>& rNotes ) const
+{
+ const ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ return pTab->GetAllNoteEntries( rNotes );
+}
+
+void ScDocument::GetNotesInRange( const ScRangeList& rRangeList, std::vector<sc::NoteEntry>& rNotes ) const
+{
+ for( size_t i = 0; i < rRangeList.size(); ++i)
+ {
+ const ScRange & rRange = rRangeList[i];
+ for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
+ {
+ maTabs[nTab]->GetNotesInRange( rRange, rNotes );
+ }
+ }
+}
+
+void ScDocument::GetUnprotectedCells( ScRangeList& rRangeList, SCTAB nTab ) const
+{
+ maTabs[nTab]->GetUnprotectedCells( rRangeList );
+}
+
+bool ScDocument::ContainsNotesInRange( const ScRangeList& rRangeList ) const
+{
+ for( size_t i = 0; i < rRangeList.size(); ++i)
+ {
+ const ScRange & rRange = rRangeList[i];
+ for( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); ++nTab )
+ {
+ bool bContainsNote = maTabs[nTab]->ContainsNotesInRange( rRange );
+ if(bContainsNote)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ScDocument::SetAutoNameCache( std::unique_ptr<ScAutoNameCache> pCache )
+{
+ pAutoNameCache = std::move(pCache);
+}
+
+thread_local ScDocumentThreadSpecific ScDocument::maThreadSpecific;
+
+ScRecursionHelper& ScDocument::GetRecursionHelper()
+{
+ if (!IsThreadedGroupCalcInProgress())
+ {
+ if (!maNonThreaded.xRecursionHelper)
+ maNonThreaded.xRecursionHelper = std::make_unique<ScRecursionHelper>();
+ return *maNonThreaded.xRecursionHelper;
+ }
+ else
+ {
+ if (!maThreadSpecific.xRecursionHelper)
+ maThreadSpecific.xRecursionHelper = std::make_unique<ScRecursionHelper>();
+ return *maThreadSpecific.xRecursionHelper;
+ }
+}
+
+void ScDocument::SetupContextFromNonThreadedContext(ScInterpreterContext& /*threadedContext*/, int /*threadNumber*/)
+{
+ (void)this;
+ // lookup cache is now only in pooled ScInterpreterContext's
+}
+
+void ScDocument::MergeContextBackIntoNonThreadedContext(ScInterpreterContext& threadedContext, int /*threadNumber*/)
+{
+ // Move data from a context used by a calculation thread to the main thread's context.
+ // Called from the main thread after the calculation thread has already finished.
+ assert(!IsThreadedGroupCalcInProgress());
+ maInterpreterContext.maDelayedSetNumberFormat.insert(
+ maInterpreterContext.maDelayedSetNumberFormat.end(),
+ std::make_move_iterator(threadedContext.maDelayedSetNumberFormat.begin()),
+ std::make_move_iterator(threadedContext.maDelayedSetNumberFormat.end()));
+ // lookup cache is now only in pooled ScInterpreterContext's
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */