wizards/source/scriptforge/SF_Array.xba          |    8 
 wizards/source/scriptforge/SF_PythonHelper.xba   |  118 ++++-----
 wizards/source/scriptforge/python/scriptforge.py |  279 +++++++++++++----------
 3 files changed, 224 insertions(+), 181 deletions(-)

New commits:
commit 0bbe5b4f2a7797bfd05fdbde316857fceaf84d77
Author:     Jean-Pierre Ledure <j...@ledure.be>
AuthorDate: Sat Aug 19 17:30:25 2023 +0200
Commit:     Jean-Pierre Ledure <j...@ledure.be>
CommitDate: Sun Aug 20 10:48:48 2023 +0200

    ScriptForge - Refactor Python <=> Basic protocol
    
    Incremental changes had made the code in
       SF_PythonHelper.xba
       scriptforge.py
    (which manage the protocol between the Python
    user scripts and the ScriptForge services
    implemented in Basic) less readable and efficient.
    
    Next features have been reviewed:
    - dict instances may be passed as arguments
      and returned. Conversions are done on-th-fly.
    - dates may be passed as arguments
      and returned. Conversions are done on-th-fly.
      The lists of hardcoded methods have been removed.
    - 2D arrays in standard modules may be passed
      as arguments and returned. Conversions are done
      on-th-fly. The lists of hardcoded methods have
      been removed.
    - The hardcoded list of methods requiring a post-
      processing has been reduced.
    - Methods in standard modules, when not returning
      an array, are also executed with CallByName().
    - The unused 'localProperties' attribute in Python
      services has been removed.
    - Flags about arrays in standard modules have been
      adapted in
       filesystem.Files()
       filesystem.SubFolders()
       session.UnoMethods()
       session.UnoProperties()
       string.SplitNotQuoted()
       string.Wrap()
       ui.Documents()
    - The platform.UserData property became a usual
      property.
    
    Dictionaries are admitted as arguments and return
    values. As a consquence next functions are supported
    in Python as well:
       session.GetPDFExportOpetions()
       session.SetPDFExportOptions()
       document, calc, writer.CustomProperties
       document, calc, writer.DocumentProperties
    These changes require an update of the help pages.
    
    Non-regression tests were run with success. All changes
    are transparent for existing scripts.
    
    Change-Id: Iae7a1f5090c590209cd3cb2314c919c44736eba9
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/155860
    Reviewed-by: Jean-Pierre Ledure <j...@ledure.be>
    Tested-by: Jenkins

diff --git a/wizards/source/scriptforge/SF_Array.xba 
b/wizards/source/scriptforge/SF_Array.xba
index 49bdab14770a..54110cde9352 100644
--- a/wizards/source/scriptforge/SF_Array.xba
+++ b/wizards/source/scriptforge/SF_Array.xba
@@ -849,7 +849,6 @@ REM 
----------------------------------------------------------------------------
 Public Function ImportFromCSVFile(Optional ByRef FileName As Variant _
                                                                        , 
Optional ByVal Delimiter As Variant _
                                                                        , 
Optional ByVal DateFormat As Variant _
-                                                                       , 
Optional ByVal _IsoDate As Variant _
                                                                        ) As 
Variant
 &apos;&apos;&apos;     Import the data contained in a comma-separated values 
(CSV) file
 &apos;&apos;&apos;     The comma may be replaced by any character
@@ -866,7 +865,6 @@ Public Function ImportFromCSVFile(Optional ByRef FileName 
As Variant _
 &apos;&apos;&apos;                     The dash (-) may be replaced by a dot 
(.), a slash (/) or a space
 &apos;&apos;&apos;                     Other date formats will be ignored
 &apos;&apos;&apos;                     If &quot;&quot; (default), dates will 
be considered as strings
-&apos;&apos;&apos;             _IsoDate: when True, the execution is initiated 
from Python, do not convert dates to Date variables. Internal use only
 &apos;&apos;&apos;     Returns:
 &apos;&apos;&apos;             A 2D-array with each row corresponding with a 
single record read in the file
 &apos;&apos;&apos;             and each column corresponding with a field of 
the record
@@ -892,7 +890,9 @@ Dim vItem As Variant                        &apos;  
Individual item in the output array
 Dim iPosition As Integer               &apos;  Date position in individual item
 Dim iYear As Integer, iMonth As Integer, iDay As Integer
                                                                &apos;  Date 
components
+Dim bIsoDate As Boolean                        &apos;  When True, do not 
convert dates to Date variables
 Dim i As Long
+
 Const cstItemsLimit = 250000   &apos;  Maximum number of admitted items
 Const cstThisSub = &quot;Array.ImportFromCSVFile&quot;
 Const cstSubArgs = &quot;FileName, [Delimiter=&quot;&quot;,&quot;&quot;], 
[DateFormat=&quot;&quot;&quot;&quot;]&quot;
@@ -903,7 +903,6 @@ Const cstSubArgs = &quot;FileName, 
[Delimiter=&quot;&quot;,&quot;&quot;], [DateF
 Check:
        If IsMissing(Delimiter) Or IsEmpty(Delimiter) Then Delimiter = 
&quot;,&quot;
        If IsMissing(DateFormat) Or IsEmpty(DateFormat) Then DateFormat = 
&quot;&quot;
-       If IsMissing(_IsoDate) Or IsEmpty(_IsoDate) Then _IsoDate = False
        If SF_Utils._EnterFunction(cstThisSub, cstSubArgs) Then
                If Not SF_Utils._ValidateFile(FileName, &quot;FileName&quot;) 
Then GoTo Finally
                If Not SF_Utils._Validate(Delimiter, &quot;Delimiter&quot;, 
V_STRING) Then GoTo Finally
@@ -912,6 +911,7 @@ Check:
        If Len(Delimiter) = 0 Then Delimiter = &quot;,&quot;
 
 Try:
+       bIsoDate = _SF_.TriggeredByPython       &apos;  Dates are not converted
        &apos;  Counts the lines present in the file to size the final array
        &apos;          Very beneficial for large files, better than multiple 
ReDims
        &apos;          Small overhead for small files
@@ -947,7 +947,7 @@ Try:
                                                                iPosition = 
InStr(DateFormat, &quot;MM&quot;)           :       iMonth = CInt(Mid(sItem, 
iPosition, 2))
                                                                iPosition = 
InStr(DateFormat, &quot;DD&quot;)           :       iDay = CInt(Mid(sItem, 
iPosition, 2))
                                                                vItem = 
DateSerial(iYear, iMonth, iDay)
-                                                               If _IsoDate 
Then vItem = SF_Utils._CDateToIso(vItem)    &apos;  Called from Python
+                                                               If bIsoDate 
Then vItem = SF_Utils._CDateToIso(vItem)    &apos;  Called from Python
                                                        Else
                                                                vItem = sItem
                                                        End If
diff --git a/wizards/source/scriptforge/SF_PythonHelper.xba 
b/wizards/source/scriptforge/SF_PythonHelper.xba
index 58a841d4dc26..b611dbfd0d7e 100644
--- a/wizards/source/scriptforge/SF_PythonHelper.xba
+++ b/wizards/source/scriptforge/SF_PythonHelper.xba
@@ -565,6 +565,8 @@ Public Function _PythonDispatcher(ByRef BasicObject As 
Variant _
 &apos;&apos;&apos;     The invocation of the method can be a Property Get, 
Property Let or a usual call
 &apos;&apos;&apos;     NB: arguments and return values must not be 2D arrays
 &apos;&apos;&apos;     The implementation intends to be as AGNOSTIC as 
possible in terms of objects nature and methods called
+&apos;&apos;&apos;     The method returns the value effectively returned by 
the called component,
+&apos;&apos;&apos;     completed with additional metadata. The whole is 
packaged in a 1D array.
 &apos;&apos;&apos;     Args:
 &apos;&apos;&apos;             BasicObject: a module or a class instance - May 
also be the reserved string: &quot;SF_Services&quot;
 &apos;&apos;&apos;             CallType: one of the constants applicable to a 
CallByName statement + optional protocol flags
@@ -597,8 +599,10 @@ Dim sServiceName As String                 &apos;  Alias 
of BasicObject.ServiceName
 Dim bBasicClass As Boolean                     &apos;  True when BasicObject 
is a class
 Dim sLibrary As String                         &apos;  Library where the 
object belongs to
 Dim bUno As Boolean                                    &apos;  Return value is 
a UNO object
+Dim bDict As Boolean                           &apos;  Return value is a Basic 
SF_Dictionary class instance
+Dim oDict As Object                                    &apos;  SF_Dictionary 
class instance
 Dim oObjDesc As Object                         &apos;  _ObjectDescriptor type
-Dim iDims As Integer                           &apos;  # of dims of vReturn
+Dim iDims As Integer                           &apos;  # of dims of vReturn 
when array
 Dim sess As Object                                     :       Set sess = 
ScriptForge.SF_Session
 Dim i As Long, j As Long
 
@@ -609,6 +613,8 @@ Const cstNoArgs = &quot;+++NOARGS+++&quot;, cstSymEmpty = 
&quot;+++EMPTY+++&quot
 &apos; Determines the CallType
 Const vbGet = 2, vbLet = 4, vbMethod = 1, vbSet = 8
 &apos; Protocol flags
+Const cstPost = 16                     &apos;  Requires a hardcoded 
post-processing
+Const cstDictArg = 32          &apos;  May contain a Dictionary argument
 Const cstDateArg = 64          &apos;  May contain a date argument
 Const cstDateRet = 128         &apos;  Return value can be a date
 Const cstUno = 256                     &apos;  Return value can be a UNO object
@@ -616,8 +622,8 @@ Const cstArgArray = 512             &apos;  Any argument 
can be a 2D array
 Const cstRetArray = 1024       &apos;  Return value can be an array
 Const cstObject = 2048         &apos;  1st argument is a Basic object when 
numeric
 Const cstHardCode = 4096       &apos;  Method must not be executed with 
CallByName()
-&apos; Object nature in returned array
-Const objMODULE = 1, objCLASS = 2, objUNO = 3
+&apos; Returned object nature
+Const objMODULE = 1, objCLASS = 2, objDICT = 3, objUNO = 4
 
 Check:
        If SF_Utils._ErrorHandling() Then On Local Error GoTo Catch
@@ -626,7 +632,10 @@ Check:
        &apos;  Ignore Null basic objects (Null = Null or Nothing)
        If IsNull(BasicObject) Or IsEmpty(BasicObject) Then GoTo Catch
 
-       &apos;  Reinterpret arguments one by one into vArgs, convert UNO 
date/times and conventional NoArgs/Empty/Null/Missing values
+       &apos;  Reinterpret arguments one by one into vArgs
+       &apos;  - convert UNO dates/times
+       &apos;  - identify conventional NoArgs/Empty/Null/Missing constants
+       &apos;  - convert arrays of property values into DictionarY
        iNbArgs = -1
        vArgs = Array()
 
@@ -642,7 +651,7 @@ Check:
                                If vArg &lt; 0 Or Not 
IsArray(_SF_.PythonStorage) Then GoTo Catch
                                If vArg &gt; UBound(_SF_.PythonStorage) Then 
GoTo Catch
                                vArg = _SF_.PythonStorage(vArg)
-                       &apos;  Is argument a symbolic constant for Null, 
Empty, ... , or a date?
+                       &apos;  Is argument a symbolic constant for Null, 
Empty, ... , or a date, or a dictionary ?
                        ElseIf VarType(vArg) = V_STRING Then
                                If Len(vArg) = 0 Then
                                ElseIf vArg = cstSymEmpty Then
@@ -654,6 +663,16 @@ Check:
                                End If
                        ElseIf  VarType(vArg) = V_OBJECT Then
                                If ( CallType And cstDateArg ) = cstDateArg 
Then vArg = CDateFromUnoDateTime(vArg)
+                       ElseIf ( CallType And cstDictArg ) = cstDictArg Then
+                               If IsArray(vArg) Then
+                                       If UBound(vArg) &gt;= 0 Then
+                                               If sess.UnoObjectType(vArg(0)) 
= &quot;com.sun.star.beans.PropertyValue&quot; Then
+                                                       Set oDict = 
CreateScriptService(&quot;ScriptForge.Dictionary&quot;)
+                                                       
oDict.ImportFromPropertyValues(vArg, Overwrite := True)
+                                                       vArg = oDict
+                                               End If
+                                       End If
+                               End If
                        End If
                        iNbArgs = iNbArgs + 1
 
@@ -669,9 +688,12 @@ Try:
        &apos;                  (2) Python has tuples and tuple of tuples, not 
2D arrays
        &apos;                  (3) Passing 2D arrays through a script provider 
always transform it into a sequence of sequences
        &apos;                  (4) The CallByName function takes exclusive 
control on the targeted object up to its exit
-       &apos;  1.      Methods in usual modules are called by 
ExecuteBasicScript() except if they use a ParamArray
+       &apos;                  (5) A script provider returns a Basic Date 
variable as Empty
+       &apos; RULES:
+       &apos;  1.      All methods in any module are invoked with CallByName
        &apos;  2.      Properties in any service are got and set with 
obj.GetProperty/SetProperty(...)
-       &apos;  3.      Methods in class modules are invoked with CallByName
+       &apos; EXCEPTIONS:
+       &apos;  3.      Methods in usual modules are called by 
ExecuteBasicScript() when they manipulate arrays
        &apos;  4.      Methods in class modules using a 2D array or returning 
arrays, or methods using ParamArray,
 &apos;&apos;&apos;                     are hardcoded as exceptions or are not 
implemented
        &apos;  5.      Due to constraint (4), a predefined list of method 
calls must be hardcoded to avoid blocking use of CallByName
@@ -706,7 +728,7 @@ Try:
                                sObjectType = vBasicObject.ObjectType
                                bBasicClass = ( Left(sObjectType, 3) &lt;&gt; 
&quot;SF_&quot; )
                        End If
-               
+
                &apos;  Implement dispatching strategy
                Case V_INTEGER
                        If BasicObject &lt; 0 Or Not 
IsArray(_SF_.PythonStorage) Then GoTo Catch
@@ -718,41 +740,9 @@ Try:
                        &apos;  Basic modules have type = &quot;SF_*&quot;
                        bBasicClass = ( Left(sObjectType, 3) &lt;&gt; 
&quot;SF_&quot; )
                        sLibrary = Split(sServiceName, &quot;.&quot;)(0)
-                       
-                       &apos;  Methods in standard modules returning/passing a 
date are hardcoded as exceptions
-                       If Not bBasicClass And ((CallType And vbMethod) = 
vbMethod) _
-                                       And (((CallType And cstDateRet) = 
cstDateRet) Or ((CallType And cstDateArg) = cstDateArg)) Then
-                               Select Case sServiceName
-                                       Case &quot;ScriptForge.FileSystem&quot;
-                                               If Script = 
&quot;GetFileModified&quot; Then vReturn = 
SF_FileSystem.GetFileModified(vArgs(0))
-                                       Case &quot;ScriptForge.Region&quot;
-                                               Select Case Script
-                                                       Case 
&quot;DSTOffset&quot;              :       vReturn = 
SF_Region.DSTOffset(vArgs(0), vArgs(1), vArgs(2))
-                                                       Case 
&quot;LocalDateTime&quot;  :       vReturn = SF_Region.LocalDateTime(vArgs(0), 
vArgs(1), vArgs(2))
-                                                       Case 
&quot;UTCDateTime&quot;            :       vReturn = 
SF_Region.UTCDateTime(vArgs(0), vArgs(1), vArgs(2))
-                                                       Case &quot;UTCNow&quot; 
                :       vReturn = SF_Region.UTCNow(vArgs(0), vArgs(1))
-                                                       Case Else
-                                               End Select
-                               End Select
 
-                       &apos;  Methods in usual modules using a 2D array or 
returning arrays are hardcoded as exceptions
-                       ElseIf Not bBasicClass And _
-                                               (((CallType And vbMethod) + 
(CallType And cstArgArray)) = vbMethod + cstArgArray Or _
-                                               ((CallType And vbMethod) + 
(CallType And cstRetArray)) = vbMethod + cstRetArray) Then
-                               &apos;  Not service related
-                               If Script = &quot;Methods&quot; Then
-                                       vReturn = vBasicObject.Methods()
-                               ElseIf Script = &quot;Properties&quot; Then
-                                       vReturn = vBasicObject.Properties()
-                               Else
-                                       Select Case sServiceName
-                                               Case 
&quot;ScriptForge.Array&quot;
-                                                       If Script = 
&quot;ImportFromCSVFile&quot; Then vReturn = 
SF_Array.ImportFromCSVFile(vArgs(0), vArgs(1), vArgs(2), True)
-                                       End Select
-                               End If
-
-                       &apos;  Methods in usual modules are called by 
ExecuteBasicScript() except if they use a ParamArray
-                       ElseIf Not bBasicClass And (CallType And vbMethod) = 
vbMethod Then
+                       &apos;  Methods in standard modules are called by 
ExecuteBasicScript() when arrays are returned
+                       If Not bBasicClass And (CallType And vbMethod) = 
vbMethod And (CallType And cstRetArray) = cstRetArray Then
                                sScript = sLibrary &amp; &quot;.&quot; &amp; 
sObjectType &amp; &quot;.&quot; &amp; Script
                                &apos;  Force validation in targeted function, 
not in ExecuteBasicScript()
                                _SF_.StackLevel = -1
@@ -768,13 +758,13 @@ Try:
                                        Case 7  :       vReturn = 
sess.ExecuteBasicScript(, sScript, vArgs(0), vArgs(1), vArgs(2), vArgs(3), 
vArgs(4), vArgs(5), vArgs(6), vArgs(7))
                                End Select
                                _SF_.StackLevel = 0
-                       
+
                        &apos;  Properties in any service are got and set with 
obj.GetProperty/SetProperty(...)
                        ElseIf (CallType And vbGet) = vbGet Then        &apos;  
In some cases (Calc ...) GetProperty may have an argument
                                If UBound(vArgs) &lt; 0 Then vReturn = 
vBasicObject.GetProperty(Script) Else vReturn = 
vBasicObject.GetProperty(Script, vArgs(0))
                        ElseIf (CallType And vbLet) = vbLet Then
                                vReturn = vBasicObject.SetProperty(Script, 
vArgs(0))
-                       
+
                        &apos;  Methods in class modules using a 2D array or 
returning arrays are hardcoded as exceptions. Bug #138155
                        ElseIf ((CallType And vbMethod) + (CallType And 
cstArgArray)) = vbMethod + cstArgArray Or _
                                   ((CallType And vbMethod) + (CallType And 
cstRetArray)) = vbMethod + cstRetArray Then
@@ -830,23 +820,18 @@ Try:
                                                        End Select
                                        End Select
                                End If
-                       
-                       &apos;  Methods in class modules may better not be 
executed with CallByName()
+
+                       &apos;  Specific methods in class modules may better 
not be executed with CallByName() because they do not return immediately
                        ElseIf bBasicClass And ((CallType And vbMethod) + 
(CallType And cstHardCode)) = vbMethod + cstHardCode Then
                                Select Case sServiceName
                                        Case &quot;SFDialogs.Dialog&quot;
                                                Select Case Script
-                                                       Case 
&quot;Activate&quot;                       :       vReturn = 
vBasicObject.Activate()
-                                                       Case &quot;Center&quot;
-                                                               If 
UBound(vArgs) &lt; 0 Then vReturn = vBasicObject.Center() Else vReturn = 
vBasicObject.Center(vArgs(0))
-                                                       Case 
&quot;EndExecute&quot;             :       vReturn = 
vBasicObject.EndExecute(vArgs(0))
                                                        Case 
&quot;Execute&quot;                        :       vReturn = 
vBasicObject.Execute(vArgs(0))
-                                                       Case &quot;Resize&quot; 
                :       vReturn = vBasicObject.Resize(vArgs(0), vArgs(1), 
vArgs(2), vArgs(3))
                                                End Select
                                End Select
 
-                       &apos;  Methods in class modules are invoked with 
CallByName
-                       ElseIf bBasicClass And ((CallType And vbMethod) = 
vbMethod) Then
+                       &apos;  Methods in all modules are invoked with 
CallByName
+                       ElseIf ((CallType And vbMethod) = vbMethod) Then
                                Select Case UBound(vArgs)
                                        &apos;  Dirty alternatives to process 
usual and ParamArray cases
                                        &apos;  But, up to ... how many ?
@@ -896,11 +881,11 @@ Try:
                        End If
 
                        &apos;  Post processing
-                       If Script = &quot;Dispose&quot; Then
-                               &apos;  Special case: Dispose() must update the 
cache for class objects created in Python scripts
-                               Set _SF_.PythonStorage(BasicObject) = Nothing
-                       ElseIf sServiceName = &quot;ScriptForge.Platform&quot; 
And Script = &quot;UserData&quot; Then
-                               vReturn = vReturn.ConvertToPropertyValues()
+                       If (CallType And cstPost) = cstPost Then
+                               If Script = &quot;Dispose&quot; Then
+                                       &apos;  Special case: Dispose() must 
update the cache for class objects created in Python scripts
+                                       Set _SF_.PythonStorage(BasicObject) = 
Nothing
+                               End If
                        End If
                Case Else
        End Select
@@ -909,6 +894,7 @@ Try:
        vReturnArray = Array()
        &apos;  Distinguish:    Basic object
        &apos;                                  UNO object
+       &apos;                                  Dictionary
        &apos;                                  Array
        &apos;                                  Scalar
        If IsArray(vReturn) Then
@@ -941,15 +927,25 @@ Try:
                        Set vReturnArray(0) = vReturn
                Else
                        ReDim vReturnArray(0 To 5)
-                       vReturnArray(0) = _SF_._AddToPythonSTorage(vReturn)
+                       bDict = ( vReturn.ObjectType = &quot;DICTIONARY&quot; )
+                       If bDict Then
+                               vReturnArray(0) = 
vReturn.ConvertToPropertyValues()
+                       Else
+                               vReturnArray(0) = 
_SF_._AddToPythonSTorage(vReturn)
+                       End If
                End If
                vReturnArray(1) = V_OBJECT
-               vReturnArray(2) = Iif(bUno, objUNO, Iif(bBasicClass, objCLASS, 
objMODULE))
+               Select Case True
+                       Case bUno                       :       vReturnArray(2) 
= objUNO
+                       Case bDICT                      :       vReturnArray(2) 
= objDICT
+                       Case bBasicClass        :       vReturnArray(2) = 
objCLASS
+                       Case Else                       :       vReturnArray(2) 
= objMODULE
+               End Select
                If Not bUno Then
                        vReturnArray(3) = vReturn.ObjectType
                        vReturnArray(4) = vReturn.ServiceName
                        vReturnArray(5) = &quot;&quot;
-                       If vReturn.ObjectType &lt;&gt; 
&quot;SF_CalcReference&quot; Then        &apos;  Calc references are 
implemented as a Type ... End Type data structure
+                       If vReturn.ObjectType &lt;&gt; 
&quot;SF_CalcReference&quot; And Not bDict Then  &apos;  Calc references are 
implemented as a Type ... End Type data structure
                                If SF_Array.Contains(vReturn.Properties(), 
&quot;Name&quot;, SortOrder := &quot;ASC&quot;) Then vReturnArray(5) = 
vReturn.Name
                        End If
                End If
diff --git a/wizards/source/scriptforge/python/scriptforge.py 
b/wizards/source/scriptforge/python/scriptforge.py
index e978c30b4304..b235a791f87d 100644
--- a/wizards/source/scriptforge/python/scriptforge.py
+++ b/wizards/source/scriptforge/python/scriptforge.py
@@ -28,27 +28,27 @@
     By collecting most-demanded document operations in a set of easy to use, 
easy to read routines, users can now
     program document macros with much less hassle and get quicker results.
 
-    ScriptForge abundant methods are organized in reusable modules that 
cleanly isolate Basic/Python programming
-    language constructs from ODF document content accesses and user 
interface(UI) features.
+    The use of the ScriptForge interfaces in user scripts hides the complexity 
of the usual UNO interfaces.
+    However it does not replace them. At the opposite their coexistence is 
ensured.
+    Indeed, ScriptForge provides a number of shortcuts to key UNO objects.
 
     The scriptforge.py module
-        - implements a protocol between Python (user) scripts and the 
ScriptForge Basic library
-        - contains the interfaces (classes and attributes) to be used in 
Python user scripts
-          to run the services implemented in the standard libraries shipped 
with LibreOffice
+        - describes the interfaces (classes and attributes) to be used in 
Python user scripts
+          to run the services implemented in the standard modules shipped with 
LibreOffice
+        - implements a protocol between those interfaces and, when 
appropriate, the corresponding ScriptForge
+          Basic libraries implementing the requested services.
 
     Usage:
 
-        When Python and LibreOffice run in the same process (usual case): 
either
-            from scriptforge import *   # or, better ...
+        When Python and LibreOffice run in the same process (usual case):
             from scriptforge import CreateScriptService
 
         When Python and LibreOffice are started in separate processes,
-        LibreOffice being started from console ... (example for Linux with 
port = 2021)
-            ./soffice --accept='socket,host=localhost,port=2021;urp;'
-        then use next statement:
-            from scriptforge import *   # or, better ...
+        LibreOffice being started from console ... (example for Linux with 
port = 2023)
+            ./soffice --accept='socket,host=localhost,port=2023;urp;'
+        then use next statements:
             from scriptforge import CreateScriptService, ScriptForge
-            ScriptForge(hostname = 'localhost', port = 2021)
+            ScriptForge(hostname = 'localhost', port = 2023)
 
     Specific documentation about the use of ScriptForge from Python scripts:
         
https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_intro.html?DbPAR=BASIC
@@ -80,24 +80,29 @@ class _Singleton(type):
 
 class ScriptForge(object, metaclass = _Singleton):
     """
-        The ScriptForge (singleton) class encapsulates the core of the 
ScriptForge run-time
+        The ScriptForge class encapsulates the core of the ScriptForge run-time
             - Bridge with the LibreOffice process
             - Implementation of the inter-language protocol with the Basic 
libraries
             - Identification of the available services interfaces
             - Dispatching of services
             - Coexistence with UNO
 
-        It embeds the Service class that manages the protocol with Basic
+        The class may be instantiated several times. Only the first instance 
will be retained ("Singleton").
         """
 
     # #########################################################################
     # Class attributes
     # #########################################################################
+    # Inter-process parameters
     hostname = ''
     port = 0
-    componentcontext = None
-    scriptprovider = None
-    SCRIPTFORGEINITDONE = False
+
+    componentcontext = None  # com.sun.star.uno.XComponentContext
+    scriptprovider = None   # com.sun.star.script.provider.XScriptProvider
+    SCRIPTFORGEINITDONE = False  # When True, an instance of the class exists
+
+    servicesdispatcher = None   # com.sun.star.script.provider.XScript to 
'basicdispatcher' constant
+    serviceslist = {}           # Dictionary of all available services
 
     # #########################################################################
     # Class constants
@@ -105,17 +110,18 @@ class ScriptForge(object, metaclass = _Singleton):
     library = 'ScriptForge'
     Version = '7.6'  # Actual version number
     #
-    # Basic dispatcher for Python scripts
+    # Basic dispatcher for Python scripts (@scope#library.module.function)
     basicdispatcher = 
'@application#ScriptForge.SF_PythonHelper._PythonDispatcher'
     # Python helper functions module
-    pythonhelpermodule = 'ScriptForgeHelper.py'
+    pythonhelpermodule = 'ScriptForgeHelper.py'     # Preset in production 
mode,
+    #                                                 might be changed (by 
devs only) in test mode
     #
     # VarType() constants
     V_EMPTY, V_NULL, V_INTEGER, V_LONG, V_SINGLE, V_DOUBLE = 0, 1, 2, 3, 4, 5
     V_CURRENCY, V_DATE, V_STRING, V_OBJECT, V_BOOLEAN = 6, 7, 8, 9, 11
     V_VARIANT, V_ARRAY, V_ERROR, V_UNO = 12, 8192, -1, 16
-    # Object types
-    objMODULE, objCLASS, objUNO = 1, 2, 3
+    # Types of objects returned from Basic
+    objMODULE, objCLASS, objDICT, objUNO = 1, 2, 3, 4
     # Special argument symbols
     cstSymEmpty, cstSymNull, cstSymMissing = '+++EMPTY+++', '+++NULL+++', 
'+++MISSING+++'
     # Predefined references for services implemented as standard Basic modules
@@ -132,7 +138,8 @@ class ScriptForge(object, metaclass = _Singleton):
     def __init__(self, hostname = '', port = 0):
         """
             Because singleton, constructor is executed only once while Python 
active
-            Arguments are mandatory when Python and LibreOffice run in 
separate processes
+            Both arguments are mandatory when Python and LibreOffice run in 
separate processes
+            Otherwise both arguments must be left out.
             :param hostname: probably 'localhost'
             :param port: port number
             """
@@ -194,57 +201,63 @@ class ScriptForge(object, metaclass = _Singleton):
     @classmethod
     def InvokeSimpleScript(cls, script, *args):
         """
-            Create a UNO object corresponding with the given Python or Basic 
script
-            The execution is done with the invoke() method applied on the 
created object
+            Low-level script execution via the script provider protocol:
+                Create a UNO object corresponding with the given Python or 
Basic script
+                The execution is done with the invoke() method applied on the 
created object
             Implicit scope: Either
-                "application"            a shared library                      
         (BASIC)
-                "share"                  a library of LibreOffice Macros       
         (PYTHON)
+                "application"            a shared library                    
(BASIC)
+                "share"                  a module within LibreOffice Macros  
(PYTHON)
             :param script: Either
                     [@][scope#][library.]module.method - Must not be a class 
module or method
                         [@] means that the targeted method accepts ParamArray 
arguments (Basic only)
                     [scope#][directory/]module.py$method - Must be a method 
defined at module level
-            :return: the value returned by the invoked script, or an error if 
the script was not found
+            :return: the value returned by the invoked script without 
interpretation
+                    An error is raised when the script is not found.
             """
 
-        # The frequently called PythonDispatcher in the ScriptForge Basic 
library is cached to privilege performance
-        if cls.servicesdispatcher is not None and script == 
ScriptForge.basicdispatcher:
-            xscript = cls.servicesdispatcher
-            fullscript = script
-            paramarray = True
-        #    Build the URI specification described in
-        #    
https://wiki.documentfoundation.org/Documentation/DevGuide/Scripting_Framework#Scripting_Framework_URI_Specification
-        elif len(script) > 0:
+        def ParseScript(_script):
             # Check ParamArray arguments
-            paramarray = False
-            if script[0] == '@':
-                script = script[1:]
-                paramarray = True
+            _paramarray = False
+            if _script[0] == '@':
+                _script = _script[1:]
+                _paramarray = True
             scope = ''
-            if '#' in script:
-                scope, script = script.split('#')
-            if '.py$' in script.lower():  # Python
+            if '#' in _script:
+                scope, _script = _script.split('#')
+            if '.py$' in _script.lower():  # Python
                 if len(scope) == 0:
                     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):]
-                    if '#' in script:
-                        scope, script = script.split('#')
-                uri = 
'vnd.sun.star.script:{0}?language=Python&location={1}'.format(script, scope)
+                if _script.startswith(cls.pythonhelpermodule) and hasattr(cls, 
'pythonhelpermodule2'):
+                    _script = cls.pythonhelpermodule2 + 
_script[len(cls.pythonhelpermodule):]
+                    if '#' in _script:
+                        scope, _script = _script.split('#')
+                uri = 
'vnd.sun.star.script:{0}?language=Python&location={1}'.format(_script, scope)
             else:  # Basic
                 if len(scope) == 0:
                     scope = 'application'  # Default for Basic
                 lib = ''
-                if len(script.split('.')) < 3:
+                if len(_script.split('.')) < 3:
                     lib = cls.library + '.'  # Default library = ScriptForge
-                uri = 
'vnd.sun.star.script:{0}{1}?language=Basic&location={2}'.format(lib, script, 
scope)
+                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
+            _fullscript = ('@' if _paramarray else '') + scope + ':' + _script
             try:
-                xscript = cls.scriptprovider.getScript(uri)
+                _xscript = cls.scriptprovider.getScript(uri)     # 
com.sun.star.script.provider.XScript
             except Exception:
                 raise RuntimeError(
-                    'The script \'{0}\' could not be located in your 
LibreOffice installation'.format(script))
+                    'The script \'{0}\' could not be located in your 
LibreOffice installation'.format(_script))
+            return _paramarray, _fullscript, _xscript
+
+        # The frequently called PythonDispatcher in the ScriptForge Basic 
library is cached to privilege performance
+        if cls.servicesdispatcher is not None and script == 
ScriptForge.basicdispatcher:
+            xscript = cls.servicesdispatcher
+            fullscript = script
+            paramarray = True
+        # Parse the 'script' argument and build the URI specification 
described in
+        # 
https://wiki.documentfoundation.org/Documentation/DevGuide/Scripting_Framework#Scripting_Framework_URI_Specification
+        elif len(script) > 0:
+            paramarray, fullscript, xscript = ParseScript(script)
         else:  # Should not happen
             return None
 
@@ -265,33 +278,57 @@ class ScriptForge(object, metaclass = _Singleton):
     @classmethod
     def InvokeBasicService(cls, basicobject, flags, method, *args):
         """
-            Execute a given Basic script and interpret its result
+            High-level script execution via the ScriptForge inter-language 
protocol:
+            To be used for all service methods having their implementation in 
the Basic world
+                Substitute dictionary arguments by sets of UNO property values
+                Execute the given Basic method on a class instance
+                Interpret its result
             This method has as counterpart the 
ScriptForge.SF_PythonHelper._PythonDispatcher() Basic method
-            :param basicobject: a Service subclass
+            :param basicobject: a SFServices subclass instance
+                The real object is cached in a Basic Global variable and 
identified by its reference
             :param flags: see the vb* and flg* constants in the SFServices 
class
             :param method: the name of the method or property to invoke, as a 
string
             :param args: the arguments of the method. Symbolic cst* constants 
may be necessary
             :return: The invoked Basic counterpart script (with 
InvokeSimpleScript()) will return a tuple
-                [0]     The returned value - scalar, object reference or a 
tuple
-                [1]     The Basic VarType() of the returned value
-                        Null, Empty and Nothing have different vartypes but 
return all None to Python
+                [0/Value]       The returned value - scalar, object reference, 
UNO object or a tuple
+                [1/VarType]     The Basic VarType() of the returned value
+                                Null, Empty and Nothing have own vartypes but 
return all None to Python
                 Additionally, when [0] is a tuple:
-                [2]     Number of dimensions in Basic
+                    [2/Dims]        Number of dimensions of the original Basic 
array
                 Additionally, when [0] is a UNO or Basic object:
-                [2]     Module (1), Class instance (2) or UNO (3)
-                [3]     The object's ObjectType
-                [4]     The object's ServiceName
-                [5]     The object's name
+                    [2/Class]       Basic module (1), Basic class instance 
(2), Dictionary (3), UNO object (4)
+                Additionally, when [0] is a Basic object:
+                    [3/Type]        The object's ObjectType
+                    [4/Service]     The object's ServiceName
+                    [5/Name]        The object's name
                 When an error occurs Python receives None as a scalar. This 
determines the occurrence of a failure
                 The method returns either
                     - the 0th element of the tuple when scalar, tuple or UNO 
object
-                    - a new Service() object or one of its subclasses otherwise
+                    - a new SFServices() object or one of its subclasses 
otherwise
             """
         # Constants
         script = ScriptForge.basicdispatcher
         cstNoArgs = '+++NOARGS+++'
         cstValue, cstVarType, cstDims, cstClass, cstType, cstService, cstName 
= 0, 1, 2, 2, 3, 4, 5
 
+        def ConvertDictArgs():
+            """
+                Convert dictionaries in arguments to sets of property values
+                """
+            argslist = list(args)
+            for i in range(len(args)):
+                arg = argslist[i]
+                if isinstance(arg, dict):
+                    argdict = arg
+                    if not isinstance(argdict, SFScriptForge.SF_Dictionary):
+                        argdict = 
CreateScriptService('ScriptForge.Dictionary', arg)
+                    argslist[i] = argdict.ConvertToPropertyValues()
+            return tuple(argslist)
+
+        #
+        # Intercept dictionary arguments
+        if flags & SFServices.flgDictArg == SFServices.flgDictArg:  # Bits 
comparison
+            args = ConvertDictArgs()
         #
         # Run the basic script
         # The targeted script has a ParamArray argument. Do not change next 4 
lines except if you know what you do !
@@ -307,9 +344,21 @@ class ScriptForge(object, metaclass = _Singleton):
             raise RuntimeError("The execution of the method '" + method + "' 
failed. Execution stops.")
         #
         # Analyze the returned tuple
-        if returntuple[cstVarType] == ScriptForge.V_OBJECT and 
len(returntuple) > cstClass:  # Avoid Nothing
+        # Distinguish:
+        #   A Basic object to be mapped onto a new Python class instance
+        #   A UNO object
+        #   A set of property values to be returned as a dict()
+        #   An array, tuple or tuple of tuples
+        #   A scalar or Nothing
+        returnvalue = returntuple[cstValue]
+        if returntuple[cstVarType] == ScriptForge.V_OBJECT and 
len(returntuple) > cstClass:  # Skip Nothing
             if returntuple[cstClass] == ScriptForge.objUNO:
                 pass
+            elif returntuple[cstClass] == ScriptForge.objDICT:
+                dico = CreateScriptService('ScriptForge.Dictionary')
+                if not isinstance(returnvalue, uno.ByteSequence):   # if array 
not empty
+                    dico.ImportFromPropertyValues(returnvalue, overwrite = 
True)
+                return dico
             else:
                 # Create the new class instance of the right subclass of 
SFServices()
                 servname = returntuple[cstService]
@@ -318,18 +367,17 @@ class ScriptForge(object, metaclass = _Singleton):
                     raise RuntimeError("The service '" + servname + "' is not 
available in Python. Execution stops.")
                 subcls = cls.serviceslist[servname]
                 if subcls is not None:
-                    return subcls(returntuple[cstValue], returntuple[cstType], 
returntuple[cstClass],
-                                  returntuple[cstName])
+                    return subcls(returnvalue, returntuple[cstType], 
returntuple[cstClass], returntuple[cstName])
         elif returntuple[cstVarType] >= ScriptForge.V_ARRAY:
             # Intercept empty array
-            if isinstance(returntuple[cstValue], uno.ByteSequence):
+            if isinstance(returnvalue, uno.ByteSequence):
                 return ()
         elif returntuple[cstVarType] == ScriptForge.V_DATE:
-            dat = 
SFScriptForge.SF_Basic.CDateFromUnoDateTime(returntuple[cstValue])
+            dat = SFScriptForge.SF_Basic.CDateFromUnoDateTime(returnvalue)
             return dat
         else:  # All other scalar values
             pass
-        return returntuple[cstValue]
+        return returnvalue
 
     @staticmethod
     def SetAttributeSynonyms():
@@ -429,8 +477,6 @@ class SFServices(object):
                 Conventionally, camel-cased and lower-cased synonyms are 
supported where relevant
                     a dictionary named 'serviceproperties' with keys = 
(proper-cased) property names and value = boolean
                         True = editable, False = read-only
-                    a list named 'localProperties' reserved to properties for 
internal use
-                        e.g. oDlg.Controls() is a method that uses '_Controls' 
to hold the list of available controls
                 When
                     forceGetProperty = False    # Standard behaviour
                 read-only serviceproperties are buffered in Python after their 
1st get request to Basic
@@ -449,7 +495,8 @@ 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 = 16  # The method or the property implies a hardcoded 
post-processing
+    flgDictArg = 32  # Invoked service method may contain a dict argument
     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
@@ -476,14 +523,12 @@ class SFServices(object):
         """
             Trivial initialization of internal properties
             If the subclass has its own __init()__ method, a call to this one 
should be its first statement.
-            Afterwards localProperties should be filled with the list of its 
own properties
             """
         self.objectreference = reference  # the index in the Python storage 
where the Basic object is stored
-        self.objecttype = objtype  # ('SF_String', 'DICTIONARY', ...)
+        self.objecttype = objtype  # ('SF_String', 'TIMER', ...)
         self.classmodule = classmodule  # Module (1), Class instance (2)
         self.name = name  # '' when no name
         self.internal = False  # True to exceptionally allow assigning a new 
value to a read-only property
-        self.localProperties = []  # the properties reserved for internal use 
(often empty)
 
     def __getattr__(self, name):
         """
@@ -495,7 +540,7 @@ class SFServices(object):
         if name in self.propertysynonyms:  # Reset real name if argument 
provided in lower or camel case
             name = self.propertysynonyms[name]
         if self.serviceimplementation == 'basic':
-            if name in ('serviceproperties', 'localProperties', 
'internal_attributes', 'propertysynonyms',
+            if name in ('serviceproperties', 'internal_attributes', 
'propertysynonyms',
                         'forceGetProperty'):
                 pass
             elif name in self.serviceproperties:
@@ -519,10 +564,10 @@ class SFServices(object):
             Management of __dict__ is automatically done in the final usual 
object.__setattr__ method
             """
         if self.serviceimplementation == 'basic':
-            if name in ('serviceproperties', 'localProperties', 
'internal_attributes', 'propertysynonyms',
+            if name in ('serviceproperties', 'internal_attributes', 
'propertysynonyms',
                         'forceGetProperty'):
                 pass
-            elif name[0:2] == '__' or name in self.internal_attributes or name 
in self.localProperties:
+            elif name[0:2] == '__' or name in self.internal_attributes:
                 pass
             elif name in self.serviceproperties or name in 
self.propertysynonyms:
                 if name in self.propertysynonyms:  # Reset real name if 
argument provided in lower or camel case
@@ -534,9 +579,9 @@ class SFServices(object):
                     return
                 else:
                     raise AttributeError(
-                        "type object '" + self.objecttype + "' has no editable 
property '" + name + "'")
+                        "object of type '" + self.objecttype + "' has no 
editable property '" + name + "'")
             else:
-                raise AttributeError("type object '" + self.objecttype + "' 
has no property '" + name + "'")
+                raise AttributeError("object of type '" + self.objecttype + "' 
has no property '" + name + "'")
         object.__setattr__(self, name, value)
         return
 
@@ -547,7 +592,7 @@ class SFServices(object):
     def Dispose(self):
         if self.serviceimplementation == 'basic':
             if self.objectreference >= len(ScriptForge.servicesmodules):  # Do 
not dispose predefined module objects
-                self.ExecMethod(self.vbMethod, 'Dispose')
+                self.ExecMethod(self.vbMethod + self.flgPost, 'Dispose')
                 self.objectreference = -1
 
     def ExecMethod(self, flags = 0, methodname = '', *args):
@@ -593,6 +638,8 @@ class SFServices(object):
             if isinstance(value, datetime.datetime):
                 value = SFScriptForge.SF_Basic.CDateToUnoDateTime(value)
                 flag += self.flgDateArg
+            elif isinstance(value, dict):
+                flag += self.flgDictArg
             if repr(type(value)) == "<class 'pyuno'>":
                 flag += self.flgUno
             return self.EXEC(self.objectreference, flag, propertyname, value)
@@ -1128,7 +1175,7 @@ class SFScriptForge:
             return self.ExecMethod(self.vbMethod, 'FileExists', filename)
 
         def Files(self, foldername, filter = '', includesubfolders = False):
-            return self.ExecMethod(self.vbMethod, 'Files', foldername, filter, 
includesubfolders)
+            return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Files', 
foldername, filter, includesubfolders)
 
         def FolderExists(self, foldername):
             return self.ExecMethod(self.vbMethod, 'FolderExists', foldername)
@@ -1186,7 +1233,8 @@ class SFScriptForge:
             return self.ExecMethod(self.vbMethod, 'PickFolder', defaultfolder, 
freetext)
 
         def SubFolders(self, foldername, filter = '', includesubfolders = 
False):
-            return self.ExecMethod(self.vbMethod, 'SubFolders', foldername, 
filter, includesubfolders)
+            return self.ExecMethod(self.vbMethod + self.flgArrayRet, 
'SubFolders', foldername,
+                                   filter, includesubfolders)
 
         @classmethod
         def _ConvertFromUrl(cls, filename):
@@ -1304,15 +1352,6 @@ class SFScriptForge:
         def PythonVersion(self):
             return self.SIMPLEEXEC(self.py, 'PythonVersion')
 
-        @property
-        def UserData(self):
-            props = self.GetProperty('UserData')    # is an array of property 
values
-            if len(props) == 0:
-                return dict()
-            dico = CreateScriptService('Dictionary')
-            dico.ImportFromPropertyValues(props, overwrite = True)
-            return dico
-
     # #########################################################################
     # SF_Region CLASS
     # #########################################################################
@@ -1457,6 +1496,9 @@ class SFScriptForge:
         def ExecutePythonScript(cls, scope = '', script = '', *args):
             return cls.SIMPLEEXEC(scope + '#' + script, *args)
 
+        def GetPDFExportOptions(self):
+            return self.ExecMethod(self.vbMethod, 'GetPDFExportOptions')
+
         def HasUnoMethod(self, unoobject, methodname):
             return self.ExecMethod(self.vbMethod, 'HasUnoMethod', unoobject, 
methodname)
 
@@ -1474,14 +1516,17 @@ class SFScriptForge:
         def SendMail(self, recipient, cc = '', bcc = '', subject = '', body = 
'', filenames = '', editmessage = True):
             return self.ExecMethod(self.vbMethod, 'SendMail', recipient, cc, 
bcc, subject, body, filenames, editmessage)
 
+        def SetPDFExportOptions(self, pdfoptions):
+            return self.ExecMethod(self.vbMethod + self.flgDictArg, 
'SetPDFExportOptions', pdfoptions)
+
         def UnoObjectType(self, unoobject):
             return self.ExecMethod(self.vbMethod, 'UnoObjectType', unoobject)
 
         def UnoMethods(self, unoobject):
-            return self.ExecMethod(self.vbMethod, 'UnoMethods', unoobject)
+            return self.ExecMethod(self.vbMethod + self.flgArrayRet, 
'UnoMethods', unoobject)
 
         def UnoProperties(self, unoobject):
-            return self.ExecMethod(self.vbMethod, 'UnoProperties', unoobject)
+            return self.ExecMethod(self.vbMethod + self.flgArrayRet, 
'UnoProperties', unoobject)
 
         def WebService(self, uri):
             return self.ExecMethod(self.vbMethod, 'WebService', uri)
@@ -1531,10 +1576,11 @@ class SFScriptForge:
             return self.ExecMethod(self.vbMethod, 'IsUrl', inputstr)
 
         def SplitNotQuoted(self, inputstr, delimiter = ' ', occurrences = 0, 
quotechar = '"'):
-            return self.ExecMethod(self.vbMethod, 'SplitNotQuoted', inputstr, 
delimiter, occurrences, quotechar)
+            return self.ExecMethod(self.vbMethod + self.flgArrayRet, 
'SplitNotQuoted', inputstr, delimiter,
+                                   occurrences, quotechar)
 
         def Wrap(self, inputstr, width = 70, tabsize = 8):
-            return self.ExecMethod(self.vbMethod, 'Wrap', inputstr, width, 
tabsize)
+            return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Wrap', 
inputstr, width, tabsize)
 
     # #########################################################################
     # SF_TextStream CLASS
@@ -1647,8 +1693,6 @@ class SFScriptForge:
         def ActiveWindow(self):
             return self.ExecMethod(self.vbMethod, 'ActiveWindow')
 
-        activeWindow, activewindow = ActiveWindow, ActiveWindow
-
         def Activate(self, windowname = ''):
             return self.ExecMethod(self.vbMethod, 'Activate', windowname)
 
@@ -1660,7 +1704,7 @@ class SFScriptForge:
             return self.ExecMethod(self.vbMethod, 'CreateDocument', 
documenttype, templatefile, hidden)
 
         def Documents(self):
-            return self.ExecMethod(self.vbMethod, 'Documents')
+            return self.ExecMethod(self.vbMethod + self.flgArrayRet, 
'Documents')
 
         def GetDocument(self, windowname = ''):
             return self.ExecMethod(self.vbMethod, 'GetDocument', windowname)
@@ -1868,13 +1912,13 @@ class SFDialogs:
 
         # Methods potentially executed while the dialog is in execution 
require the flgHardCode flag
         def Activate(self):
-            return self.ExecMethod(self.vbMethod + self.flgHardCode, 
'Activate')
+            return self.ExecMethod(self.vbMethod, 'Activate')
 
         def Center(self, parent = ScriptForge.cstSymMissing):
             parentclasses = (SFDocuments.SF_Document, SFDocuments.SF_Base, 
SFDocuments.SF_Calc, SFDocuments.SF_Writer,
                              SFDialogs.SF_Dialog)
             parentobj = parent.objectreference if isinstance(parent, 
parentclasses) else parent
-            return self.ExecMethod(self.vbMethod + self.flgObject + 
self.flgHardCode, 'Center', parentobj)
+            return self.ExecMethod(self.vbMethod + self.flgObject, 'Center', 
parentobj)
 
         def CloneControl(self, sourcename, controlname, left = 1, top = 1):
             return self.ExecMethod(self.vbMethod, 'CloneControl', sourcename, 
controlname, left, top)
@@ -1981,7 +2025,7 @@ class SFDialogs:
             return self.ExecMethod(self.vbMethod, 'CreateTreeControl', 
controlname, place, border)
 
         def EndExecute(self, returnvalue):
-            return self.ExecMethod(self.vbMethod + self.flgHardCode, 
'EndExecute', returnvalue)
+            return self.ExecMethod(self.vbMethod, 'EndExecute', returnvalue)
 
         def Execute(self, modal = True):
             return self.ExecMethod(self.vbMethod + self.flgHardCode, 
'Execute', modal)
@@ -1994,7 +2038,7 @@ class SFDialogs:
             return self.ExecMethod(self.vbMethod, 'OrderTabs', tabslist, 
start, increment)
 
         def Resize(self, left = -99999, top = -99999, width = -1, height = -1):
-            return self.ExecMethod(self.vbMethod + self.flgHardCode, 'Resize', 
left, top, width, height)
+            return self.ExecMethod(self.vbMethod, 'Resize', left, top, width, 
height)
 
         def SetPageManager(self, pilotcontrols = '', tabcontrols = '', 
wizardcontrols = '', lastpage = 0):
             return self.ExecMethod(self.vbMethod, 'SetPageManager', 
pilotcontrols, tabcontrols, wizardcontrols,
@@ -2117,10 +2161,11 @@ class SFDocuments:
         serviceimplementation = 'basic'
         servicename = 'SFDocuments.Document'
         servicesynonyms = ('document', 'sfdocuments.document')
-        serviceproperties = dict(Description = True, DocumentType = False, 
ExportFilters = False, FileSystem = False,
-                                 ImportFilters = False, IsBase = False, IsCalc 
= False, IsDraw = False,
-                                 IsFormDocument = False, IsImpress = False, 
IsMath = False, IsWriter = False,
-                                 Keywords = True, Readonly = False, Subject = 
True, Title = True, XComponent = False)
+        serviceproperties = dict(CustomProperties = True, Description = True, 
DocumentProperties = False,
+                                 DocumentType = False, ExportFilters = False, 
FileSystem = False, ImportFilters = False,
+                                 IsBase = False, IsCalc = False, IsDraw = 
False, IsFormDocument = False,
+                                 IsImpress = False, IsMath = False, IsWriter = 
False, Keywords = True, Readonly = False,
+                                 Subject = True, Title = True, XComponent = 
False)
         # Force for each property to get its value from Basic - due to intense 
interactivity with user
         forceGetProperty = True
 
@@ -2242,11 +2287,12 @@ class SFDocuments:
         serviceimplementation = 'basic'
         servicename = 'SFDocuments.Calc'
         servicesynonyms = ('calc', 'sfdocuments.calc')
-        serviceproperties = dict(CurrentSelection = True, Sheets = False,
-                                 Description = True, DocumentType = False, 
ExportFilters = False, FileSystem = False,
-                                 ImportFilters = False, IsBase = False, IsCalc 
= False, IsDraw = False,
-                                 IsFormDocument = False, IsImpress = False, 
IsMath = False, IsWriter = False,
-                                 Keywords = True, Readonly = False, Subject = 
True, Title = True, XComponent = False)
+        serviceproperties = dict(CurrentSelection = True, CustomProperties = 
True, Description = True,
+                                 DocumentProperties = False, DocumentType = 
False, ExportFilters = False,
+                                 FileSystem = False, ImportFilters = False, 
IsBase = False, IsCalc = False,
+                                 IsDraw = False, IsFormDocument = False, 
IsImpress = False, IsMath = False,
+                                 IsWriter = False, Keywords = True, Readonly = 
False, Sheets = False,  Subject = True,
+                                 Title = True, XComponent = False)
         # Force for each property to get its value from Basic - due to intense 
interactivity with user
         forceGetProperty = True
 
@@ -2637,10 +2683,11 @@ class SFDocuments:
         serviceimplementation = 'basic'
         servicename = 'SFDocuments.Writer'
         servicesynonyms = ('writer', 'sfdocuments.writer')
-        serviceproperties = dict(Description = True, DocumentType = False, 
ExportFilters = False, FileSystem = False,
-                                 ImportFilters = False, IsBase = False, IsCalc 
= False, IsDraw = False,
-                                 IsFormDocument = False, IsImpress = False, 
IsMath = False, IsWriter = False,
-                                 Keywords = True, Readonly = False, Subject = 
True, Title = True, XComponent = False)
+        serviceproperties = dict(CustomProperties = True, Description = True, 
DocumentProperties = False,
+                                 DocumentType = False, ExportFilters = False, 
FileSystem = False, ImportFilters = False,
+                                 IsBase = False, IsCalc = False, IsDraw = 
False, IsFormDocument = False,
+                                 IsImpress = False, IsMath = False, IsWriter = 
False, Keywords = True, Readonly = False,
+                                 Subject = True, Title = True, XComponent = 
False)
         # Force for each property to get its value from Basic - due to intense 
interactivity with user
         forceGetProperty = True
 

Reply via email to