/* -*- 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 <config_features.h>
#include <sal/config.h>
#include <config_version.h>
#include <cstddef>
#include <rtl/math.hxx>
#include <vcl/svapp.hxx>
#include <vcl/mapmod.hxx>
#include <vcl/outdev.hxx>
#include <vcl/timer.hxx>
#include <vcl/settings.hxx>
#include <basic/sbxvar.hxx>
#include <basic/sbx.hxx>
#include <svl/zforlist.hxx>
#include <tools/urlobj.hxx>
#include <tools/fract.hxx>
#include <o3tl/temporary.hxx>
#include <osl/file.hxx>
#include <sbobjmod.hxx>
#include <basic/sbuno.hxx>
#include <date.hxx>
#include <sbintern.hxx>
#include <runtime.hxx>
#include <rtlproto.hxx>
#include "dllmgr.hxx"
#include <iosys.hxx>
#include <sbunoobj.hxx>
#include <propacc.hxx>
#include <sal/log.hxx>
#include <eventatt.hxx>
#include <rtl/math.h>
#include <svl/numformat.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/string.hxx>
#include <com/sun/star/uno/Sequence.hxx>
#include <com/sun/star/lang/XMultiServiceFactory.hpp>
#include <com/sun/star/i18n/LocaleCalendar2.hpp>
#include <com/sun/star/sheet/XFunctionAccess.hpp>
#include <memory>
using namespace comphelper;
using namespace com::sun::star::i18n;
using namespace com::sun::star::lang;
using namespace com::sun::star::sheet;
using namespace com::sun::star::uno;
static Reference< XCalendar4 > const & getLocaleCalendar()
{
static Reference< XCalendar4 > xCalendar = LocaleCalendar2::create(getProcessComponentContext());
static css::lang::Locale aLastLocale;
static bool bNeedsReload = true;
css::lang::Locale aLocale = Application::GetSettings().GetLanguageTag().getLocale();
bNeedsReload = bNeedsReload ||
( aLocale.Language != aLastLocale.Language ||
aLocale.Country != aLastLocale.Country ||
aLocale.Variant != aLastLocale.Variant );
if( bNeedsReload )
{
bNeedsReload = false;
aLastLocale = aLocale;
xCalendar->loadDefaultCalendar( aLocale );
}
return xCalendar;
}
#if HAVE_FEATURE_SCRIPTING
void SbRtl_CallByName(StarBASIC *, SbxArray & rPar, bool)
{
const sal_Int16 vbGet = 2;
const sal_Int16 vbLet = 4;
const sal_Int16 vbMethod = 1;
const sal_Int16 vbSet = 8;
// At least 3 parameter needed plus function itself -> 4
sal_uInt32 nParCount = rPar.Count();
if ( nParCount < 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// 1. parameter is object
SbxBase* pObjVar = rPar.Get(1)->GetObject();
SbxObject* pObj = nullptr;
if( pObjVar )
pObj = dynamic_cast<SbxObject*>( pObjVar );
if( !pObj )
if (auto pSbxVar = dynamic_cast<const SbxVariable*>( pObjVar))
pObj = dynamic_cast<SbxObject*>( pSbxVar->GetObject() );
if( !pObj )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_PARAMETER );
return;
}
// 2. parameter is ProcName
OUString aNameStr = rPar.Get(2)->GetOUString();
// 3. parameter is CallType
sal_Int16 nCallType = rPar.Get(3)->GetInteger();
//SbxObject* pFindObj = NULL;
SbxVariable* pFindVar = pObj->Find( aNameStr, SbxClassType::DontCare );
if( pFindVar == nullptr )
{
StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED );
return;
}
switch( nCallType )
{
case vbGet:
{
SbxValues aVals;
aVals.eType = SbxVARIANT;
pFindVar->Get( aVals );
SbxVariableRef refVar = rPar.Get(0);
refVar->Put( aVals );
}
break;
case vbLet:
case vbSet:
{
if ( nParCount != 5 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxVariableRef pValVar = rPar.Get(4);
if( nCallType == vbLet )
{
SbxValues aVals;
aVals.eType = SbxVARIANT;
pValVar->Get( aVals );
pFindVar->Put( aVals );
}
else
{
SbxVariableRef rFindVar = pFindVar;
SbiInstance* pInst = GetSbData()->pInst;
SbiRuntime* pRT = pInst ? pInst->pRun : nullptr;
if( pRT != nullptr )
{
pRT->StepSET_Impl( pValVar, rFindVar );
}
}
}
break;
case vbMethod:
{
SbMethod* pMeth = dynamic_cast<SbMethod*>( pFindVar );
if( pMeth == nullptr )
{
StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED );
return;
}
// Setup parameters
SbxArrayRef xArray;
sal_uInt32 nMethParamCount = nParCount - 4;
if( nMethParamCount > 0 )
{
xArray = new SbxArray;
for( sal_uInt32 i = 0 ; i < nMethParamCount ; i++ )
{
SbxVariable* pPar = rPar.Get(i + 4);
xArray->Put(pPar, i + 1);
}
}
// Call method
SbxVariableRef refVar = rPar.Get(0);
if( xArray.is() )
pMeth->SetParameters( xArray.get() );
pMeth->Call( refVar.get() );
pMeth->SetParameters( nullptr );
}
break;
default:
StarBASIC::Error( ERRCODE_BASIC_PROC_UNDEFINED );
}
}
void SbRtl_CBool(StarBASIC *, SbxArray & rPar, bool) // JSM
{
bool bVal = false;
if (rPar.Count() == 2)
{
SbxVariable* pSbxVariable = rPar.Get(1);
bVal = pSbxVariable->GetBool();
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
rPar.Get(0)->PutBool(bVal);
}
void SbRtl_CByte(StarBASIC *, SbxArray & rPar, bool) // JSM
{
sal_uInt8 nByte = 0;
if (rPar.Count() == 2)
{
SbxVariable* pSbxVariable = rPar.Get(1);
nByte = pSbxVariable->GetByte();
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
rPar.Get(0)->PutByte(nByte);
}
void SbRtl_CCur(StarBASIC *, SbxArray & rPar, bool)
{
sal_Int64 nCur = 0;
if (rPar.Count() == 2)
{
SbxVariable* pSbxVariable = rPar.Get(1);
nCur = pSbxVariable->GetCurrency();
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
rPar.Get(0)->PutCurrency(nCur);
}
void SbRtl_CDec(StarBASIC *, SbxArray & rPar, bool)
{
#ifdef _WIN32
SbxDecimal* pDec = nullptr;
if (rPar.Count() == 2)
{
SbxVariable* pSbxVariable = rPar.Get(1);
pDec = pSbxVariable->GetDecimal();
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
rPar.Get(0)->PutDecimal(pDec);
#else
rPar.Get(0)->PutEmpty();
StarBASIC::Error(ERRCODE_BASIC_NOT_IMPLEMENTED);
#endif
}
void SbRtl_CDate(StarBASIC *, SbxArray & rPar, bool) // JSM
{
double nVal = 0.0;
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbxVariable* pSbxVariable = rPar.Get(1);
nVal = pSbxVariable->GetDate();
rPar.Get(0)->PutDate(nVal);
}
void SbRtl_CDbl(StarBASIC *, SbxArray & rPar, bool) // JSM
{
double nVal = 0.0;
if (rPar.Count() == 2)
{
SbxVariable* pSbxVariable = rPar.Get(1);
if( pSbxVariable->GetType() == SbxSTRING )
{
// #41690
OUString aScanStr = pSbxVariable->GetOUString();
ErrCode Error = SbxValue::ScanNumIntnl( aScanStr, nVal );
if( Error != ERRCODE_NONE )
{
StarBASIC::Error( Error );
}
}
else
{
nVal = pSbxVariable->GetDouble();
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
rPar.Get(0)->PutDouble(nVal);
}
void SbRtl_CInt(StarBASIC *, SbxArray & rPar, bool) // JSM
{
sal_Int16 nVal = 0;
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbxVariable* pSbxVariable = rPar.Get(1);
nVal = pSbxVariable->GetInteger();
rPar.Get(0)->PutInteger(nVal);
}
void SbRtl_CLng(StarBASIC *, SbxArray & rPar, bool) // JSM
{
sal_Int32 nVal = 0;
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbxVariable* pSbxVariable = rPar.Get(1);
nVal = pSbxVariable->GetLong();
rPar.Get(0)->PutLong(nVal);
}
void SbRtl_CSng(StarBASIC *, SbxArray & rPar, bool) // JSM
{
float nVal = float(0.0);
if (rPar.Count() == 2)
{
SbxVariable* pSbxVariable = rPar.Get(1);
if( pSbxVariable->GetType() == SbxSTRING )
{
// #41690
double dVal = 0.0;
OUString aScanStr = pSbxVariable->GetOUString();
ErrCode Error = SbxValue::ScanNumIntnl( aScanStr, dVal, /*bSingle=*/true );
if( SbxBase::GetError() == ERRCODE_NONE && Error != ERRCODE_NONE )
{
StarBASIC::Error( Error );
}
nVal = static_cast<float>(dVal);
}
else
{
nVal = pSbxVariable->GetSingle();
}
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
rPar.Get(0)->PutSingle(nVal);
}
void SbRtl_CStr(StarBASIC *, SbxArray & rPar, bool) // JSM
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbxVariable* pSbxVariable = rPar.Get(1);
OUString aString = pSbxVariable->GetOUString();
rPar.Get(0)->PutString(aString);
}
void SbRtl_CVar(StarBASIC *, SbxArray & rPar, bool) // JSM
{
SbxValues aVals( SbxVARIANT );
if (rPar.Count() == 2)
{
SbxVariable* pSbxVariable = rPar.Get(1);
pSbxVariable->Get( aVals );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
rPar.Get(0)->Put(aVals);
}
void SbRtl_CVErr(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbxVariable* pSbxVariable = rPar.Get(1);
sal_Int16 nErrCode = pSbxVariable->GetInteger();
rPar.Get(0)->PutErr(nErrCode);
}
void SbRtl_Iif(StarBASIC *, SbxArray & rPar, bool) // JSM
{
if (rPar.Count() != 4)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
if (rPar.Get(1)->GetBool())
{
*rPar.Get(0) = *rPar.Get(2);
}
else
{
*rPar.Get(0) = *rPar.Get(3);
}
}
void SbRtl_GetSystemType(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 1)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
// Removed for SRC595
rPar.Get(0)->PutInteger(-1);
}
void SbRtl_GetGUIType(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 1)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
// 17.7.2000 Make simple solution for testtool / fat office
#if defined(_WIN32)
rPar.Get(0)->PutInteger(1);
#elif defined(UNX)
rPar.Get(0)->PutInteger(4);
#else
rPar.Get(0)->PutInteger(-1);
#endif
}
void SbRtl_Red(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
sal_Int32 nRGB = rPar.Get(1)->GetLong();
nRGB &= 0x00FF0000;
nRGB >>= 16;
rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
}
void SbRtl_Green(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
sal_Int32 nRGB = rPar.Get(1)->GetLong();
nRGB &= 0x0000FF00;
nRGB >>= 8;
rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
}
void SbRtl_Blue(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
sal_Int32 nRGB = rPar.Get(1)->GetLong();
nRGB &= 0x000000FF;
rPar.Get(0)->PutInteger(static_cast<sal_Int16>(nRGB));
}
void SbRtl_Switch(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nCount = rPar.Count();
if( !(nCount & 0x0001 ))
{
// number of arguments must be odd
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
sal_uInt32 nCurExpr = 1;
while( nCurExpr < (nCount-1) )
{
if (rPar.Get(nCurExpr)->GetBool())
{
(*rPar.Get(0)) = *(rPar.Get(nCurExpr + 1));
return;
}
nCurExpr += 2;
}
rPar.Get(0)->PutNull();
}
//i#64882# Common wait impl for existing Wait and new WaitUntil
// rtl functions
void Wait_Impl( bool bDurationBased, SbxArray& rPar )
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
tools::Long nWait = 0;
if ( bDurationBased )
{
double dWait = rPar.Get(1)->GetDouble();
double dNow = Now_Impl();
double dSecs = ( dWait - dNow ) * 24.0 * 3600.0;
nWait = static_cast<tools::Long>( dSecs * 1000 ); // wait in thousands of sec
}
else
{
nWait = rPar.Get(1)->GetLong();
}
if( nWait < 0 )
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
Timer aTimer("basic Wait_Impl");
aTimer.SetTimeout( nWait );
aTimer.Start();
while ( aTimer.IsActive() && !Application::IsQuit())
{
Application::Yield();
}
}
//i#64882#
void SbRtl_Wait(StarBASIC *, SbxArray & rPar, bool)
{
Wait_Impl( false, rPar );
}
//i#64882# add new WaitUntil ( for application.wait )
// share wait_impl with 'normal' oobasic wait
void SbRtl_WaitUntil(StarBASIC *, SbxArray & rPar, bool)
{
Wait_Impl( true, rPar );
}
void SbRtl_DoEvents(StarBASIC *, SbxArray & rPar, bool)
{
// don't understand what upstream are up to
// we already process application events etc. in between
// basic runtime pcode ( on a timed basis )
// always return 0
rPar.Get(0)->PutInteger(0);
Application::Reschedule( true );
}
void SbRtl_GetGUIVersion(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 1)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
// Removed for SRC595
rPar.Get(0)->PutLong(-1);
}
void SbRtl_Choose(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() < 2)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
sal_Int16 nIndex = rPar.Get(1)->GetInteger();
sal_uInt32 nCount = rPar.Count();
nCount--;
if( nCount == 1 || nIndex > sal::static_int_cast<sal_Int16>(nCount-1) || nIndex < 1 )
{
rPar.Get(0)->PutNull();
return;
}
(*rPar.Get(0)) = *(rPar.Get(nIndex + 1));
}
void SbRtl_Trim(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() < 2)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
OUString aStr(comphelper::string::strip(rPar.Get(1)->GetOUString(), ' '));
rPar.Get(0)->PutString(aStr);
}
}
void SbRtl_GetSolarVersion(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutLong(LIBO_VERSION_MAJOR * 10000 + LIBO_VERSION_MINOR * 100
+ LIBO_VERSION_MICRO * 1);
}
void SbRtl_TwipsPerPixelX(StarBASIC *, SbxArray & rPar, bool)
{
sal_Int32 nResult = 0;
Size aSize( 100,0 );
MapMode aMap( MapUnit::MapTwip );
OutputDevice* pDevice = Application::GetDefaultDevice();
if( pDevice )
{
aSize = pDevice->PixelToLogic( aSize, aMap );
nResult = aSize.Width() / 100;
}
rPar.Get(0)->PutLong(nResult);
}
void SbRtl_TwipsPerPixelY(StarBASIC *, SbxArray & rPar, bool)
{
sal_Int32 nResult = 0;
Size aSize( 0,100 );
MapMode aMap( MapUnit::MapTwip );
OutputDevice* pDevice = Application::GetDefaultDevice();
if( pDevice )
{
aSize = pDevice->PixelToLogic( aSize, aMap );
nResult = aSize.Height() / 100;
}
rPar.Get(0)->PutLong(nResult);
}
void SbRtl_FreeLibrary(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
GetSbData()->pInst->GetDllMgr()->FreeDll(rPar.Get(1)->GetOUString());
}
bool IsBaseIndexOne()
{
bool bResult = false;
if ( GetSbData()->pInst && GetSbData()->pInst->pRun )
{
sal_uInt16 res = GetSbData()->pInst->pRun->GetBase();
if ( res )
{
bResult = true;
}
}
return bResult;
}
void SbRtl_Array(StarBASIC *, SbxArray & rPar, bool)
{
SbxDimArray* pArray = new SbxDimArray( SbxVARIANT );
sal_uInt32 nArraySize = rPar.Count() - 1;
bool bIncIndex = IsBaseIndexOne();
if( nArraySize )
{
if ( bIncIndex )
{
pArray->AddDim(1, sal::static_int_cast<sal_Int32>(nArraySize));
}
else
{
pArray->AddDim(0, sal::static_int_cast<sal_Int32>(nArraySize) - 1);
}
}
else
{
pArray->unoAddDim(0, -1);
}
// insert parameters into the array
for( sal_uInt32 i = 0 ; i < nArraySize ; i++ )
{
SbxVariable* pVar = rPar.Get(i + 1);
SbxVariable* pNew = new SbxEnsureParentVariable(*pVar);
pNew->SetFlag( SbxFlagBits::Write );
sal_Int32 aIdx[1];
aIdx[0] = static_cast<sal_Int32>(i);
if ( bIncIndex )
{
++aIdx[0];
}
pArray->Put(pNew, aIdx);
}
// return array
SbxVariableRef refVar = rPar.Get(0);
SbxFlagBits nFlags = refVar->GetFlags();
refVar->ResetFlag( SbxFlagBits::Fixed );
refVar->PutObject( pArray );
refVar->SetFlags( nFlags );
refVar->SetParameters( nullptr );
}
// Featurewish #57868
// The function returns a variant-array; if there are no parameters passed,
// an empty array is created (according to dim a(); equal to a sequence of
// the length 0 in Uno).
// If there are parameters passed, there's a dimension created for each of
// them; DimArray( 2, 2, 4 ) is equal to DIM a( 2, 2, 4 )
// the array is always of the type variant
void SbRtl_DimArray(StarBASIC *, SbxArray & rPar, bool)
{
SbxDimArray * pArray = new SbxDimArray( SbxVARIANT );
sal_uInt32 nArrayDims = rPar.Count() - 1;
if( nArrayDims > 0 )
{
for( sal_uInt32 i = 0; i < nArrayDims ; i++ )
{
sal_Int32 ub = rPar.Get(i + 1)->GetLong();
if( ub < 0 )
{
StarBASIC::Error( ERRCODE_BASIC_OUT_OF_RANGE );
ub = 0;
}
pArray->AddDim(0, ub);
}
}
else
{
pArray->unoAddDim(0, -1);
}
SbxVariableRef refVar = rPar.Get(0);
SbxFlagBits nFlags = refVar->GetFlags();
refVar->ResetFlag( SbxFlagBits::Fixed );
refVar->PutObject( pArray );
refVar->SetFlags( nFlags );
refVar->SetParameters( nullptr );
}
/*
* FindObject and FindPropertyObject make it possible to
* address objects and properties of the type Object with
* their name as string-parameters at the runtime.
*
* Example:
* MyObj.Prop1.Bla = 5
*
* is equal to:
* dim ObjVar as Object
* dim ObjProp as Object
* ObjName$ = "MyObj"
* ObjVar = FindObject( ObjName$ )
* PropName$ = "Prop1"
* ObjProp = FindPropertyObject( ObjVar, PropName$ )
* ObjProp.Bla = 5
*
* The names can be created dynamically at the runtime
* so that e. g. via controls "TextEdit1" to "TextEdit5"
* can be iterated in a dialog in a loop.
*/
// 1st parameter = the object's name as string
void SbRtl_FindObject(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() < 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
OUString aNameStr = rPar.Get(1)->GetOUString();
SbxBase* pFind = StarBASIC::FindSBXInCurrentScope( aNameStr );
SbxObject* pFindObj = nullptr;
if( pFind )
{
pFindObj = dynamic_cast<SbxObject*>( pFind );
}
SbxVariableRef refVar = rPar.Get(0);
refVar->PutObject( pFindObj );
}
// address object-property in an object
// 1st parameter = object
// 2nd parameter = the property's name as string
void SbRtl_FindPropertyObject(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() < 3)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbxBase* pObjVar = rPar.Get(1)->GetObject();
SbxObject* pObj = nullptr;
if( pObjVar )
{
pObj = dynamic_cast<SbxObject*>( pObjVar );
}
if( !pObj )
if (auto pSbxVar = dynamic_cast<const SbxVariable*>( pObjVar))
pObj = dynamic_cast<SbxObject*>( pSbxVar->GetObject() );
OUString aNameStr = rPar.Get(2)->GetOUString();
SbxObject* pFindObj = nullptr;
if( pObj )
{
SbxVariable* pFindVar = pObj->Find( aNameStr, SbxClassType::Object );
pFindObj = dynamic_cast<SbxObject*>( pFindVar );
}
else
{
StarBASIC::Error( ERRCODE_BASIC_BAD_PARAMETER );
}
SbxVariableRef refVar = rPar.Get(0);
refVar->PutObject( pFindObj );
}
static bool lcl_WriteSbxVariable( const SbxVariable& rVar, SvStream* pStrm,
bool bBinary, short nBlockLen, bool bIsArray )
{
sal_uInt64 const nFPos = pStrm->Tell();
bool bIsVariant = !rVar.IsFixed();
SbxDataType eType = rVar.GetType();
switch( eType )
{
case SbxBOOL:
case SbxCHAR:
case SbxBYTE:
if( bIsVariant )
{
pStrm->WriteUInt16( SbxBYTE ); // VarType Id
}
pStrm->WriteUChar( rVar.GetByte() );
break;
case SbxEMPTY:
case SbxNULL:
case SbxVOID:
case SbxINTEGER:
case SbxUSHORT:
case SbxINT:
case SbxUINT:
if( bIsVariant )
{
pStrm->WriteUInt16( SbxINTEGER ); // VarType Id
}
pStrm->WriteInt16( rVar.GetInteger() );
break;
case SbxLONG:
case SbxULONG:
if( bIsVariant )
{
pStrm->WriteUInt16( SbxLONG ); // VarType Id
}
pStrm->WriteInt32( rVar.GetLong() );
break;
case SbxSALINT64:
case SbxSALUINT64:
if( bIsVariant )
{
pStrm->WriteUInt16( SbxSALINT64 ); // VarType Id
}
pStrm->WriteUInt64( rVar.GetInt64() );
break;
case SbxSINGLE:
if( bIsVariant )
{
pStrm->WriteUInt16( eType ); // VarType Id
}
pStrm->WriteFloat( rVar.GetSingle() );
break;
case SbxDOUBLE:
case SbxCURRENCY:
case SbxDATE:
if( bIsVariant )
{
pStrm->WriteUInt16( eType ); // VarType Id
}
pStrm->WriteDouble( rVar.GetDouble() );
break;
case SbxSTRING:
case SbxLPSTR:
{
const OUString aStr = rVar.GetOUString();
if( !bBinary || bIsArray )
{
if( bIsVariant )
{
pStrm->WriteUInt16( SbxSTRING );
}
pStrm->WriteUniOrByteString( aStr, osl_getThreadTextEncoding() );
}
else
{
// without any length information! without end-identifier!
// What does that mean for Unicode?! Choosing conversion to ByteString...
OString aByteStr(OUStringToOString(aStr, osl_getThreadTextEncoding()));
pStrm->WriteOString( aByteStr );
}
}
break;
default:
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return false;
}
if( nBlockLen )
{
pStrm->Seek( nFPos + nBlockLen );
}
return pStrm->GetErrorCode() == ERRCODE_NONE;
}
static bool lcl_ReadSbxVariable( SbxVariable& rVar, SvStream* pStrm,
bool bBinary, short nBlockLen )
{
double aDouble;
sal_uInt64 const nFPos = pStrm->Tell();
bool bIsVariant = !rVar.IsFixed();
SbxDataType eVarType = rVar.GetType();
SbxDataType eSrcType = eVarType;
if( bIsVariant )
{
sal_uInt16 nTemp;
pStrm->ReadUInt16( nTemp );
eSrcType = static_cast<SbxDataType>(nTemp);
}
switch( eSrcType )
{
case SbxBOOL:
case SbxCHAR:
case SbxBYTE:
{
sal_uInt8 aByte;
pStrm->ReadUChar( aByte );
if( bBinary && SbiRuntime::isVBAEnabled() && aByte == 1 && pStrm->eof() )
{
aByte = 0;
}
rVar.PutByte( aByte );
}
break;
case SbxEMPTY:
case SbxNULL:
case SbxVOID:
case SbxINTEGER:
case SbxUSHORT:
case SbxINT:
case SbxUINT:
{
sal_Int16 aInt;
pStrm->ReadInt16( aInt );
rVar.PutInteger( aInt );
}
break;
case SbxLONG:
case SbxULONG:
{
sal_Int32 aInt;
pStrm->ReadInt32( aInt );
rVar.PutLong( aInt );
}
break;
case SbxSALINT64:
case SbxSALUINT64:
{
sal_uInt32 aInt;
pStrm->ReadUInt32( aInt );
rVar.PutInt64( static_cast<sal_Int64>(aInt) );
}
break;
case SbxSINGLE:
{
float nS;
pStrm->ReadFloat( nS );
rVar.PutSingle( nS );
}
break;
case SbxDOUBLE:
case SbxCURRENCY:
{
pStrm->ReadDouble( aDouble );
rVar.PutDouble( aDouble );
}
break;
case SbxDATE:
{
pStrm->ReadDouble( aDouble );
rVar.PutDate( aDouble );
}
break;
case SbxSTRING:
case SbxLPSTR:
{
OUString aStr = pStrm->ReadUniOrByteString(osl_getThreadTextEncoding());
rVar.PutString( aStr );
}
break;
default:
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return false;
}
if( nBlockLen )
{
pStrm->Seek( nFPos + nBlockLen );
}
return pStrm->GetErrorCode() == ERRCODE_NONE;
}
// nCurDim = 1...n
static bool lcl_WriteReadSbxArray( SbxDimArray& rArr, SvStream* pStrm,
bool bBinary, sal_Int32 nCurDim, sal_Int32* pOtherDims, bool bWrite )
{
SAL_WARN_IF( nCurDim <= 0,"basic", "Bad Dim");
sal_Int32 nLower, nUpper;
if (!rArr.GetDim(nCurDim, nLower, nUpper))
return false;
for(sal_Int32 nCur = nLower; nCur <= nUpper; nCur++ )
{
pOtherDims[ nCurDim-1 ] = nCur;
if( nCurDim != 1 )
lcl_WriteReadSbxArray(rArr, pStrm, bBinary, nCurDim-1, pOtherDims, bWrite);
else
{
SbxVariable* pVar = rArr.Get(pOtherDims);
bool bRet;
if( bWrite )
bRet = lcl_WriteSbxVariable(*pVar, pStrm, bBinary, 0, true );
else
bRet = lcl_ReadSbxVariable(*pVar, pStrm, bBinary, 0 );
if( !bRet )
return false;
}
}
return true;
}
static void PutGet( SbxArray& rPar, bool bPut )
{
if (rPar.Count() != 4)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
sal_Int16 nFileNo = rPar.Get(1)->GetInteger();
SbxVariable* pVar2 = rPar.Get(2);
SbxDataType eType2 = pVar2->GetType();
bool bHasRecordNo = (eType2 != SbxEMPTY && eType2 != SbxERROR);
tools::Long nRecordNo = pVar2->GetLong();
if ( nFileNo < 1 || ( bHasRecordNo && nRecordNo < 1 ) )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
nRecordNo--;
SbiIoSystem* pIO = GetSbData()->pInst->GetIoSystem();
SbiStream* pSbStrm = pIO->GetStream( nFileNo );
if ( !pSbStrm || !(pSbStrm->GetMode() & (SbiStreamFlags::Binary | SbiStreamFlags::Random)) )
return StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
SvStream* pStrm = pSbStrm->GetStrm();
bool bRandom = pSbStrm->IsRandom();
short nBlockLen = bRandom ? pSbStrm->GetBlockLen() : 0;
if( bPut )
{
pSbStrm->ExpandFile();
}
if( bHasRecordNo )
{
sal_uInt64 const nFilePos = bRandom
? static_cast<sal_uInt64>(nBlockLen * nRecordNo)
: static_cast<sal_uInt64>(nRecordNo);
pStrm->Seek( nFilePos );
}
SbxDimArray* pArr = nullptr;
SbxVariable* pVar = rPar.Get(3);
if( pVar->GetType() & SbxARRAY )
{
SbxBase* pParObj = pVar->GetObject();
pArr = dynamic_cast<SbxDimArray*>( pParObj );
}
bool bRet;
if( pArr )
{
sal_uInt64 const nFPos = pStrm->Tell();
sal_Int32 nDims = pArr->GetDims();
std::unique_ptr<sal_Int32[]> pDims(new sal_Int32[ nDims ]);
bRet = lcl_WriteReadSbxArray(*pArr,pStrm,!bRandom,nDims,pDims.get(),bPut);
pDims.reset();
if( nBlockLen )
pStrm->Seek( nFPos + nBlockLen );
}
else
{
if( bPut )
bRet = lcl_WriteSbxVariable(*pVar, pStrm, !bRandom, nBlockLen, false);
else
bRet = lcl_ReadSbxVariable(*pVar, pStrm, !bRandom, nBlockLen);
}
if( !bRet || pStrm->GetErrorCode() )
StarBASIC::Error( ERRCODE_BASIC_IO_ERROR );
}
void SbRtl_Put(StarBASIC *, SbxArray & rPar, bool)
{
PutGet( rPar, true );
}
void SbRtl_Get(StarBASIC *, SbxArray & rPar, bool)
{
PutGet( rPar, false );
}
void SbRtl_Environ(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
OUString aResult;
// should be ANSI but that's not possible under Win16 in the DLL
OString aByteStr(OUStringToOString(rPar.Get(1)->GetOUString(),
osl_getThreadTextEncoding()));
const char* pEnvStr = getenv(aByteStr.getStr());
if ( pEnvStr )
{
aResult = OUString(pEnvStr, strlen(pEnvStr), osl_getThreadTextEncoding());
}
rPar.Get(0)->PutString(aResult);
}
static double GetDialogZoomFactor( bool bX, tools::Long nValue )
{
OutputDevice* pDevice = Application::GetDefaultDevice();
double nResult = 0;
if( pDevice )
{
Size aRefSize( nValue, nValue );
Fraction aFracX( 1, 26 );
Fraction aFracY( 1, 24 );
MapMode aMap( MapUnit::MapAppFont, Point(), aFracX, aFracY );
Size aScaledSize = pDevice->LogicToPixel( aRefSize, aMap );
aRefSize = pDevice->LogicToPixel( aRefSize, MapMode(MapUnit::MapTwip) );
double nRef, nScaled;
if( bX )
{
nRef = aRefSize.Width();
nScaled = aScaledSize.Width();
}
else
{
nRef = aRefSize.Height();
nScaled = aScaledSize.Height();
}
nResult = nScaled / nRef;
}
return nResult;
}
void SbRtl_GetDialogZoomFactorX(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
rPar.Get(0)->PutDouble(GetDialogZoomFactor(true, rPar.Get(1)->GetLong()));
}
void SbRtl_GetDialogZoomFactorY(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
rPar.Get(0)->PutDouble(GetDialogZoomFactor(false, rPar.Get(1)->GetLong()));
}
void SbRtl_EnableReschedule(StarBASIC *, SbxArray & rPar, bool)
{
rPar.Get(0)->PutEmpty();
if (rPar.Count() != 2)
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
if( GetSbData()->pInst )
GetSbData()->pInst->EnableReschedule(rPar.Get(1)->GetBool());
}
void SbRtl_GetSystemTicks(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 1)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
rPar.Get(0)->PutLong(tools::Time::GetSystemTicks());
}
void SbRtl_GetPathSeparator(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 1)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
rPar.Get(0)->PutString(OUString(SAL_PATHDELIMITER));
}
void SbRtl_ResolvePath(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
OUString aStr = rPar.Get(1)->GetOUString();
rPar.Get(0)->PutString(aStr);
}
void SbRtl_TypeLen(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbxDataType eType = rPar.Get(1)->GetType();
sal_Int16 nLen = 0;
switch( eType )
{
case SbxEMPTY:
case SbxNULL:
case SbxVECTOR:
case SbxARRAY:
case SbxBYREF:
case SbxVOID:
case SbxHRESULT:
case SbxPOINTER:
case SbxDIMARRAY:
case SbxCARRAY:
case SbxUSERDEF:
case SbxOBJECT:
case SbxVARIANT:
case SbxDATAOBJECT:
nLen = 0;
break;
case SbxINTEGER:
case SbxERROR:
case SbxUSHORT:
case SbxINT:
case SbxUINT:
nLen = 2;
break;
case SbxLONG:
case SbxSINGLE:
case SbxULONG:
nLen = 4;
break;
case SbxDOUBLE:
case SbxCURRENCY:
case SbxDATE:
case SbxSALINT64:
case SbxSALUINT64:
nLen = 8;
break;
case SbxCHAR:
case SbxBYTE:
case SbxBOOL:
nLen = 1;
break;
case SbxLPSTR:
case SbxLPWSTR:
case SbxCoreSTRING:
case SbxSTRING:
nLen = static_cast<sal_Int16>(rPar.Get(1)->GetOUString().getLength());
break;
default:
nLen = 0;
break;
}
rPar.Get(0)->PutInteger(nLen);
}
// 1st parameter == class name, other parameters for initialisation
void SbRtl_CreateUnoStruct(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_CreateUnoStruct( rPar );
}
// 1st parameter == service-name
void SbRtl_CreateUnoService(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_CreateUnoService( rPar );
}
void SbRtl_CreateUnoServiceWithArguments(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_CreateUnoServiceWithArguments( rPar );
}
void SbRtl_CreateUnoValue(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_CreateUnoValue( rPar );
}
// no parameters
void SbRtl_GetProcessServiceManager(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_GetProcessServiceManager( rPar );
}
// 1st parameter == Sequence<PropertyValue>
void SbRtl_CreatePropertySet(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_CreatePropertySet( rPar );
}
// multiple interface-names as parameters
void SbRtl_HasUnoInterfaces(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_HasInterfaces( rPar );
}
void SbRtl_IsUnoStruct(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_IsUnoStruct( rPar );
}
void SbRtl_EqualUnoObjects(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_EqualUnoObjects( rPar );
}
void SbRtl_CreateUnoDialog(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_CreateUnoDialog( rPar );
}
// Return the application standard lib as root scope
void SbRtl_GlobalScope(StarBASIC * pBasic, SbxArray & rPar, bool)
{
SbxObject* p = pBasic;
while( p->GetParent() )
{
p = p->GetParent();
}
SbxVariableRef refVar = rPar.Get(0);
refVar->PutObject( p );
}
// Helper functions to convert Url from/to system paths
void SbRtl_ConvertToUrl(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
OUString aStr = rPar.Get(1)->GetOUString();
INetURLObject aURLObj( aStr, INetProtocol::File );
OUString aFileURL = aURLObj.GetMainURL( INetURLObject::DecodeMechanism::NONE );
if( aFileURL.isEmpty() )
{
osl::File::getFileURLFromSystemPath(aStr, aFileURL);
}
if( aFileURL.isEmpty() )
{
aFileURL = aStr;
}
rPar.Get(0)->PutString(aFileURL);
}
void SbRtl_ConvertFromUrl(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
OUString aStr = rPar.Get(1)->GetOUString();
OUString aSysPath;
::osl::File::getSystemPathFromFileURL( aStr, aSysPath );
if( aSysPath.isEmpty() )
{
aSysPath = aStr;
}
rPar.Get(0)->PutString(aSysPath);
}
// Provide DefaultContext
void SbRtl_GetDefaultContext(StarBASIC *, SbxArray & rPar, bool)
{
RTL_Impl_GetDefaultContext( rPar );
}
void SbRtl_Join(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if ( nParCount != 3 && nParCount != 2 )
return StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbxBase* pParObj = rPar.Get(1)->GetObject();
SbxDimArray* pArr = dynamic_cast<SbxDimArray*>( pParObj );
if( !pArr )
return StarBASIC::Error( ERRCODE_BASIC_MUST_HAVE_DIMS );
if (pArr->GetDims() != 1)
return StarBASIC::Error( ERRCODE_BASIC_WRONG_DIMS ); // Syntax Error?!
OUString aDelim;
if( nParCount == 3 )
{
aDelim = rPar.Get(2)->GetOUString();
}
else
{
aDelim = " ";
}
OUStringBuffer aRetStr(32);
sal_Int32 nLower, nUpper;
pArr->GetDim(1, nLower, nUpper);
sal_Int32 aIdx[1];
for (aIdx[0] = nLower; aIdx[0] <= nUpper; ++aIdx[0])
{
OUString aStr = pArr->Get(aIdx)->GetOUString();
aRetStr.append(aStr);
if (aIdx[0] != nUpper)
{
aRetStr.append(aDelim);
}
}
rPar.Get(0)->PutString(aRetStr.makeStringAndClear());
}
void SbRtl_Split(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if ( nParCount < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
OUString aExpression = rPar.Get(1)->GetOUString();
sal_Int32 nArraySize = 0;
std::vector< OUString > vRet;
if( !aExpression.isEmpty() )
{
OUString aDelim;
if( nParCount >= 3 )
{
aDelim = rPar.Get(2)->GetOUString();
}
else
{
aDelim = " ";
}
sal_Int32 nCount = -1;
if( nParCount == 4 )
{
nCount = rPar.Get(3)->GetLong();
}
sal_Int32 nDelimLen = aDelim.getLength();
if( nDelimLen )
{
sal_Int32 iSearch = -1;
sal_Int32 iStart = 0;
do
{
bool bBreak = false;
if( nCount >= 0 && nArraySize == nCount - 1 )
{
bBreak = true;
}
iSearch = aExpression.indexOf( aDelim, iStart );
OUString aSubStr;
if( iSearch >= 0 && !bBreak )
{
aSubStr = aExpression.copy( iStart, iSearch - iStart );
iStart = iSearch + nDelimLen;
}
else
{
aSubStr = aExpression.copy( iStart );
}
vRet.push_back( aSubStr );
nArraySize++;
if( bBreak )
{
break;
}
}
while( iSearch >= 0 );
}
else
{
vRet.push_back( aExpression );
nArraySize = 1;
}
}
// tdf#123025 - split returns an array of substrings
SbxDimArray* pArray = new SbxDimArray( SbxSTRING );
pArray->unoAddDim(0, nArraySize - 1);
// insert parameter(s) into the array
const bool bIsVBAInterOp = SbiRuntime::isVBAEnabled();
for(sal_Int32 i = 0 ; i < nArraySize ; i++ )
{
// tdf#123025 - split returns an array of substrings
SbxVariableRef xVar = new SbxVariable( SbxSTRING );
xVar->PutString( vRet[i] );
// tdf#144924 - allow the assignment of different data types to the individual elements
if (!bIsVBAInterOp)
{
xVar->ResetFlag(SbxFlagBits::Fixed);
}
pArray->Put(xVar.get(), &i);
}
// return array
SbxVariableRef refVar = rPar.Get(0);
SbxFlagBits nFlags = refVar->GetFlags();
refVar->ResetFlag( SbxFlagBits::Fixed );
refVar->PutObject( pArray );
refVar->SetFlags( nFlags );
refVar->SetParameters( nullptr );
}
// MonthName(month[, abbreviate])
void SbRtl_MonthName(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if( nParCount != 2 && nParCount != 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
if( !xCalendar.is() )
{
StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
return;
}
Sequence< CalendarItem2 > aMonthSeq = xCalendar->getMonths2();
sal_Int32 nMonthCount = aMonthSeq.getLength();
sal_Int16 nVal = rPar.Get(1)->GetInteger();
if( nVal < 1 || nVal > nMonthCount )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
bool bAbbreviate = false;
if( nParCount == 3 )
bAbbreviate = rPar.Get(2)->GetBool();
const CalendarItem2* pCalendarItems = aMonthSeq.getConstArray();
const CalendarItem2& rItem = pCalendarItems[nVal - 1];
OUString aRetStr = ( bAbbreviate ? rItem.AbbrevName : rItem.FullName );
rPar.Get(0)->PutString(aRetStr);
}
// WeekdayName(weekday, abbreviate, firstdayofweek)
void SbRtl_WeekdayName(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if( nParCount < 2 || nParCount > 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
if( !xCalendar.is() )
{
StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
return;
}
Sequence< CalendarItem2 > aDaySeq = xCalendar->getDays2();
sal_Int16 nDayCount = static_cast<sal_Int16>(aDaySeq.getLength());
sal_Int16 nDay = rPar.Get(1)->GetInteger();
sal_Int16 nFirstDay = 0;
if( nParCount == 4 )
{
nFirstDay = rPar.Get(3)->GetInteger();
if( nFirstDay < 0 || nFirstDay > 7 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
}
if( nFirstDay == 0 )
{
nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 );
}
nDay = 1 + (nDay + nDayCount + nFirstDay - 2) % nDayCount;
if( nDay < 1 || nDay > nDayCount )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
bool bAbbreviate = false;
if( nParCount >= 3 )
{
SbxVariable* pPar2 = rPar.Get(2);
if( !pPar2->IsErr() )
{
bAbbreviate = pPar2->GetBool();
}
}
const CalendarItem2* pCalendarItems = aDaySeq.getConstArray();
const CalendarItem2& rItem = pCalendarItems[nDay - 1];
OUString aRetStr = ( bAbbreviate ? rItem.AbbrevName : rItem.FullName );
rPar.Get(0)->PutString(aRetStr);
}
void SbRtl_Weekday(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if ( nParCount < 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
else
{
double aDate = rPar.Get(1)->GetDate();
bool bFirstDay = false;
sal_Int16 nFirstDay = 0;
if ( nParCount > 2 )
{
nFirstDay = rPar.Get(2)->GetInteger();
bFirstDay = true;
}
sal_Int16 nDay = implGetWeekDay( aDate, bFirstDay, nFirstDay );
rPar.Get(0)->PutInteger(nDay);
}
}
namespace {
enum Interval
{
INTERVAL_YYYY,
INTERVAL_Q,
INTERVAL_M,
INTERVAL_Y,
INTERVAL_D,
INTERVAL_W,
INTERVAL_WW,
INTERVAL_H,
INTERVAL_N,
INTERVAL_S
};
struct IntervalInfo
{
Interval meInterval;
char const * mStringCode;
double mdValue;
bool mbSimple;
};
}
static IntervalInfo const * getIntervalInfo( const OUString& rStringCode )
{
static IntervalInfo const aIntervalTable[] =
{
{ INTERVAL_YYYY, "yyyy", 0.0, false }, // Year
{ INTERVAL_Q, "q", 0.0, false }, // Quarter
{ INTERVAL_M, "m", 0.0, false }, // Month
{ INTERVAL_Y, "y", 1.0, true }, // Day of year
{ INTERVAL_D, "d", 1.0, true }, // Day
{ INTERVAL_W, "w", 1.0, true }, // Weekday
{ INTERVAL_WW, "ww", 7.0, true }, // Week
{ INTERVAL_H, "h", 1.0 / 24.0, true }, // Hour
{ INTERVAL_N, "n", 1.0 / 1440.0, true }, // Minute
{ INTERVAL_S, "s", 1.0 / 86400.0, true } // Second
};
auto const pred = [&rStringCode](const IntervalInfo &aInterval) {
return rStringCode.equalsIgnoreAsciiCaseAscii(aInterval.mStringCode);
};
auto intervalIter = std::find_if(std::begin(aIntervalTable), std::end(aIntervalTable), pred);
if(intervalIter != std::end(aIntervalTable)) {
return intervalIter;
}
return nullptr;
}
static void implGetDayMonthYear( sal_Int16& rnYear, sal_Int16& rnMonth, sal_Int16& rnDay, double dDate )
{
rnDay = implGetDateDay( dDate );
rnMonth = implGetDateMonth( dDate );
rnYear = implGetDateYear( dDate );
}
/** Limits a date to valid dates within tools' class Date capabilities.
@return the year number, truncated if necessary and in that case also
rMonth and rDay adjusted.
*/
static sal_Int16 limitDate( sal_Int32 n32Year, sal_Int16& rMonth, sal_Int16& rDay )
{
if( n32Year > SAL_MAX_INT16 )
{
n32Year = SAL_MAX_INT16;
rMonth = 12;
rDay = 31;
}
else if( n32Year < SAL_MIN_INT16 )
{
n32Year = SAL_MIN_INT16;
rMonth = 1;
rDay = 1;
}
return static_cast<sal_Int16>(n32Year);
}
void SbRtl_DateAdd(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if( nParCount != 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
OUString aStringCode = rPar.Get(1)->GetOUString();
IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
if( !pInfo )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_Int32 lNumber = rPar.Get(2)->GetLong();
double dDate = rPar.Get(3)->GetDate();
double dNewDate = 0;
if( pInfo->mbSimple )
{
double dAdd = pInfo->mdValue * lNumber;
dNewDate = dDate + dAdd;
}
else
{
// Keep hours, minutes, seconds
double dHoursMinutesSeconds = dDate - floor( dDate );
bool bOk = true;
sal_Int16 nYear, nMonth, nDay;
sal_Int16 nTargetYear16 = 0, nTargetMonth = 0;
implGetDayMonthYear( nYear, nMonth, nDay, dDate );
switch( pInfo->meInterval )
{
case INTERVAL_YYYY:
{
sal_Int32 nTargetYear = lNumber + nYear;
nTargetYear16 = limitDate( nTargetYear, nMonth, nDay );
/* TODO: should the result be error if the date was limited? It never was. */
nTargetMonth = nMonth;
bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, SbDateCorrection::TruncateToMonth, dNewDate );
break;
}
case INTERVAL_Q:
case INTERVAL_M:
{
bool bNeg = (lNumber < 0);
if( bNeg )
lNumber = -lNumber;
sal_Int32 nYearsAdd;
sal_Int16 nMonthAdd;
if( pInfo->meInterval == INTERVAL_Q )
{
nYearsAdd = lNumber / 4;
nMonthAdd = static_cast<sal_Int16>( 3 * (lNumber % 4) );
}
else
{
nYearsAdd = lNumber / 12;
nMonthAdd = static_cast<sal_Int16>( lNumber % 12 );
}
sal_Int32 nTargetYear;
if( bNeg )
{
nTargetMonth = nMonth - nMonthAdd;
if( nTargetMonth <= 0 )
{
nTargetMonth += 12;
nYearsAdd++;
}
nTargetYear = static_cast<sal_Int32>(nYear) - nYearsAdd;
}
else
{
nTargetMonth = nMonth + nMonthAdd;
if( nTargetMonth > 12 )
{
nTargetMonth -= 12;
nYearsAdd++;
}
nTargetYear = static_cast<sal_Int32>(nYear) + nYearsAdd;
}
nTargetYear16 = limitDate( nTargetYear, nTargetMonth, nDay );
/* TODO: should the result be error if the date was limited? It never was. */
bOk = implDateSerial( nTargetYear16, nTargetMonth, nDay, false, SbDateCorrection::TruncateToMonth, dNewDate );
break;
}
default: break;
}
if( bOk )
dNewDate += dHoursMinutesSeconds;
}
rPar.Get(0)->PutDate(dNewDate);
}
static double RoundImpl( double d )
{
return ( d >= 0 ) ? floor( d + 0.5 ) : -floor( -d + 0.5 );
}
void SbRtl_DateDiff(StarBASIC *, SbxArray & rPar, bool)
{
// DateDiff(interval, date1, date2[, firstdayofweek[, firstweekofyear]])
sal_uInt32 nParCount = rPar.Count();
if( nParCount < 4 || nParCount > 6 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
OUString aStringCode = rPar.Get(1)->GetOUString();
IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
if( !pInfo )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
double dDate1 = rPar.Get(2)->GetDate();
double dDate2 = rPar.Get(3)->GetDate();
double dRet = 0.0;
switch( pInfo->meInterval )
{
case INTERVAL_YYYY:
{
sal_Int16 nYear1 = implGetDateYear( dDate1 );
sal_Int16 nYear2 = implGetDateYear( dDate2 );
dRet = nYear2 - nYear1;
break;
}
case INTERVAL_Q:
{
sal_Int16 nYear1 = implGetDateYear( dDate1 );
sal_Int16 nYear2 = implGetDateYear( dDate2 );
sal_Int16 nQ1 = 1 + (implGetDateMonth( dDate1 ) - 1) / 3;
sal_Int16 nQ2 = 1 + (implGetDateMonth( dDate2 ) - 1) / 3;
sal_Int16 nQGes1 = 4 * nYear1 + nQ1;
sal_Int16 nQGes2 = 4 * nYear2 + nQ2;
dRet = nQGes2 - nQGes1;
break;
}
case INTERVAL_M:
{
sal_Int16 nYear1 = implGetDateYear( dDate1 );
sal_Int16 nYear2 = implGetDateYear( dDate2 );
sal_Int16 nMonth1 = implGetDateMonth( dDate1 );
sal_Int16 nMonth2 = implGetDateMonth( dDate2 );
sal_Int16 nMonthGes1 = 12 * nYear1 + nMonth1;
sal_Int16 nMonthGes2 = 12 * nYear2 + nMonth2;
dRet = nMonthGes2 - nMonthGes1;
break;
}
case INTERVAL_Y:
case INTERVAL_D:
{
double dDays1 = floor( dDate1 );
double dDays2 = floor( dDate2 );
dRet = dDays2 - dDays1;
break;
}
case INTERVAL_W:
case INTERVAL_WW:
{
double dDays1 = floor( dDate1 );
double dDays2 = floor( dDate2 );
if( pInfo->meInterval == INTERVAL_WW )
{
sal_Int16 nFirstDay = 1; // Default
if( nParCount >= 5 )
{
nFirstDay = rPar.Get(4)->GetInteger();
if( nFirstDay < 0 || nFirstDay > 7 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
if( nFirstDay == 0 )
{
const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
if( !xCalendar.is() )
{
StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
return;
}
nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 );
}
}
sal_Int16 nDay1 = implGetWeekDay( dDate1 );
sal_Int16 nDay1_Diff = nDay1 - nFirstDay;
if( nDay1_Diff < 0 )
nDay1_Diff += 7;
dDays1 -= nDay1_Diff;
sal_Int16 nDay2 = implGetWeekDay( dDate2 );
sal_Int16 nDay2_Diff = nDay2 - nFirstDay;
if( nDay2_Diff < 0 )
nDay2_Diff += 7;
dDays2 -= nDay2_Diff;
}
double dDiff = dDays2 - dDays1;
dRet = ( dDiff >= 0 ) ? floor( dDiff / 7.0 ) : -floor( -dDiff / 7.0 );
break;
}
case INTERVAL_H:
{
dRet = RoundImpl( 24.0 * (dDate2 - dDate1) );
break;
}
case INTERVAL_N:
{
dRet = RoundImpl( 1440.0 * (dDate2 - dDate1) );
break;
}
case INTERVAL_S:
{
dRet = RoundImpl( 86400.0 * (dDate2 - dDate1) );
break;
}
}
rPar.Get(0)->PutDouble(dRet);
}
static double implGetDateOfFirstDayInFirstWeek
( sal_Int16 nYear, sal_Int16& nFirstDay, sal_Int16& nFirstWeek, bool* pbError = nullptr )
{
ErrCode nError = ERRCODE_NONE;
if( nFirstDay < 0 || nFirstDay > 7 )
nError = ERRCODE_BASIC_BAD_ARGUMENT;
if( nFirstWeek < 0 || nFirstWeek > 3 )
nError = ERRCODE_BASIC_BAD_ARGUMENT;
Reference< XCalendar4 > xCalendar;
if( nFirstDay == 0 || nFirstWeek == 0 )
{
xCalendar = getLocaleCalendar();
if( !xCalendar.is() )
nError = ERRCODE_BASIC_BAD_ARGUMENT;
}
if( nError != ERRCODE_NONE )
{
StarBASIC::Error( nError );
if( pbError )
*pbError = true;
return 0.0;
}
if( nFirstDay == 0 )
nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 );
sal_Int16 nFirstWeekMinDays = 0; // Not used for vbFirstJan1 = default
if( nFirstWeek == 0 )
{
nFirstWeekMinDays = xCalendar->getMinimumNumberOfDaysForFirstWeek();
if( nFirstWeekMinDays == 1 )
{
nFirstWeekMinDays = 0;
nFirstWeek = 1;
}
else if( nFirstWeekMinDays == 4 )
nFirstWeek = 2;
else if( nFirstWeekMinDays == 7 )
nFirstWeek = 3;
}
else if( nFirstWeek == 2 )
nFirstWeekMinDays = 4; // vbFirstFourDays
else if( nFirstWeek == 3 )
nFirstWeekMinDays = 7; // vbFirstFourDays
double dBaseDate;
implDateSerial( nYear, 1, 1, false, SbDateCorrection::None, dBaseDate );
sal_Int16 nWeekDay0101 = implGetWeekDay( dBaseDate );
sal_Int16 nDayDiff = nWeekDay0101 - nFirstDay;
if( nDayDiff < 0 )
nDayDiff += 7;
if( nFirstWeekMinDays )
{
sal_Int16 nThisWeeksDaysInYearCount = 7 - nDayDiff;
if( nThisWeeksDaysInYearCount < nFirstWeekMinDays )
nDayDiff -= 7;
}
double dRetDate = dBaseDate - nDayDiff;
return dRetDate;
}
void SbRtl_DatePart(StarBASIC *, SbxArray & rPar, bool)
{
// DatePart(interval, date[,firstdayofweek[, firstweekofyear]])
sal_uInt32 nParCount = rPar.Count();
if( nParCount < 3 || nParCount > 5 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
OUString aStringCode = rPar.Get(1)->GetOUString();
IntervalInfo const * pInfo = getIntervalInfo( aStringCode );
if( !pInfo )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
double dDate = rPar.Get(2)->GetDate();
sal_Int32 nRet = 0;
switch( pInfo->meInterval )
{
case INTERVAL_YYYY:
{
nRet = implGetDateYear( dDate );
break;
}
case INTERVAL_Q:
{
nRet = 1 + (implGetDateMonth( dDate ) - 1) / 3;
break;
}
case INTERVAL_M:
{
nRet = implGetDateMonth( dDate );
break;
}
case INTERVAL_Y:
{
sal_Int16 nYear = implGetDateYear( dDate );
double dBaseDate;
implDateSerial( nYear, 1, 1, false, SbDateCorrection::None, dBaseDate );
nRet = 1 + sal_Int32( dDate - dBaseDate );
break;
}
case INTERVAL_D:
{
nRet = implGetDateDay( dDate );
break;
}
case INTERVAL_W:
{
bool bFirstDay = false;
sal_Int16 nFirstDay = 1; // Default
if( nParCount >= 4 )
{
nFirstDay = rPar.Get(3)->GetInteger();
bFirstDay = true;
}
nRet = implGetWeekDay( dDate, bFirstDay, nFirstDay );
break;
}
case INTERVAL_WW:
{
sal_Int16 nFirstDay = 1; // Default
if( nParCount >= 4 )
nFirstDay = rPar.Get(3)->GetInteger();
sal_Int16 nFirstWeek = 1; // Default
if( nParCount == 5 )
nFirstWeek = rPar.Get(4)->GetInteger();
sal_Int16 nYear = implGetDateYear( dDate );
bool bError = false;
double dYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear, nFirstDay, nFirstWeek, &bError );
if( !bError )
{
if( dYearFirstDay > dDate )
{
// Date belongs to last year's week
dYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear - 1, nFirstDay, nFirstWeek );
}
else if( nFirstWeek != 1 )
{
// Check if date belongs to next year
double dNextYearFirstDay = implGetDateOfFirstDayInFirstWeek( nYear + 1, nFirstDay, nFirstWeek );
if( dDate >= dNextYearFirstDay )
dYearFirstDay = dNextYearFirstDay;
}
// Calculate week
double dDiff = dDate - dYearFirstDay;
nRet = 1 + sal_Int32( dDiff / 7 );
}
break;
}
case INTERVAL_H:
{
nRet = implGetHour( dDate );
break;
}
case INTERVAL_N:
{
nRet = implGetMinute( dDate );
break;
}
case INTERVAL_S:
{
nRet = implGetSecond( dDate );
break;
}
}
rPar.Get(0)->PutLong(nRet);
}
// FormatDateTime(Date[,NamedFormat])
void SbRtl_FormatDateTime(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if( nParCount < 2 || nParCount > 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
double dDate = rPar.Get(1)->GetDate();
sal_Int16 nNamedFormat = 0;
if( nParCount > 2 )
{
nNamedFormat = rPar.Get(2)->GetInteger();
if( nNamedFormat < 0 || nNamedFormat > 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
}
const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
if( !xCalendar.is() )
{
StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
return;
}
OUString aRetStr;
SbxVariableRef pSbxVar = new SbxVariable( SbxSTRING );
switch( nNamedFormat )
{
// GeneralDate:
// Display a date and/or time. If there is a date part,
// display it as a short date. If there is a time part,
// display it as a long time. If present, both parts are displayed.
// 12/21/2004 11:24:50 AM
// 21.12.2004 12:13:51
case 0:
pSbxVar->PutDate( dDate );
aRetStr = pSbxVar->GetOUString();
break;
// LongDate: Display a date using the long date format specified
// in your computer's regional settings.
// Tuesday, December 21, 2004
// Dienstag, 21. December 2004
case 1:
{
std::shared_ptr<SvNumberFormatter> pFormatter;
if( GetSbData()->pInst )
{
pFormatter = GetSbData()->pInst->GetNumberFormatter();
}
else
{
sal_uInt32 n; // Dummy
pFormatter = SbiInstance::PrepareNumberFormatter( n, n, n );
}
LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
const sal_uInt32 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_LONG, eLangType );
const Color* pCol;
pFormatter->GetOutputString( dDate, nIndex, aRetStr, &pCol );
break;
}
// ShortDate: Display a date using the short date format specified
// in your computer's regional settings.
// 21.12.2004
case 2:
pSbxVar->PutDate( floor(dDate) );
aRetStr = pSbxVar->GetOUString();
break;
// LongTime: Display a time using the time format specified
// in your computer's regional settings.
// 11:24:50 AM
// 12:13:51
case 3:
// ShortTime: Display a time using the 24-hour format (hh:mm).
// 11:24
case 4:
double dTime = modf( dDate, &o3tl::temporary(double()) );
pSbxVar->PutDate( dTime );
if( nNamedFormat == 3 )
{
aRetStr = pSbxVar->GetOUString();
}
else
{
aRetStr = pSbxVar->GetOUString().copy( 0, 5 );
}
break;
}
rPar.Get(0)->PutString(aRetStr);
}
void SbRtl_Frac(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if( nParCount != 2)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxVariable* pSbxVariable = rPar.Get(1);
double dVal = pSbxVariable->GetDouble();
if(dVal >= 0)
rPar.Get(0)->PutDouble(dVal - ::rtl::math::approxFloor(dVal));
else
rPar.Get(0)->PutDouble(dVal - ::rtl::math::approxCeil(dVal));
}
void SbRtl_Round(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nParCount = rPar.Count();
if( nParCount != 2 && nParCount != 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxVariable* pSbxVariable = rPar.Get(1);
double dVal = pSbxVariable->GetDouble();
double dRes = 0.0;
if( dVal != 0.0 )
{
sal_Int16 numdecimalplaces = 0;
if( nParCount == 3 )
{
numdecimalplaces = rPar.Get(2)->GetInteger();
if( numdecimalplaces < 0 || numdecimalplaces > 22 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
}
dRes = rtl_math_round(dVal, numdecimalplaces, rtl_math_RoundingMode_HalfEven);
}
rPar.Get(0)->PutDouble(dRes);
}
static void CallFunctionAccessFunction( const Sequence< Any >& aArgs, const OUString& sFuncName, SbxVariable* pRet )
{
static Reference< XFunctionAccess > xFunc;
try
{
if ( !xFunc.is() )
{
Reference< XMultiServiceFactory > xFactory( getProcessServiceFactory() );
if( xFactory.is() )
{
xFunc.set( xFactory->createInstance(u"com.sun.star.sheet.FunctionAccess"_ustr), UNO_QUERY_THROW);
}
}
Any aRet = xFunc->callFunction( sFuncName, aArgs );
unoToSbxValue( pRet, aRet );
}
catch(const Exception& )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
void SbRtl_SYD(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 4 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
Sequence< Any > aParams
{
Any(rPar.Get(1)->GetDouble()),
Any(rPar.Get(2)->GetDouble()),
Any(rPar.Get(3)->GetDouble()),
Any(rPar.Get(4)->GetDouble())
};
CallFunctionAccessFunction(aParams, u"SYD"_ustr, rPar.Get(0));
}
void SbRtl_SLN(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
Sequence< Any > aParams
{
Any(rPar.Get(1)->GetDouble()),
Any(rPar.Get(2)->GetDouble()),
Any(rPar.Get(3)->GetDouble())
};
CallFunctionAccessFunction(aParams, u"SLN"_ustr, rPar.Get(0));
}
void SbRtl_Pmt(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 3 || nArgCount > 5 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
double rate = rPar.Get(1)->GetDouble();
double nper = rPar.Get(2)->GetDouble();
double pmt = rPar.Get(3)->GetDouble();
// set default values for Optional args
double fv = 0;
double type = 0;
// fv
if ( nArgCount >= 4 )
{
if (rPar.Get(4)->GetType() != SbxEMPTY)
fv = rPar.Get(4)->GetDouble();
}
// type
if ( nArgCount >= 5 )
{
if (rPar.Get(5)->GetType() != SbxEMPTY)
type = rPar.Get(5)->GetDouble();
}
Sequence< Any > aParams
{
Any(rate),
Any(nper),
Any(pmt),
Any(fv),
Any(type)
};
CallFunctionAccessFunction(aParams, u"Pmt"_ustr, rPar.Get(0));
}
void SbRtl_PPmt(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 4 || nArgCount > 6 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
double rate = rPar.Get(1)->GetDouble();
double per = rPar.Get(2)->GetDouble();
double nper = rPar.Get(3)->GetDouble();
double pv = rPar.Get(4)->GetDouble();
// set default values for Optional args
double fv = 0;
double type = 0;
// fv
if ( nArgCount >= 5 )
{
if (rPar.Get(5)->GetType() != SbxEMPTY)
fv = rPar.Get(5)->GetDouble();
}
// type
if ( nArgCount >= 6 )
{
if (rPar.Get(6)->GetType() != SbxEMPTY)
type = rPar.Get(6)->GetDouble();
}
Sequence< Any > aParams
{
Any(rate),
Any(per),
Any(nper),
Any(pv),
Any(fv),
Any(type)
};
CallFunctionAccessFunction(aParams, u"PPmt"_ustr, rPar.Get(0));
}
void SbRtl_PV(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 3 || nArgCount > 5 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
double rate = rPar.Get(1)->GetDouble();
double nper = rPar.Get(2)->GetDouble();
double pmt = rPar.Get(3)->GetDouble();
// set default values for Optional args
double fv = 0;
double type = 0;
// fv
if ( nArgCount >= 4 )
{
if (rPar.Get(4)->GetType() != SbxEMPTY)
fv = rPar.Get(4)->GetDouble();
}
// type
if ( nArgCount >= 5 )
{
if (rPar.Get(5)->GetType() != SbxEMPTY)
type = rPar.Get(5)->GetDouble();
}
Sequence< Any > aParams
{
Any(rate),
Any(nper),
Any(pmt),
Any(fv),
Any(type)
};
CallFunctionAccessFunction(aParams, u"PV"_ustr, rPar.Get(0));
}
void SbRtl_NPV(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 1 || nArgCount > 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
Any aValues = sbxToUnoValue(rPar.Get(2),
cppu::UnoType<Sequence<double>>::get() );
// convert for calc functions
Sequence< Sequence< double > > sValues(1);
aValues >>= sValues.getArray()[ 0 ];
aValues <<= sValues;
Sequence< Any > aParams
{
Any(rPar.Get(1)->GetDouble()),
aValues
};
CallFunctionAccessFunction(aParams, u"NPV"_ustr, rPar.Get(0));
}
void SbRtl_NPer(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 3 || nArgCount > 5 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
double rate = rPar.Get(1)->GetDouble();
double pmt = rPar.Get(2)->GetDouble();
double pv = rPar.Get(3)->GetDouble();
// set default values for Optional args
double fv = 0;
double type = 0;
// fv
if ( nArgCount >= 4 )
{
if (rPar.Get(4)->GetType() != SbxEMPTY)
fv = rPar.Get(4)->GetDouble();
}
// type
if ( nArgCount >= 5 )
{
if (rPar.Get(5)->GetType() != SbxEMPTY)
type = rPar.Get(5)->GetDouble();
}
Sequence< Any > aParams
{
Any(rate),
Any(pmt),
Any(pv),
Any(fv),
Any(type)
};
CallFunctionAccessFunction(aParams, u"NPer"_ustr, rPar.Get(0));
}
void SbRtl_MIRR(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 3 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
Any aValues = sbxToUnoValue(rPar.Get(1),
cppu::UnoType<Sequence<double>>::get() );
// convert for calc functions
Sequence< Sequence< double > > sValues(1);
aValues >>= sValues.getArray()[ 0 ];
aValues <<= sValues;
Sequence< Any > aParams
{
aValues,
Any(rPar.Get(2)->GetDouble()),
Any(rPar.Get(3)->GetDouble())
};
CallFunctionAccessFunction(aParams, u"MIRR"_ustr, rPar.Get(0));
}
void SbRtl_IRR(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 1 || nArgCount > 2 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
Any aValues = sbxToUnoValue(rPar.Get(1),
cppu::UnoType<Sequence<double>>::get() );
// convert for calc functions
Sequence< Sequence< double > > sValues(1);
aValues >>= sValues.getArray()[ 0 ];
aValues <<= sValues;
// set default values for Optional args
double guess = 0.1;
// guess
if ( nArgCount >= 2 )
{
if (rPar.Get(2)->GetType() != SbxEMPTY)
guess = rPar.Get(2)->GetDouble();
}
Sequence< Any > aParams
{
aValues,
Any(guess)
};
CallFunctionAccessFunction(aParams, u"IRR"_ustr, rPar.Get(0));
}
void SbRtl_IPmt(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 4 || nArgCount > 6 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
double rate = rPar.Get(1)->GetDouble();
double per = rPar.Get(2)->GetInteger();
double nper = rPar.Get(3)->GetDouble();
double pv = rPar.Get(4)->GetDouble();
// set default values for Optional args
double fv = 0;
double type = 0;
// fv
if ( nArgCount >= 5 )
{
if (rPar.Get(5)->GetType() != SbxEMPTY)
fv = rPar.Get(5)->GetDouble();
}
// type
if ( nArgCount >= 6 )
{
if (rPar.Get(6)->GetType() != SbxEMPTY)
type = rPar.Get(6)->GetDouble();
}
Sequence< Any > aParams
{
Any(rate),
Any(per),
Any(nper),
Any(pv),
Any(fv),
Any(type)
};
CallFunctionAccessFunction(aParams, u"IPmt"_ustr, rPar.Get(0));
}
void SbRtl_FV(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 3 || nArgCount > 5 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
double rate = rPar.Get(1)->GetDouble();
double nper = rPar.Get(2)->GetDouble();
double pmt = rPar.Get(3)->GetDouble();
// set default values for Optional args
double pv = 0;
double type = 0;
// pv
if ( nArgCount >= 4 )
{
if (rPar.Get(4)->GetType() != SbxEMPTY)
pv = rPar.Get(4)->GetDouble();
}
// type
if ( nArgCount >= 5 )
{
if (rPar.Get(5)->GetType() != SbxEMPTY)
type = rPar.Get(5)->GetDouble();
}
Sequence< Any > aParams
{
Any(rate),
Any(nper),
Any(pmt),
Any(pv),
Any(type)
};
CallFunctionAccessFunction(aParams, u"FV"_ustr, rPar.Get(0));
}
void SbRtl_DDB(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 4 || nArgCount > 5 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
double cost = rPar.Get(1)->GetDouble();
double salvage = rPar.Get(2)->GetDouble();
double life = rPar.Get(3)->GetDouble();
double period = rPar.Get(4)->GetDouble();
// set default values for Optional args
double factor = 2;
// factor
if ( nArgCount >= 5 )
{
if (rPar.Get(5)->GetType() != SbxEMPTY)
factor = rPar.Get(5)->GetDouble();
}
Sequence< Any > aParams
{
Any(cost),
Any(salvage),
Any(life),
Any(period),
Any(factor)
};
CallFunctionAccessFunction(aParams, u"DDB"_ustr, rPar.Get(0));
}
void SbRtl_Rate(StarBASIC *, SbxArray & rPar, bool)
{
sal_uInt32 nArgCount = rPar.Count() - 1;
if ( nArgCount < 3 || nArgCount > 6 )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
// retrieve non-optional params
double nper = 0;
double pmt = 0;
double pv = 0;
nper = rPar.Get(1)->GetDouble();
pmt = rPar.Get(2)->GetDouble();
pv = rPar.Get(3)->GetDouble();
// set default values for Optional args
double fv = 0;
double type = 0;
double guess = 0.1;
// fv
if ( nArgCount >= 4 )
{
if (rPar.Get(4)->GetType() != SbxEMPTY)
fv = rPar.Get(4)->GetDouble();
}
// type
if ( nArgCount >= 5 )
{
if (rPar.Get(5)->GetType() != SbxEMPTY)
type = rPar.Get(5)->GetDouble();
}
// guess
if ( nArgCount >= 6 )
{
if (rPar.Get(6)->GetType() != SbxEMPTY)
guess = rPar.Get(6)->GetDouble();
}
Sequence< Any > aParams
{
Any(nper),
Any(pmt),
Any(pv),
Any(fv),
Any(type),
Any(guess)
};
CallFunctionAccessFunction(aParams, u"Rate"_ustr, rPar.Get(0));
}
void SbRtl_StrReverse(StarBASIC *, SbxArray & rPar, bool)
{
if (rPar.Count() != 2)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
SbxVariable* pSbxVariable = rPar.Get(1);
if( pSbxVariable->IsNull() )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
OUString aStr = comphelper::string::reverseString(pSbxVariable->GetOUString());
rPar.Get(0)->PutString(aStr);
}
void SbRtl_CompatibilityMode(StarBASIC *, SbxArray & rPar, bool)
{
bool bEnabled = false;
sal_uInt32 nCount = rPar.Count();
if ( nCount != 1 && nCount != 2 )
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
SbiInstance* pInst = GetSbData()->pInst;
if( pInst )
{
if ( nCount == 2 )
{
pInst->EnableCompatibility(rPar.Get(1)->GetBool());
}
bEnabled = pInst->IsCompatibility();
}
rPar.Get(0)->PutBool(bEnabled);
}
void SbRtl_Input(StarBASIC *, SbxArray & rPar, bool)
{
// 2 parameters needed
if (rPar.Count() < 3)
{
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
return;
}
sal_uInt16 nByteCount = rPar.Get(1)->GetUShort();
sal_Int16 nFileNumber = rPar.Get(2)->GetInteger();
SbiIoSystem* pIosys = GetSbData()->pInst->GetIoSystem();
SbiStream* pSbStrm = pIosys->GetStream( nFileNumber );
if ( !pSbStrm || !(pSbStrm->GetMode() & (SbiStreamFlags::Binary | SbiStreamFlags::Input)) )
{
StarBASIC::Error( ERRCODE_BASIC_BAD_CHANNEL );
return;
}
OString aByteBuffer;
ErrCode err = pSbStrm->Read( aByteBuffer, nByteCount, true );
if( !err )
err = pIosys->GetError();
if( err )
{
StarBASIC::Error( err );
return;
}
rPar.Get(0)->PutString(OStringToOUString(aByteBuffer, osl_getThreadTextEncoding()));
}
void SbRtl_Me(StarBASIC *, SbxArray & rPar, bool)
{
SbModule* pActiveModule = GetSbData()->pInst->GetActiveModule();
SbClassModuleObject* pClassModuleObject = dynamic_cast<SbClassModuleObject*>( pActiveModule );
SbxVariableRef refVar = rPar.Get(0);
if( pClassModuleObject == nullptr )
{
SbObjModule* pMod = dynamic_cast<SbObjModule*>( pActiveModule );
if ( pMod )
refVar->PutObject( pMod );
else
StarBASIC::Error( ERRCODE_BASIC_INVALID_USAGE_OBJECT );
}
else
refVar->PutObject( pClassModuleObject );
}
#endif
sal_Int16 implGetWeekDay( double aDate, bool bFirstDayParam, sal_Int16 nFirstDay )
{
Date aRefDate(1899'12'30);
sal_Int32 nDays = static_cast<sal_Int32>(aDate);
aRefDate.AddDays( nDays);
DayOfWeek aDay = aRefDate.GetDayOfWeek();
sal_Int16 nDay;
if ( aDay != SUNDAY )
nDay = static_cast<sal_Int16>(aDay) + 2;
else
nDay = 1; // 1 == Sunday
// #117253 optional 2nd parameter "firstdayofweek"
if( bFirstDayParam )
{
if( nFirstDay < 0 || nFirstDay > 7 )
{
#if HAVE_FEATURE_SCRIPTING
StarBASIC::Error( ERRCODE_BASIC_BAD_ARGUMENT );
#endif
return 0;
}
if( nFirstDay == 0 )
{
const Reference< XCalendar4 >& xCalendar = getLocaleCalendar();
if( !xCalendar.is() )
{
#if HAVE_FEATURE_SCRIPTING
StarBASIC::Error( ERRCODE_BASIC_INTERNAL_ERROR );
#endif
return 0;
}
nFirstDay = sal_Int16( xCalendar->getFirstDayOfWeek() + 1 );
}
nDay = 1 + (nDay + 7 - nFirstDay) % 7;
}
return nDay;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'Call' is required to be utilized.