Add support for context

This commit is contained in:
Mauricio Baeza 2019-09-09 22:13:01 -05:00
parent 9f6868e91b
commit 643970525e
2 changed files with 737 additions and 34 deletions

View File

@ -48,8 +48,6 @@ ICON_EXT = f'{NAME.lower()}.png'
# ~ DEPENDENCIES_MINIMAL = '6.0' # ~ DEPENDENCIES_MINIMAL = '6.0'
DEPENDENCIES_MINIMAL = '' DEPENDENCIES_MINIMAL = ''
LICENSE_ACCEPT_BY = 'user' # or admin
LICENSE_SUPPRESS_ON_UPDATE = True
# ~ Change for you favorite license # ~ Change for you favorite license
LICENSE_EN = f"""This file is part of {NAME}. LICENSE_EN = f"""This file is part of {NAME}.
@ -84,6 +82,12 @@ INFO = {
CONTEXT = { CONTEXT = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument', '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',
} }
@ -101,7 +105,7 @@ MENUS = (
{ {
'title': {'en': 'Option 1', 'es': 'Opción 1'}, 'title': {'en': 'Option 1', 'es': 'Opción 1'},
'argument': 'option1', 'argument': 'option1',
'context': '', 'context': 'calc,writer',
'icon': 'icon', 'icon': 'icon',
'toolbar': True, 'toolbar': True,
}, },
@ -165,11 +169,6 @@ URL_XML_UPDATE = ''
URL_OXT = '' URL_OXT = ''
# ~ If used user profile for develop
# ~ PATH_DEV = '-env:UserInstallation=file:///home/mau/.temp/develop'
# ~ unopkg not support (or I not know how) other user profile
PATH_DEV = ''
# ~ Default program for test: --calc, --writer, --draw # ~ Default program for test: --calc, --writer, --draw
PROGRAM = '--calc' PROGRAM = '--calc'
# ~ Path to file for test # ~ Path to file for test
@ -305,7 +304,7 @@ FILE_DESCRIPTION = f"""<?xml version='1.0' encoding='UTF-8'?>
{NODE_PUBLISHER} {NODE_PUBLISHER}
</publisher> </publisher>
<registration> <registration>
<simple-license accept-by="{LICENSE_ACCEPT_BY}" suppress-on-update="{LICENSE_SUPPRESS_ON_UPDATE}" > <simple-license accept-by="user" suppress-on-update="true" >
{NODE_LICENSE} {NODE_LICENSE}
</simple-license> </simple-license>
</registration>{NODE_DEPENDENCIES_MINIMAL}{NODE_UPDATE} </registration>{NODE_DEPENDENCIES_MINIMAL}{NODE_UPDATE}
@ -334,6 +333,14 @@ opt = 'fuse'
if PARENT == 'OfficeMenuBar': if PARENT == 'OfficeMenuBar':
opt = 'replace' opt = 'replace'
def _get_context(args):
c = []
for v in args.split(','):
c.append(CONTEXT[v])
return ','.join(c)
menus = [] menus = []
toolbar = [] toolbar = []
tmp = ' <value xml:lang="{}">{}</value>' tmp = ' <value xml:lang="{}">{}</value>'
@ -345,7 +352,7 @@ for i, m in enumerate(MENUS):
'opt': opt, 'opt': opt,
'titles': '\n'.join(titles), 'titles': '\n'.join(titles),
'argument': m['argument'], 'argument': m['argument'],
'context': m['context'], 'context': _get_context(m['context']),
'folder': DIRS['images'], 'folder': DIRS['images'],
'icon': m['icon'], 'icon': m['icon'],
} }
@ -549,3 +556,7 @@ DATA = {
'idl': FILE_IDL, 'idl': FILE_IDL,
'addin': FILE_ADDIN, 'addin': FILE_ADDIN,
} }
# ~ LICENSE_ACCEPT_BY = 'user' # or admin
# ~ LICENSE_SUPPRESS_ON_UPDATE = True

View File

@ -20,6 +20,7 @@
import getpass import getpass
import logging import logging
import os
import platform import platform
import sys import sys
import tempfile import tempfile
@ -28,14 +29,20 @@ import time
from functools import wraps from functools import wraps
import uno import uno
import unohelper
from com.sun.star.beans import PropertyValue from com.sun.star.beans import PropertyValue
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.PosSize import POSSIZE, SIZE from com.sun.star.awt.PosSize import POSSIZE, SIZE
from com.sun.star.awt import Size from com.sun.star.awt import Size, Point
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
from com.sun.star.lang import XEventListener
from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMouseListener
FILE_NAME_DEBUG = 'debug.log' FILE_NAME_DEBUG = 'debug.log'
MSG_LANG = { MSG_LANG = {
@ -60,7 +67,7 @@ log = logging.getLogger(__name__)
OBJ_CELL = 'ScCellObj' OBJ_CELL = 'ScCellObj'
OBJ_RANGE = 'ScCellRangeObj' OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj' OBJ_RANGES = 'ScCellRangesObj'
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
CTX = uno.getComponentContext() CTX = uno.getComponentContext()
SM = CTX.getServiceManager() SM = CTX.getServiceManager()
@ -96,7 +103,7 @@ PC = platform.node()
LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/') LANGUAGE = _get_config('ooLocale', 'org.openoffice.Setup/L10N/')
NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product') NAME = TITLE = _get_config('ooName', 'org.openoffice.Setup/Product')
VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product') VERSION = _get_config('ooSetupVersion', 'org.openoffice.Setup/Product')
DESKTOP = create_instance('com.sun.star.frame.Desktop', True) # ~ DESKTOP = create_instance('com.sun.star.frame.Desktop', True)
INFO_DEBUG = '{}\n\n{}\n\n{}'.format( INFO_DEBUG = '{}\n\n{}\n\n{}'.format(
sys.version, platform.platform(), '\n'.join(sys.path)) sys.version, platform.platform(), '\n'.join(sys.path))
@ -176,6 +183,10 @@ def errorbox(message, title=TITLE):
return msgbox(message, title, type_msg='errorbox') return msgbox(message, title, type_msg='errorbox')
def get_desktop():
return create_instance('com.sun.star.frame.Desktop', True)
def get_temp_file(): def get_temp_file():
return tempfile.NamedTemporaryFile() return tempfile.NamedTemporaryFile()
@ -186,35 +197,322 @@ def _path_url(path):
return uno.systemPathToFileUrl(path) return uno.systemPathToFileUrl(path)
def _path_system(path):
if path.startswith('file://'):
return os.path.abspath(uno.fileUrlToSystemPath(path))
return path
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():
if obj.supportsService(v):
return k
return ''
# ~ Custom classes # ~ Custom classes
class LOCellRange(object):
class LODocument(object):
def __init__(self, obj): def __init__(self, obj):
self._obj = obj self._obj = obj
self._init_values() self._init_values()
def _init_values(self):
self._type_doc = get_type_doc(self.obj)
self._cc = self.obj.getCurrentController()
return
@property
def obj(self):
return self._obj
@property
def type(self):
return self._type_doc
@property
def title(self):
return self.obj.getTitle()
@property
def is_saved(self):
return self.obj.hasLocation()
@property
def is_modified(self):
return self.obj.isModified()
@property
def is_read_only(self):
return self.obj.isReadOnly()
@property
def path(self):
return _path_system(self.obj.getURL())
@property
def visible(self):
w = self._cc.getFrame().getContainerWindow()
return w.Visible
@visible.setter
def visible(self, value):
w = self._cc.getFrame().getContainerWindow()
w.setVisible(value)
@property
def zoom(self):
return self._cc.ZoomValue
@zoom.setter
def zoom(self, value):
self._cc.ZoomValue = value
def create_instance(self, name):
obj = self.obj.createInstance(name)
return obj
class LOCalc(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def obj(self):
return self._obj
@property
def active(self):
return LOCalcSheet(self._cc.getActiveSheet(), self)
@property
def selection(self):
sel = self.obj.getCurrentSelection()
if sel.ImplementationName in OBJ_TYPE_RANGES:
sel = LOCellRange(sel, self)
return sel
def get_cell(self, index=None):
"""
index is str 'A1'
index is tuple (row, col)
"""
if index is None:
cell = self.selection.first
else:
cell = LOCellRange(self.active[index].obj, self)
return cell
# ~ def create_instance(self, name):
# ~ obj = self.obj.createInstance(name)
# ~ return obj
class LOCalcSheet(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
self._init_values()
def __getitem__(self, index):
return LOCellRange(self.obj[index], self.doc)
def _init_values(self):
return
@property
def obj(self):
return self._obj
@property
def doc(self):
return self._doc
class LOWriter(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def obj(self):
return self._obj
@property
def string(self):
return self._obj.getText().String
@property
def text(self):
return self._obj.getText()
@property
def cursor(self):
return self.text.createTextCursor()
@property
def selection(self):
sel = self._cc.getSelection()
return LOTextRange(sel[0])
def insert_content(self, cursor, data, replace=False):
self.text.insertTextContent(cursor, data, replace)
return
# ~ tt = doc.createInstance('com.sun.star.text.TextTable')
# ~ tt.initialize(5, 2)
# ~ f = doc.createInstance('com.sun.star.text.TextFrame')
# ~ f.setSize(Size(10000, 500))
def insert_image(self, path, **kwargs):
cursor = kwargs.get('cursor', self.selection.cursor.getEnd())
w = kwargs.get('width', 1000)
h = kwargs.get('Height', 1000)
image = self.create_instance('com.sun.star.text.GraphicObject')
image.GraphicURL = _path_url(path)
image.AnchorType = AS_CHARACTER
image.Width = w
image.Height = h
self.insert_content(cursor, image)
return
class LOTextRange(object):
def __init__(self, obj):
self._obj = obj
@property
def obj(self):
return self._obj
@property
def string(self):
return self.obj.String
@property
def text(self):
return self.obj.getText()
@property
def cursor(self):
return self.text.createTextCursorByRange(self.obj)
class LOBase(LODocument):
def __init__(self, obj):
super().__init__(obj)
class LODrawImpress(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def draw_page(self):
return self._cc.getCurrentPage()
@catch_exception
def insert_image(self, path, **kwargs):
w = kwargs.get('width', 3000)
h = kwargs.get('Height', 1000)
x = kwargs.get('X', 1000)
y = kwargs.get('Y', 1000)
image = self.create_instance('com.sun.star.drawing.GraphicObjectShape')
image.GraphicURL = _path_url(path)
image.Size = Size(w, h)
image.Position = Point(x, y)
self.draw_page.add(image)
return
class LOImpress(LODrawImpress):
def __init__(self, obj):
super().__init__(obj)
class LODraw(LODrawImpress):
def __init__(self, obj):
super().__init__(obj)
class LOMath(LODocument):
def __init__(self, obj):
super().__init__(obj)
class LOBasicIde(LODocument):
def __init__(self, obj):
super().__init__(obj)
class LOCellRange(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
self._init_values()
def __enter__(self): def __enter__(self):
return self return self
def __exit__(self, *args): def __exit__(self, *args):
pass pass
def __getitem__(self, index):
return LOCellRange(self.obj[index], self.doc)
def _init_values(self): def _init_values(self):
self._type_obj = self._obj.ImplementationName self._type_obj = self.obj.ImplementationName
self._type_content = EMPTY self._type_content = EMPTY
if self._type_obj == OBJ_CELL: if self._type_obj == OBJ_CELL:
self._type_content = self._obj.getType() self._type_content = self.obj.getType()
return return
@property @property
def obj(self): def obj(self):
return self._obj return self._obj
@property
def doc(self):
return self._doc
@property
def type(self):
return self._type_obj
@property @property
def type_content(self): def type_content(self):
return self._type_content return self._type_content
@property
def first(self):
if self.type == OBJ_RANGES:
obj = LOCellRange(self.obj[0][0,0], self.doc)
else:
obj = LOCellRange(self.obj[0,0], self.doc)
return obj
@property @property
def value(self): def value(self):
v = None v = None
@ -235,11 +533,20 @@ class LOCellRange(object):
elif isinstance(data, (int, float)): elif isinstance(data, (int, float)):
self.obj.setValue(data) self.obj.setValue(data)
@property
def data(self):
return self.obj.getDataArray()
@data.setter
def data(self, values):
if isinstance(values, list):
values = tuple(values)
self.obj.setDataArray(values)
def offset(self, col=1, row=0): def offset(self, col=1, row=0):
a = self.address a = self.address
col = a.Column + col col = a.Column + col
row = a.Row + row row = a.Row + row
return LOCellRange(self.obj.Spreadsheet[row,col]) return LOCellRange(self.sheet[row,col], self.doc)
@property @property
def sheet(self): def sheet(self):
@ -263,12 +570,17 @@ class LOCellRange(object):
a = self.obj.getRangeAddressesAsString() a = self.obj.getRangeAddressesAsString()
return a return a
def add_image(self, path, **kwargs): @property
def current_region(self):
cursor = self.sheet.createCursorByRange(self.obj[0,0])
cursor.collapseToCurrentRegion()
return LOCellRange(self.sheet[cursor.AbsoluteName], self.doc)
def insert_image(self, path, **kwargs):
s = self.obj.Size s = self.obj.Size
w = kwargs.get('width', s.Width) w = kwargs.get('width', s.Width)
h = kwargs.get('Height', s.Height) h = kwargs.get('Height', s.Height)
doc = get_document() img = self.doc.create_instance('com.sun.star.drawing.GraphicObjectShape')
img = doc.createInstance('com.sun.star.drawing.GraphicObjectShape')
img.GraphicURL = _path_url(path) img.GraphicURL = _path_url(path)
self.draw_page.add(img) self.draw_page.add(img)
img.Anchor = self.obj img.Anchor = self.obj
@ -276,33 +588,413 @@ class LOCellRange(object):
return return
class EventsListenerBase(unohelper.Base, XEventListener):
def __init__(self, controller, window=None):
self._controller = controller
self._window = window
def disposing(self, event):
self._controller = None
if not self._window is None:
self._window.setMenuBar(None)
class EventsButton(EventsListenerBase, XActionListener):
def __init__(self, controller):
super().__init__(controller)
def actionPerformed(self, event):
name = event.Source.Model.Name
event_name = '{}_action'.format(name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsMouse(EventsListenerBase, XMouseListener):
def __init__(self, controller):
super().__init__(controller)
def mousePressed(self, event):
name = event.Source.Model.Name
event_name = '{}_click'.format(name)
if event.ClickCount == 2:
event_name = '{}_double_click'.format(name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def mouseReleased(self, event):
pass
def mouseEntered(self, event):
pass
def mouseExited(self, event):
pass
class UnoBaseObject(object):
def __init__(self, obj):
self._obj = obj
self._model = self.obj.Model
self._rules = {}
@property
def obj(self):
return self._obj
@property
def model(self):
return self._model
@property
def name(self):
return self.model.Name
@property
def parent(self):
return self.obj.getContext()
@property
def x(self):
return self.model.PositionX
@x.setter
def x(self, value):
self.model.PositionX = value
@property
def y(self):
return self.model.PositionY
@y.setter
def y(self, value):
self.model.PositionY = value
@property
def width(self):
return self._model.Width
@width.setter
def width(self, value):
self._model.Width = value
@property
def height(self):
return self._model.Height
@height.setter
def height(self, value):
self._model.Height = value
@property
def tag(self):
return self.model.Tag
@tag.setter
def tag(self, value):
self.model.Tag = value
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
@property
def rules(self):
return self._rules
@rules.setter
def rules(self, value):
self._rules = value
def set_focus(self):
self.obj.setFocus()
return
def center(self, horizontal=True, vertical=False):
p = self.parent.Model
w = p.Width
h = p.Height
if horizontal:
x = w / 2 - self.width / 2
self.x = x
if vertical:
y = h / 2 - self.height / 2
self.y = y
return
def move(self, origin, x=0, y=5):
w = 0
h = 0
if x:
w = origin.width
if y:
h = origin.height
x = origin.x + x + w
y = origin.y + y + h
self.x = x
self.y = y
return
class UnoLabel(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
class UnoButton(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
# ~ self._set_icon()
def _set_icon(self):
icon_name = self.tag.strip()
if icon_name:
path_icon = _file_url('{}/img/{}'.format(CURRENT_PATH, icon_name))
self._model.ImageURL = path_icon
if self.value:
self._model.ImageAlign = 0
return
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
class UnoText(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def value(self):
return self.model.Text
@value.setter
def value(self, value):
self.model.Text = value
def validate(self):
return
class UnoListBox(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._data = []
@property
def value(self):
return self.obj.SelectedItem
@property
def data(self):
return self._data
@data.setter
def data(self, values):
self._data = list(sorted(values))
self.model.StringItemList = self.data
return
class LODialog(object):
def __init__(self, properties):
self._obj = self._create(properties)
self._init_values()
def _init_values(self):
self._model = self._obj.Model
self._init_controls()
self._events = None
# ~ self._response = None
return
def _create(self, properties):
path = properties.pop('Path', '')
if path:
dp = create_instance('com.sun.star.awt.DialogProvider2', True)
return dp.createDialog(_path_url(path))
if 'Library' in properties:
location = properties['Location']
if location == 'user':
location = 'application'
dp = create_instance('com.sun.star.awt.DialogProvider2', True)
path = 'vnd.sun.star.script:{}.{}?location={}'.format(
properties['Library'], properties['Name'], location)
return dp.createDialog(path)
dlg = create_instance('com.sun.star.awt.UnoControlDialog', True)
model = create_instance('com.sun.star.awt.UnoControlDialogModel', True)
toolkit = create_instance('com.sun.star.awt.Toolkit', True)
set_properties(model, properties)
dlg.setModel(model)
dlg.setVisible(False)
dlg.createPeer(toolkit, None)
return dlg
def _init_controls(self):
return
@property
def obj(self):
return self._obj
@property
def model(self):
return self._model
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers
self._connect_listeners()
def _connect_listeners(self):
return
def _add_listeners(self, control):
if self.events is None:
return
listeners = {
'addActionListener': EventsButton,
'addMouseListener': EventsMouse,
}
for key, value in listeners.items():
if hasattr(control.obj, key):
getattr(control.obj, key)(listeners[key](self.events))
return
def open(self):
return self.obj.execute()
def close(self, value=0):
return self.obj.endDialog(value)
def _get_control_model(self, control):
services = {
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'button': 'com.sun.star.awt.UnoControlButtonModel',
'text': 'com.sun.star.awt.UnoControlEditModel',
'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
'image': 'com.sun.star.awt.UnoControlImageControlModel',
'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
'tree': 'com.sun.star.awt.tree.TreeControlModel',
'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
}
return services[control]
def _get_custom_class(self, tipo, obj):
classes = {
'label': UnoLabel,
'button': UnoButton,
'text': UnoText,
'listbox': UnoListBox,
# ~ 'link': UnoLink,
# ~ 'tab': UnoTab,
# ~ 'roadmap': UnoRoadmap,
# ~ 'image': UnoImage,
# ~ 'radio': UnoRadio,
# ~ 'groupbox': UnoGroupBox,
# ~ 'tree': UnoTree,
# ~ 'grid': UnoGrid,
}
return classes[tipo](obj)
def add_control(self, properties):
tipo = properties.pop('Type').lower()
model = self.model.createInstance(self._get_control_model(tipo))
set_properties(model, properties)
name = properties['Name']
self.model.insertByName(name, model)
control = self._get_custom_class(tipo, self.obj.getControl(name))
self._add_listeners(control)
setattr(self, name, control)
return
# ~ Python >= 3.7 # ~ Python >= 3.7
# ~ def __getattr__(name): # ~ def __getattr__(name):
def _get_class_doc(obj):
classes = {
'calc': LOCalc,
'writer': LOWriter,
'base': LOBase,
'impress': LOImpress,
'draw': LODraw,
'math': LOMath,
'basic': LOBasicIde,
}
type_doc = get_type_doc(obj)
return classes[type_doc](obj)
def get_document(): def get_document():
doc = None doc = None
desktop = get_desktop()
try: try:
doc = DESKTOP.getCurrentComponent() doc = _get_class_doc(desktop.getCurrentComponent())
except Exception as e: except Exception as e:
log.error(e) log.error(e)
return doc return doc
def get_selection(): def get_selection():
obj = None return get_document().selection
try:
obj = get_document().getCurrentSelection()
except Exception as e:
log.error(e)
return obj
def get_cell(*args): def get_cell(*args):
sel = get_selection() if args:
if sel.ImplementationName == OBJ_RANGE: index = args
sel = sel[0,0] if len(index) == 1:
elif sel.ImplementationName == OBJ_RANGES: index = args[0]
sel = sel[0][0,0] cell = get_document().get_cell(index)
return LOCellRange(sel) else:
cell = get_selection().first
return cell
def active_cell():
return get_cell()
def create_dialog(properties):
return LODialog(properties)
def set_properties(model, properties):
if 'X' in properties:
properties['PositionX'] = properties.pop('X')
if 'Y' in properties:
properties['PositionY'] = properties.pop('Y')
keys = tuple(properties.keys())
values = tuple(properties.values())
model.setPropertyValues(keys, values)
return