office-gobmx/wizards/source/scriptforge/SF_Services.xba
Jean-Pierre Ledure 0b6afed3b8 ScriptForge Fix tdf#163219 With blocks
Reference:
  https://bugs.documentfoundation.org/show_bug.cgi?id=163219#c7

Everywhere the With block variable is defined
or redefined inside the With block, the Basic
code has been reviewed.

Found in
  SFDatabases/SF_Database.xba/SetTransactionMode()
  SFDocuments/SF_Document.xba/Styles()
  ScriptForge/SF_Dictionary.xba/ImportFromPropertyValues()
  ScriptForge/SF_UI.xba/SetStatusBar()
  ScriptForge/SF_UI.xba/ShowProgressBar()

Most changes consist in isolating the With block
variable before the With block itself.

No functional change.

This solution is a workaround for the bug.
IT DOES NOT SOLVE THE ROOT CAUSE.

Change-Id: I48af29d3d9c8b1e36ef5a85c8cfe28f9639ae483
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/174560
Reviewed-by: Jean-Pierre Ledure <jp@ledure.be>
Tested-by: Jenkins
2024-10-07 15:04:20 +02:00

655 lines
No EOL
29 KiB
XML

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_Services" script:language="StarBasic" script:moduleType="normal">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
&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
&apos;&apos;&apos; SF_Services
&apos;&apos;&apos; ===========
&apos;&apos;&apos; Singleton class implementing the &quot;ScriptForge.Services&quot; service
&apos;&apos;&apos; Implemented as a usual Basic module
&apos;&apos;&apos; The ScriptForge framework includes
&apos;&apos;&apos; the current ScriptForge library
&apos;&apos;&apos; a number of &quot;associated&quot; libraries
&apos;&apos;&apos; any user/contributor extension wanting to fit into the framework
&apos;&apos;&apos; The methods in this module constitute the kernel of the ScriptForge framework
&apos;&apos;&apos; - RegisterScriptServices
&apos;&apos;&apos; Register for a library the list of services it implements
&apos;&apos;&apos; Each library in the framework must implement its own RegisterScriptServices method
&apos;&apos;&apos; This method consists in a series of invocations of next 2 methods
&apos;&apos;&apos; - RegisterService
&apos;&apos;&apos; Register a single service
&apos;&apos;&apos; - RegisterEventManager
&apos;&apos;&apos; Register a single event manager
&apos;&apos;&apos; - CreateScriptService
&apos;&apos;&apos; Called by user scripts to get an object giving access to a service or to the event manager
&apos;&apos;&apos;
&apos;&apos;&apos; Detailed user documentation:
&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_services.html?DbPAR=BASIC
&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
REM ================================================================== EXCEPTIONS
Const UNKNOWNSERVICEERROR = &quot;UNKNOWNSERVICEERROR&quot; &apos; Service not found within the registered services of the given library
Const SERVICESNOTLOADEDERROR = &quot;SERVICESNOTLOADEDERROR&quot; &apos; Failure during the registering of the services of the given library
Const UNKNOWNFILEERROR = &quot;UNKNOWNFILEERROR&quot; &apos; Source file does not exist
REM ============================================================== PUBLIC MEMBERS
&apos; Defines an entry in the services dictionary
Type _Service
ServiceName As String
ServiceType As Integer
&apos; 0 Undefined
&apos; 1 Basic module
&apos; 2 Method reference as a string
ServiceReference As Object
ServiceMethod As String
EventManager As Boolean &apos; True if registered item is an event manager
End Type
Private vServicesArray As Variant &apos; List of services registered by a library
REM ============================================================== PUBLIC METHODS
REM -----------------------------------------------------------------------------
Public Function CreateScriptService(Optional ByRef Service As Variant _
, ParamArray pvArgs As Variant _
) As Variant
&apos;&apos;&apos; Create access to the services of a library for the benefit of a user script
&apos;&apos;&apos; A service is to understand either:
&apos;&apos;&apos; as a set of methods gathered in a Basic standard module
&apos;&apos;&apos; or a set of methods and properties gathered in a Basic class module
&apos;&apos;&apos; Args:
&apos;&apos;&apos; Service: the name of the service in 2 parts &quot;library.service&quot;
&apos;&apos;&apos; The library is a Basic library that must exist in the GlobalScope
&apos;&apos;&apos; (default = &quot;ScriptForge&quot;)
&apos;&apos;&apos; The service is one of the services registered by the library
&apos;&apos;&apos; thru the RegisterScriptServices() routine
&apos;&apos;&apos; pvArgs: a set of arguments passed to the constructor of the service
&apos;&apos;&apos; This is only possible if the service refers to a Basic class module
&apos;&apos;&apos; Returns
&apos;&apos;&apos; The object containing either the reference of the Basic module
&apos;&apos;&apos; or of the Basic class instance
&apos;&apos;&apos; Both are Basic objects
&apos;&apos;&apos; Returns Nothing if an error occurred.
&apos;&apos;&apos; ==&gt;&gt; NOTE: The error can be within the user script creating the new class instance
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; SERVICESNOTLOADEDERROR RegisterScriptService probable failure
&apos;&apos;&apos; UNKNOWNSERVICEERROR Service not found
&apos;&apos;&apos; Examples
&apos;&apos;&apos; CreateScriptService(&quot;Array&quot;)
&apos;&apos;&apos; =&gt; Refers to ScriptForge.Array or SF_Array
&apos;&apos;&apos; CreateScriptService(&quot;ScriptForge.Dictionary&quot;)
&apos;&apos;&apos; =&gt; Returns a new empty dictionary; &quot;ScriptForge.&quot; is optional
&apos;&apos;&apos; CreateScriptService(&quot;SFDocuments.Calc&quot;)
&apos;&apos;&apos; =&gt; Refers to the Calc service, implemented in the SFDocuments library
&apos;&apos;&apos; CreateScriptService(&quot;Dialog&quot;, dlgName)
&apos;&apos;&apos; =&gt; Returns a Dialog instance referring to the dlgName dialog
&apos;&apos;&apos; CreateScriptService(&quot;SFDocuments.Event&quot;, oEvent)
&apos;&apos;&apos; =&gt; Refers to the Document service instance, implemented in the SFDocuments library, having triggered the event
Dim vScriptService As Variant &apos; Return value
Dim vServiceItem As Variant &apos; A single service (see _Service type definition)
Dim vServicesList As Variant &apos; Output of RegisterScriptServices
Dim vSplit As Variant &apos; Array to split argument in
Dim sLibrary As String &apos; Library part of the argument
Dim sService As String &apos; Service part of the argument
Dim vLibrary As Variant &apos; Dictionary of libraries
Dim vService As Variant &apos; An individual service object
Const cstThisSub = &quot;SF_Services.CreateScriptService&quot;
Const cstSubArgs = &quot;Service, arg0[, arg1] ...&quot;
&apos; Save Err, Erl, .. values before any On Error ... statement
SF_Exception._CaptureSystemError()
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Set vScriptService = Nothing
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(Service, &quot;Service&quot;, V_STRING) Then GoTo Catch
If Len(Service) = 0 Then GoTo CatchNotFound
End If
Try:
&apos; Initialize the list of services when CreateScriptService called for the very 1st time
If IsEmpty(_SF_.ServicesList) Then _SF_.ServicesList = SF_Services._NewDictionary()
&apos; Simple parsing of argument
vSplit = Split(Service, &quot;.&quot;)
If UBound(vSplit) &gt; 1 Then GoTo CatchNotFound
If UBound(vSplit) = 0 Then
sLibrary = &quot;ScriptForge&quot; &apos; Yes, the default value !
sService = vSplit(0)
&apos; Accept other default values for associated libraries
Select Case LCase(sService)
Case &quot;document&quot;, &quot;calc&quot;, &quot;writer&quot;, &quot;base&quot;, &quot;formdocument&quot;, &quot;documentevent&quot;, &quot;formevent&quot;
sLibrary = &quot;SFDocuments&quot;
Case &quot;dialog&quot;, &quot;dialogevent&quot;, &quot;newdialog&quot;
sLibrary = &quot;SFDialogs&quot;
Case &quot;database&quot;, &quot;datasheet&quot; : sLibrary = &quot;SFDatabases&quot;
Case &quot;unittest&quot; : sLibrary = &quot;SFUnitTests&quot;
Case &quot;contextmenu&quot;, &quot;menu&quot;, &quot;popupmenu&quot;, &quot;toolbar&quot;, &quot;toolbarbutton&quot;
sLibrary = &quot;SFWidgets&quot;
Case Else
End Select
Else
sLibrary = vSplit(0)
sService = vSplit(1)
End If
With _SF_.ServicesList
&apos; Load the set of services from the library, if not yet done
If Not .Exists(sLibrary) Then
If Not SF_Services._LoadLibraryServices(sLibrary) Then GoTo CatchNotLoaded
End If
&apos; Find and return the requested service
vServicesList = .Item(sLibrary)
If Not vServicesList.Exists(sService) Then GoTo CatchNotFound
vServiceItem = vServicesList.Item(sService)
Select Case vServiceItem.ServiceType
Case 1 &apos; Basic module
vScriptService = vServiceItem.ServiceReference
Case 2 &apos; Method to call
If sLibrary = &quot;ScriptForge&quot; Then &apos; Direct call
Select Case UCase(sService)
Case &quot;DICTIONARY&quot; : vScriptService = SF_Services._NewDictionary()
Case &quot;L10N&quot; : vScriptService = SF_Services._NewL10N(pvArgs)
Case &quot;TIMER&quot; : vScriptService = SF_Services._NewTimer(pvArgs)
Case Else
End Select
Else &apos; Call via script provider
Set vService = SF_Session._GetScript(&quot;Basic&quot;, SF_Session.SCRIPTISAPPLICATION, vServiceItem.ServiceMethod)
vScriptService = vService.Invoke(Array(pvArgs()), Array(), Array())
End If
Case Else
End Select
End With
Finally:
CreateScriptService = vScriptService
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
CatchNotFound:
SF_Exception.RaiseFatal(UNKNOWNSERVICEERROR, &quot;Service&quot;, Service, sLibrary, sService)
GoTo Finally
CatchNotLoaded:
SF_Exception.RaiseFatal(SERVICESNOTLOADEDERROR, &quot;Service&quot;, Service, sLibrary)
GoTo Finally
End Function &apos; ScriptForge.SF_Services.CreateScriptService
REM -----------------------------------------------------------------------------
Public Function RegisterEventManager(Optional ByVal ServiceName As Variant _
, Optional ByRef ServiceReference As Variant _
) As Boolean
&apos;&apos;&apos; Register into ScriptForge a new event entry for the library
&apos;&apos;&apos; from which this method is called
&apos;&apos;&apos; MUST BE CALLED ONLY from a specific RegisterScriptServices() method
&apos;&apos;&apos; Usually the method should be called only once by library
&apos;&apos;&apos; Args:
&apos;&apos;&apos; ServiceName: the name of the service as a string. It the service exists
&apos;&apos;&apos; already for the library the method overwrites the existing entry
&apos;&apos;&apos; ServiceReference: the function which will identify the source of the triggered event
&apos;&apos;&apos; something like: &quot;libraryname.modulename.function&quot;
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; True if successful
&apos;&apos;&apos; Example:
&apos;&apos;&apos; &apos; Code snippet stored in a module contained in the SFDocuments library
&apos;&apos;&apos; Sub RegisterScriptServices()
&apos;&apos;&apos; &apos; Register the events manager of the library
&apos;&apos;&apos; RegisterEventManager(&quot;DocumentEvent&quot;, &quot;SFDocuments.SF_Register._EventManager&quot;)
&apos;&apos;&apos; End Sub
&apos;&apos;&apos; &apos; Code snippet stored in a user script
&apos;&apos;&apos; Sub Trigger(poEvent As Object) &apos; Triggered by a DOCUMENTEVENT event
&apos;&apos;&apos; Dim myDoc As Object
&apos;&apos;&apos; &apos; To get the document concerned by the event:
&apos;&apos;&apos; Set myDoc = CreateScriptService(&quot;SFDocuments.DocumentEvent&quot;, poEvent)
&apos;&apos;&apos; End Sub
Dim bRegister As Boolean &apos; Return value
Const cstThisSub = &quot;SF_Services.RegisterEventManager&quot;
Const cstSubArgs = &quot;ServiceName, ServiceReference&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
bRegister = False
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(ServiceName, &quot;ServiceName&quot;, V_STRING) Then GoTo Finally
If Not SF_Utils._Validate(ServiceReference, &quot;ServiceReference&quot;,V_STRING) Then GoTo Finally
End If
Try:
bRegister = _AddToServicesArray(ServiceName, ServiceReference, True)
Finally:
RegisterEventManager = bRegister
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Services.RegisterEventManager
REM -----------------------------------------------------------------------------
Public Function RegisterService(Optional ByVal ServiceName As Variant _
, Optional ByRef ServiceReference As Variant _
) As Boolean
&apos;&apos;&apos; Register into ScriptForge a new service entry for the library
&apos;&apos;&apos; from which this method is called
&apos;&apos;&apos; MUST BE CALLED ONLY from a specific RegisterScriptServices() method
&apos;&apos;&apos; Args:
&apos;&apos;&apos; ServiceName: the name of the service as a string. It the service exists
&apos;&apos;&apos; already for the library the method overwrites the existing entry
&apos;&apos;&apos; ServiceReference: either
&apos;&apos;&apos; - the Basic module that implements the methods of the service
&apos;&apos;&apos; something like: GlobalScope.Library.Module
&apos;&apos;&apos; - an instance of the class implementing the methods and properties of the service
&apos;&apos;&apos; something like: &quot;libraryname.modulename.function&quot;
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; True if successful
Dim bRegister As Boolean &apos; Return value
Const cstThisSub = &quot;SF_Services.RegisterService&quot;
Const cstSubArgs = &quot;ServiceName, ServiceReference&quot;
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
bRegister = False
Check:
If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not SF_Utils._Validate(ServiceName, &quot;ServiceName&quot;, V_STRING) Then GoTo Finally
If Not SF_Utils._Validate(ServiceReference, &quot;ServiceReference&quot;, Array(V_STRING, V_OBJECT)) Then GoTo Finally
End If
Try:
bRegister = _AddToServicesArray(ServiceName, ServiceReference, False)
Finally:
RegisterService = bRegister
SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
GoTo Finally
End Function &apos; ScriptForge.SF_Services.RegisterService
REM -----------------------------------------------------------------------------
Public Sub RegisterScriptServices() As Variant
&apos;&apos;&apos; Register into ScriptForge the list of the services implemented by the current library
&apos;&apos;&apos; Each library pertaining to the framework must implement its own version of this method
&apos;&apos;&apos; This method may be stored in any standard (i.e. not class-) module
&apos;&apos;&apos;
&apos;&apos;&apos; Each individual service is registered by calling the RegisterService() method
&apos;&apos;&apos;
&apos;&apos;&apos; The current version is given as an example
&apos;&apos;&apos;
With GlobalScope.ScriptForge.SF_Services
.RegisterService(&quot;Array&quot;, GlobalScope.ScriptForge.SF_Array) &apos; Reference to the Basic module
.RegisterService(&quot;Dictionary&quot;, &quot;ScriptForge.SF_Services._NewDictionary&quot;) &apos; Reference to the function initializing the service
.RegisterService(&quot;Exception&quot;, GlobalScope.ScriptForge.SF_Exception)
.RegisterService(&quot;FileSystem&quot;, GlobalScope.ScriptForge.SF_FileSystem)
.RegisterService(&quot;L10N&quot;, &quot;ScriptForge.SF_Services._NewL10N&quot;)
.RegisterService(&quot;Platform&quot;, GlobalScope.ScriptForge.SF_Platform)
.RegisterService(&quot;Region&quot;, GlobalScope.ScriptForge.SF_Region)
.RegisterService(&quot;Session&quot;, GlobalScope.ScriptForge.SF_Session)
.RegisterService(&quot;String&quot;, GlobalScope.ScriptForge.SF_String)
.RegisterService(&quot;Timer&quot;, &quot;ScriptForge.SF_Services._NewTimer&quot;)
.RegisterService(&quot;UI&quot;, GlobalScope.ScriptForge.SF_UI)
&apos;TODO
End With
End Sub &apos; ScriptForge.SF_Services.RegisterScriptServices
REM =========================================================== PRIVATE FUNCTIONS
REM -----------------------------------------------------------------------------
Private Function _AddToServicesArray(ByVal psServiceName As String _
, ByRef pvServiceReference As Variant _
, ByVal pbEvent As Boolean _
) As Boolean
&apos;&apos;&apos; Add the arguments as an additional row in vServicesArray (Public variable)
&apos;&apos;&apos; Called from RegisterService and RegisterEvent methods
Dim bRegister As Boolean &apos; Return value
Dim lMax As Long &apos; Number of rows in vServicesArray
bRegister = False
Check:
&apos; Ignore when method is not called from RegisterScriptServices()
If IsEmpty(vServicesArray) Or IsNull(vServicesArray) Or Not IsArray(vServicesArray) Then GoTo Finally
Try:
lMax = UBound(vServicesArray, 1) + 1
If lMax &lt;= 0 Then
ReDim vServicesArray(0 To 0, 0 To 2)
Else
ReDim Preserve vServicesArray(0 To lMax, 0 To 2)
End If
vServicesArray(lMax, 0) = psServiceName
vServicesArray(lMax, 1) = pvServiceReference
vServicesArray(lMax, 2) = pbEvent
bRegister = True
Finally:
_AddToServicesArray = bRegister
Exit Function
End Function &apos; ScriptForge.SF_Services._AddToServicesArray
REM -----------------------------------------------------------------------------
Private Function _FindModuleFromMethod(ByVal psLibrary As String _
, ByVal psMethod As String _
) As String
&apos;&apos;&apos; Find in the given library the name of the module containing
&apos;&apos;&apos; the method given as 2nd argument (usually RegisterScriptServices)
&apos;&apos;&apos; Args:
&apos;&apos;&apos; psLibrary: the name of the Basic library
&apos;&apos;&apos; psMethod: the method to locate
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; The name of the module or a zero-length string if not found
Dim vCategories As Variant &apos; &quot;user&quot; or &quot;share&quot; library categories
Dim sCategory As String
Dim vLanguages As Variant &apos; &quot;Basic&quot;, &quot;Python&quot;, ... programming languages
Dim sLanguage As String
Dim vLibraries As Variant &apos; Library names
Dim sLibrary As String
Dim vModules As Variant &apos; Module names
Dim sModule As String &apos; Return value
Dim vMethods As Variant &apos; Method/properties/subs/functions
Dim sMethod As String
Dim oRoot As Object &apos; com.sun.star.script.browse.BrowseNodeFactory
Dim i As Integer, j As Integer, k As Integer, l As Integer, m As Integer
_FindModuleFromMethod = &quot;&quot;
Set oRoot = SF_Utils._GetUNOService(&quot;BrowseNodeFactory&quot;).createView(com.sun.star.script.browse.BrowseNodeFactoryViewTypes.MACROORGANIZER)
&apos; Exploration is done via tree nodes
If Not IsNull(oRoot) Then
If oRoot.hasChildNodes() Then
vCategories = oRoot.getChildNodes()
For i = 0 To UBound(vCategories)
sCategory = vCategories(i).getName()
&apos; Consider &quot;My macros &amp; Dialogs&quot; and &quot;LibreOffice Macros &amp; Dialogs&quot; only
If sCategory = &quot;user&quot; Or sCategory = &quot;share&quot; Then
If vCategories(i).hasChildNodes() Then
vLanguages = vCategories(i).getChildNodes()
For j = 0 To UBound(vLanguages)
sLanguage = vLanguages(j).getName()
&apos; Consider Basic libraries only
If sLanguage = &quot;Basic&quot; Then
If vLanguages(j).hasChildNodes() Then
vLibraries = vLanguages(j).getChildNodes()
For k = 0 To UBound(vLibraries)
sLibrary = vLibraries(k).getName()
&apos; Consider the given library only
If sLibrary = psLibrary Then
If vLibraries(k).hasChildNodes() Then
vModules = vLibraries(k).getChildNodes()
For l = 0 To UBound(vModules)
sModule = vModules(l).getName()
&apos; Check if the module contains the targeted method
If vModules(l).hasChildNodes() Then
vMethods = vModules(l).getChildNodes()
For m = 0 To UBound(vMethods)
sMethod = vMethods(m).getName()
If sMethod = psMethod Then
_FindModuleFromMethod = sModule
Exit Function
End If
Next m
End If
Next l
End If
End If
Next k
End If
End If
Next j
End If
End If
Next i
End If
End If
End Function &apos; ScriptForge.SF_Services._FindModuleFromMethod
REM -----------------------------------------------------------------------------
Private Function _LoadLibraryServices(ByVal psLibrary As String) As Boolean
&apos;&apos;&apos; Execute psLibrary.RegisterScriptServices() and load its services into the persistent storage
&apos;&apos;&apos; Args:
&apos;&apos;&apos; psLibrary: the name of the Basic library
&apos;&apos;&apos; Library will be loaded if not yet done
&apos;&apos;&apos; Returns:
&apos;&apos;&apos; True if success
&apos;&apos;&apos; The list of services is loaded directly into the persistent storage
Dim vServicesList As Variant &apos; Dictionary of services
Dim vService As Variant &apos; Single service entry in dictionary
Dim vServiceItem As Variant &apos; Single service in vServicesArray
Dim sModule As String &apos; Name of module containing the RegisterScriptServices method
Dim i As Long
Const cstRegister = &quot;RegisterScriptServices&quot;
Try:
_LoadLibraryServices = False
vServicesArray = Array()
If psLibrary = &quot;ScriptForge&quot; Then
&apos; Direct call
ScriptForge.SF_Services.RegisterScriptServices()
Else
&apos; Register services via script provider
If GlobalScope.BasicLibraries.hasByName(psLibrary) Then
If Not GlobalScope.BasicLibraries.isLibraryLoaded(psLibrary) Then
GlobalScope.BasicLibraries.LoadLibrary(psLibrary)
End If
Else
GoTo Finally
End If
sModule = SF_Services._FindModuleFromMethod(psLibrary, cstRegister)
If Len(sModule) = 0 Then GoTo Finally
SF_Session.ExecuteBasicScript(, psLibrary &amp; &quot;.&quot; &amp; sModule &amp; &quot;.&quot; &amp; cstRegister)
End If
&apos; Store in persistent storage
&apos; - Create list of services for the current library
Set vServicesList = SF_Services._NewDictionary()
For i = 0 To UBound(vServicesArray, 1)
Set vService = New _Service
With vService
.ServiceName = vServicesArray(i, 0)
vServiceItem = vServicesArray(i, 1)
If VarType(vServiceItem) = V_STRING Then
.ServiceType = 2
.ServiceMethod = vServiceItem
Set .ServiceReference = Nothing
Else &apos; OBJECT
.ServiceType = 1
.ServiceMethod = &quot;&quot;
Set .ServiceReference = vServiceItem
End If
.EventManager = vServicesArray(i, 2)
End With
vServicesList.Add(vServicesArray(i, 0), vService)
Next i
&apos; - Add the new dictionary to the persistent dictionary
_SF_.ServicesList.Add(psLibrary, vServicesList)
_LoadLibraryServices = True
vServicesArray = Empty
Finally:
Exit Function
End Function &apos; ScriptForge.SF_Services._LoadLibraryServices
REM -----------------------------------------------------------------------------
Public Function _NewDictionary(Optional ByVal pvArgs As Variant) As Variant
&apos;&apos;&apos; Create a new instance of the SF_Dictionary class
&apos;&apos;&apos; Args:
&apos;&apos;&apos; [0] : If True, the keys are compared case-sensitively. Default = False
&apos;&apos;&apos; Returns: the instance or Nothing
Dim oDict As Variant &apos; Return value
Dim bCaseSensitive As Boolean &apos; Keys comparison
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
If IsMissing(pvArgs) Then pvArgs = Array()
If Not IsArray(pvArgs) Then pvArgs = Array(pvArgs)
If UBound(pvArgs) &lt; 0 Then
bCaseSensitive = False
Else
If Not SF_Utils._Validate(pvArgs(0), &quot;CaseSensitive (Arg0)&quot;, V_BOOLEAN) Then GoTo Catch
bCaseSensitive = pvArgs(0)
End If
Try:
Set oDict = New SF_Dictionary
Set oDict.[Me] = oDict
oDict.CaseSensitive = bCaseSensitive
Finally:
Set _NewDictionary = oDict
Exit Function
Catch:
Set oDict = Nothing
GoTo Finally
End Function &apos; ScriptForge.SF_Services._NewDictionary
REM -----------------------------------------------------------------------------
Public Function _NewL10N(Optional ByVal pvArgs As Variant) As Variant
&apos;&apos;&apos; Create a new instance of the SF_L10N class
&apos; Args:
&apos;&apos;&apos; FolderName: the folder containing the PO files in SF_FileSystem.FileNaming notation
&apos;&apos;&apos; Locale: locale of user session (default) or any other valid la{nguage]-CO[UNTRY] combination
&apos;&apos;&apos; The country part is optional. Valid are f.i. &quot;fr&quot;, &quot;fr-CH&quot;, &quot;en-US&quot;
&apos;&apos;&apos; Encoding: The character set that should be used
&apos;&apos;&apos; Use one of the Names listed in https://www.iana.org/assignments/character-sets/character-sets.xhtml
&apos;&apos;&apos; Note that LibreOffice probably does not implement all existing sets
&apos;&apos;&apos; Default = UTF-8
&apos;&apos;&apos; Locale2: fallback Locale to select if Locale po file does not exist (typically &quot;en-US&quot;)
&apos;&apos;&apos; Encoding2: Encoding of the 2nd Locale file
&apos;&apos;&apos; Returns: the instance or Nothing
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; UNKNOWNFILEERROR The PO file does not exist
Dim oL10N As Variant &apos; Return value
Dim sFolderName As String &apos; Folder containing the PO files
Dim sLocale As String &apos; Passed argument or that of the user session
Dim sLocale2 As String &apos; Alias for Locale2
Dim oLocale As Variant &apos; com.sun.star.lang.Locale
Dim sPOFile As String &apos; PO file must exist
Dim sEncoding As String &apos; Alias for Encoding
Dim sEncoding2 As String &apos; Alias for Encoding2
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
If IsMissing(pvArgs) Then pvArgs = Array()
sPOFile = &quot;&quot;
sEncoding = &quot;&quot;
If UBound(pvArgs) &gt;= 0 Then
If Not SF_Utils._ValidateFile(pvArgs(0), &quot;Folder (Arg0)&quot;, , True) Then GoTo Catch
sFolderName = pvArgs(0)
sLocale = &quot;&quot;
If UBound(pvArgs) &gt;= 1 Then
If Not SF_Utils._Validate(pvArgs(1), &quot;Locale (Arg1)&quot;, V_STRING) Then GoTo Catch
sLocale = pvArgs(1)
End If
If Len(sLocale) = 0 Then &apos; Called from Python, the Locale argument may be the zero-length string
Set oLocale = SF_Utils._GetUNOService(&quot;OfficeLocale&quot;)
sLocale = oLocale.Language &amp; &quot;-&quot; &amp; oLocale.Country
End If
If UBound(pvArgs) &gt;= 2 Then
If IsMissing(pvArgs(2)) Or IsEmpty(pvArgs(2)) Then pvArgs(2) = &quot;UTF-8&quot;
If Not SF_Utils._Validate(pvArgs(2), &quot;Encoding (Arg2)&quot;, V_STRING) Then GoTo Catch
sEncoding = pvArgs(2)
Else
sEncoding = &quot;UTF-8&quot;
End If
sLocale2 = &quot;&quot;
If UBound(pvArgs) &gt;= 3 Then
If Not SF_Utils._Validate(pvArgs(3), &quot;Locale2 (Arg3)&quot;, V_STRING) Then GoTo Catch
sLocale2 = pvArgs(3)
End If
If UBound(pvArgs) &gt;= 4 Then
If Not SF_Utils._Validate(pvArgs(4), &quot;Encoding2 (Arg4)&quot;, V_STRING) Then GoTo Catch
sEncoding2 = pvArgs(4)
Else
sEncoding2 = &quot;UTF-8&quot;
End If
If Len(sFolderName) &gt; 0 Then
sPOFile = SF_FileSystem.BuildPath(sFolderName, sLocale &amp; &quot;.po&quot;)
If Not SF_FileSystem.FileExists(sPOFile) Then
If Len(sLocale2) = 0 Then GoTo CatchNotExists &apos; No fallback =&gt; error
&apos; Try the fallback
sPOFile = SF_FileSystem.BuildPath(sFolderName, sLocale2 &amp; &quot;.po&quot;)
If Not SF_FileSystem.FileExists(sPOFile) Then GoTo CatchNotExists
sEncoding = sEncoding2
End If
End If
End If
Try:
Set oL10N = New SF_L10N
Set oL10N.[Me] = oL10N
oL10N._Initialize(sPOFile, sEncoding)
Finally:
Set _NewL10N = oL10N
Exit Function
Catch:
Set oL10N = Nothing
GoTo Finally
CatchNotExists:
SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot;, sPOFile)
GoTo Finally
End Function &apos; ScriptForge.SF_Services._NewL10N
REM -----------------------------------------------------------------------------
Public Function _NewTimer(Optional ByVal pvArgs As Variant) As Variant
&apos;&apos;&apos; Create a new instance of the SF_Timer class
&apos;&apos;&apos; Args:
&apos;&apos;&apos; [0] : If True, start the timer immediately
&apos;&apos;&apos; Returns: the instance or Nothing
Dim oTimer As Variant &apos; Return value
Dim bStart As Boolean &apos; Automatic start ?
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
If IsMissing(pvArgs) Then pvArgs = Array()
If UBound(pvArgs) &lt; 0 Then
bStart = False
Else
If Not SF_Utils._Validate(pvArgs(0), &quot;Start (Arg0)&quot;, V_BOOLEAN) Then GoTo Catch
bStart = pvArgs(0)
End If
Try:
Set oTimer = New SF_Timer
Set oTimer.[Me] = oTimer
If bStart Then oTimer.Start()
Finally:
Set _NewTimer = oTimer
Exit Function
Catch:
Set oTimer = Nothing
GoTo Finally
End Function &apos; ScriptForge.SF_Services._NewTimer
REM ============================================== END OF SCRIPTFORGE.SF_SERVICES
</script:module>