zaz/source/easymacro.py

4908 lines
128 KiB
Python
Raw Normal View History

2019-09-07 00:02:59 -05:00
#!/usr/bin/env python3
# == Rapid Develop Macros in LibreOffice ==
# ~ This file is part of ZAZ.
# ~ ZAZ is free software: you can redistribute it and/or modify
# ~ it under the terms of the GNU General Public License as published by
# ~ the Free Software Foundation, either version 3 of the License, or
# ~ (at your option) any later version.
# ~ ZAZ is distributed in the hope that it will be useful,
# ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
# ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# ~ GNU General Public License for more details.
# ~ You should have received a copy of the GNU General Public License
# ~ along with ZAZ. If not, see <https://www.gnu.org/licenses/>.
2019-09-27 12:17:21 -05:00
import base64
2019-10-12 20:16:00 -05:00
import csv
2019-09-14 17:40:07 -05:00
import ctypes
import datetime
2019-09-11 23:53:29 -05:00
import errno
2019-10-19 20:01:04 -05:00
import gettext
2019-09-07 00:02:59 -05:00
import getpass
2019-09-27 12:17:21 -05:00
import hashlib
2019-09-18 21:41:47 -05:00
import json
2019-09-07 00:02:59 -05:00
import logging
2019-09-09 22:13:01 -05:00
import os
2019-09-07 00:02:59 -05:00
import platform
2019-09-18 21:41:47 -05:00
import re
2019-09-11 23:53:29 -05:00
import shlex
2019-09-18 21:41:47 -05:00
import shutil
2019-10-05 11:14:19 -05:00
import socket
2019-09-11 23:53:29 -05:00
import subprocess
2019-09-07 00:02:59 -05:00
import sys
2019-09-08 00:25:30 -05:00
import tempfile
2019-09-07 00:02:59 -05:00
import threading
import time
2019-09-27 12:17:21 -05:00
import traceback
2019-09-11 23:53:29 -05:00
import zipfile
2019-09-18 21:41:47 -05:00
from collections import OrderedDict
from collections.abc import MutableMapping
2019-09-07 00:02:59 -05:00
from functools import wraps
2019-09-27 12:17:21 -05:00
from operator import itemgetter
2019-09-11 23:53:29 -05:00
from pathlib import Path, PurePath
from pprint import pprint
2019-10-19 20:01:04 -05:00
from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError
2019-09-27 12:17:21 -05:00
from string import Template
2019-09-11 23:53:29 -05:00
from subprocess import PIPE
2019-09-07 00:02:59 -05:00
2019-09-27 12:17:21 -05:00
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
2019-09-07 00:02:59 -05:00
import uno
2019-09-09 22:13:01 -05:00
import unohelper
2019-09-27 12:17:21 -05:00
from com.sun.star.util import Time, Date, DateTime
2019-10-10 00:07:45 -05:00
from com.sun.star.beans import PropertyValue, NamedValue
2019-09-07 00:02:59 -05:00
from com.sun.star.awt import MessageBoxButtons as MSG_BUTTONS
from com.sun.star.awt.MessageBoxResults import YES
2019-09-08 00:25:30 -05:00
from com.sun.star.awt.PosSize import POSSIZE, SIZE
2019-09-09 22:13:01 -05:00
from com.sun.star.awt import Size, Point
2019-10-10 00:07:45 -05:00
from com.sun.star.awt import Rectangle
from com.sun.star.awt import KeyEvent
from com.sun.star.awt.KeyFunction import QUIT
2019-09-14 17:40:07 -05:00
from com.sun.star.datatransfer import XTransferable, DataFlavor
2019-09-08 00:25:30 -05:00
from com.sun.star.table.CellContentType import EMPTY, VALUE, TEXT, FORMULA
2019-09-07 00:02:59 -05:00
2019-10-12 20:16:00 -05:00
from com.sun.star.text.ControlCharacter import PARAGRAPH_BREAK
2019-09-09 22:13:01 -05:00
from com.sun.star.text.TextContentAnchorType import AS_CHARACTER
2019-10-19 20:01:04 -05:00
from com.sun.star.script import ScriptEventDescriptor
2019-09-09 22:13:01 -05:00
from com.sun.star.lang import XEventListener
from com.sun.star.awt import XActionListener
from com.sun.star.awt import XMouseListener
2019-10-10 00:07:45 -05:00
from com.sun.star.awt import XMouseMotionListener
2019-10-05 11:14:19 -05:00
from com.sun.star.util import XModifyListener
2019-10-10 00:07:45 -05:00
from com.sun.star.awt import XTopWindowListener
from com.sun.star.awt import XWindowListener
from com.sun.star.awt import XMenuListener
from com.sun.star.awt import XKeyListener
2019-10-14 19:48:13 -05:00
from com.sun.star.awt import XItemListener
2019-10-14 23:43:42 -05:00
from com.sun.star.awt import XFocusListener
2019-10-14 19:48:13 -05:00
2019-09-09 22:13:01 -05:00
2019-09-27 12:17:21 -05:00
try:
from fernet import Fernet, InvalidToken
except ImportError:
2019-10-05 11:14:19 -05:00
pass
2019-09-27 12:17:21 -05:00
2019-09-07 00:02:59 -05:00
2019-10-19 20:01:04 -05:00
ID_EXTENSION = ''
DIR = {
'images': 'images',
'locales': 'locales',
}
KEY = {
'enter': 1280,
}
SEPARATION = 5
2019-09-07 00:02:59 -05:00
MSG_LANG = {
'es': {
'OK': 'Aceptar',
'Cancel': 'Cancelar',
2019-09-10 22:20:31 -05:00
'Select file': 'Seleccionar archivo',
2019-09-27 12:17:21 -05:00
'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-09-07 00:02:59 -05:00
}
}
2019-09-10 22:20:31 -05:00
2019-09-11 23:53:29 -05:00
OS = platform.system()
2019-09-10 22:20:31 -05:00
USER = getpass.getuser()
PC = platform.node()
2019-09-14 17:40:07 -05:00
DESKTOP = os.environ.get('DESKTOP_SESSION', '')
INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path))
2019-09-10 22:20:31 -05:00
2019-09-18 21:41:47 -05:00
2019-09-11 23:53:29 -05:00
IS_WIN = OS == 'Windows'
LOG_NAME = 'ZAZ'
2019-09-14 17:40:07 -05:00
CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16'
2019-09-11 23:53:29 -05:00
2019-09-18 21:41:47 -05:00
2019-10-19 20:01:04 -05:00
PYTHON = 'python'
if IS_WIN:
PYTHON = 'python.exe'
2019-09-11 23:53:29 -05:00
CALC = 'calc'
WRITER = 'writer'
2019-09-08 00:25:30 -05:00
OBJ_CELL = 'ScCellObj'
OBJ_RANGE = 'ScCellRangeObj'
OBJ_RANGES = 'ScCellRangesObj'
2019-09-09 22:13:01 -05:00
OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES)
2019-09-08 00:25:30 -05:00
2019-09-18 21:41:47 -05:00
TYPE_DOC = {
'calc': 'com.sun.star.sheet.SpreadsheetDocument',
'writer': 'com.sun.star.text.TextDocument',
'impress': 'com.sun.star.presentation.PresentationDocument',
'draw': 'com.sun.star.drawing.DrawingDocument',
2019-09-27 12:17:21 -05:00
# ~ 'base': 'com.sun.star.sdb.OfficeDatabaseDocument',
'base': 'com.sun.star.sdb.DocumentDataSource',
2019-09-18 21:41:47 -05:00
'math': 'com.sun.star.formula.FormulaProperties',
'basic': 'com.sun.star.script.BasicIDE',
2019-10-19 20:01:04 -05:00
'main': 'com.sun.star.frame.StartModule',
2019-09-18 21:41:47 -05:00
}
NODE_MENUBAR = 'private:resource/menubar/menubar'
2019-10-19 20:01:04 -05:00
MENUS_MAIN = {
'file': '.uno:PickList',
'tools': '.uno:ToolsMenu',
'help': '.uno:HelpMenu',
}
2019-09-18 21:41:47 -05:00
MENUS_CALC = {
'file': '.uno:PickList',
'edit': '.uno:EditMenu',
'view': '.uno:ViewMenu',
'insert': '.uno:InsertMenu',
'format': '.uno:FormatMenu',
'styles': '.uno:FormatStylesMenu',
'sheet': '.uno:SheetMenu',
'data': '.uno:DataMenu',
'tools': '.uno:ToolsMenu',
'windows': '.uno:WindowList',
'help': '.uno:HelpMenu',
}
2019-09-27 12:17:21 -05:00
MENUS_WRITER = {
'file': '.uno:PickList',
'edit': '.uno:EditMenu',
'view': '.uno:ViewMenu',
'insert': '.uno:InsertMenu',
'format': '.uno:FormatMenu',
'styles': '.uno:FormatStylesMenu',
'sheet': '.uno:TableMenu',
'data': '.uno:FormatFormMenu',
'tools': '.uno:ToolsMenu',
'windows': '.uno:WindowList',
'help': '.uno:HelpMenu',
}
2019-09-18 21:41:47 -05:00
MENUS_APP = {
2019-10-19 20:01:04 -05:00
'main': MENUS_MAIN,
2019-09-18 21:41:47 -05:00
'calc': MENUS_CALC,
2019-09-27 12:17:21 -05:00
'writer': MENUS_WRITER,
2019-09-18 21:41:47 -05:00
}
2019-09-27 12:17:21 -05:00
EXT = {
'pdf': 'pdf',
}
FILE_NAME_DEBUG = 'debug.odt'
FILE_NAME_CONFIG = 'zaz-{}.json'
2019-09-18 21:41:47 -05: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-09-10 22:20:31 -05:00
2019-09-27 12:17:21 -05:00
_start = 0
_stop_thread = {}
TIMEOUT = 10
2019-10-12 20:16:00 -05:00
SECONDS_DAY = 60 * 60 * 24
2019-09-27 12:17:21 -05:00
2019-09-07 00:02:59 -05:00
CTX = uno.getComponentContext()
SM = CTX.getServiceManager()
def create_instance(name, with_context=False):
if with_context:
instance = SM.createInstanceWithContext(name, CTX)
else:
instance = SM.createInstance(name)
return instance
2019-10-12 20:16:00 -05:00
def get_app_config(node_name, key=''):
2019-09-07 00:02:59 -05:00
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,))
2019-10-05 11:14:19 -05:00
if ca and not key:
return ca
if ca and ca.hasByName(key):
return ca.getPropertyValue(key)
2019-09-07 00:02:59 -05:00
except Exception as e:
2019-10-05 11:14:19 -05:00
error(e)
2019-09-07 00:02:59 -05:00
return ''
2019-10-05 11:14:19 -05:00
# ~ FILTER_PDF = '/org.openoffice.Office.Common/Filter/PDF/Export/'
2019-10-12 20:16:00 -05:00
LANGUAGE = get_app_config('org.openoffice.Setup/L10N/', 'ooLocale')
2019-09-14 17:40:07 -05:00
LANG = LANGUAGE.split('-')[0]
2019-10-12 20:16:00 -05:00
NAME = TITLE = get_app_config('org.openoffice.Setup/Product', 'ooName')
VERSION = get_app_config('org.openoffice.Setup/Product','ooSetupVersion')
nd = '/org.openoffice.Office.Calc/Calculate/Other/Date'
d = get_app_config(nd, 'DD')
m = get_app_config(nd, 'MM')
y = get_app_config(nd, 'YY')
DATE_OFFSET = datetime.date(y, m, d).toordinal()
2019-09-07 00:02:59 -05:00
def mri(obj):
m = create_instance('mytools.Mri')
if m is None:
msg = 'Extension MRI not found'
error(msg)
return
m.inspect(obj)
return
def catch_exception(f):
@wraps(f)
def func(*args, **kwargs):
try:
return f(*args, **kwargs)
except Exception as e:
2019-09-27 12:17:21 -05:00
name = f.__name__
if IS_WIN:
debug(traceback.format_exc())
log.error(name, exc_info=True)
2019-09-07 00:02:59 -05:00
return func
2019-09-11 23:53:29 -05:00
class LogWin(object):
def __init__(self, doc):
self.doc = doc
def write(self, info):
text = self.doc.Text
cursor = text.createTextCursor()
cursor.gotoEnd(False)
2019-09-27 12:17:21 -05:00
text.insertString(cursor, str(info) + '\n\n', 0)
2019-09-11 23:53:29 -05:00
return
def info(data):
log.info(data)
return
2019-10-19 20:01:04 -05:00
def debug(*info):
2019-09-11 23:53:29 -05:00
if IS_WIN:
2019-09-27 12:17:21 -05:00
doc = get_document(FILE_NAME_DEBUG)
if doc is None:
return
doc = LogWin(doc.obj)
2019-10-19 20:01:04 -05:00
doc.write(str(info))
2019-09-11 23:53:29 -05:00
return
2019-10-19 20:01:04 -05:00
data = [str(d) for d in info]
log.debug('\t'.join(data))
2019-09-11 23:53:29 -05:00
return
def error(info):
log.error(info)
return
def save_log(path, data):
with open(path, 'a') as out:
2019-09-27 12:17:21 -05:00
out.write('{} -{}- '.format(str(now())[:19], LOG_NAME))
2019-09-11 23:53:29 -05:00
pprint(data, stream=out)
return
2019-09-07 00:02:59 -05:00
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-09-27 12:17:21 -05:00
def now():
return datetime.datetime.now()
2019-10-05 11:14:19 -05:00
def today():
return datetime.date.today()
2019-10-12 20:16:00 -05:00
def time():
return datetime.datetime.now().time()
2019-10-05 11:14:19 -05:00
def get_date(year, month, day, hour=-1, minute=-1, second=-1):
if hour > -1 or minute > -1 or second > -1:
h = hour
m = minute
s = second
if h == -1:
h = 0
if m == -1:
m = 0
if s == -1:
s = 0
d = datetime.datetime(year, month, day, h, m, s)
else:
d = datetime.date(year, month, day)
return d
2019-09-27 12:17:21 -05:00
def get_config(key='', default=None, prefix='config'):
path_json = FILE_NAME_CONFIG.format(prefix)
values = None
path = join(get_config_path('UserConfig'), path_json)
2019-09-18 21:41:47 -05:00
if not exists_path(path):
2019-09-27 12:17:21 -05:00
return default
2019-09-18 21:41:47 -05:00
with open(path, 'r', encoding='utf-8') as fh:
data = fh.read()
2019-09-27 12:17:21 -05:00
values = json.loads(data)
2019-09-18 21:41:47 -05:00
if key:
2019-09-27 12:17:21 -05:00
return values.get(key, default)
2019-09-18 21:41:47 -05:00
return values
2019-09-27 12:17:21 -05:00
def set_config(key, value, prefix='config'):
path_json = FILE_NAME_CONFIG.format(prefix)
path = join(get_config_path('UserConfig'), path_json)
values = get_config(default={}, prefix=prefix)
2019-09-18 21:41:47 -05:00
values[key] = value
with open(path, 'w', encoding='utf-8') as fh:
json.dump(values, fh, ensure_ascii=False, sort_keys=True, indent=4)
2019-10-05 11:14:19 -05:00
return True
2019-09-18 21:41:47 -05:00
def sleep(seconds):
time.sleep(seconds)
2019-09-16 15:39:03 -05:00
return
2019-09-07 00:02:59 -05:00
def _(msg):
L = LANGUAGE.split('-')[0]
if L == 'en':
return msg
if not L in MSG_LANG:
return msg
return MSG_LANG[L][msg]
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()
mb = toolkit.createMessageBox(parent, type_msg, buttons, title, str(message))
return mb.execute()
def question(message, title=TITLE):
res = msgbox(message, title, MSG_BUTTONS.BUTTONS_YES_NO, 'querybox')
return res == YES
def warning(message, title=TITLE):
return msgbox(message, title, type_msg='warningbox')
2019-09-08 00:25:30 -05:00
def errorbox(message, title=TITLE):
2019-09-07 00:02:59 -05:00
return msgbox(message, title, type_msg='errorbox')
2019-09-08 00:25:30 -05:00
2019-09-09 22:13:01 -05:00
def get_desktop():
return create_instance('com.sun.star.frame.Desktop', True)
2019-09-14 17:40:07 -05:00
def get_dispatch():
return create_instance('com.sun.star.frame.DispatchHelper')
2019-09-27 12:17:21 -05:00
def call_dispatch(url, args=()):
frame = get_document().frame
dispatch = get_dispatch()
dispatch.executeDispatch(frame, url, '', 0, args)
return
2019-09-08 00:25:30 -05:00
def get_temp_file():
2019-09-16 15:39:03 -05:00
delete = True
if IS_WIN:
delete = False
return tempfile.NamedTemporaryFile(delete=delete)
2019-09-08 00:25:30 -05:00
def _path_url(path):
if path.startswith('file://'):
return path
return uno.systemPathToFileUrl(path)
2019-09-09 22:13:01 -05:00
def _path_system(path):
if path.startswith('file://'):
return os.path.abspath(uno.fileUrlToSystemPath(path))
return path
2019-09-11 23:53:29 -05:00
def exists_app(name):
try:
dn = subprocess.DEVNULL
subprocess.Popen([name, ''], stdout=dn, stderr=dn).terminate()
except OSError as e:
if e.errno == errno.ENOENT:
return False
return True
2019-09-27 12:17:21 -05:00
2019-09-18 21:41:47 -05:00
def exists_path(path):
return Path(path).exists()
2019-09-11 23:53:29 -05:00
2019-09-09 22:13:01 -05:00
def get_type_doc(obj):
2019-09-18 21:41:47 -05:00
for k, v in TYPE_DOC.items():
2019-09-09 22:13:01 -05:00
if obj.supportsService(v):
return k
return ''
2019-09-18 21:41:47 -05:00
def dict_to_property(values, uno_any=False):
ps = tuple([PropertyValue(Name=n, Value=v) for n, v in values.items()])
if uno_any:
ps = uno.Any('[]com.sun.star.beans.PropertyValue', ps)
return ps
def property_to_dict(values):
d = {i.Name: i.Value for i in values}
return d
2019-10-05 11:14:19 -05:00
def set_properties(model, properties):
if 'X' in properties:
properties['PositionX'] = properties.pop('X')
if 'Y' in properties:
properties['PositionY'] = properties.pop('Y')
keys = tuple(properties.keys())
values = tuple(properties.values())
model.setPropertyValues(keys, values)
return
2019-09-27 12:17:21 -05:00
def array_to_dict(values):
d = {r[0]: r[1] for r in values}
return d
2019-09-10 22:20:31 -05:00
2019-09-08 00:25:30 -05:00
# ~ Custom classes
2019-10-12 20:16:00 -05:00
class ObjectBase(object):
def __init__(self, obj):
self._obj = obj
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
def __getitem__(self, index):
return self.obj[index]
2019-10-14 17:23:15 -05:00
def __getattr__(self, name):
a = None
if name == 'obj':
a = super().__getattr__(name)
else:
if hasattr(self.obj, name):
a = getattr(self.obj, name)
return a
2019-10-12 20:16:00 -05:00
@property
def obj(self):
return self._obj
@obj.setter
def obj(self, value):
self._obj = value
2019-10-05 11:14:19 -05:00
class LOObjectBase(object):
def __init__(self, obj):
self.__dict__['_obj'] = obj
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
return True
def __setattr__(self, name, value):
print('BASE__setattr__', name)
if name == '_obj':
super().__setattr__(name, value)
else:
self.obj.setPropertyValue(name, value)
# ~ def _try_for_method(self, name):
# ~ a = None
# ~ m = 'get{}'.format(name)
# ~ if hasattr(self.obj, m):
# ~ a = getattr(self.obj, m)()
# ~ else:
# ~ a = getattr(self.obj, name)
# ~ return a
def __getattr__(self, name):
print('BASE__getattr__', name)
if name == 'obj':
a = super().__getattr__(name)
else:
a = self.obj.getPropertyValue(name)
# ~ Bug
if a is None:
msg = 'Error get: {} - {}'.format(self.obj.ImplementationName, name)
error(msg)
raise Exception(msg)
return a
@property
def obj(self):
return self._obj
2019-09-09 22:13:01 -05:00
class LODocument(object):
def __init__(self, obj):
self._obj = obj
self._init_values()
def _init_values(self):
self._type_doc = get_type_doc(self.obj)
2019-10-14 23:43:42 -05:00
# ~ if self._type_doc == 'base':
# ~ self._cc = self.obj.DatabaseDocument.getCurrentController()
# ~ else:
self._cc = self.obj.getCurrentController()
2019-09-09 22:13:01 -05:00
return
@property
def obj(self):
return self._obj
@property
def title(self):
return self.obj.getTitle()
2019-10-05 11:14:19 -05:00
@title.setter
def title(self, value):
self.obj.setTitle(value)
@property
def uid(self):
return self.obj.RuntimeUID
2019-10-05 11:14:19 -05:00
@property
def type(self):
return self._type_doc
2019-09-09 22:13:01 -05:00
2019-09-14 17:40:07 -05:00
@property
def frame(self):
return self._cc.getFrame()
2019-09-09 22:13:01 -05:00
@property
def is_saved(self):
return self.obj.hasLocation()
@property
def is_modified(self):
return self.obj.isModified()
@property
def is_read_only(self):
return self.obj.isReadOnly()
@property
def path(self):
return _path_system(self.obj.getURL())
2019-09-27 12:17:21 -05:00
@property
def statusbar(self):
return self._cc.getStatusIndicator()
2019-09-09 22:13:01 -05:00
@property
def visible(self):
w = self._cc.getFrame().getContainerWindow()
2019-10-05 11:14:19 -05:00
return w.isVisible()
2019-09-09 22:13:01 -05:00
@visible.setter
def visible(self, value):
w = self._cc.getFrame().getContainerWindow()
w.setVisible(value)
@property
def zoom(self):
return self._cc.ZoomValue
@zoom.setter
def zoom(self, value):
self._cc.ZoomValue = value
2019-10-05 11:14:19 -05:00
@property
def table_auto_formats(self):
taf = create_instance('com.sun.star.sheet.TableAutoFormats')
return taf.ElementNames
2019-09-09 22:13:01 -05:00
def create_instance(self, name):
obj = self.obj.createInstance(name)
return obj
2019-09-11 23:53:29 -05:00
def save(self, path='', **kwargs):
2019-09-18 21:41:47 -05:00
# ~ opt = _properties(kwargs)
opt = dict_to_property(kwargs)
2019-09-11 23:53:29 -05:00
if path:
self._obj.storeAsURL(_path_url(path), opt)
else:
self._obj.store()
return True
2019-09-10 22:20:31 -05:00
def close(self):
self.obj.close(True)
return
2019-09-11 23:53:29 -05:00
def focus(self):
w = self._cc.getFrame().getComponentWindow()
w.setFocus()
return
2019-09-14 17:40:07 -05:00
def paste(self):
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
transferable = sc.getContents()
self._cc.insertTransferable(transferable)
return self.obj.getCurrentSelection()
2019-09-27 12:17:21 -05:00
def to_pdf(self, path, **kwargs):
path_pdf = path
if path:
if is_dir(path):
_, _, n, _ = get_info_path(self.path)
path_pdf = join(path, '{}.{}'.format(n, EXT['pdf']))
else:
path_pdf = replace_ext(self.path, EXT['pdf'])
filter_name = '{}_pdf_Export'.format(self.type)
filter_data = dict_to_property(kwargs, True)
args = {
'FilterName': filter_name,
'FilterData': filter_data,
}
args = dict_to_property(args)
try:
self.obj.storeToURL(_path_url(path_pdf), args)
except Exception as e:
error(e)
path_pdf = ''
return path_pdf
2019-09-09 22:13:01 -05:00
2019-10-19 20:01:04 -05:00
class FormControlBase(object):
EVENTS = {
'action': 'actionPerformed',
'click': 'mousePressed',
}
TYPES = {
'actionPerformed': 'XActionListener',
'mousePressed': 'XMouseListener',
}
def __init__(self, obj):
self._obj = obj
self._index = -1
self._rules = {}
@property
def obj(self):
return self._obj
@property
def name(self):
return self.obj.Name
@property
def form(self):
return self.obj.getParent()
@property
def index(self):
return self._index
@index.setter
def index(self, value):
self._index = value
@property
def events(self):
return self.form.getScriptEvents(self.index)
def remove_event(self, name=''):
for ev in self.events:
if name and \
ev.EventMethod == self.EVENTS[name] and \
ev.ListenerType == self.TYPES[ev.EventMethod]:
self.form.revokeScriptEvent(self.index,
ev.ListenerType, ev.EventMethod, ev.AddListenerParam)
break
else:
self.form.revokeScriptEvent(self.index,
ev.ListenerType, ev.EventMethod, ev.AddListenerParam)
return
def add_event(self, name, macro):
if not 'name' in macro:
macro['name'] = '{}_{}'.format(self.name, name)
event = ScriptEventDescriptor()
event.AddListenerParam = ''
event.EventMethod = self.EVENTS[name]
event.ListenerType = self.TYPES[event.EventMethod]
event.ScriptCode = _get_url_script(macro)
event.ScriptType = 'Script'
for ev in self.events:
if ev.EventMethod == event.EventMethod and \
ev.ListenerType == event.ListenerType:
self.form.revokeScriptEvent(self.index,
event.ListenerType, event.EventMethod, event.AddListenerParam)
break
self.form.registerScriptEvent(self.index, event)
return
class FormButton(FormControlBase):
def __init__(self, obj):
super().__init__(obj)
2019-10-14 17:23:15 -05:00
class LOForm(ObjectBase):
def __init__(self, obj):
super().__init__(obj)
2019-10-19 20:01:04 -05:00
self._init_controls()
def __getitem__(self, index):
if isinstance(index, int):
return self._controls[index]
else:
return getattr(self, index)
def _get_type_control(self, name):
types = {
# ~ 'stardiv.Toolkit.UnoFixedTextControl': 'label',
'com.sun.star.form.OButtonModel': 'formbutton',
# ~ 'stardiv.Toolkit.UnoEditControl': 'text',
# ~ 'stardiv.Toolkit.UnoRoadmapControl': 'roadmap',
# ~ 'stardiv.Toolkit.UnoFixedHyperlinkControl': 'link',
# ~ 'stardiv.Toolkit.UnoListBoxControl': 'listbox',
}
return types[name]
def _init_controls(self):
self._controls = []
for i, c in enumerate(self.obj.ControlModels):
tipo = self._get_type_control(c.ImplementationName)
control = get_custom_class(tipo, c)
control.index = i
self._controls.append(control)
setattr(self, c.Name, control)
2019-10-14 17:23:15 -05:00
@property
def name(self):
return self._obj.getName()
@name.setter
def name(self, value):
self._obj.setName(value)
class LOForms(ObjectBase):
def __init__(self, obj, doc):
self._doc = doc
super().__init__(obj)
def __getitem__(self, index):
form = super().__getitem__(index)
return LOForm(form)
@property
def doc(self):
return self._doc
@property
def count(self):
return self.obj.getCount()
@property
def names(self):
return self.obj.getElementNames()
def exists(self, name):
return name in self.names
def insert(self, name):
form = self.doc.create_instance('com.sun.star.form.component.Form')
self.obj.insertByName(name, form)
return self[name]
def remove(self, index):
if isinstance(index, int):
self.obj.removeByIndex(index)
else:
self.obj.removeByName(index)
return
2019-10-05 11:14:19 -05:00
class LOCellStyle(LOObjectBase):
2019-09-08 00:25:30 -05:00
def __init__(self, obj):
2019-09-09 22:13:01 -05:00
super().__init__(obj)
2019-10-05 11:14:19 -05:00
@property
def name(self):
return self.obj.Name
def apply(self, properties):
set_properties(self.obj, properties)
return
class LOCellStyles(object):
def __init__(self, obj):
self._obj = obj
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
2019-09-09 22:13:01 -05:00
@property
def obj(self):
return self._obj
2019-10-05 11:14:19 -05:00
@property
def names(self):
return self.obj.ElementNames
def apply(self, style, properties):
set_properties(style, properties)
return
class LOCalc(LODocument):
def __init__(self, obj):
super().__init__(obj)
self._sheets = obj.getSheets()
def __getitem__(self, index):
if isinstance(index, str):
index = [s.Name for s in self._sheets if s.CodeName == index][0] or index
return LOCalcSheet(self._sheets[index], self)
2019-10-10 00:07:45 -05:00
def __setitem__(self, key, value):
self._sheets[key] = value
def __contains__(self, item):
return item in self.obj.Sheets
2019-10-12 20:16:00 -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
2019-09-09 22:13:01 -05:00
@property
def active(self):
return LOCalcSheet(self._cc.getActiveSheet(), self)
2019-10-05 11:14:19 -05:00
def activate(self, sheet):
obj = sheet
if isinstance(sheet, LOCalcSheet):
obj = sheet.obj
elif isinstance(sheet, str):
obj = self[sheet].obj
self._cc.setActiveSheet(obj)
return
2019-09-09 22:13:01 -05:00
@property
def selection(self):
sel = self.obj.getCurrentSelection()
if sel.ImplementationName in OBJ_TYPE_RANGES:
sel = LOCellRange(sel, self)
return sel
2019-10-05 11:14:19 -05:00
@property
def sheets(self):
return LOCalcSheets(self._sheets, self)
@property
def names(self):
return self.sheets.names
@property
def cell_style(self):
obj = self.obj.getStyleFamilies()['CellStyles']
return LOCellStyles(obj)
2019-10-10 00:07:45 -05:00
def create(self):
return self.obj.createInstance('com.sun.star.sheet.Spreadsheet')
def insert(self, name, pos=-1):
# ~ sheet = obj.createInstance('com.sun.star.sheet.Spreadsheet')
# ~ obj.Sheets['New'] = sheet
2019-10-05 11:14:19 -05:00
index = pos
if pos < 0:
index = self._sheets.Count + pos + 1
if isinstance(name, str):
self._sheets.insertNewByName(name, index)
else:
for n in name:
self._sheets.insertNewByName(n, index)
name = n
return LOCalcSheet(self._sheets[name], self)
2019-10-10 00:07:45 -05:00
def move(self, name, pos=-1):
return self.sheets.move(name, pos)
def remove(self, name):
return self.sheets.remove(name)
2019-10-05 11:14:19 -05:00
def copy(self, source='', target='', pos=-1):
index = pos
if pos < 0:
index = self._sheets.Count + pos + 1
names = source
if not names:
names = self.names
elif isinstance(source, str):
names = (source,)
new_names = target
if not target:
new_names = [n + '_2' for n in names]
elif isinstance(target, str):
new_names = (target,)
for i, ns in enumerate(names):
self.sheets.copy(ns, new_names[i], index + i)
return LOCalcSheet(self._sheets[index], self)
2019-10-10 00:07:45 -05:00
def copy_from(self, doc, source='', target='', pos=-1):
2019-10-05 11:14:19 -05:00
index = pos
if pos < 0:
index = self._sheets.Count + pos + 1
names = source
if not names:
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,)
for i, n in enumerate(names):
self._sheets.importSheet(doc.obj, n, index + i)
self.sheets[index + i].name = new_names[i]
# ~ doc.getCurrentController().setActiveSheet(sheet)
# ~ For controls in sheet
# ~ doc.getCurrentController().setFormDesignMode(False)
return LOCalcSheet(self._sheets[index], self)
def sort(self, reverse=False):
names = sorted(self.names, reverse=reverse)
for i, n in enumerate(names):
self.sheets.move(n, i)
return
2019-09-09 22:13:01 -05:00
def get_cell(self, index=None):
"""
index is str 'A1'
index is tuple (row, col)
"""
if index is None:
cell = self.selection.first
else:
cell = LOCellRange(self.active[index].obj, self)
return cell
2019-09-14 17:40:07 -05:00
def select(self, rango):
r = rango
if hasattr(rango, 'obj'):
r = rango.obj
elif isinstance(rango, str):
r = self.get_cell(rango).obj
self._cc.select(r)
return
2019-09-09 22:13:01 -05:00
2019-10-05 11:14:19 -05:00
def create_cell_style(self, name=''):
obj = self.create_instance('com.sun.star.style.CellStyle')
if name:
self.cell_style[name] = obj
return LOCellStyle(obj)
2019-10-12 20:16:00 -05:00
def clear_undo(self):
self.obj.getUndoManager().clear()
return
def filter_by_color(self, cell=None):
if cell is None:
cell = self.selection.first
cr = cell.current_region
col = cell.column - cr.column
rangos = cell.get_column(col).visible
for r in rangos:
for row in range(r.rows):
c = r[row, 0]
if c.back_color != cell.back_color:
c.rows_visible = False
return
2019-10-05 11:14:19 -05:00
class LOCalcSheets(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
def __getitem__(self, index):
return LOCalcSheet(self.obj[index], self.doc)
@property
def obj(self):
return self._obj
@property
def doc(self):
return self._doc
@property
def count(self):
return self.obj.Count
@property
def names(self):
return self.obj.ElementNames
def copy(self, name, new_name, pos):
self.obj.copyByName(name, new_name, pos)
return
def move(self, name, pos):
index = pos
if pos < 0:
index = self.count + pos + 1
sheet = self.obj[name]
self.obj.moveByName(sheet.Name, index)
return
def remove(self, name):
sheet = self.obj[name]
self.obj.removeByName(sheet.Name)
return
2019-09-09 22:13:01 -05:00
class LOCalcSheet(object):
def __init__(self, obj, doc):
2019-09-08 00:25:30 -05:00
self._obj = obj
2019-09-09 22:13:01 -05:00
self._doc = doc
self._init_values()
def __getitem__(self, index):
return LOCellRange(self.obj[index], self.doc)
2019-10-10 00:07:45 -05:00
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
pass
2019-09-09 22:13:01 -05:00
def _init_values(self):
2019-10-05 11:14:19 -05:00
self._events = None
2019-10-14 17:23:15 -05:00
self._dp = self.obj.getDrawPage()
return
2019-09-09 22:13:01 -05:00
@property
def obj(self):
return self._obj
@property
def doc(self):
return self._doc
2019-10-05 11:14:19 -05:00
@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
2019-10-12 20:16:00 -05:00
@property
def color(self):
return self._obj.TabColor
@color.setter
def color(self, value):
self._obj.TabColor = get_color(value)
@property
def active(self):
return self.doc.selection.first
2019-10-05 11:14:19 -05:00
def activate(self):
self.doc.activate(self.obj)
return
2019-10-10 00:07:45 -05:00
@property
def visible(self):
return self.obj.IsVisible
@visible.setter
def visible(self, value):
self.obj.IsVisible = value
2019-10-12 20:16:00 -05:00
@property
def is_protected(self):
return self._obj.isProtected()
2019-10-10 00:07:45 -05:00
@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
def get_cursor(self, cell):
return self.obj.createCursorByRange(cell)
2019-10-12 20:16:00 -05:00
def exists_chart(self, name):
return name in self.obj.Charts.ElementNames
2019-10-14 17:23:15 -05:00
@property
def forms(self):
return LOForms(self._dp.getForms(), self.doc)
2019-10-05 11:14:19 -05:00
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers
self._connect_listeners()
def _connect_listeners(self):
if self.events is None:
return
listeners = {
'addModifyListener': EventsModify,
}
for key, value in listeners.items():
getattr(self.obj, key)(listeners[key](self.events))
print('add_listener')
return
2019-09-09 22:13:01 -05:00
class LOWriter(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def obj(self):
return self._obj
@property
def string(self):
return self._obj.getText().String
@property
def text(self):
return self._obj.getText()
@property
def cursor(self):
return self.text.createTextCursor()
2019-10-14 23:43:42 -05:00
@property
def paragraphs(self):
return [LOTextRange(p) for p in self.text]
2019-09-09 22:13:01 -05:00
@property
def selection(self):
2019-10-05 16:25:05 -05:00
sel = self.obj.getCurrentSelection()
2019-09-09 22:13:01 -05:00
return LOTextRange(sel[0])
2019-10-12 20:16:00 -05:00
def write(self, data, cursor=None):
cursor = cursor or self.selection.cursor.getEnd()
if data.startswith('\n'):
c = data.split('\n')
for i in range(len(c)-1):
self.text.insertControlCharacter(cursor, PARAGRAPH_BREAK, False)
else:
self.text.insertString(cursor, data, False)
return
def insert_table(self, data, cursor=None):
cursor = cursor or self.selection.cursor.getEnd()
table = self.obj.createInstance('com.sun.star.text.TextTable')
rows = len(data)
cols = len(data[0])
table.initialize(rows, cols)
self.insert_content(cursor, table)
table.DataArray = data
return WriterTable(table)
def create_chart(self, tipo, cursor=None):
cursor = cursor or self.selection.cursor.getEnd()
chart = LOChart(None, tipo)
chart.cursor = cursor
chart.doc = self
return chart
2019-09-09 22:13:01 -05:00
def insert_content(self, cursor, data, replace=False):
self.text.insertTextContent(cursor, data, replace)
return
# ~ f = doc.createInstance('com.sun.star.text.TextFrame')
# ~ f.setSize(Size(10000, 500))
def insert_image(self, path, **kwargs):
cursor = kwargs.get('cursor', self.selection.cursor.getEnd())
w = kwargs.get('width', 1000)
h = kwargs.get('Height', 1000)
image = self.create_instance('com.sun.star.text.GraphicObject')
image.GraphicURL = _path_url(path)
image.AnchorType = AS_CHARACTER
image.Width = w
image.Height = h
self.insert_content(cursor, image)
return
2019-10-14 23:43:42 -05:00
def go_start(self):
cursor = self._cc.getViewCursor()
cursor.gotoStart(False)
return cursor
def go_end(self):
cursor = self._cc.getViewCursor()
cursor.gotoEnd(False)
return cursor
def select(self, text):
self._cc.select(text)
return
2019-09-09 22:13:01 -05:00
class LOTextRange(object):
def __init__(self, obj):
self._obj = obj
2019-10-14 23:43:42 -05:00
self._is_paragraph = self.obj.ImplementationName == 'SwXParagraph'
self._is_table = self.obj.ImplementationName == 'SwXTextTable'
2019-09-09 22:13:01 -05:00
@property
def obj(self):
return self._obj
2019-10-14 23:43:42 -05:00
@property
def is_paragraph(self):
return self._is_paragraph
@property
def is_table(self):
return self._is_table
2019-09-09 22:13:01 -05:00
@property
def string(self):
return self.obj.String
@property
def text(self):
return self.obj.getText()
@property
def cursor(self):
return self.text.createTextCursorByRange(self.obj)
2019-10-14 23:43:42 -05:00
class LOBase(object):
2019-10-05 11:14:19 -05:00
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, name, path='', **kwargs):
self._name = name
self._path = path
self._dbc = create_instance('com.sun.star.sdb.DatabaseContext')
if path:
path_url = _path_url(path)
db = self._dbc.createInstance()
db.URL = 'sdbc:embedded:firebird'
db.DatabaseDocument.storeAsURL(path_url, ())
if not self.exists:
self._dbc.registerDatabaseLocation(name, path_url)
else:
2019-10-14 23:43:42 -05:00
if name.startswith('odbc:'):
self._con = self._odbc(name, kwargs)
else:
db = self._dbc.getByName(name)
self.path = _path_system(self._dbc.getDatabaseLocation(name))
self._con = db.getConnection('', '')
if self._con is None:
msg = 'Not connected to: {}'.format(name)
else:
msg = 'Connected to: {}'.format(name)
2019-10-05 11:14:19 -05:00
debug(msg)
2019-09-09 22:13:01 -05:00
2019-10-14 23:43:42 -05:00
def _odbc(self, name, kwargs):
dm = create_instance('com.sun.star.sdbc.DriverManager')
args = dict_to_property(kwargs)
try:
con = dm.getConnectionWithInfo('sdbc:{}'.format(name), args)
return con
except Exception as e:
error(str(e))
return None
@property
def obj(self):
return self._obj
2019-10-05 11:14:19 -05:00
@property
def name(self):
return self._name
@property
def connection(self):
return self._con
@property
def path(self):
return self._path
@path.setter
def path(self, value):
self._path = value
@property
def exists(self):
return self._dbc.hasRegisteredDatabase(self.name)
@classmethod
def register(self, path, name):
if not self._dbc.hasRegisteredDatabase(name):
self._dbc.registerDatabaseLocation(name, _path_url(path))
return
2019-10-14 23:43:42 -05:00
def revoke(self, name):
self._dbc.revokeDatabaseLocation(name)
return True
2019-10-05 11:14:19 -05:00
def save(self):
# ~ self._db.connection.commit()
# ~ self._db.connection.getTables().refresh()
# ~ oDisp.executeDispatch(oFrame,".uno:DBRefreshTables", "", 0, Array())
self._obj.DatabaseDocument.store()
self.refresh()
return
def close(self):
self._con.close()
return
def refresh(self):
self._con.getTables().refresh()
return
def get_tables(self):
tables = self._con.getTables()
tables = [tables.getByIndex(i) for i in range(tables.Count)]
return tables
def cursor(self, sql, params):
cursor = self._con.prepareStatement(sql)
for i, v in enumerate(params, 1):
if not type(v) in self.TYPES:
error('Type not support')
debug((i, type(v), v, self.TYPES[type(v)]))
getattr(cursor, self.TYPES[type(v)])(i, v)
return cursor
def execute(self, sql, params):
debug(sql)
if params:
cursor = self.cursor(sql, params)
cursor.execute()
else:
cursor = self._con.createStatement()
cursor.execute(sql)
2019-10-14 23:43:42 -05:00
# ~ resulset = cursor.executeQuery(sql)
# ~ rows = cursor.executeUpdate(sql)
2019-10-05 11:14:19 -05:00
self.save()
return cursor
2019-09-09 22:13:01 -05:00
class LODrawImpress(LODocument):
def __init__(self, obj):
super().__init__(obj)
@property
def draw_page(self):
return self._cc.getCurrentPage()
def insert_image(self, path, **kwargs):
w = kwargs.get('width', 3000)
h = kwargs.get('Height', 1000)
x = kwargs.get('X', 1000)
y = kwargs.get('Y', 1000)
image = self.create_instance('com.sun.star.drawing.GraphicObjectShape')
image.GraphicURL = _path_url(path)
image.Size = Size(w, h)
image.Position = Point(x, y)
self.draw_page.add(image)
return
class LOImpress(LODrawImpress):
def __init__(self, obj):
super().__init__(obj)
class LODraw(LODrawImpress):
def __init__(self, obj):
super().__init__(obj)
class LOMath(LODocument):
def __init__(self, obj):
super().__init__(obj)
class LOBasicIde(LODocument):
def __init__(self, obj):
super().__init__(obj)
2019-09-14 17:40:07 -05:00
@property
def selection(self):
sel = self._cc.getSelection()
return sel
2019-09-09 22:13:01 -05:00
class LOCellRange(object):
def __init__(self, obj, doc):
self._obj = obj
self._doc = doc
2019-09-08 00:25:30 -05:00
self._init_values()
def __enter__(self):
return self
2019-10-10 00:07:45 -05:00
def __exit__(self, exc_type, exc_value, traceback):
2019-09-08 00:25:30 -05:00
pass
2019-09-09 22:13:01 -05:00
def __getitem__(self, index):
return LOCellRange(self.obj[index], self.doc)
2019-09-08 00:25:30 -05:00
def _init_values(self):
2019-09-09 22:13:01 -05:00
self._type_obj = self.obj.ImplementationName
2019-09-08 00:25:30 -05:00
self._type_content = EMPTY
if self._type_obj == OBJ_CELL:
2019-09-09 22:13:01 -05:00
self._type_content = self.obj.getType()
2019-09-08 00:25:30 -05:00
return
@property
def obj(self):
return self._obj
2019-09-09 22:13:01 -05:00
@property
def doc(self):
return self._doc
@property
def type(self):
return self._type_obj
2019-09-08 00:25:30 -05:00
@property
def type_content(self):
return self._type_content
2019-09-09 22:13:01 -05:00
@property
def first(self):
if self.type == OBJ_RANGES:
obj = LOCellRange(self.obj[0][0,0], self.doc)
else:
obj = LOCellRange(self.obj[0,0], self.doc)
return obj
2019-09-08 00:25:30 -05:00
@property
def value(self):
v = None
if self._type_content == VALUE:
v = self.obj.getValue()
elif self._type_content == TEXT:
v = self.obj.getString()
elif self._type_content == FORMULA:
v = self.obj.getFormula()
return v
@value.setter
def value(self, data):
if isinstance(data, str):
if data.startswith('='):
self.obj.setFormula(data)
else:
self.obj.setString(data)
elif isinstance(data, (int, float)):
self.obj.setValue(data)
2019-10-12 20:16:00 -05:00
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)
2019-09-08 00:25:30 -05:00
2019-09-09 22:13:01 -05:00
@property
def data(self):
return self.obj.getDataArray()
@data.setter
def data(self, values):
if isinstance(values, list):
values = tuple(values)
self.obj.setDataArray(values)
2019-10-10 00:07:45 -05:00
@property
def formula(self):
return self.obj.getFormulaArray()
@formula.setter
def formula(self, values):
if isinstance(values, list):
values = tuple(values)
self.obj.setFormulaArray(values)
2019-10-12 20:16:00 -05:00
@property
def column(self):
a = self.address
if hasattr(a, 'Column'):
c = a.Column
else:
c = a.StartColumn
return c
2019-10-10 00:07:45 -05:00
@property
def columns(self):
return self._obj.Columns.Count
@property
def rows(self):
return self._obj.Rows.Count
def to_size(self, rows, cols):
cursor = self.sheet.get_cursor(self.obj[0,0])
cursor.collapseToSize(cols, rows)
return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc)
def copy_from(self, rango):
data = rango
if isinstance(rango, LOCellRange):
data = rango.data
rows = len(data)
cols = len(data[0])
self.to_size(rows, cols).data = data
return
def copy_to(self, cell, formula=False):
rango = cell.to_size(self.rows, self.columns)
if formula:
rango.formula = self.data
else:
rango.data = self.data
return
2019-10-05 11:14:19 -05:00
def offset(self, row=1, col=0):
ra = self.obj.getRangeAddress()
col = ra.EndColumn + col
row = ra.EndRow + row
2019-10-10 00:07:45 -05:00
return LOCellRange(self.sheet[row, col].obj, self.doc)
@property
def next_cell(self):
a = self.current_region.address
if hasattr(a, 'StartColumn'):
col = a.StartColumn
else:
col = a.Column
if hasattr(a, 'EndRow'):
row = a.EndRow + 1
else:
row = a.Row + 1
return LOCellRange(self.sheet[row, col].obj, self.doc)
2019-09-08 00:25:30 -05:00
@property
def sheet(self):
2019-10-10 00:07:45 -05:00
return LOCalcSheet(self.obj.Spreadsheet, self.doc)
2019-09-08 00:25:30 -05:00
2019-10-12 20:16:00 -05:00
@property
def charts(self):
return self.obj.Spreadsheet.Charts
@property
def ps(self):
ps = Rectangle()
s = self.obj.Size
p = self.obj.Position
ps.X = p.X
ps.Y = p.Y
ps.Width = s.Width
ps.Height = s.Height
return ps
2019-09-08 00:25:30 -05:00
@property
def draw_page(self):
2019-10-10 00:07:45 -05:00
return self.sheet.obj.getDrawPage()
2019-09-08 00:25:30 -05:00
@property
def name(self):
return self.obj.AbsoluteName
@property
def address(self):
if self._type_obj == OBJ_CELL:
a = self.obj.getCellAddress()
elif self._type_obj == OBJ_RANGE:
a = self.obj.getRangeAddress()
else:
a = self.obj.getRangeAddressesAsString()
return a
2019-09-09 22:13:01 -05:00
@property
def current_region(self):
2019-10-10 00:07:45 -05:00
cursor = self.sheet.get_cursor(self.obj[0,0])
2019-09-09 22:13:01 -05:00
cursor.collapseToCurrentRegion()
2019-10-10 00:07:45 -05:00
return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc)
@property
def visible(self):
cursor = self.sheet.get_cursor(self.obj)
rangos = [LOCellRange(self.sheet[r.AbsoluteName].obj, self.doc)
for r in cursor.queryVisibleCells()]
return tuple(rangos)
@property
def empty(self):
cursor = self.sheet.get_cursor(self.obj)
rangos = [LOCellRange(self.sheet[r.AbsoluteName].obj, self.doc)
for r in cursor.queryEmptyCells()]
return tuple(rangos)
2019-09-09 22:13:01 -05:00
2019-10-12 20:16:00 -05:00
@property
def back_color(self):
return self._obj.CellBackColor
@back_color.setter
def back_color(self, value):
self._obj.CellBackColor = get_color(value)
2019-10-05 11:14:19 -05:00
@property
def cell_style(self):
return self.obj.CellStyle
@cell_style.setter
def cell_style(self, value):
self.obj.CellStyle = value
@property
def auto_format(self):
return self.obj.CellStyle
@auto_format.setter
def auto_format(self, value):
self.obj.autoFormat(value)
2019-09-09 22:13:01 -05:00
def insert_image(self, path, **kwargs):
2019-09-08 00:25:30 -05:00
s = self.obj.Size
w = kwargs.get('width', s.Width)
h = kwargs.get('Height', s.Height)
2019-09-09 22:13:01 -05:00
img = self.doc.create_instance('com.sun.star.drawing.GraphicObjectShape')
2019-09-08 00:25:30 -05:00
img.GraphicURL = _path_url(path)
self.draw_page.add(img)
img.Anchor = self.obj
img.setSize(Size(w, h))
return
2019-10-14 23:43:42 -05:00
def insert_shape(self, tipo, **kwargs):
s = self.obj.Size
w = kwargs.get('width', s.Width)
h = kwargs.get('Height', s.Height)
img = self.doc.create_instance('com.sun.star.drawing.{}Shape'.format(tipo))
set_properties(img, kwargs)
self.draw_page.add(img)
img.Anchor = self.obj
img.setSize(Size(w, h))
return
2019-09-14 17:40:07 -05:00
def select(self):
self.doc._cc.select(self.obj)
return
2019-10-05 11:14:19 -05:00
def in_range(self, rango):
if isinstance(rango, LOCellRange):
address = rango.address
else:
address = rango.getRangeAddress()
2019-10-10 00:07:45 -05:00
cursor = self.sheet.get_cursor(self.obj)
2019-10-05 11:14:19 -05:00
result = cursor.queryIntersection(address)
return bool(result.Count)
def fill(self, source=1):
self.obj.fillAuto(0, source)
return
2019-10-10 00:07:45 -05:00
def clear(self, what=31):
2019-10-12 20:16:00 -05:00
# ~ http://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
2019-10-10 00:07:45 -05:00
self.obj.clearContents(what)
return
2019-10-12 20:16:00 -05:00
@property
def rows_visible(self):
return self._obj.getRows().IsVisible
@rows_visible.setter
def rows_visible(self, value):
self._obj.getRows().IsVisible = value
@property
def columns_visible(self):
return self._obj.getColumns().IsVisible
@columns_visible.setter
def columns_visible(self, value):
self._obj.getColumns().IsVisible = value
def get_column(self, index=0, first=False):
ca = self.address
ra = self.current_region.address
if hasattr(ca, 'Column'):
col = ca.Column
else:
col = ca.StartColumn + index
start = 1
if first:
start = 0
if hasattr(ra, 'Row'):
row_start = ra.Row + start
row_end = ra.Row + 1
else:
row_start = ra.StartRow + start
row_end = ra.EndRow + 1
return LOCellRange(self.sheet[row_start:row_end, col:col+1].obj, self.doc)
def import_csv(self, path, **kwargs):
data = import_csv(path, **kwargs)
self.copy_from(data)
return
def export_csv(self, path, **kwargs):
data = self.current_region.data
export_csv(path, data, **kwargs)
return
def create_chart(self, tipo):
chart = LOChart(None, tipo)
chart.cell = self
return chart
2019-09-08 00:25:30 -05:00
2019-09-09 22:13:01 -05:00
class EventsListenerBase(unohelper.Base, XEventListener):
2019-10-10 00:07:45 -05:00
def __init__(self, controller, name, window=None):
2019-09-09 22:13:01 -05:00
self._controller = controller
2019-10-10 00:07:45 -05:00
self._name = name
2019-09-09 22:13:01 -05:00
self._window = window
def disposing(self, event):
self._controller = None
if not self._window is None:
self._window.setMenuBar(None)
class EventsButton(EventsListenerBase, XActionListener):
2019-10-10 00:07:45 -05:00
def __init__(self, controller, name):
super().__init__(controller, name)
2019-09-09 22:13:01 -05:00
def actionPerformed(self, event):
2019-10-10 00:07:45 -05:00
event_name = '{}_action'.format(self._name)
2019-09-09 22:13:01 -05:00
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
2019-10-10 00:07:45 -05:00
class EventsMouse(EventsListenerBase, XMouseListener, XMouseMotionListener):
2019-09-09 22:13:01 -05:00
2019-10-10 00:07:45 -05:00
def __init__(self, controller, name):
super().__init__(controller, name)
2019-09-09 22:13:01 -05:00
def mousePressed(self, event):
2019-10-10 00:07:45 -05:00
event_name = '{}_click'.format(self._name)
2019-09-09 22:13:01 -05:00
if event.ClickCount == 2:
2019-10-19 20:01:04 -05:00
event_name = '{}_double_click'.format(self._name)
2019-09-09 22:13:01 -05:00
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
2019-10-10 00:07:45 -05:00
# ~ XMouseMotionListener
def mouseMoved(self, event):
pass
def mouseDragged(self, event):
pass
2019-09-09 22:13:01 -05:00
2019-10-14 17:23:15 -05:00
class EventsMouseLink(EventsMouse):
def mouseEntered(self, event):
obj = event.Source.Model
obj.TextColor = get_color('blue')
return
def mouseExited(self, event):
obj = event.Source.Model
obj.TextColor = 0
return
2019-09-27 12:17:21 -05:00
class EventsMouseGrid(EventsMouse):
selected = False
def mousePressed(self, event):
super().mousePressed(event)
# ~ obj = event.Source
# ~ col = obj.getColumnAtPoint(event.X, event.Y)
# ~ row = obj.getRowAtPoint(event.X, event.Y)
# ~ print(col, row)
# ~ if col == -1 and row == -1:
# ~ if self.selected:
# ~ obj.deselectAllRows()
# ~ else:
# ~ obj.selectAllRows()
# ~ self.selected = not self.selected
return
def mouseReleased(self, event):
2019-10-19 20:01:04 -05:00
# ~ obj = event.Source
# ~ col = obj.getColumnAtPoint(event.X, event.Y)
# ~ row = obj.getRowAtPoint(event.X, event.Y)
# ~ if row == -1 and col > -1:
# ~ gdm = obj.Model.GridDataModel
# ~ for i in range(gdm.RowCount):
# ~ gdm.updateRowHeading(i, i + 1)
2019-09-27 12:17:21 -05:00
return
2019-10-05 11:14:19 -05:00
class EventsModify(EventsListenerBase, XModifyListener):
def __init__(self, controller):
super().__init__(controller)
def modified(self, event):
event_name = '{}_modified'.format(event.Source.Name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
2019-10-14 19:48:13 -05:00
class EventsItem(EventsListenerBase, XItemListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def disposing(self, event):
pass
def itemStateChanged(self, event):
pass
class EventsItemRoadmap(EventsItem):
def itemStateChanged(self, event):
dialog = event.Source.Context.Model
dialog.Step = event.ItemId + 1
return
2019-10-14 23:43:42 -05:00
class EventsFocus(EventsListenerBase, XFocusListener):
def __init__(self, controller, name):
super().__init__(controller, name)
def focusGained(self, event):
2019-10-19 20:01:04 -05:00
service = event.Source.Model.ImplementationName
if service == 'stardiv.Toolkit.UnoControlListBoxModel':
return
2019-10-14 23:43:42 -05:00
obj = event.Source.Model
obj.BackgroundColor = COLOR_ON_FOCUS
def focusLost(self, event):
obj = event.Source.Model
obj.BackgroundColor = -1
2019-10-10 00:07:45 -05:00
class EventsKey(EventsListenerBase, XKeyListener):
"""
event.KeyChar
event.KeyCode
event.KeyFunc
event.Modifiers
"""
2019-10-19 20:01:04 -05:00
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
class EventsKeyWindow(EventsListenerBase, XKeyListener):
"""
event.KeyChar
event.KeyCode
event.KeyFunc
event.Modifiers
"""
2019-10-10 00:07:45 -05:00
def __init__(self, cls):
super().__init__(cls.events, cls.name)
self._cls = cls
def keyPressed(self, event):
pass
def keyReleased(self, event):
event_name = '{}_key_released'.format(self._cls.name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
else:
if event.KeyFunc == QUIT and hasattr(self._cls, 'close'):
self._cls.close()
return
class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener):
def __init__(self, cls):
self._cls = cls
super().__init__(cls.events, cls.name, cls._window)
def windowOpened(self, event):
event_name = '{}_opened'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def windowActivated(self, event):
control_name = '{}_activated'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
def windowDeactivated(self, event):
control_name = '{}_deactivated'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
def windowMinimized(self, event):
pass
def windowNormalized(self, event):
pass
def windowClosing(self, event):
if self._window:
control_name = 'window_closing'
else:
control_name = '{}_closing'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
# ~ else:
# ~ if not self._modal and not self._block:
# ~ event.Source.Visible = False
return
def windowClosed(self, event):
control_name = '{}_closed'.format(event.Source.Model.Name)
if hasattr(self._controller, control_name):
getattr(self._controller, control_name)(event)
return
# ~ XWindowListener
def windowResized(self, event):
# ~ sb = self._container.getControl('subcontainer')
# ~ sb.setPosSize(0, 0, event.Width, event.Height, SIZE)
event_name = '{}_resized'.format(self._name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def windowMoved(self, event):
pass
def windowShown(self, event):
pass
def windowHidden(self, event):
pass
class EventsMenu(EventsListenerBase, XMenuListener):
def __init__(self, controller):
super().__init__(controller, '')
def itemHighlighted(self, event):
pass
def itemSelected(self, event):
name = event.Source.getCommand(event.MenuId)
if name.startswith('menu'):
event_name = '{}_selected'.format(name)
else:
event_name = 'menu_{}_selected'.format(name)
if hasattr(self._controller, event_name):
getattr(self._controller, event_name)(event)
return
def itemActivated(self, event):
return
def itemDeactivated(self, event):
return
2019-09-09 22:13:01 -05:00
class UnoBaseObject(object):
def __init__(self, obj):
self._obj = obj
self._model = self.obj.Model
self._rules = {}
@property
def obj(self):
return self._obj
@property
def model(self):
return self._model
@property
def name(self):
return self.model.Name
@property
def parent(self):
return self.obj.getContext()
2019-10-10 00:07:45 -05:00
def _get_possize(self, name):
ps = self.obj.getPosSize()
return getattr(ps, name)
def _set_possize(self, name, value):
ps = self.obj.getPosSize()
setattr(ps, name, value)
self.obj.setPosSize(ps.X, ps.Y, ps.Width, ps.Height, POSSIZE)
return
2019-09-09 22:13:01 -05:00
@property
def x(self):
2019-10-10 00:07:45 -05:00
if hasattr(self.model, 'PositionX'):
return self.model.PositionX
return self._get_possize('X')
2019-09-09 22:13:01 -05:00
@x.setter
def x(self, value):
2019-10-10 00:07:45 -05:00
if hasattr(self.model, 'PositionX'):
self.model.PositionX = value
else:
self._set_possize('X', value)
2019-09-09 22:13:01 -05:00
@property
def y(self):
2019-10-10 00:07:45 -05:00
if hasattr(self.model, 'PositionY'):
return self.model.PositionY
return self._get_possize('Y')
2019-09-09 22:13:01 -05:00
@y.setter
def y(self, value):
2019-10-10 00:07:45 -05:00
if hasattr(self.model, 'PositionY'):
self.model.PositionY = value
else:
self._set_possize('Y', value)
2019-09-09 22:13:01 -05:00
@property
def width(self):
return self._model.Width
@width.setter
def width(self, value):
self._model.Width = value
@property
def height(self):
2019-10-10 00:07:45 -05:00
if hasattr(self._model, 'Height'):
return self._model.Height
ps = self.obj.getPosSize()
return ps.Height
2019-09-09 22:13:01 -05:00
@height.setter
def height(self, value):
self._model.Height = value
@property
def tag(self):
return self.model.Tag
@tag.setter
def tag(self, value):
self.model.Tag = value
2019-10-19 20:01:04 -05:00
@property
def visible(self):
return self.obj.Visible
@visible.setter
def visible(self, value):
self.obj.setVisible(value)
@property
def enabled(self):
return self.model.Enabled
@enabled.setter
def enabled(self, value):
self.model.Enabled = value
2019-09-09 22:13:01 -05:00
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
2019-10-14 23:43:42 -05:00
@property
def back_color(self):
return self.model.BackgroundColor
@back_color.setter
def back_color(self, value):
self.model.BackgroundColor = value
2019-09-09 22:13:01 -05:00
@property
def rules(self):
return self._rules
@rules.setter
def rules(self, value):
self._rules = value
def set_focus(self):
self.obj.setFocus()
return
def center(self, horizontal=True, vertical=False):
p = self.parent.Model
w = p.Width
h = p.Height
if horizontal:
x = w / 2 - self.width / 2
self.x = x
if vertical:
y = h / 2 - self.height / 2
self.y = y
return
def move(self, origin, x=0, y=5):
if x:
2019-10-19 20:01:04 -05:00
self.x = origin.x + origin.width + x
2019-09-09 22:13:01 -05:00
if y:
2019-10-19 20:01:04 -05:00
self.y = origin.y + origin.height + y
return
def possize(self, origin):
self.x = origin.x
self.y = origin.y
self.width = origin.width
self.height = origin.height
2019-09-09 22:13:01 -05:00
return
class UnoLabel(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
2019-09-27 12:17:21 -05:00
@property
def type(self):
return 'label'
2019-09-09 22:13:01 -05:00
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
2019-10-14 17:23:15 -05:00
class UnoLabelLink(UnoLabel):
def __init__(self, obj):
super().__init__(obj)
@property
def type(self):
return 'link'
2019-09-09 22:13:01 -05:00
class UnoButton(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
2019-09-27 12:17:21 -05:00
@property
def type(self):
return 'button'
2019-09-09 22:13:01 -05:00
@property
def value(self):
return self.model.Label
@value.setter
def value(self, value):
self.model.Label = value
class UnoText(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
2019-09-27 12:17:21 -05:00
@property
def type(self):
return 'text'
2019-09-09 22:13:01 -05:00
@property
def value(self):
return self.model.Text
@value.setter
def value(self, value):
self.model.Text = value
def validate(self):
return
class UnoListBox(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
2019-10-05 11:14:19 -05:00
@property
def type(self):
return 'listbox'
2019-09-09 22:13:01 -05:00
@property
def value(self):
return self.obj.SelectedItem
2019-10-19 20:01:04 -05:00
@property
def count(self):
return len(self.data)
2019-09-09 22:13:01 -05:00
@property
def data(self):
2019-10-19 20:01:04 -05:00
return self.model.StringItemList
2019-09-09 22:13:01 -05:00
@data.setter
def data(self, values):
2019-10-19 20:01:04 -05:00
self.model.StringItemList = list(sorted(values))
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.obj.removeItems(0, self.count)
return
def _set_image_url(self, image):
if exists_path(image):
return _path_url(image)
if not ID_EXTENSION:
return ''
path = get_path_extension(ID_EXTENSION)
path = join(path, DIR['images'], image)
return _path_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)
2019-09-09 22:13:01 -05:00
return
2019-09-27 12:17:21 -05:00
class UnoGrid(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._gdm = self._model.GridDataModel
# ~ self._data = []
self._columns = {}
# ~ self._format_columns = ()
def __getitem__(self, index):
value = self._gdm.getCellData(index[0], index[1])
return value
@property
def type(self):
return 'grid'
def _format_cols(self):
rows = tuple(tuple(
self._format_columns[i].format(r) for i, r in enumerate(row)) for row in self._data
)
return rows
# ~ @property
# ~ def format_columns(self):
# ~ return self._format_columns
# ~ @format_columns.setter
# ~ def format_columns(self, value):
# ~ self._format_columns = value
@property
def data(self):
return self._data
@data.setter
def data(self, values):
# ~ self._data = values
self._gdm.removeAllRows()
headings = tuple(range(1, len(values) + 1))
self._gdm.addRows(headings, values)
# ~ rows = range(grid_dm.RowCount)
# ~ colors = [COLORS['GRAY'] if r % 2 else COLORS['WHITE'] for r in rows]
# ~ grid.Model.RowBackgroundColors = tuple(colors)
return
@property
def row(self):
return self.obj.CurrentRow
@property
def rows(self):
return self._gdm.RowCount
@property
def column(self):
return self.obj.CurrentColumn
@property
def columns(self):
return self._gdm.ColumnCount
def set_cell_tooltip(self, col, row, value):
self._gdm.updateCellToolTip(col, row, value)
return
def get_cell_tooltip(self, col, row):
value = self._gdm.getCellToolTip(col, row)
return value
def _validate_column(self, data):
row = []
for i, d in enumerate(data):
if i in self._columns:
if 'image' in self._columns[i]:
row.append(self._columns[i]['image'])
else:
row.append(d)
return tuple(row)
def add_row(self, data):
# ~ self._data.append(data)
data = self._validate_column(data)
self._gdm.addRow(self.rows + 1, data)
return
def remove_row(self, row):
self._gdm.removeRow(row)
# ~ del self._data[row]
self._update_row_heading()
return
def _update_row_heading(self):
for i in range(self.rows):
self._gdm.updateRowHeading(i, i + 1)
return
def sort(self, column, asc=True):
self._gdm.sortByColumn(column, asc)
# ~ self._data.sort(key=itemgetter(column), reverse=not asc)
self._update_row_heading()
return
def set_column_image(self, column, path):
gp = create_instance('com.sun.star.graphic.GraphicProvider')
data = dict_to_property({'URL': _path_url(path)})
image = gp.queryGraphic(data)
if not column in self._columns:
self._columns[column] = {}
self._columns[column]['image'] = image
return
2019-10-14 19:48:13 -05:00
class UnoRoadmap(UnoBaseObject):
def __init__(self, obj):
super().__init__(obj)
self._options = ()
@property
def options(self):
return self._options
@options.setter
def options(self, values):
self._options = values
for i, v in enumerate(values):
opt = self.model.createInstance()
opt.ID = i
opt.Label = v
self.model.insertByIndex(i, opt)
return
2019-10-19 20:01:04 -05:00
@property
def enabled(self):
return True
@enabled.setter
def enabled(self, value):
for m in self.model:
m.Enabled = value
return
2019-10-14 23:43:42 -05:00
def set_enabled(self, index, value):
self.model.getByIndex(index).Enabled = value
return
2019-10-14 19:48:13 -05:00
2019-10-10 00:07:45 -05:00
def get_custom_class(tipo, obj):
classes = {
'label': UnoLabel,
'button': UnoButton,
'text': UnoText,
'listbox': UnoListBox,
'grid': UnoGrid,
2019-10-14 17:23:15 -05:00
'link': UnoLabelLink,
2019-10-14 19:48:13 -05:00
'roadmap': UnoRoadmap,
2019-10-10 00:07:45 -05:00
# ~ 'tab': UnoTab,
# ~ 'image': UnoImage,
# ~ 'radio': UnoRadio,
# ~ 'groupbox': UnoGroupBox,
# ~ 'tree': UnoTree,
2019-10-19 20:01:04 -05:00
'formbutton': FormButton,
2019-10-10 00:07:45 -05:00
}
return classes[tipo](obj)
def add_listeners(events, control, name=''):
listeners = {
'addActionListener': EventsButton,
'addMouseListener': EventsMouse,
2019-10-14 19:48:13 -05:00
'addItemListener': EventsItem,
2019-10-14 23:43:42 -05:00
'addFocusListener': EventsFocus,
2019-10-19 20:01:04 -05:00
'addKeyListener': EventsKey,
2019-10-10 00:07:45 -05:00
}
if hasattr(control, 'obj'):
control = contro.obj
2019-10-14 19:48:13 -05:00
# ~ debug(control.ImplementationName)
2019-10-10 00:07:45 -05:00
is_grid = control.ImplementationName == 'stardiv.Toolkit.GridControl'
2019-10-14 17:23:15 -05:00
is_link = control.ImplementationName == 'stardiv.Toolkit.UnoFixedHyperlinkControl'
2019-10-14 19:48:13 -05:00
is_roadmap = control.ImplementationName == 'stardiv.Toolkit.UnoRoadmapControl'
2019-10-10 00:07:45 -05:00
for key, value in listeners.items():
if hasattr(control, key):
if is_grid and key == 'addMouseListener':
2019-10-19 20:01:04 -05:00
control.addMouseListener(EventsMouseGrid(events, name))
2019-10-10 00:07:45 -05:00
continue
2019-10-14 17:23:15 -05:00
if is_link and key == 'addMouseListener':
control.addMouseListener(EventsMouseLink(events, name))
continue
2019-10-14 19:48:13 -05:00
if is_roadmap and key == 'addItemListener':
control.addItemListener(EventsItemRoadmap(events, name))
continue
2019-10-19 20:01:04 -05:00
2019-10-10 00:07:45 -05:00
getattr(control, key)(listeners[key](events, name))
return
2019-10-12 20:16:00 -05:00
class WriterTable(ObjectBase):
def __init__(self, obj):
super().__init__(obj)
def __getitem__(self, key):
obj = super().__getitem__(key)
return WriterTableRange(obj, key, self.name)
@property
def name(self):
return self.obj.Name
@name.setter
def name(self, value):
self.obj.Name = value
class WriterTableRange(ObjectBase):
def __init__(self, obj, index, table_name):
self._index = index
self._table_name = table_name
super().__init__(obj)
self._is_cell = hasattr(self.obj, 'CellName')
def __getitem__(self, key):
obj = super().__getitem__(key)
return WriterTableRange(obj, key, self._table_name)
@property
def value(self):
return self.obj.String
@value.setter
def value(self, value):
self.obj.String = value
@property
def data(self):
return self.obj.getDataArray()
@data.setter
def data(self, values):
if isinstance(values, list):
values = tuple(values)
self.obj.setDataArray(values)
@property
def rows(self):
return len(self.data)
@property
def columns(self):
return len(self.data[0])
@property
def name(self):
if self._is_cell:
name = '{}.{}'.format(self._table_name, self.obj.CellName)
elif isinstance(self._index, str):
name = '{}.{}'.format(self._table_name, self._index)
else:
c1 = self.obj[0,0].CellName
c2 = self.obj[self.rows-1,self.columns-1].CellName
name = '{}.{}:{}'.format(self._table_name, c1, c2)
return name
def get_cell(self, *index):
return self[index]
def get_column(self, index=0, start=1):
return self[start:self.rows,index:index+1]
def get_series(self):
class Serie():
pass
series = []
for i in range(self.columns):
serie = Serie()
serie.label = self.get_cell(0,i).name
serie.data = self.get_column(i).data
serie.values = self.get_column(i).name
series.append(serie)
return series
class ChartFormat(object):
def __call__(self, obj):
for k, v in self.__dict__.items():
if hasattr(obj, k):
setattr(obj, k, v)
class LOChart(object):
BASE = 'com.sun.star.chart.{}Diagram'
def __init__(self, obj, tipo=''):
self._obj = obj
self._type = tipo
self._name = ''
self._table = None
self._data = ()
self._data_series = ()
self._cell = None
self._cursor = None
self._doc = None
self._title = ChartFormat()
self._subtitle = ChartFormat()
self._legend = ChartFormat()
self._xaxistitle = ChartFormat()
self._yaxistitle = ChartFormat()
self._xaxis = ChartFormat()
self._yaxis = ChartFormat()
self._xmaingrid = ChartFormat()
self._ymaingrid = ChartFormat()
self._xhelpgrid = ChartFormat()
self._yhelpgrid = ChartFormat()
self._area = ChartFormat()
self._wall = ChartFormat()
self._dim3d = False
self._series = ()
self._labels = ()
return
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
self.insert()
@property
def obj(self):
return self._obj
@obj.setter
def obj(self, value):
self._obj = value
@property
def name(self):
return self._name
@name.setter
def name(self, value):
self._name = value
@property
def type(self):
return self._type
@type.setter
def type(self, value):
self._type = value
@property
def table(self):
return self._table
@table.setter
def table(self, value):
self._table = value
@property
def data(self):
return self._data
@data.setter
def data(self, value):
self._data = value
@property
def cell(self):
return self._cell
@cell.setter
def cell(self, value):
self._cell = value
self.doc = value.doc
@property
def cursor(self):
return self._cursor
@cursor.setter
def cursor(self, value):
self._cursor = value
@property
def doc(self):
return self._doc
@doc.setter
def doc(self, value):
self._doc = value
@property
def width(self):
return self._width
@width.setter
def width(self, value):
self._width = value
@property
def height(self):
return self._height
@height.setter
def height(self, value):
self._height = value
@property
def title(self):
return self._title
@property
def subtitle(self):
return self._subtitle
@property
def legend(self):
return self._legend
@property
def xaxistitle(self):
return self._xaxistitle
@property
def yaxistitle(self):
return self._yaxistitle
@property
def xaxis(self):
return self._xaxis
@property
def yaxis(self):
return self._yaxis
@property
def xmaingrid(self):
return self._xmaingrid
@property
def ymaingrid(self):
return self._ymaingrid
@property
def xhelpgrid(self):
return self._xhelpgrid
@property
def yhelpgrid(self):
return self._yhelpgrid
@property
def area(self):
return self._area
@property
def wall(self):
return self._wall
@property
def dim3d(self):
return self._dim3d
@dim3d.setter
def dim3d(self, value):
self._dim3d = value
@property
def series(self):
return self._series
@series.setter
def series(self, value):
self._series = value
@property
def data_series(self):
return self._series
@data_series.setter
def data_series(self, value):
self._data_series = value
@property
def labels(self):
return self._labels
@labels.setter
def labels(self, value):
self._labels = value
def _add_series_writer(self, chart):
dp = self.doc.create_instance('com.sun.star.chart2.data.DataProvider')
chart.attachDataProvider(dp)
chart_type = chart.getFirstDiagram().getCoordinateSystems()[0].getChartTypes()[0]
self._data_series = self.table[self.data].get_series()
series = [self._create_serie(dp, s) for s in self._data_series[1:]]
chart_type.setDataSeries(tuple(series))
chart_data = chart.getData()
chart_data.ComplexRowDescriptions = self._data_series[0].data
return
def _get_series(self):
rango = self._data_series
class Serie():
pass
series = []
for i in range(0, rango.columns, 2):
serie = Serie()
serie.label = rango[0, i+1].name
serie.xvalues = rango.get_column(i).name
serie.values = rango.get_column(i+1).name
series.append(serie)
return series
def _add_series_calc(self, chart):
dp = self.doc.create_instance('com.sun.star.chart2.data.DataProvider')
chart.attachDataProvider(dp)
chart_type = chart.getFirstDiagram().getCoordinateSystems()[0].getChartTypes()[0]
series = self._get_series()
series = [self._create_serie(dp, s) for s in series]
chart_type.setDataSeries(tuple(series))
return
def _create_serie(self, dp, data):
serie = create_instance('com.sun.star.chart2.DataSeries')
rango = data.values
is_x = hasattr(data, 'xvalues')
if is_x:
xrango = data.xvalues
rango_label = data.label
lds = create_instance('com.sun.star.chart2.data.LabeledDataSequence')
values = self._create_data(dp, rango, 'values-y')
lds.setValues(values)
if data.label:
label = self._create_data(dp, rango_label, '')
lds.setLabel(label)
xlds = ()
if is_x:
xlds = create_instance('com.sun.star.chart2.data.LabeledDataSequence')
values = self._create_data(dp, xrango, 'values-x')
xlds.setValues(values)
if is_x:
serie.setData((lds, xlds))
else:
serie.setData((lds,))
return serie
def _create_data(self, dp, rango, role):
data = dp.createDataSequenceByRangeRepresentation(rango)
if not data is None:
data.Role = role
return data
def _from_calc(self):
ps = self.cell.ps
ps.Width = self.width
ps.Height = self.height
charts = self.cell.charts
data = ()
if self.data:
data = (self.data.address,)
charts.addNewByName(self.name, ps, data, True, True)
self.obj = charts.getByName(self.name)
chart = self.obj.getEmbeddedObject()
chart.setDiagram(chart.createInstance(self.BASE.format(self.type)))
if not self.data:
self._add_series_calc(chart)
return chart
def _from_writer(self):
obj = self.doc.create_instance('com.sun.star.text.TextEmbeddedObject')
obj.setPropertyValue('CLSID', '12DCAE26-281F-416F-a234-c3086127382e')
obj.Name = self.name
obj.setSize(Size(self.width, self.height))
self.doc.insert_content(self.cursor, obj)
self.obj = obj
chart = obj.getEmbeddedObject()
tipo = self.type
if self.type == 'Column':
tipo = 'Bar'
chart.Diagram.Vertical = True
chart.setDiagram(chart.createInstance(self.BASE.format(tipo)))
chart.DataSourceLabelsInFirstColumn = True
if isinstance(self.data, str):
self._add_series_writer(chart)
else:
chart_data = chart.getData()
labels = [r[0] for r in self.data]
data = [(r[1],) for r in self.data]
chart_data.setData(data)
chart_data.RowDescriptions = labels
if tipo == 'Pie':
chart.setDiagram(chart.createInstance(self.BASE.format('Bar')))
chart.setDiagram(chart.createInstance(self.BASE.format('Pie')))
return chart
def insert(self):
if not self.cell is None:
chart = self._from_calc()
elif not self.cursor is None:
chart = self._from_writer()
diagram = chart.Diagram
if self.type == 'Bar':
diagram.Vertical = True
if hasattr(self.title, 'String'):
chart.HasMainTitle = True
self.title(chart.Title)
if hasattr(self.subtitle, 'String'):
chart.HasSubTitle = True
self.subtitle(chart.SubTitle)
if self.legend.__dict__:
chart.HasLegend = True
self.legend(chart.Legend)
if self.xaxistitle.__dict__:
diagram.HasXAxisTitle = True
self.xaxistitle(diagram.XAxisTitle)
if self.yaxistitle.__dict__:
diagram.HasYAxisTitle = True
self.yaxistitle(diagram.YAxisTitle)
if self.dim3d:
diagram.Dim3D = True
if self.series:
data_series = chart.getFirstDiagram(
).getCoordinateSystems(
)[0].getChartTypes()[0].DataSeries
for i, serie in enumerate(data_series):
for k, v in self.series[i].items():
if hasattr(serie, k):
setattr(serie, k, v)
return self
2019-09-09 22:13:01 -05:00
class LODialog(object):
2019-10-10 00:07:45 -05:00
def __init__(self, **properties):
2019-09-09 22:13:01 -05:00
self._obj = self._create(properties)
self._init_values()
def _init_values(self):
self._model = self._obj.Model
self._init_controls()
self._events = None
2019-10-14 23:43:42 -05:00
self._color_on_focus = -1
2019-10-19 20:01:04 -05:00
self._id_extension = ''
self._images = 'images'
2019-09-09 22:13:01 -05:00
return
def _create(self, properties):
path = properties.pop('Path', '')
if path:
dp = create_instance('com.sun.star.awt.DialogProvider', True)
2019-09-09 22:13:01 -05:00
return dp.createDialog(_path_url(path))
if 'Location' in properties:
location = properties.get('Location', 'application')
library = properties.get('Library', 'Standard')
2019-09-09 22:13:01 -05:00
if location == 'user':
location = 'application'
dp = create_instance('com.sun.star.awt.DialogProvider', True)
2019-09-09 22:13:01 -05:00
path = 'vnd.sun.star.script:{}.{}?location={}'.format(
library, properties['Name'], location)
if location == 'document':
uid = get_document().uid
path = 'vnd.sun.star.tdoc:/{}/Dialogs/{}/{}.xml'.format(
uid, library, properties['Name'])
2019-09-09 22:13:01 -05:00
return dp.createDialog(path)
dlg = create_instance('com.sun.star.awt.UnoControlDialog', True)
model = create_instance('com.sun.star.awt.UnoControlDialogModel', True)
toolkit = create_instance('com.sun.star.awt.Toolkit', True)
set_properties(model, properties)
dlg.setModel(model)
dlg.setVisible(False)
dlg.createPeer(toolkit, None)
return dlg
def _get_type_control(self, name):
types = {
'stardiv.Toolkit.UnoFixedTextControl': 'label',
'stardiv.Toolkit.UnoButtonControl': 'button',
'stardiv.Toolkit.UnoEditControl': 'text',
2019-10-14 19:48:13 -05:00
'stardiv.Toolkit.UnoRoadmapControl': 'roadmap',
2019-10-14 23:43:42 -05:00
'stardiv.Toolkit.UnoFixedHyperlinkControl': 'link',
2019-10-19 20:01:04 -05:00
'stardiv.Toolkit.UnoListBoxControl': 'listbox',
}
return types[name]
2019-09-09 22:13:01 -05:00
def _init_controls(self):
for control in self.obj.getControls():
tipo = self._get_type_control(control.ImplementationName)
name = control.Model.Name
control = get_custom_class(tipo, control)
setattr(self, name, control)
2019-09-09 22:13:01 -05:00
return
@property
def obj(self):
return self._obj
@property
def model(self):
return self._model
2019-10-19 20:01:04 -05:00
@property
def id_extension(self):
return self._id_extension
@id_extension.setter
def id_extension(self, value):
global ID_EXTENSION
ID_EXTENSION = value
self._id_extension = value
@property
def images(self):
return self._images
@images.setter
def images(self, value):
self._images = value
2019-10-14 19:48:13 -05:00
@property
def height(self):
return self.model.Height
@height.setter
def height(self, value):
self.model.Height = value
2019-10-19 20:01:04 -05:00
@property
def width(self):
return self.model.Width
@width.setter
def width(self, value):
self.model.Width = value
2019-10-14 23:43:42 -05:00
@property
def color_on_focus(self):
return self._color_on_focus
@color_on_focus.setter
def color_on_focus(self, value):
global COLOR_ON_FOCUS
COLOR_ON_FOCUS = get_color(value)
self._color_on_focus = COLOR_ON_FOCUS
2019-10-14 19:48:13 -05:00
@property
def step(self):
return self.model.Step
@step.setter
def step(self, value):
self.model.Step = value
2019-09-09 22:13:01 -05:00
@property
def events(self):
return self._events
@events.setter
def events(self, controllers):
self._events = controllers
self._connect_listeners()
def _connect_listeners(self):
for control in self.obj.getControls():
add_listeners(self._events, control, control.Model.Name)
2019-09-09 22:13:01 -05:00
return
def open(self):
return self.obj.execute()
def close(self, value=0):
return self.obj.endDialog(value)
def _get_control_model(self, control):
services = {
'button': 'com.sun.star.awt.UnoControlButtonModel',
2019-10-14 19:48:13 -05:00
'grid': 'com.sun.star.awt.grid.UnoControlGridModel',
2019-09-09 22:13:01 -05:00
'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel',
2019-10-14 19:48:13 -05:00
'image': 'com.sun.star.awt.UnoControlImageControlModel',
'label': 'com.sun.star.awt.UnoControlFixedTextModel',
'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel',
'listbox': 'com.sun.star.awt.UnoControlListBoxModel',
2019-09-09 22:13:01 -05:00
'radio': 'com.sun.star.awt.UnoControlRadioButtonModel',
2019-10-14 19:48:13 -05:00
'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel',
'text': 'com.sun.star.awt.UnoControlEditModel',
2019-09-09 22:13:01 -05:00
'tree': 'com.sun.star.awt.tree.TreeControlModel',
}
return services[control]
2019-09-27 12:17:21 -05:00
def _set_column_model(self, columns):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1grid_1_1XGridColumn.html
column_model = create_instance('com.sun.star.awt.grid.DefaultGridColumnModel', True)
for column in columns:
grid_column = create_instance('com.sun.star.awt.grid.GridColumn', True)
for k, v in column.items():
setattr(grid_column, k, v)
column_model.addColumn(grid_column)
return column_model
2019-10-19 20:01:04 -05:00
def _set_image_url(self, image):
if exists_path(image):
return _path_url(image)
if not self.id_extension:
return ''
path = get_path_extension(self.id_extension)
path = join(path, self.images, image)
return _path_url(path)
2019-09-27 12:17:21 -05:00
2019-10-14 19:48:13 -05:00
def _special_properties(self, tipo, properties):
2019-09-27 12:17:21 -05:00
columns = properties.pop('Columns', ())
if tipo == 'grid':
properties['ColumnModel'] = self._set_column_model(columns)
2019-10-14 19:48:13 -05:00
elif tipo == 'button' and 'ImageURL' in properties:
2019-09-27 12:17:21 -05:00
properties['ImageURL'] = self._set_image_url(properties['ImageURL'])
2019-10-19 20:01:04 -05:00
elif tipo == 'roadmap':
if not 'Height' in properties:
properties['Height'] = self.height
if 'Title' in properties:
properties['Text'] = properties.pop('Title')
2019-10-14 19:48:13 -05:00
return properties
2019-09-27 12:17:21 -05:00
2019-10-14 19:48:13 -05:00
def add_control(self, properties):
tipo = properties.pop('Type').lower()
properties = self._special_properties(tipo, properties)
2019-09-09 22:13:01 -05:00
model = self.model.createInstance(self._get_control_model(tipo))
set_properties(model, properties)
name = properties['Name']
self.model.insertByName(name, model)
2019-10-10 00:07:45 -05:00
control = self.obj.getControl(name)
add_listeners(self.events, control, name)
control = get_custom_class(tipo, control)
2019-09-09 22:13:01 -05:00
setattr(self, name, control)
return
2019-10-19 20:01:04 -05:00
def center(self, control, x=0, y=0):
w = self.width
h = self.height
if isinstance(control, tuple):
wt = SEPARATION * -1
for c in control:
wt += c.width + SEPARATION
x = w / 2 - wt / 2
for c in control:
c.x = x
x = c.x + c.width + SEPARATION
return
if x < 0:
x = w + x - control.width
elif x == 0:
x = w / 2 - control.width / 2
if y < 0:
y = h + y - control.height
elif y == 0:
y = h / 2 - control.height / 2
control.x = x
control.y = y
return
2019-09-08 00:25:30 -05:00
2019-10-10 00:07:45 -05:00
class LOWindow(object):
def __init__(self, **kwargs):
self._events = None
self._menu = None
self._container = None
self._obj = self._create(kwargs)
def _create(self, properties):
ps = (
properties.get('X', 0),
properties.get('Y', 0),
properties.get('Width', 500),
properties.get('Height', 500),
)
self._title = properties.get('Title', TITLE)
self._create_frame(ps)
self._create_container(ps)
# ~ self._create_splitter(ps)
return
def _create_frame(self, ps):
service = 'com.sun.star.frame.TaskCreator'
tc = create_instance(service, True)
self._frame = tc.createInstanceWithArguments((
NamedValue('FrameName', 'EasyMacroWin'),
NamedValue('PosSize', Rectangle(*ps)),
))
self._window = self._frame.getContainerWindow()
self._toolkit = self._window.getToolkit()
desktop = get_desktop()
self._frame.setCreator(desktop)
desktop.getFrames().append(self._frame)
self._frame.Title = self._title
return
def _create_container(self, ps):
# ~ toolkit = self._window.getToolkit()
service = 'com.sun.star.awt.UnoControlContainer'
self._container = create_instance(service, True)
service = 'com.sun.star.awt.UnoControlContainerModel'
model = create_instance(service, True)
model.BackgroundColor = get_color(225, 225, 225)
self._container.setModel(model)
self._container.createPeer(self._toolkit, self._window)
self._container.setPosSize(*ps, POSSIZE)
self._frame.setComponent(self._container, None)
return
def _get_base_control(self, tipo):
services = {
'label': 'com.sun.star.awt.UnoControlFixedText',
'button': 'com.sun.star.awt.UnoControlButton',
'text': 'com.sun.star.awt.UnoControlEdit',
'listbox': 'com.sun.star.awt.UnoControlListBox',
'link': 'com.sun.star.awt.UnoControlFixedHyperlink',
'roadmap': 'com.sun.star.awt.UnoControlRoadmap',
'image': 'com.sun.star.awt.UnoControlImageControl',
'groupbox': 'com.sun.star.awt.UnoControlGroupBox',
'radio': 'com.sun.star.awt.UnoControlRadioButton',
'tree': 'com.sun.star.awt.tree.TreeControl',
'grid': 'com.sun.star.awt.grid.UnoControlGrid',
}
return services[tipo]
def add_control(self, properties):
tipo = properties.pop('Type').lower()
base = self._get_base_control(tipo)
obj = create_instance(base, True)
model = create_instance('{}Model'.format(base), True)
set_properties(model, properties)
obj.setModel(model)
x = properties.get('X', 5)
y = properties.get('Y', 5)
w = properties.get('Width', 200)
h = properties.get('Height', 25)
obj.setPosSize(x, y, w, h, POSSIZE)
name = properties['Name']
self._container.addControl(name, obj)
add_listeners(self.events, obj, name)
control = get_custom_class(tipo, obj)
setattr(self, name, control)
return
def _create_popupmenu(self, menus):
menu = create_instance('com.sun.star.awt.PopupMenu', True)
for i, m in enumerate(menus):
label = m['label']
cmd = m.get('event', '')
if not cmd:
cmd = label.lower().replace(' ', '_')
if label == '-':
menu.insertSeparator(i)
else:
menu.insertItem(i, label, m.get('style', 0), i)
menu.setCommand(i, cmd)
# ~ menu.setItemImage(i, path?, True)
menu.addMenuListener(EventsMenu(self.events))
return menu
def _create_menu(self, menus):
#~ https://api.libreoffice.org/docs/idl/ref/interfacecom_1_1sun_1_1star_1_1awt_1_1XMenu.html
#~ nItemId specifies the ID of the menu item to be inserted.
#~ aText specifies the label of the menu item.
#~ nItemStyle 0 = Standard, CHECKABLE = 1, RADIOCHECK = 2, AUTOCHECK = 4
#~ nItemPos specifies the position where the menu item will be inserted.
self._menu = create_instance('com.sun.star.awt.MenuBar', True)
for i, m in enumerate(menus):
self._menu.insertItem(i, m['label'], m.get('style', 0), i)
cmd = m['label'].lower().replace(' ', '_')
self._menu.setCommand(i, cmd)
submenu = self._create_popupmenu(m['submenu'])
self._menu.setPopupMenu(i, submenu)
self._window.setMenuBar(self._menu)
return
def add_menu(self, menus):
self._create_menu(menus)
return
def _add_listeners(self, control=None):
if self.events is None:
return
controller = EventsWindow(self)
self._window.addTopWindowListener(controller)
self._window.addWindowListener(controller)
2019-10-19 20:01:04 -05:00
self._container.addKeyListener(EventsKeyWindow(self))
2019-10-10 00:07:45 -05:00
return
@property
def name(self):
return self._title.lower().replace(' ', '_')
@property
def events(self):
return self._events
@events.setter
def events(self, value):
self._events = value
self._add_listeners()
@property
def width(self):
return self._container.Size.Width
@property
def height(self):
return self._container.Size.Height
def open(self):
self._window.setVisible(True)
return
def close(self):
self._window.setMenuBar(None)
self._window.dispose()
self._frame.close(True)
return
2019-09-08 00:25:30 -05:00
# ~ Python >= 3.7
# ~ def __getattr__(name):
2019-09-09 22:13:01 -05:00
def _get_class_doc(obj):
classes = {
'calc': LOCalc,
'writer': LOWriter,
'base': LOBase,
'impress': LOImpress,
'draw': LODraw,
'math': LOMath,
'basic': LOBasicIde,
}
type_doc = get_type_doc(obj)
return classes[type_doc](obj)
2019-09-27 12:17:21 -05:00
# ~ Export ok
def get_document(title=''):
2019-09-08 00:25:30 -05:00
doc = None
2019-09-09 22:13:01 -05:00
desktop = get_desktop()
2019-09-27 12:17:21 -05:00
if not title:
2019-09-09 22:13:01 -05:00
doc = _get_class_doc(desktop.getCurrentComponent())
2019-09-27 12:17:21 -05:00
return doc
for d in desktop.getComponents():
if d.Title == title:
doc = d
break
if doc is None:
return
return _get_class_doc(doc)
def get_documents(custom=True):
docs = []
desktop = get_desktop()
for doc in desktop.getComponents():
if custom:
docs.append(_get_class_doc(doc))
else:
docs.append(doc)
return docs
2019-09-08 00:25:30 -05:00
def get_selection():
2019-09-09 22:13:01 -05:00
return get_document().selection
2019-09-08 00:25:30 -05:00
def get_cell(*args):
2019-09-09 22:13:01 -05:00
if args:
index = args
if len(index) == 1:
index = args[0]
cell = get_document().get_cell(index)
else:
cell = get_selection().first
return cell
def active_cell():
return get_cell()
def create_dialog(properties):
2019-10-10 00:07:45 -05:00
return LODialog(**properties)
2019-09-09 22:13:01 -05:00
2019-10-12 20:16:00 -05:00
def create_window(kwargs):
return LOWindow(**kwargs)
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-18 21:41:47 -05:00
def get_config_path(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 _path_system(getattr(path, name))
2019-10-19 20:01:04 -05:00
def get_path_python():
path = get_config_path('Module')
return join(path, PYTHON)
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-18 21:41:47 -05:00
def get_file(init_dir='', multiple=False, filters=()):
"""
init_folder: folder default open
multiple: True for multiple selected
filters: Example
(
('XML', '*.xml'),
('TXT', '*.txt'),
)
"""
if not init_dir:
init_dir = get_config_path()
2019-09-27 12:17:21 -05:00
init_dir = _path_url(init_dir)
2019-09-10 22:20:31 -05:00
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file'))
2019-09-18 21:41:47 -05:00
file_picker.setDisplayDirectory(init_dir)
2019-09-10 22:20:31 -05:00
file_picker.setMultiSelectionMode(multiple)
2019-09-27 12:17:21 -05:00
path = ''
2019-09-10 22:20:31 -05:00
if filters:
file_picker.setCurrentFilter(filters[0][0])
for f in filters:
file_picker.appendFilter(f[0], f[1])
if file_picker.execute():
2019-09-27 12:17:21 -05:00
path = _path_system(file_picker.getSelectedFiles()[0])
2019-09-10 22:20:31 -05:00
if multiple:
2019-09-27 12:17:21 -05:00
path = [_path_system(f) for f in file_picker.getSelectedFiles()]
2019-09-10 22:20:31 -05:00
2019-09-27 12:17:21 -05:00
return path
# ~ Export ok
def get_path(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 = get_config_path()
init_dir = _path_url(init_dir)
file_picker = create_instance('com.sun.star.ui.dialogs.FilePicker')
file_picker.setTitle(_('Select file'))
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-09-10 22:20:31 -05:00
2019-09-27 12:17:21 -05:00
path = ''
if file_picker.execute():
path = _path_system(file_picker.getSelectedFiles()[0])
return path
# ~ Export ok
def get_dir(init_dir=''):
folder_picker = create_instance('com.sun.star.ui.dialogs.FolderPicker')
if not init_dir:
init_dir = get_config_path()
init_dir = _path_url(init_dir)
folder_picker.setDisplayDirectory(init_dir)
2019-09-10 22:20:31 -05:00
2019-09-27 12:17:21 -05:00
path = ''
if folder_picker.execute():
path = _path_system(folder_picker.getDirectory())
return path
# ~ Export ok
2019-09-10 22:20:31 -05:00
def get_info_path(path):
path, filename = os.path.split(path)
name, extension = os.path.splitext(filename)
return (path, filename, name, extension)
2019-09-27 12:17:21 -05:00
# ~ Export ok
def read_file(path, mode='r', array=False):
data = ''
with open(path, mode) as f:
if array:
data = tuple(f.read().splitlines())
else:
data = f.read()
return data
# ~ Export ok
def save_file(path, mode='w', data=None):
with open(path, mode) as f:
f.write(data)
return
# ~ Export ok
def to_json(path, data):
with open(path, 'w') as f:
f.write(json.dumps(data, indent=4, sort_keys=True))
return
# ~ Export ok
def from_json(path):
with open(path) as f:
data = json.loads(f.read())
return data
2019-10-05 11:14:19 -05:00
# ~ Export ok
def json_dumps(data):
return json.dumps(data, indent=4, sort_keys=True)
# ~ Export ok
def json_loads(data):
return json.loads(data)
2019-09-27 12:17:21 -05:00
def get_path_extension(id):
pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider')
path = _path_system(pip.getPackageLocation(id))
return path
# ~ Export ok
def inputbox(message, default='', title=TITLE, echochar=''):
2019-09-10 22:20:31 -05:00
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,
}
2019-10-10 00:07:45 -05:00
dlg = LODialog(**args)
2019-09-10 22:20:31 -05:00
dlg.events = ControllersInput(dlg)
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,
}
2019-09-27 12:17:21 -05:00
if echochar:
args['EchoChar'] = ord(echochar[0])
2019-09-10 22:20:31 -05:00
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 ''
2019-09-27 12:17:21 -05:00
# ~ Export ok
def new_doc(type_doc=CALC, **kwargs):
2019-09-11 23:53:29 -05:00
path = 'private:factory/s{}'.format(type_doc)
2019-09-27 12:17:21 -05:00
opt = dict_to_property(kwargs)
doc = get_desktop().loadComponentFromURL(path, '_default', 0, opt)
2019-09-11 23:53:29 -05:00
return _get_class_doc(doc)
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-10-05 11:14:19 -05:00
def new_db(path, name=''):
p, fn, n, e = get_info_path(path)
if not name:
name = n
return LOBase(name, path)
# ~ Todo
def exists_db(name):
2019-09-27 12:17:21 -05:00
dbc = create_instance('com.sun.star.sdb.DatabaseContext')
2019-10-05 11:14:19 -05:00
return dbc.hasRegisteredDatabase(name)
# ~ Todo
def register_db(name, path):
dbc = create_instance('com.sun.star.sdb.DatabaseContext')
dbc.registerDatabaseLocation(name, _path_url(path))
return
# ~ Todo
def get_db(name):
return LOBase(name)
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-11 23:53:29 -05:00
def open_doc(path, **kwargs):
2019-09-10 22:20:31 -05:00
""" 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
2019-09-14 17:40:07 -05:00
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
2019-09-10 22:20:31 -05:00
"""
path = _path_url(path)
2019-09-18 21:41:47 -05:00
opt = dict_to_property(kwargs)
2019-10-19 20:01:04 -05:00
doc = get_desktop().loadComponentFromURL(path, '_default', 0, opt)
2019-09-10 22:20:31 -05:00
if doc is None:
return
return _get_class_doc(doc)
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-10 22:20:31 -05:00
def open_file(path):
2019-09-11 23:53:29 -05:00
if IS_WIN:
2019-09-10 22:20:31 -05:00
os.startfile(path)
else:
2019-09-11 23:53:29 -05:00
subprocess.Popen(['xdg-open', path])
2019-09-10 22:20:31 -05:00
return
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-10 22:20:31 -05:00
def join(*paths):
return os.path.join(*paths)
2019-09-11 23:53:29 -05:00
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-11 23:53:29 -05:00
def is_dir(path):
return Path(path).is_dir()
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-11 23:53:29 -05:00
def is_file(path):
return Path(path).is_file()
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-11 23:53:29 -05:00
def get_file_size(path):
return Path(path).stat().st_size
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-11 23:53:29 -05:00
def is_created(path):
return is_file(path) and bool(get_file_size(path))
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-18 21:41:47 -05:00
def replace_ext(path, extension):
2019-09-11 23:53:29 -05:00
path, _, name, _ = get_info_path(path)
2019-09-18 21:41:47 -05:00
return '{}/{}.{}'.format(path, name, extension)
2019-09-11 23:53:29 -05:00
2019-09-27 12:17:21 -05:00
# ~ Export ok
def zip_content(path):
2019-09-11 23:53:29 -05:00
with zipfile.ZipFile(path) as z:
names = z.namelist()
return names
2019-10-19 20:01:04 -05:00
def popen(command, stdin=None):
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)
def url_open(url, options={}, json=False):
data = ''
req = Request(url)
try:
response = urlopen(req)
except HTTPError as e:
error(e)
except URLError as e:
error(e.reason)
else:
if json:
data = json_loads(response.read())
else:
data = response.read()
return data
2019-09-11 23:53:29 -05:00
def run(command, wait=False):
try:
if wait:
2019-09-14 17:40:07 -05:00
result = subprocess.check_output(command, shell=True)
2019-09-11 23:53:29 -05:00
else:
p = subprocess.Popen(shlex.split(command), stdin=None,
stdout=None, stderr=None, close_fds=True)
2019-09-14 17:40:07 -05:00
result, er = p.communicate()
2019-09-11 23:53:29 -05:00
except subprocess.CalledProcessError as e:
msg = ("run [ERROR]: output = %s, error code = %s\n"
% (e.output, e.returncode))
error(msg)
return False
if result is None:
return True
return result.decode()
def _zippwd(source, target, pwd):
if IS_WIN:
return False
if not exists_app('zip'):
return False
cmd = 'zip'
opt = '-j '
args = "{} --password {} ".format(cmd, pwd)
if isinstance(source, (tuple, list)):
if not target:
return False
args += opt + target + ' ' + ' '.join(source)
else:
if is_file(source) and not target:
target = replace_ext(source, 'zip')
elif is_dir(source) and not target:
target = join(PurePath(source).parent,
'{}.zip'.format(PurePath(source).name))
opt = '-r '
args += opt + target + ' ' + source
result = run(args, True)
if not result:
return False
return is_created(target)
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-11 23:53:29 -05:00
def zip(source, target='', mode='w', pwd=''):
if pwd:
return _zippwd(source, target, pwd)
if isinstance(source, (tuple, list)):
if not target:
return False
with zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED) as z:
for path in source:
_, name, _, _ = get_info_path(path)
z.write(path, name)
return is_created(target)
if is_file(source):
if not target:
target = replace_ext(source, 'zip')
z = zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED)
_, name, _, _ = get_info_path(source)
z.write(source, name)
z.close()
return is_created(target)
if not target:
target = join(
PurePath(source).parent,
'{}.zip'.format(PurePath(source).name))
z = zipfile.ZipFile(target, mode, compression=zipfile.ZIP_DEFLATED)
root_len = len(os.path.abspath(source))
for root, dirs, files in os.walk(source):
relative = os.path.abspath(root)[root_len:]
for f in files:
fullpath = join(root, f)
file_name = join(relative, f)
z.write(fullpath, file_name)
z.close()
return is_created(target)
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-11 23:53:29 -05:00
def unzip(source, path='', members=None, pwd=None):
if not path:
path, _, _, _ = get_info_path(source)
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-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-11 23:53:29 -05:00
def merge_zip(target, zips):
try:
with zipfile.ZipFile(target, 'w', compression=zipfile.ZIP_DEFLATED) as t:
for path in zips:
with zipfile.ZipFile(path, compression=zipfile.ZIP_DEFLATED) as s:
for name in s.namelist():
t.writestr(name, s.open(name).read())
except Exception as e:
error(e)
return False
return True
2019-09-14 17:40:07 -05:00
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-14 17:40:07 -05:00
def kill(path):
p = Path(path)
2019-09-27 12:17:21 -05:00
try:
if p.is_file():
2019-09-14 17:40:07 -05:00
p.unlink()
2019-09-27 12:17:21 -05:00
elif p.is_dir():
shutil.rmtree(path)
except OSError as e:
log.error(e)
2019-09-14 17:40:07 -05:00
return
def get_size_screen():
if IS_WIN:
user32 = ctypes.windll.user32
res = '{}x{}'.format(user32.GetSystemMetrics(0), user32.GetSystemMetrics(1))
else:
args = 'xrandr | grep "*" | cut -d " " -f4'
res = run(args, True)
return res.strip()
def get_clipboard():
df = None
text = ''
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
transferable = sc.getContents()
data = transferable.getTransferDataFlavors()
for df in data:
if df.MimeType == CLIPBOARD_FORMAT_TEXT:
break
if df:
text = transferable.getTransferData(df)
return text
class TextTransferable(unohelper.Base, XTransferable):
"""Keep clipboard data and provide them."""
def __init__(self, text):
df = DataFlavor()
df.MimeType = CLIPBOARD_FORMAT_TEXT
df.HumanPresentableName = "encoded text utf-16"
self.flavors = [df]
self.data = [text]
def getTransferData(self, flavor):
if not flavor:
return
for i, f in enumerate(self.flavors):
if flavor.MimeType == f.MimeType:
return self.data[i]
return
def getTransferDataFlavors(self):
return tuple(self.flavors)
def isDataFlavorSupported(self, flavor):
if not flavor:
return False
mtype = flavor.MimeType
for f in self.flavors:
if mtype == f.MimeType:
return True
return False
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-18 21:41:47 -05:00
def set_clipboard(value):
ts = TextTransferable(value)
2019-09-14 17:40:07 -05:00
sc = create_instance('com.sun.star.datatransfer.clipboard.SystemClipboard')
sc.setContents(ts, None)
return
2019-10-05 11:14:19 -05:00
# ~ Export ok
2019-09-27 12:17:21 -05:00
def copy():
call_dispatch('.uno:Copy')
2019-09-14 17:40:07 -05:00
return
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-14 17:40:07 -05:00
def get_epoch():
2019-09-27 12:17:21 -05:00
n = now()
return int(time.mktime(n.timetuple()))
2019-09-18 21:41:47 -05:00
2019-09-27 12:17:21 -05:00
# ~ Export ok
2019-09-18 21:41:47 -05:00
def file_copy(source, target='', name=''):
p, f, n, e = get_info_path(source)
if target:
p = target
if name:
2019-09-27 12:17:21 -05:00
e = ''
2019-09-18 21:41:47 -05:00
n = name
path_new = join(p, '{}{}'.format(n, e))
shutil.copy(source, path_new)
2019-09-27 12:17:21 -05:00
return path_new
2019-09-18 21:41:47 -05:00
2019-10-05 11:14:19 -05:00
def get_path_content(path, filters=''):
2019-09-27 12:17:21 -05:00
paths = []
2019-10-05 11:14:19 -05:00
if filters in ('*', '*.*'):
filters = ''
2019-09-18 21:41:47 -05:00
for folder, _, files in os.walk(path):
2019-10-05 11:14:19 -05:00
if filters:
pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE)
paths += [join(folder, f) for f in files if pattern.search(f)]
else:
paths += files
2019-09-27 12:17:21 -05:00
return paths
2019-09-18 21:41:47 -05:00
def _get_menu(type_doc, name_menu):
instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
service = TYPE_DOC[type_doc]
manager = create_instance(instance, True)
ui = manager.getUIConfigurationManager(service)
menus = ui.getSettings(NODE_MENUBAR, True)
command = MENUS_APP[type_doc][name_menu]
for menu in menus:
data = property_to_dict(menu)
if data.get('CommandURL', '') == command:
idc = data.get('ItemDescriptorContainer', None)
return ui, menus, idc
return None, None, None
def _get_index_menu(menu, command):
for i, m in enumerate(menu):
data = property_to_dict(m)
cmd = data.get('CommandURL', '')
if cmd == command:
return i
# ~ submenu = data.get('ItemDescriptorContainer', None)
# ~ if not submenu is None:
# ~ get_index_menu(submenu, command, count + 1)
return 0
def _store_menu(ui, menus, menu, index, data=(), remove=False):
if remove:
uno.invoke(menu, 'removeByIndex', (index,))
else:
properties = dict_to_property(data, True)
uno.invoke(menu, 'insertByIndex', (index + 1, properties))
ui.replaceSettings(NODE_MENUBAR, menus)
ui.store()
return
def insert_menu(type_doc, name_menu, **kwargs):
ui, menus, menu = _get_menu(type_doc, name_menu.lower())
if menu is None:
return 0
label = kwargs.get('Label', '-')
separator = False
if label == '-':
separator = True
2019-09-27 12:17:21 -05:00
command = kwargs.get('CommandURL', '')
2019-09-18 21:41:47 -05:00
index = kwargs.get('Index', 0)
if not index:
index = _get_index_menu(menu, kwargs['After'])
if separator:
data = {'Type': 1}
_store_menu(ui, menus, menu, index, data)
return index + 1
index_menu = _get_index_menu(menu, command)
if index_menu:
msg = 'Exists: %s' % command
debug(msg)
return 0
sub_menu = kwargs.get('Submenu', ())
idc = None
if sub_menu:
idc = ui.createSettings()
data = {
'CommandURL': command,
'Label': label,
'Style': 0,
'Type': 0,
'ItemDescriptorContainer': idc
}
_store_menu(ui, menus, menu, index, data)
if sub_menu:
_add_sub_menus(ui, menus, idc, sub_menu)
return True
def _add_sub_menus(ui, menus, menu, sub_menu):
for i, sm in enumerate(sub_menu):
submenu = sm.pop('Submenu', ())
sm['Type'] = 0
if submenu:
idc = ui.createSettings()
sm['ItemDescriptorContainer'] = idc
2019-09-27 12:17:21 -05:00
if sm['Label'] == '-':
sm = {'Type': 1}
2019-09-18 21:41:47 -05:00
_store_menu(ui, menus, menu, i - 1, sm)
if submenu:
_add_sub_menus(ui, menus, idc, submenu)
return
def remove_menu(type_doc, name_menu, command):
ui, menus, menu = _get_menu(type_doc, name_menu.lower())
if menu is None:
return False
index = _get_index_menu(menu, command)
if not index:
debug('Not exists: %s' % command)
return False
_store_menu(ui, menus, menu, index, remove=True)
return True
2019-09-27 12:17:21 -05:00
def _get_app_submenus(menus, count=0):
for i, menu in enumerate(menus):
data = property_to_dict(menu)
cmd = data.get('CommandURL', '')
msg = ' ' * count + 'ā”œā”€' + cmd
debug(msg)
submenu = data.get('ItemDescriptorContainer', None)
if not submenu is None:
_get_app_submenus(submenu, count + 1)
return
def get_app_menus(name_app, index=-1):
instance = 'com.sun.star.ui.ModuleUIConfigurationManagerSupplier'
service = TYPE_DOC[name_app]
manager = create_instance(instance, True)
ui = manager.getUIConfigurationManager(service)
menus = ui.getSettings(NODE_MENUBAR, True)
if index == -1:
for menu in menus:
data = property_to_dict(menu)
debug(data.get('CommandURL', ''))
else:
menus = property_to_dict(menus[index])['ItemDescriptorContainer']
_get_app_submenus(menus)
return menus
# ~ Export ok
def start():
global _start
_start = now()
log.info(_start)
return
# ~ Export ok
def end():
global _start
e = now()
return str(e - _start).split('.')[0]
# ~ Export ok
# ~ https://en.wikipedia.org/wiki/Web_colors
2019-10-05 11:14:19 -05:00
def get_color(*value):
2019-10-14 23:43:42 -05:00
if len(value) == 1 and isinstance(value[0], int):
return value[0]
if len(value) == 1 and isinstance(value[0], tuple):
value = value[0]
2019-09-27 12:17:21 -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,
}
2019-10-05 11:14:19 -05:00
if len(value) == 3:
color = (value[0] << 16) + (value[1] << 8) + value[2]
else:
value = value[0]
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
2019-09-27 12:17:21 -05:00
2019-10-14 23:43:42 -05:00
COLOR_ON_FOCUS = get_color('LightYellow')
2019-09-27 12:17:21 -05:00
# ~ Export ok
def render(template, data):
s = Template(template)
return s.safe_substitute(**data)
def _to_date(value):
new_value = value
if isinstance(value, Time):
new_value = datetime.time(value.Hours, value.Minutes, value.Seconds)
elif isinstance(value, Date):
new_value = datetime.date(value.Year, value.Month, value.Day)
elif isinstance(value, DateTime):
new_value = datetime.datetime(
value.Year, value.Month, value.Day,
value.Hours, value.Minutes, value.Seconds)
return new_value
2019-10-05 11:14:19 -05:00
def date_to_struct(value):
# ~ print(type(value))
if isinstance(value, datetime.datetime):
d = DateTime()
d.Seconds = value.second
d.Minutes = value.minute
d.Hours = value.hour
d.Day = value.day
d.Month = value.month
d.Year = value.year
elif isinstance(value, datetime.date):
d = Date()
d.Day = value.day
d.Month = value.month
d.Year = value.year
return d
2019-09-27 12:17:21 -05:00
# ~ Export ok
def format(template, data):
"""
https://pyformat.info/
"""
if isinstance(data, (str, int, float)):
# ~ print(template.format(data))
return template.format(data)
if isinstance(data, (Time, Date, DateTime)):
return template.format(_to_date(data))
if isinstance(data, tuple) and isinstance(data[0], tuple):
data = {r[0]: _to_date(r[1]) for r in data}
return template.format(**data)
data = [_to_date(v) for v in data]
result = template.format(*data)
return result
2019-10-19 20:01:04 -05:00
def _get_url_script(macro):
macro['language'] = macro.get('language', 'Python')
macro['location'] = macro.get('location', 'user')
data = macro.copy()
if data['language'] == 'Python':
data['module'] = '.py$'
elif data['language'] == 'Basic':
data['module'] = '.{}.'.format(macro['module'])
if macro['location'] == 'user':
data['location'] = 'application'
else:
data['module'] = '.'
url = 'vnd.sun.star.script:{library}{module}{name}?language={language}&location={location}'
path = url.format(**data)
return path
2019-09-27 12:17:21 -05:00
def _call_macro(macro):
#~ https://wiki.openoffice.org/wiki/Documentation/DevGuide/Scripting/Scripting_Framework_URI_Specification
name = 'com.sun.star.script.provider.MasterScriptProviderFactory'
factory = create_instance(name, False)
2019-10-05 16:25:05 -05:00
macro['language'] = macro.get('language', 'Python')
macro['location'] = macro.get('location', 'user')
2019-09-27 12:17:21 -05:00
data = macro.copy()
2019-10-05 16:25:05 -05:00
if data['language'] == 'Python':
2019-09-27 12:17:21 -05:00
data['module'] = '.py$'
2019-10-05 16:25:05 -05:00
elif data['language'] == 'Basic':
2019-09-27 12:17:21 -05:00
data['module'] = '.{}.'.format(macro['module'])
if macro['location'] == 'user':
data['location'] = 'application'
else:
data['module'] = '.'
args = macro.get('args', ())
url = 'vnd.sun.star.script:{library}{module}{name}?language={language}&location={location}'
path = url.format(**data)
2019-10-19 20:01:04 -05:00
2019-09-27 12:17:21 -05:00
script = factory.createScriptProvider('').getScript(path)
return script.invoke(args, None, None)[0]
# ~ Export ok
def call_macro(macro):
in_thread = macro.pop('thread')
if in_thread:
t = threading.Thread(target=_call_macro, args=(macro,))
t.start()
return
return _call_macro(macro)
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']))
return
# ~ Export ok
def timer(name, seconds, macro):
global _stop_thread
_stop_thread[name] = threading.Event()
thread = TimerThread(_stop_thread[name], seconds, macro)
thread.start()
return
# ~ Export ok
def stop_timer(name):
global _stop_thread
_stop_thread[name].set()
del _stop_thread[name]
return
def _get_key(password):
digest = hashlib.sha256(password.encode()).digest()
key = base64.urlsafe_b64encode(digest)
return key
# ~ Export ok
def encrypt(data, password):
f = Fernet(_get_key(password))
token = f.encrypt(data).decode()
return token
# ~ Export ok
def decrypt(token, password):
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
2019-10-10 00:07:45 -05:00
def __exit__(self, exc_type, exc_value, traceback):
2019-09-27 12:17:21 -05:00
self.close()
@property
def is_connect(self):
return self._is_connect
@property
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()
else:
self._server = smtplib.SMTP(name, port, timeout=TIMEOUT)
self._server.login(self._sender, config['pass'])
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
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, _, _ = get_info_path(path)
part = MIMEBase('application', 'octet-stream')
part.set_payload(read_file(path, 'rb'))
encoders.encode_base64(part)
part.add_header('Content-Disposition', file_name.format(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()
return
def close(self):
try:
self._server.quit()
msg = 'Close connection...'
debug(msg)
except:
pass
return
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
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
def server_smtp_test(config):
with SmtpServer(config) as server:
if server.error:
error(server.error)
return server.error
2019-10-12 20:16:00 -05:00
def import_csv(path, **kwargs):
"""
See https://docs.python.org/3.5/library/csv.html#csv.reader
"""
with open(path) as f:
rows = tuple(csv.reader(f, **kwargs))
return rows
2019-10-19 20:01:04 -05:00
2019-10-12 20:16:00 -05:00
def export_csv(path, data, **kwargs):
with open(path, 'w') as f:
writer = csv.writer(f, **kwargs)
writer.writerows(data)
return
2019-10-10 00:07:45 -05:00
2019-10-05 11:14:19 -05:00
2019-10-19 20:01:04 -05:00
def install_locales(path, domain='base', dir_locales=DIR['locales']):
p, *_ = get_info_path(path)
path_locales = join(p, 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-05 11:14:19 -05:00
class LIBOServer(object):
HOST = 'localhost'
PORT = '8100'
ARG = 'socket,host={},port={};urp;StarOffice.ComponentContext'.format(HOST, PORT)
CMD = ['soffice',
'-env:SingleAppInstance=false',
'-env:UserInstallation=file:///tmp/LIBO_Process8100',
'--headless', '--norestore', '--invisible',
'--accept={}'.format(ARG)]
def __init__(self):
self._server = None
self._ctx = None
self._sm = None
self._start_server()
self._init_values()
def _init_values(self):
global CTX
global SM
if not self.is_running:
return
ctx = uno.getComponentContext()
service = 'com.sun.star.bridge.UnoUrlResolver'
resolver = ctx.ServiceManager.createInstanceWithContext(service, ctx)
self._ctx = resolver.resolve('uno:{}'.format(self.ARG))
self._sm = self._ctx.getServiceManager()
CTX = self._ctx
SM = self._sm
return
@property
def is_running(self):
try:
s = socket.create_connection((self.HOST, self.PORT), 5.0)
s.close()
debug('LibreOffice is running...')
return True
except ConnectionRefusedError:
return False
def _start_server(self):
if self.is_running:
return
for i in range(3):
self._server = subprocess.Popen(self.CMD,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
time.sleep(3)
if self.is_running:
break
return
def stop(self):
if self._server is None:
print('Search pgrep soffice')
else:
self._server.terminate()
debug('LibreOffice is stop...')
return
def create_instance(self, name, with_context=True):
if with_context:
instance = self._sm.createInstanceWithContext(name, self._ctx)
else:
instance = self._sm.createInstance(name)
return instance
2019-10-14 23:43:42 -05:00
# ~ controls = {
# ~ 'CheckBox': 'com.sun.star.awt.UnoControlCheckBoxModel',
# ~ 'ComboBox': 'com.sun.star.awt.UnoControlComboBoxModel',
# ~ 'CurrencyField': 'com.sun.star.awt.UnoControlCurrencyFieldModel',
# ~ 'DateField': 'com.sun.star.awt.UnoControlDateFieldModel',
# ~ 'FileControl': 'com.sun.star.awt.UnoControlFileControlModel',
# ~ 'FormattedField': 'com.sun.star.awt.UnoControlFormattedFieldModel',
# ~ 'GroupBox': 'com.sun.star.awt.UnoControlGroupBoxModel',
# ~ 'ImageControl': 'com.sun.star.awt.UnoControlImageControlModel',
# ~ 'NumericField': 'com.sun.star.awt.UnoControlNumericFieldModel',
# ~ 'PatternField': 'com.sun.star.awt.UnoControlPatternFieldModel',
# ~ 'ProgressBar': 'com.sun.star.awt.UnoControlProgressBarModel',
# ~ 'ScrollBar': 'com.sun.star.awt.UnoControlScrollBarModel',
# ~ 'SimpleAnimation': 'com.sun.star.awt.UnoControlSimpleAnimationModel',
# ~ 'SpinButton': 'com.sun.star.awt.UnoControlSpinButtonModel',
# ~ 'Throbber': 'com.sun.star.awt.UnoControlThrobberModel',
# ~ 'TimeField': 'com.sun.star.awt.UnoControlTimeFieldModel',
# ~ }