Embed py in documents

This commit is contained in:
Mauricio Baeza 2020-09-11 21:54:38 -05:00
parent 14630c1bd7
commit 6e101413b9
3 changed files with 655 additions and 66 deletions

View File

@ -950,22 +950,10 @@ class LOImage(object):
def __init__(self, obj): def __init__(self, obj):
self._obj = obj self._obj = obj
@property
def obj(self):
return self._obj
@property @property
def address(self): def address(self):
return self.obj.Anchor.AbsoluteName return self.obj.Anchor.AbsoluteName
@property
def name(self):
return self.obj.Name
@property
def mimetype(self):
return self.obj.Bitmap.MimeType
@property @property
def url(self): def url(self):
return _path_system(self.obj.URL) return _path_system(self.obj.URL)

View File

@ -4,6 +4,8 @@
# ~ This file is part of ZAZ. # ~ This file is part of ZAZ.
# ~ https://gitlab.com/mauriciobaeza/zaz
# ~ ZAZ is free software: you can redistribute it and/or modify # ~ ZAZ is free software: you can redistribute it and/or modify
# ~ it under the terms of the GNU General Public License as published by # ~ it under the terms of the GNU General Public License as published by
# ~ the Free Software Foundation, either version 3 of the License, or # ~ the Free Software Foundation, either version 3 of the License, or
@ -20,9 +22,11 @@
import datetime import datetime
import getpass import getpass
import gettext
import logging import logging
import os import os
import platform import platform
import shlex
import socket import socket
import subprocess import subprocess
import sys import sys
@ -39,6 +43,9 @@ import unohelper
from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
from com.sun.star.awt.MessageBoxResults import YES from com.sun.star.awt.MessageBoxResults import YES
from com.sun.star.awt import Rectangle from com.sun.star.awt import Rectangle
from com.sun.star.awt import Key, KeyModifier, KeyEvent
from com.sun.star.container import NoSuchElementException
from com.sun.star.beans import PropertyValue, NamedValue from com.sun.star.beans import PropertyValue, NamedValue
from com.sun.star.sheet import TableFilterField from com.sun.star.sheet import TableFilterField
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
@ -48,6 +55,7 @@ from com.sun.star.awt import XActionListener
from com.sun.star.lang import XEventListener from com.sun.star.lang import XEventListener
from com.sun.star.awt import XMouseListener from com.sun.star.awt import XMouseListener
from com.sun.star.awt import XMouseMotionListener from com.sun.star.awt import XMouseMotionListener
from com.sun.star.awt import XFocusListener
# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt_1_1FontUnderline.html # ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt_1_1FontUnderline.html
from com.sun.star.awt import FontUnderline from com.sun.star.awt import FontUnderline
@ -57,10 +65,7 @@ try:
from peewee import Database, DateTimeField, DateField, TimeField, \ from peewee import Database, DateTimeField, DateField, TimeField, \
__exception_wrapper__ __exception_wrapper__
except ImportError as e: except ImportError as e:
Database = object Database = DateField = TimeField = DateTimeField = object
DateField = object
TimeField = object
DateTimeField = object
print('Install peewee') print('Install peewee')
@ -84,6 +89,7 @@ IMPRESS = 'impress'
BASE = 'base' BASE = 'base'
MATH = 'math' MATH = 'math'
BASIC = 'basic' BASIC = 'basic'
MAIN = 'main'
TYPE_DOC = { TYPE_DOC = {
CALC: 'com.sun.star.sheet.SpreadsheetDocument', CALC: 'com.sun.star.sheet.SpreadsheetDocument',
WRITER: 'com.sun.star.text.TextDocument', WRITER: 'com.sun.star.text.TextDocument',
@ -92,7 +98,7 @@ TYPE_DOC = {
BASE: 'com.sun.star.sdb.DocumentDataSource', BASE: 'com.sun.star.sdb.DocumentDataSource',
MATH: 'com.sun.star.formula.FormulaProperties', MATH: 'com.sun.star.formula.FormulaProperties',
BASIC: 'com.sun.star.script.BasicIDE', BASIC: 'com.sun.star.script.BasicIDE',
'main': 'com.sun.star.frame.StartModule', MAIN: 'com.sun.star.frame.StartModule',
} }
OBJ_CELL = 'ScCellObj' OBJ_CELL = 'ScCellObj'
@ -107,6 +113,11 @@ class FilterOperator(IntEnum):
EQUAL = 2 EQUAL = 2
NOT_EQUAL = 3 NOT_EQUAL = 3
# ~ https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1awt_1_1UnoControlEditModel.html#a54d3ff280d892218d71e667f81ce99d4
class Border(IntEnum):
NO_BORDER = 0
BORDER = 1
SIMPLE = 2
OS = platform.system() OS = platform.system()
IS_WIN = OS == 'Windows' IS_WIN = OS == 'Windows'
@ -116,9 +127,53 @@ PC = platform.node()
DESKTOP = os.environ.get('DESKTOP_SESSION', '') DESKTOP = os.environ.get('DESKTOP_SESSION', '')
INFO_DEBUG = f"{sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path) INFO_DEBUG = f"{sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path)
_MACROS = {}
SECONDS_DAY = 60 * 60 * 24 SECONDS_DAY = 60 * 60 * 24
_MACROS = {} DIR = {
'images': 'images',
'locales': 'locales',
}
MIMETYPE = 'png'
MODIFIERS = {
'shift': KeyModifier.SHIFT,
'ctrl': KeyModifier.MOD1,
'alt': KeyModifier.MOD2,
'ctrlmac': KeyModifier.MOD3,
}
# ~ Menus
NODE_MENUBAR = 'private:resource/menubar/menubar'
MENUS = {
'file': '.uno:PickList',
'tools': '.uno:ToolsMenu',
'help': '.uno:HelpMenu',
'windows': '.uno:WindowList',
'edit': '.uno:EditMenu',
'view': '.uno:ViewMenu',
'insert': '.uno:InsertMenu',
'format': '.uno:FormatMenu',
'styles': '.uno:FormatStylesMenu',
'sheet': '.uno:SheetMenu',
'data': '.uno:DataMenu',
'table': '.uno:TableMenu',
'form': '.uno:FormatFormMenu',
'page': '.uno:PageMenu',
'shape': '.uno:ShapeMenu',
'slide': '.uno:SlideMenu',
'show': '.uno:SlideShowMenu',
}
MESSAGES = {
'es': {
'OK': 'Aceptar',
'Cancel': 'Cancelar',
'Select file': 'Seleccionar archivo',
'Incorrect user or password': 'Nombre de usuario o contraseña inválidos',
'Allow less secure apps in GMail': 'Activa: Permitir aplicaciones menos segura en GMail',
}
}
CTX = uno.getComponentContext() CTX = uno.getComponentContext()
SM = CTX.getServiceManager() SM = CTX.getServiceManager()
@ -202,6 +257,17 @@ def inspect(obj: Any) -> None:
return return
def mri(obj):
m = create_instance('mytools.Mri')
if m is None:
msg = 'Extension MRI not found'
error(msg)
return
m.inspect(obj)
return
def now(only_time=False): def now(only_time=False):
now = datetime.datetime.now() now = datetime.datetime.now()
if only_time: if only_time:
@ -213,6 +279,16 @@ def today():
return datetime.date.today() return datetime.date.today()
def _(msg):
if LANG == 'en':
return msg
if not LANG in MESSAGES:
return msg
return MESSAGES[LANG][msg]
def msgbox(message, title=TITLE, buttons=MSG_BUTTONS.BUTTONS_OK, type_msg='infobox'): def msgbox(message, title=TITLE, buttons=MSG_BUTTONS.BUTTONS_OK, type_msg='infobox'):
""" Create message box """ Create message box
type_msg: infobox, warningbox, errorbox, querybox, messbox type_msg: infobox, warningbox, errorbox, querybox, messbox
@ -258,6 +334,19 @@ def _get_class_doc(obj: Any) -> Any:
return classes[type_doc](obj) return classes[type_doc](obj)
def _get_class_uno(obj: Any) -> Any:
classes = dict(
SwXTextGraphicObject = LOImage,
SvxShapeText = LOImage,
)
name = obj.ImplementationName
print(f'ImplementationName = {name}')
instance = obj
if name in classes:
instance = classes[name](obj)
return instance
def dict_to_property(values: dict, uno_any: bool=False): def dict_to_property(values: dict, uno_any: bool=False):
ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()]) ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
if uno_any: if uno_any:
@ -391,6 +480,17 @@ def call_macro(args, in_thread=False):
return result return result
def run(command, capture=False):
result = subprocess.run(shlex.split(command),
capture_output=capture, text=True).stdout
return result
def sleep(seconds):
time.sleep(seconds)
return
class TimerThread(threading.Thread): class TimerThread(threading.Thread):
def __init__(self, event, seconds, macro): def __init__(self, event, seconds, macro):
@ -422,6 +522,28 @@ def stop_timer(name):
return return
def install_locales(path, domain='base', dir_locales=DIR['locales']):
path_locales = _P.join(_P(path).path, dir_locales)
try:
lang = gettext.translation(domain, path_locales, languages=[LANG])
lang.install()
_ = lang.gettext
except Exception as e:
from gettext import gettext as _
error(e)
return _
def _export_image(obj, args):
name = 'com.sun.star.drawing.GraphicExportFilter'
exporter = create_instance(name)
path = _P.to_system(args['URL'])
args = dict_to_property(args)
exporter.setSourceDocument(obj)
exporter.filter(args)
return _P.exists(path)
class LOBaseObject(object): class LOBaseObject(object):
def __init__(self, obj): def __init__(self, obj):
@ -445,6 +567,54 @@ class LOBaseObject(object):
return self._obj return self._obj
class LOImage(object):
TYPE = {
'png': 'image/png',
'jpg': 'image/jpeg',
}
def __init__(self, obj):
self._obj = obj
@property
def obj(self):
return self._obj
@property
def name(self):
return self.obj.Name or 'img'
@property
def mimetype(self):
return self.obj.Bitmap.MimeType
def save(self, path, mimetype=MIMETYPE):
p = _P(path)
if _P.is_dir(path):
name = self.name
else:
path = p.path
name = p.name
path = _P.join(path, f'{name}.{mimetype.lower()}')
args = dict(
URL = _P.to_url(path),
MimeType = self.TYPE[mimetype],
)
if not _export_image(self.obj, args):
path = ''
# ~ size = len(self.obj.Bitmap.DIB)
# ~ data = self.obj.GraphicStream.readBytes((), size)
# ~ data = data[-1].value
# ~ data = self.obj.Bitmap.DIB.value
# ~ data = self.obj.Graphic.DIB.value
# ~ _P.save_bin(path, data)
return path
class LODocument(object): class LODocument(object):
def __init__(self, obj): def __init__(self, obj):
@ -519,7 +689,7 @@ class LODocument(object):
@property @property
def selection(self): def selection(self):
sel = self.obj.CurrentSelection sel = self.obj.CurrentSelection
return sel return _get_class_uno(sel)
def create_instance(self, name): def create_instance(self, name):
obj = self.obj.createInstance(name) obj = self.obj.createInstance(name)
@ -534,10 +704,6 @@ class LODocument(object):
call_dispatch(self.frame, '.uno:Copy') call_dispatch(self.frame, '.uno:Copy')
return return
# ~ def paste(self):
# ~ call_dispatch(self.frame, '.uno:Paste')
# ~ return
def paste(self): def paste(self):
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard') sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
transferable = sc.getContents() transferable = sc.getContents()
@ -909,8 +1075,10 @@ class LOCalcRange(object):
@value.setter @value.setter
def value(self, data): def value(self, data):
if isinstance(data, str): if isinstance(data, str):
if data[0] == '=': print(isinstance(data, str), data[0])
if data[0] in '=+-':
self.obj.setFormula(data) self.obj.setFormula(data)
print('Set Formula')
else: else:
self.obj.setString(data) self.obj.setString(data)
elif isinstance(data, (int, float, bool)): elif isinstance(data, (int, float, bool)):
@ -1022,6 +1190,15 @@ class LODrawImpress(LODocument):
def __init__(self, obj): def __init__(self, obj):
super().__init__(obj) super().__init__(obj)
@property
def selection(self):
sel = self.obj.CurrentSelection[0]
return _get_class_uno(sel)
def paste(self):
call_dispatch(self.frame, '.uno:Paste')
return self.selection
class LODraw(LODrawImpress): class LODraw(LODrawImpress):
@ -1195,30 +1372,21 @@ class LOBase(object):
def __init__(self, obj, args={}): def __init__(self, obj, args={}):
self._obj = obj self._obj = obj
self._type = BASE self._type = BASE
self._path = args.get('path', '')
self._dbc = create_instance('com.sun.star.sdb.DatabaseContext') self._dbc = create_instance('com.sun.star.sdb.DatabaseContext')
self._rows_affected = 0 self._rows_affected = 0
if self._path: path = args.get('path', '')
self._name = Path(self._path).name self._path = _P(path)
path_url = _path_url(self._path) self._name = self._path.name
if _P.exists(path):
if not self.is_registered:
self.register()
db = self._dbc.getByName(self.name)
else:
db = self._dbc.createInstance() db = self._dbc.createInstance()
db.URL = 'sdbc:embedded:firebird' db.URL = 'sdbc:embedded:firebird'
db.DatabaseDocument.storeAsURL(path_url, ()) db.DatabaseDocument.storeAsURL(self._path.url, ())
self.register() self.register()
self._obj = db self._obj = db
else:
self._name = self._obj
if Path(self._name).exists():
self._path = self._name
self._name = Path(self._path).name
if self.is_registered:
db = self._dbc.getByName(self.name)
self._path = _path_system(self._dbc.getDatabaseLocation(self.name))
self._obj = db
else:
path_url = _path_url(self._path)
self._dbc.registerDatabaseLocation(self.name, path_url)
db = self._dbc.getByName(self.name)
self._con = db.getConnection('', '') self._con = db.getConnection('', '')
def __contains__(self, item): def __contains__(self, item):
@ -1234,7 +1402,7 @@ class LOBase(object):
@property @property
def path(self): def path(self):
return self._path return str(self._path)
@property @property
def is_registered(self): def is_registered(self):
@ -1251,8 +1419,7 @@ class LOBase(object):
def register(self): def register(self):
if not self.is_registered: if not self.is_registered:
path_url = _path_url(self._path) self._dbc.registerDatabaseLocation(self.name, self._path.url)
self._dbc.registerDatabaseLocation(self.name, path_url)
return return
def revoke(self, name): def revoke(self, name):
@ -1359,11 +1526,15 @@ class LODocs(object):
LODocs._desktop = self._desktop LODocs._desktop = self._desktop
def __getitem__(self, index): def __getitem__(self, index):
doc = None
for i, doc in enumerate(self._desktop.Components): for i, doc in enumerate(self._desktop.Components):
if isinstance(index, int) and i == index: if isinstance(index, int) and i == index:
return _get_class_doc(doc) doc = _get_class_doc(doc)
break
elif isinstance(index, str) and doc.Title == index: elif isinstance(index, str) and doc.Title == index:
return _get_class_doc(doc) doc = _get_class_doc(doc)
break
return doc
def __contains__(self, item): def __contains__(self, item):
doc = self[item] doc = self[item]
@ -1422,15 +1593,15 @@ class LODocs(object):
return _get_class_doc(doc) return _get_class_doc(doc)
def connect(self, path): def connect(self, path):
return LOBase(path) return LOBase(None, {'path': path})
def _add_listeners(events, control, name=''): def _add_listeners(events, control, name=''):
listeners = { listeners = {
'addActionListener': EventsButton, 'addActionListener': EventsButton,
'addMouseListener': EventsMouse, 'addMouseListener': EventsMouse,
'addFocusListener': EventsFocus,
# ~ 'addItemListener': EventsItem, # ~ 'addItemListener': EventsItem,
# ~ 'addFocusListener': EventsFocus,
# ~ 'addKeyListener': EventsKey, # ~ 'addKeyListener': EventsKey,
# ~ 'addTabListener': EventsTab, # ~ 'addTabListener': EventsTab,
} }
@ -1550,6 +1721,30 @@ class EventsButton(EventsListenerBase, XActionListener):
return return
class EventsFocus(EventsListenerBase, XFocusListener):
CONTROLS = (
'stardiv.Toolkit.UnoControlEditModel',
)
def __init__(self, controller, name):
super().__init__(controller, name)
def focusGained(self, event):
service = event.Source.Model.ImplementationName
# ~ print('Focus enter', service)
if service in self.CONTROLS:
obj = event.Source.Model
obj.BackgroundColor = COLOR_ON_FOCUS
return
def focusLost(self, event):
service = event.Source.Model.ImplementationName
if service in self.CONTROLS:
obj = event.Source.Model
obj.BackgroundColor = -1
return
# ~ BorderColor = ? # ~ BorderColor = ?
# ~ FontStyleName = ? # ~ FontStyleName = ?
# ~ HelpURL = ? # ~ HelpURL = ?
@ -1879,12 +2074,31 @@ class UnoCheck(UnoBaseObject):
self.model.TriState = value self.model.TriState = value
# ~ https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1awt_1_1UnoControlEditModel.html
class UnoText(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'text'
@property
def value(self):
return self.model.Text
@value.setter
def value(self, value):
self.model.Text = value
UNO_CLASSES = { UNO_CLASSES = {
'label': UnoLabel, 'label': UnoLabel,
'link': UnoLabelLink, 'link': UnoLabelLink,
'button': UnoButton, 'button': UnoButton,
'radio': UnoRadio, 'radio': UnoRadio,
'check': UnoCheck, 'check': UnoCheck,
'text': UnoText,
} }
@ -1895,11 +2109,11 @@ class LODialog(object):
'button': 'com.sun.star.awt.UnoControlButtonModel', 'button': 'com.sun.star.awt.UnoControlButtonModel',
'radio': 'com.sun.star.awt.UnoControlRadioButtonModel', 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
'check': 'com.sun.star.awt.UnoControlCheckBoxModel', 'check': 'com.sun.star.awt.UnoControlCheckBoxModel',
'text': 'com.sun.star.awt.UnoControlEditModel',
# ~ 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
# ~ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', # ~ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
# ~ 'text': 'com.sun.star.awt.UnoControlEditModel',
# ~ 'listbox': 'com.sun.star.awt.UnoControlListBoxModel', # ~ 'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
# ~ 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel', # ~ 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
# ~ 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
# ~ 'tree': 'com.sun.star.awt.tree.TreeControlModel', # ~ 'tree': 'com.sun.star.awt.tree.TreeControlModel',
# ~ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', # ~ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
# ~ 'image': 'com.sun.star.awt.UnoControlImageControlModel', # ~ 'image': 'com.sun.star.awt.UnoControlImageControlModel',
@ -1912,6 +2126,7 @@ class LODialog(object):
self._events = None self._events = None
self._modal = True self._modal = True
self._controls = {} self._controls = {}
self._color_on_focus = COLOR_ON_FOCUS
def _create(self, args): def _create(self, args):
service = 'com.sun.star.awt.DialogProvider' service = 'com.sun.star.awt.DialogProvider'
@ -1973,6 +2188,13 @@ class LODialog(object):
self._events = controllers(self) self._events = controllers(self)
self._connect_listeners() self._connect_listeners()
@property
def color_on_focus(self):
return self._color_on_focus
@color_on_focus.setter
def color_on_focus(self, value):
self._color_on_focus = get_color(value)
def _connect_listeners(self): def _connect_listeners(self):
for control in self.obj.Controls: for control in self.obj.Controls:
_add_listeners(self.events, control, control.Model.Name) _add_listeners(self.events, control, control.Model.Name)
@ -2062,30 +2284,336 @@ class LOCells(object):
return LODocs().active.active[index] return LODocs().active.active[index]
class LOShortCut(object):
# ~ getKeyEventsByCommand
def __init__(self, app):
self._app = app
self._scm = None
self._init_values()
def _init_values(self):
name = 'com.sun.star.ui.GlobalAcceleratorConfiguration'
instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
service = TYPE_DOC[self._app]
manager = create_instance(instance, True)
uicm = manager.getUIConfigurationManager(service)
self._scm = uicm.ShortCutManager
return
def __contains__(self, item):
cmd = self._get_command(item)
return bool(cmd)
def _get_key_event(self, command):
events = self._scm.AllKeyEvents
for event in events:
cmd = self._scm.getCommandByKeyEvent(event)
if cmd == command:
break
return event
def _to_key_event(self, shortcut):
key_event = KeyEvent()
keys = shortcut.split('+')
for v in keys[:-1]:
key_event.Modifiers += MODIFIERS[v.lower()]
key_event.KeyCode = getattr(Key, keys[-1].upper())
return key_event
def _get_command(self, shortcut):
command = ''
key_event = self._to_key_event(shortcut)
try:
command = self._scm.getCommandByKeyEvent(key_event)
except NoSuchElementException:
debug(f'No exists: {shortcut}')
return command
def add(self, shortcut, command):
if isinstance(command, dict):
command = _get_url_script(command)
key_event = self._to_key_event(shortcut)
self._scm.setKeyEvent(key_event, command)
self._scm.store()
return
def reset(self):
self._scm.reset()
self._scm.store()
return
def remove(self, shortcut):
key_event = self._to_key_event(shortcut)
try:
self._scm.removeKeyEvent(key_event)
self._scm.store()
except NoSuchElementException:
debug(f'No exists: {shortcut}')
return
def remove_by_command(self, command):
if isinstance(command, dict):
command = _get_url_script(command)
try:
self._scm.removeCommandFromAllKeyEvents(command)
self._scm.store()
except NoSuchElementException:
debug(f'No exists: {command}')
return
class LOShortCuts(object):
def __getitem__(self, index):
return LOShortCut(index)
class LOMenu(object):
def __init__(self, app):
self._app = app
self._ui = None
self._pymenus = None
self._menu = None
self._menus = self._get_menus()
def __getitem__(self, index):
if isinstance(index, int):
self._menu = self._menus[index]
else:
for menu in self._menus:
cmd = menu.get('CommandURL', '')
if MENUS[index.lower()] == cmd:
self._menu = menu
break
line = self._menu.get('CommandURL', '')
line += self._get_submenus(self._menu['ItemDescriptorContainer'])
return line
def _get_menus(self):
instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
service = TYPE_DOC[self._app]
manager = create_instance(instance, True)
self._ui = manager.getUIConfigurationManager(service)
self._pymenus = self._ui.getSettings(NODE_MENUBAR, True)
data = []
for menu in self._pymenus:
data.append(data_to_dict(menu))
return data
def _get_info(self, menu):
line = menu.get('CommandURL', '')
line += self._get_submenus(menu['ItemDescriptorContainer'])
return line
def _get_submenus(self, menu, level=1):
line = ''
for i, v in enumerate(menu):
data = data_to_dict(v)
cmd = data.get('CommandURL', '----------')
line += f'\n{" " * level}├─ ({i}) {cmd}'
submenu = data.get('ItemDescriptorContainer', None)
if not submenu is None:
line += self._get_submenus(submenu, level + 1)
return line
def __str__(self):
info = '\n'.join([self._get_info(m) for m in self._menus])
return info
def _get_index_menu(self, menu, command):
index = -1
for i, v in enumerate(menu):
data = data_to_dict(v)
cmd = data.get('CommandURL', '')
if cmd == command:
index = i
break
return index
def insert(self, name, args):
idc = None
replace = False
command = args['CommandURL']
label = args['Label']
self[name]
menu = self._menu['ItemDescriptorContainer']
submenu = args.get('Submenu', False)
if submenu:
idc = self._ui.createSettings()
index = self._get_index_menu(menu, command)
if index == -1:
if 'Index' in args:
index = args['Index']
else:
index = self._get_index_menu(menu, args['After']) + 1
else:
replace = True
data = dict (
CommandURL = command,
Label = label,
Style = 0,
Type = 0,
ItemDescriptorContainer = idc,
)
self._save(menu, data, index, replace)
self._insert_submenu(idc, submenu)
return
def _get_command(self, args):
shortcut = args.get('ShortCut', '')
cmd = args['CommandURL']
if isinstance(cmd, dict):
cmd = _get_url_script(cmd)
if shortcut:
LOShortCut(self._app).add(shortcut, cmd)
return cmd
def _insert_submenu(self, parent, menus):
for i, v in enumerate(menus):
submenu = v.pop('Submenu', False)
if submenu:
idc = self._ui.createSettings()
v['ItemDescriptorContainer'] = idc
v['Type'] = 0
if v['Label'] == '-':
v['Type'] = 1
else:
v['CommandURL'] = self._get_command(v)
self._save(parent, v, i)
if submenu:
self._insert_submenu(idc, submenu)
return
def remove(self, name, command):
self[name]
menu = self._menu['ItemDescriptorContainer']
index = self._get_index_menu(menu, command)
if index > -1:
uno.invoke(menu, 'removeByIndex', (index,))
self._ui.replaceSettings(NODE_MENUBAR, self._pymenus)
self._ui.store()
return
def _save(self, menu, properties, index, replace=False):
properties = dict_to_property(properties, True)
if replace:
uno.invoke(menu, 'replaceByIndex', (index, properties))
else:
uno.invoke(menu, 'insertByIndex', (index, properties))
self._ui.replaceSettings(NODE_MENUBAR, self._pymenus)
self._ui.store()
return
class LOMenus(object):
def __getitem__(self, index):
return LOMenu(index)
class classproperty:
def __init__(self, method=None):
self.fget = method
def __get__(self, instance, cls=None):
return self.fget(cls)
def getter(self, method):
self.fget = method
return self
class Paths(object): class Paths(object):
def __init__(self, path=''):
self._path = Path(path)
@property
def path(self):
return str(self._path.parent)
@property
def file_name(self):
return self._path.name
@property
def name(self):
return self._path.stem
@property
def ext(self):
return self._path.suffix
@property
def url(self):
return self._path.as_uri()
@classproperty
def home(self):
return str(Path.home())
@classmethod @classmethod
def exists(cls, path): def exists(cls, path):
return Path(path).exists() return Path(path).exists()
@classmethod
def is_dir(cls, path):
return Path(path).is_dir()
@classmethod
def join(cls, *paths):
return str(Path(paths[0]).joinpath(paths[1]))
@classmethod
def save(cls, path, data, encoding='utf-8'):
result = bool(Path(path).write_text(data, encoding=encoding))
return result
@classmethod
def save_bin(cls, path, data):
result = bool(Path(path).write_bytes(data))
return result
@classmethod
def to_url(cls, path):
if not path.startswith('file://'):
path = Path(path).as_uri()
return path
@classmethod
def to_system(cls, path):
if path.startswith('file://'):
path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
return path
_P = Paths
def __getattr__(name): def __getattr__(name):
if name == 'active': if name == 'active':
return docs.active return LODocs().active
if name == 'active_sheet': if name == 'active_sheet':
return docs.active.active return LODocs().active.active
if name == 'selection': if name == 'selection':
return docs.active.selection return LODocs().active.selection
if name in ('rectangle', 'pos_size'): if name in ('rectangle', 'pos_size'):
return Rectangle() return Rectangle()
if name == 'paths': if name == 'paths':
return Paths() return Paths
if name == 'docs': if name == 'docs':
return LODocs() return LODocs()
if name == 'sheets': if name == 'sheets':
return LOSheets() return LOSheets()
if name == 'cells': if name == 'cells':
return LOCells() return LOCells()
if name == 'menus':
return LOMenus()
if name == 'shortcuts':
return LOShortCuts()
raise AttributeError(f"module '{__name__}' has no attribute '{name}'") raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
@ -2093,6 +2621,79 @@ def create_dialog(args):
return LODialog(args) return LODialog(args)
def inputbox(message, default='', title=TITLE, echochar=''):
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
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,
}
if echochar:
args['EchoChar'] = ord(echochar[0])
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 get_fonts(): def get_fonts():
toolkit = create_instance('com.sun.star.awt.Toolkit') toolkit = create_instance('com.sun.star.awt.Toolkit')
device = toolkit.createScreenCompatibleDevice(0, 0) device = toolkit.createScreenCompatibleDevice(0, 0)

View File

@ -19,6 +19,7 @@
import argparse import argparse
import os import os
import py_compile
import re import re
import sys import sys
import zipfile import zipfile
@ -55,6 +56,7 @@ class LiboXML(object):
} }
TYPES = { TYPES = {
'py': 'application/vnd.sun.star.uno-component;type=Python', 'py': 'application/vnd.sun.star.uno-component;type=Python',
'pyc': 'application/binary',
'zip': 'application/binary', 'zip': 'application/binary',
'xcu': 'application/vnd.sun.star.configuration-data', 'xcu': 'application/vnd.sun.star.configuration-data',
'rdb': 'application/vnd.sun.star.uno-typelibrary;type=RDB', 'rdb': 'application/vnd.sun.star.uno-typelibrary;type=RDB',
@ -121,8 +123,8 @@ class LiboXML(object):
def parse_manifest(self, data): def parse_manifest(self, data):
ET.register_namespace('manifest', self.NS_MANIFEST['manifest']) ET.register_namespace('manifest', self.NS_MANIFEST['manifest'])
self._manifest = ET.fromstring(data) self._manifest = ET.fromstring(data)
data = {'xmlns:loext': self.NS_MANIFEST['xmlns:loext']} attr = {'xmlns:loext': self.NS_MANIFEST['xmlns:loext']}
self._manifest.attrib.update(**data) self._manifest.attrib.update(**attr)
self._clean('manifest', self._manifest) self._clean('manifest', self._manifest)
return return
@ -615,8 +617,8 @@ def _get_info_path(path):
def _zip_embed(source, files): def _zip_embed(source, files):
PATH = 'Scripts/python/' PATH = 'Scripts/python/'
EASYMACRO = 'easymacro.py' EASYMACRO = 'easymacro2.py'
FILE_ZIP = 'easymacro.zip' FILE_PYC = 'easymacro.pyc'
p, f, name, e = _get_info_path(source) p, f, name, e = _get_info_path(source)
now = datetime.now().strftime('_%Y%m%d_%H%M%S') now = datetime.now().strftime('_%Y%m%d_%H%M%S')
@ -624,12 +626,10 @@ def _zip_embed(source, files):
copyfile(source, path_source) copyfile(source, path_source)
target = source target = source
with zipfile.ZipFile(FILE_ZIP, mode='w', compression=zipfile.ZIP_DEFLATED) as zf: py_compile.compile(EASYMACRO, FILE_PYC)
zf.write(EASYMACRO)
xml = LiboXML() xml = LiboXML()
path_easymacro = PATH + FILE_ZIP path_easymacro = PATH + FILE_PYC
names = [f[1] for f in files] + [path_easymacro] names = [f[1] for f in files] + [path_easymacro]
nodes = [] nodes = []
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as zt: with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as zt:
@ -648,14 +648,14 @@ def _zip_embed(source, files):
data.append(name) data.append(name)
zt.write(path, name) zt.write(path, name)
zt.write(FILE_ZIP, path_easymacro) zt.write(FILE_PYC, path_easymacro)
data.append(path_easymacro) data.append(path_easymacro)
xml.parse_manifest(xml_manifest) xml.parse_manifest(xml_manifest)
xml_manifest = xml.add_data_manifest(data) xml_manifest = xml.add_data_manifest(data)
zt.writestr(path_manifest, xml_manifest) zt.writestr(path_manifest, xml_manifest)
os.unlink(FILE_ZIP) os.unlink(FILE_PYC)
return return