#!/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