Source code for

# region Import
from __future__ import annotations
from typing import Any, Dict, Iterable, Tuple, Type, cast, overload, TypeVar, TYPE_CHECKING
from enum import Enum

import uno
from import XPropertySet

from import TabAlign
from import TabStop

from import CancelEventArgs
from import EventArgs
from import FormatNamedEvent
from ooodev.exceptions import ex as mEx
from import StructBase
from ooodev.format.inner.kind.format_kind import FormatKind
from ooodev.loader import lo as mLo
from ooodev.units.unit_convert import UnitConvert
from ooodev.units.unit_mm import UnitMM
from ooodev.utils import props as mProps

    from ooodev.units.unit_obj import UnitT

# endregion Import

_TTabStopStruct = TypeVar(name="_TTabStopStruct", bound="TabStopStruct")

[docs]class FillCharKind(Enum): """Tab Fill Char""" NONE = " " DECIMAL = "." DASH = "-" UNDER_SCORE = "_" def __str__(self) -> str: return self.value
[docs]class TabStopStruct(StructBase): """ Paragraph Tab Any properties starting with ``prop_`` set or get current instance values. All methods starting with ``fmt_`` can be used to chain together properties. .. versionadded:: 0.9.0 """ # region init
[docs] def __init__( self, *, position: float | UnitT = 0.0, align: TabAlign = TabAlign.LEFT, decimal_char: str = ".", fill_char: FillCharKind | str = FillCharKind.NONE, ) -> None: """ Constructor Args: position (float, UnitT, optional): Specifies the position of the tabulator in relation to the left border (in ``mm`` units) or :ref:`proto_unit_obj`. Defaults to ``0.0`` align (TabAlign, optional): Specifies the alignment of the text range before the tabulator. Defaults to ``TabAlign.LEFT`` decimal_char (str, optional): Specifies which delimiter is used for the decimal. Argument is expected to be a single character string. This argument is only used when ``align`` is set to ``TabAlign.DECIMAL``. fill_char (FillCharKind, str, optional): specifies the character that is used to fill up the space between the text in the text range and the tabulators. If string value then argument is expected to be a single character string. Defaults to ``FillCharKind.NONE`` Returns: None: Note: If argument ``type`` is ``None`` then all other argument are ignored """ # # in Writer when a tab is added it is static. That is it remains when subsequent paragraphs are added. # existing paragraph can can add or remove Tabs. This will not affect the tabs of other existing paragraphs. # most likely it would be best practice not to back up and restore this property # (ParaLineSpacing) when appending paragraphs in Writer. init_vals = {"FillChar": str(fill_char)[:1], "Alignment": align, "DecimalChar": decimal_char[:1]} try: init_vals["Position"] = position.get_value_mm100() # type: ignore except AttributeError: init_vals["Position"] = UnitConvert.convert_mm_mm100(position) # type: ignore if align != TabAlign.DECIMAL: init_vals["DecimalChar"] = " " 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 = "ParaTabStops" return self._property_name
[docs] def get_attrs(self) -> Tuple[str, ...]: return (self._get_property_name(),)
# region apply() @overload def apply(self, obj: Any) -> None: ... @overload def apply(self, obj: Any, keys: Dict[str, str]) -> None: ...
[docs] def apply(self, obj: Any, **kwargs) -> None: # type: ignore """ Applies tab properties to ``obj`` If a Tab instance with the same position is existing it is updated; Otherwise, a new Tab is added. Args: obj (object): UNO object that supports ```` service. keys (Dict[str, str], optional): Property key, value items that map properties. :events: .. cssclass:: lo_event - :py:attr:`` :eventref:`src-docs-event-cancel` - :py:attr:`` :eventref:`src-docs-event` Returns: None: """ # sourcery skip: dict-assign-update-to-union 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 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 keys = {"prop": self._get_property_name()} if "keys" in kwargs: keys.update(kwargs["keys"]) key = keys["prop"] tss = cast(Tuple[TabStop, ...], mProps.Props.get(obj, key)) match = -1 # often Writer will change Position values, 800 to 801 etc. # for this reason using a range to check plus or minus 2 pos = int(self._get("Position")) pos_rng = range(pos - 2, pos + 3) # plus or minus 2 for i, ts in enumerate(tss): if pos == ts.Position: # also covers 0 match = i break if ts.Position in pos_rng: match = i break ts = self.get_uno_struct() tss_lst = list(tss) if match >= 0: tss_lst[match] = ts else: tss_lst.append(ts) self._set_obj_tabs(obj, tss_lst, key) eargs = EventArgs.from_args(cargs) self._events.trigger(FormatNamedEvent.STYLE_APPLIED, eargs)
# mProps.Props.set(obj, **{key: tuple(tss_lst)}) # endregion apply()
[docs] def get_uno_struct(self) -> TabStop: """ Gets UNO ``TabStop`` from instance. Returns: TabStop: ``TabStop`` instance """ dec = self._get("DecimalChar") if self.prop_align == TabAlign.DECIMAL else " " return TabStop( Position=self._get("Position"), Alignment=self._get("Alignment"), DecimalChar=dec, FillChar=self._get("FillChar"), )
# region from_obj() @overload @classmethod def from_obj(cls: Type[_TTabStopStruct], obj: Any) -> _TTabStopStruct: ... @overload @classmethod def from_obj(cls: Type[_TTabStopStruct], obj: Any, **kwargs) -> _TTabStopStruct: ...
[docs] @classmethod def from_obj(cls: Type[_TTabStopStruct], obj: Any, index: int = 0, **kwargs) -> _TTabStopStruct: """ Gets instance from object Args: obj (object): UNO object index (int, optional): Index of tab stop. Default ``0``. Raises: PropertyNotFoundError: If ``obj`` does not have required property Returns: Tab: ``Tab`` instance that represents ``obj`` Tab properties. """ # sourcery skip: raise-from-previous-error # this nu is only used to get Property Name # pylint: disable=protected-access # pylint: disable=raise-missing-from # pylint: disable=unsubscriptable-object nu = cls(**kwargs) prop_name = nu._get_property_name() try: tss = cast(Tuple[TabStop, ...], mProps.Props.get(obj, prop_name)) except mEx.PropertyNotFoundError: raise mEx.PropertyNotFoundError(prop_name, f"from_obj() obj as no {prop_name} property") # can expect for ts to contain at least one TabAlign ts = tss[index] return cls.from_uno_struct(ts, **kwargs)
# endregion from_obj() # region from_tab_stop() @overload @classmethod def from_uno_struct(cls: Type[_TTabStopStruct], ts: TabStop) -> _TTabStopStruct: ... @overload @classmethod def from_uno_struct(cls: Type[_TTabStopStruct], ts: TabStop, **kwargs) -> _TTabStopStruct: ...
[docs] @classmethod def from_uno_struct(cls: Type[_TTabStopStruct], ts: TabStop, **kwargs) -> _TTabStopStruct: """ Converts a Tab Stop instance to a Tab Args: ts (TabStop): Tab stop Returns: Tab: Tab set with Tab Stop properties """ # pylint: disable=protected-access inst = cls(**kwargs) inst._set("FillChar", ts.FillChar) inst._set("Alignment", ts.Alignment) inst._set("DecimalChar", ts.DecimalChar) inst._set("Position", ts.Position) return inst
# endregion from_tab_stop() def _set_obj_tabs(self, obj: Any, tabs: Iterable[TabStop], prop: str = "") -> None: if not prop: prop = self._get_property_name() prop_set = mLo.Lo.qi(XPropertySet, obj, raise_err=True) seq = uno.Any("[]", tabs) # type: ignore uno.invoke(prop_set, "setPropertyValue", (prop, seq)) # type: ignore # endregion methods # region dunder methods def __eq__(self, oth: object) -> bool: if isinstance(oth, TabStopStruct): return ( self._get("Position") == oth._get("Position") and self._get("Alignment") == oth._get("Alignment") and self._get("DecimalChar") == oth._get("DecimalChar") and self._get("FillChar") == oth._get("FillChar") ) if hasattr(oth, "typeName") and getattr(oth, "typeName") == "": ts = cast(TabStop, oth) return ( self._get("Position") == ts.Position and self._get("Alignment") == ts.Alignment and self._get("DecimalChar") == ts.DecimalChar and self._get("FillChar") == ts.FillChar ) return NotImplemented # endregion dunder methods # region format methods
[docs] def fmt_position(self: _TTabStopStruct, value: float | UnitT) -> _TTabStopStruct: """ Gets a copy of instance with position set. Args: value (float, UnitT): Position value (in ``mm`` units) or :ref:`proto_unit_obj`. Returns: Tab: Tab instance """ cp = self.copy() cp.prop_position = value return cp
[docs] def fmt_align(self: _TTabStopStruct, value: TabAlign) -> _TTabStopStruct: """ Gets a copy of instance with align set. Args: value (float): Align value. Returns: Tab: Tab instance """ cp = self.copy() cp.prop_align = value return cp
[docs] def fmt_decimal_char(self: _TTabStopStruct, value: str) -> _TTabStopStruct: """ Gets a copy of instance with decimal char set. Args: value (float): Decimal char value. Returns: Tab: Tab instance """ cp = self.copy() cp.prop_decimal_char = value return cp
[docs] def fmt_fill_char(self: _TTabStopStruct, value: FillCharKind | str) -> _TTabStopStruct: """ Gets a copy of instance with fill char set. Args: value (float): Fill char value. Returns: Tab: Tab instance """ cp = self.copy() cp.prop_fill_char = value return cp
# endregion format 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.PARA | FormatKind.STATIC return self._format_kind_prop @property def prop_position(self) -> UnitMM: """Gets/Sets the position of the tabulator in relation to the left border (in ``mm`` units).""" return UnitMM.from_mm100(self._get("Position")) @prop_position.setter def prop_position(self, value: float | UnitT) -> None: try: self._set("Position", value.get_value_mm100()) # type: ignore except AttributeError: self._set("Position", UnitConvert.convert_mm_mm100(value)) # type: ignore @property def prop_align(self) -> TabAlign: """Gets/Sets the alignment of the text range before the tabulator""" return self._get("Alignment") @prop_align.setter def prop_align(self, value: TabAlign) -> None: self._set("Alignment", value) @property def prop_decimal_char(self) -> str: """Gets/Sets which delimiter is used for the decimal.""" return self._get("DecimalChar") @prop_decimal_char.setter def prop_decimal_char(self, value: str) -> None: # tab align is not decimal then get_tab_stop() will replace # DecimalChar with " " # so there is no concern about letting any value being set here. self._set("DecimalChar", value[:1]) @property def prop_fill_char(self) -> str: """Gets/Sets which delimiter is used for the decimal.""" return self._get("FillChar") @prop_fill_char.setter def prop_fill_char(self, value: FillCharKind | str) -> None: self._set("FillChar", str(value)[:1])
# endregion properties