Philipp Hörist pushed to branch gajim_1.2 at gajim / gajim

Commits:

22 changed files:

Changes:

  • data/gajim.doap
    ... ... @@ -46,13 +46,6 @@
    46 46
             <xmpp:version>2.9</xmpp:version>
    
    47 47
           </xmpp:SupportedXep>
    
    48 48
         </implements>
    
    49
    -    <implements>
    
    50
    -      <xmpp:SupportedXep>
    
    51
    -        <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0016.html" />
    
    52
    -        <xmpp:status>complete</xmpp:status>
    
    53
    -        <xmpp:version>1.6</xmpp:version>
    
    54
    -      </xmpp:SupportedXep>
    
    55
    -    </implements>
    
    56 49
         <implements>
    
    57 50
           <xmpp:SupportedXep>
    
    58 51
             <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0027.html" />
    
    ... ... @@ -546,6 +539,14 @@
    546 539
             <xmpp:note>OpenPGP plugin.</xmpp:note>
    
    547 540
           </xmpp:SupportedXep>
    
    548 541
         </implements>
    
    542
    +    <implements>
    
    543
    +      <xmpp:SupportedXep>
    
    544
    +        <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0377.html" />
    
    545
    +        <xmpp:status>partial</xmpp:status>
    
    546
    +        <xmpp:version>0.2</xmpp:version>
    
    547
    +        <xmpp:note>Basic Spam/Abuse reporting via XEP-0191</xmpp:note>
    
    548
    +      </xmpp:SupportedXep>
    
    549
    +    </implements>
    
    549 550
         <implements>
    
    550 551
           <xmpp:SupportedXep>
    
    551 552
             <xmpp:xep rdf:resource="https://xmpp.org/extensions/xep-0380.html" />
    

  • gajim/app_actions.py
    ... ... @@ -26,7 +26,6 @@ from gajim import dialogs
    26 26
     from gajim.gtk.dialogs import ShortcutsWindow
    
    27 27
     from gajim.gtk.single_message import SingleMessageWindow
    
    28 28
     from gajim.gtk.about import AboutDialog
    
    29
    -from gajim.gtk.privacy_list import PrivacyListsWindow
    
    30 29
     from gajim.gtk.history import HistoryWindow
    
    31 30
     from gajim.gtk.discovery import ServiceDiscoveryWindow
    
    32 31
     from gajim.gtk.util import open_window
    
    ... ... @@ -171,15 +170,6 @@ def on_history_sync(_action, param):
    171 170
                     parent=interface.roster.window)
    
    172 171
     
    
    173 172
     
    
    174
    -def on_privacy_lists(_action, param):
    
    175
    -    account = param.get_string()
    
    176
    -    if 'privacy_lists' in interface.instances[account]:
    
    177
    -        interface.instances[account]['privacy_lists'].window.present()
    
    178
    -    else:
    
    179
    -        interface.instances[account]['privacy_lists'] = \
    
    180
    -            PrivacyListsWindow(account)
    
    181
    -
    
    182
    -
    
    183 173
     def on_server_info(_action, param):
    
    184 174
         account = param.get_string()
    
    185 175
         open_window('ServerInfo', account=account)
    

  • gajim/application.py
    ... ... @@ -473,7 +473,6 @@ class GajimApplication(Gtk.Application):
    473 473
                 ('-archive', a.on_mam_preferences, 'feature', 's'),
    
    474 474
                 ('-pep-config', a.on_pep_config, 'online', 's'),
    
    475 475
                 ('-sync-history', a.on_history_sync, 'online', 's'),
    
    476
    -            ('-privacylists', a.on_privacy_lists, 'feature', 's'),
    
    477 476
                 ('-blocking', a.on_blocking_list, 'feature', 's'),
    
    478 477
                 ('-send-server-message', a.on_send_server_message, 'online', 's'),
    
    479 478
                 ('-set-motd', a.on_set_motd, 'online', 's'),
    
    ... ... @@ -571,9 +570,6 @@ class GajimApplication(Gtk.Application):
    571 570
             elif event.feature == Namespace.MAM_2:
    
    572 571
                 action = '%s-archive' % event.account
    
    573 572
                 self.lookup_action(action).set_enabled(True)
    
    574
    -        elif event.feature == Namespace.PRIVACY:
    
    575
    -            action = '%s-privacylists' % event.account
    
    576
    -            self.lookup_action(action).set_enabled(True)
    
    577 573
             elif event.feature == Namespace.BLOCKING:
    
    578 574
                 action = '%s-blocking' % event.account
    
    579 575
                 self.lookup_action(action).set_enabled(True)

  • gajim/chat_control.py
    ... ... @@ -251,8 +251,9 @@ class ChatControl(ChatControlBase):
    251 251
             actions = [
    
    252 252
                 ('invite-contacts-', self._on_invite_contacts),
    
    253 253
                 ('add-to-roster-', self._on_add_to_roster),
    
    254
    +            ('block-contact-', self._on_block_contact),
    
    254 255
                 ('information-', self._on_information),
    
    255
    -            ]
    
    256
    +        ]
    
    256 257
     
    
    257 258
             for action in actions:
    
    258 259
                 action_name, func = action
    
    ... ... @@ -300,6 +301,11 @@ class ChatControl(ChatControlBase):
    300 301
                 win.lookup_action(
    
    301 302
                     'add-to-roster-' + self.control_id).set_enabled(False)
    
    302 303
     
    
    304
    +        # Block contact
    
    305
    +        win.lookup_action(
    
    306
    +            'block-contact-' + self.control_id).set_enabled(
    
    307
    +                online and con.get_module('Blocking').supported)
    
    308
    +
    
    303 309
             # Audio
    
    304 310
             win.lookup_action('toggle-audio-' + self.control_id).set_enabled(
    
    305 311
                 online and self.jingle['audio'].available)
    
    ... ... @@ -376,6 +382,33 @@ class ChatControl(ChatControlBase):
    376 382
         def _on_add_to_roster(self, _action, _param):
    
    377 383
             AddNewContactWindow(self.account, self.contact.jid)
    
    378 384
     
    
    385
    +    def _on_block_contact(self, _action, _param):
    
    386
    +        def _block_contact(report=None):
    
    387
    +            con = app.connections[self.account]
    
    388
    +            con.get_module('Blocking').block([self.contact.jid], report=report)
    
    389
    +
    
    390
    +            self.parent_win.remove_tab(self, None, force=True)
    
    391
    +            if _('Not in contact list') in self.contact.get_shown_groups():
    
    392
    +                app.interface.roster.remove_contact(
    
    393
    +                    self.contact.jid, self.account, force=True, backend=True)
    
    394
    +                return
    
    395
    +            app.interface.roster.draw_contact(self.contact.jid, self.account)
    
    396
    +
    
    397
    +        NewConfirmationDialog(
    
    398
    +            _('Block Contact'),
    
    399
    +            _('Really block this contact?'),
    
    400
    +            _('You will appear offline for this contact and you will '
    
    401
    +              'not receive further messages.'),
    
    402
    +            [DialogButton.make('Cancel'),
    
    403
    +             DialogButton.make('OK',
    
    404
    +                               text=_('_Report Spam'),
    
    405
    +                               callback=_block_contact,
    
    406
    +                               kwargs={'report': 'spam'}),
    
    407
    +             DialogButton.make('Remove',
    
    408
    +                               text=_('_Block'),
    
    409
    +                               callback=_block_contact)],
    
    410
    +            modal=False).show()
    
    411
    +
    
    379 412
         def _on_information(self, _action, _param):
    
    380 413
             app.interface.roster.on_info(None, self.contact, self.account)
    
    381 414
     
    
    ... ... @@ -802,37 +835,15 @@ class ChatControl(ChatControlBase):
    802 835
             name, jid
    
    803 836
             """
    
    804 837
             contact = self.contact
    
    805
    -        jid = contact.jid
    
    806
    -
    
    807 838
             name = contact.get_shown_name()
    
    808 839
             if self.resource:
    
    809 840
                 name += '/' + self.resource
    
    810 841
             if self._type.is_privatechat:
    
    811
    -            name = i18n.direction_mark +  _(
    
    842
    +            name = i18n.direction_mark + _(
    
    812 843
                     '%(nickname)s from group chat %(room_name)s') % \
    
    813 844
                     {'nickname': name, 'room_name': self.room_name}
    
    814 845
             name = i18n.direction_mark + GLib.markup_escape_text(name)
    
    815 846
     
    
    816
    -        # We know our contacts nick, but if another contact has the same nick
    
    817
    -        # in another account we need to also display the account.
    
    818
    -        # except if we are talking to two different resources of the
    
    819
    -        # same contact
    
    820
    -        acct_info = ''
    
    821
    -        for account in app.contacts.get_accounts():
    
    822
    -            if account == self.account:
    
    823
    -                continue
    
    824
    -            if acct_info: # We already found a contact with same nick
    
    825
    -                break
    
    826
    -            for jid in app.contacts.get_jid_list(account):
    
    827
    -                other_contact_ = \
    
    828
    -                    app.contacts.get_first_contact_from_jid(account, jid)
    
    829
    -                if other_contact_.get_shown_name() == \
    
    830
    -                self.contact.get_shown_name():
    
    831
    -                    acct_info = i18n.direction_mark + ' (%s)' % \
    
    832
    -                        GLib.markup_escape_text(
    
    833
    -                            app.get_account_label(self.account))
    
    834
    -                    break
    
    835
    -
    
    836 847
             status = contact.status
    
    837 848
             if status is not None:
    
    838 849
                 status_reduced = helpers.reduce_chars_newlines(status, max_lines=1)
    
    ... ... @@ -849,17 +860,12 @@ class ChatControl(ChatControlBase):
    849 860
             if app.config.get('show_chatstate_in_banner'):
    
    850 861
                 chatstate = helpers.get_uf_chatstate(cs)
    
    851 862
     
    
    852
    -            label_text = '<span>%s</span><span size="x-small" weight="light">%s %s</span>' \
    
    853
    -                % (name, acct_info, chatstate)
    
    854
    -            if acct_info:
    
    855
    -                acct_info = i18n.direction_mark + ' ' + acct_info
    
    856
    -            label_tooltip = '%s%s %s' % (name, acct_info, chatstate)
    
    863
    +            label_text = '<span>%s</span><span size="x-small" weight="light"> %s</span>' % \
    
    864
    +                (name, chatstate)
    
    865
    +            label_tooltip = '%s %s' % (name, chatstate)
    
    857 866
             else:
    
    858
    -            label_text = '<span>%s</span><span size="x-small" weight="light">%s</span>' % \
    
    859
    -                    (name, acct_info)
    
    860
    -            if acct_info:
    
    861
    -                acct_info = i18n.direction_mark + ' ' + acct_info
    
    862
    -            label_tooltip = '%s%s' % (name, acct_info)
    
    867
    +            label_text = '<span>%s</span>' % name
    
    868
    +            label_tooltip = name
    
    863 869
     
    
    864 870
             if status_escaped:
    
    865 871
                 status_text = make_href_markup(status_escaped)
    

  • gajim/chat_control_base.py
    ... ... @@ -1071,9 +1071,9 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper):
    1071 1071
             else:
    
    1072 1072
                 self.received_history_pos = pos
    
    1073 1073
     
    
    1074
    -    def add_info_message(self, text):
    
    1074
    +    def add_info_message(self, text, message_id=None):
    
    1075 1075
             self.conv_textview.print_conversation_line(
    
    1076
    -            text, 'info', '', None, graphics=False)
    
    1076
    +            text, 'info', '', None, message_id=message_id, graphics=False)
    
    1077 1077
     
    
    1078 1078
         def add_status_message(self, text):
    
    1079 1079
             self.conv_textview.print_conversation_line(
    

  • gajim/command_system/implementation/standard.py
    ... ... @@ -308,10 +308,9 @@ class StandardGroupChatCommands(CommandContainer):
    308 308
         @command(raw=True, empty=True)
    
    309 309
         @doc(_("Invite a user to a group chat for a reason"))
    
    310 310
         def invite(self, jid, reason):
    
    311
    -        self.connection.get_module('MUC').invite(self.room_jid, jid, reason)
    
    312
    -        return _("Invited %(jid)s to %(room_jid)s") % {
    
    313
    -            'jid': jid,
    
    314
    -            'room_jid': self.room_jid}
    
    311
    +        control = app.get_groupchat_control(self.account, self.room_jid)
    
    312
    +        if control is not None:
    
    313
    +            control.invite(jid)
    
    315 314
     
    
    316 315
         @command(raw=True, empty=True)
    
    317 316
         @doc(_("Join a group chat given by an XMPP Address"))
    

  • gajim/common/app.py
    ... ... @@ -644,3 +644,12 @@ def get_stored_bob_data(algo_hash: str) -> Optional[bytes]:
    644 644
                     data = file.read()
    
    645 645
                 return data
    
    646 646
         return None
    
    647
    +
    
    648
    +def get_groupchat_control(account, jid):
    
    649
    +    control = app.interface.msg_win_mgr.get_gc_control(jid, account)
    
    650
    +    if control is not None:
    
    651
    +        return control
    
    652
    +    try:
    
    653
    +        return app.interface.minimized_controls[account][jid]
    
    654
    +    except Exception:
    
    655
    +        return None

  • gajim/common/helpers.py
    ... ... @@ -833,14 +833,7 @@ def get_optional_features(account):
    833 833
     
    
    834 834
     def jid_is_blocked(account, jid):
    
    835 835
         con = app.connections[account]
    
    836
    -    return (jid in con.get_module('Blocking').blocked or
    
    837
    -            jid in con.get_module('PrivacyLists').blocked_contacts or
    
    838
    -            con.get_module('PrivacyLists').blocked_all)
    
    839
    -
    
    840
    -def group_is_blocked(account, group):
    
    841
    -    con = app.connections[account]
    
    842
    -    return (group in con.get_module('PrivacyLists').blocked_groups or
    
    843
    -            con.get_module('PrivacyLists').blocked_all)
    
    836
    +    return jid in con.get_module('Blocking').blocked
    
    844 837
     
    
    845 838
     def get_subscription_request_msg(account=None):
    
    846 839
         s = app.config.get_per('accounts', account, 'subscription_request_msg')
    

  • gajim/common/logger.py
    ... ... @@ -526,9 +526,9 @@ class Logger:
    526 526
                 return [user['jid'] for user in family]
    
    527 527
             return [jid]
    
    528 528
     
    
    529
    -    def get_account_id(self, account):
    
    529
    +    def get_account_id(self, account, type_=JIDConstant.NORMAL_TYPE):
    
    530 530
             jid = app.get_jid_from_account(account)
    
    531
    -        return self.get_jid_id(jid, type_=JIDConstant.NORMAL_TYPE)
    
    531
    +        return self.get_jid_id(jid, type_=type_)
    
    532 532
     
    
    533 533
         @timeit
    
    534 534
         def get_jid_id(self, jid, kind=None, type_=None):
    
    ... ... @@ -755,6 +755,26 @@ class Logger:
    755 755
                 all_messages.append((result, shown))
    
    756 756
             return all_messages
    
    757 757
     
    
    758
    +    def load_groupchat_messages(self, account, jid):
    
    759
    +        account_id = self.get_account_id(account, type_=JIDConstant.ROOM_TYPE)
    
    760
    +
    
    761
    +        sql = '''
    
    762
    +            SELECT time, contact_name, message, additional_data, message_id
    
    763
    +            FROM logs NATURAL JOIN jids WHERE jid = ?
    
    764
    +            AND account_id = ? AND kind = ?
    
    765
    +            ORDER BY time DESC, log_line_id DESC LIMIT ?'''
    
    766
    +
    
    767
    +        try:
    
    768
    +            messages = self._con.execute(
    
    769
    +                sql, (jid, account_id, KindConstant.GC_MSG, 50)).fetchall()
    
    770
    +        except sqlite.DatabaseError:
    
    771
    +            self.dispatch('DB_ERROR',
    
    772
    +                          exceptions.DatabaseMalformed(self._log_db_path))
    
    773
    +            return []
    
    774
    +
    
    775
    +        messages.reverse()
    
    776
    +        return messages
    
    777
    +
    
    758 778
         @timeit
    
    759 779
         def get_last_conversation_lines(self, account, jid, pending):
    
    760 780
             """
    

  • gajim/common/modules/__init__.py
    ... ... @@ -56,7 +56,6 @@ MODULES = [
    56 56
         'pep',
    
    57 57
         'ping',
    
    58 58
         'presence',
    
    59
    -    'privacylists',
    
    60 59
         'pubsub',
    
    61 60
         'receipts',
    
    62 61
         'register',
    
    ... ... @@ -99,11 +98,6 @@ class ModuleMock:
    99 98
             # Blocking
    
    100 99
             self.blocked = []  # type: List[Any]
    
    101 100
     
    
    102
    -        # Privacy Lists
    
    103
    -        self.blocked_contacts = []  # type: List[Any]
    
    104
    -        self.blocked_groups = []  # type: List[Any]
    
    105
    -        self.blocked_all = False
    
    106
    -
    
    107 101
             # Delimiter
    
    108 102
             self.delimiter = '::'
    
    109 103
     
    

  • gajim/common/modules/discovery.py
    ... ... @@ -137,7 +137,6 @@ class Discovery(BaseModule):
    137 137
             self._con.get_module('Blocking').pass_disco(result)
    
    138 138
             self._con.get_module('VCardTemp').pass_disco(result)
    
    139 139
             self._con.get_module('Carbons').pass_disco(result)
    
    140
    -        self._con.get_module('PrivacyLists').pass_disco(result)
    
    141 140
             self._con.get_module('HTTPUpload').pass_disco(result)
    
    142 141
     
    
    143 142
             if Namespace.REGISTER in result.features:
    

  • gajim/common/modules/muc.py
    ... ... @@ -777,7 +777,8 @@ class MUC(BaseModule):
    777 777
     
    
    778 778
             password = app.gc_passwords.get(room, None)
    
    779 779
             self._log.info('Invite %s to %s', to, room)
    
    780
    -        self._nbxmpp('MUC').invite(room, to, reason, password, continue_, type_)
    
    780
    +        return self._nbxmpp('MUC').invite(room, to, reason, password,
    
    781
    +                                          continue_, type_)
    
    781 782
     
    
    782 783
         @event_filter(['account'])
    
    783 784
         def _on_account_disconnected(self, _event):
    

  • gajim/common/modules/privacylists.py deleted
    1
    -# This file is part of Gajim.
    
    2
    -#
    
    3
    -# Gajim is free software; you can redistribute it and/or modify
    
    4
    -# it under the terms of the GNU General Public License as published
    
    5
    -# by the Free Software Foundation; version 3 only.
    
    6
    -#
    
    7
    -# Gajim is distributed in the hope that it will be useful,
    
    8
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    9
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    10
    -# GNU General Public License for more details.
    
    11
    -#
    
    12
    -# You should have received a copy of the GNU General Public License
    
    13
    -# along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
    
    14
    -
    
    15
    -# XEP-0016: Privacy Lists
    
    16
    -
    
    17
    -import nbxmpp
    
    18
    -from nbxmpp.namespaces import Namespace
    
    19
    -from nbxmpp.structs import StanzaHandler
    
    20
    -
    
    21
    -from gajim.common import app
    
    22
    -from gajim.common import helpers
    
    23
    -from gajim.common.nec import NetworkEvent
    
    24
    -from gajim.common.nec import NetworkIncomingEvent
    
    25
    -from gajim.common.modules.base import BaseModule
    
    26
    -from gajim.common.connection_handlers_events import InformationEvent
    
    27
    -
    
    28
    -
    
    29
    -class PrivacyLists(BaseModule):
    
    30
    -    def __init__(self, con):
    
    31
    -        BaseModule.__init__(self, con)
    
    32
    -
    
    33
    -        self.default_list = None
    
    34
    -        self.active_list = None
    
    35
    -        self.blocked_contacts = []
    
    36
    -        self.blocked_groups = []
    
    37
    -        self.blocked_list = []
    
    38
    -        self.blocked_all = False
    
    39
    -
    
    40
    -        self.handlers = [
    
    41
    -            StanzaHandler(name='iq',
    
    42
    -                          callback=self._list_push_received,
    
    43
    -                          typ='set',
    
    44
    -                          ns=Namespace.PRIVACY),
    
    45
    -        ]
    
    46
    -
    
    47
    -        self.supported = False
    
    48
    -
    
    49
    -    def pass_disco(self, info):
    
    50
    -        if Namespace.PRIVACY not in info.features:
    
    51
    -            return
    
    52
    -
    
    53
    -        self.supported = True
    
    54
    -        self._log.info('Discovered XEP-0016: Privacy Lists: %s', info.jid)
    
    55
    -
    
    56
    -        app.nec.push_incoming_event(
    
    57
    -            NetworkEvent('feature-discovered',
    
    58
    -                         account=self._account,
    
    59
    -                         feature=Namespace.PRIVACY))
    
    60
    -
    
    61
    -    def _list_push_received(self, _con, stanza, _properties):
    
    62
    -        result = stanza.buildReply('result')
    
    63
    -        result.delChild(result.getTag('query'))
    
    64
    -        self._con.connection.send(result)
    
    65
    -
    
    66
    -        for list_ in stanza.getQueryPayload():
    
    67
    -            if list_.getName() == 'list':
    
    68
    -                name = list_.getAttr('name')
    
    69
    -                self._log.info('Received Push: %s', name)
    
    70
    -                self.get_privacy_list(name)
    
    71
    -
    
    72
    -        raise nbxmpp.NodeProcessed
    
    73
    -
    
    74
    -    def get_privacy_lists(self, callback=None):
    
    75
    -        self._log.info('Request lists')
    
    76
    -        iq = nbxmpp.Iq('get', Namespace.PRIVACY)
    
    77
    -        self._con.connection.SendAndCallForResponse(
    
    78
    -            iq, self._privacy_lists_received, {'callback': callback})
    
    79
    -
    
    80
    -    def _privacy_lists_received(self, _nbxmpp_client, stanza, callback):
    
    81
    -        lists = []
    
    82
    -        new_default = None
    
    83
    -        result = nbxmpp.isResultNode(stanza)
    
    84
    -        if not result:
    
    85
    -            self._log.warning('List not available: %s', stanza.getError())
    
    86
    -        else:
    
    87
    -            for list_ in stanza.getQueryPayload():
    
    88
    -                name = list_.getAttr('name')
    
    89
    -                if list_.getName() == 'active':
    
    90
    -                    self.active_list = name
    
    91
    -                elif list_.getName() == 'default':
    
    92
    -                    new_default = name
    
    93
    -                else:
    
    94
    -                    lists.append(name)
    
    95
    -
    
    96
    -        self._log.info('Received lists: %s', lists)
    
    97
    -
    
    98
    -        # Download default list if we don’t have it
    
    99
    -        if self.default_list != new_default:
    
    100
    -            self.default_list = new_default
    
    101
    -            if new_default is not None:
    
    102
    -                self._log.info('Found new default list: %s', new_default)
    
    103
    -                self.get_privacy_list(new_default)
    
    104
    -
    
    105
    -        if callback:
    
    106
    -            callback(result)
    
    107
    -        else:
    
    108
    -            app.nec.push_incoming_event(
    
    109
    -                PrivacyListsReceivedEvent(None,
    
    110
    -                                          conn=self._con,
    
    111
    -                                          active_list=self.active_list,
    
    112
    -                                          default_list=self.default_list,
    
    113
    -                                          lists=lists))
    
    114
    -
    
    115
    -    def get_privacy_list(self, name):
    
    116
    -        self._log.info('Request list: %s', name)
    
    117
    -        list_ = nbxmpp.Node('list', {'name': name})
    
    118
    -        iq = nbxmpp.Iq('get', Namespace.PRIVACY, payload=[list_])
    
    119
    -        self._con.connection.SendAndCallForResponse(
    
    120
    -            iq, self._privacy_list_received)
    
    121
    -
    
    122
    -    def _privacy_list_received(self, _nbxmpp_client, stanza):
    
    123
    -        if not nbxmpp.isResultNode(stanza):
    
    124
    -            self._log.warning('List not available: %s', stanza.getError())
    
    125
    -            return
    
    126
    -
    
    127
    -        rules = []
    
    128
    -        list_ = stanza.getQueryPayload()[0]
    
    129
    -        name = list_.getAttr('name')
    
    130
    -
    
    131
    -        for child in list_.getChildren():
    
    132
    -
    
    133
    -            item = child.getAttrs()
    
    134
    -
    
    135
    -            children = []
    
    136
    -            for scnd_child in child.getChildren():
    
    137
    -                children.append(scnd_child.getName())
    
    138
    -
    
    139
    -            item['child'] = children
    
    140
    -            if len(item) not in (3, 5):
    
    141
    -                self._log.warning('Wrong count of attrs: %s', stanza)
    
    142
    -                continue
    
    143
    -            rules.append(item)
    
    144
    -
    
    145
    -        self._log.info('Received list: %s', name)
    
    146
    -
    
    147
    -        if name == self.default_list:
    
    148
    -            self._default_list_received(rules)
    
    149
    -
    
    150
    -        app.nec.push_incoming_event(PrivacyListReceivedEvent(
    
    151
    -            None, conn=self._con, list_name=name, rules=rules))
    
    152
    -
    
    153
    -    def del_privacy_list(self, name):
    
    154
    -        self._log.info('Remove list: %s', name)
    
    155
    -
    
    156
    -        def _del_privacy_list_result(_nbxmpp_client, stanza):
    
    157
    -            if not nbxmpp.isResultNode(stanza):
    
    158
    -                self._log.warning('List deletion failed: %s', stanza.getError())
    
    159
    -                app.nec.push_incoming_event(InformationEvent(
    
    160
    -                    None, dialog_name='privacy-list-error', args=name))
    
    161
    -            else:
    
    162
    -                app.nec.push_incoming_event(PrivacyListRemovedEvent(
    
    163
    -                    None, conn=self._con, list_name=name))
    
    164
    -
    
    165
    -        node = nbxmpp.Node('list', {'name': name})
    
    166
    -        iq = nbxmpp.Iq('set', Namespace.PRIVACY, payload=[node])
    
    167
    -        self._con.connection.SendAndCallForResponse(
    
    168
    -            iq, _del_privacy_list_result)
    
    169
    -
    
    170
    -    def set_privacy_list(self, name, rules):
    
    171
    -        node = nbxmpp.Node('list', {'name': name})
    
    172
    -        iq = nbxmpp.Iq('set', Namespace.PRIVACY, payload=[node])
    
    173
    -        for item in rules:
    
    174
    -            children = item.get('child', [])
    
    175
    -            for child in children:
    
    176
    -                node.setTag(child)
    
    177
    -            item.pop('child', None)
    
    178
    -            node.setTag('item', item)
    
    179
    -        self._log.info('Update list: %s %s', name, rules)
    
    180
    -        self._con.connection.SendAndCallForResponse(
    
    181
    -            iq, self._default_result_handler, {})
    
    182
    -
    
    183
    -    def _default_list_received(self, rules):
    
    184
    -        roster = app.interface.roster
    
    185
    -
    
    186
    -        for rule in rules:
    
    187
    -            if rule['action'] == 'allow':
    
    188
    -                if 'type' not in rule:
    
    189
    -                    self.blocked_all = False
    
    190
    -
    
    191
    -                elif rule['type'] == 'jid':
    
    192
    -                    if rule['value'] in self.blocked_contacts:
    
    193
    -                        self.blocked_contacts.remove(rule['value'])
    
    194
    -
    
    195
    -                elif rule['type'] == 'group':
    
    196
    -                    if rule['value'] in self.blocked_groups:
    
    197
    -                        self.blocked_groups.remove(rule['value'])
    
    198
    -
    
    199
    -            elif rule['action'] == 'deny':
    
    200
    -                if 'type' not in rule:
    
    201
    -                    self.blocked_all = True
    
    202
    -
    
    203
    -                elif rule['type'] == 'jid':
    
    204
    -                    if rule['value'] not in self.blocked_contacts:
    
    205
    -                        self.blocked_contacts.append(rule['value'])
    
    206
    -
    
    207
    -                elif rule['type'] == 'group':
    
    208
    -                    if rule['value'] not in self.blocked_groups:
    
    209
    -                        self.blocked_groups.append(rule['value'])
    
    210
    -
    
    211
    -            self.blocked_list.append(rule)
    
    212
    -
    
    213
    -            if 'type' in rule:
    
    214
    -                if rule['type'] == 'jid':
    
    215
    -                    roster.draw_contact(rule['value'], self._account)
    
    216
    -                if rule['type'] == 'group':
    
    217
    -                    roster.draw_group(rule['value'], self._account)
    
    218
    -
    
    219
    -    def set_active_list(self, name=None):
    
    220
    -        self._log.info('Set active list: %s', name)
    
    221
    -        attr = {}
    
    222
    -        if name:
    
    223
    -            attr['name'] = name
    
    224
    -        node = nbxmpp.Node('active', attr)
    
    225
    -        iq = nbxmpp.Iq('set', Namespace.PRIVACY, payload=[node])
    
    226
    -        self._con.connection.SendAndCallForResponse(
    
    227
    -            iq, self._default_result_handler, {})
    
    228
    -
    
    229
    -    def set_default_list(self, name=None):
    
    230
    -        self._log.info('Set default list: %s', name)
    
    231
    -        attr = {}
    
    232
    -        if name:
    
    233
    -            attr['name'] = name
    
    234
    -        node = nbxmpp.Node('default', attr)
    
    235
    -        iq = nbxmpp.Iq('set', Namespace.PRIVACY, payload=[node])
    
    236
    -        self._con.connection.SendAndCallForResponse(
    
    237
    -            iq, self._default_result_handler, {})
    
    238
    -
    
    239
    -    def _default_result_handler(self, _nbxmpp_client, stanza):
    
    240
    -        if not nbxmpp.isResultNode(stanza):
    
    241
    -            self._log.warning('Operation failed: %s', stanza.getError())
    
    242
    -
    
    243
    -    def _get_max_blocked_list_order(self):
    
    244
    -        max_order = 0
    
    245
    -        for rule in self.blocked_list:
    
    246
    -            order = int(rule['order'])
    
    247
    -            if order > max_order:
    
    248
    -                max_order = order
    
    249
    -        return max_order
    
    250
    -
    
    251
    -    def block_gc_contact(self, jid):
    
    252
    -        if jid in self.blocked_contacts:
    
    253
    -            return
    
    254
    -        self._log.info('Block GC contact: %s', jid)
    
    255
    -
    
    256
    -        if self.default_list is None:
    
    257
    -            self.default_list = 'block'
    
    258
    -
    
    259
    -        max_order = self._get_max_blocked_list_order()
    
    260
    -        new_rule = {'order': str(max_order + 1),
    
    261
    -                    'type': 'jid',
    
    262
    -                    'action': 'deny',
    
    263
    -                    'value': jid,
    
    264
    -                    'child': ['message', 'iq', 'presence-out']}
    
    265
    -        self.blocked_list.append(new_rule)
    
    266
    -        self.blocked_contacts.append(jid)
    
    267
    -        self.set_privacy_list(self.default_list, self.blocked_list)
    
    268
    -        if len(self.blocked_list) == 1:
    
    269
    -            self.set_default_list(self.default_list)
    
    270
    -
    
    271
    -    def block_contacts(self, contact_list, _message):
    
    272
    -        if not self.supported:
    
    273
    -            jid_list = [contact.jid for contact in contact_list]
    
    274
    -            self._con.get_module('Blocking').block(jid_list)
    
    275
    -            return
    
    276
    -
    
    277
    -        if self.default_list is None:
    
    278
    -            self.default_list = 'block'
    
    279
    -        for contact in contact_list:
    
    280
    -            self._log.info('Block contacts: %s', contact.jid)
    
    281
    -            contact.show = 'offline'
    
    282
    -            self._con.get_module('Presence').send_presence(contact.jid,
    
    283
    -                                                           typ='unavailable')
    
    284
    -            max_order = self._get_max_blocked_list_order()
    
    285
    -            new_rule = {'order': str(max_order + 1),
    
    286
    -                        'type': 'jid',
    
    287
    -                        'action': 'deny',
    
    288
    -                        'value': contact.jid}
    
    289
    -            self.blocked_list.append(new_rule)
    
    290
    -            self.blocked_contacts.append(contact.jid)
    
    291
    -        self.set_privacy_list(self.default_list, self.blocked_list)
    
    292
    -        if len(self.blocked_list) == 1:
    
    293
    -            self.set_default_list(self.default_list)
    
    294
    -
    
    295
    -    def unblock_gc_contact(self, jid):
    
    296
    -        new_blocked_list = []
    
    297
    -        # needed for draw_contact:
    
    298
    -        if jid not in self.blocked_contacts:
    
    299
    -            return
    
    300
    -
    
    301
    -        self.blocked_contacts.remove(jid)
    
    302
    -
    
    303
    -        self._log.info('Unblock GC contact: %s', jid)
    
    304
    -        for rule in self.blocked_list:
    
    305
    -            if (rule['action'] != 'deny' or
    
    306
    -                    rule['type'] != 'jid' or
    
    307
    -                    rule['value'] != jid):
    
    308
    -                new_blocked_list.append(rule)
    
    309
    -
    
    310
    -        if not new_blocked_list:
    
    311
    -            self.blocked_list = []
    
    312
    -            self.blocked_contacts = []
    
    313
    -            self.blocked_groups = []
    
    314
    -            self.set_default_list(None)
    
    315
    -            self.del_privacy_list(self.default_list)
    
    316
    -        else:
    
    317
    -            self.set_privacy_list(self.default_list, new_blocked_list)
    
    318
    -
    
    319
    -    def unblock_contacts(self, contact_list):
    
    320
    -        if not self.supported:
    
    321
    -            jid_list = [contact.jid for contact in contact_list]
    
    322
    -            self._con.get_module('Blocking').unblock(jid_list)
    
    323
    -            return
    
    324
    -
    
    325
    -        new_blocked_list = []
    
    326
    -        to_unblock = []
    
    327
    -        for contact in contact_list:
    
    328
    -            self._log.info('Unblock contacts: %s', contact.jid)
    
    329
    -            to_unblock.append(contact.jid)
    
    330
    -            if contact.jid in self.blocked_contacts:
    
    331
    -                self.blocked_contacts.remove(contact.jid)
    
    332
    -        for rule in self.blocked_list:
    
    333
    -            if (rule['action'] != 'deny' or
    
    334
    -                    rule['type'] != 'jid' or
    
    335
    -                    rule['value'] not in to_unblock):
    
    336
    -                new_blocked_list.append(rule)
    
    337
    -
    
    338
    -        if not new_blocked_list:
    
    339
    -            self.blocked_list = []
    
    340
    -            self.blocked_contacts = []
    
    341
    -            self.blocked_groups = []
    
    342
    -            self.set_default_list(None)
    
    343
    -            self.del_privacy_list(self.default_list)
    
    344
    -        else:
    
    345
    -            self.set_privacy_list(self.default_list, new_blocked_list)
    
    346
    -        if not app.interface.roster.regroup:
    
    347
    -            show = self._con.status
    
    348
    -        else:   # accounts merged
    
    349
    -            show = helpers.get_global_show()
    
    350
    -
    
    351
    -        for contact in contact_list:
    
    352
    -            self._con.get_module('Presence').send_presence(
    
    353
    -                contact.jid, show=show, status=self._con.status_message)
    
    354
    -            self._presence_probe(contact.jid)
    
    355
    -
    
    356
    -    def block_group(self, group, contact_list, _message):
    
    357
    -        if not self.supported:
    
    358
    -            return
    
    359
    -        if group in self.blocked_groups:
    
    360
    -            return
    
    361
    -        self.blocked_groups.append(group)
    
    362
    -
    
    363
    -        self._log.info('Block group: %s', group)
    
    364
    -
    
    365
    -        if self.default_list is None:
    
    366
    -            self.default_list = 'block'
    
    367
    -
    
    368
    -        for contact in contact_list:
    
    369
    -            self._con.get_module('Presence').send_presence(
    
    370
    -                contact.jid, typ='unavailable')
    
    371
    -
    
    372
    -        max_order = self._get_max_blocked_list_order()
    
    373
    -        new_rule = {'order': str(max_order + 1),
    
    374
    -                    'type': 'group',
    
    375
    -                    'action': 'deny',
    
    376
    -                    'value': group}
    
    377
    -
    
    378
    -        self.blocked_list.append(new_rule)
    
    379
    -        self.set_privacy_list(self.default_list, self.blocked_list)
    
    380
    -        if len(self.blocked_list) == 1:
    
    381
    -            self.set_default_list(self.default_list)
    
    382
    -
    
    383
    -    def unblock_group(self, group, contact_list):
    
    384
    -        if not self.supported:
    
    385
    -            return
    
    386
    -
    
    387
    -        if group not in self.blocked_groups:
    
    388
    -            return
    
    389
    -        self.blocked_groups.remove(group)
    
    390
    -
    
    391
    -        self._log.info('Unblock group: %s', group)
    
    392
    -        new_blocked_list = []
    
    393
    -        for rule in self.blocked_list:
    
    394
    -            if (rule['action'] != 'deny' or
    
    395
    -                    rule['type'] != 'group' or
    
    396
    -                    rule['value'] != group):
    
    397
    -                new_blocked_list.append(rule)
    
    398
    -
    
    399
    -        if not new_blocked_list:
    
    400
    -            self.blocked_list = []
    
    401
    -            self.blocked_contacts = []
    
    402
    -            self.blocked_groups = []
    
    403
    -            self.set_default_list('')
    
    404
    -            self.del_privacy_list(self.default_list)
    
    405
    -        else:
    
    406
    -            self.set_privacy_list(self.default_list, new_blocked_list)
    
    407
    -        if not app.interface.roster.regroup:
    
    408
    -            show = self._con.status
    
    409
    -        else:   # accounts merged
    
    410
    -            show = helpers.get_global_show()
    
    411
    -
    
    412
    -        for contact in contact_list:
    
    413
    -            self._con.get_module('Presence').send_presence(
    
    414
    -                contact.jid, show=show, status=self._con.status_message)
    
    415
    -
    
    416
    -    def _presence_probe(self, jid):
    
    417
    -        self._log.info('Presence probe: %s', jid)
    
    418
    -        # Send a presence Probe to get the current Status
    
    419
    -        probe = nbxmpp.Presence(jid, 'probe', frm=self._con.get_own_jid())
    
    420
    -        self._con.connection.send(probe)
    
    421
    -
    
    422
    -
    
    423
    -class PrivacyListsReceivedEvent(NetworkIncomingEvent):
    
    424
    -    name = 'privacy-lists-received'
    
    425
    -
    
    426
    -
    
    427
    -class PrivacyListReceivedEvent(NetworkIncomingEvent):
    
    428
    -    name = 'privacy-list-received'
    
    429
    -
    
    430
    -
    
    431
    -class PrivacyListRemovedEvent(NetworkIncomingEvent):
    
    432
    -    name = 'privacy-list-removed'
    
    433
    -
    
    434
    -
    
    435
    -def get_instance(*args, **kwargs):
    
    436
    -    return PrivacyLists(*args, **kwargs), 'PrivacyLists'

  • gajim/data/gui/preferences_window.ui
    ... ... @@ -2181,312 +2181,422 @@ $T will be replaced by auto-not-available timeout.</property>
    2181 2181
                   </packing>
    
    2182 2182
                 </child>
    
    2183 2183
                 <child>
    
    2184
    -              <object class="GtkBox" id="audio_video_tab">
    
    2184
    +              <object class="GtkScrolledWindow">
    
    2185 2185
                     <property name="visible">True</property>
    
    2186
    -                <property name="can_focus">False</property>
    
    2187
    -                <property name="border_width">18</property>
    
    2188
    -                <property name="orientation">vertical</property>
    
    2189
    -                <property name="spacing">6</property>
    
    2186
    +                <property name="can_focus">True</property>
    
    2190 2187
                     <child>
    
    2191
    -                  <object class="GtkGrid">
    
    2188
    +                  <object class="GtkViewport">
    
    2192 2189
                         <property name="visible">True</property>
    
    2193 2190
                         <property name="can_focus">False</property>
    
    2194
    -                    <property name="halign">start</property>
    
    2195
    -                    <property name="row_spacing">6</property>
    
    2196
    -                    <property name="column_spacing">12</property>
    
    2197
    -                    <child>
    
    2198
    -                      <object class="GtkLabel">
    
    2199
    -                        <property name="width_request">-1</property>
    
    2200
    -                        <property name="visible">True</property>
    
    2201
    -                        <property name="can_focus">False</property>
    
    2202
    -                        <property name="halign">end</property>
    
    2203
    -                        <property name="label" translatable="yes">A_udio output device</property>
    
    2204
    -                        <property name="use_underline">True</property>
    
    2205
    -                        <property name="mnemonic_widget">audio_output_combobox</property>
    
    2206
    -                        <style>
    
    2207
    -                          <class name="dim-label"/>
    
    2208
    -                        </style>
    
    2209
    -                      </object>
    
    2210
    -                      <packing>
    
    2211
    -                        <property name="left_attach">0</property>
    
    2212
    -                        <property name="top_attach">2</property>
    
    2213
    -                      </packing>
    
    2214
    -                    </child>
    
    2215
    -                    <child>
    
    2216
    -                      <object class="GtkComboBox" id="audio_output_combobox">
    
    2217
    -                        <property name="visible">True</property>
    
    2218
    -                        <property name="can_focus">False</property>
    
    2219
    -                        <signal name="changed" handler="on_audio_output_combobox_changed" swapped="no"/>
    
    2220
    -                      </object>
    
    2221
    -                      <packing>
    
    2222
    -                        <property name="left_attach">1</property>
    
    2223
    -                        <property name="top_attach">2</property>
    
    2224
    -                      </packing>
    
    2225
    -                    </child>
    
    2226
    -                    <child>
    
    2227
    -                      <object class="GtkComboBox" id="audio_input_combobox">
    
    2228
    -                        <property name="width_request">200</property>
    
    2229
    -                        <property name="visible">True</property>
    
    2230
    -                        <property name="can_focus">False</property>
    
    2231
    -                        <signal name="changed" handler="on_audio_input_combobox_changed" swapped="no"/>
    
    2232
    -                      </object>
    
    2233
    -                      <packing>
    
    2234
    -                        <property name="left_attach">1</property>
    
    2235
    -                        <property name="top_attach">1</property>
    
    2236
    -                      </packing>
    
    2237
    -                    </child>
    
    2238
    -                    <child>
    
    2239
    -                      <object class="GtkLabel">
    
    2240
    -                        <property name="width_request">-1</property>
    
    2241
    -                        <property name="visible">True</property>
    
    2242
    -                        <property name="can_focus">False</property>
    
    2243
    -                        <property name="halign">end</property>
    
    2244
    -                        <property name="label" translatable="yes">_Audio input device</property>
    
    2245
    -                        <property name="use_underline">True</property>
    
    2246
    -                        <property name="mnemonic_widget">audio_input_combobox</property>
    
    2247
    -                        <style>
    
    2248
    -                          <class name="dim-label"/>
    
    2249
    -                        </style>
    
    2250
    -                      </object>
    
    2251
    -                      <packing>
    
    2252
    -                        <property name="left_attach">0</property>
    
    2253
    -                        <property name="top_attach">1</property>
    
    2254
    -                      </packing>
    
    2255
    -                    </child>
    
    2256
    -                    <child>
    
    2257
    -                      <object class="GtkLabel">
    
    2258
    -                        <property name="visible">True</property>
    
    2259
    -                        <property name="can_focus">False</property>
    
    2260
    -                        <property name="halign">start</property>
    
    2261
    -                        <property name="label" translatable="yes">Audio</property>
    
    2262
    -                        <property name="use_markup">True</property>
    
    2263
    -                        <style>
    
    2264
    -                          <class name="bold"/>
    
    2265
    -                          <class name="margin-top6"/>
    
    2266
    -                        </style>
    
    2267
    -                      </object>
    
    2268
    -                      <packing>
    
    2269
    -                        <property name="left_attach">0</property>
    
    2270
    -                        <property name="top_attach">0</property>
    
    2271
    -                      </packing>
    
    2272
    -                    </child>
    
    2273
    -                    <child>
    
    2274
    -                      <object class="GtkLabel">
    
    2275
    -                        <property name="visible">True</property>
    
    2276
    -                        <property name="can_focus">False</property>
    
    2277
    -                        <property name="halign">start</property>
    
    2278
    -                        <property name="label" translatable="yes">Video</property>
    
    2279
    -                        <property name="use_markup">True</property>
    
    2280
    -                        <style>
    
    2281
    -                          <class name="bold"/>
    
    2282
    -                          <class name="margin-top6"/>
    
    2283
    -                        </style>
    
    2284
    -                      </object>
    
    2285
    -                      <packing>
    
    2286
    -                        <property name="left_attach">0</property>
    
    2287
    -                        <property name="top_attach">3</property>
    
    2288
    -                      </packing>
    
    2289
    -                    </child>
    
    2290 2191
                         <child>
    
    2291
    -                      <object class="GtkLabel">
    
    2292
    -                        <property name="visible">True</property>
    
    2293
    -                        <property name="can_focus">False</property>
    
    2294
    -                        <property name="halign">end</property>
    
    2295
    -                        <property name="label" translatable="yes">_Video input device</property>
    
    2296
    -                        <property name="use_underline">True</property>
    
    2297
    -                        <property name="mnemonic_widget">video_input_combobox</property>
    
    2298
    -                        <style>
    
    2299
    -                          <class name="dim-label"/>
    
    2300
    -                        </style>
    
    2301
    -                      </object>
    
    2302
    -                      <packing>
    
    2303
    -                        <property name="left_attach">0</property>
    
    2304
    -                        <property name="top_attach">4</property>
    
    2305
    -                      </packing>
    
    2306
    -                    </child>
    
    2307
    -                    <child>
    
    2308
    -                      <object class="GtkComboBox" id="video_input_combobox">
    
    2309
    -                        <property name="visible">True</property>
    
    2310
    -                        <property name="can_focus">False</property>
    
    2311
    -                        <signal name="changed" handler="on_video_input_combobox_changed" swapped="no"/>
    
    2312
    -                      </object>
    
    2313
    -                      <packing>
    
    2314
    -                        <property name="left_attach">1</property>
    
    2315
    -                        <property name="top_attach">4</property>
    
    2316
    -                      </packing>
    
    2317
    -                    </child>
    
    2318
    -                    <child>
    
    2319
    -                      <object class="GtkLabel">
    
    2320
    -                        <property name="visible">True</property>
    
    2321
    -                        <property name="can_focus">False</property>
    
    2322
    -                        <property name="halign">end</property>
    
    2323
    -                        <property name="label" translatable="yes">Video output</property>
    
    2324
    -                        <property name="mnemonic_widget">selected_video_output</property>
    
    2325
    -                        <style>
    
    2326
    -                          <class name="dim-label"/>
    
    2327
    -                        </style>
    
    2328
    -                      </object>
    
    2329
    -                      <packing>
    
    2330
    -                        <property name="left_attach">0</property>
    
    2331
    -                        <property name="top_attach">5</property>
    
    2332
    -                      </packing>
    
    2333
    -                    </child>
    
    2334
    -                    <child>
    
    2335
    -                      <object class="GtkLabel" id="selected_video_output">
    
    2336
    -                        <property name="visible">True</property>
    
    2337
    -                        <property name="can_focus">False</property>
    
    2338
    -                        <property name="halign">start</property>
    
    2339
    -                      </object>
    
    2340
    -                      <packing>
    
    2341
    -                        <property name="left_attach">1</property>
    
    2342
    -                        <property name="top_attach">5</property>
    
    2343
    -                      </packing>
    
    2344
    -                    </child>
    
    2345
    -                    <child>
    
    2346
    -                      <object class="GtkLabel">
    
    2347
    -                        <property name="visible">True</property>
    
    2348
    -                        <property name="can_focus">False</property>
    
    2349
    -                        <property name="halign">end</property>
    
    2350
    -                        <property name="margin_top">12</property>
    
    2351
    -                        <property name="label" translatable="yes">Video _framerate</property>
    
    2352
    -                        <property name="use_underline">True</property>
    
    2353
    -                        <property name="mnemonic_widget">video_framerate_combobox</property>
    
    2354
    -                        <style>
    
    2355
    -                          <class name="dim-label"/>
    
    2356
    -                        </style>
    
    2357
    -                      </object>
    
    2358
    -                      <packing>
    
    2359
    -                        <property name="left_attach">0</property>
    
    2360
    -                        <property name="top_attach">6</property>
    
    2361
    -                      </packing>
    
    2362
    -                    </child>
    
    2363
    -                    <child>
    
    2364
    -                      <object class="GtkComboBox" id="video_framerate_combobox">
    
    2365
    -                        <property name="visible">True</property>
    
    2366
    -                        <property name="can_focus">False</property>
    
    2367
    -                        <property name="margin_top">12</property>
    
    2368
    -                        <signal name="changed" handler="on_video_framerate_combobox_changed" swapped="no"/>
    
    2369
    -                      </object>
    
    2370
    -                      <packing>
    
    2371
    -                        <property name="left_attach">1</property>
    
    2372
    -                        <property name="top_attach">6</property>
    
    2373
    -                      </packing>
    
    2374
    -                    </child>
    
    2375
    -                    <child>
    
    2376
    -                      <object class="GtkLabel">
    
    2377
    -                        <property name="visible">True</property>
    
    2378
    -                        <property name="can_focus">False</property>
    
    2379
    -                        <property name="halign">end</property>
    
    2380
    -                        <property name="label" translatable="yes">Video si_ze</property>
    
    2381
    -                        <property name="use_underline">True</property>
    
    2382
    -                        <property name="mnemonic_widget">video_size_combobox</property>
    
    2383
    -                        <style>
    
    2384
    -                          <class name="dim-label"/>
    
    2385
    -                        </style>
    
    2386
    -                      </object>
    
    2387
    -                      <packing>
    
    2388
    -                        <property name="left_attach">0</property>
    
    2389
    -                        <property name="top_attach">7</property>
    
    2390
    -                      </packing>
    
    2391
    -                    </child>
    
    2392
    -                    <child>
    
    2393
    -                      <object class="GtkComboBox" id="video_size_combobox">
    
    2394
    -                        <property name="visible">True</property>
    
    2395
    -                        <property name="can_focus">False</property>
    
    2396
    -                        <signal name="changed" handler="on_video_size_combobox_changed" swapped="no"/>
    
    2397
    -                      </object>
    
    2398
    -                      <packing>
    
    2399
    -                        <property name="left_attach">1</property>
    
    2400
    -                        <property name="top_attach">7</property>
    
    2401
    -                      </packing>
    
    2402
    -                    </child>
    
    2403
    -                    <child>
    
    2404
    -                      <object class="GtkCheckButton" id="video_see_self_checkbutton">
    
    2405
    -                        <property name="label" translatable="yes">Vi_ew own video source</property>
    
    2406
    -                        <property name="visible">True</property>
    
    2407
    -                        <property name="can_focus">True</property>
    
    2408
    -                        <property name="receives_default">False</property>
    
    2409
    -                        <property name="halign">start</property>
    
    2410
    -                        <property name="use_underline">True</property>
    
    2411
    -                        <property name="draw_indicator">True</property>
    
    2412
    -                        <signal name="toggled" handler="on_video_see_self_checkbutton_toggled" swapped="no"/>
    
    2413
    -                      </object>
    
    2414
    -                      <packing>
    
    2415
    -                        <property name="left_attach">1</property>
    
    2416
    -                        <property name="top_attach">8</property>
    
    2417
    -                      </packing>
    
    2418
    -                    </child>
    
    2419
    -                    <child>
    
    2420
    -                      <object class="GtkLabel">
    
    2192
    +                      <object class="GtkBox">
    
    2421 2193
                             <property name="visible">True</property>
    
    2422 2194
                             <property name="can_focus">False</property>
    
    2423
    -                        <property name="halign">start</property>
    
    2424
    -                        <property name="label" translatable="yes">Connection</property>
    
    2425
    -                        <property name="use_markup">True</property>
    
    2426
    -                        <style>
    
    2427
    -                          <class name="bold"/>
    
    2428
    -                          <class name="margin-top6"/>
    
    2429
    -                        </style>
    
    2430
    -                      </object>
    
    2431
    -                      <packing>
    
    2432
    -                        <property name="left_attach">0</property>
    
    2433
    -                        <property name="top_attach">9</property>
    
    2434
    -                      </packing>
    
    2435
    -                    </child>
    
    2436
    -                    <child>
    
    2437
    -                      <object class="GtkCheckButton" id="stun_checkbutton">
    
    2438
    -                        <property name="label" translatable="yes">STU_N server</property>
    
    2439
    -                        <property name="visible">True</property>
    
    2440
    -                        <property name="can_focus">True</property>
    
    2441
    -                        <property name="receives_default">False</property>
    
    2442
    -                        <property name="halign">end</property>
    
    2443
    -                        <property name="use_underline">True</property>
    
    2444
    -                        <property name="draw_indicator">True</property>
    
    2445
    -                        <signal name="toggled" handler="on_stun_checkbutton_toggled" swapped="no"/>
    
    2446
    -                      </object>
    
    2447
    -                      <packing>
    
    2448
    -                        <property name="left_attach">0</property>
    
    2449
    -                        <property name="top_attach">10</property>
    
    2450
    -                      </packing>
    
    2451
    -                    </child>
    
    2452
    -                    <child>
    
    2453
    -                      <object class="GtkEntry" id="stun_server_entry">
    
    2454
    -                        <property name="visible">True</property>
    
    2455
    -                        <property name="can_focus">True</property>
    
    2456
    -                        <property name="tooltip_text" translatable="yes">STUN server hostname. If no hostname was given, Gajim will try
    
    2195
    +                        <property name="orientation">vertical</property>
    
    2196
    +                        <child>
    
    2197
    +                          <object class="GtkGrid">
    
    2198
    +                            <property name="visible">True</property>
    
    2199
    +                            <property name="can_focus">False</property>
    
    2200
    +                            <property name="hexpand">True</property>
    
    2201
    +                            <property name="border_width">18</property>
    
    2202
    +                            <property name="row_spacing">6</property>
    
    2203
    +                            <property name="column_spacing">12</property>
    
    2204
    +                            <child>
    
    2205
    +                              <object class="GtkLabel">
    
    2206
    +                                <property name="width_request">-1</property>
    
    2207
    +                                <property name="visible">True</property>
    
    2208
    +                                <property name="can_focus">False</property>
    
    2209
    +                                <property name="halign">end</property>
    
    2210
    +                                <property name="label" translatable="yes">A_udio output device</property>
    
    2211
    +                                <property name="use_underline">True</property>
    
    2212
    +                                <property name="mnemonic_widget">audio_output_combobox</property>
    
    2213
    +                                <style>
    
    2214
    +                                  <class name="dim-label"/>
    
    2215
    +                                </style>
    
    2216
    +                              </object>
    
    2217
    +                              <packing>
    
    2218
    +                                <property name="left_attach">0</property>
    
    2219
    +                                <property name="top_attach">2</property>
    
    2220
    +                              </packing>
    
    2221
    +                            </child>
    
    2222
    +                            <child>
    
    2223
    +                              <object class="GtkComboBox" id="audio_output_combobox">
    
    2224
    +                                <property name="visible">True</property>
    
    2225
    +                                <property name="can_focus">False</property>
    
    2226
    +                                <signal name="changed" handler="on_audio_output_combobox_changed" swapped="no"/>
    
    2227
    +                              </object>
    
    2228
    +                              <packing>
    
    2229
    +                                <property name="left_attach">1</property>
    
    2230
    +                                <property name="top_attach">2</property>
    
    2231
    +                              </packing>
    
    2232
    +                            </child>
    
    2233
    +                            <child>
    
    2234
    +                              <object class="GtkComboBox" id="audio_input_combobox">
    
    2235
    +                                <property name="width_request">200</property>
    
    2236
    +                                <property name="visible">True</property>
    
    2237
    +                                <property name="can_focus">False</property>
    
    2238
    +                                <signal name="changed" handler="on_audio_input_combobox_changed" swapped="no"/>
    
    2239
    +                              </object>
    
    2240
    +                              <packing>
    
    2241
    +                                <property name="left_attach">1</property>
    
    2242
    +                                <property name="top_attach">1</property>
    
    2243
    +                              </packing>
    
    2244
    +                            </child>
    
    2245
    +                            <child>
    
    2246
    +                              <object class="GtkLabel">
    
    2247
    +                                <property name="width_request">-1</property>
    
    2248
    +                                <property name="visible">True</property>
    
    2249
    +                                <property name="can_focus">False</property>
    
    2250
    +                                <property name="halign">end</property>
    
    2251
    +                                <property name="label" translatable="yes">_Audio input device</property>
    
    2252
    +                                <property name="use_underline">True</property>
    
    2253
    +                                <property name="mnemonic_widget">audio_input_combobox</property>
    
    2254
    +                                <style>
    
    2255
    +                                  <class name="dim-label"/>
    
    2256
    +                                </style>
    
    2257
    +                              </object>
    
    2258
    +                              <packing>
    
    2259
    +                                <property name="left_attach">0</property>
    
    2260
    +                                <property name="top_attach">1</property>
    
    2261
    +                              </packing>
    
    2262
    +                            </child>
    
    2263
    +                            <child>
    
    2264
    +                              <object class="GtkLabel">
    
    2265
    +                                <property name="visible">True</property>
    
    2266
    +                                <property name="can_focus">False</property>
    
    2267
    +                                <property name="halign">start</property>
    
    2268
    +                                <property name="label" translatable="yes">Audio</property>
    
    2269
    +                                <property name="use_markup">True</property>
    
    2270
    +                                <style>
    
    2271
    +                                  <class name="bold"/>
    
    2272
    +                                  <class name="margin-top6"/>
    
    2273
    +                                </style>
    
    2274
    +                              </object>
    
    2275
    +                              <packing>
    
    2276
    +                                <property name="left_attach">0</property>
    
    2277
    +                                <property name="top_attach">0</property>
    
    2278
    +                              </packing>
    
    2279
    +                            </child>
    
    2280
    +                            <child>
    
    2281
    +                              <object class="GtkLabel">
    
    2282
    +                                <property name="visible">True</property>
    
    2283
    +                                <property name="can_focus">False</property>
    
    2284
    +                                <property name="halign">start</property>
    
    2285
    +                                <property name="label" translatable="yes">Video</property>
    
    2286
    +                                <property name="use_markup">True</property>
    
    2287
    +                                <style>
    
    2288
    +                                  <class name="bold"/>
    
    2289
    +                                  <class name="margin-top6"/>
    
    2290
    +                                </style>
    
    2291
    +                              </object>
    
    2292
    +                              <packing>
    
    2293
    +                                <property name="left_attach">0</property>
    
    2294
    +                                <property name="top_attach">3</property>
    
    2295
    +                              </packing>
    
    2296
    +                            </child>
    
    2297
    +                            <child>
    
    2298
    +                              <object class="GtkLabel">
    
    2299
    +                                <property name="visible">True</property>
    
    2300
    +                                <property name="can_focus">False</property>
    
    2301
    +                                <property name="halign">end</property>
    
    2302
    +                                <property name="label" translatable="yes">_Video input device</property>
    
    2303
    +                                <property name="use_underline">True</property>
    
    2304
    +                                <property name="mnemonic_widget">video_input_combobox</property>
    
    2305
    +                                <style>
    
    2306
    +                                  <class name="dim-label"/>
    
    2307
    +                                </style>
    
    2308
    +                              </object>
    
    2309
    +                              <packing>
    
    2310
    +                                <property name="left_attach">0</property>
    
    2311
    +                                <property name="top_attach">4</property>
    
    2312
    +                              </packing>
    
    2313
    +                            </child>
    
    2314
    +                            <child>
    
    2315
    +                              <object class="GtkComboBox" id="video_input_combobox">
    
    2316
    +                                <property name="visible">True</property>
    
    2317
    +                                <property name="can_focus">False</property>
    
    2318
    +                                <signal name="changed" handler="on_video_input_combobox_changed" swapped="no"/>
    
    2319
    +                              </object>
    
    2320
    +                              <packing>
    
    2321
    +                                <property name="left_attach">1</property>
    
    2322
    +                                <property name="top_attach">4</property>
    
    2323
    +                              </packing>
    
    2324
    +                            </child>
    
    2325
    +                            <child>
    
    2326
    +                              <object class="GtkCheckButton" id="stun_checkbutton">
    
    2327
    +                                <property name="label" translatable="yes">STU_N server</property>
    
    2328
    +                                <property name="visible">True</property>
    
    2329
    +                                <property name="can_focus">True</property>
    
    2330
    +                                <property name="receives_default">False</property>
    
    2331
    +                                <property name="halign">end</property>
    
    2332
    +                                <property name="use_underline">True</property>
    
    2333
    +                                <property name="draw_indicator">True</property>
    
    2334
    +                                <signal name="toggled" handler="on_stun_checkbutton_toggled" swapped="no"/>
    
    2335
    +                              </object>
    
    2336
    +                              <packing>
    
    2337
    +                                <property name="left_attach">0</property>
    
    2338
    +                                <property name="top_attach">11</property>
    
    2339
    +                              </packing>
    
    2340
    +                            </child>
    
    2341
    +                            <child>
    
    2342
    +                              <object class="GtkEntry" id="stun_server_entry">
    
    2343
    +                                <property name="visible">True</property>
    
    2344
    +                                <property name="can_focus">True</property>
    
    2345
    +                                <property name="tooltip_text" translatable="yes">STUN server hostname. If no hostname was given, Gajim will try
    
    2457 2346
     to discover one from the server. (Example: stun.iptel.org)</property>
    
    2458
    -                        <property name="invisible_char">●</property>
    
    2459
    -                        <signal name="changed" handler="stun_server_entry_changed" swapped="no"/>
    
    2347
    +                                <property name="invisible_char">●</property>
    
    2348
    +                                <signal name="changed" handler="stun_server_entry_changed" swapped="no"/>
    
    2349
    +                              </object>
    
    2350
    +                              <packing>
    
    2351
    +                                <property name="left_attach">1</property>
    
    2352
    +                                <property name="top_attach">11</property>
    
    2353
    +                              </packing>
    
    2354
    +                            </child>
    
    2355
    +                            <child>
    
    2356
    +                              <object class="GtkLabel">
    
    2357
    +                                <property name="visible">True</property>
    
    2358
    +                                <property name="can_focus">False</property>
    
    2359
    +                                <property name="halign">start</property>
    
    2360
    +                                <property name="label" translatable="yes">Connection</property>
    
    2361
    +                                <property name="use_markup">True</property>
    
    2362
    +                                <style>
    
    2363
    +                                  <class name="bold"/>
    
    2364
    +                                  <class name="margin-top6"/>
    
    2365
    +                                </style>
    
    2366
    +                              </object>
    
    2367
    +                              <packing>
    
    2368
    +                                <property name="left_attach">0</property>
    
    2369
    +                                <property name="top_attach">10</property>
    
    2370
    +                              </packing>
    
    2371
    +                            </child>
    
    2372
    +                            <child>
    
    2373
    +                              <object class="GtkCheckButton" id="video_see_self_checkbutton">
    
    2374
    +                                <property name="label" translatable="yes">Vi_ew own video source</property>
    
    2375
    +                                <property name="visible">True</property>
    
    2376
    +                                <property name="can_focus">True</property>
    
    2377
    +                                <property name="receives_default">False</property>
    
    2378
    +                                <property name="halign">start</property>
    
    2379
    +                                <property name="use_underline">True</property>
    
    2380
    +                                <property name="draw_indicator">True</property>
    
    2381
    +                                <signal name="toggled" handler="on_video_see_self_checkbutton_toggled" swapped="no"/>
    
    2382
    +                              </object>
    
    2383
    +                              <packing>
    
    2384
    +                                <property name="left_attach">1</property>
    
    2385
    +                                <property name="top_attach">9</property>
    
    2386
    +                              </packing>
    
    2387
    +                            </child>
    
    2388
    +                            <child>
    
    2389
    +                              <object class="GtkComboBox" id="video_size_combobox">
    
    2390
    +                                <property name="visible">True</property>
    
    2391
    +                                <property name="can_focus">False</property>
    
    2392
    +                                <signal name="changed" handler="on_video_size_combobox_changed" swapped="no"/>
    
    2393
    +                              </object>
    
    2394
    +                              <packing>
    
    2395
    +                                <property name="left_attach">1</property>
    
    2396
    +                                <property name="top_attach">8</property>
    
    2397
    +                              </packing>
    
    2398
    +                            </child>
    
    2399
    +                            <child>
    
    2400
    +                              <object class="GtkLabel">
    
    2401
    +                                <property name="visible">True</property>
    
    2402
    +                                <property name="can_focus">False</property>
    
    2403
    +                                <property name="halign">end</property>
    
    2404
    +                                <property name="label" translatable="yes">Video si_ze</property>
    
    2405
    +                                <property name="use_underline">True</property>
    
    2406
    +                                <style>
    
    2407
    +                                  <class name="dim-label"/>
    
    2408
    +                                </style>
    
    2409
    +                              </object>
    
    2410
    +                              <packing>
    
    2411
    +                                <property name="left_attach">0</property>
    
    2412
    +                                <property name="top_attach">8</property>
    
    2413
    +                              </packing>
    
    2414
    +                            </child>
    
    2415
    +                            <child>
    
    2416
    +                              <object class="GtkLabel">
    
    2417
    +                                <property name="visible">True</property>
    
    2418
    +                                <property name="can_focus">False</property>
    
    2419
    +                                <property name="halign">end</property>
    
    2420
    +                                <property name="margin_top">12</property>
    
    2421
    +                                <property name="label" translatable="yes">Video _framerate</property>
    
    2422
    +                                <property name="use_underline">True</property>
    
    2423
    +                                <style>
    
    2424
    +                                  <class name="dim-label"/>
    
    2425
    +                                </style>
    
    2426
    +                              </object>
    
    2427
    +                              <packing>
    
    2428
    +                                <property name="left_attach">0</property>
    
    2429
    +                                <property name="top_attach">7</property>
    
    2430
    +                              </packing>
    
    2431
    +                            </child>
    
    2432
    +                            <child>
    
    2433
    +                              <object class="GtkComboBox" id="video_framerate_combobox">
    
    2434
    +                                <property name="visible">True</property>
    
    2435
    +                                <property name="can_focus">False</property>
    
    2436
    +                                <property name="margin_top">12</property>
    
    2437
    +                                <signal name="changed" handler="on_video_framerate_combobox_changed" swapped="no"/>
    
    2438
    +                              </object>
    
    2439
    +                              <packing>
    
    2440
    +                                <property name="left_attach">1</property>
    
    2441
    +                                <property name="top_attach">7</property>
    
    2442
    +                              </packing>
    
    2443
    +                            </child>
    
    2444
    +                            <child>
    
    2445
    +                              <object class="GtkLabel">
    
    2446
    +                                <property name="visible">True</property>
    
    2447
    +                                <property name="can_focus">False</property>
    
    2448
    +                                <property name="halign">end</property>
    
    2449
    +                                <property name="label" translatable="yes">Video output</property>
    
    2450
    +                                <style>
    
    2451
    +                                  <class name="dim-label"/>
    
    2452
    +                                </style>
    
    2453
    +                              </object>
    
    2454
    +                              <packing>
    
    2455
    +                                <property name="left_attach">0</property>
    
    2456
    +                                <property name="top_attach">6</property>
    
    2457
    +                              </packing>
    
    2458
    +                            </child>
    
    2459
    +                            <child>
    
    2460
    +                              <object class="GtkLabel" id="selected_video_output">
    
    2461
    +                                <property name="visible">True</property>
    
    2462
    +                                <property name="can_focus">False</property>
    
    2463
    +                                <property name="halign">start</property>
    
    2464
    +                              </object>
    
    2465
    +                              <packing>
    
    2466
    +                                <property name="left_attach">1</property>
    
    2467
    +                                <property name="top_attach">6</property>
    
    2468
    +                              </packing>
    
    2469
    +                            </child>
    
    2470
    +                            <child>
    
    2471
    +                              <object class="GtkCheckButton" id="live_preview_checkbutton">
    
    2472
    +                                <property name="label" translatable="yes">Show live _preview</property>
    
    2473
    +                                <property name="visible">True</property>
    
    2474
    +                                <property name="can_focus">True</property>
    
    2475
    +                                <property name="receives_default">False</property>
    
    2476
    +                                <property name="halign">start</property>
    
    2477
    +                                <property name="use_underline">True</property>
    
    2478
    +                                <property name="draw_indicator">True</property>
    
    2479
    +                                <signal name="toggled" handler="_on_live_preview_toggled" swapped="no"/>
    
    2480
    +                              </object>
    
    2481
    +                              <packing>
    
    2482
    +                                <property name="left_attach">1</property>
    
    2483
    +                                <property name="top_attach">5</property>
    
    2484
    +                              </packing>
    
    2485
    +                            </child>
    
    2486
    +                            <child>
    
    2487
    +                              <placeholder/>
    
    2488
    +                            </child>
    
    2489
    +                            <child>
    
    2490
    +                              <placeholder/>
    
    2491
    +                            </child>
    
    2492
    +                            <child>
    
    2493
    +                              <placeholder/>
    
    2494
    +                            </child>
    
    2495
    +                            <child>
    
    2496
    +                              <placeholder/>
    
    2497
    +                            </child>
    
    2498
    +                            <child>
    
    2499
    +                              <placeholder/>
    
    2500
    +                            </child>
    
    2501
    +                          </object>
    
    2502
    +                          <packing>
    
    2503
    +                            <property name="expand">False</property>
    
    2504
    +                            <property name="fill">True</property>
    
    2505
    +                            <property name="position">0</property>
    
    2506
    +                          </packing>
    
    2507
    +                        </child>
    
    2508
    +                        <child>
    
    2509
    +                          <object class="GtkBox">
    
    2510
    +                            <property name="visible">True</property>
    
    2511
    +                            <property name="can_focus">False</property>
    
    2512
    +                            <property name="orientation">vertical</property>
    
    2513
    +                            <child>
    
    2514
    +                              <object class="GtkSeparator">
    
    2515
    +                                <property name="visible">True</property>
    
    2516
    +                                <property name="can_focus">False</property>
    
    2517
    +                                <property name="margin_top">12</property>
    
    2518
    +                              </object>
    
    2519
    +                              <packing>
    
    2520
    +                                <property name="expand">False</property>
    
    2521
    +                                <property name="fill">True</property>
    
    2522
    +                                <property name="position">0</property>
    
    2523
    +                              </packing>
    
    2524
    +                            </child>
    
    2525
    +                            <child>
    
    2526
    +                              <object class="GtkBox" id="av_preview_box">
    
    2527
    +                                <property name="height_request">250</property>
    
    2528
    +                                <property name="visible">True</property>
    
    2529
    +                                <property name="can_focus">False</property>
    
    2530
    +                                <property name="orientation">vertical</property>
    
    2531
    +                                <child>
    
    2532
    +                                  <object class="GtkBox" id="av_preview_placeholder">
    
    2533
    +                                    <property name="visible">True</property>
    
    2534
    +                                    <property name="can_focus">False</property>
    
    2535
    +                                    <property name="orientation">vertical</property>
    
    2536
    +                                    <child>
    
    2537
    +                                      <object class="GtkImage">
    
    2538
    +                                        <property name="visible">True</property>
    
    2539
    +                                        <property name="can_focus">False</property>
    
    2540
    +                                        <property name="margin_top">12</property>
    
    2541
    +                                        <property name="icon_name">camera-web-symbolic</property>
    
    2542
    +                                        <property name="icon_size">6</property>
    
    2543
    +                                        <style>
    
    2544
    +                                          <class name="dim-label"/>
    
    2545
    +                                        </style>
    
    2546
    +                                      </object>
    
    2547
    +                                      <packing>
    
    2548
    +                                        <property name="expand">False</property>
    
    2549
    +                                        <property name="fill">True</property>
    
    2550
    +                                        <property name="position">0</property>
    
    2551
    +                                      </packing>
    
    2552
    +                                    </child>
    
    2553
    +                                    <child>
    
    2554
    +                                      <object class="GtkLabel">
    
    2555
    +                                        <property name="visible">True</property>
    
    2556
    +                                        <property name="can_focus">False</property>
    
    2557
    +                                        <property name="label" translatable="yes">Live Preview</property>
    
    2558
    +                                        <property name="justify">center</property>
    
    2559
    +                                        <style>
    
    2560
    +                                          <class name="bold"/>
    
    2561
    +                                          <class name="dim-label"/>
    
    2562
    +                                        </style>
    
    2563
    +                                      </object>
    
    2564
    +                                      <packing>
    
    2565
    +                                        <property name="expand">False</property>
    
    2566
    +                                        <property name="fill">True</property>
    
    2567
    +                                        <property name="position">1</property>
    
    2568
    +                                      </packing>
    
    2569
    +                                    </child>
    
    2570
    +                                  </object>
    
    2571
    +                                  <packing>
    
    2572
    +                                    <property name="expand">True</property>
    
    2573
    +                                    <property name="fill">True</property>
    
    2574
    +                                    <property name="position">0</property>
    
    2575
    +                                  </packing>
    
    2576
    +                                </child>
    
    2577
    +                              </object>
    
    2578
    +                              <packing>
    
    2579
    +                                <property name="expand">False</property>
    
    2580
    +                                <property name="fill">True</property>
    
    2581
    +                                <property name="position">1</property>
    
    2582
    +                              </packing>
    
    2583
    +                            </child>
    
    2584
    +                          </object>
    
    2585
    +                          <packing>
    
    2586
    +                            <property name="expand">False</property>
    
    2587
    +                            <property name="fill">True</property>
    
    2588
    +                            <property name="position">1</property>
    
    2589
    +                          </packing>
    
    2590
    +                        </child>
    
    2460 2591
                           </object>
    
    2461
    -                      <packing>
    
    2462
    -                        <property name="left_attach">1</property>
    
    2463
    -                        <property name="top_attach">10</property>
    
    2464
    -                      </packing>
    
    2465
    -                    </child>
    
    2466
    -                    <child>
    
    2467
    -                      <placeholder/>
    
    2468
    -                    </child>
    
    2469
    -                    <child>
    
    2470
    -                      <placeholder/>
    
    2471
    -                    </child>
    
    2472
    -                    <child>
    
    2473
    -                      <placeholder/>
    
    2474
    -                    </child>
    
    2475
    -                    <child>
    
    2476
    -                      <placeholder/>
    
    2477 2592
                         </child>
    
    2478 2593
                       </object>
    
    2479
    -                  <packing>
    
    2480
    -                    <property name="expand">False</property>
    
    2481
    -                    <property name="fill">True</property>
    
    2482
    -                    <property name="position">0</property>
    
    2483
    -                  </packing>
    
    2484 2594
                     </child>
    
    2485 2595
                   </object>
    
    2486 2596
                   <packing>
    
    2487
    -                <property name="expand">False</property>
    
    2597
    +                <property name="expand">True</property>
    
    2488 2598
                     <property name="fill">True</property>
    
    2489
    -                <property name="position">1</property>
    
    2599
    +                <property name="position">2</property>
    
    2490 2600
                   </packing>
    
    2491 2601
                 </child>
    
    2492 2602
               </object>
    

  • gajim/dialog_messages.py
    ... ... @@ -71,18 +71,6 @@ messages = {
    71 71
             _('Custom hostname "%s" is wrong. It will be ignored.'),
    
    72 72
             ErrorDialog),
    
    73 73
     
    
    74
    -    'privacy-list-error': Message(
    
    75
    -        _('Error While Removing Privacy List'),
    
    76
    -        _('Privacy list %s has not been removed. '
    
    77
    -          'It is probably active in one of your connected resources. '
    
    78
    -          'Please deactivate it and try again.'),
    
    79
    -        ErrorDialog),
    
    80
    -
    
    81
    -    'invisibility-not-supported': Message(
    
    82
    -        _('Invisibility Not Supported'),
    
    83
    -        _('This account %s doesn\'t support invisibility.'),
    
    84
    -        ErrorDialog),
    
    85
    -
    
    86 74
         'agent-register-success': Message(
    
    87 75
             _('Registration Succeeded'),
    
    88 76
             _('Registration with agent %s succeeded.'),
    

  • gajim/groupchat_control.py
    ... ... @@ -218,6 +218,7 @@ class GroupchatControl(ChatControlBase):
    218 218
                 ('signed-in', ged.GUI1, self._on_signed_in),
    
    219 219
                 ('decrypted-message-received', ged.GUI2, self._on_decrypted_message_received),
    
    220 220
                 ('message-sent', ged.OUT_POSTCORE, self._on_message_sent),
    
    221
    +            ('message-error', ged.GUI1, self._on_message_error),
    
    221 222
                 ('bookmarks-received', ged.GUI2, self._on_bookmarks_received),
    
    222 223
             ])
    
    223 224
             # pylint: enable=line-too-long
    
    ... ... @@ -241,6 +242,7 @@ class GroupchatControl(ChatControlBase):
    241 242
             # PluginSystem: adding GUI extension point for this GroupchatControl
    
    242 243
             # instance object
    
    243 244
             app.plugin_manager.gui_extension_point('groupchat_control', self)
    
    245
    +        self._restore_conversation()
    
    244 246
     
    
    245 247
         @property
    
    246 248
         def nick(self):
    
    ... ... @@ -275,8 +277,6 @@ class GroupchatControl(ChatControlBase):
    275 277
                 ('invite-', None, self._on_invite),
    
    276 278
                 ('contact-information-', 's', self._on_contact_information),
    
    277 279
                 ('execute-command-', 's', self._on_execute_command),
    
    278
    -            ('block-', 's', self._on_block),
    
    279
    -            ('unblock-', 's', self._on_unblock),
    
    280 280
                 ('ban-', 's', self._on_ban),
    
    281 281
                 ('kick-', 's', self._on_kick),
    
    282 282
                 ('change-role-', 'as', self._on_change_role),
    
    ... ... @@ -440,17 +440,29 @@ class GroupchatControl(ChatControlBase):
    440 440
     
    
    441 441
             self._get_action('execute-command-').set_enabled(self.is_connected)
    
    442 442
     
    
    443
    -        block_supported = con.get_module('PrivacyLists').supported
    
    444
    -        self._get_action('block-').set_enabled(self.is_connected and
    
    445
    -                                               block_supported)
    
    446
    -
    
    447
    -        self._get_action('unblock-').set_enabled(self.is_connected and
    
    448
    -                                                 block_supported)
    
    449
    -
    
    450 443
             self._get_action('ban-').set_enabled(self.is_connected)
    
    451 444
     
    
    452 445
             self._get_action('kick-').set_enabled(self.is_connected)
    
    453 446
     
    
    447
    +    def _restore_conversation(self):
    
    448
    +        rows = app.logger.load_groupchat_messages(
    
    449
    +            self.account, self.contact.jid)
    
    450
    +
    
    451
    +        for row in rows:
    
    452
    +            other_tags_for_name = ['muc_nickname_color_%s' % row.contact_name]
    
    453
    +            ChatControlBase.add_message(self,
    
    454
    +                                        row.message,
    
    455
    +                                        'incoming',
    
    456
    +                                        row.contact_name,
    
    457
    +                                        float(row.time),
    
    458
    +                                        other_tags_for_name=other_tags_for_name,
    
    459
    +                                        message_id=row.message_id,
    
    460
    +                                        restored=True,
    
    461
    +                                        additional_data=row.additional_data)
    
    462
    +
    
    463
    +        if rows:
    
    464
    +            self.conv_textview.print_empty_line()
    
    465
    +
    
    454 466
         def _is_subject_change_allowed(self):
    
    455 467
             contact = app.contacts.get_gc_contact(
    
    456 468
                 self.account, self.room_jid, self.nick)
    
    ... ... @@ -521,11 +533,17 @@ class GroupchatControl(ChatControlBase):
    521 533
     
    
    522 534
         def _on_invite_clicked(self, _button):
    
    523 535
             invitees = self._invite_box.get_invitees()
    
    524
    -        con = app.connections[self.account]
    
    525 536
             for jid in invitees:
    
    526
    -            con.get_module('MUC').invite(self.room_jid, jid)
    
    537
    +            self.invite(jid)
    
    527 538
             self._show_page('groupchat')
    
    528 539
     
    
    540
    +    def invite(self, contact_jid):
    
    541
    +        con = app.connections[self.account]
    
    542
    +        message_id = con.get_module('MUC').invite(self.room_jid, contact_jid)
    
    543
    +        self.add_info_message(
    
    544
    +            _(f'{contact_jid} has been invited to this group chat'),
    
    545
    +            message_id=message_id)
    
    546
    +
    
    529 547
         def _on_destroy_room(self, _action, _param):
    
    530 548
             self.xml.destroy_reason_entry.grab_focus()
    
    531 549
             self.xml.destroy_button.grab_default()
    
    ... ... @@ -661,20 +679,6 @@ class GroupchatControl(ChatControlBase):
    661 679
                 app.interface.instances[self.account]['infos'][contact.jid] = \
    
    662 680
                     vcard.VcardWindow(contact, self.account, gc_contact)
    
    663 681
     
    
    664
    -    def _on_block(self, _action, param):
    
    665
    -        nick = param.get_string()
    
    666
    -        fjid = self.room_jid + '/' + nick
    
    667
    -        con = app.connections[self.account]
    
    668
    -        con.get_module('PrivacyLists').block_gc_contact(fjid)
    
    669
    -        self.roster.draw_contact(nick)
    
    670
    -
    
    671
    -    def _on_unblock(self, _action, param):
    
    672
    -        nick = param.get_string()
    
    673
    -        fjid = self.room_jid + '/' + nick
    
    674
    -        con = app.connections[self.account]
    
    675
    -        con.get_module('PrivacyLists').unblock_gc_contact(fjid)
    
    676
    -        self.roster.draw_contact(nick)
    
    677
    -
    
    678 682
         def _on_kick(self, _action, param):
    
    679 683
             nick = param.get_string()
    
    680 684
             self._kick_nick = nick
    
    ... ... @@ -1780,10 +1784,7 @@ class GroupchatControl(ChatControlBase):
    1780 1784
                     return
    
    1781 1785
                 contact_jid = data
    
    1782 1786
     
    
    1783
    -            con = app.connections[self.account]
    
    1784
    -            con.get_module('MUC').invite(self.room_jid, contact_jid)
    
    1785
    -            self.add_info_message(_('%(jid)s has been invited to this '
    
    1786
    -                                    'group chat') % {'jid': contact_jid})
    
    1787
    +            self.invite(contact_jid)
    
    1787 1788
     
    
    1788 1789
         def _jid_not_blocked(self, bare_jid: str) -> bool:
    
    1789 1790
             fjid = self.room_jid + '/' + bare_jid
    

  • gajim/gtk/const.py
    ... ... @@ -101,7 +101,6 @@ WINDOW_MODULES = {
    101 101
         'StartChatDialog': 'gajim.gtk.start_chat',
    
    102 102
         'AddNewContactWindow': 'gajim.gtk.add_contact',
    
    103 103
         'SingleMessageWindow': 'gajim.gtk.single_message',
    
    104
    -    'PrivacyListsWindow': 'gajim.gtk.privacy_list',
    
    105 104
         'Bookmarks': 'gajim.gtk.bookmarks',
    
    106 105
         'AccountWizard': 'gajim.gtk.account_wizard',
    
    107 106
         'HistoryWindow': 'gajim.gtk.history',
    

  • gajim/gtk/preferences.py
    ... ... @@ -369,9 +369,12 @@ class Preferences(Gtk.ApplicationWindow):
    369 369
                 self._ui.av_dependencies_infobar.set_no_show_all(True)
    
    370 370
                 self._ui.av_dependencies_infobar.hide()
    
    371 371
     
    
    372
    -            create_av_combobox('audio_input', AudioInputManager().get_devices())
    
    373
    -            create_av_combobox('audio_output', AudioOutputManager().get_devices())
    
    374
    -            create_av_combobox('video_input', VideoInputManager().get_devices())
    
    372
    +            create_av_combobox(
    
    373
    +                'audio_input', AudioInputManager().get_devices())
    
    374
    +            create_av_combobox(
    
    375
    +                'audio_output', AudioOutputManager().get_devices())
    
    376
    +            create_av_combobox(
    
    377
    +                'video_input', VideoInputManager().get_devices())
    
    375 378
     
    
    376 379
                 create_av_combobox(
    
    377 380
                     'video_framerate',
    
    ... ... @@ -393,99 +396,22 @@ class Preferences(Gtk.ApplicationWindow):
    393 396
                 st = app.config.get('video_see_self')
    
    394 397
                 self._ui.video_see_self_checkbutton.set_active(st)
    
    395 398
     
    
    396
    -            def on_av_map(tab):
    
    397
    -                label = self._ui.selected_video_output
    
    398
    -                sink, widget, name = gstreamer.create_gtk_widget()
    
    399
    -                if sink is None:
    
    400
    -                    log.error('Failed to obtain a working Gstreamer GTK+ sink, '
    
    401
    -                              'video support will be disabled')
    
    402
    -                    self._ui.video_input_combobox.set_sensitive(False)
    
    403
    -                    label.set_markup(
    
    404
    -                        _('<span color="red" font-weight="bold">'
    
    405
    -                          'Unavailable</span>, video support will be disabled'))
    
    406
    -                    return
    
    407
    -
    
    408
    -                text = ''
    
    409
    -                if name == 'gtkglsink':
    
    410
    -                    text = _('<span color="green" font-weight="bold">'
    
    411
    -                             'OpenGL</span> accelerated')
    
    412
    -                elif name == 'gtksink':
    
    413
    -                    text = _('<span color="yellow" font-weight="bold">'
    
    414
    -                             'Unaccelerated</span>')
    
    415
    -                label.set_markup(text)
    
    416
    -                if self.av_pipeline is None:
    
    417
    -                    self.av_pipeline = Gst.Pipeline.new('preferences-pipeline')
    
    418
    -                else:
    
    419
    -                    self.av_pipeline.set_state(Gst.State.NULL)
    
    420
    -                self.av_pipeline.add(sink)
    
    421
    -                self.av_sink = sink
    
    422
    -                if self.av_widget is not None:
    
    423
    -                    tab.remove(self.av_widget)
    
    424
    -                tab.add(widget)
    
    425
    -                self.av_widget = widget
    
    426
    -
    
    427
    -                src_name = app.config.get('video_input_device')
    
    428
    -                try:
    
    429
    -                    self.av_src = Gst.parse_bin_from_description(src_name, True)
    
    430
    -                except GLib.Error:
    
    431
    -                    log.error('Failed to parse "%s" as Gstreamer element,'
    
    432
    -                              ' falling back to autovideosrc', src_name)
    
    433
    -                    self.av_src = None
    
    434
    -                if self.av_src is not None:
    
    435
    -                    self.av_pipeline.add(self.av_src)
    
    436
    -                    self.av_src.link(self.av_sink)
    
    437
    -                    self.av_pipeline.set_state(Gst.State.PLAYING)
    
    438
    -                else:
    
    439
    -                    # Parsing the pipeline stored in video_input_device failed,
    
    440
    -                    # let’s try the default one.
    
    441
    -                    self.av_src = Gst.ElementFactory.make('autovideosrc', None)
    
    442
    -                    if self.av_src is None:
    
    443
    -                        log.error('Failed to obtain a working Gstreamer source,'
    
    444
    -                                  ' video will be disabled.')
    
    445
    -                        self._ui.video_input_combobox.set_sensitive(False)
    
    446
    -                        return
    
    447
    -                    # Great, this succeeded, let’s store it back into the
    
    448
    -                    # config and use it. We’ve made autovideosrc the first
    
    449
    -                    # element in the combobox so we can pick index 0 without
    
    450
    -                    # worry.
    
    451
    -                    combobox = self._ui.video_input_combobox
    
    452
    -                    combobox.set_active(0)
    
    453
    -
    
    454
    -            def on_av_unmap(tab):
    
    455
    -                if self.av_pipeline is not None:
    
    456
    -                    self.av_pipeline.set_state(Gst.State.NULL)
    
    457
    -                if self.av_src is not None:
    
    458
    -                    self.av_pipeline.remove(self.av_src)
    
    459
    -                    self.av_src = None
    
    460
    -                if self.av_sink is not None:
    
    461
    -                    self.av_pipeline.remove(self.av_sink)
    
    462
    -                    self.av_sink = None
    
    463
    -                if self.av_widget is not None:
    
    464
    -                    tab.remove(self.av_widget)
    
    465
    -                    self.av_widget = None
    
    466
    -                self.av_pipeline = None
    
    467
    -
    
    468 399
                 self.av_pipeline = None
    
    469 400
                 self.av_src = None
    
    470 401
                 self.av_sink = None
    
    471 402
                 self.av_widget = None
    
    472
    -            tab = self._ui.audio_video_tab
    
    473
    -            tab.connect('map', on_av_map)
    
    474
    -            tab.connect('unmap', on_av_unmap)
    
    475
    -
    
    476 403
             else:
    
    477 404
                 for opt_name in ('audio_input', 'audio_output', 'video_input',
    
    478 405
                                  'video_framerate', 'video_size'):
    
    479 406
                     combobox = self._ui.get_object(opt_name + '_combobox')
    
    480 407
                     combobox.set_sensitive(False)
    
    408
    +            self._ui.live_preview_checkbutton.set_sensitive(False)
    
    481 409
     
    
    482 410
             # STUN
    
    483 411
             st = app.config.get('use_stun_server')
    
    484 412
             self._ui.stun_checkbutton.set_active(st)
    
    485
    -
    
    413
    +        self._ui.stun_server_entry.set_sensitive(st)
    
    486 414
             self._ui.stun_server_entry.set_text(app.config.get('stun_server'))
    
    487
    -        if not st:
    
    488
    -            self._ui.stun_server_entry.set_sensitive(False)
    
    489 415
     
    
    490 416
             ### Advanced tab ###
    
    491 417
     
    
    ... ... @@ -994,15 +920,89 @@ class Preferences(Gtk.ApplicationWindow):
    994 920
                 widget.set_active(0)
    
    995 921
                 return
    
    996 922
     
    
    997
    -        self.av_pipeline.set_state(Gst.State.NULL)
    
    998
    -        if self.av_src is not None:
    
    999
    -            self.av_pipeline.remove(self.av_src)
    
    1000
    -        self.av_pipeline.add(src)
    
    1001
    -        src.link(self.av_sink)
    
    1002
    -        self.av_src = src
    
    1003
    -        self.av_pipeline.set_state(Gst.State.PLAYING)
    
    923
    +        if self._ui.live_preview_checkbutton.get_active():
    
    924
    +            self.av_pipeline.set_state(Gst.State.NULL)
    
    925
    +            if self.av_src is not None:
    
    926
    +                self.av_pipeline.remove(self.av_src)
    
    927
    +            self.av_pipeline.add(src)
    
    928
    +            src.link(self.av_sink)
    
    929
    +            self.av_src = src
    
    930
    +            self.av_pipeline.set_state(Gst.State.PLAYING)
    
    1004 931
             app.config.set('video_input_device', device)
    
    1005 932
     
    
    933
    +    def _on_live_preview_toggled(self, widget):
    
    934
    +        if widget.get_active():
    
    935
    +            sink, widget, name = gstreamer.create_gtk_widget()
    
    936
    +            if sink is None:
    
    937
    +                log.error('Failed to obtain a working Gstreamer GTK+ sink, '
    
    938
    +                          'video support will be disabled')
    
    939
    +                self._ui.video_input_combobox.set_sensitive(False)
    
    940
    +                self._ui.selected_video_output.set_markup(
    
    941
    +                    _('<span color="red" font-weight="bold">'
    
    942
    +                      'Unavailable</span>, video support will be disabled'))
    
    943
    +                return
    
    944
    +
    
    945
    +            text = ''
    
    946
    +            if name == 'gtkglsink':
    
    947
    +                text = _('<span color="green" font-weight="bold">'
    
    948
    +                         'OpenGL</span> accelerated')
    
    949
    +            elif name == 'gtksink':
    
    950
    +                text = _('<span color="yellow" font-weight="bold">'
    
    951
    +                         'Unaccelerated</span>')
    
    952
    +            self._ui.selected_video_output.set_markup(text)
    
    953
    +            if self.av_pipeline is None:
    
    954
    +                self.av_pipeline = Gst.Pipeline.new('preferences-pipeline')
    
    955
    +            else:
    
    956
    +                self.av_pipeline.set_state(Gst.State.NULL)
    
    957
    +            self.av_pipeline.add(sink)
    
    958
    +            self.av_sink = sink
    
    959
    +
    
    960
    +            if self.av_widget is not None:
    
    961
    +                self._ui.av_preview_box.remove(self.av_widget)
    
    962
    +            self._ui.av_preview_placeholder.set_visible(False)
    
    963
    +            self._ui.av_preview_box.add(widget)
    
    964
    +            self.av_widget = widget
    
    965
    +
    
    966
    +            src_name = app.config.get('video_input_device')
    
    967
    +            try:
    
    968
    +                self.av_src = Gst.parse_bin_from_description(src_name, True)
    
    969
    +            except GLib.Error:
    
    970
    +                log.error('Failed to parse "%s" as Gstreamer element, '
    
    971
    +                          'falling back to autovideosrc', src_name)
    
    972
    +                self.av_src = None
    
    973
    +            if self.av_src is not None:
    
    974
    +                self.av_pipeline.add(self.av_src)
    
    975
    +                self.av_src.link(self.av_sink)
    
    976
    +                self.av_pipeline.set_state(Gst.State.PLAYING)
    
    977
    +            else:
    
    978
    +                # Parsing the pipeline stored in video_input_device failed,
    
    979
    +                # let’s try the default one.
    
    980
    +                self.av_src = Gst.ElementFactory.make('autovideosrc', None)
    
    981
    +                if self.av_src is None:
    
    982
    +                    log.error('Failed to obtain a working Gstreamer source, '
    
    983
    +                              'video will be disabled.')
    
    984
    +                    self._ui.video_input_combobox.set_sensitive(False)
    
    985
    +                    return
    
    986
    +                # Great, this succeeded, let’s store it back into the
    
    987
    +                # config and use it. We’ve made autovideosrc the first
    
    988
    +                # element in the combobox so we can pick index 0 without
    
    989
    +                # worry.
    
    990
    +                self._ui.video_input_combobox.set_active(0)
    
    991
    +        else:
    
    992
    +            if self.av_pipeline is not None:
    
    993
    +                self.av_pipeline.set_state(Gst.State.NULL)
    
    994
    +            if self.av_src is not None:
    
    995
    +                self.av_pipeline.remove(self.av_src)
    
    996
    +                self.av_src = None
    
    997
    +            if self.av_sink is not None:
    
    998
    +                self.av_pipeline.remove(self.av_sink)
    
    999
    +                self.av_sink = None
    
    1000
    +            if self.av_widget is not None:
    
    1001
    +                self._ui.av_preview_box.remove(self.av_widget)
    
    1002
    +                self._ui.av_preview_placeholder.set_visible(True)
    
    1003
    +                self.av_widget = None
    
    1004
    +            self.av_pipeline = None
    
    1005
    +
    
    1006 1006
         def on_video_framerate_combobox_changed(self, widget):
    
    1007 1007
             self.on_av_combobox_changed(widget, 'video_framerate')
    
    1008 1008
     
    

  • gajim/gtk/privacy_list.py deleted
    1
    -# This file is part of Gajim.
    
    2
    -#
    
    3
    -# Gajim is free software; you can redistribute it and/or modify
    
    4
    -# it under the terms of the GNU General Public License as published
    
    5
    -# by the Free Software Foundation; version 3 only.
    
    6
    -#
    
    7
    -# Gajim is distributed in the hope that it will be useful,
    
    8
    -# but WITHOUT ANY WARRANTY; without even the implied warranty of
    
    9
    -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
    
    10
    -# GNU General Public License for more details.
    
    11
    -#
    
    12
    -# You should have received a copy of the GNU General Public License
    
    13
    -# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
    
    14
    -
    
    15
    -from gi.repository import Gdk
    
    16
    -from gi.repository import Gtk
    
    17
    -from gi.repository import GLib
    
    18
    -from gi.repository import GObject
    
    19
    -
    
    20
    -from gajim.common import app
    
    21
    -from gajim.common import ged
    
    22
    -from gajim.common.i18n import _
    
    23
    -from gajim.common.nec import EventHelper
    
    24
    -
    
    25
    -from gajim.gtk.dialogs import ErrorDialog
    
    26
    -from gajim.gtk.util import get_builder
    
    27
    -
    
    28
    -
    
    29
    -class PrivacyListWindow(EventHelper):
    
    30
    -    def __init__(self, account, privacy_list_name, action):
    
    31
    -        # action is 'EDIT' or 'NEW' depending on if we create a new priv list
    
    32
    -        # or edit an already existing one
    
    33
    -        EventHelper.__init__(self)
    
    34
    -        self.account = account
    
    35
    -        self.privacy_list_name = privacy_list_name
    
    36
    -
    
    37
    -        # Dicts and Default Values
    
    38
    -        self.active_rule = ''
    
    39
    -        self.global_rules = {}
    
    40
    -        self.list_of_groups = {}
    
    41
    -
    
    42
    -        self.max_order = 0
    
    43
    -
    
    44
    -        # Default Edit Values
    
    45
    -        self.edit_rule_type = 'jid'
    
    46
    -        self.allow_deny = 'allow'
    
    47
    -
    
    48
    -        # Connect to gtk builder
    
    49
    -        self._ui = get_builder('privacy_list_window.ui')
    
    50
    -        self.window = self._ui.get_object('privacy_list_edit_window')
    
    51
    -
    
    52
    -        self._ui.privacy_lists_title_label.set_label(
    
    53
    -            _('Privacy List <b><i>%s</i></b>') %
    
    54
    -            GLib.markup_escape_text(self.privacy_list_name))
    
    55
    -
    
    56
    -        if len(app.connections) > 1:
    
    57
    -            title = _('Privacy List for %s') % self.account
    
    58
    -        else:
    
    59
    -            title = _('Privacy List')
    
    60
    -
    
    61
    -        self._ui.delete_rule_button.set_sensitive(False)
    
    62
    -        self._ui.open_rule_button.set_sensitive(False)
    
    63
    -        self._ui.privacy_list_active_checkbutton.set_sensitive(False)
    
    64
    -        self._ui.privacy_list_default_checkbutton.set_sensitive(False)
    
    65
    -        self._ui.list_of_rules_combobox.set_sensitive(False)
    
    66
    -
    
    67
    -        # set jabber id completion
    
    68
    -        jids_list_store = Gtk.ListStore(GObject.TYPE_STRING)
    
    69
    -        for jid in app.contacts.get_jid_list(self.account):
    
    70
    -            jids_list_store.append([jid])
    
    71
    -        jid_entry_completion = Gtk.EntryCompletion()
    
    72
    -        jid_entry_completion.set_text_column(0)
    
    73
    -        jid_entry_completion.set_model(jids_list_store)
    
    74
    -        jid_entry_completion.set_popup_completion(True)
    
    75
    -        self._ui.edit_type_jabberid_entry.set_completion(jid_entry_completion)
    
    76
    -        if action == 'EDIT':
    
    77
    -            self.refresh_rules()
    
    78
    -
    
    79
    -        model = self._ui.edit_type_group_combobox.get_model()
    
    80
    -        count = 0
    
    81
    -        for group in app.groups[self.account]:
    
    82
    -            self.list_of_groups[group] = count
    
    83
    -            count += 1
    
    84
    -            model.append([group])
    
    85
    -        self._ui.edit_type_group_combobox.set_active(0)
    
    86
    -
    
    87
    -        self.window.set_title(title)
    
    88
    -
    
    89
    -        # pylint: disable=line-too-long
    
    90
    -        self.register_events([
    
    91
    -            ('privacy-list-received', ged.GUI1, self._nec_privacy_list_received),
    
    92
    -            ('privacy-lists-received', ged.GUI1, self._nec_privacy_lists_received)
    
    93
    -        ])
    
    94
    -        # pylint: enable=line-too-long
    
    95
    -
    
    96
    -        self.window.show_all()
    
    97
    -        self._ui.add_edit_vbox.hide()
    
    98
    -
    
    99
    -        self._ui.connect_signals(self)
    
    100
    -
    
    101
    -    def on_key_press_event(self, _widget, event):
    
    102
    -        if event.keyval == Gdk.KEY_Escape:
    
    103
    -            self.window.destroy()
    
    104
    -
    
    105
    -    def _on_edit_window_destroy(self, _widget):
    
    106
    -        key_name = 'privacy_list_%s' % self.privacy_list_name
    
    107
    -        if key_name in app.interface.instances[self.account]:
    
    108
    -            del app.interface.instances[self.account][key_name]
    
    109
    -        self.unregister_events()
    
    110
    -
    
    111
    -    def _nec_privacy_lists_received(self, obj):
    
    112
    -        if obj.conn.name != self.account:
    
    113
    -            return
    
    114
    -        if obj.active_list == self.privacy_list_name:
    
    115
    -            self._ui.privacy_list_active_checkbutton.set_active(True)
    
    116
    -        else:
    
    117
    -            self._ui.privacy_list_active_checkbutton.set_active(False)
    
    118
    -        if obj.default_list == self.privacy_list_name:
    
    119
    -            self._ui.privacy_list_default_checkbutton.set_active(True)
    
    120
    -        else:
    
    121
    -            self._ui.privacy_list_default_checkbutton.set_active(False)
    
    122
    -
    
    123
    -    def privacy_list_received(self, rules):
    
    124
    -        model = self._ui.list_of_rules_combobox.get_model()
    
    125
    -        model.clear()
    
    126
    -        self.global_rules = {}
    
    127
    -        for rule in rules:
    
    128
    -            if 'type' in rule:
    
    129
    -                text_item = _(
    
    130
    -                    'Order: %(order)s, action: %(action)s, type: %(type)s'
    
    131
    -                    ', value: %(value)s') % {
    
    132
    -                        'order': rule['order'],
    
    133
    -                        'action': rule['action'],
    
    134
    -                        'type': rule['type'],
    
    135
    -                        'value': rule['value']}
    
    136
    -            else:
    
    137
    -                text_item = _('Order: %(order)s, action: %(action)s') % \
    
    138
    -                        {'order': rule['order'], 'action': rule['action']}
    
    139
    -            if int(rule['order']) > self.max_order:
    
    140
    -                self.max_order = int(rule['order'])
    
    141
    -            self.global_rules[text_item] = rule
    
    142
    -            model.append([text_item])
    
    143
    -        if not rules:
    
    144
    -            self._ui.title_hbox.set_sensitive(False)
    
    145
    -            self._ui.list_of_rules_combobox.set_sensitive(False)
    
    146
    -            self._ui.delete_rule_button.set_sensitive(False)
    
    147
    -            self._ui.open_rule_button.set_sensitive(False)
    
    148
    -            self._ui.privacy_list_active_checkbutton.set_sensitive(False)
    
    149
    -            self._ui.privacy_list_default_checkbutton.set_sensitive(False)
    
    150
    -        else:
    
    151
    -            self._ui.list_of_rules_combobox.set_active(0)
    
    152
    -            self._ui.title_hbox.set_sensitive(True)
    
    153
    -            self._ui.list_of_rules_combobox.set_sensitive(True)
    
    154
    -            self._ui.delete_rule_button.set_sensitive(True)
    
    155
    -            self._ui.open_rule_button.set_sensitive(True)
    
    156
    -            self._ui.privacy_list_active_checkbutton.set_sensitive(True)
    
    157
    -            self._ui.privacy_list_default_checkbutton.set_sensitive(True)
    
    158
    -        self.reset_fields()
    
    159
    -        con = app.connections[self.account]
    
    160
    -        con.get_module('PrivacyLists').get_privacy_lists()
    
    161
    -
    
    162
    -    def _nec_privacy_list_received(self, obj):
    
    163
    -        if obj.conn.name != self.account:
    
    164
    -            return
    
    165
    -        if obj.list_name != self.privacy_list_name:
    
    166
    -            return
    
    167
    -        self.privacy_list_received(obj.rules)
    
    168
    -
    
    169
    -    def refresh_rules(self):
    
    170
    -        con = app.connections[self.account]
    
    171
    -        con.get_module('PrivacyLists').get_privacy_list(self.privacy_list_name)
    
    172
    -
    
    173
    -    def on_delete_rule_button_clicked(self, _widget):
    
    174
    -        model = self._ui.list_of_rules_combobox.get_model()
    
    175
    -        iter_ = self._ui.list_of_rules_combobox.get_active_iter()
    
    176
    -        _rule = model[iter_][0]
    
    177
    -        tags = []
    
    178
    -        for rule in self.global_rules:
    
    179
    -            if rule != _rule:
    
    180
    -                tags.append(self.global_rules[rule])
    
    181
    -        con = app.connections[self.account]
    
    182
    -        con.get_module('PrivacyLists').set_privacy_list(
    
    183
    -            self.privacy_list_name, tags)
    
    184
    -        self.privacy_list_received(tags)
    
    185
    -        self._ui.add_edit_vbox.hide()
    
    186
    -        if not tags:  # we removed latest rule
    
    187
    -            if 'privacy_lists' in app.interface.instances[self.account]:
    
    188
    -                win = app.interface.instances[self.account]['privacy_lists']
    
    189
    -                win.remove_privacy_list(self.privacy_list_name)
    
    190
    -                win.draw_widgets()
    
    191
    -
    
    192
    -    def on_open_rule_button_clicked(self, _widget):
    
    193
    -        self._ui.add_edit_rule_label.set_label(_('<b>Edit a rule</b>'))
    
    194
    -        active_num = self._ui.list_of_rules_combobox.get_active()
    
    195
    -        if active_num == -1:
    
    196
    -            self.active_rule = ''
    
    197
    -        else:
    
    198
    -            model = self._ui.list_of_rules_combobox.get_model()
    
    199
    -            iter_ = self._ui.list_of_rules_combobox.get_active_iter()
    
    200
    -            self.active_rule = model[iter_][0]
    
    201
    -        if self.active_rule != '':
    
    202
    -            rule_info = self.global_rules[self.active_rule]
    
    203
    -            self._ui.edit_order_spinbutton.set_value(int(rule_info['order']))
    
    204
    -            if 'type' in rule_info:
    
    205
    -                if rule_info['type'] == 'jid':
    
    206
    -                    self._ui.edit_type_jabberid_radiobutton.set_active(True)
    
    207
    -                    self._ui.edit_type_jabberid_entry.set_text(
    
    208
    -                        rule_info['value'])
    
    209
    -                elif rule_info['type'] == 'group':
    
    210
    -                    self._ui.edit_type_group_radiobutton.set_active(True)
    
    211
    -                    if rule_info['value'] in self.list_of_groups:
    
    212
    -                        self._ui.edit_type_group_combobox.set_active(
    
    213
    -                            self.list_of_groups[rule_info['value']])
    
    214
    -                    else:
    
    215
    -                        self._ui.edit_type_group_combobox.set_active(0)
    
    216
    -                elif rule_info['type'] == 'subscription':
    
    217
    -                    self._ui.edit_type_subscription_radiobutton.set_active(True)
    
    218
    -                    sub_value = rule_info['value']
    
    219
    -                    if sub_value == 'none':
    
    220
    -                        self._ui.edit_type_subscription_combobox.set_active(0)
    
    221
    -                    elif sub_value == 'both':
    
    222
    -                        self._ui.edit_type_subscription_combobox.set_active(1)
    
    223
    -                    elif sub_value == 'from':
    
    224
    -                        self._ui.edit_type_subscription_combobox.set_active(2)
    
    225
    -                    elif sub_value == 'to':
    
    226
    -                        self._ui.edit_type_subscription_combobox.set_active(3)
    
    227
    -                else:
    
    228
    -                    self._ui.edit_type_select_all_radiobutton.set_active(True)
    
    229
    -            else:
    
    230
    -                self._ui.edit_type_select_all_radiobutton.set_active(True)
    
    231
    -            self._ui.edit_send_messages_checkbutton.set_active(False)
    
    232
    -            self._ui.edit_queries_send_checkbutton.set_active(False)
    
    233
    -            self._ui.edit_view_status_checkbutton.set_active(False)
    
    234
    -            self._ui.edit_send_status_checkbutton.set_active(False)
    
    235
    -            self._ui.edit_all_checkbutton.set_active(False)
    
    236
    -            if not rule_info['child']:
    
    237
    -                self._ui.edit_all_checkbutton.set_active(True)
    
    238
    -            else:
    
    239
    -                if 'presence-out' in rule_info['child']:
    
    240
    -                    self._ui.edit_send_status_checkbutton.set_active(True)
    
    241
    -                if 'presence-in' in rule_info['child']:
    
    242
    -                    self._ui.edit_view_status_checkbutton.set_active(True)
    
    243
    -                if 'iq' in rule_info['child']:
    
    244
    -                    self._ui.edit_queries_send_checkbutton.set_active(True)
    
    245
    -                if 'message' in rule_info['child']:
    
    246
    -                    self._ui.edit_send_messages_checkbutton.set_active(True)
    
    247
    -
    
    248
    -            if rule_info['action'] == 'allow':
    
    249
    -                self._ui.edit_allow_radiobutton.set_active(True)
    
    250
    -            else:
    
    251
    -                self._ui.edit_deny_radiobutton.set_active(True)
    
    252
    -        self._ui.add_edit_vbox.show()
    
    253
    -
    
    254
    -    def on_edit_all_checkbutton_toggled(self, widget):
    
    255
    -        if widget.get_active():
    
    256
    -            self._ui.edit_send_messages_checkbutton.set_active(True)
    
    257
    -            self._ui.edit_queries_send_checkbutton.set_active(True)
    
    258
    -            self._ui.edit_view_status_checkbutton.set_active(True)
    
    259
    -            self._ui.edit_send_status_checkbutton.set_active(True)
    
    260
    -            self._ui.edit_send_messages_checkbutton.set_sensitive(False)
    
    261
    -            self._ui.edit_queries_send_checkbutton.set_sensitive(False)
    
    262
    -            self._ui.edit_view_status_checkbutton.set_sensitive(False)
    
    263
    -            self._ui.edit_send_status_checkbutton.set_sensitive(False)
    
    264
    -        else:
    
    265
    -            self._ui.edit_send_messages_checkbutton.set_active(False)
    
    266
    -            self._ui.edit_queries_send_checkbutton.set_active(False)
    
    267
    -            self._ui.edit_view_status_checkbutton.set_active(False)
    
    268
    -            self._ui.edit_send_status_checkbutton.set_active(False)
    
    269
    -            self._ui.edit_send_messages_checkbutton.set_sensitive(True)
    
    270
    -            self._ui.edit_queries_send_checkbutton.set_sensitive(True)
    
    271
    -            self._ui.edit_view_status_checkbutton.set_sensitive(True)
    
    272
    -            self._ui.edit_send_status_checkbutton.set_sensitive(True)
    
    273
    -
    
    274
    -    def _on_privacy_list_active_toggled(self, widget):
    
    275
    -        name = None
    
    276
    -        if widget.get_active():
    
    277
    -            name = self.privacy_list_name
    
    278
    -
    
    279
    -        con = app.connections[self.account]
    
    280
    -        con.get_module('PrivacyLists').set_active_list(name)
    
    281
    -
    
    282
    -    def _on_privacy_list_default_toggled(self, widget):
    
    283
    -        name = None
    
    284
    -        if widget.get_active():
    
    285
    -            name = self.privacy_list_name
    
    286
    -
    
    287
    -        con = app.connections[self.account]
    
    288
    -        con.get_module('PrivacyLists').set_default_list(name)
    
    289
    -
    
    290
    -    def on_new_rule_button_clicked(self, _widget):
    
    291
    -        self.reset_fields()
    
    292
    -        self._ui.add_edit_vbox.show()
    
    293
    -
    
    294
    -    def reset_fields(self):
    
    295
    -        self._ui.edit_type_jabberid_entry.set_text('')
    
    296
    -        self._ui.edit_allow_radiobutton.set_active(True)
    
    297
    -        self._ui.edit_type_jabberid_radiobutton.set_active(True)
    
    298
    -        self.active_rule = ''
    
    299
    -        self._ui.edit_send_messages_checkbutton.set_active(False)
    
    300
    -        self._ui.edit_queries_send_checkbutton.set_active(False)
    
    301
    -        self._ui.edit_view_status_checkbutton.set_active(False)
    
    302
    -        self._ui.edit_send_status_checkbutton.set_active(False)
    
    303
    -        self._ui.edit_all_checkbutton.set_active(False)
    
    304
    -        self._ui.edit_order_spinbutton.set_value(self.max_order + 1)
    
    305
    -        self._ui.edit_type_group_combobox.set_active(0)
    
    306
    -        self._ui.edit_type_subscription_combobox.set_active(0)
    
    307
    -        self._ui.add_edit_rule_label.set_label(_('<b>Add a rule</b>'))
    
    308
    -
    
    309
    -    def get_current_tags(self):
    
    310
    -        if self._ui.edit_type_jabberid_radiobutton.get_active():
    
    311
    -            edit_type = 'jid'
    
    312
    -            edit_value = self._ui.edit_type_jabberid_entry.get_text()
    
    313
    -        elif self._ui.edit_type_group_radiobutton.get_active():
    
    314
    -            edit_type = 'group'
    
    315
    -            model = self._ui.edit_type_group_combobox.get_model()
    
    316
    -            iter_ = self._ui.edit_type_group_combobox.get_active_iter()
    
    317
    -            edit_value = model[iter_][0]
    
    318
    -        elif self._ui.edit_type_subscription_radiobutton.get_active():
    
    319
    -            edit_type = 'subscription'
    
    320
    -            subs = ['none', 'both', 'from', 'to']
    
    321
    -            edit_value = subs[
    
    322
    -                self._ui.edit_type_subscription_combobox.get_active()]
    
    323
    -        elif self._ui.edit_type_select_all_radiobutton.get_active():
    
    324
    -            edit_type = ''
    
    325
    -            edit_value = ''
    
    326
    -        edit_order = str(self._ui.edit_order_spinbutton.get_value_as_int())
    
    327
    -        if self._ui.edit_allow_radiobutton.get_active():
    
    328
    -            edit_deny = 'allow'
    
    329
    -        else:
    
    330
    -            edit_deny = 'deny'
    
    331
    -        child = []
    
    332
    -        if not self._ui.edit_all_checkbutton.get_active():
    
    333
    -            if self._ui.edit_send_messages_checkbutton.get_active():
    
    334
    -                child.append('message')
    
    335
    -            if self._ui.edit_queries_send_checkbutton.get_active():
    
    336
    -                child.append('iq')
    
    337
    -            if self._ui.edit_send_status_checkbutton.get_active():
    
    338
    -                child.append('presence-out')
    
    339
    -            if self._ui.edit_view_status_checkbutton.get_active():
    
    340
    -                child.append('presence-in')
    
    341
    -        if edit_type != '':
    
    342
    -            return {'order': edit_order, 'action': edit_deny,
    
    343
    -                    'type': edit_type, 'value': edit_value, 'child': child}
    
    344
    -        return {'order': edit_order, 'action': edit_deny, 'child': child}
    
    345
    -
    
    346
    -    def on_save_rule_button_clicked(self, _widget):
    
    347
    -        tags = []
    
    348
    -        current_tags = self.get_current_tags()
    
    349
    -        if int(current_tags['order']) > self.max_order:
    
    350
    -            self.max_order = int(current_tags['order'])
    
    351
    -        if self.active_rule == '':
    
    352
    -            tags.append(current_tags)
    
    353
    -
    
    354
    -        for rule in self.global_rules:
    
    355
    -            if rule != self.active_rule:
    
    356
    -                tags.append(self.global_rules[rule])
    
    357
    -            else:
    
    358
    -                tags.append(current_tags)
    
    359
    -
    
    360
    -        con = app.connections[self.account]
    
    361
    -        con.get_module('PrivacyLists').set_privacy_list(
    
    362
    -            self.privacy_list_name, tags)
    
    363
    -        self.refresh_rules()
    
    364
    -        self._ui.add_edit_vbox.hide()
    
    365
    -        if 'privacy_lists' in app.interface.instances[self.account]:
    
    366
    -            win = app.interface.instances[self.account]['privacy_lists']
    
    367
    -            win.add_privacy_list_to_combobox(self.privacy_list_name)
    
    368
    -            win.draw_widgets()
    
    369
    -
    
    370
    -    def _on_list_of_rules_changed(self, _widget):
    
    371
    -        self._ui.add_edit_vbox.hide()
    
    372
    -
    
    373
    -    def _on_edit_type_changed(self, widget, radiobutton):
    
    374
    -        active_bool = widget.get_active()
    
    375
    -        if active_bool:
    
    376
    -            self.edit_rule_type = radiobutton
    
    377
    -
    
    378
    -    def _on_edit_allow_changed(self, widget, radiobutton):
    
    379
    -        active_bool = widget.get_active()
    
    380
    -        if active_bool:
    
    381
    -            self.allow_deny = radiobutton
    
    382
    -
    
    383
    -    def _on_close_clicked(self, _widget):
    
    384
    -        self.window.destroy()
    
    385
    -
    
    386
    -
    
    387
    -class PrivacyListsWindow(EventHelper):
    
    388
    -    """
    
    389
    -    Window that is the main window for Privacy Lists; we can list there the
    
    390
    -    privacy lists and ask to create a new one or edit an already there one
    
    391
    -    """
    
    392
    -    def __init__(self, account):
    
    393
    -        EventHelper.__init__(self)
    
    394
    -        self.account = account
    
    395
    -        self.privacy_lists_save = []
    
    396
    -
    
    397
    -        self._ui = get_builder('privacy_lists_window.ui')
    
    398
    -
    
    399
    -        self.window = self._ui.get_object('privacy_lists_first_window')
    
    400
    -
    
    401
    -        self.draw_privacy_lists_in_combobox([])
    
    402
    -        self.privacy_lists_refresh()
    
    403
    -
    
    404
    -        self.enabled = True
    
    405
    -
    
    406
    -        if len(app.connections) > 1:
    
    407
    -            title = _('Privacy Lists for %s') % self.account
    
    408
    -        else:
    
    409
    -            title = _('Privacy Lists')
    
    410
    -
    
    411
    -        self.window.set_title(title)
    
    412
    -
    
    413
    -        # pylint: disable=line-too-long
    
    414
    -        self.register_events([
    
    415
    -            ('privacy-lists-received', ged.GUI1, self._nec_privacy_lists_received),
    
    416
    -            ('privacy-list-removed', ged.GUI1, self._nec_privacy_lists_removed),
    
    417
    -        ])
    
    418
    -        # pylint: enable=line-too-long
    
    419
    -
    
    420
    -        self.window.show_all()
    
    421
    -
    
    422
    -        self._ui.connect_signals(self)
    
    423
    -
    
    424
    -    def on_key_press_event(self, _widget, event):
    
    425
    -        if event.keyval == Gdk.KEY_Escape:
    
    426
    -            self.window.destroy()
    
    427
    -
    
    428
    -    def _on_privacy_lists_first_destroy(self, _widget):
    
    429
    -        if 'privacy_lists' in app.interface.instances[self.account]:
    
    430
    -            del app.interface.instances[self.account]['privacy_lists']
    
    431
    -        self.unregister_events()
    
    432
    -
    
    433
    -    def remove_privacy_list(self, privacy_list):
    
    434
    -        if privacy_list not in self.privacy_lists_save:
    
    435
    -            return
    
    436
    -
    
    437
    -        model = self._ui.list_of_privacy_lists_combobox.get_model()
    
    438
    -        for entry in model:
    
    439
    -            if entry[0] == privacy_list:
    
    440
    -                model.remove(entry.iter)
    
    441
    -
    
    442
    -        self.privacy_lists_save.remove(privacy_list)
    
    443
    -
    
    444
    -    def add_privacy_list_to_combobox(self, privacy_list):
    
    445
    -        if privacy_list in self.privacy_lists_save:
    
    446
    -            return
    
    447
    -        model = self._ui.list_of_privacy_lists_combobox.get_model()
    
    448
    -        model.append([privacy_list])
    
    449
    -        self.privacy_lists_save.append(privacy_list)
    
    450
    -
    
    451
    -    def draw_privacy_lists_in_combobox(self, privacy_lists):
    
    452
    -        self._ui.list_of_privacy_lists_combobox.set_active(-1)
    
    453
    -        self._ui.list_of_privacy_lists_combobox.get_model().clear()
    
    454
    -        self.privacy_lists_save = []
    
    455
    -        for add_item in privacy_lists:
    
    456
    -            self.add_privacy_list_to_combobox(add_item)
    
    457
    -        self.draw_widgets()
    
    458
    -
    
    459
    -    def draw_widgets(self):
    
    460
    -        if not self.privacy_lists_save:
    
    461
    -            self._ui.list_of_privacy_lists_combobox.set_sensitive(False)
    
    462
    -            self._ui.open_privacy_list_button.set_sensitive(False)
    
    463
    -            self._ui.delete_privacy_list_button.set_sensitive(False)
    
    464
    -        else:
    
    465
    -            self._ui.list_of_privacy_lists_combobox.set_sensitive(True)
    
    466
    -            self._ui.list_of_privacy_lists_combobox.set_active(0)
    
    467
    -            self._ui.open_privacy_list_button.set_sensitive(True)
    
    468
    -            self._ui.delete_privacy_list_button.set_sensitive(True)
    
    469
    -
    
    470
    -    def _on_close_clicked(self, _widget):
    
    471
    -        self.window.destroy()
    
    472
    -
    
    473
    -    def _on_delete_privacy_list_clicked(self, _widget):
    
    474
    -        active_list = self.privacy_lists_save[
    
    475
    -            self._ui.list_of_privacy_lists_combobox.get_active()]
    
    476
    -        con = app.connections[self.account]
    
    477
    -        con.get_module('PrivacyLists').del_privacy_list(active_list)
    
    478
    -
    
    479
    -    def privacy_list_removed(self, active_list):
    
    480
    -        self.privacy_lists_save.remove(active_list)
    
    481
    -        self.privacy_lists_received(self.privacy_lists_save)
    
    482
    -
    
    483
    -    def _nec_privacy_lists_removed(self, obj):
    
    484
    -        if obj.conn.name != self.account:
    
    485
    -            return
    
    486
    -        self.privacy_list_removed(obj.list_name)
    
    487
    -
    
    488
    -    def privacy_lists_received(self, lists):
    
    489
    -        privacy_lists = []
    
    490
    -        for privacy_list in lists:
    
    491
    -            privacy_lists.append(privacy_list)
    
    492
    -        self.draw_privacy_lists_in_combobox(privacy_lists)
    
    493
    -
    
    494
    -    def _nec_privacy_lists_received(self, obj):
    
    495
    -        if obj.conn.name != self.account:
    
    496
    -            return
    
    497
    -        self.privacy_lists_received(obj.lists)
    
    498
    -
    
    499
    -    def privacy_lists_refresh(self):
    
    500
    -        con = app.connections[self.account]
    
    501
    -        con.get_module('PrivacyLists').get_privacy_lists()
    
    502
    -
    
    503
    -    def _on_new_privacy_list_clicked(self, _widget):
    
    504
    -        name = self._ui.new_privacy_list_entry.get_text()
    
    505
    -        if not name:
    
    506
    -            ErrorDialog(
    
    507
    -                _('Invalid List Name'),
    
    508
    -                _('You must enter a name to create a privacy list.'),
    
    509
    -                transient_for=self.window)
    
    510
    -            return
    
    511
    -        key_name = 'privacy_list_%s' % name
    
    512
    -        if key_name in app.interface.instances[self.account]:
    
    513
    -            app.interface.instances[self.account][key_name].window.present()
    
    514
    -        else:
    
    515
    -            app.interface.instances[self.account][key_name] = \
    
    516
    -                PrivacyListWindow(self.account, name, 'NEW')
    
    517
    -        self._ui.new_privacy_list_entry.set_text('')
    
    518
    -
    
    519
    -    def _on_privacy_lists_refresh_clicked(self, _widget):
    
    520
    -        self.privacy_lists_refresh()
    
    521
    -
    
    522
    -    def _on_open_privacy_list_clicked(self, _widget):
    
    523
    -        name = self.privacy_lists_save[
    
    524
    -            self._ui.list_of_privacy_lists_combobox.get_active()]
    
    525
    -        key_name = 'privacy_list_%s' % name
    
    526
    -        if key_name in app.interface.instances[self.account]:
    
    527
    -            app.interface.instances[self.account][key_name].window.present()
    
    528
    -        else:
    
    529
    -            app.interface.instances[self.account][key_name] = \
    
    530
    -                PrivacyListWindow(self.account, name, 'EDIT')

  • gajim/gtk/server_info.py
    ... ... @@ -258,8 +258,6 @@ class ServerInfo(Gtk.ApplicationWindow, EventHelper):
    258 258
                         max_file_size
    
    259 259
     
    
    260 260
             return [
    
    261
    -            Feature('XEP-0016: Privacy Lists',
    
    262
    -                    con.get_module('PrivacyLists').supported),
    
    263 261
                 Feature('XEP-0045: Multi-User Chat',
    
    264 262
                         con.get_module('MUC').supported),
    
    265 263
                 Feature('XEP-0054: vcard-temp',
    

  • gajim/gui_menu_builder.py
    ... ... @@ -391,8 +391,7 @@ control=None, gc_contact=None, is_anonymous=True):
    391 391
     
    
    392 392
     
    
    393 393
         con = app.connections[account]
    
    394
    -    if con and (con.get_module('PrivacyLists').supported or
    
    395
    -                con.get_module('Blocking').supported):
    
    394
    +    if con.get_module('Blocking').supported:
    
    396 395
             transport = app.get_transport_name_from_jid(jid, use_config_setting=False)
    
    397 396
             if helpers.jid_is_blocked(account, jid):
    
    398 397
                 block_menuitem.set_no_show_all(True)
    
    ... ... @@ -522,6 +521,7 @@ def get_singlechat_menu(control_id, account, jid):
    522 521
             (_('Send Chatstate'), ['chatstate']),
    
    523 522
             ('win.invite-contacts-', _('Invite Contacts…')),
    
    524 523
             ('win.add-to-roster-', _('Add to Contact List…')),
    
    524
    +        ('win.block-contact-', _('Block Contact…')),
    
    525 525
             ('win.toggle-audio-', _('Voice Chat')),
    
    526 526
             ('win.toggle-video-', _('Video Chat')),
    
    527 527
             ('win.information-', _('Information')),
    
    ... ... @@ -680,7 +680,6 @@ def get_account_menu(account):
    680 680
                 ('-bookmarks', _('Bookmarks')),
    
    681 681
                 ('-pep-config', _('PEP Configuration')),
    
    682 682
                 ('-sync-history', _('Synchronise History…')),
    
    683
    -            ('-privacylists', _('Privacy Lists')),
    
    684 683
             ]),
    
    685 684
             (_('Admin'), [
    
    686 685
                 ('-send-server-message', _('Send Server Message…')),
    
    ... ... @@ -871,17 +870,6 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact):
    871 870
     
    
    872 871
         menu.append(Gtk.SeparatorMenuItem())
    
    873 872
     
    
    874
    -    if helpers.jid_is_blocked(account, contact.get_full_jid()):
    
    875
    -        action = 'win.unblock-%s::%s' % (control_id, contact.name)
    
    876
    -        label = _('Unblock')
    
    877
    -    else:
    
    878
    -        action = 'win.block-%s::%s' % (control_id, contact.name)
    
    879
    -        label = _('Block')
    
    880
    -
    
    881
    -    item = Gtk.MenuItem(label=label)
    
    882
    -    item.set_detailed_action_name(action)
    
    883
    -    menu.append(item)
    
    884
    -
    
    885 873
         item = Gtk.MenuItem(label=_('Kick'))
    
    886 874
         action = 'win.kick-%s::%s' % (control_id, contact.name)
    
    887 875
         if is_role_change_allowed(self_contact, contact):
    

  • gajim/roster_window.py
    ... ... @@ -1073,8 +1073,6 @@ class RosterWindow:
    1073 1073
             else:
    
    1074 1074
                 accounts = [account]
    
    1075 1075
             text = GLib.markup_escape_text(group)
    
    1076
    -        if helpers.group_is_blocked(account, group):
    
    1077
    -            text = '<span strikethrough="true">%s</span>' % text
    
    1078 1076
             if app.config.get('show_contacts_number'):
    
    1079 1077
                 nbr_on, nbr_total = app.contacts.get_nb_online_total_contacts(
    
    1080 1078
                         accounts=accounts, groups=[group])
    
    ... ... @@ -1165,14 +1163,7 @@ class RosterWindow:
    1165 1163
                     name = '%s [%s]' % (name, str(nb_unread))
    
    1166 1164
     
    
    1167 1165
             # Strike name if blocked
    
    1168
    -        strike = False
    
    1169
    -        if helpers.jid_is_blocked(account, jid):
    
    1170
    -            strike = True
    
    1171
    -        else:
    
    1172
    -            for group in contact.get_shown_groups():
    
    1173
    -                if helpers.group_is_blocked(account, group):
    
    1174
    -                    strike = True
    
    1175
    -                    break
    
    1166
    +        strike = helpers.jid_is_blocked(account, jid)
    
    1176 1167
             if strike:
    
    1177 1168
                 name = '<span strikethrough="true">%s</span>' % name
    
    1178 1169
     
    
    ... ... @@ -2744,55 +2735,40 @@ class RosterWindow:
    2744 2735
             for jid in obj.changed:
    
    2745 2736
                 self.draw_contact(jid, obj.conn.name)
    
    2746 2737
     
    
    2747
    -    def on_block(self, widget, list_, group=None):
    
    2738
    +    def on_block(self, widget, list_):
    
    2748 2739
             """
    
    2749 2740
             When clicked on the 'block' button in context menu. list_ is a list of
    
    2750 2741
             (contact, account)
    
    2751 2742
             """
    
    2752
    -        def on_continue(msg, pep_dict):
    
    2753
    -            if msg is None:
    
    2754
    -                # user pressed Cancel to change status message dialog
    
    2755
    -                return
    
    2743
    +        def _block_it(is_checked=None, report=None):
    
    2744
    +            if is_checked is not None:  # Dialog has been shown
    
    2745
    +                if is_checked:
    
    2746
    +                    app.config.set('confirm_block', 'no')
    
    2747
    +                else:
    
    2748
    +                    app.config.set('confirm_block', 'yes')
    
    2756 2749
     
    
    2757 2750
                 accounts = []
    
    2758 2751
                 for _, account in list_:
    
    2759 2752
                     con = app.connections[account]
    
    2760
    -                if con.get_module('PrivacyLists').supported or (
    
    2761
    -                        group is None and con.get_module('Blocking').supported):
    
    2753
    +                if con.get_module('Blocking').supported:
    
    2762 2754
                         accounts.append(account)
    
    2763 2755
     
    
    2764
    -            if group is None:
    
    2765
    -                for acct in accounts:
    
    2766
    -                    l_ = [i[0] for i in list_ if i[1] == acct]
    
    2767
    -                    con = app.connections[acct]
    
    2768
    -                    con.get_module('PrivacyLists').block_contacts(l_, msg)
    
    2769
    -                    for contact in l_:
    
    2770
    -                        ctrl = app.interface.msg_win_mgr.get_control(
    
    2771
    -                            contact.jid, acct)
    
    2772
    -                        if ctrl:
    
    2773
    -                            ctrl.parent_win.remove_tab(
    
    2774
    -                                ctrl, ctrl.parent_win.CLOSE_COMMAND, force=True)
    
    2775
    -                        if contact.show == 'not in roster':
    
    2776
    -                            self.remove_contact(contact.jid, acct, force=True,
    
    2777
    -                                                backend=True)
    
    2778
    -                            return
    
    2779
    -                        self.draw_contact(contact.jid, acct)
    
    2780
    -            else:
    
    2781
    -                for acct in accounts:
    
    2782
    -                    l_ = [i[0] for i in list_ if i[1] == acct]
    
    2783
    -                    con = app.connections[acct]
    
    2784
    -                    con.get_module('PrivacyLists').block_group(group, l_, msg)
    
    2785
    -                    self.draw_group(group, acct)
    
    2786
    -                    for contact in l_:
    
    2787
    -                        self.draw_contact(contact.jid, acct)
    
    2788
    -
    
    2789
    -        def _block_it(is_checked=None):
    
    2790
    -            if is_checked is not None: # dialog has been shown
    
    2791
    -                if is_checked:  # user does not want to be asked again
    
    2792
    -                    app.config.set('confirm_block', 'no')
    
    2793
    -                else:
    
    2794
    -                    app.config.set('confirm_block', 'yes')
    
    2795
    -            self.get_status_message('offline', on_continue, show_pep=False)
    
    2756
    +            for acct in accounts:
    
    2757
    +                l_ = [i[0] for i in list_ if i[1] == acct]
    
    2758
    +                con = app.connections[acct]
    
    2759
    +                jid_list = [contact.jid for contact in l_]
    
    2760
    +                con.get_module('Blocking').block(jid_list, report)
    
    2761
    +                for contact in l_:
    
    2762
    +                    ctrl = app.interface.msg_win_mgr.get_control(
    
    2763
    +                        contact.jid, acct)
    
    2764
    +                    if ctrl:
    
    2765
    +                        ctrl.parent_win.remove_tab(
    
    2766
    +                            ctrl, ctrl.parent_win.CLOSE_COMMAND, force=True)
    
    2767
    +                    if contact.show == 'not in roster':
    
    2768
    +                        self.remove_contact(contact.jid, acct, force=True,
    
    2769
    +                                            backend=True)
    
    2770
    +                        return
    
    2771
    +                    self.draw_contact(contact.jid, acct)
    
    2796 2772
     
    
    2797 2773
             # Check if confirmation is needed for blocking
    
    2798 2774
             confirm_block = app.config.get('confirm_block')
    
    ... ... @@ -2800,61 +2776,39 @@ class RosterWindow:
    2800 2776
                 _block_it()
    
    2801 2777
                 return
    
    2802 2778
     
    
    2803
    -        # Set dialog's labels depending on if it is a group or a single contact
    
    2804
    -        if group is None:
    
    2805
    -            title = _('Block Contact')
    
    2806
    -            pritext = _('Really block this contact?')
    
    2807
    -            sectext = _('This contact will see you offline and you will not '
    
    2808
    -                        'receive any messages sent to you by this contact.')
    
    2809
    -            button_text = _('_Block Contact')
    
    2810
    -        else:
    
    2811
    -            title = _('Block Group')
    
    2812
    -            pritext = _('Really block this group?')
    
    2813
    -            sectext = _('All contacts of this group will see you as offline '
    
    2814
    -                        'and you will not receive any messages sent to you '
    
    2815
    -                        'by any one of these contacts.')
    
    2816
    -            button_text = _('_Block Group')
    
    2817
    -
    
    2818 2779
             NewConfirmationCheckDialog(
    
    2819
    -            title,
    
    2820
    -            pritext,
    
    2821
    -            sectext,
    
    2822
    -            _('_Do not ask me again'),
    
    2780
    +            _('Block Contact'),
    
    2781
    +            _('Really block this contact?'),
    
    2782
    +            _('You will appear offline for this contact and you '
    
    2783
    +              'will not receive further messages.'),
    
    2784
    +            _('_Do not ask again'),
    
    2823 2785
                 [DialogButton.make('Cancel'),
    
    2786
    +             DialogButton.make('OK',
    
    2787
    +                               text=_('_Report Spam'),
    
    2788
    +                               callback=_block_it,
    
    2789
    +                               kwargs={'report': 'spam'}),
    
    2824 2790
                  DialogButton.make('Remove',
    
    2825
    -                               text=button_text,
    
    2791
    +                               text=_('_Block'),
    
    2826 2792
                                    callback=_block_it)],
    
    2827 2793
                 modal=False).show()
    
    2828 2794
     
    
    2829
    -    def on_unblock(self, widget, list_, group=None):
    
    2795
    +    def on_unblock(self, widget, list_):
    
    2830 2796
             """
    
    2831 2797
             When clicked on the 'unblock' button in context menu.
    
    2832 2798
             """
    
    2833 2799
             accounts = []
    
    2834 2800
             for _, account in list_:
    
    2835 2801
                 con = app.connections[account]
    
    2836
    -            if con.get_module('PrivacyLists').supported or (
    
    2837
    -                    group is None and con.get_module('Blocking').supported):
    
    2802
    +            if con.get_module('Blocking').supported:
    
    2838 2803
                     accounts.append(account)
    
    2839 2804
     
    
    2840
    -        if group is None:
    
    2841
    -            for acct in accounts:
    
    2842
    -                l_ = [i[0] for i in list_ if i[1] == acct]
    
    2843
    -                con = app.connections[acct]
    
    2844
    -                con.get_module('PrivacyLists').unblock_contacts(l_)
    
    2845
    -                for contact in l_:
    
    2846
    -                    self.draw_contact(contact.jid, acct)
    
    2847
    -        else:
    
    2848
    -            for acct in accounts:
    
    2849
    -                l_ = [i[0] for i in list_ if i[1] == acct]
    
    2850
    -                con = app.connections[acct]
    
    2851
    -                con.get_module('PrivacyLists').unblock_group(group, l_)
    
    2852
    -                self.draw_group(group, acct)
    
    2853
    -                for contact in l_:
    
    2854
    -                    self.draw_contact(contact.jid, acct)
    
    2855 2805
             for acct in accounts:
    
    2856
    -            if 'privacy_list_block' in app.interface.instances[acct]:
    
    2857
    -                del app.interface.instances[acct]['privacy_list_block']
    
    2806
    +            l_ = [i[0] for i in list_ if i[1] == acct]
    
    2807
    +            con = app.connections[acct]
    
    2808
    +            jid_list = [contact.jid for contact in l_]
    
    2809
    +            con.get_module('Blocking').unblock(jid_list)
    
    2810
    +            for contact in l_:
    
    2811
    +                self.draw_contact(contact.jid, acct)
    
    2858 2812
     
    
    2859 2813
         def on_rename(self, widget, row_type, jid, account):
    
    2860 2814
             # This function is called either by F2 or by Rename menuitem
    
    ... ... @@ -2967,24 +2921,24 @@ class RosterWindow:
    2967 2921
             app.interface.instances['file_transfers'].show_file_send_request(
    
    2968 2922
                 account, contact)
    
    2969 2923
     
    
    2970
    -    def on_invite_to_room(self, widget, list_, room_jid, room_account,
    
    2971
    -    resource=None):
    
    2924
    +    def on_invite_to_room(self,
    
    2925
    +                          _widget,
    
    2926
    +                          list_,
    
    2927
    +                          room_jid,
    
    2928
    +                          room_account,
    
    2929
    +                          resource=None):
    
    2972 2930
             """
    
    2973 2931
             Resource parameter MUST NOT be used if more than one contact in list
    
    2974 2932
             """
    
    2975
    -        for e in list_:
    
    2976
    -            contact = e[0]
    
    2933
    +        gc_control = app.get_groupchat_control(room_account, room_jid)
    
    2934
    +        if gc_control is None:
    
    2935
    +            return
    
    2936
    +
    
    2937
    +        for contact, _ in list_:
    
    2977 2938
                 contact_jid = contact.jid
    
    2978 2939
                 if resource: # we MUST have one contact only in list_
    
    2979 2940
                     contact_jid += '/' + resource
    
    2980
    -            con = app.connections[room_account]
    
    2981
    -            con.get_module('MUC').invite(room_jid, contact_jid)
    
    2982
    -            gc_control = app.interface.msg_win_mgr.get_gc_control(room_jid,
    
    2983
    -                room_account)
    
    2984
    -            if gc_control:
    
    2985
    -                gc_control.add_info_message(
    
    2986
    -                    _('%(jid)s has been invited to this group chat') % {
    
    2987
    -                    'jid': contact_jid})
    
    2941
    +            gc_control.invite(contact_jid)
    
    2988 2942
     
    
    2989 2943
         def on_all_groupchat_maximized(self, widget, group_list):
    
    2990 2944
             for (contact, account) in group_list:
    
    ... ... @@ -4204,8 +4158,9 @@ class RosterWindow:
    4204 4158
                 if type_source != 'contact':
    
    4205 4159
                     return
    
    4206 4160
                 contact_jid = data
    
    4207
    -            con = app.connections[account_dest]
    
    4208
    -            con.get_module('MUC').invite(jid_dest, contact_jid)
    
    4161
    +            gc_control = app.get_groupchat_control(account_dest, jid_dest)
    
    4162
    +            if gc_control is not None:
    
    4163
    +                gc_control.invite(contact_jid)
    
    4209 4164
                 return
    
    4210 4165
     
    
    4211 4166
             if type_source == 'group':
    
    ... ... @@ -4902,28 +4857,6 @@ class RosterWindow:
    4902 4857
                 rename_item.connect('activate', self.on_rename, 'group', group,
    
    4903 4858
                     account)
    
    4904 4859
     
    
    4905
    -            # Block group
    
    4906
    -            is_blocked = False
    
    4907
    -            if self.regroup:
    
    4908
    -                for g_account in app.connections:
    
    4909
    -                    if helpers.group_is_blocked(g_account, group):
    
    4910
    -                        is_blocked = True
    
    4911
    -            else:
    
    4912
    -                if helpers.group_is_blocked(account, group):
    
    4913
    -                    is_blocked = True
    
    4914
    -
    
    4915
    -            if is_blocked and app.connections[account].get_module('PrivacyLists').supported:
    
    4916
    -                unblock_menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Unblock'))
    
    4917
    -                unblock_menuitem.connect('activate', self.on_unblock, list_,
    
    4918
    -                    group)
    
    4919
    -                menu.append(unblock_menuitem)
    
    4920
    -            else:
    
    4921
    -                block_menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Block'))
    
    4922
    -                block_menuitem.connect('activate', self.on_block, list_, group)
    
    4923
    -                menu.append(block_menuitem)
    
    4924
    -                if not app.connections[account].get_module('PrivacyLists').supported:
    
    4925
    -                    block_menuitem.set_sensitive(False)
    
    4926
    -
    
    4927 4860
                 # Remove group
    
    4928 4861
                 remove_item = Gtk.MenuItem.new_with_mnemonic(_('Remo_ve'))
    
    4929 4862
                 menu.append(remove_item)
    
    ... ... @@ -4967,16 +4900,18 @@ class RosterWindow:
    4967 4900
             list_ = [] # list of (jid, account) tuples
    
    4968 4901
             one_account_offline = False
    
    4969 4902
             is_blocked = True
    
    4970
    -        privacy_rules_supported = True
    
    4903
    +        blocking_supported = True
    
    4971 4904
             for titer in iters:
    
    4972 4905
                 jid = model[titer][Column.JID]
    
    4973 4906
                 account = model[titer][Column.ACCOUNT]
    
    4974 4907
                 if not app.account_is_available(account):
    
    4975 4908
                     one_account_offline = True
    
    4976
    -            if not app.connections[account].get_module('PrivacyLists').supported:
    
    4977
    -                privacy_rules_supported = False
    
    4978
    -            contact = app.contacts.get_contact_with_highest_priority(account,
    
    4979
    -                jid)
    
    4909
    +
    
    4910
    +            con = app.connections[account]
    
    4911
    +            if not con.get_module('Blocking').supported:
    
    4912
    +                blocking_supported = False
    
    4913
    +            contact = app.contacts.get_contact_with_highest_priority(
    
    4914
    +                account, jid)
    
    4980 4915
                 if not helpers.jid_is_blocked(account, jid):
    
    4981 4916
                     is_blocked = False
    
    4982 4917
                 list_.append((contact, account))
    
    ... ... @@ -5027,7 +4962,7 @@ class RosterWindow:
    5027 4962
             manage_contacts_submenu.append(item)
    
    5028 4963
     
    
    5029 4964
             # Block
    
    5030
    -        if is_blocked and privacy_rules_supported:
    
    4965
    +        if is_blocked and blocking_supported:
    
    5031 4966
                 unblock_menuitem = Gtk.MenuItem.new_with_mnemonic(_('_Unblock'))
    
    5032 4967
                 unblock_menuitem.connect('activate', self.on_unblock, list_)
    
    5033 4968
                 manage_contacts_submenu.append(unblock_menuitem)
    
    ... ... @@ -5036,7 +4971,7 @@ class RosterWindow:
    5036 4971
                 block_menuitem.connect('activate', self.on_block, list_)
    
    5037 4972
                 manage_contacts_submenu.append(block_menuitem)
    
    5038 4973
     
    
    5039
    -            if not privacy_rules_supported:
    
    4974
    +            if not blocking_supported:
    
    5040 4975
                     block_menuitem.set_sensitive(False)
    
    5041 4976
     
    
    5042 4977
             # Remove
    
    ... ... @@ -5122,75 +5057,6 @@ class RosterWindow:
    5122 5057
             menu.show_all()
    
    5123 5058
             menu.popup(None, None, None, None, event_button, event.time)
    
    5124 5059
     
    
    5125
    -    def get_and_connect_advanced_menuitem_menu(self, account):
    
    5126
    -        """
    
    5127
    -        Add FOR ACCOUNT options
    
    5128
    -        """
    
    5129
    -        xml = get_builder('advanced_menuitem_menu.ui')
    
    5130
    -        advanced_menuitem_menu = xml.get_object('advanced_menuitem_menu')
    
    5131
    -
    
    5132
    -        xml_console_menuitem = xml.get_object('xml_console_menuitem')
    
    5133
    -        archiving_preferences_menuitem = xml.get_object(
    
    5134
    -            'archiving_preferences_menuitem')
    
    5135
    -        privacy_lists_menuitem = xml.get_object('privacy_lists_menuitem')
    
    5136
    -        administrator_menuitem = xml.get_object('administrator_menuitem')
    
    5137
    -        send_server_message_menuitem = xml.get_object(
    
    5138
    -            'send_server_message_menuitem')
    
    5139
    -        set_motd_menuitem = xml.get_object('set_motd_menuitem')
    
    5140
    -        update_motd_menuitem = xml.get_object('update_motd_menuitem')
    
    5141
    -        delete_motd_menuitem = xml.get_object('delete_motd_menuitem')
    
    5142
    -
    
    5143
    -        xml_console_menuitem.connect('activate',
    
    5144
    -            self.on_xml_console_menuitem_activate, account)
    
    5145
    -
    
    5146
    -        if app.connections[account]:
    
    5147
    -            if app.connections[account].get_module('PrivacyLists').supported:
    
    5148
    -                privacy_lists_menuitem.connect('activate',
    
    5149
    -                    self.on_privacy_lists_menuitem_activate, account)
    
    5150
    -            else:
    
    5151
    -                privacy_lists_menuitem.set_sensitive(False)
    
    5152
    -            if app.connections[account].get_module('MAM').available:
    
    5153
    -                archiving_preferences_menuitem.connect(
    
    5154
    -                    'activate',
    
    5155
    -                    self.on_archiving_preferences_menuitem_activate, account)
    
    5156
    -            else:
    
    5157
    -                archiving_preferences_menuitem.set_sensitive(False)
    
    5158
    -
    
    5159
    -        if app.connections[account].is_zeroconf:
    
    5160
    -            administrator_menuitem.set_sensitive(False)
    
    5161
    -            send_server_message_menuitem.set_sensitive(False)
    
    5162
    -            set_motd_menuitem.set_sensitive(False)
    
    5163
    -            update_motd_menuitem.set_sensitive(False)
    
    5164
    -            delete_motd_menuitem.set_sensitive(False)
    
    5165
    -        else:
    
    5166
    -            send_server_message_menuitem.connect('activate',
    
    5167
    -                self.on_send_server_message_menuitem_activate, account)
    
    5168
    -
    
    5169
    -            set_motd_menuitem.connect('activate',
    
    5170
    -                self.on_set_motd_menuitem_activate, account)
    
    5171
    -
    
    5172
    -            update_motd_menuitem.connect('activate',
    
    5173
    -                self.on_update_motd_menuitem_activate, account)
    
    5174
    -
    
    5175
    -            delete_motd_menuitem.connect('activate',
    
    5176
    -                self.on_delete_motd_menuitem_activate, account)
    
    5177
    -
    
    5178
    -        advanced_menuitem_menu.show_all()
    
    5179
    -
    
    5180
    -        return advanced_menuitem_menu
    
    5181
    -
    
    5182
    -    def add_history_manager_menuitem(self, menu):
    
    5183
    -        """
    
    5184
    -        Add a separator and History Manager menuitem BELOW for account menuitems
    
    5185
    -        """
    
    5186
    -        item = Gtk.SeparatorMenuItem.new() # separator
    
    5187
    -        menu.append(item)
    
    5188
    -
    
    5189
    -        # History manager
    
    5190
    -        item = Gtk.MenuItem.new_with_mnemonic(_('History Manager'))
    
    5191
    -        menu.append(item)
    
    5192
    -        item.connect('activate', self.on_history_manager_menuitem_activate)
    
    5193
    -
    
    5194 5060
         def show_appropriate_context_menu(self, event, iters):
    
    5195 5061
             # iters must be all of the same type
    
    5196 5062
             model = self.modelfilter