diff --git a/data/gui/accounts_window.ui b/data/gui/accounts_window.ui
index de0e541c6..60416ad0c 100644
--- a/data/gui/accounts_window.ui
+++ b/data/gui/accounts_window.ui
@@ -1,1573 +1,114 @@
-
+
-
-
-
diff --git a/data/gui/synchronise_select_account_dialog.ui b/data/gui/synchronise_select_account_dialog.ui
index 58d066d46..365f862c1 100644
--- a/data/gui/synchronise_select_account_dialog.ui
+++ b/data/gui/synchronise_select_account_dialog.ui
@@ -9,6 +9,7 @@
350
300
dialog
+
True
diff --git a/data/style/gajim.css b/data/style/gajim.css
index f735bc487..62c782586 100644
--- a/data/style/gajim.css
+++ b/data/style/gajim.css
@@ -30,14 +30,34 @@ popover#EmoticonPopover flowboxchild { padding-top: 5px; padding-bottom: 5px; }
#ServerInfoGrid > list > label { padding:10px; color: @insensitive_fg_color; font-weight: bold; }
#ServerInfoGrid > list > row.activatable:active { box-shadow: none; }
-/* Generic Options Dialog */
-#OptionsDialog list > row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; }
-#OptionsDialog list > row:last-child { border-bottom: 0px}
-#OptionsDialog list > row { padding: 10px; }
-#OptionsDialog list > row.activatable:active { box-shadow: none; }
+/* OptionsBox */
+#OptionsBox > row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; }
+#OptionsBox > row:last-child { border-bottom: 0px}
+#OptionsBox > row.activatable:active { box-shadow: none; }
+#OptionsBox > row { padding: 10px 20px 10px 10px; }
+#OptionsBox > row:not(.activatable) label { color: @insensitive_fg_color }
+
+/* GenericOption */
+#SubDescription { color: @insensitive_fg_color;}
+#GenericOptionBox { margin-left: 30px; }
+#GenericOptionBox > label { padding-right: 3px; }
/* Generic Popover Menu with Buttons */
.PopoverButtonListbox { padding-left: 0px; padding-right: 0px; }
.PopoverButtonListbox > list { margin-top: 10px; margin-bottom: 10px; }
.PopoverButtonListbox > list > row { padding: 10px 20px 10px 20px; }
.PopoverButtonListbox > list > row.activatable:active { box-shadow: none; background-color: @theme_selected_bg_color }
+
+/* Accounts Window */
+#AccountsWindow > box { padding:30px 30px 30px 30px;}
+#AccountsWindow scrolledwindow {border: none;}
+#AccountsWindow list {border: 1px solid; border-color: @borders;}
+#AccountsWindow > box actionbar box {border: none;}
+
+#AccountNameEntry:disabled { font-size: 16px;
+ font-weight: bold;
+ border: none;
+ background-color: @theme_unfocused_bg_color;
+ color: @theme_text_color; }
+
+
diff --git a/gajim/accounts_window.py b/gajim/accounts_window.py
new file mode 100644
index 000000000..3b4f3df1a
--- /dev/null
+++ b/gajim/accounts_window.py
@@ -0,0 +1,698 @@
+from functools import partial
+
+from gi.repository import Gtk, Gio, GLib, Gdk
+
+from gajim.common import app
+from gajim.gtkgui_helpers import get_image_button
+from gajim import gtkgui_helpers
+from gajim import gui_menu_builder
+from gajim.common import passwords
+from gajim import dialogs
+from gajim import config
+from gajim.common import helpers
+from gajim.common.connection import Connection
+from gajim.common.zeroconf.connection_zeroconf import ConnectionZeroconf
+from gajim.options_dialog import OptionsDialog, OptionsBox
+from gajim.common.const import Option, OptionKind, OptionType
+
+
+class AccountsWindow(Gtk.ApplicationWindow):
+ def __init__(self):
+ Gtk.ApplicationWindow.__init__(self)
+ self.set_application(app.app)
+ self.set_position(Gtk.WindowPosition.CENTER)
+ self.set_show_menubar(False)
+ self.set_name('AccountsWindow')
+ self.set_size_request(500, -1)
+ self.set_resizable(False)
+ self.need_relogin = {}
+
+ glade_objects = [
+ 'stack', 'box', 'actionbar', 'headerbar', 'back_button',
+ 'menu_button', 'account_page', 'account_list']
+ self.builder = gtkgui_helpers.get_gtk_builder('accounts_window.ui')
+ for obj in glade_objects:
+ setattr(self, obj, self.builder.get_object(obj))
+
+ self.set_titlebar(self.headerbar)
+
+ menu = Gio.Menu()
+ menu.append('Merge Accounts', 'app.merge')
+ menu.append('Use PGP Agent', 'app.agent')
+ self.menu_button.set_menu_model(menu)
+
+ button = get_image_button('list-add-symbolic', 'Add')
+ button.set_action_name('app.add-account')
+ self.actionbar.pack_start(button)
+
+ accounts = app.config.get_per('accounts')
+ accounts.sort()
+ for account in accounts:
+ self.need_relogin[account] = self.get_relogin_options(account)
+ account_item = Account(account, self)
+ self.account_list.add(account_item)
+ account_item.set_activatable()
+
+ self.add(self.box)
+ self.builder.connect_signals(self)
+
+ self.connect('destroy', self.on_destroy)
+ self.connect('key-press-event', self.on_key_press)
+ self.show_all()
+
+ def on_key_press(self, widget, event):
+ if event.keyval == Gdk.KEY_Escape:
+ self.destroy()
+
+ def on_destroy(self, *args):
+ self.check_relogin()
+ del app.interface.instances['accounts']
+
+ def on_child_visible(self, stack, *args):
+ page = stack.get_visible_child_name()
+ if page is None:
+ return
+ if page == 'main':
+ self.menu_button.show()
+ self.back_button.hide()
+ self.check_relogin()
+ else:
+ self.back_button.show()
+ self.menu_button.hide()
+
+ def on_back_button(self, *args):
+ page = self.stack.get_visible_child_name()
+ child = self.stack.get_visible_child()
+ self.remove_all_pages()
+ if page == 'account':
+ child.toggle.set_active(False)
+ self.stack.add_named(self.account_page, 'main')
+ self.stack.set_visible_child_name('main')
+ self.update_accounts()
+ else:
+ self.stack.add_named(child.parent, 'account')
+ self.stack.set_visible_child_name('account')
+
+ def update_accounts(self):
+ for row in self.account_list.get_children():
+ row.get_child().update()
+
+ @staticmethod
+ def on_row_activated(listbox, row):
+ row.get_child().on_row_activated()
+
+ def remove_all_pages(self):
+ for page in self.stack.get_children():
+ self.stack.remove(page)
+
+ def set_page(self, page, name):
+ self.remove_all_pages()
+ self.stack.add_named(page, name)
+ page.update()
+ page.show_all()
+ self.stack.set_visible_child(page)
+
+ def update_proxy_list(self):
+ page = self.stack.get_child_by_name('connetion')
+ if page is None:
+ return
+ page.options['proxy'].update_values()
+
+ def check_relogin(self):
+ for account in self.need_relogin:
+ options = self.get_relogin_options(account)
+ active = app.config.get_per('accounts', account, 'active')
+ if options != self.need_relogin[account]:
+ self.need_relogin[account] = options
+ if active:
+ self.relog(account)
+ break
+
+ def relog(self, account):
+ if app.connections[account].connected == 0:
+ return
+
+ if account == app.ZEROCONF_ACC_NAME:
+ app.connections[app.ZEROCONF_ACC_NAME].update_details()
+ return
+
+ def login(account, show_before, status_before):
+ """
+ Login with previous status
+ """
+ # first make sure connection is really closed,
+ # 0.5 may not be enough
+ app.connections[account].disconnect(True)
+ app.interface.roster.send_status(
+ account, show_before, status_before)
+
+ def relog(account):
+ show_before = app.SHOW_LIST[app.connections[account].connected]
+ status_before = app.connections[account].status
+ app.interface.roster.send_status(
+ account, 'offline', _('Be right back.'))
+ GLib.timeout_add(500, login, account, show_before, status_before)
+
+ dialogs.YesNoDialog(
+ _('Relogin now?'),
+ _('If you want all the changes to apply instantly, '
+ 'you must relogin.'),
+ transient_for=self,
+ on_response_yes=lambda *args: relog(account))
+
+ @staticmethod
+ def get_relogin_options(account):
+ if account == app.ZEROCONF_ACC_NAME:
+ options = ['zeroconf_first_name', 'zeroconf_last_name',
+ 'zeroconf_jabber_id', 'zeroconf_email', 'keyid']
+ else:
+ options = ['client_cert', 'proxy', 'resource',
+ 'use_custom_host', 'custom_host', 'custom_port',
+ 'keyid']
+
+ values = []
+ for option in options:
+ values.append(app.config.get_per('accounts', account, option))
+ return values
+
+ def on_remove_account(self, button, account):
+ if app.events.get_events(account):
+ dialogs.ErrorDialog(
+ _('Unread events'),
+ _('Read all pending events before removing this account.'),
+ transient_for=self)
+ return
+
+ if app.config.get_per('accounts', account, 'is_zeroconf'):
+ # Should never happen as button is insensitive
+ return
+
+ win_opened = False
+ if app.interface.msg_win_mgr.get_controls(acct=account):
+ win_opened = True
+ elif account in app.interface.instances:
+ for key in app.interface.instances[account]:
+ if (app.interface.instances[account][key] and
+ key != 'remove_account'):
+ win_opened = True
+ break
+
+ # Detect if we have opened windows for this account
+
+ def remove(account):
+ if (account in app.interface.instances and
+ 'remove_account' in app.interface.instances[account]):
+ dialog = app.interface.instances[account]['remove_account']
+ dialog.window.present()
+ else:
+ if account not in app.interface.instances:
+ app.interface.instances[account] = {}
+ app.interface.instances[account]['remove_account'] = \
+ config.RemoveAccountWindow(account)
+ if win_opened:
+ dialogs.ConfirmationDialog(
+ _('You have opened chat in account %s') % account,
+ _('All chat and groupchat windows will be closed. '
+ 'Do you want to continue?'),
+ on_response_ok=(remove, account))
+ else:
+ remove(account)
+
+ def remove_account(self, account):
+ for row in self.account_list.get_children():
+ if row.get_child().account == account:
+ self.account_list.remove(row)
+ del self.need_relogin[account]
+ break
+
+ def add_account(self, account):
+ account_item = Account(account, self)
+ self.account_list.add(account_item)
+ account_item.set_activatable()
+ self.account_list.show_all()
+ self.stack.show_all()
+ self.need_relogin[account] = self.get_relogin_options(account)
+
+ def select_account(self, account):
+ for row in self.account_list.get_children():
+ if row.get_child().account == account:
+ self.account_list.emit('row-activated', row)
+ break
+
+ @staticmethod
+ def enable_account(account):
+ if account == app.ZEROCONF_ACC_NAME:
+ app.connections[account] = ConnectionZeroconf(account)
+ else:
+ app.connections[account] = Connection(account)
+
+ # update variables
+ app.interface.instances[account] = {
+ 'infos': {}, 'disco': {}, 'gc_config': {}, 'search': {},
+ 'online_dialog': {}, 'sub_request': {}}
+ app.interface.minimized_controls[account] = {}
+ app.connections[account].connected = 0
+ app.groups[account] = {}
+ app.contacts.add_account(account)
+ app.gc_connected[account] = {}
+ app.automatic_rooms[account] = {}
+ app.newly_added[account] = []
+ app.to_be_removed[account] = []
+ if account == app.ZEROCONF_ACC_NAME:
+ app.nicks[account] = app.ZEROCONF_ACC_NAME
+ else:
+ app.nicks[account] = app.config.get_per(
+ 'accounts', account, 'name')
+ app.block_signed_in_notifications[account] = True
+ app.sleeper_state[account] = 'off'
+ app.encrypted_chats[account] = []
+ app.last_message_time[account] = {}
+ app.status_before_autoaway[account] = ''
+ app.transport_avatar[account] = {}
+ app.gajim_optional_features[account] = []
+ app.caps_hash[account] = ''
+ helpers.update_optional_features(account)
+ # refresh roster
+ if len(app.connections) >= 2:
+ # Do not merge accounts if only one exists
+ app.interface.roster.regroup = app.config.get('mergeaccounts')
+ else:
+ app.interface.roster.regroup = False
+ app.interface.roster.setup_and_draw_roster()
+ gui_menu_builder.build_accounts_menu()
+
+ @staticmethod
+ def disable_account(account):
+ app.interface.roster.close_all(account)
+ if account == app.ZEROCONF_ACC_NAME:
+ app.connections[account].disable_account()
+ app.connections[account].cleanup()
+ del app.connections[account]
+ del app.interface.instances[account]
+ del app.interface.minimized_controls[account]
+ del app.nicks[account]
+ del app.block_signed_in_notifications[account]
+ del app.groups[account]
+ app.contacts.remove_account(account)
+ del app.gc_connected[account]
+ del app.automatic_rooms[account]
+ del app.to_be_removed[account]
+ del app.newly_added[account]
+ del app.sleeper_state[account]
+ del app.encrypted_chats[account]
+ del app.last_message_time[account]
+ del app.status_before_autoaway[account]
+ del app.transport_avatar[account]
+ del app.gajim_optional_features[account]
+ del app.caps_hash[account]
+ if len(app.connections) >= 2:
+ # Do not merge accounts if only one exists
+ app.interface.roster.regroup = app.config.get('mergeaccounts')
+ else:
+ app.interface.roster.regroup = False
+ app.interface.roster.setup_and_draw_roster()
+ gui_menu_builder.build_accounts_menu()
+
+
+class Account(Gtk.Box):
+ def __init__(self, account, parent):
+ Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL,
+ spacing=12)
+ self.account = account
+ if account == app.ZEROCONF_ACC_NAME:
+ self.options = ZeroConfPage(account)
+ else:
+ self.options = AccountPage(account)
+ self.parent = parent
+
+ switch = Gtk.Switch()
+ switch.set_active(app.config.get_per('accounts', account, 'active'))
+ switch.set_vexpand(False)
+ switch.set_valign(Gtk.Align.CENTER)
+ switch.set_halign(Gtk.Align.START)
+ if account == app.ZEROCONF_ACC_NAME and not app.HAVE_ZEROCONF:
+ switch.set_sensitive(False)
+ switch.set_active(False)
+ switch.connect('notify::active', self.on_switch, self.account)
+
+ account_label = app.config.get_per('accounts', account, 'account_label')
+ self.label = Gtk.Label(label=account_label or account)
+ self.label.set_halign(Gtk.Align.START)
+ self.label.set_hexpand(True)
+
+ self.add(switch)
+ self.add(self.label)
+
+ if account != app.ZEROCONF_ACC_NAME:
+ button = get_image_button('list-remove-symbolic', 'Remove')
+ button.connect('clicked', parent.on_remove_account, account)
+ self.add(button)
+
+ def set_activatable(self):
+ if self.account == app.ZEROCONF_ACC_NAME:
+ self.get_parent().set_activatable(app.HAVE_ZEROCONF)
+
+ def on_switch(self, switch, param, account):
+ old_state = app.config.get_per('accounts', account, 'active')
+ state = switch.get_active()
+ if old_state == state:
+ return
+
+ if (account in app.connections and
+ app.connections[account].connected > 0):
+ # connecting or connected
+ dialogs.ErrorDialog(
+ _('You are currently connected to the server'),
+ _('To disable the account, you must be disconnected.'),
+ transient_for=self.parent)
+ switch.set_active(not state)
+ return
+ if state:
+ self.parent.enable_account(account)
+ else:
+ self.parent.disable_account(account)
+ app.config.set_per('accounts', account, 'active', state)
+
+ def on_row_activated(self):
+ self.options.update_states()
+ self.parent.set_page(self.options, 'account')
+
+ def update(self):
+ account_label = app.config.get_per(
+ 'accounts', self.account, 'account_label')
+ self.label.set_text(account_label or self.account)
+
+
+class GenericOptionPage(Gtk.Box):
+ def __init__(self, account, parent, options):
+ Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12)
+ self.account = account
+ self.parent = parent
+
+ self.toggle = get_image_button('document-edit-symbolic',
+ _('Rename account label'), toggle=True)
+ self.toggle.connect('toggled', self.set_entry_text)
+
+ self.entry = Gtk.Entry()
+ self.entry.set_sensitive(False)
+ self.entry.set_name('AccountNameEntry')
+ self.set_entry_text(self.toggle, update=True)
+
+ box = Gtk.Box()
+ if isinstance(self, AccountPage):
+ box.pack_start(self.toggle, False, True, 0)
+ box.pack_start(self.entry, True, True, 0)
+
+ self.listbox = OptionsBox(account)
+ self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ for option in options:
+ self.listbox.add_option(option)
+ self.listbox.update_states()
+
+ self.pack_start(box, False, False, 0)
+ self.pack_start(self.listbox, True, True, 0)
+
+ self.listbox.connect('row-activated', self.on_row_activated)
+
+ def update_states(self):
+ self.listbox.update_states()
+
+ def on_row_activated(self, listbox, row):
+ self.toggle.set_active(False)
+ row.get_child().on_row_activated()
+
+ def set_entry_text(self, toggle, update=False):
+ account_label = app.config.get_per(
+ 'accounts', self.account, 'account_label')
+ if update:
+ self.entry.set_text(account_label or self.account)
+ return
+ if toggle.get_active():
+ self.entry.set_sensitive(True)
+ self.entry.grab_focus()
+ else:
+ self.entry.set_sensitive(False)
+ value = self.entry.get_text()
+ if not value:
+ value = account_label or self.account
+ app.config.set_per('accounts', self.account,
+ 'account_label', value or self.account)
+ if app.config.get_per('accounts', self.account, 'active'):
+ app.interface.roster.draw_account(self.account)
+
+ def update(self):
+ self.set_entry_text(self.toggle, update=True)
+
+ def set_page(self, options, name):
+ options.update_states()
+ self.get_toplevel().set_page(options, name)
+
+
+class AccountPage(GenericOptionPage):
+ def __init__(self, account, parent=None):
+
+ general = partial(
+ self.set_page, GeneralPage(account, self), 'general')
+ connection = partial(
+ self.set_page, ConnectionPage(account, self), 'connection')
+
+ options = [
+ Option(OptionKind.LOGIN, _('Login'), OptionType.DIALOG,
+ props={'dialog': LoginDialog}),
+
+ Option(OptionKind.ACTION, _('Profile'), OptionType.ACTION,
+ '-profile', props={'action_args': account}),
+
+ Option(OptionKind.CALLBACK, _('General'),
+ name='general', props={'callback': general}),
+
+ Option(OptionKind.CALLBACK, _('Connection'),
+ name='connection', props={'callback': connection}),
+
+ Option(OptionKind.ACTION, _('Import Contacts'), OptionType.ACTION,
+ '-import-contacts', props={'action_args': account}),
+
+ Option(OptionKind.DIALOG, _('Client Certificate'),
+ OptionType.DIALOG, props={'dialog': CertificateDialog}),
+
+ Option(OptionKind.GPG, _('OpenPGP Key'), OptionType.DIALOG,
+ props={'dialog': None}),
+ ]
+
+ GenericOptionPage.__init__(self, account, parent, options)
+
+
+class GeneralPage(GenericOptionPage):
+ def __init__(self, account, parent=None):
+
+ options = [
+ Option(OptionKind.SWITCH, _('Connect on startup'),
+ OptionType.ACCOUNT_CONFIG, 'autoconnect'),
+
+ Option(OptionKind.SWITCH, _('Reconnect when connection is lost'),
+ OptionType.ACCOUNT_CONFIG, 'autoreconnect'),
+
+ Option(OptionKind.SWITCH, _('Save conversations for all contacts'),
+ OptionType.ACCOUNT_CONFIG, 'no_log_for',
+ desc=_('Store conversations on the harddrive')),
+
+ Option(OptionKind.SWITCH, _('Server Message Archive'),
+ OptionType.ACCOUNT_CONFIG, 'sync_logs_with_server',
+ desc=_('Messages get stored on the server.\n'
+ 'The archive is used to sync messages\n'
+ 'between multiple devices.\n'
+ 'XEP-0313')),
+
+ Option(OptionKind.SWITCH, _('Global Status'),
+ OptionType.ACCOUNT_CONFIG, 'sync_with_global_status',
+ desc=_('Synchronise the status of all accounts')),
+
+ Option(OptionKind.SWITCH, _('Message Carbons'),
+ OptionType.ACCOUNT_CONFIG, 'enable_message_carbons',
+ desc=_('All your other online devices get copies\n'
+ 'of sent and received messages.\n'
+ 'XEP-0280')),
+
+ Option(OptionKind.SWITCH, _('Use file transfer proxies'),
+ OptionType.ACCOUNT_CONFIG, 'use_ft_proxies'),
+ ]
+ GenericOptionPage.__init__(self, account, parent, options)
+
+
+class ConnectionPage(GenericOptionPage):
+ def __init__(self, account, parent=None):
+
+ options = [
+ Option(OptionKind.SWITCH, 'HTTP_PROXY',
+ OptionType.ACCOUNT_CONFIG, 'use_env_http_proxy',
+ desc=_('Use environment variable')),
+
+ Option(OptionKind.PROXY, _('Proxy'),
+ OptionType.ACCOUNT_CONFIG, 'proxy', name='proxy'),
+
+ Option(OptionKind.SWITCH, _('Warn on insecure connection'),
+ OptionType.ACCOUNT_CONFIG,
+ 'warn_when_insecure_ssl_connection'),
+
+ Option(OptionKind.SWITCH, _('Send keep-alive packets'),
+ OptionType.ACCOUNT_CONFIG, 'keep_alives_enabled'),
+
+ Option(OptionKind.HOSTNAME, _('Hostname'), OptionType.DIALOG,
+ desc=_('Manually set the hostname for the server'),
+ props={'dialog': CutstomHostnameDialog}),
+
+ Option(OptionKind.ENTRY, _('Resource'),
+ OptionType.ACCOUNT_CONFIG, 'resource'),
+
+ Option(OptionKind.PRIORITY, _('Priority'),
+ OptionType.DIALOG, props={'dialog': PriorityDialog}),
+ ]
+
+ GenericOptionPage.__init__(self, account, parent, options)
+
+
+class ZeroConfPage(GenericOptionPage):
+ def __init__(self, account, parent=None):
+
+ options = [
+ Option(OptionKind.DIALOG, _('Credentials'),
+ OptionType.DIALOG, props={'dialog': CredentialsDialog}),
+
+ Option(OptionKind.SWITCH, _('Connect on startup'),
+ OptionType.ACCOUNT_CONFIG, 'autoconnect',
+ desc=_('Use environment variable')),
+
+ Option(OptionKind.SWITCH, _('Save conversations for all contacts'),
+ OptionType.ACCOUNT_CONFIG, 'no_log_for',
+ desc=_('Store conversations on the harddrive')),
+
+ Option(OptionKind.SWITCH, _('Global Status'),
+ OptionType.ACCOUNT_CONFIG, 'sync_with_global_status',
+ desc=_('Synchronize the status of all accounts')),
+
+ Option(OptionKind.GPG, _('OpenPGP Key'),
+ OptionType.DIALOG, props={'dialog': None}),
+ ]
+
+ GenericOptionPage.__init__(self, account, parent, options)
+
+
+class CredentialsDialog(OptionsDialog):
+ def __init__(self, account, parent):
+
+ options = [
+ Option(OptionKind.ENTRY, _('First Name'),
+ OptionType.ACCOUNT_CONFIG, 'zeroconf_first_name'),
+
+ Option(OptionKind.ENTRY, _('Last Name'),
+ OptionType.ACCOUNT_CONFIG, 'zeroconf_last_name'),
+
+ Option(OptionKind.ENTRY, _('Jabber ID'),
+ OptionType.ACCOUNT_CONFIG, 'zeroconf_jabber_id'),
+
+ Option(OptionKind.ENTRY, _('Email'),
+ OptionType.ACCOUNT_CONFIG, 'zeroconf_email'),
+ ]
+
+ OptionsDialog.__init__(self, parent, _('Credential Options'),
+ Gtk.DialogFlags.MODAL, options, account)
+
+
+class PriorityDialog(OptionsDialog):
+ def __init__(self, account, parent):
+
+ neg_priority = app.config.get('enable_negative_priority')
+ if neg_priority:
+ range_ = (-128, 127)
+ else:
+ range_ = (0, 127)
+
+ options = [
+ Option(OptionKind.SWITCH, _('Adjust to status'),
+ OptionType.ACCOUNT_CONFIG, 'adjust_priority_with_status',
+ 'adjust'),
+
+ Option(OptionKind.SPIN, _('Priority'),
+ OptionType.ACCOUNT_CONFIG, 'priority',
+ enabledif=('adjust', False), props={'range_': range_}),
+ ]
+
+ OptionsDialog.__init__(self, parent, _('Priority'),
+ Gtk.DialogFlags.MODAL, options, account)
+
+ self.connect('destroy', self.on_destroy)
+
+ def on_destroy(self, *args):
+ # Update priority
+ if self.account not in app.connections:
+ return
+ show = app.SHOW_LIST[app.connections[self.account].connected]
+ status = app.connections[self.account].status
+ app.connections[self.account].change_status(show, status)
+
+
+class CutstomHostnameDialog(OptionsDialog):
+ def __init__(self, account, parent):
+
+ options = [
+ Option(OptionKind.SWITCH, _('Enable'),
+ OptionType.ACCOUNT_CONFIG, 'use_custom_host', name='custom'),
+
+ Option(OptionKind.ENTRY, _('Hostname'),
+ OptionType.ACCOUNT_CONFIG, 'custom_host',
+ enabledif=('custom', True)),
+
+ Option(OptionKind.ENTRY, _('Port'),
+ OptionType.ACCOUNT_CONFIG, 'custom_port',
+ enabledif=('custom', True)),
+ ]
+
+ OptionsDialog.__init__(self, parent, _('Connection Options'),
+ Gtk.DialogFlags.MODAL, options, account)
+
+
+class CertificateDialog(OptionsDialog):
+ def __init__(self, account, parent):
+
+ options = [
+ Option(OptionKind.FILECHOOSER, _('Client Certificate'),
+ OptionType.ACCOUNT_CONFIG, 'client_cert',
+ props={'filefilter': (_('PKCS12 Files'), '*.p12')}),
+
+ Option(OptionKind.SWITCH, _('Encrypted Certificate'),
+ OptionType.ACCOUNT_CONFIG, 'client_cert_encrypted'),
+ ]
+
+ OptionsDialog.__init__(self, parent, _('Certificate Options'),
+ Gtk.DialogFlags.MODAL, options, account)
+
+
+class LoginDialog(OptionsDialog):
+ def __init__(self, account, parent):
+
+ options = [
+ Option(OptionKind.ENTRY, _('Password'),
+ OptionType.ACCOUNT_CONFIG, 'password', name='password',
+ enabledif=('savepass', True)),
+
+ Option(OptionKind.SWITCH, _('Save Password'),
+ OptionType.ACCOUNT_CONFIG, 'savepass', name='savepass'),
+
+ Option(OptionKind.CHANGEPASSWORD, _('Change Password'),
+ OptionType.DIALOG, callback=self.on_password_change,
+ props={'dialog': None}),
+ ]
+
+ OptionsDialog.__init__(self, parent, _('Login Options'),
+ Gtk.DialogFlags.MODAL, options, account)
+
+ self.connect('destroy', self.on_destroy)
+
+ def on_password_change(self, new_password, data):
+ self.get_option('password').entry.set_text(new_password)
+
+ def on_destroy(self, *args):
+ savepass = app.config.get_per('accounts', self.account, 'savepass')
+ if not savepass:
+ passwords.save_password(self.account, '')
diff --git a/gajim/app_actions.py b/gajim/app_actions.py
index a88a75bb7..e718dfa58 100644
--- a/gajim/app_actions.py
+++ b/gajim/app_actions.py
@@ -25,10 +25,12 @@ from gajim.common.exceptions import GajimGeneralException
from gi.repository import Gtk
import sys
import os
+
from gajim import config
from gajim import dialogs
from gajim import features_window
from gajim import shortcuts_window
+from gajim import accounts_window
import gajim.plugins.gui
from gajim import history_window
from gajim import disco
@@ -57,10 +59,10 @@ class AppActions():
interface.instances['plugins'] = gajim.plugins.gui.PluginsWindow()
def on_accounts(self, action, param):
- if 'accounts' in interface.instances:
- interface.instances['accounts'].window.present()
+ if 'accounts' in app.interface.instances:
+ app.interface.instances['accounts'].present()
else:
- interface.instances['accounts'] = config.AccountsWindow()
+ app.interface.instances['accounts'] = accounts_window.AccountsWindow()
def on_history_manager(self, action, param):
from gajim.history_manager import HistoryManager
@@ -131,6 +133,35 @@ class AppActions():
def on_single_message(self, action, param):
dialogs.SingleMessageWindow(param.get_string(), action='send')
+ def on_merge_accounts(self, action, param):
+ action.set_state(param)
+ value = param.get_boolean()
+ app.config.set('mergeaccounts', value)
+ if len(app.connections) >= 2: # Do not merge accounts if only one active
+ app.interface.roster.regroup = value
+ else:
+ app.interface.roster.regroup = False
+ app.interface.roster.setup_and_draw_roster()
+
+ def on_use_pgp_agent(self, action, param):
+ action.set_state(param)
+ app.config.set('use_gpg_agent', param.get_boolean())
+
+ def on_add_account(self, action, param):
+ if 'account_creation_wizard' in app.interface.instances:
+ app.interface.instances['account_creation_wizard'].window.present()
+ else:
+ app.interface.instances['account_creation_wizard'] = \
+ config.AccountCreationWizardWindow()
+
+ def on_import_contacts(self, action, param):
+ account = param.get_string()
+ if 'import_contacts' in app.interface.instances:
+ app.interface.instances['import_contacts'].dialog.present()
+ else:
+ app.interface.instances['import_contacts'] = \
+ dialogs.SynchroniseSelectAccountDialog(account)
+
# Advanced Actions
def on_archiving_preferences(self, action, param):
@@ -174,6 +205,13 @@ class AppActions():
interface.instances[account]['xml_console'] = \
dialogs.XMLConsoleWindow(account)
+ def on_manage_proxies(self, action, param):
+ if 'manage_proxies' in app.interface.instances:
+ app.interface.instances['manage_proxies'].window.present()
+ else:
+ app.interface.instances['manage_proxies'] = \
+ config.ManageProxiesWindow(interface.roster.window)
+
# Admin Actions
def on_set_motd(self, action, param):
diff --git a/gajim/common/config.py b/gajim/common/config.py
index 4b5f05ae6..f8ad19b2a 100644
--- a/gajim/common/config.py
+++ b/gajim/common/config.py
@@ -325,6 +325,7 @@ class Config:
__options_per_key = {
'accounts': ({
'name': [ opt_str, '', '', True ],
+ 'account_label': [ opt_str, '', '', False ],
'hostname': [ opt_str, '', '', True ],
'anonymous_auth': [ opt_bool, False ],
'client_cert': [ opt_str, '', '', True ],
diff --git a/gajim/common/const.py b/gajim/common/const.py
index 63a801b86..8985ced5c 100644
--- a/gajim/common/const.py
+++ b/gajim/common/const.py
@@ -1,3 +1,34 @@
+from enum import IntEnum, unique
+from collections import namedtuple
+
+Option = namedtuple('Option', 'kind label type value name callback data desc enabledif props')
+Option.__new__.__defaults__ = (None,) * len(Option._fields)
+
+@unique
+class OptionKind(IntEnum):
+ ENTRY = 0
+ SWITCH = 1
+ SPIN = 2
+ ACTION = 3
+ LOGIN = 4
+ DIALOG = 5
+ CALLBACK = 6
+ PROXY = 7
+ HOSTNAME = 8
+ PRIORITY = 9
+ FILECHOOSER = 10
+ CHANGEPASSWORD = 11
+ GPG = 12
+
+@unique
+class OptionType(IntEnum):
+ ACCOUNT_CONFIG = 0
+ CONFIG = 1
+ BOOL = 2
+ ACTION = 3
+ DIALOG = 4
+
+
THANKS = u"""\
Alexander Futász
Alexander V. Butenko
diff --git a/gajim/config.py b/gajim/config.py
index de0ac06bc..f1d743dd1 100644
--- a/gajim/config.py
+++ b/gajim/config.py
@@ -31,12 +31,14 @@
## along with Gajim. If not, see .
##
+import os
+
from gi.repository import Gtk
from gi.repository import Gdk
from gi.repository import Pango
from gi.repository import GObject
from gi.repository import GLib
-import os
+
from gajim.common import config as c_config
from gajim.common import sleepy
from gajim.common.i18n import Q_
@@ -47,7 +49,6 @@ from gajim import cell_renderer_image
from gajim import message_control
from gajim.chat_control_base import ChatControlBase
from gajim import dataforms_widget
-from gajim import profile_window
from gajim import gui_menu_builder
try:
@@ -59,11 +60,9 @@ except (ImportError, ValueError):
from gajim.common import helpers
from gajim.common import app
from gajim.common import connection
-from gajim.common import passwords
-from gajim.common.zeroconf import connection_zeroconf
from gajim.common import dataforms
-from gajim.common import gpg
from gajim.common import ged
+from gajim.accounts_window import AccountsWindow
try:
from gajim.common.multimedia_helpers import AudioInputManager, AudioOutputManager
@@ -72,9 +71,6 @@ try:
except (ImportError, ValueError):
HAS_GST = False
-from gajim.common.exceptions import GajimGeneralException
-from gajim.common.connection_handlers_events import InformationEvent
-
#---------- PreferencesWindow class -------------#
class PreferencesWindow:
"""
@@ -1514,1216 +1510,6 @@ class ManageProxiesWindow:
app.config.set_per('proxies', proxy, 'pass', value)
-#---------- AccountsWindow class -------------#
-class AccountsWindow:
- """
- Class for accounts window: list of accounts
- """
-
- def on_accounts_window_destroy(self, widget):
- del app.interface.instances['accounts']
-
- def on_close_button_clicked(self, widget):
- self.check_resend_relog()
- self.window.destroy()
-
- def __init__(self):
- self.xml = gtkgui_helpers.get_gtk_builder('accounts_window.ui')
- self.window = self.xml.get_object('accounts_window')
- self.window.set_transient_for(app.interface.roster.window)
- self.accounts_treeview = self.xml.get_object('accounts_treeview')
- self.remove_button = self.xml.get_object('remove_button')
- self.rename_button = self.xml.get_object('rename_button')
- self.change_password_button = self.xml.get_object('change_password_button1')
- path_to_kbd_input_img = gtkgui_helpers.get_icon_path('gajim-kbd_input')
- img = self.xml.get_object('rename_image')
- img.set_from_file(path_to_kbd_input_img)
- self.notebook = self.xml.get_object('notebook')
- # Name
- model = Gtk.ListStore(str)
- self.accounts_treeview.set_model(model)
- # column
- renderer = Gtk.CellRendererText()
- col = Gtk.TreeViewColumn()
- col.set_title(_('Name'))
- col.pack_start(renderer, False)
- col.add_attribute(renderer, 'text', 0)
- self.accounts_treeview.insert_column(col, -1)
-
- self.current_account = None
- # When we fill info, we don't want to handle the changed signals
- self.ignore_events = False
- self.need_relogin = False
- self.resend_presence = False
-
- self.update_proxy_list()
- self.xml.connect_signals(self)
- self.init_accounts()
- self.window.show_all()
-
- # Merge accounts
- st = app.config.get('mergeaccounts')
- checkbutton = self.xml.get_object('merge_checkbutton')
- checkbutton.set_active(st)
- # prevent roster redraws by connecting the signal after button state is
- # set
- checkbutton.connect('toggled', self.on_merge_checkbutton_toggled)
-
- self.avahi_available = True
- try:
- import avahi
- except ImportError:
- self.avahi_available = False
-
- self.xml.get_object('close_button').grab_focus()
-
- def on_accounts_window_key_press_event(self, widget, event):
- if event.keyval == Gdk.KEY_Escape:
- self.check_resend_relog()
- self.window.destroy()
-
- def select_account(self, account):
- model = self.accounts_treeview.get_model()
- iter_ = model.get_iter_first()
- while iter_:
- acct = model[iter_][0]
- if account == acct:
- self.accounts_treeview.set_cursor(model.get_path(iter_))
- return
- iter_ = model.iter_next(iter_)
-
- def init_accounts(self):
- """
- Initialize listStore with existing accounts
- """
- self.remove_button.set_sensitive(False)
- self.rename_button.set_sensitive(False)
- self.change_password_button.set_sensitive(False)
- self.current_account = None
- model = self.accounts_treeview.get_model()
- model.clear()
- list_ = app.config.get_per('accounts')
- list_.sort()
- for account in list_:
- iter_ = model.append()
- model.set(iter_, 0, account)
-
- self.selection = self.accounts_treeview.get_selection()
- self.selection.select_iter(model.get_iter_first())
-
- def resend(self, account):
- if not account in app.connections:
- return
- show = app.SHOW_LIST[app.connections[account].connected]
- status = app.connections[account].status
- app.connections[account].change_status(show, status)
-
- def check_resend_relog(self):
- if self.need_relogin and self.current_account == \
- app.ZEROCONF_ACC_NAME:
- if app.ZEROCONF_ACC_NAME in app.connections:
- app.connections[app.ZEROCONF_ACC_NAME].update_details()
- return
-
- elif self.need_relogin and self.current_account and \
- app.connections[self.current_account].connected > 0:
- def login(account, show_before, status_before):
- """
- Login with previous status
- """
- # first make sure connection is really closed,
- # 0.5 may not be enough
- app.connections[account].disconnect(True)
- app.interface.roster.send_status(account, show_before,
- status_before)
-
- def relog(account):
- self.dialog.destroy()
- show_before = app.SHOW_LIST[app.connections[account].\
- connected]
- status_before = app.connections[account].status
- app.interface.roster.send_status(account, 'offline',
- _('Be right back.'))
- GLib.timeout_add(500, login, account, show_before,
- status_before)
-
- def on_yes(checked, account):
- relog(account)
- def on_no(account):
- if self.resend_presence:
- self.resend(account)
- if self.current_account in app.connections:
- self.dialog = dialogs.YesNoDialog(_('Relogin now?'),
- _('If you want all the changes to apply instantly, '
- 'you must relogin.'), on_response_yes=(on_yes,
- self.current_account), on_response_no=(on_no,
- self.current_account))
- elif self.resend_presence:
- self.resend(self.current_account)
-
- self.need_relogin = False
- self.resend_presence = False
-
- def on_accounts_treeview_cursor_changed(self, widget):
- """
- Activate modify buttons when a row is selected, update accounts info
- """
- sel = self.accounts_treeview.get_selection()
- if sel:
- (model, iter_) = sel.get_selected()
- if iter_:
- account = model[iter_][0]
- else:
- account = None
- else:
- iter_ = account = None
- if self.current_account and self.current_account == account:
- # We're comming back to our current account, no need to update
- # widgets
- return
- # Save config for previous account if needed cause focus_out event is
- # called after the changed event
- if self.current_account and self.window.get_focus():
- focused_widget = self.window.get_focus()
- focused_widget_name = focused_widget.get_name()
- if focused_widget_name in ('jid_entry1', 'resource_entry1',
- 'custom_port_entry', 'cert_entry1'):
- if focused_widget_name == 'jid_entry1':
- func = self.on_jid_entry1_focus_out_event
- elif focused_widget_name == 'resource_entry1':
- func = self.on_resource_entry1_focus_out_event
- elif focused_widget_name == 'custom_port_entry':
- func = self.on_custom_port_entry_focus_out_event
- elif focused_widget_name == 'cert_entry1':
- func = self.on_cert_entry1_focus_out_event
- if func(focused_widget, None):
- # Error detected in entry, don't change account,
- # re-put cursor on previous row
- self.select_account(self.current_account)
- return True
- self.window.set_focus(widget)
-
- self.check_resend_relog()
-
- if account:
- self.remove_button.set_sensitive(True)
- self.rename_button.set_sensitive(True)
-
- if (account != app.ZEROCONF_ACC_NAME and
- account in app.connections):
- self.change_password_button.set_sensitive(
- app.connections[account].register_supported)
-
- else:
- self.remove_button.set_sensitive(False)
- self.rename_button.set_sensitive(False)
- self.change_password_button.set_sensitive(False)
- if iter_:
- self.current_account = account
- if account == app.ZEROCONF_ACC_NAME:
- self.remove_button.set_sensitive(False)
- self.init_account()
- self.update_proxy_list()
-
- def on_browse_for_client_cert_button_clicked(self, widget, data=None):
- def on_ok(widget, path_to_clientcert_file):
- self.dialog.destroy()
- if not path_to_clientcert_file:
- return
- self.xml.get_object('cert_entry1').set_text(path_to_clientcert_file)
- app.config.set_per('accounts', self.current_account,
- 'client_cert', path_to_clientcert_file)
-
- def on_cancel(widget):
- self.dialog.destroy()
-
- path_to_clientcert_file = self.xml.get_object('cert_entry1').get_text()
- self.dialog = dialogs.ClientCertChooserDialog(path_to_clientcert_file,
- on_ok, on_cancel)
-
- def update_proxy_list(self):
- if self.current_account:
- our_proxy = app.config.get_per('accounts', self.current_account,
- 'proxy')
- else:
- our_proxy = ''
-
- if not our_proxy:
- our_proxy = _('None')
- proxy_combobox = self.xml.get_object('proxies_combobox1')
- model = Gtk.ListStore(str)
- proxy_combobox.set_model(model)
- l = app.config.get_per('proxies')
- l.insert(0, _('None'))
- for i in range(len(l)):
- model.append([l[i]])
- if our_proxy == l[i]:
- proxy_combobox.set_active(i)
-
- def init_account(self):
- if not self.current_account:
- self.notebook.set_current_page(0)
- return
- if app.config.get_per('accounts', self.current_account,
- 'is_zeroconf'):
- self.ignore_events = True
- self.init_zeroconf_account()
- self.ignore_events = False
- self.notebook.set_current_page(2)
- return
- self.ignore_events = True
- self.init_normal_account()
- self.ignore_events = False
- self.notebook.set_current_page(1)
-
- def init_zeroconf_account(self):
- active = app.config.get_per('accounts', app.ZEROCONF_ACC_NAME,
- 'active')
- self.xml.get_object('enable_zeroconf_checkbutton2').set_active(active)
- if not app.HAVE_ZEROCONF:
- self.xml.get_object('enable_zeroconf_checkbutton2').set_sensitive(
- False)
- self.xml.get_object('zeroconf_notebook').set_sensitive(active)
- # General tab
- st = app.config.get_per('accounts', app.ZEROCONF_ACC_NAME,
- 'autoconnect')
- self.xml.get_object('autoconnect_checkbutton2').set_active(st)
-
- list_no_log_for = app.config.get_per('accounts',
- app.ZEROCONF_ACC_NAME, 'no_log_for').split()
- if app.ZEROCONF_ACC_NAME in list_no_log_for:
- self.xml.get_object('log_history_checkbutton2').set_active(0)
- else:
- self.xml.get_object('log_history_checkbutton2').set_active(1)
-
- st = app.config.get_per('accounts', app.ZEROCONF_ACC_NAME,
- 'sync_with_global_status')
- self.xml.get_object('sync_with_global_status_checkbutton2').set_active(
- st)
-
- st = app.config.get_per('accounts', app.ZEROCONF_ACC_NAME,
- 'use_custom_host')
- self.xml.get_object('custom_port_checkbutton2').set_active(st)
- self.xml.get_object('custom_port_entry2').set_sensitive(st)
-
- st = app.config.get_per('accounts', app.ZEROCONF_ACC_NAME,
- 'custom_port')
- if not st:
- app.config.set_per('accounts', app.ZEROCONF_ACC_NAME,
- 'custom_port', '5298')
- st = '5298'
- self.xml.get_object('custom_port_entry2').set_text(str(st))
-
- # Personal tab
- gpg_key_label = self.xml.get_object('gpg_key_label2')
- if app.ZEROCONF_ACC_NAME in app.connections and \
- app.connections[app.ZEROCONF_ACC_NAME].gpg:
- self.xml.get_object('gpg_choose_button2').set_sensitive(True)
- self.init_account_gpg()
- else:
- gpg_key_label.set_text(_('OpenPGP is not usable on this computer'))
- self.xml.get_object('gpg_choose_button2').set_sensitive(False)
-
- for opt in ('first_name', 'last_name', 'jabber_id', 'email'):
- st = app.config.get_per('accounts', app.ZEROCONF_ACC_NAME,
- 'zeroconf_' + opt)
- self.xml.get_object(opt + '_entry2').set_text(st)
-
- def init_account_gpg(self):
- account = self.current_account
- keyid = app.config.get_per('accounts', account, 'keyid')
- keyname = app.config.get_per('accounts', account, 'keyname')
- use_gpg_agent = app.config.get('use_gpg_agent')
-
- if account == app.ZEROCONF_ACC_NAME:
- widget_name_add = '2'
- else:
- widget_name_add = '1'
-
- gpg_key_label = self.xml.get_object('gpg_key_label' + widget_name_add)
- gpg_name_label = self.xml.get_object('gpg_name_label' + widget_name_add)
- use_gpg_agent_checkbutton = self.xml.get_object(
- 'use_gpg_agent_checkbutton' + widget_name_add)
-
- if not keyid:
- use_gpg_agent_checkbutton.set_sensitive(False)
- gpg_key_label.set_text(_('No key selected'))
- gpg_name_label.set_text('')
- return
-
- gpg_key_label.set_text(keyid)
- gpg_name_label.set_text(keyname)
- use_gpg_agent_checkbutton.set_sensitive(True)
- use_gpg_agent_checkbutton.set_active(use_gpg_agent)
-
- def draw_normal_jid(self):
- account = self.current_account
- self.ignore_events = True
- active = app.config.get_per('accounts', account, 'active')
- self.xml.get_object('enable_checkbutton1').set_active(active)
- self.xml.get_object('normal_notebook1').set_sensitive(active)
- if app.config.get_per('accounts', account, 'anonymous_auth'):
- self.xml.get_object('anonymous_checkbutton1').set_active(True)
- self.xml.get_object('jid_label1').set_text(_('Server:'))
- save_password = self.xml.get_object('save_password_checkbutton1')
- save_password.set_active(False)
- save_password.set_sensitive(False)
- password_entry = self.xml.get_object('password_entry1')
- password_entry.set_text('')
- password_entry.set_sensitive(False)
- jid = app.config.get_per('accounts', account, 'hostname')
- else:
- self.xml.get_object('anonymous_checkbutton1').set_active(False)
- self.xml.get_object('jid_label1').set_text(_('JID:'))
- savepass = app.config.get_per('accounts', account, 'savepass')
- save_password = self.xml.get_object('save_password_checkbutton1')
- save_password.set_sensitive(True)
- save_password.set_active(savepass)
- password_entry = self.xml.get_object('password_entry1')
- if savepass:
- passstr = passwords.get_password(account) or ''
- password_entry.set_sensitive(True)
- else:
- passstr = ''
- password_entry.set_sensitive(False)
- password_entry.set_text(passstr)
-
- jid = app.config.get_per('accounts', account, 'name') \
- + '@' + app.config.get_per('accounts', account, 'hostname')
- self.xml.get_object('jid_entry1').set_text(jid)
- self.ignore_events = False
-
- def init_normal_account(self):
- account = self.current_account
- # Account tab
- self.draw_normal_jid()
- self.xml.get_object('resource_entry1').set_text(app.config.get_per(
- 'accounts', account, 'resource'))
-
- client_cert = app.config.get_per('accounts', account, 'client_cert')
- self.xml.get_object('cert_entry1').set_text(client_cert)
- client_cert_encrypted = app.config.get_per('accounts', account,
- 'client_cert_encrypted')
- self.xml.get_object('client_cert_encrypted_checkbutton1').\
- set_active(client_cert_encrypted)
-
- self.xml.get_object('adjust_priority_with_status_checkbutton1').\
- set_active(app.config.get_per('accounts', account,
- 'adjust_priority_with_status'))
- spinbutton = self.xml.get_object('priority_spinbutton1')
- if app.config.get('enable_negative_priority'):
- spinbutton.set_range(-128, 127)
- else:
- spinbutton.set_range(0, 127)
- spinbutton.set_value(app.config.get_per('accounts', account,
- 'priority'))
-
- # Connection tab
- use_env_http_proxy = app.config.get_per('accounts', account,
- 'use_env_http_proxy')
- self.xml.get_object('use_env_http_proxy_checkbutton1').set_active(
- use_env_http_proxy)
- self.xml.get_object('proxy_hbox1').set_sensitive(not use_env_http_proxy)
-
- warn_when_insecure_ssl = app.config.get_per('accounts', account,
- 'warn_when_insecure_ssl_connection')
- self.xml.get_object('warn_when_insecure_connection_checkbutton1').\
- set_active(warn_when_insecure_ssl)
-
- self.xml.get_object('send_keepalive_checkbutton1').set_active(
- app.config.get_per('accounts', account, 'keep_alives_enabled'))
-
- use_custom_host = app.config.get_per('accounts', account,
- 'use_custom_host')
- self.xml.get_object('custom_host_port_checkbutton1').set_active(
- use_custom_host)
- custom_host = app.config.get_per('accounts', account, 'custom_host')
- if not custom_host:
- custom_host = app.config.get_per('accounts', account, 'hostname')
- app.config.set_per('accounts', account, 'custom_host',
- custom_host)
- self.xml.get_object('custom_host_entry1').set_text(custom_host)
- custom_port = app.config.get_per('accounts', account, 'custom_port')
- if not custom_port:
- custom_port = 5222
- app.config.set_per('accounts', account, 'custom_port',
- custom_port)
- self.xml.get_object('custom_port_entry1').set_text(str(custom_port))
-
- # Personal tab
- gpg_key_label = self.xml.get_object('gpg_key_label1')
- if app.HAVE_GPG:
- self.xml.get_object('gpg_choose_button1').set_sensitive(True)
- self.init_account_gpg()
- else:
- gpg_key_label.set_text(_('OpenPGP is not usable on this computer'))
- self.xml.get_object('gpg_choose_button1').set_sensitive(False)
-
- # General tab
- self.xml.get_object('autoconnect_checkbutton1').set_active(
- app.config.get_per('accounts', account, 'autoconnect'))
- self.xml.get_object('autoreconnect_checkbutton1').set_active(app.
- config.get_per('accounts', account, 'autoreconnect'))
-
- list_no_log_for = app.config.get_per('accounts', account,
- 'no_log_for').split()
- if account in list_no_log_for:
- self.xml.get_object('log_history_checkbutton1').set_active(False)
- else:
- self.xml.get_object('log_history_checkbutton1').set_active(True)
-
- self.xml.get_object('sync_logs_with_server_checkbutton1').set_active(
- app.config.get_per('accounts', account, 'sync_logs_with_server'))
- self.xml.get_object('sync_with_global_status_checkbutton1').set_active(
- app.config.get_per('accounts', account,
- 'sync_with_global_status'))
- self.xml.get_object('carbons_checkbutton1').set_active(
- app.config.get_per('accounts', account, 'enable_message_carbons'))
- self.xml.get_object('use_ft_proxies_checkbutton1').set_active(
- app.config.get_per('accounts', account, 'use_ft_proxies'))
-
- def on_add_button_clicked(self, widget):
- """
- When add button is clicked: open an account information window
- """
- if 'account_creation_wizard' in app.interface.instances:
- app.interface.instances['account_creation_wizard'].window.present()
- else:
- app.interface.instances['account_creation_wizard'] = \
- AccountCreationWizardWindow()
-
- def on_remove_button_clicked(self, widget):
- """
- When delete button is clicked: Remove an account from the listStore and
- from the config file
- """
- if not self.current_account:
- return
- account = self.current_account
- if len(app.events.get_events(account)):
- dialogs.ErrorDialog(_('Unread events'),
- _('Read all pending events before removing this account.'),
- transient_for=self.window)
- return
-
- if app.config.get_per('accounts', account, 'is_zeroconf'):
- # Should never happen as button is insensitive
- return
-
- win_opened = False
- if app.interface.msg_win_mgr.get_controls(acct=account):
- win_opened = True
- elif account in app.interface.instances:
- for key in app.interface.instances[account]:
- if app.interface.instances[account][key] and key != \
- 'remove_account':
- win_opened = True
- break
- # Detect if we have opened windows for this account
- def remove(account):
- if account in app.interface.instances and \
- 'remove_account' in app.interface.instances[account]:
- app.interface.instances[account]['remove_account'].window.\
- present()
- else:
- if not account in app.interface.instances:
- app.interface.instances[account] = {}
- app.interface.instances[account]['remove_account'] = \
- RemoveAccountWindow(account)
- if win_opened:
- dialogs.ConfirmationDialog(
- _('You have opened chat in account %s') % account,
- _('All chat and groupchat windows will be closed. Do you want to '
- 'continue?'),
- on_response_ok = (remove, account))
- else:
- remove(account)
-
- def on_rename_button_clicked(self, widget):
- if not self.current_account:
- return
- active = app.config.get_per('accounts', self.current_account,
- 'active') and self.current_account in app.connections
- if active and app.connections[self.current_account].connected != 0:
- dialogs.ErrorDialog(
- _('You are currently connected to the server'),
- _('To change the account name, you must be disconnected.'),
- transient_for=self.window)
- return
- if len(app.events.get_events(self.current_account)):
- dialogs.ErrorDialog(_('Unread events'),
- _('To change the account name, you must read all pending '
- 'events.'), transient_for=self.window)
- return
- # Get the new name
- def on_renamed(new_name, old_name):
- if new_name in app.connections:
- dialogs.ErrorDialog(_('Account Name Already Used'),
- _('This name is already used by another of your accounts. '
- 'Please choose another name.'), transient_for=self.window)
- return
- if (new_name == ''):
- dialogs.ErrorDialog(_('Invalid account name'),
- _('Account name cannot be empty.'),
- transient_for=self.window)
- return
- if new_name.find(' ') != -1:
- dialogs.ErrorDialog(_('Invalid account name'),
- _('Account name cannot contain spaces.'),
- transient_for=self.window)
- return
- if active:
- # update variables
- app.interface.instances[new_name] = app.interface.instances[
- old_name]
- app.interface.minimized_controls[new_name] = \
- app.interface.minimized_controls[old_name]
- app.nicks[new_name] = app.nicks[old_name]
- app.block_signed_in_notifications[new_name] = \
- app.block_signed_in_notifications[old_name]
- app.groups[new_name] = app.groups[old_name]
- app.gc_connected[new_name] = app.gc_connected[old_name]
- app.automatic_rooms[new_name] = app.automatic_rooms[
- old_name]
- app.newly_added[new_name] = app.newly_added[old_name]
- app.to_be_removed[new_name] = app.to_be_removed[old_name]
- app.sleeper_state[new_name] = app.sleeper_state[old_name]
- app.encrypted_chats[new_name] = app.encrypted_chats[
- old_name]
- app.last_message_time[new_name] = \
- app.last_message_time[old_name]
- app.status_before_autoaway[new_name] = \
- app.status_before_autoaway[old_name]
- app.transport_avatar[new_name] = app.transport_avatar[old_name]
- app.gajim_optional_features[new_name] = \
- app.gajim_optional_features[old_name]
- app.caps_hash[new_name] = app.caps_hash[old_name]
-
- app.contacts.change_account_name(old_name, new_name)
- app.events.change_account_name(old_name, new_name)
-
- # change account variable for chat / gc controls
- app.interface.msg_win_mgr.change_account_name(old_name, new_name)
- # upgrade account variable in opened windows
- for kind in ('infos', 'disco', 'gc_config', 'search',
- 'online_dialog', 'sub_request'):
- for j in app.interface.instances[new_name][kind]:
- app.interface.instances[new_name][kind][j].account = \
- new_name
-
- # ServiceCache object keep old property account
- if hasattr(app.connections[old_name], 'services_cache'):
- app.connections[old_name].services_cache.account = \
- new_name
- del app.interface.instances[old_name]
- del app.interface.minimized_controls[old_name]
- del app.nicks[old_name]
- del app.block_signed_in_notifications[old_name]
- del app.groups[old_name]
- del app.gc_connected[old_name]
- del app.automatic_rooms[old_name]
- del app.newly_added[old_name]
- del app.to_be_removed[old_name]
- del app.sleeper_state[old_name]
- del app.encrypted_chats[old_name]
- del app.last_message_time[old_name]
- del app.status_before_autoaway[old_name]
- del app.transport_avatar[old_name]
- del app.gajim_optional_features[old_name]
- del app.caps_hash[old_name]
- app.connections[old_name].name = new_name
- app.connections[old_name].pep_change_account_name(new_name)
- app.connections[old_name].caps_change_account_name(new_name)
- app.connections[new_name] = app.connections[old_name]
- del app.connections[old_name]
- app.config.add_per('accounts', new_name)
- old_config = app.config.get_per('accounts', old_name)
- for opt in old_config:
- app.config.set_per('accounts', new_name, opt, old_config[opt])
- app.config.del_per('accounts', old_name)
- if self.current_account == old_name:
- self.current_account = new_name
- if old_name == app.ZEROCONF_ACC_NAME:
- app.ZEROCONF_ACC_NAME = new_name
- # refresh roster
- app.interface.roster.setup_and_draw_roster()
- self.init_accounts()
- self.select_account(new_name)
- app.app.remove_account_actions(old_name)
- app.app.add_account_actions(new_name)
- gui_menu_builder.build_accounts_menu()
-
- title = _('Rename Account')
- message = _('Enter a new name for account %s') % self.current_account
- old_text = self.current_account
- dialogs.InputDialog(title, message, old_text, is_modal=False,
- ok_handler=(on_renamed, self.current_account),
- transient_for=self.window)
-
- def option_changed(self, option, value):
- return app.config.get_per('accounts', self.current_account, option) \
- != value
-
- def on_jid_entry1_focus_out_event(self, widget, event):
- if self.ignore_events:
- return
- jid = widget.get_text()
- # check if jid is conform to RFC and stringprep it
- try:
- jid = helpers.parse_jid(jid)
- except helpers.InvalidFormat as s:
- if not widget.is_focus():
- pritext = _('Invalid JID')
- dialogs.ErrorDialog(pritext, str(s), transient_for=self.window)
- GLib.idle_add(lambda: widget.grab_focus())
- return True
-
- jid_splited = jid.split('@', 1)
- if len(jid_splited) != 2 and not app.config.get_per('accounts',
- self.current_account, 'anonymous_auth'):
- if not widget.is_focus():
- pritext = _('Invalid JID')
- sectext = \
- _('A JID must be in the form "user@servername".')
- dialogs.ErrorDialog(pritext, sectext, transient_for=self.window)
- GLib.idle_add(lambda: widget.grab_focus())
- return True
-
-
- if app.config.get_per('accounts', self.current_account,
- 'anonymous_auth'):
- app.config.set_per('accounts', self.current_account, 'hostname',
- jid_splited[0])
- if self.option_changed('hostname', jid_splited[0]):
- self.need_relogin = True
- else:
- if self.option_changed('name', jid_splited[0]) or \
- self.option_changed('hostname', jid_splited[1]):
- self.need_relogin = True
-
- app.config.set_per('accounts', self.current_account, 'name',
- jid_splited[0])
- app.config.set_per('accounts', self.current_account, 'hostname',
- jid_splited[1])
-
- def on_cert_entry1_focus_out_event(self, widget, event):
- if self.ignore_events:
- return
- client_cert = widget.get_text()
- if self.option_changed('client_cert', client_cert):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account, 'client_cert',
- client_cert)
-
- def on_anonymous_checkbutton1_toggled(self, widget):
- if self.ignore_events:
- return
- active = widget.get_active()
- app.config.set_per('accounts', self.current_account, 'anonymous_auth',
- active)
- self.draw_normal_jid()
-
- def on_password_entry1_changed(self, widget):
- if self.ignore_events:
- return
- passwords.save_password(self.current_account, widget.get_text())
-
- def on_save_password_checkbutton1_toggled(self, widget):
- if self.ignore_events:
- return
- active = widget.get_active()
- password_entry = self.xml.get_object('password_entry1')
- password_entry.set_sensitive(active)
- app.config.set_per('accounts', self.current_account, 'savepass',
- active)
- if active:
- password = password_entry.get_text()
- passwords.save_password(self.current_account, password)
- else:
- passwords.save_password(self.current_account, '')
-
- def on_resource_entry1_focus_out_event(self, widget, event):
- if self.ignore_events:
- return
- resource = self.xml.get_object('resource_entry1').get_text()
- try:
- resource = helpers.parse_resource(resource)
- except helpers.InvalidFormat as s:
- if not widget.is_focus():
- pritext = _('Invalid JID')
- dialogs.ErrorDialog(pritext, str(s), transient_for=self.window)
- GLib.idle_add(lambda: widget.grab_focus())
- return True
-
- if self.option_changed('resource', resource):
- self.need_relogin = True
-
- app.config.set_per('accounts', self.current_account, 'resource',
- resource)
-
- def on_adjust_priority_with_status_checkbutton1_toggled(self, widget):
- self.xml.get_object('priority_spinbutton1').set_sensitive(
- not widget.get_active())
- self.on_checkbutton_toggled(widget, 'adjust_priority_with_status',
- account = self.current_account)
-
- def on_priority_spinbutton1_value_changed(self, widget):
- prio = widget.get_value_as_int()
-
- if self.option_changed('priority', prio):
- self.resend_presence = True
-
- app.config.set_per('accounts', self.current_account, 'priority', prio)
-
- def on_synchronise_contacts_button1_clicked(self, widget):
- try:
- dialogs.SynchroniseSelectAccountDialog(self.current_account)
- except GajimGeneralException:
- # If we showed ErrorDialog, there will not be dialog instance
- return
-
- def on_change_password_button1_clicked(self, widget):
- def on_changed(new_password):
- if new_password is not None:
- app.connections[self.current_account].change_password(
- new_password)
- if self.xml.get_object('save_password_checkbutton1').\
- get_active():
- self.xml.get_object('password_entry1').set_text(
- new_password)
-
- try:
- dialogs.ChangePasswordDialog(self.current_account, on_changed,
- self.window)
- except GajimGeneralException:
- # if we showed ErrorDialog, there will not be dialog instance
- return
-
- def on_client_cert_encrypted_checkbutton1_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'client_cert_encrypted',
- account=self.current_account)
-
- def on_autoconnect_checkbutton_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'autoconnect',
- account=self.current_account)
-
- def on_autoreconnect_checkbutton_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'autoreconnect',
- account=self.current_account)
-
- def on_log_history_checkbutton_toggled(self, widget):
- if self.ignore_events:
- return
- list_no_log_for = app.config.get_per('accounts', self.current_account,
- 'no_log_for').split()
- if self.current_account in list_no_log_for:
- list_no_log_for.remove(self.current_account)
-
- if not widget.get_active():
- list_no_log_for.append(self.current_account)
- app.config.set_per('accounts', self.current_account, 'no_log_for',
- ' '.join(list_no_log_for))
-
- def on_sync_logs_with_server_checkbutton_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'sync_logs_with_server',
- account=self.current_account)
-
- def on_sync_with_global_status_checkbutton_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'sync_with_global_status',
- account=self.current_account)
- app.interface.roster.update_status_combobox()
-
- def on_carbons_checkbutton_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'enable_message_carbons',
- account=self.current_account)
-
- def on_use_ft_proxies_checkbutton1_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'use_ft_proxies',
- account=self.current_account)
-
- def on_use_env_http_proxy_checkbutton1_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'use_env_http_proxy',
- account=self.current_account)
- hbox = self.xml.get_object('proxy_hbox1')
- hbox.set_sensitive(not widget.get_active())
-
- def on_proxies_combobox1_changed(self, widget):
- active = widget.get_active()
- proxy = widget.get_model()[active][0]
- if proxy == _('None'):
- proxy = ''
-
- if self.option_changed('proxy', proxy):
- self.need_relogin = True
-
- app.config.set_per('accounts', self.current_account, 'proxy', proxy)
-
- def on_manage_proxies_button1_clicked(self, widget):
- if 'manage_proxies' in app.interface.instances:
- app.interface.instances['manage_proxies'].window.present()
- else:
- app.interface.instances['manage_proxies'] = ManageProxiesWindow(
- self.window)
-
- def on_warn_when_insecure_connection_checkbutton1_toggled(self, widget):
- if self.ignore_events:
- return
-
- self.on_checkbutton_toggled(widget, 'warn_when_insecure_ssl_connection',
- account=self.current_account)
-
- def on_send_keepalive_checkbutton1_toggled(self, widget):
- if self.ignore_events:
- return
- self.on_checkbutton_toggled(widget, 'keep_alives_enabled',
- account=self.current_account)
- app.config.set_per('accounts', self.current_account,
- 'ping_alives_enabled', widget.get_active())
-
- def on_custom_host_port_checkbutton1_toggled(self, widget):
- if self.option_changed('use_custom_host', widget.get_active()):
- self.need_relogin = True
-
- self.on_checkbutton_toggled(widget, 'use_custom_host',
- account=self.current_account)
- active = widget.get_active()
- self.xml.get_object('custom_host_port_hbox1').set_sensitive(active)
-
- def on_custom_host_entry1_changed(self, widget):
- if self.ignore_events:
- return
- host = widget.get_text()
- if self.option_changed('custom_host', host):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account, 'custom_host',
- host)
-
- def on_custom_port_entry_focus_out_event(self, widget, event):
- if self.ignore_events:
- return
- custom_port = widget.get_text()
- try:
- custom_port = int(custom_port)
- except Exception:
- if not widget.is_focus():
- dialogs.ErrorDialog(_('Invalid entry'),
- _('Custom port must be a port number.'),
- transient_for=self.window)
- GLib.idle_add(lambda: widget.grab_focus())
- return True
- if self.option_changed('custom_port', custom_port):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account, 'custom_port',
- custom_port)
-
- def on_gpg_choose_button_clicked(self, widget, data = None):
- if self.current_account in app.connections and \
- app.connections[self.current_account].gpg:
- secret_keys = app.connections[self.current_account].\
- ask_gpg_secrete_keys()
-
- # self.current_account is None and/or app.connections is {}
- else:
- if app.HAVE_GPG:
- secret_keys = gpg.GnuPG().get_secret_keys()
- else:
- secret_keys = []
- if not secret_keys:
- dialogs.ErrorDialog(_('Failed to get secret keys'),
- _('There is no OpenPGP secret key available.'),
- transient_for=self.window)
- secret_keys[_('None')] = _('None')
-
- def on_key_selected(keyID):
- if keyID is None:
- return
- if self.current_account == app.ZEROCONF_ACC_NAME:
- wiget_name_ext = '2'
- else:
- wiget_name_ext = '1'
- gpg_key_label = self.xml.get_object('gpg_key_label' + \
- wiget_name_ext)
- gpg_name_label = self.xml.get_object('gpg_name_label' + \
- wiget_name_ext)
- use_gpg_agent_checkbutton = self.xml.get_object(
- 'use_gpg_agent_checkbutton' + wiget_name_ext)
- if keyID[0] == _('None'):
- gpg_key_label.set_text(_('No key selected'))
- gpg_name_label.set_text('')
- use_gpg_agent_checkbutton.set_sensitive(False)
- if self.option_changed('keyid', ''):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account,
- 'keyname', '')
- app.config.set_per('accounts', self.current_account, 'keyid',
- '')
- else:
- gpg_key_label.set_text(keyID[0])
- gpg_name_label.set_text(keyID[1])
- use_gpg_agent_checkbutton.set_sensitive(True)
- if self.option_changed('keyid', keyID[0]):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account,
- 'keyname', keyID[1])
- app.config.set_per('accounts', self.current_account, 'keyid',
- keyID[0])
-
- dialogs.ChooseGPGKeyDialog(_('OpenPGP Key Selection'),
- _('Choose your OpenPGP key'), secret_keys, on_key_selected,
- transient_for=self.window)
-
- def on_use_gpg_agent_checkbutton_toggled(self, widget):
- self.on_checkbutton_toggled(widget, 'use_gpg_agent')
-
- def on_edit_details_button1_clicked(self, widget):
- if self.current_account not in app.interface.instances:
- dlg = dialogs.ErrorDialog(_('No such account available'),
- _('You must create your account before editing your personal '
- 'information.'), transient_for=self.window)
- return
-
- # show error dialog if account is newly created (not in app.connections)
- if self.current_account not in app.connections or \
- app.connections[self.current_account].connected < 2:
- dialogs.ErrorDialog(_('You are not connected to the server'),
- _('Without a connection, you can not edit your personal '
- 'information.'), transient_for=self.window)
- return
-
- if not app.connections[self.current_account].vcard_supported:
- dialogs.ErrorDialog(_("Your server does not have vCard support"),
- _("Your server can't save your personal information."),
- transient_for=self.window)
- return
-
- jid = app.get_jid_from_account(self.current_account)
- if 'profile' not in app.interface.instances[self.current_account]:
- app.interface.instances[self.current_account]['profile'] = \
- profile_window.ProfileWindow(self.current_account, transient_for=self.window)
- app.connections[self.current_account].request_vcard(jid)
-
- def on_checkbutton_toggled(self, widget, config_name,
- change_sensitivity_widgets = None, account = None):
- if account:
- app.config.set_per('accounts', account, config_name,
- widget.get_active())
- else:
- app.config.set(config_name, widget.get_active())
- if change_sensitivity_widgets:
- for w in change_sensitivity_widgets:
- w.set_sensitive(widget.get_active())
-
- def on_merge_checkbutton_toggled(self, widget):
- self.on_checkbutton_toggled(widget, 'mergeaccounts')
- if len(app.connections) >= 2: # Do not merge accounts if only one active
- app.interface.roster.regroup = app.config.get('mergeaccounts')
- else:
- app.interface.roster.regroup = False
- app.interface.roster.setup_and_draw_roster()
-
- def _disable_account(self, account):
- app.interface.roster.close_all(account)
- if account == app.ZEROCONF_ACC_NAME:
- app.connections[account].disable_account()
- app.connections[account].cleanup()
- del app.connections[account]
- del app.interface.instances[account]
- del app.interface.minimized_controls[account]
- del app.nicks[account]
- del app.block_signed_in_notifications[account]
- del app.groups[account]
- app.contacts.remove_account(account)
- del app.gc_connected[account]
- del app.automatic_rooms[account]
- del app.to_be_removed[account]
- del app.newly_added[account]
- del app.sleeper_state[account]
- del app.encrypted_chats[account]
- del app.last_message_time[account]
- del app.status_before_autoaway[account]
- del app.transport_avatar[account]
- del app.gajim_optional_features[account]
- del app.caps_hash[account]
- if len(app.connections) >= 2:
- # Do not merge accounts if only one exists
- app.interface.roster.regroup = app.config.get('mergeaccounts')
- else:
- app.interface.roster.regroup = False
- app.interface.roster.setup_and_draw_roster()
- app.app.remove_account_actions(account)
- gui_menu_builder.build_accounts_menu()
-
- def _enable_account(self, account):
- if account == app.ZEROCONF_ACC_NAME:
- app.connections[account] = connection_zeroconf.ConnectionZeroconf(
- account)
- if app.connections[account].gpg:
- self.xml.get_object('gpg_choose_button2').set_sensitive(True)
- else:
- app.connections[account] = connection.Connection(account)
- if app.connections[account].gpg:
- self.xml.get_object('gpg_choose_button1').set_sensitive(True)
- self.init_account_gpg()
- # update variables
- app.interface.instances[account] = {'infos': {},
- 'disco': {}, 'gc_config': {}, 'search': {}, 'online_dialog': {},
- 'sub_request': {}}
- app.interface.minimized_controls[account] = {}
- app.connections[account].connected = 0
- app.groups[account] = {}
- app.contacts.add_account(account)
- app.gc_connected[account] = {}
- app.automatic_rooms[account] = {}
- app.newly_added[account] = []
- app.to_be_removed[account] = []
- if account == app.ZEROCONF_ACC_NAME:
- app.nicks[account] = app.ZEROCONF_ACC_NAME
- else:
- app.nicks[account] = app.config.get_per('accounts', account,
- 'name')
- app.block_signed_in_notifications[account] = True
- app.sleeper_state[account] = 'off'
- app.encrypted_chats[account] = []
- app.last_message_time[account] = {}
- app.status_before_autoaway[account] = ''
- app.transport_avatar[account] = {}
- app.gajim_optional_features[account] = []
- app.caps_hash[account] = ''
- helpers.update_optional_features(account)
- # refresh roster
- if len(app.connections) >= 2:
- # Do not merge accounts if only one exists
- app.interface.roster.regroup = app.config.get('mergeaccounts')
- else:
- app.interface.roster.regroup = False
- app.interface.roster.setup_and_draw_roster()
- app.app.add_account_actions(account)
- gui_menu_builder.build_accounts_menu()
-
- def on_enable_zeroconf_checkbutton2_toggled(self, widget):
- # don't do anything if there is an account with the local name but is a
- # normal account
- if self.ignore_events:
- return
- if self.current_account in app.connections and \
- app.connections[self.current_account].connected > 0:
- self.ignore_events = True
- self.xml.get_object('enable_zeroconf_checkbutton2').set_active(True)
- self.ignore_events = False
- dialogs.ErrorDialog(
- _('You are currently connected to the server'),
- _('To disable the account, you must be disconnected.'),
- transient_for=self.window)
- return
- if app.ZEROCONF_ACC_NAME in app.connections and not \
- app.connections[app.ZEROCONF_ACC_NAME].is_zeroconf:
- app.nec.push_incoming_event(InformationEvent(None,
- conn=app.connections[app.ZEROCONF_ACC_NAME],
- level='error', pri_txt=_('Account Local already exists.'),
- sec_txt=_('Please rename or remove it before enabling '
- 'link-local messaging.')))
- return
-
- if app.config.get_per('accounts', app.ZEROCONF_ACC_NAME, 'active') \
- and not widget.get_active():
- self.xml.get_object('zeroconf_notebook').set_sensitive(False)
- # disable
- self._disable_account(app.ZEROCONF_ACC_NAME)
-
- elif not app.config.get_per('accounts', app.ZEROCONF_ACC_NAME,
- 'active') and widget.get_active():
- self.xml.get_object('zeroconf_notebook').set_sensitive(True)
- # enable (will create new account if not present)
- self._enable_account(app.ZEROCONF_ACC_NAME)
-
- self.on_checkbutton_toggled(widget, 'active',
- account=app.ZEROCONF_ACC_NAME)
-
- def on_enable_checkbutton1_toggled(self, widget):
- if self.ignore_events:
- return
- if self.current_account in app.connections and \
- app.connections[self.current_account].connected > 0:
- # connecting or connected
- self.ignore_events = True
- self.xml.get_object('enable_checkbutton1').set_active(True)
- self.ignore_events = False
- dialogs.ErrorDialog(
- _('You are currently connected to the server'),
- _('To disable the account, you must be disconnected.'),
- transient_for=self.window)
- return
- # add/remove account in roster and all variables
- if widget.get_active():
- # enable
- self._enable_account(self.current_account)
- else:
- # disable
- self._disable_account(self.current_account)
- self.on_checkbutton_toggled(widget, 'active',
- account=self.current_account, change_sensitivity_widgets=[
- self.xml.get_object('normal_notebook1')])
-
- def on_custom_port_checkbutton2_toggled(self, widget):
- self.xml.get_object('custom_port_entry2').set_sensitive(
- widget.get_active())
- self.on_checkbutton_toggled(widget, 'use_custom_host',
- account=self.current_account)
- if not widget.get_active():
- self.xml.get_object('custom_port_entry2').set_text('5298')
-
- def on_first_name_entry2_changed(self, widget):
- if self.ignore_events:
- return
- name = widget.get_text()
- if self.option_changed('zeroconf_first_name', name):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account,
- 'zeroconf_first_name', name)
-
- def on_last_name_entry2_changed(self, widget):
- if self.ignore_events:
- return
- name = widget.get_text()
- if self.option_changed('zeroconf_last_name', name):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account,
- 'zeroconf_last_name', name)
-
- def on_jabber_id_entry2_changed(self, widget):
- if self.ignore_events:
- return
- id_ = widget.get_text()
- if self.option_changed('zeroconf_jabber_id', id_):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account,
- 'zeroconf_jabber_id', id_)
-
- def on_email_entry2_changed(self, widget):
- if self.ignore_events:
- return
- email = widget.get_text()
- if self.option_changed('zeroconf_email', email):
- self.need_relogin = True
- app.config.set_per('accounts', self.current_account,
- 'zeroconf_email', email)
-
class FakeDataForm(Gtk.Table, object):
"""
Class for forms that are in XML format value1 infos in a
@@ -3158,10 +1944,10 @@ class RemoveAccountWindow:
app.app.remove_account_actions(self.account)
gui_menu_builder.build_accounts_menu()
if 'accounts' in app.interface.instances:
- app.interface.instances['accounts'].init_accounts()
- app.interface.instances['accounts'].init_account()
+ app.interface.instances['accounts'].remove_account(self.account)
self.window.destroy()
+
#---------- ManageBookmarksWindow class -------------#
class ManageBookmarksWindow:
def __init__(self):
@@ -3733,7 +2519,7 @@ class AccountCreationWizardWindow:
self.account = server
i = 1
- while self.account in app.connections:
+ while self.account in app.config.get_per('accounts'):
self.account = server + str(i)
i += 1
@@ -3754,7 +2540,7 @@ class AccountCreationWizardWindow:
return
self.account = server
i = 1
- while self.account in app.connections:
+ while self.account in app.config.get_per('accounts'):
self.account = server + str(i)
i += 1
@@ -3982,7 +2768,7 @@ class AccountCreationWizardWindow:
def on_advanced_button_clicked(self, widget):
if 'accounts' in app.interface.instances:
- app.interface.instances['accounts'].window.present()
+ app.interface.instances['accounts'].present()
else:
app.interface.instances['accounts'] = AccountsWindow()
app.interface.instances['accounts'].select_account(self.account)
@@ -4083,9 +2869,11 @@ class AccountCreationWizardWindow:
app.gajim_optional_features[self.account] = []
app.caps_hash[self.account] = ''
helpers.update_optional_features(self.account)
+ # action must be added before account window is updated
+ app.app.add_account_actions(self.account)
# refresh accounts window
if 'accounts' in app.interface.instances:
- app.interface.instances['accounts'].init_accounts()
+ app.interface.instances['accounts'].add_account(self.account)
# refresh roster
if len(app.connections) >= 2:
# Do not merge accounts if only one exists
@@ -4093,7 +2881,6 @@ class AccountCreationWizardWindow:
else:
app.interface.roster.regroup = False
app.interface.roster.setup_and_draw_roster()
- app.app.add_account_actions(self.account)
gui_menu_builder.build_accounts_menu()
class ManagePEPServicesWindow:
diff --git a/gajim/dialogs.py b/gajim/dialogs.py
index 3ae5d6301..8a3739e5e 100644
--- a/gajim/dialogs.py
+++ b/gajim/dialogs.py
@@ -48,6 +48,8 @@ from random import randrange
from gajim.common import pep
from gajim.common import ged
from gajim.common import const
+from gajim.options_dialog import OptionsDialog
+from gajim.common.const import Option, OptionKind, OptionType
try:
from gajim import gtkspell
@@ -2675,7 +2677,7 @@ class SynchroniseSelectAccountDialog:
self.account = account
self.xml = gtkgui_helpers.get_gtk_builder('synchronise_select_account_dialog.ui')
self.dialog = self.xml.get_object('synchronise_select_account_dialog')
- self.dialog.set_transient_for(app.interface.instances['accounts'].window)
+ self.dialog.set_transient_for(app.interface.instances['accounts'])
self.accounts_treeview = self.xml.get_object('accounts_treeview')
model = Gtk.ListStore(str, str, bool)
self.accounts_treeview.set_model(model)
@@ -2731,6 +2733,10 @@ class SynchroniseSelectAccountDialog:
return
self.dialog.destroy()
+ @staticmethod
+ def on_destroy(widget):
+ del app.interface.instances['import_contacts']
+
class SynchroniseSelectContactsDialog:
def __init__(self, account, remote_account):
self.local_account = account
@@ -3323,6 +3329,8 @@ class XMLConsoleWindow(Gtk.Window):
setattr(self, obj, self.builder.get_object(obj))
self.set_titlebar(self.headerbar)
+ jid = app.get_jid_from_account(account)
+ self.headerbar.set_subtitle(jid)
self.set_default_size(600, 600)
self.add(self.box)
@@ -3434,26 +3442,30 @@ class XMLConsoleWindow(Gtk.Window):
def on_filter_options(self, *args):
options = [
- SwitchOption('Presence', self.presence,
- self.on_option,
- 'presence'),
- SwitchOption('Message', self.message,
- self.on_option,
- 'message'),
- SwitchOption('Iq', self.iq,
- self.on_option,
- 'iq'),
- SwitchOption('Stream\nManagement', self.stream,
- self.on_option,
- 'stream'),
- SwitchOption('In', self.incoming,
- self.on_option,
- 'incoming'),
- SwitchOption('Out', self.outgoing,
- self.on_option,
- 'outgoing')]
+ Option(OptionKind.SWITCH, 'Presence',
+ OptionType.BOOL, self.presence,
+ callback=self.on_option, data='presence'),
- OptionsDialog(self, 'Filter', options)
+ Option(OptionKind.SWITCH, 'Message',
+ OptionType.BOOL, self.message,
+ callback=self.on_option, data='message'),
+
+ Option(OptionKind.SWITCH, 'Iq', OptionType.BOOL, self.iq,
+ callback=self.on_option, data='iq'),
+
+ Option(OptionKind.SWITCH, 'Stream\nManagement',
+ OptionType.BOOL, self.stream,
+ callback=self.on_option, data='stream'),
+
+ Option(OptionKind.SWITCH, 'In', OptionType.BOOL, self.incoming,
+ callback=self.on_option, data='incoming'),
+
+ Option(OptionKind.SWITCH, 'Out', OptionType.BOOL, self.outgoing,
+ callback=self.on_option, data='outgoing'),
+ ]
+
+ OptionsDialog(self, 'Filter', Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ options, self.account)
def on_clear(self, *args):
buffer_ = self.textview.get_buffer().set_text('')
@@ -3468,13 +3480,12 @@ class XMLConsoleWindow(Gtk.Window):
def on_enable(self, switch, param):
self.enabled = switch.get_active()
- def on_option(self, switch, param, *user_data):
- kind = user_data[0]
- setattr(self, kind, switch.get_active())
- value = not switch.get_active()
+ def on_option(self, value, data):
+ setattr(self, data, value)
+ value = not value
table = self.textview.get_buffer().get_tag_table()
- tag = table.lookup(kind)
- if kind in ('incoming', 'outgoing'):
+ tag = table.lookup(data)
+ if data in ('incoming', 'outgoing'):
if value:
tag.set_priority(table.get_size() - 1)
else:
@@ -3522,58 +3533,6 @@ class XMLConsoleWindow(Gtk.Window):
if at_the_end:
GLib.idle_add(gtkgui_helpers.scroll_to_end, self.scrolled)
-
-class OptionsDialog(Gtk.Dialog):
- def __init__(self, parent, title, options):
- Gtk.Dialog.__init__(self, title, parent,
- Gtk.DialogFlags.DESTROY_WITH_PARENT)
- self.set_name('OptionsDialog')
- self.set_resizable(False)
- self.set_default_size(250, -1)
-
- self.remove(self.get_content_area())
-
- listbox = Gtk.ListBox()
- listbox.set_hexpand(True)
- listbox.set_selection_mode(Gtk.SelectionMode.NONE)
-
- for option in options:
- listbox.add(option)
-
- self.add(listbox)
-
- self.show_all()
- listbox.connect('row-activated', self.on_row_activated)
-
- def on_row_activated(self, listbox, row):
- row.get_child().set_switch_state()
-
-
-class SwitchOption(Gtk.Grid):
- def __init__(self, label, state, callback, *user_data):
- Gtk.Grid.__init__(self)
- self.set_column_spacing(6)
-
- label = Gtk.Label(label=label)
- label.set_hexpand(True)
- label.set_halign(Gtk.Align.START)
-
- self.switch = Gtk.Switch()
- self.switch.set_active(state)
- self.switch.connect("notify::active", callback, *user_data)
- self.switch.set_hexpand(True)
- self.switch.set_halign(Gtk.Align.END)
- self.switch.set_valign(Gtk.Align.CENTER)
-
- self.add(label)
- self.add(self.switch)
- self.show_all()
-
- def set_switch_state(self):
- state = self.switch.get_active()
- self.switch.set_active(not state)
-
-
#Action that can be done with an incoming list of contacts
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
'remove': _('remove')}
@@ -4580,7 +4539,7 @@ class ClientCertChooserDialog(FileChooserDialog):
FileChooserDialog.__init__(self,
title_text=_('Choose Client Cert #PCKS12'),
- transient_for=app.interface.instances['accounts'].window,
+ transient_for=app.interface.instances['accounts'],
action=Gtk.FileChooserAction.OPEN,
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OPEN, Gtk.ResponseType.OK),
diff --git a/gajim/gajim.py b/gajim/gajim.py
index 269c1c8af..a3dd77267 100644
--- a/gajim/gajim.py
+++ b/gajim/gajim.py
@@ -319,6 +319,7 @@ class GajimApplication(Gtk.Application):
def add_actions(self):
''' Build Application Actions '''
from gajim.app_actions import AppActions
+ from gajim.common import app
action = AppActions(self)
self.account_actions = [
@@ -339,12 +340,30 @@ class GajimApplication(Gtk.Application):
('-update-motd', action.on_update_motd, 'online', 's'),
('-delete-motd', action.on_delete_motd, 'online', 's'),
('-activate-bookmark',
- action.on_activate_bookmark, 'online', 'a{sv}')
+ action.on_activate_bookmark, 'online', 'a{sv}'),
+ ('-import-contacts', action.on_import_contacts, 'online', 's')
]
+ # General Stateful Actions
+
+ act = Gio.SimpleAction.new_stateful(
+ 'merge', None,
+ GLib.Variant.new_boolean(app.config.get('mergeaccounts')))
+ act.connect('change-state', action.on_merge_accounts)
+ self.add_action(act)
+
+ act = Gio.SimpleAction.new_stateful(
+ 'agent', None,
+ GLib.Variant.new_boolean(app.config.get('use_gpg_agent')))
+ self.add_action(act)
+
+ # General Actions
+
self.general_actions = [
('quit', action.on_quit),
('accounts', action.on_accounts),
+ ('add-account', action.on_add_account),
+ ('manage-proxies', action.on_manage_proxies),
('bookmarks', action.on_manage_bookmarks),
('history-manager', action.on_history_manager),
('preferences', action.on_preferences),
@@ -364,8 +383,7 @@ class GajimApplication(Gtk.Application):
act.connect("activate", func)
self.add_action(act)
- from gajim.common import app
- accounts_list = sorted(app.contacts.get_accounts())
+ accounts_list = sorted(app.config.get_per('accounts'))
if not accounts_list:
return
if len(accounts_list) > 1:
diff --git a/gajim/gtkgui_helpers.py b/gajim/gtkgui_helpers.py
index 599b6d92d..6bd46111e 100644
--- a/gajim/gtkgui_helpers.py
+++ b/gajim/gtkgui_helpers.py
@@ -107,15 +107,16 @@ def add_image_to_button(button, icon_name):
button.set_image(img)
def get_image_button(icon_name, tooltip, toggle=False):
- icon = get_icon_pixmap(icon_name)
- image = Gtk.Image()
- image.set_from_pixbuf(icon)
if toggle:
button = Gtk.ToggleButton()
+ icon = get_icon_pixmap(icon_name)
+ image = Gtk.Image()
+ image.set_from_pixbuf(icon)
+ button.set_image(image)
else:
- button = Gtk.Button()
+ button = Gtk.Button.new_from_icon_name(
+ icon_name, Gtk.IconSize.MENU)
button.set_tooltip_text(_(tooltip))
- button.set_image(image)
return button
GUI_DIR = os.path.join(app.DATA_DIR, 'gui')
diff --git a/gajim/options_dialog.py b/gajim/options_dialog.py
new file mode 100644
index 000000000..b1f74ab5e
--- /dev/null
+++ b/gajim/options_dialog.py
@@ -0,0 +1,585 @@
+from gi.repository import Gtk, GLib, Gdk, GObject
+from gajim.common import app
+from gajim.common import passwords
+from gajim import gtkgui_helpers
+from gajim.common.const import OptionKind, OptionType
+from gajim.common.exceptions import GajimGeneralException
+from gajim import dialogs
+
+
+class OptionsDialog(Gtk.ApplicationWindow):
+ def __init__(self, parent, title, flags, options, account):
+ Gtk.ApplicationWindow.__init__(self)
+ self.set_application(app.app)
+ self.set_show_menubar(False)
+ self.set_title(title)
+ self.set_transient_for(parent)
+ self.set_resizable(False)
+ self.set_default_size(250, -1)
+ self.set_type_hint(Gdk.WindowTypeHint.DIALOG)
+ self.account = account
+ if flags == Gtk.DialogFlags.MODAL:
+ self.set_modal(True)
+ elif flags == Gtk.DialogFlags.DESTROY_WITH_PARENT:
+ self.set_destroy_with_parent(True)
+
+ self.listbox = OptionsBox(account)
+ self.listbox.set_hexpand(True)
+ self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
+
+ for option in options:
+ self.listbox.add_option(option)
+ self.listbox.update_states()
+
+ self.add(self.listbox)
+
+ self.show_all()
+ self.listbox.connect('row-activated', self.on_row_activated)
+
+ @staticmethod
+ def on_row_activated(listbox, row):
+ row.get_child().on_row_activated()
+
+ def get_option(self, name):
+ return self.listbox.get_option(name)
+
+
+class OptionsBox(Gtk.ListBox):
+ def __init__(self, account):
+ Gtk.ListBox.__init__(self)
+ self.set_name('OptionsBox')
+ self.account = account
+ self.named_options = {}
+
+ self.map = {
+ OptionKind.SWITCH: SwitchOption,
+ OptionKind.SPIN: SpinOption,
+ OptionKind.DIALOG: DialogOption,
+ OptionKind.ENTRY: EntryOption,
+ OptionKind.ACTION: ActionOption,
+ OptionKind.LOGIN: LoginOption,
+ OptionKind.FILECHOOSER: FileChooserOption,
+ OptionKind.CALLBACK: CallbackOption,
+ OptionKind.PROXY: ProxyComboOption,
+ OptionKind.PRIORITY: PriorityOption,
+ OptionKind.HOSTNAME: CutstomHostnameOption,
+ OptionKind.CHANGEPASSWORD: ChangePasswordOption,
+ OptionKind.GPG: GPGOption,
+ }
+
+ def add_option(self, option):
+ if option.props is not None:
+ listitem = self.map[option.kind](
+ self.account, *option[1:-1], **option.props)
+ else:
+ listitem = self.map[option.kind](self.account, *option[1:-1])
+ listitem.connect('notify::option-value', self.on_option_changed)
+ if option.name is not None:
+ self.named_options[option.name] = listitem
+ self.add(listitem)
+
+ def get_option(self, name):
+ return self.named_options[name]
+
+ def update_states(self):
+ values = []
+ values.append((None, None))
+ for row in self.get_children():
+ name = row.get_child().name
+ if name is None:
+ continue
+ value = row.get_child().get_property('option-value')
+ values.append((name, value))
+
+ for name, value in values:
+ for row in self.get_children():
+ row.get_child().set_activatable(name, value)
+
+ def on_option_changed(self, widget, *args):
+ value = widget.get_property('option-value')
+ for row in self.get_children():
+ row.get_child().set_activatable(widget.name, value)
+
+
+class GenericOption(Gtk.Grid):
+ def __init__(self, account, label, type_, value,
+ name, callback, data, desc, enabledif):
+ Gtk.Grid.__init__(self)
+ self.set_column_spacing(12)
+ self.set_size_request(-1, 25)
+ self.callback = callback
+ self.type_ = type_
+ self.value = value
+ self.data = data
+ self.label = label
+ self.account = account
+ self.name = name
+ self.enabledif = enabledif
+ self.option_value = self.get_value()
+
+ description_box = Gtk.Box(
+ orientation=Gtk.Orientation.VERTICAL, spacing=0)
+ description_box.set_valign(Gtk.Align.CENTER)
+
+ optiontext = Gtk.Label(label=label)
+ optiontext.set_hexpand(True)
+ optiontext.set_halign(Gtk.Align.START)
+ optiontext.set_valign(Gtk.Align.CENTER)
+ optiontext.set_vexpand(True)
+ description_box.add(optiontext)
+
+ if desc is not None:
+ description = Gtk.Label(label=desc)
+ description.set_name('SubDescription')
+ description.set_hexpand(True)
+ description.set_halign(Gtk.Align.START)
+ description.set_valign(Gtk.Align.CENTER)
+ description_box.add(description)
+
+ self.add(description_box)
+
+ self.option_box = Gtk.Box(spacing=6)
+ self.option_box.set_size_request(200, -1)
+ self.option_box.set_valign(Gtk.Align.CENTER)
+ self.option_box.set_name('GenericOptionBox')
+ self.add(self.option_box)
+
+ def do_get_property(self, prop):
+ if prop.name == 'option-value':
+ return self.option_value
+ else:
+ raise AttributeError('unknown property %s' % prop.name)
+
+ def do_set_property(self, prop, value):
+ if prop.name == 'option-value':
+ self.option_value = value
+ else:
+ raise AttributeError('unknown property %s' % prop.name)
+
+ def get_value(self):
+ return self.__get_value(self.type_, self.value, self.account)
+
+ @staticmethod
+ def __get_value(type_, value, account):
+ if value is None:
+ return
+ if type_ == OptionType.BOOL:
+ return value
+ elif type_ == OptionType.CONFIG:
+ return app.config.get(value)
+ elif type_ == OptionType.ACCOUNT_CONFIG:
+ if value == 'password':
+ return passwords.get_password(account)
+ elif value == 'no_log_for':
+ no_log = app.config.get_per(
+ 'accounts', account, 'no_log_for').split()
+ return account not in no_log
+ else:
+ return app.config.get_per('accounts', account, value)
+ elif type_ == OptionType.ACTION:
+ if value.startswith('-'):
+ return account + value
+ return value
+ else:
+ raise ValueError('Wrong OptionType?')
+
+ def set_value(self, state):
+ if self.type_ == OptionType.CONFIG:
+ app.config.set(self.value, state)
+ if self.type_ == OptionType.ACCOUNT_CONFIG:
+ if self.value == 'password':
+ passwords.save_password(self.account, state)
+ if self.value == 'no_log_for':
+ self.set_no_log_for(self.account, state)
+ else:
+ app.config.set_per('accounts', self.account, self.value, state)
+
+ if self.callback is not None:
+ self.callback(state, self.data)
+
+ self.set_property('option-value', state)
+
+ @staticmethod
+ def set_no_log_for(account, jid):
+ no_log = app.config.get_per('accounts', account, 'no_log_for').split()
+ if jid and account in no_log:
+ no_log.remove(account)
+ elif not jid and account not in no_log:
+ no_log.append(account)
+ app.config.set_per('accounts', account, 'no_log_for', ' '.join(no_log))
+
+ def on_row_activated(self):
+ raise NotImplementedError
+
+ def set_activatable(self, name, value):
+ if self.enabledif is None or self.enabledif[0] != name:
+ return
+ activatable = (name, value) == self.enabledif
+ self.get_parent().set_activatable(activatable)
+ self.set_sensitive(activatable)
+
+
+class SwitchOption(GenericOption):
+
+ __gproperties__ = {
+ "option-value": (bool, 'Switch Value', '', False,
+ GObject.ParamFlags.READWRITE),}
+
+ def __init__(self, *args):
+ GenericOption.__init__(self, *args)
+
+ self.switch = Gtk.Switch()
+ self.switch.set_active(self.option_value)
+ self.switch.connect('notify::active', self.on_switch)
+ self.switch.set_hexpand(True)
+ self.switch.set_halign(Gtk.Align.END)
+ self.switch.set_valign(Gtk.Align.CENTER)
+
+ self.option_box.add(self.switch)
+
+ self.show_all()
+
+ def on_row_activated(self):
+ state = self.switch.get_active()
+ self.switch.set_active(not state)
+
+ def on_switch(self, switch, *args):
+ value = switch.get_active()
+ self.set_value(value)
+
+
+class EntryOption(GenericOption):
+
+ __gproperties__ = {
+ "option-value": (str, 'Entry Value', '', '',
+ GObject.ParamFlags.READWRITE),}
+
+ def __init__(self, *args):
+ GenericOption.__init__(self, *args)
+
+ self.entry = Gtk.Entry()
+ self.entry.set_text(str(self.option_value))
+ self.entry.connect('notify::text', self.on_text_change)
+ self.entry.set_valign(Gtk.Align.CENTER)
+
+ if self.value == 'password':
+ self.entry.set_invisible_char('*')
+ self.entry.set_visibility(False)
+
+ self.option_box.pack_end(self.entry, True, True, 0)
+
+ self.show_all()
+
+ def on_text_change(self, *args):
+ text = self.entry.get_text()
+ self.set_value(text)
+
+ def on_row_activated(self):
+ self.entry.grab_focus()
+
+
+class DialogOption(GenericOption):
+
+ __gproperties__ = {
+ "option-value": (str, 'Dummy', '', '',
+ GObject.ParamFlags.READWRITE),}
+
+ def __init__(self, *args, dialog):
+ GenericOption.__init__(self, *args)
+ self.dialog = dialog
+
+ self.option_value = Gtk.Label()
+ self.option_value.set_text(self.get_option_value())
+ self.option_value.set_halign(Gtk.Align.END)
+ self.option_box.pack_start(self.option_value, True, True, 0)
+
+ self.show_all()
+
+ def show_dialog(self, parent):
+ if self.dialog:
+ dialog = self.dialog(self.account, parent)
+ dialog.connect('destroy', self.on_destroy)
+
+ def on_destroy(self, *args):
+ self.option_value.set_text(self.get_option_value())
+
+ def get_option_value(self):
+ self.option_value.hide()
+ return ''
+
+ def on_row_activated(self):
+ self.show_dialog(self.get_toplevel())
+
+
+class SpinOption(GenericOption):
+
+ __gproperties__ = {
+ "option-value": (int, 'Priority', '', -128, 127, 0,
+ GObject.ParamFlags.READWRITE),}
+
+ def __init__(self, *args, range_):
+ GenericOption.__init__(self, *args)
+
+ lower, upper = range_
+ adjustment = Gtk.Adjustment(0, lower, upper, 1, 10, 0)
+
+ self.spin = Gtk.SpinButton()
+ self.spin.set_adjustment(adjustment)
+ self.spin.set_numeric(True)
+ self.spin.set_update_policy(Gtk.SpinButtonUpdatePolicy.IF_VALID)
+ self.spin.set_value(self.option_value)
+ self.spin.set_halign(Gtk.Align.END)
+ self.spin.set_valign(Gtk.Align.CENTER)
+ self.spin.connect('notify::value', self.on_value_change)
+
+ self.option_box.pack_start(self.spin, True, True, 0)
+
+ self.show_all()
+
+ def on_row_activated(self):
+ self.spin.grab_focus()
+
+ def on_value_change(self, spin, *args):
+ value = spin.get_value_as_int()
+ self.set_value(value)
+
+
+class FileChooserOption(GenericOption):
+
+ __gproperties__ = {
+ "option-value": (str, 'Certificate Path', '', '',
+ GObject.ParamFlags.READWRITE),}
+
+ def __init__(self, *args, filefilter):
+ GenericOption.__init__(self, *args)
+
+ button = Gtk.FileChooserButton(self.label, Gtk.FileChooserAction.OPEN)
+ button.set_halign(Gtk.Align.END)
+
+ # GTK Bug: The FileChooserButton expands without limit
+ # get the label and use set_max_wide_chars()
+ label = button.get_children()[0].get_children()[0].get_children()[1]
+ label.set_max_width_chars(20)
+
+ if filefilter:
+ name, pattern = filefilter
+ filter_ = Gtk.FileFilter()
+ filter_.set_name(name)
+ filter_.add_pattern(pattern)
+ button.add_filter(filter_)
+ button.set_filter(filter_)
+
+ filter_ = Gtk.FileFilter()
+ filter_.set_name(_('All files'))
+ filter_.add_pattern('*')
+ button.add_filter(filter_)
+
+ if self.option_value:
+ button.set_filename(self.option_value)
+ button.connect('selection-changed', self.on_select)
+
+ clear_button = gtkgui_helpers.get_image_button(
+ 'edit-clear-all-symbolic', 'Clear File')
+ clear_button.connect('clicked', lambda *args: button.unselect_all())
+ self.option_box.pack_start(button, True, True, 0)
+ self.option_box.pack_start(clear_button, False, False, 0)
+
+ self.show_all()
+
+ def on_select(self, filechooser):
+ self.set_value(filechooser.get_filename() or '')
+
+ def on_row_activated(self):
+ pass
+
+
+class CallbackOption(GenericOption):
+
+ __gproperties__ = {
+ "option-value": (str, 'Dummy', '', '',
+ GObject.ParamFlags.READWRITE),}
+
+ def __init__(self, *args, callback):
+ GenericOption.__init__(self, *args)
+ self.callback = callback
+ self.show_all()
+
+ def on_row_activated(self):
+ self.callback()
+
+
+class ActionOption(GenericOption):
+
+ __gproperties__ = {
+ "option-value": (str, 'Dummy', '', '',
+ GObject.ParamFlags.READWRITE),}
+
+ def __init__(self, *args, action_args):
+ GenericOption.__init__(self, *args)
+ self.action = gtkgui_helpers.get_action(self.option_value)
+ self.variant = GLib.Variant.new_string(action_args)
+ self.on_enable()
+
+ self.show_all()
+ self.action.connect('notify::enabled', self.on_enable)
+
+ def on_enable(self, *args):
+ self.set_sensitive(self.action.get_enabled())
+
+ def on_row_activated(self):
+ self.action.activate(self.variant)
+
+
+class LoginOption(DialogOption):
+ def __init__(self, *args, **kwargs):
+ DialogOption.__init__(self, *args, **kwargs)
+ self.option_value.set_selectable(True)
+
+ def get_option_value(self):
+ jid = app.get_jid_from_account(self.account)
+ return jid
+
+ def set_activatable(self, name, value):
+ DialogOption.set_activatable(self, name, value)
+ anonym = app.config.get_per('accounts', self.account, 'anonymous_auth')
+ self.get_parent().set_activatable(not anonym)
+
+
+class ProxyComboOption(GenericOption):
+
+ __gproperties__ = {
+ "option-value": (str, 'Proxy', '', '',
+ GObject.ParamFlags.READWRITE),}
+
+ def __init__(self, *args):
+ GenericOption.__init__(self, *args)
+
+ self.combo = Gtk.ComboBoxText()
+ self.update_values()
+
+ self.combo.connect('changed', self.on_value_change)
+ self.combo.set_valign(Gtk.Align.CENTER)
+
+ button = gtkgui_helpers.get_image_button(
+ 'preferences-system-symbolic', 'Manage Proxies')
+ button.set_action_name('app.manage-proxies')
+ button.set_valign(Gtk.Align.CENTER)
+
+ self.option_box.pack_start(self.combo, True, True, 0)
+ self.option_box.pack_start(button, False, True, 0)
+ self.show_all()
+
+ def update_values(self):
+ proxies = app.config.get_per('proxies')
+ proxies.insert(0, _('None'))
+ self.combo.remove_all()
+ for index, value in enumerate(proxies):
+ self.combo.insert_text(-1, value)
+ if value == self.option_value or index == 0:
+ self.combo.set_active(index)
+
+ def on_value_change(self, combo):
+ self.set_value(combo.get_active_text())
+
+ def on_row_activated(self):
+ pass
+
+
+class PriorityOption(DialogOption):
+ def __init__(self, *args, **kwargs):
+ DialogOption.__init__(self, *args, **kwargs)
+
+ def get_option_value(self):
+ adjust = app.config.get_per(
+ 'accounts', self.account, 'adjust_priority_with_status')
+ if adjust:
+ return _('Adjust to Status')
+
+ priority = app.config.get_per('accounts', self.account, 'priority')
+ return str(priority)
+
+
+class CutstomHostnameOption(DialogOption):
+ def __init__(self, *args, **kwargs):
+ DialogOption.__init__(self, *args, **kwargs)
+
+ def get_option_value(self):
+ custom = app.config.get_per('accounts', self.account, 'use_custom_host')
+ return _('On') if custom else _('Off')
+
+
+class ChangePasswordOption(DialogOption):
+ def __init__(self, *args, **kwargs):
+ DialogOption.__init__(self, *args, **kwargs)
+
+ def show_dialog(self, parent):
+ try:
+ self.change_dialog = dialogs.ChangePasswordDialog(
+ self.account, self.on_changed, parent)
+ except GajimGeneralException:
+ return
+ self.change_dialog.dialog.set_modal(True)
+
+ def on_changed(self, new_password):
+ if new_password is not None:
+ app.connections[self.account].change_password(new_password)
+ self.set_value(new_password)
+
+ def set_activatable(self, name, value):
+ activatable = False
+ if self.account in app.connections:
+ con = app.connections[self.account]
+ activatable = con.connected >= 2 and con.register_supported
+ self.get_parent().set_activatable(activatable)
+
+
+class GPGOption(DialogOption):
+ def __init__(self, *args, **kwargs):
+ DialogOption.__init__(self, *args, **kwargs)
+
+ def show_dialog(self, parent):
+ secret_keys = app.connections[self.account].ask_gpg_secrete_keys()
+ secret_keys[_('None')] = _('None')
+
+ if not secret_keys:
+ dialogs.ErrorDialog(
+ _('Failed to get secret keys'),
+ _('There is no OpenPGP secret key available.'),
+ transient_for=parent)
+ return
+
+ dialog = dialogs.ChooseGPGKeyDialog(
+ _('OpenPGP Key Selection'), _('Choose your OpenPGP key'),
+ secret_keys, self.on_key_selected, transient_for=parent)
+ dialog.window.connect('destroy', self.on_destroy)
+
+ def on_key_selected(self, keyID):
+ if keyID is None:
+ return
+ keyid_new, keyname_new = keyID
+
+ keyid = app.config.get_per('accounts', self.account, 'keyid')
+
+ if keyid_new == _('None'):
+ if keyid == '':
+ return
+ app.config.set_per('accounts', self.account, 'keyname', '')
+ app.config.set_per('accounts', self.account, 'keyid', '')
+ else:
+ if keyid == keyid_new:
+ return
+ app.config.set_per(
+ 'accounts', self.account, 'keyname', keyname_new)
+ app.config.set_per(
+ 'accounts', self.account, 'keyid', keyid_new)
+
+ def get_option_value(self):
+ keyid = app.config.get_per('accounts', self.account, 'keyid')
+ keyname = app.config.get_per('accounts', self.account, 'keyname')
+ if keyid is not None:
+ return '\n'.join((keyid, keyname))
+ return ''
+
+ def set_activatable(self, name, value):
+ active = self.account in app.connections
+ self.get_parent().set_activatable(app.HAVE_GPG and active)
diff --git a/gajim/roster_window.py b/gajim/roster_window.py
index e07a7f354..b7f70cf5e 100644
--- a/gajim/roster_window.py
+++ b/gajim/roster_window.py
@@ -1052,7 +1052,8 @@ class RosterWindow:
account_name = _('Merged accounts')
accounts = []
else:
- account_name = account
+ acclabel = app.config.get_per('accounts', account, 'account_label')
+ account_name = acclabel or account
accounts = [account]
if account in self.collapsed_rows and \
@@ -3229,7 +3230,7 @@ class RosterWindow:
def on_edit_account(self, widget, account):
if 'accounts' in app.interface.instances:
- app.interface.instances['accounts'].window.present()
+ app.interface.instances['accounts'].present()
else:
app.interface.instances['accounts'] = config.AccountsWindow()
app.interface.instances['accounts'].select_account(account)