Version 6.4.0.0.beta1, tag libreoffice-6.4.0.0.beta1
[LibreOffice.git] / extensions / source / propctrlr / eventhandler.cxx
blobe368bdf5f100b382838c7f68ff1b26cecf08070a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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"
23 #include <helpids.h>
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>
69 #include <map>
70 #include <algorithm>
71 #include <iterator>
72 #include <o3tl/functional.hxx>
74 extern "C" void createRegistryInfo_EventHandler()
76 ::pcr::OAutoRegistration< ::pcr::EventHandler > aAutoRegistration;
79 namespace pcr
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 ) )
140 ,sHelpId( _sHelpId )
141 ,sUniqueBrowseId( _sUniqueBrowseId )
142 ,nId( _nId )
144 OUStringBuffer aQualifiedListenerClass;
145 aQualifiedListenerClass.append( "com.sun.star." );
146 aQualifiedListenerClass.appendAscii( _pListenerNamespaceAscii );
147 aQualifiedListenerClass.append( "." );
148 aQualifiedListenerClass.appendAscii( _pListenerClassAsciiName );
149 sListenerClassName = aQualifiedListenerClass.makeStringAndClear();
152 namespace
154 #define DESCRIBE_EVENT( map, asciinamespace, asciilistener, asciimethod, id_postfix ) \
155 map.emplace( \
156 asciimethod, \
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 = []() {
162 EventMap aMap;
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);
199 return aMap;
200 }();
202 EventMap::const_iterator pos = s_aKnownEvents.find( _rMethodName );
203 if ( pos == s_aKnownEvents.end() )
204 return false;
206 _out_rDescription = pos->second;
207 return true;
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
228 continue;
230 if ( rSED.ScriptCode.isEmpty()
231 || rSED.ScriptType.isEmpty()
234 OSL_FAIL( "lcl_getAssignedScriptEvent: me thinks this should not happen!" );
235 continue;
238 aScriptEvent = rSED;
240 if ( aScriptEvent.ScriptType != "StarBasic" )
241 continue;
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:" +
255 sMacroPath +
256 "?language=Basic&location=" +
257 sLocation;
259 // also, this new-style spec requires the script code to be "Script" instead of "StarBasic"
260 aScriptEvent.ScriptType = "Script";
262 return aScriptEvent;
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
274 // via UI
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() )
284 return;
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
298 > EventHolder_Base;
299 /* A UNO component holding assigned event descriptions, for use with a SvxMacroAssignDlg */
300 class EventHolder : public EventHolder_Base
302 private:
303 typedef std::unordered_map< OUString, ScriptEventDescriptor > EventMap;
304 typedef std::map< EventId, OUString > EventMapIndexAccess;
306 EventMap m_aEventNameAccess;
307 EventMapIndexAccess m_aEventIndexAccess;
309 public:
310 EventHolder( );
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;
319 // XNameReplace
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;
327 protected:
328 virtual ~EventHolder( ) override;
330 private:
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 ) );
363 return pos->second;
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;
411 ++pReturn;
413 return aReturn;
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" };
470 return aSupported;
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;
488 EventMap aEmpty;
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;
534 break;
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 )
553 return;
555 if ( m_bIsDialogElement )
556 impl_setDialogElementScriptEvent_nothrow( aNewScriptEvent );
557 else
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)"
614 // parse
615 Reference< XUriReferenceFactory > xUriRefFac = UriReferenceFactory::create( m_xContext );
616 Reference< XVndSunStarScriptUrlReference > xScriptUri( xUriRefFac->parse( sScript ), UNO_QUERY_THROW );
618 OUStringBuffer aComposeBuffer;
620 // name
621 aComposeBuffer.append( xScriptUri->getName() );
623 // location
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( " (" );
633 // location
634 OSL_ENSURE( !sLocation.isEmpty(), "EventHandler::convertToControlValue: unexpected: no location!" );
635 if ( !sLocation.isEmpty() )
637 aComposeBuffer.append( sLocation );
638 aComposeBuffer.append( ", " );
641 // language
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() )
700 continue;
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 ) )
708 continue;
710 if ( !impl_filterMethod_nothrow( aEvent ) )
711 continue;
713 m_aEvents.emplace(
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( )
740 // none
741 return Sequence< OUString >( );
744 Sequence< OUString > SAL_CALL EventHandler::getActuatingProperties( )
746 // none
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";
769 return aDescriptor;
772 sal_Bool SAL_CALL EventHandler::isComposable( const OUString& /*_rPropertyName*/ )
774 return false;
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() );
803 // the dialog
804 SvxAbstractDialogFactory* pFactory = SvxAbstractDialogFactory::Create();
806 ScopedVclPtr<VclAbstractDialog> pDialog( pFactory->CreateSvxMacroAssignDlg(
807 PropertyHandlerHelper::getDialogParentFrame( m_xContext ),
808 impl_getContextFrame_nothrow(),
809 m_bIsDialogElement,
810 pEventHolder.get(),
811 nInitialSelection
812 ) );
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"
829 setPropertyValue(
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()
852 EventMap aEmpty;
853 m_aEvents.swap( aEmpty );
854 m_xComponent.clear();
857 sal_Bool SAL_CALL EventHandler::suspend( sal_Bool /*_bSuspend*/ )
859 return true;
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 )
891 return i;
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
920 _out_rTypes.clear();
923 // we use a set to avoid duplicates
924 TypeBag aListeners;
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;
983 else
985 OUString sControlService;
986 OSL_VERIFY( m_xComponent->getPropertyValue( PROPERTY_DEFAULTCONTROL ) >>= sControlService );
988 xReturn = m_xContext->getServiceManager()->createInstanceWithContext( sControlService, m_xContext );
990 return xReturn;
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);
998 return pos->second;
1001 namespace
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 )
1008 return false;
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)
1038 // yes
1039 if ( !bResetScript )
1041 // set to something non-empty -> overwrite
1042 pEvent->ScriptCode = sScriptCode;
1043 pEvent->ScriptType = sScriptType;
1045 else
1047 // set to empty -> remove from vector
1048 aEvents.erase(aEvents.begin() + event );
1049 --eventCount;
1051 break;
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 +
1083 "::" +
1084 _rScriptEvent.EventMethod;
1086 bool bExists = xEvents->hasByName( sCompleteName );
1088 if ( bResetScript )
1090 if ( bExists )
1091 xEvents->removeByName( sCompleteName );
1093 else
1095 Any aNewValue; aNewValue <<= _rScriptEvent;
1097 if ( bExists )
1098 xEvents->replaceByName( sCompleteName, aNewValue );
1099 else
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 )
1118 return false;
1119 break;
1120 case FormComponentType::LISTBOX:
1121 if ( ( UID_BRWEVT_CHANGED == _rEvent.sUniqueBrowseId )
1122 || ( UID_BRWEVT_ACTIONPERFORMED == _rEvent.sUniqueBrowseId )
1124 return false;
1125 break;
1128 return true;
1131 } // namespace pcr
1133 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */