summaryrefslogtreecommitdiff
path: root/wizards/source/sfdocuments/SF_Register.xba
blob: d2b0dafd341f68973cc7f3d15eff454b2a314496 (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
<?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_Register" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
REM ===			The ScriptForge library and its associated libraries are part of the LibreOffice project.				===
REM	===						The SFDocuments library is one of the associated libraries.									===
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_Register
&apos;&apos;&apos;	===========
&apos;&apos;&apos;		The ScriptForge framework includes
&apos;&apos;&apos;			the master ScriptForge library
&apos;&apos;&apos;			a number of &quot;associated&quot; libraries SF*
&apos;&apos;&apos;			any user/contributor extension wanting to fit into the framework 
&apos;&apos;&apos;
&apos;&apos;&apos;		The main methods in this module allow the current library to cling to ScriptForge
&apos;&apos;&apos;			- RegisterScriptServices
&apos;&apos;&apos;				Register the list of services implemented by the current library
&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&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

REM ================================================================= DEFINITIONS

&apos;&apos;&apos;	Strategy for management of Form and FormControl events:
&apos;&apos;&apos;	------------------------------------------------------
&apos;&apos;&apos;	At the contrary of Dialogs and DialogControls, which are always started from some code,
&apos;&apos;&apos;	Forms and FormControls will be initiated most often by the user, even if the SFDocuments library
&apos;&apos;&apos;	allows to start forms programmatically
&apos;&apos;&apos;
&apos;&apos;&apos;	For Forms started programmatically, the corresponding objects are built top-down
&apos;&apos;&apos;	Event management of forms and their controls requires to being able to rebuild Form
&apos;&apos;&apos;	and FormControl objects bottom-up
&apos;&apos;&apos;
&apos;&apos;&apos;	To avoid multiple rebuilds requested by multiple events,
&apos;&apos;&apos;		1. The active form objects are cached in a global array of _FormCache types
&apos;&apos;&apos;		2. FormControl objects are cached in Form objects
&apos;&apos;&apos;		3. The bottom-up rebuild is executed only once, at instance creation

Type _FormCache
	Terminated				As Boolean
	XUnoForm				As Object
	BasicForm				As Object
End Type

REM ============================================================== PUBLIC METHODS

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;
&apos;&apos;&apos;	It consists in successive calls to the RegisterService() and RegisterEventManager() methods
&apos;&apos;&apos;	with 2 arguments:
&apos;&apos;&apos;		ServiceName: the name of the service as a case-insensitive string
&apos;&apos;&apos;		ServiceReference: the reference as an object
&apos;&apos;&apos;			If the reference refers to a module, then return the module as an object:
&apos;&apos;&apos;				GlobalScope.Library.Module
&apos;&apos;&apos;			If the reference is a class instance, then return a string referring to the method
&apos;&apos;&apos;			containing the New statement creating the instance
&apos;&apos;&apos;				&quot;libraryname.modulename.function&quot;

	With GlobalScope.ScriptForge.SF_Services
		.RegisterService(&quot;Document&quot;,			&quot;SFDocuments.SF_Register._NewDocument&quot;)		&apos;	Reference to the function initializing the service
		.RegisterService(&quot;Calc&quot;,				&quot;SFDocuments.SF_Register._NewDocument&quot;)		&apos;	Same references, distinction is made inside the function
		.RegisterService(&quot;Base&quot;,				&quot;SFDocuments.SF_Register._NewDocument&quot;)		&apos;	Same references, distinction is made inside the function
		.RegisterEventManager(&quot;DocumentEvent&quot;,	&quot;SFDocuments.SF_Register._EventManager&quot;)	&apos;	Reference to the events manager
		.RegisterEventManager(&quot;FormEvent&quot;,		&quot;SFDocuments.SF_Register._FormEventManager&quot;)&apos;	Reference to the form and controls events manager
	End With

End Sub			&apos;	SFDocuments.SF_Register.RegisterScriptServices

REM =========================================================== PRIVATE FUNCTIONS

REM -----------------------------------------------------------------------------
Private Function _AddFormToCache(ByRef pvUnoForm As Object _
										, ByRef pvBasicForm As Object _
										) As Long
&apos;&apos;&apos;	Add a new entry in the cache array with the references of the actual Form
&apos;&apos;&apos;	If relevant, the last entry of the cache is reused.
&apos;&apos;&apos;	The cache is located in the global _SF_ variable
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pvUnoForm: com.sun.star.form.XForm or com.sun.star.comp.forms.ODatabaseForm
&apos;&apos;&apos;		pvBasicForm: its corresponding Basic object
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The index of the new or modified entry

Dim vCache As New _FormCache		&apos;	Entry to be added
Dim lIndex As Long					&apos;	UBound of _SF_.SFForms
Dim vCacheArray As Variant			&apos;	Alias of _SF_.SFForms

Try:
	vCacheArray = _SF_.SFForms

	If IsEmpty(vCacheArray) Then vCacheArray = Array()
	lIndex = UBound(vCacheArray)
	If lIndex &lt; LBound(vCacheArray) Then
		ReDim vCacheArray(0 To 0)
		lIndex = 0
	ElseIf Not vCacheArray(lIndex).Terminated Then		&apos;	Often last entry can be reused
		lIndex = lIndex + 1
		ReDim Preserve vCacheArray(0 To lIndex)
	End If
	
	With vCache
		.Terminated = False
		Set .XUnoForm = pvUnoForm
		Set .BasicForm = pvBasicForm
	End With
	Set vCacheArray(lIndex) = vCache
	
	_SF_.SFForms = vCacheArray

Finally:
	_AddFormToCache = lIndex
	Exit Function
End Function	&apos;	SFDocuments.SF_Register._AddFormToCache

REM -----------------------------------------------------------------------------
Private Sub _CleanCacheEntry(ByVal plIndex As Long)
&apos;&apos;&apos;	Clean the plIndex-th entry in the Forms cache
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		plIndex: must fit within the actual boundaries of the cache, otherwise the request is ignored

Dim vCache As New _FormCache		&apos;	Cleaned entry

	With _SF_
		If Not IsArray(.SFForms) Then Exit Sub
		If plIndex &lt; LBound(.SFForms) Or plIndex &gt; UBound(.SFForms) Then Exit Sub

		With vCache
			.Terminated = True
			Set .XUnoForm = Nothing
			Set .BasicForm = Nothing
		End With
		.SFForms(plIndex) = vCache
	End With
	
Finally:
	Exit Sub
End Sub	&apos;	SFDocuments.SF_Register._CleanCacheEntry

REM -----------------------------------------------------------------------------
Public Function _EventManager(Optional ByRef pvArgs As Variant) As Object
&apos;&apos;&apos;	Returns a Document, Calc or Base object corresponding with the active component
&apos;&apos;&apos;	which triggered the event in argument
&apos;&apos;&apos;	This method should be triggered only thru the invocation of CreateScriptService
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pvEvent: com.sun.star.document.DocumentEvent
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		the output of a Document, Calc, ... service or Nothing
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Sub TriggeredByEvent(ByRef poEvent As Object)
&apos;&apos;&apos;			Dim oDoc As Object
&apos;&apos;&apos;			Set oDoc = CreateScriptService(&quot;SFDocuments.DocumentEvent&quot;, poEvent)
&apos;&apos;&apos;			If Not IsNull(oDoc) Then
&apos;&apos;&apos;				&apos; ... (a valid document has been identified)
&apos;&apos;&apos;		End Sub

Dim oSource As Object			&apos;	Return value
Dim vEvent As Variant			&apos;	Alias of pvArgs(0)

	&apos;	Never abort while an event is processed
	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Finally
	Set oSource = Nothing

Check:
	If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array()
	If UBound(pvArgs) &gt;= 0 Then vEvent = pvArgs(0) Else Set vEvent = Empty
	If VarType(vEvent) &lt;&gt; ScriptForge.V_OBJECT Then GoTo Finally

Try:
	If ScriptForge.SF_Session.UnoObjectType(vEvent) = &quot;com.sun.star.document.DocumentEvent&quot; Then
		Set oSource = SF_Register._NewDocument(vEvent.Source)
	End If

Finally:
	Set _EventManager = oSource
	Exit Function
End Function	&apos;	SFDocuments.SF_Register._EventManager

REM -----------------------------------------------------------------------------
Private Function _FindFormInCache(ByRef poForm As Object) As Object
&apos;&apos;&apos;	Find the Form based on its XUnoForm
&apos;&apos;&apos;	The Form must not be terminated
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The corresponding Basic Form part or Nothing

Dim oBasicForm As Object		&apos;	Return value
Dim oCache As _FormCache		&apos;	Entry in the cache

	Set oBasicForm = Nothing

Try:
	With _SF_
		If Not IsEmpty(.SFForms) Then
			For Each oCache In .SFForms
				If EqualUnoObjects(poForm, oCache.XUnoForm) And Not oCache.Terminated Then
					Set oBasicForm = oCache.BasicForm
					Exit For
				End If
			Next oCache
		End If
	End With

Finally:
	Set _FindFormInCache = oBasicForm
	Exit Function
End Function	&apos;	SFDocuments.SF_Register._FindFormInCache

REM -----------------------------------------------------------------------------
Public Function _FormEventManager(Optional ByRef pvArgs As Variant) As Object
&apos;&apos;&apos;	Returns a Form or FormControl object corresponding with the form or control
&apos;&apos;&apos;	which triggered the event in argument
&apos;&apos;&apos;	This method should be triggered only thru the invocation of CreateScriptService
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		pvEvent: com.sun.star.lang.EventObject
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		the output of a Form, FormControl service or Nothing
&apos;&apos;&apos;	Example:
&apos;&apos;&apos;		Sub TriggeredByEvent(ByRef poEvent As Object)
&apos;&apos;&apos;			Dim oForm As Object
&apos;&apos;&apos;			Set oForm = CreateScriptService(&quot;SFDocuments.FormEvent&quot;, poEvent)
&apos;&apos;&apos;			If Not IsNull(oForm) Then
&apos;&apos;&apos;				&apos; ... (a valid form or subform has been identified)
&apos;&apos;&apos;		End Sub

Dim oSource As Object			&apos;	Return value
Dim vEvent As Variant			&apos;	Alias of pvArgs(0)
Dim oControlModel As Object		&apos;	com.sun.star.awt.XControlModel
Dim oParent As Object			&apos;	com.sun.star.form.OGridControlModel or com.sun.star.comp.forms.ODatabaseForm
Dim sParentType As String		&apos;	&quot;com.sun.star.form.OGridControlModel&quot; or &quot;com.sun.star.comp.forms.ODatabaseForm&quot;
Dim oSFParent As Object			&apos;	The parent as a ScriptForge instance: SF_Form or SF_FormControl
Dim oSFForm As Object			&apos;	The grand-parent SF_Form instance
Dim oSession As Object			:	Set oSession = ScriptForge.SF_Session

	&apos;	Never abort while an event is processed
	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Finally
	Set oSource = Nothing

Check:
	If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array()
	If UBound(pvArgs) &gt;= 0 Then vEvent = pvArgs(0) Else Set vEvent = Empty
	If VarType(vEvent) &lt;&gt; ScriptForge.V_OBJECT Then GoTo Finally

Try:
	If oSession.HasUnoProperty(vEvent, &quot;Source&quot;) Then

		&apos;	FORM EVENT
		If oSession.UnoObjectType(vEvent.Source) = &quot;com.sun.star.comp.forms.ODatabaseForm&quot; Then
			Set oSource = SF_Register._NewForm(vEvent.Source, pbForceInit := True)
		
		&apos;	CONTROL EVENT
		Else
			&apos;	A SF_FormControl instance is always created from its parent, either a form, a subform or a table control
			Set oControlModel = vEvent.Source.Model		&apos;	The event source is a control view com.sun.star.awt.XControl
			Set oParent = oControlModel.Parent
			sParentType = oSession.UnoObjectType(oParent)
			Select Case sParentType
				Case &quot;com.sun.star.form.OGridControlModel&quot;
					Set oSFForm = SF_Register._NewForm(oParent.Parent, pbForceInit := True)
					Set oSFParent = oSFForm.Controls(oParent.Name)
				Case &quot;com.sun.star.comp.forms.ODatabaseForm&quot;
					Set oSFParent = SF_Register._NewForm(oParent, pbForceInit := True)
			End Select
			&apos;	The final instance is derived from its parent instance
			Set oSource = oSFParent.Controls(oControlModel.Name)

		End If

	End If

Finally:
	Set _FormEventManager = oSource
	Exit Function
End Function	&apos;	SFDocuments.SF_Register._FormEventManager

REM -----------------------------------------------------------------------------
Public Function _GetEventScriptCode(poObject As Object _
					, ByVal psEvent As String _
					, ByVal psName As String _
					) As String
&apos;&apos;&apos;	Extract from the parent of poObject the Basic script linked to psEvent.
&apos;&apos;&apos;	Helper function common to forms and form controls
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		poObject: a com.sun.star.form.XForm or XControl object
&apos;&apos;&apos;		psEvent: the &quot;On...&quot; name of the event
&apos;&apos;&apos;		psName: the name of the object to be identified from the parent object
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		The script to trigger when psEvent occurs
&apos;&apos;&apos;		See Scripting Framework URI Specification : https://wiki.openoffice.org/wiki/Documentation/DevGuide/Scripting/Scripting_Framework_URI_Specification

Dim vEvents As Variant			&apos;	List of available events in the parent object
								&apos;	Array of com.sun.star.script.ScriptEventDescriptor
Dim sEvent As String			&apos;	The targeted event name
Dim oParent As Object			&apos;	The parent object
Dim lIndex As Long				&apos;	The index of the targeted event in the events list of the parent object
Dim sName As String				&apos;	The corrected UNO event name
Dim i As Long

	_GetEventScriptCode = &quot;&quot;
	On Local Error GoTo Catch
	If Not ScriptForge.SF_Session.HasUnoMethod(poObject, &quot;getParent&quot;) Then GoTo Finally

Try:
	&apos;	Find form index i.e. find control via getByIndex()
	&apos;	The name is known (= psName) but getByIndex() is not in the same sequence as getElementNames()
	Set oParent = poObject.getParent()
	lIndex = -1
	For i = 0 To oParent.getCount() - 1
		sName = oParent.getByIndex(i).Name
		If (sName = psName) Then
			lIndex = i
			Exit For
		End If
	Next i
	If lIndex &lt; 0 Then GoTo Finally		&apos;	Not found, should not happen
	
	&apos;	Find script triggered by event
	vEvents = oParent.getScriptEvents(lIndex)				&apos;	Returns an array
	&apos;	Fix historical typo error
	sEvent = Replace(LCase(Mid(psEvent, 3, 1)) &amp; Mid(psEvent, 4), &quot;errorOccurred&quot;, &quot;errorOccured&quot;)
	For i = 0 To UBound(vEvents)
		If vEvents(i).EventMethod = sEvent Then
			_GetEventScriptCode = vEvents(i).ScriptCode
			Exit For
		End If
	Next i

Finally:
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SFDocuments.SF_Register._GetEventScriptCode

REM -----------------------------------------------------------------------------
Public Function _NewDocument(Optional ByVal pvArgs As Variant) As Object
&apos;&apos;&apos;	Create a new instance of the (super) SF_Document class or of one of its subclasses (SF_Calc, ...)
&apos;	Args:
&apos;&apos;&apos;		WindowName: see the definition of WindowName in the description of the UI service
&apos;&apos;&apos;			If absent, the document is presumed to be in the active window
&apos;&apos;&apos;			If WindowName is an object, it must be a component
&apos;&apos;&apos;				(com.sun.star.lang.XComponent or com.sun.star.comp.dba.ODatabaseDocument)
&apos;&apos;&apos;	Returns: the instance or Nothing

Dim oDocument As Object				&apos;	Return value
Dim oSuperDocument As Object		&apos;	Companion superclass document
Dim vWindowName As Variant			&apos;	Alias of pvArgs(0)
Dim oEnum As Object					&apos;	com.sun.star.container.XEnumeration
Dim oComp As Object					&apos;	com.sun.star.lang.XComponent
Dim vWindow As Window				&apos;	A single component
Dim oUi As Object					&apos;	&quot;UI&quot; service
Dim bFound As Boolean				&apos;	True if the document is found on the desktop

	If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch

Check:
	If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array()
	If Not IsArray(pvArgs) Then pvArgs = Array(pvArgs)		&apos;	Needed when _NewDocument called from _EventManager
	If UBound(pvArgs) &gt;= 0 Then vWindowName = pvArgs(0) Else vWindowName = &quot;&quot;
	If Not ScriptForge.SF_Utils._Validate(vWindowName, &quot;WindowName&quot;, Array(V_STRING, ScriptForge.V_OBJECT)) Then GoTo Finally
	Set oDocument = Nothing

Try:
	Set oUi = ScriptForge.SF_Register.CreateScriptService(&quot;UI&quot;)
	Select Case VarType(vWindowName)
		Case V_STRING
			If Len(vWindowName) &gt; 0 Then
				bFound = False
				Set oEnum = StarDesktop.Components().createEnumeration
				Do While oEnum.hasMoreElements
					Set oComp = oEnum.nextElement
					vWindow = oUi._IdentifyWindow(oComp)
					With vWindow
						&apos;	Does the current window match the argument ?
						If	(Len(.WindowFileName) &gt; 0 And .WindowFileName = ScriptForge.SF_FileSystem._ConvertToUrl(vWindowName)) _
							Or (Len(.WindowName) &gt; 0 And .WindowName = vWindowName) _
							Or (Len(.WindowTitle) &gt; 0 And .WindowTitle = vWindowName) Then
								bFound = True
								Exit Do
							End If
					End With
				Loop
			Else
				bFound = True
				vWindow = oUi._IdentifyWindow(StarDesktop.CurrentComponent)
			End If
		Case ScriptForge.V_OBJECT	&apos;	com.sun.star.lang.XComponent
			bFound = True
			vWindow = oUi._IdentifyWindow(vWindowName)
	End Select

	If bFound And Not IsNull(vWindow.Frame) And Len(vWindow.DocumentType) &gt; 0 Then
		&apos;	Create the right subclass and associate to it a new instance of the superclass
		Select Case vWindow.DocumentType
			Case &quot;Base&quot;
				Set oDocument = New SF_Base
				Set oSuperDocument = New SF_Document
				Set oDocument.[_Super] = oSuperDocument		&apos;	Now both super and subclass are twinned
				Set oSuperDocument.[_SubClass] = oDocument
			Case &quot;Calc&quot;
				Set oDocument = New SF_Calc
				Set oSuperDocument = New SF_Document
				Set oDocument.[_Super] = oSuperDocument		&apos;	Now both super and subclass are twinned
				Set oSuperDocument.[_SubClass] = oDocument
			Case Else		&apos;	Only superclass
				Set oDocument = New SF_Document
				Set oSuperDocument = oDocument
		End Select
		With oDocument		&apos;	Initialize attributes of subclass
			Set .[Me] = oDocument
			Set ._Component = vWindow.Component
			&apos;	Initialize specific attributes
			Select Case vWindow.DocumentType
				Case &quot;Base&quot;
					Set ._DataSource = ._Component.DataSource
				Case Else
			End Select
		End With
		With oSuperDocument	&apos;	Initialize attributes of superclass
			Set .[Me] = oSuperDocument
			Set ._Component = vWindow.Component
			Set ._Frame = vWindow.Frame
			._WindowName = vWindow.WindowName
			._WindowTitle = vWindow.WindowTitle
			._WindowFileName = vWindow.WindowFileName
			._DocumentType = vWindow.DocumentType
		End With
	End If

Finally:
	Set _NewDocument = oDocument
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SFDocuments.SF_Register._NewDocument

REM -----------------------------------------------------------------------------
Public Function _NewForm(ByRef poForm As Object _
							, Optional pbForceInit As Boolean _
							) As Object
&apos;&apos;&apos;	Returns an existing or a new SF_Form instance based on the argument
&apos;&apos;&apos;	If the instance is new (not found in cache), the minimal members are initialized
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		poForm: com.sun.star.form.XForm or com.sun.star.comp.forms.ODatabaseForm
&apos;&apos;&apos;		pbForceInit: when True, initialize the form instance. Default = False
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		A SF_Form instance

Dim oForm As Object				&apos;	Return value

Try:
	Set oForm = SF_Register._FindFormInCache(poForm)
	If IsNull(oForm) Then		&apos;	Not found
		If IsMissing(pbForceInit) Or IsEmpty(pbForceInit) Then pbForceInit = False
		Set oForm = New SF_Form
		With oForm
			._Name = poForm.Name
			Set .[Me] = oForm
			Set ._Form = poForm
			If pbForceInit Then ._Initialize()
		End With
	End If

Finally:
	Set _NewForm = oForm
	Exit Function
End Function	&apos;	SFDocuments.SF_Register._NewForm

REM -----------------------------------------------------------------------------
Public Function _RegisterEventScript(poObject As Object _
								, ByVal psEvent As String _
								, ByVal psListener As String _
								, ByVal psScriptCode As String _
								, ByVal psName As String _
								) As Boolean
&apos;&apos;&apos;	Register a script event (psEvent) to poObject (Form, SubForm or Control)
&apos;&apos;&apos;	Args:
&apos;&apos;&apos;		poObject: a com.sun.star.form.XForm or XControl object
&apos;&apos;&apos;		psEvent: the &quot;On...&quot; name of the event
&apos;&apos;&apos;		psListener: the listener name corresponding with the event
&apos;&apos;&apos;		psScriptCode: The script to trigger when psEvent occurs
&apos;&apos;&apos;			See Scripting Framework URI Specification : https://wiki.openoffice.org/wiki/Documentation/DevGuide/Scripting/Scripting_Framework_URI_Specification
&apos;&apos;&apos;		psName: the name of the object to associate with the event
&apos;&apos;&apos;	Returns:
&apos;&apos;&apos;		True when successful

Dim oEvent As Object			&apos;	com.sun.star.script.ScriptEventDescriptor
Dim sEvent As String			&apos;	The targeted event name
Dim oParent As Object			&apos;	The parent object
Dim lIndex As Long				&apos;	The index of the targeted event in the events list of the parent object
Dim sName As String				&apos;	The corrected UNO event name
Dim i As Long

	_RegisterEventScript = False
	On Local Error GoTo Catch
	If Not ScriptForge.SF_Session.HasUnoMethod(poObject, &quot;getParent&quot;) Then GoTo Finally

Try:
	&apos;	Find object&apos;s internal index i.e. how to reach it via getByIndex() 
	Set oParent = poObject.getParent()
	lIndex = -1
	For i = 0 To oParent.getCount() - 1
		sName = oParent.getByIndex(i).Name
		If (sName = psName) Then
			lIndex = i
			Exit For
		End If
	Next i
	If lIndex &lt; 0 Then GoTo Finally		&apos;	Not found, should not happen

	&apos;	Fix historical typo error
	sEvent = Replace(LCase(Mid(psEvent, 3, 1)) &amp; Mid(psEvent, 4), &quot;errorOccurred&quot;, &quot;errorOccured&quot;)
	&apos;	Apply new script code. Erasing it is done with a specific UNO method
	If psScriptCode = &quot;&quot; Then
		oParent.revokeScriptEvent(lIndex, psListener, sEvent, &quot;&quot;)
	Else
		Set oEvent = CreateUnoStruct(&quot;com.sun.star.script.ScriptEventDescriptor&quot;)
		With oEvent
			.ListenerType = psListener
			.EventMethod = sEvent
			.ScriptType = &quot;Script&quot;			&apos;	Better than &quot;Basic&quot;
			.ScriptCode = psScriptCode
		End With
		oParent.registerScriptEvent(lIndex, oEvent)
	End If
	_RegisterEventScript = True

Finally:
	Exit Function
Catch:
	GoTo Finally
End Function	&apos;	SFDocuments.SF_Register._RegisterEventScript

REM ============================================== END OF SFDOCUMENTS.SF_REGISTER
</script:module>