/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace com::sun::star; namespace { void sortAndRemoveDuplicates(std::vector& rStrings, bool bCaseSens) { if (bCaseSens) { std::sort(rStrings.begin(), rStrings.end(), ScTypedStrData::LessCaseSensitive()); std::vector::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::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(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>& rRangeMap) { auto itr = rRangeMap.begin(), itrEnd = rRangeMap.end(); for (; itr!=itrEnd; ++itr) { if (itr->first == STR_GLOBAL_RANGE_NAME) { pRangeName.reset(); const ScRangeName *const pName = itr->second.get(); if (!pName->empty()) pRangeName.reset( new ScRangeName( *pName ) ); } else { const ScRangeName *const pName = itr->second.get(); SCTAB nTab; bool bFound = GetTable(itr->first, nTab); assert(bFound); (void)bFound; // fouled up? if (pName->empty()) SetRangeName( nTab, nullptr ); else SetRangeName( nTab, std::unique_ptr(new ScRangeName( *pName )) ); } } } void ScDocument::GetRangeNameMap(std::map& aRangeNameMap) { for (SCTAB i = 0; i < static_cast(maTabs.size()); ++i) { if (!maTabs[i]) continue; ScRangeName* p = maTabs[i]->GetRangeName(); if (!p ) { p = new ScRangeName(); SetRangeName(i, std::unique_ptr(p)); } OUString aTableName; maTabs[i]->GetName(aTableName); aRangeNameMap.insert(std::pair(aTableName,p)); } if (!pRangeName) { pRangeName.reset(new ScRangeName()); } OUString aGlobal(STR_GLOBAL_RANGE_NAME); aRangeNameMap.insert(std::pair(aGlobal, pRangeName.get())); } ScRangeName* ScDocument::GetRangeName(SCTAB nTab) const { if (!ValidTab(nTab) || nTab >= static_cast(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 pNew) { if (!ValidTab(nTab) || nTab >= static_cast(maTabs.size()) || !maTabs[nTab]) return; return maTabs[nTab]->SetRangeName(std::move(pNew)); } void ScDocument::SetRangeName( std::unique_ptr 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()); ScRangeName::iterator itrBegin = pRangeNames->begin(), itrEnd = pRangeNames->end(); for (ScRangeName::iterator itr = itrBegin; itr != itrEnd; ++itr) { if (itr->second->IsValidReference(aNameRange)) { if (aNameRange.In(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* pName ) const { const ScRangeData* pData = nullptr; if ( pRangeName ) { pData = pRangeName->findByRange( rBlock ); if (pData && pName) *pName = pData->GetName(); } 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 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(); ScDBCollection::NamedDBs::const_iterator itr = rNamedDBs.begin(), itrEnd = rNamedDBs.end(); for (; itr != itrEnd; ++itr) { const ScDBData& rOldData = **itr; 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; iGetCount(); while ( i-- > 0 ) if ( (*pDPCollection)[i].GetOutRange().In( rBlock ) ) return &(*pDPCollection)[i]; return nullptr; } void ScDocument::StopTemporaryChartLock() { if( apTemporaryChartLock.get() ) apTemporaryChartLock->StopLocking(); } void ScDocument::SetChartListenerCollection( ScChartListenerCollection* pNewChartListenerCollection, bool bSetChartRangeLists ) { ScChartListenerCollection* pOld = pChartListenerCollection; pChartListenerCollection = pNewChartListenerCollection; if ( pChartListenerCollection ) { if ( pOld ) pChartListenerCollection->SetDiffDirty( *pOld, bSetChartRangeLists ); pChartListenerCollection->StartAllListeners(); } delete pOld; } void ScDocument::SetScenario( SCTAB nTab, bool bFlag ) { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) maTabs[nTab]->SetScenario(bFlag); } bool ScDocument::IsScenario( SCTAB nTab ) const { return ValidTab(nTab) && nTab < static_cast(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(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(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(maTabs.size()) && maTabs[nTab]) maTabs[nTab]->SetTabBgColor(rColor); } bool ScDocument::IsDefaultTabBgColor( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(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(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(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsScenario()) rFlags = maTabs[nTab]->GetScenarioFlags(); } bool ScDocument::IsLinked( SCTAB nTab ) const { return ValidTab(nTab) && nTab < static_cast(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(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->GetLinkMode(); return ScLinkMode::NONE; } const OUString ScDocument::GetLinkDoc( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->GetLinkDoc(); return OUString(); } const OUString ScDocument::GetLinkFlt( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->GetLinkFlt(); return OUString(); } const OUString ScDocument::GetLinkOpt( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->GetLinkOpt(); return OUString(); } const OUString ScDocument::GetLinkTab( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->GetLinkTab(); return OUString(); } sal_uLong ScDocument::GetLinkRefreshDelay( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(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(maTabs.size()) && maTabs[nTab]) maTabs[nTab]->SetLink( nMode, rDoc, rFilter, rOptions, rTabName, nRefreshDelay ); } bool ScDocument::HasLink( const OUString& rDoc, const OUString& rFilter, const OUString& rOptions ) const { SCTAB nCount = static_cast(maTabs.size()); for (SCTAB i=0; iIsLinked() && 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; 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, OBJECT_CLIENT_FILE, aFileName, &aFilName ); pLink->Update(); pLink->SetInCreate( false ); SfxBindings* pBindings = GetViewBindings(); if (pBindings) pBindings->Invalidate( SID_LINKS ); } return true; } ScExternalRefManager* ScDocument::GetExternalRefManager() const { ScDocument* pThis = const_cast(this); if (!pExternalRefMgr.get()) pThis->pExternalRefMgr.reset( new ScExternalRefManager( pThis)); return pExternalRefMgr.get(); } bool ScDocument::IsInExternalReferenceMarking() const { return pExternalRefMgr.get() && pExternalRefMgr->isInReferenceMarking(); } void ScDocument::MarkUsedExternalReferences() { if (!pExternalRefMgr.get()) 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.get() ) mxFormulaParserPool.reset( new ScFormulaParserPool( *this ) ); return *mxFormulaParserPool; } const ScSheetEvents* ScDocument::GetSheetEvents( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->GetSheetEvents(); return nullptr; } void ScDocument::SetSheetEvents( SCTAB nTab, std::unique_ptr pNew ) { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) maTabs[nTab]->SetSheetEvents( std::move(pNew) ); } bool ScDocument::HasSheetEventScript( SCTAB nTab, ScSheetEventId nEvent, bool bWithVbaEvents ) const { if (nTab < static_cast(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( 1 ); aArgs[ 0 ] <<= 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(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(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(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(maTabs.size()) && maTabs[nTab] && !maTabs[nTab]->GetCalcNotification()) maTabs[nTab]->SetCalcNotification(true); } void ScDocument::ResetCalcNotifications() { SCTAB nSize = static_cast(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(maTabs.size())) if (maTabs[nTab]) { pVal = maTabs[nTab]->GetOutlineTable(); if (!pVal) if (bCreate) { maTabs[nTab]->StartOutlineTable(); pVal = maTabs[nTab]->GetOutlineTable(); } } return pVal; } bool ScDocument::SetOutlineTable( SCTAB nTab, const ScOutlineTable* pNewOutline ) { return ValidTab(nTab) && nTab < static_cast(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(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(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->TestRemoveSubTotals( rParam ); } void ScDocument::RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam ) { if ( ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab] ) maTabs[nTab]->RemoveSubTotals( rParam ); } bool ScDocument::DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam ) { return ValidTab(nTab) && nTab < static_cast(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(maTabs.size()); for (SCTAB nTab=0; nTabmaTabs[nTab] && pDestDoc->maTabs[nTab]) maTabs[nTab]->CopyUpdated( pPosDoc->maTabs[nTab], pDestDoc->maTabs[nTab] ); } void ScDocument::CopyScenario( SCTAB nSrcTab, SCTAB nDestTab, bool bNewScenario ) { if (ValidTab(nSrcTab) && ValidTab(nDestTab) && nSrcTab < static_cast(maTabs.size()) && nDestTab < static_cast(maTabs.size()) && maTabs[nSrcTab] && maTabs[nDestTab]) { // 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(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] ); } } } 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] ); 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(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(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->HasScenarioRange( rRange ); } const ScRangeList* ScDocument::GetScenarioRanges( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->GetScenarioRanges(); return nullptr; } bool ScDocument::IsActiveScenario( SCTAB nTab ) const { return ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab] && maTabs[nTab]->IsActiveScenario( ); } void ScDocument::SetActiveScenario( SCTAB nTab, bool bActive ) { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) maTabs[nTab]->SetActiveScenario( bActive ); } bool ScDocument::TestCopyScenario( SCTAB nSrcTab, SCTAB nDestTab ) const { if (ValidTab(nSrcTab) && nSrcTab < static_cast(maTabs.size()) && nDestTab < static_cast(maTabs.size())&& ValidTab(nDestTab)) return maTabs[nSrcTab]->TestCopyScenarioTo( maTabs[nDestTab] ); 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) { 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& 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 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() == MAXCOLCOUNT && rCxt.maRange.aEnd.Col() == MAXCOLCOUNT) || (rCxt.mnRowDelta < 0 && // convention from ScDocument::DeleteRow() rCxt.maRange.aStart.Row() == MAXROWCOUNT && rCxt.maRange.aEnd.Row() == MAXROWCOUNT)))) return; std::unique_ptr 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 = GetClipParam(); if (!rClipParam.maRanges.empty()) aSource = rClipParam.maRanges.front(); ScAddress aDest = rDestPos; SCTAB nClipTab = 0; for (SCTAB nDestTab=0; nDestTab< static_cast(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(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(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_uLong nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd, double nStepValue, double nMaxValue) { PutInOrder( nCol1, nCol2 ); PutInOrder( nRow1, nRow2 ); SCTAB nMax = maTabs.size(); ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end(); for (; itr != itrEnd && *itr < nMax; ++itr) if (maTabs[*itr]) maTabs[*itr]->Fill(nCol1, nRow1, nCol2, nRow2, nFillCount, eFillDir, eFillCmd, eFillDateCmd, nStepValue, nMaxValue, pProgress); } OUString ScDocument::GetAutoFillPreview( const ScRange& rSource, SCCOL nEndX, SCROW nEndY ) { SCTAB nTab = rSource.aStart.Tab(); if (nTab < static_cast(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(); ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end(); for (; itr != itrEnd && *itr < nMax; ++itr) if (maTabs[*itr]) maTabs[*itr]->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(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); } } } } 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(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(); ScMarkData::const_iterator itr = rMark.begin(), itrEnd = rMark.end(); for (; itr != itrEnd && *itr < nMax; ++itr) if (maTabs[*itr]) { nCol = 0; nRow = 0; bFound |= maTabs[*itr]->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(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(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(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(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); } SCSIZE ScDocument::Query(SCTAB nTab, const ScQueryParam& rQueryParam, bool bKeepSub) { if ( ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab] ) return maTabs[nTab]->Query(rQueryParam, bKeepSub); OSL_FAIL("missing tab"); return 0; } void ScDocument::GetUpperCellString(SCCOL nCol, SCROW nRow, SCTAB nTab, OUString& rStr) { if ( ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab] ) maTabs[nTab]->GetUpperCellString( nCol, nRow, rStr ); else rStr.clear(); } 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(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(maTabs.size()) && maTabs[nTab] && pDBCollection ) { ScDBData* pDBData = pDBCollection->GetDBAtCursor(nCol, nRow, nTab, ScDBDataPortion::AREA); //!?? if (pDBData) { 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 ); } 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(maTabs.size()) && maTabs[nTab] ) { maTabs[nTab]->GetFilterEntries( nCol, nStartRow, nEndRow, rFilterEntries ); sortAndRemoveDuplicates( rFilterEntries.maStrData, bCaseSens); } } /** * Entries for selection list listbox (no numbers/formulas) */ void ScDocument::GetDataEntries( SCCOL nCol, SCROW nRow, SCTAB nTab, std::vector& rStrings, bool bLimit ) { if( !bLimit ) { /* Try to generate the list from list validation. This part is skipped, if bLimit==true, 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(maTabs.size())) return; if (!maTabs[nTab]) return; std::set aStrings; if (maTabs[nTab]->GetDataEntries(nCol, nRow, aStrings, bLimit)) { 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 ) { ScRangeName::const_iterator itr = pRangeName->begin(), itrEnd = pRangeName->end(); for (; itr != itrEnd; ++itr) rStrings.insert(ScTypedStrData(itr->second->GetName(), 0.0, ScTypedStrData::Name)); } // Database collection if ( pDBCollection ) { const ScDBCollection::NamedDBs& rDBs = pDBCollection->getNamedDBs(); ScDBCollection::NamedDBs::const_iterator itr = rDBs.begin(), itrEnd = rDBs.end(); for (; itr != itrEnd; ++itr) rStrings.insert(ScTypedStrData((*itr)->GetName(), 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, 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(maTabs.size()) ) pTable = maTabs[aEmbedRange.aStart.Tab()]; else OSL_FAIL("table out of range"); if (!pTable) { OSL_FAIL("GetEmbeddedRect without a table"); } else { SCCOL i; for (i=0; iGetColWidth(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.SetLeft( static_cast( aRect.Left() * HMM_PER_TWIPS ) ); aRect.SetRight( static_cast( aRect.Right() * HMM_PER_TWIPS ) ); aRect.SetTop( static_cast( aRect.Top() * HMM_PER_TWIPS ) ); aRect.SetBottom( static_cast( aRect.Bottom() * HMM_PER_TWIPS ) ); } 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( long & rTwips, 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(nHeight) * nRows; if (nAdd + rTwips >= nStopTwips) { sal_Int64 nDiff = nAdd + rTwips - nStopTwips; nRows -= static_cast(nDiff / nHeight); nAdd = static_cast(nHeight) * nRows; // We're looking for a value that satisfies loop condition. if (nAdd + rTwips >= nStopTwips) { --nRows; nAdd -= nHeight; } bStop = true; } rTwips += static_cast(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(maTabs.size())) pTable = maTabs[nTab]; else OSL_FAIL("table out of range"); if (!pTable) { OSL_FAIL("GetRange without a table"); return ScRange(); } tools::Rectangle aPosRect = rMMRect; if ( IsNegativePage( nTab ) ) ScDrawLayer::MirrorRectRTL( aPosRect ); // Always with positive (LTR) values long nSize; long nTwips; long nAdd; bool bEnd; nSize = 0; nTwips = static_cast(aPosRect.Left() / HMM_PER_TWIPS); SCCOL nX1 = 0; bEnd = false; while (!bEnd) { nAdd = static_cast(pTable->GetColWidth(nX1, bHiddenAsZero)); if (nSize+nAdd <= nTwips+1 && nX1(aPosRect.Right() / HMM_PER_TWIPS); SCCOL nX2 = nX1; bEnd = false; while (!bEnd) { nAdd = static_cast(pTable->GetColWidth(nX2, bHiddenAsZero)); if (nSize+nAdd < nTwips && nX2(aPosRect.Top() / HMM_PER_TWIPS); SCROW nY1 = 0; // Was if(nSize+nAdd<=nTwips+1) inside loop => if(nSize+nAdd(aPosRect.Bottom() / HMM_PER_TWIPS); SCROW nY2 = nY1; // Was if(nSize+nAdd if(nSize+nAddisProtected(); } 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(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->IsProtected(); OSL_FAIL("Wrong table number"); return false; } ScTableProtection* ScDocument::GetTabProtection( SCTAB nTab ) const { if (ValidTab(nTab) && nTab < static_cast(maTabs.size()) && maTabs[nTab]) return maTabs[nTab]->GetProtection(); return nullptr; } void ScDocument::SetTabProtection(SCTAB nTab, const ScTableProtection* pProtect) { if (!ValidTab(nTab) || nTab >= static_cast(maTabs.size())) return; maTabs[nTab]->SetProtection(pProtect); } void ScDocument::CopyTabProtection(SCTAB nTabSrc, SCTAB nTabDest) { if (!ValidTab(nTabSrc) || nTabSrc >= static_cast(maTabs.size()) || nTabDest >= static_cast(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; 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(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(aRect.Left() * HMM_PER_TWIPS) ); aRect.SetRight( static_cast(aRect.Right() * HMM_PER_TWIPS) ); aRect.SetTop( static_cast(aRect.Top() * HMM_PER_TWIPS) ); aRect.SetBottom( static_cast(aRect.Bottom() * HMM_PER_TWIPS) ); if ( IsNegativePage( nTab ) ) ScDrawLayer::MirrorRectRTL( aRect ); return aRect; } void ScDocument::SetExtDocOptions( ScExtDocOptions* pNewOptions ) { delete pExtDocOptions; pExtDocOptions = pNewOptions; } void ScDocument::SetClipOptions(const ScClipOptions& rClipOptions) { mpClipOptions = o3tl::make_unique(rClipOptions); } void ScDocument::DoMergeContents( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) { 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( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) { 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( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, 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(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(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(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(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; } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */