diff --git a/CHANGELOG b/CHANGELOG
index 85d4b85..2e67c8b 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,7 @@
+v 0.4.0 [14-sep-2019]
+---------------------
+ - Add support for locales
+
v 0.3.0 [10-sep-2019]
---------------------
- Add support for dialogs
diff --git a/TODO.md b/TODO.md
index d294d43..3d28553 100644
--- a/TODO.md
+++ b/TODO.md
@@ -3,3 +3,4 @@
* Configuration
* Option panel
* Sub-menus
+* Panel lateral
diff --git a/VERSION b/VERSION
index 0d91a54..1d0ba9e 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.3.0
+0.4.0
diff --git a/source/conf.py.example b/source/conf.py.example
index 713c604..7c03a49 100644
--- a/source/conf.py.example
+++ b/source/conf.py.example
@@ -34,6 +34,12 @@ NAME = 'TestMacro'
# ~ Should be unique, used URL inverse
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 = {
'en': {'text': 'El Mau', 'link': 'https://elmau.net'},
'es': {'text': 'El Mau', 'link': 'https://elmau.net'},
@@ -131,11 +137,12 @@ EXTENSION = {
'name': NAME,
'id': ID,
'icon': (ICON, ICON_EXT),
+ 'languages': tuple(INFO.keys())
}
# ~ If used more libraries set python path in True and copy inside
-# ~ If used easymacro pythonpath always is True
+# ~ If used easymacro pythonpath always is True, recommended
DIRS = {
'meta': 'META-INF',
'source': 'source',
@@ -143,7 +150,8 @@ DIRS = {
'images': 'images',
'registration': 'registration',
'files': 'files',
- 'pythonpath': False,
+ 'pythonpath': True,
+ 'locales': PATH_LOCALES,
}
@@ -335,6 +343,8 @@ if PARENT == 'OfficeMenuBar':
def _get_context(args):
+ if not args:
+ return ''
c = []
for v in args.split(','):
c.append(CONTEXT[v])
diff --git a/source/easymacro.py b/source/easymacro.py
index 51da1b3..622248c 100644
--- a/source/easymacro.py
+++ b/source/easymacro.py
@@ -18,7 +18,8 @@
# ~ along with ZAZ. If not, see .
-
+import ctypes
+import datetime
import errno
import getpass
import logging
@@ -45,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.PosSize import POSSIZE, SIZE
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.text.TextContentAnchorType import AS_CHARACTER
@@ -79,9 +81,12 @@ log = logging.getLogger(__name__)
OS = platform.system()
USER = getpass.getuser()
PC = platform.node()
+DESKTOP = os.environ.get('DESKTOP_SESSION', '')
+INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
IS_WIN = OS == 'Windows'
LOG_NAME = 'ZAZ'
+CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
CALC = 'calc'
WRITER = 'writer'
@@ -119,10 +124,9 @@ def _get_config(key, node_name):
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
+LANG = LANGUAGE.split('-')[0]
NAME = TITLE = _get_config('ooName', '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):
@@ -238,6 +242,10 @@ def get_desktop():
return create_instance('com.sun.star.frame.Desktop', True)
+def get_dispatch():
+ return create_instance('com.sun.star.frame.DispatchHelper')
+
+
def get_temp_file():
return tempfile.NamedTemporaryFile()
@@ -315,6 +323,10 @@ class LODocument(object):
def title(self):
return self.obj.getTitle()
+ @property
+ def frame(self):
+ return self._cc.getFrame()
+
@property
def is_saved(self):
return self.obj.hasLocation()
@@ -368,6 +380,12 @@ class LODocument(object):
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):
@@ -400,9 +418,14 @@ class LOCalc(LODocument):
cell = LOCellRange(self.active[index].obj, self)
return cell
- # ~ def create_instance(self, name):
- # ~ obj = self.obj.createInstance(name)
- # ~ return obj
+ def select(self, rango):
+ r = rango
+ 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):
@@ -551,6 +574,11 @@ class LOBasicIde(LODocument):
def __init__(self, obj):
super().__init__(obj)
+ @property
+ def selection(self):
+ sel = self._cc.getSelection()
+ return sel
+
class LOCellRange(object):
@@ -674,6 +702,10 @@ class LOCellRange(object):
img.setSize(Size(w, h))
return
+ def select(self):
+ self.doc._cc.select(self.obj)
+ return
+
class EventsListenerBase(unohelper.Base, XEventListener):
@@ -1198,6 +1230,9 @@ def open_doc(path, **kwargs):
Password: super_secret
MacroExecutionMode: 4 = Activate macros
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)
opt = _properties(kwargs)
@@ -1252,12 +1287,13 @@ def run(command, wait=False):
# ~ debug(shlex.split(command))
try:
if wait:
- p = subprocess.Popen(shlex.split(command), stdout=subprocess.PIPE)
- p.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()
+ result, er = p.communicate()
except subprocess.CalledProcessError as e:
msg = ("run [ERROR]: output = %s, error code = %s\n"
% (e.output, e.returncode))
@@ -1366,3 +1402,93 @@ def merge_zip(target, zips):
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()))
diff --git a/source/source/Addons.xcu b/source/source/Addons.xcu
new file mode 100644
index 0000000..b726cdf
--- /dev/null
+++ b/source/source/Addons.xcu
@@ -0,0 +1,58 @@
+
+
+
+
+
+
+ My Extension
+ Mi Extensión
+
+
+ _self
+
+
+
+
+ Option 1
+ Opción 1
+
+
+ service:org.myextension.test?option1
+
+
+ _self
+
+
+ com.sun.star.sheet.SpreadsheetDocument,com.sun.star.text.TextDocument
+
+
+ %origin%/images/icon
+
+
+
+
+
+
+
+
+
+ Option 1
+ Opción 1
+
+
+ service:org.myextension.test?option1
+
+
+ _self
+
+
+ com.sun.star.sheet.SpreadsheetDocument,com.sun.star.text.TextDocument
+
+
+ %origin%/images/icon
+
+
+
+
+
+
diff --git a/source/source/META-INF/manifest.xml b/source/source/META-INF/manifest.xml
new file mode 100644
index 0000000..6c00e70
--- /dev/null
+++ b/source/source/META-INF/manifest.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/source/source/TestMacro.py b/source/source/TestMacro.py
new file mode 100644
index 0000000..79cd31f
--- /dev/null
+++ b/source/source/TestMacro.py
@@ -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)
diff --git a/source/source/description.xml b/source/source/description.xml
new file mode 100644
index 0000000..cf73104
--- /dev/null
+++ b/source/source/description.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+ Test Macro
+ Macro de Prueba
+
+
+
+
+
+
+
+
+
+ El Mau
+ El Mau
+
+
+
+
+
+
+
+
diff --git a/source/source/description/desc_en.txt b/source/source/description/desc_en.txt
new file mode 100644
index 0000000..b667a4b
--- /dev/null
+++ b/source/source/description/desc_en.txt
@@ -0,0 +1 @@
+My great extension
\ No newline at end of file
diff --git a/source/source/description/desc_es.txt b/source/source/description/desc_es.txt
new file mode 100644
index 0000000..d8d8fdc
--- /dev/null
+++ b/source/source/description/desc_es.txt
@@ -0,0 +1 @@
+Mi gran extensión
\ No newline at end of file
diff --git a/source/source/images/icon_16.bmp b/source/source/images/icon_16.bmp
new file mode 100644
index 0000000..a954508
Binary files /dev/null and b/source/source/images/icon_16.bmp differ
diff --git a/source/source/images/testmacro.png b/source/source/images/testmacro.png
new file mode 100644
index 0000000..2f210ed
Binary files /dev/null and b/source/source/images/testmacro.png differ
diff --git a/source/source/pythonpath/easymacro.py b/source/source/pythonpath/easymacro.py
new file mode 100644
index 0000000..622248c
--- /dev/null
+++ b/source/source/pythonpath/easymacro.py
@@ -0,0 +1,1494 @@
+#!/usr/bin/env python3
+
+# == Rapid Develop Macros in LibreOffice ==
+
+# ~ This file is part of ZAZ.
+
+# ~ ZAZ is free software: you can redistribute it and/or modify
+# ~ it under the terms of the GNU General Public License as published by
+# ~ the Free Software Foundation, either version 3 of the License, or
+# ~ (at your option) any later version.
+
+# ~ ZAZ is distributed in the hope that it will be useful,
+# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# ~ GNU General Public License for more details.
+
+# ~ You should have received a copy of the GNU General Public License
+# ~ along with ZAZ. If not, see .
+
+
+import ctypes
+import datetime
+import errno
+import getpass
+import logging
+import os
+import platform
+import shlex
+import subprocess
+import sys
+import tempfile
+import threading
+import time
+import zipfile
+
+from datetime import datetime
+from functools import wraps
+from pathlib import Path, PurePath
+from pprint import pprint
+from subprocess import PIPE
+
+import uno
+import unohelper
+from com.sun.star.beans import PropertyValue
+from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
+from com.sun.star.awt.MessageBoxResults import YES
+from com.sun.star.awt.PosSize import POSSIZE, SIZE
+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.text.TextContentAnchorType import AS_CHARACTER
+
+from com.sun.star.lang import XEventListener
+from com.sun.star.awt import XActionListener
+from com.sun.star.awt import XMouseListener
+
+
+MSG_LANG = {
+ 'es': {
+ 'OK': 'Aceptar',
+ 'Cancel': 'Cancelar',
+ 'Select file': 'Seleccionar archivo',
+ }
+}
+
+
+FILE_NAME_DEBUG = 'zaz-debug.log'
+LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
+LOG_DATE = '%d/%m/%Y %H:%M:%S'
+LEVEL_ERROR = logging.getLevelName(logging.ERROR)
+LEVEL_DEBUG = logging.getLevelName(logging.DEBUG)
+LEVEL_INFO = logging.getLevelName(logging.INFO)
+logging.addLevelName(logging.ERROR, f'\033[1;41m{LEVEL_ERROR}\033[1;0m')
+logging.addLevelName(logging.DEBUG, f'\x1b[33m{LEVEL_DEBUG}\033[1;0m')
+logging.addLevelName(logging.INFO, f'\x1b[32m{LEVEL_INFO}\033[1;0m')
+logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
+log = logging.getLogger(__name__)
+
+
+OS = platform.system()
+USER = getpass.getuser()
+PC = platform.node()
+DESKTOP = os.environ.get('DESKTOP_SESSION', '')
+INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
+
+IS_WIN = OS == 'Windows'
+LOG_NAME = 'ZAZ'
+CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
+
+CALC = 'calc'
+WRITER = 'writer'
+OBJ_CELL = 'ScCellObj'
+OBJ_RANGE = 'ScCellRangeObj'
+OBJ_RANGES = 'ScCellRangesObj'
+OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
+
+
+CTX = uno.getComponentContext()
+SM = CTX.getServiceManager()
+
+
+def create_instance(name, with_context=False):
+ if with_context:
+ instance = SM.createInstanceWithContext(name, CTX)
+ else:
+ instance = SM.createInstance(name)
+ return instance
+
+
+def _get_config(key, node_name):
+ name = 'com.sun.star.configuration.ConfigurationProvider'
+ service = 'com.sun.star.configuration.ConfigurationAccess'
+ cp = create_instance(name, True)
+ node = PropertyValue(Name='nodepath', Value=node_name)
+ try:
+ ca = cp.createInstanceWithArguments(service, (node,))
+ if ca and (ca.hasByName(key)):
+ data = ca.getPropertyValue(key)
+ return data
+ except Exception as e:
+ log.error(e)
+ return ''
+
+
+LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
+LANG = LANGUAGE.split('-')[0]
+NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product')
+VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product')
+
+
+def mri(obj):
+ m = create_instance('mytools.Mri')
+ if m is None:
+ msg = 'Extension MRI not found'
+ error(msg)
+ return
+
+ m.inspect(obj)
+ return
+
+
+def catch_exception(f):
+ @wraps(f)
+ def func(*args, **kwargs):
+ try:
+ return f(*args, **kwargs)
+ except Exception as e:
+ log.error(f.__name__, exc_info=True)
+ 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(*k, **kw):
+ t = threading.Thread(target=fn, args=k, kwargs=kw)
+ t.start()
+ return t
+ return run
+
+
+def _(msg):
+ L = LANGUAGE.split('-')[0]
+ if L == 'en':
+ return msg
+
+ if not L in MSG_LANG:
+ return msg
+
+ return MSG_LANG[L][msg]
+
+
+def msgbox(message, title=TITLE, buttons=MSG_BUTTONS.BUTTONS_OK, type_msg='infobox'):
+ """ Create message box
+ type_msg: infobox, warningbox, errorbox, querybox, messbox
+ http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMessageBoxFactory.html
+ """
+ toolkit = create_instance('com.sun.star.awt.Toolkit')
+ parent = toolkit.getDesktopWindow()
+ mb = toolkit.createMessageBox(parent, type_msg, buttons, title, str(message))
+ return mb.execute()
+
+
+def question(message, title=TITLE):
+ res = msgbox(message, title, MSG_BUTTONS.BUTTONS_YES_NO, 'querybox')
+ return res == YES
+
+
+def warning(message, title=TITLE):
+ return msgbox(message, title, type_msg='warningbox')
+
+
+def errorbox(message, title=TITLE):
+ return msgbox(message, title, type_msg='errorbox')
+
+
+def get_desktop():
+ return create_instance('com.sun.star.frame.Desktop', True)
+
+
+def get_dispatch():
+ return create_instance('com.sun.star.frame.DispatchHelper')
+
+
+def get_temp_file():
+ return tempfile.NamedTemporaryFile()
+
+
+def _path_url(path):
+ if path.startswith('file://'):
+ return path
+ return uno.systemPathToFileUrl(path)
+
+
+def _path_system(path):
+ if path.startswith('file://'):
+ return os.path.abspath(uno.fileUrlToSystemPath(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):
+ services = {
+ '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',
+ }
+ for k, v in services.items():
+ if obj.supportsService(v):
+ return k
+ return ''
+
+
+def _properties(values):
+ p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
+ return tuple(p)
+
+
+# ~ Custom classes
+
+
+class LODocument(object):
+
+ def __init__(self, obj):
+ self._obj = obj
+ self._init_values()
+
+ def _init_values(self):
+ self._type_doc = get_type_doc(self.obj)
+ self._cc = self.obj.getCurrentController()
+ return
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def type(self):
+ return self._type_doc
+
+ @property
+ def title(self):
+ return self.obj.getTitle()
+
+ @property
+ def frame(self):
+ return self._cc.getFrame()
+
+ @property
+ def is_saved(self):
+ return self.obj.hasLocation()
+
+ @property
+ def is_modified(self):
+ return self.obj.isModified()
+
+ @property
+ def is_read_only(self):
+ return self.obj.isReadOnly()
+
+ @property
+ def path(self):
+ return _path_system(self.obj.getURL())
+
+ @property
+ def visible(self):
+ w = self._cc.getFrame().getContainerWindow()
+ return w.Visible
+ @visible.setter
+ def visible(self, value):
+ w = self._cc.getFrame().getContainerWindow()
+ w.setVisible(value)
+
+ @property
+ def zoom(self):
+ return self._cc.ZoomValue
+ @zoom.setter
+ def zoom(self, value):
+ self._cc.ZoomValue = value
+
+ def create_instance(self, name):
+ obj = self.obj.createInstance(name)
+ 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):
+ self.obj.close(True)
+ 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):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def active(self):
+ return LOCalcSheet(self._cc.getActiveSheet(), self)
+
+ @property
+ def selection(self):
+ sel = self.obj.getCurrentSelection()
+ if sel.ImplementationName in OBJ_TYPE_RANGES:
+ sel = LOCellRange(sel, self)
+ return sel
+
+ def get_cell(self, index=None):
+ """
+ index is str 'A1'
+ index is tuple (row, col)
+ """
+ if index is None:
+ cell = self.selection.first
+ else:
+ cell = LOCellRange(self.active[index].obj, self)
+ return cell
+
+ def select(self, rango):
+ r = rango
+ 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):
+
+ def __init__(self, obj, doc):
+ self._obj = obj
+ self._doc = doc
+ self._init_values()
+
+ def __getitem__(self, index):
+ return LOCellRange(self.obj[index], self.doc)
+
+ def _init_values(self):
+ return
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def doc(self):
+ return self._doc
+
+
+class LOWriter(LODocument):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def string(self):
+ return self._obj.getText().String
+
+ @property
+ def text(self):
+ return self._obj.getText()
+
+ @property
+ def cursor(self):
+ return self.text.createTextCursor()
+
+ @property
+ def selection(self):
+ sel = self._cc.getSelection()
+ return LOTextRange(sel[0])
+
+ def insert_content(self, cursor, data, replace=False):
+ self.text.insertTextContent(cursor, data, replace)
+ return
+
+ # ~ tt = doc.createInstance('com.sun.star.text.TextTable')
+ # ~ tt.initialize(5, 2)
+
+ # ~ f = doc.createInstance('com.sun.star.text.TextFrame')
+ # ~ f.setSize(Size(10000, 500))
+
+ def insert_image(self, path, **kwargs):
+ cursor = kwargs.get('cursor', self.selection.cursor.getEnd())
+ w = kwargs.get('width', 1000)
+ h = kwargs.get('Height', 1000)
+ image = self.create_instance('com.sun.star.text.GraphicObject')
+ image.GraphicURL = _path_url(path)
+ image.AnchorType = AS_CHARACTER
+ image.Width = w
+ image.Height = h
+ self.insert_content(cursor, image)
+ return
+
+
+class LOTextRange(object):
+
+ def __init__(self, obj):
+ self._obj = obj
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def string(self):
+ return self.obj.String
+
+ @property
+ def text(self):
+ return self.obj.getText()
+
+ @property
+ def cursor(self):
+ return self.text.createTextCursorByRange(self.obj)
+
+
+class LOBase(LODocument):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+
+class LODrawImpress(LODocument):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def draw_page(self):
+ return self._cc.getCurrentPage()
+
+ @catch_exception
+ def insert_image(self, path, **kwargs):
+ w = kwargs.get('width', 3000)
+ h = kwargs.get('Height', 1000)
+ x = kwargs.get('X', 1000)
+ y = kwargs.get('Y', 1000)
+
+ image = self.create_instance('com.sun.star.drawing.GraphicObjectShape')
+ image.GraphicURL = _path_url(path)
+ image.Size = Size(w, h)
+ image.Position = Point(x, y)
+ self.draw_page.add(image)
+ return
+
+
+class LOImpress(LODrawImpress):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+
+class LODraw(LODrawImpress):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+
+class LOMath(LODocument):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+
+class LOBasicIde(LODocument):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def selection(self):
+ sel = self._cc.getSelection()
+ return sel
+
+
+class LOCellRange(object):
+
+ def __init__(self, obj, doc):
+ self._obj = obj
+ self._doc = doc
+ self._init_values()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, *args):
+ pass
+
+ def __getitem__(self, index):
+ return LOCellRange(self.obj[index], self.doc)
+
+ def _init_values(self):
+ self._type_obj = self.obj.ImplementationName
+ self._type_content = EMPTY
+
+ if self._type_obj == OBJ_CELL:
+ self._type_content = self.obj.getType()
+ return
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def doc(self):
+ return self._doc
+
+ @property
+ def type(self):
+ return self._type_obj
+
+ @property
+ def type_content(self):
+ return self._type_content
+
+ @property
+ def first(self):
+ if self.type == OBJ_RANGES:
+ obj = LOCellRange(self.obj[0][0,0], self.doc)
+ else:
+ obj = LOCellRange(self.obj[0,0], self.doc)
+ return obj
+
+ @property
+ def value(self):
+ v = None
+ if self._type_content == VALUE:
+ v = self.obj.getValue()
+ elif self._type_content == TEXT:
+ v = self.obj.getString()
+ elif self._type_content == FORMULA:
+ v = self.obj.getFormula()
+ return v
+ @value.setter
+ def value(self, data):
+ if isinstance(data, str):
+ if data.startswith('='):
+ self.obj.setFormula(data)
+ else:
+ self.obj.setString(data)
+ elif isinstance(data, (int, float)):
+ self.obj.setValue(data)
+
+ @property
+ def data(self):
+ return self.obj.getDataArray()
+ @data.setter
+ def data(self, values):
+ if isinstance(values, list):
+ values = tuple(values)
+ self.obj.setDataArray(values)
+
+ def offset(self, col=1, row=0):
+ a = self.address
+ col = a.Column + col
+ row = a.Row + row
+ return LOCellRange(self.sheet[row,col], self.doc)
+
+ @property
+ def sheet(self):
+ return self.obj.Spreadsheet
+
+ @property
+ def draw_page(self):
+ return self.sheet.getDrawPage()
+
+ @property
+ def name(self):
+ return self.obj.AbsoluteName
+
+ @property
+ def address(self):
+ if self._type_obj == OBJ_CELL:
+ a = self.obj.getCellAddress()
+ elif self._type_obj == OBJ_RANGE:
+ a = self.obj.getRangeAddress()
+ else:
+ a = self.obj.getRangeAddressesAsString()
+ return a
+
+ @property
+ def current_region(self):
+ cursor = self.sheet.createCursorByRange(self.obj[0,0])
+ cursor.collapseToCurrentRegion()
+ return LOCellRange(self.sheet[cursor.AbsoluteName], self.doc)
+
+ def insert_image(self, path, **kwargs):
+ s = self.obj.Size
+ w = kwargs.get('width', s.Width)
+ h = kwargs.get('Height', s.Height)
+ img = self.doc.create_instance('com.sun.star.drawing.GraphicObjectShape')
+ img.GraphicURL = _path_url(path)
+ self.draw_page.add(img)
+ img.Anchor = self.obj
+ img.setSize(Size(w, h))
+ return
+
+ def select(self):
+ self.doc._cc.select(self.obj)
+ return
+
+
+class EventsListenerBase(unohelper.Base, XEventListener):
+
+ def __init__(self, controller, window=None):
+ self._controller = controller
+ self._window = window
+
+ def disposing(self, event):
+ self._controller = None
+ if not self._window is None:
+ self._window.setMenuBar(None)
+
+
+class EventsButton(EventsListenerBase, XActionListener):
+
+ def __init__(self, controller):
+ super().__init__(controller)
+
+ def actionPerformed(self, event):
+ name = event.Source.Model.Name
+ event_name = '{}_action'.format(name)
+ if hasattr(self._controller, event_name):
+ getattr(self._controller, event_name)(event)
+ return
+
+
+class EventsMouse(EventsListenerBase, XMouseListener):
+
+ def __init__(self, controller):
+ super().__init__(controller)
+
+ def mousePressed(self, event):
+ name = event.Source.Model.Name
+ event_name = '{}_click'.format(name)
+ if event.ClickCount == 2:
+ event_name = '{}_double_click'.format(name)
+ if hasattr(self._controller, event_name):
+ getattr(self._controller, event_name)(event)
+ return
+
+ def mouseReleased(self, event):
+ pass
+
+ def mouseEntered(self, event):
+ pass
+
+ def mouseExited(self, event):
+ pass
+
+
+class UnoBaseObject(object):
+
+ def __init__(self, obj):
+ self._obj = obj
+ self._model = self.obj.Model
+ self._rules = {}
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def model(self):
+ return self._model
+
+ @property
+ def name(self):
+ return self.model.Name
+
+ @property
+ def parent(self):
+ return self.obj.getContext()
+
+ @property
+ def x(self):
+ return self.model.PositionX
+ @x.setter
+ def x(self, value):
+ self.model.PositionX = value
+
+ @property
+ def y(self):
+ return self.model.PositionY
+ @y.setter
+ def y(self, value):
+ self.model.PositionY = value
+
+ @property
+ def width(self):
+ return self._model.Width
+ @width.setter
+ def width(self, value):
+ self._model.Width = value
+
+ @property
+ def height(self):
+ return self._model.Height
+ @height.setter
+ def height(self, value):
+ self._model.Height = value
+
+ @property
+ def tag(self):
+ return self.model.Tag
+ @tag.setter
+ def tag(self, value):
+ self.model.Tag = value
+
+ @property
+ def step(self):
+ return self.model.Step
+ @step.setter
+ def step(self, value):
+ self.model.Step = value
+
+ @property
+ def rules(self):
+ return self._rules
+ @rules.setter
+ def rules(self, value):
+ self._rules = value
+
+ def set_focus(self):
+ self.obj.setFocus()
+ return
+
+ def center(self, horizontal=True, vertical=False):
+ p = self.parent.Model
+ w = p.Width
+ h = p.Height
+ if horizontal:
+ x = w / 2 - self.width / 2
+ self.x = x
+ if vertical:
+ y = h / 2 - self.height / 2
+ self.y = y
+ return
+
+ def move(self, origin, x=0, y=5):
+ w = 0
+ h = 0
+ if x:
+ w = origin.width
+ if y:
+ h = origin.height
+ x = origin.x + x + w
+ y = origin.y + y + h
+ self.x = x
+ self.y = y
+ return
+
+
+class UnoLabel(UnoBaseObject):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def value(self):
+ return self.model.Label
+ @value.setter
+ def value(self, value):
+ self.model.Label = value
+
+
+class UnoButton(UnoBaseObject):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+ # ~ self._set_icon()
+
+ def _set_icon(self):
+ icon_name = self.tag.strip()
+ if icon_name:
+ path_icon = _file_url('{}/img/{}'.format(CURRENT_PATH, icon_name))
+ self._model.ImageURL = path_icon
+ if self.value:
+ self._model.ImageAlign = 0
+ return
+
+ @property
+ def value(self):
+ return self.model.Label
+ @value.setter
+ def value(self, value):
+ self.model.Label = value
+
+
+class UnoText(UnoBaseObject):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+
+ @property
+ def value(self):
+ return self.model.Text
+ @value.setter
+ def value(self, value):
+ self.model.Text = value
+
+ def validate(self):
+
+ return
+
+
+class UnoListBox(UnoBaseObject):
+
+ def __init__(self, obj):
+ super().__init__(obj)
+ self._data = []
+
+ @property
+ def value(self):
+ return self.obj.SelectedItem
+
+ @property
+ def data(self):
+ return self._data
+ @data.setter
+ def data(self, values):
+ self._data = list(sorted(values))
+ self.model.StringItemList = self.data
+ return
+
+
+class LODialog(object):
+
+ def __init__(self, properties):
+ self._obj = self._create(properties)
+ self._init_values()
+
+ def _init_values(self):
+ self._model = self._obj.Model
+ self._init_controls()
+ self._events = None
+ # ~ self._response = None
+ return
+
+ def _create(self, properties):
+ path = properties.pop('Path', '')
+ if path:
+ dp = create_instance('com.sun.star.awt.DialogProvider2', True)
+ return dp.createDialog(_path_url(path))
+
+ if 'Library' in properties:
+ location = properties['Location']
+ if location == 'user':
+ location = 'application'
+ dp = create_instance('com.sun.star.awt.DialogProvider2', True)
+ path = 'vnd.sun.star.script:{}.{}?location={}'.format(
+ properties['Library'], properties['Name'], location)
+ return dp.createDialog(path)
+
+ dlg = create_instance('com.sun.star.awt.UnoControlDialog', True)
+ model = create_instance('com.sun.star.awt.UnoControlDialogModel', True)
+ toolkit = create_instance('com.sun.star.awt.Toolkit', True)
+ set_properties(model, properties)
+ dlg.setModel(model)
+ dlg.setVisible(False)
+ dlg.createPeer(toolkit, None)
+
+ return dlg
+
+ def _init_controls(self):
+
+ return
+
+ @property
+ def obj(self):
+ return self._obj
+
+ @property
+ def model(self):
+ return self._model
+
+ @property
+ def events(self):
+ return self._events
+ @events.setter
+ def events(self, controllers):
+ self._events = controllers
+ self._connect_listeners()
+
+ def _connect_listeners(self):
+
+ return
+
+ def _add_listeners(self, control):
+ if self.events is None:
+ return
+
+ listeners = {
+ 'addActionListener': EventsButton,
+ 'addMouseListener': EventsMouse,
+ }
+ for key, value in listeners.items():
+ if hasattr(control.obj, key):
+ getattr(control.obj, key)(listeners[key](self.events))
+ return
+
+ def open(self):
+ return self.obj.execute()
+
+ def close(self, value=0):
+ return self.obj.endDialog(value)
+
+ def _get_control_model(self, control):
+ services = {
+ 'label': 'com.sun.star.awt.UnoControlFixedTextModel',
+ 'button': 'com.sun.star.awt.UnoControlButtonModel',
+ 'text': 'com.sun.star.awt.UnoControlEditModel',
+ 'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
+ 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
+ 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
+ 'image': 'com.sun.star.awt.UnoControlImageControlModel',
+ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
+ 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
+ 'tree': 'com.sun.star.awt.tree.TreeControlModel',
+ 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
+ }
+ return services[control]
+
+ def _get_custom_class(self, tipo, obj):
+ classes = {
+ 'label': UnoLabel,
+ 'button': UnoButton,
+ 'text': UnoText,
+ 'listbox': UnoListBox,
+ # ~ 'link': UnoLink,
+ # ~ 'tab': UnoTab,
+ # ~ 'roadmap': UnoRoadmap,
+ # ~ 'image': UnoImage,
+ # ~ 'radio': UnoRadio,
+ # ~ 'groupbox': UnoGroupBox,
+ # ~ 'tree': UnoTree,
+ # ~ 'grid': UnoGrid,
+ }
+ return classes[tipo](obj)
+
+ @catch_exception
+ def add_control(self, properties):
+ tipo = properties.pop('Type').lower()
+ model = self.model.createInstance(self._get_control_model(tipo))
+ set_properties(model, properties)
+ name = properties['Name']
+ self.model.insertByName(name, model)
+ control = self._get_custom_class(tipo, self.obj.getControl(name))
+ self._add_listeners(control)
+ setattr(self, name, control)
+ return
+
+
+# ~ Python >= 3.7
+# ~ def __getattr__(name):
+
+
+def _get_class_doc(obj):
+ classes = {
+ 'calc': LOCalc,
+ 'writer': LOWriter,
+ 'base': LOBase,
+ 'impress': LOImpress,
+ 'draw': LODraw,
+ 'math': LOMath,
+ 'basic': LOBasicIde,
+ }
+ type_doc = get_type_doc(obj)
+ return classes[type_doc](obj)
+
+
+def get_document():
+ doc = None
+ desktop = get_desktop()
+ try:
+ doc = _get_class_doc(desktop.getCurrentComponent())
+ except Exception as e:
+ log.error(e)
+ return doc
+
+
+def get_selection():
+ return get_document().selection
+
+
+def get_cell(*args):
+ if args:
+ index = args
+ if len(index) == 1:
+ index = args[0]
+ cell = get_document().get_cell(index)
+ else:
+ cell = get_selection().first
+ return cell
+
+
+def active_cell():
+ return get_cell()
+
+
+def create_dialog(properties):
+ return LODialog(properties)
+
+
+def set_properties(model, properties):
+ if 'X' in properties:
+ properties['PositionX'] = properties.pop('X')
+ if 'Y' in properties:
+ properties['PositionY'] = properties.pop('Y')
+ keys = tuple(properties.keys())
+ values = tuple(properties.values())
+ model.setPropertyValues(keys, values)
+ return
+
+
+def get_file(filters=(), multiple=False):
+ file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
+ file_picker.setTitle(_('Select file'))
+ file_picker.setMultiSelectionMode(multiple)
+
+ if filters:
+ file_picker.setCurrentFilter(filters[0][0])
+ for f in filters:
+ file_picker.appendFilter(f[0], f[1])
+
+ if file_picker.execute():
+ if multiple:
+ return [_path_system(f) for f in file_picker.getSelectedFiles()]
+ return _path_system(file_picker.getSelectedFiles()[0])
+
+ return ''
+
+
+def get_info_path(path):
+ path, filename = os.path.split(path)
+ name, extension = os.path.splitext(filename)
+ return (path, filename, name, extension)
+
+
+def inputbox(message, default='', title=TITLE):
+
+ class ControllersInput(object):
+
+ def __init__(self, dlg):
+ self.d = dlg
+
+ def cmd_ok_action(self, event):
+ self.d.close(1)
+ return
+
+ args = {
+ 'Title': title,
+ 'Width': 200,
+ 'Height': 80,
+ }
+ dlg = LODialog(args)
+ dlg.events = ControllersInput(dlg)
+
+ args = {
+ 'Type': 'Label',
+ 'Name': 'lbl_msg',
+ 'Label': message,
+ 'Width': 140,
+ 'Height': 50,
+ 'X': 5,
+ 'Y': 5,
+ 'MultiLine': True,
+ 'Border': 1,
+ }
+ dlg.add_control(args)
+
+ args = {
+ 'Type': 'Text',
+ 'Name': 'txt_value',
+ 'Text': default,
+ 'Width': 190,
+ 'Height': 15,
+ }
+ dlg.add_control(args)
+ dlg.txt_value.move(dlg.lbl_msg)
+
+ args = {
+ 'Type': 'button',
+ 'Name': 'cmd_ok',
+ 'Label': _('OK'),
+ 'Width': 40,
+ 'Height': 15,
+ 'DefaultButton': True,
+ 'PushButtonType': 1,
+ }
+ dlg.add_control(args)
+ dlg.cmd_ok.move(dlg.lbl_msg, 10, 0)
+
+ args = {
+ 'Type': 'button',
+ 'Name': 'cmd_cancel',
+ 'Label': _('Cancel'),
+ 'Width': 40,
+ 'Height': 15,
+ 'PushButtonType': 2,
+ }
+ dlg.add_control(args)
+ dlg.cmd_cancel.move(dlg.cmd_ok)
+
+ if dlg.open():
+ return dlg.txt_value.value
+
+ return ''
+
+
+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
+ Usually options:
+ Hidden: True or False
+ AsTemplate: True or False
+ ReadOnly: True or False
+ Password: super_secret
+ MacroExecutionMode: 4 = Activate macros
+ 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)
+ opt = _properties(kwargs)
+ doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt)
+ if doc is None:
+ return
+
+ return _get_class_doc(doc)
+
+
+def open_file(path):
+ if IS_WIN:
+ os.startfile(path)
+ else:
+ subprocess.Popen(['xdg-open', path])
+ return
+
+
+def 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()))
diff --git a/source/source/registration/license_en.txt b/source/source/registration/license_en.txt
new file mode 100644
index 0000000..3e7ef0e
--- /dev/null
+++ b/source/source/registration/license_en.txt
@@ -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 .
diff --git a/source/source/registration/license_es.txt b/source/source/registration/license_es.txt
new file mode 100644
index 0000000..3e7ef0e
--- /dev/null
+++ b/source/source/registration/license_es.txt
@@ -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 .
diff --git a/source/zaz.py b/source/zaz.py
index 7a0999f..ebbc1ff 100644
--- a/source/zaz.py
+++ b/source/zaz.py
@@ -20,6 +20,7 @@
import argparse
import os
import sys
+from pathlib import Path
from shutil import copyfile
from subprocess import call
import zipfile
@@ -32,6 +33,7 @@ from conf import (
INFO,
PATHS,
TYPE_EXTENSION,
+ USE_LOCALES,
log)
@@ -74,6 +76,10 @@ 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
@@ -227,6 +233,13 @@ def _update_files():
path = _join(path_source, FILES['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()
return