1# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com>
2#
3# This file is part of Gajim.
4#
5# Gajim is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published
7# by the Free Software Foundation; version 3 only.
8#
9# Gajim is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13#
14# You should have received a copy of the GNU General Public License
15# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
16
17from gi.repository import Gtk
18from gi.repository import GLib
19from gi.repository import Gdk
20from gi.repository import Pango
21
22from gajim.common import app
23from gajim.common import passwords
24from gajim.common.i18n import _
25from gajim.common.i18n import Q_
26
27from gajim import gtkgui_helpers
28
29from .util import get_image_button
30from .util import MaxWidthComboBoxText
31from .util import open_window
32from .const import SettingKind
33from .const import SettingType
34
35
36class SettingsDialog(Gtk.ApplicationWindow):
37    def __init__(self, parent, title, flags, settings, account,
38                 extend=None):
39        Gtk.ApplicationWindow.__init__(self)
40        self.set_application(app.app)
41        self.set_show_menubar(False)
42        self.set_title(title)
43        self.set_transient_for(parent)
44        self.set_resizable(False)
45        self.set_default_size(250, -1)
46        self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
47        self.get_style_context().add_class('settings-dialog')
48        self.account = account
49        if flags == Gtk.DialogFlags.MODAL:
50            self.set_modal(True)
51        elif flags == Gtk.DialogFlags.DESTROY_WITH_PARENT:
52            self.set_destroy_with_parent(True)
53
54        self.listbox = SettingsBox(account, extend=extend)
55        self.listbox.set_hexpand(True)
56        self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
57
58        for setting in settings:
59            self.listbox.add_setting(setting)
60        self.listbox.update_states()
61
62        self.add(self.listbox)
63
64        self.show_all()
65        self.connect_after('key-press-event', self.on_key_press)
66
67    def on_key_press(self, _widget, event):
68        if event.keyval == Gdk.KEY_Escape:
69            self.destroy()
70
71    def get_setting(self, name):
72        return self.listbox.get_setting(name)
73
74
75class SettingsBox(Gtk.ListBox):
76    def __init__(self, account=None, jid=None, extend=None):
77        Gtk.ListBox.__init__(self)
78        self.get_style_context().add_class('settings-box')
79        self.account = account
80        self.jid = jid
81        self.named_settings = {}
82
83        self.map = {
84            SettingKind.SWITCH: SwitchSetting,
85            SettingKind.SPIN: SpinSetting,
86            SettingKind.DIALOG: DialogSetting,
87            SettingKind.ENTRY: EntrySetting,
88            SettingKind.COLOR: ColorSetting,
89            SettingKind.ACTION: ActionSetting,
90            SettingKind.LOGIN: LoginSetting,
91            SettingKind.FILECHOOSER: FileChooserSetting,
92            SettingKind.CALLBACK: CallbackSetting,
93            SettingKind.PRIORITY: PrioritySetting,
94            SettingKind.HOSTNAME: CutstomHostnameSetting,
95            SettingKind.CHANGEPASSWORD: ChangePasswordSetting,
96            SettingKind.COMBO: ComboSetting,
97            SettingKind.POPOVER: PopoverSetting,
98            SettingKind.AUTO_AWAY: CutstomAutoAwaySetting,
99            SettingKind.AUTO_EXTENDED_AWAY: CutstomAutoExtendedAwaySetting,
100            SettingKind.USE_STUN_SERVER: CustomStunServerSetting,
101            SettingKind.NOTIFICATIONS: NotificationsSetting,
102        }
103
104        if extend is not None:
105            for setting, callback in extend:
106                self.map[setting] = callback
107
108        self.connect('row-activated', self.on_row_activated)
109
110    @staticmethod
111    def on_row_activated(_listbox, row):
112        row.on_row_activated()
113
114    def add_setting(self, setting):
115        if not isinstance(setting, Gtk.ListBoxRow):
116            if setting.props is not None:
117                listitem = self.map[setting.kind](self.account,
118                                                  self.jid,
119                                                  *setting[1:-1],
120                                                  **setting.props)
121            else:
122                listitem = self.map[setting.kind](self.account,
123                                                  self.jid,
124                                                  *setting[1:-1])
125
126        if setting.name is not None:
127            self.named_settings[setting.name] = listitem
128        self.add(listitem)
129
130    def get_setting(self, name):
131        return self.named_settings[name]
132
133    def update_states(self):
134        for row in self.get_children():
135            row.update_activatable()
136
137
138class GenericSetting(Gtk.ListBoxRow):
139    def __init__(self,
140                 account,
141                 jid,
142                 label,
143                 type_,
144                 value,
145                 name,
146                 callback,
147                 data,
148                 desc,
149                 bind,
150                 inverted,
151                 enabled_func):
152
153        Gtk.ListBoxRow.__init__(self)
154        self._grid = Gtk.Grid()
155        self._grid.set_size_request(-1, 30)
156        self._grid.set_column_spacing(12)
157
158        self.callback = callback
159        self.type_ = type_
160        self.value = value
161        self.data = data
162        self.label = label
163        self.account = account
164        self.jid = jid
165        self.name = name
166        self.bind = bind
167        self.inverted = inverted
168        self.enabled_func = enabled_func
169        self.setting_value = self.get_value()
170
171        description_box = Gtk.Box(
172            orientation=Gtk.Orientation.VERTICAL, spacing=0)
173        description_box.set_valign(Gtk.Align.CENTER)
174
175        settingtext = Gtk.Label(label=label)
176        settingtext.set_hexpand(True)
177        settingtext.set_halign(Gtk.Align.START)
178        settingtext.set_valign(Gtk.Align.CENTER)
179        settingtext.set_vexpand(True)
180        description_box.add(settingtext)
181
182        if desc is not None:
183            description = Gtk.Label(label=desc)
184            description.set_name('SubDescription')
185            description.set_hexpand(True)
186            description.set_halign(Gtk.Align.START)
187            description.set_valign(Gtk.Align.CENTER)
188            description.set_xalign(0)
189            description.set_line_wrap(True)
190            description.set_line_wrap_mode(Pango.WrapMode.WORD)
191            description.set_max_width_chars(50)
192            description_box.add(description)
193
194        self._grid.add(description_box)
195
196        self.setting_box = Gtk.Box(spacing=12)
197        self.setting_box.set_size_request(200, -1)
198        self.setting_box.set_valign(Gtk.Align.CENTER)
199        self.setting_box.set_name('GenericSettingBox')
200        self._grid.add(self.setting_box)
201        self.add(self._grid)
202
203        self._bind_sensitive_state()
204
205    def _bind_sensitive_state(self):
206        if self.bind is None:
207            return
208
209        bind_setting_type, setting, account, jid = self._parse_bind()
210
211        app.settings.bind_signal(setting,
212                                 self,
213                                 'set_sensitive',
214                                 account=account,
215                                 jid=jid,
216                                 inverted=self.inverted)
217
218        if bind_setting_type == SettingType.CONTACT:
219            value = app.settings.get_contact_setting(account, jid, setting)
220
221        elif bind_setting_type == SettingType.GROUP_CHAT:
222            value = app.settings.get_group_chat_setting(account, jid, setting)
223
224        elif bind_setting_type == SettingType.ACCOUNT_CONFIG:
225            value = app.settings.get_account_setting(account, setting)
226
227        else:
228            value = app.settings.get(setting)
229
230        if self.inverted:
231            value = not value
232        self.set_sensitive(value)
233
234    def _parse_bind(self):
235        if '::' not in self.bind:
236            return SettingType.CONFIG, self.bind, None, None
237
238        bind_setting_type, setting = self.bind.split('::')
239        if bind_setting_type == 'account':
240            return SettingType.ACCOUNT_CONFIG, setting, self.account, None
241
242        if bind_setting_type == 'contact':
243            return SettingType.CONTACT, setting, self.account, self.jid
244
245        if bind_setting_type == 'group_chat':
246            return SettingType.GROUP_CHAT, setting, self.account, self.jid
247        raise ValueError(f'Invalid bind argument: {self.bind}')
248
249    def get_value(self):
250        return self.__get_value(self.type_,
251                                self.value,
252                                self.account,
253                                self.jid)
254
255    @staticmethod
256    def __get_value(type_, value, account, jid):
257        if value is None:
258            return None
259        if type_ == SettingType.VALUE:
260            return value
261
262        if type_ == SettingType.CONTACT:
263            return app.settings.get_contact_setting(account, jid, value)
264
265        if type_ == SettingType.GROUP_CHAT:
266            return app.settings.get_group_chat_setting(
267                account, jid, value)
268
269        if type_ == SettingType.CONFIG:
270            return app.settings.get(value)
271
272        if type_ == SettingType.ACCOUNT_CONFIG:
273            if value == 'password':
274                return passwords.get_password(account)
275            if value == 'no_log_for':
276                no_log = app.settings.get_account_setting(
277                    account, 'no_log_for').split()
278                return account not in no_log
279            return app.settings.get_account_setting(account, value)
280
281        if type_ == SettingType.ACTION:
282            if value.startswith('-'):
283                return account + value
284            return value
285
286        raise ValueError('Wrong SettingType?')
287
288    def set_value(self, state):
289        if self.type_ == SettingType.CONFIG:
290            app.settings.set(self.value, state)
291
292        elif self.type_ == SettingType.ACCOUNT_CONFIG:
293            if self.value == 'password':
294                passwords.save_password(self.account, state)
295            if self.value == 'no_log_for':
296                self.set_no_log_for(self.account, state)
297            else:
298                app.settings.set_account_setting(self.account,
299                                                 self.value,
300                                                 state)
301
302        elif self.type_ == SettingType.CONTACT:
303            app.settings.set_contact_setting(
304                self.account, self.jid, self.value, state)
305
306        elif self.type_ == SettingType.GROUP_CHAT:
307            app.settings.set_group_chat_setting(
308                self.account, self.jid, self.value, state)
309
310        if self.callback is not None:
311            self.callback(state, self.data)
312
313    @staticmethod
314    def set_no_log_for(account, state):
315        no_log = app.settings.get_account_setting(account, 'no_log_for').split()
316        if state and account in no_log:
317            no_log.remove(account)
318        elif not state and account not in no_log:
319            no_log.append(account)
320        app.settings.set_account_setting(account,
321                                         'no_log_for',
322                                         ' '.join(no_log))
323
324    def on_row_activated(self):
325        raise NotImplementedError
326
327    def update_activatable(self):
328        if self.enabled_func is None:
329            return
330
331        enabled_func_value = self.enabled_func()
332        self.set_activatable(enabled_func_value)
333        self.set_sensitive(enabled_func_value)
334
335    def _add_action_button(self, kwargs):
336        icon_name = kwargs.get('button-icon-name')
337        button_text = kwargs.get('button-text')
338        tooltip_text = kwargs.get('button-tooltip') or ''
339        style = kwargs.get('button-style')
340
341        if icon_name is not None:
342            button = Gtk.Button.new_from_icon_name(icon_name, Gtk.IconSize.MENU)
343
344        elif button_text is not None:
345            button = Gtk.Button(label=button_text)
346
347        else:
348            return
349
350        if style is not None:
351            for css_class in style.split(' '):
352                button.get_style_context().add_class(css_class)
353
354        button.connect('clicked', kwargs['button-callback'])
355        button.set_tooltip_text(tooltip_text)
356        self.setting_box.add(button)
357
358
359class SwitchSetting(GenericSetting):
360    def __init__(self, *args, **kwargs):
361        GenericSetting.__init__(self, *args)
362
363        self.switch = Gtk.Switch()
364        if self.type_ == SettingType.ACTION:
365            self.switch.set_action_name('app.%s' % self.setting_value)
366            state = app.app.get_action_state(self.setting_value)
367            self.switch.set_active(state.get_boolean())
368        else:
369            self.switch.set_active(self.setting_value)
370        self.switch.connect('notify::active', self.on_switch)
371        self.switch.set_hexpand(True)
372        self.switch.set_halign(Gtk.Align.END)
373        self.switch.set_valign(Gtk.Align.CENTER)
374
375        self._switch_state_label = Gtk.Label()
376        self._switch_state_label.set_xalign(1)
377        self._switch_state_label.set_valign(Gtk.Align.CENTER)
378        self._set_label(self.setting_value)
379
380        box = Gtk.Box(spacing=12)
381        box.set_halign(Gtk.Align.END)
382        box.add(self._switch_state_label)
383        box.add(self.switch)
384        self.setting_box.add(box)
385
386        self._add_action_button(kwargs)
387
388        self.show_all()
389
390    def on_row_activated(self):
391        state = self.switch.get_active()
392        self.switch.set_active(not state)
393
394    def on_switch(self, switch, *args):
395        value = switch.get_active()
396        self.set_value(value)
397        self._set_label(value)
398
399    def _set_label(self, active):
400        text = Q_('?switch:On') if active else Q_('?switch:Off')
401        self._switch_state_label.set_text(text)
402
403
404class EntrySetting(GenericSetting):
405    def __init__(self, *args):
406        GenericSetting.__init__(self, *args)
407
408        self.entry = Gtk.Entry()
409        self.entry.set_text(str(self.setting_value))
410        self.entry.connect('notify::text', self.on_text_change)
411        self.entry.set_valign(Gtk.Align.CENTER)
412        self.entry.set_alignment(1)
413
414        if self.value == 'password':
415            self.entry.set_invisible_char('*')
416            self.entry.set_visibility(False)
417
418        self.setting_box.pack_end(self.entry, True, True, 0)
419
420        self.show_all()
421
422    def on_text_change(self, *args):
423        text = self.entry.get_text()
424        self.set_value(text)
425
426    def on_row_activated(self):
427        self.entry.grab_focus()
428
429
430class ColorSetting(GenericSetting):
431    def __init__(self, *args):
432        GenericSetting.__init__(self, *args)
433
434        rgba = Gdk.RGBA()
435        rgba.parse(self.setting_value)
436        self.color_button = Gtk.ColorButton()
437        self.color_button.set_rgba(rgba)
438        self.color_button.connect('color-set', self.on_color_set)
439        self.color_button.set_valign(Gtk.Align.CENTER)
440        self.color_button.set_halign(Gtk.Align.END)
441
442        self.setting_box.pack_end(self.color_button, True, True, 0)
443
444        self.show_all()
445
446    def on_color_set(self, button):
447        rgba = button.get_rgba()
448        self.set_value(rgba.to_string())
449        app.css_config.refresh()
450
451    def on_row_activated(self):
452        self.color_button.grab_focus()
453
454
455class DialogSetting(GenericSetting):
456    def __init__(self, *args, dialog):
457        GenericSetting.__init__(self, *args)
458        self.dialog = dialog
459
460        self.setting_value = Gtk.Label()
461        self.setting_value.set_text(self.get_setting_value())
462        self.setting_value.set_halign(Gtk.Align.END)
463        self.setting_box.pack_start(self.setting_value, True, True, 0)
464
465        self.show_all()
466
467    def show_dialog(self, parent):
468        if self.dialog:
469            dialog = self.dialog(self.account, parent)
470            dialog.connect('destroy', self.on_destroy)
471
472    def on_destroy(self, *args):
473        self.setting_value.set_text(self.get_setting_value())
474
475    def get_setting_value(self):
476        self.setting_value.hide()
477        return ''
478
479    def on_row_activated(self):
480        self.show_dialog(self.get_toplevel())
481
482
483class SpinSetting(GenericSetting):
484    def __init__(self, *args, range_):
485        GenericSetting.__init__(self, *args)
486
487        lower, upper = range_
488        adjustment = Gtk.Adjustment(value=0,
489                                    lower=lower,
490                                    upper=upper,
491                                    step_increment=1,
492                                    page_increment=10,
493                                    page_size=0)
494
495        self.spin = Gtk.SpinButton()
496        self.spin.set_adjustment(adjustment)
497        self.spin.set_numeric(True)
498        self.spin.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID)
499        self.spin.set_value(self.setting_value)
500        self.spin.set_halign(Gtk.Align.FILL)
501        self.spin.set_valign(Gtk.Align.CENTER)
502        self.spin.connect('notify::value', self.on_value_change)
503
504        self.setting_box.pack_start(self.spin, True, True, 0)
505
506        self.show_all()
507
508    def on_row_activated(self):
509        self.spin.grab_focus()
510
511    def on_value_change(self, spin, *args):
512        value = spin.get_value_as_int()
513        self.set_value(value)
514
515
516class FileChooserSetting(GenericSetting):
517    def __init__(self, *args, filefilter):
518        GenericSetting.__init__(self, *args)
519
520        button = Gtk.FileChooserButton(title=self.label,
521                                       action=Gtk.FileChooserAction.OPEN)
522        button.set_halign(Gtk.Align.END)
523
524        # GTK Bug: The FileChooserButton expands without limit
525        # get the label and use set_max_wide_chars()
526        label = button.get_children()[0].get_children()[0].get_children()[1]
527        label.set_max_width_chars(20)
528
529        if filefilter:
530            name, pattern = filefilter
531            filter_ = Gtk.FileFilter()
532            filter_.set_name(name)
533            filter_.add_pattern(pattern)
534            button.add_filter(filter_)
535            button.set_filter(filter_)
536
537        filter_ = Gtk.FileFilter()
538        filter_.set_name(_('All files'))
539        filter_.add_pattern('*')
540        button.add_filter(filter_)
541
542        if self.setting_value:
543            button.set_filename(self.setting_value)
544        button.connect('selection-changed', self.on_select)
545
546        clear_button = get_image_button(
547            'edit-clear-all-symbolic', _('Clear File'))
548        clear_button.connect('clicked', lambda *args: button.unselect_all())
549        self.setting_box.pack_start(button, True, True, 0)
550        self.setting_box.pack_start(clear_button, False, False, 0)
551
552        self.show_all()
553
554    def on_select(self, filechooser):
555        self.set_value(filechooser.get_filename() or '')
556
557    def on_row_activated(self):
558        pass
559
560
561class CallbackSetting(GenericSetting):
562    def __init__(self, *args, callback):
563        GenericSetting.__init__(self, *args)
564        self.callback = callback
565        self.show_all()
566
567    def on_row_activated(self):
568        self.callback()
569
570
571class ActionSetting(GenericSetting):
572    def __init__(self, *args, account):
573        GenericSetting.__init__(self, *args)
574        action_name = '%s%s' % (account, self.value)
575        self.action = gtkgui_helpers.get_action(action_name)
576        self.variant = GLib.Variant.new_string(account)
577        self.on_enable()
578
579        self.show_all()
580        self.action.connect('notify::enabled', self.on_enable)
581
582    def on_enable(self, *args):
583        self.set_sensitive(self.action.get_enabled())
584
585    def on_row_activated(self):
586        self.action.activate(self.variant)
587
588
589class LoginSetting(DialogSetting):
590    def __init__(self, *args, **kwargs):
591        DialogSetting.__init__(self, *args, **kwargs)
592        self.setting_value.set_selectable(True)
593
594    def get_setting_value(self):
595        jid = app.get_jid_from_account(self.account)
596        return jid
597
598
599class PopoverSetting(GenericSetting):
600    def __init__(self, *args, entries, **kwargs):
601        GenericSetting.__init__(self, *args)
602
603        self._entries = self._convert_to_dict(entries)
604
605        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
606                      spacing=12)
607        box.set_halign(Gtk.Align.END)
608        box.set_hexpand(True)
609
610        self._default_text = kwargs.get('default-text')
611
612        self._current_label = Gtk.Label()
613        self._current_label.set_valign(Gtk.Align.CENTER)
614        image = Gtk.Image.new_from_icon_name('pan-down-symbolic',
615                                             Gtk.IconSize.MENU)
616        image.set_valign(Gtk.Align.CENTER)
617
618        box.add(self._current_label)
619        box.add(image)
620
621        self._menu_listbox = Gtk.ListBox()
622        self._menu_listbox.set_selection_mode(Gtk.SelectionMode.NONE)
623        self._add_menu_entries()
624        self._menu_listbox.connect('row-activated',
625                                   self._on_menu_row_activated)
626
627        scrolled_window = Gtk.ScrolledWindow()
628        scrolled_window.set_propagate_natural_height(True)
629        scrolled_window.set_propagate_natural_width(True)
630        scrolled_window.set_max_content_height(400)
631        scrolled_window.set_policy(Gtk.PolicyType.NEVER,
632                                   Gtk.PolicyType.AUTOMATIC)
633        scrolled_window.add(self._menu_listbox)
634        scrolled_window.show_all()
635
636        self._popover = Gtk.Popover()
637        self._popover.get_style_context().add_class('combo')
638        self._popover.set_relative_to(image)
639        self._popover.set_position(Gtk.PositionType.BOTTOM)
640        self._popover.add(scrolled_window)
641
642        self.setting_box.add(box)
643
644        self._add_action_button(kwargs)
645
646        text = self._entries.get(self.setting_value, self._default_text or '')
647        self._current_label.set_text(text)
648
649        app.settings.connect_signal(self.value,
650                                    self._on_setting_changed,
651                                    account=self.account,
652                                    jid=self.jid)
653
654        self.connect('destroy', self._on_destroy)
655
656        self.show_all()
657
658    @staticmethod
659    def _convert_to_dict(entries):
660        if isinstance(entries, list):
661            entries = {key: key for key in entries}
662        return entries
663
664    def _on_setting_changed(self, value, *args):
665        text = self._entries.get(value)
666        if text is None:
667            text = self._default_text or ''
668
669        self._current_label.set_text(text)
670
671    def _add_menu_entries(self):
672        if self._default_text is not None:
673            self._menu_listbox.add(PopoverRow(self._default_text, ''))
674
675        for value, label in self._entries.items():
676            self._menu_listbox.add(PopoverRow(label, value))
677
678        self._menu_listbox.show_all()
679
680    def _on_menu_row_activated(self, listbox, row):
681        listbox.unselect_all()
682        self._popover.popdown()
683
684        self.set_value(row.value)
685
686    def on_row_activated(self):
687        self._popover.popup()
688
689    def update_entries(self, entries):
690        self._entries = self._convert_to_dict(entries)
691        self._menu_listbox.foreach(self._menu_listbox.remove)
692        self._add_menu_entries()
693
694    def _on_destroy(self, *args):
695        app.settings.disconnect_signals(self)
696
697
698class PopoverRow(Gtk.ListBoxRow):
699    def __init__(self, label, value):
700        Gtk.ListBoxRow.__init__(self)
701        self.label = label
702        self.value = value
703
704        label = Gtk.Label(label=label)
705        label.set_xalign(0)
706        self.add(label)
707
708
709class ComboSetting(GenericSetting):
710    def __init__(self, *args, combo_items):
711        GenericSetting.__init__(self, *args)
712
713        self.combo = MaxWidthComboBoxText()
714        self.combo.set_valign(Gtk.Align.CENTER)
715
716        for index, value in enumerate(combo_items):
717            if isinstance(value, tuple):
718                value, label = value
719                self.combo.append(value, _(label))
720            else:
721                self.combo.append(value, value)
722            if value == self.setting_value or index == 0:
723                self.combo.set_active(index)
724
725        self.combo.connect('changed', self.on_value_change)
726
727        self.setting_box.pack_start(self.combo, True, True, 0)
728        self.show_all()
729
730    def on_value_change(self, combo):
731        self.set_value(combo.get_active_id())
732
733    def on_row_activated(self):
734        pass
735
736
737class PrioritySetting(DialogSetting):
738    def __init__(self, *args, **kwargs):
739        DialogSetting.__init__(self, *args, **kwargs)
740
741    def get_setting_value(self):
742        adjust = app.settings.get_account_setting(
743            self.account, 'adjust_priority_with_status')
744        if adjust:
745            return _('Adjust to Status')
746
747        priority = app.settings.get_account_setting(self.account, 'priority')
748        return str(priority)
749
750
751class CutstomHostnameSetting(DialogSetting):
752    def __init__(self, *args, **kwargs):
753        DialogSetting.__init__(self, *args, **kwargs)
754
755    def get_setting_value(self):
756        custom = app.settings.get_account_setting(self.account,
757                                                  'use_custom_host')
758        return Q_('?switch:On') if custom else Q_('?switch:Off')
759
760
761class ChangePasswordSetting(DialogSetting):
762    def __init__(self, *args, **kwargs):
763        DialogSetting.__init__(self, *args, **kwargs)
764
765    def show_dialog(self, parent):
766        parent.destroy()
767        open_window('ChangePassword', account=self.account)
768
769    def update_activatable(self):
770        activatable = False
771        if self.account in app.connections:
772            con = app.connections[self.account]
773            activatable = (con.state.is_available and
774                           con.get_module('Register').supported)
775        self.set_activatable(activatable)
776
777
778class CutstomAutoAwaySetting(DialogSetting):
779    def __init__(self, *args, **kwargs):
780        DialogSetting.__init__(self, *args, **kwargs)
781
782    def get_setting_value(self):
783        value = app.settings.get('autoaway')
784        return Q_('?switch:On') if value else Q_('?switch:Off')
785
786
787class CutstomAutoExtendedAwaySetting(DialogSetting):
788    def __init__(self, *args, **kwargs):
789        DialogSetting.__init__(self, *args, **kwargs)
790
791    def get_setting_value(self):
792        value = app.settings.get('autoxa')
793        return Q_('?switch:On') if value else Q_('?switch:Off')
794
795
796class CustomStunServerSetting(DialogSetting):
797    def __init__(self, *args, **kwargs):
798        DialogSetting.__init__(self, *args, **kwargs)
799
800    def get_setting_value(self):
801        value = app.settings.get('use_stun_server')
802        return Q_('?switch:On') if value else Q_('?switch:Off')
803
804
805class NotificationsSetting(DialogSetting):
806    def __init__(self, *args, **kwargs):
807        DialogSetting.__init__(self, *args, **kwargs)
808
809    def get_setting_value(self):
810        value = app.settings.get('show_notifications')
811        return Q_('?switch:On') if value else Q_('?switch:Off')
812