Compare commits
5 Commits
Author | SHA1 | Date |
---|---|---|
Mauricio Baeza | 31ddc1a199 | |
Mauricio Baeza | fa4069030f | |
Mauricio Baeza | e5b2241008 | |
Mauricio Baeza | 37ecf06ed7 | |
Mauricio Baeza | 530b26dd82 |
|
@ -1,4 +1,8 @@
|
|||
# ---> Python
|
||||
|
||||
conf.py
|
||||
*.po~
|
||||
|
||||
# Byte-compiled / optimized / DLL files
|
||||
__pycache__/
|
||||
*.py[cod]
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
|
@ -0,0 +1,36 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<oor:component-data xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:oor="http://openoffice.org/2001/registry" oor:name="Addons" oor:package="org.openoffice.Office">
|
||||
<node oor:name="AddonUI">
|
||||
<node oor:name="OfficeMenuBar">
|
||||
<node oor:name="net.elmau.zaz.doc" oor:op="replace">
|
||||
<prop oor:name="Title" oor:type="xs:string">
|
||||
<value xml:lang="en">Zaz Doc</value>
|
||||
<value xml:lang="es">Zaz Doc</value>
|
||||
</prop>
|
||||
<prop oor:name="Target" oor:type="xs:string">
|
||||
<value>_self</value>
|
||||
</prop>
|
||||
<node oor:name="Submenu">
|
||||
<node oor:name="m0" oor:op="replace">
|
||||
<prop oor:name="Title" oor:type="xs:string">
|
||||
<value xml:lang="en">Replace styles...</value>
|
||||
<value xml:lang="es">Reemplazar estilos...</value>
|
||||
</prop>
|
||||
<prop oor:name="Context" oor:type="xs:string">
|
||||
<value>com.sun.star.text.TextDocument</value>
|
||||
</prop>
|
||||
<prop oor:name="URL" oor:type="xs:string">
|
||||
<value>service:net.elmau.zaz.doc?replacestyles</value>
|
||||
</prop>
|
||||
<prop oor:name="Target" oor:type="xs:string">
|
||||
<value>_self</value>
|
||||
</prop>
|
||||
<prop oor:name="ImageIdentifier" oor:type="xs:string">
|
||||
<value>%origin%/images/replace</value>
|
||||
</prop>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</oor:component-data>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest:manifest xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" manifest:version="1.2">
|
||||
<manifest:file-entry manifest:full-path="ZazDoc.py" manifest:media-type="application/vnd.sun.star.uno-component;type=Python"/>
|
||||
<manifest:file-entry manifest:full-path="Office/Accelerators.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
|
||||
<manifest:file-entry manifest:full-path="Addons.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
|
||||
</manifest:manifest>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<oor:component-data xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:oor="http://openoffice.org/2001/registry" oor:name="Accelerators" oor:package="org.openoffice.Office">
|
||||
<node oor:name="PrimaryKeys"/>
|
||||
</oor:component-data>
|
|
@ -0,0 +1,23 @@
|
|||
import uno
|
||||
import unohelper
|
||||
from com.sun.star.task import XJobExecutor
|
||||
import main
|
||||
|
||||
|
||||
ID_EXTENSION = 'net.elmau.zaz.doc'
|
||||
SERVICE = ('com.sun.star.task.Job',)
|
||||
|
||||
|
||||
class ZazDoc(unohelper.Base, XJobExecutor):
|
||||
|
||||
def __init__(self, ctx):
|
||||
self.ctx = ctx
|
||||
|
||||
def trigger(self, args):
|
||||
main.ID_EXTENSION = ID_EXTENSION
|
||||
main.run(args, __file__)
|
||||
return
|
||||
|
||||
|
||||
g_ImplementationHelper = unohelper.ImplementationHelper()
|
||||
g_ImplementationHelper.addImplementation(ZazDoc, ID_EXTENSION, SERVICE)
|
|
@ -0,0 +1,26 @@
|
|||
<?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="net.elmau.zaz.doc"/>
|
||||
<version value="0.1.0"/>
|
||||
<display-name>
|
||||
<name lang="en">Zaz-Doc</name>
|
||||
<name lang="es">Zaz-Doc</name>
|
||||
</display-name>
|
||||
<extension-description>
|
||||
<src lang="en" xlink:href="description/desc_en.txt"/>
|
||||
<src lang="es" xlink:href="description/desc_es.txt"/>
|
||||
</extension-description>
|
||||
<icon>
|
||||
<default xlink:href="images/zazdoc.png"/>
|
||||
</icon>
|
||||
<publisher>
|
||||
<name xlink:href="https://git.cuates.net/elmau/zaz-doc" lang="en">El Mau</name>
|
||||
<name xlink:href="https://git.cuates.net/elmau/zaz-doc" lang="es">El Mau</name>
|
||||
</publisher>
|
||||
<registration>
|
||||
<simple-license accept-by="user" suppress-on-update="true">
|
||||
<license-text xlink:href="registration/license_en.txt" lang="en"/>
|
||||
<license-text xlink:href="registration/license_es.txt" lang="es"/>
|
||||
</simple-license>
|
||||
</registration>
|
||||
</description>
|
|
@ -0,0 +1 @@
|
|||
Help for LibreOffice Documentation Teams
|
|
@ -0,0 +1 @@
|
|||
Ayuda para los equipos de documentación de LibreOffice
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 32 32"
|
||||
version="1.1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
id="svg18"
|
||||
width="32"
|
||||
height="32">
|
||||
<metadata
|
||||
id="metadata24">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title>9.4</dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs22" />
|
||||
<title
|
||||
id="title2">9.4</title>
|
||||
<desc
|
||||
id="desc4">Created with Sketch.</desc>
|
||||
<g
|
||||
stroke="none"
|
||||
stroke-width="1"
|
||||
fill="none"
|
||||
fill-rule="evenodd"
|
||||
id="g12"
|
||||
transform="scale(0.66666667)">
|
||||
<g
|
||||
fill-rule="nonzero"
|
||||
fill="#000000"
|
||||
id="g10">
|
||||
<g
|
||||
id="g8">
|
||||
<path
|
||||
d="m 26.828427,24 6.366079,6.366079 c 0.779879,0.779879 0.784376,2.039815 -0.0021,2.826309 -0.781048,0.781049 -2.047059,0.781368 -2.826309,0.0021 L 24,26.828427 17.633921,33.194506 c -0.779879,0.779879 -2.039815,0.784376 -2.826309,-0.0021 -0.781049,-0.781048 -0.781368,-2.047059 -0.0021,-2.826309 L 21.171573,24 14.805494,17.633921 c -0.779879,-0.779879 -0.784376,-2.039815 0.0021,-2.826309 0.781048,-0.781049 2.047059,-0.781368 2.826309,-0.0021 L 24,21.171573 30.366079,14.805494 c 0.779879,-0.779879 2.039815,-0.784376 2.826309,0.0021 0.781049,0.781048 0.781368,2.047059 0.0021,2.826309 z M 24,48 C 10.745166,48 0,37.254834 0,24 0,10.745166 10.745166,0 24,0 37.254834,0 48,10.745166 48,24 48,37.254834 37.254834,48 24,48 Z m 0,-4 C 35.045695,44 44,35.045695 44,24 44,12.954305 35.045695,4 24,4 12.954305,4 4,12.954305 4,24 4,35.045695 12.954305,44 24,44 Z"
|
||||
id="path6" />
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.0"
|
||||
x="0px"
|
||||
y="0px"
|
||||
viewBox="0 0 24 24"
|
||||
enable-background="new 0 0 100 100"
|
||||
xml:space="preserve"
|
||||
id="svg10"
|
||||
width="24"
|
||||
height="24"
|
||||
sodipodi:docname="ok.svg"
|
||||
inkscape:version="1.0.1 (3bc2e813f5, 2020-09-07)"><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="2560"
|
||||
inkscape:window-height="1006"
|
||||
id="namedview8"
|
||||
showgrid="false"
|
||||
inkscape:zoom="25.71875"
|
||||
inkscape:cx="9.0330528"
|
||||
inkscape:cy="13.975581"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="37"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="svg10"
|
||||
inkscape:document-rotation="0" /><metadata
|
||||
id="metadata16"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs14" /><g
|
||||
id="g20"
|
||||
transform="matrix(0.26666667,0,0,0.26666667,-1.3333334,-1.3333334)"><path
|
||||
d="M 50,5 C 25.2,5 5,25.2 5,50 5,74.8 25.2,95 50,95 74.8,95 95,74.8 95,50 95,25.2 74.8,5 50,5 Z m 0,80 C 30.7,85 15,69.3 15,50 15,30.7 30.7,15 50,15 69.3,15 85,30.7 85,50 85,69.3 69.3,85 50,85 Z"
|
||||
id="path2" /><polygon
|
||||
points="45,67.1 72.1,40 65,32.9 45,52.9 35,42.9 27.9,50 "
|
||||
id="polygon4" /></g></svg>
|
After Width: | Height: | Size: 1.9 KiB |
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
|
@ -0,0 +1,46 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2021-04-12 17:25-0500\n"
|
||||
"PO-Revision-Date: 2021-04-12 17:27-0500\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
"Last-Translator: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: en\n"
|
||||
|
||||
#: source/pythonpath/main.py:28
|
||||
msgid "Select style source"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:32
|
||||
msgid "Select style target"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:36
|
||||
msgid "Select different styles"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:40
|
||||
msgid "Replace selected styles?"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:62
|
||||
msgid "Replace Styles"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:73
|
||||
msgid "~Replace by paragraph"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:112
|
||||
msgid "~Close"
|
||||
msgstr ""
|
|
@ -0,0 +1,46 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2021-04-12 17:11-0500\n"
|
||||
"PO-Revision-Date: 2021-04-12 17:15-0500\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
"Last-Translator: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: en\n"
|
||||
|
||||
#: source/pythonpath/main.py:28
|
||||
msgid "Select style source"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:32
|
||||
msgid "Select style target"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:36
|
||||
msgid "Select different styles"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:40
|
||||
msgid "Replace style \"%s\" with style \"s%\" ?"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:62
|
||||
msgid "Replace Styles"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:73
|
||||
msgid "~Replace by paragraph"
|
||||
msgstr ""
|
||||
|
||||
#: source/pythonpath/main.py:112
|
||||
msgid "~Close"
|
||||
msgstr ""
|
|
@ -0,0 +1,49 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2021-04-12 17:25-0500\n"
|
||||
"PO-Revision-Date: 2021-04-12 17:26-0500\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
"Language: es\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
|
||||
#: source/pythonpath/main.py:28
|
||||
msgid "Select style source"
|
||||
msgstr "Seleccionar estilo origen"
|
||||
|
||||
#: source/pythonpath/main.py:32
|
||||
msgid "Select style target"
|
||||
msgstr "Seleccionar estilo destino"
|
||||
|
||||
#: source/pythonpath/main.py:36
|
||||
msgid "Select different styles"
|
||||
msgstr "Seleccionar estilos diferentes"
|
||||
|
||||
#: source/pythonpath/main.py:40
|
||||
msgid "Replace selected styles?"
|
||||
msgstr "¿Reemplazar estilos seleccionados?"
|
||||
|
||||
#: source/pythonpath/main.py:62
|
||||
msgid "Replace Styles"
|
||||
msgstr "Reemplazar Estilos"
|
||||
|
||||
#: source/pythonpath/main.py:73
|
||||
msgid "~Replace by paragraph"
|
||||
msgstr "~Reemplazar por párrafo"
|
||||
|
||||
#: source/pythonpath/main.py:112
|
||||
msgid "~Close"
|
||||
msgstr "~Cerrar"
|
||||
|
||||
#~ msgid "Replace style \"%s\" with style \"s%\" ?"
|
||||
#~ msgstr "¿Reemplazar el estilo \"%s\" con el estilo \"s%\" ?"
|
|
@ -0,0 +1,46 @@
|
|||
# SOME DESCRIPTIVE TITLE.
|
||||
# Copyright (C) YEAR ORGANIZATION
|
||||
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
|
||||
#
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"POT-Creation-Date: 2021-04-12 17:11-0500\n"
|
||||
"PO-Revision-Date: 2021-04-12 17:14-0500\n"
|
||||
"Language-Team: \n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"Generated-By: pygettext.py 1.5\n"
|
||||
"X-Generator: Poedit 2.4.1\n"
|
||||
"Last-Translator: \n"
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"Language: es\n"
|
||||
|
||||
#: source/pythonpath/main.py:28
|
||||
msgid "Select style source"
|
||||
msgstr "Seleccionar estilo origen"
|
||||
|
||||
#: source/pythonpath/main.py:32
|
||||
msgid "Select style target"
|
||||
msgstr "Seleccionar estilo destino"
|
||||
|
||||
#: source/pythonpath/main.py:36
|
||||
msgid "Select different styles"
|
||||
msgstr "Seleccionar estilos diferentes"
|
||||
|
||||
#: source/pythonpath/main.py:40
|
||||
msgid "Replace style \"%s\" with style \"s%\" ?"
|
||||
msgstr "¿Reemplazar el estilo \"%s\" con el estilo \"s%\" ?"
|
||||
|
||||
#: source/pythonpath/main.py:62
|
||||
msgid "Replace Styles"
|
||||
msgstr "Reemplazar Estilos"
|
||||
|
||||
#: source/pythonpath/main.py:73
|
||||
msgid "~Replace by paragraph"
|
||||
msgstr "~Reemplazar por párrafo"
|
||||
|
||||
#: source/pythonpath/main.py:112
|
||||
msgid "~Close"
|
||||
msgstr "~Cerrar"
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,236 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
import easymacro as app
|
||||
|
||||
|
||||
ID_EXTENSION = ''
|
||||
_ = None
|
||||
|
||||
|
||||
class Controllers(object):
|
||||
|
||||
def __init__(self, dlg):
|
||||
self.d = dlg
|
||||
self.para_styles = {}
|
||||
self.char_styles = {}
|
||||
|
||||
@app.catch_exception
|
||||
def cmd_replace_by_paragraph_action(self, event):
|
||||
source = self.d.lst_paragraph_source.value
|
||||
target = self.d.lst_paragraph_target.value
|
||||
|
||||
if not source:
|
||||
msg = _('Select style source')
|
||||
app.errorbox(msg)
|
||||
return
|
||||
if not target:
|
||||
msg = _('Select style target')
|
||||
app.errorbox(msg)
|
||||
return
|
||||
if source == target:
|
||||
msg = _('Select different styles')
|
||||
app.errorbox(msg)
|
||||
return
|
||||
|
||||
msg = _('Replace selected styles?')
|
||||
if not app.question(msg):
|
||||
return
|
||||
|
||||
source = self.para_styles[source]
|
||||
target = self.para_styles[target]
|
||||
|
||||
doc = app.active
|
||||
i = 0
|
||||
for p in doc.paragraphs:
|
||||
if not p.is_paragraph:
|
||||
continue
|
||||
if p.style == source:
|
||||
p.style = target
|
||||
i += 1
|
||||
|
||||
self.d.close()
|
||||
app.debug(f'{i} replaces')
|
||||
return
|
||||
|
||||
@app.catch_exception
|
||||
def cmd_replace_by_character_action(self, event):
|
||||
source = self.d.lst_character_source.value
|
||||
target = self.d.lst_character_target.value
|
||||
|
||||
if not source:
|
||||
msg = _('Select style source')
|
||||
app.errorbox(msg)
|
||||
return
|
||||
if not target:
|
||||
msg = _('Select style target')
|
||||
app.errorbox(msg)
|
||||
return
|
||||
if source == target:
|
||||
msg = _('Select different styles')
|
||||
app.errorbox(msg)
|
||||
return
|
||||
|
||||
msg = _('Replace selected styles?')
|
||||
if not app.question(msg):
|
||||
return
|
||||
|
||||
source = self.char_styles[source]
|
||||
target = self.char_styles[target]
|
||||
|
||||
doc = app.active
|
||||
i = 0
|
||||
for paragraph in doc.paragraphs:
|
||||
if not paragraph.is_paragraph:
|
||||
continue
|
||||
for p in paragraph:
|
||||
if p.style == source:
|
||||
p.style = target
|
||||
i += 1
|
||||
|
||||
self.d.close()
|
||||
app.debug(f'{i} replaces')
|
||||
return
|
||||
|
||||
def cmd_close_action(self, event):
|
||||
self.d.close()
|
||||
return
|
||||
|
||||
|
||||
def _create_dialog_replace_styles():
|
||||
args = {
|
||||
'Name': 'dialog',
|
||||
'Title': _('Replace Styles'),
|
||||
'Width': 220,
|
||||
'Height': 125,
|
||||
}
|
||||
dlg = app.create_dialog(args)
|
||||
dlg.id = ID_EXTENSION
|
||||
dlg.events = Controllers
|
||||
|
||||
args = {
|
||||
'Type': 'Label',
|
||||
'Name': 'lbl_paragraphs',
|
||||
'Label': _('Replace by paragraph'),
|
||||
'Width': 70,
|
||||
'Height': 10,
|
||||
'X': 10,
|
||||
'Y': 10,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'ListBox',
|
||||
'Name': 'lst_paragraph_source',
|
||||
'Width': 85,
|
||||
'Height': 15,
|
||||
'Dropdown': True,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'ListBox',
|
||||
'Name': 'lst_paragraph_target',
|
||||
'Width': 85,
|
||||
'Height': 15,
|
||||
'Dropdown': True,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'Button',
|
||||
'Name': 'cmd_replace_by_paragraph',
|
||||
'Width': 15,
|
||||
'Height': 15,
|
||||
'ImageURL': 'replace.svg',
|
||||
'ImagePosition': 1,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'Label',
|
||||
'Name': 'lbl_character',
|
||||
'Label': _('Replace by character'),
|
||||
'Width': 70,
|
||||
'Height': 10,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'ListBox',
|
||||
'Name': 'lst_character_source',
|
||||
'Width': 85,
|
||||
'Height': 15,
|
||||
'Dropdown': True,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'ListBox',
|
||||
'Name': 'lst_character_target',
|
||||
'Width': 85,
|
||||
'Height': 15,
|
||||
'Dropdown': True,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'Button',
|
||||
'Name': 'cmd_replace_by_character',
|
||||
'Width': 15,
|
||||
'Height': 15,
|
||||
'ImageURL': 'replace.svg',
|
||||
'ImagePosition': 1,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
args = {
|
||||
'Type': 'Button',
|
||||
'Name': 'cmd_close',
|
||||
'Label': _('~Close'),
|
||||
'Width': 70,
|
||||
'Height': 20,
|
||||
'ImageURL': 'close.svg',
|
||||
'ImagePosition': 1,
|
||||
}
|
||||
dlg.add_control(args)
|
||||
|
||||
dlg.lst_paragraph_source.move(dlg.lbl_paragraphs, 0, 5)
|
||||
dlg.lst_paragraph_target.move(dlg.lst_paragraph_source, 5, 0)
|
||||
dlg.cmd_replace_by_paragraph.move(dlg.lst_paragraph_target, 5, 0)
|
||||
|
||||
dlg.lbl_character.move(dlg.lst_paragraph_source, 0, 10)
|
||||
dlg.lst_character_source.move(dlg.lbl_character, 0, 5)
|
||||
dlg.lst_character_target.move(dlg.lst_character_source, 5, 0)
|
||||
dlg.cmd_replace_by_character.move(dlg.lst_character_target, 5, 0)
|
||||
|
||||
dlg.cmd_close.move(dlg.lst_character_source, 0, 15, True)
|
||||
|
||||
doc = app.active
|
||||
styles = doc.styles['Paragraph'].names
|
||||
dlg.lst_paragraph_source.data = tuple(styles.keys())
|
||||
dlg.lst_paragraph_target.data = tuple(styles.keys())
|
||||
dlg.events.para_styles = styles
|
||||
|
||||
styles = doc.styles['Character'].names
|
||||
dlg.lst_character_source.data = tuple(styles.keys())
|
||||
dlg.lst_character_target.data = tuple(styles.keys())
|
||||
dlg.events.char_styles = styles
|
||||
|
||||
return dlg
|
||||
|
||||
|
||||
def _replace_styles():
|
||||
dlg = _create_dialog_replace_styles()
|
||||
dlg.open()
|
||||
return
|
||||
|
||||
|
||||
def run(args, path_locales):
|
||||
global _
|
||||
|
||||
_ = app.install_locales(path_locales)
|
||||
|
||||
if args == 'replacestyles':
|
||||
_replace_styles()
|
||||
|
||||
return
|
|
@ -0,0 +1,14 @@
|
|||
This file is part of ZazDoc.
|
||||
|
||||
ZazDoc 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.
|
||||
|
||||
ZazDoc 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 ZazDoc. If not, see <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,14 @@
|
|||
This file is part of ZazDoc.
|
||||
|
||||
ZazDoc 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.
|
||||
|
||||
ZazDoc 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 ZazDoc. If not, see <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,822 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# == Rapid Develop Macros in LibreOffice ==
|
||||
|
||||
# ~ This file is part of ZAZ.
|
||||
|
||||
# ~ https://git.elmau.net/elmau/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 argparse
|
||||
import os
|
||||
import py_compile
|
||||
import re
|
||||
import sys
|
||||
import zipfile
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from shutil import copyfile
|
||||
from subprocess import call
|
||||
from xml.etree import ElementTree as ET
|
||||
from xml.dom.minidom import parseString
|
||||
|
||||
|
||||
from conf import (
|
||||
DATA,
|
||||
DIRS,
|
||||
DOMAIN,
|
||||
EXTENSION,
|
||||
FILES,
|
||||
INFO,
|
||||
PATHS,
|
||||
TYPE_EXTENSION,
|
||||
USE_LOCALES,
|
||||
log)
|
||||
|
||||
|
||||
EASYMACRO = 'easymacro.py'
|
||||
|
||||
|
||||
class LiboXML(object):
|
||||
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',
|
||||
}
|
||||
TYPES = {
|
||||
'py': 'application/vnd.sun.star.uno-component;type=Python',
|
||||
'pyc': 'application/binary',
|
||||
'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',
|
||||
'component': 'application/vnd.sun.star.uno-components',
|
||||
}
|
||||
NS_MANIFEST = {
|
||||
'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',
|
||||
}
|
||||
NS_DESCRIPTION = {
|
||||
'xmlns': 'http://openoffice.org/extensions/description/2006',
|
||||
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
||||
'xmlns:d': 'http://openoffice.org/extensions/description/2006',
|
||||
}
|
||||
NS_ADDONS = {
|
||||
'xmlns:xs': 'http://www.w3.org/2001/XMLSchema',
|
||||
'xmlns:oor': 'http://openoffice.org/2001/registry',
|
||||
}
|
||||
NS_UPDATE = {
|
||||
'xmlns': 'http://openoffice.org/extensions/update/2006',
|
||||
'xmlns:d': 'http://openoffice.org/extensions/description/2006',
|
||||
'xmlns:xlink': 'http://www.w3.org/1999/xlink',
|
||||
}
|
||||
|
||||
def __init__(self):
|
||||
self._manifest = None
|
||||
self._paths = []
|
||||
self._path_images = ''
|
||||
self._toolbars = []
|
||||
|
||||
def _save_path(self, attr):
|
||||
self._paths.append(attr['{{{}}}full-path'.format(self.NS_MANIFEST['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.NS_MANIFEST['manifest_version'],
|
||||
'xmlns:manifest': self.NS_MANIFEST['manifest'],
|
||||
'xmlns:loext': self.NS_MANIFEST['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.NS_MANIFEST['manifest'])
|
||||
self._manifest = ET.fromstring(data)
|
||||
attr = {'xmlns:loext': self.NS_MANIFEST['xmlns:loext']}
|
||||
self._manifest.attrib.update(**attr)
|
||||
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 new_description(self, data):
|
||||
doc = ET.Element('description', self.NS_DESCRIPTION)
|
||||
|
||||
key = 'identifier'
|
||||
ET.SubElement(doc, key, data[key])
|
||||
|
||||
key = 'version'
|
||||
ET.SubElement(doc, key, data[key])
|
||||
|
||||
key = 'display-name'
|
||||
node = ET.SubElement(doc, key)
|
||||
for k, v in data[key].items():
|
||||
sn = ET.SubElement(node, 'name', {'lang': k})
|
||||
sn.text = v
|
||||
|
||||
node = ET.SubElement(doc, 'extension-description')
|
||||
for k in data[key].keys():
|
||||
attr = {
|
||||
'lang': k,
|
||||
'xlink:href': f'description/desc_{k}.txt',
|
||||
}
|
||||
ET.SubElement(node, 'src', attr)
|
||||
|
||||
key = 'icon'
|
||||
node = ET.SubElement(doc, key)
|
||||
attr = {'xlink:href': f"images/{data[key]}"}
|
||||
ET.SubElement(node, 'default', attr)
|
||||
|
||||
key = 'publisher'
|
||||
node = ET.SubElement(doc, key)
|
||||
for k, v in data[key].items():
|
||||
attr = {
|
||||
'xlink:href': v['link'],
|
||||
'lang': k,
|
||||
}
|
||||
sn = ET.SubElement(node, 'name', attr)
|
||||
sn.text = v['text']
|
||||
|
||||
key = 'display-name'
|
||||
node = ET.SubElement(doc, 'registration')
|
||||
attr = {
|
||||
'accept-by': 'user',
|
||||
'suppress-on-update': 'true',
|
||||
}
|
||||
node = ET.SubElement(node, 'simple-license', attr)
|
||||
for k in data[key].keys():
|
||||
attr = {
|
||||
'xlink:href': f"{DIRS['registration']}/license_{k}.txt",
|
||||
'lang': k
|
||||
}
|
||||
ET.SubElement(node, 'license-text', attr)
|
||||
|
||||
if data['update']:
|
||||
node = ET.SubElement(doc, 'update-information')
|
||||
ET.SubElement(node, 'src', {'xlink:href': data['update']})
|
||||
|
||||
return self._get_xml(doc)
|
||||
|
||||
def _get_context(self, args):
|
||||
if not args:
|
||||
return ''
|
||||
context = ','.join([self.CONTEXT[v] for v in args.split(',')])
|
||||
return context
|
||||
|
||||
def _add_node_value(self, node, name, value='_self'):
|
||||
attr = {'oor:name': name, 'oor:type': 'xs:string'}
|
||||
sn = ET.SubElement(node, 'prop', attr)
|
||||
sn = ET.SubElement(sn, 'value')
|
||||
sn.text = value
|
||||
return
|
||||
|
||||
def _add_menu(self, id_extension, node, index, menu, in_menu_bar=True):
|
||||
if in_menu_bar:
|
||||
attr = {
|
||||
'oor:name': index,
|
||||
'oor:op': 'replace',
|
||||
}
|
||||
subnode = ET.SubElement(node, 'node', attr)
|
||||
else:
|
||||
subnode = node
|
||||
|
||||
attr = {'oor:name': 'Title', 'oor:type': 'xs:string'}
|
||||
sn1 = ET.SubElement(subnode, 'prop', attr)
|
||||
for k, v in menu['title'].items():
|
||||
sn2 = ET.SubElement(sn1, 'value', {'xml:lang': k})
|
||||
sn2.text = v
|
||||
value = self._get_context(menu['context'])
|
||||
self._add_node_value(subnode, 'Context', value)
|
||||
|
||||
if 'submenu' in menu:
|
||||
sn = ET.SubElement(subnode, 'node', {'oor:name': 'Submenu'})
|
||||
for i, m in enumerate(menu['submenu']):
|
||||
self._add_menu(id_extension, sn, f'{index}.s{i}', m)
|
||||
if m.get('toolbar', False):
|
||||
self._toolbars.append(m)
|
||||
return
|
||||
|
||||
value = f"service:{id_extension}?{menu['argument']}"
|
||||
self._add_node_value(subnode, 'URL', value)
|
||||
self._add_node_value(subnode, 'Target')
|
||||
value = f"%origin%/{self._path_images}/{menu['icon']}"
|
||||
self._add_node_value(subnode, 'ImageIdentifier', value)
|
||||
return
|
||||
|
||||
def new_addons(self, id_extension, data):
|
||||
in_menu_bar = data['parent'] == 'OfficeMenuBar'
|
||||
self._path_images = data['images']
|
||||
attr = {
|
||||
'oor:name': 'Addons',
|
||||
'oor:package': 'org.openoffice.Office',
|
||||
}
|
||||
attr.update(self.NS_ADDONS)
|
||||
doc = ET.Element('oor:component-data', attr)
|
||||
parent = ET.SubElement(doc, 'node', {'oor:name': 'AddonUI'})
|
||||
node = ET.SubElement(parent, 'node', {'oor:name': data['parent']})
|
||||
|
||||
op = 'fuse'
|
||||
if in_menu_bar:
|
||||
op = 'replace'
|
||||
|
||||
attr = {'oor:name': id_extension, 'oor:op': op}
|
||||
node = ET.SubElement(node, 'node', attr)
|
||||
|
||||
if in_menu_bar:
|
||||
attr = {'oor:name': 'Title', 'oor:type': 'xs:string'}
|
||||
subnode = ET.SubElement(node, 'prop', attr)
|
||||
for k, v in data['main'].items():
|
||||
sn = ET.SubElement(subnode, 'value', {'xml:lang': k})
|
||||
sn.text = v
|
||||
|
||||
self._add_node_value(node, 'Target')
|
||||
node = ET.SubElement(node, 'node', {'oor:name': 'Submenu'})
|
||||
|
||||
for i, menu in enumerate(data['menus']):
|
||||
self._add_menu(id_extension, node, f'm{i}', menu, in_menu_bar)
|
||||
if menu.get('toolbar', False):
|
||||
self._toolbars.append(menu)
|
||||
|
||||
if self._toolbars:
|
||||
attr = {'oor:name': 'OfficeToolBar'}
|
||||
toolbar = ET.SubElement(parent, 'node', attr)
|
||||
attr = {'oor:name': id_extension, 'oor:op': 'replace'}
|
||||
toolbar = ET.SubElement(toolbar, 'node', attr)
|
||||
for t, menu in enumerate(self._toolbars):
|
||||
self._add_menu(id_extension, toolbar, f't{t}', menu)
|
||||
|
||||
return self._get_xml(doc)
|
||||
|
||||
def _add_shortcut(self, node, key, id_extension, arg):
|
||||
attr = {'oor:name': key, 'oor:op': 'fuse'}
|
||||
subnode = ET.SubElement(node, 'node', attr)
|
||||
subnode = ET.SubElement(subnode, 'prop', {'oor:name': 'Command'})
|
||||
subnode = ET.SubElement(subnode, 'value', {'xml:lang': 'en-US'})
|
||||
subnode.text = f"service:{id_extension}?{arg}"
|
||||
return
|
||||
|
||||
def _get_acceleartors(self, menu):
|
||||
if 'submenu' in menu:
|
||||
for m in menu['submenu']:
|
||||
return self._get_acceleartors(m)
|
||||
|
||||
if not menu.get('shortcut', ''):
|
||||
return ''
|
||||
|
||||
return menu
|
||||
|
||||
def new_accelerators(self, id_extension, menus):
|
||||
attr = {
|
||||
'oor:name': 'Accelerators',
|
||||
'oor:package': 'org.openoffice.Office',
|
||||
}
|
||||
attr.update(self.NS_ADDONS)
|
||||
doc = ET.Element('oor:component-data', attr)
|
||||
parent = ET.SubElement(doc, 'node', {'oor:name': 'PrimaryKeys'})
|
||||
|
||||
data = []
|
||||
for m in menus:
|
||||
info = self._get_acceleartors(m)
|
||||
if info:
|
||||
data.append(info)
|
||||
|
||||
node_global = None
|
||||
node_modules = None
|
||||
for m in data:
|
||||
if m['context']:
|
||||
if node_modules is None:
|
||||
node_modules = ET.SubElement(
|
||||
parent, 'node', {'oor:name': 'Modules'})
|
||||
for app in m['context'].split(','):
|
||||
node = ET.SubElement(
|
||||
node_modules, 'node', {'oor:name': self.CONTEXT[app]})
|
||||
self._add_shortcut(
|
||||
node, m['shortcut'], id_extension, m['argument'])
|
||||
else:
|
||||
if node_global is None:
|
||||
node_global = ET.SubElement(
|
||||
parent, 'node', {'oor:name': 'Global'})
|
||||
self._add_shortcut(
|
||||
node_global, m['shortcut'], id_extension, m['argument'])
|
||||
|
||||
return self._get_xml(doc)
|
||||
|
||||
def new_update(self, extension, url_oxt):
|
||||
doc = ET.Element('description', self.NS_UPDATE)
|
||||
ET.SubElement(doc, 'identifier', {'value': extension['id']})
|
||||
ET.SubElement(doc, 'version', {'value': extension['version']})
|
||||
node = ET.SubElement(doc, 'update-download')
|
||||
ET.SubElement(node, 'src', {'xlink:href': url_oxt})
|
||||
node = ET.SubElement(doc, 'release-notes')
|
||||
return self._get_xml(doc)
|
||||
|
||||
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)
|
||||
|
||||
|
||||
def _join(*paths):
|
||||
return os.path.join(*paths)
|
||||
|
||||
|
||||
def _mkdir(path):
|
||||
return Path(path).mkdir(parents=True, exist_ok=True)
|
||||
|
||||
|
||||
def _save(path, data):
|
||||
with open(path, 'w') as f:
|
||||
f.write(data)
|
||||
return
|
||||
|
||||
|
||||
def _get_files(path, filters=''):
|
||||
paths = []
|
||||
if filters in ('*', '*.*'):
|
||||
filters = ''
|
||||
for folder, _, files in os.walk(path):
|
||||
if filters:
|
||||
pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE)
|
||||
paths += [_join(folder, f) for f in files if pattern.search(f)]
|
||||
else:
|
||||
paths += files
|
||||
return paths
|
||||
|
||||
|
||||
def _compress_oxt():
|
||||
log.info('Compress OXT extension...')
|
||||
|
||||
path_oxt = _join(DIRS['files'], FILES['oxt'])
|
||||
|
||||
z = zipfile.ZipFile(path_oxt, 'w', compression=zipfile.ZIP_DEFLATED)
|
||||
root_len = len(os.path.abspath(DIRS['source']))
|
||||
for root, dirs, files in os.walk(DIRS['source']):
|
||||
relative = os.path.abspath(root)[root_len:]
|
||||
for f in files:
|
||||
fullpath = _join(root, f)
|
||||
file_name = _join(relative, f)
|
||||
if file_name == FILES['idl']:
|
||||
continue
|
||||
z.write(fullpath, file_name, zipfile.ZIP_DEFLATED)
|
||||
z.close()
|
||||
|
||||
log.info('Extension OXT created successfully...')
|
||||
return
|
||||
|
||||
|
||||
def _install_and_test():
|
||||
path_oxt = (_join(DIRS['files'], FILES['oxt']),)
|
||||
call(PATHS['install'] + path_oxt)
|
||||
log.info('Install extension successfully...')
|
||||
log.info('Start LibreOffice...')
|
||||
call(PATHS['soffice'])
|
||||
return
|
||||
|
||||
|
||||
def _validate_new():
|
||||
path_source = DIRS['source']
|
||||
if not _exists(path_source):
|
||||
return True
|
||||
|
||||
msg = f'Path: {path_source}, exists, delete first'
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
|
||||
def _create_new_directories():
|
||||
path_source = DIRS['source']
|
||||
_mkdir(path_source)
|
||||
path = _join(path_source, DIRS['meta'])
|
||||
_mkdir(path)
|
||||
path = _join(path_source, DIRS['description'])
|
||||
_mkdir(path)
|
||||
path = _join(path_source, DIRS['images'])
|
||||
_mkdir(path)
|
||||
path = _join(path_source, DIRS['registration'])
|
||||
_mkdir(path)
|
||||
path = _join(path_source, DIRS['office'])
|
||||
_mkdir(path)
|
||||
|
||||
if FILES['easymacro'] or DIRS['pythonpath']:
|
||||
path = _join(path_source, 'pythonpath')
|
||||
_mkdir(path)
|
||||
|
||||
path = DIRS['files']
|
||||
if not _exists(path):
|
||||
_mkdir(path)
|
||||
|
||||
msg = 'Created directories...'
|
||||
log.info(msg)
|
||||
return
|
||||
|
||||
|
||||
def _create_new_files():
|
||||
path_source = DIRS['source']
|
||||
|
||||
for k, v in INFO.items():
|
||||
file_name = f'license_{k}.txt'
|
||||
path = _join(path_source, DIRS['registration'], file_name)
|
||||
_save(path, v['license'])
|
||||
|
||||
if TYPE_EXTENSION > 1:
|
||||
path = _join(path_source, FILES['idl'])
|
||||
_save(path, DATA['idl'])
|
||||
|
||||
path = _join(path_source, FILES['py'])
|
||||
_save(path, DATA['py'])
|
||||
|
||||
|
||||
msg = 'Created files...'
|
||||
log.info(msg)
|
||||
return
|
||||
|
||||
|
||||
def _validate_update():
|
||||
if TYPE_EXTENSION == 1:
|
||||
return True
|
||||
|
||||
if not _exists(PATHS['idlc']):
|
||||
msg = 'Binary: "idlc" not found'
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
if not _exists(PATHS['include']):
|
||||
msg = 'Directory: "include" not found'
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
if not _exists(PATHS['regmerge']):
|
||||
msg = 'Binary: "regmerge" not found'
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
path = _join(DIRS['source'], FILES['idl'])
|
||||
if not _exists(path):
|
||||
msg = f'File: "{FILES["idl"]}" not found'
|
||||
log.error(msg)
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def _compile_idl():
|
||||
if TYPE_EXTENSION == 1:
|
||||
return
|
||||
|
||||
log.info('Compilate IDL...')
|
||||
path_rdb = _join(DIRS['source'], FILES['rdb'])
|
||||
path_urd = _join(DIRS['source'], FILES['urd'])
|
||||
|
||||
path = _join(DIRS['source'], FILES['idl'])
|
||||
call([PATHS['idlc'], '-I', PATHS['include'], path])
|
||||
call([PATHS['regmerge'], path_rdb, '/UCR', path_urd])
|
||||
os.remove(path_urd)
|
||||
|
||||
log.info('Compilate IDL successfully...')
|
||||
return
|
||||
|
||||
|
||||
def _update_files():
|
||||
path_files = DIRS['files']
|
||||
if not _exists(path_files):
|
||||
_mkdir(path_files)
|
||||
|
||||
path_source = DIRS['source']
|
||||
|
||||
for k, v in INFO.items():
|
||||
file_name = FILES['ext_desc'].format(k)
|
||||
path = _join(path_source, DIRS['description'], file_name)
|
||||
_save(path, v['description'])
|
||||
|
||||
path_logo = EXTENSION['icon'][0]
|
||||
if _exists(path_logo):
|
||||
file_name = EXTENSION['icon'][1]
|
||||
path = _join(path_source, DIRS['images'], file_name)
|
||||
copyfile(path_logo, path)
|
||||
|
||||
files = os.listdir(DIRS['images'])
|
||||
for f in files:
|
||||
if f[-3:].lower() == 'bmp':
|
||||
source = _join(DIRS['images'], f)
|
||||
target = _join(path_source, DIRS['images'], f)
|
||||
copyfile(source, target)
|
||||
|
||||
if FILES['easymacro']:
|
||||
source = EASYMACRO
|
||||
target = _join(path_source, 'pythonpath', source)
|
||||
copyfile(source, target)
|
||||
|
||||
xml = LiboXML()
|
||||
|
||||
path = _join(path_source, DIRS['meta'], FILES['manifest'])
|
||||
data = xml.new_manifest(DATA['manifest'])
|
||||
_save(path, data)
|
||||
|
||||
path = _join(path_source, FILES['description'])
|
||||
data = xml.new_description(DATA['description'])
|
||||
_save(path, data)
|
||||
|
||||
if TYPE_EXTENSION == 1:
|
||||
path = _join(path_source, FILES['addons'])
|
||||
data = xml.new_addons(EXTENSION['id'], DATA['addons'])
|
||||
_save(path, data)
|
||||
|
||||
path = _join(path_source, DIRS['office'])
|
||||
_mkdir(path)
|
||||
path = _join(path_source, DIRS['office'], FILES['shortcut'])
|
||||
data = xml.new_accelerators(EXTENSION['id'], DATA['addons']['menus'])
|
||||
_save(path, data)
|
||||
|
||||
|
||||
if TYPE_EXTENSION == 3:
|
||||
path = _join(path_source, FILES['addin'])
|
||||
_save(path, DATA['addin'])
|
||||
|
||||
if USE_LOCALES:
|
||||
msg = "Don't forget generate DOMAIN.pot for locales"
|
||||
for lang in EXTENSION['languages']:
|
||||
path = _join(path_source, DIRS['locales'], lang, 'LC_MESSAGES')
|
||||
Path(path).mkdir(parents=True, exist_ok=True)
|
||||
log.info(msg)
|
||||
|
||||
if DATA['update']:
|
||||
path_xml = _join(path_files, FILES['update'])
|
||||
data = xml.new_update(EXTENSION, DATA['update'])
|
||||
_save(path_xml, data)
|
||||
|
||||
_compile_idl()
|
||||
return
|
||||
|
||||
|
||||
def _create():
|
||||
if not _validate_new():
|
||||
return
|
||||
|
||||
_create_new_directories()
|
||||
_create_new_files()
|
||||
_update_files()
|
||||
|
||||
msg = f"New extension: {EXTENSION['name']} make sucesfully...\n"
|
||||
msg += '\tNow, you can install and test: zaz.py -i'
|
||||
log.info(msg)
|
||||
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/'
|
||||
FILE_PYC = 'easymacro.pyc'
|
||||
|
||||
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
|
||||
|
||||
py_compile.compile(EASYMACRO, FILE_PYC)
|
||||
xml = LiboXML()
|
||||
|
||||
path_easymacro = PATH + FILE_PYC
|
||||
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(FILE_PYC, 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(FILE_PYC)
|
||||
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 _locales(args):
|
||||
if args.files:
|
||||
files = args.files.split(',')
|
||||
else:
|
||||
files = _get_files(DIRS['source'], 'py')
|
||||
paths = ' '.join([f for f in files if not EASYMACRO in f])
|
||||
path_pot = _join(DIRS['source'], DIRS['locales'], '{}.pot'.format(DOMAIN))
|
||||
call([PATHS['gettext'], '-o', path_pot, paths])
|
||||
log.info('POT generate successfully...')
|
||||
return
|
||||
|
||||
|
||||
def _update():
|
||||
path_locales = _join(DIRS['source'], DIRS['locales'])
|
||||
path_pot = _join(DIRS['source'], DIRS['locales'], '{}.pot'.format(DOMAIN))
|
||||
if not _exists(path_pot):
|
||||
log.error('Not exists file POT...')
|
||||
return
|
||||
|
||||
files = _get_files(path_locales, 'po')
|
||||
if not files:
|
||||
log.error('First, generate files PO...')
|
||||
return
|
||||
|
||||
for f in files:
|
||||
call([PATHS['msgmerge'], '-U', f, path_pot])
|
||||
log.info('\tUpdate: {}'.format(f))
|
||||
|
||||
log.info('Locales update successfully...')
|
||||
return
|
||||
|
||||
|
||||
def _new(args):
|
||||
if not args.target:
|
||||
msg = 'Add argument target: -t PATH_TARGET'
|
||||
log.error(msg)
|
||||
return
|
||||
|
||||
if not args.name:
|
||||
msg = 'Add argument name: -n name-new-extension'
|
||||
log.error(msg)
|
||||
return
|
||||
|
||||
path = _join(args.target, args.name)
|
||||
_mkdir(path)
|
||||
_mkdir(_join(path, 'files'))
|
||||
_mkdir(_join(path, 'images'))
|
||||
path_logo = 'images/pymacros.png'
|
||||
copyfile(path_logo, _join(path, 'images/logo.png'))
|
||||
copyfile('zaz.py', _join(path, 'zaz.py'))
|
||||
copyfile(EASYMACRO, _join(path, 'easymacro.py'))
|
||||
copyfile('conf.py.example', _join(path, 'conf.py'))
|
||||
|
||||
msg = 'Folders and files copy successfully for new extension.'
|
||||
log.info(msg)
|
||||
msg = f'Change to folder: {path}'
|
||||
log.info(msg)
|
||||
return
|
||||
|
||||
|
||||
def main(args):
|
||||
if args.new:
|
||||
_new(args)
|
||||
return
|
||||
|
||||
if args.update:
|
||||
_update()
|
||||
return
|
||||
|
||||
if args.locales:
|
||||
_locales(args)
|
||||
return
|
||||
|
||||
if args.embed:
|
||||
_embed(args)
|
||||
return
|
||||
|
||||
if args.create:
|
||||
_create()
|
||||
return
|
||||
|
||||
if not _validate_update():
|
||||
return
|
||||
|
||||
if not args.only_compress:
|
||||
_update_files()
|
||||
|
||||
_compress_oxt()
|
||||
|
||||
if args.install:
|
||||
_install_and_test()
|
||||
|
||||
log.info('Extension make successfully...')
|
||||
return
|
||||
|
||||
|
||||
def _process_command_line_arguments():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='Make LibreOffice extensions')
|
||||
parser.add_argument('-new', '--new', dest='new', action='store_true',
|
||||
default=False, required=False)
|
||||
parser.add_argument('-t', '--target', dest='target', default='')
|
||||
parser.add_argument('-n', '--name', dest='name', default='', required=False)
|
||||
parser.add_argument('-c', '--create', dest='create', action='store_true',
|
||||
default=False, required=False)
|
||||
parser.add_argument('-i', '--install', dest='install', 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='')
|
||||
parser.add_argument('-l', '--locales', dest='locales', action='store_true',
|
||||
default=False, required=False)
|
||||
parser.add_argument('-u', '--update', dest='update', action='store_true',
|
||||
default=False, required=False)
|
||||
parser.add_argument('-oc', '--only_compress', dest='only_compress',
|
||||
action='store_true', default=False, required=False)
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = _process_command_line_arguments()
|
||||
main(args)
|
||||
|
Loading…
Reference in New Issue