summaryrefslogtreecommitdiff
path: root/wizards
diff options
context:
space:
mode:
authorJean-Pierre Ledure <jp@ledure.be>2020-12-16 17:34:04 +0100
committerJean-Pierre Ledure <jp@ledure.be>2020-12-17 08:56:47 +0100
commite6915d4be4d576fdfd4d612c7968f493edba62c5 (patch)
tree9570a04759820be5180df2e57ca4c91ecc83483e /wizards
parent11382ccf2ff58ba470dfa604654685730e0411f8 (diff)
ScriptForge - (SFDocuments) Introduce form class
New SF_Form class in SFDocuments library Support for Writer, Calc and Base forms Skeleton of Form class module Forms() methods in Calc, Base and Document modules to create a new instance New error messages in po file Change-Id: Id78a4604caf61901d87750026be45cef8f74f110 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/107848 Tested-by: Jean-Pierre Ledure <jp@ledure.be> Tested-by: Jenkins Reviewed-by: Jean-Pierre Ledure <jp@ledure.be>
Diffstat (limited to 'wizards')
-rw-r--r--wizards/Package_sfdocuments.mk1
-rw-r--r--wizards/source/scriptforge/SF_Exception.xba18
-rw-r--r--wizards/source/scriptforge/SF_Root.xba38
-rw-r--r--wizards/source/scriptforge/SF_Utils.xba2
-rw-r--r--wizards/source/scriptforge/po/ScriptForge.pot65
-rw-r--r--wizards/source/scriptforge/po/en.po65
-rw-r--r--wizards/source/sfdatabases/SF_Register.xba2
-rw-r--r--wizards/source/sfdocuments/SF_Base.xba265
-rw-r--r--wizards/source/sfdocuments/SF_Calc.xba172
-rw-r--r--wizards/source/sfdocuments/SF_Document.xba88
-rw-r--r--wizards/source/sfdocuments/SF_Form.xba652
-rw-r--r--wizards/source/sfdocuments/SF_Register.xba2
-rw-r--r--wizards/source/sfdocuments/script.xlb1
13 files changed, 1325 insertions, 46 deletions
diff --git a/wizards/Package_sfdocuments.mk b/wizards/Package_sfdocuments.mk
index e79570ae906b..8d8be4597dd7 100644
--- a/wizards/Package_sfdocuments.mk
+++ b/wizards/Package_sfdocuments.mk
@@ -23,6 +23,7 @@ $(eval $(call gb_Package_add_files,wizards_basicsrvsfdocuments,$(LIBO_SHARE_FOLD
SF_Base.xba \
SF_Calc.xba \
SF_Document.xba \
+ SF_Form.xba \
SF_Register.xba \
__License.xba \
dialog.xlb \
diff --git a/wizards/source/scriptforge/SF_Exception.xba b/wizards/source/scriptforge/SF_Exception.xba
index 5a04fc0bca29..c3f9c96dc93a 100644
--- a/wizards/source/scriptforge/SF_Exception.xba
+++ b/wizards/source/scriptforge/SF_Exception.xba
@@ -104,6 +104,12 @@ Const CALCADDRESSERROR = &quot;CALCADDRESSERROR&quot;
Const DUPLICATESHEETERROR = &quot;DUPLICATESHEETERROR&quot;
Const OFFSETADDRESSERROR = &quot;OFFSETADDRESSERROR&quot;
+&apos; SF_Form
+Const FORMDEADERROR = &quot;FORMDEADERROR&quot;
+Const CALCFORMNOTFOUNDERROR = &quot;CALCFORMNOTFOUNDERROR&quot;
+Const WRITERFORMNOTFOUNDERROR = &quot;WRITERFORMNOTFOUNDERROR&quot;
+Const BASEFORMNOTFOUNDERROR = &quot;BASEFORMNOTFOUNDERROR&quot;
+
&apos; SF_Dialog
Const DIALOGNOTFOUNDERROR = &quot;DIALOGNOTFOUNDERROR&quot;
Const DIALOGDEADERROR = &quot;DIALOGDEADERROR&quot;
@@ -824,6 +830,18 @@ Try:
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;OFFSETADDRESS&quot;, pvArgs(0), pvArgs(1), pvArgs(2), pvArgs(3), pvArgs(4) _
, pvArgs(5), pvArgs(6), pvArgs(7), pvArgs(8), pvArgs(9), pvArgs(10), pvArgs(11))
+ Case FORMDEADERROR &apos; SF_Form._IsStillAlive(FormName, DocumentName)
+ sMessage = sLocation _
+ &amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;FORMDEAD&quot;, pvArgs(0), pvArgs(1))
+ Case CALCFORMNOTFOUNDERROR &apos; SF_Calc.Forms(Index, SheetName, Document)
+ sMessage = sLocation _
+ &amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;CALCFORMNOTFOUND&quot;, pvArgs(0), pvArgs(1), pvArgs(2))
+ Case WRITERFORMNOTFOUNDERROR &apos; SF_Document.Forms(Index, Document)
+ sMessage = sLocation _
+ &amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;WRITERFORMNOTFOUND&quot;, pvArgs(0), pvArgs(1))
+ Case BASEFORMNOTFOUNDERROR &apos; SF_Base.Forms(Index, FormDocument, BaseDocument)
+ sMessage = sLocation _
+ &amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;BASEFORMNOTFOUND&quot;, pvArgs(0), pvArgs(1), pvArgs(2))
Case DIALOGNOTFOUNDERROR &apos; SF_Dialog._NewDialog(Service, DialogName, WindowName)
sMessage = sLocation _
&amp; &quot;\n&quot; &amp; &quot;\n&quot; &amp; .GetText(&quot;DIALOGNOTFOUND&quot;, pvArgs(0), pvArgs(1), pvArgs(2), pvArgs(3), pvArgs(4) _
diff --git a/wizards/source/scriptforge/SF_Root.xba b/wizards/source/scriptforge/SF_Root.xba
index 74154285f551..07ec5acfca8d 100644
--- a/wizards/source/scriptforge/SF_Root.xba
+++ b/wizards/source/scriptforge/SF_Root.xba
@@ -725,6 +725,44 @@ Try:
&amp; &quot;%11: An identifier\n&quot; _
&amp; &quot;%12: A file name&quot; _
)
+ &apos; SF_Form._IsStillAlive
+ .AddText( Context := &quot;FORMDEAD&quot; _
+ , MsgId := &quot;The requested action could not be executed because the form is not open or the document was closed inadvertently.\n\n&quot; _
+ &amp; &quot;The concerned form is &apos;%1&apos; in document &apos;%2&apos;.&quot; _
+ , Comment := &quot;SF_Dialog._IsStillAlive error message\n&quot; _
+ &amp; &quot;%1: An identifier&quot; _
+ &amp; &quot;%2: A file name&quot; _
+ )
+ &apos; SF_Calc.Forms
+ .AddText( Context := &quot;CALCFORMNOTFOUND&quot; _
+ , MsgId := &quot;The requested form could not be found in the Calc sheet. The given index is off-limits.\n\n&quot; _
+ &amp; &quot;The concerned Calc document is &apos;%3&apos;.\n\n&quot; _
+ &amp; &quot;The name of the sheet = &apos;%2&apos;\n&quot; _
+ &amp; &quot;The index = %1&quot; _
+ , Comment := &quot;SF_Form determination\n&quot; _
+ &amp; &quot;%1: A number\n&quot; _
+ &amp; &quot;%2: A sheet name\n&quot; _
+ &amp; &quot;%3: A file name&quot; _
+ )
+ &apos; SF_Document.Forms
+ .AddText( Context := &quot;WRITERFORMNOTFOUND&quot; _
+ , MsgId := &quot;The requested form could not be found in the Writer document. The given index is off-limits.\n\n&quot; _
+ &amp; &quot;The concerned Writer document is &apos;%2&apos;.\n\n&quot; _
+ &amp; &quot;The index = %1&quot; _
+ , Comment := &quot;SF_Form determination\n&quot; _
+ &amp; &quot;%1: A number\n&quot; _
+ &amp; &quot;%2: A file name&quot; _
+ )
+ &apos; SF_Base.Forms
+ .AddText( Context := &quot;BASEFORMNOTFOUND&quot; _
+ , MsgId := &quot;The requested form could not be found in the form document &apos;%2&apos;. The given index is off-limits.\n\n&quot; _
+ &amp; &quot;The concerned Base document is &apos;%3&apos;.\n\n&quot; _
+ &amp; &quot;The index = %1&quot; _
+ , Comment := &quot;SF_Form determination\n&quot; _
+ &amp; &quot;%1: A number\n&quot; _
+ &amp; &quot;%2: A string\n&quot; _
+ &amp; &quot;%3: A file name&quot; _
+ )
&apos; SF_Dialog._NewDialog
.AddText( Context := &quot;DIALOGNOTFOUND&quot; _
, MsgId := &quot;The requested dialog could not be located in the given container or library.\n&quot; _
diff --git a/wizards/source/scriptforge/SF_Utils.xba b/wizards/source/scriptforge/SF_Utils.xba
index 80c939b697bd..22ad2dbceaab 100644
--- a/wizards/source/scriptforge/SF_Utils.xba
+++ b/wizards/source/scriptforge/SF_Utils.xba
@@ -226,6 +226,8 @@ Dim sHeader As String &apos; The specific header to insert
Try:
With _SF_
+ If Not IsNull(.Interface) Then .Interface.Dispose()
+ ._LoadLocalizedInterface(psMode := &quot;ADDTEXT&quot;) &apos; Force reload of labels from the code
.Interface.ExportToPOTFile(FileName, Header := sHeader)
End With
diff --git a/wizards/source/scriptforge/po/ScriptForge.pot b/wizards/source/scriptforge/po/ScriptForge.pot
index ea7209881cb7..9e39b5da6896 100644
--- a/wizards/source/scriptforge/po/ScriptForge.pot
+++ b/wizards/source/scriptforge/po/ScriptForge.pot
@@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.libreoffice.org/enter_bug.cgi?product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"
-"POT-Creation-Date: 2020-12-06 12:16:30\n"
+"POT-Creation-Date: 2020-12-15 15:57:29\n"
"PO-Revision-Date: YYYY-MM-DD HH:MM:SS\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
@@ -465,7 +465,7 @@ msgstr ""
#. SF_Session.ExecuteBasicScript error message
#. %1: An identifier
#. %2: A string
-#. %3: A number
+#. %3: A (long) string
#, kde-format
msgctxt "SCRIPTEXEC"
msgid ""
@@ -726,6 +726,62 @@ msgid ""
"« %11 » = %12"
msgstr ""
+#. SF_Dialog._IsStillAlive error message
+#. %1: An identifier%2: A file name
+#, kde-format
+msgctxt "FORMDEAD"
+msgid ""
+"The requested action could not be executed because the form is not "
+"open or the document was closed inadvertently.\n"
+"\n"
+"The concerned form is '%1' in document '%2'."
+msgstr ""
+
+#. SF_Form determination
+#. %1: A number
+#. %2: A sheet name
+#. %3: A file name
+#, kde-format
+msgctxt "CALCFORMNOTFOUND"
+msgid ""
+"The requested form could not be found in the Calc sheet. The given "
+"index is off-limits.\n"
+"\n"
+"The concerned Calc document is '%3'.\n"
+"\n"
+"The name of the sheet = '%2'\n"
+"The index = %1"
+msgstr ""
+
+#. SF_Form determination
+#. %1: A number
+#. %2: A file name
+#, kde-format
+msgctxt "WRITERFORMNOTFOUND"
+msgid ""
+"The requested form could not be found in the Writer document. The "
+"given index is off-limits.\n"
+"\n"
+"The concerned Writer document is '%2'.\n"
+"\n"
+"The index = %1"
+msgstr ""
+
+#. SF_Form determination
+#. %1: A number
+#. %2: A string
+#. %3: A file name
+#, kde-format
+msgctxt "BASEFORMNOTFOUND"
+msgid ""
+"The requested form could not be found in the form document '%2'. The "
+"given index is off-limits.\n"
+"\n"
+"The concerned Base document is '%3'.\n"
+"\n"
+"The index = %1"
+msgstr ""
+
#. SF_Dialog creation
#. %1: An identifier
#. %2: A string
@@ -766,7 +822,8 @@ msgstr ""
msgctxt "CONTROLTYPE"
msgid ""
"The control '%1' in dialog '%2' is of type '%3'.\n"
-"The property or method '%4' is not applicable on that type of dialog controls."
+"The property or method '%4' is not applicable on that type of dialog "
+"controls."
msgstr ""
#. SF_DialogControl add line in textbox
@@ -798,4 +855,4 @@ msgid ""
"Check its syntax, table and/or field names, ...\n"
"\n"
"SQL Statement : « %1 »"
-msgstr ""
+msgstr "" \ No newline at end of file
diff --git a/wizards/source/scriptforge/po/en.po b/wizards/source/scriptforge/po/en.po
index ea7209881cb7..9e39b5da6896 100644
--- a/wizards/source/scriptforge/po/en.po
+++ b/wizards/source/scriptforge/po/en.po
@@ -14,7 +14,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: https://bugs.libreoffice.org/enter_bug.cgi?product=LibreOffice&bug_status=UNCONFIRMED&component=UI\n"
-"POT-Creation-Date: 2020-12-06 12:16:30\n"
+"POT-Creation-Date: 2020-12-15 15:57:29\n"
"PO-Revision-Date: YYYY-MM-DD HH:MM:SS\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <EMAIL@ADDRESS>\n"
@@ -465,7 +465,7 @@ msgstr ""
#. SF_Session.ExecuteBasicScript error message
#. %1: An identifier
#. %2: A string
-#. %3: A number
+#. %3: A (long) string
#, kde-format
msgctxt "SCRIPTEXEC"
msgid ""
@@ -726,6 +726,62 @@ msgid ""
"« %11 » = %12"
msgstr ""
+#. SF_Dialog._IsStillAlive error message
+#. %1: An identifier%2: A file name
+#, kde-format
+msgctxt "FORMDEAD"
+msgid ""
+"The requested action could not be executed because the form is not "
+"open or the document was closed inadvertently.\n"
+"\n"
+"The concerned form is '%1' in document '%2'."
+msgstr ""
+
+#. SF_Form determination
+#. %1: A number
+#. %2: A sheet name
+#. %3: A file name
+#, kde-format
+msgctxt "CALCFORMNOTFOUND"
+msgid ""
+"The requested form could not be found in the Calc sheet. The given "
+"index is off-limits.\n"
+"\n"
+"The concerned Calc document is '%3'.\n"
+"\n"
+"The name of the sheet = '%2'\n"
+"The index = %1"
+msgstr ""
+
+#. SF_Form determination
+#. %1: A number
+#. %2: A file name
+#, kde-format
+msgctxt "WRITERFORMNOTFOUND"
+msgid ""
+"The requested form could not be found in the Writer document. The "
+"given index is off-limits.\n"
+"\n"
+"The concerned Writer document is '%2'.\n"
+"\n"
+"The index = %1"
+msgstr ""
+
+#. SF_Form determination
+#. %1: A number
+#. %2: A string
+#. %3: A file name
+#, kde-format
+msgctxt "BASEFORMNOTFOUND"
+msgid ""
+"The requested form could not be found in the form document '%2'. The "
+"given index is off-limits.\n"
+"\n"
+"The concerned Base document is '%3'.\n"
+"\n"
+"The index = %1"
+msgstr ""
+
#. SF_Dialog creation
#. %1: An identifier
#. %2: A string
@@ -766,7 +822,8 @@ msgstr ""
msgctxt "CONTROLTYPE"
msgid ""
"The control '%1' in dialog '%2' is of type '%3'.\n"
-"The property or method '%4' is not applicable on that type of dialog controls."
+"The property or method '%4' is not applicable on that type of dialog "
+"controls."
msgstr ""
#. SF_DialogControl add line in textbox
@@ -798,4 +855,4 @@ msgid ""
"Check its syntax, table and/or field names, ...\n"
"\n"
"SQL Statement : « %1 »"
-msgstr ""
+msgstr "" \ No newline at end of file
diff --git a/wizards/source/sfdatabases/SF_Register.xba b/wizards/source/sfdatabases/SF_Register.xba
index 63ad2085d772..c9b3f03d7334 100644
--- a/wizards/source/sfdatabases/SF_Register.xba
+++ b/wizards/source/sfdatabases/SF_Register.xba
@@ -133,7 +133,7 @@ End Function &apos; SFDatabases.SF_Register._NewDatabase
REM -----------------------------------------------------------------------------
Public Function _NewDatabaseFromSource(Optional ByVal pvArgs As Variant) As Object
-&apos;ByRef poDataSource As Object _
+&apos; ByRef poDataSource As Object _
&apos; , ByVal psUser As String _
&apos; , ByVal psPassword As String _
&apos; ) As Object
diff --git a/wizards/source/sfdocuments/SF_Base.xba b/wizards/source/sfdocuments/SF_Base.xba
index 05787fa99a33..6ae761eef85c 100644
--- a/wizards/source/sfdocuments/SF_Base.xba
+++ b/wizards/source/sfdocuments/SF_Base.xba
@@ -26,7 +26,9 @@ Option Explicit
&apos;&apos;&apos; the parent methods and properties.
&apos;&apos;&apos; They should also duplicate some generic private members as a subset of their own set of members
&apos;&apos;&apos;
-&apos;&apos;&apos; The SF_Base module is provided only to block parent properties that are NOT applicable to Base documents
+&apos;&apos;&apos; The SF_Base module is provided mainly to block parent properties that are NOT applicable to Base documents
+&apos;&apos;&apos; In addition, it provides methods to identify form documents and access their internal forms
+&apos;&apos;&apos; (read more elsewhere (the &quot;SFDocuments.Form&quot; service) about this subject)
&apos;&apos;&apos;
&apos;&apos;&apos; The current module is closely related to the &quot;UI&quot; service of the ScriptForge library
&apos;&apos;&apos;
@@ -46,6 +48,8 @@ Option Explicit
REM ================================================================== EXCEPTIONS
Private Const DBCONNECTERROR = &quot;DBCONNECTERROR&quot;
+Private Const FORMDEADERROR = &quot;FORMDEADERROR&quot;
+Private Const BASEFORMNOTFOUNDERROR = &quot;BASEFORMNOTFOUNDERROR&quot;
REM ============================================================= PRIVATE MEMBERS
@@ -55,14 +59,18 @@ Private [_Super] As Object &apos; Document superclass, which the current ins
Private ObjectType As String &apos; Must be BASE
Private ServiceName As String
-&apos; Window component
+&apos; UNO references
Private _Component As Object &apos; com.sun.star.comp.dba.ODatabaseDocument
Private _DataSource As Object &apos; com.sun.star.comp.dba.ODatabaseSource
Private _Database As Object &apos; SFDatabases.Database service instance
+Private _FormDocuments As Object
REM ============================================================ MODULE CONSTANTS
-REM ===================================================== CONSTRUCTOR/DESTRUCTOR
+Const ISBASEFORM = 2 &apos; Form is stored in a Base document
+Const cstToken = &quot;//&quot; &apos; Form names accept special characters but not slashes
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
REM -----------------------------------------------------------------------------
Private Sub Class_Initialize()
@@ -74,6 +82,7 @@ Private Sub Class_Initialize()
Set _Component = Nothing
Set _DataSource = Nothing
Set _Database = Nothing
+ Set _FormDocuments = Nothing
End Sub &apos; SFDocuments.SF_Base Constructor
REM -----------------------------------------------------------------------------
@@ -107,7 +116,7 @@ Const cstSubArgs = &quot;[SaveAsk=True]&quot;
Check:
If IsMissing(SaveAsk) Or IsEmpty(SaveAsk) Then SaveAsk = True
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(SaveAsk, &quot;SaveAsk&quot;, V_BOOLEAN) Then GoTo Finally
End If
@@ -124,6 +133,131 @@ Catch:
End Function &apos; SFDocuments.SF_Base.CloseDocument
REM -----------------------------------------------------------------------------
+Public Function FormDocuments() As Variant
+&apos;&apos;&apos; Return the list of the FormDocuments contained in the Base document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-base array of strings
+&apos;&apos;&apos; Each entry is the full path name of a form document. The path separator is the slash (&quot;/&quot;)
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myForm As Object, myList As Variant
+&apos;&apos;&apos; myList = oDoc.FormDocuments()
+
+Dim vFormNames As Variant &apos; Array of all form names present in the document
+Const cstThisSub = &quot;SFDocuments.Base.FormDocuments&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ End If
+
+Try:
+ &apos; Build list of available FormDocuments recursively with _CollectFormDocuments
+ If IsNull(_FormDocuments) Then Set _FormDocuments = _Component.getFormDocuments()
+ vFormNames = Split(_CollectFormDocuments(_FormDocuments), cstToken)
+
+Finally:
+ FormDocuments = vFormNames
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.FormDocuments
+
+REM -----------------------------------------------------------------------------
+Public Function Forms(Optional ByVal FormDocument As Variant _
+ , Optional ByVal Form As Variant _
+ ) As Variant
+&apos;&apos;&apos; Return either
+&apos;&apos;&apos; - the list of the Forms contained in the form document
+&apos;&apos;&apos; - a SFDocuments.Form object based on its name or its index
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FormDocument: a valid document form name as a case-sensitive string
+&apos;&apos;&apos; Form: a form stored in the Base document given by its name or its index
+&apos;&apos;&apos; When absent, the list of available forms is returned
+&apos;&apos;&apos; To get the first (unique ?) form stored in the form document, set Form = 0
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-base array of strings if Form is absent
+&apos;&apos;&apos; An instance of the SF_Form class if Form exists
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; FORMDEADERROR The form is not open
+&apos;&apos;&apos; BASEFORMNOTFOUNDERROR FormDocument OK but Form not found
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myForm As Object, myList As Variant
+&apos;&apos;&apos; myList = oDoc.Forms(&quot;Folder1/myFormDocument&quot;)
+&apos;&apos;&apos; Set myForm = oDoc.Forms(&quot;Folder1/myFormDocument&quot;, 0)
+
+Dim oForm As Object &apos; The new Form class instance
+Dim oMainForm As Object &apos; com.sun.star.comp.sdb.Content
+Dim oXForm As Object &apos; com.sun.star.form.XForm
+Dim vFormDocuments As Variant &apos; Array of form documents
+Dim vFormNames As Variant &apos; Array of form names
+Dim oForms As Object &apos; Forms collection
+Const cstDrawPage = 0 &apos; Only 1 drawpage in a Base document
+
+Const cstThisSub = &quot;SFDocuments.Base.Forms&quot;
+Const cstSubArgs = &quot;FormDocument, [Form=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(Form) Or IsEmpty(Form) Then Form = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ &apos; Build list of available FormDocuments recursively with _CollectFormDocuments
+ If IsNull(_FormDocuments) Then Set _FormDocuments = _Component.getFormDocuments()
+ vFormDocuments = Split(_CollectFormDocuments(_FormDocuments), cstToken)
+ If Not ScriptForge.SF_Utils._Validate(FormDocument, &quot;FormDocument&quot;, V_STRING, vFormDocuments) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Form, &quot;Form&quot;, Array(V_STRING, ScriptForge.V_NUMERIC)) Then GoTo Finally
+ End If
+ If Not IsLoaded(FormDocument) Then GoTo CatchClosed
+
+Try:
+ &apos; Start from the form document and go down to forms
+ Set oMainForm = _FormDocuments.getByHierarchicalName(FormDocument)
+ Set oForms = oMainForm.Component.DrawPages(cstDrawPage).Forms
+ vFormNames = oForms.getElementNames()
+
+ If Len(Form) = 0 Then &apos; Return the list of valid form names
+ Forms = vFormNames
+ Else
+ If VarType(Form) = V_STRING Then &apos; Find the form by name
+ If Not ScriptForge.SF_Utils._Validate(Form, &quot;Form&quot;, V_STRING, vFormNames) Then GoTo Finally
+ Set oXForm = oForms.getByName(Form)
+ Else &apos; Find the form by index
+ If Form &lt; 0 Or Form &gt;= oForms.Count Then GoTo CatchNotFound
+ Set oXForm = oForms.getByIndex(Form)
+ End If
+ &apos; Create the new Form class instance
+ Set oForm = New SF_Form
+ With oForm
+ ._Name = oXForm.Name
+ Set .[Me] = oForm
+ Set .[_Parent] = [Me]
+ ._DrawPage = cstDrawPage
+ ._UsualName = FormDocument &amp; &quot; : &quot; &amp; ._Name
+ Set ._MainForm = oMainForm
+ ._FormType = ISBASEFORM
+ Set ._Form = oXForm
+ End With
+ Set Forms = oForm
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchClosed:
+ ScriptForge.SF_Exception.RaiseFatal(FORMDEADERROR, FormDocument, _FileIdent())
+CatchNotFound:
+ ScriptForge.SF_Exception.RaiseFatal(BASEFORMNOTFOUNDERROR, Form, FormDocument, _FileIdent())
+End Function &apos; SFDocuments.SF_Base.Forms
+
+REM -----------------------------------------------------------------------------
Public Function GetDatabase(Optional ByVal User As Variant _
, Optional ByVal Password As Variant _
) As Object
@@ -148,7 +282,7 @@ Check:
If IsMissing(User) Or IsEmpty(User) Then User = &quot;&quot;
If IsMissing(Password) Or IsEmpty(Password) Then Password = &quot;&quot;
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(User, &quot;User&quot;, V_STRING) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Password, &quot;Password&quot;, V_STRING) Then GoTo Finally
End If
@@ -210,12 +344,60 @@ Catch:
End Function &apos; SFDocuments.SF_Base.GetProperty
REM -----------------------------------------------------------------------------
+Public Function IsLoaded(Optional ByVal FormDocument As Variant) As Boolean
+&apos;&apos;&apos; Return True if the given FormDocument is open for the user
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FormDocument: a valid document form name as a case-sensitive string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the form document is currently open, otherise False
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Form is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; MsgBox oDoc.IsLoaded(&quot;Folder1/myFormDocument&quot;)
+
+Dim bLoaded As Boolean &apos; Return value
+Dim vFormNames As Variant &apos; Array of all document form names present in the document
+Dim oMainForm As Object &apos; com.sun.star.comp.sdb.Content
+Const cstThisSub = &quot;SFDocuments.Base.IsLoaded&quot;
+Const cstSubArgs = &quot;FormDocument&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bLoaded = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ &apos; Build list of available FormDocuments recursively with _CollectFormDocuments
+ If IsNull(_FormDocuments) Then Set _FormDocuments = _Component.getFormDocuments()
+ vFormNames = Split(_CollectFormDocuments(_FormDocuments), cstToken)
+ If Not ScriptForge.SF_Utils._Validate(FormDocument, &quot;FormDocument&quot;, V_STRING, vFormNames) Then GoTo Finally
+ End If
+
+Try:
+
+ Set oMainForm = _FormDocuments.getByHierarchicalName(FormDocument)
+ &apos; A document form that has never been opened has no component
+ &apos; If ever opened and closed afterwards, it keeps the Component but loses its Controller
+ bLoaded = Not IsNull(oMainForm.Component)
+ If bLoaded Then bLoaded = Not IsNull(oMainForm.Component.CurrentController)
+
+Finally:
+ IsLoaded = bLoaded
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.IsLoaded
+
+REM -----------------------------------------------------------------------------
Public Function Methods() As Variant
&apos;&apos;&apos; Return the list of public methods of the Model service as an array
Methods = Array( _
&quot;Activate&quot; _
, &quot;CloseDocument&quot; _
+ , &quot;FormDocuments&quot; _
+ , &quot;Forms&quot; _
, &quot;GetDatabase&quot; _
, &quot;RunCommand&quot; _
, &quot;Save&quot; _
@@ -274,7 +456,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SFDocuments.SF_Documents.SetProperty
+End Function &apos; SFDocuments.SF_Base.SetProperty
REM ======================================================= SUPERCLASS PROPERTIES
@@ -418,6 +600,73 @@ End Function &apos; SFDocuments.SF_Base.SaveCopyAs
REM =========================================================== PRIVATE FUNCTIONS
REM -----------------------------------------------------------------------------
+Private Function _CollectFormDocuments(ByRef poContainer As Object) As String
+&apos;&apos;&apos; Returns a token-separated string of all hierarchical formdocument names
+&apos;&apos;&apos; depending on the formdocuments container in argument
+&apos;&apos;&apos; The function traverses recursively the whle tree below the container
+&apos;&apos;&apos; The initial call starts from the container _Component.getFormDocuments
+&apos;&apos;&apos; The list contains closed and open forms
+
+Dim sCollectNames As String &apos; Returno value
+Dim oSubItem As Object &apos; com.sun.star.container.XNameAccess (folder) or com.sun.star.ucb.XContent (form)
+Dim sFormName As String &apos; Single form name
+Dim i As Long
+Const cstFormType = &quot;application/vnd.oasis.opendocument.text&quot;
+ &apos; Identifies forms. Folders have a zero-length content type
+
+ On Local Error GoTo Finally
+
+Try:
+ sCollectNames = &quot;&quot;
+ With poContainer
+ For i = 0 To .Count - 1
+ Set oSubItem = .getByIndex(i)
+ If oSubItem.ContentType = cstFormType Then &apos; Add the form to the list
+ sCollectNames = sCollectNames &amp; cstToken &amp; oSubItem.HierarchicalName
+ Else
+ sCollectNames = sCollectNames &amp; cstToken &amp; _CollectFormDocuments(oSubItem)
+ End If
+ Next i
+ End With
+
+Finally:
+ _CollectFormDocuments = Mid(sCollectNames, Len(cstToken) + 1) &apos; Skip the initial token
+ Exit Function
+End Function &apos; SFDocuments.SF_Base._CollectFormDocuments
+
+REM -----------------------------------------------------------------------------
+Private Function _FileIdent() As String
+&apos;&apos;&apos; Returns a file identification from the information that is currently available
+&apos;&apos;&apos; Useful e.g. for display in error messages
+
+ _FileIdent = [_Super]._FileIdent()
+
+End Function &apos; SFDocuments.SF_Base._FileIdent
+
+REM -----------------------------------------------------------------------------
+Private Function _IsStillAlive(Optional ByVal pbForUpdate As Boolean _
+ , Optional ByVal pbError As Boolean _
+ ) As Boolean
+&apos;&apos;&apos; Returns True if the document has not been closed manually or incidentally since the last use
+&apos;&apos;&apos; If dead the actual instance is disposed. The execution is cancelled when pbError = True (default)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pbForUpdate: if True (default = False), check additionally if document is open for editing
+&apos;&apos;&apos; pbError: if True (default), raise a fatal error
+
+Dim bAlive As Boolean &apos; Return value
+
+ If IsMissing(pbForUpdate) Then pbForUpdate = False
+ If IsMissing(pbError) Then pbError = True
+
+Try:
+ bAlive = [_Super]._IsStillAlive(pbForUpdate, pbError)
+
+Finally:
+ _IsStillAlive = bAlive
+ Exit Function
+End Function &apos; SFDocuments.SF_Base._IsStillAlive
+
+REM -----------------------------------------------------------------------------
Private Function _PropertyGet(Optional ByVal psProperty As String _
, Optional ByVal pvArg As Variant _
) As Variant
@@ -437,7 +686,7 @@ Const cstSubArgs = &quot;&quot;
cstThisSub = &quot;SFDocuments.SF_Base.get&quot; &amp; psProperty
ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
Select Case psProperty
Case Else
@@ -461,4 +710,4 @@ Private Function _Repr() As String
End Function &apos; SFDocuments.SF_Base._Repr
REM ============================================ END OF SFDOCUMENTS.SF_BASE
-</script:module>
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Calc.xba b/wizards/source/sfdocuments/SF_Calc.xba
index 892d7268ad5f..86825961630c 100644
--- a/wizards/source/sfdocuments/SF_Calc.xba
+++ b/wizards/source/sfdocuments/SF_Calc.xba
@@ -80,6 +80,7 @@ Private Const BASEDOCUMENTOPENERROR = &quot;BASEDOCUMENTOPENERROR&quot;
Private Const CALCADDRESSERROR = &quot;CALCADDRESSERROR&quot;
Private Const DUPLICATESHEETERROR = &quot;DUPLICATESHEETERROR&quot;
Private Const OFFSETADDRESSERROR = &quot;OFFSETADDRESSERROR&quot;
+Private Const CALCFORMNOTFOUNDERROR = &quot;CALCFORMNOTFOUNDERROR&quot;
REM ============================================================= PRIVATE MEMBERS
@@ -115,7 +116,9 @@ Private Const MAXROWS = 2^20 &apos; Max number of rows in a sheet
Private Const CALCREFERENCE = &quot;SF_CalcReference&quot; &apos; Object type of _Address
-REM ===================================================== CONSTRUCTOR/DESTRUCTOR
+Private Const ISCALCFORM = 2 &apos; Form is stored in a Calc document
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
REM -----------------------------------------------------------------------------
Private Sub Class_Initialize()
@@ -163,7 +166,7 @@ Const cstSubArgs = &quot;Selection&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If IsArray(pvSelection) Then
If Not ScriptForge.SF_Utils._ValidateArray(pvSelection, &quot;pvSelection&quot;, 1, V_STRING, True) Then GoTo Finally
Else
@@ -276,7 +279,7 @@ Const cstSubArgs = &quot;[SheetName]&quot;
Check:
If IsMissing(SheetName) Or IsEmpty(SheetName) Then SheetName = &quot;&quot;
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , , True) Then GoTo Finally
End If
@@ -315,7 +318,7 @@ Const cstSubArgs = &quot;Range&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
End If
@@ -360,7 +363,7 @@ Const cstSubArgs = &quot;Range&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
End If
@@ -399,7 +402,7 @@ Const cstSubArgs = &quot;Range&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
End If
@@ -460,7 +463,7 @@ Const cstSubArgs = &quot;SheetName, NewName, [BeforeSheet=&quot;&quot;&quot;&quo
Check:
If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True, , , True) Then GoTo Finally
If Not _ValidateSheet(NewName, &quot;NewName&quot;, True) Then GoTo Finally
If Not _ValidateSheet(BeforeSheet, &quot;BeforeSheet&quot;, , True, , True) Then GoTo Finally
@@ -549,7 +552,7 @@ Const cstSubArgs = &quot;FileName, SheetName, NewName, [BeforeSheet=&quot;&quot;
Check:
If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not ScriptForge.SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(SheetName, &quot;SheetName&quot;, V_STRING) Then GoTo Finally
If Not _ValidateSheet(NewName, &quot;NewName&quot;, True) Then GoTo Finally
@@ -623,7 +626,7 @@ Const cstSubArgs = &quot;SourceRange, DestinationCell&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(SourceRange, &quot;SourceRange&quot;, Array(V_STRING, ScriptForge.V_OBJECT), , , CALCREFERENCE) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(DestinationCell, &quot;DestinationCell&quot;, V_STRING) Then GoTo Finally
End If
@@ -714,7 +717,7 @@ Const cstSubArgs = &quot;SourceRange, DestinationRange&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(SourceRange, &quot;SourceRange&quot;, Array(V_STRING, ScriptForge.V_OBJECT), , , CALCREFERENCE) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(DestinationRange, &quot;DestinationRange&quot;, V_STRING) Then GoTo Finally
End If
@@ -849,6 +852,86 @@ Finally:
End Function &apos; SF_Documents.SF_Calc.DSum
REM -----------------------------------------------------------------------------
+Public Function Forms(Optional ByVal SheetName As Variant _
+ , Optional ByVal Form As Variant _
+ ) As Variant
+&apos;&apos;&apos; Return either
+&apos;&apos;&apos; - the list of the Forms contained in the given sheet
+&apos;&apos;&apos; - a SFDocuments.Form object based on its name or its index
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Form: a form stored in the document given by its name or its index
+&apos;&apos;&apos; When absent, the list of available forms is returned
+&apos;&apos;&apos; To get the first (unique ?) form stored in the form document, set Form = 0
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; CALCFORMNOTFOUNDERROR Form not found
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-base array of strings if Form is absent
+&apos;&apos;&apos; An instance of the SF_Form class if Form exists
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myForm As Object, myList As Variant
+&apos;&apos;&apos; myList = oDoc.Forms()
+&apos;&apos;&apos; Set myForm = oDoc.Forms(&quot;myForm&quot;)
+
+Dim oForm As Object &apos; The new Form class instance
+Dim oMainForm As Object &apos; com.sun.star.comp.sdb.Content
+Dim oXForm As Object &apos; com.sun.star.form.XForm or com.sun.star.comp.forms.ODatabaseForm
+Dim vFormNames As Variant &apos; Array of form names
+Dim oForms As Object &apos; Forms collection
+Const cstDrawPage = -1 &apos; There is no DrawPages collection in Calc sheets
+
+Const cstThisSub = &quot;SFDocuments.Calc.Forms&quot;
+Const cstSubArgs = &quot;SheetName, [Form=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(Form) Or IsEmpty(Form) Then Form = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Form, &quot;Form&quot;, Array(V_STRING, ScriptForge.V_NUMERIC)) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Start from the Calc sheet and go down to forms
+ Set oForms = _Component.getSheets.getByName(SheetName).DrawPage.Forms
+ vFormNames = oForms.getElementNames()
+
+ If Len(Form) = 0 Then &apos; Return the list of valid form names
+ Forms = vFormNames
+ Else
+ If VarType(Form) = V_STRING Then &apos; Find the form by name
+ If Not ScriptForge.SF_Utils._Validate(Form, &quot;Form&quot;, V_STRING, vFormNames) Then GoTo Finally
+ Set oXForm = oForms.getByName(Form)
+ Else &apos; Find the form by index
+ If Form &lt; 0 Or Form &gt;= oForms.Count Then GoTo CatchNotFound
+ Set oXForm = oForms.getByIndex(Form)
+ End If
+ &apos; Create the new Form class instance
+ Set oForm = New SF_Form
+ With oForm
+ ._Name = oXForm.Name
+ Set .[Me] = oForm
+ Set .[_Parent] = [Me]
+ ._DrawPage = cstDrawPage
+ ._UsualName = SheetName &amp; &quot; : &quot; &amp; ._Name
+ Set ._MainForm = Nothing
+ ._FormType = ISCALCFORM
+ Set ._Form = oXForm
+ End With
+ Set Forms = oForm
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchNotFound:
+ ScriptForge.SF_Exception.RaiseFatal(CALCFORMNOTFOUNDERROR, Form, _FileIdent())
+End Function &apos; SFDocuments.SF_Calc.Forms
+
+REM -----------------------------------------------------------------------------
Function GetColumnName(Optional ByVal ColumnNumber As Variant) As String
&apos;&apos;&apos; Convert a column number (range 1, 2,..1024) into its letter counterpart (range &apos;A&apos;, &apos;B&apos;,..&apos;AMJ&apos;).
&apos;&apos;&apos; Args:
@@ -905,7 +988,7 @@ Const cstSubArgs = &quot;Range&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
End If
@@ -988,7 +1071,7 @@ Const cstSubArgs = &quot;Range&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
End If
@@ -1053,7 +1136,7 @@ Const cstSubArgs = &quot;FileName, DestinationCell, [FilterOptions]=&quot;&quot;
Check:
If IsMissing(FilterOptions) Or IsEmpty(FilterOptions) Then FilterOptions = cstFilterOptions
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not ScriptForge.SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(DestinationCell, &quot;DestinationCell&quot;, V_STRING) Then GoTo Finally
End If
@@ -1129,7 +1212,7 @@ Check:
If IsMissing(RegistrationName) Or IsEmpty(RegistrationName) Then RegistrationName = &quot;&quot;
If IsMissing(DirectSQL) Or IsEmpty(DirectSQL) Then DirectSQL = False
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not ScriptForge.SF_Utils._ValidateFile(FileName, &quot;FileName&quot;, , True) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(DestinationCell, &quot;DestinationCell&quot;, V_STRING) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(SQLCommand, &quot;SQLCommand&quot;, V_STRING) Then GoTo Finally
@@ -1219,7 +1302,7 @@ Const cstSubArgs = &quot;SheetName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;
Check:
If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, True) Then GoTo Finally
If Not _ValidateSheet(BeforeSheet, &quot;BeforeSheet&quot;, , True, , True) Then GoTo Finally
End If
@@ -1321,7 +1404,7 @@ Const cstSubArgs = &quot;Source, Destination&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not _Validate(Source, &quot;Source&quot;, V_STRING) Then GoTo Finally
If Not _Validate(Destination, &quot;Destination&quot;, V_STRING) Then GoTo Finally
End If
@@ -1375,7 +1458,7 @@ Const cstSubArgs = &quot;SheetName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;
Check:
If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
If Not _ValidateSheet(BeforeSheet, &quot;BeforeSheet&quot;, , True, , True) Then GoTo Finally
End If
@@ -1442,7 +1525,7 @@ Check:
If IsMissing(Height) Or IsEmpty(Height) Then Height = 0
If IsMissing(Width) Or IsEmpty(Width) Then Width = 0
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Rows, &quot;Rows&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Columns, &quot;Columns&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
@@ -1517,7 +1600,7 @@ Const cstSubArgs = &quot;SheetName&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
End If
@@ -1557,7 +1640,7 @@ Const cstSubArgs = &quot;SheetName, NewName&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive(True) Then GoTo Finally
+ If Not _IsStillAlive(True) Then GoTo Finally
If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
If Not _ValidateSheet(NewName, &quot;NewName&quot;, True) Then GoTo Finally
End If
@@ -1604,7 +1687,7 @@ Const cstSubArgs = &quot;TargetCell, Value&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(TargetCell, &quot;TargetCell&quot;, V_STRING) Then GoTo Finally
If IsArray(Value) Then
If Not ScriptForge.SF_Utils._ValidateArray(Value, &quot;Value&quot;) Then GoTo Finally
@@ -1659,7 +1742,7 @@ Const cstSubArgs = &quot;TargetRange, Style&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(TargetRange, &quot;TargetRange&quot;, V_STRING) Then GoTo Finally
Set oStyleFamilies = _Component.StyleFamilies
If oStyleFamilies.hasByName(cstStyle) Then vStyles = oStyleFamilies.getByName(cstStyle).getElementNames() Else vStyles = Array()
@@ -1714,7 +1797,7 @@ Const cstSubArgs = &quot;TargetRange, Formula&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(TargetRange, &quot;TargetRange&quot;, V_STRING) Then GoTo Finally
If IsArray(Formula) Then
If Not ScriptForge.SF_Utils._ValidateArray(Formula, &quot;Formula&quot;, 0, V_STRING) Then GoTo Finally
@@ -1831,7 +1914,7 @@ Const cstSubArgs = &quot;TargetRange, Value&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(TargetRange, &quot;TargetRange&quot;, V_STRING) Then GoTo Finally
If IsArray(Value) Then
If Not ScriptForge.SF_Utils._ValidateArray(Value, &quot;Value&quot;) Then GoTo Finally
@@ -1920,7 +2003,7 @@ Check:
If IsMissing(CaseSensitive) Or IsEmpty(CaseSensitive) Then CaseSensitive = False
If IsMissing(SortColumns) Or IsEmpty(SortColumns) Then SortColumns = False
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
If Not ScriptForge.SF_Utils._ValidateArray(SortKeys, &quot;SortKeys&quot;, 1, V_NUMERIC, True) Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(DestinationCell, &quot;DestinationCell&quot;, V_STRING) Then GoTo Finally
@@ -2339,7 +2422,7 @@ Const cstSubArgs = &quot;Range&quot;
Check:
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
End If
@@ -2365,6 +2448,15 @@ Catch:
End Function &apos; SF_Documents.SF_Calc._DFunction
REM -----------------------------------------------------------------------------
+Private Function _FileIdent() As String
+&apos;&apos;&apos; Returns a file identification from the information that is currently available
+&apos;&apos;&apos; Useful e.g. for display in error messages
+
+ _FileIdent = [_Super]._FileIdent()
+
+End Function &apos; SFDocuments.SF_Calc._FileIdent
+
+REM -----------------------------------------------------------------------------
Function _GetColumnName(ByVal plColumnNumber As Long) As String
&apos;&apos;&apos; Convert a column number (range 1, 2,..1024) into its letter counterpart (range &apos;A&apos;, &apos;B&apos;,..&apos;AMJ&apos;).
&apos;&apos;&apos; Args:
@@ -2391,6 +2483,29 @@ Finally:
End Function &apos; SFDocuments.SF_Calc._GetColumnName
REM -----------------------------------------------------------------------------
+Private Function _IsStillAlive(Optional ByVal pbForUpdate As Boolean _
+ , Optional ByVal pbError As Boolean _
+ ) As Boolean
+&apos;&apos;&apos; Returns True if the document has not been closed manually or incidentally since the last use
+&apos;&apos;&apos; If dead the actual instance is disposed. The execution is cancelled when pbError = True (default)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pbForUpdate: if True (default = False), check additionally if document is open for editing
+&apos;&apos;&apos; pbError: if True (default), raise a fatal error
+
+Dim bAlive As Boolean &apos; Return value
+
+ If IsMissing(pbForUpdate) Then pbForUpdate = False
+ If IsMissing(pbError) Then pbError = True
+
+Try:
+ bAlive = [_Super]._IsStillAlive(pbForUpdate, pbError)
+
+Finally:
+ _IsStillAlive = bAlive
+ Exit Function
+End Function &apos; SFDocuments.SF_Calc._IsStillAlive
+
+REM -----------------------------------------------------------------------------
Private Function _LastCell(ByRef poSheet As Object) As Variant
&apos;&apos;&apos; Returns in an array the coordinates of the last used cell in the given sheet
@@ -2632,7 +2747,7 @@ Const cstSubArgs = &quot;&quot;
cstThisSub = &quot;SFDocuments.SF_Calc.get&quot; &amp; psProperty
ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
- If Not [_Super]._IsStillAlive() Then GoTo Finally
+ If Not _IsStillAlive() Then GoTo Finally
Select Case psProperty
Case &quot;CurrentSelection&quot;
@@ -2782,6 +2897,7 @@ Private Function _ValidateSheet(Optional ByRef pvSheetName As Variant _
&apos;&apos;&apos; Sheet designation validation function similar to the SF_Utils._ValidateXXX functions
&apos;&apos;&apos; Args:
&apos;&apos;&apos; pvSheetName: string or numeric position
+&apos;&apos;&apos; pvArgName: the name of the variable to be used in the error message
&apos;&apos;&apos; pvNew: if True, sheet must not exist (default = False)
&apos;&apos;&apos; pvActive: if True, the shortcut &quot;~&quot; is accepted (default = False)
&apos;&apos;&apos; pvOptional: if True, a zero-length string is accepted (default = False)
@@ -2840,4 +2956,4 @@ CatchDuplicate:
End Function &apos; SFDocuments.SF_Calc._ValidateSheet
REM ============================================ END OF SFDOCUMENTS.SF_CALC
-</script:module>
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Document.xba b/wizards/source/sfdocuments/SF_Document.xba
index a9fd48af0424..227638e99efa 100644
--- a/wizards/source/sfdocuments/SF_Document.xba
+++ b/wizards/source/sfdocuments/SF_Document.xba
@@ -24,6 +24,8 @@ Option Explicit
&apos;&apos;&apos; - accessing their standard or custom properties
&apos;&apos;&apos; Specific properties and methods are implemented in the concerned subclass(es) SF_Calc, SF_Writer, ...
&apos;&apos;&apos;
+&apos;&apos;&apos; Documents might contain forms. The current service gives access to the &quot;SFDocuments.Form&quot; service
+&apos;&apos;&apos;
&apos;&apos;&apos; To workaround the absence of class inheritance in LibreOffice Basic, some redundancy is necessary
&apos;&apos;&apos; Each subclass MUST implement also the generic methods and properties, even if they only call
&apos;&apos;&apos; the parent methods and properties implemented below
@@ -53,10 +55,13 @@ Private Const DOCUMENTSAVEERROR = &quot;DOCUMENTSAVEERROR&quot;
Private Const DOCUMENTSAVEASERROR = &quot;DOCUMENTSAVEASERROR&quot;
Private Const DOCUMENTREADONLYERROR = &quot;DOCUMENTREADONLYERROR&quot;
+Private Const FORMDEADERROR = &quot;FORMDEADERROR&quot;
+
REM ============================================================= PRIVATE MEMBERS
Private [Me] As Object
Private [_Parent] As Object
+Private [_SubClass] As Object &apos; Subclass instance
Private ObjectType As String &apos; Must be DOCUMENT
Private ServiceName As String
@@ -74,12 +79,15 @@ Private _CustomProperties As Object &apos; Dictionary of custom properties
REM ============================================================ MODULE CONSTANTS
-REM ===================================================== CONSTRUCTOR/DESTRUCTOR
+Const ISDOCFORM = 1 &apos; Form is stored in a Calc, Writer, ... document
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
REM -----------------------------------------------------------------------------
Private Sub Class_Initialize()
Set [Me] = Nothing
Set [_Parent] = Nothing
+ Set [_SubClass] = Nothing
ObjectType = &quot;DOCUMENT&quot;
ServiceName = &quot;SFDocuments.Document&quot;
Set _Component = Nothing
@@ -449,6 +457,84 @@ Catch:
End Function &apos; SFDocuments.SF_Document.CloseDocument
REM -----------------------------------------------------------------------------
+Public Function Forms(Optional ByVal Form As Variant) As Variant
+&apos;&apos;&apos; APPLICABLE ONLY ON WRITER DOCUMENTS
+&apos;&apos;&apos; Return either
+&apos;&apos;&apos; - the list of the Forms contained in the form document
+&apos;&apos;&apos; - a SFDocuments.Form object based on its name or its index
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Form: a form stored in the document given by its name or its index
+&apos;&apos;&apos; When absent, the list of available forms is returned
+&apos;&apos;&apos; To get the first (unique ?) form stored in the form document, set Form = 0
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; WRITERFORMNOTFOUNDERROR Form not found
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-base array of strings if Form is absent
+&apos;&apos;&apos; An instance of the SF_Form class if Form exists
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myForm As Object, myList As Variant
+&apos;&apos;&apos; myList = oDoc.Forms()
+&apos;&apos;&apos; Set myForm = oDoc.Forms(&quot;myForm&quot;)
+
+Dim oForm As Object &apos; The new Form class instance
+Dim oMainForm As Object &apos; com.sun.star.comp.sdb.Content
+Dim oXForm As Object &apos; com.sun.star.form.XForm
+Dim vFormNames As Variant &apos; Array of form names
+Dim oForms As Object &apos; Forms collection
+Const cstDrawPage = 0 &apos; Only 1 drawpage in a Writer document
+
+Const cstThisSub = &quot;SFDocuments.Document.Forms&quot;
+Const cstSubArgs = &quot;[Form=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(Form) Or IsEmpty(Form) Then Form = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Form, &quot;Form&quot;, Array(V_STRING, ScriptForge.V_NUMERIC)) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Start from the document component and go down to forms
+ Set oForms = _Component.DrawPages(cstDrawPage).Forms
+ vFormNames = oForms.getElementNames()
+
+ If Len(Form) = 0 Then &apos; Return the list of valid form names
+ Forms = vFormNames
+ Else
+ If VarType(Form) = V_STRING Then &apos; Find the form by name
+ If Not ScriptForge.SF_Utils._Validate(Form, &quot;Form&quot;, V_STRING, vFormNames) Then GoTo Finally
+ Set oXForm = oForms.getByName(Form)
+ Else &apos; Find the form by index
+ If Form &lt; 0 Or Form &gt;= oForms.Count Then GoTo CatchNotFound
+ Set oXForm = oForms.getByIndex(Form)
+ End If
+ &apos; Create the new Form class instance
+ Set oForm = New SF_Form
+ With oForm
+ ._Name = oXForm.Name
+ Set .[Me] = oForm
+ Set .[_Parent] = [Me]
+ ._DrawPage = cstDrawPage
+ ._UsualName = ._Name
+ Set ._MainForm = Nothing
+ ._FormType = ISDOCFORM
+ Set ._Form = oXForm
+ End With
+ Set Forms = oForm
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchNotFound:
+ ScriptForge.SF_Exception.RaiseFatal(WRITERFORMNOTFOUNDERROR, Form, _FileIdent())
+End Function &apos; SFDocuments.SF_Document.Forms
+
+REM -----------------------------------------------------------------------------
Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
&apos;&apos;&apos; Return the actual value of the given property
&apos;&apos;&apos; Args:
diff --git a/wizards/source/sfdocuments/SF_Form.xba b/wizards/source/sfdocuments/SF_Form.xba
new file mode 100644
index 000000000000..cdc4fbe92c35
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_Form.xba
@@ -0,0 +1,652 @@
+<?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_Form" 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 ClassModule
+
+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_Form
+&apos;&apos;&apos; =======
+&apos;&apos;&apos; Management of forms defined in LibreOffice documents. Supported types are Base, Calc and Writer documents.
+&apos;&apos;&apos; For Base documents, it includes the management of subforms
+&apos;&apos;&apos; Each instance of the current class represents a single form or a single subform
+&apos;&apos;&apos;
+&apos;&apos;&apos; A form may optionally be (understand &quot;is often&quot;) linked to a data source manageable with the SFDatabases.Database service
+&apos;&apos;&apos; The current service offers a rapid access to that service
+&apos;&apos;&apos;
+&apos;&apos;&apos; Definitions:
+&apos;&apos;&apos;
+&apos;&apos;&apos; FormDocument:
+&apos;&apos;&apos; For usual documents, there is only 1 form document. It is in fact the document itself.
+&apos;&apos;&apos; A Base document may contain an unlimited number of form documents.
+&apos;&apos;&apos; In the Base terminology they are called &quot;forms&quot;. This could create some confusion.
+&apos;&apos;&apos; They can be organized in folders. Their name is then always the full path of folders + form
+&apos;&apos;&apos; with the slash (&quot;/&quot;) as path separator
+&apos;&apos;&apos; A FormDocument is a set of Forms. Form names are visible in the user interface thanks to the form navigator
+&apos;&apos;&apos; Often there is only 1 Form present in a FormDocument. Having more, however, might improve
+&apos;&apos;&apos; the user experience significantly
+&apos;&apos;&apos;
+&apos;&apos;&apos; Form: WHERE IT IS ABOUT IN THE CURRENT &quot;Form&quot; SERVICE
+&apos;&apos;&apos; Is an abstract set of Controls in an OPEN FormDocument
+&apos;&apos;&apos; Each form is (often) linked to a dataset (table, query or Select statement),
+&apos;&apos;&apos; located in any database (provided the user may access it)
+&apos;&apos;&apos; A usual document may contain several forms. Each of which may have its own data source (database + dataset)
+&apos;&apos;&apos; A Base form document may contain several forms. Each of which may address its own dataset. The database however is unique
+&apos;&apos;&apos; A form is defined by its owning FormDocument and its FormName or FormIndex
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocations:
+&apos;&apos;&apos;
+&apos;&apos;&apos; REM the form is stored in a not-Base document (Calc, Writer)
+&apos;&apos;&apos; Dim oDoc As Object, myForm As Object
+&apos;&apos;&apos; Set oDoc = CreateScriptService(&quot;SFDocuments.Document&quot;, ThisComponent)
+&apos;&apos;&apos; Set myForm = oDoc.Forms(&quot;Form1&quot;)
+&apos;&apos;&apos; &apos; or, alternatively, when there is only 1 form
+&apos;&apos;&apos; Set myForm = oDoc.Forms(0)
+&apos;&apos;&apos;
+&apos;&apos;&apos; REM the form is stored in one of the FormDocuments of a Base document
+&apos;&apos;&apos; Dim oDoc As Object, myForm As Object, mySubForm As Object
+&apos;&apos;&apos; Set oDoc = CreateScriptService(&quot;SFDocuments.Document&quot;, ThisDatabaseDocument)
+&apos;&apos;&apos; oDoc.OpenFormDocument(&quot;thisFormDocument&quot;)
+&apos;&apos;&apos; Set myForm = oDoc.Forms(&quot;thisFormDocument&quot;, &quot;MainForm&quot;)
+&apos;&apos;&apos; &apos; or, alternatively, when there is only 1 form
+&apos;&apos;&apos; Set myForm = oDoc.Forms(&quot;thisFormDocument&quot;, 0)
+&apos;&apos;&apos; &apos; To access a subform: myForm and mySubForm become distinct instances of the current class
+&apos;&apos;&apos; Set mySubForm = myForm.SubForms(&quot;mySubForm&quot;)
+&apos;&apos;&apos;
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&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
+
+Private Const FORMDEADERROR = &quot;FORMDEADERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object
+Private ObjectType As String &apos; Must be Form
+Private ServiceName As String
+
+&apos; Form location
+Private _Name As String &apos; Internal name of the form
+Private _DrawPage As Long &apos; Index in DrawOages collection
+Private _UsualName As String &apos; Name as known by user
+Private _FormType As Integer &apos; One of the ISxxxFORM constants
+
+&apos; Form UNO references
+&apos; The forms container found in a Base document
+&apos; Vital for Base forms and subforms
+Private _MainForm As Object &apos; com.sun.star.comp.sdb.Content
+&apos; The entry to the interactions with the form. Set by the _IsStillAlive() method
+&apos; Each method or property requiring that the form is opened should first invoke that method
+Private _Form As Object &apos; com.sun.star.form.XForm or com.sun.star.comp.forms.ODatabaseForm
+Private _Database As Object &apos; Database class instance
+
+&apos; Form attributes
+
+&apos; Persistent storage for controls
+Private _ControlCache As Variant &apos; Array of control objects sorted like ElementNames of XForm
+
+REM ============================================================ MODULE CONSTANTS
+
+Const ISDOCFORM = 1 &apos; Form is stored in a Writer document
+Const ISCALCFORM = 2 &apos; Form is stored in a Calc document
+Const ISBASEFORM = 3 &apos; Form is stored in a Base document
+Const ISSUBFORM = 4 &apos; Form is a subform of a form stored in a Base document or of another subform
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ ObjectType = &quot;Form&quot;
+ ServiceName = &quot;SFDocuments.Form&quot;
+ _Name = &quot;&quot;
+ _DrawPage = -1
+ _FormType = 0
+ Set _MainForm = Nothing
+ Set _Form = Nothing
+ Set _Database = Nothing
+ _ControlCache = Array()
+End Sub &apos; SFDocuments.SF_Form Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDocuments.SF_Form Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDocuments.SF_Form Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get Caption() As Variant
+&apos;&apos;&apos; The Caption property refers to the title of the Form
+ Caption = _PropertyGet(&quot;Caption&quot;)
+End Property &apos; SFDocuments.SF_Form.Caption (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Caption(Optional ByVal pvCaption As Variant)
+&apos;&apos;&apos; Set the updatable property Caption
+ _PropertySet(&quot;Caption&quot;, pvCaption)
+End Property &apos; SFDocumentsDialog.SF_Form.Caption (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Height() As Variant
+&apos;&apos;&apos; The Height property refers to the height of the Form box
+ Height = _PropertyGet(&quot;Height&quot;)
+End Property &apos; SFDocuments.SF_Form.Height (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Height(Optional ByVal pvHeight As Variant)
+&apos;&apos;&apos; Set the updatable property Height
+ _PropertySet(&quot;Height&quot;, pvHeight)
+End Property &apos; SFDocuments.SF_Form.Height (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Name() As String
+&apos;&apos;&apos; Return the name of the actual Form
+ Name = _PropertyGet(&quot;Name&quot;)
+End Property &apos; SFDocuments.SF_Form.Name
+
+REM -----------------------------------------------------------------------------
+Property Get Visible() As Variant
+&apos;&apos;&apos; The Visible property is False before the Execute() statement
+ Visible = _PropertyGet(&quot;Visible&quot;)
+End Property &apos; SFDocuments.SF_Form.Visible (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Visible(Optional ByVal pvVisible As Variant)
+&apos;&apos;&apos; Set the updatable property Visible
+ _PropertySet(&quot;Visible&quot;, pvVisible)
+End Property &apos; SFDocuments.SF_Form.Visible (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Width() As Variant
+&apos;&apos;&apos; The Width property refers to the Width of the Form box
+ Width = _PropertyGet(&quot;Width&quot;)
+End Property &apos; SFDocuments.SF_Form.Width (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Width(Optional ByVal pvWidth As Variant)
+&apos;&apos;&apos; Set the updatable property Width
+ _PropertySet(&quot;Width&quot;, pvWidth)
+End Property &apos; SFDocuments.SF_Form.Width (let)
+
+REM -----------------------------------------------------------------------------
+Property Get XFormModel() As Object
+&apos;&apos;&apos; The XFormModel property returns the model UNO object of the Form
+ XFormModel = _PropertyGet(&quot;XFormModel&quot;)
+End Property &apos; SFDocuments.SF_Form.XFormModel (get)
+
+REM -----------------------------------------------------------------------------
+Property Get XFormView() As Object
+&apos;&apos;&apos; The XFormView property returns the view UNO object of the Form
+ XFormView = _PropertyGet(&quot;XFormView&quot;)
+End Property &apos; SFDocuments.SF_Form.XFormView (get)
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function Activate() As Boolean
+&apos;&apos;&apos; Set the focus on the current Form instance
+&apos;&apos;&apos; Probably called from after an event occurrence or to focus on an open fForm
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if focusing is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim oDlg As Object
+&apos;&apos;&apos; Set oDlg = CreateScriptService(,, &quot;myForm&quot;) &apos; Form stored in current document&apos;s standard library
+&apos;&apos;&apos; oDlg.Activate()
+
+Dim bActivate As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDocuments.Form.Activate&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bActivate = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ End If
+Try:
+
+Finally:
+ Activate = bActivate
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function Controls(Optional ByVal ControlName As Variant) As Variant
+&apos;&apos;&apos; Return either
+&apos;&apos;&apos; - the list of the controls contained in the Form
+&apos;&apos;&apos; - a Form control object based on its name
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; ControlName: a valid control name as a case-sensitive string. If absent the list is returned
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-base array of strings if ControlName is absent
+&apos;&apos;&apos; An instance of the SF_FormControl class if ControlName exists
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; ControlName is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myForm As Object, myList As Variant, myControl As Object
+&apos;&apos;&apos; Set myForm = CreateScriptService(&quot;SFDocuments.Form&quot;, Container, Library, FormName)
+&apos;&apos;&apos; myList = myForm.Controls()
+&apos;&apos;&apos; Set myControl = myForm.Controls(&quot;myTextBox&quot;)
+
+Dim oControl As Object &apos; The new control class instance
+Dim lIndexOfNames As Long &apos; Index in ElementNames array. Used to access _ControlCache
+Dim vControl As Variant &apos; Alias of _ControlCache entry
+Const cstThisSub = &quot;SFDocuments.Form.Controls&quot;
+Const cstSubArgs = &quot;[ControlName]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(ControlName) Or IsEmpty(ControlName) Then ControlName = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ControlName, &quot;ControlName&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ If Len(ControlName) = 0 Then
+ Else
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchNotFound:
+ ScriptForge.SF_Utils._Validate(ControlName, &quot;ControlName&quot;, V_STRING, _FormModel.getElementNames())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.Controls
+
+REM -----------------------------------------------------------------------------
+Public Function GetDatabase(Optional ByVal User As Variant _
+ , Optional ByVal Password As Variant _
+ ) As Object
+&apos;&apos;&apos; Returns a Database instance (service = SFDatabases.Database) giving access
+&apos;&apos;&apos; to the execution of SQL commands on the database defined and/or stored in
+&apos;&apos;&apos; the actual Base document
+&apos;&apos;&apos; Each form has its own database connection, except within Base documents where
+&apos;&apos;&apos; they all share the same connection
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; User, Password: the login parameters as strings. Defaults = &quot;&quot;
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A SFDatabases.Database instance or Nothing
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myDb As Object
+&apos;&apos;&apos; Set myDb = oForm.GetDatabase()
+
+Dim FSO As Object &apos; Alias for SF_FileSystem
+Dim sUser As String &apos; Alias for User
+Dim sPassword As String &apos; Alias for Password
+Const cstThisSub = &quot;SFDocuments.Form.GetDatabase&quot;
+Const cstSubArgs = &quot;[User=&quot;&quot;&quot;&quot;], [Password=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set GetDatabase = Nothing
+
+Check:
+ If IsMissing(User) Or IsEmpty(User) Then User = &quot;&quot;
+ If IsMissing(Password) Or IsEmpty(Password) Then Password = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not [_Parent]._IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(User, &quot;User&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Password, &quot;Password&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Adjust connection arguments
+ If Len(User) = 0 Then
+ If ScriptForge.SF_Session.HasUnoProperty(_Form, &quot;User&quot;) Then sUser = _Form.User Else sUser = &quot;&quot;
+ Else
+ sUser = User
+ End If
+ If Len(sUser) + Len(Password) = 0 Then
+ If ScriptForge.SF_Session.HasUnoProperty(_Form, &quot;Password&quot;) Then sPassword = _Form.Password Else sPassword = Password
+ End If
+
+ &apos; Connect to database, avoiding multiple requests
+ If IsNull(_Database) Then &apos; 1st connection request from the current form instance
+ If _FormType = ISBASEFORM Then
+ &apos; Fetch the shared connection
+ Set _Database = [_Parent].GetDatabase(User, Password)
+ ElseIf Len(_Form.DataSOurceName) = 0 Then &apos; There is no database linked with the form
+ &apos; Return Nothing
+ Else
+ &apos; Check if DataSourceName is a file or a registrered name and create database instance accordingly
+ Set FSO = ScriptForge.SF_FileSystem
+ If FSO.FileExists(FSO._ConvertFromUrl(_Form.DataSourceName)) Then
+ Set _Database = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Database&quot; _
+ , _Form.DataSourceName, , , sUser, sPassword)
+ Else
+ Set _Database = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Database&quot; _
+ , , _Form.DataSourceName, , sUser, sPassword)
+ End If
+ If IsNull(_Database) Then GoTo CatchConnect
+ End If
+ Else
+ EndIf
+
+Finally:
+ Set GetDatabase = _Database
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchConnect:
+ ScriptForge.SF_Exception.RaiseFatal(DBCONNECTERROR, &quot;User&quot;, User, &quot;Password&quot;, Password, [_Super]._FileIdent())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.GetDatabase
+
+REM -----------------------------------------------------------------------------
+Public Function GetProperty(Optional ByVal PropertyName As Variant) As Variant
+&apos;&apos;&apos; Return the actual value of the given property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PropertyName: the name of the property as a string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The actual value of the property
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; ARGUMENTERROR The property does not exist
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDlg.GetProperty(&quot;Caption&quot;)
+
+Const cstThisSub = &quot;SFDocuments.Form.GetProperty&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ GetProperty = Null
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ GetProperty = _PropertyGet(PropertyName)
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Model service as an array
+
+ Methods = Array( _
+ &quot;Activate&quot; _
+ , &quot;CloseForm&quot; _
+ , &quot;Controls&quot; _
+ , &quot;First&quot; _
+ , &quot;GetDatabase&quot; _
+ , &quot;Last&quot; _
+ , &quot;Move&quot; _
+ , &quot;New&quot; _
+ , &quot;Next&quot; _
+ , &quot;Previous&quot; _
+ , &quot;Refresh&quot; _
+ , &quot;Requery&quot; _
+ , &quot;SubForms&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Form.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Form class as an array
+
+ Properties = Array( _
+ &quot;AllowAdditions&quot; _
+ , &quot;AllowDeletions&quot; _
+ , &quot;AllowEdits&quot; _
+ , &quot;Bookmark&quot; _
+ , &quot;Caption&quot; _
+ , &quot;CurrentRecord&quot; _
+ , &quot;Filter&quot; _
+ , &quot;FilterOn&quot; _
+ , &quot;Height&quot; _
+ , &quot;IsLoaded&quot; _
+ , &quot;LinkChildFields&quot; _
+ , &quot;LinkParentFields&quot; _
+ , &quot;Name&quot; _
+ , &quot;OnApproveCursorMove&quot; _
+ , &quot;OnApproveParameter&quot; _
+ , &quot;OnApproveReset&quot; _
+ , &quot;OnApproveRowChange&quot; _
+ , &quot;OnApproveSubmit&quot; _
+ , &quot;OnConfirmDelete&quot; _
+ , &quot;OnCursorMoved&quot; _
+ , &quot;OnErrorOccurred&quot; _
+ , &quot;OnLoaded&quot; _
+ , &quot;OnReloaded&quot; _
+ , &quot;OnReloading&quot; _
+ , &quot;OnResetted&quot; _
+ , &quot;OnRowChanged&quot; _
+ , &quot;OnUnloaded&quot; _
+ , &quot;OnUnloading&quot; _
+ , &quot;OrderBy&quot; _
+ , &quot;OrderByOn&quot; _
+ , &quot;RecordSource&quot; _
+ , &quot;Visible&quot; _
+ , &quot;Width&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Form.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function SetProperty(Optional ByVal PropertyName As Variant _
+ , Optional ByRef Value As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set a new value to the given property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PropertyName: the name of the property as a string
+&apos;&apos;&apos; Value: its new value
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; ARGUMENTERROR The property does not exist
+
+Const cstThisSub = &quot;SFDocuments.Form.SetProperty&quot;
+Const cstSubArgs = &quot;PropertyName, Value&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ SetProperty = False
+
+Check:
+ If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ End If
+
+Try:
+ SetProperty = _PropertySet(PropertyName, Value)
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.SetProperty
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Public Function _GetEventName(ByVal psProperty As String) As String
+&apos;&apos;&apos; Return the LO internal event name derived from the SF property name
+&apos;&apos;&apos; The SF property name is not case sensitive, while the LO name is case-sensitive
+&apos; Corrects the typo on ErrorOccur(r?)ed, if necessary
+
+Dim vProperties As Variant &apos; Array of class properties
+Dim sProperty As String &apos; Correctly cased property name
+
+ vProperties = Properties()
+ sProperty = vProperties(ScriptForge.SF_Array.IndexOf(vProperties, psProperty, SortOrder := &quot;ASC&quot;))
+
+ _GetEventName = LCase(Mid(sProperty, 3, 1)) &amp; Right(sProperty, Len(sProperty) - 3)
+
+End Function &apos; SFDocuments.SF_Form._GetEventName
+
+REM -----------------------------------------------------------------------------
+Private Function _GetListener(ByVal psEventName As String) As String
+&apos;&apos;&apos; Getting/Setting macros triggered by events requires a Listener-EventName pair
+&apos;&apos;&apos; Return the X...Listener corresponding with the event name in argument
+
+ Select Case UCase(psEventName)
+ Case Else
+ _GetListener = &quot;&quot;
+ End Select
+
+End Function &apos; SFDocuments.SF_Form._GetListener
+
+REM -----------------------------------------------------------------------------
+Private Function _IsStillAlive(Optional ByVal pbError As Boolean) As Boolean
+&apos;&apos;&apos; Return True if the Form is still open
+&apos;&apos;&apos; If dead the actual instance is partially (part related to open forms) disposed
+&apos;&apos;&apos; and the execution is cancelled when pbError = True (default)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pbError: if True (default), raise a fatal error
+
+Dim bAlive As Boolean &apos; Return value
+
+Check:
+ On Local Error GoTo Catch &apos; Anticipate DisposedException errors or alike
+ If IsMissing(pbError) Then pbError = True
+
+Try:
+ &apos; For usual documents, check that the parent document is still open
+ &apos; For Base forms and subforms, check the openess of the main form
+ Select Case _FormType
+ Case ISDOCFORM, ISCALCFORM
+ bAlive = [_Parent]._IsStillAlive(pbError)
+ Case ISBASEFORM, ISSUBFORM
+ &apos; A form that has never been opened has no component
+ &apos; If ever opened and closed afterwards, it keeps the Component but loses its Controller
+ bAlive = Not IsNull(_MainForm.Component)
+ If bAlive Then bAlive = Not IsNull(_MainForm.Component.CurrentController)
+ End Select
+ If Not bAlive Then GoTo Catch
+
+Finally:
+ _IsStillAlive = bAlive
+ Exit Function
+Catch:
+ bAlive = False
+ On Error GoTo 0
+ &apos; Dispose the properties related to *open* forms
+ Set _Form = Nothing
+ If Not IsNull(_Database) And _FormType = ISDOCFORM Then Set _Database = _Database.Dispose()
+ Set _ControlCache = Nothing
+ &apos; Display error message
+ If pbError Then ScriptForge.SF_Exception.RaiseFatal(FORMDEADERROR, _Name, [_Parent]._FileIdent())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form._IsStillAlive
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertyGet(Optional ByVal psProperty As String) As Variant
+&apos;&apos;&apos; Return the value of the named property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psProperty: the name of the property
+
+Static oSession As Object &apos; Alias of SF_Session
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ cstThisSub = &quot;SFDocuments.Form.get&quot; &amp; psProperty
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ &apos; All the properties except one require an open form
+ If Not _IsStillAlive() Then GoTo Finally
+
+ If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
+ Select Case UCase(psProperty)
+ Case UCase(&quot;Caption&quot;)
+ Case UCase(&quot;Height&quot;)
+ Case UCase(&quot;Name&quot;)
+ Case UCase(&quot;Visible&quot;)
+ Case UCase(&quot;Width&quot;)
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertySet(Optional ByVal psProperty As String _
+ , Optional ByVal pvValue As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set the new value of the named property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psProperty: the name of the property
+&apos;&apos;&apos; pvValue: the new value of the given property
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if successful
+
+Dim bSet As Boolean &apos; Return value
+Static oSession As Object &apos; Alias of SF_Session
+Dim cstThisSub As String
+Const cstSubArgs = &quot;Value&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bSet = False
+
+ cstThisSub = &quot;SFDocuments.Form.set&quot; &amp; psProperty
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+ If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
+ bSet = True
+ Select Case UCase(psProperty)
+ Case UCase(&quot;Caption&quot;)
+ Case UCase(&quot;Height&quot;)
+ Case UCase(&quot;Visible&quot;)
+ Case UCase(&quot;Width&quot;)
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ _PropertySet = bSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form._PropertySet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the Model instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[Form]: Name&quot;
+
+ _Repr = &quot;[Form]: &quot; &amp; _UsualName
+
+End Function &apos; SFDocuments.SF_Form._Repr
+
+REM ============================================ END OF SFDOCUMENTS.SF_FORM
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Register.xba b/wizards/source/sfdocuments/SF_Register.xba
index 40f327bb0d41..d003eee71c31 100644
--- a/wizards/source/sfdocuments/SF_Register.xba
+++ b/wizards/source/sfdocuments/SF_Register.xba
@@ -158,10 +158,12 @@ Try:
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
diff --git a/wizards/source/sfdocuments/script.xlb b/wizards/source/sfdocuments/script.xlb
index 82a939306752..fc075b026fc1 100644
--- a/wizards/source/sfdocuments/script.xlb
+++ b/wizards/source/sfdocuments/script.xlb
@@ -6,4 +6,5 @@
<library:element library:name="SF_Calc"/>
<library:element library:name="SF_Register"/>
<library:element library:name="SF_Base"/>
+ <library:element library:name="SF_Form"/>
</library:library> \ No newline at end of file