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] v 0.5.0 [15-sep-2019]
--------------------- ---------------------
- Add support for shortcuts - 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> {0}</node>
""" """
NODE_SHORTCUTS = '' NODE_SHORTCUTS = ''
if TYPE_EXTENSION == 1: if TYPE_EXTENSION == 1:
node_global = [] node_global = []

View File

@ -22,10 +22,13 @@ import ctypes
import datetime import datetime
import errno import errno
import getpass import getpass
import json
import logging import logging
import os import os
import platform import platform
import re
import shlex import shlex
import shutil
import subprocess import subprocess
import sys import sys
import tempfile import tempfile
@ -33,6 +36,8 @@ import threading
import time import time
import zipfile import zipfile
from collections import OrderedDict
from collections.abc import MutableMapping
from datetime import datetime from datetime import datetime
from functools import wraps from functools import wraps
from pathlib import Path, PurePath 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() OS = platform.system()
USER = getpass.getuser() USER = getpass.getuser()
PC = platform.node() PC = platform.node()
DESKTOP = os.environ.get('DESKTOP_SESSION', '') DESKTOP = os.environ.get('DESKTOP_SESSION', '')
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path)) INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
IS_WIN = OS == 'Windows' IS_WIN = OS == 'Windows'
LOG_NAME = 'ZAZ' LOG_NAME = 'ZAZ'
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16' CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
CALC = 'calc' CALC = 'calc'
WRITER = 'writer' WRITER = 'writer'
OBJ_CELL = 'ScCellObj' OBJ_CELL = 'ScCellObj'
@ -92,6 +89,46 @@ OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj' OBJ_RANGES = 'ScCellRangesObj'
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES) 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() CTX = uno.getComponentContext()
SM = CTX.getServiceManager() SM = CTX.getServiceManager()
@ -105,7 +142,7 @@ def create_instance(name, with_context=False):
return instance return instance
def _get_config(key, node_name): def _get_app_config(key, node_name):
name = 'com.sun.star.configuration.ConfigurationProvider' name = 'com.sun.star.configuration.ConfigurationProvider'
service = 'com.sun.star.configuration.ConfigurationAccess' service = 'com.sun.star.configuration.ConfigurationAccess'
cp = create_instance(name, True) cp = create_instance(name, True)
@ -120,10 +157,10 @@ def _get_config(key, node_name):
return '' return ''
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/') LANGUAGE = _get_app_config('ooLocale', 'org.openoffice.Setup/L10N/')
LANG = LANGUAGE.split('-')[0] LANG = LANGUAGE.split('-')[0]
NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product') NAME = TITLE = _get_app_config('ooName', 'org.openoffice.Setup/Product')
VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product') VERSION = _get_app_config('ooSetupVersion', 'org.openoffice.Setup/Product')
def mri(obj): def mri(obj):
@ -202,8 +239,34 @@ def run_in_thread(fn):
return run return run
def sleep(sec): def get_config(key=''):
time.sleep(sec) 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 return
@ -278,30 +341,116 @@ def exists_app(name):
return False return False
return True return True
# ~ Delete
def exists(path): def exists(path):
return Path(path).exists() return Path(path).exists()
def exists_path(path):
return Path(path).exists()
def get_type_doc(obj): def get_type_doc(obj):
services = { # ~ services = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument', # ~ 'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument', # ~ 'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument', # ~ 'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument', # ~ 'draw': 'com.sun.star.drawing.DrawingDocument',
'base': 'com.sun.star.sdb.OfficeDatabaseDocument', # ~ 'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'math': 'com.sun.star.formula.FormulaProperties', # ~ 'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE', # ~ 'basic': 'com.sun.star.script.BasicIDE',
} # ~ }
for k, v in services.items(): for k, v in TYPE_DOC.items():
if obj.supportsService(v): if obj.supportsService(v):
return k return k
return '' return ''
def _properties(values): # ~ def _properties(values):
p = [PropertyValue(Name=n, Value=v) for n, v in values.items()] # ~ p = [PropertyValue(Name=n, Value=v) for n, v in values.items()]
return tuple(p) # ~ 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 # ~ Custom classes
@ -371,7 +520,8 @@ class LODocument(object):
return obj return obj
def save(self, path='', **kwargs): def save(self, path='', **kwargs):
opt = _properties(kwargs) # ~ opt = _properties(kwargs)
opt = dict_to_property(kwargs)
if path: if path:
self._obj.storeAsURL(_path_url(path), opt) self._obj.storeAsURL(_path_url(path), opt)
else: else:
@ -1127,9 +1277,30 @@ def set_properties(model, properties):
return 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 = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file')) file_picker.setTitle(_('Select file'))
file_picker.setDisplayDirectory(init_dir)
file_picker.setMultiSelectionMode(multiple) file_picker.setMultiSelectionMode(multiple)
if filters: 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 http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
""" """
path = _path_url(path) path = _path_url(path)
opt = _properties(kwargs) # ~ opt = _properties(kwargs)
opt = dict_to_property(kwargs)
doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt) doc = get_desktop().loadComponentFromURL(path, '_blank', 0, opt)
if doc is None: if doc is None:
return return
@ -1278,9 +1450,9 @@ def is_created(path):
return is_file(path) and bool(get_file_size(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) path, _, name, _ = get_info_path(path)
return '{}/{}.{}'.format(path, name, ext) return '{}/{}.{}'.format(path, name, extension)
def zip_names(path): def zip_names(path):
@ -1477,8 +1649,8 @@ class TextTransferable(unohelper.Base, XTransferable):
return False return False
def set_clipboard(text): def set_clipboard(value):
ts = TextTransferable(text) ts = TextTransferable(value)
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard') sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
sc.setContents(ts, None) sc.setContents(ts, None)
return return
@ -1499,3 +1671,142 @@ def copy(doc=None):
def get_epoch(): def get_epoch():
now = datetime.datetime.now() now = datetime.datetime.now()
return int(time.mktime(now.timetuple())) 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