355 lines
10 KiB
Python
355 lines
10 KiB
Python
#!/usr/bin/env python3
|
|
|
|
import datetime
|
|
import getpass
|
|
import logging
|
|
import os
|
|
import platform
|
|
import sys
|
|
|
|
from typing import Any, Union
|
|
|
|
import uno
|
|
import unohelper
|
|
from com.sun.star.beans import PropertyValue, NamedValue
|
|
from com.sun.star.datatransfer import XTransferable, DataFlavor
|
|
|
|
|
|
__all__ = [
|
|
'DESKTOP',
|
|
'INFO_DEBUG',
|
|
'IS_MAC',
|
|
'IS_WIN',
|
|
'LANG',
|
|
'LANGUAGE',
|
|
'NAME',
|
|
'OS',
|
|
'PC',
|
|
'USER',
|
|
'VERSION',
|
|
'ClipBoard',
|
|
'LOMain',
|
|
# ~ 'create_instance',
|
|
'data_to_dict',
|
|
'dict_to_property',
|
|
'get_app_config',
|
|
]
|
|
|
|
|
|
CTX = uno.getComponentContext()
|
|
SM = CTX.getServiceManager()
|
|
|
|
|
|
# Global variables
|
|
OS = platform.system()
|
|
DESKTOP = os.environ.get('DESKTOP_SESSION', '')
|
|
PC = platform.node()
|
|
USER = getpass.getuser()
|
|
IS_WIN = OS == 'Windows'
|
|
IS_MAC = OS == 'Darwin'
|
|
|
|
|
|
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
|
|
LOG_DATE = '%d/%m/%Y %H:%M:%S'
|
|
if IS_WIN:
|
|
logging.addLevelName(logging.ERROR, 'ERROR')
|
|
logging.addLevelName(logging.DEBUG, 'DEBUG')
|
|
logging.addLevelName(logging.INFO, 'INFO')
|
|
else:
|
|
logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
|
|
logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
|
|
logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
|
|
logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
|
|
log = logging.getLogger(__name__)
|
|
|
|
|
|
def create_instance(name: str, with_context: bool=False, arguments: Any=None) -> Any:
|
|
"""Create a service instance
|
|
|
|
:param name: Name of service
|
|
:type name: str
|
|
:param with_context: If used context
|
|
:type with_context: bool
|
|
:param argument: If needed some argument
|
|
:type argument: Any
|
|
:return: PyUno instance
|
|
:rtype: PyUno Object
|
|
"""
|
|
|
|
if with_context:
|
|
instance = SM.createInstanceWithContext(name, CTX)
|
|
elif arguments:
|
|
instance = SM.createInstanceWithArguments(name, (arguments,))
|
|
else:
|
|
instance = SM.createInstance(name)
|
|
|
|
return instance
|
|
|
|
|
|
def get_app_config(node_name: str, key: str='') -> Any:
|
|
"""Get any key from any node from LibreOffice configuration.
|
|
|
|
:param node_name: Name of node
|
|
:type name: str
|
|
:param key: Name of key
|
|
:type key: str
|
|
:return: Any value
|
|
:rtype: Any
|
|
|
|
`See Api ConfigurationProvider <https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1configuration_1_1ConfigurationProvider.html>`_
|
|
"""
|
|
|
|
name = 'com.sun.star.configuration.ConfigurationProvider'
|
|
service = 'com.sun.star.configuration.ConfigurationAccess'
|
|
cp = create_instance(name, True)
|
|
node = PropertyValue(Name='nodepath', Value=node_name)
|
|
value = ''
|
|
|
|
try:
|
|
value = cp.createInstanceWithArguments(service, (node,))
|
|
if value and value.hasByName(key):
|
|
value = value.getPropertyValue(key)
|
|
except Exception as e:
|
|
log.error(e)
|
|
value = ''
|
|
|
|
return value
|
|
|
|
|
|
# Get info LibO
|
|
NAME = TITLE = get_app_config('/org.openoffice.Setup/Product', 'ooName')
|
|
VERSION = get_app_config('/org.openoffice.Setup/Product','ooSetupVersion')
|
|
LANGUAGE = get_app_config('/org.openoffice.Setup/L10N/', 'ooLocale')
|
|
LANG = LANGUAGE.split('-')[0]
|
|
|
|
# Get start date from Calc configuration
|
|
node = '/org.openoffice.Office.Calc/Calculate/Other/Date'
|
|
year = get_app_config(node, 'YY')
|
|
month = get_app_config(node, 'MM')
|
|
day = get_app_config(node, 'DD')
|
|
DATE_OFFSET = datetime.date(year, month, day).toordinal()
|
|
|
|
_info_debug = f"Python: {sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path)
|
|
INFO_DEBUG = f"{NAME} v{VERSION} {LANGUAGE}\n\n{_info_debug}"
|
|
|
|
|
|
def dict_to_property(values: dict, uno_any: bool=False):
|
|
"""Convert dictionary to array of PropertyValue
|
|
|
|
:param values: Dictionary of values
|
|
:type values: dict
|
|
:param uno_any: If return like array uno.Any
|
|
:type uno_any: bool
|
|
:return: Tuple of PropertyValue or array uno.Any
|
|
:rtype: tuples or uno.Any
|
|
"""
|
|
ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
|
|
if uno_any:
|
|
ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
|
|
return ps
|
|
|
|
|
|
def data_to_dict(data: Union[tuple, list]) -> dict:
|
|
"""Convert tuples, list, PropertyValue, NamedValue to dictionary
|
|
|
|
:param data: Iterator of values
|
|
:type data: array of tuples, list, PropertyValue or NamedValue
|
|
:return: Dictionary
|
|
:rtype: dict
|
|
"""
|
|
d = {}
|
|
|
|
if isinstance(data[0], (tuple, list)):
|
|
d = {r[0]: r[1] for r in data}
|
|
elif isinstance(data[0], (PropertyValue, NamedValue)):
|
|
d = {r.Name: r.Value for r in data}
|
|
|
|
return d
|
|
|
|
|
|
# ~ https://github.com/django/django/blob/main/django/utils/functional.py#L61
|
|
class classproperty:
|
|
|
|
def __init__(self, method=None):
|
|
self.fget = method
|
|
|
|
def __get__(self, instance, cls=None):
|
|
return self.fget(cls)
|
|
|
|
def getter(self, method):
|
|
self.fget = method
|
|
return self
|
|
|
|
|
|
class BaseObject():
|
|
|
|
def __init__(self, obj):
|
|
self._obj = obj
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
@property
|
|
def obj(self):
|
|
"""Return original pyUno object"""
|
|
return self._obj
|
|
|
|
|
|
class LOMain():
|
|
"""Classe for LibreOffice"""
|
|
|
|
class commands():
|
|
"""Class for disable and enable commands
|
|
|
|
`See DispatchCommands <https://wiki.documentfoundation.org/Development/DispatchCommands>`_
|
|
"""
|
|
@classmethod
|
|
def _set_app_command(cls, command: str, disable: bool) -> bool:
|
|
"""Disable or enabled UNO command
|
|
|
|
:param command: UNO command to disable or enabled
|
|
:type command: str
|
|
:param disable: True if disable, False if active
|
|
:type disable: bool
|
|
:return: True if correctly update, False if not.
|
|
:rtype: bool
|
|
"""
|
|
NEW_NODE_NAME = f'zaz_disable_command_{command.lower()}'
|
|
name = 'com.sun.star.configuration.ConfigurationProvider'
|
|
service = 'com.sun.star.configuration.ConfigurationUpdateAccess'
|
|
node_name = '/org.openoffice.Office.Commands/Execute/Disabled'
|
|
|
|
cp = create_instance(name, True)
|
|
node = PropertyValue(Name='nodepath', Value=node_name)
|
|
update = cp.createInstanceWithArguments(service, (node,))
|
|
|
|
result = True
|
|
try:
|
|
if disable:
|
|
new_node = update.createInstanceWithArguments(())
|
|
new_node.setPropertyValue('Command', command)
|
|
update.insertByName(NEW_NODE_NAME, new_node)
|
|
else:
|
|
update.removeByName(NEW_NODE_NAME)
|
|
update.commitChanges()
|
|
except Exception as e:
|
|
result = False
|
|
|
|
return result
|
|
|
|
@classmethod
|
|
def disable(cls, command: str) -> bool:
|
|
"""Disable UNO command
|
|
|
|
:param command: UNO command to disable
|
|
:type command: str
|
|
:return: True if correctly disable, False if not.
|
|
:rtype: bool
|
|
"""
|
|
return cls._set_app_command(command, True)
|
|
|
|
@classmethod
|
|
def enabled(cls, command) -> bool:
|
|
"""Enabled UNO command
|
|
|
|
:param command: UNO command to enabled
|
|
:type command: str
|
|
:return: True if correctly disable, False if not.
|
|
:rtype: bool
|
|
"""
|
|
return cls._set_app_command(command, False)
|
|
|
|
@classmethod
|
|
def fonts(cls):
|
|
"""Get all font visibles in LibreOffice
|
|
|
|
:return: tuple of FontDescriptors
|
|
:rtype: tuple
|
|
|
|
`See API FontDescriptor <https://api.libreoffice.org/docs/idl/ref/structcom_1_1sun_1_1star_1_1awt_1_1FontDescriptor.html>`_
|
|
"""
|
|
toolkit = create_instance('com.sun.star.awt.Toolkit')
|
|
device = toolkit.createScreenCompatibleDevice(0, 0)
|
|
return device.FontDescriptors
|
|
|
|
@classmethod
|
|
def filters(cls):
|
|
"""Get all support filters
|
|
|
|
`See Help ConvertFilters <https://help.libreoffice.org/latest/en-US/text/shared/guide/convertfilters.html>`_
|
|
`See API FilterFactory <https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1FilterFactory.html>`_
|
|
"""
|
|
factory = create_instance('com.sun.star.document.FilterFactory')
|
|
rows = [data_to_dict(factory[name]) for name in factory]
|
|
for row in rows:
|
|
row['UINames'] = data_to_dict(row['UINames'])
|
|
return rows
|
|
|
|
@classmethod
|
|
def dispatch(cls, frame: Any, command: str, args: dict={}) -> None:
|
|
"""Call dispatch, used only if not exists directly in API
|
|
|
|
:param frame: doc or frame instance
|
|
:type frame: pyUno
|
|
:param command: Command to execute
|
|
:type command: str
|
|
:param args: Extra argument for command
|
|
:type args: dict
|
|
|
|
`See DispatchCommands <`See DispatchCommands <https://wiki.documentfoundation.org/Development/DispatchCommands>`_>`_
|
|
"""
|
|
dispatch = create_instance('com.sun.star.frame.DispatchHelper')
|
|
if hasattr(frame, 'frame'):
|
|
frame = frame.frame
|
|
url = command
|
|
if not command.startswith('.uno:'):
|
|
url = f'.uno:{command}'
|
|
opt = dict_to_property(args)
|
|
dispatch.executeDispatch(frame, url, '', 0, opt)
|
|
return
|
|
|
|
|
|
class ClipBoard(object):
|
|
SERVICE = 'com.sun.star.datatransfer.clipboard.SystemClipboard'
|
|
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
|
|
|
|
class TextTransferable(unohelper.Base, XTransferable):
|
|
|
|
def __init__(self, text):
|
|
df = DataFlavor()
|
|
df.MimeType = ClipBoard.CLIPBOARD_FORMAT_TEXT
|
|
df.HumanPresentableName = 'encoded text utf-16'
|
|
self.flavors = (df,)
|
|
self._data = text
|
|
|
|
def getTransferData(self, flavor):
|
|
return self._data
|
|
|
|
def getTransferDataFlavors(self):
|
|
return self.flavors
|
|
|
|
@classmethod
|
|
def set(cls, value):
|
|
ts = cls.TextTransferable(value)
|
|
sc = create_instance(cls.SERVICE)
|
|
sc.setContents(ts, None)
|
|
return
|
|
|
|
@classproperty
|
|
def contents(cls):
|
|
df = None
|
|
text = ''
|
|
sc = create_instance(cls.SERVICE)
|
|
transferable = sc.getContents()
|
|
data = transferable.getTransferDataFlavors()
|
|
for df in data:
|
|
if df.MimeType == cls.CLIPBOARD_FORMAT_TEXT:
|
|
break
|
|
if df:
|
|
text = transferable.getTransferData(df)
|
|
return text
|
|
|
|
@classmethod
|
|
def get(cls):
|
|
return cls.contents
|