summaryrefslogtreecommitdiff
path: root/wizards/source/scriptforge/SF_Services.xba
blob: d77058cb3ba0fb4418fd70b670ce36e341d0a953 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
<?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;documentevent&quot;, &quot;formevent&quot;
														sLibrary = &quot;SFDocuments&quot;
			Case &quot;dialog&quot;, &quot;dialogevent&quot;			:	sLibrary = &quot;SFDialogs&quot;
			Case &quot;database&quot;							:	sLibrary = &quot;SFDatabases&quot;
			Case &quot;menu&quot;, &quot;popupmenu&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;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;	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 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

	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;SystemLocale&quot;)
			sLocale = oLocale.Language &amp; &quot;-&quot; &amp; oLocale.Country
		End If
		If UBound(pvArgs) &gt;= 2 Then
			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
		If Len(sFolderName) &gt; 0 Then
			sPOFile = SF_FileSystem.BuildPath(sFolderName, sLocale &amp; &quot;.po&quot;)
			If Not SF_FileSystem.FileExists(sPOFile) Then GoTo CatchNotExists
		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>