dataforms_widget.py 26.7 KB
Newer Older
Philipp Hörist's avatar
Philipp Hörist committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
# Copyright (C) 2003-2014 Yann Leboulanger <asterix AT lagaule.org>
# Copyright (C) 2006 Tomasz Melcer <liori AT exroot.org>
# Copyright (C) 2006-2007 Jean-Marie Traissard <jim AT lapin.org>
#
# This file is part of Gajim.
#
# Gajim 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 3 only.
#
# Gajim 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.
#
# You should have received a copy of the GNU General Public License
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
roidelapluie's avatar
roidelapluie committed
18

Alexander Krotov's avatar
Alexander Krotov committed
19
''' This module contains widget that can display data form (XEP-0004).
20
Words single and multiple refers here to types of data forms:
21
single means these with one record of data (without <reported/> element),
Yann Leboulanger's avatar
Yann Leboulanger committed
22
multiple - these which may contain more data (with <reported/> element).'''
23

24 25 26
import itertools
import base64

27
from gi.repository import Gtk
Yann Leboulanger's avatar
Yann Leboulanger committed
28
from gi.repository import Gdk
29 30
from gi.repository import GdkPixbuf
from gi.repository import GObject
Yann Leboulanger's avatar
Yann Leboulanger committed
31
from gi.repository import GLib
32

33 34
from nbxmpp.modules import dataforms

André's avatar
André committed
35 36
from gajim import gtkgui_helpers
from gajim.common import helpers
37
from gajim.common import app
38
from gajim.common.i18n import _
39

40 41
from gajim.gtk.util import get_builder

42

43
class DataFormWidget(Gtk.Alignment):
44
# "public" interface
45 46 47 48
    """
    Data Form widget. Use like any other widget
    """

49 50
    __gsignals__ = dict(validated=(
        GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, None, ()))
51

52 53
    def __init__(self, dataformnode=None):
        ''' Create a widget. '''
54
        GObject.GObject.__init__(self, xscale=1.0, yscale=1.0)
55 56

        self._data_form = None
57
        self.selectable = False
Yann Leboulanger's avatar
Yann Leboulanger committed
58
        self.clean_cb = None
59

60
        self.xml = get_builder('data_form_window.ui', ['data_form_vbox'])
61 62 63 64 65 66 67 68 69 70 71 72 73 74 75
        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)
76
        selection.set_mode(Gtk.SelectionMode.MULTIPLE)
77

78
    def on_data_form_vbox_key_press_event(self, widget, event):
Yann Leboulanger's avatar
Yann Leboulanger committed
79
        print('key pressed')
80

81 82
    def set_data_form(self, dataform):
        """
83
        Set the data form (nbxmpp.DataForm) displayed in widget
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
        """
        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):
        """
Yann Leboulanger's avatar
Yann Leboulanger committed
117
        Get the title of data form. If no title or no form,
118
        returns ''. Useful for setting window title
119 120 121 122
        """
        if self._data_form is not None:
            if self._data_form.title is not None:
                return self._data_form.title
123
        return ''
124 125 126 127 128 129

    title = property(get_title, None, None, 'Data form title')

    def show(self):
        ''' Treat 'us' as one widget. '''
        self.show_all()
130 131 132 133

# "private" methods

# we have actually two different kinds of data forms: one is a simple form to fill,
134
# second is a table with several records;
135

136 137 138 139 140 141 142 143 144
    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
        """
Yann Leboulanger's avatar
Yann Leboulanger committed
145 146
        if self.clean_cb:
            self.clean_cb()
147 148 149 150 151 152 153

    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()

154 155
        self.singleform = SingleForm(self._data_form,
            selectable=self.selectable)
156 157 158
        def _on_validated(widget):
            self.emit('validated')
        self.singleform.connect('validated', _on_validated)
159 160 161 162 163 164
        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))

Yann Leboulanger's avatar
Yann Leboulanger committed
165
        self.clean_cb = self.clean_single_data_form
166 167 168 169 170 171 172

    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()
Yann Leboulanger's avatar
Yann Leboulanger committed
173
        self.clean_cb = None # we won't call it twice
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
        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
Yann Leboulanger's avatar
Yann Leboulanger committed
191
            #elif field.type_=='boolean': fieldtypes.append(bool)
192 193 194
            fieldtypes.append(str)
            fieldvars.append(field.var)

195
        self.multiplemodel = Gtk.ListStore(*fieldtypes)
196 197 198 199 200

        # moving all data to model
        for item in self._data_form.iter_records():
            iter_ = self.multiplemodel.append()
            for field in item.iter_fields():
201 202 203
                if field.var in fieldvars:
                    self.multiplemodel.set_value(iter_,
                        fieldvars.index(field.var), field.value)
204 205 206 207 208

        # constructing columns...
        for field, counter in zip(self._data_form.reported.iter_fields(),
        itertools.count()):
            self.records_treeview.append_column(
209
                    Gtk.TreeViewColumn(field.label, Gtk.CellRendererText(),
210 211 212 213 214 215 216 217 218
                            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))

Yann Leboulanger's avatar
Yann Leboulanger committed
219
        self.clean_cb = self.clean_multiple_data_form
220

Yann Leboulanger's avatar
Yann Leboulanger committed
221
        readwrite = self._data_form.type_ != 'result'
222 223 224 225 226 227 228 229 230 231 232 233 234
        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
        """
Yann Leboulanger's avatar
Yann Leboulanger committed
235
        self.clean_cb = None # we won't call it twice
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269
        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)

270
        if not model:
271 272 273 274 275 276 277 278 279 280 281
            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
Philipp Hörist's avatar
Philipp Hörist committed
282 283
        for index, _path in enumerate(rowrefs):
            rowrefs[index] = Gtk.TreeRowReference.new(model, rowrefs[index])
284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
        # 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()
310

311
class SingleForm(Gtk.Table):
312 313 314 315 316 317
    """
    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
    """

318 319
    __gsignals__ = dict(validated=(
        GObject.SignalFlags.RUN_LAST | GObject.SignalFlags.ACTION, None, ())
320 321
    )

322
    def __init__(self, dataform, selectable=False):
323 324
        assert isinstance(dataform, dataforms.SimpleDataForm)

325
        GObject.GObject.__init__(self)
326 327 328 329 330 331 332 333 334 335
        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 != '':
Dicson's avatar
Dicson committed
336 337
                if not widget.get_window():
                #if widget.flags() & Gtk.NO_WINDOW:
338
                    evbox = Gtk.EventBox()
339 340 341 342 343 344 345 346 347 348 349
                    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?
Yann Leboulanger's avatar
Yann Leboulanger committed
350
        readwrite = dataform.type_ != 'result'
351 352 353

        # for each field...
        for field in self._data_form.iter_fields():
354 355
            if field.type_ == 'hidden':
                continue
356 357 358 359 360 361

            commonlabel = True
            commonlabelcenter = False
            commonwidget = True
            widget = None

Yann Leboulanger's avatar
Yann Leboulanger committed
362
            if field.type_ == 'boolean':
363
                commonlabelcenter = True
364
                widget = Gtk.CheckButton()
365 366 367 368
                widget.connect('toggled', self.on_boolean_checkbutton_toggled,
                        field)
                widget.set_active(field.value)

Yann Leboulanger's avatar
Yann Leboulanger committed
369
            elif field.type_ == 'fixed':
370 371 372 373 374 375 376
                leftattach = 1
                rightattach = 2
                if field.label is None:
                    commonlabel = False
                    leftattach = 0

                commonwidget = False
377
                widget = Gtk.Label(label=field.value)
378
                widget.set_property('selectable', selectable)
379 380
                widget.set_line_wrap(True)
                self.attach(widget, leftattach, rightattach, linecounter,
381
                    linecounter + 1, xoptions=Gtk.AttachOptions.FILL,
Dicson's avatar
Dicson committed
382
                    yoptions=Gtk.AttachOptions.FILL)
383

Yann Leboulanger's avatar
Yann Leboulanger committed
384
            elif field.type_ == 'list-single':
385 386 387 388
                # 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
389
                    widget = Gtk.VBox()
390 391 392 393
                    first_radio = None
                    for value, label in field.iter_options():
                        if not label:
                            label = value
Dicson's avatar
Dicson committed
394 395
                        radio = Gtk.RadioButton.new_with_label_from_widget(
                            first_radio, label)
396
                        radio.connect('toggled',
Dicson's avatar
Dicson committed
397
                            self.on_list_single_radiobutton_toggled, field, value)
398 399 400 401 402 403
                        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)
404
                        widget.pack_start(radio, False, True, 0)
405 406 407 408 409 410 411 412 413 414 415 416 417 418
                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)

Yann Leboulanger's avatar
Yann Leboulanger committed
419
            elif field.type_ == 'list-multi':
420 421 422
                # TODO: When more than few choices, make a list
                if len(field.options) < 6:
                    # 5 option max: show checkbutton
423
                    widget = Gtk.VBox()
424
                    for value, label in field.iter_options():
Yann Leboulanger's avatar
Yann Leboulanger committed
425 426
                        check = Gtk.CheckButton(label=label,
                            use_underline=False)
427 428 429
                        check.set_active(value in field.values)
                        check.connect('toggled',
                                self.on_list_multi_checkbutton_toggled, field, value)
430
                        widget.pack_start(check, False, True, 0)
431
                        widget.set_sensitive(readwrite)
432 433
                else:
                    # more than 5 options: show combobox
Philipp Hörist's avatar
Philipp Hörist committed
434 435
                    def on_list_multi_treeview_changed(selection, field_):
                        def for_selected(treemodel, _path, iter_):
436
                            vals.append(treemodel[iter_][1])
437 438
                        vals = []
                        selection.selected_foreach(for_selected)
Philipp Hörist's avatar
Philipp Hörist committed
439
                        field_.values = vals[:]
440 441
                    widget = Gtk.ScrolledWindow()
                    widget.set_policy(Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC)
442 443 444 445 446 447
                    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)
448
                    tv.set_sensitive(readwrite)
449

Yann Leboulanger's avatar
Yann Leboulanger committed
450
            elif field.type_ == 'jid-single':
451
                widget = Gtk.Entry()
452 453 454
                widget.connect('changed', self.on_text_single_entry_changed, field)
                widget.set_text(field.value)

Yann Leboulanger's avatar
Yann Leboulanger committed
455
            elif field.type_ == 'jid-multi':
456 457
                commonwidget = False

458
                xml = get_builder('data_form_window.ui', ['multiple_form_hbox'])
459 460
                widget = xml.get_object('multiple_form_hbox')
                treeview = xml.get_object('records_treeview')
461

462
                listmodel = Gtk.ListStore(str)
463 464 465 466 467 468
                for value in field.iter_values():
                    # nobody will create several megabytes long stanza
                    listmodel.insert(999999, (value,))

                treeview.set_model(listmodel)

469
                renderer = Gtk.CellRendererText()
470 471 472 473 474
                renderer.set_property('editable', True)
                renderer.connect('edited',
                        self.on_jid_multi_cellrenderertext_edited, treeview, listmodel,
                        field)

475
                treeview.append_column(Gtk.TreeViewColumn(None, renderer,
476 477 478 479
                        text=0))

                decorate_with_tooltip(treeview, field)

480
                add_button = xml.get_object('add_button')
481 482
                add_button.connect('clicked',
                        self.on_jid_multi_add_button_clicked, treeview, listmodel, field)
483
                edit_button = xml.get_object('edit_button')
484 485
                edit_button.connect('clicked',
                        self.on_jid_multi_edit_button_clicked, treeview)
486
                remove_button = xml.get_object('remove_button')
487 488
                remove_button.connect('clicked',
                        self.on_jid_multi_remove_button_clicked, treeview, field)
489
                clear_button = xml.get_object('clear_button')
490 491 492 493 494 495 496 497 498 499 500 501 502
                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

Yann Leboulanger's avatar
Yann Leboulanger committed
503
            elif field.type_ == 'text-private':
504
                commonlabelcenter = True
505
                widget = Gtk.Entry()
506 507 508 509
                widget.connect('changed', self.on_text_single_entry_changed, field)
                widget.set_visibility(False)
                widget.set_text(field.value)

Yann Leboulanger's avatar
Yann Leboulanger committed
510
            elif field.type_ == 'text-multi':
511 512 513
                # TODO: bigger text view
                commonwidget = False

514 515
                textwidget = Gtk.TextView()
                textwidget.set_wrap_mode(Gtk.WrapMode.WORD)
516 517 518
                textwidget.get_buffer().connect('changed',
                        self.on_text_multi_textbuffer_changed, field)
                textwidget.get_buffer().set_text(field.value)
519 520 521 522 523 524 525
                if readwrite:
                    textwidget.set_sensitive(True)
                else:
                    if selectable:
                        textwidget.set_editable(True)
                    else:
                        textwidget.set_sensitive(False)
526

527
                widget = Gtk.ScrolledWindow()
528 529
                widget.add(textwidget)

530
                widget = decorate_with_tooltip(widget, field)
531 532 533
                self.attach(widget, 1, 2, linecounter, linecounter+1)

            else:
Yann Leboulanger's avatar
Yann Leboulanger committed
534
                # field.type_ == 'text-single' or field.type_ is nonstandard:
535 536 537 538
                # JEP says that if we don't understand some type, we
                # should handle it as text-single
                commonlabelcenter = True
                if readwrite:
539
                    widget = Gtk.Entry()
540
                    def kpe(widget, event):
541 542
                        if event.keyval == Gdk.KEY_Return or \
                        event.keyval == Gdk.KEY_KP_Enter:
543 544
                            self.emit('validated')
                    widget.connect('key-press-event', kpe)
545 546 547 548
                    widget.connect('changed', self.on_text_single_entry_changed,
                            field)
                    widget.set_sensitive(readwrite)
                    if field.value is None:
549
                        field.value = ''
550 551
                    widget.set_text(field.value)
                else:
552
                    commonwidget = False
553
                    widget = Gtk.Label(label=field.value)
554
                    widget.set_property('selectable', selectable)
555
                    widget.set_sensitive(True)
556 557
                    widget.set_halign(Gtk.Align.START)
                    widget.set_valign(Gtk.Align.CENTER)
558
                    widget = decorate_with_tooltip(widget, field)
559
                    self.attach(widget, 1, 2, linecounter, linecounter+1,
560
                            yoptions=Gtk.AttachOptions.FILL)
561 562

            if commonlabel and field.label is not None:
563
                label = Gtk.Label(label=field.label)
564
                if commonlabelcenter:
565 566
                    label.set_halign(Gtk.Align.START)
                    label.set_valign(Gtk.Align.CENTER)
567
                else:
568 569
                    label.set_halign(Gtk.Align.START)
                    label.set_valign(Gtk.Align.START)
570 571
                label = decorate_with_tooltip(label, field)
                self.attach(label, 0, 1, linecounter, linecounter+1,
572
                        xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL)
573

574 575 576 577
            if field.media is not None:
                for uri in field.media.uris:
                    if uri.type_.startswith('image/'):
                        try:
Yann Leboulanger's avatar
Yann Leboulanger committed
578
                            img_data = base64.b64decode(uri.uri_data)
579
                            pixbuf_l = GdkPixbuf.PixbufLoader()
580 581
                            pixbuf_l.write(img_data)
                            pixbuf_l.close()
Dicson's avatar
Dicson committed
582
                            media = Gtk.Image.new_from_pixbuf(pixbuf_l.\
583 584
                                get_pixbuf())
                        except Exception:
585
                            media = Gtk.Label(label=_('Unable to load image'))
586
                    else:
587
                        media = Gtk.Label(label=_('Media type not supported: %s') % \
588 589 590
                            uri.type_)
                    linecounter += 1
                    self.attach(media, 0, 1, linecounter, linecounter+1,
591
                        xoptions=Gtk.AttachOptions.FILL, yoptions=Gtk.AttachOptions.FILL)
592

593 594 595 596 597
            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,
598
                        yoptions=Gtk.AttachOptions.FILL)
599

600
            if field.required:
601
                label = Gtk.Label(label='*')
602 603 604
                label.set_tooltip_text(_('This field is required'))
                self.attach(label, 2, 3, linecounter, linecounter+1, xoptions=0,
                    yoptions=0)
605

606
            linecounter += 1
607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624
        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:
625
            field.values = [v for v in field.values if v != value]
626 627 628 629 630

    def on_text_single_entry_changed(self, widget, field):
        field.value = widget.get_text()

    def on_text_multi_textbuffer_changed(self, widget, field):
631 632
        field.value = widget.get_text(widget.get_start_iter(),
            widget.get_end_iter(), True)
633 634 635 636 637 638 639 640

    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)
Yann Leboulanger's avatar
Yann Leboulanger committed
641
        except helpers.InvalidFormat as s:
642
            app.interface.raise_dialog('invalid-jid-with-error', str(s))
643 644
            return
        if newtext in field.values:
645
            app.interface.raise_dialog('jid-in-list')
Yann Leboulanger's avatar
Yann Leboulanger committed
646
            GLib.idle_add(treeview.set_cursor, path)
647
            return
648
        model[path][0] = newtext
649 650

        values = field.values
651
        values[values.index(old)] = newtext
652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676
        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):
677
            deleted += model[iter_]
678 679 680 681 682 683 684 685
            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