diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..03b69a9
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,14 @@
+__pycache__/
+*.py[cod]
+
+*.log
+images/
+
+docs/
+
+# Virtualenv
+.env/
+virtual/
+
+
+
diff --git a/VERSION b/VERSION
index 6e8bf73..0ea3a94 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.1.0
+0.2.0
diff --git a/__pycache__/conf.cpython-37.pyc b/__pycache__/conf.cpython-37.pyc
deleted file mode 100644
index dcd4199..0000000
Binary files a/__pycache__/conf.cpython-37.pyc and /dev/null differ
diff --git a/__pycache__/easymacro.cpython-37.pyc b/__pycache__/easymacro.cpython-37.pyc
deleted file mode 100644
index bd877f2..0000000
Binary files a/__pycache__/easymacro.cpython-37.pyc and /dev/null differ
diff --git a/conf.py b/conf.py
index fec152c..664155b 100644
--- a/conf.py
+++ b/conf.py
@@ -26,7 +26,7 @@ import logging
TYPE_EXTENSION = 1
# ~ https://semver.org/
-VERSION = '0.1.0'
+VERSION = '0.2.0'
# ~ Your great extension name, not used spaces
NAME = 'ZAZBarCode'
@@ -34,6 +34,9 @@ NAME = 'ZAZBarCode'
# ~ Should be unique, used URL inverse
ID = 'net.elmau.zaz.BarCode'
+# ~ If you extension will be multilanguage set True
+USE_LOCALES = False
+
PUBLISHER = {
'en': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'},
'es': {'text': 'El Mau', 'link': 'https://gitlab.com/mauriciobaeza'},
@@ -66,7 +69,6 @@ LICENSE_EN = f"""This file is part of {NAME}.
"""
LICENSE_ES = LICENSE_EN
-
INFO = {
'en': {
'display_name': 'ZAZ Bar Code',
@@ -258,6 +260,7 @@ DIRS = {
'registration': 'registration',
'files': 'files',
'pythonpath': False,
+ 'locales': 'locales',
}
@@ -279,16 +282,21 @@ FILES = {
# ~ URLs for update for example
# ~ URL_XML_UPDATE = 'https://gitlab.com/USER/PROYECT/raw/BRANCH/FOLDERs/FILE_NAME'
-URL_XML_UPDATE = ''
-URL_OXT = ''
+URL_BASE = 'https://gitlab.com/mauriciobaeza/zaz-barcode/raw/master/files/'
+URL_XML_UPDATE = URL_BASE + FILES['update']
+URL_OXT = URL_BASE + FILES['oxt']
-FILE_TEST = '/home/mau/test/test.ods'
+# ~ Default program for test: --calc, --writer, --draw
+PROGRAM = '--calc'
+# ~ Path to file for test
+FILE_TEST = ''
+
PATHS = {
'idlc': '/usr/lib/libreoffice/sdk/bin/idlc',
'include': '/usr/share/idl/libreoffice',
'regmerge': '/usr/lib/libreoffice/program/regmerge',
- 'soffice': ('soffice', '--calc', FILE_TEST),
+ 'soffice': ('soffice', PROGRAM, FILE_TEST),
'install': ('unopkg', 'add', '-v', '-f', '-s'),
}
@@ -445,6 +453,8 @@ if PARENT == 'OfficeMenuBar':
def _get_context(args):
+ if not args:
+ return ''
c = []
for v in args.split(','):
c.append(CONTEXT[v])
@@ -472,29 +482,31 @@ for i, m in enumerate(MENUS):
toolbar.append(NODE_MENU.format(**values))
NODE_TOOLBAR = ''
-if PARENT == 'AddonMenu':
- NODE_MENUS = '\n'.join(menus)
-else:
- tmp = ' {}'
- titles = '\n'.join([tmp.format(k, v) for k, v in MENU_MAIN.items()])
- SUBMENUS = '\n ' + '\n'.join(menus) + '\n '
- NODE_MENUS = f"""
-
-{titles}
-
-
- _self
-
- {SUBMENUS}
- """
+NODE_MENUS = ''
+if TYPE_EXTENSION == 1:
+ if PARENT == 'AddonMenu':
+ NODE_MENUS = '\n'.join(menus)
+ else:
+ tmp = ' {}'
+ titles = '\n'.join([tmp.format(k, v) for k, v in MENU_MAIN.items()])
+ SUBMENUS = '\n ' + '\n'.join(menus) + '\n '
+ NODE_MENUS = f"""
+
+ {titles}
+
+
+ _self
+
+ {SUBMENUS}
+ """
- if toolbar:
- node_toolbars = '\n'.join(toolbar)
- NODE_TOOLBAR = f"""
-
-{node_toolbars}
-
- """
+ if toolbar:
+ node_toolbars = '\n'.join(toolbar)
+ NODE_TOOLBAR = f"""
+
+ {node_toolbars}
+
+ """
FILE_ADDONS = f"""
@@ -668,5 +680,5 @@ DATA = {
}
-# ~ LICENSE_ACCEPT_BY = 'admin' # or admin
+# ~ LICENSE_ACCEPT_BY = 'user' # or admin
# ~ LICENSE_SUPPRESS_ON_UPDATE = True
diff --git a/easymacro.py b/easymacro.py
index 76de6cf..622248c 100644
--- a/easymacro.py
+++ b/easymacro.py
@@ -18,15 +18,26 @@
# ~ 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
@@ -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.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
@@ -44,31 +56,46 @@ from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMouseListener
-FILE_NAME_DEBUG = 'debug.log'
MSG_LANG = {
'es': {
'OK': 'Aceptar',
'Cancel': 'Cancelar',
+ 'Select file': 'Seleccionar archivo',
}
}
-FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
-DATE = '%d/%m/%Y %H:%M:%S'
+
+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=FORMAT, datefmt=DATE)
+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()
@@ -96,17 +123,10 @@ def _get_config(key, node_name):
return ''
-OS = sys.platform
-USER = getpass.getuser()
-PC = platform.node()
-
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')
-# ~ DESKTOP = create_instance('com.sun.star.frame.Desktop', True)
-
-INFO_DEBUG = '{}\n\n{}\n\n{}'.format(
- sys.version, platform.platform(), '\n'.join(sys.path))
def mri(obj):
@@ -120,7 +140,45 @@ def mri(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
@@ -130,14 +188,11 @@ def error(info):
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
+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):
@@ -187,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()
@@ -203,6 +262,20 @@ def _path_system(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',
@@ -219,6 +292,11 @@ def get_type_doc(obj):
return ''
+def _properties(values):
+ p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
+ return tuple(p)
+
+
# ~ Custom classes
@@ -245,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()
@@ -281,6 +363,29 @@ class LODocument(object):
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):
@@ -313,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):
@@ -464,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):
@@ -587,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):
@@ -926,6 +1045,7 @@ class LODialog(object):
}
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))
@@ -998,3 +1118,377 @@ def set_properties(model, properties):
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/files/ZAZBarCode_v0.1.0.oxt b/files/ZAZBarCode_v0.1.0.oxt
index 9abfc0b..ec1f8a0 100644
Binary files a/files/ZAZBarCode_v0.1.0.oxt and b/files/ZAZBarCode_v0.1.0.oxt differ
diff --git a/files/ZAZBarCode_v0.2.0.oxt b/files/ZAZBarCode_v0.2.0.oxt
new file mode 100644
index 0000000..6487c55
Binary files /dev/null and b/files/ZAZBarCode_v0.2.0.oxt differ
diff --git a/files/zazbarcode.update.xml b/files/zazbarcode.update.xml
new file mode 100644
index 0000000..5788adf
--- /dev/null
+++ b/files/zazbarcode.update.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/source/Addons.xcu b/source/Addons.xcu
index 49732c5..5d68361 100644
--- a/source/Addons.xcu
+++ b/source/Addons.xcu
@@ -3,14 +3,14 @@
-
- ZAZ BarCode
+
+ ZAZ BarCode
ZAZ BarCode
-
-
- _self
-
-
+
+
+ _self
+
+
Insert BarCode
@@ -318,7 +318,7 @@
-
+
diff --git a/source/ZAZBarCode.py b/source/ZAZBarCode.py
index 00d03c8..d068ad3 100644
--- a/source/ZAZBarCode.py
+++ b/source/ZAZBarCode.py
@@ -1,13 +1,13 @@
+import gettext
import uno
import unohelper
from com.sun.star.task import XJobExecutor
+import easymacro as app
import qrcode
import qrcode.image.svg as svg
from barcode import generate
-import easymacro as app
-
ID_EXTENSION = 'net.elmau.zaz.BarCode'
SERVICE = ('com.sun.star.task.Job',)
@@ -15,20 +15,31 @@ TITLE = 'ZAZ BarCode'
QR = 'qrcode'
+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 Controllers(object):
+ @app.catch_exception
def __init__(self, dlg):
self.d = dlg
def button_action(self, event):
if not self.d.listbox.value:
self.d.listbox.set_focus()
- msg = 'Select barcode type'
+ msg = _('Select barcode type')
app.warning(msg, TITLE)
return
if not self.d.text.value.strip():
self.d.text.set_focus()
- msg = 'Data field is mandatory'
+ msg = _('Data field is mandatory')
app.warning(msg, TITLE)
return
@@ -43,6 +54,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
self._data = ''
self._type = ''
+ @app.catch_exception
def trigger(self, args):
self._type = args
if args == 'ask' and not self._get_values():
@@ -79,7 +91,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
if not self._data:
self._data = cell.value
if not self._data:
- msg = 'Select data'
+ msg = _('Select data')
self._show_error(msg)
return
@@ -101,7 +113,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
if not self._data:
self._data = sel.string
if not self._data:
- msg = 'Select data'
+ msg = _('Select data')
self._show_error(msg)
return
@@ -133,7 +145,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
return
def _show_error(self, error):
- msg = 'Error in: {}\n\n{}'.format(self._type, error)
+ msg = _('Error in: {}\n\n{}').format(self._type, error)
app.error(error)
app.errorbox(msg, TITLE)
return
@@ -151,12 +163,11 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
args = {
'Type': 'Label',
'Name': 'lbl_barcode',
- 'Label': 'Select barcode type',
- 'Width': 55,
+ 'Label': _('~Select barcode type'),
+ 'Width': 70,
'Height': 15,
'X': 10,
'Y': 10,
- 'Align': 2,
'VerticalAlign': 1,
}
dlg.add_control(args)
@@ -164,8 +175,8 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
args = {
'Type': 'Label',
'Name': 'lbl_data',
- 'Label': 'Capture data for barcode',
- 'Width': 70,
+ 'Label': _('~Capture data for barcode'),
+ 'Width': 100,
'Height': 15,
'VerticalAlign': 1,
}
@@ -174,7 +185,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
args = {
'Type': 'ListBox',
'Name': 'listbox',
- 'Width': 80,
+ 'Width': 65,
'Height': 15,
'Dropdown': True,
}
@@ -197,7 +208,7 @@ class ZAZBarCode(unohelper.Base, XJobExecutor):
args = {
'Type': 'Button',
'Name': 'button',
- 'Label': 'Insert Barcode',
+ 'Label': _('~Insert Barcode'),
'Width': 60,
'Height': 15,
}
diff --git a/source/description.xml b/source/description.xml
index c3521bd..4d2d54a 100644
--- a/source/description.xml
+++ b/source/description.xml
@@ -1,7 +1,7 @@
-
+
ZAZ Bar Code
ZAZ Códigos de Barras
@@ -23,4 +23,7 @@
+
+
+
diff --git a/source/locales/base.pot b/source/locales/base.pot
new file mode 100644
index 0000000..cd4a2f3
--- /dev/null
+++ b/source/locales/base.pot
@@ -0,0 +1,48 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"POT-Creation-Date: 2019-09-14 14:23-0500\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: pygettext.py 1.5\n"
+
+
+#: source/ZAZBarCode.py:38
+msgid "Select barcode type"
+msgstr ""
+
+#: source/ZAZBarCode.py:43
+msgid "Data field is mandatory"
+msgstr ""
+
+#: source/ZAZBarCode.py:95 source/ZAZBarCode.py:117
+msgid "Select data"
+msgstr ""
+
+#: source/ZAZBarCode.py:149
+msgid ""
+"Error in: {}\n"
+"\n"
+"{}"
+msgstr ""
+
+#: source/ZAZBarCode.py:167
+msgid "~Select barcode type"
+msgstr ""
+
+#: source/ZAZBarCode.py:179
+msgid "~Capture data for barcode"
+msgstr ""
+
+#: source/ZAZBarCode.py:212
+msgid "~Insert Barcode"
+msgstr ""
+
diff --git a/source/locales/en/LC_MESSAGES/base.mo b/source/locales/en/LC_MESSAGES/base.mo
new file mode 100644
index 0000000..18ce3d0
Binary files /dev/null and b/source/locales/en/LC_MESSAGES/base.mo differ
diff --git a/source/locales/en/LC_MESSAGES/base.po b/source/locales/en/LC_MESSAGES/base.po
new file mode 100644
index 0000000..76939ed
--- /dev/null
+++ b/source/locales/en/LC_MESSAGES/base.po
@@ -0,0 +1,49 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: 2019-09-14 14:23-0500\n"
+"PO-Revision-Date: 2019-09-14 14:45-0500\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: pygettext.py 1.5\n"
+"X-Generator: Poedit 2.2.1\n"
+"Last-Translator: \n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Language: en\n"
+
+#: source/ZAZBarCode.py:38
+msgid "Select barcode type"
+msgstr ""
+
+#: source/ZAZBarCode.py:43
+msgid "Data field is mandatory"
+msgstr ""
+
+#: source/ZAZBarCode.py:95 source/ZAZBarCode.py:117
+msgid "Select data"
+msgstr ""
+
+#: source/ZAZBarCode.py:149
+msgid ""
+"Error in: {}\n"
+"\n"
+"{}"
+msgstr ""
+
+#: source/ZAZBarCode.py:167
+msgid "~Select barcode type"
+msgstr ""
+
+#: source/ZAZBarCode.py:179
+msgid "~Capture data for barcode"
+msgstr ""
+
+#: source/ZAZBarCode.py:212
+msgid "~Insert Barcode"
+msgstr ""
diff --git a/source/locales/es/LC_MESSAGES/base.mo b/source/locales/es/LC_MESSAGES/base.mo
new file mode 100644
index 0000000..7bcdff2
Binary files /dev/null and b/source/locales/es/LC_MESSAGES/base.mo differ
diff --git a/source/locales/es/LC_MESSAGES/base.po b/source/locales/es/LC_MESSAGES/base.po
new file mode 100644
index 0000000..d98fafa
--- /dev/null
+++ b/source/locales/es/LC_MESSAGES/base.po
@@ -0,0 +1,52 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR ORGANIZATION
+# FIRST AUTHOR , YEAR.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: \n"
+"POT-Creation-Date: 2019-09-14 14:23-0500\n"
+"PO-Revision-Date: 2019-09-14 14:44-0500\n"
+"Language-Team: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Generated-By: pygettext.py 1.5\n"
+"X-Generator: Poedit 2.2.1\n"
+"Last-Translator: \n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Language: es\n"
+
+#: source/ZAZBarCode.py:38
+msgid "Select barcode type"
+msgstr "Seleccione el tipo de código de barras"
+
+#: source/ZAZBarCode.py:43
+msgid "Data field is mandatory"
+msgstr "Los datos son obligatorios"
+
+#: source/ZAZBarCode.py:95 source/ZAZBarCode.py:117
+msgid "Select data"
+msgstr "Seleccione los datos"
+
+#: source/ZAZBarCode.py:149
+msgid ""
+"Error in: {}\n"
+"\n"
+"{}"
+msgstr ""
+"Error en: {}\n"
+"\n"
+"{}"
+
+#: source/ZAZBarCode.py:167
+msgid "~Select barcode type"
+msgstr "~Tipo de código de barras"
+
+#: source/ZAZBarCode.py:179
+msgid "~Capture data for barcode"
+msgstr "~Datos del código de barras"
+
+#: source/ZAZBarCode.py:212
+msgid "~Insert Barcode"
+msgstr "~Insertar Código"
diff --git a/source/pythonpath/easymacro.py b/source/pythonpath/easymacro.py
index 76de6cf..622248c 100644
--- a/source/pythonpath/easymacro.py
+++ b/source/pythonpath/easymacro.py
@@ -18,15 +18,26 @@
# ~ 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
@@ -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.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
@@ -44,31 +56,46 @@ from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMouseListener
-FILE_NAME_DEBUG = 'debug.log'
MSG_LANG = {
'es': {
'OK': 'Aceptar',
'Cancel': 'Cancelar',
+ 'Select file': 'Seleccionar archivo',
}
}
-FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
-DATE = '%d/%m/%Y %H:%M:%S'
+
+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=FORMAT, datefmt=DATE)
+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()
@@ -96,17 +123,10 @@ def _get_config(key, node_name):
return ''
-OS = sys.platform
-USER = getpass.getuser()
-PC = platform.node()
-
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')
-# ~ DESKTOP = create_instance('com.sun.star.frame.Desktop', True)
-
-INFO_DEBUG = '{}\n\n{}\n\n{}'.format(
- sys.version, platform.platform(), '\n'.join(sys.path))
def mri(obj):
@@ -120,7 +140,45 @@ def mri(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
@@ -130,14 +188,11 @@ def error(info):
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
+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):
@@ -187,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()
@@ -203,6 +262,20 @@ def _path_system(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',
@@ -219,6 +292,11 @@ def get_type_doc(obj):
return ''
+def _properties(values):
+ p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
+ return tuple(p)
+
+
# ~ Custom classes
@@ -245,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()
@@ -281,6 +363,29 @@ class LODocument(object):
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):
@@ -313,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):
@@ -464,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):
@@ -587,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):
@@ -926,6 +1045,7 @@ class LODialog(object):
}
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))
@@ -998,3 +1118,377 @@ def set_properties(model, properties):
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/zaz.py b/zaz.py
index 7a0999f..92c0533 100644
--- a/zaz.py
+++ b/zaz.py
@@ -74,6 +74,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