summaryrefslogtreecommitdiffstats
path: root/wizards/source/scriptforge/python
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /wizards/source/scriptforge/python
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'wizards/source/scriptforge/python')
-rw-r--r--wizards/source/scriptforge/python/ScriptForgeHelper.py317
-rw-r--r--wizards/source/scriptforge/python/scriptforge.py2539
2 files changed, 2856 insertions, 0 deletions
diff --git a/wizards/source/scriptforge/python/ScriptForgeHelper.py b/wizards/source/scriptforge/python/ScriptForgeHelper.py
new file mode 100644
index 000000000..396273233
--- /dev/null
+++ b/wizards/source/scriptforge/python/ScriptForgeHelper.py
@@ -0,0 +1,317 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2019-2022 Jean-Pierre LEDURE, Rafael LIMA, Alain ROMEDENNE
+
+# ======================================================================================================================
+# === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+# === Full documentation is available on https://help.libreoffice.org/ ===
+# ======================================================================================================================
+
+# ScriptForge is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# ScriptForge is free software; you can redistribute it and/or modify it under the terms of either (at your option):
+
+# 1) The Mozilla Public License, v. 2.0. If a copy of the MPL was not
+# distributed with this file, you can obtain one at http://mozilla.org/MPL/2.0/ .
+
+# 2) The GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version. If a copy of the LGPL was not
+# distributed with this file, see http://www.gnu.org/licenses/ .
+
+"""
+Collection of Python helper functions called from the ScriptForge Basic libraries
+to execute specific services that are not or not easily available from Basic directly.
+"""
+
+import getpass
+import os
+import platform
+import hashlib
+import filecmp
+import webbrowser
+import json
+
+
+class _Singleton(type):
+ """
+ A Singleton design pattern
+ Credits: « Python in a Nutshell » by Alex Martelli, O'Reilly
+ """
+ instances = {}
+
+ def __call__(cls, *args, **kwargs):
+ if cls not in cls.instances:
+ cls.instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
+ return cls.instances[cls]
+
+
+# #################################################################
+# Dictionary service
+# #################################################################
+
+def _SF_Dictionary__ConvertToJson(propval, indent = None) -> str:
+ # used by Dictionary.ConvertToJson() Basic method
+ """
+ Given an array of PropertyValues as argument, convert it to a JSON string
+ """
+ # Array of property values => Dict(ionary) => JSON
+ pvDict = {}
+ for pv in propval:
+ pvDict[pv.Name] = pv.Value
+ return json.dumps(pvDict, indent=indent, skipkeys=True)
+
+
+def _SF_Dictionary__ImportFromJson(jsonstr: str): # used by Dictionary.ImportFromJson() Basic method
+ """
+ Given a JSON string as argument, convert it to a list of tuples (name, value)
+ The value must not be a (sub)dict. This doesn't pass the python-basic bridge.
+ """
+ # JSON => Dictionary => Array of tuples/lists
+ dico = json.loads(jsonstr)
+ result = []
+ for key in iter(dico):
+ value = dico[key]
+ item = value
+ if isinstance(value, dict): # check that first level is not itself a (sub)dict
+ item = None
+ elif isinstance(value, list): # check every member of the list is not a (sub)dict
+ for i in range(len(value)):
+ if isinstance(value[i], dict): value[i] = None
+ result.append((key, item))
+ return result
+
+
+# #################################################################
+# Exception service
+# #################################################################
+
+def _SF_Exception__PythonPrint(string: str) -> bool:
+ # used by SF_Exception.PythonPrint() Basic method
+ """
+ Write the argument to stdout.
+ If the APSO shell console is active, the argument will be displayed in the console window
+ """
+ print(string)
+ return True
+
+
+# #################################################################
+# FileSystem service
+# #################################################################
+
+def _SF_FileSystem__CompareFiles(filename1: str, filename2: str, comparecontents=True) -> bool:
+ # used by SF_FileSystem.CompareFiles() Basic method
+ """
+ Compare the 2 files, returning True if they seem equal, False otherwise.
+ By default, only their signatures (modification time, ...) are compared.
+ When comparecontents == True, their contents are compared.
+ """
+ try:
+ return filecmp.cmp(filename1, filename2, not comparecontents)
+ except Exception:
+ return False
+
+
+def _SF_FileSystem__GetFilelen(systemfilepath: str) -> str: # used by SF_FileSystem.GetFilelen() Basic method
+ return str(os.path.getsize(systemfilepath))
+
+
+def _SF_FileSystem__HashFile(filename: str, algorithm: str) -> str: # used by SF_FileSystem.HashFile() Basic method
+ """
+ Hash a given file with the given hashing algorithm
+ cfr. https://www.pythoncentral.io/hashing-files-with-python/
+ Example
+ hash = _SF_FileSystem__HashFile('myfile.txt','MD5')
+ """
+ algo = algorithm.lower()
+ try:
+ if algo in hashlib.algorithms_guaranteed:
+ BLOCKSIZE = 65535 # Provision for large size files
+ if algo == 'md5':
+ hasher = hashlib.md5()
+ elif algo == 'sha1':
+ hasher = hashlib.sha1()
+ elif algo == 'sha224':
+ hasher = hashlib.sha224()
+ elif algo == 'sha256':
+ hasher = hashlib.sha256()
+ elif algo == 'sha384':
+ hasher = hashlib.sha384()
+ elif algo == 'sha512':
+ hasher = hashlib.sha512()
+ else:
+ return ''
+ with open(filename, 'rb') as file: # open in binary mode
+ buffer = file.read(BLOCKSIZE)
+ while len(buffer) > 0:
+ hasher.update(buffer)
+ buffer = file.read(BLOCKSIZE)
+ return hasher.hexdigest()
+ else:
+ return ''
+ except Exception:
+ return ''
+
+
+# #################################################################
+# Platform service
+# #################################################################
+
+def _SF_Platform(propertyname: str): # used by SF_Platform Basic module
+ """
+ Switch between SF_Platform properties (read the documentation about the ScriptForge.Platform service)
+ """
+ pf = Platform()
+ if propertyname == 'Architecture':
+ return pf.Architecture
+ elif propertyname == 'ComputerName':
+ return pf.ComputerName
+ elif propertyname == 'CPUCount':
+ return pf.CPUCount
+ elif propertyname == 'CurrentUser':
+ return pf.CurrentUser
+ elif propertyname == 'Machine':
+ return pf.Machine
+ elif propertyname == 'OSName':
+ return pf.OSName
+ elif propertyname == 'OSPlatform':
+ return pf.OSPlatform
+ elif propertyname == 'OSRelease':
+ return pf.OSRelease
+ elif propertyname == 'OSVersion':
+ return pf.OSVersion
+ elif propertyname == 'Processor':
+ return pf.Processor
+ elif propertyname == 'PythonVersion':
+ return pf.PythonVersion
+ else:
+ return None
+
+
+class Platform(object, metaclass = _Singleton):
+ @property
+ def Architecture(self): return platform.architecture()[0]
+
+ @property # computer's network name
+ def ComputerName(self): return platform.node()
+
+ @property # number of CPU's
+ def CPUCount(self): return os.cpu_count()
+
+ @property
+ def CurrentUser(self):
+ try:
+ return getpass.getuser()
+ except Exception:
+ return ''
+
+ @property # machine type e.g. 'i386'
+ def Machine(self): return platform.machine()
+
+ @property # system/OS name e.g. 'Darwin', 'Java', 'Linux', ...
+ def OSName(self): return platform.system().replace('Darwin', 'macOS')
+
+ @property # underlying platform e.g. 'Windows-10-...'
+ def OSPlatform(self): return platform.platform(aliased = True)
+
+ @property # system's release e.g. '2.2.0'
+ def OSRelease(self): return platform.release()
+
+ @property # system's version
+ def OSVersion(self): return platform.version()
+
+ @property # real processor name e.g. 'amdk'
+ def Processor(self): return platform.processor()
+
+ @property # Python major.minor.patchlevel
+ def PythonVersion(self): return 'Python ' + platform.python_version()
+
+
+# #################################################################
+# Session service
+# #################################################################
+
+def _SF_Session__OpenURLInBrowser(url: str): # Used by SF_Session.OpenURLInBrowser() Basic method
+ """
+ Display url using the default browser
+ """
+ try:
+ webbrowser.open(url, new = 2)
+ finally:
+ return None
+
+
+# #################################################################
+# String service
+# #################################################################
+
+def _SF_String__HashStr(string: str, algorithm: str) -> str: # used by SF_String.HashStr() Basic method
+ """
+ Hash a given UTF-8 string with the given hashing algorithm
+ Example
+ hash = _SF_String__HashStr('This is a UTF-8 encoded string.','MD5')
+ """
+ algo = algorithm.lower()
+ try:
+ if algo in hashlib.algorithms_guaranteed:
+ ENCODING = 'utf-8'
+ bytestring = string.encode(ENCODING) # Hashing functions expect bytes, not strings
+ if algo == 'md5':
+ hasher = hashlib.md5(bytestring)
+ elif algo == 'sha1':
+ hasher = hashlib.sha1(bytestring)
+ elif algo == 'sha224':
+ hasher = hashlib.sha224(bytestring)
+ elif algo == 'sha256':
+ hasher = hashlib.sha256(bytestring)
+ elif algo == 'sha384':
+ hasher = hashlib.sha384(bytestring)
+ elif algo == 'sha512':
+ hasher = hashlib.sha512(bytestring)
+ else:
+ return ''
+ return hasher.hexdigest()
+ else:
+ return ''
+ except Exception:
+ return ''
+
+
+# #################################################################
+# lists the scripts, that shall be visible inside the Basic/Python IDE
+# #################################################################
+
+g_exportedScripts = ()
+
+if __name__ == "__main__":
+ print(_SF_Platform('Architecture'))
+ print(_SF_Platform('ComputerName'))
+ print(_SF_Platform('CPUCount'))
+ print(_SF_Platform('CurrentUser'))
+ print(_SF_Platform('Machine'))
+ print(_SF_Platform('OSName'))
+ print(_SF_Platform('OSPlatform'))
+ print(_SF_Platform('OSRelease'))
+ print(_SF_Platform('OSVersion'))
+ print(_SF_Platform('Processor'))
+ print(_SF_Platform('PythonVersion'))
+ #
+ print(hashlib.algorithms_guaranteed)
+ print(_SF_FileSystem__HashFile('/opt/libreoffice6.4/program/libbootstraplo.so', 'md5'))
+ print(_SF_FileSystem__HashFile('/opt/libreoffice6.4/share/Scripts/python/Capitalise.py', 'sha512'))
+ #
+ print(_SF_String__HashStr('œ∑¡™£¢∞§¶•ªº–≠œ∑´®†¥¨ˆøπ“‘åß∂ƒ©˙∆˚¬', 'MD5')) # 616eb9c513ad07cd02924b4d285b9987
+ #
+ # _SF_Session__OpenURLInBrowser('https://docs.python.org/3/library/webbrowser.html')
+ #
+ js = """
+ {"firstName": "John","lastName": "Smith","isAlive": true,"age": 27,
+ "address": {"streetAddress": "21 2nd Street","city": "New York","state": "NY","postalCode": "10021-3100"},
+ "phoneNumbers": [{"type": "home","number": "212 555-1234"},{"type": "office","number": "646 555-4567"}],
+ "children": ["Q", "M", "G", "T"],"spouse": null}
+ """
+ arr = _SF_Dictionary__ImportFromJson(js)
+ print(arr)
diff --git a/wizards/source/scriptforge/python/scriptforge.py b/wizards/source/scriptforge/python/scriptforge.py
new file mode 100644
index 000000000..ebc6f147c
--- /dev/null
+++ b/wizards/source/scriptforge/python/scriptforge.py
@@ -0,0 +1,2539 @@
+# -*- coding: utf-8 -*-
+
+# Copyright 2020-2022 Jean-Pierre LEDURE, Rafael LIMA, Alain ROMEDENNE
+
+# =====================================================================================================================
+# === The ScriptForge library and its associated libraries are part of the LibreOffice project. ===
+# === Full documentation is available on https://help.libreoffice.org/ ===
+# =====================================================================================================================
+
+# ScriptForge is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+# ScriptForge is free software; you can redistribute it and/or modify it under the terms of either (at your option):
+
+# 1) The Mozilla Public License, v. 2.0. If a copy of the MPL was not
+# distributed with this file, you can obtain one at http://mozilla.org/MPL/2.0/ .
+
+# 2) The GNU Lesser General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version. If a copy of the LGPL was not
+# distributed with this file, see http://www.gnu.org/licenses/ .
+
+"""
+ ScriptForge libraries are an extensible and robust collection of macro scripting resources for LibreOffice
+ to be invoked from user Basic or Python macros. Users familiar with other BASIC macro variants often face hard
+ times to dig into the extensive LibreOffice Application Programming Interface even for the simplest operations.
+ 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 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
+
+ Usage:
+
+ When Python and LibreOffice run in the same process (usual case): either
+ from scriptforge import * # or, better ...
+ 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 ...
+ from scriptforge import CreateScriptService, ScriptForge
+ ScriptForge(hostname = 'localhost', port = 2021)
+
+ 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
+ """
+
+import uno
+
+import datetime
+import time
+import os
+
+
+class _Singleton(type):
+ """
+ A Singleton metaclass design pattern
+ Credits: « Python in a Nutshell » by Alex Martelli, O'Reilly
+ """
+ instances = {}
+
+ def __call__(cls, *args, **kwargs):
+ if cls not in cls.instances:
+ cls.instances[cls] = super(_Singleton, cls).__call__(*args, **kwargs)
+ return cls.instances[cls]
+
+
+# #####################################################################################################################
+# ScriptForge CLASS ###
+# #####################################################################################################################
+
+class ScriptForge(object, metaclass = _Singleton):
+ """
+ The ScriptForge (singleton) 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
+ """
+
+ # #########################################################################
+ # Class attributes
+ # #########################################################################
+ hostname = ''
+ port = 0
+ componentcontext = None
+ scriptprovider = None
+ SCRIPTFORGEINITDONE = False
+
+ # #########################################################################
+ # Class constants
+ # #########################################################################
+ library = 'ScriptForge'
+ Version = '7.4' # Actual version number
+ #
+ # Basic dispatcher for Python scripts
+ basicdispatcher = '@application#ScriptForge.SF_PythonHelper._PythonDispatcher'
+ # Python helper functions module
+ pythonhelpermodule = 'ScriptForgeHelper.py'
+ #
+ # 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
+ # Special argument symbols
+ cstSymEmpty, cstSymNull, cstSymMissing = '+++EMPTY+++', '+++NULL+++', '+++MISSING+++'
+ # Predefined references for services implemented as standard Basic modules
+ servicesmodules = dict([('ScriptForge.Array', 0),
+ ('ScriptForge.Exception', 1),
+ ('ScriptForge.FileSystem', 2),
+ ('ScriptForge.Platform', 3),
+ ('ScriptForge.Region', 4),
+ ('ScriptForge.Services', 5),
+ ('ScriptForge.Session', 6),
+ ('ScriptForge.String', 7),
+ ('ScriptForge.UI', 8)])
+
+ 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
+ :param hostname: probably 'localhost'
+ :param port: port number
+ """
+ ScriptForge.hostname = hostname
+ ScriptForge.port = port
+ # Determine main pyuno entry points
+ ScriptForge.componentcontext = self.ConnectToLOProcess(hostname, port) # com.sun.star.uno.XComponentContext
+ ScriptForge.scriptprovider = self.ScriptProvider(self.componentcontext) # ...script.provider.XScriptProvider
+ #
+ # Establish a list of the available services as a dictionary (servicename, serviceclass)
+ ScriptForge.serviceslist = dict((cls.servicename, cls) for cls in SFServices.__subclasses__())
+ ScriptForge.servicesdispatcher = None
+ #
+ # All properties and methods of the ScriptForge API are ProperCased
+ # Compute their synonyms as lowercased and camelCased names
+ ScriptForge.SetAttributeSynonyms()
+ #
+ ScriptForge.SCRIPTFORGEINITDONE = True
+
+ @classmethod
+ def ConnectToLOProcess(cls, hostname = '', port = 0):
+ """
+ Called by the ScriptForge class constructor to establish the connection with
+ the requested LibreOffice instance
+ The default arguments are for the usual interactive mode
+
+ :param hostname: probably 'localhost' or ''
+ :param port: port number or 0
+ :return: the derived component context
+ """
+ if len(hostname) > 0 and port > 0: # Explicit connection request via socket
+ ctx = uno.getComponentContext() # com.sun.star.uno.XComponentContext
+ resolver = ctx.ServiceManager.createInstanceWithContext(
+ 'com.sun.star.bridge.UnoUrlResolver', ctx) # com.sun.star.comp.bridge.UnoUrlResolver
+ try:
+ conn = 'socket,host=%s,port=%d' % (hostname, port)
+ url = 'uno:%s;urp;StarOffice.ComponentContext' % conn
+ ctx = resolver.resolve(url)
+ except Exception: # thrown when LibreOffice specified instance isn't started
+ raise SystemExit(
+ 'Connection to LibreOffice failed (host = ' + hostname + ', port = ' + str(port) + ')')
+ return ctx
+ elif len(hostname) == 0 and port == 0: # Usual interactive mode
+ return uno.getComponentContext()
+ else:
+ raise SystemExit('The creation of the ScriptForge() instance got invalid arguments: '
+ + '(host = ' + hostname + ', port = ' + str(port) + ')')
+
+ @classmethod
+ def ScriptProvider(cls, context = None):
+ """
+ Returns the general script provider
+ """
+ servicemanager = context.ServiceManager # com.sun.star.lang.XMultiComponentFactory
+ masterscript = servicemanager.createInstanceWithContext(
+ 'com.sun.star.script.provider.MasterScriptProviderFactory', context)
+ return masterscript.createScriptProvider("")
+
+ @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
+ Implicit scope: Either
+ "application" a shared library (BASIC)
+ "share" a library of 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
+ """
+
+ # 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:
+ # Check ParamArray arguments
+ 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 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)
+ else: # Basic
+ if len(scope) == 0:
+ scope = 'application' # Default for Basic
+ lib = ''
+ 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)
+ # Get the script object
+ fullscript = ('@' if paramarray else '') + scope + ':' + script
+ try:
+ xscript = cls.scriptprovider.getScript(uri)
+ except Exception:
+ raise RuntimeError(
+ 'The script \'{0}\' could not be located in your LibreOffice installation'.format(script))
+ else: # Should not happen
+ return None
+
+ # At 1st execution of the common Basic dispatcher, buffer xscript
+ if fullscript == ScriptForge.basicdispatcher and cls.servicesdispatcher is None:
+ cls.servicesdispatcher = xscript
+
+ # Execute the script with the given arguments
+ # Packaging for script provider depends on presence of ParamArray arguments in the called Basic script
+ if paramarray:
+ scriptreturn = xscript.invoke(args[0], (), ())
+ else:
+ scriptreturn = xscript.invoke(args, (), ())
+
+ #
+ return scriptreturn[0] # Updatable arguments passed by reference are ignored
+
+ @classmethod
+ def InvokeBasicService(cls, basicobject, flags, method, *args):
+ """
+ Execute a given Basic script and interpret its result
+ This method has as counterpart the ScriptForge.SF_PythonHelper._PythonDispatcher() Basic method
+ :param basicobject: a Service subclass
+ :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
+ Additionally, when [0] is a tuple:
+ [2] Number of dimensions in Basic
+ 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
+ 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
+ """
+ # Constants
+ script = ScriptForge.basicdispatcher
+ cstNoArgs = '+++NOARGS+++'
+ cstValue, cstVarType, cstDims, cstClass, cstType, cstService, cstName = 0, 1, 2, 2, 3, 4, 5
+
+ #
+ # Run the basic script
+ # The targeted script has a ParamArray argument. Do not change next 4 lines except if you know what you do !
+ if len(args) == 0:
+ args = (basicobject,) + (flags,) + (method,) + (cstNoArgs,)
+ else:
+ args = (basicobject,) + (flags,) + (method,) + args
+ returntuple = cls.InvokeSimpleScript(script, args)
+ #
+ # Interpret the result
+ # Did an error occur in the Basic world ?
+ if not isinstance(returntuple, (tuple, list)):
+ 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
+ if returntuple[cstClass] == ScriptForge.objUNO:
+ pass
+ else:
+ # Create the new class instance of the right subclass of SFServices()
+ servname = returntuple[cstService]
+ if servname not in cls.serviceslist:
+ # When service not found
+ 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])
+ elif returntuple[cstVarType] >= ScriptForge.V_ARRAY:
+ # Intercept empty array
+ if isinstance(returntuple[cstValue], uno.ByteSequence):
+ return ()
+ elif returntuple[cstVarType] == ScriptForge.V_DATE:
+ dat = SFScriptForge.SF_Basic.CDateFromUnoDateTime(returntuple[cstValue])
+ return dat
+ else: # All other scalar values
+ pass
+ return returntuple[cstValue]
+
+ @staticmethod
+ def SetAttributeSynonyms():
+ """
+ A synonym of an attribute is either the lowercase or the camelCase form of its original ProperCase name.
+ In every subclass of SFServices:
+ 1) Fill the propertysynonyms dictionary with the synonyms of the properties listed in serviceproperties
+ Example:
+ serviceproperties = dict(ConfigFolder = False, InstallFolder = False)
+ propertysynonyms = dict(configfolder = 'ConfigFolder', installfolder = 'InstallFolder',
+ configFolder = 'ConfigFolder', installFolder = 'InstallFolder')
+ 2) Define new method attributes synonyms of the original methods
+ Example:
+ def CopyFile(...):
+ # etc ...
+ copyFile, copyfile = CopyFile, CopyFile
+ """
+ def camelCase(key):
+ return key[0].lower() + key[1:]
+
+ for cls in SFServices.__subclasses__():
+ # 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.update(cc)
+ setattr(cls, 'propertysynonyms', dicosyn)
+ # Synonyms of methods. A method is a public callable attribute
+ methods = [method for method in dir(cls) if not method.startswith('_')]
+ for method in methods:
+ func = getattr(cls, method)
+ if callable(func):
+ # Assign to each synonym a reference to the original method
+ lc = method.lower()
+ setattr(cls, lc, func)
+ cc = camelCase(method)
+ if cc != lc:
+ setattr(cls, cc, func)
+ return
+
+ @staticmethod
+ def unpack_args(kwargs):
+ """
+ Convert a dictionary passed as argument to a list alternating keys and values
+ Example:
+ dict(A = 'a', B = 2) => 'A', 'a', 'B', 2
+ """
+ return [v for p in zip(list(kwargs.keys()), list(kwargs.values())) for v in p]
+
+
+# #####################################################################################################################
+# SFServices CLASS (ScriptForge services superclass) ###
+# #####################################################################################################################
+
+class SFServices(object):
+ """
+ Generic implementation of a parent Service class
+ Every service must subclass this class to be recognized as a valid service
+ A service instance is created by the CreateScriptService method
+ It can have a mirror in the Basic world or be totally defined in Python
+
+ Every subclass must initialize 3 class properties:
+ servicename (e.g. 'ScriptForge.FileSystem', 'ScriptForge.Basic')
+ servicesynonyms (e.g. 'FileSystem', 'Basic')
+ serviceimplementation: either 'python' or 'basic'
+ This is sufficient to register the service in the Python world
+
+ The communication with Basic is managed by 2 ScriptForge() methods:
+ InvokeSimpleScript(): low level invocation of a Basic script. This script must be located
+ in a usual Basic module. The result is passed as-is
+ InvokeBasicService(): the result comes back encapsulated with additional info
+ The result is interpreted in the method
+ The invoked script can be a property or a method of a Basic class or usual module
+ It is up to every service method to determine which method to use
+
+ For Basic services only:
+ Each instance is identified by its
+ - object reference: the real Basic object embedded as a UNO wrapper object
+ - object type ('SF_String', 'DICTIONARY', ...)
+ - class module: 1 for usual modules, 2 for class modules
+ - name (form, control, ... name) - may be blank
+
+ The role of the SFServices() superclass is mainly to propose a generic properties management
+ Properties are got and set following next strategy:
+ 1. Property names are controlled strictly ('Value' or 'value', not 'VALUE')
+ 2. Getting a property value for the first time is always done via a Basic call
+ 3. Next occurrences are fetched from the Python dictionary of the instance if the property
+ is read-only, otherwise via a Basic call
+ 4. Read-only properties may be modified or deleted exceptionally by the class
+ when self.internal == True. The latter must immediately be reset after use
+
+ Each subclass must define its interface with the user scripts:
+ 1. The properties
+ Property names are proper-cased
+ 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
+ Otherwise set it to True to force a recomputation at each property getter invocation
+ If there is a need to handle a specific property in a specific manner:
+ @property
+ def myProperty(self):
+ return self.GetProperty('myProperty')
+ 2 The methods
+ a usual def: statement
+ def myMethod(self, arg1, arg2 = ''):
+ return self.Execute(self.vbMethod, 'myMethod', arg1, arg2)
+ Method names are proper-cased, arguments are lower-cased
+ Conventionally, camel-cased and lower-cased homonyms are supported where relevant
+ All arguments must be present and initialized before the call to Basic, if any
+ """
+ # 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
+ 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
+ flgArrayRet = 1024 # Invoked service method can return a 2D array (standard modules) or any array (class modules)
+ flgUno = 256 # Invoked service method/property can return a UNO object
+ flgObject = 2048 # 1st argument may be a Basic object
+ flgHardCode = 4096 # Force hardcoded call to method, avoid CallByName()
+ # Basic class type
+ moduleClass, moduleStandard = 2, 1
+ #
+ # Define the default behaviour for read-only properties: buffer their values in Python
+ forceGetProperty = False
+ # Empty dictionary for lower/camelcased homonyms or properties
+ propertysynonyms = {}
+ # To operate dynamic property getting/setting it is necessary to
+ # enumerate all types of properties and adapt __getattr__() and __setattr__() according to their type
+ internal_attributes = ('objectreference', 'objecttype', 'name', 'internal', 'servicename',
+ 'serviceimplementation', 'classmodule', 'EXEC', 'SIMPLEEXEC')
+ # Shortcuts to script provider interfaces
+ SIMPLEEXEC = ScriptForge.InvokeSimpleScript
+ EXEC = ScriptForge.InvokeBasicService
+
+ def __init__(self, reference = -1, objtype = None, classmodule = 0, name = ''):
+ """
+ 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.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):
+ """
+ Executed for EVERY property reference if name not yet in the instance dict
+ At the 1st get, the property value is always got from Basic
+ Due to the use of lower/camelcase synonyms, it is called for each variant of the same property
+ The method manages itself the buffering in __dict__ based on the official ProperCase property name
+ """
+ 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',
+ 'forceGetProperty'):
+ pass
+ elif name in self.serviceproperties:
+ if self.forceGetProperty is False and self.serviceproperties[name] is False: # False = read-only
+ if name in self.__dict__:
+ return self.__dict__[name]
+ else:
+ # Get Property from Basic and store it
+ prop = self.GetProperty(name)
+ self.__dict__[name] = prop
+ return prop
+ 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)
+
+ def __setattr__(self, name, value):
+ """
+ Executed for EVERY property assignment, including in __init__() !!
+ Setting a property requires for serviceproperties() to be executed in Basic
+ 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',
+ 'forceGetProperty'):
+ pass
+ elif name[0:2] == '__' or name in self.internal_attributes or name in self.localProperties:
+ 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
+ name = self.propertysynonyms[name]
+ if self.internal: # internal = True forces property local setting even if property is read-only
+ pass
+ elif self.serviceproperties[name] is True: # True == Editable
+ self.SetProperty(name, value)
+ return
+ else:
+ raise AttributeError(
+ "type object '" + self.objecttype + "' has no editable property '" + name + "'")
+ else:
+ raise AttributeError("type object '" + self.objecttype + "' has no property '" + name + "'")
+ object.__setattr__(self, name, value)
+ return
+
+ def __repr__(self):
+ return self.serviceimplementation + '/' + self.servicename + '/' + str(self.objectreference) + '/' + \
+ super(SFServices, self).__repr__()
+
+ 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.objectreference = -1
+
+ def ExecMethod(self, flags = 0, methodname = '', *args):
+ if flags == 0:
+ flags = self.vbMethod
+ if len(methodname) > 0:
+ return self.EXEC(self.objectreference, flags, methodname, *args)
+
+ def GetProperty(self, propertyname, arg = None):
+ """
+ Get the given property from the Basic world
+ """
+ if self.serviceimplementation == 'basic':
+ # Conventionally properties starting with X (and only them) may return a UNO 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
+ return self.EXEC(self.objectreference, calltype, propertyname, arg)
+ return None
+
+ def Properties(self):
+ return list(self.serviceproperties)
+
+ def basicmethods(self):
+ if self.serviceimplementation == 'basic':
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Methods')
+ else:
+ return []
+
+ def basicproperties(self):
+ if self.serviceimplementation == 'basic':
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Properties')
+ else:
+ return []
+
+ def SetProperty(self, propertyname, value):
+ """
+ Set the given property to a new value in the Basic world
+ """
+ if self.serviceimplementation == 'basic':
+ flag = self.vbLet
+ if isinstance(value, datetime.datetime):
+ value = SFScriptForge.SF_Basic.CDateToUnoDateTime(value)
+ flag += self.flgDateArg
+ if repr(type(value)) == "<class 'pyuno'>":
+ flag += self.flgUno
+ return self.EXEC(self.objectreference, flag, propertyname, value)
+
+
+# #####################################################################################################################
+# SFScriptForge CLASS (alias of ScriptForge Basic library) ###
+# #####################################################################################################################
+class SFScriptForge:
+ pass
+
+ # #########################################################################
+ # SF_Array CLASS
+ # #########################################################################
+ class SF_Array(SFServices, metaclass = _Singleton):
+ """
+ Provides a collection of methods for manipulating and transforming arrays of one dimension (vectors)
+ and arrays of two dimensions (matrices). This includes set operations, sorting,
+ importing to and exporting from text files.
+ The Python version of the service provides a single method: ImportFromCSVFile
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.Array'
+ servicesynonyms = ('array', 'scriptforge.array')
+ serviceproperties = dict()
+
+ def ImportFromCSVFile(self, filename, delimiter = ',', dateformat = ''):
+ """
+ Difference with the Basic version: dates are returned in their iso format,
+ not as any of the datetime objects.
+ """
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'ImportFromCSVFile',
+ filename, delimiter, dateformat)
+
+ # #########################################################################
+ # SF_Basic CLASS
+ # #########################################################################
+ class SF_Basic(SFServices, metaclass = _Singleton):
+ """
+ This service proposes a collection of Basic methods to be executed in a Python context
+ simulating the exact syntax and behaviour of the identical Basic builtin method.
+ Typical example:
+ SF_Basic.MsgBox('This has to be displayed in a message box')
+
+ The signatures of Basic builtin functions are derived from
+ core/basic/source/runtime/stdobj.cxx
+
+ Detailed user documentation:
+ https://help.libreoffice.org/latest/en-US/text/sbasic/shared/03/sf_basic.html?DbPAR=BASIC
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'python'
+ servicename = 'ScriptForge.Basic'
+ servicesynonyms = ('basic', 'scriptforge.basic')
+ # Basic helper functions invocation
+ module = 'SF_PythonHelper'
+ # Message box constants
+ MB_ABORTRETRYIGNORE, MB_DEFBUTTON1, MB_DEFBUTTON2, MB_DEFBUTTON3 = 2, 128, 256, 512
+ MB_ICONEXCLAMATION, MB_ICONINFORMATION, MB_ICONQUESTION, MB_ICONSTOP = 48, 64, 32, 16
+ MB_OK, MB_OKCANCEL, MB_RETRYCANCEL, MB_YESNO, MB_YESNOCANCEL = 0, 1, 5, 4, 3
+ IDABORT, IDCANCEL, IDIGNORE, IDNO, IDOK, IDRETRY, IDYES = 3, 2, 5, 7, 1, 4, 6
+
+ @classmethod
+ def CDate(cls, datevalue):
+ cdate = cls.SIMPLEEXEC(cls.module + '.PyCDate', datevalue)
+ return cls.CDateFromUnoDateTime(cdate)
+
+ @staticmethod
+ def CDateFromUnoDateTime(unodate):
+ """
+ Converts a UNO date/time representation to a datetime.datetime Python native object
+ :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
+ datetype = repr(type(unodate))
+ if 'com.sun.star.util.DateTime' in datetype:
+ if 1900 <= unodate.Year <= datetime.MAXYEAR:
+ date = datetime.datetime(unodate.Year, unodate.Month, unodate.Day, unodate.Hours,
+ unodate.Minutes, unodate.Seconds, int(unodate.NanoSeconds / 1000))
+ elif 'com.sun.star.util.Date' in datetype:
+ if 1900 <= unodate.Year <= datetime.MAXYEAR:
+ date = datetime.datetime(unodate.Year, unodate.Month, unodate.Day)
+ elif 'com.sun.star.util.Time' in datetype:
+ date = datetime.datetime(unodate.Hours, unodate.Minutes, unodate.Seconds,
+ int(unodate.NanoSeconds / 1000))
+ else:
+ return unodate # Not recognized as a UNO date structure
+ return date
+
+ @staticmethod
+ def CDateToUnoDateTime(date):
+ """
+ Converts a date representation into the ccom.sun.star.util.DateTime date format
+ Acceptable boundaries: year >= 1900 and <= 32767
+ :param date: datetime.datetime, datetime.date, datetime.time, float (time.time) or time.struct_time
+ :return: a com.sun.star.util.DateTime
+ """
+ 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
+
+ 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 =\
+ date[0:6]
+ 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)):
+ if 1900 <= date.year <= 32767:
+ unodate.Year, unodate.Month, unodate.Day = date.year, date.month, date.day
+ if isinstance(date, (datetime.datetime, datetime.time)):
+ 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 unodate
+
+ @classmethod
+ def ConvertFromUrl(cls, url):
+ return cls.SIMPLEEXEC(cls.module + '.PyConvertFromUrl', url)
+
+ @classmethod
+ def ConvertToUrl(cls, systempath):
+ return cls.SIMPLEEXEC(cls.module + '.PyConvertToUrl', systempath)
+
+ @classmethod
+ def CreateUnoService(cls, servicename):
+ return cls.SIMPLEEXEC(cls.module + '.PyCreateUnoService', servicename)
+
+ @classmethod
+ def DateAdd(cls, interval, number, date):
+ if isinstance(date, datetime.datetime):
+ date = cls.CDateToUnoDateTime(date)
+ dateadd = cls.SIMPLEEXEC(cls.module + '.PyDateAdd', interval, number, date)
+ return cls.CDateFromUnoDateTime(dateadd)
+
+ @classmethod
+ def DateDiff(cls, interval, date1, date2, firstdayofweek = 1, firstweekofyear = 1):
+ if isinstance(date1, datetime.datetime):
+ date1 = cls.CDateToUnoDateTime(date1)
+ if isinstance(date2, datetime.datetime):
+ date2 = cls.CDateToUnoDateTime(date2)
+ return cls.SIMPLEEXEC(cls.module + '.PyDateDiff', interval, date1, date2, firstdayofweek, firstweekofyear)
+
+ @classmethod
+ def DatePart(cls, interval, date, firstdayofweek = 1, firstweekofyear = 1):
+ if isinstance(date, datetime.datetime):
+ date = cls.CDateToUnoDateTime(date)
+ return cls.SIMPLEEXEC(cls.module + '.PyDatePart', interval, date, firstdayofweek, firstweekofyear)
+
+ @classmethod
+ def DateValue(cls, string):
+ if isinstance(string, datetime.datetime):
+ string = string.isoformat()
+ datevalue = cls.SIMPLEEXEC(cls.module + '.PyDateValue', string)
+ return cls.CDateFromUnoDateTime(datevalue)
+
+ @classmethod
+ def Format(cls, expression, format = ''):
+ if isinstance(expression, datetime.datetime):
+ expression = cls.CDateToUnoDateTime(expression)
+ return cls.SIMPLEEXEC(cls.module + '.PyFormat', expression, format)
+
+ @classmethod
+ def GetDefaultContext(cls):
+ return ScriptForge.componentcontext
+
+ @classmethod
+ def GetGuiType(cls):
+ return cls.SIMPLEEXEC(cls.module + '.PyGetGuiType')
+
+ @classmethod
+ def GetPathSeparator(cls):
+ return os.sep
+
+ @classmethod
+ def GetSystemTicks(cls):
+ return cls.SIMPLEEXEC(cls.module + '.PyGetSystemTicks')
+
+ class GlobalScope(object, metaclass = _Singleton):
+ @classmethod # Mandatory because the GlobalScope class is normally not instantiated
+ def BasicLibraries(cls):
+ return ScriptForge.InvokeSimpleScript(SFScriptForge.SF_Basic.module + '.PyGlobalScope', 'Basic')
+
+ @classmethod
+ def DialogLibraries(cls):
+ return ScriptForge.InvokeSimpleScript(SFScriptForge.SF_Basic.module + '.PyGlobalScope', 'Dialog')
+
+ @classmethod
+ def InputBox(cls, prompt, title = '', default = '', xpostwips = -1, ypostwips = -1):
+ if xpostwips < 0 or ypostwips < 0:
+ return cls.SIMPLEEXEC(cls.module + '.PyInputBox', prompt, title, default)
+ return cls.SIMPLEEXEC(cls.module + '.PyInputBox', prompt, title, default, xpostwips, ypostwips)
+
+ @classmethod
+ def MsgBox(cls, prompt, buttons = 0, title = ''):
+ return cls.SIMPLEEXEC(cls.module + '.PyMsgBox', prompt, buttons, title)
+
+ @classmethod
+ def Now(cls):
+ return datetime.datetime.now()
+
+ @classmethod
+ def RGB(cls, red, green, blue):
+ return int('%02x%02x%02x' % (red, green, blue), 16)
+
+ @property
+ def StarDesktop(self):
+ ctx = ScriptForge.componentcontext
+ if ctx is None:
+ return None
+ smgr = ctx.getServiceManager() # com.sun.star.lang.XMultiComponentFactory
+ DESK = 'com.sun.star.frame.Desktop'
+ desktop = smgr.createInstanceWithContext(DESK, ctx)
+ return desktop
+ starDesktop, stardesktop = StarDesktop, StarDesktop
+
+ @property
+ def ThisComponent(self):
+ """
+ When the current component is the Basic IDE, the ThisComponent object returns
+ in Basic the component owning the currently run user script.
+ Above behaviour cannot be reproduced in Python.
+ :return: the current component or None when not a document
+ """
+ comp = self.StarDesktop.getCurrentComponent()
+ if comp is None:
+ 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 comp
+ thisComponent, thiscomponent = ThisComponent, ThisComponent
+
+ @property
+ def ThisDatabaseDocument(self):
+ """
+ When the current component is the Basic IDE, the ThisDatabaseDocument object returns
+ in Basic the database owning the currently run user script.
+ 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
+ if comp is None:
+ return None
+ #
+ sess = CreateScriptService('Session')
+ impl, ident = '', ''
+ if sess.HasUnoProperty(comp, 'ImplementationName'):
+ impl = comp.ImplementationName
+ if sess.HasUnoProperty(comp, 'Identifier'):
+ ident = comp.Identifier
+ #
+ targetimpl = 'com.sun.star.comp.dba.ODatabaseDocument'
+ if impl == targetimpl: # The current component is the main Base window
+ return comp
+ # Identify resp. form, table/query, table/query in edit mode, report, relations diagram
+ if impl == 'SwXTextDocument' and ident == 'com.sun.star.sdb.FormDesign' \
+ or impl == 'org.openoffice.comp.dbu.ODatasourceBrowser' \
+ or impl in ('org.openoffice.comp.dbu.OTableDesign', 'org.openoffice.comp.dbu.OQuertDesign') \
+ or impl == 'SwXTextDocument' and ident == 'com.sun.star.sdb.TextReportDesign' \
+ or impl == 'org.openoffice.comp.dbu.ORelationDesign':
+ db = comp.ScriptContainer
+ if sess.HasUnoProperty(db, 'ImplementationName'):
+ if db.ImplementationName == targetimpl:
+ return db
+ return None
+ thisDatabaseDocument, thisdatabasedocument = ThisDatabaseDocument, ThisDatabaseDocument
+
+ @classmethod
+ def Xray(cls, unoobject = None):
+ return cls.SIMPLEEXEC('XrayTool._main.xray', unoobject)
+
+ # #########################################################################
+ # SF_Dictionary CLASS
+ # #########################################################################
+ class SF_Dictionary(SFServices, dict):
+ """
+ The service adds to a Python dict instance the interfaces for conversion to and from
+ a list of UNO PropertyValues
+
+ Usage:
+ dico = dict(A = 1, B = 2, C = 3)
+ myDict = CreateScriptService('Dictionary', dico) # Initialize myDict with the content of dico
+ myDict['D'] = 4
+ print(myDict) # {'A': 1, 'B': 2, 'C': 3, 'D': 4}
+ propval = myDict.ConvertToPropertyValues()
+ or
+ dico = dict(A = 1, B = 2, C = 3)
+ myDict = CreateScriptService('Dictionary') # Initialize myDict as an empty dict object
+ myDict.update(dico) # Load the values of dico into myDict
+ myDict['D'] = 4
+ print(myDict) # {'A': 1, 'B': 2, 'C': 3, 'D': 4}
+ propval = myDict.ConvertToPropertyValues()
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'python'
+ servicename = 'ScriptForge.Dictionary'
+ servicesynonyms = ('dictionary', 'scriptforge.dictionary')
+
+ def __init__(self, dic = None):
+ SFServices.__init__(self)
+ dict.__init__(self)
+ if dic is not None:
+ self.update(dic)
+
+ def ConvertToPropertyValues(self):
+ """
+ Store the content of the dictionary in an array of PropertyValues.
+ Each entry in the array is a com.sun.star.beans.PropertyValue.
+ he key is stored in Name, the value is stored in Value.
+
+ If one of the items has a type datetime, it is converted to a com.sun.star.util.DateTime structure.
+ If one of the items is an empty list, it is converted to None.
+
+ The resulting array is empty when the dictionary is empty.
+ """
+ result = []
+ for key in iter(self):
+ value = self[key]
+ item = value
+ if isinstance(value, dict): # check that first level is not itself a (sub)dict
+ item = None
+ elif isinstance(value, (tuple, list)): # check every member of the list is not a (sub)dict
+ if len(value) == 0: # Property values do not like empty lists
+ value = None
+ else:
+ for i in range(len(value)):
+ if isinstance(value[i], dict):
+ value[i] = None
+ item = value
+ elif isinstance(value, (datetime.datetime, datetime.date, datetime.time)):
+ item = SFScriptForge.SF_Basic.CDateToUnoDateTime(value)
+ pv = uno.createUnoStruct('com.sun.star.beans.PropertyValue')
+ pv.Name = key
+ pv.Value = item
+ result.append(pv)
+ return result
+
+ def ImportFromPropertyValues(self, propertyvalues, overwrite = False):
+ """
+ Inserts the contents of an array of PropertyValue objects into the current dictionary.
+ PropertyValue Names are used as keys in the dictionary, whereas Values contain the corresponding values.
+ Date-type values are converted to datetime.datetime instances.
+ :param propertyvalues: a list.tuple containing com.sun.star.beans.PropertyValue objects
+ :param overwrite: When True, entries with same name may exist in the dictionary and their values
+ are overwritten. When False (default), repeated keys are not overwritten.
+ :return: True when successful
+ """
+ result = []
+ for pv in iter(propertyvalues):
+ key = pv.Name
+ if overwrite is True or key not in self:
+ item = pv.Value
+ if 'com.sun.star.util.DateTime' in repr(type(item)):
+ item = datetime.datetime(item.Year, item.Month, item.Day,
+ item.Hours, item.Minutes, item.Seconds, int(item.NanoSeconds / 1000))
+ elif 'com.sun.star.util.Date' in repr(type(item)):
+ item = datetime.datetime(item.Year, item.Month, item.Day)
+ elif 'com.sun.star.util.Time' in repr(type(item)):
+ item = datetime.datetime(item.Hours, item.Minutes, item.Seconds, int(item.NanoSeconds / 1000))
+ result.append((key, item))
+ self.update(result)
+ return True
+
+ # #########################################################################
+ # SF_Exception CLASS
+ # #########################################################################
+ class SF_Exception(SFServices, metaclass = _Singleton):
+ """
+ The Exception service is a collection of methods for code debugging and error handling.
+
+ The Exception service console stores events, variable values and information about errors.
+ Use the console when the Python shell is not available, for example in Calc user defined functions (UDF)
+ or during events processing.
+ Use DebugPrint() method to aggregate additional user data of any type.
+
+ Console entries can be dumped to a text file or visualized in a dialogue.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.Exception'
+ servicesynonyms = ('exception', 'scriptforge.exception')
+ serviceproperties = dict()
+
+ def Console(self, modal = True):
+ # From Python, the current XComponentContext must be added as last argument
+ return self.ExecMethod(self.vbMethod, 'Console', modal, ScriptForge.componentcontext)
+
+ def ConsoleClear(self, keep = 0):
+ return self.ExecMethod(self.vbMethod, 'ConsoleClear', keep)
+
+ def ConsoleToFile(self, filename):
+ return self.ExecMethod(self.vbMethod, 'ConsoleToFile', filename)
+
+ def DebugDisplay(self, *args):
+ # Arguments are concatenated in a single string similar to what the Python print() function would produce
+ self.DebugPrint(*args)
+ param = '\n'.join(list(map(lambda a: a.strip("'") if isinstance(a, str) else repr(a), args)))
+ bas = CreateScriptService('ScriptForge.Basic')
+ return bas.MsgBox(param, bas.MB_OK + bas.MB_ICONINFORMATION, 'DebugDisplay')
+
+ def DebugPrint(self, *args):
+ # Arguments are concatenated in a single string similar to what the Python print() function would produce
+ # Avoid using repr() on strings to not have backslashes * 4
+ param = '\t'.join(list(map(lambda a: a.strip("'") if isinstance(a, str) else repr(a),
+ args))).expandtabs(tabsize = 4)
+ return self.ExecMethod(self.vbMethod, 'DebugPrint', param)
+
+ @classmethod
+ def PythonShell(cls, variables = None):
+ """
+ Open an APSO python shell window - Thanks to its authors Hanya/Tsutomu Uchino/Hubert Lambert
+ :param variables: Typical use
+ PythonShell.({**globals(), **locals()})
+ to push the global and local dictionaries to the shell window
+ """
+ if variables is None:
+ variables = locals()
+ # Is APSO installed ?
+ ctx = ScriptForge.componentcontext
+ ext = ctx.getByName('/singletons/com.sun.star.deployment.PackageInformationProvider')
+ apso = 'apso.python.script.organizer'
+ if len(ext.getPackageLocation(apso)) > 0:
+ # Directly derived from apso.oxt|python|scripts|tools.py$console
+ # we need to load apso before import statement
+ ctx.ServiceManager.createInstance('apso.python.script.organizer.impl')
+ # now we can use apso_utils library
+ from apso_utils import console
+ kwargs = {'loc': variables}
+ kwargs['loc'].setdefault('XSCRIPTCONTEXT', uno)
+ console(**kwargs)
+ # An interprocess call is necessary to allow a redirection of STDOUT and STDERR by APSO
+ # Choice is a minimalist call to a Basic routine: no arguments, a few lines of code
+ SFScriptForge.SF_Basic.GetGuiType()
+ else:
+ # The APSO extension could not be located in your LibreOffice installation
+ cls._RaiseFatal('SF_Exception.PythonShell', 'variables=None', 'PYTHONSHELLERROR')
+
+ @classmethod
+ def RaiseFatal(cls, errorcode, *args):
+ """
+ Generate a run-time error caused by an anomaly in a user script detected by ScriptForge
+ The message is logged in the console. The execution is STOPPED
+ For INTERNAL USE only
+ """
+ # 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
+
+ @classmethod
+ def _RaiseFatal(cls, sub, subargs, errorcode, *args):
+ """
+ Wrapper of RaiseFatal(). Includes method and syntax of the failed Python routine
+ to simulate the exact behaviour of the Basic RaiseFatal() method
+ For INTERNAL USE only
+ """
+ ScriptForge.InvokeSimpleScript('ScriptForge.SF_Utils._EnterFunction', sub, subargs)
+ cls.RaiseFatal(errorcode, *args)
+ raise RuntimeError("The execution of the method '" + sub.split('.')[-1] + "' failed. Execution stops.")
+
+ # #########################################################################
+ # SF_FileSystem CLASS
+ # #########################################################################
+ class SF_FileSystem(SFServices, metaclass = _Singleton):
+ """
+ The "FileSystem" service includes common file and folder handling routines.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.FileSystem'
+ servicesynonyms = ('filesystem', 'scriptforge.filesystem')
+ serviceproperties = dict(FileNaming = True, ConfigFolder = False, ExtensionsFolder = False, HomeFolder = False,
+ InstallFolder = False, TemplatesFolder = False, TemporaryFolder = False,
+ UserTemplatesFolder = False)
+ # Force for each property to get its value from Basic - due to FileNaming updatability
+ forceGetProperty = True
+ # Open TextStream constants
+ ForReading, ForWriting, ForAppending = 1, 2, 8
+
+ def BuildPath(self, foldername, name):
+ return self.ExecMethod(self.vbMethod, 'BuildPath', foldername, name)
+
+ def CompareFiles(self, filename1, filename2, comparecontents = False):
+ py = ScriptForge.pythonhelpermodule + '$' + '_SF_FileSystem__CompareFiles'
+ if self.FileExists(filename1) and self.FileExists(filename2):
+ file1 = self._ConvertFromUrl(filename1)
+ file2 = self._ConvertFromUrl(filename2)
+ return self.SIMPLEEXEC(py, file1, file2, comparecontents)
+ else:
+ return False
+
+ def CopyFile(self, source, destination, overwrite = True):
+ return self.ExecMethod(self.vbMethod, 'CopyFile', source, destination, overwrite)
+
+ def CopyFolder(self, source, destination, overwrite = True):
+ return self.ExecMethod(self.vbMethod, 'CopyFolder', source, destination, overwrite)
+
+ def CreateFolder(self, foldername):
+ return self.ExecMethod(self.vbMethod, 'CreateFolder', foldername)
+
+ def CreateTextFile(self, filename, overwrite = True, encoding = 'UTF-8'):
+ return self.ExecMethod(self.vbMethod, 'CreateTextFile', filename, overwrite, encoding)
+
+ def DeleteFile(self, filename):
+ return self.ExecMethod(self.vbMethod, 'DeleteFile', filename)
+
+ def DeleteFolder(self, foldername):
+ return self.ExecMethod(self.vbMethod, 'DeleteFolder', foldername)
+
+ def ExtensionFolder(self, extension):
+ return self.ExecMethod(self.vbMethod, 'ExtensionFolder', extension)
+
+ def FileExists(self, filename):
+ return self.ExecMethod(self.vbMethod, 'FileExists', filename)
+
+ def Files(self, foldername, filter = ''):
+ return self.ExecMethod(self.vbMethod, 'Files', foldername, filter)
+
+ def FolderExists(self, foldername):
+ return self.ExecMethod(self.vbMethod, 'FolderExists', foldername)
+
+ def GetBaseName(self, filename):
+ return self.ExecMethod(self.vbMethod, 'GetBaseName', filename)
+
+ def GetExtension(self, filename):
+ return self.ExecMethod(self.vbMethod, 'GetExtension', filename)
+
+ def GetFileLen(self, filename):
+ py = ScriptForge.pythonhelpermodule + '$' + '_SF_FileSystem__GetFilelen'
+ if self.FileExists(filename):
+ file = self._ConvertFromUrl(filename)
+ return int(self.SIMPLEEXEC(py, file))
+ else:
+ return 0
+
+ def GetFileModified(self, filename):
+ return self.ExecMethod(self.vbMethod + self.flgDateRet, 'GetFileModified', filename)
+
+ def GetName(self, filename):
+ return self.ExecMethod(self.vbMethod, 'GetName', filename)
+
+ def GetParentFolderName(self, filename):
+ return self.ExecMethod(self.vbMethod, 'GetParentFolderName', filename)
+
+ def GetTempName(self):
+ return self.ExecMethod(self.vbMethod, 'GetTempName')
+
+ def HashFile(self, filename, algorithm):
+ py = ScriptForge.pythonhelpermodule + '$' + '_SF_FileSystem__HashFile'
+ if self.FileExists(filename):
+ file = self._ConvertFromUrl(filename)
+ return self.SIMPLEEXEC(py, file, algorithm.lower())
+ else:
+ return ''
+
+ def MoveFile(self, source, destination):
+ return self.ExecMethod(self.vbMethod, 'MoveFile', source, destination)
+
+ def MoveFolder(self, source, destination):
+ return self.ExecMethod(self.vbMethod, 'MoveFolder', source, destination)
+
+ def OpenTextFile(self, filename, iomode = 1, create = False, encoding = 'UTF-8'):
+ return self.ExecMethod(self.vbMethod, 'OpenTextFile', filename, iomode, create, encoding)
+
+ def PickFile(self, defaultfile = ScriptForge.cstSymEmpty, mode = 'OPEN', filter = ''):
+ return self.ExecMethod(self.vbMethod, 'PickFile', defaultfile, mode, filter)
+
+ def PickFolder(self, defaultfolder = ScriptForge.cstSymEmpty, freetext = ''):
+ return self.ExecMethod(self.vbMethod, 'PickFolder', defaultfolder, freetext)
+
+ def SubFolders(self, foldername, filter = ''):
+ return self.ExecMethod(self.vbMethod, 'SubFolders', foldername, filter)
+
+ @classmethod
+ def _ConvertFromUrl(cls, filename):
+ # Alias for same function in FileSystem Basic module
+ return cls.SIMPLEEXEC('ScriptForge.SF_FileSystem._ConvertFromUrl', filename)
+
+ # #########################################################################
+ # SF_L10N CLASS
+ # #########################################################################
+ class SF_L10N(SFServices):
+ """
+ This service provides a number of methods related to the translation of strings
+ with minimal impact on the program's source code.
+ The methods provided by the L10N service can be used mainly to:
+ Create POT files that can be used as templates for translation of all strings in the program.
+ Get translated strings at runtime for the language defined in the Locale property.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.L10N'
+ servicesynonyms = ('l10n', 'scriptforge.l10n')
+ serviceproperties = dict(Folder = False, Languages = False, Locale = False)
+
+ @classmethod
+ def ReviewServiceArgs(cls, foldername = '', locale = '', encoding = 'UTF-8',
+ locale2 = '', encoding2 = 'UTF-8'):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return foldername, locale, encoding, locale2, encoding2
+
+ def AddText(self, context = '', msgid = '', comment = ''):
+ return self.ExecMethod(self.vbMethod, 'AddText', context, msgid, comment)
+
+ def AddTextsFromDialog(self, dialog):
+ 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'):
+ return self.ExecMethod(self.vbMethod, 'ExportToPOTFile', filename, header, encoding)
+
+ def GetText(self, msgid, *args):
+ return self.ExecMethod(self.vbMethod, 'GetText', msgid, *args)
+ _ = GetText
+
+ # #########################################################################
+ # SF_Platform CLASS
+ # #########################################################################
+ class SF_Platform(SFServices, metaclass = _Singleton):
+ """
+ The 'Platform' service implements a collection of properties about the actual execution environment
+ and context :
+ the hardware platform
+ the operating system
+ the LibreOffice version
+ the current user
+ All those properties are read-only.
+ The implementation is mainly based on the 'platform' module of the Python standard library
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.Platform'
+ servicesynonyms = ('platform', 'scriptforge.platform')
+ serviceproperties = dict(Architecture = False, ComputerName = False, CPUCount = False, CurrentUser = False,
+ Extensions = False, FilterNames = False, Fonts = False, FormatLocale = False,
+ Locale = False, Machine = False, OfficeLocale = False, OfficeVersion = False,
+ OSName = False, OSPlatform = False, OSRelease = False, OSVersion = False,
+ Printers = False, Processor = False, PythonVersion = False, SystemLocale = False)
+ # Python helper functions
+ py = ScriptForge.pythonhelpermodule + '$' + '_SF_Platform'
+
+ @property
+ def Architecture(self):
+ return self.SIMPLEEXEC(self.py, 'Architecture')
+
+ @property
+ def ComputerName(self):
+ return self.SIMPLEEXEC(self.py, 'ComputerName')
+
+ @property
+ def CPUCount(self):
+ return self.SIMPLEEXEC(self.py, 'CPUCount')
+
+ @property
+ def CurrentUser(self):
+ return self.SIMPLEEXEC(self.py, 'CurrentUser')
+
+ @property
+ def Machine(self):
+ return self.SIMPLEEXEC(self.py, 'Machine')
+
+ @property
+ def OSName(self):
+ return self.SIMPLEEXEC(self.py, 'OSName')
+
+ @property
+ def OSPlatform(self):
+ return self.SIMPLEEXEC(self.py, 'OSPlatform')
+
+ @property
+ def OSRelease(self):
+ return self.SIMPLEEXEC(self.py, 'OSRelease')
+
+ @property
+ def OSVersion(self):
+ return self.SIMPLEEXEC(self.py, 'OSVersion')
+
+ @property
+ def Processor(self):
+ return self.SIMPLEEXEC(self.py, 'Processor')
+
+ @property
+ def PythonVersion(self):
+ return self.SIMPLEEXEC(self.py, 'PythonVersion')
+
+ # #########################################################################
+ # SF_Region CLASS
+ # #########################################################################
+ class SF_Region(SFServices, metaclass = _Singleton):
+ """
+ The "Region" service gathers a collection of functions about languages, countries and timezones
+ - Locales
+ - Currencies
+ - Numbers and dates formatting
+ - Calendars
+ - Timezones conversions
+ - Numbers transformed to text
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.Region'
+ servicesynonyms = ('region', 'scriptforge.region')
+ serviceproperties = dict()
+
+ # Next functions are implemented in Basic as read-only properties with 1 argument
+ def Country(self, region = ''):
+ return self.GetProperty('Country', region)
+
+ def Currency(self, region = ''):
+ return self.GetProperty('Currency', region)
+
+ def DatePatterns(self, region = ''):
+ return self.GetProperty('DatePatterns', region)
+
+ def DateSeparator(self, region = ''):
+ return self.GetProperty('DateSeparator', region)
+
+ def DayAbbrevNames(self, region = ''):
+ return self.GetProperty('DayAbbrevNames', region)
+
+ def DayNames(self, region = ''):
+ return self.GetProperty('DayNames', region)
+
+ def DayNarrowNames(self, region = ''):
+ return self.GetProperty('DayNarrowNames', region)
+
+ def DecimalPoint(self, region = ''):
+ return self.GetProperty('DecimalPoint', region)
+
+ def Language(self, region = ''):
+ return self.GetProperty('Language', region)
+
+ def ListSeparator(self, region = ''):
+ return self.GetProperty('ListSeparator', region)
+
+ def MonthAbbrevNames(self, region = ''):
+ return self.GetProperty('MonthAbbrevNames', region)
+
+ def MonthNames(self, region = ''):
+ return self.GetProperty('MonthNames', region)
+
+ def MonthNarrowNames(self, region = ''):
+ return self.GetProperty('MonthNarrowNames', region)
+
+ def ThousandSeparator(self, region = ''):
+ return self.GetProperty('ThousandSeparator', region)
+
+ def TimeSeparator(self, region = ''):
+ return self.GetProperty('TimeSeparator', region)
+
+ # Usual methods
+ def DSTOffset(self, localdatetime, timezone, locale = ''):
+ if isinstance(localdatetime, datetime.datetime):
+ localdatetime = SFScriptForge.SF_Basic.CDateToUnoDateTime(localdatetime)
+ return self.ExecMethod(self.vbMethod + self.flgDateArg, 'DSTOffset', localdatetime, timezone, locale)
+
+ def LocalDateTime(self, utcdatetime, timezone, locale = ''):
+ if isinstance(utcdatetime, datetime.datetime):
+ utcdatetime = SFScriptForge.SF_Basic.CDateToUnoDateTime(utcdatetime)
+ localdate = self.ExecMethod(self.vbMethod + self.flgDateArg + self.flgDateRet, 'LocalDateTime',
+ utcdatetime, timezone, locale)
+ return SFScriptForge.SF_Basic.CDateFromUnoDateTime(localdate)
+
+ def Number2Text(self, number, locale = ''):
+ return self.ExecMethod(self.vbMethod, 'Number2Text', number, locale)
+
+ def TimeZoneOffset(self, timezone, locale = ''):
+ return self.ExecMethod(self.vbMethod, 'TimeZoneOffset', timezone, locale)
+
+ def UTCDateTime(self, localdatetime, timezone, locale = ''):
+ if isinstance(localdatetime, datetime.datetime):
+ localdatetime = SFScriptForge.SF_Basic.CDateToUnoDateTime(localdatetime)
+ utcdate = self.ExecMethod(self.vbMethod + self.flgDateArg + self.flgDateRet, 'UTCDateTime', localdatetime,
+ timezone, locale)
+ return SFScriptForge.SF_Basic.CDateFromUnoDateTime(utcdate)
+
+ def UTCNow(self, timezone, locale = ''):
+ now = self.ExecMethod(self.vbMethod + self.flgDateRet, 'UTCNow', timezone, locale)
+ return SFScriptForge.SF_Basic.CDateFromUnoDateTime(now)
+
+ # #########################################################################
+ # SF_Session CLASS
+ # #########################################################################
+ class SF_Session(SFServices, metaclass = _Singleton):
+ """
+ The Session service gathers various general-purpose methods about:
+ - UNO introspection
+ - the invocation of external scripts or programs
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.Session'
+ servicesynonyms = ('session', 'scriptforge.session')
+ 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)
+ 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)
+
+ @classmethod
+ def ExecuteBasicScript(cls, scope = '', script = '', *args):
+ if scope is None or scope == '':
+ scope = cls.SCRIPTISAPPLICATION
+ if len(args) == 0:
+ args = (scope,) + (script,) + (None,)
+ else:
+ args = (scope,) + (script,) + args
+ # ExecuteBasicScript method has a ParamArray parameter in Basic
+ return cls.SIMPLEEXEC('@SF_Session.ExecuteBasicScript', args)
+
+ @classmethod
+ def ExecuteCalcFunction(cls, calcfunction, *args):
+ if len(args) == 0:
+ # Arguments of Calc functions are strings or numbers. None == Empty is a good alias for no argument
+ args = (calcfunction,) + (None,)
+ else:
+ args = (calcfunction,) + args
+ # ExecuteCalcFunction method has a ParamArray parameter in Basic
+ return cls.SIMPLEEXEC('@SF_Session.ExecuteCalcFunction', args)
+
+ @classmethod
+ def ExecutePythonScript(cls, scope = '', script = '', *args):
+ return cls.SIMPLEEXEC(scope + '#' + script, *args)
+
+ def HasUnoMethod(self, unoobject, methodname):
+ return self.ExecMethod(self.vbMethod, 'HasUnoMethod', unoobject, methodname)
+
+ def HasUnoProperty(self, unoobject, propertyname):
+ return self.ExecMethod(self.vbMethod, 'HasUnoProperty', unoobject, propertyname)
+
+ @classmethod
+ def OpenURLInBrowser(cls, url):
+ py = ScriptForge.pythonhelpermodule + '$' + '_SF_Session__OpenURLInBrowser'
+ return cls.SIMPLEEXEC(py, url)
+
+ def RunApplication(self, command, parameters):
+ return self.ExecMethod(self.vbMethod, 'RunApplication', command, parameters)
+
+ 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 UnoObjectType(self, unoobject):
+ return self.ExecMethod(self.vbMethod, 'UnoObjectType', unoobject)
+
+ def UnoMethods(self, unoobject):
+ return self.ExecMethod(self.vbMethod, 'UnoMethods', unoobject)
+
+ def UnoProperties(self, unoobject):
+ return self.ExecMethod(self.vbMethod, 'UnoProperties', unoobject)
+
+ def WebService(self, uri):
+ return self.ExecMethod(self.vbMethod, 'WebService', uri)
+
+ # #########################################################################
+ # SF_String CLASS
+ # #########################################################################
+ class SF_String(SFServices, metaclass = _Singleton):
+ """
+ Focus on string manipulation, regular expressions, encodings and hashing algorithms.
+ The methods implemented in Basic that are redundant with Python builtin functions
+ are not duplicated
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.String'
+ servicesynonyms = ('string', 'scriptforge.string')
+ serviceproperties = dict()
+
+ @classmethod
+ def HashStr(cls, inputstr, algorithm):
+ py = ScriptForge.pythonhelpermodule + '$' + '_SF_String__HashStr'
+ return cls.SIMPLEEXEC(py, inputstr, algorithm.lower())
+
+ def IsADate(self, inputstr, dateformat = 'YYYY-MM-DD'):
+ return self.ExecMethod(self.vbMethod, 'IsADate', inputstr, dateformat)
+
+ def IsEmail(self, inputstr):
+ return self.ExecMethod(self.vbMethod, 'IsEmail', inputstr)
+
+ def IsFileName(self, inputstr, osname = ScriptForge.cstSymEmpty):
+ return self.ExecMethod(self.vbMethod, 'IsFileName', inputstr, osname)
+
+ def IsIBAN(self, inputstr):
+ return self.ExecMethod(self.vbMethod, 'IsIBAN', inputstr)
+
+ def IsIPv4(self, inputstr):
+ return self.ExecMethod(self.vbMethod, 'IsIPv4', inputstr)
+
+ def IsLike(self, inputstr, pattern, casesensitive = False):
+ return self.ExecMethod(self.vbMethod, 'IsLike', inputstr, pattern, casesensitive)
+
+ def IsSheetName(self, inputstr):
+ return self.ExecMethod(self.vbMethod, 'IsSheetName', inputstr)
+
+ def IsUrl(self, inputstr):
+ 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)
+
+ def Wrap(self, inputstr, width = 70, tabsize = 8):
+ return self.ExecMethod(self.vbMethod, 'Wrap', inputstr, width, tabsize)
+
+ # #########################################################################
+ # SF_TextStream CLASS
+ # #########################################################################
+ class SF_TextStream(SFServices):
+ """
+ The TextStream service is used to sequentially read from and write to files opened or created
+ using the ScriptForge.FileSystem service..
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.TextStream'
+ servicesynonyms = ()
+ serviceproperties = dict(AtEndOfStream = False, Encoding = False, FileName = False, IOMode = False,
+ Line = False, NewLine = True)
+
+ @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):
+ return self.ExecMethod(self.vbMethod, 'CloseFile')
+
+ def ReadAll(self):
+ return self.ExecMethod(self.vbMethod, 'ReadAll')
+
+ def ReadLine(self):
+ return self.ExecMethod(self.vbMethod, 'ReadLine')
+
+ def SkipLine(self):
+ return self.ExecMethod(self.vbMethod, 'SkipLine')
+
+ def WriteBlankLines(self, lines):
+ return self.ExecMethod(self.vbMethod, 'WriteBlankLines', lines)
+
+ def WriteLine(self, line):
+ return self.ExecMethod(self.vbMethod, 'WriteLine', line)
+
+ # #########################################################################
+ # SF_Timer CLASS
+ # #########################################################################
+ class SF_Timer(SFServices):
+ """
+ The "Timer" service measures the amount of time it takes to run user scripts.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.Timer'
+ servicesynonyms = ('timer', 'scriptforge.timer')
+ serviceproperties = dict(Duration = False, IsStarted = False, IsSuspended = False,
+ SuspendDuration = False, TotalDuration = False)
+ # Force for each property to get its value from Basic
+ forceGetProperty = True
+
+ @classmethod
+ def ReviewServiceArgs(cls, start = False):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return (start,)
+
+ def Continue(self):
+ return self.ExecMethod(self.vbMethod, 'Continue')
+
+ def Restart(self):
+ return self.ExecMethod(self.vbMethod, 'Restart')
+
+ def Start(self):
+ return self.ExecMethod(self.vbMethod, 'Start')
+
+ def Suspend(self):
+ return self.ExecMethod(self.vbMethod, 'Suspend')
+
+ def Terminate(self):
+ return self.ExecMethod(self.vbMethod, 'Terminate')
+
+ # #########################################################################
+ # SF_UI CLASS
+ # #########################################################################
+ class SF_UI(SFServices, metaclass = _Singleton):
+ """
+ Singleton class for the identification and the manipulation of the
+ different windows composing the whole LibreOffice application:
+ - Windows selection
+ - Windows moving and resizing
+ - Statusbar settings
+ - Creation of new windows
+ - Access to the underlying "documents"
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'ScriptForge.UI'
+ servicesynonyms = ('ui', 'scriptforge.ui')
+ serviceproperties = dict(ActiveWindow = False, Height = False, Width = False, X = False, Y = False)
+
+ # Class constants
+ MACROEXECALWAYS, MACROEXECNEVER, MACROEXECNORMAL = 2, 1, 0
+ BASEDOCUMENT, CALCDOCUMENT, DRAWDOCUMENT, IMPRESSDOCUMENT, MATHDOCUMENT, WRITERDOCUMENT = \
+ 'Base', 'Calc', 'Draw', 'Impress', 'Math', 'Writer'
+
+ @property
+ def ActiveWindow(self):
+ return self.ExecMethod(self.vbMethod, 'ActiveWindow')
+ activeWindow, activewindow = ActiveWindow, ActiveWindow
+
+ def Activate(self, windowname = ''):
+ return self.ExecMethod(self.vbMethod, 'Activate', windowname)
+
+ def CreateBaseDocument(self, filename, embeddeddatabase = 'HSQLDB', registrationname = '', calcfilename = ''):
+ return self.ExecMethod(self.vbMethod, 'CreateBaseDocument', filename, embeddeddatabase, registrationname,
+ calcfilename)
+
+ def CreateDocument(self, documenttype = '', templatefile = '', hidden = False):
+ return self.ExecMethod(self.vbMethod, 'CreateDocument', documenttype, templatefile, hidden)
+
+ def Documents(self):
+ return self.ExecMethod(self.vbMethod, 'Documents')
+
+ def GetDocument(self, windowname = ''):
+ return self.ExecMethod(self.vbMethod, 'GetDocument', windowname)
+
+ def Maximize(self, windowname = ''):
+ return self.ExecMethod(self.vbMethod, 'Maximize', windowname)
+
+ def Minimize(self, windowname = ''):
+ return self.ExecMethod(self.vbMethod, 'Minimize', windowname)
+
+ def OpenBaseDocument(self, filename = '', registrationname = '', macroexecution = MACROEXECNORMAL):
+ return self.ExecMethod(self.vbMethod, 'OpenBaseDocument', filename, registrationname, macroexecution)
+
+ def OpenDocument(self, filename, password = '', readonly = False, hidden = False,
+ macroexecution = MACROEXECNORMAL, filtername = '', filteroptions = ''):
+ return self.ExecMethod(self.vbMethod, 'OpenDocument', filename, password, readonly, hidden,
+ macroexecution, filtername, filteroptions)
+
+ def Resize(self, left = -1, top = -1, width = -1, height = -1):
+ return self.ExecMethod(self.vbMethod, 'Resize', left, top, width, height)
+
+ def RunCommand(self, command, *args, **kwargs):
+ params = tuple(list(args) + ScriptForge.unpack_args(kwargs))
+ if len(params) == 0:
+ params = (command,) + (None,)
+ else:
+ params = (command,) + params
+ return self.SIMPLEEXEC('@SF_UI.RunCommand', params)
+
+ def SetStatusbar(self, text = '', percentage = -1):
+ return self.ExecMethod(self.vbMethod, 'SetStatusbar', text, percentage)
+
+ def ShowProgressBar(self, title = '', text = '', percentage = -1):
+ # From Python, the current XComponentContext must be added as last argument
+ return self.ExecMethod(self.vbMethod, 'ShowProgressBar', title, text, percentage,
+ ScriptForge.componentcontext)
+
+ def WindowExists(self, windowname):
+ return self.ExecMethod(self.vbMethod, 'WindowExists', windowname)
+
+
+# #####################################################################################################################
+# SFDatabases CLASS (alias of SFDatabases Basic library) ###
+# #####################################################################################################################
+class SFDatabases:
+ """
+ The SFDatabases class manages databases embedded in or connected to Base documents
+ """
+ pass
+
+ # #########################################################################
+ # SF_Database CLASS
+ # #########################################################################
+ class SF_Database(SFServices):
+ """
+ Each instance of the current class represents a single database, with essentially its tables, queries
+ and data
+ The exchanges with the database are done in SQL only.
+ To make them more readable, use optionally square brackets to surround table/query/field names
+ instead of the (RDBMS-dependent) normal surrounding character.
+ SQL statements may be run in direct or indirect mode. In direct mode the statement is transferred literally
+ without syntax checking nor review to the database engine.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDatabases.Database'
+ servicesynonyms = ('database', 'sfdatabases.database')
+ serviceproperties = dict(Queries = False, Tables = False, XConnection = False, XMetaData = False)
+
+ @classmethod
+ def ReviewServiceArgs(cls, filename = '', registrationname = '', readonly = True, user = '', password = ''):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return filename, registrationname, readonly, user, password
+
+ def CloseDatabase(self):
+ return self.ExecMethod(self.vbMethod, 'CloseDatabase')
+
+ def DAvg(self, expression, tablename, criteria = ''):
+ return self.ExecMethod(self.vbMethod, 'DAvg', expression, tablename, criteria)
+
+ def DCount(self, expression, tablename, criteria = ''):
+ return self.ExecMethod(self.vbMethod, 'DCount', expression, tablename, criteria)
+
+ def DLookup(self, expression, tablename, criteria = '', orderclause = ''):
+ return self.ExecMethod(self.vbMethod, 'DLookup', expression, tablename, criteria, orderclause)
+
+ def DMax(self, expression, tablename, criteria = ''):
+ return self.ExecMethod(self.vbMethod, 'DMax', expression, tablename, criteria)
+
+ def DMin(self, expression, tablename, criteria = ''):
+ return self.ExecMethod(self.vbMethod, 'DMin', expression, tablename, criteria)
+
+ def DSum(self, expression, tablename, criteria = ''):
+ return self.ExecMethod(self.vbMethod, 'DSum', expression, tablename, criteria)
+
+ def GetRows(self, sqlcommand, directsql = False, header = False, maxrows = 0):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'GetRows', sqlcommand, directsql, header, maxrows)
+
+ def RunSql(self, sqlcommand, directsql = False):
+ return self.ExecMethod(self.vbMethod, 'RunSql', sqlcommand, directsql)
+
+
+# #####################################################################################################################
+# SFDialogs CLASS (alias of SFDialogs Basic library) ###
+# #####################################################################################################################
+class SFDialogs:
+ """
+ The SFDialogs class manages dialogs defined with the Basic IDE
+ """
+ pass
+
+ # #########################################################################
+ # SF_Dialog CLASS
+ # #########################################################################
+ class SF_Dialog(SFServices):
+ """
+ Each instance of the current class represents a single dialog box displayed to the user.
+ The dialog box must have been designed and defined with the Basic IDE previously.
+ From a Python script, a dialog box can be displayed in modal or in non-modal modes.
+
+ In modal mode, the box is displayed and the execution of the macro process is suspended
+ until one of the OK or Cancel buttons is pressed. In the meantime, other user actions
+ executed on the box can trigger specific actions.
+
+ In non-modal mode, the floating dialog remains displayed until the dialog is terminated
+ by code (Terminate()) or until the LibreOffice application stops.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDialogs.Dialog'
+ servicesynonyms = ('dialog', 'sfdialogs.dialog')
+ serviceproperties = dict(Caption = True, Height = True, Modal = False, Name = False,
+ OnFocusGained = False, OnFocusLost = False, OnKeyPressed = False,
+ OnKeyReleased = False, OnMouseDragged = False, OnMouseEntered = False,
+ OnMouseExited = False, OnMouseMoved = False, OnMousePressed = False,
+ OnMouseReleased = False,
+ Page = True, Visible = True, Width = True, XDialogModel = False, XDialogView = False)
+ # Class constants used together with the Execute() method
+ OKBUTTON, CANCELBUTTON = 1, 0
+
+ @classmethod
+ def ReviewServiceArgs(cls, container = '', library = 'Standard', dialogname = ''):
+ """
+ Transform positional and keyword arguments into positional only
+ Add the XComponentContext as last argument
+ """
+ return container, library, dialogname, ScriptForge.componentcontext
+
+ # Methods potentially executed while the dialog is in execution require the flgHardCode flag
+ def Activate(self):
+ return self.ExecMethod(self.vbMethod + self.flgHardCode, '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)
+
+ def Controls(self, controlname = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet + self.flgHardCode, 'Controls', controlname)
+
+ def EndExecute(self, returnvalue):
+ return self.ExecMethod(self.vbMethod + self.flgHardCode, 'EndExecute', returnvalue)
+
+ def Execute(self, modal = True):
+ return self.ExecMethod(self.vbMethod + self.flgHardCode, 'Execute', modal)
+
+ def GetTextsFromL10N(self, l10n):
+ l10nobj = l10n.objectreference if isinstance(l10n, SFScriptForge.SF_L10N) else l10n
+ return self.ExecMethod(self.vbMethod + self.flgObject, 'GetTextsFromL10N', l10nobj)
+
+ def Resize(self, left = -1, top = -1, width = -1, height = -1):
+ return self.ExecMethod(self.vbMethod + self.flgHardCode, 'Resize', left, top, width, height)
+
+ def Terminate(self):
+ return self.ExecMethod(self.vbMethod, 'Terminate')
+
+ # #########################################################################
+ # SF_DialogControl CLASS
+ # #########################################################################
+ class SF_DialogControl(SFServices):
+ """
+ Each instance of the current class represents a single control within a dialog box.
+ The focus is clearly set on getting and setting the values displayed by the controls of the dialog box,
+ not on their formatting.
+ A special attention is given to controls with type TreeControl.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDialogs.DialogControl'
+ servicesynonyms = ()
+ serviceproperties = dict(Cancel = True, Caption = True, ControlType = False, CurrentNode = True,
+ Default = True, Enabled = True, Format = True, ListCount = False,
+ ListIndex = True, Locked = True, MultiSelect = True, Name = False,
+ OnActionPerformed = False, OnAdjustmentValueChanged = False, OnFocusGained = False,
+ OnFocusLost = False, OnItemStateChanged = False, OnKeyPressed = False,
+ OnKeyReleased = False, OnMouseDragged = False, OnMouseEntered = False,
+ OnMouseExited = False, OnMouseMoved = False, OnMousePressed = False,
+ OnMouseReleased = False, OnNodeExpanded = True, OnNodeSelected = True,
+ OnTextChanged = False, Page = True, Parent = False, Picture = True,
+ RootNode = False, RowSource = True, Text = False, TipText = True,
+ TripleState = True, Value = True, Visible = True,
+ XControlModel = False, XControlView = False, XGridColumnModel = False,
+ XGridDataModel = False, XTreeDataModel = False)
+
+ # Root related properties do not start with X and, nevertheless, return a UNO object
+ @property
+ def CurrentNode(self):
+ return self.EXEC(self.objectreference, self.vbGet + self.flgUno, 'CurrentNode')
+
+ @property
+ def RootNode(self):
+ return self.EXEC(self.objectreference, self.vbGet + self.flgUno, 'RootNode')
+
+ def AddSubNode(self, parentnode, displayvalue, datavalue = ScriptForge.cstSymEmpty):
+ return self.ExecMethod(self.vbMethod + self.flgUno, 'AddSubNode', parentnode, displayvalue, datavalue)
+
+ def AddSubTree(self, parentnode, flattree, withdatavalue = False):
+ return self.ExecMethod(self.vbMethod, 'AddSubTree', parentnode, flattree, withdatavalue)
+
+ def CreateRoot(self, displayvalue, datavalue = ScriptForge.cstSymEmpty):
+ return self.ExecMethod(self.vbMethod + self.flgUno, 'CreateRoot', displayvalue, datavalue)
+
+ def FindNode(self, displayvalue, datavalue = ScriptForge.cstSymEmpty, casesensitive = False):
+ return self.ExecMethod(self.vbMethod + self.flgUno, 'FindNode', displayvalue, datavalue, casesensitive)
+
+ def SetFocus(self):
+ return self.ExecMethod(self.vbMethod, 'SetFocus')
+
+ def SetTableData(self, dataarray, widths = (1,), alignments = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayArg, 'SetTableData', dataarray, widths, alignments)
+
+ def WriteLine(self, line = ''):
+ return self.ExecMethod(self.vbMethod, 'WriteLine', line)
+
+
+# #####################################################################################################################
+# SFDocuments CLASS (alias of SFDocuments Basic library) ###
+# #####################################################################################################################
+class SFDocuments:
+ """
+ The SFDocuments class gathers a number of classes, methods and properties making easy
+ managing and manipulating LibreOffice documents
+ """
+ pass
+
+ # #########################################################################
+ # SF_Document CLASS
+ # #########################################################################
+ class SF_Document(SFServices):
+ """
+ The methods and properties are generic for all types of documents: they are combined in the
+ current SF_Document class
+ - saving, closing documents
+ - accessing their standard or custom properties
+ Specific properties and methods are implemented in the concerned subclass(es) SF_Calc, SF_Base, ...
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.Document'
+ servicesynonyms = ('document', 'sfdocuments.document')
+ serviceproperties = dict(Description = True, DocumentType = False, ExportFilters = False, ImportFilters = False,
+ IsBase = False, IsCalc = False, IsDraw = False, IsImpress = False, IsMath = False,
+ IsWriter = False, Keywords = True, Readonly = False, Subject = True, Title = True,
+ XComponent = False)
+ # Force for each property to get its value from Basic - due to intense interactivity with user
+ forceGetProperty = True
+
+ @classmethod
+ def ReviewServiceArgs(cls, windowname = ''):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return windowname,
+
+ def Activate(self):
+ return self.ExecMethod(self.vbMethod, 'Activate')
+
+ def CloseDocument(self, saveask = True):
+ return self.ExecMethod(self.vbMethod, 'CloseDocument', saveask)
+
+ def CreateMenu(self, menuheader, before = '', submenuchar = '>'):
+ return self.ExecMethod(self.vbMethod, 'CreateMenu', menuheader, before, submenuchar)
+
+ def ExportAsPDF(self, filename, overwrite = False, pages = '', password = '', watermark = ''):
+ return self.ExecMethod(self.vbMethod, 'ExportAsPDF', filename, overwrite, pages, password, watermark)
+
+ def PrintOut(self, pages = '', copies = 1):
+ return self.ExecMethod(self.vbMethod, 'PrintOut', pages, copies)
+
+ def RemoveMenu(self, menuheader):
+ return self.ExecMethod(self.vbMethod, 'RemoveMenu', menuheader)
+
+ def RunCommand(self, command, *args, **kwargs):
+ params = tuple([command] + list(args) + ScriptForge.unpack_args(kwargs))
+ return self.ExecMethod(self.vbMethod, 'RunCommand', *params)
+
+ def Save(self):
+ return self.ExecMethod(self.vbMethod, 'Save')
+
+ def SaveAs(self, filename, overwrite = False, password = '', filtername = '', filteroptions = ''):
+ return self.ExecMethod(self.vbMethod, 'SaveAs', filename, overwrite, password, filtername, filteroptions)
+
+ def SaveCopyAs(self, filename, overwrite = False, password = '', filtername = '', filteroptions = ''):
+ return self.ExecMethod(self.vbMethod, 'SaveCopyAs', filename, overwrite,
+ password, filtername, filteroptions)
+
+ def SetPrinter(self, printer = '', orientation = '', paperformat = ''):
+ return self.ExecMethod(self.vbMethod, 'SetPrinter', printer, orientation, paperformat)
+
+ # #########################################################################
+ # SF_Base CLASS
+ # #########################################################################
+ class SF_Base(SF_Document, SFServices):
+ """
+ The SF_Base module is provided mainly to block parent properties that are NOT applicable to Base documents
+ In addition, it provides methods to identify form documents and access their internal forms
+ (read more elsewhere (the "SFDocuments.Form" service) about this subject)
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.Base'
+ servicesynonyms = ('base', 'scriptforge.base')
+ serviceproperties = dict(DocumentType = False, IsBase = False, IsCalc = False,
+ IsDraw = False, IsImpress = False, IsMath = False, IsWriter = False,
+ XComponent = False)
+
+ @classmethod
+ def ReviewServiceArgs(cls, windowname = ''):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return windowname,
+
+ def CloseDocument(self, saveask = True):
+ return self.ExecMethod(self.vbMethod, 'CloseDocument', saveask)
+
+ def CloseFormDocument(self, formdocument):
+ return self.ExecMethod(self.vbMethod, 'CloseFormDocument', formdocument)
+
+ def FormDocuments(self):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'FormDocuments')
+
+ def Forms(self, formdocument, form = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Forms', formdocument, form)
+
+ def GetDatabase(self, user = '', password = ''):
+ return self.ExecMethod(self.vbMethod, 'GetDatabase', user, password)
+
+ def IsLoaded(self, formdocument):
+ return self.ExecMethod(self.vbMethod, 'IsLoaded', formdocument)
+
+ def OpenFormDocument(self, formdocument, designmode = False):
+ return self.ExecMethod(self.vbMethod, 'OpenFormDocument', formdocument, designmode)
+
+ def PrintOut(self, formdocument, pages = '', copies = 1):
+ return self.ExecMethod(self.vbMethod, 'PrintOut', formdocument, pages, copies)
+
+ def SetPrinter(self, formdocument = '', printer = '', orientation = '', paperformat = ''):
+ return self.ExecMethod(self.vbMethod, 'SetPrinter', formdocument, printer, orientation, paperformat)
+
+ # #########################################################################
+ # SF_Calc CLASS
+ # #########################################################################
+ class SF_Calc(SF_Document, SFServices):
+ """
+ The SF_Calc module is focused on :
+ - management (copy, insert, move, ...) of sheets within a Calc document
+ - exchange of data between Basic data structures and Calc ranges of values
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.Calc'
+ servicesynonyms = ('calc', 'sfdocuments.calc')
+ serviceproperties = dict(CurrentSelection = True, Sheets = False,
+ Description = True, DocumentType = False, ExportFilters = False, ImportFilters = False,
+ IsBase = False, IsCalc = False, IsDraw = False, IsImpress = False, IsMath = False,
+ IsWriter = False, Keywords = True, Readonly = False, Subject = True, Title = True,
+ XComponent = False)
+ # Force for each property to get its value from Basic - due to intense interactivity with user
+ forceGetProperty = True
+
+ @classmethod
+ def ReviewServiceArgs(cls, windowname = ''):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return windowname,
+
+ # Next functions are implemented in Basic as read-only properties with 1 argument
+ def FirstCell(self, rangename):
+ return self.GetProperty('FirstCell', rangename)
+
+ def FirstColumn(self, rangename):
+ return self.GetProperty('FirstColumn', rangename)
+
+ def FirstRow(self, rangename):
+ return self.GetProperty('FirstRow', rangename)
+
+ def Height(self, rangename):
+ return self.GetProperty('Height', rangename)
+
+ def LastCell(self, rangename):
+ return self.GetProperty('LastCell', rangename)
+
+ def LastColumn(self, rangename):
+ return self.GetProperty('LastColumn', rangename)
+
+ def LastRow(self, rangename):
+ return self.GetProperty('LastRow', rangename)
+
+ def Range(self, rangename):
+ return self.GetProperty('Range', rangename)
+
+ def Region(self, rangename):
+ return self.GetProperty('Region', rangename)
+
+ def Sheet(self, sheetname):
+ return self.GetProperty('Sheet', sheetname)
+
+ def SheetName(self, rangename):
+ return self.GetProperty('SheetName', rangename)
+
+ def Width(self, rangename):
+ return self.GetProperty('Width', rangename)
+
+ def XCellRange(self, rangename):
+ return self.ExecMethod(self.vbGet + self.flgUno, 'XCellRange', rangename)
+
+ def XSheetCellCursor(self, rangename):
+ return self.ExecMethod(self.vbGet + self.flgUno, 'XSheetCellCursor', rangename)
+
+ def XSpreadsheet(self, sheetname):
+ return self.ExecMethod(self.vbGet + self.flgUno, 'XSpreadsheet', sheetname)
+
+ # Usual methods
+ def A1Style(self, row1, column1, row2 = 0, column2 = 0, sheetname = '~'):
+ return self.ExecMethod(self.vbMethod, 'A1Style', row1, column1, row2, column2, sheetname)
+
+ def Activate(self, sheetname = ''):
+ return self.ExecMethod(self.vbMethod, 'Activate', sheetname)
+
+ def Charts(self, sheetname, chartname = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Charts', sheetname, chartname)
+
+ def ClearAll(self, range):
+ return self.ExecMethod(self.vbMethod, 'ClearAll', range)
+
+ def ClearFormats(self, range):
+ return self.ExecMethod(self.vbMethod, 'ClearFormats', range)
+
+ def ClearValues(self, range):
+ return self.ExecMethod(self.vbMethod, 'ClearValues', range)
+
+ def CompactLeft(self, range, wholecolumn = False, filterformula = ''):
+ return self.ExecMethod(self.vbMethod, 'CompactLeft', range, wholecolumn, filterformula)
+
+ def CompactUp(self, range, wholerow = False, filterformula = ''):
+ return self.ExecMethod(self.vbMethod, 'CompactUp', range, wholerow, filterformula)
+
+ def CopySheet(self, sheetname, newname, beforesheet = 32768):
+ sheet = (sheetname.objectreference if isinstance(sheetname, SFDocuments.SF_CalcReference) else sheetname)
+ return self.ExecMethod(self.vbMethod + self.flgObject, 'CopySheet', sheet, newname, beforesheet)
+
+ def CopySheetFromFile(self, filename, sheetname, newname, beforesheet = 32768):
+ sheet = (sheetname.objectreference if isinstance(sheetname, SFDocuments.SF_CalcReference) else sheetname)
+ return self.ExecMethod(self.vbMethod + self.flgObject, 'CopySheetFromFile',
+ filename, sheet, newname, beforesheet)
+
+ def CopyToCell(self, sourcerange, destinationcell):
+ range = (sourcerange.objectreference if isinstance(sourcerange, SFDocuments.SF_CalcReference)
+ else sourcerange)
+ return self.ExecMethod(self.vbMethod + self.flgObject, 'CopyToCell', range, destinationcell)
+
+ def CopyToRange(self, sourcerange, destinationrange):
+ range = (sourcerange.objectreference if isinstance(sourcerange, SFDocuments.SF_CalcReference)
+ else sourcerange)
+ return self.ExecMethod(self.vbMethod + self.flgObject, 'CopyToRange', range, destinationrange)
+
+ def CreateChart(self, chartname, sheetname, range, columnheader = False, rowheader = False):
+ return self.ExecMethod(self.vbMethod, 'CreateChart', chartname, sheetname, range, columnheader, rowheader)
+
+ def CreatePivotTable(self, pivottablename, sourcerange, targetcell, datafields = ScriptForge.cstSymEmpty,
+ rowfields = ScriptForge.cstSymEmpty, columnfields = ScriptForge.cstSymEmpty,
+ filterbutton = True, rowtotals = True, columntotals = True):
+ return self.ExecMethod(self.vbMethod, 'CreatePivotTable', pivottablename, sourcerange, targetcell,
+ datafields, rowfields, columnfields, filterbutton, rowtotals, columntotals)
+
+ def DAvg(self, range):
+ return self.ExecMethod(self.vbMethod, 'DAvg', range)
+
+ def DCount(self, range):
+ return self.ExecMethod(self.vbMethod, 'DCount', range)
+
+ def DMax(self, range):
+ return self.ExecMethod(self.vbMethod, 'DMax', range)
+
+ def DMin(self, range):
+ return self.ExecMethod(self.vbMethod, 'DMin', range)
+
+ def DSum(self, range):
+ return self.ExecMethod(self.vbMethod, 'DSum', range)
+
+ def ExportRangeToFile(self, range, filename, imagetype = 'pdf', overwrite = False):
+ return self.ExecMethod(self.vbMethod, 'ExportRangeToFile', range, filename, imagetype, overwrite)
+
+ def Forms(self, sheetname, form = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Forms', sheetname, form)
+
+ def GetColumnName(self, columnnumber):
+ return self.ExecMethod(self.vbMethod, 'GetColumnName', columnnumber)
+
+ def GetFormula(self, range):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'GetFormula', range)
+
+ def GetValue(self, range):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'GetValue', range)
+
+ def ImportFromCSVFile(self, filename, destinationcell, filteroptions = ScriptForge.cstSymEmpty):
+ return self.ExecMethod(self.vbMethod, 'ImportFromCSVFile', filename, destinationcell, filteroptions)
+
+ def ImportFromDatabase(self, filename = '', registrationname = '', destinationcell = '', sqlcommand = '',
+ directsql = False):
+ return self.ExecMethod(self.vbMethod, 'ImportFromDatabase', filename, registrationname,
+ destinationcell, sqlcommand, directsql)
+
+ def InsertSheet(self, sheetname, beforesheet = 32768):
+ return self.ExecMethod(self.vbMethod, 'InsertSheet', sheetname, beforesheet)
+
+ def MoveRange(self, source, destination):
+ return self.ExecMethod(self.vbMethod, 'MoveRange', source, destination)
+
+ def MoveSheet(self, sheetname, beforesheet = 32768):
+ return self.ExecMethod(self.vbMethod, 'MoveSheet', sheetname, beforesheet)
+
+ def Offset(self, range, rows = 0, columns = 0, height = ScriptForge.cstSymEmpty,
+ width = ScriptForge.cstSymEmpty):
+ return self.ExecMethod(self.vbMethod, 'Offset', range, rows, columns, height, width)
+
+ def OpenRangeSelector(self, title = '', selection = '~', singlecell = False, closeafterselect = True):
+ return self.ExecMethod(self.vbMethod, 'OpenRangeSelector', title, selection, singlecell, closeafterselect)
+
+ def Printf(self, inputstr, range, tokencharacter = '%'):
+ return self.ExecMethod(self.vbMethod, 'Printf', inputstr, range, tokencharacter)
+
+ def PrintOut(self, sheetname = '~', pages = '', copies = 1):
+ return self.ExecMethod(self.vbMethod, 'PrintOut', sheetname, pages, copies)
+
+ def RemoveSheet(self, sheetname):
+ return self.ExecMethod(self.vbMethod, 'RemoveSheet', sheetname)
+
+ def RenameSheet(self, sheetname, newname):
+ return self.ExecMethod(self.vbMethod, 'RenameSheet', sheetname, newname)
+
+ def SetArray(self, targetcell, value):
+ return self.ExecMethod(self.vbMethod + self.flgArrayArg, 'SetArray', targetcell, value)
+
+ def SetCellStyle(self, targetrange, style):
+ return self.ExecMethod(self.vbMethod, 'SetCellStyle', targetrange, style)
+
+ def SetFormula(self, targetrange, formula):
+ return self.ExecMethod(self.vbMethod + self.flgArrayArg, 'SetFormula', targetrange, formula)
+
+ def SetValue(self, targetrange, value):
+ return self.ExecMethod(self.vbMethod + self.flgArrayArg, 'SetValue', targetrange, value)
+
+ def ShiftDown(self, range, wholerow = False, rows = 0):
+ return self.ExecMethod(self.vbMethod, 'ShiftDown', range, wholerow, rows)
+
+ def ShiftLeft(self, range, wholecolumn = False, columns = 0):
+ return self.ExecMethod(self.vbMethod, 'ShiftLeft', range, wholecolumn, columns)
+
+ def ShiftRight(self, range, wholecolumn = False, columns = 0):
+ return self.ExecMethod(self.vbMethod, 'ShiftRight', range, wholecolumn, columns)
+
+ def ShiftUp(self, range, wholerow = False, rows = 0):
+ return self.ExecMethod(self.vbMethod, 'ShiftUp', range, wholerow, rows)
+
+ def SortRange(self, range, sortkeys, sortorder = 'ASC', destinationcell = ScriptForge.cstSymEmpty,
+ containsheader = False, casesensitive = False, sortcolumns = False):
+ return self.ExecMethod(self.vbMethod, 'SortRange', range, sortkeys, sortorder, destinationcell,
+ containsheader, casesensitive, sortcolumns)
+
+ # #########################################################################
+ # SF_CalcReference CLASS
+ # #########################################################################
+ class SF_CalcReference(SFServices):
+ """
+ The SF_CalcReference class has as unique role to hold sheet and range references.
+ They are implemented in Basic as Type ... End Type data structures
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.CalcReference'
+ servicesynonyms = ()
+ serviceproperties = dict()
+
+ # #########################################################################
+ # SF_Chart CLASS
+ # #########################################################################
+ class SF_Chart(SFServices):
+ """
+ The SF_Chart module is focused on the description of chart documents
+ stored in Calc sheets.
+ With this service, many chart types and chart characteristics available
+ in the user interface can be read or modified.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.Chart'
+ servicesynonyms = ()
+ serviceproperties = dict(ChartType = True, Deep = True, Dim3D = True, Exploded = True, Filled = True,
+ Legend = True, Percent = True, Stacked = True, Title = True,
+ XChartObj = False, XDiagram = False, XShape = False, XTableChart = False,
+ XTitle = True, YTitle = True)
+
+ def Resize(self, xpos = -1, ypos = -1, width = -1, height = -1):
+ return self.ExecMethod(self.vbMethod, 'Resize', xpos, ypos, width, height)
+
+ def ExportToFile(self, filename, imagetype = 'png', overwrite = False):
+ return self.ExecMethod(self.vbMethod, 'ExportToFile', filename, imagetype, overwrite)
+
+ # #########################################################################
+ # SF_Form CLASS
+ # #########################################################################
+ class SF_Form(SFServices):
+ """
+ Management of forms defined in LibreOffice documents. Supported types are Base, Calc and Writer documents.
+ It includes the management of subforms
+ Each instance of the current class represents a single form or a single subform
+ A form may optionally be (understand "is often") linked to a data source manageable with
+ the SFDatabases.Database service. The current service offers a rapid access to that service.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.Form'
+ servicesynonyms = ()
+ serviceproperties = dict(AllowDeletes = True, AllowInserts = True, AllowUpdates = True, BaseForm = False,
+ Bookmark = True, CurrentRecord = True, Filter = True, LinkChildFields = False,
+ LinkParentFields = False, Name = False,
+ OnApproveCursorMove = True, OnApproveParameter = True, OnApproveReset = True,
+ OnApproveRowChange = True, OnApproveSubmit = True, OnConfirmDelete = True,
+ OnCursorMoved = True, OnErrorOccurred = True, OnLoaded = True, OnReloaded = True,
+ OnReloading = True, OnResetted = True, OnRowChanged = True, OnUnloaded = True,
+ OnUnloading = True,
+ OrderBy = True, Parent = False, RecordSource = True, XForm = False)
+
+ def Activate(self):
+ return self.ExecMethod(self.vbMethod, 'Activate')
+
+ def CloseFormDocument(self):
+ return self.ExecMethod(self.vbMethod, 'CloseFormDocument')
+
+ def Controls(self, controlname = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Controls', controlname)
+
+ def GetDatabase(self, user = '', password = ''):
+ return self.ExecMethod(self.vbMethod, 'GetDatabase', user, password)
+
+ def MoveFirst(self):
+ return self.ExecMethod(self.vbMethod, 'MoveFirst')
+
+ def MoveLast(self):
+ return self.ExecMethod(self.vbMethod, 'MoveLast')
+
+ def MoveNew(self):
+ return self.ExecMethod(self.vbMethod, 'MoveNew')
+
+ def MoveNext(self, offset = 1):
+ return self.ExecMethod(self.vbMethod, 'MoveNext', offset)
+
+ def MovePrevious(self, offset = 1):
+ return self.ExecMethod(self.vbMethod, 'MovePrevious', offset)
+
+ def Requery(self):
+ return self.ExecMethod(self.vbMethod, 'Requery')
+
+ def Subforms(self, subform = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Subforms', subform)
+
+ # #########################################################################
+ # SF_FormControl CLASS
+ # #########################################################################
+ class SF_FormControl(SFServices):
+ """
+ Manage the controls belonging to a form or subform stored in a document.
+ Each instance of the current class represents a single control within a form, a subform or a tablecontrol.
+ A prerequisite is that all controls within the same form, subform or tablecontrol must have
+ a unique name.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.FormControl'
+ servicesynonyms = ()
+ serviceproperties = dict(Action = True, Caption = True, ControlSource = False, ControlType = False,
+ Default = True, DefaultValue = True, Enabled = True, Format = True,
+ ListCount = False, ListIndex = True, ListSource = True, ListSourceType = True,
+ Locked = True, MultiSelect = True, Name = False,
+ OnActionPerformed = True, OnAdjustmentValueChanged = True,
+ OnApproveAction = True, OnApproveReset = True, OnApproveUpdate = True,
+ OnChanged = True, OnErrorOccurred = True, OnFocusGained = True, OnFocusLost = True,
+ OnItemStateChanged = True, OnKeyPressed = True, OnKeyReleased = True,
+ OnMouseDragged = True, OnMouseEntered = True, OnMouseExited = True,
+ OnMouseMoved = True, OnMousePressed = True, OnMouseReleased = True, OnResetted = True,
+ OnTextChanged = True, OnUpdated = True, Parent = False, Picture = True,
+ Required = True, Text = False, TipText = True, TripleState = True, Value = True,
+ Visible = True, XControlModel = False, XControlView = False)
+
+ def Controls(self, controlname = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Controls', controlname)
+
+ def SetFocus(self):
+ return self.ExecMethod(self.vbMethod, 'SetFocus')
+
+ # #########################################################################
+ # SF_Writer CLASS
+ # #########################################################################
+ class SF_Writer(SF_Document, SFServices):
+ """
+ The SF_Writer module is focused on :
+ - TBD
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFDocuments.Writer'
+ servicesynonyms = ('writer', 'sfdocuments.writer')
+ serviceproperties = dict(Description = True, DocumentType = False, ExportFilters = False, ImportFilters = False,
+ IsBase = False, IsCalc = False, IsDraw = False, IsImpress = False, IsMath = False,
+ IsWriter = False, Keywords = True, Readonly = False, Subject = True, Title = True,
+ XComponent = False)
+ # Force for each property to get its value from Basic - due to intense interactivity with user
+ forceGetProperty = True
+
+ @classmethod
+ def ReviewServiceArgs(cls, windowname = ''):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return windowname,
+
+ def Forms(self, form = ''):
+ return self.ExecMethod(self.vbMethod + self.flgArrayRet, 'Forms', form)
+
+ def PrintOut(self, pages = '', copies = 1, printbackground = True, printblankpages = False,
+ printevenpages = True, printoddpages = True, printimages = True):
+ return self.ExecMethod(self.vbMethod, 'PrintOut', pages, copies, printbackground, printblankpages,
+ printevenpages, printoddpages, printimages)
+
+
+# #####################################################################################################################
+# SFWidgets CLASS (alias of SFWidgets Basic library) ###
+# #####################################################################################################################
+class SFWidgets:
+ """
+ The SFWidgets class manages toolbars and popup menus
+ """
+ pass
+
+ # #########################################################################
+ # SF_Menu CLASS
+ # #########################################################################
+ class SF_Menu(SFServices):
+ """
+ Display a menu in the menubar of a document or a form document.
+ After use, the menu will not be saved neither in the application settings, nor in the document.
+ The menu will be displayed, as usual, when its header in the menubar is clicked.
+ When one of its items is selected, there are 3 alternative options:
+ - a UNO command (like ".uno:About") is triggered
+ - a user script is run receiving a standard argument defined in this service
+ - one of above combined with a toggle of the status of the item
+ The menu is described from top to bottom. Each menu item receives a numeric and a string identifier.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFWidgets.Menu'
+ servicesynonyms = ('menu', 'sfwidgets.menu')
+ serviceproperties = dict(ShortcutCharacter = False, SubmenuCharacter = False)
+
+ def AddCheckBox(self, menuitem, name = '', status = False, icon = '', tooltip = '',
+ command = '', script = ''):
+ return self.ExecMethod(self.vbMethod, 'AddCheckBox', menuitem, name, status, icon, tooltip,
+ command, script)
+
+ def AddItem(self, menuitem, name = '', icon = '', tooltip = '', command = '', script = ''):
+ return self.ExecMethod(self.vbMethod, 'AddItem', menuitem, name, icon, tooltip, command, script)
+
+ def AddRadioButton(self, menuitem, name = '', status = False, icon = '', tooltip = '',
+ command = '', script = ''):
+ return self.ExecMethod(self.vbMethod, 'AddRadioButton', menuitem, name, status, icon, tooltip,
+ command, script)
+
+ # #########################################################################
+ # SF_PopupMenu CLASS
+ # #########################################################################
+ class SF_PopupMenu(SFServices):
+ """
+ Display a popup menu anywhere and any time.
+ A popup menu is usually triggered by a mouse action (typically a right-click) on a dialog, a form
+ or one of their controls. In this case the menu will be displayed below the clicked area.
+ When triggered by other events, including in the normal flow of a user script, the script should
+ provide the coordinates of the topleft edge of the menu versus the actual component.
+ The menu is described from top to bottom. Each menu item receives a numeric and a string identifier.
+ The execute() method returns the item selected by the user.
+ """
+ # Mandatory class properties for service registration
+ serviceimplementation = 'basic'
+ servicename = 'SFWidgets.PopupMenu'
+ servicesynonyms = ('popupmenu', 'sfwidgets.popupmenu')
+ serviceproperties = dict(ShortcutCharacter = False, SubmenuCharacter = False)
+
+ @classmethod
+ def ReviewServiceArgs(cls, event = None, x = 0, y = 0, submenuchar = ''):
+ """
+ Transform positional and keyword arguments into positional only
+ """
+ return event, x, y, submenuchar
+
+ def AddCheckBox(self, menuitem, name = '', status = False, icon = '', tooltip = ''):
+ return self.ExecMethod(self.vbMethod, 'AddCheckBox', menuitem, name, status, icon, tooltip)
+
+ def AddItem(self, menuitem, name = '', icon = '', tooltip = ''):
+ return self.ExecMethod(self.vbMethod, 'AddItem', menuitem, name, icon, tooltip)
+
+ def AddRadioButton(self, menuitem, name = '', status = False, icon = '', tooltip = ''):
+ return self.ExecMethod(self.vbMethod, 'AddRadioButton', menuitem, name, status, icon, tooltip)
+
+ def Execute(self, returnid = True):
+ return self.ExecMethod(self.vbMethod, 'Execute', returnid)
+
+
+# ##############################################False##################################################################
+# CreateScriptService() ###
+# #####################################################################################################################
+def CreateScriptService(service, *args, **kwargs):
+ """
+ A service being the name of a collection of properties and methods,
+ this method returns either
+ - the Python object mirror of the Basic object implementing the requested service
+ - the Python object implementing the service itself
+
+ A service may be designated by its official name, stored in its class.servicename
+ or by one of its synonyms stored in its class.servicesynonyms list
+ If the service is not identified, the service creation is delegated to Basic, that might raise an error
+ if still not identified there
+
+ :param service: the name of the service as a string 'library.service' - cased exactly
+ or one of its synonyms
+ :param args: the arguments to pass to the service constructor
+ :return: the service as a Python object
+ """
+ # Init at each CreateScriptService() invocation
+ # CreateScriptService is usually the first statement in user scripts requesting ScriptForge services
+ # ScriptForge() is optional in user scripts when Python process inside LibreOffice process
+ if ScriptForge.SCRIPTFORGEINITDONE is False:
+ ScriptForge()
+
+ def ResolveSynonyms(servicename):
+ """
+ Synonyms within service names implemented in Python or predefined are resolved here
+ :param servicename: The short name of the service
+ :return: The official service name if found, the argument otherwise
+ """
+ for cls in SFServices.__subclasses__():
+ if servicename.lower() in cls.servicesynonyms:
+ return cls.servicename
+ return servicename
+
+ #
+ # Check the list of available services
+ scriptservice = ResolveSynonyms(service)
+ if scriptservice in ScriptForge.serviceslist:
+ serv = ScriptForge.serviceslist[scriptservice]
+ # Check if the requested service is within the Python world
+ if serv.serviceimplementation == 'python':
+ return serv(*args)
+ # Check if the service is a predefined standard Basic service
+ elif scriptservice in ScriptForge.servicesmodules:
+ return serv(ScriptForge.servicesmodules[scriptservice], classmodule = SFServices.moduleStandard)
+ else:
+ serv = None
+ # The requested service is to be found in the Basic world
+ # Check if the service must review the arguments
+ if serv is not None:
+ if hasattr(serv, 'ReviewServiceArgs'):
+ # ReviewServiceArgs() must be a class method
+ args = serv.ReviewServiceArgs(*args, **kwargs)
+ # Get the service object back from Basic
+ if len(args) == 0:
+ serv = ScriptForge.InvokeBasicService('SF_Services', SFServices.vbMethod, 'CreateScriptService', service)
+ else:
+ serv = ScriptForge.InvokeBasicService('SF_Services', SFServices.vbMethod, 'CreateScriptService',
+ service, *args)
+ return serv
+
+
+createScriptService, createscriptservice = CreateScriptService, CreateScriptService
+
+
+# ######################################################################
+# Lists the scripts, that shall be visible inside the Basic/Python IDE
+# ######################################################################
+
+g_exportedScripts = ()