1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*************************************************************************
4 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
6 * Copyright 2000, 2010 Oracle and/or its affiliates.
8 * OpenOffice.org - a multi-platform office productivity suite
10 * This file is part of OpenOffice.org.
12 * OpenOffice.org is free software: you can redistribute it and/or modify
13 * it under the terms of the GNU Lesser General Public License version 3
14 * only, as published by the Free Software Foundation.
16 * OpenOffice.org is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Lesser General Public License version 3 for more details
20 * (a copy is included in the LICENSE file that accompanied this code).
22 * You should have received a copy of the GNU Lesser General Public License
23 * version 3 along with OpenOffice.org. If not, see
24 * <http://www.openoffice.org/license.html>
25 * for a copy of the LGPLv3 License.
27 ************************************************************************/
29 #include "fmscriptingenv.hxx"
30 #include "svx/fmmodel.hxx"
32 /** === begin UNO includes === **/
33 #include <com/sun/star/lang/IllegalArgumentException.hpp>
34 #include <com/sun/star/script/XScriptListener.hpp>
35 #include <com/sun/star/container/XHierarchicalNameAccess.hpp>
36 #include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp>
37 #include <com/sun/star/lang/DisposedException.hpp>
38 #include <com/sun/star/lang/EventObject.hpp>
39 #include <com/sun/star/awt/XControl.hpp>
40 /** === end UNO includes === **/
42 #include <tools/diagnose_ex.h>
43 #include <cppuhelper/implbase1.hxx>
44 #include <comphelper/implementationreference.hxx>
45 #include <comphelper/componentcontext.hxx>
46 #include <comphelper/processfactory.hxx>
47 #include <vcl/svapp.hxx>
48 #include <osl/mutex.hxx>
49 #include <sfx2/objsh.hxx>
50 #include <sfx2/app.hxx>
51 #include <basic/basmgr.hxx>
53 #include <boost/shared_ptr.hpp>
55 //........................................................................
58 //........................................................................
60 /** === begin UNO using === **/
61 using ::com::sun::star::uno::Reference
;
62 using ::com::sun::star::script::XEventAttacherManager
;
63 using ::com::sun::star::lang::IllegalArgumentException
;
64 using ::com::sun::star::script::XScriptListener
;
65 using ::com::sun::star::script::ScriptEvent
;
66 using ::com::sun::star::uno::RuntimeException
;
67 using ::com::sun::star::lang::EventObject
;
68 using ::com::sun::star::reflection::InvocationTargetException
;
69 using ::com::sun::star::uno::Any
;
70 using ::com::sun::star::container::XHierarchicalNameAccess
;
71 using ::com::sun::star::reflection::XInterfaceMethodTypeDescription
;
72 using ::com::sun::star::uno::UNO_QUERY_THROW
;
73 using ::com::sun::star::lang::DisposedException
;
74 using ::com::sun::star::uno::RuntimeException
;
75 using ::com::sun::star::uno::Exception
;
76 using ::com::sun::star::uno::Sequence
;
77 using ::com::sun::star::uno::XInterface
;
78 using ::com::sun::star::lang::EventObject
;
79 using ::com::sun::star::awt::XControl
;
80 using ::com::sun::star::beans::XPropertySet
;
81 /** === end UNO using === **/
83 class FormScriptingEnvironment
;
85 //====================================================================
86 //= FormScriptListener
87 //====================================================================
88 typedef ::cppu::WeakImplHelper1
< XScriptListener
89 > FormScriptListener_Base
;
91 /** implements the XScriptListener interface, is used by FormScriptingEnvironment
93 class FormScriptListener
:public FormScriptListener_Base
96 ::osl::Mutex m_aMutex
;
97 FormScriptingEnvironment
*m_pScriptExecutor
;
100 FormScriptListener( FormScriptingEnvironment
* pScriptExecutor
);
103 virtual void SAL_CALL
firing( const ScriptEvent
& aEvent
) throw (RuntimeException
);
104 virtual Any SAL_CALL
approveFiring( const ScriptEvent
& aEvent
) throw (InvocationTargetException
, RuntimeException
);
106 virtual void SAL_CALL
disposing( const EventObject
& Source
) throw (RuntimeException
);
109 void SAL_CALL
dispose();
112 ~FormScriptListener();
115 /** determines whether calling a given method at a given listener interface can be done asynchronously
117 @param _rListenerType
118 the name of the UNO type whose method is to be checked
120 the name of the method at the interface determined by _rListenerType
123 <TRUE/> if and only if the method is declared <code>oneway</code>, i.e. can be called asynchronously
125 bool impl_allowAsynchronousCall_nothrow( const ::rtl::OUString
& _rListenerType
, const ::rtl::OUString
& _rMethodName
) const;
127 /** determines whether the instance is already disposed
129 bool impl_isDisposed_nothrow() const { return !m_pScriptExecutor
; }
131 /** fires the given script event in a thread-safe manner
133 This methods calls our script executor's doFireScriptEvent, with previously releasing the given mutex guard,
134 but ensuring that our script executor is not deleted between this release and the actual call.
137 a clearable guard to our mutex. Must be the only active guard to our mutex.
140 @param _pSyncronousResult
141 a place to take a possible result of the script call.
144 m_pScriptExecutor is not <NULL/>.
146 void impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard
& _rGuard
, const ScriptEvent
& _rEvent
, Any
* _pSyncronousResult
);
149 DECL_LINK( OnAsyncScriptEvent
, ScriptEvent
* );
152 //====================================================================
153 //= FormScriptingEnvironment
154 //====================================================================
155 class FormScriptingEnvironment
: public IFormScriptingEnvironment
158 typedef ::comphelper::ImplementationReference
< FormScriptListener
, XScriptListener
> ListenerImplementation
;
161 ::osl::Mutex m_aMutex
;
162 oslInterlockedCount m_refCount
;
163 ListenerImplementation m_pScriptListener
;
164 FmFormModel
& m_rFormModel
;
168 FormScriptingEnvironment( FmFormModel
& _rModel
);
169 virtual ~FormScriptingEnvironment();
171 // callback for FormScriptListener
172 void doFireScriptEvent( const ScriptEvent
& _rEvent
, Any
* _pSyncronousResult
);
174 // IFormScriptingEnvironment
175 virtual void registerEventAttacherManager( const Reference
< XEventAttacherManager
>& _rxManager
);
176 virtual void revokeEventAttacherManager( const Reference
< XEventAttacherManager
>& _rxManager
);
177 virtual void dispose();
180 virtual oslInterlockedCount SAL_CALL
acquire();
181 virtual oslInterlockedCount SAL_CALL
release();
184 void impl_registerOrRevoke_throw( const Reference
< XEventAttacherManager
>& _rxManager
, bool _bRegister
);
187 FormScriptingEnvironment(); // never implemented
188 FormScriptingEnvironment( const FormScriptingEnvironment
& ); // never implemented
189 FormScriptingEnvironment
& operator=( const FormScriptingEnvironment
& ); // never implemented
192 //====================================================================
193 //= FormScriptListener
194 //====================================================================
195 //--------------------------------------------------------------------
196 FormScriptListener::FormScriptListener( FormScriptingEnvironment
* pScriptExecutor
)
197 :m_pScriptExecutor( pScriptExecutor
)
201 //--------------------------------------------------------------------
202 FormScriptListener::~FormScriptListener()
206 //--------------------------------------------------------------------
207 bool FormScriptListener::impl_allowAsynchronousCall_nothrow( const ::rtl::OUString
& _rListenerType
, const ::rtl::OUString
& _rMethodName
) const
209 bool bAllowAsynchronousCall
= false;
212 ::comphelper::ComponentContext
aContext( ::comphelper::getProcessServiceFactory() );
213 Reference
< XHierarchicalNameAccess
> xTypeDescriptions( aContext
.getSingleton( "com.sun.star.reflection.theTypeDescriptionManager" ), UNO_QUERY_THROW
);
215 ::rtl::OUString
sMethodDescription( _rListenerType
);
216 sMethodDescription
+= ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "::" ));
217 sMethodDescription
+= _rMethodName
;
219 Reference
< XInterfaceMethodTypeDescription
> xMethod( xTypeDescriptions
->getByHierarchicalName( sMethodDescription
), UNO_QUERY_THROW
);
220 bAllowAsynchronousCall
= xMethod
->isOneway();
222 catch( const Exception
& )
224 DBG_UNHANDLED_EXCEPTION();
226 return bAllowAsynchronousCall
;
229 //--------------------------------------------------------------------
230 void FormScriptListener::impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard
& _rGuard
, const ScriptEvent
& _rEvent
, Any
* _pSyncronousResult
)
232 OSL_PRECOND( m_pScriptExecutor
, "FormScriptListener::impl_doFireScriptEvent_nothrow: this will crash!" );
235 m_pScriptExecutor
->doFireScriptEvent( _rEvent
, _pSyncronousResult
);
238 //--------------------------------------------------------------------
239 void SAL_CALL
FormScriptListener::firing( const ScriptEvent
& _rEvent
) throw (RuntimeException
)
241 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
242 static const ::rtl::OUString
vbaInterOp( RTL_CONSTASCII_USTRINGPARAM("VBAInterop") );
243 if ( _rEvent
.ScriptType
.equals(vbaInterOp
) )
244 return; // not handled here
246 if ( impl_isDisposed_nothrow() )
249 if ( !impl_allowAsynchronousCall_nothrow( _rEvent
.ListenerType
.getTypeName(), _rEvent
.MethodName
) )
251 impl_doFireScriptEvent_nothrow( aGuard
, _rEvent
, NULL
);
256 Application::PostUserEvent( LINK( this, FormScriptListener
, OnAsyncScriptEvent
), new ScriptEvent( _rEvent
) );
259 //--------------------------------------------------------------------
260 Any SAL_CALL
FormScriptListener::approveFiring( const ScriptEvent
& _rEvent
) throw (InvocationTargetException
, RuntimeException
)
264 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
265 if ( !impl_isDisposed_nothrow() )
266 impl_doFireScriptEvent_nothrow( aGuard
, _rEvent
, &aResult
);
271 //--------------------------------------------------------------------
272 void SAL_CALL
FormScriptListener::disposing( const EventObject
& /*Source*/ ) throw (RuntimeException
)
277 //--------------------------------------------------------------------
278 void SAL_CALL
FormScriptListener::dispose()
280 ::osl::MutexGuard
aGuard( m_aMutex
);
281 m_pScriptExecutor
= NULL
;
284 //--------------------------------------------------------------------
285 IMPL_LINK( FormScriptListener
, OnAsyncScriptEvent
, ScriptEvent
*, _pEvent
)
287 OSL_PRECOND( _pEvent
!= NULL
, "FormScriptListener::OnAsyncScriptEvent: invalid event!" );
292 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
294 if ( !impl_isDisposed_nothrow() )
295 impl_doFireScriptEvent_nothrow( aGuard
, *_pEvent
, NULL
);
299 // we acquired ourself immediately before posting the event
304 //====================================================================
305 //= FormScriptingEnvironment
306 //====================================================================
307 //--------------------------------------------------------------------
308 FormScriptingEnvironment::FormScriptingEnvironment( FmFormModel
& _rModel
)
310 ,m_pScriptListener( NULL
)
311 ,m_rFormModel( _rModel
)
312 ,m_bDisposed( false )
314 m_pScriptListener
= ListenerImplementation( new FormScriptListener( this ) );
315 // note that this is a cyclic reference between the FormScriptListener and the FormScriptingEnvironment
316 // This cycle is broken up when our instance is disposed.
319 //--------------------------------------------------------------------
320 FormScriptingEnvironment::~FormScriptingEnvironment()
324 //--------------------------------------------------------------------
325 void FormScriptingEnvironment::impl_registerOrRevoke_throw( const Reference
< XEventAttacherManager
>& _rxManager
, bool _bRegister
)
327 ::osl::MutexGuard
aGuard( m_aMutex
);
329 if ( !_rxManager
.is() )
330 throw IllegalArgumentException();
332 throw DisposedException();
337 _rxManager
->addScriptListener( m_pScriptListener
.getRef() );
339 _rxManager
->removeScriptListener( m_pScriptListener
.getRef() );
341 catch( const RuntimeException
& ) { throw; }
342 catch( const Exception
& )
344 DBG_UNHANDLED_EXCEPTION();
348 //--------------------------------------------------------------------
349 void FormScriptingEnvironment::registerEventAttacherManager( const Reference
< XEventAttacherManager
>& _rxManager
)
351 impl_registerOrRevoke_throw( _rxManager
, true );
354 //--------------------------------------------------------------------
355 void FormScriptingEnvironment::revokeEventAttacherManager( const Reference
< XEventAttacherManager
>& _rxManager
)
357 impl_registerOrRevoke_throw( _rxManager
, false );
360 //--------------------------------------------------------------------
361 oslInterlockedCount SAL_CALL
FormScriptingEnvironment::acquire()
363 return osl_incrementInterlockedCount( &m_refCount
);
366 //--------------------------------------------------------------------
367 oslInterlockedCount SAL_CALL
FormScriptingEnvironment::release()
369 if ( 0 == osl_decrementInterlockedCount( &m_refCount
) )
377 //--------------------------------------------------------------------
378 IFormScriptingEnvironment::~IFormScriptingEnvironment()
382 //--------------------------------------------------------------------
385 //................................................................
386 //. NewStyleUNOScript
387 //................................................................
388 class SAL_NO_VTABLE IScript
391 virtual void invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
) = 0;
393 virtual ~IScript() { }
395 typedef ::boost::shared_ptr
< IScript
> PScript
;
397 //................................................................
398 //. NewStyleUNOScript
399 //................................................................
400 class NewStyleUNOScript
: public IScript
402 SfxObjectShell
& m_rObjectShell
;
403 const ::rtl::OUString m_sScriptCode
;
406 NewStyleUNOScript( SfxObjectShell
& _rObjectShell
, const ::rtl::OUString
& _rScriptCode
)
407 :m_rObjectShell( _rObjectShell
)
408 ,m_sScriptCode( _rScriptCode
)
413 virtual void invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
);
416 //................................................................
417 void NewStyleUNOScript::invoke( const Sequence
< Any
>& _rArguments
, Any
& _rSynchronousResult
)
419 Sequence
< sal_Int16
> aOutArgsIndex
;
420 Sequence
< Any
> aOutArgs
;
423 if ( ( _rArguments
.getLength() > 0 ) && ( _rArguments
[ 0 ] >>= aEvent
) )
427 Reference
< XControl
> xControl( aEvent
.Source
, UNO_QUERY_THROW
);
428 Reference
< XPropertySet
> xProps( xControl
->getModel(), UNO_QUERY_THROW
);
429 aCaller
= xProps
->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Name") ) );
431 catch( Exception
& ) {}
433 m_rObjectShell
.CallXScript( m_sScriptCode
, _rArguments
, _rSynchronousResult
, aOutArgsIndex
, aOutArgs
, true, aCaller
.hasValue() ? &aCaller
: 0 );
437 //--------------------------------------------------------------------
438 void FormScriptingEnvironment::doFireScriptEvent( const ScriptEvent
& _rEvent
, Any
* _pSyncronousResult
)
440 #ifdef DISABLE_SCRIPTING
442 (void) _pSyncronousResult
;
444 SolarMutexClearableGuard aSolarGuard
;
445 ::osl::ClearableMutexGuard
aGuard( m_aMutex
);
450 // SfxObjectShellRef is good here since the model controls the lifetime of the object
451 SfxObjectShellRef xObjectShell
= m_rFormModel
.GetObjectShell();
452 if( !xObjectShell
.Is() )
455 // the script to execute
458 if ( _rEvent
.ScriptType
!= "StarBasic" )
460 pScript
.reset( new NewStyleUNOScript( *xObjectShell
, _rEvent
.ScriptCode
) );
464 ::rtl::OUString sScriptCode
= _rEvent
.ScriptCode
;
465 ::rtl::OUString sMacroLocation
;
467 // is there a location in the script name ("application" or "document")?
468 sal_Int32 nPrefixLen
= sScriptCode
.indexOf( ':' );
469 DBG_ASSERT( 0 <= nPrefixLen
, "FormScriptingEnvironment::doFireScriptEvent: Basic script name in old format encountered!" );
471 if ( 0 <= nPrefixLen
)
473 // and it has such a prefix
474 sMacroLocation
= sScriptCode
.copy( 0, nPrefixLen
);
475 DBG_ASSERT( 0 == sMacroLocation
.compareToAscii( "document" )
476 || 0 == sMacroLocation
.compareToAscii( "application" ),
477 "FormScriptingEnvironment::doFireScriptEvent: invalid (unknown) prefix!" );
479 // strip the prefix: the SfxObjectShell::CallScript knows nothing about such prefixes
480 sScriptCode
= sScriptCode
.copy( nPrefixLen
+ 1 );
483 if ( sMacroLocation
.isEmpty() )
485 // legacy format: use the app-wide Basic, if it has a respective method, otherwise fall back to the doc's Basic
486 if ( SFX_APP()->GetBasicManager()->HasMacro( sScriptCode
) )
487 sMacroLocation
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "application" ) );
489 sMacroLocation
= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "document" ) );
492 ::rtl::OUStringBuffer aScriptURI
;
493 aScriptURI
.appendAscii( "vnd.sun.star.script:" );
494 aScriptURI
.append( sScriptCode
);
495 aScriptURI
.appendAscii( "?language=Basic" );
496 aScriptURI
.appendAscii( "&location=" );
497 aScriptURI
.append( sMacroLocation
);
499 const ::rtl::OUString
sScriptURI( aScriptURI
.makeStringAndClear() );
500 pScript
.reset( new NewStyleUNOScript( *xObjectShell
, sScriptURI
) );
503 OSL_ENSURE( pScript
.get(), "FormScriptingEnvironment::doFireScriptEvent: no script to execute!" );
504 if ( !pScript
.get() )
505 // this is an internal error in the above code
506 throw RuntimeException();
512 pScript
->invoke( _rEvent
.Arguments
, _pSyncronousResult
? *_pSyncronousResult
: aIgnoreResult
);
516 // object shells are not thread safe, so guard the destruction
517 SolarMutexGuard aSolarGuarsReset
;
523 //--------------------------------------------------------------------
524 void FormScriptingEnvironment::dispose()
526 ::osl::MutexGuard
aGuard( m_aMutex
);
528 m_pScriptListener
->dispose();
531 //--------------------------------------------------------------------
532 PFormScriptingEnvironment
createDefaultFormScriptingEnvironment( FmFormModel
& _rModel
)
534 return new FormScriptingEnvironment( _rModel
);
537 //........................................................................
538 } // namespace svxform
539 //........................................................................
541 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */