From 892630bbccfdcb5f7579551e2d986159945890ae Mon Sep 17 00:00:00 2001
From: lovetox <philipp@hoerist.com>
Date: Tue, 27 Apr 2021 09:47:03 +0200
Subject: [PATCH] Refactor accessing muc data in controls

---
 gajim/chat_control.py                |  87 +++++---------
 gajim/chat_control_base.py           |  68 ++++-------
 gajim/common/modules/caps.py         |   6 +-
 gajim/common/modules/chat_markers.py |   8 +-
 gajim/common/modules/contacts.py     |  39 +++++-
 gajim/common/modules/muc.py          |   6 +-
 gajim/groupchat_control.py           | 171 +++++++++------------------
 gajim/gtk/chat_stack.py              |  15 +--
 gajim/privatechat_control.py         |   4 +-
 9 files changed, 157 insertions(+), 247 deletions(-)

diff --git a/gajim/chat_control.py b/gajim/chat_control.py
index a2d8e13e9d..7c6a8140ba 100644
--- a/gajim/chat_control.py
+++ b/gajim/chat_control.py
@@ -99,13 +99,11 @@ class ChatControl(ChatControlBase):
     # processed with this command host.
     COMMAND_HOST = ChatCommands  # type: ClassVar[Type[CommandHost]]
 
-    def __init__(self, parent_win, jid, acct, session, resource=None):
+    def __init__(self, account, jid):
         ChatControlBase.__init__(self,
-                                 parent_win,
                                  'chat_control',
-                                 jid,
-                                 acct,
-                                 resource)
+                                 account,
+                                 jid)
 
         self.last_recv_message_id = None
         self.last_recv_message_marks = None
@@ -245,7 +243,7 @@ def add_actions(self):
             action_name, func = action
             act = Gio.SimpleAction.new(action_name + self.control_id, None)
             act.connect('activate', func)
-            self.parent_win.window.add_action(act)
+            app.window.add_action(act)
 
         chatstate = self.contact.settings.get('send_chatstate')
 
@@ -254,7 +252,7 @@ def add_actions(self):
             GLib.VariantType.new("s"),
             GLib.Variant("s", chatstate))
         act.connect('change-state', self._on_send_chatstate)
-        self.parent_win.window.add_action(act)
+        app.window.add_action(act)
 
         marker = self.contact.settings.get('send_marker')
 
@@ -263,10 +261,10 @@ def add_actions(self):
             None,
             GLib.Variant.new_boolean(marker))
         act.connect('change-state', self._on_send_marker)
-        self.parent_win.window.add_action(act)
+        app.window.add_action(act)
 
     def update_actions(self):
-        win = self.parent_win.window
+        win = app.window
         online = app.account_is_connected(self.account)
 
         # Add to roster
@@ -366,7 +364,7 @@ def remove_actions(self):
             'send-marker-',
         ]
         for action in actions:
-            self.parent_win.window.remove_action(f'{action}{self.control_id}')
+            app.window.remove_action(f'{action}{self.control_id}')
 
     def focus(self):
         self.msg_textview.grab_focus()
@@ -377,7 +375,7 @@ def delegate_action(self, action):
             return res
 
         if action == 'show-contact-info':
-            self.parent_win.window.lookup_action(
+            app.window.lookup_action(
                 'information-%s' % self.control_id).activate()
             return Gdk.EVENT_STOP
 
@@ -387,7 +385,7 @@ def delegate_action(self, action):
                 app.interface.roster.tree.grab_focus()
                 return Gdk.EVENT_PROPAGATE
 
-            self.parent_win.window.lookup_action(
+            app.window.lookup_action(
                 'send-file-%s' % self.control_id).activate()
             return Gdk.EVENT_STOP
 
@@ -499,8 +497,6 @@ def _on_location_received(self, _event):
 
     def _on_nickname_received(self, _event):
         self.update_ui()
-        self.parent_win.redraw_tab(self)
-        self.parent_win.show_title()
 
     def _on_update_client_info(self, event):
         contact = app.contacts.get_contact(
@@ -527,7 +523,7 @@ def _on_caps_update(self, event):
 
     def _on_mam_message_received(self, event):
         if event.properties.is_muc_pm:
-            if not event.properties.jid == self.contact.get_full_jid():
+            if not event.properties.jid == self.contact.jid:
                 return
         else:
             if not event.properties.jid.bare_match(self.contact.jid):
@@ -608,19 +604,6 @@ def _nec_ping(self, event):
         elif event.name == 'ping-error':
             self.add_info_message(event.error)
 
-    def change_resource(self, resource):
-        old_full_jid = self.get_full_jid()
-        self.resource = resource
-        new_full_jid = self.get_full_jid()
-        # update app.last_message_time
-        if old_full_jid in app.last_message_time[self.account]:
-            app.last_message_time[self.account][new_full_jid] = \
-                    app.last_message_time[self.account][old_full_jid]
-        # update events
-        app.events.change_jid(self.account, old_full_jid, new_full_jid)
-        # update MessageWindow._controls
-        self.parent_win.change_jid(self.account, old_full_jid, new_full_jid)
-
     # Jingle AV
     def _on_start_call(self, *args):
         audio_state = self.jingle['audio'].state
@@ -771,7 +754,7 @@ def update_video(self):
             self.xml.outgoing_viewport.add(self._video_widget_self)
 
             session = self._client.get_module('Jingle').get_jingle_session(
-                self.contact.get_full_jid(), self.jingle['video'].sid)
+                self.contact.jid, self.jingle['video'].sid)
             content = session.get_content('video')
             content.do_setup(sink_self, sink_other)
 
@@ -834,7 +817,7 @@ def close_jingle_content(self, jingle_type: str,
             return
 
         session = self._client.get_module('Jingle').get_jingle_session(
-            self.contact.get_full_jid(), jingle.sid)
+            self.contact.jid, jingle.sid)
         if session:
             content = session.get_content(jingle_type)
             if content:
@@ -857,7 +840,7 @@ def _on_jingle_button_toggled(self, jingle_types):
         if all(item in jingle_types for item in ['audio', 'video']):
             # Both 'audio' and 'video' in jingle_types
             sid = self._client.get_module('Jingle').start_audio_video(
-                self.contact.get_full_jid())
+                self.contact.jid)
             self.set_jingle_state('audio', JingleState.CONNECTING, sid)
             self.set_jingle_state('video', JingleState.CONNECTING, sid)
             return
@@ -867,7 +850,7 @@ def _on_jingle_button_toggled(self, jingle_types):
                 self.close_jingle_content('audio')
             else:
                 sid = self._client.get_module('Jingle').start_audio(
-                    self.contact.get_full_jid())
+                    self.contact.jid)
                 self.set_jingle_state('audio', JingleState.CONNECTING, sid)
 
         if 'video' in jingle_types:
@@ -875,12 +858,12 @@ def _on_jingle_button_toggled(self, jingle_types):
                 self.close_jingle_content('video')
             else:
                 sid = self._client.get_module('Jingle').start_video(
-                    self.contact.get_full_jid())
+                    self.contact.jid)
                 self.set_jingle_state('video', JingleState.CONNECTING, sid)
 
     def _get_audio_content(self):
         session = self._client.get_module('Jingle').get_jingle_session(
-            self.contact.get_full_jid(), self.jingle['audio'].sid)
+            self.contact.jid, self.jingle['audio'].sid)
         return session.get_content('audio')
 
     def on_num_button_pressed(self, _widget, num):
@@ -912,11 +895,11 @@ def on_location_eventbox_leave_notify_event(self, _widget, _event):
         Just moved the mouse so show the cursor
         """
         cursor = get_cursor('default')
-        self.parent_win.window.get_window().set_cursor(cursor)
+        app.window.get_window().set_cursor(cursor)
 
     def on_location_eventbox_enter_notify_event(self, _widget, _event):
         cursor = get_cursor('pointer')
-        self.parent_win.window.get_window().set_cursor(cursor)
+        app.window.get_window().set_cursor(cursor)
 
     def update_ui(self):
         # The name banner is drawn here
@@ -932,14 +915,14 @@ def draw_banner_text(self):
         """
         contact = self.contact
         name = contact.name
-        if self.resource:
-            name += '/' + self.resource
-        if self._type.is_privatechat:
-            name = i18n.direction_mark + _(
-                '%(nickname)s from group chat %(room_name)s') % \
-                {'nickname': name, 'room_name': self.room_name}
+        # if self.resource:
+        #     name += '/' + self.resource
+        # if self._type.is_privatechat:
+        #     name = i18n.direction_mark + _(
+        #         '%(nickname)s from group chat %(room_name)s') % \
+        #         {'nickname': name, 'room_name': self.room_name}
 
-        name = i18n.direction_mark + GLib.markup_escape_text(name)
+        # name = i18n.direction_mark + GLib.markup_escape_text(name)
 
         cs = self.contact.chatstate
         if cs is not None:
@@ -1091,17 +1074,6 @@ def shutdown(self):
             self.close_jingle_content(jingle_type, shutdown=True)
         self.jingle.clear()
 
-        # disconnect self from session
-        if self.session:
-            self.session.control = None
-
-        # Clean events
-        app.events.remove_events(
-            self.account,
-            self.get_full_jid(),
-            types=['printed_%s' % self._type, str(self._type)])
-        # Remove contact instance if contact has been removed
-
         super(ChatControl, self).shutdown()
         app.check_finalize(self)
 
@@ -1112,7 +1084,7 @@ def safe_shutdown(self):
         return False
 
     def allow_shutdown(self, method, on_yes, on_no, _on_minimize):
-        time_ = app.last_message_time[self.account][self.get_full_jid()]
+        time_ = app.last_message_time[self.account][self.contact.jid]
         # 2 seconds
         if time.time() - time_ < 2:
             no_log_for = app.settings.get_account_setting(
@@ -1136,7 +1108,7 @@ def allow_shutdown(self, method, on_yes, on_no, _on_minimize):
                  DialogButton.make('Remove',
                                    text=_('_Close'),
                                    callback=lambda: on_yes(self))],
-                transient_for=self.parent_win.window).show()
+                transient_for=app.window).show()
             return
         on_yes(self)
 
@@ -1144,7 +1116,7 @@ def _on_avatar_update(self, _contact, _signal_name):
         self._update_avatar()
 
     def _update_avatar(self):
-        scale = self.parent_win.window.get_scale_factor()
+        scale = app.window.get_scale_factor()
         surface = self.contact.get_avatar(AvatarSize.CHAT, scale)
         self.xml.avatar_image.set_from_surface(surface)
 
@@ -1200,7 +1172,6 @@ def _on_presence_received(self, event):
         name = self.contact.name
 
         self.update_ui()
-        self.parent_win.redraw_tab(self)
 
         if not app.settings.get('print_status_in_chats'):
             return
diff --git a/gajim/chat_control_base.py b/gajim/chat_control_base.py
index 071e025d44..b39bc9c991 100644
--- a/gajim/chat_control_base.py
+++ b/gajim/chat_control_base.py
@@ -98,20 +98,17 @@ class ChatControlBase(ChatCommandProcessor, CommandTools, EventHelper):
 
     _type = None  # type: ControlType
 
-    def __init__(self, parent_win, widget_name, jid, acct,
-                 resource=None):
+    def __init__(self, widget_name, account, jid):
         EventHelper.__init__(self)
         # Undo needs this variable to know if space has been pressed.
         # Initialize it to True so empty textview is saved in undo list
         self.space_pressed = True
 
         self.handlers = {}
-        self.parent_win = parent_win
 
-        self.account = acct
-        self.resource = resource
+        self.account = account
 
-        self._client = app.get_client(acct)
+        self._client = app.get_client(account)
 
         groupchat = self._type != ControlType.CHAT
         self.contact = self._client.get_module('Contacts').get_contact(
@@ -123,7 +120,7 @@ def __init__(self, parent_win, widget_name, jid, acct,
         self.control_id = str(uuid.uuid4())
         self.session = None
 
-        app.last_message_time[self.account][self.get_full_jid()] = 0
+        app.last_message_time[self.account][self.contact.jid] = 0
 
         self.xml = get_builder('%s.ui' % widget_name)
         self.xml.connect_signals(self)
@@ -241,10 +238,10 @@ def __init__(self, parent_win, widget_name, jid, acct,
 
         self._client.get_module('Chatstate').set_active(self.contact)
 
-        if parent_win is not None:
-            id_ = parent_win.window.connect('motion-notify-event',
-                                            self._on_window_motion_notify)
-            self.handlers[id_] = parent_win.window
+        # TODO
+        # id_ = app.window.connect('motion-notify-event',
+        #                          self._on_window_motion_notify)
+        # self.handlers[id_] = app.window
 
         self.encryption = self.get_encryption_state()
         self.conversation_view.encryption_enabled = self.encryption is not None
@@ -322,12 +319,6 @@ def is_privatechat(self):
     def is_groupchat(self):
         return self._type.is_groupchat
 
-    def get_full_jid(self):
-        fjid = self.contact.jid
-        if self.resource:
-            fjid += '/' + self.resource
-        return fjid
-
     def minimizable(self):
         """
         Called to check if control can be minimized
@@ -360,15 +351,6 @@ def allow_shutdown(self, method, on_response_yes, on_response_no,
     def focus(self):
         raise NotImplementedError
 
-    def get_nb_unread(self):
-        jid = self.contact.jid
-        if self.resource:
-            jid += '/' + self.resource
-        return len(app.events.get_events(
-            self.account,
-            jid,
-            ['printed_%s' % self._type, str(self._type)]))
-
     def draw_banner(self):
         """
         Draw the fat line at the top of the window
@@ -522,7 +504,7 @@ def add_actions(self):
             GLib.VariantType.new('s'),
             GLib.Variant('s', self.encryption or 'disabled'))
         action.connect('change-state', self.change_encryption)
-        self.parent_win.window.add_action(action)
+        app.window.add_action(action)
 
         actions = {
             'send-message-%s': self._on_send_message,
@@ -535,7 +517,7 @@ def add_actions(self):
             action = Gio.SimpleAction.new(name % self.control_id, None)
             action.connect('activate', func)
             action.set_enabled(False)
-            self.parent_win.window.add_action(action)
+            app.window.add_action(action)
 
     def remove_actions(self):
         actions = [
@@ -547,7 +529,7 @@ def remove_actions(self):
         ]
 
         for action in actions:
-            self.parent_win.window.remove_action(f'{action}{self.control_id}')
+            app.window.remove_action(f'{action}{self.control_id}')
 
     def change_encryption(self, action, param):
         encryption = param.get_string()
@@ -783,9 +765,9 @@ def _paste_event_confirmed(self, is_checked, image):
     def _get_pref_ft_method(self):
         ft_pref = app.settings.get_account_setting(self.account,
                                                    'filetransfer_preference')
-        httpupload = self.parent_win.window.lookup_action(
+        httpupload = app.window.lookup_action(
             'send-file-httpupload-%s' % self.control_id)
-        jingle = self.parent_win.window.lookup_action(
+        jingle = app.window.lookup_action(
             'send-file-jingle-%s' % self.control_id)
 
         if self._type.is_groupchat:
@@ -1037,9 +1019,6 @@ def _on_window_motion_notify(self, *args):
         """
         It gets called no matter if it is the active window or not
         """
-        if not self.parent_win:
-            # when a groupchat is minimized there is no parent window
-            return
         # TODO
         return
         if self.parent_win.get_active_jid() == self.contact.jid:
@@ -1049,8 +1028,7 @@ def _on_window_motion_notify(self, *args):
 
     def _on_message_tv_buffer_changed(self, textbuffer):
         has_text = self.msg_textview.has_text()
-        if self.parent_win is not None:
-            self.parent_win.window.lookup_action(
+        app.window.lookup_action(
                 'send-message-' + self.control_id).set_enabled(has_text)
 
         if textbuffer.get_char_count() and self.encryption:
@@ -1123,7 +1101,7 @@ def add_message(self,
         correct_id = (message_id, correct_id)
         """
         jid = self.contact.jid
-        full_jid = self.get_full_jid()
+        full_jid = self.contact.jid
         end = False
         if self.conversation_view.autoscroll or kind == 'outgoing':
             end = True
@@ -1252,9 +1230,6 @@ def set_emoticon_popover(self):
         if not app.settings.get('emoticons_theme'):
             return
 
-        if not self.parent_win:
-            return
-
         if sys.platform in ('win32', 'darwin'):
             emoji_chooser.text_widget = self.msg_textview
             self.xml.emoticons_button.set_popover(emoji_chooser)
@@ -1270,13 +1245,13 @@ def _on_emoticon_button_clicked(self, _widget):
         self.xml.emoticons_button.set_property('active', False)
 
     def on_color_menuitem_activate(self, _widget):
-        color_dialog = Gtk.ColorChooserDialog(None, self.parent_win.window)
+        color_dialog = Gtk.ColorChooserDialog(None, app.window)
         color_dialog.set_use_alpha(False)
         color_dialog.connect('response', self.msg_textview.color_set)
         color_dialog.show_all()
 
     def on_font_menuitem_activate(self, _widget):
-        font_dialog = Gtk.FontChooserDialog(None, self.parent_win.window)
+        font_dialog = Gtk.FontChooserDialog(None, app.window)
         start, finish = self.msg_textview.get_active_iters()
         font_dialog.connect('response', self.msg_textview.font_set, start, finish)
         font_dialog.show_all()
@@ -1369,7 +1344,7 @@ def set_control_active(self, state):
                 if self._type.is_groupchat:
                     type_ = ['printed_gc_msg', 'printed_marked_gc_msg']
                 if not app.events.remove_events(self.account,
-                                                self.get_full_jid(),
+                                                self.contact.jid,
                                                 types=type_):
                     # There were events to remove
                     self.redraw_after_event_removed(jid)
@@ -1471,10 +1446,9 @@ def add_messages(self, messages):
                 history=True)
 
     def has_focus(self):
-        if self.parent_win:
-            if self.parent_win.window.get_property('has-toplevel-focus'):
-                if self == self.parent_win.get_active_control():
-                    return True
+        if app.window.get_property('has-toplevel-focus'):
+            if self == app.window.get_active_control():
+                return True
         return False
 
     def scroll_messages(self, direction, msg_buf, msg_type):
diff --git a/gajim/common/modules/caps.py b/gajim/common/modules/caps.py
index 148d62f2aa..cc01e6111f 100644
--- a/gajim/common/modules/caps.py
+++ b/gajim/common/modules/caps.py
@@ -212,9 +212,11 @@ def preconditions_met(self):
             return False
 
         if self._from_muc:
-            muc = client.get_module('MUC').get_muc_data(self.entity.jid.bare)
+            contact = client.get_module('Contacts').get_contact(
+                self.entity.jid.bare,
+                groupchat=True)
 
-            if muc is None or not muc.state.is_joined:
+            if not contact.is_joined:
                 self.set_obsolete()
                 return False
 
diff --git a/gajim/common/modules/chat_markers.py b/gajim/common/modules/chat_markers.py
index 67eb396a26..40bb19fe7b 100644
--- a/gajim/common/modules/chat_markers.py
+++ b/gajim/common/modules/chat_markers.py
@@ -45,9 +45,11 @@ def _process_chat_marker(self, _con, _stanza, properties):
             return
 
         if properties.type.is_groupchat:
-            muc_data = self._con.get_module('MUC').get_muc_data(
-                properties.muc_jid)
-            if muc_data is None:
+            contact = self._con.get_module('Contacts').get_contact(
+                properties.muc_jid,
+                groupchat=True)
+            if not contact.is_joined:
+                self._log.warning('Received chat marker while not joined')
                 return
 
             if properties.muc_nickname != muc_data.nick:
diff --git a/gajim/common/modules/contacts.py b/gajim/common/modules/contacts.py
index dacee02755..f0fb3aec93 100644
--- a/gajim/common/modules/contacts.py
+++ b/gajim/common/modules/contacts.py
@@ -346,11 +346,6 @@ def __init__(self, logger, jid, account):
     def is_groupchat(self):
         return True
 
-    @property
-    def state(self):
-        muc_data = self._module('MUC').get_muc_data(self._jid)
-        return muc_data.state
-
     def add_resource(self, resource):
         jid = self._jid.new_with(resource=resource)
         contact = GroupchatParticipant(self._log, jid, self._account)
@@ -392,6 +387,26 @@ def update_avatar(self, *args):
         app.interface.avatar_storage.invalidate_cache(self._jid)
         self.notify('avatar-update')
 
+    def get_self(self):
+        nick = self.nickname
+        if nick is None:
+            return None
+        return self.get_resource(nick)
+
+    @property
+    def nickname(self):
+        muc_data = self._module('MUC').get_muc_data(self._jid)
+        if muc_data is None:
+            return None
+        return muc_data.nick
+
+    @property
+    def occupant_jid(self):
+        muc_data = self._module('MUC').get_muc_data(self._jid)
+        if muc_data is None:
+            return None
+        return muc_data.occupant_jid
+
     @property
     def is_joined(self):
         muc_data = self._module('MUC').get_muc_data(self._jid)
@@ -399,6 +414,20 @@ def is_joined(self):
             return False
         return muc_data.state.is_joined
 
+    @property
+    def is_joining(self):
+        muc_data = self._module('MUC').get_muc_data(self._jid)
+        if muc_data is None:
+            return False
+        return muc_data.state.is_joining
+
+    @property
+    def is_not_joined(self):
+        muc_data = self._module('MUC').get_muc_data(self._jid)
+        if muc_data is None:
+            return True
+        return muc_data.state.is_not_joined
+
     def set_not_joined(self):
         for contact in self._resources.values():
             contact.update_presence(UNKNOWN_MUC_PRESENCE, notify=False)
diff --git a/gajim/common/modules/muc.py b/gajim/common/modules/muc.py
index 2a96318681..396902e11e 100644
--- a/gajim/common/modules/muc.py
+++ b/gajim/common/modules/muc.py
@@ -116,7 +116,7 @@ def __init__(self, con):
         self._muc_service_jid = None
         self._joined_users = defaultdict(dict)
         self._mucs = {}
-
+        self._muc_nicknames = {}
 
     def _on_resume_failed(self, _client, _signal_name):
         self._reset_presence()
@@ -147,6 +147,10 @@ def pass_disco(self, info):
     def get_muc_data(self, room_jid):
         return self._mucs.get(room_jid)
 
+    def set_password(self, room_jid, password):
+        muc_data = self.get_muc_data(room_jid)
+        muc_data.password = password
+
     def _get_mucs_with_state(self, states):
         return [muc for muc in self._mucs.values() if muc.state in states]
 
diff --git a/gajim/groupchat_control.py b/gajim/groupchat_control.py
index 1b03314e7b..20c12b8037 100644
--- a/gajim/groupchat_control.py
+++ b/gajim/groupchat_control.py
@@ -87,12 +87,11 @@ class GroupchatControl(ChatControlBase):
     # will be processed with this command host.
     COMMAND_HOST = GroupChatCommands
 
-    def __init__(self, parent_win, jid, muc_data, acct):
+    def __init__(self, account, jid):
         ChatControlBase.__init__(self,
-                                 parent_win,
                                  'groupchat_control',
-                                 jid,
-                                 acct)
+                                 account,
+                                 jid)
 
         self._client.connect_signal('state-changed',
                                     self._on_client_state_changed)
@@ -120,12 +119,9 @@ def __init__(self, parent_win, jid, muc_data, acct):
         self.xml.roster_revealer.add(self.roster)
         self.roster.connect('row-activated', self._on_roster_row_activated)
 
-        if parent_win is not None:
-            self.add_actions()
-            GLib.idle_add(self.update_actions)
-            self.scale_factor = parent_win.window.get_scale_factor()
-        else:
-            self.scale_factor = app.interface.roster.scale_factor
+        self.add_actions()
+        GLib.idle_add(self.update_actions)
+        self.scale_factor = app.window.get_scale_factor()
 
         if not app.settings.get('hide_groupchat_banner'):
             self.xml.banner_eventbox.set_no_show_all(False)
@@ -256,45 +252,33 @@ def _connect_contact_signals(self):
         })
 
     def _on_muc_state_changed(self, _contact, _signal_name):
-        state = self.contact.state
-        if state == MUCJoinedState.JOINING:
+        if self.contact.is_joining:
             self._groupchat_state.set_joining()
 
-        if state == MUCJoinedState.JOINED:
+        if self.contact.is_joined:
             self._set_control_active()
             self.show_roster()
             self._groupchat_state.set_joined()
 
-        elif state == MUCJoinedState.NOT_JOINED:
+        elif self.contact.is_not_joined:
             self._set_control_inactive()
 
     def _on_client_state_changed(self, _client, _signal_name, state):
         pass
 
-    @property
-    def _muc_data(self):
-        return self._client.get_module('MUC').get_muc_data(self.room_jid)
-
     @property
     def _nick_completion(self):
         if self.__nick_completion is None:
-            self.__nick_completion = NickCompletionGenerator(self._muc_data.nick)
+            self.__nick_completion = NickCompletionGenerator(
+                self.contact.nickname)
         return self.__nick_completion
 
-    @property
-    def nick(self):
-        return self._muc_data.nick
-
     @property
     def subject(self):
         if self._subject_data is None:
             return ''
         return self._subject_data.subject
 
-    @property
-    def room_name(self):
-        return self.contact.get_shown_name()
-
     @property
     def disco_info(self):
         return app.storage.cache.get_last_disco_info(self.contact.jid)
@@ -325,36 +309,35 @@ def add_actions(self):
                 variant = GLib.VariantType.new(variant)
             act = Gio.SimpleAction.new(action_name + self.control_id, variant)
             act.connect("activate", func)
-            self.parent_win.window.add_action(act)
+            app.window.add_action(act)
 
     def update_actions(self, *args):
-        if self._muc_data is None:
-            return
-
-        contact = self.contact.get_resource(self.nick)
+        joined = self.contact.is_joined
+        if joined:
+            contact = self.contact.get_self()
 
         self._get_action('request-voice-').set_enabled(
-            self.is_connected and contact.role.is_visitor)
+            joined and contact.role.is_visitor)
 
         # Change Nick
-        self._get_action('change-nickname-').set_enabled(self.is_connected)
+        self._get_action('change-nickname-').set_enabled(joined)
 
         # Execute command
-        self._get_action('execute-command-').set_enabled(self.is_connected)
+        self._get_action('execute-command-').set_enabled(joined)
 
         # Send message
         has_text = self.msg_textview.has_text()
         self._get_action('send-message-').set_enabled(
-            self.is_connected and has_text)
+            joined and has_text)
 
         # Send file (HTTP File Upload)
         httpupload = self._get_action(
             'send-file-httpupload-')
-        httpupload.set_enabled(self.is_connected and
+        httpupload.set_enabled(joined and
                                self._client.get_module('HTTPUpload').available)
         self._get_action('send-file-').set_enabled(httpupload.get_enabled())
 
-        if self.is_connected and httpupload.get_enabled():
+        if joined and httpupload.get_enabled():
             tooltip_text = _('Send File…')
             max_file_size = self._client.get_module('HTTPUpload').max_file_size
             if max_file_size is not None:
@@ -372,6 +355,7 @@ def update_actions(self, *args):
         vcard_support = False
         if self.disco_info is not None:
             vcard_support = self.disco_info.supports(Namespace.VCARD)
+
             self.xml.muc_name_entry.set_text(self.disco_info.muc_name or '')
             self.xml.muc_description_entry.set_text(
                 self.disco_info.muc_description or '')
@@ -390,13 +374,13 @@ def update_actions(self, *args):
         self.xml.manage_destroy_button.set_sensitive(
             self.is_connected and contact.affiliation.is_owner)
 
-        self._get_action('contact-information-').set_enabled(self.is_connected)
+        self._get_action('contact-information-').set_enabled(joined)
 
-        self._get_action('execute-command-').set_enabled(self.is_connected)
+        self._get_action('execute-command-').set_enabled(joined)
 
-        self._get_action('ban-').set_enabled(self.is_connected)
+        self._get_action('ban-').set_enabled(joined)
 
-        self._get_action('kick-').set_enabled(self.is_connected)
+        self._get_action('kick-').set_enabled(joined)
 
     def remove_actions(self):
         super().remove_actions()
@@ -418,10 +402,13 @@ def remove_actions(self):
         ]
 
         for action in actions:
-            self.parent_win.window.remove_action(f'{action}{self.control_id}')
+            app.window.remove_action(f'{action}{self.control_id}')
 
     def _is_subject_change_allowed(self):
-        contact = self.contact.get_resource(self.nick)
+        contact = self.contact.get_self()
+        if contact is None:
+            return False
+
         if contact.affiliation in (Affiliation.OWNER, Affiliation.ADMIN):
             return True
 
@@ -430,7 +417,7 @@ def _is_subject_change_allowed(self):
         return self.disco_info.muc_subjectmod or False
 
     def _get_action(self, name):
-        win = self.parent_win.window
+        win = app.window
         return win.lookup_action(name + self.control_id)
 
     def _show_page(self, name):
@@ -560,7 +547,7 @@ def _on_configure_room(self, _button):
             win.present()
             return
 
-        contact = self.contact.get_resource(self.nick)
+        contact = self.contact.get_self()
         if contact.affiliation.is_owner:
             self._client.get_module('MUC').request_config(
                 self.room_jid, callback=self._on_configure_form_received)
@@ -610,7 +597,7 @@ def _on_accept(path):
                 self._avatar_selector.get_prepared())
 
         AvatarChooserDialog(_on_accept,
-                            transient_for=self.parent_win.window,
+                            transient_for=app.window,
                             modal=True)
 
     def _on_upload_avatar_result(self, task):
@@ -687,23 +674,6 @@ def show_roster(self):
         self.xml.roster_revealer.set_transition_type(transition)
         self.xml.roster_revealer.set_reveal_child(show)
 
-    def on_groupchat_maximize(self):
-        self.roster.enable_tooltips()
-        self.add_actions()
-        self.update_actions()
-        self.set_lock_image()
-        self.draw_banner_text()
-        type_ = ['printed_gc_msg', 'printed_marked_gc_msg']
-        if not app.events.remove_events(self.account,
-                                        self.get_full_jid(),
-                                        types=type_):
-            # XEP-0333 Send <displayed> marker
-            self._client.get_module('ChatMarkers').send_displayed_marker(
-                self.contact,
-                self.last_msg_id,
-                self._type)
-            self.last_msg_id = None
-
     def _on_roster_row_activated(self, _roster, nick):
         muc_prefer_direct_msg = app.settings.get('muc_prefer_direct_msg')
         if not self.is_anonymous and muc_prefer_direct_msg:
@@ -746,7 +716,7 @@ def on_approve():
             _('Voice Request'),
             _('Voice Request'),
             _('<b>%(nick)s</b> from <b>%(room_name)s</b> requests voice') % {
-                'nick': voice_request.nick, 'room_name': self.room_name},
+                'nick': voice_request.nick, 'room_name': self.contact.name},
             [DialogButton.make('Cancel'),
              DialogButton.make('Accept',
                                text=_('_Approve'),
@@ -773,7 +743,7 @@ def _on_gc_message_received(self, event):
                              displaymarking=event.displaymarking,
                              additional_data=event.additional_data)
         else:
-            if event.properties.muc_nickname == self.nick:
+            if event.properties.muc_nickname == self.contact.nickname:
                 self.last_sent_txt = event.msgtxt
             stanza_id = None
             if event.properties.stanza_id:
@@ -804,24 +774,17 @@ def add_message(self, text, contact='', tim=None,
         if not contact:
             # Message from the server
             kind = 'status'
-        elif contact == self.nick: # it's us
+        elif contact == self.contact.nickname: # it's us
             kind = 'outgoing'
         else:
             kind = 'incoming'
             # muc-specific chatstate
-            if self.parent_win:
-                self.parent_win.redraw_tab(self, 'newmsg')
 
         if kind == 'incoming': # it's a message NOT from us
             # highlighting and sounds
             highlight, _sound = self.highlighting_for_message(text, tim)
             other_tags_for_name.append('muc_nickname_color_%s' % contact)
             if highlight:
-                # muc-specific chatstate
-                if self.parent_win:
-                    self.parent_win.redraw_tab(self, 'attention')
-                else:
-                    self.attention_flag = True
                 other_tags_for_name.append('bold')
                 other_tags_for_text.append('marked')
 
@@ -892,7 +855,7 @@ def needs_visual_notification(self, text):
         nick) appear
         """
         special_words = app.settings.get('muc_highlight_words').split(';')
-        special_words.append(self.nick)
+        special_words.append(self.contact.nickname)
         special_words.append(self._client.get_own_jid().bare)
         # Strip empties: ''.split(';') == [''] and would highlight everything.
         # Also lowercase everything for case insensitive compare.
@@ -929,7 +892,7 @@ def _on_room_subject(self, _contact, _signal_name, properties):
             text = '%s - %s' % (text, date)
 
         if (app.settings.get('show_subject_on_join') or
-                self._muc_data.state != MUCJoinedState.JOINING):
+                not self.contact.is_joining):
             self.add_info_message(text)
 
     def _on_room_config_changed(self, _contact, _signal_name, properties):
@@ -990,14 +953,6 @@ def _nec_ping(self, event):
         elif event.name == 'ping-error':
             self.add_info_message(event.error)
 
-    @property
-    def is_connected(self) -> bool:
-        return app.gc_connected[self.account][self.room_jid]
-
-    @is_connected.setter
-    def is_connected(self, value: bool) -> None:
-        app.gc_connected[self.account][self.room_jid] = value
-
     def _set_control_active(self):
         self.xml.formattings_button.set_sensitive(True)
         self.msg_textview.set_sensitive(True)
@@ -1005,8 +960,6 @@ def _set_control_active(self):
 
         self.roster.initial_draw()
 
-        self.is_connected = True
-
         self.xml.formattings_button.set_sensitive(True)
 
         self.conversation_view.update_avatars()
@@ -1021,8 +974,6 @@ def _set_control_inactive(self):
         self.roster.enable_sort(False)
         self.roster.clear()
 
-        self.is_connected = False
-
         self._client.get_module('Chatstate').remove_delay_timeout(self.contact)
 
         self.update_actions()
@@ -1039,13 +990,14 @@ def _on_user_joined(self, _contact, _signal_name, user_contact, properties):
         nick = user_contact.name
         if not properties.is_muc_self_presence:
 
-            if self.is_connected and self.contact.settings.get('print_join_left'):
+            if (self.contact.is_joined and
+                    self.contact.settings.get('print_join_left')):
                 self.add_info_message(_('%s has joined the group chat') % nick)
             return
 
         status_codes = properties.muc_status_codes or []
 
-        if not self.is_connected:
+        if not self.contact.is_joined:
             # We just joined the room
             self.add_info_message(_('You (%s) joined the group chat') % nick)
 
@@ -1411,21 +1363,20 @@ def shutdown(self, reason=None):
         # Remove unread events from systray
         app.events.remove_events(self.account, self.room_jid)
 
-        if self.parent_win is not None:
-            self.remove_actions()
+        self.remove_actions()
 
         super(GroupchatControl, self).shutdown()
         app.check_finalize(self)
 
     def safe_shutdown(self):
         # whether to ask for confirmation before closing muc
-        if app.settings.get('confirm_close_muc') and self.is_connected:
+        if app.settings.get('confirm_close_muc') and self.contact.is_joined:
             return False
         return True
 
     def allow_shutdown(self, method, on_yes, on_no):
         # whether to ask for confirmation before closing muc
-        if app.settings.get('confirm_close_muc') and self.is_connected:
+        if app.settings.get('confirm_close_muc') and self.contact.is_joined:
             def on_ok(is_checked):
                 if is_checked:
                     # User does not want to be asked again
@@ -1442,33 +1393,24 @@ def on_cancel(is_checked):
                 _('Leave Group Chat'),
                 _('Are you sure you want to leave this group chat?'),
                 _('If you close this window, you will leave '
-                  '\'%s\'.') % self.room_name,
+                  '\'%s\'.') % self.contact.name,
                 _('_Do not ask me again'),
                 [DialogButton.make('Cancel',
                                    callback=on_cancel),
                  DialogButton.make('Accept',
                                    text=_('_Leave'),
                                    callback=on_ok)],
-                transient_for=self.parent_win.window).show()
+                transient_for=app.window).show()
             return
 
         on_yes(self)
 
     def _close_control(self, reason=None):
-        # if self.parent_win is None:
         self.shutdown(reason)
-        # else:
-            # self.parent_win.remove_tab(self, None, reason=reason, force=True)
 
     def set_control_active(self, state):
-        # self.conv_textview.allow_focus_out_line = True
         self.attention_flag = False
         ChatControlBase.set_control_active(self, state)
-        # if not state:
-        #    # add the focus-out line to the tab we are leaving
-        #    self.check_focus_out_line()
-        # Sending active to undo unread state
-        self.parent_win.redraw_tab(self, 'active')
 
     def _on_drag_data_received(self, widget, context, x, y, selection,
                                target_type, timestamp):
@@ -1617,7 +1559,7 @@ def delegate_action(self, action):
 
         if action == 'change-nickname':
             control_action = '%s-%s' % (action, self.control_id)
-            self.parent_win.window.lookup_action(control_action).activate()
+            app.window.lookup_action(control_action).activate()
             return Gdk.EVENT_STOP
 
         if action == 'escape':
@@ -1638,11 +1580,11 @@ def delegate_action(self, action):
 
         if action == 'change-subject':
             control_action = '%s-%s' % (action, self.control_id)
-            self.parent_win.window.lookup_action(control_action).activate()
+            app.window.lookup_action(control_action).activate()
             return Gdk.EVENT_STOP
 
         if action == 'show-contact-info':
-            self.parent_win.window.lookup_action(
+            app.window.lookup_action(
                 'information-%s' % self.control_id).activate()
             return Gdk.EVENT_STOP
 
@@ -1716,14 +1658,14 @@ def _on_page_change(self, stack, _param):
     def _on_change_nick(self, _action, _param):
         if self._get_current_page() != 'groupchat':
             return
-        self.xml.nickname_entry.set_text(self.nick)
+        self.xml.nickname_entry.set_text(self.contact.nickname)
         self.xml.nickname_entry.grab_focus()
         self.xml.nickname_change_button.grab_default()
         self._show_page('nickname')
 
     def _on_nickname_text_changed(self, entry, _param):
         text = entry.get_text()
-        if not text or text == self.nick:
+        if not text or text == self.contact.nickname:
             self.xml.nickname_change_button.set_sensitive(False)
         else:
             try:
@@ -1741,7 +1683,7 @@ def _on_nickname_change_clicked(self, _button):
     def _on_rename_groupchat(self, _action, _param):
         if self._get_current_page() != 'groupchat':
             return
-        self.xml.name_entry.set_text(self.room_name)
+        self.xml.name_entry.set_text(self.contact.name)
         self.xml.name_entry.grab_focus()
         self.xml.rename_button.grab_default()
         self._show_page('rename')
@@ -1788,7 +1730,7 @@ def _on_subject_change_clicked(self, _button):
 
     def _on_password_set_clicked(self, _button):
         password = self.xml.password_entry.get_text()
-        self._muc_data.password = password
+        self._client.get_module('MUC').set_password(self.room_jid, password)
         self._client.get_module('MUC').join(self.room_jid)
         self._show_page('groupchat')
 
@@ -1810,11 +1752,6 @@ def _on_room_captcha_challenge(self, _contact, _signal_name, properties):
         self._captcha_request.show_all()
         self.xml.captcha_box.add(self._captcha_request)
 
-        if self.parent_win:
-            self.parent_win.redraw_tab(self, 'attention')
-        else:
-            self.attention_flag = True
-
         self._show_page('captcha')
         self._captcha_request.focus_first_entry()
 
diff --git a/gajim/gtk/chat_stack.py b/gajim/gtk/chat_stack.py
index 4f373bffa1..5ea020612a 100644
--- a/gajim/gtk/chat_stack.py
+++ b/gajim/gtk/chat_stack.py
@@ -47,8 +47,7 @@ def add_chat(self, account, jid):
             # Control is already in the Stack
             return
 
-        mw = self.get_toplevel()
-        chat_control = ChatControl(mw, jid, account, None, None)
+        chat_control = ChatControl(account, jid)
         self._controls[(account, jid)] = chat_control
         self.add_named(chat_control.widget, f'{account}:{jid}')
         chat_control.widget.show_all()
@@ -57,21 +56,13 @@ def add_group_chat(self, account, jid):
         if self._controls.get((account, jid)) is not None:
             return
 
-        # muc_data = self._create_muc_data(account,
-        #                                  room_jid,
-        #                                  nick,
-        #                                  password,
-        #                                  None)
-
-        mw = self.get_toplevel()
-        control = GroupchatControl(mw, jid, None, account)
+        control = GroupchatControl(account, jid)
         self._controls[(account, jid)] = control
         self.add_named(control.widget, f'{account}:{jid}')
         control.widget.show_all()
 
     def add_private_chat(self, account, jid):
-        mw = self.get_toplevel()
-        control = PrivateChatControl(mw, jid, account)
+        control = PrivateChatControl(account, jid)
         self._controls[(account, str(jid))] = control
         self.add_named(control.widget, f'{account}:{jid}')
         control.widget.show_all()
diff --git a/gajim/privatechat_control.py b/gajim/privatechat_control.py
index 3917a06f29..e48153c3e0 100644
--- a/gajim/privatechat_control.py
+++ b/gajim/privatechat_control.py
@@ -42,11 +42,11 @@ class PrivateChatControl(ChatControl):
     # will be processed with this command host.
     COMMAND_HOST = PrivateChatCommands
 
-    def __init__(self, parent_win, jid, account):
+    def __init__(self, account, jid):
         self._room_contact = self._client.get_module('Contacts').get_contact(
             jid.bare)
 
-        ChatControl.__init__(self, parent_win, jid, account, None)
+        ChatControl.__init__(self, account, jid)
 
         # self.register_events([
         #     ('update-gc-avatar', ged.GUI1, self._on_update_gc_avatar),
-- 
GitLab