tdf#130857 qt weld: Implement QtInstanceWidget::strip_mnemonic
[LibreOffice.git] / basic / source / basmgr / basicmanagerrepository.cxx
blob260369cbab21d0463f65bea48fe64e2741a760eb
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <basic/basicmanagerrepository.hxx>
21 #include <basic/basmgr.hxx>
22 #include <scriptcont.hxx>
23 #include <dlgcont.hxx>
24 #include <sbintern.hxx>
25 #include <sbxbase.hxx>
27 #include <com/sun/star/document/XStorageBasedDocument.hpp>
28 #include <com/sun/star/document/XEmbeddedScripts.hpp>
29 #include <com/sun/star/frame/Desktop.hpp>
30 #include <o3tl/string_view.hxx>
31 #include <svtools/ehdl.hxx>
32 #include <svtools/sfxecode.hxx>
33 #include <unotools/pathoptions.hxx>
34 #include <svl/hint.hxx>
35 #include <vcl/svapp.hxx>
36 #include <tools/debug.hxx>
37 #include <comphelper/diagnose_ex.hxx>
38 #include <tools/urlobj.hxx>
39 #include <comphelper/processfactory.hxx>
40 #include <comphelper/documentinfo.hxx>
41 #include <unotools/eventlisteneradapter.hxx>
43 #include <sot/storage.hxx>
45 #include <map>
46 #include <mutex>
49 namespace basic
51 using ::com::sun::star::uno::Reference;
52 using ::com::sun::star::uno::XComponentContext;
53 using ::com::sun::star::frame::XModel;
54 using ::com::sun::star::frame::Desktop;
55 using ::com::sun::star::uno::XInterface;
56 using ::com::sun::star::uno::UNO_QUERY;
57 using ::com::sun::star::embed::XStorage;
58 using ::com::sun::star::script::XPersistentLibraryContainer;
59 using ::com::sun::star::uno::UNO_QUERY_THROW;
60 using ::com::sun::star::uno::Exception;
61 using ::com::sun::star::document::XStorageBasedDocument;
62 using ::com::sun::star::document::XEmbeddedScripts;
64 typedef std::map< Reference< XInterface >, std::unique_ptr<BasicManager> > BasicManagerStore;
66 typedef std::vector< BasicManagerCreationListener* > CreationListeners;
68 class ImplRepository : public ::utl::OEventListenerAdapter, public SfxListener, public SvRefBase
70 private:
71 ImplRepository();
72 ~ImplRepository();
74 private:
75 BasicManagerStore m_aStore;
76 CreationListeners m_aCreationListeners;
78 public:
79 static ImplRepository& Instance();
81 BasicManager* getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel );
82 BasicManager* getOrCreateApplicationBasicManager();
83 static BasicManager* getApplicationBasicManager();
84 static void setApplicationBasicManager( std::unique_ptr<BasicManager> _pBasicManager );
85 void registerCreationListener( BasicManagerCreationListener& _rListener );
86 void revokeCreationListener( BasicManagerCreationListener& _rListener );
88 private:
89 /** retrieves the location at which the BasicManager for the given model
90 is stored.
92 If previously, the BasicManager for this model has never been requested,
93 then the model is added to the map, with an initial NULL BasicManager.
95 @param _rxDocumentModel
96 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
98 @precond
99 our mutex is locked
101 BasicManagerStore::iterator
102 impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel );
104 /** tests if there is a location set at which the BasicManager for the given model
105 is stored.
107 @param _rxDocumentModel
108 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
110 @precond
111 our mutex is locked
113 bool impl_hasLocationForModel( const Reference< XModel >& _rxDocumentModel ) const;
115 /** creates a new BasicManager instance for the given model
117 @param _out_rpBasicManager
118 reference to the pointer variable that will hold the new
119 BasicManager.
121 @param _rxDocumentModel
122 the model whose BasicManager will be created. Must not be <NULL/>.
124 bool impl_createManagerForModel(
125 BasicManagerStore::iterator location,
126 const Reference< XModel >& _rxDocumentModel );
128 /** creates the application-wide BasicManager
130 BasicManager* impl_createApplicationBasicManager();
132 /** notifies all listeners which expressed interest in the creation of BasicManager instances.
134 void impl_notifyCreationListeners(
135 const Reference< XModel >& _rxDocumentModel,
136 BasicManager& _rManager
139 /** retrieves the current storage of a given document
141 @param _rxDocument
142 the document whose storage is to be retrieved.
144 @param _out_rStorage
145 takes the storage upon successful return. Note that this might be <NULL/> even
146 if <TRUE/> is returned. In this case, the document has not yet been saved.
148 @return
149 <TRUE/> if the storage could be successfully retrieved (in which case
150 <arg>_out_rStorage</arg> might or might not be <NULL/>), <FALSE/> otherwise.
151 In the latter case, processing this document should stop.
153 static bool impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage );
155 /** retrieves the containers for Basic and Dialog libraries for a given document
157 @param _rxDocument
158 the document whose containers are to be retrieved.
160 @param _out_rxBasicLibraries
161 takes the basic library container upon successful return
163 @param _out_rxDialogLibraries
164 takes the dialog library container upon successful return
166 @return
167 <TRUE/> if and only if both containers exist, and could successfully be retrieved
169 static bool impl_getDocumentLibraryContainers_nothrow(
170 const Reference< XModel >& _rxDocument,
171 Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries,
172 Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries
175 /** initializes the given library containers, which belong to a document
177 static void impl_initDocLibraryContainers_nothrow(
178 const Reference< XPersistentLibraryContainer >& _rxBasicLibraries,
179 const Reference< XPersistentLibraryContainer >& _rxDialogLibraries
182 // OEventListenerAdapter overridables
183 virtual void _disposing( const css::lang::EventObject& _rSource ) override;
185 // SfxListener overridables
186 virtual void Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint ) override;
188 /** removes the Model/BasicManager pair given by iterator from our store
190 void impl_removeFromRepository( const BasicManagerStore::iterator& _pos );
192 private:
193 StarBASIC* impl_getDefaultAppBasicLibrary();
196 ImplRepository::ImplRepository()
200 ImplRepository::~ImplRepository()
202 // Avoid double-delete of managers when they are destroyed in our dtor, and start notify us
203 for (auto& it : m_aStore)
204 EndListening(*it.second);
207 ImplRepository& ImplRepository::Instance()
209 tools::SvRef<SvRefBase>& repository = GetSbxData_Impl().mrImplRepository;
211 static std::mutex aMutex;
212 std::unique_lock aGuard(aMutex);
213 if (!repository)
214 repository = new ImplRepository;
216 return *static_cast<ImplRepository*>(repository.get());
219 BasicManager* ImplRepository::getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel )
221 SolarMutexGuard g;
223 /* #163556# (DR) - This function may be called recursively while
224 constructing the Basic manager and loading the Basic storage. By
225 passing the map entry received from impl_getLocationForModel() to
226 the function impl_createManagerForModel(), the new Basic manager
227 will be put immediately into the map of existing Basic managers,
228 thus a recursive call of this function will find and return it
229 without creating another instance.
231 auto const loc = impl_getLocationForModel( _rxDocumentModel );
232 if (loc->second != nullptr)
233 return loc->second.get();
234 if (impl_createManagerForModel(loc, _rxDocumentModel))
235 return loc->second.get();
236 return nullptr;
239 BasicManager* ImplRepository::getOrCreateApplicationBasicManager()
241 SolarMutexGuard g;
243 BasicManager* pAppManager = GetSbData()->pAppBasMgr.get();
244 if (pAppManager == nullptr)
245 pAppManager = impl_createApplicationBasicManager();
246 return pAppManager;
249 BasicManager* ImplRepository::getApplicationBasicManager()
251 SolarMutexGuard g;
253 return GetSbData()->pAppBasMgr.get();
256 void ImplRepository::setApplicationBasicManager( std::unique_ptr<BasicManager> _pBasicManager )
258 SolarMutexGuard g;
260 GetSbData()->pAppBasMgr = std::move(_pBasicManager);
264 BasicManager* ImplRepository::impl_createApplicationBasicManager()
266 SolarMutexGuard g;
268 OSL_PRECOND(getApplicationBasicManager() == nullptr, "ImplRepository::impl_createApplicationBasicManager: there already is one!");
270 // Determine Directory
271 SvtPathOptions aPathCFG;
272 OUString aAppBasicDir( aPathCFG.GetBasicPath() );
273 if ( aAppBasicDir.isEmpty() )
275 aPathCFG.SetBasicPath(u"$(prog)"_ustr);
278 // Create basic and load it
279 // AppBasicDir is now a PATH
280 INetURLObject aAppBasic( SvtPathOptions().SubstituteVariable(u"$(progurl)"_ustr) );
281 aAppBasic.insertName( Application::GetAppName() );
283 BasicManager* pBasicManager = new BasicManager( new StarBASIC, &aAppBasicDir );
284 setApplicationBasicManager( std::unique_ptr<BasicManager>(pBasicManager) );
286 // The first dir in the path as destination:
287 OUString aFileName( aAppBasic.getName() );
288 aAppBasic = INetURLObject( o3tl::getToken(aAppBasicDir, 1, ';') );
289 DBG_ASSERT(aAppBasic.GetProtocol() != INetProtocol::NotValid,
290 OString("Invalid URL: \"" +
291 OUStringToOString(aAppBasicDir, osl_getThreadTextEncoding()) +
292 "\"").getStr());
293 aAppBasic.insertName( aFileName );
294 pBasicManager->SetStorageName( aAppBasic.PathToFileName() );
296 // Basic container
297 rtl::Reference<SfxScriptLibraryContainer> pBasicCont = new SfxScriptLibraryContainer( Reference< XStorage >() );
298 pBasicCont->setBasicManager( pBasicManager );
300 // Dialog container
301 rtl::Reference<SfxDialogLibraryContainer> pDialogCont = new SfxDialogLibraryContainer( Reference< XStorage >() );
303 LibraryContainerInfo aInfo( pBasicCont, pDialogCont, pBasicCont.get() );
304 pBasicManager->SetLibraryContainerInfo( aInfo );
306 // global constants
308 // StarDesktop
309 const Reference< XComponentContext >& xContext = ::comphelper::getProcessComponentContext();
310 pBasicManager->SetGlobalUNOConstant( u"StarDesktop"_ustr, css::uno::Any( Desktop::create(xContext)));
312 // (BasicLibraries and DialogLibraries have automatically been added in SetLibraryContainerInfo)
314 // notify
315 impl_notifyCreationListeners( nullptr, *pBasicManager );
317 // outta here
318 return pBasicManager;
322 void ImplRepository::registerCreationListener( BasicManagerCreationListener& _rListener )
324 SolarMutexGuard g;
326 m_aCreationListeners.push_back( &_rListener );
330 void ImplRepository::revokeCreationListener( BasicManagerCreationListener& _rListener )
332 SolarMutexGuard g;
334 CreationListeners::iterator pos = std::find( m_aCreationListeners.begin(), m_aCreationListeners.end(), &_rListener );
335 if ( pos != m_aCreationListeners.end() )
336 m_aCreationListeners.erase( pos );
337 else {
338 OSL_FAIL( "ImplRepository::revokeCreationListener: listener is not registered!" );
343 void ImplRepository::impl_notifyCreationListeners( const Reference< XModel >& _rxDocumentModel, BasicManager& _rManager )
345 for (auto const& creationListener : m_aCreationListeners)
347 creationListener->onBasicManagerCreated( _rxDocumentModel, _rManager );
352 StarBASIC* ImplRepository::impl_getDefaultAppBasicLibrary()
354 BasicManager* pAppManager = getOrCreateApplicationBasicManager();
356 StarBASIC* pAppBasic = pAppManager ? pAppManager->GetLib(0) : nullptr;
357 DBG_ASSERT( pAppBasic != nullptr, "impl_getApplicationBasic: unable to determine the default application's Basic library!" );
358 return pAppBasic;
361 BasicManagerStore::iterator ImplRepository::impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel )
363 Reference< XInterface > xNormalized( _rxDocumentModel, UNO_QUERY );
364 DBG_ASSERT( _rxDocumentModel.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
366 return m_aStore.try_emplace(xNormalized).first;
369 bool ImplRepository::impl_hasLocationForModel( const Reference< XModel >& _rxDocumentModel ) const
371 Reference< XInterface > xNormalized( _rxDocumentModel, UNO_QUERY );
372 DBG_ASSERT( _rxDocumentModel.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
374 return m_aStore.find(xNormalized) != m_aStore.end();
377 void ImplRepository::impl_initDocLibraryContainers_nothrow( const Reference< XPersistentLibraryContainer >& _rxBasicLibraries, const Reference< XPersistentLibraryContainer >& _rxDialogLibraries )
379 OSL_PRECOND( _rxBasicLibraries.is() && _rxDialogLibraries.is(),
380 "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" );
384 // ensure there's a standard library in the basic container
385 static constexpr OUString aStdLibName( u"Standard"_ustr );
386 if ( !_rxBasicLibraries->hasByName( aStdLibName ) )
388 _rxBasicLibraries->createLibrary( aStdLibName );
390 // as well as in the dialog container
391 if ( !_rxDialogLibraries->hasByName( aStdLibName ) )
393 _rxDialogLibraries->createLibrary( aStdLibName );
396 catch( const Exception& )
398 DBG_UNHANDLED_EXCEPTION("basic");
402 bool ImplRepository::impl_createManagerForModel( BasicManagerStore::iterator location, const Reference< XModel >& _rxDocumentModel )
404 auto & _out_rpBasicManager = location->second;
406 StarBASIC* pAppBasic = impl_getDefaultAppBasicLibrary();
408 _out_rpBasicManager = nullptr;
409 Reference< XStorage > xStorage;
410 if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel, xStorage ) )
412 m_aStore.erase(location);
413 // the document is not able to provide the storage it is based on.
414 return false;
416 Reference< XPersistentLibraryContainer > xBasicLibs;
417 Reference< XPersistentLibraryContainer > xDialogLibs;
418 if ( !impl_getDocumentLibraryContainers_nothrow( _rxDocumentModel, xBasicLibs, xDialogLibs ) )
420 m_aStore.erase(location);
421 // the document does not have BasicLibraries and DialogLibraries
422 return false;
425 if ( xStorage.is() )
427 // load BASIC-manager
428 SfxErrorContext aErrContext( ERRCTX_SFX_LOADBASIC,
429 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel ) );
430 OUString aAppBasicDir = SvtPathOptions().GetBasicPath();
432 // Storage and BaseURL are only needed by binary documents!
433 rtl::Reference<SotStorage> xDummyStor = new SotStorage(OUString());
434 _out_rpBasicManager.reset(new BasicManager( *xDummyStor, u"" /* TODO/LATER: xStorage */,
435 pAppBasic,
436 &aAppBasicDir, true ));
437 if ( !_out_rpBasicManager->GetErrors().empty() )
439 // handle errors
440 std::vector<BasicError>& aErrors = _out_rpBasicManager->GetErrors();
441 for(const auto& rError : aErrors)
443 // show message to user
444 if ( ErrorHandler::HandleError( rError.GetErrorId() ) == DialogMask::ButtonsCancel )
446 // user wants to break loading of BASIC-manager
447 _out_rpBasicManager.reset();
448 xStorage.clear();
449 break;
455 // not loaded?
456 if ( !xStorage.is() )
458 // create new BASIC-manager
459 StarBASIC* pBasic = new StarBASIC( pAppBasic );
460 pBasic->SetFlag( SbxFlagBits::ExtSearch );
461 _out_rpBasicManager.reset(new BasicManager( pBasic, nullptr, true ));
464 // knit the containers with the BasicManager
465 LibraryContainerInfo aInfo( xBasicLibs, xDialogLibs, dynamic_cast< SfxScriptLibraryContainer* >( xBasicLibs.get() ) );
466 OSL_ENSURE( aInfo.mpOldBasicPassword, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" );
467 _out_rpBasicManager->SetLibraryContainerInfo( aInfo );
469 // initialize the containers
470 impl_initDocLibraryContainers_nothrow( xBasicLibs, xDialogLibs );
472 // so that also dialogs etc. could be 'qualified' addressed
473 _out_rpBasicManager->GetLib(0)->SetParent( pAppBasic );
475 // global properties in the document's Basic
476 _out_rpBasicManager->SetGlobalUNOConstant( u"ThisComponent"_ustr, css::uno::Any( _rxDocumentModel ) );
478 // notify
479 impl_notifyCreationListeners( _rxDocumentModel, *_out_rpBasicManager );
481 // register as listener for this model being disposed/closed
482 OSL_ENSURE( _rxDocumentModel.is(), "ImplRepository::impl_createManagerForModel: the document must be an XComponent!" );
483 assert(impl_hasLocationForModel(_rxDocumentModel));
484 startComponentListening( _rxDocumentModel );
486 bool bOk = false;
487 // startComponentListening may fail in a disposed _rxDocumentModel, in which case _out_rpBasicManager will be removed
488 // from the map and destroyed
489 if (impl_hasLocationForModel(_rxDocumentModel))
491 bOk = true;
492 // register as listener for the BasicManager being destroyed
493 StartListening( *_out_rpBasicManager );
496 // #i104876: Library container must not be modified just after
497 // creation. This happens as side effect when creating default
498 // "Standard" libraries and needs to be corrected here
499 xBasicLibs->setModified( false );
500 xDialogLibs->setModified( false );
501 return bOk;
504 bool ImplRepository::impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage )
506 _out_rStorage.clear();
509 Reference< XStorageBasedDocument > xStorDoc( _rxDocument, UNO_QUERY_THROW );
510 _out_rStorage.set( xStorDoc->getDocumentStorage() );
512 catch( const Exception& )
514 DBG_UNHANDLED_EXCEPTION("basic");
515 return false;
517 return true;
521 bool ImplRepository::impl_getDocumentLibraryContainers_nothrow( const Reference< XModel >& _rxDocument,
522 Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries, Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries )
524 _out_rxBasicLibraries.clear();
525 _out_rxDialogLibraries.clear();
528 Reference< XEmbeddedScripts > xScripts( _rxDocument, UNO_QUERY_THROW );
529 _out_rxBasicLibraries.set( xScripts->getBasicLibraries(), UNO_QUERY_THROW );
530 _out_rxDialogLibraries.set( xScripts->getDialogLibraries(), UNO_QUERY_THROW );
532 catch( const Exception& )
534 DBG_UNHANDLED_EXCEPTION("basic");
536 return _out_rxBasicLibraries.is() && _out_rxDialogLibraries.is();
540 void ImplRepository::impl_removeFromRepository( const BasicManagerStore::iterator& _pos )
542 OSL_PRECOND( _pos != m_aStore.end(), "ImplRepository::impl_removeFromRepository: invalid position!" );
544 std::unique_ptr<BasicManager> pManager = std::move(_pos->second);
545 Reference<XModel> xModel(_pos->first, UNO_QUERY);
547 // *first* remove from map (else Notify won't work properly)
548 m_aStore.erase( _pos );
550 EndListening( *pManager );
552 assert(xModel.is());
553 if (xModel.is())
554 stopComponentListening(xModel);
558 void ImplRepository::_disposing( const css::lang::EventObject& _rSource )
560 SolarMutexGuard g;
562 Reference< XInterface > xNormalizedSource( _rSource.Source, UNO_QUERY );
564 BasicManagerStore::iterator it = std::find_if(m_aStore.begin(), m_aStore.end(),
565 [&xNormalizedSource](BasicManagerStore::reference rEntry) {
566 return rEntry.first.get() == xNormalizedSource.get(); });
567 if (it != m_aStore.end())
569 impl_removeFromRepository( it );
570 return;
573 OSL_FAIL( "ImplRepository::_disposing: where does this come from?" );
577 void ImplRepository::Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint )
579 if ( _rHint.GetId() != SfxHintId::Dying )
580 // not interested in
581 return;
583 BasicManager* pManager = dynamic_cast< BasicManager* >( &_rBC );
584 OSL_ENSURE( pManager, "ImplRepository::Notify: where does this come from?" );
586 BasicManagerStore::iterator it = std::find_if(m_aStore.begin(), m_aStore.end(),
587 [&pManager](BasicManagerStore::reference rEntry) { return rEntry.second.get() == pManager; });
588 if (it != m_aStore.end())
590 // a BasicManager which is still in our repository is being deleted.
591 // That's bad, since by definition, we *own* all instances in our
592 // repository.
593 OSL_FAIL( "ImplRepository::Notify: nobody should tamper with the managers, except ourself!" );
594 m_aStore.erase( it );
598 BasicManager* BasicManagerRepository::getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel )
600 return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel );
603 BasicManager* BasicManagerRepository::getApplicationBasicManager()
605 return ImplRepository::Instance().getOrCreateApplicationBasicManager();
608 void BasicManagerRepository::resetApplicationBasicManager()
610 ImplRepository::setApplicationBasicManager( nullptr );
613 void BasicManagerRepository::registerCreationListener( BasicManagerCreationListener& _rListener )
615 ImplRepository::Instance().registerCreationListener( _rListener );
618 void BasicManagerRepository::revokeCreationListener( BasicManagerCreationListener& _rListener )
620 ImplRepository::Instance().revokeCreationListener( _rListener );
623 } // namespace basic
626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */