Compare commits

...

24 Commits

Author SHA1 Message Date
Mauricio Baeza 5419cf889a Fix in main function 2021-06-27 21:22:55 -05:00
Mauricio Baeza d877a5f1e6 Update changelog 2021-06-27 21:22:29 -05:00
Mauricio Baeza 8e71d33841 Update changelog 2021-06-27 21:19:20 -05:00
Mauricio Baeza fbcee7a833 Fix in open files 2021-01-06 22:22:22 -06:00
Mauricio Baeza 844f9e971e Fix in open files 2021-01-06 22:21:56 -06:00
Mauricio Baeza 54fd0c1199 Update easymacro, separate main. 2020-12-27 19:31:17 -06:00
Mauricio Baeza 0c0ec54b25 Data from grid 2020-12-27 19:29:09 -06:00
Mauricio Baeza 25733e81cf Insert menus 2020-12-27 19:25:32 -06:00
Mauricio Baeza 60409ceeb3 Set and get paths 2020-12-26 22:21:23 -06:00
Mauricio Baeza 5c522ebcc4 Remove row 2020-12-25 20:50:41 -06:00
Mauricio Baeza b0ecdad5fe Add image in cell 2020-12-25 20:23:45 -06:00
Mauricio Baeza b8d37ed834 Add row data 2020-12-25 20:07:26 -06:00
Mauricio Baeza 8ec4b025ac Add image delete 2020-12-25 19:39:51 -06:00
Mauricio Baeza 705af2ea73 Clean ZAZFavorites 2020-12-24 21:52:58 -06:00
Mauricio Baeza a5b1eeb831 Replace image png for svg 2020-12-24 21:42:48 -06:00
Mauricio Baeza cff22afb63 Create main module 2020-12-24 21:26:05 -06:00
Mauricio Baeza 5d7c642903 Update easymacro 2020-12-24 21:09:08 -06:00
Mauricio Baeza 3659b4ff0c Update zaz 2020-12-24 21:06:18 -06:00
Mauricio Baeza 55867ef436 Start version 6 2020-12-24 20:55:42 -06:00
Mauricio Baeza acbba382c6 Update easymacro 2019-11-22 19:35:23 -06:00
Mauricio Baeza 7111cca36b Update easymacro 2019-11-22 19:34:48 -06:00
Mauricio Baeza 772667a5bf Fix error in main 2019-10-23 22:10:57 -05:00
Mauricio Baeza a600b12dc4 Fix error in main 2019-10-23 22:10:23 -05:00
Mauricio Baeza 611bd77a89 Open in default frame 2019-10-15 19:15:07 -05:00
25 changed files with 11946 additions and 6493 deletions

View File

@ -1,15 +1,29 @@
v 0.7.0 [27-jun-2021]
- Update easymacro.py
v 0.6.0 [20-dec-2020]
- Update easymacro.py
- Separate main
v 0.5.0 [22-nov-2019]
- Update easymacro.py
v 0.4.0 [23-oct-2019]
- Fix error in main
v 0.3.0 [15-oct-2019]
---------------------
- Add menus in start application.
- Update easymacro.py
v 0.2.0 [27-sep-2019]
---------------------
- Update easymacro.py
v 0.1.0 [19-sep-2019]
---------------------
- Initial version

View File

@ -5,19 +5,26 @@ Extension for set your favorite files in LibreOffice, with Python, of course.
Thanks!
https://gitlab.com/mauriciobaeza/zaz
### Software libre, no gratis
This extension have a cost of maintenance of 1 euro every year.
BCH: `1RPLWHJW34p7pMQV1ft4x7eWhAYw69Dsb`
BTC: `3Fe4JuADrAK8Qs7GDAxbSXR8E54avwZJLW`
PayPal :( donate ATT elmau DOT net
* https://git.cuates.net/elmau/zaz
## Free Software, not gratis software
* [Look the wiki](https://gitlab.com/mauriciobaeza/zaz-favorite/wikis/home)
* [Mira la wiki](https://gitlab.com/mauriciobaeza/zaz-favorite/wikis/home_es)
### If you don't have money, no problem, send me a postcard from your city :)
#### but, don't make the mistake of many of *thinking only in gratis software* that so much damage has done to **Free Software**.
This extension have a cost of maintenance of 1 euros every year.
BCH: `qztd3l00xle5tffdqvh2snvadkuau2ml0uqm4n875d`
MONERO: `48ygK6NuuNAfwWfeAbeK8YP2ryPqxMAsUZBnfAhaHVkoHZ48JRYiQKKFd5GFMmaytyUpfxnyA95pWavjRgPUuKv1TUTvhkd`
BTC: `3FhiXcXmAesmQzrNEngjHFnvaJRhU1AGWV`
ETH: `0x61a4f614a30ff686445751ed8328b82b77ecfc69`
LTC: `MBcgQ3LQJA4W2wsXknTdm2fxRSysLaBJHS`
You have others cryptos, welcome too!

View File

@ -1,2 +1 @@
0.3.0
0.7.0

278
conf.py
View File

@ -25,24 +25,25 @@ import logging
# ~ 3 = Calc addin
TYPE_EXTENSION = 1
# ~ https://semver.org/
VERSION = '0.3.0'
# ~ Your great extension name, not used spaces
NAME = 'ZAZFavorites'
# ~ https://semver.org/
VERSION = '0.7.0'
# ~ Should be unique, used URL inverse
ID = 'net.elmau.zaz.Favorites'
# ~ 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.
# ~ You 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_PYGETTEXT = '/usr/lib/python3.9/Tools/i18n/pygettext.py'
# ~ You can use PoEdit for update locales too
PATH_MSGMERGE = 'msgmerge'
@ -57,9 +58,6 @@ 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}.
@ -93,17 +91,6 @@ INFO = {
}
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
@ -206,6 +193,8 @@ PATHS = {
'soffice': ('soffice', PROGRAM, FILE_TEST),
'install': ('unopkg', 'add', '-v', '-f', '-s'),
'profile': '/home/mau/.config/libreoffice/4/user',
'gettext': PATH_PYGETTEXT,
'msgmerge': PATH_MSGMERGE,
}
@ -257,7 +246,7 @@ elif TYPE_EXTENSION == 3:
METHODS = _methods()
FILE_PY = f"""import uno
DATA_PY = f"""import uno
import unohelper
{SRV_IMPORT}
@ -277,177 +266,6 @@ 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 {}({});'
@ -564,72 +382,42 @@ FILE_ADDIN = f"""<?xml version="1.0" encoding="UTF-8"?>
</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_DESCRIPTION = {
'identifier': {'value': ID},
'version': {'value': VERSION},
'display-name': {k: v['display_name'] for k, v in INFO.items()},
'icon': ICON_EXT,
'publisher': PUBLISHER,
'update': URL_XML_UPDATE,
}
DATA_ADDONS = {
'parent': PARENT,
'images': DIRS['images'],
'main': MENU_MAIN,
'menus': MENUS,
}
DATA = {
'py': FILE_PY,
'py': DATA_PY,
'manifest': DATA_MANIFEST,
'description': FILE_DESCRIPTION,
'addons': FILE_ADDONS,
'update': FILE_UPDATE,
'description': DATA_DESCRIPTION,
'addons': DATA_ADDONS,
'update': URL_OXT,
'idl': FILE_IDL,
'addin': FILE_ADDIN,
'shortcut': FILE_SHORTCUTS,
}
with open('VERSION', 'w') as f:
f.write(VERSION)
# ~ LICENSE_ACCEPT_BY = 'user' # or admin
# ~ LICENSE_SUPPRESS_ON_UPDATE = True

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

93
images/add.svg Normal file
View File

@ -0,0 +1,93 @@
<?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"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 32 32"
xml:space="preserve"
width="32"
height="32"><metadata
id="metadata45"><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></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs43" />
<g
id="g8"
transform="scale(2)">
<g
id="g6">
<path
d="M 8,0 C 3.589,0 0,3.589 0,8 c 0,4.411 3.589,8 8,8 4.411,0 8,-3.589 8,-8 C 16,3.589 12.411,0 8,0 Z M 8,14 C 4.691,14 2,11.309 2,8 2,4.691 4.691,2 8,2 c 3.309,0 6,2.691 6,6 0,3.309 -2.691,6 -6,6 z"
id="path2" />
<polygon
points="9,7 9,4 7,4 7,7 4,7 4,9 7,9 7,12 9,12 9,9 12,9 12,7 "
id="polygon4" />
</g>
</g>
<g
id="g10"
transform="translate(8,8)">
</g>
<g
id="g12"
transform="translate(8,8)">
</g>
<g
id="g14"
transform="translate(8,8)">
</g>
<g
id="g16"
transform="translate(8,8)">
</g>
<g
id="g18"
transform="translate(8,8)">
</g>
<g
id="g20"
transform="translate(8,8)">
</g>
<g
id="g22"
transform="translate(8,8)">
</g>
<g
id="g24"
transform="translate(8,8)">
</g>
<g
id="g26"
transform="translate(8,8)">
</g>
<g
id="g28"
transform="translate(8,8)">
</g>
<g
id="g30"
transform="translate(8,8)">
</g>
<g
id="g32"
transform="translate(8,8)">
</g>
<g
id="g34"
transform="translate(8,8)">
</g>
<g
id="g36"
transform="translate(8,8)">
</g>
<g
id="g38"
transform="translate(8,8)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1,26 +1,25 @@
<?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">
<?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="AddonMenu">
<node oor:name="net.elmau.zaz.Favorites.101" oor:op="fuse">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Favorites...</value>
<value xml:lang="es">Favoritos...</value>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:net.elmau.zaz.Favorites?config</value>
</prop>
<prop oor:name="Target" oor:type="xs:string">
<value>_self</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value></value>
</prop>
<prop oor:name="ImageIdentifier" oor:type="xs:string">
<value>%origin%/images/favorite</value>
</prop>
</node>
<node oor:name="net.elmau.zaz.Favorites" oor:op="fuse">
<prop oor:name="Title" oor:type="xs:string">
<value xml:lang="en">Favorites...</value>
<value xml:lang="es">Favoritos...</value>
</prop>
<prop oor:name="Context" oor:type="xs:string">
<value/>
</prop>
<prop oor:name="URL" oor:type="xs:string">
<value>service:net.elmau.zaz.Favorites?config</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/favorite</value>
</prop>
</node>
</node>
</node>
</node>
</oor:component-data>

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest:manifest manifest:version="1.2" xmlns:loext="urn:org:documentfoundation:names:experimental:office:xmlns:loext:1.0" xmlns:manifest="urn:oasis:names:tc:opendocument:xmlns:manifest:1.0">
<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="ZAZFavorites.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"/>

View File

@ -1,9 +1,5 @@
<?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">
<?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">
<node oor:name="Global">
<node oor:name="F_SHIFT_MOD1_MOD2" oor:op="fuse">

View File

@ -1,196 +1,23 @@
import gettext
import uno
import unohelper
from com.sun.star.task import XJobExecutor
import easymacro as app
import main
ID_EXTENSION = 'net.elmau.zaz.Favorites'
SERVICE = ('com.sun.star.task.Job',)
p, *_ = app.get_info_path(__file__)
path_locales = app.join(p, 'locales')
try:
lang = gettext.translation('base', path_locales, languages=[app.LANG])
lang.install()
_ = lang.gettext
except Exception as e:
from gettext import gettext as _
app.error(e)
app.debug(app.LANGUAGE)
class Controllers(object):
def __init__(self, dlg):
self.d = dlg
self.paths = []
def button_add_action(self, event):
path = app.get_file()
if not path:
return
if path in self.paths:
msg = _('Path previously added')
app.msgbox(msg, self.TITLE)
return
self.paths.append(path)
p, filename, n, e = app.get_info_path(path)
self.d.grid.add_row((filename, '', path))
self.d.grid.set_cell_tooltip(0, self.d.grid.rows-1, p)
self.d.grid.sort(0)
return
def button_save_action(self, event):
msg = _('¿Want you save your favorites?')
if not app.question(msg, self.TITLE):
return
base = 'service:net.elmau.zaz.Favorites?{}'
paths = []
submenus = []
for row in range(self.d.grid.rows):
label = '{}. {}'.format(row + 1, self.d.grid[0, row])
path = self.d.grid[2, row]
paths.append(path)
path = app._path_url(path)
sm = {'Label': label, 'CommandURL': base.format(path)}
submenus.append(sm)
sm = {'Label': '-'}
submenus.append(sm)
sm = {'Label': _('Favorites...'), 'CommandURL': base.format('config')}
submenus.append(sm)
command = 'menu.zaz.favorites'
data = {
'Label': 'Favorites',
'CommandURL': command,
'After': '.uno:RecentFileList',
'Submenu': submenus,
}
for doc_type in ('main', 'calc', 'writer'):
app.remove_menu(doc_type, 'File', command)
app.insert_menu(doc_type, 'File', **data)
app.set_config('paths', paths)
self.d.close(1)
msg = _('Favorites saved correctly')
app.msgbox(msg, self.TITLE)
return
def grid_click(self, event):
col = self.d.grid.column
row = self.d.grid.row
if col != 1:
return
msg = _('¿Want you delete this file?')
if app.question(msg, self.TITLE):
path = self.d.grid[2, row]
self.paths.remove(path)
self.d.grid.remove_row(row)
return
def grid_double_click(self, event):
col = self.d.grid.column
row = self.d.grid.row
if col == -1 or row == -1 or col != 0:
return
app.msgbox(self.d.grid.get_cell_tooltip(col, row))
return
class ZAZFavorites(unohelper.Base, XJobExecutor):
cmd = 'service:' + ID_EXTENSION + '?'
IMAGES = 'images'
TITLE = _('ZAZ Favorites')
def __init__(self, ctx):
self.ctx = ctx
self.path_ext = app.get_path_extension(ID_EXTENSION)
self.IMAGES = app.join(self.path_ext, self.IMAGES)
def trigger(self, args):
if args == 'config':
return self._config()
app.open_doc(args)
main.ID_EXTENSION = ID_EXTENSION
main.run(args, __file__)
return
def _config(self):
dlg = self._create_dialog()
dlg.open()
return
def _create_dialog(self):
args = {
'Name': 'dialog',
'Title': self.TITLE,
'Width': 160,
'Height': 160,
}
dlg = app.create_dialog(args)
dlg.events = Controllers(dlg)
dlg.events.TITLE = self.TITLE
args = {
'Type': 'Button',
'Name': 'button_add',
'Label': _('~Add'),
'ImageURL': app.join(self.IMAGES, 'add.png'),
'ImagePosition': 1,
'Width': 60,
'Height': 15,
'Y': 5,
'FocusOnClick': False,
}
dlg.add_control(args)
columns = (
{'Title': _('File Name'), 'MaxWidth': 120},
{'Title': 'X', 'HorizontalAlign': 1, 'Resizeable': False, 'ColumnWidth': 10},
{'Title': '', 'Resizeable': False, 'ColumnWidth': 0},
)
args = {
'Type': 'Grid',
'Name': 'grid',
'Width': 140,
'Height': 110,
'ShowRowHeader': True,
'Columns': columns
}
dlg.add_control(args)
dlg.grid.set_column_image(1, app.join(self.IMAGES, 'delete.png'))
paths = app.get_config('paths', [])
for path in paths:
p, filename, n, e = app.get_info_path(path)
dlg.grid.add_row((filename, '', path))
dlg.grid.set_cell_tooltip(0, dlg.grid.rows-1, p)
dlg.events.paths = paths
args = {
'Type': 'Button',
'Name': 'button_save',
'Label': _('~Save'),
'ImageURL': app.join(self.IMAGES, 'save.png'),
'ImagePosition': 1,
'Width': 60,
'Height': 15,
}
dlg.add_control(args)
dlg.button_add.center()
dlg.grid.move(dlg.button_add)
dlg.grid.center()
dlg.button_save.move(dlg.grid)
dlg.button_save.center()
return dlg
g_ImplementationHelper = unohelper.ImplementationHelper()
g_ImplementationHelper.addImplementation(ZAZFavorites, ID_EXTENSION, SERVICE)

View File

@ -1,26 +1,26 @@
<?xml version='1.0' encoding='UTF-8'?>
<?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.Favorites" />
<version value="0.3.0" />
<identifier value="net.elmau.zaz.Favorites"/>
<version value="0.7.0"/>
<display-name>
<name lang="en">Favorites files</name>
<name lang="es">Archivos favoritos</name>
</display-name>
<extension-description>
<src lang="en" xlink:href="description/desc_en.txt" />
<src lang="es" xlink:href="description/desc_es.txt" />
<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/zazfavorites.png" />
<default xlink:href="images/zazfavorites.png"/>
</icon>
<publisher>
<name xlink:href="https://gitlab.com/mauriciobaeza" lang="en">El Mau</name>
<name xlink:href="https://gitlab.com/mauriciobaeza" 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 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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 B

93
source/images/add.svg Normal file
View File

@ -0,0 +1,93 @@
<?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"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 32 32"
xml:space="preserve"
width="32"
height="32"><metadata
id="metadata45"><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></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs43" />
<g
id="g8"
transform="scale(2)">
<g
id="g6">
<path
d="M 8,0 C 3.589,0 0,3.589 0,8 c 0,4.411 3.589,8 8,8 4.411,0 8,-3.589 8,-8 C 16,3.589 12.411,0 8,0 Z M 8,14 C 4.691,14 2,11.309 2,8 2,4.691 4.691,2 8,2 c 3.309,0 6,2.691 6,6 0,3.309 -2.691,6 -6,6 z"
id="path2" />
<polygon
points="9,7 9,4 7,4 7,7 4,7 4,9 7,9 7,12 9,12 9,9 12,9 12,7 "
id="polygon4" />
</g>
</g>
<g
id="g10"
transform="translate(8,8)">
</g>
<g
id="g12"
transform="translate(8,8)">
</g>
<g
id="g14"
transform="translate(8,8)">
</g>
<g
id="g16"
transform="translate(8,8)">
</g>
<g
id="g18"
transform="translate(8,8)">
</g>
<g
id="g20"
transform="translate(8,8)">
</g>
<g
id="g22"
transform="translate(8,8)">
</g>
<g
id="g24"
transform="translate(8,8)">
</g>
<g
id="g26"
transform="translate(8,8)">
</g>
<g
id="g28"
transform="translate(8,8)">
</g>
<g
id="g30"
transform="translate(8,8)">
</g>
<g
id="g32"
transform="translate(8,8)">
</g>
<g
id="g34"
transform="translate(8,8)">
</g>
<g
id="g36"
transform="translate(8,8)">
</g>
<g
id="g38"
transform="translate(8,8)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

114
source/images/delete.svg Normal file
View File

@ -0,0 +1,114 @@
<?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.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 24 24"
xml:space="preserve"
width="24"
height="24"
sodipodi:docname="delete.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="namedview23"
showgrid="false"
inkscape:zoom="37.189883"
inkscape:cx="12.986066"
inkscape:cy="9.5133673"
inkscape:window-x="0"
inkscape:window-y="37"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1"
inkscape:document-rotation="0" /><metadata
id="metadata43"><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="defs41" />
<g
id="g6"
transform="matrix(0.03252033,0,0,0.0650398,4.033515,-4.2925925)">
<g
id="g4">
<path
d="M 465.064,207.562 H 26.908 C 12.076,207.562 0,219.698 0,234.53 v 22.804 c 0,14.832 12.072,27.104 26.908,27.104 h 438.156 c 14.84,0 26.936,-12.272 26.936,-27.104 V 234.53 c 0,-14.832 -12.096,-26.968 -26.936,-26.968 z"
id="path2" />
</g>
</g>
<g
id="g8"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g10"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g12"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g14"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g16"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g18"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g20"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g22"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g24"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g26"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g28"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g30"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g32"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g34"
transform="translate(-230.00021,-243.5)">
</g>
<g
id="g36"
transform="translate(-230.00021,-243.5)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 233 B

99
source/images/save.svg Normal file
View File

@ -0,0 +1,99 @@
<?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"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 32 31.999998"
xml:space="preserve"
width="32"
height="31.999998"><metadata
id="metadata49"><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></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs47" />
<g
id="g12"
transform="matrix(0.06584362,0,0,0.06585717,0,-0.00329286)">
<g
id="g10">
<path
d="m 473.7,485.75 c 6.8,0 12.3,-5.5 12.3,-12.3 v -359.8 c 0,-3.6 -1.6,-7 -4.3,-9.3 L 363,2.85 c -0.2,-0.2 -0.4,-0.3 -0.6,-0.4 -0.3,-0.2 -0.5,-0.4 -0.8,-0.6 -0.4,-0.2 -0.7,-0.4 -1.1,-0.6 -0.3,-0.1 -0.6,-0.3 -0.9,-0.4 -0.4,-0.2 -0.9,-0.3 -1.3,-0.4 -0.3,-0.1 -0.6,-0.2 -0.9,-0.2 -0.8,-0.1 -1.5,-0.2 -2.3,-0.2 H 12.3 C 5.5,0.05 0,5.55 0,12.35 v 461.3 c 0,6.8 5.5,12.3 12.3,12.3 h 461.4 z m -89.2,-24.5 h -283 v -184.1 c 0,-3.7 3,-6.6 6.6,-6.6 h 269.8 c 3.7,0 6.6,3 6.6,6.6 z M 161.8,24.45 h 180.9 v 127.8 c 0,0.8 -0.6,1.4 -1.4,1.4 h -178 c -0.8,0 -1.4,-0.7 -1.4,-1.4 V 24.45 Z m -137.2,0 h 112.8 v 127.8 c 0,14.3 11.6,25.9 25.9,25.9 h 178 c 14.3,0 25.9,-11.6 25.9,-25.9 V 38.75 l 94.2,80.6 v 341.9 H 409 v -184.1 c 0,-17.2 -14,-31.1 -31.1,-31.1 H 108.1 c -17.2,0 -31.1,14 -31.1,31.1 v 184.2 H 24.6 Z"
id="path2" />
<path
d="m 227.4,77.65 h 53.8 v 32.6 c 0,6.8 5.5,12.3 12.3,12.3 6.8,0 12.3,-5.5 12.3,-12.3 v -44.8 c 0,-6.8 -5.5,-12.3 -12.3,-12.3 h -66.1 c -6.8,0 -12.3,5.5 -12.3,12.3 0,6.8 5.6,12.2 12.3,12.2 z"
id="path4" />
<path
d="m 304.5,322.85 h -123 c -6.8,0 -12.3,5.5 -12.3,12.3 0,6.8 5.5,12.3 12.3,12.3 h 123 c 6.8,0 12.3,-5.5 12.3,-12.3 0,-6.8 -5.5,-12.3 -12.3,-12.3 z"
id="path6" />
<path
d="m 304.5,387.75 h -123 c -6.8,0 -12.3,5.5 -12.3,12.3 0,6.8 5.5,12.3 12.3,12.3 h 123 c 6.8,0 12.3,-5.5 12.3,-12.3 0,-6.8 -5.5,-12.3 -12.3,-12.3 z"
id="path8" />
</g>
</g>
<g
id="g14"
transform="translate(-227,-227)">
</g>
<g
id="g16"
transform="translate(-227,-227)">
</g>
<g
id="g18"
transform="translate(-227,-227)">
</g>
<g
id="g20"
transform="translate(-227,-227)">
</g>
<g
id="g22"
transform="translate(-227,-227)">
</g>
<g
id="g24"
transform="translate(-227,-227)">
</g>
<g
id="g26"
transform="translate(-227,-227)">
</g>
<g
id="g28"
transform="translate(-227,-227)">
</g>
<g
id="g30"
transform="translate(-227,-227)">
</g>
<g
id="g32"
transform="translate(-227,-227)">
</g>
<g
id="g34"
transform="translate(-227,-227)">
</g>
<g
id="g36"
transform="translate(-227,-227)">
</g>
<g
id="g38"
transform="translate(-227,-227)">
</g>
<g
id="g40"
transform="translate(-227,-227)">
</g>
<g
id="g42"
transform="translate(-227,-227)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

File diff suppressed because it is too large Load Diff

192
source/pythonpath/main.py Normal file
View File

@ -0,0 +1,192 @@
#!/usr/bin/env python3
import easymacro as app
ID_EXTENSION = ''
_ = None
PREFIX = 'fav'
class Controllers(object):
TITLE = ''
def __init__(self, dlg):
self.d = dlg
self.paths = []
def button_add_action(self, event):
path = app.paths.get_file()
if not path:
return
if path in self.paths:
msg = _('Path previously added')
app.msgbox(msg, self.TITLE)
return
self.paths.append(path)
p = app.paths(path)
path_img = app.paths.join(self.d.path, 'images/delete.svg')
image = app.paths.image(path_img)
self.d.grid.add_row((p.file_name, image, path))
self.d.grid.set_cell_tooltip(0, self.d.grid.row_count-1, p.path)
self.d.grid.sort(0)
return
def button_save_action(self, event):
msg = _('Want you save your favorites files?')
if not app.question(msg, self.TITLE):
return
base = f'service:{ID_EXTENSION}?{{}}'
submenus = []
for i in range(self.d.grid.row_count):
name = self.d.grid[0, i]
path = self.d.grid[2, i]
label = f'{i+1}. {name}'
url = app.paths.to_url(path)
sm = {'Label': label, 'CommandURL': base.format(url)}
submenus.append(sm)
sm = {'Label': '-'}
submenus.append(sm)
sm = {'Label': _('Favorites...'), 'CommandURL': base.format('config')}
submenus.append(sm)
command = 'menu.zaz.favorites'
data = {
'Label': 'Favorites',
'CommandURL': command,
'After': '.uno:RecentFileList',
'Submenu': submenus,
}
for application in ('main', 'calc', 'writer'):
app.menus[application].insert('file', data)
app.set_config('paths', self.paths, PREFIX)
self.d.close(1)
msg = _('Favorites saved correctly')
app.msgbox(msg, self.TITLE)
return
def grid_click(self, event):
if not self.d.grid.is_valid:
return
col = self.d.grid.column
row = self.d.grid.row
if col != 1:
return
file_name = self.d.grid[0, row]
path = self.d.grid[2, row]
msg = _('Want you delete this file?\n\n')
if app.question(msg + file_name, self.TITLE):
self.paths.remove(path)
self.d.grid.remove_row(row)
return
def grid_double_click(self, event):
if not self.d.grid.is_valid:
return
col = self.d.grid.column
row = self.d.grid.row
if col != 0:
return
app.msgbox(self.d.grid.get_cell_tooltip(col, row))
return
def _config():
dlg = _create_dialog()
path_img = app.paths.join(dlg.path, 'images/delete.svg')
image = app.paths.image(path_img)
paths = app.get_config('paths', prefix=PREFIX)
for i, path in enumerate(paths):
p = app.paths(path)
dlg.grid.add_row((p.file_name, image, path))
dlg.grid.set_cell_tooltip(0, i, p.path)
if paths:
dlg.grid.sort(0)
dlg.events.paths = paths
dlg.open()
return
def _create_dialog():
TITLE = _('ZAZ Favorites')
args = {
'Name': 'dialog',
'Title': TITLE,
'Width': 160,
'Height': 170,
}
dlg = app.create_dialog(args)
dlg.id = ID_EXTENSION
dlg.events = Controllers
dlg.events.TITLE = TITLE
args = {
'Type': 'Button',
'Name': 'button_add',
'Label': _('~Add'),
'ImageURL': 'add.svg',
'ImagePosition': 1,
'Width': 60,
'Height': 20,
'Y': 5,
}
dlg.add_control(args)
columns = (
{'Title': _('File Name'), 'MaxWidth': 120},
{'Title': '-', 'HorizontalAlign': 1, 'Resizeable': False, 'ColumnWidth': 10},
{'Title': '', 'Resizeable': False, 'ColumnWidth': 0},
)
args = {
'Type': 'Grid',
'Name': 'grid',
'Width': 140,
'Height': 110,
'ShowRowHeader': True,
}
grid = dlg.add_control(args)
grid.columns = columns
args = {
'Type': 'Button',
'Name': 'button_save',
'Label': _('~Save'),
'ImageURL': 'save.svg',
'ImagePosition': 1,
'Width': 60,
'Height': 20,
}
dlg.add_control(args)
dlg.button_add.center()
dlg.grid.move(dlg.button_add)
dlg.button_save.move(dlg.grid)
dlg.grid.center()
dlg.button_save.center()
return dlg
def run(args, path_locales):
global _
_ = app.install_locales(path_locales)
if args == 'config':
_config()
return
app.docs.open(args)
return

426
zaz.py Normal file → Executable file
View File

@ -4,6 +4,8 @@
# ~ 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
@ -19,6 +21,7 @@
import argparse
import os
import py_compile
import re
import sys
import zipfile
@ -33,6 +36,7 @@ from xml.dom.minidom import parseString
from conf import (
DATA,
DIRS,
DOMAIN,
EXTENSION,
FILES,
INFO,
@ -42,9 +46,22 @@ from conf import (
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',
@ -52,18 +69,34 @@ class LiboXML(object):
'help': 'application/vnd.sun.star.help',
'component': 'application/vnd.sun.star.uno-components',
}
NAME_SPACES = {
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.NAME_SPACES['manifest'])])
self._paths.append(attr['{{{}}}full-path'.format(self.NS_MANIFEST['manifest'])])
return
def _clean(self, name, nodes):
@ -85,18 +118,18 @@ class LiboXML(object):
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'],
'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.NAME_SPACES['manifest'])
ET.register_namespace('manifest', self.NS_MANIFEST['manifest'])
self._manifest = ET.fromstring(data)
data = {'xmlns:loext': self.NAME_SPACES['xmlns:loext']}
self._manifest.attrib.update(**data)
attr = {'xmlns:loext': self.NS_MANIFEST['xmlns:loext']}
self._manifest.attrib.update(**attr)
self._clean('manifest', self._manifest)
return
@ -115,6 +148,217 @@ class LiboXML(object):
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')
@ -138,14 +382,23 @@ def _save(path, 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 = DIRS['files']
if not _exists(path):
_mkdir(path)
path_oxt = _join(path, FILES['oxt'])
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']))
@ -159,10 +412,6 @@ def _compress_oxt():
z.write(fullpath, file_name, zipfile.ZIP_DEFLATED)
z.close()
if DATA['update']:
path_xml = _join(path, FILES['update'])
_save(path_xml, DATA['update'])
log.info('Extension OXT created sucesfully...')
return
@ -280,6 +529,10 @@ def _compile_idl():
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():
@ -301,7 +554,7 @@ def _update_files():
copyfile(source, target)
if FILES['easymacro']:
source = 'easymacro.py'
source = EASYMACRO
target = _join(path_source, 'pythonpath', source)
copyfile(source, target)
@ -311,16 +564,21 @@ def _update_files():
data = xml.new_manifest(DATA['manifest'])
_save(path, data)
path = _join(path_source, DIRS['office'])
_mkdir(path)
path = _join(path_source, DIRS['office'], FILES['shortcut'])
_save(path, DATA['shortcut'])
path = _join(path_source, FILES['addons'])
_save(path, DATA['addons'])
path = _join(path_source, FILES['description'])
_save(path, DATA['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'])
@ -328,16 +586,21 @@ def _update_files():
if USE_LOCALES:
msg = "Don't forget generate DOMAIN.pot for locales"
log.info(msg)
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 _new():
def _create():
if not _validate_new():
return
@ -359,7 +622,7 @@ def _get_info_path(path):
def _zip_embed(source, files):
PATH = 'Scripts/python/'
EASYMACRO = 'easymacro.'
FILE_PYC = 'easymacro.pyc'
p, f, name, e = _get_info_path(source)
now = datetime.now().strftime('_%Y%m%d_%H%M%S')
@ -367,12 +630,10 @@ def _zip_embed(source, files):
copyfile(source, path_source)
target = source
with zipfile.PyZipFile(EASYMACRO + 'zip', mode='w') as zf:
zf.writepy(EASYMACRO + 'py')
py_compile.compile(EASYMACRO, FILE_PYC)
xml = LiboXML()
path_easymacro = PATH + EASYMACRO + 'zip'
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:
@ -391,14 +652,14 @@ def _zip_embed(source, files):
data.append(name)
zt.write(path, name)
zt.write(EASYMACRO + 'zip', path_easymacro)
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(EASYMACRO + 'zip')
os.unlink(FILE_PYC)
return
@ -435,20 +696,93 @@ def _embed(args):
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.new:
_new()
if args.create:
_create()
return
if not _validate_update():
return
_update_files()
if not args.only_compress:
_update_files()
_compress_oxt()
if args.install:
@ -461,14 +795,24 @@ def main(args):
def _process_command_line_arguments():
parser = argparse.ArgumentParser(
description='Make LibreOffice extensions')
parser.add_argument('-i', '--install', dest='install', action='store_true',
parser.add_argument('-new', '--new', dest='new', action='store_true',
default=False, required=False)
parser.add_argument('-n', '--new', dest='new', action='store_true',
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()