diff --git a/CHANGELOG b/CHANGELOG index 8c96867..85092b8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,7 @@ +v 0.5.0 [22-nov-2019] + - Update easymacro.py + + v 0.4.0 [23-oct-2019] - Fix error in main diff --git a/README.md b/README.md index 16d67fa..fbfcd2a 100644 --- a/README.md +++ b/README.md @@ -9,11 +9,12 @@ https://gitlab.com/mauriciobaeza/zaz ### Software libre, no gratis + This extension have a cost of maintenance of 1 euro every year. -BCH: `1RPLWHJW34p7pMQV1ft4x7eWhAYw69Dsb` +BCH: `qztd3l00xle5tffdqvh2snvadkuau2ml0uqm4n875d` -BTC: `3Fe4JuADrAK8Qs7GDAxbSXR8E54avwZJLW` +BTC: `3FhiXcXmAesmQzrNEngjHFnvaJRhU1AGWV` PayPal :( donate ATT elmau DOT net diff --git a/VERSION b/VERSION index 26b5dec..8ea2ddf 100644 --- a/VERSION +++ b/VERSION @@ -1,2 +1,2 @@ -0.4.0 +0.5.0 diff --git a/conf.py b/conf.py index 012a322..9f2b603 100644 --- a/conf.py +++ b/conf.py @@ -26,7 +26,7 @@ import logging TYPE_EXTENSION = 1 # ~ https://semver.org/ -VERSION = '0.4.0' +VERSION = '0.5.0' # ~ Your great extension name, not used spaces NAME = 'ZAZFavorites' @@ -206,6 +206,8 @@ PATHS = { 'soffice': ('soffice', PROGRAM, FILE_TEST), 'install': ('unopkg', 'add', '-v', '-f', '-s'), 'profile': '/home/mau/.config/libreoffice/4/user', + 'gettext': PATH_PYGETTEXT, + 'msgmerge': PATH_MSGMERGE, } diff --git a/easymacro.py b/easymacro.py index 547f936..8259aca 100644 --- a/easymacro.py +++ b/easymacro.py @@ -34,6 +34,7 @@ import shlex import shutil import socket import subprocess +import ssl import sys import tempfile import threading @@ -41,13 +42,9 @@ import time import traceback import zipfile -# ~ from collections import OrderedDict -# ~ from collections.abc import MutableMapping from functools import wraps -from operator import itemgetter from pathlib import Path, PurePath from pprint import pprint -from enum import IntEnum from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError from string import Template @@ -91,14 +88,10 @@ from com.sun.star.awt import XMenuListener from com.sun.star.awt import XKeyListener from com.sun.star.awt import XItemListener from com.sun.star.awt import XFocusListener +from com.sun.star.awt import XTabListener +from com.sun.star.awt.grid import XGridDataListener +from com.sun.star.awt.grid import XGridSelectionListener -class FontSlant(IntEnum): - NONE = 0 - OBLIQUE = 1 - ITALIC = 2 - DONTKNOW = 3 - REVERSE_OBLIQUE = 4 - REVERSE_ITALIC = 5 try: from fernet import Fernet, InvalidToken @@ -119,7 +112,6 @@ KEY = { SEPARATION = 5 - MSG_LANG = { 'es': { 'OK': 'Aceptar', @@ -130,35 +122,36 @@ MSG_LANG = { } } - OS = platform.system() USER = getpass.getuser() PC = platform.node() DESKTOP = os.environ.get('DESKTOP_SESSION', '') INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path)) - IS_WIN = OS == 'Windows' LOG_NAME = 'ZAZ' CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16' - PYTHON = 'python' if IS_WIN: PYTHON = 'python.exe' CALC = 'calc' WRITER = 'writer' + OBJ_CELL = 'ScCellObj' OBJ_RANGE = 'ScCellRangeObj' OBJ_RANGES = 'ScCellRangesObj' OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES) +TEXT_RANGE = 'SwXTextRange' +TEXT_RANGES = 'SwXTextRanges' +TEXT_TYPE_RANGES = (TEXT_RANGE, TEXT_RANGES) + 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', - # ~ 'base': 'com.sun.star.sdb.OfficeDatabaseDocument', 'base': 'com.sun.star.sdb.DocumentDataSource', 'math': 'com.sun.star.formula.FormulaProperties', 'basic': 'com.sun.star.script.BasicIDE', @@ -197,19 +190,16 @@ MENUS_WRITER = { 'windows': '.uno:WindowList', 'help': '.uno:HelpMenu', } - MENUS_APP = { 'main': MENUS_MAIN, 'calc': MENUS_CALC, 'writer': MENUS_WRITER, } - EXT = { 'pdf': 'pdf', } - FILE_NAME_DEBUG = 'debug.odt' FILE_NAME_CONFIG = 'zaz-{}.json' LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s' @@ -279,6 +269,12 @@ def mri(obj): return +def inspect(obj): + zaz = create_instance('net.elmau.zaz.inspect') + zaz.inspect(obj) + return + + def catch_exception(f): @wraps(f) def func(*args, **kwargs): @@ -454,12 +450,14 @@ def call_dispatch(url, args=()): return -def get_temp_file(): +def get_temp_file(only_name=False): delete = True if IS_WIN: delete = False - return tempfile.NamedTemporaryFile(delete=delete) - + tmp = tempfile.NamedTemporaryFile(delete=delete) + if only_name: + tmp = tmp.name + return tmp def _path_url(path): if path.startswith('file://'): @@ -501,6 +499,11 @@ def dict_to_property(values, uno_any=False): return ps +def dict_to_named(values): + ps = tuple([NamedValue(n, v) for n, v in values.items()]) + return ps + + def property_to_dict(values): d = {i.Name: i.Value for i in values} return d @@ -934,6 +937,67 @@ class LOCellStyles(object): return +class LOImage(object): + TYPES = { + 'image/png': 'png', + 'image/jpeg': 'jpg', + } + + def __init__(self, obj): + self._obj = obj + + @property + def obj(self): + return self._obj + + @property + def address(self): + return self.obj.Anchor.AbsoluteName + + @property + def name(self): + return self.obj.Name + + @property + def mimetype(self): + return self.obj.Bitmap.MimeType + + @property + def url(self): + return _path_system(self.obj.URL) + @url.setter + def url(self, value): + self.obj.URL = _path_url(value) + + @property + def path(self): + return _path_system(self.obj.GraphicURL) + @path.setter + def path(self, value): + self.obj.GraphicURL = _path_url(value) + + @property + def visible(self): + return self.obj.Visible + @visible.setter + def visible(self, value): + self_obj.Visible = value + + def save(self, path): + if is_dir(path): + p = path + n = self.name + else: + p, fn, n, e = get_info_path(path) + ext = self.TYPES[self.mimetype] + path = join(p, '{}.{}'.format(n, ext)) + size = len(self.obj.Bitmap.DIB) + data = self.obj.GraphicStream.readBytes((), size) + data = data[-1].value + save_file(path, 'wb', data) + return path + + class LOCalc(LODocument): def __init__(self, obj): @@ -942,7 +1006,9 @@ class LOCalc(LODocument): def __getitem__(self, index): if isinstance(index, str): - index = [s.Name for s in self._sheets if s.CodeName == index][0] or index + code_name = [s.Name for s in self._sheets if s.CodeName == index] + if code_name: + index = code_name[0] return LOCalcSheet(self._sheets[index], self) def __setitem__(self, key, value): @@ -1182,7 +1248,7 @@ class LOCalcSheet(object): def _init_values(self): self._events = None self._dp = self.obj.getDrawPage() - return + self._images = {i.Name: LOImage(i) for i in self._dp} @property def obj(self): @@ -1192,6 +1258,10 @@ class LOCalcSheet(object): def doc(self): return self._doc + @property + def images(self): + return self._images + @property def name(self): return self._obj.Name @@ -1306,7 +1376,11 @@ class LOWriter(LODocument): @property def selection(self): sel = self.obj.getCurrentSelection() - return LOTextRange(sel[0]) + if sel.ImplementationName == TEXT_RANGES: + return LOTextRange(sel[0]) + elif sel.ImplementationName == TEXT_RANGE: + return LOTextRange(sel) + return sel def write(self, data, cursor=None): cursor = cursor or self.selection.cursor.getEnd() @@ -1583,7 +1657,7 @@ class LODrawImpress(LODocument): def insert_image(self, path, **kwargs): w = kwargs.get('width', 3000) - h = kwargs.get('Height', 1000) + h = kwargs.get('Height', 3000) x = kwargs.get('X', 1000) y = kwargs.get('Y', 1000) @@ -1640,6 +1714,9 @@ class LOCellRange(object): def __getitem__(self, index): return LOCellRange(self.obj[index], self.doc) + def __contains__(self, item): + return item.in_range(self) + def _init_values(self): self._type_obj = self.obj.ImplementationName self._type_content = EMPTY @@ -1689,7 +1766,7 @@ class LOCellRange(object): self.obj.setFormula(data) else: self.obj.setString(data) - elif isinstance(data, (int, float)): + elif isinstance(data, (int, float, bool)): self.obj.setValue(data) elif isinstance(data, datetime.datetime): d = data.toordinal() @@ -1707,8 +1784,6 @@ class LOCellRange(object): return self.obj.getDataArray() @data.setter def data(self, values): - if isinstance(values, list): - values = tuple(values) self.obj.setDataArray(values) @property @@ -1716,8 +1791,6 @@ class LOCellRange(object): return self.obj.getFormulaArray() @formula.setter def formula(self, values): - if isinstance(values, list): - values = tuple(values) self.obj.setFormulaArray(values) @property @@ -1742,13 +1815,19 @@ class LOCellRange(object): cursor.collapseToSize(cols, rows) return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc) - def copy_from(self, rango): + def copy_from(self, rango, formula=False): data = rango if isinstance(rango, LOCellRange): - data = rango.data + if formula: + data = rango.formula + else: + data = rango.data rows = len(data) cols = len(data[0]) - self.to_size(rows, cols).data = data + if formula: + self.to_size(rows, cols).formula = data + else: + self.to_size(rows, cols).data = data return def copy_to(self, cell, formula=False): @@ -1759,6 +1838,10 @@ class LOCellRange(object): rango.data = self.data return + def copy(self, source): + self.sheet.obj.copyRange(self.address, source.range_address) + return + def offset(self, row=1, col=0): ra = self.obj.getRangeAddress() col = ra.EndColumn + col @@ -1816,6 +1899,10 @@ class LOCellRange(object): a = self.obj.getRangeAddressesAsString() return a + @property + def range_address(self): + return self.obj.getRangeAddress() + @property def current_region(self): cursor = self.sheet.get_cursor(self.obj[0,0]) @@ -1986,6 +2073,10 @@ class EventsListenerBase(unohelper.Base, XEventListener): self._name = name self._window = window + @property + def name(self): + return self._name + def disposing(self, event): self._controller = None if not self._window is None: @@ -2096,7 +2187,10 @@ class EventsItem(EventsListenerBase, XItemListener): pass def itemStateChanged(self, event): - pass + event_name = '{}_item_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return class EventsItemRoadmap(EventsItem): @@ -2145,6 +2239,45 @@ class EventsKey(EventsListenerBase, XKeyListener): return +class EventsTab(EventsListenerBase, XTabListener): + + def __init__(self, controller, name): + super().__init__(controller, name) + + def activated(self, id): + event_name = '{}_activated'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(id) + return + + +class EventsGrid(EventsListenerBase, XGridDataListener, XGridSelectionListener): + + def __init__(self, controller, name): + super().__init__(controller, name) + + def dataChanged(self, event): + event_name = '{}_data_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + def rowHeadingChanged(self, event): + pass + + def rowsInserted(self, event): + pass + + def rowsRemoved(self, evemt): + pass + + def selectionChanged(self, event): + event_name = '{}_selection_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + class EventsKeyWindow(EventsListenerBase, XKeyListener): """ event.KeyChar @@ -2221,8 +2354,8 @@ class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener): # ~ XWindowListener def windowResized(self, event): - # ~ sb = self._container.getControl('subcontainer') - # ~ sb.setPosSize(0, 0, event.Width, event.Height, SIZE) + sb = self._cls._subcont + sb.setPosSize(0, 0, event.Width, event.Height, SIZE) event_name = '{}_resized'.format(self._name) if hasattr(self._controller, event_name): getattr(self._controller, event_name)(event) @@ -2284,6 +2417,7 @@ class UnoBaseObject(object): @property def parent(self): + ps = self.obj.getContext().PosSize return self.obj.getContext() def _get_possize(self, name): @@ -2325,17 +2459,23 @@ class UnoBaseObject(object): return self._model.Width @width.setter def width(self, value): - self._model.Width = value + if hasattr(self.model, 'Width'): + self.model.Width = value + elif hasattr(self.obj, 'PosSize'): + self._set_possize('Width', value) @property def height(self): - if hasattr(self._model, 'Height'): - return self._model.Height + if hasattr(self.model, 'Height'): + return self.model.Height ps = self.obj.getPosSize() return ps.Height @height.setter def height(self, value): - self._model.Height = value + if hasattr(self.model, 'Height'): + self.model.Height = value + elif hasattr(self.obj, 'PosSize'): + self._set_possize('Height', value) @property def tag(self): @@ -2398,8 +2538,12 @@ class UnoBaseObject(object): def move(self, origin, x=0, y=5): if x: self.x = origin.x + origin.width + x + else: + self.x = origin.x if y: self.y = origin.y + origin.height + y + else: + self.y = origin.y return def possize(self, origin): @@ -2568,16 +2712,19 @@ class UnoGrid(UnoBaseObject): # ~ def format_columns(self, value): # ~ self._format_columns = value + @property + def value(self): + return self[self.column, self.row] + @property def data(self): return self._data @data.setter def data(self, values): # ~ self._data = values - self._gdm.removeAllRows() + self.clear() headings = tuple(range(1, len(values) + 1)) self._gdm.addRows(headings, values) - # ~ rows = range(grid_dm.RowCount) # ~ colors = [COLORS['GRAY'] if r % 2 else COLORS['WHITE'] for r in rows] # ~ grid.Model.RowBackgroundColors = tuple(colors) @@ -2617,6 +2764,10 @@ class UnoGrid(UnoBaseObject): row.append(d) return tuple(row) + def clear(self): + self._gdm.removeAllRows() + return + def add_row(self, data): # ~ self._data.append(data) data = self._validate_column(data) @@ -2626,18 +2777,17 @@ class UnoGrid(UnoBaseObject): def remove_row(self, row): self._gdm.removeRow(row) # ~ del self._data[row] - self._update_row_heading() + self.update_row_heading() return - def _update_row_heading(self): + 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() + self.update_row_heading() return def set_column_image(self, column, path): @@ -2685,7 +2835,6 @@ class UnoRoadmap(UnoBaseObject): class UnoTree(UnoBaseObject): - @catch_exception def __init__(self, obj, ): super().__init__(obj) self._tdm = None @@ -2738,6 +2887,119 @@ class UnoTree(UnoBaseObject): return +class UnoTab(UnoBaseObject): + + def __init__(self, obj): + super().__init__(obj) + self._events = None + + def __getitem__(self, index): + return self.get_sheet(index) + + @property + def current(self): + return self.obj.getActiveTabID() + @property + def active(self): + return self.current + + def get_sheet(self, id): + if isinstance(id, int): + sheet = self.obj.Controls[id-1] + else: + sheet = self.obj.getControl(id.lower()) + return sheet + + @property + def sheets(self): + return self._sheets + @sheets.setter + def sheets(self, values): + i = len(self.obj.Controls) + for title in values: + i += 1 + sheet = self.model.createInstance('com.sun.star.awt.UnoPageModel') + sheet.Title = title + self.model.insertByName('sheet{}'.format(i), sheet) + return + + def insert(self, title): + id = len(self.obj.Controls) + 1 + sheet = self.model.createInstance('com.sun.star.awt.UnoPageModel') + sheet.Title = title + self.model.insertByName('sheet{}'.format(id), sheet) + return id + + def remove(self, id): + sheet = self.get_sheet(id) + for control in sheet.getControls(): + sheet.Model.removeByName(control.Model.Name) + sheet.removeControl(control) + # ~ self._model.removeByName('page_{}'.format(ID)) + + self.obj.removeTab(id) + return + + def activate(self, id): + self.obj.activateTab(id) + return + + @property + def events(self): + return self._events + @events.setter + def events(self, controllers): + self._events = controllers + + def _special_properties(self, tipo, properties): + columns = properties.pop('Columns', ()) + if tipo == 'grid': + properties['ColumnModel'] = _set_column_model(columns) + if not 'Width' in properties: + properties['Width'] = self.width + if not 'Height' in properties: + properties['Height'] = self.height + elif tipo == 'button' and 'ImageURL' in properties: + properties['ImageURL'] = self._set_image_url(properties['ImageURL']) + elif tipo == 'roadmap': + if not 'Height' in properties: + properties['Height'] = self.height + if 'Title' in properties: + properties['Text'] = properties.pop('Title') + elif tipo == 'pages': + if not 'Width' in properties: + properties['Width'] = self.width + if not 'Height' in properties: + properties['Height'] = self.height + + return properties + + def add_control(self, id, properties): + tipo = properties.pop('Type').lower() + root = properties.pop('Root', '') + sheets = properties.pop('Sheets', ()) + properties = self._special_properties(tipo, properties) + + sheet = self.get_sheet(id) + sheet_model = sheet.getModel() + model = sheet_model.createInstance(get_control_model(tipo)) + set_properties(model, properties) + name = properties['Name'] + sheet_model.insertByName(name, model) + + control = sheet.getControl(name) + add_listeners(self.events, control, name) + control = get_custom_class(tipo, control) + + if tipo == 'tree' and root: + control.root = root + elif tipo == 'pages' and sheets: + control.sheets = sheets + + setattr(self, name, control) + return + + def get_custom_class(tipo, obj): classes = { 'label': UnoLabel, @@ -2748,7 +3010,7 @@ def get_custom_class(tipo, obj): 'link': UnoLabelLink, 'roadmap': UnoRoadmap, 'tree': UnoTree, - # ~ 'tab': UnoTab, + 'tab': UnoTab, # ~ 'image': UnoImage, # ~ 'radio': UnoRadio, # ~ 'groupbox': UnoGroupBox, @@ -2757,6 +3019,24 @@ def get_custom_class(tipo, obj): return classes[tipo](obj) +def get_control_model(control): + services = { + 'label': 'com.sun.star.awt.UnoControlFixedTextModel', + 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel', + 'text': 'com.sun.star.awt.UnoControlEditModel', + 'listbox': 'com.sun.star.awt.UnoControlListBoxModel', + 'button': 'com.sun.star.awt.UnoControlButtonModel', + 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel', + 'grid': 'com.sun.star.awt.grid.UnoControlGridModel', + 'tree': 'com.sun.star.awt.tree.TreeControlModel', + 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', + 'image': 'com.sun.star.awt.UnoControlImageControlModel', + 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel', + 'tab': 'com.sun.star.awt.UnoMultiPageModel', + } + return services[control] + + def add_listeners(events, control, name=''): listeners = { 'addActionListener': EventsButton, @@ -2764,6 +3044,7 @@ def add_listeners(events, control, name=''): 'addItemListener': EventsItem, 'addFocusListener': EventsFocus, 'addKeyListener': EventsKey, + 'addTabListener': EventsTab, } if hasattr(control, 'obj'): control = contro.obj @@ -2785,6 +3066,11 @@ def add_listeners(events, control, name=''): continue getattr(control, key)(listeners[key](events, name)) + + if is_grid: + controllers = EventsGrid(events, name) + control.addSelectionListener(controllers) + control.Model.GridDataModel.addGridDataListener(controllers) return @@ -3174,6 +3460,7 @@ class LOChart(object): chart_data.setData(data) chart_data.RowDescriptions = labels + # ~ Bug if tipo == 'Pie': chart.setDiagram(chart.createInstance(self.BASE.format('Bar'))) chart.setDiagram(chart.createInstance(self.BASE.format('Pie'))) @@ -3225,6 +3512,29 @@ class LOChart(object): return self +def _set_column_model(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 + + +def _set_image_url(image, id_extension=''): + 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) + + class LODialog(object): def __init__(self, **properties): @@ -3278,6 +3588,7 @@ class LODialog(object): 'stardiv.Toolkit.UnoButtonControl': 'button', 'stardiv.Toolkit.UnoListBoxControl': 'listbox', 'stardiv.Toolkit.UnoRoadmapControl': 'roadmap', + 'stardiv.Toolkit.UnoMultiPageControl': 'pages', } return types[name] @@ -3375,6 +3686,7 @@ class LODialog(object): 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', 'image': 'com.sun.star.awt.UnoControlImageControlModel', 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel', + 'pages': 'com.sun.star.awt.UnoMultiPageModel', } return services[control] @@ -3410,11 +3722,18 @@ class LODialog(object): properties['Height'] = self.height if 'Title' in properties: properties['Text'] = properties.pop('Title') + elif tipo == 'tab': + if not 'Width' in properties: + properties['Width'] = self.width + if not 'Height' in properties: + properties['Height'] = self.height + return properties def add_control(self, properties): tipo = properties.pop('Type').lower() root = properties.pop('Root', '') + sheets = properties.pop('Sheets', ()) properties = self._special_properties(tipo, properties) model = self.model.createInstance(self._get_control_model(tipo)) @@ -3427,6 +3746,9 @@ class LODialog(object): if tipo == 'tree' and root: control.root = root + elif tipo == 'pages' and sheets: + control.sheets = sheets + control.events = self.events setattr(self, name, control) return @@ -3459,13 +3781,26 @@ class LODialog(object): class LOWindow(object): + EMPTY = b""" + +""" def __init__(self, **kwargs): self._events = None self._menu = None self._container = None + self._id_extension = '' self._obj = self._create(kwargs) + @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 + def _create(self, properties): ps = ( properties.get('X', 0), @@ -3476,6 +3811,7 @@ class LOWindow(object): self._title = properties.get('Title', TITLE) self._create_frame(ps) self._create_container(ps) + self._create_subcontainer(ps) # ~ self._create_splitter(ps) return @@ -3507,6 +3843,31 @@ class LOWindow(object): self._frame.setComponent(self._container, None) return + def _create_subcontainer(self, ps): + service = 'com.sun.star.awt.ContainerWindowProvider' + cwp = create_instance(service, True) + with get_temp_file() as f: + f.write(self.EMPTY) + f.flush() + subcont = cwp.createContainerWindow( + _path_url(f.name), '', self._container.getPeer(), None) + + # ~ service = 'com.sun.star.awt.UnoControlDialog' + # ~ subcont2 = create_instance(service, True) + # ~ service = 'com.sun.star.awt.UnoControlDialogModel' + # ~ model = create_instance(service, True) + # ~ service = 'com.sun.star.awt.UnoControlContainer' + # ~ context = create_instance(service, True) + # ~ subcont2.setModel(model) + # ~ subcont2.setContext(context) + # ~ subcont2.createPeer(self._toolkit, self._container.getPeer()) + + subcont.setPosSize(0, 0, 500, 500, POSSIZE) + subcont.setVisible(True) + self._container.addControl('subcont', subcont) + self._subcont = subcont + return + def _get_base_control(self, tipo): services = { 'label': 'com.sun.star.awt.UnoControlFixedText', @@ -3520,25 +3881,50 @@ class LOWindow(object): 'radio': 'com.sun.star.awt.UnoControlRadioButton', 'tree': 'com.sun.star.awt.tree.TreeControl', 'grid': 'com.sun.star.awt.grid.UnoControlGrid', + 'tab': 'com.sun.star.awt.tab.UnoControlTabPage', } return services[tipo] + def _special_properties(self, tipo, properties): + columns = properties.pop('Columns', ()) + if tipo == 'grid': + properties['ColumnModel'] = self._set_column_model(columns) + elif tipo == 'button' and 'ImageURL' in properties: + properties['ImageURL'] = _set_image_url( + properties['ImageURL'], self.id_extension) + elif tipo == 'roadmap': + if not 'Height' in properties: + properties['Height'] = self.height + if 'Title' in properties: + properties['Text'] = properties.pop('Title') + elif tipo == 'tab': + if not 'Width' in properties: + properties['Width'] = self.width - 20 + if not 'Height' in properties: + properties['Height'] = self.height - 20 + + return properties + 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) + root = properties.pop('Root', '') + sheets = properties.pop('Sheets', ()) + + properties = self._special_properties(tipo, properties) + model = self._subcont.Model.createInstance(get_control_model(tipo)) 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) + self._subcont.Model.insertByName(name, model) + control = self._subcont.getControl(name) + add_listeners(self.events, control, name) + control = get_custom_class(tipo, control) + + if tipo == 'tree' and root: + control.root = root + elif tipo == 'tab' and sheets: + control.sheets = sheets + control.events = self.events + setattr(self, name, control) return @@ -3834,8 +4220,12 @@ def json_loads(data): def get_path_extension(id): + path = '' pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider') - path = _path_system(pip.getPackageLocation(id)) + try: + path = _path_system(pip.getPackageLocation(id)) + except Exception as e: + error(e) return path @@ -3979,7 +4369,7 @@ def open_file(path): if IS_WIN: os.startfile(path) else: - subprocess.Popen(['xdg-open', path]) + pid = subprocess.Popen(['xdg-open', path]).pid return @@ -4032,23 +4422,29 @@ def popen(command, stdin=None): yield (e.errno, e.strerror) -def url_open(url, options={}, json=False): +def url_open(url, options={}, verify=True, json=False): data = '' + err = '' req = Request(url) try: - response = urlopen(req) - # ~ response.info() + if verify: + response = urlopen(req) + else: + context = ssl._create_unverified_context() + response = urlopen(req, context=context) except HTTPError as e: error(e) + err = str(e) except URLError as e: error(e.reason) + err = str(e.reason) else: if json: data = json_loads(response.read()) else: data = response.read() - return data + return data, err def run(command, wait=False): @@ -5025,8 +5421,6 @@ class LIBOServer(object): return instance - - # ~ controls = { # ~ 'CheckBox': 'com.sun.star.awt.UnoControlCheckBoxModel', # ~ 'ComboBox': 'com.sun.star.awt.UnoControlComboBoxModel', diff --git a/files/ZAZFavorites_v0.1.0.oxt b/files/ZAZFavorites_v0.1.0.oxt deleted file mode 100644 index 09db96c..0000000 Binary files a/files/ZAZFavorites_v0.1.0.oxt and /dev/null differ diff --git a/files/ZAZFavorites_v0.2.0.oxt b/files/ZAZFavorites_v0.2.0.oxt deleted file mode 100644 index acaa466..0000000 Binary files a/files/ZAZFavorites_v0.2.0.oxt and /dev/null differ diff --git a/files/ZAZFavorites_v0.3.0.oxt b/files/ZAZFavorites_v0.3.0.oxt deleted file mode 100644 index 0136be9..0000000 Binary files a/files/ZAZFavorites_v0.3.0.oxt and /dev/null differ diff --git a/files/ZAZFavorites_v0.4.0.oxt b/files/ZAZFavorites_v0.5.0.oxt similarity index 51% rename from files/ZAZFavorites_v0.4.0.oxt rename to files/ZAZFavorites_v0.5.0.oxt index 679b8fe..62348e0 100644 Binary files a/files/ZAZFavorites_v0.4.0.oxt and b/files/ZAZFavorites_v0.5.0.oxt differ diff --git a/source/META-INF/manifest.xml b/source/META-INF/manifest.xml index ce03a57..d111eff 100644 --- a/source/META-INF/manifest.xml +++ b/source/META-INF/manifest.xml @@ -1,5 +1,5 @@ - + diff --git a/source/description.xml b/source/description.xml index 79c0dc7..1132090 100644 --- a/source/description.xml +++ b/source/description.xml @@ -1,7 +1,7 @@ - + Favorites files Archivos favoritos diff --git a/source/pythonpath/easymacro.py b/source/pythonpath/easymacro.py index 547f936..8259aca 100644 --- a/source/pythonpath/easymacro.py +++ b/source/pythonpath/easymacro.py @@ -34,6 +34,7 @@ import shlex import shutil import socket import subprocess +import ssl import sys import tempfile import threading @@ -41,13 +42,9 @@ import time import traceback import zipfile -# ~ from collections import OrderedDict -# ~ from collections.abc import MutableMapping from functools import wraps -from operator import itemgetter from pathlib import Path, PurePath from pprint import pprint -from enum import IntEnum from urllib.request import Request, urlopen from urllib.error import URLError, HTTPError from string import Template @@ -91,14 +88,10 @@ from com.sun.star.awt import XMenuListener from com.sun.star.awt import XKeyListener from com.sun.star.awt import XItemListener from com.sun.star.awt import XFocusListener +from com.sun.star.awt import XTabListener +from com.sun.star.awt.grid import XGridDataListener +from com.sun.star.awt.grid import XGridSelectionListener -class FontSlant(IntEnum): - NONE = 0 - OBLIQUE = 1 - ITALIC = 2 - DONTKNOW = 3 - REVERSE_OBLIQUE = 4 - REVERSE_ITALIC = 5 try: from fernet import Fernet, InvalidToken @@ -119,7 +112,6 @@ KEY = { SEPARATION = 5 - MSG_LANG = { 'es': { 'OK': 'Aceptar', @@ -130,35 +122,36 @@ MSG_LANG = { } } - OS = platform.system() USER = getpass.getuser() PC = platform.node() DESKTOP = os.environ.get('DESKTOP_SESSION', '') INFO_DEBUG = '{}\n\n{}\n\n{}'.format(sys.version, platform.platform(), '\n'.join(sys.path)) - IS_WIN = OS == 'Windows' LOG_NAME = 'ZAZ' CLIPBOARD_FORMAT_TEXT = 'text/plain;charset=utf-16' - PYTHON = 'python' if IS_WIN: PYTHON = 'python.exe' CALC = 'calc' WRITER = 'writer' + OBJ_CELL = 'ScCellObj' OBJ_RANGE = 'ScCellRangeObj' OBJ_RANGES = 'ScCellRangesObj' OBJ_TYPE_RANGES = (OBJ_CELL, OBJ_RANGE, OBJ_RANGES) +TEXT_RANGE = 'SwXTextRange' +TEXT_RANGES = 'SwXTextRanges' +TEXT_TYPE_RANGES = (TEXT_RANGE, TEXT_RANGES) + 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', - # ~ 'base': 'com.sun.star.sdb.OfficeDatabaseDocument', 'base': 'com.sun.star.sdb.DocumentDataSource', 'math': 'com.sun.star.formula.FormulaProperties', 'basic': 'com.sun.star.script.BasicIDE', @@ -197,19 +190,16 @@ MENUS_WRITER = { 'windows': '.uno:WindowList', 'help': '.uno:HelpMenu', } - MENUS_APP = { 'main': MENUS_MAIN, 'calc': MENUS_CALC, 'writer': MENUS_WRITER, } - EXT = { 'pdf': 'pdf', } - FILE_NAME_DEBUG = 'debug.odt' FILE_NAME_CONFIG = 'zaz-{}.json' LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s' @@ -279,6 +269,12 @@ def mri(obj): return +def inspect(obj): + zaz = create_instance('net.elmau.zaz.inspect') + zaz.inspect(obj) + return + + def catch_exception(f): @wraps(f) def func(*args, **kwargs): @@ -454,12 +450,14 @@ def call_dispatch(url, args=()): return -def get_temp_file(): +def get_temp_file(only_name=False): delete = True if IS_WIN: delete = False - return tempfile.NamedTemporaryFile(delete=delete) - + tmp = tempfile.NamedTemporaryFile(delete=delete) + if only_name: + tmp = tmp.name + return tmp def _path_url(path): if path.startswith('file://'): @@ -501,6 +499,11 @@ def dict_to_property(values, uno_any=False): return ps +def dict_to_named(values): + ps = tuple([NamedValue(n, v) for n, v in values.items()]) + return ps + + def property_to_dict(values): d = {i.Name: i.Value for i in values} return d @@ -934,6 +937,67 @@ class LOCellStyles(object): return +class LOImage(object): + TYPES = { + 'image/png': 'png', + 'image/jpeg': 'jpg', + } + + def __init__(self, obj): + self._obj = obj + + @property + def obj(self): + return self._obj + + @property + def address(self): + return self.obj.Anchor.AbsoluteName + + @property + def name(self): + return self.obj.Name + + @property + def mimetype(self): + return self.obj.Bitmap.MimeType + + @property + def url(self): + return _path_system(self.obj.URL) + @url.setter + def url(self, value): + self.obj.URL = _path_url(value) + + @property + def path(self): + return _path_system(self.obj.GraphicURL) + @path.setter + def path(self, value): + self.obj.GraphicURL = _path_url(value) + + @property + def visible(self): + return self.obj.Visible + @visible.setter + def visible(self, value): + self_obj.Visible = value + + def save(self, path): + if is_dir(path): + p = path + n = self.name + else: + p, fn, n, e = get_info_path(path) + ext = self.TYPES[self.mimetype] + path = join(p, '{}.{}'.format(n, ext)) + size = len(self.obj.Bitmap.DIB) + data = self.obj.GraphicStream.readBytes((), size) + data = data[-1].value + save_file(path, 'wb', data) + return path + + class LOCalc(LODocument): def __init__(self, obj): @@ -942,7 +1006,9 @@ class LOCalc(LODocument): def __getitem__(self, index): if isinstance(index, str): - index = [s.Name for s in self._sheets if s.CodeName == index][0] or index + code_name = [s.Name for s in self._sheets if s.CodeName == index] + if code_name: + index = code_name[0] return LOCalcSheet(self._sheets[index], self) def __setitem__(self, key, value): @@ -1182,7 +1248,7 @@ class LOCalcSheet(object): def _init_values(self): self._events = None self._dp = self.obj.getDrawPage() - return + self._images = {i.Name: LOImage(i) for i in self._dp} @property def obj(self): @@ -1192,6 +1258,10 @@ class LOCalcSheet(object): def doc(self): return self._doc + @property + def images(self): + return self._images + @property def name(self): return self._obj.Name @@ -1306,7 +1376,11 @@ class LOWriter(LODocument): @property def selection(self): sel = self.obj.getCurrentSelection() - return LOTextRange(sel[0]) + if sel.ImplementationName == TEXT_RANGES: + return LOTextRange(sel[0]) + elif sel.ImplementationName == TEXT_RANGE: + return LOTextRange(sel) + return sel def write(self, data, cursor=None): cursor = cursor or self.selection.cursor.getEnd() @@ -1583,7 +1657,7 @@ class LODrawImpress(LODocument): def insert_image(self, path, **kwargs): w = kwargs.get('width', 3000) - h = kwargs.get('Height', 1000) + h = kwargs.get('Height', 3000) x = kwargs.get('X', 1000) y = kwargs.get('Y', 1000) @@ -1640,6 +1714,9 @@ class LOCellRange(object): def __getitem__(self, index): return LOCellRange(self.obj[index], self.doc) + def __contains__(self, item): + return item.in_range(self) + def _init_values(self): self._type_obj = self.obj.ImplementationName self._type_content = EMPTY @@ -1689,7 +1766,7 @@ class LOCellRange(object): self.obj.setFormula(data) else: self.obj.setString(data) - elif isinstance(data, (int, float)): + elif isinstance(data, (int, float, bool)): self.obj.setValue(data) elif isinstance(data, datetime.datetime): d = data.toordinal() @@ -1707,8 +1784,6 @@ class LOCellRange(object): return self.obj.getDataArray() @data.setter def data(self, values): - if isinstance(values, list): - values = tuple(values) self.obj.setDataArray(values) @property @@ -1716,8 +1791,6 @@ class LOCellRange(object): return self.obj.getFormulaArray() @formula.setter def formula(self, values): - if isinstance(values, list): - values = tuple(values) self.obj.setFormulaArray(values) @property @@ -1742,13 +1815,19 @@ class LOCellRange(object): cursor.collapseToSize(cols, rows) return LOCellRange(self.sheet[cursor.AbsoluteName].obj, self.doc) - def copy_from(self, rango): + def copy_from(self, rango, formula=False): data = rango if isinstance(rango, LOCellRange): - data = rango.data + if formula: + data = rango.formula + else: + data = rango.data rows = len(data) cols = len(data[0]) - self.to_size(rows, cols).data = data + if formula: + self.to_size(rows, cols).formula = data + else: + self.to_size(rows, cols).data = data return def copy_to(self, cell, formula=False): @@ -1759,6 +1838,10 @@ class LOCellRange(object): rango.data = self.data return + def copy(self, source): + self.sheet.obj.copyRange(self.address, source.range_address) + return + def offset(self, row=1, col=0): ra = self.obj.getRangeAddress() col = ra.EndColumn + col @@ -1816,6 +1899,10 @@ class LOCellRange(object): a = self.obj.getRangeAddressesAsString() return a + @property + def range_address(self): + return self.obj.getRangeAddress() + @property def current_region(self): cursor = self.sheet.get_cursor(self.obj[0,0]) @@ -1986,6 +2073,10 @@ class EventsListenerBase(unohelper.Base, XEventListener): self._name = name self._window = window + @property + def name(self): + return self._name + def disposing(self, event): self._controller = None if not self._window is None: @@ -2096,7 +2187,10 @@ class EventsItem(EventsListenerBase, XItemListener): pass def itemStateChanged(self, event): - pass + event_name = '{}_item_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return class EventsItemRoadmap(EventsItem): @@ -2145,6 +2239,45 @@ class EventsKey(EventsListenerBase, XKeyListener): return +class EventsTab(EventsListenerBase, XTabListener): + + def __init__(self, controller, name): + super().__init__(controller, name) + + def activated(self, id): + event_name = '{}_activated'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(id) + return + + +class EventsGrid(EventsListenerBase, XGridDataListener, XGridSelectionListener): + + def __init__(self, controller, name): + super().__init__(controller, name) + + def dataChanged(self, event): + event_name = '{}_data_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + def rowHeadingChanged(self, event): + pass + + def rowsInserted(self, event): + pass + + def rowsRemoved(self, evemt): + pass + + def selectionChanged(self, event): + event_name = '{}_selection_changed'.format(self.name) + if hasattr(self._controller, event_name): + getattr(self._controller, event_name)(event) + return + + class EventsKeyWindow(EventsListenerBase, XKeyListener): """ event.KeyChar @@ -2221,8 +2354,8 @@ class EventsWindow(EventsListenerBase, XTopWindowListener, XWindowListener): # ~ XWindowListener def windowResized(self, event): - # ~ sb = self._container.getControl('subcontainer') - # ~ sb.setPosSize(0, 0, event.Width, event.Height, SIZE) + sb = self._cls._subcont + sb.setPosSize(0, 0, event.Width, event.Height, SIZE) event_name = '{}_resized'.format(self._name) if hasattr(self._controller, event_name): getattr(self._controller, event_name)(event) @@ -2284,6 +2417,7 @@ class UnoBaseObject(object): @property def parent(self): + ps = self.obj.getContext().PosSize return self.obj.getContext() def _get_possize(self, name): @@ -2325,17 +2459,23 @@ class UnoBaseObject(object): return self._model.Width @width.setter def width(self, value): - self._model.Width = value + if hasattr(self.model, 'Width'): + self.model.Width = value + elif hasattr(self.obj, 'PosSize'): + self._set_possize('Width', value) @property def height(self): - if hasattr(self._model, 'Height'): - return self._model.Height + if hasattr(self.model, 'Height'): + return self.model.Height ps = self.obj.getPosSize() return ps.Height @height.setter def height(self, value): - self._model.Height = value + if hasattr(self.model, 'Height'): + self.model.Height = value + elif hasattr(self.obj, 'PosSize'): + self._set_possize('Height', value) @property def tag(self): @@ -2398,8 +2538,12 @@ class UnoBaseObject(object): def move(self, origin, x=0, y=5): if x: self.x = origin.x + origin.width + x + else: + self.x = origin.x if y: self.y = origin.y + origin.height + y + else: + self.y = origin.y return def possize(self, origin): @@ -2568,16 +2712,19 @@ class UnoGrid(UnoBaseObject): # ~ def format_columns(self, value): # ~ self._format_columns = value + @property + def value(self): + return self[self.column, self.row] + @property def data(self): return self._data @data.setter def data(self, values): # ~ self._data = values - self._gdm.removeAllRows() + self.clear() headings = tuple(range(1, len(values) + 1)) self._gdm.addRows(headings, values) - # ~ rows = range(grid_dm.RowCount) # ~ colors = [COLORS['GRAY'] if r % 2 else COLORS['WHITE'] for r in rows] # ~ grid.Model.RowBackgroundColors = tuple(colors) @@ -2617,6 +2764,10 @@ class UnoGrid(UnoBaseObject): row.append(d) return tuple(row) + def clear(self): + self._gdm.removeAllRows() + return + def add_row(self, data): # ~ self._data.append(data) data = self._validate_column(data) @@ -2626,18 +2777,17 @@ class UnoGrid(UnoBaseObject): def remove_row(self, row): self._gdm.removeRow(row) # ~ del self._data[row] - self._update_row_heading() + self.update_row_heading() return - def _update_row_heading(self): + 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() + self.update_row_heading() return def set_column_image(self, column, path): @@ -2685,7 +2835,6 @@ class UnoRoadmap(UnoBaseObject): class UnoTree(UnoBaseObject): - @catch_exception def __init__(self, obj, ): super().__init__(obj) self._tdm = None @@ -2738,6 +2887,119 @@ class UnoTree(UnoBaseObject): return +class UnoTab(UnoBaseObject): + + def __init__(self, obj): + super().__init__(obj) + self._events = None + + def __getitem__(self, index): + return self.get_sheet(index) + + @property + def current(self): + return self.obj.getActiveTabID() + @property + def active(self): + return self.current + + def get_sheet(self, id): + if isinstance(id, int): + sheet = self.obj.Controls[id-1] + else: + sheet = self.obj.getControl(id.lower()) + return sheet + + @property + def sheets(self): + return self._sheets + @sheets.setter + def sheets(self, values): + i = len(self.obj.Controls) + for title in values: + i += 1 + sheet = self.model.createInstance('com.sun.star.awt.UnoPageModel') + sheet.Title = title + self.model.insertByName('sheet{}'.format(i), sheet) + return + + def insert(self, title): + id = len(self.obj.Controls) + 1 + sheet = self.model.createInstance('com.sun.star.awt.UnoPageModel') + sheet.Title = title + self.model.insertByName('sheet{}'.format(id), sheet) + return id + + def remove(self, id): + sheet = self.get_sheet(id) + for control in sheet.getControls(): + sheet.Model.removeByName(control.Model.Name) + sheet.removeControl(control) + # ~ self._model.removeByName('page_{}'.format(ID)) + + self.obj.removeTab(id) + return + + def activate(self, id): + self.obj.activateTab(id) + return + + @property + def events(self): + return self._events + @events.setter + def events(self, controllers): + self._events = controllers + + def _special_properties(self, tipo, properties): + columns = properties.pop('Columns', ()) + if tipo == 'grid': + properties['ColumnModel'] = _set_column_model(columns) + if not 'Width' in properties: + properties['Width'] = self.width + if not 'Height' in properties: + properties['Height'] = self.height + elif tipo == 'button' and 'ImageURL' in properties: + properties['ImageURL'] = self._set_image_url(properties['ImageURL']) + elif tipo == 'roadmap': + if not 'Height' in properties: + properties['Height'] = self.height + if 'Title' in properties: + properties['Text'] = properties.pop('Title') + elif tipo == 'pages': + if not 'Width' in properties: + properties['Width'] = self.width + if not 'Height' in properties: + properties['Height'] = self.height + + return properties + + def add_control(self, id, properties): + tipo = properties.pop('Type').lower() + root = properties.pop('Root', '') + sheets = properties.pop('Sheets', ()) + properties = self._special_properties(tipo, properties) + + sheet = self.get_sheet(id) + sheet_model = sheet.getModel() + model = sheet_model.createInstance(get_control_model(tipo)) + set_properties(model, properties) + name = properties['Name'] + sheet_model.insertByName(name, model) + + control = sheet.getControl(name) + add_listeners(self.events, control, name) + control = get_custom_class(tipo, control) + + if tipo == 'tree' and root: + control.root = root + elif tipo == 'pages' and sheets: + control.sheets = sheets + + setattr(self, name, control) + return + + def get_custom_class(tipo, obj): classes = { 'label': UnoLabel, @@ -2748,7 +3010,7 @@ def get_custom_class(tipo, obj): 'link': UnoLabelLink, 'roadmap': UnoRoadmap, 'tree': UnoTree, - # ~ 'tab': UnoTab, + 'tab': UnoTab, # ~ 'image': UnoImage, # ~ 'radio': UnoRadio, # ~ 'groupbox': UnoGroupBox, @@ -2757,6 +3019,24 @@ def get_custom_class(tipo, obj): return classes[tipo](obj) +def get_control_model(control): + services = { + 'label': 'com.sun.star.awt.UnoControlFixedTextModel', + 'link': 'com.sun.star.awt.UnoControlFixedHyperlinkModel', + 'text': 'com.sun.star.awt.UnoControlEditModel', + 'listbox': 'com.sun.star.awt.UnoControlListBoxModel', + 'button': 'com.sun.star.awt.UnoControlButtonModel', + 'roadmap': 'com.sun.star.awt.UnoControlRoadmapModel', + 'grid': 'com.sun.star.awt.grid.UnoControlGridModel', + 'tree': 'com.sun.star.awt.tree.TreeControlModel', + 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', + 'image': 'com.sun.star.awt.UnoControlImageControlModel', + 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel', + 'tab': 'com.sun.star.awt.UnoMultiPageModel', + } + return services[control] + + def add_listeners(events, control, name=''): listeners = { 'addActionListener': EventsButton, @@ -2764,6 +3044,7 @@ def add_listeners(events, control, name=''): 'addItemListener': EventsItem, 'addFocusListener': EventsFocus, 'addKeyListener': EventsKey, + 'addTabListener': EventsTab, } if hasattr(control, 'obj'): control = contro.obj @@ -2785,6 +3066,11 @@ def add_listeners(events, control, name=''): continue getattr(control, key)(listeners[key](events, name)) + + if is_grid: + controllers = EventsGrid(events, name) + control.addSelectionListener(controllers) + control.Model.GridDataModel.addGridDataListener(controllers) return @@ -3174,6 +3460,7 @@ class LOChart(object): chart_data.setData(data) chart_data.RowDescriptions = labels + # ~ Bug if tipo == 'Pie': chart.setDiagram(chart.createInstance(self.BASE.format('Bar'))) chart.setDiagram(chart.createInstance(self.BASE.format('Pie'))) @@ -3225,6 +3512,29 @@ class LOChart(object): return self +def _set_column_model(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 + + +def _set_image_url(image, id_extension=''): + 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) + + class LODialog(object): def __init__(self, **properties): @@ -3278,6 +3588,7 @@ class LODialog(object): 'stardiv.Toolkit.UnoButtonControl': 'button', 'stardiv.Toolkit.UnoListBoxControl': 'listbox', 'stardiv.Toolkit.UnoRoadmapControl': 'roadmap', + 'stardiv.Toolkit.UnoMultiPageControl': 'pages', } return types[name] @@ -3375,6 +3686,7 @@ class LODialog(object): 'groupbox': 'com.sun.star.awt.UnoControlGroupBoxModel', 'image': 'com.sun.star.awt.UnoControlImageControlModel', 'radio': 'com.sun.star.awt.UnoControlRadioButtonModel', + 'pages': 'com.sun.star.awt.UnoMultiPageModel', } return services[control] @@ -3410,11 +3722,18 @@ class LODialog(object): properties['Height'] = self.height if 'Title' in properties: properties['Text'] = properties.pop('Title') + elif tipo == 'tab': + if not 'Width' in properties: + properties['Width'] = self.width + if not 'Height' in properties: + properties['Height'] = self.height + return properties def add_control(self, properties): tipo = properties.pop('Type').lower() root = properties.pop('Root', '') + sheets = properties.pop('Sheets', ()) properties = self._special_properties(tipo, properties) model = self.model.createInstance(self._get_control_model(tipo)) @@ -3427,6 +3746,9 @@ class LODialog(object): if tipo == 'tree' and root: control.root = root + elif tipo == 'pages' and sheets: + control.sheets = sheets + control.events = self.events setattr(self, name, control) return @@ -3459,13 +3781,26 @@ class LODialog(object): class LOWindow(object): + EMPTY = b""" + +""" def __init__(self, **kwargs): self._events = None self._menu = None self._container = None + self._id_extension = '' self._obj = self._create(kwargs) + @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 + def _create(self, properties): ps = ( properties.get('X', 0), @@ -3476,6 +3811,7 @@ class LOWindow(object): self._title = properties.get('Title', TITLE) self._create_frame(ps) self._create_container(ps) + self._create_subcontainer(ps) # ~ self._create_splitter(ps) return @@ -3507,6 +3843,31 @@ class LOWindow(object): self._frame.setComponent(self._container, None) return + def _create_subcontainer(self, ps): + service = 'com.sun.star.awt.ContainerWindowProvider' + cwp = create_instance(service, True) + with get_temp_file() as f: + f.write(self.EMPTY) + f.flush() + subcont = cwp.createContainerWindow( + _path_url(f.name), '', self._container.getPeer(), None) + + # ~ service = 'com.sun.star.awt.UnoControlDialog' + # ~ subcont2 = create_instance(service, True) + # ~ service = 'com.sun.star.awt.UnoControlDialogModel' + # ~ model = create_instance(service, True) + # ~ service = 'com.sun.star.awt.UnoControlContainer' + # ~ context = create_instance(service, True) + # ~ subcont2.setModel(model) + # ~ subcont2.setContext(context) + # ~ subcont2.createPeer(self._toolkit, self._container.getPeer()) + + subcont.setPosSize(0, 0, 500, 500, POSSIZE) + subcont.setVisible(True) + self._container.addControl('subcont', subcont) + self._subcont = subcont + return + def _get_base_control(self, tipo): services = { 'label': 'com.sun.star.awt.UnoControlFixedText', @@ -3520,25 +3881,50 @@ class LOWindow(object): 'radio': 'com.sun.star.awt.UnoControlRadioButton', 'tree': 'com.sun.star.awt.tree.TreeControl', 'grid': 'com.sun.star.awt.grid.UnoControlGrid', + 'tab': 'com.sun.star.awt.tab.UnoControlTabPage', } return services[tipo] + def _special_properties(self, tipo, properties): + columns = properties.pop('Columns', ()) + if tipo == 'grid': + properties['ColumnModel'] = self._set_column_model(columns) + elif tipo == 'button' and 'ImageURL' in properties: + properties['ImageURL'] = _set_image_url( + properties['ImageURL'], self.id_extension) + elif tipo == 'roadmap': + if not 'Height' in properties: + properties['Height'] = self.height + if 'Title' in properties: + properties['Text'] = properties.pop('Title') + elif tipo == 'tab': + if not 'Width' in properties: + properties['Width'] = self.width - 20 + if not 'Height' in properties: + properties['Height'] = self.height - 20 + + return properties + 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) + root = properties.pop('Root', '') + sheets = properties.pop('Sheets', ()) + + properties = self._special_properties(tipo, properties) + model = self._subcont.Model.createInstance(get_control_model(tipo)) 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) + self._subcont.Model.insertByName(name, model) + control = self._subcont.getControl(name) + add_listeners(self.events, control, name) + control = get_custom_class(tipo, control) + + if tipo == 'tree' and root: + control.root = root + elif tipo == 'tab' and sheets: + control.sheets = sheets + control.events = self.events + setattr(self, name, control) return @@ -3834,8 +4220,12 @@ def json_loads(data): def get_path_extension(id): + path = '' pip = CTX.getValueByName('/singletons/com.sun.star.deployment.PackageInformationProvider') - path = _path_system(pip.getPackageLocation(id)) + try: + path = _path_system(pip.getPackageLocation(id)) + except Exception as e: + error(e) return path @@ -3979,7 +4369,7 @@ def open_file(path): if IS_WIN: os.startfile(path) else: - subprocess.Popen(['xdg-open', path]) + pid = subprocess.Popen(['xdg-open', path]).pid return @@ -4032,23 +4422,29 @@ def popen(command, stdin=None): yield (e.errno, e.strerror) -def url_open(url, options={}, json=False): +def url_open(url, options={}, verify=True, json=False): data = '' + err = '' req = Request(url) try: - response = urlopen(req) - # ~ response.info() + if verify: + response = urlopen(req) + else: + context = ssl._create_unverified_context() + response = urlopen(req, context=context) except HTTPError as e: error(e) + err = str(e) except URLError as e: error(e.reason) + err = str(e.reason) else: if json: data = json_loads(response.read()) else: data = response.read() - return data + return data, err def run(command, wait=False): @@ -5025,8 +5421,6 @@ class LIBOServer(object): return instance - - # ~ controls = { # ~ 'CheckBox': 'com.sun.star.awt.UnoControlCheckBoxModel', # ~ 'ComboBox': 'com.sun.star.awt.UnoControlComboBoxModel', diff --git a/zaz.py b/zaz.py index 2cac1bc..9cad0c8 100644 --- a/zaz.py +++ b/zaz.py @@ -33,6 +33,7 @@ from xml.dom.minidom import parseString from conf import ( DATA, DIRS, + DOMAIN, EXTENSION, FILES, INFO, @@ -138,6 +139,19 @@ def _save(path, data): return +def _get_files(path, filters=''): + paths = [] + if filters in ('*', '*.*'): + filters = '' + for folder, _, files in os.walk(path): + if filters: + pattern = re.compile(r'\.(?:{})$'.format(filters), re.IGNORECASE) + paths += [_join(folder, f) for f in files if pattern.search(f)] + else: + paths += files + return paths + + def _compress_oxt(): log.info('Compress OXT extension...') @@ -435,8 +449,50 @@ def _embed(args): return +def _locales(args): + EASYMACRO = 'easymacro.py' + + if args.files: + files = args.files.split(',') + else: + files = _get_files(DIRS['source'], 'py') + paths = ' '.join([f for f in files if not EASYMACRO in f]) + path_pot = _join(DIRS['source'], DIRS['locales'], '{}.pot'.format(DOMAIN)) + call([PATHS['gettext'], '-o', path_pot, paths]) + log.info('POT generate successfully...') + return + + +def _update(): + path_locales = _join(DIRS['source'], DIRS['locales']) + path_pot = _join(DIRS['source'], DIRS['locales'], '{}.pot'.format(DOMAIN)) + if not _exists(path_pot): + log.error('Not exists file POT...') + return + + files = _get_files(path_locales, 'po') + if not files: + log.error('First, generate files PO...') + return + + for f in files: + call([PATHS['msgmerge'], '-U', f, path_pot]) + log.info('\tUpdate: {}'.format(f)) + + log.info('Locales update successfully...') + return + + def main(args): + if args.update: + _update() + return + + if args.locales: + _locales(args) + return + if args.embed: _embed(args) return @@ -469,6 +525,10 @@ def _process_command_line_arguments(): default=False, required=False) parser.add_argument('-d', '--document', dest='document', default='') parser.add_argument('-f', '--files', dest='files', default='') + parser.add_argument('-l', '--locales', dest='locales', action='store_true', + default=False, required=False) + parser.add_argument('-u', '--update', dest='update', action='store_true', + default=False, required=False) return parser.parse_args()