summaryrefslogtreecommitdiff
path: root/wizards
diff options
context:
space:
mode:
authorJean-Pierre Ledure <jp@ledure.be>2023-01-05 13:47:01 +0100
committerJean-Pierre Ledure <jp@ledure.be>2023-01-05 15:00:28 +0000
commit88a67c50af61fa9ee5fa677589ce5db2039d6f2e (patch)
tree51b6207a40f4bd15dc69bd820244b230d8177f34 /wizards
parentb4c3291630610d38270d7d8ccda5f810b3e05d63 (diff)
ScriptForge - (SFDocuments) new FormDocument service
The SF_FormDocument service is focused on : - The orchestration of Base form documents (aka Base Forms, but this is confusing) and the identification of and the access to their controls. - Form documents are always contained in a Base document. A form document may be opened either: - via code or user interface from the Base file welcome page - via code only, without having its Base container opened first In any mode, a form document can be opened only in 1 single copy. The FormDocument service is triggered either by base.OpenFormDocument(...) database.OpenFormDocument(...) ' Base file may be closed ui.GetDocument(...) Specific methods: CloseDocument() Forms() GetDatabase() PrintOut() Next methods are inherited from the Document superclass: Activate() CreateMenu(), RemoveMenu() ExportAsPdf() RunCommand() SaveCopyAs() SetPrinter() As a consequence, next methods remain available but should be declared as deprecated in the help: base.CloseFormDocument() base.Forms() base.PrintOut() base.SetPrinter() Above changes have several more minor impacts : - beside IsCalc, IsWriter, ... , a new IsFormDocument property - the UI service identifies open form documents - a new service means a new entry to register in the Services catalog - management of form events has been reviewed - the connection between Base, FormDocument, Form and Database services is reinforced - menus were available on components, now also on sub-components The new service is available for both Basic and Python user scripts. It requires in the help - a new sf_formdocument page - a review of the sf_base, sf_database, sf_form, sf_ui pages Change-Id: Ib06d1c4565ca093af2f068fa5b8082082641752e Reviewed-on: https://gerrit.libreoffice.org/c/core/+/145080 Tested-by: Jean-Pierre Ledure <jp@ledure.be> Reviewed-by: Jean-Pierre Ledure <jp@ledure.be> Tested-by: Jenkins
Diffstat (limited to 'wizards')
-rw-r--r--wizards/Package_sfdocuments.mk1
-rw-r--r--wizards/source/scriptforge/SF_PythonHelper.xba2
-rw-r--r--wizards/source/scriptforge/SF_Services.xba2
-rw-r--r--wizards/source/scriptforge/SF_UI.xba2
-rw-r--r--wizards/source/scriptforge/python/scriptforge.py67
-rw-r--r--wizards/source/sfdatabases/SF_Database.xba91
-rw-r--r--wizards/source/sfdocuments/SF_Base.xba36
-rw-r--r--wizards/source/sfdocuments/SF_Calc.xba6
-rw-r--r--wizards/source/sfdocuments/SF_Document.xba16
-rw-r--r--wizards/source/sfdocuments/SF_Form.xba73
-rw-r--r--wizards/source/sfdocuments/SF_FormControl.xba5
-rw-r--r--wizards/source/sfdocuments/SF_FormDocument.xba642
-rw-r--r--wizards/source/sfdocuments/SF_Register.xba8
-rw-r--r--wizards/source/sfdocuments/SF_Writer.xba10
-rw-r--r--wizards/source/sfdocuments/script.xlb1
-rw-r--r--wizards/source/sfwidgets/SF_MenuListener.xba7
16 files changed, 907 insertions, 62 deletions
diff --git a/wizards/Package_sfdocuments.mk b/wizards/Package_sfdocuments.mk
index a2a03178736a..4dcebfc84140 100644
--- a/wizards/Package_sfdocuments.mk
+++ b/wizards/Package_sfdocuments.mk
@@ -27,6 +27,7 @@ $(eval $(call gb_Package_add_files,wizards_basicsrvsfdocuments,$(LIBO_SHARE_FOLD
SF_DocumentListener.xba \
SF_Form.xba \
SF_FormControl.xba \
+ SF_FormDocument.xba \
SF_Register.xba \
SF_Writer.xba \
__License.xba \
diff --git a/wizards/source/scriptforge/SF_PythonHelper.xba b/wizards/source/scriptforge/SF_PythonHelper.xba
index 99d9f86c610c..ffec0af7586c 100644
--- a/wizards/source/scriptforge/SF_PythonHelper.xba
+++ b/wizards/source/scriptforge/SF_PythonHelper.xba
@@ -814,6 +814,8 @@ Try:
End Select
Case &quot;SFDocuments.FormControl&quot;
If Script = &quot;Controls&quot; Then vReturn = vBasicObject.Controls(vArgs(0))
+ Case &quot;SFDocuments.FormDocument&quot;
+ If Script = &quot;Forms&quot; Then vReturn = vBasicObject.Forms(vArgs(0))
End Select
End If
diff --git a/wizards/source/scriptforge/SF_Services.xba b/wizards/source/scriptforge/SF_Services.xba
index b72298ea3046..8de43d389581 100644
--- a/wizards/source/scriptforge/SF_Services.xba
+++ b/wizards/source/scriptforge/SF_Services.xba
@@ -126,7 +126,7 @@ Try:
sService = vSplit(0)
&apos; Accept other default values for associated libraries
Select Case LCase(sService)
- Case &quot;document&quot;, &quot;calc&quot;, &quot;writer&quot;, &quot;base&quot;, &quot;documentevent&quot;, &quot;formevent&quot;
+ Case &quot;document&quot;, &quot;calc&quot;, &quot;writer&quot;, &quot;base&quot;, &quot;formdocument&quot;, &quot;documentevent&quot;, &quot;formevent&quot;
sLibrary = &quot;SFDocuments&quot;
Case &quot;dialog&quot;, &quot;dialogevent&quot; : sLibrary = &quot;SFDialogs&quot;
Case &quot;database&quot;, &quot;datasheet&quot; : sLibrary = &quot;SFDatabases&quot;
diff --git a/wizards/source/scriptforge/SF_UI.xba b/wizards/source/scriptforge/SF_UI.xba
index 186d88244d75..4b96ac3274fc 100644
--- a/wizards/source/scriptforge/SF_UI.xba
+++ b/wizards/source/scriptforge/SF_UI.xba
@@ -69,6 +69,7 @@ Const WELCOMESCREEN = &quot;WELCOMESCREEN&quot;
Const BASEDOCUMENT = &quot;Base&quot;
Const CALCDOCUMENT = &quot;Calc&quot;
Const DRAWDOCUMENT = &quot;Draw&quot;
+Const FORMDOCUMENT = &quot;FormDocument&quot;
Const IMPRESSDOCUMENT = &quot;Impress&quot;
Const MATHDOCUMENT = &quot;Math&quot;
Const WRITERDOCUMENT = &quot;Writer&quot;
@@ -1303,6 +1304,7 @@ Dim FSO As Object &apos; Alias for SF_FileSystem
If SF_Session.HasUnoProperty(poComponent, &quot;Title&quot;) Then .WindowTitle = poComponent.Title
Select Case sIdentifier
Case &quot;com.sun.star.sdb.FormDesign&quot; &apos; Form
+ .DocumentType = FORMDOCUMENT
Case &quot;com.sun.star.sdb.TextReportDesign&quot; &apos; Report
Case &quot;com.sun.star.text.TextDocument&quot; &apos; Writer
.DocumentType = WRITERDOCUMENT
diff --git a/wizards/source/scriptforge/python/scriptforge.py b/wizards/source/scriptforge/python/scriptforge.py
index b3b79132d4b2..a15261caed58 100644
--- a/wizards/source/scriptforge/python/scriptforge.py
+++ b/wizards/source/scriptforge/python/scriptforge.py
@@ -1751,6 +1751,9 @@ class SFDatabases:
def GetRows(self, sqlcommand, directsql = False, header = False, maxrows = 0):
return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'GetRows', sqlcommand, directsql, header, maxrows)
+ def OpenFormDocument(self, formdocument):
+ return self.ExecMethod(self.vbMethod, 'OpenFormDocument', formdocument)
+
def OpenQuery(self, queryname):
return self.ExecMethod(self.vbMethod, 'OpenQuery', queryname)
@@ -1968,9 +1971,9 @@ class SFDocuments:
servicename = 'SFDocuments.Document'
servicesynonyms = ('document', 'sfdocuments.document')
serviceproperties = dict(Description = True, DocumentType = False, ExportFilters = False, ImportFilters = False,
- IsBase = False, IsCalc = False, IsDraw = False, IsImpress = False, IsMath = False,
- IsWriter = False, Keywords = True, Readonly = False, Subject = True, Title = True,
- XComponent = False)
+ IsBase = False, IsCalc = False, IsDraw = False, IsFormDocument = False,
+ IsImpress = False, IsMath = False, IsWriter = False, Keywords = True, Readonly = False,
+ Subject = True, Title = True, XComponent = False)
# Force for each property to get its value from Basic - due to intense interactivity with user
forceGetProperty = True
@@ -2029,8 +2032,8 @@ class SFDocuments:
serviceimplementation = 'basic'
servicename = 'SFDocuments.Base'
servicesynonyms = ('base', 'scriptforge.base')
- serviceproperties = dict(DocumentType = False, IsBase = False, IsCalc = False,
- IsDraw = False, IsImpress = False, IsMath = False, IsWriter = False,
+ serviceproperties = dict(DocumentType = False, IsBase = False, IsCalc = False, IsDraw = False,
+ IsFormDocument = False, IsImpress = False, IsMath = False, IsWriter = False,
XComponent = False)
@classmethod
@@ -2088,9 +2091,9 @@ class SFDocuments:
servicesynonyms = ('calc', 'sfdocuments.calc')
serviceproperties = dict(CurrentSelection = True, Sheets = False,
Description = True, DocumentType = False, ExportFilters = False, ImportFilters = False,
- IsBase = False, IsCalc = False, IsDraw = False, IsImpress = False, IsMath = False,
- IsWriter = False, Keywords = True, Readonly = False, Subject = True, Title = True,
- XComponent = False)
+ IsBase = False, IsCalc = False, IsDraw = False, IsFormDocument = False,
+ IsImpress = False, IsMath = False, IsWriter = False, Keywords = True, Readonly = False,
+ Subject = True, Title = True, XComponent = False)
# Force for each property to get its value from Basic - due to intense interactivity with user
forceGetProperty = True
@@ -2343,7 +2346,7 @@ class SFDocuments:
It includes the management of subforms
Each instance of the current class represents a single form or a single subform
A form may optionally be (understand "is often") linked to a data source manageable with
- the SFDatabases.Database service. The current service offers a rapid access to that service.
+ the SFDatabases.Database service. The current service offers rapid access to that service.
"""
# Mandatory class properties for service registration
serviceimplementation = 'basic'
@@ -2427,6 +2430,46 @@ class SFDocuments:
return self.ExecMethod(self.vbMethod, 'SetFocus')
# #########################################################################
+ # SF_FormDocument CLASS
+ # #########################################################################
+ class SF_FormDocument(SF_Document, SFServices):
+ """
+ The orchestration of Base form documents (aka Base Forms, but this is confusing)
+ and the identification of and the access to their controls.
+ Form documents are always contained in a Base document.
+ They should not be confused with Writer documents containing forms,
+ even if it is easy to convert the former to the latter.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.FormDocument'
+ servicesynonyms = ('formdocument', 'sfdocuments.formdocument')
+ serviceproperties = dict(DocumentType = False, IsBase = False, IsCalc = False, IsDraw = False,
+ IsFormDocument = False, IsImpress = False, IsMath = False, IsWriter = False,
+ Readonly = False, XComponent = False)
+
+ @classmethod
+ def ReviewServiceArgs(cls, windowname = ''):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return windowname,
+
+ def CloseDocument(self):
+ return self.ExecMethod(self.vbMethod, 'CloseDocument')
+
+ def Forms(self, form = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Forms', form)
+
+ def GetDatabase(self, user = '', password = ''):
+ return self.ExecMethod(self.vbMethod, 'GetDatabase', user, password)
+
+ def PrintOut(self, pages = '', copies = 1, printbackground = True, printblankpages = False,
+ printevenpages = True, printoddpages = True, printimages = True):
+ return self.ExecMethod(self.vbMethod, 'PrintOut', pages, copies, printbackground, printblankpages,
+ printevenpages, printoddpages, printimages)
+
+ # #########################################################################
# SF_Writer CLASS
# #########################################################################
class SF_Writer(SF_Document, SFServices):
@@ -2439,9 +2482,9 @@ class SFDocuments:
servicename = 'SFDocuments.Writer'
servicesynonyms = ('writer', 'sfdocuments.writer')
serviceproperties = dict(Description = True, DocumentType = False, ExportFilters = False, ImportFilters = False,
- IsBase = False, IsCalc = False, IsDraw = False, IsImpress = False, IsMath = False,
- IsWriter = False, Keywords = True, Readonly = False, Subject = True, Title = True,
- XComponent = False)
+ IsBase = False, IsCalc = False, IsDraw = False, IsFormDocument = False,
+ IsImpress = False, IsMath = False, IsWriter = False, Keywords = True, Readonly = False,
+ Subject = True, Title = True, XComponent = False)
# Force for each property to get its value from Basic - due to intense interactivity with user
forceGetProperty = True
diff --git a/wizards/source/sfdatabases/SF_Database.xba b/wizards/source/sfdatabases/SF_Database.xba
index f0dec87c294e..de891935be69 100644
--- a/wizards/source/sfdatabases/SF_Database.xba
+++ b/wizards/source/sfdatabases/SF_Database.xba
@@ -69,6 +69,8 @@ Private _MetaData As Object &apos; com.sun.star.sdbc.XDatabaseMetaData
REM ============================================================ MODULE CONSTANTS
+Const cstToken = &quot;//&quot; &apos; Form names accept special characters but not slashes
+
REM ===================================================== CONSTRUCTOR/DESTRUCTOR
REM -----------------------------------------------------------------------------
@@ -401,6 +403,7 @@ Public Function Methods() As Variant
, &quot;DMin&quot; _
, &quot;DSum&quot; _
, &quot;GetRows&quot; _
+ , &quot;OpenFormDocument&quot; _
, &quot;OpenQuery&quot; _
, &quot;OpenSql&quot; _
, &quot;OpenTable&quot; _
@@ -410,6 +413,56 @@ Public Function Methods() As Variant
End Function &apos; SFDatabases.SF_Database.Methods
REM -----------------------------------------------------------------------------
+Public Function OpenFormDocument(Optional ByVal FormDocument As Variant) As Object
+&apos;&apos;&apos; Open the FormDocument given by its hierarchical name in normal mode
+&apos;&apos;&apos; If the form document is already open, the form document is made active
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FormDocument: a valid form document name as a case-sensitive string
+&apos;&apos;&apos; When hierarchical, the hierarchy must be rendered with forward slashes (&quot;/&quot;)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A FormDocument instance or Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Form name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Set oForm = oDb.OpenFormDocument(&quot;Folder1/myFormDocument&quot;)
+
+Dim oOpen As Object &apos; Return value
+Dim oFormDocuments As Variant &apos; com.sun.star.comp.dba.ODocumentContainer
+Dim vFormNames As Variant &apos; Array of all document form names present in the document
+Dim vOpenArgs As Variant &apos; Array of property values
+Dim oNewForm As Object &apos; Output of loadComponent()
+Const cstThisSub = &quot;SFDatabases.Database.OpenFormDocument&quot;
+Const cstSubArgs = &quot;FormDocument&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ &apos; Build list of available FormDocuments recursively with _CollectFormDocuments
+ Set oFormDocuments = _Connection.Parent.DataBaseDocument.FormDocuments
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ vFormNames = Split(_CollectFormDocuments(oFormDocuments), cstToken)
+ If Not ScriptForge.SF_Utils._Validate(FormDocument, &quot;FormDocument&quot;, V_STRING, vFormNames) Then GoTo Finally
+ End If
+
+Try:
+ vOpenArgs = Array(SF_Utils._MakePropertyValue(&quot;ActiveConnection&quot;, _Connection) _
+ , SF_Utils._MakePropertyValue(&quot;OpenMode&quot;, &quot;open&quot;) _
+ )
+ Set oNewForm = oFormDocuments.loadComponentFromURL(FormDocument, &quot;&quot;, 0, vOpenArgs)
+
+ Set oOpen = ScriptForge.SF_Services.CreateScriptService(&quot;SFDocuments.FormDocument&quot;, oNewForm)
+
+Finally:
+ Set OpenFormDocument = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Databases.SF_Database.OpenFormDocument
+
+REM -----------------------------------------------------------------------------
Public Function OpenQuery(Optional ByVal QueryName As Variant) As Object
&apos;&apos;&apos; Open the query given by its name
&apos;&apos;&apos; The datasheet will live independently from any other (typically Base) component
@@ -632,6 +685,44 @@ End Function &apos; SFDatabases.SF_Database.SetProperty
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 whole 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; Return value
+Dim oSubItem As Object &apos; com.sun.star.container.XNameAccess (folder) or com.sun.star.ucb.XContent (form)
+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:
+ If Len(sCollectNames) &gt; 0 Then
+ _CollectFormDocuments = Mid(sCollectNames, Len(cstToken) + 1) &apos; Skip the initial token
+ Else
+ _CollectFormDocuments = &quot;&quot;
+ End If
+ Exit Function
+End Function &apos; SFDocuments.SF_Base._CollectFormDocuments
+
REM -----------------------------------------------------------------------------------------------------------------------
Private Function _DFunction(ByVal psFunction As String _
, Optional ByVal pvExpression As Variant _
diff --git a/wizards/source/sfdocuments/SF_Base.xba b/wizards/source/sfdocuments/SF_Base.xba
index 8970b7c05b96..24d1e1b0997b 100644
--- a/wizards/source/sfdocuments/SF_Base.xba
+++ b/wizards/source/sfdocuments/SF_Base.xba
@@ -145,6 +145,7 @@ Public Function CloseFormDocument(Optional ByVal FormDocument As Variant) As Boo
&apos;&apos;&apos; True if closure is successful
&apos;&apos;&apos; Example:
&apos;&apos;&apos; oDoc.CloseFormDocument(&quot;Folder1/myFormDocument&quot;)
+&apos;&apos;&apos; DEPRECATED - Use preferably the CloseDocument() method of the FormDocument service
Dim bClose As Boolean &apos; Return value
Dim oMainForm As Object &apos; com.sun.star.comp.sdb.Content
@@ -235,6 +236,7 @@ Public Function Forms(Optional ByVal FormDocument As Variant _
&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)
+&apos;&apos;&apos; DEPRECATED - Use preferably the Forms() method of the FormDocument service
Dim oForm As Object &apos; The new Form class instance
Dim oFormDocument As Object &apos; com.sun.star.comp.sdb.Content
@@ -283,7 +285,8 @@ Try:
._Name = oXForm.Name
Set .[Me] = oForm
Set .[_Parent] = [Me]
- Set ._Component = _Component
+ Set ._Component = oFormDocument.Component
+ Set ._BaseComponent = _Component
._FormDocumentName = FormDocument
Set ._FormDocument = oFormDocument
._FormType = ISBASEFORM
@@ -455,28 +458,28 @@ End Function &apos; SFDocuments.SF_Base.Methods
REM -----------------------------------------------------------------------------
Public Function OpenFormDocument(Optional ByVal FormDocument As Variant _
, Optional ByVal DesignMode As Variant _
- ) As Boolean
+ ) As Object
&apos;&apos;&apos; Open the FormDocument given by its hierarchical name either in normal or in design mode
&apos;&apos;&apos; If the form document is already open, the form document is made active without changing its mode
&apos;&apos;&apos; Args:
&apos;&apos;&apos; FormDocument: a valid form document name as a case-sensitive string
+&apos;&apos;&apos; When hierarchical, the hierarchy must be rendered with forward slashes (&quot;/&quot;)
&apos;&apos;&apos; DesignMode: when True the form document is opened in design mode (Default = False)
&apos;&apos;&apos; Returns:
-&apos;&apos;&apos; True if the form document could be opened, otherwise False
+&apos;&apos;&apos; A FormDocument instance or Nothing
&apos;&apos;&apos; Exceptions:
&apos;&apos;&apos; Form name is invalid
&apos;&apos;&apos; Example:
-&apos;&apos;&apos; oDoc.OpenFormDocument(&quot;Folder1/myFormDocument&quot;)
+&apos;&apos;&apos; Set oForm = oDoc.OpenFormDocument(&quot;Folbder1/myFormDocument&quot;)
-Dim bOpen As Boolean &apos; Return value
+Dim oOpen As Object &apos; Return value
Dim vFormNames As Variant &apos; Array of all document form names present in the document
-Dim oContainer As Object &apos; com.sun.star.awt.XWindow
Dim oNewForm As Object &apos; Output of loadComponent()
Const cstThisSub = &quot;SFDocuments.Base.OpenFormDocument&quot;
Const cstSubArgs = &quot;FormDocument, [DesignMode=False]&quot;
If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
- bOpen = False
+ Set oOpen = Nothing
Check:
If IsMissing(DesignMode) Or IsEmpty(DesignMode) Then DesignMode = False
@@ -499,10 +502,11 @@ Try:
If .isFormDesignMode() &lt;&gt; DesignMode Then .setFormDesignMode(DesignMode)
End With
End With
- bOpen = True
+
+ Set oOpen = ScriptForge.SF_Services.CreateScriptService(&quot;SFDocuments.FormDocument&quot;, oNewForm)
Finally:
- OpenFormDocument = bOpen
+ Set OpenFormDocument = oOpen
ScriptForge.SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
@@ -632,6 +636,7 @@ Public Function PrintOut(Optional ByVal FormDocument As Variant _
&apos;&apos;&apos; True when successful
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; oDoc.PrintOut(&quot;myForm&quot;, &quot;1-4;10;15-18&quot;, Copies := 2)
+&apos;&apos;&apos; DEPRECATED - Use preferably the PrintOut() method of the FormDocument service
Dim bPrint As Boolean &apos; Return value
Dim vFormNames As Variant &apos; Array of all document form names present in the document
@@ -681,6 +686,7 @@ Public Function Properties() As Variant
, &quot;IsBase&quot; _
, &quot;IsCalc&quot; _
, &quot;IsDraw &quot; _
+ , &quot;IsFormDocument&quot; _
, &quot;IsImpress&quot; _
, &quot;IsMath&quot; _
, &quot;IsWriter&quot; _
@@ -708,6 +714,7 @@ Public Function SetPrinter(Optional ByVal FormDocument As Variant _
&apos;&apos;&apos; True when successful
&apos;&apos;&apos; Examples:
&apos;&apos;&apos; oDoc.SetPrinter(&quot;myForm&quot;, Orientation := &quot;PORTRAIT&quot;)
+&apos;&apos;&apos; DEPRECATED - Use preferably the SetPrinter() method of the FormDocument service
Dim bPrinter As Boolean &apos; Return value
Dim vFormDocuments As Variant &apos; Array of form documents
@@ -830,6 +837,11 @@ Property Get IsDraw() As Boolean
End Property &apos; SFDocuments.SF_Base.IsDraw
REM -----------------------------------------------------------------------------
+Property Get IsFormDocument() As Boolean
+ IsFormDocument = [_Super].GetProperty(&quot;IsFormDocument&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsFormDocument
+
+REM -----------------------------------------------------------------------------
Property Get IsImpress() As Boolean
IsImpress = [_Super].GetProperty(&quot;IsImpress&quot;)
End Property &apos; SFDocuments.SF_Base.IsImpress
@@ -968,7 +980,11 @@ Try:
End With
Finally:
- _CollectFormDocuments = Mid(sCollectNames, Len(cstToken) + 1) &apos; Skip the initial token
+ If Len(sCollectNames) &gt; 0 Then
+ _CollectFormDocuments = Mid(sCollectNames, Len(cstToken) + 1) &apos; Skip the initial token
+ Else
+ _CollectFormDocuments = &quot;&quot;
+ End If
Exit Function
End Function &apos; SFDocuments.SF_Base._CollectFormDocuments
diff --git a/wizards/source/sfdocuments/SF_Calc.xba b/wizards/source/sfdocuments/SF_Calc.xba
index 0733be07eb6a..806f30bd7cad 100644
--- a/wizards/source/sfdocuments/SF_Calc.xba
+++ b/wizards/source/sfdocuments/SF_Calc.xba
@@ -2556,6 +2556,7 @@ Public Function Properties() As Variant
, &quot;IsBase&quot; _
, &quot;IsCalc&quot; _
, &quot;IsDraw&quot; _
+ , &quot;IsFormDocument&quot; _
, &quot;IsImpress&quot; _
, &quot;IsMath&quot; _
, &quot;IsWriter&quot; _
@@ -3483,6 +3484,11 @@ Property Get IsDraw() As Boolean
End Property &apos; SFDocuments.SF_Calc.IsDraw
REM -----------------------------------------------------------------------------
+Property Get IsFormDocument() As Boolean
+ IsFormDocument = [_Super].GetProperty(&quot;IsFormDocument&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsFormDocument
+
+REM -----------------------------------------------------------------------------
Property Get IsImpress() As Boolean
IsImpress = [_Super].GetProperty(&quot;IsImpress&quot;)
End Property &apos; SFDocuments.SF_Calc.IsImpress
diff --git a/wizards/source/sfdocuments/SF_Document.xba b/wizards/source/sfdocuments/SF_Document.xba
index bba8b3c21bd5..03fb215369ea 100644
--- a/wizards/source/sfdocuments/SF_Document.xba
+++ b/wizards/source/sfdocuments/SF_Document.xba
@@ -269,6 +269,11 @@ Property Get IsDraw() As Boolean
End Property &apos; SFDocuments.SF_Document.IsDraw
REM -----------------------------------------------------------------------------
+Property Get IsFormDocument() As Boolean
+ IsFormDocument = _PropertyGet(&quot;IsFormDocument&quot;)
+End Property &apos; SFDocuments.SF_Document.IsFormDocument
+
+REM -----------------------------------------------------------------------------
Property Get IsImpress() As Boolean
IsImpress = _PropertyGet(&quot;IsImpress&quot;)
End Property &apos; SFDocuments.SF_Document.IsImpress
@@ -459,7 +464,7 @@ Try:
If SaveAsk And _Component.IsModified Then &apos; Execute closure with the File/Close menu command
Activate()
RunCommand(&quot;CloseDoc&quot;)
- bClosed = _IsStillAlive(, False) &apos; Do not raise error
+ bClosed = Not _IsStillAlive(, False) &apos; Do not raise error
Else
_Frame.close(True)
_Frame.dispose()
@@ -472,6 +477,7 @@ Finally:
ScriptForge.SF_Utils._ExitFunction(cstThisSub)
Exit Function
Catch:
+ On Local Error GoTo 0
GoTo Finally
End Function &apos; SFDocuments.SF_Document.CloseDocument
@@ -592,7 +598,10 @@ Check:
Try:
&apos; Setup arguments
- sFilter = LCase(_DocumentType) &amp; &quot;_pdf_Export&quot;
+ Select Case _DocumentType &apos; Disguise form documents as a Writer document
+ Case &quot;FormDocument&quot; : sFilter = &quot;Writer_pdf_Export&quot;
+ Case Else : sFilter = LCase(_DocumentType) &amp; &quot;_pdf_Export&quot;
+ End Select
&apos; FilterData parameters are added only if they are meaningful
vFilterData = Array()
If Len(Pages) &gt; 0 Then
@@ -751,6 +760,7 @@ Public Function Properties() As Variant
, &quot;IsBase&quot; _
, &quot;IsCalc&quot; _
, &quot;IsDraw&quot; _
+ , &quot;IsFormDocument&quot; _
, &quot;IsImpress&quot; _
, &quot;IsMath&quot; _
, &quot;IsWriter&quot; _
@@ -1471,7 +1481,7 @@ Const cstSubArgs = &quot;&quot;
_PropertyGet = _GetFilterNames(True)
Case &quot;ImportFilters&quot;
_PropertyGet = _GetFilterNames(False)
- Case &quot;IsBase&quot;, &quot;IsCalc&quot;, &quot;IsDraw&quot;, &quot;IsImpress&quot;, &quot;IsMath&quot;, &quot;IsWriter&quot;
+ Case &quot;IsBase&quot;, &quot;IsCalc&quot;, &quot;IsDraw&quot;, &quot;IsFormDocument&quot;, &quot;IsImpress&quot;, &quot;IsMath&quot;, &quot;IsWriter&quot;
_PropertyGet = ( Mid(psProperty, 3) = _DocumentType )
Case &quot;Keywords&quot;
_PropertyGet = Join(_Component.DocumentProperties.Keywords, &quot;, &quot;)
diff --git a/wizards/source/sfdocuments/SF_Form.xba b/wizards/source/sfdocuments/SF_Form.xba
index 2879536efb6f..10b84622d5e5 100644
--- a/wizards/source/sfdocuments/SF_Form.xba
+++ b/wizards/source/sfdocuments/SF_Form.xba
@@ -23,8 +23,8 @@ Option Explicit
&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; FormDocument: BASE DOCUMENTS ONLY
+&apos;&apos;&apos; For usual documents, there is only 1 forms cpntainer. It is either the document itself or one of its sheets (Calc)
&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; or &quot;Base 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
@@ -34,29 +34,36 @@ Option Explicit
&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; Is an abstract set of Controls in an OPEN Document or FormDocument
&apos;&apos;&apos; Each form is usually linked to one single 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; A form is defined by its owning Document or 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; REM the form is stored in a Writer document
&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 a Calc document
+&apos;&apos;&apos; Dim oCalc As Object, myForm As Object
+&apos;&apos;&apos; Set oCalc = CreateScriptService(&quot;SFDocuments.Document&quot;, ThisComponent)
+&apos;&apos;&apos; Set myForm = oCalc.Forms(&quot;Sheet1&quot;, &quot;Form1&quot;)
+&apos;&apos;&apos; &apos; or, alternatively, when there is only 1 form
+&apos;&apos;&apos; Set myForm = oCalc.Forms(&quot;Sheet1&quot;, 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; Dim oBase As Object, myFormDoc As Object, myForm As Object, mySubForm As Object
+&apos;&apos;&apos; Set oBase = CreateScriptService(&quot;SFDocuments.Document&quot;, ThisDatabaseDocument)
+&apos;&apos;&apos; Set oFormDoc = oBase.OpenFormDocument(&quot;thisFormDocument&quot;)
+&apos;&apos;&apos; Set myForm = oFormDoc.Forms(&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; Set myForm = oFormDoc.Forms(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;
@@ -87,9 +94,10 @@ Private _Name As String &apos; Internal name of the form
Private _FormType As Integer &apos; One of the ISxxxFORM constants
Private _SheetName As String &apos; Name as the sheet containing the form (Calc only)
Private _FormDocumentName As String &apos; The hierarchical name of the containing form document (Base only)
-Private _FormDocument As Object &apos; com.sun.star.comp.sdb.Content - the containing form document
- &apos; The form topmost container
-Private _Component As Object &apos; com.sun.star.lang.XComponent or com.sun.star.comp.dba.ODatabaseDocument
+Private _FormDocument As Object &apos; com.sun.star.comp.sdb.Content - the form document container
+&apos; The form topmost containers
+Private _Component As Object &apos; com.sun.star.lang.XComponent
+Private _BaseComponent As Object &apos; com.sun.star.comp.dba.ODatabaseDocument
&apos; Events management
Private _CacheIndex As Long &apos; Index in central cache storage
@@ -98,9 +106,9 @@ Private _CacheIndex As Long &apos; Index in central cache storage
&apos; The entry to the interactions with the form. Validity checked 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
+Private _Database As Object &apos; Database class instance
&apos; Cache storage for controls
Private _ControlNames As Variant &apos; Array of control names
@@ -110,7 +118,7 @@ REM ============================================================ MODULE CONSTANT
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 ISBASEFORM = 3 &apos; Form is stored in a Base form document
Const ISSUBFORM = 4 &apos; Form is a subform of a form or of another subform
Const ISUNDEFINED = -1 &apos; Undefined form type
@@ -126,6 +134,8 @@ Private Sub Class_Initialize()
_SheetName = &quot;&quot;
_FormDocumentName = &quot;&quot;
Set _FormDocument = Nothing
+ Set _Component = Nothing
+ Set _BaseComponent = Nothing
_FormType = ISUNDEFINED
_CacheIndex = -1
Set _Form = Nothing
@@ -535,11 +545,14 @@ Check:
End If
Try:
Select Case _FormType
- Case ISDOCFORM, ISCALCFORM, ISSUBFORM
- Case ISBASEFORM
- _FormDocument.close()
- Dispose()
- bClose = True
+ Case ISDOCFORM, ISCALCFORM
+ Case ISBASEFORM, ISSUBFORM
+ If Not IsNull(_FormDocument) Then
+ _FormDocument.close()
+ Dispose()
+ bClose = True
+ End If
+ Case Else
End Select
Finally:
@@ -673,7 +686,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 [_Parent]._IsStillAlive(True) Then GoTo Finally
+ If Not [_Parent]._IsStillAlive(False) 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
@@ -691,7 +704,7 @@ Try:
&apos; Connect to database, avoiding multiple requests
If IsNull(_Database) Then &apos; 1st connection request from the current form instance
- If _FormType = ISBASEFORM Then
+ If _FormType = ISBASEFORM And Not IsNull(_BaseComponent) Then
&apos; Fetch the shared connection
Set _Database = [_Parent].GetDatabase(User, Password)
ElseIf _FormType = ISSUBFORM Then
@@ -1123,6 +1136,7 @@ Try:
Set .[_Parent] = [Me]
._FormType = ISSUBFORM
Set ._Component = _Component
+ Set ._BaseComponent = _BaseComponent
Set ._FormDocument = _FormDocument
._SheetName = _SheetName
._FormDocumentName = _FormDocumentName
@@ -1208,8 +1222,8 @@ Private Sub _GetParents()
Dim oParent As Object &apos; Successive bottom-up parents
Dim sType As String &apos; UNO object type
-Dim sPersistentName As String &apos; The Obj... name of a Base form
Dim iLevel As Integer &apos; When = 1 =&gt; first parent
+Dim oBase As Object &apos; Empty Base instance
Dim oSession As Object : Set oSession = ScriptForge.SF_Session
On Local Error GoTo Finally &apos; Being probably called from events, this method should avoid failures
@@ -1243,12 +1257,15 @@ Try:
Case &quot;com.sun.star.form.OFormsCollection&quot; &apos; The collection of forms inside a drawpage
Case &quot;SwXTextDocument&quot; &apos; The parent document: a Writer document or a Base form document
If oParent.Identifier = &quot;com.sun.star.sdb.FormDesign&quot; Then
- sPersistentName = ScriptForge._GetPropertyValue(oParent.Args, &quot;HierarchicalDocumentName&quot;)
+ _FormType = ISBASEFORM
+ &apos; Make a new SF_FormDocument instance
+ Set [_Parent] = ScriptForge.SF_Services.CreateScriptService(&quot;SFDocuments.FormDocument&quot;, oParent)
+ Set _FormDocument = [_Parent]._FormDocument
ElseIf oParent.Identifier = &quot;com.sun.star.text.TextDocument&quot; Then
_FormType = ISDOCFORM
Set [_Parent] = ScriptForge.SF_Services.CreateScriptService(&quot;SFDocuments.Document&quot;, oParent)
- Set _Component = [_Parent]._Component
End If
+ Set _Component = oParent
Case &quot;ScModelObj&quot; &apos; The parent document: a Calc document
_FormType = ISCALCFORM
Set [_Parent] = ScriptForge.SF_Services.CreateScriptService(&quot;SFDocuments.Document&quot;, oParent)
@@ -1256,12 +1273,6 @@ Try:
&apos; The triggered form event is presumed to be located in the (drawpage of the) active sheet
_SheetName = [_Parent].XSpreadsheet(&quot;~&quot;)
Case &quot;com.sun.star.comp.dba.ODatabaseDocument&quot; &apos; The Base document
- _FormType = ISBASEFORM
- Set [_Parent] = ScriptForge.SF_Services.CreateScriptService(&quot;SFDocuments.Document&quot;, oParent)
- Set _Component = oParent
- If IsNull([_Parent]._FormDocuments) Then Set [_Parent]._FormDocuments = _Component.getFormDocuments()
- Set _FormDocument = [_Parent]._FindByPersistentName([_Parent]._FormDocuments, sPersistentName)
- _FormDocumentName = _FormDocument.HierarchicalName
Case Else
End Select
If oSession.HasUnoProperty(oParent, &quot;Parent&quot;) Then Set oParent = oParent.Parent Else Set oParent = Nothing
diff --git a/wizards/source/sfdocuments/SF_FormControl.xba b/wizards/source/sfdocuments/SF_FormControl.xba
index a48c22b6c1c5..180cb682b381 100644
--- a/wizards/source/sfdocuments/SF_FormControl.xba
+++ b/wizards/source/sfdocuments/SF_FormControl.xba
@@ -13,7 +13,8 @@ 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_FormControl
-&apos;&apos;&apos; ================
+&apos;&apos;&apos; ==============
+&apos;&apos;&apos;
&apos;&apos;&apos; Manage the controls belonging to a form or subform stored in a document
&apos;&apos;&apos; Each instance of the current class represents a single control within a form, a subform or a tablecontrol
&apos;&apos;&apos; A prerequisite is that all controls within the same form, subform or tablecontrol must have
@@ -1131,7 +1132,7 @@ Try:
End With
&apos; Store the SF_FormControl object in the parent cache
- Set _Parent._ControlCache(_IndexOfNames) = [Me]
+ Set [_Parent]._ControlCache(_IndexOfNames) = [Me]
Finally:
Exit Sub
diff --git a/wizards/source/sfdocuments/SF_FormDocument.xba b/wizards/source/sfdocuments/SF_FormDocument.xba
new file mode 100644
index 000000000000..8d13d86e96e4
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_FormDocument.xba
@@ -0,0 +1,642 @@
+<?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_FormDocument" 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_FormDocument
+&apos;&apos;&apos; ===============
+&apos;&apos;&apos;
+&apos;&apos;&apos; The SFDocuments library gathers a number of methods and properties making easy
+&apos;&apos;&apos; managing and manipulating LibreOffice documents
+&apos;&apos;&apos;
+&apos;&apos;&apos; Some methods are generic for all types of documents: they are combined in the SF_Document module.
+&apos;&apos;&apos; Specific properties and methods are implemented in the concerned subclass(es) SF_Calc, SF_Writer, SF_Base, ...
+&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.
+&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_FormDocument module is focused on :
+&apos;&apos;&apos; The orchestration of Base form documents (aka Base Forms, but this is confusing)
+&apos;&apos;&apos; and the identification of and the access to their controls.
+&apos;&apos;&apos; Form documents are always contained in a Base document.
+&apos;&apos;&apos; They should not be confused with Writer documents containing forms,
+&apos;&apos;&apos; even if it is easy to convert the former to the latter.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The current module is closely related to
+&apos;&apos;&apos; the &quot;Base&quot; service of the current library
+&apos;&apos;&apos; the &quot;Database&quot; service of the SFDatabases library
+&apos;&apos;&apos;
+&apos;&apos;&apos; A form document may be opened either:
+&apos;&apos;&apos; via code or user interface from the Base file welcome page
+&apos;&apos;&apos; via code only, without having its Base container opened first
+&apos;&apos;&apos; The Base document remains hidden but the user might be prompted about the macro execution mode
+&apos;&apos;&apos; In any mode, a form document can be opened only in 1 single copy
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocation examples:
+&apos;&apos;&apos; 1) From the Base service
+&apos;&apos;&apos; Dim oBase As Object, oFormDoc As Object
+&apos;&apos;&apos; &apos; oBase is presumed to represent an open Base document
+&apos;&apos;&apos; Set oFormDoc = oBade.OpenFormDocument(&quot;Folder1/Form1&quot;)
+&apos;&apos;&apos; 2) Directly without making the Base document visible
+&apos;&apos;&apos; Dim oDatabase As Object, oFormDoc As Object
+&apos;&apos;&apos; Set oDatabase = CreateScriptService(&quot;SFDatabases.Database&quot;, &quot;.../myFile.odb&quot;, ReadOnly := False)
+&apos;&apos;&apos; &apos; The substring &quot;SFDatabases.&quot; in the service name is optional
+&apos;&apos;&apos; Set oFormDoc = oDatabase.OpenFormDocument(&quot;Folder1/Form1&quot;)
+&apos;&apos;&apos;
+&apos;&apos;&apos; Definitions:
+&apos;&apos;&apos; None
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_formdocument.html?DbPAR=BASIC
+&apos;&apos;&apos;
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+
+REM ================================================================== EXCEPTIONS
+
+Private Const BASEFORMNOTFOUNDERROR = &quot;BASEFORMNOTFOUNDERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object &apos; Unused
+Private [_Super] As Object &apos; Document superclass, which the current instance is a subclass of
+Private ObjectType As String &apos; Must be FormDocument
+Private ServiceName As String
+
+&apos; Components
+Private _Component As Object &apos; com.sun.star.lang.XComponent
+Private _BaseComponent As Object &apos; com.sun.star.comp.dba.ODatabaseDocument
+Private _FormDocument As Object &apos; com.sun.star.comp.sdb.Content
+
+&apos; Form document description
+Private _PersistentName As String &apos; Typically Objxx
+Private _HierarchicalName As String
+Private _DataSource As Object &apos; com.sun.star.sdbc.XDataSource
+Private _User As String &apos; Credentials
+Private _Password As String
+
+REM ============================================================ MODULE CONSTANTS
+
+Const ISBASEFORM = 3 &apos; Form is stored in a Form document
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ Set [_Super] = Nothing
+ ObjectType = &quot;FormDocument&quot;
+ ServiceName = &quot;SFDocuments.FormDocument&quot;
+ Set _Component = Nothing
+ Set _BaseComponent = Nothing
+ Set _FormDocument = Nothing
+ Set _DataSource = Nothing
+ Set _Database = Nothing
+ _PersistentName = &quot;&quot;
+ _HierarchicalName = &quot;&quot;
+End Sub &apos; SFDocuments.SF_FormDocument Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDocuments.SF_FormDocument Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ If Not IsNull([_Super]) Then Set [_Super] = [_Super].Dispose()
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDocuments.SF_FormDocument Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function CloseDocument() As Boolean
+&apos;&apos;&apos; Close the form document and dispose the actual instance
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if closure is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myFormDoc.CloseDocument()
+
+Dim bClose As Boolean &apos; Return value
+Dim oContainer As Object &apos; com.sun.star.awt.XWindow
+Const cstThisSub = &quot;SFDocuments.FormDocument.CloseDocument&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bClose = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ End If
+Try:
+ _FormDocument.close()
+ Dispose()
+ bClose = True
+
+Finally:
+ CloseDocument = bClose
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_FormDocument.CloseDocument
+
+REM -----------------------------------------------------------------------------
+Public Function Forms(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; 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; BASEFORMNOTFOUNDERROR Form not found
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-based 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 FormDocument document
+
+Const cstThisSub = &quot;SFDocuments.FormDocument.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_Array.Contains(vFormNames, Form, CaseSensitive := True) Then GoTo CatchNotFound
+ 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 = SF_Register._NewForm(oXForm)
+ With oForm
+ Set .[_Parent] = [Me]
+ ._FormType = ISBASEFORM
+ Set ._Component = _Component
+ Set ._BaseComponent = _BaseComponent
+ ._FormDocumentName = _HierarchicalName
+ ._FormDocument = _FormDocument
+ ._Initialize()
+ End With
+ Set Forms = oForm
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchNotFound:
+ ScriptForge.SF_Exception.RaiseFatal(BASEFORMNOTFOUNDERROR, Form, _FileIdent())
+End Function &apos; SFDocuments.SF_FormDocument.Forms
+
+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 form document
+&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 = oFormDoc.GetDatabase()
+
+Dim oDatabase As Object &apos; Return value
+Const cstThisSub = &quot;SFDocuments.FormDocument.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 oDatabase = 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 _IsStillAlive(False) 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:
+ If IsNull(_DataSource) Then GoTo CatchConnect
+ Set oDatabase = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.DatabaseFromDocument&quot; _
+ , _DataSource, Iif(User = &quot;&quot;, _User, User), Iif(Password = &quot;&quot;, _Password, &quot;&quot;))
+ If IsNull(oDatabase) Then GoTo CatchConnect
+ oDatabase._Location = _DataSource.Name
+
+Finally:
+ Set GetDatabase = oDatabase
+ 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_FormDocument.GetDatabase
+
+REM -----------------------------------------------------------------------------
+Public Function GetProperty(Optional ByVal PropertyName As Variant _
+ , Optional ObjectName 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; ObjectName: a sheet or range name
+&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
+
+Const cstThisSub = &quot;SFDocuments.FormDocument.GetProperty&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ GetProperty = Null
+
+Check:
+ If IsMissing(ObjectName) Or IsEmpty(ObjectName) Then ObjectName = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(PropertyName, &quot;PropertyName&quot;, V_STRING, Properties()) Then GoTo Catch
+ If Not ScriptForge.SF_Utils._Validate(ObjectName, &quot;ObjectName&quot;, V_STRING) Then GoTo Catch
+ End If
+
+Try:
+ &apos; Superclass or subclass property ?
+ If ScriptForge.SF_Array.Contains([_Super].Properties(), PropertyName) Then
+ GetProperty = [_Super].GetProperty(PropertyName)
+ ElseIf Len(ObjectName) = 0 Then
+ GetProperty = _PropertyGet(PropertyName)
+ Else
+ GetProperty = _PropertyGet(PropertyName, ObjectName)
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_FormDocument.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the FormDocument service as an array
+
+ Methods = Array( _
+ &quot;CloseDocument&quot; _
+ , &quot;Forms&quot; _
+ , &quot;PrintOut&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_FormDocument.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function PrintOut(Optional ByVal Pages As Variant _
+ , Optional ByVal Copies As Variant _
+ , Optional ByVal PrintBackground As Variant _
+ , Optional ByVal PrintBlankPages As Variant _
+ , Optional ByVal PrintEvenPages As Variant _
+ , Optional ByVal PrintOddPages As Variant _
+ , Optional ByVal PrintImages As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Send the content of the document to the printer.
+&apos;&apos;&apos; The printer might be defined previously by default, by the user or by the SetPrinter() method
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Pages: the pages to print as a string, like in the user interface. Example: &quot;1-4;10;15-18&quot;. Default = all pages
+&apos;&apos;&apos; Copies: the number of copies
+&apos;&apos;&apos; PrintBackground: print the background image when True (default)
+&apos;&apos;&apos; PrintBlankPages: when False (default), omit empty pages
+&apos;&apos;&apos; PrintEvenPages: print the left pages when True (default)
+&apos;&apos;&apos; PrintOddPages: print the right pages when True (default)
+&apos;&apos;&apos; PrintImages: print the graphic objects when True (default)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.PrintOut(&quot;1-4;10;15-18&quot;, Copies := 2, PrintImages := False)
+
+Dim bPrint As Boolean &apos; Return value
+Dim vPrintOptions As Variant &apos; com.sun.star.text.DocumentSettings
+
+Const cstThisSub = &quot;SFDocuments.FormDocument.PrintOut&quot;
+Const cstSubArgs = &quot;[Pages=&quot;&quot;&quot;&quot;], [Copies=1], [PrintBackground=True], [PrintBlankPages=False], [PrintEvenPages=True]&quot; _
+ &amp; &quot;, [PrintOddPages=True], [PrintImages=True]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bPrint = False
+
+Check:
+ If IsMissing(Pages) Or IsEmpty(Pages) Then Pages = &quot;&quot;
+ If IsMissing(Copies) Or IsEmpty(Copies) Then Copies = 1
+ If IsMissing(PrintBackground) Or IsEmpty(PrintBackground) Then PrintBackground = True
+ If IsMissing(PrintBlankPages) Or IsEmpty(PrintBlankPages) Then PrintBlankPages = False
+ If IsMissing(PrintEvenPages) Or IsEmpty(PrintEvenPages) Then PrintEvenPages = True
+ If IsMissing(PrintOddPages) Or IsEmpty(PrintOddPages) Then PrintOddPages = True
+ If IsMissing(PrintImages) Or IsEmpty(PrintImages) Then PrintImages = True
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Pages, &quot;Pages&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Copies, &quot;Copies&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(PrintBackground, &quot;PrintBackground&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(PrintBlankPages, &quot;PrintBlankPages&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(PrintEvenPages, &quot;PrintEvenPages&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(PrintOddPages, &quot;PrintOddPages&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(PrintImages, &quot;PrintImages&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ vPrintOptions = _Component.createInstance(&quot;com.sun.star.text.DocumentSettings&quot;)
+ With vPrintOptions
+ .PrintPageBackground = PrintBackground
+ .PrintEmptyPages = PrintBlankPages
+ .PrintLeftPages = PrintEvenPages
+ .PrintRightPages = PrintOddPages
+ .PrintGraphics = PrintImages
+ .PrintDrawings = PrintImages
+ End With
+
+ bPrint = [_Super].PrintOut(Pages, Copies, _Component)
+
+Finally:
+ PrintOut = bPrint
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_FormDocument.PrintOut
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the FormDocument class as an array
+
+ Properties = Array( _
+ &quot;DocumentType&quot; _
+ , &quot;IsBase&quot; _
+ , &quot;IsCalc&quot; _
+ , &quot;IsDraw&quot; _
+ , &quot;IsFormDocument&quot; _
+ , &quot;IsImpress&quot; _
+ , &quot;IsMath&quot; _
+ , &quot;Readonly&quot; _
+ , &quot;XComponent&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_FormDocument.Properties
+
+REM ======================================================= SUPERCLASS PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get IsBase() As Boolean
+ IsBase = [_Super].GetProperty(&quot;IsBase&quot;)
+End Property &apos; SFDocuments.SF_FormDocument.IsBase
+
+REM -----------------------------------------------------------------------------
+Property Get IsCalc() As Boolean
+ IsCalc = [_Super].GetProperty(&quot;IsCalc&quot;)
+End Property &apos; SFDocuments.SF_FormDocument.IsCalc
+
+REM -----------------------------------------------------------------------------
+Property Get IsDraw() As Boolean
+ IsDraw = [_Super].GetProperty(&quot;IsDraw&quot;)
+End Property &apos; SFDocuments.SF_FormDocument.IsDraw
+
+REM -----------------------------------------------------------------------------
+Property Get IsFormDocument() As Boolean
+ IsFormDocument = [_Super].GetProperty(&quot;IsFormDocument&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsFormDocument
+
+REM -----------------------------------------------------------------------------
+Property Get IsImpress() As Boolean
+ IsImpress = [_Super].GetProperty(&quot;IsImpress&quot;)
+End Property &apos; SFDocuments.SF_FormDocument.IsImpress
+
+REM -----------------------------------------------------------------------------
+Property Get IsMath() As Boolean
+ IsMath = [_Super].GetProperty(&quot;IsMath&quot;)
+End Property &apos; SFDocuments.SF_FormDocument.IsMath
+
+REM -----------------------------------------------------------------------------
+Property Get Readonly() As Variant
+ Readonly = [_Super].GetProperty(&quot;Readonly&quot;)
+End Property &apos; SFDocuments.SF_FormDocument.Readonly
+
+REM -----------------------------------------------------------------------------
+Property Get XComponent() As Variant
+ XComponent = [_Super].GetProperty(&quot;XComponent&quot;)
+End Property &apos; SFDocuments.SF_FormDocument.XComponent
+
+REM ========================================================== SUPERCLASS METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function Activate() As Boolean
+ Activate = [_Super].Activate()
+End Function &apos; SFDocuments.SF_FormDocument.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function CreateMenu(Optional ByVal MenuHeader As Variant _
+ , Optional ByVal Before As Variant _
+ , Optional ByVal SubmenuChar As Variant _
+ ) As Object
+ Set CreateMenu = [_Super].CreateMenu(MenuHeader, Before, SubmenuChar)
+End Function &apos; SFDocuments.SF_FormDocument.CreateMenu
+
+REM -----------------------------------------------------------------------------
+Public Function ExportAsPDF(Optional ByVal FileName As Variant _
+ , Optional ByVal Overwrite As Variant _
+ , Optional ByVal Pages As Variant _
+ , Optional ByVal Password As Variant _
+ , Optional ByVal Watermark As Variant _
+ ) As Boolean
+ ExportAsPDF = [_Super].ExportAsPDF(FileName, Overwrite, Pages, Password, Watermark)
+End Function &apos; SFDocuments.SF_FormDocument.ExportAsPDF
+
+REM -----------------------------------------------------------------------------
+Public Function RemoveMenu(Optional ByVal MenuHeader As Variant) As Boolean
+ RemoveMenu = [_Super].RemoveMenu(MenuHeader)
+End Function &apos; SFDocuments.SF_FormDocument.RemoveMenu
+
+REM -----------------------------------------------------------------------------
+Public Sub RunCommand(Optional ByVal Command As Variant _
+ , ParamArray Args As Variant _
+ )
+ [_Super].RunCommand(Command, Args)
+End Sub &apos; SFDocuments.SF_FormDocument.RunCommand
+
+REM -----------------------------------------------------------------------------
+Public Function SaveCopyAs(Optional ByVal FileName As Variant _
+ , Optional ByVal Overwrite As Variant _
+ , Optional ByVal Password As Variant _
+ , Optional ByVal FilterName As Variant _
+ , Optional ByVal FilterOptions As Variant _
+ ) As Boolean
+ SaveCopyAs = [_Super].SaveCopyAs(FileName, Overwrite, Password, FilterName, FilterOptions)
+End Function &apos; SFDocuments.SF_FormDocument.SaveCopyAs
+
+REM -----------------------------------------------------------------------------
+Public Function SetPrinter(Optional ByVal Printer As Variant _
+ , Optional ByVal Orientation As Variant _
+ , Optional ByVal PaperFormat As Variant _
+ ) As Boolean
+ SetPrinter = [_Super].SetPrinter(Printer, Orientation, PaperFormat)
+End Function &apos; SFDocuments.SF_FormDocument.SetPrinter
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+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_FormDocument._FileIdent
+
+REM -----------------------------------------------------------------------------
+Public Sub _Initialize()
+&apos;&apos;&apos; Achieve the creation of a SF_Form instance
+&apos;&apos;&apos; - the database file
+&apos;&apos;&apos; - the database connection
+&apos;&apos;&apos; the internal and external names
+
+Dim oBase As Object &apos; A temporary Base instance
+
+ On Local Error GoTo Catch
+
+Try:
+ &apos; Base file where form document is stored
+ Set _BaseComponent = _Component.Parent
+
+ &apos; Connection arguments
+ Set _DataSource = _BaseComponent.DataSource
+ With _DataSource
+ _User = .User
+ _Password = .Password
+ End With
+
+ &apos; External and internal names
+ _PersistentName = ScriptForge.SF_Utils._GetPropertyValue(_Component.Args, &quot;HierarchicalDocumentName&quot;)
+ Set oBase = New SF_Base &apos; Only to be able to call the _FindByPersistentName() method
+ With oBase
+ Set _FormDocument = ._FindByPersistentName(_BaseComponent.getFormDocuments(), _PersistentName)
+ _HierarchicalName = _FormDocument.HierarchicalName
+ Set oBase = .Dispose()
+ End With
+
+Finally:
+ Exit Sub
+Catch:
+ On Local Error GoTo 0
+ GoTo Finally
+End Sub &apos; SFDocuments.SF_FormDocument._Initialize
+
+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_FormDocument._IsStillAlive
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertyGet(Optional ByVal psProperty As String _
+ , Optional ByVal pvArg As Variant _
+ ) 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
+
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ _PropertyGet = False
+
+ cstThisSub = &quot;SFDocuments.FormDocument.get&quot; &amp; psProperty
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+ Select Case psProperty
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFDocuments.SF_FormDocument._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the SF_FormDocument instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[DOCUMENT]: Type/File&quot;
+
+ _Repr = &quot;[FormDocument]: &quot; &amp; [_Super]._FileIdent()
+
+End Function &apos; SFDocuments.SF_FormDocument._Repr
+
+REM ============================================ END OF SFDOCUMENTS.SF_FORMDOCUMENT
+</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 5baf37afb484..c2a58bc61a8d 100644
--- a/wizards/source/sfdocuments/SF_Register.xba
+++ b/wizards/source/sfdocuments/SF_Register.xba
@@ -69,6 +69,7 @@ Public Sub RegisterScriptServices() As Variant
.RegisterService(&quot;Base&quot;, &quot;SFDocuments.SF_Register._NewDocument&quot;) &apos; Same reference, distinction is made inside the function
.RegisterService(&quot;Calc&quot;, &quot;SFDocuments.SF_Register._NewDocument&quot;) &apos; Same reference, distinction is made inside the function
.RegisterService(&quot;Writer&quot;, &quot;SFDocuments.SF_Register._NewDocument&quot;) &apos; Same reference, distinction is made inside the function
+ .RegisterService(&quot;FormDocument&quot;, &quot;SFDocuments.SF_Register._NewDocument&quot;) &apos; Same reference, 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
@@ -408,6 +409,11 @@ Try:
Set oSuperDocument = New SF_Document
Set oDocument.[_Super] = oSuperDocument &apos; Now both super and subclass are twinned
Set oSuperDocument.[_SubClass] = oDocument
+ Case &quot;FormDocument&quot;
+ Set oDocument = New SF_FormDocument
+ Set oSuperDocument = New SF_Document
+ Set oDocument.[_Super] = oSuperDocument &apos; Now both super and subclass are twinned
+ Set oSuperDocument.[_SubClass] = oDocument
Case &quot;Writer&quot;
Set oDocument = New SF_Writer
Set oSuperDocument = New SF_Document
@@ -424,6 +430,8 @@ Try:
Select Case vWindow.DocumentType
Case &quot;Base&quot;
Set ._DataSource = ._Component.DataSource
+ Case &quot;FormDocument&quot;
+ ._Initialize()
Case Else
End Select
End With
diff --git a/wizards/source/sfdocuments/SF_Writer.xba b/wizards/source/sfdocuments/SF_Writer.xba
index eded35de9a96..6f3f258dcb75 100644
--- a/wizards/source/sfdocuments/SF_Writer.xba
+++ b/wizards/source/sfdocuments/SF_Writer.xba
@@ -47,7 +47,7 @@ Option Explicit
&apos;&apos;&apos; TBD
&apos;&apos;&apos;
&apos;&apos;&apos; Detailed user documentation:
-&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/SF_Writer.html?DbPAR=BASIC
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_writer.html?DbPAR=BASIC
&apos;&apos;&apos;
&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
@@ -68,6 +68,8 @@ Private _Component As Object &apos; com.sun.star.lang.XComponent
REM ============================================================ MODULE CONSTANTS
+Const ISDOCFORM = 1 &apos; Form is stored in a Writer document
+
REM ====================================================== CONSTRUCTOR/DESTRUCTOR
REM -----------------------------------------------------------------------------
@@ -312,6 +314,7 @@ Public Function Properties() As Variant
, &quot;IsBase&quot; _
, &quot;IsCalc&quot; _
, &quot;IsDraw&quot; _
+ , &quot;IsFormDocument&quot; _
, &quot;IsImpress&quot; _
, &quot;IsMath&quot; _
, &quot;IsWriter&quot; _
@@ -430,6 +433,11 @@ Property Get IsDraw() As Boolean
End Property &apos; SFDocuments.SF_Writer.IsDraw
REM -----------------------------------------------------------------------------
+Property Get IsFormDocument() As Boolean
+ IsFormDocument = [_Super].GetProperty(&quot;IsFormDocument&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsFormDocument
+
+REM -----------------------------------------------------------------------------
Property Get IsImpress() As Boolean
IsImpress = [_Super].GetProperty(&quot;IsImpress&quot;)
End Property &apos; SFDocuments.SF_Writer.IsImpress
diff --git a/wizards/source/sfdocuments/script.xlb b/wizards/source/sfdocuments/script.xlb
index ff4495124f98..6945460dda77 100644
--- a/wizards/source/sfdocuments/script.xlb
+++ b/wizards/source/sfdocuments/script.xlb
@@ -11,4 +11,5 @@
<library:element library:name="SF_Writer"/>
<library:element library:name="SF_Chart"/>
<library:element library:name="SF_DocumentListener"/>
+ <library:element library:name="SF_FormDocument"/>
</library:library> \ No newline at end of file
diff --git a/wizards/source/sfwidgets/SF_MenuListener.xba b/wizards/source/sfwidgets/SF_MenuListener.xba
index 9ab018f77b8b..cb7839de5804 100644
--- a/wizards/source/sfwidgets/SF_MenuListener.xba
+++ b/wizards/source/sfwidgets/SF_MenuListener.xba
@@ -88,7 +88,10 @@ Try:
End With
If Len(sCommand) &gt; 0 Then
- Set oFrame = StarDesktop.ActiveFrame &apos; A menu has been clicked necessarily in the current window
+ &apos; A menu has been clicked necessarily in the current window (Document) or one of its subcomponents (FormDocument)
+ Set oFrame = StarDesktop.ActiveFrame
+ If oFrame.Frames.Count &gt; 0 Then Set oFrame = oFrame.getActiveFrame()
+ &apos; Command or script ?
If Left(sCommand, Len(cstUnoPrefix)) = cstUnoPrefix Then
&apos; Execute uno command
Set oDispatcher = ScriptForge.SF_Utils._GetUNOService(&quot;DispatchHelper&quot;)
@@ -124,5 +127,5 @@ Sub _SFMENU_disposing(Optional poEvent As Object) &apos; com.sun.star.awt.Menu
Exit Sub
End Sub &apos; SFWidgets.SF_MenuListener._SFMENU_disposing
-REM ============================================ END OF SFDIALOGS.SF_DIALOGLISTENER
+REM ============================================ END OF SFWIDGETS.SF_MENULISTENER
</script:module> \ No newline at end of file