Ad-Hoc commands window now uses new dataform wrapper. /me dances
This commit is contained in:
parent
8c9711d7ea
commit
c75bdcb63c
|
@ -208,7 +208,7 @@
|
||||||
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<widget class="GtkVBox" id="vbox111">
|
<widget class="GtkVBox" id="container_vbox">
|
||||||
<property name="border_width">5</property>
|
<property name="border_width">5</property>
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
<property name="homogeneous">False</property>
|
<property name="homogeneous">False</property>
|
||||||
|
@ -251,19 +251,7 @@
|
||||||
</child>
|
</child>
|
||||||
|
|
||||||
<child>
|
<child>
|
||||||
<widget class="GtkTable" id="form_table">
|
<placeholder/>
|
||||||
<property name="visible">True</property>
|
|
||||||
<property name="n_rows">1</property>
|
|
||||||
<property name="n_columns">3</property>
|
|
||||||
<property name="homogeneous">False</property>
|
|
||||||
<property name="row_spacing">0</property>
|
|
||||||
<property name="column_spacing">0</property>
|
|
||||||
</widget>
|
|
||||||
<packing>
|
|
||||||
<property name="padding">0</property>
|
|
||||||
<property name="expand">True</property>
|
|
||||||
<property name="fill">True</property>
|
|
||||||
</packing>
|
|
||||||
</child>
|
</child>
|
||||||
</widget>
|
</widget>
|
||||||
</child>
|
</child>
|
||||||
|
|
|
@ -26,11 +26,10 @@
|
||||||
import gobject
|
import gobject
|
||||||
import gtk
|
import gtk
|
||||||
|
|
||||||
import common.xmpp as xmpp
|
from common import xmpp, gajim, dataforms
|
||||||
import common.gajim as gajim
|
|
||||||
|
|
||||||
import gtkgui_helpers
|
import gtkgui_helpers
|
||||||
import dataforms
|
import dataforms as dataformwidget
|
||||||
|
|
||||||
class CommandWindow:
|
class CommandWindow:
|
||||||
'''Class for a window for single ad-hoc commands session. Note, that
|
'''Class for a window for single ad-hoc commands session. Note, that
|
||||||
|
@ -38,7 +37,9 @@ class CommandWindow:
|
||||||
|
|
||||||
TODO: maybe put this window into MessageWindow? consider this when
|
TODO: maybe put this window into MessageWindow? consider this when
|
||||||
TODO: it will be possible to manage more than one window of one
|
TODO: it will be possible to manage more than one window of one
|
||||||
TODO: account/jid pair in MessageWindowMgr.'''
|
TODO: account/jid pair in MessageWindowMgr.
|
||||||
|
|
||||||
|
TODO: gtk 2.10 has a special wizard-widget, consider using it...'''
|
||||||
|
|
||||||
def __init__(self, account, jid):
|
def __init__(self, account, jid):
|
||||||
'''Create new window.'''
|
'''Create new window.'''
|
||||||
|
@ -68,7 +69,8 @@ class CommandWindow:
|
||||||
self.__dict__[name] = self.xml.get_widget(name)
|
self.__dict__[name] = self.xml.get_widget(name)
|
||||||
|
|
||||||
# creating data forms widget
|
# creating data forms widget
|
||||||
self.data_form_widget = dataforms.DataFormWidget()
|
self.data_form_widget = dataformwidget.DataFormWidget()
|
||||||
|
self.data_form_widget.show()
|
||||||
self.sending_form_stage_vbox.pack_start(self.data_form_widget)
|
self.sending_form_stage_vbox.pack_start(self.data_form_widget)
|
||||||
|
|
||||||
# setting initial stage
|
# setting initial stage
|
||||||
|
@ -264,13 +266,14 @@ class CommandWindow:
|
||||||
self.remove_pulsing()
|
self.remove_pulsing()
|
||||||
self.sending_form_progressbar.hide()
|
self.sending_form_progressbar.hide()
|
||||||
|
|
||||||
self.sessionid = command.getAttr('sessionid')
|
if self.sessionid is None:
|
||||||
|
self.sessionid = command.getAttr('sessionid')
|
||||||
|
|
||||||
self.dataform = xmpp.DataForm(node=command.getTag('x'))
|
self.dataform = dataforms.DataForm(node=command.getTag('x'))
|
||||||
|
|
||||||
self.data_form_widget.show()
|
|
||||||
self.data_form_widget.set_sensitive(True)
|
self.data_form_widget.set_sensitive(True)
|
||||||
self.data_form_widget.set_data_form(self.dataform)
|
self.data_form_widget.set_data_form(self.dataform)
|
||||||
|
self.data_form_widget.show()
|
||||||
|
|
||||||
action = command.getTag('action')
|
action = command.getTag('action')
|
||||||
if action is None:
|
if action is None:
|
||||||
|
@ -367,6 +370,7 @@ class CommandWindow:
|
||||||
|
|
||||||
def callback(response):
|
def callback(response):
|
||||||
'''Called on response to query.'''
|
'''Called on response to query.'''
|
||||||
|
# TODO: move to connection_handlers.py
|
||||||
# is error => error stage
|
# is error => error stage
|
||||||
error = response.getError()
|
error = response.getError()
|
||||||
if error is not None:
|
if error is not None:
|
||||||
|
@ -405,10 +409,11 @@ class CommandWindow:
|
||||||
cmdnode.setAttr('sessionid', self.sessionid)
|
cmdnode.setAttr('sessionid', self.sessionid)
|
||||||
|
|
||||||
if self.data_form_widget.data_form is not None:
|
if self.data_form_widget.data_form is not None:
|
||||||
cmdnode.addChild(node=self.data_form_widget.filled_data_form())
|
cmdnode.addChild(node=dataforms.DataForm(tofill=self.data_form_widget.data_form))
|
||||||
|
|
||||||
def callback(response):
|
def callback(response):
|
||||||
# TODO: error handling
|
# TODO: error handling
|
||||||
|
# TODO: move to connection_handlers.py
|
||||||
self.stage3_next_form(response.getTag('command'))
|
self.stage3_next_form(response.getTag('command'))
|
||||||
|
|
||||||
print stanza
|
print stanza
|
||||||
|
|
|
@ -37,7 +37,10 @@ def del_multiple_tag_value(node, childname):
|
||||||
childname in node.
|
childname in node.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
while node.delChild(childname):
|
try:
|
||||||
|
while node.delChild(childname):
|
||||||
|
pass
|
||||||
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def iter_elements(node, childname):
|
def iter_elements(node, childname):
|
||||||
|
@ -65,7 +68,7 @@ class DataForm(xmpp.Node, object):
|
||||||
assert (isinstance(tofill, DataForm) or tofill is None)
|
assert (isinstance(tofill, DataForm) or tofill is None)
|
||||||
assert not (node is not None and tofill is not None)
|
assert not (node is not None and tofill is not None)
|
||||||
|
|
||||||
assert typ in (None, 'form', 'submit', 'cancel', 'return')
|
assert typ in (None, 'form', 'submit', 'cancel', 'result')
|
||||||
assert isinstance(title, basestring) or title is None
|
assert isinstance(title, basestring) or title is None
|
||||||
assert isinstance(instructions, basestring) or instructions is None
|
assert isinstance(instructions, basestring) or instructions is None
|
||||||
assert (fields is None or fields.__iter__)
|
assert (fields is None or fields.__iter__)
|
||||||
|
@ -94,11 +97,16 @@ class DataForm(xmpp.Node, object):
|
||||||
self._mode = DATAFORM_SINGLE
|
self._mode = DATAFORM_SINGLE
|
||||||
|
|
||||||
# change every <field/> to DataField object
|
# change every <field/> to DataField object
|
||||||
|
toadd=[]
|
||||||
for field in self.getChildren():
|
for field in self.getChildren():
|
||||||
if not isinstance(field, xmpp.Node): continue
|
if not isinstance(field, xmpp.Node): continue
|
||||||
if not field.getName()=='field': continue
|
if not field.getName()=='field': continue
|
||||||
self.delChild(field)
|
toadd.append(DataField(node=field))
|
||||||
self.addChild(node=DataField(node=field)
|
|
||||||
|
del_multiple_tag_value(self, 'field')
|
||||||
|
|
||||||
|
for field in toadd:
|
||||||
|
self.addChild(node=field)
|
||||||
else: # neither tofill nor node has a Node
|
else: # neither tofill nor node has a Node
|
||||||
if typ is None: typ='result'
|
if typ is None: typ='result'
|
||||||
if records is not None and len(records)>1:
|
if records is not None and len(records)>1:
|
||||||
|
@ -133,7 +141,10 @@ class DataForm(xmpp.Node, object):
|
||||||
self.setTagData('title')
|
self.setTagData('title')
|
||||||
|
|
||||||
def del_title(self):
|
def del_title(self):
|
||||||
self.delChild('title')
|
try:
|
||||||
|
self.delChild('title')
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
title = property(get_title, set_title, del_title,
|
title = property(get_title, set_title, del_title,
|
||||||
"Form title, in unicode, from <title/> element.")
|
"Form title, in unicode, from <title/> element.")
|
||||||
|
@ -168,22 +179,22 @@ class DataForm(xmpp.Node, object):
|
||||||
|
|
||||||
def iter_records(self):
|
def iter_records(self):
|
||||||
if self.mode is DATAFORM_SINGLE:
|
if self.mode is DATAFORM_SINGLE:
|
||||||
yield DataRecord(self)
|
yield DataRecord(node=self)
|
||||||
else:
|
else:
|
||||||
for item in self.getChildren():
|
for item in self.getChildren():
|
||||||
if not isinstance(item, xmpp.Node): continue
|
if not isinstance(item, xmpp.Node): continue
|
||||||
if not item.getName()=='item': continue
|
if not item.getName()=='item': continue
|
||||||
yield DataRecord(item)
|
yield DataRecord(node=item)
|
||||||
|
|
||||||
def get_records(self):
|
def get_records(self):
|
||||||
if self.mode is DATAFORM_SINGLE:
|
if self.mode is DATAFORM_SINGLE:
|
||||||
return [DataRecord(self),]
|
return [DataRecord(node=self),]
|
||||||
else:
|
else:
|
||||||
items = []
|
items = []
|
||||||
for node in self.getChildren():
|
for node in self.getChildren():
|
||||||
if not isinstance(node, xmpp.Node): continue
|
if not isinstance(node, xmpp.Node): continue
|
||||||
if not node.getName()=='item': continue
|
if not node.getName()=='item': continue
|
||||||
items.append(DataRecord(node))
|
items.append(DataRecord(node=node))
|
||||||
return items
|
return items
|
||||||
|
|
||||||
def set_records(self, records):
|
def set_records(self, records):
|
||||||
|
@ -191,9 +202,9 @@ class DataForm(xmpp.Node, object):
|
||||||
assert len(records)==1
|
assert len(records)==1
|
||||||
|
|
||||||
record = records[0]
|
record = records[0]
|
||||||
assert isinstance(record, dict)
|
assert isinstance(record, DataRecord)
|
||||||
for name, value in record.iteritems():
|
for field in record.iter_fields():
|
||||||
self[name]=value
|
self[field.var]=field.value
|
||||||
else:
|
else:
|
||||||
self.del_records(self)
|
self.del_records(self)
|
||||||
for record in records:
|
for record in records:
|
||||||
|
@ -229,19 +240,48 @@ class DataForm(xmpp.Node, object):
|
||||||
self.addChild(node=field)
|
self.addChild(node=field)
|
||||||
else:
|
else:
|
||||||
assert len(self.records)==0
|
assert len(self.records)==0
|
||||||
self.delChild('recorded')
|
try:
|
||||||
|
self.delChild('recorded')
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
self.addChild('recorded', None, fields)
|
self.addChild('recorded', None, fields)
|
||||||
|
|
||||||
def del_fields(self):
|
def del_fields(self):
|
||||||
if self.mode is DATAFORM_SINGLE:
|
if self.mode is DATAFORM_SINGLE:
|
||||||
del_multiple_tag_value(self, "field")
|
del_multiple_tag_value(self, "field")
|
||||||
else:
|
else:
|
||||||
self.delChild('recorded')
|
try:
|
||||||
|
self.delChild('recorded')
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def iter_fields(self):
|
||||||
|
if self.mode is DATAFORM_SINGLE:
|
||||||
|
container = self
|
||||||
|
else:
|
||||||
|
container = self.getTag("recorded")
|
||||||
|
|
||||||
|
for child in container.getChildren():
|
||||||
|
if isinstance(child, DataField):
|
||||||
|
yield child
|
||||||
|
|
||||||
fields = property(get_fields, set_fields, del_fields,
|
fields = property(get_fields, set_fields, del_fields,
|
||||||
"""Fields in this form; a list; if in DATAFORM_SINGLE mode, you should not
|
"""Fields in this form; a list; if in DATAFORM_SINGLE mode, you should not
|
||||||
set their values directly.""")
|
set their values directly.""")
|
||||||
|
|
||||||
|
def __getitem__(self, var):
|
||||||
|
for field in self.iter_fields():
|
||||||
|
if field.var==var:
|
||||||
|
return field.value
|
||||||
|
raise KeyError, "This form does not contain %r field." % var
|
||||||
|
|
||||||
|
def __setitem__(self, var, value):
|
||||||
|
for field in self.iter_fields():
|
||||||
|
if field.var==var:
|
||||||
|
field.value=value
|
||||||
|
return
|
||||||
|
raise KeyError, "This form does not contain %r field." % var
|
||||||
|
|
||||||
class DataField(xmpp.Node, object):
|
class DataField(xmpp.Node, object):
|
||||||
def __init__(self, typ='text-single', desc=None, required=None, value=None, options=None, node=None):
|
def __init__(self, typ='text-single', desc=None, required=None, value=None, options=None, node=None):
|
||||||
assert typ in ('boolean', 'fixed', 'hidden', 'jid-multi', 'jid-single', 'list-multi',
|
assert typ in ('boolean', 'fixed', 'hidden', 'jid-multi', 'jid-single', 'list-multi',
|
||||||
|
@ -306,7 +346,10 @@ class DataField(xmpp.Node, object):
|
||||||
self.setTagData('desc', desc)
|
self.setTagData('desc', desc)
|
||||||
|
|
||||||
def del_description(self):
|
def del_description(self):
|
||||||
self.delChild('desc')
|
try:
|
||||||
|
self.delChild('desc')
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
|
||||||
description = property(get_description, set_description, del_description,
|
description = property(get_description, set_description, del_description,
|
||||||
""" A natural-language description of the field. It should not contain
|
""" A natural-language description of the field. It should not contain
|
||||||
|
@ -369,16 +412,30 @@ class DataField(xmpp.Node, object):
|
||||||
""" The value of field. Depending on the type, it is a boolean, a unicode string or a list
|
""" The value of field. Depending on the type, it is a boolean, a unicode string or a list
|
||||||
of stings. """)
|
of stings. """)
|
||||||
|
|
||||||
|
def iter_options(self):
|
||||||
|
""" Yields a pair: label, value """
|
||||||
|
for element in self.getChildren():
|
||||||
|
if not isinstance(element, xmpp.Node): continue
|
||||||
|
if not element.getName()=='option': continue
|
||||||
|
yield element.getAttr('label'), element.getTag('value').getData()
|
||||||
|
|
||||||
def get_options(self):
|
def get_options(self):
|
||||||
return [tag.getData() for tag in self.getTags('option')]
|
""" Returns a list of tuples: (label, value). """
|
||||||
|
return [(tag.getAttr('label'), tag.getTag('value').getData()) for tag in self.getTags('option')]
|
||||||
|
|
||||||
def set_options(self, options):
|
def set_options(self, options):
|
||||||
|
""" Options need to be a list of tuples (label, value), both unicode. """
|
||||||
assert options.__iter__
|
assert options.__iter__
|
||||||
|
|
||||||
del_multiple_tag_value(self, 'option')
|
del_multiple_tag_value(self, 'option')
|
||||||
for option in options:
|
for option in options:
|
||||||
assert isinstance(option, basestring)
|
assert option[0] is None or isinstance(option[0], unicode)
|
||||||
self.addChild('option', None, (option,))
|
assert isinstance(option[1], unicode)
|
||||||
|
if option[0] is None:
|
||||||
|
attr=None
|
||||||
|
else:
|
||||||
|
attr={'label': option[0].encode('utf-8')}
|
||||||
|
self.addChild('option', attr, (option[1].encode('utf-8'),))
|
||||||
|
|
||||||
def del_options(self):
|
def del_options(self):
|
||||||
del_multiple_tag_value(self, 'option')
|
del_multiple_tag_value(self, 'option')
|
||||||
|
@ -386,22 +443,30 @@ class DataField(xmpp.Node, object):
|
||||||
options = property(get_options, set_options, del_options,
|
options = property(get_options, set_options, del_options,
|
||||||
""" Options to choose between in list-* fields. """)
|
""" Options to choose between in list-* fields. """)
|
||||||
|
|
||||||
class DataRecord(xmpp.Node, dict):
|
class DataRecord(xmpp.Node):
|
||||||
""" Class to store fields. May be used as temporary storage (for example when reading a list of
|
""" Class to store fields. May be used as temporary storage (for example when reading a list of
|
||||||
fields from DataForm in DATAFORM_SINGLE mode), may be used as permanent storage place (for example
|
fields from DataForm in DATAFORM_SINGLE mode), may be used as permanent storage place (for example
|
||||||
for DataForms in DATAFORM_MULTIPLE mode)."""
|
for DataForms in DATAFORM_MULTIPLE mode). It expects that every <field/> element is actually
|
||||||
|
a DataField instance."""
|
||||||
def __init__(self, fields=None, node=None):
|
def __init__(self, fields=None, node=None):
|
||||||
assert (fields is None) or (node is None)
|
assert (fields is None) or (node is None)
|
||||||
assert (fields is None) or (fields.__iter__)
|
assert (fields is None) or (fields.__iter__)
|
||||||
assert (node is None) or (isinstance(node, xmpp.Node))
|
assert (node is None) or (isinstance(node, xmpp.Node))
|
||||||
|
|
||||||
dict.__init__(self)
|
self.vars = {}
|
||||||
|
|
||||||
xmpp.Node.__init__(self, node=node)
|
xmpp.Node.__init__(self, node=node)
|
||||||
|
|
||||||
|
if node is not None:
|
||||||
|
for field in node.getTags('field'):
|
||||||
|
assert isinstance(field, DataField)
|
||||||
|
self.vars[field.var] = field
|
||||||
|
|
||||||
if fields is not None:
|
if fields is not None:
|
||||||
for field in fields:
|
for field in fields:
|
||||||
assert isinstance(field, DataField)
|
assert isinstance(field, DataField)
|
||||||
self.addChild(node=field)
|
self.addChild(node=field)
|
||||||
self[field.name] = field
|
self.vars[field.var] = field
|
||||||
|
|
||||||
# if there will be ever needed access to all fields as a list, write it here, in form of property
|
# if there will be ever needed access to all fields as a list, write it here, in form of property
|
||||||
|
|
||||||
|
@ -409,3 +474,6 @@ class DataRecord(xmpp.Node, dict):
|
||||||
for field in self.getChildren():
|
for field in self.getChildren():
|
||||||
if not isinstance(field, xmpp.DataField): continue
|
if not isinstance(field, xmpp.DataField): continue
|
||||||
yield field
|
yield field
|
||||||
|
|
||||||
|
def __getitem__(self, item):
|
||||||
|
return self.vars[item]
|
||||||
|
|
|
@ -58,9 +58,9 @@ class Node:
|
||||||
"node" and other arguments is provided then the node initially created as replica of "node"
|
"node" and other arguments is provided then the node initially created as replica of "node"
|
||||||
provided and then modified to be compliant with other arguments."""
|
provided and then modified to be compliant with other arguments."""
|
||||||
if node:
|
if node:
|
||||||
if self.FORCE_NODE_RECREATION and type(node)==type(self):
|
if self.FORCE_NODE_RECREATION and isinstance(node, Node):
|
||||||
node=str(node)
|
node=str(node)
|
||||||
if type(node)<>type(self):
|
if not isinstance(node, Node):
|
||||||
node=NodeBuilder(node,self)
|
node=NodeBuilder(node,self)
|
||||||
else:
|
else:
|
||||||
self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = node.name,node.namespace,{},[],[],node.parent
|
self.name,self.namespace,self.attrs,self.data,self.kids,self.parent = node.name,node.namespace,{},[],[],node.parent
|
||||||
|
|
266
src/dataforms.py
266
src/dataforms.py
|
@ -20,8 +20,9 @@ import gtk
|
||||||
import gtkgui_helpers
|
import gtkgui_helpers
|
||||||
|
|
||||||
import common.xmpp as xmpp
|
import common.xmpp as xmpp
|
||||||
|
import common.dataforms as dataforms
|
||||||
|
|
||||||
class DataFormWidget(gtk.Alignment):
|
class DataFormWidget(gtk.Alignment, object):
|
||||||
# "public" interface
|
# "public" interface
|
||||||
""" Data Form widget. Use like any other widget. """
|
""" Data Form widget. Use like any other widget. """
|
||||||
def __init__(self, dataformnode=None):
|
def __init__(self, dataformnode=None):
|
||||||
|
@ -32,25 +33,40 @@ class DataFormWidget(gtk.Alignment):
|
||||||
|
|
||||||
self.xml=gtkgui_helpers.get_glade('data_form_window.glade', 'data_form_scrolledwindow')
|
self.xml=gtkgui_helpers.get_glade('data_form_window.glade', 'data_form_scrolledwindow')
|
||||||
self.instructions = self.xml.get_widget('form_instructions_label')
|
self.instructions = self.xml.get_widget('form_instructions_label')
|
||||||
self.form = self.xml.get_widget('form_table')
|
self.container = self.xml.get_widget('container_vbox')
|
||||||
|
|
||||||
self.add(self.xml.get_widget('data_form_scrolledwindow'))
|
self.add(self.xml.get_widget('data_form_scrolledwindow'))
|
||||||
|
|
||||||
self.set_data_form(dataformnode)
|
if dataformnode is not None:
|
||||||
|
self.set_data_form(dataformnode)
|
||||||
|
|
||||||
def set_data_form(self, dataform=None):
|
def set_data_form(self, dataform=None):
|
||||||
""" Set the data form (xmpp.DataForm) displayed in widget.
|
""" Set the data form (xmpp.DataForm) displayed in widget.
|
||||||
Set to None to erase the form. """
|
Set to None to erase the form. """
|
||||||
assert (isinstance(dataform, xmpp.Node) or (dataform is None))
|
assert isinstance(dataform, dataforms.DataForm)
|
||||||
|
|
||||||
if self._data_form is not None: self._cleanWidgets()
|
self.del_data_form()
|
||||||
self._data_form = dataform
|
self._data_form = dataform
|
||||||
if self._data_form is not None: self._buildWidgets()
|
if dataform.mode==dataforms.DATAFORM_SINGLE:
|
||||||
|
self.form = self.__class__.SingleForm(dataform)
|
||||||
|
else:
|
||||||
|
self.form = self.__class__.MultipleForm(dataform)
|
||||||
|
self.form.show()
|
||||||
|
self.container.pack_end(self.form)
|
||||||
|
|
||||||
def get_data_form(self):
|
def get_data_form(self):
|
||||||
""" Data form displayed in the widget or None if no form. """
|
""" Data form displayed in the widget or None if no form. """
|
||||||
return self._data_form
|
return self._data_form
|
||||||
|
|
||||||
|
def del_data_form(self):
|
||||||
|
if self._data_form is not None:
|
||||||
|
self.container.remove(self.form)
|
||||||
|
self.form = None
|
||||||
|
self._data_form = None
|
||||||
|
|
||||||
|
data_form = property(get_data_form, set_data_form, del_data_form,
|
||||||
|
"Data form presented in a widget")
|
||||||
|
|
||||||
def get_title(self):
|
def get_title(self):
|
||||||
""" Get the title of data form, as a unicode object. If no
|
""" Get the title of data form, as a unicode object. If no
|
||||||
title or no form, returns u''. Useful for setting window title. """
|
title or no form, returns u''. Useful for setting window title. """
|
||||||
|
@ -59,17 +75,19 @@ class DataFormWidget(gtk.Alignment):
|
||||||
return self._data_form['title'].encode('utf-8')
|
return self._data_form['title'].encode('utf-8')
|
||||||
return u''
|
return u''
|
||||||
|
|
||||||
|
title = property(get_title, None, None, "Data form title")
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
""" Treat 'us' as one widget. """
|
""" Treat 'us' as one widget. """
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def filled_data_form(self):
|
def filled_data_form(self):
|
||||||
""" Generates form that contains values filled by user. This
|
""" Generates form that contains values filled by user. """
|
||||||
won't be DataForm object, as the DataFields seem to be uncapable
|
assert isinstance(self._data_form, dataforms.DataForm)
|
||||||
of some things. """
|
|
||||||
assert isinstance(self._data_form, xmpp.DataForm)
|
|
||||||
|
|
||||||
form = xmpp.Node('x', {'xmlns':xmpp.NS_DATA, 'type':'submit'})
|
form = xmpp.Node('x', {'xmlns':xmpp.NS_DATA, 'type':'submit'})
|
||||||
|
|
||||||
|
|
||||||
for field in self._data_form.kids:
|
for field in self._data_form.kids:
|
||||||
if not isinstance(field, xmpp.DataField): continue
|
if not isinstance(field, xmpp.DataField): continue
|
||||||
|
|
||||||
|
@ -96,158 +114,122 @@ class DataFormWidget(gtk.Alignment):
|
||||||
|
|
||||||
return form
|
return form
|
||||||
|
|
||||||
data_form = property(get_data_form, set_data_form, None, "Data form presented in a widget")
|
|
||||||
title = property(get_title, None, None, "Data form title")
|
|
||||||
|
|
||||||
# "private" methods
|
# "private" methods
|
||||||
def _buildWidgets(self):
|
|
||||||
""" Create all sub-widgets according to self._data_form and
|
|
||||||
JEP-0004. """
|
|
||||||
assert self._data_form is not None
|
|
||||||
assert len(self.form.get_children())==0
|
|
||||||
|
|
||||||
# it is *very* often used here
|
# we have actually two different kinds of data forms: one is a simple form to fill,
|
||||||
df = self._data_form
|
# second is a table with several records; we will treat second as read-only, but still
|
||||||
|
# we should have a way to show it
|
||||||
|
|
||||||
instructions = df.getInstructions()
|
# we will place both types in two different private classes, so the code will be clean;
|
||||||
if instructions is not None:
|
# both will have the same interface
|
||||||
self.instructions.set_text(instructions)
|
|
||||||
|
|
||||||
linecounter = 0
|
class SingleForm(gtk.Table, object):
|
||||||
|
""" Widget that represent DATAFORM_SINGLE mode form. """
|
||||||
|
def __init__(self, dataform):
|
||||||
|
assert dataform.mode==dataforms.DATAFORM_SINGLE
|
||||||
|
|
||||||
for field in df.kids:
|
gtk.Table.__init__(self)
|
||||||
if not isinstance(field, xmpp.DataField): continue
|
|
||||||
|
|
||||||
# TODO: rewrite that when xmpp.DataField will be rewritten
|
self._data_form = dataform
|
||||||
ftype = field.getType()
|
|
||||||
if ftype not in ('boolean', 'fixed', 'hidden', 'jid-multi',
|
|
||||||
'jid-single', 'list-multi', 'list-single',
|
|
||||||
'text-multi', 'text-private', 'text-single'):
|
|
||||||
ftype = 'text-single'
|
|
||||||
|
|
||||||
if ftype == 'hidden': continue
|
# building widget
|
||||||
|
linecounter = 0
|
||||||
|
|
||||||
# field label
|
# for each field...
|
||||||
flabel = field.getAttr('label')
|
for field in self._data_form.iter_fields():
|
||||||
if flabel is None:
|
if field.type=='hidden': continue
|
||||||
flabel = field.getVar()
|
|
||||||
|
|
||||||
# field description
|
commonlabel = True
|
||||||
fdesc = field.getDesc()
|
commondesc = True
|
||||||
|
commonwidget = True
|
||||||
|
widget = None
|
||||||
|
|
||||||
# field value (if one)
|
if field.type=='boolean':
|
||||||
fvalue = field.getValue()
|
widget = gtk.CheckButton()
|
||||||
|
widget.connect('toggled', self.on_boolean_checkbutton_toggled, field)
|
||||||
|
widget.set_active(field.value)
|
||||||
|
|
||||||
# field values (if one or more)
|
elif field.type=='fixed':
|
||||||
fvalues = field.getValues()
|
leftattach = 1
|
||||||
|
rightattach = 2
|
||||||
|
if field.label is None:
|
||||||
|
commonlabel = False
|
||||||
|
leftattach = 0
|
||||||
|
if field.description is None:
|
||||||
|
commondesc = False
|
||||||
|
rightattach = 3
|
||||||
|
|
||||||
|
commonwidget=False
|
||||||
|
widget = gtk.Label(field.value)
|
||||||
|
widget.set_line_wrap(True)
|
||||||
|
self.attach(widget, leftattach, rightattach, linecounter, linecounter+1)
|
||||||
|
|
||||||
# field options
|
elif field.type in ('jid-multi', 'jid-single', 'list-multi', 'text-multi',
|
||||||
foptions = field.getOptions()
|
'text-private'):
|
||||||
|
widget = gtk.Label(field.type)
|
||||||
|
|
||||||
commonlabel = True
|
elif field.type == 'list-single':
|
||||||
commondesc = True
|
# TODO: When more than few choices, make a list
|
||||||
commonwidget = True
|
# TODO: Think of moving that to another function (it could be used
|
||||||
|
# TODO: in stage2 of adhoc commands too).
|
||||||
|
# TODO: What if we have radio buttons and non-required field?
|
||||||
|
# TODO: We cannot deactivate them all...
|
||||||
|
widget = gtk.VBox()
|
||||||
|
first_radio = None
|
||||||
|
for label, value in field.iter_options():
|
||||||
|
radio = gtk.RadioButton(first_radio, label=label)
|
||||||
|
radio.connect('toggled', self.on_list_single_radiobutton_toggled,
|
||||||
|
field, value)
|
||||||
|
if first_radio is None:
|
||||||
|
first_radio = radio
|
||||||
|
if field.value is None:
|
||||||
|
field.value = value
|
||||||
|
if value == field.value:
|
||||||
|
radio.set_active(True)
|
||||||
|
widget.pack_start(radio, expand=False)
|
||||||
|
|
||||||
if ftype == 'boolean':
|
else:# field.type == 'text-single' or field.type is nonstandard:
|
||||||
widget = gtk.CheckButton()
|
# JEP says that if we don't understand some type, we
|
||||||
widget.connect('toggled', self.on_boolean_checkbutton_toggled, field)
|
# should handle it as text-single
|
||||||
if fvalue in ('1', 'true'):
|
widget = gtk.Entry()
|
||||||
widget.set_active(True)
|
widget.connect('changed', self.on_text_single_entry_changed, field)
|
||||||
else:
|
if field.value is None:
|
||||||
field.setValue('0')
|
field.value = u''
|
||||||
|
widget.set_text(field.value)
|
||||||
|
|
||||||
elif ftype == 'fixed':
|
if commonlabel and field.label is not None:
|
||||||
leftattach = 1
|
label = gtk.Label(field.label)
|
||||||
rightattach = 2
|
label.set_justify(gtk.JUSTIFY_RIGHT)
|
||||||
if flabel is None:
|
self.attach(label, 0, 1, linecounter, linecounter+1)
|
||||||
commonlabel = False
|
|
||||||
leftattach = 0
|
|
||||||
if fdesc is None:
|
|
||||||
commondesc = False
|
|
||||||
rightattach = 3
|
|
||||||
commonwidget = False
|
|
||||||
widget = gtk.Label(fvalue)
|
|
||||||
widget.set_line_wrap(True)
|
|
||||||
self.form.attach(widget, leftattach, rightattach, linecounter, linecounter+1)
|
|
||||||
|
|
||||||
elif ftype == 'jid-multi':
|
if commonwidget:
|
||||||
widget = gtk.Label('jid-multi field')
|
assert widget is not None
|
||||||
|
self.attach(widget, 1, 2, linecounter, linecounter+1)
|
||||||
|
widget.show_all()
|
||||||
|
|
||||||
elif ftype == 'jid-single':
|
if commondesc and field.description is not None:
|
||||||
widget = gtk.Label('jid-single field')
|
# TODO: with smaller font
|
||||||
|
label = gtk.Label(field.description)
|
||||||
|
label.set_line_wrap(True)
|
||||||
|
self.attach(label, 2, 3, linecounter, linecounter+1)
|
||||||
|
|
||||||
elif ftype == 'list-multi':
|
linecounter+=1
|
||||||
widget = gtk.Label('list-multi field')
|
if self.get_property('visible'):
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
elif ftype == 'list-single':
|
def show(self):
|
||||||
# TODO: When more than few choices, make a list
|
# simulate that we are one widget
|
||||||
widget = gtk.VBox()
|
self.show_all()
|
||||||
first_radio = None
|
|
||||||
right_value = False
|
|
||||||
for label, value in foptions:
|
|
||||||
radio = gtk.RadioButton(first_radio, label=label)
|
|
||||||
radio.connect('toggled', self.on_list_single_radiobutton_toggled,
|
|
||||||
field, value)
|
|
||||||
if first_radio is None:
|
|
||||||
first_radio = radio
|
|
||||||
first_value = value
|
|
||||||
if value == fvalue:
|
|
||||||
right_value = True
|
|
||||||
widget.pack_end(radio, expand=False)
|
|
||||||
if not right_value:
|
|
||||||
field.setValue(first_value)
|
|
||||||
|
|
||||||
elif ftype == 'text-multi':
|
def on_boolean_checkbutton_toggled(self, widget, field):
|
||||||
widget = gtk.Label('text-multi field')
|
field.value = widget.get_active()
|
||||||
|
|
||||||
elif ftype == 'text-private':
|
def on_list_single_radiobutton_toggled(self, widget, field, value):
|
||||||
widget = gtk.Label('text-private field')
|
field.value = value
|
||||||
|
|
||||||
elif ftype == 'text-single':
|
def on_text_single_entry_changed(self, widget, field):
|
||||||
widget = gtk.Entry()
|
field.value = widget.get_text()
|
||||||
widget.connect('changed', self.on_text_single_entry_changed, field)
|
|
||||||
if fvalue is None:
|
|
||||||
field.setValue('')
|
|
||||||
fvalue = ''
|
|
||||||
widget.set_text(fvalue)
|
|
||||||
|
|
||||||
else:
|
|
||||||
widget = gtk.Label('Unhandled widget type!')
|
|
||||||
|
|
||||||
if commonlabel and flabel is not None:
|
class MultipleForm(gtk.Alignment, object):
|
||||||
label = gtk.Label(flabel)
|
def __init__(self, dataform):
|
||||||
label.set_justify(gtk.JUSTIFY_RIGHT)
|
assert dataform.mode==dataforms.DATAFORM_MULTIPLE
|
||||||
self.form.attach(label, 0, 1, linecounter, linecounter+1)
|
|
||||||
|
|
||||||
if commonwidget:
|
|
||||||
self.form.attach(widget, 1, 2, linecounter, linecounter+1)
|
|
||||||
|
|
||||||
if commondesc and fdesc is not None:
|
|
||||||
label = gtk.Label(fdesc)
|
|
||||||
label.set_line_wrap(True)
|
|
||||||
self.form.attach(label, 2, 3, linecounter, linecounter+1)
|
|
||||||
|
|
||||||
linecounter += 1
|
|
||||||
|
|
||||||
self.form.show_all()
|
|
||||||
|
|
||||||
def _cleanWidgets(self):
|
|
||||||
""" Destroy all sub-widgets used to build current data form. """
|
|
||||||
def remove(widget):
|
|
||||||
self.form.remove(widget)
|
|
||||||
|
|
||||||
self.form.foreach(remove)
|
|
||||||
self.instructions.set_text(u"")
|
|
||||||
|
|
||||||
def on_boolean_checkbutton_toggled(self, widget, field):
|
|
||||||
if widget.get_active():
|
|
||||||
field.setValue('true')
|
|
||||||
else:
|
|
||||||
field.setValue('false')
|
|
||||||
|
|
||||||
def on_list_single_radiobutton_toggled(self, widget, field, value):
|
|
||||||
field.setValue(value)
|
|
||||||
|
|
||||||
def on_text_single_entry_changed(self, widget, field):
|
|
||||||
# TODO: check for encoding?
|
|
||||||
field.setValue(widget.get_text())
|
|
||||||
|
|
Loading…
Reference in New Issue