2008-08-15 19:31:51 +02:00
|
|
|
# -*- coding:utf-8 -*-
|
2008-08-15 05:20:23 +02:00
|
|
|
## src/dataforms_widget.py
|
2006-09-13 18:07:51 +02:00
|
|
|
##
|
2014-01-02 09:33:54 +01:00
|
|
|
## Copyright (C) 2003-2014 Yann Leboulanger <asterix AT lagaule.org>
|
2008-08-15 05:20:23 +02:00
|
|
|
## Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
|
|
|
|
## Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
|
2006-09-13 18:07:51 +02:00
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## This file is part of Gajim.
|
|
|
|
##
|
|
|
|
## Gajim is free software; you can redistribute it and/or modify
|
2006-09-13 18:07:51 +02:00
|
|
|
## it under the terms of the GNU General Public License as published
|
2007-10-22 13:13:13 +02:00
|
|
|
## by the Free Software Foundation; version 3 only.
|
2006-09-13 18:07:51 +02:00
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## Gajim is distributed in the hope that it will be useful,
|
2006-09-13 18:07:51 +02:00
|
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
2008-08-15 05:20:23 +02:00
|
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2006-09-13 18:07:51 +02:00
|
|
|
## GNU General Public License for more details.
|
|
|
|
##
|
2007-10-22 13:13:13 +02:00
|
|
|
## You should have received a copy of the GNU General Public License
|
2008-08-15 05:20:23 +02:00
|
|
|
## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
2007-10-22 13:13:13 +02:00
|
|
|
##
|
2008-08-15 05:20:23 +02:00
|
|
|
|
2007-11-21 14:02:06 +01:00
|
|
|
''' This module contains widget that can display data form (JEP-0004).
|
2006-09-13 18:07:51 +02:00
|
|
|
Words single and multiple refers here to types of data forms:
|
2007-04-19 15:27:14 +02:00
|
|
|
single means these with one record of data (without <reported/> element),
|
2007-11-21 14:02:06 +01:00
|
|
|
multiple - these which may contain more data (with <reported/> element).'''
|
2006-09-13 18:07:51 +02:00
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
from gi.repository import Gtk
|
2013-01-04 23:11:07 +01:00
|
|
|
from gi.repository import Gdk
|
2012-12-23 16:23:43 +01:00
|
|
|
from gi.repository import GdkPixbuf
|
|
|
|
from gi.repository import GObject
|
2013-07-29 18:35:49 +02:00
|
|
|
from gi.repository import GLib
|
2010-06-27 23:09:07 +02:00
|
|
|
import base64
|
2006-09-13 18:07:51 +02:00
|
|
|
|
2017-06-13 23:58:06 +02:00
|
|
|
from gajim import gtkgui_helpers
|
|
|
|
from gajim import dialogs
|
2006-09-13 18:07:51 +02:00
|
|
|
|
2017-06-13 23:58:06 +02:00
|
|
|
from gajim.common import dataforms
|
|
|
|
from gajim.common import helpers
|
2006-09-13 18:07:51 +02:00
|
|
|
|
2006-09-15 10:38:01 +02:00
|
|
|
import itertools
|
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
class DataFormWidget(Gtk.Alignment, object):
|
2006-09-13 18:07:51 +02:00
|
|
|
# "public" interface
|
2010-02-08 15:08:40 +01:00
|
|
|
"""
|
|
|
|
Data Form widget. Use like any other widget
|
|
|
|
"""
|
|
|
|
|
2010-11-14 21:21:39 +01:00
|
|
|
__gsignals__ = dict(
|
2012-12-23 16:23:43 +01:00
|
|
|
validated = (GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, None, ())
|
2010-11-14 21:21:39 +01:00
|
|
|
)
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def __init__(self, dataformnode=None):
|
|
|
|
''' Create a widget. '''
|
2012-12-23 16:23:43 +01:00
|
|
|
GObject.GObject.__init__(self, xscale=1.0, yscale=1.0)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
self._data_form = None
|
2012-04-17 15:23:32 +02:00
|
|
|
self.selectable = False
|
2017-08-24 15:41:39 +02:00
|
|
|
self.clean_cb = None
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
self.xml = gtkgui_helpers.get_gtk_builder('data_form_window.ui',
|
|
|
|
'data_form_vbox')
|
|
|
|
self.xml.connect_signals(self)
|
|
|
|
for name in ('instructions_label', 'instructions_hseparator',
|
|
|
|
'single_form_viewport', 'data_form_types_notebook',
|
|
|
|
'single_form_scrolledwindow', 'multiple_form_hbox',
|
|
|
|
'records_treeview', 'buttons_vbox', 'add_button', 'remove_button',
|
|
|
|
'edit_button', 'up_button', 'down_button', 'clear_button'):
|
|
|
|
self.__dict__[name] = self.xml.get_object(name)
|
|
|
|
|
|
|
|
self.add(self.xml.get_object('data_form_vbox'))
|
|
|
|
|
|
|
|
if dataformnode is not None:
|
|
|
|
self.set_data_form(dataformnode)
|
|
|
|
|
|
|
|
selection = self.records_treeview.get_selection()
|
|
|
|
selection.connect('changed', self.on_records_selection_changed)
|
2012-12-23 16:23:43 +01:00
|
|
|
selection.set_mode(Gtk.SelectionMode.MULTIPLE)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2010-11-14 21:21:39 +01:00
|
|
|
def on_data_form_vbox_key_press_event(self, widget, event):
|
2013-01-01 19:36:56 +01:00
|
|
|
print('key pressed')
|
2010-11-14 21:21:39 +01:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def set_data_form(self, dataform):
|
|
|
|
"""
|
2012-12-09 21:37:51 +01:00
|
|
|
Set the data form (nbxmpp.DataForm) displayed in widget
|
2010-02-08 15:08:40 +01:00
|
|
|
"""
|
|
|
|
assert isinstance(dataform, dataforms.DataForm)
|
|
|
|
|
|
|
|
self.del_data_form()
|
|
|
|
self._data_form = dataform
|
|
|
|
if isinstance(dataform, dataforms.SimpleDataForm):
|
|
|
|
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 == '':
|
|
|
|
self.instructions_label.set_no_show_all(True)
|
|
|
|
self.instructions_label.hide()
|
|
|
|
else:
|
|
|
|
self.instructions_label.set_text(dataform.instructions)
|
|
|
|
gtkgui_helpers.label_set_autowrap(self.instructions_label)
|
|
|
|
|
|
|
|
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):
|
|
|
|
"""
|
2013-01-01 21:06:16 +01:00
|
|
|
Get the title of data form. If no title or no form,
|
2013-01-01 19:44:25 +01:00
|
|
|
returns ''. Useful for setting window title
|
2010-02-08 15:08:40 +01:00
|
|
|
"""
|
|
|
|
if self._data_form is not None:
|
|
|
|
if self._data_form.title is not None:
|
|
|
|
return self._data_form.title
|
2013-01-01 19:44:25 +01:00
|
|
|
return ''
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
title = property(get_title, None, None, 'Data form title')
|
|
|
|
|
|
|
|
def show(self):
|
|
|
|
''' Treat 'us' as one widget. '''
|
|
|
|
self.show_all()
|
2006-09-13 18:07:51 +02:00
|
|
|
|
|
|
|
# "private" methods
|
|
|
|
|
|
|
|
# we have actually two different kinds of data forms: one is a simple form to fill,
|
2008-12-03 22:56:12 +01:00
|
|
|
# second is a table with several records;
|
2006-09-13 18:07:51 +02:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def empty_method(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
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
|
|
|
|
"""
|
2017-08-24 15:41:39 +02:00
|
|
|
if self.clean_cb:
|
|
|
|
self.clean_cb()
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
def build_single_data_form(self):
|
|
|
|
'''Invoked when new single form is to be created.'''
|
|
|
|
assert isinstance(self._data_form, dataforms.SimpleDataForm)
|
|
|
|
|
|
|
|
self.clean_data_form()
|
|
|
|
|
2012-04-17 15:23:32 +02:00
|
|
|
self.singleform = SingleForm(self._data_form,
|
|
|
|
selectable=self.selectable)
|
2010-11-14 21:21:39 +01:00
|
|
|
def _on_validated(widget):
|
|
|
|
self.emit('validated')
|
|
|
|
self.singleform.connect('validated', _on_validated)
|
2010-02-08 15:08:40 +01:00
|
|
|
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))
|
|
|
|
|
2017-08-24 15:41:39 +02:00
|
|
|
self.clean_cb = self.clean_single_data_form
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
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()
|
2017-08-24 15:41:39 +02:00
|
|
|
self.clean_cb = None # we won't call it twice
|
2010-02-08 15:08:40 +01:00
|
|
|
del self.singleform
|
|
|
|
|
|
|
|
def build_multiple_data_form(self):
|
|
|
|
"""
|
|
|
|
Invoked when new multiple form is to be created
|
|
|
|
"""
|
|
|
|
assert isinstance(self._data_form, dataforms.MultipleDataForm)
|
|
|
|
|
|
|
|
self.clean_data_form()
|
|
|
|
|
|
|
|
# creating model for form...
|
|
|
|
fieldtypes = []
|
|
|
|
fieldvars = []
|
|
|
|
for field in self._data_form.reported.iter_fields():
|
|
|
|
# note: we store also text-private and hidden fields,
|
|
|
|
# we just do not display them.
|
|
|
|
# TODO: boolean fields
|
2012-08-22 12:55:57 +02:00
|
|
|
#elif field.type_=='boolean': fieldtypes.append(bool)
|
2010-02-08 15:08:40 +01:00
|
|
|
fieldtypes.append(str)
|
|
|
|
fieldvars.append(field.var)
|
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
self.multiplemodel = Gtk.ListStore(*fieldtypes)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
# moving all data to model
|
|
|
|
for item in self._data_form.iter_records():
|
|
|
|
iter_ = self.multiplemodel.append()
|
|
|
|
for field in item.iter_fields():
|
2011-03-01 12:01:10 +01:00
|
|
|
if field.var in fieldvars:
|
|
|
|
self.multiplemodel.set_value(iter_,
|
|
|
|
fieldvars.index(field.var), field.value)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
# constructing columns...
|
|
|
|
for field, counter in zip(self._data_form.reported.iter_fields(),
|
|
|
|
itertools.count()):
|
|
|
|
self.records_treeview.append_column(
|
2012-12-23 16:23:43 +01:00
|
|
|
Gtk.TreeViewColumn(field.label, Gtk.CellRendererText(),
|
2010-02-08 15:08:40 +01:00
|
|
|
text=counter))
|
|
|
|
|
|
|
|
self.records_treeview.set_model(self.multiplemodel)
|
|
|
|
self.records_treeview.show_all()
|
|
|
|
|
|
|
|
self.data_form_types_notebook.set_current_page(
|
|
|
|
self.data_form_types_notebook.page_num(
|
|
|
|
self.multiple_form_hbox))
|
|
|
|
|
2017-08-24 15:41:39 +02:00
|
|
|
self.clean_cb = self.clean_multiple_data_form
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
readwrite = self._data_form.type_ != 'result'
|
2010-02-08 15:08:40 +01:00
|
|
|
if not readwrite:
|
|
|
|
self.buttons_vbox.set_no_show_all(True)
|
|
|
|
self.buttons_vbox.hide()
|
|
|
|
else:
|
|
|
|
self.buttons_vbox.set_no_show_all(False)
|
|
|
|
# refresh list look
|
|
|
|
self.refresh_multiple_buttons()
|
|
|
|
|
|
|
|
def clean_multiple_data_form(self):
|
|
|
|
"""
|
|
|
|
Called as clean_data_form, read the docs of clean_data_form(). Remove
|
|
|
|
form from widget
|
|
|
|
"""
|
2017-08-24 15:41:39 +02:00
|
|
|
self.clean_cb = None # we won't call it twice
|
2010-02-08 15:08:40 +01:00
|
|
|
del self.multiplemodel
|
|
|
|
|
|
|
|
def refresh_multiple_buttons(self):
|
|
|
|
"""
|
|
|
|
Checks for treeview state and makes control buttons sensitive
|
|
|
|
"""
|
|
|
|
selection = self.records_treeview.get_selection()
|
|
|
|
model = self.records_treeview.get_model()
|
|
|
|
count = selection.count_selected_rows()
|
|
|
|
if count == 0:
|
|
|
|
self.remove_button.set_sensitive(False)
|
|
|
|
self.edit_button.set_sensitive(False)
|
|
|
|
self.up_button.set_sensitive(False)
|
|
|
|
self.down_button.set_sensitive(False)
|
|
|
|
elif count == 1:
|
|
|
|
self.remove_button.set_sensitive(True)
|
|
|
|
self.edit_button.set_sensitive(True)
|
|
|
|
_, (path,) = selection.get_selected_rows()
|
|
|
|
iter_ = model.get_iter(path)
|
|
|
|
if model.iter_next(iter_) is None:
|
|
|
|
self.up_button.set_sensitive(True)
|
|
|
|
self.down_button.set_sensitive(False)
|
|
|
|
elif path == (0, ):
|
|
|
|
self.up_button.set_sensitive(False)
|
|
|
|
self.down_button.set_sensitive(True)
|
|
|
|
else:
|
|
|
|
self.up_button.set_sensitive(True)
|
|
|
|
self.down_button.set_sensitive(True)
|
|
|
|
else:
|
|
|
|
self.remove_button.set_sensitive(True)
|
|
|
|
self.edit_button.set_sensitive(True)
|
|
|
|
self.up_button.set_sensitive(False)
|
|
|
|
self.down_button.set_sensitive(False)
|
|
|
|
|
|
|
|
if len(model) == 0:
|
|
|
|
self.clear_button.set_sensitive(False)
|
|
|
|
else:
|
|
|
|
self.clear_button.set_sensitive(True)
|
|
|
|
|
|
|
|
def on_clear_button_clicked(self, widget):
|
|
|
|
self.records_treeview.get_model().clear()
|
|
|
|
|
|
|
|
def on_remove_button_clicked(self, widget):
|
|
|
|
selection = self.records_treeview.get_selection()
|
|
|
|
model, rowrefs = selection.get_selected_rows()
|
|
|
|
# rowref is a list of paths
|
2017-02-07 21:05:27 +01:00
|
|
|
for i in range(len(rowrefs)):
|
2013-01-11 19:54:12 +01:00
|
|
|
rowrefs[i] = Gtk.TreeRowReference.new(model, rowrefs[i])
|
2010-02-08 15:08:40 +01:00
|
|
|
# rowref is a list of row references; need to convert because we will
|
|
|
|
# modify the model, paths would change
|
|
|
|
for rowref in rowrefs:
|
|
|
|
del model[rowref.get_path()]
|
|
|
|
|
|
|
|
def on_up_button_clicked(self, widget):
|
|
|
|
selection = self.records_treeview.get_selection()
|
|
|
|
model, (path,) = selection.get_selected_rows()
|
|
|
|
iter_ = model.get_iter(path)
|
|
|
|
# constructing path for previous iter
|
|
|
|
previter = model.get_iter((path[0]-1,))
|
|
|
|
model.swap(iter_, previter)
|
|
|
|
|
|
|
|
self.refresh_multiple_buttons()
|
|
|
|
|
|
|
|
def on_down_button_clicked(self, widget):
|
|
|
|
selection = self.records_treeview.get_selection()
|
|
|
|
model, (path,) = selection.get_selected_rows()
|
|
|
|
iter_ = model.get_iter(path)
|
|
|
|
nextiter = model.iter_next(iter_)
|
|
|
|
model.swap(iter_, nextiter)
|
|
|
|
|
|
|
|
self.refresh_multiple_buttons()
|
|
|
|
|
|
|
|
def on_records_selection_changed(self, widget):
|
|
|
|
self.refresh_multiple_buttons()
|
2006-09-13 18:07:51 +02:00
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
class SingleForm(Gtk.Table, object):
|
2010-02-08 15:08:40 +01:00
|
|
|
"""
|
|
|
|
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
|
|
|
|
"""
|
|
|
|
|
2010-11-14 21:21:39 +01:00
|
|
|
__gsignals__ = dict(
|
2012-12-23 16:23:43 +01:00
|
|
|
validated = (GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, None, ())
|
2010-11-14 21:21:39 +01:00
|
|
|
)
|
|
|
|
|
2012-04-17 15:23:32 +02:00
|
|
|
def __init__(self, dataform, selectable=False):
|
2010-02-08 15:08:40 +01:00
|
|
|
assert isinstance(dataform, dataforms.SimpleDataForm)
|
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
GObject.GObject.__init__(self)
|
2010-02-08 15:08:40 +01:00
|
|
|
self.set_col_spacings(12)
|
|
|
|
self.set_row_spacings(6)
|
|
|
|
|
|
|
|
def decorate_with_tooltip(widget, field):
|
|
|
|
"""
|
|
|
|
Adds a tooltip containing field's description to a widget. Creates
|
|
|
|
EventBox if widget doesn't have its own gdk window. Returns decorated
|
|
|
|
widget
|
|
|
|
"""
|
|
|
|
if field.description != '':
|
2013-01-11 17:44:29 +01:00
|
|
|
if not widget.get_window():
|
|
|
|
#if widget.flags() & Gtk.NO_WINDOW:
|
2012-12-23 16:23:43 +01:00
|
|
|
evbox = Gtk.EventBox()
|
2010-02-08 15:08:40 +01:00
|
|
|
evbox.add(widget)
|
|
|
|
widget = evbox
|
|
|
|
widget.set_tooltip_text(field.description)
|
|
|
|
return widget
|
|
|
|
|
|
|
|
self._data_form = dataform
|
|
|
|
|
|
|
|
# building widget
|
|
|
|
linecounter = 0
|
|
|
|
|
|
|
|
# is the form changeable?
|
2012-08-22 12:55:57 +02:00
|
|
|
readwrite = dataform.type_ != 'result'
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
# for each field...
|
|
|
|
for field in self._data_form.iter_fields():
|
2012-08-22 12:55:57 +02:00
|
|
|
if field.type_ == 'hidden': continue
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
commonlabel = True
|
|
|
|
commonlabelcenter = False
|
|
|
|
commonwidget = True
|
|
|
|
widget = None
|
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
if field.type_ == 'boolean':
|
2010-02-08 15:08:40 +01:00
|
|
|
commonlabelcenter = True
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.CheckButton()
|
2010-02-08 15:08:40 +01:00
|
|
|
widget.connect('toggled', self.on_boolean_checkbutton_toggled,
|
|
|
|
field)
|
|
|
|
widget.set_active(field.value)
|
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
elif field.type_ == 'fixed':
|
2010-02-08 15:08:40 +01:00
|
|
|
leftattach = 1
|
|
|
|
rightattach = 2
|
|
|
|
if field.label is None:
|
|
|
|
commonlabel = False
|
|
|
|
leftattach = 0
|
|
|
|
|
|
|
|
commonwidget = False
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.Label(label=field.value)
|
2012-04-17 15:23:32 +02:00
|
|
|
widget.set_property('selectable', selectable)
|
2010-02-08 15:08:40 +01:00
|
|
|
widget.set_line_wrap(True)
|
|
|
|
self.attach(widget, leftattach, rightattach, linecounter,
|
2013-01-07 11:54:29 +01:00
|
|
|
linecounter+1, xoptions=Gtk.AttachOptions.FILL,
|
|
|
|
yoptions=Gtk.AttachOptions.FILL)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
elif field.type_ == 'list-single':
|
2010-02-08 15:08:40 +01:00
|
|
|
# TODO: What if we have radio buttons and non-required field?
|
|
|
|
# TODO: We cannot deactivate them all...
|
|
|
|
if len(field.options) < 6:
|
|
|
|
# 5 option max: show radiobutton
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.VBox()
|
2010-02-08 15:08:40 +01:00
|
|
|
first_radio = None
|
|
|
|
for value, label in field.iter_options():
|
|
|
|
if not label:
|
|
|
|
label = value
|
2013-01-07 11:54:29 +01:00
|
|
|
radio = Gtk.RadioButton.new_with_label_from_widget(
|
|
|
|
first_radio, label)
|
2010-02-08 15:08:40 +01:00
|
|
|
radio.connect('toggled',
|
2013-01-07 11:54:29 +01:00
|
|
|
self.on_list_single_radiobutton_toggled, field, value)
|
2010-02-08 15:08:40 +01:00
|
|
|
if first_radio is None:
|
|
|
|
first_radio = radio
|
|
|
|
if field.value == '': # TODO: is None when done
|
|
|
|
field.value = value
|
|
|
|
if value == field.value:
|
|
|
|
radio.set_active(True)
|
2012-12-23 16:23:43 +01:00
|
|
|
widget.pack_start(radio, False, True, 0)
|
2010-02-08 15:08:40 +01:00
|
|
|
else:
|
|
|
|
# more than 5 options: show combobox
|
|
|
|
def on_list_single_combobox_changed(combobox, f):
|
|
|
|
iter_ = combobox.get_active_iter()
|
|
|
|
if iter_:
|
|
|
|
model = combobox.get_model()
|
|
|
|
f.value = model[iter_][1]
|
|
|
|
else:
|
|
|
|
f.value = ''
|
|
|
|
widget = gtkgui_helpers.create_combobox(field.options,
|
|
|
|
field.value)
|
|
|
|
widget.connect('changed', on_list_single_combobox_changed, field)
|
|
|
|
widget.set_sensitive(readwrite)
|
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
elif field.type_ == 'list-multi':
|
2010-02-08 15:08:40 +01:00
|
|
|
# TODO: When more than few choices, make a list
|
|
|
|
if len(field.options) < 6:
|
|
|
|
# 5 option max: show checkbutton
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.VBox()
|
2010-02-08 15:08:40 +01:00
|
|
|
for value, label in field.iter_options():
|
2015-07-30 15:18:03 +02:00
|
|
|
check = Gtk.CheckButton(label=label,
|
|
|
|
use_underline=False)
|
2010-02-08 15:08:40 +01:00
|
|
|
check.set_active(value in field.values)
|
|
|
|
check.connect('toggled',
|
|
|
|
self.on_list_multi_checkbutton_toggled, field, value)
|
2012-12-23 16:23:43 +01:00
|
|
|
widget.pack_start(check, False, True, 0)
|
2010-08-06 00:10:35 +02:00
|
|
|
widget.set_sensitive(readwrite)
|
2010-02-08 15:08:40 +01:00
|
|
|
else:
|
|
|
|
# more than 5 options: show combobox
|
|
|
|
def on_list_multi_treeview_changed(selection, f):
|
|
|
|
def for_selected(treemodel, path, iter):
|
|
|
|
vals.append(treemodel[iter][1])
|
|
|
|
vals = []
|
|
|
|
selection.selected_foreach(for_selected)
|
|
|
|
field.values = vals[:]
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.ScrolledWindow()
|
|
|
|
widget.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
|
2010-02-08 15:08:40 +01:00
|
|
|
tv = gtkgui_helpers.create_list_multi(field.options,
|
|
|
|
field.values)
|
|
|
|
widget.add(tv)
|
|
|
|
widget.set_size_request(-1, 120)
|
|
|
|
tv.get_selection().connect('changed',
|
|
|
|
on_list_multi_treeview_changed, field)
|
2010-08-06 00:10:35 +02:00
|
|
|
tv.set_sensitive(readwrite)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
elif field.type_ == 'jid-single':
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.Entry()
|
2010-02-08 15:08:40 +01:00
|
|
|
widget.connect('changed', self.on_text_single_entry_changed, field)
|
|
|
|
widget.set_text(field.value)
|
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
elif field.type_ == 'jid-multi':
|
2010-02-08 15:08:40 +01:00
|
|
|
commonwidget = False
|
|
|
|
|
|
|
|
xml = gtkgui_helpers.get_gtk_builder('data_form_window.ui',
|
2010-02-19 13:54:42 +01:00
|
|
|
'multiple_form_hbox')
|
|
|
|
widget = xml.get_object('multiple_form_hbox')
|
|
|
|
treeview = xml.get_object('records_treeview')
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
listmodel = Gtk.ListStore(str)
|
2010-02-08 15:08:40 +01:00
|
|
|
for value in field.iter_values():
|
|
|
|
# nobody will create several megabytes long stanza
|
|
|
|
listmodel.insert(999999, (value,))
|
|
|
|
|
|
|
|
treeview.set_model(listmodel)
|
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
renderer = Gtk.CellRendererText()
|
2010-02-08 15:08:40 +01:00
|
|
|
renderer.set_property('editable', True)
|
|
|
|
renderer.connect('edited',
|
|
|
|
self.on_jid_multi_cellrenderertext_edited, treeview, listmodel,
|
|
|
|
field)
|
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
treeview.append_column(Gtk.TreeViewColumn(None, renderer,
|
2010-02-08 15:08:40 +01:00
|
|
|
text=0))
|
|
|
|
|
|
|
|
decorate_with_tooltip(treeview, field)
|
|
|
|
|
|
|
|
add_button=xml.get_object('add_button')
|
|
|
|
add_button.connect('clicked',
|
|
|
|
self.on_jid_multi_add_button_clicked, treeview, listmodel, field)
|
|
|
|
edit_button=xml.get_object('edit_button')
|
|
|
|
edit_button.connect('clicked',
|
|
|
|
self.on_jid_multi_edit_button_clicked, treeview)
|
|
|
|
remove_button=xml.get_object('remove_button')
|
|
|
|
remove_button.connect('clicked',
|
|
|
|
self.on_jid_multi_remove_button_clicked, treeview, field)
|
|
|
|
clear_button=xml.get_object('clear_button')
|
|
|
|
clear_button.connect('clicked',
|
|
|
|
self.on_jid_multi_clean_button_clicked, listmodel, field)
|
|
|
|
if not readwrite:
|
|
|
|
add_button.set_no_show_all(True)
|
|
|
|
edit_button.set_no_show_all(True)
|
|
|
|
remove_button.set_no_show_all(True)
|
|
|
|
clear_button.set_no_show_all(True)
|
|
|
|
|
|
|
|
widget.set_sensitive(readwrite)
|
|
|
|
self.attach(widget, 1, 2, linecounter, linecounter+1)
|
|
|
|
|
|
|
|
del xml
|
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
elif field.type_ == 'text-private':
|
2010-02-08 15:08:40 +01:00
|
|
|
commonlabelcenter = True
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.Entry()
|
2010-02-08 15:08:40 +01:00
|
|
|
widget.connect('changed', self.on_text_single_entry_changed, field)
|
|
|
|
widget.set_visibility(False)
|
|
|
|
widget.set_text(field.value)
|
|
|
|
|
2012-08-22 12:55:57 +02:00
|
|
|
elif field.type_ == 'text-multi':
|
2010-02-08 15:08:40 +01:00
|
|
|
# TODO: bigger text view
|
|
|
|
commonwidget = False
|
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
textwidget = Gtk.TextView()
|
|
|
|
textwidget.set_wrap_mode(Gtk.WrapMode.WORD)
|
2010-02-08 15:08:40 +01:00
|
|
|
textwidget.get_buffer().connect('changed',
|
|
|
|
self.on_text_multi_textbuffer_changed, field)
|
|
|
|
textwidget.get_buffer().set_text(field.value)
|
2012-04-17 15:23:32 +02:00
|
|
|
if readwrite:
|
|
|
|
textwidget.set_sensitive(True)
|
|
|
|
else:
|
|
|
|
if selectable:
|
|
|
|
textwidget.set_editable(True)
|
|
|
|
else:
|
|
|
|
textwidget.set_sensitive(False)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.ScrolledWindow()
|
2010-02-08 15:08:40 +01:00
|
|
|
widget.add(textwidget)
|
|
|
|
|
|
|
|
widget=decorate_with_tooltip(widget, field)
|
|
|
|
self.attach(widget, 1, 2, linecounter, linecounter+1)
|
|
|
|
|
|
|
|
else:
|
2012-08-22 12:55:57 +02:00
|
|
|
# field.type_ == 'text-single' or field.type_ is nonstandard:
|
2010-02-08 15:08:40 +01:00
|
|
|
# JEP says that if we don't understand some type, we
|
|
|
|
# should handle it as text-single
|
|
|
|
commonlabelcenter = True
|
|
|
|
if readwrite:
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.Entry()
|
2010-11-14 21:21:39 +01:00
|
|
|
def kpe(widget, event):
|
2012-12-23 16:23:43 +01:00
|
|
|
if event.keyval == Gdk.KEY_Return or \
|
|
|
|
event.keyval == Gdk.KEY_KP_Enter:
|
2010-11-14 21:21:39 +01:00
|
|
|
self.emit('validated')
|
|
|
|
widget.connect('key-press-event', kpe)
|
2010-02-08 15:08:40 +01:00
|
|
|
widget.connect('changed', self.on_text_single_entry_changed,
|
|
|
|
field)
|
|
|
|
widget.set_sensitive(readwrite)
|
|
|
|
if field.value is None:
|
2013-01-01 19:44:25 +01:00
|
|
|
field.value = ''
|
2010-02-08 15:08:40 +01:00
|
|
|
widget.set_text(field.value)
|
|
|
|
else:
|
|
|
|
commonwidget=False
|
2012-12-23 16:23:43 +01:00
|
|
|
widget = Gtk.Label(label=field.value)
|
2012-04-17 15:23:32 +02:00
|
|
|
widget.set_property('selectable', selectable)
|
2010-02-08 15:08:40 +01:00
|
|
|
widget.set_sensitive(True)
|
2016-09-25 23:44:38 +02:00
|
|
|
widget.set_halign(Gtk.Align.START)
|
|
|
|
widget.set_valign(Gtk.Align.CENTER)
|
2010-02-08 15:08:40 +01:00
|
|
|
widget=decorate_with_tooltip(widget, field)
|
|
|
|
self.attach(widget, 1, 2, linecounter, linecounter+1,
|
2012-12-23 16:23:43 +01:00
|
|
|
yoptions=Gtk.AttachOptions.FILL)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
if commonlabel and field.label is not None:
|
2012-12-23 16:23:43 +01:00
|
|
|
label = Gtk.Label(label=field.label)
|
2010-02-08 15:08:40 +01:00
|
|
|
if commonlabelcenter:
|
2016-09-25 23:44:38 +02:00
|
|
|
label.set_halign(Gtk.Align.START)
|
|
|
|
label.set_valign(Gtk.Align.CENTER)
|
2010-02-08 15:08:40 +01:00
|
|
|
else:
|
2016-09-25 23:44:38 +02:00
|
|
|
label.set_halign(Gtk.Align.START)
|
|
|
|
label.set_valign(Gtk.Align.START)
|
2010-02-08 15:08:40 +01:00
|
|
|
label = decorate_with_tooltip(label, field)
|
|
|
|
self.attach(label, 0, 1, linecounter, linecounter+1,
|
2012-12-23 16:23:43 +01:00
|
|
|
xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
2010-06-27 23:09:07 +02:00
|
|
|
if field.media is not None:
|
|
|
|
for uri in field.media.uris:
|
|
|
|
if uri.type_.startswith('image/'):
|
|
|
|
try:
|
2013-01-04 23:11:07 +01:00
|
|
|
img_data = base64.b64decode(uri.uri_data)
|
2012-12-23 16:23:43 +01:00
|
|
|
pixbuf_l = GdkPixbuf.PixbufLoader()
|
2010-06-27 23:09:07 +02:00
|
|
|
pixbuf_l.write(img_data)
|
|
|
|
pixbuf_l.close()
|
2013-01-02 10:32:17 +01:00
|
|
|
media = Gtk.Image.new_from_pixbuf(pixbuf_l.\
|
2010-06-27 23:09:07 +02:00
|
|
|
get_pixbuf())
|
|
|
|
except Exception:
|
2012-12-23 16:23:43 +01:00
|
|
|
media = Gtk.Label(label=_('Unable to load image'))
|
2010-06-27 23:09:07 +02:00
|
|
|
else:
|
2012-12-23 16:23:43 +01:00
|
|
|
media = Gtk.Label(label=_('Media type not supported: %s') % \
|
2010-06-27 23:09:07 +02:00
|
|
|
uri.type_)
|
|
|
|
linecounter += 1
|
|
|
|
self.attach(media, 0, 1, linecounter, linecounter+1,
|
2012-12-23 16:23:43 +01:00
|
|
|
xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL)
|
2010-06-27 23:09:07 +02:00
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
if commonwidget:
|
|
|
|
assert widget is not None
|
|
|
|
widget.set_sensitive(readwrite)
|
|
|
|
widget = decorate_with_tooltip(widget, field)
|
|
|
|
self.attach(widget, 1, 2, linecounter, linecounter+1,
|
2012-12-23 16:23:43 +01:00
|
|
|
yoptions=Gtk.AttachOptions.FILL)
|
2011-03-01 12:01:10 +01:00
|
|
|
|
2010-08-11 09:18:15 +02:00
|
|
|
if field.required:
|
2012-12-23 16:23:43 +01:00
|
|
|
label = Gtk.Label(label='*')
|
2010-08-11 09:18:15 +02:00
|
|
|
label.set_tooltip_text(_('This field is required'))
|
|
|
|
self.attach(label, 2, 3, linecounter, linecounter+1, xoptions=0,
|
|
|
|
yoptions=0)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
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.values:
|
|
|
|
field.values += [value]
|
|
|
|
elif not widget.get_active() and value in field.values:
|
|
|
|
field.values = [v for v in field.values 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):
|
2012-12-23 16:23:43 +01:00
|
|
|
field.value = widget.get_text(widget.get_start_iter(),
|
|
|
|
widget.get_end_iter(), True)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
def on_jid_multi_cellrenderertext_edited(self, cell, path, newtext, treeview,
|
|
|
|
model, field):
|
|
|
|
old = model[path][0]
|
|
|
|
if old == newtext:
|
|
|
|
return
|
|
|
|
try:
|
|
|
|
newtext = helpers.parse_jid(newtext)
|
2013-01-01 23:18:36 +01:00
|
|
|
except helpers.InvalidFormat as s:
|
2017-02-04 23:29:45 +01:00
|
|
|
dialogs.ErrorDialog(_('Invalid JID'), str(s))
|
2010-02-08 15:08:40 +01:00
|
|
|
return
|
|
|
|
if newtext in field.values:
|
|
|
|
dialogs.ErrorDialog(
|
2017-02-04 23:29:45 +01:00
|
|
|
_('JID already in list'),
|
|
|
|
_('The JID you entered is already in the list. Choose another one.'))
|
2013-07-29 18:35:49 +02:00
|
|
|
GLib.idle_add(treeview.set_cursor, path)
|
2010-02-08 15:08:40 +01:00
|
|
|
return
|
|
|
|
model[path][0]=newtext
|
|
|
|
|
|
|
|
values = field.values
|
|
|
|
values[values.index(old)]=newtext
|
|
|
|
field.values = values
|
|
|
|
|
|
|
|
def on_jid_multi_add_button_clicked(self, widget, treeview, model, field):
|
|
|
|
#Default jid
|
|
|
|
jid = _('new@jabber.id')
|
|
|
|
if jid in field.values:
|
|
|
|
i = 1
|
|
|
|
while _('new%d@jabber.id') % i in field.values:
|
|
|
|
i += 1
|
|
|
|
jid = _('new%d@jabber.id') % i
|
|
|
|
iter_ = model.insert(999999, (jid,))
|
|
|
|
treeview.set_cursor(model.get_path(iter_), treeview.get_column(0), True)
|
|
|
|
field.values = field.values + [jid]
|
|
|
|
|
|
|
|
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()
|
|
|
|
deleted = []
|
|
|
|
|
|
|
|
def remove(model, path, iter_, deleted):
|
|
|
|
deleted+=model[iter_]
|
|
|
|
model.remove(iter_)
|
|
|
|
|
|
|
|
selection.selected_foreach(remove, deleted)
|
|
|
|
field.values = (v for v in field.values if v not in deleted)
|
|
|
|
|
|
|
|
def on_jid_multi_clean_button_clicked(self, widget, model, field):
|
|
|
|
model.clear()
|
|
|
|
del field.values
|