diff options
Diffstat (limited to 'eventattacher')
-rw-r--r-- | eventattacher/IwyuFilter_eventattacher.yaml | 2 | ||||
-rw-r--r-- | eventattacher/Library_evtatt.mk | 31 | ||||
-rw-r--r-- | eventattacher/Makefile | 7 | ||||
-rw-r--r-- | eventattacher/Module_eventattacher.mk | 15 | ||||
-rw-r--r-- | eventattacher/README.md | 8 | ||||
-rw-r--r-- | eventattacher/source/eventattacher.cxx | 850 | ||||
-rw-r--r-- | eventattacher/source/evtatt.component | 26 |
7 files changed, 939 insertions, 0 deletions
diff --git a/eventattacher/IwyuFilter_eventattacher.yaml b/eventattacher/IwyuFilter_eventattacher.yaml new file mode 100644 index 0000000000..2922f0b494 --- /dev/null +++ b/eventattacher/IwyuFilter_eventattacher.yaml @@ -0,0 +1,2 @@ +--- +assumeFilename: eventattacher/source/eventattacher.cxx diff --git a/eventattacher/Library_evtatt.mk b/eventattacher/Library_evtatt.mk new file mode 100644 index 0000000000..969c2b4b71 --- /dev/null +++ b/eventattacher/Library_evtatt.mk @@ -0,0 +1,31 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# +# 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/. +# +# + +$(eval $(call gb_Library_Library,evtatt)) + +$(eval $(call gb_Library_set_componentfile,evtatt,eventattacher/source/evtatt,services)) + +$(eval $(call gb_Library_use_libraries,evtatt,\ + comphelper \ + cppuhelper \ + cppu \ + sal \ +)) + +$(eval $(call gb_Library_add_exception_objects,evtatt,\ + eventattacher/source/eventattacher \ +)) + +$(eval $(call gb_Library_use_api,evtatt,\ + udkapi \ +)) + +# vim: set ts=4 sw=4 et: diff --git a/eventattacher/Makefile b/eventattacher/Makefile new file mode 100644 index 0000000000..ccb1c85a04 --- /dev/null +++ b/eventattacher/Makefile @@ -0,0 +1,7 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- + +module_directory:=$(dir $(realpath $(firstword $(MAKEFILE_LIST)))) + +include $(module_directory)/../solenv/gbuild/partial_build.mk + +# vim: set noet sw=4 ts=4: diff --git a/eventattacher/Module_eventattacher.mk b/eventattacher/Module_eventattacher.mk new file mode 100644 index 0000000000..3821ff864c --- /dev/null +++ b/eventattacher/Module_eventattacher.mk @@ -0,0 +1,15 @@ +# +# +# 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/. +# +# + +$(eval $(call gb_Module_Module,eventattacher)) + +$(eval $(call gb_Module_add_targets,eventattacher,\ + Library_evtatt \ +)) diff --git a/eventattacher/README.md b/eventattacher/README.md new file mode 100644 index 0000000000..75cc782fe6 --- /dev/null +++ b/eventattacher/README.md @@ -0,0 +1,8 @@ +# Event Attacher + +This is how StarBasic handles events. + +## See Also + +- <https://wiki.documentfoundation.org/Documentation/DevGuide/Forms#Programmatic_Assignment_of_Scripts_to_Events> +- <https://wiki.documentfoundation.org/Documentation/DevGuide/Forms#Scripting_and_Events> diff --git a/eventattacher/source/eventattacher.cxx b/eventattacher/source/eventattacher.cxx new file mode 100644 index 0000000000..d4ffa6be3e --- /dev/null +++ b/eventattacher/source/eventattacher.cxx @@ -0,0 +1,850 @@ +/* -*- 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/lang/ServiceNotRegisteredException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/beans/IntrospectionException.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/CannotCreateAdapterException.hpp> +#include <com/sun/star/script/XEventAttacher2.hpp> +#include <com/sun/star/script/Converter.hpp> +#include <com/sun/star/script/XAllListener.hpp> +#include <com/sun/star/script/InvocationAdapterFactory.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> + +// InvocationToAllListenerMapper +#include <com/sun/star/script/XInvocation.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <mutex> +#include <utility> + +namespace com::sun::star::lang { class XMultiServiceFactory; } + +using namespace com::sun::star::uno; +using namespace com::sun::star::registry; +using namespace com::sun::star::lang; +using namespace com::sun::star::beans; +using namespace com::sun::star::script; +using namespace com::sun::star::reflection; +using namespace cppu; +using namespace osl; + + +namespace comp_EventAttacher { + + +// class InvocationToAllListenerMapper +// helper class to map XInvocation to XAllListener + +namespace { + +class InvocationToAllListenerMapper : public WeakImplHelper< XInvocation > +{ +public: + InvocationToAllListenerMapper( const Reference< XIdlClass >& ListenerType, + const Reference< XAllListener >& AllListener, Any Helper ); + + // XInvocation + virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection() override; + virtual Any SAL_CALL invoke(const OUString& FunctionName, const Sequence< Any >& Params, Sequence< sal_Int16 >& OutParamIndex, Sequence< Any >& OutParam) override; + virtual void SAL_CALL setValue(const OUString& PropertyName, const Any& Value) override; + virtual Any SAL_CALL getValue(const OUString& PropertyName) override; + virtual sal_Bool SAL_CALL hasMethod(const OUString& Name) override; + virtual sal_Bool SAL_CALL hasProperty(const OUString& Name) override; + +private: + Reference< XAllListener > m_xAllListener; + Reference< XIdlClass > m_xListenerType; + Any m_Helper; +}; + +} + +// Function to replace AllListenerAdapterService::createAllListerAdapter +static Reference< XInterface > createAllListenerAdapter +( + const Reference< XInvocationAdapterFactory2 >& xInvocationAdapterFactory, + const Reference< XIdlClass >& xListenerType, + const Reference< XAllListener >& xListener, + const Any& Helper +) +{ + Reference< XInterface > xAdapter; + if( xInvocationAdapterFactory.is() && xListenerType.is() && xListener.is() ) + { + Reference< XInvocation > xInvocationToAllListenerMapper = + new InvocationToAllListenerMapper(xListenerType, xListener, Helper); + Type aListenerType( xListenerType->getTypeClass(), xListenerType->getName()); + Sequence<Type> arg2 { aListenerType }; + xAdapter = xInvocationAdapterFactory->createAdapter( xInvocationToAllListenerMapper, arg2 ); + } + return xAdapter; +} + + +// InvocationToAllListenerMapper +InvocationToAllListenerMapper::InvocationToAllListenerMapper + ( const Reference< XIdlClass >& ListenerType, const Reference< XAllListener >& AllListener, Any Helper ) + : m_xAllListener( AllListener ) + , m_xListenerType( ListenerType ) + , m_Helper(std::move( Helper )) +{ +} + + +Reference< XIntrospectionAccess > SAL_CALL InvocationToAllListenerMapper::getIntrospection() +{ + return Reference< XIntrospectionAccess >(); +} + + +Any SAL_CALL InvocationToAllListenerMapper::invoke(const OUString& FunctionName, const Sequence< Any >& Params, + Sequence< sal_Int16 >& , Sequence< Any >& ) +{ + Any aRet; + + // Check if to firing or approveFiring has to be called + Reference< XIdlMethod > xMethod = m_xListenerType->getMethod( FunctionName ); + bool bApproveFiring = false; + if( !xMethod.is() ) + return aRet; + Reference< XIdlClass > xReturnType = xMethod->getReturnType(); + Sequence< Reference< XIdlClass > > aExceptionSeq = xMethod->getExceptionTypes(); + if( ( xReturnType.is() && xReturnType->getTypeClass() != TypeClass_VOID ) || + aExceptionSeq.hasElements() ) + { + bApproveFiring = true; + } + else + { + Sequence< ParamInfo > aParamSeq = xMethod->getParameterInfos(); + sal_uInt32 nParamCount = aParamSeq.getLength(); + if( nParamCount > 1 ) + { + const ParamInfo* pInfo = aParamSeq.getConstArray(); + for( sal_uInt32 i = 0 ; i < nParamCount ; i++ ) + { + if( pInfo[ i ].aMode != ParamMode_IN ) + { + bApproveFiring = true; + break; + } + } + } + } + + AllEventObject aAllEvent; + aAllEvent.Source = getXWeak(); + aAllEvent.Helper = m_Helper; + aAllEvent.ListenerType = Type(m_xListenerType->getTypeClass(), m_xListenerType->getName()); + aAllEvent.MethodName = FunctionName; + aAllEvent.Arguments = Params; + if( bApproveFiring ) + aRet = m_xAllListener->approveFiring( aAllEvent ); + else + m_xAllListener->firing( aAllEvent ); + return aRet; +} + + +void SAL_CALL InvocationToAllListenerMapper::setValue(const OUString& , const Any& ) +{ +} + + +Any SAL_CALL InvocationToAllListenerMapper::getValue(const OUString& ) +{ + return Any(); +} + + +sal_Bool SAL_CALL InvocationToAllListenerMapper::hasMethod(const OUString& Name) +{ + Reference< XIdlMethod > xMethod = m_xListenerType->getMethod( Name ); + return xMethod.is(); +} + + +sal_Bool SAL_CALL InvocationToAllListenerMapper::hasProperty(const OUString& Name) +{ + Reference< XIdlField > xField = m_xListenerType->getField( Name ); + return xField.is(); +} + + +// class EventAttacherImpl +// represents an implementation of the EventAttacher service + +namespace { + +class EventAttacherImpl : public WeakImplHelper < XEventAttacher2, XInitialization, XServiceInfo > +{ +public: + explicit EventAttacherImpl( const Reference< XComponentContext >& ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName( ) override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; + + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + + // methods of XEventAttacher + virtual Reference< XEventListener > SAL_CALL attachListener(const Reference< XInterface >& xObject, + const Reference< XAllListener >& AllListener, const Any& Helper, + const OUString& ListenerType, const OUString& AddListenerParam) override; + virtual Reference< XEventListener > SAL_CALL attachSingleEventListener(const Reference< XInterface >& xObject, + const Reference< XAllListener >& AllListener, const Any& Helper, + const OUString& ListenerType, const OUString& AddListenerParam, + const OUString& EventMethod) override; + virtual void SAL_CALL removeListener(const Reference< XInterface >& xObject, + const OUString& ListenerType, const OUString& AddListenerParam, + const Reference< XEventListener >& aToRemoveListener) override; + + // XEventAttacher2 + virtual Sequence< Reference<XEventListener> > SAL_CALL attachMultipleEventListeners( + const Reference<XInterface>& xObject, const Sequence<css::script::EventListener>& aListeners ) override; + + // used by FilterAllListener_Impl + /// @throws Exception + Reference< XTypeConverter > getConverter(); + + friend class FilterAllListenerImpl; + +private: + static Reference<XEventListener> attachListenerForTarget( + const Reference<XIntrospectionAccess>& xAccess, + const Reference<XInvocationAdapterFactory2>& xInvocationAdapterFactory, + const Reference<XAllListener>& xAllListener, + const Any& aObject, + const Any& aHelper, + const OUString& aListenerType, + const OUString& aAddListenerParam ); + + Sequence< Reference<XEventListener> > attachListeners( + const Reference<XInterface>& xObject, + const Sequence< Reference<XAllListener> >& AllListeners, + const Sequence<css::script::EventListener>& aListeners ); + +private: + std::mutex m_aMutex; + Reference< XComponentContext > m_xContext; + + // Save Services + Reference< XIntrospection > m_xIntrospection; + Reference< XIdlReflection > m_xReflection; + Reference< XTypeConverter > m_xConverter; + Reference< XInvocationAdapterFactory2 > m_xInvocationAdapterFactory; + + // needed services + /// @throws Exception + Reference< XIntrospection > getIntrospection(); + /// @throws Exception + Reference< XIdlReflection > getReflection(); + /// @throws Exception + Reference< XInvocationAdapterFactory2 > getInvocationAdapterService(); +}; + +} + +EventAttacherImpl::EventAttacherImpl( const Reference< XComponentContext >& rxContext ) + : m_xContext( rxContext ) +{ +} + +/// @throws Exception +OUString SAL_CALL EventAttacherImpl::getImplementationName( ) +{ + return "com.sun.star.comp.EventAttacher"; +} + +sal_Bool SAL_CALL EventAttacherImpl::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL EventAttacherImpl::getSupportedServiceNames( ) +{ + return { "com.sun.star.script.EventAttacher" }; +} + +void SAL_CALL EventAttacherImpl::initialize(const Sequence< Any >& Arguments) +{ + // get services from the argument list + for( const Any& arg : Arguments ) + { + if( arg.getValueType().getTypeClass() != TypeClass_INTERFACE ) + throw IllegalArgumentException(); + + // InvocationAdapter service ? + Reference< XInvocationAdapterFactory2 > xALAS; + arg >>= xALAS; + if( xALAS.is() ) + { + std::scoped_lock aGuard( m_aMutex ); + m_xInvocationAdapterFactory = xALAS; + } + // Introspection service ? + Reference< XIntrospection > xI; + arg >>= xI; + if( xI.is() ) + { + std::scoped_lock aGuard( m_aMutex ); + m_xIntrospection = xI; + } + // Reflection service ? + Reference< XIdlReflection > xIdlR; + arg >>= xIdlR; + if( xIdlR.is() ) + { + std::scoped_lock aGuard( m_aMutex ); + m_xReflection = xIdlR; + } + // Converter Service ? + Reference< XTypeConverter > xC; + arg >>= xC; + if( xC.is() ) + { + std::scoped_lock aGuard( m_aMutex ); + m_xConverter = xC; + } + + // no right interface + if( !xALAS.is() && !xI.is() && !xIdlR.is() && !xC.is() ) + throw IllegalArgumentException(); + } +} + + +//*** Private helper methods *** +Reference< XIntrospection > EventAttacherImpl::getIntrospection() +{ + std::scoped_lock aGuard( m_aMutex ); + if( !m_xIntrospection.is() ) + { + m_xIntrospection = theIntrospection::get( m_xContext ); + } + return m_xIntrospection; +} + + +//*** Private helper methods *** +Reference< XIdlReflection > EventAttacherImpl::getReflection() +{ + std::scoped_lock aGuard( m_aMutex ); + if( !m_xReflection.is() ) + { + m_xReflection = theCoreReflection::get(m_xContext); + } + return m_xReflection; +} + + +//*** Private helper methods *** +Reference< XInvocationAdapterFactory2 > EventAttacherImpl::getInvocationAdapterService() +{ + std::scoped_lock aGuard( m_aMutex ); + if( !m_xInvocationAdapterFactory.is() ) + { + m_xInvocationAdapterFactory = InvocationAdapterFactory::create(m_xContext); + } + return m_xInvocationAdapterFactory; +} + + +//*** Private helper methods *** +Reference< XTypeConverter > EventAttacherImpl::getConverter() +{ + std::scoped_lock aGuard( m_aMutex ); + if( !m_xConverter.is() ) + { + m_xConverter = Converter::create(m_xContext); + } + return m_xConverter; +} + +namespace { + +// Implementation of an EventAttacher-related AllListeners, which brings +// a few Events to a general AllListener +class FilterAllListenerImpl : public WeakImplHelper< XAllListener > +{ +public: + FilterAllListenerImpl( EventAttacherImpl * pEA_, OUString EventMethod_, + const Reference< XAllListener >& AllListener_ ); + + // XAllListener + virtual void SAL_CALL firing(const AllEventObject& Event) override; + virtual Any SAL_CALL approveFiring(const AllEventObject& Event) override; + + // XEventListener + virtual void SAL_CALL disposing(const EventObject& Source) override; + +private: + // convert + /// @throws CannotConvertException + /// @throws RuntimeException + void convertToEventReturn( Any & rRet, const Type& rRetType ); + + EventAttacherImpl * m_pEA; + OUString m_EventMethod; + Reference< XAllListener > m_AllListener; +}; + +} + +FilterAllListenerImpl::FilterAllListenerImpl( EventAttacherImpl * pEA_, OUString EventMethod_, + const Reference< XAllListener >& AllListener_ ) + : m_pEA( pEA_ ) + , m_EventMethod(std::move( EventMethod_ )) + , m_AllListener( AllListener_ ) +{ +} + + +void SAL_CALL FilterAllListenerImpl::firing(const AllEventObject& Event) +{ + if( Event.MethodName == m_EventMethod && m_AllListener.is() ) + m_AllListener->firing( Event ); +} + +// Convert to the standard event return +void FilterAllListenerImpl::convertToEventReturn( Any & rRet, const Type & rRetType ) +{ + // no return value? Set to the specified values + if( rRet.getValueType().getTypeClass() == TypeClass_VOID ) + { + switch( rRetType.getTypeClass() ) + { + case TypeClass_INTERFACE: + { + rRet <<= Reference< XInterface >(); + } + break; + + case TypeClass_BOOLEAN: + rRet <<= true; + break; + + case TypeClass_STRING: + rRet <<= OUString(); + break; + + case TypeClass_FLOAT: rRet <<= float(0); break; + case TypeClass_DOUBLE: rRet <<= 0.0; break; + case TypeClass_BYTE: rRet <<= sal_uInt8( 0 ); break; + case TypeClass_SHORT: rRet <<= sal_Int16( 0 ); break; + case TypeClass_LONG: rRet <<= sal_Int32( 0 ); break; + case TypeClass_UNSIGNED_SHORT: rRet <<= sal_uInt16( 0 ); break; + case TypeClass_UNSIGNED_LONG: rRet <<= sal_uInt32( 0 ); break; + default: + break; + } + } + else if( !rRet.getValueType().equals( rRetType ) ) + { + Reference< XTypeConverter > xConverter = m_pEA->getConverter(); + if( !xConverter.is() ) + throw CannotConvertException(); // TODO TypeConversionException + rRet = xConverter->convertTo( rRet, rRetType ); + } +} + + +Any SAL_CALL FilterAllListenerImpl::approveFiring( const AllEventObject& Event ) +{ + Any aRet; + + if( Event.MethodName == m_EventMethod && m_AllListener.is() ) + aRet = m_AllListener->approveFiring( Event ); + else + { + // Convert to the standard event return + try + { + Reference< XIdlClass > xListenerType = m_pEA->getReflection()-> + forName( Event.ListenerType.getTypeName() ); + Reference< XIdlMethod > xMeth = xListenerType->getMethod( Event.MethodName ); + if( xMeth.is() ) + { + Reference< XIdlClass > xRetType = xMeth->getReturnType(); + Type aRetType( xRetType->getTypeClass(), xRetType->getName() ); + convertToEventReturn( aRet, aRetType ); + } + } + catch( const CannotConvertException& ) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw InvocationTargetException( OUString(), Reference< XInterface >(), anyEx ); + } + } + return aRet; +} + + +void FilterAllListenerImpl::disposing(const EventObject& ) +{ + // TODO: ??? +} + + +Reference< XEventListener > EventAttacherImpl::attachListener +( + const Reference< XInterface >& xObject, + const Reference< XAllListener >& AllListener, + const Any& Helper, + const OUString& ListenerType, + const OUString& AddListenerParam +) +{ + if( !xObject.is() || !AllListener.is() ) + throw IllegalArgumentException(); + + Reference< XInvocationAdapterFactory2 > xInvocationAdapterFactory = getInvocationAdapterService(); + if( !xInvocationAdapterFactory.is() ) + throw ServiceNotRegisteredException(); + + Reference< XIdlReflection > xReflection = getReflection(); + if( !xReflection.is() ) + throw ServiceNotRegisteredException(); + + // Sign in, Call the fitting addListener method + // First Introspection, as the Methods can be analyzed in the same way + // For better performance it is implemented here again or make the Impl-Method + // of the Introspection configurable for this purpose. + Reference< XIntrospection > xIntrospection = getIntrospection(); + if( !xIntrospection.is() ) + return Reference<XEventListener>(); + + // Inspect Introspection + Any aObjAny( &xObject, cppu::UnoType<XInterface>::get()); + + Reference< XIntrospectionAccess > xAccess = xIntrospection->inspect( aObjAny ); + if( !xAccess.is() ) + return Reference<XEventListener>(); + + return attachListenerForTarget( + xAccess, xInvocationAdapterFactory, AllListener, aObjAny, Helper, + ListenerType, AddListenerParam); +} + +Reference<XEventListener> EventAttacherImpl::attachListenerForTarget( + const Reference<XIntrospectionAccess>& xAccess, + const Reference<XInvocationAdapterFactory2>& xInvocationAdapterFactory, + const Reference<XAllListener>& xAllListener, + const Any& aObject, + const Any& aHelper, + const OUString& aListenerType, + const OUString& aAddListenerParam) +{ + Reference< XEventListener > xRet; + + // Construct the name of the addListener-Method. + sal_Int32 nIndex = aListenerType.lastIndexOf('.'); + // set index to the interface name without package name + if( nIndex == -1 ) + // not found + nIndex = 0; + else + nIndex++; + + OUString aListenerName = (!aListenerType.isEmpty() && aListenerType[nIndex] == 'X') ? aListenerType.copy(nIndex+1) : aListenerType; + OUString aAddListenerName = "add" + aListenerName; + + // Send Methods to the correct addListener-Method + const Sequence< Reference< XIdlMethod > > aMethodSeq = xAccess->getMethods( MethodConcept::LISTENER ); + for (const Reference< XIdlMethod >& rxMethod : aMethodSeq) + { + // Is it the correct method? + OUString aMethName = rxMethod->getName(); + + if (aAddListenerName != aMethName) + continue; + + Sequence< Reference< XIdlClass > > params = rxMethod->getParameterTypes(); + sal_uInt32 nParamCount = params.getLength(); + + Reference< XIdlClass > xListenerType; + if( nParamCount == 1 ) + xListenerType = params.getConstArray()[0]; + else if( nParamCount == 2 ) + xListenerType = params.getConstArray()[1]; + + // Request Adapter for the actual Listener type + Reference< XInterface > xAdapter = createAllListenerAdapter( + xInvocationAdapterFactory, xListenerType, xAllListener, aHelper ); + + if( !xAdapter.is() ) + throw CannotCreateAdapterException(); + xRet.set( xAdapter, UNO_QUERY ); + + // Just the Listener as parameter? + if( nParamCount == 1 ) + { + Sequence< Any > args( 1 ); + args.getArray()[0] <<= xAdapter; + try + { + rxMethod->invoke( aObject, args ); + } + catch( const InvocationTargetException& ) + { + throw IntrospectionException(); + } + } + // Else, pass the other parameter now + else if( nParamCount == 2 ) + { + Sequence< Any > args( 2 ); + Any* pAnys = args.getArray(); + + // Check the type of the 1st parameter + Reference< XIdlClass > xParamClass = params.getConstArray()[0]; + if( xParamClass->getTypeClass() == TypeClass_STRING ) + { + pAnys[0] <<= aAddListenerParam; + } + + // 2nd Parameter == Listener? TODO: Test! + pAnys[1] <<= xAdapter; + + // TODO: Convert String -> ? + // else + try + { + rxMethod->invoke( aObject, args ); + } + catch( const InvocationTargetException& ) + { + throw IntrospectionException(); + } + } + break; + // else... + // Anything else is not supported + } + + return xRet; +} + +Sequence< Reference<XEventListener> > EventAttacherImpl::attachListeners( + const Reference<XInterface>& xObject, + const Sequence< Reference<XAllListener> >& AllListeners, + const Sequence<css::script::EventListener>& aListeners ) +{ + sal_Int32 nCount = aListeners.getLength(); + if (nCount != AllListeners.getLength()) + // This is a prerequisite! + throw RuntimeException(); + + if (!xObject.is()) + throw IllegalArgumentException(); + + Reference< XInvocationAdapterFactory2 > xInvocationAdapterFactory = getInvocationAdapterService(); + if( !xInvocationAdapterFactory.is() ) + throw ServiceNotRegisteredException(); + + Reference< XIdlReflection > xReflection = getReflection(); + if( !xReflection.is() ) + throw ServiceNotRegisteredException(); + + // Sign in, Call the fitting addListener method + // First Introspection, as the Methods can be analyzed in the same way + // For better performance it is implemented here again or make the Impl-Method + // of the Introspection configurable for this purpose. + Reference< XIntrospection > xIntrospection = getIntrospection(); + if( !xIntrospection.is() ) + return Sequence< Reference<XEventListener> >(); + + // Inspect Introspection + Any aObjAny( &xObject, cppu::UnoType<XInterface>::get() ); + + Reference<XIntrospectionAccess> xAccess = xIntrospection->inspect(aObjAny); + if (!xAccess.is()) + return Sequence< Reference<XEventListener> >(); + + Sequence< Reference<XEventListener> > aRet(nCount); + Reference<XEventListener>* pArray = aRet.getArray(); + + for (sal_Int32 i = 0; i < nCount; ++i) + { + pArray[i] = attachListenerForTarget( + xAccess, xInvocationAdapterFactory, AllListeners[ i ], + aObjAny, aListeners[i].Helper, aListeners[i].ListenerType, aListeners[i].AddListenerParam); + } + + return aRet; +} + +// XEventAttacher +Reference< XEventListener > EventAttacherImpl::attachSingleEventListener +( + const Reference< XInterface >& xObject, + const Reference< XAllListener >& AllListener, + const Any& Helper, + const OUString& ListenerType, + const OUString& AddListenerParam, + const OUString& EventMethod +) +{ + // Subscribe FilterListener + Reference<XAllListener> aFilterListener + = new FilterAllListenerImpl(this, EventMethod, AllListener); + return attachListener( xObject, aFilterListener, Helper, ListenerType, AddListenerParam); +} + +// XEventAttacher +void EventAttacherImpl::removeListener +( + const Reference< XInterface >& xObject, + const OUString& ListenerType, + const OUString& AddListenerParam, + const Reference< XEventListener >& aToRemoveListener +) +{ + if( !xObject.is() || !aToRemoveListener.is() ) + throw IllegalArgumentException(); + + Reference< XIdlReflection > xReflection = getReflection(); + if( !xReflection.is() ) + throw IntrospectionException(); + + // Sign off, Call the fitting removeListener method + // First Introspection, as the Methods can be analyzed in the same way + // For better performance it is implemented here again or make the Impl-Method + // of the Introspection configurable for this purpose. + Reference< XIntrospection > xIntrospection = getIntrospection(); + if( !xIntrospection.is() ) + throw IntrospectionException(); + + //Inspect Introspection + Any aObjAny( &xObject, cppu::UnoType<XInterface>::get()); + Reference< XIntrospectionAccess > xAccess = xIntrospection->inspect( aObjAny ); + if( !xAccess.is() ) + throw IntrospectionException(); + + // Create name of the removeListener-Method + OUString aRemoveListenerName; + OUString aListenerName( ListenerType ); + sal_Int32 nIndex = aListenerName.lastIndexOf( '.' ); + // set index to the interface name without package name + if( nIndex == -1 ) + // not found + nIndex = 0; + else + nIndex++; + if( aListenerName[nIndex] == 'X' ) + // erase X from the interface name + aListenerName = aListenerName.copy( nIndex +1 ); + aRemoveListenerName = "remove" + aListenerName; + + // Search methods for the correct removeListener method + Sequence< Reference< XIdlMethod > > aMethodSeq = xAccess->getMethods( MethodConcept::LISTENER ); + sal_uInt32 i, nLen = aMethodSeq.getLength(); + const Reference< XIdlMethod >* pMethods = aMethodSeq.getConstArray(); + for( i = 0 ; i < nLen ; i++ ) + { + // Call Method + const Reference< XIdlMethod >& rxMethod = pMethods[i]; + + // Is it the right method? + if( aRemoveListenerName == rxMethod->getName() ) + { + Sequence< Reference< XIdlClass > > params = rxMethod->getParameterTypes(); + sal_uInt32 nParamCount = params.getLength(); + + // Just the Listener as parameter? + if( nParamCount == 1 ) + { + Sequence< Any > args( 1 ); + args.getArray()[0] <<= aToRemoveListener; + try + { + rxMethod->invoke( aObjAny, args ); + } + catch( const InvocationTargetException& ) + { + throw IntrospectionException(); + } + } + // Else pass the other parameter + else if( nParamCount == 2 ) + { + Sequence< Any > args( 2 ); + Any* pAnys = args.getArray(); + + // Check the type of the 1st parameter + Reference< XIdlClass > xParamClass = params.getConstArray()[0]; + if( xParamClass->getTypeClass() == TypeClass_STRING ) + pAnys[0] <<= AddListenerParam; + + // 2nd parameter == Listener? TODO: Test! + pAnys[1] <<= aToRemoveListener; + + // TODO: Convert String -> ? + // else + try + { + rxMethod->invoke( aObjAny, args ); + } + catch( const InvocationTargetException& ) + { + throw IntrospectionException(); + } + } + break; + } + } +} + +Sequence< Reference<XEventListener> > EventAttacherImpl::attachMultipleEventListeners( + const Reference<XInterface>& xObject, const Sequence<css::script::EventListener>& aListeners ) +{ + sal_Int32 nCount = aListeners.getLength(); + Sequence< Reference<XAllListener> > aFilterListeners(nCount); + auto aFilterListenersRange = asNonConstRange(aFilterListeners); + for (sal_Int32 i = 0; i < nCount; ++i) + { + aFilterListenersRange[i] + = new FilterAllListenerImpl(this, aListeners[i].EventMethod, aListeners[i].AllListener); + } + + return attachListeners(xObject, aFilterListeners, aListeners); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +eventattacher_EventAttacher( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& ) +{ + return cppu::acquire(new comp_EventAttacher::EventAttacherImpl(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/eventattacher/source/evtatt.component b/eventattacher/source/evtatt.component new file mode 100644 index 0000000000..8d51ca1dee --- /dev/null +++ b/eventattacher/source/evtatt.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.EventAttacher" + constructor="eventattacher_EventAttacher" single-instance="true"> + <service name="com.sun.star.script.EventAttacher"/> + </implementation> +</component> |