Source code for ooodev.format.inner.direct.structs.tab_stop_struct

# 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 com.sun.star.beans import XPropertySet

from ooo.dyn.style.tab_align import TabAlign
from ooo.dyn.style.tab_stop import TabStop

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.exceptions import ex as mEx
from ooodev.format.inner.direct.structs.struct_base 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

if TYPE_CHECKING:
    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 """ # https://api.libreoffice.org/docs/idl/ref/servicecom_1_1sun_1_1star_1_1style_1_1ParagraphProperties-members.html # 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 ``com.sun.star.style.ParagraphProperties`` service. keys (Dict[str, str], optional): Property key, value items that map properties. :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` 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("[]com.sun.star.style.TabStop", 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") == "com.sun.star.style.TabStop": 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