summaryrefslogtreecommitdiffstats
path: root/sc/source/core/data/documen3.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'sc/source/core/data/documen3.cxx')
-rw-r--r--sc/source/core/data/documen3.cxx2155
1 files changed, 2155 insertions, 0 deletions
diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx
new file mode 100644
index 000000000..77afc2ff7
--- /dev/null
+++ b/sc/source/core/data/documen3.cxx
@@ -0,0 +1,2155 @@
+/* -*- 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 <com/sun/star/script/vba/XVBAEventProcessor.hpp>
+#include <com/sun/star/sheet/TableValidationVisibility.hpp>
+#include <scitems.hxx>
+#include <editeng/langitem.hxx>
+#include <svl/srchitem.hxx>
+#include <sfx2/linkmgr.hxx>
+#include <sfx2/bindings.hxx>
+#include <sfx2/objsh.hxx>
+#include <sfx2/viewsh.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/thread.hxx>
+#include <osl/diagnose.h>
+#include <document.hxx>
+#include <attrib.hxx>
+#include <table.hxx>
+#include <rangenam.hxx>
+#include <dbdata.hxx>
+#include <docpool.hxx>
+#include <poolhelp.hxx>
+#include <rangelst.hxx>
+#include <chartlock.hxx>
+#include <refupdat.hxx>
+#include <docoptio.hxx>
+#include <scmod.hxx>
+#include <clipoptions.hxx>
+#include <viewopti.hxx>
+#include <scextopt.hxx>
+#include <tablink.hxx>
+#include <externalrefmgr.hxx>
+#include <markdata.hxx>
+#include <validat.hxx>
+#include <dociter.hxx>
+#include <detdata.hxx>
+#include <inputopt.hxx>
+#include <chartlis.hxx>
+#include <sc.hrc>
+#include <hints.hxx>
+#include <dpobject.hxx>
+#include <drwlayer.hxx>
+#include <unoreflist.hxx>
+#include <listenercalls.hxx>
+#include <tabprotection.hxx>
+#include <formulaparserpool.hxx>
+#include <clipparam.hxx>
+#include <sheetevents.hxx>
+#include <queryentry.hxx>
+#include <formulacell.hxx>
+#include <refupdatecontext.hxx>
+#include <scopetools.hxx>
+#include <filterentries.hxx>
+#include <queryparam.hxx>
+
+#include <globalnames.hxx>
+#include <LibreOfficeKit/LibreOfficeKitEnums.h>
+#include <comphelper/lok.hxx>
+#include <config_fuzzers.h>
+#include <memory>
+
+using namespace com::sun::star;
+
+namespace {
+
+void sortAndRemoveDuplicates(std::vector<ScTypedStrData>& rStrings, bool bCaseSens)
+{
+ if (bCaseSens)
+ {
+ std::sort(rStrings.begin(), rStrings.end(), ScTypedStrData::LessCaseSensitive());
+ std::vector<ScTypedStrData>::iterator it =
+ std::unique(rStrings.begin(), rStrings.end(), ScTypedStrData::EqualCaseSensitive());
+ rStrings.erase(it, rStrings.end());
+ }
+ else
+ {
+ std::sort(rStrings.begin(), rStrings.end(), ScTypedStrData::LessCaseInsensitive());
+ std::vector<ScTypedStrData>::iterator it =
+ std::unique(rStrings.begin(), rStrings.end(), ScTypedStrData::EqualCaseInsensitive());
+ rStrings.erase(it, rStrings.end());
+ }
+}
+
+}
+
+void ScDocument::GetAllTabRangeNames(ScRangeName::TabNameCopyMap& rNames) const
+{
+ ScRangeName::TabNameCopyMap aNames;
+ for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); ++i)
+ {
+ if (!maTabs[i])
+ // no more tables to iterate through.
+ break;
+
+ const ScRangeName* p = maTabs[i]->mpRangeName.get();
+ if (!p || p->empty())
+ // ignore empty ones.
+ continue;
+
+ aNames.emplace(i, p);
+ }
+ rNames.swap(aNames);
+}
+
+void ScDocument::SetAllRangeNames(const std::map<OUString, std::unique_ptr<ScRangeName>>& rRangeMap)
+{
+ for (const auto& [rName, rxRangeName] : rRangeMap)
+ {
+ if (rName == STR_GLOBAL_RANGE_NAME)
+ {
+ pRangeName.reset();
+ const ScRangeName *const pName = rxRangeName.get();
+ if (!pName->empty())
+ pRangeName.reset( new ScRangeName( *pName ) );
+ }
+ else
+ {
+ const ScRangeName *const pName = rxRangeName.get();
+ SCTAB nTab;
+ bool bFound = GetTable(rName, nTab);
+ assert(bFound); (void)bFound; // fouled up?
+ if (pName->empty())
+ SetRangeName( nTab, nullptr );
+ else
+ SetRangeName( nTab, std::unique_ptr<ScRangeName>(new ScRangeName( *pName )) );
+ }
+ }
+}
+
+void ScDocument::GetRangeNameMap(std::map<OUString, ScRangeName*>& aRangeNameMap)
+{
+ for (SCTAB i = 0; i < static_cast<SCTAB>(maTabs.size()); ++i)
+ {
+ if (!maTabs[i])
+ continue;
+ ScRangeName* p = maTabs[i]->GetRangeName();
+ if (!p )
+ {
+ p = new ScRangeName();
+ SetRangeName(i, std::unique_ptr<ScRangeName>(p));
+ }
+ OUString aTableName = maTabs[i]->GetName();
+ aRangeNameMap.insert(std::pair<OUString, ScRangeName*>(aTableName,p));
+ }
+ if (!pRangeName)
+ {
+ pRangeName.reset(new ScRangeName());
+ }
+ aRangeNameMap.insert(std::pair<OUString, ScRangeName*>(STR_GLOBAL_RANGE_NAME, pRangeName.get()));
+}
+
+ScRangeName* ScDocument::GetRangeName(SCTAB nTab) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return nullptr;
+
+ return maTabs[nTab]->GetRangeName();
+}
+
+ScRangeName* ScDocument::GetRangeName() const
+{
+ if (!pRangeName)
+ pRangeName.reset(new ScRangeName);
+ return pRangeName.get();
+}
+
+void ScDocument::SetRangeName(SCTAB nTab, std::unique_ptr<ScRangeName> pNew)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ return;
+
+ return maTabs[nTab]->SetRangeName(std::move(pNew));
+}
+
+void ScDocument::SetRangeName( std::unique_ptr<ScRangeName> pNewRangeName )
+{
+ pRangeName = std::move(pNewRangeName);
+}
+
+bool ScDocument::IsAddressInRangeName( RangeNameScope eScope, const ScAddress& rAddress )
+{
+ ScRangeName* pRangeNames;
+ ScRange aNameRange;
+
+ if (eScope == RangeNameScope::GLOBAL)
+ pRangeNames= GetRangeName();
+ else
+ pRangeNames= GetRangeName(rAddress.Tab());
+
+ for (const auto& rEntry : *pRangeNames)
+ {
+ if (rEntry.second->IsValidReference(aNameRange))
+ {
+ if (aNameRange.Contains(rAddress))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool ScDocument::InsertNewRangeName( const OUString& rName, const ScAddress& rPos, const OUString& rExpr )
+{
+ ScRangeName* pGlobalNames = GetRangeName();
+ if (!pGlobalNames)
+ return false;
+
+ ScRangeData* pName = new ScRangeData(*this, rName, rExpr, rPos, ScRangeData::Type::Name, GetGrammar());
+ return pGlobalNames->insert(pName);
+}
+
+bool ScDocument::InsertNewRangeName( SCTAB nTab, const OUString& rName, const ScAddress& rPos, const OUString& rExpr )
+{
+ ScRangeName* pLocalNames = GetRangeName(nTab);
+ if (!pLocalNames)
+ return false;
+
+ ScRangeData* pName = new ScRangeData(*this, rName, rExpr, rPos, ScRangeData::Type::Name, GetGrammar());
+ return pLocalNames->insert(pName);
+}
+
+const ScRangeData* ScDocument::GetRangeAtBlock( const ScRange& rBlock, OUString& rName, bool* pSheetLocal ) const
+{
+ const ScRangeData* pData = nullptr;
+ if (rBlock.aStart.Tab() == rBlock.aEnd.Tab())
+ {
+ const ScRangeName* pLocalNames = GetRangeName(rBlock.aStart.Tab());
+ if (pLocalNames)
+ {
+ pData = pLocalNames->findByRange( rBlock );
+ if (pData)
+ {
+ rName = pData->GetName();
+ if (pSheetLocal)
+ *pSheetLocal = true;
+ return pData;
+ }
+ }
+ }
+ if ( pRangeName )
+ {
+ pData = pRangeName->findByRange( rBlock );
+ if (pData)
+ {
+ rName = pData->GetName();
+ if (pSheetLocal)
+ *pSheetLocal = false;
+ }
+ }
+ return pData;
+}
+
+ScRangeData* ScDocument::FindRangeNameBySheetAndIndex( SCTAB nTab, sal_uInt16 nIndex ) const
+{
+ const ScRangeName* pRN = (nTab < 0 ? GetRangeName() : GetRangeName(nTab));
+ return (pRN ? pRN->findByIndex( nIndex) : nullptr);
+}
+
+void ScDocument::SetDBCollection( std::unique_ptr<ScDBCollection> pNewDBCollection, bool bRemoveAutoFilter )
+{
+ if (pDBCollection && bRemoveAutoFilter)
+ {
+ // remove auto filter attribute if new db data don't contain auto filter flag
+ // start position is also compared, so bRemoveAutoFilter must not be set from ref-undo!
+
+ ScDBCollection::NamedDBs& rNamedDBs = pDBCollection->getNamedDBs();
+ for (const auto& rxNamedDB : rNamedDBs)
+ {
+ const ScDBData& rOldData = *rxNamedDB;
+ if (!rOldData.HasAutoFilter())
+ continue;
+
+ ScRange aOldRange;
+ rOldData.GetArea(aOldRange);
+
+ bool bFound = false;
+ if (pNewDBCollection)
+ {
+ ScDBData* pNewData = pNewDBCollection->getNamedDBs().findByUpperName(rOldData.GetUpperName());
+ if (pNewData)
+ {
+ if (pNewData->HasAutoFilter())
+ {
+ ScRange aNewRange;
+ pNewData->GetArea(aNewRange);
+ if (aOldRange.aStart == aNewRange.aStart)
+ bFound = true;
+ }
+ }
+ }
+
+ if (!bFound)
+ {
+ aOldRange.aEnd.SetRow(aOldRange.aStart.Row());
+ RemoveFlagsTab( aOldRange.aStart.Col(), aOldRange.aStart.Row(),
+ aOldRange.aEnd.Col(), aOldRange.aEnd.Row(),
+ aOldRange.aStart.Tab(), ScMF::Auto );
+ RepaintRange( aOldRange );
+ }
+ }
+ }
+
+ pDBCollection = std::move(pNewDBCollection);
+}
+
+const ScDBData* ScDocument::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion) const
+{
+ if (pDBCollection)
+ return pDBCollection->GetDBAtCursor(nCol, nRow, nTab, ePortion);
+ else
+ return nullptr;
+}
+
+ScDBData* ScDocument::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, ScDBDataPortion ePortion)
+{
+ if (pDBCollection)
+ return pDBCollection->GetDBAtCursor(nCol, nRow, nTab, ePortion);
+ else
+ return nullptr;
+}
+
+const ScDBData* ScDocument::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const
+{
+ if (pDBCollection)
+ return pDBCollection->GetDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2);
+ else
+ return nullptr;
+}
+
+ScDBData* ScDocument::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2)
+{
+ if (pDBCollection)
+ return pDBCollection->GetDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2);
+ else
+ return nullptr;
+}
+
+void ScDocument::RefreshDirtyTableColumnNames()
+{
+ if (pDBCollection)
+ pDBCollection->RefreshDirtyTableColumnNames();
+}
+
+bool ScDocument::HasPivotTable() const
+{
+ return pDPCollection && pDPCollection->GetCount();
+}
+
+ScDPCollection* ScDocument::GetDPCollection()
+{
+ if (!pDPCollection)
+ pDPCollection.reset( new ScDPCollection(*this) );
+ return pDPCollection.get();
+}
+
+const ScDPCollection* ScDocument::GetDPCollection() const
+{
+ return pDPCollection.get();
+}
+
+ScDPObject* ScDocument::GetDPAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab) const
+{
+ if (!pDPCollection)
+ return nullptr;
+
+ sal_uInt16 nCount = pDPCollection->GetCount();
+ ScAddress aPos( nCol, nRow, nTab );
+ for (sal_uInt16 i=0; i<nCount; i++)
+ if ( (*pDPCollection)[i].GetOutRange().Contains( aPos ) )
+ return &(*pDPCollection)[i];
+
+ return nullptr;
+}
+
+ScDPObject* ScDocument::GetDPAtBlock( const ScRange & rBlock ) const
+{
+ if (!pDPCollection)
+ return nullptr;
+
+ /* Walk the collection in reverse order to get something of an
+ * approximation of MS Excels 'most recent' effect. */
+ sal_uInt16 i = pDPCollection->GetCount();
+ while ( i-- > 0 )
+ if ( (*pDPCollection)[i].GetOutRange().Contains( rBlock ) )
+ return &(*pDPCollection)[i];
+
+ return nullptr;
+}
+
+void ScDocument::StopTemporaryChartLock()
+{
+ if (apTemporaryChartLock)
+ apTemporaryChartLock->StopLocking();
+}
+
+void ScDocument::SetChartListenerCollection(
+ std::unique_ptr<ScChartListenerCollection> pNewChartListenerCollection,
+ bool bSetChartRangeLists )
+{
+ std::unique_ptr<ScChartListenerCollection> pOld = std::move(pChartListenerCollection);
+ pChartListenerCollection = std::move(pNewChartListenerCollection);
+ if ( pChartListenerCollection )
+ {
+ if ( pOld )
+ pChartListenerCollection->SetDiffDirty( *pOld, bSetChartRangeLists );
+ pChartListenerCollection->StartAllListeners();
+ }
+}
+
+void ScDocument::SetScenario( SCTAB nTab, bool bFlag )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetScenario(bFlag);
+}
+
+bool ScDocument::IsScenario( SCTAB nTab ) const
+{
+ return ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] &&maTabs[nTab]->IsScenario();
+}
+
+void ScDocument::SetScenarioData( SCTAB nTab, const OUString& rComment,
+ const Color& rColor, ScScenarioFlags nFlags )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsScenario())
+ {
+ maTabs[nTab]->SetScenarioComment( rComment );
+ maTabs[nTab]->SetScenarioColor( rColor );
+ maTabs[nTab]->SetScenarioFlags( nFlags );
+ }
+}
+
+Color ScDocument::GetTabBgColor( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetTabBgColor();
+ return COL_AUTO;
+}
+
+void ScDocument::SetTabBgColor( SCTAB nTab, const Color& rColor )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetTabBgColor(rColor);
+}
+
+bool ScDocument::IsDefaultTabBgColor( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetTabBgColor() == COL_AUTO;
+ return true;
+}
+
+void ScDocument::GetScenarioData( SCTAB nTab, OUString& rComment,
+ Color& rColor, ScScenarioFlags& rFlags ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsScenario())
+ {
+ maTabs[nTab]->GetScenarioComment( rComment );
+ rColor = maTabs[nTab]->GetScenarioColor();
+ rFlags = maTabs[nTab]->GetScenarioFlags();
+ }
+}
+
+void ScDocument::GetScenarioFlags( SCTAB nTab, ScScenarioFlags& rFlags ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsScenario())
+ rFlags = maTabs[nTab]->GetScenarioFlags();
+}
+
+bool ScDocument::IsLinked( SCTAB nTab ) const
+{
+ return ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsLinked();
+ // equivalent to
+ //if (ValidTab(nTab) && pTab[nTab])
+ // return pTab[nTab]->IsLinked();
+ //return false;
+}
+
+formula::FormulaGrammar::AddressConvention ScDocument::GetAddressConvention() const
+{
+ return formula::FormulaGrammar::extractRefConvention(eGrammar);
+}
+
+void ScDocument::SetGrammar( formula::FormulaGrammar::Grammar eGram )
+{
+ eGrammar = eGram;
+}
+
+ScLinkMode ScDocument::GetLinkMode( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetLinkMode();
+ return ScLinkMode::NONE;
+}
+
+OUString ScDocument::GetLinkDoc( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetLinkDoc();
+ return OUString();
+}
+
+OUString ScDocument::GetLinkFlt( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetLinkFlt();
+ return OUString();
+}
+
+OUString ScDocument::GetLinkOpt( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetLinkOpt();
+ return OUString();
+}
+
+OUString ScDocument::GetLinkTab( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetLinkTab();
+ return OUString();
+}
+
+sal_uLong ScDocument::GetLinkRefreshDelay( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetLinkRefreshDelay();
+ return 0;
+}
+
+void ScDocument::SetLink( SCTAB nTab, ScLinkMode nMode, const OUString& rDoc,
+ const OUString& rFilter, const OUString& rOptions,
+ const OUString& rTabName, sal_uLong nRefreshDelay )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetLink( nMode, rDoc, rFilter, rOptions, rTabName, nRefreshDelay );
+}
+
+bool ScDocument::HasLink( std::u16string_view rDoc,
+ std::u16string_view rFilter, std::u16string_view rOptions ) const
+{
+ SCTAB nCount = static_cast<SCTAB>(maTabs.size());
+ for (SCTAB i=0; i<nCount; i++)
+ if (maTabs[i]->IsLinked()
+ && maTabs[i]->GetLinkDoc() == rDoc
+ && maTabs[i]->GetLinkFlt() == rFilter
+ && maTabs[i]->GetLinkOpt() == rOptions)
+ return true;
+
+ return false;
+}
+
+bool ScDocument::LinkExternalTab( SCTAB& rTab, const OUString& aDocTab,
+ const OUString& aFileName, const OUString& aTabName )
+{
+ if ( IsClipboard() )
+ {
+ OSL_FAIL( "LinkExternalTab in Clipboard" );
+ return false;
+ }
+ rTab = 0;
+#if ENABLE_FUZZERS
+ (void)aDocTab;
+ (void)aFileName;
+ (void)aTabName;
+ return false;
+#else
+ OUString aFilterName; // Is filled by the Loader
+ OUString aOptions; // Filter options
+ sal_uInt32 nLinkCnt = pExtDocOptions ? pExtDocOptions->GetDocSettings().mnLinkCnt : 0;
+ ScDocumentLoader aLoader( aFileName, aFilterName, aOptions, nLinkCnt + 1 );
+ if ( aLoader.IsError() )
+ return false;
+ ScDocument* pSrcDoc = aLoader.GetDocument();
+
+ // Copy table
+ SCTAB nSrcTab;
+ if ( pSrcDoc->GetTable( aTabName, nSrcTab ) )
+ {
+ if ( !InsertTab( SC_TAB_APPEND, aDocTab, true ) )
+ {
+ OSL_FAIL("can't insert external document table");
+ return false;
+ }
+ rTab = GetTableCount() - 1;
+ // Don't insert anew, just the results
+ TransferTab( *pSrcDoc, nSrcTab, rTab, false, true );
+ }
+ else
+ return false;
+
+ sal_uLong nRefreshDelay = 0;
+
+ bool bWasThere = HasLink( aFileName, aFilterName, aOptions );
+ SetLink( rTab, ScLinkMode::VALUE, aFileName, aFilterName, aOptions, aTabName, nRefreshDelay );
+ if ( !bWasThere ) // Add link only once per source document
+ {
+ ScTableLink* pLink = new ScTableLink( mpShell, aFileName, aFilterName, aOptions, nRefreshDelay );
+ pLink->SetInCreate( true );
+ OUString aFilName = aFilterName;
+ GetLinkManager()->InsertFileLink( *pLink, sfx2::SvBaseLinkObjectType::ClientFile, aFileName, &aFilName );
+ pLink->Update();
+ pLink->SetInCreate( false );
+ SfxBindings* pBindings = GetViewBindings();
+ if (pBindings)
+ pBindings->Invalidate( SID_LINKS );
+ }
+ return true;
+#endif
+}
+
+ScExternalRefManager* ScDocument::GetExternalRefManager() const
+{
+ ScDocument* pThis = const_cast<ScDocument*>(this);
+ if (!pExternalRefMgr)
+ pThis->pExternalRefMgr.reset( new ScExternalRefManager(*pThis));
+
+ return pExternalRefMgr.get();
+}
+
+bool ScDocument::IsInExternalReferenceMarking() const
+{
+ return pExternalRefMgr && pExternalRefMgr->isInReferenceMarking();
+}
+
+void ScDocument::MarkUsedExternalReferences()
+{
+ if (!pExternalRefMgr)
+ return;
+ if (!pExternalRefMgr->hasExternalData())
+ return;
+ // Charts.
+ pExternalRefMgr->markUsedByLinkListeners();
+ // Formula cells.
+ pExternalRefMgr->markUsedExternalRefCells();
+
+ /* NOTE: Conditional formats and validation objects are marked when
+ * collecting them during export. */
+}
+
+ScFormulaParserPool& ScDocument::GetFormulaParserPool() const
+{
+ if (!mxFormulaParserPool)
+ mxFormulaParserPool.reset( new ScFormulaParserPool( *this ) );
+ return *mxFormulaParserPool;
+}
+
+const ScSheetEvents* ScDocument::GetSheetEvents( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetSheetEvents();
+ return nullptr;
+}
+
+void ScDocument::SetSheetEvents( SCTAB nTab, std::unique_ptr<ScSheetEvents> pNew )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetSheetEvents( std::move(pNew) );
+}
+
+bool ScDocument::HasSheetEventScript( SCTAB nTab, ScSheetEventId nEvent, bool bWithVbaEvents ) const
+{
+ if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ {
+ // check if any event handler script has been configured
+ const ScSheetEvents* pEvents = maTabs[nTab]->GetSheetEvents();
+ if ( pEvents && pEvents->GetScript( nEvent ) )
+ return true;
+ // check if VBA event handlers exist
+ if (bWithVbaEvents && mxVbaEvents.is()) try
+ {
+ uno::Sequence< uno::Any > aArgs{ uno::Any(nTab) };
+ if (mxVbaEvents->hasVbaEventHandler( ScSheetEvents::GetVbaSheetEventId( nEvent ), aArgs ) ||
+ mxVbaEvents->hasVbaEventHandler( ScSheetEvents::GetVbaDocumentEventId( nEvent ), uno::Sequence< uno::Any >() ))
+ return true;
+ }
+ catch( uno::Exception& )
+ {
+ }
+ }
+ return false;
+}
+
+bool ScDocument::HasAnySheetEventScript( ScSheetEventId nEvent, bool bWithVbaEvents ) const
+{
+ SCTAB nSize = static_cast<SCTAB>(maTabs.size());
+ for (SCTAB nTab = 0; nTab < nSize; nTab++)
+ if (HasSheetEventScript( nTab, nEvent, bWithVbaEvents ))
+ return true;
+ return false;
+}
+
+bool ScDocument::HasAnyCalcNotification() const
+{
+ SCTAB nSize = static_cast<SCTAB>(maTabs.size());
+ for (SCTAB nTab = 0; nTab < nSize; nTab++)
+ if (maTabs[nTab] && maTabs[nTab]->GetCalcNotification())
+ return true;
+ return false;
+}
+
+bool ScDocument::HasCalcNotification( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetCalcNotification();
+ return false;
+}
+
+void ScDocument::SetCalcNotification( SCTAB nTab )
+{
+ // set only if not set before
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && !maTabs[nTab]->GetCalcNotification())
+ maTabs[nTab]->SetCalcNotification(true);
+}
+
+void ScDocument::ResetCalcNotifications()
+{
+ SCTAB nSize = static_cast<SCTAB>(maTabs.size());
+ for (SCTAB nTab = 0; nTab < nSize; nTab++)
+ if (maTabs[nTab] && maTabs[nTab]->GetCalcNotification())
+ maTabs[nTab]->SetCalcNotification(false);
+}
+
+ScOutlineTable* ScDocument::GetOutlineTable( SCTAB nTab, bool bCreate )
+{
+ ScOutlineTable* pVal = nullptr;
+
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ if (maTabs[nTab])
+ {
+ pVal = maTabs[nTab]->GetOutlineTable();
+ if (!pVal && bCreate)
+ {
+ maTabs[nTab]->StartOutlineTable();
+ pVal = maTabs[nTab]->GetOutlineTable();
+ }
+ }
+
+ return pVal;
+}
+
+bool ScDocument::SetOutlineTable( SCTAB nTab, const ScOutlineTable* pNewOutline )
+{
+ return ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->SetOutlineTable(pNewOutline);
+}
+
+void ScDocument::DoAutoOutline( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow );
+}
+
+bool ScDocument::TestRemoveSubTotals( SCTAB nTab, const ScSubTotalParam& rParam )
+{
+ return ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->TestRemoveSubTotals( rParam );
+}
+
+void ScDocument::RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->RemoveSubTotals( rParam );
+}
+
+bool ScDocument::DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam )
+{
+ return ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->DoSubTotals( rParam );
+}
+
+bool ScDocument::HasSubTotalCells( const ScRange& rRange )
+{
+ ScCellIterator aIter(*this, rRange);
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (aIter.getType() != CELLTYPE_FORMULA)
+ continue;
+
+ if (aIter.getFormulaCell()->IsSubTotal())
+ return true;
+ }
+ return false; // none found
+}
+
+/**
+ * From this document this method copies the cells of positions at which
+ * there are also cells in pPosDoc to pDestDoc
+ */
+void ScDocument::CopyUpdated( ScDocument* pPosDoc, ScDocument* pDestDoc )
+{
+ SCTAB nCount = static_cast<SCTAB>(maTabs.size());
+ for (SCTAB nTab=0; nTab<nCount; nTab++)
+ if (maTabs[nTab] && pPosDoc->maTabs[nTab] && pDestDoc->maTabs[nTab])
+ maTabs[nTab]->CopyUpdated( pPosDoc->maTabs[nTab].get(), pDestDoc->maTabs[nTab].get() );
+}
+
+void ScDocument::CopyScenario( SCTAB nSrcTab, SCTAB nDestTab, bool bNewScenario )
+{
+ if (!(ValidTab(nSrcTab) && ValidTab(nDestTab) && nSrcTab < static_cast<SCTAB>(maTabs.size())
+ && nDestTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nSrcTab] && maTabs[nDestTab]))
+ return;
+
+ // Set flags correctly for active scenarios
+ // and write current values back to recently active scenarios
+ ScRangeList aRanges = *maTabs[nSrcTab]->GetScenarioRanges();
+
+ // nDestTab is the target table
+ for ( SCTAB nTab = nDestTab+1;
+ nTab< static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsScenario();
+ nTab++ )
+ {
+ if ( maTabs[nTab]->IsActiveScenario() ) // Even if it's the same scenario
+ {
+ bool bTouched = false;
+ for ( size_t nR=0, nRangeCount = aRanges.size(); nR < nRangeCount && !bTouched; nR++ )
+ {
+ const ScRange& rRange = aRanges[ nR ];
+ if ( maTabs[nTab]->HasScenarioRange( rRange ) )
+ bTouched = true;
+ }
+ if (bTouched)
+ {
+ maTabs[nTab]->SetActiveScenario(false);
+ if ( maTabs[nTab]->GetScenarioFlags() & ScScenarioFlags::TwoWay )
+ maTabs[nTab]->CopyScenarioFrom( maTabs[nDestTab].get() );
+ }
+ }
+ }
+
+ maTabs[nSrcTab]->SetActiveScenario(true); // This is where it's from ...
+ if (!bNewScenario) // Copy data from the selected scenario
+ {
+ sc::AutoCalcSwitch aACSwitch(*this, false);
+ maTabs[nSrcTab]->CopyScenarioTo( maTabs[nDestTab].get() );
+
+ sc::SetFormulaDirtyContext aCxt;
+ SetAllFormulasDirty(aCxt);
+ }
+}
+
+void ScDocument::MarkScenario( SCTAB nSrcTab, SCTAB nDestTab, ScMarkData& rDestMark,
+ bool bResetMark, ScScenarioFlags nNeededBits ) const
+{
+ if (bResetMark)
+ rDestMark.ResetMark();
+
+ if (ValidTab(nSrcTab) && nSrcTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nSrcTab])
+ maTabs[nSrcTab]->MarkScenarioIn( rDestMark, nNeededBits );
+
+ rDestMark.SetAreaTab( nDestTab );
+}
+
+bool ScDocument::HasScenarioRange( SCTAB nTab, const ScRange& rRange ) const
+{
+ return ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->HasScenarioRange( rRange );
+}
+
+const ScRangeList* ScDocument::GetScenarioRanges( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetScenarioRanges();
+
+ return nullptr;
+}
+
+bool ScDocument::IsActiveScenario( SCTAB nTab ) const
+{
+ return ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsActiveScenario( );
+}
+
+void ScDocument::SetActiveScenario( SCTAB nTab, bool bActive )
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ maTabs[nTab]->SetActiveScenario( bActive );
+}
+
+bool ScDocument::TestCopyScenario( SCTAB nSrcTab, SCTAB nDestTab ) const
+{
+ if (ValidTab(nSrcTab) && nSrcTab < static_cast<SCTAB>(maTabs.size())
+ && nDestTab < static_cast<SCTAB>(maTabs.size())&& ValidTab(nDestTab))
+ return maTabs[nSrcTab]->TestCopyScenarioTo( maTabs[nDestTab].get() );
+
+ OSL_FAIL("wrong table at TestCopyScenario");
+ return false;
+}
+
+void ScDocument::AddUnoObject( SfxListener& rObject )
+{
+ if (!pUnoBroadcaster)
+ pUnoBroadcaster.reset( new SfxBroadcaster );
+
+ rObject.StartListening( *pUnoBroadcaster );
+}
+
+void ScDocument::RemoveUnoObject( SfxListener& rObject )
+{
+ if (pUnoBroadcaster)
+ {
+ rObject.EndListening( *pUnoBroadcaster );
+
+ if ( bInUnoBroadcast )
+ {
+ // Broadcasts from ScDocument::BroadcastUno are the only way that
+ // uno object methods are called without holding a reference.
+ //
+ // If RemoveUnoObject is called from an object dtor in the finalizer thread
+ // while the main thread is calling BroadcastUno, the dtor thread must wait
+ // (or the object's Notify might try to access a deleted object).
+ // The SolarMutex can't be locked here because if a component is called from
+ // a VCL event, the main thread has the SolarMutex locked all the time.
+ //
+ // This check is done after calling EndListening, so a later BroadcastUno call
+ // won't touch this object.
+
+ vcl::SolarMutexTryAndBuyGuard g;
+ if (g.isAcquired())
+ {
+ // BroadcastUno is always called with the SolarMutex locked, so if it
+ // can be acquired, this is within the same thread (should not happen)
+ OSL_FAIL( "RemoveUnoObject called from BroadcastUno" );
+ }
+ else
+ {
+ // Let the thread that called BroadcastUno continue
+ while ( bInUnoBroadcast )
+ {
+ osl::Thread::yield();
+ }
+ }
+ }
+ }
+ else
+ {
+ OSL_FAIL("No Uno broadcaster");
+ }
+}
+
+void ScDocument::BroadcastUno( const SfxHint &rHint )
+{
+ if (!pUnoBroadcaster)
+ return;
+
+ bInUnoBroadcast = true;
+ pUnoBroadcaster->Broadcast( rHint );
+ bInUnoBroadcast = false;
+
+ // During Broadcast notification, Uno objects can add to pUnoListenerCalls.
+ // The listener calls must be processed after completing the broadcast,
+ // because they can add or remove objects from pUnoBroadcaster.
+
+ if ( pUnoListenerCalls &&
+ rHint.GetId() == SfxHintId::DataChanged &&
+ !bInUnoListenerCall )
+ {
+ // Listener calls may lead to BroadcastUno calls again. The listener calls
+ // are not nested, instead the calls are collected in the list, and the
+ // outermost call executes them all.
+
+ ScChartLockGuard aChartLockGuard(this);
+ bInUnoListenerCall = true;
+ pUnoListenerCalls->ExecuteAndClear();
+ bInUnoListenerCall = false;
+ }
+}
+
+void ScDocument::AddUnoListenerCall( const uno::Reference<util::XModifyListener>& rListener,
+ const lang::EventObject& rEvent )
+{
+ OSL_ENSURE( bInUnoBroadcast, "AddUnoListenerCall is supposed to be called from BroadcastUno only" );
+
+ if ( !pUnoListenerCalls )
+ pUnoListenerCalls.reset( new ScUnoListenerCalls );
+ pUnoListenerCalls->Add( rListener, rEvent );
+}
+
+void ScDocument::BeginUnoRefUndo()
+{
+ OSL_ENSURE( !pUnoRefUndoList, "BeginUnoRefUndo twice" );
+ pUnoRefUndoList.reset( new ScUnoRefList );
+}
+
+std::unique_ptr<ScUnoRefList> ScDocument::EndUnoRefUndo()
+{
+ return std::move(pUnoRefUndoList);
+ // Must be deleted by caller!
+}
+
+void ScDocument::AddUnoRefChange( sal_Int64 nId, const ScRangeList& rOldRanges )
+{
+ if ( pUnoRefUndoList )
+ pUnoRefUndoList->Add( nId, rOldRanges );
+}
+
+void ScDocument::UpdateReference(
+ sc::RefUpdateContext& rCxt, ScDocument* pUndoDoc, bool bIncludeDraw, bool bUpdateNoteCaptionPos )
+{
+ if (!ValidRange(rCxt.maRange) && !(rCxt.meMode == URM_INSDEL &&
+ ((rCxt.mnColDelta < 0 && // convention from ScDocument::DeleteCol()
+ rCxt.maRange.aStart.Col() == GetMaxColCount() && rCxt.maRange.aEnd.Col() == GetMaxColCount()) ||
+ (rCxt.mnRowDelta < 0 && // convention from ScDocument::DeleteRow()
+ rCxt.maRange.aStart.Row() == GetMaxRowCount() && rCxt.maRange.aEnd.Row() == GetMaxRowCount()))))
+ return;
+
+ std::unique_ptr<sc::ExpandRefsSwitch> pExpandRefsSwitch;
+ if (rCxt.isInserted())
+ pExpandRefsSwitch.reset(new sc::ExpandRefsSwitch(*this, SC_MOD()->GetInputOptions().GetExpandRefs()));
+
+ size_t nFirstTab, nLastTab;
+ if (rCxt.meMode == URM_COPY)
+ {
+ nFirstTab = rCxt.maRange.aStart.Tab();
+ nLastTab = rCxt.maRange.aEnd.Tab();
+ }
+ else
+ {
+ // TODO: Have these methods use the context object directly.
+ ScRange aRange = rCxt.maRange;
+ UpdateRefMode eUpdateRefMode = rCxt.meMode;
+ SCCOL nDx = rCxt.mnColDelta;
+ SCROW nDy = rCxt.mnRowDelta;
+ SCTAB nDz = rCxt.mnTabDelta;
+ SCCOL nCol1 = rCxt.maRange.aStart.Col(), nCol2 = rCxt.maRange.aEnd.Col();
+ SCROW nRow1 = rCxt.maRange.aStart.Row(), nRow2 = rCxt.maRange.aEnd.Row();
+ SCTAB nTab1 = rCxt.maRange.aStart.Tab(), nTab2 = rCxt.maRange.aEnd.Tab();
+
+ xColNameRanges->UpdateReference( eUpdateRefMode, this, aRange, nDx, nDy, nDz );
+ xRowNameRanges->UpdateReference( eUpdateRefMode, this, aRange, nDx, nDy, nDz );
+ pDBCollection->UpdateReference( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz );
+ if (pRangeName)
+ pRangeName->UpdateReference(rCxt);
+ if ( pDPCollection )
+ pDPCollection->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz );
+ UpdateChartRef( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz );
+ UpdateRefAreaLinks( eUpdateRefMode, aRange, nDx, nDy, nDz );
+ if ( pValidationList )
+ {
+ ScMutationGuard aGuard(*this, ScMutationGuardFlags::CORE);
+ pValidationList->UpdateReference(rCxt);
+ }
+ if ( pDetOpList )
+ pDetOpList->UpdateReference( this, eUpdateRefMode, aRange, nDx, nDy, nDz );
+ if ( pUnoBroadcaster )
+ pUnoBroadcaster->Broadcast( ScUpdateRefHint(
+ eUpdateRefMode, aRange, nDx, nDy, nDz ) );
+
+ nFirstTab = 0;
+ nLastTab = maTabs.size()-1;
+ }
+
+ for (size_t i = nFirstTab, n = maTabs.size() ; i <= nLastTab && i < n; ++i)
+ {
+ if (!maTabs[i])
+ continue;
+
+ maTabs[i]->UpdateReference(rCxt, pUndoDoc, bIncludeDraw, bUpdateNoteCaptionPos);
+ }
+
+ if ( bIsEmbedded )
+ {
+ SCCOL theCol1;
+ SCROW theRow1;
+ SCTAB theTab1;
+ SCCOL theCol2;
+ SCROW theRow2;
+ SCTAB theTab2;
+ theCol1 = aEmbedRange.aStart.Col();
+ theRow1 = aEmbedRange.aStart.Row();
+ theTab1 = aEmbedRange.aStart.Tab();
+ theCol2 = aEmbedRange.aEnd.Col();
+ theRow2 = aEmbedRange.aEnd.Row();
+ theTab2 = aEmbedRange.aEnd.Tab();
+
+ // TODO: Have ScRefUpdate::Update() use the context object directly.
+ UpdateRefMode eUpdateRefMode = rCxt.meMode;
+ SCCOL nDx = rCxt.mnColDelta;
+ SCROW nDy = rCxt.mnRowDelta;
+ SCTAB nDz = rCxt.mnTabDelta;
+ SCCOL nCol1 = rCxt.maRange.aStart.Col(), nCol2 = rCxt.maRange.aEnd.Col();
+ SCROW nRow1 = rCxt.maRange.aStart.Row(), nRow2 = rCxt.maRange.aEnd.Row();
+ SCTAB nTab1 = rCxt.maRange.aStart.Tab(), nTab2 = rCxt.maRange.aEnd.Tab();
+
+ if ( ScRefUpdate::Update( this, eUpdateRefMode, nCol1,nRow1,nTab1, nCol2,nRow2,nTab2,
+ nDx,nDy,nDz, theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) )
+ {
+ aEmbedRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 );
+ }
+ }
+
+ // After moving, no clipboard move ref-updates are possible
+ if (rCxt.meMode != URM_COPY && IsClipboardSource())
+ {
+ ScDocument* pClipDoc = ScModule::GetClipDoc();
+ if (pClipDoc)
+ pClipDoc->GetClipParam().mbCutMode = false;
+ }
+}
+
+void ScDocument::UpdateTranspose( const ScAddress& rDestPos, ScDocument* pClipDoc,
+ const ScMarkData& rMark, ScDocument* pUndoDoc )
+{
+ OSL_ENSURE(pClipDoc->bIsClip, "UpdateTranspose: No Clip");
+
+ ScRange aSource;
+ ScClipParam& rClipParam = pClipDoc->GetClipParam();
+ if (!rClipParam.maRanges.empty())
+ aSource = rClipParam.maRanges.front();
+ ScAddress aDest = rDestPos;
+
+ SCTAB nClipTab = 0;
+ for (SCTAB nDestTab=0; nDestTab< static_cast<SCTAB>(maTabs.size()) && maTabs[nDestTab]; nDestTab++)
+ if (rMark.GetTableSelect(nDestTab))
+ {
+ while (!pClipDoc->maTabs[nClipTab]) nClipTab = (nClipTab+1) % (MAXTAB+1);
+ aSource.aStart.SetTab( nClipTab );
+ aSource.aEnd.SetTab( nClipTab );
+ aDest.SetTab( nDestTab );
+
+ // Like UpdateReference
+ if (pRangeName)
+ pRangeName->UpdateTranspose( aSource, aDest ); // Before the cells!
+ for (SCTAB i=0; i< static_cast<SCTAB>(maTabs.size()); i++)
+ if (maTabs[i])
+ maTabs[i]->UpdateTranspose( aSource, aDest, pUndoDoc );
+
+ nClipTab = (nClipTab+1) % (MAXTAB+1);
+ }
+}
+
+void ScDocument::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY )
+{
+ //TODO: pDBCollection
+ //TODO: pPivotCollection
+ //TODO: UpdateChartRef
+
+ if (pRangeName)
+ pRangeName->UpdateGrow( rArea, nGrowX, nGrowY );
+
+ for (SCTAB i=0; i< static_cast<SCTAB>(maTabs.size()) && maTabs[i]; i++)
+ maTabs[i]->UpdateGrow( rArea, nGrowX, nGrowY );
+}
+
+void ScDocument::Fill(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScProgress* pProgress, const ScMarkData& rMark,
+ sal_uInt64 nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd,
+ double nStepValue, double nMaxValue)
+{
+ PutInOrder( nCol1, nCol2 );
+ PutInOrder( nRow1, nRow2 );
+ const ScRange& aRange = rMark.GetMarkArea();
+ SCTAB nMax = maTabs.size();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ {
+ maTabs[rTab]->Fill(nCol1, nRow1, nCol2, nRow2,
+ nFillCount, eFillDir, eFillCmd, eFillDateCmd,
+ nStepValue, nMaxValue, pProgress);
+ RefreshAutoFilter(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(), aRange.aEnd.Row(), rTab);
+ }
+ }
+}
+
+OUString ScDocument::GetAutoFillPreview( const ScRange& rSource, SCCOL nEndX, SCROW nEndY )
+{
+ SCTAB nTab = rSource.aStart.Tab();
+ if (nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetAutoFillPreview( rSource, nEndX, nEndY );
+
+ return OUString();
+}
+
+void ScDocument::AutoFormat( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ sal_uInt16 nFormatNo, const ScMarkData& rMark )
+{
+ PutInOrder( nStartCol, nEndCol );
+ PutInOrder( nStartRow, nEndRow );
+ SCTAB nMax = maTabs.size();
+ for (const auto& rTab : rMark)
+ {
+ if (rTab >= nMax)
+ break;
+ if (maTabs[rTab])
+ maTabs[rTab]->AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo );
+ }
+}
+
+void ScDocument::GetAutoFormatData(SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ ScAutoFormatData& rData)
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()))
+ {
+ if (maTabs[nTab])
+ {
+ PutInOrder(nStartCol, nEndCol);
+ PutInOrder(nStartRow, nEndRow);
+ maTabs[nTab]->GetAutoFormatData(nStartCol, nStartRow, nEndCol, nEndRow, rData);
+ }
+ }
+}
+
+void ScDocument::GetSearchAndReplaceStart( const SvxSearchItem& rSearchItem,
+ SCCOL& rCol, SCROW& rRow )
+{
+ SvxSearchCmd nCommand = rSearchItem.GetCommand();
+ bool bReplace = ( nCommand == SvxSearchCmd::REPLACE ||
+ nCommand == SvxSearchCmd::REPLACE_ALL );
+ if ( rSearchItem.GetBackward() )
+ {
+ if ( rSearchItem.GetRowDirection() )
+ {
+ if ( rSearchItem.GetPattern() )
+ {
+ rCol = MaxCol();
+ rRow = MaxRow()+1;
+ }
+ else if ( bReplace )
+ {
+ rCol = MaxCol();
+ rRow = MaxRow();
+ }
+ else
+ {
+ rCol = MaxCol()+1;
+ rRow = MaxRow();
+ }
+ }
+ else
+ {
+ if ( rSearchItem.GetPattern() )
+ {
+ rCol = MaxCol()+1;
+ rRow = MaxRow();
+ }
+ else if ( bReplace )
+ {
+ rCol = MaxCol();
+ rRow = MaxRow();
+ }
+ else
+ {
+ rCol = MaxCol();
+ rRow = MaxRow()+1;
+ }
+ }
+ }
+ else
+ {
+ if ( rSearchItem.GetRowDirection() )
+ {
+ if ( rSearchItem.GetPattern() )
+ {
+ rCol = 0;
+ rRow = SCROW(-1);
+ }
+ else if ( bReplace )
+ {
+ rCol = 0;
+ rRow = 0;
+ }
+ else
+ {
+ rCol = SCCOL(-1);
+ rRow = 0;
+ }
+ }
+ else
+ {
+ if ( rSearchItem.GetPattern() )
+ {
+ rCol = SCCOL(-1);
+ rRow = 0;
+ }
+ else if ( bReplace )
+ {
+ rCol = 0;
+ rRow = 0;
+ }
+ else
+ {
+ rCol = 0;
+ rRow = SCROW(-1);
+ }
+ }
+ }
+}
+
+// static
+bool ScDocument::IsEmptyCellSearch( const SvxSearchItem& rSearchItem )
+{
+ return !rSearchItem.GetPattern() && (rSearchItem.GetCellType() != SvxSearchCellType::NOTE)
+ && (rSearchItem.GetSearchOptions().searchString.isEmpty()
+ || (rSearchItem.GetRegExp() && rSearchItem.GetSearchOptions().searchString == "^$"));
+}
+
+bool ScDocument::SearchAndReplace(
+ const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, SCTAB& rTab,
+ const ScMarkData& rMark, ScRangeList& rMatchedRanges,
+ OUString& rUndoStr, ScDocument* pUndoDoc)
+{
+ // FIXME: Manage separated marks per table!
+ bool bFound = false;
+ if (rTab >= static_cast<SCTAB>(maTabs.size()))
+ OSL_FAIL("table out of range");
+ if (ValidTab(rTab))
+ {
+ SCCOL nCol;
+ SCROW nRow;
+ SCTAB nTab;
+ SvxSearchCmd nCommand = rSearchItem.GetCommand();
+ if ( nCommand == SvxSearchCmd::FIND_ALL ||
+ nCommand == SvxSearchCmd::REPLACE_ALL )
+ {
+ SCTAB nMax = maTabs.size();
+ for (const auto& rMarkedTab : rMark)
+ {
+ if (rMarkedTab >= nMax)
+ break;
+ if (maTabs[rMarkedTab])
+ {
+ nCol = 0;
+ nRow = 0;
+ bFound |= maTabs[rMarkedTab]->SearchAndReplace(
+ rSearchItem, nCol, nRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
+ }
+ }
+
+ // Mark is set completely inside already
+ }
+ else
+ {
+ nCol = rCol;
+ nRow = rRow;
+ if (rSearchItem.GetBackward())
+ {
+ for (nTab = rTab; (nTab >= 0) && !bFound; nTab--)
+ if (maTabs[nTab])
+ {
+ if (rMark.GetTableSelect(nTab))
+ {
+ bFound = maTabs[nTab]->SearchAndReplace(
+ rSearchItem, nCol, nRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ rTab = nTab;
+ }
+ else
+ {
+ ScDocument::GetSearchAndReplaceStart(
+ rSearchItem, nCol, nRow );
+
+ // notify LibreOfficeKit about changed page
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OString aPayload = OString::number(nTab);
+ if (SfxViewShell* pViewShell = SfxViewShell::Current())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr());
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for (nTab = rTab; (nTab < static_cast<SCTAB>(maTabs.size())) && !bFound; nTab++)
+ if (maTabs[nTab])
+ {
+ if (rMark.GetTableSelect(nTab))
+ {
+ bFound = maTabs[nTab]->SearchAndReplace(
+ rSearchItem, nCol, nRow, rMark, rMatchedRanges, rUndoStr, pUndoDoc);
+ if (bFound)
+ {
+ rCol = nCol;
+ rRow = nRow;
+ rTab = nTab;
+ }
+ else
+ {
+ ScDocument::GetSearchAndReplaceStart(
+ rSearchItem, nCol, nRow );
+
+ // notify LibreOfficeKit about changed page
+ if (comphelper::LibreOfficeKit::isActive())
+ {
+ OString aPayload = OString::number(nTab);
+ if(SfxViewShell* pViewShell = SfxViewShell::Current())
+ pViewShell->libreOfficeKitViewCallback(LOK_CALLBACK_SET_PART, aPayload.getStr());
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ return bFound;
+}
+
+/**
+ * Adapt Outline
+ */
+bool ScDocument::UpdateOutlineCol( SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab, bool bShow )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->UpdateOutlineCol( nStartCol, nEndCol, bShow );
+
+ OSL_FAIL("missing tab");
+ return false;
+}
+
+bool ScDocument::UpdateOutlineRow( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bShow )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->UpdateOutlineRow( nStartRow, nEndRow, bShow );
+
+ OSL_FAIL("missing tab");
+ return false;
+}
+
+void ScDocument::Sort(
+ SCTAB nTab, const ScSortParam& rSortParam, bool bKeepQuery, bool bUpdateRefs,
+ ScProgress* pProgress, sc::ReorderParam* pUndo )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ {
+ bool bOldEnableIdle = IsIdleEnabled();
+ EnableIdle(false);
+ maTabs[nTab]->Sort(rSortParam, bKeepQuery, bUpdateRefs, pProgress, pUndo);
+ EnableIdle(bOldEnableIdle);
+ }
+}
+
+void ScDocument::Reorder( const sc::ReorderParam& rParam )
+{
+ ScTable* pTab = FetchTable(rParam.maSortRange.aStart.Tab());
+ if (!pTab)
+ return;
+
+ bool bOldEnableIdle = IsIdleEnabled();
+ EnableIdle(false);
+ pTab->Reorder(rParam);
+ EnableIdle(bOldEnableIdle);
+}
+
+void ScDocument::PrepareQuery( SCTAB nTab, ScQueryParam& rQueryParam )
+{
+ if( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->PrepareQuery(rQueryParam);
+ else
+ {
+ OSL_FAIL("missing tab");
+ return;
+ }
+}
+
+SCSIZE ScDocument::Query(SCTAB nTab, const ScQueryParam& rQueryParam, bool bKeepSub)
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->Query(rQueryParam, bKeepSub);
+
+ OSL_FAIL("missing tab");
+ return 0;
+}
+
+OUString ScDocument::GetUpperCellString(SCCOL nCol, SCROW nRow, SCTAB nTab)
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetUpperCellString( nCol, nRow );
+ else
+ return OUString();
+}
+
+bool ScDocument::CreateQueryParam( const ScRange& rRange, ScQueryParam& rQueryParam )
+{
+ ScTable* pTab = FetchTable(rRange.aStart.Tab());
+ if (!pTab)
+ {
+ OSL_FAIL("missing tab");
+ return false;
+ }
+
+ return pTab->CreateQueryParam(
+ rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row(), rQueryParam);
+}
+
+bool ScDocument::HasAutoFilter( SCCOL nCurCol, SCROW nCurRow, SCTAB nCurTab )
+{
+ const ScDBData* pDBData = GetDBAtCursor( nCurCol, nCurRow, nCurTab, ScDBDataPortion::AREA );
+ bool bHasAutoFilter = (pDBData != nullptr);
+
+ if ( pDBData )
+ {
+ if ( pDBData->HasHeader() )
+ {
+ SCCOL nCol;
+ SCROW nRow;
+ ScMF nFlag;
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam( aParam );
+ nRow = aParam.nRow1;
+
+ for ( nCol=aParam.nCol1; nCol<=aParam.nCol2 && bHasAutoFilter; nCol++ )
+ {
+ nFlag = GetAttr( nCol, nRow, nCurTab, ATTR_MERGE_FLAG )->GetValue();
+
+ if ( !(nFlag & ScMF::Auto) )
+ bHasAutoFilter = false;
+ }
+ }
+ else
+ bHasAutoFilter = false;
+ }
+
+ return bHasAutoFilter;
+}
+
+bool ScDocument::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ SCTAB nTab )
+{
+ return ValidTab(nTab) && maTabs[nTab] && maTabs[nTab]->HasColHeader( nStartCol, nStartRow, nEndCol, nEndRow );
+}
+
+bool ScDocument::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow,
+ SCTAB nTab )
+{
+ return ValidTab(nTab) && maTabs[nTab] && maTabs[nTab]->HasRowHeader( nStartCol, nStartRow, nEndCol, nEndRow );
+}
+
+void ScDocument::GetFilterSelCount( SCCOL nCol, SCROW nRow, SCTAB nTab, SCSIZE& nSelected, SCSIZE& nTotal )
+{
+ nSelected = 0;
+ nTotal = 0;
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ {
+ ScDBData* pDBData = GetDBAtCursor( nCol, nRow, nTab, ScDBDataPortion::AREA );
+ if( pDBData && pDBData->HasAutoFilter() )
+ pDBData->GetFilterSelCount( nSelected, nTotal );
+ }
+}
+
+/**
+ * Entries for AutoFilter listbox
+ */
+void ScDocument::GetFilterEntries(
+ SCCOL nCol, SCROW nRow, SCTAB nTab, ScFilterEntries& rFilterEntries )
+{
+ if ( !(ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] && pDBCollection) )
+ return;
+
+ ScDBData* pDBData = pDBCollection->GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA); //!??
+ if (!pDBData)
+ return;
+
+ pDBData->ExtendDataArea(*this);
+ SCTAB nAreaTab;
+ SCCOL nStartCol;
+ SCROW nStartRow;
+ SCCOL nEndCol;
+ SCROW nEndRow;
+ pDBData->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow );
+
+ if (pDBData->HasHeader())
+ ++nStartRow;
+
+ ScQueryParam aParam;
+ pDBData->GetQueryParam( aParam );
+
+ // Return all filter entries, if a filter condition is connected with a boolean OR
+ bool bFilter = true;
+ SCSIZE nEntryCount = aParam.GetEntryCount();
+ for ( SCSIZE i = 0; i < nEntryCount && aParam.GetEntry(i).bDoQuery; ++i )
+ {
+ ScQueryEntry& rEntry = aParam.GetEntry(i);
+ if ( rEntry.eConnect != SC_AND )
+ {
+ bFilter = false;
+ break;
+ }
+ }
+
+ if ( bFilter )
+ {
+ maTabs[nTab]->GetFilteredFilterEntries( nCol, nStartRow, nEndRow, aParam, rFilterEntries, bFilter );
+ }
+ else
+ {
+ maTabs[nTab]->GetFilterEntries( nCol, nStartRow, nEndRow, rFilterEntries );
+ }
+
+ sortAndRemoveDuplicates( rFilterEntries.maStrData, aParam.bCaseSens);
+}
+
+/**
+ * Entries for Filter dialog
+ */
+void ScDocument::GetFilterEntriesArea(
+ SCCOL nCol, SCROW nStartRow, SCROW nEndRow, SCTAB nTab, bool bCaseSens,
+ ScFilterEntries& rFilterEntries )
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ {
+ maTabs[nTab]->GetFilterEntries( nCol, nStartRow, nEndRow, rFilterEntries, true );
+ sortAndRemoveDuplicates( rFilterEntries.maStrData, bCaseSens);
+ }
+}
+
+/**
+ * Entries for selection list listbox (no numbers/formulas)
+ */
+void ScDocument::GetDataEntries(
+ SCCOL nCol, SCROW nRow, SCTAB nTab,
+ std::vector<ScTypedStrData>& rStrings, bool bValidation )
+{
+ if( bValidation )
+ {
+ /* Try to generate the list from list validation. This part is skipped,
+ if bValidation==false, because in that case this function is called to get
+ cell values for auto completion on input. */
+ sal_uInt32 nValidation = GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA )->GetValue();
+ if( nValidation )
+ {
+ const ScValidationData* pData = GetValidationEntry( nValidation );
+ if( pData && pData->FillSelectionList( rStrings, ScAddress( nCol, nRow, nTab ) ) )
+ {
+ if (pData->GetListType() == css::sheet::TableValidationVisibility::SORTEDASCENDING)
+ sortAndRemoveDuplicates(rStrings, true/*bCaseSens*/);
+
+ return;
+ }
+ }
+ }
+
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()))
+ return;
+
+ if (!maTabs[nTab])
+ return;
+
+ std::set<ScTypedStrData> aStrings;
+ if (maTabs[nTab]->GetDataEntries(nCol, nRow, aStrings))
+ {
+ rStrings.insert(rStrings.end(), aStrings.begin(), aStrings.end());
+ sortAndRemoveDuplicates(rStrings, true/*bCaseSens*/);
+ }
+}
+
+/**
+ * Entries for Formula auto input
+ */
+void ScDocument::GetFormulaEntries( ScTypedCaseStrSet& rStrings )
+{
+
+ // Range name
+ if ( pRangeName )
+ {
+ for (const auto& rEntry : *pRangeName)
+ rStrings.insert(ScTypedStrData(rEntry.second->GetName(), 0.0, 0.0, ScTypedStrData::Name));
+ }
+
+ // Database collection
+ if ( pDBCollection )
+ {
+ const ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs();
+ for (const auto& rxDB : rDBs)
+ rStrings.insert(ScTypedStrData(rxDB->GetName(), 0.0, 0.0, ScTypedStrData::DbName));
+ }
+
+ // Content of name ranges
+ ScRangePairList* pLists[2];
+ pLists[0] = GetColNameRanges();
+ pLists[1] = GetRowNameRanges();
+ for (ScRangePairList* pList : pLists)
+ {
+ if (!pList)
+ continue;
+
+ for ( size_t i = 0, nPairs = pList->size(); i < nPairs; ++i )
+ {
+ const ScRangePair & rPair = (*pList)[i];
+ const ScRange & rRange = rPair.GetRange(0);
+ ScCellIterator aIter( *this, rRange );
+ for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
+ {
+ if (!aIter.hasString())
+ continue;
+
+ OUString aStr = aIter.getString();
+ rStrings.insert(ScTypedStrData(aStr, 0.0, 0.0, ScTypedStrData::Header));
+ }
+ }
+ }
+}
+
+void ScDocument::GetEmbedded( ScRange& rRange ) const
+{
+ rRange = aEmbedRange;
+}
+
+tools::Rectangle ScDocument::GetEmbeddedRect() const // 1/100 mm
+{
+ tools::Rectangle aRect;
+ ScTable* pTable = nullptr;
+ if ( aEmbedRange.aStart.Tab() < static_cast<SCTAB>(maTabs.size()) )
+ pTable = maTabs[aEmbedRange.aStart.Tab()].get();
+ else
+ OSL_FAIL("table out of range");
+ if (!pTable)
+ {
+ OSL_FAIL("GetEmbeddedRect without a table");
+ }
+ else
+ {
+ SCCOL i;
+
+ for (i=0; i<aEmbedRange.aStart.Col(); i++)
+ aRect.AdjustLeft(pTable->GetColWidth(i) );
+ aRect.AdjustTop(pTable->GetRowHeight( 0, aEmbedRange.aStart.Row() - 1) );
+ aRect.SetRight( aRect.Left() );
+ for (i=aEmbedRange.aStart.Col(); i<=aEmbedRange.aEnd.Col(); i++)
+ aRect.AdjustRight(pTable->GetColWidth(i) );
+ aRect.SetBottom( aRect.Top() );
+ aRect.AdjustBottom(pTable->GetRowHeight( aEmbedRange.aStart.Row(), aEmbedRange.aEnd.Row()) );
+
+ aRect = o3tl::convert(aRect, o3tl::Length::twip, o3tl::Length::mm100);
+ }
+ return aRect;
+}
+
+void ScDocument::SetEmbedded( const ScRange& rRange )
+{
+ bIsEmbedded = true;
+ aEmbedRange = rRange;
+}
+
+void ScDocument::ResetEmbedded()
+{
+ bIsEmbedded = false;
+ aEmbedRange = ScRange();
+}
+
+/** Similar to ScViewData::AddPixelsWhile(), but add height twips and only
+ while result is less than nStopTwips.
+ @return true if advanced at least one row.
+ */
+static bool lcl_AddTwipsWhile( tools::Long & rTwips, tools::Long nStopTwips, SCROW & rPosY, SCROW nEndRow, const ScTable * pTable, bool bHiddenAsZero )
+{
+ SCROW nRow = rPosY;
+ bool bAdded = false;
+ bool bStop = false;
+ while (rTwips < nStopTwips && nRow <= nEndRow && !bStop)
+ {
+ SCROW nHeightEndRow;
+ sal_uInt16 nHeight = pTable->GetRowHeight( nRow, nullptr, &nHeightEndRow, bHiddenAsZero );
+ if (nHeightEndRow > nEndRow)
+ nHeightEndRow = nEndRow;
+ if (!nHeight)
+ nRow = nHeightEndRow + 1;
+ else
+ {
+ SCROW nRows = nHeightEndRow - nRow + 1;
+ sal_Int64 nAdd = static_cast<sal_Int64>(nHeight) * nRows;
+ if (nAdd + rTwips >= nStopTwips)
+ {
+ sal_Int64 nDiff = nAdd + rTwips - nStopTwips;
+ nRows -= static_cast<SCROW>(nDiff / nHeight);
+ nAdd = static_cast<sal_Int64>(nHeight) * nRows;
+ // We're looking for a value that satisfies loop condition.
+ if (nAdd + rTwips >= nStopTwips)
+ {
+ --nRows;
+ nAdd -= nHeight;
+ }
+ bStop = true;
+ }
+ rTwips += static_cast<tools::Long>(nAdd);
+ nRow += nRows;
+ }
+ }
+ if (nRow > rPosY)
+ {
+ --nRow;
+ bAdded = true;
+ }
+ rPosY = nRow;
+ return bAdded;
+}
+
+ScRange ScDocument::GetRange( SCTAB nTab, const tools::Rectangle& rMMRect, bool bHiddenAsZero ) const
+{
+ ScTable* pTable = nullptr;
+ if (nTab < static_cast<SCTAB>(maTabs.size()))
+ pTable = maTabs[nTab].get();
+ else
+ OSL_FAIL("table out of range");
+ if (!pTable)
+ {
+ OSL_FAIL("GetRange without a table");
+ return ScRange();
+ }
+
+ tools::Rectangle aPosRect = o3tl::convert(rMMRect, o3tl::Length::mm100, o3tl::Length::twip);
+ if ( IsNegativePage( nTab ) )
+ ScDrawLayer::MirrorRectRTL( aPosRect ); // Always with positive (LTR) values
+
+ tools::Long nSize;
+ tools::Long nTwips;
+ tools::Long nAdd;
+ bool bEnd;
+
+ nSize = 0;
+ nTwips = aPosRect.Left();
+
+ SCCOL nX1 = 0;
+ bEnd = false;
+ while (!bEnd)
+ {
+ nAdd = pTable->GetColWidth(nX1, bHiddenAsZero);
+ if (nSize+nAdd <= nTwips+1 && nX1<MaxCol())
+ {
+ nSize += nAdd;
+ ++nX1;
+ }
+ else
+ bEnd = true;
+ }
+
+
+ SCCOL nX2 = nX1;
+ if (!aPosRect.IsEmpty())
+ {
+ bEnd = false;
+ nTwips = aPosRect.Right();
+ while (!bEnd)
+ {
+ nAdd = pTable->GetColWidth(nX2, bHiddenAsZero);
+ if (nSize+nAdd < nTwips && nX2<MaxCol())
+ {
+ nSize += nAdd;
+ ++nX2;
+ }
+ else
+ bEnd = true;
+ }
+ }
+
+ nSize = 0;
+ nTwips = aPosRect.Top();
+
+ SCROW nY1 = 0;
+ // Was if(nSize+nAdd<=nTwips+1) inside loop => if(nSize+nAdd<nTwips+2)
+ if (lcl_AddTwipsWhile( nSize, nTwips+2, nY1, MaxRow(), pTable, bHiddenAsZero) && nY1 < MaxRow())
+ ++nY1; // original loop ended on last matched +1 unless that was rDoc.MaxRow()
+
+ SCROW nY2 = nY1;
+ if (!aPosRect.IsEmpty())
+ {
+ nTwips = aPosRect.Bottom();
+ // Was if(nSize+nAdd<nTwips) inside loop => if(nSize+nAdd<nTwips)
+ if (lcl_AddTwipsWhile( nSize, nTwips, nY2, MaxRow(), pTable, bHiddenAsZero) && nY2 < MaxRow())
+ ++nY2; // original loop ended on last matched +1 unless that was rDoc.MaxRow()
+ }
+
+ return ScRange( nX1,nY1,nTab, nX2,nY2,nTab );
+}
+
+void ScDocument::SetEmbedded( SCTAB nTab, const tools::Rectangle& rRect ) // From VisArea (1/100 mm)
+{
+ bIsEmbedded = true;
+ aEmbedRange = GetRange( nTab, rRect );
+}
+
+ScDocProtection* ScDocument::GetDocProtection() const
+{
+ return pDocProtection.get();
+}
+
+void ScDocument::SetDocProtection(const ScDocProtection* pProtect)
+{
+ if (pProtect)
+ pDocProtection.reset(new ScDocProtection(*pProtect));
+ else
+ pDocProtection.reset();
+}
+
+bool ScDocument::IsDocProtected() const
+{
+ return pDocProtection && pDocProtection->isProtected();
+}
+
+bool ScDocument::IsDocEditable() const
+{
+ // Import into read-only document is possible
+ return !IsDocProtected() && ( bImportingXML || mbChangeReadOnlyEnabled || !mpShell || !mpShell->IsReadOnly() );
+}
+
+bool ScDocument::IsTabProtected( SCTAB nTab ) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->IsProtected();
+
+ OSL_FAIL("Wrong table number");
+ return false;
+}
+
+const ScTableProtection* ScDocument::GetTabProtection(SCTAB nTab) const
+{
+ if (ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab])
+ return maTabs[nTab]->GetProtection();
+
+ return nullptr;
+}
+
+void ScDocument::SetTabProtection(SCTAB nTab, const ScTableProtection* pProtect)
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()))
+ return;
+
+ maTabs[nTab]->SetProtection(pProtect);
+}
+
+void ScDocument::CopyTabProtection(SCTAB nTabSrc, SCTAB nTabDest)
+{
+ if (!ValidTab(nTabSrc) || nTabSrc >= static_cast<SCTAB>(maTabs.size()) || nTabDest >= static_cast<SCTAB>(maTabs.size()) || !ValidTab(nTabDest))
+ return;
+
+ maTabs[nTabDest]->SetProtection( maTabs[nTabSrc]->GetProtection() );
+}
+
+const ScDocOptions& ScDocument::GetDocOptions() const
+{
+ assert(pDocOptions && "No DocOptions! :-(");
+ return *pDocOptions;
+}
+
+void ScDocument::SetDocOptions( const ScDocOptions& rOpt )
+{
+ assert(pDocOptions && "No DocOptions! :-(");
+
+ *pDocOptions = rOpt;
+ if (mxPoolHelper)
+ mxPoolHelper->SetFormTableOpt(rOpt);
+}
+
+const ScViewOptions& ScDocument::GetViewOptions() const
+{
+ assert(pViewOptions && "No ViewOptions! :-(");
+ return *pViewOptions;
+}
+
+void ScDocument::SetViewOptions( const ScViewOptions& rOpt )
+{
+ assert(pViewOptions && "No ViewOptions! :-(");
+ *pViewOptions = rOpt;
+}
+
+void ScDocument::GetLanguage( LanguageType& rLatin, LanguageType& rCjk, LanguageType& rCtl ) const
+{
+ rLatin = eLanguage;
+ rCjk = eCjkLanguage;
+ rCtl = eCtlLanguage;
+}
+
+void ScDocument::SetLanguage( LanguageType eLatin, LanguageType eCjk, LanguageType eCtl )
+{
+ eLanguage = eLatin;
+ eCjkLanguage = eCjk;
+ eCtlLanguage = eCtl;
+ if ( mxPoolHelper.is() )
+ {
+ ScDocumentPool* pPool = mxPoolHelper->GetDocPool();
+ pPool->SetPoolDefaultItem( SvxLanguageItem( eLanguage, ATTR_FONT_LANGUAGE ) );
+ pPool->SetPoolDefaultItem( SvxLanguageItem( eCjkLanguage, ATTR_CJK_FONT_LANGUAGE ) );
+ pPool->SetPoolDefaultItem( SvxLanguageItem( eCtlLanguage, ATTR_CTL_FONT_LANGUAGE ) );
+ }
+
+ UpdateDrawLanguages(); // Set edit engine defaults in drawing layer pool
+}
+
+tools::Rectangle ScDocument::GetMMRect( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, bool bHiddenAsZero ) const
+{
+ if (!ValidTab(nTab) || nTab >= static_cast<SCTAB>(maTabs.size()) || !maTabs[nTab])
+ {
+ OSL_FAIL("GetMMRect: wrong table");
+ return tools::Rectangle(0,0,0,0);
+ }
+
+ SCCOL i;
+ tools::Rectangle aRect;
+
+ for (i=0; i<nStartCol; i++)
+ aRect.AdjustLeft(GetColWidth(i,nTab, bHiddenAsZero ) );
+ aRect.AdjustTop(GetRowHeight( 0, nStartRow-1, nTab, bHiddenAsZero ) );
+
+ aRect.SetRight( aRect.Left() );
+ aRect.SetBottom( aRect.Top() );
+
+ for (i=nStartCol; i<=nEndCol; i++)
+ aRect.AdjustRight(GetColWidth(i,nTab, bHiddenAsZero) );
+ aRect.AdjustBottom(GetRowHeight( nStartRow, nEndRow, nTab, bHiddenAsZero ) );
+
+ aRect = o3tl::convert(aRect, o3tl::Length::twip, o3tl::Length::mm100);
+
+ if ( IsNegativePage( nTab ) )
+ ScDrawLayer::MirrorRectRTL( aRect );
+
+ return aRect;
+}
+
+void ScDocument::SetExtDocOptions( std::unique_ptr<ScExtDocOptions> pNewOptions )
+{
+ pExtDocOptions = std::move(pNewOptions);
+}
+
+void ScDocument::SetClipOptions(std::unique_ptr<ScClipOptions> pClipOptions)
+{
+ mpClipOptions = std::move(pClipOptions);
+}
+
+void ScDocument::DoMergeContents( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
+{
+ OUStringBuffer aTotal;
+ OUString aCellStr;
+ SCCOL nCol;
+ SCROW nRow;
+ for (nRow=nStartRow; nRow<=nEndRow; nRow++)
+ for (nCol=nStartCol; nCol<=nEndCol; nCol++)
+ {
+ aCellStr = GetString(nCol, nRow, nTab);
+ if (!aCellStr.isEmpty())
+ {
+ if (!aTotal.isEmpty())
+ aTotal.append(' ');
+ aTotal.append(aCellStr);
+ }
+ if (nCol != nStartCol || nRow != nStartRow)
+ SetString(nCol,nRow,nTab,"");
+ }
+
+ SetString(nStartCol,nStartRow,nTab,aTotal.makeStringAndClear());
+}
+
+void ScDocument::DoEmptyBlock( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab )
+{
+ SCCOL nCol;
+ SCROW nRow;
+ for (nRow=nStartRow; nRow<=nEndRow; nRow++)
+ for (nCol=nStartCol; nCol<=nEndCol; nCol++)
+ { // empty block except first cell
+ if (nCol != nStartCol || nRow != nStartRow)
+ SetString(nCol,nRow,nTab,"");
+ }
+}
+
+void ScDocument::DoMerge( SCCOL nStartCol, SCROW nStartRow,
+ SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, bool bDeleteCaptions )
+{
+ ScTable* pTab = FetchTable(nTab);
+ if (!pTab)
+ return;
+
+ pTab->SetMergedCells(nStartCol, nStartRow, nEndCol, nEndRow);
+
+ // Remove all covered notes (removed captions are collected by drawing undo if active)
+ InsertDeleteFlags nDelFlag = InsertDeleteFlags::NOTE | (bDeleteCaptions ? InsertDeleteFlags::NONE : InsertDeleteFlags::NOCAPTIONS);
+ if( nStartCol < nEndCol )
+ DeleteAreaTab( nStartCol + 1, nStartRow, nEndCol, nStartRow, nTab, nDelFlag );
+ if( nStartRow < nEndRow )
+ DeleteAreaTab( nStartCol, nStartRow + 1, nEndCol, nEndRow, nTab, nDelFlag );
+}
+
+void ScDocument::RemoveMerge( SCCOL nCol, SCROW nRow, SCTAB nTab )
+{
+ const ScMergeAttr* pAttr = GetAttr( nCol, nRow, nTab, ATTR_MERGE );
+
+ if ( pAttr->GetColMerge() <= 1 && pAttr->GetRowMerge() <= 1 )
+ return;
+
+ SCCOL nEndCol = nCol + pAttr->GetColMerge() - 1;
+ SCROW nEndRow = nRow + pAttr->GetRowMerge() - 1;
+
+ RemoveFlagsTab( nCol, nRow, nEndCol, nEndRow, nTab, ScMF::Hor | ScMF::Ver );
+
+ const ScMergeAttr* pDefAttr = &mxPoolHelper->GetDocPool()->GetDefaultItem( ATTR_MERGE );
+ ApplyAttr( nCol, nRow, nTab, *pDefAttr );
+}
+
+void ScDocument::ExtendPrintArea( OutputDevice* pDev, SCTAB nTab,
+ SCCOL nStartCol, SCROW nStartRow, SCCOL& rEndCol, SCROW nEndRow ) const
+{
+ if ( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->ExtendPrintArea( pDev, nStartCol, nStartRow, rEndCol, nEndRow );
+}
+
+SCSIZE ScDocument::GetPatternCount( SCTAB nTab, SCCOL nCol ) const
+{
+ if( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetPatternCount( nCol );
+ else
+ return 0;
+}
+
+SCSIZE ScDocument::GetPatternCount( SCTAB nTab, SCCOL nCol, SCROW nRow1, SCROW nRow2 ) const
+{
+ if( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ return maTabs[nTab]->GetPatternCount( nCol, nRow1, nRow2 );
+ else
+ return 0;
+}
+
+void ScDocument::ReservePatternCount( SCTAB nTab, SCCOL nCol, SCSIZE nReserve )
+{
+ if( ValidTab(nTab) && nTab < static_cast<SCTAB>(maTabs.size()) && maTabs[nTab] )
+ maTabs[nTab]->ReservePatternCount( nCol, nReserve );
+}
+
+void ScDocument::GetSortParam( ScSortParam& rParam, SCTAB nTab )
+{
+ rParam = mSheetSortParams[ nTab ];
+}
+
+void ScDocument::SetSortParam( const ScSortParam& rParam, SCTAB nTab )
+{
+ mSheetSortParams[ nTab ] = rParam;
+}
+
+SCCOL ScDocument::ClampToAllocatedColumns(SCTAB nTab, SCCOL nCol) const
+{
+ return maTabs[nTab]->ClampToAllocatedColumns(nCol);
+}
+
+SCCOL ScDocument::GetAllocatedColumnsCount(SCTAB nTab) const
+{
+ return maTabs[nTab]->GetAllocatedColumnsCount();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */