diff libervia/backend/tools/common/template_xmlui.py @ 4071:4b842c1fb686

refactoring: renamed `sat` package to `libervia.backend`
author Goffi <goffi@goffi.org>
date Fri, 02 Jun 2023 11:49:51 +0200
parents sat/tools/common/template_xmlui.py@524856bd7b19
children 26b7ed2817da
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libervia/backend/tools/common/template_xmlui.py	Fri Jun 02 11:49:51 2023 +0200
@@ -0,0 +1,243 @@
+#!/usr/bin/env python3
+
+
+# SAT: a jabber client
+# Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org)
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+
+# You should have received a copy of the GNU Affero General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+""" template XMLUI parsing
+
+XMLUI classes from this modules can then be iterated to create the template
+"""
+
+from functools import partial
+from libervia.backend.core.log import getLogger
+from sat_frontends.tools import xmlui
+from sat_frontends.tools import jid
+try:
+    from jinja2 import Markup as safe
+except ImportError:
+    # Safe marks XHTML values as usable as it.
+    # If jinja2 is not there, we can use a simple lamba
+    safe = lambda x: x
+
+
+log = getLogger(__name__)
+
+
+## Widgets ##
+
+
+class Widget(object):
+    category = "widget"
+    type = None
+    enabled = True
+    read_only = True
+
+    def __init__(self, xmlui_parent):
+        self.xmlui_parent = xmlui_parent
+
+    @property
+    def name(self):
+        return self._xmlui_name
+
+
+class ValueWidget(Widget):
+    def __init__(self, xmlui_parent, value):
+        super(ValueWidget, self).__init__(xmlui_parent)
+        self.value = value
+
+    @property
+    def values(self):
+        return [self.value]
+
+    @property
+    def labels(self):
+        #  helper property, there is not label for ValueWidget
+        # but using labels make rendering more easy (one single method to call)
+        #  values are actually returned
+        return [self.value]
+
+
+class InputWidget(ValueWidget):
+    def __init__(self, xmlui_parent, value, read_only=False):
+        super(InputWidget, self).__init__(xmlui_parent, value)
+        self.read_only = read_only
+
+
+class OptionsWidget(Widget):
+    def __init__(self, xmlui_parent, options, selected, style):
+        super(OptionsWidget, self).__init__(xmlui_parent)
+        self.options = options
+        self.selected = selected
+        self.style = style
+
+    @property
+    def values(self):
+        for value, label in self.items:
+            yield value
+
+    @property
+    def value(self):
+        if self.multi or self.no_select or len(self.selected) != 1:
+            raise ValueError(
+                "Can't get value for list with multiple selections or nothing selected"
+            )
+        return self.selected[0]
+
+    @property
+    def labels(self):
+        """return only labels from self.items"""
+        for value, label in self.items:
+            yield label
+
+    @property
+    def items(self):
+        """return suitable items, according to style"""
+        no_select = self.no_select
+        for value, label in self.options:
+            if no_select or value in self.selected:
+                yield value, label
+
+    @property
+    def inline(self):
+        return "inline" in self.style
+
+    @property
+    def no_select(self):
+        return "noselect" in self.style
+
+    @property
+    def multi(self):
+        return "multi" in self.style
+
+
+class EmptyWidget(xmlui.EmptyWidget, Widget):
+    def __init__(self, _xmlui_parent):
+        Widget.__init__(self)
+
+
+class TextWidget(xmlui.TextWidget, ValueWidget):
+    type = "text"
+
+
+class JidWidget(xmlui.JidWidget, ValueWidget):
+    type = "jid"
+
+    def __init__(self, xmlui_parent, value):
+        self.value = jid.JID(value)
+
+
+class LabelWidget(xmlui.LabelWidget, ValueWidget):
+    type = "label"
+
+    @property
+    def for_name(self):
+        try:
+            return self._xmlui_for_name
+        except AttributeError:
+            return None
+
+
+class StringWidget(xmlui.StringWidget, InputWidget):
+    type = "string"
+
+
+class JidInputWidget(xmlui.JidInputWidget, StringWidget):
+    type = "jid"
+
+class TextBoxWidget(xmlui.TextWidget, InputWidget):
+    type = "textbox"
+
+
+class XHTMLBoxWidget(xmlui.XHTMLBoxWidget, InputWidget):
+    type = "xhtmlbox"
+
+    def __init__(self, xmlui_parent, value, read_only=False):
+        # XXX: XHTMLBoxWidget value must be cleaned (harmful XHTML must be removed)
+        #      This is normally done in the backend, the frontends should not need to
+        #      worry about it.
+        super(XHTMLBoxWidget, self).__init__(
+            xmlui_parent=xmlui_parent, value=safe(value), read_only=read_only)
+
+
+class ListWidget(xmlui.ListWidget, OptionsWidget):
+    type = "list"
+
+
+## Containers ##
+
+
+class Container(object):
+    category = "container"
+    type = None
+
+    def __init__(self, xmlui_parent):
+        self.xmlui_parent = xmlui_parent
+        self.children = []
+
+    def __iter__(self):
+        return iter(self.children)
+
+    def _xmlui_append(self, widget):
+        self.children.append(widget)
+
+    def _xmlui_remove(self, widget):
+        self.children.remove(widget)
+
+
+class VerticalContainer(xmlui.VerticalContainer, Container):
+    type = "vertical"
+
+
+class PairsContainer(xmlui.PairsContainer, Container):
+    type = "pairs"
+
+
+class LabelContainer(xmlui.PairsContainer, Container):
+    type = "label"
+
+
+## Factory ##
+
+
+class WidgetFactory(object):
+
+    def __getattr__(self, attr):
+        if attr.startswith("create"):
+            cls = globals()[attr[6:]]
+            return cls
+
+
+## Core ##
+
+
+class XMLUIPanel(xmlui.XMLUIPanel):
+    widget_factory = WidgetFactory()
+
+    def show(self, *args, **kwargs):
+        raise NotImplementedError
+
+
+class XMLUIDialog(xmlui.XMLUIDialog):
+    dialog_factory = WidgetFactory()
+
+    def __init__(*args, **kwargs):
+        raise NotImplementedError
+
+
+create = partial(xmlui.create, class_map={
+    xmlui.CLASS_PANEL: XMLUIPanel,
+    xmlui.CLASS_DIALOG: XMLUIDialog})