1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: fmscriptingenv.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_svx.hxx"
33 #include "fmscriptingenv.hxx"
34 #include <svx/fmmodel.hxx>
36 /** === begin UNO includes === **/
37 #include <com/sun/star/lang/IllegalArgumentException.hpp>
38 #include <com/sun/star/script/XScriptListener.hpp>
39 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
40 #include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp>
41 #include <com/sun/star/lang/DisposedException.hpp>
42 #include <com/sun/star/lang/EventObject.hpp>
43 #include <com/sun/star/awt/XControl.hpp>
44 /** === end UNO includes === **/
45 #include <tools/diagnose_ex.h>
46 #include <cppuhelper/implbase1.hxx>
47 #include <comphelper/implementationreference.hxx>
48 #include <comphelper/componentcontext.hxx>
49 #include <comphelper/processfactory.hxx>
50 #include <vcl/svapp.hxx>
51 #include <vos/mutex.hxx>
52 #include <sfx2/objsh.hxx>
54 #include <boost/shared_ptr.hpp>
56 //........................................................................
59 //........................................................................
61 /** === begin UNO using === **/
62 using ::com::sun::star::uno::Reference
;
63 using ::com::sun::star::script::XEventAttacherManager
;
64 using ::com::sun::star::lang::IllegalArgumentException
;
65 using ::com::sun::star::script::XScriptListener
;
66 using ::com::sun::star::script::ScriptEvent
;
67 using ::com::sun::star::uno::RuntimeException
;
68 using ::com::sun::star::lang::EventObject
;
69 using ::com::sun::star::reflection::InvocationTargetException
;
70 using ::com::sun::star::uno::Any
;
71 using ::com::sun::star::container::XHierarchicalNameAccess
;
72 using ::com::sun::star::reflection::XInterfaceMethodTypeDescription
;
73 using ::com::sun::star::uno::UNO_QUERY_THROW
;
74 using ::com::sun::star::lang::DisposedException
;
75 using ::com::sun::star::uno::RuntimeException
;
76 using ::com::sun::star::uno::Exception
;
77 using ::com::sun::star::uno::Sequence
;
78 using ::com::sun::star::uno::XInterface
;
79 using ::com::sun::star::lang::EventObject
;
80 using ::com::sun::star::awt::XControl
;
81 using ::com::sun::star::beans::XPropertySet
;
82 /** === end UNO using === **/
84 class FormScriptingEnvironment
;
86 //====================================================================
87 //= FormScriptListener
88 //====================================================================
89 typedef ::cppu::WeakImplHelper1
< XScriptListener
90 > FormScriptListener_Base
;
92 /** implements the XScriptListener interface, is used by FormScriptingEnvironment
94 class FormScriptListener
:public FormScriptListener_Base
97 ::osl::Mutex m_aMutex
;
98 ::rtl::Reference
< FormScriptingEnvironment
> m_pScriptExecutor
;
101 FormScriptListener( const ::rtl::Reference
< FormScriptingEnvironment
>& _pScriptExecutor
);
104 virtual void SAL_CALL
firing( const ScriptEvent
& aEvent
) throw (RuntimeException
);
105 virtual Any SAL_CALL
approveFiring( const ScriptEvent
& aEvent
) throw (InvocationTargetException
, RuntimeException
);
107 virtual void SAL_CALL
disposing( const EventObject
& Source
) throw (RuntimeException
);
110 void SAL_CALL
dispose();
113 ~FormScriptListener();
116 /** determines whether calling a given method at a given listener interface can be done asynchronously
118 @param _rListenerType
119 the name of the UNO type whose method is to be checked
121 the name of the method at the interface determined by _rListenerType
124 <TRUE/> if and only if the method is declared <code>oneway</code>, i.e. can be called asynchronously
126 bool impl_allowAsynchronousCall_nothrow( const ::rtl::OUString
& _rListenerType
, const ::rtl::OUString
& _rMethodName
) const;
128 /** determines whether the instance is already disposed
130 bool impl_isDisposed_nothrow() const { return !m_pScriptExecutor
.is(); }
132 /** fires the given script event in a thread-safe manner
134 This methods calls our script executor's doFireScriptEvent, with previously releasing the given mutex guard,
135 but ensuring that our script executor is not deleted between this release and the actual call.
138 a clearable guard to our mutex. Must be the only active guard to our mutex.
141 @param _pSyncronousResult
142 a place to take a possible result of the script call.
145 m_pScriptExecutor is not <NULL/>.
147 void impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard
& _rGuard
, const ScriptEvent
& _rEvent
, Any
* _pSyncronousResult
);
150 DECL_LINK( OnAsyncScriptEvent
, ScriptEvent
* );
153 //====================================================================
154 //= FormScriptingEnvironment
155 //====================================================================
156 class FormScriptingEnvironment
: public IFormScriptingEnvironment
159 typedef ::comphelper::ImplementationReference
< FormScriptListener
, XScriptListener
> ListenerImplementation
;
162 ::osl::Mutex m_aMutex
;
163 oslInterlockedCount m_refCount
;
164 ListenerImplementation m_pScriptListener
;
165 FmFormModel
& m_rFormModel
;
169 FormScriptingEnvironment( FmFormModel
& _rModel
);
170 virtual ~FormScriptingEnvironment();
172 // callback for FormScriptListener
173 void doFireScriptEvent( const ScriptEvent
& _rEvent
, Any
* _pSyncronousResult
);
175 // IFormScriptingEnvironment
176 virtual void registerEventAttacherManager( const Reference
< XEventAttacherManager
>& _rxManager
);
177 virtual void revokeEventAttacherManager( const Reference
< XEventAttacherManager
>& _rxManager
);
178 virtual void dispose();
181 virtual oslInterlockedCount SAL_CALL
acquire();
182 virtual oslInterlockedCount SAL_CALL
release();
185 void impl_registerOrRevoke_throw( const Reference
< XEventAttacherManager
>& _rxManager
, bool _bRegister
);
188 FormScriptingEnvironment(); // never implemented
189 FormScriptingEnvironment( const FormScriptingEnvironment
& ); // never implemented
190 FormScriptingEnvironment
& operator=( const FormScriptingEnvironment
& ); // never implemented
193 //====================================================================
194 //= FormScriptListener
195 //====================================================================
196 //--------------------------------------------------------------------
197 FormScriptListener::FormScriptListener( const ::rtl::Reference
< FormScriptingEnvironment
>& _pScriptExecutor
)
198 :m_pScriptExecutor( _pScriptExecutor
)
202 //--------------------------------------------------------------------
203 FormScriptListener::~FormScriptListener()
207 //--------------------------------------------------------------------
208 bool FormScriptListener::impl_allowAsynchronousCall_nothrow( const ::rtl::OUString
& _rListenerType
, const ::rtl::OUString
& _rMethodName
) const
210 bool bAllowAsynchronousCall
= false;
213 ::comphelper::ComponentContext
aContext( ::comphelper::getProcessServiceFactory() );
214 Reference
< XHierarchicalNameAccess
> xTypeDescriptions( aContext
.getSingleton( "com.sun.star.reflection.theTypeDescriptionManager" ), UNO_QUERY_THROW
);
216 ::rtl::OUString
sMethodDescription( _rListenerType
);
217 sMethodDescription
+= ::rtl::OUString::createFromAscii( "::" );
218 sMethodDescription
+= _rMethodName
;
220 Reference
< XInterfaceMethodTypeDescription
> xMethod( xTypeDescriptions
->getByHierarchicalName( sMethodDescription
), UNO_QUERY_THROW
);
221 bAllowAsynchronousCall
= xMethod
->isOneway();
223 catch( const Exception
& )
225 DBG_UNHANDLED_EXCEPTION();
227 return bAllowAsynchronousCall
;
230 //--------------------------------------------------------------------
231 void FormScriptListener::impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard
& _rGuard
, const ScriptEvent
& _rEvent
, Any
* _pSyncronousResult
)
233 OSL_PRECOND( m_pScriptExecutor
.is(), "FormScriptListener::impl_doFireScriptEvent_nothrow: this will crash!" );
235 ::rtl::Reference
< FormScriptingEnvironment
> pExecutor( m_pScriptExecutor
);
237 pExecutor
->doFireScriptEvent( _rEvent
, _pSyncronousResult
);
240 //--------------------------------------------------------------------
241 void SAL_CALL
FormScriptListener::firing( const ScriptEvent
& _rEvent
) throw (RuntimeException
)
243 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
244 static const ::rtl::OUString vbaInterOp
=
245 ::rtl::OUString::createFromAscii("VBAInterop");
246 if ( _rEvent
.ScriptType
.equals(vbaInterOp
) )
247 return; // not handled here
249 if ( impl_isDisposed_nothrow() )
252 if ( !impl_allowAsynchronousCall_nothrow( _rEvent
.ListenerType
.getTypeName(), _rEvent
.MethodName
) )
254 impl_doFireScriptEvent_nothrow( aGuard
, _rEvent
, NULL
);
259 Application::PostUserEvent( LINK( this, FormScriptListener
, OnAsyncScriptEvent
), new ScriptEvent( _rEvent
) );
262 //--------------------------------------------------------------------
263 Any SAL_CALL
FormScriptListener::approveFiring( const ScriptEvent
& _rEvent
) throw (InvocationTargetException
, RuntimeException
)
267 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
268 if ( !impl_isDisposed_nothrow() )
269 impl_doFireScriptEvent_nothrow( aGuard
, _rEvent
, &aResult
);
274 //--------------------------------------------------------------------
275 void SAL_CALL
FormScriptListener::disposing( const EventObject
& /*Source*/ ) throw (RuntimeException
)
280 //--------------------------------------------------------------------
281 void SAL_CALL
FormScriptListener::dispose()
283 ::osl::MutexGuard
aGuard( m_aMutex
);
284 m_pScriptExecutor
= NULL
;
287 //--------------------------------------------------------------------
288 IMPL_LINK( FormScriptListener
, OnAsyncScriptEvent
, ScriptEvent
*, _pEvent
)
290 OSL_PRECOND( _pEvent
!= NULL
, "FormScriptListener::OnAsyncScriptEvent: invalid event!" );
295 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
297 if ( !impl_isDisposed_nothrow() )
298 impl_doFireScriptEvent_nothrow( aGuard
, *_pEvent
, NULL
);
302 // we acquired ourself immediately before posting the event
307 //====================================================================
308 //= FormScriptingEnvironment
309 //====================================================================
310 //--------------------------------------------------------------------
311 FormScriptingEnvironment::FormScriptingEnvironment( FmFormModel
& _rModel
)
313 ,m_pScriptListener( NULL
)
314 ,m_rFormModel( _rModel
)
315 ,m_bDisposed( false )
317 m_pScriptListener
= ListenerImplementation( new FormScriptListener( this ) );
318 // note that this is a cyclic reference between the FormScriptListener and the FormScriptingEnvironment
319 // This cycle is broken up when our instance is disposed.
322 //--------------------------------------------------------------------
323 FormScriptingEnvironment::~FormScriptingEnvironment()
327 //--------------------------------------------------------------------
328 void FormScriptingEnvironment::impl_registerOrRevoke_throw( const Reference
< XEventAttacherManager
>& _rxManager
, bool _bRegister
)
330 ::osl::MutexGuard
aGuard( m_aMutex
);
332 if ( !_rxManager
.is() )
333 throw IllegalArgumentException();
335 throw DisposedException();
340 _rxManager
->addScriptListener( m_pScriptListener
.getRef() );
342 _rxManager
->removeScriptListener( m_pScriptListener
.getRef() );
344 catch( const RuntimeException
& ) { throw; }
345 catch( const Exception
& )
347 DBG_UNHANDLED_EXCEPTION();
351 //--------------------------------------------------------------------
352 void FormScriptingEnvironment::registerEventAttacherManager( const Reference
< XEventAttacherManager
>& _rxManager
)
354 impl_registerOrRevoke_throw( _rxManager
, true );
357 //--------------------------------------------------------------------
358 void FormScriptingEnvironment::revokeEventAttacherManager( const Reference
< XEventAttacherManager
>& _rxManager
)
360 impl_registerOrRevoke_throw( _rxManager
, false );
363 //--------------------------------------------------------------------
364 oslInterlockedCount SAL_CALL
FormScriptingEnvironment::acquire()
366 return osl_incrementInterlockedCount( &m_refCount
);
369 //--------------------------------------------------------------------
370 oslInterlockedCount SAL_CALL
FormScriptingEnvironment::release()
372 if ( 0 == osl_decrementInterlockedCount( &m_refCount
) )
380 //--------------------------------------------------------------------
381 IFormScriptingEnvironment::~IFormScriptingEnvironment()
385 //--------------------------------------------------------------------
388 //................................................................
389 //. NewStyleUNOScript
390 //................................................................
391 class SAL_NO_VTABLE IScript
394 virtual void invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
) = 0;
396 virtual ~IScript() { }
398 typedef ::boost::shared_ptr
< IScript
> PScript
;
400 //................................................................
401 //. NewStyleUNOScript
402 //................................................................
403 class NewStyleUNOScript
: public IScript
405 SfxObjectShell
& m_rObjectShell
;
406 const ::rtl::OUString m_sScriptCode
;
409 NewStyleUNOScript( SfxObjectShell
& _rObjectShell
, const ::rtl::OUString
& _rScriptCode
)
410 :m_rObjectShell( _rObjectShell
)
411 ,m_sScriptCode( _rScriptCode
)
416 virtual void invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
);
419 //................................................................
420 void NewStyleUNOScript::invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
)
422 Sequence
< sal_Int16
> aOutArgsIndex
;
423 Sequence
< Any
> aOutArgs
;
426 if ( ( _rArguments
.getLength() > 0 ) && ( _rArguments
[ 0 ] >>= aEvent
) )
430 Reference
< XControl
> xControl( aEvent
.Source
, UNO_QUERY_THROW
);
431 Reference
< XPropertySet
> xProps( xControl
->getModel(), UNO_QUERY_THROW
);
432 aCaller
= xProps
->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Name") ) );
434 catch( Exception
& ) {}
436 m_rObjectShell
.CallXScript( m_sScriptCode
, _rArguments
, _rSynchronousResult
, aOutArgsIndex
, aOutArgs
, true, aCaller
.hasValue() ? &aCaller
: 0 );
439 //................................................................
440 //. QualifiedBasicScript
441 //................................................................
442 class QualifiedBasicScript
: public IScript
444 SfxObjectShell
& m_rObjectShell
;
445 const ::rtl::OUString m_sMacroLocation
;
446 const ::rtl::OUString m_sScriptCode
;
449 QualifiedBasicScript( SfxObjectShell
& _rObjectShell
, const ::rtl::OUString
& _rLocation
, const ::rtl::OUString
& _rScriptCode
)
450 :m_rObjectShell( _rObjectShell
)
451 ,m_sMacroLocation( _rLocation
)
452 ,m_sScriptCode( _rScriptCode
)
457 virtual void invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
);
460 //................................................................
461 void QualifiedBasicScript::invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
)
463 m_rObjectShell
.CallStarBasicScript( m_sScriptCode
, m_sMacroLocation
,
464 &_rArguments
, &_rSynchronousResult
);
467 //................................................................
468 //. UnqualifiedBasicScript
469 //................................................................
470 class UnqualifiedBasicScript
: public IScript
472 SfxObjectShell
& m_rObjectShell
;
473 const ::rtl::OUString m_sScriptCode
;
476 UnqualifiedBasicScript( SfxObjectShell
& _rObjectShell
, const ::rtl::OUString
& _rScriptCode
)
477 :m_rObjectShell( _rObjectShell
)
478 ,m_sScriptCode( _rScriptCode
)
483 virtual void invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
);
486 //................................................................
487 void UnqualifiedBasicScript::invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
)
489 m_rObjectShell
.CallScript( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StarBasic" ) ), m_sScriptCode
,
490 &_rArguments
, &_rSynchronousResult
);
494 //--------------------------------------------------------------------
495 void FormScriptingEnvironment::doFireScriptEvent( const ScriptEvent
& _rEvent
, Any
* _pSyncronousResult
)
497 ::vos::OClearableGuard
aSolarGuard( Application::GetSolarMutex() );
498 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
503 SfxObjectShellRef xObjectShell
= m_rFormModel
.GetObjectShell();
504 if( !xObjectShell
.Is() )
507 // the script to execute
510 if ( !_rEvent
.ScriptType
.equalsAscii( "StarBasic" ) )
512 pScript
.reset( new NewStyleUNOScript( *xObjectShell
, _rEvent
.ScriptCode
) );
516 ::rtl::OUString sScriptCode
= _rEvent
.ScriptCode
;
517 ::rtl::OUString sMacroLocation
;
519 // is there a location in the script name ("application" or "document")?
520 sal_Int32 nPrefixLen
= sScriptCode
.indexOf( ':' );
521 DBG_ASSERT( 0 <= nPrefixLen
, "FormScriptingEnvironment::doFireScriptEvent: Basic script name in old format encountered!" );
523 if ( 0 <= nPrefixLen
)
525 // and it has such a prefix
526 sMacroLocation
= sScriptCode
.copy( 0, nPrefixLen
);
527 DBG_ASSERT( 0 == sMacroLocation
.compareToAscii( "document" )
528 || 0 == sMacroLocation
.compareToAscii( "application" ),
529 "FormScriptingEnvironment::doFireScriptEvent: invalid (unknown) prefix!" );
531 // strip the prefix: the SfxObjectShell::CallScript knows nothing about such prefixes
532 sScriptCode
= sScriptCode
.copy( nPrefixLen
+ 1 );
535 if ( sMacroLocation
.getLength() )
536 { // we have a StarBasic macro with fully-qualified macro location
537 pScript
.reset( new QualifiedBasicScript( *xObjectShell
, sMacroLocation
, sScriptCode
) );
540 { // we have a StarBasic macro without qualified location - let the object shell gues ....
541 pScript
.reset( new UnqualifiedBasicScript( *xObjectShell
, sScriptCode
) );
545 OSL_ENSURE( pScript
.get(), "FormScriptingEnvironment::doFireScriptEvent: no script to execute!" );
546 if ( !pScript
.get() )
547 // this is an internal error in the above code
548 throw RuntimeException();
554 pScript
->invoke( _rEvent
.Arguments
, _pSyncronousResult
? *_pSyncronousResult
: aIgnoreResult
);
558 // object shells are not thread safe, so guard the destruction
559 ::vos::OGuard
aSolarGuarsReset( Application::GetSolarMutex() );
564 //--------------------------------------------------------------------
565 void FormScriptingEnvironment::dispose()
567 ::osl::MutexGuard
aGuard( m_aMutex
);
569 m_pScriptListener
->dispose();
572 //--------------------------------------------------------------------
573 PFormScriptingEnvironment
createDefaultFormScriptingEnvironment( FmFormModel
& _rModel
)
575 return new FormScriptingEnvironment( _rModel
);
578 //........................................................................
579 } // namespace svxform
580 //........................................................................