office-gobmx/wizards/source/scriptforge/SF_Services.xba
Jean-Pierre Ledure 0a1022b04c ScriptForge (SFDialogs: create dialogs on-the-fly
A dialog service is returned by next statement

  dialog = CreateScriptService("newdialog", dialogname, place)

All properties and methods applicable to predefined
dialogs are available for such new dialogs.
In particular the series of CreateXXX() methods for the addition
of ne dialog controls.

The functionality is available from Basic and Python user
scripts.

An update of the SFDialogs.SF_Dialog help page is required.

A display rendering unstability (flickerings, delays, ..) has
been observed when (all conditions must be met)
- the user script is run from Python
- from inside the LibreOffice process
- the dialog is non-modal

Change-Id: Id3f311cd04497fd79712ce712bdb2724b5caa861
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152071
Tested-by: Jean-Pierre Ledure <jp@ledure.be>
Reviewed-by: Jean-Pierre Ledure <jp@ledure.be>
2023-05-22 14:08:20 +02:00

641 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 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;
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;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() As Variant
&apos;&apos;&apos; Create a new instance of the SF_Dictionary class
&apos;&apos;&apos; Returns: the instance or Nothing
Dim oDict As Variant
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
Check:
Try:
Set oDict = New SF_Dictionary
Set oDict.[Me] = oDict
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>