Add styles
This commit is contained in:
parent
d0c5f3a8ed
commit
5a02f50cc6
|
@ -5,3 +5,4 @@ build/
|
|||
*.lock
|
||||
bk/
|
||||
site/
|
||||
update.sh
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
v 0.6.0 [17-Dec-2023]
|
||||
---------------------
|
||||
- Add control styles.
|
||||
|
||||
v 0.5.0 [07-Dec-2023]
|
||||
---------------------
|
||||
- Add acctions to controls.
|
||||
|
|
Binary file not shown.
After Width: | Height: | Size: 6.4 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.2 KiB |
Binary file not shown.
After Width: | Height: | Size: 7.6 KiB |
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
Binary file not shown.
After Width: | Height: | Size: 10 KiB |
|
@ -0,0 +1,177 @@
|
|||
|
||||
!!! tip "Atención"
|
||||
|
||||
La fecha inicial en Calc y en Python son diferentes.
|
||||
|
||||
<br>
|
||||
|
||||
### **today**
|
||||
|
||||
Obtener la fecha actual.
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
app.msgbox(d.today)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **now**
|
||||
|
||||
Obtener la fecha y hora actuales.
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
app.msgbox(d.now)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **time**
|
||||
|
||||
Obtener la hora actual.
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
app.msgbox(d.now.time())
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **epoch**
|
||||
|
||||
Obtener el [tiempo Unix][1]
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
app.msgbox(d.epoch)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **date**
|
||||
|
||||
Devolver una fecha
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
date = d.date(1974, 1, 15)
|
||||
app.msgbox(date)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **time**
|
||||
|
||||
Devolver una hora
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
time = d.time(10, 20, 15)
|
||||
app.msgbox(time)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **datetime**
|
||||
|
||||
Devolver fecha y hora
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
dt = d.datetime(1974, 1, 15, 10, 11, 12)
|
||||
app.msgbox(dt)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **str_to_date**
|
||||
|
||||
Convertir una cadena en fecha. Mira este [excelente recurso][2]
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
cadena = '1974-01-15'
|
||||
plantilla = '%Y-%m-%d'
|
||||
fecha = d.str_to_date(cadena, plantilla)
|
||||
app.msgbox(fecha)
|
||||
app.msgbox(type(fecha))
|
||||
```
|
||||
|
||||
Para obtener un valor válido para establecer en una celda de Calc.
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
cadena = '1974-01-15'
|
||||
plantilla = '%Y-%m-%d'
|
||||
fecha = d.str_to_date(cadena, plantilla, True)
|
||||
app.msgbox(fecha)
|
||||
app.msgbox(type(fecha))
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **calc_to_date**
|
||||
|
||||
Convierte el valor de una celda en una fecha Python, por ejemplo, la fecha inicial configurada en Calc.
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
valor_en_celda = 0
|
||||
fecha = d.calc_to_date(valor_en_celda)
|
||||
app.msgbox(fecha)
|
||||
app.msgbox(type(fecha))
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **sleep**
|
||||
|
||||
Pausar la ejecución por X segundos.
|
||||
|
||||
!!! tip inline end "Atención"
|
||||
|
||||
La pausa es bloqueante.
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
app.sleep(3)
|
||||
app.msgbox('Fin')
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **start** y **end**
|
||||
|
||||
Medir tiempo en segundos.
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
d.start()
|
||||
app.sleep(5)
|
||||
seconds = d.end()
|
||||
app.msgbox(seconds)
|
||||
```
|
||||
|
||||
Regresar timedelta en vez de segundos.
|
||||
|
||||
```py
|
||||
d = app.dates
|
||||
|
||||
d.start()
|
||||
app.sleep(5)
|
||||
td = d.end(False)
|
||||
app.msgbox(td)
|
||||
```
|
||||
|
||||
|
||||
[1]: https://es.wikipedia.org/wiki/Tiempo_Unix
|
||||
[2]: https://strftime.org
|
|
@ -0,0 +1,123 @@
|
|||
---
|
||||
title: Information
|
||||
---
|
||||
|
||||
Remember, import first the library.
|
||||
|
||||
```py
|
||||
import easymacro as app
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## About PC
|
||||
|
||||
<br>
|
||||
|
||||
### **OS**
|
||||
|
||||
Get Operate System.
|
||||
```py
|
||||
app.msgbox(app.OS)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **DESKTOP**
|
||||
|
||||
Get desktop type, only GNU/Linux.
|
||||
```py
|
||||
app.msgbox(app.DESKTOP)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **PC**
|
||||
|
||||
Get PC name.
|
||||
```py
|
||||
app.msgbox(app.PC)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **USER**
|
||||
|
||||
Get user name.
|
||||
```py
|
||||
app.msgbox(app.USER)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **IS_WIN**
|
||||
|
||||
If OS is Windows.
|
||||
```py
|
||||
app.msgbox(app.IS_WIN)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **IS_MAC**
|
||||
|
||||
IF OS is MAC
|
||||
```py
|
||||
app.msgbox(app.IS_MAC)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
## About LibreOffice
|
||||
|
||||
### **NAME**
|
||||
|
||||
Application name.
|
||||
```py
|
||||
app.msgbox(app.NAME)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **VERSION**
|
||||
|
||||
Version.
|
||||
```py
|
||||
app.msgbox(app.VERSION)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **LANG**
|
||||
|
||||
Language
|
||||
```py
|
||||
app.msgbox(app.LANG)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **LANGUAGE**
|
||||
|
||||
Language with variant.
|
||||
```py
|
||||
app.msgbox(app.LANGUAGE)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **IS_APPIMAGE**
|
||||
|
||||
If LibreOffice use by AppImage.
|
||||
```py
|
||||
app.msgbox(app.IS_APPIMAGE)
|
||||
```
|
||||
|
||||
<br>
|
||||
|
||||
### **IS_FLATPAK**
|
||||
|
||||
If LibreOffice is use by FlatPak.
|
||||
```py
|
||||
app.msgbox(app.IS_FLATPAK)
|
||||
```
|
|
@ -0,0 +1,75 @@
|
|||
## Message Box
|
||||
|
||||
### **msgbox**
|
||||
|
||||
Show standard message.
|
||||
```py
|
||||
message = 'Fucking World'
|
||||
title = 'My Macro'
|
||||
app.msgbox(message, title)
|
||||
```
|
||||
|
||||
![msgbox](../img/tools_msg_01.png)
|
||||
|
||||
<br>
|
||||
|
||||
### **warning**
|
||||
|
||||
Show message with warning icon.
|
||||
```py
|
||||
message = 'Caution, this action is dangerous'
|
||||
title = 'My Macro'
|
||||
app.warning(message, title)
|
||||
```
|
||||
|
||||
![warning](../img/tools_msg_02.png)
|
||||
|
||||
<br>
|
||||
|
||||
### **errorbox**
|
||||
|
||||
Show message with error icon.
|
||||
```py
|
||||
message = 'ERROR: contact support'
|
||||
title = 'My Macro'
|
||||
app.errorbox(message, title)
|
||||
```
|
||||
|
||||
![error](../img/tools_msg_03.png)
|
||||
|
||||
<br>
|
||||
|
||||
### **question**
|
||||
|
||||
Ask a question by showing the interrogation icon and displaying the command buttons `Yes` and `No`. The answer is always True if user select `yes` and False otherwise.
|
||||
```py
|
||||
message = 'Python is easy?'
|
||||
title = 'My Macro'
|
||||
result = app.question(message, title)
|
||||
app.msgbox(result)
|
||||
```
|
||||
|
||||
![question](../img/tools_msg_04.png)
|
||||
|
||||
<br>
|
||||
|
||||
### **inputbox**
|
||||
|
||||
Shows a message to user, allowing to capture an answer.
|
||||
```py
|
||||
message = 'Capture your name'
|
||||
name = app.inputbox(message)
|
||||
app.msgbox(name)
|
||||
```
|
||||
|
||||
![inputbox](../img/tools_msg_05.png)
|
||||
|
||||
To hide on screen what the user typing, util for request passwords.
|
||||
```py
|
||||
message = 'Type your password'
|
||||
echochar = '*'
|
||||
password = app.inputbox(message, echochar=echochar)
|
||||
app.msgbox(password)
|
||||
```
|
||||
|
||||
![inputbox](../img/tools_msg_06.png)
|
|
@ -5,6 +5,10 @@ nav:
|
|||
- Home: index.md
|
||||
- Install: install.md
|
||||
- Debug: debug.md
|
||||
- Tools:
|
||||
- tools/index.md
|
||||
- Messages: tools/messages.md
|
||||
- Dates and time: tools/datetime.md
|
||||
theme:
|
||||
name: material
|
||||
locale: en
|
||||
|
@ -33,8 +37,8 @@ markdown_extensions:
|
|||
extra:
|
||||
alternate:
|
||||
- name: English
|
||||
link: /
|
||||
link: /easymacro
|
||||
lang: en
|
||||
- name: Español
|
||||
link: /langs/es
|
||||
link: /easymacro/langs/es
|
||||
lang: es
|
||||
|
|
|
@ -69,8 +69,8 @@ markdown_extensions:
|
|||
extra:
|
||||
alternate:
|
||||
- name: English
|
||||
link: /
|
||||
link: /easymacro
|
||||
lang: en
|
||||
- name: Español
|
||||
link: /langs/es
|
||||
link: /easymacro/langs/es
|
||||
lang: es
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# ~ Is de sum of:
|
||||
# ~ https://api.libreoffice.org/docs/idl/ref/namespacecom_1_1sun_1_1star_1_1sheet_1_1CellFlags.html
|
||||
|
||||
from com.sun.star.sheet import CellFlags
|
||||
|
||||
ONLY_DATA = 31
|
||||
|
||||
ALL = 1023
|
|
@ -16,75 +16,13 @@ from .easyevents import EventsRangeSelectionListener, LOEvents
|
|||
from .easyshape import LOShapes, LOShape
|
||||
from .easydrawpage import LODrawPage
|
||||
from .easyforms import LOForms
|
||||
from .easystyles import LOStyleFamilies
|
||||
|
||||
|
||||
SECONDS_DAY = 60 * 60 * 24
|
||||
ONLY_VALUES = CellFlags.VALUE + CellFlags.DATETIME + CellFlags.STRING
|
||||
|
||||
|
||||
class LOCellStyle():
|
||||
|
||||
def __init__(self, obj):
|
||||
self._obj = obj
|
||||
|
||||
def __str__(self):
|
||||
return f'CellStyle: {self.name}'
|
||||
|
||||
@property
|
||||
def obj(self):
|
||||
return self._obj
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.obj.Name
|
||||
|
||||
@property
|
||||
def properties(self):
|
||||
properties = self.obj.PropertySetInfo.Properties
|
||||
data = {p.Name: getattr(self.obj, p.Name) for p in properties}
|
||||
return data
|
||||
@properties.setter
|
||||
def properties(self, values):
|
||||
set_properties(self.obj, values)
|
||||
|
||||
|
||||
class LOCellStyles():
|
||||
|
||||
def __init__(self, obj, doc):
|
||||
self._obj = obj
|
||||
self._doc = doc
|
||||
|
||||
def __len__(self):
|
||||
return len(self.obj)
|
||||
|
||||
def __getitem__(self, index):
|
||||
return LOCellStyle(self.obj[index])
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
self.obj[key] = value
|
||||
|
||||
def __delitem__(self, key):
|
||||
if not isinstance(key, str):
|
||||
key = key.Name
|
||||
del self.obj[key]
|
||||
|
||||
def __contains__(self, item):
|
||||
return item in self.obj
|
||||
|
||||
@property
|
||||
def obj(self):
|
||||
return self._obj
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
return self.obj.ElementNames
|
||||
|
||||
def new(self, name: str):
|
||||
obj = self._doc.create_instance('com.sun.star.style.CellStyle')
|
||||
self.obj[name] = obj
|
||||
return LOCellStyle(obj)
|
||||
|
||||
|
||||
# ~ IsFiltered,
|
||||
# ~ IsManualPageBreak,
|
||||
# ~ IsStartOfNewPage
|
||||
|
@ -939,6 +877,11 @@ class LOCalc(LODocument):
|
|||
"""Get class events"""
|
||||
return LOEvents(self.obj.Events)
|
||||
|
||||
@property
|
||||
def styles(self):
|
||||
ci = self.obj.createInstance
|
||||
return LOStyleFamilies(self.obj.StyleFamilies, ci)
|
||||
|
||||
def ranges(self):
|
||||
"""Create ranges container"""
|
||||
obj = self._create_instance('com.sun.star.sheet.SheetCellRanges')
|
||||
|
@ -1111,5 +1054,4 @@ class LOCalc(LODocument):
|
|||
return self.cell_styles
|
||||
@property
|
||||
def cell_styles(self):
|
||||
obj = self.obj.StyleFamilies['CellStyles']
|
||||
return LOCellStyles(obj, self)
|
||||
return self.styles['CellStyles']
|
||||
|
|
|
@ -24,6 +24,7 @@ from com.sun.star.beans import PropertyValue, NamedValue, StringPair
|
|||
from com.sun.star.datatransfer import XTransferable, DataFlavor
|
||||
from com.sun.star.ui.dialogs import TemplateDescription
|
||||
|
||||
from .constants import ALL
|
||||
from .messages import MESSAGES
|
||||
|
||||
|
||||
|
@ -31,6 +32,8 @@ __all__ = [
|
|||
'ALL',
|
||||
'DESKTOP',
|
||||
'INFO_DEBUG',
|
||||
'IS_APPIMAGE',
|
||||
'IS_FLATPAK',
|
||||
'IS_MAC',
|
||||
'IS_WIN',
|
||||
'LANG',
|
||||
|
@ -63,13 +66,12 @@ PC = platform.node()
|
|||
USER = getpass.getuser()
|
||||
IS_WIN = OS == 'Windows'
|
||||
IS_MAC = OS == 'Darwin'
|
||||
|
||||
|
||||
ALL = 1023
|
||||
IS_FLATPAK = bool(os.getenv("FLATPAK_ID", ""))
|
||||
IS_APPIMAGE = bool(os.getenv("APPIMAGE", ""))
|
||||
|
||||
|
||||
LOG_FORMAT = '%(asctime)s - %(levelname)s - %(message)s'
|
||||
LOG_DATE = '%d/%m/%Y %H:%M:%S'
|
||||
LOG_DATE = '%Y/%m/%d %H:%M:%S'
|
||||
if IS_WIN:
|
||||
logging.addLevelName(logging.ERROR, 'ERROR')
|
||||
logging.addLevelName(logging.DEBUG, 'DEBUG')
|
||||
|
@ -149,7 +151,6 @@ day = get_app_config(node, 'DD')
|
|||
DATE_OFFSET = datetime.date(year, month, day).toordinal()
|
||||
|
||||
_info_debug = f"Python: {sys.version}\n\n{platform.platform()}\n\n" + '\n'.join(sys.path)
|
||||
# ~ doc
|
||||
INFO_DEBUG = f"{NAME} v{VERSION} {LANGUAGE}\n\n{_info_debug}"
|
||||
|
||||
|
||||
|
@ -221,6 +222,12 @@ def set_properties(model, properties):
|
|||
return
|
||||
|
||||
|
||||
def get_properties(obj):
|
||||
properties = obj.PropertySetInfo.Properties
|
||||
values = {p.Name: getattr(obj, p.Name) for p in properties}
|
||||
return values
|
||||
|
||||
|
||||
# ~ https://github.com/django/django/blob/main/django/utils/functional.py#L61
|
||||
class classproperty:
|
||||
|
||||
|
|
|
@ -0,0 +1,143 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
|
||||
from .easymain import log, BaseObject, set_properties, get_properties
|
||||
|
||||
|
||||
STYLE_FAMILIES = 'StyleFamilies'
|
||||
|
||||
|
||||
class LOBaseStyles(BaseObject):
|
||||
|
||||
def __init__(self, obj, create_instance=None):
|
||||
super().__init__(obj)
|
||||
self._create_intance = create_instance
|
||||
|
||||
def __len__(self):
|
||||
return self.obj.Count
|
||||
|
||||
def __contains__(self, item):
|
||||
return self.obj.hasByName(item)
|
||||
|
||||
def __getitem__(self, index):
|
||||
if index in self:
|
||||
style = self.obj.getByName(index)
|
||||
else:
|
||||
raise IndexError
|
||||
|
||||
if self.NAME == STYLE_FAMILIES:
|
||||
s = LOStyles(style, index, self._create_intance)
|
||||
else:
|
||||
s = LOStyle(style)
|
||||
return s
|
||||
|
||||
def __iter__(self):
|
||||
self._index = 0
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self._index < self.obj.Count:
|
||||
style = self[self.names[self._index]]
|
||||
else:
|
||||
raise StopIteration
|
||||
|
||||
self._index += 1
|
||||
return style
|
||||
|
||||
@property
|
||||
def names(self):
|
||||
return self.obj.ElementNames
|
||||
|
||||
|
||||
class LOStyle():
|
||||
NAME = 'Style'
|
||||
|
||||
def __init__(self, obj):
|
||||
self._obj = obj
|
||||
|
||||
def __str__(self):
|
||||
return f'Style: {self.name}'
|
||||
|
||||
def __contains__(self, item):
|
||||
return hasattr(self.obj, item)
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name != '_obj':
|
||||
self.obj.setPropertyValue(name, value)
|
||||
else:
|
||||
super().__setattr__(name, value)
|
||||
|
||||
def __getattr__(self, name):
|
||||
return self.obj.getPropertyValue(name)
|
||||
|
||||
@property
|
||||
def obj(self):
|
||||
return self._obj
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self.obj.Name
|
||||
|
||||
@property
|
||||
def is_in_use(self):
|
||||
return self.obj.isInUse()
|
||||
|
||||
@property
|
||||
def is_user_defined(self):
|
||||
return self.obj.isUserDefined()
|
||||
|
||||
@property
|
||||
def properties(self):
|
||||
return get_properties(self.obj)
|
||||
@properties.setter
|
||||
def properties(self, values):
|
||||
set_properties(self.obj, values)
|
||||
|
||||
|
||||
class LOStyles(LOBaseStyles):
|
||||
NAME = 'Styles'
|
||||
|
||||
def __init__(self, obj, type_style, create_instance):
|
||||
super().__init__(obj)
|
||||
self._type_style = type_style
|
||||
self._create_instance = create_instance
|
||||
|
||||
def __str__(self):
|
||||
return f'Styles: {self._type_style}'
|
||||
|
||||
def __setitem__(self, key, value):
|
||||
if key in self:
|
||||
style = self.obj.getByName(key)
|
||||
else:
|
||||
name = f'com.sun.star.style.{self._type_style[:-1]}'
|
||||
style = self._create_instance(name)
|
||||
self.obj.insertByName(key, style)
|
||||
set_properties(style, value)
|
||||
|
||||
def __delitem__(self, key):
|
||||
self.obj.removeByName(key)
|
||||
|
||||
|
||||
class LOStyleFamilies(LOBaseStyles):
|
||||
NAME = STYLE_FAMILIES
|
||||
|
||||
def __init__(self, obj, create_instance):
|
||||
super().__init__(obj, create_instance)
|
||||
|
||||
def __str__(self):
|
||||
return f'Style Families: {self.names}'
|
||||
|
||||
def _validate_name(self, name):
|
||||
if not name.endswith('Styles'):
|
||||
name = f'{name}Styles'
|
||||
return name
|
||||
|
||||
def __contains__(self, item):
|
||||
return self.obj.hasByName(self._validate_name(item))
|
||||
|
||||
def __getitem__(self, index):
|
||||
if isinstance(index, int):
|
||||
index = self.names[index]
|
||||
else:
|
||||
index = self._validate_name(index)
|
||||
return super().__getitem__(index)
|
|
@ -1,11 +1,123 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
from .easymain import log, BaseObject
|
||||
from .easydoc import LODocument
|
||||
|
||||
|
||||
class LOWriterTextPortion(BaseObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
def __str__(self):
|
||||
return 'Writer: TextPortion'
|
||||
|
||||
@property
|
||||
def string(self):
|
||||
return self.obj.String
|
||||
|
||||
|
||||
class LOWriterParagraph(BaseObject):
|
||||
TEXT_PORTION = 'SwXTextPortion'
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
def __str__(self):
|
||||
return 'Writer: Paragraph'
|
||||
|
||||
def __iter__(self):
|
||||
self._iter = iter(self.obj)
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
obj = next(self._iter)
|
||||
type_obj = obj.ImplementationName
|
||||
if type_obj == self.TEXT_PORTION:
|
||||
obj = LOWriterTextPortion(obj)
|
||||
return obj
|
||||
|
||||
@property
|
||||
def string(self):
|
||||
return self.obj.String
|
||||
|
||||
@property
|
||||
def cursor(self):
|
||||
return self.obj.Text.createTextCursorByRange(self.obj)
|
||||
|
||||
|
||||
class LOWriterTextRange(BaseObject):
|
||||
PARAGRAPH = 'SwXParagraph'
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
||||
def __str__(self):
|
||||
return 'Writer: TextRange'
|
||||
|
||||
def __getitem__(self, index):
|
||||
for i, v in enumerate(self):
|
||||
if index == i:
|
||||
return v
|
||||
if index > i:
|
||||
raise IndexError
|
||||
|
||||
def __iter__(self):
|
||||
self._enum = self.obj.createEnumeration()
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
if self._enum.hasMoreElements():
|
||||
obj = self._enum.nextElement()
|
||||
type_obj = obj.ImplementationName
|
||||
if type_obj == self.PARAGRAPH:
|
||||
obj = LOWriterParagraph(obj)
|
||||
else:
|
||||
raise StopIteration
|
||||
return obj
|
||||
|
||||
@property
|
||||
def string(self):
|
||||
return self.obj.String
|
||||
|
||||
@property
|
||||
def cursor(self):
|
||||
return self.obj.Text.createTextCursorByRange(self.obj)
|
||||
|
||||
|
||||
class LOWriterTextRanges(BaseObject):
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
# ~ self._doc = doc
|
||||
# ~ self._paragraphs = [LOWriterTextRange(p, doc) for p in obj]
|
||||
|
||||
def __str__(self):
|
||||
return 'Writer: TextRanges'
|
||||
|
||||
def __len__(self):
|
||||
return self.obj.Count
|
||||
|
||||
def __getitem__(self, index):
|
||||
return LOWriterTextRange(self.obj[index])
|
||||
|
||||
def __iter__(self):
|
||||
self._index = 0
|
||||
return self
|
||||
|
||||
def __next__(self):
|
||||
try:
|
||||
obj = LOWriterTextRange(self.obj[self._index])
|
||||
except IndexError:
|
||||
raise StopIteration
|
||||
|
||||
self._index += 1
|
||||
return obj
|
||||
|
||||
|
||||
class LOWriter(LODocument):
|
||||
_type = 'writer'
|
||||
TEXT_RANGES = 'SwXTextRanges'
|
||||
_type = 'writer'
|
||||
|
||||
def __init__(self, obj):
|
||||
super().__init__(obj)
|
||||
|
@ -17,3 +129,25 @@ class LOWriter(LODocument):
|
|||
@zoom.setter
|
||||
def zoom(self, value):
|
||||
self._view_settings.ZoomValue = value
|
||||
|
||||
@property
|
||||
def selection(self):
|
||||
"""Get current seleccion"""
|
||||
sel = None
|
||||
selection = self.obj.CurrentSelection
|
||||
type_obj = selection.ImplementationName
|
||||
|
||||
if type_obj == self.TEXT_RANGES:
|
||||
sel = LOWriterTextRanges(selection)
|
||||
if len(sel) == 1:
|
||||
sel = sel[0]
|
||||
else:
|
||||
log.debug(type_obj)
|
||||
log.debug(selection)
|
||||
sel = selection
|
||||
|
||||
return sel
|
||||
|
||||
@property
|
||||
def string(self):
|
||||
return self._obj.Text.String
|
||||
|
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env python
|
||||
|
||||
# ~ https://github.com/pyca/cryptography/blob/main/src/cryptography/fernet.py#L27
|
||||
|
||||
import base64
|
||||
import binascii
|
||||
import os
|
||||
import time
|
||||
import typing
|
||||
|
||||
|
||||
_MAX_CLOCK_SKEW = 60
|
||||
|
||||
|
||||
class InvalidSignature(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class InvalidToken(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Fernet:
|
||||
def __init__(
|
||||
self,
|
||||
key: bytes | str,
|
||||
backend: typing.Any = None,
|
||||
) -> None:
|
||||
try:
|
||||
key = base64.urlsafe_b64decode(key)
|
||||
except binascii.Error as exc:
|
||||
raise ValueError(
|
||||
"Fernet key must be 32 url-safe base64-encoded bytes."
|
||||
) from exc
|
||||
if len(key) != 32:
|
||||
raise ValueError(
|
||||
"Fernet key must be 32 url-safe base64-encoded bytes."
|
||||
)
|
||||
|
||||
|
||||
self._signing_key = key[:16]
|
||||
self._encryption_key = key[16:]
|
||||
|
||||
|
||||
@classmethod
|
||||
def generate_key(cls) -> bytes:
|
||||
return base64.urlsafe_b64encode(os.urandom(32))
|
||||
|
||||
|
||||
def encrypt(self, data: bytes) -> bytes:
|
||||
return self.encrypt_at_time(data, int(time.time()))
|
||||
|
||||
|
||||
def encrypt_at_time(self, data: bytes, current_time: int) -> bytes:
|
||||
iv = os.urandom(16)
|
||||
return self._encrypt_from_parts(data, current_time, iv)
|
||||
|
||||
|
||||
def _encrypt_from_parts(
|
||||
self, data: bytes, current_time: int, iv: bytes
|
||||
) -> bytes:
|
||||
utils._check_bytes("data", data)
|
||||
|
||||
|
||||
padder = padding.PKCS7(algorithms.AES.block_size).padder()
|
||||
padded_data = padder.update(data) + padder.finalize()
|
||||
encryptor = Cipher(
|
||||
algorithms.AES(self._encryption_key),
|
||||
modes.CBC(iv),
|
||||
).encryptor()
|
||||
ciphertext = encryptor.update(padded_data) + encryptor.finalize()
|
||||
|
||||
|
||||
basic_parts = (
|
||||
b"\x80"
|
||||
+ current_time.to_bytes(length=8, byteorder="big")
|
||||
+ iv
|
||||
+ ciphertext
|
||||
)
|
||||
|
||||
|
||||
h = HMAC(self._signing_key, hashes.SHA256())
|
||||
h.update(basic_parts)
|
||||
hmac = h.finalize()
|
||||
return base64.urlsafe_b64encode(basic_parts + hmac)
|
||||
|
||||
|
||||
def decrypt(self, token: bytes | str, ttl: int | None = None) -> bytes:
|
||||
timestamp, data = Fernet._get_unverified_token_data(token)
|
||||
if ttl is None:
|
||||
time_info = None
|
||||
else:
|
||||
time_info = (ttl, int(time.time()))
|
||||
return self._decrypt_data(data, timestamp, time_info)
|
||||
|
||||
|
||||
def decrypt_at_time(
|
||||
self, token: bytes | str, ttl: int, current_time: int
|
||||
) -> bytes:
|
||||
if ttl is None:
|
||||
raise ValueError(
|
||||
"decrypt_at_time() can only be used with a non-None ttl"
|
||||
)
|
||||
timestamp, data = Fernet._get_unverified_token_data(token)
|
||||
return self._decrypt_data(data, timestamp, (ttl, current_time))
|
||||
|
||||
|
||||
def extract_timestamp(self, token: bytes | str) -> int:
|
||||
timestamp, data = Fernet._get_unverified_token_data(token)
|
||||
# Verify the token was not tampered with.
|
||||
self._verify_signature(data)
|
||||
return timestamp
|
||||
|
||||
|
||||
@staticmethod
|
||||
def _get_unverified_token_data(token: bytes | str) -> tuple[int, bytes]:
|
||||
if not isinstance(token, (str, bytes)):
|
||||
raise TypeError("token must be bytes or str")
|
||||
|
||||
|
||||
try:
|
||||
data = base64.urlsafe_b64decode(token)
|
||||
except (TypeError, binascii.Error):
|
||||
raise InvalidToken
|
||||
|
||||
|
||||
if not data or data[0] != 0x80:
|
||||
raise InvalidToken
|
||||
|
||||
|
||||
if len(data) < 9:
|
||||
raise InvalidToken
|
||||
|
||||
|
||||
timestamp = int.from_bytes(data[1:9], byteorder="big")
|
||||
return timestamp, data
|
||||
|
||||
|
||||
def _verify_signature(self, data: bytes) -> None:
|
||||
h = HMAC(self._signing_key, hashes.SHA256())
|
||||
h.update(data[:-32])
|
||||
try:
|
||||
h.verify(data[-32:])
|
||||
except InvalidSignature:
|
||||
raise InvalidToken
|
||||
|
||||
|
||||
def _decrypt_data(
|
||||
self,
|
||||
data: bytes,
|
||||
timestamp: int,
|
||||
time_info: tuple[int, int] | None,
|
||||
) -> bytes:
|
||||
if time_info is not None:
|
||||
ttl, current_time = time_info
|
||||
if timestamp + ttl < current_time:
|
||||
raise InvalidToken
|
||||
|
||||
|
||||
if current_time + _MAX_CLOCK_SKEW < timestamp:
|
||||
raise InvalidToken
|
||||
|
||||
|
||||
self._verify_signature(data)
|
||||
|
||||
|
||||
iv = data[9:25]
|
||||
ciphertext = data[25:-32]
|
||||
decryptor = Cipher(
|
||||
algorithms.AES(self._encryption_key), modes.CBC(iv)
|
||||
).decryptor()
|
||||
plaintext_padded = decryptor.update(ciphertext)
|
||||
try:
|
||||
plaintext_padded += decryptor.finalize()
|
||||
except ValueError:
|
||||
raise InvalidToken
|
||||
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
||||
|
||||
|
||||
unpadded = unpadder.update(plaintext_padded)
|
||||
try:
|
||||
unpadded += unpadder.finalize()
|
||||
except ValueError:
|
||||
raise InvalidToken
|
||||
return unpadded
|
|
@ -9,6 +9,6 @@ USER = 'elmau'
|
|||
IS_WIN = False
|
||||
IS_MAC = False
|
||||
|
||||
LIBO_VERSION = '7.4'
|
||||
LIBO_VERSION = '7.6'
|
||||
LANGUAGE = 'en-US'
|
||||
LANG = 'en'
|
||||
|
|
Loading…
Reference in New Issue