2676 lines
80 KiB
C++
2676 lines
80 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
|
|
#include <utility>
|
|
#include <vcl/svapp.hxx>
|
|
#include <tools/stream.hxx>
|
|
#include <comphelper/diagnose_ex.hxx>
|
|
#include <svl/SfxBroadcaster.hxx>
|
|
#include <basic/codecompletecache.hxx>
|
|
#include <basic/sbx.hxx>
|
|
#include <basic/sbuno.hxx>
|
|
#include <sbjsmeth.hxx>
|
|
#include <sbjsmod.hxx>
|
|
#include <sbintern.hxx>
|
|
#include <sbprop.hxx>
|
|
#include <image.hxx>
|
|
#include <opcodes.hxx>
|
|
#include <runtime.hxx>
|
|
#include <token.hxx>
|
|
#include <sbunoobj.hxx>
|
|
|
|
#include <sal/log.hxx>
|
|
|
|
#include <basic/sberrors.hxx>
|
|
#include <sbobjmod.hxx>
|
|
#include <basic/vbahelper.hxx>
|
|
#include <comphelper/sequence.hxx>
|
|
#include <cppuhelper/implbase.hxx>
|
|
#include <unotools/eventcfg.hxx>
|
|
#include <com/sun/star/lang/XServiceInfo.hpp>
|
|
#include <com/sun/star/script/ModuleType.hpp>
|
|
#include <com/sun/star/script/vba/XVBACompatibility.hpp>
|
|
#include <com/sun/star/script/vba/VBAScriptEventId.hpp>
|
|
#include <com/sun/star/beans/XPropertySet.hpp>
|
|
#include <com/sun/star/document/XDocumentEventBroadcaster.hpp>
|
|
#include <com/sun/star/document/XDocumentEventListener.hpp>
|
|
|
|
#ifdef UNX
|
|
#include <sys/resource.h>
|
|
#endif
|
|
|
|
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <comphelper/asyncquithandler.hxx>
|
|
#include <map>
|
|
#include <com/sun/star/reflection/ProxyFactory.hpp>
|
|
#include <com/sun/star/uno/XAggregation.hpp>
|
|
#include <com/sun/star/script/XInvocation.hpp>
|
|
|
|
#include <com/sun/star/awt/DialogProvider.hpp>
|
|
#include <com/sun/star/awt/XTopWindow.hpp>
|
|
#include <com/sun/star/awt/XWindow.hpp>
|
|
#include <ooo/vba/VbQueryClose.hpp>
|
|
#include <memory>
|
|
#include <sbxmod.hxx>
|
|
#include <parser.hxx>
|
|
|
|
#include <limits>
|
|
|
|
using namespace com::sun::star;
|
|
using namespace com::sun::star::lang;
|
|
using namespace com::sun::star::reflection;
|
|
using namespace com::sun::star::beans;
|
|
using namespace com::sun::star::script;
|
|
using namespace com::sun::star::uno;
|
|
|
|
typedef ::cppu::WeakImplHelper< XInvocation > DocObjectWrapper_BASE;
|
|
typedef std::map< sal_Int16, Any > OutParamMap;
|
|
|
|
namespace {
|
|
|
|
class DocObjectWrapper : public DocObjectWrapper_BASE
|
|
{
|
|
Reference< XAggregation > m_xAggProxy;
|
|
Reference< XInvocation > m_xAggInv;
|
|
Reference< XTypeProvider > m_xAggregateTypeProv;
|
|
Sequence< Type > m_Types;
|
|
SbModule* m_pMod;
|
|
/// @throws css::uno::RuntimeException
|
|
SbMethodRef getMethod( const OUString& aName );
|
|
/// @throws css::uno::RuntimeException
|
|
SbPropertyRef getProperty( const OUString& aName );
|
|
|
|
public:
|
|
explicit DocObjectWrapper( SbModule* pMod );
|
|
|
|
virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override
|
|
{
|
|
return css::uno::Sequence<sal_Int8>();
|
|
}
|
|
|
|
virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection( ) override;
|
|
|
|
virtual Any SAL_CALL invoke( const OUString& aFunctionName, const Sequence< Any >& aParams, Sequence< ::sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam ) override;
|
|
virtual void SAL_CALL setValue( const OUString& aPropertyName, const Any& aValue ) override;
|
|
virtual Any SAL_CALL getValue( const OUString& aPropertyName ) override;
|
|
virtual sal_Bool SAL_CALL hasMethod( const OUString& aName ) override;
|
|
virtual sal_Bool SAL_CALL hasProperty( const OUString& aName ) override;
|
|
virtual Any SAL_CALL queryInterface( const Type& aType ) override;
|
|
|
|
virtual Sequence< Type > SAL_CALL getTypes() override;
|
|
};
|
|
|
|
}
|
|
|
|
DocObjectWrapper::DocObjectWrapper( SbModule* pVar ) : m_pMod( pVar )
|
|
{
|
|
SbObjModule* pMod = dynamic_cast<SbObjModule*>( pVar );
|
|
if ( !pMod )
|
|
return;
|
|
|
|
if ( pMod->GetModuleType() != ModuleType::DOCUMENT )
|
|
return;
|
|
|
|
// Use proxy factory service to create aggregatable proxy.
|
|
SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>( pMod->GetObject() );
|
|
Reference< XInterface > xIf;
|
|
if ( pUnoObj )
|
|
{
|
|
Any aObj = pUnoObj->getUnoAny();
|
|
aObj >>= xIf;
|
|
if ( xIf.is() )
|
|
{
|
|
m_xAggregateTypeProv.set( xIf, UNO_QUERY );
|
|
m_xAggInv.set( xIf, UNO_QUERY );
|
|
}
|
|
}
|
|
if ( xIf.is() )
|
|
{
|
|
try
|
|
{
|
|
Reference< XProxyFactory > xProxyFac = ProxyFactory::create( comphelper::getProcessComponentContext() );
|
|
m_xAggProxy = xProxyFac->createProxy( xIf );
|
|
}
|
|
catch(const Exception& )
|
|
{
|
|
TOOLS_WARN_EXCEPTION( "basic", "DocObjectWrapper::DocObjectWrapper" );
|
|
}
|
|
}
|
|
|
|
if ( !m_xAggProxy.is() )
|
|
return;
|
|
|
|
osl_atomic_increment( &m_refCount );
|
|
|
|
/* i35609 - Fix crash on Solaris. The setDelegator call needs
|
|
to be in its own block to ensure that all temporary Reference
|
|
instances that are acquired during the call are released
|
|
before m_refCount is decremented again */
|
|
{
|
|
m_xAggProxy->setDelegator( getXWeak() );
|
|
}
|
|
|
|
osl_atomic_decrement( &m_refCount );
|
|
}
|
|
|
|
Sequence< Type > SAL_CALL DocObjectWrapper::getTypes()
|
|
{
|
|
if ( !m_Types.hasElements() )
|
|
{
|
|
Sequence< Type > sTypes;
|
|
if ( m_xAggregateTypeProv.is() )
|
|
{
|
|
sTypes = m_xAggregateTypeProv->getTypes();
|
|
}
|
|
m_Types = comphelper::concatSequences(sTypes,
|
|
Sequence { cppu::UnoType<XInvocation>::get() });
|
|
}
|
|
return m_Types;
|
|
}
|
|
|
|
Reference< XIntrospectionAccess > SAL_CALL
|
|
DocObjectWrapper::getIntrospection( )
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
Any SAL_CALL
|
|
DocObjectWrapper::invoke( const OUString& aFunctionName, const Sequence< Any >& aParams, Sequence< ::sal_Int16 >& aOutParamIndex, Sequence< Any >& aOutParam )
|
|
{
|
|
if ( m_xAggInv.is() && m_xAggInv->hasMethod( aFunctionName ) )
|
|
return m_xAggInv->invoke( aFunctionName, aParams, aOutParamIndex, aOutParam );
|
|
SbMethodRef pMethod = getMethod( aFunctionName );
|
|
if ( !pMethod.is() )
|
|
throw RuntimeException(u"DocObjectWrapper::invoke - Could not get the method reference!"_ustr);
|
|
// check number of parameters
|
|
sal_Int32 nParamsCount = aParams.getLength();
|
|
SbxInfo* pInfo = pMethod->GetInfo();
|
|
if ( pInfo )
|
|
{
|
|
sal_Int32 nSbxOptional = 0;
|
|
sal_uInt16 n = 1;
|
|
for ( const SbxParamInfo* pParamInfo = pInfo->GetParam( n ); pParamInfo; pParamInfo = pInfo->GetParam( ++n ) )
|
|
{
|
|
if ( pParamInfo->nFlags & SbxFlagBits::Optional )
|
|
++nSbxOptional;
|
|
else
|
|
nSbxOptional = 0;
|
|
}
|
|
sal_Int32 nSbxCount = n - 1;
|
|
if ( nParamsCount < nSbxCount - nSbxOptional )
|
|
{
|
|
throw RuntimeException( u"wrong number of parameters!"_ustr );
|
|
}
|
|
}
|
|
// set parameters
|
|
SbxArrayRef xSbxParams;
|
|
if ( nParamsCount > 0 )
|
|
{
|
|
xSbxParams = new SbxArray;
|
|
const Any* pParams = aParams.getConstArray();
|
|
for ( sal_Int32 i = 0; i < nParamsCount; ++i )
|
|
{
|
|
SbxVariableRef xSbxVar = new SbxVariable( SbxVARIANT );
|
|
unoToSbxValue( xSbxVar.get(), pParams[i] );
|
|
xSbxParams->Put(xSbxVar.get(), static_cast<sal_uInt32>(i) + 1);
|
|
|
|
// Enable passing by ref
|
|
if ( xSbxVar->GetType() != SbxVARIANT )
|
|
xSbxVar->SetFlag( SbxFlagBits::Fixed );
|
|
}
|
|
}
|
|
if ( xSbxParams.is() )
|
|
pMethod->SetParameters( xSbxParams.get() );
|
|
|
|
// call method
|
|
SbxVariableRef xReturn = new SbxVariable;
|
|
|
|
pMethod->Call( xReturn.get() );
|
|
Any aReturn;
|
|
// get output parameters
|
|
if ( xSbxParams.is() )
|
|
{
|
|
SbxInfo* pInfo_ = pMethod->GetInfo();
|
|
if ( pInfo_ )
|
|
{
|
|
OutParamMap aOutParamMap;
|
|
for (sal_uInt32 n = 1, nCount = xSbxParams->Count(); n < nCount; ++n)
|
|
{
|
|
assert(n <= std::numeric_limits<sal_uInt16>::max());
|
|
const SbxParamInfo* pParamInfo = pInfo_->GetParam( sal::static_int_cast<sal_uInt16>(n) );
|
|
if ( pParamInfo && ( pParamInfo->eType & SbxBYREF ) != 0 )
|
|
{
|
|
SbxVariable* pVar = xSbxParams->Get(n);
|
|
if ( pVar )
|
|
{
|
|
SbxVariableRef xVar = pVar;
|
|
aOutParamMap.emplace( n - 1, sbxToUnoValue( xVar.get() ) );
|
|
}
|
|
}
|
|
}
|
|
sal_Int32 nOutParamCount = aOutParamMap.size();
|
|
aOutParamIndex.realloc( nOutParamCount );
|
|
aOutParam.realloc( nOutParamCount );
|
|
sal_Int16* pOutParamIndex = aOutParamIndex.getArray();
|
|
Any* pOutParam = aOutParam.getArray();
|
|
for (auto const& outParam : aOutParamMap)
|
|
{
|
|
*pOutParamIndex = outParam.first;
|
|
*pOutParam = outParam.second;
|
|
++pOutParamIndex;
|
|
++pOutParam;
|
|
}
|
|
}
|
|
}
|
|
|
|
// get return value
|
|
aReturn = sbxToUnoValue( xReturn.get() );
|
|
|
|
pMethod->SetParameters( nullptr );
|
|
|
|
return aReturn;
|
|
}
|
|
|
|
void SAL_CALL
|
|
DocObjectWrapper::setValue( const OUString& aPropertyName, const Any& aValue )
|
|
{
|
|
if ( m_xAggInv.is() && m_xAggInv->hasProperty( aPropertyName ) )
|
|
return m_xAggInv->setValue( aPropertyName, aValue );
|
|
|
|
SbPropertyRef pProperty = getProperty( aPropertyName );
|
|
if ( !pProperty.is() )
|
|
throw UnknownPropertyException(aPropertyName);
|
|
unoToSbxValue( pProperty.get(), aValue );
|
|
}
|
|
|
|
Any SAL_CALL
|
|
DocObjectWrapper::getValue( const OUString& aPropertyName )
|
|
{
|
|
if ( m_xAggInv.is() && m_xAggInv->hasProperty( aPropertyName ) )
|
|
return m_xAggInv->getValue( aPropertyName );
|
|
|
|
SbPropertyRef pProperty = getProperty( aPropertyName );
|
|
if ( !pProperty.is() )
|
|
throw UnknownPropertyException(aPropertyName);
|
|
|
|
SbxVariable* pProp = pProperty.get();
|
|
if ( pProp->GetType() == SbxEMPTY )
|
|
pProperty->Broadcast( SfxHintId::BasicDataWanted );
|
|
|
|
Any aRet = sbxToUnoValue( pProp );
|
|
return aRet;
|
|
}
|
|
|
|
sal_Bool SAL_CALL
|
|
DocObjectWrapper::hasMethod( const OUString& aName )
|
|
{
|
|
if ( m_xAggInv.is() && m_xAggInv->hasMethod( aName ) )
|
|
return true;
|
|
return getMethod( aName ).is();
|
|
}
|
|
|
|
sal_Bool SAL_CALL
|
|
DocObjectWrapper::hasProperty( const OUString& aName )
|
|
{
|
|
bool bRes = false;
|
|
if ( m_xAggInv.is() && m_xAggInv->hasProperty( aName ) )
|
|
bRes = true;
|
|
else bRes = getProperty( aName ).is();
|
|
return bRes;
|
|
}
|
|
|
|
Any SAL_CALL DocObjectWrapper::queryInterface( const Type& aType )
|
|
{
|
|
Any aRet = DocObjectWrapper_BASE::queryInterface( aType );
|
|
if ( aRet.hasValue() )
|
|
return aRet;
|
|
else if ( m_xAggProxy.is() )
|
|
aRet = m_xAggProxy->queryAggregation( aType );
|
|
return aRet;
|
|
}
|
|
|
|
SbMethodRef DocObjectWrapper::getMethod( const OUString& aName )
|
|
{
|
|
SbMethodRef pMethod;
|
|
if ( m_pMod )
|
|
{
|
|
SbxFlagBits nSaveFlgs = m_pMod->GetFlags();
|
|
// Limit search to this module
|
|
m_pMod->ResetFlag( SbxFlagBits::GlobalSearch );
|
|
pMethod = dynamic_cast<SbMethod*>(m_pMod->SbModule::Find(aName, SbxClassType::Method));
|
|
m_pMod->SetFlags( nSaveFlgs );
|
|
}
|
|
|
|
return pMethod;
|
|
}
|
|
|
|
SbPropertyRef DocObjectWrapper::getProperty( const OUString& aName )
|
|
{
|
|
SbPropertyRef pProperty;
|
|
if ( m_pMod )
|
|
{
|
|
SbxFlagBits nSaveFlgs = m_pMod->GetFlags();
|
|
// Limit search to this module.
|
|
m_pMod->ResetFlag( SbxFlagBits::GlobalSearch );
|
|
pProperty = dynamic_cast<SbProperty*>(m_pMod->SbModule::Find(aName, SbxClassType::Property));
|
|
m_pMod->SetFlag( nSaveFlgs );
|
|
}
|
|
|
|
return pProperty;
|
|
}
|
|
|
|
|
|
uno::Reference< frame::XModel > getDocumentModel( StarBASIC* pb )
|
|
{
|
|
uno::Reference< frame::XModel > xModel;
|
|
if( pb && pb->IsDocBasic() )
|
|
{
|
|
uno::Any aDoc;
|
|
if( pb->GetUNOConstant( u"ThisComponent"_ustr, aDoc ) )
|
|
xModel.set( aDoc, uno::UNO_QUERY );
|
|
}
|
|
return xModel;
|
|
}
|
|
|
|
static uno::Reference< vba::XVBACompatibility > getVBACompatibility( const uno::Reference< frame::XModel >& rxModel )
|
|
{
|
|
uno::Reference< vba::XVBACompatibility > xVBACompat;
|
|
try
|
|
{
|
|
uno::Reference< beans::XPropertySet > xModelProps( rxModel, uno::UNO_QUERY_THROW );
|
|
xVBACompat.set( xModelProps->getPropertyValue( u"BasicLibraries"_ustr ), uno::UNO_QUERY );
|
|
}
|
|
catch(const uno::Exception& )
|
|
{
|
|
}
|
|
return xVBACompat;
|
|
}
|
|
|
|
static bool getDefaultVBAMode( StarBASIC* pb )
|
|
{
|
|
uno::Reference< frame::XModel > xModel( getDocumentModel( pb ) );
|
|
if (!xModel.is())
|
|
return false;
|
|
uno::Reference< vba::XVBACompatibility > xVBACompat = getVBACompatibility( xModel );
|
|
return xVBACompat.is() && xVBACompat->getVBACompatibilityMode();
|
|
}
|
|
|
|
// A Basic module has set EXTSEARCH, so that the elements, that the module contains,
|
|
// could be found from other module.
|
|
|
|
SbModule::SbModule( const OUString& rName, bool bVBASupport )
|
|
: SbxObject( u"StarBASICModule"_ustr )
|
|
, mbVBASupport(bVBASupport), mbCompat(bVBASupport), bIsProxyModule(false)
|
|
{
|
|
SetName( rName );
|
|
SetFlag( SbxFlagBits::ExtSearch | SbxFlagBits::GlobalSearch );
|
|
SetModuleType( script::ModuleType::NORMAL );
|
|
|
|
// #i92642: Set name property to initial name
|
|
SbxVariable* pNameProp = pProps->Find( u"Name"_ustr, SbxClassType::Property );
|
|
if( pNameProp != nullptr )
|
|
{
|
|
pNameProp->PutString( GetName() );
|
|
}
|
|
}
|
|
|
|
SbModule::~SbModule()
|
|
{
|
|
SAL_INFO("basic","Module named " << GetName() << " is destructing");
|
|
pImage.reset();
|
|
pBreaks.reset();
|
|
pClassData.reset();
|
|
mxWrapper = nullptr;
|
|
}
|
|
|
|
uno::Reference< script::XInvocation > const &
|
|
SbModule::GetUnoModule()
|
|
{
|
|
if ( !mxWrapper.is() )
|
|
mxWrapper = new DocObjectWrapper( this );
|
|
|
|
SAL_INFO("basic","Module named " << GetName() << " returning wrapper mxWrapper (0x" << mxWrapper.get() <<")" );
|
|
return mxWrapper;
|
|
}
|
|
|
|
bool SbModule::IsCompiled() const
|
|
{
|
|
return pImage != nullptr;
|
|
}
|
|
|
|
const SbxObject* SbModule::FindType( const OUString& aTypeName ) const
|
|
{
|
|
return pImage ? pImage->FindType( aTypeName ) : nullptr;
|
|
}
|
|
|
|
|
|
// From the code generator: deletion of images and the opposite of validation for entries
|
|
|
|
void SbModule::StartDefinitions()
|
|
{
|
|
pImage.reset();
|
|
if( pClassData )
|
|
pClassData->clear();
|
|
|
|
// methods and properties persist, but they are invalid;
|
|
// at least are the information under certain conditions clogged
|
|
sal_uInt32 i;
|
|
for (i = 0; i < pMethods->Count(); i++)
|
|
{
|
|
SbMethod* p = dynamic_cast<SbMethod*>(pMethods->Get(i));
|
|
if( p )
|
|
p->bInvalid = true;
|
|
}
|
|
for (i = 0; i < pProps->Count();)
|
|
{
|
|
SbProperty* p = dynamic_cast<SbProperty*>(pProps->Get(i));
|
|
if( p )
|
|
pProps->Remove( i );
|
|
else
|
|
i++;
|
|
}
|
|
}
|
|
|
|
// request/create method
|
|
|
|
SbMethod* SbModule::GetMethod( const OUString& rName, SbxDataType t )
|
|
{
|
|
SbxVariable* p = pMethods->Find( rName, SbxClassType::Method );
|
|
SbMethod* pMeth = dynamic_cast<SbMethod*>( p );
|
|
if( p && !pMeth )
|
|
{
|
|
pMethods->Remove( p );
|
|
}
|
|
if( !pMeth )
|
|
{
|
|
pMeth = new SbMethod( rName, t, this );
|
|
pMeth->SetParent( this );
|
|
pMeth->SetFlags( SbxFlagBits::Read );
|
|
pMethods->Put(pMeth, pMethods->Count());
|
|
StartListening(pMeth->GetBroadcaster(), DuplicateHandling::Prevent);
|
|
}
|
|
// The method is per default valid, because it could be
|
|
// created from the compiler (code generator) as well.
|
|
pMeth->bInvalid = false;
|
|
pMeth->ResetFlag( SbxFlagBits::Fixed );
|
|
pMeth->SetFlag( SbxFlagBits::Write );
|
|
pMeth->SetType( t );
|
|
pMeth->ResetFlag( SbxFlagBits::Write );
|
|
if( t != SbxVARIANT )
|
|
{
|
|
pMeth->SetFlag( SbxFlagBits::Fixed );
|
|
}
|
|
return pMeth;
|
|
}
|
|
|
|
SbMethod* SbModule::FindMethod( const OUString& rName, SbxClassType t )
|
|
{
|
|
return dynamic_cast<SbMethod*> (pMethods->Find( rName, t ));
|
|
}
|
|
|
|
|
|
// request/create property
|
|
|
|
SbProperty* SbModule::GetProperty( const OUString& rName, SbxDataType t )
|
|
{
|
|
SbxVariable* p = pProps->Find( rName, SbxClassType::Property );
|
|
SbProperty* pProp = dynamic_cast<SbProperty*>( p );
|
|
if( p && !pProp )
|
|
{
|
|
pProps->Remove( p );
|
|
}
|
|
if( !pProp )
|
|
{
|
|
pProp = new SbProperty( rName, t, this );
|
|
pProp->SetFlag( SbxFlagBits::ReadWrite );
|
|
pProp->SetParent( this );
|
|
pProps->Put(pProp, pProps->Count());
|
|
StartListening(pProp->GetBroadcaster(), DuplicateHandling::Prevent);
|
|
}
|
|
return pProp;
|
|
}
|
|
|
|
void SbModule::GetProcedureProperty( const OUString& rName, SbxDataType t )
|
|
{
|
|
SbxVariable* p = pProps->Find( rName, SbxClassType::Property );
|
|
SbProcedureProperty* pProp = dynamic_cast<SbProcedureProperty*>( p );
|
|
if( p && !pProp )
|
|
{
|
|
pProps->Remove( p );
|
|
}
|
|
if( !pProp )
|
|
{
|
|
tools::SvRef<SbProcedureProperty> pNewProp = new SbProcedureProperty( rName, t );
|
|
pNewProp->SetFlag( SbxFlagBits::ReadWrite );
|
|
pNewProp->SetParent( this );
|
|
pProps->Put(pNewProp.get(), pProps->Count());
|
|
StartListening(pNewProp->GetBroadcaster(), DuplicateHandling::Prevent);
|
|
}
|
|
}
|
|
|
|
void SbModule::GetIfaceMapperMethod( const OUString& rName, SbMethod* pImplMeth )
|
|
{
|
|
SbxVariable* p = pMethods->Find( rName, SbxClassType::Method );
|
|
SbIfaceMapperMethod* pMapperMethod = dynamic_cast<SbIfaceMapperMethod*>( p );
|
|
if( p && !pMapperMethod )
|
|
{
|
|
pMethods->Remove( p );
|
|
}
|
|
if( !pMapperMethod )
|
|
{
|
|
pMapperMethod = new SbIfaceMapperMethod( rName, pImplMeth );
|
|
pMapperMethod->SetParent( this );
|
|
pMapperMethod->SetFlags( SbxFlagBits::Read );
|
|
pMethods->Put(pMapperMethod, pMethods->Count());
|
|
}
|
|
pMapperMethod->bInvalid = false;
|
|
}
|
|
|
|
SbIfaceMapperMethod::~SbIfaceMapperMethod()
|
|
{
|
|
}
|
|
|
|
|
|
// From the code generator: remove invalid entries
|
|
|
|
void SbModule::EndDefinitions( bool bNewState )
|
|
{
|
|
for (sal_uInt32 i = 0; i < pMethods->Count();)
|
|
{
|
|
SbMethod* p = dynamic_cast<SbMethod*>(pMethods->Get(i));
|
|
if( p )
|
|
{
|
|
if( p->bInvalid )
|
|
{
|
|
pMethods->Remove( p );
|
|
}
|
|
else
|
|
{
|
|
p->bInvalid = bNewState;
|
|
i++;
|
|
}
|
|
}
|
|
else
|
|
i++;
|
|
}
|
|
SetModified( true );
|
|
}
|
|
|
|
void SbModule::Clear()
|
|
{
|
|
pImage.reset();
|
|
if( pClassData )
|
|
pClassData->clear();
|
|
SbxObject::Clear();
|
|
}
|
|
|
|
|
|
SbxVariable* SbModule::Find( const OUString& rName, SbxClassType t )
|
|
{
|
|
// make sure a search in an uninstantiated class module will fail
|
|
SbxVariable* pRes = SbxObject::Find( rName, t );
|
|
if ( bIsProxyModule && !GetSbData()->bRunInit )
|
|
{
|
|
return nullptr;
|
|
}
|
|
if( !pRes && pImage )
|
|
{
|
|
SbiInstance* pInst = GetSbData()->pInst;
|
|
if( pInst && pInst->IsCompatibility() )
|
|
{
|
|
// Put enum types as objects into module,
|
|
// allows MyEnum.First notation
|
|
SbxArrayRef xArray = pImage->GetEnums();
|
|
if( xArray.is() )
|
|
{
|
|
SbxVariable* pEnumVar = xArray->Find( rName, SbxClassType::DontCare );
|
|
SbxObject* pEnumObject = dynamic_cast<SbxObject*>( pEnumVar );
|
|
if( pEnumObject )
|
|
{
|
|
bool bPrivate = pEnumObject->IsSet( SbxFlagBits::Private );
|
|
OUString aEnumName = pEnumObject->GetName();
|
|
|
|
pRes = new SbxVariable( SbxOBJECT );
|
|
pRes->SetName( aEnumName );
|
|
pRes->SetParent( this );
|
|
pRes->SetFlag( SbxFlagBits::Read );
|
|
if( bPrivate )
|
|
{
|
|
pRes->SetFlag( SbxFlagBits::Private );
|
|
}
|
|
pRes->PutObject( pEnumObject );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return pRes;
|
|
}
|
|
|
|
// Parent and BASIC are one!
|
|
|
|
void SbModule::SetParent( SbxObject* p )
|
|
{
|
|
pParent = p;
|
|
}
|
|
|
|
void SbModule::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
|
|
{
|
|
const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint);
|
|
if( !pHint )
|
|
return;
|
|
|
|
SbxVariable* pVar = pHint->GetVar();
|
|
SbProperty* pProp = dynamic_cast<SbProperty*>( pVar );
|
|
SbMethod* pMeth = dynamic_cast<SbMethod*>( pVar );
|
|
SbProcedureProperty* pProcProperty = dynamic_cast<SbProcedureProperty*>( pVar );
|
|
if( pProcProperty )
|
|
{
|
|
|
|
if( pHint->GetId() == SfxHintId::BasicDataWanted )
|
|
{
|
|
OUString aProcName = "Property Get "
|
|
+ pProcProperty->GetName();
|
|
|
|
SbxVariable* pMethVar = Find( aProcName, SbxClassType::Method );
|
|
if( pMethVar )
|
|
{
|
|
SbxValues aVals;
|
|
aVals.eType = SbxVARIANT;
|
|
|
|
SbxArray* pArg = pVar->GetParameters();
|
|
sal_uInt32 nVarParCount = (pArg != nullptr) ? pArg->Count() : 0;
|
|
if( nVarParCount > 1 )
|
|
{
|
|
auto xMethParameters = tools::make_ref<SbxArray>();
|
|
xMethParameters->Put(pMethVar, 0); // Method as parameter 0
|
|
for( sal_uInt32 i = 1 ; i < nVarParCount ; ++i )
|
|
{
|
|
SbxVariable* pPar = pArg->Get(i);
|
|
xMethParameters->Put(pPar, i);
|
|
}
|
|
|
|
pMethVar->SetParameters( xMethParameters.get() );
|
|
pMethVar->Get( aVals );
|
|
pMethVar->SetParameters( nullptr );
|
|
}
|
|
else
|
|
{
|
|
pMethVar->Get( aVals );
|
|
}
|
|
|
|
pVar->Put( aVals );
|
|
}
|
|
}
|
|
else if( pHint->GetId() == SfxHintId::BasicDataChanged )
|
|
{
|
|
SbxVariable* pMethVar = nullptr;
|
|
|
|
bool bSet = pProcProperty->isSet();
|
|
if( bSet )
|
|
{
|
|
pProcProperty->setSet( false );
|
|
|
|
OUString aProcName = "Property Set "
|
|
+ pProcProperty->GetName();
|
|
pMethVar = Find( aProcName, SbxClassType::Method );
|
|
}
|
|
if( !pMethVar ) // Let
|
|
{
|
|
OUString aProcName = "Property Let "
|
|
+ pProcProperty->GetName();
|
|
pMethVar = Find( aProcName, SbxClassType::Method );
|
|
}
|
|
|
|
if( pMethVar )
|
|
{
|
|
// Setup parameters
|
|
SbxArrayRef xArray = new SbxArray;
|
|
xArray->Put(pMethVar, 0); // Method as parameter 0
|
|
xArray->Put(pVar, 1);
|
|
pMethVar->SetParameters( xArray.get() );
|
|
|
|
SbxValues aVals;
|
|
pMethVar->Get( aVals );
|
|
pMethVar->SetParameters( nullptr );
|
|
}
|
|
}
|
|
}
|
|
if( pProp )
|
|
{
|
|
if( pProp->GetModule() != this )
|
|
SetError( ERRCODE_BASIC_BAD_ACTION );
|
|
}
|
|
else if( pMeth )
|
|
{
|
|
if( pHint->GetId() == SfxHintId::BasicDataWanted )
|
|
{
|
|
if( pMeth->bInvalid && !Compile() )
|
|
{
|
|
// auto compile has not worked!
|
|
StarBASIC::Error( ERRCODE_BASIC_BAD_PROP_VALUE );
|
|
}
|
|
else
|
|
{
|
|
// Call of a subprogram
|
|
SbModule* pOld = GetSbData()->pMod;
|
|
GetSbData()->pMod = this;
|
|
Run( static_cast<SbMethod*>(pVar) );
|
|
GetSbData()->pMod = pOld;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// #i92642: Special handling for name property to avoid
|
|
// side effects when using name as variable implicitly
|
|
bool bForwardToSbxObject = true;
|
|
|
|
const SfxHintId nId = pHint->GetId();
|
|
if( (nId == SfxHintId::BasicDataWanted || nId == SfxHintId::BasicDataChanged) &&
|
|
pVar->GetName().equalsIgnoreAsciiCase( "name" ) )
|
|
{
|
|
bForwardToSbxObject = false;
|
|
}
|
|
if( bForwardToSbxObject )
|
|
{
|
|
SbxObject::Notify( rBC, rHint );
|
|
}
|
|
}
|
|
}
|
|
|
|
// The setting of the source makes the image invalid
|
|
// and scans the method definitions newly in
|
|
|
|
void SbModule::SetSource32( const OUString& r )
|
|
{
|
|
// Default basic mode to library container mode, but... allow Option VBASupport 0/1 override
|
|
SetVBASupport( getDefaultVBAMode( static_cast< StarBASIC*>( GetParent() ) ) );
|
|
aOUSource = r;
|
|
StartDefinitions();
|
|
SbiTokenizer aTok( r );
|
|
aTok.SetCompatible( IsVBASupport() );
|
|
|
|
while( !aTok.IsEof() )
|
|
{
|
|
SbiToken eEndTok = NIL;
|
|
|
|
// Searching for SUB or FUNCTION
|
|
SbiToken eLastTok = NIL;
|
|
while( !aTok.IsEof() )
|
|
{
|
|
// #32385: not by declare
|
|
SbiToken eCurTok = aTok.Next();
|
|
if( eLastTok != DECLARE )
|
|
{
|
|
if( eCurTok == SUB )
|
|
{
|
|
eEndTok = ENDSUB; break;
|
|
}
|
|
if( eCurTok == FUNCTION )
|
|
{
|
|
eEndTok = ENDFUNC; break;
|
|
}
|
|
if( eCurTok == PROPERTY )
|
|
{
|
|
eEndTok = ENDPROPERTY; break;
|
|
}
|
|
if( eCurTok == OPTION )
|
|
{
|
|
eCurTok = aTok.Next();
|
|
if( eCurTok == COMPATIBLE )
|
|
{
|
|
mbCompat = true;
|
|
aTok.SetCompatible( true );
|
|
}
|
|
else if ( ( eCurTok == VBASUPPORT ) && ( aTok.Next() == NUMBER ) )
|
|
{
|
|
bool bIsVBA = ( aTok.GetDbl()== 1 );
|
|
SetVBASupport( bIsVBA );
|
|
aTok.SetCompatible( bIsVBA );
|
|
}
|
|
}
|
|
}
|
|
eLastTok = eCurTok;
|
|
}
|
|
// Definition of the method
|
|
SbMethod* pMeth = nullptr;
|
|
if( eEndTok != NIL )
|
|
{
|
|
sal_uInt16 nLine1 = aTok.GetLine();
|
|
if( aTok.Next() == SYMBOL )
|
|
{
|
|
const OUString& rName_( aTok.GetSym() );
|
|
SbxDataType t = aTok.GetType();
|
|
if( t == SbxVARIANT && eEndTok == ENDSUB )
|
|
{
|
|
t = SbxVOID;
|
|
}
|
|
pMeth = GetMethod( rName_, t );
|
|
pMeth->nLine1 = pMeth->nLine2 = nLine1;
|
|
// The method is for a start VALID
|
|
pMeth->bInvalid = false;
|
|
}
|
|
else
|
|
{
|
|
eEndTok = NIL;
|
|
}
|
|
}
|
|
// Skip up to END SUB/END FUNCTION
|
|
if( eEndTok != NIL )
|
|
{
|
|
while( !aTok.IsEof() )
|
|
{
|
|
if( aTok.Next() == eEndTok )
|
|
{
|
|
pMeth->nLine2 = aTok.GetLine();
|
|
break;
|
|
}
|
|
}
|
|
if( aTok.IsEof() )
|
|
{
|
|
pMeth->nLine2 = aTok.GetLine();
|
|
}
|
|
}
|
|
}
|
|
EndDefinitions( true );
|
|
}
|
|
|
|
// Broadcast of a hint to all Basics
|
|
|
|
static void SendHint_( SbxObject* pObj, SfxHintId nId, SbMethod* p )
|
|
{
|
|
// Self a BASIC?
|
|
if( dynamic_cast<const StarBASIC *>(pObj) != nullptr && pObj->IsBroadcaster() )
|
|
pObj->GetBroadcaster().Broadcast( SbxHint( nId, p ) );
|
|
// Then ask for the subobjects
|
|
SbxArray* pObjs = pObj->GetObjects();
|
|
for (sal_uInt32 i = 0; i < pObjs->Count(); i++)
|
|
{
|
|
SbxVariable* pVar = pObjs->Get(i);
|
|
if( dynamic_cast<const SbxObject *>(pVar) != nullptr )
|
|
SendHint_( dynamic_cast<SbxObject*>( pVar), nId, p );
|
|
}
|
|
}
|
|
|
|
static void SendHint( SbxObject* pObj, SfxHintId nId, SbMethod* p )
|
|
{
|
|
while( pObj->GetParent() )
|
|
pObj = pObj->GetParent();
|
|
SendHint_( pObj, nId, p );
|
|
}
|
|
|
|
// #57841 Clear Uno-Objects, which were held in RTL functions,
|
|
// at the end of the program, so that nothing is held
|
|
static void ClearUnoObjectsInRTL_Impl_Rek( StarBASIC* pBasic )
|
|
{
|
|
// delete the return value of CreateUnoService
|
|
SbxVariable* pVar = pBasic->GetRtl()->Find( u"CreateUnoService"_ustr, SbxClassType::Method );
|
|
if( pVar )
|
|
{
|
|
pVar->SbxValue::Clear();
|
|
}
|
|
// delete the return value of CreateUnoDialog
|
|
pVar = pBasic->GetRtl()->Find( u"CreateUnoDialog"_ustr, SbxClassType::Method );
|
|
if( pVar )
|
|
{
|
|
pVar->SbxValue::Clear();
|
|
}
|
|
// delete the return value of CDec
|
|
pVar = pBasic->GetRtl()->Find( u"CDec"_ustr, SbxClassType::Method );
|
|
if( pVar )
|
|
{
|
|
pVar->SbxValue::Clear();
|
|
}
|
|
// delete return value of CreateObject
|
|
pVar = pBasic->GetRtl()->Find( u"CreateObject"_ustr, SbxClassType::Method );
|
|
if( pVar )
|
|
{
|
|
pVar->SbxValue::Clear();
|
|
}
|
|
// Go over all Sub-Basics
|
|
SbxArray* pObjs = pBasic->GetObjects();
|
|
sal_uInt32 nCount = pObjs->Count();
|
|
for( sal_uInt32 i = 0 ; i < nCount ; i++ )
|
|
{
|
|
SbxVariable* pObjVar = pObjs->Get(i);
|
|
StarBASIC* pSubBasic = dynamic_cast<StarBASIC*>( pObjVar );
|
|
if( pSubBasic )
|
|
{
|
|
ClearUnoObjectsInRTL_Impl_Rek( pSubBasic );
|
|
}
|
|
}
|
|
}
|
|
|
|
static void ClearUnoObjectsInRTL_Impl( StarBASIC* pBasic )
|
|
{
|
|
// #67781 Delete return values of the Uno-methods
|
|
clearUnoMethods();
|
|
|
|
ClearUnoObjectsInRTL_Impl_Rek( pBasic );
|
|
|
|
// Search for the topmost Basic
|
|
SbxObject* p = pBasic;
|
|
while( p->GetParent() )
|
|
p = p->GetParent();
|
|
if( static_cast<StarBASIC*>(p) != pBasic )
|
|
ClearUnoObjectsInRTL_Impl_Rek( static_cast<StarBASIC*>(p) );
|
|
}
|
|
|
|
|
|
void SbModule::SetVBASupport( bool bSupport )
|
|
{
|
|
if( mbVBASupport == bSupport )
|
|
return;
|
|
|
|
mbVBASupport = bSupport;
|
|
// initialize VBA document API
|
|
if( mbVBASupport ) try
|
|
{
|
|
mbCompat = true;
|
|
StarBASIC* pBasic = static_cast< StarBASIC* >( GetParent() );
|
|
uno::Reference< lang::XMultiServiceFactory > xFactory( getDocumentModel( pBasic ), uno::UNO_QUERY_THROW );
|
|
xFactory->createInstance( u"ooo.vba.VBAGlobals"_ustr );
|
|
}
|
|
catch( Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
namespace
|
|
{
|
|
class RunInitGuard
|
|
{
|
|
protected:
|
|
std::unique_ptr<SbiRuntime> m_xRt;
|
|
SbiGlobals* m_pSbData;
|
|
SbModule* m_pOldMod;
|
|
public:
|
|
RunInitGuard(SbModule* pModule, SbMethod* pMethod, sal_uInt32 nArg, SbiGlobals* pSbData)
|
|
: m_xRt(new SbiRuntime(pModule, pMethod, nArg))
|
|
, m_pSbData(pSbData)
|
|
, m_pOldMod(pSbData->pMod)
|
|
{
|
|
m_xRt->pNext = pSbData->pInst->pRun;
|
|
m_pSbData->pMod = pModule;
|
|
m_pSbData->pInst->pRun = m_xRt.get();
|
|
}
|
|
void run()
|
|
{
|
|
while (m_xRt->Step()) {}
|
|
}
|
|
virtual ~RunInitGuard()
|
|
{
|
|
m_pSbData->pInst->pRun = m_xRt->pNext;
|
|
m_pSbData->pMod = m_pOldMod;
|
|
m_xRt.reset();
|
|
}
|
|
};
|
|
|
|
class RunGuard : public RunInitGuard
|
|
{
|
|
private:
|
|
bool m_bDelInst;
|
|
public:
|
|
RunGuard(SbModule* pModule, SbMethod* pMethod, sal_uInt32 nArg, SbiGlobals* pSbData, bool bDelInst)
|
|
: RunInitGuard(pModule, pMethod, nArg, pSbData)
|
|
, m_bDelInst(bDelInst)
|
|
{
|
|
if (m_xRt->pNext)
|
|
m_xRt->pNext->block();
|
|
}
|
|
virtual ~RunGuard() override
|
|
{
|
|
if (m_xRt->pNext)
|
|
m_xRt->pNext->unblock();
|
|
|
|
// #63710 It can happen by an another thread handling at events,
|
|
// that the show call returns to a dialog (by closing the
|
|
// dialog per UI), before a by an event triggered further call returned,
|
|
// which stands in Basic more top in the stack and that had been run on
|
|
// a Basic-Breakpoint. Then would the instance below destroyed. And if the Basic,
|
|
// that stand still in the call, further runs, there is a GPF.
|
|
// Thus here had to be wait until the other call comes back.
|
|
if (m_bDelInst)
|
|
{
|
|
// Compare here with 1 instead of 0, because before nCallLvl--
|
|
while (m_pSbData->pInst->nCallLvl != 1 && !Application::IsQuit())
|
|
Application::Yield();
|
|
}
|
|
|
|
m_pSbData->pInst->nCallLvl--; // Call-Level down again
|
|
|
|
// Exist an higher-ranking runtime instance?
|
|
// Then take over BasicDebugFlags::Break, if set
|
|
SbiRuntime* pRtNext = m_xRt->pNext;
|
|
if (pRtNext && (m_xRt->GetDebugFlags() & BasicDebugFlags::Break))
|
|
pRtNext->SetDebugFlags(BasicDebugFlags::Break);
|
|
}
|
|
};
|
|
}
|
|
|
|
// Run a Basic-subprogram
|
|
void SbModule::Run( SbMethod* pMeth )
|
|
{
|
|
SAL_INFO("basic","About to run " << pMeth->GetName() << ", vba compatmode is " << mbVBASupport );
|
|
|
|
static sal_uInt16 nMaxCallLevel = 0;
|
|
|
|
SbiGlobals* pSbData = GetSbData();
|
|
|
|
bool bDelInst = pSbData->pInst == nullptr;
|
|
bool bQuit = false;
|
|
StarBASICRef xBasic;
|
|
uno::Reference< frame::XModel > xModel;
|
|
uno::Reference< script::vba::XVBACompatibility > xVBACompat;
|
|
if( bDelInst )
|
|
{
|
|
// #32779: Hold Basic during the execution
|
|
xBasic = static_cast<StarBASIC*>( GetParent() );
|
|
|
|
pSbData->pInst = new SbiInstance( static_cast<StarBASIC*>(GetParent()) );
|
|
|
|
/* If a VBA script in a document is started, get the VBA compatibility
|
|
interface from the document Basic library container, and notify all
|
|
VBA script listeners about the started script. */
|
|
if( mbVBASupport )
|
|
{
|
|
StarBASIC* pBasic = static_cast< StarBASIC* >( GetParent() );
|
|
if( pBasic && pBasic->IsDocBasic() ) try
|
|
{
|
|
xModel.set( getDocumentModel( pBasic ), uno::UNO_SET_THROW );
|
|
xVBACompat.set( getVBACompatibility( xModel ), uno::UNO_SET_THROW );
|
|
xVBACompat->broadcastVBAScriptEvent( script::vba::VBAScriptEventId::SCRIPT_STARTED, GetName() );
|
|
}
|
|
catch(const uno::Exception& )
|
|
{
|
|
}
|
|
}
|
|
|
|
// Launcher problem
|
|
// i80726 The Find below will generate an error in Testtool so we reset it unless there was one before already
|
|
bool bWasError = SbxBase::GetError() != ERRCODE_NONE;
|
|
SbxVariable* pMSOMacroRuntimeLibVar = Find( u"Launcher"_ustr, SbxClassType::Object );
|
|
if ( !bWasError && (SbxBase::GetError() == ERRCODE_BASIC_PROC_UNDEFINED) )
|
|
SbxBase::ResetError();
|
|
if( pMSOMacroRuntimeLibVar )
|
|
{
|
|
StarBASIC* pMSOMacroRuntimeLib = dynamic_cast<StarBASIC*>( pMSOMacroRuntimeLibVar );
|
|
if( pMSOMacroRuntimeLib )
|
|
{
|
|
SbxFlagBits nGblFlag = pMSOMacroRuntimeLib->GetFlags() & SbxFlagBits::GlobalSearch;
|
|
pMSOMacroRuntimeLib->ResetFlag( SbxFlagBits::GlobalSearch );
|
|
SbxVariable* pAppSymbol = pMSOMacroRuntimeLib->Find( u"Application"_ustr, SbxClassType::Method );
|
|
pMSOMacroRuntimeLib->SetFlag( nGblFlag );
|
|
if( pAppSymbol )
|
|
{
|
|
pMSOMacroRuntimeLib->SetFlag( SbxFlagBits::ExtSearch ); // Could have been disabled before
|
|
pSbData->pMSOMacroRuntimLib = pMSOMacroRuntimeLib;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( nMaxCallLevel == 0 )
|
|
{
|
|
#ifdef UNX
|
|
struct rlimit rl;
|
|
getrlimit ( RLIMIT_STACK, &rl );
|
|
#endif
|
|
#if defined LINUX
|
|
// Empiric value, 900 = needed bytes/Basic call level
|
|
// for Linux including 10% safety margin
|
|
nMaxCallLevel = rl.rlim_cur / 900;
|
|
#elif defined __sun
|
|
// Empiric value, 1650 = needed bytes/Basic call level
|
|
// for Solaris including 10% safety margin
|
|
nMaxCallLevel = rl.rlim_cur / 1650;
|
|
#elif defined _WIN32
|
|
nMaxCallLevel = 5800;
|
|
#else
|
|
nMaxCallLevel = MAXRECURSION;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
// Recursion to deep?
|
|
if( ++pSbData->pInst->nCallLvl <= nMaxCallLevel )
|
|
{
|
|
// Define a globale variable in all Mods
|
|
GlobalRunInit( /* bBasicStart = */ bDelInst );
|
|
|
|
// Appeared a compiler error? Then we don't launch
|
|
if( !pSbData->bGlobalInitErr )
|
|
{
|
|
if( bDelInst )
|
|
{
|
|
SendHint( GetParent(), SfxHintId::BasicStart, pMeth );
|
|
|
|
// 1996-10-16: #31460 New concept for StepInto/Over/Out
|
|
// For an explanation see runtime.cxx at SbiInstance::CalcBreakCallLevel()
|
|
// Identify the BreakCallLevel
|
|
pSbData->pInst->CalcBreakCallLevel( pMeth->GetDebugFlags() );
|
|
}
|
|
|
|
{
|
|
RunGuard xRuntimeGuard(this, pMeth, pMeth->nStart, pSbData, bDelInst);
|
|
|
|
if (mbVBASupport)
|
|
pSbData->pInst->EnableCompatibility(true);
|
|
|
|
xRuntimeGuard.run();
|
|
}
|
|
|
|
if( bDelInst )
|
|
{
|
|
// #57841 Clear Uno-Objects, which were held in RTL functions,
|
|
// at the end of the program, so that nothing is held.
|
|
ClearUnoObjectsInRTL_Impl( xBasic.get() );
|
|
|
|
clearNativeObjectWrapperVector();
|
|
|
|
SAL_WARN_IF(pSbData->pInst->nCallLvl != 0,"basic","BASIC-Call-Level > 0");
|
|
delete pSbData->pInst;
|
|
pSbData->pInst = nullptr;
|
|
bDelInst = false;
|
|
|
|
// #i30690
|
|
SolarMutexGuard aSolarGuard;
|
|
SendHint( GetParent(), SfxHintId::BasicStop, pMeth );
|
|
|
|
GlobalRunDeInit();
|
|
|
|
if( xVBACompat.is() )
|
|
{
|
|
// notify all VBA script listeners about the stopped script
|
|
try
|
|
{
|
|
xVBACompat->broadcastVBAScriptEvent( script::vba::VBAScriptEventId::SCRIPT_STOPPED, GetName() );
|
|
}
|
|
catch(const uno::Exception& )
|
|
{
|
|
}
|
|
// VBA always ensures screenupdating is enabled after completing
|
|
::basic::vba::lockControllersOfAllDocuments( xModel, false );
|
|
::basic::vba::enableContainerWindowsOfAllDocuments( xModel, true );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
pSbData->pInst->nCallLvl--; // Call-Level down again
|
|
}
|
|
else
|
|
{
|
|
pSbData->pInst->nCallLvl--; // Call-Level down again
|
|
StarBASIC::FatalError( ERRCODE_BASIC_STACK_OVERFLOW );
|
|
}
|
|
|
|
StarBASIC* pBasic = dynamic_cast<StarBASIC*>( GetParent() );
|
|
if( bDelInst )
|
|
{
|
|
// #57841 Clear Uno-Objects, which were held in RTL functions,
|
|
// the end of the program, so that nothing is held.
|
|
ClearUnoObjectsInRTL_Impl( xBasic.get() );
|
|
|
|
delete pSbData->pInst;
|
|
pSbData->pInst = nullptr;
|
|
}
|
|
if ( pBasic && pBasic->IsDocBasic() && pBasic->IsQuitApplication() && !pSbData->pInst )
|
|
bQuit = true;
|
|
if ( bQuit )
|
|
{
|
|
Application::PostUserEvent( LINK( &AsyncQuitHandler::instance(), AsyncQuitHandler, OnAsyncQuit ) );
|
|
}
|
|
}
|
|
|
|
// Execute of the init method of a module after the loading
|
|
// or the compilation
|
|
void SbModule::RunInit()
|
|
{
|
|
if( !(pImage
|
|
&& !pImage->bInit
|
|
&& pImage->IsFlag( SbiImageFlags::INITCODE )) )
|
|
return;
|
|
|
|
SbiGlobals* pSbData = GetSbData();
|
|
|
|
// Set flag, so that RunInit get active (Testtool)
|
|
pSbData->bRunInit = true;
|
|
|
|
// The init code starts always here
|
|
RunInitGuard(this, nullptr, 0, pSbData).run();
|
|
|
|
pImage->bInit = true;
|
|
pImage->bFirstInit = false;
|
|
|
|
// RunInit is not active anymore
|
|
pSbData->bRunInit = false;
|
|
}
|
|
|
|
// Delete with private/dim declared variables
|
|
|
|
void SbModule::AddVarName( const OUString& aName )
|
|
{
|
|
// see if the name is added already
|
|
for ( const auto& rModuleVariableName: mModuleVariableNames )
|
|
{
|
|
if ( aName == rModuleVariableName )
|
|
return;
|
|
}
|
|
mModuleVariableNames.push_back( aName );
|
|
}
|
|
|
|
void SbModule::RemoveVars()
|
|
{
|
|
for ( const auto& rModuleVariableName: mModuleVariableNames )
|
|
{
|
|
// We don't want a Find being called in a derived class ( e.g.
|
|
// SbUserform because it could trigger say an initialise event
|
|
// which would cause basic to be re-run in the middle of the init ( and remember RemoveVars is called from compile and we don't want code to run as part of the compile )
|
|
SbxVariableRef p = SbModule::Find( rModuleVariableName, SbxClassType::Property );
|
|
if( p.is() )
|
|
Remove( p.get() );
|
|
}
|
|
}
|
|
|
|
void SbModule::ClearPrivateVars()
|
|
{
|
|
for (sal_uInt32 i = 0; i < pProps->Count(); i++)
|
|
{
|
|
SbProperty* p = dynamic_cast<SbProperty*>(pProps->Get(i));
|
|
if( p )
|
|
{
|
|
// Delete not the arrays, only their content
|
|
if( p->GetType() & SbxARRAY )
|
|
{
|
|
SbxArray* pArray = dynamic_cast<SbxArray*>( p->GetObject() );
|
|
if( pArray )
|
|
{
|
|
for (sal_uInt32 j = 0; j < pArray->Count(); j++)
|
|
{
|
|
SbxVariable* pj = pArray->Get(j);
|
|
pj->SbxValue::Clear();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
p->SbxValue::Clear();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SbModule::implClearIfVarDependsOnDeletedBasic(SbxVariable& rVar, StarBASIC* pDeletedBasic)
|
|
{
|
|
if (rVar.SbxValue::GetType() != SbxOBJECT || dynamic_cast<const SbProcedureProperty*>(&rVar) != nullptr)
|
|
return;
|
|
|
|
SbxObject* pObj = dynamic_cast<SbxObject*>(rVar.GetObject());
|
|
if( pObj == nullptr )
|
|
return;
|
|
|
|
SbxObject* p = pObj;
|
|
|
|
SbModule* pMod = dynamic_cast<SbModule*>( p );
|
|
if( pMod != nullptr )
|
|
pMod->ClearVarsDependingOnDeletedBasic( pDeletedBasic );
|
|
|
|
while( (p = p->GetParent()) != nullptr )
|
|
{
|
|
StarBASIC* pBasic = dynamic_cast<StarBASIC*>( p );
|
|
if( pBasic != nullptr && pBasic == pDeletedBasic )
|
|
{
|
|
rVar.SbxValue::Clear();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SbModule::ClearVarsDependingOnDeletedBasic( StarBASIC* pDeletedBasic )
|
|
{
|
|
for (sal_uInt32 i = 0; i < pProps->Count(); i++)
|
|
{
|
|
SbProperty* p = dynamic_cast<SbProperty*>(pProps->Get(i));
|
|
if( p )
|
|
{
|
|
if( p->GetType() & SbxARRAY )
|
|
{
|
|
SbxArray* pArray = dynamic_cast<SbxArray*>( p->GetObject() );
|
|
if( pArray )
|
|
{
|
|
for (sal_uInt32 j = 0; j < pArray->Count(); j++)
|
|
{
|
|
SbxVariable* pVar = pArray->Get(j);
|
|
implClearIfVarDependsOnDeletedBasic(*pVar, pDeletedBasic);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
implClearIfVarDependsOnDeletedBasic(*p, pDeletedBasic);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarBASIC::ClearAllModuleVars()
|
|
{
|
|
// Initialise the own module
|
|
for (const auto& rModule: pModules)
|
|
{
|
|
// Initialise only, if the startcode was already executed
|
|
if( rModule->pImage && rModule->pImage->bInit && !rModule->isProxyModule() && dynamic_cast<const SbObjModule*>( rModule.get()) == nullptr )
|
|
rModule->ClearPrivateVars();
|
|
}
|
|
|
|
}
|
|
|
|
// Execution of the init-code of all module
|
|
void SbModule::GlobalRunInit( bool bBasicStart )
|
|
{
|
|
// If no Basic-Start, only initialise, if the module is not initialised
|
|
if( !bBasicStart )
|
|
if( !pImage || pImage->bInit )
|
|
return;
|
|
|
|
// Initialise GlobalInitErr-Flag for Compiler-Error
|
|
// With the help of this flags could be located in SbModule::Run() after the call of
|
|
// GlobalRunInit, if at the initialising of the module
|
|
// an error occurred. Then it will not be launched.
|
|
GetSbData()->bGlobalInitErr = false;
|
|
|
|
// Parent of the module is a Basic
|
|
StarBASIC *pBasic = dynamic_cast<StarBASIC*>( GetParent() );
|
|
if( !pBasic )
|
|
return;
|
|
|
|
pBasic->InitAllModules();
|
|
|
|
SbxObject* pParent_ = pBasic->GetParent();
|
|
if( !pParent_ )
|
|
return;
|
|
|
|
StarBASIC * pParentBasic = dynamic_cast<StarBASIC*>( pParent_ );
|
|
if( !pParentBasic )
|
|
return;
|
|
|
|
pParentBasic->InitAllModules( pBasic );
|
|
|
|
// #109018 Parent can also have a parent (library in doc)
|
|
SbxObject* pParentParent = pParentBasic->GetParent();
|
|
if( pParentParent )
|
|
{
|
|
StarBASIC * pParentParentBasic = dynamic_cast<StarBASIC*>( pParentParent );
|
|
if( pParentParentBasic )
|
|
pParentParentBasic->InitAllModules( pParentBasic );
|
|
}
|
|
}
|
|
|
|
void SbModule::GlobalRunDeInit()
|
|
{
|
|
StarBASIC *pBasic = dynamic_cast<StarBASIC*>( GetParent() );
|
|
if( pBasic )
|
|
{
|
|
pBasic->DeInitAllModules();
|
|
|
|
SbxObject* pParent_ = pBasic->GetParent();
|
|
if( pParent_ )
|
|
pBasic = dynamic_cast<StarBASIC*>( pParent_ );
|
|
if( pBasic )
|
|
pBasic->DeInitAllModules();
|
|
}
|
|
}
|
|
|
|
// Search for the next STMNT-Command in the code. This was used from the STMNT-
|
|
// Opcode to set the endcolumn.
|
|
|
|
const sal_uInt8* SbModule::FindNextStmnt( const sal_uInt8* p, sal_uInt16& nLine, sal_uInt16& nCol ) const
|
|
{
|
|
return FindNextStmnt( p, nLine, nCol, false );
|
|
}
|
|
|
|
const sal_uInt8* SbModule::FindNextStmnt( const sal_uInt8* p, sal_uInt16& nLine, sal_uInt16& nCol,
|
|
bool bFollowJumps, const SbiImage* pImg ) const
|
|
{
|
|
sal_uInt32 nPC = static_cast<sal_uInt32>( p - pImage->GetCode() );
|
|
while( nPC < pImage->GetCodeSize() )
|
|
{
|
|
SbiOpcode eOp = static_cast<SbiOpcode>( *p++ );
|
|
nPC++;
|
|
if( bFollowJumps && eOp == SbiOpcode::JUMP_ && pImg )
|
|
{
|
|
SAL_WARN_IF( !pImg, "basic", "FindNextStmnt: pImg==NULL with FollowJumps option" );
|
|
sal_uInt32 nOp1 = *p++; nOp1 |= *p++ << 8;
|
|
nOp1 |= *p++ << 16; nOp1 |= *p++ << 24;
|
|
p = pImg->GetCode() + nOp1;
|
|
}
|
|
else if( eOp >= SbiOpcode::SbOP1_START && eOp <= SbiOpcode::SbOP1_END )
|
|
{
|
|
p += 4;
|
|
nPC += 4;
|
|
}
|
|
else if( eOp == SbiOpcode::STMNT_ )
|
|
{
|
|
sal_uInt32 nl, nc;
|
|
nl = *p++; nl |= *p++ << 8;
|
|
nl |= *p++ << 16 ; nl |= *p++ << 24;
|
|
nc = *p++; nc |= *p++ << 8;
|
|
nc |= *p++ << 16 ; nc |= *p++ << 24;
|
|
nLine = static_cast<sal_uInt16>(nl); nCol = static_cast<sal_uInt16>(nc);
|
|
return p;
|
|
}
|
|
else if( eOp >= SbiOpcode::SbOP2_START && eOp <= SbiOpcode::SbOP2_END )
|
|
{
|
|
p += 8;
|
|
nPC += 8;
|
|
}
|
|
else if( eOp < SbiOpcode::SbOP0_START || eOp > SbiOpcode::SbOP0_END )
|
|
{
|
|
StarBASIC::FatalError( ERRCODE_BASIC_INTERNAL_ERROR );
|
|
break;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Test, if a line contains STMNT-Opcodes
|
|
|
|
bool SbModule::IsBreakable( sal_uInt16 nLine ) const
|
|
{
|
|
if( !pImage )
|
|
return false;
|
|
const sal_uInt8* p = pImage->GetCode();
|
|
sal_uInt16 nl, nc;
|
|
while( ( p = FindNextStmnt( p, nl, nc ) ) != nullptr )
|
|
if( nl == nLine )
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool SbModule::IsBP( sal_uInt16 nLine ) const
|
|
{
|
|
if( pBreaks )
|
|
{
|
|
for( size_t i = 0; i < pBreaks->size(); i++ )
|
|
{
|
|
sal_uInt16 b = pBreaks->operator[]( i );
|
|
if( b == nLine )
|
|
return true;
|
|
if( b < nLine )
|
|
break;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool SbModule::SetBP( sal_uInt16 nLine )
|
|
{
|
|
if( !IsBreakable( nLine ) )
|
|
return false;
|
|
if( !pBreaks )
|
|
pBreaks.reset(new SbiBreakpoints);
|
|
auto it = std::find_if(pBreaks->begin(), pBreaks->end(),
|
|
[&nLine](const sal_uInt16 b) { return b <= nLine; });
|
|
if (it != pBreaks->end() && *it == nLine)
|
|
return true;
|
|
pBreaks->insert( it, nLine );
|
|
|
|
// #38568: Set during runtime as well here BasicDebugFlags::Break
|
|
if( GetSbData()->pInst && GetSbData()->pInst->pRun )
|
|
GetSbData()->pInst->pRun->SetDebugFlags( BasicDebugFlags::Break );
|
|
|
|
return IsBreakable( nLine );
|
|
}
|
|
|
|
bool SbModule::ClearBP( sal_uInt16 nLine )
|
|
{
|
|
bool bRes = false;
|
|
if( pBreaks )
|
|
{
|
|
auto it = std::find_if(pBreaks->begin(), pBreaks->end(),
|
|
[&nLine](const sal_uInt16 b) { return b <= nLine; });
|
|
bRes = (it != pBreaks->end()) && (*it == nLine);
|
|
if (bRes)
|
|
pBreaks->erase(it);
|
|
if (pBreaks->empty())
|
|
pBreaks.reset();
|
|
}
|
|
return bRes;
|
|
}
|
|
|
|
void SbModule::ClearAllBP()
|
|
{
|
|
pBreaks.reset();
|
|
}
|
|
|
|
void
|
|
SbModule::fixUpMethodStart( bool bCvtToLegacy, SbiImage* pImg ) const
|
|
{
|
|
if ( !pImg )
|
|
pImg = pImage.get();
|
|
for (sal_uInt32 i = 0; i < pMethods->Count(); i++)
|
|
{
|
|
SbMethod* pMeth = dynamic_cast<SbMethod*>(pMethods->Get(i));
|
|
if( pMeth )
|
|
{
|
|
//fixup method start positions
|
|
if ( bCvtToLegacy )
|
|
pMeth->nStart = pImg->CalcLegacyOffset( pMeth->nStart );
|
|
else
|
|
pMeth->nStart = pImg->CalcNewOffset( static_cast<sal_uInt16>(pMeth->nStart) );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
bool SbModule::LoadData( SvStream& rStrm, sal_uInt16 nVer )
|
|
{
|
|
Clear();
|
|
if( !SbxObject::LoadData( rStrm, 1 ) )
|
|
return false;
|
|
// As a precaution...
|
|
SetFlag( SbxFlagBits::ExtSearch | SbxFlagBits::GlobalSearch );
|
|
sal_uInt8 bImage;
|
|
rStrm.ReadUChar( bImage );
|
|
if( !bImage )
|
|
return true;
|
|
|
|
std::unique_ptr<SbiImage> p(new SbiImage);
|
|
sal_uInt32 nImgVer = 0;
|
|
|
|
if( !p->Load( rStrm, nImgVer ) )
|
|
{
|
|
return false;
|
|
}
|
|
// If the image is in old format, we fix up the method start offsets
|
|
if ( nImgVer < B_IMG_VERSION_12 )
|
|
{
|
|
fixUpMethodStart( false, p.get() );
|
|
p->ReleaseLegacyBuffer();
|
|
}
|
|
aComment = p->aComment;
|
|
SetName( p->aName );
|
|
if( p->GetCodeSize() )
|
|
{
|
|
aOUSource = p->aOUSource;
|
|
// Old version: image away
|
|
if( nVer == 1 )
|
|
{
|
|
SetSource32( p->aOUSource );
|
|
}
|
|
else
|
|
pImage = std::move(p);
|
|
}
|
|
else
|
|
{
|
|
SetSource32( p->aOUSource );
|
|
}
|
|
return true;
|
|
}
|
|
|
|
std::pair<bool, sal_uInt32> SbModule::StoreData( SvStream& rStrm ) const
|
|
{
|
|
bool bFixup = ( pImage && !pImage->ExceedsLegacyLimits() );
|
|
if ( bFixup )
|
|
fixUpMethodStart( true );
|
|
const auto [bSuccess, nVersion] = SbxObject::StoreData(rStrm);
|
|
if (!bSuccess)
|
|
return { false, 0 };
|
|
|
|
if( pImage )
|
|
{
|
|
pImage->aOUSource = aOUSource;
|
|
pImage->aComment = aComment;
|
|
pImage->aName = GetName();
|
|
rStrm.WriteUChar( 1 );
|
|
// # PCode is saved only for legacy formats only
|
|
// It should be noted that it probably isn't necessary
|
|
// It would be better not to store the image ( more flexible with
|
|
// formats )
|
|
bool bRes = pImage->Save( rStrm, nVersion );
|
|
if ( bFixup )
|
|
fixUpMethodStart( false ); // restore method starts
|
|
return { bRes, nVersion };
|
|
|
|
}
|
|
else
|
|
{
|
|
SbiImage aImg;
|
|
aImg.aOUSource = aOUSource;
|
|
aImg.aComment = aComment;
|
|
aImg.aName = GetName();
|
|
rStrm.WriteUChar( 1 );
|
|
return { aImg.Save(rStrm, nVersion), nVersion };
|
|
}
|
|
}
|
|
|
|
bool SbModule::ExceedsImgVersion12ModuleSize()
|
|
{
|
|
if ( !IsCompiled() )
|
|
Compile();
|
|
return pImage && pImage->ExceedsImgVersion12Limits();
|
|
}
|
|
|
|
namespace {
|
|
|
|
class ErrorHdlResetter
|
|
{
|
|
Link<StarBASIC*,bool> mErrHandler;
|
|
bool mbError;
|
|
public:
|
|
ErrorHdlResetter()
|
|
: mErrHandler(StarBASIC::GetGlobalErrorHdl()) // save error handler
|
|
, mbError( false )
|
|
{
|
|
// set new error handler
|
|
StarBASIC::SetGlobalErrorHdl( LINK( this, ErrorHdlResetter, BasicErrorHdl ) );
|
|
}
|
|
~ErrorHdlResetter()
|
|
{
|
|
// restore error handler
|
|
StarBASIC::SetGlobalErrorHdl(mErrHandler);
|
|
}
|
|
DECL_LINK( BasicErrorHdl, StarBASIC *, bool );
|
|
bool HasError() const { return mbError; }
|
|
};
|
|
|
|
}
|
|
|
|
IMPL_LINK( ErrorHdlResetter, BasicErrorHdl, StarBASIC *, /*pBasic*/, bool)
|
|
{
|
|
mbError = true;
|
|
return false;
|
|
}
|
|
|
|
void SbModule::GetCodeCompleteDataFromParse(CodeCompleteDataCache& aCache)
|
|
{
|
|
ErrorHdlResetter aErrHdl;
|
|
SbxBase::ResetError();
|
|
|
|
SbiParser aParser(static_cast<StarBASIC*>(GetParent()), this );
|
|
aParser.SetCodeCompleting(true);
|
|
|
|
while( aParser.Parse() ) {}
|
|
SbiSymPool* pPool = aParser.pPool;
|
|
aCache.Clear();
|
|
for( sal_uInt16 i = 0; i < pPool->GetSize(); ++i )
|
|
{
|
|
SbiSymDef* pSymDef = pPool->Get(i);
|
|
//std::cerr << "i: " << i << ", type: " << pSymDef->GetType() << "; name:" << pSymDef->GetName() << std::endl;
|
|
if( (pSymDef->GetType() != SbxEMPTY) && (pSymDef->GetType() != SbxNULL) )
|
|
aCache.InsertGlobalVar( pSymDef->GetName(), aParser.aGblStrings.Find(pSymDef->GetTypeId()) );
|
|
|
|
SbiSymPool& rChildPool = pSymDef->GetPool();
|
|
for(sal_uInt16 j = 0; j < rChildPool.GetSize(); ++j )
|
|
{
|
|
SbiSymDef* pChildSymDef = rChildPool.Get(j);
|
|
//std::cerr << "j: " << j << ", type: " << pChildSymDef->GetType() << "; name:" << pChildSymDef->GetName() << std::endl;
|
|
if( (pChildSymDef->GetType() != SbxEMPTY) && (pChildSymDef->GetType() != SbxNULL) )
|
|
aCache.InsertLocalVar( pSymDef->GetName(), pChildSymDef->GetName(), aParser.aGblStrings.Find(pChildSymDef->GetTypeId()) );
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
OUString SbModule::GetKeywordCase( std::u16string_view sKeyword )
|
|
{
|
|
return SbiParser::GetKeywordCase( sKeyword );
|
|
}
|
|
|
|
bool SbModule::HasExeCode()
|
|
{
|
|
// And empty Image always has the Global Chain set up
|
|
static const unsigned char pEmptyImage[] = { 0x45, 0x0 , 0x0, 0x0, 0x0 };
|
|
// let's be stricter for the moment than VBA
|
|
|
|
if (!IsCompiled())
|
|
{
|
|
ErrorHdlResetter aGblErrHdl;
|
|
Compile();
|
|
if (aGblErrHdl.HasError()) //assume unsafe on compile error
|
|
return true;
|
|
}
|
|
|
|
bool bRes = false;
|
|
if (pImage && (pImage->GetCodeSize() != 5 || (memcmp(pImage->GetCode(), pEmptyImage, pImage->GetCodeSize()) != 0 )))
|
|
bRes = true;
|
|
|
|
return bRes;
|
|
}
|
|
|
|
// Store only image, no source
|
|
void SbModule::StoreBinaryData( SvStream& rStrm )
|
|
{
|
|
if (!Compile())
|
|
return;
|
|
|
|
const auto [bSuccess, nVersion] = SbxObject::StoreData(rStrm);
|
|
if (!bSuccess)
|
|
return;
|
|
|
|
pImage->aOUSource.clear();
|
|
pImage->aComment = aComment;
|
|
pImage->aName = GetName();
|
|
|
|
rStrm.WriteUChar(1);
|
|
pImage->Save(rStrm, nVersion);
|
|
|
|
pImage->aOUSource = aOUSource;
|
|
}
|
|
|
|
// Called for >= OO 1.0 passwd protected libraries only
|
|
|
|
void SbModule::LoadBinaryData( SvStream& rStrm )
|
|
{
|
|
OUString aKeepSource = aOUSource;
|
|
LoadData( rStrm, 2 );
|
|
LoadCompleted();
|
|
aOUSource = aKeepSource;
|
|
}
|
|
|
|
bool SbModule::LoadCompleted()
|
|
{
|
|
SbxArray* p = GetMethods().get();
|
|
sal_uInt32 i;
|
|
for (i = 0; i < p->Count(); i++)
|
|
{
|
|
SbMethod* q = dynamic_cast<SbMethod*>(p->Get(i));
|
|
if( q )
|
|
q->pMod = this;
|
|
}
|
|
p = GetProperties();
|
|
for (i = 0; i < p->Count(); i++)
|
|
{
|
|
SbProperty* q = dynamic_cast<SbProperty*>(p->Get(i));
|
|
if( q )
|
|
q->pMod = this;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void SbModule::handleProcedureProperties( SfxBroadcaster& rBC, const SfxHint& rHint )
|
|
{
|
|
bool bDone = false;
|
|
|
|
const SbxHint* pHint = dynamic_cast<const SbxHint*>(&rHint);
|
|
if( pHint )
|
|
{
|
|
SbxVariable* pVar = pHint->GetVar();
|
|
SbProcedureProperty* pProcProperty = dynamic_cast<SbProcedureProperty*>( pVar );
|
|
if( pProcProperty )
|
|
{
|
|
bDone = true;
|
|
|
|
if( pHint->GetId() == SfxHintId::BasicDataWanted )
|
|
{
|
|
OUString aProcName = "Property Get "
|
|
+ pProcProperty->GetName();
|
|
|
|
SbxVariable* pMeth = Find( aProcName, SbxClassType::Method );
|
|
if( pMeth )
|
|
{
|
|
SbxValues aVals;
|
|
aVals.eType = SbxVARIANT;
|
|
|
|
SbxArray* pArg = pVar->GetParameters();
|
|
sal_uInt32 nVarParCount = (pArg != nullptr) ? pArg->Count() : 0;
|
|
if( nVarParCount > 1 )
|
|
{
|
|
SbxArrayRef xMethParameters = new SbxArray;
|
|
xMethParameters->Put(pMeth, 0); // Method as parameter 0
|
|
for( sal_uInt32 i = 1 ; i < nVarParCount ; ++i )
|
|
{
|
|
SbxVariable* pPar = pArg->Get(i);
|
|
xMethParameters->Put(pPar, i);
|
|
}
|
|
|
|
pMeth->SetParameters( xMethParameters.get() );
|
|
pMeth->Get( aVals );
|
|
pMeth->SetParameters( nullptr );
|
|
}
|
|
else
|
|
{
|
|
pMeth->Get( aVals );
|
|
}
|
|
|
|
pVar->Put( aVals );
|
|
}
|
|
}
|
|
else if( pHint->GetId() == SfxHintId::BasicDataChanged )
|
|
{
|
|
SbxVariable* pMeth = nullptr;
|
|
|
|
bool bSet = pProcProperty->isSet();
|
|
if( bSet )
|
|
{
|
|
pProcProperty->setSet( false );
|
|
|
|
OUString aProcName = "Property Set "
|
|
+ pProcProperty->GetName();
|
|
pMeth = Find( aProcName, SbxClassType::Method );
|
|
}
|
|
if( !pMeth ) // Let
|
|
{
|
|
OUString aProcName = "Property Let "
|
|
+ pProcProperty->GetName();
|
|
pMeth = Find( aProcName, SbxClassType::Method );
|
|
}
|
|
|
|
if( pMeth )
|
|
{
|
|
// Setup parameters
|
|
SbxArrayRef xArray = new SbxArray;
|
|
xArray->Put(pMeth, 0); // Method as parameter 0
|
|
xArray->Put(pVar, 1);
|
|
pMeth->SetParameters( xArray.get() );
|
|
|
|
SbxValues aVals;
|
|
pMeth->Get( aVals );
|
|
pMeth->SetParameters( nullptr );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !bDone )
|
|
SbModule::Notify( rBC, rHint );
|
|
}
|
|
|
|
|
|
// Implementation SbJScriptModule (Basic module for JavaScript source code)
|
|
SbJScriptModule::SbJScriptModule()
|
|
:SbModule( u""_ustr )
|
|
{
|
|
}
|
|
|
|
bool SbJScriptModule::LoadData( SvStream& rStrm, sal_uInt16 )
|
|
{
|
|
Clear();
|
|
if( !SbxObject::LoadData( rStrm, 1 ) )
|
|
return false;
|
|
|
|
// Get the source string
|
|
aOUSource = rStrm.ReadUniOrByteString( osl_getThreadTextEncoding() );
|
|
return true;
|
|
}
|
|
|
|
std::pair<bool, sal_uInt32> SbJScriptModule::StoreData( SvStream& rStrm ) const
|
|
{
|
|
const auto [bSuccess, nVersion] = SbxObject::StoreData(rStrm);
|
|
if( !bSuccess )
|
|
return { false, 0 };
|
|
|
|
// Write the source string
|
|
OUString aTmp = aOUSource;
|
|
rStrm.WriteUniOrByteString( aTmp, osl_getThreadTextEncoding() );
|
|
return { true, nVersion };
|
|
}
|
|
|
|
|
|
SbMethod::SbMethod( const OUString& r, SbxDataType t, SbModule* p )
|
|
: SbxMethod( r, t ), pMod( p )
|
|
{
|
|
bInvalid = true;
|
|
nStart = 0;
|
|
nDebugFlags = BasicDebugFlags::NONE;
|
|
nLine1 = 0;
|
|
nLine2 = 0;
|
|
refStatics = new SbxArray;
|
|
mCaller = nullptr;
|
|
// HACK due to 'Reference could not be saved'
|
|
SetFlag( SbxFlagBits::NoModify );
|
|
}
|
|
|
|
SbMethod::SbMethod( const SbMethod& r )
|
|
: SvRefBase( r ), SbxMethod( r )
|
|
{
|
|
pMod = r.pMod;
|
|
bInvalid = r.bInvalid;
|
|
nStart = r.nStart;
|
|
nDebugFlags = r.nDebugFlags;
|
|
nLine1 = r.nLine1;
|
|
nLine2 = r.nLine2;
|
|
refStatics = r.refStatics;
|
|
mCaller = r.mCaller;
|
|
SetFlag( SbxFlagBits::NoModify );
|
|
}
|
|
|
|
SbMethod::~SbMethod()
|
|
{
|
|
}
|
|
|
|
void SbMethod::ClearStatics()
|
|
{
|
|
refStatics = new SbxArray;
|
|
|
|
}
|
|
SbxArray* SbMethod::GetStatics()
|
|
{
|
|
return refStatics.get();
|
|
}
|
|
|
|
bool SbMethod::LoadData( SvStream& rStrm, sal_uInt16 nVer )
|
|
{
|
|
if( !SbxMethod::LoadData( rStrm, 1 ) )
|
|
return false;
|
|
|
|
sal_uInt16 nFlag;
|
|
rStrm.ReadUInt16( nFlag );
|
|
|
|
sal_Int16 nTempStart = static_cast<sal_Int16>(nStart);
|
|
|
|
if( nVer == 2 )
|
|
{
|
|
rStrm.ReadUInt16( nLine1 ).ReadUInt16( nLine2 ).ReadInt16( nTempStart ).ReadCharAsBool( bInvalid );
|
|
//tdf#94617
|
|
if (nFlag & 0x8000)
|
|
{
|
|
sal_uInt16 nMult = nFlag & 0x7FFF;
|
|
sal_Int16 const nMax = std::numeric_limits<sal_Int16>::max();
|
|
nStart = nMult * nMax + nTempStart;
|
|
}
|
|
else
|
|
{
|
|
nStart = nTempStart;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
nStart = nTempStart;
|
|
}
|
|
|
|
// HACK due to 'Reference could not be saved'
|
|
SetFlag( SbxFlagBits::NoModify );
|
|
|
|
return true;
|
|
}
|
|
|
|
std::pair<bool, sal_uInt32> SbMethod::StoreData( SvStream& rStrm ) const
|
|
{
|
|
auto [bSuccess, nVersion] = SbxMethod::StoreData(rStrm);
|
|
if( !bSuccess )
|
|
return { false, 0 };
|
|
|
|
//tdf#94617
|
|
const sal_uInt32 nMax = std::numeric_limits<sal_Int16>::max();
|
|
// tdf#142391 - store method using binary format 0x13 only when actually needed, i.e.,
|
|
// when method starts at an offset that would overflow 16 bits
|
|
const sal_Int16 nStartTemp = nStart % nMax;
|
|
sal_uInt16 nDebugFlagsTemp = static_cast<sal_uInt16>(nDebugFlags);
|
|
if (nStart >= nMax)
|
|
{
|
|
assert(nStart <= nMax * 0x7FFF); // Larger addresses can't be stored in version 13
|
|
nDebugFlagsTemp = (nStart / nMax) | 0x8000;
|
|
nVersion = B_IMG_VERSION_13;
|
|
}
|
|
|
|
rStrm.WriteUInt16( nDebugFlagsTemp )
|
|
.WriteInt16( nLine1 )
|
|
.WriteInt16( nLine2 )
|
|
.WriteInt16( nStartTemp )
|
|
.WriteBool( bInvalid );
|
|
|
|
return { true, nVersion };
|
|
}
|
|
|
|
void SbMethod::GetLineRange( sal_uInt16& l1, sal_uInt16& l2 )
|
|
{
|
|
l1 = nLine1; l2 = nLine2;
|
|
}
|
|
|
|
// Could later be deleted
|
|
|
|
SbxInfo* SbMethod::GetInfo()
|
|
{
|
|
return pInfo.get();
|
|
}
|
|
|
|
// Interface to execute a method of the applications
|
|
// With special RefCounting, so that the Basic was not fired of by CloseDocument()
|
|
// The return value will be delivered as string.
|
|
ErrCode SbMethod::Call( SbxValue* pRet, SbxVariable* pCaller )
|
|
{
|
|
if ( pCaller )
|
|
{
|
|
SAL_INFO("basic", "SbMethod::Call Have been passed a caller 0x" << pCaller );
|
|
mCaller = pCaller;
|
|
}
|
|
// Increment the RefCount of the module
|
|
tools::SvRef<SbModule> pMod_ = static_cast<SbModule*>(GetParent());
|
|
|
|
tools::SvRef<StarBASIC> xHolder = static_cast<StarBASIC*>(pMod_->GetParent());
|
|
|
|
// Establish the values to get the return value
|
|
SbxValues aVals;
|
|
aVals.eType = SbxVARIANT;
|
|
|
|
// #104083: Compile BEFORE get
|
|
if( bInvalid && !pMod_->Compile() )
|
|
StarBASIC::Error( ERRCODE_BASIC_BAD_PROP_VALUE );
|
|
|
|
// tdf#143582 - clear return value of the method before calling it
|
|
Clear();
|
|
|
|
Get( aVals );
|
|
if ( pRet )
|
|
pRet->Put( aVals );
|
|
|
|
// Was there an error
|
|
ErrCode nErr = SbxBase::GetError();
|
|
SbxBase::ResetError();
|
|
|
|
mCaller = nullptr;
|
|
return nErr;
|
|
}
|
|
|
|
|
|
// #100883 Own Broadcast for SbMethod
|
|
void SbMethod::Broadcast( SfxHintId nHintId )
|
|
{
|
|
if( !mpBroadcaster || IsSet( SbxFlagBits::NoBroadcast ) )
|
|
return;
|
|
|
|
// Because the method could be called from outside, test here once again
|
|
// the authorisation
|
|
if( nHintId == SfxHintId::BasicDataWanted )
|
|
if( !CanRead() )
|
|
return;
|
|
if( nHintId == SfxHintId::BasicDataChanged )
|
|
if( !CanWrite() )
|
|
return;
|
|
|
|
if( pMod && !pMod->IsCompiled() )
|
|
pMod->Compile();
|
|
|
|
// Block broadcasts while creating new method
|
|
std::unique_ptr<SfxBroadcaster> pSaveBroadcaster = std::move(mpBroadcaster);
|
|
SbMethodRef xThisCopy = new SbMethod( *this );
|
|
if( mpPar.is() )
|
|
{
|
|
// Enregister this as element 0, but don't reset the parent!
|
|
if( GetType() != SbxVOID ) {
|
|
mpPar->PutDirect( xThisCopy.get(), 0 );
|
|
}
|
|
SetParameters( nullptr );
|
|
}
|
|
|
|
mpBroadcaster = std::move(pSaveBroadcaster);
|
|
mpBroadcaster->Broadcast( SbxHint( nHintId, xThisCopy.get() ) );
|
|
|
|
SbxFlagBits nSaveFlags = GetFlags();
|
|
SetFlag( SbxFlagBits::ReadWrite );
|
|
pSaveBroadcaster = std::move(mpBroadcaster);
|
|
Put( xThisCopy->GetValues_Impl() );
|
|
mpBroadcaster = std::move(pSaveBroadcaster);
|
|
SetFlags( nSaveFlags );
|
|
}
|
|
|
|
|
|
// Implementation of SbJScriptMethod (method class as a wrapper for JavaScript-functions)
|
|
|
|
SbJScriptMethod::SbJScriptMethod( SbxDataType t )
|
|
: SbMethod( u""_ustr, t, nullptr )
|
|
{
|
|
}
|
|
|
|
SbJScriptMethod::~SbJScriptMethod()
|
|
{}
|
|
|
|
|
|
SbObjModule::SbObjModule( const OUString& rName, const css::script::ModuleInfo& mInfo, bool bIsVbaCompatible )
|
|
: SbModule( rName, bIsVbaCompatible )
|
|
{
|
|
SetModuleType( mInfo.ModuleType );
|
|
if ( mInfo.ModuleType == script::ModuleType::FORM )
|
|
{
|
|
SetClassName( u"Form"_ustr );
|
|
}
|
|
else if ( mInfo.ModuleObject.is() )
|
|
{
|
|
SetUnoObject( uno::Any( mInfo.ModuleObject ) );
|
|
}
|
|
}
|
|
|
|
SbObjModule::~SbObjModule()
|
|
{
|
|
}
|
|
|
|
void
|
|
SbObjModule::SetUnoObject( const uno::Any& aObj )
|
|
{
|
|
SbUnoObject* pUnoObj = pDocObject.get();
|
|
if ( pUnoObj && pUnoObj->getUnoAny() == aObj ) // object is equal, nothing to do
|
|
return;
|
|
pDocObject = new SbUnoObject( GetName(), aObj );
|
|
|
|
css::uno::Reference< css::lang::XServiceInfo > xServiceInfo( aObj, css::uno::UNO_QUERY_THROW );
|
|
if( xServiceInfo->supportsService( u"ooo.vba.excel.Worksheet"_ustr ) )
|
|
{
|
|
SetClassName( u"Worksheet"_ustr );
|
|
}
|
|
else if( xServiceInfo->supportsService( u"ooo.vba.excel.Workbook"_ustr ) )
|
|
{
|
|
SetClassName( u"Workbook"_ustr );
|
|
}
|
|
}
|
|
|
|
SbxVariable*
|
|
SbObjModule::GetObject()
|
|
{
|
|
return pDocObject.get();
|
|
}
|
|
SbxVariable*
|
|
SbObjModule::Find( const OUString& rName, SbxClassType t )
|
|
{
|
|
SbxVariable* pVar = nullptr;
|
|
if ( pDocObject )
|
|
pVar = pDocObject->Find( rName, t );
|
|
if ( !pVar )
|
|
pVar = SbModule::Find( rName, t );
|
|
return pVar;
|
|
}
|
|
|
|
void SbObjModule::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
|
|
{
|
|
SbModule::handleProcedureProperties( rBC, rHint );
|
|
}
|
|
|
|
|
|
typedef ::cppu::WeakImplHelper<
|
|
awt::XTopWindowListener,
|
|
awt::XWindowListener,
|
|
document::XDocumentEventListener > FormObjEventListener_BASE;
|
|
|
|
class FormObjEventListenerImpl:
|
|
public FormObjEventListener_BASE
|
|
{
|
|
SbUserFormModule* mpUserForm;
|
|
uno::Reference< lang::XComponent > mxComponent;
|
|
uno::Reference< frame::XModel > mxModel;
|
|
bool mbDisposed;
|
|
bool mbOpened;
|
|
bool mbActivated;
|
|
bool mbShowing;
|
|
|
|
public:
|
|
FormObjEventListenerImpl(const FormObjEventListenerImpl&) = delete;
|
|
const FormObjEventListenerImpl& operator=(const FormObjEventListenerImpl&) = delete;
|
|
FormObjEventListenerImpl( SbUserFormModule* pUserForm, uno::Reference< lang::XComponent > xComponent, uno::Reference< frame::XModel > xModel ) :
|
|
mpUserForm( pUserForm ), mxComponent(std::move( xComponent)), mxModel(std::move( xModel )),
|
|
mbDisposed( false ), mbOpened( false ), mbActivated( false ), mbShowing( false )
|
|
{
|
|
if ( mxComponent.is() )
|
|
{
|
|
try
|
|
{
|
|
uno::Reference< awt::XTopWindow >( mxComponent, uno::UNO_QUERY_THROW )->addTopWindowListener( this );
|
|
}
|
|
catch(const uno::Exception& ) {}
|
|
try
|
|
{
|
|
uno::Reference< awt::XWindow >( mxComponent, uno::UNO_QUERY_THROW )->addWindowListener( this );
|
|
}
|
|
catch(const uno::Exception& ) {}
|
|
}
|
|
|
|
if ( mxModel.is() )
|
|
{
|
|
try
|
|
{
|
|
uno::Reference< document::XDocumentEventBroadcaster >( mxModel, uno::UNO_QUERY_THROW )->addDocumentEventListener( this );
|
|
}
|
|
catch(const uno::Exception& ) {}
|
|
}
|
|
}
|
|
|
|
virtual ~FormObjEventListenerImpl() override
|
|
{
|
|
removeListener();
|
|
}
|
|
|
|
bool isShowing() const { return mbShowing; }
|
|
|
|
void removeListener()
|
|
{
|
|
if ( mxComponent.is() && !mbDisposed )
|
|
{
|
|
try
|
|
{
|
|
uno::Reference< awt::XTopWindow >( mxComponent, uno::UNO_QUERY_THROW )->removeTopWindowListener( this );
|
|
}
|
|
catch(const uno::Exception& ) {}
|
|
try
|
|
{
|
|
uno::Reference< awt::XWindow >( mxComponent, uno::UNO_QUERY_THROW )->removeWindowListener( this );
|
|
}
|
|
catch(const uno::Exception& ) {}
|
|
}
|
|
mxComponent.clear();
|
|
|
|
if ( mxModel.is() && !mbDisposed )
|
|
{
|
|
try
|
|
{
|
|
uno::Reference< document::XDocumentEventBroadcaster >( mxModel, uno::UNO_QUERY_THROW )->removeDocumentEventListener( this );
|
|
}
|
|
catch(const uno::Exception& ) {}
|
|
}
|
|
mxModel.clear();
|
|
}
|
|
|
|
virtual void SAL_CALL windowOpened( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
if ( mpUserForm )
|
|
{
|
|
mbOpened = true;
|
|
mbShowing = true;
|
|
if ( mbActivated )
|
|
{
|
|
mbOpened = mbActivated = false;
|
|
mpUserForm->triggerActivateEvent();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
virtual void SAL_CALL windowClosing( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
#ifdef IN_THE_FUTURE
|
|
uno::Reference< awt::XDialog > xDialog( e.Source, uno::UNO_QUERY );
|
|
if ( xDialog.is() )
|
|
{
|
|
uno::Reference< awt::XControl > xControl( xDialog, uno::UNO_QUERY );
|
|
if ( xControl->getPeer().is() )
|
|
{
|
|
uno::Reference< document::XVbaMethodParameter > xVbaMethodParameter( xControl->getPeer(), uno::UNO_QUERY );
|
|
if ( xVbaMethodParameter.is() )
|
|
{
|
|
sal_Int8 nCancel = 0;
|
|
sal_Int8 nCloseMode = ::ooo::vba::VbQueryClose::vbFormControlMenu;
|
|
|
|
Sequence< Any > aParams;
|
|
aParams.realloc(2);
|
|
aParams[0] <<= nCancel;
|
|
aParams[1] <<= nCloseMode;
|
|
|
|
mpUserForm->triggerMethod( "Userform_QueryClose", aParams);
|
|
return;
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
mpUserForm->triggerMethod( "Userform_QueryClose" );
|
|
#endif
|
|
}
|
|
|
|
|
|
virtual void SAL_CALL windowClosed( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
mbOpened = false;
|
|
mbShowing = false;
|
|
}
|
|
|
|
virtual void SAL_CALL windowMinimized( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
}
|
|
|
|
virtual void SAL_CALL windowNormalized( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
}
|
|
|
|
virtual void SAL_CALL windowActivated( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
if ( mpUserForm )
|
|
{
|
|
mbActivated = true;
|
|
if ( mbOpened )
|
|
{
|
|
mbOpened = mbActivated = false;
|
|
mpUserForm->triggerActivateEvent();
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void SAL_CALL windowDeactivated( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
if ( mpUserForm )
|
|
mpUserForm->triggerDeactivateEvent();
|
|
}
|
|
|
|
virtual void SAL_CALL windowResized( const awt::WindowEvent& /*e*/ ) override
|
|
{
|
|
if ( mpUserForm )
|
|
{
|
|
mpUserForm->triggerResizeEvent();
|
|
mpUserForm->triggerLayoutEvent();
|
|
}
|
|
}
|
|
|
|
virtual void SAL_CALL windowMoved( const awt::WindowEvent& /*e*/ ) override
|
|
{
|
|
if ( mpUserForm )
|
|
mpUserForm->triggerLayoutEvent();
|
|
}
|
|
|
|
virtual void SAL_CALL windowShown( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
}
|
|
|
|
virtual void SAL_CALL windowHidden( const lang::EventObject& /*e*/ ) override
|
|
{
|
|
}
|
|
|
|
virtual void SAL_CALL documentEventOccured( const document::DocumentEvent& rEvent ) override
|
|
{
|
|
// early disposing on document event "OnUnload", to be sure Basic still exists when calling VBA "UserForm_Terminate"
|
|
if( rEvent.EventName == GlobalEventConfig::GetEventName( GlobalEventId::CLOSEDOC ) )
|
|
{
|
|
SolarMutexGuard g;
|
|
removeListener();
|
|
mbDisposed = true;
|
|
if ( mpUserForm )
|
|
mpUserForm->ResetApiObj(); // will trigger "UserForm_Terminate"
|
|
}
|
|
}
|
|
|
|
virtual void SAL_CALL disposing( const lang::EventObject& /*Source*/ ) override
|
|
{
|
|
removeListener();
|
|
mbDisposed = true;
|
|
if ( mpUserForm )
|
|
mpUserForm->ResetApiObj( false ); // pass false (too late to trigger VBA events here)
|
|
}
|
|
};
|
|
|
|
SbUserFormModule::SbUserFormModule( const OUString& rName, const css::script::ModuleInfo& mInfo, bool bIsCompat )
|
|
: SbObjModule( rName, mInfo, bIsCompat )
|
|
, m_mInfo( mInfo )
|
|
, mbInit( false )
|
|
{
|
|
m_xModel.set( mInfo.ModuleObject, uno::UNO_QUERY_THROW );
|
|
}
|
|
|
|
SbUserFormModule::~SbUserFormModule()
|
|
{
|
|
}
|
|
|
|
void SbUserFormModule::ResetApiObj( bool bTriggerTerminateEvent )
|
|
{
|
|
SAL_INFO("basic", " SbUserFormModule::ResetApiObj( " << (bTriggerTerminateEvent ? "true )" : "false )") );
|
|
if ( bTriggerTerminateEvent && m_xDialog.is() ) // probably someone close the dialog window
|
|
{
|
|
triggerTerminateEvent();
|
|
}
|
|
pDocObject = nullptr;
|
|
m_xDialog = nullptr;
|
|
}
|
|
|
|
void SbUserFormModule::triggerMethod( const OUString& aMethodToRun )
|
|
{
|
|
Sequence< Any > aArguments;
|
|
triggerMethod( aMethodToRun, aArguments );
|
|
}
|
|
|
|
void SbUserFormModule::triggerMethod( const OUString& aMethodToRun, Sequence< Any >& aArguments )
|
|
{
|
|
SAL_INFO("basic", "trigger " << aMethodToRun);
|
|
// Search method
|
|
SbxVariable* pMeth = SbObjModule::Find( aMethodToRun, SbxClassType::Method );
|
|
if( !pMeth )
|
|
return;
|
|
|
|
if ( aArguments.hasElements() ) // Setup parameters
|
|
{
|
|
auto xArray = tools::make_ref<SbxArray>();
|
|
xArray->Put(pMeth, 0); // Method as parameter 0
|
|
|
|
for ( sal_Int32 i = 0; i < aArguments.getLength(); ++i )
|
|
{
|
|
auto xSbxVar = tools::make_ref<SbxVariable>( SbxVARIANT );
|
|
unoToSbxValue( xSbxVar.get(), aArguments[i] );
|
|
xArray->Put(xSbxVar.get(), static_cast<sal_uInt32>(i) + 1);
|
|
|
|
// Enable passing by ref
|
|
if ( xSbxVar->GetType() != SbxVARIANT )
|
|
xSbxVar->SetFlag( SbxFlagBits::Fixed );
|
|
}
|
|
pMeth->SetParameters( xArray.get() );
|
|
|
|
SbxValues aVals;
|
|
pMeth->Get( aVals );
|
|
|
|
auto pArguments = aArguments.getArray();
|
|
for ( sal_Int32 i = 0; i < aArguments.getLength(); ++i )
|
|
{
|
|
pArguments[i] = sbxToUnoValue(xArray->Get(static_cast<sal_uInt32>(i) + 1));
|
|
}
|
|
pMeth->SetParameters( nullptr );
|
|
}
|
|
else
|
|
{
|
|
SbxValues aVals;
|
|
pMeth->Get( aVals );
|
|
}
|
|
}
|
|
|
|
void SbUserFormModule::triggerActivateEvent()
|
|
{
|
|
triggerMethod( u"UserForm_Activate"_ustr );
|
|
}
|
|
|
|
void SbUserFormModule::triggerDeactivateEvent()
|
|
{
|
|
triggerMethod( u"Userform_Deactivate"_ustr );
|
|
}
|
|
|
|
void SbUserFormModule::triggerInitializeEvent()
|
|
{
|
|
if ( mbInit )
|
|
return;
|
|
triggerMethod(u"Userform_Initialize"_ustr);
|
|
mbInit = true;
|
|
}
|
|
|
|
void SbUserFormModule::triggerTerminateEvent()
|
|
{
|
|
triggerMethod(u"Userform_Terminate"_ustr);
|
|
mbInit=false;
|
|
}
|
|
|
|
void SbUserFormModule::triggerLayoutEvent()
|
|
{
|
|
triggerMethod(u"Userform_Layout"_ustr);
|
|
}
|
|
|
|
void SbUserFormModule::triggerResizeEvent()
|
|
{
|
|
triggerMethod(u"Userform_Resize"_ustr);
|
|
}
|
|
|
|
SbUserFormModuleInstance* SbUserFormModule::CreateInstance()
|
|
{
|
|
SbUserFormModuleInstance* pInstance = new SbUserFormModuleInstance( this, GetName(), m_mInfo, IsVBASupport() );
|
|
return pInstance;
|
|
}
|
|
|
|
SbUserFormModuleInstance::SbUserFormModuleInstance( SbUserFormModule* pParentModule,
|
|
const OUString& rName, const css::script::ModuleInfo& mInfo, bool bIsVBACompat )
|
|
: SbUserFormModule( rName, mInfo, bIsVBACompat )
|
|
, m_pParentModule( pParentModule )
|
|
{
|
|
}
|
|
|
|
bool SbUserFormModuleInstance::IsClass( const OUString& rName ) const
|
|
{
|
|
bool bParentNameMatches = m_pParentModule->GetName().equalsIgnoreAsciiCase( rName );
|
|
bool bRet = bParentNameMatches || SbxObject::IsClass( rName );
|
|
return bRet;
|
|
}
|
|
|
|
SbxVariable* SbUserFormModuleInstance::Find( const OUString& rName, SbxClassType t )
|
|
{
|
|
SbxVariable* pVar = m_pParentModule->Find( rName, t );
|
|
return pVar;
|
|
}
|
|
|
|
|
|
void SbUserFormModule::Load()
|
|
{
|
|
// forces a load
|
|
if ( !pDocObject.is() )
|
|
InitObject();
|
|
}
|
|
|
|
|
|
void SbUserFormModule::Unload()
|
|
{
|
|
sal_Int8 nCancel = 0;
|
|
|
|
Sequence< Any > aParams = { Any(nCancel), Any(sal_Int8(::ooo::vba::VbQueryClose::vbFormCode)) };
|
|
|
|
triggerMethod( u"Userform_QueryClose"_ustr, aParams);
|
|
|
|
aParams[0] >>= nCancel;
|
|
// basic boolean ( and what the user might use ) can be ambiguous ( e.g. basic true = -1 )
|
|
// test against 0 ( false ) and assume anything else is true
|
|
// ( Note: ) this used to work ( something changes somewhere )
|
|
if (nCancel != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( m_xDialog.is() )
|
|
{
|
|
triggerTerminateEvent();
|
|
}
|
|
// Search method
|
|
SbxVariable* pMeth = SbObjModule::Find( u"UnloadObject"_ustr, SbxClassType::Method );
|
|
if( !pMeth )
|
|
return;
|
|
|
|
SAL_INFO("basic", "Attempting to run the UnloadObjectMethod");
|
|
m_xDialog.clear(); //release ref to the uno object
|
|
SbxValues aVals;
|
|
bool bWaitForDispose = true; // assume dialog is showing
|
|
if (m_DialogListener)
|
|
{
|
|
bWaitForDispose = m_DialogListener->isShowing();
|
|
SAL_INFO("basic", "Showing " << bWaitForDispose );
|
|
}
|
|
pMeth->Get( aVals);
|
|
if ( !bWaitForDispose )
|
|
{
|
|
// we've either already got a dispose or we are never going to get one
|
|
ResetApiObj();
|
|
} // else wait for dispose
|
|
SAL_INFO("basic", "UnloadObject completed (we hope)");
|
|
}
|
|
|
|
|
|
void SbUserFormModule::InitObject()
|
|
{
|
|
try
|
|
{
|
|
SbUnoObject* pGlobs = static_cast<SbUnoObject*>(GetParent()->Find( u"VBAGlobals"_ustr, SbxClassType::DontCare ));
|
|
if ( m_xModel.is() && pGlobs )
|
|
{
|
|
// broadcast INITIALIZE_USERFORM script event before the dialog is created
|
|
Reference< script::vba::XVBACompatibility > xVBACompat( getVBACompatibility( m_xModel ), uno::UNO_SET_THROW );
|
|
xVBACompat->broadcastVBAScriptEvent( script::vba::VBAScriptEventId::INITIALIZE_USERFORM, GetName() );
|
|
uno::Reference< lang::XMultiServiceFactory > xVBAFactory( pGlobs->getUnoAny(), uno::UNO_QUERY_THROW );
|
|
const uno::Reference< uno::XComponentContext >& xContext = comphelper::getProcessComponentContext();
|
|
OUString sDialogUrl( u"vnd.sun.star.script:"_ustr );
|
|
OUString sProjectName( u"Standard"_ustr );
|
|
|
|
try
|
|
{
|
|
Reference< beans::XPropertySet > xProps( m_xModel, UNO_QUERY_THROW );
|
|
uno::Reference< script::vba::XVBACompatibility > xVBAMode( xProps->getPropertyValue( u"BasicLibraries"_ustr ), uno::UNO_QUERY_THROW );
|
|
sProjectName = xVBAMode->getProjectName();
|
|
}
|
|
catch(const Exception& ) {}
|
|
|
|
sDialogUrl += sProjectName + "." + GetName() + "?location=document";
|
|
|
|
uno::Reference< awt::XDialogProvider > xProvider = awt::DialogProvider::createWithModel( xContext, m_xModel );
|
|
m_xDialog = xProvider->createDialog( sDialogUrl );
|
|
|
|
// create vba api object
|
|
uno::Sequence< uno::Any > aArgs
|
|
{
|
|
uno::Any(),
|
|
Any(m_xDialog),
|
|
Any(m_xModel),
|
|
Any(GetParent()->GetName())
|
|
};
|
|
pDocObject = new SbUnoObject( GetName(), uno::Any( xVBAFactory->createInstanceWithArguments( u"ooo.vba.msforms.UserForm"_ustr, aArgs ) ) );
|
|
|
|
uno::Reference< lang::XComponent > xComponent( m_xDialog, uno::UNO_QUERY_THROW );
|
|
|
|
// the dialog must be disposed at the end!
|
|
StarBASIC* pParentBasic = nullptr;
|
|
SbxObject* pCurObject = this;
|
|
do
|
|
{
|
|
SbxObject* pObjParent = pCurObject->GetParent();
|
|
pParentBasic = dynamic_cast<StarBASIC*>( pObjParent );
|
|
pCurObject = pObjParent;
|
|
}
|
|
while( pParentBasic == nullptr && pCurObject != nullptr );
|
|
|
|
SAL_WARN_IF( pParentBasic == nullptr, "basic", "pParentBasic == NULL" );
|
|
registerComponentToBeDisposedForBasic( xComponent, pParentBasic );
|
|
|
|
// if old listener object exists, remove it from dialog and document model
|
|
if( m_DialogListener.is() )
|
|
m_DialogListener->removeListener();
|
|
m_DialogListener.set( new FormObjEventListenerImpl( this, xComponent, m_xModel ) );
|
|
|
|
triggerInitializeEvent();
|
|
}
|
|
}
|
|
catch(const uno::Exception& )
|
|
{
|
|
}
|
|
|
|
}
|
|
|
|
SbxVariable*
|
|
SbUserFormModule::Find( const OUString& rName, SbxClassType t )
|
|
{
|
|
if ( !pDocObject.is() && !GetSbData()->bRunInit && GetSbData()->pInst )
|
|
InitObject();
|
|
return SbObjModule::Find( rName, t );
|
|
}
|
|
|
|
SbProperty::SbProperty( const OUString& r, SbxDataType t, SbModule* p )
|
|
: SbxProperty( r, t ), pMod( p )
|
|
{
|
|
}
|
|
|
|
SbProperty::~SbProperty()
|
|
{}
|
|
|
|
|
|
SbProcedureProperty::~SbProcedureProperty()
|
|
{}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|