Add support for execute dialogs into documents

This commit is contained in:
Mauricio Baeza 2019-10-13 23:44:45 -05:00
parent 114d10fc14
commit cbbc7772c4
5 changed files with 225 additions and 59 deletions

View File

@ -1,3 +1,8 @@
v 0.8.0 [13-oct-2019]
- Generate manifest
- Add support for execute dialogs in documents.
v 0.7.0 [27-sep-2019]
- Add support for InputBox and documents

View File

@ -1 +1 @@
0.7.0
0.8.0

View File

@ -205,6 +205,7 @@ PATHS = {
'regmerge': '/usr/lib/libreoffice/program/regmerge',
'soffice': ('soffice', PROGRAM, FILE_TEST),
'install': ('unopkg', 'add', '-v', '-f', '-s'),
'profile': '/home/mau/.config/libreoffice/4/user',
}
@ -427,20 +428,6 @@ FILE_ADDONS = f"""<?xml version='1.0' encoding='UTF-8'?>
"""
NODE_ADDONS = '\n <manifest:file-entry manifest:full-path="Addons.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>'
if TYPE_EXTENSION > 1:
NODE_ADDONS = f'\n <manifest:file-entry manifest:full-path="{FILES["rdb"]}" manifest:media-type="application/vnd.sun.star.uno-typelibrary;type=RDB"/>'
if TYPE_EXTENSION == 3:
NODE_ADDONS += '\n <manifest:file-entry manifest:full-path="CalcAddIn.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>'
FILE_MANIFEST = f"""<?xml version="1.0" encoding="UTF-8"?>
<manifest:manifest>
<manifest:file-entry manifest:full-path="{FILES['py']}" manifest:media-type="application/vnd.sun.star.uno-component;type=Python"/>
<manifest:file-entry manifest:full-path="Office/{FILES['shortcut']}" manifest:media-type="application/vnd.sun.star.configuration-data"/>{NODE_ADDONS}
</manifest:manifest>
"""
FILE_UPDATE = ''
if URL_XML_UPDATE:
FILE_UPDATE = f"""<?xml version="1.0" encoding="UTF-8"?>
@ -625,9 +612,16 @@ FILE_SHORTCUTS = f"""<?xml version="1.0" encoding="UTF-8"?>
"""
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': FILE_MANIFEST,
'manifest': DATA_MANIFEST,
'description': FILE_DESCRIPTION,
'addons': FILE_ADDONS,
'update': FILE_UPDATE,

View File

@ -576,6 +576,10 @@ class LODocument(object):
def title(self, value):
self.obj.setTitle(value)
@property
def uid(self):
return self.obj.RuntimeUID
@property
def type(self):
return self._type_doc
@ -2656,16 +2660,21 @@ class LODialog(object):
def _create(self, properties):
path = properties.pop('Path', '')
if path:
dp = create_instance('com.sun.star.awt.DialogProvider2', True)
dp = create_instance('com.sun.star.awt.DialogProvider', True)
return dp.createDialog(_path_url(path))
if 'Library' in properties:
location = properties['Location']
if 'Location' in properties:
location = properties.get('Location', 'application')
library = properties.get('Library', 'Standard')
if location == 'user':
location = 'application'
dp = create_instance('com.sun.star.awt.DialogProvider2', True)
dp = create_instance('com.sun.star.awt.DialogProvider', True)
path = 'vnd.sun.star.script:{}.{}?location={}'.format(
properties['Library'], properties['Name'], location)
library, properties['Name'], location)
if location == 'document':
uid = get_document().uid
path = 'vnd.sun.star.tdoc:/{}/Dialogs/{}/{}.xml'.format(
uid, library, properties['Name'])
return dp.createDialog(path)
dlg = create_instance('com.sun.star.awt.UnoControlDialog', True)
@ -2678,8 +2687,20 @@ class LODialog(object):
return dlg
def _init_controls(self):
def _get_type_control(self, name):
types = {
'stardiv.Toolkit.UnoFixedTextControl': 'label',
'stardiv.Toolkit.UnoButtonControl': 'button',
'stardiv.Toolkit.UnoEditControl': 'text',
}
return types[name]
def _init_controls(self):
for control in self.obj.getControls():
tipo = self._get_type_control(control.ImplementationName)
name = control.Model.Name
control = get_custom_class(tipo, control)
setattr(self, name, control)
return
@property
@ -2699,23 +2720,8 @@ class LODialog(object):
self._connect_listeners()
def _connect_listeners(self):
return
def _add_listeners(self, control):
if self.events is None:
return
listeners = {
'addActionListener': EventsButton,
'addMouseListener': EventsMouse,
}
for key, value in listeners.items():
if hasattr(control.obj, key):
if control.type == 'grid' and key == 'addMouseListener':
control.obj.addMouseListener(EventsMouseGrid(self.events))
continue
getattr(control.obj, key)(listeners[key](self.events))
for control in self.obj.getControls():
add_listeners(self._events, control, control.Model.Name)
return
def open(self):
@ -2740,23 +2746,6 @@ class LODialog(object):
}
return services[control]
# ~ def _get_custom_class(self, tipo, obj):
# ~ classes = {
# ~ 'label': UnoLabel,
# ~ 'button': UnoButton,
# ~ 'text': UnoText,
# ~ 'listbox': UnoListBox,
# ~ 'grid': UnoGrid,
# ~ 'link': UnoLink,
# ~ 'tab': UnoTab,
# ~ 'roadmap': UnoRoadmap,
# ~ 'image': UnoImage,
# ~ 'radio': UnoRadio,
# ~ 'groupbox': UnoGroupBox,
# ~ 'tree': UnoTree,
# ~ }
# ~ return classes[tipo](obj)
def _set_column_model(self, columns):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
column_model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True)

View File

@ -19,11 +19,16 @@
import argparse
import os
import re
import sys
import zipfile
from datetime import datetime
from pathlib import Path
from shutil import copyfile
from subprocess import call
import zipfile
from xml.etree import ElementTree as ET
from xml.dom.minidom import parseString
from conf import (
DATA,
@ -37,6 +42,83 @@ from conf import (
log)
class LiboXML(object):
TYPES = {
'py': 'application/vnd.sun.star.uno-component;type=Python',
'zip': 'application/binary',
'xcu': 'application/vnd.sun.star.configuration-data',
'rdb': 'application/vnd.sun.star.uno-typelibrary;type=RDB',
'xcs': 'application/vnd.sun.star.configuration-schema',
'help': 'application/vnd.sun.star.help',
}
NAME_SPACES = {
'manifest_version': '1.2',
'manifest': 'urn:oasis:names:tc:opendocument:xmlns:manifest:1.0',
'xmlns:loext': 'urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0',
}
def __init__(self):
self._manifest = None
self._paths = []
def _save_path(self, attr):
self._paths.append(attr['{{{}}}full-path'.format(self.NAME_SPACES['manifest'])])
return
def _clean(self, name, nodes):
has_words = re.compile('\\w')
if not re.search(has_words, str(nodes.tail)):
nodes.tail = ''
if not re.search(has_words, str(nodes.text)):
nodes.text = ''
for node in nodes:
if name == 'manifest':
self._save_path(node.attrib)
if not re.search(has_words, str(node.tail)):
node.tail = ''
if not re.search(has_words, str(node.text)):
node.text = ''
return
def new_manifest(self, data):
attr = {
'manifest:version': self.NAME_SPACES['manifest_version'],
'xmlns:manifest': self.NAME_SPACES['manifest'],
'xmlns:loext': self.NAME_SPACES['xmlns:loext'],
}
self._manifest = ET.Element('manifest:manifest', attr)
return self.add_data_manifest(data)
def parse_manifest(self, data):
ET.register_namespace('manifest', self.NAME_SPACES['manifest'])
self._manifest = ET.fromstring(data)
data = {'xmlns:loext': self.NAME_SPACES['xmlns:loext']}
self._manifest.attrib.update(**data)
self._clean('manifest', self._manifest)
return
def add_data_manifest(self, data):
node_name = 'manifest:file-entry'
attr = {
'manifest:full-path': '',
'manifest:media-type': '',
}
for path in data:
if path in self._paths:
continue
ext = path.split('.')[-1]
attr['manifest:full-path'] = path
attr['manifest:media-type'] = self.TYPES.get(ext, '')
ET.SubElement(self._manifest, node_name, attr)
return self._get_xml(self._manifest)
def _get_xml(self, doc):
xml = parseString(ET.tostring(doc, encoding='utf-8'))
return xml.toprettyxml(indent=' ', encoding='utf-8').decode('utf-8')
def _exists(path):
return os.path.exists(path)
@ -222,8 +304,11 @@ def _update_files():
target = _join(path_source, 'pythonpath', source)
copyfile(source, target)
xml = LiboXML()
path = _join(path_source, DIRS['meta'], FILES['manifest'])
_save(path, DATA['manifest'])
data = xml.new_manifest(DATA['manifest'])
_save(path, data)
path = _join(path_source, DIRS['office'])
_mkdir(path)
@ -265,7 +350,96 @@ def _new():
return
def _get_info_path(path):
path, filename = os.path.split(path)
name, extension = os.path.splitext(filename)
return (path, filename, name, extension)
def _zip_embed(source, files):
PATH = 'Scripts/python/'
EASYMACRO = 'easymacro.'
p, f, name, e = _get_info_path(source)
now = datetime.now().strftime('_%Y%m%d_%H%M%S')
path_source = _join(p, name + now + e)
copyfile(source, path_source)
target = source
with zipfile.PyZipFile(EASYMACRO + 'zip', mode='w') as zf:
zf.writepy(EASYMACRO + 'py')
xml = LiboXML()
path_easymacro = PATH + EASYMACRO + 'zip'
names = [f[1] for f in files] + [path_easymacro]
nodes = []
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as zt:
with zipfile.ZipFile(path_source, compression=zipfile.ZIP_DEFLATED) as zs:
for name in zs.namelist():
if FILES['manifest'] in name:
path_manifest = name
xml_manifest = zs.open(name).read()
elif name in names:
continue
else:
zt.writestr(name, zs.open(name).read())
data = []
for path, name in files:
data.append(name)
zt.write(path, name)
zt.write(EASYMACRO + 'zip', path_easymacro)
data.append(path_easymacro)
xml.parse_manifest(xml_manifest)
xml_manifest = xml.add_data_manifest(data)
zt.writestr(path_manifest, xml_manifest)
os.unlink(EASYMACRO + 'zip')
return
def _embed(args):
PATH = 'Scripts/python'
PYTHONPATH = 'pythonpath'
doc = args.document
if not doc:
msg = '-d/--document Path file to embed is mandatory'
log.error(msg)
return
if not _exists(doc):
msg = 'Path file not exists'
log.error(msg)
return
files = []
if args.files:
files = args.files.split(',')
source = _join(PATHS['profile'], PATH)
content = os.listdir(source)
if PYTHONPATH in content:
content.remove(PYTHONPATH)
if files:
files = [(_join(source, f), _join(PATH, f)) for f in files if f in content]
else:
files = [(_join(source, f), _join(PATH, f)) for f in content]
_zip_embed(doc, files)
log.info('Embedded macros successfully...')
return
def main(args):
if args.embed:
_embed(args)
return
if args.new:
_new()
return
@ -279,7 +453,7 @@ def main(args):
if args.install:
_install_and_test()
log.info('Extension make sucesfully...')
log.info('Extension make successfully...')
return
@ -290,6 +464,10 @@ def _process_command_line_arguments():
default=False, required=False)
parser.add_argument('-n', '--new', dest='new', action='store_true',
default=False, required=False)
parser.add_argument('-e', '--embed', dest='embed', action='store_true',
default=False, required=False)
parser.add_argument('-d', '--document', dest='document', default='')
parser.add_argument('-f', '--files', dest='files', default='')
return parser.parse_args()