zaz-pip/easymacro.py

4856 lines
123 KiB
Python
Raw Normal View History

2019-10-18 22:13:43 -05:00
#!/usr/bin/env python3
# == Rapid Develop Macros in LibreOffice ==
# ~ This file is part of ZAZ.
2020-11-12 15:33:31 -06:00
# ~ https://git.elmau.net/elmau/zaz
2019-10-18 22:13:43 -05:00
# ~ 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 base64
import csv
import datetime
import getpass
2020-11-12 15:33:31 -06:00
import gettext
2019-10-18 22:13:43 -05:00
import hashlib
import json
import logging
import os
import platform
import re
import shlex
import shutil
import socket
2019-11-12 19:30:28 -06:00
import ssl
2020-11-12 15:33:31 -06:00
import subprocess
2019-10-18 22:13:43 -05:00
import sys
import tempfile
import threading
import time
2020-11-12 23:10:42 -06:00
import traceback
2019-10-18 22:13:43 -05:00
import zipfile
2020-11-12 15:33:31 -06:00
from collections import OrderedDict
from collections.abc import MutableMapping
from decimal import Decimal
from enum import IntEnum
2019-10-18 22:13:43 -05:00
from functools import wraps
2020-11-12 15:33:31 -06:00
from pathlib import Path
2019-10-18 22:13:43 -05:00
from pprint import pprint
2020-11-12 15:33:31 -06:00
from string import Template
from typing import Any
2019-10-18 22:13:43 -05:00
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
import smtplib
from smtplib import SMTPException, SMTPAuthenticationError
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email.mime.text import MIMEText
from email.utils import formatdate
from email import encoders
import mailbox
import uno
import unohelper
from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
from com.sun.star.awt.MessageBoxResults import YES
2020-11-12 15:33:31 -06:00
from com.sun.star.awt import Rectangle, Size, Point
2020-11-12 19:35:29 -06:00
from com.sun.star.awt.PosSize import POSSIZE
2020-11-12 15:33:31 -06:00
from com.sun.star.awt import Key, KeyModifier, KeyEvent
from com.sun.star.container import NoSuchElementException
2019-10-18 22:13:43 -05:00
from com.sun.star.datatransfer import XTransferable, DataFlavor
2020-11-12 15:33:31 -06:00
from com.sun.star.beans import PropertyValue, NamedValue
from com.sun.star.sheet import TableFilterField
2019-10-18 22:13:43 -05:00
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
2020-11-12 15:33:31 -06:00
from com.sun.star.util import Time, Date, DateTime
2019-10-18 22:13:43 -05:00
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
from com.sun.star.awt import XActionListener
2020-11-12 15:33:31 -06:00
from com.sun.star.lang import XEventListener
2019-10-18 22:13:43 -05:00
from com.sun.star.awt import XMouseListener
from com.sun.star.awt import XMouseMotionListener
from com.sun.star.awt import XFocusListener
2020-11-17 22:54:10 -06:00
from com.sun.star.awt import XKeyListener
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
# ~ 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
2019-10-28 22:14:24 -06:00
2019-10-18 22:13:43 -05:00
try:
2020-11-12 15:33:31 -06:00
from peewee import Database, DateTimeField, DateField, TimeField, \
__exception_wrapper__
except ImportError as e:
Database = DateField = TimeField = DateTimeField = object
print('Install peewee')
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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__)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
# ~ You can get custom salt
# ~ codecs.encode(os.urandom(16), 'hex')
SALT = b'c9548699d4e432dfd2b46adddafbb06d'
2020-03-09 21:36:33 -06:00
2020-11-12 15:33:31 -06:00
TIMEOUT = 10
2019-10-18 22:13:43 -05:00
LOG_NAME = 'ZAZ'
2020-11-12 15:33:31 -06:00
FILE_NAME_CONFIG = 'zaz-{}.json'
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
LEFT = 0
CENTER = 1
RIGHT = 2
2020-03-09 21:36:33 -06:00
2019-10-18 22:13:43 -05:00
CALC = 'calc'
WRITER = 'writer'
2020-11-12 15:33:31 -06:00
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',
BASE: 'com.sun.star.sdb.DocumentDataSource',
MATH: 'com.sun.star.formula.FormulaProperties',
BASIC: 'com.sun.star.script.BasicIDE',
MAIN: 'com.sun.star.frame.StartModule',
}
2019-11-12 19:30:28 -06:00
2019-10-18 22:13:43 -05:00
OBJ_CELL = 'ScCellObj'
OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj'
2020-11-12 15:33:31 -06:00
TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
OBJ_SHAPES = 'com.sun.star.drawing.SvxShapeCollection'
OBJ_SHAPE = 'com.sun.star.comp.sc.ScShapeObj'
OBJ_GRAPHIC = 'SwXTextGraphicObject'
2019-11-12 19:30:28 -06:00
2020-11-12 15:33:31 -06:00
OBJ_TEXTS = 'SwXTextRanges'
OBJ_TEXT = 'SwXTextRange'
# ~ 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
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)
2020-11-12 23:10:42 -06:00
PYTHON = 'python'
if IS_WIN:
PYTHON = 'python.exe'
2020-11-12 15:33:31 -06:00
_MACROS = {}
_start = 0
SECONDS_DAY = 60 * 60 * 24
DIR = {
'images': 'images',
'locales': 'locales',
}
DEFAULT_MIME_TYPE = 'png'
2020-11-17 22:54:10 -06:00
KEY = {
'enter': 1280,
}
2020-11-12 15:33:31 -06:00
MODIFIERS = {
'shift': KeyModifier.SHIFT,
'ctrl': KeyModifier.MOD1,
'alt': KeyModifier.MOD2,
'ctrlmac': KeyModifier.MOD3,
2019-10-18 22:13:43 -05:00
}
2020-11-12 15:33:31 -06:00
# ~ Menus
2019-10-18 22:13:43 -05:00
NODE_MENUBAR = 'private:resource/menubar/menubar'
2020-11-12 15:33:31 -06:00
MENUS = {
2019-10-18 22:13:43 -05:00
'file': '.uno:PickList',
'tools': '.uno:ToolsMenu',
'help': '.uno:HelpMenu',
2020-11-12 15:33:31 -06:00
'windows': '.uno:WindowList',
2019-10-18 22:13:43 -05:00
'edit': '.uno:EditMenu',
'view': '.uno:ViewMenu',
'insert': '.uno:InsertMenu',
'format': '.uno:FormatMenu',
'styles': '.uno:FormatStylesMenu',
'sheet': '.uno:SheetMenu',
'data': '.uno:DataMenu',
2020-11-12 15:33:31 -06:00
'table': '.uno:TableMenu',
'form': '.uno:FormatFormMenu',
'page': '.uno:PageMenu',
'shape': '.uno:ShapeMenu',
'slide': '.uno:SlideMenu',
'show': '.uno:SlideShowMenu',
2019-10-18 22:13:43 -05:00
}
2020-11-12 15:33:31 -06:00
MIME_TYPE = {
'png': 'image/png',
'jpg': 'image/jpeg',
2019-10-18 22:13:43 -05:00
}
2020-11-12 15:33:31 -06:00
MESSAGES = {
'es': {
'OK': 'Aceptar',
'Cancel': 'Cancelar',
'Select path': 'Seleccionar ruta',
'Select directory': 'Seleccionar directorio',
'Select file': 'Seleccionar archivo',
'Incorrect user or password': 'Nombre de usuario o contraseƱa invƔlidos',
'Allow less secure apps in GMail': 'Activa: Permitir aplicaciones menos segura en GMail',
}
}
2019-10-18 22:13:43 -05:00
CTX = uno.getComponentContext()
SM = CTX.getServiceManager()
2020-11-12 15:33:31 -06:00
def create_instance(name: str, with_context: bool=False, args: Any=None) -> Any:
2019-10-18 22:13:43 -05:00
if with_context:
instance = SM.createInstanceWithContext(name, CTX)
2020-11-12 15:33:31 -06:00
elif args:
instance = SM.createInstanceWithArguments(name, (args,))
2019-10-18 22:13:43 -05:00
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')
2020-11-12 15:33:31 -06:00
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')
2019-10-18 22:13:43 -05:00
DATE_OFFSET = datetime.date(y, m, d).toordinal()
2020-11-12 15:33:31 -06:00
def error(info):
log.error(info)
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def debug(*args):
data = [str(a) for a in args]
log.debug('\t'.join(data))
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def info(*args):
data = [str(a) for a in args]
log.info('\t'.join(data))
return
def save_log(path, data):
with open(path, 'a') as f:
f.write(f'{str(now())[:19]} -{LOG_NAME}- ')
pprint(data, stream=f)
2019-11-12 19:30:28 -06:00
return
2019-10-18 22:13:43 -05:00
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:
2020-11-12 23:10:42 -06:00
msgbox(traceback.format_exc())
2019-10-18 22:13:43 -05:00
log.error(name, exc_info=True)
return func
2020-11-12 15:33:31 -06:00
def inspect(obj: Any) -> None:
zaz = create_instance('net.elmau.zaz.inspect')
if hasattr(obj, 'obj'):
obj = obj.obj
zaz.inspect(obj)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def mri(obj):
m = create_instance('mytools.Mri')
if m is None:
msg = 'Extension MRI not found'
error(msg)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
m.inspect(obj)
2019-10-18 22:13:43 -05:00
return
def run_in_thread(fn):
def run(*k, **kw):
t = threading.Thread(target=fn, args=k, kwargs=kw)
t.start()
return t
return run
2019-10-28 22:14:24 -06:00
def now(only_time=False):
now = datetime.datetime.now()
if only_time:
2020-11-12 15:33:31 -06:00
now = now.time()
2019-10-28 22:14:24 -06:00
return now
2019-10-18 22:13:43 -05:00
def today():
return datetime.date.today()
def _(msg):
2020-11-12 15:33:31 -06:00
if LANG == 'en':
2019-10-18 22:13:43 -05:00
return msg
2020-11-12 15:33:31 -06:00
if not LANG in MESSAGES:
2019-10-18 22:13:43 -05:00
return msg
2020-11-12 15:33:31 -06:00
return MESSAGES[LANG][msg]
2019-10-18 22:13:43 -05:00
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()
2020-11-12 15:33:31 -06:00
box = toolkit.createMessageBox(parent, type_msg, buttons, title, str(message))
return box.execute()
2019-10-18 22:13:43 -05:00
def question(message, title=TITLE):
2020-11-12 15:33:31 -06:00
result = msgbox(message, title, MSG_BUTTONS.BUTTONS_YES_NO, 'querybox')
return result == YES
2019-10-18 22:13:43 -05:00
def warning(message, title=TITLE):
return msgbox(message, title, type_msg='warningbox')
def errorbox(message, title=TITLE):
return msgbox(message, title, type_msg='errorbox')
2020-11-12 15:33:31 -06:00
def get_type_doc(obj: Any) -> str:
for k, v in TYPE_DOC.items():
if obj.supportsService(v):
return k
return ''
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _array_to_dict(values):
d = {v[0]: v[1] for v in values}
return d
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _property_to_dict(values):
d = {v.Name: v.Value for v in values}
return d
2019-10-18 22:13:43 -05:00
2020-11-12 19:35:29 -06:00
def json_dumps(data):
return json.dumps(data, indent=4, sort_keys=True)
def json_loads(data):
return json.loads(data)
2020-11-12 15:33:31 -06:00
def data_to_dict(data):
if isinstance(data, tuple) and isinstance(data[0], tuple):
return _array_to_dict(data)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
if isinstance(data, tuple) and isinstance(data[0], (PropertyValue, NamedValue)):
return _property_to_dict(data)
return {}
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _get_dispatch() -> Any:
return create_instance('com.sun.star.frame.DispatchHelper')
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2020-11-12 15:33:31 -06:00
def get_desktop():
return create_instance('com.sun.star.frame.Desktop', True)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
return d
2020-11-12 15:33:31 -06:00
def _get_url_script(args):
library = args['library']
module = '.'
name = args['name']
language = args.get('language', 'Python')
location = args.get('location', 'user')
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
if language == 'Python':
module = '.py$'
elif language == 'Basic':
module = f".{module}."
if location == 'user':
location = 'application'
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
url = 'vnd.sun.star.script'
url = f'{url}:{library}{module}{name}?language={language}&location={location}'
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
return url
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _call_macro(args):
#~ https://wiki.openoffice.org/wiki/Documentation/DevGuide/Scripting/Scripting_Framework_URI_Specification
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
url = _get_url_script(args)
args = args.get('args', ())
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
service = 'com.sun.star.script.provider.MasterScriptProviderFactory'
factory = create_instance(service)
script = factory.createScriptProvider('').getScript(url)
result = script.invoke(args, None, None)[0]
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
return result
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def call_macro(args, in_thread=False):
result = None
if in_thread:
t = threading.Thread(target=_call_macro, args=(args,))
t.start()
else:
result = _call_macro(args)
return result
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def run(command, capture=False, split=True):
if not split:
return subprocess.check_output(command, shell=True).decode()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
cmd = shlex.split(command)
2020-11-12 23:10:42 -06:00
result = subprocess.run(cmd, capture_output=capture, text=True, shell=IS_WIN)
2020-11-12 15:33:31 -06:00
if capture:
result = result.stdout
else:
result = result.returncode
return result
2019-10-18 22:13:43 -05:00
2020-11-12 23:10:42 -06:00
def popen(command):
try:
proc = subprocess.Popen(shlex.split(command), shell=IS_WIN,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in proc.stdout:
yield line.decode().rstrip()
except Exception as e:
error(e)
yield (e.errno, e.strerror)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def sleep(seconds):
time.sleep(seconds)
return
class TimerThread(threading.Thread):
def __init__(self, event, seconds, macro):
threading.Thread.__init__(self)
self.stopped = event
self.seconds = seconds
self.macro = macro
def run(self):
info('Timer started... {}'.format(self.macro['name']))
while not self.stopped.wait(self.seconds):
_call_macro(self.macro)
info('Timer stopped... {}'.format(self.macro['name']))
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def start_timer(name, seconds, macro):
global _MACROS
_MACROS[name] = threading.Event()
thread = TimerThread(_MACROS[name], seconds, macro)
thread.start()
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def stop_timer(name):
global _MACROS
_MACROS[name].set()
del _MACROS[name]
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def install_locales(path, domain='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 _
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def sha256(data):
result = hashlib.sha256(data.encode()).hexdigest()
return result
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def sha512(data):
result = hashlib.sha512(data.encode()).hexdigest()
return result
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def get_config(key='', default={}, prefix='conf'):
name_file = FILE_NAME_CONFIG.format(prefix)
values = None
path = _P.join(_P.config('UserConfig'), name_file)
if not _P.exists(path):
return default
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
values = _P.from_json(path)
if key:
values = values.get(key, default)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
return values
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def set_config(key, value, prefix='conf'):
name_file = FILE_NAME_CONFIG.format(prefix)
path = _P.join(_P.config('UserConfig'), name_file)
values = get_config(default={}, prefix=prefix)
values[key] = value
result = _P.to_json(path, values)
return result
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def start():
global _start
_start = now()
info(_start)
return
def end(get_seconds=False):
global _start
e = now()
td = e - _start
result = str(td)
if get_seconds:
result = td.total_seconds()
return result
def get_epoch():
n = now()
return int(time.mktime(n.timetuple()))
def render(template, data):
s = Template(template)
return s.safe_substitute(**data)
def get_size_screen():
if IS_WIN:
user32 = ctypes.windll.user32
res = f'{user32.GetSystemMetrics(0)}x{user32.GetSystemMetrics(1)}'
else:
args = 'xrandr | grep "*" | cut -d " " -f4'
res = run(args, split=False)
return res.strip()
def url_open(url, data=None, headers={}, verify=True, get_json=False):
err = ''
req = Request(url)
for k, v in headers.items():
req.add_header(k, v)
try:
# ~ debug(url)
if verify:
if not data is None and isinstance(data, str):
data = data.encode()
response = urlopen(req, data=data)
2019-10-18 22:13:43 -05:00
else:
2020-11-12 15:33:31 -06:00
context = ssl._create_unverified_context()
response = urlopen(req, context=context)
except HTTPError as e:
error(e)
err = str(e)
except URLError as e:
error(e.reason)
err = str(e.reason)
else:
headers = dict(response.info())
result = response.read()
if get_json:
result = json.loads(result)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
return result, headers, err
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _get_key(password):
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
2020-03-10 11:50:13 -06:00
2020-11-12 15:33:31 -06:00
kdf = PBKDF2HMAC(algorithm=hashes.SHA256(), length=32, salt=SALT,
iterations=100000)
key = base64.urlsafe_b64encode(kdf.derive(password.encode()))
return key
2019-10-18 22:13:43 -05:00
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def encrypt(data, password):
from cryptography.fernet import Fernet
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
f = Fernet(_get_key(password))
if isinstance(data, str):
data = data.encode()
token = f.encrypt(data).decode()
return token
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
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
class SmtpServer(object):
def __init__(self, config):
self._server = None
self._error = ''
self._sender = ''
self._is_connect = self._login(config)
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
2019-10-28 22:14:24 -06:00
@property
2020-11-12 15:33:31 -06:00
def is_connect(self):
return self._is_connect
2019-10-28 22:14:24 -06:00
@property
2020-11-12 15:33:31 -06:00
def error(self):
return self._error
def _login(self, config):
name = config['server']
port = config['port']
is_ssl = config['ssl']
self._sender = config['user']
hosts = ('gmail' in name or 'outlook' in name)
try:
if is_ssl and hosts:
self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
self._server.ehlo()
self._server.starttls()
self._server.ehlo()
elif is_ssl:
self._server = smtplib.SMTP_SSL(name, port, timeout=TIMEOUT)
self._server.ehlo()
2019-10-28 22:14:24 -06:00
else:
2020-11-12 15:33:31 -06:00
self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
self._server.login(self._sender, config['password'])
msg = 'Connect to: {}'.format(name)
debug(msg)
return True
except smtplib.SMTPAuthenticationError as e:
if '535' in str(e):
self._error = _('Incorrect user or password')
return False
if '534' in str(e) and 'gmail' in name:
self._error = _('Allow less secure apps in GMail')
return False
except smtplib.SMTPException as e:
self._error = str(e)
return False
except Exception as e:
self._error = str(e)
return False
return False
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def _body(self, msg):
body = msg.replace('\\n', '<BR>')
return body
def send(self, message):
file_name = 'attachment; filename={}'
email = MIMEMultipart()
email['From'] = self._sender
email['To'] = message['to']
email['Cc'] = message.get('cc', '')
email['Subject'] = message['subject']
email['Date'] = formatdate(localtime=True)
if message.get('confirm', False):
email['Disposition-Notification-To'] = email['From']
email.attach(MIMEText(self._body(message['body']), 'html'))
for path in message.get('files', ()):
fn = _P(path).file_name
part = MIMEBase('application', 'octet-stream')
part.set_payload(_P.read_bin(path))
encoders.encode_base64(part)
part.add_header('Content-Disposition', f'attachment; filename={fn}')
email.attach(part)
receivers = (
email['To'].split(',') +
email['CC'].split(',') +
message.get('bcc', '').split(','))
try:
self._server.sendmail(self._sender, receivers, email.as_string())
msg = 'Email sent...'
debug(msg)
if message.get('path', ''):
self.save_message(email, message['path'])
return True
except Exception as e:
self._error = str(e)
return False
return False
def save_message(self, email, path):
mbox = mailbox.mbox(path, create=True)
mbox.lock()
try:
msg = mailbox.mboxMessage(email)
mbox.add(msg)
mbox.flush()
finally:
mbox.unlock()
2019-10-28 22:14:24 -06:00
return
2020-11-12 15:33:31 -06:00
def close(self):
try:
self._server.quit()
msg = 'Close connection...'
debug(msg)
except:
pass
return
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def _send_email(server, messages):
with SmtpServer(server) as server:
if server.is_connect:
for msg in messages:
server.send(msg)
else:
error(server.error)
return server.error
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def send_email(server, message):
messages = message
if isinstance(message, dict):
messages = (message,)
t = threading.Thread(target=_send_email, args=(server, messages))
t.start()
return
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
# ~ Classes
class LOBaseObject(object):
2019-10-18 22:13:43 -05:00
def __init__(self, obj):
2020-11-12 15:33:31 -06:00
self._obj = obj
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __setattr__(self, name, value):
exists = hasattr(self, name)
if not exists and not name in ('_obj', '_index'):
setattr(self._obj, name, value)
2019-10-28 22:14:24 -06:00
else:
2020-11-12 15:33:31 -06:00
super().__setattr__(name, value)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __enter__(self):
return self
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __exit__(self, exc_type, exc_value, traceback):
pass
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def obj(self):
return self._obj
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOImage(object):
TYPE = {
'png': 'image/png',
'jpg': 'image/jpeg',
}
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
self._obj = obj
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def obj(self):
return self._obj
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def name(self):
return self.obj.Name or 'img'
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def mimetype(self):
return self.obj.Bitmap.MimeType
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def save(self, path, mimetype=DEFAULT_MIME_TYPE):
p = _P(path)
if _P.is_dir(path):
name = self.name
else:
path = p.path
name = p.name
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
path = _P.join(path, f'{name}.{mimetype.lower()}')
args = dict(
URL = _P.to_url(path),
MimeType = self.TYPE[mimetype],
)
if not _export_image(self.obj, args):
path = ''
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
# ~ size = len(self.obj.Bitmap.DIB)
# ~ data = self.obj.GraphicStream.readBytes((), size)
# ~ data = data[-1].value
# ~ data = self.obj.Bitmap.DIB.value
# ~ data = self.obj.Graphic.DIB.value
# ~ _P.save_bin(path, data)
return path
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LODocument(object):
FILTERS = {
'doc': 'MS Word 97',
'docx': 'MS Word 2007 XML',
}
2019-10-18 22:13:43 -05:00
def __init__(self, obj):
2020-11-12 15:33:31 -06:00
self._obj = obj
self._cc = self.obj.getCurrentController()
self._undo = True
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.close()
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def obj(self):
return self._obj
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def title(self):
return self.obj.getTitle()
@title.setter
def title(self, value):
self.obj.setTitle(value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def type(self):
return self._type
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def uid(self):
return self.obj.RuntimeUID
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def frame(self):
return self._cc.getFrame()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def is_saved(self):
return self.obj.hasLocation()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def is_modified(self):
return self.obj.isModified()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def is_read_only(self):
return self.obj.isReadOnly()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def path(self):
return _P.to_system(self.obj.URL)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def dir(self):
return _P(self.path).path
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def file_name(self):
return _P(self.path).file_name
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def name(self):
return _P(self.path).name
@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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def undo(self):
return self._undo
@undo.setter
def undo(self, value):
self._undo = value
um = self.obj.UndoManager
if value:
try:
um.leaveUndoContext()
except:
pass
else:
um.enterHiddenUndoContext()
def clear_undo(self):
self.obj.getUndoManager().clear()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
@property
def selection(self):
sel = self.obj.CurrentSelection
# ~ return _get_class_uno(sel)
return sel
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def table_auto_formats(self):
taf = create_instance('com.sun.star.sheet.TableAutoFormats')
return taf.ElementNames
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def create_instance(self, name):
obj = self.obj.createInstance(name)
return obj
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def set_focus(self):
w = self.frame.ComponentWindow
w.setFocus()
return
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def copy(self):
call_dispatch(self.frame, '.uno:Copy')
return
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def paste(self):
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
transferable = sc.getContents()
self._cc.insertTransferable(transferable)
# ~ return self.obj.getCurrentSelection()
return
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def select(self, obj):
self._cc.select(obj)
return
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def to_pdf(self, path: str='', args: dict={}):
path_pdf = path
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(_P.to_url(path), opt)
except Exception as e:
error(e)
path_pdf = ''
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
return _P.exists(path_pdf)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def export(self, path: str, ext: str='', args: dict={}):
if not ext:
ext = _P(path).ext
filter_name = self.FILTERS[ext]
filter_data = dict_to_property(args, True)
args = {
'FilterName': filter_name,
'FilterData': filter_data,
}
opt = dict_to_property(args)
try:
self.obj.storeToURL(_P.to_url(path), opt)
except Exception as e:
error(e)
path = ''
return _P.exists(path)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def save(self, path: str='', args: dict={}) -> bool:
result = True
opt = dict_to_property(args)
if path:
try:
2020-11-12 19:35:29 -06:00
self.obj.storeAsURL(_P.to_url(path), opt)
2020-11-12 15:33:31 -06:00
except Exception as e:
error(e)
result = False
2019-10-28 22:14:24 -06:00
else:
2020-11-12 15:33:31 -06:00
self.obj.store()
return result
def close(self):
self.obj.close(True)
return
2019-10-28 22:14:24 -06:00
2020-11-17 22:54:10 -06:00
class LOCellStyle(LOBaseObject):
def __init__(self, obj):
super().__init__(obj)
@property
def name(self):
return self.obj.Name
@property
def properties(self):
properties = self.obj.PropertySetInfo.Properties
data = {p.Name: getattr(self.obj, p.Name) for p in properties}
return data
@properties.setter
def properties(self, values):
_set_properties(self.obj, values)
class LOCellStyles(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
def __len__(self):
return len(self.obj)
def __getitem__(self, index):
return LOCellStyle(self.obj[index])
def __setitem__(self, key, value):
self.obj[key] = value
def __delitem__(self, key):
if not isinstance(key, str):
key = key.Name
del self.obj[key]
def __contains__(self, item):
return item in self.obj
@property
def obj(self):
return self._obj
@property
def names(self):
return self.obj.ElementNames
def new(self, name: str=''):
obj = self._doc.create_instance('com.sun.star.style.CellStyle')
if name:
self.obj[name] = obj
obj = LOCellStyle(obj)
return obj
2019-10-18 22:13:43 -05:00
class LOCalc(LODocument):
def __init__(self, obj):
super().__init__(obj)
2020-11-12 15:33:31 -06:00
self._type = CALC
self._sheets = obj.Sheets
2019-10-18 22:13:43 -05:00
def __getitem__(self, index):
2020-11-12 15:33:31 -06:00
return LOCalcSheet(self._sheets[index])
2019-10-18 22:13:43 -05:00
def __setitem__(self, key, value):
self._sheets[key] = value
2020-11-12 15:33:31 -06:00
def __len__(self):
return self._sheets.Count
2019-10-18 22:13:43 -05:00
def __contains__(self, item):
2020-11-12 15:33:31 -06:00
return item in self._sheets
@property
def names(self):
names = self.obj.Sheets.ElementNames
return names
@property
def selection(self):
sel = self.obj.CurrentSelection
if sel.ImplementationName in TYPE_RANGES:
sel = LOCalcRange(sel)
elif sel.ImplementationName == OBJ_SHAPES:
if len(sel) == 1:
sel = sel[0]
sel = LODrawPage(sel.Parent)[sel.Name]
else:
debug(sel.ImplementationName)
return sel
@property
def active(self):
return LOCalcSheet(self._cc.ActiveSheet)
2019-10-18 22:13:43 -05:00
@property
def headers(self):
return self._cc.ColumnRowHeaders
@headers.setter
def headers(self, value):
self._cc.ColumnRowHeaders = value
@property
def tabs(self):
return self._cc.SheetTabs
@tabs.setter
def tabs(self, value):
self._cc.SheetTabs = value
2020-11-17 22:54:10 -06:00
@property
def cs(self):
return self.cell_styles
@property
def cell_styles(self):
obj = self.obj.StyleFamilies['CellStyles']
return LOCellStyles(obj, self)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def db_ranges(self):
# ~ return LOCalcDataBaseRanges(self.obj.DataBaseRanges)
return self.obj.DatabaseRanges
2019-10-18 22:13:43 -05:00
def activate(self, sheet):
obj = sheet
if isinstance(sheet, LOCalcSheet):
obj = sheet.obj
elif isinstance(sheet, str):
2020-11-12 15:33:31 -06:00
obj = self._sheets[sheet]
2019-10-18 22:13:43 -05:00
self._cc.setActiveSheet(obj)
return
2020-11-12 15:33:31 -06:00
def new_sheet(self):
s = self.create_instance('com.sun.star.sheet.Spreadsheet')
return s
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def insert(self, name):
names = name
2019-10-18 22:13:43 -05:00
if isinstance(name, str):
2020-11-12 15:33:31 -06:00
names = (name,)
for n in names:
self._sheets[n] = self.new_sheet()
return LOCalcSheet(self._sheets[n])
2019-10-18 22:13:43 -05:00
def move(self, name, pos=-1):
2020-11-12 15:33:31 -06:00
index = pos
if pos < 0:
index = len(self)
if isinstance(name, LOCalcSheet):
name = name.name
self._sheets.moveByName(name, index)
return
2019-10-18 22:13:43 -05:00
def remove(self, name):
2020-11-12 15:33:31 -06:00
if isinstance(name, LOCalcSheet):
name = name.name
self._sheets.removeByName(name)
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def copy(self, name, new_name='', pos=-1):
if isinstance(name, LOCalcSheet):
name = name.name
2019-10-18 22:13:43 -05:00
index = pos
if pos < 0:
2020-11-12 15:33:31 -06:00
index = len(self)
self._sheets.copyByName(name, new_name, index)
return LOCalcSheet(self._sheets[new_name])
2019-10-18 22:13:43 -05:00
def copy_from(self, doc, source='', target='', pos=-1):
index = pos
if pos < 0:
2020-11-12 15:33:31 -06:00
index = len(self)
2019-10-18 22:13:43 -05:00
names = source
2020-11-12 15:33:31 -06:00
if not source:
2019-10-18 22:13:43 -05:00
names = doc.names
elif isinstance(source, str):
names = (source,)
new_names = target
if not target:
new_names = names
elif isinstance(target, str):
new_names = (target,)
2020-11-12 15:33:31 -06:00
for i, name in enumerate(names):
self._sheets.importSheet(doc.obj, name, index + i)
self[index + i].name = new_names[i]
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
return LOCalcSheet(self._sheets[index])
2019-10-18 22:13:43 -05:00
def sort(self, reverse=False):
names = sorted(self.names, reverse=reverse)
for i, n in enumerate(names):
2020-11-12 15:33:31 -06:00
self.move(n, i)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def render(self, data, sheet=None, clean=True):
if sheet is None:
sheet = self.active
return sheet.render(data, clean=clean)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOChart(object):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __getitem__(self, index):
return LOBaseObject(self.diagram.getDataRowProperties(index))
def __enter__(self):
return self
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __exit__(self, exc_type, exc_value, traceback):
pass
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def obj(self):
return self._obj
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@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):
2019-10-18 22:13:43 -05:00
self._obj = obj
2020-11-12 15:33:31 -06:00
self._sheet = sheet
2019-10-18 22:13:43 -05:00
def __getitem__(self, index):
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
@property
def obj(self):
return self._obj
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
@property
def doc(self):
2020-11-12 15:33:31 -06:00
return self.obj.Parent.Parent.Parent
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def name(self):
return self.obj.Name
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def label(self):
return self.obj.Label
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def set_focus(self):
self._control.setFocus()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
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 LOSheetRows(object):
def __init__(self, sheet):
self._sheet = sheet
self._obj = sheet.obj.Rows
def __getitem__(self, index):
return LOSheetRows(self.obj[index])
@property
def obj(self):
return self._obj
def insert(self, index, count):
self.obj.insertByIndex(index, count)
end = index + count
return self._sheet[index:end,0:]
2019-10-18 22:13:43 -05:00
class LOCalcSheet(object):
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
2019-10-18 22:13:43 -05:00
self._obj = obj
def __getitem__(self, index):
2020-11-12 15:33:31 -06:00
return LOCalcRange(self.obj[index])
2019-10-18 22:13:43 -05:00
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
2020-11-12 15:33:31 -06:00
def __str__(self):
return f'easymacro.LOCalcSheet: {self.name}'
2019-10-18 22:13:43 -05:00
@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 code_name(self):
return self._obj.CodeName
@code_name.setter
def code_name(self, value):
self._obj.CodeName = value
@property
def visible(self):
2020-11-12 15:33:31 -06:00
return self._obj.IsVisible
2019-10-18 22:13:43 -05:00
@visible.setter
def visible(self, value):
2020-11-12 15:33:31 -06:00
self._obj.IsVisible = value
2019-10-18 22:13:43 -05:00
@property
def is_protected(self):
return self._obj.isProtected()
@property
def password(self):
return ''
@visible.setter
def password(self, value):
self.obj.protect(value)
def unprotect(self, value):
try:
self.obj.unprotect(value)
return True
except:
pass
return False
@property
2020-11-12 15:33:31 -06:00
def color(self):
return self._obj.TabColor
@color.setter
def color(self, value):
self._obj.TabColor = get_color(value)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def used_area(self):
cursor = self.get_cursor()
cursor.gotoEndOfUsedArea(True)
return LOCalcRange(self[cursor.AbsoluteName].obj)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def draw_page(self):
return LODrawPage(self.obj.DrawPage)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def dp(self):
return self.draw_page
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def shapes(self):
return self.draw_page
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def doc(self):
return LOCalc(self.obj.DrawPage.Forms.Parent)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def charts(self):
return LOSheetCharts(self.obj.Charts, self)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def rows(self):
return LOSheetRows(self)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def forms(self):
return LOSheetForms(self.obj.DrawPage.Forms)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def activate(self):
self.doc.activate(self._obj)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def clean(self):
doc = self.doc
sheet = doc.create_instance('com.sun.star.sheet.Spreadsheet')
doc._sheets.replaceByName(self.name, sheet)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def move(self, pos=-1):
index = pos
if pos < 0:
index = len(self.doc)
self.doc._sheets.moveByName(self.name, index)
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def remove(self):
self.doc._sheets.removeByName(self.name)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def copy(self, new_name='', pos=-1):
index = pos
if pos < 0:
index = len(self.doc)
self.doc._sheets.copyByName(self.name, new_name, index)
return LOCalcSheet(self.doc._sheets[new_name])
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def copy_to(self, doc, target='', pos=-1):
index = pos
if pos < 0:
index = len(doc)
name = self.name
if not target:
new_name = name
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
doc._sheets.importSheet(self.doc.obj, name, index)
sheet = doc[name]
sheet.name = new_name
return sheet
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def get_cursor(self, cell=None):
if cell is None:
cursor = self.obj.createCursor()
2019-10-28 22:14:24 -06:00
else:
2020-11-12 15:33:31 -06:00
cursor = self.obj.createCursorByRange(cell)
return cursor
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def render(self, data, rango=None, clean=True):
if rango is None:
rango = self.used_area
return rango.render(data, clean)
2019-10-28 22:14:24 -06:00
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOCalcRows(object):
2019-10-18 22:13:43 -05:00
def __init__(self, obj):
self._obj = obj
2020-11-12 15:33:31 -06:00
def __len__(self):
return self.obj.Count
def __str__(self):
return 'Rows'
2019-10-18 22:13:43 -05:00
@property
def obj(self):
return self._obj
@property
2020-11-12 15:33:31 -06:00
def count(self):
return len(self)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def visible(self):
return self.obj.IsVisible
@visible.setter
def visible(self, value):
self.obj.IsVisible = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOCalcRange(object):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
self._obj = obj
self._sd = None
self._is_cell = obj.ImplementationName == OBJ_CELL
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __getitem__(self, index):
return LOCalcRange(self.obj[index])
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __iter__(self):
self._r = 0
self._c = 0
return self
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __next__(self):
2019-10-18 22:13:43 -05:00
try:
2020-11-12 15:33:31 -06:00
rango = self[self._r, self._c]
2019-10-18 22:13:43 -05:00
except Exception as e:
2020-11-12 15:33:31 -06:00
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
def __str__(self):
if self.is_none:
s = 'Range: None'
else:
s = f'Range: {self.name}'
return s
2019-10-18 22:13:43 -05:00
@property
def obj(self):
return self._obj
@property
2020-11-12 15:33:31 -06:00
def is_none(self):
return self.obj is None
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def is_cell(self):
return self._is_cell
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def back_color(self):
return self._obj.CellBackColor
@back_color.setter
def back_color(self, value):
self._obj.CellBackColor = get_color(value)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def dp(self):
return self.sheet.dp
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def sheet(self):
return LOCalcSheet(self.obj.Spreadsheet)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def doc(self):
doc = self.obj.Spreadsheet.DrawPage.Forms.Parent
return LODocument(doc)
2019-11-12 19:30:28 -06:00
2020-11-12 15:33:31 -06:00
@property
def name(self):
return self.obj.AbsoluteName
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def code_name(self):
name = self.name.replace('$', '').replace('.', '_').replace(':', '')
return name
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def columns(self):
return self.obj.Columns.Count
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def rows(self):
return LOCalcRows(self.obj.Rows)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def type(self):
return self.obj.Type
2019-10-18 22:13:43 -05:00
@property
def value(self):
v = None
2020-11-12 15:33:31 -06:00
if self.type == VALUE:
2019-10-18 22:13:43 -05:00
v = self.obj.getValue()
2020-11-12 15:33:31 -06:00
elif self.type == TEXT:
2019-10-18 22:13:43 -05:00
v = self.obj.getString()
2020-11-12 15:33:31 -06:00
elif self.type == FORMULA:
2019-10-18 22:13:43 -05:00
v = self.obj.getFormula()
return v
@value.setter
def value(self, data):
if isinstance(data, str):
2020-11-12 15:33:31 -06:00
# ~ print(isinstance(data, str), data[0])
if data[0] in '=':
2019-10-18 22:13:43 -05:00
self.obj.setFormula(data)
2020-11-12 15:33:31 -06:00
# ~ print('Set Formula')
2019-10-18 22:13:43 -05:00
else:
self.obj.setString(data)
2020-11-12 15:33:31 -06:00
elif isinstance(data, Decimal):
self.obj.setValue(float(data))
2019-11-12 19:30:28 -06:00
elif isinstance(data, (int, float, bool)):
2019-10-18 22:13:43 -05:00
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
2020-11-12 15:33:31 -06:00
def date(self):
value = int(self.obj.Value)
date = datetime.date.fromordinal(value + DATE_OFFSET)
return date
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def time(self):
seconds = self.obj.Value * SECONDS_DAY
time_delta = datetime.timedelta(seconds=seconds)
time = (datetime.datetime.min + time_delta).time()
return time
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def datetime(self):
return datetime.datetime.combine(self.date, self.time)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def data(self):
return self.obj.getDataArray()
@data.setter
def data(self, values):
if self._is_cell:
self.to_size(len(values), len(values[0])).data = values
2019-10-18 22:13:43 -05:00
else:
2020-11-12 15:33:31 -06:00
self.obj.setDataArray(values)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def formula(self):
return self.obj.getFormulaArray()
@formula.setter
def formula(self, values):
self.obj.setFormulaArray(values)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def array_formula(self):
return self.obj.ArrayFormula
@array_formula.setter
def array_formula(self, value):
self.obj.ArrayFormula = value
2019-10-18 22:13:43 -05:00
@property
def address(self):
2020-11-12 15:33:31 -06:00
return self.obj.CellAddress
2019-10-18 22:13:43 -05:00
2019-10-28 22:14:24 -06:00
@property
def range_address(self):
2020-11-12 15:33:31 -06:00
return self.obj.RangeAddress
@property
def cursor(self):
cursor = self.obj.Spreadsheet.createCursorByRange(self.obj)
return cursor
2019-10-28 22:14:24 -06:00
2019-10-18 22:13:43 -05:00
@property
def current_region(self):
2020-11-12 15:33:31 -06:00
cursor = self.cursor
2019-10-18 22:13:43 -05:00
cursor.collapseToCurrentRegion()
2020-11-12 15:33:31 -06:00
return LOCalcRange(self.sheet[cursor.AbsoluteName].obj)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def next_cell(self):
a = self.current_region.range_address
col = a.StartColumn
row = a.EndRow + 1
return LOCalcRange(self.sheet[row, col].obj)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def position(self):
return self.obj.Position
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def size(self):
return self.obj.Size
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def possize(self):
data = {
'Width': self.size.Width,
'Height': self.size.Height,
'X': self.position.X,
'Y': self.position.Y,
}
return data
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def visible(self):
cursor = self.cursor
rangos = cursor.queryVisibleCells()
rangos = [LOCalcRange(self.sheet[r.AbsoluteName].obj) for r in rangos]
return tuple(rangos)
2019-10-18 22:13:43 -05:00
def select(self):
2020-11-12 15:33:31 -06:00
self.doc.select(self.obj)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def offset(self, rows=0, cols=1):
ra = self.range_address
col = ra.EndColumn + cols
row = ra.EndRow + rows
return LOCalcRange(self.sheet[row, col].obj)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def to_size(self, rows, cols):
cursor = self.cursor
cursor.collapseToSize(cols, rows)
return LOCalcRange(self.sheet[cursor.AbsoluteName].obj)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def copy_to(self, cell, formula=False):
rango = cell.to_size(self.rows, self.columns)
if formula:
rango.formula = self.data
2019-10-18 22:13:43 -05:00
else:
2020-11-12 15:33:31 -06:00
rango.data = self.data
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
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
2019-10-28 22:14:24 -06:00
else:
2020-11-12 15:33:31 -06:00
self.to_size(rows, cols).data = data
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def auto_width(self):
self.obj.Columns.OptimalWidth = True
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def clean_render(self, template='\{(\w.+)\}'):
self._sd.SearchRegularExpression = True
self._sd.setSearchString(template)
self.obj.replaceAll(self._sd)
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _render_value(self, key, value, parent=''):
cell = None
if isinstance(value, dict):
for k, v in value.items():
cell = self._render_value(k, v, key)
return cell
elif isinstance(value, (list, tuple)):
self._render_list(key, value)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
search = f'{{{key}}}'
if parent:
search = f'{{{parent}.{key}}}'
ranges = self.find_all(search)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
for cell in ranges or range(0):
self._set_new_value(cell, search, value)
return LOCalcRange(cell)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _set_new_value(self, cell, search, value):
if not cell.ImplementationName == 'ScCellObj':
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _render_list(self, key, rows):
for row in rows:
for k, v in row.items():
self._render_value(k, v)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
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, 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'])
# ~ img.ResizeWithCell = True
img = self.sheet.dp.insert_image(path, args)
img.Anchor = self.obj
args.clear()
return img
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
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
2019-10-28 22:14:24 -06:00
return
2020-11-12 15:33:31 -06:00
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
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
class LOWriterPageStyle(LOBaseObject):
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __str__(self):
return f'Page Style: {self.name}'
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
@property
def name(self):
return self._obj.Name
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
class LOWriterPageStyles(object):
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __init__(self, styles):
self._styles = styles
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __getitem__(self, index):
return LOWriterPageStyle(self._styles[index])
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def names(self):
return self._styles.ElementNames
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __str__(self):
return '\n'.join(self.names)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOWriterTextRange(object):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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'
2019-10-18 22:13:43 -05:00
2020-11-17 22:54:10 -06:00
def __iter__(self):
self._index = 0
return self
def __next__(self):
for i, p in enumerate(self.obj):
if i == self._index:
obj = LOWriterTextRange(p, self._doc)
self._index += 1
return obj
raise StopIteration
2020-11-12 15:33:31 -06:00
@property
def obj(self):
return self._obj
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def string(self):
return self.obj.String
@string.setter
def string(self, value):
self.obj.String = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def value(self):
return self.string
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def is_table(self):
return self._is_table
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def text(self):
2020-11-17 22:54:10 -06:00
return self.obj.Text
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def cursor(self):
return self.text.createTextCursorByRange(self.obj)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def dp(self):
return self._doc.dp
2019-10-18 22:13:43 -05:00
2020-11-17 22:54:10 -06:00
@property
def is_table(self):
return self._is_table
2020-11-12 15:33:31 -06:00
def offset(self):
cursor = self.cursor.getEnd()
return LOWriterTextRange(cursor, self._doc)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def insert_content(self, data, cursor=None, replace=False):
if cursor is None:
cursor = self.cursor
self.text.insertTextContent(cursor, data, replace)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def insert_image(self, path, args={}):
w = args.get('Width', 1000)
h = args.get('Height', 1000)
image = self._doc.create_instance('com.sun.star.text.GraphicObject')
image.GraphicURL = _P.to_url(path)
image.AnchorType = AS_CHARACTER
image.Width = w
image.Height = h
self.insert_content(image)
return self._doc.dp.last
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOWriterTextRanges(object):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __getitem__(self, index):
2020-11-17 22:54:10 -06:00
for i, p in enumerate(self.obj):
if i == index:
obj = LOWriterTextRange(p, self._doc)
break
return obj
def __iter__(self):
self._index = 0
return self
def __next__(self):
for i, p in enumerate(self.obj):
if i == self._index:
obj = LOWriterTextRange(p, self._doc)
self._index += 1
return obj
raise StopIteration
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def obj(self):
return self._obj
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOWriter(LODocument):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
self._type = WRITER
2019-10-18 22:13:43 -05:00
2020-11-17 22:54:10 -06:00
@property
def text(self):
return LOWriterTextRange(self.obj.Text, self)
@property
def paragraphs(self):
return LOWriterTextRanges(self.obj.Text, self)
2020-11-12 15:33:31 -06:00
@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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
if sel.ImplementationName == OBJ_SHAPES:
if len(sel) == 1:
sel = sel[0]
sel = LODrawPage(sel.Parent)[sel.Name]
return sel
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
if sel.ImplementationName == OBJ_GRAPHIC:
sel = self.dp[sel.Name]
else:
debug(sel.ImplementationName)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
return sel
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def dp(self):
return self.draw_page
@property
def draw_page(self):
return LODrawPage(self.obj.DrawPage)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def view_cursor(self):
return self._cc.ViewCursor
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def cursor(self):
return self.obj.Text.createTextCursor()
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def page_styles(self):
ps = self.obj.StyleFamilies['PageStyles']
return LOWriterPageStyles(ps)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOShape(LOBaseObject):
def __init__(self, obj, index):
self._index = index
super().__init__(obj)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def type(self):
return 'shape'
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def name(self):
return self.obj.Name or f'shape{self.index}'
@name.setter
def name(self, value):
self.obj.Name = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def index(self):
return self._index
@property
2020-11-12 15:33:31 -06:00
def size(self):
s = self.obj.Size
a = dict(Width=s.Width, Height=s.Height)
return a
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def string(self):
return self.obj.String
@string.setter
def string(self, value):
self.obj.String = value
@property
2020-11-12 15:33:31 -06:00
def description(self):
return self.obj.Description
@description.setter
def description(self, value):
self.obj.Description = value
@property
2020-11-12 15:33:31 -06:00
def cell(self):
return self.anchor
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
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
self.obj.Anchor = value
2019-10-18 22:13:43 -05:00
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
2020-11-12 15:33:31 -06:00
self.obj.Visible = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def remove(self):
self.obj.Parent.remove(self.obj)
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
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def name(self):
return self.obj.Name
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def doc(self):
return self.obj.Forms.Parent
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def width(self):
return self.obj.Width
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def height(self):
return self.obj.Height
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def count(self):
return self.obj.Count
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def last(self):
return self[self.count - 1]
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def create_instance(self, name):
return self.doc.createInstance(name)
def add(self, type_shape, args={}):
"""Insert a shape in page, type shapes:
Line
Rectangle
Ellipse
Text
"""
w = args.get('Width', 3000)
h = args.get('Height', 3000)
x = args.get('X', 1000)
y = args.get('Y', 1000)
service = f'com.sun.star.drawing.{type_shape}Shape'
shape = self.create_instance(service)
shape.Size = Size(w, h)
shape.Position = Point(x, y)
index = self.count
shape.Name = f'{type_shape.lower()}{index}'
self.obj.add(shape)
return LOShape(self.obj[index], index)
def remove(self, shape):
if hasattr(shape, 'obj'):
shape = shape.obj
return self.obj.remove(shape)
def insert_image(self, path, args={}):
w = args.get('Width', 3000)
h = args.get('Height', 3000)
x = args.get('X', 1000)
y = args.get('Y', 1000)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
image = self.create_instance('com.sun.star.drawing.GraphicObjectShape')
image.GraphicURL = _P.to_url(path)
image.Size = Size(w, h)
image.Position = Point(x, y)
index = self.count
image.Name = f'image{index}'
self.obj.add(image)
return LOShape(self.obj[index], index)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LODrawImpress(LODocument):
2019-10-18 22:13:43 -05:00
def __init__(self, obj):
super().__init__(obj)
2020-11-12 15:33:31 -06:00
def __getitem__(self, index):
if isinstance(index, int):
page = self.obj.DrawPages[index]
else:
page = self.obj.DrawPages.getByName(index)
return LODrawPage(page)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def selection(self):
sel = self.obj.CurrentSelection[0]
# ~ return _get_class_uno(sel)
return sel
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def current_page(self):
return LODrawPage(self._cc.getCurrentPage())
def paste(self):
call_dispatch(self.frame, '.uno:Paste')
return self.selection
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def add(self, type_shape, args={}):
return self.current_page.add(type_shape, args)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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 LODraw(LODrawImpress):
2019-10-18 22:13:43 -05:00
def __init__(self, obj):
super().__init__(obj)
2020-11-12 15:33:31 -06:00
self._type = DRAW
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOImpress(LODrawImpress):
2019-10-18 22:13:43 -05:00
def __init__(self, obj):
super().__init__(obj)
2020-11-12 15:33:31 -06:00
self._type = IMPRESS
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class BaseDateField(DateField):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def db_value(self, value):
return _date_to_struct(value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def python_value(self, value):
return _struct_to_date(value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class BaseTimeField(TimeField):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def db_value(self, value):
return _date_to_struct(value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def python_value(self, value):
return _struct_to_date(value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class BaseDateTimeField(DateTimeField):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def db_value(self, value):
return _date_to_struct(value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def python_value(self, value):
return _struct_to_date(value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class FirebirdDatabase(Database):
field_types = {'BOOL': 'BOOLEAN', 'DATETIME': 'TIMESTAMP'}
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, database, **kwargs):
super().__init__(database, **kwargs)
self._db = database
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _connect(self):
return self._db
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def execute_sql(self, sql, params=None, commit=True):
with __exception_wrapper__:
cursor = self._db.execute(sql, params)
return cursor
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def last_insert_id(self, cursor, query_type=None):
# ~ debug('LAST_ID', cursor)
return 0
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def rows_affected(self, cursor):
return self._db.rows_affected
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def path(self):
return self._db.path
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class BaseRow:
pass
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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')
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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()
2019-10-18 22:13:43 -05:00
def __getitem__(self, index):
2020-11-12 15:33:31 -06:00
return self._data[index]
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __iter__(self):
self._index = 0
return self
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2019-10-28 22:14:24 -06:00
@property
2020-11-12 15:33:31 -06:00
def tuples(self):
data = [tuple(r.__dict__.values()) for r in self._data]
return tuple(data)
2019-10-28 22:14:24 -06:00
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def dicts(self):
data = [r.__dict__ for r in self._data]
return tuple(data)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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._dbc = create_instance('com.sun.star.sdb.DatabaseContext')
self._rows_affected = 0
path = args.get('path', '')
self._path = _P(path)
self._name = self._path.name
if _P.exists(path):
if not self.is_registered:
self.register()
db = self._dbc.getByName(self.name)
else:
db = self._dbc.createInstance()
db.URL = 'sdbc:embedded:firebird'
db.DatabaseDocument.storeAsURL(self._path.url, ())
self.register()
self._obj = db
self._con = db.getConnection('', '')
def __contains__(self, item):
return item in self.tables
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def obj(self):
return self._obj
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def name(self):
return self._name
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def path(self):
return str(self._path)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def is_registered(self):
return self._dbc.hasRegisteredDatabase(self.name)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def tables(self):
tables = [t.Name.lower() for t in self._con.getTables()]
return tables
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def rows_affected(self):
return self._rows_affected
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def register(self):
if not self.is_registered:
self._dbc.registerDatabaseLocation(self.name, self._path.url)
2019-10-28 22:14:24 -06:00
return
2020-11-12 15:33:31 -06:00
def revoke(self, name):
self._dbc.revokeDatabaseLocation(name)
return True
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def save(self):
self.obj.DatabaseDocument.store()
self.refresh()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def close(self):
self._con.close()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def refresh(self):
self._con.getTables().refresh()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def initialize(self, database_proxy, tables):
db = FirebirdDatabase(self)
database_proxy.initialize(db)
db.create_tables(tables)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def cursor(self, sql, params):
if sql.startswith('SELECT'):
sql = self._validate_sql(sql, params)
cursor = self._con.prepareStatement(sql)
return cursor
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
if not params:
cursor = self._con.createStatement()
return cursor
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
return result
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def select(self, sql):
debug('SELECT', sql)
if not sql.startswith('SELECT'):
return ()
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
cursor = self._con.prepareStatement(sql)
query = cursor.executeQuery()
return BaseQuery(query)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def get_query(self, query):
sql, args = query.sql()
sql = self._validate_sql(sql, args)
return self.select(sql)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
class LOMath(LODocument):
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
self._type = MATH
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
class LOBasic(LODocument):
2019-10-28 22:14:24 -06:00
def __init__(self, obj):
super().__init__(obj)
2020-11-12 15:33:31 -06:00
self._type = BASIC
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
class LODocs(object):
_desktop = None
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __init__(self):
self._desktop = get_desktop()
LODocs._desktop = self._desktop
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __getitem__(self, index):
doc = None
for i, doc in enumerate(self._desktop.Components):
if isinstance(index, int) and i == index:
doc = _get_class_doc(doc)
break
elif isinstance(index, str) and doc.Title == index:
doc = _get_class_doc(doc)
break
return doc
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __contains__(self, item):
doc = self[item]
return not doc is None
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __iter__(self):
self._i = 0
return self
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __next__(self):
doc = self[self._i]
if doc is None:
raise StopIteration
self._i += 1
return doc
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __len__(self):
for i, _ in enumerate(self._desktop.Components):
pass
return i + 1
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
@property
def active(self):
return _get_class_doc(self._desktop.getCurrentComponent())
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
@classmethod
def new(cls, type_doc=CALC, args={}):
if type_doc == BASE:
return LOBase(None, args)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@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
"""
2020-11-12 19:35:29 -06:00
path = _P.to_url(path)
2020-11-12 15:33:31 -06:00
opt = dict_to_property(args)
doc = cls._desktop.loadComponentFromURL(path, '_default', 0, opt)
if doc is None:
return
return _get_class_doc(doc)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def connect(self, path):
return LOBase(None, {'path': path})
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def _add_listeners(events, control, name=''):
2019-10-18 22:13:43 -05:00
listeners = {
'addActionListener': EventsButton,
'addMouseListener': EventsMouse,
'addFocusListener': EventsFocus,
2020-11-12 15:33:31 -06:00
# ~ 'addItemListener': EventsItem,
2020-11-17 22:54:10 -06:00
'addKeyListener': EventsKey,
2020-11-12 15:33:31 -06:00
# ~ 'addTabListener': EventsTab,
2019-10-18 22:13:43 -05:00
}
if hasattr(control, 'obj'):
2020-11-12 15:33:31 -06:00
control = control.obj
2019-10-18 22:13:43 -05:00
# ~ 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))
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
# ~ 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)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
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 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
2020-11-17 22:54:10 -06:00
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)
return
2020-11-12 15:33:31 -06:00
# ~ BorderColor = ?
# ~ FontStyleName = ?
# ~ HelpURL = ?
class UnoBaseObject(object):
2019-10-18 22:13:43 -05:00
2020-11-12 19:35:29 -06:00
def __init__(self, obj, path=''):
2020-11-12 15:33:31 -06:00
self._obj = obj
self._model = obj.Model
2020-11-12 19:35:29 -06:00
# ~ self._path = path
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
@property
def name(self):
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.setVisible(value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def enabled(self):
return self.model.Enabled
@enabled.setter
def enabled(self, value):
self.model.Enabled = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def align(self):
return self.model.Align
@align.setter
def align(self, value):
self.model.Align = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def valign(self):
return self.model.VerticalAlign
@valign.setter
def valign(self, value):
self.model.VerticalAlign = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def font_weight(self):
return self.model.FontWeight
@font_weight.setter
def font_weight(self, value):
self.model.FontWeight = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def font_height(self):
return self.model.FontHeight
@font_height.setter
def font_height(self, value):
self.model.FontHeight = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def font_name(self):
return self.model.FontName
@font_name.setter
def font_name(self, value):
self.model.FontName = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def font_underline(self):
return self.model.FontUnderline
@font_underline.setter
def font_underline(self, value):
self.model.FontUnderline = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def text_color(self):
return self.model.TextColor
@text_color.setter
def text_color(self, value):
self.model.TextColor = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def back_color(self):
return self.model.BackgroundColor
@back_color.setter
def back_color(self, value):
self.model.BackgroundColor = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def multi_line(self):
return self.model.MultiLine
@multi_line.setter
def multi_line(self, value):
self.model.MultiLine = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def help_text(self):
return self.model.HelpText
@help_text.setter
def help_text(self, value):
self.model.HelpText = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def border(self):
return self.model.Border
@border.setter
def border(self, value):
# ~ Bug for report
self.model.Border = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def width(self):
return self._model.Width
@width.setter
def width(self, value):
self.model.Width = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def height(self):
return self.model.Height
@height.setter
def height(self, value):
self.model.Height = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _get_possize(self, name):
ps = self.obj.getPosSize()
return getattr(ps, name)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
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)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
@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)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@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)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def tab_index(self):
return self._model.TabIndex
@tab_index.setter
def tab_index(self, value):
self.model.TabIndex = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def tab_stop(self):
return self._model.Tabstop
@tab_stop.setter
def tab_stop(self, value):
self.model.Tabstop = value
2020-11-12 19:35:29 -06:00
@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
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class UnoLabel(UnoBaseObject):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def type(self):
return 'label'
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class UnoLabelLink(UnoLabel):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def type(self):
return 'link'
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class UnoButton(UnoBaseObject):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def type(self):
return 'button'
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class UnoRadio(UnoBaseObject):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def type(self):
return 'radio'
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class UnoCheck(UnoBaseObject):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def type(self):
return 'check'
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def value(self):
return self.model.State
@value.setter
def value(self, value):
self.model.State = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def label(self):
return self.model.Label
@label.setter
def label(self, value):
self.model.Label = value
2019-10-18 22:13:43 -05:00
@property
2020-11-12 15:33:31 -06:00
def tri_state(self):
return self.model.TriState
@tri_state.setter
def tri_state(self, value):
self.model.TriState = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
# ~ https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1awt_1_1UnoControlEditModel.html
class UnoText(UnoBaseObject):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def type(self):
return 'text'
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def value(self):
return self.model.Text
@value.setter
def value(self, value):
self.model.Text = value
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class UnoImage(UnoBaseObject):
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
def __init__(self, obj):
super().__init__(obj)
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
@property
def type(self):
return 'image'
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
@property
def value(self):
return self.url
@value.setter
def value(self, value):
self.url = value
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
@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)
2020-11-12 19:35:29 -06:00
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
2020-11-12 15:33:31 -06:00
UNO_CLASSES = {
'label': UnoLabel,
'link': UnoLabelLink,
'button': UnoButton,
'radio': UnoRadio,
'check': UnoCheck,
'text': UnoText,
'image': UnoImage,
2020-11-12 19:35:29 -06:00
'listbox': UnoListBox,
2020-11-12 15:33:31 -06:00
}
2019-10-28 22:14:24 -06:00
2019-10-18 22:13:43 -05:00
class LODialog(object):
2020-11-12 15:33:31 -06:00
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',
'check': 'com.sun.star.awt.UnoControlCheckBoxModel',
'text': 'com.sun.star.awt.UnoControlEditModel',
'image': 'com.sun.star.awt.UnoControlImageControlModel',
2020-11-12 19:35:29 -06:00
'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
2020-11-12 15:33:31 -06:00
# ~ 'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
# ~ 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
# ~ 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
# ~ 'tree': 'com.sun.star.awt.tree.TreeControlModel',
# ~ 'pages': 'com.sun.star.awt.UnoMultiPageModel',
}
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, args):
self._obj = self._create(args)
self._model = self.obj.Model
2019-10-18 22:13:43 -05:00
self._events = None
2020-11-12 15:33:31 -06:00
self._modal = True
self._controls = {}
self._color_on_focus = COLOR_ON_FOCUS
self._id = ''
self._path = ''
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def _create(self, args):
service = 'com.sun.star.awt.DialogProvider'
path = args.pop('Path', '')
2019-10-18 22:13:43 -05:00
if path:
2020-11-12 15:33:31 -06:00
dp = create_instance(service, True)
2020-11-12 19:35:29 -06:00
dlg = dp.createDialog(_P.to_url(path))
2020-11-12 15:33:31 -06:00
return dlg
if 'Location' in args:
name = args['Name']
library = args.get('Library', 'Standard')
location = args.get('Location', 'application')
2019-10-18 22:13:43 -05:00
if location == 'user':
location = 'application'
2020-11-12 15:33:31 -06:00
url = f'vnd.sun.star.script:{library}.{name}?location={location}'
2019-10-18 22:13:43 -05:00
if location == 'document':
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
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)
2020-11-12 15:33:31 -06:00
_set_properties(model, args)
2019-10-18 22:13:43 -05:00
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
2020-11-12 15:33:31 -06:00
def controls(self):
return self._controls
2019-10-18 22:13:43 -05:00
@property
2020-11-12 19:35:29 -06:00
def path(self):
return self._path
@property
2020-11-12 15:33:31 -06:00
def id(self):
return self._id
@id.setter
def id(self, value):
self._id = value
self._path = _P.from_id(value)
2019-10-18 22:13:43 -05:00
@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
2020-11-12 15:33:31 -06:00
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.Visible = value
2019-10-18 22:13:43 -05:00
2020-11-12 19:35:29 -06:00
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
2019-10-18 22:13:43 -05:00
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
2020-11-12 15:33:31 -06:00
self._events = controllers(self)
2019-10-18 22:13:43 -05:00
self._connect_listeners()
2020-11-12 15:33:31 -06:00
@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)
2019-10-18 22:13:43 -05:00
def _connect_listeners(self):
2020-11-12 15:33:31 -06:00
for control in self.obj.Controls:
_add_listeners(self.events, control, control.Model.Name)
2019-10-18 22:13:43 -05:00
return
def _set_image_url(self, image):
2020-11-12 15:33:31 -06:00
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):
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
2019-10-18 22:13:43 -05:00
elif tipo == 'roadmap':
2020-11-12 15:33:31 -06:00
if not 'Height' in args:
args['Height'] = self.height
if 'Title' in args:
args['Text'] = args.pop('Title')
2019-10-28 22:14:24 -06:00
elif tipo == 'tab':
2020-11-12 15:33:31 -06:00
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']
2019-10-18 22:13:43 -05:00
self.model.insertByName(name, model)
control = self.obj.getControl(name)
2020-11-12 15:33:31 -06:00
_add_listeners(self.events, control, name)
control = UNO_CLASSES[tipo](control)
2020-11-12 19:35:29 -06:00
if tipo in ('listbox',):
control.path = self.path
2019-10-28 22:14:24 -06:00
if tipo == 'tree' and root:
control.root = root
elif tipo == 'pages' and sheets:
control.sheets = sheets
control.events = self.events
2019-10-18 22:13:43 -05:00
setattr(self, name, control)
2020-11-12 15:33:31 -06:00
self._controls[name] = control
return control
2019-10-18 22:13:43 -05:00
def center(self, control, x=0, y=0):
w = self.width
h = self.height
if isinstance(control, tuple):
2020-11-12 15:33:31 -06:00
wt = self.SEPARATION * -1
2019-10-18 22:13:43 -05:00
for c in control:
2020-11-12 15:33:31 -06:00
wt += c.width + self.SEPARATION
2019-10-18 22:13:43 -05:00
x = w / 2 - wt / 2
for c in control:
c.x = x
2020-11-12 15:33:31 -06:00
x = c.x + c.width + self.SEPARATION
2019-10-18 22:13:43 -05:00
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
2020-11-12 15:33:31 -06:00
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
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class LOSheets(object):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __getitem__(self, index):
return LODocs().active[index]
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
class LOCells(object):
def __getitem__(self, index):
return LODocs().active.active[index]
class LOShortCut(object):
# ~ getKeyEventsByCommand
def __init__(self, app):
self._app = app
self._scm = None
self._init_values()
def _init_values(self):
name = 'com.sun.star.ui.GlobalAcceleratorConfiguration'
instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
service = TYPE_DOC[self._app]
manager = create_instance(instance, True)
uicm = manager.getUIConfigurationManager(service)
self._scm = uicm.ShortCutManager
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def __contains__(self, item):
cmd = self._get_command(item)
return bool(cmd)
def _get_key_event(self, command):
events = self._scm.AllKeyEvents
for event in events:
cmd = self._scm.getCommandByKeyEvent(event)
if cmd == command:
break
return event
def _to_key_event(self, shortcut):
key_event = KeyEvent()
keys = shortcut.split('+')
for v in keys[:-1]:
key_event.Modifiers += MODIFIERS[v.lower()]
key_event.KeyCode = getattr(Key, keys[-1].upper())
return key_event
def _get_command(self, shortcut):
command = ''
key_event = self._to_key_event(shortcut)
try:
command = self._scm.getCommandByKeyEvent(key_event)
except NoSuchElementException:
debug(f'No exists: {shortcut}')
return command
def add(self, shortcut, command):
if isinstance(command, dict):
command = _get_url_script(command)
key_event = self._to_key_event(shortcut)
self._scm.setKeyEvent(key_event, command)
self._scm.store()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def reset(self):
self._scm.reset()
self._scm.store()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def remove(self, shortcut):
key_event = self._to_key_event(shortcut)
try:
self._scm.removeKeyEvent(key_event)
self._scm.store()
except NoSuchElementException:
debug(f'No exists: {shortcut}')
2019-10-28 22:14:24 -06:00
return
2020-11-12 15:33:31 -06:00
def remove_by_command(self, command):
if isinstance(command, dict):
command = _get_url_script(command)
try:
self._scm.removeCommandFromAllKeyEvents(command)
self._scm.store()
except NoSuchElementException:
debug(f'No exists: {command}')
return
2019-10-28 22:14:24 -06:00
2020-11-12 15:33:31 -06:00
class LOShortCuts(object):
def __getitem__(self, index):
return LOShortCut(index)
class LOMenu(object):
def __init__(self, app):
self._app = app
self._ui = None
self._pymenus = None
self._menu = None
self._menus = self._get_menus()
def __getitem__(self, index):
if isinstance(index, int):
self._menu = self._menus[index]
else:
for menu in self._menus:
cmd = menu.get('CommandURL', '')
if MENUS[index.lower()] == cmd:
self._menu = menu
break
line = self._menu.get('CommandURL', '')
line += self._get_submenus(self._menu['ItemDescriptorContainer'])
return line
def _get_menus(self):
instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
service = TYPE_DOC[self._app]
manager = create_instance(instance, True)
self._ui = manager.getUIConfigurationManager(service)
self._pymenus = self._ui.getSettings(NODE_MENUBAR, True)
data = []
for menu in self._pymenus:
data.append(data_to_dict(menu))
return data
def _get_info(self, menu):
line = menu.get('CommandURL', '')
line += self._get_submenus(menu['ItemDescriptorContainer'])
return line
def _get_submenus(self, menu, level=1):
line = ''
for i, v in enumerate(menu):
data = data_to_dict(v)
cmd = data.get('CommandURL', '----------')
line += f'\n{" " * level}ā”œā”€ ({i}) {cmd}'
submenu = data.get('ItemDescriptorContainer', None)
if not submenu is None:
line += self._get_submenus(submenu, level + 1)
return line
def __str__(self):
info = '\n'.join([self._get_info(m) for m in self._menus])
return info
def _get_index_menu(self, menu, command):
index = -1
for i, v in enumerate(menu):
data = data_to_dict(v)
cmd = data.get('CommandURL', '')
if cmd == command:
index = i
break
return index
def insert(self, name, args):
idc = None
replace = False
command = args['CommandURL']
label = args['Label']
self[name]
menu = self._menu['ItemDescriptorContainer']
submenu = args.get('Submenu', False)
if submenu:
idc = self._ui.createSettings()
index = self._get_index_menu(menu, command)
if index == -1:
if 'Index' in args:
index = args['Index']
else:
index = self._get_index_menu(menu, args['After']) + 1
else:
replace = True
data = dict (
CommandURL = command,
Label = label,
Style = 0,
Type = 0,
ItemDescriptorContainer = idc,
)
self._save(menu, data, index, replace)
self._insert_submenu(idc, submenu)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def _get_command(self, args):
shortcut = args.get('ShortCut', '')
cmd = args['CommandURL']
if isinstance(cmd, dict):
cmd = _get_url_script(cmd)
if shortcut:
LOShortCut(self._app).add(shortcut, cmd)
return cmd
def _insert_submenu(self, parent, menus):
for i, v in enumerate(menus):
submenu = v.pop('Submenu', False)
if submenu:
idc = self._ui.createSettings()
v['ItemDescriptorContainer'] = idc
v['Type'] = 0
if v['Label'] == '-':
v['Type'] = 1
2019-10-18 22:13:43 -05:00
else:
2020-11-12 15:33:31 -06:00
v['CommandURL'] = self._get_command(v)
self._save(parent, v, i)
if submenu:
self._insert_submenu(idc, submenu)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def remove(self, name, command):
self[name]
menu = self._menu['ItemDescriptorContainer']
index = self._get_index_menu(menu, command)
if index > -1:
uno.invoke(menu, 'removeByIndex', (index,))
self._ui.replaceSettings(NODE_MENUBAR, self._pymenus)
self._ui.store()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
def _save(self, menu, properties, index, replace=False):
properties = dict_to_property(properties, True)
if replace:
uno.invoke(menu, 'replaceByIndex', (index, properties))
else:
uno.invoke(menu, 'insertByIndex', (index, properties))
self._ui.replaceSettings(NODE_MENUBAR, self._pymenus)
self._ui.store()
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
class LOMenus(object):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __getitem__(self, index):
return LOMenu(index)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
class classproperty:
def __init__(self, method=None):
self.fget = method
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __get__(self, instance, cls=None):
return self.fget(cls)
def getter(self, method):
self.fget = method
return self
class ClipBoard(object):
SERVICE = 'com.sun.star.datatransfer.clipboard.SystemClipboard'
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
class TextTransferable(unohelper.Base, XTransferable):
def __init__(self, text):
df = DataFlavor()
df.MimeType = ClipBoard.CLIPBOARD_FORMAT_TEXT
df.HumanPresentableName = "encoded text utf-16"
self.flavors = (df,)
self._data = text
def getTransferData(self, flavor):
return self._data
def getTransferDataFlavors(self):
return self.flavors
@classmethod
def set(cls, value):
ts = cls.TextTransferable(value)
sc = create_instance(cls.SERVICE)
sc.setContents(ts, None)
2019-10-18 22:13:43 -05:00
return
2020-11-12 15:33:31 -06:00
@classproperty
def contents(cls):
df = None
text = ''
sc = create_instance(cls.SERVICE)
transferable = sc.getContents()
data = transferable.getTransferDataFlavors()
for df in data:
if df.MimeType == cls.CLIPBOARD_FORMAT_TEXT:
break
if df:
text = transferable.getTransferData(df)
return text
class Paths(object):
FILE_PICKER = 'com.sun.star.ui.dialogs.FilePicker'
def __init__(self, path=''):
if path.startswith('file://'):
path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
self._path = Path(path)
@property
def path(self):
return str(self._path.parent)
@property
def file_name(self):
return self._path.name
@property
def name(self):
return self._path.stem
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def ext(self):
return self._path.suffix[1:]
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def info(self):
return self.path, self.file_name, self.name, self.ext
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def url(self):
return self._path.as_uri()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@property
def size(self):
return self._path.stat().st_size
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classproperty
def home(self):
return str(Path.home())
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classproperty
def documents(self):
return self.config()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classproperty
def temp_dir(self):
return tempfile.gettempdir()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classproperty
def python(self):
2020-11-12 23:10:42 -06:00
if IS_WIN:
path = self.join(self.config('Module'), PYTHON)
elif IS_MAC:
path = self.join(self.config('Module'), '..', 'Resources', PYTHON)
else:
path = sys.executable
return path
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def dir_tmp(self, only_name=False):
dt = tempfile.TemporaryDirectory()
if only_name:
dt = dt.name
return dt
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def tmp(cls, ext=''):
tmp = tempfile.NamedTemporaryFile(suffix=ext)
return tmp.name
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def config(cls, name='Work'):
"""
Return de path name in config
http://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1util_1_1XPathSettings.html
"""
path = create_instance('com.sun.star.util.PathSettings')
return cls.to_system(getattr(path, name))
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def get(cls, init_dir='', filters=()):
"""
Options: http://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1ui_1_1dialogs_1_1TemplateDescription.html
filters: Example
(
('XML', '*.xml'),
('TXT', '*.txt'),
)
"""
if not init_dir:
init_dir = cls.documents
init_dir = cls.to_url(init_dir)
file_picker = create_instance(cls.FILE_PICKER)
file_picker.setTitle(_('Select path'))
file_picker.setDisplayDirectory(init_dir)
file_picker.initialize((2,))
if filters:
file_picker.setCurrentFilter(filters[0][0])
for f in filters:
file_picker.appendFilter(f[0], f[1])
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
path = ''
if file_picker.execute():
path = cls.to_system(file_picker.getSelectedFiles()[0])
return path
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def get_dir(cls, init_dir=''):
folder_picker = create_instance(cls.FILE_PICKER)
if not init_dir:
init_dir = cls.documents
init_dir = cls.to_url(init_dir)
folder_picker.setTitle(_('Select directory'))
folder_picker.setDisplayDirectory(init_dir)
path = ''
if folder_picker.execute():
path = cls.to_system(folder_picker.getDisplayDirectory())
return path
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
2020-11-17 23:16:06 -06:00
def get_file(cls, init_dir: str='', filters=(), multiple: bool=False):
2020-11-12 15:33:31 -06:00
"""
init_folder: folder default open
multiple: True for multiple selected
filters: Example
(
('XML', '*.xml'),
('TXT', '*.txt'),
)
"""
if not init_dir:
init_dir = cls.documents
init_dir = cls.to_url(init_dir)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
file_picker = create_instance(cls.FILE_PICKER)
file_picker.setTitle(_('Select file'))
file_picker.setDisplayDirectory(init_dir)
file_picker.setMultiSelectionMode(multiple)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
if filters:
file_picker.setCurrentFilter(filters[0][0])
for f in filters:
file_picker.appendFilter(f[0], f[1])
path = ''
if file_picker.execute():
files = file_picker.getSelectedFiles()
path = [cls.to_system(f) for f in files]
if not multiple:
path = path[0]
return path
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def replace_ext(cls, path, new_ext):
p = Paths(path)
name = f'{p.name}.{new_ext}'
path = cls.join(p.path, name)
return path
2020-03-09 18:26:35 -06:00
2020-11-12 15:33:31 -06:00
@classmethod
def exists(cls, path):
result = False
if path:
path = cls.to_system(path)
result = Path(path).exists()
return result
2020-03-09 18:26:35 -06:00
2020-11-12 15:33:31 -06:00
@classmethod
def exists_app(cls, name_app):
return bool(shutil.which(name_app))
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def open(cls, path):
if IS_WIN:
os.startfile(path)
else:
pid = subprocess.Popen(['xdg-open', path]).pid
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def is_dir(cls, path):
return Path(path).is_dir()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def is_file(cls, path):
return Path(path).is_file()
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def join(cls, *paths):
return str(Path(paths[0]).joinpath(*paths[1:]))
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def save(cls, path, data, encoding='utf-8'):
result = bool(Path(path).write_text(data, encoding=encoding))
return result
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def save_bin(cls, path, data):
result = bool(Path(path).write_bytes(data))
return result
2020-03-10 22:19:50 -06:00
2020-11-12 15:33:31 -06:00
@classmethod
def read(cls, path, encoding='utf-8'):
data = Path(path).read_text(encoding=encoding)
return data
2020-03-10 22:19:50 -06:00
2020-11-12 15:33:31 -06:00
@classmethod
def read_bin(cls, path):
data = Path(path).read_bytes()
return data
2020-03-10 22:19:50 -06:00
2020-11-12 15:33:31 -06:00
@classmethod
def to_url(cls, path):
if not path.startswith('file://'):
path = Path(path).as_uri()
2020-03-10 22:19:50 -06:00
return path
2020-11-12 15:33:31 -06:00
@classmethod
def to_system(cls, path):
if path.startswith('file://'):
path = str(Path(uno.fileUrlToSystemPath(path)).resolve())
2020-03-10 22:19:50 -06:00
return path
2020-11-12 15:33:31 -06:00
@classmethod
def kill(cls, path):
result = True
p = Path(path)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
try:
if p.is_file():
p.unlink()
elif p.is_dir():
shutil.rmtree(path)
except OSError as e:
log.error(e)
result = False
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
return result
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def walk(cls, path, filters=''):
paths = []
if filters in ('*', '*.*'):
filters = ''
for folder, _, files in os.walk(path):
if filters:
pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE)
paths += [cls.join(folder, f) for f in files if pattern.search(f)]
else:
paths += [cls.join(folder, f) for f in files]
return paths
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def from_id(cls, id_ext):
pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider')
path = _P.to_system(pip.getPackageLocation(id_ext))
return path
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def from_json(cls, path):
data = json.loads(cls.read(path))
return data
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def to_json(cls, path, data):
data = json.dumps(data, indent=4, ensure_ascii=False, sort_keys=True)
return cls.save(path, data)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def from_csv(cls, path, args={}):
# ~ See https://docs.python.org/3.7/library/csv.html#csv.reader
with open(path) as f:
rows = tuple(csv.reader(f, **args))
return rows
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def to_csv(cls, path, data, args={}):
with open(path, 'w') as f:
writer = csv.writer(f, **args)
writer.writerows(data)
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def zip(cls, source, target='', pwd=''):
path_zip = target
if not isinstance(source, (tuple, list)):
path, _, name, _ = _P(source).info
start = len(path) + 1
if not target:
path_zip = f'{path}/{name}.zip'
if isinstance(source, (tuple, list)):
files = [(f, f[len(_P(f).path)+1:]) for f in source]
elif _P.is_file(source):
files = ((source, source[start:]),)
else:
files = [(f, f[start:]) for f in _P.walk(source)]
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
compression = zipfile.ZIP_DEFLATED
with zipfile.ZipFile(path_zip, 'w', compression=compression) as z:
for f in files:
z.write(f[0], f[1])
return
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def zip_content(cls, path):
with zipfile.ZipFile(path) as z:
names = z.namelist()
return names
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def unzip(cls, source, target='', members=None, pwd=None):
path = target
if not target:
path = _P(source).path
with zipfile.ZipFile(source) as z:
if not pwd is None:
pwd = pwd.encode()
if isinstance(members, str):
members = (members,)
z.extractall(path, members=members, pwd=pwd)
return True
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
@classmethod
def copy(cls, source, target='', name=''):
p, f, n, e = _P(source).info
if target:
p = target
if name:
e = ''
n = name
path_new = cls.join(p, f'{n}{e}')
shutil.copy(source, path_new)
return path_new
_P = Paths
def __getattr__(name):
if name == 'active':
return LODocs().active
if name == 'active_sheet':
return LODocs().active.active
if name == 'selection':
return LODocs().active.selection
if name == 'current_region':
return LODocs().active.selection.current_region
if name in ('rectangle', 'pos_size'):
return Rectangle()
if name == 'paths':
return Paths
if name == 'docs':
return LODocs()
if name == 'sheets':
return LOSheets()
if name == 'cells':
return LOCells()
if name == 'menus':
return LOMenus()
if name == 'shortcuts':
return LOShortCuts()
if name == 'clipboard':
return ClipBoard
raise AttributeError(f"module '{__name__}' has no attribute '{name}'")
def create_dialog(args):
return LODialog(args)
2019-10-28 22:14:24 -06:00
2019-10-18 22:13:43 -05:00
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,
}
2020-11-12 15:33:31 -06:00
dlg = LODialog(args)
dlg.events = ControllersInput
2019-10-18 22:13:43 -05:00
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 ''
2020-11-12 15:33:31 -06:00
def get_fonts():
toolkit = create_instance('com.sun.star.awt.Toolkit')
device = toolkit.createScreenCompatibleDevice(0, 0)
return device.FontDescriptors
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
# ~ From request
# ~ https://github.com/psf/requests/blob/master/requests/structures.py#L15
class CaseInsensitiveDict(MutableMapping):
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __init__(self, data=None, **kwargs):
self._store = OrderedDict()
if data is None:
data = {}
self.update(data, **kwargs)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __setitem__(self, key, value):
# Use the lowercased key for lookups, but store the actual
# key alongside the value.
self._store[key.lower()] = (key, value)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __getitem__(self, key):
return self._store[key.lower()][1]
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __delitem__(self, key):
del self._store[key.lower()]
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __iter__(self):
return (casedkey for casedkey, mappedvalue in self._store.values())
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __len__(self):
return len(self._store)
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def lower_items(self):
"""Like iteritems(), but with all lowercase keys."""
values = (
(lowerkey, keyval[1]) for (lowerkey, keyval) in self._store.items()
)
return values
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
# Copy is required
def copy(self):
return CaseInsensitiveDict(self._store.values())
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
def __repr__(self):
return str(dict(self.items()))
2019-10-18 22:13:43 -05:00
2020-11-12 15:33:31 -06:00
# ~ https://en.wikipedia.org/wiki/Web_colors
def get_color(value):
2019-10-18 22:13:43 -05:00
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,
}
2020-11-12 15:33:31 -06:00
if isinstance(value, tuple):
2019-10-18 22:13:43 -05:00
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')
2020-11-12 15:33:31 -06:00
class LOServer(object):
2019-10-18 22:13:43 -05:00
HOST = 'localhost'
PORT = '8100'
2020-11-12 15:33:31 -06:00
ARG = f'socket,host={HOST},port={PORT};urp;StarOffice.ComponentContext'
2019-10-18 22:13:43 -05:00
CMD = ['soffice',
'-env:SingleAppInstance=false',
2020-11-12 15:33:31 -06:00
'-env:UserInstallation=file:///tmp/LO_Process8100',
2019-10-18 22:13:43 -05:00
'--headless', '--norestore', '--invisible',
2020-11-12 15:33:31 -06:00
f'--accept={ARG}']
2019-10-18 22:13:43 -05:00
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