diff options
Diffstat (limited to '')
-rw-r--r-- | scripting/source/vbaevents/eventhelper.cxx | 982 | ||||
-rw-r--r-- | scripting/source/vbaevents/vbaevents.component | 30 |
2 files changed, 1012 insertions, 0 deletions
diff --git a/scripting/source/vbaevents/eventhelper.cxx b/scripting/source/vbaevents/eventhelper.cxx new file mode 100644 index 000000000..9da09b734 --- /dev/null +++ b/scripting/source/vbaevents/eventhelper.cxx @@ -0,0 +1,982 @@ +/* -*- 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 <sal/log.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/uno3.hxx> +#include <comphelper/proparrhlp.hxx> +#include <comphelper/propertycontainer.hxx> +#include <tools/diagnose_ex.h> + +#include <ooo/vba/XVBAToOOEventDescGen.hpp> + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> + +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> + +#include <com/sun/star/util/XCloseListener.hpp> +#include <com/sun/star/util/XCloseBroadcaster.hpp> + +#include <com/sun/star/frame/XModel.hpp> + +#include <com/sun/star/script/ScriptEventDescriptor.hpp> +#include <com/sun/star/script/provider/XScriptProviderSupplier.hpp> + +#include <com/sun/star/container/XNamed.hpp> + +#include <com/sun/star/drawing/XControlShape.hpp> + +#include <com/sun/star/awt/XControl.hpp> +#include <com/sun/star/awt/XDialog.hpp> +#include <com/sun/star/awt/KeyEvent.hpp> +#include <com/sun/star/awt/MouseEvent.hpp> +#include <com/sun/star/awt/XFixedText.hpp> +#include <com/sun/star/awt/XTextComponent.hpp> +#include <com/sun/star/awt/XComboBox.hpp> +#include <com/sun/star/awt/XRadioButton.hpp> +#include <com/sun/star/awt/XListBox.hpp> + +#include <sfx2/objsh.hxx> +#include <basic/basmgr.hxx> +#include <filter/msfilter/msvbahelper.hxx> +#include <vbahelper/vbareturntypes.hxx> + +#include <com/sun/star/script/XScriptListener.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/evtmethodhelper.hxx> + +#include <vector> +#include <unordered_map> + +using namespace ::com::sun::star; +using namespace ::com::sun::star::script; +using namespace ::com::sun::star::uno; +using namespace ::ooo::vba; + +// Some constants +constexpr std::u16string_view DELIM = u"::"; +constexpr sal_Int32 DELIMLEN = DELIM.size(); + +static bool isKeyEventOk( awt::KeyEvent& evt, const Sequence< Any >& params ) +{ + return params.hasElements() && ( params[ 0 ] >>= evt ); +} + +static bool isMouseEventOk( awt::MouseEvent& evt, const Sequence< Any >& params ) +{ + return params.hasElements() && ( params[ 0 ] >>= evt ); +} + +static Sequence< Any > ooMouseEvtToVBADblClick( const Sequence< Any >& params ) +{ + awt::MouseEvent evt; + + if ( !( isMouseEventOk(evt, params)) || + (evt.ClickCount != 2) ) + return Sequence< Any >(); + // give back orig params, this will signal that the event is good + return params; +} + +static Sequence< Any > ooMouseEvtToVBAMouseEvt( const Sequence< Any >& params ) +{ + awt::MouseEvent evt; + + if ( !isMouseEventOk(evt, params) ) + return Sequence< Any >(); + + Sequence< Any > translatedParams{ Any(evt.Buttons), // Buttons + Any(evt.Modifiers), // Shift + Any(evt.X), // X + Any(evt.Y) }; // Y + return translatedParams; +} + +static Sequence< Any > ooKeyPressedToVBAKeyPressed( const Sequence< Any >& params ) +{ + awt::KeyEvent evt; + + if ( !isKeyEventOk( evt, params ) ) + return Sequence< Any >(); + + Reference< msforms::XReturnInteger> xKeyCode = new ReturnInteger( sal_Int32( evt.KeyCode ) ); + Sequence< Any > translatedParams{ Any(xKeyCode) }; + return translatedParams; +} + +static Sequence< Any > ooKeyPressedToVBAKeyUpDown( const Sequence< Any >& params ) +{ + awt::KeyEvent evt; + + if ( !isKeyEventOk( evt, params ) ) + return Sequence< Any >(); + + Reference< msforms::XReturnInteger> xKeyCode = new ReturnInteger( evt.KeyCode ); + sal_Int8 shift = sal::static_int_cast<sal_Int8>( evt.Modifiers ); + + // #TODO check whether values from OOO conform to values generated from vba + Sequence< Any > translatedParams{ Any(xKeyCode), Any(shift) }; + return translatedParams; +} + +typedef Sequence< Any > (*Translator)(const Sequence< Any >&); + +namespace { + +//expand the "TranslateInfo" struct to support more kinds of events +struct TranslateInfo +{ + OUString sVBAName; //vba event name + Translator toVBA; //the method to convert OO event parameters to VBA event parameters + bool (*ApproveRule)(const ScriptEvent& evt, void const * pPara); //this method is used to determine which types of controls should execute the event + void const *pPara; //Parameters for the above approve method +}; + +} + +typedef std::unordered_map< + OUString, + std::vector< TranslateInfo > > EventInfoHash; + +namespace { + +struct TranslatePropMap +{ + OUString sEventInfo; //OO event name + TranslateInfo aTransInfo; +}; + +} + +static bool ApproveAll(const ScriptEvent& evt, void const * pPara); //allow all types of controls to execute the event +static bool ApproveType(const ScriptEvent& evt, void const * pPara); //certain types of controls should execute the event, those types are given by pPara +static bool DenyType(const ScriptEvent& evt, void const * pPara); //certain types of controls should not execute the event, those types are given by pPara +static bool DenyMouseDrag(const ScriptEvent& evt, void const * pPara); //used for VBA MouseMove event when "Shift" key is pressed + +namespace { + +struct TypeList +{ + uno::Type const * pTypeList; + int nListLength; +}; + +} + +Type const typeXFixedText = cppu::UnoType<awt::XFixedText>::get(); +Type const typeXTextComponent = cppu::UnoType<awt::XTextComponent>::get(); +Type const typeXComboBox = cppu::UnoType<awt::XComboBox>::get(); +Type const typeXRadioButton = cppu::UnoType<awt::XRadioButton>::get(); +Type const typeXListBox = cppu::UnoType<awt::XListBox>::get(); + + +TypeList const fixedTextList = {&typeXFixedText, 1}; +TypeList const textCompList = {&typeXTextComponent, 1}; +TypeList const radioButtonList = {&typeXRadioButton, 1}; +TypeList const comboBoxList = {&typeXComboBox, 1}; +TypeList const listBoxList = {&typeXListBox, 1}; + +//this array stores the OO event to VBA event translation info +static TranslatePropMap aTranslatePropMap_Impl[] = +{ + { OUString("actionPerformed"), { OUString("_Change"), nullptr, DenyType, static_cast<void const *>(&radioButtonList) } }, + // actionPerformed ooo event + { OUString("actionPerformed"), { OUString("_Click"), nullptr, ApproveAll, nullptr } }, + { OUString("itemStateChanged"), { OUString("_Change"), nullptr, ApproveType, static_cast<void const *>(&radioButtonList) } }, + // itemStateChanged ooo event + { OUString("itemStateChanged"), { OUString("_Click"), nullptr, ApproveType, static_cast<void const *>(&comboBoxList) } }, + + { OUString("itemStateChanged"), { OUString("_Click"), nullptr, ApproveType, static_cast<void const *>(&listBoxList) } }, + // changed ooo event + { OUString("changed"), { OUString("_Change"), nullptr, ApproveAll, nullptr } }, + + // focusGained ooo event + { OUString("focusGained"), { OUString("_GotFocus"), nullptr, ApproveAll, nullptr } }, + + // focusLost ooo event + { OUString("focusLost"), { OUString("_LostFocus"), nullptr, ApproveAll, nullptr } }, + { OUString("focusLost"), { OUString("_Exit"), nullptr, ApproveType, static_cast<void const *>(&textCompList) } }, // support VBA TextBox_Exit event + + // adjustmentValueChanged ooo event + { OUString("adjustmentValueChanged"), { OUString("_Scroll"), nullptr, ApproveAll, nullptr } }, + { OUString("adjustmentValueChanged"), { OUString("_Change"), nullptr, ApproveAll, nullptr } }, + + // textChanged ooo event + { OUString("textChanged"), { OUString("_Change"), nullptr, ApproveAll, nullptr } }, + + // keyReleased ooo event + { OUString("keyReleased"), { OUString("_KeyUp"), ooKeyPressedToVBAKeyUpDown, ApproveAll, nullptr } }, + + // mouseReleased ooo event + { OUString("mouseReleased"), { OUString("_Click"), ooMouseEvtToVBAMouseEvt, ApproveType, static_cast<void const *>(&fixedTextList) } }, + { OUString("mouseReleased"), { OUString("_MouseUp"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } }, + + // mousePressed ooo event + { OUString("mousePressed"), { OUString("_MouseDown"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } }, + { OUString("mousePressed"), { OUString("_DblClick"), ooMouseEvtToVBADblClick, ApproveAll, nullptr } }, + + // mouseMoved ooo event + { OUString("mouseMoved"), { OUString("_MouseMove"), ooMouseEvtToVBAMouseEvt, ApproveAll, nullptr } }, + { OUString("mouseDragged"), { OUString("_MouseMove"), ooMouseEvtToVBAMouseEvt, DenyMouseDrag, nullptr } }, + + // keyPressed ooo event + { OUString("keyPressed"), { OUString("_KeyDown"), ooKeyPressedToVBAKeyUpDown, ApproveAll, nullptr } }, + { OUString("keyPressed"), { OUString("_KeyPress"), ooKeyPressedToVBAKeyPressed, ApproveAll, nullptr } } +}; + +static EventInfoHash& getEventTransInfo() +{ + static EventInfoHash eventTransInfo = []() + { + EventInfoHash tmp; + OUString sEventInfo; + TranslatePropMap* pTransProp = aTranslatePropMap_Impl; + int nCount = SAL_N_ELEMENTS(aTranslatePropMap_Impl); + + int i = 0; + while (i < nCount) + { + sEventInfo = pTransProp->sEventInfo; + std::vector< TranslateInfo > infoList; + do + { + infoList.push_back( pTransProp->aTransInfo ); + pTransProp++; + i++; + }while(i < nCount && sEventInfo == pTransProp->sEventInfo); + tmp[sEventInfo] = std::move(infoList); + } + return tmp; + }(); + return eventTransInfo; +} + + +// Helper class + +namespace { + +class ScriptEventHelper +{ +public: + explicit ScriptEventHelper( const Reference< XInterface >& xControl ); + explicit ScriptEventHelper( const OUString& sCntrlServiceName ); + ~ScriptEventHelper(); + Sequence< ScriptEventDescriptor > createEvents( const OUString& sCodeName ); + Sequence< OUString > getEventListeners() const; +private: + Reference< XComponentContext > m_xCtx; + Reference< XInterface > m_xControl; + bool m_bDispose; +}; + +} + +static bool +eventMethodToDescriptor( std::u16string_view rEventMethod, ScriptEventDescriptor& evtDesc, const OUString& sCodeName ) +{ + // format of ControlListener is TypeName::methodname e.g. + // "com.sun.star.awt.XActionListener::actionPerformed" or + // "XActionListener::actionPerformed + + OUString sMethodName; + OUString sTypeName; + size_t nDelimPos = rEventMethod.find( DELIM ); + if ( nDelimPos == std::u16string_view::npos ) + { + return false; + } + sMethodName = rEventMethod.substr( nDelimPos + DELIMLEN ); + sTypeName = rEventMethod.substr( 0, nDelimPos ); + + EventInfoHash& infos = getEventTransInfo(); + + // Only create an ScriptEventDescriptor for an event we can translate + // or emulate + if ( !sMethodName.isEmpty() + && !sTypeName.isEmpty() + && ( infos.find( sMethodName ) != infos.end() ) ) + { + // just fill in CodeName, when the event fires the other + // info is gathered from the event source to determine what + // event handler we try to call + evtDesc.ScriptCode = sCodeName; + evtDesc.ListenerType = sTypeName; + evtDesc.EventMethod = sMethodName; + + // set this it VBAInterop, ensures that it doesn't + // get persisted or shown in property editors + evtDesc.ScriptType = "VBAInterop"; + return true; + } + return false; + +} + +ScriptEventHelper::ScriptEventHelper( const Reference< XInterface >& xControl ) : + m_xCtx( comphelper::getProcessComponentContext() ), + m_xControl( xControl ), + m_bDispose( false ) +{} + +ScriptEventHelper::ScriptEventHelper( const OUString& sCntrlServiceName ) : + m_xCtx( comphelper::getProcessComponentContext() ), + m_bDispose( true ) +{ + m_xControl.set( m_xCtx->getServiceManager()->createInstanceWithContext( sCntrlServiceName, m_xCtx ), uno::UNO_QUERY ); +} + +ScriptEventHelper::~ScriptEventHelper() +{ + // dispose control ( and remove any associated event registrations ) + if ( m_bDispose ) + { + try + { + uno::Reference< lang::XComponent > xComp( m_xControl, uno::UNO_QUERY_THROW ); + xComp->dispose(); + } + // destructor can't throw + catch( uno::Exception& ) + { + } + } +} + +Sequence< OUString > +ScriptEventHelper::getEventListeners() const +{ + std::vector< OUString > eventMethods; + + Reference< beans::XIntrospection > xIntrospection = beans::theIntrospection::get( m_xCtx ); + + Reference< beans::XIntrospectionAccess > xIntrospectionAccess = + xIntrospection->inspect( Any( m_xControl ) ); + const Sequence< Type > aControlListeners = + xIntrospectionAccess->getSupportedListeners(); + for ( const Type& listType : aControlListeners ) + { + OUString sFullTypeName = listType.getTypeName(); + const Sequence< OUString > sMeths = + comphelper::getEventMethodsForType( listType ); + std::transform(sMeths.begin(), sMeths.end(), std::back_inserter(eventMethods), + [&sFullTypeName](const OUString& rMeth) -> OUString { return sFullTypeName + DELIM + rMeth; }); + } + + return comphelper::containerToSequence(eventMethods); +} + +Sequence< ScriptEventDescriptor > +ScriptEventHelper::createEvents( const OUString& sCodeName ) +{ + const Sequence< OUString > aControlListeners = getEventListeners(); + sal_Int32 nLength = aControlListeners.getLength(); + + Sequence< ScriptEventDescriptor > aDest( nLength ); + sal_Int32 nEvts = 0; + for ( OUString const & i : aControlListeners) + { + // from getListeners eventName is of form + // "com.sun.star.awt.XActionListener::actionPerformed" + // we need to strip "com.sun.star.awt." from that for form + // controls + ScriptEventDescriptor evtDesc; + if ( eventMethodToDescriptor( i, evtDesc, sCodeName ) ) + { + sal_Int32 dIndex = nEvts; + ++nEvts; + if ( nEvts > aDest.getLength() ) + aDest.realloc( nEvts );// should never happen + aDest.getArray()[ dIndex ] = evtDesc; + } + } + aDest.realloc( nEvts ); + + return aDest; +} + + +typedef ::cppu::WeakImplHelper< container::XNameContainer > NameContainer_BASE; + +namespace { + +class ReadOnlyEventsNameContainer : public NameContainer_BASE +{ +public: + ReadOnlyEventsNameContainer( const Sequence< OUString >& eventMethods, const OUString& sCodeName ); + // XNameContainer + + virtual void SAL_CALL insertByName( const OUString&, const Any& ) override + { + throw RuntimeException("ReadOnly container" ); + + } + virtual void SAL_CALL removeByName( const OUString& ) override + { + throw RuntimeException("ReadOnly container" ); + } + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString&, const Any& ) override + { + throw RuntimeException("ReadOnly container" ); + + } + + // XNameAccess + virtual Any SAL_CALL getByName( const OUString& aName ) override; + virtual Sequence< OUString > SAL_CALL getElementNames( ) override; + virtual sal_Bool SAL_CALL hasByName( const OUString& aName ) override; + + // XElementAccess + virtual Type SAL_CALL getElementType( ) override + { return cppu::UnoType<OUString>::get(); } + virtual sal_Bool SAL_CALL hasElements( ) override + { return !m_hEvents.empty(); } +private: + +typedef std::unordered_map< OUString, Any > EventSupplierHash; + + EventSupplierHash m_hEvents; +}; + +} + +ReadOnlyEventsNameContainer::ReadOnlyEventsNameContainer( const Sequence< OUString >& eventMethods, const OUString& sCodeName ) +{ + for ( const OUString& rSrc : eventMethods ) + { + Any aDesc; + ScriptEventDescriptor evtDesc; + if ( eventMethodToDescriptor( rSrc, evtDesc, sCodeName ) ) + { + aDesc <<= evtDesc; + m_hEvents[ rSrc ] = aDesc; + } + } +} + +Any SAL_CALL +ReadOnlyEventsNameContainer::getByName( const OUString& aName ){ + EventSupplierHash::const_iterator it = m_hEvents.find( aName ); + if ( it == m_hEvents.end() ) + throw container::NoSuchElementException(); + return it->second; +} + +Sequence< OUString > SAL_CALL +ReadOnlyEventsNameContainer::getElementNames( ) +{ + return comphelper::mapKeysToSequence(m_hEvents); +} + +sal_Bool SAL_CALL +ReadOnlyEventsNameContainer::hasByName( const OUString& aName ) +{ + EventSupplierHash::const_iterator it = m_hEvents.find( aName ); + if ( it == m_hEvents.end() ) + return false; + return true; +} + +namespace { + +class ReadOnlyEventsSupplier : public ::cppu::WeakImplHelper< XScriptEventsSupplier > +{ +public: + ReadOnlyEventsSupplier( const Sequence< OUString >& eventMethods, const OUString& sCodeName ) + { m_xNameContainer = new ReadOnlyEventsNameContainer( eventMethods, sCodeName ); } + + // XScriptEventSupplier + virtual Reference< container::XNameContainer > SAL_CALL getEvents( ) override { return m_xNameContainer; } +private: + Reference< container::XNameContainer > m_xNameContainer; +}; + +} + +typedef ::cppu::WeakImplHelper< XScriptListener, util::XCloseListener, lang::XInitialization, css::lang::XServiceInfo > EventListener_BASE; + +#define EVENTLSTNR_PROPERTY_ID_MODEL 1 +constexpr OUStringLiteral EVENTLSTNR_PROPERTY_MODEL = u"Model"; + +namespace { + +class EventListener : public EventListener_BASE + ,public ::comphelper::OMutexAndBroadcastHelper + ,public ::comphelper::OPropertyContainer + ,public ::comphelper::OPropertyArrayUsageHelper< EventListener > +{ + +public: + EventListener(); + // XEventListener + virtual void SAL_CALL disposing(const lang::EventObject& Source) override; + using cppu::OPropertySetHelper::disposing; + + // XScriptListener + virtual void SAL_CALL firing(const ScriptEvent& evt) override; + virtual Any SAL_CALL approveFiring(const ScriptEvent& evt) override; + // XCloseListener + virtual void SAL_CALL queryClosing( const lang::EventObject& Source, sal_Bool GetsOwnership ) override; + virtual void SAL_CALL notifyClosing( const lang::EventObject& Source ) override; + // XPropertySet + virtual css::uno::Reference< css::beans::XPropertySetInfo > SAL_CALL getPropertySetInfo( ) override; + // XInitialization + virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; + // XInterface + DECLARE_XINTERFACE() + + // XTypeProvider + DECLARE_XTYPEPROVIDER() + virtual void SAL_CALL setFastPropertyValue( sal_Int32 nHandle, const css::uno::Any& rValue ) override + { + if ( nHandle == EVENTLSTNR_PROPERTY_ID_MODEL ) + { + uno::Reference< frame::XModel > xModel( rValue, uno::UNO_QUERY ); + if( xModel != m_xModel) + { + // Remove the listener from the old XCloseBroadcaster. + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xModel, uno::UNO_QUERY ); + if (xCloseBroadcaster.is()) + { + xCloseBroadcaster->removeCloseListener( this ); + } + // Add the listener into the new XCloseBroadcaster. + xCloseBroadcaster.set( xModel, uno::UNO_QUERY ); + if (xCloseBroadcaster.is()) + { + xCloseBroadcaster->addCloseListener( this ); + } + } + } + OPropertyContainer::setFastPropertyValue( nHandle, rValue ); + if ( nHandle == EVENTLSTNR_PROPERTY_ID_MODEL ) + setShellFromModel(); + } + + OUString SAL_CALL getImplementationName() override + { + return "ooo.vba.EventListener"; + } + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { getImplementationName() }; + } + +protected: + // OPropertySetHelper + virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper( ) override; + + // OPropertyArrayUsageHelper + virtual ::cppu::IPropertyArrayHelper* createArrayHelper( ) const override; + +private: + void setShellFromModel(); + /// @throws RuntimeException + void firing_Impl( const ScriptEvent& evt, Any *pSyncRet ); + + Reference< frame::XModel > m_xModel; + bool m_bDocClosed; + SfxObjectShell* mpShell; +}; + +} + +EventListener::EventListener() : +OPropertyContainer(GetBroadcastHelper()), m_bDocClosed(false), mpShell( nullptr ) +{ + registerProperty( EVENTLSTNR_PROPERTY_MODEL, EVENTLSTNR_PROPERTY_ID_MODEL, + beans::PropertyAttribute::TRANSIENT, &m_xModel, cppu::UnoType<decltype(m_xModel)>::get() ); +} + +void +EventListener::setShellFromModel() +{ + // reset mpShell + mpShell = nullptr; + SfxObjectShell* pShell = SfxObjectShell::GetFirst(); + while ( m_xModel.is() && pShell ) + { + if ( pShell->GetModel() == m_xModel ) + { + mpShell = pShell; + break; + } + pShell = SfxObjectShell::GetNext( *pShell ); + } +} + +//XEventListener +void +EventListener::disposing(const lang::EventObject&) +{ +} + +//XScriptListener + +void SAL_CALL +EventListener::firing(const ScriptEvent& evt) +{ + firing_Impl( evt, nullptr ); +} + +Any SAL_CALL +EventListener::approveFiring(const ScriptEvent& evt) +{ + Any ret; + firing_Impl( evt, &ret ); + return ret; +} + +// XCloseListener +void SAL_CALL +EventListener::queryClosing( const lang::EventObject& /*Source*/, sal_Bool /*GetsOwnership*/ ) +{ + //Nothing to do +} + +void SAL_CALL +EventListener::notifyClosing( const lang::EventObject& /*Source*/ ) +{ + m_bDocClosed = true; + uno::Reference< util::XCloseBroadcaster > xCloseBroadcaster( m_xModel, uno::UNO_QUERY ); + if (xCloseBroadcaster.is()) + { + xCloseBroadcaster->removeCloseListener( this ); + } +} + +// XInitialization +void SAL_CALL +EventListener::initialize( const Sequence< Any >& aArguments ) +{ + if ( aArguments.getLength() == 1 ) + aArguments[0] >>= m_xModel; + SAL_INFO( + "scripting", + "args " << aArguments.getLength() << " m_xModel " << m_xModel.is()); +} + +// XInterface + +IMPLEMENT_FORWARD_XINTERFACE2( EventListener, EventListener_BASE, OPropertyContainer ) + +// XTypeProvider + +IMPLEMENT_FORWARD_XTYPEPROVIDER2( EventListener, EventListener_BASE, OPropertyContainer ) + +// OPropertySetHelper + +::cppu::IPropertyArrayHelper& +EventListener::getInfoHelper( ) +{ + return *getArrayHelper(); +} + +// OPropertyArrayUsageHelper + +::cppu::IPropertyArrayHelper* +EventListener::createArrayHelper( ) const +{ + Sequence< beans::Property > aProps; + describeProperties( aProps ); + return new ::cppu::OPropertyArrayHelper( aProps ); +} + +// XPropertySet +Reference< beans::XPropertySetInfo > +EventListener::getPropertySetInfo( ) +{ + Reference< beans::XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + + +//decide if the control should execute the event +bool ApproveAll(SAL_UNUSED_PARAMETER const ScriptEvent&, SAL_UNUSED_PARAMETER void const * ) +{ + return true; +} + +//for the given control type in evt.Arguments[0], look for if it appears in the type list in pPara +static bool FindControl(const ScriptEvent& evt, void const * pPara) +{ + lang::EventObject aEvent; + evt.Arguments[ 0 ] >>= aEvent; + uno::Reference< uno::XInterface > xInterface( aEvent.Source, uno::UNO_QUERY ); + + TypeList const * pTypeListInfo = static_cast<TypeList const *>(pPara); + Type const * pType = pTypeListInfo->pTypeList; + int nLen = pTypeListInfo->nListLength; + + for (int i = 0; i < nLen; i++) + { + if ( xInterface->queryInterface( *pType ).hasValue() ) + { + return true; + } + pType++; + } + + return false; +} + +//if the given control type in evt.Arguments[0] appears in the type list in pPara, then approve the execution +bool ApproveType(const ScriptEvent& evt, void const * pPara) +{ + return FindControl(evt, pPara); +} + +//if the given control type in evt.Arguments[0] appears in the type list in pPara, then deny the execution +bool DenyType(const ScriptEvent& evt, void const * pPara) +{ + return !FindControl(evt, pPara); +} + +//when mouse is moving, either the mouse button is pressed or some key is pressed can trigger the OO mouseDragged event, +//the former should be denied, and the latter allowed, only by doing so can the VBA MouseMove event when the "Shift" key is +//pressed can be correctly triggered +bool DenyMouseDrag(const ScriptEvent& evt, SAL_UNUSED_PARAMETER void const * ) +{ + awt::MouseEvent aEvent; + evt.Arguments[ 0 ] >>= aEvent; + return aEvent.Buttons == 0; +} + + +// EventListener + +void +EventListener::firing_Impl(const ScriptEvent& evt, Any* pRet ) +{ + // let default handlers deal with non vba stuff + if ( evt.ScriptType != "VBAInterop" ) + return; + lang::EventObject aEvent; + evt.Arguments[ 0 ] >>= aEvent; + OUString sName = "UserForm"; + + uno::Reference< awt::XDialog > xDlg( aEvent.Source, uno::UNO_QUERY ); + if ( !xDlg.is() ) + { + // evt.Source is + // a) Dialog + // b) xShapeControl ( from api (sheet control) ) + // c) eventmanager ( I guess ) + // d) vba control ( from api also ) + uno::Reference< drawing::XControlShape > xCntrlShape( evt.Source, uno::UNO_QUERY ); + uno::Reference< awt::XControl > xControl( aEvent.Source, uno::UNO_QUERY ); + if ( xCntrlShape.is() ) + { + // for sheet controls ( that fire from the api ) we don't + // have the real control ( that's only available from the view ) + // api code creates just a control instance that is transferred + // via aEvent.Arguments[ 0 ] that control though has no + // info like name etc. + uno::Reference< container::XNamed > xName( xCntrlShape->getControl(), uno::UNO_QUERY_THROW ); + sName = xName->getName(); + } + else + { + // Userform control ( fired from the api or from event manager ) + uno::Reference< beans::XPropertySet > xProps; + xProps.set( xControl->getModel(), uno::UNO_QUERY_THROW ); + xProps->getPropertyValue("Name") >>= sName; + } + } + //dumpEvent( evt ); + EventInfoHash& infos = getEventTransInfo(); + EventInfoHash::const_iterator eventInfo_it = infos.find( evt.MethodName ); + EventInfoHash::const_iterator it_end = infos.end(); + if ( eventInfo_it == it_end ) + { + SAL_WARN("scripting", "Bogus event for " << evt.ScriptType ); + return; + } + + uno::Reference< script::provider::XScriptProviderSupplier > xSPS( m_xModel, uno::UNO_QUERY ); + uno::Reference< script::provider::XScriptProvider > xScriptProvider; + if ( xSPS.is() ) + { + xScriptProvider = xSPS->getScriptProvider(); + } + if ( !(xScriptProvider.is() && mpShell) ) + return; + + BasicManager* pBasicManager = mpShell->GetBasicManager(); + OUString sProject; + OUString sScriptCode( evt.ScriptCode ); + // dialogs pass their own library, presence of Dot determines that + if ( sScriptCode.indexOf( '.' ) == -1 ) + { + //'Project' is a better default but I want to force failures + //OUString sMacroLoc("Project"); + sProject = "Standard"; + + if (!pBasicManager->GetName().isEmpty()) + { + sProject = pBasicManager->GetName(); + } + } + else + { + sal_Int32 nIndex = sScriptCode.indexOf( '.' ); + sProject = sScriptCode.copy( 0, nIndex ); + sScriptCode = sScriptCode.copy( nIndex + 1 ); + } + OUString sMacroLoc = sProject + "." + sScriptCode + "."; + + for (const auto& rTxInfo : eventInfo_it->second) + { + // If the document is closed, we should not execute macro. + if (m_bDocClosed) + { + break; + } + + // see if we have a match for the handlerextension + // where ScriptCode is methodname_handlerextension + OUString sToResolve = sMacroLoc + sName + rTxInfo.sVBAName; + + ooo::vba::MacroResolvedInfo aMacroResolvedInfo = ooo::vba::resolveVBAMacro( mpShell, sToResolve ); + if ( aMacroResolvedInfo.mbFound ) + { + + if (! rTxInfo.ApproveRule(evt, rTxInfo.pPara) ) + { + continue; + } + + // !! translate arguments & emulate events where necessary + Sequence< Any > aArguments; + if ( rTxInfo.toVBA ) + { + aArguments = rTxInfo.toVBA( evt.Arguments ); + } + else + { + aArguments = evt.Arguments; + } + if ( aArguments.hasElements() ) + { + // call basic event handlers for event + + // create script url + OUString url = aMacroResolvedInfo.msResolvedMacro; + try + { + uno::Any aDummyCaller( OUString("Error") ); + if ( pRet ) + { + ooo::vba::executeMacro( mpShell, url, aArguments, *pRet, aDummyCaller ); + } + else + { + uno::Any aRet; + ooo::vba::executeMacro( mpShell, url, aArguments, aRet, aDummyCaller ); + } + } + catch ( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION("scripting", "event script raised" ); + } + } + } + } +} + +namespace { + +class VBAToOOEventDescGen : public ::cppu::WeakImplHelper< XVBAToOOEventDescGen, css::lang::XServiceInfo > +{ +public: + VBAToOOEventDescGen(); + + // XVBAToOOEventDescGen + virtual Sequence< ScriptEventDescriptor > SAL_CALL getEventDescriptions( const OUString& sCtrlServiceName, const OUString& sCodeName ) override; + virtual Reference< XScriptEventsSupplier > SAL_CALL getEventSupplier( const Reference< XInterface >& xControl, const OUString& sCodeName ) override; + + OUString SAL_CALL getImplementationName() override + { + return "ooo.vba.VBAToOOEventDesc"; + } + + sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { + return cppu::supportsService(this, ServiceName); + } + + css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override + { + return { getImplementationName() }; + } + +}; + +} + +VBAToOOEventDescGen::VBAToOOEventDescGen() {} + +Sequence< ScriptEventDescriptor > SAL_CALL +VBAToOOEventDescGen::getEventDescriptions( const OUString& sCntrlServiceName, const OUString& sCodeName ) +{ + ScriptEventHelper evntHelper( sCntrlServiceName ); + return evntHelper.createEvents( sCodeName ); +} + +Reference< XScriptEventsSupplier > SAL_CALL +VBAToOOEventDescGen::getEventSupplier( const Reference< XInterface >& xControl, const OUString& sCodeName ) +{ + ScriptEventHelper evntHelper( xControl ); + Reference< XScriptEventsSupplier > xSupplier = + new ReadOnlyEventsSupplier( + evntHelper.getEventListeners(), sCodeName ) ; + return xSupplier; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ooo_vba_EventListener_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new EventListener); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +ooo_vba_VBAToOOEventDesc_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new VBAToOOEventDescGen); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/scripting/source/vbaevents/vbaevents.component b/scripting/source/vbaevents/vbaevents.component new file mode 100644 index 000000000..75fcf0230 --- /dev/null +++ b/scripting/source/vbaevents/vbaevents.component @@ -0,0 +1,30 @@ +<?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="ooo.vba.EventListener" + constructor="ooo_vba_EventListener_get_implementation"> + <service name="ooo.vba.EventListener"/> + </implementation> + <implementation name="ooo.vba.VBAToOOEventDesc" + constructor="ooo_vba_VBAToOOEventDesc_get_implementation"> + <service name="ooo.vba.VBAToOOEventDesc"/> + </implementation> +</component> |