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 u
"com.sun.star.comp.sfx2.SfxMacroLoader"_ustr
;
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 { u
"com.sun.star.frame.ProtocolHandler"_ustr
};
71 SfxObjectShell
* SfxMacroLoader::GetObjectShell(const Reference
<XFrame
>& xFrame
)
73 SfxObjectShell
* pDocShell
= nullptr;
77 SfxFrame
* pFrame
=nullptr;
78 for ( pFrame
= SfxFrame::GetFirst(); pFrame
; pFrame
= SfxFrame::GetNext( *pFrame
) )
80 if ( pFrame
->GetFrameInterface() == xFrame
)
85 pDocShell
= pFrame
->GetCurrentDocument();
91 SfxObjectShell
* SfxMacroLoader::GetObjectShell_Impl()
93 Reference
< XFrame
> xFrame( m_xFrame
.get(), UNO_QUERY
);
94 return SfxMacroLoader::GetObjectShell(xFrame
);
97 uno::Reference
<frame::XDispatch
> SAL_CALL
SfxMacroLoader::queryDispatch(
98 const util::URL
& aURL
,
99 const OUString
& /*sTargetFrameName*/,
100 sal_Int32
/*nSearchFlags*/ )
102 uno::Reference
<frame::XDispatch
> xDispatcher
;
103 if(aURL
.Complete
.startsWith("macro:"))
109 uno::Sequence
< uno::Reference
<frame::XDispatch
> > SAL_CALL
110 SfxMacroLoader::queryDispatches( const uno::Sequence
< frame::DispatchDescriptor
>& seqDescriptor
)
112 sal_Int32 nCount
= seqDescriptor
.getLength();
113 uno::Sequence
< uno::Reference
<frame::XDispatch
> > lDispatcher(nCount
);
114 std::transform(seqDescriptor
.begin(), seqDescriptor
.end(), lDispatcher
.getArray(),
115 [this](const frame::DispatchDescriptor
& rDescr
) -> uno::Reference
<frame::XDispatch
> {
116 return queryDispatch(rDescr
.FeatureURL
, rDescr
.FrameName
, rDescr
.SearchFlags
); });
121 void SAL_CALL
SfxMacroLoader::dispatchWithNotification(
122 const util::URL
& aURL
, const uno::Sequence
<beans::PropertyValue
>& /*lArgs*/,
123 const uno::Reference
<frame::XDispatchResultListener
>& xListener
)
125 SolarMutexGuard aGuard
;
128 ErrCode nErr
= loadMacro( aURL
.Complete
, aAny
, GetObjectShell_Impl() );
129 if( !xListener
.is() )
132 // always call dispatchFinished(), because we didn't load a document but
133 // executed a macro instead!
134 frame::DispatchResultEvent aEvent
;
136 aEvent
.Source
= getXWeak();
137 if( nErr
== ERRCODE_NONE
)
138 aEvent
.State
= frame::DispatchResultState::SUCCESS
;
140 aEvent
.State
= frame::DispatchResultState::FAILURE
;
142 xListener
->dispatchFinished( aEvent
) ;
145 uno::Any SAL_CALL
SfxMacroLoader::dispatchWithReturnValue(
146 const util::URL
& aURL
, const uno::Sequence
<beans::PropertyValue
>& )
149 ErrCode nErr
= loadMacro( aURL
.Complete
, aRet
, GetObjectShell_Impl() );
151 // aRet gets set to a different value only if nErr == ERRCODE_NONE
152 // Return it in such case to preserve the original behaviour
154 // In all other cases (nErr != ERRCODE_NONE), the calling code gets
155 // the actual error code back
156 if ( nErr
!= ERRCODE_NONE
)
158 beans::PropertyValue aErrorCode
;
160 aErrorCode
.Name
= "ErrorCode";
161 aErrorCode
.Value
<<= sal_uInt32(nErr
);
169 void SAL_CALL
SfxMacroLoader::dispatch(
170 const util::URL
& aURL
, const uno::Sequence
<beans::PropertyValue
>& /*lArgs*/ )
172 SolarMutexGuard aGuard
;
175 loadMacro( aURL
.Complete
, aAny
, GetObjectShell_Impl() );
178 void SAL_CALL
SfxMacroLoader::addStatusListener(
179 const uno::Reference
< frame::XStatusListener
>& ,
183 How we can handle different listener for further coming or currently running dispatch() jobs
184 without any inconsistency!
189 void SAL_CALL
SfxMacroLoader::removeStatusListener(
190 const uno::Reference
< frame::XStatusListener
>&,
195 ErrCode
SfxMacroLoader::loadMacro( const OUString
& rURL
, css::uno::Any
& rRetval
, SfxObjectShell
* pSh
)
197 #if !HAVE_FEATURE_SCRIPTING
201 return ERRCODE_BASIC_PROC_UNDEFINED
;
203 SfxObjectShell
* pCurrent
= pSh
;
205 // all not full qualified names use the BASIC of the given or current document
206 pCurrent
= SfxObjectShell::Current();
208 // 'macro:///lib.mod.proc(args)' => macro of App-BASIC
209 // 'macro://[docname|.]/lib.mod.proc(args)' => macro of current or qualified document
210 // 'macro://obj.method(args)' => direct API call, execute it via App-BASIC
211 const OUString
& aMacro( rURL
);
212 sal_Int32 nThirdSlashPos
= aMacro
.indexOf( '/', 8 );
213 sal_Int32 nArgsPos
= aMacro
.indexOf( '(' );
214 BasicManager
*pAppMgr
= SfxApplication::GetBasicManager();
215 BasicManager
*pBasMgr
= nullptr;
216 ErrCode nErr
= ERRCODE_NONE
;
218 // should a macro function be executed ( no direct API call)?
219 if ( -1 != nThirdSlashPos
&& ( -1 == nArgsPos
|| nThirdSlashPos
< nArgsPos
) )
222 SfxObjectShell
* pDoc
= nullptr;
223 OUString
aBasMgrName( INetURLObject::decode(aMacro
.subView( 8, nThirdSlashPos
-8 ), INetURLObject::DecodeMechanism::WithCharset
) );
224 if ( aBasMgrName
.isEmpty() )
226 else if ( aBasMgrName
== "." )
228 // current/actual document
231 pBasMgr
= pDoc
->GetBasicManager();
235 // full qualified name, find document by name
236 for ( SfxObjectShell
*pObjSh
= SfxObjectShell::GetFirst();
238 pObjSh
= SfxObjectShell::GetNext(*pObjSh
) )
239 if ( aBasMgrName
== pObjSh
->GetTitle(SFX_TITLE_APINAME
) )
242 pBasMgr
= pDoc
->GetBasicManager();
248 const bool bIsAppBasic
= ( pBasMgr
== pAppMgr
);
249 const bool bIsDocBasic
= ( pBasMgr
!= pAppMgr
);
253 // security check for macros from document basic if an SFX doc is given
254 if ( !pDoc
->AdjustMacroMode() )
255 // check forbids execution
256 return ERRCODE_IO_ACCESSDENIED
;
260 OUString
aQualifiedMethod( INetURLObject::decode(aMacro
.subView( nThirdSlashPos
+1 ), INetURLObject::DecodeMechanism::WithCharset
) );
262 if ( -1 != nArgsPos
)
264 // remove arguments from macro name
265 aArgs
= aQualifiedMethod
.copy( nArgsPos
- nThirdSlashPos
- 1 );
266 aQualifiedMethod
= aQualifiedMethod
.copy( 0, nArgsPos
- nThirdSlashPos
- 1 );
269 if ( pBasMgr
->HasMacro( aQualifiedMethod
) )
271 Any aOldThisComponent
;
272 const bool bSetDocMacroMode
= ( pDoc
!= nullptr ) && bIsDocBasic
;
273 const bool bSetGlobalThisComponent
= ( pDoc
!= nullptr ) && bIsAppBasic
;
274 if ( bSetDocMacroMode
)
276 // mark document: it executes an own macro, so it's in a modal mode
277 pDoc
->SetMacroMode_Impl();
280 if ( bSetGlobalThisComponent
)
282 // document is executed via AppBASIC, adjust ThisComponent variable
283 pAppMgr
->SetGlobalUNOConstant( u
"ThisComponent"_ustr
, Any( pDoc
->GetModel() ), &aOldThisComponent
);
286 // just to let the shell be alive
287 SfxObjectShellRef xKeepDocAlive
= pDoc
;
290 // attempt to protect the document against the script tampering with its Undo Context
291 std::optional
< ::framework::DocumentUndoGuard
> pUndoGuard
;
293 pUndoGuard
.emplace( pDoc
->GetModel() );
295 // execute the method
296 SbxVariableRef retValRef
= new SbxVariable
;
297 nErr
= pBasMgr
->ExecuteMacro( aQualifiedMethod
, aArgs
, retValRef
.get() );
298 if ( nErr
== ERRCODE_NONE
)
299 rRetval
= sbxToUnoValue( retValRef
.get() );
302 if ( bSetGlobalThisComponent
)
304 pAppMgr
->SetGlobalUNOConstant( u
"ThisComponent"_ustr
, aOldThisComponent
);
307 if ( bSetDocMacroMode
)
309 // remove flag for modal mode
310 pDoc
->SetMacroMode_Impl( false );
314 nErr
= ERRCODE_BASIC_PROC_UNDEFINED
;
317 nErr
= ERRCODE_IO_NOTEXISTS
;
321 // direct API call on a specified object
324 INetURLObject::decode(aMacro
.subView(6),
325 INetURLObject::DecodeMechanism::WithCharset
) +
327 pAppMgr
->GetLib(0)->Execute(aCall
);
328 nErr
= SbxBase::GetError();
331 SbxBase::ResetError();
336 extern "C" SAL_DLLPUBLIC_EXPORT
css::uno::XInterface
*
337 com_sun_star_comp_sfx2_SfxMacroLoader_get_implementation(
338 css::uno::XComponentContext
*,
339 css::uno::Sequence
<css::uno::Any
> const &arguments
)
341 return cppu::acquire(new SfxMacroLoader(arguments
));
344 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */