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