Avoid potential negative array index access to cached text.
[LibreOffice.git] / framework / source / uiconfiguration / uiconfigurationmanager.cxx
blobb678f7b0635cb3f161d2c41047cdafafd88847f2
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 <uiconfiguration/imagemanager.hxx>
21 #include <uielement/rootitemcontainer.hxx>
22 #include <uielement/constitemcontainer.hxx>
23 #include <uielement/uielementtypenames.hxx>
24 #include <menuconfiguration.hxx>
25 #include <statusbarconfiguration.hxx>
26 #include <toolboxconfiguration.hxx>
28 #include <com/sun/star/beans/XPropertySet.hpp>
29 #include <com/sun/star/container/ElementExistException.hpp>
30 #include <com/sun/star/container/XIndexContainer.hpp>
31 #include <com/sun/star/embed/ElementModes.hpp>
32 #include <com/sun/star/embed/InvalidStorageException.hpp>
33 #include <com/sun/star/embed/StorageWrappedTargetException.hpp>
34 #include <com/sun/star/embed/XTransactedObject.hpp>
35 #include <com/sun/star/lang/IllegalAccessException.hpp>
36 #include <com/sun/star/lang/DisposedException.hpp>
37 #include <com/sun/star/io/IOException.hpp>
38 #include <com/sun/star/io/XStream.hpp>
39 #include <com/sun/star/ui/UIElementType.hpp>
40 #include <com/sun/star/ui/ConfigurationEvent.hpp>
41 #include <com/sun/star/ui/DocumentAcceleratorConfiguration.hpp>
42 #include <com/sun/star/ui/XAcceleratorConfiguration.hpp>
43 #include <com/sun/star/ui/XUIConfigurationManager2.hpp>
44 #include <com/sun/star/lang/XComponent.hpp>
45 #include <com/sun/star/lang/XServiceInfo.hpp>
47 #include <cppuhelper/implbase.hxx>
48 #include <cppuhelper/supportsservice.hxx>
49 #include <comphelper/propertysequence.hxx>
50 #include <comphelper/propertyvalue.hxx>
51 #include <comphelper/interfacecontainer4.hxx>
52 #include <comphelper/sequence.hxx>
53 #include <comphelper/servicehelper.hxx>
54 #include <utility>
55 #include <vcl/svapp.hxx>
56 #include <sal/log.hxx>
57 #include <o3tl/string_view.hxx>
59 #include <mutex>
60 #include <string_view>
61 #include <unordered_map>
63 using namespace com::sun::star::uno;
64 using namespace com::sun::star::io;
65 using namespace com::sun::star::embed;
66 using namespace com::sun::star::lang;
67 using namespace com::sun::star::container;
68 using namespace com::sun::star::beans;
69 using namespace com::sun::star::ui;
70 using namespace framework;
72 namespace {
74 class UIConfigurationManager : public ::cppu::WeakImplHelper<
75 css::lang::XServiceInfo ,
76 css::ui::XUIConfigurationManager2 >
78 public:
79 virtual OUString SAL_CALL getImplementationName() override
81 return "com.sun.star.comp.framework.UIConfigurationManager";
84 virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
86 return cppu::supportsService(this, ServiceName);
89 virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
91 return {"com.sun.star.ui.UIConfigurationManager"};
94 explicit UIConfigurationManager( css::uno::Reference< css::uno::XComponentContext > xContext );
96 // XComponent
97 virtual void SAL_CALL dispose() override;
98 virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
99 virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
101 // XUIConfiguration
102 virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override;
103 virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override;
105 // XUIConfigurationManager
106 virtual void SAL_CALL reset() override;
107 virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getUIElementsInfo( sal_Int16 ElementType ) override;
108 virtual css::uno::Reference< css::container::XIndexContainer > SAL_CALL createSettings( ) override;
109 virtual sal_Bool SAL_CALL hasSettings( const OUString& ResourceURL ) override;
110 virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) override;
111 virtual void SAL_CALL replaceSettings( const OUString& ResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override;
112 virtual void SAL_CALL removeSettings( const OUString& ResourceURL ) override;
113 virtual void SAL_CALL insertSettings( const OUString& NewResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override;
114 virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getImageManager() override;
115 virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL getShortCutManager() override;
116 virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL createShortCutManager() override;
117 virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getEventsManager() override;
119 // XUIConfigurationPersistence
120 virtual void SAL_CALL reload() override;
121 virtual void SAL_CALL store() override;
122 virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override;
123 virtual sal_Bool SAL_CALL isModified() override;
124 virtual sal_Bool SAL_CALL isReadOnly() override;
126 // XUIConfigurationStorage
127 virtual void SAL_CALL setStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override;
128 virtual sal_Bool SAL_CALL hasStorage() override;
130 private:
131 // private data types
132 enum NotifyOp
134 NotifyOp_Remove,
135 NotifyOp_Insert,
136 NotifyOp_Replace
139 struct UIElementInfo
141 UIElementInfo( OUString _aResourceURL, OUString _aUIName ) :
142 aResourceURL(std::move( _aResourceURL)), aUIName(std::move( _aUIName )) {}
143 OUString aResourceURL;
144 OUString aUIName;
147 struct UIElementData
149 UIElementData() : bModified( false ), bDefault( true ) {};
151 OUString aResourceURL;
152 OUString aName;
153 bool bModified; // has been changed since last storing
154 bool bDefault; // default settings
155 css::uno::Reference< css::container::XIndexAccess > xSettings;
158 struct UIElementType;
159 friend struct UIElementType;
160 typedef std::unordered_map< OUString, UIElementData > UIElementDataHashMap;
162 struct UIElementType
164 UIElementType() : bModified( false ),
165 bLoaded( false ),
166 nElementType( css::ui::UIElementType::UNKNOWN ) {}
168 bool bModified;
169 bool bLoaded;
170 sal_Int16 nElementType;
171 UIElementDataHashMap aElementsHashMap;
172 css::uno::Reference< css::embed::XStorage > xStorage;
175 typedef std::vector< UIElementType > UIElementTypesVector;
176 typedef std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer;
177 typedef std::unordered_map< OUString, UIElementInfo > UIElementInfoHashMap;
179 void impl_Initialize();
180 void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp );
181 void impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType );
182 void impl_preloadUIElementTypeList( sal_Int16 nElementType );
183 UIElementData* impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad = true );
184 void impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData );
185 void impl_storeElementTypeData( css::uno::Reference< css::embed::XStorage > const & xStorage, UIElementType& rElementType, bool bResetModifyState = true );
186 void impl_resetElementTypeData( UIElementType& rDocElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer );
187 void impl_reloadElementTypeData( UIElementType& rDocElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer );
189 UIElementTypesVector m_aUIElements;
190 css::uno::Reference< css::embed::XStorage > m_xDocConfigStorage;
191 bool m_bReadOnly;
192 bool m_bModified;
193 bool m_bDisposed;
194 OUString m_aPropUIName;
195 css::uno::Reference< css::uno::XComponentContext > m_xContext;
196 std::mutex m_mutex;
197 comphelper::OInterfaceContainerHelper4<css::lang::XEventListener> m_aEventListeners;
198 comphelper::OInterfaceContainerHelper4<css::ui::XUIConfigurationListener> m_aConfigListeners;
199 rtl::Reference< ImageManager > m_xImageManager;
200 css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xAccConfig;
203 // important: The order and position of the elements must match the constant
204 // definition of "css::ui::UIElementType"
205 std::u16string_view UIELEMENTTYPENAMES[] =
207 u"", // Dummy value for unknown!
208 u"" UIELEMENTTYPE_MENUBAR_NAME,
209 u"" UIELEMENTTYPE_POPUPMENU_NAME,
210 u"" UIELEMENTTYPE_TOOLBAR_NAME,
211 u"" UIELEMENTTYPE_STATUSBAR_NAME,
212 u"" UIELEMENTTYPE_FLOATINGWINDOW_NAME,
213 u"" UIELEMENTTYPE_PROGRESSBAR_NAME,
214 u"" UIELEMENTTYPE_TOOLPANEL_NAME
217 constexpr std::u16string_view RESOURCEURL_PREFIX = u"private:resource/";
219 sal_Int16 RetrieveTypeFromResourceURL( std::u16string_view aResourceURL )
222 if (( o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX ) ) &&
223 ( aResourceURL.size() > RESOURCEURL_PREFIX.size() ))
225 std::u16string_view aTmpStr = aResourceURL.substr( RESOURCEURL_PREFIX.size() );
226 size_t nIndex = aTmpStr.find( '/' );
227 if (( nIndex > 0 ) && ( aTmpStr.size() > nIndex ))
229 std::u16string_view aTypeStr( aTmpStr.substr( 0, nIndex ));
230 for ( int i = 0; i < UIElementType::COUNT; i++ )
232 if ( aTypeStr == UIELEMENTTYPENAMES[i] )
233 return sal_Int16( i );
238 return UIElementType::UNKNOWN;
241 OUString RetrieveNameFromResourceURL( std::u16string_view aResourceURL )
243 if (( o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX ) ) &&
244 ( aResourceURL.size() > RESOURCEURL_PREFIX.size() ))
246 size_t nIndex = aResourceURL.rfind( '/' );
247 if ( (nIndex > 0) && (nIndex != std::u16string_view::npos) && (( nIndex+1 ) < aResourceURL.size()) )
248 return OUString(aResourceURL.substr( nIndex+1 ));
251 return OUString();
254 void UIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType )
256 // preload list of element types on demand
257 impl_preloadUIElementTypeList( nElementType );
259 UIElementDataHashMap& rUserElements = m_aUIElements[nElementType].aElementsHashMap;
261 for (auto const& elem : rUserElements)
263 UIElementData* pDataSettings = impl_findUIElementData( elem.second.aResourceURL, nElementType );
264 if ( pDataSettings && !pDataSettings->bDefault )
266 // Retrieve user interface name from XPropertySet interface
267 OUString aUIName;
268 Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY );
269 if ( xPropSet.is() )
271 Any a = xPropSet->getPropertyValue( m_aPropUIName );
272 a >>= aUIName;
275 UIElementInfo aInfo( elem.second.aResourceURL, aUIName );
276 aUIElementInfoCollection.emplace( elem.second.aResourceURL, aInfo );
281 void UIConfigurationManager::impl_preloadUIElementTypeList( sal_Int16 nElementType )
283 UIElementType& rElementTypeData = m_aUIElements[nElementType];
285 if ( !rElementTypeData.bLoaded )
287 Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
288 if ( xElementTypeStorage.is() )
290 OUString aResURLPrefix =
291 OUString::Concat(RESOURCEURL_PREFIX) +
292 UIELEMENTTYPENAMES[ nElementType ] +
293 "/";
295 UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap;
296 const Sequence< OUString > aUIElementNames = xElementTypeStorage->getElementNames();
297 for ( OUString const & rElementName : aUIElementNames )
299 UIElementData aUIElementData;
301 // Resource name must be without ".xml"
302 sal_Int32 nIndex = rElementName.lastIndexOf( '.' );
303 if (( nIndex > 0 ) && ( nIndex < rElementName.getLength() ))
305 std::u16string_view aExtension( rElementName.subView( nIndex+1 ));
306 std::u16string_view aUIElementName( rElementName.subView( 0, nIndex ));
308 if (!aUIElementName.empty() &&
309 ( o3tl::equalsIgnoreAsciiCase(aExtension, u"xml")))
311 aUIElementData.aResourceURL = aResURLPrefix + aUIElementName;
312 aUIElementData.aName = rElementName;
313 aUIElementData.bModified = false;
314 aUIElementData.bDefault = false;
316 // Create unordered_map entries for all user interface elements inside the storage. We don't load the
317 // settings to speed up the process.
318 rHashMap.emplace( aUIElementData.aResourceURL, aUIElementData );
325 rElementTypeData.bLoaded = true;
328 void UIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData )
330 UIElementType& rElementTypeData = m_aUIElements[nElementType];
332 Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
333 if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() )
337 Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ );
338 Reference< XInputStream > xInputStream = xStream->getInputStream();
340 if ( xInputStream.is() )
342 switch ( nElementType )
344 case css::ui::UIElementType::UNKNOWN:
345 break;
347 case css::ui::UIElementType::MENUBAR:
348 case css::ui::UIElementType::POPUPMENU:
352 MenuConfiguration aMenuCfg( m_xContext );
353 Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream ));
354 auto pRootItemContainer = dynamic_cast<RootItemContainer*>( xContainer.get() );
355 if ( pRootItemContainer )
356 aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
357 else
358 aUIElementData.xSettings = new ConstItemContainer( xContainer, true );
359 return;
361 catch ( const css::lang::WrappedTargetException& )
365 break;
367 case css::ui::UIElementType::TOOLBAR:
371 Reference< XIndexContainer > xIndexContainer( new RootItemContainer() );
372 ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer );
373 auto pRootItemContainer = dynamic_cast<RootItemContainer*>( xIndexContainer.get() );
374 aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
375 return;
377 catch ( const css::lang::WrappedTargetException& )
381 break;
384 case css::ui::UIElementType::STATUSBAR:
388 Reference< XIndexContainer > xIndexContainer( new RootItemContainer() );
389 StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer );
390 auto pRootItemContainer = dynamic_cast<RootItemContainer*>( xIndexContainer.get() );
391 aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
392 return;
394 catch ( const css::lang::WrappedTargetException& )
398 break;
401 case css::ui::UIElementType::FLOATINGWINDOW:
403 break;
408 catch ( const css::embed::InvalidStorageException& )
411 catch ( const css::lang::IllegalArgumentException& )
414 catch ( const css::io::IOException& )
417 catch ( const css::embed::StorageWrappedTargetException& )
422 // At least we provide an empty settings container!
423 aUIElementData.xSettings = new ConstItemContainer();
426 UIConfigurationManager::UIElementData* UIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad )
428 // preload list of element types on demand
429 impl_preloadUIElementTypeList( nElementType );
431 // try to look into our document vector/unordered_map combination
432 UIElementDataHashMap& rUserHashMap = m_aUIElements[nElementType].aElementsHashMap;
433 UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL );
434 if ( pIter != rUserHashMap.end() )
436 // Default data settings data means removed!
437 if ( pIter->second.bDefault )
438 return &(pIter->second);
439 else
441 if ( !pIter->second.xSettings.is() && bLoad )
442 impl_requestUIElementData( nElementType, pIter->second );
443 return &(pIter->second);
447 // Nothing has been found!
448 return nullptr;
451 void UIConfigurationManager::impl_storeElementTypeData( Reference< XStorage > const & xStorage, UIElementType& rElementType, bool bResetModifyState )
453 UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap;
455 for (auto & elem : rHashMap)
457 UIElementData& rElement = elem.second;
458 if ( rElement.bModified )
460 if ( rElement.bDefault )
462 xStorage->removeElement( rElement.aName );
463 rElement.bModified = false; // mark as not modified
465 else
467 Reference< XStream > xStream = xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE );
468 Reference< XOutputStream > xOutputStream( xStream->getOutputStream() );
470 if ( xOutputStream.is() )
472 switch( rElementType.nElementType )
474 case css::ui::UIElementType::MENUBAR:
475 case css::ui::UIElementType::POPUPMENU:
479 MenuConfiguration aMenuCfg( m_xContext );
480 aMenuCfg.StoreMenuBarConfigurationToXML(
481 rElement.xSettings, xOutputStream, rElementType.nElementType == css::ui::UIElementType::MENUBAR );
483 catch ( const css::lang::WrappedTargetException& )
487 break;
489 case css::ui::UIElementType::TOOLBAR:
493 ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings );
495 catch ( const css::lang::WrappedTargetException& )
499 break;
501 case css::ui::UIElementType::STATUSBAR:
505 StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings );
507 catch ( const css::lang::WrappedTargetException& )
511 break;
513 default:
514 break;
518 // mark as not modified if we store to our own storage
519 if ( bResetModifyState )
520 rElement.bModified = false;
525 // commit element type storage
526 Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY );
527 if ( xTransactedObject.is() )
528 xTransactedObject->commit();
530 // mark UIElementType as not modified if we store to our own storage
531 if ( bResetModifyState )
532 rElementType.bModified = false;
535 void UIConfigurationManager::impl_resetElementTypeData(
536 UIElementType& rDocElementType,
537 ConfigEventNotifyContainer& rRemoveNotifyContainer )
539 UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap;
541 Reference< XUIConfigurationManager > xThis(this);
542 Reference< XInterface > xIfac( xThis, UNO_QUERY );
544 // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling
545 // our listeners!
546 for (auto & elem : rHashMap)
548 UIElementData& rElement = elem.second;
549 if ( !rElement.bDefault )
551 // Remove user-defined settings from document
552 ConfigurationEvent aEvent;
553 aEvent.ResourceURL = rElement.aResourceURL;
554 aEvent.Accessor <<= xThis;
555 aEvent.Source = xIfac;
556 aEvent.Element <<= rElement.xSettings;
558 rRemoveNotifyContainer.push_back( aEvent );
560 // Mark element as default.
561 rElement.bModified = false;
562 rElement.bDefault = true;
564 else
565 rElement.bModified = false;
568 // Remove all settings from our user interface elements
569 rHashMap.clear();
572 void UIConfigurationManager::impl_reloadElementTypeData(
573 UIElementType& rDocElementType,
574 ConfigEventNotifyContainer& rRemoveNotifyContainer,
575 ConfigEventNotifyContainer& rReplaceNotifyContainer )
577 UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap;
578 Reference< XStorage > xElementStorage( rDocElementType.xStorage );
580 Reference< XUIConfigurationManager > xThis(this);
581 Reference< XInterface > xIfac( xThis, UNO_QUERY );
582 sal_Int16 nType = rDocElementType.nElementType;
584 for (auto & elem : rHashMap)
586 UIElementData& rElement = elem.second;
587 if ( rElement.bModified )
589 if ( xElementStorage->hasByName( rElement.aName ))
591 // Replace settings with data from user layer
592 Reference< XIndexAccess > xOldSettings( rElement.xSettings );
594 impl_requestUIElementData( nType, rElement );
596 ConfigurationEvent aReplaceEvent;
598 aReplaceEvent.ResourceURL = rElement.aResourceURL;
599 aReplaceEvent.Accessor <<= xThis;
600 aReplaceEvent.Source = xIfac;
601 aReplaceEvent.ReplacedElement <<= xOldSettings;
602 aReplaceEvent.Element <<= rElement.xSettings;
603 rReplaceNotifyContainer.push_back( aReplaceEvent );
605 rElement.bModified = false;
607 else
609 // Element settings are not in any storage => remove
610 ConfigurationEvent aRemoveEvent;
612 aRemoveEvent.ResourceURL = rElement.aResourceURL;
613 aRemoveEvent.Accessor <<= xThis;
614 aRemoveEvent.Source = xIfac;
615 aRemoveEvent.Element <<= rElement.xSettings;
617 rRemoveNotifyContainer.push_back( aRemoveEvent );
619 // Mark element as default and not modified. That means "not active" in the document anymore
620 rElement.bModified = false;
621 rElement.bDefault = true;
626 rDocElementType.bModified = false;
629 void UIConfigurationManager::impl_Initialize()
631 // Initialize the top-level structures with the storage data
632 if ( m_xDocConfigStorage.is() )
634 tools::Long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE;
636 // Try to access our module sub folder
637 for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT;
638 i++ )
640 Reference< XStorage > xElementTypeStorage;
643 xElementTypeStorage = m_xDocConfigStorage->openStorageElement( OUString(UIELEMENTTYPENAMES[i]), nModes );
645 catch ( const css::container::NoSuchElementException& )
648 catch ( const css::embed::InvalidStorageException& )
651 catch ( const css::lang::IllegalArgumentException& )
654 catch ( const css::io::IOException& )
657 catch ( const css::embed::StorageWrappedTargetException& )
661 m_aUIElements[i].nElementType = i;
662 m_aUIElements[i].bModified = false;
663 m_aUIElements[i].xStorage = xElementTypeStorage;
666 else
668 // We have no storage, just initialize ui element types with empty storage!
669 for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
670 m_aUIElements[i].xStorage = m_xDocConfigStorage;
674 UIConfigurationManager::UIConfigurationManager( css::uno::Reference< css::uno::XComponentContext > xContext ) :
675 m_bReadOnly( true )
676 , m_bModified( false )
677 , m_bDisposed( false )
678 , m_aPropUIName( "UIName" )
679 , m_xContext(std::move( xContext ))
681 // Make sure we have a default initialized entry for every layer and user interface element type!
682 // The following code depends on this!
683 m_aUIElements.resize( css::ui::UIElementType::COUNT );
686 // XComponent
687 void SAL_CALL UIConfigurationManager::dispose()
689 Reference< XComponent > xThis(this);
691 css::lang::EventObject aEvent( xThis );
693 std::unique_lock aGuard(m_mutex);
694 m_aEventListeners.disposeAndClear( aGuard, aEvent );
697 std::unique_lock aGuard(m_mutex);
698 m_aConfigListeners.disposeAndClear( aGuard, aEvent );
702 SolarMutexGuard g;
705 if ( m_xImageManager.is() )
706 m_xImageManager->dispose();
708 catch ( const Exception& )
712 m_xImageManager.clear();
713 m_aUIElements.clear();
714 m_xDocConfigStorage.clear();
715 m_bModified = false;
716 m_bDisposed = true;
720 void SAL_CALL UIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener )
723 SolarMutexGuard g;
725 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
726 if ( m_bDisposed )
727 throw DisposedException();
730 std::unique_lock aGuard(m_mutex);
731 m_aEventListeners.addInterface( aGuard, xListener );
734 void SAL_CALL UIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener )
736 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
737 std::unique_lock aGuard(m_mutex);
738 m_aEventListeners.removeInterface( aGuard, xListener );
741 // XUIConfigurationManager
742 void SAL_CALL UIConfigurationManager::addConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener )
745 SolarMutexGuard g;
747 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
748 if ( m_bDisposed )
749 throw DisposedException();
752 std::unique_lock aGuard(m_mutex);
753 m_aConfigListeners.addInterface( aGuard, xListener );
756 void SAL_CALL UIConfigurationManager::removeConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener )
758 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
759 std::unique_lock aGuard(m_mutex);
760 m_aConfigListeners.removeInterface( aGuard, xListener );
763 void SAL_CALL UIConfigurationManager::reset()
765 SolarMutexClearableGuard aGuard;
767 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
768 if ( m_bDisposed )
769 throw DisposedException();
771 if ( isReadOnly() )
772 return;
774 if ( !m_xDocConfigStorage.is() )
775 return;
779 // Remove all elements from our user-defined storage!
780 bool bCommit( false );
781 for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
783 UIElementType& rElementType = m_aUIElements[i];
785 if ( rElementType.xStorage.is() )
787 bool bCommitSubStorage( false );
788 const Sequence< OUString > aUIElementStreamNames = rElementType.xStorage->getElementNames();
789 for ( OUString const & rStreamName : aUIElementStreamNames )
791 rElementType.xStorage->removeElement( rStreamName );
792 bCommitSubStorage = true;
793 bCommit = true;
796 if ( bCommitSubStorage )
798 Reference< XTransactedObject > xTransactedObject( rElementType.xStorage, UNO_QUERY );
799 if ( xTransactedObject.is() )
800 xTransactedObject->commit();
805 // Commit changes
806 if ( bCommit )
808 Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY );
809 if ( xTransactedObject.is() )
810 xTransactedObject->commit();
813 // remove settings from user defined layer and notify listener about removed settings data!
814 // Try to access our module sub folder
815 ConfigEventNotifyContainer aRemoveEventNotifyContainer;
816 for ( sal_Int16 j = 1; j < css::ui::UIElementType::COUNT; j++ )
818 UIElementType& rDocElementType = m_aUIElements[j];
820 impl_resetElementTypeData( rDocElementType, aRemoveEventNotifyContainer );
821 rDocElementType.bModified = false;
824 m_bModified = false;
826 // Unlock mutex before notify our listeners
827 aGuard.clear();
829 // Notify our listeners
830 for (const ConfigurationEvent & k : aRemoveEventNotifyContainer)
831 implts_notifyContainerListener( k, NotifyOp_Remove );
833 catch ( const css::lang::IllegalArgumentException& )
836 catch ( const css::container::NoSuchElementException& )
839 catch ( const css::embed::InvalidStorageException& )
842 catch ( const css::embed::StorageWrappedTargetException& )
847 Sequence< Sequence< PropertyValue > > SAL_CALL UIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType )
849 if (( ElementType < 0 ) || ( ElementType >= css::ui::UIElementType::COUNT ))
850 throw IllegalArgumentException();
852 SolarMutexGuard g;
853 if ( m_bDisposed )
854 throw DisposedException();
856 std::vector< Sequence< PropertyValue > > aElementInfoSeq;
857 UIElementInfoHashMap aUIElementInfoCollection;
859 if ( ElementType == css::ui::UIElementType::UNKNOWN )
861 for ( sal_Int16 i = 0; i < css::ui::UIElementType::COUNT; i++ )
862 impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, i );
864 else
865 impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType );
867 aElementInfoSeq.resize( aUIElementInfoCollection.size() );
868 sal_Int32 n = 0;
869 for (auto const& elem : aUIElementInfoCollection)
871 Sequence< PropertyValue > aUIElementInfo{
872 comphelper::makePropertyValue("ResourceURL", elem.second.aResourceURL),
873 comphelper::makePropertyValue(m_aPropUIName, elem.second.aUIName)
875 aElementInfoSeq[n++] = aUIElementInfo;
878 return comphelper::containerToSequence(aElementInfoSeq);
881 Reference< XIndexContainer > SAL_CALL UIConfigurationManager::createSettings()
883 SolarMutexGuard g;
885 if ( m_bDisposed )
886 throw DisposedException();
888 // Creates an empty item container which can be filled from outside
889 return Reference< XIndexContainer >( new RootItemContainer() );
892 sal_Bool SAL_CALL UIConfigurationManager::hasSettings( const OUString& ResourceURL )
894 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
896 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
897 ( nElementType >= css::ui::UIElementType::COUNT ))
898 throw IllegalArgumentException();
899 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false );
900 if ( pDataSettings && !pDataSettings->bDefault )
901 return true;
903 return false;
906 Reference< XIndexAccess > SAL_CALL UIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable )
908 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
910 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
911 ( nElementType >= css::ui::UIElementType::COUNT ))
912 throw IllegalArgumentException();
914 SolarMutexGuard g;
916 if ( m_bDisposed )
917 throw DisposedException();
919 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
920 if ( pDataSettings && !pDataSettings->bDefault )
922 // Create a copy of our data if someone wants to change the data.
923 if ( bWriteable )
924 return Reference< XIndexAccess >( new RootItemContainer( pDataSettings->xSettings ) );
925 else
926 return pDataSettings->xSettings;
929 throw NoSuchElementException();
932 void SAL_CALL UIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< css::container::XIndexAccess >& aNewData )
934 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
936 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
937 ( nElementType >= css::ui::UIElementType::COUNT ))
938 throw IllegalArgumentException();
939 else if ( m_bReadOnly )
940 throw IllegalAccessException();
941 else
943 SolarMutexClearableGuard aGuard;
945 if ( m_bDisposed )
946 throw DisposedException();
948 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
949 if ( !pDataSettings || pDataSettings->bDefault )
950 throw NoSuchElementException();
951 // we have a settings entry in our user-defined layer - replace
952 Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings;
954 // Create a copy of the data if the container is not const
955 Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
956 if ( xReplace.is() )
957 pDataSettings->xSettings = new ConstItemContainer( aNewData );
958 else
959 pDataSettings->xSettings = aNewData;
961 pDataSettings->bDefault = false;
962 pDataSettings->bModified = true;
963 m_bModified = true;
965 // Modify type container
966 UIElementType& rElementType = m_aUIElements[nElementType];
967 rElementType.bModified = true;
969 Reference< XUIConfigurationManager > xThis(this);
970 Reference< XInterface > xIfac( xThis, UNO_QUERY );
972 // Create event to notify listener about replaced element settings
973 ConfigurationEvent aEvent;
975 aEvent.ResourceURL = ResourceURL;
976 aEvent.Accessor <<= xThis;
977 aEvent.Source = xIfac;
978 aEvent.ReplacedElement <<= xOldSettings;
979 aEvent.Element <<= pDataSettings->xSettings;
981 aGuard.clear();
983 implts_notifyContainerListener( aEvent, NotifyOp_Replace );
987 void SAL_CALL UIConfigurationManager::removeSettings( const OUString& ResourceURL )
989 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
991 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
992 ( nElementType >= css::ui::UIElementType::COUNT ))
993 throw IllegalArgumentException( "The ResourceURL is not valid or "
994 "describes an unknown type. "
995 "ResourceURL: " + ResourceURL, nullptr, 0 );
996 else if ( m_bReadOnly )
997 throw IllegalAccessException( "The configuration manager is read-only. "
998 "ResourceURL: " + ResourceURL, nullptr );
999 else
1001 SolarMutexClearableGuard aGuard;
1003 if ( m_bDisposed )
1004 throw DisposedException( "The configuration manager has been disposed, "
1005 "and can't uphold its method specification anymore. "
1006 "ResourceURL: " + ResourceURL, nullptr );
1008 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
1009 if ( !pDataSettings )
1010 throw NoSuchElementException( "The settings data cannot be found. "
1011 "ResourceURL: " + ResourceURL, nullptr);
1012 // If element settings are default, we don't need to change anything!
1013 if ( pDataSettings->bDefault )
1014 return;
1015 else
1017 Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings;
1018 pDataSettings->bDefault = true;
1020 // check if this is a default layer node
1021 pDataSettings->bModified = true; // we have to remove this node from the user layer!
1022 pDataSettings->xSettings.clear();
1023 m_bModified = true; // user layer must be written
1025 // Modify type container
1026 UIElementType& rElementType = m_aUIElements[nElementType];
1027 rElementType.bModified = true;
1029 Reference< XUIConfigurationManager > xThis(this);
1031 // Create event to notify listener about removed element settings
1032 ConfigurationEvent aEvent;
1034 aEvent.ResourceURL = ResourceURL;
1035 aEvent.Accessor <<= xThis;
1036 aEvent.Source.set(xThis, UNO_QUERY);
1038 aEvent.Element <<= xRemovedSettings;
1040 aGuard.clear();
1042 implts_notifyContainerListener( aEvent, NotifyOp_Remove );
1047 void SAL_CALL UIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData )
1049 sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL );
1051 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
1052 ( nElementType >= css::ui::UIElementType::COUNT ))
1053 throw IllegalArgumentException();
1054 else if ( m_bReadOnly )
1055 throw IllegalAccessException();
1056 else
1058 SolarMutexClearableGuard aGuard;
1060 if ( m_bDisposed )
1061 throw DisposedException();
1063 bool bInsertData( false );
1064 UIElementData aUIElementData;
1065 UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType );
1067 if ( pDataSettings && !pDataSettings->bDefault )
1068 throw ElementExistException();
1070 if ( !pDataSettings )
1072 pDataSettings = &aUIElementData;
1073 bInsertData = true;
1077 pDataSettings->bDefault = false;
1078 pDataSettings->bModified = true;
1080 // Create a copy of the data if the container is not const
1081 Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
1082 if ( xReplace.is() )
1083 pDataSettings->xSettings = new ConstItemContainer( aNewData );
1084 else
1085 pDataSettings->xSettings = aNewData;
1087 m_bModified = true;
1089 UIElementType& rElementType = m_aUIElements[nElementType];
1090 rElementType.bModified = true;
1092 if ( bInsertData )
1094 pDataSettings->aName = RetrieveNameFromResourceURL( NewResourceURL ) + ".xml";
1095 pDataSettings->aResourceURL = NewResourceURL;
1097 UIElementDataHashMap& rElements = rElementType.aElementsHashMap;
1098 rElements.emplace( NewResourceURL, *pDataSettings );
1101 Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings );
1102 Reference< XUIConfigurationManager > xThis(this);
1103 Reference< XInterface > xIfac( xThis, UNO_QUERY );
1105 // Create event to notify listener about removed element settings
1106 ConfigurationEvent aEvent;
1108 aEvent.ResourceURL = NewResourceURL;
1109 aEvent.Accessor <<= xThis;
1110 aEvent.Source = xIfac;
1111 aEvent.Element <<= xInsertSettings;
1113 aGuard.clear();
1115 implts_notifyContainerListener( aEvent, NotifyOp_Insert );
1120 Reference< XInterface > SAL_CALL UIConfigurationManager::getImageManager()
1122 if ( m_bDisposed )
1123 throw DisposedException();
1125 if ( !m_xImageManager.is() )
1127 m_xImageManager = new ImageManager( m_xContext, /*bForModule*/false );
1129 Sequence<Any> aPropSeq(comphelper::InitAnyPropertySequence(
1131 {"UserConfigStorage", Any(m_xDocConfigStorage)},
1132 {"ModuleIdentifier", Any(OUString())},
1133 }));
1135 m_xImageManager->initialize( aPropSeq );
1138 return Reference< XInterface >( static_cast<cppu::OWeakObject*>(m_xImageManager.get()), UNO_QUERY );
1141 Reference< XAcceleratorConfiguration > SAL_CALL UIConfigurationManager::createShortCutManager()
1143 return DocumentAcceleratorConfiguration::createWithDocumentRoot(m_xContext, m_xDocConfigStorage);
1146 Reference< XAcceleratorConfiguration > SAL_CALL UIConfigurationManager::getShortCutManager()
1148 // SAFE ->
1149 SolarMutexGuard g;
1151 if (!m_xAccConfig.is()) try
1153 m_xAccConfig = DocumentAcceleratorConfiguration::
1154 createWithDocumentRoot(m_xContext, m_xDocConfigStorage);
1156 catch ( const css::uno::DeploymentException& )
1158 SAL_WARN("fwk.uiconfiguration", "DocumentAcceleratorConfiguration"
1159 " not available. This should happen only on mobile platforms.");
1162 return m_xAccConfig;
1165 Reference< XInterface > SAL_CALL UIConfigurationManager::getEventsManager()
1167 return Reference< XInterface >();
1170 // XUIConfigurationStorage
1171 void SAL_CALL UIConfigurationManager::setStorage( const Reference< XStorage >& Storage )
1173 SolarMutexGuard g;
1175 if ( m_bDisposed )
1176 throw DisposedException();
1178 if ( m_xDocConfigStorage.is() )
1182 // Dispose old storage to be sure that it will be closed
1183 m_xDocConfigStorage->dispose();
1185 catch ( const Exception& )
1190 // We store the new storage. Be careful it could be an empty reference!
1191 m_xDocConfigStorage = Storage;
1192 m_bReadOnly = true;
1194 if ( m_xAccConfig.is() )
1195 m_xAccConfig->setStorage( m_xDocConfigStorage );
1197 if ( m_xImageManager )
1198 m_xImageManager->setStorage( m_xDocConfigStorage );
1200 if ( m_xDocConfigStorage.is() )
1202 Reference< XPropertySet > xPropSet( m_xDocConfigStorage, UNO_QUERY );
1203 if ( xPropSet.is() )
1207 tools::Long nOpenMode = 0;
1208 Any a = xPropSet->getPropertyValue("OpenMode");
1209 if ( a >>= nOpenMode )
1210 m_bReadOnly = !( nOpenMode & ElementModes::WRITE );
1212 catch ( const css::beans::UnknownPropertyException& )
1215 catch ( const css::lang::WrappedTargetException& )
1221 impl_Initialize();
1224 sal_Bool SAL_CALL UIConfigurationManager::hasStorage()
1226 SolarMutexGuard g;
1228 if ( m_bDisposed )
1229 throw DisposedException();
1231 return m_xDocConfigStorage.is();
1234 // XUIConfigurationPersistence
1235 void SAL_CALL UIConfigurationManager::reload()
1237 SolarMutexClearableGuard aGuard;
1239 if ( m_bDisposed )
1240 throw DisposedException();
1242 if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly )
1243 return;
1245 // Try to access our module sub folder
1246 ConfigEventNotifyContainer aRemoveNotifyContainer;
1247 ConfigEventNotifyContainer aReplaceNotifyContainer;
1248 for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; i++ )
1252 UIElementType& rDocElementType = m_aUIElements[i];
1253 if ( rDocElementType.bModified )
1254 impl_reloadElementTypeData( rDocElementType, aRemoveNotifyContainer, aReplaceNotifyContainer );
1256 catch ( const Exception& )
1258 throw IOException();
1262 m_bModified = false;
1264 // Unlock mutex before notify our listeners
1265 aGuard.clear();
1267 // Notify our listeners
1268 for (const ConfigurationEvent & j : aRemoveNotifyContainer)
1269 implts_notifyContainerListener( j, NotifyOp_Remove );
1270 for (const ConfigurationEvent & k : aReplaceNotifyContainer)
1271 implts_notifyContainerListener( k, NotifyOp_Replace );
1274 void SAL_CALL UIConfigurationManager::store()
1276 SolarMutexGuard g;
1278 if ( m_bDisposed )
1279 throw DisposedException();
1281 if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly )
1282 return;
1284 // Try to access our module sub folder
1285 for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
1289 UIElementType& rElementType = m_aUIElements[i];
1291 if ( rElementType.bModified && rElementType.xStorage.is() )
1292 impl_storeElementTypeData( rElementType.xStorage, rElementType );
1294 catch ( const Exception& )
1296 throw IOException();
1300 m_bModified = false;
1301 Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY );
1302 if ( xTransactedObject.is() )
1303 xTransactedObject->commit();
1306 void SAL_CALL UIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage )
1308 SolarMutexGuard g;
1310 if ( m_bDisposed )
1311 throw DisposedException();
1313 if ( !m_xDocConfigStorage.is() || !m_bModified || m_bReadOnly )
1314 return;
1316 // Try to access our module sub folder
1317 for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
1321 Reference< XStorage > xElementTypeStorage( Storage->openStorageElement(
1322 OUString(UIELEMENTTYPENAMES[i]), ElementModes::READWRITE ));
1323 UIElementType& rElementType = m_aUIElements[i];
1325 if ( rElementType.bModified && xElementTypeStorage.is() )
1326 impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag!
1328 catch ( const Exception& )
1330 throw IOException();
1334 Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY );
1335 if ( xTransactedObject.is() )
1336 xTransactedObject->commit();
1339 sal_Bool SAL_CALL UIConfigurationManager::isModified()
1341 SolarMutexGuard g;
1343 return m_bModified;
1346 sal_Bool SAL_CALL UIConfigurationManager::isReadOnly()
1348 SolarMutexGuard g;
1350 return m_bReadOnly;
1353 void UIConfigurationManager::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp )
1355 std::unique_lock aGuard(m_mutex);
1356 m_aConfigListeners.forEach(aGuard, [&eOp, &aEvent](const css::uno::Reference<XUIConfigurationListener>& l) {
1357 switch ( eOp )
1359 case NotifyOp_Replace:
1360 l->elementReplaced( aEvent );
1361 break;
1362 case NotifyOp_Insert:
1363 l->elementInserted( aEvent );
1364 break;
1365 case NotifyOp_Remove:
1366 l->elementRemoved( aEvent );
1367 break;
1374 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
1375 com_sun_star_comp_framework_UIConfigurationManager_get_implementation(
1376 css::uno::XComponentContext *context,
1377 css::uno::Sequence<css::uno::Any> const &)
1379 return cppu::acquire(new UIConfigurationManager(context));
1382 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */