1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
21 #include "eventhandler.hxx"
23 #include <propctrlr.h>
24 #include "formbrowsertools.hxx"
25 #include <strings.hrc>
26 #include "formstrings.hxx"
27 #include "handlerhelper.hxx"
28 #include "modulepcr.hxx"
29 #include "pcrcommon.hxx"
30 #include "propertycontrolextender.hxx"
32 #include <com/sun/star/awt/XTabControllerModel.hpp>
33 #include <com/sun/star/beans/PropertyAttribute.hpp>
34 #include <com/sun/star/beans/UnknownPropertyException.hpp>
35 #include <com/sun/star/beans/theIntrospection.hpp>
36 #include <com/sun/star/beans/XIntrospectionAccess.hpp>
37 #include <com/sun/star/container/NoSuchElementException.hpp>
38 #include <com/sun/star/container/XChild.hpp>
39 #include <com/sun/star/container/XIndexAccess.hpp>
40 #include <com/sun/star/container/XNameContainer.hpp>
41 #include <com/sun/star/container/XNameReplace.hpp>
42 #include <com/sun/star/form/FormComponentType.hpp>
43 #include <com/sun/star/form/XForm.hpp>
44 #include <com/sun/star/form/runtime/FormController.hpp>
45 #include <com/sun/star/inspection/PropertyControlType.hpp>
46 #include <com/sun/star/lang/NullPointerException.hpp>
47 #include <com/sun/star/script/XEventAttacherManager.hpp>
48 #include <com/sun/star/script/XScriptEventsSupplier.hpp>
49 #include <com/sun/star/uri/UriReferenceFactory.hpp>
50 #include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
52 #include <comphelper/namedvaluecollection.hxx>
53 #include <comphelper/evtmethodhelper.hxx>
54 #include <comphelper/propertyvalue.hxx>
55 #include <comphelper/sequence.hxx>
56 #include <comphelper/types.hxx>
57 #include <cppuhelper/implbase.hxx>
58 #include <cppuhelper/supportsservice.hxx>
59 #include <rtl/ref.hxx>
60 #include <rtl/ustrbuf.hxx>
61 #include <svx/svxdlg.hxx>
62 #include <comphelper/diagnose_ex.hxx>
67 #include <string_view>
73 using ::com::sun::star::uno::Reference
;
74 using ::com::sun::star::uno::XComponentContext
;
75 using ::com::sun::star::uno::Any
;
76 using ::com::sun::star::uno::TypeClass_STRING
;
77 using ::com::sun::star::uno::Type
;
78 using ::com::sun::star::beans::theIntrospection
;
79 using ::com::sun::star::beans::XPropertyChangeListener
;
80 using ::com::sun::star::beans::Property
;
81 using ::com::sun::star::beans::PropertyState
;
82 using ::com::sun::star::beans::PropertyState_DIRECT_VALUE
;
83 using ::com::sun::star::uno::Sequence
;
84 using ::com::sun::star::script::ScriptEventDescriptor
;
85 using ::com::sun::star::script::XScriptEventsSupplier
;
86 using ::com::sun::star::lang::NullPointerException
;
87 using ::com::sun::star::uno::Exception
;
88 using ::com::sun::star::container::XChild
;
89 using ::com::sun::star::container::XIndexAccess
;
90 using ::com::sun::star::script::XEventAttacherManager
;
91 using ::com::sun::star::uno::UNO_QUERY
;
92 using ::com::sun::star::uno::UNO_QUERY_THROW
;
93 using ::com::sun::star::uno::XInterface
;
94 using ::com::sun::star::beans::XIntrospection
;
95 using ::com::sun::star::beans::XIntrospectionAccess
;
96 using ::com::sun::star::container::XNameContainer
;
97 using ::com::sun::star::awt::XTabControllerModel
;
98 using ::com::sun::star::form::XForm
;
99 using ::com::sun::star::form::runtime::FormController
;
100 using ::com::sun::star::form::runtime::XFormController
;
101 using ::com::sun::star::beans::UnknownPropertyException
;
102 using ::com::sun::star::container::NoSuchElementException
;
103 using ::com::sun::star::beans::XPropertySetInfo
;
104 using ::com::sun::star::container::XNameReplace
;
105 using ::com::sun::star::beans::PropertyValue
;
106 using ::com::sun::star::inspection::LineDescriptor
;
107 using ::com::sun::star::inspection::XPropertyControlFactory
;
108 using ::com::sun::star::inspection::InteractiveSelectionResult
;
109 using ::com::sun::star::inspection::InteractiveSelectionResult_Cancelled
;
110 using ::com::sun::star::inspection::InteractiveSelectionResult_Success
;
111 using ::com::sun::star::inspection::XObjectInspectorUI
;
112 using ::com::sun::star::beans::PropertyChangeEvent
;
113 using ::com::sun::star::frame::XFrame
;
114 using ::com::sun::star::frame::XModel
;
115 using ::com::sun::star::frame::XController
;
116 using ::com::sun::star::uno::UNO_SET_THROW
;
117 using com::sun::star::uri::UriReferenceFactory
;
118 using com::sun::star::uri::XUriReferenceFactory
;
119 using com::sun::star::uri::XVndSunStarScriptUrlReference
;
121 namespace PropertyControlType
= css::inspection::PropertyControlType
;
122 namespace PropertyAttribute
= css::beans::PropertyAttribute
;
123 namespace FormComponentType
= css::form::FormComponentType
;
125 EventDescription::EventDescription( EventId _nId
, std::u16string_view listenerClassName
,
126 std::u16string_view listenerMethodName
, TranslateId pDisplayNameResId
, OUString _sHelpId
, OString _sUniqueBrowseId
)
127 :sDisplayName(PcrRes( pDisplayNameResId
))
128 ,sListenerClassName( listenerClassName
)
129 ,sListenerMethodName( listenerMethodName
)
130 ,sHelpId(std::move( _sHelpId
))
131 ,sUniqueBrowseId(std::move( _sUniqueBrowseId
))
138 #define DESCRIBE_EVENT( map, listener, method, id_postfix ) \
141 EventDescription( ++nEventId, u"com.sun.star." listener, u"" method, RID_STR_EVT_##id_postfix, HID_EVT_##id_postfix, UID_BRWEVT_##id_postfix ) )
143 bool lcl_getEventDescriptionForMethod( const OUString
& _rMethodName
, EventDescription
& _out_rDescription
)
145 static EventMap s_aKnownEvents
= []() {
147 sal_Int32 nEventId
= 0;
149 DESCRIBE_EVENT(aMap
, "form.XApproveActionListener", "approveAction", APPROVEACTIONPERFORMED
);
150 DESCRIBE_EVENT(aMap
, "awt.XActionListener", "actionPerformed", ACTIONPERFORMED
);
151 DESCRIBE_EVENT(aMap
, "form.XChangeListener", "changed", CHANGED
);
152 DESCRIBE_EVENT(aMap
, "awt.XTextListener", "textChanged", TEXTCHANGED
);
153 DESCRIBE_EVENT(aMap
, "awt.XItemListener", "itemStateChanged", ITEMSTATECHANGED
);
154 DESCRIBE_EVENT(aMap
, "awt.XFocusListener", "focusGained", FOCUSGAINED
);
155 DESCRIBE_EVENT(aMap
, "awt.XFocusListener", "focusLost", FOCUSLOST
);
156 DESCRIBE_EVENT(aMap
, "awt.XKeyListener", "keyPressed", KEYTYPED
);
157 DESCRIBE_EVENT(aMap
, "awt.XKeyListener", "keyReleased", KEYUP
);
158 DESCRIBE_EVENT(aMap
, "awt.XMouseListener", "mouseEntered", MOUSEENTERED
);
159 DESCRIBE_EVENT(aMap
, "awt.XMouseMotionListener", "mouseDragged", MOUSEDRAGGED
);
160 DESCRIBE_EVENT(aMap
, "awt.XMouseMotionListener", "mouseMoved", MOUSEMOVED
);
161 DESCRIBE_EVENT(aMap
, "awt.XMouseListener", "mousePressed", MOUSEPRESSED
);
162 DESCRIBE_EVENT(aMap
, "awt.XMouseListener", "mouseReleased", MOUSERELEASED
);
163 DESCRIBE_EVENT(aMap
, "awt.XMouseListener", "mouseExited", MOUSEEXITED
);
164 DESCRIBE_EVENT(aMap
, "form.XResetListener", "approveReset", APPROVERESETTED
);
165 DESCRIBE_EVENT(aMap
, "form.XResetListener", "resetted", RESETTED
);
166 DESCRIBE_EVENT(aMap
, "form.XSubmitListener", "approveSubmit", SUBMITTED
);
167 DESCRIBE_EVENT(aMap
, "form.XUpdateListener", "approveUpdate", BEFOREUPDATE
);
168 DESCRIBE_EVENT(aMap
, "form.XUpdateListener", "updated", AFTERUPDATE
);
169 DESCRIBE_EVENT(aMap
, "form.XLoadListener", "loaded", LOADED
);
170 DESCRIBE_EVENT(aMap
, "form.XLoadListener", "reloading", RELOADING
);
171 DESCRIBE_EVENT(aMap
, "form.XLoadListener", "reloaded", RELOADED
);
172 DESCRIBE_EVENT(aMap
, "form.XLoadListener", "unloading", UNLOADING
);
173 DESCRIBE_EVENT(aMap
, "form.XLoadListener", "unloaded", UNLOADED
);
174 DESCRIBE_EVENT(aMap
, "form.XConfirmDeleteListener", "confirmDelete", CONFIRMDELETE
);
175 DESCRIBE_EVENT(aMap
, "sdb.XRowSetApproveListener", "approveRowChange", APPROVEROWCHANGE
);
176 DESCRIBE_EVENT(aMap
, "sdbc.XRowSetListener", "rowChanged", ROWCHANGE
);
177 DESCRIBE_EVENT(aMap
, "sdb.XRowSetApproveListener", "approveCursorMove", POSITIONING
);
178 DESCRIBE_EVENT(aMap
, "sdbc.XRowSetListener", "cursorMoved", POSITIONED
);
179 DESCRIBE_EVENT(aMap
, "form.XDatabaseParameterListener", "approveParameter", APPROVEPARAMETER
);
180 DESCRIBE_EVENT(aMap
, "sdb.XSQLErrorListener", "errorOccured", ERROROCCURRED
);
181 DESCRIBE_EVENT(aMap
, "awt.XAdjustmentListener", "adjustmentValueChanged", ADJUSTMENTVALUECHANGED
);
186 EventMap::const_iterator pos
= s_aKnownEvents
.find( _rMethodName
);
187 if ( pos
== s_aKnownEvents
.end() )
190 _out_rDescription
= pos
->second
;
194 OUString
lcl_getEventPropertyName( std::u16string_view _rListenerClassName
, std::u16string_view _rMethodName
)
196 return _rListenerClassName
+ OUStringChar(';') + _rMethodName
;
199 ScriptEventDescriptor
lcl_getAssignedScriptEvent( const EventDescription
& _rEvent
, const std::vector
< ScriptEventDescriptor
>& _rAllAssignedMacros
)
201 ScriptEventDescriptor aScriptEvent
;
202 // for the case there is actually no event assigned, initialize at least ListenerType and MethodName,
203 // so this ScriptEventDescriptor properly describes the given event
204 aScriptEvent
.ListenerType
= _rEvent
.sListenerClassName
;
205 aScriptEvent
.EventMethod
= _rEvent
.sListenerMethodName
;
207 for ( const ScriptEventDescriptor
& rSED
: _rAllAssignedMacros
)
209 if ( rSED
.ListenerType
!= _rEvent
.sListenerClassName
210 || rSED
.EventMethod
!= _rEvent
.sListenerMethodName
214 if ( rSED
.ScriptCode
.isEmpty()
215 || rSED
.ScriptType
.isEmpty()
218 OSL_FAIL( "lcl_getAssignedScriptEvent: me thinks this should not happen!" );
224 if ( aScriptEvent
.ScriptType
!= "StarBasic" )
227 // this is an old-style macro specification:
228 // [document|application]:Library.Module.Function
229 // we need to translate this to the new-style macro specification
230 // vnd.sun.star.script:Library.Module.Function?language=Basic&location=[document|application]
232 sal_Int32 nPrefixLen
= aScriptEvent
.ScriptCode
.indexOf( ':' );
233 OSL_ENSURE( nPrefixLen
> 0, "lcl_getAssignedScriptEvent: illegal location!" );
234 std::u16string_view sLocation
= aScriptEvent
.ScriptCode
.subView( 0, nPrefixLen
);
235 std::u16string_view sMacroPath
= aScriptEvent
.ScriptCode
.subView( nPrefixLen
+ 1 );
237 aScriptEvent
.ScriptCode
=
238 OUString::Concat("vnd.sun.star.script:") +
240 "?language=Basic&location=" +
243 // also, this new-style spec requires the script code to be "Script" instead of "StarBasic"
244 aScriptEvent
.ScriptType
= "Script";
249 OUString
lcl_getQualifiedKnownListenerName( const ScriptEventDescriptor
& _rFormComponentEventDescriptor
)
251 EventDescription aKnownEvent
;
252 if ( lcl_getEventDescriptionForMethod( _rFormComponentEventDescriptor
.EventMethod
, aKnownEvent
) )
253 return aKnownEvent
.sListenerClassName
;
254 OSL_FAIL( "lcl_getQualifiedKnownListenerName: unknown method name!" );
255 // somebody assigned an script to a form component event which we don't know
256 // Speaking strictly, this is not really an error - it is possible to do
257 // this programmatically -, but it should rarely happen, since it's not possible
259 return _rFormComponentEventDescriptor
.ListenerType
;
262 typedef std::set
< Type
, TypeLessByName
> TypeBag
;
264 void lcl_addListenerTypesFor_throw( const Reference
< XInterface
>& _rxComponent
,
265 const Reference
< XIntrospection
>& _rxIntrospection
, TypeBag
& _out_rTypes
)
267 if ( !_rxComponent
.is() )
269 OSL_PRECOND( _rxIntrospection
.is(), "lcl_addListenerTypesFor_throw: this will crash!" );
271 Reference
< XIntrospectionAccess
> xIntrospectionAccess(
272 _rxIntrospection
->inspect( Any( _rxComponent
) ), UNO_SET_THROW
);
274 const Sequence
< Type
> aListeners( xIntrospectionAccess
->getSupportedListeners() );
276 std::copy( aListeners
.begin(), aListeners
.end(),
277 std::insert_iterator
< TypeBag
>( _out_rTypes
, _out_rTypes
.begin() ) );
281 typedef ::cppu::WeakImplHelper
< css::container::XNameReplace
286 /* A UNO component holding assigned event descriptions, for use with a SvxMacroAssignDlg */
287 class EventHolder
: public EventHolder_Base
290 typedef std::unordered_map
< OUString
, ScriptEventDescriptor
> EventMap
;
291 typedef std::map
< EventId
, OUString
> EventMapIndexAccess
;
293 EventMap m_aEventNameAccess
;
294 EventMapIndexAccess m_aEventIndexAccess
;
299 void addEvent( EventId _nId
, const OUString
& _rEventName
, const ScriptEventDescriptor
& _rScriptEvent
);
301 /** effectively the same as getByName, but instead of converting the ScriptEventDescriptor to the weird
302 format used by the macro assignment dialog, it is returned directly
304 ScriptEventDescriptor
getNormalizedDescriptorByName( const OUString
& _rEventName
) const;
307 virtual void SAL_CALL
replaceByName( const OUString
& _rName
, const Any
& aElement
) override
;
308 virtual Any SAL_CALL
getByName( const OUString
& _rName
) override
;
309 virtual Sequence
< OUString
> SAL_CALL
getElementNames( ) override
;
310 virtual sal_Bool SAL_CALL
hasByName( const OUString
& _rName
) override
;
311 virtual Type SAL_CALL
getElementType( ) override
;
312 virtual sal_Bool SAL_CALL
hasElements( ) override
;
315 virtual ~EventHolder( ) override
;
318 ScriptEventDescriptor
const & impl_getDescriptor_throw( const OUString
& _rEventName
) const;
323 EventHolder::EventHolder()
327 EventHolder::~EventHolder()
329 m_aEventNameAccess
.clear();
330 m_aEventIndexAccess
.clear();
333 void EventHolder::addEvent( EventId _nId
, const OUString
& _rEventName
, const ScriptEventDescriptor
& _rScriptEvent
)
335 std::pair
< EventMap::iterator
, bool > insertionResult
=
336 m_aEventNameAccess
.emplace( _rEventName
, _rScriptEvent
);
337 OSL_ENSURE( insertionResult
.second
, "EventHolder::addEvent: there already was a MacroURL for this event!" );
338 m_aEventIndexAccess
[ _nId
] = _rEventName
;
341 ScriptEventDescriptor
EventHolder::getNormalizedDescriptorByName( const OUString
& _rEventName
) const
343 return impl_getDescriptor_throw( _rEventName
);
346 ScriptEventDescriptor
const & EventHolder::impl_getDescriptor_throw( const OUString
& _rEventName
) const
348 EventMap::const_iterator pos
= m_aEventNameAccess
.find( _rEventName
);
349 if ( pos
== m_aEventNameAccess
.end() )
350 throw NoSuchElementException( OUString(), *const_cast< EventHolder
* >( this ) );
354 void SAL_CALL
EventHolder::replaceByName( const OUString
& _rName
, const Any
& _rElement
)
356 EventMap::iterator pos
= m_aEventNameAccess
.find( _rName
);
357 if ( pos
== m_aEventNameAccess
.end() )
358 throw NoSuchElementException( OUString(), *this );
360 Sequence
< PropertyValue
> aScriptDescriptor
;
361 OSL_VERIFY( _rElement
>>= aScriptDescriptor
);
363 ::comphelper::NamedValueCollection
aExtractor( aScriptDescriptor
);
365 pos
->second
.ScriptType
= aExtractor
.getOrDefault( u
"EventType"_ustr
, OUString() );
366 pos
->second
.ScriptCode
= aExtractor
.getOrDefault( u
"Script"_ustr
, OUString() );
369 Any SAL_CALL
EventHolder::getByName( const OUString
& _rName
)
371 ScriptEventDescriptor
aDescriptor( impl_getDescriptor_throw( _rName
) );
373 Sequence
< PropertyValue
> aScriptDescriptor
{
374 comphelper::makePropertyValue(u
"EventType"_ustr
, aDescriptor
.ScriptType
),
375 comphelper::makePropertyValue(u
"Script"_ustr
, aDescriptor
.ScriptCode
)
378 return Any( aScriptDescriptor
);
381 Sequence
< OUString
> SAL_CALL
EventHolder::getElementNames( )
383 Sequence
< OUString
> aReturn( m_aEventIndexAccess
.size() );
384 OUString
* pReturn
= aReturn
.getArray();
386 // SvxMacroAssignDlg has a weird API: It expects a XNameReplace, means a container whose
387 // main access method is by name. In its UI, it shows the possible events in exactly the
388 // order in which XNameAccess::getElementNames returns them.
389 // However, SvxMacroAssignDlg *also* takes an index for the initial selection, which is
390 // relative to the sequence returned by XNameAccess::getElementNames.
391 // This is IMO weird, since it mixes index access with name access, which decreases efficiency
392 // of the implementation.
393 // Well, it means we're forced to return the events in getElementNames in exactly the same as they
394 // appear in the property browser UI.
395 for (auto const& elem
: m_aEventIndexAccess
)
397 *pReturn
= elem
.second
;
403 sal_Bool SAL_CALL
EventHolder::hasByName( const OUString
& _rName
)
405 EventMap::const_iterator pos
= m_aEventNameAccess
.find( _rName
);
406 return pos
!= m_aEventNameAccess
.end();
409 Type SAL_CALL
EventHolder::getElementType( )
411 return cppu::UnoType
<Sequence
< PropertyValue
>>::get();
414 sal_Bool SAL_CALL
EventHolder::hasElements( )
416 return !m_aEventNameAccess
.empty();
420 EventHandler::EventHandler( const Reference
< XComponentContext
>& _rxContext
)
421 :EventHandler_Base( m_aMutex
)
422 ,m_xContext( _rxContext
)
423 ,m_aPropertyListeners( m_aMutex
)
424 ,m_bEventsMapInitialized( false )
425 ,m_bIsDialogElement( false )
426 ,m_nGridColumnType( -1 )
430 EventHandler::~EventHandler()
434 OUString SAL_CALL
EventHandler::getImplementationName( )
436 return u
"com.sun.star.comp.extensions.EventHandler"_ustr
;
439 sal_Bool SAL_CALL
EventHandler::supportsService( const OUString
& ServiceName
)
441 return cppu::supportsService(this, ServiceName
);
444 Sequence
< OUString
> SAL_CALL
EventHandler::getSupportedServiceNames( )
446 return { u
"com.sun.star.form.inspection.EventHandler"_ustr
};
449 void SAL_CALL
EventHandler::inspect( const Reference
< XInterface
>& _rxIntrospectee
)
451 ::osl::MutexGuard
aGuard( m_aMutex
);
453 if ( !_rxIntrospectee
.is() )
454 throw NullPointerException();
456 m_xComponent
.set( _rxIntrospectee
, UNO_QUERY_THROW
);
458 m_bEventsMapInitialized
= false;
459 EventMap().swap(m_aEvents
);
461 m_bIsDialogElement
= false;
462 m_nGridColumnType
= -1;
465 Reference
< XPropertySetInfo
> xPSI( m_xComponent
->getPropertySetInfo() );
466 m_bIsDialogElement
= xPSI
.is()
467 && xPSI
->hasPropertyByName( PROPERTY_WIDTH
)
468 && xPSI
->hasPropertyByName( PROPERTY_HEIGHT
)
469 && xPSI
->hasPropertyByName( PROPERTY_POSITIONX
)
470 && xPSI
->hasPropertyByName( PROPERTY_POSITIONY
);
472 Reference
< XChild
> xAsChild( _rxIntrospectee
, UNO_QUERY
);
473 if ( xAsChild
.is() && !Reference
< XForm
>( _rxIntrospectee
, UNO_QUERY
).is() )
475 if ( FormComponentType::GRIDCONTROL
== classifyComponent( xAsChild
->getParent() ) )
477 m_nGridColumnType
= classifyComponent( _rxIntrospectee
);
481 catch( const Exception
& )
483 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
487 Any SAL_CALL
EventHandler::getPropertyValue( const OUString
& _rPropertyName
)
489 ::osl::MutexGuard
aGuard( m_aMutex
);
491 const EventDescription
& rEvent
= impl_getEventForName_throw( _rPropertyName
);
493 std::vector
< ScriptEventDescriptor
> aEvents
;
494 impl_getComponentScriptEvents_nothrow( aEvents
);
496 ScriptEventDescriptor aPropertyValue
;
497 for ( const ScriptEventDescriptor
& rSCD
: aEvents
)
499 if ( rEvent
.sListenerClassName
== rSCD
.ListenerType
500 && rEvent
.sListenerMethodName
== rSCD
.EventMethod
503 aPropertyValue
= rSCD
;
508 return Any( aPropertyValue
);
511 void SAL_CALL
EventHandler::setPropertyValue( const OUString
& _rPropertyName
, const Any
& _rValue
)
513 ::osl::MutexGuard
aGuard( m_aMutex
);
515 const EventDescription
& rEvent
= impl_getEventForName_throw( _rPropertyName
);
517 ScriptEventDescriptor aNewScriptEvent
;
518 OSL_VERIFY( _rValue
>>= aNewScriptEvent
);
520 ScriptEventDescriptor aOldScriptEvent
;
521 OSL_VERIFY( getPropertyValue( _rPropertyName
) >>= aOldScriptEvent
);
522 if ( aOldScriptEvent
== aNewScriptEvent
)
525 if ( m_bIsDialogElement
)
526 impl_setDialogElementScriptEvent_nothrow( aNewScriptEvent
);
528 impl_setFormComponentScriptEvent_nothrow( aNewScriptEvent
);
530 PropertyHandlerHelper::setContextDocumentModified( m_xContext
);
532 PropertyChangeEvent aEvent
;
533 aEvent
.Source
= m_xComponent
;
534 aEvent
.PropertyHandle
= rEvent
.nId
;
535 aEvent
.PropertyName
= _rPropertyName
;
536 aEvent
.OldValue
<<= aOldScriptEvent
;
537 aEvent
.NewValue
<<= aNewScriptEvent
;
538 m_aPropertyListeners
.notifyEach( &XPropertyChangeListener::propertyChange
, aEvent
);
541 Any SAL_CALL
EventHandler::convertToPropertyValue( const OUString
& _rPropertyName
, const Any
& _rControlValue
)
543 ::osl::MutexGuard
aGuard( m_aMutex
);
545 OUString sNewScriptCode
;
546 OSL_VERIFY( _rControlValue
>>= sNewScriptCode
);
548 std::vector
< ScriptEventDescriptor
> aAllAssignedEvents
;
549 impl_getComponentScriptEvents_nothrow( aAllAssignedEvents
);
551 const EventDescription
& rEvent
= impl_getEventForName_throw( _rPropertyName
);
552 ScriptEventDescriptor aAssignedScript
= lcl_getAssignedScriptEvent( rEvent
, aAllAssignedEvents
);
554 OSL_ENSURE( sNewScriptCode
.isEmpty(), "EventHandler::convertToPropertyValue: cannot convert a non-empty display name!" );
555 // Usually, there is no possibility for the user to change the content of an event binding directly in the
556 // input field, this instead is done with the macro assignment dialog.
557 // The only exception is the user pressing "DEL" while the control has the focus, in this case, we reset the
558 // control content to an empty string. So this is the only scenario where this method is allowed to be called.
560 // Strictly, we would be able to convert the display value to a property value,
561 // using the "name (location, language)" format we used in convertToControlValue. However,
562 // there is no need for this code...
564 aAssignedScript
.ScriptCode
= sNewScriptCode
;
565 return Any( aAssignedScript
);
568 Any SAL_CALL
EventHandler::convertToControlValue( const OUString
& /*_rPropertyName*/, const Any
& _rPropertyValue
, const Type
& _rControlValueType
)
570 ::osl::MutexGuard
aGuard( m_aMutex
);
572 ScriptEventDescriptor aScriptEvent
;
573 OSL_VERIFY( _rPropertyValue
>>= aScriptEvent
);
575 OSL_ENSURE( _rControlValueType
.getTypeClass() == TypeClass_STRING
,
576 "EventHandler::convertToControlValue: unexpected ControlValue type class!" );
578 OUString
sScript( aScriptEvent
.ScriptCode
);
579 if ( !sScript
.isEmpty() )
581 // format is: "name (location, language)"
585 Reference
< XUriReferenceFactory
> xUriRefFac
= UriReferenceFactory::create( m_xContext
);
586 Reference
< XVndSunStarScriptUrlReference
> xScriptUri( xUriRefFac
->parse( sScript
), UNO_QUERY_THROW
);
588 OUStringBuffer aComposeBuffer
;
591 aComposeBuffer
.append( xScriptUri
->getName() );
594 const OUString sLocation
= xScriptUri
->getParameter( u
"location"_ustr
);
595 const OUString sLanguage
= xScriptUri
->getParameter( u
"language"_ustr
);
597 if ( !(sLocation
.isEmpty() && sLanguage
.isEmpty()) )
599 aComposeBuffer
.append( " (" );
602 OSL_ENSURE( !sLocation
.isEmpty(), "EventHandler::convertToControlValue: unexpected: no location!" );
603 if ( !sLocation
.isEmpty() )
605 aComposeBuffer
.append( sLocation
+ ", " );
609 if ( !sLanguage
.isEmpty() )
611 aComposeBuffer
.append( sLanguage
);
614 aComposeBuffer
.append( ')' );
617 sScript
= aComposeBuffer
.makeStringAndClear();
619 catch( const Exception
& )
621 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
625 return Any( sScript
);
628 PropertyState SAL_CALL
EventHandler::getPropertyState( const OUString
& /*_rPropertyName*/ )
630 return PropertyState_DIRECT_VALUE
;
633 void SAL_CALL
EventHandler::addPropertyChangeListener( const Reference
< XPropertyChangeListener
>& _rxListener
)
635 ::osl::MutexGuard
aGuard( m_aMutex
);
636 if ( !_rxListener
.is() )
637 throw NullPointerException();
638 m_aPropertyListeners
.addInterface( _rxListener
);
641 void SAL_CALL
EventHandler::removePropertyChangeListener( const Reference
< XPropertyChangeListener
>& _rxListener
)
643 ::osl::MutexGuard
aGuard( m_aMutex
);
644 m_aPropertyListeners
.removeInterface( _rxListener
);
647 Sequence
< Property
> SAL_CALL
EventHandler::getSupportedProperties()
649 ::osl::MutexGuard
aGuard( m_aMutex
);
650 if ( !m_bEventsMapInitialized
)
652 m_bEventsMapInitialized
= true;
655 std::vector
< Type
> aListeners
;
656 impl_getComponentListenerTypes_nothrow( aListeners
);
658 OUString sListenerClassName
;
660 // loop through all listeners and all methods, and see which we can present at the UI
661 for ( const Type
& rListener
: aListeners
)
663 // the programmatic name of the listener, to be used as "property" name
664 sListenerClassName
= rListener
.getTypeName();
665 OSL_ENSURE( !sListenerClassName
.isEmpty(), "EventHandler::getSupportedProperties: strange - no listener name ..." );
666 if ( sListenerClassName
.isEmpty() )
669 // loop through all methods
670 const Sequence
<OUString
> aEventMethods
= comphelper::getEventMethodsForType( rListener
);
671 for (const OUString
& rMethod
: aEventMethods
)
673 EventDescription aEvent
;
674 if ( !lcl_getEventDescriptionForMethod( rMethod
, aEvent
) )
677 if ( !impl_filterMethod_nothrow( aEvent
) )
681 lcl_getEventPropertyName( sListenerClassName
, rMethod
), aEvent
);
686 catch( const Exception
& )
688 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
692 // sort them by ID - this is the relative ordering in the UI
693 std::map
< EventId
, Property
> aOrderedProperties
;
694 for (auto const& event
: m_aEvents
)
696 aOrderedProperties
[ event
.second
.nId
] = Property(
697 event
.first
, event
.second
.nId
,
698 ::cppu::UnoType
<OUString
>::get(),
699 PropertyAttribute::BOUND
);
702 return comphelper::mapValuesToSequence( aOrderedProperties
);
705 Sequence
< OUString
> SAL_CALL
EventHandler::getSupersededProperties( )
708 return Sequence
< OUString
>( );
711 Sequence
< OUString
> SAL_CALL
EventHandler::getActuatingProperties( )
714 return Sequence
< OUString
>( );
717 LineDescriptor SAL_CALL
EventHandler::describePropertyLine( const OUString
& _rPropertyName
,
718 const Reference
< XPropertyControlFactory
>& _rxControlFactory
)
720 if ( !_rxControlFactory
.is() )
721 throw NullPointerException();
723 ::osl::MutexGuard
aGuard( m_aMutex
);
725 LineDescriptor aDescriptor
;
727 aDescriptor
.Control
= _rxControlFactory
->createPropertyControl( PropertyControlType::TextField
, true );
728 new PropertyControlExtender( aDescriptor
.Control
);
730 const EventDescription
& rEvent
= impl_getEventForName_throw( _rPropertyName
);
731 aDescriptor
.DisplayName
= rEvent
.sDisplayName
;
732 aDescriptor
.HelpURL
= HelpIdUrl::getHelpURL( rEvent
.sHelpId
);
733 aDescriptor
.PrimaryButtonId
= OStringToOUString(rEvent
.sUniqueBrowseId
, RTL_TEXTENCODING_UTF8
);
734 aDescriptor
.HasPrimaryButton
= true;
735 aDescriptor
.Category
= "Events";
739 sal_Bool SAL_CALL
EventHandler::isComposable( const OUString
& /*_rPropertyName*/ )
744 InteractiveSelectionResult SAL_CALL
EventHandler::onInteractivePropertySelection( const OUString
& _rPropertyName
, sal_Bool
/*_bPrimary*/, Any
& /*_rData*/, const Reference
< XObjectInspectorUI
>& _rxInspectorUI
)
746 if ( !_rxInspectorUI
.is() )
747 throw NullPointerException();
749 ::osl::MutexGuard
aGuard( m_aMutex
);
750 const EventDescription
& rForEvent
= impl_getEventForName_throw( _rPropertyName
);
752 std::vector
< ScriptEventDescriptor
> aAllAssignedEvents
;
753 impl_getComponentScriptEvents_nothrow( aAllAssignedEvents
);
755 // SvxMacroAssignDlg-compatible structure holding all event/assignments
756 ::rtl::Reference
< EventHolder
> pEventHolder( new EventHolder
);
758 for (auto const& event
: m_aEvents
)
760 // the script which is assigned to the current event (if any)
761 ScriptEventDescriptor aAssignedScript
= lcl_getAssignedScriptEvent( event
.second
, aAllAssignedEvents
);
762 pEventHolder
->addEvent( event
.second
.nId
, event
.second
.sListenerMethodName
, aAssignedScript
);
765 // the initial selection in the dialog
766 const Sequence
< OUString
> aNames( pEventHolder
->getElementNames() );
767 const OUString
* pChosenEvent
= std::find( aNames
.begin(), aNames
.end(), rForEvent
.sListenerMethodName
);
768 sal_uInt16 nInitialSelection
= static_cast<sal_uInt16
>( pChosenEvent
- aNames
.begin() );
771 SvxAbstractDialogFactory
* pFactory
= SvxAbstractDialogFactory::Create();
773 ScopedVclPtr
<VclAbstractDialog
> pDialog( pFactory
->CreateSvxMacroAssignDlg(
774 PropertyHandlerHelper::getDialogParentFrame( m_xContext
),
775 impl_getContextFrame_nothrow(),
782 return InteractiveSelectionResult_Cancelled
;
784 // DF definite problem here
785 // OK & Cancel seem to be both returning 0
786 if ( pDialog
->Execute() == RET_CANCEL
)
787 return InteractiveSelectionResult_Cancelled
;
791 for (auto const& event
: m_aEvents
)
793 ScriptEventDescriptor
aScriptDescriptor( pEventHolder
->getNormalizedDescriptorByName( event
.second
.sListenerMethodName
) );
795 // set the new "property value"
797 lcl_getEventPropertyName( event
.second
.sListenerClassName
, event
.second
.sListenerMethodName
),
798 Any( aScriptDescriptor
)
802 catch( const Exception
& )
804 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
807 return InteractiveSelectionResult_Success
;
810 void SAL_CALL
EventHandler::actuatingPropertyChanged( const OUString
& /*_rActuatingPropertyName*/, const Any
& /*_rNewValue*/, const Any
& /*_rOldValue*/, const Reference
< XObjectInspectorUI
>& /*_rxInspectorUI*/, sal_Bool
/*_bFirstTimeInit*/ )
812 OSL_FAIL( "EventHandler::actuatingPropertyChanged: no actuating properties -> no callback (well, this is how it *should* be!)" );
815 IMPLEMENT_FORWARD_XCOMPONENT( EventHandler
, EventHandler_Base
)
817 void SAL_CALL
EventHandler::disposing()
819 EventMap().swap(m_aEvents
);
820 m_xComponent
.clear();
823 sal_Bool SAL_CALL
EventHandler::suspend( sal_Bool
/*_bSuspend*/ )
828 Reference
< XFrame
> EventHandler::impl_getContextFrame_nothrow() const
830 Reference
< XFrame
> xContextFrame
;
834 Reference
< XModel
> xContextDocument( PropertyHandlerHelper::getContextDocument(m_xContext
), UNO_QUERY_THROW
);
835 Reference
< XController
> xController( xContextDocument
->getCurrentController(), UNO_SET_THROW
);
836 xContextFrame
.set( xController
->getFrame(), UNO_SET_THROW
);
838 catch( const Exception
& )
840 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
843 return xContextFrame
;
846 sal_Int32
EventHandler::impl_getComponentIndexInParent_throw() const
848 Reference
< XChild
> xChild( m_xComponent
, UNO_QUERY_THROW
);
849 Reference
< XIndexAccess
> xParentAsIndexAccess( xChild
->getParent(), UNO_QUERY_THROW
);
851 // get the index of the inspected object within its parent container
852 sal_Int32 nElements
= xParentAsIndexAccess
->getCount();
853 for ( sal_Int32 i
=0; i
<nElements
; ++i
)
855 Reference
< XInterface
> xElement( xParentAsIndexAccess
->getByIndex( i
), UNO_QUERY_THROW
);
856 if ( xElement
== m_xComponent
)
859 throw NoSuchElementException();
862 void EventHandler::impl_getFormComponentScriptEvents_nothrow( std::vector
< ScriptEventDescriptor
>& _out_rEvents
) const
864 _out_rEvents
.clear();
867 Reference
< XChild
> xChild( m_xComponent
, UNO_QUERY_THROW
);
868 Reference
< XEventAttacherManager
> xEventManager( xChild
->getParent(), UNO_QUERY_THROW
);
869 comphelper::sequenceToContainer(_out_rEvents
, xEventManager
->getScriptEvents( impl_getComponentIndexInParent_throw() ));
871 // the form component script API has unqualified listener names, but for normalization
872 // purpose, we want fully qualified ones
873 for ( ScriptEventDescriptor
& rSED
: _out_rEvents
)
875 rSED
.ListenerType
= lcl_getQualifiedKnownListenerName( rSED
);
878 catch( const Exception
& )
880 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
884 void EventHandler::impl_getComponentListenerTypes_nothrow( std::vector
< Type
>& _out_rTypes
) const
889 // we use a set to avoid duplicates
892 Reference
< XIntrospection
> xIntrospection
= theIntrospection::get( m_xContext
);
894 // --- model listeners
895 lcl_addListenerTypesFor_throw(
896 m_xComponent
, xIntrospection
, aListeners
);
898 // --- "secondary component" (usually: "control" listeners)
900 Reference
< XInterface
> xSecondaryComponent( impl_getSecondaryComponentForEventInspection_throw() );
901 lcl_addListenerTypesFor_throw( xSecondaryComponent
, xIntrospection
, aListeners
);
902 ::comphelper::disposeComponent( xSecondaryComponent
);
905 // now that they're disambiguated, copy these types into our member
906 _out_rTypes
.insert( _out_rTypes
.end(), aListeners
.begin(), aListeners
.end() );
908 catch( const Exception
& )
910 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
914 void EventHandler::impl_getDialogElementScriptEvents_nothrow( std::vector
< ScriptEventDescriptor
>& _out_rEvents
) const
916 _out_rEvents
.clear();
919 Reference
< XScriptEventsSupplier
> xEventsSupplier( m_xComponent
, UNO_QUERY_THROW
);
920 Reference
< XNameContainer
> xEvents( xEventsSupplier
->getEvents(), UNO_SET_THROW
);
921 Sequence
< OUString
> aEventNames( xEvents
->getElementNames() );
923 sal_Int32 nEventCount
= aEventNames
.getLength();
924 _out_rEvents
.resize( nEventCount
);
926 for( sal_Int32 i
= 0; i
< nEventCount
; ++i
)
927 OSL_VERIFY( xEvents
->getByName( aEventNames
[i
] ) >>= _out_rEvents
[i
] );
929 catch( const Exception
& )
931 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
935 Reference
< XInterface
> EventHandler::impl_getSecondaryComponentForEventInspection_throw( ) const
937 Reference
< XInterface
> xReturn
;
939 // if it's a form, create a form controller for the additional events
940 Reference
< XForm
> xComponentAsForm( m_xComponent
, UNO_QUERY
);
941 if ( xComponentAsForm
.is() )
943 Reference
< XTabControllerModel
> xComponentAsTCModel( m_xComponent
, UNO_QUERY_THROW
);
944 Reference
< XFormController
> xController
= FormController::create( m_xContext
);
945 xController
->setModel( xComponentAsTCModel
);
947 xReturn
= xController
;
951 OUString sControlService
;
952 OSL_VERIFY( m_xComponent
->getPropertyValue( PROPERTY_DEFAULTCONTROL
) >>= sControlService
);
954 xReturn
= m_xContext
->getServiceManager()->createInstanceWithContext( sControlService
, m_xContext
);
959 const EventDescription
& EventHandler::impl_getEventForName_throw( const OUString
& _rPropertyName
) const
961 EventMap::const_iterator pos
= m_aEvents
.find( _rPropertyName
);
962 if ( pos
== m_aEvents
.end() )
963 throw UnknownPropertyException(_rPropertyName
);
969 bool lcl_endsWith( std::u16string_view _rText
, std::u16string_view _rCheck
)
971 size_t nTextLen
= _rText
.size();
972 size_t nCheckLen
= _rCheck
.size();
973 if ( nCheckLen
> nTextLen
)
976 return _rText
.find( _rCheck
) == ( nTextLen
- nCheckLen
);
980 void EventHandler::impl_setFormComponentScriptEvent_nothrow( const ScriptEventDescriptor
& _rScriptEvent
)
984 OUString
sScriptCode( _rScriptEvent
.ScriptCode
);
985 OUString
sScriptType( _rScriptEvent
.ScriptType
);
986 bool bResetScript
= sScriptCode
.isEmpty();
988 sal_Int32 nObjectIndex
= impl_getComponentIndexInParent_throw();
989 Reference
< XChild
> xChild( m_xComponent
, UNO_QUERY_THROW
);
990 Reference
< XEventAttacherManager
> xEventManager( xChild
->getParent(), UNO_QUERY_THROW
);
991 std::vector
< ScriptEventDescriptor
> aEvents
;
992 comphelper::sequenceToContainer( aEvents
, xEventManager
->getScriptEvents( nObjectIndex
) );
994 // is there already a registered script for this event?
995 sal_Int32 eventCount
= aEvents
.size(), event
= 0;
996 for ( event
= 0; event
< eventCount
; ++event
)
998 ScriptEventDescriptor
* pEvent
= &aEvents
[event
];
999 if ( ( pEvent
->EventMethod
== _rScriptEvent
.EventMethod
)
1000 && ( lcl_endsWith( _rScriptEvent
.ListenerType
, pEvent
->ListenerType
) )
1001 // (strange enough, the events we get from getScriptEvents are not fully qualified)
1005 if ( !bResetScript
)
1007 // set to something non-empty -> overwrite
1008 pEvent
->ScriptCode
= sScriptCode
;
1009 pEvent
->ScriptType
= sScriptType
;
1013 // set to empty -> remove from vector
1014 aEvents
.erase(aEvents
.begin() + event
);
1020 if ( ( event
>= eventCount
) && !bResetScript
)
1022 // no, did not find it -> append
1023 aEvents
.push_back( _rScriptEvent
);
1026 xEventManager
->revokeScriptEvents( nObjectIndex
);
1027 xEventManager
->registerScriptEvents( nObjectIndex
, comphelper::containerToSequence(aEvents
) );
1029 PropertyHandlerHelper::setContextDocumentModified( m_xContext
);
1031 catch( const Exception
& )
1033 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1037 void EventHandler::impl_setDialogElementScriptEvent_nothrow( const ScriptEventDescriptor
& _rScriptEvent
)
1041 OUString
sScriptCode( _rScriptEvent
.ScriptCode
);
1042 bool bResetScript
= sScriptCode
.isEmpty();
1044 Reference
< XScriptEventsSupplier
> xEventsSupplier( m_xComponent
, UNO_QUERY_THROW
);
1045 Reference
< XNameContainer
> xEvents( xEventsSupplier
->getEvents(), UNO_SET_THROW
);
1047 OUString sCompleteName
=
1048 _rScriptEvent
.ListenerType
+
1050 _rScriptEvent
.EventMethod
;
1052 bool bExists
= xEvents
->hasByName( sCompleteName
);
1057 xEvents
->removeByName( sCompleteName
);
1061 Any aNewValue
; aNewValue
<<= _rScriptEvent
;
1064 xEvents
->replaceByName( sCompleteName
, aNewValue
);
1066 xEvents
->insertByName( sCompleteName
, aNewValue
);
1069 catch( const Exception
& )
1071 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1075 bool EventHandler::impl_filterMethod_nothrow( const EventDescription
& _rEvent
) const
1077 // some (control-triggered) events do not make sense for certain grid control columns. However,
1078 // our mechanism to retrieve control-triggered events does not know about this, so we do some
1079 // late filtering here.
1080 switch ( m_nGridColumnType
)
1082 case FormComponentType::COMBOBOX
:
1083 if ( UID_BRWEVT_ACTIONPERFORMED
== _rEvent
.sUniqueBrowseId
)
1086 case FormComponentType::LISTBOX
:
1087 if ( ( UID_BRWEVT_CHANGED
== _rEvent
.sUniqueBrowseId
)
1088 || ( UID_BRWEVT_ACTIONPERFORMED
== _rEvent
.sUniqueBrowseId
)
1099 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
1100 extensions_propctrlr_EventHandler_get_implementation(
1101 css::uno::XComponentContext
* context
, css::uno::Sequence
<css::uno::Any
> const&)
1103 return cppu::acquire(new pcr::EventHandler(context
));
1106 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */