Add support for modify menus

This commit is contained in:
Mauricio Baeza 2019-09-18 21:41:47 -05:00
parent 9dce2cc503
commit 598692003c
4 changed files with 352 additions and 39 deletions

View File

@ -1,3 +1,6 @@
v 0.6.0 [18-sep-2019]
- Add support for modify menus
v 0.5.0 [15-sep-2019]
---------------------
- Add support for shortcuts

View File

@ -1 +1 @@
0.5.0
0.6.0

View File

@ -584,7 +584,6 @@ NODE_SHORTCUT = """ {0}<node oor:name="{1}" oor:op="fuse">
{0}</node>
"""
NODE_SHORTCUTS = ''
if TYPE_EXTENSION == 1:
node_global = []

View File

@ -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