Merge branch 'newaccount' into 'master'
New Account Window See merge request !129
This commit is contained in:
commit
2d8def8c1b
File diff suppressed because it is too large
Load Diff
|
@ -9,6 +9,7 @@
|
||||||
<property name="default_width">350</property>
|
<property name="default_width">350</property>
|
||||||
<property name="default_height">300</property>
|
<property name="default_height">300</property>
|
||||||
<property name="type_hint">dialog</property>
|
<property name="type_hint">dialog</property>
|
||||||
|
<signal name="destroy" handler="on_destroy" swapped="no"/>
|
||||||
<child internal-child="vbox">
|
<child internal-child="vbox">
|
||||||
<object class="GtkBox" id="dialog-vbox7">
|
<object class="GtkBox" id="dialog-vbox7">
|
||||||
<property name="visible">True</property>
|
<property name="visible">True</property>
|
||||||
|
|
|
@ -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 > label { padding:10px; color: @insensitive_fg_color; font-weight: bold; }
|
||||||
#ServerInfoGrid > list > row.activatable:active { box-shadow: none; }
|
#ServerInfoGrid > list > row.activatable:active { box-shadow: none; }
|
||||||
|
|
||||||
/* Generic Options Dialog */
|
/* OptionsBox */
|
||||||
#OptionsDialog list > row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; }
|
#OptionsBox > row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; }
|
||||||
#OptionsDialog list > row:last-child { border-bottom: 0px}
|
#OptionsBox > row:last-child { border-bottom: 0px}
|
||||||
#OptionsDialog list > row { padding: 10px; }
|
#OptionsBox > row.activatable:active { box-shadow: none; }
|
||||||
#OptionsDialog list > 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 */
|
/* Generic Popover Menu with Buttons */
|
||||||
.PopoverButtonListbox { padding-left: 0px; padding-right: 0px; }
|
.PopoverButtonListbox { padding-left: 0px; padding-right: 0px; }
|
||||||
.PopoverButtonListbox > list { margin-top: 10px; margin-bottom: 10px; }
|
.PopoverButtonListbox > list { margin-top: 10px; margin-bottom: 10px; }
|
||||||
.PopoverButtonListbox > list > row { padding: 10px 20px 10px 20px; }
|
.PopoverButtonListbox > list > row { padding: 10px 20px 10px 20px; }
|
||||||
.PopoverButtonListbox > list > row.activatable:active { box-shadow: none; background-color: @theme_selected_bg_color }
|
.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; }
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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, '')
|
|
@ -25,10 +25,12 @@ from gajim.common.exceptions import GajimGeneralException
|
||||||
from gi.repository import Gtk
|
from gi.repository import Gtk
|
||||||
import sys
|
import sys
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from gajim import config
|
from gajim import config
|
||||||
from gajim import dialogs
|
from gajim import dialogs
|
||||||
from gajim import features_window
|
from gajim import features_window
|
||||||
from gajim import shortcuts_window
|
from gajim import shortcuts_window
|
||||||
|
from gajim import accounts_window
|
||||||
import gajim.plugins.gui
|
import gajim.plugins.gui
|
||||||
from gajim import history_window
|
from gajim import history_window
|
||||||
from gajim import disco
|
from gajim import disco
|
||||||
|
@ -57,10 +59,10 @@ class AppActions():
|
||||||
interface.instances['plugins'] = gajim.plugins.gui.PluginsWindow()
|
interface.instances['plugins'] = gajim.plugins.gui.PluginsWindow()
|
||||||
|
|
||||||
def on_accounts(self, action, param):
|
def on_accounts(self, action, param):
|
||||||
if 'accounts' in interface.instances:
|
if 'accounts' in app.interface.instances:
|
||||||
interface.instances['accounts'].window.present()
|
app.interface.instances['accounts'].present()
|
||||||
else:
|
else:
|
||||||
interface.instances['accounts'] = config.AccountsWindow()
|
app.interface.instances['accounts'] = accounts_window.AccountsWindow()
|
||||||
|
|
||||||
def on_history_manager(self, action, param):
|
def on_history_manager(self, action, param):
|
||||||
from gajim.history_manager import HistoryManager
|
from gajim.history_manager import HistoryManager
|
||||||
|
@ -131,6 +133,35 @@ class AppActions():
|
||||||
def on_single_message(self, action, param):
|
def on_single_message(self, action, param):
|
||||||
dialogs.SingleMessageWindow(param.get_string(), action='send')
|
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
|
# Advanced Actions
|
||||||
|
|
||||||
def on_archiving_preferences(self, action, param):
|
def on_archiving_preferences(self, action, param):
|
||||||
|
@ -174,6 +205,13 @@ class AppActions():
|
||||||
interface.instances[account]['xml_console'] = \
|
interface.instances[account]['xml_console'] = \
|
||||||
dialogs.XMLConsoleWindow(account)
|
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
|
# Admin Actions
|
||||||
|
|
||||||
def on_set_motd(self, action, param):
|
def on_set_motd(self, action, param):
|
||||||
|
|
|
@ -325,6 +325,7 @@ class Config:
|
||||||
__options_per_key = {
|
__options_per_key = {
|
||||||
'accounts': ({
|
'accounts': ({
|
||||||
'name': [ opt_str, '', '', True ],
|
'name': [ opt_str, '', '', True ],
|
||||||
|
'account_label': [ opt_str, '', '', False ],
|
||||||
'hostname': [ opt_str, '', '', True ],
|
'hostname': [ opt_str, '', '', True ],
|
||||||
'anonymous_auth': [ opt_bool, False ],
|
'anonymous_auth': [ opt_bool, False ],
|
||||||
'client_cert': [ opt_str, '', '', True ],
|
'client_cert': [ opt_str, '', '', True ],
|
||||||
|
|
|
@ -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"""\
|
THANKS = u"""\
|
||||||
Alexander Futász
|
Alexander Futász
|
||||||
Alexander V. Butenko
|
Alexander V. Butenko
|
||||||
|
|
1237
gajim/config.py
1237
gajim/config.py
File diff suppressed because it is too large
Load Diff
117
gajim/dialogs.py
117
gajim/dialogs.py
|
@ -48,6 +48,8 @@ from random import randrange
|
||||||
from gajim.common import pep
|
from gajim.common import pep
|
||||||
from gajim.common import ged
|
from gajim.common import ged
|
||||||
from gajim.common import const
|
from gajim.common import const
|
||||||
|
from gajim.options_dialog import OptionsDialog
|
||||||
|
from gajim.common.const import Option, OptionKind, OptionType
|
||||||
|
|
||||||
try:
|
try:
|
||||||
from gajim import gtkspell
|
from gajim import gtkspell
|
||||||
|
@ -2675,7 +2677,7 @@ class SynchroniseSelectAccountDialog:
|
||||||
self.account = account
|
self.account = account
|
||||||
self.xml = gtkgui_helpers.get_gtk_builder('synchronise_select_account_dialog.ui')
|
self.xml = gtkgui_helpers.get_gtk_builder('synchronise_select_account_dialog.ui')
|
||||||
self.dialog = self.xml.get_object('synchronise_select_account_dialog')
|
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')
|
self.accounts_treeview = self.xml.get_object('accounts_treeview')
|
||||||
model = Gtk.ListStore(str, str, bool)
|
model = Gtk.ListStore(str, str, bool)
|
||||||
self.accounts_treeview.set_model(model)
|
self.accounts_treeview.set_model(model)
|
||||||
|
@ -2731,6 +2733,10 @@ class SynchroniseSelectAccountDialog:
|
||||||
return
|
return
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def on_destroy(widget):
|
||||||
|
del app.interface.instances['import_contacts']
|
||||||
|
|
||||||
class SynchroniseSelectContactsDialog:
|
class SynchroniseSelectContactsDialog:
|
||||||
def __init__(self, account, remote_account):
|
def __init__(self, account, remote_account):
|
||||||
self.local_account = account
|
self.local_account = account
|
||||||
|
@ -3323,6 +3329,8 @@ class XMLConsoleWindow(Gtk.Window):
|
||||||
setattr(self, obj, self.builder.get_object(obj))
|
setattr(self, obj, self.builder.get_object(obj))
|
||||||
|
|
||||||
self.set_titlebar(self.headerbar)
|
self.set_titlebar(self.headerbar)
|
||||||
|
jid = app.get_jid_from_account(account)
|
||||||
|
self.headerbar.set_subtitle(jid)
|
||||||
self.set_default_size(600, 600)
|
self.set_default_size(600, 600)
|
||||||
self.add(self.box)
|
self.add(self.box)
|
||||||
|
|
||||||
|
@ -3434,26 +3442,30 @@ class XMLConsoleWindow(Gtk.Window):
|
||||||
|
|
||||||
def on_filter_options(self, *args):
|
def on_filter_options(self, *args):
|
||||||
options = [
|
options = [
|
||||||
SwitchOption('Presence', self.presence,
|
Option(OptionKind.SWITCH, 'Presence',
|
||||||
self.on_option,
|
OptionType.BOOL, self.presence,
|
||||||
'presence'),
|
callback=self.on_option, data='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')]
|
|
||||||
|
|
||||||
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):
|
def on_clear(self, *args):
|
||||||
buffer_ = self.textview.get_buffer().set_text('')
|
buffer_ = self.textview.get_buffer().set_text('')
|
||||||
|
@ -3468,13 +3480,12 @@ class XMLConsoleWindow(Gtk.Window):
|
||||||
def on_enable(self, switch, param):
|
def on_enable(self, switch, param):
|
||||||
self.enabled = switch.get_active()
|
self.enabled = switch.get_active()
|
||||||
|
|
||||||
def on_option(self, switch, param, *user_data):
|
def on_option(self, value, data):
|
||||||
kind = user_data[0]
|
setattr(self, data, value)
|
||||||
setattr(self, kind, switch.get_active())
|
value = not value
|
||||||
value = not switch.get_active()
|
|
||||||
table = self.textview.get_buffer().get_tag_table()
|
table = self.textview.get_buffer().get_tag_table()
|
||||||
tag = table.lookup(kind)
|
tag = table.lookup(data)
|
||||||
if kind in ('incoming', 'outgoing'):
|
if data in ('incoming', 'outgoing'):
|
||||||
if value:
|
if value:
|
||||||
tag.set_priority(table.get_size() - 1)
|
tag.set_priority(table.get_size() - 1)
|
||||||
else:
|
else:
|
||||||
|
@ -3522,58 +3533,6 @@ class XMLConsoleWindow(Gtk.Window):
|
||||||
if at_the_end:
|
if at_the_end:
|
||||||
GLib.idle_add(gtkgui_helpers.scroll_to_end, self.scrolled)
|
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
|
#Action that can be done with an incoming list of contacts
|
||||||
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
|
TRANSLATED_ACTION = {'add': _('add'), 'modify': _('modify'),
|
||||||
'remove': _('remove')}
|
'remove': _('remove')}
|
||||||
|
@ -4580,7 +4539,7 @@ class ClientCertChooserDialog(FileChooserDialog):
|
||||||
|
|
||||||
FileChooserDialog.__init__(self,
|
FileChooserDialog.__init__(self,
|
||||||
title_text=_('Choose Client Cert #PCKS12'),
|
title_text=_('Choose Client Cert #PCKS12'),
|
||||||
transient_for=app.interface.instances['accounts'].window,
|
transient_for=app.interface.instances['accounts'],
|
||||||
action=Gtk.FileChooserAction.OPEN,
|
action=Gtk.FileChooserAction.OPEN,
|
||||||
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||||
Gtk.STOCK_OPEN, Gtk.ResponseType.OK),
|
Gtk.STOCK_OPEN, Gtk.ResponseType.OK),
|
||||||
|
|
|
@ -319,6 +319,7 @@ class GajimApplication(Gtk.Application):
|
||||||
def add_actions(self):
|
def add_actions(self):
|
||||||
''' Build Application Actions '''
|
''' Build Application Actions '''
|
||||||
from gajim.app_actions import AppActions
|
from gajim.app_actions import AppActions
|
||||||
|
from gajim.common import app
|
||||||
action = AppActions(self)
|
action = AppActions(self)
|
||||||
|
|
||||||
self.account_actions = [
|
self.account_actions = [
|
||||||
|
@ -339,12 +340,30 @@ class GajimApplication(Gtk.Application):
|
||||||
('-update-motd', action.on_update_motd, 'online', 's'),
|
('-update-motd', action.on_update_motd, 'online', 's'),
|
||||||
('-delete-motd', action.on_delete_motd, 'online', 's'),
|
('-delete-motd', action.on_delete_motd, 'online', 's'),
|
||||||
('-activate-bookmark',
|
('-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 = [
|
self.general_actions = [
|
||||||
('quit', action.on_quit),
|
('quit', action.on_quit),
|
||||||
('accounts', action.on_accounts),
|
('accounts', action.on_accounts),
|
||||||
|
('add-account', action.on_add_account),
|
||||||
|
('manage-proxies', action.on_manage_proxies),
|
||||||
('bookmarks', action.on_manage_bookmarks),
|
('bookmarks', action.on_manage_bookmarks),
|
||||||
('history-manager', action.on_history_manager),
|
('history-manager', action.on_history_manager),
|
||||||
('preferences', action.on_preferences),
|
('preferences', action.on_preferences),
|
||||||
|
@ -364,8 +383,7 @@ class GajimApplication(Gtk.Application):
|
||||||
act.connect("activate", func)
|
act.connect("activate", func)
|
||||||
self.add_action(act)
|
self.add_action(act)
|
||||||
|
|
||||||
from gajim.common import app
|
accounts_list = sorted(app.config.get_per('accounts'))
|
||||||
accounts_list = sorted(app.contacts.get_accounts())
|
|
||||||
if not accounts_list:
|
if not accounts_list:
|
||||||
return
|
return
|
||||||
if len(accounts_list) > 1:
|
if len(accounts_list) > 1:
|
||||||
|
|
|
@ -107,15 +107,16 @@ def add_image_to_button(button, icon_name):
|
||||||
button.set_image(img)
|
button.set_image(img)
|
||||||
|
|
||||||
def get_image_button(icon_name, tooltip, toggle=False):
|
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:
|
if toggle:
|
||||||
button = Gtk.ToggleButton()
|
button = Gtk.ToggleButton()
|
||||||
|
icon = get_icon_pixmap(icon_name)
|
||||||
|
image = Gtk.Image()
|
||||||
|
image.set_from_pixbuf(icon)
|
||||||
|
button.set_image(image)
|
||||||
else:
|
else:
|
||||||
button = Gtk.Button()
|
button = Gtk.Button.new_from_icon_name(
|
||||||
|
icon_name, Gtk.IconSize.MENU)
|
||||||
button.set_tooltip_text(_(tooltip))
|
button.set_tooltip_text(_(tooltip))
|
||||||
button.set_image(image)
|
|
||||||
return button
|
return button
|
||||||
|
|
||||||
GUI_DIR = os.path.join(app.DATA_DIR, 'gui')
|
GUI_DIR = os.path.join(app.DATA_DIR, 'gui')
|
||||||
|
|
|
@ -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)
|
|
@ -1052,7 +1052,8 @@ class RosterWindow:
|
||||||
account_name = _('Merged accounts')
|
account_name = _('Merged accounts')
|
||||||
accounts = []
|
accounts = []
|
||||||
else:
|
else:
|
||||||
account_name = account
|
acclabel = app.config.get_per('accounts', account, 'account_label')
|
||||||
|
account_name = acclabel or account
|
||||||
accounts = [account]
|
accounts = [account]
|
||||||
|
|
||||||
if account in self.collapsed_rows and \
|
if account in self.collapsed_rows and \
|
||||||
|
@ -3229,7 +3230,7 @@ class RosterWindow:
|
||||||
|
|
||||||
def on_edit_account(self, widget, account):
|
def on_edit_account(self, widget, account):
|
||||||
if 'accounts' in app.interface.instances:
|
if 'accounts' in app.interface.instances:
|
||||||
app.interface.instances['accounts'].window.present()
|
app.interface.instances['accounts'].present()
|
||||||
else:
|
else:
|
||||||
app.interface.instances['accounts'] = config.AccountsWindow()
|
app.interface.instances['accounts'] = config.AccountsWindow()
|
||||||
app.interface.instances['accounts'].select_account(account)
|
app.interface.instances['accounts'].select_account(account)
|
||||||
|
|
Loading…
Reference in New Issue