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

Commits:

16 changed files:

Changes:

  • gajim/gtk/application.py
    ... ... @@ -787,14 +787,14 @@ def _on_remove_history_action(_action: Gio.SimpleAction,
    787 787
                                       params: structs.RemoveHistoryActionParams
    
    788 788
                                       ) -> None:
    
    789 789
             def _remove() -> None:
    
    790
    +            app.storage.archive.remove_history(params.account, params.jid)
    
    791
    +            control = app.window.get_control()
    
    790 792
                 if params.jid is not None:
    
    791
    -                app.storage.archive.remove_history(params.account, params.jid)
    
    792
    -                control = app.window.get_control(params.account, params.jid)
    
    793
    -                if control is not None:
    
    794
    -                    control.reset_view()
    
    795
    -            else:
    
    796
    -                for control in app.window.get_controls(params.account):
    
    797
    -                    control.reset_view()
    
    793
    +                if not control.is_loaded(params.account, params.jid):
    
    794
    +                    return
    
    795
    +
    
    796
    +            control.reset_view()
    
    797
    +
    
    798 798
             ConfirmationDialog(
    
    799 799
                 _('Remove Chat History'),
    
    800 800
                 _('Remove Chat History?'),
    

  • gajim/gtk/chat_action_processor.py
    ... ... @@ -122,11 +122,13 @@ def _replace_text(self, selected_action: str) -> None:
    122 122
             self._buf.insert(start_iter, selected_action)
    
    123 123
     
    
    124 124
         def _get_commands(self) -> list[str]:
    
    125
    +        # TODO
    
    126
    +        return []
    
    127
    +
    
    125 128
             commands: list[str] = []
    
    126 129
             assert self._account
    
    127 130
             assert self._contact
    
    128
    -        control = app.window.get_control(self._account, self._contact.jid)
    
    129
    -        assert control is not None
    
    131
    +        control = app.window.get_control()
    
    130 132
             for command in control.list_commands():
    
    131 133
                 for name in command.names:
    
    132 134
                     commands.append(name)
    

  • gajim/gtk/chat_list.py
    ... ... @@ -819,8 +819,10 @@ def _get_unread_string(count: int) -> str:
    819 819
             return '999+'
    
    820 820
     
    
    821 821
         def add_unread(self, text: str) -> None:
    
    822
    -        control = app.window.get_control(self.account, self.jid)
    
    823
    -        if self.is_active and control is not None and control.get_autoscroll():
    
    822
    +        control = app.window.get_control()
    
    823
    +        if (self.is_active and
    
    824
    +                control.is_loaded(self.account, self.jid) and
    
    825
    +                control.get_autoscroll()):
    
    824 826
                 return
    
    825 827
     
    
    826 828
             self._unread_count += 1
    

  • gajim/gtk/chat_page.py
    ... ... @@ -35,7 +35,6 @@
    35 35
     from .chat_list import ChatList
    
    36 36
     from .chat_list_stack import ChatListStack
    
    37 37
     from .chat_stack import ChatStack
    
    38
    -from .control_stack import ControlStack
    
    39 38
     from .search_view import SearchView
    
    40 39
     from .types import ControlT
    
    41 40
     
    
    ... ... @@ -61,7 +60,7 @@ def __init__(self):
    61 60
             self._chat_stack = ChatStack()
    
    62 61
             self._ui.right_grid_overlay.add(self._chat_stack)
    
    63 62
     
    
    64
    -        self._control_stack = self._chat_stack.get_control_stack()
    
    63
    +        self._chat_control = self._chat_stack.get_chat_control()
    
    65 64
     
    
    66 65
             self._search_view = SearchView()
    
    67 66
             self._search_view.connect('hide-search', self._on_search_hide)
    
    ... ... @@ -124,9 +123,6 @@ def get_chat_list_stack(self) -> ChatListStack:
    124 123
         def get_chat_stack(self) -> ChatStack:
    
    125 124
             return self._chat_stack
    
    126 125
     
    
    127
    -    def get_control_stack(self) -> ControlStack:
    
    128
    -        return self._control_stack
    
    129
    -
    
    130 126
         @staticmethod
    
    131 127
         def _on_start_chat_clicked(_button: Gtk.Button) -> None:
    
    132 128
             app.app.activate_action('start-chat', GLib.Variant('s', ''))
    
    ... ... @@ -166,9 +162,10 @@ def _on_search_history(self,
    166 162
                                _action: Gio.SimpleAction,
    
    167 163
                                _param: Literal[None]) -> None:
    
    168 164
     
    
    169
    -        control = self.get_active_control()
    
    170
    -        if control is not None:
    
    171
    -            self._search_view.set_context(control.account, control.contact.jid)
    
    165
    +        if self._chat_control.has_active_chat():
    
    166
    +            self._search_view.set_context(self._chat_control.account,
    
    167
    +                                          self._chat_control.contact.jid)
    
    168
    +
    
    172 169
             self._search_view.clear()
    
    173 170
             self._search_revealer.set_reveal_child(True)
    
    174 171
             self._search_view.set_focus()
    
    ... ... @@ -232,16 +229,6 @@ def add_chat_for_workspace(self,
    232 229
                     self._chat_list_stack.select_chat(account, jid)
    
    233 230
                 return
    
    234 231
     
    
    235
    -        if type_ == 'groupchat':
    
    236
    -            self._control_stack.add_group_chat(account, jid)
    
    237
    -        elif type_ == 'pm':
    
    238
    -            if not self._startup_finished:
    
    239
    -                # TODO: Currently we can’t load private chats at start
    
    240
    -                # because the Contacts dont exist yet
    
    241
    -                return
    
    242
    -            self._control_stack.add_private_chat(account, jid)
    
    243
    -        else:
    
    244
    -            self._control_stack.add_chat(account, jid)
    
    245 232
             self._chat_list_stack.add_chat(workspace_id, account, jid, type_,
    
    246 233
                                            pinned)
    
    247 234
     
    
    ... ... @@ -285,7 +272,8 @@ def remove_chat(self, account: str, jid: JID) -> None:
    285 272
     
    
    286 273
         def _on_chat_removed(self, _chat_list: ChatList, account: str, jid: JID,
    
    287 274
                              type_: str) -> None:
    
    288
    -        self._control_stack.remove_chat(account, jid)
    
    275
    +
    
    276
    +        self._chat_control.clear()
    
    289 277
             if type_ == 'groupchat':
    
    290 278
                 client = app.get_client(account)
    
    291 279
                 client.get_module('MUC').leave(jid)
    
    ... ... @@ -296,23 +284,12 @@ def remove_chats_for_account(self, account: str) -> None:
    296 284
                 chat_list.unselect_all()
    
    297 285
     
    
    298 286
             self._chat_list_stack.remove_chats_for_account(account)
    
    299
    -        self._control_stack.remove_chats_for_account(account)
    
    300
    -
    
    301
    -    def get_control(self, account: str, jid: JID) -> Optional[ControlT]:
    
    302
    -        return self._control_stack.get_control(account, jid)
    
    303
    -
    
    304
    -    def get_active_control(self) -> Optional[ControlT]:
    
    305
    -        chat = self._chat_list_stack.get_selected_chat()
    
    306
    -        if chat is None:
    
    307
    -            return None
    
    308
    -        return self.get_control(chat.account, chat.jid)
    
    309
    -
    
    310
    -    def is_chat_loaded(self, account: str, jid: JID) -> bool:
    
    311
    -        return self._control_stack.is_chat_loaded(account, jid)
    
    287
    +        if self._chat_control.has_active_chat():
    
    288
    +            if self._chat_control.contact.account == account:
    
    289
    +                self._chat_control.clear()
    
    312 290
     
    
    313
    -    def get_controls(self, account: Optional[str]
    
    314
    -                     ) -> Generator[ControlT, None, None]:
    
    315
    -        return self._control_stack.get_controls(account)
    
    291
    +    def get_control(self) -> ControlT:
    
    292
    +        return self._chat_control
    
    316 293
     
    
    317 294
         def hide_search(self) -> bool:
    
    318 295
             if self._search_revealer.get_reveal_child():
    

  • gajim/gtk/chat_stack.py
    ... ... @@ -44,8 +44,7 @@
    44 44
     from .chat_banner import ChatBanner
    
    45 45
     from .chat_function_page import ChatFunctionPage
    
    46 46
     from .const import TARGET_TYPE_URI_LIST
    
    47
    -from .control_stack import ControlStack
    
    48
    -from .controls.groupchat import GroupchatControl
    
    47
    +from .control import ChatControl
    
    49 48
     from .message_actions_box import MessageActionsBox
    
    50 49
     from .util import EventHelper, open_window
    
    51 50
     
    
    ... ... @@ -70,13 +69,13 @@ def __init__(self):
    70 69
             self.add_named(self._chat_function_page, 'function')
    
    71 70
     
    
    72 71
             self._chat_banner = ChatBanner()
    
    73
    -        self._control_stack = ControlStack()
    
    72
    +        self._chat_control = ChatControl()
    
    74 73
             self._message_action_box = MessageActionsBox()
    
    75 74
     
    
    76 75
             box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
    
    77 76
             box.add(self._chat_banner)
    
    78 77
             box.add(Gtk.Separator())
    
    79
    -        box.add(self._control_stack)
    
    78
    +        box.add(self._chat_control)
    
    80 79
             box.add(Gtk.Separator())
    
    81 80
             box.add(self._message_action_box)
    
    82 81
     
    
    ... ... @@ -139,8 +138,8 @@ def process_escape(self) -> bool:
    139 138
                 return True
    
    140 139
             return False
    
    141 140
     
    
    142
    -    def get_control_stack(self) -> ControlStack:
    
    143
    -        return self._control_stack
    
    141
    +    def get_chat_control(self) -> ChatControl:
    
    142
    +        return self._chat_control
    
    144 143
     
    
    145 144
         def get_message_action_box(self) -> MessageActionsBox:
    
    146 145
             return self._message_action_box
    
    ... ... @@ -157,7 +156,7 @@ def show_chat(self, account: str, jid: JID) -> None:
    157 156
             self._current_contact = client.get_module('Contacts').get_contact(jid)
    
    158 157
     
    
    159 158
             self._chat_banner.switch_contact(account, jid)
    
    160
    -        self._control_stack.show_chat(account, jid)
    
    159
    +        self._chat_control.switch_contact(self._current_contact)
    
    161 160
             self._message_action_box.switch_contact(self._current_contact)
    
    162 161
     
    
    163 162
             self._update_base_actions(self._current_contact)
    
    ... ... @@ -409,7 +408,6 @@ def _on_action(self,
    409 408
             account = contact.account
    
    410 409
             client = app.get_client(account)
    
    411 410
             jid = contact.jid
    
    412
    -        current_control = self._control_stack.get_current_control()
    
    413 411
     
    
    414 412
             if action_name == 'send-message':
    
    415 413
                 self._on_send_message()
    
    ... ... @@ -442,8 +440,7 @@ def _on_action(self,
    442 440
                 open_window('AddContact', account=account, jid=jid)
    
    443 441
     
    
    444 442
             elif action_name == 'clear-chat':
    
    445
    -            assert current_control is not None
    
    446
    -            current_control.reset_view()
    
    443
    +            self._chat_control.reset_view()
    
    447 444
     
    
    448 445
             elif action_name == 'show-contact-info':
    
    449 446
                 if isinstance(contact, GroupchatContact):
    
    ... ... @@ -466,7 +463,6 @@ def _on_action(self,
    466 463
                 self._show_chat_function_page('change-nickname')
    
    467 464
     
    
    468 465
             elif action_name == 'muc-execute-command':
    
    469
    -            assert isinstance(current_control, GroupchatControl)
    
    470 466
                 nick = None
    
    471 467
                 if param is not None:
    
    472 468
                     nick = param.get_string()
    
    ... ... @@ -477,13 +473,11 @@ def _on_action(self,
    477 473
                 open_window('AdHocCommands', account=account, jid=jid)
    
    478 474
     
    
    479 475
             elif action_name == 'muc-kick':
    
    480
    -            assert isinstance(current_control, GroupchatControl)
    
    481 476
                 assert param is not None
    
    482 477
                 kick_nick = param.get_string()
    
    483 478
                 self._show_chat_function_page('kick', data=kick_nick)
    
    484 479
     
    
    485 480
             elif action_name == 'muc-ban':
    
    486
    -            assert isinstance(current_control, GroupchatControl)
    
    487 481
                 assert param is not None
    
    488 482
                 ban_jid = param.get_string()
    
    489 483
                 self._show_chat_function_page('ban', data=ban_jid)
    
    ... ... @@ -519,9 +513,8 @@ def _on_drag_data_received(self,
    519 513
     
    
    520 514
             # TODO: Contact drag and drop for invitations (AdHoc MUC/MUC)
    
    521 515
             if target_type == TARGET_TYPE_URI_LIST:
    
    522
    -            control = app.window.get_active_control()
    
    523
    -            if control is not None:
    
    524
    -                control.drag_data_file_transfer(selection)
    
    516
    +            if self._chat_control.has_active_chat():
    
    517
    +                self._chat_control.drag_data_file_transfer(selection)
    
    525 518
     
    
    526 519
         def _on_drag_leave(self,
    
    527 520
                            _widget: Gtk.Widget,
    
    ... ... @@ -569,17 +562,13 @@ def _on_function_message(self,
    569 562
                                  _function_page: ChatFunctionPage,
    
    570 563
                                  message: str
    
    571 564
                                  ) -> None:
    
    572
    -        control = self._control_stack.get_current_control()
    
    573
    -        assert control is not None
    
    574
    -        control.add_info_message(message)
    
    565
    +
    
    566
    +        self._chat_control.add_info_message(message)
    
    575 567
     
    
    576 568
         def _on_send_message(self) -> None:
    
    577 569
             self._message_action_box.msg_textview.replace_emojis()
    
    578 570
             message = self._message_action_box.msg_textview.get_text()
    
    579 571
     
    
    580
    -        control = self._control_stack.get_current_control()
    
    581
    -        assert control is not None
    
    582
    -
    
    583 572
             contact = self._current_contact
    
    584 573
             assert contact is not None
    
    585 574
     
    
    ... ... @@ -588,7 +577,7 @@ def _on_send_message(self) -> None:
    588 577
                 self.sendmessage = True
    
    589 578
                 app.plugin_manager.extension_point(
    
    590 579
                     'send_message' + encryption,
    
    591
    -                control)
    
    580
    +                self._chat_control)
    
    592 581
                 if not self.sendmessage:
    
    593 582
                     return
    
    594 583
     
    
    ... ... @@ -622,7 +611,7 @@ def _on_send_message(self) -> None:
    622 611
                                        type_=type_,
    
    623 612
                                        chatstate=chatstate,
    
    624 613
                                        label=label,
    
    625
    -                                   control=control,
    
    614
    +                                   control=self._chat_control,
    
    626 615
                                        correct_id=correct_id)
    
    627 616
     
    
    628 617
             client.send_message(message_)
    
    ... ... @@ -647,12 +636,12 @@ def clear(self) -> None:
    647 636
             self.set_visible_child_name('empty')
    
    648 637
             self._chat_banner.clear()
    
    649 638
             self._message_action_box.clear()
    
    650
    -        self._control_stack.clear()
    
    639
    +        self._chat_control.clear()
    
    651 640
     
    
    652 641
         def process_event(self, event: Any) -> None:
    
    653 642
             if isinstance(event, MucDiscoUpdate):
    
    654 643
                 self._on_muc_disco_update(event)
    
    655
    -        self._control_stack.process_event(event)
    
    644
    +        self._chat_control.process_event(event)
    
    656 645
             self._message_action_box.process_event(event)
    
    657 646
     
    
    658 647
     
    

  • gajim/gtk/control.py
    ... ... @@ -22,7 +22,6 @@
    22 22
     import os
    
    23 23
     import logging
    
    24 24
     import time
    
    25
    -import uuid
    
    26 25
     
    
    27 26
     from gi.repository import Gtk
    
    28 27
     from gi.repository import GLib
    
    ... ... @@ -36,11 +35,9 @@
    36 35
     
    
    37 36
     from gajim.common import app
    
    38 37
     from gajim.common import events
    
    39
    -from gajim.common import ged
    
    40 38
     from gajim.common import helpers
    
    41 39
     from gajim.common.helpers import message_needs_highlight
    
    42 40
     from gajim.common.helpers import get_file_path_from_dnd_dropped_uri
    
    43
    -from gajim.common.helpers import event_filter
    
    44 41
     from gajim.common.helpers import to_user_string
    
    45 42
     from gajim.common.i18n import _
    
    46 43
     from gajim.common.ged import EventHelper
    
    ... ... @@ -66,33 +63,24 @@
    66 63
     from gajim.gui.groupchat_roster import GroupchatRoster
    
    67 64
     from gajim.gui.groupchat_state import GroupchatState
    
    68 65
     
    
    69
    -log = logging.getLogger('gajim.gui.controls.base')
    
    66
    +log = logging.getLogger('gajim.gui.control')
    
    70 67
     
    
    71 68
     
    
    72 69
     class ChatControl(EventHelper):
    
    73
    -
    
    74
    -    _type: Optional[ControlType] = None
    
    75
    -
    
    76
    -    def __init__(self, widget_name: str, account: str, jid: JID) -> None:
    
    70
    +    def __init__(self) -> None:
    
    77 71
             EventHelper.__init__(self)
    
    78 72
     
    
    79 73
             self.handlers: dict[int, Any] = {}
    
    80 74
     
    
    81
    -        self.account = account
    
    82
    -
    
    83
    -        self._client = app.get_client(account)
    
    84
    -
    
    85
    -        groupchat = self._type != ControlType.CHAT
    
    86
    -        self.contact = self._client.get_module('Contacts').get_contact(
    
    87
    -            jid, groupchat=groupchat)
    
    88
    -
    
    89
    -        self.control_id: str = str(uuid.uuid4())
    
    75
    +        self.account = None
    
    76
    +        self.contact = None
    
    77
    +        self._client = None
    
    90 78
     
    
    91 79
             self.xml = get_builder('chat_control.ui')
    
    92 80
             self.widget = cast(Gtk.Box, self.xml.get_object('control_box'))
    
    93 81
     
    
    94 82
             # Create ConversationView and connect signals
    
    95
    -        self.conversation_view = ConversationView(self.account, self.contact)
    
    83
    +        self.conversation_view = ConversationView()
    
    96 84
     
    
    97 85
             self._scrolled_view = ScrolledView()
    
    98 86
             self._scrolled_view.add(self.conversation_view)
    
    ... ... @@ -100,7 +88,7 @@ def __init__(self, widget_name: str, account: str, jid: JID) -> None:
    100 88
     
    
    101 89
             self.xml.conv_view_overlay.add(self._scrolled_view)
    
    102 90
     
    
    103
    -        self._jump_to_end_button = JumpToEndButton(self.contact)
    
    91
    +        self._jump_to_end_button = JumpToEndButton()
    
    104 92
             self._jump_to_end_button.connect('clicked', self._on_jump_to_end)
    
    105 93
             self.xml.conv_view_overlay.add_overlay(self._jump_to_end_button)
    
    106 94
     
    
    ... ... @@ -115,12 +103,6 @@ def __init__(self, widget_name: str, account: str, jid: JID) -> None:
    115 103
             # XEP-0333 Chat Markers
    
    116 104
             self.last_msg_id: Optional[str] = None
    
    117 105
     
    
    118
    -        # XEP-0172 User Nickname
    
    119
    -        # TODO:
    
    120
    -        self.user_nick: Optional[str] = None
    
    121
    -
    
    122
    -        self._client.get_module('Chatstate').set_active(self.contact)
    
    123
    -
    
    124 106
             self.encryption: Optional[str] = self.get_encryption_state()
    
    125 107
             self.conversation_view.encryption_enabled = self.encryption is not None
    
    126 108
     
    
    ... ... @@ -151,12 +133,33 @@ def __init__(self, widget_name: str, account: str, jid: JID) -> None:
    151 133
     
    
    152 134
             # self._set_control_inactive()
    
    153 135
     
    
    136
    +    def is_loaded(self, account: str, jid: JID) -> bool:
    
    137
    +        if self.contact is None:
    
    138
    +            return False
    
    139
    +        return self.contact.account == account and self.contact.jid == jid
    
    140
    +
    
    141
    +    def has_active_chat(self) -> bool:
    
    142
    +        return self.contact is not None
    
    143
    +
    
    144
    +    def clear(self) -> None:
    
    145
    +        self.contact.disconnect_all_from_obj(self)
    
    146
    +        self.contact = None
    
    147
    +        self._clinet = None
    
    148
    +        self.account = None
    
    149
    +        self.reset_view()
    
    150
    +
    
    154 151
         def switch_contact(self, contact: Union[BareContact,
    
    155 152
                                                 GroupchatContact,
    
    156 153
                                                 GroupchatParticipant]) -> None:
    
    157 154
     
    
    158 155
             self.contact.disconnect_all_from_obj(self)
    
    159 156
             self.contact = contact
    
    157
    +        self.account = contact.account
    
    158
    +
    
    159
    +        self._client = app.get_client(contact.account)
    
    160
    +
    
    161
    +        self._jump_to_end_button.switch_contact(contact)
    
    162
    +        self.conversation_view.switch_contact(contact)
    
    160 163
     
    
    161 164
             if isinstance(self.contact, GroupchatParticipant):
    
    162 165
                 self.contact.multi_connect({
    
    ... ... @@ -184,6 +187,8 @@ def switch_contact(self, contact: Union[BareContact,
    184 187
                     'room-subject': self._on_room_subject,
    
    185 188
                 })
    
    186 189
     
    
    190
    +        self._client.get_module('Chatstate').set_active(self.contact)
    
    191
    +
    
    187 192
         def process_event(self, event: events.ApplicationEvent) -> None:
    
    188 193
             if event.account != self.account:
    
    189 194
                 return
    
    ... ... @@ -338,7 +343,7 @@ def get_our_nick(self) -> str:
    338 343
     
    
    339 344
         def _allow_add_message(self) -> bool:
    
    340 345
             # Only add messages if the view is already populated
    
    341
    -        return self.is_chat_loaded and self._scrolled_view.get_lower_complete()
    
    346
    +        return self._chat_loaded and self._scrolled_view.get_lower_complete()
    
    342 347
     
    
    343 348
         def add_info_message(self, text: str) -> None:
    
    344 349
             self.conversation_view.add_info_message(text)
    
    ... ... @@ -491,10 +496,6 @@ def load_messages(self) -> None:
    491 496
     
    
    492 497
             self.fetch_n_lines_history(self._scrolled_view, True, 20)
    
    493 498
     
    
    494
    -    @property
    
    495
    -    def is_chat_loaded(self) -> bool:
    
    496
    -        return self._chat_loaded
    
    497
    -
    
    498 499
         def reset_view(self) -> None:
    
    499 500
             self._chat_loaded = False
    
    500 501
             self.conversation_view.clear()
    

  • gajim/gtk/control_stack.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 typing import Any
    
    16
    -from typing import Optional
    
    17
    -from typing import Generator
    
    18
    -
    
    19
    -import logging
    
    20
    -
    
    21
    -from gi.repository import Gtk
    
    22
    -
    
    23
    -from nbxmpp import JID
    
    24
    -
    
    25
    -from .controls.chat import ChatControl
    
    26
    -from .controls.groupchat import GroupchatControl
    
    27
    -from .controls.private import PrivateChatControl
    
    28
    -
    
    29
    -from .types import ControlT
    
    30
    -
    
    31
    -log = logging.getLogger('gajim.gui.controlstack')
    
    32
    -
    
    33
    -
    
    34
    -class ControlStack(Gtk.Stack):
    
    35
    -    def __init__(self):
    
    36
    -        Gtk.Stack.__init__(self)
    
    37
    -        self.set_vexpand(True)
    
    38
    -        self.set_hexpand(True)
    
    39
    -
    
    40
    -        self.add_named(Gtk.Box(), 'empty')
    
    41
    -
    
    42
    -        self.show_all()
    
    43
    -        self._controls: dict[tuple[str, JID], ControlT] = {}
    
    44
    -        self._current_control: Optional[ControlT] = None
    
    45
    -
    
    46
    -    def get_control(self, account: str, jid: JID) -> Optional[ControlT]:
    
    47
    -        try:
    
    48
    -            return self._controls[(account, jid)]
    
    49
    -        except KeyError:
    
    50
    -            return None
    
    51
    -
    
    52
    -    def get_current_control(self) -> Optional[ControlT]:
    
    53
    -        return self._current_control
    
    54
    -
    
    55
    -    def get_controls(self, account: Optional[str]
    
    56
    -                     ) -> Generator[ControlT, None, None]:
    
    57
    -        if account is None:
    
    58
    -            for control in self._controls.values():
    
    59
    -                yield control
    
    60
    -            return
    
    61
    -
    
    62
    -        for key, control in self._controls.items():
    
    63
    -            if key[0] == account:
    
    64
    -                yield control
    
    65
    -
    
    66
    -    def add_chat(self, account: str, jid: JID) -> None:
    
    67
    -        if self._controls.get((account, jid)) is not None:
    
    68
    -            # Control is already in the Stack
    
    69
    -            return
    
    70
    -
    
    71
    -        chat_control = ChatControl(account, jid)
    
    72
    -        self._controls[(account, jid)] = chat_control
    
    73
    -        self.add_named(chat_control.widget, f'{account}:{jid}')
    
    74
    -        chat_control.widget.show_all()
    
    75
    -
    
    76
    -    def add_group_chat(self, account: str, jid: JID) -> None:
    
    77
    -        if self._controls.get((account, jid)) is not None:
    
    78
    -            return
    
    79
    -
    
    80
    -        control = GroupchatControl(account, jid)
    
    81
    -        self._controls[(account, jid)] = control
    
    82
    -        self.add_named(control.widget, f'{account}:{jid}')
    
    83
    -        control.widget.show_all()
    
    84
    -
    
    85
    -    def add_private_chat(self, account: str, jid: JID) -> None:
    
    86
    -        control = PrivateChatControl(account, jid)
    
    87
    -        self._controls[(account, jid)] = control
    
    88
    -        self.add_named(control.widget, f'{account}:{jid}')
    
    89
    -        control.widget.show_all()
    
    90
    -
    
    91
    -    def remove_chat(self, account: str, jid: JID) -> None:
    
    92
    -        control = self._controls.pop((account, jid))
    
    93
    -        self.remove(control.widget)
    
    94
    -        control.shutdown()
    
    95
    -
    
    96
    -    def show_chat(self, account: str, jid: JID) -> None:
    
    97
    -        new_name = f'{account}:{jid}'
    
    98
    -        current_name = self.get_visible_child_name()
    
    99
    -        assert current_name is not None
    
    100
    -
    
    101
    -        if current_name == new_name:
    
    102
    -            return
    
    103
    -
    
    104
    -        control = self.get_control(account, jid)
    
    105
    -        if control is None:
    
    106
    -            log.warning('No Control found for %s, %s', account, jid)
    
    107
    -            return
    
    108
    -
    
    109
    -        if self._current_control is not None:
    
    110
    -            self._current_control.reset_view()
    
    111
    -
    
    112
    -        control.load_messages()
    
    113
    -        self._current_control = control
    
    114
    -
    
    115
    -        self.set_visible_child_name(new_name)
    
    116
    -
    
    117
    -    def is_chat_loaded(self, account: str, jid: JID) -> bool:
    
    118
    -        control = self.get_control(account, jid)
    
    119
    -        if control is None:
    
    120
    -            return False
    
    121
    -        return control.is_chat_loaded
    
    122
    -
    
    123
    -    def clear(self) -> None:
    
    124
    -        self.set_visible_child_name('empty')
    
    125
    -        self._current_control = None
    
    126
    -
    
    127
    -    def process_event(self, event: Any) -> None:
    
    128
    -        control = self.get_control(event.account, event.jid)
    
    129
    -        if control is None:
    
    130
    -            return
    
    131
    -        control.process_event(event)
    
    132
    -
    
    133
    -    def remove_chats_for_account(self, account: str) -> None:
    
    134
    -        for chat_account, jid in list(self._controls.keys()):
    
    135
    -            if chat_account != account:
    
    136
    -                continue
    
    137
    -            self.remove_chat(account, jid)

  • gajim/gtk/conversation/jump_to_end_button.py
    ... ... @@ -36,15 +36,13 @@ class JumpToEndButton(Gtk.Overlay):
    36 36
                 ()),
    
    37 37
         }
    
    38 38
     
    
    39
    -    def __init__(self, contact: ContactT) -> None:
    
    39
    +    def __init__(self) -> None:
    
    40 40
             Gtk.Overlay.__init__(self)
    
    41 41
             self.set_halign(Gtk.Align.END)
    
    42 42
             self.set_valign(Gtk.Align.END)
    
    43 43
             self.set_margin_end(6)
    
    44 44
             self.set_margin_bottom(12)
    
    45 45
     
    
    46
    -        self._contact = contact
    
    47
    -
    
    48 46
             icon = Gtk.Image.new_from_icon_name(
    
    49 47
                 'go-bottom-symbolic', Gtk.IconSize.BUTTON)
    
    50 48
             icon.set_margin_start(4)
    
    ... ... @@ -59,13 +57,7 @@ def __init__(self, contact: ContactT) -> None:
    59 57
             self.add(button)
    
    60 58
     
    
    61 59
             self._unread_label = Gtk.Label()
    
    62
    -        self._unread_label.get_style_context().add_class(
    
    63
    -            'unread-counter')
    
    64
    -
    
    65
    -        if isinstance(contact, GroupchatContact) and not contact.can_notify():
    
    66
    -            self._unread_label.get_style_context().add_class(
    
    67
    -                'unread-counter-silent')
    
    68
    -
    
    60
    +        self._unread_label.get_style_context().add_class('unread-counter')
    
    69 61
             self._unread_label.set_no_show_all(True)
    
    70 62
             self._unread_label.set_halign(Gtk.Align.END)
    
    71 63
             self._unread_label.set_valign(Gtk.Align.START)
    
    ... ... @@ -76,6 +68,14 @@ def __init__(self, contact: ContactT) -> None:
    76 68
             self._count = 0
    
    77 69
             self.set_no_show_all(True)
    
    78 70
     
    
    71
    +    def switch_contact(self, contact: ContactT) -> None:
    
    72
    +        if isinstance(contact, GroupchatContact) and not contact.can_notify():
    
    73
    +            self._unread_label.get_style_context().add_class(
    
    74
    +                'unread-counter-silent')
    
    75
    +        else:
    
    76
    +            self._unread_label.get_style_context().remove_class(
    
    77
    +                'unread-counter-silent')
    
    78
    +
    
    79 79
         def _on_jump_clicked(self, _button: Gtk.Button) -> None:
    
    80 80
             self.reset_unread_count()
    
    81 81
             self.emit('clicked')
    

  • gajim/gtk/groupchat_roster.py
    ... ... @@ -42,7 +42,7 @@
    42 42
     from .menus import get_groupchat_roster_menu
    
    43 43
     from .tooltips import GCTooltip
    
    44 44
     from .builder import get_builder
    
    45
    -from .types import GroupchatControlT
    
    45
    +from .types import ControlT
    
    46 46
     from .util import EventHelper
    
    47 47
     
    
    48 48
     
    
    ... ... @@ -75,7 +75,7 @@ class GroupchatRoster(Gtk.ScrolledWindow, EventHelper):
    75 75
         def __init__(self,
    
    76 76
                      account: str,
    
    77 77
                      room_jid: str,
    
    78
    -                 control: GroupchatControlT
    
    78
    +                 control: ControlT
    
    79 79
                      ) -> None:
    
    80 80
             Gtk.ScrolledWindow.__init__(self)
    
    81 81
             EventHelper.__init__(self)
    
    ... ... @@ -84,7 +84,6 @@ def __init__(self,
    84 84
             self._account = account
    
    85 85
             self.room_jid = room_jid
    
    86 86
             self._control = control
    
    87
    -        self._control_id = control.control_id
    
    88 87
             self._tooltip = GCTooltip()
    
    89 88
             self._group_chat_contact = cast(GroupchatContact, control.contact)
    
    90 89
             self._group_chat_contact.connect(
    

  • gajim/gtk/main.py
    ... ... @@ -409,8 +409,8 @@ def _on_action(self,
    409 409
             if action_name == 'escape' and chat_stack.process_escape():
    
    410 410
                 return None
    
    411 411
     
    
    412
    -        control = self.get_active_control()
    
    413
    -        if control is not None:
    
    412
    +        control = self.get_control()
    
    413
    +        if control.has_active_chat():
    
    414 414
                 if action_name == 'change-nickname':
    
    415 415
                     app.window.activate_action('muc-change-nickname', None)
    
    416 416
                     return None
    
    ... ... @@ -487,8 +487,8 @@ def _on_window_motion_notify(self,
    487 487
                                      _widget: Gtk.ApplicationWindow,
    
    488 488
                                      _event: Gdk.EventMotion
    
    489 489
                                      ) -> None:
    
    490
    -        control = self.get_active_control()
    
    491
    -        if control is None:
    
    490
    +        control = self.get_control()
    
    491
    +        if not control.has_active_chat():
    
    492 492
                 return
    
    493 493
     
    
    494 494
             if self.get_property('has-toplevel-focus'):
    
    ... ... @@ -759,18 +759,8 @@ def add_app_message(self,
    759 759
                             message: Optional[str] = None) -> None:
    
    760 760
             self._app_page.add_app_message(category, message)
    
    761 761
     
    
    762
    -    def get_control(self, account: str, jid: JID) -> Optional[ControlT]:
    
    763
    -        return self._chat_page.get_control(account, jid)
    
    764
    -
    
    765
    -    def get_controls(self, account: Optional[str] = None
    
    766
    -                     ) -> Generator[ControlT, None, None]:
    
    767
    -        return self._chat_page.get_controls(account)
    
    768
    -
    
    769
    -    def get_active_control(self) -> Optional[ControlT]:
    
    770
    -        return self._chat_page.get_active_control()
    
    771
    -
    
    772
    -    def is_chat_loaded(self, account: str, jid: JID) -> bool:
    
    773
    -        return self._chat_page.is_chat_loaded(account, jid)
    
    762
    +    def get_control(self) -> ControlT:
    
    763
    +        return self._chat_page.get_control()
    
    774 764
     
    
    775 765
         def chat_exists(self, account: str, jid: JID) -> bool:
    
    776 766
             return self._chat_page.chat_exists(account, jid)
    
    ... ... @@ -805,6 +795,8 @@ def get_chat_unread_count(self,
    805 795
     
    
    806 796
         def mark_as_read(self, account: str, jid: JID,
    
    807 797
                          send_marker: bool = True) -> None:
    
    798
    +        # TODO
    
    799
    +        return
    
    808 800
             set_urgency_hint(self, False)
    
    809 801
             control = self.get_control(account, jid)
    
    810 802
             if control is not None:
    
    ... ... @@ -824,8 +816,8 @@ def _on_window_active(self,
    824 816
                 return
    
    825 817
     
    
    826 818
             set_urgency_hint(self, False)
    
    827
    -        control = self.get_active_control()
    
    828
    -        if control is None:
    
    819
    +        control = self.get_control()
    
    820
    +        if not control.has_active_chat():
    
    829 821
                 return
    
    830 822
     
    
    831 823
             if control.get_autoscroll():
    
    ... ... @@ -933,6 +925,9 @@ def _on_message_received(self, event: events.MessageReceived) -> None:
    933 925
             self._main_stack.process_event(event)
    
    934 926
     
    
    935 927
         def _on_read_state_sync(self, event: events.ReadStateSync) -> None:
    
    928
    +        # TODO
    
    929
    +        return
    
    930
    +
    
    936 931
             if event.is_muc_pm:
    
    937 932
                 jid = JID.from_string(event.jid.bare)
    
    938 933
             else:
    

  • gajim/gtk/message_actions_box.py
    ... ... @@ -318,7 +318,7 @@ def _change_encryption(self,
    318 318
                     # TODO: Add GUI error here
    
    319 319
                     return
    
    320 320
     
    
    321
    -            if not plugin.activate_encryption(app.window.get_active_control()):
    
    321
    +            if not plugin.activate_encryption(app.window.get_control()):
    
    322 322
                     return
    
    323 323
     
    
    324 324
             self._set_encryption_state(new_state)
    
    ... ... @@ -360,7 +360,7 @@ def _on_encryption_details_clicked(self, _button: Gtk.Button) -> None:
    360 360
             contact = self.get_current_contact()
    
    361 361
             encryption = contact.settings.get('encryption')
    
    362 362
             app.plugin_manager.extension_point(
    
    363
    -            f'encryption_dialog{encryption}', app.window.get_active_control())
    
    363
    +            f'encryption_dialog{encryption}', app.window.get_control())
    
    364 364
     
    
    365 365
         def _set_settings_menu(self, contact: ChatContactT) -> None:
    
    366 366
             if isinstance(contact, GroupchatContact):
    
    ... ... @@ -559,8 +559,8 @@ def _on_msg_textview_key_press_event(self,
    559 559
                         return True
    
    560 560
     
    
    561 561
                 if event.keyval in (Gdk.KEY_Page_Down, Gdk.KEY_Page_Up):
    
    562
    -                control = app.window.get_active_control()
    
    563
    -                if control is not None:
    
    562
    +                control = app.window.get_control()
    
    563
    +                if control.has_active_chat():
    
    564 564
                         control.conversation_view.event(event)
    
    565 565
                         return True
    
    566 566
     
    

  • gajim/gtk/preferences.py
    ... ... @@ -40,7 +40,6 @@
    40 40
     from .const import Setting
    
    41 41
     from .const import SettingKind
    
    42 42
     from .const import SettingType
    
    43
    -from .controls.groupchat import GroupchatControl
    
    44 43
     from .dialogs import DialogButton
    
    45 44
     from .dialogs import ConfirmationDialog
    
    46 45
     from .emoji_chooser import emoji_chooser
    
    ... ... @@ -320,6 +319,8 @@ def _speller_available() -> bool:
    320 319
     
    
    321 320
         @staticmethod
    
    322 321
         def _on_use_speller(value: bool, *args: Any) -> None:
    
    322
    +        # TODO
    
    323
    +        return
    
    323 324
             if not value:
    
    324 325
                 return
    
    325 326
     
    
    ... ... @@ -401,17 +402,19 @@ def __init__(self, *args: Any) -> None:
    401 402
     
    
    402 403
         @staticmethod
    
    403 404
         def _on_sort_by_show_in_muc(_value: bool, *args: Any) -> None:
    
    405
    +        # TODO
    
    406
    +        return
    
    404 407
             for ctrl in app.window.get_controls():
    
    405 408
                 if ctrl.is_groupchat:
    
    406
    -                assert isinstance(ctrl, GroupchatControl)
    
    407 409
                     if ctrl.roster is not None:
    
    408 410
                         ctrl.roster.invalidate_sort()
    
    409 411
     
    
    410 412
         @staticmethod
    
    411 413
         def _on_show_status_in_roster(_value: bool, *args: Any) -> None:
    
    414
    +        # TODO
    
    415
    +        return
    
    412 416
             for ctrl in app.window.get_controls():
    
    413 417
                 if ctrl.is_groupchat:
    
    414
    -                assert isinstance(ctrl, GroupchatControl)
    
    415 418
                     if ctrl.roster is not None:
    
    416 419
                         ctrl.roster.draw_contacts()
    
    417 420
     
    
    ... ... @@ -755,6 +758,8 @@ def _on_emoticons_theme(self, *args: Any) -> None:
    755 758
     
    
    756 759
         @staticmethod
    
    757 760
         def _toggle_emoticons() -> None:
    
    761
    +        # TODO
    
    762
    +        return
    
    758 763
             for ctrl in app.window.get_controls():
    
    759 764
                 ctrl.toggle_emoticons()
    
    760 765
     
    

  • gajim/gtk/search_view.py
    ... ... @@ -307,8 +307,8 @@ def _select_date(self,
    307 307
             self._ui.calendar.select_day(date.day)
    
    308 308
     
    
    309 309
         def _scroll_to_date(self, date: datetime) -> None:
    
    310
    -        control = app.window.get_active_control()
    
    311
    -        if control is None:
    
    310
    +        control = app.window.get_control()
    
    311
    +        if not control.has_active_chat():
    
    312 312
                 return
    
    313 313
             if control.contact.jid == self._jid:
    
    314 314
                 meta_row = app.storage.archive.get_first_message_meta_for_date(
    
    ... ... @@ -336,8 +336,8 @@ def _get_accounts() -> dict[int, str]:
    336 336
     
    
    337 337
         @staticmethod
    
    338 338
         def _on_row_activated(_listbox: SearchView, row: ResultRow) -> None:
    
    339
    -        control = app.window.get_active_control()
    
    340
    -        if control is not None:
    
    339
    +        control = app.window.get_control()
    
    340
    +        if control.has_active_chat():
    
    341 341
                 if control.contact.jid == row.jid:
    
    342 342
                     control.scroll_to_message(row.log_line_id, row.timestamp)
    
    343 343
                     return
    
    ... ... @@ -346,8 +346,8 @@ def _on_row_activated(_listbox: SearchView, row: ResultRow) -> None:
    346 346
             # TODO: type 'pm' is KindConstant.CHAT_MSG_RECV, too
    
    347 347
             jid = JID.from_string(row.jid)
    
    348 348
             app.window.add_chat(row.account, jid, row.type, select=True)
    
    349
    -        control = app.window.get_active_control()
    
    350
    -        if control is not None:
    
    349
    +        control = app.window.get_control()
    
    350
    +        if control.has_active_chat():
    
    351 351
                 control.scroll_to_message(row.log_line_id, row.timestamp)
    
    352 352
     
    
    353 353
         def set_focus(self) -> None:
    

  • gajim/gtk/types.py
    ... ... @@ -19,10 +19,7 @@
    19 19
     
    
    20 20
     
    
    21 21
     if TYPE_CHECKING:
    
    22
    -    from .controls.base import BaseControl
    
    23
    -    from .controls.chat import ChatControl
    
    24
    -    from .controls.private import PrivateChatControl
    
    25
    -    from .controls.groupchat import GroupchatControl
    
    22
    +    from .control import ChatControl
    
    26 23
         from .conversation.message_widget import MessageWidget  # noqa: F401
    
    27 24
         from .conversation.view import ConversationView  # noqa: F401
    
    28 25
         from .conversation.rows.call import CallRow  # noqa: F401
    
    ... ... @@ -39,13 +36,6 @@
    39 36
         from .conversation.rows.scroll_hint import ScrollHintRow  # noqa: F401
    
    40 37
     
    
    41 38
     
    
    42
    -ControlT = Union[
    
    43
    -    'BaseControl',
    
    44
    -    'ChatControl',
    
    45
    -    'PrivateChatControl',
    
    46
    -    'GroupchatControl'
    
    47
    -]
    
    48
    -
    
    49
    -GroupchatControlT = Union['GroupchatControl']
    
    39
    +ControlT = Union['ChatControl']
    
    50 40
     
    
    51 41
     ConversationViewT = Union['ConversationView']

  • gajim/gui_interface.py
    ... ... @@ -403,13 +403,12 @@ def start_file_transfer(self,
    403 403
                 if method is None:
    
    404 404
                     return
    
    405 405
     
    
    406
    -        current_control = app.window.get_active_control()
    
    407
    -        if current_control is None:
    
    406
    +        if app.window.get_control().has_active_chat():
    
    408 407
                 return
    
    409 408
     
    
    410 409
             if path is None:
    
    411 410
                 if method == 'httpupload':
    
    412
    -                self.send_httpupload(current_control)
    
    411
    +                self.send_httpupload(control)
    
    413 412
                     return
    
    414 413
                 if method == 'jingle':
    
    415 414
                     self.instances['file_transfers'].show_file_send_request(
    
    ... ... @@ -417,7 +416,7 @@ def start_file_transfer(self,
    417 416
                 return
    
    418 417
     
    
    419 418
             if method == 'httpupload':
    
    420
    -            self.send_httpupload(current_control, path)
    
    419
    +            self.send_httpupload(control, path)
    
    421 420
             else:
    
    422 421
                 assert isinstance(contact, BareContact)
    
    423 422
                 send_callback = partial(
    
    ... ... @@ -457,6 +456,7 @@ def start_chat_from_jid(account: str,
    457 456
                                 jid: str,
    
    458 457
                                 message: Optional[str] = None
    
    459 458
                                 ) -> None:
    
    459
    +        # TODO
    
    460 460
             jid_ = JID.from_string(jid)
    
    461 461
             if app.window.chat_exists(account, jid_):
    
    462 462
                 app.window.select_chat(account, jid_)
    

  • pyrightconfig.json
    ... ... @@ -41,7 +41,7 @@
    41 41
             "gajim/common/sound.py",
    
    42 42
             "gajim/common/storage/",
    
    43 43
             "gajim/common/task_manager.py",
    
    44
    -        "gajim/gtk/controls/private.py",
    
    44
    +        "gajim/gtk/control.py",
    
    45 45
             "gajim/gtk/conversation",
    
    46 46
             "gajim/gtk/about.py",
    
    47 47
             "gajim/gtk/account_page.py",
    
    ... ... @@ -65,7 +65,6 @@
    65 65
             "gajim/gtk/chat_list.py",
    
    66 66
             "gajim/gtk/chat_page.py",
    
    67 67
             "gajim/gtk/chat_stack.py",
    
    68
    -        "gajim/gtk/control_stack.py",
    
    69 68
             "gajim/gtk/const.py",
    
    70 69
             "gajim/gtk/contact_info.py",
    
    71 70
             "gajim/gtk/contact_settings.py",