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

Commits:

16 changed files:

Changes:

  • README.md
    ... ... @@ -12,6 +12,7 @@
    12 12
     - python3-css-parser
    
    13 13
     - python3-keyring
    
    14 14
     - python3-precis-i18n
    
    15
    +- gir1.2-soup-2.4
    
    15 16
     
    
    16 17
     ### Optional Runtime Requirements
    
    17 18
     
    

  • gajim/chat_control_base.py
    ... ... @@ -322,6 +322,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
    322 322
             self.received_history_pos = 0
    
    323 323
             self.orig_msg = None
    
    324 324
     
    
    325
    +        # For XEP-0333
    
    326
    +        self.last_msg_id = None
    
    327
    +
    
    325 328
             self.set_emoticon_popover()
    
    326 329
     
    
    327 330
             # Attach speller
    
    ... ... @@ -1022,6 +1025,9 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
    1022 1025
             if restored:
    
    1023 1026
                 return
    
    1024 1027
     
    
    1028
    +        if message_id:
    
    1029
    +            self.last_msg_id = message_id
    
    1030
    +
    
    1025 1031
             if kind == 'incoming':
    
    1026 1032
                 if not self.type_id == message_control.TYPE_GC or \
    
    1027 1033
                 app.config.notify_for_muc(jid) or \
    
    ... ... @@ -1065,6 +1071,7 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
    1065 1071
                         event_type.type_, self.contact.jid)
    
    1066 1072
     
    
    1067 1073
                     event = event_type(text, subject, self, msg_log_id,
    
    1074
    +                    message_id=message_id,
    
    1068 1075
                         show_in_roster=show_in_roster,
    
    1069 1076
                         show_in_systray=show_in_systray)
    
    1070 1077
                     app.events.add_event(self.account, full_jid, event)
    
    ... ... @@ -1220,6 +1227,11 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
    1220 1227
                     types=type_):
    
    1221 1228
                         # There were events to remove
    
    1222 1229
                         self.redraw_after_event_removed(jid)
    
    1230
    +                    # XEP-0333 Send <displayed> tag.
    
    1231
    +                    con.get_module('ChatMarkers').send_displayed_marker(
    
    1232
    +                        self.get_full_jid(), self.last_msg_id,
    
    1233
    +                        self.type_id == message_control.TYPE_GC)
    
    1234
    +                    self.last_msg_id = None
    
    1223 1235
                 # send chatstate inactive to the one we're leaving
    
    1224 1236
                 # and active to the one we visit
    
    1225 1237
                 if self.msg_textview.has_text():
    
    ... ... @@ -1262,8 +1274,14 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
    1262 1274
                 # we are at the end
    
    1263 1275
                 if not app.events.remove_events(
    
    1264 1276
                         self.account, jid, types=types_list):
    
    1277
    +                con = app.connections[self.account]
    
    1265 1278
                     # There were events to remove
    
    1266 1279
                     self.redraw_after_event_removed(jid)
    
    1280
    +                # XEP-0333 Send <displayed> tag.
    
    1281
    +                con.get_module('ChatMarkers').send_displayed_marker(
    
    1282
    +                    self.get_full_jid(), self.last_msg_id,
    
    1283
    +                    self.type_id == message_control.TYPE_GC)
    
    1284
    +                self.last_msg_id = None
    
    1267 1285
     
    
    1268 1286
         def _on_scrollbar_button_release(self, scrollbar, event):
    
    1269 1287
             if event.get_button()[1] != 1:
    
    ... ... @@ -1328,6 +1346,8 @@ class ChatControlBase(MessageControl, ChatCommandProcessor, CommandTools):
    1328 1346
             We just removed a 'printed_*' event, redraw contact in roster or
    
    1329 1347
             gc_roster and titles in roster and msg_win
    
    1330 1348
             """
    
    1349
    +        if not self.parent_win: # minimized groupchat
    
    1350
    +            return
    
    1331 1351
             self.parent_win.redraw_tab(self)
    
    1332 1352
             self.parent_win.show_title()
    
    1333 1353
             # TODO : get the contact and check get_show_in_roster()
    

  • gajim/common/app.py
    ... ... @@ -152,6 +152,7 @@ gajim_common_features = [
    152 152
         nbxmpp.NS_BYTESTREAM,
    
    153 153
         nbxmpp.NS_FILE,
    
    154 154
         nbxmpp.NS_MUC,
    
    155
    +    nbxmpp.NS_CHATMARKERS,
    
    155 156
         nbxmpp.NS_COMMANDS,
    
    156 157
         nbxmpp.NS_DISCO_INFO,
    
    157 158
         nbxmpp.NS_LAST,
    

  • gajim/common/connection.py
    ... ... @@ -308,6 +308,9 @@ class CommonConnection:
    308 308
                     obj.session.last_send = time.time()
    
    309 309
                     msg_iq.setThread(obj.session.thread_id)
    
    310 310
     
    
    311
    +        if obj.message:
    
    312
    +            msg_iq.setMarkable()
    
    313
    +
    
    311 314
             self._push_stanza_message_outgoing(obj, msg_iq)
    
    312 315
     
    
    313 316
         def _push_stanza_message_outgoing(self, obj, msg_iq):
    
    ... ... @@ -1637,6 +1640,7 @@ class Connection(CommonConnection, ConnectionHandlers):
    1637 1640
             msg_iq.setID(obj.stanza_id)
    
    1638 1641
             if obj.message:
    
    1639 1642
                 msg_iq.setOriginID(obj.stanza_id)
    
    1643
    +            msg_iq.setMarkable()
    
    1640 1644
     
    
    1641 1645
             if obj.correct_id:
    
    1642 1646
                 msg_iq.setTag('replace', attrs={'id': obj.correct_id},
    

  • gajim/common/events.py
    ... ... @@ -95,13 +95,14 @@ class PmEvent(ChatEvent):
    95 95
     class PrintedChatEvent(Event):
    
    96 96
         type_ = 'printed_chat'
    
    97 97
         def __init__(self, message, subject, control, msg_log_id, time_=None,
    
    98
    -    show_in_roster=False, show_in_systray=True):
    
    98
    +    message_id=None, show_in_roster=False, show_in_systray=True):
    
    99 99
             Event.__init__(self, time_, show_in_roster=show_in_roster,
    
    100 100
                 show_in_systray=show_in_systray)
    
    101 101
             self.message = message
    
    102 102
             self.subject = subject
    
    103 103
             self.control = control
    
    104 104
             self.msg_log_id = msg_log_id
    
    105
    +        self.message_id = message_id
    
    105 106
     
    
    106 107
     class PrintedGcMsgEvent(PrintedChatEvent):
    
    107 108
         type_ = 'printed_gc_msg'
    

  • gajim/common/jingle_content.py
    ... ... @@ -88,7 +88,7 @@ class JingleContent:
    88 88
                 'session-terminate': [],
    
    89 89
                 'transport-info': [self.__on_transport_info],
    
    90 90
                 'transport-replace': [self.__on_transport_replace],
    
    91
    -            'transport-accept': [],
    
    91
    +            'transport-accept': [self.__on_transport_replace],
    
    92 92
                 'transport-reject': [],
    
    93 93
                 'iq-result': [],
    
    94 94
                 'iq-error': [],
    

  • gajim/common/jingle_ft.py
    ... ... @@ -312,7 +312,7 @@ class JingleFileTransfer(JingleContent):
    312 312
         def __on_iq_result(self, stanza, content, error, action):
    
    313 313
             log.info("__on_iq_result")
    
    314 314
     
    
    315
    -        if self.state == State.NOT_STARTED:
    
    315
    +        if self.state in (State.NOT_STARTED, State.CAND_RECEIVED):
    
    316 316
                 self.__state_changed(State.INITIALIZED)
    
    317 317
             elif self.state == State.CAND_SENT_AND_RECEIVED:
    
    318 318
                 if not self.nominated_cand['our-cand'] and \
    

  • gajim/common/jingle_session.py
    ... ... @@ -140,7 +140,9 @@ class JingleSession:
    140 140
                 'transport-info':       [self.__ack, self.__broadcast],
    
    141 141
                 'transport-replace':    [self.__ack, self.__broadcast,
    
    142 142
                                          self.__on_transport_replace], #TODO
    
    143
    -            'transport-accept':     [self.__ack], #TODO
    
    143
    +            'transport-accept':     [self.__ack, self.__on_session_accept,
    
    144
    +                                     self.__on_content_accept,
    
    145
    +                                     self.__broadcast],
    
    144 146
                 'transport-reject':     [self.__ack], #TODO
    
    145 147
                 'iq-result':            [self.__broadcast],
    
    146 148
                 'iq-error':             [self.__on_error],
    
    ... ... @@ -244,8 +246,9 @@ class JingleSession:
    244 246
             Currently used for transport replacement
    
    245 247
             '''
    
    246 248
             content = self.contents[(creator, name)]
    
    247
    -        transport.set_sid(content.transport.sid)
    
    248
    -        transport.set_file_props(content.transport.file_props)
    
    249
    +        file_props = content.transport.file_props
    
    250
    +        file_props.transport_sid = transport.sid
    
    251
    +        transport.set_file_props(file_props)
    
    249 252
             content.transport = transport
    
    250 253
             # The content will have to be resend now that it is modified
    
    251 254
             content.sent = False
    
    ... ... @@ -410,12 +413,13 @@ class JingleSession:
    410 413
                         # Anyway, content's transport is not modifiable yet
    
    411 414
                         pass
    
    412 415
                     elif transport_ns == nbxmpp.NS_JINGLE_IBB:
    
    413
    -                    transport = JingleTransportIBB()
    
    416
    +                    transport = JingleTransportIBB(node=content.getTag(
    
    417
    +                        'transport'))
    
    414 418
                         self.modify_content(creator, name, transport)
    
    415 419
                         self.state = JingleStates.PENDING
    
    416 420
                         self.contents[(creator, name)].state = State.TRANSPORT_REPLACE
    
    417 421
                         self.__ack(stanza, jingle, error, action)
    
    418
    -                    self.__session_accept()
    
    422
    +                    self.__transport_accept(transport)
    
    419 423
                     else:
    
    420 424
                         stanza, jingle = self.__make_jingle('transport-reject')
    
    421 425
                         content = jingle.setTag('content', attrs={'creator': creator,
    
    ... ... @@ -834,3 +838,12 @@ class JingleSession:
    834 838
                                                                        conn=self.connection,
    
    835 839
                                                                        jingle_session=self,
    
    836 840
                                                                        media=media))
    
    841
    +
    
    842
    +    def __transport_accept(self, transport):
    
    843
    +        assert self.state != JingleStates.ENDED
    
    844
    +        stanza, jingle = self.__make_jingle('transport-accept')
    
    845
    +        self.__append_contents(jingle)
    
    846
    +        self.__broadcast(stanza, jingle, None, 'transport-accept')
    
    847
    +        self.connection.connection.send(stanza)
    
    848
    +        self.collect_iq_id(stanza.getID())
    
    849
    +        self.state = JingleStates.ACTIVE

  • gajim/common/modules/__init__.py
    ... ... @@ -40,6 +40,7 @@ MODULES = [
    40 40
         'bookmarks',
    
    41 41
         'caps',
    
    42 42
         'carbons',
    
    43
    +    'chat_markers',
    
    43 44
         'chatstates',
    
    44 45
         'delimiter',
    
    45 46
         'discovery',
    

  • gajim/common/modules/chat_markers.py
    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
    +# Chat Markers (XEP-0333)
    
    16
    +
    
    17
    +import nbxmpp
    
    18
    +from nbxmpp.structs import StanzaHandler
    
    19
    +
    
    20
    +from gajim.common import app
    
    21
    +from gajim.common.nec import NetworkEvent
    
    22
    +from gajim.common.modules.base import BaseModule
    
    23
    +
    
    24
    +
    
    25
    +class ChatMarkers(BaseModule):
    
    26
    +
    
    27
    +    _nbxmpp_extends = 'ChatMarkers'
    
    28
    +
    
    29
    +    def __init__(self, con):
    
    30
    +        BaseModule.__init__(self, con)
    
    31
    +
    
    32
    +        self.handlers = [
    
    33
    +            StanzaHandler(name='message',
    
    34
    +                          callback=self._process_read_state_sync,
    
    35
    +                          ns=nbxmpp.NS_CHATMARKERS,
    
    36
    +                          priority=47),
    
    37
    +            StanzaHandler(name='message',
    
    38
    +                          callback=self._process_chat_marker,
    
    39
    +                          ns=nbxmpp.NS_CHATMARKERS,
    
    40
    +                          priority=48),
    
    41
    +        ]
    
    42
    +
    
    43
    +    def _process_read_state_sync(self, _con, _stanza, properties):
    
    44
    +        if not properties.is_marker:
    
    45
    +            return
    
    46
    +
    
    47
    +        if not properties.marker.is_displayed:
    
    48
    +            return
    
    49
    +
    
    50
    +        if properties.type.is_error:
    
    51
    +            return
    
    52
    +
    
    53
    +        if properties.is_mam_message:
    
    54
    +            return
    
    55
    +
    
    56
    +        if properties.type.is_groupchat:
    
    57
    +            manager = self._con.get_module('MUC').get_manager()
    
    58
    +            muc_data = manager.get(properties.muc_jid)
    
    59
    +            if muc_data is None:
    
    60
    +                return
    
    61
    +
    
    62
    +            if properties.muc_nickname != muc_data.nick:
    
    63
    +                return
    
    64
    +
    
    65
    +        elif not properties.is_carbon_message or not properties.carbon.is_sent:
    
    66
    +            return
    
    67
    +
    
    68
    +        self._log.info('Read state sync: %s %s',
    
    69
    +                       properties.jid,
    
    70
    +                       properties.marker.id)
    
    71
    +
    
    72
    +        app.nec.push_outgoing_event(
    
    73
    +            NetworkEvent('read-state-sync',
    
    74
    +                         account=self._account,
    
    75
    +                         jid=properties.jid,
    
    76
    +                         properties=properties,
    
    77
    +                         type=properties.type,
    
    78
    +                         is_muc_pm=properties.is_muc_pm,
    
    79
    +                         marker_id=properties.marker.id))
    
    80
    +
    
    81
    +        raise nbxmpp.NodeProcessed
    
    82
    +
    
    83
    +    def _process_chat_marker(self, _con, _stanza, properties):
    
    84
    +        if not properties.is_marker:
    
    85
    +            return
    
    86
    +        # TODO: Implement showing displayed state in ConversationsTextview
    
    87
    +        raise nbxmpp.NodeProcessed
    
    88
    +
    
    89
    +    def send_marker(self, jid, marker, id_, is_gc):
    
    90
    +        if is_gc:
    
    91
    +            message = nbxmpp.Message(to=jid, typ='groupchat')
    
    92
    +        else:
    
    93
    +            message = nbxmpp.Message(to=jid, typ='chat')
    
    94
    +        message.setTag(marker, namespace=nbxmpp.NS_CHATMARKERS,
    
    95
    +            attrs={'id': id_})
    
    96
    +        self._nbxmpp().send(message)
    
    97
    +
    
    98
    +    def send_displayed_marker(self, jid, id_, is_gc):
    
    99
    +        self.send_marker(jid, 'displayed', id_, is_gc)
    
    100
    +
    
    101
    +
    
    102
    +def get_instance(*args, **kwargs):
    
    103
    +    return ChatMarkers(*args, **kwargs), 'ChatMarkers'

  • gajim/common/modules/mam.py
    ... ... @@ -174,7 +174,7 @@ class MAM(BaseModule):
    174 174
             if groupchat:
    
    175 175
                 event_attrs.update(self._parse_gc_attrs(properties))
    
    176 176
             else:
    
    177
    -            event_attrs.update(self._parse_chat_attrs(stanza, properties))
    
    177
    +            event_attrs.update(self._parse_chat_attrs(properties))
    
    178 178
     
    
    179 179
             stanza_id, message_id = self._get_unique_id(properties)
    
    180 180
     
    
    ... ... @@ -243,17 +243,13 @@ class MAM(BaseModule):
    243 243
                     'real_jid': real_jid,
    
    244 244
                     'kind': KindConstant.GC_MSG}
    
    245 245
     
    
    246
    -    def _parse_chat_attrs(self, stanza, properties):
    
    247
    -        frm = properties.jid
    
    248
    -        to = stanza.getTo()
    
    249
    -        if frm.bareMatch(self._con.get_own_jid()):
    
    250
    -            with_ = to
    
    246
    +    def _parse_chat_attrs(self, properties):
    
    247
    +        if properties.from_.bareMatch(self._con.get_own_jid()):
    
    251 248
                 kind = KindConstant.CHAT_MSG_SENT
    
    252 249
             else:
    
    253
    -            with_ = frm
    
    254 250
                 kind = KindConstant.CHAT_MSG_RECV
    
    255 251
     
    
    256
    -        return {'with_': with_,
    
    252
    +        return {'with_': properties.jid,
    
    257 253
                     'nick': None,
    
    258 254
                     'kind': kind}
    
    259 255
     
    

  • gajim/common/modules/message.py
    ... ... @@ -69,9 +69,7 @@ class Message(BaseModule):
    69 69
                 stanza=stanza,
    
    70 70
                 account=self._account))
    
    71 71
     
    
    72
    -        forwarded = properties.carbon_type is not None
    
    73
    -        sent = properties.carbon_type == 'sent'
    
    74
    -        if sent:
    
    72
    +        if properties.is_carbon_message and properties.carbon.is_sent:
    
    75 73
                 # Ugly, we treat the from attr as the remote jid,
    
    76 74
                 # to make that work with sent carbons we have to do this.
    
    77 75
                 # TODO: Check where in Gajim and plugins we depend on that behavior
    
    ... ... @@ -157,8 +155,8 @@ class Message(BaseModule):
    157 155
                 'id_': properties.id,
    
    158 156
                 'encrypted': False,
    
    159 157
                 'additional_data': additional_data,
    
    160
    -            'forwarded': forwarded,
    
    161
    -            'sent': sent,
    
    158
    +            'forwarded': properties.is_carbon_message,
    
    159
    +            'sent': properties.is_carbon_message and properties.carbon.is_sent,
    
    162 160
                 'fjid': fjid,
    
    163 161
                 'jid': jid,
    
    164 162
                 'resource': resource,
    

  • gajim/gajim.py
    ... ... @@ -67,12 +67,16 @@ def _init_gui(gui):
    67 67
     
    
    68 68
     def _init_gtk():
    
    69 69
         import gi
    
    70
    -    gi.require_version('GLib', '2.0')
    
    71
    -    gi.require_version('Gio', '2.0')
    
    72
    -    gi.require_version('Gtk', '3.0')
    
    73
    -    gi.require_version('Gdk', '3.0')
    
    74
    -    gi.require_version('GObject', '2.0')
    
    75
    -    gi.require_version('Pango', '1.0')
    
    70
    +    try:
    
    71
    +        gi.require_version('GLib', '2.0')
    
    72
    +        gi.require_version('Gio', '2.0')
    
    73
    +        gi.require_version('Gtk', '3.0')
    
    74
    +        gi.require_version('Gdk', '3.0')
    
    75
    +        gi.require_version('GObject', '2.0')
    
    76
    +        gi.require_version('Pango', '1.0')
    
    77
    +        gi.require_version('Soup', '2.4')
    
    78
    +    except ValueError as error:
    
    79
    +        sys.exit('Missing dependency: %s' % error)
    
    76 80
     
    
    77 81
         from gi.repository import Gtk
    
    78 82
         gtk_ver = '%s.%s.%s' % (Gtk.get_major_version(),
    

  • gajim/groupchat_control.py
    ... ... @@ -847,7 +847,8 @@ class GroupchatControl(ChatControlBase):
    847 847
     
    
    848 848
         def on_private_message(self, nick, sent, msg, tim, xhtml, session,
    
    849 849
                                msg_log_id=None, encrypted=False,
    
    850
    -                           displaymarking=None):
    
    850
    +                           displaymarking=None, message_id=None):
    
    851
    +
    
    851 852
             # Do we have a queue?
    
    852 853
             fjid = self.room_jid + '/' + nick
    
    853 854
     
    
    ... ... @@ -862,7 +863,9 @@ class GroupchatControl(ChatControlBase):
    862 863
                                    session=session,
    
    863 864
                                    form_node=None,
    
    864 865
                                    displaymarking=displaymarking,
    
    865
    -                               sent_forwarded=sent)
    
    866
    +                               sent_forwarded=sent,
    
    867
    +                               message_id=message_id)
    
    868
    +
    
    866 869
             app.events.add_event(self.account, fjid, event)
    
    867 870
     
    
    868 871
             autopopup = app.config.get('autopopup')
    
    ... ... @@ -1106,7 +1109,8 @@ class GroupchatControl(ChatControlBase):
    1106 1109
                     self.on_private_message(
    
    1107 1110
                         nick, obj.sent, obj.msgtxt, obj.timestamp,
    
    1108 1111
                         obj.xhtml, self.session, msg_log_id=obj.msg_log_id,
    
    1109
    -                    encrypted=obj.encrypted, displaymarking=obj.displaymarking)
    
    1112
    +                    encrypted=obj.encrypted, displaymarking=obj.displaymarking,
    
    1113
    +                    message_id=obj.message_id)
    
    1110 1114
     
    
    1111 1115
         def _nec_ping(self, obj):
    
    1112 1116
             if self.contact.jid != obj.contact.room_jid:
    

  • gajim/gui_interface.py
    ... ... @@ -285,6 +285,39 @@ class Interface:
    285 285
             if ctrl and ctrl.session and len(obj.contact_list) > 1:
    
    286 286
                 ctrl.remove_session(ctrl.session)
    
    287 287
     
    
    288
    +    def handle_event_read_state_sync(self, event):
    
    289
    +        if event.type.is_groupchat:
    
    290
    +            control = self.get_groupchat_control(event.account,
    
    291
    +                                                 event.jid.getBare())
    
    292
    +            if control is None:
    
    293
    +                log.warning('Groupchat control not found')
    
    294
    +                return
    
    295
    +
    
    296
    +            jid = event.jid.getBare()
    
    297
    +            types = ['printed_gc_msg', 'printed_marked_gc_msg']
    
    298
    +
    
    299
    +        else:
    
    300
    +            types = ['chat', 'pm', 'printed_chat', 'printed_pm']
    
    301
    +            jid = event.jid.getBare()
    
    302
    +            if event.is_muc_pm:
    
    303
    +                jid = event.jid
    
    304
    +
    
    305
    +            control = app.interface.msg_win_mgr.get_control(jid, event.account)
    
    306
    +
    
    307
    +        # Compare with control.last_msg_id.
    
    308
    +        events_ = app.events.get_events(event.account, jid, types)
    
    309
    +        if not events_:
    
    310
    +            log.warning('No Events')
    
    311
    +            return
    
    312
    +
    
    313
    +        if events_[-1].message_id != event.marker_id:
    
    314
    +            return
    
    315
    +
    
    316
    +        if not app.events.remove_events(event.account, jid, types=types):
    
    317
    +            # There were events to remove
    
    318
    +            if control is not None:
    
    319
    +                control.redraw_after_event_removed(event.jid)
    
    320
    +
    
    288 321
         def handle_event_msgerror(self, obj):
    
    289 322
             #'MSGERROR' (account, (jid, error_code, error_msg, msg, time[session]))
    
    290 323
             account = obj.conn.name
    
    ... ... @@ -1173,6 +1206,7 @@ class Interface:
    1173 1206
                 'unsubscribed-presence-received': [
    
    1174 1207
                     self.handle_event_unsubscribed_presence],
    
    1175 1208
                 'zeroconf-name-conflict': [self.handle_event_zc_name_conflict],
    
    1209
    +            'read-state-sync': [self.handle_event_read_state_sync],
    
    1176 1210
             }
    
    1177 1211
     
    
    1178 1212
         def register_core_handlers(self):
    
    ... ... @@ -1449,6 +1483,12 @@ class Interface:
    1449 1483
     ### Methods for opening new messages controls
    
    1450 1484
     ################################################################################
    
    1451 1485
     
    
    1486
    +    def get_groupchat_control(self, account, room_jid):
    
    1487
    +        control = self.minimized_controls[account].get(room_jid)
    
    1488
    +        if control is None:
    
    1489
    +            control = self.msg_win_mgr.get_gc_control(room_jid, account)
    
    1490
    +        return control
    
    1491
    +
    
    1452 1492
         def show_groupchat(self, account, room_jid):
    
    1453 1493
             minimized_control = self.minimized_controls[account].get(room_jid)
    
    1454 1494
             if minimized_control is not None:
    

  • gajim/plugins/pluginmanager.py
    ... ... @@ -553,10 +553,10 @@ class PluginManager(metaclass=Singleton):
    553 553
             '''
    
    554 554
             for plugin in self.plugins:
    
    555 555
                 if not plugin.active:
    
    556
    -                return
    
    556
    +                continue
    
    557 557
     
    
    558 558
                 if not hasattr(plugin, 'modules'):
    
    559
    -                return
    
    559
    +                continue
    
    560 560
     
    
    561 561
                 for module in plugin.modules:
    
    562 562
                     instance, name = module.get_instance(con)