Version 3.6.0.4, tag libreoffice-3.6.0.4
[LibreOffice.git] / svx / source / form / fmscriptingenv.cxx
blob22d3bf6999f11bc2cd9ab5f9cea05e6a0eeef0a7
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 //........................................................................
56 namespace svxform
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
95 private:
96 ::osl::Mutex m_aMutex;
97 FormScriptingEnvironment *m_pScriptExecutor;
99 public:
100 FormScriptListener( FormScriptingEnvironment * pScriptExecutor );
102 // XScriptListener
103 virtual void SAL_CALL firing( const ScriptEvent& aEvent ) throw (RuntimeException);
104 virtual Any SAL_CALL approveFiring( const ScriptEvent& aEvent ) throw (InvocationTargetException, RuntimeException);
105 // XEventListener
106 virtual void SAL_CALL disposing( const EventObject& Source ) throw (RuntimeException);
108 // lifetime control
109 void SAL_CALL dispose();
111 protected:
112 ~FormScriptListener();
114 private:
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
119 @param _rMethodName
120 the name of the method at the interface determined by _rListenerType
122 @return
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.
136 @param _rGuard
137 a clearable guard to our mutex. Must be the only active guard to our mutex.
138 @param _rEvent
139 the event to fire
140 @param _pSyncronousResult
141 a place to take a possible result of the script call.
143 @precond
144 m_pScriptExecutor is not <NULL/>.
146 void impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard& _rGuard, const ScriptEvent& _rEvent, Any* _pSyncronousResult );
148 private:
149 DECL_LINK( OnAsyncScriptEvent, ScriptEvent* );
152 //====================================================================
153 //= FormScriptingEnvironment
154 //====================================================================
155 class FormScriptingEnvironment : public IFormScriptingEnvironment
157 private:
158 typedef ::comphelper::ImplementationReference< FormScriptListener, XScriptListener > ListenerImplementation;
160 private:
161 ::osl::Mutex m_aMutex;
162 oslInterlockedCount m_refCount;
163 ListenerImplementation m_pScriptListener;
164 FmFormModel& m_rFormModel;
165 bool m_bDisposed;
167 public:
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();
179 // IReference
180 virtual oslInterlockedCount SAL_CALL acquire();
181 virtual oslInterlockedCount SAL_CALL release();
183 private:
184 void impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister );
186 private:
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!" );
234 _rGuard.clear();
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() )
247 return;
249 if ( !impl_allowAsynchronousCall_nothrow( _rEvent.ListenerType.getTypeName(), _rEvent.MethodName ) )
251 impl_doFireScriptEvent_nothrow( aGuard, _rEvent, NULL );
252 return;
255 acquire();
256 Application::PostUserEvent( LINK( this, FormScriptListener, OnAsyncScriptEvent ), new ScriptEvent( _rEvent ) );
259 //--------------------------------------------------------------------
260 Any SAL_CALL FormScriptListener::approveFiring( const ScriptEvent& _rEvent ) throw (InvocationTargetException, RuntimeException)
262 Any aResult;
264 ::osl::ClearableMutexGuard aGuard( m_aMutex );
265 if ( !impl_isDisposed_nothrow() )
266 impl_doFireScriptEvent_nothrow( aGuard, _rEvent, &aResult );
268 return aResult;
271 //--------------------------------------------------------------------
272 void SAL_CALL FormScriptListener::disposing( const EventObject& /*Source*/ ) throw (RuntimeException)
274 // not interested in
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!" );
288 if ( !_pEvent )
289 return 1L;
292 ::osl::ClearableMutexGuard aGuard( m_aMutex );
294 if ( !impl_isDisposed_nothrow() )
295 impl_doFireScriptEvent_nothrow( aGuard, *_pEvent, NULL );
298 delete _pEvent;
299 // we acquired ourself immediately before posting the event
300 release();
301 return 0L;
304 //====================================================================
305 //= FormScriptingEnvironment
306 //====================================================================
307 //--------------------------------------------------------------------
308 FormScriptingEnvironment::FormScriptingEnvironment( FmFormModel& _rModel )
309 :m_refCount( 0 )
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();
331 if ( m_bDisposed )
332 throw DisposedException();
336 if ( _bRegister )
337 _rxManager->addScriptListener( m_pScriptListener.getRef() );
338 else
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 ) )
371 delete this;
372 return 0;
374 return m_refCount;
377 //--------------------------------------------------------------------
378 IFormScriptingEnvironment::~IFormScriptingEnvironment()
382 //--------------------------------------------------------------------
383 namespace
385 //................................................................
386 //. NewStyleUNOScript
387 //................................................................
388 class SAL_NO_VTABLE IScript
390 public:
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;
405 public:
406 NewStyleUNOScript( SfxObjectShell& _rObjectShell, const ::rtl::OUString& _rScriptCode )
407 :m_rObjectShell( _rObjectShell )
408 ,m_sScriptCode( _rScriptCode )
412 // IScript
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;
421 EventObject aEvent;
422 Any aCaller;
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
441 (void) _rEvent;
442 (void) _pSyncronousResult;
443 #else
444 SolarMutexClearableGuard aSolarGuard;
445 ::osl::ClearableMutexGuard aGuard( m_aMutex );
447 if ( m_bDisposed )
448 return;
450 // SfxObjectShellRef is good here since the model controls the lifetime of the object
451 SfxObjectShellRef xObjectShell = m_rFormModel.GetObjectShell();
452 if( !xObjectShell.Is() )
453 return;
455 // the script to execute
456 PScript pScript;
458 if ( _rEvent.ScriptType != "StarBasic" )
460 pScript.reset( new NewStyleUNOScript( *xObjectShell, _rEvent.ScriptCode ) );
462 else
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" ) );
488 else
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();
508 aGuard.clear();
509 aSolarGuard.clear();
511 Any aIgnoreResult;
512 pScript->invoke( _rEvent.Arguments, _pSyncronousResult ? *_pSyncronousResult : aIgnoreResult );
513 pScript.reset();
516 // object shells are not thread safe, so guard the destruction
517 SolarMutexGuard aSolarGuarsReset;
518 xObjectShell = NULL;
520 #endif
523 //--------------------------------------------------------------------
524 void FormScriptingEnvironment::dispose()
526 ::osl::MutexGuard aGuard( m_aMutex );
527 m_bDisposed = true;
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: */