Ad-Hoc commands window now uses new dataform wrapper. /me dances
This commit is contained in:
		
							parent
							
								
									8c9711d7ea
								
							
						
					
					
						commit
						c75bdcb63c
					
				
					 5 changed files with 233 additions and 190 deletions
				
			
		| 
						 | 
				
			
			@ -208,7 +208,7 @@
 | 
			
		|||
      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
 | 
			
		||||
 | 
			
		||||
      <child>
 | 
			
		||||
	<widget class="GtkVBox" id="vbox111">
 | 
			
		||||
	<widget class="GtkVBox" id="container_vbox">
 | 
			
		||||
	  <property name="border_width">5</property>
 | 
			
		||||
	  <property name="visible">True</property>
 | 
			
		||||
	  <property name="homogeneous">False</property>
 | 
			
		||||
| 
						 | 
				
			
			@ -251,19 +251,7 @@
 | 
			
		|||
	  </child>
 | 
			
		||||
 | 
			
		||||
	  <child>
 | 
			
		||||
	    <widget class="GtkTable" id="form_table">
 | 
			
		||||
	      <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>
 | 
			
		||||
	    <placeholder/>
 | 
			
		||||
	  </child>
 | 
			
		||||
	</widget>
 | 
			
		||||
      </child>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -26,11 +26,10 @@
 | 
			
		|||
import gobject
 | 
			
		||||
import gtk
 | 
			
		||||
 | 
			
		||||
import common.xmpp as xmpp
 | 
			
		||||
import common.gajim as gajim
 | 
			
		||||
from common import xmpp, gajim, dataforms
 | 
			
		||||
 | 
			
		||||
import gtkgui_helpers
 | 
			
		||||
import dataforms
 | 
			
		||||
import dataforms as dataformwidget
 | 
			
		||||
 | 
			
		||||
class CommandWindow:
 | 
			
		||||
	'''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: 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):
 | 
			
		||||
		'''Create new window.'''
 | 
			
		||||
| 
						 | 
				
			
			@ -68,7 +69,8 @@ class CommandWindow:
 | 
			
		|||
			self.__dict__[name] = self.xml.get_widget(name)
 | 
			
		||||
 | 
			
		||||
		# 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)
 | 
			
		||||
 | 
			
		||||
		# setting initial stage
 | 
			
		||||
| 
						 | 
				
			
			@ -264,13 +266,14 @@ class CommandWindow:
 | 
			
		|||
		self.remove_pulsing()
 | 
			
		||||
		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_data_form(self.dataform)
 | 
			
		||||
		self.data_form_widget.show()
 | 
			
		||||
 | 
			
		||||
		action = command.getTag('action')
 | 
			
		||||
		if action is None:
 | 
			
		||||
| 
						 | 
				
			
			@ -367,6 +370,7 @@ class CommandWindow:
 | 
			
		|||
 | 
			
		||||
		def callback(response):
 | 
			
		||||
			'''Called on response to query.'''
 | 
			
		||||
			# TODO: move to connection_handlers.py
 | 
			
		||||
			# is error => error stage
 | 
			
		||||
			error = response.getError()
 | 
			
		||||
			if error is not None:
 | 
			
		||||
| 
						 | 
				
			
			@ -405,10 +409,11 @@ class CommandWindow:
 | 
			
		|||
			cmdnode.setAttr('sessionid', self.sessionid)
 | 
			
		||||
 | 
			
		||||
		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):
 | 
			
		||||
			# TODO: error handling
 | 
			
		||||
			# TODO: move to connection_handlers.py
 | 
			
		||||
			self.stage3_next_form(response.getTag('command'))
 | 
			
		||||
 | 
			
		||||
		print stanza
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -37,7 +37,10 @@ def del_multiple_tag_value(node, childname):
 | 
			
		|||
	childname in node.
 | 
			
		||||
	"""
 | 
			
		||||
 | 
			
		||||
	while node.delChild(childname):
 | 
			
		||||
	try:
 | 
			
		||||
		while node.delChild(childname):
 | 
			
		||||
			pass
 | 
			
		||||
	except ValueError:
 | 
			
		||||
		pass
 | 
			
		||||
 | 
			
		||||
def iter_elements(node, childname):
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +68,7 @@ class DataForm(xmpp.Node, object):
 | 
			
		|||
		assert (isinstance(tofill, DataForm) or tofill is 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(instructions, basestring) or instructions is None
 | 
			
		||||
		assert (fields is None or fields.__iter__)
 | 
			
		||||
| 
						 | 
				
			
			@ -94,11 +97,16 @@ class DataForm(xmpp.Node, object):
 | 
			
		|||
				self._mode = DATAFORM_SINGLE
 | 
			
		||||
 | 
			
		||||
				# change every <field/> to DataField object
 | 
			
		||||
				toadd=[]
 | 
			
		||||
				for field in self.getChildren():
 | 
			
		||||
					if not isinstance(field, xmpp.Node): continue
 | 
			
		||||
					if not field.getName()=='field': continue
 | 
			
		||||
					self.delChild(field)
 | 
			
		||||
					self.addChild(node=DataField(node=field)
 | 
			
		||||
					toadd.append(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
 | 
			
		||||
			if typ is None: typ='result'
 | 
			
		||||
			if records is not None and len(records)>1:
 | 
			
		||||
| 
						 | 
				
			
			@ -133,7 +141,10 @@ class DataForm(xmpp.Node, object):
 | 
			
		|||
		self.setTagData('title')
 | 
			
		||||
 | 
			
		||||
	def del_title(self):
 | 
			
		||||
		self.delChild('title')
 | 
			
		||||
		try:
 | 
			
		||||
			self.delChild('title')
 | 
			
		||||
		except ValueError:
 | 
			
		||||
			pass
 | 
			
		||||
 | 
			
		||||
	title = property(get_title, set_title, del_title,
 | 
			
		||||
		"Form title, in unicode, from <title/> element.")
 | 
			
		||||
| 
						 | 
				
			
			@ -168,22 +179,22 @@ class DataForm(xmpp.Node, object):
 | 
			
		|||
 | 
			
		||||
	def iter_records(self):
 | 
			
		||||
		if self.mode is DATAFORM_SINGLE:
 | 
			
		||||
			yield DataRecord(self)
 | 
			
		||||
			yield DataRecord(node=self)
 | 
			
		||||
		else:
 | 
			
		||||
			for item in self.getChildren():
 | 
			
		||||
				if not isinstance(item, xmpp.Node): continue
 | 
			
		||||
				if not item.getName()=='item': continue
 | 
			
		||||
				yield DataRecord(item)
 | 
			
		||||
				yield DataRecord(node=item)
 | 
			
		||||
 | 
			
		||||
	def get_records(self):
 | 
			
		||||
		if self.mode is DATAFORM_SINGLE:
 | 
			
		||||
			return [DataRecord(self),]
 | 
			
		||||
			return [DataRecord(node=self),]
 | 
			
		||||
		else:
 | 
			
		||||
			items = []
 | 
			
		||||
			for node in self.getChildren():
 | 
			
		||||
				if not isinstance(node, xmpp.Node): continue
 | 
			
		||||
				if not node.getName()=='item': continue
 | 
			
		||||
				items.append(DataRecord(node))
 | 
			
		||||
				items.append(DataRecord(node=node))
 | 
			
		||||
			return items
 | 
			
		||||
 | 
			
		||||
	def set_records(self, records):
 | 
			
		||||
| 
						 | 
				
			
			@ -191,9 +202,9 @@ class DataForm(xmpp.Node, object):
 | 
			
		|||
			assert len(records)==1
 | 
			
		||||
 | 
			
		||||
			record = records[0]
 | 
			
		||||
			assert isinstance(record, dict)
 | 
			
		||||
			for name, value in record.iteritems():
 | 
			
		||||
				self[name]=value
 | 
			
		||||
			assert isinstance(record, DataRecord)
 | 
			
		||||
			for field in record.iter_fields():
 | 
			
		||||
				self[field.var]=field.value
 | 
			
		||||
		else:
 | 
			
		||||
			self.del_records(self)
 | 
			
		||||
			for record in records:
 | 
			
		||||
| 
						 | 
				
			
			@ -229,19 +240,48 @@ class DataForm(xmpp.Node, object):
 | 
			
		|||
				self.addChild(node=field)
 | 
			
		||||
		else:
 | 
			
		||||
			assert len(self.records)==0
 | 
			
		||||
			self.delChild('recorded')
 | 
			
		||||
			try:
 | 
			
		||||
				self.delChild('recorded')
 | 
			
		||||
			except ValueError:
 | 
			
		||||
				pass
 | 
			
		||||
			self.addChild('recorded', None, fields)
 | 
			
		||||
 | 
			
		||||
	def del_fields(self):
 | 
			
		||||
		if self.mode is DATAFORM_SINGLE:
 | 
			
		||||
			del_multiple_tag_value(self, "field")
 | 
			
		||||
		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 in this form; a list; if in DATAFORM_SINGLE mode, you should not
 | 
			
		||||
		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):
 | 
			
		||||
	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',
 | 
			
		||||
| 
						 | 
				
			
			@ -306,7 +346,10 @@ class DataField(xmpp.Node, object):
 | 
			
		|||
		self.setTagData('desc', desc)
 | 
			
		||||
 | 
			
		||||
	def del_description(self):
 | 
			
		||||
		self.delChild('desc')
 | 
			
		||||
		try:
 | 
			
		||||
			self.delChild('desc')
 | 
			
		||||
		except ValueError:
 | 
			
		||||
			pass
 | 
			
		||||
 | 
			
		||||
	description = property(get_description, set_description, del_description,
 | 
			
		||||
		""" 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
 | 
			
		||||
		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):
 | 
			
		||||
		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):
 | 
			
		||||
		""" Options need to be a list of tuples (label, value), both unicode. """
 | 
			
		||||
		assert options.__iter__
 | 
			
		||||
 | 
			
		||||
		del_multiple_tag_value(self, 'option')
 | 
			
		||||
		for option in options:
 | 
			
		||||
			assert isinstance(option, basestring)
 | 
			
		||||
			self.addChild('option', None, (option,))
 | 
			
		||||
			assert option[0] is None or isinstance(option[0], unicode)
 | 
			
		||||
			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):
 | 
			
		||||
		del_multiple_tag_value(self, 'option')
 | 
			
		||||
| 
						 | 
				
			
			@ -386,22 +443,30 @@ class DataField(xmpp.Node, object):
 | 
			
		|||
	options = property(get_options, set_options, del_options,
 | 
			
		||||
		""" 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
 | 
			
		||||
	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):
 | 
			
		||||
		assert (fields is None) or (node is None)
 | 
			
		||||
		assert (fields is None) or (fields.__iter__)
 | 
			
		||||
		assert (node is None) or (isinstance(node, xmpp.Node))
 | 
			
		||||
 | 
			
		||||
		dict.__init__(self)
 | 
			
		||||
		self.vars = {}
 | 
			
		||||
 | 
			
		||||
		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:
 | 
			
		||||
			for field in fields:
 | 
			
		||||
				assert isinstance(field, DataField)
 | 
			
		||||
				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
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -409,3 +474,6 @@ class DataRecord(xmpp.Node, dict):
 | 
			
		|||
		for field in self.getChildren():
 | 
			
		||||
			if not isinstance(field, xmpp.DataField): continue
 | 
			
		||||
			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"
 | 
			
		||||
			provided and then modified to be compliant with other arguments."""
 | 
			
		||||
		if node:
 | 
			
		||||
			if self.FORCE_NODE_RECREATION and type(node)==type(self): 
 | 
			
		||||
			if self.FORCE_NODE_RECREATION and isinstance(node, Node): 
 | 
			
		||||
				node=str(node)
 | 
			
		||||
			if type(node)<>type(self): 
 | 
			
		||||
			if not isinstance(node, Node): 
 | 
			
		||||
				node=NodeBuilder(node,self)
 | 
			
		||||
			else:
 | 
			
		||||
				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 common.xmpp as xmpp
 | 
			
		||||
import common.dataforms as dataforms
 | 
			
		||||
 | 
			
		||||
class DataFormWidget(gtk.Alignment):
 | 
			
		||||
class DataFormWidget(gtk.Alignment, object):
 | 
			
		||||
# "public" interface
 | 
			
		||||
	""" Data Form widget. Use like any other widget. """
 | 
			
		||||
	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.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.set_data_form(dataformnode)
 | 
			
		||||
		if dataformnode is not None:
 | 
			
		||||
			self.set_data_form(dataformnode)
 | 
			
		||||
 | 
			
		||||
	def set_data_form(self, dataform=None):
 | 
			
		||||
		""" Set the data form (xmpp.DataForm) displayed in widget.
 | 
			
		||||
		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
 | 
			
		||||
		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):
 | 
			
		||||
		""" Data form displayed in the widget or None if no 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):
 | 
			
		||||
		""" Get the title of data form, as a unicode object. If no
 | 
			
		||||
		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 u''
 | 
			
		||||
 | 
			
		||||
	title = property(get_title, None, None, "Data form title")
 | 
			
		||||
 | 
			
		||||
	def show(self):
 | 
			
		||||
		""" Treat 'us' as one widget. """
 | 
			
		||||
		self.show_all()
 | 
			
		||||
 | 
			
		||||
	def filled_data_form(self):
 | 
			
		||||
		""" Generates form that contains values filled by user. This
 | 
			
		||||
		won't be DataForm object, as the DataFields seem to be uncapable
 | 
			
		||||
		of some things. """
 | 
			
		||||
		assert isinstance(self._data_form, xmpp.DataForm)
 | 
			
		||||
		""" Generates form that contains values filled by user. """
 | 
			
		||||
		assert isinstance(self._data_form, dataforms.DataForm)
 | 
			
		||||
 | 
			
		||||
		form = xmpp.Node('x', {'xmlns':xmpp.NS_DATA, 'type':'submit'})
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		for field in self._data_form.kids:
 | 
			
		||||
			if not isinstance(field, xmpp.DataField): continue
 | 
			
		||||
			
 | 
			
		||||
| 
						 | 
				
			
			@ -96,158 +114,122 @@ class DataFormWidget(gtk.Alignment):
 | 
			
		|||
 | 
			
		||||
		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
 | 
			
		||||
	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
 | 
			
		||||
		df = self._data_form
 | 
			
		||||
# we have actually two different kinds of data forms: one is a simple form to fill,
 | 
			
		||||
# 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()
 | 
			
		||||
		if instructions is not None:
 | 
			
		||||
			self.instructions.set_text(instructions)
 | 
			
		||||
# we will place both types in two different private classes, so the code will be clean;
 | 
			
		||||
# both will have the same interface
 | 
			
		||||
 | 
			
		||||
		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:
 | 
			
		||||
			if not isinstance(field, xmpp.DataField): continue
 | 
			
		||||
			gtk.Table.__init__(self)
 | 
			
		||||
 | 
			
		||||
			# TODO: rewrite that when xmpp.DataField will be rewritten
 | 
			
		||||
			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'
 | 
			
		||||
			self._data_form = dataform
 | 
			
		||||
 | 
			
		||||
			if ftype == 'hidden': continue
 | 
			
		||||
			# building widget
 | 
			
		||||
			linecounter = 0
 | 
			
		||||
 | 
			
		||||
			# field label
 | 
			
		||||
			flabel = field.getAttr('label')
 | 
			
		||||
			if flabel is None:
 | 
			
		||||
				flabel = field.getVar()
 | 
			
		||||
			# for each field...
 | 
			
		||||
			for field in self._data_form.iter_fields():
 | 
			
		||||
				if field.type=='hidden': continue
 | 
			
		||||
 | 
			
		||||
			# field description
 | 
			
		||||
			fdesc = field.getDesc()
 | 
			
		||||
				commonlabel = True
 | 
			
		||||
				commondesc = True
 | 
			
		||||
				commonwidget = True
 | 
			
		||||
				widget = None
 | 
			
		||||
 | 
			
		||||
			# field value (if one)
 | 
			
		||||
			fvalue = field.getValue()
 | 
			
		||||
				if field.type=='boolean':
 | 
			
		||||
					widget = gtk.CheckButton()
 | 
			
		||||
					widget.connect('toggled', self.on_boolean_checkbutton_toggled, field)
 | 
			
		||||
					widget.set_active(field.value)
 | 
			
		||||
 | 
			
		||||
			# field values (if one or more)
 | 
			
		||||
			fvalues = field.getValues()
 | 
			
		||||
				elif field.type=='fixed':
 | 
			
		||||
					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
 | 
			
		||||
			foptions = field.getOptions()
 | 
			
		||||
				elif field.type in ('jid-multi', 'jid-single', 'list-multi', 'text-multi',
 | 
			
		||||
					'text-private'):
 | 
			
		||||
					widget = gtk.Label(field.type)
 | 
			
		||||
 | 
			
		||||
			commonlabel = True
 | 
			
		||||
			commondesc = True
 | 
			
		||||
			commonwidget = True
 | 
			
		||||
				elif field.type == 'list-single':
 | 
			
		||||
					# TODO: When more than few choices, make a list
 | 
			
		||||
					# 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':
 | 
			
		||||
				widget = gtk.CheckButton()
 | 
			
		||||
				widget.connect('toggled', self.on_boolean_checkbutton_toggled, field)
 | 
			
		||||
				if fvalue in ('1', 'true'):
 | 
			
		||||
					widget.set_active(True)
 | 
			
		||||
				else:
 | 
			
		||||
					field.setValue('0')
 | 
			
		||||
				else:# field.type == 'text-single' or field.type is nonstandard:
 | 
			
		||||
					# JEP says that if we don't understand some type, we
 | 
			
		||||
					# should handle it as text-single
 | 
			
		||||
					widget = gtk.Entry()
 | 
			
		||||
					widget.connect('changed', self.on_text_single_entry_changed, field)
 | 
			
		||||
					if field.value is None:
 | 
			
		||||
						field.value = u''
 | 
			
		||||
					widget.set_text(field.value)
 | 
			
		||||
 | 
			
		||||
			elif ftype == 'fixed':
 | 
			
		||||
				leftattach = 1
 | 
			
		||||
				rightattach = 2
 | 
			
		||||
				if flabel is None:
 | 
			
		||||
					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)
 | 
			
		||||
				if commonlabel and field.label is not None:
 | 
			
		||||
					label = gtk.Label(field.label)
 | 
			
		||||
					label.set_justify(gtk.JUSTIFY_RIGHT)
 | 
			
		||||
					self.attach(label, 0, 1, linecounter, linecounter+1)
 | 
			
		||||
 | 
			
		||||
			elif ftype == 'jid-multi':
 | 
			
		||||
				widget = gtk.Label('jid-multi field')
 | 
			
		||||
				if commonwidget:
 | 
			
		||||
					assert widget is not None
 | 
			
		||||
					self.attach(widget, 1, 2, linecounter, linecounter+1)
 | 
			
		||||
				widget.show_all()
 | 
			
		||||
 | 
			
		||||
			elif ftype == 'jid-single':
 | 
			
		||||
				widget = gtk.Label('jid-single field')
 | 
			
		||||
				if commondesc and field.description is not None:
 | 
			
		||||
					# 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':
 | 
			
		||||
				widget = gtk.Label('list-multi field')
 | 
			
		||||
				linecounter+=1
 | 
			
		||||
			if self.get_property('visible'):
 | 
			
		||||
				self.show_all()
 | 
			
		||||
 | 
			
		||||
			elif ftype == 'list-single':
 | 
			
		||||
				# TODO: When more than few choices, make a list
 | 
			
		||||
				widget = gtk.VBox()
 | 
			
		||||
				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)
 | 
			
		||||
		def show(self):
 | 
			
		||||
			# simulate that we are one widget
 | 
			
		||||
			self.show_all()
 | 
			
		||||
 | 
			
		||||
			elif ftype == 'text-multi':
 | 
			
		||||
				widget = gtk.Label('text-multi field')
 | 
			
		||||
		def on_boolean_checkbutton_toggled(self, widget, field):
 | 
			
		||||
			field.value = widget.get_active()
 | 
			
		||||
 | 
			
		||||
			elif ftype == 'text-private':
 | 
			
		||||
				widget = gtk.Label('text-private field')
 | 
			
		||||
		def on_list_single_radiobutton_toggled(self, widget, field, value):
 | 
			
		||||
			field.value = value
 | 
			
		||||
 | 
			
		||||
			elif ftype == 'text-single':
 | 
			
		||||
				widget = gtk.Entry()
 | 
			
		||||
				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!')
 | 
			
		||||
		def on_text_single_entry_changed(self, widget, field):
 | 
			
		||||
			field.value = widget.get_text()
 | 
			
		||||
 | 
			
		||||
			if commonlabel and flabel is not None:
 | 
			
		||||
				label = gtk.Label(flabel)
 | 
			
		||||
				label.set_justify(gtk.JUSTIFY_RIGHT)
 | 
			
		||||
				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())
 | 
			
		||||
	class MultipleForm(gtk.Alignment, object):
 | 
			
		||||
		def __init__(self, dataform):
 | 
			
		||||
			assert dataform.mode==dataforms.DATAFORM_MULTIPLE
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue