2237 lines
56 KiB
Python
2237 lines
56 KiB
Python
|
#!/usr/bin/env python3
|
||
|
|
||
|
# == Rapid Develop Macros in LibreOffice ==
|
||
|
|
||
|
# ~ This file is part of ZAZ.
|
||
|
|
||
|
# ~ ZAZ is free software: you can redistribute it and/or modify
|
||
|
# ~ it under the terms of the GNU General Public License as published by
|
||
|
# ~ the Free Software Foundation, either version 3 of the License, or
|
||
|
# ~ (at your option) any later version.
|
||
|
|
||
|
# ~ ZAZ is distributed in the hope that it will be useful,
|
||
|
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# ~ GNU General Public License for more details.
|
||
|
|
||
|
# ~ You should have received a copy of the GNU General Public License
|
||
|
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
|
||
|
|
||
|
|
||
|
import datetime
|
||
|
import getpass
|
||
|
import logging
|
||
|
import os
|
||
|
import platform
|
||
|
import socket
|
||
|
import subprocess
|
||
|
import sys
|
||
|
import time
|
||
|
|
||
|
from enum import IntEnum
|
||
|
from functools import wraps
|
||
|
from pathlib import Path
|
||
|
from typing import Any
|
||
|
|
||
|
import uno
|
||
|
import unohelper
|
||
|
from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
|
||
|
from com.sun.star.awt.MessageBoxResults import YES
|
||
|
from com.sun.star.awt import Rectangle
|
||
|
from com.sun.star.beans import PropertyValue, NamedValue
|
||
|
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.awt import XActionListener
|
||
|
from com.sun.star.lang import XEventListener
|
||
|
from com.sun.star.awt import XMouseListener
|
||
|
from com.sun.star.awt import XMouseMotionListener
|
||
|
|
||
|
# ~ 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
|
||
|
|
||
|
try:
|
||
|
from peewee import Database, DateTimeField, DateField, TimeField, \
|
||
|
__exception_wrapper__
|
||
|
except ImportError as e:
|
||
|
print('Install peewee')
|
||
|
peewee = None
|
||
|
|
||
|
|
||
|
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__)
|
||
|
|
||
|
|
||
|
LEFT = 0
|
||
|
CENTER = 1
|
||
|
RIGHT = 2
|
||
|
|
||
|
CALC = 'calc'
|
||
|
WRITER = 'writer'
|
||
|
DRAW = 'draw'
|
||
|
IMPRESS = 'impress'
|
||
|
BASE = 'base'
|
||
|
MATH = 'math'
|
||
|
BASIC = 'basic'
|
||
|
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',
|
||
|
BASE: 'com.sun.star.sdb.DocumentDataSource',
|
||
|
MATH: 'com.sun.star.formula.FormulaProperties',
|
||
|
BASIC: 'com.sun.star.script.BasicIDE',
|
||
|
'main': 'com.sun.star.frame.StartModule',
|
||
|
}
|
||
|
|
||
|
OBJ_CELL = 'ScCellObj'
|
||
|
OBJ_RANGE = 'ScCellRangeObj'
|
||
|
OBJ_RANGES = 'ScCellRangesObj'
|
||
|
TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
|
||
|
|
||
|
# ~ 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
|
||
|
|
||
|
|
||
|
OS = platform.system()
|
||
|
IS_WIN = OS == 'Windows'
|
||
|
IS_MAC = OS == 'Darwin'
|
||
|
USER = getpass.getuser()
|
||
|
PC = platform.node()
|
||
|
DESKTOP = os.environ.get('DESKTOP_SESSION', '')
|
||
|
INFO_DEBUG = f"{sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path)
|
||
|
|
||
|
|
||
|
SECONDS_DAY = 60 * 60 * 24
|
||
|
|
||
|
|
||
|
CTX = uno.getComponentContext()
|
||
|
SM = CTX.getServiceManager()
|
||
|
|
||
|
|
||
|
def create_instance(name: str, with_context: bool=False, args: Any=None) -> Any:
|
||
|
if with_context:
|
||
|
instance = SM.createInstanceWithContext(name, CTX)
|
||
|
elif args:
|
||
|
instance = SM.createInstanceWithArguments(name, (args,))
|
||
|
else:
|
||
|
instance = SM.createInstance(name)
|
||
|
return instance
|
||
|
|
||
|
|
||
|
def get_app_config(node_name, key=''):
|
||
|
name = 'com.sun.star.configuration.ConfigurationProvider'
|
||
|
service = 'com.sun.star.configuration.ConfigurationAccess'
|
||
|
cp = create_instance(name, True)
|
||
|
node = PropertyValue(Name='nodepath', Value=node_name)
|
||
|
try:
|
||
|
ca = cp.createInstanceWithArguments(service, (node,))
|
||
|
if ca and not key:
|
||
|
return ca
|
||
|
if ca and ca.hasByName(key):
|
||
|
return ca.getPropertyValue(key)
|
||
|
except Exception as e:
|
||
|
error(e)
|
||
|
return ''
|
||
|
|
||
|
|
||
|
LANGUAGE = get_app_config('org.openoffice.Setup/L10N/', 'ooLocale')
|
||
|
LANG = LANGUAGE.split('-')[0]
|
||
|
NAME = TITLE = get_app_config('org.openoffice.Setup/Product', 'ooName')
|
||
|
VERSION = get_app_config('org.openoffice.Setup/Product','ooSetupVersion')
|
||
|
|
||
|
INFO_DEBUG = f"{NAME} v{VERSION} {LANGUAGE}\n\n{INFO_DEBUG}"
|
||
|
|
||
|
node = '/org.openoffice.Office.Calc/Calculate/Other/Date'
|
||
|
y = get_app_config(node, 'YY')
|
||
|
m = get_app_config(node, 'MM')
|
||
|
d = get_app_config(node, 'DD')
|
||
|
DATE_OFFSET = datetime.date(y, m, d).toordinal()
|
||
|
|
||
|
|
||
|
def debug(*args):
|
||
|
data = [str(a) for a in args]
|
||
|
log.debug('\t'.join(data))
|
||
|
return
|
||
|
|
||
|
|
||
|
def error(info):
|
||
|
log.error(info)
|
||
|
return
|
||
|
|
||
|
|
||
|
def catch_exception(f):
|
||
|
@wraps(f)
|
||
|
def func(*args, **kwargs):
|
||
|
try:
|
||
|
return f(*args, **kwargs)
|
||
|
except Exception as e:
|
||
|
name = f.__name__
|
||
|
if IS_WIN:
|
||
|
debug(traceback.format_exc())
|
||
|
log.error(name, exc_info=True)
|
||
|
return func
|
||
|
|
||
|
|
||
|
def inspect(obj: Any) -> None:
|
||
|
zaz = create_instance('net.elmau.zaz.inspect')
|
||
|
if hasattr(obj, 'obj'):
|
||
|
obj = obj.obj
|
||
|
zaz.inspect(obj)
|
||
|
return
|
||
|
|
||
|
|
||
|
def now(only_time=False):
|
||
|
now = datetime.datetime.now()
|
||
|
if only_time:
|
||
|
now = now.time()
|
||
|
return now
|
||
|
|
||
|
|
||
|
def today():
|
||
|
return datetime.date.today()
|
||
|
|
||
|
|
||
|
def msgbox(message, title=TITLE, buttons=MSG_BUTTONS.BUTTONS_OK, type_msg='infobox'):
|
||
|
""" Create message box
|
||
|
type_msg: infobox, warningbox, errorbox, querybox, messbox
|
||
|
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMessageBoxFactory.html
|
||
|
"""
|
||
|
toolkit = create_instance('com.sun.star.awt.Toolkit')
|
||
|
parent = toolkit.getDesktopWindow()
|
||
|
box = toolkit.createMessageBox(parent, type_msg, buttons, title, str(message))
|
||
|
return box.execute()
|
||
|
|
||
|
|
||
|
def question(message, title=TITLE):
|
||
|
result = msgbox(message, title, MSG_BUTTONS.BUTTONS_YES_NO, 'querybox')
|
||
|
return result == YES
|
||
|
|
||
|
|
||
|
def warning(message, title=TITLE):
|
||
|
return msgbox(message, title, type_msg='warningbox')
|
||
|
|
||
|
|
||
|
def errorbox(message, title=TITLE):
|
||
|
return msgbox(message, title, type_msg='errorbox')
|
||
|
|
||
|
|
||
|
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 dict_to_property(values: dict, uno_any: bool=False):
|
||
|
ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
|
||
|
if uno_any:
|
||
|
ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
|
||
|
return ps
|
||
|
|
||
|
|
||
|
def _path_url(path: str) -> str:
|
||
|
if path.startswith('file://'):
|
||
|
return path
|
||
|
return uno.systemPathToFileUrl(path)
|
||
|
|
||
|
|
||
|
def _path_system(path: str) -> str:
|
||
|
if path.startswith('file://'):
|
||
|
return str(Path(uno.fileUrlToSystemPath(path)).resolve())
|
||
|
return path
|
||
|
|
||
|
|
||
|
def _get_dispatch() -> Any:
|
||
|
return create_instance('com.sun.star.frame.DispatchHelper')
|
||
|
|
||
|
|
||
|
def call_dispatch(frame: Any, url: str, args: dict={}) -> None:
|
||
|
dispatch = _get_dispatch()
|
||
|
opt = dict_to_property(args)
|
||
|
dispatch.executeDispatch(frame, url, '', 0, opt)
|
||
|
return
|
||
|
|
||
|
|
||
|
def get_desktop():
|
||
|
return create_instance('com.sun.star.frame.Desktop', True)
|
||
|
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
class LOBaseObject(object):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
self._obj = obj
|
||
|
|
||
|
def __setattr__(self, name, value):
|
||
|
exists = hasattr(self, name)
|
||
|
if not exists and name != '_obj':
|
||
|
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 LODocument(object):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
self._obj = obj
|
||
|
self._cc = self.obj.getCurrentController()
|
||
|
|
||
|
def __enter__(self):
|
||
|
return self
|
||
|
|
||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||
|
pass
|
||
|
|
||
|
@property
|
||
|
def obj(self):
|
||
|
return self._obj
|
||
|
|
||
|
@property
|
||
|
def title(self):
|
||
|
return self.obj.getTitle()
|
||
|
@title.setter
|
||
|
def title(self, value):
|
||
|
self.obj.setTitle(value)
|
||
|
|
||
|
@property
|
||
|
def type(self):
|
||
|
return self._type
|
||
|
|
||
|
@property
|
||
|
def uid(self):
|
||
|
return self.obj.RuntimeUID
|
||
|
|
||
|
@property
|
||
|
def frame(self):
|
||
|
return self._cc.getFrame()
|
||
|
|
||
|
@property
|
||
|
def is_saved(self):
|
||
|
return self.obj.hasLocation()
|
||
|
|
||
|
@property
|
||
|
def is_modified(self):
|
||
|
return self.obj.isModified()
|
||
|
|
||
|
@property
|
||
|
def is_read_only(self):
|
||
|
return self.obj.isReadOnly()
|
||
|
|
||
|
@property
|
||
|
def path(self):
|
||
|
return _path_system(self.obj.URL)
|
||
|
|
||
|
@property
|
||
|
def status_bar(self):
|
||
|
return self._cc.getStatusIndicator()
|
||
|
|
||
|
@property
|
||
|
def visible(self):
|
||
|
w = self.frame.ContainerWindow
|
||
|
return w.isVisible()
|
||
|
@visible.setter
|
||
|
def visible(self, value):
|
||
|
w = self.frame.ContainerWindow
|
||
|
w.setVisible(value)
|
||
|
|
||
|
@property
|
||
|
def zoom(self):
|
||
|
return self._cc.ZoomValue
|
||
|
@zoom.setter
|
||
|
def zoom(self, value):
|
||
|
self._cc.ZoomValue = value
|
||
|
|
||
|
@property
|
||
|
def selection(self):
|
||
|
sel = self.obj.CurrentSelection
|
||
|
return sel
|
||
|
|
||
|
def create_instance(self, name):
|
||
|
obj = self.obj.createInstance(name)
|
||
|
return obj
|
||
|
|
||
|
def set_focus(self):
|
||
|
w = self.frame.ComponentWindow
|
||
|
w.setFocus()
|
||
|
return
|
||
|
|
||
|
def copy(self):
|
||
|
call_dispatch(self.frame, '.uno:Copy')
|
||
|
return
|
||
|
|
||
|
# ~ def paste(self):
|
||
|
# ~ call_dispatch(self.frame, '.uno:Paste')
|
||
|
# ~ return
|
||
|
|
||
|
def paste(self):
|
||
|
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
|
||
|
transferable = sc.getContents()
|
||
|
self._cc.insertTransferable(transferable)
|
||
|
return
|
||
|
|
||
|
def to_pdf(self, path: str='', args: dict={}):
|
||
|
path_pdf = path
|
||
|
if path:
|
||
|
if is_dir(path):
|
||
|
_, _, n, _ = get_info_path(self.path)
|
||
|
path_pdf = join(path, '{}.{}'.format(n, EXT['pdf']))
|
||
|
else:
|
||
|
path_pdf = replace_ext(self.path, EXT['pdf'])
|
||
|
|
||
|
filter_name = '{}_pdf_Export'.format(self.type)
|
||
|
filter_data = dict_to_property(args, True)
|
||
|
args = {
|
||
|
'FilterName': filter_name,
|
||
|
'FilterData': filter_data,
|
||
|
}
|
||
|
opt = dict_to_property(args)
|
||
|
try:
|
||
|
self.obj.storeToURL(_path_url(path_pdf), opt)
|
||
|
except Exception as e:
|
||
|
error(e)
|
||
|
path_pdf = ''
|
||
|
|
||
|
return path_pdf
|
||
|
|
||
|
def save(self, path: str='', args: dict={}) -> bool:
|
||
|
result = True
|
||
|
opt = dict_to_property(args)
|
||
|
if path:
|
||
|
try:
|
||
|
self.obj.storeAsURL(_path_url(path), opt)
|
||
|
except Exception as e:
|
||
|
error(e)
|
||
|
result = False
|
||
|
else:
|
||
|
self.obj.store()
|
||
|
return result
|
||
|
|
||
|
def close(self):
|
||
|
self.obj.close(True)
|
||
|
return
|
||
|
|
||
|
|
||
|
|
||
|
class LOCalc(LODocument):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
super().__init__(obj)
|
||
|
self._type = CALC
|
||
|
self._sheets = obj.Sheets
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
return LOCalcSheet(self._sheets[index])
|
||
|
|
||
|
def __len__(self):
|
||
|
return self._sheets.Count
|
||
|
|
||
|
@property
|
||
|
def selection(self):
|
||
|
sel = self.obj.CurrentSelection
|
||
|
if sel.ImplementationName in TYPE_RANGES:
|
||
|
sel = LOCalcRange(sel)
|
||
|
return sel
|
||
|
|
||
|
@property
|
||
|
def active(self):
|
||
|
return LOCalcSheet(self._cc.ActiveSheet)
|
||
|
|
||
|
@property
|
||
|
def db_ranges(self):
|
||
|
# ~ return LOCalcDataBaseRanges(self.obj.DataBaseRanges)
|
||
|
return self.obj.DatabaseRanges
|
||
|
|
||
|
|
||
|
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 LOFormControl(LOBaseObject):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
self._obj = obj
|
||
|
self._control = self.doc.CurrentController.getControl(self.obj)
|
||
|
|
||
|
def __setattr__(self, name, value):
|
||
|
if name == '_control':
|
||
|
self.__dict__[name] = value
|
||
|
else:
|
||
|
super().__setattr__(name, value)
|
||
|
|
||
|
@property
|
||
|
def doc(self):
|
||
|
return self.obj.Parent.Parent.Parent
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
return self.obj.Name
|
||
|
|
||
|
@property
|
||
|
def label(self):
|
||
|
return self.obj.Label
|
||
|
|
||
|
def set_focus(self):
|
||
|
self._control.setFocus()
|
||
|
return
|
||
|
|
||
|
|
||
|
class LOForm(object):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
self._obj = obj
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
return LOFormControl(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
|
||
|
|
||
|
def __len__(self):
|
||
|
return len(self.obj)
|
||
|
|
||
|
@property
|
||
|
def obj(self):
|
||
|
return self._obj
|
||
|
|
||
|
|
||
|
class LOSheetForms(object):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
self._obj = obj
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
return LOForm(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
|
||
|
|
||
|
def __len__(self):
|
||
|
return len(self.obj)
|
||
|
|
||
|
@property
|
||
|
def obj(self):
|
||
|
return self._obj
|
||
|
|
||
|
|
||
|
class LOCalcSheet(object):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
self._obj = obj
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
return LOCalcRange(self.obj[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._obj.Name
|
||
|
@name.setter
|
||
|
def name(self, value):
|
||
|
self._obj.Name = value
|
||
|
|
||
|
@property
|
||
|
def draw_page(self):
|
||
|
return self.obj.DrawPage
|
||
|
|
||
|
@property
|
||
|
def doc(self):
|
||
|
return self.draw_page.Forms.Parent
|
||
|
|
||
|
@property
|
||
|
def charts(self):
|
||
|
return LOSheetCharts(self.obj.Charts, self)
|
||
|
|
||
|
@property
|
||
|
def forms(self):
|
||
|
return LOSheetForms(self.obj.DrawPage.Forms)
|
||
|
|
||
|
def get_cursor(self, cell):
|
||
|
return self.obj.createCursorByRange(cell)
|
||
|
|
||
|
|
||
|
class LOCalcRange(object):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
self._obj = obj
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
return LOCalcRange(self.obj[index])
|
||
|
|
||
|
def __iter__(self):
|
||
|
self._r = 0
|
||
|
self._c = 0
|
||
|
return self
|
||
|
|
||
|
def __next__(self):
|
||
|
try:
|
||
|
rango = self[self._r, self._c]
|
||
|
except Exception as e:
|
||
|
raise StopIteration
|
||
|
self._c += 1
|
||
|
if self._c == self.columns:
|
||
|
self._c = 0
|
||
|
self._r +=1
|
||
|
return rango
|
||
|
|
||
|
def __enter__(self):
|
||
|
return self
|
||
|
|
||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||
|
pass
|
||
|
|
||
|
@property
|
||
|
def obj(self):
|
||
|
return self._obj
|
||
|
|
||
|
@property
|
||
|
def sheet(self):
|
||
|
return LOCalcSheet(self.obj.Spreadsheet)
|
||
|
|
||
|
@property
|
||
|
def doc(self):
|
||
|
doc = self.obj.Spreadsheet.DrawPage.Forms.Parent
|
||
|
return doc
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
return self.obj.AbsoluteName
|
||
|
|
||
|
@property
|
||
|
def columns(self):
|
||
|
return self.obj.Columns.Count
|
||
|
|
||
|
@property
|
||
|
def rows(self):
|
||
|
return self.obj.Rows.Count
|
||
|
|
||
|
@property
|
||
|
def type(self):
|
||
|
return self.obj.Type
|
||
|
|
||
|
@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] == '=':
|
||
|
self.obj.setFormula(data)
|
||
|
else:
|
||
|
self.obj.setString(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 data(self):
|
||
|
return self.obj.getDataArray()
|
||
|
@data.setter
|
||
|
def data(self, values):
|
||
|
self.obj.setDataArray(values)
|
||
|
|
||
|
@property
|
||
|
def formula(self):
|
||
|
return self.obj.getFormulaArray()
|
||
|
@formula.setter
|
||
|
def formula(self, values):
|
||
|
self.obj.setFormulaArray(values)
|
||
|
|
||
|
@property
|
||
|
def address(self):
|
||
|
return self.obj.CellAddress
|
||
|
|
||
|
@property
|
||
|
def range_address(self):
|
||
|
return self.obj.RangeAddress
|
||
|
|
||
|
@property
|
||
|
def cursor(self):
|
||
|
cursor = self.obj.Spreadsheet.createCursorByRange(self.obj)
|
||
|
return cursor
|
||
|
|
||
|
@property
|
||
|
def current_region(self):
|
||
|
cursor = self.cursor
|
||
|
cursor.collapseToCurrentRegion()
|
||
|
return LOCalcRange(self.sheet[cursor.AbsoluteName].obj)
|
||
|
|
||
|
@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
|
||
|
|
||
|
def select(self):
|
||
|
self.doc.CurrentController.select(self.obj)
|
||
|
return
|
||
|
|
||
|
def to_size(self, rows, cols):
|
||
|
cursor = self.cursor
|
||
|
cursor.collapseToSize(cols, rows)
|
||
|
return LOCalcRange(self.sheet[cursor.AbsoluteName].obj)
|
||
|
|
||
|
def copy_to(self, cell, formula=False):
|
||
|
rango = cell.to_size(self.rows, self.columns)
|
||
|
if formula:
|
||
|
rango.formula = self.data
|
||
|
else:
|
||
|
rango.data = self.data
|
||
|
return
|
||
|
|
||
|
def auto_width(self):
|
||
|
self.obj.Columns.OptimalWidth = True
|
||
|
return
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
class LOWriter(LODocument):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
super().__init__(obj)
|
||
|
self._type = WRITER
|
||
|
|
||
|
|
||
|
class LODrawImpress(LODocument):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
super().__init__(obj)
|
||
|
|
||
|
|
||
|
class LODraw(LODrawImpress):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
super().__init__(obj)
|
||
|
self._type = DRAW
|
||
|
|
||
|
|
||
|
class LOImpress(LODrawImpress):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
super().__init__(obj)
|
||
|
self._type = IMPRESS
|
||
|
|
||
|
|
||
|
class BaseDateField(DateField):
|
||
|
|
||
|
def db_value(self, value):
|
||
|
return _date_to_struct(value)
|
||
|
|
||
|
def python_value(self, value):
|
||
|
return _struct_to_date(value)
|
||
|
|
||
|
|
||
|
class BaseTimeField(TimeField):
|
||
|
|
||
|
def db_value(self, value):
|
||
|
return _date_to_struct(value)
|
||
|
|
||
|
def python_value(self, value):
|
||
|
return _struct_to_date(value)
|
||
|
|
||
|
|
||
|
class BaseDateTimeField(DateTimeField):
|
||
|
|
||
|
def db_value(self, value):
|
||
|
return _date_to_struct(value)
|
||
|
|
||
|
def python_value(self, value):
|
||
|
return _struct_to_date(value)
|
||
|
|
||
|
|
||
|
class FirebirdDatabase(Database):
|
||
|
field_types = {'BOOL': 'BOOLEAN', 'DATETIME': 'TIMESTAMP'}
|
||
|
|
||
|
def __init__(self, database, **kwargs):
|
||
|
super().__init__(database, **kwargs)
|
||
|
self._db = database
|
||
|
|
||
|
def _connect(self):
|
||
|
return self._db
|
||
|
|
||
|
def create_tables(self, models, **options):
|
||
|
options['safe'] = False
|
||
|
tables = self._db.tables
|
||
|
models = [m for m in models if not m.__name__.lower() in tables]
|
||
|
super().create_tables(models, **options)
|
||
|
|
||
|
def execute_sql(self, sql, params=None, commit=True):
|
||
|
with __exception_wrapper__:
|
||
|
cursor = self._db.execute(sql, params)
|
||
|
return cursor
|
||
|
|
||
|
def last_insert_id(self, cursor, query_type=None):
|
||
|
# ~ debug('LAST_ID', cursor)
|
||
|
return 0
|
||
|
|
||
|
def rows_affected(self, cursor):
|
||
|
return self._db.rows_affected
|
||
|
|
||
|
@property
|
||
|
def path(self):
|
||
|
return self._db.path
|
||
|
|
||
|
|
||
|
class BaseRow:
|
||
|
pass
|
||
|
|
||
|
|
||
|
class BaseQuery(object):
|
||
|
PY_TYPES = {
|
||
|
'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')
|
||
|
|
||
|
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)
|
||
|
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)
|
||
|
|
||
|
|
||
|
class LOBase(object):
|
||
|
DB_TYPES = {
|
||
|
str: 'setString',
|
||
|
int: 'setInt',
|
||
|
float: 'setFloat',
|
||
|
bool: 'setBoolean',
|
||
|
Date: 'setDate',
|
||
|
Time: 'setTime',
|
||
|
DateTime: 'setTimestamp',
|
||
|
}
|
||
|
# ~ setArray
|
||
|
# ~ setBinaryStream
|
||
|
# ~ setBlob
|
||
|
# ~ setByte
|
||
|
# ~ setBytes
|
||
|
# ~ setCharacterStream
|
||
|
# ~ setClob
|
||
|
# ~ setNull
|
||
|
# ~ setObject
|
||
|
# ~ setObjectNull
|
||
|
# ~ setObjectWithInfo
|
||
|
# ~ setPropertyValue
|
||
|
# ~ setRef
|
||
|
|
||
|
def __init__(self, obj, args={}):
|
||
|
self._obj = obj
|
||
|
self._type = BASE
|
||
|
self._path = args.get('path', '')
|
||
|
self._dbc = create_instance('com.sun.star.sdb.DatabaseContext')
|
||
|
self._rows_affected = 0
|
||
|
if self._path:
|
||
|
self._name = Path(self._path).name
|
||
|
path_url = _path_url(self._path)
|
||
|
db = self._dbc.createInstance()
|
||
|
db.URL = 'sdbc:embedded:firebird'
|
||
|
db.DatabaseDocument.storeAsURL(path_url, ())
|
||
|
self.register()
|
||
|
self._obj = db
|
||
|
else:
|
||
|
self._name = self._obj
|
||
|
if Path(self._name).exists():
|
||
|
self._path = self._name
|
||
|
self._name = Path(self._path).name
|
||
|
if self.is_registered:
|
||
|
db = self._dbc.getByName(self.name)
|
||
|
self._path = _path_system(self._dbc.getDatabaseLocation(self.name))
|
||
|
self._obj = db
|
||
|
else:
|
||
|
path_url = _path_url(self._path)
|
||
|
self._dbc.registerDatabaseLocation(self.name, path_url)
|
||
|
db = self._dbc.getByName(self.name)
|
||
|
self._con = db.getConnection('', '')
|
||
|
|
||
|
def __contains__(self, item):
|
||
|
return item in self.tables
|
||
|
|
||
|
@property
|
||
|
def obj(self):
|
||
|
return self._obj
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
return self._name
|
||
|
|
||
|
@property
|
||
|
def path(self):
|
||
|
return self._path
|
||
|
|
||
|
@property
|
||
|
def is_registered(self):
|
||
|
return self._dbc.hasRegisteredDatabase(self.name)
|
||
|
|
||
|
@property
|
||
|
def tables(self):
|
||
|
tables = [t.Name.lower() for t in self._con.getTables()]
|
||
|
return tables
|
||
|
|
||
|
@property
|
||
|
def rows_affected(self):
|
||
|
return self._rows_affected
|
||
|
|
||
|
def register(self):
|
||
|
if not self.is_registered:
|
||
|
path_url = _path_url(self._path)
|
||
|
self._dbc.registerDatabaseLocation(self.name, path_url)
|
||
|
return
|
||
|
|
||
|
def revoke(self, name):
|
||
|
self._dbc.revokeDatabaseLocation(name)
|
||
|
return True
|
||
|
|
||
|
def save(self):
|
||
|
self.obj.DatabaseDocument.store()
|
||
|
self.refresh()
|
||
|
return
|
||
|
|
||
|
def close(self):
|
||
|
self._con.close()
|
||
|
return
|
||
|
|
||
|
def refresh(self):
|
||
|
self._con.getTables().refresh()
|
||
|
return
|
||
|
|
||
|
def initialize(self, database_proxy, tables):
|
||
|
db = FirebirdDatabase(self)
|
||
|
database_proxy.initialize(db)
|
||
|
db.create_tables(tables)
|
||
|
return
|
||
|
|
||
|
def _validate_sql(self, sql, params):
|
||
|
limit = ' LIMIT '
|
||
|
for p in params:
|
||
|
sql = sql.replace('?', f"'{p}'", 1)
|
||
|
if limit in sql:
|
||
|
sql = sql.split(limit)[0]
|
||
|
sql = sql.replace('SELECT', f'SELECT FIRST {params[-1]}')
|
||
|
return sql
|
||
|
|
||
|
def cursor(self, sql, params):
|
||
|
if sql.startswith('SELECT'):
|
||
|
sql = self._validate_sql(sql, params)
|
||
|
cursor = self._con.prepareStatement(sql)
|
||
|
return cursor
|
||
|
|
||
|
if not params:
|
||
|
cursor = self._con.createStatement()
|
||
|
return cursor
|
||
|
|
||
|
cursor = self._con.prepareStatement(sql)
|
||
|
for i, v in enumerate(params, 1):
|
||
|
t = type(v)
|
||
|
if not t in self.DB_TYPES:
|
||
|
error('Type not support')
|
||
|
debug((i, t, v, self.DB_TYPES[t]))
|
||
|
getattr(cursor, self.DB_TYPES[t])(i, v)
|
||
|
return cursor
|
||
|
|
||
|
def execute(self, sql, params):
|
||
|
debug(sql, params)
|
||
|
cursor = self.cursor(sql, params)
|
||
|
|
||
|
if sql.startswith('SELECT'):
|
||
|
result = cursor.executeQuery()
|
||
|
elif params:
|
||
|
result = cursor.executeUpdate()
|
||
|
self._rows_affected = result
|
||
|
self.save()
|
||
|
else:
|
||
|
result = cursor.execute(sql)
|
||
|
self.save()
|
||
|
|
||
|
return result
|
||
|
|
||
|
def select(self, sql):
|
||
|
debug('SELECT', sql)
|
||
|
if not sql.startswith('SELECT'):
|
||
|
return ()
|
||
|
|
||
|
cursor = self._con.prepareStatement(sql)
|
||
|
query = cursor.executeQuery()
|
||
|
return BaseQuery(query)
|
||
|
|
||
|
def get_query(self, query):
|
||
|
sql, args = query.sql()
|
||
|
sql = self._validate_sql(sql, args)
|
||
|
return self.select(sql)
|
||
|
|
||
|
|
||
|
class LOMath(LODocument):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
super().__init__(obj)
|
||
|
self._type = MATH
|
||
|
|
||
|
|
||
|
class LOBasic(LODocument):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
super().__init__(obj)
|
||
|
self._type = BASIC
|
||
|
|
||
|
|
||
|
class LODocs(object):
|
||
|
_desktop = None
|
||
|
|
||
|
def __init__(self):
|
||
|
self._desktop = get_desktop()
|
||
|
LODocs._desktop = self._desktop
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
for i, doc in enumerate(self._desktop.Components):
|
||
|
if isinstance(index, int) and i == index:
|
||
|
return _get_class_doc(doc)
|
||
|
elif isinstance(index, str) and doc.Title == index:
|
||
|
return _get_class_doc(doc)
|
||
|
|
||
|
def __contains__(self, item):
|
||
|
doc = self[item]
|
||
|
return not doc is None
|
||
|
|
||
|
def __iter__(self):
|
||
|
self._i = 0
|
||
|
return self
|
||
|
|
||
|
def __next__(self):
|
||
|
doc = self[self._i]
|
||
|
if doc is None:
|
||
|
raise StopIteration
|
||
|
self._i += 1
|
||
|
return doc
|
||
|
|
||
|
def __len__(self):
|
||
|
for i, _ in enumerate(self._desktop.Components):
|
||
|
pass
|
||
|
return i + 1
|
||
|
|
||
|
@property
|
||
|
def active(self):
|
||
|
return _get_class_doc(self._desktop.getCurrentComponent())
|
||
|
|
||
|
@classmethod
|
||
|
def new(cls, type_doc=CALC, args={}):
|
||
|
if type_doc == BASE:
|
||
|
return LOBase(None, args)
|
||
|
|
||
|
path = f'private:factory/s{type_doc}'
|
||
|
opt = dict_to_property(args)
|
||
|
doc = cls._desktop.loadComponentFromURL(path, '_default', 0, opt)
|
||
|
return _get_class_doc(doc)
|
||
|
|
||
|
@classmethod
|
||
|
def open(cls, path, args={}):
|
||
|
""" Open document in path
|
||
|
Usually options:
|
||
|
Hidden: True or False
|
||
|
AsTemplate: True or False
|
||
|
ReadOnly: True or False
|
||
|
Password: super_secret
|
||
|
MacroExecutionMode: 4 = Activate macros
|
||
|
Preview: True or False
|
||
|
|
||
|
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1frame_1_1XComponentLoader.html
|
||
|
http://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1document_1_1MediaDescriptor.html
|
||
|
"""
|
||
|
path = _path_url(path)
|
||
|
opt = dict_to_property(args)
|
||
|
doc = cls._desktop.loadComponentFromURL(path, '_default', 0, opt)
|
||
|
if doc is None:
|
||
|
return
|
||
|
|
||
|
return _get_class_doc(doc)
|
||
|
|
||
|
def connect(self, path):
|
||
|
return LOBase(path)
|
||
|
|
||
|
|
||
|
def _add_listeners(events, control, name=''):
|
||
|
listeners = {
|
||
|
'addActionListener': EventsButton,
|
||
|
'addMouseListener': EventsMouse,
|
||
|
# ~ 'addItemListener': EventsItem,
|
||
|
# ~ 'addFocusListener': EventsFocus,
|
||
|
# ~ 'addKeyListener': EventsKey,
|
||
|
# ~ 'addTabListener': EventsTab,
|
||
|
}
|
||
|
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'
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
def _set_properties(model, properties):
|
||
|
if 'X' in properties:
|
||
|
properties['PositionX'] = properties.pop('X')
|
||
|
if 'Y' in properties:
|
||
|
properties['PositionY'] = properties.pop('Y')
|
||
|
keys = tuple(properties.keys())
|
||
|
values = tuple(properties.values())
|
||
|
model.setPropertyValues(keys, values)
|
||
|
return
|
||
|
|
||
|
|
||
|
class EventsListenerBase(unohelper.Base, XEventListener):
|
||
|
|
||
|
def __init__(self, controller, name, window=None):
|
||
|
self._controller = controller
|
||
|
self._name = name
|
||
|
self._window = window
|
||
|
|
||
|
@property
|
||
|
def name(self):
|
||
|
return self._name
|
||
|
|
||
|
def disposing(self, event):
|
||
|
self._controller = None
|
||
|
if not self._window is None:
|
||
|
self._window.setMenuBar(None)
|
||
|
|
||
|
|
||
|
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):
|
||
|
pass
|
||
|
|
||
|
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 UnoBaseObject(object):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
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)
|
||
|
|
||
|
@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
|
||
|
|
||
|
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:
|
||
|
self.y = origin.y + origin.height + 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
|
||
|
|
||
|
|
||
|
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 UnoCheck(UnoBaseObject):
|
||
|
|
||
|
def __init__(self, obj):
|
||
|
super().__init__(obj)
|
||
|
|
||
|
@property
|
||
|
def type(self):
|
||
|
return 'check'
|
||
|
|
||
|
@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
|
||
|
|
||
|
|
||
|
UNO_CLASSES = {
|
||
|
'label': UnoLabel,
|
||
|
'link': UnoLabelLink,
|
||
|
'button': UnoButton,
|
||
|
'radio': UnoRadio,
|
||
|
'check': UnoCheck,
|
||
|
}
|
||
|
|
||
|
|
||
|
class LODialog(object):
|
||
|
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',
|
||
|
'check': 'com.sun.star.awt.UnoControlCheckBoxModel',
|
||
|
# ~ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
|
||
|
# ~ 'text': 'com.sun.star.awt.UnoControlEditModel',
|
||
|
# ~ 'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
|
||
|
# ~ 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
|
||
|
# ~ 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
|
||
|
# ~ 'tree': 'com.sun.star.awt.tree.TreeControlModel',
|
||
|
# ~ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
|
||
|
# ~ 'image': 'com.sun.star.awt.UnoControlImageControlModel',
|
||
|
# ~ 'pages': 'com.sun.star.awt.UnoMultiPageModel',
|
||
|
}
|
||
|
|
||
|
def __init__(self, args):
|
||
|
self._obj = self._create(args)
|
||
|
self._model = self.obj.Model
|
||
|
self._events = None
|
||
|
self._modal = True
|
||
|
self._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(_path_url(path))
|
||
|
return dlg
|
||
|
|
||
|
if 'Location' in args:
|
||
|
name = args['Name']
|
||
|
library = args.get('Library', 'Standard')
|
||
|
location = args.get('Location', 'application')
|
||
|
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)
|
||
|
_set_properties(model, args)
|
||
|
dlg.setModel(model)
|
||
|
dlg.setVisible(False)
|
||
|
dlg.createPeer(toolkit, None)
|
||
|
return dlg
|
||
|
|
||
|
@property
|
||
|
def obj(self):
|
||
|
return self._obj
|
||
|
|
||
|
@property
|
||
|
def model(self):
|
||
|
return self._model
|
||
|
|
||
|
@property
|
||
|
def controls(self):
|
||
|
return self._controls
|
||
|
|
||
|
@property
|
||
|
def visible(self):
|
||
|
return self.obj.Visible
|
||
|
@visible.setter
|
||
|
def visible(self, value):
|
||
|
self.obj.Visible = value
|
||
|
|
||
|
@property
|
||
|
def events(self):
|
||
|
return self._events
|
||
|
@events.setter
|
||
|
def events(self, controllers):
|
||
|
self._events = controllers(self)
|
||
|
self._connect_listeners()
|
||
|
|
||
|
def _connect_listeners(self):
|
||
|
for control in self.obj.Controls:
|
||
|
_add_listeners(self.events, control, control.Model.Name)
|
||
|
return
|
||
|
|
||
|
def _special_properties(self, tipo, args):
|
||
|
columns = args.pop('Columns', ())
|
||
|
|
||
|
if tipo == 'link' and not 'Label' in args:
|
||
|
args['Label'] = args['URL']
|
||
|
elif tipo == 'grid':
|
||
|
args['ColumnModel'] = self._set_column_model(columns)
|
||
|
elif tipo == 'button':
|
||
|
if 'ImageURL' in args:
|
||
|
args['ImageURL'] = self._set_image_url(args['ImageURL'])
|
||
|
if not 'FocusOnClick' in args:
|
||
|
args['FocusOnClick'] = False
|
||
|
elif tipo == 'roadmap':
|
||
|
if not 'Height' in args:
|
||
|
args['Height'] = self.height
|
||
|
if 'Title' in args:
|
||
|
args['Text'] = args.pop('Title')
|
||
|
elif tipo == 'tab':
|
||
|
if not 'Width' in args:
|
||
|
args['Width'] = self.width
|
||
|
if not 'Height' in args:
|
||
|
args['Height'] = self.height
|
||
|
|
||
|
return args
|
||
|
|
||
|
def add_control(self, args):
|
||
|
tipo = args.pop('Type').lower()
|
||
|
root = args.pop('Root', '')
|
||
|
sheets = args.pop('Sheets', ())
|
||
|
|
||
|
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 == 'tree' and root:
|
||
|
control.root = root
|
||
|
elif tipo == 'pages' and sheets:
|
||
|
control.sheets = sheets
|
||
|
control.events = self.events
|
||
|
|
||
|
setattr(self, name, control)
|
||
|
self._controls[name] = control
|
||
|
return control
|
||
|
|
||
|
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
|
||
|
|
||
|
|
||
|
class LOSheets(object):
|
||
|
|
||
|
def __init__(self):
|
||
|
pass
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
return docs.active[index]
|
||
|
|
||
|
def __enter__(self):
|
||
|
return self
|
||
|
|
||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class LOCells(object):
|
||
|
|
||
|
def __init__(self):
|
||
|
pass
|
||
|
|
||
|
def __getitem__(self, index):
|
||
|
return docs.active.active[index]
|
||
|
|
||
|
|
||
|
class Paths(object):
|
||
|
|
||
|
@classmethod
|
||
|
def exists(cls, path):
|
||
|
return Path(path).exists()
|
||
|
|
||
|
|
||
|
def __getattr__(name):
|
||
|
if name == 'active':
|
||
|
return docs.active
|
||
|
if name == 'active_sheet':
|
||
|
return docs.active.active
|
||
|
if name == 'selection':
|
||
|
return docs.active.selection
|
||
|
if name in ('rectangle', 'pos_size'):
|
||
|
return Rectangle()
|
||
|
if name == 'paths':
|
||
|
return Paths()
|
||
|
if name == 'docs':
|
||
|
return LODocs()
|
||
|
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
|
||
|
|
||
|
|
||
|
def create_dialog(args):
|
||
|
return LODialog(args)
|
||
|
|
||
|
|
||
|
def get_fonts():
|
||
|
toolkit = create_instance('com.sun.star.awt.Toolkit')
|
||
|
device = toolkit.createScreenCompatibleDevice(0, 0)
|
||
|
return device.FontDescriptors
|
||
|
|
||
|
|
||
|
# ~ docs = LODocs()
|
||
|
sheets = LOSheets()
|
||
|
cells = LOCells()
|
||
|
|
||
|
# ~ https://en.wikipedia.org/wiki/Web_colors
|
||
|
def get_color(value):
|
||
|
COLORS = {
|
||
|
'aliceblue': 15792383,
|
||
|
'antiquewhite': 16444375,
|
||
|
'aqua': 65535,
|
||
|
'aquamarine': 8388564,
|
||
|
'azure': 15794175,
|
||
|
'beige': 16119260,
|
||
|
'bisque': 16770244,
|
||
|
'black': 0,
|
||
|
'blanchedalmond': 16772045,
|
||
|
'blue': 255,
|
||
|
'blueviolet': 9055202,
|
||
|
'brown': 10824234,
|
||
|
'burlywood': 14596231,
|
||
|
'cadetblue': 6266528,
|
||
|
'chartreuse': 8388352,
|
||
|
'chocolate': 13789470,
|
||
|
'coral': 16744272,
|
||
|
'cornflowerblue': 6591981,
|
||
|
'cornsilk': 16775388,
|
||
|
'crimson': 14423100,
|
||
|
'cyan': 65535,
|
||
|
'darkblue': 139,
|
||
|
'darkcyan': 35723,
|
||
|
'darkgoldenrod': 12092939,
|
||
|
'darkgray': 11119017,
|
||
|
'darkgreen': 25600,
|
||
|
'darkgrey': 11119017,
|
||
|
'darkkhaki': 12433259,
|
||
|
'darkmagenta': 9109643,
|
||
|
'darkolivegreen': 5597999,
|
||
|
'darkorange': 16747520,
|
||
|
'darkorchid': 10040012,
|
||
|
'darkred': 9109504,
|
||
|
'darksalmon': 15308410,
|
||
|
'darkseagreen': 9419919,
|
||
|
'darkslateblue': 4734347,
|
||
|
'darkslategray': 3100495,
|
||
|
'darkslategrey': 3100495,
|
||
|
'darkturquoise': 52945,
|
||
|
'darkviolet': 9699539,
|
||
|
'deeppink': 16716947,
|
||
|
'deepskyblue': 49151,
|
||
|
'dimgray': 6908265,
|
||
|
'dimgrey': 6908265,
|
||
|
'dodgerblue': 2003199,
|
||
|
'firebrick': 11674146,
|
||
|
'floralwhite': 16775920,
|
||
|
'forestgreen': 2263842,
|
||
|
'fuchsia': 16711935,
|
||
|
'gainsboro': 14474460,
|
||
|
'ghostwhite': 16316671,
|
||
|
'gold': 16766720,
|
||
|
'goldenrod': 14329120,
|
||
|
'gray': 8421504,
|
||
|
'grey': 8421504,
|
||
|
'green': 32768,
|
||
|
'greenyellow': 11403055,
|
||
|
'honeydew': 15794160,
|
||
|
'hotpink': 16738740,
|
||
|
'indianred': 13458524,
|
||
|
'indigo': 4915330,
|
||
|
'ivory': 16777200,
|
||
|
'khaki': 15787660,
|
||
|
'lavender': 15132410,
|
||
|
'lavenderblush': 16773365,
|
||
|
'lawngreen': 8190976,
|
||
|
'lemonchiffon': 16775885,
|
||
|
'lightblue': 11393254,
|
||
|
'lightcoral': 15761536,
|
||
|
'lightcyan': 14745599,
|
||
|
'lightgoldenrodyellow': 16448210,
|
||
|
'lightgray': 13882323,
|
||
|
'lightgreen': 9498256,
|
||
|
'lightgrey': 13882323,
|
||
|
'lightpink': 16758465,
|
||
|
'lightsalmon': 16752762,
|
||
|
'lightseagreen': 2142890,
|
||
|
'lightskyblue': 8900346,
|
||
|
'lightslategray': 7833753,
|
||
|
'lightslategrey': 7833753,
|
||
|
'lightsteelblue': 11584734,
|
||
|
'lightyellow': 16777184,
|
||
|
'lime': 65280,
|
||
|
'limegreen': 3329330,
|
||
|
'linen': 16445670,
|
||
|
'magenta': 16711935,
|
||
|
'maroon': 8388608,
|
||
|
'mediumaquamarine': 6737322,
|
||
|
'mediumblue': 205,
|
||
|
'mediumorchid': 12211667,
|
||
|
'mediumpurple': 9662683,
|
||
|
'mediumseagreen': 3978097,
|
||
|
'mediumslateblue': 8087790,
|
||
|
'mediumspringgreen': 64154,
|
||
|
'mediumturquoise': 4772300,
|
||
|
'mediumvioletred': 13047173,
|
||
|
'midnightblue': 1644912,
|
||
|
'mintcream': 16121850,
|
||
|
'mistyrose': 16770273,
|
||
|
'moccasin': 16770229,
|
||
|
'navajowhite': 16768685,
|
||
|
'navy': 128,
|
||
|
'oldlace': 16643558,
|
||
|
'olive': 8421376,
|
||
|
'olivedrab': 7048739,
|
||
|
'orange': 16753920,
|
||
|
'orangered': 16729344,
|
||
|
'orchid': 14315734,
|
||
|
'palegoldenrod': 15657130,
|
||
|
'palegreen': 10025880,
|
||
|
'paleturquoise': 11529966,
|
||
|
'palevioletred': 14381203,
|
||
|
'papayawhip': 16773077,
|
||
|
'peachpuff': 16767673,
|
||
|
'peru': 13468991,
|
||
|
'pink': 16761035,
|
||
|
'plum': 14524637,
|
||
|
'powderblue': 11591910,
|
||
|
'purple': 8388736,
|
||
|
'red': 16711680,
|
||
|
'rosybrown': 12357519,
|
||
|
'royalblue': 4286945,
|
||
|
'saddlebrown': 9127187,
|
||
|
'salmon': 16416882,
|
||
|
'sandybrown': 16032864,
|
||
|
'seagreen': 3050327,
|
||
|
'seashell': 16774638,
|
||
|
'sienna': 10506797,
|
||
|
'silver': 12632256,
|
||
|
'skyblue': 8900331,
|
||
|
'slateblue': 6970061,
|
||
|
'slategray': 7372944,
|
||
|
'slategrey': 7372944,
|
||
|
'snow': 16775930,
|
||
|
'springgreen': 65407,
|
||
|
'steelblue': 4620980,
|
||
|
'tan': 13808780,
|
||
|
'teal': 32896,
|
||
|
'thistle': 14204888,
|
||
|
'tomato': 16737095,
|
||
|
'turquoise': 4251856,
|
||
|
'violet': 15631086,
|
||
|
'wheat': 16113331,
|
||
|
'white': 16777215,
|
||
|
'whitesmoke': 16119285,
|
||
|
'yellow': 16776960,
|
||
|
'yellowgreen': 10145074,
|
||
|
}
|
||
|
|
||
|
if isinstance(value, tuple):
|
||
|
color = (value[0] << 16) + (value[1] << 8) + value[2]
|
||
|
else:
|
||
|
if value[0] == '#':
|
||
|
r, g, b = bytes.fromhex(value[1:])
|
||
|
color = (r << 16) + (g << 8) + b
|
||
|
else:
|
||
|
color = COLORS.get(value.lower(), -1)
|
||
|
return color
|
||
|
|
||
|
|
||
|
COLOR_ON_FOCUS = get_color('LightYellow')
|
||
|
|
||
|
|
||
|
class LOServer(object):
|
||
|
HOST = 'localhost'
|
||
|
PORT = '8100'
|
||
|
ARG = f'socket,host={HOST},port={PORT};urp;StarOffice.ComponentContext'
|
||
|
CMD = ['soffice',
|
||
|
'-env:SingleAppInstance=false',
|
||
|
'-env:UserInstallation=file:///tmp/LO_Process8100',
|
||
|
'--headless', '--norestore', '--invisible',
|
||
|
f'--accept={ARG}']
|
||
|
|
||
|
def __init__(self):
|
||
|
self._server = None
|
||
|
self._ctx = None
|
||
|
self._sm = None
|
||
|
self._start_server()
|
||
|
self._init_values()
|
||
|
|
||
|
def _init_values(self):
|
||
|
global CTX
|
||
|
global SM
|
||
|
|
||
|
if not self.is_running:
|
||
|
return
|
||
|
|
||
|
ctx = uno.getComponentContext()
|
||
|
service = 'com.sun.star.bridge.UnoUrlResolver'
|
||
|
resolver = ctx.ServiceManager.createInstanceWithContext(service, ctx)
|
||
|
self._ctx = resolver.resolve('uno:{}'.format(self.ARG))
|
||
|
self._sm = self._ctx.getServiceManager()
|
||
|
CTX = self._ctx
|
||
|
SM = self._sm
|
||
|
return
|
||
|
|
||
|
@property
|
||
|
def is_running(self):
|
||
|
try:
|
||
|
s = socket.create_connection((self.HOST, self.PORT), 5.0)
|
||
|
s.close()
|
||
|
debug('LibreOffice is running...')
|
||
|
return True
|
||
|
except ConnectionRefusedError:
|
||
|
return False
|
||
|
|
||
|
def _start_server(self):
|
||
|
if self.is_running:
|
||
|
return
|
||
|
|
||
|
for i in range(3):
|
||
|
self._server = subprocess.Popen(self.CMD,
|
||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||
|
time.sleep(3)
|
||
|
if self.is_running:
|
||
|
break
|
||
|
return
|
||
|
|
||
|
def stop(self):
|
||
|
if self._server is None:
|
||
|
print('Search pgrep soffice')
|
||
|
else:
|
||
|
self._server.terminate()
|
||
|
debug('LibreOffice is stop...')
|
||
|
return
|
||
|
|
||
|
def create_instance(self, name, with_context=True):
|
||
|
if with_context:
|
||
|
instance = self._sm.createInstanceWithContext(name, self._ctx)
|
||
|
else:
|
||
|
instance = self._sm.createInstance(name)
|
||
|
return instance
|