Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/extensions/source/propctrlr/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 45 kB image not shown  

Quelle  eventhandler.cxx   Sprache: C

 
/* -*- 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 "eventhandler.hxx"
#include <helpids.h>
#include <propctrlr.h>
#include "formbrowsertools.hxx"
#include <strings.hrc>
#include "formstrings.hxx"
#include "handlerhelper.hxx"
#include "modulepcr.hxx"
#include "pcrcommon.hxx"
#include "propertycontrolextender.hxx"

#include <com/sun/star/awt/XTabControllerModel.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/UnknownPropertyException.hpp>
#include <com/sun/star/beans/theIntrospection.hpp>
#include <com/sun/star/beans/XIntrospectionAccess.hpp>
#include <com/sun/star/container/NoSuchElementException.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/container/XNameReplace.hpp>
#include <com/sun/star/form/FormComponentType.hpp>
#include <com/sun/star/form/XForm.hpp>
#include <com/sun/star/form/runtime/FormController.hpp>
#include <com/sun/star/inspection/PropertyControlType.hpp>
#include <com/sun/star/lang/NullPointerException.hpp>
#include <com/sun/star/script/XEventAttacherManager.hpp>
#include <com/sun/star/script/XScriptEventsSupplier.hpp>
#include <com/sun/star/uri/UriReferenceFactory.hpp>
#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>

#include <comphelper/namedvaluecollection.hxx>
#include <comphelper/evtmethodhelper.hxx>
#include <comphelper/propertyvalue.hxx>
#include <comphelper/sequence.hxx>
#include <comphelper/types.hxx>
#include <cppuhelper/implbase.hxx>
#include <cppuhelper/supportsservice.hxx>
#include <rtl/ref.hxx>
#include <rtl/ustrbuf.hxx>
#include <svx/svxdlg.hxx>
#include <comphelper/diagnose_ex.hxx>

#include <map>
#include <algorithm>
#include <iterator>
#include <string_view>
#include <utility>

namespace pcr
{

    using ::com::sun::star::uno::Reference;
    using ::com::sun::star::uno::XComponentContext;
    using ::com::sun::star::uno::Any;
    using ::com::sun::star::uno::TypeClass_STRING;
    using ::com::sun::star::uno::Type;
    using ::com::sun::star::beans::theIntrospection;
    using ::com::sun::star::beans::XPropertyChangeListener;
    using ::com::sun::star::beans::Property;
    using ::com::sun::star::beans::PropertyState;
    using ::com::sun::star::beans::PropertyState_DIRECT_VALUE;
    using ::com::sun::star::uno::Sequence;
    using ::com::sun::star::script::ScriptEventDescriptor;
    using ::com::sun::star::script::XScriptEventsSupplier;
    using ::com::sun::star::lang::NullPointerException;
    using ::com::sun::star::uno::Exception;
    using ::com::sun::star::container::XChild;
    using ::com::sun::star::container::XIndexAccess;
    using ::com::sun::star::script::XEventAttacherManager;
    using ::com::sun::star::uno::UNO_QUERY;
    using ::com::sun::star::uno::UNO_QUERY_THROW;
    using ::com::sun::star::uno::XInterface;
    using ::com::sun::star::beans::XIntrospection;
    using ::com::sun::star::beans::XIntrospectionAccess;
    using ::com::sun::star::container::XNameContainer;
    using ::com::sun::star::awt::XTabControllerModel;
    using ::com::sun::star::form::XForm;
    using ::com::sun::star::form::runtime::FormController;
    using ::com::sun::star::form::runtime::XFormController;
    using ::com::sun::star::beans::UnknownPropertyException;
    using ::com::sun::star::container::NoSuchElementException;
    using ::com::sun::star::beans::XPropertySetInfo;
    using ::com::sun::star::container::XNameReplace;
    using ::com::sun::star::beans::PropertyValue;
    using ::com::sun::star::inspection::LineDescriptor;
    using ::com::sun::star::inspection::XPropertyControlFactory;
    using ::com::sun::star::inspection::InteractiveSelectionResult;
    using ::com::sun::star::inspection::InteractiveSelectionResult_Cancelled;
    using ::com::sun::star::inspection::InteractiveSelectionResult_Success;
    using ::com::sun::star::inspection::XObjectInspectorUI;
    using ::com::sun::star::beans::PropertyChangeEvent;
    using ::com::sun::star::frame::XFrame;
    using ::com::sun::star::frame::XModel;
    using ::com::sun::star::frame::XController;
    using ::com::sun::star::uno::UNO_SET_THROW;
    using com::sun::star::uri::UriReferenceFactory;
    using com::sun::star::uri::XUriReferenceFactory;
    using com::sun::star::uri::XVndSunStarScriptUrlReference;

    namespace PropertyControlType = css::inspection::PropertyControlType;
    namespace PropertyAttribute = css::beans::PropertyAttribute;
    namespace FormComponentType = css::form::FormComponentType;

    EventDescription::EventDescription( EventId _nId, std::u16string_view listenerClassName,
            std::u16string_view listenerMethodName, TranslateId pDisplayNameResId, OUString _sHelpId, OString _sUniqueBrowseId )
        :sDisplayName(PcrRes( pDisplayNameResId ))
        ,sListenerClassName( listenerClassName )
        ,sListenerMethodName( listenerMethodName )
        ,sHelpId(std::move( _sHelpId ))
        ,sUniqueBrowseId(std::move( _sUniqueBrowseId ))
        ,nId( _nId )
    {
    }

    namespace
    {
        #define DESCRIBE_EVENT( map, listener, method, id_postfix ) \
            map.emplace(  \
                u"" method##_ustr, \
                EventDescription( ++nEventId, u"com.sun.star." listener, u"" method, RID_STR_EVT_##id_postfix, HID_EVT_##id_postfix, UID_BRWEVT_##id_postfix ) )

        bool lcl_getEventDescriptionForMethod( const OUString& _rMethodName, EventDescription& _out_rDescription )
        {
            static EventMap s_aKnownEvents = []() {
                EventMap aMap;
                sal_Int32 nEventId = 0;

                DESCRIBE_EVENT(aMap, "form.XApproveActionListener",     "approveAction",          APPROVEACTIONPERFORMED);
                DESCRIBE_EVENT(aMap, "awt.XActionListener",             "actionPerformed",        ACTIONPERFORMED);
                DESCRIBE_EVENT(aMap, "form.XChangeListener",            "changed",                CHANGED);
                DESCRIBE_EVENT(aMap, "awt.XTextListener",               "textChanged",            TEXTCHANGED);
                DESCRIBE_EVENT(aMap, "awt.XItemListener",               "itemStateChanged",       ITEMSTATECHANGED);
                DESCRIBE_EVENT(aMap, "awt.XFocusListener",              "focusGained",            FOCUSGAINED);
                DESCRIBE_EVENT(aMap, "awt.XFocusListener",              "focusLost",              FOCUSLOST);
                DESCRIBE_EVENT(aMap, "awt.XKeyListener",                "keyPressed",             KEYTYPED);
                DESCRIBE_EVENT(aMap, "awt.XKeyListener",                "keyReleased",            KEYUP);
                DESCRIBE_EVENT(aMap, "awt.XMouseListener",              "mouseEntered",           MOUSEENTERED);
                DESCRIBE_EVENT(aMap, "awt.XMouseMotionListener",        "mouseDragged",           MOUSEDRAGGED);
                DESCRIBE_EVENT(aMap, "awt.XMouseMotionListener",        "mouseMoved",             MOUSEMOVED);
                DESCRIBE_EVENT(aMap, "awt.XMouseListener",              "mousePressed",           MOUSEPRESSED);
                DESCRIBE_EVENT(aMap, "awt.XMouseListener",              "mouseReleased",          MOUSERELEASED);
                DESCRIBE_EVENT(aMap, "awt.XMouseListener",              "mouseExited",            MOUSEEXITED);
                DESCRIBE_EVENT(aMap, "form.XResetListener",             "approveReset",           APPROVERESETTED);
                DESCRIBE_EVENT(aMap, "form.XResetListener",             "resetted",               RESETTED);
                DESCRIBE_EVENT(aMap, "form.XSubmitListener",            "approveSubmit",          SUBMITTED);
                DESCRIBE_EVENT(aMap, "form.XUpdateListener",            "approveUpdate",          BEFOREUPDATE);
                DESCRIBE_EVENT(aMap, "form.XUpdateListener",            "updated",                AFTERUPDATE);
                DESCRIBE_EVENT(aMap, "form.XLoadListener",              "loaded",                 LOADED);
                DESCRIBE_EVENT(aMap, "form.XLoadListener",              "reloading",              RELOADING);
                DESCRIBE_EVENT(aMap, "form.XLoadListener",              "reloaded",               RELOADED);
                DESCRIBE_EVENT(aMap, "form.XLoadListener",              "unloading",              UNLOADING);
                DESCRIBE_EVENT(aMap, "form.XLoadListener",              "unloaded",               UNLOADED);
                DESCRIBE_EVENT(aMap, "form.XConfirmDeleteListener",     "confirmDelete",          CONFIRMDELETE);
                DESCRIBE_EVENT(aMap, "sdb.XRowSetApproveListener",      "approveRowChange",       APPROVEROWCHANGE);
                DESCRIBE_EVENT(aMap, "sdbc.XRowSetListener",            "rowChanged",             ROWCHANGE);
                DESCRIBE_EVENT(aMap, "sdb.XRowSetApproveListener",      "approveCursorMove",      POSITIONING);
                DESCRIBE_EVENT(aMap, "sdbc.XRowSetListener",            "cursorMoved",            POSITIONED);
                DESCRIBE_EVENT(aMap, "form.XDatabaseParameterListener""approveParameter",       APPROVEPARAMETER);
                DESCRIBE_EVENT(aMap, "sdb.XSQLErrorListener",           "errorOccured",           ERROROCCURRED);
                DESCRIBE_EVENT(aMap, "awt.XAdjustmentListener",         "adjustmentValueChanged", ADJUSTMENTVALUECHANGED);

                return aMap;
            }();

            EventMap::const_iterator pos = s_aKnownEvents.find( _rMethodName );
            if ( pos == s_aKnownEvents.end() )
                return false;

            _out_rDescription = pos->second;
            return true;
        }

        OUString lcl_getEventPropertyName( std::u16string_view _rListenerClassName, std::u16string_view _rMethodName )
        {
            return _rListenerClassName + OUStringChar(';') + _rMethodName;
        }

        ScriptEventDescriptor lcl_getAssignedScriptEvent( const EventDescription& _rEvent, const std::vector< ScriptEventDescriptor >& _rAllAssignedMacros )
        {
            ScriptEventDescriptor aScriptEvent;
            // for the case there is actually no event assigned, initialize at least ListenerType and MethodName,
            //  so this ScriptEventDescriptor properly describes the given event
            aScriptEvent.ListenerType = _rEvent.sListenerClassName;
            aScriptEvent.EventMethod = _rEvent.sListenerMethodName;

            for ( const ScriptEventDescriptor& rSED :  _rAllAssignedMacros )
            {
                if  (   rSED.ListenerType != _rEvent.sListenerClassName
                    ||  rSED.EventMethod != _rEvent.sListenerMethodName
                    )
                    continue;

                if  (  rSED.ScriptCode.isEmpty()
                    || rSED.ScriptType.isEmpty()
                    )
                {
                    OSL_FAIL( "lcl_getAssignedScriptEvent: me thinks this should not happen!" );
                    continue;
                }

                aScriptEvent = rSED;

                if ( aScriptEvent.ScriptType != "StarBasic" )
                    continue;

                // this is an old-style macro specification:
                // [document|application]:Library.Module.Function
                // we need to translate this to the new-style macro specification
                // vnd.sun.star.script:Library.Module.Function?language=Basic&location=[document|application]

                sal_Int32 nPrefixLen = aScriptEvent.ScriptCode.indexOf( ':' );
                OSL_ENSURE( nPrefixLen > 0, "lcl_getAssignedScriptEvent: illegal location!" );
                std::u16string_view sLocation = aScriptEvent.ScriptCode.subView( 0, nPrefixLen );
                std::u16string_view sMacroPath = aScriptEvent.ScriptCode.subView( nPrefixLen + 1 );

                aScriptEvent.ScriptCode =
                    OUString::Concat("vnd.sun.star.script:") +
                    sMacroPath +
                    "?language=Basic&location=" +
                    sLocation;

                // also, this new-style spec requires the script code to be "Script" instead of "StarBasic"
                aScriptEvent.ScriptType = "Script";
            }
            return aScriptEvent;
        }

        OUString lcl_getQualifiedKnownListenerName( const ScriptEventDescriptor& _rFormComponentEventDescriptor )
        {
            EventDescription aKnownEvent;
            if ( lcl_getEventDescriptionForMethod( _rFormComponentEventDescriptor.EventMethod, aKnownEvent ) )
                return aKnownEvent.sListenerClassName;
            OSL_FAIL( "lcl_getQualifiedKnownListenerName: unknown method name!" );
                // somebody assigned an script to a form component event which we don't know
                // Speaking strictly, this is not really an error - it is possible to do
                // this programmatically -, but it should rarely happen, since it's not possible
                // via UI
            return _rFormComponentEventDescriptor.ListenerType;
        }

        typedef std::set< Type, TypeLessByName > TypeBag;

        void lcl_addListenerTypesFor_throw( const Reference< XInterface >& _rxComponent,
            const Reference< XIntrospection >& _rxIntrospection, TypeBag& _out_rTypes )
        {
            if ( !_rxComponent.is() )
                return;
            OSL_PRECOND( _rxIntrospection.is(), "lcl_addListenerTypesFor_throw: this will crash!" );

            Reference< XIntrospectionAccess > xIntrospectionAccess(
                _rxIntrospection->inspect( Any( _rxComponent ) ), UNO_SET_THROW );

            const Sequence< Type > aListeners( xIntrospectionAccess->getSupportedListeners() );

            std::copy( aListeners.begin(), aListeners.end(),
                         std::insert_iterator< TypeBag >( _out_rTypes, _out_rTypes.begin() ) );
        }
    }

    typedef ::cppu::WeakImplHelper <   css::container::XNameReplace
                                    >   EventHolder_Base;

    namespace {

    /* A UNO component holding assigned event descriptions, for use with a SvxMacroAssignDlg */
    class EventHolder : public EventHolder_Base
    {
    private:
        typedef std::unordered_map< OUString, ScriptEventDescriptor >  EventMap;
        typedef std::map< EventId, OUString >                          EventMapIndexAccess;

        EventMap            m_aEventNameAccess;
        EventMapIndexAccess m_aEventIndexAccess;

    public:
        EventHolder( );

        void addEvent( EventId _nId, const OUString& _rEventName, const ScriptEventDescriptor& _rScriptEvent );

        /** effectively the same as getByName, but instead of converting the ScriptEventDescriptor to the weird
            format used by the macro assignment dialog, it is returned directly
        */

        ScriptEventDescriptor getNormalizedDescriptorByName( const OUString& _rEventName const;

        // XNameReplace
        virtual void SAL_CALL replaceByName( const OUString& _rName, const Any& aElement ) override;
        virtual Any SAL_CALL getByName( const OUString& _rName ) override;
        virtual Sequence< OUString > SAL_CALL getElementNames(  ) override;
        virtual sal_Bool SAL_CALL hasByName( const OUString& _rName ) override;
        virtual Type SAL_CALL getElementType(  ) override;
        virtual sal_Bool SAL_CALL hasElements(  ) override;

    protected:
        virtual ~EventHolder( ) override;

    private:
        ScriptEventDescriptor const & impl_getDescriptor_throw( const OUString& _rEventName ) const;
    };

    }

    EventHolder::EventHolder()
    {
    }

    EventHolder::~EventHolder()
    {
        m_aEventNameAccess.clear();
        m_aEventIndexAccess.clear();
    }

    void EventHolder::addEvent( EventId _nId, const OUString& _rEventName, const ScriptEventDescriptor& _rScriptEvent )
    {
        std::pair< EventMap::iterator, bool > insertionResult =
            m_aEventNameAccess.emplace( _rEventName, _rScriptEvent );
        OSL_ENSURE( insertionResult.second, "EventHolder::addEvent: there already was a MacroURL for this event!" );
        m_aEventIndexAccess[ _nId ] = _rEventName;
    }

    ScriptEventDescriptor EventHolder::getNormalizedDescriptorByName( const OUString&&nbsp;_rEventName ) const
    {
        return impl_getDescriptor_throw( _rEventName );
    }

    ScriptEventDescriptor const & EventHolder::impl_getDescriptor_throw( const OUString& _rEventName ) const
    {
        EventMap::const_iterator pos = m_aEventNameAccess.find( _rEventName );
        if ( pos == m_aEventNameAccess.end() )
            throw NoSuchElementException( OUString(), *const_cast< EventHolder* >( this ) );
        return pos->second;
    }

    void SAL_CALL EventHolder::replaceByName( const OUString& _rName, const Any& _rElement )
    {
        EventMap::iterator pos = m_aEventNameAccess.find( _rName );
        if ( pos == m_aEventNameAccess.end() )
            throw NoSuchElementException( OUString(), *this );

        Sequence< PropertyValue > aScriptDescriptor;
        OSL_VERIFY( _rElement >>= aScriptDescriptor );

        ::comphelper::NamedValueCollection aExtractor( aScriptDescriptor );

        pos->second.ScriptType = aExtractor.getOrDefault( u"EventType"_ustr, OUString() );
        pos->second.ScriptCode = aExtractor.getOrDefault( u"Script"_ustr, OUString() );
    }

    Any SAL_CALL EventHolder::getByName( const OUString& _rName )
    {
        ScriptEventDescriptor aDescriptor( impl_getDescriptor_throw( _rName ) );

        Sequence< PropertyValue > aScriptDescriptor{
            comphelper::makePropertyValue(u"EventType"_ustr, aDescriptor.ScriptType),
            comphelper::makePropertyValue(u"Script"_ustr, aDescriptor.ScriptCode)
        };

        return Any( aScriptDescriptor );
    }

    Sequence< OUString > SAL_CALL EventHolder::getElementNames(  )
    {
        Sequence< OUString > aReturn( m_aEventIndexAccess.size() );
        OUString* pReturn = aReturn.getArray();

        // SvxMacroAssignDlg has a weird API: It expects a XNameReplace, means a container whose
        // main access method is by name. In its UI, it shows the possible events in exactly the
        // order in which XNameAccess::getElementNames returns them.
        // However, SvxMacroAssignDlg *also* takes an index for the initial selection, which is
        // relative to the sequence returned by XNameAccess::getElementNames.
        // This is IMO weird, since it mixes index access with name access, which decreases efficiency
        // of the implementation.
        // Well, it means we're forced to return the events in getElementNames in exactly the same as they
        // appear in the property browser UI.
        for (auto const& elem : m_aEventIndexAccess)
        {
            *pReturn = elem.second;
            ++pReturn;
        }
        return aReturn;
    }

    sal_Bool SAL_CALL EventHolder::hasByName( const OUString& _rName )
    {
        EventMap::const_iterator pos = m_aEventNameAccess.find( _rName );
        return pos != m_aEventNameAccess.end();
    }

    Type SAL_CALL EventHolder::getElementType(  )
    {
        return cppu::UnoType<Sequence< PropertyValue >>::get();
    }

    sal_Bool SAL_CALL EventHolder::hasElements(  )
    {
        return !m_aEventNameAccess.empty();
    }


    EventHandler::EventHandler( const Reference< XComponentContext >& _rxContext )
        :EventHandler_Base( m_aMutex )
        ,m_xContext( _rxContext )
        ,m_aPropertyListeners( m_aMutex )
        ,m_bEventsMapInitialized( false )
        ,m_bIsDialogElement( false )
        ,m_nGridColumnType( -1 )
    {
    }

    EventHandler::~EventHandler()
    {
    }

    OUString SAL_CALL EventHandler::getImplementationName(  )
    {
        return u"com.sun.star.comp.extensions.EventHandler"_ustr;
    }

    sal_Bool SAL_CALL EventHandler::supportsService( const OUString& ServiceName )
    {
        return cppu::supportsService(this, ServiceName);
    }

    Sequence< OUString > SAL_CALL EventHandler::getSupportedServiceNames(  )
    {
        return { u"com.sun.star.form.inspection.EventHandler"_ustr };
    }

    void SAL_CALL EventHandler::inspect( const Reference< XInterface >& _rxIntrospectee )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        if ( !_rxIntrospectee.is() )
            throw NullPointerException();

        m_xComponent.set( _rxIntrospectee, UNO_QUERY_THROW );

        m_bEventsMapInitialized = false;
        EventMap().swap(m_aEvents);

        m_bIsDialogElement = false;
        m_nGridColumnType = -1;
        try
        {
            Reference< XPropertySetInfo > xPSI( m_xComponent->getPropertySetInfo() );
            m_bIsDialogElement = xPSI.is()
                              && xPSI->hasPropertyByName( PROPERTY_WIDTH )
                              && xPSI->hasPropertyByName( PROPERTY_HEIGHT )
                              && xPSI->hasPropertyByName( PROPERTY_POSITIONX )
                              && xPSI->hasPropertyByName( PROPERTY_POSITIONY );

            Reference< XChild > xAsChild( _rxIntrospectee, UNO_QUERY );
            if ( xAsChild.is() && !Reference< XForm >( _rxIntrospectee, UNO_QUERY ).is() )
            {
                if ( FormComponentType::GRIDCONTROL == classifyComponent( xAsChild->getParent() ) )
                {
                    m_nGridColumnType = classifyComponent( _rxIntrospectee );
                }
            }
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }

    Any SAL_CALL EventHandler::getPropertyValue( const OUString& _rPropertyName )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName );

        std::vector< ScriptEventDescriptor > aEvents;
        impl_getComponentScriptEvents_nothrow( aEvents );

        ScriptEventDescriptor aPropertyValue;
        for ( const ScriptEventDescriptor& rSCD : aEvents )
        {
            if  (   rEvent.sListenerClassName == rSCD.ListenerType
                &&  rEvent.sListenerMethodName == rSCD.EventMethod
                )
            {
                aPropertyValue = rSCD;
                break;
            }
        }

        return Any( aPropertyValue );
    }

    void SAL_CALL EventHandler::setPropertyValue( const OUString& _rPropertyName, const Any& _rValue )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName );

        ScriptEventDescriptor aNewScriptEvent;
        OSL_VERIFY( _rValue >>= aNewScriptEvent );

        ScriptEventDescriptor aOldScriptEvent;
        OSL_VERIFY( getPropertyValue( _rPropertyName ) >>= aOldScriptEvent );
        if ( aOldScriptEvent == aNewScriptEvent )
            return;

        if ( m_bIsDialogElement )
            impl_setDialogElementScriptEvent_nothrow( aNewScriptEvent );
        else
            impl_setFormComponentScriptEvent_nothrow( aNewScriptEvent );

        PropertyHandlerHelper::setContextDocumentModified( m_xContext );

        PropertyChangeEvent aEvent;
        aEvent.Source = m_xComponent;
        aEvent.PropertyHandle = rEvent.nId;
        aEvent.PropertyName = _rPropertyName;
        aEvent.OldValue <<= aOldScriptEvent;
        aEvent.NewValue <<= aNewScriptEvent;
        m_aPropertyListeners.notifyEach( &XPropertyChangeListener::propertyChange, aEvent );
    }

    Any SAL_CALL EventHandler::convertToPropertyValue( const OUString& _rPropertyNameconst Any& _rControlValue )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        OUString sNewScriptCode;
        OSL_VERIFY( _rControlValue >>= sNewScriptCode );

        std::vector< ScriptEventDescriptor > aAllAssignedEvents;
        impl_getComponentScriptEvents_nothrow( aAllAssignedEvents );

        const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName );
        ScriptEventDescriptor aAssignedScript = lcl_getAssignedScriptEvent( rEvent, aAllAssignedEvents );

        OSL_ENSURE( sNewScriptCode.isEmpty(), "EventHandler::convertToPropertyValue: cannot convert a non-empty display name!" );
        // Usually, there is no possibility for the user to change the content of an event binding directly in the
        // input field, this instead is done with the macro assignment dialog.
        // The only exception is the user pressing "DEL" while the control has the focus, in this case, we reset the
        // control content to an empty string. So this is the only scenario where this method is allowed to be called.

        // Strictly, we would be able to convert the display value to a property value,
        // using the "name (location, language)" format we used in convertToControlValue. However,
        // there is no need for this code...

        aAssignedScript.ScriptCode = sNewScriptCode;
        return Any( aAssignedScript );
    }

    Any SAL_CALL EventHandler::convertToControlValue( const OUString& /*_rPropertyName*/, const Any& _rPropertyValue, const Type& _rControlValueType )
    {
        ::osl::MutexGuard aGuard( m_aMutex );

        ScriptEventDescriptor aScriptEvent;
        OSL_VERIFY( _rPropertyValue >>= aScriptEvent );

        OSL_ENSURE( _rControlValueType.getTypeClass() == TypeClass_STRING,
            "EventHandler::convertToControlValue: unexpected ControlValue type class!" );

        OUString sScript( aScriptEvent.ScriptCode );
        if ( !sScript.isEmpty() )
        {
            // format is: "name (location, language)"
            try
            {
                // parse
                Reference< XUriReferenceFactory > xUriRefFac = UriReferenceFactory::create( m_xContext );
                Reference< XVndSunStarScriptUrlReference > xScriptUri( xUriRefFac->parse( sScript ), UNO_QUERY_THROW );

                OUStringBuffer aComposeBuffer;

                // name
                aComposeBuffer.append( xScriptUri->getName() );

                // location
                const OUString sLocation = xScriptUri->getParameter( u"location"_ustr );
                const OUString sLanguage = xScriptUri->getParameter( u"language"_ustr );

                if ( !(sLocation.isEmpty() && sLanguage.isEmpty()) )
                {
                    aComposeBuffer.append( " (" );

                    // location
                    OSL_ENSURE( !sLocation.isEmpty(), "EventHandler::convertToControlValue: unexpected: no location!" );
                    if ( !sLocation.isEmpty() )
                    {
                        aComposeBuffer.append( sLocation + ", " );
                    }

                    // language
                    if ( !sLanguage.isEmpty() )
                    {
                        aComposeBuffer.append( sLanguage );
                    }

                    aComposeBuffer.append( ')' );
                }

                sScript = aComposeBuffer.makeStringAndClear();
            }
            catchconst Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
            }
        }

        return Any( sScript );
    }

    PropertyState SAL_CALL EventHandler::getPropertyState( const OUString& /*_rPropertyName*/ )
    {
        return PropertyState_DIRECT_VALUE;
    }

    void SAL_CALL EventHandler::addPropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !_rxListener.is() )
            throw NullPointerException();
        m_aPropertyListeners.addInterface( _rxListener );
    }

    void SAL_CALL EventHandler::removePropertyChangeListener( const Reference< XPropertyChangeListener >& _rxListener )
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        m_aPropertyListeners.removeInterface( _rxListener );
    }

    Sequence< Property > SAL_CALL EventHandler::getSupportedProperties()
    {
        ::osl::MutexGuard aGuard( m_aMutex );
        if ( !m_bEventsMapInitialized )
        {
            m_bEventsMapInitialized = true;
            try
            {
                std::vector< Type > aListeners;
                impl_getComponentListenerTypes_nothrow( aListeners );

                OUString sListenerClassName;

                // loop through all listeners and all methods, and see which we can present at the UI
                for ( const Type& rListener : aListeners )
                {
                    // the programmatic name of the listener, to be used as "property" name
                    sListenerClassName = rListener.getTypeName();
                    OSL_ENSURE( !sListenerClassName.isEmpty(), "EventHandler::getSupportedProperties: strange - no listener name ..." );
                    if ( sListenerClassName.isEmpty() )
                        continue;

                    // loop through all methods
                    const Sequence<OUString> aEventMethods = comphelper::getEventMethodsForType( rListener );
                    for (const OUString& rMethod : aEventMethods)
                    {
                        EventDescription aEvent;
                        if ( !lcl_getEventDescriptionForMethod( rMethod, aEvent ) )
                            continue;

                        if ( !impl_filterMethod_nothrow( aEvent ) )
                            continue;

                        m_aEvents.emplace(
                            lcl_getEventPropertyName( sListenerClassName, rMethod ), aEvent );
                    }
                }

            }
            catchconst Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
            }
        }

        // sort them by ID - this is the relative ordering in the UI
        std::map< EventId, Property > aOrderedProperties;
        for (auto const& event : m_aEvents)
        {
            aOrderedProperties[ event.second.nId ] = Property(
                event.first, event.second.nId,
                ::cppu::UnoType<OUString>::get(),
                PropertyAttribute::BOUND );
        }

        return comphelper::mapValuesToSequence( aOrderedProperties );
    }

    Sequence< OUString > SAL_CALL EventHandler::getSupersededProperties( )
    {
        // none
        return Sequence< OUString >( );
    }

    Sequence< OUString > SAL_CALL EventHandler::getActuatingProperties( )
    {
        // none
        return Sequence< OUString >( );
    }

    LineDescriptor SAL_CALL EventHandler::describePropertyLine( const OUString& _rPropertyName,
        const Reference< XPropertyControlFactory >& _rxControlFactory )
    {
        if ( !_rxControlFactory.is() )
            throw NullPointerException();

        ::osl::MutexGuard aGuard( m_aMutex );

        LineDescriptor aDescriptor;

        aDescriptor.Control = _rxControlFactory->createPropertyControl( PropertyControlType::TextField, true );
        new PropertyControlExtender( aDescriptor.Control );

        const EventDescription& rEvent = impl_getEventForName_throw( _rPropertyName );
        aDescriptor.DisplayName = rEvent.sDisplayName;
        aDescriptor.HelpURL = HelpIdUrl::getHelpURL( rEvent.sHelpId );
        aDescriptor.PrimaryButtonId = OStringToOUString(rEvent.sUniqueBrowseId, RTL_TEXTENCODING_UTF8);
        aDescriptor.HasPrimaryButton = true;
        aDescriptor.Category = "Events";
        return aDescriptor;
    }

    sal_Bool SAL_CALL EventHandler::isComposable( const OUString& /*_rPropertyName*/ )
    {
        return false;
    }

    InteractiveSelectionResult SAL_CALL EventHandler::onInteractivePropertySelection( const OUString& _rPropertyName, sal_Bool /*_bPrimary*/, Any& /*_rData*/, const Reference< XObjectInspectorUI >& _rxInspectorUI )
    {
        if ( !_rxInspectorUI.is() )
            throw NullPointerException();

        ::osl::MutexGuard aGuard( m_aMutex );
        const EventDescription& rForEvent = impl_getEventForName_throw( _rPropertyName );

        std::vector< ScriptEventDescriptor > aAllAssignedEvents;
        impl_getComponentScriptEvents_nothrow( aAllAssignedEvents );

        // SvxMacroAssignDlg-compatible structure holding all event/assignments
        ::rtl::Reference< EventHolder >  pEventHolder( new EventHolder );

        for (auto const& event : m_aEvents)
        {
            // the script which is assigned to the current event (if any)
            ScriptEventDescriptor aAssignedScript = lcl_getAssignedScriptEvent( event.second, aAllAssignedEvents );
            pEventHolder->addEvent( event.second.nId, event.second.sListenerMethodName, aAssignedScript );
        }

        // the initial selection in the dialog
        const Sequence< OUString > aNames( pEventHolder->getElementNames() );
        const OUString* pChosenEvent = std::find( aNames.begin(), aNames.end(), rForEvent.sListenerMethodName );
        sal_uInt16 nInitialSelection = static_cast<sal_uInt16>( pChosenEvent - aNames.begin() );

        // the dialog
        SvxAbstractDialogFactory* pFactory = SvxAbstractDialogFactory::Create();

        ScopedVclPtr<VclAbstractDialog> pDialog( pFactory->CreateSvxMacroAssignDlg(
            PropertyHandlerHelper::getDialogParentFrame( m_xContext ),
            impl_getContextFrame_nothrow(),
            m_bIsDialogElement,
            pEventHolder,
            nInitialSelection
        ) );

        if ( !pDialog )
            return InteractiveSelectionResult_Cancelled;

        // DF definite problem here
        // OK & Cancel seem to be both returning 0
        if ( pDialog->Execute() == RET_CANCEL )
            return InteractiveSelectionResult_Cancelled;

        try
        {
            for (auto const& event : m_aEvents)
            {
                ScriptEventDescriptor aScriptDescriptor( pEventHolder->getNormalizedDescriptorByName( event.second.sListenerMethodName ) );

                // set the new "property value"
                setPropertyValue(
                    lcl_getEventPropertyName( event.second.sListenerClassName, event.second.sListenerMethodName ),
                    Any( aScriptDescriptor )
                );
            }
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }

        return InteractiveSelectionResult_Success;
    }

    void SAL_CALL EventHandler::actuatingPropertyChanged( const OUString& /*_rActuatingPropertyName*/, const Any& /*_rNewValue*/, const Any& /*_rOldValue*/, const Reference< XObjectInspectorUI >& /*_rxInspectorUI*/, sal_Bool /*_bFirstTimeInit*/ )
    {
        OSL_FAIL( "EventHandler::actuatingPropertyChanged: no actuating properties -> no callback (well, this is how it *should* be!)" );
    }

    IMPLEMENT_FORWARD_XCOMPONENT( EventHandler, EventHandler_Base )

    void SAL_CALL EventHandler::disposing()
    {
        EventMap().swap(m_aEvents);
        m_xComponent.clear();
    }

    sal_Bool SAL_CALL EventHandler::suspend( sal_Bool /*_bSuspend*/ )
    {
        return true;
    }

    Reference< XFrame > EventHandler::impl_getContextFrame_nothrow() const
    {
        Reference< XFrame > xContextFrame;

        try
        {
            Reference< XModel > xContextDocument( PropertyHandlerHelper::getContextDocument(m_xContext), UNO_QUERY_THROW );
            Reference< XController > xController( xContextDocument->getCurrentController(), UNO_SET_THROW );
            xContextFrame.set( xController->getFrame(), UNO_SET_THROW );
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }

        return xContextFrame;
    }

    sal_Int32 EventHandler::impl_getComponentIndexInParent_throw() const
    {
        Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW );
        Reference< XIndexAccess > xParentAsIndexAccess( xChild->getParent(), UNO_QUERY_THROW );

        // get the index of the inspected object within its parent container
        sal_Int32 nElements = xParentAsIndexAccess->getCount();
        for ( sal_Int32 i=0; i<nElements; ++i )
        {
            Reference< XInterface > xElement( xParentAsIndexAccess->getByIndex( i ), UNO_QUERY_THROW );
            if ( xElement == m_xComponent )
                return i;
        }
        throw NoSuchElementException();
    }

    void EventHandler::impl_getFormComponentScriptEvents_nothrow( std::vector < ScriptEventDescriptor >& _out_rEvents ) const
    {
        _out_rEvents.clear();
        try
        {
            Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW );
            Reference< XEventAttacherManager > xEventManager( xChild->getParent(), UNO_QUERY_THROW );
            comphelper::sequenceToContainer(_out_rEvents, xEventManager->getScriptEvents( impl_getComponentIndexInParent_throw() ));

            // the form component script API has unqualified listener names, but for normalization
            // purpose, we want fully qualified ones
            for ( ScriptEventDescriptor& rSED :  _out_rEvents)
            {
                rSED.ListenerType = lcl_getQualifiedKnownListenerName( rSED );
            }
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }

    void EventHandler::impl_getComponentListenerTypes_nothrow( std::vector< Type >& _out_rTypes ) const
    {
        _out_rTypes.clear();
        try
        {
            // we use a set to avoid duplicates
            TypeBag aListeners;

            Reference< XIntrospection > xIntrospection = theIntrospection::get( m_xContext );

            // --- model listeners
            lcl_addListenerTypesFor_throw(
                m_xComponent, xIntrospection, aListeners );

            // --- "secondary component" (usually: "control" listeners)
            {
                Reference< XInterface > xSecondaryComponent( impl_getSecondaryComponentForEventInspection_throw() );
                lcl_addListenerTypesFor_throw( xSecondaryComponent, xIntrospection, aListeners );
                ::comphelper::disposeComponent( xSecondaryComponent );
            }

            // now that they're disambiguated, copy these types into our member
            _out_rTypes.insert( _out_rTypes.end(), aListeners.begin(), aListeners.end() );
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }

    void EventHandler::impl_getDialogElementScriptEvents_nothrow( std::vector < ScriptEventDescriptor >& _out_rEvents ) const
    {
        _out_rEvents.clear();
        try
        {
            Reference< XScriptEventsSupplier > xEventsSupplier( m_xComponent, UNO_QUERY_THROW );
            Reference< XNameContainer > xEvents( xEventsSupplier->getEvents(), UNO_SET_THROW );
            Sequence< OUString > aEventNames( xEvents->getElementNames() );

            sal_Int32 nEventCount = aEventNames.getLength();
            _out_rEvents.resize( nEventCount );

            for( sal_Int32 i = 0; i < nEventCount; ++i )
                OSL_VERIFY( xEvents->getByName( aEventNames[i] ) >>= _out_rEvents[i] );
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }

    Reference< XInterface > EventHandler::impl_getSecondaryComponentForEventInspection_throw( ) const
    {
        Reference< XInterface > xReturn;

        // if it's a form, create a form controller for the additional events
        Reference< XForm > xComponentAsForm( m_xComponent, UNO_QUERY );
        if ( xComponentAsForm.is() )
        {
            Reference< XTabControllerModel > xComponentAsTCModel( m_xComponent, UNO_QUERY_THROW );
            Reference< XFormController > xController = FormController::create( m_xContext );
            xController->setModel( xComponentAsTCModel );

            xReturn = xController;
        }
        else
        {
            OUString sControlService;
            OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_DEFAULTCONTROL ) >>= sControlService );

            xReturn = m_xContext->getServiceManager()->createInstanceWithContext( sControlService, m_xContext );
        }
        return xReturn;
    }

    const EventDescription& EventHandler::impl_getEventForName_throw( const OUString&&nbsp;_rPropertyName ) const
    {
        EventMap::const_iterator pos = m_aEvents.find( _rPropertyName );
        if ( pos == m_aEvents.end() )
            throw UnknownPropertyException(_rPropertyName);
        return pos->second;
    }

    namespace
    {
        bool lcl_endsWith( std::u16string_view _rText, std::u16string_view _rCheck )
        {
            size_t nTextLen = _rText.size();
            size_t nCheckLen = _rCheck.size();
            if ( nCheckLen > nTextLen )
                return false;

            return _rText.find( _rCheck ) == ( nTextLen - nCheckLen );
        }
    }

    void EventHandler::impl_setFormComponentScriptEvent_nothrow( const ScriptEventDescriptor& _rScriptEvent )
    {
        try
        {
            OUString sScriptCode( _rScriptEvent.ScriptCode );
            OUString sScriptType( _rScriptEvent.ScriptType );
            bool bResetScript = sScriptCode.isEmpty();

            sal_Int32 nObjectIndex = impl_getComponentIndexInParent_throw();
            Reference< XChild > xChild( m_xComponent, UNO_QUERY_THROW );
            Reference< XEventAttacherManager > xEventManager( xChild->getParent(), UNO_QUERY_THROW );
            std::vector< ScriptEventDescriptor > aEvents;
            comphelper::sequenceToContainer( aEvents, xEventManager->getScriptEvents( nObjectIndex ) );

            // is there already a registered script for this event?
            sal_Int32 eventCount = aEvents.size(), event = 0;
            for ( event = 0; event < eventCount; ++event )
            {
                ScriptEventDescriptor* pEvent = &aEvents[event];
                if  (   ( pEvent->EventMethod == _rScriptEvent.EventMethod )
                    &&  ( lcl_endsWith( _rScriptEvent.ListenerType, pEvent->ListenerType ) )
                          // (strange enough, the events we get from getScriptEvents are not fully qualified)
                    )
                {
                    // yes
                    if ( !bResetScript )
                    {
                        // set to something non-empty -> overwrite
                        pEvent->ScriptCode = sScriptCode;
                        pEvent->ScriptType = sScriptType;
                    }
                    else
                    {
                        // set to empty -> remove from vector
                        aEvents.erase(aEvents.begin() + event );
                        --eventCount;
                    }
                    break;
                }
            }
            if ( ( event >= eventCount ) && !bResetScript )
            {
                // no, did not find it -> append
                aEvents.push_back( _rScriptEvent );
            }

            xEventManager->revokeScriptEvents( nObjectIndex );
            xEventManager->registerScriptEvents( nObjectIndex, comphelper::containerToSequence(aEvents) );

            PropertyHandlerHelper::setContextDocumentModified( m_xContext );
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }

    void EventHandler::impl_setDialogElementScriptEvent_nothrow( const ScriptEventDescriptor& _rScriptEvent )
    {
        try
        {
            OUString sScriptCode( _rScriptEvent.ScriptCode );
            bool bResetScript =  sScriptCode.isEmpty();

            Reference< XScriptEventsSupplier > xEventsSupplier( m_xComponent, UNO_QUERY_THROW );
            Reference< XNameContainer > xEvents( xEventsSupplier->getEvents(), UNO_SET_THROW );

            OUString sCompleteName =
                _rScriptEvent.ListenerType +
                "::" +
                _rScriptEvent.EventMethod;

            bool bExists = xEvents->hasByName( sCompleteName );

            if ( bResetScript )
            {
                if ( bExists )
                    xEvents->removeByName( sCompleteName );
            }
            else
            {
                Any aNewValue; aNewValue <<= _rScriptEvent;

                if ( bExists )
                    xEvents->replaceByName( sCompleteName, aNewValue );
                else
                    xEvents->insertByName( sCompleteName, aNewValue );
            }
        }
        catchconst Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
        }
    }

    bool EventHandler::impl_filterMethod_nothrow( const EventDescription& _rEvent ) const
    {
        // some (control-triggered) events do not make sense for certain grid control columns. However,
        // our mechanism to retrieve control-triggered events does not know about this, so we do some
        // late filtering here.
        switch ( m_nGridColumnType )
        {
        case FormComponentType::COMBOBOX:
            if ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId )
                return false;
            break;
        case FormComponentType::LISTBOX:
            if  (   ( UID_BRWEVT_CHANGED == _rEvent.sUniqueBrowseId )
                ||  ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId )
                )
                return false;
            break;
        }

        return true;
    }

// namespace pcr

extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
extensions_propctrlr_EventHandler_get_implementation(
    css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&)
{
    return cppu::acquire(new pcr::EventHandler(context));
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=96 H=99 G=97

¤ Dauer der Verarbeitung: 0.13 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.