comparison 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
comparison
equal deleted inserted replaced
976:68faf7d77a42 977:d2e612a45e76
188 @main_cont.setter 188 @main_cont.setter
189 def main_cont(self, value): 189 def main_cont(self, value):
190 if self._main_cont is not None: 190 if self._main_cont is not None:
191 raise ValueError(_("XMLUI can have only one main container")) 191 raise ValueError(_("XMLUI can have only one main container"))
192 self._main_cont = value 192 self._main_cont = value
193
194 193
195 def _parseChilds(self, parent, current_node, wanted = ('container',), data = None): 194 def _parseChilds(self, parent, current_node, wanted = ('container',), data = None):
196 """ Recursively parse childNodes of an elemen 195 """ Recursively parse childNodes of an elemen
197 @param parent: widget container with '_xmluiAppend' method 196 @param parent: widget container with '_xmluiAppend' method
198 @param current_node: element from which childs will be parsed 197 @param current_node: element from which childs will be parsed
313 ctrl._xmlui_param_id = (callback_id, [field.getAttribute('name') for field in node.getElementsByTagName("field_back")]) 312 ctrl._xmlui_param_id = (callback_id, [field.getAttribute('name') for field in node.getElementsByTagName("field_back")])
314 else: 313 else:
315 print(_("FIXME FIXME FIXME: widget type [%s] is not implemented") % type_) 314 print(_("FIXME FIXME FIXME: widget type [%s] is not implemented") % type_)
316 raise NotImplementedError(_("FIXME FIXME FIXME: type [%s] is not implemented") % type_) 315 raise NotImplementedError(_("FIXME FIXME FIXME: type [%s] is not implemented") % type_)
317 316
318 if self.type == 'param' and type_ != 'text': 317 if self.type == 'param' and type_ not in ('text', 'button'):
319 try: 318 try:
320 ctrl._xmluiOnChange(self.onParamChange) 319 ctrl._xmluiOnChange(self.onParamChange)
321 ctrl._param_category = self._current_category 320 ctrl._param_category = self._current_category
322 except (AttributeError, TypeError): # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError 321 except (AttributeError, TypeError): # XXX: TypeError is here because pyjamas raise a TypeError instead of an AttributeError
323 if not isinstance(ctrl, (EmptyWidget, TextWidget, LabelWidget, JidWidget)): 322 if not isinstance(ctrl, (EmptyWidget, TextWidget, LabelWidget, JidWidget)):
324 print(_("No change listener on [%s]") % ctrl) 323 print(_("No change listener on [%s]") % ctrl)
324
325 if type_ != 'text':
326 callback = node.getAttribute("internal_callback") or None
327 if callback:
328 fields = [field.getAttribute('name') for field in node.getElementsByTagName("internal_field")]
329 data = self.getInternalCallbackData(callback, node)
330 ctrl._xmlui_param_internal = (callback, fields, data)
331 if type_ == 'button':
332 ctrl._xmluiOnClick(self.onChangeInternal)
333 else:
334 ctrl._xmluiOnChange(self.onChangeInternal)
325 335
326 ctrl._xmlui_name = name 336 ctrl._xmlui_name = name
327 parent._xmluiAppend(ctrl) 337 parent._xmluiAppend(ctrl)
328 338
329 else: 339 else:
401 Launch the action associated to the button 411 Launch the action associated to the button
402 @param button: the button clicked 412 @param button: the button clicked
403 413
404 """ 414 """
405 callback_id, fields = button._xmlui_param_id 415 callback_id, fields = button._xmlui_param_id
416 if not callback_id: # the button is probably bound to an internal action
417 return
406 data = {} 418 data = {}
407 for field in fields: 419 for field in fields:
408 escaped = self.escape(field) 420 escaped = self.escape(field)
409 ctrl = self.ctrl_list[field] 421 ctrl = self.ctrl_list[field]
410 if isinstance(ctrl['control'], ListWidget): 422 if isinstance(ctrl['control'], ListWidget):
411 data[escaped] = u'\t'.join(ctrl['control']._xmluiGetSelectedValues()) 423 data[escaped] = u'\t'.join(ctrl['control']._xmluiGetSelectedValues())
412 else: 424 else:
413 data[escaped] = ctrl['control']._xmluiGetValue() 425 data[escaped] = ctrl['control']._xmluiGetValue()
414 self._xmluiLaunchAction(callback_id, data) 426 self._xmluiLaunchAction(callback_id, data)
427
428 def onChangeInternal(self, ctrl):
429 """ Called when a widget that has been bound to an internal callback is changed.
430
431 This is used to perform some UI actions without communicating with the backend.
432 See sat.tools.xml_tools.Widget.setInternalCallback for more details.
433 @param ctrl: widget modified
434 """
435 action, fields, data = ctrl._xmlui_param_internal
436 if action not in ('copy', 'move', 'groups_of_contact'):
437 raise NotImplementedError(_("FIXME: XMLUI internal action [%s] is not implemented") % action)
438
439 def copy_move(source, target):
440 """Depending of 'action' value, copy or move from source to target."""
441 if isinstance(target, ListWidget):
442 if isinstance(source, ListWidget):
443 values = source._xmluiGetSelectedValues()
444 else:
445 values = [source._xmluiGetValue()]
446 if action == 'move':
447 source._xmluiSetValue('')
448 values = [value for value in values if value]
449 if values:
450 target._xmluiAddValues(values, select=True)
451 else:
452 if isinstance(source, ListWidget):
453 value = u', '.join(source._xmluiGetSelectedValues())
454 else:
455 value = source._xmluiGetValue()
456 if action == 'move':
457 source._xmluiSetValue('')
458 target._xmluiSetValue(value)
459
460 def groups_of_contact(source, target):
461 """Select in target the groups of the contact which is selected in source."""
462 assert(isinstance(source, ListWidget))
463 assert(isinstance(target, ListWidget))
464 try:
465 contact_jid_s = source._xmluiGetSelectedValues()[0]
466 except IndexError:
467 return
468 target._xmluiSelectValues(data[contact_jid_s])
469 pass
470
471 source = None
472 for field in fields:
473 widget = self.ctrl_list[field]['control']
474 if not source:
475 source = widget
476 continue
477 if action in ('copy', 'move'):
478 copy_move(source, widget)
479 elif action == 'groups_of_contact':
480 groups_of_contact(source, widget)
481 source = None
482
483 def getInternalCallbackData(self, action, node):
484 """Retrieve from node the data needed to perform given action.
485
486 TODO: it would be better to not have a specific way to retrieve
487 data for each action, but instead to have a generic method to
488 extract any kind of data structure from the 'internal_data' element.
489
490 @param action (string): a value from the one that can be passed to the
491 'callback' parameter of sat.tools.xml_tools.Widget.setInternalCallback
492 @param node (DOM Element): the node of the widget that triggers the callback
493 """
494 try: # data is stored in the first 'internal_data' element of the node
495 data_elts = node.getElementsByTagName('internal_data')[0].childNodes
496 except IndexError:
497 return None
498 data = {}
499 if action == 'groups_of_contact': # return a dict(key: string, value: list[string])
500 for elt in data_elts:
501 jid_s = elt.getAttribute('name')
502 data[jid_s] = []
503 for value_elt in elt.childNodes:
504 data[jid_s].append(value_elt.getAttribute('name'))
505 return data
415 506
416 def onFormSubmitted(self, ignore=None): 507 def onFormSubmitted(self, ignore=None):
417 """ An XMLUI form has been submited 508 """ An XMLUI form has been submited
418 call the submit action associated with this form 509 call the submit action associated with this form
419 510