easymacro/source/easymacro.bk2

4672 lines
123 KiB
Python

#!/usr/bin/env python3
# == Rapid Develop Macros in LibreOffice ==
import base64
import ctypes
import gettext
import zipfile
from collections import OrderedDict
from collections.abc import MutableMapping
from decimal import Decimal
from enum import IntEnum
import imaplib
from com.sun.star.awt.PosSize import POSSIZE, SIZE
from com.sun.star.sheet import TableFilterField
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
from com.sun.star.util import Time, Date, DateTime
from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
from com.sun.star.lang import Locale
from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMenuListener
from com.sun.star.awt import XMouseListener
from com.sun.star.awt import XMouseMotionListener
from com.sun.star.awt import XFocusListener
from com.sun.star.awt import XKeyListener
from com.sun.star.awt import XItemListener
from com.sun.star.awt import XTabListener
from com.sun.star.awt import XSpinListener
from com.sun.star.awt import XWindowListener
from com.sun.star.awt import XTopWindowListener
from com.sun.star.awt.grid import XGridDataListener
from com.sun.star.awt.grid import XGridSelectionListener
from com.sun.star.script import ScriptEventDescriptor
# ~ 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.style.VerticalAlignment import TOP, MIDDLE, BOTTOM
from com.sun.star.view.SelectionType import SINGLE, MULTI, RANGE
from com.sun.star.sdb.CommandType import TABLE, QUERY, COMMAND
try:
from peewee import Database, DateTimeField, DateField, TimeField, \
__exception_wrapper__
except ImportError as e:
Database = DateField = TimeField = DateTimeField = object
print('You need install peewee, only if you will develop with Base')
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__)
# ~ You can get custom salt
# ~ codecs.encode(os.urandom(16), 'hex')
# ~ but, not modify this file, modify in import file
SALT = b'c9548699d4e432dfd2b46adddafbb06d'
LOG_NAME = 'ZAZ'
LEFT = 0
CENTER = 1
RIGHT = 2
CALC = 'calc'
WRITER = 'writer'
DRAW = 'draw'
IMPRESS = 'impress'
BASE = 'base'
MATH = 'math'
BASIC = 'basic'
MAIN = 'main'
TYPE_DOC = {
CALC: 'com.sun.star.sheet.SpreadsheetDocument',
WRITER: 'com.sun.star.text.TextDocument',
DRAW: 'com.sun.star.drawing.DrawingDocument',
IMPRESS: 'com.sun.star.presentation.PresentationDocument',
MATH: 'com.sun.star.formula.FormulaProperties',
BASE: 'com.sun.star.sdb.DocumentDataSource',
BASIC: 'com.sun.star.script.BasicIDE',
MAIN: 'com.sun.star.frame.StartModule',
}
OBJ_SHAPE = 'com.sun.star.comp.sc.ScShapeObj'
OBJ_GRAPHIC = 'SwXTextGraphicObject'
OBJ_TEXTS = 'SwXTextRanges'
OBJ_TEXT = 'SwXTextRange'
CLSID = {
'FORMULA': '078B7ABA-54FC-457F-8551-6147e776a997',
}
SERVICES = {
'TEXT_EMBEDDED': 'com.sun.star.text.TextEmbeddedObject',
'TEXT_TABLE': 'com.sun.star.text.TextTable',
'GRAPHIC': 'com.sun.star.text.GraphicObject',
}
# ~ from com.sun.star.sheet.FilterOperator import EMPTY, NO_EMPTY, EQUAL, NOT_EQUAL
class FilterOperator(IntEnum):
EMPTY = 0
NO_EMPTY = 1
EQUAL = 2
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
# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet.html#aa5aa6dbecaeb5e18a476b0a58279c57a
class ValidationType():
from com.sun.star.sheet.ValidationType \
import ANY, WHOLE, DECIMAL, DATE, TIME, TEXT_LEN, LIST, CUSTOM
VT = ValidationType
# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet.html#aecf58149730f4c8c5c18c70f3c7c5db7
class ValidationAlertStyle():
from com.sun.star.sheet.ValidationAlertStyle \
import STOP, WARNING, INFO, MACRO
VAS = ValidationAlertStyle
# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1ConditionOperator2.html
class ConditionOperator():
from com.sun.star.sheet.ConditionOperator2 \
import NONE, EQUAL, NOT_EQUAL, GREATER, GREATER_EQUAL, LESS, \
LESS_EQUAL, BETWEEN, NOT_BETWEEN, FORMULA, DUPLICATE, NOT_DUPLICATE
CO = ConditionOperator
class DataPilotFieldOrientation():
from com.sun.star.sheet.DataPilotFieldOrientation \
import HIDDEN, COLUMN, ROW, PAGE, DATA
DPFO = DataPilotFieldOrientation
class CellInsertMode():
from com.sun.star.sheet.CellInsertMode import DOWN, RIGHT, ROWS, COLUMNS
CIM = CellInsertMode
class CellDeleteMode():
from com.sun.star.sheet.CellDeleteMode import UP, LEFT, ROWS, COLUMNS
CDM = CellDeleteMode
class FormButtonType():
from com.sun.star.form.FormButtonType import PUSH, SUBMIT, RESET, URL
FBT = FormButtonType
class TextContentAnchorType():
from com.sun.star.text.TextContentAnchorType \
import AT_PARAGRAPH, AS_CHARACTER, AT_PAGE, AT_FRAME, AT_CHARACTER
TCAT = TextContentAnchorType
SECONDS_DAY = 60 * 60 * 24
DIR = {
'images': 'images',
'locales': 'locales',
}
KEY = {
'enter': 1280,
}
DEFAULT_MIME_TYPE = 'png'
MIME_TYPE = {
'png': 'image/png',
'jpg': 'image/jpeg',
}
try:
COUNTRY = LANGUAGE.split('-')[1]
except:
COUNTRY = ''
LOCALE = Locale(LANG, COUNTRY, '')
def inspect(obj: Any, to_sheet: bool=True) -> None:
if hasattr(obj, 'obj'):
obj = obj.obj
if to_sheet:
_inspect_to_sheet(obj)
else:
zaz = create_instance('net.elmau.zaz.inspect')
zaz.inspect(obj)
return
def get_type_doc(obj: Any) -> str:
for k, v in TYPE_DOC.items():
if obj.supportsService(v):
return k
return ''
def _get_class_doc(obj: Any) -> Any:
classes = {
CALC: LOCalc,
WRITER: LOWriter,
DRAW: LODraw,
IMPRESS: LOImpress,
BASE: LOBase,
MATH: LOMath,
BASIC: LOBasic,
}
type_doc = get_type_doc(obj)
return classes[type_doc](obj)
def _date_to_struct(value):
if isinstance(value, datetime.datetime):
d = DateTime()
d.Year = value.year
d.Month = value.month
d.Day = value.day
d.Hours = value.hour
d.Minutes = value.minute
d.Seconds = value.second
elif isinstance(value, datetime.date):
d = Date()
d.Day = value.day
d.Month = value.month
d.Year = value.year
elif isinstance(value, datetime.time):
d = Time()
d.Hours = value.hour
d.Minutes = value.minute
d.Seconds = value.second
return d
def _struct_to_date(value):
d = None
if isinstance(value, Time):
d = datetime.time(value.Hours, value.Minutes, value.Seconds)
elif isinstance(value, Date):
if value != Date():
d = datetime.date(value.Year, value.Month, value.Day)
elif isinstance(value, DateTime):
if value.Year > 0:
d = datetime.datetime(
value.Year, value.Month, value.Day,
value.Hours, value.Minutes, value.Seconds)
return d
def install_locales(path: str, domain: str='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)
def get_size_screen():
res = ''
if IS_WIN:
user32 = ctypes.windll.user32
res = f'{user32.GetSystemMetrics(0)}x{user32.GetSystemMetrics(1)}'
else:
try:
args = 'xrandr | grep "*" | cut -d " " -f4'
res = run(args, split=False)
except Exception as e:
error(e)
return res.strip()
def _get_key(password):
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=SALT,
iterations=100000)
key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
return key
def encrypt(data, password):
from cryptography.fernet import Fernet
f = Fernet(_get_key(password))
if isinstance(data, str):
data = data.encode()
token = f.encrypt(data).decode()
return token
def decrypt(token, password):
from cryptography.fernet import Fernet, InvalidToken
data = ''
f = Fernet(_get_key(password))
try:
data = f.decrypt(token.encode()).decode()
except InvalidToken as e:
error('Invalid Token')
return data
def switch_design_mode(doc):
call_dispatch(doc.frame, '.uno:SwitchControlDesignMode')
return
class ImapServer(object):
def __init__(self, config):
self._server = None
self._error = ''
self._is_connect = self._login(config)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
@property
def is_connect(self):
return self._is_connect
@property
def error(self):
return self._error
def _login(self, config):
try:
# ~ hosts = 'gmail' in config['server']
if config['ssl']:
self._server = imaplib.IMAP4_SSL(config['server'], config['port'])
else:
self._server = imaplib.IMAP4(config['server'], config['port'])
self._server.login(config['user'], config['password'])
self._server.select()
return True
except imaplib.IMAP4.error as e:
self._error = str(e)
return False
except Exception as e:
self._error = str(e)
return False
return False
def get_folders(self, exclude=()):
folders = {}
result, subdir = self._server.list()
for s in subdir:
print(s.decode('utf-8'))
return folders
def close(self):
try:
self._server.close()
self._server.logout()
msg = 'Close connection...'
debug(msg)
except:
pass
return
# ~ Classes
class LOBaseObject(object):
def __init__(self, obj):
self._obj = obj
def __setattr__(self, name, value):
exists = hasattr(self, name)
if not exists and not name in ('_obj', '_index', '_view'):
setattr(self._obj, name, value)
else:
super().__setattr__(name, value)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
class LOCalc(LODocument):
@property
def db_ranges(self):
# ~ return LOCalcDataBaseRanges(self.obj.DataBaseRanges)
return self.obj.DatabaseRanges
def render(self, data, sheet=None, clean=True):
if sheet is None:
sheet = self.active
return sheet.render(data, clean=clean)
class LOChart(object):
def __init__(self, name, obj, draw_page):
self._name = name
self._obj = obj
self._eobj = self._obj.EmbeddedObject
self._type = 'Column'
self._cell = None
self._shape = self._get_shape(draw_page)
self._pos = self._shape.Position
def __getitem__(self, index):
return LOBaseObject(self.diagram.getDataRowProperties(index))
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
@property
def name(self):
return self._name
@property
def diagram(self):
return self._eobj.Diagram
@property
def type(self):
return self._type
@type.setter
def type(self, value):
self._type = value
if value == 'Bar':
self.diagram.Vertical = True
return
type_chart = f'com.sun.star.chart.{value}Diagram'
self._eobj.setDiagram(self._eobj.createInstance(type_chart))
@property
def cell(self):
return self._cell
@cell.setter
def cell(self, value):
self._cell = value
self._shape.Anchor = value.obj
@property
def position(self):
return self._pos
@position.setter
def position(self, value):
self._pos = value
self._shape.Position = value
def _get_shape(self, draw_page):
for shape in draw_page:
if shape.PersistName == self.name:
break
return shape
class LOSheetCharts(object):
def __init__(self, obj, sheet):
self._obj = obj
self._sheet = sheet
def __getitem__(self, index):
return LOChart(index, self.obj[index], self._sheet.draw_page)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __contains__(self, item):
return item in self.obj
def __len__(self):
return len(self.obj)
@property
def obj(self):
return self._obj
def new(self, name, pos_size, data):
self.obj.addNewByName(name, pos_size, data, True, True)
return LOChart(name, self.obj[name], self._sheet.draw_page)
class LOSheetTableField(object):
def __init__(self, obj):
self._obj = obj
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
@property
def name(self):
return self.obj.Name
@property
def orientation(self):
return self.obj.Orientation
@orientation.setter
def orientation(self, value):
self.obj.Orientation = value
# ~ com.sun.star.sheet.DataPilotFieldOrientation.ROW
class LOSheetTable(object):
def __init__(self, obj):
self._obj = obj
self._source = None
def __getitem__(self, index):
field = self.obj.DataPilotFields[index]
return LOSheetTableField(field)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
@property
def obj(self):
return self._obj
@property
def filter(self):
return self.obj.ShowFilterButton
@filter.setter
def filter(self, value):
self.obj.ShowFilterButton = value
@property
def source(self):
return self._source
@source.setter
def source(self, value):
self._source = value
self.obj.SourceRange = value.range_address
@property
def rows(self):
return self.obj.RowFields
@rows.setter
def rows(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.ROW
@property
def columns(self):
return self.obj.ColumnFields
@columns.setter
def columns(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.COLUMN
@property
def data(self):
return self.obj.DataFields
@data.setter
def data(self, values):
if not isinstance(values, tuple):
values = (values,)
for v in values:
with self[v] as f:
f.orientation = DPFO.DATA
class LOSheetTables(object):
def __init__(self, obj, sheet):
self._obj = obj
self._sheet = sheet
def __getitem__(self, index):
return LOSheetTable(self.obj[index])
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __contains__(self, item):
return item in self.obj
@property
def obj(self):
return self._obj
@property
def count(self):
return self.obj.Count
@property
def names(self):
return self.obj.ElementNames
def new(self, name, target):
table = self.obj.createDataPilotDescriptor()
self.obj.insertNewByName(name, target.address, table)
return LOSheetTable(self.obj[name])
def remove(self, name):
self.obj.removeByName(name)
return
# ~ class LOFormControl(LOBaseObject):
class LOFormControl():
EVENTS = {
'action': 'actionPerformed',
'click': 'mousePressed',
}
TYPES = {
'actionPerformed': 'XActionListener',
'mousePressed': 'XMouseListener',
}
def __init__(self, obj, view, form):
self._obj = obj
self._view = view
self._form = form
self._m = view.Model
self._index = -1
# ~ def __setattr__(self, name, value):
# ~ if name in ('_form', '_view', '_m', '_index'):
# ~ self.__dict__[name] = value
# ~ else:
# ~ super().__setattr__(name, value)
def __str__(self):
return f'{self.name} ({self.type}) {[self.index]}'
@property
def obj(self):
return self._obj
@property
def form(self):
return self._form
@property
def doc(self):
return self.obj.Parent.Forms.Parent
@property
def name(self):
return self._m.Name
@name.setter
def name(self, value):
self._m.Name = value
@property
def tag(self):
return self._m.Tag
@tag.setter
def tag(self, value):
self._m.Tag = value
@property
def index(self):
return self._index
@index.setter
def index(self, value):
self._index = value
@property
def enabled(self):
return self._m.Enabled
@enabled.setter
def enabled(self, value):
self._m.Enabled = value
@property
def anchor(self):
return self.obj.Anchor
@anchor.setter
def anchor(self, value):
size = None
if hasattr(value, 'obj'):
size = getattr(value, 'size', None)
value = value.obj
self.obj.Anchor = value
if not size is None:
self.size = size
try:
self.obj.ResizeWithCell = True
except:
pass
@property
def size(self):
return self.obj.Size
@size.setter
def size(self, value):
self.obj.Size = value
@property
def events(self):
return self.form.getScriptEvents(self.index)
def add_event(self, name, macro):
if not 'name' in macro:
macro['name'] = '{}_{}'.format(self.name, name)
event = ScriptEventDescriptor()
event.AddListenerParam = ''
event.EventMethod = self.EVENTS[name]
event.ListenerType = self.TYPES[event.EventMethod]
event.ScriptCode = _get_url_script(macro)
event.ScriptType = 'Script'
for ev in self.events:
if ev.EventMethod == event.EventMethod and \
ev.ListenerType == event.ListenerType:
self.form.revokeScriptEvent(self.index,
event.ListenerType, event.EventMethod, event.AddListenerParam)
break
self.form.registerScriptEvent(self.index, event)
return
def set_focus(self):
self._view.setFocus()
return
class LOFormControlLabel(LOFormControl):
def __init__(self, obj, view, form):
super().__init__(obj, view, form)
@property
def type(self):
return 'label'
@property
def value(self):
return self._m.Label
@value.setter
def value(self, value):
self._m.Label = value
class LOFormControlText(LOFormControl):
def __init__(self, obj, view, form):
super().__init__(obj, view, form)
@property
def type(self):
return 'text'
@property
def value(self):
return self._m.Text
@value.setter
def value(self, value):
self._m.Text = value
class LOFormControlButton(LOFormControl):
def __init__(self, obj, view, form):
super().__init__(obj, view, form)
@property
def type(self):
return 'button'
@property
def value(self):
return self._m.Label
@value.setter
def value(self, value):
self._m.Text = Label
@property
def url(self):
return self._m.TargetURL
@url.setter
def url(self, value):
self._m.TargetURL = value
self._m.ButtonType = FormButtonType.URL
FORM_CONTROL_CLASS = {
'label': LOFormControlLabel,
'text': LOFormControlText,
'button': LOFormControlButton,
}
class LOForm(object):
MODELS = {
'label': 'com.sun.star.form.component.FixedText',
'text': 'com.sun.star.form.component.TextField',
'button': 'com.sun.star.form.component.CommandButton',
}
def __init__(self, obj, draw_page):
self._obj = obj
self._dp = draw_page
self._controls = {}
self._init_controls()
def __getitem__(self, index):
control = self.obj[index]
return self._controls[control.Name]
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __contains__(self, item):
return item in self.obj
def __len__(self):
return len(self.obj)
def __str__(self):
return f'Form: {self.name}'
def _init_controls(self):
types = {
'com.sun.star.form.OFixedTextModel': 'label',
'com.sun.star.form.OEditModel': 'text',
'com.sun.star.form.OButtonModel': 'button',
}
for i, control in enumerate(self.obj):
name = control.Name
tipo = types[control.ImplementationName]
view = self.doc.CurrentController.getControl(control)
control = FORM_CONTROL_CLASS[tipo](control, view, self._obj)
control.index = i
setattr(self, name, control)
self._controls[name] = control
return
@property
def obj(self):
return self._obj
@property
def name(self):
return self.obj.Name
@name.setter
def name(self, value):
self.obj.Name = value
@property
def source(self):
return self.obj.DataSourceName
@source.setter
def source(self, value):
self.obj.DataSourceName = value
@property
def type(self):
return self.obj.CommandType
@type.setter
def type(self, value):
self.obj.CommandType = value
@property
def command(self):
return self.obj.Command
@command.setter
def command(self, value):
self.obj.Command = value
@property
def doc(self):
return self.obj.Parent.Parent
def _special_properties(self, tipo, args):
if tipo == 'button':
# ~ if 'ImageURL' in args:
# ~ args['ImageURL'] = self._set_image_url(args['ImageURL'])
args['FocusOnClick'] = args.get('FocusOnClick', False)
return args
return args
def add(self, args):
name = args['Name']
tipo = args.pop('Type').lower()
w = args.pop('Width', 1000)
h = args.pop('Height', 200)
x = args.pop('X', 0)
y = args.pop('Y', 0)
control = self.doc.createInstance('com.sun.star.drawing.ControlShape')
control.setSize(Size(w, h))
control.setPosition(Point(x, y))
model = self.doc.createInstance(self.MODELS[tipo])
args = self._special_properties(tipo, args)
_set_properties(model, args)
control.Control = model
index = len(self)
self.obj.insertByIndex(index, model)
self._dp.add(control)
view = self.doc.CurrentController.getControl(self.obj.getByName(name))
control = FORM_CONTROL_CLASS[tipo](control, view, self.obj)
control.index = index
setattr(self, name, control)
self._controls[name] = control
return control
class LOSheetForms(object):
def __init__(self, draw_page):
self._dp = draw_page
self._obj = draw_page.Forms
def __getitem__(self, index):
return LOForm(self.obj[index], self._dp)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __contains__(self, item):
return item in self.obj
def __len__(self):
return len(self.obj)
@property
def obj(self):
return self._obj
@property
def doc(self):
return self.obj.Parent
@property
def count(self):
return len(self)
@property
def names(self):
return self.obj.ElementNames
def insert(self, name=''):
if not name:
name = f'form{self.count + 1}'
form = self.doc.createInstance('com.sun.star.form.component.Form')
self.obj.insertByName(name, form)
return LOForm(form, self._dp)
def remove(self, index):
if isinstance(index, int):
self.obj.removeByIndex(index)
else:
self.obj.removeByName(index)
return
# ~ IsFiltered,
# ~ IsManualPageBreak,
# ~ IsStartOfNewPage
class LOSheetRows(object):
def __init__(self, sheet, obj):
self._sheet = sheet
self._obj = obj
def __getitem__(self, index):
if isinstance(index, int):
rows = LOSheetRows(self._sheet, self.obj[index])
else:
rango = self._sheet[index.start:index.stop,0:]
rows = LOSheetRows(self._sheet, rango.obj.Rows)
return rows
def __len__(self):
return self.obj.Count
@property
def obj(self):
return self._obj
@property
def visible(self):
return self._obj.IsVisible
@visible.setter
def visible(self, value):
self._obj.IsVisible = value
@property
def color(self):
return self.obj.CellBackColor
@color.setter
def color(self, value):
self.obj.CellBackColor = value
@property
def is_transparent(self):
return self.obj.IsCellBackgroundTransparent
@is_transparent.setter
def is_transparent(self, value):
self.obj.IsCellBackgroundTransparent = value
@property
def height(self):
return self.obj.Height
@height.setter
def height(self, value):
self.obj.Height = value
def optimal(self):
self.obj.OptimalHeight = True
return
def insert(self, index, count):
self.obj.insertByIndex(index, count)
return
def remove(self, index, count):
self.obj.removeByIndex(index, count)
return
# ~ IsManualPageBreak,
# ~ IsStartOfNewPage
class LOSheetColumns(object):
def __init__(self, sheet, obj):
self._sheet = sheet
self._obj = obj
def __getitem__(self, index):
if isinstance(index, (int, str)):
rows = LOSheetColumns(self._sheet, self.obj[index])
else:
rango = self._sheet[0,index.start:index.stop]
rows = LOSheetColumns(self._sheet, rango.obj.Columns)
return rows
def __len__(self):
return self.obj.Count
@property
def obj(self):
return self._obj
@property
def visible(self):
return self._obj.IsVisible
@visible.setter
def visible(self, value):
self._obj.IsVisible = value
@property
def width(self):
return self.obj.Width
@width.setter
def width(self, value):
self.obj.Width = value
def optimal(self):
self.obj.OptimalWidth = True
return
def insert(self, index, count):
self.obj.insertByIndex(index, count)
return
def remove(self, index, count):
self.obj.removeByIndex(index, count)
return
class LOCalcSheet(object):
@property
def draw_page(self):
return LODrawPage(self.obj.DrawPage)
@property
def dp(self):
return self.draw_page
@property
def shapes(self):
return self.draw_page
@property
def charts(self):
return LOSheetCharts(self.obj.Charts, self)
@property
def tables(self):
return LOSheetTables(self.obj.DataPilotTables, self)
@property
def rows(self):
return LOSheetRows(self, self.obj.Rows)
@property
def columns(self):
return LOSheetColumns(self, self.obj.Columns)
@property
def forms(self):
return LOSheetForms(self.obj.DrawPage)
@property
def search_descriptor(self):
return self.obj.createSearchDescriptor()
@property
def replace_descriptor(self):
return self.obj.createReplaceDescriptor()
def render(self, data, rango=None, clean=True):
if rango is None:
rango = self.used_area
return rango.render(data, clean)
def find(self, search_string, rango=None):
if rango is None:
rango = self.used_area
return rango.find(search_string)
class LOCalcRange(object):
def __contains__(self, item):
return item.in_range(self)
@property
def back_color(self):
return self._obj.CellBackColor
@back_color.setter
def back_color(self, value):
self._obj.CellBackColor = get_color(value)
@property
def columns(self):
return self.obj.Columns.Count
@property
def column(self):
c1 = self.address.Column
c2 = c1 + 1
ra = self.current_region.range_address
r1 = ra.StartRow
r2 = ra.EndRow + 1
return LOCalcRange(self.sheet[r1:r2, c1:c2].obj)
@property
def rows(self):
return LOSheetRows(self.sheet, self.obj.Rows)
@property
def row(self):
r1 = self.address.Row
r2 = r1 + 1
ra = self.current_region.range_address
c1 = ra.StartColumn
c2 = ra.EndColumn + 1
return LOCalcRange(self.sheet[r1:r2, c1:c2].obj)
@property
def type(self):
return self.obj.Type
@property
def error(self):
return self.obj.getError()
# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
@property
def value(self):
v = None
if self.type == VALUE:
v = self.obj.getValue()
elif self.type == TEXT:
v = self.obj.getString()
elif self.type == FORMULA:
v = self.obj.getFormula()
return v
@value.setter
def value(self, data):
if isinstance(data, str):
if data[0] in '=':
self.obj.setFormula(data)
else:
self.obj.setString(data)
elif isinstance(data, Decimal):
self.obj.setValue(float(data))
elif isinstance(data, (int, float, bool)):
self.obj.setValue(data)
elif isinstance(data, datetime.datetime):
d = data.toordinal()
t = (data - datetime.datetime.fromordinal(d)).seconds / SECONDS_DAY
self.obj.setValue(d - DATE_OFFSET + t)
elif isinstance(data, datetime.date):
d = data.toordinal()
self.obj.setValue(d - DATE_OFFSET)
elif isinstance(data, datetime.time):
d = (data.hour * 3600 + data.minute * 60 + data.second) / SECONDS_DAY
self.obj.setValue(d)
@property
def date(self):
value = int(self.obj.Value)
date = datetime.date.fromordinal(value + DATE_OFFSET)
return date
@property
def time(self):
seconds = self.obj.Value * SECONDS_DAY
time_delta = datetime.timedelta(seconds=seconds)
time = (datetime.datetime.min + time_delta).time()
return time
@property
def datetime(self):
return datetime.datetime.combine(self.date, self.time)
@property
def dict(self):
rows = self.data
k = rows[0]
data = [dict(zip(k, r)) for r in rows[1:]]
return data
@dict.setter
def dict(self, values):
data = [tuple(values[0].keys())]
data += [tuple(d.values()) for d in values]
self.data = data
@property
def formula(self):
return self.obj.getFormulaArray()
@formula.setter
def formula(self, values):
self.obj.setFormulaArray(values)
@property
def array_formula(self):
return self.obj.ArrayFormula
@array_formula.setter
def array_formula(self, value):
self.obj.ArrayFormula = value
@property
def next_cell(self):
a = self.current_region.range_address
col = a.StartColumn
row = a.EndRow + 1
return LOCalcRange(self.sheet[row, col].obj)
@property
def position(self):
return self.obj.Position
@property
def size(self):
return self.obj.Size
@property
def possize(self):
data = {
'Width': self.size.Width,
'Height': self.size.Height,
'X': self.position.X,
'Y': self.position.Y,
}
return data
@property
def visible(self):
cursor = self.cursor
rangos = cursor.queryVisibleCells()
rangos = LOCalcRanges(rangos)
return rangos
@property
def merged_area(self):
cursor = self.cursor
cursor.collapseToMergedArea()
rango = LOCalcRange(self.sheet[cursor.AbsoluteName].obj)
return rango
@property
def empty(self):
cursor = self.cursor
rangos = cursor.queryEmptyCells()
rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos]
return tuple(rangos)
def query_content(self, type_content=1023):
cursor = self.cursor
rangos = cursor.queryContentCells(type_content)
rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos]
return tuple(rangos)
@property
def merge(self):
return self.obj.IsMerged
@merge.setter
def merge(self, value):
self.obj.merge(value)
@property
def auto_format(self):
return ''
@auto_format.setter
def auto_format(self, value):
self.obj.autoFormat(value)
@property
def validation(self):
return self.obj.Validation
@validation.setter
def validation(self, values):
current = self.validation
if not values:
current.Type = ValidationType.ANY
current.ShowInputMessage = False
else:
is_list = False
for k, v in values.items():
if k == 'Type' and v == VT.LIST:
is_list = True
if k == 'Formula1' and is_list:
if isinstance(v, (tuple, list)):
v = ';'.join(['"{}"'.format(i) for i in v])
setattr(current, k, v)
self.obj.Validation = current
def select(self):
self.doc._cc.select(self.obj)
return
def search(self, options, find_all=True):
rangos = None
descriptor = self.sheet.search_descriptor
descriptor.setSearchString(options['Search'])
descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
descriptor.SearchWords = options.get('Words', False)
if hasattr(descriptor, 'SearchRegularExpression'):
descriptor.SearchRegularExpression = options.get('RegularExpression', False)
if hasattr(descriptor, 'SearchType') and 'Type' in options:
descriptor.SearchType = options['Type']
if find_all:
found = self.obj.findAll(descriptor)
else:
found = self.obj.findFirst(descriptor)
if found:
if found.ImplementationName == OBJ_CELL:
rangos = LOCalcRange(found)
else:
rangos = [LOCalcRange(f) for f in found]
return rangos
def replace(self, options):
descriptor = self.sheet.replace_descriptor
descriptor.setSearchString(options['Search'])
descriptor.setReplaceString(options['Replace'])
descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
descriptor.SearchWords = options.get('Words', False)
if hasattr(descriptor, 'SearchRegularExpression'):
descriptor.SearchRegularExpression = options.get('RegularExpression', False)
if hasattr(descriptor, 'SearchType') and 'Type' in options:
descriptor.SearchType = options['Type']
count = self.obj.replaceAll(descriptor)
return count
def in_range(self, rango):
if isinstance(rango, LOCalcRange):
address = rango.range_address
else:
address = rango.RangeAddress
result = self.cursor.queryIntersection(address)
return bool(result.Count)
def move(self, target):
sheet = self.sheet.obj
sheet.moveRange(target.address, self.range_address)
return
def insert(self, insert_mode=CIM.DOWN):
sheet = self.sheet.obj
sheet.insertCells(self.range_address, insert_mode)
return
def delete(self, delete_mode=CDM.UP):
sheet = self.sheet.obj
sheet.removeRange(self.range_address, delete_mode)
return
def copy_from(self, source):
self.sheet.obj.copyRange(self.address, source.range_address)
return
def copy_to(self, target):
self.sheet.obj.copyRange(target.address, self.range_address)
return
# ~ def copy_to(self, cell, formula=False):
# ~ rango = cell.to_size(self.rows, self.columns)
# ~ if formula:
# ~ rango.formula = self.formula
# ~ else:
# ~ rango.data = self.data
# ~ return
# ~ def copy_from(self, rango, formula=False):
# ~ data = rango
# ~ if isinstance(rango, LOCalcRange):
# ~ if formula:
# ~ data = rango.formula
# ~ else:
# ~ data = rango.data
# ~ rows = len(data)
# ~ cols = len(data[0])
# ~ if formula:
# ~ self.to_size(rows, cols).formula = data
# ~ else:
# ~ self.to_size(rows, cols).data = data
# ~ return
def optimal_width(self):
self.obj.Columns.OptimalWidth = True
return
def clean_render(self, template='\{(\w.+)\}'):
self._sd.SearchRegularExpression = True
self._sd.setSearchString(template)
self.obj.replaceAll(self._sd)
return
def render(self, data, clean=True):
self._sd = self.sheet.obj.createSearchDescriptor()
self._sd.SearchCaseSensitive = False
for k, v in data.items():
cell = self._render_value(k, v)
return cell
def _render_value(self, key, value, parent=''):
cell = None
if isinstance(value, dict):
for k, v in value.items():
# ~ print(1, 'RENDER', k, v)
cell = self._render_value(k, v, key)
return cell
elif isinstance(value, (list, tuple)):
self._render_list(key, value)
return
search = f'{{{key}}}'
if parent:
search = f'{{{parent}.{key}}}'
ranges = self.find_all(search)
if ranges is None:
return
# ~ for cell in ranges or range(0):
for cell in ranges:
self._set_new_value(cell, search, value)
return LOCalcRange(cell)
def _set_new_value(self, cell, search, value):
if not cell.ImplementationName == 'ScCellObj':
return
if isinstance(value, str):
pattern = re.compile(search, re.IGNORECASE)
new_value = pattern.sub(value, cell.String)
cell.String = new_value
else:
LOCalcRange(cell).value = value
return
def _render_list(self, key, rows):
for row in rows:
for k, v in row.items():
self._render_value(k, v)
return
def find(self, search_string):
if self._sd is None:
self._sd = self.sheet.obj.createSearchDescriptor()
self._sd.SearchCaseSensitive = False
self._sd.setSearchString(search_string)
cell = self.obj.findFirst(self._sd)
if cell:
cell = LOCalcRange(cell)
return cell
def find_all(self, search_string):
if self._sd is None:
self._sd = self.sheet.obj.createSearchDescriptor()
self._sd.SearchCaseSensitive = False
self._sd.setSearchString(search_string)
ranges = self.obj.findAll(self._sd)
return ranges
def filter(self, args, with_headers=True):
ff = TableFilterField()
ff.Field = args['Field']
ff.Operator = args['Operator']
if isinstance(args['Value'], str):
ff.IsNumeric = False
ff.StringValue = args['Value']
else:
ff.IsNumeric = True
ff.NumericValue = args['Value']
fd = self.obj.createFilterDescriptor(True)
fd.ContainsHeader = with_headers
fd.FilterFields = ((ff,))
# ~ self.obj.AutoFilter = True
self.obj.filter(fd)
return
def copy_format_from(self, rango):
rango.select()
self.doc.copy()
self.select()
args = {
'Flags': 'T',
'MoveMode': 4,
}
url = '.uno:InsertContents'
call_dispatch(self.doc.frame, url, args)
return
def to_image(self):
self.select()
self.doc.copy()
args = {'SelectedFormat': 141}
url = '.uno:ClipboardFormatItems'
call_dispatch(self.doc.frame, url, args)
return self.sheet.shapes[-1]
def insert_image(self, path, options={}):
args = options.copy()
ps = self.possize
args['Width'] = args.get('Width', ps['Width'])
args['Height'] = args.get('Height', ps['Height'])
args['X'] = args.get('X', ps['X'])
args['Y'] = args.get('Y', ps['Y'])
# ~ img.ResizeWithCell = True
img = self.sheet.dp.insert_image(path, args)
img.anchor = self.obj
args.clear()
return img
def insert_shape(self, tipo, args={}):
ps = self.possize
args['Width'] = args.get('Width', ps['Width'])
args['Height'] = args.get('Height', ps['Height'])
args['X'] = args.get('X', ps['X'])
args['Y'] = args.get('Y', ps['Y'])
shape = self.sheet.dp.add(tipo, args)
shape.anchor = self.obj
args.clear()
return
def filter_by_color(self, cell):
rangos = cell.column[1:,:].visible
for r in rangos:
for c in r:
if c.back_color != cell.back_color:
c.rows.visible = False
return
def clear(self, what=1023):
# ~ http://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
self.obj.clearContents(what)
return
def transpose(self):
# ~ 'Flags': 'A',
# ~ 'FormulaCommand': 0,
# ~ 'SkipEmptyCells': False,
# ~ 'AsLink': False,
# ~ 'MoveMode': 4,
self.select()
self.doc.copy()
self.clear(1023)
self[0,0].select()
self.doc.insert_contents({'Transpose': True})
_CB.set('')
return
def transpose_data(self, formula=False):
data = self.data
if formula:
data = self.formula
data = tuple(zip(*data))
self.clear(1023)
self[0,0].copy_from(data, formula=formula)
return
def merge_by_row(self):
for r in range(len(self.rows)):
self[r].merge = True
return
def fill(self, source=1):
self.obj.fillAuto(0, source)
return
def _cast(self, t, v):
if not t:
return v
if t == datetime.date:
nv = datetime.date.fromordinal(int(v) + DATE_OFFSET)
else:
nv = t(v)
return nv
def get_data(self, types):
values = [
[self._cast(types[i], v) for i, v in enumerate(row)]
for row in self.data
]
return values
class LOWriterStyles(object):
def __init__(self, styles):
self._styles = styles
@property
def names(self):
return {s.DisplayName: s.Name for s in self._styles}
def __str__(self):
return '\n'.join(tuple(self.names.values()))
class LOWriterStylesFamilies(object):
def __init__(self, styles):
self._styles = styles
def __getitem__(self, index):
styles = {
'Character': 'CharacterStyles',
'Paragraph': 'ParagraphStyles',
'Page': 'PageStyles',
'Frame': 'FrameStyles',
'Numbering': 'NumberingStyles',
'Table': 'TableStyles',
'Cell': 'CellStyles',
}
name = styles.get(index, index)
return LOWriterStyles(self._styles[name])
def __iter__(self):
self._index = 0
return self
def __next__(self):
obj = LOWriterStyles(self._styles[self._index])
self._index += 1
return obj
# ~ raise StopIteration
@property
def names(self):
return self._styles.ElementNames
def __str__(self):
return '\n'.join(self.names)
class LOWriterPageStyle(LOBaseObject):
def __init__(self, obj):
super().__init__(obj)
def __str__(self):
return f'Page Style: {self.name}'
@property
def name(self):
return self._obj.Name
class LOWriterPageStyles(object):
def __init__(self, styles):
self._styles = styles
def __getitem__(self, index):
return LOWriterPageStyle(self._styles[index])
@property
def names(self):
return self._styles.ElementNames
def __str__(self):
return '\n'.join(self.names)
class LOWriterTextRange(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
self._is_paragraph = self.obj.ImplementationName == 'SwXParagraph'
self._is_table = self.obj.ImplementationName == 'SwXTextTable'
self._is_text = self.obj.ImplementationName == 'SwXTextPortion'
self._is_section = not self.obj.TextSection is None
self._parts = []
if self._is_paragraph:
self._parts = [LOWriterTextRange(p, doc) for p in obj]
def __iter__(self):
self._index = 0
return self
def __next__(self):
try:
obj = self._parts[self._index]
except IndexError:
raise StopIteration
self._index += 1
return obj
@property
def string(self):
s = ''
if not self._is_table:
s = self.obj.String
return s
@string.setter
def string(self, value):
self.obj.String = value
@property
def value(self):
return self.string
@value.setter
def value(self, value):
self.string = value
@property
def style(self):
s = ''
if self.is_paragraph:
s = self.obj.ParaStyleName
elif self.is_text:
s = self.obj.CharStyleName
return s
@style.setter
def style(self, value):
if self.is_paragraph:
self.obj.ParaStyleName = value
elif self.is_text:
self.obj.CharStyleName = value
@property
def is_paragraph(self):
return self._is_paragraph
@property
def is_table(self):
return self._is_table
@property
def is_text(self):
return self._is_text
@property
def is_section(self):
return self._is_section
@property
def text_cursor(self):
return self.text.createTextCursor()
@property
def dp(self):
return self._doc.dp
@property
def paragraph(self):
cursor = self.cursor
cursor.gotoStartOfParagraph(False)
cursor.gotoNextParagraph(True)
return LOWriterTextRange(cursor, self._doc)
def goto_start(self):
if self.is_section:
rango = self.obj.TextSection.Anchor.Start
else:
rango = self.obj.Start
return LOWriterTextRange(rango, self._doc)
def goto_end(self):
if self.is_section:
rango = self.obj.TextSection.Anchor.End
else:
rango = self.obj.End
return LOWriterTextRange(rango, self._doc)
def goto_previous(self, expand=True):
cursor = self.cursor
cursor.gotoPreviousParagraph(expand)
return LOWriterTextRange(cursor, self._doc)
def goto_next(self, expand=True):
cursor = self.cursor
cursor.gotoNextParagraph(expand)
return LOWriterTextRange(cursor, self._doc)
def go_left(self, from_self=True, count=1, expand=False):
cursor = self.cursor
if not from_self:
cursor = self.text_cursor
cursor.gotoRange(self.obj, False)
cursor.goLeft(count, expand)
return LOWriterTextRange(cursor, self._doc)
def go_right(self, from_self=True, count=1, expand=False):
cursor = self.cursor
if not from_self:
cursor = self.text_cursor
cursor.gotoRange(self.obj, False)
cursor.goRight(count, expand)
return LOWriterTextRange(cursor, self._doc)
def delete(self):
self.value = ''
return
def offset(self):
cursor = self.cursor.getEnd()
return LOWriterTextRange(cursor, self._doc)
def insert_content(self, data, cursor=None, replace=False):
if cursor is None:
cursor = self.cursor
self.text.insertTextContent(cursor, data, replace)
return
def insert_math(self, formula,
anchor_type=TextContentAnchorType.AS_CHARACTER,
cursor=None, replace=False):
math = self._doc.create_instance(SERVICES['TEXT_EMBEDDED'])
math.CLSID = CLSID['FORMULA']
math.AnchorType = anchor_type
self.insert_content(math, cursor, replace)
math.EmbeddedObject.Component.Formula = formula
return math
def new_line(self, count=1):
cursor = self.cursor
for i in range(count):
self.text.insertControlCharacter(cursor, PARAGRAPH_BREAK, False)
return LOWriterTextRange(cursor, self._doc)
def insert_table(self, data):
table = self._doc.create_instance(SERVICES['TEXT_TABLE'])
rows = len(data)
cols = len(data[0])
table.initialize(rows, cols)
self.insert_content(table)
table.DataArray = data
name = table.Name
table = LOWriterTextTable(self._doc.tables[name], self._doc)
return table
def insert_shape(self, tipo, args={}):
# ~ args['Width'] = args.get('Width', 1000)
# ~ args['Height'] = args.get('Height', 1000)
# ~ args['X'] = args.get('X', 0)
# ~ args['Y'] = args.get('Y', 0)
shape = self._doc.dp.add(tipo, args)
# ~ shape.anchor = self.obj
return shape
def insert_image(self, path, args={}):
w = args.get('Width', 1000)
h = args.get('Height', 1000)
image = self._doc.create_instance(SERVICES['GRAPHIC'])
image.GraphicURL = _P.to_url(path)
image.AnchorType = TextContentAnchorType.AS_CHARACTER
image.Width = w
image.Height = h
self.insert_content(image)
return self._doc.dp.last
class LOWriterTextRanges(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
self._paragraphs = [LOWriterTextRange(p, doc) for p in obj]
def __len__(self):
return len(self._paragraphs)
def __getitem__(self, index):
return self._paragraphs[index]
def __iter__(self):
self._index = 0
return self
def __next__(self):
try:
obj = self._paragraphs[self._index]
except IndexError:
raise StopIteration
self._index += 1
return obj
@property
def obj(self):
return self._obj
class LOWriterTextTable(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
@property
def obj(self):
return self._obj
@property
def name(self):
return self._obj.Name
@property
def data(self):
return self.obj.DataArray
@data.setter
def data(self, values):
self.obj.DataArray = values
@property
def style(self):
return self.obj.TableTemplateName
@style.setter
def style(self, value):
self.obj.autoFormat(value)
class LOWriterTextTables(object):
def __init__(self, doc):
self._doc = doc
self._obj = doc.obj.TextTables
def __getitem__(self, key):
return LOWriterTextTable(self._obj[key], self._doc)
def __len__(self):
return self._obj.Count
def insert(self, data, text_range=None):
if text_range is None:
text_range = self._doc.selection
text_range.insert_table(data)
return
class LOWriter(LODocument):
def __init__(self, obj):
super().__init__(obj)
self._type = WRITER
self._settings = self._cc.ViewSettings
@property
def text(self):
return self.paragraphs
@property
def paragraphs(self):
return LOWriterTextRanges(self.obj.Text, self)
@property
def tables(self):
return LOWriterTextTables(self)
@property
def selection(self):
sel = self.obj.CurrentSelection
if sel.ImplementationName == OBJ_TEXTS:
if len(sel) == 1:
sel = LOWriterTextRanges(sel, self)[0]
else:
sel = LOWriterTextRanges(sel, self)
return sel
if sel.ImplementationName == OBJ_SHAPES:
if len(sel) == 1:
sel = sel[0]
sel = LODrawPage(sel.Parent)[sel.Name]
return sel
if sel.ImplementationName == OBJ_GRAPHIC:
sel = self.dp[sel.Name]
else:
debug(sel.ImplementationName)
return sel
@property
def dp(self):
return self.draw_page
@property
def shapes(self):
return self.draw_page
@property
def draw_page(self):
return LODrawPage(self.obj.DrawPage)
@property
def cursor(self):
return self.obj.Text.createTextCursor()
@property
def view_cursor(self):
return self._cc.ViewCursor
@property
def page_styles(self):
ps = self.obj.StyleFamilies['PageStyles']
return LOWriterPageStyles(ps)
@property
def styles(self):
return LOWriterStylesFamilies(self.obj.StyleFamilies)
@property
def search_descriptor(self):
return self.obj.createSearchDescriptor()
@property
def replace_descriptor(self):
return self.obj.createReplaceDescriptor()
@property
def view_web(self):
return self._settings.ShowOnlineLayout
@view_web.setter
def view_web(self, value):
self._settings.ShowOnlineLayout = value
def goto_start(self):
self.view_cursor.gotoStart(False)
return self.selection
def goto_end(self):
self.view_cursor.gotoEnd(False)
return self.selection
def search(self, options, find_all=True):
descriptor = self.search_descriptor
descriptor.setSearchString(options.get('Search', ''))
descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
descriptor.SearchWords = options.get('Words', False)
if 'Attributes' in options:
attr = dict_to_property(options['Attributes'])
descriptor.setSearchAttributes(attr)
if hasattr(descriptor, 'SearchRegularExpression'):
descriptor.SearchRegularExpression = options.get('RegularExpression', False)
if hasattr(descriptor, 'SearchType') and 'Type' in options:
descriptor.SearchType = options['Type']
result = False
if find_all:
found = self.obj.findAll(descriptor)
if len(found):
result = [LOWriterTextRange(f, self) for f in found]
else:
found = self.obj.findFirst(descriptor)
if found:
result = LOWriterTextRange(found, self)
return result
def replace(self, options):
descriptor = self.replace_descriptor
descriptor.setSearchString(options['Search'])
descriptor.setReplaceString(options['Replace'])
descriptor.SearchCaseSensitive = options.get('CaseSensitive', False)
descriptor.SearchWords = options.get('Words', False)
if 'Attributes' in options:
attr = dict_to_property(options['Attributes'])
descriptor.setSearchAttributes(attr)
if hasattr(descriptor, 'SearchRegularExpression'):
descriptor.SearchRegularExpression = options.get('RegularExpression', False)
if hasattr(descriptor, 'SearchType') and 'Type' in options:
descriptor.SearchType = options['Type']
found = self.obj.replaceAll(descriptor)
return found
def select(self, text):
if hasattr(text, 'obj'):
text = text.obj
self._cc.select(text)
return
class LOShape(LOBaseObject):
@property
def cell(self):
return self.anchor
@property
def anchor(self):
obj = self.obj.Anchor
if obj.ImplementationName == OBJ_CELL:
obj = LOCalcRange(obj)
elif obj.ImplementationName == OBJ_TEXT:
obj = LOWriterTextRange(obj, LODocs().active)
else:
debug('Anchor', obj.ImplementationName)
return obj
@anchor.setter
def anchor(self, value):
if hasattr(value, 'obj'):
value = value.obj
try:
self.obj.Anchor = value
except Exception as e:
self.obj.AnchorType = value
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.Visible = value
@property
def path(self):
return self.url
@property
def url(self):
url = ''
if self.is_image:
url = _P.to_system(self.obj.GraphicURL.OriginURL)
return url
@property
def mimetype(self):
mt = ''
if self.is_image:
mt = self.obj.GraphicURL.MimeType
return mt
@property
def linked(self):
l = False
if self.is_image:
l = self.obj.GraphicURL.Linked
return l
def delete(self):
self.remove()
return
def remove(self):
self.obj.Parent.remove(self.obj)
return
def save(self, path: str, mimetype=DEFAULT_MIME_TYPE):
if _P.is_dir(path):
name = self.name
ext = mimetype.lower()
else:
p = _P(path)
path = p.path
name = p.name
ext = p.ext.lower()
path = _P.join(path, f'{name}.{ext}')
args = dict(
URL = _P.to_url(path),
MimeType = MIME_TYPE[ext],
)
if not _export_image(self.obj, args):
path = ''
return path
# ~ def save2(self, path: str):
# ~ size = len(self.obj.Bitmap.DIB)
# ~ data = self.obj.GraphicStream.readBytes((), size)
# ~ data = data[-1].value
# ~ path = _P.join(path, f'{self.name}.png')
# ~ _P.save_bin(path, b'')
# ~ return
class LODrawPage(LOBaseObject):
def __init__(self, obj):
super().__init__(obj)
def __getitem__(self, index):
if isinstance(index, int):
shape = LOShape(self.obj[index], index)
else:
for i, o in enumerate(self.obj):
shape = self.obj[i]
name = shape.Name or f'shape{i}'
if name == index:
shape = LOShape(shape, i)
break
return shape
def __iter__(self):
self._index = 0
return self
def __next__(self):
if self._index == self.count:
raise StopIteration
shape = self[self._index]
self._index += 1
return shape
@property
def name(self):
return self.obj.Name
@property
def doc(self):
return self.obj.Forms.Parent
@property
def width(self):
return self.obj.Width
@property
def height(self):
return self.obj.Height
@property
def count(self):
return self.obj.Count
@property
def last(self):
return self[self.count - 1]
def create_instance(self, name):
return self.doc.createInstance(name)
def add(self, type_shape, options={}):
args = options.copy()
"""Insert a shape in page, type shapes:
Line
Rectangle
Ellipse
Text
Connector
"""
index = self.count
default_height = 3000
if type_shape == 'Line':
default_height = 0
w = args.pop('Width', 3000)
h = args.pop('Height', default_height)
x = args.pop('X', 1000)
y = args.pop('Y', 1000)
name = args.pop('Name', f'{type_shape.lower()}{index}')
service = f'com.sun.star.drawing.{type_shape}Shape'
shape = self.create_instance(service)
shape.Size = Size(w, h)
shape.Position = Point(x, y)
shape.Name = name
self.obj.add(shape)
if args:
_set_properties(shape, args)
return LOShape(self.obj[index], index)
def remove(self, shape):
if hasattr(shape, 'obj'):
shape = shape.obj
return self.obj.remove(shape)
def remove_all(self):
while self.count:
self.obj.remove(self.obj[0])
return
def insert_image(self, path, options={}):
args = options.copy()
index = self.count
w = args.get('Width', 3000)
h = args.get('Height', 3000)
x = args.get('X', 1000)
y = args.get('Y', 1000)
name = args.get('Name', f'image{index}')
image = self.create_instance('com.sun.star.drawing.GraphicObjectShape')
if isinstance(path, str):
image.GraphicURL = _P.to_url(path)
else:
gp = create_instance('com.sun.star.graphic.GraphicProvider')
properties = dict_to_property({'InputStream': path})
image.Graphic = gp.queryGraphic(properties)
self.obj.add(image)
image.Size = Size(w, h)
image.Position = Point(x, y)
image.Name = name
return LOShape(self.obj[index], index)
class LODrawImpress(LODocument):
def __init__(self, obj):
super().__init__(obj)
def __getitem__(self, index):
if isinstance(index, int):
page = self.obj.DrawPages[index]
else:
page = self.obj.DrawPages.getByName(index)
return LODrawPage(page)
@property
def selection(self):
sel = self.obj.CurrentSelection[0]
# ~ return _get_class_uno(sel)
return sel
@property
def current_page(self):
return LODrawPage(self._cc.getCurrentPage())
def paste(self):
call_dispatch(self.frame, '.uno:Paste')
return self.current_page[-1]
def add(self, type_shape, args={}):
return self.current_page.add(type_shape, args)
def insert_image(self, path, args={}):
self.current_page.insert_image(path, args)
return
# ~ def export(self, path, mimetype='png'):
# ~ args = dict(
# ~ URL = _P.to_url(path),
# ~ MimeType = MIME_TYPE[mimetype],
# ~ )
# ~ result = _export_image(self.obj, args)
# ~ return result
class BaseRow:
pass
class BaseQuery(object):
PY_TYPES = {
'VARCHAR': 'getString',
'INTEGER': 'getLong',
'DATE': 'getDate',
# ~ 'SQL_LONG': 'getLong',
# ~ 'SQL_VARYING': 'getString',
# ~ 'SQL_FLOAT': 'getFloat',
# ~ 'SQL_BOOLEAN': 'getBoolean',
# ~ 'SQL_TYPE_DATE': 'getDate',
# ~ 'SQL_TYPE_TIME': 'getTime',
# ~ 'SQL_TIMESTAMP': 'getTimestamp',
}
# ~ TYPES_DATE = ('SQL_TYPE_DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
TYPES_DATE = ('DATE', 'SQL_TYPE_TIME', 'SQL_TIMESTAMP')
def __init__(self, query):
self._query = query
self._meta = query.MetaData
self._cols = self._meta.ColumnCount
self._names = query.Columns.ElementNames
self._data = self._get_data()
def __getitem__(self, index):
return self._data[index]
def __iter__(self):
self._index = 0
return self
def __next__(self):
try:
row = self._data[self._index]
except IndexError:
raise StopIteration
self._index += 1
return row
def _to_python(self, index):
type_field = self._meta.getColumnTypeName(index)
# ~ print('TF', type_field)
value = getattr(self._query, self.PY_TYPES[type_field])(index)
if type_field in self.TYPES_DATE:
value = _struct_to_date(value)
return value
def _get_row(self):
row = BaseRow()
for i in range(1, self._cols + 1):
column_name = self._meta.getColumnName(i)
value = self._to_python(i)
setattr(row, column_name, value)
return row
def _get_data(self):
data = []
while self._query.next():
row = self._get_row()
data.append(row)
return data
@property
def tuples(self):
data = [tuple(r.__dict__.values()) for r in self._data]
return tuple(data)
@property
def dicts(self):
data = [r.__dict__ for r in self._data]
return tuple(data)
def _add_listeners(events, control, name=''):
listeners = {
'addActionListener': EventsButton,
'addMouseListener': EventsMouse,
'addFocusListener': EventsFocus,
'addItemListener': EventsItem,
'addKeyListener': EventsKey,
'addTabListener': EventsTab,
'addSpinListener': EventsSpin,
}
if hasattr(control, 'obj'):
control = control.obj
# ~ debug(control.ImplementationName)
is_grid = control.ImplementationName == 'stardiv.Toolkit.GridControl'
is_link = control.ImplementationName == 'stardiv.Toolkit.UnoFixedHyperlinkControl'
is_roadmap = control.ImplementationName == 'stardiv.Toolkit.UnoRoadmapControl'
is_pages = control.ImplementationName == 'stardiv.Toolkit.UnoMultiPageControl'
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 EventsSpin(EventsListenerBase, XSpinListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def up(self, event):
event_name = f'{self.name}_up'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def down(self, event):
event_name = f'{self.name}_up'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def first(self, event):
event_name = f'{self.name}_first'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def last(self, event):
event_name = f'{self.name}_last'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsMouse(EventsListenerBase, XMouseListener, XMouseMotionListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def mousePressed(self, event):
event_name = '{}_click'.format(self._name)
if event.ClickCount == 2:
event_name = '{}_double_click'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def mouseReleased(self, event):
event_name = '{}_after_click'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def mouseEntered(self, event):
pass
def mouseExited(self, event):
pass
# ~ XMouseMotionListener
def mouseMoved(self, event):
pass
def mouseDragged(self, event):
pass
class EventsMouseLink(EventsMouse):
def __init__(self, controller, name):
super().__init__(controller, name)
self._text_color = 0
def mouseEntered(self, event):
model = event.Source.Model
self._text_color = model.TextColor or 0
model.TextColor = get_color('blue')
return
def mouseExited(self, event):
model = event.Source.Model
model.TextColor = self._text_color
return
class EventsButton(EventsListenerBase, XActionListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def actionPerformed(self, event):
event_name = f'{self.name}_action'
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
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
class EventsKey(EventsListenerBase, XKeyListener):
"""
event.KeyChar
event.KeyCode
event.KeyFunc
event.Modifiers
"""
def __init__(self, controller, name):
super().__init__(controller, name)
def keyPressed(self, event):
pass
def keyReleased(self, event):
event_name = '{}_key_released'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
# ~ else:
# ~ if event.KeyFunc == QUIT and hasattr(self._cls, 'close'):
# ~ self._cls.close()
return
class EventsItem(EventsListenerBase, XItemListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def disposing(self, event):
pass
def itemStateChanged(self, event):
event_name = '{}_item_changed'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsItemRoadmap(EventsItem):
def itemStateChanged(self, event):
dialog = event.Source.Context.Model
dialog.Step = event.ItemId + 1
return
class EventsGrid(EventsListenerBase, XGridDataListener, XGridSelectionListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def dataChanged(self, event):
event_name = '{}_data_changed'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def rowHeadingChanged(self, event):
pass
def rowsInserted(self, event):
pass
def rowsRemoved(self, evemt):
pass
def selectionChanged(self, event):
event_name = '{}_selection_changed'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
class EventsMouseGrid(EventsMouse):
selected = False
def mousePressed(self, event):
super().mousePressed(event)
# ~ obj = event.Source
# ~ col = obj.getColumnAtPoint(event.X, event.Y)
# ~ row = obj.getRowAtPoint(event.X, event.Y)
# ~ print(col, row)
# ~ if col == -1 and row == -1:
# ~ if self.selected:
# ~ obj.deselectAllRows()
# ~ else:
# ~ obj.selectAllRows()
# ~ self.selected = not self.selected
return
def mouseReleased(self, event):
# ~ obj = event.Source
# ~ col = obj.getColumnAtPoint(event.X, event.Y)
# ~ row = obj.getRowAtPoint(event.X, event.Y)
# ~ if row == -1 and col > -1:
# ~ gdm = obj.Model.GridDataModel
# ~ for i in range(gdm.RowCount):
# ~ gdm.updateRowHeading(i, i + 1)
return
class EventsTab(EventsListenerBase, XTabListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def activated(self, id):
event_name = '{}_activated'.format(self.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(id)
return
class EventsMenu(EventsListenerBase, XMenuListener):
def __init__(self, controller):
super().__init__(controller, '')
def itemHighlighted(self, event):
pass
def itemSelected(self, event):
name = event.Source.getCommand(event.MenuId)
if name.startswith('menu'):
event_name = '{}_selected'.format(name)
else:
event_name = 'menu_{}_selected'.format(name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def itemActivated(self, event):
return
def itemDeactivated(self, event):
return
class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener):
def __init__(self, cls):
self._cls = cls
super().__init__(cls.events, cls.name, cls._window)
def windowOpened(self, event):
event_name = '{}_opened'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def windowActivated(self, event):
control_name = '{}_activated'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
def windowDeactivated(self, event):
control_name = '{}_deactivated'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
def windowMinimized(self, event):
pass
def windowNormalized(self, event):
pass
def windowClosing(self, event):
if self._window:
control_name = 'window_closing'
else:
control_name = '{}_closing'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
# ~ else:
# ~ if not self._modal and not self._block:
# ~ event.Source.Visible = False
return
def windowClosed(self, event):
control_name = '{}_closed'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
# ~ XWindowListener
def windowResized(self, event):
sb = self._cls._subcont
sb.setPosSize(0, 0, event.Width, event.Height, SIZE)
event_name = '{}_resized'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def windowMoved(self, event):
pass
def windowShown(self, event):
pass
def windowHidden(self, event):
pass
# ~ BorderColor = ?
# ~ FontStyleName = ?
# ~ HelpURL = ?
class UnoBaseObject(object):
def __init__(self, obj, path=''):
self._obj = obj
self._model = obj.Model
def __setattr__(self, name, value):
exists = hasattr(self, name)
if not exists and not name in ('_obj', '_model'):
setattr(self._model, name, value)
else:
super().__setattr__(name, value)
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):
return {}
@properties.setter
def properties(self, values):
_set_properties(self.model, values)
@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
class UnoLabelLink(UnoLabel):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'link'
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 = _P.to_url(value)
class UnoRadio(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'radio'
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
class UnoCheckBox(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'checkbox'
@property
def value(self):
return self.model.State
@value.setter
def value(self, value):
self.model.State = value
@property
def label(self):
return self.model.Label
@label.setter
def label(self, value):
self.model.Label = value
@property
def tri_state(self):
return self.model.TriState
@tri_state.setter
def tri_state(self, 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
@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 UnoImage(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'image'
@property
def value(self):
return self.url
@value.setter
def value(self, value):
self.url = value
@property
def url(self):
return self.m.ImageURL
@url.setter
def url(self, value):
self.m.ImageURL = None
self.m.ImageURL = _P.to_url(value)
class UnoListBox(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._path = ''
def __setattr__(self, name, value):
if name in ('_path',):
self.__dict__[name] = value
else:
super().__setattr__(name, value)
@property
def type(self):
return 'listbox'
@property
def value(self):
return self.obj.getSelectedItem()
@property
def count(self):
return len(self.data)
@property
def data(self):
return self.model.StringItemList
@data.setter
def data(self, values):
self.model.StringItemList = list(sorted(values))
@property
def path(self):
return self._path
@path.setter
def path(self, value):
self._path = value
def unselect(self):
self.obj.selectItem(self.value, False)
return
def select(self, pos=0):
if isinstance(pos, str):
self.obj.selectItem(pos, True)
else:
self.obj.selectItemPos(pos, True)
return
def clear(self):
self.model.removeAllItems()
return
def _set_image_url(self, image):
if _P.exists(image):
return _P.to_url(image)
path = _P.join(self._path, DIR['images'], image)
return _P.to_url(path)
def insert(self, value, path='', pos=-1, show=True):
if pos < 0:
pos = self.count
if path:
self.model.insertItem(pos, value, self._set_image_url(path))
else:
self.model.insertItemText(pos, value)
if show:
self.select(pos)
return
class UnoRoadmap(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._options = ()
def __setattr__(self, name, value):
if name in ('_options',):
self.__dict__[name] = value
else:
super().__setattr__(name, value)
@property
def options(self):
return self._options
@options.setter
def options(self, values):
self._options = values
for i, v in enumerate(values):
opt = self.model.createInstance()
opt.ID = i
opt.Label = v
self.model.insertByIndex(i, opt)
return
@property
def enabled(self):
return True
@enabled.setter
def enabled(self, value):
for m in self.model:
m.Enabled = value
return
def set_enabled(self, index, value):
self.model.getByIndex(index).Enabled = value
return
class UnoTree(UnoBaseObject):
def __init__(self, obj, ):
super().__init__(obj)
self._tdm = None
self._data = []
def __setattr__(self, name, value):
if name in ('_tdm', '_data'):
self.__dict__[name] = value
else:
super().__setattr__(name, value)
@property
def selection(self):
sel = self.obj.Selection
return sel.DataValue, sel.DisplayValue
@property
def parent(self):
parent = self.obj.Selection.Parent
if parent is None:
return ()
return parent.DataValue, parent.DisplayValue
def _get_parents(self, node):
value = (node.DisplayValue,)
parent = node.Parent
if parent is None:
return value
return self._get_parents(parent) + value
@property
def parents(self):
values = self._get_parents(self.obj.Selection)
return values
@property
def root(self):
if self._tdm is None:
return ''
return self._tdm.Root.DisplayValue
@root.setter
def root(self, value):
self._add_data_model(value)
def _add_data_model(self, name):
tdm = create_instance('com.sun.star.awt.tree.MutableTreeDataModel')
root = tdm.createNode(name, True)
root.DataValue = 0
tdm.setRoot(root)
self.model.DataModel = tdm
self._tdm = self.model.DataModel
return
@property
def path(self):
return self.root
@path.setter
def path(self, value):
self.data = _P.walk_dir(value, True)
@property
def data(self):
return self._data
@data.setter
def data(self, values):
self._data = list(values)
self._add_data()
def _add_data(self):
if not self.data:
return
parents = {}
for node in self.data:
parent = parents.get(node[1], self._tdm.Root)
child = self._tdm.createNode(node[2], False)
child.DataValue = node[0]
parent.appendChild(child)
parents[node[0]] = child
self.obj.expandNode(self._tdm.Root)
return
# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1awt_1_1grid.html
class UnoGrid(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._gdm = self.model.GridDataModel
self._data = []
self._formats = ()
def __setattr__(self, name, value):
if name in ('_gdm', '_data', '_formats'):
self.__dict__[name] = value
else:
super().__setattr__(name, value)
def __getitem__(self, key):
value = self._gdm.getCellData(key[0], key[1])
return value
def __setitem__(self, key, value):
self._gdm.updateCellData(key[0], key[1], value)
return
@property
def type(self):
return 'grid'
@property
def columns(self):
return {}
@columns.setter
def columns(self, values):
# ~ self._columns = values
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True)
for properties in values:
column = create_instance('com.sun.star.awt.grid.GridColumn', True)
for k, v in properties.items():
setattr(column, k, v)
model.addColumn(column)
self.model.ColumnModel = model
return
@property
def data(self):
return self._data
@data.setter
def data(self, values):
self._data = values
self.clear()
headings = tuple(range(1, len(values) + 1))
self._gdm.addRows(headings, values)
# ~ rows = range(grid_dm.RowCount)
# ~ colors = [COLORS['GRAY'] if r % 2 else COLORS['WHITE'] for r in rows]
# ~ grid.Model.RowBackgroundColors = tuple(colors)
return
@property
def value(self):
if self.column == -1 or self.row == -1:
return ''
return self[self.column, self.row]
@value.setter
def value(self, value):
if self.column > -1 and self.row > -1:
self[self.column, self.row] = value
@property
def row(self):
return self.obj.CurrentRow
@property
def row_count(self):
return self._gdm.RowCount
@property
def column(self):
return self.obj.CurrentColumn
@property
def is_valid(self):
return not (self.row == -1 or self.column == -1)
@property
def selected_rows(self):
value = self.obj.SelectedRows
return value
@property
def row_background_colors(self):
value = self.m.RowBackgroundColors
return value
@row_background_colors.setter
def row_background_colors(self, colors):
c = Color()
self.m.RowBackgroundColors = (c(colors[0]), c(colors[1]))
@property
def formats(self):
return self._formats
@formats.setter
def formats(self, values):
self._formats = values
def clear(self):
self._gdm.removeAllRows()
return
def _format_columns(self, data):
row = data
if self.formats:
for i, f in enumerate(formats):
if f:
row[i] = f.format(data[i])
return row
def add_row(self, data):
self._data.append(data)
row = self._format_columns(data)
self._gdm.addRow(self.row_count + 1, row)
return
def set_cell_tooltip(self, col, row, value):
self._gdm.updateCellToolTip(col, row, value)
return
def get_cell_tooltip(self, col, row):
value = self._gdm.getCellToolTip(col, row)
return value
def sort(self, column, asc=True):
self._gdm.sortByColumn(column, asc)
self.update_row_heading()
return
def update_row_heading(self):
for i in range(self.row_count):
self._gdm.updateRowHeading(i, i + 1)
return
def remove_row(self, row):
self._gdm.removeRow(row)
del self._data[row]
self.update_row_heading()
return
class UnoPage(object):
def __init__(self, obj):
self._obj = obj
self._events = None
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._obj.Model
# ~ @property
# ~ def id(self):
# ~ return self.m.TabPageID
@property
def parent(self):
return self.obj.Context
def _set_image_url(self, image):
if _P.exists(image):
return _P.to_url(image)
path = _P.join(self._path, DIR['images'], image)
return _P.to_url(path)
def _special_properties(self, tipo, args):
if tipo == 'link' and not 'Label' in args:
args['Label'] = args['URL']
return args
if tipo == 'button':
if 'ImageURL' in args:
args['ImageURL'] = self._set_image_url(args['ImageURL'])
args['FocusOnClick'] = args.get('FocusOnClick', False)
return args
if tipo == 'roadmap':
args['Height'] = args.get('Height', self.height)
if 'Title' in args:
args['Text'] = args.pop('Title')
return args
if tipo == 'tree':
args['SelectionType'] = args.get('SelectionType', SINGLE)
return args
if tipo == 'grid':
args['ShowRowHeader'] = args.get('ShowRowHeader', True)
return args
if tipo == 'pages':
args['Width'] = args.get('Width', self.width)
args['Height'] = args.get('Height', self.height)
return args
def add_control(self, args):
tipo = args.pop('Type').lower()
root = args.pop('Root', '')
sheets = args.pop('Sheets', ())
columns = args.pop('Columns', ())
args = self._special_properties(tipo, args)
model = self.model.createInstance(UNO_MODELS[tipo])
_set_properties(model, args)
name = args['Name']
self.model.insertByName(name, model)
control = self.obj.getControl(name)
_add_listeners(self._events, control, name)
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)
return control
class UnoPages(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._sheets = []
self._events = None
def __setattr__(self, name, value):
if name in ('_sheets', '_events'):
self.__dict__[name] = value
else:
super().__setattr__(name, value)
def __getitem__(self, index):
name = index
if isinstance(index, int):
name = f'sheet{index}'
sheet = self.obj.getControl(name)
page = UnoPage(sheet)
page._events = self._events
return page
@property
def type(self):
return 'pages'
@property
def current(self):
return self.obj.ActiveTabID
@property
def active(self):
return self.current
@property
def sheets(self):
return self._sheets
@sheets.setter
def sheets(self, values):
self._sheets = values
for i, title in enumerate(values):
sheet = self.m.createInstance('com.sun.star.awt.UnoPageModel')
sheet.Title = title
self.m.insertByName(f'sheet{i + 1}', sheet)
return
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.Visible = value
def insert(self, title):
self._sheets.append(title)
id = len(self._sheets)
sheet = self.m.createInstance('com.sun.star.awt.UnoPageModel')
sheet.Title = title
self.m.insertByName(f'sheet{id}', sheet)
return self[id]
def remove(self, id):
self.obj.removeTab(id)
return
def activate(self, id):
self.obj.activateTab(id)
return
class UnoSpinButton(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'spinbutton'
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
class UnoNumericField(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'numeric'
@property
def int(self):
return int(self.value)
@property
def value(self):
return self.model.Value
@value.setter
def value(self, value):
self.model.Value = value
UNO_CLASSES = {
'label': UnoLabel,
'link': UnoLabelLink,
'button': UnoButton,
'radio': UnoRadio,
'checkbox': UnoCheckBox,
'text': UnoText,
'image': UnoImage,
'listbox': UnoListBox,
'roadmap': UnoRoadmap,
'tree': UnoTree,
'grid': UnoGrid,
'pages': UnoPages,
'spinbutton': UnoSpinButton,
'numeric': UnoNumericField,
}
UNO_MODELS = {
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'button': 'com.sun.star.awt.UnoControlButtonModel',
'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',
}
# ~ 'CurrencyField': 'com.sun.star.awt.UnoControlCurrencyFieldModel',
# ~ 'DateField': 'com.sun.star.awt.UnoControlDateFieldModel',
# ~ 'FileControl': 'com.sun.star.awt.UnoControlFileControlModel',
# ~ 'FormattedField': 'com.sun.star.awt.UnoControlFormattedFieldModel',
# ~ 'PatternField': 'com.sun.star.awt.UnoControlPatternFieldModel',
# ~ 'ProgressBar': 'com.sun.star.awt.UnoControlProgressBarModel',
# ~ 'ScrollBar': 'com.sun.star.awt.UnoControlScrollBarModel',
# ~ 'SimpleAnimation': 'com.sun.star.awt.UnoControlSimpleAnimationModel',
# ~ 'Throbber': 'com.sun.star.awt.UnoControlThrobberModel',
# ~ 'TimeField': 'com.sun.star.awt.UnoControlTimeFieldModel',
class LODialog(object):
SEPARATION = 5
MODELS = {
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'button': 'com.sun.star.awt.UnoControlButtonModel',
'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',
}
def __init__(self, args={}):
self._obj = self._create(args)
self._model = self.obj.Model
self._events = None
self._modal = True
self._controls = {}
self._color_on_focus = COLOR_ON_FOCUS
self._id = ''
self._path = ''
self._init_controls()
def _create(self, args):
service = 'com.sun.star.awt.DialogProvider'
path = args.pop('Path', '')
if path:
dp = create_instance(service, True)
dlg = dp.createDialog(_P.to_url(path))
return dlg
if 'Location' in args:
name = args['Name']
library = args.get('Library', 'Standard')
location = args.get('Location', 'application').lower()
if location == 'user':
location = 'application'
url = f'vnd.sun.star.script:{library}.{name}?location={location}'
if location == 'document':
dp = create_instance(service, args=docs.active.obj)
else:
dp = create_instance(service, True)
# ~ uid = docs.active.uid
# ~ url = f'vnd.sun.star.tdoc:/{uid}/Dialogs/{library}/{name}.xml'
dlg = dp.createDialog(url)
return dlg
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)
args['Title'] = args.get('Title', TITLE)
args['Width'] = args.get('Width', 200)
args['Height'] = args.get('Height', 150)
_set_properties(model, args)
dlg.setModel(model)
dlg.setVisible(False)
dlg.createPeer(toolkit, None)
return dlg
def _get_type_control(self, name):
name = name.split('.')[2]
types = {
'UnoFixedTextControl': 'label',
'UnoEditControl': 'text',
'UnoButtonControl': 'button',
}
return types[name]
def _init_controls(self):
for control in self.obj.getControls():
tipo = self._get_type_control(control.ImplementationName)
name = control.Model.Name
control = UNO_CLASSES[tipo](control)
setattr(self, name, control)
return
@property
def obj(self):
return self._obj
@property
def model(self):
return self._model
@property
def controls(self):
return self._controls
@property
def path(self):
return self._path
@property
def path_images(self):
return _P.join(self.path, DIR['images'])
@property
def id(self):
return self._id
@id.setter
def id(self, value):
self._id = value
self._path = _P.from_id(value)
@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 events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers(self)
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):
for control in self.obj.Controls:
_add_listeners(self.events, control, control.Model.Name)
return
def _set_image_url(self, image):
if _P.exists(image):
return _P.to_url(image)
path = _P.join(self._path, DIR['images'], image)
return _P.to_url(path)
def _special_properties(self, tipo, args):
if tipo == 'link' and not 'Label' in args:
args['Label'] = args['URL']
return args
if tipo == 'button':
if 'ImageURL' in args:
args['ImageURL'] = self._set_image_url(args['ImageURL'])
args['FocusOnClick'] = args.get('FocusOnClick', False)
return args
if tipo == 'roadmap':
args['Height'] = args.get('Height', self.height)
if 'Title' in args:
args['Text'] = args.pop('Title')
return args
if tipo == 'tree':
args['SelectionType'] = args.get('SelectionType', SINGLE)
return args
if tipo == 'grid':
args['X'] = args.get('X', self.SEPARATION)
args['Y'] = args.get('Y', self.SEPARATION)
args['Width'] = args.get('Width', self.width - self.SEPARATION * 2)
args['Height'] = args.get('Height', self.height - self.SEPARATION * 2)
args['ShowRowHeader'] = args.get('ShowRowHeader', True)
return args
if tipo == 'pages':
args['Width'] = args.get('Width', self.width)
args['Height'] = args.get('Height', self.height)
return args
def add_control(self, args):
tipo = args.pop('Type').lower()
root = args.pop('Root', '')
sheets = args.pop('Sheets', ())
columns = args.pop('Columns', ())
args = self._special_properties(tipo, args)
model = self.model.createInstance(self.MODELS[tipo])
_set_properties(model, args)
name = args['Name']
self.model.insertByName(name, model)
control = self.obj.getControl(name)
_add_listeners(self.events, control, name)
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
def center(self, control, x=0, y=0):
w = self.width
h = self.height
if isinstance(control, tuple):
wt = self.SEPARATION * -1
for c in control:
wt += c.width + self.SEPARATION
x = w / 2 - wt / 2
for c in control:
c.x = x
x = c.x + c.width + self.SEPARATION
return
if x < 0:
x = w + x - control.width
elif x == 0:
x = w / 2 - control.width / 2
if y < 0:
y = h + y - control.height
elif y == 0:
y = h / 2 - control.height / 2
control.x = x
control.y = y
return
def open(self, modal=True):
self._modal = modal
if modal:
return self.obj.execute()
else:
self.visible = True
return
def close(self, value=0):
if self._modal:
value = self.obj.endDialog(value)
else:
self.visible = False
self.obj.dispose()
return value
def set_values(self, data):
for k, v in data.items():
self._controls[k].value = v
return
class LOCells(object):
def __getitem__(self, index):
return LODocs().active.active[index]
class LOWindow(object):
EMPTY = """<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE dlg:window PUBLIC "-//OpenOffice.org//DTD OfficeDocument 1.0//EN" "dialog.dtd">
<dlg:window xmlns:dlg="http://openoffice.org/2000/dialog" xmlns:script="http://openoffice.org/2000/script" dlg:id="empty" dlg:left="0" dlg:top="0" dlg:width="0" dlg:height="0" dlg:closeable="true" dlg:moveable="true" dlg:withtitlebar="false"/>"""
MODELS = {
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'button': 'com.sun.star.awt.UnoControlButtonModel',
'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',
}
def __init__(self, args):
self._events = None
self._menu = None
self._container = None
self._model = None
self._id = ''
self._path = ''
self._obj = self._create(args)
def _create(self, properties):
ps = (
properties.get('X', 0),
properties.get('Y', 0),
properties.get('Width', 500),
properties.get('Height', 500),
)
self._title = properties.get('Title', TITLE)
self._create_frame(ps)
self._create_container(ps)
self._create_subcontainer(ps)
# ~ self._create_splitter(ps)
return
def _create_frame(self, ps):
service = 'com.sun.star.frame.TaskCreator'
tc = create_instance(service, True)
self._frame = tc.createInstanceWithArguments((
NamedValue('FrameName', 'EasyMacroWin'),
NamedValue('PosSize', Rectangle(*ps)),
))
self._window = self._frame.getContainerWindow()
self._toolkit = self._window.getToolkit()
desktop = get_desktop()
self._frame.setCreator(desktop)
desktop.getFrames().append(self._frame)
self._frame.Title = self._title
return
def _create_container(self, ps):
service = 'com.sun.star.awt.UnoControlContainer'
self._container = create_instance(service, True)
service = 'com.sun.star.awt.UnoControlContainerModel'
model = create_instance(service, True)
model.BackgroundColor = get_color((225, 225, 225))
self._container.setModel(model)
self._container.createPeer(self._toolkit, self._window)
self._container.setPosSize(*ps, POSSIZE)
self._frame.setComponent(self._container, None)
return
def _create_subcontainer(self, ps):
service = 'com.sun.star.awt.ContainerWindowProvider'
cwp = create_instance(service, True)
path_tmp = _P.save_tmp(self.EMPTY)
subcont = cwp.createContainerWindow(
_P.to_url(path_tmp), '', self._container.getPeer(), None)
_P.kill(path_tmp)
subcont.setPosSize(0, 0, 500, 500, POSSIZE)
subcont.setVisible(True)
self._container.addControl('subcont', subcont)
self._subcont = subcont
self._model = subcont.Model
return
def _create_popupmenu(self, menus):
menu = create_instance('com.sun.star.awt.PopupMenu', True)
for i, m in enumerate(menus):
label = m['label']
cmd = m.get('event', '')
if not cmd:
cmd = label.lower().replace(' ', '_')
if label == '-':
menu.insertSeparator(i)
else:
menu.insertItem(i, label, m.get('style', 0), i)
menu.setCommand(i, cmd)
# ~ menu.setItemImage(i, path?, True)
menu.addMenuListener(EventsMenu(self.events))
return menu
def _create_menu(self, menus):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMenu.html
#~ nItemId specifies the ID of the menu item to be inserted.
#~ aText specifies the label of the menu item.
#~ nItemStyle 0 = Standard, CHECKABLE = 1, RADIOCHECK = 2, AUTOCHECK = 4
#~ nItemPos specifies the position where the menu item will be inserted.
self._menu = create_instance('com.sun.star.awt.MenuBar', True)
for i, m in enumerate(menus):
self._menu.insertItem(i, m['label'], m.get('style', 0), i)
cmd = m['label'].lower().replace(' ', '_')
self._menu.setCommand(i, cmd)
submenu = self._create_popupmenu(m['submenu'])
self._menu.setPopupMenu(i, submenu)
self._window.setMenuBar(self._menu)
return
def _add_listeners(self, control=None):
if self.events is None:
return
controller = EventsWindow(self)
self._window.addTopWindowListener(controller)
self._window.addWindowListener(controller)
# ~ self._container.addKeyListener(EventsKeyWindow(self))
return
def _set_image_url(self, image):
if _P.exists(image):
return _P.to_url(image)
path = _P.join(self._path, DIR['images'], image)
return _P.to_url(path)
def _special_properties(self, tipo, args):
if tipo == 'link' and not 'Label' in args:
args['Label'] = args['URL']
return args
if tipo == 'button':
if 'ImageURL' in args:
args['ImageURL'] = self._set_image_url(args['ImageURL'])
args['FocusOnClick'] = args.get('FocusOnClick', False)
return args
if tipo == 'roadmap':
args['Height'] = args.get('Height', self.height)
if 'Title' in args:
args['Text'] = args.pop('Title')
return args
if tipo == 'tree':
args['SelectionType'] = args.get('SelectionType', SINGLE)
return args
if tipo == 'grid':
args['ShowRowHeader'] = args.get('ShowRowHeader', True)
return args
if tipo == 'pages':
args['Width'] = args.get('Width', self.width)
args['Height'] = args.get('Height', self.height)
return args
def add_control(self, args):
tipo = args.pop('Type').lower()
root = args.pop('Root', '')
sheets = args.pop('Sheets', ())
columns = args.pop('Columns', ())
args = self._special_properties(tipo, args)
model = self.model.createInstance(self.MODELS[tipo])
_set_properties(model, args)
name = args['Name']
self.model.insertByName(name, model)
control = self._subcont.getControl(name)
_add_listeners(self.events, control, name)
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)
return control
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers(self)
self._add_listeners()
@property
def model(self):
return self._model
@property
def width(self):
return self._container.Size.Width
@property
def height(self):
return self._container.Size.Height
@property
def name(self):
return self._title.lower().replace(' ', '_')
def add_menu(self, menus):
self._create_menu(menus)
return
def open(self):
self._window.setVisible(True)
return
def close(self):
self._window.setMenuBar(None)
self._window.dispose()
self._frame.close(True)
return
class LODBServer(object):
DRIVERS = {
'mysql': 'mysqlc',
'mariadb': 'mysqlc',
'postgres': 'postgresql:postgresql',
}
PORTS = {
'mysql': 3306,
'mariadb': 3306,
'postgres': 5432,
}
def __init__(self):
self._conn = None
self._error = 'Not connected'
self._type = ''
self._drivers = []
def __str__(self):
return f'DB type {self._type}'
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.disconnet()
@property
def is_connected(self):
return not self._conn is None
@property
def error(self):
return self._error
@property
def drivers(self):
return self._drivers
def disconnet(self):
if not self._conn is None:
if not self._conn.isClosed():
self._conn.close()
self._conn.dispose()
return
def connect(self, options={}):
args = options.copy()
self._error = ''
self._type = args.get('type', 'postgres')
driver = self.DRIVERS[self._type]
server = args.get('server', 'localhost')
port = args.get('port', self.PORTS[self._type])
dbname = args.get('dbname', '')
user = args['user']
password = args['password']
data = {'user': user, 'password': password}
url = f'sdbc:{driver}:{server}:{port}/{dbname}'
# ~ https://downloads.mariadb.com/Connectors/java/
# ~ data['JavaDriverClass'] = 'org.mariadb.jdbc.Driver'
# ~ url = f'jdbc:mysql://{server}:{port}/{dbname}'
args = dict_to_property(data)
manager = create_instance('com.sun.star.sdbc.DriverManager')
self._drivers = [d.ImplementationName for d in manager]
try:
self._conn = manager.getConnectionWithInfo(url, args)
except Exception as e:
error(e)
self._error = str(e)
return self
def execute(self, sql):
query = self._conn.createStatement()
try:
query.execute(sql)
result = True
except Exception as e:
error(e)
self._error = str(e)
result = False
return result
def create_window(args):
return LOWindow(args)
class Paths(object):
@classmethod
def image(cls, path):
# ~ sfa = create_instance('com.sun.star.ucb.SimpleFileAccess')
# ~ stream = sfa.openFileRead(cls.to_url(path))
gp = create_instance('com.sun.star.graphic.GraphicProvider')
if isinstance(path, str):
properties = (PropertyValue(Name='URL', Value=cls.to_url(path)),)
else:
properties = (PropertyValue(Name='InputStream', Value=path),)
image = gp.queryGraphic(properties)
return image
class IOStream(object):
@classmethod
def qr(cls, data, **kwargs):
import segno
kwargs['kind'] = kwargs.get('kind', 'svg')
kwargs['scale'] = kwargs.get('scale', 8)
kwargs['border'] = kwargs.get('border', 2)
buffer = cls.buffer()
segno.make(data).save(buffer, **kwargs)
stream = cls.input(buffer)
return stream
class SpellChecker(object):
def __init__(self):
service = 'com.sun.star.linguistic2.SpellChecker'
self._spellchecker = create_instance(service, True)
self._locale = LOCALE
@property
def locale(self):
slocal = f'{self._locale.Language}-{self._locale.Country}'
return slocale
@locale.setter
def locale(self, value):
lang = value.split('-')
self._locale = Locale(lang[0], lang[1], '')
def is_valid(self, word):
result = self._spellchecker.isValid(word, self._locale, ())
return result
def spell(self, word):
result = self._spellchecker.spell(word, self._locale, ())
if result:
result = result.getAlternatives()
if not isinstance(result, tuple):
result = ()
return result
def spell(word, locale=''):
sc = SpellChecker()
if locale:
sc.locale = locale
return sc.spell(word)
def __getattr__(name):
if name == 'current_region':
return LODocs().active.selection.current_region
if name in ('rectangle', 'pos_size'):
return Rectangle()
if name == 'db':
return LODBServer()
if name == 'cells':
return LOCells()
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
def create_dialog(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 ''