Philipp Hörist pushed to branch unify-control at gajim / gajim

Commits:

4 changed files:

Changes:

  • gajim/common/helpers.py
    ... ... @@ -1229,19 +1229,46 @@ def __init__(self, log_: Union[logging.Logger, LogAdapter, None] = None):
    1229 1229
             self._log = log_
    
    1230 1230
             self._callbacks: types.ObservableCbDict = defaultdict(list)
    
    1231 1231
     
    
    1232
    -    def disconnect_signals(self) -> None:
    
    1233
    -        self._callbacks = defaultdict(list)
    
    1232
    +    def __disconnect(self,
    
    1233
    +                     obj: Any,
    
    1234
    +                     signals: Optional[set[str]] = None
    
    1235
    +                     ) -> None:
    
    1236
    +
    
    1237
    +        def _remove(handlers: list[weakref.WeakMethod[types.AnyCallableT]]
    
    1238
    +                    ) -> None:
    
    1234 1239
     
    
    1235
    -    def disconnect_all_from_obj(self, obj: Any) -> None:
    
    1236
    -        for handlers in self._callbacks.values():
    
    1237 1240
                 for handler in list(handlers):
    
    1238 1241
                     func = handler()
    
    1239 1242
                     if func is None or func.__self__ is obj:
    
    1240 1243
                         handlers.remove(handler)
    
    1241 1244
     
    
    1245
    +        if signals is None:
    
    1246
    +            for handlers in self._callbacks.values():
    
    1247
    +                _remove(handlers)
    
    1248
    +
    
    1249
    +        else:
    
    1250
    +            for signal in signals:
    
    1251
    +                _remove(self._callbacks.get(signal, []))
    
    1252
    +
    
    1253
    +    def disconnect_signals(self) -> None:
    
    1254
    +        self._callbacks = defaultdict(list)
    
    1255
    +
    
    1256
    +    def multi_disconnect(self,
    
    1257
    +                         obj: Any,
    
    1258
    +                         signals: Optional[set[str]]
    
    1259
    +                         ) -> None:
    
    1260
    +
    
    1261
    +        self.__disconnect(obj, signals)
    
    1262
    +
    
    1263
    +    def disconnect_all_from_obj(self, obj: Any) -> None:
    
    1264
    +        self.__disconnect(obj)
    
    1265
    +
    
    1242 1266
         def disconnect(self, obj: Any) -> None:
    
    1243 1267
             self.disconnect_all_from_obj(obj)
    
    1244 1268
     
    
    1269
    +    def disconnect_signal(self, obj: Any, signal: str) -> None:
    
    1270
    +        self.__disconnect(obj, {signal})
    
    1271
    +
    
    1245 1272
         def connect_signal(self,
    
    1246 1273
                            signal_name: str,
    
    1247 1274
                            func: types.AnyCallableT) -> None:
    

  • gajim/common/modules/contacts.py
    ... ... @@ -669,6 +669,11 @@ def get_resource(self, resource: str) -> GroupchatParticipant:
    669 669
                 contact = self.add_resource(resource)
    
    670 670
             return contact
    
    671 671
     
    
    672
    +    def get_participants(self) -> Iterator[GroupchatParticipant]:
    
    673
    +        for contact in self._resources.values():
    
    674
    +            if contact.is_available:
    
    675
    +                yield contact
    
    676
    +
    
    672 677
         @property
    
    673 678
         def name(self) -> str:
    
    674 679
             client = app.get_client(self._account)
    

  • gajim/gtk/control.py
    ... ... @@ -119,8 +119,6 @@ def __init__(self) -> None:
    119 119
     
    
    120 120
             self._subject_text_cache: dict[JID, str] = {}
    
    121 121
     
    
    122
    -        # self._set_control_inactive()
    
    123
    -
    
    124 122
             self.widget = cast(Gtk.Box, self._ui.get_object('control_box'))
    
    125 123
             self.widget.show_all()
    
    126 124
     
    
    ... ... @@ -133,6 +131,8 @@ def has_active_chat(self) -> bool:
    133 131
             return self.contact is not None
    
    134 132
     
    
    135 133
         def clear(self) -> None:
    
    134
    +        log.info('Clear')
    
    135
    +
    
    136 136
             if self.contact is not None:
    
    137 137
                 self.contact.disconnect_all_from_obj(self)
    
    138 138
     
    
    ... ... @@ -149,6 +149,7 @@ def switch_contact(self, contact: Union[BareContact,
    149 149
                                                 GroupchatContact,
    
    150 150
                                                 GroupchatParticipant]) -> None:
    
    151 151
     
    
    152
    +        log.info('Switch to %s (%s)', contact.jid, contact.account)
    
    152 153
             if self.contact is not None:
    
    153 154
                 self.contact.disconnect_all_from_obj(self)
    
    154 155
     
    
    ... ... @@ -179,7 +180,6 @@ def switch_contact(self, contact: Union[BareContact,
    179 180
     
    
    180 181
             elif isinstance(self.contact, GroupchatContact):
    
    181 182
                 self.contact.multi_connect({
    
    182
    -                'state-changed': self._on_muc_state_changed,
    
    183 183
                     'user-joined': self._on_user_joined,
    
    184 184
                     'user-left': self._on_user_left,
    
    185 185
                     'user-affiliation-changed': self._on_user_affiliation_changed,
    
    ... ... @@ -791,17 +791,6 @@ def _on_user_status_show_changed(self,
    791 791
                                                                        status=status)
    
    792 792
                 self.add_info_message(message)
    
    793 793
     
    
    794
    -    def _on_muc_state_changed(self,
    
    795
    -                              _contact: GroupchatContact,
    
    796
    -                              _signal_name: str
    
    797
    -                              ) -> None:
    
    798
    -
    
    799
    -        if self.contact.is_joined:
    
    800
    -            self._set_control_active()
    
    801
    -
    
    802
    -        elif self.contact.is_not_joined:
    
    803
    -            self._set_control_inactive()
    
    804
    -
    
    805 794
         def _on_muc_disco_update(self, event: events.MucDiscoUpdate) -> None:
    
    806 795
             pass
    
    807 796
     
    
    ... ... @@ -969,18 +958,6 @@ def _on_room_config_changed(self,
    969 958
             for change in changes:
    
    970 959
                 self.add_info_message(change)
    
    971 960
     
    
    972
    -    def _set_control_active(self) -> None:
    
    973
    -        assert self.roster is not None
    
    974
    -        self.roster.initial_draw()
    
    975
    -        self.conversation_view.update_avatars()
    
    976
    -
    
    977
    -    def _set_control_inactive(self) -> None:
    
    978
    -        assert self.roster is not None
    
    979
    -        self.roster.enable_sort(False)
    
    980
    -        self.roster.reset()
    
    981
    -
    
    982
    -        self._client.get_module('Chatstate').remove_delay_timeout(self.contact)
    
    983
    -
    
    984 961
         def rejoin(self) -> None:
    
    985 962
             self._client.get_module('MUC').join(self.contact.jid)
    
    986 963
     
    

  • gajim/gtk/groupchat_roster.py
    ... ... @@ -18,6 +18,7 @@
    18 18
     from typing import Optional
    
    19 19
     
    
    20 20
     import locale
    
    21
    +import logging
    
    21 22
     from enum import IntEnum
    
    22 23
     
    
    23 24
     from gi.repository import Gdk
    
    ... ... @@ -44,6 +45,9 @@
    44 45
     from .util import EventHelper
    
    45 46
     
    
    46 47
     
    
    48
    +log = logging.getLogger('gajim.gui.groupchat_roster')
    
    49
    +
    
    50
    +
    
    47 51
     AffiliationRoleSortOrder = {
    
    48 52
         'owner': 0,
    
    49 53
         'admin': 1,
    
    ... ... @@ -61,6 +65,17 @@ class Column(IntEnum):
    61 65
         NICK_OR_GROUP = 4
    
    62 66
     
    
    63 67
     
    
    68
    +CONTACT_SIGNALS = {
    
    69
    +    'user-affiliation-changed',
    
    70
    +    'user-avatar-update',
    
    71
    +    'user-joined',
    
    72
    +    'user-left',
    
    73
    +    'user-nickname-changed',
    
    74
    +    'user-role-changed',
    
    75
    +    'user-status-show-changed',
    
    76
    +}
    
    77
    +
    
    78
    +
    
    64 79
     class GroupchatRoster(Gtk.ScrolledWindow, EventHelper):
    
    65 80
     
    
    66 81
         __gsignals__ = {
    
    ... ... @@ -107,36 +122,33 @@ def __init__(self) -> None:
    107 122
             ])
    
    108 123
     
    
    109 124
         def clear(self) -> None:
    
    110
    -        if self._contact is not None:
    
    111
    -            self._contact.disconnect_all_from_obj(self)
    
    112
    -
    
    125
    +        log.info('Clear')
    
    126
    +        if self._contact is None:
    
    127
    +            return
    
    128
    +        self._unload_roster()
    
    129
    +        self._contact.disconnect_signal(self, 'state-changed')
    
    113 130
             self._contact = None
    
    114
    -        self.reset()
    
    115 131
     
    
    116 132
         def switch_contact(self, contact: types.ChatContactT) -> None:
    
    117
    -        self.clear()
    
    133
    +        if self._contact is not None:
    
    134
    +            self.clear()
    
    118 135
     
    
    119 136
             if not isinstance(contact, GroupchatContact):
    
    120 137
                 return
    
    121 138
     
    
    122
    -        contact.connect('user-avatar-update', self._on_user_avatar_update)
    
    123
    -        contact.connect('user-joined', self._on_user_joined)
    
    124
    -        contact.connect('user-left', self._on_user_left)
    
    125
    -        contact.connect('user-affiliation-changed', self._update_contact)
    
    126
    -        contact.connect('user-role-changed', self._update_contact)
    
    127
    -        contact.connect('user-nickname-changed', self._on_user_nickname_changed)
    
    128
    -        contact.connect('user-status-show-changed',
    
    129
    -                        self._on_user_status_show_changed)
    
    139
    +        log.info('Switch to %s (%s)', contact.jid, contact.account)
    
    140
    +
    
    141
    +        contact.connect('state-changed', self._on_muc_state_changed)
    
    130 142
     
    
    131 143
             self._contact = contact
    
    132 144
     
    
    145
    +        if self._contact.is_joined:
    
    146
    +            self._load_roster()
    
    147
    +
    
    133 148
         @staticmethod
    
    134 149
         def _on_focus_out(treeview: Gtk.TreeView, _param: Gdk.EventFocus) -> None:
    
    135 150
             treeview.get_selection().unselect_all()
    
    136 151
     
    
    137
    -    def _set_model(self) -> None:
    
    138
    -        self._roster.set_model(self._store)
    
    139
    -
    
    140 152
         def _query_tooltip(self,
    
    141 153
                            widget: Gtk.Widget,
    
    142 154
                            x_pos: int,
    
    ... ... @@ -393,6 +405,17 @@ def destroy(popover: Gtk.Popover) -> None:
    393 405
             popover.connect('closed', destroy)
    
    394 406
             popover.popup()
    
    395 407
     
    
    408
    +    def _on_muc_state_changed(self,
    
    409
    +                              contact: GroupchatContact,
    
    410
    +                              _signal_name: str
    
    411
    +                              ) -> None:
    
    412
    +
    
    413
    +        if contact.is_joined:
    
    414
    +            self._load_roster()
    
    415
    +
    
    416
    +        elif contact.is_not_joined:
    
    417
    +            self._unload_roster()
    
    418
    +
    
    396 419
         def _tree_compare_iters(self,
    
    397 420
                                 model: Gtk.TreeModel,
    
    398 421
                                 iter1: Gtk.TreeIter,
    
    ... ... @@ -436,15 +459,40 @@ def enable_sort(self, enable: bool) -> None:
    436 459
     
    
    437 460
             self._store.set_sort_column_id(column, Gtk.SortType.ASCENDING)
    
    438 461
     
    
    439
    -    def invalidate_sort(self) -> None:
    
    440
    -        self.enable_sort(False)
    
    441
    -        self.enable_sort(True)
    
    462
    +    def _load_roster(self) -> None:
    
    463
    +        log.info('Load Roster')
    
    464
    +        self._contact.multi_connect({
    
    465
    +            'user-affiliation-changed': self._update_contact,
    
    466
    +            'user-avatar-update': self._on_user_avatar_update,
    
    467
    +            'user-joined': self._on_user_joined,
    
    468
    +            'user-left': self._on_user_left,
    
    469
    +            'user-nickname-changed': self._on_user_nickname_changed,
    
    470
    +            'user-role-changed': self._update_contact,
    
    471
    +            'user-status-show-changed': self._on_user_status_show_changed,
    
    472
    +        })
    
    473
    +
    
    474
    +        for participant in self._contact.get_participants():
    
    475
    +            self._add_contact(participant)
    
    442 476
     
    
    443
    -    def initial_draw(self) -> None:
    
    444 477
             self.enable_sort(True)
    
    445
    -        self._set_model()
    
    478
    +        self._roster.set_model(self._store)
    
    446 479
             self._roster.expand_all()
    
    447 480
     
    
    481
    +    def _unload_roster(self) -> None:
    
    482
    +        log.info('Unload Roster')
    
    483
    +        self._contact.multi_disconnect(self, CONTACT_SIGNALS)
    
    484
    +
    
    485
    +        self._roster.set_model(None)
    
    486
    +        self._store.clear()
    
    487
    +        self.enable_sort(False)
    
    488
    +
    
    489
    +        self._contact_refs = {}
    
    490
    +        self._group_refs = {}
    
    491
    +
    
    492
    +    def invalidate_sort(self) -> None:
    
    493
    +        self.enable_sort(False)
    
    494
    +        self.enable_sort(True)
    
    495
    +
    
    448 496
         def _redraw(self) -> None:
    
    449 497
             self._roster.set_model(None)
    
    450 498
             self._roster.set_model(self._store)
    
    ... ... @@ -530,8 +578,3 @@ def _get_total_user_count(self) -> int:
    530 578
     
    
    531 579
         def _on_theme_update(self, _event: ApplicationEvent) -> None:
    
    532 580
             self._redraw()
    533
    -
    
    534
    -    def reset(self) -> None:
    
    535
    -        self._contact_refs = {}
    
    536
    -        self._group_refs = {}
    
    537
    -        self._store.clear()