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"
22 #include "pcrservices.hxx"
24 #include <propctrlr.h>
25 #include "formbrowsertools.hxx"
26 #include <strings.hrc>
27 #include "formstrings.hxx"
28 #include "handlerhelper.hxx"
29 #include "modulepcr.hxx"
30 #include "pcrcommon.hxx"
31 #include "pcrstrings.hxx"
32 #include "propertycontrolextender.hxx"
34 #include <com/sun/star/awt/XTabControllerModel.hpp>
35 #include <com/sun/star/beans/PropertyAttribute.hpp>
36 #include <com/sun/star/beans/UnknownPropertyException.hpp>
37 #include <com/sun/star/beans/theIntrospection.hpp>
38 #include <com/sun/star/beans/XIntrospectionAccess.hpp>
39 #include <com/sun/star/container/NoSuchElementException.hpp>
40 #include <com/sun/star/container/XChild.hpp>
41 #include <com/sun/star/container/XIndexAccess.hpp>
42 #include <com/sun/star/container/XNameContainer.hpp>
43 #include <com/sun/star/container/XNameReplace.hpp>
44 #include <com/sun/star/form/FormComponentType.hpp>
45 #include <com/sun/star/form/XForm.hpp>
46 #include <com/sun/star/form/runtime/FormController.hpp>
47 #include <com/sun/star/inspection/PropertyControlType.hpp>
48 #include <com/sun/star/lang/NullPointerException.hpp>
49 #include <com/sun/star/script/XEventAttacherManager.hpp>
50 #include <com/sun/star/script/XScriptEventsSupplier.hpp>
51 #include <com/sun/star/uri/UriReferenceFactory.hpp>
52 #include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp>
54 #include <comphelper/namedvaluecollection.hxx>
55 #include <comphelper/evtmethodhelper.hxx>
56 #include <comphelper/sequence.hxx>
57 #include <comphelper/types.hxx>
58 #include <cppuhelper/implbase.hxx>
59 #include <cppuhelper/supportsservice.hxx>
60 #include <rtl/ref.hxx>
61 #include <rtl/ustrbuf.hxx>
62 #include <sfx2/app.hxx>
63 #include <svl/eitem.hxx>
64 #include <svl/itemset.hxx>
65 #include <svx/svxdlg.hxx>
66 #include <svx/svxids.hrc>
67 #include <tools/diagnose_ex.h>
72 #include <o3tl/functional.hxx>
74 extern "C" void createRegistryInfo_EventHandler()
76 ::pcr::OAutoRegistration
< ::pcr::EventHandler
> aAutoRegistration
;
82 using ::com::sun::star::uno::Reference
;
83 using ::com::sun::star::uno::XComponentContext
;
84 using ::com::sun::star::uno::Any
;
85 using ::com::sun::star::uno::TypeClass_STRING
;
86 using ::com::sun::star::uno::Type
;
87 using ::com::sun::star::beans::theIntrospection
;
88 using ::com::sun::star::beans::XPropertyChangeListener
;
89 using ::com::sun::star::beans::Property
;
90 using ::com::sun::star::beans::PropertyState
;
91 using ::com::sun::star::beans::PropertyState_DIRECT_VALUE
;
92 using ::com::sun::star::uno::Sequence
;
93 using ::com::sun::star::script::ScriptEventDescriptor
;
94 using ::com::sun::star::script::XScriptEventsSupplier
;
95 using ::com::sun::star::lang::NullPointerException
;
96 using ::com::sun::star::uno::Exception
;
97 using ::com::sun::star::container::XChild
;
98 using ::com::sun::star::container::XIndexAccess
;
99 using ::com::sun::star::script::XEventAttacherManager
;
100 using ::com::sun::star::uno::UNO_QUERY
;
101 using ::com::sun::star::uno::UNO_QUERY_THROW
;
102 using ::com::sun::star::uno::XInterface
;
103 using ::com::sun::star::beans::XIntrospection
;
104 using ::com::sun::star::beans::XIntrospectionAccess
;
105 using ::com::sun::star::container::XNameContainer
;
106 using ::com::sun::star::awt::XTabControllerModel
;
107 using ::com::sun::star::form::XForm
;
108 using ::com::sun::star::form::runtime::FormController
;
109 using ::com::sun::star::form::runtime::XFormController
;
110 using ::com::sun::star::beans::UnknownPropertyException
;
111 using ::com::sun::star::uno::makeAny
;
112 using ::com::sun::star::container::NoSuchElementException
;
113 using ::com::sun::star::beans::XPropertySetInfo
;
114 using ::com::sun::star::container::XNameReplace
;
115 using ::com::sun::star::beans::PropertyValue
;
116 using ::com::sun::star::inspection::LineDescriptor
;
117 using ::com::sun::star::inspection::XPropertyControlFactory
;
118 using ::com::sun::star::inspection::InteractiveSelectionResult
;
119 using ::com::sun::star::inspection::InteractiveSelectionResult_Cancelled
;
120 using ::com::sun::star::inspection::InteractiveSelectionResult_Success
;
121 using ::com::sun::star::inspection::XObjectInspectorUI
;
122 using ::com::sun::star::beans::PropertyChangeEvent
;
123 using ::com::sun::star::frame::XFrame
;
124 using ::com::sun::star::frame::XModel
;
125 using ::com::sun::star::frame::XController
;
126 using ::com::sun::star::uno::UNO_SET_THROW
;
127 using com::sun::star::uri::UriReferenceFactory
;
128 using com::sun::star::uri::XUriReferenceFactory
;
129 using com::sun::star::uri::XVndSunStarScriptUrlReference
;
130 using ::com::sun::star::lang::XEventListener
;
132 namespace PropertyControlType
= css::inspection::PropertyControlType
;
133 namespace PropertyAttribute
= css::beans::PropertyAttribute
;
134 namespace FormComponentType
= css::form::FormComponentType
;
136 EventDescription::EventDescription( EventId _nId
, const sal_Char
* _pListenerNamespaceAscii
, const sal_Char
* _pListenerClassAsciiName
,
137 const sal_Char
* _pListenerMethodAsciiName
, const char* pDisplayNameResId
, const OString
& _sHelpId
, const OString
& _sUniqueBrowseId
)
138 :sDisplayName(PcrRes( pDisplayNameResId
))
139 ,sListenerMethodName( OUString::createFromAscii( _pListenerMethodAsciiName
) )
141 ,sUniqueBrowseId( _sUniqueBrowseId
)
144 OUStringBuffer aQualifiedListenerClass
;
145 aQualifiedListenerClass
.append( "com.sun.star." );
146 aQualifiedListenerClass
.appendAscii( _pListenerNamespaceAscii
);
147 aQualifiedListenerClass
.append( "." );
148 aQualifiedListenerClass
.appendAscii( _pListenerClassAsciiName
);
149 sListenerClassName
= aQualifiedListenerClass
.makeStringAndClear();
154 #define DESCRIBE_EVENT( map, asciinamespace, asciilistener, asciimethod, id_postfix ) \
157 EventDescription( ++nEventId, asciinamespace, asciilistener, asciimethod, RID_STR_EVT_##id_postfix, HID_EVT_##id_postfix, UID_BRWEVT_##id_postfix ) )
159 bool lcl_getEventDescriptionForMethod( const OUString
& _rMethodName
, EventDescription
& _out_rDescription
)
161 static EventMap s_aKnownEvents
= []() {
163 sal_Int32 nEventId
= 0;
165 DESCRIBE_EVENT(aMap
, "form", "XApproveActionListener", "approveAction", APPROVEACTIONPERFORMED
);
166 DESCRIBE_EVENT(aMap
, "awt", "XActionListener", "actionPerformed", ACTIONPERFORMED
);
167 DESCRIBE_EVENT(aMap
, "form", "XChangeListener", "changed", CHANGED
);
168 DESCRIBE_EVENT(aMap
, "awt", "XTextListener", "textChanged", TEXTCHANGED
);
169 DESCRIBE_EVENT(aMap
, "awt", "XItemListener", "itemStateChanged", ITEMSTATECHANGED
);
170 DESCRIBE_EVENT(aMap
, "awt", "XFocusListener", "focusGained", FOCUSGAINED
);
171 DESCRIBE_EVENT(aMap
, "awt", "XFocusListener", "focusLost", FOCUSLOST
);
172 DESCRIBE_EVENT(aMap
, "awt", "XKeyListener", "keyPressed", KEYTYPED
);
173 DESCRIBE_EVENT(aMap
, "awt", "XKeyListener", "keyReleased", KEYUP
);
174 DESCRIBE_EVENT(aMap
, "awt", "XMouseListener", "mouseEntered", MOUSEENTERED
);
175 DESCRIBE_EVENT(aMap
, "awt", "XMouseMotionListener", "mouseDragged", MOUSEDRAGGED
);
176 DESCRIBE_EVENT(aMap
, "awt", "XMouseMotionListener", "mouseMoved", MOUSEMOVED
);
177 DESCRIBE_EVENT(aMap
, "awt", "XMouseListener", "mousePressed", MOUSEPRESSED
);
178 DESCRIBE_EVENT(aMap
, "awt", "XMouseListener", "mouseReleased", MOUSERELEASED
);
179 DESCRIBE_EVENT(aMap
, "awt", "XMouseListener", "mouseExited", MOUSEEXITED
);
180 DESCRIBE_EVENT(aMap
, "form", "XResetListener", "approveReset", APPROVERESETTED
);
181 DESCRIBE_EVENT(aMap
, "form", "XResetListener", "resetted", RESETTED
);
182 DESCRIBE_EVENT(aMap
, "form", "XSubmitListener", "approveSubmit", SUBMITTED
);
183 DESCRIBE_EVENT(aMap
, "form", "XUpdateListener", "approveUpdate", BEFOREUPDATE
);
184 DESCRIBE_EVENT(aMap
, "form", "XUpdateListener", "updated", AFTERUPDATE
);
185 DESCRIBE_EVENT(aMap
, "form", "XLoadListener", "loaded", LOADED
);
186 DESCRIBE_EVENT(aMap
, "form", "XLoadListener", "reloading", RELOADING
);
187 DESCRIBE_EVENT(aMap
, "form", "XLoadListener", "reloaded", RELOADED
);
188 DESCRIBE_EVENT(aMap
, "form", "XLoadListener", "unloading", UNLOADING
);
189 DESCRIBE_EVENT(aMap
, "form", "XLoadListener", "unloaded", UNLOADED
);
190 DESCRIBE_EVENT(aMap
, "form", "XConfirmDeleteListener", "confirmDelete", CONFIRMDELETE
);
191 DESCRIBE_EVENT(aMap
, "sdb", "XRowSetApproveListener", "approveRowChange", APPROVEROWCHANGE
);
192 DESCRIBE_EVENT(aMap
, "sdbc", "XRowSetListener", "rowChanged", ROWCHANGE
);
193 DESCRIBE_EVENT(aMap
, "sdb", "XRowSetApproveListener", "approveCursorMove", POSITIONING
);
194 DESCRIBE_EVENT(aMap
, "sdbc", "XRowSetListener", "cursorMoved", POSITIONED
);
195 DESCRIBE_EVENT(aMap
, "form", "XDatabaseParameterListener", "approveParameter", APPROVEPARAMETER
);
196 DESCRIBE_EVENT(aMap
, "sdb", "XSQLErrorListener", "errorOccured", ERROROCCURRED
);
197 DESCRIBE_EVENT(aMap
, "awt", "XAdjustmentListener", "adjustmentValueChanged", ADJUSTMENTVALUECHANGED
);
202 EventMap::const_iterator pos
= s_aKnownEvents
.find( _rMethodName
);
203 if ( pos
== s_aKnownEvents
.end() )
206 _out_rDescription
= pos
->second
;
210 OUString
lcl_getEventPropertyName( const OUString
& _rListenerClassName
, const OUString
& _rMethodName
)
212 return _rListenerClassName
+ OUStringChar(';') + _rMethodName
;
215 ScriptEventDescriptor
lcl_getAssignedScriptEvent( const EventDescription
& _rEvent
, const std::vector
< ScriptEventDescriptor
>& _rAllAssignedMacros
)
217 ScriptEventDescriptor aScriptEvent
;
218 // for the case there is actually no event assigned, initialize at least ListenerType and MethodName,
219 // so this ScriptEventDescriptor properly describes the given event
220 aScriptEvent
.ListenerType
= _rEvent
.sListenerClassName
;
221 aScriptEvent
.EventMethod
= _rEvent
.sListenerMethodName
;
223 for ( const ScriptEventDescriptor
& rSED
: _rAllAssignedMacros
)
225 if ( rSED
.ListenerType
!= _rEvent
.sListenerClassName
226 || rSED
.EventMethod
!= _rEvent
.sListenerMethodName
230 if ( rSED
.ScriptCode
.isEmpty()
231 || rSED
.ScriptType
.isEmpty()
234 OSL_FAIL( "lcl_getAssignedScriptEvent: me thinks this should not happen!" );
240 if ( aScriptEvent
.ScriptType
!= "StarBasic" )
243 // this is an old-style macro specification:
244 // [document|application]:Library.Module.Function
245 // we need to translate this to the new-style macro specification
246 // vnd.sun.star.script:Library.Module.Function?language=Basic&location=[document|application]
248 sal_Int32 nPrefixLen
= aScriptEvent
.ScriptCode
.indexOf( ':' );
249 OSL_ENSURE( nPrefixLen
> 0, "lcl_getAssignedScriptEvent: illegal location!" );
250 OUString sLocation
= aScriptEvent
.ScriptCode
.copy( 0, nPrefixLen
);
251 OUString sMacroPath
= aScriptEvent
.ScriptCode
.copy( nPrefixLen
+ 1 );
253 aScriptEvent
.ScriptCode
=
254 "vnd.sun.star.script:" +
256 "?language=Basic&location=" +
259 // also, this new-style spec requires the script code to be "Script" instead of "StarBasic"
260 aScriptEvent
.ScriptType
= "Script";
265 OUString
lcl_getQualifiedKnownListenerName( const ScriptEventDescriptor
& _rFormComponentEventDescriptor
)
267 EventDescription aKnownEvent
;
268 if ( lcl_getEventDescriptionForMethod( _rFormComponentEventDescriptor
.EventMethod
, aKnownEvent
) )
269 return aKnownEvent
.sListenerClassName
;
270 OSL_FAIL( "lcl_getQualifiedKnownListenerName: unknown method name!" );
271 // somebody assigned an script to a form component event which we don't know
272 // Speaking strictly, this is not really an error - it is possible to do
273 // this programmatically -, but it should rarely happen, since it's not possible
275 return _rFormComponentEventDescriptor
.ListenerType
;
278 typedef std::set
< Type
, TypeLessByName
> TypeBag
;
280 void lcl_addListenerTypesFor_throw( const Reference
< XInterface
>& _rxComponent
,
281 const Reference
< XIntrospection
>& _rxIntrospection
, TypeBag
& _out_rTypes
)
283 if ( !_rxComponent
.is() )
285 OSL_PRECOND( _rxIntrospection
.is(), "lcl_addListenerTypesFor_throw: this will crash!" );
287 Reference
< XIntrospectionAccess
> xIntrospectionAccess(
288 _rxIntrospection
->inspect( makeAny( _rxComponent
) ), UNO_SET_THROW
);
290 Sequence
< Type
> aListeners( xIntrospectionAccess
->getSupportedListeners() );
292 std::copy( aListeners
.begin(), aListeners
.end(),
293 std::insert_iterator
< TypeBag
>( _out_rTypes
, _out_rTypes
.begin() ) );
297 typedef ::cppu::WeakImplHelper
< css::container::XNameReplace
299 /* A UNO component holding assigned event descriptions, for use with a SvxMacroAssignDlg */
300 class EventHolder
: public EventHolder_Base
303 typedef std::unordered_map
< OUString
, ScriptEventDescriptor
> EventMap
;
304 typedef std::map
< EventId
, OUString
> EventMapIndexAccess
;
306 EventMap m_aEventNameAccess
;
307 EventMapIndexAccess m_aEventIndexAccess
;
312 void addEvent( EventId _nId
, const OUString
& _rEventName
, const ScriptEventDescriptor
& _rScriptEvent
);
314 /** effectively the same as getByName, but instead of converting the ScriptEventDescriptor to the weird
315 format used by the macro assignment dialog, it is returned directly
317 ScriptEventDescriptor
getNormalizedDescriptorByName( const OUString
& _rEventName
) const;
320 virtual void SAL_CALL
replaceByName( const OUString
& _rName
, const Any
& aElement
) override
;
321 virtual Any SAL_CALL
getByName( const OUString
& _rName
) override
;
322 virtual Sequence
< OUString
> SAL_CALL
getElementNames( ) override
;
323 virtual sal_Bool SAL_CALL
hasByName( const OUString
& _rName
) override
;
324 virtual Type SAL_CALL
getElementType( ) override
;
325 virtual sal_Bool SAL_CALL
hasElements( ) override
;
328 virtual ~EventHolder( ) override
;
331 ScriptEventDescriptor
const & impl_getDescriptor_throw( const OUString
& _rEventName
) const;
335 EventHolder::EventHolder()
339 EventHolder::~EventHolder()
341 m_aEventNameAccess
.clear();
342 m_aEventIndexAccess
.clear();
345 void EventHolder::addEvent( EventId _nId
, const OUString
& _rEventName
, const ScriptEventDescriptor
& _rScriptEvent
)
347 std::pair
< EventMap::iterator
, bool > insertionResult
=
348 m_aEventNameAccess
.emplace( _rEventName
, _rScriptEvent
);
349 OSL_ENSURE( insertionResult
.second
, "EventHolder::addEvent: there already was a MacroURL for this event!" );
350 m_aEventIndexAccess
[ _nId
] = _rEventName
;
353 ScriptEventDescriptor
EventHolder::getNormalizedDescriptorByName( const OUString
& _rEventName
) const
355 return impl_getDescriptor_throw( _rEventName
);
358 ScriptEventDescriptor
const & EventHolder::impl_getDescriptor_throw( const OUString
& _rEventName
) const
360 EventMap::const_iterator pos
= m_aEventNameAccess
.find( _rEventName
);
361 if ( pos
== m_aEventNameAccess
.end() )
362 throw NoSuchElementException( OUString(), *const_cast< EventHolder
* >( this ) );
366 void SAL_CALL
EventHolder::replaceByName( const OUString
& _rName
, const Any
& _rElement
)
368 EventMap::iterator pos
= m_aEventNameAccess
.find( _rName
);
369 if ( pos
== m_aEventNameAccess
.end() )
370 throw NoSuchElementException( OUString(), *this );
372 Sequence
< PropertyValue
> aScriptDescriptor
;
373 OSL_VERIFY( _rElement
>>= aScriptDescriptor
);
375 ::comphelper::NamedValueCollection
aExtractor( aScriptDescriptor
);
377 pos
->second
.ScriptType
= aExtractor
.getOrDefault( "EventType", OUString() );
378 pos
->second
.ScriptCode
= aExtractor
.getOrDefault( "Script", OUString() );
381 Any SAL_CALL
EventHolder::getByName( const OUString
& _rName
)
383 ScriptEventDescriptor
aDescriptor( impl_getDescriptor_throw( _rName
) );
385 Sequence
< PropertyValue
> aScriptDescriptor( 2 );
386 aScriptDescriptor
[0].Name
= "EventType";
387 aScriptDescriptor
[0].Value
<<= aDescriptor
.ScriptType
;
388 aScriptDescriptor
[1].Name
= "Script";
389 aScriptDescriptor
[1].Value
<<= aDescriptor
.ScriptCode
;
391 return makeAny( aScriptDescriptor
);
394 Sequence
< OUString
> SAL_CALL
EventHolder::getElementNames( )
396 Sequence
< OUString
> aReturn( m_aEventIndexAccess
.size() );
397 OUString
* pReturn
= aReturn
.getArray();
399 // SvxMacroAssignDlg has a weird API: It expects a XNameReplace, means a container whose
400 // main access method is by name. In its UI, it shows the possible events in exactly the
401 // order in which XNameAccess::getElementNames returns them.
402 // However, SvxMacroAssignDlg *also* takes an index for the initial selection, which is
403 // relative to the sequence returned by XNameAccess::getElementNames.
404 // This is IMO weird, since it mixes index access with name access, which decreases efficiency
405 // of the implementation.
406 // Well, it means we're forced to return the events in getElementNames in exactly the same as they
407 // appear in the property browser UI.
408 for (auto const& elem
: m_aEventIndexAccess
)
410 *pReturn
= elem
.second
;
416 sal_Bool SAL_CALL
EventHolder::hasByName( const OUString
& _rName
)
418 EventMap::const_iterator pos
= m_aEventNameAccess
.find( _rName
);
419 return pos
!= m_aEventNameAccess
.end();
422 Type SAL_CALL
EventHolder::getElementType( )
424 return cppu::UnoType
<Sequence
< PropertyValue
>>::get();
427 sal_Bool SAL_CALL
EventHolder::hasElements( )
429 return !m_aEventNameAccess
.empty();
433 EventHandler::EventHandler( const Reference
< XComponentContext
>& _rxContext
)
434 :EventHandler_Base( m_aMutex
)
435 ,m_xContext( _rxContext
)
436 ,m_aPropertyListeners( m_aMutex
)
437 ,m_bEventsMapInitialized( false )
438 ,m_bIsDialogElement( false )
439 ,m_nGridColumnType( -1 )
443 EventHandler::~EventHandler()
447 OUString SAL_CALL
EventHandler::getImplementationName( )
449 return getImplementationName_static();
452 sal_Bool SAL_CALL
EventHandler::supportsService( const OUString
& ServiceName
)
454 return cppu::supportsService(this, ServiceName
);
457 Sequence
< OUString
> SAL_CALL
EventHandler::getSupportedServiceNames( )
459 return getSupportedServiceNames_static();
462 OUString
EventHandler::getImplementationName_static( )
464 return "com.sun.star.comp.extensions.EventHandler";
467 Sequence
< OUString
> EventHandler::getSupportedServiceNames_static( )
469 Sequence
<OUString
> aSupported
{ "com.sun.star.form.inspection.EventHandler" };
473 Reference
< XInterface
> EventHandler::Create( const Reference
< XComponentContext
>& _rxContext
)
475 return *( new EventHandler( _rxContext
) );
478 void SAL_CALL
EventHandler::inspect( const Reference
< XInterface
>& _rxIntrospectee
)
480 ::osl::MutexGuard
aGuard( m_aMutex
);
482 if ( !_rxIntrospectee
.is() )
483 throw NullPointerException();
485 m_xComponent
.set( _rxIntrospectee
, UNO_QUERY_THROW
);
487 m_bEventsMapInitialized
= false;
489 m_aEvents
.swap( aEmpty
);
491 m_bIsDialogElement
= false;
492 m_nGridColumnType
= -1;
495 Reference
< XPropertySetInfo
> xPSI( m_xComponent
->getPropertySetInfo() );
496 m_bIsDialogElement
= xPSI
.is()
497 && xPSI
->hasPropertyByName( PROPERTY_WIDTH
)
498 && xPSI
->hasPropertyByName( PROPERTY_HEIGHT
)
499 && xPSI
->hasPropertyByName( PROPERTY_POSITIONX
)
500 && xPSI
->hasPropertyByName( PROPERTY_POSITIONY
);
502 Reference
< XChild
> xAsChild( _rxIntrospectee
, UNO_QUERY
);
503 if ( xAsChild
.is() && !Reference
< XForm
>( _rxIntrospectee
, UNO_QUERY
).is() )
505 if ( FormComponentType::GRIDCONTROL
== classifyComponent( xAsChild
->getParent() ) )
507 m_nGridColumnType
= classifyComponent( _rxIntrospectee
);
511 catch( const Exception
& )
513 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
517 Any SAL_CALL
EventHandler::getPropertyValue( const OUString
& _rPropertyName
)
519 ::osl::MutexGuard
aGuard( m_aMutex
);
521 const EventDescription
& rEvent
= impl_getEventForName_throw( _rPropertyName
);
523 std::vector
< ScriptEventDescriptor
> aEvents
;
524 impl_getComponentScriptEvents_nothrow( aEvents
);
526 ScriptEventDescriptor aPropertyValue
;
527 for ( const ScriptEventDescriptor
& rSCD
: aEvents
)
529 if ( rEvent
.sListenerClassName
== rSCD
.ListenerType
530 && rEvent
.sListenerMethodName
== rSCD
.EventMethod
533 aPropertyValue
= rSCD
;
538 return makeAny( aPropertyValue
);
541 void SAL_CALL
EventHandler::setPropertyValue( const OUString
& _rPropertyName
, const Any
& _rValue
)
543 ::osl::MutexGuard
aGuard( m_aMutex
);
545 const EventDescription
& rEvent
= impl_getEventForName_throw( _rPropertyName
);
547 ScriptEventDescriptor aNewScriptEvent
;
548 OSL_VERIFY( _rValue
>>= aNewScriptEvent
);
550 ScriptEventDescriptor aOldScriptEvent
;
551 OSL_VERIFY( getPropertyValue( _rPropertyName
) >>= aOldScriptEvent
);
552 if ( aOldScriptEvent
== aNewScriptEvent
)
555 if ( m_bIsDialogElement
)
556 impl_setDialogElementScriptEvent_nothrow( aNewScriptEvent
);
558 impl_setFormComponentScriptEvent_nothrow( aNewScriptEvent
);
560 PropertyHandlerHelper::setContextDocumentModified( m_xContext
);
562 PropertyChangeEvent aEvent
;
563 aEvent
.Source
= m_xComponent
;
564 aEvent
.PropertyHandle
= rEvent
.nId
;
565 aEvent
.PropertyName
= _rPropertyName
;
566 aEvent
.OldValue
<<= aOldScriptEvent
;
567 aEvent
.NewValue
<<= aNewScriptEvent
;
568 m_aPropertyListeners
.notify( aEvent
, &XPropertyChangeListener::propertyChange
);
571 Any SAL_CALL
EventHandler::convertToPropertyValue( const OUString
& _rPropertyName
, const Any
& _rControlValue
)
573 ::osl::MutexGuard
aGuard( m_aMutex
);
575 OUString sNewScriptCode
;
576 OSL_VERIFY( _rControlValue
>>= sNewScriptCode
);
578 std::vector
< ScriptEventDescriptor
> aAllAssignedEvents
;
579 impl_getComponentScriptEvents_nothrow( aAllAssignedEvents
);
581 const EventDescription
& rEvent
= impl_getEventForName_throw( _rPropertyName
);
582 ScriptEventDescriptor aAssignedScript
= lcl_getAssignedScriptEvent( rEvent
, aAllAssignedEvents
);
584 OSL_ENSURE( sNewScriptCode
.isEmpty(), "EventHandler::convertToPropertyValue: cannot convert a non-empty display name!" );
585 // Usually, there is no possibility for the user to change the content of an event binding directly in the
586 // input field, this instead is done with the macro assignment dialog.
587 // The only exception is the user pressing "DEL" while the control has the focus, in this case, we reset the
588 // control content to an empty string. So this is the only scenario where this method is allowed to be called.
590 // Strictly, we would be able to convert the display value to a property value,
591 // using the "name (location, language)" format we used in convertToControlValue. However,
592 // there is no need for this code...
594 aAssignedScript
.ScriptCode
= sNewScriptCode
;
595 return makeAny( aAssignedScript
);
598 Any SAL_CALL
EventHandler::convertToControlValue( const OUString
& /*_rPropertyName*/, const Any
& _rPropertyValue
, const Type
& _rControlValueType
)
600 ::osl::MutexGuard
aGuard( m_aMutex
);
602 ScriptEventDescriptor aScriptEvent
;
603 OSL_VERIFY( _rPropertyValue
>>= aScriptEvent
);
605 OSL_ENSURE( _rControlValueType
.getTypeClass() == TypeClass_STRING
,
606 "EventHandler::convertToControlValue: unexpected ControlValue type class!" );
608 OUString
sScript( aScriptEvent
.ScriptCode
);
609 if ( !sScript
.isEmpty() )
611 // format is: "name (location, language)"
615 Reference
< XUriReferenceFactory
> xUriRefFac
= UriReferenceFactory::create( m_xContext
);
616 Reference
< XVndSunStarScriptUrlReference
> xScriptUri( xUriRefFac
->parse( sScript
), UNO_QUERY_THROW
);
618 OUStringBuffer aComposeBuffer
;
621 aComposeBuffer
.append( xScriptUri
->getName() );
624 const OUString
sLocationParamName( "location" );
625 const OUString sLocation
= xScriptUri
->getParameter( sLocationParamName
);
626 const OUString
sLangParamName( "language" );
627 const OUString sLanguage
= xScriptUri
->getParameter( sLangParamName
);
629 if ( !(sLocation
.isEmpty() && sLanguage
.isEmpty()) )
631 aComposeBuffer
.append( " (" );
634 OSL_ENSURE( !sLocation
.isEmpty(), "EventHandler::convertToControlValue: unexpected: no location!" );
635 if ( !sLocation
.isEmpty() )
637 aComposeBuffer
.append( sLocation
);
638 aComposeBuffer
.append( ", " );
642 if ( !sLanguage
.isEmpty() )
644 aComposeBuffer
.append( sLanguage
);
647 aComposeBuffer
.append( ')' );
650 sScript
= aComposeBuffer
.makeStringAndClear();
652 catch( const Exception
& )
654 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
658 return makeAny( sScript
);
661 PropertyState SAL_CALL
EventHandler::getPropertyState( const OUString
& /*_rPropertyName*/ )
663 return PropertyState_DIRECT_VALUE
;
666 void SAL_CALL
EventHandler::addPropertyChangeListener( const Reference
< XPropertyChangeListener
>& _rxListener
)
668 ::osl::MutexGuard
aGuard( m_aMutex
);
669 if ( !_rxListener
.is() )
670 throw NullPointerException();
671 m_aPropertyListeners
.addListener( _rxListener
);
674 void SAL_CALL
EventHandler::removePropertyChangeListener( const Reference
< XPropertyChangeListener
>& _rxListener
)
676 ::osl::MutexGuard
aGuard( m_aMutex
);
677 m_aPropertyListeners
.removeListener( _rxListener
);
680 Sequence
< Property
> SAL_CALL
EventHandler::getSupportedProperties()
682 ::osl::MutexGuard
aGuard( m_aMutex
);
683 if ( !m_bEventsMapInitialized
)
685 m_bEventsMapInitialized
= true;
688 std::vector
< Type
> aListeners
;
689 impl_getComponentListenerTypes_nothrow( aListeners
);
691 OUString sListenerClassName
;
693 // loop through all listeners and all methods, and see which we can present at the UI
694 for ( const Type
& rListener
: aListeners
)
696 // the programmatic name of the listener, to be used as "property" name
697 sListenerClassName
= rListener
.getTypeName();
698 OSL_ENSURE( !sListenerClassName
.isEmpty(), "EventHandler::getSupportedProperties: strange - no listener name ..." );
699 if ( sListenerClassName
.isEmpty() )
702 // loop through all methods
703 const Sequence
<OUString
> aEventMethods
= comphelper::getEventMethodsForType( rListener
);
704 for (const OUString
& rMethod
: aEventMethods
)
706 EventDescription aEvent
;
707 if ( !lcl_getEventDescriptionForMethod( rMethod
, aEvent
) )
710 if ( !impl_filterMethod_nothrow( aEvent
) )
714 lcl_getEventPropertyName( sListenerClassName
, rMethod
), aEvent
);
719 catch( const Exception
& )
721 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
725 // sort them by ID - this is the relative ordering in the UI
726 std::map
< EventId
, Property
> aOrderedProperties
;
727 for (auto const& event
: m_aEvents
)
729 aOrderedProperties
[ event
.second
.nId
] = Property(
730 event
.first
, event
.second
.nId
,
731 ::cppu::UnoType
<OUString
>::get(),
732 PropertyAttribute::BOUND
);
735 return comphelper::mapValuesToSequence( aOrderedProperties
);
738 Sequence
< OUString
> SAL_CALL
EventHandler::getSupersededProperties( )
741 return Sequence
< OUString
>( );
744 Sequence
< OUString
> SAL_CALL
EventHandler::getActuatingProperties( )
747 return Sequence
< OUString
>( );
750 LineDescriptor SAL_CALL
EventHandler::describePropertyLine( const OUString
& _rPropertyName
,
751 const Reference
< XPropertyControlFactory
>& _rxControlFactory
)
753 if ( !_rxControlFactory
.is() )
754 throw NullPointerException();
756 ::osl::MutexGuard
aGuard( m_aMutex
);
758 LineDescriptor aDescriptor
;
760 aDescriptor
.Control
= _rxControlFactory
->createPropertyControl( PropertyControlType::TextField
, true );
761 Reference
< XEventListener
> xControlExtender
= new PropertyControlExtender( aDescriptor
.Control
);
763 const EventDescription
& rEvent
= impl_getEventForName_throw( _rPropertyName
);
764 aDescriptor
.DisplayName
= rEvent
.sDisplayName
;
765 aDescriptor
.HelpURL
= HelpIdUrl::getHelpURL( rEvent
.sHelpId
);
766 aDescriptor
.PrimaryButtonId
= OStringToOUString(rEvent
.sUniqueBrowseId
, RTL_TEXTENCODING_UTF8
);
767 aDescriptor
.HasPrimaryButton
= true;
768 aDescriptor
.Category
= "Events";
772 sal_Bool SAL_CALL
EventHandler::isComposable( const OUString
& /*_rPropertyName*/ )
777 InteractiveSelectionResult SAL_CALL
EventHandler::onInteractivePropertySelection( const OUString
& _rPropertyName
, sal_Bool
/*_bPrimary*/, Any
& /*_rData*/, const Reference
< XObjectInspectorUI
>& _rxInspectorUI
)
779 if ( !_rxInspectorUI
.is() )
780 throw NullPointerException();
782 ::osl::MutexGuard
aGuard( m_aMutex
);
783 const EventDescription
& rForEvent
= impl_getEventForName_throw( _rPropertyName
);
785 std::vector
< ScriptEventDescriptor
> aAllAssignedEvents
;
786 impl_getComponentScriptEvents_nothrow( aAllAssignedEvents
);
788 // SvxMacroAssignDlg-compatible structure holding all event/assignments
789 ::rtl::Reference
< EventHolder
> pEventHolder( new EventHolder
);
791 for (auto const& event
: m_aEvents
)
793 // the script which is assigned to the current event (if any)
794 ScriptEventDescriptor aAssignedScript
= lcl_getAssignedScriptEvent( event
.second
, aAllAssignedEvents
);
795 pEventHolder
->addEvent( event
.second
.nId
, event
.second
.sListenerMethodName
, aAssignedScript
);
798 // the initial selection in the dialog
799 Sequence
< OUString
> aNames( pEventHolder
->getElementNames() );
800 const OUString
* pChosenEvent
= std::find( aNames
.begin(), aNames
.end(), rForEvent
.sListenerMethodName
);
801 sal_uInt16 nInitialSelection
= static_cast<sal_uInt16
>( pChosenEvent
- aNames
.begin() );
804 SvxAbstractDialogFactory
* pFactory
= SvxAbstractDialogFactory::Create();
806 ScopedVclPtr
<VclAbstractDialog
> pDialog( pFactory
->CreateSvxMacroAssignDlg(
807 PropertyHandlerHelper::getDialogParentFrame( m_xContext
),
808 impl_getContextFrame_nothrow(),
814 if ( !pDialog
.get() )
815 return InteractiveSelectionResult_Cancelled
;
817 // DF definite problem here
818 // OK & Cancel seem to be both returning 0
819 if ( pDialog
->Execute() == RET_CANCEL
)
820 return InteractiveSelectionResult_Cancelled
;
824 for (auto const& event
: m_aEvents
)
826 ScriptEventDescriptor
aScriptDescriptor( pEventHolder
->getNormalizedDescriptorByName( event
.second
.sListenerMethodName
) );
828 // set the new "property value"
830 lcl_getEventPropertyName( event
.second
.sListenerClassName
, event
.second
.sListenerMethodName
),
831 makeAny( aScriptDescriptor
)
835 catch( const Exception
& )
837 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
840 return InteractiveSelectionResult_Success
;
843 void SAL_CALL
EventHandler::actuatingPropertyChanged( const OUString
& /*_rActuatingPropertyName*/, const Any
& /*_rNewValue*/, const Any
& /*_rOldValue*/, const Reference
< XObjectInspectorUI
>& /*_rxInspectorUI*/, sal_Bool
/*_bFirstTimeInit*/ )
845 OSL_FAIL( "EventHandler::actuatingPropertyChanged: no actuating properties -> no callback (well, this is how it *should* be!)" );
848 IMPLEMENT_FORWARD_XCOMPONENT( EventHandler
, EventHandler_Base
)
850 void SAL_CALL
EventHandler::disposing()
853 m_aEvents
.swap( aEmpty
);
854 m_xComponent
.clear();
857 sal_Bool SAL_CALL
EventHandler::suspend( sal_Bool
/*_bSuspend*/ )
862 Reference
< XFrame
> EventHandler::impl_getContextFrame_nothrow() const
864 Reference
< XFrame
> xContextFrame
;
868 Reference
< XModel
> xContextDocument( PropertyHandlerHelper::getContextDocument(m_xContext
), UNO_QUERY_THROW
);
869 Reference
< XController
> xController( xContextDocument
->getCurrentController(), UNO_SET_THROW
);
870 xContextFrame
.set( xController
->getFrame(), UNO_SET_THROW
);
872 catch( const Exception
& )
874 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
877 return xContextFrame
;
880 sal_Int32
EventHandler::impl_getComponentIndexInParent_throw() const
882 Reference
< XChild
> xChild( m_xComponent
, UNO_QUERY_THROW
);
883 Reference
< XIndexAccess
> xParentAsIndexAccess( xChild
->getParent(), UNO_QUERY_THROW
);
885 // get the index of the inspected object within its parent container
886 sal_Int32 nElements
= xParentAsIndexAccess
->getCount();
887 for ( sal_Int32 i
=0; i
<nElements
; ++i
)
889 Reference
< XInterface
> xElement( xParentAsIndexAccess
->getByIndex( i
), UNO_QUERY_THROW
);
890 if ( xElement
== m_xComponent
)
893 throw NoSuchElementException();
896 void EventHandler::impl_getFormComponentScriptEvents_nothrow( std::vector
< ScriptEventDescriptor
>& _out_rEvents
) const
898 _out_rEvents
.clear();
901 Reference
< XChild
> xChild( m_xComponent
, UNO_QUERY_THROW
);
902 Reference
< XEventAttacherManager
> xEventManager( xChild
->getParent(), UNO_QUERY_THROW
);
903 comphelper::sequenceToContainer(_out_rEvents
, xEventManager
->getScriptEvents( impl_getComponentIndexInParent_throw() ));
905 // the form component script API has unqualified listener names, but for normalization
906 // purpose, we want fully qualified ones
907 for ( ScriptEventDescriptor
& rSED
: _out_rEvents
)
909 rSED
.ListenerType
= lcl_getQualifiedKnownListenerName( rSED
);
912 catch( const Exception
& )
914 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
918 void EventHandler::impl_getComponentListenerTypes_nothrow( std::vector
< Type
>& _out_rTypes
) const
923 // we use a set to avoid duplicates
926 Reference
< XIntrospection
> xIntrospection
= theIntrospection::get( m_xContext
);
928 // --- model listeners
929 lcl_addListenerTypesFor_throw(
930 m_xComponent
, xIntrospection
, aListeners
);
932 // --- "secondary component" (usually: "control" listeners)
934 Reference
< XInterface
> xSecondaryComponent( impl_getSecondaryComponentForEventInspection_throw() );
935 lcl_addListenerTypesFor_throw( xSecondaryComponent
, xIntrospection
, aListeners
);
936 ::comphelper::disposeComponent( xSecondaryComponent
);
939 // now that they're disambiguated, copy these types into our member
940 std::copy(aListeners
.begin(), aListeners
.end(), std::back_inserter(_out_rTypes
));
942 catch( const Exception
& )
944 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
948 void EventHandler::impl_getDialogElementScriptEvents_nothrow( std::vector
< ScriptEventDescriptor
>& _out_rEvents
) const
950 _out_rEvents
.clear();
953 Reference
< XScriptEventsSupplier
> xEventsSupplier( m_xComponent
, UNO_QUERY_THROW
);
954 Reference
< XNameContainer
> xEvents( xEventsSupplier
->getEvents(), UNO_SET_THROW
);
955 Sequence
< OUString
> aEventNames( xEvents
->getElementNames() );
957 sal_Int32 nEventCount
= aEventNames
.getLength();
958 _out_rEvents
.resize( nEventCount
);
960 for( sal_Int32 i
= 0; i
< nEventCount
; ++i
)
961 OSL_VERIFY( xEvents
->getByName( aEventNames
[i
] ) >>= _out_rEvents
[i
] );
963 catch( const Exception
& )
965 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
969 Reference
< XInterface
> EventHandler::impl_getSecondaryComponentForEventInspection_throw( ) const
971 Reference
< XInterface
> xReturn
;
973 // if it's a form, create a form controller for the additional events
974 Reference
< XForm
> xComponentAsForm( m_xComponent
, UNO_QUERY
);
975 if ( xComponentAsForm
.is() )
977 Reference
< XTabControllerModel
> xComponentAsTCModel( m_xComponent
, UNO_QUERY_THROW
);
978 Reference
< XFormController
> xController
= FormController::create( m_xContext
);
979 xController
->setModel( xComponentAsTCModel
);
981 xReturn
= xController
;
985 OUString sControlService
;
986 OSL_VERIFY( m_xComponent
->getPropertyValue( PROPERTY_DEFAULTCONTROL
) >>= sControlService
);
988 xReturn
= m_xContext
->getServiceManager()->createInstanceWithContext( sControlService
, m_xContext
);
993 const EventDescription
& EventHandler::impl_getEventForName_throw( const OUString
& _rPropertyName
) const
995 EventMap::const_iterator pos
= m_aEvents
.find( _rPropertyName
);
996 if ( pos
== m_aEvents
.end() )
997 throw UnknownPropertyException(_rPropertyName
);
1003 bool lcl_endsWith( const OUString
& _rText
, const OUString
& _rCheck
)
1005 sal_Int32 nTextLen
= _rText
.getLength();
1006 sal_Int32 nCheckLen
= _rCheck
.getLength();
1007 if ( nCheckLen
> nTextLen
)
1010 return _rText
.indexOf( _rCheck
) == ( nTextLen
- nCheckLen
);
1014 void EventHandler::impl_setFormComponentScriptEvent_nothrow( const ScriptEventDescriptor
& _rScriptEvent
)
1018 OUString
sScriptCode( _rScriptEvent
.ScriptCode
);
1019 OUString
sScriptType( _rScriptEvent
.ScriptType
);
1020 bool bResetScript
= sScriptCode
.isEmpty();
1022 sal_Int32 nObjectIndex
= impl_getComponentIndexInParent_throw();
1023 Reference
< XChild
> xChild( m_xComponent
, UNO_QUERY_THROW
);
1024 Reference
< XEventAttacherManager
> xEventManager( xChild
->getParent(), UNO_QUERY_THROW
);
1025 std::vector
< ScriptEventDescriptor
> aEvents
;
1026 comphelper::sequenceToContainer( aEvents
, xEventManager
->getScriptEvents( nObjectIndex
) );
1028 // is there already a registered script for this event?
1029 sal_Int32 eventCount
= aEvents
.size(), event
= 0;
1030 for ( event
= 0; event
< eventCount
; ++event
)
1032 ScriptEventDescriptor
* pEvent
= &aEvents
[event
];
1033 if ( ( pEvent
->EventMethod
== _rScriptEvent
.EventMethod
)
1034 && ( lcl_endsWith( _rScriptEvent
.ListenerType
, pEvent
->ListenerType
) )
1035 // (strange enough, the events we get from getScriptEvents are not fully qualified)
1039 if ( !bResetScript
)
1041 // set to something non-empty -> overwrite
1042 pEvent
->ScriptCode
= sScriptCode
;
1043 pEvent
->ScriptType
= sScriptType
;
1047 // set to empty -> remove from vector
1048 aEvents
.erase(aEvents
.begin() + event
);
1054 if ( ( event
>= eventCount
) && !bResetScript
)
1056 // no, did not find it -> append
1057 aEvents
.push_back( _rScriptEvent
);
1060 xEventManager
->revokeScriptEvents( nObjectIndex
);
1061 xEventManager
->registerScriptEvents( nObjectIndex
, comphelper::containerToSequence(aEvents
) );
1063 PropertyHandlerHelper::setContextDocumentModified( m_xContext
);
1065 catch( const Exception
& )
1067 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1071 void EventHandler::impl_setDialogElementScriptEvent_nothrow( const ScriptEventDescriptor
& _rScriptEvent
)
1075 OUString
sScriptCode( _rScriptEvent
.ScriptCode
);
1076 bool bResetScript
= sScriptCode
.isEmpty();
1078 Reference
< XScriptEventsSupplier
> xEventsSupplier( m_xComponent
, UNO_QUERY_THROW
);
1079 Reference
< XNameContainer
> xEvents( xEventsSupplier
->getEvents(), UNO_SET_THROW
);
1081 OUString sCompleteName
=
1082 _rScriptEvent
.ListenerType
+
1084 _rScriptEvent
.EventMethod
;
1086 bool bExists
= xEvents
->hasByName( sCompleteName
);
1091 xEvents
->removeByName( sCompleteName
);
1095 Any aNewValue
; aNewValue
<<= _rScriptEvent
;
1098 xEvents
->replaceByName( sCompleteName
, aNewValue
);
1100 xEvents
->insertByName( sCompleteName
, aNewValue
);
1103 catch( const Exception
& )
1105 DBG_UNHANDLED_EXCEPTION("extensions.propctrlr");
1109 bool EventHandler::impl_filterMethod_nothrow( const EventDescription
& _rEvent
) const
1111 // some (control-triggered) events do not make sense for certain grid control columns. However,
1112 // our mechanism to retrieve control-triggered events does not know about this, so we do some
1113 // late filtering here.
1114 switch ( m_nGridColumnType
)
1116 case FormComponentType::COMBOBOX
:
1117 if ( UID_BRWEVT_ACTIONPERFORMED
== _rEvent
.sUniqueBrowseId
)
1120 case FormComponentType::LISTBOX
:
1121 if ( ( UID_BRWEVT_CHANGED
== _rEvent
.sUniqueBrowseId
)
1122 || ( UID_BRWEVT_ACTIONPERFORMED
== _rEvent
.sUniqueBrowseId
)
1133 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */