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
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 #include <svx/msvbahelper.hxx>
32 #include <basic/sbx.hxx>
33 #include <basic/sbstar.hxx>
34 #include <basic/basmgr.hxx>
35 #include <basic/sbmod.hxx>
36 #include <basic/sbmeth.hxx>
37 #include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
38 #include <com/sun/star/document/XDocumentProperties.hpp>
39 #include <com/sun/star/document/XDocumentInfoSupplier.hpp>
40 #include <tools/urlobj.hxx>
41 #include <osl/file.hxx>
43 using namespace ::com::sun::star
;
45 const static rtl::OUString sUrlPart0
= rtl::OUString::createFromAscii( "vnd.sun.star.script:");
46 const static rtl::OUString sUrlPart1
= rtl::OUString::createFromAscii( "?language=Basic&location=document");
48 namespace ooo
{ namespace vba
{
50 String
makeMacroURL( const String
& sMacroName
)
52 return sUrlPart0
.concat( sMacroName
).concat( sUrlPart1
) ;
55 SfxObjectShell
* findShellForUrl( const rtl::OUString
& sMacroURLOrPath
)
57 SfxObjectShell
* pFoundShell
=NULL
;
58 SfxObjectShell
* pShell
= SfxObjectShell::GetFirst();
60 aObj
.SetURL( sMacroURLOrPath
);
61 bool bIsURL
= aObj
.GetProtocol() != INET_PROT_NOT_VALID
;
64 aURL
= sMacroURLOrPath
;
67 osl::FileBase::getFileURLFromSystemPath( sMacroURLOrPath
, aURL
);
70 OSL_TRACE("Trying to find shell for url %s", rtl::OUStringToOString( aURL
, RTL_TEXTENCODING_UTF8
).getStr() );
74 uno::Reference
< frame::XModel
> xModel
= pShell
->GetModel();
75 // are we searching for a template? if so we have to cater for the
76 // fact that in openoffice a document opened from a template is always
80 OSL_TRACE("shell 0x%x has model with url %s and we look for %s", pShell
81 , rtl::OUStringToOString( xModel
->getURL(), RTL_TEXTENCODING_UTF8
).getStr()
82 , rtl::OUStringToOString( aURL
, RTL_TEXTENCODING_UTF8
).getStr()
84 if ( sMacroURLOrPath
.endsWithIgnoreAsciiCaseAsciiL( ".dot", 4 ) )
86 uno::Reference
< document::XDocumentInfoSupplier
> xDocInfoSupp( xModel
, uno::UNO_QUERY
);
87 if( xDocInfoSupp
.is() )
89 uno::Reference
< document::XDocumentPropertiesSupplier
> xDocPropSupp( xDocInfoSupp
->getDocumentInfo(), uno::UNO_QUERY_THROW
);
90 uno::Reference
< document::XDocumentProperties
> xDocProps( xDocPropSupp
->getDocumentProperties(), uno::UNO_QUERY_THROW
);
91 rtl::OUString sCurrName
= xDocProps
->getTemplateName();
92 if( sMacroURLOrPath
.lastIndexOf( sCurrName
) >= 0 )
101 if ( aURL
.equals( xModel
->getURL() ) )
103 pFoundShell
= pShell
;
108 pShell
= SfxObjectShell::GetNext( *pShell
);
113 // sMod can be empty ( but we really need the library to search in )
114 // if sMod is empty and a macro is found then sMod is updated
115 bool hasMacro( SfxObjectShell
* pShell
, const String
& sLibrary
, String
& sMod
, const String
& sMacro
)
118 if ( sLibrary
.Len() && sMacro
.Len() )
120 OSL_TRACE("** Searching for %s.%s in library %s"
121 ,rtl::OUStringToOString( sMod
, RTL_TEXTENCODING_UTF8
).getStr()
122 ,rtl::OUStringToOString( sMacro
, RTL_TEXTENCODING_UTF8
).getStr()
123 ,rtl::OUStringToOString( sLibrary
, RTL_TEXTENCODING_UTF8
).getStr() );
124 BasicManager
* pBasicMgr
= pShell
-> GetBasicManager();
127 StarBASIC
* pBasic
= pBasicMgr
->GetLib( sLibrary
);
130 USHORT nId
= pBasicMgr
->GetLibId( sLibrary
);
131 pBasicMgr
->LoadLib( nId
);
132 pBasic
= pBasicMgr
->GetLib( sLibrary
);
136 if ( sMod
.Len() ) // we wish to find the macro is a specific module
138 SbModule
* pModule
= pBasic
->FindModule( sMod
);
141 SbxArray
* pMethods
= pModule
->GetMethods();
144 SbMethod
* pMethod
= static_cast< SbMethod
* >( pMethods
->Find( sMacro
, SbxCLASS_METHOD
) );
150 else if( SbMethod
* pMethod
= dynamic_cast< SbMethod
* >( pBasic
->Find( sMacro
, SbxCLASS_METHOD
) ) )
152 if( SbModule
* pModule
= pMethod
->GetModule() )
154 sMod
= pModule
->GetName();
163 void parseMacro( const rtl::OUString
& sMacro
, String
& sContainer
, String
& sModule
, String
& sProcedure
)
165 sal_Int32 nMacroDot
= sMacro
.lastIndexOf( '.' );
167 if ( nMacroDot
!= -1 )
169 sProcedure
= sMacro
.copy( nMacroDot
+ 1 );
171 sal_Int32 nContainerDot
= sMacro
.lastIndexOf( '.', nMacroDot
- 1 );
172 if ( nContainerDot
!= -1 )
174 sModule
= sMacro
.copy( nContainerDot
+ 1, nMacroDot
- nContainerDot
- 1 );
175 sContainer
= sMacro
.copy( 0, nContainerDot
);
178 sModule
= sMacro
.copy( 0, nMacroDot
);
184 VBAMacroResolvedInfo
resolveVBAMacro( SfxObjectShell
* pShell
, const rtl::OUString
& MacroName
, bool bSearchGlobalTemplates
)
186 VBAMacroResolvedInfo aRes
;
189 aRes
.SetMacroDocContext( pShell
);
190 // parse the macro name
191 sal_Int32 nDocSepIndex
= MacroName
.indexOfAsciiL( "!", 1 );
192 String sMacroUrl
= MacroName
;
198 if( nDocSepIndex
> 0 )
200 // macro specified by document name
201 // find document shell for document name and call ourselves
204 // assume for now that the document name is *this* document
205 String sDocUrlOrPath
= MacroName
.copy( 0, nDocSepIndex
);
206 sMacroUrl
= MacroName
.copy( nDocSepIndex
+ 1 );
207 OSL_TRACE("doc search, current shell is 0x%x", pShell
);
208 SfxObjectShell
* pFoundShell
= findShellForUrl( sDocUrlOrPath
);
209 OSL_TRACE("doc search, after find, found shell is 0x%x", pFoundShell
);
210 aRes
= resolveVBAMacro( pFoundShell
, sMacroUrl
);
214 // macro is contained in 'this' document ( or code imported from a template
215 // where that template is a global template or perhaps the template this
216 // document is created from )
218 // macro format = Container.Module.Procedure
219 parseMacro( MacroName
, sContainer
, sModule
, sProcedure
);
220 uno::Reference
< lang::XMultiServiceFactory
> xSF( pShell
->GetModel(), uno::UNO_QUERY
);
221 uno::Reference
< container::XNameContainer
> xPrjNameCache
;
223 xPrjNameCache
.set( xSF
->createInstance( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "ooo.vba.VBAProjectNameProvider" ) ) ), uno::UNO_QUERY
);
225 std::vector
< rtl::OUString
> sSearchList
;
227 if ( sContainer
.Len() > 0 )
229 // get the Project associated with the Container
230 if ( xPrjNameCache
.is() )
232 if ( xPrjNameCache
->hasByName( sContainer
) )
234 rtl::OUString sProject
;
235 xPrjNameCache
->getByName( sContainer
) >>= sProject
;
236 sContainer
= sProject
;
239 sSearchList
.push_back( sContainer
); // First Lib to search
243 // Ok, if we have no Container specified then we need to search them in order, this document, template this document created from, global templates,
244 // get the name of Project/Library for 'this' document
245 rtl::OUString sThisProject
;
246 BasicManager
* pBasicMgr
= pShell
-> GetBasicManager();
249 if ( pBasicMgr
->GetName().Len() )
250 sThisProject
= pBasicMgr
->GetName();
251 else // cater for the case where VBA is not enabled
252 sThisProject
= rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("Standard") );
254 sSearchList
.push_back( sThisProject
); // First Lib to search
255 if ( xPrjNameCache
.is() )
257 // is this document created from a template?
258 uno::Reference
< document::XDocumentInfoSupplier
> xDocInfoSupp( pShell
->GetModel(), uno::UNO_QUERY_THROW
);
259 uno::Reference
< document::XDocumentPropertiesSupplier
> xDocPropSupp( xDocInfoSupp
->getDocumentInfo(), uno::UNO_QUERY_THROW
);
260 uno::Reference
< document::XDocumentProperties
> xDocProps( xDocPropSupp
->getDocumentProperties(), uno::UNO_QUERY_THROW
);
262 rtl::OUString sCreatedFrom
= xDocProps
->getTemplateURL();
263 if ( sCreatedFrom
.getLength() )
266 aObj
.SetURL( sCreatedFrom
);
267 bool bIsURL
= aObj
.GetProtocol() != INET_PROT_NOT_VALID
;
273 osl::FileBase::getFileURLFromSystemPath( sCreatedFrom
, aURL
);
276 sCreatedFrom
= aObj
.GetLastName();
279 sal_Int32 nIndex
= sCreatedFrom
.lastIndexOf( '.' );
281 sCreatedFrom
= sCreatedFrom
.copy( 0, nIndex
);
284 if ( sCreatedFrom
.getLength() && xPrjNameCache
->hasByName( sCreatedFrom
) )
286 xPrjNameCache
->getByName( sCreatedFrom
) >>= sPrj
;
287 // Make sure we don't double up with this project
288 if ( !sPrj
.equals( sThisProject
) )
289 sSearchList
.push_back( sPrj
);
292 // get list of global template Names
293 uno::Sequence
< rtl::OUString
> sTemplateNames
= xPrjNameCache
->getElementNames();
294 sal_Int32 nLen
= sTemplateNames
.getLength();
295 for ( sal_Int32 index
= 0; ( bSearchGlobalTemplates
&& index
< nLen
); ++index
)
298 if ( !sCreatedFrom
.equals( sTemplateNames
[ index
] ) )
300 if ( xPrjNameCache
->hasByName( sTemplateNames
[ index
] ) )
302 xPrjNameCache
->getByName( sTemplateNames
[ index
] ) >>= sPrj
;
303 // Make sure we don't double up with this project
304 if ( !sPrj
.equals( sThisProject
) )
305 sSearchList
.push_back( sPrj
);
312 std::vector
< rtl::OUString
>::iterator it_end
= sSearchList
.end();
313 for ( std::vector
< rtl::OUString
>::iterator it
= sSearchList
.begin(); it
!= it_end
; ++it
)
315 bool bRes
= hasMacro( pShell
, *it
, sModule
, sProcedure
);
318 aRes
.SetResolved( true );
319 aRes
.SetMacroDocContext( pShell
);
325 aRes
.SetResolvedMacro( sProcedure
.Insert( '.', 0 ).Insert( sModule
, 0).Insert( '.', 0 ).Insert( sContainer
, 0 ) );
330 // Treat the args as possible inouts ( convertion at bottom of method )
331 sal_Bool
executeMacro( SfxObjectShell
* pShell
, const String
& sMacroName
, uno::Sequence
< uno::Any
>& aArgs
, uno::Any
& /*aRet*/, const uno::Any
& aCaller
)
333 sal_Bool bRes
= sal_False
;
336 rtl::OUString sUrl
= makeMacroURL( sMacroName
);
338 uno::Sequence
< sal_Int16
> aOutArgsIndex
;
339 uno::Sequence
< uno::Any
> aOutArgs
;
343 uno::Reference
< script::provider::XScriptProvider
> xScriptProvider
;
344 uno::Reference
< script::provider::XScriptProviderSupplier
> xSPS( pShell
->GetModel(), uno::UNO_QUERY_THROW
);
346 xScriptProvider
.set( xSPS
->getScriptProvider(), uno::UNO_QUERY_THROW
);
348 uno::Reference
< script::provider::XScript
> xScript( xScriptProvider
->getScript( sUrl
), uno::UNO_QUERY_THROW
);
350 if ( aCaller
.hasValue() )
352 uno::Reference
< beans::XPropertySet
> xProps( xScript
, uno::UNO_QUERY
);
355 uno::Sequence
< uno::Any
> aCallerHack(1);
356 aCallerHack
[ 0 ] = aCaller
;
357 xProps
->setPropertyValue( rtl::OUString::createFromAscii( "Caller" ), uno::makeAny( aCallerHack
) );
362 xScript
->invoke( aArgs
, aOutArgsIndex
, aOutArgs
);
364 sal_Int32 nLen
= aOutArgs
.getLength();
365 // convert any out params to seem like they were inouts
368 for ( sal_Int32 index
=0; index
< nLen
; ++index
)
370 sal_Int32 nOutIndex
= aOutArgsIndex
[ index
];
371 aArgs
[ nOutIndex
] = aOutArgs
[ index
];
377 catch ( uno::Exception
& e
)