REM ======================================================================================================================= REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. === REM === Full documentation is available on https://help.libreoffice.org/ === REM ======================================================================================================================= Option Compatible Option Explicit ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' ''' SF_PythonHelper (aka Basic) ''' =============== ''' Singleton class implementing the "ScriptForge.Basic" service ''' Implemented as a usual Basic module ''' ''' The "Basic" service must be called ONLY from a PYTHON script ''' Service invocations: Next Python code lines are equivalent: ''' bas = CreateScriptService('ScriptForge.Basic') ''' bas = CreateScriptService('Basic') ''' ''' This service proposes a collection of methods to be executed in a Python context ''' to simulate the exact behaviour of the identical Basic builtin method. ''' Typical example: ''' bas.MsgBox('This has to be displayed in a message box') ''' ''' The service includes also an agnostic "Python Dispatcher" function. ''' It dispatches Python script requests to execute Basic services to the ''' appropriate properties and methods via dynamic call techniques ''' ''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''' REM ================================================================== EXCEPTIONS REM ============================================================ MODULE CONSTANTS REM ===================================================== CONSTRUCTOR/DESTRUCTOR REM ----------------------------------------------------------------------------- Public Function Dispose() As Variant Set Dispose = Nothing End Function ' ScriptForge.SF_PythonHelper Explicit destructor REM ================================================================== PROPERTIES REM ----------------------------------------------------------------------------- Property Get ObjectType As String ''' Only to enable object representation ObjectType = "SF_PythonHelper" End Property ' ScriptForge.SF_PythonHelper.ObjectType REM ----------------------------------------------------------------------------- Property Get ServiceName As String ''' Internal use ServiceName = "ScriptForge.Basic" End Property ' ScriptForge.SF_PythonHelper.ServiceName REM ============================================================== PUBLIC METHODS REM ----------------------------------------------------------------------------- Public Function PyCDate(ByVal DateArg As Variant) As Variant ''' Convenient function to replicate CDate() in Python scripts ''' Args: ''' DateArg: a date as a string or as a double ''' Returns: ''' The converted date as a UNO DateTime structure ''' If the input argument could not be recognized as a date, return the argument unchanged ''' Example: (Python code) ''' a = bas.CDate('2021-02-18') Dim vDate As Variant ' Return value Const cstThisSub = "Basic.CDate" Const cstSubArgs = "datearg" On Local Error GoTo Catch vDate = Null Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: vDate = CDate(DateArg) Finally: If VarType(vDate) = V_DATE Then PyCDate = CDateToUnoDateTime(vDate) Else PyCDate = DateArg SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: On Local Error GoTo 0 GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyCDate REM ----------------------------------------------------------------------------- Public Function PyConvertFromUrl(ByVal FileName As Variant) As String ''' Convenient function to replicate ConvertFromUrl() in Python scripts ''' Args: ''' FileName: a string representing a file in URL format ''' Returns: ''' The same file name in native operating system notation ''' Example: (Python code) ''' a = bas.ConvertFromUrl('file:////boot.sys') Dim sFileName As String ' Return value Const cstThisSub = "Basic.ConvertFromUrl" Const cstSubArgs = "filename" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch sFileName = "" Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: sFileName = ConvertFromUrl(FileName) Finally: PyConvertFromUrl = sFileName SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyConvertFromUrl REM ----------------------------------------------------------------------------- Public Function PyConvertToUrl(ByVal FileName As Variant) As String ''' Convenient function to replicate ConvertToUrl() in Python scripts ''' Args: ''' FileName: a string representing a file in native operating system notation ''' Returns: ''' The same file name in URL format ''' Example: (Python code) ''' a = bas.ConvertToUrl('C:\boot.sys') Dim sFileName As String ' Return value Const cstThisSub = "Basic.ConvertToUrl" Const cstSubArgs = "filename" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch sFileName = "" Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: sFileName = ConvertToUrl(FileName) Finally: PyConvertToUrl = sFileName SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyConvertToUrl REM ----------------------------------------------------------------------------- Public Function PyCreateUnoService(ByVal UnoService As Variant) As Variant ''' Convenient function to replicate CreateUnoService() in Python scripts ''' Args: ''' UnoService: a string representing the service to create ''' Returns: ''' A UNO object ''' Example: (Python code) ''' a = bas.CreateUnoService('com.sun.star.i18n.CharacterClassification') Dim vUno As Variant ' Return value Const cstThisSub = "Basic.CreateUnoService" Const cstSubArgs = "unoservice" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch Set vUno = Nothing Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: Set vUno = CreateUnoService(UnoService) Finally: Set PyCreateUnoService = vUno SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyCreateUnoService REM ----------------------------------------------------------------------------- Public Function PyDateAdd(ByVal Add As Variant _ , ByVal Count As Variant _ , ByVal DateArg As Variant _ ) As Variant ''' Convenient function to replicate DateAdd() in Python scripts ''' Args: ''' Add: The unit to add ''' Count: how many times to add (might be negative) ''' DateArg: a date as a com.sun.star.util.DateTime UNO structure ''' Returns: ''' The new date as a string in iso format ''' Example: (Python code) ''' a = bas.DateAdd('d', 1, bas.Now()) ' Tomorrow Dim vNewDate As Variant ' Return value Dim vDate As Date ' Alias of DateArg Const cstThisSub = "Basic.DateAdd" Const cstSubArgs = "add, count, datearg" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch vNewDate = "" Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: If VarType(DateArg) = V_OBJECT Then vDate = CDateFromUnoDateTime(DateArg) Else vDate = SF_Utils._CStrToDate(DateArg) End If vNewDate = DateAdd(Add, Count, vDate) Finally: If VarType(vNewDate) = V_DATE Then PyDateAdd = CDateToUnoDateTime(vNewDate) Else PyDateAdd = vNewDate SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyDateAdd REM ----------------------------------------------------------------------------- Public Function PyDateDiff(ByVal Add As Variant _ , ByVal Date1 As Variant _ , ByVal Date2 As Variant _ , ByVal WeekStart As Variant _ , ByVal YearStart As Variant _ ) As Long ''' Convenient function to replicate DateDiff() in Python scripts ''' Args: ''' Add: The unit of the date interval ''' Date1, Date2: the two dates to be compared ''' WeekStart: the starting day of a week ''' YearStart: the starting week of a year ''' Returns: ''' The number of intervals expressed in Adds ''' Example: (Python code) ''' a = bas.DateDiff('d', bas.DateAdd('d', 1, bas.Now()), bas.Now()) ' -1 day Dim lDiff As Long ' Return value Dim vDate1 As Date ' Alias of Date1 Dim vDate2 As Date ' Alias of Date2 Const cstThisSub = "Basic.DateDiff" Const cstSubArgs = "add, date1, date2, [weekstart=1], [yearstart=1]" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch lDiff = 0 Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: If VarType(Date1) = V_OBJECT Then vDate1 = CDateFromUnoDateTime(Date1) Else vDate1 = SF_Utils._CStrToDate(Date1) End If If VarType(Date2) = V_OBJECT Then vDate2 = CDateFromUnoDateTime(Date2) Else vDate2 = SF_Utils._CStrToDate(Date2) End If lDiff = DateDiff(Add, vDate1, vDate2, WeekStart, YearStart) Finally: PyDateDiff = lDiff SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyDateDiff REM ----------------------------------------------------------------------------- Public Function PyDatePart(ByVal Add As Variant _ , ByVal DateArg As Variant _ , ByVal WeekStart As Variant _ , ByVal YearStart As Variant _ ) As Long ''' Convenient function to replicate DatePart() in Python scripts ''' Args: ''' Add: The unit of the date interval ''' DateArg: The date from which to extract a part ''' WeekStart: the starting day of a week ''' YearStart: the starting week of a year ''' Returns: ''' The specified part of the date ''' Example: (Python code) ''' a = bas.DatePart('y', bas.Now()) ' day of year Dim lPart As Long ' Return value Dim vDate As Date ' Alias of DateArg Const cstThisSub = "Basic.DatePart" Const cstSubArgs = "add, datearg, [weekstart=1], [yearstart=1]" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch lPart = 0 Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: If VarType(DateArg) = V_OBJECT Then vDate = CDateFromUnoDateTime(DateArg) Else vDate = SF_Utils._CStrToDate(DateArg) End If lPart = DatePart(Add, vDate, WeekStart, YearStart) Finally: PyDatePart = lPart SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyDatePart REM ----------------------------------------------------------------------------- Public Function PyDateValue(ByVal DateArg As Variant) As Variant ''' Convenient function to replicate DateValue() in Python scripts ''' Args: ''' DateArg: a date as a string ''' Returns: ''' The converted date as a UNO DateTime structure ''' Example: (Python code) ''' a = bas.DateValue('2021-02-18') Dim vDate As Variant ' Return value Const cstThisSub = "Basic.DateValue" Const cstSubArgs = "datearg" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch vDate = "" Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: vDate = DateValue(DateArg) Finally: If VarType(vDate) = V_DATE Then PyDateValue = CDateToUnoDateTime(vDate) Else PyDateValue = vDate SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyDateValue REM ----------------------------------------------------------------------------- Public Function PyFormat(ByVal Value As Variant _ , ByVal Pattern As Variant _ ) As String ''' Convenient function to replicate Format() in Python scripts ''' Args: ''' Value: a date or a number ''' Pattern: the format to apply ''' Returns: ''' The formatted value ''' Example: (Python code) ''' MsgBox bas.Format(6328.2, '##,##0.00') Dim sFormat As String ' Return value Dim vValue As Variant ' Alias of Value Const cstThisSub = "Basic.Format" Const cstSubArgs = "value, pattern" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch sFormat = "" Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: If VarType(Value) = V_OBJECT Then vValue = CDateFromUnoDateTime(Value) ELse vValue = Value If IsEmpty(Pattern) Or Len(Pattern) = 0 Then sFormat = Str(vValue) Else sFormat = Format(vValue, Pattern) Finally: PyFormat = sFormat SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyFormat REM ----------------------------------------------------------------------------- Public Function PyGetGuiType() As Integer ''' Convenient function to replicate GetGuiType() in Python scripts ''' Args: ''' Returns: ''' The GetGuiType value ''' Example: (Python code) ''' MsgBox bas.GetGuiType() Const cstThisSub = "Basic.GetGuiType" Const cstSubArgs = "" Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: PyGetGuiType = GetGuiType() Finally: SF_Utils._ExitFunction(cstThisSub) Exit Function End Function ' ScriptForge.SF_PythonHelper.PyGetGuiType REM ----------------------------------------------------------------------------- Public Function PyGetSystemTicks() As Long ''' Convenient function to replicate GetSystemTicks() in Python scripts ''' Args: ''' Returns: ''' The GetSystemTicks value ''' Example: (Python code) ''' MsgBox bas.GetSystemTicks() Const cstThisSub = "Basic.GetSystemTicks" Const cstSubArgs = "" Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: PyGetSystemTicks = GetSystemTicks() Finally: SF_Utils._ExitFunction(cstThisSub) Exit Function End Function ' ScriptForge.SF_PythonHelper.PyGetSystemTicks REM ----------------------------------------------------------------------------- Public Function PyGlobalScope(ByVal Library As Variant) As Object ''' Convenient function to replicate GlobalScope() in Python scripts ''' Args: ''' Library: "Basic" or "Dialog" ''' Returns: ''' The GlobalScope value ''' Example: (Python code) ''' MsgBox bas.GlobalScope.BasicLibraries() Const cstThisSub = "Basic.GlobalScope.BasicLibraries" ' or DialogLibraries Const cstSubArgs = "" Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: Select Case Library Case "Basic" PyGlobalScope = GlobalScope.BasicLibraries() Case "Dialog" PyGlobalScope = GlobalScope.DialogLibraries() Case Else End Select Finally: SF_Utils._ExitFunction(cstThisSub) Exit Function End Function ' ScriptForge.SF_PythonHelper.PyGlobalScope REM ----------------------------------------------------------------------------- Public Function PyInputBox(ByVal Msg As Variant _ , ByVal Title As Variant _ , ByVal Default As Variant _ , Optional ByVal XPosTwips As Variant _ , Optional ByVal YPosTwips As Variant _ ) As String ''' Convenient function to replicate InputBox() in Python scripts ''' Args: ''' Msg: String expression displayed as the message in the dialog box ''' Title: String expression displayed in the title bar of the dialog box ''' Default: String expression displayed in the text box as default if no other input is given ''' XPosTwips: Integer expression that specifies the horizontal position of the dialog ''' YPosTwips: Integer expression that specifies the vertical position of the dialog ''' If XPosTwips and YPosTwips are omitted, the dialog is centered on the screen ''' The position is specified in twips. ''' Returns: ''' The entered value or "" if the user pressed the Cancel button ''' Example: (Python code) ''' a = bas.InputBox ('Please enter a phrase:', 'Dear User') Dim sInput As String ' Return value Const cstThisSub = "Basic.InputBox" Const cstSubArgs = "msg, [title=''], [default=''], [xpostwips], [ypostwips]" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch sInput = "" Check: If IsMissing(YPosTwips) Then YPosTwips = 1 SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: If IsMissing(XPosTwips) Then sInput = InputBox(Msg, Title, Default) Else sInput = InputBox(Msg, Title, Default, XPosTwips, YPosTwips) End If Finally: PyInputBox = sInput SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyInputBox REM ----------------------------------------------------------------------------- Public Function PyMsgBox(ByVal Text As Variant _ , ByVal DialogType As Variant _ , ByVal DialogTitle As Variant _ ) As Integer ''' Convenient function to replicate MsgBox() in Python scripts ''' Args: ''' Text: String expression displayed as a message in the dialog box ''' DialogType: Any integer expression that defines the number and type of buttons or icons displayed ''' DialogTitle: String expression displayed in the title bar of the dialog ''' Returns: ''' The pressed button ''' Example: (Python code) ''' a = bas.MsgBox ('Please press a button:', bas.MB_EXCLAMATION, 'Dear User') Dim iMsg As Integer ' Return value Const cstThisSub = "Basic.MsgBox" Const cstSubArgs = "text, [dialogtype=0], [dialogtitle]" If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch iMsg = -1 Check: SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Try: iMsg = MsgBox(Text, DialogType, DialogTitle) Finally: PyMsgBox = iMsg SF_Utils._ExitFunction(cstThisSub) Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper.PyMsgBox REM ============================================================= PRIVATE METHODS REM ----------------------------------------------------------------------------- Public Function _PythonDispatcher(ByRef BasicObject As Variant _ , ByVal CallType As Variant _ , ByVal Script As Variant _ , ParamArray Args() As Variant _ ) As Variant ''' Called from Python only ''' The method calls the method Script associated with the BasicObject class or module ''' with the given arguments ''' The invocation of the method can be a Property Get, Property Let or a usual call ''' NB: arguments and return values must not be 2D arrays ''' The implementation intends to be as AGNOSTIC as possible in terms of objects nature and methods called ''' Args: ''' BasicObject: a module or a class instance - May also be the reserved string: "SF_Services" ''' CallType: one of the constants applicable to a CallByName statement + optional protocol flags ''' Script: the name of the method or property ''' Args: the arguments to pass to the method. Input arguments can contain symbolic constants for Null, Missing, etc. ''' Returns: ''' A 1D array: ''' [0] The returned value - scalar, object or 1D array ''' [1] The VarType() of the returned value ''' Null, Empty and Nothing have different vartypes but return all None to Python ''' Additionally, when array: ''' [2] Number of dimensions in Basic ''' Additionally, when Basic object: ''' [2] Module (1), Class instance (2) or UNO (3) ''' [3] The object's ObjectType ''' [4] The object's service name ''' [5] The object's name ''' When an error occurs Python receives None as a scalar. This determines the occurrence of a failure Dim vReturn As Variant ' The value returned by the invoked property or method Dim vReturnArray As Variant ' Return value Dim vBasicObject As Variant ' Alias of BasicObject to avoid "Object reference not set" error Dim iNbArgs As Integer ' Number of valid input arguments Dim vArg As Variant ' Alias for a single argument Dim vArgs() As Variant ' Alias for Args() Dim sScript As String ' Argument of ExecuteBasicScript() Dim vParams As Variant ' Array of arguments to pass to a ParamArray Dim sObjectType As String ' Alias of object.ObjectType Dim sServiceName As String ' Alias of BasicObject.ServiceName Dim bBasicClass As Boolean ' True when BasicObject is a class Dim sLibrary As String ' Library where the object belongs to Dim bUno As Boolean ' Return value is a UNO object Dim oObjDesc As Object ' _ObjectDescriptor type Dim iDims As Integer ' # of dims of vReturn Dim sess As Object : Set sess = ScriptForge.SF_Session Dim i As Long, j As Long ' Conventional special input or output values Const cstNoArgs = "+++NOARGS+++", cstSymEmpty = "+++EMPTY+++", cstSymNull = "+++NULL+++", cstSymMissing = "+++MISSING+++" ' https://support.office.com/en-us/article/CallByName-fonction-49ce9475-c315-4f13-8d35-e98cfe98729a ' Determines the CallType Const vbGet = 2, vbLet = 4, vbMethod = 1, vbSet = 8 ' Protocol flags Const cstDateArg = 64 ' May contain a date argument Const cstDateRet = 128 ' Return value can be a date Const cstUno = 256 ' Return value can be a UNO object Const cstArgArray = 512 ' Any argument can be a 2D array Const cstRetArray = 1024 ' Return value can be an array Const cstObject = 2048 ' 1st argument is a Basic object when numeric Const cstHardCode = 4096 ' Method must not be executed with CallByName() ' Object nature in returned array Const objMODULE = 1, objCLASS = 2, objUNO = 3 Check: If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch _PythonDispatcher = Null ' Ignore Null basic objects (Null = Null or Nothing) If IsNull(BasicObject) Or IsEmpty(BasicObject) Then GoTo Catch ' Reinterpret arguments one by one into vArgs, convert UNO date/times and conventional NoArgs/Empty/Null/Missing values iNbArgs = -1 vArgs = Array() If UBound(Args) >= 0 Then For i = 0 To UBound(Args) vArg = Args(i) ' Are there arguments ? If i = 0 And VarType(vArg) = V_STRING Then If vArg = cstNoArgs Then Exit For End If ' Is 1st argument a reference to a Basic object ? If i = 0 And (( CallType And cstObject ) = cstObject) And SF_Utils._VarTypeExt(vArg) = V_NUMERIC Then If vArg < 0 Or Not IsArray(_SF_.PythonStorage) Then GoTo Catch If vArg > UBound(_SF_.PythonStorage) Then GoTo Catch vArg = _SF_.PythonStorage(vArg) ' Is argument a symbolic constant for Null, Empty, ... , or a date? ElseIf VarType(vArg) = V_STRING Then If Len(vArg) = 0 Then ElseIf vArg = cstSymEmpty Then vArg = Empty ElseIf vArg = cstSymNull Then vArg = Null ElseIf vArg = cstSymMissing Then Exit For ' Next arguments must be missing also End If ElseIf VarType(vArg) = V_OBJECT Then If ( CallType And cstDateArg ) = cstDateArg Then vArg = CDateFromUnoDateTime(vArg) End If iNbArgs = iNbArgs + 1 ReDim Preserve vArgs(iNbArgs) vArgs(iNbArgs) = vArg Next i End If Try: ' Dispatching strategy: based on next constraints ' (1) Bug https://bugs.documentfoundation.org/show_bug.cgi?id=138155 ' The CallByName function fails when returning an array ' (2) Python has tuples and tuple of tuples, not 2D arrays ' (3) Passing 2D arrays through a script provider always transform it into a sequence of sequences ' (4) The CallByName function takes exclusive control on the targeted object up to its exit ' 1. Methods in usual modules are called by ExecuteBasicScript() except if they use a ParamArray ' 2. Properties in any service are got and set with obj.GetProperty/SetProperty(...) ' 3. Methods in class modules are invoked with CallByName ' 4. Methods in class modules using a 2D array or returning arrays, or methods using ParamArray, ''' are hardcoded as exceptions or are not implemented ' 5. Due to constraint (4), a predefined list of method calls must be hardcoded to avoid blocking use of CallByName ' The concerned methods are flagged with cstHardCode With _SF_ ' Initialize Python persistent storage at 1st call If IsEmpty(.PythonStorage) Then ._InitPythonStorage() ' Reset any error ._Stackreset() ' Set Python trigger to manage signatures in error messages .TriggeredByPython = True End With Select case VarType(BasicObject) Case V_STRING ' Special entry for CreateScriptService() vBasicObject = BasicObject If vBasicObject = "SF_Services" Then If UBound(vArgs) = 0 Then vParams = Array() Else vParams = SF_Array.Slice(vArgs, 1) Select Case UBound(vParams) Case -1 : vReturn = SF_Services.CreateScriptService(vArgs(0)) Case 0 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0)) Case 1 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0), vParams(1)) Case 2 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0), vParams(1), vParams(2)) Case 3 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0), vParams(1), vParams(2), vParams(3)) Case 4 : vReturn = SF_Services.CreateScriptService(vArgs(0), vParams(0), vParams(1), vParams(2), vParams(3), vParams(4)) End Select End If If VarType(vReturn) = V_OBJECT And Not IsNull(vReturn) Then vBasicObject = vReturn sObjectType = vBasicObject.ObjectType bBasicClass = ( Left(sObjectType, 3) <> "SF_" ) End If ' Implement dispatching strategy Case V_INTEGER If BasicObject < 0 Or Not IsArray(_SF_.PythonStorage) Then GoTo Catch If BasicObject > UBound(_SF_.PythonStorage) Then GoTo Catch vBasicObject = _SF_.PythonStorage(BasicObject) sObjectType = vBasicObject.ObjectType sServiceName = vBasicObject.ServiceName ' Basic modules have type = "SF_*" bBasicClass = ( Left(sObjectType, 3) <> "SF_" ) sLibrary = Split(sServiceName, ".")(0) ' Methods in standard modules returning/passing a date are hardcoded as exceptions If Not bBasicClass And ((CallType And vbMethod) = vbMethod) _ And (((CallType And cstDateRet) = cstDateRet) Or ((CallType And cstDateArg) = cstDateArg)) Then Select Case sServiceName Case "ScriptForge.FileSystem" If Script = "GetFileModified" Then vReturn = SF_FileSystem.GetFileModified(vArgs(0)) Case "ScriptForge.Region" Select Case Script Case "DSTOffset" : vReturn = SF_Region.DSTOffset(vArgs(0), vArgs(1), vArgs(2)) Case "LocalDateTime" : vReturn = SF_Region.LocalDateTime(vArgs(0), vArgs(1), vArgs(2)) Case "UTCDateTime" : vReturn = SF_Region.UTCDateTime(vArgs(0), vArgs(1), vArgs(2)) Case "UTCNow" : vReturn = SF_Region.UTCNow(vArgs(0), vArgs(1)) Case Else End Select End Select ' Methods in usual modules using a 2D array or returning arrays are hardcoded as exceptions ElseIf Not bBasicClass And _ (((CallType And vbMethod) + (CallType And cstArgArray)) = vbMethod + cstArgArray Or _ ((CallType And vbMethod) + (CallType And cstRetArray)) = vbMethod + cstRetArray) Then ' Not service related If Script = "Methods" Then vReturn = vBasicObject.Methods() ElseIf Script = "Properties" Then vReturn = vBasicObject.Properties() Else Select Case sServiceName Case "ScriptForge.Array" If Script = "ImportFromCSVFile" Then vReturn = SF_Array.ImportFromCSVFile(vArgs(0), vArgs(1), vArgs(2), True) End Select End If ' Methods in usual modules are called by ExecuteBasicScript() except if they use a ParamArray ElseIf Not bBasicClass And (CallType And vbMethod) = vbMethod Then sScript = sLibrary & "." & sObjectType & "." & Script ' Force validation in targeted function, not in ExecuteBasicScript() _SF_.StackLevel = -1 Select Case UBound(vArgs) Case -1 : vReturn = sess.ExecuteBasicScript(, sScript) Case 0 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0)) Case 1 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1)) Case 2 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2)) Case 3 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3)) Case 4 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4)) Case 5 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5)) Case 6 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6)) Case 7 : vReturn = sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7)) End Select _SF_.StackLevel = 0 ' Properties in any service are got and set with obj.GetProperty/SetProperty(...) ElseIf (CallType And vbGet) = vbGet Then ' In some cases (Calc ...) GetProperty may have an argument If UBound(vArgs) < 0 Then vReturn = vBasicObject.GetProperty(Script) Else vReturn = vBasicObject.GetProperty(Script, vArgs(0)) ElseIf (CallType And vbLet) = vbLet Then vReturn = vBasicObject.SetProperty(Script, vArgs(0)) ' Methods in class modules using a 2D array or returning arrays are hardcoded as exceptions. Bug #138155 ElseIf ((CallType And vbMethod) + (CallType And cstArgArray)) = vbMethod + cstArgArray Or _ ((CallType And vbMethod) + (CallType And cstRetArray)) = vbMethod + cstRetArray Then If Script = "Methods" Then vReturn = vBasicObject.Methods() ElseIf Script = "Properties" Then vReturn = vBasicObject.Properties() Else Select Case sServiceName Case "SFDatabases.Database" If Script = "GetRows" Then vReturn = vBasicObject.GetRows(vArgs(0), vArgs(1), vArgs(2), vArgs(3)) Case "SFDialogs.Dialog" If Script = "Controls" Then vReturn = vBasicObject.Controls(vArgs(0)) Case "SFDialogs.DialogControl" If Script = "SetTableData" Then vReturn = vBasicObject.SetTableData(vArgs(0), vArgs(1), vArgs(2)) Case "SFDocuments.Document" If Script = "Forms" Then vReturn = vBasicObject.Forms(vArgs(0)) Case "SFDocuments.Base" Select Case Script Case "FormDocuments" : vReturn = vBasicObject.FormDocuments() Case "Forms" : vReturn = vBasicObject.Forms(vArgs(0), vArgs(1)) End Select Case "SFDocuments.Calc" Select Case Script Case "Charts" : vReturn = vBasicObject.Charts(vArgs(0), vArgs(1)) Case "Forms" : vReturn = vBasicObject.Forms(vArgs(0), vArgs(1)) Case "GetFormula" : vReturn = vBasicObject.GetFormula(vArgs(0)) Case "GetValue" : vReturn = vBasicObject.GetValue(vArgs(0)) Case "SetArray" : vReturn = vBasicObject.SetArray(vArgs(0), vArgs(1)) Case "SetFormula" : vReturn = vBasicObject.SetFormula(vArgs(0), vArgs(1)) Case "SetValue" : vReturn = vBasicObject.SetValue(vArgs(0), vArgs(1)) End Select Case "SFDocuments.Form" Select Case Script Case "Controls" : vReturn = vBasicObject.Controls(vArgs(0)) Case "Subforms" : vReturn = vBasicObject.Subforms(vArgs(0)) End Select Case "SFDocuments.FormControl" If Script = "Controls" Then vReturn = vBasicObject.Controls(vArgs(0)) End Select End If ' Methods in class modules may better not be executed with CallByName() ElseIf bBasicClass And ((CallType And vbMethod) + (CallType And cstHardCode)) = vbMethod + cstHardCode Then Select Case sServiceName Case "SFDialogs.Dialog" Select Case Script Case "Activate" : vReturn = vBasicObject.Activate() Case "Center" If UBound(vArgs) < 0 Then vReturn = vBasicObject.Center() Else vReturn = vBasicObject.Center(vArgs(0)) Case "EndExecute" : vReturn = vBasicObject.EndExecute(vArgs(0)) Case "Execute" : vReturn = vBasicObject.Execute(vArgs(0)) Case "Resize" : vReturn = vBasicObject.Resize(vArgs(0), vArgs(1), vArgs(2), vArgs(3)) End Select End Select ' Methods in class modules are invoked with CallByName ElseIf bBasicClass And ((CallType And vbMethod) = vbMethod) Then Select Case UBound(vArgs) ' Dirty alternatives to process usual and ParamArray cases ' But, up to ... how many ? ' - The OFFSETADDRESSERROR has 12 arguments ' - The ".uno:DataSort" command may have 14 property name-value pairs Case -1 : vReturn = CallByName(vBasicObject, Script, vbMethod) Case 0 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0)) Case 1 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1)) Case 2 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2)) Case 3 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3)) Case 4 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4)) Case 5 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5)) Case 6 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6)) Case 7 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7)) Case 8 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8)) Case 9 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9)) Case 10 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10)) Case 11 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11)) Case 12, 13 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12)) Case 14, 15 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14)) Case 16, 17 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16)) Case 18, 19 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18)) Case 20, 21 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ , vArgs(19), vArgs(20)) Case 22, 23 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ , vArgs(19), vArgs(20), vArgs(21), vArgs(22)) Case 24, 25 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ , vArgs(19), vArgs(20), vArgs(21), vArgs(22), vArgs(23), vArgs(24)) Case 26, 27 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ , vArgs(19), vArgs(20), vArgs(21), vArgs(22), vArgs(23), vArgs(24), vArgs(25), vArgs(26)) Case >= 28 : vReturn = CallByName(vBasicObject, Script, vbMethod, vArgs(0), vArgs(1), vArgs(2), vArgs(3), vArgs(4), vArgs(5), vArgs(6), vArgs(7) _ , vArgs(8), vArgs(9), vArgs(10), vArgs(11), vArgs(12), vArgs(13), vArgs(14), vArgs(15), vArgs(16), vArgs(17), vArgs(18) _ , vArgs(19), vArgs(20), vArgs(21), vArgs(22), vArgs(23), vArgs(24), vArgs(25), vArgs(26), vArgs(27), vArgs(28)) End Select End If ' Post processing If Script = "Dispose" Then ' Special case: Dispose() must update the cache for class objects created in Python scripts Set _SF_.PythonStorage(BasicObject) = Nothing End If Case Else End Select ' Format the returned array vReturnArray = Array() ' Distinguish: Basic object ' UNO object ' Array ' Scalar If IsArray(vReturn) Then ReDim vReturnArray(0 To 2) iDims = SF_Array.CountDims(vReturn) ' Replace dates by UNO format If iDims = 1 Then For i = LBound(vReturn) To UBound(vReturn) If VarType(vReturn(i)) = V_DATE Then vReturn(i) = CDateToUnoDateTime(vReturn(i)) Next i ElseIf iDims = 2 Then For i = LBound(vReturn, 1) To UBound(vReturn, 1) For j = LBound(vReturn, 2) To UBound(vReturn, 2) If VarType(vReturn(i, j)) = V_DATE Then vReturn(i, j) = CDateToUnoDateTime(vReturn(i, j)) Next j Next i End If vReturnArray(0) = vReturn ' 2D arrays are flattened by the script provider when returning to Python vReturnArray(1) = VarType(vReturn) vReturnArray(2) = iDims ElseIf VarType(vReturn) = V_OBJECT And Not IsNull(vReturn) Then ' Uno or not Uno ? bUno = False If (CallType And cstUno) = cstUno Then ' UNO considered only when pre-announced in CallType Set oObjDesc = SF_Utils._VarTypeObj(vReturn) bUno = ( oObjDesc.iVarType = V_UNOOBJECT ) End If If bUno Then ReDim vReturnArray(0 To 2) Set vReturnArray(0) = vReturn Else ReDim vReturnArray(0 To 5) vReturnArray(0) = _SF_._AddToPythonSTorage(vReturn) End If vReturnArray(1) = V_OBJECT vReturnArray(2) = Iif(bUno, objUNO, Iif(bBasicClass, objCLASS, objMODULE)) If Not bUno Then vReturnArray(3) = vReturn.ObjectType vReturnArray(4) = vReturn.ServiceName vReturnArray(5) = "" If vReturn.ObjectType <> "SF_CalcReference" Then ' Calc references are implemented as a Type ... End Type data structure If SF_Array.Contains(vReturn.Properties(), "Name", SortOrder := "ASC") Then vReturnArray(5) = vReturn.Name End If End If Else ' Scalar or Nothing ReDim vReturnArray(0 To 1) If VarType(vReturn) = V_DATE Then vReturnArray(0) = CDateToUnoDateTime(vReturn) Else vReturnArray(0) = vReturn vReturnArray(1) = VarType(vReturn) End If _PythonDispatcher = vReturnArray Finally: _SF_.TriggeredByPython = False ' Reset normal state Exit Function Catch: GoTo Finally End Function ' ScriptForge.SF_PythonHelper._PythonDispatcher REM ----------------------------------------------------------------------------- Private Function _Repr() As String ''' Convert the Basic instance to a readable string, typically for debugging purposes (DebugPrint ...) ''' Args: ''' Return: ''' "[PythonHelper]" _Repr = "[PythonHelper]" End Function ' ScriptForge.SF_PythonHelper._Repr REM ================================================= END OF SCRIPTFORGE.SF_PythonHelper