LibreOffice Module framework (master) 1
moduleuiconfigurationmanager.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
25#include <menuconfiguration.hxx>
27
29
30#include <com/sun/star/ui/UIElementType.hpp>
31#include <com/sun/star/ui/ConfigurationEvent.hpp>
32#include <com/sun/star/ui/ModuleAcceleratorConfiguration.hpp>
33#include <com/sun/star/ui/XModuleUIConfigurationManager2.hpp>
34#include <com/sun/star/lang/DisposedException.hpp>
35#include <com/sun/star/lang/IllegalAccessException.hpp>
36#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp>
37#include <com/sun/star/beans/XPropertySet.hpp>
38#include <com/sun/star/embed/ElementModes.hpp>
39#include <com/sun/star/embed/InvalidStorageException.hpp>
40#include <com/sun/star/embed/StorageWrappedTargetException.hpp>
41#include <com/sun/star/embed/XTransactedObject.hpp>
42#include <com/sun/star/container/ElementExistException.hpp>
43#include <com/sun/star/container/XNameAccess.hpp>
44#include <com/sun/star/container/XIndexContainer.hpp>
45#include <com/sun/star/io/IOException.hpp>
46#include <com/sun/star/io/XStream.hpp>
47#include <com/sun/star/lang/XServiceInfo.hpp>
48#include <com/sun/star/lang/XComponent.hpp>
49
55#include <utility>
56#include <vcl/svapp.hxx>
57#include <sal/log.hxx>
62#include <o3tl/string_view.hxx>
63#include <memory>
64#include <mutex>
65#include <string_view>
66
67using namespace css;
68using namespace com::sun::star::uno;
69using namespace com::sun::star::io;
70using namespace com::sun::star::embed;
71using namespace com::sun::star::lang;
72using namespace com::sun::star::container;
73using namespace com::sun::star::beans;
74using namespace framework;
75
76constexpr OUStringLiteral RESOURCETYPE_MENUBAR = u"menubar";
77constexpr OUStringLiteral RESOURCETYPE_TOOLBAR = u"toolbar";
78constexpr OUStringLiteral RESOURCETYPE_STATUSBAR = u"statusbar";
79constexpr OUStringLiteral RESOURCETYPE_POPUPMENU = u"popupmenu";
80
81namespace {
82
83class ModuleUIConfigurationManager : public cppu::WeakImplHelper<
84 css::lang::XServiceInfo,
85 css::lang::XComponent,
86 css::ui::XModuleUIConfigurationManager2 >
87{
88public:
89 ModuleUIConfigurationManager(
90 const css::uno::Reference< css::uno::XComponentContext >& xServiceManager,
91 const css::uno::Sequence< css::uno::Any >& aArguments);
92
93 virtual OUString SAL_CALL getImplementationName() override
94 {
95 return "com.sun.star.comp.framework.ModuleUIConfigurationManager";
96 }
97
98 virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override
99 {
101 }
102
103 virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override
104 {
105 return {"com.sun.star.ui.ModuleUIConfigurationManager"};
106 }
107
108 // XComponent
109 virtual void SAL_CALL dispose() override;
110 virtual void SAL_CALL addEventListener( const css::uno::Reference< css::lang::XEventListener >& xListener ) override;
111 virtual void SAL_CALL removeEventListener( const css::uno::Reference< css::lang::XEventListener >& aListener ) override;
112
113 // XUIConfiguration
114 virtual void SAL_CALL addConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override;
115 virtual void SAL_CALL removeConfigurationListener( const css::uno::Reference< css::ui::XUIConfigurationListener >& Listener ) override;
116
117 // XUIConfigurationManager
118 virtual void SAL_CALL reset() override;
119 virtual css::uno::Sequence< css::uno::Sequence< css::beans::PropertyValue > > SAL_CALL getUIElementsInfo( sal_Int16 ElementType ) override;
120 virtual css::uno::Reference< css::container::XIndexContainer > SAL_CALL createSettings( ) override;
121 virtual sal_Bool SAL_CALL hasSettings( const OUString& ResourceURL ) override;
122 virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getSettings( const OUString& ResourceURL, sal_Bool bWriteable ) override;
123 virtual void SAL_CALL replaceSettings( const OUString& ResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override;
124 virtual void SAL_CALL removeSettings( const OUString& ResourceURL ) override;
125 virtual void SAL_CALL insertSettings( const OUString& NewResourceURL, const css::uno::Reference< css::container::XIndexAccess >& aNewData ) override;
126 virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getImageManager() override;
127 virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL getShortCutManager() override;
128 virtual css::uno::Reference< css::ui::XAcceleratorConfiguration > SAL_CALL createShortCutManager() override;
129 virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getEventsManager() override;
130
131 // XModuleUIConfigurationManager
132 virtual sal_Bool SAL_CALL isDefaultSettings( const OUString& ResourceURL ) override;
133 virtual css::uno::Reference< css::container::XIndexAccess > SAL_CALL getDefaultSettings( const OUString& ResourceURL ) override;
134
135 // XUIConfigurationPersistence
136 virtual void SAL_CALL reload() override;
137 virtual void SAL_CALL store() override;
138 virtual void SAL_CALL storeToStorage( const css::uno::Reference< css::embed::XStorage >& Storage ) override;
139 virtual sal_Bool SAL_CALL isModified() override;
140 virtual sal_Bool SAL_CALL isReadOnly() override;
141
142private:
143 // private data types
144 enum Layer
145 {
146 LAYER_DEFAULT,
147 LAYER_USERDEFINED,
148 LAYER_COUNT
149 };
150
151 enum NotifyOp
152 {
153 NotifyOp_Remove,
154 NotifyOp_Insert,
155 NotifyOp_Replace
156 };
157
158 struct UIElementInfo
159 {
160 UIElementInfo( OUString _aResourceURL, OUString _aUIName ) :
161 aResourceURL(std::move( _aResourceURL)), aUIName(std::move( _aUIName )) {}
162 OUString aResourceURL;
163 OUString aUIName;
164 };
165
166 struct UIElementData
167 {
168 UIElementData() : bModified( false ), bDefault( true ), bDefaultNode( true ) {};
169
170 OUString aResourceURL;
171 OUString aName;
172 bool bModified; // has been changed since last storing
173 bool bDefault; // default settings
174 bool bDefaultNode; // this is a default layer element data
175 css::uno::Reference< css::container::XIndexAccess > xSettings;
176 };
177
178 typedef std::unordered_map< OUString, UIElementData > UIElementDataHashMap;
179
180 struct UIElementType
181 {
182 UIElementType() : bModified( false ),
183 bLoaded( false ),
184 nElementType( css::ui::UIElementType::UNKNOWN ) {}
185
186 bool bModified;
187 bool bLoaded;
188 sal_Int16 nElementType;
189 UIElementDataHashMap aElementsHashMap;
190 css::uno::Reference< css::embed::XStorage > xStorage;
191 };
192
193 typedef std::vector< UIElementType > UIElementTypesVector;
194 typedef std::vector< css::ui::ConfigurationEvent > ConfigEventNotifyContainer;
195 typedef std::unordered_map< OUString, UIElementInfo > UIElementInfoHashMap;
196
197 void impl_Initialize();
198 void implts_notifyContainerListener( const css::ui::ConfigurationEvent& aEvent, NotifyOp eOp );
199 void impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType );
200 void impl_preloadUIElementTypeList( Layer eLayer, sal_Int16 nElementType );
201 UIElementData* impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad = true );
202 void impl_requestUIElementData( sal_Int16 nElementType, Layer eLayer, UIElementData& aUIElementData );
203 void impl_storeElementTypeData( const css::uno::Reference< css::embed::XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState = true );
204 void impl_resetElementTypeData( UIElementType& rUserElementType, UIElementType const & rDefaultElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer );
205 void impl_reloadElementTypeData( UIElementType& rUserElementType, UIElementType const & rDefaultElementType, ConfigEventNotifyContainer& rRemoveNotifyContainer, ConfigEventNotifyContainer& rReplaceNotifyContainer );
206
207 UIElementTypesVector m_aUIElements[LAYER_COUNT];
208 std::unique_ptr<PresetHandler> m_pStorageHandler[css::ui::UIElementType::COUNT];
209 css::uno::Reference< css::embed::XStorage > m_xDefaultConfigStorage;
210 css::uno::Reference< css::embed::XStorage > m_xUserConfigStorage;
211 bool m_bReadOnly;
212 bool m_bModified;
213 bool m_bDisposed;
214 OUString m_aXMLPostfix;
215 OUString m_aPropUIName;
216 OUString m_aModuleIdentifier;
217 css::uno::Reference< css::embed::XTransactedObject > m_xUserRootCommit;
218 css::uno::Reference< css::uno::XComponentContext > m_xContext;
219 std::mutex m_mutex;
222 rtl::Reference< ImageManager > m_xModuleImageManager;
223 css::uno::Reference< css::ui::XAcceleratorConfiguration > m_xModuleAcceleratorManager;
224};
225
226// important: The order and position of the elements must match the constant
227// definition of "css::ui::UIElementType"
228std::u16string_view UIELEMENTTYPENAMES[] =
229{
230 u"", // Dummy value for unknown!
238};
239
240constexpr std::u16string_view RESOURCEURL_PREFIX = u"private:resource/";
241
242sal_Int16 RetrieveTypeFromResourceURL( std::u16string_view aResourceURL )
243{
244
245 if (( o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX ) ) &&
246 ( aResourceURL.size() > RESOURCEURL_PREFIX.size() ))
247 {
248 std::u16string_view aTmpStr = aResourceURL.substr( RESOURCEURL_PREFIX.size() );
249 size_t nIndex = aTmpStr.find( '/' );
250 if (( nIndex > 0 ) && ( aTmpStr.size() > nIndex ))
251 {
252 std::u16string_view aTypeStr( aTmpStr.substr( 0, nIndex ));
253 for ( int i = 0; i < ui::UIElementType::COUNT; i++ )
254 {
255 if ( aTypeStr == UIELEMENTTYPENAMES[i] )
256 return sal_Int16( i );
257 }
258 }
259 }
260
261 return ui::UIElementType::UNKNOWN;
262}
263
264OUString RetrieveNameFromResourceURL( std::u16string_view aResourceURL )
265{
266 if (( o3tl::starts_with(aResourceURL, RESOURCEURL_PREFIX ) ) &&
267 ( aResourceURL.size() > RESOURCEURL_PREFIX.size() ))
268 {
269 size_t nIndex = aResourceURL.rfind( '/' );
270
271 if ( nIndex > 0 && nIndex != std::u16string_view::npos && (( nIndex+1 ) < aResourceURL.size()) )
272 return OUString(aResourceURL.substr( nIndex+1 ));
273 }
274
275 return OUString();
276}
277
278void ModuleUIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType )
279{
280 // preload list of element types on demand
281 impl_preloadUIElementTypeList( LAYER_USERDEFINED, nElementType );
282 impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType );
283
284 UIElementDataHashMap& rUserElements = m_aUIElements[LAYER_USERDEFINED][nElementType].aElementsHashMap;
285
286 OUString aCustomUrlPrefix( "custom_" );
287 for (auto const& userElement : rUserElements)
288 {
289 sal_Int32 nIndex = userElement.second.aResourceURL.indexOf( aCustomUrlPrefix, RESOURCEURL_PREFIX.size() );
290 if ( nIndex > static_cast<sal_Int32>(RESOURCEURL_PREFIX.size()) )
291 {
292 // Performance: Retrieve user interface name only for custom user interface elements.
293 // It's only used by them!
294 UIElementData* pDataSettings = impl_findUIElementData( userElement.second.aResourceURL, nElementType );
295 if ( pDataSettings )
296 {
297 // Retrieve user interface name from XPropertySet interface
298 OUString aUIName;
299 Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY );
300 if ( xPropSet.is() )
301 {
302 Any a = xPropSet->getPropertyValue( m_aPropUIName );
303 a >>= aUIName;
304 }
305
306 UIElementInfo aInfo( userElement.second.aResourceURL, aUIName );
307 aUIElementInfoCollection.emplace( userElement.second.aResourceURL, aInfo );
308 }
309 }
310 else
311 {
312 // The user interface name for standard user interface elements is stored in the WindowState.xcu file
313 UIElementInfo aInfo( userElement.second.aResourceURL, OUString() );
314 aUIElementInfoCollection.emplace( userElement.second.aResourceURL, aInfo );
315 }
316 }
317
318 UIElementDataHashMap& rDefaultElements = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap;
319
320 for (auto const& defaultElement : rDefaultElements)
321 {
322 UIElementInfoHashMap::const_iterator pIterInfo = aUIElementInfoCollection.find( defaultElement.second.aResourceURL );
323 if ( pIterInfo == aUIElementInfoCollection.end() )
324 {
325 sal_Int32 nIndex = defaultElement.second.aResourceURL.indexOf( aCustomUrlPrefix, RESOURCEURL_PREFIX.size() );
326 if ( nIndex > static_cast<sal_Int32>(RESOURCEURL_PREFIX.size()) )
327 {
328 // Performance: Retrieve user interface name only for custom user interface elements.
329 // It's only used by them!
330 UIElementData* pDataSettings = impl_findUIElementData( defaultElement.second.aResourceURL, nElementType );
331 if ( pDataSettings )
332 {
333 // Retrieve user interface name from XPropertySet interface
334 OUString aUIName;
335 Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY );
336 if ( xPropSet.is() )
337 {
338 Any a = xPropSet->getPropertyValue( m_aPropUIName );
339 a >>= aUIName;
340 }
341 UIElementInfo aInfo( defaultElement.second.aResourceURL, aUIName );
342 aUIElementInfoCollection.emplace( defaultElement.second.aResourceURL, aInfo );
343 }
344 }
345 else
346 {
347 // The user interface name for standard user interface elements is stored in the WindowState.xcu file
348 UIElementInfo aInfo( defaultElement.second.aResourceURL, OUString() );
349 aUIElementInfoCollection.emplace( defaultElement.second.aResourceURL, aInfo );
350 }
351 }
352 }
353}
354
355void ModuleUIConfigurationManager::impl_preloadUIElementTypeList( Layer eLayer, sal_Int16 nElementType )
356{
357 UIElementType& rElementTypeData = m_aUIElements[eLayer][nElementType];
358
359 if ( rElementTypeData.bLoaded )
360 return;
361
362 Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
363 if ( !xElementTypeStorage.is() )
364 return;
365
366 OUString aResURLPrefix =
367 OUString::Concat(RESOURCEURL_PREFIX) +
368 UIELEMENTTYPENAMES[ nElementType ] +
369 "/";
370
371 UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap;
372 const Sequence< OUString > aUIElementNames = xElementTypeStorage->getElementNames();
373 for ( OUString const & rElementName : aUIElementNames )
374 {
375 UIElementData aUIElementData;
376
377 // Resource name must be without ".xml"
378 sal_Int32 nIndex = rElementName.lastIndexOf( '.' );
379 if (( nIndex > 0 ) && ( nIndex < rElementName.getLength() ))
380 {
381 std::u16string_view aExtension( rElementName.subView( nIndex+1 ));
382 std::u16string_view aUIElementName( rElementName.subView( 0, nIndex ));
383
384 if (!aUIElementName.empty() &&
385 ( o3tl::equalsIgnoreAsciiCase(aExtension, u"xml")))
386 {
387 aUIElementData.aResourceURL = aResURLPrefix + aUIElementName;
388 aUIElementData.aName = rElementName;
389
390 if ( eLayer == LAYER_USERDEFINED )
391 {
392 aUIElementData.bModified = false;
393 aUIElementData.bDefault = false;
394 aUIElementData.bDefaultNode = false;
395 }
396
397 // Create std::unordered_map entries for all user interface elements inside the storage. We don't load the
398 // settings to speed up the process.
399 rHashMap.emplace( aUIElementData.aResourceURL, aUIElementData );
400 }
401 }
402 rElementTypeData.bLoaded = true;
403 }
404
405}
406
407void ModuleUIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, Layer eLayer, UIElementData& aUIElementData )
408{
409 UIElementType& rElementTypeData = m_aUIElements[eLayer][nElementType];
410
411 Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
412 if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() )
413 {
414 try
415 {
416 Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ );
417 Reference< XInputStream > xInputStream = xStream->getInputStream();
418
419 if ( xInputStream.is() )
420 {
421 switch ( nElementType )
422 {
423 case css::ui::UIElementType::UNKNOWN:
424 break;
425
426 case css::ui::UIElementType::MENUBAR:
427 case css::ui::UIElementType::POPUPMENU:
428 {
429 try
430 {
431 MenuConfiguration aMenuCfg( m_xContext );
432 Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream ));
433 auto pRootItemContainer = dynamic_cast<RootItemContainer*>( xContainer.get() );
434 if ( pRootItemContainer )
435 aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
436 else
437 aUIElementData.xSettings = new ConstItemContainer( xContainer, true );
438 return;
439 }
440 catch ( const css::lang::WrappedTargetException& )
441 {
442 }
443 }
444 break;
445
446 case css::ui::UIElementType::TOOLBAR:
447 {
448 try
449 {
450 Reference< XIndexContainer > xIndexContainer( new RootItemContainer() );
451 ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer );
452 auto pRootItemContainer = dynamic_cast<RootItemContainer*>( xIndexContainer.get() );
453 aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
454 return;
455 }
456 catch ( const css::lang::WrappedTargetException& )
457 {
458 }
459
460 break;
461 }
462
463 case css::ui::UIElementType::STATUSBAR:
464 {
465 try
466 {
467 Reference< XIndexContainer > xIndexContainer( new RootItemContainer() );
468 StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer );
469 auto pRootItemContainer = dynamic_cast<RootItemContainer*>( xIndexContainer.get() );
470 aUIElementData.xSettings = new ConstItemContainer( pRootItemContainer, true );
471 return;
472 }
473 catch ( const css::lang::WrappedTargetException& )
474 {
475 }
476
477 break;
478 }
479
480 case css::ui::UIElementType::FLOATINGWINDOW:
481 {
482 break;
483 }
484 }
485 }
486 }
487 catch ( const css::embed::InvalidStorageException& )
488 {
489 }
490 catch ( const css::lang::IllegalArgumentException& )
491 {
492 }
493 catch ( const css::io::IOException& )
494 {
495 }
496 catch ( const css::embed::StorageWrappedTargetException& )
497 {
498 }
499 }
500
501 // At least we provide an empty settings container!
502 aUIElementData.xSettings = new ConstItemContainer();
503}
504
505ModuleUIConfigurationManager::UIElementData* ModuleUIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad )
506{
507 // preload list of element types on demand
508 impl_preloadUIElementTypeList( LAYER_USERDEFINED, nElementType );
509 impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType );
510
511 // first try to look into our user-defined vector/unordered_map combination
512 UIElementDataHashMap& rUserHashMap = m_aUIElements[LAYER_USERDEFINED][nElementType].aElementsHashMap;
513 UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL );
514 if ( pIter != rUserHashMap.end() )
515 {
516 // Default data settings data must be retrieved from the default layer!
517 if ( !pIter->second.bDefault )
518 {
519 if ( !pIter->second.xSettings.is() && bLoad )
520 impl_requestUIElementData( nElementType, LAYER_USERDEFINED, pIter->second );
521 return &(pIter->second);
522 }
523 }
524
525 // Not successful, we have to look into our default vector/unordered_map combination
526 UIElementDataHashMap& rDefaultHashMap = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap;
527 pIter = rDefaultHashMap.find( aResourceURL );
528 if ( pIter != rDefaultHashMap.end() )
529 {
530 if ( !pIter->second.xSettings.is() && bLoad )
531 impl_requestUIElementData( nElementType, LAYER_DEFAULT, pIter->second );
532 return &(pIter->second);
533 }
534
535 // Nothing has been found!
536 return nullptr;
537}
538
539void ModuleUIConfigurationManager::impl_storeElementTypeData( const Reference< XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState )
540{
541 UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap;
542
543 for (auto & elem : rHashMap)
544 {
545 UIElementData& rElement = elem.second;
546 if ( rElement.bModified )
547 {
548 if ( rElement.bDefault )
549 {
550 xStorage->removeElement( rElement.aName );
551 rElement.bModified = false; // mark as not modified
552 }
553 else
554 {
555 Reference< XStream > xStream = xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE );
556 Reference< XOutputStream > xOutputStream( xStream->getOutputStream() );
557
558 if ( xOutputStream.is() )
559 {
560 switch( rElementType.nElementType )
561 {
562 case css::ui::UIElementType::MENUBAR:
563 case css::ui::UIElementType::POPUPMENU:
564 {
565 try
566 {
567 MenuConfiguration aMenuCfg( m_xContext );
569 rElement.xSettings, xOutputStream, rElementType.nElementType == css::ui::UIElementType::MENUBAR );
570 }
571 catch ( const css::lang::WrappedTargetException& )
572 {
573 }
574 }
575 break;
576
577 case css::ui::UIElementType::TOOLBAR:
578 {
579 try
580 {
581 ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings );
582 }
583 catch ( const css::lang::WrappedTargetException& )
584 {
585 }
586 }
587 break;
588
589 case css::ui::UIElementType::STATUSBAR:
590 {
591 try
592 {
593 StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings );
594 }
595 catch ( const css::lang::WrappedTargetException& )
596 {
597 }
598 }
599 break;
600
601 default:
602 break;
603 }
604 }
605
606 // mark as not modified if we store to our own storage
607 if ( bResetModifyState )
608 rElement.bModified = false;
609 }
610 }
611 }
612
613 // commit element type storage
614 Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY );
615 if ( xTransactedObject.is() )
616 xTransactedObject->commit();
617
618 // mark UIElementType as not modified if we store to our own storage
619 if ( bResetModifyState )
620 rElementType.bModified = false;
621}
622
623// This is only allowed to be called on the LAYER_USER_DEFINED!
624void ModuleUIConfigurationManager::impl_resetElementTypeData(
625 UIElementType& rUserElementType,
626 UIElementType const & rDefaultElementType,
627 ConfigEventNotifyContainer& rRemoveNotifyContainer,
628 ConfigEventNotifyContainer& rReplaceNotifyContainer )
629{
630 UIElementDataHashMap& rHashMap = rUserElementType.aElementsHashMap;
631
633 Reference< XInterface > xIfac( xThis, UNO_QUERY );
634 sal_Int16 nType = rUserElementType.nElementType;
635
636 // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling
637 // our listeners!
638 for (auto & elem : rHashMap)
639 {
640 UIElementData& rElement = elem.second;
641 if ( !rElement.bDefault )
642 {
643 if ( rDefaultElementType.xStorage->hasByName( rElement.aName ))
644 {
645 // Replace settings with data from default layer
646 Reference< XIndexAccess > xOldSettings( rElement.xSettings );
647 impl_requestUIElementData( nType, LAYER_DEFAULT, rElement );
648
649 ui::ConfigurationEvent aReplaceEvent;
650 aReplaceEvent.ResourceURL = rElement.aResourceURL;
651 aReplaceEvent.Accessor <<= xThis;
652 aReplaceEvent.Source = xIfac;
653 aReplaceEvent.ReplacedElement <<= xOldSettings;
654 aReplaceEvent.Element <<= rElement.xSettings;
655
656 rReplaceNotifyContainer.push_back( aReplaceEvent );
657
658 // Mark element as default and not modified. That means "not active"
659 // in the user layer anymore.
660 rElement.bModified = false;
661 rElement.bDefault = true;
662 }
663 else
664 {
665 // Remove user-defined settings from user layer
666 ui::ConfigurationEvent aEvent;
667 aEvent.ResourceURL = rElement.aResourceURL;
668 aEvent.Accessor <<= xThis;
669 aEvent.Source = xIfac;
670 aEvent.Element <<= rElement.xSettings;
671
672 rRemoveNotifyContainer.push_back( aEvent );
673
674 // Mark element as default and not modified. That means "not active"
675 // in the user layer anymore.
676 rElement.bModified = false;
677 rElement.bDefault = true;
678 }
679 }
680 }
681
682 // Remove all settings from our user interface elements
683 rHashMap.clear();
684}
685
686void ModuleUIConfigurationManager::impl_reloadElementTypeData(
687 UIElementType& rUserElementType,
688 UIElementType const & rDefaultElementType,
689 ConfigEventNotifyContainer& rRemoveNotifyContainer,
690 ConfigEventNotifyContainer& rReplaceNotifyContainer )
691{
692 UIElementDataHashMap& rHashMap = rUserElementType.aElementsHashMap;
693
695 Reference< XInterface > xIfac( xThis, UNO_QUERY );
696 sal_Int16 nType = rUserElementType.nElementType;
697
698 for (auto & elem : rHashMap)
699 {
700 UIElementData& rElement = elem.second;
701 if ( rElement.bModified )
702 {
703 if ( rUserElementType.xStorage->hasByName( rElement.aName ))
704 {
705 // Replace settings with data from user layer
706 Reference< XIndexAccess > xOldSettings( rElement.xSettings );
707
708 impl_requestUIElementData( nType, LAYER_USERDEFINED, rElement );
709
710 ui::ConfigurationEvent aReplaceEvent;
711
712 aReplaceEvent.ResourceURL = rElement.aResourceURL;
713 aReplaceEvent.Accessor <<= xThis;
714 aReplaceEvent.Source = xIfac;
715 aReplaceEvent.ReplacedElement <<= xOldSettings;
716 aReplaceEvent.Element <<= rElement.xSettings;
717 rReplaceNotifyContainer.push_back( aReplaceEvent );
718
719 rElement.bModified = false;
720 }
721 else if ( rDefaultElementType.xStorage->hasByName( rElement.aName ))
722 {
723 // Replace settings with data from default layer
724 Reference< XIndexAccess > xOldSettings( rElement.xSettings );
725
726 impl_requestUIElementData( nType, LAYER_DEFAULT, rElement );
727
728 ui::ConfigurationEvent aReplaceEvent;
729
730 aReplaceEvent.ResourceURL = rElement.aResourceURL;
731 aReplaceEvent.Accessor <<= xThis;
732 aReplaceEvent.Source = xIfac;
733 aReplaceEvent.ReplacedElement <<= xOldSettings;
734 aReplaceEvent.Element <<= rElement.xSettings;
735 rReplaceNotifyContainer.push_back( aReplaceEvent );
736
737 // Mark element as default and not modified. That means "not active"
738 // in the user layer anymore.
739 rElement.bModified = false;
740 rElement.bDefault = true;
741 }
742 else
743 {
744 // Element settings are not in any storage => remove
745 ui::ConfigurationEvent aRemoveEvent;
746
747 aRemoveEvent.ResourceURL = rElement.aResourceURL;
748 aRemoveEvent.Accessor <<= xThis;
749 aRemoveEvent.Source = xIfac;
750 aRemoveEvent.Element <<= rElement.xSettings;
751
752 rRemoveNotifyContainer.push_back( aRemoveEvent );
753
754 // Mark element as default and not modified. That means "not active"
755 // in the user layer anymore.
756 rElement.bModified = false;
757 rElement.bDefault = true;
758 }
759 }
760 }
761
762 rUserElementType.bModified = false;
763}
764
765void ModuleUIConfigurationManager::impl_Initialize()
766{
767 // Initialize the top-level structures with the storage data
768 if ( m_xUserConfigStorage.is() )
769 {
770 // Try to access our module sub folder
771 for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT;
772 i++ )
773 {
774 Reference< XStorage > xElementTypeStorage;
775 try
776 {
777 if ( m_pStorageHandler[i] )
778 xElementTypeStorage = m_pStorageHandler[i]->getWorkingStorageUser();
779 }
780 catch ( const css::container::NoSuchElementException& )
781 {
782 }
783 catch ( const css::embed::InvalidStorageException& )
784 {
785 }
786 catch ( const css::lang::IllegalArgumentException& )
787 {
788 }
789 catch ( const css::io::IOException& )
790 {
791 }
792 catch ( const css::embed::StorageWrappedTargetException& )
793 {
794 }
795
796 m_aUIElements[LAYER_USERDEFINED][i].nElementType = i;
797 m_aUIElements[LAYER_USERDEFINED][i].bModified = false;
798 m_aUIElements[LAYER_USERDEFINED][i].xStorage = xElementTypeStorage;
799 }
800 }
801
802 if ( !m_xDefaultConfigStorage.is() )
803 return;
804
805 Reference< XNameAccess > xNameAccess( m_xDefaultConfigStorage, UNO_QUERY_THROW );
806
807 // Try to access our module sub folder
808 for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT;
809 i++ )
810 {
811 Reference< XStorage > xElementTypeStorage;
812 try
813 {
814 const OUString sName( UIELEMENTTYPENAMES[i] );
815 if( xNameAccess->hasByName( sName ) )
816 xNameAccess->getByName( sName ) >>= xElementTypeStorage;
817 }
818 catch ( const css::container::NoSuchElementException& )
819 {
820 }
821
822 m_aUIElements[LAYER_DEFAULT][i].nElementType = i;
823 m_aUIElements[LAYER_DEFAULT][i].bModified = false;
824 m_aUIElements[LAYER_DEFAULT][i].xStorage = xElementTypeStorage;
825 }
826}
827
828ModuleUIConfigurationManager::ModuleUIConfigurationManager(
829 const Reference< XComponentContext >& xContext,
830 const css::uno::Sequence< css::uno::Any >& aArguments)
831 : m_bReadOnly( true )
832 , m_bModified( false )
833 , m_bDisposed( false )
834 , m_aXMLPostfix( ".xml" )
835 , m_aPropUIName( "UIName" )
836 , m_xContext( xContext )
837{
838 // Make sure we have a default initialized entry for every layer and user interface element type!
839 // The following code depends on this!
840 m_aUIElements[LAYER_DEFAULT].resize( css::ui::UIElementType::COUNT );
841 m_aUIElements[LAYER_USERDEFINED].resize( css::ui::UIElementType::COUNT );
842
844
845 OUString aModuleShortName;
846 if( aArguments.getLength() == 2 && (aArguments[0] >>= aModuleShortName) && (aArguments[1] >>= m_aModuleIdentifier))
847 {
848 }
849 else
850 {
852 aModuleShortName = lArgs.getUnpackedValueOrDefault("ModuleShortName", OUString());
853 m_aModuleIdentifier = lArgs.getUnpackedValueOrDefault("ModuleIdentifier", OUString());
854 }
855
856 for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
857 {
858 OUString aResourceType;
859 if ( i == css::ui::UIElementType::MENUBAR )
860 aResourceType = RESOURCETYPE_MENUBAR;
861 else if ( i == css::ui::UIElementType::TOOLBAR )
862 aResourceType = RESOURCETYPE_TOOLBAR;
863 else if ( i == css::ui::UIElementType::STATUSBAR )
864 aResourceType = RESOURCETYPE_STATUSBAR;
865 else if ( i == css::ui::UIElementType::POPUPMENU )
866 aResourceType = RESOURCETYPE_POPUPMENU;
867
868 if ( !aResourceType.isEmpty() )
869 {
870 m_pStorageHandler[i].reset( new PresetHandler( m_xContext ) );
871 m_pStorageHandler[i]->connectToResource( PresetHandler::E_MODULES,
872 aResourceType, // this path won't be used later... see next lines!
873 aModuleShortName,
874 css::uno::Reference< css::embed::XStorage >()); // no document root used here!
875 }
876 }
877
878 // initialize root storages for all resource types
879 m_xUserRootCommit.set( m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getOrCreateRootStorageUser(), css::uno::UNO_QUERY); // can be empty
880 m_xDefaultConfigStorage = m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getParentStorageShare();
881 m_xUserConfigStorage = m_pStorageHandler[css::ui::UIElementType::MENUBAR]->getParentStorageUser();
882
883 if ( m_xUserConfigStorage.is() )
884 {
885 Reference< XPropertySet > xPropSet( m_xUserConfigStorage, UNO_QUERY );
886 if ( xPropSet.is() )
887 {
888 tools::Long nOpenMode = 0;
889 Any a = xPropSet->getPropertyValue("OpenMode");
890 if ( a >>= nOpenMode )
891 m_bReadOnly = !( nOpenMode & ElementModes::WRITE );
892 }
893 }
894
895 impl_Initialize();
896}
897
898// XComponent
899void SAL_CALL ModuleUIConfigurationManager::dispose()
900{
901 Reference< XComponent > xThis(this);
902
903 css::lang::EventObject aEvent( xThis );
904 {
905 std::unique_lock aGuard(m_mutex);
906 m_aEventListeners.disposeAndClear( aGuard, aEvent );
907 }
908 {
909 std::unique_lock aGuard(m_mutex);
910 m_aConfigListeners.disposeAndClear( aGuard, aEvent );
911 }
912
913 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
915 Reference< XComponent > xModuleImageManager( m_xModuleImageManager );
916 m_xModuleImageManager.clear();
917 m_xModuleAcceleratorManager.clear();
918 m_aUIElements[LAYER_USERDEFINED].clear();
919 m_aUIElements[LAYER_DEFAULT].clear();
920 m_xDefaultConfigStorage.clear();
921 m_xUserConfigStorage.clear();
922 m_xUserRootCommit.clear();
923 m_bModified = false;
924 m_bDisposed = true;
925 aGuard.clear();
926 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
927
928 try
929 {
930 if ( xModuleImageManager.is() )
931 xModuleImageManager->dispose();
932 }
933 catch ( const Exception& )
934 {
935 }
936}
937
938void SAL_CALL ModuleUIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener )
939{
940 {
942
943 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
944 if ( m_bDisposed )
945 throw DisposedException();
946 }
947
948 std::unique_lock aGuard(m_mutex);
949 m_aEventListeners.addInterface( aGuard, xListener );
950}
951
952void SAL_CALL ModuleUIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener )
953{
954 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
955 std::unique_lock aGuard(m_mutex);
956 m_aEventListeners.removeInterface( aGuard, xListener );
957}
958
959// XUIConfiguration
960void SAL_CALL ModuleUIConfigurationManager::addConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener )
961{
962 {
964
965 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
966 if ( m_bDisposed )
967 throw DisposedException();
968 }
969
970 std::unique_lock aGuard(m_mutex);
971 m_aConfigListeners.addInterface( aGuard, xListener );
972}
973
974void SAL_CALL ModuleUIConfigurationManager::removeConfigurationListener( const Reference< css::ui::XUIConfigurationListener >& xListener )
975{
976 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
977 std::unique_lock aGuard(m_mutex);
978 m_aConfigListeners.removeInterface( aGuard, xListener );
979}
980
981// XUIConfigurationManager
982void SAL_CALL ModuleUIConfigurationManager::reset()
983{
985
986 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
987 if ( m_bDisposed )
988 throw DisposedException();
989
990 if ( isReadOnly() )
991 return;
992
993 // Remove all elements from our user-defined storage!
994 try
995 {
996 for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
997 {
998 UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i];
999
1000 if ( rElementType.xStorage.is() )
1001 {
1002 bool bCommitSubStorage( false );
1003 const Sequence< OUString > aUIElementStreamNames = rElementType.xStorage->getElementNames();
1004 for ( OUString const & rName : aUIElementStreamNames )
1005 {
1006 rElementType.xStorage->removeElement( rName );
1007 bCommitSubStorage = true;
1008 }
1009
1010 if ( bCommitSubStorage )
1011 {
1012 Reference< XTransactedObject > xTransactedObject( rElementType.xStorage, UNO_QUERY );
1013 if ( xTransactedObject.is() )
1014 xTransactedObject->commit();
1015 m_pStorageHandler[i]->commitUserChanges();
1016 }
1017 }
1018 }
1019
1020 // remove settings from user defined layer and notify listener about removed settings data!
1021 ConfigEventNotifyContainer aRemoveEventNotifyContainer;
1022 ConfigEventNotifyContainer aReplaceEventNotifyContainer;
1023 for ( sal_Int16 j = 1; j < css::ui::UIElementType::COUNT; j++ )
1024 {
1025 try
1026 {
1027 UIElementType& rUserElementType = m_aUIElements[LAYER_USERDEFINED][j];
1028 UIElementType& rDefaultElementType = m_aUIElements[LAYER_DEFAULT][j];
1029
1030 impl_resetElementTypeData( rUserElementType, rDefaultElementType, aRemoveEventNotifyContainer, aReplaceEventNotifyContainer );
1031 rUserElementType.bModified = false;
1032 }
1033 catch (const Exception&)
1034 {
1035 css::uno::Any anyEx = cppu::getCaughtException();
1036 throw css::lang::WrappedTargetRuntimeException(
1037 "ModuleUIConfigurationManager::reset exception",
1038 css::uno::Reference<css::uno::XInterface>(*this), anyEx);
1039 }
1040 }
1041
1042 m_bModified = false;
1043
1044 // Unlock mutex before notify our listeners
1045 aGuard.clear();
1046
1047 // Notify our listeners
1048 for ( auto const & k: aRemoveEventNotifyContainer )
1049 implts_notifyContainerListener( k, NotifyOp_Remove );
1050 for ( auto const & k: aReplaceEventNotifyContainer )
1051 implts_notifyContainerListener( k, NotifyOp_Replace );
1052 }
1053 catch ( const css::lang::IllegalArgumentException& )
1054 {
1055 }
1056 catch ( const css::container::NoSuchElementException& )
1057 {
1058 }
1059 catch ( const css::embed::InvalidStorageException& )
1060 {
1061 }
1062 catch ( const css::embed::StorageWrappedTargetException& )
1063 {
1064 }
1065}
1066
1067Sequence< Sequence< PropertyValue > > SAL_CALL ModuleUIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType )
1068{
1069 if (( ElementType < 0 ) || ( ElementType >= css::ui::UIElementType::COUNT ))
1070 throw IllegalArgumentException();
1071
1073 if ( m_bDisposed )
1074 throw DisposedException();
1075
1076 std::vector< Sequence< PropertyValue > > aElementInfoSeq;
1077 UIElementInfoHashMap aUIElementInfoCollection;
1078
1079 if ( ElementType == css::ui::UIElementType::UNKNOWN )
1080 {
1081 for ( sal_Int16 i = 0; i < css::ui::UIElementType::COUNT; i++ )
1082 impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, i );
1083 }
1084 else
1085 impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType );
1086
1087 aElementInfoSeq.resize( aUIElementInfoCollection.size() );
1088
1089 sal_Int32 n = 0;
1090 for (auto const& elem : aUIElementInfoCollection)
1091 {
1092 Sequence< PropertyValue > aUIElementInfo{
1093 comphelper::makePropertyValue("ResourceURL", elem.second.aResourceURL),
1094 comphelper::makePropertyValue(m_aPropUIName, elem.second.aUIName)
1095 };
1096 aElementInfoSeq[n++] = aUIElementInfo;
1097 }
1098
1099 return comphelper::containerToSequence(aElementInfoSeq);
1100}
1101
1102Reference< XIndexContainer > SAL_CALL ModuleUIConfigurationManager::createSettings()
1103{
1105
1106 if ( m_bDisposed )
1107 throw DisposedException();
1108
1109 // Creates an empty item container which can be filled from outside
1111}
1112
1113sal_Bool SAL_CALL ModuleUIConfigurationManager::hasSettings( const OUString& ResourceURL )
1114{
1115 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
1116
1117 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
1118 ( nElementType >= css::ui::UIElementType::COUNT ))
1119 throw IllegalArgumentException();
1120
1122
1123 if ( m_bDisposed )
1124 throw DisposedException();
1125
1126 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false );
1127 if ( pDataSettings )
1128 return true;
1129
1130 return false;
1131}
1132
1133Reference< XIndexAccess > SAL_CALL ModuleUIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable )
1134{
1135 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
1136
1137 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
1138 ( nElementType >= css::ui::UIElementType::COUNT ))
1139 throw IllegalArgumentException();
1140
1142
1143 if ( m_bDisposed )
1144 throw DisposedException();
1145
1146 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
1147 if ( pDataSettings )
1148 {
1149 // Create a copy of our data if someone wants to change the data.
1150 if ( bWriteable )
1151 return Reference< XIndexAccess >( new RootItemContainer( pDataSettings->xSettings ) );
1152 else
1153 return pDataSettings->xSettings;
1154 }
1155
1156 throw NoSuchElementException();
1157}
1158
1159void SAL_CALL ModuleUIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< css::container::XIndexAccess >& aNewData )
1160{
1161 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
1162
1163 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
1164 ( nElementType >= css::ui::UIElementType::COUNT ))
1165 throw IllegalArgumentException();
1166 else if ( m_bReadOnly )
1167 throw IllegalAccessException();
1168 else
1169 {
1171
1172 if ( m_bDisposed )
1173 throw DisposedException();
1174
1175 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
1176 if ( !pDataSettings )
1177 throw NoSuchElementException();
1178 if ( !pDataSettings->bDefaultNode )
1179 {
1180 // we have a settings entry in our user-defined layer - replace
1181 Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings;
1182
1183 // Create a copy of the data if the container is not const
1184 Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
1185 if ( xReplace.is() )
1186 pDataSettings->xSettings = new ConstItemContainer( aNewData );
1187 else
1188 pDataSettings->xSettings = aNewData;
1189 pDataSettings->bDefault = false;
1190 pDataSettings->bModified = true;
1191 m_bModified = true;
1192
1193 // Modify type container
1194 UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType];
1195 rElementType.bModified = true;
1196
1198 Reference< XInterface > xIfac( xThis, UNO_QUERY );
1199
1200 // Create event to notify listener about replaced element settings
1201 ui::ConfigurationEvent aEvent;
1202 aEvent.ResourceURL = ResourceURL;
1203 aEvent.Accessor <<= xThis;
1204 aEvent.Source = xIfac;
1205 aEvent.ReplacedElement <<= xOldSettings;
1206 aEvent.Element <<= pDataSettings->xSettings;
1207
1208 aGuard.clear();
1209
1210 implts_notifyContainerListener( aEvent, NotifyOp_Replace );
1211 }
1212 else
1213 {
1214 // we have no settings in our user-defined layer - insert
1215 UIElementData aUIElementData;
1216
1217 aUIElementData.bDefault = false;
1218 aUIElementData.bDefaultNode = false;
1219 aUIElementData.bModified = true;
1220
1221 // Create a copy of the data if the container is not const
1222 Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
1223 if ( xReplace.is() )
1224 aUIElementData.xSettings = new ConstItemContainer( aNewData );
1225 else
1226 aUIElementData.xSettings = aNewData;
1227 aUIElementData.aName = RetrieveNameFromResourceURL( ResourceURL ) + m_aXMLPostfix;
1228 aUIElementData.aResourceURL = ResourceURL;
1229 m_bModified = true;
1230
1231 // Modify type container
1232 UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType];
1233 rElementType.bModified = true;
1234
1235 UIElementDataHashMap& rElements = rElementType.aElementsHashMap;
1236
1237 // Check our user element settings hash map as it can already contain settings that have been set to default!
1238 // If no node can be found, we have to insert it.
1239 UIElementDataHashMap::iterator pIter = rElements.find( ResourceURL );
1240 if ( pIter != rElements.end() )
1241 pIter->second = aUIElementData;
1242 else
1243 rElements.emplace( ResourceURL, aUIElementData );
1244
1246 Reference< XInterface > xIfac( xThis, UNO_QUERY );
1247
1248 // Create event to notify listener about replaced element settings
1249 ui::ConfigurationEvent aEvent;
1250
1251 aEvent.ResourceURL = ResourceURL;
1252 aEvent.Accessor <<= xThis;
1253 aEvent.Source = xIfac;
1254 aEvent.ReplacedElement <<= pDataSettings->xSettings;
1255 aEvent.Element <<= aUIElementData.xSettings;
1256
1257 aGuard.clear();
1258
1259 implts_notifyContainerListener( aEvent, NotifyOp_Replace );
1260 }
1261 }
1262}
1263
1264void SAL_CALL ModuleUIConfigurationManager::removeSettings( const OUString& ResourceURL )
1265{
1266 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
1267
1268 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
1269 ( nElementType >= css::ui::UIElementType::COUNT ))
1270 throw IllegalArgumentException( "The ResourceURL is not valid or "
1271 "describes an unknown type. "
1272 "ResourceURL: " + ResourceURL, nullptr, 0 );
1273 else if ( m_bReadOnly )
1274 throw IllegalAccessException( "The configuration manager is read-only. "
1275 "ResourceURL: " + ResourceURL, nullptr );
1276 else
1277 {
1279
1280 if ( m_bDisposed )
1281 throw DisposedException( "The configuration manager has been disposed, "
1282 "and can't uphold its method specification anymore. "
1283 "ResourceURL: " + ResourceURL, nullptr );
1284
1285 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
1286 if ( !pDataSettings )
1287 throw NoSuchElementException( "The settings data cannot be found. "
1288 "ResourceURL: " + ResourceURL, nullptr );
1289 // If element settings are default, we don't need to change anything!
1290 if ( pDataSettings->bDefault )
1291 return;
1292 else
1293 {
1294 Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings;
1295 pDataSettings->bDefault = true;
1296
1297 // check if this is a default layer node
1298 if ( !pDataSettings->bDefaultNode )
1299 pDataSettings->bModified = true; // we have to remove this node from the user layer!
1300 pDataSettings->xSettings.clear();
1301 m_bModified = true; // user layer must be written
1302
1303 // Modify type container
1304 UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType];
1305 rElementType.bModified = true;
1306
1308 Reference< XInterface > xIfac( xThis, UNO_QUERY );
1309
1310 // Check if we have settings in the default layer which replaces the user-defined one!
1311 UIElementData* pDefaultDataSettings = impl_findUIElementData( ResourceURL, nElementType );
1312 if ( pDefaultDataSettings )
1313 {
1314 // Create event to notify listener about replaced element settings
1315 ui::ConfigurationEvent aEvent;
1316
1317 aEvent.ResourceURL = ResourceURL;
1318 aEvent.Accessor <<= xThis;
1319 aEvent.Source = xIfac;
1320 aEvent.Element <<= xRemovedSettings;
1321 aEvent.ReplacedElement <<= pDefaultDataSettings->xSettings;
1322
1323 aGuard.clear();
1324
1325 implts_notifyContainerListener( aEvent, NotifyOp_Replace );
1326 }
1327 else
1328 {
1329 // Create event to notify listener about removed element settings
1330 ui::ConfigurationEvent aEvent;
1331
1332 aEvent.ResourceURL = ResourceURL;
1333 aEvent.Accessor <<= xThis;
1334 aEvent.Source = xIfac;
1335 aEvent.Element <<= xRemovedSettings;
1336
1337 aGuard.clear();
1338
1339 implts_notifyContainerListener( aEvent, NotifyOp_Remove );
1340 }
1341 }
1342 }
1343}
1344
1345void SAL_CALL ModuleUIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData )
1346{
1347 sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL );
1348
1349 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
1350 ( nElementType >= css::ui::UIElementType::COUNT ))
1351 throw IllegalArgumentException();
1352 else if ( m_bReadOnly )
1353 throw IllegalAccessException();
1354 else
1355 {
1357
1358 if ( m_bDisposed )
1359 throw DisposedException();
1360
1361 UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType );
1362 if ( !(!pDataSettings) )
1363 throw ElementExistException();
1364 UIElementData aUIElementData;
1365
1366 aUIElementData.bDefault = false;
1367 aUIElementData.bDefaultNode = false;
1368 aUIElementData.bModified = true;
1369
1370 // Create a copy of the data if the container is not const
1371 Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
1372 if ( xReplace.is() )
1373 aUIElementData.xSettings = new ConstItemContainer( aNewData );
1374 else
1375 aUIElementData.xSettings = aNewData;
1376 aUIElementData.aName = RetrieveNameFromResourceURL( NewResourceURL ) + m_aXMLPostfix;
1377 aUIElementData.aResourceURL = NewResourceURL;
1378 m_bModified = true;
1379
1380 UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][nElementType];
1381 rElementType.bModified = true;
1382
1383 UIElementDataHashMap& rElements = rElementType.aElementsHashMap;
1384 rElements.emplace( NewResourceURL, aUIElementData );
1385
1386 Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings );
1388
1389 // Create event to notify listener about removed element settings
1390 ui::ConfigurationEvent aEvent;
1391
1392 aEvent.ResourceURL = NewResourceURL;
1393 aEvent.Accessor <<= xThis;
1394 aEvent.Source = xThis;
1395 aEvent.Element <<= xInsertSettings;
1396
1397 aGuard.clear();
1398
1399 implts_notifyContainerListener( aEvent, NotifyOp_Insert );
1400 }
1401}
1402
1403Reference< XInterface > SAL_CALL ModuleUIConfigurationManager::getImageManager()
1404{
1406
1407 if ( m_bDisposed )
1408 throw DisposedException();
1409
1410 if ( !m_xModuleImageManager.is() )
1411 {
1412 m_xModuleImageManager = new ImageManager( m_xContext, /*bForModule*/true );
1413
1414 uno::Sequence<uno::Any> aPropSeq(comphelper::InitAnyPropertySequence(
1415 {
1416 {"UserConfigStorage", uno::Any(m_xUserConfigStorage)},
1417 {"ModuleIdentifier", uno::Any(m_aModuleIdentifier)},
1418 {"UserRootCommit", uno::Any(m_xUserRootCommit)},
1419 }));
1420 m_xModuleImageManager->initialize( aPropSeq );
1421 }
1422
1423 return Reference< XInterface >( static_cast<cppu::OWeakObject*>(m_xModuleImageManager.get()), UNO_QUERY );
1424}
1425
1426Reference< ui::XAcceleratorConfiguration > SAL_CALL ModuleUIConfigurationManager::createShortCutManager()
1427{
1428 return ui::ModuleAcceleratorConfiguration::createWithModuleIdentifier(m_xContext, m_aModuleIdentifier);
1429}
1430
1431Reference< ui::XAcceleratorConfiguration > SAL_CALL ModuleUIConfigurationManager::getShortCutManager()
1432{
1434
1435 if ( m_bDisposed )
1436 throw DisposedException();
1437
1438 if ( !m_xModuleAcceleratorManager.is() ) try
1439 {
1440 m_xModuleAcceleratorManager = ui::ModuleAcceleratorConfiguration::
1441 createWithModuleIdentifier(m_xContext, m_aModuleIdentifier);
1442 }
1443 catch ( const css::uno::DeploymentException& )
1444 {
1445 SAL_WARN("fwk.uiconfiguration", "ModuleAcceleratorConfiguration"
1446 " not available. This should happen only on mobile platforms.");
1447 }
1448
1449 return m_xModuleAcceleratorManager;
1450}
1451
1452Reference< XInterface > SAL_CALL ModuleUIConfigurationManager::getEventsManager()
1453{
1454 return Reference< XInterface >();
1455}
1456
1457// XModuleUIConfigurationManager
1458sal_Bool SAL_CALL ModuleUIConfigurationManager::isDefaultSettings( const OUString& ResourceURL )
1459{
1460 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
1461
1462 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
1463 ( nElementType >= css::ui::UIElementType::COUNT ))
1464 throw IllegalArgumentException();
1465
1467
1468 if ( m_bDisposed )
1469 throw DisposedException();
1470
1471 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false );
1472 if ( pDataSettings && pDataSettings->bDefaultNode )
1473 return true;
1474
1475 return false;
1476}
1477
1478Reference< XIndexAccess > SAL_CALL ModuleUIConfigurationManager::getDefaultSettings( const OUString& ResourceURL )
1479{
1480 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
1481
1482 if (( nElementType == css::ui::UIElementType::UNKNOWN ) ||
1483 ( nElementType >= css::ui::UIElementType::COUNT ))
1484 throw IllegalArgumentException();
1485
1487
1488 if ( m_bDisposed )
1489 throw DisposedException();
1490
1491 // preload list of element types on demand
1492 impl_preloadUIElementTypeList( LAYER_DEFAULT, nElementType );
1493
1494 // Look into our default vector/unordered_map combination
1495 UIElementDataHashMap& rDefaultHashMap = m_aUIElements[LAYER_DEFAULT][nElementType].aElementsHashMap;
1496 UIElementDataHashMap::iterator pIter = rDefaultHashMap.find( ResourceURL );
1497 if ( pIter != rDefaultHashMap.end() )
1498 {
1499 if ( !pIter->second.xSettings.is() )
1500 impl_requestUIElementData( nElementType, LAYER_DEFAULT, pIter->second );
1501 return pIter->second.xSettings;
1502 }
1503
1504 // Nothing has been found!
1505 throw NoSuchElementException();
1506}
1507
1508// XUIConfigurationPersistence
1509void SAL_CALL ModuleUIConfigurationManager::reload()
1510{
1512
1513 if ( m_bDisposed )
1514 throw DisposedException();
1515
1516 if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly )
1517 return;
1518
1519 // Try to access our module sub folder
1520 ConfigEventNotifyContainer aRemoveNotifyContainer;
1521 ConfigEventNotifyContainer aReplaceNotifyContainer;
1522 for ( sal_Int16 i = 1; i < css::ui::UIElementType::COUNT; i++ )
1523 {
1524 try
1525 {
1526 UIElementType& rUserElementType = m_aUIElements[LAYER_USERDEFINED][i];
1527
1528 if ( rUserElementType.bModified )
1529 {
1530 UIElementType& rDefaultElementType = m_aUIElements[LAYER_DEFAULT][i];
1531 impl_reloadElementTypeData( rUserElementType, rDefaultElementType, aRemoveNotifyContainer, aReplaceNotifyContainer );
1532 }
1533 }
1534 catch ( const Exception& )
1535 {
1536 throw IOException();
1537 }
1538 }
1539
1540 m_bModified = false;
1541
1542 // Unlock mutex before notify our listeners
1543 aGuard.clear();
1544
1545 // Notify our listeners
1546 for (const ui::ConfigurationEvent & j : aRemoveNotifyContainer)
1547 implts_notifyContainerListener( j, NotifyOp_Remove );
1548 for (const ui::ConfigurationEvent & k : aReplaceNotifyContainer)
1549 implts_notifyContainerListener( k, NotifyOp_Replace );
1550}
1551
1552void SAL_CALL ModuleUIConfigurationManager::store()
1553{
1555
1556 if ( m_bDisposed )
1557 throw DisposedException();
1558
1559 if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly )
1560 return;
1561
1562 // Try to access our module sub folder
1563 for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
1564 {
1565 try
1566 {
1567 UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i];
1568
1569 if ( rElementType.bModified && rElementType.xStorage.is() )
1570 {
1571 impl_storeElementTypeData( rElementType.xStorage, rElementType );
1572 m_pStorageHandler[i]->commitUserChanges();
1573 }
1574 }
1575 catch ( const Exception& )
1576 {
1577 throw IOException();
1578 }
1579 }
1580
1581 m_bModified = false;
1582}
1583
1584void SAL_CALL ModuleUIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage )
1585{
1587
1588 if ( m_bDisposed )
1589 throw DisposedException();
1590
1591 if ( !m_xUserConfigStorage.is() || !m_bModified || m_bReadOnly )
1592 return;
1593
1594 // Try to access our module sub folder
1595 for ( int i = 1; i < css::ui::UIElementType::COUNT; i++ )
1596 {
1597 try
1598 {
1599 Reference< XStorage > xElementTypeStorage( Storage->openStorageElement(
1600 OUString(UIELEMENTTYPENAMES[i]), ElementModes::READWRITE ));
1601 UIElementType& rElementType = m_aUIElements[LAYER_USERDEFINED][i];
1602
1603 if ( rElementType.bModified && xElementTypeStorage.is() )
1604 impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag!
1605 }
1606 catch ( const Exception& )
1607 {
1608 throw IOException();
1609 }
1610 }
1611
1612 Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY );
1613 if ( xTransactedObject.is() )
1614 xTransactedObject->commit();
1615}
1616
1617sal_Bool SAL_CALL ModuleUIConfigurationManager::isModified()
1618{
1620
1621 return m_bModified;
1622}
1623
1624sal_Bool SAL_CALL ModuleUIConfigurationManager::isReadOnly()
1625{
1627
1628 return m_bReadOnly;
1629}
1630
1631void ModuleUIConfigurationManager::implts_notifyContainerListener( const ui::ConfigurationEvent& aEvent, NotifyOp eOp )
1632{
1633 std::unique_lock aGuard(m_mutex);
1634 using ListenerMethodType = void (SAL_CALL css::ui::XUIConfigurationListener::*)(const ui::ConfigurationEvent&);
1635 ListenerMethodType aListenerMethod {};
1636 switch ( eOp )
1637 {
1638 case NotifyOp_Replace:
1639 aListenerMethod = &css::ui::XUIConfigurationListener::elementReplaced;
1640 break;
1641 case NotifyOp_Insert:
1642 aListenerMethod = &css::ui::XUIConfigurationListener::elementInserted;
1643 break;
1644 case NotifyOp_Remove:
1645 aListenerMethod = &css::ui::XUIConfigurationListener::elementRemoved;
1646 break;
1647 }
1648 m_aConfigListeners.notifyEach(aGuard, aListenerMethod, aEvent);
1649}
1650
1651}
1652
1653extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface *
1655 css::uno::XComponentContext *context,
1656 css::uno::Sequence<css::uno::Any> const &arguments)
1657{
1658 return cppu::acquire(new ModuleUIConfigurationManager(context, arguments));
1659}
1660
1661/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
Reference< XInputStream > xStream
AnyEventRef aEvent
TValueType getUnpackedValueOrDefault(const OUString &sKey, const TValueType &aDefault) const
void StoreMenuBarConfigurationToXML(css::uno::Reference< css::container::XIndexAccess > const &rMenuBarConfiguration, css::uno::Reference< css::io::XOutputStream > const &rOutputStream, bool bIsMenuBar)
css::uno::Reference< css::container::XIndexAccess > CreateMenuBarConfigurationFromXML(css::uno::Reference< css::io::XInputStream > const &rInputStream)
float u
OUString sName
css::uno::Reference< css::uno::XComponentContext > m_xContext
bool m_bDisposed
bool m_bReadOnly
Sequence< PropertyValue > aArguments
sal_Int32 nIndex
OUString aName
sal_Int64 n
uno_Any a
std::mutex m_mutex
Definition: loadenv.cxx:108
#define SAL_WARN(area, stream)
constexpr OUStringLiteral RESOURCETYPE_POPUPMENU
SAL_DLLPUBLIC_EXPORT css::uno::XInterface * com_sun_star_comp_framework_ModuleUIConfigurationManager_get_implementation(css::uno::XComponentContext *context, css::uno::Sequence< css::uno::Any > const &arguments)
constexpr OUStringLiteral RESOURCETYPE_TOOLBAR
constexpr OUStringLiteral RESOURCETYPE_MENUBAR
constexpr OUStringLiteral RESOURCETYPE_STATUSBAR
@ Exception
css::uno::Sequence< css::uno::Any > InitAnyPropertySequence(::std::initializer_list< ::std::pair< OUString, css::uno::Any > > vInit)
css::uno::Sequence< DstElementType > containerToSequence(const SrcType &i_Container)
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
css::uno::Sequence< OUString > getSupportedServiceNames()
OUString getImplementationName()
bool CPPUHELPER_DLLPUBLIC supportsService(css::lang::XServiceInfo *implementation, rtl::OUString const &name)
Any SAL_CALL getCaughtException()
ElementType
int i
bool equalsIgnoreAsciiCase(std::u16string_view s1, std::u16string_view s2)
constexpr bool starts_with(std::basic_string_view< charT, traits > sv, std::basic_string_view< charT, traits > x) noexcept
void dispose()
long Long
QPRO_FUNC_TYPE nType
OUString aUIName
unsigned char sal_Bool
#define UIELEMENTTYPE_POPUPMENU_NAME
#define UIELEMENTTYPE_PROGRESSBAR_NAME
#define UIELEMENTTYPE_TOOLBAR_NAME
#define UIELEMENTTYPE_STATUSBAR_NAME
#define UIELEMENTTYPE_MENUBAR_NAME
#define UIELEMENTTYPE_TOOLPANEL_NAME
#define UIELEMENTTYPE_FLOATINGWINDOW_NAME