diff --git a/CHANGELOG b/CHANGELOG
index fdd2314..c351bc4 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,6 @@
+v 0.3.0 [18-sep-2019]
+ - Add support for generate barcode by code
+
v 0.2.1 [16-sep-2019]
- Fix #1
diff --git a/README.md b/README.md
index ae7e03f..3afea37 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,9 @@ https://github.com/WhyNotHugo/python-barcode
https://github.com/lincolnloop/python-qrcode
+### Software libre, no gratis
+
+
This extension have a cost of maintenance of 1 euro every year.
BCH: `1RPLWHJW34p7pMQV1ft4x7eWhAYw69Dsb`
@@ -40,3 +43,8 @@ BTC: `3Fe4JuADrAK8Qs7GDAxbSXR8E54avwZJLW`
* [See the wiki](https://gitlab.com/mauriciobaeza/zaz-barcode/wikis/home)
* [Mira la wiki](https://gitlab.com/mauriciobaeza/zaz-barcode/wikis/home_es)
+
+
+Thanks for translations:
+
+Esperanto: [Guillermo Molleda](https://gitlab.com/gmolleda)
diff --git a/VERSION b/VERSION
index cf74bd3..69367fd 100644
--- a/VERSION
+++ b/VERSION
@@ -1,2 +1,2 @@
-0.2.1
+0.3.0
diff --git a/conf.py b/conf.py
index 614eab2..7096f82 100644
--- a/conf.py
+++ b/conf.py
@@ -26,7 +26,7 @@ import logging
TYPE_EXTENSION = 1
# ~ https://semver.org/
-VERSION = '0.2.1'
+VERSION = '0.3.0'
# ~ Your great extension name, not used spaces
NAME = 'ZAZBarCode'
@@ -697,7 +697,6 @@ NODE_SHORTCUT = """ {0}
{0}
"""
-
NODE_SHORTCUTS = ''
if TYPE_EXTENSION == 1:
node_global = []
diff --git a/easymacro.py b/easymacro.py
index 15ea311..8b41c24 100644
--- a/easymacro.py
+++ b/easymacro.py
@@ -22,10 +22,13 @@ import ctypes
import datetime
import errno
import getpass
+import json
import logging
import os
import platform
+import re
import shlex
+import shutil
import subprocess
import sys
import tempfile
@@ -33,6 +36,8 @@ import threading
import time
import zipfile
+from collections import OrderedDict
+from collections.abc import MutableMapping
from datetime import datetime
from functools import wraps
from pathlib import Path, PurePath
@@ -65,26 +70,18 @@ MSG_LANG = {
}
-FILE_NAME_DEBUG = 'zaz-debug.log'
-LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
-LOG_DATE = '%d/%m/%Y %H:%M:%S'
-logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
-logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
-logging.addLevelName(logging.INFO, '\x1b[32mINFO\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'
@@ -92,6 +89,46 @@ OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj'
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
+TYPE_DOC = {
+ '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',
+}
+
+NODE_MENUBAR = 'private:resource/menubar/menubar'
+MENUS_CALC = {
+ 'file': '.uno:PickList',
+ 'edit': '.uno:EditMenu',
+ 'view': '.uno:ViewMenu',
+ 'insert': '.uno:InsertMenu',
+ 'format': '.uno:FormatMenu',
+ 'styles': '.uno:FormatStylesMenu',
+ 'sheet': '.uno:SheetMenu',
+ 'data': '.uno:DataMenu',
+ 'tools': '.uno:ToolsMenu',
+ 'windows': '.uno:WindowList',
+ 'help': '.uno:HelpMenu',
+}
+
+MENUS_APP = {
+ 'calc': MENUS_CALC,
+}
+
+
+FILE_NAME_DEBUG = 'zaz-debug.log'
+FILE_NAME_CONFIG = 'zaz-config.json'
+LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
+LOG_DATE = '%d/%m/%Y %H:%M:%S'
+logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
+logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
+logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
+logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
+log = logging.getLogger(__name__)
+
CTX = uno.getComponentContext()
SM = CTX.getServiceManager()
@@ -105,7 +142,7 @@ def create_instance(name, with_context=False):
return instance
-def _get_config(key, node_name):
+def _get_app_config(key, node_name):
name = 'com.sun.star.configuration.ConfigurationProvider'
service = 'com.sun.star.configuration.ConfigurationAccess'
cp = create_instance(name, True)
@@ -120,10 +157,10 @@ def _get_config(key, node_name):
return ''
-LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
+LANGUAGE = _get_app_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')
+NAME = TITLE = _get_app_config('ooName', 'org.openoffice.Setup/Product')
+VERSION = _get_app_config('ooSetupVersion', 'org.openoffice.Setup/Product')
def mri(obj):
@@ -202,8 +239,34 @@ def run_in_thread(fn):
return run
-def sleep(sec):
- time.sleep(sec)
+def get_config(key=''):
+ values = {}
+ path = join(get_config_path('UserConfig'), FILE_NAME_CONFIG)
+ if not exists_path(path):
+ return values
+
+ with open(path, 'r', encoding='utf-8') as fh:
+ data = fh.read()
+ if data:
+ values = json.loads(data)
+
+ if key:
+ return values.get(key, None)
+
+ return values
+
+
+def set_config(key, value):
+ path = join(get_config_path('UserConfig'), FILE_NAME_CONFIG)
+ values = get_config()
+ values[key] = value
+ with open(path, 'w', encoding='utf-8') as fh:
+ json.dump(values, fh, ensure_ascii=False, sort_keys=True, indent=4)
+ return True
+
+
+def sleep(seconds):
+ time.sleep(seconds)
return
@@ -278,30 +341,116 @@ def exists_app(name):
return False
return True
-
+# ~ Delete
def exists(path):
return Path(path).exists()
+def exists_path(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():
+ # ~ 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 TYPE_DOC.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)
+# ~ def _properties(values):
+ # ~ p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
+ # ~ return tuple(p)
+
+def dict_to_property(values, uno_any=False):
+ ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
+ if uno_any:
+ ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
+ return ps
+
+
+def property_to_dict(values):
+ d = {i.Name: i.Value for i in values}
+ return d
+
+
+# ~ Third classes
+
+
+# ~ https://github.com/psf/requests/blob/v2.22.0/requests/structures.py
+class CaseInsensitiveDict(MutableMapping):
+ """A case-insensitive ``dict``-like object.
+ Implements all methods and operations of
+ ``MutableMapping`` as well as dict's ``copy``. Also
+ provides ``lower_items``.
+ All keys are expected to be strings. The structure remembers the
+ case of the last key to be set, and ``iter(instance)``,
+ ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
+ will contain case-sensitive keys. However, querying and contains
+ testing is case insensitive::
+ cid = CaseInsensitiveDict()
+ cid['Accept'] = 'application/json'
+ cid['aCCEPT'] == 'application/json' # True
+ list(cid) == ['Accept'] # True
+ For example, ``headers['content-encoding']`` will return the
+ value of a ``'Content-Encoding'`` response header, regardless
+ of how the header name was originally stored.
+ If the constructor, ``.update``, or equality comparison
+ operations are given keys that have equal ``.lower()``s, the
+ behavior is undefined.
+ """
+
+ def __init__(self, data=None, **kwargs):
+ self._store = OrderedDict()
+ if data is None:
+ data = {}
+ self.update(data, **kwargs)
+
+ def __setitem__(self, key, value):
+ # Use the lowercased key for lookups, but store the actual
+ # key alongside the value.
+ self._store[key.lower()] = (key, value)
+
+ def __getitem__(self, key):
+ return self._store[key.lower()][1]
+
+ def __delitem__(self, key):
+ del self._store[key.lower()]
+
+ def __iter__(self):
+ return (casedkey for casedkey, mappedvalue in self._store.values())
+
+ def __len__(self):
+ return len(self._store)
+
+ def lower_items(self):
+ """Like iteritems(), but with all lowercase keys."""
+ return (
+ (lowerkey, keyval[1])
+ for (lowerkey, keyval)
+ in self._store.items()
+ )
+
+ def __eq__(self, other):
+ if isinstance(other, Mapping):
+ other = CaseInsensitiveDict(other)
+ else:
+ return NotImplemented
+ # Compare insensitively
+ return dict(self.lower_items()) == dict(other.lower_items())
+
+ # Copy is required
+ def copy(self):
+ return CaseInsensitiveDict(self._store.values())
+
+ def __repr__(self):
+ return str(dict(self.items()))
# ~ Custom classes
@@ -371,7 +520,8 @@ class LODocument(object):
return obj
def save(self, path='', **kwargs):
- opt = _properties(kwargs)
+ # ~ opt = _properties(kwargs)
+ opt = dict_to_property(kwargs)
if path:
self._obj.storeAsURL(_path_url(path), opt)
else:
@@ -1127,9 +1277,30 @@ def set_properties(model, properties):
return
-def get_file(filters=(), multiple=False):
+def get_config_path(name='Work'):
+ """
+ Return de path name in config
+ http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1util_1_1XPathSettings.html
+ """
+ path = create_instance('com.sun.star.util.PathSettings')
+ return _path_system(getattr(path, name))
+
+
+def get_file(init_dir='', multiple=False, filters=()):
+ """
+ init_folder: folder default open
+ multiple: True for multiple selected
+ filters: Example
+ (
+ ('XML', '*.xml'),
+ ('TXT', '*.txt'),
+ )
+ """
+ if not init_dir:
+ init_dir = get_config_path()
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file'))
+ file_picker.setDisplayDirectory(init_dir)
file_picker.setMultiSelectionMode(multiple)
if filters:
@@ -1242,7 +1413,8 @@ def open_doc(path, **kwargs):
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
"""
path = _path_url(path)
- opt = _properties(kwargs)
+ # ~ opt = _properties(kwargs)
+ opt = dict_to_property(kwargs)
doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt)
if doc is None:
return
@@ -1278,9 +1450,9 @@ def is_created(path):
return is_file(path) and bool(get_file_size(path))
-def replace_ext(path, ext):
+def replace_ext(path, extension):
path, _, name, _ = get_info_path(path)
- return '{}/{}.{}'.format(path, name, ext)
+ return '{}/{}.{}'.format(path, name, extension)
def zip_names(path):
@@ -1477,8 +1649,8 @@ class TextTransferable(unohelper.Base, XTransferable):
return False
-def set_clipboard(text):
- ts = TextTransferable(text)
+def set_clipboard(value):
+ ts = TextTransferable(value)
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
sc.setContents(ts, None)
return
@@ -1499,3 +1671,142 @@ def copy(doc=None):
def get_epoch():
now = datetime.datetime.now()
return int(time.mktime(now.timetuple()))
+
+
+def file_copy(source, target='', name=''):
+ p, f, n, e = get_info_path(source)
+ if target:
+ p = target
+ if name:
+ n = name
+ path_new = join(p, '{}{}'.format(n, e))
+ shutil.copy(source, path_new)
+ return
+
+
+def get_files(path, ext='*'):
+ docs = []
+ for folder, _, files in os.walk(path):
+ pattern = re.compile(r'\.{}'.format(ext), re.IGNORECASE)
+ docs += [join(folder, f) for f in files if pattern.search(f)]
+ return docs
+
+
+def _get_menu(type_doc, name_menu):
+ instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
+ service = TYPE_DOC[type_doc]
+ manager = create_instance(instance, True)
+ ui = manager.getUIConfigurationManager(service)
+ menus = ui.getSettings(NODE_MENUBAR, True)
+ command = MENUS_APP[type_doc][name_menu]
+ for menu in menus:
+ data = property_to_dict(menu)
+ if data.get('CommandURL', '') == command:
+ idc = data.get('ItemDescriptorContainer', None)
+ return ui, menus, idc
+ return None, None, None
+
+
+def _get_index_menu(menu, command):
+ for i, m in enumerate(menu):
+ data = property_to_dict(m)
+ cmd = data.get('CommandURL', '')
+ if cmd == command:
+ return i
+ # ~ submenu = data.get('ItemDescriptorContainer', None)
+ # ~ if not submenu is None:
+ # ~ get_index_menu(submenu, command, count + 1)
+ return 0
+
+
+def _store_menu(ui, menus, menu, index, data=(), remove=False):
+ if remove:
+ uno.invoke(menu, 'removeByIndex', (index,))
+ else:
+ properties = dict_to_property(data, True)
+ uno.invoke(menu, 'insertByIndex', (index + 1, properties))
+ ui.replaceSettings(NODE_MENUBAR, menus)
+ ui.store()
+ return
+
+
+def insert_menu(type_doc, name_menu, **kwargs):
+ ui, menus, menu = _get_menu(type_doc, name_menu.lower())
+ if menu is None:
+ return 0
+
+ label = kwargs.get('Label', '-')
+ separator = False
+ if label == '-':
+ separator = True
+ command = kwargs.get('Command', '')
+ index = kwargs.get('Index', 0)
+ if not index:
+ index = _get_index_menu(menu, kwargs['After'])
+ if separator:
+ data = {'Type': 1}
+ _store_menu(ui, menus, menu, index, data)
+ return index + 1
+
+ index_menu = _get_index_menu(menu, command)
+ if index_menu:
+ msg = 'Exists: %s' % command
+ debug(msg)
+ return 0
+
+ sub_menu = kwargs.get('Submenu', ())
+ idc = None
+ if sub_menu:
+ idc = ui.createSettings()
+
+ data = {
+ 'CommandURL': command,
+ 'Label': label,
+ 'Style': 0,
+ 'Type': 0,
+ 'ItemDescriptorContainer': idc
+ }
+ _store_menu(ui, menus, menu, index, data)
+ if sub_menu:
+ _add_sub_menus(ui, menus, idc, sub_menu)
+ return True
+
+
+def _add_sub_menus(ui, menus, menu, sub_menu):
+ for i, sm in enumerate(sub_menu):
+ submenu = sm.pop('Submenu', ())
+ sm['Type'] = 0
+ if submenu:
+ idc = ui.createSettings()
+ sm['ItemDescriptorContainer'] = idc
+ _store_menu(ui, menus, menu, i - 1, sm)
+ if submenu:
+ _add_sub_menus(ui, menus, idc, submenu)
+ return
+
+
+def remove_menu(type_doc, name_menu, command):
+ ui, menus, menu = _get_menu(type_doc, name_menu.lower())
+ if menu is None:
+ return False
+
+ index = _get_index_menu(menu, command)
+ if not index:
+ debug('Not exists: %s' % command)
+ return False
+
+ _store_menu(ui, menus, menu, index, remove=True)
+ return True
+
+
+# ~ name = 'com.sun.star.configuration.ConfigurationProvider'
+# ~ cp = create_instance(name, True)
+# ~ node = PropertyValue(Name='nodepath', Value=NODE_SETTING)
+# ~ try:
+ # ~ cua = cp.createInstanceWithArguments(
+ # ~ 'com.sun.star.configuration.ConfigurationUpdateAccess', (node,))
+ # ~ cua.setPropertyValue(key, json.dumps(value))
+ # ~ cua.commitChanges()
+# ~ except Exception as e:
+ # ~ log.error(e, exc_info=True)
+ # ~ return False
diff --git a/files/ZAZBarCode_v0.2.1.oxt b/files/ZAZBarCode_v0.2.1.oxt
index 5e30629..21c5190 100644
Binary files a/files/ZAZBarCode_v0.2.1.oxt and b/files/ZAZBarCode_v0.2.1.oxt differ
diff --git a/files/ZAZBarCode_v0.3.0.oxt b/files/ZAZBarCode_v0.3.0.oxt
new file mode 100644
index 0000000..492ab05
Binary files /dev/null and b/files/ZAZBarCode_v0.3.0.oxt differ
diff --git a/files/zazbarcode.update.xml b/files/zazbarcode.update.xml
index 09e7bae..ffa7315 100644
--- a/files/zazbarcode.update.xml
+++ b/files/zazbarcode.update.xml
@@ -5,10 +5,10 @@
xmlns:xlink="http://www.w3.org/1999/xlink">
-
+
-
+
diff --git a/source/ZAZBarCode.py b/source/ZAZBarCode.py
index 1caf0c4..7fcb43a 100644
--- a/source/ZAZBarCode.py
+++ b/source/ZAZBarCode.py
@@ -1,7 +1,7 @@
import gettext
import uno
import unohelper
-from com.sun.star.task import XJobExecutor
+from com.sun.star.task import XJobExecutor, XJob
import easymacro as app
import qrcode
@@ -22,6 +22,7 @@ try:
lang.install()
_ = lang.gettext
except Exception as e:
+ from gettext import gettext as _
app.error(e)
@@ -46,13 +47,27 @@ class Controllers(object):
return
-class ZAZBarCode(unohelper.Base, XJobExecutor):
+class ZAZBarCode(unohelper.Base, XJob, XJobExecutor):
def __init__(self, ctx):
self.ctx = ctx
self._data = ''
self._type = ''
+ def execute(self, args):
+ data = app.property_to_dict(args)
+
+ if data['Type'] == QR:
+ factory = svg.SvgImage
+ img = qrcode.make(data['Data'], image_factory=factory)
+ img.save(data['Path'])
+ else:
+ try:
+ generate(data['Type'].lower(), data['Data'], output=data['Path'])
+ except Exception as e:
+ app.debug(str(e))
+ return
+
def trigger(self, args):
self._type = args
if args == 'ask' and not self._get_values():
diff --git a/source/description.xml b/source/description.xml
index 02d2290..c574c9c 100644
--- a/source/description.xml
+++ b/source/description.xml
@@ -1,7 +1,7 @@
-
+
ZAZ Bar Code
ZAZ Códigos de Barras
diff --git a/source/locales/eo/LC_MESSAGES/base.mo b/source/locales/eo/LC_MESSAGES/base.mo
new file mode 100644
index 0000000..b42efbd
Binary files /dev/null and b/source/locales/eo/LC_MESSAGES/base.mo differ
diff --git a/source/locales/eo/LC_MESSAGES/base.po b/source/locales/eo/LC_MESSAGES/base.po
new file mode 100644
index 0000000..a26f435
--- /dev/null
+++ b/source/locales/eo/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-20 19:21+0200\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.0.6\n"
+"Last-Translator: \n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+"Language: eo\n"
+
+#: source/ZAZBarCode.py:38
+msgid "Select barcode type"
+msgstr "Elektu strikodan tipon"
+
+#: source/ZAZBarCode.py:43
+msgid "Data field is mandatory"
+msgstr "Datumkampo estas deviga"
+
+#: source/ZAZBarCode.py:95 source/ZAZBarCode.py:117
+msgid "Select data"
+msgstr "Elektu datumojn"
+
+#: source/ZAZBarCode.py:149
+msgid ""
+"Error in: {}\n"
+"\n"
+"{}"
+msgstr ""
+"Eraro en: {}\n"
+"\n"
+"{}"
+
+#: source/ZAZBarCode.py:167
+msgid "~Select barcode type"
+msgstr "~Elektu strikodan tipon"
+
+#: source/ZAZBarCode.py:179
+msgid "~Capture data for barcode"
+msgstr "~Kaptu datumojn por strikodo"
+
+#: source/ZAZBarCode.py:212
+msgid "~Insert Barcode"
+msgstr "~Enmetu Strikodon"
diff --git a/source/pythonpath/easymacro.py b/source/pythonpath/easymacro.py
index 15ea311..8b41c24 100644
--- a/source/pythonpath/easymacro.py
+++ b/source/pythonpath/easymacro.py
@@ -22,10 +22,13 @@ import ctypes
import datetime
import errno
import getpass
+import json
import logging
import os
import platform
+import re
import shlex
+import shutil
import subprocess
import sys
import tempfile
@@ -33,6 +36,8 @@ import threading
import time
import zipfile
+from collections import OrderedDict
+from collections.abc import MutableMapping
from datetime import datetime
from functools import wraps
from pathlib import Path, PurePath
@@ -65,26 +70,18 @@ MSG_LANG = {
}
-FILE_NAME_DEBUG = 'zaz-debug.log'
-LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
-LOG_DATE = '%d/%m/%Y %H:%M:%S'
-logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
-logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
-logging.addLevelName(logging.INFO, '\x1b[32mINFO\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'
@@ -92,6 +89,46 @@ OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj'
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
+TYPE_DOC = {
+ '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',
+}
+
+NODE_MENUBAR = 'private:resource/menubar/menubar'
+MENUS_CALC = {
+ 'file': '.uno:PickList',
+ 'edit': '.uno:EditMenu',
+ 'view': '.uno:ViewMenu',
+ 'insert': '.uno:InsertMenu',
+ 'format': '.uno:FormatMenu',
+ 'styles': '.uno:FormatStylesMenu',
+ 'sheet': '.uno:SheetMenu',
+ 'data': '.uno:DataMenu',
+ 'tools': '.uno:ToolsMenu',
+ 'windows': '.uno:WindowList',
+ 'help': '.uno:HelpMenu',
+}
+
+MENUS_APP = {
+ 'calc': MENUS_CALC,
+}
+
+
+FILE_NAME_DEBUG = 'zaz-debug.log'
+FILE_NAME_CONFIG = 'zaz-config.json'
+LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
+LOG_DATE = '%d/%m/%Y %H:%M:%S'
+logging.addLevelName(logging.ERROR, '\033[1;41mERROR\033[1;0m')
+logging.addLevelName(logging.DEBUG, '\x1b[33mDEBUG\033[1;0m')
+logging.addLevelName(logging.INFO, '\x1b[32mINFO\033[1;0m')
+logging.basicConfig(level=logging.DEBUG, format=LOG_FORMAT, datefmt=LOG_DATE)
+log = logging.getLogger(__name__)
+
CTX = uno.getComponentContext()
SM = CTX.getServiceManager()
@@ -105,7 +142,7 @@ def create_instance(name, with_context=False):
return instance
-def _get_config(key, node_name):
+def _get_app_config(key, node_name):
name = 'com.sun.star.configuration.ConfigurationProvider'
service = 'com.sun.star.configuration.ConfigurationAccess'
cp = create_instance(name, True)
@@ -120,10 +157,10 @@ def _get_config(key, node_name):
return ''
-LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
+LANGUAGE = _get_app_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')
+NAME = TITLE = _get_app_config('ooName', 'org.openoffice.Setup/Product')
+VERSION = _get_app_config('ooSetupVersion', 'org.openoffice.Setup/Product')
def mri(obj):
@@ -202,8 +239,34 @@ def run_in_thread(fn):
return run
-def sleep(sec):
- time.sleep(sec)
+def get_config(key=''):
+ values = {}
+ path = join(get_config_path('UserConfig'), FILE_NAME_CONFIG)
+ if not exists_path(path):
+ return values
+
+ with open(path, 'r', encoding='utf-8') as fh:
+ data = fh.read()
+ if data:
+ values = json.loads(data)
+
+ if key:
+ return values.get(key, None)
+
+ return values
+
+
+def set_config(key, value):
+ path = join(get_config_path('UserConfig'), FILE_NAME_CONFIG)
+ values = get_config()
+ values[key] = value
+ with open(path, 'w', encoding='utf-8') as fh:
+ json.dump(values, fh, ensure_ascii=False, sort_keys=True, indent=4)
+ return True
+
+
+def sleep(seconds):
+ time.sleep(seconds)
return
@@ -278,30 +341,116 @@ def exists_app(name):
return False
return True
-
+# ~ Delete
def exists(path):
return Path(path).exists()
+def exists_path(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():
+ # ~ 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 TYPE_DOC.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)
+# ~ def _properties(values):
+ # ~ p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
+ # ~ return tuple(p)
+
+def dict_to_property(values, uno_any=False):
+ ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
+ if uno_any:
+ ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
+ return ps
+
+
+def property_to_dict(values):
+ d = {i.Name: i.Value for i in values}
+ return d
+
+
+# ~ Third classes
+
+
+# ~ https://github.com/psf/requests/blob/v2.22.0/requests/structures.py
+class CaseInsensitiveDict(MutableMapping):
+ """A case-insensitive ``dict``-like object.
+ Implements all methods and operations of
+ ``MutableMapping`` as well as dict's ``copy``. Also
+ provides ``lower_items``.
+ All keys are expected to be strings. The structure remembers the
+ case of the last key to be set, and ``iter(instance)``,
+ ``keys()``, ``items()``, ``iterkeys()``, and ``iteritems()``
+ will contain case-sensitive keys. However, querying and contains
+ testing is case insensitive::
+ cid = CaseInsensitiveDict()
+ cid['Accept'] = 'application/json'
+ cid['aCCEPT'] == 'application/json' # True
+ list(cid) == ['Accept'] # True
+ For example, ``headers['content-encoding']`` will return the
+ value of a ``'Content-Encoding'`` response header, regardless
+ of how the header name was originally stored.
+ If the constructor, ``.update``, or equality comparison
+ operations are given keys that have equal ``.lower()``s, the
+ behavior is undefined.
+ """
+
+ def __init__(self, data=None, **kwargs):
+ self._store = OrderedDict()
+ if data is None:
+ data = {}
+ self.update(data, **kwargs)
+
+ def __setitem__(self, key, value):
+ # Use the lowercased key for lookups, but store the actual
+ # key alongside the value.
+ self._store[key.lower()] = (key, value)
+
+ def __getitem__(self, key):
+ return self._store[key.lower()][1]
+
+ def __delitem__(self, key):
+ del self._store[key.lower()]
+
+ def __iter__(self):
+ return (casedkey for casedkey, mappedvalue in self._store.values())
+
+ def __len__(self):
+ return len(self._store)
+
+ def lower_items(self):
+ """Like iteritems(), but with all lowercase keys."""
+ return (
+ (lowerkey, keyval[1])
+ for (lowerkey, keyval)
+ in self._store.items()
+ )
+
+ def __eq__(self, other):
+ if isinstance(other, Mapping):
+ other = CaseInsensitiveDict(other)
+ else:
+ return NotImplemented
+ # Compare insensitively
+ return dict(self.lower_items()) == dict(other.lower_items())
+
+ # Copy is required
+ def copy(self):
+ return CaseInsensitiveDict(self._store.values())
+
+ def __repr__(self):
+ return str(dict(self.items()))
# ~ Custom classes
@@ -371,7 +520,8 @@ class LODocument(object):
return obj
def save(self, path='', **kwargs):
- opt = _properties(kwargs)
+ # ~ opt = _properties(kwargs)
+ opt = dict_to_property(kwargs)
if path:
self._obj.storeAsURL(_path_url(path), opt)
else:
@@ -1127,9 +1277,30 @@ def set_properties(model, properties):
return
-def get_file(filters=(), multiple=False):
+def get_config_path(name='Work'):
+ """
+ Return de path name in config
+ http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1util_1_1XPathSettings.html
+ """
+ path = create_instance('com.sun.star.util.PathSettings')
+ return _path_system(getattr(path, name))
+
+
+def get_file(init_dir='', multiple=False, filters=()):
+ """
+ init_folder: folder default open
+ multiple: True for multiple selected
+ filters: Example
+ (
+ ('XML', '*.xml'),
+ ('TXT', '*.txt'),
+ )
+ """
+ if not init_dir:
+ init_dir = get_config_path()
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file'))
+ file_picker.setDisplayDirectory(init_dir)
file_picker.setMultiSelectionMode(multiple)
if filters:
@@ -1242,7 +1413,8 @@ def open_doc(path, **kwargs):
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
"""
path = _path_url(path)
- opt = _properties(kwargs)
+ # ~ opt = _properties(kwargs)
+ opt = dict_to_property(kwargs)
doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt)
if doc is None:
return
@@ -1278,9 +1450,9 @@ def is_created(path):
return is_file(path) and bool(get_file_size(path))
-def replace_ext(path, ext):
+def replace_ext(path, extension):
path, _, name, _ = get_info_path(path)
- return '{}/{}.{}'.format(path, name, ext)
+ return '{}/{}.{}'.format(path, name, extension)
def zip_names(path):
@@ -1477,8 +1649,8 @@ class TextTransferable(unohelper.Base, XTransferable):
return False
-def set_clipboard(text):
- ts = TextTransferable(text)
+def set_clipboard(value):
+ ts = TextTransferable(value)
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
sc.setContents(ts, None)
return
@@ -1499,3 +1671,142 @@ def copy(doc=None):
def get_epoch():
now = datetime.datetime.now()
return int(time.mktime(now.timetuple()))
+
+
+def file_copy(source, target='', name=''):
+ p, f, n, e = get_info_path(source)
+ if target:
+ p = target
+ if name:
+ n = name
+ path_new = join(p, '{}{}'.format(n, e))
+ shutil.copy(source, path_new)
+ return
+
+
+def get_files(path, ext='*'):
+ docs = []
+ for folder, _, files in os.walk(path):
+ pattern = re.compile(r'\.{}'.format(ext), re.IGNORECASE)
+ docs += [join(folder, f) for f in files if pattern.search(f)]
+ return docs
+
+
+def _get_menu(type_doc, name_menu):
+ instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
+ service = TYPE_DOC[type_doc]
+ manager = create_instance(instance, True)
+ ui = manager.getUIConfigurationManager(service)
+ menus = ui.getSettings(NODE_MENUBAR, True)
+ command = MENUS_APP[type_doc][name_menu]
+ for menu in menus:
+ data = property_to_dict(menu)
+ if data.get('CommandURL', '') == command:
+ idc = data.get('ItemDescriptorContainer', None)
+ return ui, menus, idc
+ return None, None, None
+
+
+def _get_index_menu(menu, command):
+ for i, m in enumerate(menu):
+ data = property_to_dict(m)
+ cmd = data.get('CommandURL', '')
+ if cmd == command:
+ return i
+ # ~ submenu = data.get('ItemDescriptorContainer', None)
+ # ~ if not submenu is None:
+ # ~ get_index_menu(submenu, command, count + 1)
+ return 0
+
+
+def _store_menu(ui, menus, menu, index, data=(), remove=False):
+ if remove:
+ uno.invoke(menu, 'removeByIndex', (index,))
+ else:
+ properties = dict_to_property(data, True)
+ uno.invoke(menu, 'insertByIndex', (index + 1, properties))
+ ui.replaceSettings(NODE_MENUBAR, menus)
+ ui.store()
+ return
+
+
+def insert_menu(type_doc, name_menu, **kwargs):
+ ui, menus, menu = _get_menu(type_doc, name_menu.lower())
+ if menu is None:
+ return 0
+
+ label = kwargs.get('Label', '-')
+ separator = False
+ if label == '-':
+ separator = True
+ command = kwargs.get('Command', '')
+ index = kwargs.get('Index', 0)
+ if not index:
+ index = _get_index_menu(menu, kwargs['After'])
+ if separator:
+ data = {'Type': 1}
+ _store_menu(ui, menus, menu, index, data)
+ return index + 1
+
+ index_menu = _get_index_menu(menu, command)
+ if index_menu:
+ msg = 'Exists: %s' % command
+ debug(msg)
+ return 0
+
+ sub_menu = kwargs.get('Submenu', ())
+ idc = None
+ if sub_menu:
+ idc = ui.createSettings()
+
+ data = {
+ 'CommandURL': command,
+ 'Label': label,
+ 'Style': 0,
+ 'Type': 0,
+ 'ItemDescriptorContainer': idc
+ }
+ _store_menu(ui, menus, menu, index, data)
+ if sub_menu:
+ _add_sub_menus(ui, menus, idc, sub_menu)
+ return True
+
+
+def _add_sub_menus(ui, menus, menu, sub_menu):
+ for i, sm in enumerate(sub_menu):
+ submenu = sm.pop('Submenu', ())
+ sm['Type'] = 0
+ if submenu:
+ idc = ui.createSettings()
+ sm['ItemDescriptorContainer'] = idc
+ _store_menu(ui, menus, menu, i - 1, sm)
+ if submenu:
+ _add_sub_menus(ui, menus, idc, submenu)
+ return
+
+
+def remove_menu(type_doc, name_menu, command):
+ ui, menus, menu = _get_menu(type_doc, name_menu.lower())
+ if menu is None:
+ return False
+
+ index = _get_index_menu(menu, command)
+ if not index:
+ debug('Not exists: %s' % command)
+ return False
+
+ _store_menu(ui, menus, menu, index, remove=True)
+ return True
+
+
+# ~ name = 'com.sun.star.configuration.ConfigurationProvider'
+# ~ cp = create_instance(name, True)
+# ~ node = PropertyValue(Name='nodepath', Value=NODE_SETTING)
+# ~ try:
+ # ~ cua = cp.createInstanceWithArguments(
+ # ~ 'com.sun.star.configuration.ConfigurationUpdateAccess', (node,))
+ # ~ cua.setPropertyValue(key, json.dumps(value))
+ # ~ cua.commitChanges()
+# ~ except Exception as e:
+ # ~ log.error(e, exc_info=True)
+ # ~ return False