Daniel Brötzmann pushed to branch newcontrol at gajim / gajim
Commits:
-
e45b19c0
by wurstsalat at 2022-07-20T21:51:03+02:00
-
1e44ef2f
by wurstsalat at 2022-07-21T20:29:53+02:00
11 changed files:
- gajim/common/storage/archive.py
- gajim/gtk/chat_stack.py
- gajim/gtk/const.py
- gajim/gtk/controls/base.py
- gajim/gtk/controls/chat.py
- gajim/gtk/controls/groupchat.py
- gajim/gtk/conversation/rows/message.py
- gajim/gtk/conversation/rows/widgets.py
- gajim/gtk/conversation/view.py
- gajim/gtk/main.py
- gajim/gtk/message_actions_box.py
Changes:
| ... | ... | @@ -992,6 +992,35 @@ def find_stanza_id(self, |
| 992 | 992 | return True
|
| 993 | 993 | return False
|
| 994 | 994 | |
| 995 | + @timeit
|
|
| 996 | + def get_last_correctable_message(self,
|
|
| 997 | + account: str,
|
|
| 998 | + jid: JID,
|
|
| 999 | + message_id: str
|
|
| 1000 | + ) -> Optional[LastConversationRow]:
|
|
| 1001 | + """
|
|
| 1002 | + Load the last correctable message of a conversation by message_id.
|
|
| 1003 | + Conditions: max 5 min old
|
|
| 1004 | + """
|
|
| 1005 | + jids = [jid]
|
|
| 1006 | + account_id = self.get_account_id(account)
|
|
| 1007 | + min_time = time.time() - 5 * 60
|
|
| 1008 | + print(min_time)
|
|
| 1009 | + |
|
| 1010 | + sql = '''
|
|
| 1011 | + SELECT contact_name, time, kind, message, stanza_id, message_id,
|
|
| 1012 | + additional_data
|
|
| 1013 | + FROM logs NATURAL JOIN jids WHERE jid IN ({jids})
|
|
| 1014 | + AND account_id = {account_id}
|
|
| 1015 | + AND message_id = ?
|
|
| 1016 | + AND time > ?
|
|
| 1017 | + '''.format(jids=', '.join('?' * len(jids)),
|
|
| 1018 | + account_id=account_id)
|
|
| 1019 | + |
|
| 1020 | + return self._con.execute(
|
|
| 1021 | + sql,
|
|
| 1022 | + tuple(jids) + (message_id, min_time)).fetchone()
|
|
| 1023 | + |
|
| 995 | 1024 | @timeit
|
| 996 | 1025 | def store_message_correction(self,
|
| 997 | 1026 | account: str,
|
| ... | ... | @@ -306,6 +306,8 @@ def _update_base_actions(self, contact: ChatContactT) -> None: |
| 306 | 306 | httpupload.get_enabled())
|
| 307 | 307 | |
| 308 | 308 | app.window.get_action('show-contact-info').set_enabled(online)
|
| 309 | + app.window.get_action('correct-message').set_enabled(online)
|
|
| 310 | + |
|
| 309 | 311 | app.window.get_action('input-bold').set_enabled(True)
|
| 310 | 312 | app.window.get_action('input-italic').set_enabled(True)
|
| 311 | 313 | app.window.get_action('input-strike').set_enabled(True)
|
| ... | ... | @@ -538,11 +540,11 @@ def _on_send_message(self) -> None: |
| 538 | 540 | |
| 539 | 541 | label = self._message_action_box.get_seclabel()
|
| 540 | 542 | |
| 541 | - correct_id: Optional[str] = None
|
|
| 542 | - if control.correcting and control.last_sent_msg:
|
|
| 543 | - correct_id = control.last_sent_msg
|
|
| 544 | - else:
|
|
| 545 | - correct_id = None
|
|
| 543 | + correct_id = None
|
|
| 544 | + if self._message_action_box.is_correcting:
|
|
| 545 | + correct_id = self._message_action_box.last_message_id.get(
|
|
| 546 | + (contact.account, contact.jid))
|
|
| 547 | + self._message_action_box.is_correcting = False
|
|
| 546 | 548 | |
| 547 | 549 | chatstate = client.get_module('Chatstate').get_active_chatstate(
|
| 548 | 550 | contact)
|
| ... | ... | @@ -563,8 +565,9 @@ def _on_send_message(self) -> None: |
| 563 | 565 | client.send_message(message_)
|
| 564 | 566 | |
| 565 | 567 | self._message_action_box.msg_textview.clear()
|
| 566 | - self._message_action_box.msg_textview.get_style_context().remove_class(
|
|
| 567 | - 'gajim-msg-correcting')
|
|
| 568 | + |
|
| 569 | + def get_last_message_id(self, account: str, jid: JID) -> Optional[str]:
|
|
| 570 | + return self._message_action_box.last_message_id.get((account, jid))
|
|
| 568 | 571 | |
| 569 | 572 | def _close_control(self) -> None:
|
| 570 | 573 | assert self._current_contact is not None
|
| ... | ... | @@ -582,6 +585,7 @@ def process_event(self, event: Any) -> None: |
| 582 | 585 | if isinstance(event, MucDiscoUpdate):
|
| 583 | 586 | self._on_muc_disco_update(event)
|
| 584 | 587 | self._control_stack.process_event(event)
|
| 588 | + self._message_action_box.process_event(event)
|
|
| 585 | 589 | |
| 586 | 590 | |
| 587 | 591 | class ChatPlaceholderBox(Gtk.Box):
|
| ... | ... | @@ -194,6 +194,7 @@ def __str__(self): |
| 194 | 194 | ('input-strike', None, False),
|
| 195 | 195 | ('input-clear', None, False),
|
| 196 | 196 | ('show-emoji-chooser', None, False),
|
| 197 | + ('correct-message', None, False),
|
|
| 197 | 198 | ('quote', 's', False),
|
| 198 | 199 | ('mention', 's', False),
|
| 199 | 200 | ('send-file-httpupload', None, False),
|
| ... | ... | @@ -182,15 +182,10 @@ def __init__(self, widget_name: str, account: str, jid: JID) -> None: |
| 182 | 182 | # XEP-0333 Chat Markers
|
| 183 | 183 | self.last_msg_id: Optional[str] = None
|
| 184 | 184 | |
| 185 | - # XEP-0308 Message Correction
|
|
| 186 | - self.last_sent_msg: Optional[str] = None
|
|
| 187 | - |
|
| 188 | 185 | # XEP-0172 User Nickname
|
| 189 | 186 | # TODO:
|
| 190 | 187 | self.user_nick: Optional[str] = None
|
| 191 | 188 | |
| 192 | - self.sendmessage: bool = True
|
|
| 193 | - |
|
| 194 | 189 | self._client.get_module('Chatstate').set_active(self.contact)
|
| 195 | 190 | |
| 196 | 191 | self.encryption: Optional[str] = self.get_encryption_state()
|
| ... | ... | @@ -501,11 +496,8 @@ def add_message(self, |
| 501 | 496 | self.last_msg_id = message_id
|
| 502 | 497 | |
| 503 | 498 | if kind == 'incoming':
|
| 504 | - # Record the history of received messages
|
|
| 505 | - # self.save_message(text, 'received')
|
|
| 506 | - |
|
| 507 | - # Issue notification
|
|
| 508 | 499 | if notify:
|
| 500 | + # Issue notification
|
|
| 509 | 501 | self._notify(name, text, tim, additional_data)
|
| 510 | 502 | |
| 511 | 503 | if not chat_active and notify:
|
| ... | ... | @@ -70,12 +70,6 @@ def __init__(self, account: str, jid: JID) -> None: |
| 70 | 70 | 'chat_control',
|
| 71 | 71 | account,
|
| 72 | 72 | jid)
|
| 73 | - |
|
| 74 | - self.sendmessage: bool = True
|
|
| 75 | - |
|
| 76 | - # XEP-0308 Message Correction
|
|
| 77 | - self.last_sent_msg: Optional[str] = None
|
|
| 78 | - |
|
| 79 | 73 | # PluginSystem: adding GUI extension point for this ChatControl
|
| 80 | 74 | # instance object
|
| 81 | 75 | app.plugin_manager.gui_extension_point('chat_control', self)
|
| ... | ... | @@ -132,13 +126,6 @@ def _on_message_sent(self, event: events.MessageSent) -> None: |
| 132 | 126 | if not event.message:
|
| 133 | 127 | return
|
| 134 | 128 | |
| 135 | - if event.correct_id is None:
|
|
| 136 | - oob_url = event.additional_data.get_value('gajim', 'oob_url')
|
|
| 137 | - if oob_url == event.message:
|
|
| 138 | - self.last_sent_msg = None
|
|
| 139 | - else:
|
|
| 140 | - self.last_sent_msg = event.message_id
|
|
| 141 | - |
|
| 142 | 129 | message_id = event.message_id
|
| 143 | 130 | |
| 144 | 131 | if event.label:
|
| ... | ... | @@ -82,15 +82,6 @@ def __init__(self, account: str, jid: JID) -> None: |
| 82 | 82 | |
| 83 | 83 | self.room_jid = str(self.contact.jid)
|
| 84 | 84 | |
| 85 | - # Last sent message text
|
|
| 86 | - self.last_sent_txt: str = ''
|
|
| 87 | - |
|
| 88 | - # Attribute, encryption plugins use to signal the message can be sent
|
|
| 89 | - self.sendmessage: bool = False
|
|
| 90 | - |
|
| 91 | - # XEP-0308 Message Correction
|
|
| 92 | - self.last_sent_msg: Optional[str] = None
|
|
| 93 | - |
|
| 94 | 85 | self._groupchat_state = GroupchatState(self.contact)
|
| 95 | 86 | self.xml.conv_view_overlay.add_overlay(self._groupchat_state)
|
| 96 | 87 | |
| ... | ... | @@ -226,9 +217,6 @@ def _on_gc_message_received(self, event: events.GcMessageReceived) -> None: |
| 226 | 217 | displaymarking=event.displaymarking,
|
| 227 | 218 | additional_data=event.additional_data)
|
| 228 | 219 | else:
|
| 229 | - if event.properties.muc_nickname == self.contact.nickname:
|
|
| 230 | - self.last_sent_txt = event.msgtxt
|
|
| 231 | - |
|
| 232 | 220 | self.add_message(event.msgtxt,
|
| 233 | 221 | contact=event.properties.muc_nickname,
|
| 234 | 222 | tim=event.properties.timestamp,
|
| ... | ... | @@ -634,16 +622,7 @@ def _on_room_destroyed(self, |
| 634 | 622 | self.add_info_message(join_message)
|
| 635 | 623 | |
| 636 | 624 | def _on_message_sent(self, event: events.MessageSent) -> None:
|
| 637 | - if not event.message:
|
|
| 638 | - return
|
|
| 639 | - # we'll save sent message text when we'll receive it in
|
|
| 640 | - # _nec_gc_message_received
|
|
| 641 | - if event.correct_id is None:
|
|
| 642 | - oob_url = event.additional_data.get_value('gajim', 'oob_url')
|
|
| 643 | - if oob_url == event.message:
|
|
| 644 | - self.last_sent_msg = None
|
|
| 645 | - else:
|
|
| 646 | - self.last_sent_msg = event.message_id
|
|
| 625 | + pass
|
|
| 647 | 626 | |
| 648 | 627 | def _on_message_error(self, event: events.MessageError) -> None:
|
| 649 | 628 | self.conversation_view.show_error(event.message_id, event.error)
|
| ... | ... | @@ -319,6 +319,9 @@ def on_quote_message(self, _widget: Gtk.Widget) -> None: |
| 319 | 319 | app.window.activate_action(
|
| 320 | 320 | 'quote', GLib.Variant('s', self._message_widget.get_text()))
|
| 321 | 321 | |
| 322 | + def on_correct_message(self, _widget: Gtk.Widget) -> None:
|
|
| 323 | + app.window.activate_action('correct-message', None)
|
|
| 324 | + |
|
| 322 | 325 | def on_retract_message(self, _widget: Gtk.Widget) -> None:
|
| 323 | 326 | def _on_retract(reason: str) -> None:
|
| 324 | 327 | self._client.get_module('MUC').retract_message(
|
| ... | ... | @@ -14,8 +14,7 @@ |
| 14 | 14 | |
| 15 | 15 | from __future__ import annotations
|
| 16 | 16 | |
| 17 | -from typing import Union
|
|
| 18 | -from typing import Any
|
|
| 17 | +from typing import TYPE_CHECKING
|
|
| 19 | 18 | |
| 20 | 19 | from gi.repository import Gtk
|
| 21 | 20 | from gi.repository import Pango
|
| ... | ... | @@ -24,13 +23,13 @@ |
| 24 | 23 | from gajim.common import app
|
| 25 | 24 | from gajim.common.i18n import _
|
| 26 | 25 | from gajim.common.helpers import is_retraction_allowed
|
| 27 | -from gajim.common.modules.contacts import BareContact
|
|
| 28 | 26 | from gajim.common.modules.contacts import GroupchatContact
|
| 29 | -from gajim.common.modules.contacts import GroupchatParticipant
|
|
| 27 | +from gajim.common.types import ChatContactT
|
|
| 30 | 28 | |
| 31 | -from ...util import wrap_with_event_box
|
|
| 29 | +if TYPE_CHECKING:
|
|
| 30 | + from .message import MessageRow
|
|
| 32 | 31 | |
| 33 | -ContactT = Union[BareContact, GroupchatContact, GroupchatParticipant]
|
|
| 32 | +from ...util import wrap_with_event_box
|
|
| 34 | 33 | |
| 35 | 34 | |
| 36 | 35 | class SimpleLabel(Gtk.Label):
|
| ... | ... | @@ -45,8 +44,8 @@ def __init__(self) -> None: |
| 45 | 44 | @wrap_with_event_box
|
| 46 | 45 | class MoreMenuButton(Gtk.Button):
|
| 47 | 46 | def __init__(self,
|
| 48 | - row: Any,
|
|
| 49 | - contact: ContactT,
|
|
| 47 | + row: MessageRow,
|
|
| 48 | + contact: ChatContactT,
|
|
| 50 | 49 | name: str
|
| 51 | 50 | ) -> None:
|
| 52 | 51 | |
| ... | ... | @@ -70,23 +69,33 @@ def _on_click(self, _button: Gtk.Button) -> None: |
| 70 | 69 | show_retract = False
|
| 71 | 70 | if isinstance(self._contact, GroupchatContact):
|
| 72 | 71 | if not self._contact.is_joined:
|
| 73 | - self._create_popover(False)
|
|
| 72 | + self._create_popover(show_retract=False)
|
|
| 74 | 73 | return
|
| 75 | 74 | |
| 76 | - disco_info = app.storage.cache.get_last_disco_info(
|
|
| 77 | - self._contact.jid)
|
|
| 78 | - assert disco_info is not None
|
|
| 79 | - |
|
| 80 | 75 | contact = self._contact.get_resource(self._name)
|
| 81 | 76 | self_contact = self._contact.get_self()
|
| 82 | 77 | assert self_contact is not None
|
| 83 | - |
|
| 84 | 78 | is_allowed = is_retraction_allowed(self_contact, contact)
|
| 79 | + |
|
| 80 | + disco_info = app.storage.cache.get_last_disco_info(
|
|
| 81 | + self._contact.jid)
|
|
| 82 | + assert disco_info is not None
|
|
| 83 | + |
|
| 85 | 84 | if disco_info.has_message_moderation and is_allowed:
|
| 86 | 85 | show_retract = True
|
| 87 | - self._create_popover(show_retract)
|
|
| 88 | 86 | |
| 89 | - def _create_popover(self, show_retract: bool) -> None:
|
|
| 87 | + show_correction = False
|
|
| 88 | + if self._row.message_id is not None:
|
|
| 89 | + show_correction = app.window.is_message_correctable(
|
|
| 90 | + self._contact.account, self._contact.jid, self._row.message_id)
|
|
| 91 | + |
|
| 92 | + self._create_popover(show_retract=show_retract,
|
|
| 93 | + show_correction=show_correction)
|
|
| 94 | + |
|
| 95 | + def _create_popover(self,
|
|
| 96 | + show_retract: bool = False,
|
|
| 97 | + show_correction: bool = False
|
|
| 98 | + ) -> None:
|
|
| 90 | 99 | menu_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
| 91 | 100 | menu_box.get_style_context().add_class('padding-6')
|
| 92 | 101 | |
| ... | ... | @@ -116,6 +125,16 @@ def _create_popover(self, show_retract: bool) -> None: |
| 116 | 125 | 'edit-copy-symbolic', Gtk.IconSize.MENU))
|
| 117 | 126 | menu_box.add(copy_button)
|
| 118 | 127 | |
| 128 | + if show_correction:
|
|
| 129 | + correct_button = Gtk.ModelButton()
|
|
| 130 | + correct_button.set_halign(Gtk.Align.START)
|
|
| 131 | + correct_button.connect(
|
|
| 132 | + 'clicked', self._row.on_correct_message)
|
|
| 133 | + correct_button.set_label(_('Correct'))
|
|
| 134 | + correct_button.set_image(Gtk.Image.new_from_icon_name(
|
|
| 135 | + 'document-edit-symbolic', Gtk.IconSize.MENU))
|
|
| 136 | + menu_box.add(correct_button)
|
|
| 137 | + |
|
| 119 | 138 | if show_retract:
|
| 120 | 139 | retract_button = Gtk.ModelButton()
|
| 121 | 140 | retract_button.set_halign(Gtk.Align.START)
|
| ... | ... | @@ -380,7 +380,7 @@ def _reduce_messages_before(self) -> bool: |
| 380 | 380 | row1 = self.get_row_at_index(2)
|
| 381 | 381 | row2 = self.get_row_at_index(3)
|
| 382 | 382 | |
| 383 | - if row1.type == row2.type == 'date':
|
|
| 383 | + if row1.type == 'date' and row2.type == 'date':
|
|
| 384 | 384 | # First two rows are date rows,
|
| 385 | 385 | # it’s safe to remove the fist row
|
| 386 | 386 | row1.destroy()
|
| ... | ... | @@ -38,7 +38,8 @@ |
| 38 | 38 | from gajim.common.ged import EventHelper
|
| 39 | 39 | from gajim.common.i18n import _
|
| 40 | 40 | from gajim.common.modules.bytestream import is_transfer_active
|
| 41 | -from gajim.gtk.const import MAIN_WIN_ACTIONS, MAIN_WIN_STATEFUL_ACTIONS
|
|
| 41 | +from gajim.gtk.const import MAIN_WIN_ACTIONS
|
|
| 42 | +from gajim.gtk.const import MAIN_WIN_STATEFUL_ACTIONS
|
|
| 42 | 43 | from gajim.plugins.pluginmanager import PluginManifest
|
| 43 | 44 | from gajim.plugins.repository import PluginRepository
|
| 44 | 45 | |
| ... | ... | @@ -731,6 +732,20 @@ def is_chat_loaded(self, account: str, jid: JID) -> bool: |
| 731 | 732 | def chat_exists(self, account: str, jid: JID) -> bool:
|
| 732 | 733 | return self._chat_page.chat_exists(account, jid)
|
| 733 | 734 | |
| 735 | + def is_message_correctable(self,
|
|
| 736 | + account: str,
|
|
| 737 | + jid: JID,
|
|
| 738 | + message_id: str
|
|
| 739 | + ) -> bool:
|
|
| 740 | + chat_stack = self._chat_page.get_chat_stack()
|
|
| 741 | + last_message_id = chat_stack.get_last_message_id(account, jid)
|
|
| 742 | + if last_message_id is None or last_message_id != message_id:
|
|
| 743 | + return False
|
|
| 744 | + |
|
| 745 | + message_row = app.storage.archive.get_last_correctable_message(
|
|
| 746 | + account, jid, last_message_id)
|
|
| 747 | + return message_row is not None
|
|
| 748 | + |
|
| 734 | 749 | def get_total_unread_count(self) -> int:
|
| 735 | 750 | chat_list_stack = self._chat_page.get_chat_list_stack()
|
| 736 | 751 | return chat_list_stack.get_total_unread_count()
|
| ... | ... | @@ -32,8 +32,10 @@ |
| 32 | 32 | |
| 33 | 33 | from nbxmpp.const import Chatstate
|
| 34 | 34 | from nbxmpp.modules.security_labels import SecurityLabel
|
| 35 | +from nbxmpp.protocol import JID
|
|
| 35 | 36 | |
| 36 | 37 | from gajim.common import app
|
| 38 | +from gajim.common import events
|
|
| 37 | 39 | from gajim.common import i18n
|
| 38 | 40 | from gajim.common.i18n import _
|
| 39 | 41 | from gajim.common.client import Client
|
| ... | ... | @@ -65,7 +67,7 @@ |
| 65 | 67 | |
| 66 | 68 | class MessageActionsBox(Gtk.Grid):
|
| 67 | 69 | def __init__(self) -> None:
|
| 68 | - super().__init__()
|
|
| 70 | + Gtk.Grid.__init__(self)
|
|
| 69 | 71 | |
| 70 | 72 | self._client: Optional[Client] = None
|
| 71 | 73 | self._contact: Optional[ChatContactT] = None
|
| ... | ... | @@ -75,8 +77,13 @@ def __init__(self) -> None: |
| 75 | 77 | |
| 76 | 78 | self.attach(self._ui.box, 0, 0, 1, 1)
|
| 77 | 79 | |
| 80 | + # For undo
|
|
| 78 | 81 | self.space_pressed = False
|
| 79 | 82 | |
| 83 | + # XEP-0308 Message Correction
|
|
| 84 | + self.is_correcting = False
|
|
| 85 | + self.last_message_id: dict[tuple[str, JID], Optional[str]] = {}
|
|
| 86 | + |
|
| 80 | 87 | self._ui.send_message_button.set_visible(
|
| 81 | 88 | app.settings.get('show_send_message_button'))
|
| 82 | 89 | app.settings.bind_signal('show_send_message_button',
|
| ... | ... | @@ -90,7 +97,7 @@ def __init__(self) -> None: |
| 90 | 97 | self.msg_textview.get_buffer().connect('changed',
|
| 91 | 98 | self._on_buffer_changed)
|
| 92 | 99 | self.msg_textview.connect('key-press-event',
|
| 93 | - self._on_textview_key_press_event)
|
|
| 100 | + self._on_msg_textview_key_press_event)
|
|
| 94 | 101 | self.msg_textview.connect('paste-clipboard',
|
| 95 | 102 | self._on_paste_clipboard)
|
| 96 | 103 | |
| ... | ... | @@ -133,6 +140,7 @@ def _connect_actions(self) -> None: |
| 133 | 140 | 'show-emoji-chooser',
|
| 134 | 141 | 'quote',
|
| 135 | 142 | 'mention',
|
| 143 | + 'correct-message',
|
|
| 136 | 144 | ]
|
| 137 | 145 | |
| 138 | 146 | for action in actions:
|
| ... | ... | @@ -179,6 +187,9 @@ def _on_action(self, |
| 179 | 187 | assert param
|
| 180 | 188 | self.msg_textview.mention_participant(param.get_string())
|
| 181 | 189 | |
| 190 | + elif action_name == 'correct-message':
|
|
| 191 | + self.toggle_message_correction()
|
|
| 192 | + |
|
| 182 | 193 | def switch_contact(self, contact: ChatContactT) -> None:
|
| 183 | 194 | if self._client is not None:
|
| 184 | 195 | self._set_chatstate(False)
|
| ... | ... | @@ -503,10 +514,10 @@ def _on_buffer_changed(self, textbuffer: Gtk.TextBuffer) -> None: |
| 503 | 514 | client.get_module('Chatstate').set_chatstate(
|
| 504 | 515 | self._contact, Chatstate.COMPOSING)
|
| 505 | 516 | |
| 506 | - def _on_textview_key_press_event(self,
|
|
| 507 | - textview: MessageInputTextView,
|
|
| 508 | - event: Gdk.EventKey
|
|
| 509 | - ) -> bool:
|
|
| 517 | + def _on_msg_textview_key_press_event(self,
|
|
| 518 | + textview: MessageInputTextView,
|
|
| 519 | + event: Gdk.EventKey
|
|
| 520 | + ) -> bool:
|
|
| 510 | 521 | # pylint: disable=too-many-nested-blocks
|
| 511 | 522 | if event.keyval == Gdk.KEY_space:
|
| 512 | 523 | self.space_pressed = True
|
| ... | ... | @@ -525,24 +536,33 @@ def _on_textview_key_press_event(self, |
| 525 | 536 | True))
|
| 526 | 537 | self.space_pressed = False
|
| 527 | 538 | |
| 539 | + event_state = event.get_state()
|
|
| 528 | 540 | # Ctrl [+ Shift] + Tab are not forwarded to notebook. We handle it here
|
| 529 | - if event.get_state() & Gdk.ModifierType.SHIFT_MASK:
|
|
| 530 | - if (event.get_state() & Gdk.ModifierType.CONTROL_MASK and
|
|
| 531 | - event.keyval == Gdk.KEY_ISO_Left_Tab):
|
|
| 532 | - app.window.select_next_chat(Direction.PREV, unread_first=True)
|
|
| 533 | - return True
|
|
| 541 | + if event_state & Gdk.ModifierType.SHIFT_MASK:
|
|
| 542 | + if event_state & Gdk.ModifierType.CONTROL_MASK:
|
|
| 543 | + if event.keyval == Gdk.KEY_ISO_Left_Tab:
|
|
| 544 | + app.window.select_next_chat(
|
|
| 545 | + Direction.PREV, unread_first=True)
|
|
| 546 | + return True
|
|
| 534 | 547 | |
| 535 | 548 | if event.keyval in (Gdk.KEY_Page_Down, Gdk.KEY_Page_Up):
|
| 536 | 549 | self.conversation_view.event(event)
|
| 537 | 550 | return True
|
| 538 | 551 | |
| 539 | - if event.get_state() & Gdk.ModifierType.CONTROL_MASK:
|
|
| 552 | + if event_state & Gdk.ModifierType.CONTROL_MASK:
|
|
| 540 | 553 | if event.keyval == Gdk.KEY_Tab:
|
| 541 | 554 | app.window.select_next_chat(Direction.NEXT, unread_first=True)
|
| 542 | 555 | return True
|
| 543 | 556 | |
| 544 | - event_state = event.get_state()
|
|
| 545 | - if event.keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter): # ENTER
|
|
| 557 | + if event.keyval == Gdk.KEY_z:
|
|
| 558 | + self.msg_textview.undo()
|
|
| 559 | + return True
|
|
| 560 | + |
|
| 561 | + if event.keyval == Gdk.KEY_Up:
|
|
| 562 | + self.toggle_message_correction()
|
|
| 563 | + return True
|
|
| 564 | + |
|
| 565 | + if event.keyval in (Gdk.KEY_Return, Gdk.KEY_KP_Enter):
|
|
| 546 | 566 | if event_state & Gdk.ModifierType.SHIFT_MASK:
|
| 547 | 567 | textview.insert_newline()
|
| 548 | 568 | return True
|
| ... | ... | @@ -566,11 +586,6 @@ def _on_textview_key_press_event(self, |
| 566 | 586 | app.window.activate_action('send-message', None)
|
| 567 | 587 | return True
|
| 568 | 588 | |
| 569 | - if event.keyval == Gdk.KEY_z: # CTRL+z
|
|
| 570 | - if event_state & Gdk.ModifierType.CONTROL_MASK:
|
|
| 571 | - self.msg_textview.undo()
|
|
| 572 | - return True
|
|
| 573 | - |
|
| 574 | 589 | return False
|
| 575 | 590 | |
| 576 | 591 | def _on_paste_clipboard(self,
|
| ... | ... | @@ -616,3 +631,52 @@ def _paste_event_confirmed(self, |
| 616 | 631 | |
| 617 | 632 | assert self._contact is not None
|
| 618 | 633 | app.interface.start_file_transfer(self._contact, path)
|
| 634 | + |
|
| 635 | + def toggle_message_correction(self) -> None:
|
|
| 636 | + if self.is_correcting:
|
|
| 637 | + self.is_correcting = False
|
|
| 638 | + self.msg_textview.clear()
|
|
| 639 | + self.msg_textview.get_style_context().remove_class(
|
|
| 640 | + 'gajim-msg-correcting')
|
|
| 641 | + self.msg_textview.grab_focus()
|
|
| 642 | + return
|
|
| 643 | + |
|
| 644 | + assert self._contact is not None
|
|
| 645 | + last_message_id = self.last_message_id.get(
|
|
| 646 | + (self._contact.account, self._contact.jid))
|
|
| 647 | + if last_message_id is None:
|
|
| 648 | + return
|
|
| 649 | + |
|
| 650 | + message_row = app.storage.archive.get_last_correctable_message(
|
|
| 651 | + self._contact.account, self._contact.jid, last_message_id)
|
|
| 652 | + if message_row is None:
|
|
| 653 | + return
|
|
| 654 | + |
|
| 655 | + self.is_correcting = True
|
|
| 656 | + self.msg_textview.clear()
|
|
| 657 | + self.msg_textview.insert_text(message_row.message)
|
|
| 658 | + self.msg_textview.get_style_context().add_class('gajim-msg-correcting')
|
|
| 659 | + self.msg_textview.grab_focus()
|
|
| 660 | + |
|
| 661 | + def _on_message_sent(self, event: events.MessageSent) -> None:
|
|
| 662 | + if not event.message:
|
|
| 663 | + return
|
|
| 664 | + |
|
| 665 | + if event.correct_id is None:
|
|
| 666 | + # This wasn't a corrected message
|
|
| 667 | + assert self._contact is not None
|
|
| 668 | + oob_url = event.additional_data.get_value('gajim', 'oob_url')
|
|
| 669 | + account = self._contact.account
|
|
| 670 | + jid = self._contact.jid
|
|
| 671 | + if oob_url == event.message:
|
|
| 672 | + # Don't allow to correct HTTP Upload file transfer URLs
|
|
| 673 | + self.last_message_id[(account, jid)] = None
|
|
| 674 | + else:
|
|
| 675 | + self.last_message_id[(account, jid)] = event.message_id
|
|
| 676 | + |
|
| 677 | + self.msg_textview.get_style_context().remove_class(
|
|
| 678 | + 'gajim-msg-correcting')
|
|
| 679 | + |
|
| 680 | + def process_event(self, event: events.ApplicationEvent) -> None:
|
|
| 681 | + if isinstance(event, events.MessageSent):
|
|
| 682 | + self._on_message_sent(event) |