Philipp Hörist pushed to branch master at gajim / gajim

Commits:

5 changed files:

Changes:

  • gajim/common/app.py
    ... ... @@ -409,6 +409,16 @@ def get_connected_accounts():
    409 409
                 account_list.append(account)
    
    410 410
         return account_list
    
    411 411
     
    
    412
    +def get_accounts_sorted():
    
    413
    +    '''
    
    414
    +    Get all accounts alphabetically sorted with Local first
    
    415
    +    '''
    
    416
    +    account_list = config.get_per('accounts')
    
    417
    +    account_list.sort(key=str.lower)
    
    418
    +    account_list.remove('Local')
    
    419
    +    account_list.insert(0, 'Local')
    
    420
    +    return account_list
    
    421
    +
    
    412 422
     def get_enabled_accounts_with_labels(exclude_local=True, connected_only=False,
    
    413 423
                                          private_storage_only=False):
    
    414 424
         """
    

  • gajim/data/gui/accounts_window.ui deleted
    1
    -<?xml version="1.0" encoding="UTF-8"?>
    
    2
    -<!-- Generated with glade 3.21.0 -->
    
    3
    -<interface>
    
    4
    -  <requires lib="gtk+" version="3.18"/>
    
    5
    -  <object class="GtkBox" id="box">
    
    6
    -    <property name="visible">True</property>
    
    7
    -    <property name="can_focus">False</property>
    
    8
    -    <child>
    
    9
    -      <object class="GtkBox">
    
    10
    -        <property name="visible">True</property>
    
    11
    -        <property name="can_focus">False</property>
    
    12
    -        <property name="halign">start</property>
    
    13
    -        <property name="orientation">vertical</property>
    
    14
    -        <child>
    
    15
    -          <placeholder/>
    
    16
    -        </child>
    
    17
    -        <child>
    
    18
    -          <object class="GtkScrolledWindow">
    
    19
    -            <property name="visible">True</property>
    
    20
    -            <property name="can_focus">True</property>
    
    21
    -            <property name="halign">start</property>
    
    22
    -            <property name="hscrollbar_policy">never</property>
    
    23
    -            <property name="shadow_type">in</property>
    
    24
    -            <property name="max_content_height">400</property>
    
    25
    -            <property name="propagate_natural_height">True</property>
    
    26
    -            <child>
    
    27
    -              <object class="GtkViewport">
    
    28
    -                <property name="visible">True</property>
    
    29
    -                <property name="can_focus">False</property>
    
    30
    -                <child>
    
    31
    -                  <object class="GtkListBox" id="account_list">
    
    32
    -                    <property name="name">OptionsBox1</property>
    
    33
    -                    <property name="width_request">170</property>
    
    34
    -                    <property name="visible">True</property>
    
    35
    -                    <property name="can_focus">False</property>
    
    36
    -                    <property name="halign">start</property>
    
    37
    -                    <property name="hexpand">False</property>
    
    38
    -                    <property name="vexpand">True</property>
    
    39
    -                    <signal name="row-activated" handler="on_row_activated" swapped="no"/>
    
    40
    -                    <style>
    
    41
    -                      <class name="account-list"/>
    
    42
    -                    </style>
    
    43
    -                  </object>
    
    44
    -                </child>
    
    45
    -              </object>
    
    46
    -            </child>
    
    47
    -          </object>
    
    48
    -          <packing>
    
    49
    -            <property name="expand">False</property>
    
    50
    -            <property name="fill">True</property>
    
    51
    -            <property name="position">1</property>
    
    52
    -          </packing>
    
    53
    -        </child>
    
    54
    -      </object>
    
    55
    -      <packing>
    
    56
    -        <property name="expand">False</property>
    
    57
    -        <property name="fill">True</property>
    
    58
    -        <property name="position">0</property>
    
    59
    -      </packing>
    
    60
    -    </child>
    
    61
    -    <child>
    
    62
    -      <object class="GtkStack" id="stack">
    
    63
    -        <property name="visible">True</property>
    
    64
    -        <property name="can_focus">False</property>
    
    65
    -        <property name="valign">start</property>
    
    66
    -        <property name="hhomogeneous">False</property>
    
    67
    -        <property name="vhomogeneous">False</property>
    
    68
    -        <signal name="notify::visible-child-name" handler="on_child_visible" swapped="no"/>
    
    69
    -        <child>
    
    70
    -          <placeholder/>
    
    71
    -        </child>
    
    72
    -      </object>
    
    73
    -      <packing>
    
    74
    -        <property name="expand">True</property>
    
    75
    -        <property name="fill">True</property>
    
    76
    -        <property name="position">1</property>
    
    77
    -      </packing>
    
    78
    -    </child>
    
    79
    -  </object>
    
    80
    -</interface>

  • gajim/data/style/gajim.css
    ... ... @@ -84,11 +84,11 @@ popover#EmoticonPopover { padding: 5px; background-color: @theme_unfocused_base_
    84 84
     #FeaturesInfoGrid > list > row.activatable:active { box-shadow: none; }
    
    85 85
     
    
    86 86
     /* SettingsBox */
    
    87
    -#SettingsBox > row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; }
    
    88
    -#SettingsBox > row:last-child { border-bottom: 0px}
    
    89
    -#SettingsBox > row.activatable:active { box-shadow: none; }
    
    90
    -#SettingsBox > row { padding: 10px 20px 10px 10px; }
    
    91
    -#SettingsBox > row:not(.activatable) label { color: @insensitive_fg_color }
    
    87
    +.settings-box > row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; }
    
    88
    +.settings-box > row:last-child { border-bottom: 0px}
    
    89
    +.settings-box > row.activatable:active { box-shadow: none; }
    
    90
    +.settings-box > row { padding: 10px 20px 10px 10px; }
    
    91
    +.settings-box > row:not(.activatable) label { color: @insensitive_fg_color }
    
    92 92
     
    
    93 93
     /* GenericSetting */
    
    94 94
     #SubDescription { color: @insensitive_fg_color; font-size: small;}
    
    ... ... @@ -119,13 +119,15 @@ list.settings > row > box {
    119 119
     .PreferencesStack { background-color: @theme_bg_color; }
    
    120 120
     
    
    121 121
     /* Accounts Window */
    
    122
    -#AccountsWindow > box > stack { padding:30px 30px 30px 30px;}
    
    123
    -#AccountsWindow scrolledwindow {border: none;}
    
    124
    -#AccountsWindow stack list {border: 1px solid; border-color: @borders;}
    
    125
    -
    
    126
    -#AccountsWindow > box > box { border-right: 1px solid; border-color: @borders; }
    
    127
    -
    
    128
    -.account-list > row { padding: 20px 20px 20px 10px; }
    
    122
    +.accounts-menu { border-right: 1px solid; border-color: @borders;}
    
    123
    +.accounts-settings-border {border: 1px solid; border-color: @borders;}
    
    124
    +.accounts-settings stack > box { margin:30px; }
    
    125
    +.accounts-label-row {
    
    126
    +    padding-top: 10px;
    
    127
    +    padding-bottom: 10px;
    
    128
    +    font-size: 18px;
    
    129
    +    font-weight: bold;
    
    130
    +    color: @insensitive_fg_color;}
    
    129 131
     
    
    130 132
     /* StartChatListBox */
    
    131 133
     #StartChatListBox > row { border-bottom: 1px solid; border-color: @theme_unfocused_bg_color; }
    
    ... ... @@ -214,6 +216,7 @@ list.settings > row > box {
    214 216
     .status-away { color: #ff8533;}
    
    215 217
     .status-dnd { color: #e62e00;}
    
    216 218
     .status-online { color: #66bf10;}
    
    219
    +.insensitive-fg-color {color: @insensitive_fg_color;}
    
    217 220
     
    
    218 221
     /* Padding/Margins */
    
    219 222
     .margin-top6 { margin-top: 6px; }
    

  • gajim/gtk/accounts.py
    ... ... @@ -12,16 +12,20 @@
    12 12
     # You should have received a copy of the GNU General Public License
    
    13 13
     # along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14 14
     
    
    15
    -from functools import partial
    
    15
    +import sys
    
    16
    +import locale
    
    17
    +import logging
    
    18
    +from collections import defaultdict
    
    16 19
     
    
    17 20
     from gi.repository import Gtk
    
    18 21
     from gi.repository import Gdk
    
    19 22
     from gi.repository import GLib
    
    23
    +from gi.repository import Pango
    
    24
    +from gi.repository import GObject
    
    20 25
     
    
    21 26
     from gajim.common import app
    
    22 27
     from gajim.common import passwords
    
    23 28
     from gajim.common import helpers
    
    24
    -from gajim.common import ged
    
    25 29
     from gajim.common.i18n import _
    
    26 30
     from gajim.common.connection import Connection
    
    27 31
     from gajim.common.zeroconf.connection_zeroconf import ConnectionZeroconf
    
    ... ... @@ -37,13 +41,15 @@ from gajim.gtk.dialogs import ErrorDialog
    37 41
     from gajim.gtk.dialogs import YesNoDialog
    
    38 42
     from gajim.gtk.dialogs import DialogButton
    
    39 43
     from gajim.gtk.dialogs import NewConfirmationDialog
    
    40
    -from gajim.gtk.util import get_icon_name
    
    41 44
     from gajim.gtk.util import get_builder
    
    42 45
     from gajim.gtk.const import Setting
    
    43 46
     from gajim.gtk.const import SettingKind
    
    44 47
     from gajim.gtk.const import SettingType
    
    45 48
     
    
    46 49
     
    
    50
    +log = logging.getLogger('gajim.gtk.accounts')
    
    51
    +
    
    52
    +
    
    47 53
     class AccountsWindow(Gtk.ApplicationWindow):
    
    48 54
         def __init__(self):
    
    49 55
             Gtk.ApplicationWindow.__init__(self)
    
    ... ... @@ -52,101 +58,62 @@ class AccountsWindow(Gtk.ApplicationWindow):
    52 58
             self.set_show_menubar(False)
    
    53 59
             self.set_name('AccountsWindow')
    
    54 60
             self.set_default_size(700, 550)
    
    55
    -        self.set_resizable(False)
    
    61
    +        self.set_resizable(True)
    
    56 62
             self.set_title(_('Accounts'))
    
    57
    -        self.need_relogin = {}
    
    58
    -
    
    59
    -        self._ui = get_builder('accounts_window.ui')
    
    63
    +        self._need_relogin = {}
    
    64
    +        self._accounts = {}
    
    60 65
     
    
    61
    -        self._ui.account_list.add(Preferences(self))
    
    62
    -        account_item = AddAccount()
    
    63
    -        self._ui.account_list.add(account_item)
    
    64
    -        account_item.set_activatable()
    
    66
    +        self._menu = AccountMenu()
    
    67
    +        self._settings = Settings()
    
    65 68
     
    
    66
    -        accounts = app.config.get_per('accounts')
    
    67
    -        accounts.sort()
    
    68
    -        for account in accounts:
    
    69
    -            self.need_relogin[account] = self.get_relogin_settings(account)
    
    70
    -            account_item = Account(account, self)
    
    71
    -            self._ui.account_list.add(account_item)
    
    72
    -            account_item.set_activatable()
    
    69
    +        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
    
    70
    +        box.add(self._menu)
    
    71
    +        box.add(self._settings)
    
    72
    +        self.add(box)
    
    73 73
     
    
    74
    -        self.add(self._ui.box)
    
    75
    -        self._ui.connect_signals(self)
    
    74
    +        for account in app.get_accounts_sorted():
    
    75
    +            self.add_account(account, inital=True)
    
    76 76
     
    
    77
    -        self.connect('destroy', self.on_destroy)
    
    78
    -        self.connect('key-press-event', self.on_key_press)
    
    77
    +        self._menu.connect('menu-activated', self._on_menu_activated)
    
    78
    +        self.connect('destroy', self._on_destroy)
    
    79
    +        self.connect('key-press-event', self._on_key_press)
    
    79 80
     
    
    80
    -        self._activate_preferences_page()
    
    81 81
             self.show_all()
    
    82 82
     
    
    83
    -        app.ged.register_event_handler(
    
    84
    -            'our-show', ged.GUI2, self._nec_our_status)
    
    85
    -
    
    86
    -    @property
    
    87
    -    def stack(self):
    
    88
    -        return self._ui.stack
    
    89
    -
    
    90
    -    def _nec_our_status(self, event):
    
    91
    -        self.update_accounts()
    
    92
    -
    
    93
    -    def _activate_preferences_page(self):
    
    94
    -        row = self._ui.account_list.get_row_at_index(0)
    
    95
    -        self._ui.account_list.select_row(row)
    
    96
    -        self._ui.account_list.emit('row-activated', row)
    
    83
    +    def _on_menu_activated(self, _listbox, account, name):
    
    84
    +        if name == 'back':
    
    85
    +            self._settings.set_page('add-account')
    
    86
    +            self._check_relogin()
    
    87
    +        elif name == 'remove':
    
    88
    +            self.on_remove_account(account)
    
    89
    +        else:
    
    90
    +            self._settings.set_page(name)
    
    97 91
     
    
    98
    -    def on_key_press(self, widget, event):
    
    92
    +    def _on_key_press(self, _widget, event):
    
    99 93
             if event.keyval == Gdk.KEY_Escape:
    
    100 94
                 self.destroy()
    
    101 95
     
    
    102
    -    def on_destroy(self, *args):
    
    103
    -        self.check_relogin()
    
    104
    -        app.ged.remove_event_handler(
    
    105
    -            'our-show', ged.GUI2, self._nec_our_status)
    
    96
    +    def _on_destroy(self, *args):
    
    97
    +        self._check_relogin()
    
    106 98
     
    
    107
    -    def on_child_visible(self, stack, *args):
    
    108
    -        page = stack.get_visible_child_name()
    
    109
    -        if page is None:
    
    110
    -            return
    
    111
    -        if page == 'account':
    
    112
    -            self.check_relogin()
    
    113
    -
    
    114
    -    def update_accounts(self):
    
    115
    -        for row in self._ui.account_list.get_children():
    
    116
    -            row.get_child().update()
    
    117
    -
    
    118
    -    @staticmethod
    
    119
    -    def on_row_activated(listbox, row):
    
    120
    -        row.get_child().on_row_activated()
    
    121
    -
    
    122
    -    def remove_all_pages(self):
    
    123
    -        for page in self._ui.stack.get_children():
    
    124
    -            self._ui.stack.remove(page)
    
    125
    -
    
    126
    -    def set_page(self, page, name):
    
    127
    -        self.remove_all_pages()
    
    128
    -        self._ui.stack.add_named(page, name)
    
    129
    -        page.update()
    
    130
    -        page.show_all()
    
    131
    -        self._ui.stack.set_visible_child(page)
    
    99
    +    def update_account_label(self, account):
    
    100
    +        self._accounts[account].update_account_label()
    
    132 101
     
    
    133 102
         def update_proxy_list(self):
    
    134
    -        page = self._ui.stack.get_child_by_name('connection')
    
    135
    -        if page is None:
    
    136
    -            return
    
    137
    -        page.listbox.get_setting('proxy').update_values()
    
    103
    +        for account in self._accounts:
    
    104
    +            self._settings.update_proxy_list(account)
    
    138 105
     
    
    139
    -    def check_relogin(self):
    
    140
    -        for account in self.need_relogin:
    
    141
    -            settings = self.get_relogin_settings(account)
    
    106
    +    def _check_relogin(self):
    
    107
    +        for account in self._need_relogin:
    
    108
    +            settings = self._get_relogin_settings(account)
    
    142 109
                 active = app.config.get_per('accounts', account, 'active')
    
    143
    -            if settings != self.need_relogin[account]:
    
    144
    -                self.need_relogin[account] = settings
    
    110
    +            if settings != self._need_relogin[account]:
    
    111
    +                self._need_relogin[account] = settings
    
    145 112
                     if active:
    
    146
    -                    self.relog(account)
    
    113
    +                    self._relog(account)
    
    147 114
                     break
    
    148 115
     
    
    149
    -    def relog(self, account):
    
    116
    +    def _relog(self, account):
    
    150 117
             if app.connections[account].connected == 0:
    
    151 118
                 return
    
    152 119
     
    
    ... ... @@ -179,7 +146,7 @@ class AccountsWindow(Gtk.ApplicationWindow):
    179 146
                 on_response_yes=lambda *args: relog(account))
    
    180 147
     
    
    181 148
         @staticmethod
    
    182
    -    def get_relogin_settings(account):
    
    149
    +    def _get_relogin_settings(account):
    
    183 150
             if account == app.ZEROCONF_ACC_NAME:
    
    184 151
                 settings = ['zeroconf_first_name', 'zeroconf_last_name',
    
    185 152
                             'zeroconf_jabber_id', 'zeroconf_email']
    
    ... ... @@ -192,7 +159,7 @@ class AccountsWindow(Gtk.ApplicationWindow):
    192 159
                 values.append(app.config.get_per('accounts', account, setting))
    
    193 160
             return values
    
    194 161
     
    
    195
    -    def on_remove_account(self, button, account):
    
    162
    +    def on_remove_account(self, account):
    
    196 163
             if app.events.get_events(account):
    
    197 164
                 app.interface.raise_dialog('unread-events-on-remove-account')
    
    198 165
                 return
    
    ... ... @@ -234,27 +201,21 @@ class AccountsWindow(Gtk.ApplicationWindow):
    234 201
                 remove(account)
    
    235 202
     
    
    236 203
         def remove_account(self, account):
    
    237
    -        for row in self._ui.account_list.get_children():
    
    238
    -            if row.get_child().account == account:
    
    239
    -                self._ui.account_list.remove(row)
    
    240
    -                del self.need_relogin[account]
    
    241
    -                break
    
    242
    -        self._activate_preferences_page()
    
    204
    +        del self._need_relogin[account]
    
    205
    +        self._accounts[account].remove()
    
    243 206
     
    
    244
    -    def add_account(self, account):
    
    245
    -        account_item = Account(account, self)
    
    246
    -        self._ui.account_list.add(account_item)
    
    247
    -        account_item.set_activatable()
    
    248
    -        self._ui.account_list.show_all()
    
    249
    -        self._ui.stack.show_all()
    
    250
    -        self.need_relogin[account] = self.get_relogin_settings(account)
    
    207
    +    def add_account(self, account, inital=False):
    
    208
    +        self._need_relogin[account] = self._get_relogin_settings(account)
    
    209
    +        self._accounts[account] = Account(account, self._menu, self._settings)
    
    210
    +        if not inital:
    
    211
    +            self._accounts[account].show()
    
    251 212
     
    
    252 213
         def select_account(self, account):
    
    253
    -        for row in self._ui.account_list.get_children():
    
    254
    -            if row.get_child().account == account:
    
    255
    -                self._ui.account_list.select_row(row)
    
    256
    -                self._ui.account_list.emit('row-activated', row)
    
    257
    -                break
    
    214
    +        try:
    
    215
    +            self._accounts[account].select()
    
    216
    +        except KeyError:
    
    217
    +            log.warning('select_account() failed, account %s not found',
    
    218
    +                        account)
    
    258 219
     
    
    259 220
         @staticmethod
    
    260 221
         def enable_account(account):
    
    ... ... @@ -332,209 +293,350 @@ class AccountsWindow(Gtk.ApplicationWindow):
    332 293
             gui_menu_builder.build_accounts_menu()
    
    333 294
     
    
    334 295
     
    
    335
    -class AddAccount(Gtk.Box):
    
    296
    +class Settings(Gtk.ScrolledWindow):
    
    336 297
         def __init__(self):
    
    337
    -        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL,
    
    338
    -                         spacing=12)
    
    298
    +        Gtk.ScrolledWindow.__init__(self)
    
    299
    +        self.set_hexpand(True)
    
    300
    +        self.set_vexpand(True)
    
    301
    +        self.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
    
    302
    +        self._stack = Gtk.Stack()
    
    303
    +        self._stack.set_transition_type(Gtk.StackTransitionType.CROSSFADE)
    
    304
    +        self._stack.add_named(AddNewAccountPage(), 'add-account')
    
    305
    +        self.get_style_context().add_class('accounts-settings')
    
    306
    +
    
    307
    +        self.add(self._stack)
    
    308
    +        self._page_signal_ids = {}
    
    309
    +        self._pages = defaultdict(list)
    
    310
    +
    
    311
    +    def add_page(self, page):
    
    312
    +        self._pages[page.account].append(page)
    
    313
    +        self._stack.add_named(page, '%s-%s' % (page.account, page.name))
    
    314
    +        self._page_signal_ids[page] = page.connect_signal(self._stack)
    
    315
    +
    
    316
    +    def set_page(self, name):
    
    317
    +        self._stack.set_visible_child_name(name)
    
    339 318
     
    
    340
    -        self.account = None
    
    319
    +    def remove_account(self, account):
    
    320
    +        for page in self._pages[account]:
    
    321
    +            signal_id = self._page_signal_ids[page]
    
    322
    +            del self._page_signal_ids[page]
    
    323
    +            self._stack.disconnect(signal_id)
    
    324
    +            self._stack.remove(page)
    
    325
    +            page.destroy()
    
    326
    +        del self._pages[account]
    
    341 327
     
    
    342
    -        self.label = Gtk.Label(label=_('Add Account…'))
    
    343
    -        self.label.set_halign(Gtk.Align.START)
    
    344
    -        self.label.set_hexpand(True)
    
    328
    +    def update_proxy_list(self, account):
    
    329
    +        for page in self._pages[account]:
    
    330
    +            if page.name != 'connection':
    
    331
    +                continue
    
    332
    +            page.listbox.get_setting('proxy').update_values()
    
    345 333
     
    
    346
    -        self.image = Gtk.Image.new_from_icon_name(
    
    347
    -            'list-add-symbolic', Gtk.IconSize.MENU)
    
    348 334
     
    
    349
    -        self.add(self.image)
    
    350
    -        self.add(self.label)
    
    335
    +class AccountMenu(Gtk.Box):
    
    351 336
     
    
    352
    -    def set_activatable(self):
    
    353
    -        self.get_parent().set_selectable(False)
    
    337
    +    __gsignals__ = {
    
    338
    +        'menu-activated': (GObject.SignalFlags.RUN_FIRST, None, (str, str)),
    
    339
    +    }
    
    354 340
     
    
    355
    -    def on_row_activated(self):
    
    356
    -        app.app.activate_action('add-account')
    
    341
    +    def __init__(self):
    
    342
    +        Gtk.Box.__init__(self)
    
    343
    +        self.set_hexpand(False)
    
    344
    +        self.set_size_request(160, -1)
    
    357 345
     
    
    358
    -    def update(self):
    
    359
    -        pass
    
    346
    +        self.get_style_context().add_class('accounts-menu')
    
    360 347
     
    
    348
    +        self._stack = Gtk.Stack()
    
    349
    +        self._stack.set_transition_type(Gtk.StackTransitionType.SLIDE_LEFT)
    
    361 350
     
    
    362
    -class Preferences(Gtk.Box):
    
    363
    -    def __init__(self, parent):
    
    364
    -        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL,
    
    365
    -                         spacing=12)
    
    351
    +        self._accounts_listbox = Gtk.ListBox()
    
    352
    +        self._accounts_listbox.set_sort_func(self._sort_func)
    
    353
    +        self._accounts_listbox.get_style_context().add_class('settings-box')
    
    354
    +        self._accounts_listbox.connect('row-activated',
    
    355
    +                                       self._on_account_row_activated)
    
    366 356
     
    
    367
    -        self.settings = PreferencesPage()
    
    368
    -        self.parent = parent
    
    369
    -        self.account = None
    
    357
    +        scrolled = Gtk.ScrolledWindow()
    
    358
    +        scrolled.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC)
    
    359
    +        scrolled.add(self._accounts_listbox)
    
    360
    +        self._stack.add_named(scrolled, 'accounts')
    
    361
    +        self.add(self._stack)
    
    370 362
     
    
    371
    -        self.label = Gtk.Label(label=_('Preferences'))
    
    372
    -        self.label.set_halign(Gtk.Align.START)
    
    373
    -        self.label.set_hexpand(True)
    
    363
    +    @staticmethod
    
    364
    +    def _sort_func(row1, row2):
    
    365
    +        if row1.label == 'Local':
    
    366
    +            return -1
    
    367
    +        return locale.strcoll(row1.label.lower(), row2.label.lower())
    
    368
    +
    
    369
    +    def add_account(self, row):
    
    370
    +        self._accounts_listbox.add(row)
    
    371
    +        sub_menu = AccountSubMenu(row.account)
    
    372
    +        self._stack.add_named(sub_menu, '%s-menu' % row.account)
    
    373
    +
    
    374
    +        sub_menu.connect('row-activated', self._on_sub_menu_row_activated)
    
    375
    +
    
    376
    +    def remove_account(self, row):
    
    377
    +        if self._stack.get_visible_child_name() != 'accounts':
    
    378
    +            # activate 'back' button
    
    379
    +            self._stack.get_visible_child().get_row_at_index(1).emit('activate')
    
    380
    +        self._accounts_listbox.remove(row)
    
    381
    +        sub_menu = self._stack.get_child_by_name('%s-menu' % row.account)
    
    382
    +        self._stack.remove(sub_menu)
    
    383
    +        row.destroy()
    
    384
    +        sub_menu.destroy()
    
    385
    +
    
    386
    +    def _on_account_row_activated(self, _listbox, row):
    
    387
    +        self._stack.set_visible_child_name('%s-menu' % row.account)
    
    388
    +        self._stack.get_visible_child().get_row_at_index(2).emit('activate')
    
    389
    +
    
    390
    +    def _on_sub_menu_row_activated(self, listbox, row):
    
    391
    +        if row.name == 'back':
    
    392
    +            self._stack.set_visible_child_full(
    
    393
    +                'accounts', Gtk.StackTransitionType.OVER_RIGHT)
    
    394
    +
    
    395
    +        if row.name in ('back', 'remove'):
    
    396
    +            self.emit('menu-activated', listbox.account, row.name)
    
    397
    +
    
    398
    +        else:
    
    399
    +            self.emit('menu-activated',
    
    400
    +                      listbox.account,
    
    401
    +                      '%s-%s' % (listbox.account, row.name))
    
    374 402
     
    
    375
    -        self.image = Gtk.Image.new_from_icon_name(
    
    376
    -            'system-run-symbolic', Gtk.IconSize.MENU)
    
    403
    +    def update_account_label(self, account):
    
    404
    +        self._accounts_listbox.invalidate_sort()
    
    405
    +        sub_menu = self._stack.get_child_by_name('%s-menu' % account)
    
    406
    +        sub_menu.update()
    
    377 407
     
    
    378
    -        self.add(self.image)
    
    379
    -        self.add(self.label)
    
    380 408
     
    
    381
    -    def set_activatable(self):
    
    382
    -        pass
    
    409
    +class AccountSubMenu(Gtk.ListBox):
    
    410
    +
    
    411
    +    __gsignals__ = {
    
    412
    +        'update': (GObject.SignalFlags.RUN_FIRST, None, (str,))
    
    413
    +    }
    
    414
    +
    
    415
    +    def __init__(self, account):
    
    416
    +        Gtk.ListBox.__init__(self)
    
    417
    +        self.set_vexpand(True)
    
    418
    +        self.set_hexpand(True)
    
    419
    +        self.get_style_context().add_class('settings-box')
    
    383 420
     
    
    384
    -    def on_row_activated(self):
    
    385
    -        self.settings.update_states()
    
    386
    -        self.parent.set_page(self.settings, 'pref')
    
    421
    +        self._account = account
    
    422
    +
    
    423
    +        self.add(AccountLabelMenuItem(self, self._account))
    
    424
    +        self.add(BackMenuItem())
    
    425
    +        self.add(PageMenuItem('general', _('General')))
    
    426
    +        if account != 'Local':
    
    427
    +            self.add(PageMenuItem('privacy', _('Privacy')))
    
    428
    +            self.add(PageMenuItem('connection', _('Connection')))
    
    429
    +            self.add(RemoveMenuItem())
    
    430
    +
    
    431
    +    @property
    
    432
    +    def account(self):
    
    433
    +        return self._account
    
    387 434
     
    
    388 435
         def update(self):
    
    389
    -        pass
    
    436
    +        self.emit('update', self._account)
    
    390 437
     
    
    391 438
     
    
    392
    -class Account(Gtk.Box):
    
    393
    -    def __init__(self, account, parent):
    
    394
    -        Gtk.Box.__init__(self, orientation=Gtk.Orientation.HORIZONTAL,
    
    395
    -                         spacing=12)
    
    396
    -        self.account = account
    
    397
    -        if account == app.ZEROCONF_ACC_NAME:
    
    398
    -            self.settings = ZeroConfPage(account, parent)
    
    399
    -        else:
    
    400
    -            self.settings = AccountPage(account, parent)
    
    401
    -        self.parent = parent
    
    439
    +class MenuItem(Gtk.ListBoxRow):
    
    440
    +    def __init__(self, name):
    
    441
    +        Gtk.ListBoxRow.__init__(self)
    
    442
    +        self._name = name
    
    443
    +        self._box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL,
    
    444
    +                            spacing=12)
    
    445
    +        self._label = Gtk.Label()
    
    402 446
     
    
    403
    -        self.label = Gtk.Label(label=app.get_account_label(account))
    
    404
    -        self.label.set_halign(Gtk.Align.START)
    
    405
    -        self.label.set_hexpand(True)
    
    447
    +        self.add(self._box)
    
    406 448
     
    
    407
    -        self.image = Gtk.Image()
    
    408
    -        self._update_image()
    
    449
    +    @property
    
    450
    +    def name(self):
    
    451
    +        return self._name
    
    409 452
     
    
    410
    -        self.add(self.image)
    
    411
    -        self.add(self.label)
    
    412 453
     
    
    413
    -    def set_activatable(self):
    
    414
    -        if self.account == app.ZEROCONF_ACC_NAME:
    
    415
    -            zeroconf = app.is_installed('ZEROCONF')
    
    416
    -            self.get_parent().set_activatable(zeroconf)
    
    417
    -            self.get_parent().set_sensitive(zeroconf)
    
    418
    -            if not zeroconf:
    
    419
    -                self.get_parent().set_tooltip_text(
    
    420
    -                    _('Please check if Avahi or Bonjour is installed.'))
    
    454
    +class RemoveMenuItem(MenuItem):
    
    455
    +    def __init__(self):
    
    456
    +        super().__init__('remove')
    
    457
    +        self._label.set_text(_('Remove'))
    
    458
    +        image = Gtk.Image.new_from_icon_name('user-trash-symbolic',
    
    459
    +                                             Gtk.IconSize.MENU)
    
    421 460
     
    
    422
    -    def on_row_activated(self):
    
    423
    -        self.settings.update_states()
    
    424
    -        self.parent.set_page(self.settings, 'account')
    
    461
    +        self.set_selectable(False)
    
    462
    +        image.get_style_context().add_class('error-color')
    
    425 463
     
    
    426
    -    def update(self):
    
    427
    -        self.label.set_text(app.get_account_label(self.account))
    
    428
    -        self._update_image()
    
    464
    +        self._box.add(image)
    
    465
    +        self._box.add(self._label)
    
    429 466
     
    
    430
    -    def _update_image(self):
    
    431
    -        show = helpers.get_current_show(self.account)
    
    432
    -        icon = get_icon_name(show)
    
    433
    -        self.image.set_from_icon_name(icon, Gtk.IconSize.MENU)
    
    434 467
     
    
    468
    +class AccountLabelMenuItem(MenuItem):
    
    469
    +    def __init__(self, parent, account):
    
    470
    +        super().__init__('account-label')
    
    471
    +        self._update_account_label(parent, account)
    
    435 472
     
    
    436
    -class GenericSettingPage(Gtk.Box):
    
    437
    -    def __init__(self, account, parent, settings):
    
    438
    -        Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12)
    
    439
    -        self.account = account
    
    440
    -        self.parent = parent
    
    473
    +        self.set_selectable(False)
    
    474
    +        self.set_sensitive(False)
    
    475
    +        self.set_activatable(False)
    
    441 476
     
    
    442
    -        button = Gtk.Button.new_from_icon_name(
    
    443
    -            'go-previous-symbolic', Gtk.IconSize.MENU)
    
    444
    -        button.set_halign(Gtk.Align.START)
    
    445
    -        button.connect('clicked', self._on_back_button)
    
    446
    -        if not isinstance(self, (AccountPage, PreferencesPage, ZeroConfPage)):
    
    447
    -            self.pack_start(button, False, True, 0)
    
    477
    +        image = Gtk.Image.new_from_icon_name('avatar-default-symbolic',
    
    478
    +                                             Gtk.IconSize.MENU)
    
    479
    +        image.get_style_context().add_class('insensitive-fg-color')
    
    448 480
     
    
    449
    -        self.listbox = SettingsBox(account)
    
    450
    -        self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
    
    451
    -        self.listbox.set_vexpand(False)
    
    452
    -        self.listbox.set_valign(Gtk.Align.END)
    
    481
    +        self._label.get_style_context().add_class('accounts-label-row')
    
    482
    +        self._label.set_ellipsize(Pango.EllipsizeMode.END)
    
    483
    +        self._label.set_xalign(0)
    
    453 484
     
    
    454
    -        for setting in settings:
    
    455
    -            self.listbox.add_setting(setting)
    
    456
    -        self.listbox.update_states()
    
    485
    +        self._box.add(image)
    
    486
    +        self._box.add(self._label)
    
    457 487
     
    
    458
    -        self.pack_end(self.listbox, True, True, 0)
    
    488
    +        parent.connect('update', self._update_account_label)
    
    459 489
     
    
    460
    -        self.listbox.connect('row-activated', self.on_row_activated)
    
    490
    +    def _update_account_label(self, _listbox, account):
    
    491
    +        account_label = app.get_account_label(account)
    
    492
    +        self._label.set_text(account_label)
    
    461 493
     
    
    462
    -    def _on_back_button(self, *args):
    
    463
    -        account_window = self.get_toplevel()
    
    464
    -        child = account_window.stack.get_visible_child()
    
    465
    -        account_window.remove_all_pages()
    
    466
    -        account_window.stack.add_named(child.parent, 'account')
    
    467
    -        account_window.stack.set_visible_child_name('account')
    
    468 494
     
    
    469
    -    def update_states(self):
    
    470
    -        self.listbox.update_states()
    
    495
    +class BackMenuItem(MenuItem):
    
    496
    +    def __init__(self):
    
    497
    +        super().__init__('back')
    
    498
    +        self.set_selectable(False)
    
    471 499
     
    
    472
    -    def on_row_activated(self, listbox, row):
    
    473
    -        row.on_row_activated()
    
    500
    +        self._label.set_text(_('Back'))
    
    474 501
     
    
    475
    -    def set_entry_text(self, toggle, update=False):
    
    476
    -        account_label = app.get_account_label(self.account)
    
    477
    -        if update:
    
    478
    -            self.entry.set_text(account_label)
    
    479
    -            return
    
    480
    -        if toggle.get_active():
    
    481
    -            self.entry.set_sensitive(True)
    
    482
    -            self.entry.grab_focus()
    
    502
    +        image = Gtk.Image.new_from_icon_name('go-previous-symbolic',
    
    503
    +                                             Gtk.IconSize.MENU)
    
    504
    +        image.get_style_context().add_class('insensitive-fg-color')
    
    505
    +
    
    506
    +        self._box.add(image)
    
    507
    +        self._box.add(self._label)
    
    508
    +
    
    509
    +
    
    510
    +class PageMenuItem(MenuItem):
    
    511
    +    def __init__(self, name, label):
    
    512
    +        super().__init__(name)
    
    513
    +
    
    514
    +        if name == 'general':
    
    515
    +            icon = 'preferences-system-symbolic'
    
    516
    +        elif name == 'privacy':
    
    517
    +            icon = 'preferences-system-privacy-symbolic'
    
    518
    +        elif name == 'connection':
    
    519
    +            icon = 'preferences-system-network-symbolic'
    
    483 520
             else:
    
    484
    -            self.entry.set_sensitive(False)
    
    485
    -            value = self.entry.get_text()
    
    486
    -            if not value:
    
    487
    -                value = account_label
    
    488
    -            app.config.set_per('accounts', self.account,
    
    489
    -                               'account_label', value or self.account)
    
    490
    -            if app.config.get_per('accounts', self.account, 'active'):
    
    491
    -                app.interface.roster.draw_account(self.account)
    
    492
    -                gui_menu_builder.build_accounts_menu()
    
    521
    +            icon = 'dialog-error-symbolic'
    
    493 522
     
    
    494
    -    def update(self):
    
    495
    -        pass
    
    523
    +        image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.MENU)
    
    524
    +        self._label.set_text(label)
    
    525
    +
    
    526
    +        self._box.add(image)
    
    527
    +        self._box.add(self._label)
    
    528
    +
    
    529
    +
    
    530
    +class Account:
    
    531
    +    def __init__(self, account, menu, settings):
    
    532
    +        self._account = account
    
    533
    +        self._menu = menu
    
    534
    +        self._settings = settings
    
    535
    +
    
    536
    +        if account == app.ZEROCONF_ACC_NAME:
    
    537
    +            self._settings.add_page(ZeroConfPage(account))
    
    538
    +        else:
    
    539
    +            self._settings.add_page(GeneralPage(account))
    
    540
    +            self._settings.add_page(ConnectionPage(account))
    
    541
    +            self._settings.add_page(PrivacyPage(account))
    
    542
    +
    
    543
    +        self._account_row = AccountRow(account)
    
    544
    +        self._menu.add_account(self._account_row)
    
    545
    +
    
    546
    +    def select(self):
    
    547
    +        self._account_row.emit('activate')
    
    548
    +
    
    549
    +    def show(self):
    
    550
    +        self._menu.show_all()
    
    551
    +        self._settings.show_all()
    
    552
    +        self.select()
    
    553
    +
    
    554
    +    def remove(self):
    
    555
    +        self._menu.remove_account(self._account_row)
    
    556
    +        self._settings.remove_account(self._account)
    
    557
    +
    
    558
    +    def update_account_label(self):
    
    559
    +        self._account_row.update_account_label()
    
    560
    +        self._menu.update_account_label(self._account)
    
    561
    +
    
    562
    +    @property
    
    563
    +    def menu(self):
    
    564
    +        return self._menu
    
    496 565
     
    
    497
    -    def set_page(self, settings, name):
    
    498
    -        settings.update_states()
    
    499
    -        self.get_toplevel().set_page(settings, name)
    
    566
    +    @property
    
    567
    +    def account(self):
    
    568
    +        return self._account
    
    569
    +
    
    570
    +    @property
    
    571
    +    def settings(self):
    
    572
    +        return self._account
    
    573
    +
    
    574
    +
    
    575
    +class AccountRow(Gtk.ListBoxRow):
    
    576
    +    def __init__(self, account):
    
    577
    +        Gtk.ListBoxRow.__init__(self)
    
    578
    +        self.set_selectable(False)
    
    579
    +
    
    580
    +        box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=12)
    
    581
    +
    
    582
    +        self._account = account
    
    583
    +
    
    584
    +        self._label = Gtk.Label(label=app.get_account_label(account))
    
    585
    +        self._label.set_halign(Gtk.Align.START)
    
    586
    +        self._label.set_hexpand(True)
    
    587
    +        self._label.set_ellipsize(Pango.EllipsizeMode.END)
    
    588
    +        self._label.set_xalign(0)
    
    589
    +        self._label.set_width_chars(18)
    
    590
    +
    
    591
    +        next_icon = Gtk.Image.new_from_icon_name('go-next-symbolic',
    
    592
    +                                                 Gtk.IconSize.MENU)
    
    593
    +        next_icon.get_style_context().add_class('insensitive-fg-color')
    
    500 594
     
    
    501
    -    def _add_top_buttons(self, parent):
    
    502
    -        # This adds the Account enable switch and the back button
    
    503
    -        box = Gtk.Box()
    
    504
    -        box.set_hexpand(True)
    
    505
    -        box.set_halign(Gtk.Align.FILL)
    
    506 595
             switch = Gtk.Switch()
    
    507
    -        switch.set_active(app.config.get_per('accounts', self.account, 'active'))
    
    596
    +        switch.set_active(
    
    597
    +            app.config.get_per('accounts', self._account, 'active'))
    
    508 598
             switch.set_vexpand(False)
    
    509
    -        switch.set_valign(Gtk.Align.CENTER)
    
    510
    -        switch.set_halign(Gtk.Align.END)
    
    511
    -        if self.account == app.ZEROCONF_ACC_NAME and not app.is_installed('ZEROCONF'):
    
    512
    -            switch.set_sensitive(False)
    
    599
    +
    
    600
    +        if (self._account == app.ZEROCONF_ACC_NAME and
    
    601
    +                not app.is_installed('ZEROCONF')):
    
    513 602
                 switch.set_active(False)
    
    603
    +            self.set_activatable(False)
    
    604
    +            self.set_sensitive(False)
    
    605
    +            if sys.platform in ('win32', 'darwin'):
    
    606
    +                tooltip = _('Please check if Bonjour is installed.')
    
    607
    +            else:
    
    608
    +                tooltip = _('Please check if Avahi is installed.')
    
    609
    +            self.set_tooltip_text(tooltip)
    
    514 610
     
    
    515
    -        switch.connect('state-set', self._on_enable_switch, self.account)
    
    516
    -        box.pack_start(switch, False, False, 0)
    
    517
    -        if self.account != app.ZEROCONF_ACC_NAME:
    
    518
    -            button = Gtk.Button(label=_('Remove'))
    
    519
    -            button.connect(
    
    520
    -                'clicked', parent.on_remove_account, self.account)
    
    521
    -            button.get_style_context().add_class('destructive-action')
    
    522
    -            button.set_halign(Gtk.Align.END)
    
    523
    -            switch.set_vexpand(False)
    
    524
    -            box.pack_end(button, False, False, 0)
    
    525
    -        self.pack_start(box, True, True, 0)
    
    611
    +        switch.connect('state-set', self._on_enable_switch, self._account)
    
    612
    +
    
    613
    +        box.add(switch)
    
    614
    +        box.add(self._label)
    
    615
    +        box.add(next_icon)
    
    616
    +        self.add(box)
    
    617
    +
    
    618
    +    @property
    
    619
    +    def account(self):
    
    620
    +        return self._account
    
    621
    +
    
    622
    +    @property
    
    623
    +    def label(self):
    
    624
    +        return self._label.get_text()
    
    625
    +
    
    626
    +    def update_account_label(self):
    
    627
    +        self._label.set_text(app.get_account_label(self._account))
    
    526 628
     
    
    527 629
         def _on_enable_switch(self, switch, state, account):
    
    528 630
             def _disable():
    
    529 631
                 app.connections[account].change_status('offline', 'offline')
    
    530 632
                 app.connections[account].disconnect(reconnect=False)
    
    531
    -            self.parent.disable_account(account)
    
    633
    +            self.get_toplevel().disable_account(account)
    
    532 634
                 app.config.set_per('accounts', account, 'active', False)
    
    533 635
                 switch.set_state(state)
    
    534 636
     
    
    535 637
             old_state = app.config.get_per('accounts', account, 'active')
    
    536 638
             if old_state == state:
    
    537
    -            return
    
    639
    +            return Gdk.EVENT_PROPAGATE
    
    538 640
     
    
    539 641
             if (account in app.connections and
    
    540 642
                     app.connections[account].connected > 0):
    
    ... ... @@ -549,38 +651,89 @@ class GenericSettingPage(Gtk.Box):
    549 651
                      DialogButton.make('Remove',
    
    550 652
                                        text=_('Disable Account'),
    
    551 653
                                        callback=_disable)],
    
    552
    -                transient_for=self.parent).show()
    
    654
    +                transient_for=self.get_toplevel()).show()
    
    553 655
                 return Gdk.EVENT_STOP
    
    554 656
     
    
    555 657
             if state:
    
    556
    -            self.parent.enable_account(account)
    
    658
    +            self.get_toplevel().enable_account(account)
    
    557 659
             else:
    
    558
    -            self.parent.disable_account(account)
    
    660
    +            self.get_toplevel().disable_account(account)
    
    559 661
             app.config.set_per('accounts', account, 'active', state)
    
    560 662
     
    
    663
    +        return Gdk.EVENT_PROPAGATE
    
    664
    +
    
    561 665
     
    
    562
    -class PreferencesPage(GenericSettingPage):
    
    666
    +class AddNewAccountPage(Gtk.Box):
    
    563 667
         def __init__(self):
    
    668
    +        Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL)
    
    669
    +        self.set_vexpand(True)
    
    670
    +        self.set_hexpand(True)
    
    671
    +        button = Gtk.Button(label='Add Account')
    
    672
    +        button.get_style_context().add_class('suggested-action')
    
    673
    +        button.set_action_name('app.add-account')
    
    674
    +        button.set_halign(Gtk.Align.CENTER)
    
    675
    +        button.set_valign(Gtk.Align.CENTER)
    
    676
    +        self.add(button)
    
    564 677
     
    
    565
    -        settings = [
    
    566
    -            Setting(SettingKind.SWITCH, _('Merge Accounts'),
    
    567
    -                    SettingType.ACTION, 'merge'),
    
    568
    -            ]
    
    569 678
     
    
    570
    -        GenericSettingPage.__init__(self, None, None, settings)
    
    679
    +class GenericSettingPage(Gtk.Box):
    
    680
    +    def __init__(self, account, settings):
    
    681
    +        Gtk.Box.__init__(self, orientation=Gtk.Orientation.VERTICAL, spacing=12)
    
    682
    +        self.set_valign(Gtk.Align.START)
    
    683
    +        self.set_vexpand(True)
    
    684
    +        self.account = account
    
    685
    +
    
    686
    +        self.listbox = SettingsBox(account)
    
    687
    +        self.listbox.get_style_context().add_class('accounts-settings-border')
    
    688
    +        self.listbox.set_selection_mode(Gtk.SelectionMode.NONE)
    
    689
    +        self.listbox.set_vexpand(False)
    
    690
    +        self.listbox.set_valign(Gtk.Align.END)
    
    691
    +
    
    692
    +        for setting in settings:
    
    693
    +            self.listbox.add_setting(setting)
    
    694
    +        self.listbox.update_states()
    
    571 695
     
    
    696
    +        self.pack_end(self.listbox, True, True, 0)
    
    572 697
     
    
    573
    -class AccountPage(GenericSettingPage):
    
    574
    -    def __init__(self, account, parent=None):
    
    698
    +        self.listbox.connect('row-activated', self.on_row_activated)
    
    575 699
     
    
    576
    -        general = partial(
    
    577
    -            self.set_page, GeneralPage(account, self), 'general')
    
    700
    +    def connect_signal(self, stack):
    
    701
    +        return stack.connect('notify::visible-child',
    
    702
    +                             self._on_visible_child_changed)
    
    578 703
     
    
    579
    -        connection = partial(
    
    580
    -            self.set_page, ConnectionPage(account, self), 'connection')
    
    704
    +    def _on_visible_child_changed(self, stack, param):
    
    705
    +        if self == stack.get_visible_child():
    
    706
    +            self.listbox.update_states()
    
    581 707
     
    
    582
    -        privacy = partial(
    
    583
    -            self.set_page, PrivacyPage(account, self), 'privacy')
    
    708
    +    @staticmethod
    
    709
    +    def on_row_activated(_listbox, row):
    
    710
    +        row.on_row_activated()
    
    711
    +
    
    712
    +    def set_entry_text(self, toggle, update=False):
    
    713
    +        account_label = app.get_account_label(self.account)
    
    714
    +        if update:
    
    715
    +            self.entry.set_text(account_label)
    
    716
    +            return
    
    717
    +        if toggle.get_active():
    
    718
    +            self.entry.set_sensitive(True)
    
    719
    +            self.entry.grab_focus()
    
    720
    +        else:
    
    721
    +            self.entry.set_sensitive(False)
    
    722
    +            value = self.entry.get_text()
    
    723
    +            if not value:
    
    724
    +                value = account_label
    
    725
    +            app.config.set_per('accounts', self.account,
    
    726
    +                               'account_label', value or self.account)
    
    727
    +            if app.config.get_per('accounts', self.account, 'active'):
    
    728
    +                app.interface.roster.draw_account(self.account)
    
    729
    +                gui_menu_builder.build_accounts_menu()
    
    730
    +
    
    731
    +
    
    732
    +class GeneralPage(GenericSettingPage):
    
    733
    +
    
    734
    +    name = 'general'
    
    735
    +
    
    736
    +    def __init__(self, account):
    
    584 737
     
    
    585 738
             settings = [
    
    586 739
                 Setting(SettingKind.ENTRY, _('Label'),
    
    ... ... @@ -590,43 +743,21 @@ class AccountPage(GenericSettingPage):
    590 743
                 Setting(SettingKind.LOGIN, _('Login'), SettingType.DIALOG,
    
    591 744
                         props={'dialog': LoginDialog}),
    
    592 745
     
    
    593
    -            Setting(SettingKind.ACTION, _('Profile'), SettingType.ACTION,
    
    594
    -                    '-profile', props={'action_args': account}),
    
    595
    -
    
    596
    -            Setting(SettingKind.CALLBACK, _('General'),
    
    597
    -                    name='general', props={'callback': general}),
    
    598
    -
    
    599
    -            Setting(SettingKind.CALLBACK, _('Privacy'),
    
    600
    -                    name='privacy', props={'callback': privacy}),
    
    601
    -
    
    602
    -            Setting(SettingKind.CALLBACK, _('Connection'),
    
    603
    -                    name='connection', props={'callback': connection}),
    
    604
    -
    
    605
    -            Setting(SettingKind.ACTION, _('Import Contacts'), SettingType.ACTION,
    
    606
    -                    '-import-contacts', props={'action_args': account}),
    
    746
    +            Setting(SettingKind.ACTION, _('Import Contacts'),
    
    747
    +                    SettingType.ACTION, '-import-contacts',
    
    748
    +                    props={'account': account}),
    
    607 749
     
    
    608 750
                 Setting(SettingKind.DIALOG, _('Client Certificate'),
    
    609 751
                         SettingType.DIALOG, props={'dialog': CertificateDialog}),
    
    610
    -            ]
    
    611
    -
    
    612
    -        GenericSettingPage.__init__(self, account, parent, settings)
    
    613
    -        self._add_top_buttons(parent)
    
    614
    -
    
    615
    -    def _on_account_name_change(self, account_name, *args):
    
    616
    -        self.parent.update_accounts()
    
    617
    -
    
    618
    -
    
    619
    -class GeneralPage(GenericSettingPage):
    
    620
    -    def __init__(self, account, parent=None):
    
    621 752
     
    
    622
    -        settings = [
    
    623 753
                 Setting(SettingKind.SWITCH, _('Connect on startup'),
    
    624 754
                         SettingType.ACCOUNT_CONFIG, 'autoconnect'),
    
    625 755
     
    
    626 756
                 Setting(SettingKind.SWITCH, _('Reconnect when connection is lost'),
    
    627 757
                         SettingType.ACCOUNT_CONFIG, 'autoreconnect'),
    
    628 758
     
    
    629
    -            Setting(SettingKind.SWITCH, _('Save conversations for all contacts'),
    
    759
    +            Setting(SettingKind.SWITCH,
    
    760
    +                    _('Save conversations for all contacts'),
    
    630 761
                         SettingType.ACCOUNT_CONFIG, 'no_log_for',
    
    631 762
                         desc=_('Store conversations on the harddrive')),
    
    632 763
     
    
    ... ... @@ -647,12 +778,18 @@ class GeneralPage(GenericSettingPage):
    647 778
     
    
    648 779
                 Setting(SettingKind.SWITCH, _('Use file transfer proxies'),
    
    649 780
                         SettingType.ACCOUNT_CONFIG, 'use_ft_proxies'),
    
    650
    -            ]
    
    651
    -        GenericSettingPage.__init__(self, account, parent, settings)
    
    781
    +        ]
    
    782
    +        GenericSettingPage.__init__(self, account, settings)
    
    783
    +
    
    784
    +    def _on_account_name_change(self, *args):
    
    785
    +        self.get_toplevel().update_account_label(self.account)
    
    652 786
     
    
    653 787
     
    
    654 788
     class PrivacyPage(GenericSettingPage):
    
    655
    -    def __init__(self, account, parent=None):
    
    789
    +
    
    790
    +    name = 'privacy'
    
    791
    +
    
    792
    +    def __init__(self, account):
    
    656 793
     
    
    657 794
             settings = [
    
    658 795
                 Setting(SettingKind.SWITCH, _('Idle Time'),
    
    ... ... @@ -675,11 +812,14 @@ class PrivacyPage(GenericSettingPage):
    675 812
                                'Roster')),
    
    676 813
     
    
    677 814
                 ]
    
    678
    -        GenericSettingPage.__init__(self, account, parent, settings)
    
    815
    +        GenericSettingPage.__init__(self, account, settings)
    
    679 816
     
    
    680 817
     
    
    681 818
     class ConnectionPage(GenericSettingPage):
    
    682
    -    def __init__(self, account, parent=None):
    
    819
    +
    
    820
    +    name = 'connection'
    
    821
    +
    
    822
    +    def __init__(self, account):
    
    683 823
     
    
    684 824
             settings = [
    
    685 825
                 Setting(SettingKind.SWITCH, 'HTTP_PROXY',
    
    ... ... @@ -707,21 +847,26 @@ class ConnectionPage(GenericSettingPage):
    707 847
                         SettingType.DIALOG, props={'dialog': PriorityDialog}),
    
    708 848
                 ]
    
    709 849
     
    
    710
    -        GenericSettingPage.__init__(self, account, parent, settings)
    
    850
    +        GenericSettingPage.__init__(self, account, settings)
    
    711 851
     
    
    712 852
     
    
    713 853
     class ZeroConfPage(GenericSettingPage):
    
    714
    -    def __init__(self, account, parent=None):
    
    854
    +
    
    855
    +    name = 'general'
    
    856
    +
    
    857
    +    def __init__(self, account):
    
    715 858
     
    
    716 859
             settings = [
    
    717 860
                 Setting(SettingKind.DIALOG, _('Profile'),
    
    718
    -                    SettingType.DIALOG, props={'dialog': ZeroconfProfileDialog}),
    
    861
    +                    SettingType.DIALOG,
    
    862
    +                    props={'dialog': ZeroconfProfileDialog}),
    
    719 863
     
    
    720 864
                 Setting(SettingKind.SWITCH, _('Connect on startup'),
    
    721 865
                         SettingType.ACCOUNT_CONFIG, 'autoconnect',
    
    722 866
                         desc=_('Use environment variable')),
    
    723 867
     
    
    724
    -            Setting(SettingKind.SWITCH, _('Save conversations for all contacts'),
    
    868
    +            Setting(SettingKind.SWITCH,
    
    869
    +                    _('Save conversations for all contacts'),
    
    725 870
                         SettingType.ACCOUNT_CONFIG, 'no_log_for',
    
    726 871
                         desc=_('Store conversations on the harddrive')),
    
    727 872
     
    
    ... ... @@ -730,8 +875,7 @@ class ZeroConfPage(GenericSettingPage):
    730 875
                         desc=_('Synchronize the status of all accounts')),
    
    731 876
                 ]
    
    732 877
     
    
    733
    -        GenericSettingPage.__init__(self, account, parent, settings)
    
    734
    -        self._add_top_buttons(None)
    
    878
    +        GenericSettingPage.__init__(self, account, settings)
    
    735 879
     
    
    736 880
     
    
    737 881
     class ZeroconfProfileDialog(SettingsDialog):
    
    ... ... @@ -752,7 +896,7 @@ class ZeroconfProfileDialog(SettingsDialog):
    752 896
                 ]
    
    753 897
     
    
    754 898
             SettingsDialog.__init__(self, parent, _('Profile'),
    
    755
    -                               Gtk.DialogFlags.MODAL, settings, account)
    
    899
    +                                Gtk.DialogFlags.MODAL, settings, account)
    
    756 900
     
    
    757 901
     
    
    758 902
     class PriorityDialog(SettingsDialog):
    
    ... ... @@ -775,7 +919,7 @@ class PriorityDialog(SettingsDialog):
    775 919
                 ]
    
    776 920
     
    
    777 921
             SettingsDialog.__init__(self, parent, _('Priority'),
    
    778
    -                               Gtk.DialogFlags.MODAL, settings, account)
    
    922
    +                                Gtk.DialogFlags.MODAL, settings, account)
    
    779 923
     
    
    780 924
             self.connect('destroy', self.on_destroy)
    
    781 925
     
    
    ... ... @@ -793,7 +937,8 @@ class CutstomHostnameDialog(SettingsDialog):
    793 937
     
    
    794 938
             settings = [
    
    795 939
                 Setting(SettingKind.SWITCH, _('Enable'),
    
    796
    -                    SettingType.ACCOUNT_CONFIG, 'use_custom_host', name='custom'),
    
    940
    +                    SettingType.ACCOUNT_CONFIG,
    
    941
    +                    'use_custom_host', name='custom'),
    
    797 942
     
    
    798 943
                 Setting(SettingKind.ENTRY, _('Hostname'),
    
    799 944
                         SettingType.ACCOUNT_CONFIG, 'custom_host',
    
    ... ... @@ -805,13 +950,13 @@ class CutstomHostnameDialog(SettingsDialog):
    805 950
                 ]
    
    806 951
     
    
    807 952
             SettingsDialog.__init__(self, parent, _('Connection Settings'),
    
    808
    -                               Gtk.DialogFlags.MODAL, settings, account)
    
    953
    +                                Gtk.DialogFlags.MODAL, settings, account)
    
    809 954
     
    
    810 955
     
    
    811 956
     class CertificateDialog(SettingsDialog):
    
    812 957
         def __init__(self, account, parent):
    
    813 958
     
    
    814
    -        Settings = [
    
    959
    +        settings = [
    
    815 960
                 Setting(SettingKind.FILECHOOSER, _('Client Certificate'),
    
    816 961
                         SettingType.ACCOUNT_CONFIG, 'client_cert',
    
    817 962
                         props={'filefilter': (_('PKCS12 Files'), '*.p12')}),
    
    ... ... @@ -821,7 +966,7 @@ class CertificateDialog(SettingsDialog):
    821 966
                 ]
    
    822 967
     
    
    823 968
             SettingsDialog.__init__(self, parent, _('Certificate Settings'),
    
    824
    -                               Gtk.DialogFlags.MODAL, Settings, account)
    
    969
    +                                Gtk.DialogFlags.MODAL, settings, account)
    
    825 970
     
    
    826 971
     
    
    827 972
     class LoginDialog(SettingsDialog):
    
    ... ... @@ -841,11 +986,11 @@ class LoginDialog(SettingsDialog):
    841 986
                 ]
    
    842 987
     
    
    843 988
             SettingsDialog.__init__(self, parent, _('Login Settings'),
    
    844
    -                               Gtk.DialogFlags.MODAL, settings, account)
    
    989
    +                                Gtk.DialogFlags.MODAL, settings, account)
    
    845 990
     
    
    846 991
             self.connect('destroy', self.on_destroy)
    
    847 992
     
    
    848
    -    def on_password_change(self, new_password, data):
    
    993
    +    def on_password_change(self, new_password, _data):
    
    849 994
             passwords.save_password(self.account, new_password)
    
    850 995
     
    
    851 996
         def on_destroy(self, *args):
    
    ... ... @@ -860,11 +1005,11 @@ class RemoveAccountWindow:
    860 1005
         then remove the account given
    
    861 1006
         """
    
    862 1007
     
    
    863
    -    def on_remove_account_window_destroy(self, widget):
    
    1008
    +    def on_remove_account_window_destroy(self, _widget):
    
    864 1009
             if self.account in app.interface.instances:
    
    865 1010
                 del app.interface.instances[self.account]['remove_account']
    
    866 1011
     
    
    867
    -    def on_cancel_button_clicked(self, widget):
    
    1012
    +    def on_cancel_button_clicked(self, _widget):
    
    868 1013
             self._ui.remove_account_window.destroy()
    
    869 1014
     
    
    870 1015
         def __init__(self, account):
    
    ... ... @@ -872,43 +1017,45 @@ class RemoveAccountWindow:
    872 1017
             self._ui = get_builder('remove_account_window.ui')
    
    873 1018
             active_window = app.app.get_active_window()
    
    874 1019
             self._ui.remove_account_window.set_transient_for(active_window)
    
    875
    -        self._ui.remove_account_window.set_title(_('Removing account %s') % self.account)
    
    1020
    +        self._ui.remove_account_window.set_title(
    
    1021
    +            _('Removing account %s') % self.account)
    
    876 1022
             self._ui.connect_signals(self)
    
    877 1023
             self._ui.remove_account_window.show_all()
    
    878 1024
     
    
    879
    -    def on_remove_button_clicked(self, widget):
    
    1025
    +    def on_remove_button_clicked(self, _widget):
    
    880 1026
             def remove():
    
    881 1027
                 if self.account in app.connections and \
    
    882 1028
                 app.connections[self.account].connected and \
    
    883 1029
                 not self._ui.remove_and_unregister_radiobutton.get_active():
    
    884
    -                # change status to offline only if we will not remove this JID from
    
    885
    -                # server
    
    1030
    +                # change status to offline only if we will not
    
    1031
    +                # remove this JID from server
    
    886 1032
                     app.connections[self.account].change_status('offline', 'offline')
    
    887 1033
                 if self._ui.remove_and_unregister_radiobutton.get_active():
    
    888 1034
                     if not self.account in app.connections:
    
    889 1035
                         ErrorDialog(
    
    890 1036
                             _('Account is disabled'),
    
    891 1037
                             _('To unregister from a server, the account must be '
    
    892
    -                        'enabled.'),
    
    1038
    +                          'enabled.'),
    
    893 1039
                             transient_for=self._ui.remove_account_window)
    
    894 1040
                         return
    
    895 1041
                     if not app.connections[self.account].password:
    
    896
    -                    def on_ok(passphrase, checked):
    
    1042
    +                    def on_ok(passphrase, _checked):
    
    897 1043
                             if passphrase == -1:
    
    898
    -                            # We don't remove account cause we canceled pw window
    
    1044
    +                            # We don't remove account cause we
    
    1045
    +                            # canceled pw window
    
    899 1046
                                 return
    
    900 1047
                             app.connections[self.account].password = passphrase
    
    901 1048
                             app.connections[self.account].unregister_account(
    
    902
    -                                self._on_remove_success)
    
    1049
    +                            self._on_remove_success)
    
    903 1050
     
    
    904 1051
                         PassphraseDialog(
    
    905
    -                            _('Password required'),
    
    906
    -                            _('Enter your password for account %s') % self.account,
    
    907
    -                            _('Save password'), ok_handler=on_ok,
    
    908
    -                            transient_for=self._ui.remove_account_window)
    
    1052
    +                        _('Password required'),
    
    1053
    +                        _('Enter your password for account %s') % self.account,
    
    1054
    +                        _('Save password'), ok_handler=on_ok,
    
    1055
    +                        transient_for=self._ui.remove_account_window)
    
    909 1056
                         return
    
    910 1057
                     app.connections[self.account].unregister_account(
    
    911
    -                        self._on_remove_success)
    
    1058
    +                    self._on_remove_success)
    
    912 1059
                 else:
    
    913 1060
                     self._on_remove_success(True)
    
    914 1061
     
    
    ... ... @@ -931,12 +1078,12 @@ class RemoveAccountWindow:
    931 1078
             # Error message is send by connect_and_auth()
    
    932 1079
             if not res:
    
    933 1080
                 ConfirmationDialogDoubleRadio(
    
    934
    -                    _('Connection to server %s failed') % self.account,
    
    935
    -                    _('What would you like to do?'),
    
    936
    -                    _('Remove only from Gajim'),
    
    937
    -                    _('Don\'t remove anything. I\'ll try again later'),
    
    938
    -                    on_response_ok=self.on_remove_response_ok, is_modal=False,
    
    939
    -                    transient_for=self._ui.remove_account_window)
    
    1081
    +                _('Connection to server %s failed') % self.account,
    
    1082
    +                _('What would you like to do?'),
    
    1083
    +                _('Remove only from Gajim'),
    
    1084
    +                _('Don\'t remove anything. I\'ll try again later'),
    
    1085
    +                on_response_ok=self.on_remove_response_ok, is_modal=False,
    
    1086
    +                transient_for=self._ui.remove_account_window)
    
    940 1087
                 return
    
    941 1088
             # Close all opened windows
    
    942 1089
             app.interface.roster.close_all(self.account, force=True)
    

  • gajim/gtk/settings.py
    ... ... @@ -81,7 +81,7 @@ class SettingsDialog(Gtk.ApplicationWindow):
    81 81
     class SettingsBox(Gtk.ListBox):
    
    82 82
         def __init__(self, account, extend=None):
    
    83 83
             Gtk.ListBox.__init__(self)
    
    84
    -        self.set_name('SettingsBox')
    
    84
    +        self.get_style_context().add_class('settings-box')
    
    85 85
             self.account = account
    
    86 86
             self.named_settings = {}
    
    87 87
     
    
    ... ... @@ -179,7 +179,7 @@ class GenericSetting(Gtk.ListBoxRow):
    179 179
                 description.set_xalign(0)
    
    180 180
                 description.set_line_wrap(True)
    
    181 181
                 description.set_line_wrap_mode(Pango.WrapMode.WORD)
    
    182
    -            description.set_max_width_chars(40)
    
    182
    +            description.set_max_width_chars(50)
    
    183 183
                 description_box.add(description)
    
    184 184
     
    
    185 185
             self._grid.add(description_box)
    
    ... ... @@ -474,10 +474,11 @@ class ActionSetting(GenericSetting):
    474 474
             "setting-value": (str, 'Dummy', '', '',
    
    475 475
                              GObject.ParamFlags.READWRITE),}
    
    476 476
     
    
    477
    -    def __init__(self, *args, action_args):
    
    477
    +    def __init__(self, *args, account):
    
    478 478
             GenericSetting.__init__(self, *args)
    
    479
    -        self.action = gtkgui_helpers.get_action(self.setting_value)
    
    480
    -        self.variant = GLib.Variant.new_string(action_args)
    
    479
    +        action_name = '%s%s' % (account, self.value)
    
    480
    +        self.action = gtkgui_helpers.get_action(action_name)
    
    481
    +        self.variant = GLib.Variant.new_string(account)
    
    481 482
             self.on_enable()
    
    482 483
     
    
    483 484
             self.show_all()