"""
Module for table border (``TableBorder2``) struct
.. versionadded:: 0.9.0
"""
# region Import
from __future__ import annotations
from typing import Any, Tuple, Type, cast, overload, TypeVar, TYPE_CHECKING
from ooo.dyn.table.table_border import TableBorder
from ooo.dyn.table.table_border2 import TableBorder2
from ooo.dyn.table.border_line import BorderLine
from ooo.dyn.table.border_line2 import BorderLine2
from ooodev.events.args.cancel_event_args import CancelEventArgs
from ooodev.events.args.event_args import EventArgs
from ooodev.events.format_named_event import FormatNamedEvent
from ooodev.events.lo_events import Events
from ooodev.events.props_named_event import PropsNamedEvent
from ooodev.exceptions import ex as mEx
from ooodev.format.inner.common.props.prop_pair import PropPair
from ooodev.format.inner.common.props.struct_border_table_props import StructBorderTableProps
from ooodev.format.inner.direct.structs.side import Side
from ooodev.format.inner.direct.structs.struct_base import StructBase
from ooodev.format.inner.kind.format_kind import FormatKind
from ooodev.format.inner.style_base import _on_props_setting, _on_props_set
from ooodev.units.unit_convert import UnitConvert
from ooodev.units.unit_convert import UnitLength
from ooodev.units.unit_mm import UnitMM
from ooodev.utils import props as mProps
if TYPE_CHECKING:
from ooodev.units.unit_obj import UnitT
# endregion Import
_TTableBorderStruct = TypeVar("_TTableBorderStruct", bound="TableBorderStruct")
[docs]class TableBorderStruct(StructBase):
"""
Table Border struct positioning for use in styles.
Any properties starting with ``prop_`` set or get current instance values.
All methods starting with ``fmt_`` can be used to chain together Border Table properties.
"""
# region init
[docs] def __init__(
self,
*,
left: Side | None = None,
right: Side | None = None,
top: Side | None = None,
bottom: Side | None = None,
border_side: Side | None = None,
vertical: Side | None = None,
horizontal: Side | None = None,
distance: float | UnitT | None = None,
) -> None:
"""
Constructor
Args:
left (Side, optional): Determines the line style at the left edge.
right (Side, optional): Determines the line style at the right edge.
top (Side, optional): Determines the line style at the top edge.
bottom (Side, optional): Determines the line style at the bottom edge.
border_side (Side, optional): Determines the line style at the top, bottom, left, right edges. If this argument has a value then arguments ``top``, ``bottom``, ``left``, ``right`` are ignored
horizontal (Side, optional): Determines the line style of horizontal lines for the inner part of a cell range.
vertical (Side, optional): Determines the line style of vertical lines for the inner part of a cell range.
distance (float, UnitT, optional): Contains the distance between the lines and other contents (in mm units) or :ref:`proto_unit_obj`.
"""
# sourcery skip: low-code-quality
init_vals = {}
if border_side is not None:
init_vals[self._props.top.first] = border_side
if self._props.top.second:
init_vals[self._props.top.second] = True
init_vals[self._props.bottom.first] = border_side
if self._props.bottom.second:
init_vals[self._props.bottom.second] = True
init_vals[self._props.left.first] = border_side
if self._props.left.second:
init_vals[self._props.left.second] = True
init_vals[self._props.right.first] = border_side
if self._props.right.second:
init_vals[self._props.right.second] = True
else:
if top is not None:
init_vals[self._props.top.first] = top
if self._props.top.second:
init_vals[self._props.top.second] = True
if bottom is not None:
init_vals[self._props.bottom.first] = bottom
if self._props.bottom.second:
init_vals[self._props.bottom.second] = True
if left is not None:
init_vals[self._props.left.first] = left
if self._props.left.second:
init_vals[self._props.left.second] = True
if right is not None:
init_vals[self._props.right.first] = right
if self._props.right.second:
init_vals[self._props.right.second] = True
if horizontal is not None:
init_vals[self._props.horz.first] = horizontal
if self._props.horz.second:
init_vals[self._props.horz.second] = True
if vertical is not None:
init_vals[self._props.vert.first] = vertical
if self._props.vert.second:
init_vals[self._props.vert.second] = True
if distance is not None:
try:
init_vals[self._props.dist.first] = distance.get_value_mm100() # type: ignore
except AttributeError:
init_vals[self._props.dist.first] = UnitConvert.convert(
num=distance, frm=UnitLength.MM, to=UnitLength.MM100 # type: ignore
)
if self._props.dist.second:
init_vals[self._props.dist.second] = True
super().__init__(**init_vals)
# endregion init
# region methods
def _supported_services(self) -> Tuple[str, ...]:
try:
return self._supported_services_values
except AttributeError:
self._supported_services_values = ()
return self._supported_services_values
def _get_property_name(self) -> str:
try:
return self._property_name
except AttributeError:
self._property_name = "TableBorder2"
return self._property_name
# region apply()
@overload
def apply(self, obj: Any) -> None: # type: ignore
...
[docs] def apply(self, obj: Any, **kwargs) -> None:
"""
Applies Style to obj
Args:
obj (object): UNO object
:events:
.. cssclass:: lo_event
- :py:attr:`~.events.format_named_event.FormatNamedEvent.STYLE_APPLYING` :eventref:`src-docs-event-cancel`
- :py:attr:`~.events.format_named_event.FormatNamedEvent.STYLE_APPLIED` :eventref:`src-docs-event`
Raises:
PropertyNotFoundError: If ``obj`` does not have ``TableBorder2`` property.
Returns:
None:
"""
# there seems to be a bug in LibreOffice for TableBorder2.HorizontalLine
# When top or bottom line is set for some reason TableBorder2.HorizontalLine also picks it up.
#
# current work around is to get if the current TableBorder2.HorizontalLine contains any data on read.
# if it does not and the current style is not applying HorizontalLine then read back TableBorder2
# and reset its HorizontalLine to default empty values.
# the save TableBorder2 again.
# Even if HorizontalLine is present in current style that is being applied it is ignored and set to top line or bottom line values.
# The work around is to save TableBorder2 after setting other properties, read it again, set the HorizontalLine and save it again.
if not self._is_valid_obj(obj):
# will not apply on this class but may apply on child classes
self._print_not_valid_srv("apply()")
return
prop_name = self._get_property_name()
cargs = CancelEventArgs(source=f"{self.apply.__qualname__}")
cargs.event_data = self
if cargs.cancel:
return
self._events.trigger(FormatNamedEvent.STYLE_APPLYING, cargs)
if cargs.cancel:
return
events = Events(source=self)
events.on(PropsNamedEvent.PROP_SETTING, _on_props_setting)
events.on(PropsNamedEvent.PROP_SET, _on_props_set)
tb = cast(TableBorder2, mProps.Props.get(obj, prop_name, None))
if tb is None:
raise mEx.PropertyNotFoundError(prop_name, "apply() obj has no property")
attrs_props = (*(self._props[i] for i in range(6)),)
for ap in attrs_props:
val = cast(Side, self._get(ap.first))
if val is not None:
setattr(tb, ap.first, val.get_uno_struct())
if ap.second:
setattr(tb, ap.second, True)
distance = cast(int, self._get(self._props.dist.first))
if distance is not None:
tb.Distance = distance
tb.IsDistanceValid = True
mProps.Props.set(obj, **{prop_name: tb})
h_line = cast(Side, self._get(self._props.horz.first))
if h_line is None:
h_ln = tb.HorizontalLine
h_invalid = (
h_ln.InnerLineWidth == 0
and h_ln.LineDistance == 0
and h_ln.LineWidth == 0
and h_ln.OuterLineWidth == 0
)
if h_invalid:
tb = cast(TableBorder2, mProps.Props.get(obj, prop_name))
tb.HorizontalLine = Side().empty.get_uno_struct()
tb.IsHorizontalLineValid = True
mProps.Props.set(obj, **{prop_name: tb})
else:
tb = cast(TableBorder2, mProps.Props.get(obj, prop_name))
tb.HorizontalLine = h_line.get_uno_struct()
tb.IsHorizontalLineValid = True
mProps.Props.set(obj, **{prop_name: tb})
events = None
eargs = EventArgs.from_args(cargs)
self._events.trigger(FormatNamedEvent.STYLE_APPLIED, eargs)
# endregion apply()
# region from_obj()
@overload
@classmethod
def from_obj(cls: Type[_TTableBorderStruct], obj: Any) -> _TTableBorderStruct: ...
@overload
@classmethod
def from_obj(cls: Type[_TTableBorderStruct], obj: Any, **kwargs) -> _TTableBorderStruct: ...
[docs] @classmethod
def from_obj(cls: Type[_TTableBorderStruct], obj: Any, **kwargs) -> _TTableBorderStruct:
"""
Gets instance from object properties
Args:
obj (object): UNO object
Raises:
PropertyNotFoundError: If ``obj`` does not have required property.
Returns:
BorderTable: Border Table.
"""
# this nu is only used to get Property Name
nu = cls(**kwargs)
prop_name = nu._get_property_name()
tb = cast(TableBorder2, mProps.Props.get(obj, prop_name, None))
if tb is None:
raise mEx.PropertyNotFoundError(prop_name, f"from_obj() obj as no {prop_name} property")
left = Side.from_uno_struct(tb.LeftLine) if tb.IsLeftLineValid else None
top = Side.from_uno_struct(tb.TopLine) if tb.IsTopLineValid else None
right = Side.from_uno_struct(tb.RightLine) if tb.IsRightLineValid else None
bottom = Side.from_uno_struct(tb.BottomLine) if tb.IsBottomLineValid else None
if tb.IsVerticalLineValid:
vertical = Side.from_uno_struct(tb.VerticalLine)
else:
vertical = None
if tb.IsHorizontalLineValid:
horizontal = Side.from_uno_struct(tb.HorizontalLine)
else:
horizontal = None
inst = cls(left=left, right=right, top=top, bottom=bottom, vertical=vertical, horizontal=horizontal, **kwargs)
if tb.IsDistanceValid:
p = inst._props.dist
inst._set(p.first, tb.Distance)
if p.second:
inst._set(p.second, True)
return inst
# endregion from_obj()
[docs] def get_uno_struct(self) -> TableBorder2:
"""
Gets UNO ``TableBorder2`` from instance.
Returns:
TableBorder2: ``TableBorder2`` instance
"""
tb = TableBorder2()
# put attribs in a tuple
attrs = (*(self._props[i].first for i in range(6)),)
for key, val in self._dv.items():
if key in attrs:
side = cast(Side, val)
setattr(tb, key, side.get_uno_struct())
else:
setattr(tb, key, val)
return tb
[docs] def get_uno_struct_table_border(self) -> TableBorder:
"""
Gets UNO ``TableBorder`` from instance.
Returns:
TableBorder: ``TableBorder`` instance
"""
def border2_border(b2: BorderLine2) -> BorderLine:
# convert borderline2 to borderline
return BorderLine(
Color=b2.Color,
InnerLineWidth=b2.InnerLineWidth,
OuterLineWidth=b2.OuterLineWidth,
LineDistance=b2.LineDistance,
)
tb2 = TableBorder2()
# put attribs in a tuple
attrs = (*(self._props[i].first for i in range(6)),)
for key, val in self._dv.items():
if key in attrs:
side = cast(Side, val)
setattr(tb2, key, side.get_uno_struct_border_line())
else:
setattr(tb2, key, val)
return TableBorder(
TopLine=border2_border(tb2.TopLine),
IsTopLineValid=tb2.IsTopLineValid,
BottomLine=border2_border(tb2.BottomLine),
IsBottomLineValid=tb2.IsBottomLineValid,
LeftLine=border2_border(tb2.LeftLine),
IsLeftLineValid=tb2.IsLeftLineValid,
RightLine=border2_border(tb2.RightLine),
IsRightLineValid=tb2.IsRightLineValid,
HorizontalLine=border2_border(tb2.HorizontalLine),
IsHorizontalLineValid=tb2.IsHorizontalLineValid,
VerticalLine=border2_border(tb2.VerticalLine),
IsVerticalLineValid=tb2.IsVerticalLineValid,
Distance=tb2.Distance,
IsDistanceValid=tb2.IsDistanceValid,
)
# endregion methods
# region Style methods
[docs] def fmt_border_side(self: _TTableBorderStruct, value: Side | None) -> _TTableBorderStruct:
"""
Gets copy of instance with left, right, top, bottom sides set or removed
Args:
value (Side | None): Side value
Returns:
BorderTable: Border Table
"""
cp = self.copy()
cp.prop_top = value
cp.prop_bottom = value
cp.prop_left = value
cp.prop_right = value
return cp
[docs] def fmt_top(self: _TTableBorderStruct, value: Side | None) -> _TTableBorderStruct:
"""
Gets a copy of instance with top side set or removed
Args:
value (Side | None): Side value
Returns:
BorderTable: Border Table
"""
cp = self.copy()
cp.prop_top = value
return cp
[docs] def fmt_bottom(self: _TTableBorderStruct, value: Side | None) -> _TTableBorderStruct:
"""
Gets a copy of instance with bottom side set or removed
Args:
value (Side | None): Side value
Returns:
BorderTable: Border Table
"""
cp = self.copy()
cp.prop_bottom = value
return cp
[docs] def fmt_left(self: _TTableBorderStruct, value: Side | None) -> _TTableBorderStruct:
"""
Gets a copy of instance with left side set or removed
Args:
value (Side | None): Side value
Returns:
BorderTable: Border Table
"""
cp = self.copy()
cp.prop_left = value
return cp
[docs] def fmt_right(self: _TTableBorderStruct, value: Side | None) -> _TTableBorderStruct:
"""
Gets a copy of instance with right side set or removed
Args:
value (Side | None): Side value
Returns:
BorderTable: Border Table
"""
cp = self.copy()
cp.prop_right = value
return cp
[docs] def fmt_horizontal(self: _TTableBorderStruct, value: Side | None) -> _TTableBorderStruct:
"""
Gets a copy of instance with horizontal side set or removed
Args:
value (Side | None): Side value
Returns:
BorderTable: Border Table
"""
cp = self.copy()
cp.prop_horizontal = value
return cp
[docs] def fmt_vertical(self: _TTableBorderStruct, value: Side | None) -> _TTableBorderStruct:
"""
Gets a copy of instance with top vertical set or removed
Args:
value (Side | None): Side value
Returns:
BorderTable: Border Table
"""
cp = self.copy()
cp.prop_vertical = value
return cp
[docs] def fmt_distance(self: _TTableBorderStruct, value: float | UnitT | None) -> _TTableBorderStruct:
"""
Gets a copy of instance with distance set or removed
Args:
value (float | UnitT | None): Distance value in ``mm`` units or :ref:`proto_unit_obj`.
Returns:
BorderTable: Border Table
"""
cp = self.copy()
cp.prop_distance = value
return cp
# endregion Style methods
# region Properties
@property
def prop_format_kind(self) -> FormatKind:
"""Gets the kind of style"""
try:
return self._format_kind_prop
except AttributeError:
self._format_kind_prop = FormatKind.STRUCT
return self._format_kind_prop
@property
def prop_distance(self) -> UnitMM | None:
"""Gets/Sets distance value (``in mm`` units)"""
pv = cast(int, self._get(self._props.dist.first))
return None if pv is None else UnitMM.from_mm100(pv)
@prop_distance.setter
def prop_distance(self, value: float | UnitT | None) -> None:
p = self._props.dist
if value is None:
self._remove(p.first)
if p.second:
self._remove(p.second)
return
try:
self._set(p.first, value.get_value_mm100()) # type: ignore
except AttributeError:
self._set(p.first, UnitConvert.convert(num=value, frm=UnitLength.MM, to=UnitLength.MM100)) # type: ignore
if p.second:
self._set(p.second, True)
@property
def prop_left(self) -> Side | None:
"""Gets/Sets left value"""
return self._get(self._props.left.first)
@prop_left.setter
def prop_left(self, value: Side | None) -> None:
p = self._props.left
if value is None:
self._remove(p.first)
if p.second:
self._remove(p.second)
return
self._set(p.first, value)
if p.second:
self._set(p.second, True)
@property
def prop_right(self) -> Side | None:
"""Gets/Sets right value"""
return self._get(self._props.right.first)
@prop_right.setter
def prop_right(self, value: Side | None) -> None:
p = self._props.right
if value is None:
self._remove(p.first)
if p.second:
self._remove(p.second)
return
self._set(p.first, value)
if p.second:
self._set(p.second, True)
@property
def prop_top(self) -> Side | None:
"""Gets/Sets bottom value"""
return self._get(self._props.top.first)
@prop_top.setter
def prop_top(self, value: Side | None) -> None:
p = self._props.top
if value is None:
self._remove(p.first)
if p.second:
self._remove(p.second)
return
self._set(p.first, value)
if p.second:
self._set(p.second, True)
@property
def prop_bottom(self) -> Side | None:
"""Gets/Sets bottom value"""
return self._get(self._props.bottom.first)
@prop_bottom.setter
def prop_bottom(self, value: Side | None) -> None:
p = self._props.bottom
if value is None:
self._remove(p.first)
if p.second:
self._remove(p.second)
return
self._set(p.first, value)
if p.second:
self._set(p.second, True)
@property
def prop_horizontal(self) -> Side | None:
"""Gets/Sets horizontal value"""
return self._get(self._props.horz.first)
@prop_horizontal.setter
def prop_horizontal(self, value: Side | None) -> None:
p = self._props.horz
if value is None:
self._remove(p.first)
if p.second:
self._remove(p.second)
return
self._set(p.first, value)
if p.second:
self._set(p.second, True)
@property
def prop_vertical(self) -> Side | None:
"""Gets/Sets vertical value"""
return self._get(self._props.vert.first)
@prop_vertical.setter
def prop_vertical(self, value: Side | None) -> None:
p = self._props.vert
if value is None:
self._remove(p.first)
if p.second:
self._remove(p.second)
return
self._set(p.first, value)
if p.second:
self._set(p.second, True)
@property
def _props(self) -> StructBorderTableProps:
try:
return self._props_internal_attributes
except AttributeError:
self._props_internal_attributes = StructBorderTableProps(
left=PropPair("LeftLine", "IsLeftLineValid"),
top=PropPair("TopLine", "IsTopLineValid"),
right=PropPair("RightLine", "IsRightLineValid"),
bottom=PropPair("BottomLine", "IsBottomLineValid"),
horz=PropPair("HorizontalLine", "IsHorizontalLineValid"),
vert=PropPair("VerticalLine", "IsVerticalLineValid"),
dist=PropPair("Distance", "IsDistanceValid"),
)
return self._props_internal_attributes
# endregion Properties