bump product version to 4.1.6.2
[LibreOffice.git] / framework / source / uiconfiguration / uiconfigurationmanager.cxx
bloba2469e087ceaeef491121df52a9190c6d0c0a9a0
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <uiconfiguration/uiconfigurationmanager.hxx>
21 #include <threadhelp/resetableguard.hxx>
22 #include <services.h>
23 #include <uielement/rootitemcontainer.hxx>
24 #include <uielement/constitemcontainer.hxx>
25 #include <uielement/uielementtypenames.hxx>
26 #include <framework/menuconfiguration.hxx>
27 #include <framework/toolboxconfiguration.hxx>
29 #include <framework/statusbarconfiguration.hxx>
31 #include <com/sun/star/beans/XPropertySet.hpp>
32 #include <com/sun/star/container/XNameAccess.hpp>
33 #include <com/sun/star/embed/ElementModes.hpp>
34 #include <com/sun/star/embed/XTransactedObject.hpp>
35 #include <com/sun/star/lang/XInitialization.hpp>
36 #include <com/sun/star/lang/DisposedException.hpp>
37 #include <com/sun/star/io/XStream.hpp>
38 #include <com/sun/star/ui/UIElementType.hpp>
39 #include <com/sun/star/ui/ConfigurationEvent.hpp>
40 #include <com/sun/star/ui/ConfigurationEvent.hpp>
42 #include <comphelper/componentcontext.hxx>
43 #include <vcl/svapp.hxx>
44 #include <rtl/ustrbuf.hxx>
46 //_________________________________________________________________________________________________________________
47 // namespaces
48 //_________________________________________________________________________________________________________________
50 using namespace com::sun::star::uno;
51 using namespace com::sun::star::io;
52 using namespace com::sun::star::embed;
53 using namespace com::sun::star::lang;
54 using namespace com::sun::star::container;
55 using namespace com::sun::star::beans;
56 using namespace ::com::sun::star::ui;
58 namespace framework
61 //*****************************************************************************************************************
62 // XInterface, XTypeProvider, XServiceInfo
63 //*****************************************************************************************************************
64 DEFINE_XINTERFACE_8 ( UIConfigurationManager ,
65 OWeakObject ,
66 DIRECT_INTERFACE( css::lang::XTypeProvider ),
67 DIRECT_INTERFACE( css::lang::XServiceInfo ),
68 DIRECT_INTERFACE( css::lang::XComponent ),
69 DIRECT_INTERFACE( css::ui::XUIConfiguration ),
70 DIRECT_INTERFACE( css::ui::XUIConfigurationManager ),
71 DIRECT_INTERFACE( css::ui::XUIConfigurationManager2 ),
72 DIRECT_INTERFACE( css::ui::XUIConfigurationPersistence ),
73 DIRECT_INTERFACE( css::ui::XUIConfigurationStorage )
76 DEFINE_XTYPEPROVIDER_8 ( UIConfigurationManager ,
77 css::lang::XTypeProvider ,
78 css::lang::XServiceInfo ,
79 css::lang::XComponent ,
80 css::ui::XUIConfiguration ,
81 css::ui::XUIConfigurationManager ,
82 css::ui::XUIConfigurationManager2 ,
83 css::ui::XUIConfigurationPersistence ,
84 css::ui::XUIConfigurationStorage
87 DEFINE_XSERVICEINFO_MULTISERVICE_2 ( UIConfigurationManager ,
88 ::cppu::OWeakObject ,
89 OUString("com.sun.star.ui.UIConfigurationManager"),
90 OUString("com.sun.star.comp.framework.UIConfigurationManager")
93 DEFINE_INIT_SERVICE ( UIConfigurationManager, {} )
96 // important: The order and position of the elements must match the constant
97 // definition of "::com::sun::star::ui::UIElementType"
98 static const char* UIELEMENTTYPENAMES[] =
100 "", // Dummy value for unknown!
101 UIELEMENTTYPE_MENUBAR_NAME,
102 UIELEMENTTYPE_POPUPMENU_NAME,
103 UIELEMENTTYPE_TOOLBAR_NAME,
104 UIELEMENTTYPE_STATUSBAR_NAME,
105 UIELEMENTTYPE_FLOATINGWINDOW_NAME,
106 UIELEMENTTYPE_PROGRESSBAR_NAME,
107 UIELEMENTTYPE_TOOLPANEL_NAME
110 static const char RESOURCEURL_PREFIX[] = "private:resource/";
111 static const sal_Int32 RESOURCEURL_PREFIX_SIZE = 17;
113 static sal_Int16 RetrieveTypeFromResourceURL( const OUString& aResourceURL )
116 if (( aResourceURL.indexOf( RESOURCEURL_PREFIX ) == 0 ) &&
117 ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE ))
119 OUString aTmpStr = aResourceURL.copy( RESOURCEURL_PREFIX_SIZE );
120 sal_Int32 nIndex = aTmpStr.indexOf( '/' );
121 if (( nIndex > 0 ) && ( aTmpStr.getLength() > nIndex ))
123 OUString aTypeStr( aTmpStr.copy( 0, nIndex ));
124 for ( int i = 0; i < UIElementType::COUNT; i++ )
126 if ( aTypeStr.equalsAscii( UIELEMENTTYPENAMES[i] ))
127 return sal_Int16( i );
132 return UIElementType::UNKNOWN;
135 static OUString RetrieveNameFromResourceURL( const OUString& aResourceURL )
137 if (( aResourceURL.indexOf( RESOURCEURL_PREFIX ) == 0 ) &&
138 ( aResourceURL.getLength() > RESOURCEURL_PREFIX_SIZE ))
140 sal_Int32 nIndex = aResourceURL.lastIndexOf( '/' );
141 if (( nIndex > 0 ) && (( nIndex+1 ) < aResourceURL.getLength()))
142 return aResourceURL.copy( nIndex+1 );
145 return OUString();
148 void UIConfigurationManager::impl_fillSequenceWithElementTypeInfo( UIElementInfoHashMap& aUIElementInfoCollection, sal_Int16 nElementType )
150 // preload list of element types on demand
151 impl_preloadUIElementTypeList( nElementType );
153 UIElementDataHashMap& rUserElements = m_aUIElements[nElementType].aElementsHashMap;
154 UIElementDataHashMap::const_iterator pUserIter = rUserElements.begin();
156 while ( pUserIter != rUserElements.end() )
158 UIElementData* pDataSettings = impl_findUIElementData( pUserIter->second.aResourceURL, nElementType );
159 if ( pDataSettings && !pDataSettings->bDefault )
161 // Retrieve user interface name from XPropertySet interface
162 OUString aUIName;
163 Reference< XPropertySet > xPropSet( pDataSettings->xSettings, UNO_QUERY );
164 if ( xPropSet.is() )
166 Any a = xPropSet->getPropertyValue( m_aPropUIName );
167 a >>= aUIName;
170 UIElementInfo aInfo( pUserIter->second.aResourceURL, aUIName );
171 aUIElementInfoCollection.insert( UIElementInfoHashMap::value_type( pUserIter->second.aResourceURL, aInfo ));
173 ++pUserIter;
177 void UIConfigurationManager::impl_preloadUIElementTypeList( sal_Int16 nElementType )
179 UIElementType& rElementTypeData = m_aUIElements[nElementType];
181 if ( !rElementTypeData.bLoaded )
183 Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
184 if ( xElementTypeStorage.is() )
186 OUStringBuffer aBuf( RESOURCEURL_PREFIX_SIZE );
187 aBuf.appendAscii( RESOURCEURL_PREFIX );
188 aBuf.appendAscii( UIELEMENTTYPENAMES[ nElementType ] );
189 aBuf.appendAscii( "/" );
190 OUString aResURLPrefix( aBuf.makeStringAndClear() );
192 UIElementDataHashMap& rHashMap = rElementTypeData.aElementsHashMap;
193 Reference< XNameAccess > xNameAccess( xElementTypeStorage, UNO_QUERY );
194 Sequence< OUString > aUIElementNames = xNameAccess->getElementNames();
195 for ( sal_Int32 n = 0; n < aUIElementNames.getLength(); n++ )
197 UIElementData aUIElementData;
199 // Resource name must be without ".xml"
200 sal_Int32 nIndex = aUIElementNames[n].lastIndexOf( '.' );
201 if (( nIndex > 0 ) && ( nIndex < aUIElementNames[n].getLength() ))
203 OUString aExtension( aUIElementNames[n].copy( nIndex+1 ));
204 OUString aUIElementName( aUIElementNames[n].copy( 0, nIndex ));
206 if (!aUIElementName.isEmpty() &&
207 ( aExtension.equalsIgnoreAsciiCase("xml")))
209 aUIElementData.aResourceURL = aResURLPrefix + aUIElementName;
210 aUIElementData.aName = aUIElementNames[n];
211 aUIElementData.bModified = false;
212 aUIElementData.bDefault = false;
214 // Create boost::unordered_map entries for all user interface elements inside the storage. We don't load the
215 // settings to speed up the process.
216 rHashMap.insert( UIElementDataHashMap::value_type( aUIElementData.aResourceURL, aUIElementData ));
223 rElementTypeData.bLoaded = true;
226 void UIConfigurationManager::impl_requestUIElementData( sal_Int16 nElementType, UIElementData& aUIElementData )
228 UIElementType& rElementTypeData = m_aUIElements[nElementType];
230 Reference< XStorage > xElementTypeStorage = rElementTypeData.xStorage;
231 if ( xElementTypeStorage.is() && !aUIElementData.aName.isEmpty() )
235 Reference< XStream > xStream = xElementTypeStorage->openStreamElement( aUIElementData.aName, ElementModes::READ );
236 Reference< XInputStream > xInputStream = xStream->getInputStream();
238 if ( xInputStream.is() )
240 switch ( nElementType )
242 case ::com::sun::star::ui::UIElementType::UNKNOWN:
243 break;
245 case ::com::sun::star::ui::UIElementType::MENUBAR:
249 MenuConfiguration aMenuCfg( m_xContext );
250 Reference< XIndexAccess > xContainer( aMenuCfg.CreateMenuBarConfigurationFromXML( xInputStream ));
251 RootItemContainer* pRootItemContainer = RootItemContainer::GetImplementation( xContainer );
252 if ( pRootItemContainer )
253 aUIElementData.xSettings = Reference< XIndexAccess >( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, sal_True ) ), UNO_QUERY );
254 else
255 aUIElementData.xSettings = Reference< XIndexAccess >( static_cast< OWeakObject * >( new ConstItemContainer( xContainer, sal_True ) ), UNO_QUERY );
256 return;
258 catch ( const ::com::sun::star::lang::WrappedTargetException& )
262 break;
264 case ::com::sun::star::ui::UIElementType::POPUPMENU:
266 break;
269 case ::com::sun::star::ui::UIElementType::TOOLBAR:
273 Reference< XIndexContainer > xIndexContainer( static_cast< OWeakObject * >( new RootItemContainer() ), UNO_QUERY );
274 ToolBoxConfiguration::LoadToolBox( m_xContext, xInputStream, xIndexContainer );
275 RootItemContainer* pRootItemContainer = RootItemContainer::GetImplementation( xIndexContainer );
276 aUIElementData.xSettings = Reference< XIndexAccess >( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, sal_True ) ), UNO_QUERY );
277 return;
279 catch ( const ::com::sun::star::lang::WrappedTargetException& )
283 break;
286 case ::com::sun::star::ui::UIElementType::STATUSBAR:
290 Reference< XIndexContainer > xIndexContainer( static_cast< OWeakObject * >( new RootItemContainer() ), UNO_QUERY );
291 StatusBarConfiguration::LoadStatusBar( m_xContext, xInputStream, xIndexContainer );
292 RootItemContainer* pRootItemContainer = RootItemContainer::GetImplementation( xIndexContainer );
293 aUIElementData.xSettings = Reference< XIndexAccess >( static_cast< OWeakObject * >( new ConstItemContainer( pRootItemContainer, sal_True ) ), UNO_QUERY );
294 return;
296 catch ( const ::com::sun::star::lang::WrappedTargetException& )
300 break;
303 case ::com::sun::star::ui::UIElementType::FLOATINGWINDOW:
305 break;
310 catch ( const ::com::sun::star::embed::InvalidStorageException& )
313 catch ( const ::com::sun::star::lang::IllegalArgumentException& )
316 catch ( const ::com::sun::star::io::IOException& )
319 catch ( const ::com::sun::star::embed::StorageWrappedTargetException& )
324 // At least we provide an empty settings container!
325 aUIElementData.xSettings = Reference< XIndexAccess >( static_cast< OWeakObject * >( new ConstItemContainer()), UNO_QUERY );
328 UIConfigurationManager::UIElementData* UIConfigurationManager::impl_findUIElementData( const OUString& aResourceURL, sal_Int16 nElementType, bool bLoad )
330 // preload list of element types on demand
331 impl_preloadUIElementTypeList( nElementType );
333 // try to look into our document vector/boost::unordered_map combination
334 UIElementDataHashMap& rUserHashMap = m_aUIElements[nElementType].aElementsHashMap;
335 UIElementDataHashMap::iterator pIter = rUserHashMap.find( aResourceURL );
336 if ( pIter != rUserHashMap.end() )
338 // Default data settings data means removed!
339 if ( pIter->second.bDefault )
340 return &(pIter->second);
341 else
343 if ( !pIter->second.xSettings.is() && bLoad )
344 impl_requestUIElementData( nElementType, pIter->second );
345 return &(pIter->second);
349 // Nothing has been found!
350 return NULL;
353 void UIConfigurationManager::impl_storeElementTypeData( Reference< XStorage >& xStorage, UIElementType& rElementType, bool bResetModifyState )
355 UIElementDataHashMap& rHashMap = rElementType.aElementsHashMap;
356 UIElementDataHashMap::iterator pIter = rHashMap.begin();
358 while ( pIter != rHashMap.end() )
360 UIElementData& rElement = pIter->second;
361 if ( rElement.bModified )
363 if ( rElement.bDefault )
365 xStorage->removeElement( rElement.aName );
366 rElement.bModified = sal_False; // mark as not modified
368 else
370 Reference< XStream > xStream( xStorage->openStreamElement( rElement.aName, ElementModes::WRITE|ElementModes::TRUNCATE ), UNO_QUERY );
371 Reference< XOutputStream > xOutputStream( xStream->getOutputStream() );
373 if ( xOutputStream.is() )
375 switch( rElementType.nElementType )
377 case ::com::sun::star::ui::UIElementType::MENUBAR:
381 MenuConfiguration aMenuCfg( m_xContext );
382 aMenuCfg.StoreMenuBarConfigurationToXML( rElement.xSettings, xOutputStream );
384 catch ( const ::com::sun::star::lang::WrappedTargetException& )
388 break;
390 case ::com::sun::star::ui::UIElementType::TOOLBAR:
394 ToolBoxConfiguration::StoreToolBox( m_xContext, xOutputStream, rElement.xSettings );
396 catch ( const ::com::sun::star::lang::WrappedTargetException& )
400 break;
402 case ::com::sun::star::ui::UIElementType::STATUSBAR:
406 StatusBarConfiguration::StoreStatusBar( m_xContext, xOutputStream, rElement.xSettings );
408 catch ( const ::com::sun::star::lang::WrappedTargetException& )
412 break;
414 default:
415 break;
419 // mark as not modified if we store to our own storage
420 if ( bResetModifyState )
421 rElement.bModified = sal_False;
425 ++pIter;
428 // commit element type storage
429 Reference< XTransactedObject > xTransactedObject( xStorage, UNO_QUERY );
430 if ( xTransactedObject.is() )
431 xTransactedObject->commit();
433 // mark UIElementType as not modified if we store to our own storage
434 if ( bResetModifyState )
435 rElementType.bModified = sal_False;
438 void UIConfigurationManager::impl_resetElementTypeData(
439 UIElementType& rDocElementType,
440 ConfigEventNotifyContainer& rRemoveNotifyContainer )
442 UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap;
443 UIElementDataHashMap::iterator pIter = rHashMap.begin();
445 Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY );
446 Reference< XInterface > xIfac( xThis, UNO_QUERY );
448 // Make copies of the event structures to be thread-safe. We have to unlock our mutex before calling
449 // our listeners!
450 while ( pIter != rHashMap.end() )
452 UIElementData& rElement = pIter->second;
453 if ( !rElement.bDefault )
455 // Remove user-defined settings from document
456 ConfigurationEvent aEvent;
457 aEvent.ResourceURL = rElement.aResourceURL;
458 aEvent.Accessor <<= xThis;
459 aEvent.Source = xIfac;
460 aEvent.Element <<= rElement.xSettings;
462 rRemoveNotifyContainer.push_back( aEvent );
464 // Mark element as default.
465 rElement.bModified = false;
466 rElement.bDefault = true;
468 else
469 rElement.bModified = false;
471 ++pIter;
474 // Remove all settings from our user interface elements
475 rHashMap.clear();
478 void UIConfigurationManager::impl_reloadElementTypeData(
479 UIElementType& rDocElementType,
480 ConfigEventNotifyContainer& rRemoveNotifyContainer,
481 ConfigEventNotifyContainer& rReplaceNotifyContainer )
483 UIElementDataHashMap& rHashMap = rDocElementType.aElementsHashMap;
484 UIElementDataHashMap::iterator pIter = rHashMap.begin();
485 Reference< XStorage > xElementStorage( rDocElementType.xStorage );
486 Reference< XNameAccess > xElementNameAccess( xElementStorage, UNO_QUERY );
488 Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY );
489 Reference< XInterface > xIfac( xThis, UNO_QUERY );
490 sal_Int16 nType = rDocElementType.nElementType;
492 while ( pIter != rHashMap.end() )
494 UIElementData& rElement = pIter->second;
495 if ( rElement.bModified )
497 if ( xElementNameAccess->hasByName( rElement.aName ))
499 // Replace settings with data from user layer
500 Reference< XIndexAccess > xOldSettings( rElement.xSettings );
502 impl_requestUIElementData( nType, rElement );
504 ConfigurationEvent aReplaceEvent;
506 aReplaceEvent.ResourceURL = rElement.aResourceURL;
507 aReplaceEvent.Accessor <<= xThis;
508 aReplaceEvent.Source = xIfac;
509 aReplaceEvent.ReplacedElement <<= xOldSettings;
510 aReplaceEvent.Element <<= rElement.xSettings;
511 rReplaceNotifyContainer.push_back( aReplaceEvent );
513 rElement.bModified = false;
515 else
517 // Element settings are not in any storage => remove
518 ConfigurationEvent aRemoveEvent;
520 aRemoveEvent.ResourceURL = rElement.aResourceURL;
521 aRemoveEvent.Accessor <<= xThis;
522 aRemoveEvent.Source = xIfac;
523 aRemoveEvent.Element <<= rElement.xSettings;
525 rRemoveNotifyContainer.push_back( aRemoveEvent );
527 // Mark element as default and not modified. That means "not active" in the document anymore
528 rElement.bModified = false;
529 rElement.bDefault = true;
532 ++pIter;
535 rDocElementType.bModified = sal_False;
538 void UIConfigurationManager::impl_Initialize()
540 // Initialize the top-level structures with the storage data
541 if ( m_xDocConfigStorage.is() )
543 long nModes = m_bReadOnly ? ElementModes::READ : ElementModes::READWRITE;
545 // Try to access our module sub folder
546 for ( sal_Int16 i = 1; i < ::com::sun::star::ui::UIElementType::COUNT;
547 i++ )
549 Reference< XStorage > xElementTypeStorage;
552 xElementTypeStorage = m_xDocConfigStorage->openStorageElement( OUString::createFromAscii( UIELEMENTTYPENAMES[i] ), nModes );
554 catch ( const com::sun::star::container::NoSuchElementException& )
557 catch ( const ::com::sun::star::embed::InvalidStorageException& )
560 catch ( const ::com::sun::star::lang::IllegalArgumentException& )
563 catch ( const ::com::sun::star::io::IOException& )
566 catch ( const ::com::sun::star::embed::StorageWrappedTargetException& )
570 m_aUIElements[i].nElementType = i;
571 m_aUIElements[i].bModified = false;
572 m_aUIElements[i].xStorage = xElementTypeStorage;
573 m_aUIElements[i].bDefaultLayer = false;
576 else
578 // We have no storage, just initialize ui element types with empty storage!
579 for ( int i = 1; i < ::com::sun::star::ui::UIElementType::COUNT; i++ )
580 m_aUIElements[i].xStorage = m_xDocConfigStorage;
584 UIConfigurationManager::UIConfigurationManager( const com::sun::star::uno::Reference< com::sun::star::uno::XComponentContext > & rxContext ) :
585 ThreadHelpBase( &Application::GetSolarMutex() )
586 , m_xDocConfigStorage( 0 )
587 , m_bReadOnly( true )
588 , m_bModified( false )
589 , m_bConfigRead( false )
590 , m_bDisposed( false )
591 , m_aXMLPostfix( ".xml" )
592 , m_aPropUIName( "UIName" )
593 , m_aPropResourceURL( "ResourceURL" )
594 , m_xContext( rxContext )
595 , m_aListenerContainer( m_aLock.getShareableOslMutex() )
597 // Make sure we have a default initialized entry for every layer and user interface element type!
598 // The following code depends on this!
599 m_aUIElements.resize( ::com::sun::star::ui::UIElementType::COUNT );
602 UIConfigurationManager::~UIConfigurationManager()
606 // XComponent
607 void SAL_CALL UIConfigurationManager::dispose() throw (::com::sun::star::uno::RuntimeException)
609 Reference< XComponent > xThis( static_cast< OWeakObject* >(this), UNO_QUERY );
611 css::lang::EventObject aEvent( xThis );
612 m_aListenerContainer.disposeAndClear( aEvent );
615 ResetableGuard aGuard( m_aLock );
618 if ( m_xImageManager.is() )
619 m_xImageManager->dispose();
621 catch ( const Exception& )
625 m_xImageManager.clear();
626 m_aUIElements.clear();
627 m_xDocConfigStorage.clear();
628 m_bConfigRead = false;
629 m_bModified = false;
630 m_bDisposed = true;
634 void SAL_CALL UIConfigurationManager::addEventListener( const Reference< XEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException)
637 ResetableGuard aGuard( m_aLock );
639 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
640 if ( m_bDisposed )
641 throw DisposedException();
644 m_aListenerContainer.addInterface( ::getCppuType( ( const Reference< XEventListener >* ) NULL ), xListener );
647 void SAL_CALL UIConfigurationManager::removeEventListener( const Reference< XEventListener >& xListener ) throw (::com::sun::star::uno::RuntimeException)
649 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
650 m_aListenerContainer.removeInterface( ::getCppuType( ( const Reference< XEventListener >* ) NULL ), xListener );
653 // XUIConfigurationManager
654 void SAL_CALL UIConfigurationManager::addConfigurationListener( const Reference< ::com::sun::star::ui::XUIConfigurationListener >& xListener ) throw (::com::sun::star::uno::RuntimeException)
657 ResetableGuard aGuard( m_aLock );
659 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
660 if ( m_bDisposed )
661 throw DisposedException();
664 m_aListenerContainer.addInterface( ::getCppuType( ( const Reference< XUIConfigurationListener >* ) NULL ), xListener );
667 void SAL_CALL UIConfigurationManager::removeConfigurationListener( const Reference< ::com::sun::star::ui::XUIConfigurationListener >& xListener ) throw (::com::sun::star::uno::RuntimeException)
669 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
670 m_aListenerContainer.removeInterface( ::getCppuType( ( const Reference< XUIConfigurationListener >* ) NULL ), xListener );
674 void SAL_CALL UIConfigurationManager::reset() throw (::com::sun::star::uno::RuntimeException)
676 ResetableGuard aGuard( m_aLock );
678 /* SAFE AREA ----------------------------------------------------------------------------------------------- */
679 if ( m_bDisposed )
680 throw DisposedException();
682 if ( isReadOnly() )
683 return;
685 if ( m_xDocConfigStorage.is() )
689 // Remove all elements from our user-defined storage!
690 bool bCommit( false );
691 for ( int i = 1; i < ::com::sun::star::ui::UIElementType::COUNT; i++ )
693 UIElementType& rElementType = m_aUIElements[i];
694 Reference< XStorage > xSubStorage( rElementType.xStorage, UNO_QUERY );
696 if ( xSubStorage.is() )
698 bool bCommitSubStorage( false );
699 Reference< XNameAccess > xSubStorageNameAccess( xSubStorage, UNO_QUERY );
700 Sequence< OUString > aUIElementStreamNames = xSubStorageNameAccess->getElementNames();
701 for ( sal_Int32 j = 0; j < aUIElementStreamNames.getLength(); j++ )
703 xSubStorage->removeElement( aUIElementStreamNames[j] );
704 bCommitSubStorage = true;
705 bCommit = true;
708 if ( bCommitSubStorage )
710 Reference< XTransactedObject > xTransactedObject( xSubStorage, UNO_QUERY );
711 if ( xTransactedObject.is() )
712 xTransactedObject->commit();
717 // Commit changes
718 if ( bCommit )
720 Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY );
721 if ( xTransactedObject.is() )
722 xTransactedObject->commit();
725 // remove settings from user defined layer and notify listener about removed settings data!
726 // Try to access our module sub folder
727 ConfigEventNotifyContainer aRemoveEventNotifyContainer;
728 for ( sal_Int16 j = 1; j < ::com::sun::star::ui::UIElementType::COUNT; j++ )
730 UIElementType& rDocElementType = m_aUIElements[j];
732 impl_resetElementTypeData( rDocElementType, aRemoveEventNotifyContainer );
733 rDocElementType.bModified = sal_False;
736 m_bModified = sal_False;
738 // Unlock mutex before notify our listeners
739 aGuard.unlock();
741 // Notify our listeners
742 for ( sal_uInt32 k = 0; k < aRemoveEventNotifyContainer.size(); k++ )
743 implts_notifyContainerListener( aRemoveEventNotifyContainer[k], NotifyOp_Remove );
745 catch ( const ::com::sun::star::lang::IllegalArgumentException& )
748 catch ( const ::com::sun::star::container::NoSuchElementException& )
751 catch ( const ::com::sun::star::embed::InvalidStorageException& )
754 catch ( const ::com::sun::star::embed::StorageWrappedTargetException& )
760 Sequence< Sequence< PropertyValue > > SAL_CALL UIConfigurationManager::getUIElementsInfo( sal_Int16 ElementType )
761 throw ( IllegalArgumentException, RuntimeException )
763 if (( ElementType < 0 ) || ( ElementType >= ::com::sun::star::ui::UIElementType::COUNT ))
764 throw IllegalArgumentException();
766 ResetableGuard aGuard( m_aLock );
767 if ( m_bDisposed )
768 throw DisposedException();
770 Sequence< Sequence< PropertyValue > > aElementInfoSeq;
771 UIElementInfoHashMap aUIElementInfoCollection;
773 if ( ElementType == ::com::sun::star::ui::UIElementType::UNKNOWN )
775 for ( sal_Int16 i = 0; i < ::com::sun::star::ui::UIElementType::COUNT; i++ )
776 impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, sal_Int16( i ) );
778 else
779 impl_fillSequenceWithElementTypeInfo( aUIElementInfoCollection, ElementType );
781 Sequence< PropertyValue > aUIElementInfo( 2 );
782 aUIElementInfo[0].Name = m_aPropResourceURL;
783 aUIElementInfo[1].Name = m_aPropUIName;
785 aElementInfoSeq.realloc( aUIElementInfoCollection.size() );
786 UIElementInfoHashMap::const_iterator pIter = aUIElementInfoCollection.begin();
788 sal_Int32 n = 0;
789 while ( pIter != aUIElementInfoCollection.end() )
791 aUIElementInfo[0].Value <<= pIter->second.aResourceURL;
792 aUIElementInfo[1].Value <<= pIter->second.aUIName;
793 aElementInfoSeq[n++] = aUIElementInfo;
794 ++pIter;
797 return aElementInfoSeq;
800 Reference< XIndexContainer > SAL_CALL UIConfigurationManager::createSettings() throw (::com::sun::star::uno::RuntimeException)
802 ResetableGuard aGuard( m_aLock );
804 if ( m_bDisposed )
805 throw DisposedException();
807 // Creates an empty item container which can be filled from outside
808 return Reference< XIndexContainer >( static_cast< OWeakObject * >( new RootItemContainer()), UNO_QUERY );
811 sal_Bool SAL_CALL UIConfigurationManager::hasSettings( const OUString& ResourceURL )
812 throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
814 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
816 if (( nElementType == ::com::sun::star::ui::UIElementType::UNKNOWN ) ||
817 ( nElementType >= ::com::sun::star::ui::UIElementType::COUNT ))
818 throw IllegalArgumentException();
819 else
821 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType, false );
822 if ( pDataSettings && !pDataSettings->bDefault )
823 return sal_True;
826 return sal_False;
829 Reference< XIndexAccess > SAL_CALL UIConfigurationManager::getSettings( const OUString& ResourceURL, sal_Bool bWriteable )
830 throw (::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::uno::RuntimeException)
832 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
834 if (( nElementType == ::com::sun::star::ui::UIElementType::UNKNOWN ) ||
835 ( nElementType >= ::com::sun::star::ui::UIElementType::COUNT ))
836 throw IllegalArgumentException();
837 else
839 ResetableGuard aGuard( m_aLock );
841 if ( m_bDisposed )
842 throw DisposedException();
844 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
845 if ( pDataSettings && !pDataSettings->bDefault )
847 // Create a copy of our data if someone wants to change the data.
848 if ( bWriteable )
849 return Reference< XIndexAccess >( static_cast< OWeakObject * >( new RootItemContainer( pDataSettings->xSettings ) ), UNO_QUERY );
850 else
851 return pDataSettings->xSettings;
855 throw NoSuchElementException();
858 void SAL_CALL UIConfigurationManager::replaceSettings( const OUString& ResourceURL, const Reference< ::com::sun::star::container::XIndexAccess >& aNewData )
859 throw (::com::sun::star::container::NoSuchElementException, ::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::IllegalAccessException, ::com::sun::star::uno::RuntimeException)
861 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
863 if (( nElementType == ::com::sun::star::ui::UIElementType::UNKNOWN ) ||
864 ( nElementType >= ::com::sun::star::ui::UIElementType::COUNT ))
865 throw IllegalArgumentException();
866 else if ( m_bReadOnly )
867 throw IllegalAccessException();
868 else
870 ResetableGuard aGuard( m_aLock );
872 if ( m_bDisposed )
873 throw DisposedException();
875 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
876 if ( pDataSettings && !pDataSettings->bDefault )
878 // we have a settings entry in our user-defined layer - replace
879 Reference< XIndexAccess > xOldSettings = pDataSettings->xSettings;
881 // Create a copy of the data if the container is not const
882 Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
883 if ( xReplace.is() )
884 pDataSettings->xSettings = Reference< XIndexAccess >( static_cast< OWeakObject * >( new ConstItemContainer( aNewData ) ), UNO_QUERY );
885 else
886 pDataSettings->xSettings = aNewData;
888 pDataSettings->bDefault = false;
889 pDataSettings->bModified = true;
890 m_bModified = true;
892 // Modify type container
893 UIElementType& rElementType = m_aUIElements[nElementType];
894 rElementType.bModified = true;
896 Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY );
898 // Create event to notify listener about replaced element settings
899 ConfigurationEvent aEvent;
900 Reference< XInterface > xIfac( xThis, UNO_QUERY );
902 aEvent.ResourceURL = ResourceURL;
903 aEvent.Accessor <<= xThis;
904 aEvent.Source = xIfac;
905 aEvent.ReplacedElement <<= xOldSettings;
906 aEvent.Element <<= pDataSettings->xSettings;
908 aGuard.unlock();
910 implts_notifyContainerListener( aEvent, NotifyOp_Replace );
912 else
913 throw NoSuchElementException();
917 void SAL_CALL UIConfigurationManager::removeSettings( const OUString& ResourceURL )
918 throw ( NoSuchElementException, IllegalArgumentException, IllegalAccessException, RuntimeException)
920 sal_Int16 nElementType = RetrieveTypeFromResourceURL( ResourceURL );
922 if (( nElementType == ::com::sun::star::ui::UIElementType::UNKNOWN ) ||
923 ( nElementType >= ::com::sun::star::ui::UIElementType::COUNT ))
924 throw IllegalArgumentException();
925 else if ( m_bReadOnly )
926 throw IllegalAccessException();
927 else
929 ResetableGuard aGuard( m_aLock );
931 if ( m_bDisposed )
932 throw DisposedException();
934 UIElementData* pDataSettings = impl_findUIElementData( ResourceURL, nElementType );
935 if ( pDataSettings )
937 // If element settings are default, we don't need to change anything!
938 if ( pDataSettings->bDefault )
939 return;
940 else
942 Reference< XIndexAccess > xRemovedSettings = pDataSettings->xSettings;
943 pDataSettings->bDefault = true;
945 // check if this is a default layer node
946 pDataSettings->bModified = true; // we have to remove this node from the user layer!
947 pDataSettings->xSettings.clear();
948 m_bModified = true; // user layer must be written
950 // Modify type container
951 UIElementType& rElementType = m_aUIElements[nElementType];
952 rElementType.bModified = true;
954 Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY );
955 Reference< XInterface > xIfac( xThis, UNO_QUERY );
957 // Create event to notify listener about removed element settings
958 ConfigurationEvent aEvent;
960 aEvent.ResourceURL = ResourceURL;
961 aEvent.Accessor <<= xThis;
962 aEvent.Source = xIfac;
963 aEvent.Element <<= xRemovedSettings;
965 aGuard.unlock();
967 implts_notifyContainerListener( aEvent, NotifyOp_Remove );
970 else
971 throw NoSuchElementException();
975 void SAL_CALL UIConfigurationManager::insertSettings( const OUString& NewResourceURL, const Reference< XIndexAccess >& aNewData )
976 throw ( ElementExistException, IllegalArgumentException, IllegalAccessException, RuntimeException )
978 sal_Int16 nElementType = RetrieveTypeFromResourceURL( NewResourceURL );
980 if (( nElementType == ::com::sun::star::ui::UIElementType::UNKNOWN ) ||
981 ( nElementType >= ::com::sun::star::ui::UIElementType::COUNT ))
982 throw IllegalArgumentException();
983 else if ( m_bReadOnly )
984 throw IllegalAccessException();
985 else
987 ResetableGuard aGuard( m_aLock );
989 if ( m_bDisposed )
990 throw DisposedException();
992 bool bInsertData( false );
993 UIElementData aUIElementData;
994 UIElementData* pDataSettings = impl_findUIElementData( NewResourceURL, nElementType );
996 if ( pDataSettings && !pDataSettings->bDefault )
997 throw ElementExistException();
999 if ( !pDataSettings )
1001 pDataSettings = &aUIElementData;
1002 bInsertData = true;
1006 pDataSettings->bDefault = false;
1007 pDataSettings->bModified = true;
1009 // Create a copy of the data if the container is not const
1010 Reference< XIndexReplace > xReplace( aNewData, UNO_QUERY );
1011 if ( xReplace.is() )
1012 pDataSettings->xSettings = Reference< XIndexAccess >( static_cast< OWeakObject * >( new ConstItemContainer( aNewData ) ), UNO_QUERY );
1013 else
1014 pDataSettings->xSettings = aNewData;
1016 m_bModified = true;
1018 UIElementType& rElementType = m_aUIElements[nElementType];
1019 rElementType.bModified = true;
1021 if ( bInsertData )
1023 pDataSettings->aName = RetrieveNameFromResourceURL( NewResourceURL ) + m_aXMLPostfix;
1024 pDataSettings->aResourceURL = NewResourceURL;
1026 UIElementDataHashMap& rElements = rElementType.aElementsHashMap;
1027 rElements.insert( UIElementDataHashMap::value_type( NewResourceURL, *pDataSettings ));
1030 Reference< XIndexAccess > xInsertSettings( aUIElementData.xSettings );
1031 Reference< XUIConfigurationManager > xThis( static_cast< OWeakObject* >( this ), UNO_QUERY );
1032 Reference< XInterface > xIfac( xThis, UNO_QUERY );
1034 // Create event to notify listener about removed element settings
1035 ConfigurationEvent aEvent;
1037 aEvent.ResourceURL = NewResourceURL;
1038 aEvent.Accessor <<= xThis;
1039 aEvent.Source = xIfac;
1040 aEvent.Element <<= xInsertSettings;
1042 aGuard.unlock();
1044 implts_notifyContainerListener( aEvent, NotifyOp_Insert );
1049 Reference< XInterface > SAL_CALL UIConfigurationManager::getImageManager() throw (::com::sun::star::uno::RuntimeException)
1051 if ( m_bDisposed )
1052 throw DisposedException();
1054 if ( !m_xImageManager.is() )
1056 Reference<XMultiServiceFactory> xMSF(m_xContext->getServiceManager(), UNO_QUERY_THROW);
1058 m_xImageManager = Reference< XComponent >( static_cast< cppu::OWeakObject *>( new ImageManager( xMSF )),
1059 UNO_QUERY );
1060 Reference< XInitialization > xInit( m_xImageManager, UNO_QUERY );
1062 Sequence< Any > aPropSeq( 2 );
1063 PropertyValue aPropValue;
1064 aPropValue.Name = OUString( "UserConfigStorage" );
1065 aPropValue.Value = makeAny( m_xDocConfigStorage );
1066 aPropSeq[0] = makeAny( aPropValue );
1067 aPropValue.Name = OUString( "ModuleIdentifier" );
1068 aPropValue.Value = makeAny( m_aModuleIdentifier );
1069 aPropSeq[1] = makeAny( aPropValue );
1071 xInit->initialize( aPropSeq );
1074 return Reference< XInterface >( m_xImageManager, UNO_QUERY );
1077 Reference< XInterface > SAL_CALL UIConfigurationManager::getShortCutManager() throw (::com::sun::star::uno::RuntimeException)
1079 // SAFE ->
1080 ResetableGuard aGuard( m_aLock );
1082 if (m_xAccConfig.is())
1083 return m_xAccConfig;
1085 Reference<XMultiServiceFactory> xSMGR(m_xContext->getServiceManager(), UNO_QUERY_THROW);
1086 Reference< XStorage > xDocumentRoot = m_xDocConfigStorage;
1088 aGuard.unlock();
1089 // <- SAFE
1091 Reference< XInterface > xAccConfig = xSMGR->createInstance(SERVICENAME_DOCUMENTACCELERATORCONFIGURATION);
1092 Reference< XInitialization > xInit (xAccConfig, UNO_QUERY_THROW);
1094 PropertyValue aProp;
1095 aProp.Name = OUString("DocumentRoot");
1096 aProp.Value <<= xDocumentRoot;
1098 Sequence< Any > lArgs(1);
1099 lArgs[0] <<= aProp;
1101 xInit->initialize(lArgs);
1103 // SAFE ->
1104 aGuard.lock();
1105 m_xAccConfig = xAccConfig;
1106 aGuard.unlock();
1107 // <- SAFE
1109 return xAccConfig;
1112 Reference< XInterface > SAL_CALL UIConfigurationManager::getEventsManager() throw (::com::sun::star::uno::RuntimeException)
1114 return Reference< XInterface >();
1117 // XUIConfigurationStorage
1118 void SAL_CALL UIConfigurationManager::setStorage( const Reference< XStorage >& Storage ) throw (::com::sun::star::uno::RuntimeException)
1120 ResetableGuard aGuard( m_aLock );
1122 if ( m_bDisposed )
1123 throw DisposedException();
1125 if ( m_xDocConfigStorage.is() )
1129 // Dispose old storage to be sure that it will be closed
1130 Reference< XComponent > xComponent( m_xDocConfigStorage, UNO_QUERY );
1131 if ( xComponent.is() )
1132 xComponent->dispose();
1134 catch ( const Exception& )
1139 // We store the new storage. Be careful it could be an empty reference!
1140 m_xDocConfigStorage = Storage;
1141 m_bReadOnly = sal_True;
1143 Reference< XUIConfigurationStorage > xAccUpdate(m_xAccConfig, UNO_QUERY);
1144 if ( xAccUpdate.is() )
1145 xAccUpdate->setStorage( m_xDocConfigStorage );
1147 if ( m_xImageManager.is() )
1149 ImageManager* pImageManager = (ImageManager*)m_xImageManager.get();
1150 if ( pImageManager )
1151 pImageManager->setStorage( m_xDocConfigStorage );
1154 if ( m_xDocConfigStorage.is() )
1156 Reference< XPropertySet > xPropSet( m_xDocConfigStorage, UNO_QUERY );
1157 if ( xPropSet.is() )
1161 long nOpenMode = 0;
1162 Any a = xPropSet->getPropertyValue( OUString( "OpenMode" ));
1163 if ( a >>= nOpenMode )
1164 m_bReadOnly = !( nOpenMode & ElementModes::WRITE );
1166 catch ( const com::sun::star::beans::UnknownPropertyException& )
1169 catch ( const com::sun::star::lang::WrappedTargetException& )
1175 impl_Initialize();
1178 sal_Bool SAL_CALL UIConfigurationManager::hasStorage() throw (::com::sun::star::uno::RuntimeException)
1180 ResetableGuard aGuard( m_aLock );
1182 if ( m_bDisposed )
1183 throw DisposedException();
1185 return ( m_xDocConfigStorage.is() );
1188 // XUIConfigurationPersistence
1189 void SAL_CALL UIConfigurationManager::reload() throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException)
1191 ResetableGuard aGuard( m_aLock );
1193 if ( m_bDisposed )
1194 throw DisposedException();
1196 if ( m_xDocConfigStorage.is() && m_bModified && !m_bReadOnly )
1198 // Try to access our module sub folder
1199 ConfigEventNotifyContainer aRemoveNotifyContainer;
1200 ConfigEventNotifyContainer aReplaceNotifyContainer;
1201 for ( sal_Int16 i = 1; i < ::com::sun::star::ui::UIElementType::COUNT; i++ )
1205 UIElementType& rDocElementType = m_aUIElements[i];
1206 if ( rDocElementType.bModified )
1207 impl_reloadElementTypeData( rDocElementType, aRemoveNotifyContainer, aReplaceNotifyContainer );
1209 catch ( const Exception& )
1211 throw IOException();
1215 m_bModified = sal_False;
1217 // Unlock mutex before notify our listeners
1218 aGuard.unlock();
1220 // Notify our listeners
1221 for ( sal_uInt32 j = 0; j < aRemoveNotifyContainer.size(); j++ )
1222 implts_notifyContainerListener( aRemoveNotifyContainer[j], NotifyOp_Remove );
1223 for ( sal_uInt32 k = 0; k < aReplaceNotifyContainer.size(); k++ )
1224 implts_notifyContainerListener( aReplaceNotifyContainer[k], NotifyOp_Replace );
1228 void SAL_CALL UIConfigurationManager::store() throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException)
1230 ResetableGuard aGuard( m_aLock );
1232 if ( m_bDisposed )
1233 throw DisposedException();
1235 if ( m_xDocConfigStorage.is() && m_bModified && !m_bReadOnly )
1237 // Try to access our module sub folder
1238 for ( int i = 1; i < ::com::sun::star::ui::UIElementType::COUNT; i++ )
1242 UIElementType& rElementType = m_aUIElements[i];
1243 Reference< XStorage > xStorage( rElementType.xStorage, UNO_QUERY );
1245 if ( rElementType.bModified && xStorage.is() )
1246 impl_storeElementTypeData( xStorage, rElementType );
1248 catch ( const Exception& )
1250 throw IOException();
1254 m_bModified = false;
1255 Reference< XTransactedObject > xTransactedObject( m_xDocConfigStorage, UNO_QUERY );
1256 if ( xTransactedObject.is() )
1257 xTransactedObject->commit();
1261 void SAL_CALL UIConfigurationManager::storeToStorage( const Reference< XStorage >& Storage ) throw (::com::sun::star::uno::Exception, ::com::sun::star::uno::RuntimeException)
1263 ResetableGuard aGuard( m_aLock );
1265 if ( m_bDisposed )
1266 throw DisposedException();
1268 if ( m_xDocConfigStorage.is() && m_bModified && !m_bReadOnly )
1270 // Try to access our module sub folder
1271 for ( int i = 1; i < ::com::sun::star::ui::UIElementType::COUNT; i++ )
1275 Reference< XStorage > xElementTypeStorage( Storage->openStorageElement(
1276 OUString::createFromAscii( UIELEMENTTYPENAMES[i] ), ElementModes::READWRITE ));
1277 UIElementType& rElementType = m_aUIElements[i];
1279 if ( rElementType.bModified && xElementTypeStorage.is() )
1280 impl_storeElementTypeData( xElementTypeStorage, rElementType, false ); // store data to storage, but don't reset modify flag!
1282 catch ( const Exception& )
1284 throw IOException();
1288 Reference< XTransactedObject > xTransactedObject( Storage, UNO_QUERY );
1289 if ( xTransactedObject.is() )
1290 xTransactedObject->commit();
1294 sal_Bool SAL_CALL UIConfigurationManager::isModified() throw (::com::sun::star::uno::RuntimeException)
1296 ResetableGuard aGuard( m_aLock );
1298 return m_bModified;
1301 sal_Bool SAL_CALL UIConfigurationManager::isReadOnly() throw (::com::sun::star::uno::RuntimeException)
1303 ResetableGuard aGuard( m_aLock );
1305 return m_bReadOnly;
1308 void UIConfigurationManager::implts_notifyContainerListener( const ConfigurationEvent& aEvent, NotifyOp eOp )
1310 ::cppu::OInterfaceContainerHelper* pContainer = m_aListenerContainer.getContainer( ::getCppuType( ( const css::uno::Reference< ::com::sun::star::ui::XUIConfigurationListener >*) NULL ) );
1311 if ( pContainer != NULL )
1313 ::cppu::OInterfaceIteratorHelper pIterator( *pContainer );
1314 while ( pIterator.hasMoreElements() )
1318 switch ( eOp )
1320 case NotifyOp_Replace:
1321 ((::com::sun::star::ui::XUIConfigurationListener*)pIterator.next())->elementReplaced( aEvent );
1322 break;
1323 case NotifyOp_Insert:
1324 ((::com::sun::star::ui::XUIConfigurationListener*)pIterator.next())->elementInserted( aEvent );
1325 break;
1326 case NotifyOp_Remove:
1327 ((::com::sun::star::ui::XUIConfigurationListener*)pIterator.next())->elementRemoved( aEvent );
1328 break;
1331 catch( const css::uno::RuntimeException& )
1333 pIterator.remove();
1339 } // namespace framework
1341 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */