From db089f103d71fd2c99d08acb3a68493143122b66 Mon Sep 17 00:00:00 2001 From: Tomasz Melcer Date: Wed, 12 Jul 2006 14:19:58 +0000 Subject: Jid-multi almost works. --- src/adhoc_commands.py | 68 ++++++++++++++++------ src/common/dataforms.py | 26 +++++++-- src/dataforms.py | 148 +++++++++++++++++++++++++++++++++--------------- 3 files changed, 174 insertions(+), 68 deletions(-) (limited to 'src') diff --git a/src/adhoc_commands.py b/src/adhoc_commands.py index 7519fee3b..7af0ff234 100644 --- a/src/adhoc_commands.py +++ b/src/adhoc_commands.py @@ -61,11 +61,11 @@ class CommandWindow: self.xml = gtkgui_helpers.get_glade('adhoc_commands_window.glade') self.window = self.xml.get_widget('adhoc_commands_window') for name in ('cancel_button', 'back_button', 'forward_button', - 'execute_button','stages_notebook', + 'execute_button','close_button','stages_notebook', 'retrieving_commands_stage_vbox', 'command_list_stage_vbox','command_list_vbox', 'sending_form_stage_vbox','sending_form_progressbar', - 'no_commands_stage_vbox','error_stage_vbox', + 'notes_label','no_commands_stage_vbox','error_stage_vbox', 'error_description_label'): self.__dict__[name] = self.xml.get_widget(name) @@ -75,6 +75,7 @@ class CommandWindow: self.sending_form_stage_vbox.pack_start(self.data_form_widget) # setting initial stage + self.close_button.set_no_show_all(True) self.stage1() # displaying the window @@ -87,6 +88,7 @@ class CommandWindow: def stage_back_button_clicked(self, *anything): assert False def stage_forward_button_clicked(self, *anything): assert False def stage_execute_button_clicked(self, *anything): assert False + def stage_close_button_clicked(self, *anything): assert False def stage_adhoc_commands_window_delete_event(self, *anything): assert False def do_nothing(self, *anything): return False @@ -103,8 +105,11 @@ class CommandWindow: def on_execute_button_clicked(self, *anything): return self.stage_execute_button_clicked(*anything) + def on_close_button_clicked(self, *anything): + return self.stage_close_button_clicked(*anything) + def on_adhoc_commands_window_destroy(self, *anything): - # do all actions that are needed to remove this object from memory... + # TODO: do all actions that are needed to remove this object from memory... self.remove_pulsing() def on_adhoc_commands_window_delete_event(self, *anything): @@ -215,6 +220,8 @@ class CommandWindow: assert isinstance(self.commandnode, unicode) + self.form_status = None + self.stages_notebook.set_current_page( self.stages_notebook.page_num( self.sending_form_stage_vbox)) @@ -231,6 +238,7 @@ class CommandWindow: self.stage_back_button_clicked = self.stage3_back_button_clicked self.stage_forward_button_clicked = self.stage3_forward_button_clicked self.stage_execute_button_clicked = self.stage3_execute_button_clicked + self.stage_close_button_clicked = self.stage3_close_button_clicked self.stage_adhoc_commands_window_delete_event = self.stage3_cancel_button_clicked def stage3_finish(self): @@ -255,6 +263,14 @@ class CommandWindow: return False return True + def stage3_close_button_clicked(self, widget): + # this works also as a handler for window_delete_event, so we have to return appropriate + # values + if widget==self.window: + return False + else: + self.window.destroy() + def stage3_back_button_clicked(self, widget): self.stage3_submit_form('prev') @@ -287,23 +303,24 @@ class CommandWindow: if self.sessionid is None: self.sessionid = command.getAttr('sessionid') - self.dataform = dataforms.DataForm(node=command.getTag('x')) + self.form_status = command.getAttr('status') - self.data_form_widget.set_sensitive(True) - try: - self.data_form_widget.data_form=self.dataform - except dataforms.BadDataFormNode: - # TODO: translate - self.stage5('Service sent malformed data', senderror=True) - self.data_form_widget.show() + if command.getTag('x') is not None: + self.dataform = dataforms.DataForm(node=command.getTag('x')) + + self.data_form_widget.set_sensitive(True) + try: + self.data_form_widget.data_form=self.dataform + except dataforms.BadDataFormNode: + # TODO: translate + self.stage5('Service sent malformed data', senderror=True) + self.data_form_widget.show() + else: + self.data_form_widget.hide() action = command.getTag('action') if action is None: - # no action tag? check if that's last stage... - if command.getAttr('status')=='completed': - self.cancel_button.set_sensitive(False) - else: - self.cancel_button.set_sensitive(True) + self.cancel_button.set_sensitive(True) self.back_button.set_sensitive(False) self.forward_button.set_sensitive(False) self.execute_button.set_sensitive(True) @@ -314,6 +331,25 @@ class CommandWindow: self.forward_button.set_sensitive(action.getTag('next') is not None) self.execute_button.set_sensitive(True) + if self.form_status == 'completed': + self.cancel_button.set_sensitive(False) + self.back_button.set_sensitive(False) + self.forward_button.set_sensitive(False) + self.execute_button.set_no_show_all(True) + self.execute_button.hide() + self.close_button.set_no_show_all(False) + self.close_button.show() + self.stage_adhoc_commands_window_delete_event = self.stage3_close_button_clicked + + note = command.getTag('note') + if note is not None: + self.notes_label.set_text(note.getData().decode('utf-8')) + self.notes_label.set_no_show_all(False) + self.notes_label.show() + else: + self.notes_label.set_no_show_all(True) + self.notes_label.hide() + # stage 4: no commands are exposed def stage4(self): '''Display the message. Wait for user to close the window''' diff --git a/src/common/dataforms.py b/src/common/dataforms.py index f1390bf05..5acf452ad 100644 --- a/src/common/dataforms.py +++ b/src/common/dataforms.py @@ -249,7 +249,7 @@ class DataForm(xmpp.Node, object): self.delChild('recorded') except ValueError: pass - self.addChild('recorded', None, fields) + self.addChild('recorded', {}, fields) def del_fields(self): if self.mode is DATAFORM_SINGLE: @@ -335,7 +335,10 @@ class DataField(xmpp.Node, object): self.setAttr('var', var) def del_var(self): - self.delAttr('var') + try: + self.delAttr('var') + except KeyError: + pass var = property(get_var, set_var, del_var, """ Field name. """) @@ -347,7 +350,10 @@ class DataField(xmpp.Node, object): self.setAttr('label', label) def del_label(self): - self.delAttr('label') + try: + self.delAttr('label') + except KeyError: + pass label = property(get_label, set_label, del_label, """ Human-readable name for field. """) @@ -384,6 +390,14 @@ class DataField(xmpp.Node, object): required = property(get_required, set_required, None, """ If this is set to True, the field is required for form to be valid. """) + def iter_values(self): + assert self.type in ('list-single', 'list-multi', 'jid-multi') + + for element in self.getChildren(): + if not isinstance(element, xmpp.Node): continue + if not element.getName()=='value': continue + yield element.getData().decode('utf-8') + def get_value(self): if self.type in ('boolean',): if self.getTagData('value') in (1, 'true'): @@ -430,8 +444,10 @@ class DataField(xmpp.Node, object): for element in self.getChildren(): if not isinstance(element, xmpp.Node): continue if not element.getName()=='option': continue - if element.getTag('value') is None: raise BadDataFormNode - yield element.getAttr('label'), element.getTag('value').getData() + try: + yield element.getAttr('label'), element.getTag('value').getData() + except TypeError: + raise BadDataFormNode def get_options(self): """ Returns a list of tuples: (label, value). """ diff --git a/src/dataforms.py b/src/dataforms.py index 4d82a5885..7fc7f835c 100644 --- a/src/dataforms.py +++ b/src/dataforms.py @@ -17,6 +17,8 @@ """ This module contains widget that can display data form (JEP-0004). """ import gtk +import pango + import gtkgui_helpers import common.xmpp as xmpp @@ -27,12 +29,13 @@ class DataFormWidget(gtk.Alignment, object): """ Data Form widget. Use like any other widget. """ def __init__(self, dataformnode=None): """ Create a widget. """ - gtk.Alignment.__init__(self) + 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')) @@ -54,6 +57,18 @@ class DataFormWidget(gtk.Alignment, object): 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 @@ -81,39 +96,6 @@ class DataFormWidget(gtk.Alignment, object): """ Treat 'us' as one widget. """ self.show_all() -#? def filled_data_form(self): -#? """ 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 -#? -#? 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 in ('fixed',): -#? continue -#? -#? newfield = xmpp.Node('field', {'var': field.getVar()}) -#? -#? if ftype in ('jid-multi', 'list-multi', 'text-multi'): -#? for value in field.getValues(): -#? newvalue = xmpp.Node('value', {}, [value]) -#? newfield.addChild(node=newvalue) -#? else: -#? newvalue = xmpp.Node('value', {}, [field.getValue()]) -#? newfield.addChild(node=newvalue) -#? -#? form.addChild(node=newfield) -#? -#? return form - # "private" methods # we have actually two different kinds of data forms: one is a simple form to fill, @@ -129,6 +111,8 @@ class DataFormWidget(gtk.Alignment, object): assert dataform.mode==dataforms.DATAFORM_SINGLE gtk.Table.__init__(self) + self.set_col_spacings(6) + self.set_row_spacings(6) self._data_form = dataform @@ -162,10 +146,8 @@ class DataFormWidget(gtk.Alignment, object): commonwidget=False widget = gtk.Label(field.value) widget.set_line_wrap(True) - self.attach(widget, leftattach, rightattach, linecounter, linecounter+1) - - elif field.type in ('jid-multi'): - widget = gtk.Label(field.type) + 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 @@ -204,6 +186,42 @@ class DataFormWidget(gtk.Alignment, object): 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, bool) + for value in field.iter_values(): + # nobody will create several megabytes long stanza + listmodel.insert(999999, (value,False)) + + treeview.set_model(listmodel) + + renderer = gtk.CellRendererText() + renderer.set_property('ellipsize', pango.ELLIPSIZE_START) + 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, editable=1)) + + xml.get_widget('add_button').connect('clicked', + self.on_jid_multi_add_button_clicked, 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) + 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) @@ -214,13 +232,20 @@ class DataFormWidget(gtk.Alignment, object): elif field.type == 'text-multi': # TODO: bigger text view - widget = gtk.TextView() - widget.set_wrap_mode(gtk.WRAP_WORD) - widget.get_buffer().connect('changed', self.on_text_multi_textbuffer_changed, + 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'' - widget.get_buffer().set_text(field.value) + 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 @@ -234,11 +259,13 @@ class DataFormWidget(gtk.Alignment, object): 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) + 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) + self.attach(widget, 1, 2, linecounter, linecounter+1, + yoptions=gtk.FILL) widget.show_all() if commondesc and field.description is not None: @@ -247,7 +274,8 @@ class DataFormWidget(gtk.Alignment, object): gtkgui_helpers.escape_for_pango_markup(field.description)+\ '') label.set_line_wrap(True) - self.attach(label, 2, 3, linecounter, linecounter+1) + 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'): @@ -264,10 +292,11 @@ class DataFormWidget(gtk.Alignment, object): 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.append(value) + field.value += [value] elif not widget.get_active() and value in field.value: - field.value.remove(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() @@ -277,6 +306,31 @@ class DataFormWidget(gtk.Alignment, object): 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.values + values[values.index(old)]=newtext + field.values = values + + def on_jid_multi_add_button_clicked(self, widget, model, field): + iter = model.insert(999999, ("new@jid",)) + field.value += ["new@jid"] + + def on_jid_multi_edit_button_clicked(self, widget, treeview): + model, iter = treeview.get_selection().get_selected() + assert iter is not None + + model[iter][1]=True + + def on_jid_multi_remove_button_clicked(self, widget, model, field): + pass + + 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 -- cgit v1.2.3