1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
55 #include <vcl/svapp.hxx>
56 #include <sal/log.hxx>
57 #include <o3tl/string_view.hxx>
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
;
74 class UIConfigurationManager
: public ::cppu::WeakImplHelper
<
75 css::lang::XServiceInfo
,
76 css::ui::XUIConfigurationManager2
>
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
);
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
;
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
;
131 // private data types
141 UIElementInfo( OUString _aResourceURL
, OUString _aUIName
) :
142 aResourceURL(std::move( _aResourceURL
)), aUIName(std::move( _aUIName
)) {}
143 OUString aResourceURL
;
149 UIElementData() : bModified( false ), bDefault( true ) {};
151 OUString aResourceURL
;
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
;
164 UIElementType() : bModified( false ),
166 nElementType( css::ui::UIElementType::UNKNOWN
) {}
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
;
194 OUString m_aPropUIName
;
195 css::uno::Reference
< css::uno::XComponentContext
> m_xContext
;
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 ));
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
268 Reference
< XPropertySet
> xPropSet( pDataSettings
->xSettings
, UNO_QUERY
);
271 Any a
= xPropSet
->getPropertyValue( m_aPropUIName
);
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
] +
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
:
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 );
358 aUIElementData
.xSettings
= new ConstItemContainer( xContainer
, true );
361 catch ( const css::lang::WrappedTargetException
& )
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 );
377 catch ( const css::lang::WrappedTargetException
& )
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 );
394 catch ( const css::lang::WrappedTargetException
& )
401 case css::ui::UIElementType::FLOATINGWINDOW
:
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
);
441 if ( !pIter
->second
.xSettings
.is() && bLoad
)
442 impl_requestUIElementData( nElementType
, pIter
->second
);
443 return &(pIter
->second
);
447 // Nothing has been found!
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
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
& )
489 case css::ui::UIElementType::TOOLBAR
:
493 ToolBoxConfiguration::StoreToolBox( m_xContext
, xOutputStream
, rElement
.xSettings
);
495 catch ( const css::lang::WrappedTargetException
& )
501 case css::ui::UIElementType::STATUSBAR
:
505 StatusBarConfiguration::StoreStatusBar( m_xContext
, xOutputStream
, rElement
.xSettings
);
507 catch ( const css::lang::WrappedTargetException
& )
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
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;
565 rElement
.bModified
= false;
568 // Remove all settings from our user interface elements
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;
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
;
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
;
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
) :
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
);
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
);
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();
720 void SAL_CALL
UIConfigurationManager::addEventListener( const Reference
< XEventListener
>& xListener
)
725 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
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
)
747 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
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 ----------------------------------------------------------------------------------------------- */
769 throw DisposedException();
774 if ( !m_xDocConfigStorage
.is() )
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;
796 if ( bCommitSubStorage
)
798 Reference
< XTransactedObject
> xTransactedObject( rElementType
.xStorage
, UNO_QUERY
);
799 if ( xTransactedObject
.is() )
800 xTransactedObject
->commit();
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;
826 // Unlock mutex before notify our listeners
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();
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
);
865 impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection
, ElementType
);
867 aElementInfoSeq
.resize( aUIElementInfoCollection
.size() );
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()
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
)
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();
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.
924 return Reference
< XIndexAccess
>( new RootItemContainer( pDataSettings
->xSettings
) );
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();
943 SolarMutexClearableGuard aGuard
;
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
);
957 pDataSettings
->xSettings
= new ConstItemContainer( aNewData
);
959 pDataSettings
->xSettings
= aNewData
;
961 pDataSettings
->bDefault
= false;
962 pDataSettings
->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
;
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 );
1001 SolarMutexClearableGuard aGuard
;
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
)
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
;
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();
1058 SolarMutexClearableGuard aGuard
;
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
;
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
);
1085 pDataSettings
->xSettings
= aNewData
;
1089 UIElementType
& rElementType
= m_aUIElements
[nElementType
];
1090 rElementType
.bModified
= true;
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
;
1115 implts_notifyContainerListener( aEvent
, NotifyOp_Insert
);
1120 Reference
< XInterface
> SAL_CALL
UIConfigurationManager::getImageManager()
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())},
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()
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
)
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
;
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
& )
1224 sal_Bool SAL_CALL
UIConfigurationManager::hasStorage()
1229 throw DisposedException();
1231 return m_xDocConfigStorage
.is();
1234 // XUIConfigurationPersistence
1235 void SAL_CALL
UIConfigurationManager::reload()
1237 SolarMutexClearableGuard aGuard
;
1240 throw DisposedException();
1242 if ( !m_xDocConfigStorage
.is() || !m_bModified
|| m_bReadOnly
)
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
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()
1279 throw DisposedException();
1281 if ( !m_xDocConfigStorage
.is() || !m_bModified
|| m_bReadOnly
)
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
)
1311 throw DisposedException();
1313 if ( !m_xDocConfigStorage
.is() || !m_bModified
|| m_bReadOnly
)
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()
1346 sal_Bool SAL_CALL
UIConfigurationManager::isReadOnly()
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
) {
1359 case NotifyOp_Replace
:
1360 l
->elementReplaced( aEvent
);
1362 case NotifyOp_Insert
:
1363 l
->elementInserted( aEvent
);
1365 case NotifyOp_Remove
:
1366 l
->elementRemoved( aEvent
);
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: */