1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <config_features.h>
22 #include <macroloader.hxx>
24 #include <com/sun/star/frame/DispatchResultState.hpp>
25 #include <basic/basmgr.hxx>
26 #include <basic/sbuno.hxx>
27 #include <basic/sberrors.hxx>
28 #include <cppuhelper/supportsservice.hxx>
29 #include <cppuhelper/weak.hxx>
30 #include <cppuhelper/weakref.hxx>
31 #include <framework/documentundoguard.hxx>
32 #include <sfx2/app.hxx>
33 #include <sfx2/frame.hxx>
34 #include <sfx2/objsh.hxx>
35 #include <tools/urlobj.hxx>
36 #include <vcl/svapp.hxx>
40 using namespace ::com::sun::star
;
41 using namespace ::com::sun::star::frame
;
42 using namespace ::com::sun::star::lang
;
43 using namespace ::com::sun::star::uno
;
44 using namespace ::com::sun::star::util
;
46 SfxMacroLoader::SfxMacroLoader(const css::uno::Sequence
< css::uno::Any
>& aArguments
)
48 Reference
< XFrame
> xFrame
;
49 if ( aArguments
.hasElements() )
51 aArguments
[0] >>= xFrame
;
56 OUString SAL_CALL
SfxMacroLoader::getImplementationName()
58 return "com.sun.star.comp.sfx2.SfxMacroLoader";
61 sal_Bool SAL_CALL
SfxMacroLoader::supportsService(OUString
const & ServiceName
)
63 return cppu::supportsService(this, ServiceName
);
66 css::uno::Sequence
<OUString
> SAL_CALL
SfxMacroLoader::getSupportedServiceNames()
68 return { "com.sun.star.frame.ProtocolHandler" };
71 SfxObjectShell
* SfxMacroLoader::GetObjectShell_Impl()
73 SfxObjectShell
* pDocShell
= nullptr;
74 Reference
< XFrame
> xFrame( m_xFrame
.get(), UNO_QUERY
);
77 SfxFrame
* pFrame
=nullptr;
78 for ( pFrame
= SfxFrame::GetFirst(); pFrame
; pFrame
= SfxFrame::GetNext( *pFrame
) )
80 if ( pFrame
->GetFrameInterface() == xFrame
)
85 pDocShell
= pFrame
->GetCurrentDocument();
92 uno::Reference
<frame::XDispatch
> SAL_CALL
SfxMacroLoader::queryDispatch(
93 const util::URL
& aURL
,
94 const OUString
& /*sTargetFrameName*/,
95 sal_Int32
/*nSearchFlags*/ )
97 uno::Reference
<frame::XDispatch
> xDispatcher
;
98 if(aURL
.Complete
.startsWith("macro:"))
104 uno::Sequence
< uno::Reference
<frame::XDispatch
> > SAL_CALL
105 SfxMacroLoader::queryDispatches( const uno::Sequence
< frame::DispatchDescriptor
>& seqDescriptor
)
107 sal_Int32 nCount
= seqDescriptor
.getLength();
108 uno::Sequence
< uno::Reference
<frame::XDispatch
> > lDispatcher(nCount
);
109 std::transform(seqDescriptor
.begin(), seqDescriptor
.end(), lDispatcher
.begin(),
110 [this](const frame::DispatchDescriptor
& rDescr
) -> uno::Reference
<frame::XDispatch
> {
111 return queryDispatch(rDescr
.FeatureURL
, rDescr
.FrameName
, rDescr
.SearchFlags
); });
116 void SAL_CALL
SfxMacroLoader::dispatchWithNotification(
117 const util::URL
& aURL
, const uno::Sequence
<beans::PropertyValue
>& /*lArgs*/,
118 const uno::Reference
<frame::XDispatchResultListener
>& xListener
)
120 SolarMutexGuard aGuard
;
123 ErrCode nErr
= loadMacro( aURL
.Complete
, aAny
, GetObjectShell_Impl() );
124 if( !xListener
.is() )
127 // always call dispatchFinished(), because we didn't load a document but
128 // executed a macro instead!
129 frame::DispatchResultEvent aEvent
;
131 aEvent
.Source
= static_cast< ::cppu::OWeakObject
* >(this);
132 if( nErr
== ERRCODE_NONE
)
133 aEvent
.State
= frame::DispatchResultState::SUCCESS
;
135 aEvent
.State
= frame::DispatchResultState::FAILURE
;
137 xListener
->dispatchFinished( aEvent
) ;
140 uno::Any SAL_CALL
SfxMacroLoader::dispatchWithReturnValue(
141 const util::URL
& aURL
, const uno::Sequence
<beans::PropertyValue
>& )
144 ErrCode nErr
= loadMacro( aURL
.Complete
, aRet
, GetObjectShell_Impl() );
146 // aRet gets set to a different value only if nErr == ERRCODE_NONE
147 // Return it in such case to preserve the original behaviour
149 // In all other cases (nErr != ERRCODE_NONE), the calling code gets
150 // the actual error code back
151 if ( nErr
!= ERRCODE_NONE
)
153 beans::PropertyValue aErrorCode
;
155 aErrorCode
.Name
= "ErrorCode";
156 aErrorCode
.Value
<<= sal_uInt32(nErr
);
164 void SAL_CALL
SfxMacroLoader::dispatch(
165 const util::URL
& aURL
, const uno::Sequence
<beans::PropertyValue
>& /*lArgs*/ )
167 SolarMutexGuard aGuard
;
170 loadMacro( aURL
.Complete
, aAny
, GetObjectShell_Impl() );
173 void SAL_CALL
SfxMacroLoader::addStatusListener(
174 const uno::Reference
< frame::XStatusListener
>& ,
178 How we can handle different listener for further coming or currently running dispatch() jobs
179 without any inconsistency!
184 void SAL_CALL
SfxMacroLoader::removeStatusListener(
185 const uno::Reference
< frame::XStatusListener
>&,
190 ErrCode
SfxMacroLoader::loadMacro( const OUString
& rURL
, css::uno::Any
& rRetval
, SfxObjectShell
* pSh
)
192 #if !HAVE_FEATURE_SCRIPTING
196 return ERRCODE_BASIC_PROC_UNDEFINED
;
198 SfxObjectShell
* pCurrent
= pSh
;
200 // all not full qualified names use the BASIC of the given or current document
201 pCurrent
= SfxObjectShell::Current();
203 // 'macro:///lib.mod.proc(args)' => macro of App-BASIC
204 // 'macro://[docname|.]/lib.mod.proc(args)' => macro of current or qualified document
205 // 'macro://obj.method(args)' => direct API call, execute it via App-BASIC
206 const OUString
& aMacro( rURL
);
207 sal_Int32 nThirdSlashPos
= aMacro
.indexOf( '/', 8 );
208 sal_Int32 nArgsPos
= aMacro
.indexOf( '(' );
209 BasicManager
*pAppMgr
= SfxApplication::GetBasicManager();
210 BasicManager
*pBasMgr
= nullptr;
211 ErrCode nErr
= ERRCODE_NONE
;
213 // should a macro function be executed ( no direct API call)?
214 if ( -1 != nThirdSlashPos
&& ( -1 == nArgsPos
|| nThirdSlashPos
< nArgsPos
) )
217 SfxObjectShell
* pDoc
= nullptr;
218 OUString
aBasMgrName( INetURLObject::decode(aMacro
.copy( 8, nThirdSlashPos
-8 ), INetURLObject::DecodeMechanism::WithCharset
) );
219 if ( aBasMgrName
.isEmpty() )
221 else if ( aBasMgrName
== "." )
223 // current/actual document
226 pBasMgr
= pDoc
->GetBasicManager();
230 // full qualified name, find document by name
231 for ( SfxObjectShell
*pObjSh
= SfxObjectShell::GetFirst();
233 pObjSh
= SfxObjectShell::GetNext(*pObjSh
) )
234 if ( aBasMgrName
== pObjSh
->GetTitle(SFX_TITLE_APINAME
) )
237 pBasMgr
= pDoc
->GetBasicManager();
243 const bool bIsAppBasic
= ( pBasMgr
== pAppMgr
);
244 const bool bIsDocBasic
= ( pBasMgr
!= pAppMgr
);
248 // security check for macros from document basic if an SFX doc is given
249 if ( !pDoc
->AdjustMacroMode() )
250 // check forbids execution
251 return ERRCODE_IO_ACCESSDENIED
;
255 OUString
aQualifiedMethod( INetURLObject::decode(aMacro
.copy( nThirdSlashPos
+1 ), INetURLObject::DecodeMechanism::WithCharset
) );
257 if ( -1 != nArgsPos
)
259 // remove arguments from macro name
260 aArgs
= aQualifiedMethod
.copy( nArgsPos
- nThirdSlashPos
- 1 );
261 aQualifiedMethod
= aQualifiedMethod
.copy( 0, nArgsPos
- nThirdSlashPos
- 1 );
264 if ( pBasMgr
->HasMacro( aQualifiedMethod
) )
266 Any aOldThisComponent
;
267 const bool bSetDocMacroMode
= ( pDoc
!= nullptr ) && bIsDocBasic
;
268 const bool bSetGlobalThisComponent
= ( pDoc
!= nullptr ) && bIsAppBasic
;
269 if ( bSetDocMacroMode
)
271 // mark document: it executes an own macro, so it's in a modal mode
272 pDoc
->SetMacroMode_Impl();
275 if ( bSetGlobalThisComponent
)
277 // document is executed via AppBASIC, adjust ThisComponent variable
278 aOldThisComponent
= pAppMgr
->SetGlobalUNOConstant( "ThisComponent", makeAny( pDoc
->GetModel() ) );
281 // just to let the shell be alive
282 SfxObjectShellRef xKeepDocAlive
= pDoc
;
285 // attempt to protect the document against the script tampering with its Undo Context
286 std::unique_ptr
< ::framework::DocumentUndoGuard
> pUndoGuard
;
288 pUndoGuard
.reset( new ::framework::DocumentUndoGuard( pDoc
->GetModel() ) );
290 // execute the method
291 SbxVariableRef retValRef
= new SbxVariable
;
292 nErr
= pBasMgr
->ExecuteMacro( aQualifiedMethod
, aArgs
, retValRef
.get() );
293 if ( nErr
== ERRCODE_NONE
)
294 rRetval
= sbxToUnoValue( retValRef
.get() );
297 if ( bSetGlobalThisComponent
)
299 pAppMgr
->SetGlobalUNOConstant( "ThisComponent", aOldThisComponent
);
302 if ( bSetDocMacroMode
)
304 // remove flag for modal mode
305 pDoc
->SetMacroMode_Impl( false );
309 nErr
= ERRCODE_BASIC_PROC_UNDEFINED
;
312 nErr
= ERRCODE_IO_NOTEXISTS
;
316 // direct API call on a specified object
317 OUStringBuffer aCall
;
318 aCall
.append('[').append(INetURLObject::decode(aMacro
.copy(6),
319 INetURLObject::DecodeMechanism::WithCharset
));
321 pAppMgr
->GetLib(0)->Execute(aCall
.makeStringAndClear());
322 nErr
= SbxBase::GetError();
325 SbxBase::ResetError();
330 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
331 com_sun_star_comp_sfx2_SfxMacroLoader_get_implementation(
332 css::uno::XComponentContext
*,
333 css::uno::Sequence
<css::uno::Any
> const &arguments
)
335 return cppu::acquire(new SfxMacroLoader(arguments
));
338 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */