diff frontends/src/tools/xmlui.py @ 977:d2e612a45e76

tools, frontends (xmlui): add Widget.setInternalCallback: - to add UI callback not communication with the bridge - a list of predefined actions can be used to copy, move, select values - extra data can be passed within the 'internal_data' element TODO: frontend side operation to retrieve the data from node to data structure should be generic
author souliane <souliane@mailoo.org>
date Thu, 03 Apr 2014 14:56:16 +0200
parents b37b1d183ac3
children 7a39ae3950f7
line wrap: on
line diff
--- a/frontends/src/tools/xmlui.py	Thu Apr 03 14:49:05 2014 +0200
+++ b/frontends/src/tools/xmlui.py	Thu Apr 03 14:56:16 2014 +0200
@@ -191,7 +191,6 @@
             raise ValueError(_("XMLUI can have only one main container"))
         self._main_cont = value
 
-
     def _parseChilds(self, parent, current_node, wanted = ('container',), data = None):
         """ Recursively parse childNodes of an elemen
         @param parent: widget container with '_xmluiAppend' method
@@ -315,14 +314,25 @@
                     print(_("FIXME FIXME FIXME: widget type [%s] is not implemented") % type_)
                     raise NotImplementedError(_("FIXME FIXME FIXME: type [%s] is not implemented") % type_)
 
-                if self.type == 'param' and type_ != 'text':
+                if self.type == 'param' and type_ not in ('text', 'button'):
                     try:
                         ctrl._xmluiOnChange(self.onParamChange)
                         ctrl._param_category = self._current_category
-                    except (AttributeError, TypeError): # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError
+                    except (AttributeError, TypeError):  # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError
                         if not isinstance(ctrl, (EmptyWidget, TextWidget, LabelWidget, JidWidget)):
                             print(_("No change listener on [%s]") % ctrl)
 
+                if type_ != 'text':
+                    callback = node.getAttribute("internal_callback") or None
+                    if callback:
+                        fields = [field.getAttribute('name') for field in node.getElementsByTagName("internal_field")]
+                        data = self.getInternalCallbackData(callback, node)
+                        ctrl._xmlui_param_internal = (callback, fields, data)
+                        if type_ == 'button':
+                            ctrl._xmluiOnClick(self.onChangeInternal)
+                        else:
+                            ctrl._xmluiOnChange(self.onChangeInternal)
+
                 ctrl._xmlui_name = name
                 parent._xmluiAppend(ctrl)
 
@@ -403,6 +413,8 @@
 
         """
         callback_id, fields = button._xmlui_param_id
+        if not callback_id:  # the button is probably bound to an internal action
+            return
         data = {}
         for field in fields:
             escaped = self.escape(field)
@@ -413,6 +425,85 @@
                 data[escaped] = ctrl['control']._xmluiGetValue()
         self._xmluiLaunchAction(callback_id, data)
 
+    def onChangeInternal(self, ctrl):
+        """ Called when a widget that has been bound to an internal callback is changed.
+
+        This is used to perform some UI actions without communicating with the backend.
+        See sat.tools.xml_tools.Widget.setInternalCallback for more details.
+        @param ctrl: widget modified
+        """
+        action, fields, data = ctrl._xmlui_param_internal
+        if action not in ('copy', 'move', 'groups_of_contact'):
+            raise NotImplementedError(_("FIXME: XMLUI internal action [%s] is not implemented") % action)
+
+        def copy_move(source, target):
+            """Depending of 'action' value, copy or move from source to target."""
+            if isinstance(target, ListWidget):
+                if isinstance(source, ListWidget):
+                    values = source._xmluiGetSelectedValues()
+                else:
+                    values = [source._xmluiGetValue()]
+                    if action == 'move':
+                        source._xmluiSetValue('')
+                values = [value for value in values if value]
+                if values:
+                    target._xmluiAddValues(values, select=True)
+            else:
+                if isinstance(source, ListWidget):
+                    value = u', '.join(source._xmluiGetSelectedValues())
+                else:
+                    value = source._xmluiGetValue()
+                    if action == 'move':
+                        source._xmluiSetValue('')
+                target._xmluiSetValue(value)
+
+        def groups_of_contact(source, target):
+            """Select in target the groups of the contact which is selected in source."""
+            assert(isinstance(source, ListWidget))
+            assert(isinstance(target, ListWidget))
+            try:
+                contact_jid_s = source._xmluiGetSelectedValues()[0]
+            except IndexError:
+                return
+            target._xmluiSelectValues(data[contact_jid_s])
+            pass
+
+        source = None
+        for field in fields:
+            widget = self.ctrl_list[field]['control']
+            if not source:
+                source = widget
+                continue
+            if action in ('copy', 'move'):
+                copy_move(source, widget)
+            elif action == 'groups_of_contact':
+                groups_of_contact(source, widget)
+            source = None
+
+    def getInternalCallbackData(self, action, node):
+        """Retrieve from node the data needed to perform given action.
+
+        TODO: it would be better to not have a specific way to retrieve
+        data for each action, but instead to have a generic method to
+        extract any kind of data structure from the 'internal_data' element.
+
+        @param action (string): a value from the one that can be passed to the
+            'callback' parameter of sat.tools.xml_tools.Widget.setInternalCallback
+        @param node (DOM Element): the node of the widget that triggers the callback
+        """
+        try:  # data is stored in the first 'internal_data' element of the node
+            data_elts = node.getElementsByTagName('internal_data')[0].childNodes
+        except IndexError:
+            return None
+        data = {}
+        if action == 'groups_of_contact':  # return a dict(key: string, value: list[string])
+            for elt in data_elts:
+                jid_s = elt.getAttribute('name')
+                data[jid_s] = []
+                for value_elt in elt.childNodes:
+                    data[jid_s].append(value_elt.getAttribute('name'))
+        return data
+
     def onFormSubmitted(self, ignore=None):
         """ An XMLUI form has been submited
         call the submit action associated with this form