zaz-pip/conf.py

639 lines
18 KiB
Python

#!/usr/bin/env python3
# ~ This file is part of ZAZ.
# ~ ZAZ is free software: you can redistribute it and/or modify
# ~ it under the terms of the GNU General Public License as published by
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~ ZAZ 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. See the
# ~ GNU General Public License for more details.
# ~ You should have received a copy of the GNU General Public License
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
import logging
# ~ Type extension:
# ~ 1 = normal extension
# ~ 2 = new component
# ~ 3 = Calc addin
TYPE_EXTENSION = 1
# ~ https://semver.org/
VERSION = '0.4.0'
# ~ Your great extension name, not used spaces
NAME = 'ZAZPip'
# ~ Should be unique, used URL inverse
ID = 'net.elmau.zaz.pip'
# ~ If you extension will be multilanguage set: True
# ~ This feature used gettext, set pythonpath and easymacro in True
# ~ Yu can used PoEdit for edit PO files and generate MO files.
# ~ https://poedit.net/
USE_LOCALES = True
DOMAIN = 'base'
PATH_LOCALES = 'locales'
# ~ locate pygettext.py
PATH_PYGETTEXT = '/usr/lib/python3.7/Tools/i18n/pygettext.py'
PATH_MSGMERGE = 'msgmerge'
# ~ Show in extension manager
PUBLISHER = {
'en': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'},
'es': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'},
}
# ~ Name in this folder for copy
ICON = 'images/logo.png'
# ~ Name inside extensions
ICON_EXT = f'{NAME.lower()}.png'
# ~ For example
# ~ DEPENDENCIES_MINIMAL = '6.0'
DEPENDENCIES_MINIMAL = ''
# ~ Change for you favorite license
LICENSE_EN = f"""This file is part of {NAME}.
{NAME} is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
{NAME} 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. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with {NAME}. If not, see <https://www.gnu.org/licenses/>.
"""
LICENSE_ES = LICENSE_EN
INFO = {
'en': {
'display_name': 'ZAZ Pip',
'description': 'Pip for LibreOffice',
'license': LICENSE_EN,
},
'es': {
'display_name': 'ZAZ Pip',
'description': 'Pip para LibreOffice',
'license': LICENSE_ES,
},
}
CONTEXT = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument',
'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE',
}
# ~ Menus, only for TYPE_EXTENSION = 1
# ~ Parent can be: AddonMenu or OfficeMenuBar
# ~ For icons con name: NAME_16.bmp, used only NAME
# ~ PARENT = ''
# ~ MENU_MAIN = {}
# ~ Shortcut: Key + "Modifier Keys"
# ~ Important: Not used any shortcuts used for LibreOffice
# ~ SHIFT is mapped to Shift on all platforms.
# ~ MOD1 is mapped to Ctrl on Windows/Linux, while it is mapped to Cmd on Mac.
# ~ MOD2 is mapped to Alt on all platforms.
# ~ For example: Shift+Ctrl+Alt+T -> T_SHIFT_MOD1_MOD2
PARENT = 'AddonMenu'
# ~ MENU_MAIN = {
# ~ 'en': 'My Extension',
# ~ 'es': 'Mi Extensión',
# ~ }
MENU_MAIN = {}
MENUS = (
{
'title': {'en': 'Open Pip', 'es': 'Abrir Pip'},
'argument': 'open',
'context': '',
'icon': 'icon',
'toolbar': False,
'shortcut': 'P_SHIFT_MOD1_MOD2',
},
)
# ~ Functions, only for TYPE_EXTENSION = 3
FUNCTIONS = {
'test': {
'displayname': {'en': 'test', 'es': 'prueba'},
'description': {'en': 'My test', 'es': 'Mi prueba'},
'parameters': {
'value': {
'displayname': {'en': 'value', 'es': 'valor'},
'description': {'en': 'The value', 'es': 'El valor'},
},
},
},
}
# ~ FUNCTIONS = {}
EXTENSION = {
'version': VERSION,
'name': NAME,
'id': ID,
'icon': (ICON, ICON_EXT),
'languages': tuple(INFO.keys())
}
# ~ If used more libraries set python path in True and copy inside
# ~ If used easymacro pythonpath always is True, recommended
DIRS = {
'meta': 'META-INF',
'source': 'source',
'description': 'description',
'images': 'images',
'registration': 'registration',
'files': 'files',
'office': 'Office',
'locales': PATH_LOCALES,
'pythonpath': True,
}
FILES = {
'oxt': f'{NAME}_v{VERSION}.oxt',
'py': f'{NAME}.py',
'ext_desc': 'desc_{}.txt',
'manifest': 'manifest.xml',
'description': 'description.xml',
'idl': f'X{NAME}.idl',
'addons': 'Addons.xcu',
'urd': f'X{NAME}.urd',
'rdb': f'X{NAME}.rdb',
'update': f'{NAME.lower()}.update.xml',
'addin': 'CalcAddIn.xcu',
'shortcut': 'Accelerators.xcu',
'easymacro': True,
}
# ~ URLs for update for example
# ~ URL_XML_UPDATE = 'https://gitlab.com/USER/PROYECT/raw/BRANCH/FOLDERs/FILE_NAME'
URL_XML_UPDATE = ''
URL_OXT = ''
# ~ Default program for test: --calc, --writer, --draw
PROGRAM = '--calc'
# ~ Path to file for test
FILE_TEST = ''
PATHS = {
'idlc': '/usr/lib/libreoffice/sdk/bin/idlc',
'include': '/usr/share/idl/libreoffice',
'regmerge': '/usr/lib/libreoffice/program/regmerge',
'soffice': ('soffice', PROGRAM, FILE_TEST),
'install': ('unopkg', 'add', '-v', '-f', '-s'),
'profile': '/home/mau/.config/libreoffice/4/user',
'gettext': PATH_PYGETTEXT,
'msgmerge': PATH_MSGMERGE,
}
SERVICES = {
'job': "('com.sun.star.task.Job',)",
'addin': "('com.sun.star.sheet.AddIn',)",
}
FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
DATE = '%d/%m/%Y %H:%M:%S'
LEVEL_ERROR = logging.getLevelName(logging.ERROR)
LEVEL_INFO = logging.getLevelName(logging.INFO)
logging.addLevelName(logging.ERROR, f'\033[1;41m{LEVEL_ERROR}\033[1;0m')
logging.addLevelName(logging.INFO, f'\x1b[32m{LEVEL_INFO}\033[1;0m')
logging.basicConfig(level=logging.DEBUG, format=FORMAT, datefmt=DATE)
log = logging.getLogger(NAME)
def _methods():
template = """ def {0}(self, {1}):
print({1})
return 'ok'\n"""
functions = ''
for k, v in FUNCTIONS.items():
args = ','.join(v['parameters'].keys())
functions += template.format(k, args)
return functions
SRV = SERVICES['job']
XSRV = 'XJobExecutor'
SRV_IMPORT = f'from com.sun.star.task import {XSRV}'
METHODS = """ def trigger(self, args='pyUNO'):
print('Hello World', args)
return\n"""
if TYPE_EXTENSION > 1:
MENUS = ()
XSRV = f'X{NAME}'
SRV_IMPORT = f'from {ID} import {XSRV}'
if TYPE_EXTENSION == 2:
SRV = f"('{ID}',)"
METHODS = """ def test(self, args='pyUNO'):
print('Hello World', args)
return\n"""
elif TYPE_EXTENSION == 3:
SRV = SERVICES['addin']
METHODS = _methods()
FILE_PY = f"""import uno
import unohelper
{SRV_IMPORT}
ID_EXTENSION = '{ID}'
SERVICE = {SRV}
class {NAME}(unohelper.Base, {XSRV}):
def __init__(self, ctx):
self.ctx = ctx
{METHODS}
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation({NAME}, ID_EXTENSION, SERVICE)
"""
tmp = ' <name lang="{}">{}</name>'
node = [tmp.format(k, v['display_name']) for k, v in INFO.items()]
NODE_DISPLAY_NAME = '\n'.join(node)
tmp = ' <src lang="{0}" xlink:href="description/desc_{0}.txt" />'
node = [tmp.format(k) for k, v in INFO.items()]
NODE_EXTENSION_DESCRIPTION = '\n'.join(node)
NODE_ICON = ''
if ICON:
NODE_ICON = f' <default xlink:href="images/{ICON_EXT}" />'
NODE_PUBLISHER = ''
if PUBLISHER:
tmp = ' <name xlink:href="{}" lang="{}">{}</name>'
node = [tmp.format(v['link'], k, v['text']) for k, v in PUBLISHER.items()]
NODE_PUBLISHER = '\n'.join(node)
NODE_DEPENDENCIES_MINIMAL = ''
if DEPENDENCIES_MINIMAL:
NODE_DEPENDENCIES_MINIMAL = f"""\n <dependencies>
<OpenOffice.org-minimal-version value="{DEPENDENCIES_MINIMAL}" d:name="LibreOffice {DEPENDENCIES_MINIMAL}"/>
</dependencies>"""
tmp = ' <license-text xlink:href="{0}/license_{1}.txt" lang="{1}" />'
node = [tmp.format(DIRS['registration'], k) for k in INFO.keys()]
NODE_LICENSE = '\n'.join(node)
NODE_UPDATE = ''
if URL_XML_UPDATE:
NODE_UPDATE = f"""
<update-information>
<src xlink:href="{URL_XML_UPDATE}" />
</update-information>"""
FILE_DESCRIPTION = f"""<?xml version='1.0' encoding='UTF-8'?>
<description xmlns="http://openoffice.org/extensions/description/2006" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:d="http://openoffice.org/extensions/description/2006">
<identifier value="{ID}" />
<version value="{VERSION}" />
<display-name>
{NODE_DISPLAY_NAME}
</display-name>
<extension-description>
{NODE_EXTENSION_DESCRIPTION}
</extension-description>
<icon>
{NODE_ICON}
</icon>
<publisher>
{NODE_PUBLISHER}
</publisher>
<registration>
<simple-license accept-by="user" suppress-on-update="true" >
{NODE_LICENSE}
</simple-license>
</registration>{NODE_DEPENDENCIES_MINIMAL}{NODE_UPDATE}
</description>
"""
NODE_MENU = """ <node oor:name="{id}.{index}" oor:op="{opt}">
<prop oor:name="Title" oor:type="xs:string">
{titles}
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:{id}?{argument}</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value>{context}</value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value>%origin%/{folder}/{icon}</value>
</prop>
</node>"""
opt = 'fuse'
if PARENT == 'OfficeMenuBar':
opt = 'replace'
def _get_context(args):
if not args:
return ''
c = []
for v in args.split(','):
c.append(CONTEXT[v])
return ','.join(c)
menus = []
toolbar = []
tmp = ' <value xml:lang="{}">{}</value>'
for i, m in enumerate(MENUS):
titles = [tmp.format(k, v) for k, v in m['title'].items()]
values = {
'id': ID,
'index': i+101,
'opt': opt,
'titles': '\n'.join(titles),
'argument': m['argument'],
'context': _get_context(m['context']),
'folder': DIRS['images'],
'icon': m['icon'],
}
menus.append(NODE_MENU.format(**values))
if m['toolbar']:
values['index'] = f't{i+1}'
toolbar.append(NODE_MENU.format(**values))
NODE_TOOLBAR = ''
NODE_MENUS = ''
if TYPE_EXTENSION == 1:
if PARENT == 'AddonMenu':
NODE_MENUS = '\n'.join(menus)
elif PARENT == 'OfficeMenuBar':
tmp = ' <value xml:lang="{}">{}</value>'
titles = '\n'.join([tmp.format(k, v) for k, v in MENU_MAIN.items()])
SUBMENUS = '<node oor:name="Submenu">\n ' + '\n'.join(menus) + '\n </node>'
NODE_MENUS = f""" <node oor:name="{ID}" oor:op="replace">
<prop oor:name="Title" oor:type="xs:string">
{titles}
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
{SUBMENUS}
</node>"""
if toolbar:
node_toolbars = '\n'.join(toolbar)
NODE_TOOLBAR = f""" <node oor:name="OfficeToolBar">
<node oor:name="{ID}" oor:op="replace">
{node_toolbars}
</node>
</node>"""
FILE_ADDONS = f"""<?xml version='1.0' encoding='UTF-8'?>
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="Addons" oor:package="org.openoffice.Office">
<node oor:name="AddonUI">
<node oor:name="{PARENT}">
{NODE_MENUS}
</node>
{NODE_TOOLBAR}
</node>
</oor:component-data>
"""
FILE_UPDATE = ''
if URL_XML_UPDATE:
FILE_UPDATE = f"""<?xml version="1.0" encoding="UTF-8"?>
<description
xmlns="http://openoffice.org/extensions/update/2006"
xmlns:d="http://openoffice.org/extensions/description/2006"
xmlns:xlink="http://www.w3.org/1999/xlink">
<identifier value="{ID}" />
<version value="{VERSION}" />
<update-download>
<src xlink:href="{URL_OXT}"/>
</update-download>
<release-notes>
</release-notes>
</description>"""
def _functions():
a = '[in] any {}'
t = ' any {}({});'
f = ''
for k, v in FUNCTIONS.items():
args = ','.join([a.format(k) for k, v in v['parameters'].items()])
f += t.format(k, args)
return f
FILE_IDL = ''
if TYPE_EXTENSION > 1:
id_ext = ID.replace('.', '_')
interface = f'X{NAME}'
module = ''
for i, P in enumerate(ID.split('.')):
module += f'module {P} {{ '
close_module = '}; ' * (i + 1)
functions = ' void test([in] any argument);'
if TYPE_EXTENSION == 3:
functions = _functions()
FILE_IDL = f"""#ifndef __{id_ext}_idl__
#define __{id_ext}_idl__
#include <com/sun/star/uno/XInterface.idl>
{module}
interface {interface} : com::sun::star::uno::XInterface
{{
{functions}
}};
service {P} {{
interface {interface};
}};
{close_module}
#endif
"""
def _parameters(args):
NODE = """ <node oor:name="{name}" oor:op="replace">
<prop oor:name="DisplayName">
{displayname}
</prop>
<prop oor:name="Description">
{description}
</prop>
</node>"""
line = '{}<value xml:lang="{}">{}</value>'
node = ''
for k, v in args.items():
displayname = '\n'.join(
[line.format(' ' * 16, k, v) for k, v in v['displayname'].items()])
description = '\n'.join(
[line.format(' ' * 16, k, v) for k, v in v['description'].items()])
values = {
'name': k,
'displayname': displayname,
'description': description,
}
node += NODE.format(**values)
return node
NODE_FUNCTIONS = ''
if TYPE_EXTENSION == 3:
tmp = '{}<value xml:lang="{}">{}</value>'
NODE_FUNCTION = """ <node oor:name="{name}" oor:op="replace">
<prop oor:name="DisplayName">
{displayname}
</prop>
<prop oor:name="Description">
{description}
</prop>
<prop oor:name="Category">
<value>Add-In</value>
</prop>
<prop oor:name="CompatibilityName">
<value xml:lang="en">AutoAddIn.{name}</value>
</prop>
<node oor:name="Parameters">
{parameters}
</node>
</node>"""
for k, v in FUNCTIONS.items():
displayname = '\n'.join(
[tmp.format(' ' * 12, k, v) for k, v in v['displayname'].items()])
description = '\n'.join(
[tmp.format(' ' * 12, k, v) for k, v in v['description'].items()])
parameters = _parameters(v['parameters'])
values = {
'name': k,
'displayname': displayname,
'description': description,
'parameters': parameters,
}
NODE_FUNCTIONS += NODE_FUNCTION.format(**values)
FILE_ADDIN = f"""<?xml version="1.0" encoding="UTF-8"?>
<oor:component-data xmlns:oor="http://openoffice.org/2001/registry" xmlns:xs="http://www.w3.org/2001/XMLSchema" oor:name="CalcAddIns" oor:package="org.openoffice.Office">
<node oor:name="AddInInfo">
<node oor:name="{ID}" oor:op="replace">
<node oor:name="AddInFunctions">
{NODE_FUNCTIONS}
</node>
</node>
</node>
</oor:component-data>"""
NODE_SHORTCUT = """ {0}<node oor:name="{1}" oor:op="fuse">
{0}<prop oor:name="Command">
{0}<value xml:lang="en-US">service:{2}?{3}</value>
{0}</prop>
{0}</node>
"""
NODE_SHORTCUTS = ''
if TYPE_EXTENSION == 1:
node_global = []
node_module = {}
for m in MENUS:
if not m.get('shortcut', ''):
continue
if m['context']:
for c in m['context'].split(','):
if not c in node_module:
node_module[c] = []
node = NODE_SHORTCUT.format(' ', m['shortcut'], ID, m['argument'])
node_module[c].append(node)
continue
node = NODE_SHORTCUT.format('', m['shortcut'], ID, m['argument'])
node_global.append(node)
if node_global:
NODE_SHORTCUTS = ' <node oor:name="Global">\n'
NODE_SHORTCUTS += '\n'.join(node_global)
NODE_SHORTCUTS += ' </node>'
if node_module:
NODE_SHORTCUTS += ' <node oor:name="Modules">\n'
for c, n in node_module.items():
NODE_SHORTCUTS += ' <node oor:name="{}">\n'.format(CONTEXT[c])
NODE_SHORTCUTS += '\n'.join(n)
NODE_SHORTCUTS += ' </node>\n'
NODE_SHORTCUTS += ' </node>'
FILE_SHORTCUTS = f"""<?xml version="1.0" encoding="UTF-8"?>
<oor:component-data
xmlns:oor="http://openoffice.org/2001/registry"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
oor:name="Accelerators"
oor:package="org.openoffice.Office">
<node oor:name="PrimaryKeys">
{NODE_SHORTCUTS}
</node>
</oor:component-data>
"""
DATA_MANIFEST = [FILES['py'], f"Office/{FILES['shortcut']}", 'Addons.xcu']
if TYPE_EXTENSION > 1:
DATA_MANIFEST.append(FILES['rdb'])
if TYPE_EXTENSION == 3:
DATA_MANIFEST.append('CalcAddIn.xcu')
DATA = {
'py': FILE_PY,
'manifest': DATA_MANIFEST,
'description': FILE_DESCRIPTION,
'addons': FILE_ADDONS,
'update': FILE_UPDATE,
'idl': FILE_IDL,
'addin': FILE_ADDIN,
'shortcut': FILE_SHORTCUTS,
}
# ~ LICENSE_ACCEPT_BY = 'user' # or admin
# ~ LICENSE_SUPPRESS_ON_UPDATE = True