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

Commits:

10 changed files:

Changes:

  • gajim/common/helpers.py
    ... ... @@ -64,6 +64,7 @@
    64 64
     
    
    65 65
     from nbxmpp.namespaces import Namespace
    
    66 66
     from nbxmpp.const import Role
    
    67
    +from nbxmpp.const import Chatstate
    
    67 68
     from nbxmpp.const import ConnectionProtocol
    
    68 69
     from nbxmpp.const import ConnectionType
    
    69 70
     from nbxmpp.const import Affiliation
    
    ... ... @@ -304,17 +305,23 @@ def from_one_line(msg: str) -> str:
    304 305
         return msg
    
    305 306
     
    
    306 307
     
    
    307
    -def get_uf_chatstate(chatstate: str) -> str:
    
    308
    -    '''
    
    309
    -    Remove chatstate jargon and returns user friendly messages
    
    310
    -    '''
    
    311
    -    if chatstate in ('inactive', 'gone'):
    
    312
    -        return _('is doing something else')
    
    313
    -    if chatstate == 'composing':
    
    308
    +def chatstate_to_string(chatstate: Optional[Chatstate]) -> str:
    
    309
    +    if chatstate is None:
    
    310
    +        return ''
    
    311
    +
    
    312
    +    if chatstate == Chatstate.ACTIVE:
    
    313
    +        return ''
    
    314
    +
    
    315
    +    if chatstate == Chatstate.COMPOSING:
    
    314 316
             return _('is composing a message…')
    
    315
    -    if chatstate == 'paused':
    
    317
    +
    
    318
    +    if chatstate in (Chatstate.INACTIVE, Chatstate.GONE):
    
    319
    +        return _('is doing something else')
    
    320
    +
    
    321
    +    if chatstate == Chatstate.PAUSED:
    
    316 322
             return _('paused composing a message')
    
    317
    -    return ''
    
    323
    +
    
    324
    +    raise ValueError('unknown value: %s' % chatstate)
    
    318 325
     
    
    319 326
     
    
    320 327
     def exec_command(command: str,
    

  • gajim/common/modules/contacts.py
    ... ... @@ -45,6 +45,7 @@
    45 45
     from gajim.common.structs import UNKNOWN_MUC_PRESENCE
    
    46 46
     from gajim.common.structs import MUCPresenceData
    
    47 47
     from gajim.common.helpers import Observable
    
    48
    +from gajim.common.helpers import chatstate_to_string
    
    48 49
     from gajim.common.modules.base import BaseModule
    
    49 50
     from gajim.common.modules.util import LogAdapter
    
    50 51
     from gajim.common.helpers import get_groupchat_name
    
    ... ... @@ -322,6 +323,14 @@ def is_jingle_available(self) -> bool:
    322 323
         def get_address(self, _prefer_real: bool = True) -> JID:
    
    323 324
             return self._jid
    
    324 325
     
    
    326
    +    @property
    
    327
    +    def chatstate(self) -> Optional[Chatstate]:
    
    328
    +        return None
    
    329
    +
    
    330
    +    @property
    
    331
    +    def chatstate_string(self) -> str:
    
    332
    +        return chatstate_to_string(self.chatstate)
    
    333
    +
    
    325 334
         def __repr__(self) -> str:
    
    326 335
             return f'{self.jid} ({self._account})'
    
    327 336
     
    

  • gajim/common/preview.py
    ... ... @@ -237,8 +237,13 @@ def _accept_uri(urlparts: ParseResult,
    237 237
             if urlparts.scheme in ('https', 'http'):
    
    238 238
                 if app.settings.get('preview_allow_all_images'):
    
    239 239
                     mime_type = guess_mime_type(uri)
    
    240
    -                if (mime_type not in MIME_TYPES or
    
    241
    -                        mime_type == 'application/octet-stream'):
    
    240
    +                if mime_type not in MIME_TYPES:
    
    241
    +                    return False
    
    242
    +
    
    243
    +                if mime_type == 'application/octet-stream' and uri != oob_url:
    
    244
    +                    # guess_mime_type yields 'application/octet-stream' for
    
    245
    +                    # paths without suffix. Check oob_url to make sure we
    
    246
    +                    # display a preview for files sent via http upload.
    
    242 247
                         return False
    
    243 248
                     return True
    
    244 249
     
    
    ... ... @@ -345,7 +350,7 @@ def _on_orig_load_finished(self,
    345 350
                 log.error('%s: %s', preview.orig_path.name, error)
    
    346 351
                 return
    
    347 352
     
    
    348
    -        preview.mime_type = guess_mime_type(preview.orig_path)
    
    353
    +        preview.mime_type = guess_mime_type(preview.orig_path, data)
    
    349 354
             preview.file_size = os.path.getsize(preview.orig_path)
    
    350 355
             if preview.is_previewable:
    
    351 356
                 if preview.create_thumbnail(data):
    
    ... ... @@ -368,7 +373,7 @@ def _on_thumb_load_finished(data: Optional[bytes],
    368 373
                 return
    
    369 374
     
    
    370 375
             preview.thumbnail = data
    
    371
    -        preview.mime_type = guess_mime_type(preview.orig_path)
    
    376
    +        preview.mime_type = guess_mime_type(preview.orig_path, data)
    
    372 377
             preview.file_size = os.path.getsize(preview.orig_path)
    
    373 378
     
    
    374 379
             try:
    

  • gajim/data/gui/search_view.ui
    1 1
     <?xml version="1.0" encoding="UTF-8"?>
    
    2
    -<!-- Generated with glade 3.38.2 -->
    
    2
    +<!-- Generated with glade 3.40.0 -->
    
    3 3
     <interface>
    
    4 4
       <requires lib="gtk+" version="3.24"/>
    
    5 5
       <object class="GtkPopover" id="calendar_popover">
    
    ... ... @@ -215,6 +215,10 @@
    215 215
             <property name="primary-icon-name">edit-find-symbolic</property>
    
    216 216
             <property name="primary-icon-activatable">False</property>
    
    217 217
             <property name="primary-icon-sensitive">False</property>
    
    218
    +        <property name="primary-icon-tooltip-text" translatable="yes">Use filters to narrow down your search:
    
    219
    +from:username
    
    220
    +before:yyyy-mm-dd
    
    221
    +after:yyyy-mm-dd</property>
    
    218 222
             <signal name="activate" handler="_on_search" swapped="no"/>
    
    219 223
           </object>
    
    220 224
           <packing>
    

  • gajim/data/other/shortcuts.json
    ... ... @@ -37,5 +37,7 @@
    37 37
         "win.switch-workspace-7": ["<Primary>7", "<Primary>KP_7"],
    
    38 38
         "win.switch-workspace-8": ["<Primary>8", "<Primary>KP_8"],
    
    39 39
         "win.switch-workspace-9": ["<Primary>9", "<Primary>KP_9"],
    
    40
    -    "win.toggle-chat-list": ["<Primary>R"]
    
    40
    +    "win.toggle-chat-list": ["<Primary>R"],
    
    41
    +    "win.scroll-view-up": ["<Shift>Page_Up"],
    
    42
    +    "win.scroll-view-down": ["<Shift>Page_Down"]
    
    41 43
     }

  • gajim/gtk/chat_banner.py
    ... ... @@ -17,6 +17,7 @@
    17 17
     from typing import Any
    
    18 18
     from typing import Optional
    
    19 19
     
    
    20
    +from gi.repository import GLib
    
    20 21
     from gi.repository import Gtk
    
    21 22
     
    
    22 23
     import cairo
    
    ... ... @@ -31,7 +32,6 @@
    31 32
     from gajim.common.events import MessageReceived
    
    32 33
     from gajim.common.events import MucDiscoUpdate
    
    33 34
     from gajim.common.ged import EventHelper
    
    34
    -from gajim.common.helpers import get_uf_chatstate
    
    35 35
     from gajim.common.i18n import _
    
    36 36
     from gajim.common.modules.contacts import BareContact
    
    37 37
     from gajim.common.modules.contacts import GroupchatContact
    
    ... ... @@ -63,37 +63,54 @@ def __init__(self) -> None:
    63 63
     
    
    64 64
             hide_roster = app.settings.get('hide_groupchat_occupants_list')
    
    65 65
             self._set_toggle_roster_button_icon(hide_roster)
    
    66
    +
    
    66 67
             app.settings.connect_signal(
    
    67 68
                 'hide_groupchat_occupants_list',
    
    68 69
                 self._set_toggle_roster_button_icon)
    
    69 70
     
    
    70
    -        self.show_all()
    
    71
    +        app.settings.connect_signal(
    
    72
    +            'hide_chat_banner',
    
    73
    +            self._update_hide_banner)
    
    71 74
     
    
    72
    -    def clear(self) -> None:
    
    73
    -        if self._contact is not None:
    
    74
    -            self._contact.disconnect_all_from_obj(self)
    
    75
    -            self._contact = None
    
    75
    +        app.settings.connect_signal(
    
    76
    +            'hide_groupchat_banner',
    
    77
    +            self._update_hide_banner)
    
    76 78
     
    
    77
    -        if self._client is not None:
    
    78
    -            self._client.disconnect_all_from_obj(self)
    
    79
    -            self._client = None
    
    79
    +        self.show_all()
    
    80 80
     
    
    81
    +    def clear(self) -> None:
    
    82
    +        self._disconnect_signals()
    
    81 83
             self.unregister_events()
    
    82 84
     
    
    83 85
         def switch_contact(self, contact: types.ChatContactT) -> None:
    
    84
    -        self._update_account_badge(contact.account)
    
    86
    +        self._disconnect_signals()
    
    87
    +        self._client = app.get_client(contact.account)
    
    88
    +        self._contact = contact
    
    89
    +        self._connect_signals()
    
    85 90
     
    
    86
    -        if self._client is not None:
    
    87
    -            self._client.disconnect_all_from_obj(self)
    
    91
    +        if not self.has_events_registered():
    
    92
    +            self.register_events([
    
    93
    +                ('message-received', ged.GUI2, self._on_message_received),
    
    94
    +                ('muc-disco-update', ged.GUI2, self._on_muc_disco_update),
    
    95
    +                ('bookmarks-received', ged.GUI2, self._on_bookmarks_received),
    
    96
    +                ('account-enabled', ged.GUI2, self._on_account_changed),
    
    97
    +                ('account-disabled', ged.GUI2, self._on_account_changed)
    
    98
    +            ])
    
    88 99
     
    
    89
    -        self._client = app.get_client(contact.account)
    
    90
    -        self._client.connect_signal('state-changed',
    
    91
    -                                    self._on_client_state_changed)
    
    100
    +        self._voice_requests_button.switch_contact(self._contact)
    
    92 101
     
    
    93
    -        if self._contact is not None:
    
    94
    -            self._contact.disconnect_all_from_obj(self)
    
    102
    +        self._update_phone_image()
    
    103
    +        self._update_roster_button()
    
    104
    +        self._update_avatar()
    
    105
    +        self._update_visitor_button()
    
    106
    +        self._update_name_label()
    
    107
    +        self._update_account_badge()
    
    108
    +        self._update_hide_banner()
    
    109
    +
    
    110
    +    def _connect_signals(self) -> None:
    
    111
    +        assert self._contact is not None
    
    112
    +        assert self._client is not None
    
    95 113
     
    
    96
    -        self._contact = contact
    
    97 114
             self._contact.multi_connect({
    
    98 115
                 'chatstate-update': self._on_chatstate_update,
    
    99 116
                 'nickname-update': self._on_nickname_update,
    
    ... ... @@ -108,93 +125,81 @@ def switch_contact(self, contact: types.ChatContactT) -> None:
    108 125
                     'state-changed': self._on_muc_state_changed,
    
    109 126
                     'room-voice-request': self._on_room_voice_request
    
    110 127
                 })
    
    111
    -            self._ui.toggle_roster_button.show()
    
    112
    -            hide_banner = app.settings.get('hide_groupchat_banner')
    
    113
    -        else:
    
    114
    -            self._ui.toggle_roster_button.hide()
    
    115
    -            hide_banner = app.settings.get('hide_chat_banner')
    
    116 128
     
    
    117
    -        if isinstance(self._contact, GroupchatParticipant):
    
    129
    +        elif isinstance(self._contact, GroupchatParticipant):
    
    118 130
                 self._contact.multi_connect({
    
    119 131
                     'user-joined': self._on_user_state_changed,
    
    120 132
                     'user-left': self._on_user_state_changed,
    
    121 133
                     'user-avatar-update': self._on_user_avatar_update,
    
    122 134
                 })
    
    123 135
     
    
    124
    -        if not self.has_events_registered():
    
    125
    -            self.register_events([
    
    126
    -                ('message-received', ged.GUI2, self._on_message_received),
    
    127
    -                ('muc-disco-update', ged.GUI2, self._on_muc_disco_update),
    
    128
    -                ('bookmarks-received', ged.GUI2, self._on_bookmarks_received),
    
    129
    -                ('account-enabled', ged.GUI2, self._on_account_changed),
    
    130
    -                ('account-disabled', ged.GUI2, self._on_account_changed)
    
    131
    -            ])
    
    132
    -
    
    133
    -        self._ui.phone_image.set_visible(
    
    134
    -            self._contact in self._last_message_from_phone)
    
    135
    -
    
    136
    -        self._voice_requests_button.switch_contact(self._contact)
    
    136
    +        self._client.connect_signal('state-changed',
    
    137
    +                                    self._on_client_state_changed)
    
    137 138
     
    
    138
    -        if hide_banner:
    
    139
    -            self.set_no_show_all(True)
    
    140
    -            self.hide()
    
    141
    -        else:
    
    142
    -            self.set_no_show_all(False)
    
    143
    -            self.show_all()
    
    139
    +    def _disconnect_signals(self) -> None:
    
    140
    +        if self._contact is not None:
    
    141
    +            self._contact.disconnect_all_from_obj(self)
    
    142
    +            self._contact = None
    
    144 143
     
    
    145
    -        self._update_avatar()
    
    146
    -        self._update_content()
    
    144
    +        if self._client is not None:
    
    145
    +            self._client.disconnect_all_from_obj(self)
    
    146
    +            self._client = None
    
    147 147
     
    
    148 148
         def _on_client_state_changed(self,
    
    149 149
                                      _client: types.Client,
    
    150 150
                                      _signal_name: str,
    
    151 151
                                      state: SimpleClientState):
    
    152
    +
    
    152 153
             self._update_avatar()
    
    153
    -        self._update_content()
    
    154 154
     
    
    155 155
         def _on_presence_update(self,
    
    156 156
                                 _contact: types.BareContact,
    
    157 157
                                 _signal_name: str
    
    158 158
                                 ) -> None:
    
    159
    +
    
    159 160
             self._update_avatar()
    
    160 161
     
    
    161 162
         def _on_chatstate_update(self,
    
    162 163
                                  _contact: types.BareContact,
    
    163 164
                                  _signal_name: str
    
    164 165
                                  ) -> None:
    
    165
    -        self._update_content()
    
    166
    +
    
    167
    +        self._update_name_label()
    
    166 168
     
    
    167 169
         def _on_nickname_update(self,
    
    168 170
                                 _contact: types.BareContact,
    
    169 171
                                 _signal_name: str
    
    170 172
                                 ) -> None:
    
    171
    -        self._update_content()
    
    173
    +
    
    174
    +        self._update_name_label()
    
    172 175
     
    
    173 176
         def _on_avatar_update(self,
    
    174 177
                               _contact: types.BareContact,
    
    175 178
                               _signal_name: str
    
    176 179
                               ) -> None:
    
    180
    +
    
    177 181
             self._update_avatar()
    
    178 182
     
    
    179 183
         def _on_caps_update(self,
    
    180 184
                             _contact: types.BareContact,
    
    181 185
                             _signal_name: str
    
    182 186
                             ) -> None:
    
    187
    +
    
    183 188
             self._update_avatar()
    
    184 189
     
    
    185 190
         def _on_muc_state_changed(self,
    
    186 191
                                   contact: GroupchatContact,
    
    187 192
                                   _signal_name: str
    
    188 193
                                   ) -> None:
    
    189
    -        if contact.is_joined:
    
    190
    -            self._update_content()
    
    194
    +
    
    195
    +        self._update_visitor_button()
    
    191 196
     
    
    192 197
         def _on_room_voice_request(self, *args: Any) -> None:
    
    193 198
             self._voice_requests_button.set_no_show_all(False)
    
    194 199
             self._voice_requests_button.show_all()
    
    195 200
     
    
    196 201
         def _on_user_role_changed(self, *args: Any) -> None:
    
    197
    -        self._update_content()
    
    202
    +        self._update_visitor_button()
    
    198 203
     
    
    199 204
         def _on_user_state_changed(self, *args: Any) -> None:
    
    200 205
             self._update_avatar()
    
    ... ... @@ -206,18 +211,17 @@ def _on_bookmarks_received(self, _event: BookmarksReceived) -> None:
    206 211
             if not isinstance(self._contact, GroupchatContact):
    
    207 212
                 return
    
    208 213
     
    
    209
    -        self._update_content()
    
    214
    +        self._update_name_label()
    
    210 215
     
    
    211 216
         def _on_muc_disco_update(self, event: MucDiscoUpdate) -> None:
    
    212 217
             assert self._contact is not None
    
    213 218
             if event.jid != self._contact.jid:
    
    214 219
                 return
    
    215 220
     
    
    216
    -        self._update_content()
    
    221
    +        self._update_name_label()
    
    217 222
     
    
    218 223
         def _on_account_changed(self, event: AccountEnabled) -> None:
    
    219
    -        assert self._contact is not None
    
    220
    -        self._update_account_badge(self._contact.account)
    
    224
    +        self._update_account_badge()
    
    221 225
     
    
    222 226
         def _on_message_received(self, event: MessageReceived) -> None:
    
    223 227
             if (not isinstance(self._contact, BareContact) or
    
    ... ... @@ -233,7 +237,31 @@ def _on_message_received(self, event: MessageReceived) -> None:
    233 237
             else:
    
    234 238
                 self._last_message_from_phone.discard(self._contact)
    
    235 239
     
    
    236
    -        self._ui.phone_image.set_visible(resource_contact.is_phone)
    
    240
    +        self._update_phone_image()
    
    241
    +
    
    242
    +    def _update_phone_image(self) -> None:
    
    243
    +        self._ui.phone_image.set_visible(
    
    244
    +            self._contact in self._last_message_from_phone)
    
    245
    +
    
    246
    +    def _update_roster_button(self) -> None:
    
    247
    +        self._ui.toggle_roster_button.set_visible(
    
    248
    +            isinstance(self._contact, GroupchatContact))
    
    249
    +
    
    250
    +    def _update_hide_banner(self, *args: Any) -> None:
    
    251
    +        if self._contact is None:
    
    252
    +            return
    
    253
    +
    
    254
    +        if isinstance(self._contact, GroupchatContact):
    
    255
    +            hide_banner = app.settings.get('hide_groupchat_banner')
    
    256
    +        else:
    
    257
    +            hide_banner = app.settings.get('hide_chat_banner')
    
    258
    +
    
    259
    +        if hide_banner:
    
    260
    +            self.set_no_show_all(True)
    
    261
    +            self.hide()
    
    262
    +        else:
    
    263
    +            self.set_no_show_all(False)
    
    264
    +            self.show_all()
    
    237 265
     
    
    238 266
         def _update_avatar(self) -> None:
    
    239 267
             scale = app.window.get_scale_factor()
    
    ... ... @@ -242,54 +270,50 @@ def _update_avatar(self) -> None:
    242 270
             assert isinstance(surface, cairo.ImageSurface)
    
    243 271
             self._ui.avatar_image.set_from_surface(surface)
    
    244 272
     
    
    245
    -    def _update_content(self) -> None:
    
    246
    -        assert self._contact is not None
    
    247
    -        assert self._client is not None
    
    273
    +    def _update_visitor_button(self) -> None:
    
    274
    +        if not isinstance(self._contact, GroupchatContact):
    
    275
    +            self._ui.visitor_box.set_visible(False)
    
    276
    +            return
    
    248 277
     
    
    249
    -        name = self._contact.name
    
    278
    +        if self._contact.is_not_joined:
    
    279
    +            self._ui.visitor_box.set_visible(False)
    
    280
    +            return
    
    250 281
     
    
    251
    -        if self._contact.jid.bare_match(self._client.get_own_jid()):
    
    252
    -            name = _('Note to myself')
    
    282
    +        self_contact = self._contact.get_self()
    
    283
    +        assert self_contact is not None
    
    284
    +        self._ui.visitor_box.set_visible(self_contact.role.is_visitor)
    
    253 285
     
    
    254
    -        if self._contact.is_pm_contact:
    
    255
    -            gc_contact = self._client.get_module('Contacts').get_contact(
    
    256
    -                self._contact.jid.bare)
    
    257
    -            name = f'{name} ({gc_contact.name})'
    
    286
    +    def _update_name_label(self) -> None:
    
    287
    +        assert self._contact is not None
    
    288
    +
    
    289
    +        name = self._get_name_from_contact(self._contact)
    
    258 290
     
    
    259
    -        label_text = f'<span>{name}</span>'
    
    260
    -        label_tooltip = name
    
    261
    -        show_chatstate = app.settings.get('show_chatstate_in_banner')
    
    262
    -        if (show_chatstate and isinstance(
    
    263
    -                self._contact, (BareContact, GroupchatParticipant))):
    
    264
    -            chatstate = self._contact.chatstate
    
    265
    -            if chatstate is not None:
    
    266
    -                chatstate = get_uf_chatstate(chatstate.value)
    
    267
    -            else:
    
    268
    -                chatstate = ''
    
    291
    +        chatstate = ''
    
    292
    +        if app.settings.get('show_chatstate_in_banner'):
    
    293
    +            chatstate = self._contact.chatstate_string
    
    269 294
     
    
    270
    -            label_text = f'<span>{name}</span>' \
    
    271
    -                         f'<span size="60%" weight="light">' \
    
    272
    -                         f' {chatstate}</span>'
    
    273
    -            label_tooltip = f'{name} {chatstate}'
    
    295
    +        label_text = f'<span>{GLib.markup_escape_text(name)}</span>'
    
    296
    +        if chatstate:
    
    297
    +            label_markup = '<span size="60%" weight="light"> %s</span>'
    
    298
    +            label_text += label_markup % GLib.markup_escape_text(chatstate)
    
    274 299
     
    
    275 300
             self._ui.name_label.set_markup(label_text)
    
    276
    -        self._ui.name_label.set_tooltip_text(label_tooltip)
    
    277 301
     
    
    278
    -        if isinstance(self._contact, GroupchatContact):
    
    279
    -            self_contact = self._contact.get_self()
    
    280
    -            if self_contact:
    
    281
    -                self._ui.visitor_box.set_visible(self_contact.role.is_visitor)
    
    282
    -                return
    
    302
    +        tooltip_text = name
    
    303
    +        if chatstate:
    
    304
    +            tooltip_text = f'{name} {chatstate}'
    
    283 305
     
    
    284
    -        self._ui.visitor_box.set_visible(False)
    
    306
    +        self._ui.name_label.set_tooltip_text(tooltip_text)
    
    307
    +
    
    308
    +    def _update_account_badge(self) -> None:
    
    309
    +        assert self._contact is not None
    
    285 310
     
    
    286
    -    def _update_account_badge(self, account: str) -> None:
    
    287 311
             if self._account_badge is not None:
    
    288 312
                 self._account_badge.destroy()
    
    289 313
     
    
    290 314
             enabled_accounts = app.get_enabled_accounts_with_labels()
    
    291 315
             if len(enabled_accounts) > 1:
    
    292
    -            self._account_badge = AccountBadge(account)
    
    316
    +            self._account_badge = AccountBadge(self._contact.account)
    
    293 317
                 self._ui.additional_items_box.pack_end(
    
    294 318
                     self._account_badge, False, True, 0)
    
    295 319
     
    
    ... ... @@ -308,3 +332,17 @@ def _set_toggle_roster_button_icon(self,
    308 332
             icon = 'go-next-symbolic' if not hide_roster else 'go-previous-symbolic'
    
    309 333
             self._ui.toggle_roster_image.set_from_icon_name(
    
    310 334
                 icon, Gtk.IconSize.BUTTON)
    
    335
    +
    
    336
    +    @staticmethod
    
    337
    +    def _get_name_from_contact(contact: types.ChatContactT) -> str:
    
    338
    +        name = contact.name
    
    339
    +
    
    340
    +        if isinstance(contact, BareContact):
    
    341
    +            if contact.is_self:
    
    342
    +                return _('Note to myself')
    
    343
    +            return name
    
    344
    +
    
    345
    +        if isinstance(contact, GroupchatParticipant):
    
    346
    +            return f'{name} ({contact.room.name})'
    
    347
    +
    
    348
    +        return name

  • gajim/gtk/chat_stack.py
    ... ... @@ -704,9 +704,23 @@ def _on_send_message(self) -> None:
    704 704
     
    
    705 705
             correct_id = None
    
    706 706
             if self._message_action_box.is_correcting:
    
    707
    +            self._message_action_box.is_correcting = False
    
    708
    +            context = self._message_action_box.msg_textview.get_style_context()
    
    709
    +            context.remove_class('gajim-msg-correcting')
    
    710
    +
    
    707 711
                 correct_id = self._message_action_box.last_message_id.get(
    
    708 712
                     (contact.account, contact.jid))
    
    709
    -            self._message_action_box.is_correcting = False
    
    713
    +            message_row = app.storage.archive.get_last_correctable_message(
    
    714
    +                contact.account, contact.jid, correct_id)
    
    715
    +            if message_row is None:
    
    716
    +                self._message_action_box.msg_textview.clear()
    
    717
    +                log.info('Trying to correct message older than threshold')
    
    718
    +                return
    
    719
    +
    
    720
    +            if message_row.message == message:
    
    721
    +                self._message_action_box.msg_textview.clear()
    
    722
    +                log.info('Trying to correct message with original text')
    
    723
    +                return
    
    710 724
     
    
    711 725
             chatstate = client.get_module('Chatstate').get_active_chatstate(
    
    712 726
                 contact)
    

  • gajim/gtk/const.py
    ... ... @@ -214,6 +214,8 @@ def __str__(self):
    214 214
         ('muc-change-role', 'as', False),
    
    215 215
         ('muc-change-affiliation', 'as', False),
    
    216 216
         ('muc-request-voice', None, False),
    
    217
    +    ('scroll-view-up', None, True),
    
    218
    +    ('scroll-view-down', None, True),
    
    217 219
     ]
    
    218 220
     
    
    219 221
     
    

  • gajim/gtk/conversation/view.py
    ... ... @@ -15,6 +15,7 @@
    15 15
     from __future__ import annotations
    
    16 16
     
    
    17 17
     from typing import Any
    
    18
    +from typing import Literal
    
    18 19
     from typing import Optional
    
    19 20
     from typing import Union
    
    20 21
     from typing import Generator
    
    ... ... @@ -29,6 +30,7 @@
    29 30
     from gi.repository import GLib
    
    30 31
     from gi.repository import Gtk
    
    31 32
     from gi.repository import GObject
    
    33
    +from gi.repository import Gio
    
    32 34
     
    
    33 35
     from nbxmpp.errors import StanzaError
    
    34 36
     from nbxmpp.modules.security_labels import Displaymarking
    
    ... ... @@ -135,6 +137,22 @@ def __init__(self) -> None:
    135 137
             self.add(self._list_box)
    
    136 138
             self.set_focus_vadjustment(Gtk.Adjustment())
    
    137 139
     
    
    140
    +        app.window.get_action('scroll-view-up').connect(
    
    141
    +            'activate', self._on_scroll_view)
    
    142
    +        app.window.get_action('scroll-view-down').connect(
    
    143
    +            'activate', self._on_scroll_view)
    
    144
    +
    
    145
    +    def _on_scroll_view(self,
    
    146
    +                        action: Gio.SimpleAction,
    
    147
    +                        _param: Literal[None]) -> None:
    
    148
    +
    
    149
    +        action_name = action.get_name()
    
    150
    +        if action_name == 'scroll-view-down':
    
    151
    +            self.emit('scroll-child', Gtk.ScrollType.PAGE_DOWN, False)
    
    152
    +
    
    153
    +        elif action_name == 'scroll-view-up':
    
    154
    +            self.emit('scroll-child', Gtk.ScrollType.PAGE_UP, False)
    
    155
    +
    
    138 156
         def _enable_signal_handlers(self, enable: bool) -> None:
    
    139 157
             if self._signal_handlers_enabled == enable:
    
    140 158
                 return
    

  • gajim/gtk/message_actions_box.py
    ... ... @@ -515,12 +515,6 @@ def _on_msg_textview_key_press_event(self,
    515 515
                             Direction.PREV, unread_first=True)
    
    516 516
                         return True
    
    517 517
     
    
    518
    -            if event.keyval in (Gdk.KEY_Page_Down, Gdk.KEY_Page_Up):
    
    519
    -                control = app.window.get_control()
    
    520
    -                if control.has_active_chat():
    
    521
    -                    control.conversation_view.event(event)
    
    522
    -                    return True
    
    523
    -
    
    524 518
             if event_state & Gdk.ModifierType.CONTROL_MASK:
    
    525 519
                 if event.keyval == Gdk.KEY_Tab:
    
    526 520
                     app.window.select_next_chat(Direction.NEXT, unread_first=True)
    
    ... ... @@ -659,6 +653,3 @@ def _on_message_sent(self, event: events.MessageSent) -> None:
    659 653
                     self.last_message_id[(account, jid)] = None
    
    660 654
                 else:
    
    661 655
                     self.last_message_id[(account, jid)] = event.message_id
    662
    -
    
    663
    -        self.msg_textview.get_style_context().remove_class(
    
    664
    -            'gajim-msg-correcting')