commit
51158ba8ea
|
@ -1,3 +1,7 @@
|
||||||
|
v 0.4.0 [14-sep-2019]
|
||||||
|
---------------------
|
||||||
|
- Add support for locales
|
||||||
|
|
||||||
v 0.3.0 [10-sep-2019]
|
v 0.3.0 [10-sep-2019]
|
||||||
---------------------
|
---------------------
|
||||||
- Add support for dialogs
|
- Add support for dialogs
|
||||||
|
|
1
TODO.md
1
TODO.md
|
@ -3,3 +3,4 @@
|
||||||
* Configuration
|
* Configuration
|
||||||
* Option panel
|
* Option panel
|
||||||
* Sub-menus
|
* Sub-menus
|
||||||
|
* Panel lateral
|
||||||
|
|
|
@ -23,7 +23,7 @@ import logging
|
||||||
# ~ 1 = normal extension
|
# ~ 1 = normal extension
|
||||||
# ~ 2 = new component
|
# ~ 2 = new component
|
||||||
# ~ 3 = Calc addin
|
# ~ 3 = Calc addin
|
||||||
TYPE_EXTENSION = 2
|
TYPE_EXTENSION = 1
|
||||||
|
|
||||||
# ~ https://semver.org/
|
# ~ https://semver.org/
|
||||||
VERSION = '0.1.0'
|
VERSION = '0.1.0'
|
||||||
|
@ -34,6 +34,12 @@ NAME = 'TestMacro'
|
||||||
# ~ Should be unique, used URL inverse
|
# ~ Should be unique, used URL inverse
|
||||||
ID = 'org.myextension.test'
|
ID = 'org.myextension.test'
|
||||||
|
|
||||||
|
# ~ If you extension will be multilanguage set: True
|
||||||
|
# ~ This feature used gettext, set pythonpath and easymacro in True
|
||||||
|
USE_LOCALES = True
|
||||||
|
DOMAIN = 'base'
|
||||||
|
PATH_LOCALES = 'locales'
|
||||||
|
|
||||||
PUBLISHER = {
|
PUBLISHER = {
|
||||||
'en': {'text': 'El Mau', 'link': 'https://elmau.net'},
|
'en': {'text': 'El Mau', 'link': 'https://elmau.net'},
|
||||||
'es': {'text': 'El Mau', 'link': 'https://elmau.net'},
|
'es': {'text': 'El Mau', 'link': 'https://elmau.net'},
|
||||||
|
@ -131,11 +137,12 @@ EXTENSION = {
|
||||||
'name': NAME,
|
'name': NAME,
|
||||||
'id': ID,
|
'id': ID,
|
||||||
'icon': (ICON, ICON_EXT),
|
'icon': (ICON, ICON_EXT),
|
||||||
|
'languages': tuple(INFO.keys())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
# ~ If used more libraries set python path in True and copy inside
|
# ~ If used more libraries set python path in True and copy inside
|
||||||
# ~ If used easymacro pythonpath always is True
|
# ~ If used easymacro pythonpath always is True, recommended
|
||||||
DIRS = {
|
DIRS = {
|
||||||
'meta': 'META-INF',
|
'meta': 'META-INF',
|
||||||
'source': 'source',
|
'source': 'source',
|
||||||
|
@ -143,7 +150,8 @@ DIRS = {
|
||||||
'images': 'images',
|
'images': 'images',
|
||||||
'registration': 'registration',
|
'registration': 'registration',
|
||||||
'files': 'files',
|
'files': 'files',
|
||||||
'pythonpath': False,
|
'pythonpath': True,
|
||||||
|
'locales': PATH_LOCALES,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -335,6 +343,8 @@ if PARENT == 'OfficeMenuBar':
|
||||||
|
|
||||||
|
|
||||||
def _get_context(args):
|
def _get_context(args):
|
||||||
|
if not args:
|
||||||
|
return ''
|
||||||
c = []
|
c = []
|
||||||
for v in args.split(','):
|
for v in args.split(','):
|
||||||
c.append(CONTEXT[v])
|
c.append(CONTEXT[v])
|
||||||
|
|
|
@ -18,15 +18,26 @@
|
||||||
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
|
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
|
||||||
|
import ctypes
|
||||||
|
import datetime
|
||||||
|
import errno
|
||||||
import getpass
|
import getpass
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
|
import shlex
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import tempfile
|
import tempfile
|
||||||
import threading
|
import threading
|
||||||
import time
|
import time
|
||||||
|
import zipfile
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
from pathlib import Path, PurePath
|
||||||
|
from pprint import pprint
|
||||||
|
from subprocess import PIPE
|
||||||
|
|
||||||
import uno
|
import uno
|
||||||
import unohelper
|
import unohelper
|
||||||
|
@ -35,6 +46,7 @@ from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
|
||||||
from com.sun.star.awt.MessageBoxResults import YES
|
from com.sun.star.awt.MessageBoxResults import YES
|
||||||
from com.sun.star.awt.PosSize import POSSIZE, SIZE
|
from com.sun.star.awt.PosSize import POSSIZE, SIZE
|
||||||
from com.sun.star.awt import Size, Point
|
from com.sun.star.awt import Size, Point
|
||||||
|
from com.sun.star.datatransfer import XTransferable, DataFlavor
|
||||||
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
|
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
|
||||||
|
|
||||||
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
|
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
|
||||||
|
@ -66,11 +78,18 @@ logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
OS = sys.platform
|
OS = platform.system()
|
||||||
USER = getpass.getuser()
|
USER = getpass.getuser()
|
||||||
PC = platform.node()
|
PC = platform.node()
|
||||||
|
DESKTOP = os.environ.get('DESKTOP_SESSION', '')
|
||||||
|
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
|
||||||
|
|
||||||
WIN = 'win32'
|
IS_WIN = OS == 'Windows'
|
||||||
|
LOG_NAME = 'ZAZ'
|
||||||
|
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
|
||||||
|
|
||||||
|
CALC = 'calc'
|
||||||
|
WRITER = 'writer'
|
||||||
OBJ_CELL = 'ScCellObj'
|
OBJ_CELL = 'ScCellObj'
|
||||||
OBJ_RANGE = 'ScCellRangeObj'
|
OBJ_RANGE = 'ScCellRangeObj'
|
||||||
OBJ_RANGES = 'ScCellRangesObj'
|
OBJ_RANGES = 'ScCellRangesObj'
|
||||||
|
@ -105,10 +124,9 @@ def _get_config(key, node_name):
|
||||||
|
|
||||||
|
|
||||||
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
|
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
|
||||||
|
LANG = LANGUAGE.split('-')[0]
|
||||||
NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product')
|
NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product')
|
||||||
VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product')
|
VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product')
|
||||||
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(
|
|
||||||
sys.version, platform.platform(), '\n'.join(sys.path))
|
|
||||||
|
|
||||||
|
|
||||||
def mri(obj):
|
def mri(obj):
|
||||||
|
@ -122,17 +140,6 @@ def mri(obj):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def debug(*info):
|
|
||||||
for i in info:
|
|
||||||
log.debug(i)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def error(info):
|
|
||||||
log.error(info)
|
|
||||||
return
|
|
||||||
|
|
||||||
|
|
||||||
def catch_exception(f):
|
def catch_exception(f):
|
||||||
@wraps(f)
|
@wraps(f)
|
||||||
def func(*args, **kwargs):
|
def func(*args, **kwargs):
|
||||||
|
@ -143,6 +150,51 @@ def catch_exception(f):
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
|
class LogWin(object):
|
||||||
|
|
||||||
|
def __init__(self, doc):
|
||||||
|
self.doc = doc
|
||||||
|
|
||||||
|
def write(self, info):
|
||||||
|
text = self.doc.Text
|
||||||
|
cursor = text.createTextCursor()
|
||||||
|
cursor.gotoEnd(False)
|
||||||
|
text.insertString(cursor, str(info), 0)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def info(data):
|
||||||
|
log.info(data)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def debug(info):
|
||||||
|
if IS_WIN:
|
||||||
|
# ~ app = LOApp(self.ctx, self.sm, self.desktop, self.toolkit)
|
||||||
|
# ~ doc = app.getDoc(FILE_NAME_DEBUG)
|
||||||
|
# ~ if not doc:
|
||||||
|
# ~ doc = app.newDoc(WRITER)
|
||||||
|
# ~ out = OutputDoc(doc)
|
||||||
|
# ~ sys.stdout = out
|
||||||
|
pprint(info)
|
||||||
|
return
|
||||||
|
|
||||||
|
log.debug(info)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def error(info):
|
||||||
|
log.error(info)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def save_log(path, data):
|
||||||
|
with open(path, 'a') as out:
|
||||||
|
out.write('{} -{}- '.format(str(datetime.now())[:19], LOG_NAME))
|
||||||
|
pprint(data, stream=out)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
def run_in_thread(fn):
|
def run_in_thread(fn):
|
||||||
def run(*k, **kw):
|
def run(*k, **kw):
|
||||||
t = threading.Thread(target=fn, args=k, kwargs=kw)
|
t = threading.Thread(target=fn, args=k, kwargs=kw)
|
||||||
|
@ -190,6 +242,10 @@ def get_desktop():
|
||||||
return create_instance('com.sun.star.frame.Desktop', True)
|
return create_instance('com.sun.star.frame.Desktop', True)
|
||||||
|
|
||||||
|
|
||||||
|
def get_dispatch():
|
||||||
|
return create_instance('com.sun.star.frame.DispatchHelper')
|
||||||
|
|
||||||
|
|
||||||
def get_temp_file():
|
def get_temp_file():
|
||||||
return tempfile.NamedTemporaryFile()
|
return tempfile.NamedTemporaryFile()
|
||||||
|
|
||||||
|
@ -206,6 +262,20 @@ def _path_system(path):
|
||||||
return path
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def exists_app(name):
|
||||||
|
try:
|
||||||
|
dn = subprocess.DEVNULL
|
||||||
|
subprocess.Popen([name, ''], stdout=dn, stderr=dn).terminate()
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def exists(path):
|
||||||
|
return Path(path).exists()
|
||||||
|
|
||||||
|
|
||||||
def get_type_doc(obj):
|
def get_type_doc(obj):
|
||||||
services = {
|
services = {
|
||||||
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
|
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
|
||||||
|
@ -253,6 +323,10 @@ class LODocument(object):
|
||||||
def title(self):
|
def title(self):
|
||||||
return self.obj.getTitle()
|
return self.obj.getTitle()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def frame(self):
|
||||||
|
return self._cc.getFrame()
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def is_saved(self):
|
def is_saved(self):
|
||||||
return self.obj.hasLocation()
|
return self.obj.hasLocation()
|
||||||
|
@ -289,10 +363,29 @@ class LODocument(object):
|
||||||
obj = self.obj.createInstance(name)
|
obj = self.obj.createInstance(name)
|
||||||
return obj
|
return obj
|
||||||
|
|
||||||
|
def save(self, path='', **kwargs):
|
||||||
|
opt = _properties(kwargs)
|
||||||
|
if path:
|
||||||
|
self._obj.storeAsURL(_path_url(path), opt)
|
||||||
|
else:
|
||||||
|
self._obj.store()
|
||||||
|
return True
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.obj.close(True)
|
self.obj.close(True)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def focus(self):
|
||||||
|
w = self._cc.getFrame().getComponentWindow()
|
||||||
|
w.setFocus()
|
||||||
|
return
|
||||||
|
|
||||||
|
def paste(self):
|
||||||
|
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
|
||||||
|
transferable = sc.getContents()
|
||||||
|
self._cc.insertTransferable(transferable)
|
||||||
|
return self.obj.getCurrentSelection()
|
||||||
|
|
||||||
|
|
||||||
class LOCalc(LODocument):
|
class LOCalc(LODocument):
|
||||||
|
|
||||||
|
@ -325,9 +418,14 @@ class LOCalc(LODocument):
|
||||||
cell = LOCellRange(self.active[index].obj, self)
|
cell = LOCellRange(self.active[index].obj, self)
|
||||||
return cell
|
return cell
|
||||||
|
|
||||||
# ~ def create_instance(self, name):
|
def select(self, rango):
|
||||||
# ~ obj = self.obj.createInstance(name)
|
r = rango
|
||||||
# ~ return obj
|
if hasattr(rango, 'obj'):
|
||||||
|
r = rango.obj
|
||||||
|
elif isinstance(rango, str):
|
||||||
|
r = self.get_cell(rango).obj
|
||||||
|
self._cc.select(r)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class LOCalcSheet(object):
|
class LOCalcSheet(object):
|
||||||
|
@ -476,6 +574,11 @@ class LOBasicIde(LODocument):
|
||||||
def __init__(self, obj):
|
def __init__(self, obj):
|
||||||
super().__init__(obj)
|
super().__init__(obj)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def selection(self):
|
||||||
|
sel = self._cc.getSelection()
|
||||||
|
return sel
|
||||||
|
|
||||||
|
|
||||||
class LOCellRange(object):
|
class LOCellRange(object):
|
||||||
|
|
||||||
|
@ -599,6 +702,10 @@ class LOCellRange(object):
|
||||||
img.setSize(Size(w, h))
|
img.setSize(Size(w, h))
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def select(self):
|
||||||
|
self.doc._cc.select(self.obj)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class EventsListenerBase(unohelper.Base, XEventListener):
|
class EventsListenerBase(unohelper.Base, XEventListener):
|
||||||
|
|
||||||
|
@ -1002,7 +1109,6 @@ def create_dialog(properties):
|
||||||
return LODialog(properties)
|
return LODialog(properties)
|
||||||
|
|
||||||
|
|
||||||
@catch_exception
|
|
||||||
def set_properties(model, properties):
|
def set_properties(model, properties):
|
||||||
if 'X' in properties:
|
if 'X' in properties:
|
||||||
properties['PositionX'] = properties.pop('X')
|
properties['PositionX'] = properties.pop('X')
|
||||||
|
@ -1109,7 +1215,13 @@ def inputbox(message, default='', title=TITLE):
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
def open(path, **kwargs):
|
def new_doc(type_doc=CALC):
|
||||||
|
path = 'private:factory/s{}'.format(type_doc)
|
||||||
|
doc = get_desktop().loadComponentFromURL(path, '_default', 0, ())
|
||||||
|
return _get_class_doc(doc)
|
||||||
|
|
||||||
|
|
||||||
|
def open_doc(path, **kwargs):
|
||||||
""" Open document in path
|
""" Open document in path
|
||||||
Usually options:
|
Usually options:
|
||||||
Hidden: True or False
|
Hidden: True or False
|
||||||
|
@ -1118,6 +1230,9 @@ def open(path, **kwargs):
|
||||||
Password: super_secret
|
Password: super_secret
|
||||||
MacroExecutionMode: 4 = Activate macros
|
MacroExecutionMode: 4 = Activate macros
|
||||||
Preview: True or False
|
Preview: True or False
|
||||||
|
|
||||||
|
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1frame_1_1XComponentLoader.html
|
||||||
|
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
|
||||||
"""
|
"""
|
||||||
path = _path_url(path)
|
path = _path_url(path)
|
||||||
opt = _properties(kwargs)
|
opt = _properties(kwargs)
|
||||||
|
@ -1129,12 +1244,251 @@ def open(path, **kwargs):
|
||||||
|
|
||||||
|
|
||||||
def open_file(path):
|
def open_file(path):
|
||||||
if OS == WIN:
|
if IS_WIN:
|
||||||
os.startfile(path)
|
os.startfile(path)
|
||||||
else:
|
else:
|
||||||
subprocess.call(['xdg-open', path])
|
subprocess.Popen(['xdg-open', path])
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
def join(*paths):
|
def join(*paths):
|
||||||
return os.path.join(*paths)
|
return os.path.join(*paths)
|
||||||
|
|
||||||
|
|
||||||
|
def is_dir(path):
|
||||||
|
return Path(path).is_dir()
|
||||||
|
|
||||||
|
|
||||||
|
def is_file(path):
|
||||||
|
return Path(path).is_file()
|
||||||
|
|
||||||
|
|
||||||
|
def get_file_size(path):
|
||||||
|
return Path(path).stat().st_size
|
||||||
|
|
||||||
|
|
||||||
|
def is_created(path):
|
||||||
|
return is_file(path) and bool(get_file_size(path))
|
||||||
|
|
||||||
|
|
||||||
|
def replace_ext(path, ext):
|
||||||
|
path, _, name, _ = get_info_path(path)
|
||||||
|
return '{}/{}.{}'.format(path, name, ext)
|
||||||
|
|
||||||
|
|
||||||
|
def zip_names(path):
|
||||||
|
with zipfile.ZipFile(path) as z:
|
||||||
|
names = z.namelist()
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def run(command, wait=False):
|
||||||
|
# ~ debug(command)
|
||||||
|
# ~ debug(shlex.split(command))
|
||||||
|
try:
|
||||||
|
if wait:
|
||||||
|
# ~ p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
|
||||||
|
# ~ p.wait()
|
||||||
|
result = subprocess.check_output(command, shell=True)
|
||||||
|
else:
|
||||||
|
p = subprocess.Popen(shlex.split(command), stdin=None,
|
||||||
|
stdout=None, stderr=None, close_fds=True)
|
||||||
|
result, er = p.communicate()
|
||||||
|
except subprocess.CalledProcessError as e:
|
||||||
|
msg = ("run [ERROR]: output = %s, error code = %s\n"
|
||||||
|
% (e.output, e.returncode))
|
||||||
|
error(msg)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if result is None:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return result.decode()
|
||||||
|
|
||||||
|
|
||||||
|
def _zippwd(source, target, pwd):
|
||||||
|
if IS_WIN:
|
||||||
|
return False
|
||||||
|
if not exists_app('zip'):
|
||||||
|
return False
|
||||||
|
|
||||||
|
cmd = 'zip'
|
||||||
|
opt = '-j '
|
||||||
|
args = "{} --password {} ".format(cmd, pwd)
|
||||||
|
|
||||||
|
if isinstance(source, (tuple, list)):
|
||||||
|
if not target:
|
||||||
|
return False
|
||||||
|
args += opt + target + ' ' + ' '.join(source)
|
||||||
|
else:
|
||||||
|
if is_file(source) and not target:
|
||||||
|
target = replace_ext(source, 'zip')
|
||||||
|
elif is_dir(source) and not target:
|
||||||
|
target = join(PurePath(source).parent,
|
||||||
|
'{}.zip'.format(PurePath(source).name))
|
||||||
|
opt = '-r '
|
||||||
|
args += opt + target + ' ' + source
|
||||||
|
|
||||||
|
result = run(args, True)
|
||||||
|
if not result:
|
||||||
|
return False
|
||||||
|
|
||||||
|
return is_created(target)
|
||||||
|
|
||||||
|
|
||||||
|
def zip(source, target='', mode='w', pwd=''):
|
||||||
|
if pwd:
|
||||||
|
return _zippwd(source, target, pwd)
|
||||||
|
|
||||||
|
if isinstance(source, (tuple, list)):
|
||||||
|
if not target:
|
||||||
|
return False
|
||||||
|
|
||||||
|
with zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED) as z:
|
||||||
|
for path in source:
|
||||||
|
_, name, _, _ = get_info_path(path)
|
||||||
|
z.write(path, name)
|
||||||
|
|
||||||
|
return is_created(target)
|
||||||
|
|
||||||
|
if is_file(source):
|
||||||
|
if not target:
|
||||||
|
target = replace_ext(source, 'zip')
|
||||||
|
z = zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED)
|
||||||
|
_, name, _, _ = get_info_path(source)
|
||||||
|
z.write(source, name)
|
||||||
|
z.close()
|
||||||
|
return is_created(target)
|
||||||
|
|
||||||
|
if not target:
|
||||||
|
target = join(
|
||||||
|
PurePath(source).parent,
|
||||||
|
'{}.zip'.format(PurePath(source).name))
|
||||||
|
z = zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED)
|
||||||
|
root_len = len(os.path.abspath(source))
|
||||||
|
for root, dirs, files in os.walk(source):
|
||||||
|
relative = os.path.abspath(root)[root_len:]
|
||||||
|
for f in files:
|
||||||
|
fullpath = join(root, f)
|
||||||
|
file_name = join(relative, f)
|
||||||
|
z.write(fullpath, file_name)
|
||||||
|
z.close()
|
||||||
|
|
||||||
|
return is_created(target)
|
||||||
|
|
||||||
|
|
||||||
|
def unzip(source, path='', members=None, pwd=None):
|
||||||
|
if not path:
|
||||||
|
path, _, _, _ = get_info_path(source)
|
||||||
|
with zipfile.ZipFile(source) as z:
|
||||||
|
if not pwd is None:
|
||||||
|
pwd = pwd.encode()
|
||||||
|
if isinstance(members, str):
|
||||||
|
members = (members,)
|
||||||
|
z.extractall(path, members=members, pwd=pwd)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def merge_zip(target, zips):
|
||||||
|
try:
|
||||||
|
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as t:
|
||||||
|
for path in zips:
|
||||||
|
with zipfile.ZipFile(path, compression=zipfile.ZIP_DEFLATED) as s:
|
||||||
|
for name in s.namelist():
|
||||||
|
t.writestr(name, s.open(name).read())
|
||||||
|
except Exception as e:
|
||||||
|
error(e)
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def kill(path):
|
||||||
|
p = Path(path)
|
||||||
|
if p.is_file():
|
||||||
|
try:
|
||||||
|
p.unlink()
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
elif p.is_dir():
|
||||||
|
p.rmdir()
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def get_size_screen():
|
||||||
|
if IS_WIN:
|
||||||
|
user32 = ctypes.windll.user32
|
||||||
|
res = '{}x{}'.format(user32.GetSystemMetrics(0), user32.GetSystemMetrics(1))
|
||||||
|
else:
|
||||||
|
args = 'xrandr | grep "*" | cut -d " " -f4'
|
||||||
|
res = run(args, True)
|
||||||
|
return res.strip()
|
||||||
|
|
||||||
|
|
||||||
|
def get_clipboard():
|
||||||
|
df = None
|
||||||
|
text = ''
|
||||||
|
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
|
||||||
|
transferable = sc.getContents()
|
||||||
|
data = transferable.getTransferDataFlavors()
|
||||||
|
for df in data:
|
||||||
|
if df.MimeType == CLIPBOARD_FORMAT_TEXT:
|
||||||
|
break
|
||||||
|
if df:
|
||||||
|
text = transferable.getTransferData(df)
|
||||||
|
return text
|
||||||
|
|
||||||
|
|
||||||
|
class TextTransferable(unohelper.Base, XTransferable):
|
||||||
|
"""Keep clipboard data and provide them."""
|
||||||
|
|
||||||
|
def __init__(self, text):
|
||||||
|
df = DataFlavor()
|
||||||
|
df.MimeType = CLIPBOARD_FORMAT_TEXT
|
||||||
|
df.HumanPresentableName = "encoded text utf-16"
|
||||||
|
self.flavors = [df]
|
||||||
|
self.data = [text]
|
||||||
|
|
||||||
|
def getTransferData(self, flavor):
|
||||||
|
if not flavor:
|
||||||
|
return
|
||||||
|
for i, f in enumerate(self.flavors):
|
||||||
|
if flavor.MimeType == f.MimeType:
|
||||||
|
return self.data[i]
|
||||||
|
return
|
||||||
|
|
||||||
|
def getTransferDataFlavors(self):
|
||||||
|
return tuple(self.flavors)
|
||||||
|
|
||||||
|
def isDataFlavorSupported(self, flavor):
|
||||||
|
if not flavor:
|
||||||
|
return False
|
||||||
|
mtype = flavor.MimeType
|
||||||
|
for f in self.flavors:
|
||||||
|
if mtype == f.MimeType:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def set_clipboard(text):
|
||||||
|
ts = TextTransferable(text)
|
||||||
|
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
|
||||||
|
sc.setContents(ts, None)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def copy(doc=None):
|
||||||
|
if doc is None:
|
||||||
|
doc = get_document()
|
||||||
|
if hasattr(doc, 'frame'):
|
||||||
|
frame = doc.frame
|
||||||
|
else:
|
||||||
|
frame = doc.getCurrentController().getFrame()
|
||||||
|
dispatch = get_dispatch()
|
||||||
|
dispatch.executeDispatch(frame, '.uno:Copy', '', 0, ())
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def get_epoch():
|
||||||
|
now = datetime.datetime.now()
|
||||||
|
return int(time.mktime(now.timetuple()))
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?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="OfficeMenuBar">
|
||||||
|
<node oor:name="org.myextension.test" oor:op="replace">
|
||||||
|
<prop oor:name="Title" oor:type="xs:string">
|
||||||
|
<value xml:lang="en">My Extension</value>
|
||||||
|
<value xml:lang="es">Mi Extensión</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="Target" oor:type="xs:string">
|
||||||
|
<value>_self</value>
|
||||||
|
</prop>
|
||||||
|
<node oor:name="Submenu">
|
||||||
|
<node oor:name="org.myextension.test.101" oor:op="replace">
|
||||||
|
<prop oor:name="Title" oor:type="xs:string">
|
||||||
|
<value xml:lang="en">Option 1</value>
|
||||||
|
<value xml:lang="es">Opción 1</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="URL" oor:type="xs:string">
|
||||||
|
<value>service:org.myextension.test?option1</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="Target" oor:type="xs:string">
|
||||||
|
<value>_self</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="Context" oor:type="xs:string">
|
||||||
|
<value>com.sun.star.sheet.SpreadsheetDocument,com.sun.star.text.TextDocument</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="ImageIdentifier" oor:type="xs:string">
|
||||||
|
<value>%origin%/images/icon</value>
|
||||||
|
</prop>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
<node oor:name="OfficeToolBar">
|
||||||
|
<node oor:name="org.myextension.test" oor:op="replace">
|
||||||
|
<node oor:name="org.myextension.test.t1" oor:op="replace">
|
||||||
|
<prop oor:name="Title" oor:type="xs:string">
|
||||||
|
<value xml:lang="en">Option 1</value>
|
||||||
|
<value xml:lang="es">Opción 1</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="URL" oor:type="xs:string">
|
||||||
|
<value>service:org.myextension.test?option1</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="Target" oor:type="xs:string">
|
||||||
|
<value>_self</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="Context" oor:type="xs:string">
|
||||||
|
<value>com.sun.star.sheet.SpreadsheetDocument,com.sun.star.text.TextDocument</value>
|
||||||
|
</prop>
|
||||||
|
<prop oor:name="ImageIdentifier" oor:type="xs:string">
|
||||||
|
<value>%origin%/images/icon</value>
|
||||||
|
</prop>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</node>
|
||||||
|
</oor:component-data>
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<manifest:manifest>
|
||||||
|
<manifest:file-entry manifest:full-path="TestMacro.py" manifest:media-type="application/vnd.sun.star.uno-component;type=Python"/>
|
||||||
|
<manifest:file-entry manifest:full-path="Addons.xcu" manifest:media-type="application/vnd.sun.star.configuration-data"/>
|
||||||
|
</manifest:manifest>
|
|
@ -0,0 +1,33 @@
|
||||||
|
import gettext
|
||||||
|
import uno
|
||||||
|
import unohelper
|
||||||
|
from com.sun.star.task import XJobExecutor
|
||||||
|
import easymacro as app
|
||||||
|
|
||||||
|
|
||||||
|
ID_EXTENSION = 'org.myextension.test'
|
||||||
|
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:
|
||||||
|
app.error(e)
|
||||||
|
|
||||||
|
|
||||||
|
class TestMacro(unohelper.Base, XJobExecutor):
|
||||||
|
|
||||||
|
def __init__(self, ctx):
|
||||||
|
self.ctx = ctx
|
||||||
|
|
||||||
|
def trigger(self, args='pyUNO'):
|
||||||
|
print('Hello World', args)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
g_ImplementationHelper = unohelper.ImplementationHelper()
|
||||||
|
g_ImplementationHelper.addImplementation(TestMacro, 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="org.myextension.test" />
|
||||||
|
<version value="0.1.0" />
|
||||||
|
<display-name>
|
||||||
|
<name lang="en">Test Macro</name>
|
||||||
|
<name lang="es">Macro de Prueba</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/testmacro.png" />
|
||||||
|
</icon>
|
||||||
|
<publisher>
|
||||||
|
<name xlink:href="https://elmau.net" lang="en">El Mau</name>
|
||||||
|
<name xlink:href="https://elmau.net" 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 @@
|
||||||
|
My great extension
|
|
@ -0,0 +1 @@
|
||||||
|
Mi gran extensión
|
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
Binary file not shown.
After Width: | Height: | Size: 26 KiB |
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,14 @@
|
||||||
|
This file is part of TestMacro.
|
||||||
|
|
||||||
|
TestMacro 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.
|
||||||
|
|
||||||
|
TestMacro 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 TestMacro. If not, see <https://www.gnu.org/licenses/>.
|
|
@ -0,0 +1,14 @@
|
||||||
|
This file is part of TestMacro.
|
||||||
|
|
||||||
|
TestMacro 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.
|
||||||
|
|
||||||
|
TestMacro 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 TestMacro. If not, see <https://www.gnu.org/licenses/>.
|
|
@ -20,6 +20,7 @@
|
||||||
import argparse
|
import argparse
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
from shutil import copyfile
|
from shutil import copyfile
|
||||||
from subprocess import call
|
from subprocess import call
|
||||||
import zipfile
|
import zipfile
|
||||||
|
@ -32,6 +33,7 @@ from conf import (
|
||||||
INFO,
|
INFO,
|
||||||
PATHS,
|
PATHS,
|
||||||
TYPE_EXTENSION,
|
TYPE_EXTENSION,
|
||||||
|
USE_LOCALES,
|
||||||
log)
|
log)
|
||||||
|
|
||||||
|
|
||||||
|
@ -74,6 +76,10 @@ def _compress_oxt():
|
||||||
z.write(fullpath, file_name, zipfile.ZIP_DEFLATED)
|
z.write(fullpath, file_name, zipfile.ZIP_DEFLATED)
|
||||||
z.close()
|
z.close()
|
||||||
|
|
||||||
|
if DATA['update']:
|
||||||
|
path_xml = _join(path, FILES['update'])
|
||||||
|
_save(path_xml, DATA['update'])
|
||||||
|
|
||||||
log.info('Extension OXT created sucesfully...')
|
log.info('Extension OXT created sucesfully...')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -227,6 +233,13 @@ def _update_files():
|
||||||
path = _join(path_source, FILES['addin'])
|
path = _join(path_source, FILES['addin'])
|
||||||
_save(path, DATA['addin'])
|
_save(path, DATA['addin'])
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
_compile_idl()
|
_compile_idl()
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue