Dataforms widget moved to another file, some ui tweaks.

This commit is contained in:
Tomasz Melcer 2006-09-13 16:07:51 +00:00
parent a81aaf8c16
commit 5824d3b873
5 changed files with 861 additions and 388 deletions

View File

@ -183,7 +183,7 @@
</child>
</widget>
<widget class="GtkWindow" id="data_form_fake_window">
<widget class="GtkWindow" id="data_form_old_fake_window">
<property name="visible">True</property>
<property name="title" translatable="yes">window1</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
@ -412,4 +412,398 @@
</child>
</widget>
<widget class="GtkWindow" id="data_form_fake_window">
<property name="visible">True</property>
<property name="title" translatable="yes">window1</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
<property name="focus_on_map">True</property>
<child>
<widget class="GtkVBox" id="data_form_vbox">
<property name="border_width">3</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">3</property>
<child>
<widget class="GtkLabel" id="instructions_label">
<property name="visible">True</property>
<property name="label" translatable="yes">Fill in the form.</property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHSeparator" id="instructions_hseparator">
<property name="visible">True</property>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkNotebook" id="data_form_types_notebook">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="show_tabs">True</property>
<property name="show_border">True</property>
<property name="tab_pos">GTK_POS_TOP</property>
<property name="scrollable">False</property>
<property name="enable_popup">False</property>
<child>
<widget class="GtkScrolledWindow" id="single_form_scrolledwindow">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_NONE</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkViewport" id="single_form_viewport">
<property name="visible">True</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<child>
<placeholder/>
</child>
</widget>
</child>
</widget>
<packing>
<property name="tab_expand">False</property>
<property name="tab_fill">True</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label1">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="type">tab</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkLabel" id="label2">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="type">tab</property>
</packing>
</child>
<child>
<widget class="GtkHBox" id="multiple_form_hbox">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkScrolledWindow" id="scrolledwindow38">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="hscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="vscrollbar_policy">GTK_POLICY_ALWAYS</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="records_treeview">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">True</property>
<property name="fixed_height_mode">False</property>
<property name="hover_selection">False</property>
<property name="hover_expand">False</property>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkVBox" id="vbox114">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkButton" id="add_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-add</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_add_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:32:45 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="remove_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-remove</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_remove_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:32:50 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHSeparator" id="hseparator17">
<property name="visible">True</property>
</widget>
<packing>
<property name="padding">3</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="edit_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-edit</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_edit_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:32:54 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHSeparator" id="hseparator18">
<property name="visible">True</property>
</widget>
<packing>
<property name="padding">3</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="up_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-go-up</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_up_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:32:59 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="down_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-go-down</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_down_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:33:03 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHSeparator" id="hseparator19">
<property name="visible">True</property>
</widget>
<packing>
<property name="padding">3</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
<child>
<widget class="GtkButton" id="clear_button">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="label">gtk-clear</property>
<property name="use_stock">True</property>
<property name="relief">GTK_RELIEF_NORMAL</property>
<property name="focus_on_click">True</property>
<signal name="clicked" handler="on_clear_button_clicked" last_modification_time="Mon, 21 Aug 2006 22:33:07 GMT"/>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
<packing>
<property name="tab_expand">False</property>
<property name="tab_fill">True</property>
</packing>
</child>
<child>
<widget class="GtkLabel" id="label3">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="type">tab</property>
</packing>
</child>
<child>
<placeholder/>
</child>
<child>
<widget class="GtkLabel" id="label4">
<property name="visible">True</property>
<property name="label" translatable="yes"></property>
<property name="use_underline">False</property>
<property name="use_markup">False</property>
<property name="justify">GTK_JUSTIFY_LEFT</property>
<property name="wrap">False</property>
<property name="selectable">False</property>
<property name="xalign">0.5</property>
<property name="yalign">0.5</property>
<property name="xpad">0</property>
<property name="ypad">0</property>
<property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
<property name="width_chars">-1</property>
<property name="single_line_mode">False</property>
<property name="angle">0</property>
</widget>
<packing>
<property name="type">tab</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@ -30,7 +30,7 @@ from common import xmpp, gajim, dataforms
import gtkgui_helpers
import dialogs
import dataforms as dataformwidget
import dataforms_widget
class CommandWindow:
'''Class for a window for single ad-hoc commands session. Note, that
@ -70,7 +70,7 @@ class CommandWindow:
self.__dict__[name] = self.xml.get_widget(name)
# creating data forms widget
self.data_form_widget = dataformwidget.DataFormWidget()
self.data_form_widget = dataforms_widget.DataFormWidget()
self.data_form_widget.show()
self.sending_form_stage_vbox.pack_start(self.data_form_widget)
@ -497,14 +497,18 @@ class CommandWindow:
def send_cancel(self):
'''Send the command with action='cancel'. '''
assert self.commandnode is not None
assert self.sessionid is not None
stanza = xmpp.Iq(typ='set', to=self.jid)
stanza.addChild('command', attrs={
'xmlns':xmpp.NS_COMMANDS,
'node':self.commandnode,
'sessionid':self.sessionid,
'action':'cancel'
})
self.account.connection.send(stanza)
if self.sessionid is not None:
# we already have sessionid, so the service sent at least one reply.
stanza = xmpp.Iq(typ='set', to=self.jid)
stanza.addChild('command', attrs={
'xmlns':xmpp.NS_COMMANDS,
'node':self.commandnode,
'sessionid':self.sessionid,
'action':'cancel'
})
self.account.connection.send(stanza)
else:
# we did not received any reply from service; TODO: we should wait and
# then send cancel; for now we do nothing
pass

View File

@ -64,13 +64,14 @@ class DataForm(xmpp.Node, object):
You can also set 'tofill' to DataForm to get a form of type 'submit' with the
same fields as in the form given, but without unnecessary data like
instructions or title. Also the type will be set to 'submit' by default."""
assert (isinstance(node, xmpp.Node) or node is None)
assert (isinstance(tofill, DataForm) or tofill is None)
assert not (node is not None and tofill is not None)
# assert that exactly one of fields 'node' and 'tofill' is filled
assert node is None or isinstance(node, xmpp.Node)
assert tofill is None or isinstance(tofill, xmpp.Node)
assert node is None or tofill is None
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 title is None or isinstance(title, basestring)
assert instructions is None or isinstance(instructions, basestring)
assert (fields is None or fields.__iter__)
xmpp.Node.__init__(self, 'x', node=node)
@ -186,21 +187,14 @@ class DataForm(xmpp.Node, object):
if self.mode is DATAFORM_SINGLE:
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(node=item)
for i in iter_elements(self, 'item'):
yield i
def get_records(self):
if self.mode is DATAFORM_SINGLE:
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=node))
return items
return list(iter_elements(self, 'item'))
def set_records(self, records):
if self.mode is DATAFORM_SINGLE:
@ -211,10 +205,15 @@ class DataForm(xmpp.Node, object):
for field in record.iter_fields():
self[field.var]=field.value
else:
self.del_records(self)
self.del_records()
for record in records:
assert isinstance(record, dict)
newitem = self.addChild('item', node=record)
newitem = self.addChild('item')
for key, value in record.iteritems():
newitem.addChild(node=DataField(
var=key,
value=value,
typ=self.get_field(key).type))
def del_records(self):
if self.mode is DATAFORM_SINGLE:
@ -229,6 +228,22 @@ class DataForm(xmpp.Node, object):
"""Records kept in this form; if in DATAFORM_SINGLE mode, there will be exactly
one record, otherwise there might be more or less records.""")
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
def get_field(self, fieldvar):
''' Find DataField that has fieldvar as var. '''
for field in self.iter_fields():
if field.var == fieldvar: return field
raise KeyError, "This form does not contain %r field." % fieldvar
def get_fields(self):
if self.mode is DATAFORM_SINGLE:
container = self
@ -260,16 +275,6 @@ class DataForm(xmpp.Node, object):
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.""")

View File

@ -1,347 +0,0 @@
## dataforms.py
##
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <nkour@jabber.org>
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2003-2005 Vincent Hanquez <tab@snarc.org>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## 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 General Public License for more details.
##
""" This module contains widget that can display data form (JEP-0004). """
# TODO: forms of type='result' should be read-only
import gtk
import pango
import gtkgui_helpers
import common.xmpp as xmpp
import common.dataforms as dataforms
class DataFormWidget(gtk.Alignment, object):
# "public" interface
""" Data Form widget. Use like any other widget. """
def __init__(self, dataformnode=None):
""" Create a widget. """
gtk.Alignment.__init__(self, xscale=1.0, yscale=1.0)
self._data_form = None
self.xml=gtkgui_helpers.get_glade('data_form_window.glade', 'data_form_scrolledwindow')
self.instructions = self.xml.get_widget('form_instructions_label')
self.separator = self.xml.get_widget('form_instructions_hseparator')
self.container = self.xml.get_widget('container_vbox')
self.add(self.xml.get_widget('data_form_scrolledwindow'))
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, dataforms.DataForm)
self.del_data_form()
self._data_form = dataform
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, expand=True, fill=True)
if dataform.instructions is None:
self.instructions.set_no_show_all(True)
self.instructions.hide()
self.separator.set_no_show_all(True)
self.separator.hide()
else:
self.instructions.set_text(dataform.instructions)
self.instructions.set_no_show_all(False)
self.instructions.show()
self.separator.set_no_show_all(False)
self.separator.show()
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. """
if self._data_form is not None:
if self._data_form.has_key('title'):
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()
# "private" methods
# 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
# we will place both types in two different private classes, so the code will be clean;
# both will have the same interface
class SingleForm(gtk.Table, object):
""" Widget that represent DATAFORM_SINGLE mode form. """
def __init__(self, dataform):
assert dataform.mode==dataforms.DATAFORM_SINGLE
gtk.Table.__init__(self)
self.set_col_spacings(6)
self.set_row_spacings(6)
self._data_form = dataform
# building widget
linecounter = 0
# for each field...
for field in self._data_form.iter_fields():
if field.type=='hidden': continue
commonlabel = True
commondesc = True
commonwidget = True
widget = None
if field.type=='boolean':
widget = gtk.CheckButton()
widget.connect('toggled', self.on_boolean_checkbutton_toggled, field)
widget.set_active(field.value)
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,
xoptions=gtk.FILL, yoptions=gtk.FILL)
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 value, label 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)
elif field.type == 'list-multi':
# TODO: When more than few choices, make a list
widget = gtk.VBox()
for value, label in field.iter_options():
check = gtk.CheckButton(label, use_underline=False)
check.set_active(value in field.value)
check.connect('toggled', self.on_list_multi_checkbutton_toggled,
field, value)
widget.pack_start(check, expand=False)
elif field.type == 'jid-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 field.type == 'jid-multi':
commonwidget = False
xml = gtkgui_helpers.get_glade('data_form_window.glade', 'item_list_table')
widget = xml.get_widget('item_list_table')
treeview = xml.get_widget('item_treeview')
listmodel = gtk.ListStore(str)
for value in field.iter_values():
# nobody will create several megabytes long stanza
listmodel.insert(999999, (value,))
treeview.set_model(listmodel)
renderer = gtk.CellRendererText()
renderer.set_property('editable', True)
renderer.connect('edited',
self.on_jid_multi_cellrenderertext_edited, listmodel, field)
treeview.append_column(gtk.TreeViewColumn(None, renderer,
text=0))
xml.get_widget('add_button').connect('clicked',
self.on_jid_multi_add_button_clicked, treeview, listmodel, field)
xml.get_widget('edit_button').connect('clicked',
self.on_jid_multi_edit_button_clicked, treeview)
xml.get_widget('remove_button').connect('clicked',
self.on_jid_multi_remove_button_clicked, treeview, field)
xml.get_widget('clear_button').connect('clicked',
self.on_jid_multi_clean_button_clicked, listmodel, field)
self.attach(widget, 1, 2, linecounter, linecounter+1)
del xml
elif field.type == 'text-private':
widget = gtk.Entry()
widget.connect('changed', self.on_text_single_entry_changed, field)
widget.set_visibility(False)
if field.value is None:
field.value = u''
widget.set_text(field.value)
elif field.type == 'text-multi':
# TODO: bigger text view
commonwidget = False
textwidget = gtk.TextView()
textwidget.set_wrap_mode(gtk.WRAP_WORD)
textwidget.get_buffer().connect('changed', self.on_text_multi_textbuffer_changed,
field)
if field.value is None:
field.value = u''
textwidget.get_buffer().set_text(field.value)
widget = gtk.ScrolledWindow()
widget.add(textwidget)
self.attach(widget, 1, 2, linecounter, linecounter+1)
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)
if commonlabel and field.label is not None:
label = gtk.Label(field.label)
label.set_alignment(1.0, 0.5)
self.attach(label, 0, 1, linecounter, linecounter+1,
xoptions=gtk.FILL, yoptions=gtk.FILL)
if commonwidget:
assert widget is not None
self.attach(widget, 1, 2, linecounter, linecounter+1,
yoptions=gtk.FILL)
widget.show_all()
if commondesc and field.description is not None:
label = gtk.Label()
label.set_markup('<small>'+\
gtkgui_helpers.escape_for_pango_markup(field.description)+\
'</small>')
label.set_line_wrap(True)
self.attach(label, 2, 3, linecounter, linecounter+1,
xoptions=gtk.FILL|gtk.SHRINK, yoptions=gtk.FILL|gtk.SHRINK)
linecounter+=1
if self.get_property('visible'):
self.show_all()
def show(self):
# simulate that we are one widget
self.show_all()
def on_boolean_checkbutton_toggled(self, widget, field):
field.value = widget.get_active()
def on_list_single_radiobutton_toggled(self, widget, field, value):
field.value = value
def on_list_multi_checkbutton_toggled(self, widget, field, value):
# TODO: make some methods like add_value and remove_value
if widget.get_active() and value not in field.value:
field.value += [value]
elif not widget.get_active() and value in field.value:
field.value = [v for v in field.value if v!=value]
def on_text_single_entry_changed(self, widget, field):
field.value = widget.get_text()
def on_text_multi_textbuffer_changed(self, widget, field):
field.value = widget.get_text(
widget.get_start_iter(),
widget.get_end_iter())
def on_jid_multi_cellrenderertext_edited(self, cell, path, newtext, model, field):
old=model[path][0]
model[path][0]=newtext
values = field.value
values[values.index(old)]=newtext
field.value = values
def on_jid_multi_add_button_clicked(self, widget, treeview, model, field):
iter = model.insert(999999, ("new@jabber.id",))
treeview.set_cursor(model.get_path(iter), treeview.get_column(0), True)
field.value = field.value + ["new@jabber.id"]
def on_jid_multi_edit_button_clicked(self, widget, treeview):
model, iter = treeview.get_selection().get_selected()
assert iter is not None
treeview.set_cursor(model.get_path(iter), treeview.get_column(0), True)
def on_jid_multi_remove_button_clicked(self, widget, treeview, field):
selection = treeview.get_selection()
model = treeview.get_model()
deleted = []
def remove(model, path, iter, deleted):
deleted+=model[iter]
model.remove(iter)
selection.selected_foreach(remove, deleted)
field.value = (v for v in field.value if v not in deleted)
def on_jid_multi_clean_button_clicked(self, widget, model, field):
model.clear()
del field.value
class MultipleForm(gtk.Alignment, object):
def __init__(self, dataform):
assert dataform.mode==dataforms.DATAFORM_MULTIPLE

417
src/dataforms_widget.py Normal file
View File

@ -0,0 +1,417 @@
## dataforms.py
##
## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org>
## Copyright (C) 2005-2006 Nikos Kouremenos <nkour@jabber.org>
## Copyright (C) 2005 Dimitur Kirov <dkirov@gmail.com>
## Copyright (C) 2003-2005 Vincent Hanquez <tab@snarc.org>
##
## This program is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published
## by the Free Software Foundation; version 2 only.
##
## 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 General Public License for more details.
##
""" This module contains widget that can display data form (JEP-0004).
Words single and multiple refers here to types of data forms:
single means these with one record of data (without <recorded/> element),
multiple - these that may contain more data (with <recorded/> element)."""
# TODO: forms of type='result' should be read-only
# TODO: remove tabs from dialog
import gtk
import pango
import gtkgui_helpers
import common.xmpp as xmpp
import common.dataforms as dataforms
class DataFormWidget(gtk.Alignment, object):
# "public" interface
""" Data Form widget. Use like any other widget. """
def __init__(self, dataformnode=None):
""" Create a widget. """
gtk.Alignment.__init__(self, xscale=1.0, yscale=1.0)
self._data_form = None
self.xml=gtkgui_helpers.get_glade('data_form_window.glade', 'data_form_vbox')
for name in ('instructions_label', 'instructions_hseparator',
'single_form_viewport', 'data_form_types_notebook',
'single_form_scrolledwindow', 'multiple_form_hbox',
'records_treeview', 'up_button', 'down_button', 'clear_button'):
self.__dict__[name] = self.xml.get_widget(name)
self.add(self.xml.get_widget('data_form_vbox'))
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, dataforms.DataForm)
self.del_data_form()
self._data_form = dataform
if dataform.mode==dataforms.DATAFORM_SINGLE:
self.build_single_data_form()
else:
self.build_multiple_data_form()
# create appropriate description for instructions field if there isn't any
if dataform.instructions is None:
if dataform.type=='result':
# form is single
instructions = _('This is result of query.')
else:
# form is writable
if dataform.mode==dataforms.DATAFORM_SINGLE:
instructions = _('Fill in the form.')
else:
instructions = _('Edit items on the list')
else:
instructions = dataform.instructions
self.instructions_label.set_text(dataform.instructions)
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):
self.clean_data_form()
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. """
if self._data_form is not None:
if self._data_form.has_key('title'):
# TODO: encode really needed? check this
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()
# "private" methods
# we have actually two different kinds of data forms: one is a simple form to fill,
# second is a table with several records;
def clean_data_form(self):
'''Remove data about existing form. This metod is empty, because
it is rewritten by build_*_data_form, according to type of form
which is actually displayed.'''
pass
def build_single_data_form(self):
'''Invoked when new single form is to be created.'''
assert self._data_form.mode==dataforms.DATAFORM_SINGLE
self.clean_data_form()
self.singleform = SingleForm(self._data_form)
self.singleform.show()
self.single_form_viewport.add(self.singleform)
self.data_form_types_notebook.set_current_page(
self.data_form_types_notebook.page_num(
self.single_form_scrolledwindow))
self.clean_data_form = self.clean_single_data_form
def clean_single_data_form(self):
'''(Called as clean_data_form, read the docs of clean_data_form()).
Remove form from widget.'''
self.singleform.destroy()
del self.singleform
def build_multiple_data_form(self):
'''Invoked when new multiple form is to be created.'''
assert self._data_form.mode==dataforms.DATAFORM_MULTIPLE
self.clean_data_form()
# creating model for form...
fieldtypes = []
for field in self._data_form.iter_fields():
# note: we store also text-private and hidden fields,
# we just do not display them.
# TODO: boolean fields
#elif field.type=='boolean': fieldtypes.append(bool)
fieldtypes.append(unicode)
self.multiplemodel = gtk.ListModel(*fieldtypes)
# moving all data to model
for item in self._data_form.iter_records():
self.multiplemodel.append(field.value for field in item.iter_fields())
# contructing columns...
for field in self._data_form.iter_fields():
self.records_treeview.append(
gtk.TreeViewColumn(
title=field.label,
cell_renderer=gtk.CellRendererText(),
text=field.value))
self.records.show_all()
self.data_form_types_notebook.set_current_page(
self.data_form_types_notebook.page_num(
self.multiple_form_hbox))
self.clean_data_form = self.clean_multiple_data_form
def clean_multiple_data_form(self):
'''(Called as clean_data_form, read the docs of clean_data_form()).
Remove form from widget.'''
pass
class SingleForm(gtk.Table, object):
""" Widget that represent DATAFORM_SINGLE mode form. Because this is used
not only to display single forms, but to form input windows of multiple-type
forms, it is in another class."""
def __init__(self, dataform):
assert dataform.mode==dataforms.DATAFORM_SINGLE
gtk.Table.__init__(self)
self.set_col_spacings(6)
self.set_row_spacings(6)
self._data_form = dataform
# building widget
linecounter = 0
# for each field...
for field in self._data_form.iter_fields():
if field.type=='hidden': continue
commonlabel = True
commondesc = True
commonwidget = True
widget = None
if field.type=='boolean':
widget = gtk.CheckButton()
widget.connect('toggled', self.on_boolean_checkbutton_toggled, field)
widget.set_active(field.value)
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,
xoptions=gtk.FILL, yoptions=gtk.FILL)
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 value, label 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)
elif field.type == 'list-multi':
# TODO: When more than few choices, make a list
widget = gtk.VBox()
for value, label in field.iter_options():
check = gtk.CheckButton(label, use_underline=False)
check.set_active(value in field.value)
check.connect('toggled', self.on_list_multi_checkbutton_toggled,
field, value)
widget.pack_start(check, expand=False)
elif field.type == 'jid-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 field.type == 'jid-multi':
commonwidget = False
xml = gtkgui_helpers.get_glade('data_form_window.glade', 'item_list_table')
widget = xml.get_widget('item_list_table')
treeview = xml.get_widget('item_treeview')
listmodel = gtk.ListStore(str)
for value in field.iter_values():
# nobody will create several megabytes long stanza
listmodel.insert(999999, (value,))
treeview.set_model(listmodel)
renderer = gtk.CellRendererText()
renderer.set_property('editable', True)
renderer.connect('edited',
self.on_jid_multi_cellrenderertext_edited, listmodel, field)
treeview.append_column(gtk.TreeViewColumn(None, renderer,
text=0))
xml.get_widget('add_button').connect('clicked',
self.on_jid_multi_add_button_clicked, treeview, listmodel, field)
xml.get_widget('edit_button').connect('clicked',
self.on_jid_multi_edit_button_clicked, treeview)
xml.get_widget('remove_button').connect('clicked',
self.on_jid_multi_remove_button_clicked, treeview, field)
xml.get_widget('clear_button').connect('clicked',
self.on_jid_multi_clean_button_clicked, listmodel, field)
self.attach(widget, 1, 2, linecounter, linecounter+1)
del xml
elif field.type == 'text-private':
widget = gtk.Entry()
widget.connect('changed', self.on_text_single_entry_changed, field)
widget.set_visibility(False)
if field.value is None:
field.value = u''
widget.set_text(field.value)
elif field.type == 'text-multi':
# TODO: bigger text view
commonwidget = False
textwidget = gtk.TextView()
textwidget.set_wrap_mode(gtk.WRAP_WORD)
textwidget.get_buffer().connect('changed', self.on_text_multi_textbuffer_changed,
field)
if field.value is None:
field.value = u''
textwidget.get_buffer().set_text(field.value)
widget = gtk.ScrolledWindow()
widget.add(textwidget)
self.attach(widget, 1, 2, linecounter, linecounter+1)
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)
if commonlabel and field.label is not None:
label = gtk.Label(field.label)
label.set_alignment(1.0, 0.5)
self.attach(label, 0, 1, linecounter, linecounter+1,
xoptions=gtk.FILL, yoptions=gtk.FILL)
if commonwidget:
assert widget is not None
self.attach(widget, 1, 2, linecounter, linecounter+1,
yoptions=gtk.FILL)
widget.show_all()
if commondesc and field.description is not None:
label = gtk.Label()
label.set_markup('<small>'+\
gtkgui_helpers.escape_for_pango_markup(field.description)+\
'</small>')
label.set_line_wrap(True)
self.attach(label, 2, 3, linecounter, linecounter+1,
xoptions=gtk.FILL|gtk.SHRINK, yoptions=gtk.FILL|gtk.SHRINK)
linecounter+=1
if self.get_property('visible'):
self.show_all()
def show(self):
# simulate that we are one widget
self.show_all()
def on_boolean_checkbutton_toggled(self, widget, field):
field.value = widget.get_active()
def on_list_single_radiobutton_toggled(self, widget, field, value):
field.value = value
def on_list_multi_checkbutton_toggled(self, widget, field, value):
# TODO: make some methods like add_value and remove_value
if widget.get_active() and value not in field.value:
field.value += [value]
elif not widget.get_active() and value in field.value:
field.value = [v for v in field.value if v!=value]
def on_text_single_entry_changed(self, widget, field):
field.value = widget.get_text()
def on_text_multi_textbuffer_changed(self, widget, field):
field.value = widget.get_text(
widget.get_start_iter(),
widget.get_end_iter())
def on_jid_multi_cellrenderertext_edited(self, cell, path, newtext, model, field):
old=model[path][0]
model[path][0]=newtext
values = field.value
values[values.index(old)]=newtext
field.value = values
def on_jid_multi_add_button_clicked(self, widget, treeview, model, field):
iter = model.insert(999999, ("new@jabber.id",))
treeview.set_cursor(model.get_path(iter), treeview.get_column(0), True)
field.value = field.value + ["new@jabber.id"]
def on_jid_multi_edit_button_clicked(self, widget, treeview):
model, iter = treeview.get_selection().get_selected()
assert iter is not None
treeview.set_cursor(model.get_path(iter), treeview.get_column(0), True)
def on_jid_multi_remove_button_clicked(self, widget, treeview, field):
selection = treeview.get_selection()
model = treeview.get_model()
deleted = []
def remove(model, path, iter, deleted):
deleted+=model[iter]
model.remove(iter)
selection.selected_foreach(remove, deleted)
field.value = (v for v in field.value if v not in deleted)
def on_jid_multi_clean_button_clicked(self, widget, model, field):
model.clear()
del field.value