/* -*- 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 using namespace ::com::sun::star; using ::com::sun::star::uno::Reference; using ::std::vector; using ::std::pair; /** * A simple container to keep track of cells that depend on basic modules * changes. We don't check for duplicates at insertion time; instead, we * remove duplicates at query time. */ class ScUserMacroDepTracker { public: void addCell(const OUString& rModuleName, ScFormulaCell* pCell) { ModuleCellMap::iterator itr = maCells.find(rModuleName); if (itr == maCells.end()) { pair r = maCells.emplace( rModuleName, vector()); if (!r.second) // insertion failed. return; itr = r.first; } itr->second.push_back(pCell); } void removeCell(const ScFormulaCell* pCell) { for (auto& rEntry : maCells) { std::erase(rEntry.second, pCell); } } void getCellsByModule(const OUString& rModuleName, vector& rCells) { ModuleCellMap::iterator itr = maCells.find(rModuleName); if (itr == maCells.end()) return; vector& rCellList = itr->second; // Remove duplicates. std::sort(rCellList.begin(), rCellList.end()); auto last = std::unique(rCellList.begin(), rCellList.end()); rCellList.erase(last, rCellList.end()); // exception safe copy vector temp(rCellList); rCells.swap(temp); } private: typedef std::unordered_map> ModuleCellMap; ModuleCellMap maCells; }; ScMacroManager::ScMacroManager(ScDocument& rDoc) : mpDepTracker(new ScUserMacroDepTracker), mrDoc(rDoc) { } ScMacroManager::~ScMacroManager() { } typedef ::cppu::WeakImplHelper< css::container::XContainerListener > ContainerListenerHelper; namespace { class VBAProjectListener : public ContainerListenerHelper { ScMacroManager* mpMacroMgr; public: explicit VBAProjectListener( ScMacroManager* pMacroMgr ) : mpMacroMgr( pMacroMgr ) {} // XEventListener virtual void SAL_CALL disposing( const lang::EventObject& /*Source*/ ) override {} // XContainerListener virtual void SAL_CALL elementInserted( const container::ContainerEvent& /*Event*/ ) override {} virtual void SAL_CALL elementReplaced( const container::ContainerEvent& Event ) override { OUString sModuleName; Event.Accessor >>= sModuleName; mpMacroMgr->InitUserFuncData(); mpMacroMgr->BroadcastModuleUpdate(sModuleName); } virtual void SAL_CALL elementRemoved( const container::ContainerEvent& /*Event*/ ) override {} }; } void ScMacroManager::InitUserFuncData() { // Clear unordered_map mhFuncToVolatile.clear(); OUString sProjectName("Standard"); Reference< container::XContainer > xModuleContainer; ScDocShell* pShell = mrDoc.GetDocumentShell(); if (!pShell) return; #if HAVE_FEATURE_SCRIPTING const BasicManager *pBasicManager = pShell->GetBasicManager(); if (!pBasicManager->GetName().isEmpty()) { sProjectName = pBasicManager->GetName(); } #endif try { Reference< script::XLibraryContainer > xLibraries( pShell->GetBasicContainer(), uno::UNO_SET_THROW ); xModuleContainer.set( xLibraries->getByName( sProjectName ), uno::UNO_QUERY_THROW ); // remove old listener ( if there was one ) if ( mxContainerListener.is() ) xModuleContainer->removeContainerListener( mxContainerListener ); // Create listener mxContainerListener = new VBAProjectListener( this ); xModuleContainer->addContainerListener( mxContainerListener ); } catch (const uno::Exception&) { } } void ScMacroManager::SetUserFuncVolatile( const OUString& sName, bool isVolatile ) { mhFuncToVolatile[ sName ] = isVolatile; } bool ScMacroManager::GetUserFuncVolatile( const OUString& sName ) { NameBoolMap::iterator it = mhFuncToVolatile.find( sName ); if ( it == mhFuncToVolatile.end() ) return false; return it->second; } void ScMacroManager::AddDependentCell(const OUString& aModuleName, ScFormulaCell* pCell) { mpDepTracker->addCell(aModuleName, pCell); } void ScMacroManager::RemoveDependentCell(const ScFormulaCell* pCell) { mpDepTracker->removeCell(pCell); } void ScMacroManager::BroadcastModuleUpdate(const OUString& aModuleName) { vector aCells; mpDepTracker->getCellsByModule(aModuleName, aCells); for (ScFormulaCell* pCell : aCells) { mrDoc.PutInFormulaTree(pCell); // for F9 recalc // for recalc on cell value change. If the cell is not volatile, the // cell stops listening right away after it gets re-interpreted. mrDoc.StartListeningArea(BCA_LISTEN_ALWAYS, false, pCell); } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */