summaryrefslogtreecommitdiffstats
path: root/framework/source/fwe
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 05:54:39 +0000
commit267c6f2ac71f92999e969232431ba04678e7437e (patch)
tree358c9467650e1d0a1d7227a21dac2e3d08b622b2 /framework/source/fwe
parentInitial commit. (diff)
downloadlibreoffice-267c6f2ac71f92999e969232431ba04678e7437e.tar.xz
libreoffice-267c6f2ac71f92999e969232431ba04678e7437e.zip
Adding upstream version 4:24.2.0.upstream/4%24.2.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'framework/source/fwe')
-rw-r--r--framework/source/fwe/classes/actiontriggercontainer.cxx134
-rw-r--r--framework/source/fwe/classes/actiontriggerpropertyset.cxx371
-rw-r--r--framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx245
-rw-r--r--framework/source/fwe/classes/addonmenu.cxx299
-rw-r--r--framework/source/fwe/classes/addonsoptions.cxx1952
-rw-r--r--framework/source/fwe/classes/framelistanalyzer.cxx260
-rw-r--r--framework/source/fwe/classes/fwkresid.cxx24
-rw-r--r--framework/source/fwe/classes/rootactiontriggercontainer.cxx237
-rw-r--r--framework/source/fwe/classes/sfxhelperfunctions.cxx158
-rw-r--r--framework/source/fwe/dispatch/interaction.cxx225
-rw-r--r--framework/source/fwe/helper/actiontriggerhelper.cxx376
-rw-r--r--framework/source/fwe/helper/configimporter.cxx74
-rw-r--r--framework/source/fwe/helper/documentundoguard.cxx195
-rw-r--r--framework/source/fwe/helper/propertysetcontainer.cxx160
-rw-r--r--framework/source/fwe/helper/titlehelper.cxx684
-rw-r--r--framework/source/fwe/helper/undomanagerhelper.cxx1094
-rw-r--r--framework/source/fwe/xml/menuconfiguration.cxx156
-rw-r--r--framework/source/fwe/xml/menudocumenthandler.cxx885
-rw-r--r--framework/source/fwe/xml/saxnamespacefilter.cxx163
-rw-r--r--framework/source/fwe/xml/statusbarconfiguration.cxx105
-rw-r--r--framework/source/fwe/xml/statusbardocumenthandler.cxx616
-rw-r--r--framework/source/fwe/xml/toolboxconfiguration.cxx106
-rw-r--r--framework/source/fwe/xml/toolboxdocumenthandler.cxx713
-rw-r--r--framework/source/fwe/xml/xmlnamespaces.cxx152
24 files changed, 9384 insertions, 0 deletions
diff --git a/framework/source/fwe/classes/actiontriggercontainer.cxx b/framework/source/fwe/classes/actiontriggercontainer.cxx
new file mode 100644
index 0000000000..360223e6eb
--- /dev/null
+++ b/framework/source/fwe/classes/actiontriggercontainer.cxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/actiontriggercontainer.hxx>
+#include <classes/actiontriggerpropertyset.hxx>
+#include <classes/actiontriggerseparatorpropertyset.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+
+namespace framework
+{
+
+ActionTriggerContainer::ActionTriggerContainer()
+{
+}
+
+ActionTriggerContainer::~ActionTriggerContainer()
+{
+}
+
+// XInterface
+Any SAL_CALL ActionTriggerContainer::queryInterface( const Type& aType )
+{
+ Any a = ::cppu::queryInterface(
+ aType ,
+ static_cast< XMultiServiceFactory* >(this),
+ static_cast< XServiceInfo* >(this),
+ static_cast< XTypeProvider* >(this));
+
+ if( a.hasValue() )
+ {
+ return a;
+ }
+
+ return PropertySetContainer::queryInterface( aType );
+}
+
+void ActionTriggerContainer::acquire() noexcept
+{
+ PropertySetContainer::acquire();
+}
+
+void ActionTriggerContainer::release() noexcept
+{
+ PropertySetContainer::release();
+}
+
+// XMultiServiceFactory
+Reference< XInterface > SAL_CALL ActionTriggerContainer::createInstance( const OUString& aServiceSpecifier )
+{
+ if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGER )
+ return static_cast<OWeakObject *>( new ActionTriggerPropertySet());
+ else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERCONTAINER )
+ return static_cast<OWeakObject *>( new ActionTriggerContainer());
+ else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERSEPARATOR )
+ return static_cast<OWeakObject *>( new ActionTriggerSeparatorPropertySet());
+ else
+ throw css::uno::RuntimeException("Unknown service specifier!", static_cast<OWeakObject *>(this) );
+}
+
+Reference< XInterface > SAL_CALL ActionTriggerContainer::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/ )
+{
+ return createInstance( ServiceSpecifier );
+}
+
+Sequence< OUString > SAL_CALL ActionTriggerContainer::getAvailableServiceNames()
+{
+ Sequence< OUString > aSeq{ SERVICENAME_ACTIONTRIGGER,
+ SERVICENAME_ACTIONTRIGGERCONTAINER,
+ SERVICENAME_ACTIONTRIGGERSEPARATOR };
+
+ return aSeq;
+}
+
+// XServiceInfo
+OUString SAL_CALL ActionTriggerContainer::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_ACTIONTRIGGERCONTAINER;
+}
+
+sal_Bool SAL_CALL ActionTriggerContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL ActionTriggerContainer::getSupportedServiceNames()
+{
+ Sequence< OUString > seqServiceNames { SERVICENAME_ACTIONTRIGGERCONTAINER };
+ return seqServiceNames;
+}
+
+// XTypeProvider
+Sequence< Type > SAL_CALL ActionTriggerContainer::getTypes()
+{
+ // Create a static typecollection ...
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<XMultiServiceFactory>::get(),
+ cppu::UnoType<XIndexContainer>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get());
+
+ return ourTypeCollection.getTypes();
+}
+
+Sequence< sal_Int8 > SAL_CALL ActionTriggerContainer::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/actiontriggerpropertyset.cxx b/framework/source/fwe/classes/actiontriggerpropertyset.cxx
new file mode 100644
index 0000000000..4592174df5
--- /dev/null
+++ b/framework/source/fwe/classes/actiontriggerpropertyset.cxx
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/actiontriggerpropertyset.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <cppuhelper/proptypehlp.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::awt;
+
+//struct SAL_DLLPUBLIC_IMPORT ::cppu::OBroadcastHelperVar< OMultiTypeInterfaceContainerHelper, OMultiTypeInterfaceContainerHelper::keyType >;
+
+namespace {
+
+// Handles for properties
+// (PLEASE SORT THIS FIELD, IF YOU ADD NEW PROPERTIES!)
+// We use an enum to define these handles, to use all numbers from 0 to nn and
+// if you add someone, you don't must control this!
+// But don't forget to change values of follow defines, if you do something with this enum!
+enum EPROPERTIES
+{
+ HANDLE_COMMANDURL,
+ HANDLE_HELPURL,
+ HANDLE_IMAGE,
+ HANDLE_SUBCONTAINER,
+ HANDLE_TEXT,
+ PROPERTYCOUNT
+};
+
+}
+
+namespace framework
+{
+
+ActionTriggerPropertySet::ActionTriggerPropertySet()
+ : OBroadcastHelper ( m_aMutex )
+ , OPropertySetHelper ( *static_cast< OBroadcastHelper * >(this) )
+{
+}
+
+ActionTriggerPropertySet::~ActionTriggerPropertySet()
+{
+}
+
+// XInterface
+Any SAL_CALL ActionTriggerPropertySet::queryInterface( const Type& aType )
+{
+ Any a = ::cppu::queryInterface(
+ aType,
+ static_cast< XServiceInfo* >(this),
+ static_cast< XTypeProvider* >(this));
+
+ if( a.hasValue() )
+ return a;
+ else
+ {
+ a = OPropertySetHelper::queryInterface( aType );
+
+ if( a.hasValue() )
+ return a;
+ }
+
+ return OWeakObject::queryInterface( aType );
+}
+
+void SAL_CALL ActionTriggerPropertySet::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL ActionTriggerPropertySet::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL ActionTriggerPropertySet::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_ACTIONTRIGGER;
+}
+
+sal_Bool SAL_CALL ActionTriggerPropertySet::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL ActionTriggerPropertySet::getSupportedServiceNames()
+{
+ Sequence<OUString> seqServiceNames { SERVICENAME_ACTIONTRIGGER };
+ return seqServiceNames;
+}
+
+// XTypeProvider
+Sequence< Type > SAL_CALL ActionTriggerPropertySet::getTypes()
+{
+ // Create a static typecollection ...
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XFastPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get());
+
+
+ return ourTypeCollection.getTypes();
+}
+
+Sequence< sal_Int8 > SAL_CALL ActionTriggerPropertySet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+sal_Bool SAL_CALL ActionTriggerPropertySet::convertFastPropertyValue(
+ Any& aConvertedValue,
+ Any& aOldValue,
+ sal_Int32 nHandle,
+ const Any& aValue )
+{
+ // Check, if value of property will changed in method "setFastPropertyValue_NoBroadcast()".
+ // Return sal_True, if changed - else return sal_False.
+ // Attention: Method "impl_tryToChangeProperty()" can throw the IllegalArgumentException !!!
+ // Initialize return value with sal_False !!!
+ // (Handle can be invalid)
+ bool bReturn = false;
+
+ switch( nHandle )
+ {
+ case HANDLE_COMMANDURL:
+ bReturn = impl_tryToChangeProperty( m_aCommandURL, aValue, aOldValue, aConvertedValue );
+ break;
+
+ case HANDLE_HELPURL:
+ bReturn = impl_tryToChangeProperty( m_aHelpURL, aValue, aOldValue, aConvertedValue );
+ break;
+
+ case HANDLE_IMAGE:
+ bReturn = impl_tryToChangeProperty( m_xBitmap, aValue, aOldValue, aConvertedValue );
+ break;
+
+ case HANDLE_SUBCONTAINER:
+ bReturn = impl_tryToChangeProperty( m_xActionTriggerContainer, aValue, aOldValue, aConvertedValue );
+ break;
+
+ case HANDLE_TEXT:
+ bReturn = impl_tryToChangeProperty( m_aText, aValue, aOldValue, aConvertedValue );
+ break;
+ }
+
+ // Return state of operation.
+ return bReturn;
+}
+
+void SAL_CALL ActionTriggerPropertySet::setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle, const Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ // Search for right handle ... and try to set property value.
+ switch( nHandle )
+ {
+ case HANDLE_COMMANDURL:
+ aValue >>= m_aCommandURL;
+ break;
+
+ case HANDLE_HELPURL:
+ aValue >>= m_aHelpURL;
+ break;
+
+ case HANDLE_IMAGE:
+ aValue >>= m_xBitmap;
+ break;
+
+ case HANDLE_SUBCONTAINER:
+ aValue >>= m_xActionTriggerContainer;
+ break;
+
+ case HANDLE_TEXT:
+ aValue >>= m_aText;
+ break;
+ }
+}
+
+void SAL_CALL ActionTriggerPropertySet::getFastPropertyValue(
+ Any& aValue, sal_Int32 nHandle ) const
+{
+ SolarMutexGuard aGuard;
+
+ // Search for right handle ... and try to get property value.
+ switch( nHandle )
+ {
+ case HANDLE_COMMANDURL:
+ aValue <<= m_aCommandURL;
+ break;
+
+ case HANDLE_HELPURL:
+ aValue <<= m_aHelpURL;
+ break;
+
+ case HANDLE_IMAGE:
+ aValue <<= m_xBitmap;
+ break;
+
+ case HANDLE_SUBCONTAINER:
+ aValue <<= m_xActionTriggerContainer;
+ break;
+
+ case HANDLE_TEXT:
+ aValue <<= m_aText;
+ break;
+ }
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL ActionTriggerPropertySet::getInfoHelper()
+{
+ // Define static member to give structure of properties to baseclass "OPropertySetHelper".
+ // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable.
+ // "true" say: Table is sorted by name.
+ static OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true );
+
+ return ourInfoHelper;
+}
+
+Reference< XPropertySetInfo > SAL_CALL ActionTriggerPropertySet::getPropertySetInfo()
+{
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+Sequence< Property > ActionTriggerPropertySet::impl_getStaticPropertyDescriptor()
+{
+ return
+ {
+ Property( "CommandURL" , HANDLE_COMMANDURL , cppu::UnoType<OUString>::get(), PropertyAttribute::TRANSIENT ),
+ Property( "HelpURL" , HANDLE_HELPURL , cppu::UnoType<OUString>::get(), PropertyAttribute::TRANSIENT ),
+ Property( "Image" , HANDLE_IMAGE , cppu::UnoType<XBitmap>::get(), PropertyAttribute::TRANSIENT ),
+ Property( "SubContainer" , HANDLE_SUBCONTAINER , cppu::UnoType<OUString>::get(), PropertyAttribute::TRANSIENT ),
+ Property( "Text" , HANDLE_TEXT , cppu::UnoType<XInterface>::get(), PropertyAttribute::TRANSIENT )
+ };
+}
+
+bool ActionTriggerPropertySet::impl_tryToChangeProperty(
+ const OUString& sCurrentValue ,
+ const Any& aNewValue ,
+ Any& aOldValue ,
+ Any& aConvertedValue )
+{
+ // Set default return value if method failed.
+ bool bReturn = false;
+ // Get new value from any.
+ // IllegalArgumentException() can be thrown!
+ OUString sValue;
+ convertPropertyValue( sValue, aNewValue );
+
+ // If value change ...
+ if( sValue != sCurrentValue )
+ {
+ // ... set information of change.
+ aOldValue <<= sCurrentValue;
+ aConvertedValue <<= sValue;
+ // Return OK - "value will be change ..."
+ bReturn = true;
+ }
+ else
+ {
+ // ... clear information of return parameter!
+ aOldValue.clear ();
+ aConvertedValue.clear ();
+ // Return NOTHING - "value will not be change ..."
+ bReturn = false;
+ }
+
+ return bReturn;
+}
+
+bool ActionTriggerPropertySet::impl_tryToChangeProperty(
+ const Reference< XBitmap >& aCurrentValue ,
+ const Any& aNewValue ,
+ Any& aOldValue ,
+ Any& aConvertedValue )
+{
+ // Set default return value if method failed.
+ bool bReturn = false;
+ // Get new value from any.
+ // IllegalArgumentException() can be thrown!
+ Reference< XBitmap > aValue;
+ convertPropertyValue( aValue, aNewValue );
+
+ // If value change ...
+ if( aValue != aCurrentValue )
+ {
+ // ... set information of change.
+ aOldValue <<= aCurrentValue;
+ aConvertedValue <<= aValue;
+ // Return OK - "value will be change ..."
+ bReturn = true;
+ }
+ else
+ {
+ // ... clear information of return parameter!
+ aOldValue.clear ();
+ aConvertedValue.clear ();
+ // Return NOTHING - "value will not be change ..."
+ bReturn = false;
+ }
+
+ return bReturn;
+}
+
+bool ActionTriggerPropertySet::impl_tryToChangeProperty(
+ const Reference< XInterface >& aCurrentValue ,
+ const Any& aNewValue ,
+ Any& aOldValue ,
+ Any& aConvertedValue )
+{
+ // Set default return value if method failed.
+ bool bReturn = false;
+ // Get new value from any.
+ // IllegalArgumentException() can be thrown!
+ Reference< XInterface > aValue;
+ convertPropertyValue( aValue, aNewValue );
+
+ // If value change ...
+ if( aValue != aCurrentValue )
+ {
+ // ... set information of change.
+ aOldValue <<= aCurrentValue;
+ aConvertedValue <<= aValue;
+ // Return OK - "value will be change ..."
+ bReturn = true;
+ }
+ else
+ {
+ // ... clear information of return parameter!
+ aOldValue.clear ();
+ aConvertedValue.clear ();
+ // Return NOTHING - "value will not be change ..."
+ bReturn = false;
+ }
+
+ return bReturn;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx b/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx
new file mode 100644
index 0000000000..104d765f5c
--- /dev/null
+++ b/framework/source/fwe/classes/actiontriggerseparatorpropertyset.cxx
@@ -0,0 +1,245 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/actiontriggerseparatorpropertyset.hxx>
+#include <com/sun/star/beans/PropertyAttribute.hpp>
+#include <cppuhelper/proptypehlp.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <vcl/svapp.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::awt;
+
+namespace {
+
+// Handles for properties
+// (PLEASE SORT THIS FIELD, IF YOU ADD NEW PROPERTIES!)
+// We use an enum to define these handles, to use all numbers from 0 to nn and
+// if you add someone, you don't must control this!
+// But don't forget to change values of follow defines, if you do something with this enum!
+enum EPROPERTIES
+{
+ HANDLE_TYPE,
+ PROPERTYCOUNT
+};
+
+}
+
+namespace framework
+{
+
+ActionTriggerSeparatorPropertySet::ActionTriggerSeparatorPropertySet()
+ : OBroadcastHelper ( m_aMutex )
+ , OPropertySetHelper ( *static_cast< OBroadcastHelper * >(this) )
+ , m_nSeparatorType( 0 )
+{
+}
+
+ActionTriggerSeparatorPropertySet::~ActionTriggerSeparatorPropertySet()
+{
+}
+
+// XInterface
+Any SAL_CALL ActionTriggerSeparatorPropertySet::queryInterface( const Type& aType )
+{
+ Any a = ::cppu::queryInterface(
+ aType,
+ static_cast< XServiceInfo* >(this),
+ static_cast< XTypeProvider* >(this));
+
+ if( a.hasValue() )
+ return a;
+ else
+ {
+ a = OPropertySetHelper::queryInterface( aType );
+
+ if( a.hasValue() )
+ return a;
+ }
+
+ return OWeakObject::queryInterface( aType );
+}
+
+void ActionTriggerSeparatorPropertySet::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void ActionTriggerSeparatorPropertySet::release() noexcept
+{
+ OWeakObject::release();
+}
+
+// XServiceInfo
+OUString SAL_CALL ActionTriggerSeparatorPropertySet::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_ACTIONTRIGGERSEPARATOR;
+}
+
+sal_Bool SAL_CALL ActionTriggerSeparatorPropertySet::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL ActionTriggerSeparatorPropertySet::getSupportedServiceNames()
+{
+ Sequence<OUString> seqServiceNames { SERVICENAME_ACTIONTRIGGERSEPARATOR };
+ return seqServiceNames;
+}
+
+// XTypeProvider
+Sequence< Type > SAL_CALL ActionTriggerSeparatorPropertySet::getTypes()
+{
+ // Create a static typecollection ...
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<XPropertySet>::get(),
+ cppu::UnoType<XFastPropertySet>::get(),
+ cppu::UnoType<XMultiPropertySet>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get());
+
+ return ourTypeCollection.getTypes();
+}
+
+Sequence< sal_Int8 > SAL_CALL ActionTriggerSeparatorPropertySet::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+sal_Bool SAL_CALL ActionTriggerSeparatorPropertySet::convertFastPropertyValue(
+ Any& aConvertedValue,
+ Any& aOldValue,
+ sal_Int32 nHandle,
+ const Any& aValue )
+{
+ // Check, if value of property will changed in method "setFastPropertyValue_NoBroadcast()".
+ // Return sal_True, if changed - else return sal_False.
+ // Attention: Method "impl_tryToChangeProperty()" can throw the IllegalArgumentException !!!
+ // Initialize return value with sal_False !!!
+ // (Handle can be invalid)
+ bool bReturn = false;
+
+ switch( nHandle )
+ {
+ case HANDLE_TYPE:
+ bReturn = impl_tryToChangeProperty( m_nSeparatorType, aValue, aOldValue, aConvertedValue );
+ break;
+ }
+
+ // Return state of operation.
+ return bReturn;
+}
+
+void SAL_CALL ActionTriggerSeparatorPropertySet::setFastPropertyValue_NoBroadcast(
+ sal_Int32 nHandle, const Any& aValue )
+{
+ SolarMutexGuard aGuard;
+
+ // Search for right handle ... and try to set property value.
+ switch( nHandle )
+ {
+ case HANDLE_TYPE:
+ aValue >>= m_nSeparatorType;
+ break;
+ }
+}
+
+void SAL_CALL ActionTriggerSeparatorPropertySet::getFastPropertyValue(
+ Any& aValue, sal_Int32 nHandle ) const
+{
+ SolarMutexGuard aGuard;
+
+ // Search for right handle ... and try to get property value.
+ switch( nHandle )
+ {
+ case HANDLE_TYPE:
+ aValue <<= m_nSeparatorType;
+ break;
+ }
+}
+
+::cppu::IPropertyArrayHelper& SAL_CALL ActionTriggerSeparatorPropertySet::getInfoHelper()
+{
+ // Define static member to give structure of properties to baseclass "OPropertySetHelper".
+ // "impl_getStaticPropertyDescriptor" is a non exported and static function, who will define a static propertytable.
+ // "true" indicates: Table is sorted by name.
+ static OPropertyArrayHelper ourInfoHelper( impl_getStaticPropertyDescriptor(), true );
+
+ return ourInfoHelper;
+}
+
+Reference< XPropertySetInfo > SAL_CALL ActionTriggerSeparatorPropertySet::getPropertySetInfo()
+{
+ // Create structure of propertysetinfo for baseclass "OPropertySetHelper".
+ // (Use method "getInfoHelper()".)
+ static Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) );
+
+ return xInfo;
+}
+
+Sequence< Property > ActionTriggerSeparatorPropertySet::impl_getStaticPropertyDescriptor()
+{
+ return
+ {
+ Property( "SeparatorType", HANDLE_TYPE, cppu::UnoType<sal_Int16>::get(), PropertyAttribute::TRANSIENT )
+ };
+}
+
+bool ActionTriggerSeparatorPropertySet::impl_tryToChangeProperty(
+ sal_Int16 aCurrentValue ,
+ const Any& aNewValue ,
+ Any& aOldValue ,
+ Any& aConvertedValue )
+{
+ // Set default return value if method failed.
+ bool bReturn = false;
+ // Get new value from any.
+ // IllegalArgumentException() can be thrown!
+ sal_Int16 aValue = 0;
+ convertPropertyValue( aValue, aNewValue );
+
+ // If value change ...
+ if( aValue != aCurrentValue )
+ {
+ // ... set information of change.
+ aOldValue <<= aCurrentValue;
+ aConvertedValue <<= aValue;
+ // Return OK - "value will be change ..."
+ bReturn = true;
+ }
+ else
+ {
+ // ... clear information of return parameter!
+ aOldValue.clear ();
+ aConvertedValue.clear ();
+ // Return NOTHING - "value will not be change ..."
+ bReturn = false;
+ }
+
+ return bReturn;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/addonmenu.cxx b/framework/source/fwe/classes/addonmenu.cxx
new file mode 100644
index 0000000000..db6a7435ac
--- /dev/null
+++ b/framework/source/fwe/classes/addonmenu.cxx
@@ -0,0 +1,299 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <addonmenu.hxx>
+#include <framework/addonsoptions.hxx>
+#include <menuconfiguration.hxx>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <vcl/commandinfoprovider.hxx>
+#include <vcl/menu.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::beans;
+
+namespace framework
+{
+
+bool AddonMenuManager::HasAddonMenuElements()
+{
+ return AddonsOptions().HasAddonsMenu();
+}
+
+// Create the Add-Ons menu
+VclPtr<PopupMenu> AddonMenuManager::CreateAddonMenu( const Reference< XFrame >& rFrame )
+{
+ AddonsOptions aOptions;
+ VclPtr<PopupMenu> pAddonMenu;
+
+ const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aOptions.GetAddonsMenu();
+ if ( rAddonMenuEntries.hasElements() )
+ {
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+ pAddonMenu = VclPtr<PopupMenu>::Create();
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier( rFrame );
+ AddonMenuManager::BuildMenu( pAddonMenu, MENU_APPEND, nUniqueMenuId, rAddonMenuEntries, rFrame, aModuleIdentifier );
+
+ // Don't return an empty Add-On menu
+ if ( pAddonMenu->GetItemCount() == 0 )
+ {
+ pAddonMenu.disposeAndClear();
+ }
+ }
+
+ return pAddonMenu;
+}
+
+// Returns the next insert position from nPos.
+sal_uInt16 AddonMenuManager::GetNextPos( sal_uInt16 nPos )
+{
+ return ( nPos == MENU_APPEND ) ? MENU_APPEND : ( nPos+1 );
+}
+
+static sal_uInt16 FindMenuId( Menu const * pMenu, std::u16string_view aCommand )
+{
+ sal_uInt16 nPos = 0;
+ OUString aCmd;
+ for ( nPos = 0; nPos < pMenu->GetItemCount(); nPos++ )
+ {
+ sal_uInt16 nId = pMenu->GetItemId( nPos );
+ aCmd = pMenu->GetItemCommand( nId );
+ if ( aCmd == aCommand )
+ return nId;
+ }
+
+ return USHRT_MAX;
+}
+
+// Merge the Add-Ons help menu items into the given menu bar at a defined pos
+void AddonMenuManager::MergeAddonHelpMenu( const Reference< XFrame >& rFrame,
+ MenuBar const * pMergeMenuBar )
+{
+ if ( !pMergeMenuBar )
+ return;
+
+ PopupMenu* pHelpMenu(nullptr);
+ sal_uInt16 nId = FindMenuId(pMergeMenuBar, u".uno:HelpMenu");
+ if ( nId != USHRT_MAX )
+ pHelpMenu = pMergeMenuBar->GetPopupMenu( nId );
+
+ if ( !pHelpMenu )
+ return;
+
+ // Add-Ons help menu items should be inserted after the "registration" menu item
+ sal_uInt16 nItemCount = pHelpMenu->GetItemCount();
+ sal_uInt16 nInsSepAfterPos = MENU_APPEND;
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+ AddonsOptions aOptions;
+
+ // try to detect the about menu item with the command URL
+ nId = FindMenuId(pHelpMenu, u".uno:About");
+ sal_uInt16 nInsPos = pHelpMenu->GetItemPos( nId );
+
+ const Sequence< Sequence< PropertyValue > >& rAddonHelpMenuEntries = aOptions.GetAddonsHelpMenu();
+
+ if ( nInsPos < nItemCount && pHelpMenu->GetItemType( nInsPos ) != MenuItemType::SEPARATOR )
+ nInsSepAfterPos = nInsPos;
+
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(rFrame);
+ AddonMenuManager::BuildMenu( pHelpMenu, nInsPos, nUniqueMenuId, rAddonHelpMenuEntries, rFrame, aModuleIdentifier );
+
+ if ( pHelpMenu->GetItemCount() > nItemCount )
+ {
+ if ( nInsSepAfterPos < MENU_APPEND )
+ {
+ nInsSepAfterPos += ( pHelpMenu->GetItemCount() - nItemCount );
+ if ( pHelpMenu->GetItemType( nInsSepAfterPos ) != MenuItemType::SEPARATOR )
+ pHelpMenu->InsertSeparator({}, nInsSepAfterPos);
+ }
+ pHelpMenu->InsertSeparator({}, nItemCount);
+ }
+}
+
+// Merge the addon popup menus into the given menu bar at the provided pos.
+void AddonMenuManager::MergeAddonPopupMenus( const Reference< XFrame >& rFrame,
+ sal_uInt16 nMergeAtPos,
+ MenuBar* pMergeMenuBar )
+{
+ if ( !pMergeMenuBar )
+ return;
+
+ AddonsOptions aAddonsOptions;
+ sal_uInt16 nInsertPos = nMergeAtPos;
+
+ OUString aTitle;
+ OUString aURL;
+ OUString aTarget;
+ OUString aContext;
+ Sequence< Sequence< PropertyValue > > aAddonSubMenu;
+ sal_uInt16 nUniqueMenuId = ADDONMENU_ITEMID_START;
+
+ OUString aModuleIdentifier = vcl::CommandInfoProvider::GetModuleIdentifier(rFrame);
+
+ const Sequence< Sequence< PropertyValue > >& rAddonMenuEntries = aAddonsOptions.GetAddonsMenuBarPart();
+ for ( const Sequence< PropertyValue >& rEntry : rAddonMenuEntries )
+ {
+ AddonMenuManager::GetMenuEntry( rEntry,
+ aTitle,
+ aURL,
+ aTarget,
+ aContext,
+ aAddonSubMenu );
+ if ( !aTitle.isEmpty() &&
+ !aURL.isEmpty() &&
+ aAddonSubMenu.hasElements() &&
+ AddonMenuManager::IsCorrectContext( aModuleIdentifier, aContext ))
+ {
+ sal_uInt16 nId = nUniqueMenuId++;
+ VclPtrInstance<PopupMenu> pAddonPopupMenu;
+
+ AddonMenuManager::BuildMenu( pAddonPopupMenu, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, aModuleIdentifier );
+
+ if ( pAddonPopupMenu->GetItemCount() > 0 )
+ {
+ pMergeMenuBar->InsertItem( nId, aTitle, MenuItemBits::NONE, {}, nInsertPos++);
+ pMergeMenuBar->SetPopupMenu( nId, pAddonPopupMenu );
+
+ // Store the command URL into the VCL menu bar for later identification
+ pMergeMenuBar->SetItemCommand( nId, aURL );
+ }
+ else
+ pAddonPopupMenu.disposeAndClear();
+ }
+ }
+}
+
+// Insert the menu and sub menu entries into pCurrentMenu with the aAddonMenuDefinition provided
+void AddonMenuManager::BuildMenu( PopupMenu* pCurrentMenu,
+ sal_uInt16 nInsPos,
+ sal_uInt16& nUniqueMenuId,
+ const Sequence< Sequence< PropertyValue > >& aAddonMenuDefinition,
+ const Reference< XFrame >& rFrame,
+ const OUString& rModuleIdentifier )
+{
+ Sequence< Sequence< PropertyValue > > aAddonSubMenu;
+ bool bInsertSeparator = false;
+ sal_uInt32 i = 0;
+ sal_uInt32 nElements = 0;
+ sal_uInt32 nCount = aAddonMenuDefinition.getLength();
+
+ OUString aTitle;
+ OUString aURL;
+ OUString aTarget;
+ OUString aContext;
+
+ for ( i = 0; i < nCount; ++i )
+ {
+ GetMenuEntry( aAddonMenuDefinition[i], aTitle, aURL, aTarget, aContext, aAddonSubMenu );
+
+ if ( !IsCorrectContext( rModuleIdentifier, aContext ) || ( aTitle.isEmpty() && aURL.isEmpty() ))
+ continue;
+
+ if ( aURL == "private:separator" )
+ bInsertSeparator = true;
+ else
+ {
+ VclPtr<PopupMenu> pSubMenu;
+ if ( aAddonSubMenu.hasElements() )
+ {
+ pSubMenu = VclPtr<PopupMenu>::Create();
+ AddonMenuManager::BuildMenu( pSubMenu, MENU_APPEND, nUniqueMenuId, aAddonSubMenu, rFrame, rModuleIdentifier );
+
+ // Don't create a menu item for an empty sub menu
+ if ( pSubMenu->GetItemCount() == 0 )
+ {
+ pSubMenu.disposeAndClear();
+ continue;
+ }
+ }
+
+ if ( bInsertSeparator && nElements > 0 )
+ {
+ // Insert a separator only when we insert a new element afterwards and we
+ // have already one before us
+ nElements = 0;
+ bInsertSeparator = false;
+ pCurrentMenu->InsertSeparator({}, nInsPos);
+ nInsPos = AddonMenuManager::GetNextPos( nInsPos );
+ }
+
+ sal_uInt16 nId = nUniqueMenuId++;
+ pCurrentMenu->InsertItem(nId, aTitle, MenuItemBits::NONE, {}, nInsPos);
+ nInsPos = AddonMenuManager::GetNextPos( nInsPos );
+
+ ++nElements;
+
+ void* nAttributePtr = MenuAttributes::CreateAttribute(aTarget, OUString());
+ pCurrentMenu->SetUserValue(nId, nAttributePtr, MenuAttributes::ReleaseAttribute);
+ pCurrentMenu->SetItemCommand( nId, aURL );
+
+ if ( pSubMenu )
+ pCurrentMenu->SetPopupMenu( nId, pSubMenu );
+ }
+ }
+}
+
+// Retrieve the menu entry property values from a sequence
+void AddonMenuManager::GetMenuEntry( const Sequence< PropertyValue >& rAddonMenuEntry,
+ OUString& rTitle,
+ OUString& rURL,
+ OUString& rTarget,
+ OUString& rContext,
+ Sequence< Sequence< PropertyValue > >& rAddonSubMenu )
+{
+ // Reset submenu parameter
+ rAddonSubMenu = Sequence< Sequence< PropertyValue > >();
+
+ for ( const PropertyValue& rEntry : rAddonMenuEntry )
+ {
+ OUString aMenuEntryPropName = rEntry.Name;
+ if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_URL )
+ rEntry.Value >>= rURL;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TITLE )
+ rEntry.Value >>= rTitle;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_TARGET )
+ rEntry.Value >>= rTarget;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_SUBMENU )
+ rEntry.Value >>= rAddonSubMenu;
+ else if ( aMenuEntryPropName == ADDONSMENUITEM_STRING_CONTEXT )
+ rEntry.Value >>= rContext;
+ }
+}
+
+// Check if the context string matches the provided xModel context
+bool AddonMenuManager::IsCorrectContext( std::u16string_view rModuleIdentifier, std::u16string_view rContext )
+{
+ if ( rContext.empty() )
+ return true;
+
+ if ( !rModuleIdentifier.empty() )
+ {
+ return rContext.find( rModuleIdentifier ) != std::u16string_view::npos;
+ }
+
+ return false;
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/addonsoptions.cxx b/framework/source/fwe/classes/addonsoptions.cxx
new file mode 100644
index 0000000000..7801fa5e81
--- /dev/null
+++ b/framework/source/fwe/classes/addonsoptions.cxx
@@ -0,0 +1,1952 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <framework/addonsoptions.hxx>
+#include <o3tl/safeint.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/configitem.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <tools/stream.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <sal/log.hxx>
+#include <comphelper/getexpandeduri.hxx>
+#include <comphelper/processfactory.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/toolbox.hxx>
+#include <vcl/svapp.hxx>
+
+#include <algorithm>
+#include <string_view>
+#include <unordered_map>
+#include <vector>
+
+// namespaces
+
+using namespace ::utl;
+using namespace ::osl;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star;
+
+constexpr OUStringLiteral ROOTNODE_ADDONMENU = u"Office.Addons";
+constexpr OUStringLiteral PATHDELIMITER = u"/";
+constexpr OUString SEPARATOR_URL = u"private:separator"_ustr;
+
+#define PROPERTYNAME_URL ADDONSMENUITEM_STRING_URL
+#define PROPERTYNAME_TITLE ADDONSMENUITEM_STRING_TITLE
+#define PROPERTYNAME_TARGET ADDONSMENUITEM_STRING_TARGET
+#define PROPERTYNAME_IMAGEIDENTIFIER ADDONSMENUITEM_STRING_IMAGEIDENTIFIER
+#define PROPERTYNAME_CONTEXT ADDONSMENUITEM_STRING_CONTEXT
+#define PROPERTYNAME_SUBMENU ADDONSMENUITEM_STRING_SUBMENU
+
+constexpr OUStringLiteral IMAGES_NODENAME = u"UserDefinedImages";
+
+// The following order is mandatory. Please add properties at the end!
+#define INDEX_URL 0
+#define INDEX_TITLE 1
+#define INDEX_IMAGEIDENTIFIER 2
+#define INDEX_TARGET 3
+#define INDEX_CONTEXT 4
+#define INDEX_SUBMENU 5
+#define INDEX_CONTROLTYPE 6
+#define INDEX_WIDTH 7
+#define INDEX_ALIGN 8
+#define INDEX_AUTOSIZE 9
+#define INDEX_OWNERDRAW 10
+#define INDEX_MANDATORY 11
+#define INDEX_STYLE 12
+#define PROPERTYCOUNT_INDEX 13
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_MENUITEM 6
+#define OFFSET_MENUITEM_URL 0
+#define OFFSET_MENUITEM_TITLE 1
+#define OFFSET_MENUITEM_IMAGEIDENTIFIER 2
+#define OFFSET_MENUITEM_TARGET 3
+#define OFFSET_MENUITEM_CONTEXT 4
+#define OFFSET_MENUITEM_SUBMENU 5
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_POPUPMENU 4
+#define OFFSET_POPUPMENU_TITLE 0
+#define OFFSET_POPUPMENU_CONTEXT 1
+#define OFFSET_POPUPMENU_SUBMENU 2
+#define OFFSET_POPUPMENU_URL 3 // Used for property set
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_TOOLBARITEM 7
+#define OFFSET_TOOLBARITEM_URL 0
+#define OFFSET_TOOLBARITEM_TITLE 1
+#define OFFSET_TOOLBARITEM_IMAGEIDENTIFIER 2
+#define OFFSET_TOOLBARITEM_TARGET 3
+#define OFFSET_TOOLBARITEM_CONTEXT 4
+#define OFFSET_TOOLBARITEM_CONTROLTYPE 5
+#define OFFSET_TOOLBARITEM_WIDTH 6
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_NOTEBOOKBARITEM 8
+#define OFFSET_NOTEBOOKBARITEM_URL 0
+#define OFFSET_NOTEBOOKBARITEM_TITLE 1
+#define OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER 2
+#define OFFSET_NOTEBOOKBARITEM_TARGET 3
+#define OFFSET_NOTEBOOKBARITEM_CONTEXT 4
+#define OFFSET_NOTEBOOKBARITEM_CONTROLTYPE 5
+#define OFFSET_NOTEBOOKBARITEM_WIDTH 6
+#define OFFSET_NOTEBOOKBARITEM_STYLE 7
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_STATUSBARITEM 8
+#define OFFSET_STATUSBARITEM_URL 0
+#define OFFSET_STATUSBARITEM_TITLE 1
+#define OFFSET_STATUSBARITEM_CONTEXT 2
+#define OFFSET_STATUSBARITEM_ALIGN 3
+#define OFFSET_STATUSBARITEM_AUTOSIZE 4
+#define OFFSET_STATUSBARITEM_OWNERDRAW 5
+#define OFFSET_STATUSBARITEM_MANDATORY 6
+#define OFFSET_STATUSBARITEM_WIDTH 7
+
+// The following order is mandatory. Please add properties at the end!
+#define PROPERTYCOUNT_IMAGES 8
+#define PROPERTYCOUNT_EMBEDDED_IMAGES 2
+#define OFFSET_IMAGES_SMALL 0
+#define OFFSET_IMAGES_BIG 1
+#define OFFSET_IMAGES_SMALLHC 2
+#define OFFSET_IMAGES_BIGHC 3
+#define OFFSET_IMAGES_SMALL_URL 4
+#define OFFSET_IMAGES_BIG_URL 5
+#define OFFSET_IMAGES_SMALLHC_URL 6
+#define OFFSET_IMAGES_BIGHC_URL 7
+
+#define PROPERTYCOUNT_MERGE_MENUBAR 6
+#define OFFSET_MERGEMENU_MERGEPOINT 0
+#define OFFSET_MERGEMENU_MERGECOMMAND 1
+#define OFFSET_MERGEMENU_MERGECOMMANDPARAMETER 2
+#define OFFSET_MERGEMENU_MERGEFALLBACK 3
+#define OFFSET_MERGEMENU_MERGECONTEXT 4
+#define OFFSET_MERGEMENU_MENUITEMS 5
+
+#define PROPERTYCOUNT_MERGE_TOOLBAR 7
+#define OFFSET_MERGETOOLBAR_TOOLBAR 0
+#define OFFSET_MERGETOOLBAR_MERGEPOINT 1
+#define OFFSET_MERGETOOLBAR_MERGECOMMAND 2
+#define OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER 3
+#define OFFSET_MERGETOOLBAR_MERGEFALLBACK 4
+#define OFFSET_MERGETOOLBAR_MERGECONTEXT 5
+#define OFFSET_MERGETOOLBAR_TOOLBARITEMS 6
+
+#define PROPERTYCOUNT_MERGE_NOTEBOOKBAR 7
+#define OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR 0
+#define OFFSET_MERGENOTEBOOKBAR_MERGEPOINT 1
+#define OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND 2
+#define OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER 3
+#define OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK 4
+#define OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT 5
+#define OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS 6
+
+#define PROPERTYCOUNT_MERGE_STATUSBAR 6
+#define OFFSET_MERGESTATUSBAR_MERGEPOINT 0
+#define OFFSET_MERGESTATUSBAR_MERGECOMMAND 1
+#define OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER 2
+#define OFFSET_MERGESTATUSBAR_MERGEFALLBACK 3
+#define OFFSET_MERGESTATUSBAR_MERGECONTEXT 4
+#define OFFSET_MERGESTATUSBAR_STATUSBARITEMS 5
+
+// private declarations!
+
+/*-****************************************************************************************************************
+ @descr struct to hold information about one menu entry.
+****************************************************************************************************************-*/
+
+namespace framework
+{
+
+class AddonsOptions_Impl : public ConfigItem
+{
+
+ // public methods
+
+ public:
+
+ // constructor / destructor
+
+ AddonsOptions_Impl();
+ virtual ~AddonsOptions_Impl() override;
+
+ // overridden methods of baseclass
+
+ /*-****************************************************************************************************
+ @short called for notify of configmanager
+ @descr This method is called from the ConfigManager before application ends or from the
+ PropertyChangeListener if the sub tree broadcasts changes. You must update your
+ internal values.
+
+ @seealso baseclass ConfigItem
+
+ @param "lPropertyNames" is the list of properties which should be updated.
+ *//*-*****************************************************************************************************/
+
+ virtual void Notify( const Sequence< OUString >& lPropertyNames ) override;
+
+ // public interface
+
+ /*-****************************************************************************************************
+ @short base implementation of public interface for "SvtDynamicMenuOptions"!
+ @descr These class is used as static member of "SvtDynamicMenuOptions" ...
+ => The code exist only for one time and isn't duplicated for every instance!
+ *//*-*****************************************************************************************************/
+
+ bool HasAddonsMenu () const;
+ sal_Int32 GetAddonsToolBarCount() const;
+ sal_Int32 GetAddonsNotebookBarCount() const;
+ const Sequence< Sequence< PropertyValue > >& GetAddonsMenu () const { return m_aCachedMenuProperties;}
+ const Sequence< Sequence< PropertyValue > >& GetAddonsMenuBarPart () const { return m_aCachedMenuBarPartProperties;}
+ const Sequence< Sequence< PropertyValue > >& GetAddonsToolBarPart ( sal_uInt32 nIndex ) const;
+ const Sequence< Sequence< PropertyValue > >& GetAddonsNotebookBarPart ( sal_uInt32 nIndex ) const;
+ OUString GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const;
+ OUString GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const;
+ const Sequence< Sequence< PropertyValue > >& GetAddonsHelpMenu () const { return m_aCachedHelpMenuProperties;}
+ BitmapEx GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale );
+ const MergeMenuInstructionContainer& GetMergeMenuInstructions() const { return m_aCachedMergeMenuInsContainer;}
+ bool GetMergeToolbarInstructions( const OUString& rToolbarName, MergeToolbarInstructionContainer& rToolbarInstructions ) const;
+ bool GetMergeNotebookBarInstructions( const OUString& rNotebookBarName, MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const;
+ const MergeStatusbarInstructionContainer& GetMergeStatusbarInstructions() const { return m_aCachedStatusbarMergingInstructions;}
+ void ReadConfigurationData();
+
+ private:
+ enum ImageSize
+ {
+ IMGSIZE_SMALL = 0,
+ IMGSIZE_BIG
+ };
+
+ struct OneImageEntry
+ {
+ BitmapEx aScaled; ///< cached scaled image
+ BitmapEx aImage; ///< original un-scaled image
+ OUString aURL; ///< URL in case it is not loaded yet
+ };
+
+ struct ImageEntry
+ {
+ // if the image is set, it was embedded in some way,
+ // otherwise we use the associated URL to load on demand
+
+ // accessed in this order
+ OneImageEntry aSizeEntry[2];
+ ImageEntry() {}
+ void addImage(ImageSize eSize, const BitmapEx &rImage);
+ void addImage(ImageSize eSize, const OUString &rURL);
+ };
+
+ typedef std::unordered_map< OUString, ImageEntry > ImageManager;
+ typedef std::unordered_map< OUString, sal_uInt32 > StringToIndexMap;
+ typedef std::vector< Sequence< Sequence< PropertyValue > > > AddonToolBars;
+ typedef std::vector< Sequence< Sequence< PropertyValue > > > AddonNotebookBars;
+ typedef std::unordered_map< OUString, MergeToolbarInstructionContainer > ToolbarMergingInstructions;
+ typedef std::unordered_map< OUString, MergeNotebookBarInstructionContainer > NotebookBarMergingInstructions;
+
+ /*-****************************************************************************************************
+ @short return list of key names of our configuration management which represent our module tree
+ @descr These methods return the current list of key names! We need it to get needed values from our
+ configuration management!
+ @param "nCount" , returns count of menu entries for "new"
+ @return A list of configuration key names is returned.
+ *//*-*****************************************************************************************************/
+
+ void ReadAddonMenuSet( Sequence< Sequence< PropertyValue > >& aAddonMenuSeq );
+ void ReadOfficeMenuBarSet( Sequence< Sequence< PropertyValue > >& aAddonOfficeMenuBarSeq );
+ void ReadOfficeToolBarSet( AddonToolBars& rAddonOfficeToolBars, std::vector< OUString >& rAddonOfficeToolBarResNames );
+ bool ReadToolBarItemSet( const OUString& rToolBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& aAddonOfficeToolBarSeq );
+ void ReadOfficeNotebookBarSet( AddonNotebookBars& rAddonOfficeNotebookBars, std::vector< OUString >& rAddonOfficeNotebookBarResNames );
+ bool ReadNotebookBarItemSet( const OUString& rNotebookBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& aAddonOfficeNotebookBarSeq );
+
+ void ReadOfficeHelpSet( Sequence< Sequence< PropertyValue > >& aAddonOfficeHelpMenuSeq );
+ void ReadImages( ImageManager& aImageManager );
+ void ReadMenuMergeInstructions( MergeMenuInstructionContainer& rContainer );
+ void ReadToolbarMergeInstructions( ToolbarMergingInstructions& rToolbarMergeMap );
+ void ReadNotebookBarMergeInstructions( NotebookBarMergingInstructions& rNotebookBarMergeMap );
+ void ReadStatusbarMergeInstructions( MergeStatusbarInstructionContainer& rContainer );
+
+ void ReadMergeMenuData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeMenu );
+ void ReadMergeToolbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeToolbarItems );
+ void ReadMergeNotebookBarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeNotebookBarItems );
+ void ReadMergeStatusbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeStatusbar );
+ bool ReadMenuItem( std::u16string_view aMenuItemNodeName, Sequence< PropertyValue >& aMenuItem, bool bIgnoreSubMenu = false );
+ bool ReadPopupMenu( std::u16string_view aPopupMenuNodeName, Sequence< PropertyValue >& aPopupMenu );
+ void AppendPopupMenu( Sequence< PropertyValue >& aTargetPopupMenu, const Sequence< PropertyValue >& rSourcePopupMenu );
+ bool ReadToolBarItem( std::u16string_view aToolBarItemNodeName, Sequence< PropertyValue >& aToolBarItem );
+ bool ReadNotebookBarItem( std::u16string_view aNotebookBarItemNodeName, Sequence< PropertyValue >& aNotebookBarItem );
+
+ bool ReadStatusBarItem( std::u16string_view aStatusbarItemNodeName, Sequence< PropertyValue >& aStatusbarItem );
+ std::unique_ptr<ImageEntry> ReadImageData( std::u16string_view aImagesNodeName );
+ void ReadAndAssociateImages( const OUString& aURL, const OUString& aImageId );
+ BitmapEx ReadImageFromURL( const OUString& aURL );
+ bool HasAssociatedImages( const OUString& aURL );
+ void SubstituteVariables( OUString& aURL );
+
+ void ReadSubMenuEntries( const Sequence< OUString >& aSubMenuNodeNames, Sequence< Sequence< PropertyValue > >& rSubMenu );
+ OUString GeneratePrefixURL();
+
+ Sequence< OUString > GetPropertyNamesMenuItem( std::u16string_view aPropertyRootNode )
+ const;
+ Sequence< OUString > GetPropertyNamesPopupMenu( std::u16string_view aPropertyRootNode )
+ const;
+ Sequence< OUString > GetPropertyNamesToolBarItem( std::u16string_view aPropertyRootNode )
+ const;
+ Sequence< OUString > GetPropertyNamesNotebookBarItem( std::u16string_view aPropertyRootNode ) const;
+
+ Sequence< OUString > GetPropertyNamesStatusbarItem( std::u16string_view aPropertyRootNode ) const;
+ Sequence< OUString > GetPropertyNamesImages( std::u16string_view aPropertyRootNode ) const;
+ bool CreateImageFromSequence( BitmapEx& rImage, Sequence< sal_Int8 >& rBitmapDataSeq ) const;
+
+ DECL_LINK(NotifyEvent, void*, void);
+
+ virtual void ImplCommit() override;
+
+ // private member
+
+ private:
+ sal_Int32 m_nRootAddonPopupMenuId;
+ OUString m_aPropNames[PROPERTYCOUNT_INDEX];
+ OUString m_aPropImagesNames[PROPERTYCOUNT_IMAGES];
+ OUString m_aPropMergeMenuNames[PROPERTYCOUNT_MERGE_MENUBAR];
+ OUString m_aPropMergeToolbarNames[PROPERTYCOUNT_MERGE_TOOLBAR];
+ OUString m_aPropMergeNotebookBarNames[PROPERTYCOUNT_MERGE_NOTEBOOKBAR];
+ OUString m_aPropMergeStatusbarNames[PROPERTYCOUNT_MERGE_STATUSBAR];
+ OUString m_aPathDelimiter;
+ OUString m_aRootAddonPopupMenuURLPrexfix;
+ Sequence< Sequence< PropertyValue > > m_aCachedMenuProperties;
+ Sequence< Sequence< PropertyValue > > m_aCachedMenuBarPartProperties;
+ AddonToolBars m_aCachedToolBarPartProperties;
+ AddonNotebookBars m_aCachedNotebookBarPartProperties;
+ std::vector< OUString > m_aCachedToolBarPartResourceNames;
+ std::vector< OUString > m_aCachedNotebookBarPartResourceNames;
+ Sequence< Sequence< PropertyValue > > m_aCachedHelpMenuProperties;
+ ImageManager m_aImageManager;
+ Sequence< Sequence< PropertyValue > > m_aEmptyAddonToolBar;
+ Sequence< Sequence< PropertyValue > > m_aEmptyAddonNotebookBar;
+ MergeMenuInstructionContainer m_aCachedMergeMenuInsContainer;
+ ToolbarMergingInstructions m_aCachedToolbarMergingInstructions;
+ NotebookBarMergingInstructions m_aCachedNotebookBarMergingInstructions;
+ MergeStatusbarInstructionContainer m_aCachedStatusbarMergingInstructions;
+};
+
+void AddonsOptions_Impl::ImageEntry::addImage(ImageSize eSize, const BitmapEx& rImage)
+{
+ aSizeEntry[static_cast<int>(eSize)].aImage = rImage;
+}
+
+void AddonsOptions_Impl::ImageEntry::addImage(ImageSize eSize, const OUString &rURL)
+{
+ aSizeEntry[static_cast<int>(eSize)].aURL = rURL;
+}
+
+// constructor
+
+AddonsOptions_Impl::AddonsOptions_Impl()
+ // Init baseclasses first
+ : ConfigItem( ROOTNODE_ADDONMENU ),
+ m_nRootAddonPopupMenuId( 0 ),
+ m_aPathDelimiter( PATHDELIMITER ),
+ m_aRootAddonPopupMenuURLPrexfix( ADDONSPOPUPMENU_URL_PREFIX_STR )
+{
+ // initialize array with fixed property names
+ m_aPropNames[ INDEX_URL ] = PROPERTYNAME_URL;
+ m_aPropNames[ INDEX_TITLE ] = PROPERTYNAME_TITLE;
+ m_aPropNames[ INDEX_TARGET ] = PROPERTYNAME_TARGET;
+ m_aPropNames[ INDEX_IMAGEIDENTIFIER ] = PROPERTYNAME_IMAGEIDENTIFIER;
+ m_aPropNames[ INDEX_CONTEXT ] = PROPERTYNAME_CONTEXT;
+ m_aPropNames[ INDEX_SUBMENU ] = PROPERTYNAME_SUBMENU; // Submenu set!
+ m_aPropNames[ INDEX_CONTROLTYPE ] = "ControlType";
+ m_aPropNames[ INDEX_WIDTH ] = "Width";
+ m_aPropNames[ INDEX_ALIGN ] = "Alignment";
+ m_aPropNames[ INDEX_AUTOSIZE ] = "AutoSize";
+ m_aPropNames[ INDEX_OWNERDRAW ] = "OwnerDraw";
+ m_aPropNames[ INDEX_MANDATORY ] = "Mandatory";
+ m_aPropNames[ INDEX_STYLE ] = "Style";
+
+ // initialize array with fixed images property names
+ m_aPropImagesNames[ OFFSET_IMAGES_SMALL ] = "ImageSmall";
+ m_aPropImagesNames[ OFFSET_IMAGES_BIG ] = "ImageBig";
+ m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC ] = "ImageSmallHC";
+ m_aPropImagesNames[ OFFSET_IMAGES_BIGHC ] = "ImageBigHC";
+ m_aPropImagesNames[ OFFSET_IMAGES_SMALL_URL ] = "ImageSmallURL";
+ m_aPropImagesNames[ OFFSET_IMAGES_BIG_URL ] = "ImageBigURL";
+ m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC_URL ] = "ImageSmallHCURL";
+ m_aPropImagesNames[ OFFSET_IMAGES_BIGHC_URL ] = "ImageBigHCURL";
+
+ // initialize array with fixed merge menu property names
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEPOINT ] = "MergePoint";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMAND ] = "MergeCommand";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMANDPARAMETER ] = "MergeCommandParameter";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEFALLBACK ] = "MergeFallback";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECONTEXT ] = "MergeContext";
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MENUITEMS ] = "MenuItems";
+
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBAR ] = "MergeToolBar";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEPOINT ] = "MergePoint";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMAND ] = "MergeCommand";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEFALLBACK ] = "MergeFallback";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECONTEXT ] = "MergeContext";
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBARITEMS ] = "ToolBarItems";
+
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR ] = "MergeNotebookBar";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGEPOINT ] = "MergePoint";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND ] = "MergeCommand";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK ] = "MergeFallback";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT ] = "MergeContext";
+ m_aPropMergeNotebookBarNames[ OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS ] = "NotebookBarItems";
+
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGEPOINT ] = "MergePoint";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECOMMAND ] = "MergeCommand";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER ] = "MergeCommandParameter";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGEFALLBACK ] = "MergeFallback";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_MERGECONTEXT ] = "MergeContext";
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_STATUSBARITEMS ] = "StatusBarItems";
+
+ ReadConfigurationData();
+
+ // Enable notification mechanism of our baseclass.
+ // We need it to get information about changes outside these class on our used configuration keys!
+ Sequence<OUString> aNotifySeq { "AddonUI" };
+ EnableNotification( aNotifySeq );
+}
+
+// destructor
+
+AddonsOptions_Impl::~AddonsOptions_Impl()
+{
+ assert(!IsModified()); // should have been committed
+}
+
+void AddonsOptions_Impl::ReadConfigurationData()
+{
+ // reset members to be read again from configuration
+ m_aCachedMenuProperties = Sequence< Sequence< PropertyValue > >();
+ m_aCachedMenuBarPartProperties = Sequence< Sequence< PropertyValue > >();
+ m_aCachedToolBarPartProperties = AddonToolBars();
+ m_aCachedNotebookBarPartProperties = AddonNotebookBars();
+ m_aCachedHelpMenuProperties = Sequence< Sequence< PropertyValue > >();
+ m_aCachedToolBarPartResourceNames.clear();
+ m_aCachedNotebookBarPartResourceNames.clear();
+ m_aImageManager = ImageManager();
+
+ ReadAddonMenuSet( m_aCachedMenuProperties );
+ ReadOfficeMenuBarSet( m_aCachedMenuBarPartProperties );
+ ReadOfficeToolBarSet( m_aCachedToolBarPartProperties, m_aCachedToolBarPartResourceNames );
+ ReadOfficeNotebookBarSet( m_aCachedNotebookBarPartProperties, m_aCachedNotebookBarPartResourceNames );
+
+ ReadOfficeHelpSet( m_aCachedHelpMenuProperties );
+ ReadImages( m_aImageManager );
+
+ m_aCachedMergeMenuInsContainer.clear();
+ m_aCachedToolbarMergingInstructions.clear();
+ m_aCachedNotebookBarMergingInstructions.clear();
+ m_aCachedStatusbarMergingInstructions.clear();
+
+ ReadMenuMergeInstructions( m_aCachedMergeMenuInsContainer );
+ ReadToolbarMergeInstructions( m_aCachedToolbarMergingInstructions );
+ ReadNotebookBarMergeInstructions( m_aCachedNotebookBarMergingInstructions );
+ ReadStatusbarMergeInstructions( m_aCachedStatusbarMergingInstructions );
+}
+
+// public method
+
+void AddonsOptions_Impl::Notify( const Sequence< OUString >& /*lPropertyNames*/ )
+{
+ Application::PostUserEvent(LINK(this, AddonsOptions_Impl, NotifyEvent));
+}
+
+// public method
+
+void AddonsOptions_Impl::ImplCommit()
+{
+ SAL_WARN("fwk", "AddonsOptions_Impl::ImplCommit(): Not implemented yet!");
+}
+
+// public method
+
+bool AddonsOptions_Impl::HasAddonsMenu() const
+{
+ return m_aCachedMenuProperties.hasElements();
+}
+
+// public method
+
+sal_Int32 AddonsOptions_Impl::GetAddonsToolBarCount() const
+{
+ return m_aCachedToolBarPartProperties.size();
+}
+
+// public method
+
+sal_Int32 AddonsOptions_Impl::GetAddonsNotebookBarCount() const
+{
+ return m_aCachedNotebookBarPartProperties.size();
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions_Impl::GetAddonsToolBarPart( sal_uInt32 nIndex ) const
+{
+ if ( /*nIndex >= 0 &&*/ nIndex < m_aCachedToolBarPartProperties.size() )
+ return m_aCachedToolBarPartProperties[nIndex];
+ else
+ return m_aEmptyAddonToolBar;
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions_Impl::GetAddonsNotebookBarPart( sal_uInt32 nIndex ) const
+{
+ if ( /*nIndex >= 0 &&*/ nIndex < m_aCachedNotebookBarPartProperties.size() )
+ return m_aCachedNotebookBarPartProperties[nIndex];
+ else
+ return m_aEmptyAddonNotebookBar;
+}
+
+// public method
+
+OUString AddonsOptions_Impl::GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const
+{
+ if ( nIndex < m_aCachedToolBarPartResourceNames.size() )
+ return m_aCachedToolBarPartResourceNames[nIndex];
+ else
+ return OUString();
+}
+
+// public method
+
+OUString AddonsOptions_Impl::GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const
+{
+ if ( nIndex < m_aCachedNotebookBarPartResourceNames.size() )
+ return m_aCachedNotebookBarPartResourceNames[nIndex];
+ else
+ return OUString();
+}
+
+// public method
+
+bool AddonsOptions_Impl::GetMergeToolbarInstructions(
+ const OUString& rToolbarName,
+ MergeToolbarInstructionContainer& rToolbarInstructions ) const
+{
+ ToolbarMergingInstructions::const_iterator pIter = m_aCachedToolbarMergingInstructions.find( rToolbarName );
+ if ( pIter != m_aCachedToolbarMergingInstructions.end() )
+ {
+ rToolbarInstructions = pIter->second;
+ return true;
+ }
+ else
+ return false;
+}
+
+// public method
+
+bool AddonsOptions_Impl::GetMergeNotebookBarInstructions(
+ const OUString& rNotebookBarName,
+ MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const
+{
+ NotebookBarMergingInstructions::const_iterator pIter = m_aCachedNotebookBarMergingInstructions.find( rNotebookBarName );
+ if ( pIter != m_aCachedNotebookBarMergingInstructions.end() )
+ {
+ rNotebookBarInstructions = pIter->second;
+ return true;
+ }
+ else
+ return false;
+}
+
+// public method
+
+static BitmapEx ScaleImage( const BitmapEx &rImage, bool bBig )
+{
+ Size aSize = ToolBox::GetDefaultImageSize(bBig ? ToolBoxButtonSize::Large : ToolBoxButtonSize::Small);
+ BitmapEx aScaleBmp(rImage);
+ SAL_INFO("fwk", "Addons: expensive scale image from "
+ << aScaleBmp.GetSizePixel() << " to " << aSize);
+ aScaleBmp.Scale(aSize, BmpScaleFlag::BestQuality);
+ return aScaleBmp;
+}
+
+BitmapEx AddonsOptions_Impl::GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale )
+{
+ BitmapEx aImage;
+
+ SAL_INFO("fwk", "Expensive: Addons GetImageFromURL " << aURL <<
+ " big " << (bBig?"big":"little") <<
+ " scale " << (bNoScale ? "noscale" : "scale"));
+
+ ImageManager::iterator pIter = m_aImageManager.find(aURL);
+ if ( pIter != m_aImageManager.end() )
+ {
+ ImageSize eSize = bBig ? IMGSIZE_BIG : IMGSIZE_SMALL;
+ int nIdx = static_cast<int>(eSize);
+ int nOtherIdx = nIdx ? 0 : 1;
+
+ OneImageEntry& rSizeEntry = pIter->second.aSizeEntry[nIdx];
+ OneImageEntry& rOtherEntry = pIter->second.aSizeEntry[nOtherIdx];
+ // actually read the image ...
+ if (rSizeEntry.aImage.IsEmpty())
+ rSizeEntry.aImage = ReadImageFromURL(rSizeEntry.aURL);
+
+ if (rSizeEntry.aImage.IsEmpty())
+ { // try the other size and scale it
+ aImage = ScaleImage(ReadImageFromURL(rOtherEntry.aURL), bBig);
+ rSizeEntry.aImage = aImage;
+ if (rSizeEntry.aImage.IsEmpty())
+ SAL_WARN("fwk", "failed to load addons image " << aURL);
+ }
+
+ // FIXME: bNoScale is not terribly meaningful or useful
+
+ if (aImage.IsEmpty() && bNoScale)
+ aImage = rSizeEntry.aImage;
+
+ if (aImage.IsEmpty() && !rSizeEntry.aScaled.IsEmpty())
+ aImage = rSizeEntry.aScaled;
+
+ else // scale to the correct size for the theme / toolbox
+ {
+ aImage = rSizeEntry.aImage;
+ if (aImage.IsEmpty()) // use and scale the other if one size is missing
+ aImage = rOtherEntry.aImage;
+
+ aImage = ScaleImage(aImage, bBig);
+ rSizeEntry.aScaled = aImage; // cache for next time
+ }
+ }
+
+ return aImage;
+}
+
+void AddonsOptions_Impl::ReadAddonMenuSet( Sequence< Sequence< PropertyValue > >& rAddonMenuSeq )
+{
+ // Read the AddonMenu set and fill property sequences
+ OUString aAddonMenuNodeName( "AddonUI/AddonMenu" );
+ Sequence< OUString > aAddonMenuNodeSeq = GetNodeNames( aAddonMenuNodeName );
+ OUString aAddonMenuItemNode( aAddonMenuNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonMenuNodeSeq.getLength();
+ sal_uInt32 nIndex = 0;
+ Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM );
+ auto pMenuItem = aMenuItem.getArray();
+ // Init the property value sequence
+ pMenuItem[ OFFSET_MENUITEM_URL ].Name = m_aPropNames[ INDEX_URL ];
+ pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ];
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ];
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU ]; // Submenu set!
+
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aRootMenuItemNode( aAddonMenuItemNode + aAddonMenuNodeSeq[n] );
+
+ // Read the MenuItem
+ if ( ReadMenuItem( aRootMenuItemNode, aMenuItem ) )
+ {
+ // Successfully read a menu item, append to our list
+ sal_uInt32 nMenuItemCount = rAddonMenuSeq.getLength() + 1;
+ rAddonMenuSeq.realloc( nMenuItemCount );
+ rAddonMenuSeq.getArray()[nIndex++] = aMenuItem;
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadOfficeHelpSet( Sequence< Sequence< PropertyValue > >& rAddonOfficeHelpMenuSeq )
+{
+ // Read the AddonMenu set and fill property sequences
+ OUString aAddonHelpMenuNodeName( "AddonUI/OfficeHelp" );
+ Sequence< OUString > aAddonHelpMenuNodeSeq = GetNodeNames( aAddonHelpMenuNodeName );
+ OUString aAddonHelpMenuItemNode( aAddonHelpMenuNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonHelpMenuNodeSeq.getLength();
+ sal_uInt32 nIndex = 0;
+ Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM );
+ auto pMenuItem = aMenuItem.getArray();
+ // Init the property value sequence
+ pMenuItem[ OFFSET_MENUITEM_URL ].Name = m_aPropNames[ INDEX_URL ];
+ pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ];
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ];
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU ]; // Submenu set!
+
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aRootMenuItemNode( aAddonHelpMenuItemNode + aAddonHelpMenuNodeSeq[n] );
+
+ // Read the MenuItem
+ if ( ReadMenuItem( aRootMenuItemNode, aMenuItem, true ) )
+ {
+ // Successfully read a menu item, append to our list
+ sal_uInt32 nMenuItemCount = rAddonOfficeHelpMenuSeq.getLength() + 1;
+ rAddonOfficeHelpMenuSeq.realloc( nMenuItemCount );
+ rAddonOfficeHelpMenuSeq.getArray()[nIndex++] = aMenuItem;
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadOfficeMenuBarSet( Sequence< Sequence< PropertyValue > >& rAddonOfficeMenuBarSeq )
+{
+ // Read the OfficeMenuBar set and fill property sequences
+ OUString aAddonMenuBarNodeName( "AddonUI/OfficeMenuBar" );
+ Sequence< OUString > aAddonMenuBarNodeSeq = GetNodeNames( aAddonMenuBarNodeName );
+ OUString aAddonMenuBarNode( aAddonMenuBarNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonMenuBarNodeSeq.getLength();
+ sal_uInt32 nIndex = 0;
+ Sequence< PropertyValue > aPopupMenu( PROPERTYCOUNT_POPUPMENU );
+ auto pPopupMenu = aPopupMenu.getArray();
+ // Init the property value sequence
+ pPopupMenu[ OFFSET_POPUPMENU_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pPopupMenu[ OFFSET_POPUPMENU_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT];
+ pPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Name = m_aPropNames[ INDEX_SUBMENU];
+ pPopupMenu[ OFFSET_POPUPMENU_URL ].Name = m_aPropNames[ INDEX_URL ];
+
+ StringToIndexMap aTitleToIndexMap;
+ auto pAddonOfficeMenuBarSeq = rAddonOfficeMenuBarSeq.getArray();
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aPopupMenuNode( aAddonMenuBarNode + aAddonMenuBarNodeSeq[n] );
+
+ // Read the MenuItem
+ if ( ReadPopupMenu( aPopupMenuNode, aPopupMenu ) )
+ {
+ // Successfully read a popup menu, append to our list
+ OUString aPopupTitle;
+ if ( aPopupMenu[OFFSET_POPUPMENU_TITLE].Value >>= aPopupTitle )
+ {
+ StringToIndexMap::const_iterator pIter = aTitleToIndexMap.find( aPopupTitle );
+ if ( pIter != aTitleToIndexMap.end() )
+ {
+ // title already there => concat both popup menus
+ Sequence< PropertyValue >& rOldPopupMenu = pAddonOfficeMenuBarSeq[pIter->second];
+ AppendPopupMenu( rOldPopupMenu, aPopupMenu );
+ }
+ else
+ {
+ // not found
+ sal_uInt32 nMenuItemCount = rAddonOfficeMenuBarSeq.getLength() + 1;
+ rAddonOfficeMenuBarSeq.realloc( nMenuItemCount );
+ pAddonOfficeMenuBarSeq = rAddonOfficeMenuBarSeq.getArray();
+ pAddonOfficeMenuBarSeq[nIndex] = aPopupMenu;
+ aTitleToIndexMap.emplace( aPopupTitle, nIndex );
+ ++nIndex;
+ }
+ }
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadOfficeToolBarSet( AddonToolBars& rAddonOfficeToolBars, std::vector< OUString >& rAddonOfficeToolBarResNames )
+{
+ // Read the OfficeToolBar set and fill property sequences
+ OUString aAddonToolBarNodeName( "AddonUI/OfficeToolBar" );
+ Sequence< OUString > aAddonToolBarNodeSeq = GetNodeNames( aAddonToolBarNodeName );
+ OUString aAddonToolBarNode( aAddonToolBarNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonToolBarNodeSeq.getLength();
+
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aToolBarItemNode( aAddonToolBarNode + aAddonToolBarNodeSeq[n] );
+ rAddonOfficeToolBarResNames.push_back( aAddonToolBarNodeSeq[n] );
+ rAddonOfficeToolBars.push_back( m_aEmptyAddonToolBar );
+ ReadToolBarItemSet( aToolBarItemNode, rAddonOfficeToolBars[n] );
+ }
+}
+
+bool AddonsOptions_Impl::ReadToolBarItemSet( const OUString& rToolBarItemSetNodeName, Sequence< Sequence< PropertyValue > >& rAddonOfficeToolBarSeq )
+{
+ sal_uInt32 nToolBarItemCount = rAddonOfficeToolBarSeq.getLength();
+ OUString aAddonToolBarItemSetNode( rToolBarItemSetNodeName + m_aPathDelimiter );
+ Sequence< OUString > aAddonToolBarItemSetNodeSeq = GetNodeNames( rToolBarItemSetNodeName );
+ Sequence< PropertyValue > aToolBarItem( PROPERTYCOUNT_TOOLBARITEM );
+ auto pToolBarItem = aToolBarItem.getArray();
+ // Init the property value sequence
+ pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Name = m_aPropNames[ INDEX_URL ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Name = m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Name = m_aPropNames[ INDEX_TARGET ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Name = m_aPropNames[ INDEX_CONTROLTYPE ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Name = m_aPropNames[ INDEX_WIDTH ];
+
+ sal_uInt32 nCount = aAddonToolBarItemSetNodeSeq.getLength();
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aToolBarItemNode( aAddonToolBarItemSetNode + aAddonToolBarItemSetNodeSeq[n] );
+
+ // Read the ToolBarItem
+ if ( ReadToolBarItem( aToolBarItemNode, aToolBarItem ) )
+ {
+ // Successfully read a toolbar item, append to our list
+ sal_uInt32 nAddonCount = rAddonOfficeToolBarSeq.getLength();
+ rAddonOfficeToolBarSeq.realloc( nAddonCount+1 );
+ rAddonOfficeToolBarSeq.getArray()[nAddonCount] = aToolBarItem;
+ }
+ }
+
+ return ( o3tl::make_unsigned(rAddonOfficeToolBarSeq.getLength()) > nToolBarItemCount );
+}
+
+void AddonsOptions_Impl::ReadOfficeNotebookBarSet(
+ AddonNotebookBars& rAddonOfficeNotebookBars,
+ std::vector<OUString>& rAddonOfficeNotebookBarResNames)
+{
+ // Read the OfficeToolBar set and fill property sequences
+ OUString aAddonNotebookBarNodeName("AddonUI/OfficeNotebookBar");
+ Sequence<OUString> aAddonNotebookBarNodeSeq = GetNodeNames(aAddonNotebookBarNodeName);
+ OUString aAddonNotebookBarNode(aAddonNotebookBarNodeName + m_aPathDelimiter);
+
+ sal_uInt32 nCount = aAddonNotebookBarNodeSeq.getLength();
+
+ for (sal_uInt32 n = 0; n < nCount; n++)
+ {
+ OUString aNotebookBarItemNode(aAddonNotebookBarNode + aAddonNotebookBarNodeSeq[n]);
+ rAddonOfficeNotebookBarResNames.push_back(aAddonNotebookBarNodeSeq[n]);
+ rAddonOfficeNotebookBars.push_back(m_aEmptyAddonNotebookBar);
+ ReadNotebookBarItemSet(aNotebookBarItemNode, rAddonOfficeNotebookBars[n]);
+ }
+}
+
+bool AddonsOptions_Impl::ReadNotebookBarItemSet(
+ const OUString& rNotebookBarItemSetNodeName,
+ Sequence<Sequence<PropertyValue>>& rAddonOfficeNotebookBarSeq)
+{
+ sal_uInt32 nNotebookBarItemCount = rAddonOfficeNotebookBarSeq.getLength();
+ OUString aAddonNotebookBarItemSetNode(rNotebookBarItemSetNodeName + m_aPathDelimiter);
+ Sequence<OUString> aAddonNotebookBarItemSetNodeSeq = GetNodeNames(rNotebookBarItemSetNodeName);
+ Sequence<PropertyValue> aNotebookBarItem(PROPERTYCOUNT_NOTEBOOKBARITEM);
+ auto pNotebookBarItem = aNotebookBarItem.getArray();
+ // Init the property value sequence
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_URL].Name = m_aPropNames[INDEX_URL];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_TITLE].Name = m_aPropNames[INDEX_TITLE];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER].Name
+ = m_aPropNames[INDEX_IMAGEIDENTIFIER];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_TARGET].Name = m_aPropNames[INDEX_TARGET];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_CONTEXT].Name = m_aPropNames[INDEX_CONTEXT];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_CONTROLTYPE].Name = m_aPropNames[INDEX_CONTROLTYPE];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_WIDTH].Name = m_aPropNames[INDEX_WIDTH];
+ pNotebookBarItem[OFFSET_NOTEBOOKBARITEM_STYLE].Name = m_aPropNames[INDEX_STYLE];
+
+ sal_uInt32 nCount = aAddonNotebookBarItemSetNodeSeq.getLength();
+ for (sal_uInt32 n = 0; n < nCount; n++)
+ {
+ OUString aNotebookBarItemNode(aAddonNotebookBarItemSetNode
+ + aAddonNotebookBarItemSetNodeSeq[n]);
+ // Read the NotebookBarItem
+ if (ReadNotebookBarItem(aNotebookBarItemNode, aNotebookBarItem))
+ {
+ // Successfully read a toolbar item, append to our list
+ sal_uInt32 nAddonCount = rAddonOfficeNotebookBarSeq.getLength();
+ rAddonOfficeNotebookBarSeq.realloc(nAddonCount + 1);
+ rAddonOfficeNotebookBarSeq.getArray()[nAddonCount] = aNotebookBarItem;
+ }
+ }
+
+ return (o3tl::make_unsigned(rAddonOfficeNotebookBarSeq.getLength())
+ > nNotebookBarItemCount);
+}
+
+void AddonsOptions_Impl::ReadImages( ImageManager& aImageManager )
+{
+ // Read the user-defined Images set and fill image manager
+ OUString aAddonImagesNodeName( "AddonUI/Images" );
+ Sequence< OUString > aAddonImagesNodeSeq = GetNodeNames( aAddonImagesNodeName );
+ OUString aAddonImagesNode( aAddonImagesNodeName + m_aPathDelimiter );
+
+ sal_uInt32 nCount = aAddonImagesNodeSeq.getLength();
+
+ // Init the property value sequence
+ OUString aURL;
+
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aImagesItemNode( aAddonImagesNode + aAddonImagesNodeSeq[n] );
+
+ // Create sequence for data access
+ Sequence< OUString > aAddonImageItemNodePropNames = { aImagesItemNode +
+ m_aPathDelimiter +
+ m_aPropNames[ OFFSET_MENUITEM_URL ] };
+
+ Sequence< Any > aAddonImageItemNodeValues = GetProperties( aAddonImageItemNodePropNames );
+
+ // An user-defined image entry must have a URL. As "ImageIdentifier" has a higher priority
+ // we also check if we already have an images association.
+ if (( aAddonImageItemNodeValues[0] >>= aURL ) &&
+ !aURL.isEmpty() &&
+ !HasAssociatedImages( aURL ))
+ {
+ OUString aImagesUserDefinedItemNode = aImagesItemNode +
+ m_aPathDelimiter +
+ IMAGES_NODENAME +
+ m_aPathDelimiter;
+
+ // Read a user-defined images data
+ std::unique_ptr<ImageEntry> pImageEntry = ReadImageData( aImagesUserDefinedItemNode );
+ if ( pImageEntry )
+ {
+ // Successfully read a user-defined images item, put it into our image manager
+ aImageManager.emplace( aURL, std::move(*pImageEntry) );
+ }
+ }
+ }
+}
+
+OUString AddonsOptions_Impl::GeneratePrefixURL()
+{
+ // Create a unique prefixed Add-On popup menu URL so it can be identified later as a runtime popup menu.
+ return m_aRootAddonPopupMenuURLPrexfix + OUString::number( ++m_nRootAddonPopupMenuId );
+}
+
+void AddonsOptions_Impl::ReadMenuMergeInstructions( MergeMenuInstructionContainer& aContainer )
+{
+ static constexpr OUString aMenuMergeRootName( u"AddonUI/OfficeMenuBarMerging/"_ustr );
+
+ Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aMenuMergeRootName );
+
+ sal_uInt32 nCount = aAddonMergeNodesSeq.getLength();
+
+ // Init the property value sequence
+ Sequence< OUString > aNodePropNames( 5 );
+ auto pNodePropNames = aNodePropNames.getArray();
+
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ OUString aMergeAddonInstructions( aMenuMergeRootName + aAddonMergeNodesSeq[i] );
+
+ Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions );
+ sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength();
+
+ for ( sal_uInt32 j = 0; j < nCountAddons; j++ )
+ {
+ OUString aMergeAddonInstructionBase = aMergeAddonInstructions +
+ m_aPathDelimiter +
+ aAddonInstMergeNodesSeq[j] +
+ m_aPathDelimiter;
+
+ // Create sequence for data access
+ pNodePropNames[0] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEPOINT ];
+
+ pNodePropNames[1] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMAND ];
+
+ pNodePropNames[2] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECOMMANDPARAMETER ];
+
+ pNodePropNames[3] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGEFALLBACK ];
+
+ pNodePropNames[4] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MERGECONTEXT ];
+
+ Sequence< Any > aNodePropValues = GetProperties( aNodePropNames );
+
+ MergeMenuInstruction aMergeMenuInstruction;
+ aNodePropValues[0] >>= aMergeMenuInstruction.aMergePoint;
+ aNodePropValues[1] >>= aMergeMenuInstruction.aMergeCommand;
+ aNodePropValues[2] >>= aMergeMenuInstruction.aMergeCommandParameter;
+ aNodePropValues[3] >>= aMergeMenuInstruction.aMergeFallback;
+ aNodePropValues[4] >>= aMergeMenuInstruction.aMergeContext;
+
+ ReadMergeMenuData( aMergeAddonInstructionBase, aMergeMenuInstruction.aMergeMenu );
+
+ aContainer.push_back( aMergeMenuInstruction );
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadMergeMenuData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeMenu )
+{
+ OUString aMergeMenuBaseNode( aMergeAddonInstructionBase+m_aPropMergeMenuNames[ OFFSET_MERGEMENU_MENUITEMS ] );
+
+ Sequence< OUString > aSubMenuNodeNames = GetNodeNames( aMergeMenuBaseNode );
+ aMergeMenuBaseNode += m_aPathDelimiter;
+
+ // extend the node names to have full path strings
+ for ( OUString& rName : asNonConstRange(aSubMenuNodeNames) )
+ rName = aMergeMenuBaseNode + rName;
+
+ ReadSubMenuEntries( aSubMenuNodeNames, rMergeMenu );
+}
+
+void AddonsOptions_Impl::ReadToolbarMergeInstructions( ToolbarMergingInstructions& rCachedToolbarMergingInstructions )
+{
+ static constexpr OUString aToolbarMergeRootName( u"AddonUI/OfficeToolbarMerging/"_ustr );
+
+ Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aToolbarMergeRootName );
+ sal_uInt32 nCount = aAddonMergeNodesSeq.getLength();
+
+ // Init the property value sequence
+ Sequence< OUString > aNodePropNames( 6 );
+ auto pNodePropNames = aNodePropNames.getArray();
+
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ OUString aMergeAddonInstructions( aToolbarMergeRootName + aAddonMergeNodesSeq[i] );
+
+ Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions );
+ sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength();
+
+ for ( sal_uInt32 j = 0; j < nCountAddons; j++ )
+ {
+ OUString aMergeAddonInstructionBase = aMergeAddonInstructions +
+ m_aPathDelimiter +
+ aAddonInstMergeNodesSeq[j] +
+ m_aPathDelimiter;
+
+ // Create sequence for data access
+ pNodePropNames[0] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBAR ];
+
+ pNodePropNames[1] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEPOINT ];
+
+ pNodePropNames[2] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMAND ];
+
+ pNodePropNames[3] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECOMMANDPARAMETER ];
+
+ pNodePropNames[4] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGEFALLBACK ];
+
+ pNodePropNames[5] = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_MERGECONTEXT ];
+
+ Sequence< Any > aNodePropValues = GetProperties( aNodePropNames );
+
+ MergeToolbarInstruction aMergeToolbarInstruction;
+ aNodePropValues[0] >>= aMergeToolbarInstruction.aMergeToolbar;
+ aNodePropValues[1] >>= aMergeToolbarInstruction.aMergePoint;
+ aNodePropValues[2] >>= aMergeToolbarInstruction.aMergeCommand;
+ aNodePropValues[3] >>= aMergeToolbarInstruction.aMergeCommandParameter;
+ aNodePropValues[4] >>= aMergeToolbarInstruction.aMergeFallback;
+ aNodePropValues[5] >>= aMergeToolbarInstruction.aMergeContext;
+
+ ReadMergeToolbarData( aMergeAddonInstructionBase,
+ aMergeToolbarInstruction.aMergeToolbarItems );
+
+ MergeToolbarInstructionContainer& rVector = rCachedToolbarMergingInstructions[ aMergeToolbarInstruction.aMergeToolbar ];
+ rVector.push_back( aMergeToolbarInstruction );
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadMergeToolbarData( std::u16string_view aMergeAddonInstructionBase, Sequence< Sequence< PropertyValue > >& rMergeToolbarItems )
+{
+ OUString aMergeToolbarBaseNode = aMergeAddonInstructionBase +
+ m_aPropMergeToolbarNames[ OFFSET_MERGETOOLBAR_TOOLBARITEMS ];
+
+ ReadToolBarItemSet( aMergeToolbarBaseNode, rMergeToolbarItems );
+}
+
+void AddonsOptions_Impl::ReadNotebookBarMergeInstructions(
+ NotebookBarMergingInstructions& rCachedNotebookBarMergingInstructions)
+{
+ static constexpr OUString aNotebookBarMergeRootName(u"AddonUI/OfficeNotebookBarMerging/"_ustr);
+
+ Sequence<OUString> aAddonMergeNodesSeq = GetNodeNames(aNotebookBarMergeRootName);
+ sal_uInt32 nCount = aAddonMergeNodesSeq.getLength();
+
+ // Init the property value sequence
+ Sequence<OUString> aNodePropNames(6);
+ auto pNodePropNames = aNodePropNames.getArray();
+
+ for (sal_uInt32 i = 0; i < nCount; i++)
+ {
+ OUString aMergeAddonInstructions(aNotebookBarMergeRootName + aAddonMergeNodesSeq[i]);
+
+ Sequence<OUString> aAddonInstMergeNodesSeq = GetNodeNames(aMergeAddonInstructions);
+ sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength();
+
+ for (sal_uInt32 j = 0; j < nCountAddons; j++)
+ {
+ OUString aMergeAddonInstructionBase = aMergeAddonInstructions +
+ m_aPathDelimiter +
+ aAddonInstMergeNodesSeq[j] +
+ m_aPathDelimiter;
+
+ // Create sequence for data access
+ pNodePropNames[0] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBAR];
+
+ pNodePropNames[1] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGEPOINT];
+
+ pNodePropNames[2] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECOMMAND];
+
+ pNodePropNames[3] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECOMMANDPARAMETER];
+
+ pNodePropNames[4] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGEFALLBACK];
+
+ pNodePropNames[5] = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_MERGECONTEXT];
+
+ Sequence<Any> aNodePropValues = GetProperties(aNodePropNames);
+
+ MergeNotebookBarInstruction aMergeNotebookBarInstruction;
+ aNodePropValues[0] >>= aMergeNotebookBarInstruction.aMergeNotebookBar;
+ aNodePropValues[1] >>= aMergeNotebookBarInstruction.aMergePoint;
+ aNodePropValues[2] >>= aMergeNotebookBarInstruction.aMergeCommand;
+ aNodePropValues[3] >>= aMergeNotebookBarInstruction.aMergeCommandParameter;
+ aNodePropValues[4] >>= aMergeNotebookBarInstruction.aMergeFallback;
+ aNodePropValues[5] >>= aMergeNotebookBarInstruction.aMergeContext;
+
+ ReadMergeNotebookBarData(aMergeAddonInstructionBase,
+ aMergeNotebookBarInstruction.aMergeNotebookBarItems);
+
+ MergeNotebookBarInstructionContainer& rVector
+ = rCachedNotebookBarMergingInstructions[aMergeNotebookBarInstruction
+ .aMergeNotebookBar];
+ rVector.push_back(aMergeNotebookBarInstruction);
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadMergeNotebookBarData(
+ std::u16string_view aMergeAddonInstructionBase,
+ Sequence<Sequence<PropertyValue>>& rMergeNotebookBarItems)
+{
+ OUString aMergeNotebookBarBaseNode = aMergeAddonInstructionBase +
+ m_aPropMergeNotebookBarNames[OFFSET_MERGENOTEBOOKBAR_NOTEBOOKBARITEMS];
+
+ ReadNotebookBarItemSet(aMergeNotebookBarBaseNode, rMergeNotebookBarItems);
+}
+
+void AddonsOptions_Impl::ReadStatusbarMergeInstructions( MergeStatusbarInstructionContainer& aContainer )
+{
+ static constexpr OUString aStatusbarMergeRootName( u"AddonUI/OfficeStatusbarMerging/"_ustr );
+
+ Sequence< OUString > aAddonMergeNodesSeq = GetNodeNames( aStatusbarMergeRootName );
+ sal_uInt32 nCount = aAddonMergeNodesSeq.getLength();
+
+ Sequence< OUString > aNodePropNames( 5 );
+ auto pNodePropNames = aNodePropNames.getArray();
+
+ for ( sal_uInt32 i = 0; i < nCount; i++ )
+ {
+ OUString aMergeAddonInstructions( aStatusbarMergeRootName + aAddonMergeNodesSeq[i] );
+
+ Sequence< OUString > aAddonInstMergeNodesSeq = GetNodeNames( aMergeAddonInstructions );
+ sal_uInt32 nCountAddons = aAddonInstMergeNodesSeq.getLength();
+
+ for ( sal_uInt32 j = 0; j < nCountAddons; j++ )
+ {
+ OUString aMergeAddonInstructionBase = aMergeAddonInstructions +
+ m_aPathDelimiter +
+ aAddonInstMergeNodesSeq[j] +
+ m_aPathDelimiter;
+
+ // Create sequence for data access
+ pNodePropNames[0] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGEPOINT ];
+
+ pNodePropNames[1] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECOMMAND ];
+
+ pNodePropNames[2] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECOMMANDPARAMETER ];
+
+ pNodePropNames[3] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGEFALLBACK ];
+
+ pNodePropNames[4] = aMergeAddonInstructionBase +
+ m_aPropMergeMenuNames[ OFFSET_MERGESTATUSBAR_MERGECONTEXT ];
+
+ Sequence< Any > aNodePropValues = GetProperties( aNodePropNames );
+
+ MergeStatusbarInstruction aMergeStatusbarInstruction;
+ aNodePropValues[0] >>= aMergeStatusbarInstruction.aMergePoint;
+ aNodePropValues[1] >>= aMergeStatusbarInstruction.aMergeCommand;
+ aNodePropValues[2] >>= aMergeStatusbarInstruction.aMergeCommandParameter;
+ // aNodePropValues[3] >>= aMergeStatusbarInstruction.aMergeFallback;
+ aNodePropValues[4] >>= aMergeStatusbarInstruction.aMergeContext;
+
+ ReadMergeStatusbarData( aMergeAddonInstructionBase,
+ aMergeStatusbarInstruction.aMergeStatusbarItems );
+
+ aContainer.push_back( aMergeStatusbarInstruction );
+ }
+ }
+}
+
+void AddonsOptions_Impl::ReadMergeStatusbarData(
+ std::u16string_view aMergeAddonInstructionBase,
+ Sequence< Sequence< PropertyValue > >& rMergeStatusbarItems )
+{
+ OUString aMergeStatusbarBaseNode = aMergeAddonInstructionBase +
+ m_aPropMergeStatusbarNames[ OFFSET_MERGESTATUSBAR_STATUSBARITEMS ];
+
+ OUString aAddonStatusbarItemSetNode( aMergeStatusbarBaseNode + m_aPathDelimiter );
+ Sequence< OUString > aAddonStatusbarItemSetNodeSeq = GetNodeNames( aMergeStatusbarBaseNode );
+
+ Sequence< PropertyValue > aStatusbarItem( PROPERTYCOUNT_STATUSBARITEM );
+ auto pStatusbarItem = aStatusbarItem.getArray();
+ pStatusbarItem[ OFFSET_STATUSBARITEM_URL ].Name = m_aPropNames[ INDEX_URL ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_TITLE ].Name = m_aPropNames[ INDEX_TITLE ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_CONTEXT ].Name = m_aPropNames[ INDEX_CONTEXT ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_ALIGN ].Name = m_aPropNames[ INDEX_ALIGN ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_AUTOSIZE ].Name = m_aPropNames[ INDEX_AUTOSIZE ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_OWNERDRAW ].Name = m_aPropNames[ INDEX_OWNERDRAW ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_MANDATORY ].Name = m_aPropNames[ INDEX_MANDATORY ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_WIDTH ].Name = m_aPropNames[ INDEX_WIDTH ];
+
+ sal_uInt32 nCount = aAddonStatusbarItemSetNodeSeq.getLength();
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ OUString aStatusbarItemNode( aAddonStatusbarItemSetNode + aAddonStatusbarItemSetNodeSeq[n] );
+
+ if ( ReadStatusBarItem( aStatusbarItemNode, aStatusbarItem ) )
+ {
+ sal_uInt32 nAddonCount = rMergeStatusbarItems.getLength();
+ rMergeStatusbarItems.realloc( nAddonCount+1 );
+ rMergeStatusbarItems.getArray()[nAddonCount] = aStatusbarItem;
+ }
+ }
+}
+
+bool AddonsOptions_Impl::ReadStatusBarItem(
+ std::u16string_view aStatusarItemNodeName,
+ Sequence< PropertyValue >& aStatusbarItem )
+{
+ bool bResult( false );
+ OUString aURL;
+ OUString aAddonStatusbarItemTreeNode( aStatusarItemNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aStatusbarItemNodePropValues = GetProperties( GetPropertyNamesStatusbarItem( aAddonStatusbarItemTreeNode ) );
+
+ // Command URL is required
+ if (( aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_URL ] >>= aURL ) && aURL.getLength() > 0 )
+ {
+ auto pStatusbarItem = aStatusbarItem.getArray();
+ pStatusbarItem[ OFFSET_STATUSBARITEM_URL ].Value <<= aURL;
+ pStatusbarItem[ OFFSET_STATUSBARITEM_TITLE ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_TITLE ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_CONTEXT ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_CONTEXT ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_ALIGN ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_ALIGN ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_AUTOSIZE ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_AUTOSIZE ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_OWNERDRAW ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_OWNERDRAW ];
+ pStatusbarItem[ OFFSET_STATUSBARITEM_MANDATORY ].Value = aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_MANDATORY ];
+
+ // Configuration uses hyper for long. Therefore transform into sal_Int32
+ sal_Int64 nValue( 0 );
+ aStatusbarItemNodePropValues[ OFFSET_STATUSBARITEM_WIDTH ] >>= nValue;
+ pStatusbarItem[ OFFSET_STATUSBARITEM_WIDTH ].Value <<= sal_Int32( nValue );
+
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+bool AddonsOptions_Impl::ReadMenuItem( std::u16string_view aMenuNodeName, Sequence< PropertyValue >& aMenuItem, bool bIgnoreSubMenu )
+{
+ bool bResult = false;
+ OUString aStrValue;
+ OUString aAddonMenuItemTreeNode( aMenuNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aMenuItemNodePropValues = GetProperties( GetPropertyNamesMenuItem( aAddonMenuItemTreeNode ) );
+ if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_TITLE ] >>= aStrValue ) && !aStrValue.isEmpty() )
+ {
+ auto pMenuItem = aMenuItem.getArray();
+ pMenuItem[ OFFSET_MENUITEM_TITLE ].Value <<= aStrValue;
+
+ OUString aRootSubMenuName( aAddonMenuItemTreeNode + m_aPropNames[ INDEX_SUBMENU ] );
+ Sequence< OUString > aRootSubMenuNodeNames = GetNodeNames( aRootSubMenuName );
+ if ( aRootSubMenuNodeNames.hasElements() && !bIgnoreSubMenu )
+ {
+ // Set a unique prefixed Add-On popup menu URL so it can be identified later
+ OUString aPopupMenuURL = GeneratePrefixURL();
+ OUString aPopupMenuImageId;
+
+ aMenuItemNodePropValues[ OFFSET_MENUITEM_IMAGEIDENTIFIER ] >>= aPopupMenuImageId;
+ ReadAndAssociateImages( aPopupMenuURL, aPopupMenuImageId );
+
+ // A popup menu must have a title and can have a URL and ImageIdentifier
+ // Set the other property values to empty
+ pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aPopupMenuURL;
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Value <<= OUString();
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= aPopupMenuImageId;
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_CONTEXT ];
+
+ // Continue to read the sub menu nodes
+ Sequence< Sequence< PropertyValue > > aSubMenuSeq;
+ OUString aSubMenuRootNodeName( aRootSubMenuName + m_aPathDelimiter );
+ for ( OUString& rName : asNonConstRange(aRootSubMenuNodeNames) )
+ rName = aSubMenuRootNodeName + rName;
+ ReadSubMenuEntries( aRootSubMenuNodeNames, aSubMenuSeq );
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= aSubMenuSeq;
+ bResult = true;
+ }
+ else if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_URL ] >>= aStrValue ) && !aStrValue.isEmpty() )
+ {
+ // A simple menu item => read the other properties;
+ OUString aMenuImageId;
+
+ aMenuItemNodePropValues[ OFFSET_MENUITEM_IMAGEIDENTIFIER ] >>= aMenuImageId;
+ ReadAndAssociateImages( aStrValue, aMenuImageId );
+
+ pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aStrValue;
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_TARGET ];
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= aMenuImageId;
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value = aMenuItemNodePropValues[ OFFSET_MENUITEM_CONTEXT ];
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= Sequence< Sequence< PropertyValue > >(); // Submenu set!
+
+ bResult = true;
+ }
+ }
+ else if (( aMenuItemNodePropValues[ OFFSET_MENUITEM_URL ] >>= aStrValue ) &&
+ aStrValue == SEPARATOR_URL )
+ {
+ auto pMenuItem = aMenuItem.getArray();
+
+ // Separator
+ pMenuItem[ OFFSET_MENUITEM_URL ].Value <<= aStrValue;
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Value <<= OUString();
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Value <<= OUString();
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Value <<= OUString();
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Value <<= Sequence< Sequence< PropertyValue > >(); // Submenu set!
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+bool AddonsOptions_Impl::ReadPopupMenu( std::u16string_view aPopupMenuNodeName, Sequence< PropertyValue >& aPopupMenu )
+{
+ bool bResult = false;
+ OUString aStrValue;
+ OUString aAddonPopupMenuTreeNode( aPopupMenuNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aPopupMenuNodePropValues = GetProperties( GetPropertyNamesPopupMenu( aAddonPopupMenuTreeNode ) );
+ if (( aPopupMenuNodePropValues[ OFFSET_POPUPMENU_TITLE ] >>= aStrValue ) &&
+ !aStrValue.isEmpty() )
+ {
+ auto pPopupMenu = aPopupMenu.getArray();
+ pPopupMenu[ OFFSET_POPUPMENU_TITLE ].Value <<= aStrValue;
+
+ OUString aRootSubMenuName( aAddonPopupMenuTreeNode + m_aPropNames[ INDEX_SUBMENU ] );
+ Sequence< OUString > aRootSubMenuNodeNames = GetNodeNames( aRootSubMenuName );
+ if ( aRootSubMenuNodeNames.hasElements() )
+ {
+ // A top-level popup menu needs a title
+ // Set a unique prefixed Add-On popup menu URL so it can be identified later
+ OUString aPopupMenuURL = GeneratePrefixURL();
+
+ pPopupMenu[ OFFSET_POPUPMENU_URL ].Value <<= aPopupMenuURL;
+ pPopupMenu[ OFFSET_POPUPMENU_CONTEXT ].Value = aPopupMenuNodePropValues[ OFFSET_POPUPMENU_CONTEXT ];
+
+ // Continue to read the sub menu nodes
+ Sequence< Sequence< PropertyValue > > aSubMenuSeq;
+ OUString aSubMenuRootNodeName( aRootSubMenuName + m_aPathDelimiter );
+ for ( OUString& rName : asNonConstRange(aRootSubMenuNodeNames) )
+ rName = aSubMenuRootNodeName + rName;
+ ReadSubMenuEntries( aRootSubMenuNodeNames, aSubMenuSeq );
+ pPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value <<= aSubMenuSeq;
+ bResult = true;
+ }
+ }
+
+ return bResult;
+}
+
+void AddonsOptions_Impl::AppendPopupMenu( Sequence< PropertyValue >& rTargetPopupMenu, const Sequence< PropertyValue >& rSourcePopupMenu )
+{
+ Sequence< Sequence< PropertyValue > > aTargetSubMenuSeq;
+ Sequence< Sequence< PropertyValue > > aSourceSubMenuSeq;
+
+ if (( rTargetPopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value >>= aTargetSubMenuSeq ) &&
+ ( rSourcePopupMenu[ OFFSET_POPUPMENU_SUBMENU ].Value >>= aSourceSubMenuSeq ))
+ {
+ sal_uInt32 nIndex = aTargetSubMenuSeq.getLength();
+ aTargetSubMenuSeq.realloc( nIndex + aSourceSubMenuSeq.getLength() );
+ auto pTargetSubMenuSeq = aTargetSubMenuSeq.getArray();
+ for ( Sequence<PropertyValue> const & rSeq : std::as_const(aSourceSubMenuSeq) )
+ pTargetSubMenuSeq[nIndex++] = rSeq;
+ rTargetPopupMenu.getArray()[ OFFSET_POPUPMENU_SUBMENU ].Value <<= aTargetSubMenuSeq;
+ }
+}
+
+bool AddonsOptions_Impl::ReadToolBarItem( std::u16string_view aToolBarItemNodeName, Sequence< PropertyValue >& aToolBarItem )
+{
+ bool bResult = false;
+ OUString aURL;
+ OUString aAddonToolBarItemTreeNode( aToolBarItemNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aToolBarItemNodePropValues = GetProperties( GetPropertyNamesToolBarItem( aAddonToolBarItemTreeNode ) );
+
+ // A toolbar item must have a command URL
+ if (( aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_URL ] >>= aURL ) && !aURL.isEmpty() )
+ {
+ OUString aTitle;
+ if ( aURL == SEPARATOR_URL )
+ {
+ auto pToolBarItem = aToolBarItem.getArray();
+
+ // A separator toolbar item only needs a URL
+ pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Value <<= aURL;
+ pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Value <<= OUString();
+ pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Value <<= sal_Int32( 0 );
+
+ bResult = true;
+ }
+ else if (( aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_TITLE ] >>= aTitle ) && !aTitle.isEmpty() )
+ {
+ auto pToolBarItem = aToolBarItem.getArray();
+
+ // A normal toolbar item must also have title => read the other properties;
+ OUString aImageId;
+
+ // Try to map a user-defined image URL to our internal private image URL
+ aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ] >>= aImageId;
+ ReadAndAssociateImages( aURL, aImageId );
+
+ pToolBarItem[ OFFSET_TOOLBARITEM_URL ].Value <<= aURL;
+ pToolBarItem[ OFFSET_TOOLBARITEM_TITLE ].Value <<= aTitle;
+ pToolBarItem[ OFFSET_TOOLBARITEM_TARGET ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_TARGET ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_IMAGEIDENTIFIER ].Value <<= aImageId;
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTEXT ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_CONTEXT ];
+ pToolBarItem[ OFFSET_TOOLBARITEM_CONTROLTYPE ].Value = aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_CONTROLTYPE ];
+
+ // Configuration uses hyper for long. Therefore transform into sal_Int32
+ sal_Int64 nValue( 0 );
+ aToolBarItemNodePropValues[ OFFSET_TOOLBARITEM_WIDTH ] >>= nValue;
+ pToolBarItem[ OFFSET_TOOLBARITEM_WIDTH ].Value <<= sal_Int32( nValue );
+
+ bResult = true;
+ }
+ }
+
+ return bResult;
+}
+
+bool AddonsOptions_Impl::ReadNotebookBarItem( std::u16string_view aNotebookBarItemNodeName, Sequence< PropertyValue >& aNotebookBarItem )
+{
+ bool bResult = false;
+ OUString aURL;
+ OUString aAddonNotebookBarItemTreeNode( aNotebookBarItemNodeName + m_aPathDelimiter );
+
+ Sequence< Any > aNotebookBarItemNodePropValues = GetProperties( GetPropertyNamesNotebookBarItem( aAddonNotebookBarItemTreeNode ) );
+
+ // A toolbar item must have a command URL
+ if (( aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_URL ] >>= aURL ) && !aURL.isEmpty() )
+ {
+ OUString aTitle;
+ if ( aURL == SEPARATOR_URL )
+ {
+ auto pNotebookBarItem = aNotebookBarItem.getArray();
+
+ // A separator toolbar item only needs a URL
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_URL ].Value <<= aURL;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TITLE ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TARGET ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTEXT ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ].Value <<= OUString();
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_WIDTH ].Value <<= sal_Int32( 0 );
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_STYLE ].Value <<= OUString();
+
+ bResult = true;
+ }
+ else if (( aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_TITLE ] >>= aTitle ) && !aTitle.isEmpty() )
+ {
+ auto pNotebookBarItem = aNotebookBarItem.getArray();
+
+ // A normal toolbar item must also have title => read the other properties;
+ OUString aImageId;
+
+ // Try to map a user-defined image URL to our internal private image URL
+ aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ] >>= aImageId;
+ ReadAndAssociateImages( aURL, aImageId );
+
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_URL ].Value <<= aURL;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TITLE ].Value <<= aTitle;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_TARGET ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_TARGET ];
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_IMAGEIDENTIFIER ].Value <<= aImageId;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTEXT ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_CONTEXT ];
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_CONTROLTYPE ];
+
+ // Configuration uses hyper for long. Therefore transform into sal_Int32
+ sal_Int64 nValue( 0 );
+ aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_WIDTH ] >>= nValue;
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_WIDTH ].Value <<= sal_Int32( nValue );
+ pNotebookBarItem[ OFFSET_NOTEBOOKBARITEM_STYLE ].Value = aNotebookBarItemNodePropValues[ OFFSET_NOTEBOOKBARITEM_STYLE ];
+
+ bResult = true;
+ }
+ }
+
+ return bResult;
+}
+
+void AddonsOptions_Impl::ReadSubMenuEntries( const Sequence< OUString >& aSubMenuNodeNames, Sequence< Sequence< PropertyValue > >& rSubMenuSeq )
+{
+ Sequence< PropertyValue > aMenuItem( PROPERTYCOUNT_MENUITEM );
+ auto pMenuItem = aMenuItem.getArray();
+
+ // Init the property value sequence
+ pMenuItem[ OFFSET_MENUITEM_URL ].Name = PROPERTYNAME_URL;
+ pMenuItem[ OFFSET_MENUITEM_TITLE ].Name = PROPERTYNAME_TITLE;
+ pMenuItem[ OFFSET_MENUITEM_TARGET ].Name = PROPERTYNAME_TARGET;
+ pMenuItem[ OFFSET_MENUITEM_IMAGEIDENTIFIER ].Name = PROPERTYNAME_IMAGEIDENTIFIER;
+ pMenuItem[ OFFSET_MENUITEM_CONTEXT ].Name = PROPERTYNAME_CONTEXT;
+ pMenuItem[ OFFSET_MENUITEM_SUBMENU ].Name = PROPERTYNAME_SUBMENU; // Submenu set!
+
+ sal_uInt32 nIndex = 0;
+ sal_uInt32 nCount = aSubMenuNodeNames.getLength();
+ for ( sal_uInt32 n = 0; n < nCount; n++ )
+ {
+ if ( ReadMenuItem( aSubMenuNodeNames[n], aMenuItem ))
+ {
+ sal_uInt32 nSubMenuCount = rSubMenuSeq.getLength() + 1;
+ rSubMenuSeq.realloc( nSubMenuCount );
+ rSubMenuSeq.getArray()[nIndex++] = aMenuItem;
+ }
+ }
+}
+
+bool AddonsOptions_Impl::HasAssociatedImages( const OUString& aURL )
+{
+ // FIXME: potentially this is not so useful in a world of delayed image loading
+ ImageManager::const_iterator pIter = m_aImageManager.find( aURL );
+ return ( pIter != m_aImageManager.end() );
+}
+
+void AddonsOptions_Impl::SubstituteVariables( OUString& aURL )
+{
+ aURL = comphelper::getExpandedUri(
+ comphelper::getProcessComponentContext(), aURL);
+}
+
+BitmapEx AddonsOptions_Impl::ReadImageFromURL(const OUString& aImageURL)
+{
+ BitmapEx aImage;
+
+ std::unique_ptr<SvStream> pStream = UcbStreamHelper::CreateStream( aImageURL, StreamMode::STD_READ );
+ if ( pStream && ( pStream->GetErrorCode() == ERRCODE_NONE ))
+ {
+ // Use graphic class to also support more graphic formats (bmp,png,...)
+ Graphic aGraphic;
+
+ GraphicFilter& rGF = GraphicFilter::GetGraphicFilter();
+ rGF.ImportGraphic( aGraphic, u"", *pStream );
+
+ BitmapEx aBitmapEx = aGraphic.GetBitmapEx();
+
+ Size aBmpSize = aBitmapEx.GetSizePixel();
+ if ( !aBmpSize.IsEmpty() )
+ {
+ // Support non-transparent bitmaps to be downward compatible with OOo 1.1.x addons
+ if( !aBitmapEx.IsAlpha() )
+ aBitmapEx = BitmapEx( aBitmapEx.GetBitmap(), COL_LIGHTMAGENTA );
+
+ aImage = aBitmapEx;
+ }
+ }
+
+ return aImage;
+}
+
+void AddonsOptions_Impl::ReadAndAssociateImages( const OUString& aURL, const OUString& aImageId )
+{
+ if ( aImageId.isEmpty() )
+ return;
+
+ ImageEntry aImageEntry;
+ OUString aImageURL( aImageId );
+
+ SubstituteVariables( aImageURL );
+
+ // Loop to create the two possible image names and try to read the bitmap files
+ static const char* aExtArray[] = { "_16", "_26" };
+ for ( size_t i = 0; i < std::size(aExtArray); i++ )
+ {
+ OUStringBuffer aFileURL( aImageURL );
+ aFileURL.appendAscii( aExtArray[i] );
+ aFileURL.append( ".bmp" );
+
+ aImageEntry.addImage( !i ? IMGSIZE_SMALL : IMGSIZE_BIG, aFileURL.makeStringAndClear() );
+ }
+
+ m_aImageManager.emplace( aURL, aImageEntry );
+}
+
+std::unique_ptr<AddonsOptions_Impl::ImageEntry> AddonsOptions_Impl::ReadImageData( std::u16string_view aImagesNodeName )
+{
+ Sequence< OUString > aImageDataNodeNames = GetPropertyNamesImages( aImagesNodeName );
+ Sequence< Any > aPropertyData;
+ Sequence< sal_Int8 > aImageDataSeq;
+ OUString aImageURL;
+
+ std::unique_ptr<ImageEntry> pEntry;
+
+ // It is possible to use both forms (embedded image data and URLs to external bitmap files) at the
+ // same time. Embedded image data has a higher priority.
+ aPropertyData = GetProperties( aImageDataNodeNames );
+ for ( int i = 0; i < PROPERTYCOUNT_IMAGES; i++ )
+ {
+ if ( i < PROPERTYCOUNT_EMBEDDED_IMAGES )
+ {
+ // Extract image data from the embedded hex binary sequence
+ BitmapEx aImage;
+ if (( aPropertyData[i] >>= aImageDataSeq ) &&
+ aImageDataSeq.hasElements() &&
+ ( CreateImageFromSequence( aImage, aImageDataSeq ) ) )
+ {
+ if ( !pEntry )
+ pEntry.reset(new ImageEntry);
+ pEntry->addImage(i == OFFSET_IMAGES_SMALL ? IMGSIZE_SMALL : IMGSIZE_BIG, aImage);
+ }
+ }
+ else if ( i == OFFSET_IMAGES_SMALL_URL || i == OFFSET_IMAGES_BIG_URL )
+ {
+ if(!pEntry)
+ pEntry.reset(new ImageEntry());
+
+ // Retrieve image data from an external bitmap file. Make sure that embedded image data
+ // has a higher priority.
+ if (aPropertyData[i] >>= aImageURL)
+ {
+ SubstituteVariables(aImageURL);
+ pEntry->addImage(i == OFFSET_IMAGES_SMALL_URL ? IMGSIZE_SMALL : IMGSIZE_BIG, aImageURL);
+ }
+ }
+ }
+
+ return pEntry;
+}
+
+bool AddonsOptions_Impl::CreateImageFromSequence( BitmapEx& rImage, Sequence< sal_Int8 >& rBitmapDataSeq ) const
+{
+ bool bResult = false;
+
+ if ( rBitmapDataSeq.hasElements() )
+ {
+ SvMemoryStream aMemStream( rBitmapDataSeq.getArray(), rBitmapDataSeq.getLength(), StreamMode::STD_READ );
+
+ ReadDIBBitmapEx(rImage, aMemStream);
+
+ if( !rImage.IsAlpha() )
+ {
+ // Support non-transparent bitmaps to be downward compatible with OOo 1.1.x addons
+ rImage = BitmapEx( rImage.GetBitmap(), COL_LIGHTMAGENTA );
+ }
+
+ bResult = true;
+ }
+
+ return bResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesMenuItem( std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_MENUITEM );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[OFFSET_MENUITEM_URL] = aPropertyRootNode + m_aPropNames[ INDEX_URL ];
+ plResult[OFFSET_MENUITEM_TITLE] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ];
+ plResult[OFFSET_MENUITEM_IMAGEIDENTIFIER] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER ];
+ plResult[OFFSET_MENUITEM_TARGET] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ];
+ plResult[OFFSET_MENUITEM_CONTEXT] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ];
+ plResult[OFFSET_MENUITEM_SUBMENU] = aPropertyRootNode + m_aPropNames[ INDEX_SUBMENU ];
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesPopupMenu( std::u16string_view aPropertyRootNode ) const
+{
+ // The URL is automatically set and not read from the configuration.
+ Sequence< OUString > lResult( PROPERTYCOUNT_POPUPMENU-1 );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[OFFSET_POPUPMENU_TITLE] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ];
+ plResult[OFFSET_POPUPMENU_CONTEXT] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ];
+ plResult[OFFSET_POPUPMENU_SUBMENU] = aPropertyRootNode + m_aPropNames[ INDEX_SUBMENU ];
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesToolBarItem( std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_TOOLBARITEM );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[0] = aPropertyRootNode + m_aPropNames[ INDEX_URL ];
+ plResult[1] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ];
+ plResult[2] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ plResult[3] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ];
+ plResult[4] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ];
+ plResult[5] = aPropertyRootNode + m_aPropNames[ INDEX_CONTROLTYPE ];
+ plResult[6] = aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ];
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesNotebookBarItem( std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_NOTEBOOKBARITEM );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[0] = aPropertyRootNode + m_aPropNames[ INDEX_URL ];
+ plResult[1] = aPropertyRootNode + m_aPropNames[ INDEX_TITLE ];
+ plResult[2] = aPropertyRootNode + m_aPropNames[ INDEX_IMAGEIDENTIFIER];
+ plResult[3] = aPropertyRootNode + m_aPropNames[ INDEX_TARGET ];
+ plResult[4] = aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ];
+ plResult[5] = aPropertyRootNode + m_aPropNames[ INDEX_CONTROLTYPE ];
+ plResult[6] = aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ];
+ plResult[7] = aPropertyRootNode + m_aPropNames[ INDEX_STYLE ];
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesStatusbarItem(
+ std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_STATUSBARITEM );
+ auto plResult = lResult.getArray();
+
+ plResult[0] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_URL ] );
+ plResult[1] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_TITLE ] );
+ plResult[2] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_CONTEXT ] );
+ plResult[3] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_ALIGN ] );
+ plResult[4] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_AUTOSIZE ] );
+ plResult[5] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_OWNERDRAW ] );
+ plResult[6] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_MANDATORY ] );
+ plResult[7] = OUString( aPropertyRootNode + m_aPropNames[ INDEX_WIDTH ] );
+
+ return lResult;
+}
+
+Sequence< OUString > AddonsOptions_Impl::GetPropertyNamesImages( std::u16string_view aPropertyRootNode ) const
+{
+ Sequence< OUString > lResult( PROPERTYCOUNT_IMAGES );
+ auto plResult = lResult.getArray();
+
+ // Create property names dependent from the root node name
+ plResult[0] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALL ];
+ plResult[1] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIG ];
+ plResult[2] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC ];
+ plResult[3] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIGHC ];
+ plResult[4] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALL_URL ];
+ plResult[5] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIG_URL ];
+ plResult[6] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_SMALLHC_URL];
+ plResult[7] = aPropertyRootNode + m_aPropImagesNames[ OFFSET_IMAGES_BIGHC_URL ];
+
+ return lResult;
+}
+
+namespace{
+ //global
+ std::weak_ptr<AddonsOptions_Impl> g_pAddonsOptions;
+}
+
+AddonsOptions::AddonsOptions()
+{
+ // Global access, must be guarded (multithreading!).
+ MutexGuard aGuard( GetOwnStaticMutex() );
+
+ m_pImpl = g_pAddonsOptions.lock();
+ if( !m_pImpl )
+ {
+ m_pImpl = std::make_shared<AddonsOptions_Impl>();
+ g_pAddonsOptions = m_pImpl;
+ }
+}
+
+AddonsOptions::~AddonsOptions()
+{
+ // Global access, must be guarded (multithreading!)
+ MutexGuard aGuard( GetOwnStaticMutex() );
+
+ m_pImpl.reset();
+}
+
+// public method
+
+bool AddonsOptions::HasAddonsMenu() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->HasAddonsMenu();
+}
+
+// public method
+
+sal_Int32 AddonsOptions::GetAddonsToolBarCount() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsToolBarCount();
+}
+
+// public method
+
+sal_Int32 AddonsOptions::GetAddonsNotebookBarCount() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsNotebookBarCount();
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsMenu() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsMenu();
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsMenuBarPart() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsMenuBarPart();
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsToolBarPart( sal_uInt32 nIndex ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsToolBarPart( nIndex );
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsNotebookBarPart( sal_uInt32 nIndex ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsNotebookBarPart( nIndex );
+}
+
+// public method
+
+OUString AddonsOptions::GetAddonsToolbarResourceName( sal_uInt32 nIndex ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsToolbarResourceName( nIndex );
+}
+
+// public method
+
+OUString AddonsOptions::GetAddonsNotebookBarResourceName( sal_uInt32 nIndex ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsNotebookBarResourceName( nIndex );
+}
+
+// public method
+
+const Sequence< Sequence< PropertyValue > >& AddonsOptions::GetAddonsHelpMenu() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetAddonsHelpMenu();
+}
+
+// public method
+
+const MergeMenuInstructionContainer& AddonsOptions::GetMergeMenuInstructions() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetMergeMenuInstructions();
+}
+
+// public method
+
+bool AddonsOptions::GetMergeToolbarInstructions(
+ const OUString& rToolbarName,
+ MergeToolbarInstructionContainer& rToolbarInstructions ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetMergeToolbarInstructions(
+ rToolbarName, rToolbarInstructions );
+}
+
+// public method
+
+bool AddonsOptions::GetMergeNotebookBarInstructions(
+ const OUString& rNotebookBarName,
+ MergeNotebookBarInstructionContainer& rNotebookBarInstructions ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetMergeNotebookBarInstructions(
+ rNotebookBarName, rNotebookBarInstructions );
+}
+
+//public method
+
+const MergeStatusbarInstructionContainer& AddonsOptions::GetMergeStatusbarInstructions() const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetMergeStatusbarInstructions();
+}
+
+// public method
+
+BitmapEx AddonsOptions::GetImageFromURL( const OUString& aURL, bool bBig, bool bNoScale ) const
+{
+ MutexGuard aGuard( GetOwnStaticMutex() );
+ return m_pImpl->GetImageFromURL( aURL, bBig, bNoScale );
+}
+
+// public method
+
+BitmapEx AddonsOptions::GetImageFromURL( const OUString& aURL, bool bBig ) const
+{
+ return GetImageFromURL( aURL, bBig, false );
+}
+
+Mutex& AddonsOptions::GetOwnStaticMutex()
+{
+ // Create static mutex variable.
+ static Mutex ourMutex;
+
+ return ourMutex;
+}
+
+IMPL_LINK_NOARG(AddonsOptions_Impl, NotifyEvent, void*, void)
+{
+ MutexGuard aGuard(AddonsOptions::GetOwnStaticMutex());
+ ReadConfigurationData();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/framelistanalyzer.cxx b/framework/source/fwe/classes/framelistanalyzer.cxx
new file mode 100644
index 0000000000..d524d23fdf
--- /dev/null
+++ b/framework/source/fwe/classes/framelistanalyzer.cxx
@@ -0,0 +1,260 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/framelistanalyzer.hxx>
+
+#include <targets.h>
+#include <properties.h>
+
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/UnknownModuleException.hpp>
+#include <com/sun/star/frame/XFrame.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <sal/log.hxx>
+
+namespace framework{
+
+FrameListAnalyzer::FrameListAnalyzer( const css::uno::Reference< css::frame::XFramesSupplier >& xSupplier ,
+ const css::uno::Reference< css::frame::XFrame >& xReferenceFrame ,
+ FrameAnalyzerFlags eDetectMode )
+ : m_xSupplier (xSupplier )
+ , m_xReferenceFrame(xReferenceFrame)
+ , m_eDetectMode (eDetectMode )
+{
+ impl_analyze();
+}
+
+FrameListAnalyzer::~FrameListAnalyzer()
+{
+}
+
+/** returns an analyzed list of all currently opened (top!) frames inside the desktop tree.
+
+ We try to get a snapshot of all opened frames, which are part of the desktop frame container.
+ Of course we can't access frames, which stands outside of this tree.
+ But it's necessary to collect top frames here only. Otherwise we interpret closing of last
+ frame wrong. Further we analyze this list and split into different parts.
+ E.g. for "CloseDoc" we must know, which frames of the given list refer to the same model.
+ These frames must be closed then. But all other frames must be untouched.
+ In case the request was "CloseWin" these split lists can be used too, to decide if the last window
+ or document was closed. Then we have to initialize the backing window...
+ Last but not least we must know something about our special help frame. It must be handled
+ separately. And last but not least - the backing component frame must be detected too.
+*/
+
+void FrameListAnalyzer::impl_analyze()
+{
+ // reset all members to get a consistent state
+ m_bReferenceIsHidden = false;
+ m_bReferenceIsHelp = false;
+ m_bReferenceIsBacking = false;
+ m_xHelp.clear();
+ m_xBackingComponent.clear();
+
+ // try to get the task container by using the given supplier
+ css::uno::Reference< css::container::XIndexAccess > xFrameContainer = m_xSupplier->getFrames();
+
+ // All return list get an initial size to include all possible frames.
+ // They will be packed at the end of this method ... using the actual step positions then.
+ sal_Int32 nVisibleStep = 0;
+ sal_Int32 nHiddenStep = 0;
+ sal_Int32 nModelStep = 0;
+ sal_Int32 nCount = xFrameContainer->getCount();
+
+ m_lOtherVisibleFrames.resize(nCount);
+ m_lOtherHiddenFrames.resize(nCount);
+ m_lModelFrames.resize(nCount);
+
+ // ask for the model of the given reference frame.
+ // It must be compared with the model of every frame of the container
+ // to sort it into the list of frames with the same model.
+ // Suppress this step, if right detect mode isn't set.
+ css::uno::Reference< css::frame::XModel > xReferenceModel;
+ if (m_eDetectMode & FrameAnalyzerFlags::Model)
+ {
+ css::uno::Reference< css::frame::XController > xReferenceController;
+ if (m_xReferenceFrame.is())
+ xReferenceController = m_xReferenceFrame->getController();
+ if (xReferenceController.is())
+ xReferenceModel = xReferenceController->getModel();
+ }
+
+ // check, if the reference frame is in hidden mode.
+ // But look, if this analyze step is really needed.
+ css::uno::Reference< css::beans::XPropertySet > xSet(m_xReferenceFrame, css::uno::UNO_QUERY);
+ if ( (m_eDetectMode & FrameAnalyzerFlags::Hidden) && xSet.is() )
+ {
+ xSet->getPropertyValue(FRAME_PROPNAME_ASCII_ISHIDDEN) >>= m_bReferenceIsHidden;
+ }
+
+ // check, if the reference frame includes the backing component.
+ // But look, if this analyze step is really needed.
+ if ((m_eDetectMode & FrameAnalyzerFlags::BackingComponent) && m_xReferenceFrame.is() )
+ {
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleMgr = css::frame::ModuleManager::create(xContext);
+ OUString sModule = xModuleMgr->identify(m_xReferenceFrame);
+ m_bReferenceIsBacking = sModule == "com.sun.star.frame.StartModule";
+ }
+ catch(const css::frame::UnknownModuleException&)
+ {
+ }
+ catch(const css::uno::Exception&)
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ // check, if the reference frame includes the help module.
+ // But look, if this analyze step is really needed.
+ if (
+ (m_eDetectMode & FrameAnalyzerFlags::Help) &&
+ (m_xReferenceFrame.is() ) &&
+ (m_xReferenceFrame->getName() == SPECIALTARGET_HELPTASK)
+ )
+ {
+ m_bReferenceIsHelp = true;
+ }
+
+ try
+ {
+ // Step over all frames of the desktop frame container and analyze it.
+ for (sal_Int32 i=0; i<nCount; ++i)
+ {
+ // Ignore invalid items ... and of course the reference frame.
+ // It will be a member of the given frame list too - but it was already
+ // analyzed before!
+ css::uno::Reference< css::frame::XFrame > xFrame;
+ if (
+ !(xFrameContainer->getByIndex(i) >>= xFrame) ||
+ !(xFrame.is() ) ||
+ (xFrame==m_xReferenceFrame )
+ )
+ continue;
+
+ if (
+ (m_eDetectMode & FrameAnalyzerFlags::Zombie) &&
+ (
+ (!xFrame->getContainerWindow().is()) ||
+ (!xFrame->getComponentWindow().is())
+ )
+ )
+ {
+ SAL_INFO("fwk", "FrameListAnalyzer::impl_analyze(): ZOMBIE!");
+ }
+
+ // a) Is it the special help task?
+ // Return it separated from any return list.
+ if (
+ (m_eDetectMode & FrameAnalyzerFlags::Help) &&
+ (xFrame->getName()==SPECIALTARGET_HELPTASK)
+ )
+ {
+ m_xHelp = xFrame;
+ continue;
+ }
+
+ // b) Or is includes this task the special backing component?
+ // Return it separated from any return list.
+ // But check if the reference task itself is the backing frame.
+ // Our user must know it to decide right.
+ if (m_eDetectMode & FrameAnalyzerFlags::BackingComponent)
+ {
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext = ::comphelper::getProcessComponentContext();
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleMgr = css::frame::ModuleManager::create(xContext);
+ OUString sModule = xModuleMgr->identify(xFrame);
+ if (sModule == "com.sun.star.frame.StartModule")
+ {
+ m_xBackingComponent = xFrame;
+ continue;
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+ }
+
+ // c) Or is it the a task, which uses the specified model?
+ // Add it to the list of "model frames".
+ if (m_eDetectMode & FrameAnalyzerFlags::Model)
+ {
+ css::uno::Reference< css::frame::XController > xController = xFrame->getController();
+ css::uno::Reference< css::frame::XModel > xModel;
+ if (xController.is())
+ xModel = xController->getModel();
+ if (xModel==xReferenceModel)
+ {
+ m_lModelFrames[nModelStep] = xFrame;
+ ++nModelStep;
+ continue;
+ }
+ }
+
+ // d) Or is it the a task, which use another or no model at all?
+ // Add it to the list of "other frames". But look for its
+ // visible state ... if it's allowed to do so.
+
+ bool bHidden = false;
+ if (m_eDetectMode & FrameAnalyzerFlags::Hidden)
+ {
+ xSet.set(xFrame, css::uno::UNO_QUERY);
+ if (xSet.is())
+ {
+ xSet->getPropertyValue(FRAME_PROPNAME_ASCII_ISHIDDEN) >>= bHidden;
+ }
+ }
+
+ if (bHidden)
+ {
+ m_lOtherHiddenFrames[nHiddenStep] = xFrame;
+ ++nHiddenStep;
+ }
+ else
+ {
+ m_lOtherVisibleFrames[nVisibleStep] = xFrame;
+ ++nVisibleStep;
+ }
+ }
+ }
+ catch (const css::lang::IndexOutOfBoundsException&)
+ {
+ // stop copying if index seems to be wrong.
+ // This interface can't really guarantee its count for multithreaded
+ // environments. So it can occur!
+ }
+
+ // Pack both lists by using the actual step positions.
+ // All empty or ignorable items should exist at the end of these lists
+ // behind the position pointers. So they will be removed by a reallocation.
+ m_lOtherVisibleFrames.resize(nVisibleStep);
+ m_lOtherHiddenFrames.resize(nHiddenStep);
+ m_lModelFrames.resize(nModelStep);
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/fwkresid.cxx b/framework/source/fwe/classes/fwkresid.cxx
new file mode 100644
index 0000000000..e9a1d639d3
--- /dev/null
+++ b/framework/source/fwe/classes/fwkresid.cxx
@@ -0,0 +1,24 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/fwkresid.hxx>
+
+OUString FwkResId(TranslateId aId) { return Translate::get(aId, Translate::Create("fwk")); }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/rootactiontriggercontainer.cxx b/framework/source/fwe/classes/rootactiontriggercontainer.cxx
new file mode 100644
index 0000000000..1493f08bf4
--- /dev/null
+++ b/framework/source/fwe/classes/rootactiontriggercontainer.cxx
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <classes/rootactiontriggercontainer.hxx>
+#include <classes/actiontriggercontainer.hxx>
+#include <classes/actiontriggerpropertyset.hxx>
+#include <classes/actiontriggerseparatorpropertyset.hxx>
+#include <comphelper/servicehelper.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <cppuhelper/typeprovider.hxx>
+#include <framework/actiontriggerhelper.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::container;
+using namespace com::sun::star::beans;
+
+namespace framework
+{
+
+RootActionTriggerContainer::RootActionTriggerContainer(css::uno::Reference<css::awt::XPopupMenu> xMenu,
+ const OUString* pMenuIdentifier)
+ : m_bContainerCreated(false)
+ , m_xMenu(std::move(xMenu))
+ , m_pMenuIdentifier(pMenuIdentifier)
+{
+}
+
+RootActionTriggerContainer::~RootActionTriggerContainer()
+{
+}
+
+// XInterface
+Any SAL_CALL RootActionTriggerContainer::queryInterface( const Type& aType )
+{
+ Any a = ::cppu::queryInterface(
+ aType ,
+ static_cast< XMultiServiceFactory* >(this),
+ static_cast< XServiceInfo* >(this),
+ static_cast< XTypeProvider* >(this),
+ static_cast< XNamed* >(this));
+
+ if( a.hasValue() )
+ {
+ return a;
+ }
+
+ return PropertySetContainer::queryInterface( aType );
+}
+
+void SAL_CALL RootActionTriggerContainer::acquire() noexcept
+{
+ PropertySetContainer::acquire();
+}
+
+void SAL_CALL RootActionTriggerContainer::release() noexcept
+{
+ PropertySetContainer::release();
+}
+
+// XMultiServiceFactory
+Reference< XInterface > SAL_CALL RootActionTriggerContainer::createInstance( const OUString& aServiceSpecifier )
+{
+ if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGER )
+ return static_cast<OWeakObject *>( new ActionTriggerPropertySet());
+ else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERCONTAINER )
+ return static_cast<OWeakObject *>( new ActionTriggerContainer());
+ else if ( aServiceSpecifier == SERVICENAME_ACTIONTRIGGERSEPARATOR )
+ return static_cast<OWeakObject *>( new ActionTriggerSeparatorPropertySet());
+ else
+ throw css::uno::RuntimeException("Unknown service specifier!", static_cast<OWeakObject *>(this) );
+}
+
+Reference< XInterface > SAL_CALL RootActionTriggerContainer::createInstanceWithArguments( const OUString& ServiceSpecifier, const Sequence< Any >& /*Arguments*/ )
+{
+ return createInstance( ServiceSpecifier );
+}
+
+Sequence< OUString > SAL_CALL RootActionTriggerContainer::getAvailableServiceNames()
+{
+ Sequence< OUString > aSeq{ SERVICENAME_ACTIONTRIGGER,
+ SERVICENAME_ACTIONTRIGGERCONTAINER,
+ SERVICENAME_ACTIONTRIGGERSEPARATOR };
+ return aSeq;
+}
+
+// XIndexContainer
+void SAL_CALL RootActionTriggerContainer::insertByIndex( sal_Int32 Index, const Any& Element )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ FillContainer();
+
+ PropertySetContainer::insertByIndex( Index, Element );
+}
+
+void SAL_CALL RootActionTriggerContainer::removeByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ FillContainer();
+
+ PropertySetContainer::removeByIndex( Index );
+}
+
+// XIndexReplace
+void SAL_CALL RootActionTriggerContainer::replaceByIndex( sal_Int32 Index, const Any& Element )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ FillContainer();
+
+ PropertySetContainer::replaceByIndex( Index, Element );
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL RootActionTriggerContainer::getCount()
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ {
+ if ( m_xMenu )
+ return m_xMenu->getItemCount();
+ else
+ return 0;
+ }
+ else
+ {
+ return PropertySetContainer::getCount();
+ }
+}
+
+Any SAL_CALL RootActionTriggerContainer::getByIndex( sal_Int32 Index )
+{
+ SolarMutexGuard g;
+
+ if ( !m_bContainerCreated )
+ FillContainer();
+
+ return PropertySetContainer::getByIndex( Index );
+}
+
+// XElementAccess
+Type SAL_CALL RootActionTriggerContainer::getElementType()
+{
+ return cppu::UnoType<XPropertySet>::get();
+}
+
+sal_Bool SAL_CALL RootActionTriggerContainer::hasElements()
+{
+ if (m_xMenu)
+ return m_xMenu->getItemCount() > 0;
+ return false;
+}
+
+// XServiceInfo
+OUString SAL_CALL RootActionTriggerContainer::getImplementationName()
+{
+ return IMPLEMENTATIONNAME_ROOTACTIONTRIGGERCONTAINER;
+}
+
+sal_Bool SAL_CALL RootActionTriggerContainer::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL RootActionTriggerContainer::getSupportedServiceNames()
+{
+ return { SERVICENAME_ACTIONTRIGGERCONTAINER };
+}
+
+// XTypeProvider
+Sequence< Type > SAL_CALL RootActionTriggerContainer::getTypes()
+{
+ // Create a static typecollection ...
+ static ::cppu::OTypeCollection ourTypeCollection(
+ cppu::UnoType<XMultiServiceFactory>::get(),
+ cppu::UnoType<XIndexContainer>::get(),
+ cppu::UnoType<XServiceInfo>::get(),
+ cppu::UnoType<XTypeProvider>::get(),
+ cppu::UnoType<XUnoTunnel>::get(),
+ cppu::UnoType<XNamed>::get());
+
+ return ourTypeCollection.getTypes();
+}
+
+Sequence< sal_Int8 > SAL_CALL RootActionTriggerContainer::getImplementationId()
+{
+ return css::uno::Sequence<sal_Int8>();
+}
+
+// private implementation helper
+void RootActionTriggerContainer::FillContainer()
+{
+ m_bContainerCreated = true;
+ ActionTriggerHelper::FillActionTriggerContainerFromMenu(
+ this, m_xMenu);
+}
+OUString RootActionTriggerContainer::getName()
+{
+ OUString sRet;
+ if( m_pMenuIdentifier )
+ sRet = *m_pMenuIdentifier;
+ return sRet;
+}
+
+void RootActionTriggerContainer::setName( const OUString& )
+{
+ throw RuntimeException();
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/classes/sfxhelperfunctions.cxx b/framework/source/fwe/classes/sfxhelperfunctions.cxx
new file mode 100644
index 0000000000..5a1cc0d716
--- /dev/null
+++ b/framework/source/fwe/classes/sfxhelperfunctions.cxx
@@ -0,0 +1,158 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/sfxhelperfunctions.hxx>
+#include <framework/ContextChangeEventMultiplexerTunnel.hxx>
+#include <helper/mischelper.hxx>
+#include <svtools/toolboxcontroller.hxx>
+#include <svtools/statusbarcontroller.hxx>
+
+static pfunc_setToolBoxControllerCreator pToolBoxControllerCreator = nullptr;
+static pfunc_setStatusBarControllerCreator pStatusBarControllerCreator = nullptr;
+static pfunc_getRefreshToolbars pRefreshToolbars = nullptr;
+static pfunc_createDockingWindow pCreateDockingWindow = nullptr;
+static pfunc_isDockingWindowVisible pIsDockingWindowVisible = nullptr;
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+
+namespace framework
+{
+
+pfunc_setToolBoxControllerCreator SetToolBoxControllerCreator( pfunc_setToolBoxControllerCreator pSetToolBoxControllerCreator )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_setToolBoxControllerCreator pOldSetToolBoxControllerCreator = pToolBoxControllerCreator;
+ pToolBoxControllerCreator = pSetToolBoxControllerCreator;
+ return pOldSetToolBoxControllerCreator;
+}
+
+rtl::Reference<svt::ToolboxController> CreateToolBoxController( const Reference< XFrame >& rFrame, ToolBox* pToolbox, ToolBoxItemId nID, const OUString& aCommandURL )
+{
+ pfunc_setToolBoxControllerCreator pFactory = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pFactory = pToolBoxControllerCreator;
+ }
+
+ if ( pFactory )
+ return (*pFactory)( rFrame, pToolbox, nID, aCommandURL );
+ else
+ return nullptr;
+}
+
+pfunc_setStatusBarControllerCreator SetStatusBarControllerCreator( pfunc_setStatusBarControllerCreator pSetStatusBarControllerCreator )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_setStatusBarControllerCreator pOldSetStatusBarControllerCreator = pSetStatusBarControllerCreator;
+ pStatusBarControllerCreator = pSetStatusBarControllerCreator;
+ return pOldSetStatusBarControllerCreator;
+}
+
+rtl::Reference<svt::StatusbarController> CreateStatusBarController( const Reference< XFrame >& rFrame, StatusBar* pStatusBar, unsigned short nID, const OUString& aCommandURL )
+{
+ pfunc_setStatusBarControllerCreator pFactory = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pFactory = pStatusBarControllerCreator;
+ }
+
+ if ( pFactory )
+ return (*pFactory)( rFrame, pStatusBar, nID, aCommandURL );
+ else
+ return nullptr;
+}
+
+pfunc_getRefreshToolbars SetRefreshToolbars( pfunc_getRefreshToolbars pNewRefreshToolbarsFunc )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_getRefreshToolbars pOldFunc = pRefreshToolbars;
+ pRefreshToolbars = pNewRefreshToolbarsFunc;
+
+ return pOldFunc;
+}
+
+void RefreshToolbars( css::uno::Reference< css::frame::XFrame > const & rFrame )
+{
+ pfunc_getRefreshToolbars pCallback = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pCallback = pRefreshToolbars;
+ }
+
+ if ( pCallback )
+ (*pCallback)( rFrame );
+}
+
+pfunc_createDockingWindow SetDockingWindowCreator( pfunc_createDockingWindow pNewCreateDockingWindow )
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_createDockingWindow pOldFunc = pCreateDockingWindow;
+ pCreateDockingWindow = pNewCreateDockingWindow;
+
+ return pOldFunc;
+}
+
+void CreateDockingWindow( const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rResourceURL )
+{
+ pfunc_createDockingWindow pFactory = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pFactory = pCreateDockingWindow;
+ }
+
+ if ( pFactory )
+ (*pFactory)( rFrame, rResourceURL );
+}
+
+pfunc_isDockingWindowVisible SetIsDockingWindowVisible( pfunc_isDockingWindowVisible pNewIsDockingWindowVisible)
+{
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pfunc_isDockingWindowVisible pOldFunc = pIsDockingWindowVisible;
+ pIsDockingWindowVisible = pNewIsDockingWindowVisible;
+
+ return pOldFunc;
+}
+
+bool IsDockingWindowVisible( const css::uno::Reference< css::frame::XFrame >& rFrame, std::u16string_view rResourceURL )
+{
+ pfunc_isDockingWindowVisible pCall = nullptr;
+ {
+ ::osl::MutexGuard aGuard( ::osl::Mutex::getGlobalMutex() );
+ pCall = pIsDockingWindowVisible;
+ }
+
+ if ( pCall )
+ return (*pCall)( rFrame, rResourceURL );
+ else
+ return false;
+}
+
+using namespace ::com::sun::star;
+uno::Reference<ui::XContextChangeEventListener> GetFirstListenerWith(
+ css::uno::Reference<css::uno::XComponentContext> const & xComponentContext,
+ uno::Reference<uno::XInterface> const& xEventFocus,
+ std::function<bool (uno::Reference<ui::XContextChangeEventListener> const&)> const& rPredicate)
+{
+ return GetFirstListenerWith_Impl(xComponentContext, xEventFocus, rPredicate);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/dispatch/interaction.cxx b/framework/source/fwe/dispatch/interaction.cxx
new file mode 100644
index 0000000000..d0cf88e6f6
--- /dev/null
+++ b/framework/source/fwe/dispatch/interaction.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <comphelper/interaction.hxx>
+#include <framework/interaction.hxx>
+#include <com/sun/star/document/XInteractionFilterSelect.hpp>
+#include <com/sun/star/document/NoSuchFilterRequest.hpp>
+#include <utility>
+
+using namespace ::com::sun::star;
+
+namespace framework{
+
+namespace {
+
+/*-************************************************************************************************************
+ @short declaration of special continuation for filter selection
+ @descr Sometimes filter detection during loading document failed. Then we need a possibility
+ to ask user for his decision. These continuation transport selected filter by user to
+ code user of interaction.
+
+ @attention This implementation could be used one times only. We don't support a resettable continuation yet!
+ Why? Normally interaction should show a filter selection dialog and ask user for his decision.
+ He can select any filter - then instances of these class will be called by handler... or user
+ close dialog without any selection. Then another continuation should be selected by handler to
+ abort continuations... Retrying isn't very useful here... I think.
+
+ @implements XInteractionFilterSelect
+
+ @base ImplInheritanceHelper
+ ContinuationBase
+
+ @devstatus ready to use
+ @threadsafe no (used on once position only!)
+*//*-*************************************************************************************************************/
+class ContinuationFilterSelect : public comphelper::OInteraction< css::document::XInteractionFilterSelect >
+{
+ // c++ interface
+ public:
+ ContinuationFilterSelect();
+
+ // uno interface
+ public:
+ virtual void SAL_CALL setFilter( const OUString& sFilter ) override;
+ virtual OUString SAL_CALL getFilter( ) override;
+
+ // member
+ private:
+ OUString m_sFilter;
+
+}; // class ContinuationFilterSelect
+
+}
+
+// initialize continuation with right start values
+
+ContinuationFilterSelect::ContinuationFilterSelect()
+{
+}
+
+// handler should use it after selection to set user specified filter for transport
+
+void SAL_CALL ContinuationFilterSelect::setFilter( const OUString& sFilter )
+{
+ m_sFilter = sFilter;
+}
+
+// read access to transported filter
+
+OUString SAL_CALL ContinuationFilterSelect::getFilter()
+{
+ return m_sFilter;
+}
+
+class RequestFilterSelect_Impl : public ::cppu::WeakImplHelper< css::task::XInteractionRequest >
+{
+public:
+ explicit RequestFilterSelect_Impl(const OUString& rURL);
+ bool isAbort () const;
+ OUString getFilter() const;
+
+public:
+ virtual css::uno::Any SAL_CALL getRequest() override;
+ virtual css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > SAL_CALL getContinuations() override;
+
+private:
+ css::uno::Any m_aRequest;
+ rtl::Reference<comphelper::OInteractionAbort> m_xAbort;
+ rtl::Reference<ContinuationFilterSelect> m_xFilter;
+};
+
+// initialize instance with all necessary information
+// We use it without any further checks on our member then ...!
+
+RequestFilterSelect_Impl::RequestFilterSelect_Impl( const OUString& sURL )
+{
+ css::uno::Reference< css::uno::XInterface > temp2;
+ css::document::NoSuchFilterRequest aFilterRequest( OUString(),
+ temp2 ,
+ sURL );
+ m_aRequest <<= aFilterRequest;
+
+ m_xAbort = new comphelper::OInteractionAbort;
+ m_xFilter = new ContinuationFilterSelect;
+}
+
+// return abort state of interaction
+// If it is true, return value of method "getFilter()" will be unspecified then!
+
+bool RequestFilterSelect_Impl::isAbort() const
+{
+ return m_xAbort->wasSelected();
+}
+
+// return user selected filter
+// Return value valid for non aborted interaction only. Please check "isAbort()" before you call these only!
+
+OUString RequestFilterSelect_Impl::getFilter() const
+{
+ return m_xFilter->getFilter();
+}
+
+// handler call it to get type of request
+// Is hard coded to "please select filter" here. see ctor for further information.
+
+css::uno::Any SAL_CALL RequestFilterSelect_Impl::getRequest()
+{
+ return m_aRequest;
+}
+
+// handler call it to get possible continuations
+// We support "abort/select_filter" only here.
+// After interaction we support read access on these continuations on our c++ interface to
+// return user decision.
+
+css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > SAL_CALL RequestFilterSelect_Impl::getContinuations()
+{
+ return { m_xAbort, m_xFilter };
+}
+
+RequestFilterSelect::RequestFilterSelect( const OUString& sURL )
+ : mxImpl(new RequestFilterSelect_Impl( sURL ))
+{
+}
+
+RequestFilterSelect::~RequestFilterSelect()
+{
+}
+
+// return abort state of interaction
+// If it is true, return value of method "getFilter()" will be unspecified then!
+
+bool RequestFilterSelect::isAbort() const
+{
+ return mxImpl->isAbort();
+}
+
+// return user selected filter
+// Return value valid for non aborted interaction only. Please check "isAbort()" before you call these only!
+
+OUString RequestFilterSelect::getFilter() const
+{
+ return mxImpl->getFilter();
+}
+
+uno::Reference < task::XInteractionRequest > RequestFilterSelect::GetRequest() const
+{
+ return mxImpl;
+}
+
+namespace {
+
+class InteractionRequest_Impl : public ::cppu::WeakImplHelper< css::task::XInteractionRequest >
+{
+ uno::Any m_aRequest;
+ uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > m_lContinuations;
+
+public:
+ InteractionRequest_Impl( css::uno::Any aRequest,
+ const css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > >& lContinuations )
+ : m_aRequest(std::move(aRequest)), m_lContinuations(lContinuations)
+ {
+ }
+
+ virtual uno::Any SAL_CALL getRequest() override;
+ virtual uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL getContinuations() override;
+};
+
+}
+
+uno::Any SAL_CALL InteractionRequest_Impl::getRequest()
+{
+ return m_aRequest;
+}
+
+uno::Sequence< uno::Reference< task::XInteractionContinuation > > SAL_CALL InteractionRequest_Impl::getContinuations()
+{
+ return m_lContinuations;
+}
+
+uno::Reference < task::XInteractionRequest > InteractionRequest::CreateRequest(
+ const uno::Any& aRequest, const uno::Sequence< uno::Reference< task::XInteractionContinuation > >& lContinuations )
+{
+ return new InteractionRequest_Impl( aRequest, lContinuations );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/actiontriggerhelper.cxx b/framework/source/fwe/helper/actiontriggerhelper.cxx
new file mode 100644
index 0000000000..88edd70beb
--- /dev/null
+++ b/framework/source/fwe/helper/actiontriggerhelper.cxx
@@ -0,0 +1,376 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/actiontriggerhelper.hxx>
+#include <classes/actiontriggerseparatorpropertyset.hxx>
+#include <classes/rootactiontriggercontainer.hxx>
+#include <framework/addonsoptions.hxx>
+#include <com/sun/star/awt/XBitmap.hpp>
+#include <com/sun/star/awt/XPopupMenu.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <tools/stream.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
+#include <o3tl/string_view.hxx>
+
+const sal_uInt16 START_ITEMID = 1000;
+
+using namespace com::sun::star::awt;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::container;
+
+namespace framework
+{
+
+// implementation helper ( menu => ActionTrigger )
+
+static bool IsSeparator( const Reference< XPropertySet >& xPropertySet )
+{
+ Reference< XServiceInfo > xServiceInfo( xPropertySet, UNO_QUERY );
+ try
+ {
+ return xServiceInfo->supportsService( SERVICENAME_ACTIONTRIGGERSEPARATOR );
+ }
+ catch (const Exception&)
+ {
+ }
+
+ return false;
+}
+
+static void GetMenuItemAttributes( const Reference< XPropertySet >& xActionTriggerPropertySet,
+ OUString& aMenuLabel,
+ OUString& aCommandURL,
+ OUString& aHelpURL,
+ Reference< XBitmap >& xBitmap,
+ Reference< XIndexContainer >& xSubContainer )
+{
+ Any a;
+
+ try
+ {
+ // mandatory properties
+ a = xActionTriggerPropertySet->getPropertyValue("Text");
+ a >>= aMenuLabel;
+ a = xActionTriggerPropertySet->getPropertyValue("CommandURL");
+ a >>= aCommandURL;
+ a = xActionTriggerPropertySet->getPropertyValue("Image");
+ a >>= xBitmap;
+ a = xActionTriggerPropertySet->getPropertyValue("SubContainer");
+ a >>= xSubContainer;
+ }
+ catch (const Exception&)
+ {
+ }
+
+ // optional properties
+ try
+ {
+ a = xActionTriggerPropertySet->getPropertyValue("HelpURL");
+ a >>= aHelpURL;
+ }
+ catch (const Exception&)
+ {
+ }
+}
+
+static void InsertSubMenuItems(const Reference<XPopupMenu>& rSubMenu, sal_uInt16& nItemId,
+ const Reference<XIndexContainer>& xActionTriggerContainer)
+{
+ if ( !xActionTriggerContainer.is() )
+ return;
+
+ AddonsOptions aAddonOptions;
+ OUString aSlotURL( "slot:" );
+
+ for ( sal_Int32 i = 0; i < xActionTriggerContainer->getCount(); i++ )
+ {
+ try
+ {
+ Reference< XPropertySet > xPropSet;
+ if (( xActionTriggerContainer->getByIndex( i ) >>= xPropSet ) && ( xPropSet.is() ))
+ {
+ if ( IsSeparator( xPropSet ))
+ {
+ // Separator
+ SolarMutexGuard aGuard;
+ rSubMenu->insertSeparator(i);
+ }
+ else
+ {
+ // Menu item
+ OUString aLabel;
+ OUString aCommandURL;
+ OUString aHelpURL;
+ Reference< XBitmap > xBitmap;
+ Reference< XIndexContainer > xSubContainer;
+
+ sal_uInt16 nNewItemId = nItemId++;
+ GetMenuItemAttributes( xPropSet, aLabel, aCommandURL, aHelpURL, xBitmap, xSubContainer );
+
+ SolarMutexGuard aGuard;
+ {
+ // insert new menu item
+ sal_Int32 nIndex = aCommandURL.indexOf( aSlotURL );
+ if ( nIndex >= 0 )
+ {
+ // Special code for our menu implementation: some menu items don't have a
+ // command url but uses the item id as a unique identifier. These entries
+ // got a special url during conversion from menu=>actiontriggercontainer.
+ // Now we have to extract this special url and set the correct item id!!!
+ nNewItemId = static_cast<sal_uInt16>(o3tl::toInt32(aCommandURL.subView( nIndex+aSlotURL.getLength() )));
+ rSubMenu->insertItem(nNewItemId, aLabel, 0, i);
+ }
+ else
+ {
+ rSubMenu->insertItem(nNewItemId, aLabel, 0, i);
+ rSubMenu->setCommand(nNewItemId, aCommandURL);
+ }
+
+ // handle bitmap
+ if ( xBitmap.is() )
+ {
+ bool bImageSet = false;
+
+ Reference<css::graphic::XGraphic> xGraphic(xBitmap, UNO_QUERY);
+ if (xGraphic.is())
+ {
+ // we can take the optimized route if XGraphic is supported
+ rSubMenu->setItemImage(nNewItemId, xGraphic, false);
+ bImageSet = true;
+ }
+
+ if ( !bImageSet )
+ {
+ // This is an unknown implementation of a XBitmap interface. We have to
+ // use a more time consuming way to build an Image!
+ BitmapEx aBitmap;
+
+ Sequence< sal_Int8 > aDIBSeq;
+ {
+ aDIBSeq = xBitmap->getDIB();
+ SvMemoryStream aMem( const_cast<sal_Int8 *>(aDIBSeq.getConstArray()), aDIBSeq.getLength(), StreamMode::READ );
+ ReadDIBBitmapEx(aBitmap, aMem);
+ }
+
+ aDIBSeq = xBitmap->getMaskDIB();
+ if ( aDIBSeq.hasElements() )
+ {
+ Bitmap aMaskBitmap;
+ SvMemoryStream aMem( const_cast<sal_Int8 *>(aDIBSeq.getConstArray()), aDIBSeq.getLength(), StreamMode::READ );
+ ReadDIB(aMaskBitmap, aMem, true);
+ aBitmap = BitmapEx(aBitmap.GetBitmap(), aMaskBitmap);
+ }
+
+ if (!aBitmap.IsEmpty())
+ rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false);
+ }
+ }
+ else
+ {
+ // Support add-on images for context menu interceptors
+ BitmapEx aBitmap(aAddonOptions.GetImageFromURL(aCommandURL, false, true));
+ if (!aBitmap.IsEmpty())
+ rSubMenu->setItemImage(nNewItemId, Graphic(aBitmap).GetXGraphic(), false);
+ }
+
+ if ( xSubContainer.is() )
+ {
+ rtl::Reference xNewSubMenu(new VCLXPopupMenu);
+
+ // Sub menu (recursive call CreateSubMenu )
+ InsertSubMenuItems(xNewSubMenu, nItemId, xSubContainer);
+ rSubMenu->setPopupMenu(nNewItemId, xNewSubMenu);
+ }
+ }
+ }
+ }
+ }
+ catch (const IndexOutOfBoundsException&)
+ {
+ return;
+ }
+ catch (const WrappedTargetException&)
+ {
+ return;
+ }
+ catch (const RuntimeException&)
+ {
+ return;
+ }
+ }
+}
+
+// implementation helper ( ActionTrigger => menu )
+
+/// @throws RuntimeException
+static Reference< XPropertySet > CreateActionTrigger(sal_uInt16 nItemId,
+ const Reference<XPopupMenu>& rMenu,
+ const Reference<XIndexContainer>& rActionTriggerContainer)
+{
+ Reference< XPropertySet > xPropSet;
+
+ Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
+ if ( xMultiServiceFactory.is() )
+ {
+ xPropSet.set( xMultiServiceFactory->createInstance( "com.sun.star.ui.ActionTrigger" ),
+ UNO_QUERY );
+
+ Any a;
+
+ try
+ {
+ // Retrieve the menu attributes and set them in our PropertySet
+ OUString aLabel = rMenu->getItemText(nItemId);
+ a <<= aLabel;
+ xPropSet->setPropertyValue("Text", a );
+
+ OUString aCommandURL = rMenu->getCommand(nItemId);
+
+ if ( aCommandURL.isEmpty() )
+ {
+ aCommandURL = "slot:" + OUString::number( nItemId );
+ }
+
+ a <<= aCommandURL;
+ xPropSet->setPropertyValue("CommandURL", a );
+
+ Reference<XBitmap> xBitmap(rMenu->getItemImage(nItemId), UNO_QUERY);
+ if (xBitmap.is())
+ {
+ a <<= xBitmap;
+ xPropSet->setPropertyValue("Image", a );
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+
+ return xPropSet;
+}
+
+/// @throws RuntimeException
+static Reference< XPropertySet > CreateActionTriggerSeparator( const Reference< XIndexContainer >& rActionTriggerContainer )
+{
+ Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
+ if ( xMultiServiceFactory.is() )
+ {
+ return Reference< XPropertySet >( xMultiServiceFactory->createInstance(
+ "com.sun.star.ui.ActionTriggerSeparator" ),
+ UNO_QUERY );
+ }
+
+ return Reference< XPropertySet >();
+}
+
+/// @throws RuntimeException
+static Reference< XIndexContainer > CreateActionTriggerContainer( const Reference< XIndexContainer >& rActionTriggerContainer )
+{
+ Reference< XMultiServiceFactory > xMultiServiceFactory( rActionTriggerContainer, UNO_QUERY );
+ if ( xMultiServiceFactory.is() )
+ {
+ return Reference< XIndexContainer >( xMultiServiceFactory->createInstance(
+ "com.sun.star.ui.ActionTriggerContainer" ),
+ UNO_QUERY );
+ }
+
+ return Reference< XIndexContainer >();
+}
+
+static void FillActionTriggerContainerWithMenu(const Reference<XPopupMenu>& rMenu,
+ const Reference<XIndexContainer>& rActionTriggerContainer)
+{
+ SolarMutexGuard aGuard;
+
+ for (sal_uInt16 nPos = 0, nCount = rMenu->getItemCount(); nPos < nCount; ++nPos)
+ {
+ sal_uInt16 nItemId = rMenu->getItemId(nPos);
+ css::awt::MenuItemType nType = rMenu->getItemType(nPos);
+
+ try
+ {
+ Any a;
+ Reference< XPropertySet > xPropSet;
+
+ if (nType == css::awt::MenuItemType_SEPARATOR)
+ {
+ xPropSet = CreateActionTriggerSeparator( rActionTriggerContainer );
+
+ a <<= xPropSet;
+ rActionTriggerContainer->insertByIndex( nPos, a );
+ }
+ else
+ {
+ xPropSet = CreateActionTrigger(nItemId, rMenu, rActionTriggerContainer);
+
+ a <<= xPropSet;
+ rActionTriggerContainer->insertByIndex( nPos, a );
+
+ css::uno::Reference<XPopupMenu> xPopupMenu = rMenu->getPopupMenu(nItemId);
+ if (xPopupMenu.is())
+ {
+ // recursive call to build next sub menu
+ Reference< XIndexContainer > xSubContainer = CreateActionTriggerContainer( rActionTriggerContainer );
+
+ a <<= xSubContainer;
+ xPropSet->setPropertyValue("SubContainer", a );
+ FillActionTriggerContainerWithMenu(xPopupMenu, xSubContainer);
+ }
+ }
+ }
+ catch (const Exception&)
+ {
+ }
+ }
+}
+
+void ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
+ const Reference<XPopupMenu>& rNewMenu,
+ const Reference<XIndexContainer>& rActionTriggerContainer)
+{
+ sal_uInt16 nItemId = START_ITEMID;
+
+ if ( rActionTriggerContainer.is() )
+ InsertSubMenuItems(rNewMenu, nItemId, rActionTriggerContainer);
+}
+
+void ActionTriggerHelper::FillActionTriggerContainerFromMenu(
+ Reference< XIndexContainer > const & xActionTriggerContainer,
+ const css::uno::Reference<XPopupMenu>& rMenu)
+{
+ FillActionTriggerContainerWithMenu(rMenu, xActionTriggerContainer);
+}
+
+Reference< XIndexContainer > ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
+ const css::uno::Reference<XPopupMenu>& rMenu,
+ const OUString* pMenuIdentifier )
+{
+ return new RootActionTriggerContainer(rMenu, pMenuIdentifier);
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/configimporter.cxx b/framework/source/fwe/helper/configimporter.cxx
new file mode 100644
index 0000000000..bc92d180b9
--- /dev/null
+++ b/framework/source/fwe/helper/configimporter.cxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/configimporter.hxx>
+#include <toolboxconfiguration.hxx>
+#include <com/sun/star/embed/ElementModes.hpp>
+#include <com/sun/star/ui/XUIConfigurationManager2.hpp>
+
+using namespace ::com::sun::star;
+
+namespace framework
+{
+
+bool UIConfigurationImporterOOo1x::ImportCustomToolbars(
+ const uno::Reference< ui::XUIConfigurationManager2 >& rContainerFactory,
+ std::vector< uno::Reference< container::XIndexContainer > >& rSeqContainer,
+ const uno::Reference< uno::XComponentContext >& rxContext,
+ const uno::Reference< embed::XStorage >& rToolbarStorage )
+{
+ bool bResult ( false );
+ if ( rToolbarStorage.is() && rContainerFactory.is() )
+ {
+ try
+ {
+ for ( sal_uInt16 i = 1; i <= 4; i++ )
+ {
+ OUString aTbxStreamName = "userdeftoolbox" + OUString::number(i) + ".xml";
+ uno::Reference< io::XStream > xStream = rToolbarStorage->openStreamElement( aTbxStreamName, embed::ElementModes::READ );
+ if ( xStream.is() )
+ {
+ uno::Reference< io::XInputStream > xInputStream = xStream->getInputStream();
+ if ( xInputStream.is() )
+ {
+ uno::Reference< container::XIndexContainer > xContainer = rContainerFactory->createSettings();
+ if ( ToolBoxConfiguration::LoadToolBox( rxContext, xInputStream, xContainer ))
+ {
+ rSeqContainer.push_back( xContainer );
+ bResult = true;
+ }
+ }
+ }
+ }
+ }
+ catch ( const uno::RuntimeException& )
+ {
+ throw;
+ }
+ catch ( const uno::Exception& )
+ {
+ }
+ }
+
+ return bResult;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/documentundoguard.cxx b/framework/source/fwe/helper/documentundoguard.cxx
new file mode 100644
index 0000000000..f578d86827
--- /dev/null
+++ b/framework/source/fwe/helper/documentundoguard.cxx
@@ -0,0 +1,195 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/documentundoguard.hxx>
+
+#include <com/sun/star/document/XUndoManagerSupplier.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <rtl/ref.hxx>
+#include <comphelper/diagnose_ex.hxx>
+
+namespace framework
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::document::XUndoManagerSupplier;
+ using ::com::sun::star::document::XUndoManager;
+ using ::com::sun::star::document::XUndoManagerListener;
+ using ::com::sun::star::document::UndoManagerEvent;
+ using ::com::sun::star::lang::EventObject;
+
+ //= UndoManagerContextListener
+
+ typedef ::cppu::WeakImplHelper < XUndoManagerListener
+ > UndoManagerContextListener_Base;
+
+ class UndoManagerContextListener : public UndoManagerContextListener_Base
+ {
+ public:
+ explicit UndoManagerContextListener( const Reference< XUndoManager >& i_undoManager )
+ :m_xUndoManager( i_undoManager )
+ ,m_nRelativeContextDepth( 0 )
+ ,m_documentDisposed( false )
+ {
+ osl_atomic_increment( &m_refCount );
+ {
+ m_xUndoManager->addUndoManagerListener( this );
+ }
+ osl_atomic_decrement( &m_refCount );
+ }
+
+ void finish()
+ {
+ OSL_ENSURE( m_nRelativeContextDepth >= 0, "UndoManagerContextListener: more contexts left than entered?" );
+
+ if ( m_documentDisposed )
+ return;
+
+ // work with a copy of m_nRelativeContextDepth, to be independent from possible bugs in the
+ // listener notifications (where it would be decremented with every leaveUndoContext)
+ sal_Int32 nDepth = m_nRelativeContextDepth;
+ while ( nDepth-- > 0 )
+ {
+ m_xUndoManager->leaveUndoContext();
+ }
+ m_xUndoManager->removeUndoManagerListener( this );
+ }
+
+ // XUndoManagerListener
+ virtual void SAL_CALL undoActionAdded( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL actionUndone( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL actionRedone( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL allActionsCleared( const EventObject& i_event ) override;
+ virtual void SAL_CALL redoActionsCleared( const EventObject& i_event ) override;
+ virtual void SAL_CALL resetAll( const EventObject& i_event ) override;
+ virtual void SAL_CALL enteredContext( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL enteredHiddenContext( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL leftContext( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL leftHiddenContext( const UndoManagerEvent& i_event ) override;
+ virtual void SAL_CALL cancelledContext( const UndoManagerEvent& i_event ) override;
+
+ // XEventListener
+ virtual void SAL_CALL disposing( const EventObject& i_event ) override;
+
+ private:
+ Reference< XUndoManager > const m_xUndoManager;
+ oslInterlockedCount m_nRelativeContextDepth;
+ bool m_documentDisposed;
+ };
+
+ void SAL_CALL UndoManagerContextListener::undoActionAdded( const UndoManagerEvent& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::actionUndone( const UndoManagerEvent& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::actionRedone( const UndoManagerEvent& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::allActionsCleared( const EventObject& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::redoActionsCleared( const EventObject& )
+ {
+ // not interested in
+ }
+
+ void SAL_CALL UndoManagerContextListener::resetAll( const EventObject& )
+ {
+ m_nRelativeContextDepth = 0;
+ }
+
+ void SAL_CALL UndoManagerContextListener::enteredContext( const UndoManagerEvent& )
+ {
+ osl_atomic_increment( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::enteredHiddenContext( const UndoManagerEvent& )
+ {
+ osl_atomic_increment( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::leftContext( const UndoManagerEvent& )
+ {
+ osl_atomic_decrement( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::leftHiddenContext( const UndoManagerEvent& )
+ {
+ osl_atomic_decrement( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::cancelledContext( const UndoManagerEvent& )
+ {
+ osl_atomic_decrement( &m_nRelativeContextDepth );
+ }
+
+ void SAL_CALL UndoManagerContextListener::disposing( const EventObject& )
+ {
+ m_documentDisposed = true;
+ }
+
+ //= DocumentUndoGuard
+
+ DocumentUndoGuard::DocumentUndoGuard( const Reference< XInterface >& i_undoSupplierComponent )
+ {
+ try
+ {
+ Reference< XUndoManagerSupplier > xUndoSupplier( i_undoSupplierComponent, UNO_QUERY );
+ if ( xUndoSupplier.is() )
+ mxUndoManager.set( xUndoSupplier->getUndoManager(), css::uno::UNO_SET_THROW );
+
+ if ( mxUndoManager.is() )
+ mxContextListener.set( new UndoManagerContextListener( mxUndoManager ) );
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ DocumentUndoGuard::~DocumentUndoGuard()
+ {
+ try
+ {
+ if ( mxContextListener.is() )
+ mxContextListener->finish();
+ mxContextListener.clear();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/propertysetcontainer.cxx b/framework/source/fwe/helper/propertysetcontainer.cxx
new file mode 100644
index 0000000000..c766b40db5
--- /dev/null
+++ b/framework/source/fwe/helper/propertysetcontainer.cxx
@@ -0,0 +1,160 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <helper/propertysetcontainer.hxx>
+
+#include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
+#include <cppuhelper/queryinterface.hxx>
+#include <vcl/svapp.hxx>
+
+constexpr OUString WRONG_TYPE_EXCEPTION = u"Only XPropertSet allowed!"_ustr;
+
+using namespace cppu;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::container;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::beans;
+
+namespace framework
+{
+
+PropertySetContainer::PropertySetContainer()
+{
+}
+
+PropertySetContainer::~PropertySetContainer()
+{
+}
+
+// XInterface
+void SAL_CALL PropertySetContainer::acquire() noexcept
+{
+ OWeakObject::acquire();
+}
+
+void SAL_CALL PropertySetContainer::release() noexcept
+{
+ OWeakObject::release();
+}
+
+Any SAL_CALL PropertySetContainer::queryInterface( const Type& rType )
+{
+ Any a = ::cppu::queryInterface(
+ rType ,
+ static_cast< XIndexContainer* >(this),
+ static_cast< XIndexReplace* >(this),
+ static_cast< XIndexAccess* >(this),
+ static_cast< XElementAccess* >(this) );
+
+ if( a.hasValue() )
+ {
+ return a;
+ }
+
+ return OWeakObject::queryInterface( rType );
+}
+
+// XIndexContainer
+void SAL_CALL PropertySetContainer::insertByIndex( sal_Int32 Index, const css::uno::Any& Element )
+{
+ std::unique_lock g(m_aMutex);
+
+ sal_Int32 nSize = m_aPropertySetVector.size();
+
+ if ( nSize < Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ Reference< XPropertySet > aPropertySetElement;
+
+ if ( !(Element >>= aPropertySetElement) )
+ {
+ throw IllegalArgumentException(
+ WRONG_TYPE_EXCEPTION,
+ static_cast<OWeakObject *>(this), 2 );
+ }
+
+ if ( nSize == Index )
+ m_aPropertySetVector.push_back( aPropertySetElement );
+ else
+ {
+ PropertySetVector::iterator aIter = m_aPropertySetVector.begin();
+ aIter += Index;
+ m_aPropertySetVector.insert( aIter, aPropertySetElement );
+ }
+}
+
+void SAL_CALL PropertySetContainer::removeByIndex( sal_Int32 nIndex )
+{
+ std::unique_lock g(m_aMutex);
+
+ if ( static_cast<sal_Int32>(m_aPropertySetVector.size()) <= nIndex )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ m_aPropertySetVector.erase(m_aPropertySetVector.begin() + nIndex);
+}
+
+// XIndexReplace
+void SAL_CALL PropertySetContainer::replaceByIndex( sal_Int32 Index, const css::uno::Any& Element )
+{
+ std::unique_lock g(m_aMutex);
+
+ if ( static_cast<sal_Int32>(m_aPropertySetVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ Reference< XPropertySet > aPropertySetElement;
+
+ if ( !(Element >>= aPropertySetElement) )
+ {
+ throw IllegalArgumentException(
+ WRONG_TYPE_EXCEPTION,
+ static_cast<OWeakObject *>(this), 2 );
+ }
+
+ m_aPropertySetVector[ Index ] = aPropertySetElement;
+}
+
+// XIndexAccess
+sal_Int32 SAL_CALL PropertySetContainer::getCount()
+{
+ std::unique_lock g(m_aMutex);
+
+ return m_aPropertySetVector.size();
+}
+
+Any SAL_CALL PropertySetContainer::getByIndex( sal_Int32 Index )
+{
+ std::unique_lock g(m_aMutex);
+
+ if ( static_cast<sal_Int32>(m_aPropertySetVector.size()) <= Index )
+ throw IndexOutOfBoundsException( OUString(), static_cast<OWeakObject *>(this) );
+
+ return Any(m_aPropertySetVector[ Index ]);
+}
+
+// XElementAccess
+sal_Bool SAL_CALL PropertySetContainer::hasElements()
+{
+ std::unique_lock g(m_aMutex);
+
+ return !( m_aPropertySetVector.empty() );
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/titlehelper.cxx b/framework/source/fwe/helper/titlehelper.cxx
new file mode 100644
index 0000000000..5ab03ab10e
--- /dev/null
+++ b/framework/source/fwe/helper/titlehelper.cxx
@@ -0,0 +1,684 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/titlehelper.hxx>
+#include <classes/fwkresid.hxx>
+#include <strings.hrc>
+#include <properties.h>
+
+#include <com/sun/star/frame/UntitledNumbersConst.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/frame/ModuleManager.hpp>
+#include <com/sun/star/frame/XUntitledNumbers.hpp>
+#include <com/sun/star/frame/XModel3.hpp>
+#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
+
+#include <unotools/configmgr.hxx>
+#include <unotools/bootstrap.hxx>
+#include <unotools/mediadescriptor.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <osl/mutex.hxx>
+#include <tools/urlobj.hxx>
+#include <utility>
+#include <vcl/svapp.hxx>
+
+
+using namespace css;
+using namespace css::uno;
+using namespace css::frame;
+
+namespace framework{
+
+TitleHelper::TitleHelper(css::uno::Reference< css::uno::XComponentContext > xContext,
+ const css::uno::Reference< css::uno::XInterface >& xOwner,
+ const css::uno::Reference< css::frame::XUntitledNumbers >& xNumbers)
+ : ::cppu::BaseMutex ()
+ , m_xContext (std::move(xContext))
+ , m_xOwner (xOwner)
+ , m_xUntitledNumbers(xNumbers)
+ , m_bExternalTitle (false)
+ , m_nLeasedNumber (css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ , m_aListener (m_aMutex)
+{
+ if (css::uno::Reference<css::frame::XModel> xModel{ xOwner, css::uno::UNO_QUERY })
+ {
+ impl_startListeningForModel (xModel);
+ }
+ else if (css::uno::Reference<css::frame::XController> xController{ xOwner,
+ css::uno::UNO_QUERY })
+ {
+ impl_startListeningForController (xController);
+ }
+ else if (css::uno::Reference<css::frame::XFrame> xFrame{ xOwner, css::uno::UNO_QUERY })
+ {
+ impl_startListeningForFrame (xFrame);
+ }
+}
+
+TitleHelper::~TitleHelper()
+{
+}
+
+OUString SAL_CALL TitleHelper::getTitle()
+{
+ // SYNCHRONIZED ->
+ osl::MutexGuard aLock(m_aMutex);
+
+ // An external title will win always and disable all internal logic about
+ // creating/using a title value.
+ // Even an empty string will be accepted as valid title !
+ if (m_bExternalTitle)
+ return m_sTitle;
+
+ // Title seems to be up-to-date. Return it directly.
+ if (!m_sTitle.isEmpty())
+ return m_sTitle;
+
+ // Title seems to be unused till now ... do bootstrapping
+ impl_updateTitle (true);
+
+ return m_sTitle;
+ // <- SYNCHRONIZED
+}
+
+void SAL_CALL TitleHelper::setTitle(const OUString& sTitle)
+{
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ m_bExternalTitle = true;
+ m_sTitle = sTitle;
+ }
+ // <- SYNCHRONIZED
+
+ impl_sendTitleChangedEvent ();
+}
+
+void SAL_CALL TitleHelper::addTitleChangeListener(const css::uno::Reference< css::frame::XTitleChangeListener >& xListener)
+{
+ // container is threadsafe by himself
+ m_aListener.addInterface( cppu::UnoType<css::frame::XTitleChangeListener>::get(), xListener );
+}
+
+void SAL_CALL TitleHelper::removeTitleChangeListener(const css::uno::Reference< css::frame::XTitleChangeListener >& xListener)
+{
+ // container is threadsafe by himself
+ m_aListener.removeInterface( cppu::UnoType<css::frame::XTitleChangeListener>::get(), xListener );
+}
+
+void SAL_CALL TitleHelper::titleChanged(const css::frame::TitleChangedEvent& aEvent)
+{
+ css::uno::Reference< css::frame::XTitle > xSubTitle;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xSubTitle = m_xSubTitle;
+ }
+ // <- SYNCHRONIZED
+
+ if (aEvent.Source != xSubTitle)
+ return;
+
+ impl_updateTitle ();
+}
+
+void SAL_CALL TitleHelper::documentEventOccured(const css::document::DocumentEvent& aEvent)
+{
+ if ( ! aEvent.EventName.equalsIgnoreAsciiCase("OnSaveAsDone")
+ && ! aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged")
+ && ! aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged"))
+ return;
+
+ css::uno::Reference< css::frame::XModel > xOwner;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner.set(m_xOwner, css::uno::UNO_QUERY);
+ }
+ // <- SYNCHRONIZED
+
+ if (aEvent.Source != xOwner
+ || ((aEvent.EventName.equalsIgnoreAsciiCase("OnModeChanged")
+ || aEvent.EventName.equalsIgnoreAsciiCase("OnTitleChanged"))
+ && !xOwner.is()))
+ {
+ return;
+ }
+
+ impl_updateTitle ();
+}
+
+void SAL_CALL TitleHelper::frameAction(const css::frame::FrameActionEvent& aEvent)
+{
+ css::uno::Reference< css::frame::XFrame > xOwner;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner.set(m_xOwner, css::uno::UNO_QUERY);
+ }
+ // <- SYNCHRONIZED
+
+ if (aEvent.Source != xOwner)
+ return;
+
+ // we are interested on events only, which must trigger a title bar update
+ // because component was changed.
+ if (
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_ATTACHED ) ||
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_REATTACHED) ||
+ (aEvent.Action == css::frame::FrameAction_COMPONENT_DETACHING )
+ )
+ {
+ impl_updateListeningForFrame (xOwner);
+ impl_updateTitle ();
+ }
+}
+
+void SAL_CALL TitleHelper::disposing(const css::lang::EventObject& aEvent)
+{
+ css::uno::Reference< css::uno::XInterface > xOwner;
+ css::uno::Reference< css::frame::XUntitledNumbers > xNumbers;
+ ::sal_Int32 nLeasedNumber;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner = m_xOwner;
+ xNumbers = m_xUntitledNumbers;
+ nLeasedNumber = m_nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if ( ! xOwner.is ())
+ return;
+
+ css::uno::Reference< css::frame::XFrame > xFrame(xOwner, css::uno::UNO_QUERY);
+ if (xFrame.is())
+ xFrame->removeFrameActionListener(this);
+
+ if (xOwner != aEvent.Source)
+ return;
+
+ if (
+ (xNumbers.is () ) &&
+ (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ )
+ xNumbers->releaseNumber (nLeasedNumber);
+
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ m_xOwner.clear();
+ m_sTitle.clear();
+ m_nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER;
+ }
+ // <- SYNCHRONIZED
+}
+
+void TitleHelper::impl_sendTitleChangedEvent ()
+{
+ css::uno::Reference<css::uno::XInterface> xOwner;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner = m_xOwner;
+ }
+ // <- SYNCHRONIZED
+
+ css::frame::TitleChangedEvent aEvent(xOwner, m_sTitle);
+
+ if( ! aEvent.Source.is() )
+ return;
+
+ comphelper::OInterfaceContainerHelper2* pContainer = m_aListener.getContainer( cppu::UnoType<css::frame::XTitleChangeListener>::get());
+ if ( ! pContainer)
+ return;
+
+ comphelper::OInterfaceIteratorHelper2 pIt( *pContainer );
+ while ( pIt.hasMoreElements() )
+ {
+ try
+ {
+ static_cast<css::frame::XTitleChangeListener*>(pIt.next())->titleChanged( aEvent );
+ }
+ catch(const css::uno::Exception&)
+ {
+ pIt.remove();
+ }
+ }
+}
+
+void TitleHelper::impl_updateTitle (bool init)
+{
+ css::uno::Reference<css::uno::XInterface> xOwner;
+
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner = m_xOwner;
+ }
+ // <- SYNCHRONIZED
+
+ if (css::uno::Reference<css::frame::XModel3> xModel{ xOwner, css::uno::UNO_QUERY })
+ {
+ impl_updateTitleForModel (xModel, init);
+ }
+ else if (css::uno::Reference<css::frame::XController> xController{ xOwner,
+ css::uno::UNO_QUERY })
+ {
+ impl_updateTitleForController (xController, init);
+ }
+ else if (css::uno::Reference<css::frame::XFrame> xFrame{ xOwner, css::uno::UNO_QUERY })
+ {
+ impl_updateTitleForFrame (xFrame, init);
+ }
+}
+
+static OUString getURLFromModel(const css::uno::Reference< css::frame::XModel3 >& xModel)
+{
+ if (css::uno::Reference<css::frame::XStorable> xURLProvider{ xModel, css::uno::UNO_QUERY })
+ return xURLProvider->getLocation();
+ return {};
+}
+
+void TitleHelper::impl_updateTitleForModel (const css::uno::Reference< css::frame::XModel3 >& xModel, bool init)
+{
+ css::uno::Reference< css::uno::XInterface > xOwner;
+ css::uno::Reference< css::frame::XUntitledNumbers > xNumbers;
+ ::sal_Int32 nLeasedNumber;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // external title won't be updated internally!
+ // It has to be set from outside new.
+ if (m_bExternalTitle)
+ return;
+
+ xOwner = m_xOwner;
+ xNumbers = m_xUntitledNumbers;
+ nLeasedNumber = m_nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if (
+ ( ! xOwner.is ()) ||
+ ( ! xNumbers.is ()) ||
+ ( ! xModel.is ())
+ )
+ return;
+
+ OUString sTitle;
+
+ utl::MediaDescriptor aDescriptor(
+ xModel->getArgs2({ utl::MediaDescriptor::PROP_DOCUMENTTITLE,
+ utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME }));
+
+ if (const OUString sMediaTitle = aDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_DOCUMENTTITLE, OUString());
+ !sMediaTitle.isEmpty())
+ {
+ sTitle = sMediaTitle;
+ }
+ else if (const OUString sURL = getURLFromModel(xModel); !sURL.isEmpty())
+ {
+ sTitle = impl_convertURL2Title(sURL);
+ if (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ xNumbers->releaseNumber (nLeasedNumber);
+ nLeasedNumber = css::frame::UntitledNumbersConst::INVALID_NUMBER;
+ }
+ else if (const OUString sSuggestedSaveAsName = aDescriptor.getUnpackedValueOrDefault(
+ utl::MediaDescriptor::PROP_SUGGESTEDSAVEASNAME, OUString());
+ !sSuggestedSaveAsName.isEmpty())
+ {
+ // tdf#121537 Use suggested save as name for title if file has not yet been saved
+ sTitle = sSuggestedSaveAsName;
+ }
+ else
+ {
+ if (nLeasedNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ nLeasedNumber = xNumbers->leaseNumber (xOwner);
+
+ if (nLeasedNumber != css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ sTitle = xNumbers->getUntitledPrefix() + OUString::number(nLeasedNumber);
+ else
+ sTitle = xNumbers->getUntitledPrefix() + "?";
+ }
+
+ bool bChanged;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // WORKAROUND: the notification is currently sent always,
+ // can be changed after shared mode is supported per UNO API
+ bChanged = !init; // && m_sTitle != sTitle
+
+ m_sTitle = sTitle;
+ m_nLeasedNumber = nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if (bChanged)
+ impl_sendTitleChangedEvent ();
+}
+
+void TitleHelper::impl_updateTitleForController (const css::uno::Reference< css::frame::XController >& xController, bool init)
+{
+ css::uno::Reference< css::uno::XInterface > xOwner;
+ css::uno::Reference< css::frame::XUntitledNumbers > xNumbers;
+ ::sal_Int32 nLeasedNumber;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // external title won't be updated internally!
+ // It has to be set from outside new.
+ if (m_bExternalTitle)
+ return;
+
+ xOwner = m_xOwner;
+ xNumbers = m_xUntitledNumbers;
+ nLeasedNumber = m_nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if (
+ ( ! xOwner.is ()) ||
+ ( ! xNumbers.is ()) ||
+ ( ! xController.is ())
+ )
+ return;
+
+ OUStringBuffer sTitle(256);
+
+ if (nLeasedNumber == css::frame::UntitledNumbersConst::INVALID_NUMBER)
+ nLeasedNumber = xNumbers->leaseNumber (xOwner);
+
+ css::uno::Reference< css::frame::XTitle > xModelTitle(xController->getModel (), css::uno::UNO_QUERY);
+ css::uno::Reference< css::frame::XModel > xModel = xController->getModel ();
+ if (!xModelTitle.is ())
+ xModelTitle.set(xController, css::uno::UNO_QUERY);
+ if (xModelTitle.is ())
+ {
+ sTitle.append (xModelTitle->getTitle ());
+ if ( nLeasedNumber > 1 )
+ {
+ sTitle.append(" : " + OUString::number(nLeasedNumber));
+ }
+ if (xModel.is ())
+ {
+ INetURLObject aURL (xModel->getURL ());
+ if (aURL.GetProtocol () != INetProtocol::File
+ && aURL.GetProtocol () != INetProtocol::NotValid)
+ {
+ OUString sRemoteText (FwkResId (STR_REMOTE_TITLE));
+ sTitle.append (sRemoteText);
+ }
+ }
+ }
+ else
+ {
+ sTitle.append (xNumbers->getUntitledPrefix ());
+ if ( nLeasedNumber > 1 )
+ {
+ sTitle.append(nLeasedNumber );
+ }
+ }
+
+ bool bChanged;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ OUString sNewTitle = sTitle.makeStringAndClear ();
+ bChanged = !init && m_sTitle != sNewTitle;
+ m_sTitle = sNewTitle;
+ m_nLeasedNumber = nLeasedNumber;
+ }
+ // <- SYNCHRONIZED
+
+ if (bChanged)
+ impl_sendTitleChangedEvent ();
+}
+
+void TitleHelper::impl_updateTitleForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame, bool init)
+{
+ if ( ! xFrame.is ())
+ return;
+
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // external title won't be updated internally!
+ // It has to be set from outside new.
+ if (m_bExternalTitle)
+ return;
+ }
+ // <- SYNCHRONIZED
+
+ css::uno::Reference< css::uno::XInterface > xComponent = xFrame->getController ();
+ if ( ! xComponent.is ())
+ xComponent = xFrame->getComponentWindow ();
+
+ OUStringBuffer sTitle (256);
+
+ impl_appendComponentTitle (sTitle, xComponent);
+#ifndef MACOSX
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ // fdo#70376: We want the window title to contain just the
+ // document name (from the above "component title").
+ impl_appendProductName (sTitle);
+ impl_appendModuleName (sTitle);
+ impl_appendDebugVersion (sTitle);
+ }
+#endif
+ impl_appendSafeMode (sTitle);
+
+ bool bChanged;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ OUString sNewTitle = sTitle.makeStringAndClear ();
+ bChanged = !init && m_sTitle != sNewTitle;
+ m_sTitle = sNewTitle;
+ }
+ // <- SYNCHRONIZED
+
+ if (bChanged)
+ impl_sendTitleChangedEvent ();
+}
+
+void TitleHelper::impl_appendComponentTitle ( OUStringBuffer& sTitle ,
+ const css::uno::Reference< css::uno::XInterface >& xComponent)
+{
+ css::uno::Reference< css::frame::XTitle > xTitle(xComponent, css::uno::UNO_QUERY);
+
+ // Note: Title has to be used (even if it's empty) if the right interface is supported.
+ if (xTitle.is ())
+ sTitle.append (xTitle->getTitle ());
+}
+
+void TitleHelper::impl_appendProductName (OUStringBuffer& sTitle)
+{
+ OUString name(utl::ConfigManager::getProductName());
+ if (!name.isEmpty())
+ {
+ if (!sTitle.isEmpty())
+ {
+ OUString separator (FwkResId (STR_EMDASH_SEPARATOR));
+ sTitle.append(separator);
+ }
+ sTitle.append(name);
+ }
+}
+
+void TitleHelper::impl_appendModuleName (OUStringBuffer& sTitle)
+{
+ css::uno::Reference< css::uno::XInterface > xOwner;
+ css::uno::Reference< css::uno::XComponentContext > xContext;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ xOwner = m_xOwner;
+ xContext = m_xContext;
+ }
+ // <- SYNCHRONIZED
+
+ try
+ {
+ css::uno::Reference< css::frame::XModuleManager2 > xModuleManager =
+ css::frame::ModuleManager::create(xContext);
+
+ const OUString sID = xModuleManager->identify(xOwner);
+ ::comphelper::SequenceAsHashMap lProps = xModuleManager->getByName (sID);
+ const OUString sUIName = lProps.getUnpackedValueOrDefault (OFFICEFACTORY_PROPNAME_ASCII_UINAME, OUString());
+
+ // An UIname property is an optional value !
+ // So please add it to the title in case it does really exists only.
+ if (!sUIName.isEmpty())
+ {
+ sTitle.append (" " );
+ sTitle.append (sUIName);
+ }
+ }
+ catch(const css::uno::Exception&)
+ {}
+}
+
+#ifdef DBG_UTIL
+void TitleHelper::impl_appendDebugVersion (OUStringBuffer& sTitle)
+{
+ OUString version(utl::ConfigManager::getProductVersion());
+ sTitle.append(' ');
+ sTitle.append(version);
+ OUString sVersion = ::utl::Bootstrap::getBuildIdData("development");
+ sTitle.append(" [");
+ sTitle.append(sVersion);
+ sTitle.append("]");
+}
+#else
+void TitleHelper::impl_appendDebugVersion (OUStringBuffer&)
+{
+}
+#endif
+
+void TitleHelper::impl_appendSafeMode (OUStringBuffer& sTitle)
+{
+ if (Application::IsSafeModeEnabled())
+ sTitle.append(FwkResId (STR_SAFEMODE_TITLE));
+}
+
+void TitleHelper::impl_startListeningForModel (const css::uno::Reference< css::frame::XModel >& xModel)
+{
+ css::uno::Reference< css::document::XDocumentEventBroadcaster > xBroadcaster(xModel, css::uno::UNO_QUERY);
+ if ( ! xBroadcaster.is ())
+ return;
+
+ xBroadcaster->addDocumentEventListener (static_cast< css::document::XDocumentEventListener* >(this));
+}
+
+void TitleHelper::impl_startListeningForController (const css::uno::Reference< css::frame::XController >& xController)
+{
+ xController->addEventListener (static_cast< css::lang::XEventListener* > (static_cast< css::frame::XFrameActionListener* > (this) ) );
+ css::uno::Reference< css::frame::XTitle > xSubTitle(xController->getModel (), css::uno::UNO_QUERY);
+ impl_setSubTitle (xSubTitle);
+}
+
+void TitleHelper::impl_startListeningForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ xFrame->addFrameActionListener(this );
+ impl_updateListeningForFrame (xFrame);
+}
+
+void TitleHelper::impl_updateListeningForFrame (const css::uno::Reference< css::frame::XFrame >& xFrame)
+{
+ css::uno::Reference< css::frame::XTitle > xSubTitle(xFrame->getController (), css::uno::UNO_QUERY);
+ impl_setSubTitle (xSubTitle);
+}
+
+void TitleHelper::impl_setSubTitle (const css::uno::Reference< css::frame::XTitle >& xSubTitle)
+{
+ css::uno::Reference< css::frame::XTitle > xOldSubTitle;
+ // SYNCHRONIZED ->
+ {
+ osl::MutexGuard aLock(m_aMutex);
+
+ // ignore duplicate calls. Makes outside using of this helper more easy :-)
+ xOldSubTitle = m_xSubTitle;
+ if (xOldSubTitle == xSubTitle)
+ return;
+
+ m_xSubTitle = xSubTitle;
+ }
+ // <- SYNCHRONIZED
+
+ css::uno::Reference< css::frame::XTitleChangeBroadcaster > xOldBroadcaster(xOldSubTitle , css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XTitleChangeBroadcaster > xNewBroadcaster(xSubTitle , css::uno::UNO_QUERY );
+ css::uno::Reference< css::frame::XTitleChangeListener > xThis(this);
+
+ if (xOldBroadcaster.is())
+ xOldBroadcaster->removeTitleChangeListener (xThis);
+
+ if (xNewBroadcaster.is())
+ xNewBroadcaster->addTitleChangeListener (xThis);
+}
+
+OUString TitleHelper::impl_convertURL2Title(std::u16string_view sURL)
+{
+ INetURLObject aURL (sURL);
+ OUString sTitle;
+
+ if (aURL.GetProtocol() == INetProtocol::File)
+ {
+ if (aURL.HasMark())
+ aURL = INetURLObject(aURL.GetURLNoMark());
+
+ sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
+ }
+ else
+ {
+ if (aURL.hasExtension())
+ sTitle = aURL.getName(INetURLObject::LAST_SEGMENT, true, INetURLObject::DecodeMechanism::WithCharset);
+
+ if ( sTitle.isEmpty() )
+ sTitle = aURL.GetHostPort(INetURLObject::DecodeMechanism::WithCharset);
+
+ if ( sTitle.isEmpty() )
+ sTitle = aURL.GetURLNoPass(INetURLObject::DecodeMechanism::WithCharset);
+ }
+
+ return sTitle;
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/helper/undomanagerhelper.cxx b/framework/source/fwe/helper/undomanagerhelper.cxx
new file mode 100644
index 0000000000..3a2fdd6c06
--- /dev/null
+++ b/framework/source/fwe/helper/undomanagerhelper.cxx
@@ -0,0 +1,1094 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <framework/undomanagerhelper.hxx>
+#include <framework/imutex.hxx>
+
+#include <com/sun/star/document/EmptyUndoStackException.hpp>
+#include <com/sun/star/document/UndoContextNotClosedException.hpp>
+#include <com/sun/star/document/UndoFailedException.hpp>
+#include <com/sun/star/document/XUndoManager.hpp>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/util/InvalidStateException.hpp>
+#include <com/sun/star/util/NotLockedException.hpp>
+#include <com/sun/star/util/XModifyListener.hpp>
+
+#include <comphelper/interfacecontainer4.hxx>
+#include <cppuhelper/exc_hlp.hxx>
+#include <comphelper/flagguard.hxx>
+#include <comphelper/asyncnotification.hxx>
+#include <svl/undo.hxx>
+#include <comphelper/diagnose_ex.hxx>
+#include <osl/conditn.hxx>
+#include <vcl/svapp.hxx>
+
+#include <functional>
+#include <mutex>
+#include <stack>
+#include <queue>
+#include <utility>
+
+namespace framework
+{
+
+ using ::com::sun::star::uno::Reference;
+ using ::com::sun::star::uno::XInterface;
+ using ::com::sun::star::uno::UNO_QUERY;
+ using ::com::sun::star::uno::Exception;
+ using ::com::sun::star::uno::RuntimeException;
+ using ::com::sun::star::uno::Any;
+ using ::com::sun::star::uno::Sequence;
+ using ::com::sun::star::document::XUndoManagerListener;
+ using ::com::sun::star::document::UndoManagerEvent;
+ using ::com::sun::star::document::EmptyUndoStackException;
+ using ::com::sun::star::document::UndoContextNotClosedException;
+ using ::com::sun::star::document::UndoFailedException;
+ using ::com::sun::star::util::NotLockedException;
+ using ::com::sun::star::lang::EventObject;
+ using ::com::sun::star::document::XUndoAction;
+ using ::com::sun::star::lang::XComponent;
+ using ::com::sun::star::document::XUndoManager;
+ using ::com::sun::star::util::InvalidStateException;
+ using ::com::sun::star::lang::IllegalArgumentException;
+ using ::com::sun::star::util::XModifyListener;
+
+ //= UndoActionWrapper
+
+ namespace {
+
+ class UndoActionWrapper : public SfxUndoAction
+ {
+ public:
+ explicit UndoActionWrapper(
+ Reference< XUndoAction > const& i_undoAction
+ );
+ virtual ~UndoActionWrapper() override;
+
+ virtual OUString GetComment() const override;
+ virtual void Undo() override;
+ virtual void Redo() override;
+ virtual bool CanRepeat(SfxRepeatTarget&) const override;
+
+ private:
+ const Reference< XUndoAction > m_xUndoAction;
+ };
+
+ }
+
+ UndoActionWrapper::UndoActionWrapper( Reference< XUndoAction > const& i_undoAction )
+ : m_xUndoAction( i_undoAction )
+ {
+ ENSURE_OR_THROW( m_xUndoAction.is(), "illegal undo action" );
+ }
+
+ UndoActionWrapper::~UndoActionWrapper()
+ {
+ try
+ {
+ Reference< XComponent > xComponent( m_xUndoAction, UNO_QUERY );
+ if ( xComponent.is() )
+ xComponent->dispose();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ }
+
+ OUString UndoActionWrapper::GetComment() const
+ {
+ OUString sComment;
+ try
+ {
+ sComment = m_xUndoAction->getTitle();
+ }
+ catch( const Exception& )
+ {
+ DBG_UNHANDLED_EXCEPTION("fwk");
+ }
+ return sComment;
+ }
+
+ void UndoActionWrapper::Undo()
+ {
+ m_xUndoAction->undo();
+ }
+
+ void UndoActionWrapper::Redo()
+ {
+ m_xUndoAction->redo();
+ }
+
+ bool UndoActionWrapper::CanRepeat(SfxRepeatTarget&) const
+ {
+ return false;
+ }
+
+ //= UndoManagerRequest
+
+ namespace {
+
+ class UndoManagerRequest : public ::comphelper::AnyEvent
+ {
+ public:
+ explicit UndoManagerRequest( ::std::function<void ()> i_request )
+ :m_request(std::move( i_request ))
+ {
+ m_finishCondition.reset();
+ }
+
+ void execute()
+ {
+ try
+ {
+ m_request();
+ }
+ catch( const Exception& )
+ {
+ m_caughtException = ::cppu::getCaughtException();
+ }
+ m_finishCondition.set();
+ }
+
+ void wait()
+ {
+ m_finishCondition.wait();
+ if ( m_caughtException.hasValue() )
+ ::cppu::throwException( m_caughtException );
+ }
+
+ void cancel( const Reference< XInterface >& i_context )
+ {
+ m_caughtException <<= RuntimeException(
+ "Concurrency error: an earlier operation on the stack failed.",
+ i_context
+ );
+ m_finishCondition.set();
+ }
+
+ protected:
+ virtual ~UndoManagerRequest() override
+ {
+ }
+
+ private:
+ ::std::function<void ()> m_request;
+ Any m_caughtException;
+ ::osl::Condition m_finishCondition;
+ };
+
+ }
+
+ //= UndoManagerHelper_Impl
+
+ class UndoManagerHelper_Impl : public SfxUndoListener
+ {
+ private:
+ ::osl::Mutex m_aMutex;
+ /// Use different mutex for listeners to prevent ABBA deadlocks
+ std::mutex m_aListenerMutex;
+ std::mutex m_aQueueMutex;
+ bool m_bAPIActionRunning;
+ bool m_bProcessingEvents;
+ sal_Int32 m_nLockCount;
+ ::comphelper::OInterfaceContainerHelper4<XUndoManagerListener> m_aUndoListeners;
+ ::comphelper::OInterfaceContainerHelper4<XModifyListener> m_aModifyListeners;
+ IUndoManagerImplementation& m_rUndoManagerImplementation;
+ ::std::stack< bool > m_aContextVisibilities;
+#if OSL_DEBUG_LEVEL > 0
+ bool m_bContextAPIFlagsEverPushed = {false};
+ ::std::stack< bool > m_aContextAPIFlags;
+#endif
+ ::std::queue< ::rtl::Reference< UndoManagerRequest > >
+ m_aEventQueue;
+
+ public:
+ ::osl::Mutex& getMutex() { return m_aMutex; }
+
+ public:
+ explicit UndoManagerHelper_Impl( IUndoManagerImplementation& i_undoManagerImpl )
+ :m_bAPIActionRunning( false )
+ ,m_bProcessingEvents( false )
+ ,m_nLockCount( 0 )
+ ,m_rUndoManagerImplementation( i_undoManagerImpl )
+ {
+ getUndoManager().AddUndoListener( *this );
+ }
+
+ virtual ~UndoManagerHelper_Impl()
+ {
+ }
+
+ SfxUndoManager& getUndoManager() const
+ {
+ return m_rUndoManagerImplementation.getImplUndoManager();
+ }
+
+ Reference< XUndoManager > getXUndoManager() const
+ {
+ return m_rUndoManagerImplementation.getThis();
+ }
+
+ // SfxUndoListener
+ virtual void actionUndone( const OUString& i_actionComment ) override;
+ virtual void actionRedone( const OUString& i_actionComment ) override;
+ virtual void undoActionAdded( const OUString& i_actionComment ) override;
+ virtual void cleared() override;
+ virtual void clearedRedo() override;
+ virtual void resetAll() override;
+ virtual void listActionEntered( const OUString& i_comment ) override;
+ virtual void listActionLeft( const OUString& i_comment ) override;
+ virtual void listActionCancelled() override;
+
+ // public operations
+ void disposing();
+
+ void enterUndoContext( const OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock );
+ void leaveUndoContext( IMutexGuard& i_instanceLock );
+ void addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock );
+ void undo( IMutexGuard& i_instanceLock );
+ void redo( IMutexGuard& i_instanceLock );
+ void clear( IMutexGuard& i_instanceLock );
+ void clearRedo( IMutexGuard& i_instanceLock );
+ void reset( IMutexGuard& i_instanceLock );
+
+ void lock();
+ void unlock();
+
+ void addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.addInterface( g, i_listener );
+ }
+
+ void removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.removeInterface( g, i_listener );
+ }
+
+ void addModifyListener( const Reference< XModifyListener >& i_listener )
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aModifyListeners.addInterface( g, i_listener );
+ }
+
+ void removeModifyListener( const Reference< XModifyListener >& i_listener )
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aModifyListeners.removeInterface( g, i_listener );
+ }
+
+ UndoManagerEvent
+ buildEvent( OUString const& i_title ) const;
+
+ void impl_notifyModified();
+ void notify( OUString const& i_title,
+ void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& )
+ );
+ void notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) );
+
+ private:
+ /// adds a function to be called to the request processor's queue
+ void impl_processRequest(::std::function<void ()> const& i_request, IMutexGuard& i_instanceLock);
+
+ /// impl-versions of the XUndoManager API.
+ void impl_enterUndoContext( const OUString& i_title, const bool i_hidden );
+ void impl_leaveUndoContext();
+ void impl_addUndoAction( const Reference< XUndoAction >& i_action );
+ void impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo );
+ void impl_clear();
+ void impl_clearRedo();
+ void impl_reset();
+ };
+
+ void UndoManagerHelper_Impl::disposing()
+ {
+ EventObject aEvent;
+ aEvent.Source = getXUndoManager();
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.disposeAndClear( g, aEvent );
+ m_aModifyListeners.disposeAndClear( g, aEvent );
+ }
+ ::osl::MutexGuard aGuard( m_aMutex );
+
+ getUndoManager().RemoveUndoListener( *this );
+ }
+
+ UndoManagerEvent UndoManagerHelper_Impl::buildEvent( OUString const& i_title ) const
+ {
+ UndoManagerEvent aEvent;
+ aEvent.Source = getXUndoManager();
+ aEvent.UndoActionTitle = i_title;
+ aEvent.UndoContextDepth = getUndoManager().GetListActionDepth();
+ return aEvent;
+ }
+
+ void UndoManagerHelper_Impl::impl_notifyModified()
+ {
+ const EventObject aEvent( getXUndoManager() );
+ std::unique_lock g(m_aListenerMutex);
+ m_aModifyListeners.notifyEach( g, &XModifyListener::modified, aEvent );
+ }
+
+ void UndoManagerHelper_Impl::notify( OUString const& i_title,
+ void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const UndoManagerEvent& ) )
+ {
+ const UndoManagerEvent aEvent( buildEvent( i_title ) );
+
+ // TODO: this notification method here is used by UndoManagerHelper_Impl, to multiplex the notifications we
+ // receive from the SfxUndoManager. Those notifications are sent with a locked SolarMutex, which means
+ // we're doing the multiplexing here with a locked SM, too. Which is Bad (TM).
+ // Fixing this properly would require outsourcing all the notifications into an own thread - which might lead
+ // to problems of its own, since clients might expect synchronous notifications.
+
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, i_notificationMethod, aEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::notify( void ( SAL_CALL XUndoManagerListener::*i_notificationMethod )( const EventObject& ) )
+ {
+ const EventObject aEvent( getXUndoManager() );
+
+ // TODO: the same comment as in the other notify, regarding SM locking applies here ...
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, i_notificationMethod, aEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::enterUndoContext( const OUString& i_title, const bool i_hidden, IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this, &i_title, i_hidden] () { return this->impl_enterUndoContext(i_title, i_hidden); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::leaveUndoContext( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this] () { return this->impl_leaveUndoContext(); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock )
+ {
+ if ( !i_action.is() )
+ throw IllegalArgumentException(
+ "illegal undo action object",
+ getXUndoManager(),
+ 1
+ );
+
+ impl_processRequest(
+ [this, &i_action] () { return this->impl_addUndoAction(i_action); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::clear( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this] () { return this->impl_clear(); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::clearRedo( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this] () { return this->impl_clearRedo(); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::reset( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this] () { return this->impl_reset(); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::lock()
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( getMutex() );
+
+ if ( ++m_nLockCount == 1 )
+ {
+ SfxUndoManager& rUndoManager = getUndoManager();
+ rUndoManager.EnableUndo( false );
+ }
+ // <--- SYNCHRONIZED
+ }
+
+ void UndoManagerHelper_Impl::unlock()
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( getMutex() );
+
+ if ( m_nLockCount == 0 )
+ throw NotLockedException( "Undo manager is not locked", getXUndoManager() );
+
+ if ( --m_nLockCount == 0 )
+ {
+ SfxUndoManager& rUndoManager = getUndoManager();
+ rUndoManager.EnableUndo( true );
+ }
+ // <--- SYNCHRONIZED
+ }
+
+ void UndoManagerHelper_Impl::impl_processRequest(::std::function<void ()> const& i_request, IMutexGuard& i_instanceLock)
+ {
+ // create the request, and add it to our queue
+ ::rtl::Reference< UndoManagerRequest > pRequest( new UndoManagerRequest( i_request ) );
+ {
+ std::unique_lock aQueueGuard( m_aQueueMutex );
+ m_aEventQueue.push( pRequest );
+ }
+
+ i_instanceLock.clear();
+
+ if ( m_bProcessingEvents )
+ {
+ // another thread is processing the event queue currently => it will also process the event which we just added
+ pRequest->wait();
+ return;
+ }
+
+ m_bProcessingEvents = true;
+ do
+ {
+ pRequest.clear();
+ {
+ std::unique_lock aQueueGuard( m_aQueueMutex );
+ if ( m_aEventQueue.empty() )
+ {
+ // reset the flag before releasing the queue mutex, otherwise it's possible that another thread
+ // could add an event after we release the mutex, but before we reset the flag. If then this other
+ // thread checks the flag before be reset it, this thread's event would starve.
+ m_bProcessingEvents = false;
+ return;
+ }
+ pRequest = m_aEventQueue.front();
+ m_aEventQueue.pop();
+ }
+ try
+ {
+ pRequest->execute();
+ pRequest->wait();
+ }
+ catch( ... )
+ {
+ {
+ // no chance to process further requests, if the current one failed
+ // => discard them
+ std::unique_lock aQueueGuard( m_aQueueMutex );
+ while ( !m_aEventQueue.empty() )
+ {
+ pRequest = m_aEventQueue.front();
+ m_aEventQueue.pop();
+ pRequest->cancel( getXUndoManager() );
+ }
+ m_bProcessingEvents = false;
+ }
+ // re-throw the error
+ throw;
+ }
+ }
+ while ( true );
+ }
+
+ void UndoManagerHelper_Impl::impl_enterUndoContext( const OUString& i_title, const bool i_hidden )
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( !rUndoManager.IsUndoEnabled() )
+ // ignore this request if the manager is locked
+ return;
+
+ if ( i_hidden && ( rUndoManager.GetUndoActionCount() == 0 ) )
+ throw EmptyUndoStackException(
+ "can't enter a hidden context without a previous Undo action",
+ m_rUndoManagerImplementation.getThis()
+ );
+
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.EnterListAction( i_title, OUString(), 0, ViewShellId(-1) );
+ }
+
+ m_aContextVisibilities.push( i_hidden );
+
+ const UndoManagerEvent aEvent( buildEvent( i_title ) );
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, i_hidden ? &XUndoManagerListener::enteredHiddenContext : &XUndoManagerListener::enteredContext, aEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_leaveUndoContext()
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( !rUndoManager.IsUndoEnabled() )
+ // ignore this request if the manager is locked
+ return;
+
+ if ( !rUndoManager.IsInListAction() )
+ throw InvalidStateException(
+ "no active undo context",
+ getXUndoManager()
+ );
+
+ size_t nContextElements = 0;
+
+ const bool isHiddenContext = m_aContextVisibilities.top();
+ m_aContextVisibilities.pop();
+
+ const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0 );
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ if ( isHiddenContext )
+ nContextElements = rUndoManager.LeaveAndMergeListAction();
+ else
+ nContextElements = rUndoManager.LeaveListAction();
+ }
+ const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0 );
+
+ // prepare notification
+ void ( SAL_CALL XUndoManagerListener::*notificationMethod )( const UndoManagerEvent& ) = nullptr;
+
+ UndoManagerEvent aContextEvent( buildEvent( OUString() ) );
+ const EventObject aClearedEvent( getXUndoManager() );
+ if ( nContextElements == 0 )
+ {
+ notificationMethod = &XUndoManagerListener::cancelledContext;
+ }
+ else if ( isHiddenContext )
+ {
+ notificationMethod = &XUndoManagerListener::leftHiddenContext;
+ }
+ else
+ {
+ aContextEvent.UndoActionTitle = rUndoManager.GetUndoActionComment();
+ notificationMethod = &XUndoManagerListener::leftContext;
+ }
+
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ {
+ std::unique_lock g(m_aListenerMutex);
+ if ( bHadRedoActions && !bHasRedoActions )
+ m_aUndoListeners.notifyEach( g, &XUndoManagerListener::redoActionsCleared, aClearedEvent );
+ m_aUndoListeners.notifyEach( g, notificationMethod, aContextEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_doUndoRedo( IMutexGuard& i_externalLock, const bool i_undo )
+ {
+ ::osl::Guard< ::framework::IMutex > aExternalGuard( i_externalLock.getGuardedMutex() );
+ // note that this assumes that the mutex has been released in the thread which added the
+ // Undo/Redo request, so we can successfully acquire it
+
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ throw UndoContextNotClosedException( OUString(), getXUndoManager() );
+
+ const size_t nElements = i_undo
+ ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
+ if ( nElements == 0 )
+ throw EmptyUndoStackException("stack is empty", getXUndoManager() );
+
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ try
+ {
+ if ( i_undo )
+ rUndoManager.Undo();
+ else
+ rUndoManager.Redo();
+ }
+ catch( const RuntimeException& ) { /* allowed to leave here */ throw; }
+ catch( const UndoFailedException& ) { /* allowed to leave here */ throw; }
+ catch( const Exception& )
+ {
+ // not allowed to leave
+ const Any aError( ::cppu::getCaughtException() );
+ throw UndoFailedException( OUString(), getXUndoManager(), aError );
+ }
+
+ // note that in opposite to all of the other methods, we do *not* have our mutex locked when calling
+ // into the SfxUndoManager implementation. This ensures that an actual XUndoAction::undo/redo is also
+ // called without our mutex being locked.
+ // As a consequence, we do not set m_bAPIActionRunning here. Instead, our actionUndone/actionRedone methods
+ // *always* multiplex the event to our XUndoManagerListeners, not only when m_bAPIActionRunning is FALSE (This
+ // again is different from all other SfxUndoListener methods).
+ // So, we do not need to do this notification here ourself.
+ }
+
+ void UndoManagerHelper_Impl::impl_addUndoAction( const Reference< XUndoAction >& i_action )
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( !rUndoManager.IsUndoEnabled() )
+ // ignore the request if the manager is locked
+ return;
+
+ const UndoManagerEvent aEventAdd( buildEvent( i_action->getTitle() ) );
+ const EventObject aEventClear( getXUndoManager() );
+
+ const bool bHadRedoActions = ( rUndoManager.GetRedoActionCount() > 0 );
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.AddUndoAction( std::make_unique<UndoActionWrapper>( i_action ) );
+ }
+ const bool bHasRedoActions = ( rUndoManager.GetRedoActionCount() > 0 );
+
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, &XUndoManagerListener::undoActionAdded, aEventAdd );
+ if ( bHadRedoActions && !bHasRedoActions )
+ m_aUndoListeners.notifyEach( g, &XUndoManagerListener::redoActionsCleared, aEventClear );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_clear()
+ {
+ EventObject aEvent;
+ {
+ SolarMutexGuard aGuard;
+ ::osl::MutexGuard aGuard2( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ throw UndoContextNotClosedException( OUString(), getXUndoManager() );
+
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.Clear();
+ }
+
+ aEvent = EventObject( getXUndoManager() );
+ }
+
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, &XUndoManagerListener::allActionsCleared, aEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_clearRedo()
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ throw UndoContextNotClosedException( OUString(), getXUndoManager() );
+
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.ClearRedo();
+ }
+
+ const EventObject aEvent( getXUndoManager() );
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, &XUndoManagerListener::redoActionsCleared, aEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::impl_reset()
+ {
+ // SYNCHRONIZED --->
+ ::osl::ClearableMutexGuard aGuard( m_aMutex );
+
+ SfxUndoManager& rUndoManager = getUndoManager();
+ {
+ ::comphelper::FlagGuard aNotificationGuard( m_bAPIActionRunning );
+ rUndoManager.Reset();
+ }
+
+ const EventObject aEvent( getXUndoManager() );
+ aGuard.clear();
+ // <--- SYNCHRONIZED
+
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, &XUndoManagerListener::resetAll, aEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::actionUndone( const OUString& i_actionComment )
+ {
+ UndoManagerEvent aEvent;
+ aEvent.Source = getXUndoManager();
+ aEvent.UndoActionTitle = i_actionComment;
+ aEvent.UndoContextDepth = 0; // Undo can happen on level 0 only
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, &XUndoManagerListener::actionUndone, aEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::actionRedone( const OUString& i_actionComment )
+ {
+ UndoManagerEvent aEvent;
+ aEvent.Source = getXUndoManager();
+ aEvent.UndoActionTitle = i_actionComment;
+ aEvent.UndoContextDepth = 0; // Redo can happen on level 0 only
+ {
+ std::unique_lock g(m_aListenerMutex);
+ m_aUndoListeners.notifyEach( g, &XUndoManagerListener::actionRedone, aEvent );
+ }
+ impl_notifyModified();
+ }
+
+ void UndoManagerHelper_Impl::undoActionAdded( const OUString& i_actionComment )
+ {
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( i_actionComment, &XUndoManagerListener::undoActionAdded );
+ }
+
+ void UndoManagerHelper_Impl::cleared()
+ {
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( &XUndoManagerListener::allActionsCleared );
+ }
+
+ void UndoManagerHelper_Impl::clearedRedo()
+ {
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( &XUndoManagerListener::redoActionsCleared );
+ }
+
+ void UndoManagerHelper_Impl::resetAll()
+ {
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( &XUndoManagerListener::resetAll );
+ }
+
+ void UndoManagerHelper_Impl::listActionEntered( const OUString& i_comment )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ m_aContextAPIFlags.push( m_bAPIActionRunning );
+ m_bContextAPIFlagsEverPushed = true;
+#endif
+
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( i_comment, &XUndoManagerListener::enteredContext );
+ }
+
+ void UndoManagerHelper_Impl::listActionLeft( const OUString& i_comment )
+ {
+#if OSL_DEBUG_LEVEL > 0
+ // It may happen that the very first event listener is added during a
+ // list action after listActionEntered() was already called, e.g. Calc
+ // formula calculation event listener during the input of the very
+ // first formula. Instead of checking m_aContextAPIFlags for empty,
+ // still assert (on calling top()) other stack mismatches but ignore
+ // this one case. See tdf#142980
+ if (m_bContextAPIFlagsEverPushed)
+ {
+ const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
+ m_aContextAPIFlags.pop();
+ OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionLeft: API and non-API contexts interwoven!" );
+ }
+#endif
+
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( i_comment, &XUndoManagerListener::leftContext );
+ }
+
+ void UndoManagerHelper_Impl::listActionCancelled()
+ {
+#if OSL_DEBUG_LEVEL > 0
+ const bool bCurrentContextIsAPIContext = m_aContextAPIFlags.top();
+ m_aContextAPIFlags.pop();
+ OSL_ENSURE( bCurrentContextIsAPIContext == m_bAPIActionRunning, "UndoManagerHelper_Impl::listActionCancelled: API and non-API contexts interwoven!" );
+#endif
+
+ if ( m_bAPIActionRunning )
+ return;
+
+ notify( OUString(), &XUndoManagerListener::cancelledContext );
+ }
+
+ //= UndoManagerHelper
+
+ UndoManagerHelper::UndoManagerHelper( IUndoManagerImplementation& i_undoManagerImpl )
+ :m_xImpl( new UndoManagerHelper_Impl( i_undoManagerImpl ) )
+ {
+ }
+
+ UndoManagerHelper::~UndoManagerHelper()
+ {
+ }
+
+ void UndoManagerHelper::disposing()
+ {
+ m_xImpl->disposing();
+ }
+
+ void UndoManagerHelper::enterUndoContext( const OUString& i_title, IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->enterUndoContext( i_title, false, i_instanceLock );
+ }
+
+ void UndoManagerHelper::enterHiddenUndoContext( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->enterUndoContext( OUString(), true, i_instanceLock );
+ }
+
+ void UndoManagerHelper::leaveUndoContext( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->leaveUndoContext( i_instanceLock );
+ }
+
+ void UndoManagerHelper_Impl::undo( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this, &i_instanceLock] () { return this->impl_doUndoRedo(i_instanceLock, true); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper_Impl::redo( IMutexGuard& i_instanceLock )
+ {
+ impl_processRequest(
+ [this, &i_instanceLock] () { return this->impl_doUndoRedo(i_instanceLock, false); },
+ i_instanceLock
+ );
+ }
+
+ void UndoManagerHelper::addUndoAction( const Reference< XUndoAction >& i_action, IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->addUndoAction( i_action, i_instanceLock );
+ }
+
+ void UndoManagerHelper::undo( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->undo( i_instanceLock );
+ }
+
+ void UndoManagerHelper::redo( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->redo( i_instanceLock );
+ }
+
+ bool UndoManagerHelper::isUndoPossible() const
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
+ SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ return false;
+ return rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel ) > 0;
+ // <--- SYNCHRONIZED
+ }
+
+ bool UndoManagerHelper::isRedoPossible() const
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
+ const SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
+ if ( rUndoManager.IsInListAction() )
+ return false;
+ return rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel ) > 0;
+ // <--- SYNCHRONIZED
+ }
+
+ namespace
+ {
+
+ OUString lcl_getCurrentActionTitle( UndoManagerHelper_Impl& i_impl, const bool i_undo )
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( i_impl.getMutex() );
+
+ const SfxUndoManager& rUndoManager = i_impl.getUndoManager();
+ const size_t nActionCount = i_undo
+ ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
+ if ( nActionCount == 0 )
+ throw EmptyUndoStackException(
+ i_undo ? OUString( "no action on the undo stack" )
+ : OUString( "no action on the redo stack" ),
+ i_impl.getXUndoManager()
+ );
+ return i_undo
+ ? rUndoManager.GetUndoActionComment( 0, SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionComment( 0, SfxUndoManager::TopLevel );
+ // <--- SYNCHRONIZED
+ }
+
+ Sequence< OUString > lcl_getAllActionTitles( UndoManagerHelper_Impl& i_impl, const bool i_undo )
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( i_impl.getMutex() );
+
+ const SfxUndoManager& rUndoManager = i_impl.getUndoManager();
+ const size_t nCount = i_undo
+ ? rUndoManager.GetUndoActionCount( SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionCount( SfxUndoManager::TopLevel );
+
+ Sequence< OUString > aTitles( nCount );
+ auto aTitlesRange = asNonConstRange(aTitles);
+ for ( size_t i=0; i<nCount; ++i )
+ {
+ aTitlesRange[i] = i_undo
+ ? rUndoManager.GetUndoActionComment( i, SfxUndoManager::TopLevel )
+ : rUndoManager.GetRedoActionComment( i, SfxUndoManager::TopLevel );
+ }
+ return aTitles;
+ // <--- SYNCHRONIZED
+ }
+ }
+
+ OUString UndoManagerHelper::getCurrentUndoActionTitle() const
+ {
+ return lcl_getCurrentActionTitle( *m_xImpl, true );
+ }
+
+ OUString UndoManagerHelper::getCurrentRedoActionTitle() const
+ {
+ return lcl_getCurrentActionTitle( *m_xImpl, false );
+ }
+
+ Sequence< OUString > UndoManagerHelper::getAllUndoActionTitles() const
+ {
+ return lcl_getAllActionTitles( *m_xImpl, true );
+ }
+
+ Sequence< OUString > UndoManagerHelper::getAllRedoActionTitles() const
+ {
+ return lcl_getAllActionTitles( *m_xImpl, false );
+ }
+
+ void UndoManagerHelper::clear( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->clear( i_instanceLock );
+ }
+
+ void UndoManagerHelper::clearRedo( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->clearRedo( i_instanceLock );
+ }
+
+ void UndoManagerHelper::reset( IMutexGuard& i_instanceLock )
+ {
+ m_xImpl->reset( i_instanceLock );
+ }
+
+ void UndoManagerHelper::lock()
+ {
+ m_xImpl->lock();
+ }
+
+ void UndoManagerHelper::unlock()
+ {
+ m_xImpl->unlock();
+ }
+
+ bool UndoManagerHelper::isLocked()
+ {
+ // SYNCHRONIZED --->
+ ::osl::MutexGuard aGuard( m_xImpl->getMutex() );
+
+ SfxUndoManager& rUndoManager = m_xImpl->getUndoManager();
+ return !rUndoManager.IsUndoEnabled();
+ // <--- SYNCHRONIZED
+ }
+
+ void UndoManagerHelper::addUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ if ( i_listener.is() )
+ m_xImpl->addUndoManagerListener( i_listener );
+ }
+
+ void UndoManagerHelper::removeUndoManagerListener( const Reference< XUndoManagerListener >& i_listener )
+ {
+ if ( i_listener.is() )
+ m_xImpl->removeUndoManagerListener( i_listener );
+ }
+
+ void UndoManagerHelper::addModifyListener( const Reference< XModifyListener >& i_listener )
+ {
+ if ( i_listener.is() )
+ m_xImpl->addModifyListener( i_listener );
+ }
+
+ void UndoManagerHelper::removeModifyListener( const Reference< XModifyListener >& i_listener )
+ {
+ if ( i_listener.is() )
+ m_xImpl->removeModifyListener( i_listener );
+ }
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/menuconfiguration.cxx b/framework/source/fwe/xml/menuconfiguration.cxx
new file mode 100644
index 0000000000..d2131c8a8d
--- /dev/null
+++ b/framework/source/fwe/xml/menuconfiguration.cxx
@@ -0,0 +1,156 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <menuconfiguration.hxx>
+
+#include <addonmenu.hxx>
+#include <utility>
+#include <xml/menudocumenthandler.hxx>
+#include <xml/saxnamespacefilter.hxx>
+
+#include <uielement/rootitemcontainer.hxx>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <cppuhelper/exc_hlp.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::io;
+
+namespace framework
+{
+
+MenuConfiguration::MenuConfiguration( css::uno::Reference< css::uno::XComponentContext > xContext )
+: m_xContext(std::move( xContext ))
+{
+}
+
+MenuConfiguration::~MenuConfiguration()
+{
+}
+
+Reference< XIndexAccess > MenuConfiguration::CreateMenuBarConfigurationFromXML(
+ Reference< XInputStream > const & rInputStream )
+{
+ Reference< XParser > xParser = Parser::create( m_xContext );
+
+ // connect stream to input stream to the parser
+ InputSource aInputSource;
+
+ aInputSource.aInputStream = rInputStream;
+
+ // create menu bar
+ Reference< XIndexContainer > xItemContainer( new RootItemContainer() );
+
+ // create namespace filter and set menudocument handler inside to support xml namespaces
+
+ Reference< XDocumentHandler > xDocHandler( new OReadMenuDocumentHandler( xItemContainer ));
+
+ Reference< XDocumentHandler > xFilter( new SaxNamespaceFilter( xDocHandler ));
+
+ // connect parser and filter
+ xParser->setDocumentHandler( xFilter );
+
+ try
+ {
+ xParser->parseStream( aInputSource );
+ return xItemContainer;
+ }
+ catch ( const RuntimeException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+ catch( const SAXException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ SAXException aWrappedSAXException;
+
+ if ( !( e.WrappedException >>= aWrappedSAXException ))
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ else
+ throw WrappedTargetException( aWrappedSAXException.Message, Reference< XInterface >(), e.WrappedException );
+ }
+ catch( const css::io::IOException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+}
+
+void MenuConfiguration::StoreMenuBarConfigurationToXML(
+ Reference< XIndexAccess > const & rMenuBarConfiguration,
+ Reference< XOutputStream > const & rOutputStream, bool bIsMenuBar )
+{
+ Reference< XWriter > xWriter = Writer::create(m_xContext);
+ xWriter->setOutputStream( rOutputStream );
+
+ try
+ {
+ OWriteMenuDocumentHandler aWriteMenuDocumentHandler( rMenuBarConfiguration, xWriter, bIsMenuBar );
+ aWriteMenuDocumentHandler.WriteMenuDocument();
+ }
+ catch ( const RuntimeException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+ catch ( const SAXException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+ catch ( const css::io::IOException& e )
+ {
+ css::uno::Any anyEx = cppu::getCaughtException();
+ throw WrappedTargetException( e.Message, Reference< XInterface >(), anyEx );
+ }
+}
+
+void* MenuAttributes::CreateAttribute(const OUString& rFrame, const OUString& rImageIdStr)
+{
+ MenuAttributes* pAttributes = new MenuAttributes(rFrame, rImageIdStr);
+ pAttributes->acquire();
+ return pAttributes;
+}
+
+void* MenuAttributes::CreateAttribute(const css::uno::WeakReference<css::frame::XDispatchProvider>& rDispatchProvider)
+{
+ MenuAttributes* pAttributes = new MenuAttributes(rDispatchProvider);
+ pAttributes->acquire();
+ return pAttributes;
+}
+
+void MenuAttributes::ReleaseAttribute(void* nAttributePtr)
+{
+ if (!nAttributePtr)
+ return;
+ MenuAttributes* pAttributes = static_cast<MenuAttributes*>(nAttributePtr);
+ pAttributes->release();
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/menudocumenthandler.cxx b/framework/source/fwe/xml/menudocumenthandler.cxx
new file mode 100644
index 0000000000..96e4b2324b
--- /dev/null
+++ b/framework/source/fwe/xml/menudocumenthandler.cxx
@@ -0,0 +1,885 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/macros.h>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <xml/menudocumenthandler.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/lang/XSingleComponentFactory.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/attributelist.hxx>
+
+#ifdef ATTRIBUTE_HELPID
+#undef ATTRIBUTE_HELPID
+#endif
+
+constexpr OUStringLiteral XMLNS_MENU = u"http://openoffice.org/2001/menu";
+
+constexpr OUString ELEMENT_MENUBAR = u"http://openoffice.org/2001/menu^menubar"_ustr;
+constexpr OUString ELEMENT_MENU = u"http://openoffice.org/2001/menu^menu"_ustr;
+constexpr OUString ELEMENT_MENUPOPUP = u"http://openoffice.org/2001/menu^menupopup"_ustr;
+constexpr OUString ELEMENT_MENUITEM = u"http://openoffice.org/2001/menu^menuitem"_ustr;
+constexpr OUString ELEMENT_MENUSEPARATOR = u"http://openoffice.org/2001/menu^menuseparator"_ustr;
+
+constexpr OUStringLiteral ELEMENT_NS_MENUBAR = u"menu:menubar";
+constexpr OUString ELEMENT_NS_MENU = u"menu:menu"_ustr;
+constexpr OUString ELEMENT_NS_MENUPOPUP = u"menu:menupopup"_ustr;
+constexpr OUString ELEMENT_NS_MENUITEM = u"menu:menuitem"_ustr;
+constexpr OUString ELEMENT_NS_MENUSEPARATOR = u"menu:menuseparator"_ustr;
+
+constexpr OUString ATTRIBUTE_ID = u"http://openoffice.org/2001/menu^id"_ustr;
+constexpr OUString ATTRIBUTE_LABEL = u"http://openoffice.org/2001/menu^label"_ustr;
+constexpr OUString ATTRIBUTE_HELPID = u"http://openoffice.org/2001/menu^helpid"_ustr;
+constexpr OUString ATTRIBUTE_STYLE = u"http://openoffice.org/2001/menu^style"_ustr;
+
+constexpr OUString ATTRIBUTE_NS_ID = u"menu:id"_ustr;
+constexpr OUString ATTRIBUTE_NS_LABEL = u"menu:label"_ustr;
+constexpr OUStringLiteral ATTRIBUTE_NS_HELPID = u"menu:helpid";
+constexpr OUStringLiteral ATTRIBUTE_NS_STYLE = u"menu:style";
+
+constexpr OUStringLiteral ATTRIBUTE_XMLNS_MENU = u"xmlns:menu";
+
+constexpr OUStringLiteral MENUBAR_DOCTYPE = u"<!DOCTYPE menu:menubar PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"menubar.dtd\">";
+
+#define ATTRIBUTE_ITEMSTYLE_TEXT "text"
+#define ATTRIBUTE_ITEMSTYLE_IMAGE "image"
+#define ATTRIBUTE_ITEMSTYLE_RADIO "radio"
+
+// Property names of a menu/menu item ItemDescriptor
+constexpr OUString ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_HELPURL = u"HelpURL"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_CONTAINER = u"ItemDescriptorContainer"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_LABEL = u"Label"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_TYPE = u"Type"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_STYLE = u"Style"_ustr;
+
+// using namespaces
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::ui;
+
+namespace framework
+{
+
+namespace {
+
+struct MenuStyleItem
+{
+ sal_Int16 nBit;
+ const char* attrName;
+};
+
+}
+
+const MenuStyleItem MenuItemStyles[ ] = {
+ { css::ui::ItemStyle::ICON, ATTRIBUTE_ITEMSTYLE_IMAGE },
+ { css::ui::ItemStyle::TEXT, ATTRIBUTE_ITEMSTYLE_TEXT },
+ { css::ui::ItemStyle::RADIO_CHECK, ATTRIBUTE_ITEMSTYLE_RADIO }
+};
+
+sal_Int32 const nMenuStyleItemEntries = SAL_N_ELEMENTS(MenuItemStyles);
+
+static void ExtractMenuParameters( const Sequence< PropertyValue >& rProp,
+ OUString& rCommandURL,
+ OUString& rLabel,
+ OUString& rHelpURL,
+ Reference< XIndexAccess >& rSubMenu,
+ sal_Int16& rType,
+ sal_Int16& rStyle )
+{
+ for ( const PropertyValue& p : rProp )
+ {
+ if ( p.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ {
+ p.Value >>= rCommandURL;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_HELPURL )
+ {
+ p.Value >>= rHelpURL;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_CONTAINER )
+ {
+ p.Value >>= rSubMenu;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_LABEL )
+ {
+ p.Value >>= rLabel;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_TYPE )
+ {
+ p.Value >>= rType;
+ }
+ else if ( p.Name == ITEM_DESCRIPTOR_STYLE )
+ {
+ p.Value >>= rStyle;
+ }
+ }
+}
+
+// Base class implementation
+
+ReadMenuDocumentHandlerBase::ReadMenuDocumentHandlerBase() :
+ m_aType( ITEM_DESCRIPTOR_TYPE ),
+ m_aLabel( ITEM_DESCRIPTOR_LABEL ),
+ m_aContainer( ITEM_DESCRIPTOR_CONTAINER ),
+ m_aHelpURL( ITEM_DESCRIPTOR_HELPURL ),
+ m_aCommandURL( ITEM_DESCRIPTOR_COMMANDURL ),
+ m_aStyle( ITEM_DESCRIPTOR_STYLE )
+{
+}
+
+ReadMenuDocumentHandlerBase::~ReadMenuDocumentHandlerBase()
+{
+}
+
+void SAL_CALL ReadMenuDocumentHandlerBase::ignorableWhitespace(
+ const OUString& )
+{
+}
+
+void SAL_CALL ReadMenuDocumentHandlerBase::processingInstruction(
+ const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+void SAL_CALL ReadMenuDocumentHandlerBase::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+}
+
+OUString ReadMenuDocumentHandlerBase::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - ";
+ else
+ return OUString();
+}
+
+void ReadMenuDocumentHandlerBase::initPropertyCommon(
+ Sequence< PropertyValue > &rProps, const OUString &rCommandURL,
+ const OUString &rHelpId, const OUString &rLabel, sal_Int16 nItemStyleBits )
+{
+ auto pProps = rProps.getArray();
+
+ pProps[0].Name = m_aCommandURL;
+ pProps[1].Name = m_aHelpURL;
+ pProps[2].Name = m_aContainer;
+ pProps[3].Name = m_aLabel;
+ pProps[4].Name = m_aStyle;
+ pProps[5].Name = m_aType;
+
+ // Common values
+ pProps[0].Value <<= rCommandURL;
+ pProps[1].Value <<= rHelpId;
+ pProps[2].Value <<= Reference< XIndexContainer >();
+ pProps[3].Value <<= rLabel;
+ pProps[4].Value <<= nItemStyleBits;
+ pProps[5].Value <<= css::ui::ItemType::DEFAULT;
+}
+
+OReadMenuDocumentHandler::OReadMenuDocumentHandler(
+ const Reference< XIndexContainer >& rMenuBarContainer )
+: m_nElementDepth( 0 ),
+ m_eReaderMode( ReaderMode::None ),
+ m_xMenuBarContainer( rMenuBarContainer ),
+ m_xContainerFactory( rMenuBarContainer, UNO_QUERY )
+{
+}
+
+OReadMenuDocumentHandler::~OReadMenuDocumentHandler()
+{
+}
+
+void SAL_CALL OReadMenuDocumentHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadMenuDocumentHandler::endDocument()
+{
+ if ( m_nElementDepth > 0 )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "A closing element is missing!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadMenuDocumentHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttrList )
+{
+ if ( m_eReaderMode != ReaderMode::None )
+ {
+ ++m_nElementDepth;
+ m_xReader->startElement( aName, xAttrList );
+ }
+ else
+ {
+ if ( aName == ELEMENT_MENUBAR )
+ {
+ m_eReaderMode = ReaderMode::MenuBar;
+ m_xReader.set( new OReadMenuBarHandler( m_xMenuBarContainer, m_xContainerFactory ));
+ }
+ else if ( aName == ELEMENT_MENUPOPUP )
+ {
+ m_eReaderMode = ReaderMode::MenuPopup;
+ m_xReader.set( new OReadMenuPopupHandler( m_xMenuBarContainer, m_xContainerFactory ));
+ }
+ ++m_nElementDepth;
+ m_xReader->startDocument();
+ }
+}
+
+void SAL_CALL OReadMenuDocumentHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadMenuDocumentHandler::endElement( const OUString& aName )
+{
+ if ( m_eReaderMode == ReaderMode::None )
+ return;
+
+ --m_nElementDepth;
+ m_xReader->endElement( aName );
+ if ( 0 != m_nElementDepth )
+ return;
+
+ m_xReader->endDocument();
+ m_xReader.clear();
+ if ( m_eReaderMode == ReaderMode::MenuBar && aName != ELEMENT_MENUBAR )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menubar expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ else if ( m_eReaderMode == ReaderMode::MenuPopup && aName != ELEMENT_MENUPOPUP )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menupopup expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ m_eReaderMode = ReaderMode::None;
+}
+
+OReadMenuBarHandler::OReadMenuBarHandler(
+ const Reference< XIndexContainer >& rMenuBarContainer,
+ const Reference< XSingleComponentFactory >& rFactory )
+: m_nElementDepth( 0 ),
+ m_bMenuMode( false ),
+ m_xMenuBarContainer( rMenuBarContainer ),
+ m_xContainerFactory( rFactory )
+{
+}
+
+OReadMenuBarHandler::~OReadMenuBarHandler()
+{
+}
+
+void SAL_CALL OReadMenuBarHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadMenuBarHandler::endDocument()
+{
+}
+
+void SAL_CALL OReadMenuBarHandler::startElement(
+ const OUString& rName, const Reference< XAttributeList > &xAttrList )
+{
+ if ( m_bMenuMode )
+ {
+ ++m_nElementDepth;
+ m_xReader->startElement( rName, xAttrList );
+ }
+ else if ( rName == ELEMENT_MENU )
+ {
+ ++m_nElementDepth;
+
+ OUString aHelpId;
+ OUString aCommandId;
+ OUString aLabel;
+ sal_Int16 nItemBits(0);
+
+ m_bMenuMode = true;
+
+ // Container must be factory to create sub container
+ Reference< XComponentContext > xComponentContext(
+ comphelper::getProcessComponentContext() );
+
+ Reference< XIndexContainer > xSubItemContainer;
+ if ( m_xContainerFactory.is() )
+ xSubItemContainer.set( m_xContainerFactory->createInstanceWithContext( xComponentContext ), UNO_QUERY );
+
+ if ( xSubItemContainer.is() )
+ {
+ // read attributes for menu
+ for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
+ {
+ OUString aName = xAttrList->getNameByIndex( i );
+ const OUString aValue = xAttrList->getValueByIndex( i );
+ if ( aName == ATTRIBUTE_ID )
+ aCommandId = aValue;
+ else if ( aName == ATTRIBUTE_LABEL )
+ aLabel = aValue;
+ else if ( aName == ATTRIBUTE_HELPID )
+ aHelpId = aValue;
+ else if ( aName == ATTRIBUTE_STYLE )
+ {
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = aValue.getToken( 0, '+', nIndex );
+ if ( !aToken.isEmpty() )
+ {
+ if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT )
+ nItemBits |= css::ui::ItemStyle::TEXT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE )
+ nItemBits |= css::ui::ItemStyle::ICON;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO )
+ nItemBits |= css::ui::ItemStyle::RADIO_CHECK;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+ }
+
+ if ( !aCommandId.isEmpty() )
+ {
+ Sequence< PropertyValue > aSubMenuProp( 6 );
+ initPropertyCommon( aSubMenuProp, aCommandId, aHelpId, aLabel, nItemBits );
+ aSubMenuProp.getArray()[2].Value <<= xSubItemContainer;
+
+ m_xMenuBarContainer->insertByIndex( m_xMenuBarContainer->getCount(), Any( aSubMenuProp ) );
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "attribute id for element menu required!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_xReader.set( new OReadMenuHandler( xSubItemContainer, m_xContainerFactory ));
+ m_xReader->startDocument();
+ }
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "element menu expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadMenuBarHandler::characters(const OUString&)
+{
+}
+
+void OReadMenuBarHandler::endElement( const OUString& aName )
+{
+ if ( !m_bMenuMode )
+ return;
+
+ --m_nElementDepth;
+ if ( 0 == m_nElementDepth )
+ {
+ m_xReader->endDocument();
+ m_xReader.clear();
+ m_bMenuMode = false;
+ if ( aName != ELEMENT_MENU )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menu expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ else
+ m_xReader->endElement( aName );
+}
+
+OReadMenuHandler::OReadMenuHandler(
+ const Reference< XIndexContainer >& rMenuContainer,
+ const Reference< XSingleComponentFactory >& rFactory ) :
+ m_nElementDepth( 0 ),
+ m_bMenuPopupMode( false ),
+ m_xMenuContainer( rMenuContainer ),
+ m_xContainerFactory( rFactory )
+{
+}
+
+OReadMenuHandler::~OReadMenuHandler()
+{
+}
+
+void SAL_CALL OReadMenuHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadMenuHandler::endDocument()
+{
+}
+
+void SAL_CALL OReadMenuHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttrList )
+{
+ if ( m_bMenuPopupMode )
+ {
+ ++m_nElementDepth;
+ m_xReader->startElement( aName, xAttrList );
+ }
+ else if ( aName == ELEMENT_MENUPOPUP )
+ {
+ ++m_nElementDepth;
+ m_bMenuPopupMode = true;
+ m_xReader.set( new OReadMenuPopupHandler( m_xMenuContainer, m_xContainerFactory ));
+ m_xReader->startDocument();
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "unknown element found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadMenuHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadMenuHandler::endElement( const OUString& aName )
+{
+ if ( !m_bMenuPopupMode )
+ return;
+
+ --m_nElementDepth;
+ if ( 0 == m_nElementDepth )
+ {
+ m_xReader->endDocument();
+ m_xReader.clear();
+ m_bMenuPopupMode = false;
+ if ( aName != ELEMENT_MENUPOPUP )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menupopup expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ else
+ m_xReader->endElement( aName );
+}
+
+OReadMenuPopupHandler::OReadMenuPopupHandler(
+ const Reference< XIndexContainer >& rMenuContainer,
+ const Reference< XSingleComponentFactory >& rFactory ) :
+ m_nElementDepth( 0 ),
+ m_bMenuMode( false ),
+ m_xMenuContainer( rMenuContainer ),
+ m_xContainerFactory( rFactory ),
+ m_xComponentContext( comphelper::getProcessComponentContext() ),
+ m_nNextElementExpected( ELEM_CLOSE_NONE )
+{
+}
+
+OReadMenuPopupHandler::~OReadMenuPopupHandler()
+{
+}
+
+void SAL_CALL OReadMenuPopupHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadMenuPopupHandler::endDocument()
+{
+}
+
+void SAL_CALL OReadMenuPopupHandler::startElement(
+ const OUString& rName, const Reference< XAttributeList > &xAttrList )
+{
+ ++m_nElementDepth;
+
+ if ( m_bMenuMode )
+ m_xReader->startElement( rName, xAttrList );
+ else if ( rName == ELEMENT_MENU )
+ {
+ OUString aHelpId;
+ OUString aCommandId;
+ OUString aLabel;
+ sal_Int16 nItemBits(0);
+
+ m_bMenuMode = true;
+
+ // Container must be factory to create sub container
+ Reference< XIndexContainer > xSubItemContainer;
+ if ( m_xContainerFactory.is() )
+ xSubItemContainer.set( m_xContainerFactory->createInstanceWithContext( m_xComponentContext ), UNO_QUERY );
+
+ // read attributes for menu
+ for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
+ {
+ OUString aName = xAttrList->getNameByIndex( i );
+ const OUString aValue = xAttrList->getValueByIndex( i );
+ if ( aName == ATTRIBUTE_ID )
+ aCommandId = aValue;
+ else if ( aName == ATTRIBUTE_LABEL )
+ aLabel = aValue;
+ else if ( aName == ATTRIBUTE_HELPID )
+ aHelpId = aValue;
+ else if ( aName == ATTRIBUTE_STYLE )
+ {
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = aValue.getToken( 0, '+', nIndex );
+ if ( !aToken.isEmpty() )
+ {
+ if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT )
+ nItemBits |= css::ui::ItemStyle::TEXT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE )
+ nItemBits |= css::ui::ItemStyle::ICON;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO )
+ nItemBits |= css::ui::ItemStyle::RADIO_CHECK;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+
+ }
+
+ if ( !aCommandId.isEmpty() )
+ {
+ Sequence< PropertyValue > aSubMenuProp( 6 );
+ initPropertyCommon( aSubMenuProp, aCommandId, aHelpId, aLabel, nItemBits );
+ aSubMenuProp.getArray()[2].Value <<= xSubItemContainer;
+
+ m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aSubMenuProp ) );
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "attribute id for element menu required!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_xReader.set( new OReadMenuHandler( xSubItemContainer, m_xContainerFactory ));
+ m_xReader->startDocument();
+ }
+ else if ( rName == ELEMENT_MENUITEM )
+ {
+ OUString aHelpId;
+ OUString aCommandId;
+ OUString aLabel;
+ sal_Int16 nItemBits(0);
+ // read attributes for menu item
+ for ( sal_Int16 i=0; i< xAttrList->getLength(); i++ )
+ {
+ OUString aName = xAttrList->getNameByIndex( i );
+ const OUString aValue = xAttrList->getValueByIndex( i );
+ if ( aName == ATTRIBUTE_ID )
+ aCommandId = aValue;
+ else if ( aName == ATTRIBUTE_LABEL )
+ aLabel = aValue;
+ else if ( aName == ATTRIBUTE_HELPID )
+ aHelpId = aValue;
+ else if ( aName == ATTRIBUTE_STYLE )
+ {
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OUString aToken = aValue.getToken( 0, '+', nIndex );
+ if ( !aToken.isEmpty() )
+ {
+ if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT )
+ nItemBits |= css::ui::ItemStyle::TEXT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE )
+ nItemBits |= css::ui::ItemStyle::ICON;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO )
+ nItemBits |= css::ui::ItemStyle::RADIO_CHECK;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+
+ }
+
+ if ( !aCommandId.isEmpty() )
+ {
+ Sequence< PropertyValue > aMenuItem( 6 );
+ initPropertyCommon( aMenuItem, aCommandId, aHelpId, aLabel, nItemBits );
+ aMenuItem.getArray()[2].Value <<= Reference< XIndexContainer >();
+
+ m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aMenuItem ) );
+ }
+
+ m_nNextElementExpected = ELEM_CLOSE_MENUITEM;
+ }
+ else if ( rName == ELEMENT_MENUSEPARATOR )
+ {
+ Sequence< PropertyValue > aMenuSeparator{ comphelper::makePropertyValue(
+ ITEM_DESCRIPTOR_TYPE, css::ui::ItemType::SEPARATOR_LINE) };
+
+ m_xMenuContainer->insertByIndex( m_xMenuContainer->getCount(), Any( aMenuSeparator ) );
+
+ m_nNextElementExpected = ELEM_CLOSE_MENUSEPARATOR;
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "unknown element found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadMenuPopupHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadMenuPopupHandler::endElement( const OUString& aName )
+{
+ --m_nElementDepth;
+ if ( m_bMenuMode )
+ {
+ if ( 0 == m_nElementDepth )
+ {
+ m_xReader->endDocument();
+ m_xReader.clear();
+ m_bMenuMode = false;
+ if ( aName != ELEMENT_MENU )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menu expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ else
+ m_xReader->endElement( aName );
+ }
+ else
+ {
+ if ( m_nNextElementExpected == ELEM_CLOSE_MENUITEM )
+ {
+ if ( aName != ELEMENT_MENUITEM )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menuitem expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ else if ( m_nNextElementExpected == ELEM_CLOSE_MENUSEPARATOR )
+ {
+ if ( aName != ELEMENT_MENUSEPARATOR )
+ {
+ OUString aErrorMessage = getErrorLineString() +
+ "closing element menuseparator expected!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+
+ m_nNextElementExpected = ELEM_CLOSE_NONE;
+ }
+}
+
+// --------------------------------- Write XML ---------------------------------
+
+OWriteMenuDocumentHandler::OWriteMenuDocumentHandler(
+ const Reference< XIndexAccess >& rMenuBarContainer,
+ const Reference< XDocumentHandler >& rDocumentHandler,
+ bool bIsMenuBar ) :
+ m_xMenuBarContainer( rMenuBarContainer ),
+ m_xWriteDocumentHandler( rDocumentHandler ),
+ m_bIsMenuBar( bIsMenuBar )
+{
+ m_xEmptyList = new ::comphelper::AttributeList;
+}
+
+OWriteMenuDocumentHandler::~OWriteMenuDocumentHandler()
+{
+}
+
+void OWriteMenuDocumentHandler::WriteMenuDocument()
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ m_xWriteDocumentHandler->startDocument();
+
+ // write DOCTYPE line!
+ Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY );
+ if ( m_bIsMenuBar /*FIXME*/ && xExtendedDocHandler.is() )
+ {
+ xExtendedDocHandler->unknown( MENUBAR_DOCTYPE );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ }
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_MENU,
+ XMLNS_MENU );
+
+ if ( m_bIsMenuBar ) //FIXME
+ pList->AddAttribute( ATTRIBUTE_NS_ID,
+ "menubar" );
+
+ OUString aRootElement;
+ if ( m_bIsMenuBar )
+ aRootElement = ELEMENT_NS_MENUBAR;
+ else
+ aRootElement = ELEMENT_NS_MENUPOPUP;
+ m_xWriteDocumentHandler->startElement( aRootElement, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ WriteMenu( m_xMenuBarContainer );
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( aRootElement );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endDocument();
+}
+
+void OWriteMenuDocumentHandler::WriteMenu( const Reference< XIndexAccess >& rMenuContainer )
+{
+ sal_Int32 nItemCount = rMenuContainer->getCount();
+ bool bSeparator = false;
+ Any aAny;
+
+ for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ )
+ {
+ Sequence< PropertyValue > aProps;
+ aAny = rMenuContainer->getByIndex( nItemPos );
+ if ( aAny >>= aProps )
+ {
+ OUString aCommandURL;
+ OUString aLabel;
+ OUString aHelpURL;
+ sal_Int16 nType( css::ui::ItemType::DEFAULT );
+ sal_Int16 nItemBits( 0 );
+ Reference< XIndexAccess > xSubMenu;
+
+ ExtractMenuParameters( aProps, aCommandURL, aLabel, aHelpURL, xSubMenu, nType, nItemBits );
+ if ( xSubMenu.is() )
+ {
+ if ( !aCommandURL.isEmpty() )
+ {
+ rtl::Reference<::comphelper::AttributeList> pListMenu = new ::comphelper::AttributeList;
+
+ pListMenu->AddAttribute( ATTRIBUTE_NS_ID,
+ aCommandURL );
+
+ if ( !aLabel.isEmpty() )
+ pListMenu->AddAttribute( ATTRIBUTE_NS_LABEL,
+ aLabel );
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENU, pListMenu );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUPOPUP, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ WriteMenu( xSubMenu );
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUPOPUP );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENU );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ bSeparator = false;
+ }
+ }
+ else
+ {
+ if ( nType == css::ui::ItemType::DEFAULT )
+ {
+ if ( !aCommandURL.isEmpty() )
+ {
+ bSeparator = false;
+ WriteMenuItem( aCommandURL, aLabel, aHelpURL, nItemBits );
+ }
+ }
+ else if ( !bSeparator )
+ {
+ // Don't write two separators together
+ WriteMenuSeparator();
+ bSeparator = true;
+ }
+ }
+ }
+ }
+}
+
+void OWriteMenuDocumentHandler::WriteMenuItem( const OUString& aCommandURL, const OUString& aLabel, const OUString& aHelpURL, sal_Int16 nStyle )
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ pList->AddAttribute( ATTRIBUTE_NS_ID,
+ aCommandURL );
+
+ if ( !aHelpURL.isEmpty() )
+ {
+ pList->AddAttribute( ATTRIBUTE_NS_HELPID,
+ aHelpURL );
+ }
+
+ if ( !aLabel.isEmpty() )
+ {
+ pList->AddAttribute( ATTRIBUTE_NS_LABEL,
+ aLabel );
+ }
+ if ( nStyle > 0 )
+ {
+ OUStringBuffer aValue;
+ const MenuStyleItem* pStyle = MenuItemStyles;
+
+ for ( sal_Int32 nIndex = 0; nIndex < nMenuStyleItemEntries; ++nIndex, ++pStyle )
+ {
+ if ( nStyle & pStyle->nBit )
+ {
+ if ( !aValue.isEmpty() )
+ aValue.append("+");
+ aValue.appendAscii( pStyle->attrName );
+ }
+ }
+ pList->AddAttribute( ATTRIBUTE_NS_STYLE,
+ aValue.makeStringAndClear() );
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUITEM, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUITEM );
+}
+
+void OWriteMenuDocumentHandler::WriteMenuSeparator()
+{
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_MENUSEPARATOR, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_MENUSEPARATOR );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/saxnamespacefilter.cxx b/framework/source/fwe/xml/saxnamespacefilter.cxx
new file mode 100644
index 0000000000..b5349eb488
--- /dev/null
+++ b/framework/source/fwe/xml/saxnamespacefilter.cxx
@@ -0,0 +1,163 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <vector>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
+#include <xml/saxnamespacefilter.hxx>
+
+#include <comphelper/attributelist.hxx>
+#include <rtl/ref.hxx>
+
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::uno;
+
+namespace framework{
+
+SaxNamespaceFilter::SaxNamespaceFilter( Reference< XDocumentHandler > const & rSax1DocumentHandler ) :
+ xDocumentHandler( rSax1DocumentHandler )
+{
+}
+
+SaxNamespaceFilter::~SaxNamespaceFilter()
+{
+}
+
+// XDocumentHandler
+void SAL_CALL SaxNamespaceFilter::startDocument()
+{
+}
+
+void SAL_CALL SaxNamespaceFilter::endDocument()
+{
+}
+
+void SAL_CALL SaxNamespaceFilter::startElement(
+ const OUString& rName, const Reference< XAttributeList > &xAttribs )
+{
+ XMLNamespaces aXMLNamespaces;
+ if ( !m_aNamespaceStack.empty() )
+ aXMLNamespaces = m_aNamespaceStack.top();
+
+ rtl::Reference<::comphelper::AttributeList> pNewList = new ::comphelper::AttributeList();
+
+ // examine all namespaces for this level
+ ::std::vector< sal_Int16 > aAttributeIndexes;
+ {
+ for ( sal_Int16 i=0; i< xAttribs->getLength(); i++ )
+ {
+ OUString aName = xAttribs->getNameByIndex( i );
+ if ( aName.startsWith( "xmlns" ) )
+ aXMLNamespaces.addNamespace( aName, xAttribs->getValueByIndex( i ));
+ else
+ aAttributeIndexes.push_back( i );
+ }
+ }
+
+ // current namespaces for this level
+ m_aNamespaceStack.push( aXMLNamespaces );
+
+ try
+ {
+ // apply namespaces to all remaining attributes
+ for (auto const& attributeIndex : aAttributeIndexes)
+ {
+ OUString aAttributeName = xAttribs->getNameByIndex(attributeIndex);
+ OUString aValue = xAttribs->getValueByIndex(attributeIndex);
+ OUString aNamespaceAttributeName = aXMLNamespaces.applyNSToAttributeName( aAttributeName );
+ pNewList->AddAttribute(aNamespaceAttributeName, aValue);
+ }
+ }
+ catch ( SAXException& e )
+ {
+ e.Message = getErrorLineString() + e.Message;
+ throw;
+ }
+
+ OUString aNamespaceElementName;
+
+ try
+ {
+ aNamespaceElementName = aXMLNamespaces.applyNSToElementName( rName );
+ }
+ catch ( SAXException& e )
+ {
+ e.Message = getErrorLineString() + e.Message;
+ throw;
+ }
+
+ xDocumentHandler->startElement( aNamespaceElementName, pNewList );
+}
+
+void SAL_CALL SaxNamespaceFilter::endElement(const OUString& aName)
+{
+ XMLNamespaces& aXMLNamespaces = m_aNamespaceStack.top();
+ OUString aNamespaceElementName;
+
+ try
+ {
+ aNamespaceElementName = aXMLNamespaces.applyNSToElementName( aName );
+ }
+ catch ( SAXException& e )
+ {
+ e.Message = getErrorLineString() + e.Message;
+ throw;
+ }
+
+ xDocumentHandler->endElement( aNamespaceElementName );
+ m_aNamespaceStack.pop();
+}
+
+void SAL_CALL SaxNamespaceFilter::characters(const OUString& aChars)
+{
+ xDocumentHandler->characters( aChars );
+}
+
+void SAL_CALL SaxNamespaceFilter::ignorableWhitespace(const OUString& aWhitespaces)
+{
+ xDocumentHandler->ignorableWhitespace( aWhitespaces );
+}
+
+void SAL_CALL SaxNamespaceFilter::processingInstruction(
+ const OUString& aTarget, const OUString& aData)
+{
+ xDocumentHandler->processingInstruction( aTarget, aData );
+}
+
+void SAL_CALL SaxNamespaceFilter::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+ xDocumentHandler->setDocumentLocator( xLocator );
+}
+
+OUString SaxNamespaceFilter::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - ";
+ else
+ return OUString();
+}
+
+} // namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/statusbarconfiguration.cxx b/framework/source/fwe/xml/statusbarconfiguration.cxx
new file mode 100644
index 0000000000..ce974237b2
--- /dev/null
+++ b/framework/source/fwe/xml/statusbarconfiguration.cxx
@@ -0,0 +1,105 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <statusbarconfiguration.hxx>
+#include <xml/statusbardocumenthandler.hxx>
+#include <xml/saxnamespacefilter.hxx>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::container;
+
+namespace framework
+{
+
+bool StatusBarConfiguration::LoadStatusBar(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XInputStream >& xInputStream,
+ const Reference< XIndexContainer >& rStatusbarConfiguration )
+{
+ Reference< XParser > xParser = Parser::create(rxContext);
+
+ // connect stream to input stream to the parser
+ InputSource aInputSource;
+ aInputSource.aInputStream = xInputStream;
+
+ // create namespace filter and set menudocument handler inside to support xml namespaces
+ Reference< XDocumentHandler > xDocHandler( new OReadStatusBarDocumentHandler( rStatusbarConfiguration ));
+ Reference< XDocumentHandler > xFilter( new SaxNamespaceFilter( xDocHandler ));
+
+ // connect parser and filter
+ xParser->setDocumentHandler( xFilter );
+
+ try
+ {
+ xParser->parseStream( aInputSource );
+ return true;
+ }
+ catch ( const RuntimeException& )
+ {
+ return false;
+ }
+ catch( const SAXException& )
+ {
+ return false;
+ }
+ catch( const css::io::IOException& )
+ {
+ return false;
+ }
+}
+
+bool StatusBarConfiguration::StoreStatusBar(
+ const Reference< XComponentContext >& rxContext,
+ const Reference< XOutputStream >& xOutputStream,
+ const Reference< XIndexAccess >& rStatusbarConfiguration )
+{
+ Reference< XWriter > xWriter = Writer::create( rxContext );
+ xWriter->setOutputStream( xOutputStream );
+
+ try
+ {
+ OWriteStatusBarDocumentHandler aWriteStatusBarDocumentHandler( rStatusbarConfiguration, xWriter );
+ aWriteStatusBarDocumentHandler.WriteStatusBarDocument();
+ return true;
+ }
+ catch ( const RuntimeException& )
+ {
+ return false;
+ }
+ catch ( const SAXException& )
+ {
+ return false;
+ }
+ catch ( const css::io::IOException& )
+ {
+ return false;
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/statusbardocumenthandler.cxx b/framework/source/fwe/xml/statusbardocumenthandler.cxx
new file mode 100644
index 0000000000..cafd3258ec
--- /dev/null
+++ b/framework/source/fwe/xml/statusbardocumenthandler.cxx
@@ -0,0 +1,616 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xml/statusbardocumenthandler.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+
+#include <vcl/status.hxx>
+
+#include <comphelper/attributelist.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star::container;
+
+constexpr OUString XMLNS_STATUSBAR = u"http://openoffice.org/2001/statusbar"_ustr;
+constexpr OUString XMLNS_XLINK = u"http://www.w3.org/1999/xlink"_ustr;
+constexpr OUStringLiteral XMLNS_STATUSBAR_PREFIX = u"statusbar:";
+constexpr OUStringLiteral XMLNS_XLINK_PREFIX = u"xlink:";
+
+constexpr OUString XMLNS_FILTER_SEPARATOR = u"^"_ustr;
+
+#define ELEMENT_STATUSBAR "statusbar"
+#define ELEMENT_STATUSBARITEM "statusbaritem"
+
+#define ATTRIBUTE_ALIGN "align"
+#define ATTRIBUTE_STYLE "style"
+#define ATTRIBUTE_URL "href"
+#define ATTRIBUTE_WIDTH "width"
+#define ATTRIBUTE_OFFSET "offset"
+#define ATTRIBUTE_AUTOSIZE "autosize"
+#define ATTRIBUTE_OWNERDRAW "ownerdraw"
+#define ATTRIBUTE_HELPURL "helpid"
+#define ATTRIBUTE_MANDATORY "mandatory"
+
+constexpr OUString ELEMENT_NS_STATUSBAR = u"statusbar:statusbar"_ustr;
+constexpr OUString ELEMENT_NS_STATUSBARITEM = u"statusbar:statusbaritem"_ustr;
+
+constexpr OUStringLiteral ATTRIBUTE_XMLNS_STATUSBAR = u"xmlns:statusbar";
+constexpr OUStringLiteral ATTRIBUTE_XMLNS_XLINK = u"xmlns:xlink";
+
+constexpr OUString ATTRIBUTE_BOOLEAN_TRUE = u"true"_ustr;
+constexpr OUString ATTRIBUTE_BOOLEAN_FALSE = u"false"_ustr;
+
+constexpr OUString ATTRIBUTE_ALIGN_LEFT = u"left"_ustr;
+constexpr OUString ATTRIBUTE_ALIGN_RIGHT = u"right"_ustr;
+constexpr OUString ATTRIBUTE_ALIGN_CENTER = u"center"_ustr;
+
+constexpr OUStringLiteral ATTRIBUTE_STYLE_IN = u"in";
+constexpr OUString ATTRIBUTE_STYLE_OUT = u"out"_ustr;
+constexpr OUString ATTRIBUTE_STYLE_FLAT = u"flat"_ustr;
+
+constexpr OUStringLiteral STATUSBAR_DOCTYPE = u"<!DOCTYPE statusbar:statusbar PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"statusbar.dtd\">";
+
+namespace framework
+{
+
+// Property names of a menu/menu item ItemDescriptor
+constexpr OUString ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_HELPURL = u"HelpURL"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_OFFSET = u"Offset"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_STYLE = u"Style"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_WIDTH = u"Width"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_TYPE = u"Type"_ustr;
+
+static void ExtractStatusbarItemParameters(
+ const Sequence< PropertyValue >& rProp,
+ OUString& rCommandURL,
+ OUString& rHelpURL,
+ sal_Int16& rOffset,
+ sal_Int16& rStyle,
+ sal_Int16& rWidth )
+{
+ for ( const PropertyValue& rEntry : rProp )
+ {
+ if ( rEntry.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ {
+ rEntry.Value >>= rCommandURL;
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_HELPURL )
+ {
+ rEntry.Value >>= rHelpURL;
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_OFFSET )
+ {
+ rEntry.Value >>= rOffset;
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_STYLE )
+ {
+ rEntry.Value >>= rStyle;
+ }
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_WIDTH )
+ {
+ rEntry.Value >>= rWidth;
+ }
+ }
+}
+
+namespace {
+
+struct StatusBarEntryProperty
+{
+ OReadStatusBarDocumentHandler::StatusBar_XML_Namespace nNamespace;
+ char aEntryName[20];
+};
+
+}
+
+StatusBarEntryProperty const StatusBarEntries[OReadStatusBarDocumentHandler::SB_XML_ENTRY_COUNT] =
+{
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ELEMENT_STATUSBAR },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ELEMENT_STATUSBARITEM },
+ { OReadStatusBarDocumentHandler::SB_NS_XLINK, ATTRIBUTE_URL },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_ALIGN },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_STYLE },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_AUTOSIZE },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_OWNERDRAW },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_WIDTH },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_OFFSET },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_HELPURL },
+ { OReadStatusBarDocumentHandler::SB_NS_STATUSBAR, ATTRIBUTE_MANDATORY }
+};
+
+OReadStatusBarDocumentHandler::OReadStatusBarDocumentHandler(
+ const Reference< XIndexContainer >& rStatusBarItems ) :
+ m_aStatusBarItems( rStatusBarItems )
+{
+ // create hash map
+ for ( int i = 0; i < SB_XML_ENTRY_COUNT; i++ )
+ {
+ if ( StatusBarEntries[i].nNamespace == SB_NS_STATUSBAR )
+ {
+ OUString temp = XMLNS_STATUSBAR + XMLNS_FILTER_SEPARATOR +
+ OUString::createFromAscii( StatusBarEntries[i].aEntryName );
+ m_aStatusBarMap.emplace( temp, static_cast<StatusBar_XML_Entry>(i) );
+ }
+ else
+ {
+ OUString temp = XMLNS_XLINK + XMLNS_FILTER_SEPARATOR +
+ OUString::createFromAscii( StatusBarEntries[i].aEntryName );
+ m_aStatusBarMap.emplace( temp, static_cast<StatusBar_XML_Entry>(i) );
+ }
+ }
+
+ m_bStatusBarStartFound = false;
+ m_bStatusBarItemStartFound = false;
+}
+
+OReadStatusBarDocumentHandler::~OReadStatusBarDocumentHandler()
+{
+}
+
+// XDocumentHandler
+void SAL_CALL OReadStatusBarDocumentHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::endDocument()
+{
+ if ( m_bStatusBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "No matching start or end element 'statusbar' found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttribs )
+{
+ StatusBarHashMap::const_iterator pStatusBarEntry = m_aStatusBarMap.find( aName );
+ if ( pStatusBarEntry == m_aStatusBarMap.end() )
+ return;
+
+ switch ( pStatusBarEntry->second )
+ {
+ case SB_ELEMENT_STATUSBAR:
+ {
+ if ( m_bStatusBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'statusbar:statusbar' cannot be embedded into 'statusbar:statusbar'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bStatusBarStartFound = true;
+ }
+ break;
+
+ case SB_ELEMENT_STATUSBARITEM:
+ {
+ if ( !m_bStatusBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'statusbar:statusbaritem' must be embedded into element 'statusbar:statusbar'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ if ( m_bStatusBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element statusbar:statusbaritem is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ OUString aCommandURL;
+ OUString aHelpURL;
+ sal_Int16 nItemBits( ItemStyle::ALIGN_CENTER|ItemStyle::DRAW_IN3D|ItemStyle::MANDATORY );
+ sal_Int16 nWidth( 0 );
+ sal_Int16 nOffset( STATUSBAR_OFFSET );
+ bool bCommandURL( false );
+
+ m_bStatusBarItemStartFound = true;
+ for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ )
+ {
+ pStatusBarEntry = m_aStatusBarMap.find( xAttribs->getNameByIndex( n ) );
+ if ( pStatusBarEntry != m_aStatusBarMap.end() )
+ {
+ switch ( pStatusBarEntry->second )
+ {
+ case SB_ATTRIBUTE_URL:
+ {
+ bCommandURL = true;
+ aCommandURL = xAttribs->getValueByIndex( n );
+ }
+ break;
+
+ case SB_ATTRIBUTE_ALIGN:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_LEFT )
+ {
+ nItemBits |= ItemStyle::ALIGN_LEFT;
+ nItemBits &= ~ItemStyle::ALIGN_CENTER;
+ }
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_CENTER )
+ {
+ nItemBits |= ItemStyle::ALIGN_CENTER;
+ nItemBits &= ~ItemStyle::ALIGN_LEFT;
+ }
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_ALIGN_RIGHT )
+ {
+ nItemBits |= ItemStyle::ALIGN_RIGHT;
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:align must have one value of 'left','right' or 'center'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case SB_ATTRIBUTE_STYLE:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_IN )
+ {
+ nItemBits |= ItemStyle::DRAW_IN3D;
+ nItemBits &= ~ItemStyle::DRAW_OUT3D;
+ }
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_OUT )
+ {
+ nItemBits |= ItemStyle::DRAW_OUT3D;
+ nItemBits &= ~ItemStyle::DRAW_IN3D;
+ }
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_STYLE_FLAT )
+ {
+ nItemBits |= ItemStyle::DRAW_FLAT;
+ }
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:autosize must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case SB_ATTRIBUTE_AUTOSIZE:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE )
+ nItemBits |= ItemStyle::AUTO_SIZE;
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE )
+ nItemBits &= ~ItemStyle::AUTO_SIZE;
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:autosize must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case SB_ATTRIBUTE_OWNERDRAW:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE )
+ nItemBits |= ItemStyle::OWNER_DRAW;
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE )
+ nItemBits &= ~ItemStyle::OWNER_DRAW;
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:ownerdraw must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case SB_ATTRIBUTE_WIDTH:
+ {
+ nWidth = static_cast<sal_Int16>(xAttribs->getValueByIndex( n ).toInt32());
+ }
+ break;
+
+ case SB_ATTRIBUTE_OFFSET:
+ {
+ nOffset = static_cast<sal_Int16>(xAttribs->getValueByIndex( n ).toInt32());
+ }
+ break;
+
+ case SB_ATTRIBUTE_HELPURL:
+ {
+ aHelpURL = xAttribs->getValueByIndex( n );
+ }
+ break;
+
+ case SB_ATTRIBUTE_MANDATORY:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE )
+ nItemBits |= ItemStyle::MANDATORY;
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE )
+ nItemBits &= ~ItemStyle::MANDATORY;
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute statusbar:mandatory must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ } // for
+
+ if ( !bCommandURL )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Required attribute statusbar:url must have a value!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ else
+ {
+ Sequence< PropertyValue > aStatusbarItemProp{
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_COMMANDURL, aCommandURL),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_HELPURL, aHelpURL),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_OFFSET, nOffset),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_STYLE, nItemBits),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_WIDTH, nWidth),
+ comphelper::makePropertyValue(ITEM_DESCRIPTOR_TYPE, css::ui::ItemType::DEFAULT)
+ };
+
+ m_aStatusBarItems->insertByIndex( m_aStatusBarItems->getCount(), Any( aStatusbarItemProp ) );
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::endElement(const OUString& aName)
+{
+ StatusBarHashMap::const_iterator pStatusBarEntry = m_aStatusBarMap.find( aName );
+ if ( pStatusBarEntry == m_aStatusBarMap.end() )
+ return;
+
+ switch ( pStatusBarEntry->second )
+ {
+ case SB_ELEMENT_STATUSBAR:
+ {
+ if ( !m_bStatusBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'statusbar' found, but no start element 'statusbar'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bStatusBarStartFound = false;
+ }
+ break;
+
+ case SB_ELEMENT_STATUSBARITEM:
+ {
+ if ( !m_bStatusBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'statusbar:statusbaritem' found, but no start element 'statusbar:statusbaritem'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bStatusBarItemStartFound = false;
+ }
+ break;
+
+ default: break;
+ }
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::ignorableWhitespace(const OUString&)
+{
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::processingInstruction(
+ const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+void SAL_CALL OReadStatusBarDocumentHandler::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+}
+
+OUString OReadStatusBarDocumentHandler::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - ";
+ else
+ return OUString();
+}
+
+// OWriteStatusBarDocumentHandler
+
+OWriteStatusBarDocumentHandler::OWriteStatusBarDocumentHandler(
+ const Reference< XIndexAccess >& aStatusBarItems,
+ const Reference< XDocumentHandler >& rWriteDocumentHandler ) :
+ m_aStatusBarItems( aStatusBarItems ),
+ m_xWriteDocumentHandler( rWriteDocumentHandler )
+{
+ m_xEmptyList = new ::comphelper::AttributeList;
+ m_aXMLXlinkNS = XMLNS_XLINK_PREFIX;
+ m_aXMLStatusBarNS = XMLNS_STATUSBAR_PREFIX;
+}
+
+OWriteStatusBarDocumentHandler::~OWriteStatusBarDocumentHandler()
+{
+}
+
+void OWriteStatusBarDocumentHandler::WriteStatusBarDocument()
+{
+ m_xWriteDocumentHandler->startDocument();
+
+ // write DOCTYPE line!
+ Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY );
+ if ( xExtendedDocHandler.is() )
+ {
+ xExtendedDocHandler->unknown( STATUSBAR_DOCTYPE );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ }
+
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_STATUSBAR,
+ XMLNS_STATUSBAR );
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_XLINK,
+ XMLNS_XLINK );
+
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_STATUSBAR, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ sal_Int32 nItemCount = m_aStatusBarItems->getCount();
+ Any aAny;
+
+ for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ )
+ {
+ Sequence< PropertyValue > aProps;
+ aAny = m_aStatusBarItems->getByIndex( nItemPos );
+ if ( aAny >>= aProps )
+ {
+ OUString aCommandURL;
+ OUString aHelpURL;
+ sal_Int16 nStyle( ItemStyle::ALIGN_CENTER|ItemStyle::DRAW_IN3D );
+ sal_Int16 nWidth( 0 );
+ sal_Int16 nOffset( STATUSBAR_OFFSET );
+
+ ExtractStatusbarItemParameters(
+ aProps,
+ aCommandURL,
+ aHelpURL,
+ nOffset,
+ nStyle,
+ nWidth );
+
+ if ( !aCommandURL.isEmpty() )
+ WriteStatusBarItem( aCommandURL, nOffset, nStyle, nWidth );
+ }
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_STATUSBAR );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endDocument();
+}
+
+// protected member functions
+
+void OWriteStatusBarDocumentHandler::WriteStatusBarItem(
+ const OUString& rCommandURL,
+ sal_Int16 nOffset,
+ sal_Int16 nStyle,
+ sal_Int16 nWidth )
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ if (m_aAttributeURL.isEmpty() )
+ {
+ m_aAttributeURL = m_aXMLXlinkNS + ATTRIBUTE_URL;
+ }
+
+ // save required attribute (URL)
+ pList->AddAttribute( m_aAttributeURL, rCommandURL );
+
+ // alignment
+ if ( nStyle & ItemStyle::ALIGN_RIGHT )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN,
+ ATTRIBUTE_ALIGN_RIGHT );
+ }
+ else if ( nStyle & ItemStyle::ALIGN_CENTER )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN,
+ ATTRIBUTE_ALIGN_CENTER );
+ }
+ else
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_ALIGN,
+ ATTRIBUTE_ALIGN_LEFT );
+ }
+
+ // style ( StatusBarItemBits::In is default )
+ if ( nStyle & ItemStyle::DRAW_FLAT )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_STYLE,
+ ATTRIBUTE_STYLE_FLAT );
+ }
+ else if ( nStyle & ItemStyle::DRAW_OUT3D )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_STYLE,
+ ATTRIBUTE_STYLE_OUT );
+ }
+
+ // autosize (default sal_False)
+ if ( nStyle & ItemStyle::AUTO_SIZE )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_AUTOSIZE,
+ ATTRIBUTE_BOOLEAN_TRUE );
+ }
+
+ // ownerdraw (default sal_False)
+ if ( nStyle & ItemStyle::OWNER_DRAW )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_OWNERDRAW,
+ ATTRIBUTE_BOOLEAN_TRUE );
+ }
+
+ // width (default 0)
+ if ( nWidth > 0 )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_WIDTH,
+ OUString::number( nWidth ) );
+ }
+
+ // offset (default STATUSBAR_OFFSET)
+ if ( nOffset != STATUSBAR_OFFSET )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_OFFSET,
+ OUString::number( nOffset ) );
+ }
+
+ // mandatory (default sal_True)
+ if ( !( nStyle & ItemStyle::MANDATORY ) )
+ {
+ pList->AddAttribute( m_aXMLStatusBarNS + ATTRIBUTE_MANDATORY,
+ ATTRIBUTE_BOOLEAN_FALSE );
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_STATUSBARITEM, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_STATUSBARITEM );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/toolboxconfiguration.cxx b/framework/source/fwe/xml/toolboxconfiguration.cxx
new file mode 100644
index 0000000000..d9e34baabc
--- /dev/null
+++ b/framework/source/fwe/xml/toolboxconfiguration.cxx
@@ -0,0 +1,106 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <toolboxconfiguration.hxx>
+#include <xml/toolboxdocumenthandler.hxx>
+#include <xml/saxnamespacefilter.hxx>
+
+#include <com/sun/star/xml/sax/Parser.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XInputStream.hpp>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::container;
+
+namespace framework
+{
+bool ToolBoxConfiguration::LoadToolBox(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::io::XInputStream>& rInputStream,
+ const css::uno::Reference<css::container::XIndexContainer>& rToolbarConfiguration)
+{
+ Reference<XParser> xParser = Parser::create(rxContext);
+
+ // connect stream to input stream to the parser
+ InputSource aInputSource;
+
+ aInputSource.aInputStream = rInputStream;
+
+ // create namespace filter and set menudocument handler inside to support xml namespaces
+ Reference<XDocumentHandler> xDocHandler(new OReadToolBoxDocumentHandler(rToolbarConfiguration));
+ Reference<XDocumentHandler> xFilter(new SaxNamespaceFilter(xDocHandler));
+
+ // connect parser and filter
+ xParser->setDocumentHandler(xFilter);
+
+ try
+ {
+ xParser->parseStream(aInputSource);
+ return true;
+ }
+ catch (const RuntimeException&)
+ {
+ return false;
+ }
+ catch (const SAXException&)
+ {
+ return false;
+ }
+ catch (const css::io::IOException&)
+ {
+ return false;
+ }
+}
+
+bool ToolBoxConfiguration::StoreToolBox(
+ const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+ const css::uno::Reference<css::io::XOutputStream>& rOutputStream,
+ const css::uno::Reference<css::container::XIndexAccess>& rToolbarConfiguration)
+{
+ Reference<XWriter> xWriter = Writer::create(rxContext);
+ xWriter->setOutputStream(rOutputStream);
+
+ try
+ {
+ Reference<XDocumentHandler> xHandler(xWriter, UNO_QUERY_THROW);
+ OWriteToolBoxDocumentHandler aWriteToolBoxDocumentHandler(rToolbarConfiguration, xHandler);
+ aWriteToolBoxDocumentHandler.WriteToolBoxDocument();
+ return true;
+ }
+ catch (const RuntimeException&)
+ {
+ return false;
+ }
+ catch (const SAXException&)
+ {
+ return false;
+ }
+ catch (const css::io::IOException&)
+ {
+ return false;
+ }
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/toolboxdocumenthandler.cxx b/framework/source/fwe/xml/toolboxdocumenthandler.cxx
new file mode 100644
index 0000000000..7464939e1e
--- /dev/null
+++ b/framework/source/fwe/xml/toolboxdocumenthandler.cxx
@@ -0,0 +1,713 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xml/toolboxdocumenthandler.hxx>
+#include <xml/toolboxconfigurationdefines.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+#include <com/sun/star/xml/sax/XExtendedDocumentHandler.hpp>
+#include <com/sun/star/ui/ItemType.hpp>
+#include <com/sun/star/ui/ItemStyle.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/container/XIndexContainer.hpp>
+
+#include <sal/config.h>
+#include <sal/macros.h>
+#include <vcl/settings.hxx>
+#include <rtl/ref.hxx>
+#include <rtl/ustrbuf.hxx>
+
+#include <comphelper/attributelist.hxx>
+#include <comphelper/propertysequence.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::container;
+using namespace ::com::sun::star::xml::sax;
+
+constexpr OUStringLiteral TOOLBAR_DOCTYPE = u"<!DOCTYPE toolbar:toolbar PUBLIC \"-//OpenOffice.org//DTD OfficeDocument 1.0//EN\" \"toolbar.dtd\">";
+
+namespace framework
+{
+
+// Property names of a menu/menu item ItemDescriptor
+constexpr OUString ITEM_DESCRIPTOR_COMMANDURL = u"CommandURL"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_LABEL = u"Label"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_TYPE = u"Type"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_STYLE = u"Style"_ustr;
+constexpr OUString ITEM_DESCRIPTOR_VISIBLE = u"IsVisible"_ustr;
+
+static void ExtractToolbarParameters( const Sequence< PropertyValue >& rProp,
+ OUString& rCommandURL,
+ OUString& rLabel,
+ sal_Int16& rStyle,
+ bool& rVisible,
+ sal_Int16& rType )
+{
+ for ( const PropertyValue& rEntry : rProp )
+ {
+ if ( rEntry.Name == ITEM_DESCRIPTOR_COMMANDURL )
+ rEntry.Value >>= rCommandURL;
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_LABEL )
+ rEntry.Value >>= rLabel;
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_TYPE )
+ rEntry.Value >>= rType;
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_VISIBLE )
+ rEntry.Value >>= rVisible;
+ else if ( rEntry.Name == ITEM_DESCRIPTOR_STYLE )
+ rEntry.Value >>= rStyle;
+ }
+}
+
+namespace {
+
+struct ToolboxStyleItem
+{
+ sal_Int16 nBit;
+ OUString attrName;
+};
+
+}
+
+constexpr ToolboxStyleItem Styles[ ] = {
+ { css::ui::ItemStyle::RADIO_CHECK, ATTRIBUTE_ITEMSTYLE_RADIO },
+ { css::ui::ItemStyle::ALIGN_LEFT, ATTRIBUTE_ITEMSTYLE_LEFT },
+ { css::ui::ItemStyle::AUTO_SIZE, ATTRIBUTE_ITEMSTYLE_AUTO },
+ { css::ui::ItemStyle::REPEAT, ATTRIBUTE_ITEMSTYLE_REPEAT },
+ { css::ui::ItemStyle::DROPDOWN_ONLY, ATTRIBUTE_ITEMSTYLE_DROPDOWNONLY },
+ { css::ui::ItemStyle::DROP_DOWN, ATTRIBUTE_ITEMSTYLE_DROPDOWN },
+ { css::ui::ItemStyle::ICON, ATTRIBUTE_ITEMSTYLE_IMAGE },
+ { css::ui::ItemStyle::TEXT, ATTRIBUTE_ITEMSTYLE_TEXT },
+};
+
+sal_Int32 const nStyleItemEntries = SAL_N_ELEMENTS(Styles);
+
+namespace {
+
+struct ToolBarEntryProperty
+{
+ OReadToolBoxDocumentHandler::ToolBox_XML_Namespace nNamespace;
+ char aEntryName[20];
+};
+
+}
+
+ToolBarEntryProperty const ToolBoxEntries[OReadToolBoxDocumentHandler::TB_XML_ENTRY_COUNT] =
+{
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBAR },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARITEM },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARSPACE },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARBREAK },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ELEMENT_TOOLBARSEPARATOR },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_TEXT },
+ { OReadToolBoxDocumentHandler::TB_NS_XLINK, ATTRIBUTE_URL },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_VISIBLE },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_ITEMSTYLE },
+ { OReadToolBoxDocumentHandler::TB_NS_TOOLBAR, ATTRIBUTE_UINAME },
+};
+
+OReadToolBoxDocumentHandler::OReadToolBoxDocumentHandler( const Reference< XIndexContainer >& rItemContainer ) :
+ m_rItemContainer( rItemContainer ),
+ m_aType( ITEM_DESCRIPTOR_TYPE ),
+ m_aLabel( ITEM_DESCRIPTOR_LABEL ),
+ m_aStyle( ITEM_DESCRIPTOR_STYLE ),
+ m_aIsVisible( ITEM_DESCRIPTOR_VISIBLE ),
+ m_aCommandURL( ITEM_DESCRIPTOR_COMMANDURL )
+ {
+ // create hash map
+ for ( int i = 0; i < TB_XML_ENTRY_COUNT; i++ )
+ {
+ if ( ToolBoxEntries[i].nNamespace == TB_NS_TOOLBAR )
+ {
+ OUString temp = XMLNS_TOOLBAR XMLNS_FILTER_SEPARATOR +
+ OUString::createFromAscii( ToolBoxEntries[i].aEntryName );
+ m_aToolBoxMap.emplace( temp, static_cast<ToolBox_XML_Entry>(i) );
+ }
+ else
+ {
+ OUString temp = XMLNS_XLINK XMLNS_FILTER_SEPARATOR +
+ OUString::createFromAscii( ToolBoxEntries[i].aEntryName );
+ m_aToolBoxMap.emplace( temp, static_cast<ToolBox_XML_Entry>(i) );
+ }
+ }
+
+ m_bToolBarStartFound = false;
+ m_bToolBarItemStartFound = false;
+ m_bToolBarSpaceStartFound = false;
+ m_bToolBarBreakStartFound = false;
+ m_bToolBarSeparatorStartFound = false;
+}
+
+OReadToolBoxDocumentHandler::~OReadToolBoxDocumentHandler()
+{
+}
+
+// XDocumentHandler
+void SAL_CALL OReadToolBoxDocumentHandler::startDocument()
+{
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::endDocument()
+{
+ if ( m_bToolBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "No matching start or end element 'toolbar' found!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::startElement(
+ const OUString& aName, const Reference< XAttributeList > &xAttribs )
+{
+ ToolBoxHashMap::const_iterator pToolBoxEntry = m_aToolBoxMap.find( aName );
+ if ( pToolBoxEntry == m_aToolBoxMap.end() )
+ return;
+
+ switch ( pToolBoxEntry->second )
+ {
+ case TB_ELEMENT_TOOLBAR:
+ {
+ if ( m_bToolBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'toolbar:toolbar' cannot be embedded into 'toolbar:toolbar'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ else
+ {
+ // Check if we have a UI name set in our XML file
+ OUString aUIName;
+ for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ )
+ {
+ pToolBoxEntry = m_aToolBoxMap.find( xAttribs->getNameByIndex( n ) );
+ if ( pToolBoxEntry != m_aToolBoxMap.end() )
+ {
+ switch ( pToolBoxEntry->second )
+ {
+ case TB_ATTRIBUTE_UINAME:
+ aUIName = xAttribs->getValueByIndex( n );
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ if ( !aUIName.isEmpty() )
+ {
+ // Try to set UI name as a container property
+ Reference< XPropertySet > xPropSet( m_rItemContainer, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->setPropertyValue("UIName", Any( aUIName ) );
+ }
+ catch ( const UnknownPropertyException& )
+ {
+ }
+ }
+
+ }
+ }
+ m_bToolBarStartFound = true;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARITEM:
+ {
+ if ( !m_bToolBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element 'toolbar:toolbaritem' must be embedded into element 'toolbar:toolbar'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ if ( m_bToolBarSeparatorStartFound ||
+ m_bToolBarBreakStartFound ||
+ m_bToolBarSpaceStartFound ||
+ m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbaritem is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ bool bAttributeURL = false;
+
+ m_bToolBarItemStartFound = true;
+ OUString aLabel;
+ OUString aCommandURL;
+ sal_uInt16 nItemBits( 0 );
+ bool bVisible( true );
+
+ for ( sal_Int16 n = 0; n < xAttribs->getLength(); n++ )
+ {
+ pToolBoxEntry = m_aToolBoxMap.find( xAttribs->getNameByIndex( n ) );
+ if ( pToolBoxEntry != m_aToolBoxMap.end() )
+ {
+ switch ( pToolBoxEntry->second )
+ {
+ case TB_ATTRIBUTE_TEXT:
+ {
+ aLabel = xAttribs->getValueByIndex( n );
+ }
+ break;
+
+ case TB_ATTRIBUTE_URL:
+ {
+ bAttributeURL = true;
+ aCommandURL = xAttribs->getValueByIndex( n );
+ }
+ break;
+
+ case TB_ATTRIBUTE_VISIBLE:
+ {
+ if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_TRUE )
+ bVisible = true;
+ else if ( xAttribs->getValueByIndex( n ) == ATTRIBUTE_BOOLEAN_FALSE )
+ bVisible = false;
+ else
+ {
+ OUString aErrorMessage = getErrorLineString() + "Attribute toolbar:visible must have value 'true' or 'false'!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+ }
+ break;
+
+ case TB_ATTRIBUTE_STYLE:
+ {
+ // read space separated item style list
+ OUString aTemp = xAttribs->getValueByIndex( n );
+ sal_Int32 nIndex = 0;
+
+ do
+ {
+ OUString aToken = aTemp.getToken( 0, ' ', nIndex );
+ if ( !aToken.isEmpty() )
+ {
+ if ( aToken == ATTRIBUTE_ITEMSTYLE_RADIO )
+ nItemBits |= css::ui::ItemStyle::RADIO_CHECK;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_LEFT )
+ nItemBits |= css::ui::ItemStyle::ALIGN_LEFT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_AUTOSIZE )
+ nItemBits |= css::ui::ItemStyle::AUTO_SIZE;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_REPEAT )
+ nItemBits |= css::ui::ItemStyle::REPEAT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_DROPDOWNONLY )
+ nItemBits |= css::ui::ItemStyle::DROPDOWN_ONLY;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_DROPDOWN )
+ nItemBits |= css::ui::ItemStyle::DROP_DOWN;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_TEXT )
+ nItemBits |= css::ui::ItemStyle::TEXT;
+ else if ( aToken == ATTRIBUTE_ITEMSTYLE_IMAGE )
+ nItemBits |= css::ui::ItemStyle::ICON;
+ }
+ }
+ while ( nIndex >= 0 );
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ } // for
+
+ if ( !bAttributeURL )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Required attribute toolbar:url must have a value!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ if ( !aCommandURL.isEmpty() )
+ {
+ //fix for fdo#39370
+ /// check whether RTL interface or not
+ if(AllSettings::GetLayoutRTL()){
+ if (aCommandURL == ".uno:ParaLeftToRight")
+ aCommandURL = ".uno:ParaRightToLeft";
+ else if (aCommandURL == ".uno:ParaRightToLeft")
+ aCommandURL = ".uno:ParaLeftToRight";
+ else if (aCommandURL == ".uno:LeftPara")
+ aCommandURL = ".uno:RightPara";
+ else if (aCommandURL == ".uno:RightPara")
+ aCommandURL = ".uno:LeftPara";
+ else if (aCommandURL == ".uno:AlignLeft")
+ aCommandURL = ".uno:AlignRight";
+ else if (aCommandURL == ".uno:AlignRight")
+ aCommandURL = ".uno:AlignLeft";
+ else if (aCommandURL == ".uno:WrapLeft")
+ aCommandURL = ".uno:WrapRight";
+ else if (aCommandURL == ".uno:WrapRight")
+ aCommandURL = ".uno:WrapLeft";
+ }
+
+ auto aToolbarItemProp( comphelper::InitPropertySequence( {
+ { m_aCommandURL, css::uno::Any( aCommandURL ) },
+ { m_aLabel, css::uno::Any( aLabel ) },
+ { m_aType, css::uno::Any( css::ui::ItemType::DEFAULT ) },
+ { m_aStyle, css::uno::Any( nItemBits ) },
+ { m_aIsVisible, css::uno::Any( bVisible ) },
+ } ) );
+
+ m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) );
+ }
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARSPACE:
+ {
+ if ( m_bToolBarSeparatorStartFound ||
+ m_bToolBarBreakStartFound ||
+ m_bToolBarSpaceStartFound ||
+ m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarspace is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarSpaceStartFound = true;
+
+ Sequence< PropertyValue > aToolbarItemProp{
+ comphelper::makePropertyValue(m_aCommandURL, OUString()),
+ comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_SPACE)
+ };
+
+ m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) );
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARBREAK:
+ {
+ if ( m_bToolBarSeparatorStartFound ||
+ m_bToolBarBreakStartFound ||
+ m_bToolBarSpaceStartFound ||
+ m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarbreak is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarBreakStartFound = true;
+
+ Sequence< PropertyValue > aToolbarItemProp{
+ comphelper::makePropertyValue(m_aCommandURL, OUString()),
+ comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_LINEBREAK)
+ };
+
+ m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) );
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARSEPARATOR:
+ {
+ if ( m_bToolBarSeparatorStartFound ||
+ m_bToolBarBreakStartFound ||
+ m_bToolBarSpaceStartFound ||
+ m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "Element toolbar:toolbarseparator is not a container!";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarSeparatorStartFound = true;
+
+ Sequence< PropertyValue > aToolbarItemProp{
+ comphelper::makePropertyValue(m_aCommandURL, OUString()),
+ comphelper::makePropertyValue(m_aType, css::ui::ItemType::SEPARATOR_LINE)
+ };
+
+ m_rItemContainer->insertByIndex( m_rItemContainer->getCount(), Any( aToolbarItemProp ) );
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::endElement(const OUString& aName)
+{
+ ToolBoxHashMap::const_iterator pToolBoxEntry = m_aToolBoxMap.find( aName );
+ if ( pToolBoxEntry == m_aToolBoxMap.end() )
+ return;
+
+ switch ( pToolBoxEntry->second )
+ {
+ case TB_ELEMENT_TOOLBAR:
+ {
+ if ( !m_bToolBarStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar' found, but no start element 'toolbar'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarStartFound = false;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARITEM:
+ {
+ if ( !m_bToolBarItemStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbaritem' found, but no start element 'toolbar:toolbaritem'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarItemStartFound = false;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARBREAK:
+ {
+ if ( !m_bToolBarBreakStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarbreak' found, but no start element 'toolbar:toolbarbreak'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarBreakStartFound = false;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARSPACE:
+ {
+ if ( !m_bToolBarSpaceStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarspace' found, but no start element 'toolbar:toolbarspace'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarSpaceStartFound = false;
+ }
+ break;
+
+ case TB_ELEMENT_TOOLBARSEPARATOR:
+ {
+ if ( !m_bToolBarSeparatorStartFound )
+ {
+ OUString aErrorMessage = getErrorLineString() + "End element 'toolbar:toolbarseparator' found, but no start element 'toolbar:toolbarseparator'";
+ throw SAXException( aErrorMessage, Reference< XInterface >(), Any() );
+ }
+
+ m_bToolBarSeparatorStartFound = false;
+ }
+ break;
+
+ default: break;
+ }
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::characters(const OUString&)
+{
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::ignorableWhitespace(const OUString&)
+{
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::processingInstruction(
+ const OUString& /*aTarget*/, const OUString& /*aData*/ )
+{
+}
+
+void SAL_CALL OReadToolBoxDocumentHandler::setDocumentLocator(
+ const Reference< XLocator > &xLocator)
+{
+ m_xLocator = xLocator;
+}
+
+OUString OReadToolBoxDocumentHandler::getErrorLineString()
+{
+ if ( m_xLocator.is() )
+ return "Line: " + OUString::number( m_xLocator->getLineNumber() ) + " - ";
+ else
+ return OUString();
+}
+
+// OWriteToolBoxDocumentHandler
+
+OWriteToolBoxDocumentHandler::OWriteToolBoxDocumentHandler(
+ const Reference< XIndexAccess >& rItemAccess,
+ Reference< XDocumentHandler > const & rWriteDocumentHandler ) :
+ m_xWriteDocumentHandler( rWriteDocumentHandler ),
+ m_rItemAccess( rItemAccess )
+{
+ m_xEmptyList = new ::comphelper::AttributeList;
+ m_aXMLXlinkNS = XMLNS_XLINK_PREFIX;
+ m_aXMLToolbarNS = XMLNS_TOOLBAR_PREFIX;
+}
+
+OWriteToolBoxDocumentHandler::~OWriteToolBoxDocumentHandler()
+{
+}
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxDocument()
+{
+ m_xWriteDocumentHandler->startDocument();
+
+ // write DOCTYPE line!
+ Reference< XExtendedDocumentHandler > xExtendedDocHandler( m_xWriteDocumentHandler, UNO_QUERY );
+ if ( xExtendedDocHandler.is() )
+ {
+ xExtendedDocHandler->unknown( TOOLBAR_DOCTYPE );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ }
+
+ OUString aUIName;
+ Reference< XPropertySet > xPropSet( m_rItemAccess, UNO_QUERY );
+ if ( xPropSet.is() )
+ {
+ try
+ {
+ xPropSet->getPropertyValue("UIName") >>= aUIName;
+ }
+ catch ( const UnknownPropertyException& )
+ {
+ }
+ }
+
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_TOOLBAR,
+ XMLNS_TOOLBAR );
+
+ pList->AddAttribute( ATTRIBUTE_XMLNS_XLINK,
+ XMLNS_XLINK );
+
+ if ( !aUIName.isEmpty() )
+ pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_UINAME,
+ aUIName );
+
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBAR, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+
+ sal_Int32 nItemCount = m_rItemAccess->getCount();
+ Any aAny;
+
+ for ( sal_Int32 nItemPos = 0; nItemPos < nItemCount; nItemPos++ )
+ {
+ Sequence< PropertyValue > aProps;
+ aAny = m_rItemAccess->getByIndex( nItemPos );
+ if ( aAny >>= aProps )
+ {
+ OUString aCommandURL;
+ OUString aLabel;
+ bool bVisible( true );
+ sal_Int16 nType( css::ui::ItemType::DEFAULT );
+ sal_Int16 nStyle( 0 );
+
+ ExtractToolbarParameters( aProps, aCommandURL, aLabel, nStyle, bVisible, nType );
+ if ( nType == css::ui::ItemType::DEFAULT )
+ WriteToolBoxItem( aCommandURL, aLabel, nStyle, bVisible );
+ else if ( nType == css::ui::ItemType::SEPARATOR_SPACE )
+ WriteToolBoxSpace();
+ else if ( nType == css::ui::ItemType::SEPARATOR_LINE )
+ WriteToolBoxSeparator();
+ else if ( nType == css::ui::ItemType::SEPARATOR_LINEBREAK )
+ WriteToolBoxBreak();
+ }
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBAR );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endDocument();
+}
+
+// protected member functions
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxItem(
+ const OUString& rCommandURL,
+ const OUString& rLabel,
+ sal_Int16 nStyle,
+ bool bVisible )
+{
+ rtl::Reference<::comphelper::AttributeList> pList = new ::comphelper::AttributeList;
+
+ if ( m_aAttributeURL.isEmpty() )
+ {
+ m_aAttributeURL = m_aXMLXlinkNS + ATTRIBUTE_URL;
+ }
+
+ // save required attribute (URL)
+ pList->AddAttribute( m_aAttributeURL, rCommandURL );
+
+ if ( !rLabel.isEmpty() )
+ {
+ pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_TEXT,
+ rLabel );
+ }
+
+ if ( !bVisible )
+ {
+ pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_VISIBLE,
+ ATTRIBUTE_BOOLEAN_FALSE );
+ }
+
+ if ( nStyle > 0 )
+ {
+ OUStringBuffer aValue;
+ const ToolboxStyleItem* pStyle = Styles;
+
+ for ( sal_Int32 nIndex = 0; nIndex < nStyleItemEntries; ++nIndex, ++pStyle )
+ {
+ if ( nStyle & pStyle->nBit )
+ {
+ if ( !aValue.isEmpty() )
+ aValue.append(" ");
+ aValue.append( pStyle->attrName );
+ }
+ }
+ pList->AddAttribute( m_aXMLToolbarNS + ATTRIBUTE_ITEMSTYLE,
+ aValue.makeStringAndClear() );
+ }
+
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARITEM, pList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARITEM );
+}
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxSpace()
+{
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARSPACE, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARSPACE );
+}
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxBreak()
+{
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARBREAK, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARBREAK );
+}
+
+void OWriteToolBoxDocumentHandler::WriteToolBoxSeparator()
+{
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->startElement( ELEMENT_NS_TOOLBARSEPARATOR, m_xEmptyList );
+ m_xWriteDocumentHandler->ignorableWhitespace( OUString() );
+ m_xWriteDocumentHandler->endElement( ELEMENT_NS_TOOLBARSEPARATOR );
+}
+
+} // namespace framework
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/framework/source/fwe/xml/xmlnamespaces.cxx b/framework/source/fwe/xml/xmlnamespaces.cxx
new file mode 100644
index 0000000000..7faa6c48d8
--- /dev/null
+++ b/framework/source/fwe/xml/xmlnamespaces.cxx
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <xml/xmlnamespaces.hxx>
+
+#include <com/sun/star/xml/sax/SAXException.hpp>
+
+using namespace ::com::sun::star::xml::sax;
+using namespace ::com::sun::star::uno;
+
+namespace framework
+{
+
+void XMLNamespaces::addNamespace( const OUString& aName, const OUString& aValue )
+{
+ NamespaceMap::iterator p;
+ OUString aNamespaceName( aName );
+
+ // delete preceding "xmlns"
+ constexpr char aXMLAttributeNamespace[] = "xmlns";
+ if ( aNamespaceName.startsWith( aXMLAttributeNamespace ) )
+ {
+ constexpr sal_Int32 nXMLNamespaceLength = RTL_CONSTASCII_LENGTH(aXMLAttributeNamespace);
+ if ( aNamespaceName.getLength() == nXMLNamespaceLength )
+ {
+ aNamespaceName.clear();
+ }
+ else if ( aNamespaceName.getLength() >= nXMLNamespaceLength+2 )
+ {
+ aNamespaceName = aNamespaceName.copy( nXMLNamespaceLength+1 );
+ }
+ else
+ {
+ // a xml namespace without name is not allowed (e.g. "xmlns:" )
+ throw SAXException( "A xml namespace without name is not allowed!", Reference< XInterface >(), Any() );
+ }
+ }
+
+ if ( aValue.isEmpty() && !aNamespaceName.isEmpty() )
+ {
+ // namespace should be reset - as xml draft states this is only allowed
+ // for the default namespace - check and throw exception if check fails
+ throw SAXException( "Clearing xml namespace only allowed for default namespace!", Reference< XInterface >(), Any() );
+ }
+
+ if ( aNamespaceName.isEmpty() )
+ m_aDefaultNamespace = aValue;
+ else
+ {
+ p = m_aNamespaceMap.find( aNamespaceName );
+ if ( p != m_aNamespaceMap.end() )
+ {
+ // replace current namespace definition
+ m_aNamespaceMap.erase( p );
+ m_aNamespaceMap.emplace( aNamespaceName, aValue );
+ }
+ else
+ {
+ m_aNamespaceMap.emplace( aNamespaceName, aValue );
+ }
+ }
+}
+
+OUString XMLNamespaces::applyNSToAttributeName( const OUString& aName ) const
+{
+ // xml draft: there is no default namespace for attributes!
+
+ int index;
+ if (( index = aName.indexOf( ':' )) > 0 )
+ {
+ if ( aName.getLength() <= index+1 )
+ {
+ // attribute with namespace but without name "namespace:" is not allowed!!
+ throw SAXException( "Attribute has no name only preceding namespace!", Reference< XInterface >(), Any() );
+ }
+ OUString aAttributeName = getNamespaceValue( aName.copy( 0, index )) + "^" + aName.subView( index+1);
+ return aAttributeName;
+ }
+
+ return aName;
+}
+
+OUString XMLNamespaces::applyNSToElementName( const OUString& aName ) const
+{
+ // xml draft: element names can have a default namespace
+
+ int index = aName.indexOf( ':' );
+ OUString aNamespace;
+ OUString aElementName = aName;
+
+ if ( index > 0 )
+ aNamespace = getNamespaceValue( aName.copy( 0, index ) );
+ else
+ aNamespace = m_aDefaultNamespace;
+
+ if ( !aNamespace.isEmpty() )
+ {
+ aElementName = aNamespace + "^";
+ }
+ else
+ return aName;
+
+ if ( index > 0 )
+ {
+ if ( aName.getLength() <= index+1 )
+ {
+ // attribute with namespace but without a name is not allowed (e.g. "cfg:" )
+ throw SAXException( "Attribute has no name only preceding namespace!", Reference< XInterface >(), Any() );
+ }
+ aElementName += aName.subView( index+1 );
+ }
+ else
+ aElementName += aName;
+
+ return aElementName;
+}
+
+OUString const & XMLNamespaces::getNamespaceValue( const OUString& aNamespace ) const
+{
+ if ( aNamespace.isEmpty() )
+ return m_aDefaultNamespace;
+ else
+ {
+ NamespaceMap::const_iterator p = m_aNamespaceMap.find( aNamespace );
+ if ( p == m_aNamespaceMap.end() )
+ {
+ // namespace not defined => throw exception!
+ throw SAXException( "XML namespace used but not defined!", Reference< XInterface >(), Any() );
+ }
+ return p->second;
+ }
+}
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */