summaryrefslogtreecommitdiff
path: root/wizards
diff options
context:
space:
mode:
authorJean-Pierre Ledure <jp@ledure.be>2022-11-01 17:27:21 +0100
committerJean-Pierre Ledure <jp@ledure.be>2022-11-02 11:26:46 +0100
commit985c77b570807dcc558ccff4a51430fe489b68fd (patch)
treea359700248abe601efb1c5341af1542ef9bc689b /wizards
parent4f88a1d624a97f53b58d3cdf36b2f5cfc237da59 (diff)
ScriptForge = (SFDatabases) New Datasheet service
A datasheet is the visual representation of tabular data produced by a database. In the user interface of LibreOffice it is the result of the opening of a table or a query. In this case the concerned Base document must be open. In the context of ScriptForge, a datasheet may be opened automatically by script code : - either by reproducing the behaviour of the user interface - or at any moment. In this case the Base document does not need to be open. Additionally, any SELECT SQL statement may define the datasheet display. The proposed API allows for either datasheets (opened manually of by code) in particular to know which cell is selected and its content. Properties: ColumnHeaders CurrentColumn CurrentRow LastRow Source SourceType XComponent XControlModel XTabControllerModel Methods Activate ApplyFilter CloseDatasheet GetValue GetText GoToCell OrderBy The Base and Database services are enriched with the OpenTable OpenQuery methods. The Database service gets also a new OpenSql method. The whole set of properties and methods is available both for Basic and Python scripts. This new service requires a new help page dedicated to this service, as well as an update of the pages about the Base and Database services. Change-Id: Ib409ce74d95de78f2792ba53e7ae554eab0867ab Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142118 Tested-by: Jenkins Reviewed-by: Jean-Pierre Ledure <jp@ledure.be>
Diffstat (limited to 'wizards')
-rw-r--r--wizards/Package_sfdatabases.mk1
-rw-r--r--wizards/source/scriptforge/SF_Services.xba2
-rw-r--r--wizards/source/scriptforge/SF_UI.xba31
-rw-r--r--wizards/source/scriptforge/python/scriptforge.py117
-rw-r--r--wizards/source/sfdatabases/SF_Database.xba189
-rw-r--r--wizards/source/sfdatabases/SF_Datasheet.xba744
-rw-r--r--wizards/source/sfdatabases/SF_Register.xba58
-rw-r--r--wizards/source/sfdatabases/script.xlb1
-rw-r--r--wizards/source/sfdialogs/SF_Dialog.xba4
-rw-r--r--wizards/source/sfdocuments/SF_Base.xba107
-rw-r--r--wizards/source/sfdocuments/SF_Calc.xba50
-rw-r--r--wizards/source/sfdocuments/SF_Document.xba4
12 files changed, 1232 insertions, 76 deletions
diff --git a/wizards/Package_sfdatabases.mk b/wizards/Package_sfdatabases.mk
index 81fc55750291..bc5636fa1b2f 100644
--- a/wizards/Package_sfdatabases.mk
+++ b/wizards/Package_sfdatabases.mk
@@ -21,6 +21,7 @@ $(eval $(call gb_Package_Package,wizards_basicsrvsfdatabases,$(SRCDIR)/wizards/s
$(eval $(call gb_Package_add_files,wizards_basicsrvsfdatabases,$(LIBO_SHARE_FOLDER)/basic/SFDatabases,\
SF_Database.xba \
+ SF_Datasheet.xba \
SF_Register.xba \
__License.xba \
dialog.xlb \
diff --git a/wizards/source/scriptforge/SF_Services.xba b/wizards/source/scriptforge/SF_Services.xba
index 627dc4d2e8fe..b72298ea3046 100644
--- a/wizards/source/scriptforge/SF_Services.xba
+++ b/wizards/source/scriptforge/SF_Services.xba
@@ -129,7 +129,7 @@ Try:
Case &quot;document&quot;, &quot;calc&quot;, &quot;writer&quot;, &quot;base&quot;, &quot;documentevent&quot;, &quot;formevent&quot;
sLibrary = &quot;SFDocuments&quot;
Case &quot;dialog&quot;, &quot;dialogevent&quot; : sLibrary = &quot;SFDialogs&quot;
- Case &quot;database&quot; : sLibrary = &quot;SFDatabases&quot;
+ Case &quot;database&quot;, &quot;datasheet&quot; : sLibrary = &quot;SFDatabases&quot;
Case &quot;unittest&quot; : sLibrary = &quot;SFUnitTests&quot;
Case &quot;menu&quot;, &quot;popupmenu&quot; : sLibrary = &quot;SFWidgets&quot;
Case Else
diff --git a/wizards/source/scriptforge/SF_UI.xba b/wizards/source/scriptforge/SF_UI.xba
index c8a7f9a8f861..e64011b9ed78 100644
--- a/wizards/source/scriptforge/SF_UI.xba
+++ b/wizards/source/scriptforge/SF_UI.xba
@@ -53,6 +53,7 @@ Type Window
WindowTitle As String &apos; Only mean to identify new documents
WindowFileName As String &apos; URL of file name
DocumentType As String &apos; Writer, Calc, ...
+ ParentName As String &apos; Identifier of the parent Base file when Window is a subcomponent
End Type
&apos; The progress/status bar of the active window
@@ -72,11 +73,12 @@ Const IMPRESSDOCUMENT = &quot;Impress&quot;
Const MATHDOCUMENT = &quot;Math&quot;
Const WRITERDOCUMENT = &quot;Writer&quot;
-&apos; Window subtypes - Not supported yet
-Const BASETABLE = &quot;BASETABLE&quot;
-Const BASEQUERY = &quot;BASEQUERY&quot;
-Const BASEREPORT = &quot;BASEREPORT&quot;
-Const BASEDIAGRAM = &quot;BASEDIAGRAM&quot;
+&apos; Window subtypes
+Const TABLEDATA = &quot;TableData&quot;
+Const QUERYDATA = &quot;QueryData&quot;
+Const SQLDATA = &quot;SqlData&quot;
+Const BASEREPORT = &quot;BaseReport&quot;
+Const BASEDIAGRAM = &quot;BaseDiaram&quot;
&apos; Macro execution modes
Const cstMACROEXECNORMAL = 0 &apos; Default, execution depends on user configuration and choice
@@ -1246,7 +1248,8 @@ Public Function _IdentifyWindow(ByRef poComponent As Object) As Object
Dim oWindow As Window &apos; Return value
Dim sImplementation As String &apos; Component&apos;s implementationname
Dim sIdentifier As String &apos; Component&apos;s identifier
-Dim vArg As Variant &apos; One single item of the Args UNO property
+Dim vSelection As Variant &apos; Array of poCOmponent.Selection property values
+Dim iCommandType As Integer &apos; Datasheet type
Dim FSO As Object &apos; Alias for SF_FileSystem
If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
@@ -1261,6 +1264,7 @@ Dim FSO As Object &apos; Alias for SF_FileSystem
.WindowTitle = &quot;&quot;
.WindowFileName = &quot;&quot;
.DocumentType = &quot;&quot;
+ .ParentName = &quot;&quot;
If IsNull(poComponent) Then GoTo Finally
If SF_Session.HasUnoProperty(poComponent, &quot;ImplementationName&quot;) Then sImplementation = poComponent.ImplementationName
If SF_Session.HasUnoProperty(poComponent, &quot;Identifier&quot;) Then sIdentifier = poComponent.Identifier
@@ -1272,7 +1276,20 @@ Dim FSO As Object &apos; Alias for SF_FileSystem
.WindowFileName = SF_Utils._GetPropertyValue(poComponent.Args, &quot;URL&quot;)
If Len(.WindowFileName) &gt; 0 Then .WindowName = FSO.GetName(FSO._ConvertFromUrl(.WindowFileName))
.DocumentType = BASEDOCUMENT
- Case &quot;org.openoffice.comp.dbu.ODatasourceBrowser&quot;
+ Case &quot;org.openoffice.comp.dbu.ODatasourceBrowser&quot; &apos; Base datasheet (table, query or sql in read mode
+ Set .Frame = poComponent.Frame
+ If Not IsEmpty(poComponent.Selection) Then &apos; Empty for (F4) DatasourceBrowser !!
+ vSelection = poComponent.Selection
+ .WindowName = SF_Utils._GetPropertyValue(vSelection, &quot;Command&quot;)
+ iCommandType = SF_Utils._GetPropertyValue(vSelection, &quot;CommandType&quot;)
+ Select Case iCommandType
+ Case com.sun.star.sdb.CommandType.TABLE : .DocumentType = TABLEDATA
+ Case com.sun.star.sdb.CommandType.QUERY : .DocumentType = QUERYDATA
+ Case com.sun.star.sdb.CommandType.COMMAND : .DocumentType = SQLDATA
+ End Select
+ .ParentName = SF_Utils._GetPropertyValue(vSelection, &quot;DataSourceName&quot;)
+ .WindowTitle = .WindowName
+ End If
Case &quot;org.openoffice.comp.dbu.OTableDesign&quot;, &quot;org.openoffice.comp.dbu.OQueryDesign&quot; &apos; Table or Query in Edit mode
Case &quot;org.openoffice.comp.dbu.ORelationDesign&quot;
Case &quot;com.sun.star.comp.sfx2.BackingComp&quot; &apos; Welcome screen
diff --git a/wizards/source/scriptforge/python/scriptforge.py b/wizards/source/scriptforge/python/scriptforge.py
index c05d5349f0b4..940c355f1e44 100644
--- a/wizards/source/scriptforge/python/scriptforge.py
+++ b/wizards/source/scriptforge/python/scriptforge.py
@@ -224,7 +224,7 @@ class ScriptForge(object, metaclass = _Singleton):
scope, script = script.split('#')
if '.py$' in script.lower(): # Python
if len(scope) == 0:
- scope = 'share' # Default for Python
+ scope = 'share' # Default for Python
# Provide an alternate helper script depending on test context
if script.startswith(cls.pythonhelpermodule) and hasattr(cls, 'pythonhelpermodule2'):
script = cls.pythonhelpermodule2 + script[len(cls.pythonhelpermodule):]
@@ -233,10 +233,10 @@ class ScriptForge(object, metaclass = _Singleton):
uri = 'vnd.sun.star.script:{0}?language=Python&location={1}'.format(script, scope)
else: # Basic
if len(scope) == 0:
- scope = 'application' # Default for Basic
+ scope = 'application' # Default for Basic
lib = ''
if len(script.split('.')) < 3:
- lib = cls.library + '.' # Default library = ScriptForge
+ lib = cls.library + '.' # Default library = ScriptForge
uri = 'vnd.sun.star.script:{0}{1}?language=Basic&location={2}'.format(lib, script, scope)
# Get the script object
fullscript = ('@' if paramarray else '') + scope + ':' + script
@@ -327,7 +327,7 @@ class ScriptForge(object, metaclass = _Singleton):
elif returntuple[cstVarType] == ScriptForge.V_DATE:
dat = SFScriptForge.SF_Basic.CDateFromUnoDateTime(returntuple[cstValue])
return dat
- else: # All other scalar values
+ else: # All other scalar values
pass
return returntuple[cstValue]
@@ -347,6 +347,7 @@ class ScriptForge(object, metaclass = _Singleton):
# etc ...
copyFile, copyfile = CopyFile, CopyFile
"""
+
def camelCase(key):
return key[0].lower() + key[1:]
@@ -354,8 +355,8 @@ class ScriptForge(object, metaclass = _Singleton):
# Synonyms of properties
if hasattr(cls, 'serviceproperties'):
dico = cls.serviceproperties
- dicosyn = dict(zip(map(str.lower, dico.keys()), dico.keys())) # lower case
- cc = dict(zip(map(camelCase, dico.keys()), dico.keys())) # camel Case
+ dicosyn = dict(zip(map(str.lower, dico.keys()), dico.keys())) # lower case
+ cc = dict(zip(map(camelCase, dico.keys()), dico.keys())) # camel Case
dicosyn.update(cc)
setattr(cls, 'propertysynonyms', dicosyn)
# Synonyms of methods. A method is a public callable attribute
@@ -448,7 +449,7 @@ class SFServices(object):
"""
# Python-Basic protocol constants and flags
vbGet, vbLet, vbMethod, vbSet = 2, 4, 1, 8 # CallByName constants
- flgPost = 32 # The method or the property implies a hardcoded post-processing
+ flgPost = 32 # The method or the property implies a hardcoded post-processing
flgDateArg = 64 # Invoked service method may contain a date argument
flgDateRet = 128 # Invoked service method can return a date
flgArrayArg = 512 # 1st argument can be a 2D array
@@ -506,7 +507,7 @@ class SFServices(object):
prop = self.GetProperty(name)
self.__dict__[name] = prop
return prop
- else: # Get Property from Basic and do not store it
+ else: # Get Property from Basic and do not store it
return self.GetProperty(name)
# Execute the usual attributes getter
return super(SFServices, self).__getattribute__(name)
@@ -545,7 +546,7 @@ class SFServices(object):
def Dispose(self):
if self.serviceimplementation == 'basic':
- if self.objectreference >= len(ScriptForge.servicesmodules): # Do not dispose predefined module objects
+ if self.objectreference >= len(ScriptForge.servicesmodules): # Do not dispose predefined module objects
self.ExecMethod(self.vbMethod, 'Dispose')
self.objectreference = -1
@@ -564,7 +565,7 @@ class SFServices(object):
calltype = self.vbGet + (self.flgUno if propertyname[0] == 'X' else 0)
if arg is None:
return self.EXEC(self.objectreference, calltype, propertyname)
- else: # There are a few cases (Calc ...) where GetProperty accepts an argument
+ else: # There are a few cases (Calc ...) where GetProperty accepts an argument
return self.EXEC(self.objectreference, calltype, propertyname, arg)
return None
@@ -667,7 +668,7 @@ class SFScriptForge:
:param unodate: com.sun.star.util.DateTime, com.sun.star.util.Date or com.sun.star.util.Time
:return: the equivalent datetime.datetime
"""
- date = datetime.datetime(1899, 12, 30, 0, 0, 0, 0) # Idem as Basic builtin TimeSeria() function
+ date = datetime.datetime(1899, 12, 30, 0, 0, 0, 0) # Idem as Basic builtin TimeSeria() function
datetype = repr(type(unodate))
if 'com.sun.star.util.DateTime' in datetype:
if 1900 <= unodate.Year <= datetime.MAXYEAR:
@@ -693,16 +694,16 @@ class SFScriptForge:
"""
unodate = uno.createUnoStruct('com.sun.star.util.DateTime')
unodate.Year, unodate.Month, unodate.Day, unodate.Hours, unodate.Minutes, unodate.Seconds, \
- unodate.NanoSeconds, unodate.IsUTC = \
- 1899, 12, 30, 0, 0, 0, 0, False # Identical to Basic TimeSerial() function
+ unodate.NanoSeconds, unodate.IsUTC = \
+ 1899, 12, 30, 0, 0, 0, 0, False # Identical to Basic TimeSerial() function
if isinstance(date, float):
date = time.localtime(date)
if isinstance(date, time.struct_time):
if 1900 <= date[0] <= 32767:
- unodate.Year, unodate.Month, unodate.Day, unodate.Hours, unodate.Minutes, unodate.Seconds =\
+ unodate.Year, unodate.Month, unodate.Day, unodate.Hours, unodate.Minutes, unodate.Seconds = \
date[0:6]
- else: # Copy only the time related part
+ else: # Copy only the time related part
unodate.Hours, unodate.Minutes, unodate.Seconds = date[3:3]
elif isinstance(date, (datetime.datetime, datetime.date, datetime.time)):
if isinstance(date, (datetime.datetime, datetime.date)):
@@ -712,7 +713,7 @@ class SFScriptForge:
unodate.Hours, unodate.Minutes, unodate.Seconds, unodate.NanoSeconds = \
date.hour, date.minute, date.second, date.microsecond * 1000
else:
- return date # Not recognized as a date
+ return date # Not recognized as a date
return unodate
@classmethod
@@ -817,6 +818,7 @@ class SFScriptForge:
DESK = 'com.sun.star.frame.Desktop'
desktop = smgr.createInstanceWithContext(DESK, ctx)
return desktop
+
starDesktop, stardesktop = StarDesktop, StarDesktop
@property
@@ -832,8 +834,9 @@ class SFScriptForge:
return None
impl = comp.ImplementationName
if impl in ('com.sun.star.comp.basic.BasicIDE', 'com.sun.star.comp.sfx2.BackingComp'):
- return None # None when Basic IDE or welcome screen
+ return None # None when Basic IDE or welcome screen
return comp
+
thisComponent, thiscomponent = ThisComponent, ThisComponent
@property
@@ -844,7 +847,7 @@ class SFScriptForge:
Above behaviour cannot be reproduced in Python.
:return: the current Base (main) component or None when not a Base document or one of its subcomponents
"""
- comp = self.ThisComponent # Get the current component
+ comp = self.ThisComponent # Get the current component
if comp is None:
return None
#
@@ -869,6 +872,7 @@ class SFScriptForge:
if db.ImplementationName == targetimpl:
return db
return None
+
thisDatabaseDocument, thisdatabasedocument = ThisDatabaseDocument, ThisDatabaseDocument
@classmethod
@@ -1055,7 +1059,7 @@ class SFScriptForge:
# Direct call because RaiseFatal forces an execution stop in Basic
if len(args) == 0:
args = (None,)
- return cls.SIMPLEEXEC('@SF_Exception.RaiseFatal', (errorcode, *args)) # With ParamArray
+ return cls.SIMPLEEXEC('@SF_Exception.RaiseFatal', (errorcode, *args)) # With ParamArray
@classmethod
def _RaiseFatal(cls, sub, subargs, errorcode, *args):
@@ -1221,11 +1225,12 @@ class SFScriptForge:
dialogobj = dialog.objectreference if isinstance(dialog, SFDialogs.SF_Dialog) else dialog
return self.ExecMethod(self.vbMethod + self.flgObject, 'AddTextsFromDialog', dialogobj)
- def ExportToPOTFile(self, filename, header = '', encoding= 'UTF-8'):
+ def ExportToPOTFile(self, filename, header = '', encoding = 'UTF-8'):
return self.ExecMethod(self.vbMethod, 'ExportToPOTFile', filename, header, encoding)
def GetText(self, msgid, *args):
return self.ExecMethod(self.vbMethod, 'GetText', msgid, *args)
+
_ = GetText
# #########################################################################
@@ -1409,13 +1414,13 @@ class SFScriptForge:
serviceproperties = dict()
# Class constants Where to find an invoked library ?
- SCRIPTISEMBEDDED = 'document' # in the document
- SCRIPTISAPPLICATION = 'application' # in any shared library (Basic)
- SCRIPTISPERSONAL = 'user' # in My Macros (Python)
- SCRIPTISPERSOXT = 'user:uno_packages' # in an extension installed for the current user (Python)
- SCRIPTISSHARED = 'share' # in LibreOffice macros (Python)
+ SCRIPTISEMBEDDED = 'document' # in the document
+ SCRIPTISAPPLICATION = 'application' # in any shared library (Basic)
+ SCRIPTISPERSONAL = 'user' # in My Macros (Python)
+ SCRIPTISPERSOXT = 'user:uno_packages' # in an extension installed for the current user (Python)
+ SCRIPTISSHARED = 'share' # in LibreOffice macros (Python)
SCRIPTISSHAROXT = 'share:uno_packages' # in an extension installed for all users (Python)
- SCRIPTISOXT = 'uno_packages' # in an extension but the installation parameters are unknown (Python)
+ SCRIPTISOXT = 'uno_packages' # in an extension but the installation parameters are unknown (Python)
@classmethod
def ExecuteBasicScript(cls, scope = '', script = '', *args):
@@ -1539,11 +1544,13 @@ class SFScriptForge:
@property
def AtEndOfStream(self):
return self.GetProperty('AtEndOfStream')
+
atEndOfStream, atendofstream = AtEndOfStream, AtEndOfStream
@property
def Line(self):
return self.GetProperty('Line')
+
line = Line
def CloseFile(self):
@@ -1629,6 +1636,7 @@ class SFScriptForge:
@property
def ActiveWindow(self):
return self.ExecMethod(self.vbMethod, 'ActiveWindow')
+
activeWindow, activewindow = ActiveWindow, ActiveWindow
def Activate(self, windowname = ''):
@@ -1743,9 +1751,57 @@ class SFDatabases:
def GetRows(self, sqlcommand, directsql = False, header = False, maxrows = 0):
return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'GetRows', sqlcommand, directsql, header, maxrows)
+ def OpenQuery(self, queryname):
+ return self.ExecMethod(self.vbMethod, 'OpenQuery', queryname)
+
+ def OpenSql(self, sql, directsql = False):
+ return self.ExecMethod(self.vbMethod, 'OpenSql', sql, directsql)
+
+ def OpenTable(self, tablename):
+ return self.ExecMethod(self.vbMethod, 'OpenTable', tablename)
+
def RunSql(self, sqlcommand, directsql = False):
return self.ExecMethod(self.vbMethod, 'RunSql', sqlcommand, directsql)
+ # #########################################################################
+ # SF_Datasheet CLASS
+ # #########################################################################
+ class SF_Datasheet(SFServices):
+ """
+ A datasheet is the visual representation of tabular data produced by a database.
+ A datasheet may be opened automatically by script code at any moment.
+ The Base document owning the data may or may not be opened.
+ Any SELECT SQL statement may trigger the datasheet display.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDatabases.Datasheet'
+ servicesynonyms = ('datasheet', 'sfdatabases.datasheet')
+ serviceproperties = dict(ColumnHeaders = False, CurrentColumn = False, CurrentRow = False, LastRow = False,
+ SOurce = False, SourceType = False, XComponent = False, XControlModel = False,
+ XTabControllerModel = False)
+
+ def Activate(self):
+ return self.ExecMethod(self.vbMethod, 'Activate')
+
+ def ApplyFilter(self, filter = ''):
+ return self.ExecMethod(self.vbMethod, 'ApplyFilter', filter)
+
+ def CloseDatasheet(self):
+ return self.ExecMethod(self.vbMethod, 'CloseDatasheet')
+
+ def GetText(self, column = 0):
+ return self.ExecMethod(self.vbMethod, 'GetText', column)
+
+ def GetValue(self, column = 0):
+ return self.ExecMethod(self.vbMethod, 'GetValue', column)
+
+ def GoToCell(self, row = 0, column = 0):
+ return self.ExecMethod(self.vbMethod, 'GoToCell', row, column)
+
+ def OrderBy(self, order = ''):
+ return self.ExecMethod(self.vbMethod, 'OrderBy', order)
+
# #####################################################################################################################
# SFDialogs CLASS (alias of SFDialogs Basic library) ###
@@ -1799,7 +1855,7 @@ class SFDialogs:
def Center(self, parent = ScriptForge.cstSymMissing):
parentclasses = (SFDocuments.SF_Document, SFDocuments.SF_Base, SFDocuments.SF_Calc, SFDocuments.SF_Writer,
- SFDialogs.SF_Dialog)
+ SFDialogs.SF_Dialog)
parentobj = parent.objectreference if isinstance(parent, parentclasses) else parent
return self.ExecMethod(self.vbMethod + self.flgObject + self.flgHardCode, 'Center', parentobj)
@@ -2004,6 +2060,12 @@ class SFDocuments:
def OpenFormDocument(self, formdocument, designmode = False):
return self.ExecMethod(self.vbMethod, 'OpenFormDocument', formdocument, designmode)
+ def OpenQuery(self, queryname):
+ return self.ExecMethod(self.vbMethod, 'OpenQuery', queryname)
+
+ def OpenTable(self, tablename):
+ return self.ExecMethod(self.vbMethod, 'OpenTable', tablename)
+
def PrintOut(self, formdocument, pages = '', copies = 1):
return self.ExecMethod(self.vbMethod, 'PrintOut', formdocument, pages, copies)
@@ -2546,7 +2608,6 @@ def CreateScriptService(service, *args, **kwargs):
createScriptService, createscriptservice = CreateScriptService, CreateScriptService
-
# ######################################################################
# Lists the scripts, that shall be visible inside the Basic/Python IDE
# ######################################################################
diff --git a/wizards/source/sfdatabases/SF_Database.xba b/wizards/source/sfdatabases/SF_Database.xba
index 804084aff28e..6994f791a433 100644
--- a/wizards/source/sfdatabases/SF_Database.xba
+++ b/wizards/source/sfdatabases/SF_Database.xba
@@ -13,7 +13,7 @@ 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_Database
-&apos;&apos;&apos; =========
+&apos;&apos;&apos; ===========
&apos;&apos;&apos; Management of databases embedded in or related to Base documents
&apos;&apos;&apos; Each instance of the current class represents a single database, with essentially its tables, queries and data
&apos;&apos;&apos;
@@ -27,7 +27,7 @@ Option Explicit
&apos;&apos;&apos;
&apos;&apos;&apos; Service invocation and usage:
&apos;&apos;&apos; 1) To access any database at anytime
-&apos;&apos;&apos; Dim myDatabase As Object
+&apos;&apos;&apos; Dim myDatabase As Object
&apos;&apos;&apos; Set myDatabase = CreateScriptService(&quot;SFDatabases.Database&quot;, FileName, , [ReadOnly], [User, [Password]])
&apos;&apos;&apos; &apos; Args:
&apos;&apos;&apos; &apos; FileName: the name of the Base file compliant with the SF_FileSystem.FileNaming notation
@@ -38,7 +38,7 @@ Option Explicit
&apos;&apos;&apos; myDatabase.CloseDatabase()
&apos;&apos;&apos;
&apos;&apos;&apos; 2) To access the database related to the current Base document
-&apos;&apos;&apos; Dim myDoc As Object, myDatabase As Object, ui As Object
+&apos;&apos;&apos; Dim myDoc As Object, myDatabase As Object, ui As Object
&apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
&apos;&apos;&apos; Set myDoc = ui.OpenBaseDocument(&quot;myDb.odb&quot;)
&apos;&apos;&apos; Set myDatabase = myDoc.GetDatabase() &apos; user and password are supplied here, if needed
@@ -401,12 +401,128 @@ Public Function Methods() As Variant
, &quot;DMin&quot; _
, &quot;DSum&quot; _
, &quot;GetRows&quot; _
+ , &quot;OpenQuery&quot; _
+ , &quot;OpenSql&quot; _
+ , &quot;OpenTable&quot; _
, &quot;RunSql&quot; _
)
End Function &apos; SFDatabases.SF_Database.Methods
REM -----------------------------------------------------------------------------
+Public Function OpenQuery(Optional ByVal QueryName As Variant) As Object
+&apos;&apos;&apos; Open the query given by its name
+&apos;&apos;&apos; The datasheet will live independently from any other (typically Base) component
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; QueryName: a valid query name as a case-sensitive string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if the query could be opened, otherwise Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Query name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDb.OpenQuery(&quot;myQuery&quot;)
+
+Dim oOpen As Object &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Database.OpenQuery&quot;
+Const cstSubArgs = &quot;QueryName&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(QueryName, &quot;QueryName&quot;, V_STRING, Queries) Then GoTo Finally
+ End If
+
+Try:
+ Set oOpen = _OpenDatasheet(QueryName, com.sun.star.sdb.CommandType.QUERY _
+ , _Connection.Queries.getByName(QueryName).EscapeProcessing)
+
+Finally:
+ Set OpenQuery = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.OpenQuery
+
+REM -----------------------------------------------------------------------------
+Public Function OpenSql(Optional ByRef Sql As Variant _
+ , Optional ByVal DirectSql As Variant _
+ ) As Object
+&apos;&apos;&apos; Open the datasheet based on a SQL SELECT statement.
+&apos;&apos;&apos; The datasheet will live independently from any other (typically Base) component
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Sql: a valid Sql statement as a case-sensitive string.
+&apos;&apos;&apos; Identifiers may be surrounded by square brackets
+&apos;&apos;&apos; DirectSql: when True, the statement is processed by the targeted RDBMS
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if it could be opened, otherwise Nothing
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDb.OpenSql(&quot;SELECT * FROM [Customers] ORDER BY [CITY]&quot;)
+
+Dim oOpen As Object &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Database.OpenSql&quot;
+Const cstSubArgs = &quot;Sql, [DirectSql=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ If IsMissing(DirectSql) Or IsEmpty(DirectSql) Then DirectSql = False
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(Sql, &quot;Sql&quot;, V_STRING) Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(DirectSql, &quot;DirectSql&quot;, ScriptForge.V_BOOLEAN) Then GoTo Finally
+ End If
+
+Try:
+ Set oOpen = _OpenDatasheet(_ReplaceSquareBrackets(Sql), com.sun.star.sdb.CommandType.COMMAND, Not DirectSql)
+
+Finally:
+ Set OpenSql = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.OpenSql
+
+REM -----------------------------------------------------------------------------
+Public Function OpenTable(Optional ByVal TableName As Variant) As Object
+&apos;&apos;&apos; Open the table given by its name
+&apos;&apos;&apos; The datasheet will live independently from any other (typically Base) component
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TableName: a valid table name as a case-sensitive string
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if the table could be opened, otherwise Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Table name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDb.OpenTable(&quot;myTable&quot;)
+
+Dim oOpen As Object &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Database.OpenTable&quot;
+Const cstSubArgs = &quot;TableName&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(TableName, &quot;TableName&quot;, V_STRING, Tables) Then GoTo Finally
+ End If
+
+Try:
+ Set oOpen = _OpenDatasheet(TableName, com.sun.star.sdb.CommandType.TABLE, True)
+
+Finally:
+ Set OpenTable = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.OpenTable
+
+REM -----------------------------------------------------------------------------
Public Function Properties() As Variant
&apos;&apos;&apos; Return the list or properties of the Database class as an array
@@ -664,8 +780,8 @@ Private Function _GetColumnValue(ByRef poResultSet As Object _
&apos;&apos;&apos; Get the data stored in the current record of a result set in a given column
&apos;&apos;&apos; The type of the column is found in the resultset&apos;s metadata
&apos;&apos;&apos; Args:
-&apos;&apos;&apos; poResultSet: com.sun.star.sdbc.XResultSet
-&apos;&apos;&apos; plColIndex: the index of the column to extract the value from
+&apos;&apos;&apos; poResultSet: com.sun.star.sdbc.XResultSet or com.sun.star.awt.XTabControllerModel
+&apos;&apos;&apos; plColIndex: the index of the column to extract the value from. Starts at 1
&apos;&apos;&apos; pbReturnBinary: when True, the method returns the content of a binary field,
&apos;&apos;&apos; as long as its length does not exceed a maximum length.
&apos;&apos;&apos; Default = False: binary fields are not returned, only their length
@@ -685,7 +801,7 @@ Dim lSize As Long &apos; Binary field length
Const cstMaxBinlength = 2 * 65535
On Local Error Goto 0 &apos; Disable error handler
- vValue = Null &apos; Default value if error
+ vValue = Empty &apos; Default value if error
If IsMissing(pbReturnBinary) Then pbReturnBinary = False
With com.sun.star.sdbc.DataType
@@ -701,17 +817,17 @@ Const cstMaxBinlength = 2 * 65535
If Not ScriptForge.SF_Session.HasUNOMethod(oStream, &quot;getLength&quot;) Then &apos; When no recordset
lSize = cstMaxBinLength
Else
- lSize = CLng(oValue.getLength())
+ lSize = CLng(oStream.getLength())
End If
If lSize &lt;= cstMaxBinLength And pbReturnBinary Then
vValue = Array()
- oValue.readBytes(vValue, lSize)
+ oStream.readBytes(vValue, lSize)
Else &apos; Return length of field, not content
vValue = lSize
End If
End If
End If
- oValue.closeInput()
+ If Not IsNull(oStream) Then oStream.closeInput()
Case .BIT, .BOOLEAN : vValue = poResultSet.getBoolean(plColIndex)
Case .DATE
vDateTime = poResultSet.getDate(plColIndex)
@@ -755,6 +871,61 @@ Const cstMaxBinlength = 2 * 65535
End Function &apos; SFDatabases.SF_Database.GetColumnValue
REM -----------------------------------------------------------------------------
+Public Function _OpenDatasheet(Optional ByVal psCommand As Variant _
+ , piDatasheetType As Integer _
+ , pbEscapeProcessing As Boolean _
+ ) As Object
+&apos;&apos;&apos; Open the datasheet given by its name and its type
+&apos;&apos;&apos; The datasheet will live independently from any other component
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; psCommand: a valid table or query name or an SQL statement as a case-sensitive string
+&apos;&apos;&apos; piDatasheetType: one of the com.sun.star.sdb.CommandType constants
+&apos;&apos;&apos; pbEscapeProcessing: == Not DirectSql
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if the datasheet could be opened, otherwise Nothing
+
+Dim oOpen As Object &apos; Return value
+Dim oNewDatasheet As Object &apos; com.sun.star.lang.XComponent
+Dim oURL As Object &apos; com.sun.star.util.URL
+Dim oDispatch As Object &apos; com.sun.star.frame.XDispatch
+Dim vArgs As Variant &apos; Array of property values
+
+ On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Try:
+ &apos; Setup the dispatcher
+ Set oURL = New com.sun.star.util.URL
+ oURL.Complete = &quot;.component:DB/DataSourceBrowser&quot;
+ Set oDispatch = StarDesktop.queryDispatch(oURL, &quot;_Blank&quot;, 8)
+
+ &apos; Setup the arguments of the component to create
+ With ScriptForge.SF_Utils
+ vArgs = Array( _
+ ._MakePropertyValue(&quot;ActiveConnection&quot;, _Connection) _
+ , ._MakePropertyValue(&quot;CommandType&quot;, piDatasheetType) _
+ , ._MakePropertyValue(&quot;Command&quot;, psCommand) _
+ , ._MakePropertyValue(&quot;ShowMenu&quot;, True) _
+ , ._MakePropertyValue(&quot;ShowTreeView&quot;, False) _
+ , ._MakePropertyValue(&quot;ShowTreeViewButton&quot;, False) _
+ , ._MakePropertyValue(&quot;Filter&quot;, &quot;&quot;) _
+ , ._MakePropertyValue(&quot;ApplyFilter&quot;, False) _
+ , ._MakePropertyValue(&quot;EscapeProcessing&quot;, pbEscapeProcessing) _
+ )
+ End With
+
+ &apos; Open the targeted datasheet
+ Set oNewDatasheet = oDispatch.dispatchWithReturnValue(oURL, vArgs)
+ If Not IsNull(oNewDatasheet) Then Set oOpen = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Datasheet&quot;, [Me], oNewDatasheet)
+
+Finally:
+ Set _OpenDatasheet = oOpen
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base._OpenDatasheet
+
+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:
diff --git a/wizards/source/sfdatabases/SF_Datasheet.xba b/wizards/source/sfdatabases/SF_Datasheet.xba
new file mode 100644
index 000000000000..5550e35df30c
--- /dev/null
+++ b/wizards/source/sfdatabases/SF_Datasheet.xba
@@ -0,0 +1,744 @@
+<?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_Datasheet" script:language="StarBasic" script:moduleType="normal">REM =======================================================================================================================
+REM === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+REM === The SFDatabases 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_Datasheet
+&apos;&apos;&apos; ============
+&apos;&apos;&apos; A datasheet is the visual representation of tabular data produced by a database.
+&apos;&apos;&apos; In the user interface of LibreOffice it is the result of the opening of
+&apos;&apos;&apos; a table or a query. In this case the concerned Base document must be open.
+&apos;&apos;&apos;
+&apos;&apos;&apos; In the context of ScriptForge, a datasheet may be opened automatically by script code :
+&apos;&apos;&apos; - either by reproducing the behaviour of the user interface
+&apos;&apos;&apos; - or at any moment. In this case the Base document may or may not be opened.
+&apos;&apos;&apos; Additionally, any SELECT SQL statement may trigger the datasheet display.
+&apos;&apos;&apos;
+&apos;&apos;&apos; The proposed API allows for either datasheets (opened manually of by code) in particular
+&apos;&apos;&apos; to know which cell is selected and its content.
+&apos;&apos;&apos;
+&apos;&apos;&apos; Service invocation:
+&apos;&apos;&apos; 1) From an open Base document
+&apos;&apos;&apos; Set ui = CreateScriptService(&quot;UI&quot;)
+&apos;&apos;&apos; Set oBase = ui.getDocument(&quot;/home/user/Documents/myDb.odb&quot;)
+&apos;&apos;&apos; Set oSheet1 = oBase.OpenTable(&quot;Customers&quot;) &apos; or OpenQuery(...)
+&apos;&apos;&apos; Set oSheet2 = oBase.Datasheets(&quot;Products&quot;) &apos; when the datasheet has been opened manually
+&apos;&apos;&apos; 2) Independently from a Base document
+&apos;&apos;&apos; Set oDatabase = CreateScriptService(&quot;Database&quot;, &quot;/home/user/Documents/myDb.odb&quot;)
+&apos;&apos;&apos; Set oSheet = oDatabase.OpenTable(&quot;Customers&quot;)
+&apos;&apos;&apos;
+&apos;&apos;&apos; Detailed user documentation:
+&apos;&apos;&apos; https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_datasheet.html?DbPAR=BASIC
+&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;&apos;
+
+REM ================================================================== EXCEPTIONS
+
+Private Const DOCUMENTDEADERROR = &quot;DOCUMENTDEADERROR&quot;
+
+REM ============================================================= PRIVATE MEMBERS
+
+Private [Me] As Object
+Private [_Parent] As Object &apos; Base instance when opened from a Base document by code
+ &apos; or Database instance when opened without Base document
+Private ObjectType As String &apos; Must be DATASHEET
+Private ServiceName As String
+
+Private _Component As Object &apos; com.sun.star.lang.XComponent - org.openoffice.comp.dbu.ODatasourceBrowser
+Private _Frame As Object &apos; com.sun.star.frame.XFrame
+Private _ParentBase As Object &apos; The parent SF_Base instance (may be void)
+Private _ParentDatabase As Object &apos; The parent SF_Database instance (must not be void)
+Private _SheetType As String &apos; TABLE, QUERY or SQL
+Private _ParentType As String &apos; BASE or DATABASE
+Private _BaseFileName As String &apos; URL format of parent Base file
+Private _Command As String &apos; Table name, query name or SQL statement
+Private _DirectSql As Boolean &apos; When True, SQL processed by RDBMS
+Private _TabControllerModel As Object &apos; com.sun.star.awt.XTabControllerModel - com.sun.star.comp.forms.ODatabaseForm
+Private _ControlModel As Object &apos; com.sun.star.awt.XControlModel - com.sun.star.form.OGridControlModel
+Private _ControlView As Object &apos; com.sun.star.awt.XControl - org.openoffice.comp.dbu.ODatasourceBrowser
+Private _ColumnHeaders As Variant &apos; List of column headers as an array of strings
+
+REM ============================================================ MODULE CONSTANTS
+
+REM ====================================================== CONSTRUCTOR/DESTRUCTOR
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Initialize()
+ Set [Me] = Nothing
+ Set [_Parent] = Nothing
+ ObjectType = &quot;DATASHEET&quot;
+ ServiceName = &quot;SFDatabases.Datasheet&quot;
+ Set _Component = Nothing
+ Set _Frame = Nothing
+ Set _ParentBase = Nothing
+ Set _ParentDatabase = Nothing
+ _SheetType = &quot;&quot;
+ _ParentType = &quot;&quot;
+ _BaseFileName = &quot;&quot;
+ _Command = &quot;&quot;
+ _DirectSql = False
+ Set _TabControllerModel = Nothing
+ Set _ControlModel = Nothing
+ Set _ControlView = Nothing
+ _ColumnHeaders = Array()
+End Sub &apos; SFDatabases.SF_Datasheet Constructor
+
+REM -----------------------------------------------------------------------------
+Private Sub Class_Terminate()
+ Call Class_Initialize()
+End Sub &apos; SFDatabases.SF_Datasheet Destructor
+
+REM -----------------------------------------------------------------------------
+Public Function Dispose() As Variant
+ Call Class_Terminate()
+ Set Dispose = Nothing
+End Function &apos; SFDatabases.SF_Datasheet Explicit Destructor
+
+REM ================================================================== PROPERTIES
+
+REM -----------------------------------------------------------------------------
+Property Get ColumnHeaders() As Variant
+&apos;&apos;&apos; Returns the list of column headers of the datasheet as an array of strings
+ ColumnHeaders = _PropertyGet(&quot;ColumnHeaders&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.ColumnHeaders
+
+REM -----------------------------------------------------------------------------
+Property Get CurrentColumn() As String
+&apos;&apos;&apos; Returns the currently selected column by its name
+ CurrentColumn = _PropertyGet(&quot;CurrentColumn&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.CurrentColumn
+
+REM -----------------------------------------------------------------------------
+Property Get CurrentRow() As Long
+&apos;&apos;&apos; Returns the currently selected row by its number &gt;= 1
+ CurrentRow = _PropertyGet(&quot;CurrentRow&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.CurrentRow
+
+REM -----------------------------------------------------------------------------
+Property Get LastRow() As Long
+&apos;&apos;&apos; Returns the total number of rows
+&apos;&apos;&apos; The process may imply to move the cursor to the last available row.
+&apos;&apos;&apos; Afterwards the cursor is reset to the current row.
+ LastRow = _PropertyGet(&quot;LastRow&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.LastRow
+
+REM -----------------------------------------------------------------------------
+Property Get Source() As String
+&apos;&apos;&apos; Returns the source of the data: table name, query name or sql statement
+ Source = _PropertyGet(&quot;Source&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.Source
+
+REM -----------------------------------------------------------------------------
+Property Get SourceType() As String
+&apos;&apos;&apos; Returns thetype of source of the data: TABLE, QUERY or SQL
+ SourceType = _PropertyGet(&quot;SourceType&quot;)
+End Property &apos; SFDatabases.SF_Datasheet.SourceType
+
+REM -----------------------------------------------------------------------------
+Property Get XComponent() As Object
+&apos;&apos;&apos; Returns the com.sun.star.lang.XComponent UNO object representing the datasheet
+ XComponent = _PropertyGet(&quot;XComponent&quot;)
+End Property &apos; SFDocuments.SF_Document.XComponent
+
+REM -----------------------------------------------------------------------------
+Property Get XControlModel() As Object
+&apos;&apos;&apos; Returns the com.sun.star.lang.XControl UNO object representing the datasheet
+ XControlModel = _PropertyGet(&quot;XControlModel&quot;)
+End Property &apos; SFDocuments.SF_Document.XControlModel
+
+REM -----------------------------------------------------------------------------
+Property Get XTabControllerModel() As Object
+&apos;&apos;&apos; Returns the com.sun.star.lang.XTabControllerModel UNO object representing the datasheet
+ XTabControllerModel = _PropertyGet(&quot;XTabControllerModel&quot;)
+End Property &apos; SFDocuments.SF_Document.XTabControllerModel
+
+REM ===================================================================== METHODS
+
+REM -----------------------------------------------------------------------------
+Public Sub Activate()
+&apos;&apos;&apos; Make the actual datasheet active
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.Activate()
+
+Dim oContainer As Object &apos; com.sun.star.awt.XWindow
+Const cstThisSub = &quot;SFDatabases.Datasheet.Activate&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+Check:
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+Try:
+ Set oContainer = _Component.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
+
+Finally:
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Sub
+Catch:
+ GoTo Finally
+End Sub &apos; SFDatabases.SF_Datasheet.Activate
+
+REM -----------------------------------------------------------------------------
+Public Function ApplyFilter(Optional ByVal Filter As Variant) As Boolean
+&apos;&apos;&apos; Apply the given filter to the actual datasheet
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Filter: a SQL WHERE clause without the WHERE keyword
+&apos;&apos;&apos; Table and field names may be surrounded by square brackets
+&apos;&apos;&apos; When the argument is the zero-length string or absent, the actual filter is removed
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.ApplyFilter(&quot;[ShipCountry]=&apos;USA&apos;&quot;)
+
+Dim bApply As Boolean &apos; Return value
+Dim sFilter As String &apos; Filter after replacement of square brackets by real delimiter
+Const cstThisSub = &quot;SFDatabases.Datasheet.ApplyFilter&quot;
+Const cstSubArgs = &quot;[Filter=&quot;&quot;&quot;&quot;]&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bApply = False
+
+Check:
+ If IsMissing(Filter) Or IsEmpty(Filter) Then Filter = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Filter, &quot;Filter&quot;, V_STRING) Then GoTo Catch
+ End If
+
+Try:
+ With _TabControllerModel
+ If Len(Filter) &gt; 0 Then
+ .Filter = _ParentDatabase._ReplaceSquareBrackets(Filter)
+ .ApplyFilter = True
+ .reload()
+ Else
+ .Filter = &quot;&quot;
+ .ApplyFilter = False
+ .reload()
+ End If
+ End With
+
+ bApply = True
+
+Finally:
+ ApplyFilter = bApply
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.ApplyFilter
+
+REM -----------------------------------------------------------------------------
+Public Function CloseDatasheet() As Boolean
+&apos;&apos;&apos; Close the actual datasheet
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.CloseDatasheet()
+
+Dim bClose As Boolean &apos; Return value
+Const cstThisSub = &quot;SFDatabases.Datasheet.CloseDatasheet&quot;
+Const cstSubArgs = &quot;&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bClose = False
+
+Check:
+ SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive() Then GoTo Finally
+
+Try:
+ _TabControllerModel.close()
+ _Frame.close(True)
+ _Frame.dispose()
+ Dispose()
+ bClose = True
+
+Finally:
+ CloseDatasheet = bClose
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.CloseDatasheet
+
+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 propRATTCerty
+&apos;&apos;&apos; If the property does not exist, returns Null
+
+Const cstThisSub = &quot;SFDatabases.Datasheet.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; SFDatabases.SF_Datasheet.GetProperty
+
+REM -----------------------------------------------------------------------------
+Public Function GetText(Optional ByVal Column As Variant) As String
+&apos;&apos;&apos; Get the text in the given column of the current row.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Column: the name of the column as a string or its position (&gt;= 1). Default = the current column
+&apos;&apos;&apos; If the argument exceeds the number of columns, the last column is selected.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The text in the cell as a string as how it is displayed
+&apos;&apos;&apos; Note that the position of the cursor is left unchanged.
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.GeText(&quot;ShipCity&quot;)) &apos; Extract the text on the current row from the column &quot;ShipCity&quot;
+
+Dim sText As String &apos; Return Text
+Dim lCol As Long &apos; Numeric index of Column in lists of columns
+Dim lMaxCol As Long &apos; Index of last column
+Const cstThisSub = &quot;SFDatabases.Datasheet.GetText&quot;
+Const cstSubArgs = &quot;[Column=0]&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ sText = &quot;&quot;
+
+Check:
+ If IsMissing(Column) Or IsEmpty(Column) Then Column = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If VarType(Column) &lt;&gt; V_STRING Then
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, V_STRING, _ColumnHeaders) Then GoTo Catch
+ End If
+ End If
+
+Try:
+ &apos; Position the column - The index to be passed starts at 0
+ With _ControlView
+ If VarType(Column) = V_STRING Then
+ lCol = ScriptForge.SF_Array.IndexOf(_ColumnHeaders, Column, CaseSensitive := False)
+ Else
+ lCol = -1
+ If Column &gt;= 1 Then
+ lMaxCol = .Count - 1
+ If Column &gt; lMaxCol + 1 Then lCol = lMaxCol Else lCol = Column - 1
+ End If
+ End If
+
+ If lCol &gt;= 0 Then sText = .getByIndex(lCol).Text
+ End With
+
+Finally:
+ GetText = sText
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.GetText
+
+REM -----------------------------------------------------------------------------
+Public Function GetValue(Optional ByVal Column As Variant) As Variant
+&apos;&apos;&apos; Get the value in the given column of the current row.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Column: the name of the column as a string or its position (&gt;= 1). Default = the current column
+&apos;&apos;&apos; If the argument exceeds the number of columns, the last column is selected.
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The value in the cell as a valid Basic type
+&apos;&apos;&apos; Typical types are: STRING, INTEGER, LONG, FLOAT, DOUBLE, DATE, NULL
+&apos;&apos;&apos; Binary types are returned as a LONG giving their length, not their content
+&apos;&apos;&apos; An EMPTY return value means that the value could not be retrieved.
+&apos;&apos;&apos; Note that the position of the cursor is left unchanged.
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.GeValue(&quot;ShipCity&quot;)) &apos; Extract the value on the current row from the column &quot;ShipCity&quot;
+
+Dim vValue As Variant &apos; Return value
+Dim lCol As Long &apos; Numeric index of Column in lists of columns
+Dim lMaxCol As Long &apos; Index of last column
+Const cstThisSub = &quot;SFDatabases.Datasheet.GetValue&quot;
+Const cstSubArgs = &quot;[Column=0]&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ vValue = Empty
+
+Check:
+ If IsMissing(Column) Or IsEmpty(Column) Then Column = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If VarType(Column) &lt;&gt; V_STRING Then
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, V_STRING, _ColumnHeaders) Then GoTo Catch
+ End If
+ End If
+
+Try:
+ &apos; Position the column - The index to be passed starts at 1
+ If VarType(Column) = V_STRING Then
+ lCol = ScriptForge.SF_Array.IndexOf(_ColumnHeaders, Column, CaseSensitive := False) + 1
+ Else
+ lCol = 0
+ If Column &gt;= 1 Then
+ lMaxCol = _ControlView.Count
+ If Column &gt; lMaxCol Then lCol = lMaxCol Else lCol = Column
+ End If
+ End If
+
+ &apos; The _TabControllerModel acts exactly as a result set, from which the generic _GetColumnValue can extract the searched value
+ If lCol &gt;= 1 Then vValue = _ParentDatabase._GetColumnValue(_TabControllerModel, lCol)
+
+Finally:
+ GetValue = vValue
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.GetValue
+
+REM -----------------------------------------------------------------------------
+Public Function GoToCell(Optional ByVal Row As Variant _
+ , Optional ByVal Column As Variant _
+ ) As Boolean
+&apos;&apos;&apos; Set the cursor on the given row and the given column.
+&apos;&apos;&apos; If the requested row exceeds the number of available rows, the cursor is set on the last row.
+&apos;&apos;&apos; If the requested column exceeds the number of available columns, the selected column is the last one.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Row: the row number (&gt;= 1) as a numeric value. Default= no change
+&apos;&apos;&apos; Column: the name of the column as a string or its position (&gt;= 1). Default = the current column
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.GoToCell(1000000, &quot;ShipCity&quot;)) &apos; Set the cursor on he last row, column &quot;ShipCity&quot;
+
+Dim bGoTo As Boolean &apos; Return value
+Dim lCol As Long &apos; Numercic index of Column in lists of columns
+Dim lMaxCol As Long &apos; Index of last column
+Const cstThisSub = &quot;SFDatabases.Datasheet.GoToCell&quot;
+Const cstSubArgs = &quot;[Row=0], [Column=0]&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bGoTo = False
+
+Check:
+ If IsMissing(Row) Or IsEmpty(Row) Then Row = 0
+ If IsMissing(Column) Or IsEmpty(Column) Then Column = 0
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Row, &quot;Row&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ If VarType(Column) &lt;&gt; V_STRING Then
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, ScriptForge.V_NUMERIC) Then GoTo Catch
+ Else
+ If Not ScriptForge.SF_Utils._Validate(Column, &quot;Column&quot;, V_STRING, _ColumnHeaders) Then GoTo Catch
+ End If
+ End If
+
+Try:
+ &apos; Position the row
+ With _TabControllerModel
+ If Row &lt;= 0 Then Row = .Row Else .absolute(Row)
+ &apos; Does Row exceed the total number of rows ?
+ If .IsRowCountFinal And Row &gt; .RowCount Then .absolute(.RowCount)
+ End With
+
+ &apos; Position the column
+ With _ControlView
+ If VarType(Column) = V_STRING Then
+ lCol = ScriptForge.SF_Array.IndexOf(_ColumnHeaders, Column, CaseSensitive := False)
+ Else
+ lCol = -1
+ If Column &gt;= 1 Then
+ lMaxCol = .Count - 1
+ If Column &gt; lMaxCol + 1 Then lCol = lMaxCol Else lCol = Column - 1
+ End If
+ End If
+ If lCol &gt;= 0 Then .setCurrentColumnPosition(lCol)
+ End With
+
+ bGoTo = True
+
+Finally:
+ GoToCell = bGoTo
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.GoToCell
+
+REM -----------------------------------------------------------------------------
+Public Function Methods() As Variant
+&apos;&apos;&apos; Return the list of public methods of the Model service as an array
+
+ Methods = Array( _
+ &quot;Activate&quot; _
+ , &quot;ApplyFilter&quot; _
+ , &quot;CloseDatasheet&quot; _
+ , &quot;GetText&quot; _
+ , &quot;GetValue&quot; _
+ , &quot;GoToCell&quot; _
+ , &quot;OrderBy&quot; _
+ )
+
+End Function &apos; SFDatabases.SF_Datasheet.Methods
+
+REM -----------------------------------------------------------------------------
+Public Function OrderBy(Optional ByVal Order As Variant) As Boolean
+&apos;&apos;&apos; Sort the actual datasheet based on the given ordering instructions
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Order: a SQL ORDER BY clause without the ORDER BY keywords
+&apos;&apos;&apos; Table and field names may be surrounded by square brackets
+&apos;&apos;&apos; When the argument is the zero-length string or absent, the actual sort is removed
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; True when successful
+&apos;&apos;&apos; Examples:
+&apos;&apos;&apos; oSheet.OrderBy(&quot;[ShipCountry] DESC, [EmployeeID]&quot;)
+
+Dim bOrder As Boolean &apos; Return value
+Dim sOrder As String &apos; Order after replacement of square brackets by real delimiter
+Const cstThisSub = &quot;SFDatabases.Datasheet.OrderBy&quot;
+Const cstSubArgs = &quot;[Order=&quot;&quot;&quot;&quot;]&quot;
+
+ If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ bOrder = False
+
+Check:
+ If IsMissing(Order) Or IsEmpty(Order) Then Order = &quot;&quot;
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not _IsStillAlive() Then GoTo Finally
+ If Not ScriptForge.SF_Utils._Validate(Order, &quot;Order&quot;, V_STRING) Then GoTo Catch
+ End If
+
+Try:
+ With _TabControllerModel
+ If Len(Order) &gt; 0 Then
+ .Order = _ParentDatabase._ReplaceSquareBrackets(Order)
+ .ApplyFilter = True
+ .reload()
+ Else
+ .Order = &quot;&quot;
+ .ApplyFilter = False
+ .reload()
+ End If
+ End With
+
+ bOrder = True
+
+Finally:
+ OrderBy = bOrder
+ SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet.OrderBy
+
+REM -----------------------------------------------------------------------------
+Public Function Properties() As Variant
+&apos;&apos;&apos; Return the list or properties of the Model class as an array
+
+ Properties = Array( _
+ &quot;ColumnHeaders&quot; _
+ , &quot;CurrentColumn&quot; _
+ , &quot;CurrentRow&quot; _
+ , &quot;LastRow&quot; _
+ , &quot;Source&quot; _
+ , &quot;SourceType&quot; _
+ , &quot;XComponent&quot; _
+ , &quot;XControlModel&quot; _
+ , &quot;XTabControllerModel&quot; _
+ )
+
+End Function &apos; SFDatabases.SF_Datasheet.Properties
+
+REM =========================================================== PRIVATE FUNCTIONS
+
+REM -----------------------------------------------------------------------------
+Public Sub _Initialize()
+&apos;&apos;&apos; Called immediately after instance creation to complete the initial values
+&apos;&apos;&apos; An eventual error must be trapped in the calling routine to cancel the instance creation
+
+Dim iType As Integer &apos; One of the com.sun.star.sdb.CommandType constants
+Dim oColumn As Object &apos; A single column
+Dim oColumnDescriptor As Object &apos; A single column descriptor
+Dim i As Long
+
+Try:
+ _ParentType = [_Parent].ObjectType
+
+ With _Component
+ &apos; The existence of _Component.Selection must be checked upfront
+ _Command = ScriptForge.SF_Utils._GetPropertyValue(.Selection, &quot;Command&quot;)
+
+ iType = ScriptForge.SF_Utils._GetPropertyValue(.Selection, &quot;CommandType&quot;)
+ Select Case iType
+ Case com.sun.star.sdb.CommandType.TABLE : _SheetType = &quot;TABLE&quot;
+ Case com.sun.star.sdb.CommandType.QUERY : _SheetType = &quot;QUERY&quot;
+ Case com.sun.star.sdb.CommandType.COMMAND : _SheetType = &quot;SQL&quot;
+ End Select
+
+ _BaseFileName = ScriptForge.SF_Utils._GetPropertyValue(.Selection, &quot;DataSourceName&quot;)
+ _DirectSql = Not ScriptForge.SF_Utils._GetPropertyValue(.Selection, &quot;EscapeProcessing&quot;)
+
+ &apos; Useful UNO objects
+ Set _Frame = .Frame
+ Set _ControlView = .CurrentControl
+ Set _TabControllerModel = .com_sun_star_awt_XTabController_getModel
+ Set _ControlModel = _ControlView.getModel()
+
+ &apos; Retrieve the parent database instance
+ Select Case [_Parent].ObjectType
+ Case &quot;BASE&quot;
+ With _TabControllerModel
+ Set _ParentDatabase = [_Parent].GetDatabase(.User, .Password)
+ End With
+ Set _ParentBase = [_Parent]
+ Case &quot;DATABASE&quot;
+ Set _ParentDatabase = [_Parent]
+ Set _ParentBase = Nothing
+ End Select
+
+ &apos; Load column headers
+ _ColumnHeaders = _TabControllerModel.getColumns().getElementNames()
+
+ End With
+
+Finally:
+ Exit Sub
+End Sub &apos; SFDatabases.SF_Datasheet._Initialize
+
+ &apos; or Nothing when opened manually from the user interface
+REM -----------------------------------------------------------------------------
+Private Function _IsStillAlive(Optional ByVal pbError As Boolean) As Boolean
+&apos;&apos;&apos; Returns True if the datasheet 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; pbError: if True (default), raise a fatal error
+
+Dim bAlive As Boolean &apos; Return value
+Dim sName As String &apos; Used in error message
+
+ On Local Error GoTo Catch &apos; Anticipate DisposedException errors or alike
+ If IsMissing(pbError) Then pbError = True
+
+Try:
+ &apos; Check existence of datasheet
+ bAlive = Not IsNull(_Component.ComponentWindow)
+
+Finally:
+ If pbError And Not bAlive Then
+ sName = _Command
+ Dispose()
+ If pbError Then ScriptForge.SF_Exception.RaiseFatal(DOCUMENTDEADERROR, sName)
+ End If
+ _IsStillAlive = bAlive
+ Exit Function
+Catch:
+ bAlive = False
+ On Error GoTo 0
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet._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
+
+Dim lRow As Long &apos; Actual row number
+Dim cstThisSub As String
+Const cstSubArgs = &quot;&quot;
+
+ cstThisSub = &quot;SFDatabases.Datasheet.get&quot; &amp; psProperty
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+
+ ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs)
+ If Not _IsStillAlive(False) Then GoTo Finally
+
+ Select Case psProperty
+ Case &quot;ColumnHeaders&quot;
+ &apos; or Nothing when opened manually from the user interface
+
+ &apos; or Nothing when opened manually from the user interface
+ _PropertyGet = _ColumnHeaders
+ Case &quot;CurrentColumn&quot;
+ _PropertyGet = _ColumnHeaders(_ControlView.getCurrentColumnPosition())
+ Case &quot;CurrentRow&quot;
+ _PropertyGet = _TabControllerModel.Row
+ Case &quot;LastRow&quot;
+ With _TabControllerModel
+ If .IsRowCountFinal Then
+ _PropertyGet = .RowCount
+ Else
+ lRow = .Row
+ If lRow &gt; 0 Then
+ .last()
+ _PropertyGet = .RowCount
+ .absolute(lRow)
+ Else
+ _PropertyGet = 0
+ End If
+ End If
+ End With
+ Case &quot;Source&quot;
+ _PropertyGet = _Command
+ Case &quot;SourceType&quot;
+ _PropertyGet = _SheetType
+ Case &quot;XComponent&quot;
+ Set _PropertyGet = _Component
+ Case &quot;XControlModel&quot;
+ Set _PropertyGet = _ControlModel
+ Case &quot;XTabControllerModel&quot;
+ Set _PropertyGet = _TabControllerModel
+ Case Else
+ _PropertyGet = Null
+ End Select
+
+Finally:
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Datasheet._PropertyGet
+
+REM -----------------------------------------------------------------------------
+Private Function _Repr() As String
+&apos;&apos;&apos; Convert the Datasheet instance to a readable string, typically for debugging purposes (DebugPrint ...)
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Return:
+&apos;&apos;&apos; &quot;[DATASHEET]: A readable string&quot;
+
+ _Repr = &quot;[DATASHEET]: A readable string&quot;
+
+End Function &apos; SFDatabases.SF_Datasheet._Repr
+
+REM ============================================ END OF SFDATABASES.SF_DATASHEET
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdatabases/SF_Register.xba b/wizards/source/sfdatabases/SF_Register.xba
index c9b3f03d7334..25d41e99718a 100644
--- a/wizards/source/sfdatabases/SF_Register.xba
+++ b/wizards/source/sfdatabases/SF_Register.xba
@@ -46,6 +46,7 @@ Public Sub RegisterScriptServices() As Variant
With GlobalScope.ScriptForge.SF_Services
.RegisterService(&quot;Database&quot;, &quot;SFDatabases.SF_Register._NewDatabase&quot;) &apos; Reference to the function initializing the service
.RegisterService(&quot;DatabaseFromDocument&quot;, &quot;SFDatabases.SF_Register._NewDatabaseFromSource&quot;)
+ .RegisterService(&quot;Datasheet&quot;, &quot;SFDatabases.SF_Register._NewDatasheet&quot;)
End With
End Sub &apos; SFDatabases.SF_Register.RegisterScriptServices
@@ -55,7 +56,7 @@ REM =========================================================== PRIVATE FUNCTION
REM -----------------------------------------------------------------------------
Public Function _NewDatabase(Optional ByVal pvArgs As Variant) As Object
&apos;&apos;&apos; Create a new instance of the SF_Database class
-&apos; Args:
+&apos;&apos;&apos; Args:
&apos;&apos;&apos; FileName : the name of the file (compliant with the SF_FileSystem.FileNaming notation)
&apos;&apos;&apos; RegistrationName: mutually exclusive with FileName. Used when database is registered
&apos;&apos;&apos; ReadOnly : (boolean). Default = True
@@ -140,7 +141,7 @@ Public Function _NewDatabaseFromSource(Optional ByVal pvArgs As Variant) As Obje
&apos;&apos;&apos; Create a new instance of the SF_Database class from the given datasource
&apos;&apos;&apos; established in the SFDocuments.Base service
&apos;&apos;&apos; THIS SERVICE MUST NOT BE CALLED FROM A USER SCRIPT
-&apos; Args:
+&apos;&apos;&apos; Args:
&apos;&apos;&apos; DataSource: com.sun.star.sdbc.XDataSource
&apos;&apos;&apos; User, Password : connection parameters
&apos;&apos;&apos; Returns:
@@ -191,5 +192,58 @@ Catch:
GoTo Finally
End Function &apos; SFDatabases.SF_Register._NewDatabaseFromSource
+REM -----------------------------------------------------------------------------
+Public Function _NewDatasheet(Optional ByVal pvArgs As Variant) As Object
+&apos; Optional ByRef poParent As Object _
+&apos; , Optional ByRef poComponent As Object _
+&apos; ) As Object
+&apos;&apos;&apos; Create a new instance of the SF_Datasheet class
+&apos;&apos;&apos; Called from (internal calls only)
+&apos;&apos;&apos; base.Datasheets()
+&apos;&apos;&apos; base.OpenTable()
+&apos;&apos;&apos; base.OpenQuery()
+&apos;&apos;&apos; database.OpenTable()
+&apos;&apos;&apos; database.OpenQuery()
+&apos;&apos;&apos; database.OpenSql()
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; Parent: the parent SF_Database or SF_Base instance having produced the new datasheet
+&apos;&apos;&apos; Component: the component of the new datasheet
+&apos;&apos;&apos; com.sun.star.lang.XComponent - org.openoffice.comp.dbu.ODatasourceBrowser
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; The instance or Nothing
+
+Dim oDatasheet As Object &apos; Return value
+Dim oParent As Object &apos; The parent SF_Database or SF_Base instance having produced the new datasheet
+Dim oComponent As Object &apos; The component of the new datasheet
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oDatasheet = Nothing
+
+Check:
+ &apos; Get arguments
+ If UBound(pvArgs) &lt;&gt; 1 Then GoTo Catch
+ Set oParent = pvArgs(0)
+ Set oComponent = pvArgs(1)
+ If IsNull(oParent) Or IsNull(oComponent) Then GoTo Catch
+ If IsEmpty(oComponent.Selection) Then GoTo Catch
+
+Try:
+ Set oDatasheet = New SF_Datasheet
+ With oDatasheet
+ Set .[Me] = oDatasheet
+ Set .[_Parent] = oParent
+ Set ._Component = oComponent
+ &apos; Achieve the initialization
+ ._Initialize()
+ End With
+
+Finally:
+ Set _NewDatasheet = oDatasheet
+ Exit Function
+Catch:
+ Set oDatasheet = Nothing
+ GoTo Finally
+End Function &apos; SFDatabases.SF_Register._NewDatasheet
+
REM ============================================== END OF SFDATABASES.SF_REGISTER
</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdatabases/script.xlb b/wizards/source/sfdatabases/script.xlb
index 6cea80d2a912..15d7cbdbe978 100644
--- a/wizards/source/sfdatabases/script.xlb
+++ b/wizards/source/sfdatabases/script.xlb
@@ -4,4 +4,5 @@
<library:element library:name="SF_Register"/>
<library:element library:name="__License"/>
<library:element library:name="SF_Database"/>
+ <library:element library:name="SF_Datasheet"/>
</library:library> \ No newline at end of file
diff --git a/wizards/source/sfdialogs/SF_Dialog.xba b/wizards/source/sfdialogs/SF_Dialog.xba
index 486e90ef71dd..507e5ac72c00 100644
--- a/wizards/source/sfdialogs/SF_Dialog.xba
+++ b/wizards/source/sfdialogs/SF_Dialog.xba
@@ -1083,7 +1083,7 @@ Try:
Next sControlName
vRadioList = ScriptForge.SF_Array.SortRows(vRadioList, 1)
- &apos; Retain continuous tav indexes
+ &apos; Retain contiguous tab indexes
For i = 1 To UBound(vRadioList, 1) &apos; First row = argument
If vRadioList(i, 1) = iRadioTabIndex + i Then sList = sList &amp; cstComma &amp; vRadioList(i, 0)
Next i
@@ -1481,4 +1481,4 @@ Private Function _Repr() As String
End Function &apos; SFDialogs.SF_Dialog._Repr
REM ============================================ END OF SFDIALOGS.SF_DIALOG
-</script:module>
+</script:module> \ No newline at end of file
diff --git a/wizards/source/sfdocuments/SF_Base.xba b/wizards/source/sfdocuments/SF_Base.xba
index 1e6395dbfa2c..81afacc7bbda 100644
--- a/wizards/source/sfdocuments/SF_Base.xba
+++ b/wizards/source/sfdocuments/SF_Base.xba
@@ -459,7 +459,7 @@ Public Function OpenFormDocument(Optional ByVal FormDocument As Variant _
&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; FormDocument: a valid form document 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
@@ -510,6 +510,111 @@ Catch:
End Function &apos; SFDocuments.SF_Base.OpenFormDocument
REM -----------------------------------------------------------------------------
+Public Function OpenQuery(Optional ByVal QueryName As Variant _
+ , Optional ByVal DesignMode As Variant _
+ ) As Object
+&apos;&apos;&apos; Open the query given by its name either in normal or in design mode
+&apos;&apos;&apos; If the query is already open, the query datasheet is made active without changing its mode
+&apos;&apos;&apos; If still open, the datasheet will be closed together with the actual Base document.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; QueryName: a valid Query name as a case-sensitive string
+&apos;&apos;&apos; DesignMode: when True the query is opened in design mode (Default = False)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if the query could be opened and DesignMode = False, otherwise False
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Query name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDoc.OpenQuery(&quot;myQuery&quot;, DesignMode := False)
+
+Dim oOpen As Object &apos; Return value
+Dim vQueries As Variant &apos; Array of query names
+Dim oNewQuery As Object &apos; Output of loadComponent()
+Const cstThisSub = &quot;SFDocuments.Base.OpenQuery&quot;
+Const cstSubArgs = &quot;QueryName, [DesignMode=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ If IsMissing(DesignMode) Or IsEmpty(DesignMode) Then DesignMode = False
+ vQueries = GetDatabase().Queries &apos; Includes _IsStillAlive()
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(QueryName, &quot;QueryName&quot;, V_STRING, vQueries) 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
+ &apos; The connection may have been done previously by a user commmand. If not, do it now.
+ If Not .IsConnected Then .connect()
+ &apos; loadComponent activates the query component when already loaded
+ Set oNewQuery = .loadComponent(com.sun.star.sdb.application.DatabaseObject.QUERY, QueryName, DesignMode)
+ End With
+ &apos; When design mode, the method returns Nothing
+ If Not DesignMode Then Set oOpen = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Datasheet&quot;, [Me], oNewQuery)
+
+Finally:
+ Set OpenQuery = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.OpenQuery
+
+REM -----------------------------------------------------------------------------
+Public Function OpenTable(Optional ByVal TableName As Variant _
+ , Optional ByVal DesignMode As Variant _
+ ) As Object
+&apos;&apos;&apos; Open the table given by its name either in normal or in design mode
+&apos;&apos;&apos; If the table is already open, the table datasheet is made active without changing its mode
+&apos;&apos;&apos; If still open, the datasheet will be closed together with the actual Base document.
+&apos;&apos;&apos; Args:
+&apos;&apos;&apos; TableName: a valid table name as a case-sensitive string
+&apos;&apos;&apos; DesignMode: when True the table is opened in design mode (Default = False)
+&apos;&apos;&apos; Returns:
+&apos;&apos;&apos; A Datasheet class instance if the table could be opened or was already open, and DesignMode = False.
+&apos;&apos;&apos; Otherwise Nothing
+&apos;&apos;&apos; Exceptions:
+&apos;&apos;&apos; Table name is invalid
+&apos;&apos;&apos; Example:
+&apos;&apos;&apos; oDoc.OpenTable(&quot;myTable&quot;, DesignMode := False)
+
+Dim oOpen As Object &apos; Return value
+Dim vTables As Variant &apos; Array of table names
+Dim oNewTable As Object &apos; Output of loadComponent()
+Const cstThisSub = &quot;SFDocuments.Base.OpenTable&quot;
+Const cstSubArgs = &quot;TableName, [DesignMode=False]&quot;
+
+ If ScriptForge.SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
+ Set oOpen = Nothing
+
+Check:
+ If IsMissing(DesignMode) Or IsEmpty(DesignMode) Then DesignMode = False
+ vTables = GetDatabase().Tables
+ If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
+ If Not ScriptForge.SF_Utils._Validate(TableName, &quot;TableName&quot;, V_STRING, vTables) 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
+ &apos; The connection may have been done previously by a user commmand. If not, do it now.
+ If Not .IsConnected Then .connect()
+ &apos; loadComponent activates the table component when already loaded
+ Set oNewTable = .loadComponent(com.sun.star.sdb.application.DatabaseObject.TABLE, TableName, DesignMode)
+ End With
+ &apos; When design mode, the method returns Nothing
+ If Not DesignMode Then Set oOpen = ScriptForge.SF_Services.CreateScriptService(&quot;SFDatabases.Datasheet&quot;, [Me], oNewTable)
+
+Finally:
+ Set OpenTable = oOpen
+ ScriptForge.SF_Utils._ExitFunction(cstThisSub)
+ Exit Function
+Catch:
+ GoTo Finally
+End Function &apos; SFDocuments.SF_Base.OpenTable
+
+REM -----------------------------------------------------------------------------
Public Function PrintOut(Optional ByVal FormDocument As Variant _
, Optional ByVal Pages As Variant _
, Optional ByVal Copies As Variant _
diff --git a/wizards/source/sfdocuments/SF_Calc.xba b/wizards/source/sfdocuments/SF_Calc.xba
index e32dab652166..0733be07eb6a 100644
--- a/wizards/source/sfdocuments/SF_Calc.xba
+++ b/wizards/source/sfdocuments/SF_Calc.xba
@@ -401,7 +401,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.A1Style
+End Function &apos; SFDocuments.SF_Calc.A1Style
REM -----------------------------------------------------------------------------
Public Function Activate(Optional ByVal SheetName As Variant) As Boolean
@@ -560,7 +560,7 @@ Public Sub ClearAll(Optional ByVal Range As Variant _
_ClearRange(&quot;All&quot;, Range, FilterFormula, FilterScope)
-End Sub &apos; SF_Documents.SF_Calc.ClearAll
+End Sub &apos; SFDocuments.SF_Calc.ClearAll
REM -----------------------------------------------------------------------------
Public Sub ClearFormats(Optional ByVal Range As Variant _
@@ -580,7 +580,7 @@ Public Sub ClearFormats(Optional ByVal Range As Variant _
_ClearRange(&quot;Formats&quot;, Range, FilterFormula, FilterScope)
-End Sub &apos; SF_Documents.SF_Calc.ClearFormats
+End Sub &apos; SFDocuments.SF_Calc.ClearFormats
REM -----------------------------------------------------------------------------
Public Sub ClearValues(Optional ByVal Range As Variant _
@@ -600,7 +600,7 @@ Public Sub ClearValues(Optional ByVal Range As Variant _
_ClearRange(&quot;Values&quot;, Range, FilterFormula, FilterScope)
-End Sub &apos; SF_Documents.SF_Calc.ClearValues
+End Sub &apos; SFDocuments.SF_Calc.ClearValues
REM -----------------------------------------------------------------------------
Public Function CompactLeft(Optional ByVal Range As Variant _
@@ -1424,7 +1424,7 @@ Try:
Finally:
Exit Function
-End Function &apos; SF_Documents.SF_Calc.DAvg
+End Function &apos; SFDocuments.SF_Calc.DAvg
REM -----------------------------------------------------------------------------
Public Function DCount(Optional ByVal Range As Variant) As Long
@@ -1441,7 +1441,7 @@ Try:
Finally:
Exit Function
-End Function &apos; SF_Documents.SF_Calc.DCount
+End Function &apos; SFDocuments.SF_Calc.DCount
REM -----------------------------------------------------------------------------
Public Function DMax(Optional ByVal Range As Variant) As Double
@@ -1458,7 +1458,7 @@ Try:
Finally:
Exit Function
-End Function &apos; SF_Documents.SF_Calc.DMax
+End Function &apos; SFDocuments.SF_Calc.DMax
REM -----------------------------------------------------------------------------
Public Function DMin(Optional ByVal Range As Variant) As Double
@@ -1475,7 +1475,7 @@ Try:
Finally:
Exit Function
-End Function &apos; SF_Documents.SF_Calc.DMin
+End Function &apos; SFDocuments.SF_Calc.DMin
REM -----------------------------------------------------------------------------
Public Function DSum(Optional ByVal Range As Variant) As Double
@@ -1492,7 +1492,7 @@ Try:
Finally:
Exit Function
-End Function &apos; SF_Documents.SF_Calc.DSum
+End Function &apos; SFDocuments.SF_Calc.DSum
REM -----------------------------------------------------------------------------
Public Function ExportRangeToFile(Optional ByVal Range As Variant _
@@ -1754,7 +1754,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.GetFormula
+End Function &apos; SFDocuments.SF_Calc.GetFormula
REM -----------------------------------------------------------------------------
Public Function GetProperty(Optional ByVal PropertyName As Variant _
@@ -1839,7 +1839,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.GetValue
+End Function &apos; SFDocuments.SF_Calc.GetValue
REM -----------------------------------------------------------------------------
Public Function ImportFromCSVFile(Optional ByVal FileName As Variant _
@@ -2300,7 +2300,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.Offset
+End Function &apos; SFDocuments.SF_Calc.Offset
REM -----------------------------------------------------------------------------
Public Function OpenRangeSelector(Optional ByVal Title As Variant _
@@ -2381,7 +2381,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.OpenRangeSelector
+End Function &apos; SFDocuments.SF_Calc.OpenRangeSelector
REM -----------------------------------------------------------------------------
Public Function Printf(Optional ByVal InputStr As Variant _
@@ -2478,7 +2478,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.Printf
+End Function &apos; SFDocuments.SF_Calc.Printf
REM -----------------------------------------------------------------------------
Public Function PrintOut(Optional ByVal SheetName As Variant _
@@ -2711,7 +2711,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.SetArray
+End Function &apos; SFDocuments.SF_Calc.SetArray
REM -----------------------------------------------------------------------------
Public Function SetCellStyle(Optional ByVal TargetRange As Variant _
@@ -2789,7 +2789,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.SetCellStyle
+End Function &apos; SFDocuments.SF_Calc.SetCellStyle
REM -----------------------------------------------------------------------------
Public Function SetFormula(Optional ByVal TargetRange As Variant _
@@ -2858,7 +2858,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.SetFormula
+End Function &apos; SFDocuments.SF_Calc.SetFormula
REM -----------------------------------------------------------------------------
Private Function SetProperty(Optional ByVal psProperty As String _
@@ -2966,7 +2966,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.SetValue
+End Function &apos; SFDocuments.SF_Calc.SetValue
REM -----------------------------------------------------------------------------
Public Function ShiftDown(Optional ByVal Range As Variant _
@@ -3423,7 +3423,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc.SortRange
+End Function &apos; SFDocuments.SF_Calc.SortRange
REM ======================================================= SUPERCLASS PROPERTIES
@@ -3685,7 +3685,7 @@ Finally:
Exit Sub
Catch:
GoTo Finally
-End Sub &apos; SF_Documents.SF_Calc._ClearRange
+End Sub &apos; SFDocuments.SF_Calc._ClearRange
REM -----------------------------------------------------------------------------
Private Function _ComputeFilter(ByRef poRange As Object _
@@ -3853,7 +3853,7 @@ Try:
Finally:
_ConvertFromDataArray = vArray
-End Function &apos; SF_Documents.SF_Calc._ConvertFromDataArray
+End Function &apos; SFDocuments.SF_Calc._ConvertFromDataArray
REM -----------------------------------------------------------------------------
Private Function _ConvertToCellValue(ByVal pvItem As Variant) As Variant
@@ -3873,7 +3873,7 @@ Try:
Finally:
_ConvertToCellValue = vCell
Exit Function
-End Function &apos; SF_Documents.SF_Calc._ConvertToCellValue
+End Function &apos; SFDocuments.SF_Calc._ConvertToCellValue
REM -----------------------------------------------------------------------------
Private Function _ConvertToDataArray(ByRef pvArray As Variant _
@@ -3997,7 +3997,7 @@ Try:
Finally:
_ConvertToDataArray = vDataArray
Exit Function
-End Function &apos; SF_Documents.SF_Calc._ConvertToDataArray
+End Function &apos; SFDocuments.SF_Calc._ConvertToDataArray
REM -----------------------------------------------------------------------------
Private Function _DFunction(ByVal psFunction As String _
@@ -4043,7 +4043,7 @@ Finally:
Exit Function
Catch:
GoTo Finally
-End Function &apos; SF_Documents.SF_Calc._DFunction
+End Function &apos; SFDocuments.SF_Calc._DFunction
REM -----------------------------------------------------------------------------
Private Function _FileIdent() As String
@@ -4205,7 +4205,7 @@ CatchAddress:
, &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
+End Function &apos; SFDocuments.SF_Calc._Offset
REM -----------------------------------------------------------------------------
Private Function _ParseAddress(ByVal psAddress As String) As Object
diff --git a/wizards/source/sfdocuments/SF_Document.xba b/wizards/source/sfdocuments/SF_Document.xba
index 30508f2e87b1..e537b90e5da1 100644
--- a/wizards/source/sfdocuments/SF_Document.xba
+++ b/wizards/source/sfdocuments/SF_Document.xba
@@ -864,7 +864,9 @@ 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)
+ If UBound(Args) &gt;= 0 Then
+ If IsArray(Args(0)) Then vArgs = Args(0)
+ End If
End If
If ScriptForge.SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
If Not _IsStillAlive() Then GoTo Finally