summaryrefslogtreecommitdiffstats
path: root/wizards/source/sfdocuments
diff options
context:
space:
mode:
Diffstat (limited to 'wizards/source/sfdocuments')
-rw-r--r--wizards/source/sfdocuments/SF_Base.xba993
-rw-r--r--wizards/source/sfdocuments/SF_Calc.xba4501
-rw-r--r--wizards/source/sfdocuments/SF_Chart.xba814
-rw-r--r--wizards/source/sfdocuments/SF_Document.xba1504
-rw-r--r--wizards/source/sfdocuments/SF_DocumentListener.xba114
-rw-r--r--wizards/source/sfdocuments/SF_Form.xba1535
-rw-r--r--wizards/source/sfdocuments/SF_FormControl.xba1888
-rw-r--r--wizards/source/sfdocuments/SF_Register.xba546
-rw-r--r--wizards/source/sfdocuments/SF_Writer.xba635
-rw-r--r--wizards/source/sfdocuments/__License.xba26
-rw-r--r--wizards/source/sfdocuments/dialog.xlb3
-rw-r--r--wizards/source/sfdocuments/script.xlb14
12 files changed, 12573 insertions, 0 deletions
diff --git a/wizards/source/sfdocuments/SF_Base.xba b/wizards/source/sfdocuments/SF_Base.xba
new file mode 100644
index 000000000..1e6395dbf
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_Base.xba
@@ -0,0 +1,993 @@
+<?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_Base" 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_Base
+&apos;&apos;&apos; =======
+&apos;&apos;&apos;
+&apos;&apos;&apos; The SFDocuments library gathers a number of methods and properties making easy
+&apos;&apos;&apos; the management and several manipulations of 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, ...
+&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_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;
+&apos;&apos;&apos; Service invocation examples:
+&apos;&apos;&apos; 1) From the UI service
+&apos;&apos;&apos; Dim ui As Object, oDoc As Object
+&apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
+&apos;&apos;&apos; Set oDoc = ui.CreateBaseDocument(&quot;C:\Me\MyFile.odb&quot;, ...)
+&apos;&apos;&apos; &apos; or Set oDoc = ui.OpenDocument(&quot;C:\Me\MyFile.odb&quot;)
+&apos;&apos;&apos; 2) Directly if the document is already opened
+&apos;&apos;&apos; Dim oDoc As Object
+&apos;&apos;&apos; Set oDoc = CreateScriptService(&quot;SFDocuments.Base&quot;, &quot;MyFile.odb&quot;)
+&apos;&apos;&apos; &apos; The substring &quot;SFDocuments.&quot; in the service name is optional
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_base.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 DBCONNECTERROR = &quot;DBCONNECTERROR&quot;
+Private Const FORMDEADERROR = &quot;FORMDEADERROR&quot;
+Private Const BASEFORMNOTFOUNDERROR = &quot;BASEFORMNOTFOUNDERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object
+Private [_Super] As Object &apos; Document superclass, which the current instance is a subclass of
+Private ObjectType As String &apos; Must be BASE
+Private ServiceName As String
+
+&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
+
+Const ISBASEFORM = 3 &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()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ Set [_Super] = Nothing
+ ObjectType = &quot;BASE&quot;
+ ServiceName = &quot;SFDocuments.Base&quot;
+ Set _Component = Nothing
+ Set _DataSource = Nothing
+ Set _Database = Nothing
+ Set _FormDocuments = Nothing
+End Sub &apos; SFDocuments.SF_Base Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDocuments.SF_Base 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_Base Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function CloseDocument(Optional ByVal SaveAsk As Variant) As Boolean
+&apos;&apos;&apos; The closure of a Base document requires the closures of
+&apos;&apos;&apos; 1) the connection =&gt; done in the CloseDatabase() method
+&apos;&apos;&apos; 2) the data source
+&apos;&apos;&apos; 3) the document itself =&gt; done in the superclass
+
+Const cstThisSub = &quot;SFDocuments.Base.CloseDocument&quot;
+Const cstSubArgs = &quot;[SaveAsk=True]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(SaveAsk) Or IsEmpty(SaveAsk) Then SaveAsk = True
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(SaveAsk, &quot;SaveAsk&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ If Not IsNull(_Database) Then _Database.CloseDatabase()
+ If Not IsNull(_DataSource) Then _DataSource.dispose()
+ CloseDocument = [_Super].CloseDocument(SaveAsk)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.CloseDocument
+
+REM -----------------------------------------------------------------------------
+Public Function CloseFormDocument(Optional ByVal FormDocument As Variant) As Boolean
+&apos;&apos;&apos; Close the given form document
+&apos;&apos;&apos; Nothing happens if the form document is not open
+&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 closure is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDoc.CloseFormDocument(&quot;Folder1/myFormDocument&quot;)
+
+Dim bClose As Boolean &apos; Return value
+Dim oMainForm As Object &apos; com.sun.star.comp.sdb.Content
+Dim vFormNames As Variant &apos; Array of all document form names present in the document
+
+Const cstThisSub = &quot;SFDocuments.Base.CloseFormDocument&quot;
+Const cstSubArgs = &quot;FormDocument&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
+ &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
+ If Not IsLoaded(FormDocument) Then GoTo Finally
+
+Try:
+ Set oMainForm = _FormDocuments.getByHierarchicalName(FormDocument)
+ bClose = oMainForm.close()
+
+Finally:
+ CloseFormDocument = bClose
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.CloseFormDocument
+
+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-based 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 oFormDocument 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 oFormDocument = _FormDocuments.getByHierarchicalName(FormDocument)
+ Set oForms = oFormDocument.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]
+ Set ._Component = _Component
+ ._FormDocumentName = FormDocument
+ Set ._FormDocument = oFormDocument
+ ._FormType = ISBASEFORM
+ Set ._Form = oXForm
+ ._Initialize()
+ 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
+&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; 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 = oDoc.GetDatabase()
+
+Const cstThisSub = &quot;SFDocuments.Base.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 _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:
+ If IsNull(_Database) Then &apos; 1st connection from the current document instance
+ If IsNull(_DataSource) Then GoTo CatchConnect
+ Set _Database = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.DatabaseFromDocument&quot; _
+ , _DataSource, User, Password)
+ If IsNull(_Database) Then GoTo CatchConnect
+ _Database._Location = [_Super]._WindowFileName
+ 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_Base.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
+
+Const cstThisSub = &quot;SFDocuments.Base.GetProperty&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.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:
+ &apos; Superclass or subclass property ?
+ If ScriptForge.SF_Array.Contains([_Super].Properties(), PropertyName) Then
+ GetProperty = [_Super].GetProperty(PropertyName)
+ Else
+ GetProperty = _PropertyGet(PropertyName)
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+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, otherwise False
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Form name 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 Base class as an array
+
+ Methods = Array( _
+ &quot;CloseFormDocument&quot; _
+ , &quot;FormDocuments&quot; _
+ , &quot;Forms&quot; _
+ , &quot;GetDatabase&quot; _
+ , &quot;IsLoaded&quot; _
+ , &quot;OpenFormDocument&quot; _
+ , &quot;PrintOut&quot; _
+ , &quot;SetPrinter&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Base.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function OpenFormDocument(Optional ByVal FormDocument As Variant _
+ , Optional ByVal DesignMode As Variant _
+ ) As Boolean
+&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 document form name as a case-sensitive string
+&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; Exceptions:
+&apos;&apos;&apos; Form name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDoc.OpenFormDocument(&quot;Folder1/myFormDocument&quot;)
+
+Dim bOpen As Boolean &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
+
+Check:
+ If IsMissing(DesignMode) Or IsEmpty(DesignMode) Then DesignMode = False
+ 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
+ If Not ScriptForge.SF_Utils._Validate(DesignMode, &quot;DesignMode&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ With _Component.CurrentController
+ If Not .IsConnected Then .connect()
+ &apos; loadComponent activates the form when already loaded
+ Set oNewForm = .loadComponent(com.sun.star.sdb.application.DatabaseObject.FORM, FormDocument, DesignMode)
+ &apos; When user opened manually the form in design mode and closed it, the next execution in normal mode needs to be confirmed as below
+ With oNewForm.CurrentController
+ If .isFormDesignMode() &lt;&gt; DesignMode Then .setFormDesignMode(DesignMode)
+ End With
+ End With
+ bOpen = True
+
+Finally:
+ OpenFormDocument = bOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.OpenFormDocument
+
+REM -----------------------------------------------------------------------------
+Public Function PrintOut(Optional ByVal FormDocument As Variant _
+ , Optional ByVal Pages As Variant _
+ , Optional ByVal Copies As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Send the content of the given form 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; The given form document must be open. It is activated by the method.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FormDocument: a valid document form name as a case-sensitive string
+&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; Exceptions:
+&apos;&apos;&apos; FORMDEADERROR The form is not open
+&apos;&apos;&apos; Returns:
+&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)
+
+Dim bPrint As Boolean &apos; Return value
+Dim vFormNames As Variant &apos; Array of all document form names present in the document
+Dim oFormDocument As Object &apos; com.sun.star.comp.sdb.Content
+
+Const cstThisSub = &quot;SFDocuments.Base.PrintOut&quot;
+Const cstSubArgs = &quot;FormDocument, [Pages=&quot;&quot;&quot;&quot;], [Copies=1]&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 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
+ 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
+ End If
+ If Not IsLoaded(FormDocument) Then GoTo CatchClosed
+
+Try:
+ Set oFormDocument = _FormDocuments.getByHierarchicalName(FormDocument)
+ bPrint = [_Super].PrintOut(Pages, Copies, oFormDocument.Component)
+
+Finally:
+ PrintOut = bPrint
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchClosed:
+ ScriptForge.SF_Exception.RaiseFatal(FORMDEADERROR, FormDocument, _FileIdent())
+End Function &apos; SFDocuments.SF_Base.PrintOut
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Base class as an array
+
+ Properties = Array( _
+ &quot;DocumentType&quot; _
+ , &quot;IsBase&quot; _
+ , &quot;IsCalc&quot; _
+ , &quot;IsDraw &quot; _
+ , &quot;IsImpress&quot; _
+ , &quot;IsMath&quot; _
+ , &quot;IsWriter&quot; _
+ , &quot;XComponent&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Base.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function SetPrinter(Optional ByVal FormDocument As Variant _
+ , Optional ByVal Printer As Variant _
+ , Optional ByVal Orientation As Variant _
+ , Optional ByVal PaperFormat As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Define the printer options for a form document. The form document must be open.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FormDocument: a valid document form name as a case-sensitive string
+&apos;&apos;&apos; Printer: the name of the printer queue where to print to
+&apos;&apos;&apos; When absent or space, the default printer is set
+&apos;&apos;&apos; Orientation: either &quot;PORTRAIT&quot; or &quot;LANDSCAPE&quot;. Left unchanged when absent
+&apos;&apos;&apos; PaperFormat: one of next values
+&apos;&apos;&apos; &quot;A3&quot;, &quot;A4&quot;, &quot;A5&quot;, &quot;B4&quot;, &quot;B5&quot;, &quot;LETTER&quot;, &quot;LEGAL&quot;, &quot;TABLOID&quot;
+&apos;&apos;&apos; Left unchanged when absent
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.SetPrinter(&quot;myForm&quot;, Orientation := &quot;PORTRAIT&quot;)
+
+Dim bPrinter As Boolean &apos; Return value
+Dim vFormDocuments As Variant &apos; Array of form documents
+Dim oFormDocument As Object &apos; com.sun.star.comp.sdb.Content
+
+Const cstThisSub = &quot;SFDocuments.Base.SetPrinter&quot;
+Const cstSubArgs = &quot;FormDocument, [Printer=&quot;&quot;&quot;&quot;], [Orientation=&quot;&quot;PORTRAIT&quot;&quot;|&quot;&quot;LANDSCAPE&quot;&quot;]&quot; _
+ &amp; &quot;, [PaperFormat=&quot;&quot;A3&quot;&quot;|&quot;&quot;A4&quot;&quot;|&quot;&quot;A5&quot;&quot;|&quot;&quot;B4&quot;&quot;|&quot;&quot;B5&quot;&quot;|&quot;&quot;LETTER&quot;&quot;|&quot;&quot;LEGAL&quot;&quot;|&quot;&quot;TABLOID&quot;&quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bPrinter = False
+
+Check:
+ If IsMissing(Printer) Or IsEmpty(Printer) Then Printer = &quot;&quot;
+ If IsMissing(Orientation) Or IsEmpty(Orientation) Then Orientation = &quot;&quot;
+ If IsMissing(PaperFormat) Or IsEmpty(PaperFormat) Then PaperFormat = &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
+ End If
+ If Not IsLoaded(FormDocument) Then GoTo CatchClosed
+
+Try:
+ Set oFormDocument = _FormDocuments.getByHierarchicalName(FormDocument)
+ bPrinter = [_Super].SetPrinter(Printer, Orientation, PaperFormat, oFormDocument.Component)
+
+Finally:
+ SetPrinter = bPrinter
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchClosed:
+ ScriptForge.SF_Exception.RaiseFatal(FORMDEADERROR, FormDocument, _FileIdent())
+End Function &apos; SFDocuments.SF_Base.SetPrinter
+
+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.Base.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:
+ Select Case UCase(PropertyName)
+ Case Else
+ End Select
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.SetProperty
+
+REM ======================================================= SUPERCLASS PROPERTIES
+
+REM -----------------------------------------------------------------------------
+&apos;Property Get CustomProperties() As Variant
+&apos; CustomProperties = [_Super].GetProperty(&quot;CustomProperties&quot;)
+&apos;End Property &apos; SFDocuments.SF_Base.CustomProperties
+
+REM -----------------------------------------------------------------------------
+&apos;Property Let CustomProperties(Optional ByVal pvCustomProperties As Variant)
+&apos; [_Super].CustomProperties = pvCustomProperties
+&apos;End Property &apos; SFDocuments.SF_Base.CustomProperties
+
+REM -----------------------------------------------------------------------------
+&apos;Property Get Description() As Variant
+&apos; Description = [_Super].GetProperty(&quot;Description&quot;)
+&apos;End Property &apos; SFDocuments.SF_Base.Description
+
+REM -----------------------------------------------------------------------------
+&apos;Property Let Description(Optional ByVal pvDescription As Variant)
+&apos; [_Super].Description = pvDescription
+&apos;End Property &apos; SFDocuments.SF_Base.Description
+
+REM -----------------------------------------------------------------------------
+&apos;Property Get DocumentProperties() As Variant
+&apos; DocumentProperties = [_Super].GetProperty(&quot;DocumentProperties&quot;)
+&apos;End Property &apos; SFDocuments.SF_Base.DocumentProperties
+
+REM -----------------------------------------------------------------------------
+Property Get DocumentType() As String
+ DocumentType = [_Super].GetProperty(&quot;DocumentType&quot;)
+End Property &apos; SFDocuments.SF_Base.DocumentType
+
+REM -----------------------------------------------------------------------------
+Property Get IsBase() As Boolean
+ IsBase = [_Super].GetProperty(&quot;IsBase&quot;)
+End Property &apos; SFDocuments.SF_Base.IsBase
+
+REM -----------------------------------------------------------------------------
+Property Get IsCalc() As Boolean
+ IsCalc = [_Super].GetProperty(&quot;IsCalc&quot;)
+End Property &apos; SFDocuments.SF_Base.IsCalc
+
+REM -----------------------------------------------------------------------------
+Property Get IsDraw() As Boolean
+ IsDraw = [_Super].GetProperty(&quot;IsDraw&quot;)
+End Property &apos; SFDocuments.SF_Base.IsDraw
+
+REM -----------------------------------------------------------------------------
+Property Get IsImpress() As Boolean
+ IsImpress = [_Super].GetProperty(&quot;IsImpress&quot;)
+End Property &apos; SFDocuments.SF_Base.IsImpress
+
+REM -----------------------------------------------------------------------------
+Property Get IsMath() As Boolean
+ IsMath = [_Super].GetProperty(&quot;IsMath&quot;)
+End Property &apos; SFDocuments.SF_Base.IsMath
+
+REM -----------------------------------------------------------------------------
+Property Get IsWriter() As Boolean
+ IsWriter = [_Super].GetProperty(&quot;IsWriter&quot;)
+End Property &apos; SFDocuments.SF_Base.IsWriter
+
+REM -----------------------------------------------------------------------------
+&apos;Property Get Keywords() As Variant
+&apos; Keywords = [_Super].GetProperty(&quot;Keywords&quot;)
+&apos;End Property &apos; SFDocuments.SF_Base.Keywords
+
+REM -----------------------------------------------------------------------------
+&apos;Property Let Keywords(Optional ByVal pvKeywords As Variant)
+&apos; [_Super].Keywords = pvKeywords
+&apos;End Property &apos; SFDocuments.SF_Base.Keywords
+
+REM -----------------------------------------------------------------------------
+&apos;Property Get Readonly() As Variant
+&apos; Readonly = [_Super].GetProperty(&quot;Readonly&quot;)
+&apos;End Property &apos; SFDocuments.SF_Base.Readonly
+
+REM -----------------------------------------------------------------------------
+&apos;Property Get Subject() As Variant
+&apos; Subject = [_Super].GetProperty(&quot;Subject&quot;)
+&apos;End Property &apos; SFDocuments.SF_Base.Subject
+
+REM -----------------------------------------------------------------------------
+&apos;Property Let Subject(Optional ByVal pvSubject As Variant)
+&apos; [_Super].Subject = pvSubject
+&apos;End Property &apos; SFDocuments.SF_Base.Subject
+
+REM -----------------------------------------------------------------------------
+&apos;Property Get Title() As Variant
+&apos; Title = [_Super].GetProperty(&quot;Title&quot;)
+&apos;End Property &apos; SFDocuments.SF_Base.Title
+
+REM -----------------------------------------------------------------------------
+&apos;Property Let Title(Optional ByVal pvTitle As Variant)
+&apos; [_Super].Title = pvTitle
+&apos;End Property &apos; SFDocuments.SF_Base.Title
+
+REM -----------------------------------------------------------------------------
+Property Get XComponent() As Variant
+ XComponent = [_Super].GetProperty(&quot;XComponent&quot;)
+End Property &apos; SFDocuments.SF_Base.XComponent
+
+REM ========================================================== SUPERCLASS METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function Activate() As Boolean
+ Activate = [_Super].Activate()
+End Function &apos; SFDocuments.SF_Base.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_Base.CreateMenu
+
+REM -----------------------------------------------------------------------------
+Public Function RemoveMenu(Optional ByVal MenuHeader As Variant) As Boolean
+ RemoveMenu = [_Super].RemoveMenu(MenuHeader)
+End Function &apos; SFDocuments.SF_Base.RemoveMenu
+
+REM -----------------------------------------------------------------------------
+Public Sub RunCommand(Optional ByVal Command As Variant _
+ , ParamArray Args As Variant _
+ )
+ [_Super].RunCommand(Command, Args)
+End Sub &apos; SFDocuments.SF_Base.RunCommand
+
+REM -----------------------------------------------------------------------------
+Public Function Save() As Boolean
+ Save = [_Super].Save()
+End Function &apos; SFDocuments.SF_Base.Save
+
+REM -----------------------------------------------------------------------------
+Public Function SaveAs(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
+ SaveAs = [_Super].SaveAs(FileName, Overwrite, Password, FilterName, FilterOptions)
+End Function &apos; SFDocuments.SF_Base.SaveAs
+
+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_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 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:
+ _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 _FindByPersistentName(ByRef poContainer As Object _
+ , psPersistent As String _
+ ) As Object
+&apos;&apos;&apos; The FormDocuments property of a Base component has strangely
+&apos;&apos;&apos; a getByHierarchical() method but no access to the same com.sun.star.comp.sdb.Content
+&apos;&apos;&apos; object via its persistent/ODF name
+&apos;&apos;&apos; This method returns the object having the given persistent name
+&apos;&apos;&apos; The function traverses recursively the whole tree below the container until found
+&apos;&apos;&apos; The initial call starts from the container _Component.getFormDocuments
+&apos;&apos;&apos; The list contains closed and open forms
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; poContainer: the actual top of the free, initially _FormDocuments
+&apos;&apos;&apos; psPersistent: a name like &quot;Obj...&quot;
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A com.sun.star.comp.sdb.Content object (object found, the process stops)
+&apos;&apos;&apos; or Nothing (object not found, the process continues)
+
+Dim oMainForm As Object &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:
+ Set oMainForm = Nothing
+ With poContainer
+ For i = 0 To .Count - 1
+ Set oSubItem = .getByIndex(i)
+ If oSubItem.ContentType = cstFormType Then &apos; Examine its persistent name
+ If oSubItem.PersistentName = psPersistent Then
+ Set oMainForm = oSubItem
+ Exit For
+ End If
+ Else
+ Set oMainForm = _FindByPersistentName(oSubItem, psPersistent)
+ End If
+ Next i
+ End With
+
+Finally:
+ Set _FindByPersistentName = oMainForm
+ Exit Function
+End Function &apos; SFDocuments.SF_Base.FindByPersistentName
+
+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
+&apos;&apos;&apos; Return the value of the named property
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psProperty: the name of the property
+
+Dim oProperties As Object &apos; Document or Custom properties
+Dim vLastCell As Variant &apos; Coordinates of last used cell in a sheet
+Dim oSelect As Object &apos; Current selection
+Dim vRanges As Variant &apos; List of selected ranges
+Dim i As Long
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ _PropertyGet = False
+
+ cstThisSub = &quot;SFDocuments.SF_Base.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_Base._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the SF_Base instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[Base]: Type/File&quot;
+
+ _Repr = &quot;[Base]: &quot; &amp; [_Super]._FileIdent()
+
+End Function &apos; SFDocuments.SF_Base._Repr
+
+REM ============================================ END OF SFDOCUMENTS.SF_BASE
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Calc.xba b/wizards/source/sfdocuments/SF_Calc.xba
new file mode 100644
index 000000000..0b7b88ae8
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_Calc.xba
@@ -0,0 +1,4501 @@
+<?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_Calc" 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_Calc
+&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_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_Calc module is focused on :
+&apos;&apos;&apos; - management (copy, insert, move, ...) of sheets within a Calc document
+&apos;&apos;&apos; - exchange of data between Basic data structures and Calc ranges of values
+&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;
+&apos;&apos;&apos; Service invocation examples:
+&apos;&apos;&apos; 1) From the UI service
+&apos;&apos;&apos; Dim ui As Object, oDoc As Object
+&apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
+&apos;&apos;&apos; Set oDoc = ui.CreateDocument(&quot;Calc&quot;, ...)
+&apos;&apos;&apos; &apos; or Set oDoc = ui.OpenDocument(&quot;C:\Me\MyFile.ods&quot;)
+&apos;&apos;&apos; 2) Directly if the document is already opened
+&apos;&apos;&apos; Dim oDoc As Object
+&apos;&apos;&apos; Set oDoc = CreateScriptService(&quot;SFDocuments.Calc&quot;, &quot;Untitled 1&quot;) &apos; Default = ActiveWindow
+&apos;&apos;&apos; &apos; or Set oDoc = CreateScriptService(&quot;SFDocuments.Calc&quot;, &quot;Untitled 1&quot;) &apos; Untitled 1 is presumed a Calc document
+&apos;&apos;&apos; &apos; The substring &quot;SFDocuments.&quot; in the service name is optional
+&apos;&apos;&apos;
+&apos;&apos;&apos; Definitions:
+&apos;&apos;&apos; Many methods require a &quot;Sheet&quot; or a &quot;Range&quot; as argument. (NB: a single cell is considered as a special case of a Range)
+&apos;&apos;&apos; Usually, within a specific Calc instance, sheets and ranges are given as a string: &quot;SheetX&quot; and &quot;D2:F6&quot;
+&apos;&apos;&apos; Multiple ranges are not supported in this context.
+&apos;&apos;&apos; Additionally, the .Sheet and .Range methods return a reference that may be used
+&apos;&apos;&apos; as argument of a method called from another instance of the Calc service
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim oDocA As Object : Set oDocA = ui.OpenDocument(&quot;C:\FileA.ods&quot;, Hidden := True, ReadOnly := True)
+&apos;&apos;&apos; Dim oDocB As Object : Set oDocB = ui.OpenDocument(&quot;C:\FileB.ods&quot;)
+&apos;&apos;&apos; oDocB.CopyToRange(oDocA.Range(&quot;SheetX.D4:F8&quot;), &quot;D2:F6&quot;) &apos; CopyToRange(source, target)
+&apos;&apos;&apos;
+&apos;&apos;&apos; Sheet: the sheet name as a string or an object produced by .Sheet()
+&apos;&apos;&apos; &quot;~&quot; = current sheet
+&apos;&apos;&apos; Range: a string designating a set of contiguous cells located in a sheet of the current instance
+&apos;&apos;&apos; &quot;~&quot; = current selection (if multiple selections, its 1st component)
+&apos;&apos;&apos; or an object produced by .Range()
+&apos;&apos;&apos; The sheet name is optional (default = active sheet). Surrounding quotes and $ signs are optional
+&apos;&apos;&apos; ~.~, ~ The current selection in the active sheet
+&apos;&apos;&apos; &apos;$SheetX&apos;.D2 or $D$2 A single cell
+&apos;&apos;&apos; &apos;$SheetX&apos;.D2:F6, D2:D10 Multiple cells
+&apos;&apos;&apos; &apos;$SheetX&apos;.A:A or 3:5 All cells in the same column or row up to the last active cell
+&apos;&apos;&apos; SheetX.* All cells up to the last active cell
+&apos;&apos;&apos; myRange A range name at spreadsheet level
+&apos;&apos;&apos; ~.yourRange, SheetX.someRange A range name at sheet level
+&apos;&apos;&apos; myDoc.Range(&quot;SheetX.D2:F6&quot;)
+&apos;&apos;&apos; A range within the sheet SheetX in file associated with the myDoc Calc instance
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_calc.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 UNKNOWNFILEERROR = &quot;UNKNOWNFILEERROR&quot;
+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;
+Private Const DUPLICATECHARTERROR = &quot;DUPLICATECHARTERROR&quot;
+Private Const RANGEEXPORTERROR = &quot;RANGEEXPORTERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Super] As Object &apos; Document superclass, which the current instance is a subclass of
+Private ObjectType As String &apos; Must be CALC
+Private ServiceName As String
+
+&apos; Window component
+Private _Component As Object &apos; com.sun.star.lang.XComponent
+
+Type _Address
+ ObjectType As String &apos; Must be &quot;SF_CalcReference&quot;
+ ServiceName As String &apos; Must be &quot;SFDocuments.CalcReference&quot;
+ RawAddress As String
+ Component As Object &apos; com.sun.star.lang.XComponent
+ SheetName As String
+ SheetIndex As Integer
+ RangeName As String
+ Height As Long
+ Width As Long
+ XSpreadSheet As Object &apos; com.sun.star.sheet.XSpreadsheet
+ XCellRange As Object &apos; com.sun.star.table.XCellRange
+End Type
+
+Private _LastParsedAddress As Object &apos; _Address type - parsed ranges are cached
+
+REM ============================================================ MODULE CONSTANTS
+
+Private Const cstSHEET = 1
+Private Const cstRANGE = 2
+
+Private Const MAXCOLS = 2^10 &apos; Max number of columns in a sheet
+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
+Private Const SERVICEREFERENCE = &quot;SFDocuments.CalcReference&quot;
+ &apos; Service name of _Address (used in Python)
+
+Private Const ISCALCFORM = 2 &apos; Form is stored in a Calc document
+
+Private Const cstSPECIALCHARS = &quot; `~!@#$%^&amp;()-_=+{}|;:,&lt;.&gt;&quot;&quot;&quot;
+ &apos; Presence of a special character forces surrounding the sheet name with single quotes in absolute addresses
+
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Super] = Nothing
+ ObjectType = &quot;CALC&quot;
+ ServiceName = &quot;SFDocuments.Calc&quot;
+ Set _Component = Nothing
+ Set _LastParsedAddress = Nothing
+End Sub &apos; SFDocuments.SF_Calc Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDocuments.SF_Calc 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_Calc Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get CurrentSelection() As Variant
+&apos;&apos;&apos; Returns as a string the currently selected range or as an array the list of the currently selected ranges
+ CurrentSelection = _PropertyGet(&quot;CurrentSelection&quot;)
+End Property &apos; SFDocuments.SF_Calc.CurrentSelection (get)
+
+REM -----------------------------------------------------------------------------
+Property Let CurrentSelection(Optional ByVal pvSelection As Variant)
+&apos;&apos;&apos; Set the selection to a single or a multiple range
+&apos;&apos;&apos; The argument is a string or an array of strings
+
+Dim sRange As String &apos; A single selection
+Dim oCellRanges As Object &apos; com.sun.star.sheet.SheetCellRanges
+Dim vRangeAddresses As Variant &apos; Array of com.sun.star.table.CellRangeAddress
+Dim i As Long
+Const cstThisSub = &quot;SFDocuments.Calc.setCurrentSelection&quot;
+Const cstSubArgs = &quot;Selection&quot;
+
+ On Local Error GoTo Catch
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+ If Not ScriptForge.SF_Utils._Validate(pvSelection, &quot;pvSelection&quot;, V_STRING) Then GoTo Finally
+ End If
+ End If
+
+Try:
+ If IsArray(pvSelection) Then
+ Set oCellRanges = _Component.createInstance(&quot;com.sun.star.sheet.SheetCellRanges&quot;)
+ vRangeAddresses = Array()
+ ReDim vRangeAddresses(0 To UBound(pvSelection))
+ For i = 0 To UBound(pvSelection)
+ vRangeAddresses(i) = Range(pvSelection(i)).XCellRange.RangeAddress
+ Next i
+ oCellRanges.addRangeAddresses(vRangeAddresses, False)
+ _Component.CurrentController.select(oCellRanges)
+ Else
+ _Component.CurrentController.select(_ParseAddress(pvSelection).XCellRange)
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Property
+Catch:
+ GoTo Finally
+End Property &apos; SFDocuments.SF_Calc.CurrentSelection (let)
+
+REM -----------------------------------------------------------------------------
+Property Get FirstCell(Optional ByVal RangeName As Variant) As String
+&apos;&apos;&apos; Returns the First used cell in a given range or sheet
+&apos;&apos;&apos; When the argument is a sheet it will always return the &quot;sheet.$A$1&quot; cell
+ FirstCell = _PropertyGet(&quot;FirstCell&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.FirstCell
+
+REM -----------------------------------------------------------------------------
+Property Get FirstColumn(Optional ByVal RangeName As Variant) As Long
+&apos;&apos;&apos; Returns the leftmost column in a given sheet or range
+&apos;&apos;&apos; When the argument is a sheet it will always return 1
+ FirstColumn = _PropertyGet(&quot;FirstColumn&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.FirstColumn
+
+REM -----------------------------------------------------------------------------
+Property Get FirstRow(Optional ByVal RangeName As Variant) As Long
+&apos;&apos;&apos; Returns the First used column in a given range
+&apos;&apos;&apos; When the argument is a sheet it will always return 1
+ FirstRow = _PropertyGet(&quot;FirstRow&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.FirstRow
+
+REM -----------------------------------------------------------------------------
+Property Get Height(Optional ByVal RangeName As Variant) As Long
+&apos;&apos;&apos; Returns the height in # of rows of the given range
+ Height = _PropertyGet(&quot;Height&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.Height
+
+REM -----------------------------------------------------------------------------
+Property Get LastCell(Optional ByVal RangeName As Variant) As String
+&apos;&apos;&apos; Returns the last used cell in a given sheet or range
+ LastCell = _PropertyGet(&quot;LastCell&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.LastCell
+
+REM -----------------------------------------------------------------------------
+Property Get LastColumn(Optional ByVal RangeName As Variant) As Long
+&apos;&apos;&apos; Returns the last used column in a given sheet
+ LastColumn = _PropertyGet(&quot;LastColumn&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.LastColumn
+
+REM -----------------------------------------------------------------------------
+Property Get LastRow(Optional ByVal RangeName As Variant) As Long
+&apos;&apos;&apos; Returns the last used column in a given sheet
+ LastRow = _PropertyGet(&quot;LastRow&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.LastRow
+
+REM -----------------------------------------------------------------------------
+Property Get Range(Optional ByVal RangeName As Variant) As Variant
+&apos;&apos;&apos; Returns a (internal) range object
+ Range = _PropertyGet(&quot;Range&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.Range
+
+REM -----------------------------------------------------------------------------
+Property Get Region(Optional ByVal RangeName As Variant) As String
+&apos;&apos;&apos; Returns the smallest area as a range string that contains the given range
+&apos;&apos;&apos; and which is completely surrounded with empty cells
+ Region = _PropertyGet(&quot;Region&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.Region
+
+REM -----------------------------------------------------------------------------
+Property Get Sheet(Optional ByVal SheetName As Variant) As Variant
+&apos;&apos;&apos; Returns a (internal) sheet object
+ Sheet = _PropertyGet(&quot;Sheet&quot;, SheetName)
+End Property &apos; SFDocuments.SF_Calc.Sheet
+
+REM -----------------------------------------------------------------------------
+Property Get SheetName(Optional ByVal RangeName As Variant) As String
+&apos;&apos;&apos; Returns the sheet name part of a range
+ SheetName = _PropertyGet(&quot;SheetName&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.SheetName
+
+REM -----------------------------------------------------------------------------
+Property Get Sheets() As Variant
+&apos;&apos;&apos; Returns an array listing the existing sheet names
+ Sheets = _PropertyGet(&quot;Sheets&quot;)
+End Property &apos; SFDocuments.SF_Calc.Sheets
+
+REM -----------------------------------------------------------------------------
+Property Get Width(Optional ByVal RangeName As Variant) As Long
+&apos;&apos;&apos; Returns the width in # of columns of the given range
+ Width = _PropertyGet(&quot;Width&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.Width
+
+REM -----------------------------------------------------------------------------
+Property Get XCellRange(Optional ByVal RangeName As Variant) As Variant
+&apos;&apos;&apos; Returns a UNO object of type com.sun.star.Table.CellRange
+ XCellRange = _PropertyGet(&quot;XCellRange&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.XCellRange
+
+REM -----------------------------------------------------------------------------
+Property Get XSheetCellCursor(Optional ByVal RangeName As Variant) As Variant
+&apos;&apos;&apos; Returns a UNO object of type com.sun.star.sheet.XSheetCellCursor
+&apos;&apos; After having moved the cursor (gotoNext(), ...) the resulting range can be got
+&apos;&apos;&apos; back as a string with the cursor.AbsoluteName UNO property.
+ XSheetCellCursor = _PropertyGet(&quot;XSheetCellCursor&quot;, RangeName)
+End Property &apos; SFDocuments.SF_Calc.XSheetCellCursor
+
+REM -----------------------------------------------------------------------------
+Property Get XSpreadsheet(Optional ByVal SheetName As Variant) As Variant
+&apos;&apos;&apos; Returns a UNO object of type com.sun.star.sheet.XSpreadsheet
+ XSpreadsheet = _PropertyGet(&quot;XSpreadsheet&quot;, SheetName)
+End Property &apos; SFDocuments.SF_Calc.XSpreadsheet
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function A1Style(Optional ByVal Row1 As Variant _
+ , Optional ByVal Column1 As Variant _
+ , Optional ByVal Row2 As Variant _
+ , Optional ByVal Column2 As Variant _
+ , Optional ByVal SheetName As Variant _
+ ) As String
+&apos;&apos;&apos; Returns a range expressed in A1-style as defined by its coordinates
+&apos;&apos;&apos; If only one pair of coordinates is given, the range will embrace only a single cell
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Row1 : the row number of the first coordinate
+&apos;&apos;&apos; Column1 : the column number of the first coordinates
+&apos;&apos;&apos; Row2 : the row number of the second coordinate
+&apos;&apos;&apos; Column2 : the column number of the second coordinates
+&apos;&apos;&apos; SheetName: Default = the current sheet. If present, the sheet must exist.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A range as a string
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; range = oDoc.A1Style(5, 2, 10, 4, &quot;SheetX&quot;) &apos; &quot;&apos;$SheetX&apos;.$E$2:$J$4&quot;
+
+Dim sA1Style As String &apos; Return value
+Dim vSheetName As Variant &apos; Alias of SheetName - necessary see [Bug 145279]
+Dim lTemp As Long &apos; To switch 2 values
+Dim i As Long
+
+Const cstThisSub = &quot;SFDocuments.Calc.A1Style&quot;
+Const cstSubArgs = &quot;Row1, Column1, [Row2], [Column2], [SheetName]=&quot;&quot;&quot;&quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sA1Style = &quot;&quot;
+
+Check:
+ If IsMissing(Row2) Or IsEmpty(Row2) Then Row2 = 0
+ If IsMissing(Column2) Or IsEmpty(Column2) Then Column2 = 0
+ If IsMissing(SheetName) Or IsEmpty(SheetName) Then SheetName = &quot;~&quot;
+ vSheetName = SheetName
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Row1, &quot;Row1&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Column1, &quot;Column1&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Row2, &quot;Row2&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Column2, &quot;Column2&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not _ValidateSheet(vSheetName, &quot;SheetName&quot;, , True, True, , , True) Then GoTo Finally
+ End If
+
+ If Row1 &gt; MAXROWS Then Row1 = MAXROWS
+ If Row2 &gt; MAXROWS Then Row2 = MAXROWS
+ If Column1 &gt; MAXCOLS Then Column1 = MAXCOLS
+ If Column2 &gt; MAXCOLS Then Column2 = MAXCOLS
+
+ If Row2 &gt; 0 And Row2 &lt; Row1 Then
+ lTemp = Row2 : Row2 = Row1 : Row1 = lTemp
+ End If
+ If Column2 &gt; 0 And Column2 &lt; Column1 Then
+ lTemp = Column2 : Column2 = Column1 : Column1 = lTemp
+ End If
+
+Try:
+ &apos; Surround the sheet name with single quotes when required by the presence of special characters
+ vSheetName = _QuoteSheetName(vSheetName)
+ &apos; Define the new range string
+ sA1Style = &quot;$&quot; &amp; vSheetName &amp; &quot;.&quot; _
+ &amp; &quot;$&quot; &amp; _GetColumnName(Column1) &amp; &quot;$&quot; &amp; CLng(Row1) _
+ &amp; Iif(Row2 &gt; 0 And Column2 &gt; 0, &quot;:$&quot; &amp; _GetColumnName(Column2) &amp; &quot;$&quot; &amp; CLng(Row2), &quot;&quot;)
+
+Finally:
+ A1Style = sA1Style
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.A1Style
+
+REM -----------------------------------------------------------------------------
+Public Function Activate(Optional ByVal SheetName As Variant) As Boolean
+&apos;&apos;&apos; Make the current document or the given sheet active
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SheetName: Default = the Calc document as a whole
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the document or the sheet could be made active
+&apos;&apos;&apos; Otherwise, there is no change in the actual user interface
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.Activate(&quot;SheetX&quot;)
+
+Dim bActive As Boolean &apos; Return value
+Dim oSheet As Object &apos; Reference to sheet
+Const cstThisSub = &quot;SFDocuments.Calc.Activate&quot;
+Const cstSubArgs = &quot;[SheetName]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bActive = False
+
+Check:
+ If IsMissing(SheetName) Or IsEmpty(SheetName) Then SheetName = &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
+ End If
+
+Try:
+ &apos; Sheet activation, to do only when meaningful, precedes document activation
+ If Len(SheetName) &gt; 0 Then
+ With _Component
+ Set oSheet = .getSheets.getByName(SheetName)
+ Set .CurrentController.ActiveSheet = oSheet
+ End With
+ End If
+ bActive = [_Super].Activate()
+
+Finally:
+ Activate = bActive
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function Charts(Optional ByVal SheetName As Variant _
+ , Optional ByVal ChartName As Variant _
+ ) As Variant
+&apos;&apos;&apos; Return either the list of charts present in the given sheet or a chart object
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SheetName: The name of an existing sheet
+&apos;&apos;&apos; ChartName: The user-defined name of the targeted chart or the zero-length string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; When ChartName = &quot;&quot;, return the list of the charts present in the sheet,
+&apos;&apos;&apos; otherwise, return a new chart service instance
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim oChart As Object
+&apos;&apos;&apos; Set oChart = oDoc.Charts(&quot;SheetX&quot;, &quot;myChart&quot;)
+
+Dim vCharts As Variant &apos; Return value when array of chart names
+Dim oChart As Object &apos; Return value when new chart instance
+Dim oSheet As Object &apos; Alias of SheetName as reference
+Dim oDrawPage As Object &apos; com.sun.star.drawing.XDrawPage
+Dim oNextShape As Object &apos; com.sun.star.drawing.XShape
+Dim sChartName As String &apos; Some chart name
+Dim lCount As Long &apos; Counter for charts among all drawing objects
+Dim i As Long
+Const cstChartShape = &quot;com.sun.star.drawing.OLE2Shape&quot;
+
+Const cstThisSub = &quot;SFDocuments.Calc.Charts&quot;
+Const cstSubArgs = &quot;SheetName, [ChartName=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vCharts = Array()
+
+Check:
+ If IsMissing(ChartName) Or IsEmpty(ChartName) Then ChartName = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ChartName, &quot;ChartName&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Because the user can change it constantly, the list of valid charts has to be rebuilt at each time
+ &apos; Explore charts starting from the draw page
+ Set oSheet = _Component.getSheets.getByName(SheetName)
+ Set oDrawPage = oSheet.getDrawPage()
+ vCharts = Array()
+ Set oChart = Nothing
+ lCount = -1
+ For i = 0 To oDrawPage.Count - 1
+ Set oNextShape = oDrawPage.getByIndex(i)
+ if oNextShape.supportsService(cstChartShape) Then &apos; Ignore other shapes
+ sChartName = oNextShape.Name &apos; User-defined name
+ If Len(sChartName) = 0 Then sChartName = oNextShape.PersistName &apos; Internal name
+ &apos; Is chart found ?
+ If Len(ChartName) &gt; 0 Then
+ If ChartName = sChartName Then
+ Set oChart = New SF_Chart
+ With oChart
+ Set .[Me] = oChart
+ Set .[_Parent] = [Me]
+ ._SheetName = SheetName
+ ._DrawIndex = i
+ ._ChartName = ChartName
+ ._PersistentName = oNextShape.PersistName
+ Set ._Shape = oNextShape
+ Set ._Chart = oSheet.getCharts().getByName(._PersistentName)
+ Set ._ChartObject = ._Chart.EmbeddedObject
+ Set ._Diagram = ._ChartObject.Diagram
+ End With
+ Exit For
+ End If
+ End If
+ &apos; Build stack of chart names
+ lCount = lCount + 1
+ If UBound(vCharts) &lt; 0 Then
+ vCharts = Array(sChartName)
+ Else
+ ReDim Preserve vCharts(0 To UBound(vCharts) + 1)
+ vCharts(lCount) = sChartName
+ End If
+ End If
+ Next i
+
+ &apos; Raise error when chart not found
+ If Len(ChartName) &gt; 0 And IsNull(oChart) Then
+ If Not ScriptForge.SF_Utils._Validate(ChartName, &quot;ChartName&quot;, V_STRING, vCharts) Then GoTo Finally
+ End If
+
+Finally:
+ If Len(ChartName) = 0 Then Charts = vCharts Else Set Charts = oChart
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.Charts
+
+REM -----------------------------------------------------------------------------
+Public Sub ClearAll(Optional ByVal Range As Variant) As String
+&apos;&apos;&apos; Clear entirely the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the cell or the range as a string that should be cleared
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.ClearAll(&quot;SheetX&quot;) &apos; Clears the used area of the sheet
+
+Dim lClear As Long &apos; The elements to clear
+Dim oRange As Object &apos; Alias of Range
+Const cstThisSub = &quot;SFDocuments.Calc.ClearAll&quot;
+Const cstSubArgs = &quot;Range&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
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ With com.sun.star.sheet.CellFlags
+ lClear = 0 _
+ + .VALUE _
+ + .DATETIME _
+ + .STRING _
+ + .ANNOTATION _
+ + .FORMULA _
+ + .HARDATTR _
+ + .STYLES _
+ + .OBJECTS _
+ + .EDITATTR _
+ + .FORMATTED
+ Set oRange = _ParseAddress(Range)
+ oRange.XCellRange.clearContents(lClear)
+ End With
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub &apos; SF_Documents.SF_Calc.ClearAll
+
+REM -----------------------------------------------------------------------------
+Public Sub ClearFormats(Optional ByVal Range As Variant) As String
+&apos;&apos;&apos; Clear all the formatting elements of the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the cell or the range as a string that should be cleared
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.ClearFormats(&quot;SheetX:A1:E100&quot;) &apos; Clear the formats of the given range
+
+Dim lClear As Long &apos; The elements to clear
+Dim oRange As Object &apos; Alias of Range
+Const cstThisSub = &quot;SFDocuments.Calc.ClearFormats&quot;
+Const cstSubArgs = &quot;Range&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
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ With com.sun.star.sheet.CellFlags
+ lClear = 0 _
+ + .HARDATTR _
+ + .STYLES _
+ + .EDITATTR _
+ + .FORMATTED
+ Set oRange = _ParseAddress(Range)
+ oRange.XCellRange.clearContents(lClear)
+ End With
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub &apos; SF_Documents.SF_Calc.ClearFormats
+
+REM -----------------------------------------------------------------------------
+Public Sub ClearValues(Optional ByVal Range As Variant) As String
+&apos;&apos;&apos; Clear values and formulas in the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the cell or the range as a string that should be cleared
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.ClearValues(&quot;SheetX:*&quot;) &apos; Clears the used area of the sheet
+
+Dim lClear As Long &apos; The elements to clear
+Dim oRange As Object &apos; Alias of Range
+Const cstThisSub = &quot;SFDocuments.Calc.ClearValues&quot;
+Const cstSubArgs = &quot;Range&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
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ With com.sun.star.sheet.CellFlags
+ lClear = 0 _
+ + .VALUE _
+ + .DATETIME _
+ + .STRING _
+ + .FORMULA
+ Set oRange = _ParseAddress(Range)
+ oRange.XCellRange.clearContents(lClear)
+ End With
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub &apos; SF_Documents.SF_Calc.ClearValues
+
+REM -----------------------------------------------------------------------------
+Public Function CompactLeft(Optional ByVal Range As Variant _
+ , Optional ByVal WholeColumn As Variant _
+ , Optional ByVal FilterFormula As Variant _
+ ) As String
+&apos;&apos;&apos; Delete the columns of a specified range matching a filter expressed as a formula
+&apos;&apos;&apos; applied on each column.
+&apos;&apos;&apos; The deleted cells can span whole columns or be limited to the height of the range
+&apos;&apos;&apos; The execution of the method has no effect on the current selection
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range: the range in which cells have to be erased, as a string
+&apos;&apos;&apos; WholeColumn: when True (default = False), erase whole columns
+&apos;&apos;&apos; FilterFormula: the formula to be applied on each column.
+&apos;&apos;&apos; The column is erased when the formula results in True,
+&apos;&apos;&apos; The formula shall probably involve one or more cells of the first column of the range..
+&apos;&apos;&apos; By default, a column is erased when all the cells of the column are empty,
+&apos;&apos;&apos; i.e. suppose the range is &quot;A1:J200&quot; (height = 0) the default value becomes
+&apos;&apos;&apos; &quot;=(COUNTBLANK(A1:A200)=200)&quot;
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the location of the initial range after compaction,
+&apos;&apos;&apos; or the zero-length string if the whole range has been deleted
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; newrange = oDoc.CompactLeft(&quot;SheetX.G1:L10&quot;) &apos; All empty columns of the range are suppressed
+&apos;&apos;&apos; newrange = oDoc.CompactLeft(&quot;SheetX.G1:L10&quot;, WholeColumn := True, FilterFormula := &quot;=(G$7=&quot;&quot;X&quot;&quot;)&quot;)
+&apos;&apos;&apos; &apos; The columns having a &quot;X&quot; in row 7 are completely suppressed
+
+Dim sCompact As String &apos; Return value
+Dim oSourceAddress As Object &apos; Alias of Range as _Address
+Dim lLastRow As Long &apos; Last used row number in the sheet containing Range
+Dim sFormulaRange As String &apos; Range, as a string, where the FilterFormula must be stored
+Dim vCompact As Variant &apos; Array of Boolean values indicating which columns should be erased
+Dim lCountDeleted As Long &apos; Count the deleted columns
+Dim lCountToDelete As Long &apos; Count contiguous columns to be deleted at once
+Dim sPartialRange As String &apos; Contiguous columns to be deleted
+Dim i As Long
+
+Const cstThisSub = &quot;SFDocuments.Calc.CompactLeft&quot;
+Const cstSubArgs = &quot;Range, [WholeColumn=False], [FilterFormula=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sCompact = &quot;&quot;
+
+Check:
+ If IsMissing(WholeColumn) Or IsEmpty(WholeColumn) Then WholeColumn = False
+ If IsMissing(FilterFormula) Or IsEmpty(FilterFormula) Then FilterFormula = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(WholeColumn, &quot;WholeColumn&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(FilterFormula, &quot;FilterFormula&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ Set oSourceAddress = _ParseAddress(Range)
+
+ With oSourceAddress
+
+ &apos; Set the default formula =&gt; all cells are blank
+ If FilterFormula = &quot;&quot; Then FilterFormula = Printf(&quot;=(COUNTBLANK(%C1%R1:%C1%R2)-&quot; &amp; .Height &amp; &quot;=0)&quot;, Range)
+ &apos; Compute the range where to apply the formula
+ lLastRow = LastRow(.SheetName)
+ sFormulaRange = Offset(Range, lLastRow - .XCellRange.RangeAddress.StartColumn + 1, , 1)
+ SetFormula(sFormulaRange, FilterFormula)
+ &apos; Get the columns to compact: 0 = False, 1 = True
+ vCompact = GetValue(sFormulaRange)
+ If Not IsArray(vCompact) Then vCompact = Array(vCompact)
+ ClearAll(sFormulaRange)
+
+ &apos; Iterates from the last to the first column of the range and remove the columns that match the filter
+ &apos; by groups of contiguous columns
+ lCountDeleted = 0
+ lCountToDelete = 0
+ For i = UBound(vCompact) To 0 Step -1
+ If vCompact(i) = 1 Then lCountToDelete = lCountToDelete + 1
+ If i &gt; 0 And vCompact(i) = 1 Then
+ &apos; Do nothing
+ ElseIf lCountToDelete &gt; 0 Then &apos; The current column must be kept but columns at the left must be removed
+ &apos; Do not forget when the 1st column must be removed
+ sPartialRange = Offset(Range, , Iif(i = 0 And vCompact(i) = 1, 0, i + 1), , lCountToDelete)
+ ShiftLeft(sPartialRange, WholeColumn)
+ lCountDeleted = lCountDeleted + lCountToDelete
+ lCountToDelete = 0
+ End If
+ Next i
+
+ &apos; Compute the final range position
+ If lCountDeleted &lt; .Width Then sCompact = Offset(Range, 0, 0, , .Width - lCountDeleted)
+
+ &apos; Push rightwards the cells that migrated leftwards irrelevantly
+ If Not WholeColumn Then
+ If Len(sCompact) &gt; 0 Then
+ sPartialRange = Offset(sCompact, 0, .Width - lCountDeleted, , lCountDeleted)
+ Else
+ sPartialRange = .RangeName
+ End If
+ ShiftRight(sPartialRange, WholeColumn := False)
+ End If
+
+ End With
+
+Finally:
+ CompactLeft = sCompact
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ &apos; When error, return the original range
+ If Not IsNull(oSourceAddress) Then sCompact = oSourceAddress.RangeName
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.CompactLeft
+
+REM -----------------------------------------------------------------------------
+Public Function CompactUp(Optional ByVal Range As Variant _
+ , Optional ByVal WholeRow As Variant _
+ , Optional ByVal FilterFormula As Variant _
+ ) As String
+&apos;&apos;&apos; Delete the rows of a specified range matching a filter expressed as a formula
+&apos;&apos;&apos; applied on each row.
+&apos;&apos;&apos; The deleted cells can span whole rows or be limited to the width of the range
+&apos;&apos;&apos; The execution of the method has no effect on the current selection
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range: the range in which cells have to be erased, as a string
+&apos;&apos;&apos; WholeRow: when True (default = False), erase whole rows
+&apos;&apos;&apos; FilterFormula: the formula to be applied on each row.
+&apos;&apos;&apos; The row is erased when the formula results in True,
+&apos;&apos;&apos; The formula shall probably involve one or more cells of the first row of the range..
+&apos;&apos;&apos; By default, a row is erased when all the cells of the row are empty,
+&apos;&apos;&apos; i.e. suppose the range is &quot;A1:J200&quot; (width = 10) the default value becomes
+&apos;&apos;&apos; &quot;=(COUNTBLANK(A1:J1)=10)&quot;
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the location of the initial range after compaction,
+&apos;&apos;&apos; or the zero-length string if the whole range has been deleted
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; newrange = oDoc.CompactUp(&quot;SheetX.G1:L10&quot;) &apos; All empty rows of the range are suppressed
+&apos;&apos;&apos; newrange = oDoc.CompactUp(&quot;SheetX.G1:L10&quot;, WholeRow := True, FilterFormula := &quot;=(G1=&quot;&quot;X&quot;&quot;)&quot;)
+&apos;&apos;&apos; &apos; The rows having a &quot;X&quot; in column G are completely suppressed
+
+Dim sCompact As String &apos; Return value
+Dim oSourceAddress As Object &apos; Alias of Range as _Address
+Dim lLastCol As Long &apos; Last used column number in the sheet containing Range
+Dim sFormulaRange As String &apos; Range, as a string, where the FilterFormula must be stored
+Dim vCompact As Variant &apos; Array of Boolean values indicating which rows should be erased
+Dim lCountDeleted As Long &apos; Count the deleted rows
+Dim lCountToDelete As Long &apos; Count contiguous rows to be deleted at once
+Dim sPartialRange As String &apos; Contiguous rows to be deleted
+Dim i As Long
+
+Const cstThisSub = &quot;SFDocuments.Calc.CompactUp&quot;
+Const cstSubArgs = &quot;Range, [WholeRow=False], [FilterFormula=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sCompact = &quot;&quot;
+
+Check:
+ If IsMissing(WholeRow) Or IsEmpty(WholeRow) Then WholeRow = False
+ If IsMissing(FilterFormula) Or IsEmpty(FilterFormula) Then FilterFormula = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(WholeRow, &quot;WholeRow&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(FilterFormula, &quot;FilterFormula&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ Set oSourceAddress = _ParseAddress(Range)
+
+ With oSourceAddress
+
+ &apos; Set the default formula =&gt; all cells are blank
+ If FilterFormula = &quot;&quot; Then FilterFormula = Printf(&quot;=(COUNTBLANK(%C1%R1:%C2%R1)-&quot; &amp; .Width &amp; &quot;=0)&quot;, Range)
+ &apos; Compute the range where to apply the formula
+ lLastCol = LastColumn(.SheetName)
+ sFormulaRange = Offset(Range, , lLastCol - .XCellRange.RangeAddress.StartRow + 1, , 1)
+ SetFormula(sFormulaRange, FilterFormula)
+ &apos; Get the rows to compact: 0 = False, 1 = True
+ vCompact = GetValue(sFormulaRange)
+ If Not IsArray(vCompact) Then vCompact = Array(vCompact)
+ ClearAll(sFormulaRange)
+
+ &apos; Iterates from the last to the first row of the range and remove the rows that match the filter
+ &apos; by groups of contiguous rows
+ lCountDeleted = 0
+ lCountToDelete = 0
+ For i = UBound(vCompact) To 0 Step -1
+ If vCompact(i) = 1 Then lCountToDelete = lCountToDelete + 1
+ If i &gt; 0 And vCompact(i) = 1 Then
+ &apos; Do nothing
+ ElseIf lCountToDelete &gt; 0 Then &apos; The current row must be kept but rows below must be removed
+ &apos; Do not forget when the 1st row must be removed
+ sPartialRange = Offset(Range, Iif(i = 0 And vCompact(i) = 1, 0, i + 1), , lCountToDelete)
+ ShiftUp(sPartialRange, WholeRow)
+ lCountDeleted = lCountDeleted + lCountToDelete
+ lCountToDelete = 0
+ End If
+ Next i
+
+ &apos; Compute the final range position
+ If lCountDeleted &lt; .Height Then sCompact = Offset(Range, 0, 0, .Height - lCountDeleted)
+
+ &apos; Push downwards the cells that migrated upwards irrelevantly
+ If Not WholeRow Then
+ If Len(sCompact) &gt; 0 Then
+ sPartialRange = Offset(sCompact, .Height - lCountDeleted, 0, lCountDeleted)
+ Else
+ sPartialRange = .RangeName
+ End If
+ ShiftDown(sPartialRange, WholeRow := False)
+ End If
+
+ End With
+
+Finally:
+ CompactUp = sCompact
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ &apos; When error, return the original range
+ If Not IsNull(oSourceAddress) Then sCompact = oSourceAddress.RangeName
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.CompactUp
+
+REM -----------------------------------------------------------------------------
+Public Function CopySheet(Optional ByVal SheetName As Variant _
+ , Optional ByVal NewName As Variant _
+ , Optional ByVal BeforeSheet As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Copy a specified sheet before an existing sheet or at the end of the list of sheets
+&apos;&apos;&apos; The sheet to copy may be inside any open Calc document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SheetName: The name of the sheet to copy or its reference
+&apos;&apos;&apos; NewName: Must not exist
+&apos;&apos;&apos; BeforeSheet: The name (string) or index (numeric, starting from 1) of the sheet before which to insert
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the sheet could be copied successfully
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DUPLICATESHEETERROR A sheet with the given name exists already
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.CopySheet(&quot;SheetX&quot;, &quot;SheetY&quot;)
+&apos;&apos;&apos; &apos; Copy within the same document
+&apos;&apos;&apos; Dim oDocA As Object : Set oDocA = ui.OpenDocument(&quot;C:\Temp\FileA.ods&quot;, Hidden := True, ReadOnly := True)
+&apos;&apos;&apos; Dim oDocB As Object : Set oDocB = ui.OpenDocument(&quot;C:\Temp\FileB.ods&quot;)
+&apos;&apos;&apos; oDocB.CopySheet(oDocA.Sheet(&quot;SheetX&quot;), &quot;SheetY&quot;)
+&apos;&apos;&apos; &apos; Copy from 1 file to another and put the new sheet at the end
+
+Dim bCopy As Boolean &apos; Return value
+Dim oSheets As Object &apos; com.sun.star.sheet.XSpreadsheets
+Dim vSheets As Variant &apos; List of existing sheets
+Dim lSheetIndex As Long &apos; Index of a sheet
+Dim oSheet As Object &apos; Alias of SheetName as reference
+Dim lRandom As Long &apos; Output of random number generator
+Dim sRandom &apos; Random sheet name
+Const cstThisSub = &quot;SFDocuments.Calc.CopySheet&quot;
+Const cstSubArgs = &quot;SheetName, NewName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bCopy = False
+
+Check:
+ If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+ End If
+
+Try:
+ &apos; Determine the index of the sheet before which to insert the copy
+ Set oSheets = _Component.getSheets
+ vSheets = oSheets.getElementNames()
+ If VarType(BeforeSheet) = V_STRING Then
+ lSheetIndex = ScriptForge.SF_Array.IndexOf(vSheets, BeforeSheet)
+ Else
+ lSheetIndex = BeforeSheet - 1
+ If lSheetIndex &lt; 0 Then lSheetIndex = 0
+ If lSheetIndex &gt; UBound(vSheets) Then lSheetIndex = UBound(vSheets) + 1
+ End If
+
+ &apos; Copy sheet inside the same document OR import from another document
+ If VarType(SheetName) = V_STRING Then
+ _Component.getSheets.copyByName(SheetName, NewName, lSheetIndex)
+ Else
+ Set oSheet = SheetName
+ With oSheet
+ &apos; If a sheet with same name as input exists in the target sheet, rename it first with a random name
+ sRandom = &quot;&quot;
+ If ScriptForge.SF_Array.Contains(vSheets, .SheetName) Then
+ lRandom = ScriptForge.SF_Session.ExecuteCalcFunction(&quot;RANDBETWEEN&quot;, 1, 9999999)
+ sRandom = &quot;SF_&quot; &amp; Right(&quot;0000000&quot; &amp; lRandom, 7)
+ oSheets.getByName(.SheetName).setName(sRandom)
+ End If
+ &apos; Import i.o. Copy
+ oSheets.importSheet(oSheet.Component, .SheetName, lSheetIndex)
+ &apos; Rename to new sheet name
+ oSheets.getByName(.SheetName).setName(NewName)
+ &apos; Reset random name
+ If Len(sRandom) &gt; 0 Then oSheets.getByName(srandom).setName(.SheetName)
+ End With
+ End If
+ bCopy = True
+
+Finally:
+ CopySheet = bCopy
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchDuplicate:
+ ScriptForge.SF_Exception.RaiseFatal(DUPLICATESHEETERROR, &quot;NewName&quot;, NewName, &quot;Document&quot;, [_Super]._FileIdent())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.CopySheet
+
+REM -----------------------------------------------------------------------------
+Public Function CopySheetFromFile(Optional ByVal FileName As Variant _
+ , Optional ByVal SheetName As Variant _
+ , Optional ByVal NewName As Variant _
+ , Optional ByVal BeforeSheet As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Copy a specified sheet before an existing sheet or at the end of the list of sheets
+&apos;&apos;&apos; The sheet to copy is located inside any closed Calc document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FileName: Identifies the file to open. It must follow the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; The file must not be protected with a password
+&apos;&apos;&apos; SheetName: The name of the sheet to copy
+&apos;&apos;&apos; NewName: Must not exist
+&apos;&apos;&apos; BeforeSheet: The name (string) or index (numeric, starting from 1) of the sheet before which to insert
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the sheet could be created
+&apos;&apos;&apos; The created sheet is blank when the input file is not a Calc file
+&apos;&apos;&apos; The created sheet contains an error message when the input sheet was not found
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DUPLICATESHEETERROR A sheet with the given name exists already
+&apos;&apos;&apos; UNKNOWNFILEERROR The input file is unknown
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.CopySheetFromFile(&quot;C:\MyFile.ods&quot;, &quot;SheetX&quot;, &quot;SheetY&quot;, 3)
+
+Dim bCopy As Boolean &apos; Return value
+Dim oSheet As Object &apos; com.sun.star.sheet.XSpreadsheet
+Dim sFileName As String &apos; URL alias of FileName
+Dim FSO As Object &apos; SF_FileSystem
+Const cstThisSub = &quot;SFDocuments.Calc.CopySheetFromFile&quot;
+Const cstSubArgs = &quot;FileName, SheetName, NewName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bCopy = False
+
+Check:
+ If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+ If Not _ValidateSheet(BeforeSheet, &quot;BeforeSheet&quot;, , True, , True) Then GoTo Finally
+ End If
+
+Try:
+ Set FSO = ScriptForge.SF_FileSystem
+ &apos; Does the input file exist ?
+ If Not FSO.FileExists(FileName) Then GoTo CatchNotExists
+ sFileName = FSO._ConvertToUrl(FileName)
+
+ &apos; Insert a blank new sheet and import sheet from file via link setting and deletion
+ If Not InsertSheet(Newname, BeforeSheet) Then GoTo Finally
+ Set oSheet = _Component.getSheets.getByName(NewName)
+ With oSheet
+ .link(sFileName,SheetName, &quot;&quot;, &quot;&quot;, com.sun.star.sheet.SheetLinkMode.NORMAL)
+ .LinkMode = com.sun.star.sheet.SheetLinkMode.NONE
+ .LinkURL = &quot;&quot;
+ End With
+ bCopy = True
+
+Finally:
+ CopySheetFromFile = bCopy
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchNotExists:
+ ScriptForge.SF_Exception.RaiseFatal(UNKNOWNFILEERROR, &quot;FileName&quot;, FileName)
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.CopySheetFromFile
+
+REM -----------------------------------------------------------------------------
+Public Function CopyToCell(Optional ByVal SourceRange As Variant _
+ , Optional ByVal DestinationCell As Variant _
+ ) As String
+&apos;&apos;&apos; Copy a specified source range to a destination range or cell
+&apos;&apos;&apos; The source range may belong to another open document
+&apos;&apos;&apos; The method imitates the behaviour of a Copy/Paste from a range to a single cell
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SourceRange: the source range as a string if it belongs to the same document
+&apos;&apos;&apos; or as a reference if it belongs to another open Calc document
+&apos;&apos;&apos; DestinationCell: the destination of the copied range of cells, as a string
+&apos;&apos;&apos; If given as a range of cells, the destination will be reduced to its top-left cell
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the modified range of cells
+&apos;&apos;&apos; The modified area depends only on the size of the source area
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.CopyToCell(&quot;SheetX.A1:F10&quot;, &quot;SheetY.C5&quot;)
+&apos;&apos;&apos; &apos; Copy within the same document
+&apos;&apos;&apos; Dim oDocA As Object : Set oDocA = ui.OpenDocument(&quot;C:\Temp\FileA.ods&quot;, Hidden := True, ReadOnly := True)
+&apos;&apos;&apos; Dim oDocB As Object : Set oDocB = ui.OpenDocument(&quot;C:\Temp\FileB.ods&quot;)
+&apos;&apos;&apos; oDocB.CopyToCell(oDocA.Range(&quot;SheetX.A1:F10&quot;), &quot;SheetY.C5&quot;)
+&apos;&apos;&apos; &apos; Copy from 1 file to another
+
+Dim sCopy As String &apos; Return value
+Dim oSource As Object &apos; Alias of SourceRange to avoid &quot;Object variable not set&quot; run-time error
+Dim oSourceAddress As Object &apos; com.sun.star.table.CellRangeAddress
+Dim oDestRange As Object &apos; Destination as a range
+Dim oDestAddress As Object &apos; com.sun.star.table.CellRangeAddress
+Dim oDestCell As Object &apos; com.sun.star.table.CellAddress
+Dim oSelect As Object &apos; Current selection in source
+Dim oClipboard As Object &apos; com.sun.star.datatransfer.XTransferable
+
+Const cstThisSub = &quot;SFDocuments.Calc.CopyToCell&quot;
+Const cstSubArgs = &quot;SourceRange, DestinationCell&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sCopy = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+
+Try:
+ If VarType(SourceRange) = V_STRING Then &apos; Same document - Use UNO copyRange method
+ Set oSourceAddress = _ParseAddress(SourceRange).XCellRange.RangeAddress
+ Set oDestRange = _ParseAddress(DestinationCell)
+ Set oDestAddress = oDestRange.XCellRange.RangeAddress
+ Set oDestCell = New com.sun.star.table.CellAddress
+ With oDestAddress
+ oDestCell.Sheet = .Sheet
+ oDestCell.Column = .StartColumn
+ oDestCell.Row = .StartRow
+ End With
+ oDestRange.XSpreadsheet.copyRange(oDestCell, oSourceAddress)
+ Else &apos; Use clipboard to copy - current selection in Source should be preserved
+ Set oSource = SourceRange
+ With oSource
+ &apos; Keep current selection in source document
+ Set oSelect = .Component.CurrentController.getSelection()
+ &apos; Select, copy the source range and paste in the top-left cell of the destination
+ .Component.CurrentController.select(.XCellRange)
+ Set oClipboard = .Component.CurrentController.getTransferable()
+ _Component.CurrentController.select(_Offset(DestinationCell, 0, 0, 1, 1).XCellRange)
+ _Component.CurrentController.insertTransferable(oClipBoard)
+ &apos; Restore previous selection in Source
+ _RestoreSelections(.Component, oSelect)
+ Set oSourceAddress = .XCellRange.RangeAddress
+ End With
+ End If
+
+ With oSourceAddress
+ sCopy = _Offset(DestinationCell, 0, 0, .EndRow - .StartRow + 1, .EndColumn - .StartColumn + 1).RangeName
+ End With
+
+Finally:
+ CopyToCell = sCopy
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.CopyToCell
+
+REM -----------------------------------------------------------------------------
+Public Function CopyToRange(Optional ByVal SourceRange As Variant _
+ , Optional ByVal DestinationRange As Variant _
+ ) As String
+&apos;&apos;&apos; Copy downwards and/or rightwards a specified source range to a destination range
+&apos;&apos;&apos; The source range may belong to another open document
+&apos;&apos;&apos; The method imitates the behaviour of a Copy/Paste from a range to a larger range
+&apos;&apos;&apos; If the height (resp. width) of the destination area is &gt; 1 row (resp. column)
+&apos;&apos;&apos; then the height (resp. width) of the source must be &lt;= the height (resp. width)
+&apos;&apos;&apos; of the destination. Otherwise nothing happens
+&apos;&apos;&apos; If the height (resp.width) of the destination is = 1 then the destination
+&apos;&apos;&apos; is expanded downwards (resp. rightwards) up to the height (resp. width)
+&apos;&apos;&apos; of the source range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SourceRange: the source range as a string if it belongs to the same document
+&apos;&apos;&apos; or as a reference if it belongs to another open Calc document
+&apos;&apos;&apos; DestinationRange: the destination of the copied range of cells, as a string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the modified range of cells
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.CopyToRange(&quot;SheetX.A1:F10&quot;, &quot;SheetY.C5:J5&quot;)
+&apos;&apos;&apos; &apos; Copy within the same document
+&apos;&apos;&apos; &apos; Returned range: $SheetY.$C$5:$J$14
+&apos;&apos;&apos; Dim oDocA As Object : Set oDocA = ui.OpenDocument(&quot;C:\Temp\FileA.ods&quot;, Hidden := True, ReadOnly := True)
+&apos;&apos;&apos; Dim oDocB As Object : Set oDocB = ui.OpenDocument(&quot;C:\Temp\FileB.ods&quot;)
+&apos;&apos;&apos; oDocB.CopyToRange(oDocA.Range(&quot;SheetX.A1:F10&quot;), &quot;SheetY.C5:J5&quot;)
+&apos;&apos;&apos; &apos; Copy from 1 file to another
+
+Dim sCopy As String &apos; Return value
+Dim oSource As Object &apos; Alias of SourceRange to avoid &quot;Object variable not set&quot; run-time error
+Dim oDestRange As Object &apos; Destination as a range
+Dim oDestCell As Object &apos; com.sun.star.table.CellAddress
+Dim oSelect As Object &apos; Current selection in source
+Dim oClipboard As Object &apos; com.sun.star.datatransfer.XTransferable
+Dim bSameDocument As Boolean &apos; True when source in same document as destination
+Dim lHeight As Long &apos; Height of destination
+Dim lWidth As Long &apos; Width of destination
+
+Const cstThisSub = &quot;SFDocuments.Calc.CopyToRange&quot;
+Const cstSubArgs = &quot;SourceRange, DestinationRange&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sCopy = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+
+Try:
+ &apos; Copy done via clipboard
+
+ &apos; Check Height/Width destination = 1 or &gt; Height/Width of source
+ bSameDocument = ( VarType(SourceRange) = V_STRING )
+ If bSameDocument Then Set oSource = _ParseAddress(SourceRange) Else Set oSource = SourceRange
+ Set oDestRange = _ParseAddress(DestinationRange)
+ With oDestRange
+ lHeight = .Height
+ lWidth = .Width
+ If lHeight = 1 Then
+ lHeight = oSource.Height &apos; Future height
+ ElseIf lHeight &lt; oSource.Height Then
+ GoTo Finally
+ End If
+ If lWidth = 1 Then
+ lWidth = oSource.Width &apos; Future width
+ ElseIf lWidth &lt; oSource.Width Then
+ GoTo Finally
+ End If
+ End With
+
+ With oSource
+ &apos; Store actual selection in source
+ Set oSelect = .Component.CurrentController.getSelection()
+ &apos; Select, copy the source range and paste in the destination
+ .Component.CurrentController.select(.XCellRange)
+ Set oClipboard = .Component.CurrentController.getTransferable()
+ _Component.CurrentController.select(oDestRange.XCellRange)
+ _Component.CurrentController.insertTransferable(oClipBoard)
+ &apos; Restore selection in source
+ _RestoreSelections(.Component, oSelect)
+ End With
+
+ sCopy = _Offset(oDestRange, 0, 0, lHeight, lWidth).RangeName
+
+Finally:
+ CopyToRange = sCopy
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.CopyToRange
+
+REM -----------------------------------------------------------------------------
+Public Function CreateChart(Optional ByVal ChartName As Variant _
+ , Optional ByVal SheetName As Variant _
+ , Optional ByVal Range As Variant _
+ , Optional ColumnHeader As Variant _
+ , Optional RowHeader As Variant _
+ ) As Variant
+&apos;&apos;&apos; Return a new chart instance initialized with default values
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; ChartName: The user-defined name of the new chart
+&apos;&apos;&apos; SheetName: The name of an existing sheet
+&apos;&apos;&apos; Range: the cell or the range as a string that should be drawn
+&apos;&apos;&apos; ColumnHeader: when True, the topmost row of the range will be used to set labels for the category axis or the legend.
+&apos;&apos;&apos; Default = False
+&apos;&apos;&apos; RowHeader: when True, the leftmost column of the range will be used to set labels for the category axis or the legend.
+&apos;&apos;&apos; Default = False
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A new chart service instance
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DUPLICATECHARTERROR A chart with the same name exists already in the given sheet
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim oChart As Object
+&apos;&apos;&apos; Set oChart = oDoc.CreateChart(&quot;myChart&quot;, &quot;SheetX&quot;, &quot;A1:C8&quot;, ColumnHeader := True)
+
+Dim oChart As Object &apos; Return value
+Dim vCharts As Variant &apos; List of pre-existing charts
+Dim oSheet As Object &apos; Alias of SheetName as reference
+Dim oRange As Object &apos; Alias of Range
+Dim oRectangle as new com.sun.star.awt.Rectangle &apos; Simple shape
+
+Const cstThisSub = &quot;SFDocuments.Calc.CreateChart&quot;
+Const cstSubArgs = &quot;ChartName, SheetName, Range, [ColumnHeader=False], [RowHeader=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oChart = Nothing
+
+Check:
+ If IsMissing(RowHeader) Or IsEmpty(RowHeader) Then Rowheader = False
+ If IsMissing(ColumnHeader) Or IsEmpty(ColumnHeader) Then ColumnHeader = False
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ChartName, &quot;ChartName&quot;, V_STRING) Then GoTo Finally
+ If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ColumnHeader, &quot;ColumnHeader&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(RowHeader, &quot;RowHeader&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+ vCharts = Charts(SheetName)
+ If ScriptForge.SF_Array.Contains(vCharts, ChartName, CaseSensitive := True) Then GoTo CatchDuplicate
+
+Try:
+ &apos; The rectangular shape receives arbitrary values. User can Resize() it later
+ With oRectangle
+ .X = 0 : .Y = 0
+ .Width = 8000 : .Height = 6000
+ End With
+ &apos; Initialize sheet and range
+ Set oSheet = _Component.getSheets.getByName(SheetName)
+ Set oRange = _ParseAddress(Range)
+ &apos; Create the chart and get ihe corresponding chart instance
+ oSheet.getCharts.addNewByName(ChartName, oRectangle, Array(oRange.XCellRange.RangeAddress), ColumnHeader, RowHeader)
+ Set oChart = Charts(SheetName, ChartName)
+ oChart._Shape.Name = ChartName &apos; Both user-defined and internal names match ChartName
+ oChart._Diagram.Wall.FillColor = RGB(255, 255, 255) &apos; Align on background color set by the user interface by default
+
+Finally:
+ Set CreateChart = oChart
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchDuplicate:
+ ScriptForge.SF_Exception.RaiseFatal(DUPLICATECHARTERROR, &quot;ChartName&quot;, ChartName, &quot;SheetName&quot;, SheetName, &quot;Document&quot;, [_Super]._FileIdent())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.CreateChart
+
+REM -----------------------------------------------------------------------------
+Public Function CreatePivotTable(Optional ByVal PivotTableName As Variant _
+ , Optional ByVal SourceRange As Variant _
+ , Optional ByVal TargetCell As Variant _
+ , Optional ByRef DataFields As Variant _
+ , Optional ByRef RowFields As Variant _
+ , Optional ByRef ColumnFields As Variant _
+ , Optional ByVal FilterButton As Variant _
+ , Optional ByVal RowTotals As Variant _
+ , Optional ByVal ColumnTotals As Variant _
+ ) As String
+&apos;&apos;&apos; Create a new pivot table with the properties defined by the arguments.
+&apos;&apos;&apos; If a pivot table with the same name exists already in the targeted sheet, it will be erased without warning.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; PivotTableName: The user-defined name of the new pivottable
+&apos;&apos;&apos; SourceRange: The range as a string containing the raw data.
+&apos;&apos;&apos; The first row of the range is presumed to contain the field names of the new pivot table
+&apos;&apos;&apos; TargetCell: the top left cell or the range as a string where to locate the pivot table.
+&apos;&apos;&apos; Only the top left cell of the range will be considered.
+&apos;&apos;&apos; DataFields: A single string or an array of field name + function to apply, formatted like:
+&apos;&apos;&apos; Array(&quot;FieldName[;Function]&quot;, ...)
+&apos;&apos;&apos; The allowed functions are: Sum, Count, Average, Max, Min, Product, CountNums, StDev, StDevP, Var, VarP and Median.
+&apos;&apos;&apos; The default function is: When the values are all numerical, Sum is used, otherwise Count
+&apos;&apos;&apos; RowFields: A single string or an array of the field names heading the pivot table rows
+&apos;&apos;&apos; ColumnFields: A single string or an array of the field names heading the pivot table columns
+&apos;&apos;&apos; FilterButton: When True (default), display a &quot;Filter&quot; button above the pivot table
+&apos;&apos;&apos; RowTotals: When True (default), display a separate column for row totals
+&apos;&apos;&apos; ColumnTotals: When True (default), display a separate row for column totals
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Return the range where the new pivot table is deployed.
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim vData As Variant, oDoc As Object, sTable As String, sPivot As String
+&apos;&apos;&apos; vData = Array(Array(&quot;Item&quot;, &quot;State&quot;, &quot;Team&quot;, &quot;2002&quot;, &quot;2003&quot;, &quot;2004&quot;), _
+&apos;&apos;&apos; Array(&quot;Books&quot;, &quot;Michigan&quot;, &quot;Jean&quot;, 14788, 30222, 23490), _
+&apos;&apos;&apos; Array(&quot;Candy&quot;, &quot;Michigan&quot;, &quot;Jean&quot;, 26388, 15641, 32849), _
+&apos;&apos;&apos; Array(&quot;Pens&quot;, &quot;Michigan&quot;, &quot;Jean&quot;, 16569, 32675, 25396), _
+&apos;&apos;&apos; Array(&quot;Books&quot;, &quot;Michigan&quot;, &quot;Volker&quot;, 21961, 21242, 29009), _
+&apos;&apos;&apos; Array(&quot;Candy&quot;, &quot;Michigan&quot;, &quot;Volker&quot;, 26142, 22407, 32841))
+&apos;&apos;&apos; Set oDoc = ui.CreateDocument(&quot;Calc&quot;)
+&apos;&apos;&apos; sTable = oDoc.SetArray(&quot;A1&quot;, vData)
+&apos;&apos;&apos; sPivot = oDoc.CreatePivotTable(&quot;PT1&quot;, sTable, &quot;H1&quot;, Array(&quot;2002&quot;, &quot;2003;count&quot;, &quot;2004;average&quot;), &quot;Item&quot;, Array(&quot;State&quot;, &quot;Team&quot;), False)
+
+Dim sPivotTable As String &apos; Return value
+Dim vData As Variant &apos; Alias of DataFields
+Dim vRows As Variant &apos; Alias of RowFields
+Dim vColumns As Variant &apos; Alias of ColumnFields
+Dim oSourceAddress As Object &apos; Source as an _Address
+Dim oTargetAddress As Object &apos; Target as an _Address
+Dim vHeaders As Variant &apos; Array of header fields in the source range
+Dim oPivotTables As Object &apos; com.sun.star.sheet.XDataPilotTables
+Dim oDescriptor As Object &apos; com.sun.star.sheet.DataPilotDescriptor
+Dim oFields As Object &apos; ScDataPilotFieldsObj - Collection of fields
+Dim oField As Object &apos; ScDataPilotFieldsObj - A single field
+Dim sField As String &apos; A single field name
+Dim sData As String &apos; A single data field name + function
+Dim vDataField As Variant &apos; A single vData element, split on semicolon
+Dim sFunction As String &apos; Function to apply on a data field (string)
+Dim iFunction As Integer &apos; Equivalent of sFunction as com.sun.star.sheet.GeneralFunction2 constant
+Dim oOutputRange As Object &apos; com.sun.star.table.CellRangeAddress
+Dim i As Integer
+
+Const cstThisSub = &quot;SFDocuments.Calc.CreatePivotTable&quot;
+Const cstSubArgs = &quot;PivotTableName, SourceRange, TargetCell, DataFields, [RowFields], [ColumnFields]&quot; _
+ &amp; &quot;, [FilterButton=True], [RowTotals=True], [ColumnTotals=True]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sPivotTable = &quot;&quot;
+
+Check:
+ If IsMissing(RowFields) Or IsEmpty(RowFields) Then RowFields = Array()
+ If IsMissing(ColumnFields) Or IsEmpty(ColumnFields) Then ColumnFields = Array()
+ If IsMissing(FilterButton) Or IsEmpty(FilterButton) Then FilterButton = True
+ If IsMissing(RowTotals) Or IsEmpty(RowTotals) Then RowTotals = True
+ If IsMissing(ColumnTotals) Or IsEmpty(ColumnTotals) Then ColumnTotals = True
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(PivotTableName, &quot;PivotTableName&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(SourceRange, &quot;SourceRange&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(TargetCell, &quot;TargetCell&quot;, V_STRING) Then GoTo Finally
+ If IsArray(DataFields) Then
+ If Not ScriptForge.SF_Utils._ValidateArray(DataFields, &quot;DataFields&quot;, 1, V_STRING, True) Then GoTo Finally
+ Else
+ If Not ScriptForge.SF_Utils._Validate(DataFields, &quot;DataFields&quot;, V_STRING) Then GoTo Finally
+ End If
+ If IsArray(RowFields) Then
+ If Not ScriptForge.SF_Utils._ValidateArray(RowFields, &quot;RowFields&quot;, 1, V_STRING, True) Then GoTo Finally
+ Else
+ If Not ScriptForge.SF_Utils._Validate(RowFields, &quot;RowFields&quot;, V_STRING) Then GoTo Finally
+ End If
+ If IsArray(ColumnFields) Then
+ If Not ScriptForge.SF_Utils._ValidateArray(ColumnFields, &quot;ColumnFields&quot;, 1, V_STRING, True) Then GoTo Finally
+ Else
+ If Not ScriptForge.SF_Utils._Validate(ColumnFields, &quot;ColumnFields&quot;, V_STRING) Then GoTo Finally
+ End If
+ If Not ScriptForge.SF_Utils._Validate(FilterButton, &quot;FilterButton&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(RowTotals, &quot;RowTotals&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ColumnTotals, &quot;ColumnTotals&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+ &apos; Next statements must be outside previous If-block to force their execution even in case of internal call
+ If IsArray(DataFields) Then vData = DataFields Else vData = Array(DataFields)
+ If IsArray(RowFields) Then vRows = RowFields Else vRows = Array(RowFields)
+ If IsArray(ColumnFields) Then vColumns = ColumnFields Else vColumns = Array(ColumnFields)
+
+Try:
+
+ Set oSourceAddress = _ParseAddress(SourceRange)
+ vHeaders = GetValue(Offset(SourceRange, 0, 0, 1)) &apos; Content of the first row of the source
+ Set oTargetAddress = _Offset(TargetCell, 0, 0, 1, 1) &apos; Retain the top left cell only
+ Set oPivotTables = oTargetAddress.XSpreadsheet.getDataPilotTables()
+
+ &apos; Initialize new pivot table
+ Set oDescriptor = oPivotTables.createDataPilotDescriptor()
+ oDescriptor.setSourceRange(oSourceAddress.XCellRange.RangeAddress)
+ Set oFields = oDescriptor.getDataPilotFields()
+
+ &apos; Set row fields
+ For i = 0 To UBound(vRows)
+ sField = vRows(i)
+ If Len(sField) &gt; 0 Then
+ If Not ScriptForge.SF_Utils._Validate(sField, &quot;RowFields&quot;, V_STRING, vHeaders) Then GoTo Finally
+ Set oField = oFields.getByName(sField)
+ oField.Orientation = com.sun.star.sheet.DataPilotFieldOrientation.ROW
+ End If
+ Next i
+
+ &apos; Set column fields
+ For i = 0 To UBound(vColumns)
+ sField = vColumns(i)
+ If Len(sField) &gt; 0 Then
+ If Not ScriptForge.SF_Utils._Validate(sField, &quot;ColumnFields&quot;, V_STRING, vHeaders) Then GoTo Finally
+ Set oField = oFields.getByName(sField)
+ oField.Orientation = com.sun.star.sheet.DataPilotFieldOrientation.COLUMN
+ End If
+ Next i
+
+ &apos; Set data fields
+ For i = 0 To UBound(vData)
+ sData = vData(i)
+ &apos; Minimal parsing
+ If Right(sData, 1) = &quot;;&quot; Then sData = Left(sData, Len(sData) - 1)
+ vDataField = Split(sData, &quot;;&quot;)
+ sField = vDataField(0)
+ If UBound(vDataField) &gt; 0 Then sFunction = vDataField(1) Else sFunction = &quot;&quot;
+ &apos; Define field properties
+ If Len(sField) &gt; 0 Then
+ If Not ScriptForge.SF_Utils._Validate(sField, &quot;DataFields&quot;, V_STRING, vHeaders) Then GoTo Finally
+ Set oField = oFields.getByName(sField)
+ oField.Orientation = com.sun.star.sheet.DataPilotFieldOrientation.DATA
+ &apos; Associate the correct function
+ With com.sun.star.sheet.GeneralFunction2
+ Select Case UCase(sFunction)
+ Case &quot;&quot; : iFunction = .AUTO
+ Case &quot;SUM&quot; : iFunction = .SUM
+ Case &quot;COUNT&quot; : iFunction = .COUNT
+ Case &quot;AVERAGE&quot; : iFunction = .AVERAGE
+ Case &quot;MAX&quot; : iFunction = .MAX
+ Case &quot;MIN&quot; : iFunction = .MIN
+ Case &quot;PRODUCT&quot; : iFunction = .PRODUCT
+ Case &quot;COUNTNUMS&quot;: iFunction = .COUNTNUMS
+ Case &quot;STDEV&quot; : iFunction = .STDEV
+ Case &quot;STDEVP&quot; : iFunction = .STDEVP
+ Case &quot;VAR&quot; : iFunction = .VAR
+ Case &quot;VARP&quot; : iFunction = .VARP
+ Case &quot;MEDIAN&quot; : iFunction = .MEDIAN
+ Case Else
+ If Not ScriptForge.SF_Utils._Validate(sFunction, &quot;DataFields/Function&quot;, V_STRING _
+ , Array(&quot;Sum&quot;, &quot;Count&quot;, &quot;Average&quot;, &quot;Max&quot;, &quot;Min&quot;, &quot;Product&quot;, &quot;CountNums&quot; _
+ , &quot;StDev&quot;, &quot;StDevP&quot;, &quot;Var&quot;, &quot;VarP&quot;, &quot;Median&quot;) _
+ ) Then GoTo Finally
+ End Select
+ End With
+ oField.Function2 = iFunction
+ End If
+ Next i
+
+ &apos; Remove any pivot table with same name
+ If oPivotTables.hasByName(PivotTableName) Then oPivotTables.removeByName(PivotTableName)
+
+ &apos; Finalize the new pivot table
+ oDescriptor.ShowFilterButton = FilterButton
+ oDescriptor.RowGrand = RowTotals
+ oDescriptor.ColumnGrand = ColumnTotals
+ oPivotTables.insertNewByName(PivotTableName, oTargetAddress.XCellRange.getCellByPosition(0, 0).CellAddress, oDescriptor)
+
+ &apos; Determine the range of the new pivot table
+ Set oOutputRange = oPivotTables.getByName(PivotTableName).OutputRange
+ With oOutputRange
+ sPivotTable = _Component.getSheets().getCellRangeByPosition(.StartColumn, .StartRow, .EndColumn, .EndRow, .Sheet).AbsoluteName
+ End With
+
+Finally:
+ CreatePivotTable = sPivotTable
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.CreatePivotTable
+
+REM -----------------------------------------------------------------------------
+Public Function DAvg(Optional ByVal Range As Variant) As Double
+&apos;&apos;&apos; Get the average of the numeric values stored in the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range as a string where to get the values from
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The average of the numeric values as a double
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Val = oDoc.DAvg(&quot;~.A1:A1000&quot;)
+
+Try:
+ DAvg = _DFunction(&quot;DAvg&quot;, Range)
+
+Finally:
+ Exit Function
+End Function &apos; SF_Documents.SF_Calc.DAvg
+
+REM -----------------------------------------------------------------------------
+Public Function DCount(Optional ByVal Range As Variant) As Long
+&apos;&apos;&apos; Get the number of numeric values stored in the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range as a string where to get the values from
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The number of numeric values as a Long
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Val = oDoc.DCount(&quot;~.A1:A1000&quot;)
+
+Try:
+ DCount = _DFunction(&quot;DCount&quot;, Range)
+
+Finally:
+ Exit Function
+End Function &apos; SF_Documents.SF_Calc.DCount
+
+REM -----------------------------------------------------------------------------
+Public Function DMax(Optional ByVal Range As Variant) As Double
+&apos;&apos;&apos; Get the greatest of the numeric values stored in the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range as a string where to get the values from
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The greatest of the numeric values as a double
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Val = oDoc.DMax(&quot;~.A1:A1000&quot;)
+
+Try:
+ DMax = _DFunction(&quot;DMax&quot;, Range)
+
+Finally:
+ Exit Function
+End Function &apos; SF_Documents.SF_Calc.DMax
+
+REM -----------------------------------------------------------------------------
+Public Function DMin(Optional ByVal Range As Variant) As Double
+&apos;&apos;&apos; Get the smallest of the numeric values stored in the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range as a string where to get the values from
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The smallest of the numeric values as a double
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Val = oDoc.DMin(&quot;~.A1:A1000&quot;)
+
+Try:
+ DMin = _DFunction(&quot;DMin&quot;, Range)
+
+Finally:
+ Exit Function
+End Function &apos; SF_Documents.SF_Calc.DMin
+
+REM -----------------------------------------------------------------------------
+Public Function DSum(Optional ByVal Range As Variant) As Double
+&apos;&apos;&apos; Get sum of the numeric values stored in the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range as a string where to get the values from
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The sum of the numeric values as a double
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Val = oDoc.DSum(&quot;~.A1:A1000&quot;)
+
+Try:
+ DSum = _DFunction(&quot;DSum&quot;, Range)
+
+Finally:
+ Exit Function
+End Function &apos; SF_Documents.SF_Calc.DSum
+
+REM -----------------------------------------------------------------------------
+Public Function ExportRangeToFile(Optional ByVal Range As Variant _
+ , Optional ByVal FileName As Variant _
+ , Optional ByVal ImageType As Variant _
+ , Optional ByVal Overwrite As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Store the given range as an image to the given file location
+&apos;&apos;&apos; Actual selections are not impacted
+&apos;&apos;&apos; Inspired by https://stackoverflow.com/questions/30509532/how-to-export-cell-range-to-pdf-file
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range: sheet name or cell range to be exported, as a string
+&apos;&apos;&apos; FileName: Identifies the file where to save. It must follow the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; ImageType: the name of the targeted media type
+&apos;&apos;&apos; Allowed values: jpeg, pdf (default) and png
+&apos;&apos;&apos; Overwrite: True if the destination file may be overwritten (default = False)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False if the document could not be saved
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; RANGEEXPORTERROR The destination has its readonly attribute set or overwriting rejected
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.ExportRangeToFile(&apos;SheetX.B2:J15&quot;, &quot;C:\Me\Range2.png&quot;, ImageType := &quot;png&quot;, Overwrite := True)
+
+Dim bSaved As Boolean &apos; return value
+Dim oSfa As Object &apos; com.sun.star.ucb.SimpleFileAccess
+Dim sFile As String &apos; Alias of FileName
+Dim vStoreArguments As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim vFilterData As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim FSO As Object &apos; SF_FileSystem
+Dim vImageTypes As Variant &apos; Array of permitted image types
+Dim vFilters As Variant &apos; Array of corresponding filters in the same order as vImageTypes
+Dim sFilter As String &apos; The filter to apply
+Dim oSelect As Object &apos; Currently selected range(s)
+Dim oAddress As Object &apos; Alias of Range
+
+Const cstImageTypes = &quot;jpeg,pdf,png&quot;
+Const cstFilters = &quot;calc_jpg_Export,calc_pdf_Export,calc_png_Export&quot;
+
+Const cstThisSub = &quot;SFDocuments.Calc.ExportRangeToFile&quot;
+Const cstSubArgs = &quot;Range, FileName, [ImageType=&quot;&quot;pdf&quot;&quot;|&quot;&quot;jpeg&quot;&quot;|&quot;&quot;png&quot;&quot;], [Overwrite=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo CatchError
+ bSaved = False
+
+Check:
+ If IsMissing(ImageType) Or IsEmpty(ImageType) Then ImageType = &quot;pdf&quot;
+ If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = False
+
+ vImageTypes = Split(cstImageTypes, &quot;,&quot;)
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ImageType, &quot;ImageType&quot;, V_STRING, vImageTypes) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+ &apos; Check destination file overwriting
+ Set FSO = CreateScriptService(&quot;FileSystem&quot;)
+ sFile = FSO._ConvertToUrl(FileName)
+ If FSO.FileExists(FileName) Then
+ If Overwrite = False Then GoTo CatchError
+ Set oSfa = ScriptForge.SF_Utils._GetUNOService(&quot;FileAccess&quot;)
+ If oSfa.isReadonly(sFile) Then GoTo CatchError
+ End If
+
+Try:
+ &apos; Setup arguments
+ vFilters = Split(cstFilters, &quot;,&quot;)
+ sFilter = vFilters(ScriptForge.SF_Array.IndexOf(vImageTypes, ImageType, CaseSensitive := False))
+ Set oAddress = _ParseAddress(Range)
+
+ &apos; The filter arguments differ between
+ &apos; 1) pdf : store range in Selection property value
+ &apos; 2) png, jpeg : save current selection, select range, restore initial selection
+ If LCase(ImageType) = &quot;pdf&quot; Then
+ vFilterData = Array(ScriptForge.SF_Utils._MakePropertyValue(&quot;Selection&quot;, oAddress.XCellRange) )
+ vStoreArguments = Array( _
+ ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterName&quot;, sFilter) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterData&quot;, vFilterData) _
+ )
+ Else &apos; png, jpeg
+ &apos; Save the current selection(s)
+ Set oSelect = _Component.CurrentController.getSelection()
+ _Component.CurrentController.select(oAddress.XCellRange)
+ vStoreArguments = Array( _
+ ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterName&quot;, sFilter) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;SelectionOnly&quot;, True) _
+ )
+ End If
+
+ &apos; Apply the filter and export
+ _Component.storeToUrl(sFile, vStoreArguments)
+ If LCase(ImageType) &lt;&gt; &quot;pdf&quot; Then _RestoreSelections(_Component, oSelect)
+
+ bSaved = True
+
+Finally:
+ ExportRangeToFile = bSaved
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchError:
+ ScriptForge.SF_Exception.RaiseFatal(RANGEEXPORTERROR, &quot;FileName&quot;, FileName, &quot;Overwrite&quot;, Overwrite)
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Chart.ExportRangeToFile
+
+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; SheetName: the name of the sheet containing the requested form or forms
+&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-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(&quot;ThisSheet&quot;)
+&apos;&apos;&apos; Set myForm = oDoc.Forms(&quot;ThisSheet&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 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 = SF_Register._NewForm(oXForm)
+ With oForm
+ Set .[_Parent] = [Me]
+ ._SheetName = SheetName
+ ._FormType = ISCALCFORM
+ Set ._Component = _Component
+ ._Initialize()
+ 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:
+&apos;&apos;&apos; ColumnNumber: the column number, must be in the interval 1 ... 1024
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; a string representation of the column name, in range &apos;A&apos;..&apos;AMJ&apos;
+&apos;&apos;&apos; If ColumnNumber is not in the allowed range, returns a zero-length string
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; MsgBox oDoc.GetColumnName(1022) &apos; &quot;AMH&quot;
+&apos;&apos;&apos; Adapted from a Python function by sundar nataraj
+&apos;&apos;&apos; http://stackoverflow.com/questions/23861680/convert-spreadsheet-number-to-column-letter
+
+Dim sCol As String &apos; Return value
+Const cstThisSub = &quot;SFDocuments.Calc.GetColumnName&quot;
+Const cstSubArgs = &quot;ColumnNumber&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sCol = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not SF_Utils._Validate(ColumnNumber, &quot;ColumnNumber&quot;, V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ If (ColumnNumber &gt; 0) And (ColumnNumber &lt;= MAXCOLS) Then sCol = _GetColumnName(ColumnNumber)
+
+Finally:
+ GetColumnName = sCol
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.GetColumnName
+
+REM -----------------------------------------------------------------------------
+Public Function GetFormula(Optional ByVal Range As Variant) As Variant
+&apos;&apos;&apos; Get the formula(e) stored in the given range of cells
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range as a string where to get the formula from
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A scalar, a zero-based 1D array or a zero-based 2D array of strings
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Val = oDoc.GetFormula(&quot;~.A1:A1000&quot;)
+
+Dim vGet As Variant &apos; Return value
+Dim oAddress As Object &apos; Alias of Range
+Dim vDataArray As Variant &apos; DataArray compatible with .DataArray UNO property
+Const cstThisSub = &quot;SFDocuments.Calc.GetFormula&quot;
+Const cstSubArgs = &quot;Range&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vGet = Empty
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Get the data
+ Set oAddress = _ParseAddress(Range)
+ vDataArray = oAddress.XCellRange.getFormulaArray()
+
+ &apos; Convert the data array to scalar, vector or array
+ vGet = _ConvertFromDataArray(vDataArray)
+
+Finally:
+ GetFormula = vGet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.GetFormula
+
+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.Calc.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_Calc.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function GetValue(Optional ByVal Range As Variant) As Variant
+&apos;&apos;&apos; Get the value(s) stored in the given range of cells
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range as a string where to get the value from
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A scalar, a zero-based 1D array or a zero-based 2D array of strings and doubles
+&apos;&apos;&apos; To convert doubles to dates, use the CDate builtin function
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Val = oDoc.GetValue(&quot;~.A1:A1000&quot;)
+
+Dim vGet As Variant &apos; Return value
+Dim oAddress As Object &apos; Alias of Range
+Dim vDataArray As Variant &apos; DataArray compatible with .DataArray UNO property
+Const cstThisSub = &quot;SFDocuments.Calc.GetValue&quot;
+Const cstSubArgs = &quot;Range&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vGet = Empty
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Get the data
+ Set oAddress = _ParseAddress(Range)
+ vDataArray = oAddress.XCellRange.getDataArray()
+
+ &apos; Convert the data array to scalar, vector or array
+ vGet = _ConvertFromDataArray(vDataArray)
+
+Finally:
+ GetValue = vGet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.GetValue
+
+REM -----------------------------------------------------------------------------
+Public Function ImportFromCSVFile(Optional ByVal FileName As Variant _
+ , Optional ByVal DestinationCell As Variant _
+ , Optional ByVal FilterOptions As Variant _
+ ) As String
+&apos;&apos;&apos; Import the content of a CSV-formatted text file starting from a given cell
+&apos;&apos;&apos; Beforehand the destination area will be cleared from any content and format
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FileName: Identifies the file to open. It must follow the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; DestinationCell: the destination of the copied range of cells, as a string
+&apos;&apos;&apos; If given as range, the destination will be reduced to its top-left cell
+&apos;&apos;&apos; FilterOptions: The arguments of the CSV input filter.
+&apos;&apos;&apos; Read https://wiki.documentfoundation.org/Documentation/DevGuide/Spreadsheet_Documents#Filter_Options_for_the_CSV_Filter
+&apos;&apos;&apos; Default: input file encoding is UTF8
+&apos;&apos;&apos; separator = comma, semi-colon or tabulation
+&apos;&apos;&apos; string delimiter = double quote
+&apos;&apos;&apos; all lines are included
+&apos;&apos;&apos; quoted strings are formatted as texts
+&apos;&apos;&apos; special numbers are detected
+&apos;&apos;&apos; all columns are presumed texts
+&apos;&apos;&apos; language = english/US =&gt; decimal separator is &quot;.&quot;, thousands separator = &quot;,&quot;
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the modified range of cells
+&apos;&apos;&apos; The modified area depends only on the content of the source file
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DOCUMENTOPENERROR The csv file could not be opened
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.ImportFromCSVFile(&quot;C:\Temp\myCsvFile.csv&quot;, &quot;SheetY.C5&quot;)
+
+Dim sImport As String &apos; Return value
+Dim oUI As Object &apos; UI service
+Dim oSource As Object &apos; New Calc document with csv loaded
+Dim oSelect As Object &apos; Current selection in destination
+
+Const cstFilter = &quot;Text - txt - csv (StarCalc)&quot;
+Const cstFilterOptions = &quot;9/44/59/MRG,34,76,1,,1033,true,true&quot;
+Const cstThisSub = &quot;SFDocuments.Calc.ImportFromCSVFile&quot;
+Const cstSubArgs = &quot;FileName, DestinationCell, [FilterOptions]=&quot;&quot;9/44/59/MRG,34,76,1,,1033,true,true&quot;&quot;&quot;
+
+&apos; If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sImport = &quot;&quot;
+
+Check:
+ If IsMissing(FilterOptions) Or IsEmpty(FilterOptions) Then FilterOptions = cstFilterOptions
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+
+Try:
+ &apos; Input file is loaded in an empty worksheet. Data are copied to destination cell
+ Set oUI = CreateScriptService(&quot;UI&quot;)
+ Set oSource = oUI.OpenDocument(FileName _
+ , ReadOnly := True _
+ , Hidden := True _
+ , FilterName := cstFilter _
+ , FilterOptions := FilterOptions _
+ )
+ &apos; Remember current selection and restore it after copy
+ Set oSelect = _Component.CurrentController.getSelection()
+ sImport = CopyToCell(oSource.Range(&quot;*&quot;), DestinationCell)
+ _RestoreSelections(_Component, oSelect)
+
+Finally:
+ If Not IsNull(oSource) Then oSource.CloseDocument(False)
+ ImportFromCSVFile = sImport
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.ImportFromCSVFile
+
+REM -----------------------------------------------------------------------------
+Public Sub ImportFromDatabase(Optional ByVal FileName As Variant _
+ , Optional ByVal RegistrationName As Variant _
+ , Optional ByVal DestinationCell As Variant _
+ , Optional ByVal SQLCommand As Variant _
+ , Optional ByVal DirectSQL As Variant _
+ )
+&apos;&apos;&apos; Import the content of a database table, query or resultset, i.e. the result of a SELECT SQL command,
+&apos;&apos;&apos; starting from a given cell
+&apos;&apos;&apos; Beforehand the destination area will be cleared from any content and format
+&apos;&apos;&apos; The modified area depends only on the content of the source data
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FileName: Identifies the file to open. It must follow the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; RegistrationName: the name of a registered database
+&apos;&apos;&apos; It is ignored if FileName &lt;&gt; &quot;&quot;
+&apos;&apos;&apos; DestinationCell: the destination of the copied range of cells, as a string
+&apos;&apos;&apos; If given as a range of cells, the destination will be reduced to its top-left cell
+&apos;&apos;&apos; SQLCommand: either a table or query name (without square brackets)
+&apos;&apos;&apos; or a full SQL commands where table and fieldnames are preferably surrounded with square brackets
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Implemented as a Sub because the doImport UNO method does not return any error
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; BASEDOCUMENTOPENERROR The database file could not be opened
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.ImportFromDatabase(&quot;C:\Temp\myDbFile.odb&quot;, , &quot;SheetY.C5&quot;, &quot;SELECT * FROM [Employees] ORDER BY [LastName]&quot;)
+
+Dim oDBContext As Object &apos; com.sun.star.sdb.DatabaseContext
+Dim oDatabase As Object &apos; SFDatabases.Database service
+Dim lCommandType As Long &apos; A com.sun.star.sheet.DataImportMode.xxx constant
+Dim oQuery As Object &apos; com.sun.star.ucb.XContent
+Dim bDirect As Boolean &apos; Alias of DirectSQL
+Dim oDestRange As Object &apos; Destination as a range
+Dim oDestAddress As Object &apos; com.sun.star.table.CellRangeAddress
+Dim oDestCell As Object &apos; com.sun.star.table.XCell
+Dim oSelect As Object &apos; Current selection in destination
+Dim vImportOptions As Variant &apos; Array of PropertyValues
+
+Const cstThisSub = &quot;SFDocuments.Calc.ImportFromDatabase&quot;
+Const cstSubArgs = &quot;[FileName=&quot;&quot;&quot;&quot;], [RegistrationName=&quot;&quot;&quot;&quot;], DestinationCell, SQLCommand, [DirectSQL=False]&quot;
+
+&apos; If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+
+ If IsMissing(FileName) Or IsEmpty(FileName) Then FileName = &quot;&quot;
+ 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 _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
+ If Not ScriptForge.SF_Utils._Validate(DirectSQL, &quot;DirectSQL&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+ &apos; Check the existence of FileName
+ If Len(FileName) = 0 Then &apos; FileName has precedence over RegistrationName
+ If Len(RegistrationName) = 0 Then GoTo CatchError
+ Set oDBContext = ScriptForge.SF_Utils._GetUNOService(&quot;DatabaseContext&quot;)
+ If Not oDBContext.hasRegisteredDatabase(RegistrationName) Then GoTo CatchError
+ FileName = ScriptForge.SF_FileSystem._ConvertFromUrl(oDBContext.getDatabaseLocation(RegistrationName))
+ End If
+ If Not ScriptForge.SF_FileSystem.FileExists(FileName) Then GoTo CatchError
+
+Try:
+ &apos; Check command type
+ Set oDatabase = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Database&quot;, FileName, , True) &apos; Read-only
+ If IsNull(oDatabase) Then GoTo CatchError
+ With oDatabase
+ If ScriptForge.SF_Array.Contains(.Tables, SQLCommand) Then
+ bDirect = True
+ lCommandType = com.sun.star.sheet.DataImportMode.TABLE
+ ElseIf ScriptForge.SF_Array.Contains(.Queries, SQLCommand) Then
+ Set oQuery = .XConnection.Queries.getByName(SQLCommand)
+ bDirect = Not oQuery.EscapeProcessing
+ lCommandType = com.sun.star.sheet.DataImportMode.QUERY
+ Else
+ bDirect = DirectSQL
+ lCommandType = com.sun.star.sheet.DataImportMode.SQL
+ SQLCommand = ._ReplaceSquareBrackets(SQLCommand)
+ End If
+ .CloseDatabase()
+ Set oDatabase = oDatabase.Dispose()
+ End With
+
+ &apos; Determine the destination cell as the top-left coordinates of the given range
+ Set oDestRange = _ParseAddress(DestinationCell)
+ Set oDestAddress = oDestRange.XCellRange.RangeAddress
+ Set oDestCell = oDestRange.XSpreadsheet.getCellByPosition(oDestAddress.StartColumn, oDestAddress.StartRow)
+
+ &apos; Remember current selection
+ Set oSelect = _Component.CurrentController.getSelection()
+ &apos; Import arguments
+ vImportOptions = Array(_
+ ScriptForge.SF_Utils._MakePropertyValue(&quot;DatabaseName&quot;, ScriptForge.SF_FileSystem._ConvertToUrl(FileName)) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;SourceObject&quot;, SQLCommand) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;SourceType&quot;, lCommandType) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;IsNative&quot;, bDirect) _
+ )
+ oDestCell.doImport(vImportOptions)
+ &apos; Restore selection after import_
+ _RestoreSelections(_Component, oSelect)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+CatchError:
+ SF_Exception.RaiseFatal(BASEDOCUMENTOPENERROR, &quot;FileName&quot;, FileName, &quot;RegistrationName&quot;, RegistrationName)
+ GoTo Finally
+End Sub &apos; SFDocuments.SF_Calc.ImportFromDatabase
+
+REM -----------------------------------------------------------------------------
+Public Function InsertSheet(Optional ByVal SheetName As Variant _
+ , Optional ByVal BeforeSheet As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Insert a new empty sheet before an existing sheet or at the end of the list of sheets
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SheetName: The name of the new sheet
+&apos;&apos;&apos; BeforeSheet: The name (string) or index (numeric, starting from 1) of the sheet before which to insert
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the sheet could be inserted successfully
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.InsertSheet(&quot;SheetX&quot;, &quot;SheetY&quot;)
+
+Dim bInsert As Boolean &apos; Return value
+Dim vSheets As Variant &apos; List of existing sheets
+Dim lSheetIndex As Long &apos; Index of a sheet
+Const cstThisSub = &quot;SFDocuments.Calc.InsertSheet&quot;
+Const cstSubArgs = &quot;SheetName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bInsert = False
+
+Check:
+ If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+ vSheets = _Component.getSheets.getElementNames()
+
+Try:
+ If VarType(BeforeSheet) = V_STRING Then
+ lSheetIndex = ScriptForge.SF_Array.IndexOf(vSheets, BeforeSheet)
+ Else
+ lSheetIndex = BeforeSheet - 1
+ If lSheetIndex &lt; 0 Then lSheetIndex = 0
+ If lSheetIndex &gt; UBound(vSheets) Then lSheetIndex = UBound(vSheets) + 1
+ End If
+ _Component.getSheets.insertNewByName(SheetName, lSheetIndex)
+ bInsert = True
+
+Finally:
+ InsertSheet = binsert
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.InsertSheet
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Calc service as an array
+
+ Methods = Array( _
+ &quot;A1Style&quot; _
+ , &quot;Charts&quot; _
+ , &quot;ClearAll&quot; _
+ , &quot;ClearFormats&quot; _
+ , &quot;ClearValues&quot; _
+ , &quot;CopySheet&quot; _
+ , &quot;CopySheetFromFile&quot; _
+ , &quot;CopyToCell&quot; _
+ , &quot;CopyToRange&quot; _
+ , &quot;CreateChart&quot; _
+ , &quot;DAvg&quot; _
+ , &quot;DCount&quot; _
+ , &quot;DMax&quot; _
+ , &quot;DMin&quot; _
+ , &quot;DSum&quot; _
+ , &quot;ExportRangeToFile&quot; _
+ , &quot;GetColumnName&quot; _
+ , &quot;GetFormula&quot; _
+ , &quot;GetValue&quot; _
+ , &quot;ImportFromCSVFile&quot; _
+ , &quot;ImportFromDatabase&quot; _
+ , &quot;InsertSheet&quot; _
+ , &quot;MoveRange&quot; _
+ , &quot;MoveSheet&quot; _
+ , &quot;Offset&quot; _
+ , &quot;OpenRangeSelector&quot; _
+ , &quot;Printf&quot; _
+ , &quot;PrintOut&quot; _
+ , &quot;RemoveSheet&quot; _
+ , &quot;RenameSheet&quot; _
+ , &quot;SetArray&quot; _
+ , &quot;SetCellStyle&quot; _
+ , &quot;SetFormula&quot; _
+ , &quot;SetValue&quot; _
+ , &quot;ShiftDown&quot; _
+ , &quot;ShiftLeft&quot; _
+ , &quot;ShiftRight&quot; _
+ , &quot;ShiftUp&quot; _
+ , &quot;SortRange&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Calc.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function MoveRange(Optional ByVal Source As Variant _
+ , Optional ByVal Destination As Variant _
+ ) As String
+&apos;&apos;&apos; Move a specified source range to a destination range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Source: the source range of cells as a string
+&apos;&apos;&apos; Destination: the destination of the moved range of cells, as a string
+&apos;&apos;&apos; If given as a range of cells, the destination will be reduced to its top-left cell
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the modified range of cells
+&apos;&apos;&apos; The modified area depends only on the size of the source area
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.MoveRange(&quot;SheetX.A1:F10&quot;, &quot;SheetY.C5&quot;)
+
+Dim sMove As String &apos; Return value
+Dim oSource As Object &apos; Alias of Source to avoid &quot;Object variable not set&quot; run-time error
+Dim oSourceAddress As Object &apos; com.sun.star.table.CellRangeAddress
+Dim oDestRange As Object &apos; Destination as a range
+Dim oDestAddress As Object &apos; com.sun.star.table.CellRangeAddress
+Dim oDestCell As Object &apos; com.sun.star.table.CellAddress
+Dim oSelect As Object &apos; Current selection in source
+Dim oClipboard As Object &apos; com.sun.star.datatransfer.XTransferable
+Dim oCellRanges As Object &apos; com.sun.star.sheet.SheetCellRanges
+Dim vRangeAddresses As Variant &apos; Array of com.sun.star.table.CellRangeAddress
+Dim i As Long
+
+Const cstThisSub = &quot;SFDocuments.Calc.MoveRange&quot;
+Const cstSubArgs = &quot;Source, Destination&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sMove = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+
+Try:
+ Set oSourceAddress = _ParseAddress(Source).XCellRange.RangeAddress
+ Set oDestRange = _ParseAddress(Destination)
+ Set oDestAddress = oDestRange.XCellRange.RangeAddress
+ Set oDestCell = New com.sun.star.table.CellAddress
+ With oDestAddress
+ oDestCell.Sheet = .Sheet
+ oDestCell.Column = .StartColumn
+ oDestCell.Row = .StartRow
+ End With
+ oDestRange.XSpreadsheet.moveRange(oDestCell, oSourceAddress)
+
+ With oSourceAddress
+ sMove = _Offset(Destination, 0, 0, .EndRow - .StartRow + 1, .EndColumn - .StartColumn + 1).RangeName
+ End With
+
+Finally:
+ MoveRange = sMove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.MoveRange
+
+REM -----------------------------------------------------------------------------
+Public Function MoveSheet(Optional ByVal SheetName As Variant _
+ , Optional ByVal BeforeSheet As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Move a sheet before an existing sheet or at the end of the list of sheets
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SheetName: The name of the sheet to move
+&apos;&apos;&apos; BeforeSheet: The name (string) or index (numeric, starting from 1) of the sheet before which to move the sheet
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the sheet could be moved successfully
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.MoveSheet(&quot;SheetX&quot;, &quot;SheetY&quot;)
+
+Dim bMove As Boolean &apos; Return value
+Dim vSheets As Variant &apos; List of existing sheets
+Dim lSheetIndex As Long &apos; Index of a sheet
+Const cstThisSub = &quot;SFDocuments.Calc.MoveSheet&quot;
+Const cstSubArgs = &quot;SheetName, [BeforeSheet=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bMove = False
+
+Check:
+ If IsMissing(BeforeSheet) Or IsEmpty(BeforeSheet) Then BeforeSheet = 32768
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+ vSheets = _Component.getSheets.getElementNames()
+
+Try:
+ If VarType(BeforeSheet) = V_STRING Then
+ lSheetIndex = ScriptForge.SF_Array.IndexOf(vSheets, BeforeSheet)
+ Else
+ lSheetIndex = BeforeSheet - 1
+ If lSheetIndex &lt; 0 Then lSheetIndex = 0
+ If lSheetIndex &gt; UBound(vSheets) Then lSheetIndex = UBound(vSheets) + 1
+ End If
+ _Component.getSheets.MoveByName(SheetName, lSheetIndex)
+ bMove = True
+
+Finally:
+ MoveSheet = bMove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.MoveSheet
+
+REM -----------------------------------------------------------------------------
+Public Function Offset(Optional ByRef Range As Variant _
+ , Optional ByVal Rows As Variant _
+ , Optional ByVal Columns As Variant _
+ , Optional ByVal Height As Variant _
+ , Optional ByVal Width As Variant _
+ ) As String
+&apos;&apos;&apos; Returns a new range offset by a certain number of rows and columns from a given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range, as a string, from which the function searches for the new range
+&apos;&apos;&apos; Rows : the number of rows by which the reference was corrected up (negative value) or down.
+&apos;&apos;&apos; Use 0 (default) to stay in the same row.
+&apos;&apos;&apos; Columns : the number of columns by which the reference was corrected to the left (negative value) or to the right.
+&apos;&apos;&apos; Use 0 (default) to stay in the same column
+&apos;&apos;&apos; Height : the vertical height for an area that starts at the new reference position.
+&apos;&apos;&apos; Default = no vertical resizing
+&apos;&apos;&apos; Width : the horizontal width for an area that starts at the new reference position.
+&apos;&apos;&apos; Default - no horizontal resizing
+&apos;&apos;&apos; Arguments Rows and Columns must not lead to zero or negative start row or column.
+&apos;&apos;&apos; Arguments Height and Width must not lead to zero or negative count of rows or columns.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A new range as a string
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; OFFSETADDRESSERROR The computed range of cells falls beyond the sheet boundaries
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.Offset(&quot;A1&quot;, 2, 2) &apos; &quot;&apos;SheetX&apos;.$C$3&quot; (A1 moved by two rows and two columns down)
+&apos;&apos;&apos; oDoc.Offset(&quot;A1&quot;, 2, 2, 5, 6) &apos; &quot;&apos;SheetX&apos;.$C$3:$H$7&quot;
+
+Dim sOffset As String &apos; Return value
+Dim oAddress As Object &apos; Alias of Range
+Const cstThisSub = &quot;SFDocuments.Calc.Offset&quot;
+Const cstSubArgs = &quot;Range, [Rows=0], [Columns=0], [Height], [Width]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sOffset = &quot;&quot;
+
+Check:
+ If IsMissing(Rows) Or IsEmpty(Rows) Then Rows = 0
+ If IsMissing(Columns) Or IsEmpty(Columns) Then Columns = 0
+ 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 _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
+ If Not ScriptForge.SF_Utils._Validate(Height, &quot;Height&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Width, &quot;Width&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Define the new range string
+ Set oAddress = _Offset(Range, Rows, Columns, Height, Width)
+ sOffset = oAddress.RangeName
+
+Finally:
+ Offset = sOffset
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.Offset
+
+REM -----------------------------------------------------------------------------
+Public Function OpenRangeSelector(Optional ByVal Title As Variant _
+ , Optional ByVal Selection As Variant _
+ , Optional ByVal SingleCell As Variant _
+ , Optional ByVal CloseAfterSelect As Variant _
+ ) As String
+&apos;&apos;&apos; Activates the Calc document, opens a non-modal dialog with a text box,
+&apos;&apos;&apos; let the user make a selection in the current or another sheet and
+&apos;&apos;&apos; returns the selected area as a string.
+&apos;&apos;&apos; This method does not change the current selection.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Title: the title to display on the top of the dialog
+&apos;&apos;&apos; Selection: a default preselection as a String. When absent, the first element of the
+&apos;&apos;&apos; current selection is preselected.
+&apos;&apos;&apos; SingleCell: When True, only a single cell may be selected. Default = False
+&apos;&apos;&apos; CloseAfterSelect: When True (default-, the dialog is closed immediately after
+&apos;&apos;&apos; the selection. When False, the user may change his/her mind and must close
+&apos;&apos;&apos; the dialog manually.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The selected range as a string, or the empty string when the user cancelled the request (close window button)
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim sSelect As String, vValues As Variant
+&apos;&apos;&apos; sSelect = oDoc.OpenRangeSelector(&quot;Select a range ...&quot;)
+&apos;&apos;&apos; If sSelect = &quot;&quot; Then Exit Function
+&apos;&apos;&apos; vValues = oDoc.GetValue(sSelect)
+
+Dim sSelector As String &apos; Return value
+Dim vPropertyValues As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim oSelection As Object &apos; The current selection before opening the selector
+Dim oAddress As Object &apos; Preselected address as _Address
+
+Const cstThisSub = &quot;SFDocuments.Calc.OpenRangeSelector&quot;
+Const cstSubArgs = &quot;[Title=&quot;&quot;&quot;&quot;], [Selection=&quot;&quot;~&quot;&quot;], [SingleCell=False], [CloseAfterSelect=True]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sSelector = &quot;&quot;
+
+Check:
+ If IsMissing(Title) Or IsEmpty(Title) Then Title = &quot;&quot;
+ If IsMissing(Selection) Or IsEmpty(Selection) Then Selection = &quot;~&quot;
+ If IsMissing(SingleCell) Or IsEmpty(SingleCell) Then SingleCell = False
+ If IsMissing(CloseAfterSelect) Or IsEmpty(CloseAfterSelect) Then CloseAfterSelect = True
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Title, &quot;Title&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Selection, &quot;Selection&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(SingleCell, &quot;SingleCell&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(CloseAfterSelect, &quot;CloseAfterSelect&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Save the current selections
+ Set oSelection = _Component.CurrentController.getSelection()
+
+ &apos; Process preselection and select its containing sheet
+ Set oAddress = _ParseAddress(Selection)
+ Activate(oAddress.SheetName)
+
+ &apos; Build arguments array and execute the dialog box
+ With ScriptForge.SF_Utils
+ vPropertyValues = Array( _
+ ._MakePropertyValue(&quot;Title&quot;, Title) _
+ , ._MakePropertyValue(&quot;CloseOnMouseRelease&quot;, CloseAfterSelect) _
+ , ._MakePropertyValue(&quot;InitialValue&quot;, oAddress.XCellRange.AbsoluteName) _
+ , ._MakePropertyValue(&quot;SingleCellMode&quot;, SingleCell) _
+ )
+ End With
+ sSelector = SF_DocumentListener.RunRangeSelector(_Component, vPropertyValues)
+
+ &apos; Restore the saved selections
+ _RestoreSelections(_Component, oSelection)
+
+Finally:
+ OpenRangeSelector = sSelector
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.OpenRangeSelector
+
+REM -----------------------------------------------------------------------------
+Public Function Printf(Optional ByVal InputStr As Variant _
+ , Optional ByVal Range As Variant _
+ , Optional ByVal TokenCharacter As Variant _
+ ) As String
+&apos;&apos;&apos; Returns the input string after substitution of its tokens by
+&apos;&apos;&apos; their values in the given range
+&apos;&apos;&apos; This method is usually used in combination with SetFormula()
+&apos;&apos;&apos; The accepted tokens are:
+&apos;&apos;&apos; - %S The sheet name containing the range, including single quotes when necessary
+&apos;&apos;&apos; - %R1 The row number of the topleft part of the range
+&apos;&apos;&apos; - %C1 The column letter of the topleft part of the range
+&apos;&apos;&apos; - %R2 The row number of the bottomright part of the range
+&apos;&apos;&apos; - %C2 The column letter of the bottomright part of the range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; InputStr: usually a Calc formula or a part of a formula, but may be any string
+&apos;&apos;&apos; Range: the range, as a string from which the values of the tokens are derived
+&apos;&apos;&apos; TokenCharacter: the character identifying tokens. Default = &quot;%&quot;.
+&apos;&apos;&apos; Double the TokenCharacter to not consider it as a token.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The input string after substitution of the contained tokens
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Assume we have in A1:E10 a matrix of numbers. To obtain the sum by row in F1:F10 ...
+&apos;&apos;&apos; Dim range As String, formula As String
+&apos;&apos;&apos; range = &quot;$A$1:$E$10&quot;)
+&apos;&apos;&apos; formula = &quot;=SUM($%C1%R1:$%C2%R1)&quot; &apos; &quot;=SUM($A1:$E1)&quot;, note the relative references
+&apos;&apos;&apos; oDoc.SetFormula(&quot;$F$1:$F$10&quot;, formula)
+&apos;&apos;&apos; &apos;F1 will contain =Sum($A1:$E1)
+&apos;&apos;&apos; &apos;F2 =Sum($A2:$E2)
+&apos;&apos;&apos; &apos; ...
+
+Dim sPrintf As String &apos; Return value
+Dim vSubstitute As Variants &apos; Array of strings representing the token values
+Dim oAddress As Object &apos; A range as an _Address object
+Dim sSheetName As String &apos; The %S token value
+Dim sC1 As String &apos; The %C1 token value
+Dim sR1 As String &apos; The %R1 token value
+Dim sC2 As String &apos; The %C2 token value
+Dim sR2 As String &apos; The %R2 token value
+Dim i As Long
+Const cstPseudoToken = &quot;@#@&quot;
+
+Const cstThisSub = &quot;SFDocuments.Calc.Printf&quot;
+Const cstSubArgs = &quot;InputStr, Range, TokenCharacter=&quot;&quot;%&quot;&quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sPrintf = &quot;&quot;
+
+Check:
+ If IsMissing(TokenCharacter) Or IsEmpty(TokenCharacter) Then TokenCharacter = &quot;%&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(InputStr, &quot;InputStr&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(TokenCharacter, &quot;TokenCharacter&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Define the token values
+ Set oAddress = _ParseAddress(Range)
+ With oAddress.XCellRange
+ sC1 = _GetColumnName(.RangeAddress.StartColumn + 1)
+ sR1 = CStr(.RangeAddress.StartRow + 1)
+ sC2 = _GetColumnName(.RangeAddress.EndColumn + 1)
+ sR2 = CStr(.RangeAddress.EndRow + 1)
+ sSheetName = _QuoteSheetName(oAddress.XSpreadsheet.Name)
+ End With
+
+ &apos; Substitute tokens by their values
+ sPrintf = ScriptForge.SF_String.ReplaceStr(InputStr _
+ , Array(TokenCharacter &amp; TokenCharacter _
+ , TokenCharacter &amp; &quot;R1&quot; _
+ , TokenCharacter &amp; &quot;C1&quot; _
+ , TokenCharacter &amp; &quot;R2&quot; _
+ , TokenCharacter &amp; &quot;C2&quot; _
+ , TokenCharacter &amp; &quot;S&quot; _
+ , cstPseudoToken _
+ ) _
+ , Array(cstPseudoToken _
+ , sR1 _
+ , sC1 _
+ , sR2 _
+ , sC2 _
+ , sSheetName _
+ , TokenCharacter _
+ ) _
+ )
+
+Finally:
+ Printf = sPrintf
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.Printf
+
+REM -----------------------------------------------------------------------------
+Public Function PrintOut(Optional ByVal SheetName As Variant _
+ , Optional ByVal Pages As Variant _
+ , Optional ByVal Copies As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Send the content of the given sheet 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; SheetName: the sheet to print. Default = the active sheet
+&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; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.PrintOut(&quot;SheetX&quot;, &quot;1-4;10;15-18&quot;, Copies := 2)
+
+Dim bPrint As Boolean &apos; Return value
+Dim oSheet As Object &apos; SheetName as a reference
+
+Const cstThisSub = &quot;SFDocuments.Calc.PrintOut&quot;
+Const cstSubArgs = &quot;[SheetName=&quot;&quot;~&quot;&quot;], [Pages=&quot;&quot;&quot;&quot;], [Copies=1]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bPrint = False
+
+Check:
+ If IsMissing(SheetName) Or IsEmpty(SheetName) Then SheetName = &quot;&quot;
+ If IsMissing(Pages) Or IsEmpty(Pages) Then Pages = &quot;&quot;
+ If IsMissing(Copies) Or IsEmpty(Copies) Then Copies = 1
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True, True) 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
+ End If
+
+Try:
+ If SheetName = &quot;~&quot; Then SheetName = &quot;&quot;
+ &apos; Make given sheet active
+ If Len(SheetName) &gt; 0 Then
+ With _Component
+ Set oSheet = .getSheets.getByName(SheetName)
+ Set .CurrentController.ActiveSheet = oSheet
+ End With
+ End If
+
+ bPrint = [_Super].PrintOut(Pages, Copies, _Component)
+
+Finally:
+ PrintOut = bPrint
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.PrintOut
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Calc class as an array
+
+ Properties = Array( _
+ &quot;CurrentSelection&quot; _
+ , &quot;CustomProperties&quot; _
+ , &quot;Description&quot; _
+ , &quot;DocumentProperties&quot; _
+ , &quot;DocumentType&quot; _
+ , &quot;ExportFilters&quot; _
+ , &quot;FirstCell&quot; _
+ , &quot;FirstColumn&quot; _
+ , &quot;FirstRow&quot; _
+ , &quot;Height&quot; _
+ , &quot;ImportFilters&quot; _
+ , &quot;IsBase&quot; _
+ , &quot;IsCalc&quot; _
+ , &quot;IsDraw&quot; _
+ , &quot;IsImpress&quot; _
+ , &quot;IsMath&quot; _
+ , &quot;IsWriter&quot; _
+ , &quot;Keywords&quot; _
+ , &quot;LastCell&quot; _
+ , &quot;LastColumn&quot; _
+ , &quot;LastRow&quot; _
+ , &quot;Range&quot; _
+ , &quot;Readonly&quot; _
+ , &quot;Region&quot; _
+ , &quot;Sheet&quot; _
+ , &quot;SheetName&quot; _
+ , &quot;Sheets&quot; _
+ , &quot;Subject&quot; _
+ , &quot;Title&quot; _
+ , &quot;Width&quot; _
+ , &quot;XCellRange&quot; _
+ , &quot;XComponent&quot; _
+ , &quot;XSheetCellCursor&quot; _
+ , &quot;XSpreadsheet&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Calc.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function RemoveSheet(Optional ByVal SheetName As Variant) As Boolean
+&apos;&apos;&apos; Remove an existing sheet from the document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SheetName: The name of the sheet to remove
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the sheet could be removed successfully
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.RemoveSheet(&quot;SheetX&quot;)
+
+Dim bRemove As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDocuments.Calc.RemoveSheet&quot;
+Const cstSubArgs = &quot;SheetName&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bRemove = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not _ValidateSheet(SheetName, &quot;SheetName&quot;, , True) Then GoTo Finally
+ End If
+
+Try:
+ _Component.getSheets.RemoveByName(SheetName)
+ bRemove = True
+
+Finally:
+ RemoveSheet = bRemove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.RemoveSheet
+
+REM -----------------------------------------------------------------------------
+Public Function RenameSheet(Optional ByVal SheetName As Variant _
+ , Optional ByVal NewName As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Rename a specified sheet
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SheetName: The name of the sheet to rename
+&apos;&apos;&apos; NewName: Must not exist
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the sheet could be renamed successfully
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DUPLICATESHEETERROR A sheet with the given name exists already
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.RenameSheet(&quot;SheetX&quot;, &quot;SheetY&quot;)
+
+Dim bRename As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDocuments.Calc.RenameSheet&quot;
+Const cstSubArgs = &quot;SheetName, NewName&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bRename = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+
+Try:
+ _Component.getSheets.getByName(SheetName).setName(NewName)
+ bRename = True
+
+Finally:
+ RenameSheet = bRename
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.RenameSheet
+
+REM -----------------------------------------------------------------------------
+Public Function SetArray(Optional ByVal TargetCell As Variant _
+ , Optional ByRef Value As Variant _
+ ) As String
+&apos;&apos;&apos; Set the given (array of) values starting from the target cell
+&apos;&apos;&apos; The updated area expands itself from the target cell or from the top-left corner of the given range
+&apos;&apos;&apos; as far as determined by the size of the input Value.
+&apos;&apos;&apos; Vectors are always expanded vertically
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TargetCell : the cell or the range as a string that should receive a new value
+&apos;&apos;&apos; Value: a scalar, a vector or an array with the new values
+&apos;&apos;&apos; The new values should be strings, numeric values or dates. Other types empty the corresponding cell
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the updated range
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; OFFSETADDRESSERROR The computed range of cells falls beyond the sheet boundaries
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.SetArray(&quot;SheetX.A1&quot;, SF_Array.RangeInit(1, 1000))
+
+Dim sSet As String &apos; Return value
+Dim oSet As Object &apos; _Address alias of sSet
+Dim vDataArray As Variant &apos; DataArray compatible with .DataArray UNO property
+Const cstThisSub = &quot;SFDocuments.Calc.SetArray&quot;
+Const cstSubArgs = &quot;TargetCell, Value&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sSet = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Value, &quot;Value&quot;) Then GoTo Finally
+ End If
+ End If
+
+Try:
+ &apos; Convert argument to data array and derive new range from its size
+ vDataArray = _ConvertToDataArray(Value)
+ If UBound(vDataArray) &lt; LBound(vDataArray) Then GoTo Finally
+ Set oSet = _Offset(TargetCell, 0, 0, plHeight := UBound(vDataArray) + 1, plWidth := UBound(vDataArray(0)) + 1) &apos; +1 : vDataArray is zero-based
+ With oSet
+ .XCellRange.setDataArray(vDataArray)
+ sSet = .RangeName
+ End With
+
+Finally:
+ SetArray = sSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.SetArray
+
+REM -----------------------------------------------------------------------------
+Public Function SetCellStyle(Optional ByVal TargetRange As Variant _
+ , Optional ByVal Style As Variant _
+ ) As String
+&apos;&apos;&apos; Apply the given cell style in the given range
+&apos;&apos;&apos; The full range is updated and the remainder of the sheet is left untouched
+&apos;&apos;&apos; If the cell style does not exist, an error is raised
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TargetRange : the range as a string that should receive a new cell style
+&apos;&apos;&apos; Style: the style name as a string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the updated range
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.SetCellStyle(&quot;A1:F1&quot;, &quot;Heading 2&quot;)
+
+Dim sSet As String &apos; Return value
+Dim oAddress As _Address &apos; Alias of TargetRange
+Dim oStyleFamilies As Object &apos; com.sun.star.container.XNameAccess
+Dim vStyles As Variant &apos; Array of existing cell styles
+Const cstStyle = &quot;CellStyles&quot;
+Const cstThisSub = &quot;SFDocuments.Calc.SetCellStyle&quot;
+Const cstSubArgs = &quot;TargetRange, Style&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sSet = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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()
+ If Not ScriptForge.SF_Utils._Validate(Style, &quot;Style&quot;, V_STRING, vStyles) Then GoTo Finally
+ End If
+
+Try:
+ Set oAddress = _ParseAddress(TargetRange)
+ With oAddress
+ .XCellRange.CellStyle = Style
+ sSet = .RangeName
+ End With
+
+Finally:
+ SetCellStyle = sSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.SetCellStyle
+
+REM -----------------------------------------------------------------------------
+Public Function SetFormula(Optional ByVal TargetRange As Variant _
+ , Optional ByRef Formula As Variant _
+ ) As String
+&apos;&apos;&apos; Set the given (array of) formulae in the given range
+&apos;&apos;&apos; The full range is updated and the remainder of the sheet is left untouched
+&apos;&apos;&apos; If the given formula is a string:
+&apos;&apos;&apos; the unique formula is pasted across the whole range with adjustment of the relative references
+&apos;&apos;&apos; Otherwise
+&apos;&apos;&apos; If the size of Formula &lt; the size of Range, then the other cells are emptied
+&apos;&apos;&apos; If the size of Formula &gt; the size of Range, then Formula is only partially copied
+&apos;&apos;&apos; Vectors are always expanded vertically, except if the range has a height of exactly 1 row
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TargetRange : the range as a string that should receive a new Formula
+&apos;&apos;&apos; Formula: a scalar, a vector or an array with the new formula(e) as strings for each cell of the range.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the updated range
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.SetFormula(&quot;A1&quot;, &quot;=A2&quot;)
+&apos;&apos;&apos; oDoc.SetFormula(&quot;A1:F1&quot;, Array(&quot;=A2&quot;, &quot;=B2&quot;, &quot;=C2+10&quot;)) &apos; Horizontal vector, partially empty
+&apos;&apos;&apos; oDoc.SetFormula(&quot;A1:D2&quot;, &quot;=E1&quot;) &apos; D2 contains the formula &quot;=H2&quot;
+
+Dim sSet As String &apos; Return value.XSpreadsheet.Name)
+Dim oAddress As Object &apos; Alias of TargetRange
+Dim vDataArray As Variant &apos; DataArray compatible with .DataArray UNO property
+Const cstThisSub = &quot;SFDocuments.Calc.SetFormula&quot;
+Const cstSubArgs = &quot;TargetRange, Formula&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sSet = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Formula, &quot;Formula&quot;, V_STRING) Then GoTo Finally
+ End If
+ End If
+
+Try:
+ Set oAddress = _ParseAddress(TargetRange)
+ With oAddress
+ If IsArray(Formula) Then
+ &apos; Convert to data array and limit its size to the size of the initial range
+ vDataArray = _ConvertToDataArray(Formula, .Height - 1, .Width - 1)
+ If UBound(vDataArray) &lt; LBound(vDataArray) Then GoTo Finally
+ .XCellRange.setFormulaArray(vDataArray)
+ Else
+ With .XCellRange
+ &apos; Store formula in top-left cell and paste it along the whole range
+ .getCellByPosition(0, 0).setFormula(Formula)
+ .fillSeries(com.sun.star.sheet.FillDirection.TO_BOTTOM, com.sun.star.sheet.FillMode.SIMPLE, 0, 0, 0)
+ .fillSeries(com.sun.star.sheet.FillDirection.TO_RIGHT, com.sun.star.sheet.FillMode.SIMPLE, 0, 0, 0)
+ End With
+ End If
+ sSet = .RangeName
+ End With
+
+Finally:
+ SetFormula = sSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.SetFormula
+
+REM -----------------------------------------------------------------------------
+Private Function SetProperty(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.Calc.set&quot; &amp; psProperty
+ If IsMissing(pvValue) Then pvValue = Empty
+ &apos;ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Validation done in Property Lets
+
+ If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
+ bSet = True
+ Select Case UCase(psProperty)
+ Case UCase(&quot;CurrentSelection&quot;)
+ CurrentSelection = pvValue
+ Case UCase(&quot;CustomProperties&quot;)
+ CustomProperties = pvValue
+ Case UCase(&quot;Description&quot;)
+ Description = pvValue
+ Case UCase(&quot;Keywords&quot;)
+ Keywords = pvValue
+ Case UCase(&quot;Subject&quot;)
+ Subject = pvValue
+ Case UCase(&quot;Title&quot;)
+ Title = pvValue
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ SetProperty = bSet
+ &apos;ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.SetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function SetValue(Optional ByVal TargetRange As Variant _
+ , Optional ByRef Value As Variant _
+ ) As String
+&apos;&apos;&apos; Set the given value in the given range
+&apos;&apos;&apos; The full range is updated and the remainder of the sheet is left untouched
+&apos;&apos;&apos; If the size of Value &lt; the size of Range, then the other cells are emptied
+&apos;&apos;&apos; If the size of Value &gt; the size of Range, then Value is only partially copied
+&apos;&apos;&apos; Vectors are always expanded vertically, except if the range has a height of exactly 1 row
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TargetRange : the range as a string that should receive a new value
+&apos;&apos;&apos; Value: a scalar, a vector or an array with the new values for each cell o.XSpreadsheet.Name)f the range.
+&apos;&apos;&apos; The new values should be strings, numeric values or dates. Other types empty the corresponding cell
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the updated range
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.SetValue(&quot;A1&quot;, 2)
+&apos;&apos;&apos; oDoc.SetValue(&quot;A1:F1&quot;, Array(1, 2, 3)) &apos; Horizontal vector, partially empty
+&apos;&apos;&apos; oDoc.SetValue(&quot;A1:D2&quot;, SF_Array.AppendRow(Array(1, 2, 3, 4), Array(5, 6, 7, 8)))
+
+Dim sSet As String &apos; Return value
+Dim oAddress As Object &apos; Alias of TargetRange
+Dim vDataArray As Variant &apos; DataArray compatible with .DataArray UNO property
+Const cstThisSub = &quot;SFDocuments.Calc.SetValue&quot;
+Const cstSubArgs = &quot;TargetRange, Value&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sSet = &quot;&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ 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
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Value, &quot;Value&quot;) Then GoTo Finally
+ End If
+ End If
+
+Try:
+ Set oAddress = _ParseAddress(TargetRange)
+ With oAddress
+ &apos; Convert to data array and limit its size to the size of the initial range
+ vDataArray = _ConvertToDataArray(Value, .Height - 1, .Width - 1)
+ If UBound(vDataArray) &lt; LBound(vDataArray) Then GoTo Finally
+ .XCellRange.setDataArray(vDataArray)
+ sSet = .RangeName
+ End With
+
+Finally:
+ SetValue = sSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.SetValue
+
+REM -----------------------------------------------------------------------------
+Public Function ShiftDown(Optional ByVal Range As Variant _
+ , Optional ByVal WholeRow As Variant _
+ , Optional ByVal Rows As Variant _
+ ) As String
+&apos;&apos;&apos; Move a specified range and all cells below in the same columns downwards by inserting empty cells
+&apos;&apos;&apos; The inserted cells can span whole rows or be limited to the width of the range
+&apos;&apos;&apos; The height of the inserted area is provided by the Rows argument
+&apos;&apos;&apos; Nothing happens if the range shift crosses one of the edges of the worksheet
+&apos;&apos;&apos; The execution of the method has no effect on the current selection
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range: the range above which cells have to be inserted, as a string
+&apos;&apos;&apos; WholeRow: when True (default = False), insert whole rows
+&apos;&apos;&apos; Rows: the height of the area to insert. Default = the height of the Range argument
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the new location of the initial range
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; newrange = oDoc.ShiftDown(&quot;SheetX.A1:F10&quot;) &apos; &quot;$SheetX.$A$11:$F$20&quot;
+&apos;&apos;&apos; newrange = oDoc.ShiftDown(&quot;SheetX.A1:F10&quot;, Rows := 3) &apos; &quot;$SheetX.$A$4:$F$13&quot;
+
+Dim sShift As String &apos; Return value
+Dim oSourceAddress As Object &apos; Alias of Range as _Address
+Dim lHeight As Long &apos; Range height
+Dim oShiftAddress As Object &apos; com.sun.star.table.CellRangeAddress - Range adjusted to the right width
+Dim lShiftMode As Long &apos; One of the com.sun.star.sheet.CellInsertMode enum values
+
+Const cstThisSub = &quot;SFDocuments.Calc.ShiftDown&quot;
+Const cstSubArgs = &quot;Range, [WholeRow=False], [Rows]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sShift = &quot;&quot;
+
+Check:
+ If IsMissing(WholeRow) Or IsEmpty(WholeRow) Then WholeRow = False
+ If IsMissing(Rows) Or IsEmpty(Rows) Then Rows = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(WholeRow, &quot;WholeRow&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Rows, &quot;Rows&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ Set oSourceAddress = _ParseAddress(Range)
+
+ With oSourceAddress
+
+ &apos; Manage the height of the area to shift
+ &apos; The insertCells() method inserts a number of rows equal to the height of the cell range to shift
+ lHeight = .Height
+ If Rows &lt;= 0 Then Rows = lHeight
+ If _LastCell(.XSpreadsheet)(1) + Rows &gt; MAXROWS Then GoTo Catch
+ If Rows &lt;&gt; lHeight Then
+ Set oShiftAddress = _Offset(oSourceAddress, 0, 0, Rows, 0).XCellRange.RangeAddress
+ Else
+ Set oShiftAddress = .XCellRange.RangeAddress
+ End If
+
+ &apos; Determine the shift mode
+ With com.sun.star.sheet.CellInsertMode
+ If WholeRow Then lShiftMode = .ROWS Else lShiftMode = .DOWN
+ End With
+
+ &apos; Move the cells as requested. This modifies .XCellRange
+ .XSpreadsheet.insertCells(oShiftAddress, lShiftMode)
+
+ &apos; Determine the receiving area
+ sShift = .XCellRange.AbsoluteName
+
+ End With
+
+Finally:
+ ShiftDown = sShift
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ &apos; When error, return the original range
+ If Not IsNull(oSourceAddress) Then sShift = oSourceAddress.RangeName
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.ShiftDown
+
+REM -----------------------------------------------------------------------------
+Public Function ShiftLeft(Optional ByVal Range As Variant _
+ , Optional ByVal WholeColumn As Variant _
+ , Optional ByVal Columns As Variant _
+ ) As String
+&apos;&apos;&apos; Delete the leftmost columns of a specified range and move all cells at their right leftwards
+&apos;&apos;&apos; The deleted cells can span whole columns or be limited to the height of the range
+&apos;&apos;&apos; The width of the deleted area is provided by the Columns argument
+&apos;&apos;&apos; The execution of the method has no effect on the current selection
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range: the range in which cells have to be erased, as a string
+&apos;&apos;&apos; WholeColumn: when True (default = False), erase whole columns
+&apos;&apos;&apos; Columns: the width of the area to delete.
+&apos;&apos;&apos; Default = the width of the Range argument, it is also its maximum value
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the location of the remaining part of the initial range,
+&apos;&apos;&apos; or the zero-length string if the whole range has been deleted
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; newrange = oDoc.ShiftLeft(&quot;SheetX.G1:L10&quot;) &apos; &quot;&quot;&quot;
+&apos;&apos;&apos; newrange = oDoc.ShiftLeft(&quot;SheetX.G1:L10&quot;, Columns := 3) &apos; &quot;$SheetX.$G$1:$I$10&quot;
+
+Dim sShift As String &apos; Return value
+Dim oSourceAddress As Object &apos; Alias of Range as _Address
+Dim lWidth As Long &apos; Range width
+Dim oShiftAddress As Object &apos; com.sun.star.table.CellRangeAddress - Range adjusted to the right width
+Dim lShiftMode As Long &apos; One of the com.sun.star.sheet.CellDeleteMode enum values
+
+Const cstThisSub = &quot;SFDocuments.Calc.ShiftLeft&quot;
+Const cstSubArgs = &quot;Range, [WholeColumn=False], [Columns]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sShift = &quot;&quot;
+
+Check:
+ If IsMissing(WholeColumn) Or IsEmpty(WholeColumn) Then WholeColumn = False
+ If IsMissing(Columns) Or IsEmpty(Columns) Then Columns = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(WholeColumn, &quot;WholeColumn&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Columns, &quot;Columns&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ Set oSourceAddress = _ParseAddress(Range)
+ Set _LastParsedAddress = Nothing &apos; Range will be erased. Force re-parsing next time
+
+ With oSourceAddress
+
+ &apos; Manage the width of the area to delete
+ &apos; The removeRange() method erases a number of columns equal to the width of the cell range to delete
+ lWidth = .Width
+ If Columns &lt;= 0 Then Columns = lWidth
+ If Columns &lt; lWidth Then
+ Set oShiftAddress = _Offset(oSourceAddress, 0, 0, 0, Columns).XCellRange.RangeAddress
+ Else &apos; Columns is capped at the range width
+ Set oShiftAddress = .XCellRange.RangeAddress
+ End If
+
+ &apos; Determine the Delete mode
+ With com.sun.star.sheet.CellDeleteMode
+ If WholeColumn Then lShiftMode = .COLUMNS Else lShiftMode = .LEFT
+ End With
+
+ &apos; Move the cells as requested. This modifies .XCellRange
+ .XSpreadsheet.removeRange(oShiftAddress, lShiftMode)
+
+ &apos; Determine the remaining area
+ If Columns &lt; lWidth Then sShift = .XCellRange.AbsoluteName
+
+ End With
+
+Finally:
+ ShiftLeft = sShift
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ &apos; When error, return the original range
+ If Not IsNull(oSourceAddress) Then sShift = oSourceAddress.RangeName
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.ShiftLeft
+
+REM -----------------------------------------------------------------------------
+Public Function ShiftRight(Optional ByVal Range As Variant _
+ , Optional ByVal WholeColumn As Variant _
+ , Optional ByVal Columns As Variant _
+ ) As String
+&apos;&apos;&apos; Move a specified range and all next cells in the same rows to the right by inserting empty cells
+&apos;&apos;&apos; The inserted cells can span whole columns or be limited to the height of the range
+&apos;&apos;&apos; The width of the inserted area is provided by the Columns argument
+&apos;&apos;&apos; Nothing happens if the range shift crosses one of the edges of the worksheet
+&apos;&apos;&apos; The execution of the method has no effect on the current selection
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range: the range before which cells have to be inserted, as a string
+&apos;&apos;&apos; WholeColumn: when True (default = False), insert whole columns
+&apos;&apos;&apos; Columns: the width of the area to insert. Default = the width of the Range argument
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the new location of the initial range
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; newrange = oDoc.ShiftRight(&quot;SheetX.A1:F10&quot;) &apos; &quot;$SheetX.$G$1:$L$10&quot;
+&apos;&apos;&apos; newrange = oDoc.ShiftRight(&quot;SheetX.A1:F10&quot;, Columns := 3) &apos; &quot;$SheetX.$D$1:$I$10&quot;
+
+Dim sShift As String &apos; Return value
+Dim oSourceAddress As Object &apos; Alias of Range as _Address
+Dim lWidth As Long &apos; Range width
+Dim oShiftAddress As Object &apos; com.sun.star.table.CellRangeAddress - Range adjusted to the right width
+Dim lShiftMode As Long &apos; One of the com.sun.star.sheet.CellInsertMode enum values
+
+Const cstThisSub = &quot;SFDocuments.Calc.ShiftRight&quot;
+Const cstSubArgs = &quot;Range, [WholeColumn=False], [Columns]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sShift = &quot;&quot;
+
+Check:
+ If IsMissing(WholeColumn) Or IsEmpty(WholeColumn) Then WholeColumn = False
+ If IsMissing(Columns) Or IsEmpty(Columns) Then Columns = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(WholeColumn, &quot;WholeColumn&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Columns, &quot;Columns&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ Set oSourceAddress = _ParseAddress(Range)
+
+ With oSourceAddress
+
+ &apos; Manage the width of the area to Shift
+ &apos; The insertCells() method inserts a number of columns equal to the width of the cell range to Shift
+ lWidth = .Width
+ If Columns &lt;= 0 Then Columns = lWidth
+ If _LastCell(.XSpreadsheet)(0) + Columns &gt; MAXCOLS Then GoTo Catch
+ If Columns &lt;&gt; lWidth Then
+ Set oShiftAddress = _Offset(oSourceAddress, 0, 0, 0, Columns).XCellRange.RangeAddress
+ Else
+ Set oShiftAddress = .XCellRange.RangeAddress
+ End If
+
+ &apos; Determine the Shift mode
+ With com.sun.star.sheet.CellInsertMode
+ If WholeColumn Then lShiftMode = .COLUMNS Else lShiftMode = .RIGHT
+ End With
+
+ &apos; Move the cells as requested. This modifies .XCellRange
+ .XSpreadsheet.insertCells(oShiftAddress, lShiftMode)
+
+ &apos; Determine the receiving area
+ sShift = .XCellRange.AbsoluteName
+
+ End With
+
+Finally:
+ ShiftRight = sShift
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ &apos; When error, return the original range
+ If Not IsNull(oSourceAddress) Then sShift = oSourceAddress.RangeName
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.ShiftRight
+
+REM -----------------------------------------------------------------------------
+Public Function ShiftUp(Optional ByVal Range As Variant _
+ , Optional ByVal WholeRow As Variant _
+ , Optional ByVal Rows As Variant _
+ ) As String
+&apos;&apos;&apos; Delete the topmost rows of a specified range and move all cells below upwards
+&apos;&apos;&apos; The deleted cells can span whole rows or be limited to the width of the range
+&apos;&apos;&apos; The height of the deleted area is provided by the Rows argument
+&apos;&apos;&apos; The execution of the method has no effect on the current selection
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range: the range in which cells have to be erased, as a string
+&apos;&apos;&apos; WholeRow: when True (default = False), erase whole rows
+&apos;&apos;&apos; Rows: the height of the area to delete.
+&apos;&apos;&apos; Default = the height of the Range argument, it is also its maximum value
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A string representing the location of the remaining part of the initial range,
+&apos;&apos;&apos; or the zero-length string if the whole range has been deleted
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; newrange = oDoc.ShiftUp(&quot;SheetX.G1:L10&quot;) &apos; &quot;&quot;
+&apos;&apos;&apos; newrange = oDoc.ShiftUp(&quot;SheetX.G1:L10&quot;, Rows := 3) &apos; &quot;$SheetX.$G$1:$I$10&quot;
+
+Dim sShift As String &apos; Return value
+Dim oSourceAddress As Object &apos; Alias of Range as _Address
+Dim lHeight As Long &apos; Range height
+Dim oShiftAddress As Object &apos; com.sun.star.table.CellRangeAddress - Range adjusted to the right height
+Dim lShiftMode As Long &apos; One of the com.sun.star.sheet.CellDeleteMode enum values
+
+Const cstThisSub = &quot;SFDocuments.Calc.ShiftUp&quot;
+Const cstSubArgs = &quot;Range, [WholeRow=False], [Rows]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sShift = &quot;&quot;
+
+Check:
+ If IsMissing(WholeRow) Or IsEmpty(WholeRow) Then WholeRow = False
+ If IsMissing(Rows) Or IsEmpty(Rows) Then Rows = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(WholeRow, &quot;WholeRow&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Rows, &quot;Rows&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ Set oSourceAddress = _ParseAddress(Range)
+ Set _LastParsedAddress = Nothing &apos; Range will be erased. Force re-parsing next time
+
+ With oSourceAddress
+
+ &apos; Manage the height of the area to delete
+ &apos; The removeRange() method erases a number of rows equal to the height of the cell range to delete
+ lHeight = .Height
+ If Rows &lt;= 0 Then Rows = lHeight
+ If Rows &lt; lHeight Then
+ Set oShiftAddress = _Offset(oSourceAddress, 0, 0, Rows, 0).XCellRange.RangeAddress
+ Else &apos; Rows is capped at the range height
+ Set oShiftAddress = .XCellRange.RangeAddress
+ End If
+
+ &apos; Determine the Delete mode
+ With com.sun.star.sheet.CellDeleteMode
+ If WholeRow Then lShiftMode = .ROWS Else lShiftMode = .UP
+ End With
+
+ &apos; Move the cells as requested. This modifies .XCellRange
+ .XSpreadsheet.removeRange(oShiftAddress, lShiftMode)
+
+ &apos; Determine the remaining area
+ If Rows &lt; lHeight Then sShift = .XCellRange.AbsoluteName
+
+ End With
+
+Finally:
+ ShiftUp = sShift
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ &apos; When error, return the original range
+ If Not IsNull(oSourceAddress) Then sShift = oSourceAddress.RangeName
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc.ShiftUp
+
+REM -----------------------------------------------------------------------------
+Public Function SortRange(Optional ByVal Range As Variant _
+ , Optional ByVal SortKeys As Variant _
+ , Optional ByVal SortOrder As Variant _
+ , Optional ByVal DestinationCell As Variant _
+ , Optional ByVal ContainsHeader As Variant _
+ , Optional ByVal CaseSensitive As Variant _
+ , Optional ByVal SortColumns As Variant _
+ ) As Variant
+&apos;&apos;&apos; Sort the given range on maximum 3 columns/rows. The sorting order may vary by column/row
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range: the range to sort as a string
+&apos;&apos;&apos; SortKeys: a scalar (if 1 column/row) or an array of column/row numbers starting from 1
+&apos;&apos;&apos; SortOrder: a scalar or an array of strings: &quot;ASC&quot; or &quot;DESC&quot;
+&apos;&apos;&apos; Each item is paired with the corresponding item in SortKeys
+&apos;&apos;&apos; If the SortOrder array is shorter than SortKeys, the remaining keys are sorted
+&apos;&apos;&apos; in ascending order
+&apos;&apos;&apos; DestinationCell: the destination of the sorted range of cells, as a string
+&apos;&apos;&apos; If given as range, the destination will be reduced to its top-left cell
+&apos;&apos;&apos; By default, Range is overwritten with its sorted content
+&apos;&apos;&apos; ContainsHeader: when True, the first row/column is not sorted. Default = False
+&apos;&apos;&apos; CaseSensitive: only for string comparisons, default = False
+&apos;&apos;&apos; SortColumns: when True, the columns are sorted from left to right
+&apos;&apos;&apos; Default = False: rows are sorted from top to bottom.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The modified range of cells as a string
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDoc.SortRange(&quot;A2:J200&quot;, Array(1, 3), , Array(&quot;ASC&quot;, &quot;DESC&quot;), CaseSensitive := True)
+&apos;&apos;&apos; &apos; Sort on columns A (ascending) and C (descending)
+
+Dim sSort As String &apos; Return value
+Dim oRangeAddress As _Address &apos; Parsed range
+Dim oRange As Object &apos; com.sun.star.table.XCellRange
+Dim oDestRange As Object &apos; Destination as a range
+Dim oDestAddress As Object &apos; com.sun.star.table.CellRangeAddress
+Dim oDestCell As Object &apos; com.sun.star.table.CellAddress
+Dim vSortDescriptor As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim vSortFields As Variant &apos; Array of com.sun.star.table.TableSortField
+Dim sOrder As String &apos; Item in SortOrder
+Dim i As Long
+Const cstThisSub = &quot;SFDocuments.Calc.SortRange&quot;
+Const cstSubArgs = &quot;Range, SortKeys, [TargetRange=&quot;&quot;&quot;&quot;], [SortOrder=&quot;&quot;ASC&quot;&quot;], [DestinationCell=&quot;&quot;&quot;&quot;], [ContainsHeader=False], [CaseSensitive=False], [SortColumns=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sSort = &quot;&quot;
+
+Check:
+ If IsMissing(SortKeys) Or IsEmpty(SortKeys) Then
+ SortKeys = Array(1)
+ ElseIf Not IsArray(SortKeys) Then
+ SortKeys = Array(SortKeys)
+ End If
+ If IsMissing(DestinationCell) Or IsEmpty(DestinationCell) Then DestinationCell = &quot;&quot;
+ If IsMissing(SortOrder) Or IsEmpty(SortOrder) Then
+ SortOrder = Array(&quot;ASC&quot;)
+ ElseIf Not IsArray(SortOrder) Then
+ SortOrder = Array(SortOrder)
+ End If
+ If IsMissing(ContainsHeader) Or IsEmpty(ContainsHeader) Then ContainsHeader = False
+ 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 _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
+ If Not ScriptForge.SF_Utils._ValidateArray(SortOrder, &quot;SortOrder&quot;, 1, V_STRING, True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ContainsHeader, &quot;ContainsHeader&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(CaseSensitive, &quot;CaseSensitive&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(SortColumns, &quot;SortColumns&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+ Set oRangeAddress = _ParseAddress(Range)
+ If Len(DestinationCell) &gt; 0 Then Set oDestRange = _ParseAddress(DestinationCell)
+
+Try:
+ &apos; Initialize the sort descriptor
+ Set oRange = oRangeAddress.XCellRange
+ vSortDescriptor = oRange.createSortDescriptor
+ vSortDescriptor = ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;IsSortColumns&quot;, SortColumns)
+ vSortDescriptor = ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;ContainsHeader&quot;, ContainsHeader)
+ vSortDescriptor = ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;BindFormatsToContent&quot;, True)
+ If Len(DestinationCell) = 0 Then
+ vSortDescriptor = ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;CopyOutputData&quot;, False)
+ Else
+ Set oDestAddress = oDestRange.XCellRange.RangeAddress
+ Set oDestCell = New com.sun.star.table.CellAddress
+ With oDestAddress
+ oDestCell.Sheet = .Sheet
+ oDestCell.Column = .StartColumn
+ oDestCell.Row = .StartRow
+ End With
+ vSortDescriptor = ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;CopyOutputData&quot;, True)
+ vSortDescriptor = ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;OutputPosition&quot;, oDestCell)
+ End If
+ vSortDescriptor = ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;IsUserListEnabled&quot;, False)
+
+ &apos; Define the sorting keys
+ vSortFields = Array()
+ ReDim vSortFields(0 To UBound(SortKeys))
+ For i = 0 To UBound(SortKeys)
+ vSortFields(i) = New com.sun.star.table.TableSortField
+ If i &gt; UBound(SortOrder) Then sOrder = &quot;&quot; Else sOrder = SortOrder(i)
+ If Len(sOrder) = 0 Then sOrder = &quot;ASC&quot;
+ With vSortFields(i)
+ .Field = SortKeys(i) - 1
+ .IsAscending = ( UCase(sOrder) = &quot;ASC&quot; )
+ .IsCaseSensitive = CaseSensitive
+ End With
+ Next i
+
+ &apos; Associate the keys and the descriptor, and sort
+ vSortDescriptor = ScriptForge.SF_Utils._SetPropertyValue(vSortDescriptor, &quot;SortFields&quot;, vSortFields)
+ oRange.sort(vSortDescriptor)
+
+ &apos; Compute the changed area
+ If Len(DestinationCell) = 0 Then
+ sSort = oRangeAddress.RangeName
+ Else
+ With oRangeAddress
+ sSort = _Offset(oDestRange, 0, 0, .Height, .Width).RangeName
+ End With
+ End If
+
+Finally:
+ SortRange = sSort
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc.SortRange
+
+REM ======================================================= SUPERCLASS PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get CustomProperties() As Variant
+ CustomProperties = [_Super].GetProperty(&quot;CustomProperties&quot;)
+End Property &apos; SFDocuments.SF_Calc.CustomProperties
+
+REM -----------------------------------------------------------------------------
+Property Let CustomProperties(Optional ByVal pvCustomProperties As Variant)
+ [_Super].CustomProperties = pvCustomProperties
+End Property &apos; SFDocuments.SF_Calc.CustomProperties
+
+REM -----------------------------------------------------------------------------
+Property Get Description() As Variant
+ Description = [_Super].GetProperty(&quot;Description&quot;)
+End Property &apos; SFDocuments.SF_Calc.Description
+
+REM -----------------------------------------------------------------------------
+Property Let Description(Optional ByVal pvDescription As Variant)
+ [_Super].Description = pvDescription
+End Property &apos; SFDocuments.SF_Calc.Description
+
+REM -----------------------------------------------------------------------------
+Property Get DocumentProperties() As Variant
+ DocumentProperties = [_Super].GetProperty(&quot;DocumentProperties&quot;)
+End Property &apos; SFDocuments.SF_Calc.DocumentProperties
+
+REM -----------------------------------------------------------------------------
+Property Get DocumentType() As String
+ DocumentType = [_Super].GetProperty(&quot;DocumentType&quot;)
+End Property &apos; SFDocuments.SF_Calc.DocumentType
+
+REM -----------------------------------------------------------------------------
+Property Get ExportFilters() As Variant
+ ExportFilters = [_Super].GetProperty(&quot;ExportFilters&quot;)
+End Property &apos; SFDocuments.SF_Calc.ExportFilters
+
+REM -----------------------------------------------------------------------------
+Property Get ImportFilters() As Variant
+ ImportFilters = [_Super].GetProperty(&quot;ImportFilters&quot;)
+End Property &apos; SFDocuments.SF_Calc.ImportFilters
+
+REM -----------------------------------------------------------------------------
+Property Get IsBase() As Boolean
+ IsBase = [_Super].GetProperty(&quot;IsBase&quot;)
+End Property &apos; SFDocuments.SF_Calc.IsBase
+
+REM -----------------------------------------------------------------------------
+Property Get IsCalc() As Boolean
+ IsCalc = [_Super].GetProperty(&quot;IsCalc&quot;)
+End Property &apos; SFDocuments.SF_Calc.IsCalc
+
+REM -----------------------------------------------------------------------------
+Property Get IsDraw() As Boolean
+ IsDraw = [_Super].GetProperty(&quot;IsDraw&quot;)
+End Property &apos; SFDocuments.SF_Calc.IsDraw
+
+REM -----------------------------------------------------------------------------
+Property Get IsImpress() As Boolean
+ IsImpress = [_Super].GetProperty(&quot;IsImpress&quot;)
+End Property &apos; SFDocuments.SF_Calc.IsImpress
+
+REM -----------------------------------------------------------------------------
+Property Get IsMath() As Boolean
+ IsMath = [_Super].GetProperty(&quot;IsMath&quot;)
+End Property &apos; SFDocuments.SF_Calc.IsMath
+
+REM -----------------------------------------------------------------------------
+Property Get IsWriter() As Boolean
+ IsWriter = [_Super].GetProperty(&quot;IsWriter&quot;)
+End Property &apos; SFDocuments.SF_Calc.IsWriter
+
+REM -----------------------------------------------------------------------------
+Property Get Keywords() As Variant
+ Keywords = [_Super].GetProperty(&quot;Keywords&quot;)
+End Property &apos; SFDocuments.SF_Calc.Keywords
+
+REM -----------------------------------------------------------------------------
+Property Let Keywords(Optional ByVal pvKeywords As Variant)
+ [_Super].Keywords = pvKeywords
+End Property &apos; SFDocuments.SF_Calc.Keywords
+
+REM -----------------------------------------------------------------------------
+Property Get Readonly() As Variant
+ Readonly = [_Super].GetProperty(&quot;Readonly&quot;)
+End Property &apos; SFDocuments.SF_Calc.Readonly
+
+REM -----------------------------------------------------------------------------
+Property Get Subject() As Variant
+ Subject = [_Super].GetProperty(&quot;Subject&quot;)
+End Property &apos; SFDocuments.SF_Calc.Subject
+
+REM -----------------------------------------------------------------------------
+Property Let Subject(Optional ByVal pvSubject As Variant)
+ [_Super].Subject = pvSubject
+End Property &apos; SFDocuments.SF_Calc.Subject
+
+REM -----------------------------------------------------------------------------
+Property Get Title() As Variant
+ Title = [_Super].GetProperty(&quot;Title&quot;)
+End Property &apos; SFDocuments.SF_Calc.Title
+
+REM -----------------------------------------------------------------------------
+Property Let Title(Optional ByVal pvTitle As Variant)
+ [_Super].Title = pvTitle
+End Property &apos; SFDocuments.SF_Calc.Title
+
+REM -----------------------------------------------------------------------------
+Property Get XComponent() As Variant
+ XComponent = [_Super].GetProperty(&quot;XComponent&quot;)
+End Property &apos; SFDocuments.SF_Calc.XComponent
+
+REM ========================================================== SUPERCLASS METHODS
+
+REM -----------------------------------------------------------------------------
+&apos;Public Function Activate() As Boolean
+&apos; Activate = [_Super].Activate()
+&apos;End Function &apos; SFDocuments.SF_Calc.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function CloseDocument(Optional ByVal SaveAsk As Variant) As Boolean
+ CloseDocument = [_Super].CloseDocument(SaveAsk)
+End Function &apos; SFDocuments.SF_Calc.CloseDocument
+
+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_Calc.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_Calc.ExportAsPDF
+
+REM -----------------------------------------------------------------------------
+Public Function RemoveMenu(Optional ByVal MenuHeader As Variant) As Boolean
+ RemoveMenu = [_Super].RemoveMenu(MenuHeader)
+End Function &apos; SFDocuments.SF_Calc.RemoveMenu
+
+REM -----------------------------------------------------------------------------
+Public Sub RunCommand(Optional ByVal Command As Variant _
+ , ParamArray Args As Variant _
+ )
+ [_Super].RunCommand(Command, Args)
+End Sub &apos; SFDocuments.SF_Calc.RunCommand
+
+REM -----------------------------------------------------------------------------
+Public Function Save() As Boolean
+ Save = [_Super].Save()
+End Function &apos; SFDocuments.SF_Calc.Save
+
+REM -----------------------------------------------------------------------------
+Public Function SaveAs(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
+ SaveAs = [_Super].SaveAs(FileName, Overwrite, Password, FilterName, FilterOptions)
+End Function &apos; SFDocuments.SF_Calc.SaveAs
+
+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_Calc.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_Calc.SetPrinter
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Public Function _ConvertFromDataArray(ByRef pvDataArray As Variant) As Variant
+&apos;&apos;&apos; Convert a data array to a scalar, a vector or a 2D array
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pvDataArray: an array as returned by the XCellRange.getDataArray or .getFormulaArray methods
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A scalar, a zero-based 1D array or a zero-based 2D array of strings and/or doubles
+&apos;&apos;&apos; To convert doubles to dates, use the CDate builtin function
+
+Dim vArray As Variant &apos; Return value
+Dim lMax1 As Long &apos; UBound of pvDataArray
+Dim lMax2 As Long &apos; UBound of pvDataArray items
+Dim i As Long
+Dim j As Long
+
+ vArray = Empty
+
+Try:
+ &apos; Convert the data array to scalar, vector or array
+ lMax1 = UBound(pvDataArray)
+ If lMax1 &gt;= 0 Then
+ lMax2 = UBound(pvDataArray(0))
+ If lMax2 &gt;= 0 Then
+ If lMax1 + lMax2 &gt; 0 Then vArray = Array()
+ Select Case True
+ Case lMax1 = 0 And lMax2 = 0 &apos; Scalar
+ vArray = pvDataArray(0)(0)
+ Case lMax1 &gt; 0 And lMax2 = 0 &apos; Vertical vector
+ ReDim vArray(0 To lMax1)
+ For i = 0 To lMax1
+ vArray(i) = pvDataArray(i)(0)
+ Next i
+ Case lMax1 = 0 And lMax2 &gt; 0 &apos; Horizontal vector
+ ReDim vArray(0 To lMax2)
+ For j = 0 To lMax2
+ vArray(j) = pvDataArray(0)(j)
+ Next j
+ Case Else &apos; Array
+ ReDim vArray(0 To lMax1, 0 To lMax2)
+ For i = 0 To lMax1
+ For j = 0 To lMax2
+ vArray(i, j) = pvDataArray(i)(j)
+ Next j
+ Next i
+ End Select
+ End If
+ End If
+
+Finally:
+ _ConvertFromDataArray = vArray
+End Function &apos; SF_Documents.SF_Calc._ConvertFromDataArray
+
+REM -----------------------------------------------------------------------------
+Private Function _ConvertToCellValue(ByVal pvItem As Variant) As Variant
+&apos;&apos;&apos; Convert the argument to a valid Calc cell content
+
+Dim vCell As Variant &apos; Return value
+
+Try:
+ Select Case ScriptForge.SF_Utils._VarTypeExt(pvItem)
+ Case V_STRING : vCell = pvItem
+ Case V_DATE : vCell = CDbl(pvItem)
+ Case ScriptForge.V_NUMERIC : vCell = CDbl(pvItem)
+ Case ScriptForge.V_BOOLEAN : vCell = CDbl(Iif(pvItem, 1, 0))
+ Case Else : vCell = &quot;&quot;
+ End Select
+
+Finally:
+ _ConvertToCellValue = vCell
+ Exit Function
+End Function &apos; SF_Documents.SF_Calc._ConvertToCellValue
+
+REM -----------------------------------------------------------------------------
+Private Function _ConvertToDataArray(ByRef pvArray As Variant _
+ , Optional ByVal plRows As Long _
+ , Optional ByVal plColumns As Long _
+ ) As Variant
+&apos;&apos;&apos; Create a 2-dimensions nested array (compatible with the ranges .DataArray property)
+&apos;&apos;&apos; from a scalar, a 1D array or a 2D array
+&apos;&apos;&apos; Input may be a 1D array of arrays, typically when call issued by a Python script
+&apos;&apos;&apos; Array items are converted to (possibly empty) strings or doubles
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pvArray: the input scalar or array. If array, must be 1 or 2D otherwise it is ignored.
+&apos;&apos;&apos; plRows, plColumns: the upper bounds of the data array
+&apos;&apos;&apos; If bigger than input array, fill with zero-length strings
+&apos;&apos;&apos; If smaller than input array, truncate
+&apos;&apos;&apos; If plRows = 0 and the input array is a vector, the data array is aligned horizontally
+&apos;&apos;&apos; They are either both present or both absent
+&apos;&apos;&apos; When absent
+&apos;&apos;&apos; The size of the output is fully determined by the input array
+&apos;&apos;&apos; Vectors are aligned vertically
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A data array compatible with ranges .DataArray property
+&apos;&apos;&apos; The output is always an array of nested arrays
+
+Dim vDataArray() As Variant &apos; Return value
+Dim vVector() As Variant &apos; A temporary 1D array
+Dim vItem As Variant &apos; A single input item
+Dim iDims As Integer &apos; Number of dimensions of the input argument
+Dim lMin1 As Long &apos; Lower bound (1) of input array
+Dim lMax1 As Long &apos; Upper bound (1)
+Dim lMin2 As Long &apos; Lower bound (2)
+Dim lMax2 As Long &apos; Upper bound (2)
+Dim lRows As Long &apos; Upper bound of vDataArray
+Dim lCols As Long &apos; Upper bound of vVector
+Dim bHorizontal As Boolean &apos; Horizontal vector
+Dim bDataArray As Boolean &apos; Input array is already an array of arrays
+Dim i As Long
+Dim j As Long
+
+Const cstEmpty = &quot;&quot; &apos; Empty cell
+
+ If IsMissing(plRows) Or IsEmpty(plRows) Then plRows = -1
+ If IsMissing(plColumns) Or IsEmpty(plColumns) Then plColumns = -1
+
+ vDataArray = Array()
+
+Try:
+ &apos; Check the input argument and know its boundaries
+ iDims = ScriptForge.SF_Array.CountDims(pvArray)
+ If iDims = 0 Or iDims &gt; 2 Then Exit Function
+ lMin1 = 0 : lMax1 = 0 &apos; Default values
+ lMin2 = 0 : lMax2 = 0
+ Select Case iDims
+ Case -1 &apos; Scalar value
+ Case 1
+ bHorizontal = ( plRows = 0 And plColumns &gt; 0 )
+ bDataArray = IsArray(pvArray(0))
+ If Not bDataArray Then
+ If Not bHorizontal Then
+ lMin1 = LBound(pvArray) : lMax1 = UBound(pvArray)
+ Else
+ lMin2 = LBound(pvArray) : lMax2 = UBound(pvArray)
+ End If
+ Else
+ iDims = 2
+ lMin1 = LBound(pvArray) : lMax1 = UBound(pvArray)
+ lMin2 = LBound(pvArray(0)) : lMax2 = UBound(pvArray(0))
+ End If
+ Case 2
+ lMin1 = LBound(pvArray, 1) : lMax1 = UBound(pvArray, 1)
+ lMin2 = LBound(pvArray, 2) : lMax2 = UBound(pvArray, 2)
+ End Select
+
+ &apos; Set the output dimensions accordingly
+ If plRows &gt;= 0 Then &apos; Dimensions of output are imposed
+ lRows = plRows
+ lCols = plColumns
+ Else &apos; Dimensions of output determined by input argument
+ lRows = 0 : lCols = 0 &apos; Default values
+ Select Case iDims
+ Case -1 &apos; Scalar value
+ Case 1 &apos; Vectors are aligned vertically
+ lRows = lMax1 - lMin1
+ Case 2
+ lRows = lMax1 - lMin1
+ lCols = lMax2 - lMin2
+ End Select
+ End If
+ ReDim vDataArray(0 To lRows)
+
+ &apos; Feed the output array row by row, each row being a vector
+ For i = 0 To lRows
+ ReDim vVector(0 To lCols)
+ For j = 0 To lCols
+ If i &gt; lMax1 - lMin1 Then
+ vVector(j) = cstEmpty
+ ElseIf j &gt; lMax2 - lMin2 Then
+ vVector(j) = cstEmpty
+ Else
+ Select Case iDims
+ Case -1 : vItem = _ConvertToCellValue(pvArray)
+ Case 1
+ If bHorizontal Then
+ vItem = _ConvertToCellValue(pvArray(j + lMin2))
+ Else
+ vItem = _ConvertToCellValue(pvArray(i + lMin1))
+ End If
+ Case 2
+ If bDataArray Then
+ vItem = _ConvertToCellValue(pvArray(i + lMin1)(j + lMin2))
+ Else
+ vItem = _ConvertToCellValue(pvArray(i + lMin1, j + lMin2))
+ End If
+ End Select
+ vVector(j) = vItem
+ End If
+ vDataArray(i) = vVector
+ Next j
+ Next i
+
+Finally:
+ _ConvertToDataArray = vDataArray
+ Exit Function
+End Function &apos; SF_Documents.SF_Calc._ConvertToDataArray
+
+REM -----------------------------------------------------------------------------
+Private Function _DFunction(ByVal psFunction As String _
+ , Optional ByVal Range As Variant _
+ ) As Double
+&apos;&apos;&apos; Apply the given function on all the numeric values stored in the given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Range : the range as a string where to apply the function on
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The resulting value as a double
+
+Dim dblGet As Double &apos; Return value
+Dim oAddress As Object &apos; Alias of Range
+Dim vFunction As Variant &apos; com.sun.star.sheet.GeneralFunction.XXX
+Dim cstThisSub As String : cstThisSub = &quot;SFDocuments.Calc.&quot; &amp; psFunction
+Const cstSubArgs = &quot;Range&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ dblGet = 0
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Range, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Get the data
+ Set oAddress = _ParseAddress(Range)
+ Select Case psFunction
+ Case &quot;DAvg&quot; : vFunction = com.sun.star.sheet.GeneralFunction.AVERAGE
+ Case &quot;DCount&quot; : vFunction = com.sun.star.sheet.GeneralFunction.COUNTNUMS
+ Case &quot;DMax&quot; : vFunction = com.sun.star.sheet.GeneralFunction.MAX
+ Case &quot;DMin&quot; : vFunction = com.sun.star.sheet.GeneralFunction.MIN
+ Case &quot;DSum&quot; : vFunction = com.sun.star.sheet.GeneralFunction.SUM
+ Case Else : GoTo Finally
+ End Select
+ dblGet = oAddress.XCellRange.computeFunction(vFunction)
+
+Finally:
+ _DFunction = dblGet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+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:
+&apos;&apos;&apos; ColumnNumber: the column number, must be in the interval 1 ... 1024
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; a string representation of the column name, in range &apos;A&apos;..&apos;AMJ&apos;
+&apos;&apos;&apos; Adapted from a Python function by sundar nataraj
+&apos;&apos;&apos; http://stackoverflow.com/questions/23861680/convert-spreadsheet-number-to-column-letter
+
+Dim sCol As String &apos; Return value
+Dim lDiv As Long &apos; Intermediate result
+Dim lMod As Long &apos; Result of modulo 26 operation
+
+Try:
+ lDiv = plColumnNumber
+ Do While lDiv &gt; 0
+ lMod = (lDiv - 1) Mod 26
+ sCol = Chr(65 + lMod) + sCol
+ lDiv = Int((lDiv - lMod)/26)
+ Loop
+
+Finally:
+ _GetColumnName = sCol
+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
+
+Dim oCursor As Object &apos; Cursor on the cell
+Dim oRange As Object &apos; The used range
+Dim vCoordinates(0 To 1) As Long &apos; Return value: (0) = Column, (1) = Row
+
+Try:
+ Set oCursor = poSheet.createCursorByRange(poSheet.getCellRangeByName(&quot;A1&quot;))
+ oCursor.gotoEndOfUsedArea(True)
+ Set oRange = poSheet.getCellRangeByName(oCursor.AbsoluteName)
+
+ vCoordinates(0) = oRange.RangeAddress.EndColumn + 1
+ vCoordinates(1) = oRange.RangeAddress.EndRow + 1
+
+Finally:
+ _LastCell = vCoordinates
+End Function &apos; SFDocuments.SF_Calc._LastCell
+
+REM -----------------------------------------------------------------------------
+Public Function _Offset(ByRef pvRange As Variant _
+ , ByVal plRows As Long _
+ , ByVal plColumns As Long _
+ , ByVal plHeight As Long _
+ , ByVal plWidth As Long _
+ ) As Object
+&apos;&apos;&apos; Returns a new range offset by a certain number of rows and columns from a given range
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pvRange : the range, as a string or an object, from which the function searches for the new range
+&apos;&apos;&apos; plRows : the number of rows by which the reference was corrected up (negative value) or down.
+&apos;&apos;&apos; plColumns : the number of columns by which the reference was corrected to the left (negative value) or to the right.
+&apos;&apos;&apos; plHeight : the vertical height for an area that starts at the new reference position.
+&apos;&apos;&apos; plWidth : the horizontal width for an area that starts at the new reference position.
+&apos;&apos;&apos; Arguments Rows and Columns must not lead to zero or negative start row or column.
+&apos;&apos;&apos; Arguments Height and Width must not lead to zero or negative count of rows or columns.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A new range as object of type _Address
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; OFFSETADDRESSERROR The computed range of cells falls beyond the sheet boundaries
+
+Dim oOffset As Object &apos; Return value
+Dim oAddress As Object &apos; Alias of Range
+Dim oSheet As Object &apos; com.sun.star.sheet.XSpreadsheet
+Dim oRange As Object &apos; com.sun.star.table.XCellRange
+Dim oNewRange As Object &apos; com.sun.star.table.XCellRange
+Dim lLeft As Long &apos; New range coordinates
+Dim lTop As Long
+Dim lRight As Long
+Dim lBottom As Long
+
+ Set oOffset = Nothing
+
+Check:
+ If plHeight &lt; 0 Or plWidth &lt; 0 Then GoTo CatchAddress
+
+Try:
+ If VarType(pvRange) = V_STRING Then Set oAddress = _ParseAddress(pvRange) Else Set oAddress = pvRange
+ Set oSheet = oAddress.XSpreadSheet
+ Set oRange = oAddress.XCellRange.RangeAddress
+
+
+ &apos; Compute and validate new coordinates
+ With oRange
+ lLeft = .StartColumn + plColumns
+ lTop = .StartRow + plRows
+ lRight = lLeft + Iif(plWidth = 0, .EndColumn - .StartColumn, plWidth - 1)
+ lBottom = lTop + Iif(plHeight = 0, .EndRow - .StartRow, plHeight - 1)
+ If lLeft &lt; 0 Or lRight &lt; 0 Or lTop &lt; 0 Or lBottom &lt; 0 _
+ Or lLeft &gt; MAXCOLS Or lRight &gt; MAXCOLS _
+ Or lTop &gt; MAXROWS Or lBottom &gt; MAXROWS _
+ Then GoTo CatchAddress
+ Set oNewRange = oSheet.getCellRangeByPosition(lLeft, lTop, lRight, lBottom)
+ End With
+
+ &apos; Define the new range address
+ Set oOffset = New _Address
+ With oOffset
+ .ObjectType = CALCREFERENCE
+ .ServiceName = SERVICEREFERENCE
+ .RawAddress = oNewRange.AbsoluteName
+ .Component = _Component
+ .XSpreadsheet = oNewRange.Spreadsheet
+ .SheetName = .XSpreadsheet.Name
+ .SheetIndex = .XSpreadsheet.RangeAddress.Sheet
+ .RangeName = .RawAddress
+ .XCellRange = oNewRange
+ .Height = oNewRange.RangeAddress.EndRow - oNewRange.RangeAddress.StartRow + 1
+ .Width = oNewRange.RangeAddress.EndColumn - oNewRange.RangeAddress.StartColumn + 1
+ End With
+
+Finally:
+ Set _Offset = oOffset
+ Exit Function
+Catch:
+ GoTo Finally
+CatchAddress:
+ ScriptForge.SF_Exception.RaiseFatal(OFFSETADDRESSERROR, &quot;Range&quot;, oAddress.RawAddress _
+ , &quot;Rows&quot;, plRows, &quot;Columns&quot;, plColumns, &quot;Height&quot;, plHeight, &quot;Width&quot;, plWidth _
+ , &quot;Document&quot;, [_Super]._FileIdent())
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Calc._Offset
+
+REM -----------------------------------------------------------------------------
+Private Function _ParseAddress(ByVal psAddress As String) As Object
+&apos;&apos;&apos; Parse and validate a sheet or range reference
+&apos;&apos;&apos; Syntax to parse:
+&apos;&apos;&apos; [Sheet].[Range]
+&apos;&apos;&apos; Sheet =&gt; [&apos;][$]sheet[&apos;] or document named range or ~
+&apos;&apos;&apos; Range =&gt; A1:D10, A1, A:D, 10:10 ($ ignored), or sheet named range or ~
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; An object of type _Address
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; CALCADDRESSERROR &apos; Address could not be parsed to a valid address
+
+Dim oAddress As Object &apos; Return value
+Dim sAddress As String &apos; Alias of psAddress
+Dim lStart As Long &apos; Position of found regex
+Dim sSheet As String &apos; Sheet component
+Dim sRange As String &apos; Range component
+Dim oSheets As Object &apos; com.sun.star.sheet.XSpreadsheets
+Dim oNamedRanges As Object &apos; com.sun.star.sheet.XNamedRanges
+Dim oRangeAddress As Object &apos; Alias for rangeaddress
+Dim vLastCell As Variant &apos; Result of _LastCell() method
+Dim oSelect As Object &apos; Current selection
+
+ &apos; If psAddress has already been parsed, get the result back
+ If Not IsNull(_LastParsedAddress) Then
+ &apos; Given argument must contain an explicit reference to a sheet
+ If (InStr(psAddress, &quot;~.&quot;) = 0 And InStr(psAddress, &quot;.&quot;) &gt; 0 And psAddress = _LastParsedAddress.RawAddress) _
+ Or psAddress = _LastParsedAddress.RangeName Then
+ Set _ParseAddress = _LastParsedAddress
+ Exit Function
+ Else
+ Set _LastParsedAddress = Nothing
+ End If
+ End If
+
+ &apos; Reinitialize a new _Address object
+ Set oAddress = New _Address
+ With oAddress
+ sSheet = &quot;&quot; : sRange = &quot;&quot;
+ .SheetName = &quot;&quot; : .RangeName = &quot;&quot;
+
+ .ObjectType = CALCREFERENCE
+ .ServiceName = SERVICEREFERENCE
+ .RawAddress = psAddress
+ Set .XSpreadSheet = Nothing : Set .XCellRange = Nothing
+
+ &apos; Remove leading &apos;$&apos;
+ If Left(psAddress, 1) = &quot;$&quot; Then sAddress = Mid(psAddress, 2) Else sAddress = psAddress
+ &apos; Split in sheet and range components - Check presence of surrounding single quotes or dot
+ If Left(sAddress, 1) = &quot;&apos;&quot; Then
+ lStart = 1
+ sSheet = ScriptForge.SF_String.FindRegex(sAddress, &quot;^&apos;[^\[\]*?:\/\\]+&apos;&quot;)
+ If lStart = 0 Then GoTo CatchAddress &apos; Invalid sheet name
+ If Len(sAddress) &gt; Len(sSheet) + 1 Then
+ If Mid(sAddress, Len(sSheet) + 1, 1) = &quot;.&quot; then sRange = Mid(sAddress, Len(sSheet) + 2)
+ End If
+ sSheet = Replace(Replace(sSheet, &quot;$&quot;, &quot;&quot;), &quot;&apos;&quot;, &quot;&quot;)
+ ElseIf InStr(sAddress, &quot;.&quot;) &gt; 0 Then
+ sSheet = Replace(Split(sAddress, &quot;.&quot;)(0), &quot;$&quot;, &quot;&quot;)
+ sRange = Replace(Split(sAddress, &quot;.&quot;)(1), &quot;$&quot;, &quot;&quot;)
+ Else
+ sSheet = sAddress
+ End If
+
+ &apos; Resolve sheet part: either a document named range, or the active sheet or a real sheet
+ Set oSheets = _Component.getSheets()
+ Set oNamedRanges = _Component.NamedRanges
+ If oSheets.hasByName(sSheet) Then
+ ElseIf sSheet = &quot;~&quot; And Len(sRange) &gt; 0 Then
+ sSheet = _Component.CurrentController.ActiveSheet.Name
+ ElseIf oNamedRanges.hasByName(sSheet) Then
+ .XCellRange = oNamedRanges.getByName(sSheet).ReferredCells
+ sSheet = oSheets.getByIndex(oNamedRanges.getByName(sSheet).ReferencePosition.Sheet).Name
+ Else
+ sRange = sSheet
+ sSheet = _Component.CurrentController.ActiveSheet.Name
+ End If
+ .SheetName = sSheet
+ .XSpreadSheet = oSheets.getByName(sSheet)
+ .SheetIndex = .XSpreadSheet.RangeAddress.Sheet
+
+ &apos; Resolve range part - either a sheet named range or the current selection or a real range or &quot;&quot;
+ If IsNull(.XCellRange) Then
+ Set oNamedRanges = .XSpreadSheet.NamedRanges
+ If sRange = &quot;~&quot; Then
+ Set oSelect = _Component.CurrentController.getSelection()
+ If oSelect.supportsService(&quot;com.sun.star.sheet.SheetCellRanges&quot;) Then &apos; Multiple selections
+ Set .XCellRange = oSelect.getByIndex(0)
+ Else
+ Set .XCellRange = oSelect
+ End If
+ ElseIf sRange = &quot;*&quot; Or sRange = &quot;&quot; Then
+ vLastCell = _LastCell(.XSpreadSheet)
+ sRange = &quot;A1:&quot; &amp; _GetColumnName(vLastCell(0)) &amp; CStr(vLastCell(1))
+ Set .XCellRange = .XSpreadSheet.getCellRangeByName(sRange)
+ ElseIf oNamedRanges.hasByName(sRange) Then
+ .XCellRange = oNamedRanges.getByName(sRange).ReferredCells
+ Else
+ On Local Error GoTo CatchError
+ Set .XCellRange = .XSpreadSheet.getCellRangeByName(sRange)
+ &apos; If range reaches the limits of the sheets, reduce it up to the used area
+ Set oRangeAddress = .XCellRange.RangeAddress
+ If oRangeAddress.StartColumn = 0 And oRangeAddress.EndColumn = MAXCOLS - 1 Then
+ vLastCell = _LastCell(.XSpreadSheet)
+ sRange = &quot;A&quot; &amp; CStr(oRangeAddress.StartRow + 1) &amp; &quot;:&quot; _
+ &amp; _GetColumnName(vLastCell(0)) &amp; CStr(oRangeAddress.EndRow + 1)
+ Set .XCellRange = .XSpreadSheet.getCellRangeByName(sRange)
+ ElseIf oRangeAddress.StartRow = 0 And oRangeAddress.EndRow = MAXROWS - 1 Then
+ vLastCell = _LastCell(.XSpreadSheet)
+ sRange = _GetColumnName(oRangeAddress.StartColumn + 1) &amp; &quot;1&quot; &amp; &quot;:&quot; _
+ &amp; _GetColumnName(oRangeAddress.EndColumn + 1) &amp; CStr(_LastCell(.XSpreadSheet)(1))
+ Set .XCellRange = .XSpreadSheet.getCellRangeByName(sRange)
+ End If
+ End If
+ End If
+ If IsNull(.XCellRange) Then GoTo CatchAddress
+
+ Set oRangeAddress = .XCellRange.RangeAddress
+ .RangeName = .XCellRange.AbsoluteName
+ .Height = oRangeAddress.EndRow - oRangeAddress.StartRow + 1
+ .Width = oRangeAddress.EndColumn - oRangeAddress.StartColumn + 1
+
+ &apos; Remember the current component in case of use outside the current instance
+ Set .Component = _Component
+
+ End With
+
+ &apos; Store last parsed address for reuse
+ Set _LastParsedAddress = oAddress
+
+Finally:
+ Set _ParseAddress = oAddress
+ Exit Function
+CatchError:
+ ScriptForge.SF_Exception.Clear()
+CatchAddress:
+ ScriptForge.SF_Exception.RaiseFatal(CALCADDRESSERROR, &quot;Range&quot;, psAddress _
+ , &quot;Document&quot;, [_Super]._FileIdent())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc._ParseAddress
+
+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 oProperties As Object &apos; Document or Custom properties
+Dim vLastCell As Variant &apos; Coordinates of last used cell in a sheet
+Dim oSelect As Object &apos; Current selection
+Dim vRanges As Variant &apos; List of selected ranges
+Dim oAddress As Object &apos; _Address type for range description
+Dim oCursor As Object &apos; com.sun.star.sheet.XSheetCellCursor
+Dim i As Long
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ _PropertyGet = False
+
+ cstThisSub = &quot;SFDocuments.Calc.get&quot; &amp; psProperty
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+ Select Case UCase(psProperty)
+ Case UCase(&quot;CurrentSelection&quot;)
+ Set oSelect = _Component.CurrentController.getSelection()
+ If IsNull(oSelect) Then
+ _PropertyGet = Array()
+ ElseIf oSelect.supportsService(&quot;com.sun.star.sheet.SheetCellRanges&quot;) Then &apos; Multiple selections
+ vRanges = Array()
+ For i = 0 To oSelect.Count - 1
+ vRanges = ScriptForge.SF_Array.Append(vRanges, oSelect.getByIndex(i).AbsoluteName)
+ Next i
+ _PropertyGet = vRanges
+ Else
+ _PropertyGet = oSelect.AbsoluteName
+ End If
+ Case UCase(&quot;Height&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then
+ _PropertyGet = 0
+ Else
+ If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ _PropertyGet = _ParseAddress(pvArg).Height
+ End If
+ Case UCase(&quot;FirstCell&quot;), UCase(&quot;FirstRow&quot;), UCase(&quot;FirstColumn&quot;) _
+ , UCase(&quot;LastCell&quot;), UCase(&quot;LastColumn&quot;), UCase(&quot;LastRow&quot;) _
+ , UCase(&quot;SheetName&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then &apos; Avoid errors when instance is watched in Basic IDE
+ If InStr(UCase(psProperty), &quot;CELL&quot;) &gt; 0 Then _PropertyGet = &quot;&quot; Else _PropertyGet = -1
+ Else
+ If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ Set oAddress = _ParseAddress(pvArg)
+ With oAddress.XCellRange
+ Select Case UCase(psProperty)
+ Case UCase(&quot;FirstCell&quot;)
+ _PropertyGet = A1Style(.RangeAddress.StartRow + 1, .RangeAddress.StartColumn + 1, , , oAddress.XSpreadsheet.Name)
+ Case UCase(&quot;FirstColumn&quot;) : _PropertyGet = CLng(.RangeAddress.StartColumn + 1)
+ Case UCase(&quot;FirstRow&quot;) : _PropertyGet = CLng(.RangeAddress.StartRow + 1)
+ Case UCase(&quot;LastCell&quot;)
+ _PropertyGet = A1Style(.RangeAddress.EndRow + 1, .RangeAddress.EndColumn + 1, , , oAddress.XSpreadsheet.Name)
+ Case UCase(&quot;LastColumn&quot;) : _PropertyGet = CLng(.RangeAddress.EndColumn + 1)
+ Case UCase(&quot;LastRow&quot;) : _PropertyGet = CLng(.RangeAddress.EndRow + 1)
+ Case UCase(&quot;SheetName&quot;) : _PropertyGet = oAddress.XSpreadsheet.Name
+ End Select
+ End With
+ End If
+ Case UCase(&quot;Range&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then
+ Set _PropertyGet = Nothing
+ Else
+ If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ Set _PropertyGet = _ParseAddress(pvArg)
+ End If
+ Case UCase(&quot;Region&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then
+ _PropertyGet = &quot;&quot;
+ Else
+ If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ Set oAddress = _ParseAddress(pvArg)
+ With oAddress
+ Set oCursor = .XSpreadsheet.createCursorByRange(.XCellRange)
+ oCursor.collapseToCurrentRegion()
+ _PropertyGet = oCursor.AbsoluteName
+ End With
+ End If
+ Case UCase(&quot;Sheet&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then
+ Set _PropertyGet = Nothing
+ Else
+ If Not _ValidateSheet(pvArg, &quot;SheetName&quot;, , True) Then GoTo Finally
+ Set _PropertyGet = _ParseAddress(pvArg)
+ End If
+ Case UCase(&quot;Sheets&quot;)
+ _PropertyGet = _Component.getSheets.getElementNames()
+ Case UCase(&quot;Width&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then
+ _PropertyGet = 0
+ Else
+ If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ _PropertyGet = _ParseAddress(pvArg).Width
+ End If
+ Case UCase(&quot;XCellRange&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then
+ Set _PropertyGet = Nothing
+ Else
+ If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ Set _PropertyGet = _ParseAddress(pvArg).XCellRange
+ End If
+ Case UCase(&quot;XSheetCellCursor&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then
+ Set _PropertyGet = Nothing
+ Else
+ If Not ScriptForge.SF_Utils._Validate(pvArg, &quot;Range&quot;, V_STRING) Then GoTo Finally
+ Set oAddress = _ParseAddress(pvArg)
+ Set _PropertyGet = oAddress.XSpreadsheet.createCursorByRange(oAddress.XCellRange)
+ End If
+ Case UCase(&quot;XSpreadsheet&quot;)
+ If IsMissing(pvArg) Or IsEmpty(pvArg) Then
+ Set _PropertyGet = Nothing
+ Else
+ If Not _ValidateSheet(pvArg, &quot;SheetName&quot;, , True) Then GoTo Finally
+ Set _PropertyGet = _Component.getSheets.getByName(pvArg)
+ End If
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFDocuments.SF_Calc._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _QuoteSheetName(ByVal psSheetName As String) As String
+&apos;&apos;&apos; Return the given sheet name surrounded with single quotes
+&apos;&apos;&apos; when required to insert the sheet name into a Calc formula
+&apos;&apos;&apos; Enclosed single quotes are doubled
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psSheetName: the name to quote
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The quoted or unchanged sheet name
+
+Dim sSheetName As String &apos; Return value
+Dim i As Long
+
+Try:
+ &apos; Surround the sheet name with single quotes when required by the presence of single quotes
+ If InStr(psSheetName, &quot;&apos;&quot;) &gt; 0 Then
+ sSheetName = &quot;&apos;&quot; &amp; Replace(psSheetName, &quot;&apos;&quot;, &quot;&apos;&apos;&quot;) &amp; &quot;&apos;&quot;
+ Else
+ &apos; Surround the sheet name with single quotes when required by the presence of at least one of the special characters
+ sSheetName = psSheetName
+ For i = 1 To Len(cstSPECIALCHARS)
+ If InStr(sSheetName, Mid(cstSPECIALCHARS, i, 1)) &gt; 0 Then
+ sSheetName = &quot;&apos;&quot; &amp; sSheetName &amp; &quot;&apos;&quot;
+ Exit For
+ End If
+ Next i
+ End If
+
+Finally:
+ _QuoteSheetName = sSheetName
+ Exit Function
+End Function &apos; SFDocuments.SF_Calc._QuoteSheetName
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the SF_Calc 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;[Calc]: &quot; &amp; [_Super]._FileIdent()
+
+End Function &apos; SFDocuments.SF_Calc._Repr
+
+REM -----------------------------------------------------------------------------
+Private Sub _RestoreSelections(ByRef pvComponent As Variant _
+ , ByRef pvSelection As Variant _
+ )
+&apos;&apos;&apos; Set the selection to a single or a multiple range
+&apos;&apos;&apos; Does not work well when multiple selections and macro terminating in Basic IDE
+&apos;&apos;&apos; Called by the CopyToCell and CopyToRange methods
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pvComponent: should work for foreign instances as well
+&apos;&apos;&apos; pvSelection: the stored selection done previously by Component.CurrentController.getSelection()
+
+Dim oCellRanges As Object &apos; com.sun.star.sheet.SheetCellRanges
+Dim vRangeAddresses As Variant &apos; Array of com.sun.star.table.CellRangeAddress
+Dim i As Long
+
+Try:
+ If IsArray(pvSelection) Then
+ Set oCellRanges = pvComponent.createInstance(&quot;com.sun.star.sheet.SheetCellRanges&quot;)
+ vRangeAddresses = Array()
+ ReDim vRangeAddresses(0 To UBound(pvSelection))
+ For i = 0 To UBound(pvSelection)
+ vRangeAddresses(i) = pvSelection.getByIndex(i).RangeAddress
+ Next i
+ oCellRanges.addRangeAddresses(vRangeAddresses, False)
+ pvComponent.CurrentController.select(oCellRanges)
+ Else
+ pvComponent.CurrentController.select(pvSelection)
+ End If
+
+Finally:
+ Exit Sub
+End Sub &apos; SFDocuments.SF_Calc._RestoreSelections
+
+REM -----------------------------------------------------------------------------
+Private Function _ValidateSheet(Optional ByRef pvSheetName As Variant _
+ , Optional ByVal psArgName As String _
+ , Optional ByVal pvNew As Variant _
+ , Optional ByVal pvActive As Variant _
+ , Optional ByVal pvOptional as Variant _
+ , Optional ByVal pvNumeric As Variant _
+ , Optional ByVal pvReference As Variant _
+ , Optional ByVal pvResetSheet As Variant _
+ ) As Boolean
+&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)
+&apos;&apos;&apos; pvNumeric: if True, the sheet position is accepted (default = False)
+&apos;&apos;&apos; pvReference: if True, a sheet reference is acceptable (default = False)
+&apos;&apos;&apos; pvNumeric and pvReference must not both be = True
+&apos;&apos;&apos; pvResetSheet: if True, return in pvSheetName the correct (case-sensitive) sheet name (default = False)
+&apos;&apos;&apos; Returns
+&apos;&apos;&apos; True if valid. SheetName is reset to current value if = &quot;~&quot;
+&apos;&apos;&apos; Exceptions
+&apos;&apos;&apos; DUPLICATESHEETERROR A sheet with the given name exists already
+
+Dim vSheets As Variant &apos; List of sheets
+Dim sSheet As String &apos; Sheet name without single quotes
+Dim lSheet As Long &apos; Index in list of sheets
+Dim vTypes As Variant &apos; Array of accepted variable types
+Dim bValid As Boolean &apos; Return value
+
+Check:
+ If IsMissing(pvNew) Or IsEmpty(pvNew) Then pvNew = False
+ If IsMissing(pvActive) Or IsEmpty(pvActive) Then pvActive = False
+ If IsMissing(pvOptional) Or IsEmpty(pvOptional) Then pvOptional = False
+ If IsMissing(pvNumeric) Or IsEmpty(pvNumeric) Then pvNumeric = False
+ If IsMissing(pvReference) Or IsEmpty(pvReference) Then pvReference = False
+ If IsMissing(pvResetSheet) Or IsEmpty(pvResetSheet) Then pvResetSheet = False
+
+ &apos; Define the acceptable variable types
+ If pvNumeric Then
+ vTypes = Array(V_STRING, V_NUMERIC)
+ ElseIf pvReference Then
+ vTypes = Array(V_STRING, ScriptForge.V_OBJECT)
+ Else
+ vTypes = V_STRING
+ End If
+ If Not ScriptForge.SF_Utils._Validate(pvSheetName, psArgName, vTypes, , , Iif(pvReference, CALCREFERENCE, &quot;&quot;)) Then GoTo Finally
+ bValid = False
+
+Try:
+ If VarType(pvSheetName) = V_STRING Then
+ If pvOptional And Len(pvSheetName) = 0 Then
+ ElseIf pvActive And pvSheetName = &quot;~&quot; Then
+ pvSheetName = _Component.CurrentController.ActiveSheet.Name
+ Else
+ vSheets = _Component.getSheets.getElementNames()
+ sSheet = Replace(pvSheetName, &quot;&apos;&quot;, &quot;&quot;)
+ If pvNew Then
+ If ScriptForge.SF_Array.Contains(vSheets, sSheet) Then GoTo CatchDuplicate
+ Else
+ If Not ScriptForge.SF_Utils._Validate(sSheet, psArgName, V_STRING, vSheets) Then GoTo Finally
+ If pvResetSheet Then
+ lSheet = ScriptForge.SF_Array.IndexOf(vSheets, sSheet, CaseSensitive := False)
+ pvSheetName = vSheets(lSheet)
+ End If
+ End If
+ End If
+ End If
+ bValid = True
+
+Finally:
+ _ValidateSheet = bValid
+ Exit Function
+CatchDuplicate:
+ ScriptForge.SF_Exception.RaiseFatal(DUPLICATESHEETERROR, psArgName, pvSheetName, &quot;Document&quot;, [_Super]._FileIdent())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Calc._ValidateSheet
+
+REM ============================================ END OF SFDOCUMENTS.SF_CALC
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Chart.xba b/wizards/source/sfdocuments/SF_Chart.xba
new file mode 100644
index 000000000..0538fb8af
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_Chart.xba
@@ -0,0 +1,814 @@
+<?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_Chart" 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_Chart
+&apos;&apos;&apos; ========
+&apos;&apos;&apos;
+&apos;&apos;&apos; The SF_Chart module is focused on the description of chart documents
+&apos;&apos;&apos; stored in Calc sheets.
+&apos;&apos;&apos; With this service, many chart types and chart characteristics available
+&apos;&apos;&apos; in the user interface can be read or modified.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Definitions
+&apos;&apos;&apos; Charts have 2 distinct names:
+&apos;&apos;&apos; - an internal name, given by the LibreOffice application
+&apos;&apos;&apos; - an optional user-defined name
+&apos;&apos;&apos; In the scope of the ScriptForge libraries, the chart name is the name given by the user.
+&apos;&apos;&apos; Only when there is no user name, the internal name may be used instead.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocation from the &quot;Calc&quot; service
+&apos;&apos;&apos; Either make a new chart
+&apos;&apos;&apos; calc.CreateChart(ChartName, SheetName, &quot;SheetX.A1:C8&quot;)
+&apos;&apos;&apos; or select an existing one
+&apos;&apos;&apos; calc.Charts(SheetName, ChartName)
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_chart.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 CHARTEXPORTERROR = &quot;CHARTEXPORTERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object &apos; Parent Calc document
+Private ObjectType As String &apos; Must be CHART
+Private ServiceName As String
+
+&apos; Chart description
+Private _SheetName As String &apos; Name of the Calc sheet containing the chart
+Private _DrawIndex As Long &apos; Index of the chart in the sheet&apos;s draw page
+Private _ChartName As String &apos; User name
+Private _PersistentName As String &apos; Internal name
+Private _Shape As Object &apos; com.sun.star.drawing.XShape
+Private _Chart As Object &apos; com.sun.star.table.XTableChart
+Private _ChartObject As Object &apos; com.sun.star.lang.XComponent - ScChartObj
+Private _Diagram As Object &apos; com.sun.star.chart.XDiagram
+
+REM ============================================================ MODULE CONSTANTS
+
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ ObjectType = &quot;CHART&quot;
+ ServiceName = &quot;SFDocuments.Chart&quot;
+ _SheetName = &quot;&quot;
+ _DrawIndex = -1
+ _ChartName = &quot;&quot;
+ _PersistentName = &quot;&quot;
+ Set _Shape = Nothing
+ Set _Chart = Nothing
+ Set _ChartObject = Nothing
+ Set _Diagram = Nothing
+End Sub &apos; SFDocuments.SF_Chart Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDocuments.SF_Chart Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDocuments.SF_Chart Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get ChartType() As Variant
+&apos;&apos;&apos; The ChartType property specifies the type of chart as a string among next values:
+&apos;&apos;&apos; Pie, Bar, Donut, Column, Area, Line, XY, Bubble, Net
+ ChartType = _PropertyGet(&quot;ChartType&quot;)
+End Property &apos; SFDocuments.SF_Chart.ChartType (get)
+
+REM -----------------------------------------------------------------------------
+Property Let ChartType(Optional ByVal pvChartType As Variant)
+&apos;&apos;&apos; Set the updatable property ChartType
+ _PropertySet(&quot;ChartType&quot;, pvChartType)
+End Property &apos; SFDocuments.SF_Chart.ChartType (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Deep() As Variant
+&apos;&apos;&apos; If True, determines that in a three-dimensional bar chart the bars of each series are arranged behind each other in the z-direction.
+&apos;&apos;&apos; If False the arrangement of bars is like in two-dimensional bar charts.
+&apos;&apos;&apos; Bar and Column chart types only
+ Deep = _PropertyGet(&quot;Deep&quot;)
+End Property &apos; SFDocuments.SF_Chart.Deep (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Deep(Optional ByVal pvDeep As Variant)
+&apos;&apos;&apos; Set the updatable property Deep
+ _PropertySet(&quot;Deep&quot;, pvDeep)
+End Property &apos; SFDocuments.SF_Chart.Deep (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Dim3D() As Variant
+&apos;&apos;&apos; The Dim3D property specifies if the chart is displayed with 3D elements
+&apos;&apos;&apos; String or Boolean
+&apos;&apos;&apos; When String, must be 1 of next values: Bar, Cylinder, Cone or Pyramid
+&apos;&apos;&apos; When Boolean True, Bar is assumed; when False, no 3D to be applied
+ Dim3D = _PropertyGet(&quot;Dim3D&quot;)
+End Property &apos; SFDocuments.SF_Chart.Dim3D (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Dim3D(Optional ByVal pvDim3D As Variant)
+&apos;&apos;&apos; Set the updatable property Dim3D
+ _PropertySet(&quot;Dim3D&quot;, pvDim3D)
+End Property &apos; SFDocuments.SF_Chart.Dim3D (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Exploded() As Variant
+&apos;&apos;&apos; the offset by which pie segments in a PieDiagram (pie or donut) are dragged outside from the center.
+&apos;&apos;&apos; This value is given in percent of the radius.
+ Exploded = _PropertyGet(&quot;Exploded&quot;)
+End Property &apos; SFDocuments.SF_Chart.Exploded (get)_ChartObject
+
+REM -----------------------------------------------------------------------------
+Property Let Exploded(Optional ByVal pvExploded As Variant)
+&apos;&apos;&apos; Set the updatable property Exploded
+ _PropertySet(&quot;Exploded&quot;, pvExploded)
+End Property &apos; SFDocuments.SF_Chart.Exploded (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Filled() As Variant
+&apos;&apos;&apos; When True, the Net diagram is said of FilledNet type
+&apos;&apos;&apos; Net chart type only
+ Filled = _PropertyGet(&quot;Filled&quot;)
+End Property &apos; SFDocuments.SF_Chart.Filled (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Filled(Optional ByVal pvFilled As Variant)
+&apos;&apos;&apos; Set the updatable property Filled
+ _PropertySet(&quot;Filled&quot;, pvFilled)
+End Property &apos; SFDocuments.SF_Chart.Filled (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Legend() As Variant
+&apos;&apos;&apos; Specifies if the chart has a legend
+ Legend = _PropertyGet(&quot;Legend&quot;)
+End Property &apos; SFDocuments.SF_Chart.Legend (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Legend(Optional ByVal pvLegend As Variant)
+&apos;&apos;&apos; Set the updatable property Legend
+ _PropertySet(&quot;Legend&quot;, pvLegend)
+End Property &apos; SFDocuments.SF_Chart.Legend (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Percent() As Variant
+&apos;&apos;&apos; When True, the series of the diagram are stacked and each category sums up to 100%.
+&apos;&apos;&apos; Area, Bar, Bubble, Column and Net chart types only_ChartObject
+ Percent = _PropertyGet(&quot;Percent&quot;)
+End Property &apos; SFDocuments.SF_Chart.Percent (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Percent(Optional ByVal pvPercent As Variant)
+&apos;&apos;&apos; Set the updatable property Percent
+ _PropertySet(&quot;Percent&quot;, pvPercent)
+End Property &apos; SFDocuments.SF_Chart.Percent (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Stacked() As Variant
+&apos;&apos;&apos; When True, the series of the diagram are stacked.
+&apos;&apos;&apos; Area, Bar, Bubble, Column and Net chart types only
+ Stacked = _PropertyGet(&quot;Stacked&quot;)
+End Property &apos; SFDocuments.SF_Chart.Stacked (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Stacked(Optional ByVal pvStacked As Variant)
+&apos;&apos;&apos; Set the updatable property Stacked
+ _PropertySet(&quot;Stacked&quot;, pvStacked)
+End Property &apos; SFDocuments.SF_Chart.Stacked (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Title() As Variant
+&apos;&apos;&apos; Specifies the main title of the chart
+ Title = _PropertyGet(&quot;Title&quot;)
+End Property &apos; SFDocuments.SF_Chart.Title (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Title(Optional ByVal pvTitle As Variant)
+&apos;&apos;&apos; Set the updatable property Title
+ _PropertySet(&quot;Title&quot;, pvTitle)
+End Property &apos; SFDocuments.SF_Chart.Title (let)
+
+REM -----------------------------------------------------------------------------
+Property Get XTitle() As Variant
+&apos;&apos;&apos; Specifies the main XTitle of the chart
+ XTitle = _PropertyGet(&quot;XTitle&quot;)
+End Property &apos; SFDocuments.SF_Chart.XTitle (get)
+
+REM -----------------------------------------------------------------------------
+Property Let XTitle(Optional ByVal pvXTitle As Variant)
+&apos;&apos;&apos; Set the updatable property XTitle
+ _PropertySet(&quot;XTitle&quot;, pvXTitle)
+End Property &apos; SFDocuments.SF_Chart.XTitle (let)
+
+REM -----------------------------------------------------------------------------
+Property Get YTitle() As Variant
+&apos;&apos;&apos; Specifies the main YTitle of the chart
+ YTitle = _PropertyGet(&quot;YTitle&quot;)
+End Property &apos; SFDocuments.SF_Chart.YTitle (get)
+
+REM -----------------------------------------------------------------------------
+Property Let YTitle(Optional ByVal pvYTitle As Variant)
+&apos;&apos;&apos; Set the updatable property YTitle
+ _PropertySet(&quot;YTitle&quot;, pvYTitle)
+End Property &apos; SFDocuments.SF_Chart.YTitle (let)
+
+REM -----------------------------------------------------------------------------
+Property Get XChartObj() As Variant
+&apos;&apos;&apos; com.sun.star.lang.XComponent - ScChartObj
+ ChartType = _PropertyGet(&quot;XChartObj&quot;)
+End Property &apos; SFDocuments.SF_Chart.XChartObj (get)
+
+REM -----------------------------------------------------------------------------
+Property Get XDiagram() As Variant
+&apos;&apos;&apos; com.sun.star.chart.XDiagram
+ ChartType = _PropertyGet(&quot;XDiagram&quot;)
+End Property &apos; SFDocuments.SF_Chart.XDiagram (get)
+
+REM -----------------------------------------------------------------------------
+Property Get XShape() As Variant
+&apos;&apos;&apos; com.sun.star.drawing.XShape
+ ChartType = _PropertyGet(&quot;XShape&quot;)
+End Property &apos; SFDocuments.SF_Chart.XShape (get)
+
+REM -----------------------------------------------------------------------------
+Property Get XTableChart() As Variant
+&apos;&apos;&apos; com.sun.star.table.XTableChart
+ ChartType = _PropertyGet(&quot;XTableChart&quot;)
+End Property &apos; SFDocuments.SF_Chart.XTableChart (get)
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function ExportToFile(Optional ByVal FileName As Variant _
+ , Optional ByVal ImageType As Variant _
+ , Optional ByVal Overwrite As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Store the chart as an image to the given file location
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FileName: Identifies the file where to save. It must follow the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; ImageType: the name of the targeted image type
+&apos;&apos;&apos; Allowed values: gif, jpeg, png (default), svg and tiff
+&apos;&apos;&apos; Overwrite: True if the destination file may be overwritten (default = False)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False if the document could not be saved
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; CHARTEXPORTERROR The destination has its readonly attribute set or overwriting rejected
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oChart.ExportToFile(&quot;C:\Me\Chart2.gif&quot;, ImageType := &quot;gif&quot;, Overwrite := True)
+
+Dim bSaved As Boolean &apos; return value
+Dim oSfa As Object &apos; com.sun.star.ucb.SimpleFileAccess
+Dim sFile As String &apos; Alias of FileName
+Dim vStoreArguments As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim FSO As Object &apos; SF_FileSystem
+Dim oExport As Object &apos; com.sun.star.drawing.GraphicExportFilter
+Dim vImageTypes As Variant &apos; Array of permitted image types
+Dim vMimeTypes As Variant &apos; Array of corresponding mime types in the same order as vImageTypes
+
+Const cstImageTypes = &quot;gif,jpeg,png,svg,tiff&quot;
+Const cstMimeTypes = &quot;image/gif,image/jpeg,image/png,image/svg+xml,image/tiff&quot;
+
+Const cstThisSub = &quot;SFDocuments.Chart.ExportToFile&quot;
+Const cstSubArgs = &quot;FileName, [ImageType=&quot;&quot;png&quot;&quot;|&quot;&quot;gif&quot;&quot;|&quot;&quot;jpeg&quot;&quot;|&quot;&quot;svg&quot;&quot;|&quot;&quot;tiff&quot;&quot;], [Overwrite=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo CatchError
+ bSaved = False
+
+Check:
+ If IsMissing(ImageType) Or IsEmpty(ImageType) Then ImageType = &quot;png&quot;
+ If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = False
+
+ vImageTypes = Split(cstImageTypes, &quot;,&quot;)
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not [_Parent]._IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ImageType, &quot;ImageType&quot;, V_STRING, vImageTypes) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+ &apos; Check destination file overwriting
+ Set FSO = CreateScriptService(&quot;FileSystem&quot;)
+ sFile = FSO._ConvertToUrl(FileName)
+ If FSO.FileExists(FileName) Then
+ If Overwrite = False Then GoTo CatchError
+ Set oSfa = ScriptForge.SF_Utils._GetUNOService(&quot;FileAccess&quot;)
+ If oSfa.isReadonly(sFile) Then GoTo CatchError
+ End If
+
+Try:
+ &apos; Setup arguments
+ vMimeTypes = Split(cstMimeTypes, &quot;,&quot;)
+ vStoreArguments = Array( _
+ ScriptForge.SF_Utils._MakePropertyValue(&quot;URL&quot;, sFile) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;MediaType&quot; _
+ , vMimeTypes(ScriptForge.SF_Array.IndexOf(vImageTypes, ImageType, CaseSensitive := False))) _
+ )
+ &apos; Export with the com.sun.star.drawing.GraphicExportFilter UNO service
+ Set oExport = ScriptForge.SF_Utils._GetUNOService(&quot;GraphicExportFilter&quot;)
+ With oExport
+ .setSourceDocument(_Shape)
+ .filter(vStoreArguments)
+ End With
+ bSaved = True
+
+Finally:
+ ExportToFile = bSaved
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchError:
+ ScriptForge.SF_Exception.RaiseFatal(CHARTEXPORTERROR, &quot;FileName&quot;, FileName, &quot;Overwrite&quot;, Overwrite)
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Chart.ExportToFile
+
+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; If the property does not exist, returns Null
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; ARGUMENTERROR The property does not exist
+
+Const cstThisSub = &quot;SFDocuments.Chart.GetProperty&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ GetProperty = Null
+
+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:
+ GetProperty = _PropertyGet(PropertyName)
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Chart.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Chart service as an array
+
+ Methods = Array( _
+ &quot;ExportToFile&quot; _
+ , &quot;Resize&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Chart.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Chart class as an array
+
+ Properties = Array( _
+ &quot;ChartType&quot; _
+ , &quot;Deep&quot; _
+ , &quot;Dim3D&quot; _
+ , &quot;Exploded&quot; _
+ , &quot;Filled&quot; _
+ , &quot;Legend&quot; _
+ , &quot;Percent&quot; _
+ , &quot;Stacked&quot; _
+ , &quot;Title&quot; _
+ , &quot;XChartObj&quot; _
+ , &quot;XDiagram&quot; _
+ , &quot;XShape&quot; _
+ , &quot;XTableChart&quot; _
+ , &quot;XTitle&quot; _
+ , &quot;YTitle&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Chart.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function Resize(Optional ByVal XPos As Variant _
+ , Optional ByVal YPos As Variant _
+ , Optional ByVal Width As Variant _
+ , Optional ByVal Height As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Move the topleft corner of a chart to new coordinates and/or modify its dimensions
+&apos;&apos;&apos; All distances are expressed in 1/100th mm
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; XPos : the vertical distance from the topleft corner
+&apos;&apos;&apos; YPos : the horizontal distance from the topleft corner
+&apos;&apos;&apos; Width : the horizontal width of the shape containing the chart
+&apos;&apos;&apos; Height : the vertical height of the shape containing the chart
+&apos;&apos;&apos; Negative or missing arguments are left unchanged
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oChart.Resize(1000, 2000, Height := 6000) &apos; Width is not changed
+
+Dim bResize As Boolean &apos; Return value
+Dim oPosition As Object &apos; com.sun.star.awt.Point
+Dim oSize As Object &apos; com.sun.star.awt.Size
+Const cstThisSub = &quot;SFDocuments.Chart.Resize&quot;
+Const cstSubArgs = &quot;[XPos], [YPos], [Width], [Height]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bResize = False
+
+Check:
+ If IsMissing(XPos) Or IsEmpty(XPos) Then XPos = -1
+ If IsMissing(YPos) Or IsEmpty(YPos) Then YPos = -1
+ If IsMissing(Height) Or IsEmpty(Height) Then Height = -1
+ If IsMissing(Width) Or IsEmpty(Width) Then Width = -1
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not [_Parent]._IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(XPos, &quot;XPos&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(YPos, &quot;YPos&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Width, &quot;Width&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Height, &quot;Height&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+
+Try:
+ With _Shape
+ &apos; Get the current values
+ Set oPosition = .Position
+ Set oSize = .Size
+ &apos; Modify relevant elements
+ If XPos &gt;= 0 Then oPosition.X = CLng(XPos)
+ If YPos &gt;= 0 Then oPosition.Y = CLng(YPos)
+ If Width &gt; 0 Then oSize.Width = CLng(Width)
+ If Height &gt; 0 Then oSize.Height = CLng(Height)
+ &apos; Rewrite
+ .setPosition(oPosition)
+ .setSize(oSize)
+ End With
+ bResize = True
+
+Finally:
+ Resize = bResize
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SF_Documents.SF_Chart.Resize
+
+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.Chart.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 ScriptForge.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_Chart.SetProperty
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+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 vData As Variant &apos; Data points array of values
+
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ cstThisSub = &quot;SFDocuments.Chart.get&quot; &amp; psProperty
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not [_Parent]._IsStillAlive() Then GoTo Finally
+
+ If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
+ Select Case UCase(psProperty)
+ Case UCase(&quot;ChartType&quot;)
+ With _Diagram
+ Select Case .DiagramType
+ Case &quot;com.sun.star.chart.BarDiagram&quot;
+ If .Vertical Then _PropertyGet = &quot;Bar&quot; Else _PropertyGet = &quot;Column&quot;
+ Case &quot;com.sun.star.chart.PieDiagram&quot;
+ _PropertyGet = &quot;Pie&quot;
+ Case &quot;com.sun.star.chart.DonutDiagram&quot;
+ _PropertyGet = &quot;Donut&quot;
+ Case &quot;com.sun.star.chart.AreaDiagram&quot;
+ _PropertyGet = &quot;Area&quot;
+ Case &quot;com.sun.star.chart.LineDiagram&quot;
+ _PropertyGet = &quot;Line&quot;
+ Case &quot;com.sun.star.chart.XYDiagram&quot;
+ _PropertyGet = &quot;XY&quot;
+ Case &quot;com.sun.star.chart.BubbleDiagram&quot;
+ _PropertyGet = &quot;Bubble&quot;
+ Case &quot;com.sun.star.chart.NetDiagram&quot;, &quot;com.sun.star.chart.FilledNetDiagram&quot;
+ _PropertyGet = &quot;Net&quot;
+ Case Else
+ _PropertyGet = &quot;&quot;
+ End Select
+ End With
+ Case UCase(&quot;Deep&quot;)
+ If oSession.HasUnoProperty(_Diagram, &quot;Deep&quot;) Then _PropertyGet = _Diagram.Deep Else _PropertyGet = False
+ Case UCase(&quot;Dim3D&quot;)
+ If oSession.HasUnoProperty(_Diagram, &quot;Dim3D&quot;) Then
+ If _Diagram.Dim3D Then
+ If oSession.HasUnoProperty(_Diagram, &quot;SolidType&quot;) Then
+ Select Case _Diagram.SolidType
+ Case com.sun.star.chart.ChartSolidType.RECTANGULAR_SOLID : _PropertyGet = &quot;Bar&quot;
+ Case com.sun.star.chart.ChartSolidType.CYLINDER : _PropertyGet = &quot;Cylinder&quot;
+ Case com.sun.star.chart.ChartSolidType.CONE : _PropertyGet = &quot;Cone&quot;
+ Case com.sun.star.chart.ChartSolidType.PYRAMID : _PropertyGet = &quot;Pyramid&quot;
+ End Select
+ Else
+ _PropertyGet = _Diagram.Dim3D
+ End If
+ Else
+ _PropertyGet = False
+ End If
+ Else
+ _PropertyGet = False
+ End If
+ Case UCase(&quot;Exploded&quot;)
+ If oSession.HasUnoProperty(_ChartObject, &quot;Data&quot;) Then
+ &apos; All data points are presumed exploded with the same coefficient. Determine the (0, 0)th
+ With _ChartObject
+ vData = .Data.Data
+ _PropertyGet = 0
+ If IsArray(vData) Then
+ If UBound(vData) &gt;= 0 Then
+ If IsArray(vData(0)) Then
+ If UBound(vData(0)) &gt;= 0 Then _PropertyGet = _Diagram.getDataPointProperties(0, 0).SegmentOffset
+ End If
+ End If
+ End If
+ End With
+ End If
+ Case UCase(&quot;Filled&quot;)
+ _PropertyGet = ( _Diagram.DiagramType = &quot;com.sun.star.chart.FilledNetDiagram&quot; )
+ Case UCase(&quot;Legend&quot;)
+ If oSession.HasUnoProperty(_ChartObject, &quot;HasLegend&quot;) Then _PropertyGet = _ChartObject.HasLegend Else _PropertyGet = False
+ Case UCase(&quot;Percent&quot;)
+ If oSession.HasUnoProperty(_Diagram, &quot;Percent&quot;) Then _PropertyGet = _Diagram.Percent Else _PropertyGet = False
+ Case UCase(&quot;Stacked&quot;)
+ If oSession.HasUnoProperty(_Diagram, &quot;Stacked&quot;) Then _PropertyGet = _Diagram.Stacked Else _PropertyGet = False
+ Case UCase(&quot;Title&quot;)
+ If oSession.HasUnoProperty(_ChartObject, &quot;HasMainTitle&quot;) Then
+ If _ChartObject.HasMainTitle Then _PropertyGet = _ChartObject.Title.String Else _PropertyGet = &quot;&quot;
+ End If
+ Case UCase(&quot;XTitle&quot;)
+ If oSession.HasUnoProperty(_Diagram, &quot;HasXAxisTitle&quot;) Then
+ If _Diagram.HasXAxisTitle Then _PropertyGet = _Diagram.XAxisTitle.String Else _PropertyGet = &quot;&quot;
+ End If
+ Case UCase(&quot;YTitle&quot;)
+ If oSession.HasUnoProperty(_Diagram, &quot;HasYAxisTitle&quot;) Then
+ If _Diagram.HasYAxisTitle Then _PropertyGet = _Diagram.YAxisTitle.String Else _PropertyGet = &quot;&quot;
+ End If
+ Case UCase(&quot;XChartObj&quot;)
+ Set _PropertyGet = _ChartObject
+ Case UCase(&quot;XDiagram&quot;)
+ Set _PropertyGet = _Diagram
+ Case UCase(&quot;XShape&quot;)
+ Set _PropertyGet = _Shape
+ Case UCase(&quot;XTableChart&quot;)
+ Set _PropertyGet = _Chart
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFDocuments.SF_Chart._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
+
+Dim bSet As Boolean &apos; Return value
+Static oSession As Object &apos; Alias of SF_Session
+Dim sChartType As String &apos; Diagram type
+Dim bDim3D As Boolean &apos; Alias of Dim3D property of diagram
+Dim bVertical As Boolean &apos; When True, chart type is a bar, not a column
+Dim vData As Variant &apos; Data points array of values
+Dim i As Long, j As Long
+Const cstChart = &quot;com.sun.star.chart.&quot;
+
+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.Chart.set&quot; &amp; psProperty
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not [_Parent]._IsStillAlive() Then GoTo Catch
+
+ bSet = True
+ If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
+ Select Case UCase(psProperty)
+ Case UCase(&quot;ChartType&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;ChartType&quot;, V_STRING _
+ , Array(&quot;Bar&quot;, &quot;Column&quot;, &quot;Pie&quot;, &quot;Donut&quot;, &quot;Area&quot;, &quot;Line&quot;, &quot;XY&quot;, &quot;Bubble&quot;, &quot;Net&quot;) _
+ ) Then GoTo Finally
+ With _Diagram
+ &apos; Specify the targeted chart type
+ Select Case UCase(pvValue)
+ Case &quot;BAR&quot;, &quot;COLUMN&quot; : sChartType = cstChart &amp; &quot;BarDiagram&quot;
+ Case &quot;PIE&quot; : sChartType = cstChart &amp; &quot;PieDiagram&quot;
+ Case &quot;DONUT&quot; : sChartType = cstChart &amp; &quot;DonutDiagram&quot;
+ Case &quot;AREA&quot; : sChartType = cstChart &amp; &quot;AreaDiagram&quot;
+ Case &quot;LINE&quot; : sChartType = cstChart &amp; &quot;LineDiagram&quot;
+ Case &quot;XY&quot; : sChartType = cstChart &amp; &quot;XYDiagram&quot;
+ Case &quot;BUBBLE&quot; : sChartType = cstChart &amp; &quot;BubbleDiagram&quot;
+ Case &quot;NET&quot; : sChartType = cstChart &amp; &quot;NetDiagram&quot;
+ End Select
+ &apos; If there is no change, do nothing
+ If sChartType &lt;&gt; .DiagramType Then
+ &apos; Some combinations old type =&gt; new type require the cancellation of 3D graphs
+ bDim3D = .Dim3D
+ .Dim3D = False
+ _ChartObject.createInstance(sChartType)
+ Set _Diagram = _ChartObject.Diagram
+ .Dim3D = bDim3D
+ End If
+ If UCase(pvValue) = &quot;BAR&quot; Or UCase(pvValue) = &quot;COLUMN&quot; Then .Vertical = ( UCase(pvValue) = &quot;BAR&quot; )
+ End With
+ Case UCase(&quot;Deep&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Deep&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_Diagram, &quot;Deep&quot;) Then _Diagram.Deep = pvValue
+ Case UCase(&quot;Dim3D&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Dim3D&quot;, Array(ScriptForge.V_Boolean, V_STRING) _
+ , Array(False, True, &quot;Bar&quot;, &quot;Cylinder&quot;, &quot;Cone&quot;, &quot;Pyramid&quot;) _
+ ) Then GoTo Finally
+ With _Diagram
+ If oSession.HasUnoProperty(_Diagram, &quot;Dim3D&quot;) Then
+ If _Diagram.DiagramType = &quot;com.sun.star.chart.BubbleDiagram&quot; Then
+ .Dim3D = False &apos; Force False value to avoid empty graph
+ ElseIf VarType(pvValue) = V_STRING Then
+ bVertical = .Vertical
+ .Dim3D = True
+ .Vertical = bVertical
+ If oSession.HasUnoProperty(_Diagram, &quot;SolidType&quot;) Then
+ If .DiagramType = cstChart &amp; &quot;BarDiagram&quot; Then
+ Select Case UCase(pvValue)
+ Case &quot;BAR&quot; : .SolidType = com.sun.star.chart.ChartSolidType.RECTANGULAR_SOLID
+ Case &quot;CYLINDER&quot; : .SolidType = com.sun.star.chart.ChartSolidType.CYLINDER
+ Case &quot;CONE&quot; : .SolidType = com.sun.star.chart.ChartSolidType.CONE
+ Case &quot;PYRAMID&quot; : .SolidType = com.sun.star.chart.ChartSolidType.PYRAMID
+ End Select
+ Else
+ .SolidType = 0
+ End If
+ End If
+ Else &apos; Boolean
+ If oSession.HasUnoProperty(_Diagram, &quot;SolidType&quot;) Then .SolidType = 0
+ .Dim3D = pvValue
+ End If
+ End If
+ End With
+ Case UCase(&quot;Exploded&quot;)
+ If oSession.HasUnoProperty(_ChartObject, &quot;Data&quot;) And _Diagram.DiagramType &lt;&gt; &quot;com.sun.star.chart.BubbleDiagram&quot; Then
+ &apos; All data points are presumed exploded with the same coefficient
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Exploded&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ With _ChartObject
+ vData = .Data.Data
+ If IsArray(vData) Then
+ For i = 0 To UBound(vData)
+ If IsArray(vData(i)) Then
+ For j = 0 To UBound(vData(i))
+ _Diagram.getDataPointProperties(i, j).SegmentOffset = CLng(pvValue)
+ Next j
+ End If
+ Next i
+ End If
+ End With
+ End If
+ Case UCase(&quot;Filled&quot;)
+ &apos; Flipflop between NetDiagram and FilledNetDiagram
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Filled&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ With _Diagram
+ &apos; Specify the targeted chart type
+ sChartType = cstChart &amp; Iif(pvValue, &quot;Filled&quot;, &quot;&quot;) &amp; &quot;NetDiagram&quot;
+ &apos; If there is no change, do nothing
+ If sChartType &lt;&gt; .DiagramType then
+ &apos; Do not apply if the chart type not = &quot;Net&quot;
+ If (pvValue And .DiagramType = cstChart &amp; &quot;NetDiagram&quot;) _
+ Or (Not pvValue And .DiagramType = cstChart &amp; &quot;FilledNetDiagram&quot;) Then
+ &apos; Some combinations old type =&gt; new type require the cancellation of 3D graphs
+ bDim3D = .Dim3D
+ .Dim3D = False
+ _ChartObject.createInstance(sChartType)
+ Set _Diagram = _ChartObject.Diagram
+ .Dim3D = bDim3D
+ End If
+ End If
+ End With
+ Case UCase(&quot;Legend&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Legend&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_ChartObject, &quot;HasLegend&quot;) Then _ChartObject.HasLegend = pvValue
+ Case UCase(&quot;Percent&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Percent&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_Diagram, &quot;Percent&quot;) Then
+ _Diagram.Stacked = pvValue
+ _Diagram.Percent = pvValue
+ End If
+ Case UCase(&quot;Stacked&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Stacked&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_Diagram, &quot;Stacked&quot;) Then
+ _Diagram.Stacked = pvValue
+ If Not pvValue Then _Diagram.Percent = False
+ End If
+ Case UCase(&quot;Title&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Title&quot;, V_STRING) Then GoTo Finally
+ If oSession.HasUnoProperty(_ChartObject, &quot;HasMainTitle&quot;) Then
+ _ChartObject.HasMainTitle = ( Len(pvValue) &gt; 0 )
+ If Len(pvValue) &gt; 0 Then _ChartObject.Title.String = pvValue
+ End If
+ Case UCase(&quot;XTitle&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;XTitle&quot;, V_STRING) Then GoTo Finally
+ If oSession.HasUnoProperty(_Diagram, &quot;HasXAxisTitle&quot;) Then
+ _Diagram.HasXAxisTitle = ( Len(pvValue) &gt; 0 )
+ If Len(pvValue) &gt; 0 Then _Diagram.XAxisTitle.String = pvValue
+ End If
+ Case UCase(&quot;YTitle&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;YTitle&quot;, V_STRING) Then GoTo Finally
+ If oSession.HasUnoProperty(_Diagram, &quot;HasYAxisTitle&quot;) Then
+ _Diagram.HasYAxisTitle = ( Len(pvValue) &gt; 0 )
+ If Len(pvValue) &gt; 0 Then _Diagram.YAxisTitle.String = pvValue
+ End If
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ _PropertySet = bSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bSet = False
+ GoTo Finally
+End Function &apos; SFDocuments.SF_FormControl._PropertySet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the Chart instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[Chart]: Name - Type
+
+ _Repr = &quot;[Chart]: &quot; &amp; ChartName &amp; &quot; - &quot; &amp; ChartType
+
+End Function &apos; SFDocuments.SF_Chart._Repr
+
+REM ============================================ END OF SFDOCUMENTS.SF_CHART
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Document.xba b/wizards/source/sfdocuments/SF_Document.xba
new file mode 100644
index 000000000..c54409445
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_Document.xba
@@ -0,0 +1,1504 @@
+<?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_Document" 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_Document
+&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
+&apos;&apos;&apos; current SF_Document module
+&apos;&apos;&apos; - saving, closing documents
+&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_Base, ...
+&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
+&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 current module is closely related to the &quot;UI&quot; and &quot;FileSystem&quot; services
+&apos;&apos;&apos; of the ScriptForge library
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocation examples:
+&apos;&apos;&apos; 1) From the UI service
+&apos;&apos;&apos; Dim ui As Object, oDoc As Object
+&apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
+&apos;&apos;&apos; Set oDoc = ui.GetDocument(&quot;Untitled 1&quot;)
+&apos;&apos;&apos; &apos; or Set oDoc = ui.CreateDocument(&quot;Calc&quot;, ...)
+&apos;&apos;&apos; &apos; or Set oDoc = ui.OpenDocument(&quot;C:\Me\MyFile.odt&quot;)
+&apos;&apos;&apos; 2) Directly if the document is already opened
+&apos;&apos;&apos; Dim oDoc As Object
+&apos;&apos;&apos; Set oDoc = CreateScriptService(&quot;SFDocuments.Document&quot;, &quot;Untitled 1&quot;) &apos; Default = ActiveWindow
+&apos;&apos;&apos; &apos; The substring &quot;SFDocuments.&quot; in the service name is optional
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_document.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 DOCUMENTDEADERROR = &quot;DOCUMENTDEADERROR&quot;
+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
+
+&apos; Window description
+Private _Component As Object &apos; com.sun.star.lang.XComponent
+Private _Frame As Object &apos; com.sun.star.comp.framework.Frame
+Private _WindowName As String &apos; Object Name
+Private _WindowTitle As String &apos; Only mean to identify new documents
+Private _WindowFileName As String &apos; URL of file name
+Private _DocumentType As String &apos; Writer, Calc, ...
+
+&apos; Properties (work variables - real properties could have been set manually by user)
+Private _DocumentProperties As Object &apos; Dictionary of document properties
+Private _CustomProperties As Object &apos; Dictionary of custom properties
+
+REM ============================================================ MODULE CONSTANTS
+
+Const ISDOCFORM = 1 &apos; Form is stored in a 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
+ Set _Frame = Nothing
+ _WindowName = &quot;&quot;
+ _WindowTitle = &quot;&quot;
+ _WindowFileName = &quot;&quot;
+ _DocumentType = &quot;&quot;
+ Set _DocumentProperties = Nothing
+ Set _CustomProperties = Nothing
+End Sub &apos; SFDocuments.SF_Document Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDocuments.SF_Document Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDocuments.SF_Document Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get CustomProperties() As Variant
+&apos;&apos;&apos; Returns a dictionary of all custom properties of the document
+ CustomProperties = _PropertyGet(&quot;CustomProperties&quot;)
+End Property &apos; SFDocuments.SF_Document.CustomProperties
+
+REM -----------------------------------------------------------------------------
+Property Let CustomProperties(Optional ByVal pvCustomProperties As Variant)
+&apos;&apos;&apos; Sets the updatable custom properties
+&apos;&apos;&apos; The argument is a dictionary
+
+Dim vPropertyValues As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim vCustomProperties As Variant &apos; Alias of argument
+Dim oUserdefinedProperties As Object &apos; Custom properties object
+Dim vOldPropertyValues As Variant &apos; Array of (to remove) existing user defined properties
+Dim oProperty As Object &apos; Single com.sun.star.beans.PropertyValues
+Dim sProperty As String &apos; Property name
+Dim vKeys As Variant &apos; Array of dictionary keys
+Dim vItems As Variant &apos; Array of dictionary items
+Dim vValue As Variant &apos; Value to store in property
+Dim iAttribute As Integer &apos; com.sun.star.beans.PropertyAttribute.REMOVEABLE
+Dim i As Long
+Const cstThisSub = &quot;SFDocuments.Document.setCustomProperties&quot;
+Const cstSubArgs = &quot;CustomProperties&quot;
+
+ On Local Error GoTo Catch
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(pvCustomProperties, &quot;CustomProperties&quot;, ScriptForge.V_OBJECT, , , &quot;DICTIONARY&quot;) Then GoTo Finally
+ End If
+
+Try:
+ Set oUserDefinedProperties = _Component.getDocumentProperties().UserDefinedProperties
+
+ Set vCustomProperties = pvCustomProperties &apos; To avoid &quot;Object variable not set&quot; error
+ With vCustomProperties
+
+ &apos; All existing custom properties must first be removed to avoid type conflicts
+ vOldPropertyValues = oUserDefinedProperties.getPropertyValues
+ For Each oProperty In vOldPropertyValues
+ sProperty = oProperty.Name
+ oUserDefinedProperties.removeProperty(sProperty)
+ Next oProperty
+
+ &apos; Insert new properties one by one after type adjustment (dates, arrays, numbers)
+ vKeys = .Keys
+ vItems = .Items
+ iAttribute = com.sun.star.beans.PropertyAttribute.REMOVEABLE
+ For i = 0 To UBound(vKeys)
+ If VarType(vItems(i)) = V_DATE Then
+ vValue = ScriptForge.SF_Utils._CDateToUnoDate(vItems(i))
+ ElseIf IsArray(vItems(i)) Then
+ vValue = Null
+ ElseIf ScriptForge.SF_Utils._VarTypeExt(vItems(i)) = ScriptForge.V_NUMERIC Then
+ vValue = CreateUnoValue(&quot;double&quot;, vItems(i))
+ Else
+ vValue = vItems(i)
+ End If
+ oUserDefinedProperties.addProperty(vKeys(i), iAttribute, vValue)
+ Next i
+
+ &apos; Declare the document as changed
+ _Component.setModified(True)
+ End With
+
+ &apos; Reload custom properties in current object instance
+ _PropertyGet(&quot;CustomProperties&quot;)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Property
+Catch:
+ GoTo Finally
+End Property &apos; SFDocuments.SF_Document.CustomProperties
+
+REM -----------------------------------------------------------------------------
+Property Get Description() As Variant
+&apos;&apos;&apos; Returns the updatable document property Description
+ Description = _PropertyGet(&quot;Description&quot;)
+End Property &apos; SFDocuments.SF_Document.Description
+
+REM -----------------------------------------------------------------------------
+Property Let Description(Optional ByVal pvDescription As Variant)
+&apos;&apos;&apos; Sets the updatable document property Description
+&apos;&apos;&apos; If multilined, separate lines by &quot;\n&quot; escape sequence or by hard breaks
+
+Dim sDescription As String &apos; Alias of pvDescription
+Const cstThisSub = &quot;SFDocuments.Document.setDescription&quot;
+Const cstSubArgs = &quot;Description&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(pvDescription, &quot;Description&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Update in UNO component object and in current instance
+ sDescription = Replace(pvDescription, &quot;\n&quot;, ScriptForge.SF_String.sfNEWLINE)
+ _Component.DocumentProperties.Description = sDescription
+ If Not IsNull(_DocumentProperties) Then _DocumentProperties.ReplaceItem(&quot;Description&quot;, sdescription)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Property
+End Property &apos; SFDocuments.SF_Document.Description
+
+REM -----------------------------------------------------------------------------
+Property Get DocumentProperties() As Variant
+&apos;&apos;&apos; Returns a dictionary of all standard document properties, custom properties are excluded
+ DocumentProperties = _PropertyGet(&quot;DocumentProperties&quot;)
+End Property &apos; SFDocuments.SF_Document.DocumentProperties
+
+REM -----------------------------------------------------------------------------
+Property Get DocumentType() As String
+&apos;&apos;&apos; Returns &quot;Base&quot;, &quot;Calc&quot;, &quot;Draw&quot;, ... or &quot;Writer&quot;
+ DocumentType = _PropertyGet(&quot;DocumentType&quot;)
+End Property &apos; SFDocuments.SF_Document.DocumentType
+
+REM -----------------------------------------------------------------------------
+Property Get ExportFilters() As Variant
+&apos;&apos;&apos; Returns the list of the export filter names applicable to the current document
+&apos;&apos;&apos; as a zero-based array of strings
+&apos;&apos;&apos; Import/Export filters are included
+ ExportFilters = _PropertyGet(&quot;ExportFilters&quot;)
+End Property &apos; SFDocuments.SF_Document.ExportFilters
+
+REM -----------------------------------------------------------------------------
+Property Get ImportFilters() As Variant
+&apos;&apos;&apos; Returns the list of the import filter names applicable to the current document
+&apos;&apos;&apos; as a zero-based array of strings
+&apos;&apos;&apos; Import/Export filters are included
+ ImportFilters = _PropertyGet(&quot;ImportFilters&quot;)
+End Property &apos; SFDocuments.SF_Document.ImportFilters
+
+REM -----------------------------------------------------------------------------
+Property Get IsBase() As Boolean
+ IsBase = _PropertyGet(&quot;IsBase&quot;)
+End Property &apos; SFDocuments.SF_Document.IsBase
+
+REM -----------------------------------------------------------------------------
+Property Get IsCalc() As Boolean
+ IsCalc = _PropertyGet(&quot;IsCalc&quot;)
+End Property &apos; SFDocuments.SF_Document.IsCalc
+
+REM -----------------------------------------------------------------------------
+Property Get IsDraw() As Boolean
+ IsDraw = _PropertyGet(&quot;IsDraw&quot;)
+End Property &apos; SFDocuments.SF_Document.IsDraw
+
+REM -----------------------------------------------------------------------------
+Property Get IsImpress() As Boolean
+ IsImpress = _PropertyGet(&quot;IsImpress&quot;)
+End Property &apos; SFDocuments.SF_Document.IsImpress
+
+REM -----------------------------------------------------------------------------
+Property Get IsMath() As Boolean
+ IsMath = _PropertyGet(&quot;IsMath&quot;)
+End Property &apos; SFDocuments.SF_Document.IsMath
+
+REM -----------------------------------------------------------------------------
+Property Get IsWriter() As Boolean
+ IsWriter = _PropertyGet(&quot;IsWriter&quot;)
+End Property &apos; SFDocuments.SF_Document.IsWriter
+
+REM -----------------------------------------------------------------------------
+Property Get Keywords() As Variant
+&apos;&apos;&apos; Returns the updatable document property Keywords
+ Keywords = _PropertyGet(&quot;Keywords&quot;)
+End Property &apos; SFDocuments.SF_Document.Keywords
+
+REM -----------------------------------------------------------------------------
+Property Let Keywords(Optional ByVal pvKeywords As Variant)
+&apos;&apos;&apos; Sets the updatable document property Keywords
+
+Dim vKeywords As Variant &apos; Alias of pvKeywords
+Const cstThisSub = &quot;SFDocuments.Document.setKeywords&quot;
+Const cstSubArgs = &quot;Keywords&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(pvKeywords, &quot;Keywords&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Update in UNO component object and in current instance
+ vKeywords = ScriptForge.SF_Array.TrimArray(Split(pvKeywords, &quot;,&quot;))
+ _Component.DocumentProperties.Keywords = vKeywords
+ If Not IsNull(_DocumentProperties) Then _DocumentProperties.ReplaceItem(&quot;Keywords&quot;, Join(vKeywords, &quot;, &quot;))
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Property
+End Property &apos; SFDocuments.SF_Document.Keywords
+
+REM -----------------------------------------------------------------------------
+Property Get Readonly() As Boolean
+&apos;&apos;&apos; Returns True if the document must not be modified
+ Readonly = _PropertyGet(&quot;Readonly&quot;)
+End Property &apos; SFDocuments.SF_Document.Readonly
+
+REM -----------------------------------------------------------------------------
+Property Get Subject() As Variant
+&apos;&apos;&apos; Returns the updatable document property Subject
+ Subject = _PropertyGet(&quot;Subject&quot;)
+End Property &apos; SFDocuments.SF_Document.Subject
+
+REM -----------------------------------------------------------------------------
+Property Let Subject(Optional ByVal pvSubject As Variant)
+&apos;&apos;&apos; Sets the updatable document property Subject
+
+Const cstThisSub = &quot;SFDocuments.Document.setSubject&quot;
+Const cstSubArgs = &quot;Subject&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(pvSubject, &quot;Subject&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Update in UNO component object and in current instance
+ _Component.DocumentProperties.Subject = pvSubject
+ If Not IsNull(_DocumentProperties) Then _DocumentProperties.ReplaceItem(&quot;Subject&quot;, pvSubject)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Property
+End Property &apos; SFDocuments.SF_Document.Subject
+
+REM -----------------------------------------------------------------------------
+Property Get Title() As Variant
+&apos;&apos;&apos; Returns the updatable document property Title
+ Title = _PropertyGet(&quot;Title&quot;)
+End Property &apos; SFDocuments.SF_Document.Title
+
+REM -----------------------------------------------------------------------------
+Property Let Title(Optional ByVal pvTitle As Variant)
+&apos;&apos;&apos; Sets the updatable document property Title
+
+Const cstThisSub = &quot;SFDocuments.Document.setTitle&quot;
+Const cstSubArgs = &quot;Title&quot;
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive(True) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(pvTitle, &quot;Title&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Update in UNO component object and in current instance
+ _Component.DocumentProperties.Title = pvTitle
+ If Not IsNull(_DocumentProperties) Then _DocumentProperties.ReplaceItem(&quot;Title&quot;, pvTitle)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Property
+End Property &apos; SFDocuments.SF_Document.Title
+
+REM -----------------------------------------------------------------------------
+Property Get XComponent() As Variant
+&apos;&apos;&apos; Returns the com.sun.star.lang.XComponent UNO object representing the document
+ XComponent = _PropertyGet(&quot;XComponent&quot;)
+End Property &apos; SFDocuments.SF_Document.XComponent
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function Activate() As Boolean
+&apos;&apos;&apos; Make the current document active
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if the document could be activated
+&apos;&apos;&apos; Otherwise, there is no change in the actual user interface
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.Activate()
+
+Dim bActivate As Boolean &apos; Return value
+Dim oContainer As Object &apos; com.sun.star.awt.XWindow
+Const cstThisSub = &quot;SFDocuments.Document.Activate&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bActivate = False
+
+Check:
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+Try:
+ Set oContainer = _Frame.ContainerWindow
+ With oContainer
+ If .isVisible() = False Then .setVisible(True)
+ .IsMinimized = False
+ .setFocus()
+ .toFront() &apos; Force window change in Linux
+ Wait 1 &apos; Bypass desynchro issue in Linux
+ End With
+ bActivate = True
+
+Finally:
+ Activate = bActivate
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function CloseDocument(Optional ByVal SaveAsk As Variant) As Boolean
+&apos;&apos;&apos; Close the document. Does nothing if the document is already closed
+&apos;&apos;&apos; regardless of how the document was closed, manually or by program
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; SaveAsk: If True (default), the user is invited to confirm or not the writing of the changes on disk
+&apos;&apos;&apos; No effect if the document was not modified
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False if the user declined to close
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; If oDoc.CloseDocument() Then
+&apos;&apos;&apos; &apos; ...
+
+Dim bClosed As Boolean &apos; return value
+Dim oDispatch &apos; com.sun.star.frame.DispatchHelper
+Const cstThisSub = &quot;SFDocuments.Document.CloseDocument&quot;
+Const cstSubArgs = &quot;[SaveAsk=True]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bClosed = False
+
+Check:
+ If IsMissing(SaveAsk) Or IsEmpty(SaveAsk) Then SaveAsk = True
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(SaveAsk, &quot;SaveAsk&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+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
+ Else
+ _Frame.close(True)
+ _Frame.dispose()
+ bClosed = True
+ End If
+
+Finally:
+ If bClosed Then Dispose()
+ CloseDocument = bClosed
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.CloseDocument
+
+REM -----------------------------------------------------------------------------
+Public Function CreateMenu(Optional ByVal MenuHeader As Variant _
+ , Optional ByVal Before As Variant _
+ , Optional ByVal SubmenuChar As Variant _
+ , Optional ByRef _Document As Variant _
+ ) As Object
+&apos;&apos;&apos; Create a new menu entry in the document&apos;s menubar
+&apos;&apos;&apos; The menu is not intended to be saved neither in the LibreOffice global environment, nor in the document
+&apos;&apos;&apos; The method returns a SFWidgets.Menu instance. Its methods let define the menu further.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; MenuHeader: the name/header of the menu
+&apos;&apos;&apos; Before: the place where to put the new menu on the menubar (string or number &gt;= 1)
+&apos;&apos;&apos; When not found =&gt; last position
+&apos;&apos;&apos; SubmenuChar: the delimiter used in menu trees. Default = &quot;&gt;&quot;
+&apos;&apos;&apos; _Document: undocumented argument to designate the document where the menu will be located
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A SFWidgets.Menu instance or Nothing
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; Dim oMenu As Object
+&apos;&apos;&apos; Set oMenu = oDoc.CreateMenu(&quot;My menu&quot;, Before := &quot;Styles&quot;)
+&apos;&apos;&apos; With oMenu
+&apos;&apos;&apos; .AddItem(&quot;Item 1&quot;, Command := &quot;About&quot;)
+&apos;&apos;&apos; &apos;...
+&apos;&apos;&apos; .Dispose() &apos; When definition is complete, the menu instance may be disposed
+&apos;&apos;&apos; End With
+&apos;&apos;&apos; &apos; ...
+
+Dim oMenu As Object &apos; return value
+Const cstThisSub = &quot;SFDocuments.Document.CreateMenu&quot;
+Const cstSubArgs = &quot;MenuHeader, [Before=&quot;&quot;&quot;&quot;], [SubmenuChar=&quot;&quot;&gt;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oMenu = Nothing
+
+Check:
+ If IsMissing(Before) Or IsEmpty(Before) Then Before = &quot;&quot;
+ If IsMissing(SubmenuChar) Or IsEmpty(SubmenuChar) Then SubmenuChar = &quot;&quot;
+ If IsMissing(_Document) Or IsEmpty(_Document) Or IsNull(_Document) Then Set _Document = _Component
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(MenuHeader, &quot;MenuHeader&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Before, &quot;Before&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(SubmenuChar, &quot;SubmenuChar&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ Set oMenu = ScriptForge.SF_Services.CreateScriptService(&quot;SFWidgets.Menu&quot;, _Document, MenuHeader, Before, SubmenuChar)
+
+Finally:
+ Set CreateMenu = oMenu
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.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
+&apos;&apos;&apos; Store the document to the given file location in PDF format
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FileName: Identifies the file where to save. It must follow the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; Overwrite: True if the destination file may be overwritten (default = False)
+&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; Password: password to open the document
+&apos;&apos;&apos; Watermark: the text for a watermark to be drawn on every page of the exported PDF file
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False if the document could not be saved
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DOCUMENTSAVEASERROR The destination has its readonly attribute set or overwriting rejected
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.ExportAsPDF(&quot;C:\Me\myDoc.pdf&quot;, Overwrite := True)
+
+Dim bSaved As Boolean &apos; return value
+Dim oSfa As Object &apos; com.sun.star.ucb.SimpleFileAccess
+Dim sFile As String &apos; Alias of FileName
+Dim sFilter As String &apos; One of the pdf filter names
+Dim vFilterData As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim vProperties As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim FSO As Object &apos; SF_FileSystem
+Const cstThisSub = &quot;SFDocuments.Document.ExportAsPDF&quot;
+Const cstSubArgs = &quot;FileName, [Overwrite=False], [Pages=&quot;&quot;&quot;&quot;], [Password=&quot;&quot;&quot;&quot;], [Watermark=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo CatchError
+ bSaved = False
+
+Check:
+ If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = False
+ If IsMissing(Pages) Or IsEmpty(Pages) Then Pages = &quot;&quot;
+ If IsMissing(Password) Or IsEmpty(Password) Then Password = &quot;&quot;
+ If IsMissing(Watermark) Or IsEmpty(Watermark) Then Watermark = &quot;&quot;
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
+ If Not SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not SF_Utils._Validate(Pages, &quot;Pages&quot;, V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(Password, &quot;Password&quot;, V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(Watermark, &quot;Watermark&quot;, V_STRING) Then GoTo Finally
+ End If
+
+ &apos; Check destination file overwriting
+ Set FSO = CreateScriptService(&quot;FileSystem&quot;)
+ sFile = FSO._ConvertToUrl(FileName)
+ If FSO.FileExists(FileName) Then
+ If Overwrite = False Then GoTo CatchError
+ Set oSfa = ScriptForge.SF_Utils._GetUNOService(&quot;FileAccess&quot;)
+ If oSfa.isReadonly(sFile) Then GoTo CatchError
+ End If
+
+Try:
+ &apos; Setup arguments
+ sFilter = LCase(_DocumentType) &amp; &quot;_pdf_Export&quot;
+ &apos; FilterData parameters are added only if they are meaningful
+ vFilterData = Array()
+ If Len(Pages) &gt; 0 Then
+ vFilterData = ScriptForge.SF_Array.Append(vFilterData _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;PageRange&quot;, Pages))
+ End If
+ If Len(Password) &gt; 0 Then
+ vFilterData = ScriptForge.SF_Array.Append(vFilterData _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;EncryptFile&quot;, True) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;DocumentOpenPassword&quot;, Password))
+ End If
+ If Len(Watermark) &gt; 0 Then
+ vFilterData = ScriptForge.SF_Array.Append(vFilterData _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;Watermark&quot;, Watermark))
+ End If
+
+ &apos; Finalize properties and export
+ vProperties = Array( _
+ ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterName&quot;, sFilter) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterData&quot;, vFilterData))
+ _Component.StoreToURL(sFile, vProperties)
+ bSaved = True
+
+Finally:
+ ExportAsPDF = bSaved
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchError:
+ ScriptForge.SF_Exception.RaiseFatal(DOCUMENTSAVEASERROR, &quot;FileName&quot;, FileName, &quot;Overwrite&quot;, Overwrite _
+ , &quot;FilterName&quot;, &quot;PDF Export&quot;)
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.ExportAsPDF
+
+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; If the property does not exist, returns Null
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; see the exceptions of the individual properties
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myModel.GetProperty(&quot;MyProperty&quot;)
+
+Const cstThisSub = &quot;SFDocuments.Document.GetProperty&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.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:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Document service as an array
+
+ Methods = Array( _
+ &quot;Activate&quot; _
+ , &quot;CloseDocument&quot; _
+ , &quot;CreateMenu&quot; _
+ , &quot;ExportAsPDF&quot; _
+ , &quot;PrintOut&quot; _
+ , &quot;RemoveMenu&quot; _
+ , &quot;RunCommand&quot; _
+ , &quot;Save&quot; _
+ , &quot;SaveAs&quot; _
+ , &quot;SaveCopyAs&quot; _
+ , &quot;SetPrinter&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Document.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function PrintOut(Optional ByVal Pages As Variant _
+ , Optional ByVal Copies As Variant _
+ , Optional ByRef _Document 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; _Document: undocumented argument to designate the document to print when called from a subclass
+&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)
+
+Dim bPrint As Boolean &apos; Return value
+Dim vPrintGoal As Variant &apos; Array of property values
+
+Const cstThisSub = &quot;SFDocuments.Document.PrintOut&quot;
+Const cstSubArgs = &quot;[Pages=&quot;&quot;&quot;&quot;], [Copies=1]&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(_Document) Or IsEmpty(_Document) Or IsNull(_Document) Then Set _Document = _Component
+
+ 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
+ End If
+
+Try:
+ vPrintGoal = Array( _
+ ScriptForge.SF_Utils._MakePropertyValue(&quot;CopyCount&quot;, CInt(Copies)) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;Collate&quot;, True) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;Pages&quot;, Pages) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;Wait&quot;, False) _
+ )
+
+ _Document.Print(vPrintGoal)
+ bPrint = True
+
+Finally:
+ PrintOut = bPrint
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.PrintOut
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Document class as an array
+
+ Properties = Array( _
+ &quot;CustomProperties&quot; _
+ , &quot;Description&quot; _
+ , &quot;DocumentProperties&quot; _
+ , &quot;DocumentType&quot; _
+ , &quot;ExportFilters&quot; _
+ , &quot;ImportFilters&quot; _
+ , &quot;IsBase&quot; _
+ , &quot;IsCalc&quot; _
+ , &quot;IsDraw&quot; _
+ , &quot;IsImpress&quot; _
+ , &quot;IsMath&quot; _
+ , &quot;IsWriter&quot; _
+ , &quot;Keywords&quot; _
+ , &quot;Readonly&quot; _
+ , &quot;Subject&quot; _
+ , &quot;Title&quot; _
+ , &quot;XComponent&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Document.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function RemoveMenu(Optional ByVal MenuHeader As Variant _
+ , Optional ByRef _Document As Variant _
+) As Boolean
+&apos;&apos;&apos; Remove a menu entry in the document&apos;s menubar
+&apos;&apos;&apos; The removal is not intended to be saved neither in the LibreOffice global environment, nor in the document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; MenuHeader: the name/header of the menu, without tilde &quot;~&quot;, as a case-sensitive string
+&apos;&apos;&apos; _Document: undocumented argument to designate the document where the menu is located
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.RemoveMenu(&quot;File&quot;)
+&apos;&apos;&apos; &apos; ...
+
+Dim bRemove As Boolean &apos; Return value
+Dim oLayout As Object &apos; com.sun.star.comp.framework.LayoutManager
+Dim oMenuBar As Object &apos; com.sun.star.awt.XMenuBar or stardiv.Toolkit.VCLXMenuBar
+Dim sName As String &apos; Menu name
+Dim iMenuId As Integer &apos; Menu identifier
+Dim iMenuPosition As Integer &apos; Menu position &gt;= 0
+Dim i As Integer
+Const cstTilde = &quot;~&quot;
+
+Const cstThisSub = &quot;SFDocuments.Document.RemoveMenu&quot;
+Const cstSubArgs = &quot;MenuHeader&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bRemove = False
+
+Check:
+ If IsMissing(_Document) Or IsEmpty(_Document) Or IsNull(_Document) Then Set _Document = _Component
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(MenuHeader, &quot;MenuHeader&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ Set oLayout = _Document.CurrentController.Frame.LayoutManager
+ Set oMenuBar = oLayout.getElement(&quot;private:resource/menubar/menubar&quot;).XMenuBar
+
+ &apos; Search the menu identifier to remove by its name, Mark its position
+ With oMenuBar
+ iMenuPosition = -1
+ For i = 0 To .ItemCount - 1
+ iMenuId = .getItemId(i)
+ sName = Replace(.getItemText(iMenuId), cstTilde, &quot;&quot;)
+ If MenuHeader= sName Then
+ iMenuPosition = i
+ Exit For
+ End If
+ Next i
+ &apos; Remove the found menu item
+ If iMenuPosition &gt;= 0 Then
+ .removeItem(iMenuPosition, 1)
+ bRemove = True
+ End If
+ End With
+
+Finally:
+ RemoveMenu = bRemove
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.RemoveMenu
+
+REM -----------------------------------------------------------------------------
+Public Sub RunCommand(Optional ByVal Command As Variant _
+ , ParamArray Args As Variant _
+ )
+&apos;&apos;&apos; Run on the current document window the given menu command. The command is executed with or without arguments
+&apos;&apos;&apos; A few typical commands:
+&apos;&apos;&apos; Save, SaveAs, ExportToPDF, SetDocumentProperties, Undo, Copy, Paste, ...
+&apos;&apos;&apos; Dozens can be found on next page: https://wiki.documentfoundation.org/Development/DispatchCommands
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Command: Case-sensitive. The command itself is not checked.
+&apos;&apos;&apos; If the command does not contain the &quot;.uno:&quot; prefix, it is added.
+&apos;&apos;&apos; If nothing happens, then the command is probably wrong
+&apos;&apos;&apos; Args: Pairs of arguments name (string), value (any)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.RunCommand(&quot;EditDoc&quot;, &quot;Editable&quot;, False) &apos; Toggle edit mode
+
+Dim vArgs As Variant &apos; Alias of Args
+Dim oDispatch &apos; com.sun.star.frame.DispatchHelper
+Dim vProps As Variant &apos; Array of PropertyValues
+Dim vValue As Variant &apos; A single value argument
+Dim sCommand As String &apos; Alias of Command
+Dim i As Long
+Const cstPrefix = &quot;.uno:&quot;
+
+Const cstThisSub = &quot;SFDocuments.Document.RunCommand&quot;
+Const cstSubArgs = &quot;Command, [arg0Name, arg0Value], [arg1Name, arg1Value], ...&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ &apos; When called from a subclass (Calc, Writer, ..) the arguments are gathered into one single array item
+ vArgs = Args
+ If IsArray(Args) Then
+ If UBound(Args) &gt;= 0 And IsArray(Args(0)) Then vArgs = Args(0)
+ End If
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Command, &quot;Command&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._ValidateArray(vArgs, &quot;Args&quot;, 1) Then GoTo Finally
+ For i = 0 To UBound(vArgs) - 1 Step 2
+ If Not ScriptForge.SF_Utils._Validate(vArgs(i), &quot;Arg&quot; &amp; CStr(i/2) &amp; &quot;Name&quot;, V_STRING) Then GoTo Finally
+ Next i
+ End If
+
+Try:
+ &apos; Build array of property values
+ vProps = Array()
+ For i = 0 To UBound(vArgs) - 1 Step 2
+ If IsEmpty(vArgs(i + 1)) Then vValue = Null Else vValue = vArgs(i + 1)
+ vProps = ScriptForge.SF_Array.Append(vProps, ScriptForge.SF_Utils._MakePropertyValue(vArgs(i), vValue))
+ Next i
+ Set oDispatch = ScriptForge.SF_Utils._GetUNOService(&quot;DispatchHelper&quot;)
+ If ScriptForge.SF_String.StartsWith(Command, cstPrefix) Then sCommand = Command Else sCommand = cstPrefix &amp; Command
+ oDispatch.executeDispatch(_Frame, sCommand, &quot;&quot;, 0, vProps)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub &apos; SFDocuments.SF_Document.RunCommand
+
+REM -----------------------------------------------------------------------------
+Public Function Save() As Boolean
+&apos;&apos;&apos; Store the document to the file location from which it was loaded
+&apos;&apos;&apos; Ignored if the document was not modified
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False if the document could not be saved
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DOCUMENTSAVEERROR The file has been opened readonly or was opened as new and was not yet saved
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; If Not oDoc.Save() Then
+&apos;&apos;&apos; &apos; ...
+
+Dim bSaved As Boolean &apos; return value
+Const cstThisSub = &quot;SFDocuments.Document.Save&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bSaved = False
+
+Check:
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+ bSaved = False
+
+Try:
+ With _Component
+ If .isReadonly() Or Not .hasLocation() Then GoTo CatchReadonly
+ If .IsModified() Then
+ .store()
+ bSaved = True
+ End If
+ End With
+
+Finally:
+ Save = bSaved
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchReadonly:
+ ScriptForge.SF_Exception.RaiseFatal(DOCUMENTSAVEERROR, &quot;FileName&quot;, _FileIdent())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.Save
+
+REM -----------------------------------------------------------------------------
+Public Function SaveAs(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
+&apos;&apos;&apos; Store the document to the given file location
+&apos;&apos;&apos; The new location becomes the new file name on which simple Save method calls will be applied
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FileName: Identifies the file where to save. It must follow the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; Overwrite: True if the destination file may be overwritten (default = False)
+&apos;&apos;&apos; Password: Use to protect the document
+&apos;&apos;&apos; FilterName: the name of a filter that should be used for saving the document
+&apos;&apos;&apos; If present, the filter must exist
+&apos;&apos;&apos; FilterOptions: an optional string of options associated with the filter
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False if the document could not be saved
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DOCUMENTSAVEASERROR The destination has its readonly attribute set or overwriting rejected
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.SaveAs(&quot;C:\Me\Copy2.odt&quot;, Overwrite := True)
+
+Dim bSaved As Boolean &apos; return value
+Dim oFilterFactory As Object &apos; com.sun.star.document.FilterFactory
+Dim oSfa As Object &apos; com.sun.star.ucb.SimpleFileAccess
+Dim sFile As String &apos; Alias of FileName
+Dim vProperties As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim FSO As Object &apos; SF_FileSystem
+Const cstThisSub = &quot;SFDocuments.Document.SaveAs&quot;
+Const cstSubArgs = &quot;FileName, [Overwrite=False], [Password=&quot;&quot;&quot;&quot;], [FilterName=&quot;&quot;&quot;&quot;], [FilterOptions=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo CatchError
+ bSaved = False
+
+Check:
+ If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = False
+ If IsMissing(Password) Or IsEmpty(Password) Then Password = &quot;&quot;
+ If IsMissing(FilterName) Or IsEmpty(FilterName) Then FilterName = &quot;&quot;
+ If IsMissing(FilterOptions) Or IsEmpty(FilterOptions) Then FilterOptions = &quot;&quot;
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
+ If Not SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not SF_Utils._Validate(Password, &quot;Password&quot;, V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(FilterName, &quot;FilterName&quot;, V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(FilterOptions, &quot;FilterOptions&quot;, V_STRING) Then GoTo Finally
+ End If
+
+ &apos; Check that the filter exists
+ If Len(FilterName) &gt; 0 Then
+ Set oFilterFactory = ScriptForge.SF_Utils._GetUNOService(&quot;FilterFactory&quot;)
+ If Not oFilterFactory.hasByName(FilterName) Then GoTo CatchError
+ End If
+
+ &apos; Check destination file overwriting
+ Set FSO = CreateScriptService(&quot;FileSystem&quot;)
+ sFile = FSO._ConvertToUrl(FileName)
+ If FSO.FileExists(FileName) Then
+ If Overwrite = False Then GoTo CatchError
+ Set oSfa = ScriptForge.SF_Utils._GetUNOService(&quot;FileAccess&quot;)
+ If oSfa.isReadonly(sFile) Then GoTo CatchError
+ End If
+
+Try:
+ &apos; Setup arguments
+ If Len(Password) + Len(FilterName) = 0 Then
+ vProperties = Array()
+ Else
+ vProperties = Array( _
+ ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterName&quot;, FilterName) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterOptions&quot;, FilterOptions) _
+ )
+ If Len(Password) &gt; 0 Then &apos; Password is to add only if &lt;&gt; &quot;&quot; !?
+ vProperties = ScriptForge.SF_Array.Append(vProperties _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;Password&quot;, Password))
+ End If
+ End If
+
+ _Component.StoreAsURL(sFile, vProperties)
+
+ &apos; Remind the new file name
+ _WindowFileName = sFile
+ _WindowName = FSO.GetName(FileName)
+ bSaved = True
+
+Finally:
+ SaveAs = bSaved
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchError:
+ ScriptForge.SF_Exception.RaiseFatal(DOCUMENTSAVEASERROR, &quot;FileName&quot;, FileName, &quot;Overwrite&quot;, Overwrite _
+ , &quot;FilterName&quot;, FilterName)
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.SaveAs
+
+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
+&apos;&apos;&apos; Store a copy or export the document to the given file location
+&apos;&apos;&apos; The actual location is unchanged
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; FileName: Identifies the file where to save. It must follow the SF_FileSystem.FileNaming notation
+&apos;&apos;&apos; Overwrite: True if the destination file may be overwritten (default = False)
+&apos;&apos;&apos; Password: Use to protect the document
+&apos;&apos;&apos; FilterName: the name of a filter that should be used for saving the document
+&apos;&apos;&apos; If present, the filter must exist
+&apos;&apos;&apos; FilterOptions: an optional string of options associated with the filter
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; False if the document could not be saved
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; DOCUMENTSAVEASERROR The destination has its readonly attribute set or overwriting rejected
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.SaveCopyAs(&quot;C:\Me\Copy2.odt&quot;, Overwrite := True)
+
+Dim bSaved As Boolean &apos; return value
+Dim oFilterFactory As Object &apos; com.sun.star.document.FilterFactory
+Dim oSfa As Object &apos; com.sun.star.ucb.SimpleFileAccess
+Dim sFile As String &apos; Alias of FileName
+Dim vProperties As Variant &apos; Array of com.sun.star.beans.PropertyValue
+Dim FSO As Object &apos; SF_FileSystem
+Const cstThisSub = &quot;SFDocuments.Document.SaveCopyAs&quot;
+Const cstSubArgs = &quot;FileName, [Overwrite=False], [Password=&quot;&quot;&quot;&quot;], [FilterName=&quot;&quot;&quot;&quot;], [FilterOptions=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo CatchError
+ bSaved = False
+
+Check:
+ If IsMissing(Overwrite) Or IsEmpty(Overwrite) Then Overwrite = False
+ If IsMissing(Password) Or IsEmpty(Password) Then Password = &quot;&quot;
+ If IsMissing(FilterName) Or IsEmpty(FilterName) Then FilterName = &quot;&quot;
+ If IsMissing(FilterOptions) Or IsEmpty(FilterOptions) Then FilterOptions = &quot;&quot;
+
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) Then GoTo Finally
+ If Not SF_Utils._Validate(Overwrite, &quot;Overwrite&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not SF_Utils._Validate(Password, &quot;Password&quot;, V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(FilterName, &quot;FilterName&quot;, V_STRING) Then GoTo Finally
+ If Not SF_Utils._Validate(FilterOptions, &quot;FilterOptions&quot;, V_STRING) Then GoTo Finally
+ End If
+
+ &apos; Check that the filter exists
+ If Len(FilterName) &gt; 0 Then
+ Set oFilterFactory = ScriptForge.SF_Utils._GetUNOService(&quot;FilterFactory&quot;)
+ If Not oFilterFactory.hasByName(FilterName) Then GoTo CatchError
+ End If
+
+ &apos; Check destination file overwriting
+ Set FSO = CreateScriptService(&quot;FileSystem&quot;)
+ sFile = FSO._ConvertToUrl(FileName)
+ If FSO.FileExists(FileName) Then
+ If Overwrite = False Then GoTo CatchError
+ Set oSfa = ScriptForge.SF_Utils._GetUNOService(&quot;FileAccess&quot;)
+ If oSfa.isReadonly(sFile) Then GoTo CatchError
+ End If
+
+Try:
+ &apos; Setup arguments
+ If Len(Password) + Len(FilterName) = 0 Then
+ vProperties = Array()
+ Else
+ vProperties = Array( _
+ ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterName&quot;, FilterName) _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;FilterOptions&quot;, FilterOptions) _
+ )
+ If Len(Password) &gt; 0 Then &apos; Password is to add only if &lt;&gt; &quot;&quot; !?
+ vProperties = ScriptForge.SF_Array.Append(vProperties _
+ , ScriptForge.SF_Utils._MakePropertyValue(&quot;Password&quot;, Password))
+ End If
+ End If
+
+ _Component.StoreToURL(sFile, vProperties)
+ bSaved = True
+
+Finally:
+ SaveCopyAs = bSaved
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchError:
+ ScriptForge.SF_Exception.RaiseFatal(DOCUMENTSAVEASERROR, &quot;FileName&quot;, FileName, &quot;Overwrite&quot;, Overwrite _
+ , &quot;FilterName&quot;, FilterName)
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.SaveCopyAs
+
+REM -----------------------------------------------------------------------------
+Public Function SetPrinter(Optional ByVal Printer As Variant _
+ , Optional ByVal Orientation As Variant _
+ , Optional ByVal PaperFormat As Variant _
+ , Optional ByRef _PrintComponent As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Define the printer options for the document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Printer: the name of the printer queue where to print to
+&apos;&apos;&apos; When absent or space, the default printer is set
+&apos;&apos;&apos; Orientation: either &quot;PORTRAIT&quot; or &quot;LANDSCAPE&quot;. Left unchanged when absent
+&apos;&apos;&apos; PaperFormat: one of next values
+&apos;&apos;&apos; &quot;A3&quot;, &quot;A4&quot;, &quot;A5&quot;, &quot;B4&quot;, &quot;B5&quot;, &quot;LETTER&quot;, &quot;LEGAL&quot;, &quot;TABLOID&quot;
+&apos;&apos;&apos; Left unchanged when absent
+&apos;&apos;&apos; _PrintComponent: undocumented argument to determine the component
+&apos;&apos;&apos; Useful typically to apply printer settings on a Base form document
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oDoc.SetPrinter(Orientation := &quot;PORTRAIT&quot;)
+
+Dim bPrinter As Boolean &apos; Return value
+Dim vPrinters As Variant &apos; Array of known printers
+Dim vOrientations As Variant &apos; Array of allowed paper orientations
+Dim vPaperFormats As Variant &apos; Array of allowed formats
+Dim vPrinterSettings As Variant &apos; Array of property values
+Dim oPropertyValue As New com.sun.star.beans.PropertyValue
+ &apos; A single property value item
+Const cstThisSub = &quot;SFDocuments.Document.SetPrinter&quot;
+Const cstSubArgs = &quot;[Printer=&quot;&quot;&quot;&quot;], [Orientation=&quot;&quot;PORTRAIT&quot;&quot;|&quot;&quot;LANDSCAPE&quot;&quot;]&quot; _
+ &amp; &quot;, [PaperFormat=&quot;&quot;A3&quot;&quot;|&quot;&quot;A4&quot;&quot;|&quot;&quot;A5&quot;&quot;|&quot;&quot;B4&quot;&quot;|&quot;&quot;B5&quot;&quot;|&quot;&quot;LETTER&quot;&quot;|&quot;&quot;LEGAL&quot;&quot;|&quot;&quot;TABLOID&quot;&quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bPrinter = False
+
+Check:
+ If IsMissing(Printer) Or IsEmpty(Printer) Then Printer = &quot;&quot;
+ If IsMissing(Orientation) Or IsEmpty(Orientation) Then Orientation = &quot;&quot;
+ If IsMissing(PaperFormat) Or IsEmpty(PaperFormat) Then PaperFormat = &quot;&quot;
+ If IsMissing(_PrintComponent) Or IsEmpty(_PrintComponent) Then Set _PrintComponent = _Component
+
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Unconditional validation
+ If Not _IsStillAlive() Then GoTo Finally
+ If VarType(Printer) = V_STRING Then
+ vPrinters = ScriptForge.SF_Platform.Printers
+ If Len(Printer) &gt; 0 Then
+ If Not ScriptForge.SF_Utils._Validate(Printer, &quot;Printer&quot;, V_STRING, vPrinters) Then GoTo Finally
+ End If
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Printer, &quot;Printer&quot;, V_STRING) Then GoTo Finally &apos; Manage here the VarType error
+ End If
+ If VarType(Orientation) = V_STRING Then
+ vOrientations = Array(&quot;PORTRAIT&quot;, &quot;LANDSCAPE&quot;)
+ If Len(Orientation) &gt; 0 Then
+ If Not ScriptForge.SF_Utils._Validate(Orientation, &quot;Orientation&quot;, V_STRING, vOrientations) Then GoTo Finally
+ End If
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Orientation, &quot;Orientation&quot;, V_STRING) Then GoTo Finally
+ End If
+ If VarType(PaperFormat) = V_STRING Then
+ vPaperFormats = Array(&quot;A3&quot;, &quot;A4&quot;, &quot;A5&quot;, &quot;B4&quot;, &quot;B5&quot;, &quot;LETTER&quot;, &quot;LEGAL&quot;, &quot;TABLOID&quot;)
+ If Len(PaperFormat) &gt; 0 Then
+ If Not ScriptForge.SF_Utils._Validate(PaperFormat, &quot;PaperFormat&quot;, V_STRING, vPaperFormats) Then GoTo Finally
+ End If
+ Else
+ If Not ScriptForge.SF_Utils._Validate(PaperFormat, &quot;PaperFormat&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ With _PrintComponent
+ Set oPropertyValue = ScriptForge.SF_Utils._MakePropertyValue(&quot;Name&quot;, Iif(Len(Printer) &gt; 0, Printer, vPrinters(0)))
+ vPrinterSettings = Array(oPropertyValue)
+ If Len(Orientation) &gt; 0 Then
+ vPrinterSettings = ScriptForge.SF_Utils._SetPropertyValue(vPrinterSettings, &quot;PaperOrientation&quot; _
+ , ScriptForge.SF_Array.IndexOf(vOrientations, Orientation, CaseSensitive := False))
+ End If
+ If Len(PaperFormat) &gt; 0 Then
+ vPrinterSettings = ScriptForge.SF_Utils._SetPropertyValue(vPrinterSettings, &quot;PaperFormat&quot; _
+ , ScriptForge.SF_Array.IndexOf(vPaperFormats, PaperFormat, CaseSensitive := False))
+ End If
+ .setPrinter(vPrinterSettings)
+ End With
+ bPrinter = True
+
+Finally:
+ SetPrinter = bPrinter
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.SetPrinter
+
+REM -----------------------------------------------------------------------------
+Private Function SetProperty(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.Document.set&quot; &amp; psProperty
+ If IsMissing(pvValue) Then pvValue = Empty
+ &apos;ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Validation done in Property Lets
+
+ If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
+ bSet = True
+ Select Case UCase(psProperty)
+ Case UCase(&quot;CustomProperties&quot;)
+ CustomProperties = pvValue
+ Case UCase(&quot;Description&quot;)
+ Description = pvValue
+ Case UCase(&quot;Keywords&quot;)
+ Keywords = pvValue
+ Case UCase(&quot;Subject&quot;)
+ Subject = pvValue
+ Case UCase(&quot;Title&quot;)
+ Title = pvValue
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ SetProperty = bSet
+ &apos;ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document.SetProperty
+
+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 = Iif(Len(_WindowFileName) &gt; 0, SF_FileSystem._ConvertFromUrl(_WindowFileName), _WindowTitle)
+
+End Function &apos; SFDocuments.SF_Document._FileIdent
+
+REM -----------------------------------------------------------------------------
+Private Function _GetFilterNames(ByVal pbExport As Boolean) As Variant
+&apos;&apos;&apos; Returns the list of export (pbExport = True) or import filters
+&apos;&apos;&apos; applicable to the current document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pbExport: True for export, False for import
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-based array of strings
+
+Dim vFilters As Variant &apos; Return value
+Dim sIdentifier As String &apos; Document service, like com.sun.star.text.TextDocument
+Dim oFilterFactory As Object &apos; com.sun.star.document.FilterFactory
+Dim vAllFilters As Variant &apos; The full list of installed filters
+Dim sFilter As String &apos; A single filter name
+Dim iCount As Integer &apos; Filters counter
+Dim vFilter As Variant &apos; A filter descriptor as an array of Name/Value pairs
+Dim sType As String &apos; The filter type to be compared with the document service
+Dim lFlags As Long &apos; Read https://wiki.documentfoundation.org/Documentation/DevGuide/Office_Development#Properties_of_a_Filter
+Dim bExport As Boolean &apos; Filter valid for export when True
+Dim bImport As Boolean &apos; Filter valid for import when True
+Dim bImportExport As Boolean &apos; Filter valid both for import and export when True
+
+ vFilters = Array()
+ On Local Error GoTo Finally &apos; Return empty or partial list if error
+
+Try:
+ sIdentifier = _Component.Identifier
+ Set oFilterFactory = ScriptForge.SF_Utils._GetUNOService(&quot;FilterFactory&quot;)
+ vAllFilters = oFilterFactory.getElementNames()
+ ReDim vFilters(0 To UBound(vAllFilters))
+ iCount = -1
+
+ For Each sFilter In vAllFilters
+ vFilter = oFilterFactory.getByName(sFilter)
+ sType = vFilter(12).Value &apos; Hard-coded index for document types
+ If sType = sIdentifier Then
+ lFlags = vFilter(10).Value &apos; Hard-coded index for flags
+ &apos; export: flag is even
+ &apos; import: flag is odd and flag/2 is even
+ &apos; import/export: flag is odd and flag/2 is odd
+ bExport = ( lFlags Mod 2 = 0 )
+ bImport = ( (lFlags Mod 2 = 1) And ((lFlags \ 2) Mod 2 = 0) )
+ bImportExport = ( (lFlags Mod 2 = 1) And ((lFlags \ 2) Mod 2 = 1) )
+ &apos; Select filter ?
+ If bImportExport _
+ Or (pbExport And bExport) _
+ Or (Not pbExport And bImport) Then
+ iCount = iCount + 1
+ vFilters(iCount) = sFilter
+ End If
+ End If
+ Next sFilter
+
+ If iCount &gt; -1 Then
+ ReDim Preserve vFilters(0 To iCount)
+ End If
+
+Finally:
+ _GetFilterNames = vFilters
+ Exit Function
+End Function &apos; SFDocuments.SF_Document._GetFilterNames
+
+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
+Dim sFileName As String &apos; File identification used to display error message
+
+ On Local Error GoTo Catch &apos; Anticipate DisposedException errors or alike
+ If IsMissing(pbForUpdate) Then pbForUpdate = False
+ If IsMissing(pbError) Then pbError = True
+
+Try:
+ &apos; Check existence of document
+ bAlive = Not IsNull(_Frame)
+ If bAlive Then bAlive = Not IsNull(_Component)
+ If bAlive Then bAlive = Not IsNull(_Component.CurrentController)
+
+ &apos; Check document is not read only
+ If bAlive And pbForUpdate Then
+ If _Component.isreadonly() Then GoTo CatchReadonly
+ End If
+
+Finally:
+ _IsStillAlive = bAlive
+ Exit Function
+Catch:
+ bAlive = False
+ On Error GoTo 0
+ sFileName = _FileIdent()
+ Dispose()
+ If pbError Then ScriptForge.SF_Exception.RaiseFatal(DOCUMENTDEADERROR, sFileName)
+ GoTo Finally
+CatchReadonly:
+ bAlive = False
+ If pbError Then ScriptForge.SF_Exception.RaiseFatal(DOCUMENTREADONLYERROR, &quot;Document&quot;, _FileIdent())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Document._IsStillAlive
+
+REM -----------------------------------------------------------------------------
+Private Sub _LoadDocumentProperties()
+&apos;&apos;&apos; Create dictionary with document properties as entries/ Custom properties are excluded
+&apos;&apos;&apos; Document is presumed still alive
+&apos;&apos;&apos; Special values:
+&apos;&apos;&apos; Only valid dates are taken
+&apos;&apos;&apos; Statistics are exploded in subitems. Subitems are specific to document type
+&apos;&apos;&apos; Keywords are joined
+&apos;&apos;&apos; Language is aligned on L10N convention la-CO
+
+Dim oProperties As Object &apos; Document properties
+Dim vNamedValue As Variant &apos; com.sun.star.beans.NamedValue
+
+ If IsNull(_DocumentProperties) Then
+ Set oProperties = _Component.getDocumentProperties
+ Set _DocumentProperties = CreateScriptService(&quot;Dictionary&quot;)
+ With _DocumentProperties
+ .Add(&quot;Author&quot;, oProperties.Author)
+ .Add(&quot;AutoloadSecs&quot;, oProperties.AutoloadSecs)
+ .Add(&quot;AutoloadURL&quot;, oProperties.AutoloadURL)
+ If oProperties.CreationDate.Year &gt; 0 Then .Add(&quot;CreationDate&quot;, CDateFromUnoDateTime(oProperties.CreationDate))
+ .Add(&quot;DefaultTarget&quot;, oProperties.DefaultTarget)
+ .Add(&quot;Description&quot;, oProperties.Description) &apos; The description can be multiline
+ &apos; DocumentStatistics : number and names of statistics depend on document type
+ For Each vNamedValue In oProperties.DocumentStatistics
+ .Add(vNamedValue.Name, vNamedValue.Value)
+ Next vNamedValue
+ .Add(&quot;EditingDuration&quot;, oProperties.EditingDuration)
+ .Add(&quot;Generator&quot;, oProperties.Generator)
+ .Add(&quot;Keywords&quot;, Join(oProperties.Keywords, &quot;, &quot;))
+ .Add(&quot;Language&quot;, oProperties.Language.Language &amp; Iif(Len(oProperties.Language.Country) &gt; 0, &quot;-&quot; &amp; oProperties.Language.Country, &quot;&quot;))
+ If oProperties.ModificationDate.Year &gt; 0 Then .Add(&quot;ModificationDate&quot;, CDateFromUnoDateTime(oProperties.ModificationDate))
+ If oProperties.PrintDate.Year &gt; 0 Then .Add(&quot;PrintDate&quot;, CDateFromUnoDateTime(oProperties.PrintDate))
+ .Add(&quot;PrintedBy&quot;, oProperties.PrintedBy)
+ .Add(&quot;Subject&quot;, oProperties.Subject)
+ If oProperties.TemplateDate.Year &gt; 0 Then .Add(&quot;TemplateDate&quot;, CDateFromUnoDateTime(oProperties.TemplateDate))
+ .Add(&quot;TemplateName&quot;, oProperties.TemplateName)
+ .Add(&quot;TemplateURL&quot;, oProperties.TemplateURL)
+ .Add(&quot;Title&quot;, oProperties.Title)
+ End With
+ End If
+
+End Sub &apos; SFDocuments.SF_Document._LoadDocumentProperties
+
+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
+
+Dim oProperties As Object &apos; Document or Custom properties
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ _PropertyGet = False
+
+ Select Case _DocumentType
+ Case &quot;Calc&quot; : cstThisSub = &quot;SFDocuments.SF_&quot; &amp; _DocumentType &amp; &quot;.get&quot; &amp; psProperty
+ Case Else : cstThisSub = &quot;SFDocuments.SF_Document.get&quot; &amp; psProperty
+ End Select
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+ Select Case psProperty
+ Case &quot;CustomProperties&quot;
+ _CustomProperties = CreateScriptService(&quot;Dictionary&quot;) &apos; Always reload as updates could have been done manually by user
+ _CustomProperties.ImportFromPropertyValues(_Component.getDocumentProperties().UserDefinedProperties.getPropertyValues)
+ _PropertyGet = _CustomProperties
+ Case &quot;Description&quot;
+ _PropertyGet = _Component.DocumentProperties.Description
+ Case &quot;DocumentProperties&quot;
+ _LoadDocumentProperties() &apos; Always reload as updates could have been done manually by user
+ Set _PropertyGet = _DocumentProperties
+ Case &quot;DocumentType&quot;
+ _PropertyGet = _DocumentType
+ Case &quot;ExportFilters&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;
+ _PropertyGet = ( Mid(psProperty, 3) = _DocumentType )
+ Case &quot;Keywords&quot;
+ _PropertyGet = Join(_Component.DocumentProperties.Keywords, &quot;, &quot;)
+ Case &quot;Readonly&quot;
+ _PropertyGet = _Component.isReadonly()
+ Case &quot;Subject&quot;
+ _PropertyGet = _Component.DocumentProperties.Subject
+ Case &quot;Title&quot;
+ _PropertyGet = _Component.DocumentProperties.Title
+ Case &quot;XComponent&quot;
+ Set _PropertyGet = _Component
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+End Function &apos; SFDocuments.SF_Document._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the SF_Document 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;[Document]: &quot; &amp; _DocumentType &amp; &quot; - &quot; &amp; _FileIdent()
+
+End Function &apos; SFDocuments.SF_Document._Repr
+
+REM ============================================ END OF SFDOCUMENTS.SF_DOCUMENT
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_DocumentListener.xba b/wizards/source/sfdocuments/SF_DocumentListener.xba
new file mode 100644
index 000000000..fbb0271bb
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_DocumentListener.xba
@@ -0,0 +1,114 @@
+<?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_DocumentListener" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === The SFDocuments library is one of the associated libraries. ===
+REM === Full documentation is available on https://help.libreoffice.org/ ===
+REM =======================================================================================================================
+
+Option Compatible
+Option Explicit
+
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+&apos;&apos;&apos; SF_DocumentListener
+&apos;&apos;&apos; ===================
+&apos;&apos;&apos; The current module is dedicated to the management of document events + listeners, triggered by user actions,
+&apos;&apos;&apos; which cannot be defined with the Basic IDE
+&apos;&apos;&apos;
+&apos;&apos;&apos; Concerned listeners:
+&apos;&apos;&apos; com.sun.star.sheet.XRangeSelectionListener
+&apos;&apos;&apos; allowing a user to select a cell range at any moment
+&apos;&apos;&apos;
+&apos;&apos;&apos; The described events/listeners are processed by UNO listeners
+&apos;&apos;&apos;
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&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 ================================================================= DEFINITIONS
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private _SelectedRange As String &apos; The selected range is returned by a &quot;done&quot; event
+Private _RangeSelectionFinished As Boolean &apos; Flag indicating that the interaction with the user has stopped
+
+REM ================================================================== EXCEPTIONS
+
+REM ============================================================== PUBLIC METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function RunRangeSelector(ByRef poComponent As Object _
+ , ByRef pvPropertyValues As Variant _
+ ) As String
+&apos;&apos;&apos; Called from the SF_Calc.OpenRangeSelector() method
+&apos;&apos;&apos; Opens a non-modal dialog with a text box,
+&apos;&apos;&apos; let the user make a selection in the current or another sheet and
+&apos;&apos;&apos; returns the selected area as a string.
+
+Dim oController As Object &apos; com.sun.star.frame.Controller
+Dim oListener As Object &apos; com.sun.star.sheet.XRangeSelectionListener
+Dim lCountLoops As Long &apos; Sleep cycles counter
+
+Const cstListenerPrefix = &quot;_SFRGSEL_&quot; &apos; Prefix used for naming events Subs
+Const cstSleep = 50 &apos; Sleep steps in ms while waiting for the end of the interaction
+Const cstMaxSleep = (60 * 5 * 1000) / cstSleep &apos; Never sleep more than 5 minutes. Afterwards, processing continues
+
+ On Local Error GoTo Catch &apos; Avoid stopping event scripts
+
+Try:
+ &apos; Create the listener
+ Set oController = poComponent.CurrentController
+ Set oListener = CreateUnoListener(cstListenerPrefix, &quot;com.sun.star.sheet.XRangeSelectionListener&quot;)
+ oController.addRangeSelectionListener(oListener)
+
+ &apos; Open the selector
+ _SelectedRange = &quot;&quot;
+ _RangeSelectionFinished = False
+ oController.startRangeSelection(pvPropertyValues)
+
+ &apos; Dummy thread synchronization
+ lCountLoops = 0
+ Do While Not _RangeSelectionFinished And lCountLoops &lt; cstMaxSleep
+ Wait(cstSleep)
+ lCountLoops = lCountLoops + 1
+ Loop
+
+Finally:
+ If Not IsNull(oListener) Then oController.removeRangeSelectionListener(oListener)
+ RunRangeSelector = _SelectedRange
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_DocumentListener.RunRangeSelector
+
+REM ============================================================= PRIVATE METHODS
+
+REM -----------------------------------------------------------------------------
+Sub _SFRGSEL_done(Optional poEvent As Object) &apos; com.sun.star.sheet.RangeSelectionEvent
+
+ On Local Error GoTo Catch &apos; Avoid stopping event scripts
+
+Try:
+ _SelectedRange = poEvent.RangeDescriptor
+ _RangeSelectionFinished = True
+
+Finally:
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub
+
+REM -----------------------------------------------------------------------------
+Sub _SFRGSEL_aborted(Optional poEvent As Object) &apos; com.sun.star.sheet.RangeSelectionEvent
+
+ On Local Error GoTo Catch &apos; Avoid stopping event scripts
+
+Try:
+ _RangeSelectionFinished = True
+
+Finally:
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub
+
+REM ============================================ END OF SFDIALOGS.SF_DIALOGLISTENER
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Form.xba b/wizards/source/sfdocuments/SF_Form.xba
new file mode 100644
index 000000000..404c24bd3
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_Form.xba
@@ -0,0 +1,1535 @@
+<?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; 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; 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
+&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 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;
+&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; REM the form is the subject of an event
+&apos;&apos;&apos; Sub OnEvent(ByRef poEvent As Object)
+&apos;&apos;&apos; Dim myForm As Object
+&apos;&apos;&apos; Set myForm = CreateScriptService(&quot;SFDocuments.FormEvent&quot;, poEvent)
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_form.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 FORMDEADERROR = &quot;FORMDEADERROR&quot;
+Private Const SUBFORMNOTFOUNDERROR = &quot;SUBFORMNOTFOUNDERROR&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 _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
+
+&apos; Events management
+Private _CacheIndex As Long &apos; Index in central cache storage
+
+&apos; Form UNO references
+&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
+
+&apos; Cache storage for controls
+Private _ControlNames As Variant &apos; Array of control names
+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 or of another subform
+Const ISUNDEFINED = -1 &apos; Undefined form type
+
+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;
+ _SheetName = &quot;&quot;
+ _FormDocumentName = &quot;&quot;
+ Set _FormDocument = Nothing
+ _FormType = ISUNDEFINED
+ _CacheIndex = -1
+ Set _Form = Nothing
+ Set _Database = Nothing
+ _ControlNames = Array()
+ _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
+ If Not IsNull(_Database) And (_FormType = ISDOCFORM Or _FormType = ISCALCFORM) Then
+ Set _Database = _Database.Dispose()
+ End If
+ SF_Register._CleanCacheEntry(_CacheIndex)
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDocuments.SF_Form Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get AllowDeletes() As Variant
+&apos;&apos;&apos; The AllowDeletes property specifies if the form allows to delete records
+ AllowDeletes = _PropertyGet(&quot;AllowDeletes&quot;)
+End Property &apos; SFDocuments.SF_Form.AllowDeletes (get)
+
+REM -----------------------------------------------------------------------------
+Property Let AllowDeletes(Optional ByVal pvAllowDeletes As Variant)
+&apos;&apos;&apos; Set the updatable property AllowDeletes
+ _PropertySet(&quot;AllowDeletes&quot;, pvAllowDeletes)
+End Property &apos; SFDocuments.SF_Form.AllowDeletes (let)
+
+REM -----------------------------------------------------------------------------
+Property Get AllowInserts() As Variant
+&apos;&apos;&apos; The AllowInserts property specifies if the form allows to add records
+ AllowInserts = _PropertyGet(&quot;AllowInserts&quot;)
+End Property &apos; SFDocuments.SF_Form.AllowInserts (get)
+
+REM -----------------------------------------------------------------------------
+Property Let AllowInserts(Optional ByVal pvAllowInserts As Variant)
+&apos;&apos;&apos; Set the updatable property AllowInserts
+ _PropertySet(&quot;AllowInserts&quot;, pvAllowInserts)
+End Property &apos; SFDocuments.SF_Form.AllowInserts (let)
+
+REM -----------------------------------------------------------------------------
+Property Get AllowUpdates() As Variant
+&apos;&apos;&apos; The AllowUpdates property specifies if the form allows to update records
+ AllowUpdates = _PropertyGet(&quot;AllowUpdates&quot;)
+End Property &apos; SFDocuments.SF_Form.AllowUpdates (get)
+
+REM -----------------------------------------------------------------------------
+Property Let AllowUpdates(Optional ByVal pvAllowUpdates As Variant)
+&apos;&apos;&apos; Set the updatable property AllowUpdates
+ _PropertySet(&quot;AllowUpdates&quot;, pvAllowUpdates)
+End Property &apos; SFDocuments.SF_Form.AllowUpdates (let)
+
+REM -----------------------------------------------------------------------------
+Property Get BaseForm() As String
+&apos;&apos;&apos; The BaseForm property specifies the hierarchical name of the Base form containing the actual form
+ BaseForm = _PropertyGet(&quot;BaseForm&quot;)
+End Property &apos; SFDocuments.SF_Form.BaseForm (get)
+
+REM -----------------------------------------------------------------------------
+Property Get Bookmark() As Variant
+&apos;&apos;&apos; The Bookmark property specifies uniquely the current record of the form&apos;s underlying table, query or SQL statement.
+ Bookmark = _PropertyGet(&quot;Bookmark&quot;)
+End Property &apos; SFDocuments.SF_Form.Bookmark (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Bookmark(Optional ByVal pvBookmark As Variant)
+&apos;&apos;&apos; Set the updatable property Bookmark
+ _PropertySet(&quot;Bookmark&quot;, pvBookmark)
+End Property &apos; SFDocuments.SF_Form.Bookmark (let)
+
+REM -----------------------------------------------------------------------------
+Property Get CurrentRecord() As Variant
+&apos;&apos;&apos; The CurrentRecord property identifies the current record in the recordset being viewed on a form
+ CurrentRecord = _PropertyGet(&quot;CurrentRecord&quot;)
+End Property &apos; SFDocuments.SF_Form.CurrentRecord (get)
+
+REM -----------------------------------------------------------------------------
+Property Let CurrentRecord(Optional ByVal pvCurrentRecord As Variant)
+&apos;&apos;&apos; Set the updatable property CurrentRecord
+&apos;&apos;&apos; If the row number is positive, the cursor moves to the given row number with respect to the beginning of the result set.
+&apos;&apos;&apos; The first row is row 1, the second is row 2, and so on.
+&apos;&apos;&apos; If the given row number is negative, the cursor moves to an absolute row position with respect to the end of the result set.
+&apos;&apos;&apos; For example, setting CurrentRecord = -1 positions the cursor on the last row, -2 indicates the next-to-last row, and so on
+ _PropertySet(&quot;CurrentRecord&quot;, pvCurrentRecord)
+End Property &apos; SFDocuments.SF_Form.CurrentRecord (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Filter() As Variant
+&apos;&apos;&apos; The Filter property specifies a subset of records to be displayed.
+ Filter = _PropertyGet(&quot;Filter&quot;)
+End Property &apos; SFDocuments.SF_Form.Filter (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Filter(Optional ByVal pvFilter As Variant)
+&apos;&apos;&apos; Set the updatable property Filter
+ _PropertySet(&quot;Filter&quot;, pvFilter)
+End Property &apos; SFDocuments.SF_Form.Filter (let)
+
+REM -----------------------------------------------------------------------------
+Property Get LinkChildFields() As Variant
+&apos;&apos;&apos; The LinkChildFields property specifies how records in a subform (child) are linked to records in its parent form
+&apos;&apos;&apos; It returns an array of strings
+ LinkChildFields = _PropertyGet(&quot;LinkChildFields&quot;)
+End Property &apos; SFDocuments.SF_Form.LinkChildFields (get)
+
+REM -----------------------------------------------------------------------------
+Property Get LinkParentFields() As Variant
+&apos;&apos;&apos; The LinkParentFields property specifies how records in a subform (Child) are linked to records in its parent form
+&apos;&apos;&apos; It returns an array of strings
+ LinkParentFields = _PropertyGet(&quot;LinkParentFields&quot;)
+End Property &apos; SFDocuments.SF_Form.LinkParentFields (get)
+
+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 OnApproveCursorMove() As Variant
+&apos;&apos;&apos; The OnApproveCursorMove property specifies the script to trigger when this event occurs
+ OnApproveCursorMove = _PropertyGet(&quot;OnApproveCursorMove&quot;)
+End Property &apos; SFDocuments.SF_Form.OnApproveCursorMove (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnApproveCursorMove(Optional ByVal pvOnApproveCursorMove As Variant)
+&apos;&apos;&apos; Set the updatable property OnApproveCursorMove
+ _PropertySet(&quot;OnApproveCursorMove&quot;, pvOnApproveCursorMove)
+End Property &apos; SFDocuments.SF_Form.OnApproveCursorMove (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnApproveReset() As Variant
+&apos;&apos;&apos; The OnApproveReset property specifies the script to trigger when this event occurs
+ OnApproveReset = _PropertyGet(&quot;OnApproveReset&quot;)
+End Property &apos; SFDocuments.SF_Form.OnApproveReset (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnApproveReset(Optional ByVal pvOnApproveReset As Variant)
+&apos;&apos;&apos; Set the updatable property OnApproveReset
+ _PropertySet(&quot;OnApproveReset&quot;, pvOnApproveReset)
+End Property &apos; SFDocuments.SF_Form.OnApproveReset (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnApproveRowChange() As Variant
+&apos;&apos;&apos; The OnApproveRowChange property specifies the script to trigger when this event occurs
+ OnApproveRowChange = _PropertyGet(&quot;OnApproveRowChange&quot;)
+End Property &apos; SFDocuments.SF_Form.OnApproveRowChange (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnApproveRowChange(Optional ByVal pvOnApproveRowChange As Variant)
+&apos;&apos;&apos; Set the updatable property OnApproveRowChange
+ _PropertySet(&quot;OnApproveRowChange&quot;, pvOnApproveRowChange)
+End Property &apos; SFDocuments.SF_Form.OnApproveRowChange (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnApproveSubmit() As Variant
+&apos;&apos;&apos; The OnApproveSubmit property specifies the script to trigger when this event occurs
+ OnApproveSubmit = _PropertyGet(&quot;OnApproveSubmit&quot;)
+End Property &apos; SFDocuments.SF_Form.OnApproveSubmit (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnApproveSubmit(Optional ByVal pvOnApproveSubmit As Variant)
+&apos;&apos;&apos; Set the updatable property OnApproveSubmit
+ _PropertySet(&quot;OnApproveSubmit&quot;, pvOnApproveSubmit)
+End Property &apos; SFDocuments.SF_Form.OnApproveSubmit (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnConfirmDelete() As Variant
+&apos;&apos;&apos; The OnConfirmDelete property specifies the script to trigger when this event occurs
+ OnConfirmDelete = _PropertyGet(&quot;OnConfirmDelete&quot;)
+End Property &apos; SFDocuments.SF_Form.OnConfirmDelete (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnConfirmDelete(Optional ByVal pvOnConfirmDelete As Variant)
+&apos;&apos;&apos; Set the updatable property OnConfirmDelete
+ _PropertySet(&quot;OnConfirmDelete&quot;, pvOnConfirmDelete)
+End Property &apos; SFDocuments.SF_Form.OnConfirmDelete (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnCursorMoved() As Variant
+&apos;&apos;&apos; The OnCursorMoved property specifies the script to trigger when this event occurs
+ OnCursorMoved = _PropertyGet(&quot;OnCursorMoved&quot;)
+End Property &apos; SFDocuments.SF_Form.OnCursorMoved (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnCursorMoved(Optional ByVal pvOnCursorMoved As Variant)
+&apos;&apos;&apos; Set the updatable property OnCursorMoved
+ _PropertySet(&quot;OnCursorMoved&quot;, pvOnCursorMoved)
+End Property &apos; SFDocuments.SF_Form.OnCursorMoved (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnErrorOccurred() As Variant
+&apos;&apos;&apos; The OnErrorOccurred property specifies the script to trigger when this event occurs
+ OnErrorOccurred = _PropertyGet(&quot;OnErrorOccurred&quot;)
+End Property &apos; SFDocuments.SF_Form.OnErrorOccurred (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnErrorOccurred(Optional ByVal pvOnErrorOccurred As Variant)
+&apos;&apos;&apos; Set the updatable property OnErrorOccurred
+ _PropertySet(&quot;OnErrorOccurred&quot;, pvOnErrorOccurred)
+End Property &apos; SFDocuments.SF_Form.OnErrorOccurred (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnLoaded() As Variant
+&apos;&apos;&apos; The OnLoaded property specifies the script to trigger when this event occurs
+ OnLoaded = _PropertyGet(&quot;OnLoaded&quot;)
+End Property &apos; SFDocuments.SF_Form.OnLoaded (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnLoaded(Optional ByVal pvOnLoaded As Variant)
+&apos;&apos;&apos; Set the updatable property OnLoaded
+ _PropertySet(&quot;OnLoaded&quot;, pvOnLoaded)
+End Property &apos; SFDocuments.SF_Form.OnLoaded (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnReloaded() As Variant
+&apos;&apos;&apos; The OnReloaded property specifies the script to trigger when this event occurs
+ OnReloaded = _PropertyGet(&quot;OnReloaded&quot;)
+End Property &apos; SFDocuments.SF_Form.OnReloaded (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnReloaded(Optional ByVal pvOnReloaded As Variant)
+&apos;&apos;&apos; Set the updatable property OnReloaded
+ _PropertySet(&quot;OnReloaded&quot;, pvOnReloaded)
+End Property &apos; SFDocuments.SF_Form.OnReloaded (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnReloading() As Variant
+&apos;&apos;&apos; The OnReloading property specifies the script to trigger when this event occurs
+ OnReloading = _PropertyGet(&quot;OnReloading&quot;)
+End Property &apos; SFDocuments.SF_Form.OnReloading (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnReloading(Optional ByVal pvOnReloading As Variant)
+&apos;&apos;&apos; Set the updatable property OnReloading
+ _PropertySet(&quot;OnReloading&quot;, pvOnReloading)
+End Property &apos; SFDocuments.SF_Form.OnReloading (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnResetted() As Variant
+&apos;&apos;&apos; The OnResetted property specifies the script to trigger when this event occurs
+ OnResetted = _PropertyGet(&quot;OnResetted&quot;)
+End Property &apos; SFDocuments.SF_Form.OnResetted (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnResetted(Optional ByVal pvOnResetted As Variant)
+&apos;&apos;&apos; Set the updatable property OnResetted
+ _PropertySet(&quot;OnResetted&quot;, pvOnResetted)
+End Property &apos; SFDocuments.SF_Form.OnResetted (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnRowChanged() As Variant
+&apos;&apos;&apos; The OnRowChanged property specifies the script to trigger when this event occurs
+ OnRowChanged = _PropertyGet(&quot;OnRowChanged&quot;)
+End Property &apos; SFDocuments.SF_Form.OnRowChanged (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnRowChanged(Optional ByVal pvOnRowChanged As Variant)
+&apos;&apos;&apos; Set the updatable property OnRowChanged
+ _PropertySet(&quot;OnRowChanged&quot;, pvOnRowChanged)
+End Property &apos; SFDocuments.SF_Form.OnRowChanged (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnUnloaded() As Variant
+&apos;&apos;&apos; The OnUnloaded property specifies the script to trigger when this event occurs
+ OnUnloaded = _PropertyGet(&quot;OnUnloaded&quot;)
+End Property &apos; SFDocuments.SF_Form.OnUnloaded (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnUnloaded(Optional ByVal pvOnUnloaded As Variant)
+&apos;&apos;&apos; Set the updatable property OnUnloaded
+ _PropertySet(&quot;OnUnloaded&quot;, pvOnUnloaded)
+End Property &apos; SFDocuments.SF_Form.OnUnloaded (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnUnloading() As Variant
+&apos;&apos;&apos; The OnUnloading property specifies the script to trigger when this event occurs
+ OnUnloading = _PropertyGet(&quot;OnUnloading&quot;)
+End Property &apos; SFDocuments.SF_Form.OnUnloading (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnUnloading(Optional ByVal pvOnUnloading As Variant)
+&apos;&apos;&apos; Set the updatable property OnUnloading
+ _PropertySet(&quot;OnUnloading&quot;, pvOnUnloading)
+End Property &apos; SFDocuments.SF_Form.OnUnloading (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OrderBy() As Variant
+&apos;&apos;&apos; The OrderBy property specifies in which order the records should be displayed.
+ OrderBy = _PropertyGet(&quot;OrderBy&quot;)
+End Property &apos; SFDocuments.SF_Form.OrderBy (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OrderBy(Optional ByVal pvOrderBy As Variant)
+&apos;&apos;&apos; Set the updatable property OrderBy
+ _PropertySet(&quot;OrderBy&quot;, pvOrderBy)
+End Property &apos; SFDocuments.SF_Form.OrderBy (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Parent() As Object
+&apos;&apos;&apos; Return the Parent of the actual Form
+ Parent = _PropertyGet(&quot;Parent&quot;)
+End Property &apos; SFDocuments.SF_Form.Parent
+
+REM -----------------------------------------------------------------------------
+Property Get RecordSource() As Variant
+&apos;&apos;&apos; The RecordSource property specifies the source of the data,
+&apos;&apos;&apos; a table name, a query name or a SQL statement
+ RecordSource = _PropertyGet(&quot;RecordSource&quot;)
+End Property &apos; SFDocuments.SF_Form.RecordSource (get)
+
+REM -----------------------------------------------------------------------------
+Property Let RecordSource(Optional ByVal pvRecordSource As Variant)
+&apos;&apos;&apos; Set the updatable property RecordSource
+ _PropertySet(&quot;RecordSource&quot;, pvRecordSource)
+End Property &apos; SFDocuments.SF_Form.RecordSource (let)
+
+REM -----------------------------------------------------------------------------
+Property Get XForm() As Object
+&apos;&apos;&apos; The XForm property returns the XForm UNO object of the Form
+ XForm = _PropertyGet(&quot;XForm&quot;)
+End Property &apos; SFDocuments.SF_Form.XForm (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 Base form document
+&apos;&apos;&apos; If the parent document is ...
+&apos;&apos;&apos; Calc Activate the corresponding sheet
+&apos;&apos;&apos; Writer Activate the parent document
+&apos;&apos;&apos; Base Activate the parent form document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if focusing is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myForm.Activate()
+
+Dim bActivate As Boolean &apos; Return value
+Dim oContainer As Object &apos; com.sun.star.awt.XWindow
+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:
+ Select Case _FormType
+ Case ISDOCFORM : bActivate = [_Parent].Activate()
+ Case ISCALCFORM : bActivate = [_Parent].Activate(_SheetName)
+ Case ISBASEFORM
+ Set oContainer = _FormDocument.Component.CurrentController.Frame.ContainerWindow
+ With oContainer
+ If .isVisible() = False Then .setVisible(True)
+ .IsMinimized = False
+ .setFocus()
+ .toFront() &apos; Force window change in Linux
+ Wait 1 &apos; Bypass desynchro issue in Linux
+ End With
+ bActivate = True
+ End Select
+
+Finally:
+ Activate = bActivate
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function CloseFormDocument() As Boolean
+&apos;&apos;&apos; Close the form document containing the actual form instance
+&apos;&apos;&apos; The form instance is disposed
+&apos;&apos;&apos; The method does nothing if the actual form is not located in a Base form document
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if closure is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myForm.CloseFormDocument()
+
+Dim bClose As Boolean &apos; Return value
+Dim oContainer As Object &apos; com.sun.star.awt.XWindow
+Const cstThisSub = &quot;SFDocuments.Form.CloseFormDocument&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:
+ Select Case _FormType
+ Case ISDOCFORM, ISCALCFORM, ISSUBFORM
+ Case ISBASEFORM
+ _FormDocument.close()
+ Dispose()
+ bClose = True
+ End Select
+
+Finally:
+ CloseFormDocument = bClose
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.CloseFormDocument
+
+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 = myDoc.Forms(&quot;myForm&quot;)
+&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
+Dim i As Long
+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:
+ &apos; Collect all control names if not yet done
+ If UBound(_ControlNames) &lt; 0 Then
+ _ControlNames = _Form.getElementNames()
+ &apos; Remove all subforms from the list
+ For i = 0 To UBound(_ControlNames)
+ &apos; Subforms have no ClassId property
+ If Not ScriptForge.SF_Session.HasUnoProperty(_Form.getByName(_ControlNames(i)), &quot;ClassId&quot;) Then _ControlNames(i) = &quot;&quot;
+ Next i
+ _ControlNames = ScriptForge.SF_Array.TrimArray(_ControlNames)
+ &apos; Size the cache accordingly
+ If UBound(_ControlNames) &gt;= 0 Then
+ ReDim _ControlCache(0 To UBound(_ControlNames))
+ End If
+ End If
+
+ &apos; Return the list of controls or a FormControl instance
+ If Len(ControlName) = 0 Then
+ Controls = _ControlNames
+
+ Else
+
+ If Not _Form.hasByName(ControlName) Then GoTo CatchNotFound
+ lIndexOfNames = ScriptForge.SF_Array.IndexOf(_ControlNames, ControlName, CaseSensitive := True)
+ &apos; Reuse cache when relevant
+ vControl = _ControlCache(lIndexOfNames)
+
+ If IsEmpty(vControl) Then
+ &apos; Create the new form control class instance
+ Set oControl = New SF_FormControl
+ With oControl
+ ._Name = ControlName
+ Set .[Me] = oControl
+ Set .[_Parent] = [Me]
+ Set ._ParentForm = [Me]
+ ._IndexOfNames = lIndexOfNames
+ ._FormName = _Name
+ &apos; Get model and view of the current control
+ Set ._ControlModel = _Form.getByName(ControlName)
+ ._Initialize()
+ End With
+ Else
+ Set oControl = vControl
+ End If
+
+ Set Controls = oControl
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchNotFound:
+ ScriptForge.SF_Utils._Validate(ControlName, &quot;ControlName&quot;, V_STRING, _Form.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 main 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 _FormType = ISSUBFORM Then
+ Set _Database = [_Parent].GetDatabase() &apos; Recursive call, climb the tree
+ 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 registered 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 Form service as an array
+
+ Methods = Array( _
+ &quot;Activate&quot; _
+ , &quot;CloseForm&quot; _
+ , &quot;Controls&quot; _
+ , &quot;GetDatabase&quot; _
+ , &quot;MoveFirst&quot; _
+ , &quot;MoveLast&quot; _
+ , &quot;MoveNew&quot; _
+ , &quot;MoveNext&quot; _
+ , &quot;MovePrevious&quot; _
+ , &quot;Requery&quot; _
+ , &quot;SubForms&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Form.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function MoveFirst() As Boolean
+&apos;&apos;&apos; The cursor is (re)positioned on the first row
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if cursor move is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myForm.MoveFirst()
+
+Dim bMoveFirst As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDocuments.Form.MoveFirst&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bMoveFirst = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ End If
+Try:
+ With _Form
+ bMoveFirst = .first()
+ End With
+
+Finally:
+ MoveFirst = bMoveFirst
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.MoveFirst
+
+REM -----------------------------------------------------------------------------
+Public Function MoveLast() As Boolean
+&apos;&apos;&apos; The cursor is (re)positioned on the last row
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if cursor move is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myForm.MoveLast()
+
+Dim bMoveLast As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDocuments.Form.MoveLast&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bMoveLast = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ End If
+Try:
+ With _Form
+ bMoveLast = .last()
+ End With
+
+Finally:
+ MoveLast = bMoveLast
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.MoveLast
+
+REM -----------------------------------------------------------------------------
+Public Function MoveNew() As Boolean
+&apos;&apos;&apos; The cursor is (re)positioned in the new record area
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if cursor move is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myForm.MoveNew()
+
+Dim bMoveNew As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDocuments.Form.MoveNew&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bMoveNew = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ End If
+Try:
+ With _Form
+ .last() &apos; To simulate the behaviour in the UI
+ .moveToInsertRow()
+ End With
+ bMoveNew = True
+
+Finally:
+ MoveNew = bMoveNew
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.MoveNew
+
+REM -----------------------------------------------------------------------------
+Public Function MoveNext(Optional ByVal Offset As Variant) As Boolean
+&apos;&apos;&apos; The cursor is (re)positioned on the next row
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Offset: The number of records to go forward (default = 1)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if cursor move is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myForm.MoveNext()
+
+Dim bMoveNext As Boolean &apos; Return value
+Dim lOffset As Long &apos; Alias of Offset
+Const cstThisSub = &quot;SFDocuments.Form.MoveNext&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bMoveNext = False
+
+Check:
+ If IsMissing(Offset) Or IsEmpty(Offset) Then Offset = 1
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Offset, &quot;Offset&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+Try:
+ lOffset = CLng(Offset) &apos; To be sure to have the right argument type
+ With _Form
+ If lOffset = 1 Then bMoveNext = .next() Else bMoveNext = .relative(lOffset)
+ End With
+
+Finally:
+ MoveNext = bMoveNext
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.MoveNext
+
+REM -----------------------------------------------------------------------------
+Public Function MovePrevious(Optional ByVal Offset As Variant) As Boolean
+&apos;&apos;&apos; The cursor is (re)positioned on the previous row
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Offset: The number of records to go backward (default = 1)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if cursor move is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myForm.MovePrevious()
+
+Dim bMovePrevious As Boolean &apos; Return value
+Dim lOffset As Long &apos; Alias of Offset
+Const cstThisSub = &quot;SFDocuments.Form.MovePrevious&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bMovePrevious = False
+
+Check:
+ If IsMissing(Offset) Or IsEmpty(Offset) Then Offset = 1
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Offset, &quot;Offset&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ End If
+Try:
+ lOffset = CLng(Offset) &apos; To be sure to have the right argument type
+ With _Form
+ If lOffset = 1 Then bMovePrevious = .previous() Else bMovePrevious = .relative(-lOffset)
+ End With
+
+Finally:
+ MovePrevious = bMovePrevious
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.MovePrevious
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Form class as an array
+
+ Properties = Array( _
+ &quot;AllowDeletes&quot; _
+ , &quot;AllowInserts&quot; _
+ , &quot;AllowUpdates&quot; _
+ , &quot;BaseForm&quot; _
+ , &quot;Bookmark&quot; _
+ , &quot;CurrentRecord&quot; _
+ , &quot;Filter&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;Parent&quot; _
+ , &quot;RecordSource&quot; _
+ , &quot;XForm&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Form.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function Requery() As Boolean
+&apos;&apos;&apos; Reload from the database the actual data into the form
+&apos;&apos;&apos; The cursor is (re)positioned on the first row
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if requery is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; myForm.Requery()
+
+Dim bRequery As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDocuments.Form.Requery&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bRequery = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ End If
+Try:
+ With _Form
+ If .isLoaded() Then .reload() Else .load()
+ End With
+ bRequery = True
+
+Finally:
+ Requery = bRequery
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.Requery
+
+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 -----------------------------------------------------------------------------
+Public Function Subforms(Optional ByVal Subform As Variant) As Variant
+&apos;&apos;&apos; Return either
+&apos;&apos;&apos; - the list of the subforms contained in the actual form or subform instance
+&apos;&apos;&apos; - a SFDocuments.Form object based on its name or its index in the alphabetic list of subforms
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Subform: a subform stored in the parent form given by its name or its index
+&apos;&apos;&apos; When absent, the list of available subforms is returned
+&apos;&apos;&apos; To get the first (unique ?) subform stored in the parent form, set Subform = 0
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; SUBFORMNOTFOUNDERROR Subform not found
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A zero-based array of strings if Subform is absent
+&apos;&apos;&apos; An instance of the SF_Form class if Subform exists
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim myForm As Object, myList As Variant, mySubform As Object
+&apos;&apos;&apos; myList = myForm.Subforms()
+&apos;&apos;&apos; Set mySubform = myForm.Subforms(&quot;mySubform&quot;)
+
+Dim oSubform As Object &apos; The new Form class instance
+Dim oXSubform As Object &apos; com.sun.star.form.XForm
+Dim vSubformNames As Variant &apos; Array of subform names
+Dim i As Long
+Const cstDrawPage = 0 &apos; Only 1 drawpage in a Writer document
+
+Const cstThisSub = &quot;SFDocuments.Form.Subforms&quot;
+Const cstSubArgs = &quot;[Subform=&quot;&quot;&quot;&quot;]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(Subform) Or IsEmpty(Subform) Then Subform = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Subform, &quot;Subform&quot;, Array(V_STRING, ScriptForge.V_NUMERIC)) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Collect all control names and retain only the subforms
+ vSubformNames = _Form.getElementNames()
+ For i = 0 To UBound(vSubformNames)
+ Set oSubform = _Form.getByName(vSubformNames(i))
+ &apos; Subforms are the only control types having no ClassId property
+ If ScriptForge.SF_Session.HasUnoProperty(oSubform, &quot;ClassId&quot;) Then vSubformNames(i) = &quot;&quot;
+ Next i
+ vSubformNames = ScriptForge.SF_Array.TrimArray(vSubformNames)
+
+ If Len(Subform) = 0 Then &apos; Return the list of valid subform names
+ Subforms = vSubformNames
+ Else
+ If VarType(Subform) = V_STRING Then &apos; Find the form by name
+ If Not ScriptForge.SF_Array.Contains(vSubformNames, Subform, CaseSensitive := True) Then GoTo CatchNotFound
+ Set oXSubform = _Form.getByName(Subform)
+ Else &apos; Find the form by index
+ If Subform &lt; 0 Or Subform &gt; UBound(vSubformNames) Then GoTo CatchNotFound
+ Set oXSubform = _Form.getByName(vSubformNames(Subform))
+ End If
+ &apos; Create the new Form class instance
+ Set oSubform = SF_Register._NewForm(oXSubform)
+ With oSubform
+ Set .[_Parent] = [Me]
+ ._FormType = ISSUBFORM
+ Set ._Component = _Component
+ Set ._FormDocument = _FormDocument
+ ._SheetName = _SheetName
+ ._FormDocumentName = _FormDocumentName
+ Set ._Database = _Database
+ ._Initialize()
+ End With
+ Set Subforms = oSubform
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchNotFound:
+ ScriptForge.SF_Exception.RaiseFatal(SUBFORMNOTFOUNDERROR, Subform, _Name)
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Form.Subforms
+
+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 UCase(&quot;OnApproveCursorMove&quot;)
+ _GetListener = &quot;XRowSetApproveListener&quot;
+ Case UCase(&quot;OnApproveParameter&quot;)
+ _GetListener = &quot;XDatabaseParameterListener&quot;
+ Case UCase(&quot;OnApproveReset&quot;), UCase(&quot;OnResetted&quot;)
+ _GetListener = &quot;XResetListener&quot;
+ Case UCase(&quot;OnApproveRowChange&quot;)
+ _GetListener = &quot;XRowSetApproveListener&quot;
+ Case UCase(&quot;OnApproveSubmit&quot;)
+ _GetListener = &quot;XSubmitListener&quot;
+ Case UCase(&quot;OnConfirmDelete&quot;)
+ _GetListener = &quot;XConfirmDeleteListener&quot;
+ Case UCase(&quot;OnCursorMoved&quot;), UCase(&quot;OnRowChanged&quot;)
+ _GetListener = &quot;XRowSetListener&quot;
+ Case UCase(&quot;OnErrorOccurred&quot;)
+ _GetListener = &quot;XSQLErrorListener&quot;
+ Case UCase(&quot;OnLoaded&quot;), UCase(&quot;OnReloaded&quot;), UCase(&quot;OnReloading&quot;), UCase(&quot;OnUnloaded&quot;), UCase(&quot;OnUnloading&quot;)
+ _GetListener = &quot;XLoadListener&quot;
+ End Select
+
+End Function &apos; SFDocuments.SF_Form._GetListener
+
+REM -----------------------------------------------------------------------------
+Private Sub _GetParents()
+&apos;&apos;&apos; When the current instance is created top-down, the parents are completely defined
+&apos;&apos;&apos; and nothing should be done in this method
+&apos;&apos;&apos; When the a class instance is created in a (form/control) event, it is the opposite
+&apos;&apos;&apos; The current method rebuilds the missing members in the instance from the bottom
+&apos;&apos;&apos; Members potentially to collect are:
+&apos;&apos;&apos; - _FormType
+&apos;&apos;&apos; - [_Parent], the immediate parent: a form or a document instance
+&apos;&apos;&apos; + Only when the _FormType is a main form
+&apos;&apos;&apos; - _SheetName (Calc only)
+&apos;&apos;&apos; - _FormDocumentName (Base only)
+&apos;&apos;&apos; - _FormDocument, the topmost form collection
+&apos;&apos;&apos; - _Component, the containing document
+&apos;&apos;&apos; They must be identified only starting from the _Form UNO object
+&apos;&apos;&apos;
+&apos;&apos;&apos; The method is called from the _Initialize() method at instance creation
+
+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 oSession As Object : Set oSession = ScriptForge.SF_Session
+
+ On Local Error GoTo Finally &apos; Being probably called from events, this method should avoid failures
+ &apos; When the form type is known, the upper part of the branch is not scanned
+ If _FormType &lt;&gt; ISUNDEFINED Then GoTo Finally
+
+Try:
+ &apos; The whole branch is scanned bottom-up
+ If oSession.HasUnoProperty(_Form, &quot;Parent&quot;) Then Set oParent = _Form.Parent Else Set oParent = Nothing
+ _FormType = ISUNDEFINED
+ iLevel = 1
+
+ Do While Not IsNull(oParent)
+ sType = SF_Session.UnoObjectType(oParent)
+ Select Case sType
+ &apos; Collect at each level the needed info
+ Case &quot;com.sun.star.comp.forms.ODatabaseForm&quot; &apos; The parent _Form of a subform
+ If iLevel = 1 Then
+ _FormType = ISSUBFORM
+ Set [_Parent] = SF_Register._NewForm(oParent)
+ &apos; Everything is in the parent, copy items and stop scan
+ [_Parent]._Initialize() &apos; Current method is called recursively here
+ With [_Parent]
+ _SheetName = ._SheetName
+ _FormDocumentName = ._FormDocumentName
+ Set _FormDocument = ._FormDocument
+ Set _Component = ._Component
+ End With
+ Exit Sub
+ End If
+ 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;)
+ 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
+ Case &quot;ScModelObj&quot; &apos; The parent document: a Calc document
+ _FormType = ISCALCFORM
+ Set [_Parent] = ScriptForge.SF_Services.CreateScriptService(&quot;SFDocuments.Document&quot;, oParent)
+ Set _Component = oParent
+ &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
+ iLevel = iLevel + 1
+ Loop
+
+Finally:
+ Exit Sub
+End Sub &apos; SFDocuments.SF_Form._GetParents
+
+REM -----------------------------------------------------------------------------
+Public Sub _Initialize()
+&apos;&apos;&apos; Achieve the creation of a SF_Form instance
+&apos;&apos;&apos; - complete the missing private members
+&apos;&apos;&apos; - store the new instance in the cache
+
+ _GetParents()
+ _CacheIndex = SF_Register._AddFormToCache(_Form, [Me])
+
+End Sub &apos; SFDocuments.SF_Form._Initialize
+
+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 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
+Dim sName As String &apos; Alias of _Name
+Dim sId As String &apos; Alias of FileIdent
+
+Check:
+ On Local Error GoTo Catch &apos; Anticipate DisposedException errors or alike
+ If IsMissing(pbError) Then pbError = True
+
+Try:
+ &apos; At main form termination, all database connections are lost
+ bAlive = Not IsNull(_Form)
+ If Not bAlive Then GoTo Catch
+
+Finally:
+ _IsStillAlive = bAlive
+ Exit Function
+Catch:
+ bAlive = False
+ On Error GoTo 0
+ &apos; Keep error message elements before disposing the instance
+ sName = _SheetName &amp; _FormDocumentName &apos; At least one of them is a zero-length string
+ sName = Iif(Len(sName) &gt; 0, &quot;[&quot; &amp; sName &amp; &quot;].&quot;, &quot;&quot;) &amp; _Name
+ If Not IsNull(_Component) Then sId = _Component.Location Else sId = &quot;&quot;
+ &apos; Dispose the actual forms instance
+ Dispose()
+ &apos; Display error message
+ If pbError Then ScriptForge.SF_Exception.RaiseFatal(FORMDEADERROR, sName, sId)
+ 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 vBookmark As Variant &apos; Form bookmark
+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)
+ _PropertyGet = Empty
+ 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;AllowDeletes&quot;)
+ If Not IsNull(_Form) Then _PropertyGet = _Form.AllowDeletes
+ Case UCase(&quot;AllowInserts&quot;)
+ If Not IsNull(_Form) Then _PropertyGet = _Form.AllowInserts
+ Case UCase(&quot;AllowUpdates&quot;)
+ If Not IsNull(_Form) Then _PropertyGet = _Form.AllowUpdates
+ Case UCase(&quot;BaseForm&quot;)
+ _PropertyGet = _FormDocumentName
+ Case UCase(&quot;Bookmark&quot;)
+ If IsNull(_Form) Then
+ _PropertyGet = 0
+ Else
+ On Local Error Resume Next &apos; Disable error handler because bookmarking does not always react well in events ...
+ If _Form.IsBookmarkable Then vBookmark = _Form.getBookmark() Else vBookmark = Nothing
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error Goto Catch Else On Local Error Goto 0
+ If IsNull(vBookmark) Then Goto Catch
+ _PropertyGet = vBookmark
+ End If
+ Case UCase(&quot;CurrentRecord&quot;)
+ If IsNull(_Form) Then _PropertyGet = 0 Else _PropertyGet = _Form.Row
+ Case UCase(&quot;Filter&quot;)
+ If IsNull(_Form) Then _PropertyGet = &quot;&quot; Else _PropertyGet = _Form.Filter
+ Case UCase(&quot;LinkChildFields&quot;)
+ If IsNull(_Form) Or _FormType &lt;&gt; ISSUBFORM Then _PropertyGet = Array() Else _PropertyGet = _Form.DetailFields
+ Case UCase(&quot;LinkParentFields&quot;)
+ If IsNull(_Form) Or _FormType &lt;&gt; ISSUBFORM Then _PropertyGet = Array() Else _PropertyGet = _Form.MasterFields
+ Case UCase(&quot;Name&quot;)
+ _PropertyGet = _Name
+ Case UCase(&quot;OnApproveCursorMove&quot;), UCase(&quot;OnApproveParameter&quot;), UCase(&quot;OnApproveReset&quot;), UCase(&quot;OnApproveRowChange&quot;) _
+ , UCase(&quot;OnApproveSubmit&quot;), UCase(&quot;OnConfirmDelete&quot;), UCase(&quot;OnCursorMoved&quot;), UCase(&quot;OnErrorOccurred&quot;) _
+ , UCase(&quot;OnLoaded&quot;), UCase(&quot;OnReloaded&quot;), UCase(&quot;OnReloading&quot;), UCase(&quot;OnResetted&quot;), UCase(&quot;OnRowChanged&quot;) _
+ , UCase(&quot;OnUnloaded&quot;), UCase(&quot;OnUnloading&quot;)
+ If IsNull(_Form) Then _PropertyGet = &quot;&quot; Else _PropertyGet = SF_Register._GetEventScriptCode(_Form, psProperty, _Name)
+ Case UCase(&quot;OrderBy&quot;)
+ If IsNull(_Form) Then _PropertyGet = &quot;&quot; Else _PropertyGet = _Form.Order
+ Case UCase(&quot;Parent&quot;)
+ _PropertyGet = [_Parent]
+ Case UCase(&quot;RecordSource&quot;)
+ If IsNull(_Form) Then _PropertyGet = &quot;&quot; Else _PropertyGet = _Form.Command
+ Case UCase(&quot;XForm&quot;)
+ Set _PropertyGet = _Form
+ 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
+Dim oDatabase As Object &apos; Database class instance
+Dim lCommandType As Long &apos; Record source type: 0 = Table, 1 = Query, 2 = SELECT
+Dim sCommand As String &apos; Record source
+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;AllowDeletes&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;AllowDeletes&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not IsNull(_Form) Then
+ _Form.AllowDeletes = pvValue
+ _Form.reload()
+ End If
+ Case UCase(&quot;AllowInserts&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;AllowInserts&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not IsNull(_Form) Then
+ _Form.AllowInserts = pvValue
+ _Form.reload()
+ End If
+ Case UCase(&quot;AllowUpdates&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;AllowUpdates&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If Not IsNull(_Form) Then
+ _Form.AllowUpdates = pvValue
+ _Form.reload()
+ End If
+ Case UCase(&quot;Bookmark&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Bookmark&quot;, Array(ScriptForge.V_NUMERIC, ScriptForge.V_OBJECT)) Then GoTo Finally
+ If Not IsNull(pvValue) And Not IsNull(_Form) Then bSet = _Form.moveToBookmark(pvValue)
+ Case UCase(&quot;CurrentRecord&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;CurrentRecord&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If Not IsNull(_Form) Then bSet = _Form.absolute(pvValue)
+ Case UCase(&quot;Filter&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Filter&quot;, V_STRING) Then GoTo Finally
+ If Not IsNull(_Form) Then
+ With _Form
+ If Len(pvValue) &gt; 0 Then
+ Set oDatabase = GetDatabase()
+ If Not IsNull(oDatabase) Then .Filter = oDatabase._ReplaceSquareBrackets(pvValue) Else .Filter = pvValue
+ Else
+ .Filter = &quot;&quot;
+ End If
+ .ApplyFilter = True
+ .reload()
+ End With
+ End If
+ Case UCase(&quot;OnApproveCursorMove&quot;), UCase(&quot;OnApproveParameter&quot;), UCase(&quot;OnApproveReset&quot;), UCase(&quot;OnApproveRowChange&quot;) _
+ , UCase(&quot;OnApproveSubmit&quot;), UCase(&quot;OnConfirmDelete&quot;), UCase(&quot;OnCursorMoved&quot;), UCase(&quot;OnErrorOccurred&quot;) _
+ , UCase(&quot;OnLoaded&quot;), UCase(&quot;OnReloaded&quot;), UCase(&quot;OnReloading&quot;), UCase(&quot;OnResetted&quot;), UCase(&quot;OnRowChanged&quot;) _
+ , UCase(&quot;OnUnloaded&quot;), UCase(&quot;OnUnloading&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, psProperty, V_STRING) Then Goto Finally
+ If Not IsNull(_Form) Then
+ bSet = SF_Register._RegisterEventScript(_Form _
+ , psProperty _
+ , _GetListener(psProperty) _
+ , pvValue _
+ , _Name _
+ )
+ End If
+ Case UCase(&quot;OrderBy&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;OrderBy&quot;, V_STRING) Then GoTo Finally
+ If Not IsNull(_Form) Then
+ With _Form
+ If Len(pvValue) &gt; 0 Then
+ Set oDatabase = GetDatabase()
+ If Not IsNull(oDatabase) Then .Order = oDatabase._ReplaceSquareBrackets(pvValue) Else .Order = pvValue
+ Else
+ .Order = &quot;&quot;
+ End If
+ .reload()
+ End With
+ End If
+ Case UCase(&quot;RecordSource&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;RecordSource&quot;, V_STRING) Then GoTo Finally
+ If Not IsNull(_Form) And Len(pvValue) &gt; 0 Then
+ Set oDatabase = GetDatabase()
+ If Not IsNull(oDatabase) Then
+ With oDatabase
+ If ScriptForge.SF_Array.Contains(.Tables, pvValue, CaseSensitive := True) Then
+ sCommand = pvValue
+ lCommandType = com.sun.star.sdb.CommandType.TABLE
+ ElseIf ScriptForge.SF_Array.Contains(.Queries, pvValue, CaseSensitive := True) Then
+ sCommand = pvValue
+ lCommandType = com.sun.star.sdb.CommandType.QUERY
+ ElseIf ScriptForge.SF_String.StartsWith(pvValue, &quot;SELECT&quot;, CaseSensitive := False) Then
+ sCommand = .ReplaceSquareBrackets(pvValue)
+ lCommandType = com.sun.star.sdb.CommandType.COMMAND
+ End If
+ _Form.Command = sCommand
+ _Form.CommandType = lCommandType
+ End With
+ End If
+ End If
+ 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;
+
+Dim sParent As String &apos; To recognize the parent
+
+ sParent = _SheetName &amp; _FormDocumentName &apos; At least one of them is a zero-length string
+ _Repr = &quot;[Form]: &quot; &amp; Iif(Len(sParent) &gt; 0, sParent &amp; &quot;...&quot;, &quot;&quot;) &amp; _Name
+
+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_FormControl.xba b/wizards/source/sfdocuments/SF_FormControl.xba
new file mode 100644
index 000000000..a48c22b6c
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_FormControl.xba
@@ -0,0 +1,1888 @@
+<?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_FormControl" 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_FormControl
+&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
+&apos;&apos;&apos; a unique name. This is also true for the individual radio buttons belonging to the same group.
+&apos;&apos;&apos; A common group name must identify such a single group.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The focus is clearly set on getting and setting the values displayed by the controls of the form,
+&apos;&apos;&apos; not on their formatting. The latter is easily accessible via the XControlModel and XControlView
+&apos;&apos;&apos; UNO objects.
+&apos;&apos;&apos; Essentially a single property &quot;Value&quot; maps many alternative UNO properties depending each on
+&apos;&apos;&apos; the control type.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocations:
+&apos;&apos;&apos; Dim myForm As Object, myControl As Object
+&apos;&apos;&apos; Set myForm = ... (read the comments in the SF_Form module)
+&apos;&apos;&apos; Set myControl = myForm.Controls(&quot;myTextBox&quot;)
+&apos;&apos;&apos; myControl.Value = &quot;Current time = &quot; &amp; Now()
+&apos;&apos;&apos;
+&apos;&apos;&apos; REM the control is the subject of an event
+&apos;&apos;&apos; Sub OnEvent(ByRef poEvent As Object)
+&apos;&apos;&apos; Dim myControl As Object
+&apos;&apos;&apos; Set myControl = CreateScriptService(&quot;SFDocuments.FormEvent&quot;, poEvent)
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_formcontrol.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 FORMCONTROLTYPEERROR = &quot;FORMCONTROLTYPEERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object
+Private ObjectType As String &apos; Must be FORMCONTROL
+Private ServiceName As String
+
+&apos; Control naming and context
+Private _Name As String
+Private _IndexOfNames As Long &apos; Index in ElementNames array. Used to access SF_Form._ControlCache
+Private _FormName As String &apos; Parent form name
+Private _ParentForm As Object &apos; Parent form or subform instance
+Private _ParentIsTable As Boolean &apos; True when parent is a table control
+
+&apos; Control UNO references
+Private _ControlModel As Object &apos; com.sun.star.awt.XControlModel
+Private _ControlView As Object &apos; com.sun.star.awt.XControl - stardiv.Toolkit.UnoDialogControl
+
+&apos; Control attributes
+Private _ImplementationName As String
+Private _ControlType As String &apos; One of the CTLxxx constants
+Private _ClassId As Integer &apos; Numerical type of control
+
+&apos; Cache storage for table controls
+Private _ControlNames As Variant &apos; Array of control names
+Private _ControlCache As Variant &apos; Array of control objects sorted like ElementNames of XControlModel
+
+REM ============================================================ MODULE CONSTANTS
+
+&apos; ClassId
+Private Const CTLBUTTON = &quot;Button&quot; &apos; 2
+Private Const CTLCHECKBOX = &quot;CheckBox&quot; &apos; 5
+Private Const CTLCOMBOBOX = &quot;ComboBox&quot; &apos; 7
+Private Const CTLCURRENCYFIELD = &quot;CurrencyField&quot; &apos; 18
+Private Const CTLDATEFIELD = &quot;DateField&quot; &apos; 15
+Private Const CTLFILECONTROL = &quot;FileControl&quot; &apos; 12
+Private Const CTLFIXEDTEXT = &quot;FixedText&quot; &apos; 10
+Private Const CTLFORMATTEDFIELD = &quot;FormattedField&quot; &apos; Idem TextField
+Private Const CTLGROUPBOX = &quot;GroupBox&quot; &apos; 8
+Private Const CTLHIDDENCONTROL = &quot;HiddenControl&quot; &apos; 13
+Private Const CTLIMAGEBUTTON = &quot;ImageButton&quot; &apos; 4
+Private Const CTLIMAGECONTROL = &quot;ImageControl&quot; &apos; 14
+Private Const CTLLISTBOX = &quot;ListBox&quot; &apos; 6
+Private Const CTLNAVIGATIONBAR = &quot;NavigationBar&quot; &apos; 22
+Private Const CTLNUMERICFIELD = &quot;NumericField&quot; &apos; 17
+Private Const CTLPATTERNFIELD = &quot;PatternField&quot; &apos; 19
+Private Const CTLRADIOBUTTON = &quot;RadioButton&quot; &apos; 3
+Private Const CTLSCROLLBAR = &quot;ScrollBar&quot; &apos; 20
+Private Const CTLSPINBUTTON = &quot;SpinButton&quot; &apos; 21
+Private Const CTLTABLECONTROL = &quot;TableControl&quot; &apos; 11
+Private Const CTLTEXTFIELD = &quot;TextField&quot; &apos; 9
+Private Const CTLTIMEFIELD = &quot;TimeField&quot; &apos; 16
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ ObjectType = &quot;FORMCONTROL&quot;
+ ServiceName = &quot;SFDocuments.FormControl&quot;
+ _Name = &quot;&quot;
+ _IndexOfNames = -1
+ _FormName = &quot;&quot;
+ _ParentIsTable = False
+ Set _ParentForm = Nothing
+ Set _ControlModel = Nothing
+ Set _ControlView = Nothing
+ _ImplementationName = &quot;&quot;
+ _ControlType = &quot;&quot;
+ _ClassId = 0
+ _ControlNames = Array()
+ _ControlCache = Array()
+End Sub &apos; SFDocuments.SF_FormControl Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDocuments.SF_FormControl Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ If Not IsNull([_Parent]) And _IndexOfNames &gt;= 0 Then [_Parent]._ControlCache(_IndexOfNames) = Empty
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDocuments.SF_FormControl Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get Action() As Variant
+&apos;&apos;&apos; The Action property specifies the action triggered when the button is clicked
+&apos;&apos;&apos; Accepted values: none, submitForm, resetForm, refreshForm, moveToFirst, moveToLast,
+&apos;&apos;&apos; moveToNext, moveToPrev, saveRecord, moveToNew, deleteRecord, undoRecord
+ Action = _PropertyGet(&quot;Action&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.Action (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Action(Optional ByVal pvAction As Variant)
+&apos;&apos;&apos; Set the updatable property Action
+ _PropertySet(&quot;Action&quot;, pvAction)
+End Property &apos; SFDocuments.SF_FormControl.Action (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Caption() As Variant
+&apos;&apos;&apos; The Caption property refers to the text associated with the control
+ Caption = _PropertyGet(&quot;Caption&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.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; SFDocuments.SF_FormControl.Caption (let)
+
+REM -----------------------------------------------------------------------------
+Property Get ControlSource() As Variant
+&apos;&apos;&apos; The ControlSource property specifies the rowset field mapped onto the actual control
+ ControlSource = _PropertyGet(&quot;ControlSource&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.ControlSource (get)
+
+REM -----------------------------------------------------------------------------
+Property Get ControlType() As String
+&apos;&apos;&apos; Return the type of the actual control: &quot;CheckBox&quot;, &quot;TextField&quot;, &quot;DateField&quot;, ...
+ ControlType = _PropertyGet(&quot;ControlType&quot;)
+End Property &apos; SFDocuments.SF_FormControl.ControlType
+
+REM -----------------------------------------------------------------------------
+Property Get Default() As Variant
+&apos;&apos;&apos; The Default property specifies whether a command button is the default (OK) button.
+ Default = _PropertyGet(&quot;Default&quot;, False)
+End Property &apos; SFDocuments.SF_FormControl.Default (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Default(Optional ByVal pvDefault As Variant)
+&apos;&apos;&apos; Set the updatable property Default
+ _PropertySet(&quot;Default&quot;, pvDefault)
+End Property &apos; SFDocuments.SF_FormControl.Default (let)
+
+REM -----------------------------------------------------------------------------
+Property Get DefaultValue() As Variant
+&apos;&apos;&apos; The DefaultValue property specifies how the control is initialized in a new record
+ DefaultValue = _PropertyGet(&quot;DefaultValue&quot;, Null)
+End Property &apos; SFDocuments.SF_FormControl.DefaultValue (get)
+
+REM -----------------------------------------------------------------------------
+Property Let DefaultValue(Optional ByVal pvDefaultValue As Variant)
+&apos;&apos;&apos; Set the updatable property DefaultValue
+ _PropertySet(&quot;DefaultValue&quot;, pvDefaultValue)
+End Property &apos; SFDocuments.SF_FormControl.DefaultValue (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Enabled() As Variant
+&apos;&apos;&apos; The Enabled property specifies if the control is accessible with the cursor.
+ Enabled = _PropertyGet(&quot;Enabled&quot;, False)
+End Property &apos; SFDocuments.SF_FormControl.Enabled (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Enabled(Optional ByVal pvEnabled As Variant)
+&apos;&apos;&apos; Set the updatable property Enabled
+ _PropertySet(&quot;Enabled&quot;, pvEnabled)
+End Property &apos; SFDocuments.SF_FormControl.Enabled (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Format() As Variant
+&apos;&apos;&apos; The Format property specifies the format in which to display dates and times.
+ Format = _PropertyGet(&quot;Format&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.Format (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Format(Optional ByVal pvFormat As Variant)
+&apos;&apos;&apos; Set the updatable property Format
+&apos;&apos;&apos; NB: Format is read-only for formatted field controls
+ _PropertySet(&quot;Format&quot;, pvFormat)
+End Property &apos; SFDocuments.SF_FormControl.Format (let)
+
+REM -----------------------------------------------------------------------------
+Property Get ListCount() As Long
+&apos;&apos;&apos; The ListCount property specifies the number of rows in a list box or a combo box
+ ListCount = _PropertyGet(&quot;ListCount&quot;, 0)
+End Property &apos; SFDocuments.SF_FormControl.ListCount (get)
+
+REM -----------------------------------------------------------------------------
+Property Get ListIndex() As Variant
+&apos;&apos;&apos; The ListIndex property specifies which item is selected in a list box or combo box.
+&apos;&apos;&apos; In case of multiple selection, the index of the first one is returned or only one is set
+ ListIndex = _PropertyGet(&quot;ListIndex&quot;, -1)
+End Property &apos; SFDocuments.SF_FormControl.ListIndex (get)
+
+REM -----------------------------------------------------------------------------
+Property Let ListIndex(Optional ByVal pvListIndex As Variant)
+&apos;&apos;&apos; Set the updatable property ListIndex
+ _PropertySet(&quot;ListIndex&quot;, pvListIndex)
+End Property &apos; SFDocuments.SF_FormControl.ListIndex (let)
+
+REM -----------------------------------------------------------------------------
+Property Get ListSource() As Variant
+&apos;&apos;&apos; The ListSource property specifies the data contained in a combobox or a listbox
+&apos;&apos;&apos; as a zero-based array of string values
+ ListSource = _PropertyGet(&quot;ListSource&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.ListSource (get)
+
+REM -----------------------------------------------------------------------------
+Property Let ListSource(Optional ByVal pvListSource As Variant)
+&apos;&apos;&apos; Set the updatable property ListSource
+ _PropertySet(&quot;ListSource&quot;, pvListSource)
+End Property &apos; SFDocuments.SF_FormControl.ListSource (let)
+
+REM -----------------------------------------------------------------------------
+Property Get ListSourceType() As Variant
+&apos;&apos;&apos; The ListSourceType property specifies the kind of data source used to fill the list data of a listbox or a combobox
+ ListSourceType = _PropertyGet(&quot;ListSourceType&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.ListSourceType (get)
+
+REM -----------------------------------------------------------------------------
+Property Let ListSourceType(Optional ByVal pvListSourceType As Variant)
+&apos;&apos;&apos; Set the updatable property ListSourceType
+ _PropertySet(&quot;ListSourceType&quot;, pvListSourceType)
+End Property &apos; SFDocuments.SF_FormControl.ListSourceType (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Locked() As Variant
+&apos;&apos;&apos; The Locked property specifies if a control is read-only
+ Locked = _PropertyGet(&quot;Locked&quot;, False)
+End Property &apos; SFDocuments.SF_FormControl.Locked (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Locked(Optional ByVal pvLocked As Variant)
+&apos;&apos;&apos; Set the updatable property Locked
+ _PropertySet(&quot;Locked&quot;, pvLocked)
+End Property &apos; SFDocuments.SF_FormControl.Locked (let)
+
+REM -----------------------------------------------------------------------------
+Property Get MultiSelect() As Variant
+&apos;&apos;&apos; The MultiSelect property specifies whether a user can make multiple selections in a listbox
+ MultiSelect = _PropertyGet(&quot;MultiSelect&quot;, False)
+End Property &apos; SFDocuments.SF_FormControl.MultiSelect (get)
+
+REM -----------------------------------------------------------------------------
+Property Let MultiSelect(Optional ByVal pvMultiSelect As Variant)
+&apos;&apos;&apos; Set the updatable property MultiSelect
+ _PropertySet(&quot;MultiSelect&quot;, pvMultiSelect)
+End Property &apos; SFDocuments.SF_FormControl.MultiSelect (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Name() As String
+&apos;&apos;&apos; Return the name of the actual control
+ Name = _PropertyGet(&quot;Name&quot;)
+End Property &apos; SFDocuments.SF_FormControl.Name
+
+REM -----------------------------------------------------------------------------
+Property Get OnActionPerformed() As Variant
+&apos;&apos;&apos; Get the script associated with the OnActionPerformed event
+ OnActionPerformed = _PropertyGet(&quot;OnActionPerformed&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnActionPerformed (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnActionPerformed(Optional ByVal pvOnActionPerformed As Variant)
+&apos;&apos;&apos; Set the updatable property OnActionPerformed
+ _PropertySet(&quot;OnActionPerformed&quot;, pvOnActionPerformed)
+End Property &apos; SFDocuments.SF_FormControl.OnActionPerformed (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnAdjustmentValueChanged() As Variant
+&apos;&apos;&apos; Get the script associated with the OnAdjustmentValueChanged event
+ OnAdjustmentValueChanged = _PropertyGet(&quot;OnAdjustmentValueChanged&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnAdjustmentValueChanged (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnAdjustmentValueChanged(Optional ByVal pvOnAdjustmentValueChanged As Variant)
+&apos;&apos;&apos; Set the updatable property OnAdjustmentValueChanged
+ _PropertySet(&quot;OnAdjustmentValueChanged&quot;, pvOnAdjustmentValueChanged)
+End Property &apos; SFDocuments.SF_FormControl.OnAdjustmentValueChanged (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnApproveAction() As Variant
+&apos;&apos;&apos; Get the script associated with the OnApproveAction event
+ OnApproveAction = _PropertyGet(&quot;OnApproveAction&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnApproveAction (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnApproveAction(Optional ByVal pvOnApproveAction As Variant)
+&apos;&apos;&apos; Set the updatable property OnApproveAction
+ _PropertySet(&quot;OnApproveAction&quot;, pvOnApproveAction)
+End Property &apos; SFDocuments.SF_FormControl.OnApproveAction (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnApproveReset() As Variant
+&apos;&apos;&apos; Get the script associated with the OnApproveReset event
+ OnApproveReset = _PropertyGet(&quot;OnApproveReset&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnApproveReset (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnApproveReset(Optional ByVal pvOnApproveReset As Variant)
+&apos;&apos;&apos; Set the updatable property OnApproveReset
+ _PropertySet(&quot;OnApproveReset&quot;, pvOnApproveReset)
+End Property &apos; SFDocuments.SF_FormControl.OnApproveReset (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnApproveUpdate() As Variant
+&apos;&apos;&apos; Get the script associated with the OnApproveUpdate event
+ OnApproveUpdate = _PropertyGet(&quot;OnApproveUpdate&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnApproveUpdate (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnApproveUpdate(Optional ByVal pvOnApproveUpdate As Variant)
+&apos;&apos;&apos; Set the updatable property OnApproveUpdate
+ _PropertySet(&quot;OnApproveUpdate&quot;, pvOnApproveUpdate)
+End Property &apos; SFDocuments.SF_FormControl.OnApproveUpdate (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnChanged() As Variant
+&apos;&apos;&apos; Get the script associated with the OnChanged event
+ OnChanged = _PropertyGet(&quot;OnChanged&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnChanged (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnChanged(Optional ByVal pvOnChanged As Variant)
+&apos;&apos;&apos; Set the updatable property OnChanged
+ _PropertySet(&quot;OnChanged&quot;, pvOnChanged)
+End Property &apos; SFDocuments.SF_FormControl.OnChanged (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnErrorOccurred() As Variant
+&apos;&apos;&apos; Get the script associated with the OnErrorOccurred event
+ OnErrorOccurred = _PropertyGet(&quot;OnErrorOccurred&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnErrorOccurred (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnErrorOccurred(Optional ByVal pvOnErrorOccurred As Variant)
+&apos;&apos;&apos; Set the updatable property OnErrorOccurred
+ _PropertySet(&quot;OnErrorOccurred&quot;, pvOnErrorOccurred)
+End Property &apos; SFDocuments.SF_FormControl.OnErrorOccurred (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnFocusGained() As Variant
+&apos;&apos;&apos; Get the script associated with the OnFocusGained event
+ OnFocusGained = _PropertyGet(&quot;OnFocusGained&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnFocusGained (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnFocusGained(Optional ByVal pvOnFocusGained As Variant)
+&apos;&apos;&apos; Set the updatable property OnFocusGained
+ _PropertySet(&quot;OnFocusGained&quot;, pvOnFocusGained)
+End Property &apos; SFDocuments.SF_FormControl.OnFocusGained (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnFocusLost() As Variant
+&apos;&apos;&apos; Get the script associated with the OnFocusLost event
+ OnFocusLost = _PropertyGet(&quot;OnFocusLost&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnFocusLost (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnFocusLost(Optional ByVal pvOnFocusLost As Variant)
+&apos;&apos;&apos; Set the updatable property OnFocusLost
+ _PropertySet(&quot;OnFocusLost&quot;, pvOnFocusLost)
+End Property &apos; SFDocuments.SF_FormControl.OnFocusLost (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnItemStateChanged() As Variant
+&apos;&apos;&apos; Get the script associated with the OnItemStateChanged event
+ OnItemStateChanged = _PropertyGet(&quot;OnItemStateChanged&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnItemStateChanged (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnItemStateChanged(Optional ByVal pvOnItemStateChanged As Variant)
+&apos;&apos;&apos; Set the updatable property OnItemStateChanged
+ _PropertySet(&quot;OnItemStateChanged&quot;, pvOnItemStateChanged)
+End Property &apos; SFDocuments.SF_FormControl.OnItemStateChanged (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnKeyPressed() As Variant
+&apos;&apos;&apos; Get the script associated with the OnKeyPressed event
+ OnKeyPressed = _PropertyGet(&quot;OnKeyPressed&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnKeyPressed (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnKeyPressed(Optional ByVal pvOnKeyPressed As Variant)
+&apos;&apos;&apos; Set the updatable property OnKeyPressed
+ _PropertySet(&quot;OnKeyPressed&quot;, pvOnKeyPressed)
+End Property &apos; SFDocuments.SF_FormControl.OnKeyPressed (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnKeyReleased() As Variant
+&apos;&apos;&apos; Get the script associated with the OnKeyReleased event
+ OnKeyReleased = _PropertyGet(&quot;OnKeyReleased&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnKeyReleased (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnKeyReleased(Optional ByVal pvOnKeyReleased As Variant)
+&apos;&apos;&apos; Set the updatable property OnKeyReleased
+ _PropertySet(&quot;OnKeyReleased&quot;, pvOnKeyReleased)
+End Property &apos; SFDocuments.SF_FormControl.OnKeyReleased (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnMouseDragged() As Variant
+&apos;&apos;&apos; Get the script associated with the OnMouseDragged event
+ OnMouseDragged = _PropertyGet(&quot;OnMouseDragged&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseDragged (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnMouseDragged(Optional ByVal pvOnMouseDragged As Variant)
+&apos;&apos;&apos; Set the updatable property OnMouseDragged
+ _PropertySet(&quot;OnMouseDragged&quot;, pvOnMouseDragged)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseDragged (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnMouseEntered() As Variant
+&apos;&apos;&apos; Get the script associated with the OnMouseEntered event
+ OnMouseEntered = _PropertyGet(&quot;OnMouseEntered&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseEntered (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnMouseEntered(Optional ByVal pvOnMouseEntered As Variant)
+&apos;&apos;&apos; Set the updatable property OnMouseEntered
+ _PropertySet(&quot;OnMouseEntered&quot;, pvOnMouseEntered)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseEntered (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnMouseExited() As Variant
+&apos;&apos;&apos; Get the script associated with the OnMouseExited event
+ OnMouseExited = _PropertyGet(&quot;OnMouseExited&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseExited (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnMouseExited(Optional ByVal pvOnMouseExited As Variant)
+&apos;&apos;&apos; Set the updatable property OnMouseExited
+ _PropertySet(&quot;OnMouseExited&quot;, pvOnMouseExited)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseExited (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnMouseMoved() As Variant
+&apos;&apos;&apos; Get the script associated with the OnMouseMoved event
+ OnMouseMoved = _PropertyGet(&quot;OnMouseMoved&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseMoved (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnMouseMoved(Optional ByVal pvOnMouseMoved As Variant)
+&apos;&apos;&apos; Set the updatable property OnMouseMoved
+ _PropertySet(&quot;OnMouseMoved&quot;, pvOnMouseMoved)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseMoved (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnMousePressed() As Variant
+&apos;&apos;&apos; Get the script associated with the OnMousePressed event
+ OnMousePressed = _PropertyGet(&quot;OnMousePressed&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnMousePressed (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnMousePressed(Optional ByVal pvOnMousePressed As Variant)
+&apos;&apos;&apos; Set the updatable property OnMousePressed
+ _PropertySet(&quot;OnMousePressed&quot;, pvOnMousePressed)
+End Property &apos; SFDocuments.SF_FormControl.OnMousePressed (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnMouseReleased() As Variant
+&apos;&apos;&apos; Get the script associated with the OnMouseReleased event
+ OnMouseReleased = _PropertyGet(&quot;OnMouseReleased&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseReleased (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnMouseReleased(Optional ByVal pvOnMouseReleased As Variant)
+&apos;&apos;&apos; Set the updatable property OnMouseReleased
+ _PropertySet(&quot;OnMouseReleased&quot;, pvOnMouseReleased)
+End Property &apos; SFDocuments.SF_FormControl.OnMouseReleased (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnResetted() As Variant
+&apos;&apos;&apos; Get the script associated with the OnResetted event
+ OnResetted = _PropertyGet(&quot;OnResetted&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnResetted (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnResetted(Optional ByVal pvOnResetted As Variant)
+&apos;&apos;&apos; Set the updatable property OnResetted
+ _PropertySet(&quot;OnResetted&quot;, pvOnResetted)
+End Property &apos; SFDocuments.SF_FormControl.OnResetted (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnTextChanged() As Variant
+&apos;&apos;&apos; Get the script associated with the OnTextChanged event
+ OnTextChanged = _PropertyGet(&quot;OnTextChanged&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnTextChanged (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnTextChanged(Optional ByVal pvOnTextChanged As Variant)
+&apos;&apos;&apos; Set the updatable property OnTextChanged
+ _PropertySet(&quot;OnTextChanged&quot;, pvOnTextChanged)
+End Property &apos; SFDocuments.SF_FormControl.OnTextChanged (let)
+
+REM -----------------------------------------------------------------------------
+Property Get OnUpdated() As Variant
+&apos;&apos;&apos; Get the script associated with the OnUpdated event
+ OnUpdated = _PropertyGet(&quot;OnUpdated&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.OnUpdated (get)
+
+REM -----------------------------------------------------------------------------
+Property Let OnUpdated(Optional ByVal pvOnUpdated As Variant)
+&apos;&apos;&apos; Set the updatable property OnUpdated
+ _PropertySet(&quot;OnUpdated&quot;, pvOnUpdated)
+End Property &apos; SFDocuments.SF_FormControl.OnUpdated (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Parent() As Object
+&apos;&apos;&apos; Return the Parent form or [table]control object of the actual control
+ Parent = _PropertyGet(&quot;Parent&quot;, Nothing)
+End Property &apos; SFDocuments.SF_FormControl.Parent
+
+REM -----------------------------------------------------------------------------
+Property Get Picture() As Variant
+&apos;&apos;&apos; The Picture property specifies a bitmap or other type of graphic to be displayed on the specified control
+ Picture = _PropertyGet(&quot;Picture&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.Picture (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Picture(Optional ByVal pvPicture As Variant)
+&apos;&apos;&apos; Set the updatable property Picture
+ _PropertySet(&quot;Picture&quot;, pvPicture)
+End Property &apos; SFDocuments.SF_FormControl.Picture (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Required() As Variant
+&apos;&apos;&apos; A control is said Required when it must not contain a null value
+ Required = _PropertyGet(&quot;Required&quot;, False)
+End Property &apos; SFDocuments.SF_FormControl.Required (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Required(Optional ByVal pvRequired As Variant)
+&apos;&apos;&apos; Set the updatable property Required
+ _PropertySet(&quot;Required&quot;, pvRequired)
+End Property &apos; SFDocuments.SF_FormControl.Required (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Text() As Variant
+&apos;&apos;&apos; The Text property specifies the actual content of the control like it is displayed on the screen
+ Text = _PropertyGet(&quot;Text&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.Text (get)
+
+REM -----------------------------------------------------------------------------
+Property Get TipText() As Variant
+&apos;&apos;&apos; The TipText property specifies the text that appears in a screentip when you hold the mouse pointer over a control
+ TipText = _PropertyGet(&quot;TipText&quot;, &quot;&quot;)
+End Property &apos; SFDocuments.SF_FormControl.TipText (get)
+
+REM -----------------------------------------------------------------------------
+Property Let TipText(Optional ByVal pvTipText As Variant)
+&apos;&apos;&apos; Set the updatable property TipText
+ _PropertySet(&quot;TipText&quot;, pvTipText)
+End Property &apos; SFDocuments.SF_FormControl.TipText (let)
+
+REM -----------------------------------------------------------------------------
+Property Get TripleState() As Variant
+&apos;&apos;&apos; The TripleState property specifies how a check box will display Null values
+&apos;&apos;&apos; When True, the control will cycle through states for Yes, No, and Null values. The control appears dimmed (grayed) when its Value property is set to Null.
+&apos;&apos;&apos; When False, the control will cycle through states for Yes and No values. Null values display as if they were No values.
+ TripleState = _PropertyGet(&quot;TripleState&quot;, False)
+End Property &apos; SFDocuments.SF_FormControl.TripleState (get)
+
+REM -----------------------------------------------------------------------------
+Property Let TripleState(Optional ByVal pvTripleState As Variant)
+&apos;&apos;&apos; Set the updatable property TripleState
+ _PropertySet(&quot;TripleState&quot;, pvTripleState)
+End Property &apos; SFDocuments.SF_FormControl.TripleState (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Value() As Variant
+&apos;&apos;&apos; The Value property specifies the data contained in the control
+ Value = _PropertyGet(&quot;Value&quot;, Empty)
+End Property &apos; SFDocuments.SF_FormControl.Value (get)
+
+REM -----------------------------------------------------------------------------
+Property Let Value(Optional ByVal pvValue As Variant)
+&apos;&apos;&apos; Set the updatable property Value
+ _PropertySet(&quot;Value&quot;, pvValue)
+End Property &apos; SFDocuments.SF_FormControl.Value (let)
+
+REM -----------------------------------------------------------------------------
+Property Get Visible() As Variant
+&apos;&apos;&apos; The Visible property specifies if the control is accessible with the cursor.
+ Visible = _PropertyGet(&quot;Visible&quot;, True)
+End Property &apos; SFDocuments.SF_FormControl.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_FormControl.Visible (let)
+
+REM -----------------------------------------------------------------------------
+Property Get XControlModel() As Object
+&apos;&apos;&apos; The XControlModel property returns the model UNO object of the control
+ XControlModel = _PropertyGet(&quot;XControlModel&quot;, Nothing)
+End Property &apos; SFDocuments.SF_FormControl.XControlModel (get)
+
+REM -----------------------------------------------------------------------------
+Property Get XControlView() As Object
+&apos;&apos;&apos; The XControlView property returns the view UNO object of the control
+ XControlView = _PropertyGet(&quot;XControlView&quot;, Nothing)
+End Property &apos; SFDocuments.SF_FormControl.XControlView (get)
+
+REM ===================================================================== METHODS
+
+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 actual table control
+&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 myGrid As Object, myList As Variant, myControl As Object
+&apos;&apos;&apos; Set myGrid = myForm.Controls(&quot;myTableControl&quot;)
+&apos;&apos;&apos; myList = myGrid.Controls()
+&apos;&apos;&apos; Set myControl = myGrid.Controls(&quot;myCheckBox&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
+Dim oView As Object &apos; com.sun.star.awt.XControl - stardiv.Toolkit.UnoDialogControl
+Dim i As Long
+Const cstThisSub = &quot;SFDocuments.FormControl.Controls&quot;
+Const cstSubArgs = &quot;[ControlName]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set Controls = Nothing
+
+Check:
+ If IsMissing(ControlName) Or IsEmpty(ControlName) Then ControlName = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If _ControlType &lt;&gt; CTLTABLECONTROL Then GoTo Catch
+ If Not [_Parent]._IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(ControlName, &quot;ControlName&quot;, V_STRING) Then GoTo Finally
+ End If
+
+Try:
+ &apos; Collect all control names if not yet done
+ If UBound(_ControlNames) &lt; 0 Then
+ _ControlNames = _ControlModel.getElementNames()
+ If UBound(_ControlNames) &gt;= 0 Then
+ ReDim _ControlCache(0 To UBound(_ControlNames))
+ End If
+ End If
+
+ &apos; Return the list of controls or a FormControl instance
+ If Len(ControlName) = 0 Then
+ Controls = _ControlNames
+
+ Else
+
+ If Not _ControlModel.hasByName(ControlName) Then GoTo CatchNotFound
+ lIndexOfNames = ScriptForge.SF_Array.IndexOf(_ControlNames, ControlName, CaseSensitive := True)
+ &apos; Reuse cache when relevant
+ vControl = _ControlCache(lIndexOfNames)
+
+ If IsEmpty(vControl) Then
+ &apos; Not in cache =&gt; Create the new form control class instance
+ Set oControl = New SF_FormControl
+ With oControl
+ ._Name = ControlName
+ Set .[Me] = oControl
+ Set .[_Parent] = [Me]
+ ._ParentIsTable = True
+ ._IndexOfNames = lIndexOfNames
+ ._FormName = _FormName
+ Set ._ParentForm = _ParentForm
+ &apos; Get model and view of the current control
+ Set ._ControlModel = _ControlModel.getByName(ControlName)
+ ._ImplementationName = ._ControlModel.ColumnServiceName &apos; getImplementationName aborts for subcontrols !?
+ &apos; Bypass to find the control view: cannot be done from the top component
+ If Not IsNull(_ControlView) Then &apos; Anticipate absence of ControlView in table controls when edit mode
+ For i = 0 to _ControlView.getCount() - 1
+ Set oView = _ControlView.GetByIndex(i)
+ If Not IsNull(oView) Then
+ If oView.getModel.Name = ControlName Then
+ Set ._ControlView = oView
+ Exit For
+ End If
+ End If
+ Next i
+ End If
+ ._Initialize()
+ End With
+ Else
+ Set oControl = vControl
+ End If
+
+ Set Controls = oControl
+ End If
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchNotFound:
+ ScriptForge.SF_Utils._Validate(ControlName, &quot;ControlName&quot;, V_STRING, _ControlModel.getElementNames())
+ GoTo Finally
+End Function &apos; SFDocuments.SF_FormControl.Controls
+
+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; If the property does not exist, returns Null
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; see the exceptions of the individual properties
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; myControl.GetProperty(&quot;MyProperty&quot;)
+
+Dim vDefault As Variant &apos; Default value when property not applicable on control type
+Const cstThisSub = &quot;SFDocuments.FormControl.GetProperty&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.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:
+ &apos; FormControl properties are far from applicable to all control types
+ &apos; Getting a property must never abort to not interfere with the Basic IDE watch function
+ &apos; Hence a default value must be provided
+ Select Case UCase(PropertyName)
+ Case UCase(&quot;Default&quot;) : vDefault = False
+ Case UCase(&quot;DefaultValue&quot;) : vDefault = Null
+ Case UCase(&quot;Enabled&quot;) : vDefault = False
+ Case UCase(&quot;ListCount&quot;) : vDefault = 0
+ Case UCase(&quot;ListIndex&quot;) : vDefault = -1
+ Case UCase(&quot;Locked&quot;) : vDefault = False
+ Case UCase(&quot;MultiSelect&quot;) : vDefault = False
+ Case UCase(&quot;Parent&quot;) : vDefault = Nothing
+ Case UCase(&quot;Required&quot;) : vDefault = False
+ Case UCase(&quot;TripleState&quot;) : vDefault = False
+ Case UCase(&quot;Value&quot;) : vDefault = Empty
+ Case UCase(&quot;Visible&quot;) : vDefault = True
+ Case UCase(&quot;XControlModel&quot;) : vDefault = Nothing
+ Case UCase(&quot;XControlView&quot;) : vDefault = Nothing
+ Case Else : vDefault = &quot;&quot;
+ End Select
+
+ GetProperty = _PropertyGet(PropertyName, vDefault)
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_FormControl.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the FormControl service as an array
+
+ Methods = Array( _
+ &quot;AddSubNode&quot; _
+ , &quot;AddSubTree&quot; _
+ , &quot;CreateRoot&quot; _
+ , &quot;FindNode&quot; _
+ , &quot;SetFocus&quot; _
+ , &quot;WriteLine&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_FormControl.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the FormControl class as an array
+
+ Properties = Array( _
+ &quot;Action&quot; _
+ , &quot;Cancel&quot; _
+ , &quot;Caption&quot; _
+ , &quot;ControlSource&quot; _
+ , &quot;ControlType&quot; _
+ , &quot;Default&quot; _
+ , &quot;DefaultValue&quot; _
+ , &quot;Enabled&quot; _
+ , &quot;Format&quot; _
+ , &quot;ListCount&quot; _
+ , &quot;ListIndex&quot; _
+ , &quot;ListSource&quot; _
+ , &quot;ListSourceType&quot; _
+ , &quot;Locked&quot; _
+ , &quot;MultiSelect&quot; _
+ , &quot;Name&quot; _
+ , &quot;OnActionPerformed&quot; _
+ , &quot;OnAdjustmentValueChanged&quot; _
+ , &quot;OnApproveAction&quot; _
+ , &quot;OnApproveReset&quot; _
+ , &quot;OnApproveUpdate&quot; _
+ , &quot;OnChanged&quot; _
+ , &quot;OnErrorOccurred&quot; _
+ , &quot;OnFocusGained&quot; _
+ , &quot;OnFocusLost&quot; _
+ , &quot;OnItemStateChanged&quot; _
+ , &quot;OnKeyPressed&quot; _
+ , &quot;OnKeyReleased&quot; _
+ , &quot;OnMouseDragged&quot; _
+ , &quot;OnMouseEntered&quot; _
+ , &quot;OnMouseExited&quot; _
+ , &quot;OnMouseMoved&quot; _
+ , &quot;OnMousePressed&quot; _
+ , &quot;OnMouseReleased&quot; _
+ , &quot;OnResetted&quot; _
+ , &quot;OnTextChanged&quot; _
+ , &quot;OnUpdated&quot; _
+ , &quot;Parent&quot; _
+ , &quot;Picture&quot; _
+ , &quot;Required&quot; _
+ , &quot;Text&quot; _
+ , &quot;TipText&quot; _
+ , &quot;TripleState&quot; _
+ , &quot;Value&quot; _
+ , &quot;Visible&quot; _
+ , &quot;XControlModel&quot; _
+ , &quot;XControlView&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_FormControl.Properties
+
+REM -----------------------------------------------------------------------------
+Public Function SetFocus() As Boolean
+&apos;&apos;&apos; Set the focus on the current Control instance
+&apos;&apos;&apos; Probably called from after an event occurrence
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True if focusing is successful
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Dim oDoc As Object, oForm As Object, oControl As Object
+&apos;&apos;&apos; Set oDoc = CreateScriptService(&quot;SFDocuments.Document&quot;, ThisComponent)
+&apos;&apos;&apos; Set oForm = oDoc.Forms(0)
+&apos;&apos;&apos; Set oControl = oForm.Controls(&quot;thisControl&quot;)
+&apos;&apos;&apos; oControl.SetFocus()
+
+Dim bSetFocus As Boolean &apos; Return value
+Dim iColPosition As Integer &apos; Position of control in table
+Dim oTableModel As Object &apos; XControlModel of parent table
+Dim oControl As Object &apos; com.sun.star.awt.XControlModel
+Dim i As Integer, j As Integer
+Const cstThisSub = &quot;SFDocuments.FormControl.SetFocus&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bSetFocus = False
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _ParentForm._IsStillAlive() Then GoTo Finally
+ End If
+
+Try:
+ If Not IsNull(_ControlView) Then
+ If _ParentIsTable Then &apos; setFocus() method does not work on controlviews in table control ?!?
+ &apos; Find the column position of the current instance in the parent table control
+ iColPosition = -1
+ Set oTableModel = [_Parent]._ControlModel
+ j = -1
+ For i = 0 To oTableModel.Count - 1
+ Set oControl = oTableModel.getByIndex(i)
+ If Not oControl.Hidden Then j = j + 1 &apos; Skip hidden columns
+ If oControl.Name = _Name Then
+ iColPosition = j
+ Exit For
+ End If
+ Next i
+ If iColPosition &gt;= 0 Then
+ [_Parent]._ControlView.setFocus() &apos;Set first focus on table control itself
+ [_Parent]._ControlView.setCurrentColumnPosition(iColPosition) &apos;Deprecated but no alternative found
+ End If
+ Else
+ _ControlView.setFocus()
+ End If
+ bSetFocus = True
+ End If
+ bSetFocus = True
+
+Finally:
+ SetFocus = bSetFocus
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFControls.SF_FormControl.SetFocus
+
+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.FormControl.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 ScriptForge.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_FormControl.SetProperty
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Private Function _FormatsList() As Variant
+&apos;&apos;&apos; Return the allowed format entries as a zero-based array for Date and Time control types
+
+Dim vFormats() As Variant &apos; Return value
+
+ Select Case _ControlType
+ Case CTLDATEFIELD
+ vFormats = Array( _
+ &quot;Standard (short)&quot; _
+ , &quot;Standard (short YY)&quot; _
+ , &quot;Standard (short YYYY)&quot; _
+ , &quot;Standard (long)&quot; _
+ , &quot;DD/MM/YY&quot; _
+ , &quot;MM/DD/YY&quot; _
+ , &quot;YY/MM/DD&quot; _
+ , &quot;DD/MM/YYYY&quot; _
+ , &quot;MM/DD/YYYY&quot; _
+ , &quot;YYYY/MM/DD&quot; _
+ , &quot;YY-MM-DD&quot; _
+ , &quot;YYYY-MM-DD&quot; _
+ )
+ Case CTLTIMEFIELD
+ vFormats = Array( _
+ &quot;24h short&quot; _
+ , &quot;24h long&quot; _
+ , &quot;12h short&quot; _
+ , &quot;12h long&quot; _
+ )
+ Case Else
+ vFormats = Array()
+ End Select
+
+ _FormatsList = vFormats
+
+End Function &apos; SFDocuments.SF_FormControl._FormatsList
+
+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_FormControl._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 UCase(&quot;OnActionPerformed&quot;)
+ _GetListener = &quot;XActionListener&quot;
+ Case UCase(&quot;OnAdjustmentValueChanged&quot;)
+ _GetListener = &quot;XAdjustmentListener&quot;
+ Case UCase(&quot;OnApproveAction&quot;)
+ _GetListener = &quot;XApproveActionListener&quot;
+ Case UCase(&quot;OnApproveReset&quot;), UCase(&quot;OnResetted&quot;)
+ _GetListener = &quot;XResetListener&quot;
+ Case UCase(&quot;OnApproveUpdate&quot;), UCase(&quot;OnUpdated&quot;)
+ _GetListener = &quot;XUpdateListener&quot;
+ Case UCase(&quot;OnChanged&quot;)
+ _GetListener = &quot;XChangeListener&quot;
+ Case UCase(&quot;OnErrorOccurred&quot;)
+ _GetListener = &quot;XErrorListener&quot;
+ Case UCase(&quot;OnFocusGained&quot;), UCase(&quot;OnFocusLost&quot;)
+ _GetListener = &quot;XFocusListener&quot;
+ Case UCase(&quot;OnItemStateChanged&quot;)
+ _GetListener = &quot;XItemListener&quot;
+ Case UCase(&quot;OnKeyPressed&quot;), UCase(&quot;OnKeyReleased&quot;)
+ _GetListener = &quot;XKeyListener&quot;
+ Case UCase(&quot;OnMouseDragged&quot;), UCase(&quot;OnMouseMoved&quot;)
+ _GetListener = &quot;XMouseMotionListener&quot;
+ Case UCase(&quot;OnMouseEntered&quot;), UCase(&quot;OnMouseExited&quot;), UCase(&quot;OnMousePressed&quot;), UCase(&quot;OnMouseReleased&quot;)
+ _GetListener = &quot;XMouseListener&quot;
+ Case UCase(&quot;OnTextChanged&quot;)
+ _GetListener = &quot;XTextListener&quot;
+ End Select
+
+End Function &apos; SFDocuments.SF_FormControl._GetListener
+
+REM -----------------------------------------------------------------------------
+Public Sub _Initialize()
+&apos;&apos;&apos; Complete the object creation process:
+&apos;&apos;&apos; - Initialization of private members
+&apos;&apos;&apos; - Collection of specific attributes
+&apos;&apos;&apos; - Synchronization with parent form instance
+
+Dim vControlTypes As Variant &apos; Array of control types ordered by the ClassId property of XControlModel - 2
+Const acHiddenControl = 13 &apos; Class Id of an hidden control: has no ControlView
+
+ vControlTypes = array( CTLBUTTON _
+ , CTLRADIOBUTTON _
+ , CTLIMAGEBUTTON _
+ , CTLCHECKBOX _
+ , CTLLISTBOX _
+ , CTLCOMBOBOX _
+ , CTLGROUPBOX _
+ , CTLTEXTFIELD _
+ , CTLFIXEDTEXT _
+ , CTLTABLECONTROL _
+ , CTLFILECONTROL _
+ , CTLHIDDENCONTROL _
+ , CTLIMAGECONTROL _
+ , CTLDATEFIELD _
+ , CTLTIMEFIELD _
+ , CTLNUMERICFIELD _
+ , CTLCURRENCYFIELD _
+ , CTLPATTERNFIELD _
+ , CTLSCROLLBAR _
+ , CTLSPINBUTTON _
+ , CTLNAVIGATIONBAR _
+ )
+
+Try:
+ &apos; _implementationName is set elsewhere for controls in table control
+ If Len(_ImplementationName) = 0 Then _ImplementationName = ScriptForge.SF_Session.UnoObjectType(_ControlModel)
+ _ClassId = _ControlModel.ClassId
+
+ &apos; Identify the control type, ignore subforms and pay attention to formatted fields
+ If ScriptForge.SF_Session.HasUnoproperty(_ControlModel, &quot;ClassId&quot;) Then &apos; All control types have a ClassId property except subforms
+ _ControlType = vControlTypes(_ClassId - 2)
+ &apos; Formatted fields belong to the TextField family
+ If _ControlType = CTLTEXTFIELD Then
+ If _ImplementationName = &quot;com.sun.star.comp.forms.OFormattedFieldWrapper&quot; _
+ Or _ImplementationName = &quot;com.sun.star.comp.forms.OFormattedFieldWrapper_ForcedFormatted&quot; _
+ Or _ImplementationName = &quot;com.sun.star.form.component.FormattedField&quot; Then &apos; When in table control
+ _ControlType = CTLFORMATTEDFIELD
+ End If
+ End If
+ Else
+ Exit Sub &apos; Ignore subforms, should not happen
+ End If
+
+ With [_Parent]
+ &apos; Set control view if not set yet
+ If IsNull(_ControlView) Then
+ If _ClassId &gt; 0 And _ClassId &lt;&gt; acHiddenControl Then &apos; No view on hidden controls
+ If IsNull(._FormDocument) Then &apos; Usual document
+ Set _ControlView = ._Component.CurrentController.getControl(_ControlModel)
+ Else &apos; Base form document
+ Set _ControlView = ._FormDocument.Component.CurrentController.getControl(_ControlModel)
+ End If
+ End If
+ End If
+ End With
+
+ &apos; Store the SF_FormControl object in the parent cache
+ Set _Parent._ControlCache(_IndexOfNames) = [Me]
+
+Finally:
+ Exit Sub
+End Sub &apos; SFDocuments.SF_FormControl._Initialize
+
+REM -----------------------------------------------------------------------------
+Private Function _ListboxBound() As Boolean
+&apos;&apos;&apos; Return True if the actual control, which is a listbox, has a bound column
+&apos;&apos;&apos; Called before setting the value of a listbox, i.e. the value to be rewritten in the underlying table data
+&apos;&apos;&apos; The existence of a bound column is derived from the comparison between StringItemList and ValueItemList
+&apos;&apos;&apos; String ... : the strings displayed in the list box
+&apos;&apos;&apos; Value ... : the database values
+&apos;&apos;&apos; If they are different, then there is a bound column
+
+Dim bListboxBound As Boolean &apos; Return value
+Dim vValue() As Variant &apos; Alias of the control model ValueItemList
+Dim vString() As Variant &apos; Alias of the control model StringItemList
+Dim i As Long
+
+ bListboxBound = False
+
+ With _ControlModel
+ If Not IsNull(.ValueItemList) _
+ And .DataField &lt;&gt; &quot;&quot; _
+ And Not IsNull(.BoundField) _
+ And ScriptForge.SF_Array.Contains(Array( _
+ com.sun.star.form.ListSourceType.TABLE _
+ , com.sun.star.form.ListSourceType.QUERY _
+ , com.sun.star.form.ListSourceType.SQL _
+ , com.sun.star.form.ListSourceType.SQLPASSTHROUGH _
+ ), .ListSourceType) Then
+ If IsArray(.ValueItemList) Then
+ vValue = .ValueItemList
+ vString = .StringItemList
+ For i = 0 To UBound(vValue)
+ If VarType(vValue(i)) &lt;&gt; VarType(vString(i)) Then
+ bListboxBound = True
+ ElseIf vValue(i) &lt;&gt; vString(i) Then
+ bListboxBound = True
+ End If
+ If bListboxBound Then Exit For
+ Next i
+ End If
+ End If
+ End With
+
+ _ListboxBound = bListboxBound
+
+End Function &apos; _ListboxBound V0.9.0
+
+REM -----------------------------------------------------------------------------
+Private Function _PropertyGet(Optional ByVal psProperty As String _
+ , Optional ByVal pvDefault 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
+&apos;&apos;&apos; pvDefault: the value returned when the property is not applicable on the control&apos;s type
+&apos;&apos;&apos; Getting a non-existing property for a specific control type should
+&apos;&apos;&apos; not generate an error to not disrupt the Basic IDE debugger
+
+Dim vGet As Variant &apos; Return value
+Static oSession As Object &apos; Alias of SF_Session
+Dim vSelection As Variant &apos; Alias of Model.SelectedItems or Model.Selection
+Dim vList As Variant &apos; Alias of Model.StringItemList
+Dim lIndex As Long &apos; Index in StringItemList
+Dim sItem As String &apos; A single item
+Dim vDate As Variant &apos; Date after conversion from com.sun.star.util.Date or com.sun.star.util.Time
+Dim vValues As Variant &apos; Array of listbox values
+Dim oControlEvents As Object &apos; com.sun.star.container.XNameContainer
+Dim sEventName As String &apos; Internal event name
+Const cstUnoUrl = &quot;.uno:FormController/&quot;
+Dim i As Long
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ cstThisSub = &quot;SFDocuments.FormControl.get&quot; &amp; psProperty
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _ParentForm._IsStillAlive() Then GoTo Finally
+
+ If IsMissing(pvDefault) Or IsEmpty(pvDefault) Then pvDefault = Null
+ _PropertyGet = pvDefault
+
+ If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
+ Select Case UCase(psProperty)
+ Case UCase(&quot;Action&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON
+ If oSession.HasUNOProperty(_ControlModel, &quot;ButtonType&quot;) Then
+ Select Case _ControlModel.ButtonType
+ Case com.sun.star.form.FormButtonType.PUSH : _PropertyGet = &quot;none&quot;
+ Case com.sun.star.form.FormButtonType.SUBMIT : _PropertyGet = &quot;submitForm&quot;
+ Case com.sun.star.form.FormButtonType.RESET : _PropertyGet = &quot;resetForm&quot;
+ Case com.sun.star.form.FormButtonType.URL
+ &apos; &quot;.uno:FormController/moveToFirst&quot;
+ If Left(_ControlModel.TargetURL, Len(cstUnoUrl)) = cstUnoUrl Then
+ _PropertyGet = Mid(_ControlModel.TargetURL, Len(cstUnoUrl) + 1)
+ ElseIf Left(_ControlModel.TargetURL, 4) = &quot;http&quot; Then
+ _PropertyGet = &quot;openWebPage&quot;
+ ElseIf Left(_ControlModel.TargetURL, 4) = &quot;file&quot; Then
+ _PropertyGet =&quot;openDocument&quot;
+ End If
+ End Select
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Caption&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON, CTLCHECKBOX, CTLFIXEDTEXT, CTLGROUPBOX, CTLRADIOBUTTON
+ If oSession.HasUNOProperty(_ControlModel, &quot;Label&quot;) Then _PropertyGet = _ControlModel.Label
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ControlSource&quot;)
+ Select Case _ControlType
+ Case CTLCHECKBOX, CTLCOMBOBOX, CTLCURRENCYFIELD, CTLDATEFIELD, CTLFORMATTEDFIELD, CTLIMAGECONTROL, CTLLISTBOX _
+ , CTLNUMERICFIELD, CTLPATTERNFIELD, CTLRADIOBUTTON, CTLTEXTFIELD, CTLTIMEFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;DataField&quot;) Then _PropertyGet = _ControlModel.DataField
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ControlType&quot;)
+ _PropertyGet = _ControlType
+ Case UCase(&quot;Default&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultButton&quot;) Then _PropertyGet = _ControlModel.DefaultButton
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;DefaultValue&quot;)
+ Select Case _ControlType
+ Case CTLCHECKBOX, CTLRADIOBUTTON
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultState&quot;) Then _PropertyGet = _ControlModel.DefaultState
+ Case CTLCOMBOBOX, CTLFILECONTROL, CTLPATTERNFIELD, CTLTEXTFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultText&quot;) Then _PropertyGet = _ControlModel.DefaultText
+ Case CTLCURRENCYFIELD, CTLNUMERICFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultValue&quot;) Then _PropertyGet = _ControlModel.DefaultValue
+ Case CTLDATEFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultDate&quot;) Then
+ If Not IsEmpty(_ControlModel.DefaultDate) Then
+ With _ControlModel.DefaultDate
+ vDate = DateSerial(.Year, .Month, .Day)
+ End With
+ _PropertyGet = vDate
+ End If
+ End If
+ Case CTLFORMATTEDFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;EffectiveDefault&quot;) Then _PropertyGet = _ControlModel.EffectiveDefault
+ Case CTLLISTBOX
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultSelection&quot;) And oSession.HasUNOProperty(_ControlModel, &quot;StringItemList&quot;) Then
+ vList = _ControlModel.DefaultSelection
+ If IsArray(vList) Then
+ If UBound(vList) &gt;= LBound(vList) Then &apos; Is array initialized ?
+ lIndex = UBound(_ControlModel.StringItemList)
+ If vList(0) &gt;= 0 And vList(0) &lt;= lIndex Then _PropertyGet = _ControlModel.StringItemList(vList(0))
+ &apos; Only first default value is considered
+ End If
+ End If
+ End If
+ Case CTLSPINBUTTON
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultSpinValue&quot;) Then _PropertyGet = _ControlModel.DefaultSpinValue
+ Case CTLTIMEFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultTime&quot;) Then
+ If Not IsEmpty(_ControlModel.DefaultTime) Then
+ With _ControlModel.DefaultTime
+ vDate = TimeSerial(.Hours, .Minutes, .Seconds)
+ End With
+ _PropertyGet = vDate
+ End If
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Enabled&quot;)
+ Select Case _ControlType
+ Case CTLHIDDENCONTROL : GoTo CatchType
+ Case Else
+ If oSession.HasUnoProperty(_ControlModel, &quot;Enabled&quot;) Then _PropertyGet = _ControlModel.Enabled
+ End Select
+ Case UCase(&quot;Format&quot;)
+ Select Case _ControlType
+ Case CTLDATEFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;DateFormat&quot;) Then _PropertyGet = _FormatsList()(_ControlModel.DateFormat)
+ Case CTLTIMEFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;TimeFormat&quot;) Then _PropertyGet = _FormatsList()(_ControlModel.TimeFormat)
+ Case CTLFORMATTEDFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;FormatsSupplier&quot;) And oSession.HasUNOProperty(_ControlModel, &quot;FormatKey&quot;) Then
+ _PropertyGet = _ControlModel.FormatsSupplier.getNumberFormats.getByKey(_ControlModel.FormatKey).FormatString
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ListCount&quot;)
+ Select Case _ControlType
+ Case CTLCOMBOBOX, CTLLISTBOX
+ If oSession.HasUNOProperty(_ControlModel, &quot;StringItemList&quot;) Then _PropertyGet = UBound(_ControlModel.StringItemList) + 1
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ListIndex&quot;)
+ Select Case _ControlType
+ Case CTLCOMBOBOX
+ _PropertyGet = -1 &apos; Not found, multiselection
+ If oSession.HasUNOProperty(_ControlModel, &quot;Text&quot;) And oSession.HasUNOProperty(_ControlModel, &quot;StringItemList&quot;) Then
+ _PropertyGet = ScriptForge.SF_Array.IndexOf(_ControlModel.StringItemList, _ControlModel.Text, CaseSensitive := True)
+ End If
+ Case CTLLISTBOX
+ _PropertyGet = -1 &apos; Not found, multiselection
+ If oSession.HasUNOProperty(_ControlModel, &quot;SelectedItems&quot;) And oSession.HasUNOProperty(_ControlModel, &quot;StringItemList&quot;) Then
+ vSelection = _ControlModel.SelectedItems
+ If UBound(vSelection) &gt;= 0 Then _PropertyGet = vSelection(0)
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ListSource&quot;)
+ Select Case _ControlType
+ Case CTLCOMBOBOX, CTLLISTBOX
+ If oSession.HasUNOProperty(_ControlModel, &quot;ListSource&quot;) Then
+ With com.sun.star.form.ListSourceType
+ Select Case _ControlModel.ListSourceType
+ Case .VALUELIST _
+ , .TABLEFIELDS
+ If IsArray(_ControlModel.StringItemList) Then vValues = _ControlModel.StringItemList Else vValues = Array(_ControlModel.StringItemList)
+ Case .TABLE _
+ , .QUERY _
+ , .SQL _
+ , .SQLPASSTHROUGH
+ If IsArray(_ControlModel.ListSource) Then vValues = _ControlModel.ListSource Else vValues = Array(_ControlModel.ListSource)
+ End Select
+ End With
+ _PropertyGet = Join(vValues, &quot;;&quot;)
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ListSourceType&quot;)
+ Select Case _ControlType
+ Case CTLCOMBOBOX, CTLLISTBOX
+ If oSession.HasUnoProperty(_ControlModel, &quot;ListSourceType&quot;) Then _PropertyGet = _ControlModel.ListSourceType
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Locked&quot;)
+ Select Case _ControlType
+ Case CTLCOMBOBOX, CTLCURRENCYFIELD, CTLDATEFIELD, CTLFILECONTROL, CTLFORMATTEDFIELD, CTLIMAGECONTROL _
+ , CTLLISTBOX, CTLNUMERICFIELD, CTLPATTERNFIELD, CTLTEXTFIELD, CTLTIMEFIELD
+ If oSession.HasUnoProperty(_ControlModel, &quot;ReadOnly&quot;) Then _PropertyGet = _ControlModel.ReadOnly
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;MultiSelect&quot;)
+ Select Case _ControlType
+ Case CTLLISTBOX
+ If oSession.HasUnoProperty(_ControlModel, &quot;MultiSelection&quot;) Then
+ _PropertyGet = _ControlModel.MultiSelection
+ ElseIf oSession.HasUnoProperty(_ControlModel, &quot;MultiSelectionSimpleMode&quot;) Then &apos; Not documented: gridcontrols only TBC ??
+ _PropertyGet = _ControlModel.MultiSelectionSimpleMode
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Name&quot;)
+ _PropertyGet = _Name
+ Case UCase(&quot;OnActionPerformed&quot;), UCase(&quot;OnAdjustmentValueChanged&quot;), UCase(&quot;OnApproveAction&quot;), UCase(&quot;OnApproveReset&quot;), UCase(&quot;OnApproveUpdate&quot;) _
+ , UCase(&quot;OnChanged&quot;), UCase(&quot;OnErrorOccurred&quot;), UCase(&quot;OnFocusGained&quot;), UCase(&quot;OnFocusLost&quot;) _
+ , UCase(&quot;OnItemStateChanged&quot;), UCase(&quot;OnKeyPressed&quot;), UCase(&quot;OnKeyReleased&quot;) _
+ , UCase(&quot;OnMouseDragged&quot;), UCase(&quot;OnMouseEntered&quot;), UCase(&quot;OnMouseExited&quot;), UCase(&quot;OnMouseMoved&quot;) _
+ , UCase(&quot;OnMousePressed&quot;), UCase(&quot;OnMouseReleased&quot;), UCase(&quot;OnResetted&quot;) _
+ , UCase(&quot;OnTextChanged&quot;), UCase(&quot;OnUpdated&quot;)
+ If IsNull(_ControlModel) Then _PropertyGet = &quot;&quot; Else _PropertyGet = SF_Register._GetEventScriptCode(_ControlModel, psProperty, _Name)
+ Case UCase(&quot;Parent&quot;)
+ Set _PropertyGet = [_Parent]
+ Case UCase(&quot;Picture&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON, CTLIMAGEBUTTON, CTLIMAGECONTROL
+ If oSession.HasUnoProperty(_ControlModel, &quot;ImageURL&quot;) Then _PropertyGet = ScriptForge.SF_FileSystem._ConvertFromUrl(_ControlModel.ImageURL)
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Required&quot;)
+ Select Case _ControlType
+ Case CTLCHECKBOX, CTLCOMBOBOX, CTLCURRENCYFIELD, CTLDATEFIELD, CTLIMAGECONTROL, CTLLISTBOX, CTLNUMERICFIELD _
+ , CTLPATTERNFIELD, CTLRADIOBUTTON, CTLTEXTFIELD, CTLTIMEFIELD
+ If oSession.HasUnoProperty(_ControlModel, &quot;InputRequired&quot;) Then _PropertyGet = _ControlModel.InputRequired
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Text&quot;)
+ Select Case _ControlType
+ Case CTLDATEFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;Date&quot;) _
+ And oSession.HasUNOProperty(_ControlModel, &quot;FormatKey&quot;) _
+ And oSession.HasUNOProperty(_ControlModel, &quot;FormatsSupplier&quot;) Then
+ If Not IsEmpty(_ControlModel.Date) Then
+ With _ControlModel.Date
+ vDate = DateSerial(.Year, .Month, .Day)
+ End With
+ _PropertyGet = Format(vDate, _ControlModel.FormatsSupplier.getNumberFormats.getByKey(_ControlModel.FormatKey).FormatString)
+ End If
+ End If
+ Case CTLTIMEFIELD
+ If oSession.HasUNOProperty(_ControlModel, &quot;Text&quot;) Then
+ If Not IsEmpty(_ControlModel.Time) Then
+ With _ControlModel.Time
+ vDate = TimeSerial(.Hours, .Minutes, .Seconds)
+ End With
+ _PropertyGet = Format(vDate, &quot;HH:MM:SS&quot;)
+ End If
+ End If
+ Case CTLCOMBOBOX, CTLFILECONTROL, CTLFORMATTEDFIELD, CTLPATTERNFIELD, CTLTEXTFIELD
+ If oSession.HasUnoProperty(_ControlModel, &quot;Text&quot;) Then _PropertyGet = _ControlModel.Text
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;TipText&quot;)
+ Select Case _ControlType
+ Case CTLHIDDENCONTROL : GoTo CatchType
+ Case Else
+ If oSession.HasUnoProperty(_ControlModel, &quot;HelpText&quot;) Then _PropertyGet = _ControlModel.HelpText
+ End Select
+ Case UCase(&quot;TripleState&quot;)
+ Select Case _ControlType
+ Case CTLCHECKBOX
+ If oSession.HasUnoProperty(_ControlModel, &quot;TriState&quot;) Then _PropertyGet = _ControlModel.TriState
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Value&quot;) &apos; Default values are set here by control type, not in the 2nd argument (pvDefault)
+ vGet = pvDefault
+ Select Case _ControlType
+ Case CTLBUTTON &apos;Boolean, toggle buttons only
+ vGet = False
+ If oSession.HasUnoProperty(_ControlModel, &quot;Toggle&quot;) Then
+ If oSession.HasUnoProperty(_ControlModel, &quot;State&quot;) Then vGet = ( _ControlModel.State = 1 )
+ End If
+ Case CTLCHECKBOX &apos;0 = Not checked, 1 = Checked, 2 = Don&apos;t know
+ If oSession.HasUnoProperty(_ControlModel, &quot;State&quot;) Then vGet = _ControlModel.State Else vGet = 2
+ Case CTLCOMBOBOX, CTLFILECONTROL, CTLPATTERNFIELD, CTLTEXTFIELD &apos;String
+ If oSession.HasUnoProperty(_ControlModel, &quot;Text&quot;) Then vGet = _ControlModel.Text Else vGet = &quot;&quot;
+ Case CTLCURRENCYFIELD, CTLNUMERICFIELD &apos;Numeric
+ If oSession.HasUnoProperty(_ControlModel, &quot;Value&quot;) Then vGet = _ControlModel.Value Else vGet = 0
+ Case CTLDATEFIELD &apos;Date
+ vGet = CDate(1)
+ If oSession.HasUnoProperty(_ControlModel, &quot;Date&quot;) Then
+ If VarType(_ControlModel.Date) = ScriptForge.V_OBJECT Then &apos; com.sun.star.util.Date
+ With _ControlModel.Date
+ vDate = DateSerial(.Year, .Month, .Day)
+ End With
+ vGet = vDate
+ Else &apos; .Date = Empty
+ End If
+ End If
+ Case CTLFORMATTEDFIELD &apos;String or numeric
+ If oSession.HasUnoProperty(_ControlModel, &quot;EffectiveValue&quot;) Then vGet = _ControlModel.EffectiveValue Else vGet = &quot;&quot;
+ Case CTLHIDDENCONTROL &apos;String
+ If oSession.HasUnoProperty(_ControlModel, &quot;HiddenValue&quot;) Then vGet = _ControlModel.HiddenValue Else vGet = &quot;&quot;
+ Case CTLLISTBOX &apos;String or array of strings depending on MultiSelection
+ &apos; StringItemList is the list of the items displayed in the box
+ &apos; ValueItemList is the list of the values in the underlying database field
+ &apos; SelectedItems is the list of the indexes in StringItemList of the selected items
+ &apos; It can go beyond the limits of StringItemList
+ &apos; It can contain multiple values even if the listbox is not multiselect
+ If oSession.HasUnoProperty(_ControlModel, &quot;StringItemList&quot;) And oSession.HasUnoProperty(_ControlModel, &quot;SelectedItems&quot;) _
+ And oSession.HasUnoProperty(_ControlModel, &quot;MultiSelection&quot;) Then
+ vSelection = _ControlModel.SelectedItems
+ &apos; The list of allowed values depends on the existence of a bound column
+ If _ListBoxBound() Then vList = _ControlModel.ValueItemList Else vList = _ControlModel.StringItemList
+ If _ControlModel.MultiSelection Then vValues = Array()
+ For i = 0 To UBound(vSelection)
+ lIndex = vSelection(i)
+ If lIndex &gt;= 0 And lIndex &lt;= UBound(vList) Then
+ If Not _ControlModel.MultiSelection Then
+ vValues = vList(lIndex)
+ Exit For
+ End If
+ vValues = ScriptForge.SF_Array.Append(vValues, vList(lIndex))
+ End If
+ Next i
+ vGet = vValues
+ Else
+ vGet = &quot;&quot;
+ End If
+ Case CTLRADIOBUTTON &apos;Boolean
+ If oSession.HasUnoProperty(_ControlModel, &quot;State&quot;) Then vGet = ( _ControlModel.State = 1 ) Else vGet = False
+ Case CTLSCROLLBAR &apos;Numeric
+ vGet = 0
+ If oSession.HasUnoProperty(_ControlModel, &quot;ScrollValue&quot;) Then
+ If Not IsEmpty(_ControlModel.ScrollValue) Then vGet = _ControlModel.ScrollValue
+ End If
+ Case CTLSPINBUTTON
+ If oSession.HasUnoProperty(_ControlModel, &quot;SpinValue&quot;) Then vGet = _ControlModel.SpinValue Else vGet = 0
+ Case CTLTIMEFIELD
+ vGet = CDate(0)
+ If oSession.HasUnoProperty(_ControlModel, &quot;Time&quot;) Then
+ If VarType(_ControlModel.Time) = ScriptForge.V_OBJECT Then &apos; com.sun.star.Util.Time
+ With _ControlModel.Time
+ vDate = TimeSerial(.Hours, .Minutes, .Seconds)
+ End With
+ vGet = vDate
+ Else &apos; .Time = Empty
+ End If
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ _PropertyGet = vGet
+ Case UCase(&quot;Visible&quot;)
+ If oSession.HasUnoMethod(_ControlView, &quot;isVisible&quot;) Then _PropertyGet = CBool(_ControlView.isVisible())
+ Case UCase(&quot;XControlModel&quot;)
+ Set _PropertyGet = _ControlModel
+ Case UCase(&quot;XControlView&quot;)
+ Set _PropertyGet = _ControlView
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+CatchType:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_FormControl._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
+
+Dim bSet As Boolean &apos; Return value
+Static oSession As Object &apos; Alias of SF_Session
+Dim sFormName As String &apos; Full form identification for error messages
+Dim vSet As Variant &apos; Value to set in UNO model or view property
+Dim vActions As Variant &apos; Action property: list of available actions
+Dim sAction As String &apos; A single action
+Dim vFormats As Variant &apos; Format property: output of _FormatsList()
+Dim iFormat As Integer &apos; Format property: index in vFormats
+Dim vSelection As Variant &apos; Alias of Model.SelectedItems
+Dim vList As Variant &apos; Alias of Model.StringItemList
+Dim lIndex As Long &apos; Index in StringItemList
+Dim sItem As String &apos; A single item
+Dim oDatabase As Object &apos; The database object related to the parent form of the control instance
+Dim i As Long
+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.FormControl.set&quot; &amp; psProperty
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _ParentForm._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;Action&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON
+ vActions = Array(&quot;none&quot;, &quot;submitForm&quot;, &quot;resetForm&quot;, &quot;refreshForm&quot;, &quot;moveToFirst&quot;, &quot;moveToLast&quot;, &quot;moveToNext&quot;, &quot;moveToPrev&quot; _
+ , &quot;saveRecord&quot;, &quot;moveToNew&quot;, &quot;deleteRecord&quot;, &quot;undoRecord&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Action&quot;, ScriptForge.V_STRING, vActions) Then GoTo Finally
+ If oSession.HasUNOProperty(_ControlModel, &quot;ButtonType&quot;) Then
+ sAction = vActions(ScriptForge.SF_Array.IndexOf(vActions, pvValue, CaseSensitive := False))
+ _ControlModel.TargetURL = &quot;&quot;
+ Select Case sAction
+ Case &quot;none&quot; : vSet = com.sun.star.form.FormButtonType.PUSH
+ Case &quot;submitForm&quot; : vSet = com.sun.star.form.FormButtonType.SUBMIT
+ Case &quot;resetForm&quot; : vSet = com.sun.star.form.FormButtonType.RESET
+ Case Else
+ vSet = com.sun.star.form.FormButtonType.URL
+ _ControlModel.TargetURL = &quot;.uno:FormController/&quot; &amp; sAction
+ End Select
+ _ControlModel.ButtonType = vSet
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Caption&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON, CTLCHECKBOX, CTLFIXEDTEXT, CTLGROUPBOX, CTLRADIOBUTTON
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Caption&quot;, V_STRING) Then GoTo Finally
+ If oSession.HasUNOProperty(_ControlModel, &quot;Label&quot;) Then _ControlModel.Label = pvValue
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Default&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Default&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUNOProperty(_ControlModel, &quot;DefaultButton&quot;) Then _ControlModel.DefaultButton = pvValue
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Enabled&quot;)
+ Select Case _ControlType
+ Case CTLHIDDENCONTROL : GoTo CatchType
+ Case Else
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Enabled&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;Enabled&quot;) Then _ControlModel.Enabled = pvValue
+ End Select
+ Case UCase(&quot;Format&quot;)
+ Select Case _ControlType
+ Case CTLDATEFIELD, CTLTIMEFIELD
+ vFormats = _FormatsList()
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Format&quot;, V_STRING, vFormats) Then GoTo Finally
+ iFormat = ScriptForge.SF_Array.IndexOf(vFormats, pvValue, CaseSensitive := False)
+ If oSession.HasUNOProperty(_ControlModel, &quot;DateFormat&quot;) Then
+ _ControlModel.DateFormat = iFormat
+ ElseIf oSession.HasUNOProperty(_ControlModel, &quot;TimeFormat&quot;) Then
+ _ControlModel.TimeFormat = iFormat
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ListIndex&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;ListIndex&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ Select Case _ControlType
+ Case CTLCOMBOBOX
+ If oSession.HasUNOProperty(_ControlModel, &quot;Text&quot;) And oSession.HasUNOProperty(_ControlModel, &quot;StringItemList&quot;) Then
+ If pvValue &gt;= 0 And pvValue &lt;= UBound(_ControlModel.StringItemList) Then _ControlModel.Text = _ControlModel.StringItemList(CInt(pvValue))
+ End If
+ Case CTLLISTBOX
+ If oSession.HasUNOProperty(_ControlModel, &quot;SelectedItems&quot;) Then _ControlModel.SelectedItems = Array(CInt(pvValue))
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ListSource&quot;)
+ Select Case _ControlType
+ Case CTLCOMBOBOX, CTLLISTBOX
+ If oSession.HasUNOProperty(_ControlModel, &quot;ListSource&quot;) Then
+ If Not ScriptForge.SF_Utils._Validate(pvValue, psProperty, V_STRING) Then Goto Finally
+ With com.sun.star.form.ListSourceType
+ Select Case _ControlModel.ListSourceType
+ Case .QUERY _
+ , .TABLE _
+ , .TABLEFIELDS
+ Set oDatabase = _ParentForm.GetDatabase()
+ If _ControlModel.ListSourceType = .QUERY Then vList = oDatabase.Queries Else vList = oDatabase.Tables
+ If Not ScriptForge.SF_Utils._Validate(pvValue, psProperty, V_STRING, vList) Then Goto Finally
+ If _ControlType = CTLCOMBOBOX Then _ControlModel.ListSource = pvValue Else _ControlModel.ListSource = Array(pvValue)
+ _ControlModel.refresh()
+ Case .SQL
+ Set oDatabase = _ParentForm.GetDatabase()
+ If _ControlType = CTLCOMBOBOX Then _ControlModel.ListSource = oDatabase._ReplaceSquareBrackets(pvValue) Else _ControlModel.ListSource = Array(oDatabase._ReplaceSquareBrackets(pvValue))
+ _ControlModel.refresh()
+ Case .VALUELIST &apos; ListBox only !
+ _ControlModel.ListSource = Split(pvValue, &quot;;&quot;)
+ _ControlModel.StringItemList = _ControlModel.ListSource
+ Case .SQLPASSTHROUGH
+ If _ControlType = CTLCOMBOBOX Then _ControlModel.ListSource = pvValue Else _ControlModel.ListSource = Array(pvValue)
+ _ControlModel.refresh()
+ End Select
+ End With
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;ListSourceType&quot;)
+ With com.sun.star.form.ListSourceType
+ Select Case _ControlType
+ Case CTLCOMBOBOX
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;ListSourceType&quot;, ScriptForge.V_NUMERIC, Array( _
+ .TABLE _
+ , .QUERY _
+ , .SQL _
+ , .SQLPASSTHROUGH _
+ , .TABLEFIELDS _
+ )) Then GoTo Finally
+ Case CTLLISTBOX
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;ListSourceType&quot;, ScriptForge.V_NUMERIC, Array( _
+ .VALUELIST _
+ , .TABLE _
+ , .QUERY _
+ , .SQL _
+ , .SQLPASSTHROUGH _
+ , .TABLEFIELDS _
+ )) Then GoTo Finally
+ Case Else : GoTo CatchType
+ End Select
+ End With
+ If oSession.HasUnoProperty(_ControlModel, &quot;ListSourceType&quot;) Then _ControlModel.ListSourceType = pvValue
+ Case UCase(&quot;Locked&quot;)
+ Select Case _ControlType
+ Case CTLCOMBOBOX, CTLCURRENCYFIELD, CTLDATEFIELD, CTLFILECONTROL, CTLFORMATTEDFIELD, CTLIMAGECONTROL _
+ , CTLLISTBOX, CTLNUMERICFIELD, CTLPATTERNFIELD, CTLTEXTFIELD, CTLTIMEFIELD
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Locked&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;ReadOnly&quot;) Then _ControlModel.ReadOnly = pvValue
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;MultiSelect&quot;)
+ Select Case _ControlType
+ Case CTLLISTBOX
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;MultiSelect&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;MultiSelection&quot;) Then _ControlModel.MultiSelection = pvValue
+ If oSession.HasUnoProperty(_ControlModel, &quot;MultiSelectionSimpleMode&quot;) Then _ControlModel.MultiSelectionSimpleMode = pvValue
+ If oSession.HasUnoProperty(_ControlModel, &quot;SelectedItems&quot;) Then
+ &apos; Cancel selections when MultiSelect becomes False
+ If Not pvValue And UBound(_ControlModel.SelectedItems) &gt; 0 Then
+ lIndex = _ControlModel.SelectedItems(0)
+ _ControlModel.SelectedItems = Array(lIndex)
+ End If
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;OnActionPerformed&quot;), UCase(&quot;OnAdjustmentValueChanged&quot;), UCase(&quot;OnApproveAction&quot;), UCase(&quot;OnApproveReset&quot;), UCase(&quot;OnApproveUpdate&quot;) _
+ , UCase(&quot;OnChanged&quot;), UCase(&quot;OnErrorOccurred&quot;), UCase(&quot;OnFocusGained&quot;), UCase(&quot;OnFocusLost&quot;) _
+ , UCase(&quot;OnItemStateChanged&quot;), UCase(&quot;OnKeyPressed&quot;), UCase(&quot;OnKeyReleased&quot;) _
+ , UCase(&quot;OnMouseDragged&quot;), UCase(&quot;OnMouseEntered&quot;), UCase(&quot;OnMouseExited&quot;), UCase(&quot;OnMouseMoved&quot;) _
+ , UCase(&quot;OnMousePressed&quot;), UCase(&quot;OnMouseReleased&quot;), UCase(&quot;OnResetted&quot;) _
+ , UCase(&quot;OnTextChanged&quot;), UCase(&quot;OnUpdated&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, psProperty, V_STRING) Then Goto Finally
+ If Not IsNull(_ControlModel) Then
+ bSet = SF_Register._RegisterEventScript(_ControlModel _
+ , psProperty _
+ , _GetListener(psProperty) _
+ , pvValue _
+ , _Name _
+ )
+ End If
+ Case UCase(&quot;Picture&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON, CTLIMAGEBUTTON, CTLIMAGECONTROL
+ If Not ScriptForge.SF_Utils._ValidateFile(pvValue, &quot;Picture&quot;) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;ImageURL&quot;) Then _ControlModel.ImageURL = ScriptForge.SF_FileSystem._ConvertToUrl(pvValue)
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;TipText&quot;)
+ Select Case _ControlType
+ Case CTLHIDDENCONTROL : GoTo CatchType
+ Case Else
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;TipText&quot;, V_STRING) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;HelpText&quot;) Then _ControlModel.HelpText = pvValue
+ End Select
+ Case UCase(&quot;TripleState&quot;)
+ Select Case _ControlType
+ Case CTLCHECKBOX
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;TripleState&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;TriState&quot;) Then _ControlModel.TriState = pvValue
+ Case Else : GoTo CatchType
+ End Select
+ Case UCase(&quot;Value&quot;)
+ Select Case _ControlType
+ Case CTLBUTTON &apos;Boolean, toggle buttons only
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;Toggle&quot;) And oSession.HasUnoProperty(_ControlModel, &quot;State&quot;) Then
+ _ControlModel.State = Iif(pvValue, 1, 0)
+ End If
+ Case CTLCHECKBOX &apos;0 = Not checked, 1 = Checked, 2 = Don&apos;t know
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, Array(ScriptForge.V_BOOLEAN, ScriptForge.V_NUMERIC), Array(0, 1, 2, True, False)) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;State&quot;) Then
+ If VarType(pvValue) = ScriptForge.V_BOOLEAN Then pvValue = Iif(pvValue, 1, 0)
+ _ControlModel.State = pvValue
+ End If
+ Case CTLCOMBOBOX
+ If oSession.HasUnoProperty(_ControlModel, &quot;Text&quot;) And oSession.HasUnoProperty(_ControlModel, &quot;StringItemList&quot;) Then
+ If pvValue &lt;&gt; &quot;&quot; Then
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, V_STRING, _ControlModel.StringItemList) Then Goto Finally
+ End If
+ _ControlModel.Text = pvValue
+ End If
+ Case CTLCURRENCYFIELD, CTLNUMERICFIELD &apos;Numeric
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;Value&quot;) Then _ControlModel.Value = pvValue
+ Case CTLDATEFIELD &apos;Date
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, V_DATE) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;Date&quot;) Then
+ Set vSet = New com.sun.star.util.Date
+ vSet.Year = Year(pvValue)
+ vSet.Month = Month(pvValue)
+ vSet.Day = Day(pvValue)
+ _ControlModel.Date = vSet
+ End If
+ Case CTLFILECONTROL
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, V_STRING) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;Text&quot;) Then _ControlModel.Text = ScriptForge.SF_FileSystem._ConvertToUrl(pvValue)
+ Case CTLFORMATTEDFIELD &apos;String or numeric
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, Array(V_STRING, ScriptForge.V_NUMERIC)) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;EffectiveValue&quot;) Then _ControlModel.EffectiveValue = pvValue
+ Case CTLHIDDENCONTROL &apos;String
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, V_STRING) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;HiddenValue&quot;) Then _ControlModel.HiddenValue = pvValue
+ Case CTLLISTBOX &apos;String or number - Only a single value may be set
+ &apos; StringItemList is the list of the items displayed in the box
+ &apos; ValueItemList is the list of the values in the underlying database field
+ &apos; SelectedItems is the list of the indexes in StringItemList of the selected items
+ If oSession.HasUnoProperty(_ControlModel, &quot;StringItemList&quot;) And oSession.HasUnoProperty(_ControlModel, &quot;SelectedItems&quot;) Then
+ &apos; Setting the value on a listbox is allowed only if single value and value in the list
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, Array(V_STRING, ScriptForge.V_NUMERIC)) Then GoTo Finally
+ &apos; The list of allowed values depends on the existence of a bound column
+ If _ListboxBound() Then vList = _ControlModel.ValueItemList Else vList = _ControlModel.StringItemList
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, , vList) Then GoTo Finally
+ _ControlModel.SelectedItems = Array(ScriptForge.SF_Array.IndexOf(vList, pvValue, CaseSensitive := True))
+ End If
+ Case CTLPATTERNFIELD, CTLTEXTFIELD &apos;String
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, V_STRING) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;Text&quot;) Then _ControlModel.Text = pvValue
+ Case CTLRADIOBUTTON &apos;Boolean
+ &apos; A group of radio buttons is presumed sharing the same GroupName
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;State&quot;) Then _ControlModel.State = Iif(pvValue, 1, 0)
+ Case CTLSCROLLBAR &apos;Numeric
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;ScrollValueMin&quot;) Then
+ If pvValue &lt; _ControlModel.ScrollValueMin Then pvValue = _ControlModel.ScrollValueMin
+ End If
+ If oSession.HasUnoProperty(_ControlModel, &quot;ScrollValueMax&quot;) Then
+ If pvValue &gt; _ControlModel.ScrollValueMax Then pvValue = _ControlModel.ScrollValueMax
+ End If
+ If oSession.HasUnoProperty(_ControlModel, &quot;ScrollValue&quot;) Then _ControlModel.ScrollValue = pvValue
+ Case CTLSPINBUTTON &apos;Numeric
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, ScriptForge.V_NUMERIC) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;SpinValueMin&quot;) Then
+ If pvValue &lt; _ControlModel.SpinValueMin Then pvValue = _ControlModel.SpinValueMin
+ End If
+ If oSession.HasUnoProperty(_ControlModel, &quot;SpinValueMax&quot;) Then
+ If pvValue &gt; _ControlModel.SpinValueMax Then pvValue = _ControlModel.SpinValueMax
+ End If
+ If oSession.HasUnoProperty(_ControlModel, &quot;SpinValue&quot;) Then _ControlModel.SpinValue = pvValue
+ Case CTLTIMEFIELD
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Value&quot;, V_DATE) Then GoTo Finally
+ If oSession.HasUnoProperty(_ControlModel, &quot;Time&quot;) Then
+ Set vSet = New com.sun.star.util.Time
+ vSet.Hours = Hour(pvValue)
+ vSet.Minutes = Minute(pvValue)
+ vSet.Seconds = Second(pvValue)
+ _ControlModel.Time = vSet
+ End If
+ Case Else : GoTo CatchType
+ End Select
+ &apos; FINAL COMMITMENT
+ If oSession.HasUNOMethod(_ControlModel, &quot;commit&quot;) Then _ControlModel.commit() &apos; f.i. checkboxes have no commit method ??
+ Case UCase(&quot;Visible&quot;)
+ If Not ScriptForge.SF_Utils._Validate(pvValue, &quot;Visible&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ If oSession.HasUnoMethod(_ControlView, &quot;setVisible&quot;) Then
+ If pvValue Then _ControlModel.EnableVisible = True
+ _ControlView.setVisible(pvValue)
+ End If
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ _PropertySet = bSet
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ bSet = False
+ GoTo Finally
+CatchType:
+ If Len(_ParentForm._FormDocumentName) &gt; 0 Then sFormName = _ParentForm._FormDocumentName &amp; &quot;.&quot; Else sFormName = &quot;&quot;
+ ScriptForge.SF_Exception.RaiseFatal(FORMCONTROLTYPEERROR, _Name, sFormName &amp; _FormName, _ControlType, psProperty)
+ GoTo Finally
+End Function &apos; SFDocuments.SF_FormControl._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;[FORMCONTROL]: Name, Type (formname)
+ _Repr = &quot;[FORMCONTROL]: &quot; &amp; _Name &amp; &quot;, &quot; &amp; _ControlType &amp; &quot; (&quot; &amp; _FormName &amp; &quot;)&quot;
+
+End Function &apos; SFDocuments.SF_FormControl._Repr
+
+REM ============================================ END OF SFDOCUMENTS.SF_FORMCONTROL
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Register.xba b/wizards/source/sfdocuments/SF_Register.xba
new file mode 100644
index 000000000..5baf37afb
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_Register.xba
@@ -0,0 +1,546 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE script:module PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "module.dtd">
+<script:module xmlns:script="http://openoffice.org/2000/script" script:name="SF_Register" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === The SFDocuments library is one of the associated libraries. ===
+REM === Full documentation is available on https://help.libreoffice.org/ ===
+REM =======================================================================================================================
+
+Option Compatible
+Option Explicit
+
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+&apos;&apos;&apos; SF_Register
+&apos;&apos;&apos; ===========
+&apos;&apos;&apos; The ScriptForge framework includes
+&apos;&apos;&apos; the master ScriptForge library
+&apos;&apos;&apos; a number of &quot;associated&quot; libraries SF*
+&apos;&apos;&apos; any user/contributor extension wanting to fit into the framework
+&apos;&apos;&apos;
+&apos;&apos;&apos; The main methods in this module allow the current library to cling to ScriptForge
+&apos;&apos;&apos; - RegisterScriptServices
+&apos;&apos;&apos; Register the list of services implemented by the current library
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+
+REM ================================================================== EXCEPTIONS
+
+REM ================================================================= DEFINITIONS
+
+&apos;&apos;&apos; Strategy for management of Form and FormControl events:
+&apos;&apos;&apos; ------------------------------------------------------
+&apos;&apos;&apos; At the contrary of Dialogs and DialogControls, which are always started from some code,
+&apos;&apos;&apos; Forms and FormControls will be initiated most often by the user, even if the SFDocuments library
+&apos;&apos;&apos; allows to start forms programmatically
+&apos;&apos;&apos;
+&apos;&apos;&apos; For Forms started programmatically, the corresponding objects are built top-down
+&apos;&apos;&apos; Event management of forms and their controls requires to being able to rebuild Form
+&apos;&apos;&apos; and FormControl objects bottom-up
+&apos;&apos;&apos;
+&apos;&apos;&apos; To avoid multiple rebuilds requested by multiple events,
+&apos;&apos;&apos; 1. The active form objects are cached in a global array of _FormCache types
+&apos;&apos;&apos; 2. FormControl objects are cached in Form objects
+&apos;&apos;&apos; 3. The bottom-up rebuild is executed only once, at instance creation
+
+Type _FormCache
+ Terminated As Boolean
+ XUnoForm As Object
+ BasicForm As Object
+End Type
+
+REM ============================================================== PUBLIC METHODS
+
+REM -----------------------------------------------------------------------------
+Public Sub RegisterScriptServices() As Variant
+&apos;&apos;&apos; Register into ScriptForge the list of the services implemented by the current library
+&apos;&apos;&apos; Each library pertaining to the framework must implement its own version of this method
+&apos;&apos;&apos;
+&apos;&apos;&apos; It consists in successive calls to the RegisterService() and RegisterEventManager() methods
+&apos;&apos;&apos; with 2 arguments:
+&apos;&apos;&apos; ServiceName: the name of the service as a case-insensitive string
+&apos;&apos;&apos; ServiceReference: the reference as an object
+&apos;&apos;&apos; If the reference refers to a module, then return the module as an object:
+&apos;&apos;&apos; GlobalScope.Library.Module
+&apos;&apos;&apos; If the reference is a class instance, then return a string referring to the method
+&apos;&apos;&apos; containing the New statement creating the instance
+&apos;&apos;&apos; &quot;libraryname.modulename.function&quot;
+
+ With GlobalScope.ScriptForge.SF_Services
+ .RegisterService(&quot;Document&quot;, &quot;SFDocuments.SF_Register._NewDocument&quot;) &apos; Reference to the function initializing the service
+ .RegisterService(&quot;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
+ .RegisterEventManager(&quot;DocumentEvent&quot;, &quot;SFDocuments.SF_Register._EventManager&quot;) &apos; Reference to the events manager
+ .RegisterEventManager(&quot;FormEvent&quot;, &quot;SFDocuments.SF_Register._FormEventManager&quot;)&apos; Reference to the form and controls events manager
+ End With
+
+End Sub &apos; SFDocuments.SF_Register.RegisterScriptServices
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Private Function _AddFormToCache(ByRef pvUnoForm As Object _
+ , ByRef pvBasicForm As Object _
+ ) As Long
+&apos;&apos;&apos; Add a new entry in the cache array with the references of the actual Form
+&apos;&apos;&apos; If relevant, the last entry of the cache is reused.
+&apos;&apos;&apos; The cache is located in the global _SF_ variable
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pvUnoForm: com.sun.star.form.XForm or com.sun.star.comp.forms.ODatabaseForm
+&apos;&apos;&apos; pvBasicForm: its corresponding Basic object
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The index of the new or modified entry
+
+Dim vCache As New _FormCache &apos; Entry to be added
+Dim lIndex As Long &apos; UBound of _SF_.SFForms
+Dim vCacheArray As Variant &apos; Alias of _SF_.SFForms
+
+Try:
+ vCacheArray = _SF_.SFForms
+
+ If IsEmpty(vCacheArray) Then vCacheArray = Array()
+ lIndex = UBound(vCacheArray)
+ If lIndex &lt; LBound(vCacheArray) Then
+ ReDim vCacheArray(0 To 0)
+ lIndex = 0
+ ElseIf Not vCacheArray(lIndex).Terminated Then &apos; Often last entry can be reused
+ lIndex = lIndex + 1
+ ReDim Preserve vCacheArray(0 To lIndex)
+ End If
+
+ With vCache
+ .Terminated = False
+ Set .XUnoForm = pvUnoForm
+ Set .BasicForm = pvBasicForm
+ End With
+ Set vCacheArray(lIndex) = vCache
+
+ _SF_.SFForms = vCacheArray
+
+Finally:
+ _AddFormToCache = lIndex
+ Exit Function
+End Function &apos; SFDocuments.SF_Register._AddFormToCache
+
+REM -----------------------------------------------------------------------------
+Private Sub _CleanCacheEntry(ByVal plIndex As Long)
+&apos;&apos;&apos; Clean the plIndex-th entry in the Forms cache
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; plIndex: must fit within the actual boundaries of the cache, otherwise the request is ignored
+
+Dim vCache As New _FormCache &apos; Cleaned entry
+
+ With _SF_
+ If Not IsArray(.SFForms) Then Exit Sub
+ If plIndex &lt; LBound(.SFForms) Or plIndex &gt; UBound(.SFForms) Then Exit Sub
+
+ With vCache
+ .Terminated = True
+ Set .XUnoForm = Nothing
+ Set .BasicForm = Nothing
+ End With
+ .SFForms(plIndex) = vCache
+ End With
+
+Finally:
+ Exit Sub
+End Sub &apos; SFDocuments.SF_Register._CleanCacheEntry
+
+REM -----------------------------------------------------------------------------
+Public Function _EventManager(Optional ByRef pvArgs As Variant) As Object
+&apos;&apos;&apos; Returns a Document, Calc or Base object corresponding with the active component
+&apos;&apos;&apos; which triggered the event in argument
+&apos;&apos;&apos; This method should be triggered only thru the invocation of CreateScriptService
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pvEvent: com.sun.star.document.DocumentEvent
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; the output of a Document, Calc, ... service or Nothing
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Sub TriggeredByEvent(ByRef poEvent As Object)
+&apos;&apos;&apos; Dim oDoc As Object
+&apos;&apos;&apos; Set oDoc = CreateScriptService(&quot;SFDocuments.DocumentEvent&quot;, poEvent)
+&apos;&apos;&apos; If Not IsNull(oDoc) Then
+&apos;&apos;&apos; &apos; ... (a valid document has been identified)
+&apos;&apos;&apos; End Sub
+
+Dim oSource As Object &apos; Return value
+Dim vEvent As Variant &apos; Alias of pvArgs(0)
+
+ &apos; Never abort while an event is processed
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Finally
+ Set oSource = Nothing
+
+Check:
+ If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array()
+ If UBound(pvArgs) &gt;= 0 Then vEvent = pvArgs(0) Else Set vEvent = Empty
+ If VarType(vEvent) &lt;&gt; ScriptForge.V_OBJECT Then GoTo Finally
+
+Try:
+ If ScriptForge.SF_Session.UnoObjectType(vEvent) = &quot;com.sun.star.document.DocumentEvent&quot; Then
+ Set oSource = SF_Register._NewDocument(vEvent.Source)
+ End If
+
+Finally:
+ Set _EventManager = oSource
+ Exit Function
+End Function &apos; SFDocuments.SF_Register._EventManager
+
+REM -----------------------------------------------------------------------------
+Private Function _FindFormInCache(ByRef poForm As Object) As Object
+&apos;&apos;&apos; Find the Form based on its XUnoForm
+&apos;&apos;&apos; The Form must not be terminated
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The corresponding Basic Form part or Nothing
+
+Dim oBasicForm As Object &apos; Return value
+Dim oCache As _FormCache &apos; Entry in the cache
+
+ Set oBasicForm = Nothing
+
+Try:
+ With _SF_
+ If Not IsEmpty(.SFForms) Then
+ For Each oCache In .SFForms
+ If EqualUnoObjects(poForm, oCache.XUnoForm) And Not oCache.Terminated Then
+ Set oBasicForm = oCache.BasicForm
+ Exit For
+ End If
+ Next oCache
+ End If
+ End With
+
+Finally:
+ Set _FindFormInCache = oBasicForm
+ Exit Function
+End Function &apos; SFDocuments.SF_Register._FindFormInCache
+
+REM -----------------------------------------------------------------------------
+Public Function _FormEventManager(Optional ByRef pvArgs As Variant) As Object
+&apos;&apos;&apos; Returns a Form or FormControl object corresponding with the form or control
+&apos;&apos;&apos; which triggered the event in argument
+&apos;&apos;&apos; This method should be triggered only thru the invocation of CreateScriptService
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; pvEvent: com.sun.star.lang.EventObject
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; the output of a Form, FormControl service or Nothing
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; Sub TriggeredByEvent(ByRef poEvent As Object)
+&apos;&apos;&apos; Dim oForm As Object
+&apos;&apos;&apos; Set oForm = CreateScriptService(&quot;SFDocuments.FormEvent&quot;, poEvent)
+&apos;&apos;&apos; If Not IsNull(oForm) Then
+&apos;&apos;&apos; &apos; ... (a valid form or subform has been identified)
+&apos;&apos;&apos; End Sub
+
+Dim oSource As Object &apos; Return value
+Dim vEvent As Variant &apos; Alias of pvArgs(0)
+Dim oControlModel As Object &apos; com.sun.star.awt.XControlModel
+Dim oParent As Object &apos; com.sun.star.form.OGridControlModel or com.sun.star.comp.forms.ODatabaseForm
+Dim sParentType As String &apos; &quot;com.sun.star.form.OGridControlModel&quot; or &quot;com.sun.star.comp.forms.ODatabaseForm&quot;
+Dim oSFParent As Object &apos; The parent as a ScriptForge instance: SF_Form or SF_FormControl
+Dim oSFForm As Object &apos; The grand-parent SF_Form instance
+Dim oSession As Object : Set oSession = ScriptForge.SF_Session
+
+ &apos; Never abort while an event is processed
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Finally
+ Set oSource = Nothing
+
+Check:
+ If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array()
+ If UBound(pvArgs) &gt;= 0 Then vEvent = pvArgs(0) Else Set vEvent = Empty
+ If VarType(vEvent) &lt;&gt; ScriptForge.V_OBJECT Then GoTo Finally
+
+Try:
+ If oSession.HasUnoProperty(vEvent, &quot;Source&quot;) Then
+
+ &apos; FORM EVENT
+ If oSession.UnoObjectType(vEvent.Source) = &quot;com.sun.star.comp.forms.ODatabaseForm&quot; Then
+ Set oSource = SF_Register._NewForm(vEvent.Source, pbForceInit := True)
+
+ &apos; CONTROL EVENT
+ Else
+ &apos; A SF_FormControl instance is always created from its parent, either a form, a subform or a table control
+ Set oControlModel = vEvent.Source.Model &apos; The event source is a control view com.sun.star.awt.XControl
+ Set oParent = oControlModel.Parent
+ sParentType = oSession.UnoObjectType(oParent)
+ Select Case sParentType
+ Case &quot;com.sun.star.form.OGridControlModel&quot;
+ Set oSFForm = SF_Register._NewForm(oParent.Parent, pbForceInit := True)
+ Set oSFParent = oSFForm.Controls(oParent.Name)
+ Case &quot;com.sun.star.comp.forms.ODatabaseForm&quot;
+ Set oSFParent = SF_Register._NewForm(oParent, pbForceInit := True)
+ End Select
+ &apos; The final instance is derived from its parent instance
+ Set oSource = oSFParent.Controls(oControlModel.Name)
+
+ End If
+
+ End If
+
+Finally:
+ Set _FormEventManager = oSource
+ Exit Function
+End Function &apos; SFDocuments.SF_Register._FormEventManager
+
+REM -----------------------------------------------------------------------------
+Public Function _GetEventScriptCode(poObject As Object _
+ , ByVal psEvent As String _
+ , ByVal psName As String _
+ ) As String
+&apos;&apos;&apos; Extract from the parent of poObject the Basic script linked to psEvent.
+&apos;&apos;&apos; Helper function common to forms and form controls
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; poObject: a com.sun.star.form.XForm or XControl object
+&apos;&apos;&apos; psEvent: the &quot;On...&quot; name of the event
+&apos;&apos;&apos; psName: the name of the object to be identified from the parent object
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The script to trigger when psEvent occurs
+&apos;&apos;&apos; See Scripting Framework URI Specification : https://wiki.documentfoundation.org/Documentation/DevGuide/Scripting_Framework#Scripting_Framework_URI_Specification
+
+Dim vEvents As Variant &apos; List of available events in the parent object
+ &apos; Array of com.sun.star.script.ScriptEventDescriptor
+Dim sEvent As String &apos; The targeted event name
+Dim oParent As Object &apos; The parent object
+Dim lIndex As Long &apos; The index of the targeted event in the events list of the parent object
+Dim sName As String &apos; The corrected UNO event name
+Dim i As Long
+
+ _GetEventScriptCode = &quot;&quot;
+ On Local Error GoTo Catch
+ If Not ScriptForge.SF_Session.HasUnoMethod(poObject, &quot;getParent&quot;) Then GoTo Finally
+
+Try:
+ &apos; Find form index i.e. find control via getByIndex()
+ &apos; The name is known (= psName) but getByIndex() is not in the same sequence as getElementNames()
+ Set oParent = poObject.getParent()
+ lIndex = -1
+ For i = 0 To oParent.getCount() - 1
+ sName = oParent.getByIndex(i).Name
+ If (sName = psName) Then
+ lIndex = i
+ Exit For
+ End If
+ Next i
+ If lIndex &lt; 0 Then GoTo Finally &apos; Not found, should not happen
+
+ &apos; Find script triggered by event
+ vEvents = oParent.getScriptEvents(lIndex) &apos; Returns an array
+ &apos; Fix historical typo error
+ sEvent = Replace(LCase(Mid(psEvent, 3, 1)) &amp; Mid(psEvent, 4), &quot;errorOccurred&quot;, &quot;errorOccured&quot;)
+ For i = 0 To UBound(vEvents)
+ If vEvents(i).EventMethod = sEvent Then
+ _GetEventScriptCode = vEvents(i).ScriptCode
+ Exit For
+ End If
+ Next i
+
+Finally:
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Register._GetEventScriptCode
+
+REM -----------------------------------------------------------------------------
+Public Function _NewDocument(Optional ByVal pvArgs As Variant) As Object
+&apos;&apos;&apos; Create a new instance of the (super) SF_Document class or of one of its subclasses (SF_Calc, ...)
+&apos; Args:
+&apos;&apos;&apos; WindowName: see the definition of WindowName in the description of the UI service
+&apos;&apos;&apos; If absent, the document is presumed to be in the active window
+&apos;&apos;&apos; If WindowName is an object, it must be a component
+&apos;&apos;&apos; (com.sun.star.lang.XComponent or com.sun.star.comp.dba.ODatabaseDocument)
+&apos;&apos;&apos; Returns: the instance or Nothing
+
+Dim oDocument As Object &apos; Return value
+Dim oSuperDocument As Object &apos; Companion superclass document
+Dim vWindowName As Variant &apos; Alias of pvArgs(0)
+Dim oEnum As Object &apos; com.sun.star.container.XEnumeration
+Dim oComp As Object &apos; com.sun.star.lang.XComponent
+Dim vWindow As Window &apos; A single component
+Dim oUi As Object &apos; &quot;UI&quot; service
+Dim bFound As Boolean &apos; True if the document is found on the desktop
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ If IsMissing(pvArgs) Or IsEmpty(pvArgs) Then pvArgs = Array()
+ If Not IsArray(pvArgs) Then pvArgs = Array(pvArgs) &apos; Needed when _NewDocument called from _EventManager
+ If UBound(pvArgs) &gt;= 0 Then vWindowName = pvArgs(0) Else vWindowName = &quot;&quot;
+ If Not ScriptForge.SF_Utils._Validate(vWindowName, &quot;WindowName&quot;, Array(V_STRING, ScriptForge.V_OBJECT)) Then GoTo Finally
+ Set oDocument = Nothing
+
+Try:
+ Set oUi = ScriptForge.SF_Services.CreateScriptService(&quot;UI&quot;)
+ Select Case VarType(vWindowName)
+ Case V_STRING
+ If Len(vWindowName) &gt; 0 Then
+ bFound = False
+ Set oEnum = StarDesktop.Components().createEnumeration
+ Do While oEnum.hasMoreElements
+ Set oComp = oEnum.nextElement
+ vWindow = oUi._IdentifyWindow(oComp)
+ With vWindow
+ &apos; Does the current window match the argument ?
+ If (Len(.WindowFileName) &gt; 0 And .WindowFileName = ScriptForge.SF_FileSystem._ConvertToUrl(vWindowName)) _
+ Or (Len(.WindowName) &gt; 0 And .WindowName = vWindowName) _
+ Or (Len(.WindowTitle) &gt; 0 And .WindowTitle = vWindowName) Then
+ bFound = True
+ Exit Do
+ End If
+ End With
+ Loop
+ Else
+ bFound = True
+ vWindow = oUi._IdentifyWindow(StarDesktop.CurrentComponent)
+ End If
+ Case ScriptForge.V_OBJECT &apos; com.sun.star.lang.XComponent
+ bFound = True
+ vWindow = oUi._IdentifyWindow(vWindowName)
+ End Select
+
+ If bFound And Not IsNull(vWindow.Frame) And Len(vWindow.DocumentType) &gt; 0 Then
+ &apos; Create the right subclass and associate to it a new instance of the superclass
+ Select Case vWindow.DocumentType
+ Case &quot;Base&quot;
+ Set oDocument = New SF_Base
+ Set oSuperDocument = New SF_Document
+ Set oDocument.[_Super] = oSuperDocument &apos; Now both super and subclass are twinned
+ Set oSuperDocument.[_SubClass] = oDocument
+ Case &quot;Calc&quot;
+ Set oDocument = New SF_Calc
+ Set oSuperDocument = New SF_Document
+ Set oDocument.[_Super] = oSuperDocument &apos; Now both super and subclass are twinned
+ Set oSuperDocument.[_SubClass] = oDocument
+ Case &quot;Writer&quot;
+ Set oDocument = New SF_Writer
+ Set oSuperDocument = New SF_Document
+ Set oDocument.[_Super] = oSuperDocument &apos; Now both super and subclass are twinned
+ Set oSuperDocument.[_SubClass] = oDocument
+ Case Else &apos; Only superclass
+ Set oDocument = New SF_Document
+ Set oSuperDocument = oDocument
+ End Select
+ With oDocument &apos; Initialize attributes of subclass
+ Set .[Me] = oDocument
+ Set ._Component = vWindow.Component
+ &apos; Initialize specific attributes
+ Select Case vWindow.DocumentType
+ Case &quot;Base&quot;
+ Set ._DataSource = ._Component.DataSource
+ Case Else
+ End Select
+ End With
+ With oSuperDocument &apos; Initialize attributes of superclass
+ Set .[Me] = oSuperDocument
+ Set ._Component = vWindow.Component
+ Set ._Frame = vWindow.Frame
+ ._WindowName = vWindow.WindowName
+ ._WindowTitle = vWindow.WindowTitle
+ ._WindowFileName = vWindow.WindowFileName
+ ._DocumentType = vWindow.DocumentType
+ End With
+ End If
+
+Finally:
+ Set _NewDocument = oDocument
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Register._NewDocument
+
+REM -----------------------------------------------------------------------------
+Public Function _NewForm(ByRef poForm As Object _
+ , Optional pbForceInit As Boolean _
+ ) As Object
+&apos;&apos;&apos; Returns an existing or a new SF_Form instance based on the argument
+&apos;&apos;&apos; If the instance is new (not found in cache), the minimal members are initialized
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; poForm: com.sun.star.form.XForm or com.sun.star.comp.forms.ODatabaseForm
+&apos;&apos;&apos; pbForceInit: when True, initialize the form instance. Default = False
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A SF_Form instance
+
+Dim oForm As Object &apos; Return value
+
+Try:
+ Set oForm = SF_Register._FindFormInCache(poForm)
+ If IsNull(oForm) Then &apos; Not found
+ If IsMissing(pbForceInit) Or IsEmpty(pbForceInit) Then pbForceInit = False
+ Set oForm = New SF_Form
+ With oForm
+ ._Name = poForm.Name
+ Set .[Me] = oForm
+ Set ._Form = poForm
+ If pbForceInit Then ._Initialize()
+ End With
+ End If
+
+Finally:
+ Set _NewForm = oForm
+ Exit Function
+End Function &apos; SFDocuments.SF_Register._NewForm
+
+REM -----------------------------------------------------------------------------
+Public Function _RegisterEventScript(poObject As Object _
+ , ByVal psEvent As String _
+ , ByVal psListener As String _
+ , ByVal psScriptCode As String _
+ , ByVal psName As String _
+ ) As Boolean
+&apos;&apos;&apos; Register a script event (psEvent) to poObject (Form, SubForm or Control)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; poObject: a com.sun.star.form.XForm or XControl object
+&apos;&apos;&apos; psEvent: the &quot;On...&quot; name of the event
+&apos;&apos;&apos; psListener: the listener name corresponding with the event
+&apos;&apos;&apos; psScriptCode: The script to trigger when psEvent occurs
+&apos;&apos;&apos; See Scripting Framework URI Specification : https://wiki.documentfoundation.org/Documentation/DevGuide/Scripting_Framework#Scripting_Framework_URI_Specification
+&apos;&apos;&apos; psName: the name of the object to associate with the event
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+
+Dim oEvent As Object &apos; com.sun.star.script.ScriptEventDescriptor
+Dim sEvent As String &apos; The targeted event name
+Dim oParent As Object &apos; The parent object
+Dim lIndex As Long &apos; The index of the targeted event in the events list of the parent object
+Dim sName As String &apos; The corrected UNO event name
+Dim i As Long
+
+ _RegisterEventScript = False
+ On Local Error GoTo Catch
+ If Not ScriptForge.SF_Session.HasUnoMethod(poObject, &quot;getParent&quot;) Then GoTo Finally
+
+Try:
+ &apos; Find object&apos;s internal index i.e. how to reach it via getByIndex()
+ Set oParent = poObject.getParent()
+ lIndex = -1
+ For i = 0 To oParent.getCount() - 1
+ sName = oParent.getByIndex(i).Name
+ If (sName = psName) Then
+ lIndex = i
+ Exit For
+ End If
+ Next i
+ If lIndex &lt; 0 Then GoTo Finally &apos; Not found, should not happen
+
+ &apos; Fix historical typo error
+ sEvent = Replace(LCase(Mid(psEvent, 3, 1)) &amp; Mid(psEvent, 4), &quot;errorOccurred&quot;, &quot;errorOccured&quot;)
+ &apos; Apply new script code. Erasing it is done with a specific UNO method
+ If psScriptCode = &quot;&quot; Then
+ oParent.revokeScriptEvent(lIndex, psListener, sEvent, &quot;&quot;)
+ Else
+ Set oEvent = CreateUnoStruct(&quot;com.sun.star.script.ScriptEventDescriptor&quot;)
+ With oEvent
+ .ListenerType = psListener
+ .EventMethod = sEvent
+ .ScriptType = &quot;Script&quot; &apos; Better than &quot;Basic&quot;
+ .ScriptCode = psScriptCode
+ End With
+ oParent.registerScriptEvent(lIndex, oEvent)
+ End If
+ _RegisterEventScript = True
+
+Finally:
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Register._RegisterEventScript
+
+REM ============================================== END OF SFDOCUMENTS.SF_REGISTER
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Writer.xba b/wizards/source/sfdocuments/SF_Writer.xba
new file mode 100644
index 000000000..eded35de9
--- /dev/null
+++ b/wizards/source/sfdocuments/SF_Writer.xba
@@ -0,0 +1,635 @@
+<?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_Writer" 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_Writer
+&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_Writer module is focused on :
+&apos;&apos;&apos; TBD
+&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;
+&apos;&apos;&apos; Service invocation examples:
+&apos;&apos;&apos; 1) From the UI service
+&apos;&apos;&apos; Dim ui As Object, oDoc As Object
+&apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
+&apos;&apos;&apos; Set oDoc = ui.CreateDocument(&quot;Writer&quot;, ...)
+&apos;&apos;&apos; &apos; or Set oDoc = ui.OpenDocument(&quot;C:\Me\MyFile.odt&quot;)
+&apos;&apos;&apos; 2) Directly if the document is already opened
+&apos;&apos;&apos; Dim oDoc As Object
+&apos;&apos;&apos; Set oDoc = CreateScriptService(&quot;SFDocuments.Writer&quot;, &quot;Untitled 1&quot;) &apos; Default = ActiveWindow
+&apos;&apos;&apos; &apos; or Set oDoc = CreateScriptService(&quot;SFDocuments.Writer&quot;, &quot;Untitled 1&quot;) &apos; Untitled 1 is presumed a Writer document
+&apos;&apos;&apos; &apos; The substring &quot;SFDocuments.&quot; in the service name is optional
+&apos;&apos;&apos;
+&apos;&apos;&apos; Definitions:
+&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;
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&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 WRITERFORMNOTFOUNDERROR = &quot;WRITERFORMNOTFOUNDERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object
+Private [_Super] As Object &apos; Document superclass, which the current instance is a subclass of
+Private ObjectType As String &apos; Must be WRITER
+Private ServiceName As String
+
+&apos; Window component
+Private _Component As Object &apos; com.sun.star.lang.XComponent
+
+REM ============================================================ MODULE CONSTANTS
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ Set [_Super] = Nothing
+ ObjectType = &quot;WRITER&quot;
+ ServiceName = &quot;SFDocuments.Writer&quot;
+ Set _Component = Nothing
+End Sub &apos; SFDocuments.SF_Writer Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDocuments.SF_Writer 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_Writer Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM ===================================================================== METHODS
+
+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; WRITERFORMNOTFOUNDERROR 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 Writer document
+
+Const cstThisSub = &quot;SFDocuments.Writer.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 = ISDOCFORM
+ Set ._Component = _Component
+ ._Initialize()
+ 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_Writer.Forms
+
+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.Writer.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_Writer.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Writer service as an array
+
+ Methods = Array( _
+ &quot;Forms&quot; _
+ , &quot;PrintOut&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Writer.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.Writer.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_Writer.PrintOut
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Writer class as an array
+
+ Properties = Array( _
+ &quot;CustomProperties&quot; _
+ , &quot;Description&quot; _
+ , &quot;DocumentProperties&quot; _
+ , &quot;DocumentType&quot; _
+ , &quot;ExportFilters&quot; _
+ , &quot;ImportFilters&quot; _
+ , &quot;IsBase&quot; _
+ , &quot;IsCalc&quot; _
+ , &quot;IsDraw&quot; _
+ , &quot;IsImpress&quot; _
+ , &quot;IsMath&quot; _
+ , &quot;IsWriter&quot; _
+ , &quot;Keywords&quot; _
+ , &quot;Readonly&quot; _
+ , &quot;Subject&quot; _
+ , &quot;Title&quot; _
+ , &quot;XComponent&quot; _
+ )
+
+End Function &apos; SFDocuments.SF_Writer.Properties
+
+REM -----------------------------------------------------------------------------
+Private Function SetProperty(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.Writer.set&quot; &amp; psProperty
+ If IsMissing(pvValue) Then pvValue = Empty
+ &apos;ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) &apos; Validation done in Property Lets
+
+ If IsNull(oSession) Then Set oSession = ScriptForge.SF_Services.CreateScriptService(&quot;Session&quot;)
+ bSet = True
+ Select Case UCase(psProperty)
+ Case UCase(&quot;CustomProperties&quot;)
+ CustomProperties = pvValue
+ Case UCase(&quot;Description&quot;)
+ Description = pvValue
+ Case UCase(&quot;Keywords&quot;)
+ Keywords = pvValue
+ Case UCase(&quot;Subject&quot;)
+ Subject = pvValue
+ Case UCase(&quot;Title&quot;)
+ Title = pvValue
+ Case Else
+ bSet = False
+ End Select
+
+Finally:
+ SetProperty = bSet
+ &apos;ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Writer.SetProperty
+
+REM ======================================================= SUPERCLASS PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get CustomProperties() As Variant
+ CustomProperties = [_Super].GetProperty(&quot;CustomProperties&quot;)
+End Property &apos; SFDocuments.SF_Writer.CustomProperties
+
+REM -----------------------------------------------------------------------------
+Property Let CustomProperties(Optional ByVal pvCustomProperties As Variant)
+ [_Super].CustomProperties = pvCustomProperties
+End Property &apos; SFDocuments.SF_Writer.CustomProperties
+
+REM -----------------------------------------------------------------------------
+Property Get Description() As Variant
+ Description = [_Super].GetProperty(&quot;Description&quot;)
+End Property &apos; SFDocuments.SF_Writer.Description
+
+REM -----------------------------------------------------------------------------
+Property Let Description(Optional ByVal pvDescription As Variant)
+ [_Super].Description = pvDescription
+End Property &apos; SFDocuments.SF_Writer.Description
+
+REM -----------------------------------------------------------------------------
+Property Get DocumentProperties() As Variant
+ DocumentProperties = [_Super].GetProperty(&quot;DocumentProperties&quot;)
+End Property &apos; SFDocuments.SF_Writer.DocumentProperties
+
+REM -----------------------------------------------------------------------------
+Property Get DocumentType() As String
+ DocumentType = [_Super].GetProperty(&quot;DocumentType&quot;)
+End Property &apos; SFDocuments.SF_Writer.DocumentType
+
+REM -----------------------------------------------------------------------------
+Property Get ExportFilters() As Variant
+ ExportFilters = [_Super].GetProperty(&quot;ExportFilters&quot;)
+End Property &apos; SFDocuments.SF_Writer.ExportFilters
+
+REM -----------------------------------------------------------------------------
+Property Get ImportFilters() As Variant
+ ImportFilters = [_Super].GetProperty(&quot;ImportFilters&quot;)
+End Property &apos; SFDocuments.SF_Writer.ImportFilters
+
+REM -----------------------------------------------------------------------------
+Property Get IsBase() As Boolean
+ IsBase = [_Super].GetProperty(&quot;IsBase&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsBase
+
+REM -----------------------------------------------------------------------------
+Property Get IsCalc() As Boolean
+ IsCalc = [_Super].GetProperty(&quot;IsCalc&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsCalc
+
+REM -----------------------------------------------------------------------------
+Property Get IsDraw() As Boolean
+ IsDraw = [_Super].GetProperty(&quot;IsDraw&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsDraw
+
+REM -----------------------------------------------------------------------------
+Property Get IsImpress() As Boolean
+ IsImpress = [_Super].GetProperty(&quot;IsImpress&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsImpress
+
+REM -----------------------------------------------------------------------------
+Property Get IsMath() As Boolean
+ IsMath = [_Super].GetProperty(&quot;IsMath&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsMath
+
+REM -----------------------------------------------------------------------------
+Property Get IsWriter() As Boolean
+ IsWriter = [_Super].GetProperty(&quot;IsWriter&quot;)
+End Property &apos; SFDocuments.SF_Writer.IsWriter
+
+REM -----------------------------------------------------------------------------
+Property Get Keywords() As Variant
+ Keywords = [_Super].GetProperty(&quot;Keywords&quot;)
+End Property &apos; SFDocuments.SF_Writer.Keywords
+
+REM -----------------------------------------------------------------------------
+Property Let Keywords(Optional ByVal pvKeywords As Variant)
+ [_Super].Keywords = pvKeywords
+End Property &apos; SFDocuments.SF_Writer.Keywords
+
+REM -----------------------------------------------------------------------------
+Property Get Readonly() As Variant
+ Readonly = [_Super].GetProperty(&quot;Readonly&quot;)
+End Property &apos; SFDocuments.SF_Writer.Readonly
+
+REM -----------------------------------------------------------------------------
+Property Get Subject() As Variant
+ Subject = [_Super].GetProperty(&quot;Subject&quot;)
+End Property &apos; SFDocuments.SF_Writer.Subject
+
+REM -----------------------------------------------------------------------------
+Property Let Subject(Optional ByVal pvSubject As Variant)
+ [_Super].Subject = pvSubject
+End Property &apos; SFDocuments.SF_Writer.Subject
+
+REM -----------------------------------------------------------------------------
+Property Get Title() As Variant
+ Title = [_Super].GetProperty(&quot;Title&quot;)
+End Property &apos; SFDocuments.SF_Writer.Title
+
+REM -----------------------------------------------------------------------------
+Property Let Title(Optional ByVal pvTitle As Variant)
+ [_Super].Title = pvTitle
+End Property &apos; SFDocuments.SF_Writer.Title
+
+REM -----------------------------------------------------------------------------
+Property Get XComponent() As Variant
+ XComponent = [_Super].GetProperty(&quot;XComponent&quot;)
+End Property &apos; SFDocuments.SF_Writer.XComponent
+
+REM ========================================================== SUPERCLASS METHODS
+
+REM -----------------------------------------------------------------------------
+Public Function Activate() As Boolean
+ Activate = [_Super].Activate()
+End Function &apos; SFDocuments.SF_Writer.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function CloseDocument(Optional ByVal SaveAsk As Variant) As Boolean
+ CloseDocument = [_Super].CloseDocument(SaveAsk)
+End Function &apos; SFDocuments.SF_Writer.CloseDocument
+
+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_Writer.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_Writer.ExportAsPDF
+
+REM -----------------------------------------------------------------------------
+Public Function RemoveMenu(Optional ByVal MenuHeader As Variant) As Boolean
+ RemoveMenu = [_Super].RemoveMenu(MenuHeader)
+End Function &apos; SFDocuments.SF_Writer.RemoveMenu
+
+REM -----------------------------------------------------------------------------
+Public Sub RunCommand(Optional ByVal Command As Variant _
+ , ParamArray Args As Variant _
+ )
+ [_Super].RunCommand(Command, Args)
+End Sub &apos; SFDocuments.SF_Writer.RunCommand
+
+REM -----------------------------------------------------------------------------
+Public Function Save() As Boolean
+ Save = [_Super].Save()
+End Function &apos; SFDocuments.SF_Writer.Save
+
+REM -----------------------------------------------------------------------------
+Public Function SaveAs(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
+ SaveAs = [_Super].SaveAs(FileName, Overwrite, Password, FilterName, FilterOptions)
+End Function &apos; SFDocuments.SF_Writer.SaveAs
+
+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_Writer.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_Writer.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_Writer._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_Writer._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.Writer.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_Writer._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the SF_Writer 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;[Writer]: &quot; &amp; [_Super]._FileIdent()
+
+End Function &apos; SFDocuments.SF_Writer._Repr
+
+REM ============================================ END OF SFDOCUMENTS.SF_WRITER
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/__License.xba b/wizards/source/sfdocuments/__License.xba
new file mode 100644
index 000000000..47cca670f
--- /dev/null
+++ b/wizards/source/sfdocuments/__License.xba
@@ -0,0 +1,26 @@
+<?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="__License" script:language="StarBasic" script:moduleType="normal">
+&apos;&apos;&apos; Copyright 2019-2022 Jean-Pierre LEDURE, Rafael LIMA, Alain ROMEDENNE
+
+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 =======================================================================================================================
+
+&apos;&apos;&apos; ScriptForge is distributed in the hope that it will be useful,
+&apos;&apos;&apos; but WITHOUT ANY WARRANTY; without even the implied warranty of
+&apos;&apos;&apos; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+&apos;&apos;&apos; ScriptForge is free software; you can redistribute it and/or modify it under the terms of either (at your option):
+
+&apos;&apos;&apos; 1) The Mozilla Public License, v. 2.0. If a copy of the MPL was not
+&apos;&apos;&apos; distributed with this file, you can obtain one at http://mozilla.org/MPL/2.0/ .
+
+&apos;&apos;&apos; 2) The GNU Lesser General Public License as published by
+&apos;&apos;&apos; the Free Software Foundation, either version 3 of the License, or
+&apos;&apos;&apos; (at your option) any later version. If a copy of the LGPL was not
+&apos;&apos;&apos; distributed with this file, see http://www.gnu.org/licenses/ .
+
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/dialog.xlb b/wizards/source/sfdocuments/dialog.xlb
new file mode 100644
index 000000000..62e84ea5c
--- /dev/null
+++ b/wizards/source/sfdocuments/dialog.xlb
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
+<library:library xmlns:library="http://openoffice.org/2000/library" library:name="SFDocuments" library:readonly="false" library:passwordprotected="false"/> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/script.xlb b/wizards/source/sfdocuments/script.xlb
new file mode 100644
index 000000000..ff4495124
--- /dev/null
+++ b/wizards/source/sfdocuments/script.xlb
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE library:library PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "library.dtd">
+<library:library xmlns:library="http://openoffice.org/2000/library" library:name="SFDocuments" library:readonly="false" library:passwordprotected="false">
+ <library:element library:name="__License"/>
+ <library:element library:name="SF_Document"/>
+ <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:element library:name="SF_FormControl"/>
+ <library:element library:name="SF_Writer"/>
+ <library:element library:name="SF_Chart"/>
+ <library:element library:name="SF_DocumentListener"/>
+</library:library> \ No newline at end of file