Daniel Brötzmann pushed to branch master at gajim / gajim
Commits: 42adccad by wurstsalat at 2022-11-24T20:54:40+01:00 refactor: MoreMenuButton: Use Gio.Menu
Fixes #11262
- - - - -
7 changed files:
- gajim/gtk/chat_stack.py - gajim/gtk/const.py - gajim/gtk/conversation/rows/message.py - gajim/gtk/conversation/rows/widgets.py - gajim/gtk/main.py - gajim/gtk/menus.py - gajim/gtk/structs.py
Changes:
===================================== gajim/gtk/chat_stack.py ===================================== @@ -537,6 +537,7 @@ def _update_group_chat_actions(self, contact: GroupchatContact) -> None:
app.window.get_action('quote').set_enabled(joined) app.window.get_action('mention').set_enabled(joined) + app.window.get_action('retract-message').set_enabled(joined)
def _update_participant_actions(self, contact: GroupchatParticipant) -> None:
===================================== gajim/gtk/const.py ===================================== @@ -194,6 +194,8 @@ def __str__(self): ('show-emoji-chooser', None, True), ('activate-message-selection', 'u', True), ('correct-message', None, False), + ('copy-message', 's', True), + ('retract-message', 'a{sv}', False), ('quote', 's', False), ('mention', 's', False), ('send-file-httpupload', None, False),
===================================== gajim/gtk/conversation/rows/message.py ===================================== @@ -21,7 +21,6 @@ from datetime import timedelta import textwrap
-from gi.repository import Gdk from gi.repository import GdkPixbuf from gi.repository import GLib from gi.repository import Gtk @@ -38,7 +37,6 @@ from gajim.common.const import Trust from gajim.common.const import TRUST_SYMBOL_DATA from gajim.common.helpers import AdditionalDataDict -from gajim.common.helpers import from_one_line from gajim.common.helpers import get_group_chat_nick from gajim.common.helpers import message_needs_highlight from gajim.common.helpers import to_user_string @@ -55,8 +53,6 @@ from .widgets import MessageIcons from .widgets import MoreMenuButton from ..message_widget import MessageWidget -from ...dialogs import InputDialog -from ...dialogs import DialogButton from ...preview import PreviewWidget from ...util import format_fingerprint
@@ -314,37 +310,6 @@ def is_mergeable(self, message: MessageRow) -> bool: def get_text(self) -> str: return self._message_widget.get_text()
- def on_copy_message(self, _widget: Gtk.Widget) -> None: - time_format = from_one_line(app.settings.get('chat_timestamp_format')) - timestamp_formatted = self.timestamp.strftime(time_format) - - clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) - text = self.get_text() - clip.set_text(f'{timestamp_formatted} - {self.name}: {text}', -1) - - def on_quote_message(self, _widget: Gtk.Widget) -> None: - app.window.activate_action( - 'quote', GLib.Variant('s', self.get_text())) - - def on_correct_message(self, _widget: Gtk.Widget) -> None: - app.window.activate_action('correct-message', None) - - def on_retract_message(self, _widget: Gtk.Widget) -> None: - def _on_retract(reason: str) -> None: - self._client.get_module('MUC').retract_message( - self._contact.jid, self.stanza_id, reason or None) - - InputDialog( - _('Retract Message'), - _('Retract message?'), - _('Why do you want to retract this message?'), - [DialogButton.make('Cancel'), - DialogButton.make('Remove', - text=_('_Retract'), - callback=_on_retract)], - input_str=_('Spam'), - transient_for=app.window).show() - def _get_encryption_image(self, additional_data: AdditionalDataDict, ) -> Optional[Gtk.Image]:
===================================== gajim/gtk/conversation/rows/widgets.py ===================================== @@ -28,13 +28,13 @@ from gajim.common import app from gajim.common.const import AvatarSize from gajim.common.i18n import Q_ -from gajim.common.helpers import is_retraction_allowed from gajim.common.modules.contacts import GroupchatContact from gajim.common.types import ChatContactT
if TYPE_CHECKING: from .message import MessageRow
+from ...menus import get_chat_row_menu from ...menus import get_groupchat_participant_menu from ...util import wrap_with_event_box from ...util import get_cursor @@ -76,112 +76,18 @@ def __init__(self, self.connect('clicked', self._on_click)
def _on_click(self, _button: Gtk.Button) -> None: - show_retract = False - if isinstance(self._contact, GroupchatContact): - if not self._contact.is_joined: - self._create_popover() - return - - contact = self._contact.get_resource(self._name) - self_contact = self._contact.get_self() - assert self_contact is not None - is_allowed = is_retraction_allowed(self_contact, contact) - - disco_info = app.storage.cache.get_last_disco_info( - self._contact.jid) - assert disco_info is not None - - if disco_info.has_message_moderation and is_allowed: - show_retract = True - - show_correction = False - if self._row.message_id is not None: - show_correction = app.window.is_message_correctable( - self._contact, self._row.message_id) - - self._create_popover(show_retract, - show_correction) - - def _create_popover(self, - show_retract: bool = False, - show_correction: bool = False - ) -> None: - - menu_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6) - menu_box.get_style_context().add_class('padding-6') - - quote_enabled = True - if isinstance(self._contact, GroupchatContact): - if self._contact.is_joined: - self_contact = self._contact.get_self() - assert self_contact is not None - quote_enabled = not self_contact.role.is_visitor - else: - quote_enabled = False - - quote_button = Gtk.ModelButton() - quote_button.set_halign(Gtk.Align.START) - quote_button.connect('clicked', self._row.on_quote_message) - quote_button.set_label(Q_('?Message row action:Quote…')) - quote_button.set_image(Gtk.Image.new_from_icon_name( - 'mail-reply-sender-symbolic', Gtk.IconSize.MENU)) - quote_button.set_sensitive(quote_enabled) - menu_box.add(quote_button) - - copy_button = Gtk.ModelButton() - copy_button.set_halign(Gtk.Align.START) - copy_button.connect('clicked', self._row.on_copy_message) - copy_button.set_label(Q_('?Message row action:Copy')) - copy_button.set_image(Gtk.Image.new_from_icon_name( - 'edit-copy-symbolic', Gtk.IconSize.MENU)) - menu_box.add(copy_button) - - select_button = Gtk.ModelButton() - select_button.set_halign(Gtk.Align.START) - select_button.connect('clicked', self._on_activate_message_selection) - select_button.set_label(Q_('?Message row action:Select Messages…')) - select_button.set_image(Gtk.Image.new_from_icon_name( - 'edit-select-symbolic', Gtk.IconSize.MENU)) - menu_box.add(select_button) - - if show_correction: - correct_button = Gtk.ModelButton() - correct_button.set_halign(Gtk.Align.START) - correct_button.connect( - 'clicked', self._row.on_correct_message) - correct_button.set_label(Q_('?Message row action:Correct…')) - correct_button.set_image(Gtk.Image.new_from_icon_name( - 'document-edit-symbolic', Gtk.IconSize.MENU)) - menu_box.add(correct_button) - - if show_retract: - retract_button = Gtk.ModelButton() - retract_button.set_halign(Gtk.Align.START) - retract_button.connect( - 'clicked', self._row.on_retract_message) - retract_button.set_label(Q_('?Message row action:Retract…')) - retract_button.set_image(Gtk.Image.new_from_icon_name( - 'edit-undo-symbolic', Gtk.IconSize.MENU)) - menu_box.add(retract_button) - - menu_box.show_all() - - popover = Gtk.PopoverMenu() - popover.add(menu_box) - popover.set_relative_to(self) - popover.set_position(Gtk.PositionType.BOTTOM) - popover.connect('closed', self._on_closed) + menu = get_chat_row_menu( + self._contact, + self._name, + self._row.get_text(), + self._row.timestamp, + self._row.message_id, + self._row.stanza_id, + self._row.log_line_id) + + popover = GajimPopover(menu, relative_to=self) popover.popup()
- def _on_activate_message_selection(self, _button: Gtk.ModelButton) -> None: - app.window.activate_action( - 'activate-message-selection', - GLib.Variant('u', self._row.log_line_id or 0)) - - @staticmethod - def _on_closed(popover: Gtk.Popover) -> None: - GLib.idle_add(popover.destroy) -
class DateTimeLabel(Gtk.Label): def __init__(self, timestamp: datetime) -> None:
===================================== gajim/gtk/main.py ===================================== @@ -54,6 +54,7 @@ from .dialogs import DialogButton from .dialogs import ConfirmationDialog from .dialogs import ConfirmationCheckDialog +from .dialogs import InputDialog from .builder import get_builder from .util import get_app_window from .util import resize_window @@ -65,6 +66,7 @@ from .structs import AddChatActionParams from .structs import actionmethod from .structs import ChatListEntryParam +from .structs import RetractMessageParam
if TYPE_CHECKING: from .control import ChatControl @@ -401,6 +403,8 @@ def _connect_actions(self) -> None: ('switch-workspace-8', self._on_action), ('switch-workspace-9', self._on_action), ('toggle-chat-list', self._on_action), + ('copy-message', self._on_copy_message), + ('retract-message', self._on_retract_message), ('add-workspace', self._add_workspace), ('edit-workspace', self._edit_workspace), ('remove-workspace', self._remove_workspace), @@ -510,6 +514,36 @@ def _toggle_chat_list(self) -> None: 'go-previous-symbolic', Gtk.IconSize.BUTTON) self._chat_page.toggle_chat_list()
+ def _on_copy_message(self, + _action: Gio.SimpleAction, + param: GLib.Variant + ) -> None: + + clip = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) + clip.set_text(param.get_string(), -1) + + @actionmethod + def _on_retract_message(self, + _action: Gio.SimpleAction, + params: RetractMessageParam + ) -> None: + + def _on_retract(reason: str) -> None: + client = app.get_client(params.account) + client.get_module('MUC').retract_message( + params.jid, params.stanza_id, reason or None) + + InputDialog( + _('Retract Message'), + _('Retract message?'), + _('Why do you want to retract this message?'), + [DialogButton.make('Cancel'), + DialogButton.make('Remove', + text=_('_Retract'), + callback=_on_retract)], + input_str=_('Spam'), + transient_for=app.window).show() + def _on_window_motion_notify(self, _widget: Gtk.ApplicationWindow, _event: Gdk.EventMotion
===================================== gajim/gtk/menus.py ===================================== @@ -21,6 +21,7 @@ from typing import Optional from typing import Union
+from datetime import datetime import textwrap from urllib.parse import quote
@@ -32,10 +33,13 @@
from gajim.common import app from gajim.common import types +from gajim.common.helpers import from_one_line from gajim.common.helpers import is_affiliation_change_allowed +from gajim.common.helpers import is_retraction_allowed from gajim.common.helpers import is_role_change_allowed from gajim.common.helpers import jid_is_blocked from gajim.common.i18n import _ +from gajim.common.i18n import Q_ from gajim.common.i18n import get_short_lang_code from gajim.common.const import URIType from gajim.common.const import XmppUriQuery @@ -48,6 +52,7 @@ from gajim.gui.structs import AccountJidParam from gajim.gui.structs import ChatListEntryParam from gajim.gui.structs import RemoveHistoryActionParams +from gajim.gui.structs import RetractMessageParam from gajim.gui.util import GajimMenu
@@ -594,6 +599,77 @@ def get_component_search_menu(jid: Optional[str], copy_text: str) -> Gio.Menu: return menu
+def get_chat_row_menu(contact: types.ChatContactT, + name: str, + text: str, + timestamp: datetime, + message_id: Optional[str], + stanza_id: Optional[str], + log_line_id: Optional[int] + ) -> GajimMenu: + + menu_items: MenuItemListT = [] + + text = text.replace('"', '\"') + + time_format = from_one_line(app.settings.get('chat_timestamp_format')) + timestamp_formatted = timestamp.strftime(time_format) + copy_text = f'{timestamp_formatted} - {name}: {text}' + menu_items.append( + (Q_('?Message row action:Copy'), 'win.copy-message', f'"{copy_text}"')) + + menu_items.append( + (Q_('?Message row action:Select Messages…'), + 'win.activate-message-selection', + GLib.Variant('u', log_line_id or 0))) + + show_quote = True + if isinstance(contact, GroupchatContact): + if contact.is_joined: + self_contact = contact.get_self() + assert self_contact is not None + show_quote = not self_contact.role.is_visitor + else: + show_quote = False + if show_quote: + menu_items.append(( + Q_('?Message row action:Quote…'), + 'win.quote', + f'"{text}"')) + + show_correction = False + if message_id is not None: + show_correction = app.window.is_message_correctable( + contact, message_id) + if show_correction: + menu_items.append(( + Q_('?Message row action:Correct…'), 'win.correct-message', None)) + + show_retract = False + if isinstance(contact, GroupchatContact) and contact.is_joined: + resource_contact = contact.get_resource(name) + self_contact = contact.get_self() + assert self_contact is not None + is_allowed = is_retraction_allowed(self_contact, resource_contact) + + disco_info = app.storage.cache.get_last_disco_info(contact.jid) + assert disco_info is not None + + if disco_info.has_message_moderation and is_allowed: + show_retract = True + if show_retract: + assert stanza_id is not None + param = RetractMessageParam( + account=contact.account, + jid=contact.jid, + stanza_id=stanza_id) + menu_items.append(( + Q_('?Message row action:Retract…'), 'win.retract-message', param)) + + menu = GajimMenu.from_list(menu_items) + return menu + + def get_format_menu() -> GajimMenu:
menuitems: MenuItemListT = [
===================================== gajim/gtk/structs.py ===================================== @@ -21,7 +21,9 @@ import inspect import functools from dataclasses import dataclass -from gi.repository import GLib, Gio + +from gi.repository import Gio +from gi.repository import GLib
from nbxmpp.protocol import JID
@@ -66,6 +68,13 @@ class AccountJidParam(VariantMixin): jid: JID
+@dataclass +class RetractMessageParam(VariantMixin): + account: str + jid: JID + stanza_id: str + + def get_params_class(func: Callable[..., Any]) -> Any: module = sys.modules[__name__] params = inspect.signature(func).parameters
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/commit/42adccad22a91815d9f504943c1744156...