easymacro/source/easymacro/easydialog.py

742 lines
19 KiB
Python

#!/usr/bin/env python3
from typing import Any
from com.sun.star.view.SelectionType import SINGLE, MULTI, RANGE
from .easyevents import *
from .easydocs import LODocuments
from .easymain import log, TITLE, Color, BaseObject, create_instance, set_properties
from .easytools import _, LOInspect, Paths, Services
__all__ = [
'LODialog',
'inputbox',
]
COLOR_ON_FOCUS = Color()('LightYellow')
SEPARATION = 5
MODELS = {
'button': 'com.sun.star.awt.UnoControlButtonModel',
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
'checkbox': 'com.sun.star.awt.UnoControlCheckBoxModel',
'text': 'com.sun.star.awt.UnoControlEditModel',
'image': 'com.sun.star.awt.UnoControlImageControlModel',
'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
'tree': 'com.sun.star.awt.tree.TreeControlModel',
'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
'pages': 'com.sun.star.awt.UnoMultiPageModel',
'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
'combobox': 'com.sun.star.awt.UnoControlComboBoxModel',
'spinbutton': 'com.sun.star.awt.UnoControlSpinButtonModel',
'numeric': 'com.sun.star.awt.UnoControlNumericFieldModel',
}
TYPE_CONTROL = {
'stardiv.Toolkit.UnoFixedTextControl': 'label',
'stardiv.Toolkit.UnoEditControl': 'text',
'stardiv.Toolkit.UnoButtonControl': 'button',
}
IMPLEMENTATIONS = {
'grid': 'stardiv.Toolkit.GridControl',
'link': 'stardiv.Toolkit.UnoFixedHyperlinkControl',
'roadmap': 'stardiv.Toolkit.UnoRoadmapControl',
'pages': 'stardiv.Toolkit.UnoMultiPageControl',
}
def add_listeners(events, control):
name = control.Model.Name
listeners = {
'addActionListener': EventsButton,
# ~ 'addMouseListener': EventsMouse,
# ~ 'addFocusListener': EventsFocus,
# ~ 'addItemListener': EventsItem,
# ~ 'addKeyListener': EventsKey,
# ~ 'addTabListener': EventsTab,
# ~ 'addSpinListener': EventsSpin,
}
if hasattr(control, 'obj'):
control = control.obj
# ~ log.debug(control.ImplementationName)
is_grid = control.ImplementationName == IMPLEMENTATIONS['grid']
is_link = control.ImplementationName == IMPLEMENTATIONS['link']
is_roadmap = control.ImplementationName == IMPLEMENTATIONS['roadmap']
is_pages = control.ImplementationName == IMPLEMENTATIONS['pages']
for key, value in listeners.items():
if hasattr(control, key):
if is_grid and key == 'addMouseListener':
control.addMouseListener(EventsMouseGrid(events, name))
continue
if is_link and key == 'addMouseListener':
control.addMouseListener(EventsMouseLink(events, name))
continue
if is_roadmap and key == 'addItemListener':
control.addItemListener(EventsItemRoadmap(events, name))
continue
getattr(control, key)(listeners[key](events, name))
if is_grid:
controllers = EventsGrid(events, name)
control.addSelectionListener(controllers)
control.Model.GridDataModel.addGridDataListener(controllers)
return
class UnoBaseObject(object):
def __init__(self, obj: Any):
self._obj = obj
self._model = obj.Model
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
@property
def model(self):
return self._model
@property
def m(self):
return self._model
@property
def properties(self):
data = LOInspect(self.obj).properties
return data
@properties.setter
def properties(self, properties: dict):
set_properties(self.model, properties)
@property
def name(self):
return self.model.Name
@property
def parent(self):
return self.obj.Context
@property
def tag(self):
return self.model.Tag
@tag.setter
def tag(self, value):
self.model.Tag = value
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.setVisible(value)
@property
def enabled(self):
return self.model.Enabled
@enabled.setter
def enabled(self, value):
self.model.Enabled = value
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
@property
def align(self):
return self.model.Align
@align.setter
def align(self, value):
self.model.Align = value
@property
def valign(self):
return self.model.VerticalAlign
@valign.setter
def valign(self, value):
self.model.VerticalAlign = value
@property
def font_weight(self):
return self.model.FontWeight
@font_weight.setter
def font_weight(self, value):
self.model.FontWeight = value
@property
def font_height(self):
return self.model.FontHeight
@font_height.setter
def font_height(self, value):
self.model.FontHeight = value
@property
def font_name(self):
return self.model.FontName
@font_name.setter
def font_name(self, value):
self.model.FontName = value
@property
def font_underline(self):
return self.model.FontUnderline
@font_underline.setter
def font_underline(self, value):
self.model.FontUnderline = value
@property
def text_color(self):
return self.model.TextColor
@text_color.setter
def text_color(self, value):
self.model.TextColor = value
@property
def back_color(self):
return self.model.BackgroundColor
@back_color.setter
def back_color(self, value):
self.model.BackgroundColor = value
@property
def multi_line(self):
return self.model.MultiLine
@multi_line.setter
def multi_line(self, value):
self.model.MultiLine = value
@property
def help_text(self):
return self.model.HelpText
@help_text.setter
def help_text(self, value):
self.model.HelpText = value
@property
def border(self):
return self.model.Border
@border.setter
def border(self, value):
# ~ Bug for report
self.model.Border = 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
def _get_possize(self, name):
ps = self.obj.getPosSize()
return getattr(ps, name)
def _set_possize(self, name, value):
ps = self.obj.getPosSize()
setattr(ps, name, value)
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
return
@property
def x(self):
if hasattr(self.model, 'PositionX'):
return self.model.PositionX
return self._get_possize('X')
@x.setter
def x(self, value):
if hasattr(self.model, 'PositionX'):
self.model.PositionX = value
else:
self._set_possize('X', value)
@property
def y(self):
if hasattr(self.model, 'PositionY'):
return self.model.PositionY
return self._get_possize('Y')
@y.setter
def y(self, value):
if hasattr(self.model, 'PositionY'):
self.model.PositionY = value
else:
self._set_possize('Y', value)
@property
def tab_index(self):
return self._model.TabIndex
@tab_index.setter
def tab_index(self, value):
self.model.TabIndex = value
@property
def tab_stop(self):
return self._model.Tabstop
@tab_stop.setter
def tab_stop(self, value):
self.model.Tabstop = value
@property
def ps(self):
ps = self.obj.getPosSize()
return ps
@ps.setter
def ps(self, ps):
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
def set_focus(self):
self.obj.setFocus()
return
def ps_from(self, source):
self.ps = source.ps
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, center=False):
if x:
self.x = origin.x + origin.width + x
else:
self.x = origin.x
if y:
h = origin.height
if y < 0:
h = 0
self.y = origin.y + h + y
else:
self.y = origin.y
if center:
self.center()
return
class UnoLabel(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'label'
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = 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
@property
def echochar(self):
return chr(self.model.EchoChar)
@echochar.setter
def echochar(self, value):
if value:
self.model.EchoChar = ord(value[0])
else:
self.model.EchoChar = 0
def validate(self):
return
class UnoButton(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'button'
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
@property
def image(self):
return self.model.ImageURL
@image.setter
def image(self, value):
self.model.ImageURL = Paths.to_url(value)
UNO_CLASSES = {
'label': UnoLabel,
'text': UnoText,
'button': UnoButton,
# ~ 'link': UnoLabelLink,
# ~ 'radio': UnoRadio,
# ~ 'checkbox': UnoCheckBox,
# ~ 'image': UnoImage,
# ~ 'listbox': UnoListBox,
# ~ 'roadmap': UnoRoadmap,
# ~ 'tree': UnoTree,
# ~ 'grid': UnoGrid,
# ~ 'pages': UnoPages,
# ~ 'spinbutton': UnoSpinButton,
# ~ 'numeric': UnoNumericField,
}
class DialogBox(BaseObject):
SERVICE = 'com.sun.star.awt.DialogProvider'
SERVICE_DIALOG = 'com.sun.star.awt.UnoControlDialog'
def __init__(self, properties: dict={}):
self._controls = {}
obj = self._create(properties)
super().__init__(obj)
self._init_controls()
def _create_from_path(self, path: str):
dp = create_instance(self.SERVICE, True)
dialog = dp.createDialog(Paths.to_url(path))
return dialog
def _create_from_location(self, properties: dict):
# ~ uid = docs.active.uid
# ~ url = f'vnd.sun.star.tdoc:/{uid}/Dialogs/{library}/{name}.xml'
name = properties['Name']
library = properties.get('Library', 'Standard')
location = properties.get('Location', 'application').lower()
if location == 'user':
location = 'application'
url = f'vnd.sun.star.script:{library}.{name}?location={location}'
if location == 'document':
doc = LODocuments().active.obj
dp = create_instance(self.SERVICE, arguments=doc)
else:
dp = create_instance(self.SERVICE, True)
dialog = dp.createDialog(url)
return dialog
def _create_from_properties(self, properties: dict):
dialog = create_instance(self.SERVICE_DIALOG, True)
model = create_instance(f'{self.SERVICE_DIALOG}Model', True)
properties['Width'] = properties.get('Width', 200)
properties['Height'] = properties.get('Height', 150)
set_properties(model, properties)
dialog.setModel(model)
dialog.setVisible(False)
dialog.createPeer(Services.toolkit, None)
return dialog
def _create(self, properties: dict):
path = properties.pop('Path', '')
if path:
dialog = self._create_from_path(path)
elif 'Location' in properties:
dialog = self._create_from_location(properties)
else:
dialog = self._create_from_properties(properties)
return dialog
def _init_controls(self):
for control in self.obj.Controls:
tipo = control.ImplementationName
name = control.Model.Name
if not tipo in TYPE_CONTROL:
log.debug(f'Type control: {tipo}')
raise AttributeError(f"Has no class '{tipo}'")
control = UNO_CLASSES[TYPE_CONTROL[tipo]](control)
setattr(self, name, control)
self._controls[name] = control
return
def execute(self):
return self.obj.execute()
def open(self):
return self.execute()
def close(self, value=0):
value = self.obj.endDialog(value)
return value
# ~ def close(self, value=0):
# ~ if self._modal:
# ~ value = self.obj.endDialog(value)
# ~ else:
# ~ self.visible = False
# ~ self.obj.dispose()
# ~ return value
# ~ def show(self, modal=True):
# ~ self._modal = modal
# ~ if modal:
# ~ return self.obj.execute()
# ~ else:
# ~ self.visible = True
# ~ return
@property
def model(self):
return self.obj.Model
@property
def name(self):
return self.model.Name
@property
def height(self):
return self.model.Height
@height.setter
def height(self, value):
self.model.Height = value
@property
def width(self):
return self.model.Width
@width.setter
def width(self, value):
self.model.Width = value
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.Visible = value
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
@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)
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers(self)
self._connect_listeners()
def _connect_listeners(self):
for control in self.obj.Controls:
add_listeners(self.events, control)
return
def _set_image_url(self, image: str):
if Paths.exists(image):
return Paths.to_url(image)
path = Paths.join(self._path, DIR['images'], image)
return Paths.to_url(path)
def _special_properties(self, tipo, properties):
if tipo == 'link' and not 'Label' in properties:
properties['Label'] = properties['URL']
return properties
if tipo == 'button':
if 'ImageURL' in properties:
properties['ImageURL'] = self._set_image_url(properties['ImageURL'])
properties['FocusOnClick'] = properties.get('FocusOnClick', False)
return properties
if tipo == 'roadmap':
properties['Height'] = properties.get('Height', self.height)
if 'Title' in properties:
properties['Text'] = properties.pop('Title')
return properties
if tipo == 'tree':
properties['SelectionType'] = properties.get('SelectionType', SINGLE)
return properties
if tipo == 'grid':
properties['X'] = properties.get('X', SEPARATION)
properties['Y'] = properties.get('Y', SEPARATION)
properties['Width'] = properties.get('Width', self.width - SEPARATION * 2)
properties['Height'] = properties.get('Height', self.height - SEPARATION * 2)
properties['ShowRowHeader'] = properties.get('ShowRowHeader', True)
return properties
if tipo == 'pages':
properties['Width'] = properties.get('Width', self.width)
properties['Height'] = properties.get('Height', self.height)
return properties
def add_control(self, properties):
tipo = properties.pop('Type').lower()
root = properties.pop('Root', '')
sheets = properties.pop('Sheets', ())
columns = properties.pop('Columns', ())
properties = self._special_properties(tipo, properties)
model = self.model.createInstance(MODELS[tipo])
set_properties(model, properties)
name = properties['Name']
self.model.insertByName(name, model)
control = self.obj.getControl(name)
add_listeners(self.events, control)
control = UNO_CLASSES[tipo](control)
if tipo in ('listbox',):
control.path = self.path
if tipo == 'tree' and root:
control.root = root
elif tipo == 'grid' and columns:
control.columns = columns
elif tipo == 'pages' and sheets:
control.sheets = sheets
control.events = self.events
setattr(self, name, control)
self._controls[name] = control
return control
class LODialog():
@classmethod
def create(cls, properties: dict={}):
return DialogBox(properties)
def inputbox(message, default='', title=TITLE, echochar=''):
class ControllersInput(object):
def __init__(self, dialog):
self.d = dialog
def cmd_ok_action(self, event):
self.d.close(1)
return
properties = {
'Title': title,
'Width': 200,
'Height': 80,
}
dlg = DialogBox(properties)
dlg.events = ControllersInput
properties = {
'Type': 'Label',
'Name': 'lbl_msg',
'Label': message,
'Width': 140,
'Height': 50,
'X': 5,
'Y': 5,
'MultiLine': True,
'Border': 1,
}
dlg.add_control(properties)
properties = {
'Type': 'Text',
'Name': 'txt_value',
'Text': default,
'Width': 190,
'Height': 15,
}
if echochar:
properties['EchoChar'] = ord(echochar[0])
dlg.add_control(properties)
dlg.txt_value.move(dlg.lbl_msg)
properties = {
'Type': 'button',
'Name': 'cmd_ok',
'Label': _('OK'),
'Width': 40,
'Height': 15,
'DefaultButton': True,
'PushButtonType': 1,
}
dlg.add_control(properties)
dlg.cmd_ok.move(dlg.lbl_msg, 10, 0)
properties = {
'Type': 'button',
'Name': 'cmd_cancel',
'Label': _('Cancel'),
'Width': 40,
'Height': 15,
'PushButtonType': 2,
}
dlg.add_control(properties)
dlg.cmd_cancel.move(dlg.cmd_ok)
value = ''
if dlg.open():
value = dlg.txt_value.value
return value