Daniel Brötzmann pushed to branch mainwindow at gajim / gajim
Commits: 54681a79 by wurstsalat at 2021-11-06T18:14:07+01:00 Refactor 'Actions for' context menu
- - - - - 63aa18a9 by wurstsalat at 2021-11-06T18:25:58+01:00 Menu Builder: Use f-strings
- - - - - e484fc87 by wurstsalat at 2021-11-06T18:34:26+01:00 Menu Builder: Typing
- - - - -
2 changed files:
- gajim/gtk/conversation/plain_widget.py - gajim/gui_menu_builder.py
Changes:
===================================== gajim/gtk/conversation/plain_widget.py ===================================== @@ -13,7 +13,6 @@ # along with Gajim. If not, see http://www.gnu.org/licenses/.
import logging -from urllib.parse import quote
from gi.repository import Gtk from gi.repository import Pango @@ -21,14 +20,13 @@ from gi.repository import GLib
from gajim.common import app -from gajim.common import i18n from gajim.common.const import StyleAttr from gajim.common.helpers import open_uri from gajim.common.helpers import reduce_chars_newlines from gajim.common.helpers import parse_uri -from gajim.common.i18n import _
-from gajim.gui_menu_builder import get_conv_context_menu +from gajim.gui_menu_builder import get_conv_action_context_menu +from gajim.gui_menu_builder import get_conv_uri_context_menu
from ..util import get_cursor from ..util import make_pango_attribute @@ -72,6 +70,20 @@ def __init__(self, account, selectable):
self.get_style_context().add_class('gajim-conversation-text')
+ self.connect('populate-popup', self._on_populate_popup) + + def _on_populate_popup(self, label, menu): + selected, start, end = label.get_selection_bounds() + if not selected: + menu.show_all() + return + + selected_text = label.get_text()[start:end] + action_menu_item = get_conv_action_context_menu( + self._account, selected_text) + menu.prepend(action_menu_item) + menu.show_all() + def print_text_with_styling(self, block): text = GLib.markup_escape_text(block.text.strip()) for uri in block.uris: @@ -261,69 +273,9 @@ def _on_populate_popup(self, _textview, menu): menu.show_all() return
- selected_text_short = reduce_chars_newlines( - self._selected_text, 25, 2) - item = Gtk.MenuItem.new_with_mnemonic( - _('_Actions for "%s"') % selected_text_short) - menu.prepend(item) - submenu = Gtk.Menu() - item.set_submenu(submenu) - uri_text = quote(self._selected_text.encode('utf-8')) - - if app.settings.get('always_english_wikipedia'): - uri = (f'https://en.wikipedia.org/wiki/' - f'Special:Search?search={uri_text}') - else: - uri = (f'https://%7Bi18n.get_short_lang_code()%7D.wikipedia.org/' - f'wiki/Special:Search?search={uri_text}') - item = Gtk.MenuItem.new_with_mnemonic(_('Read _Wikipedia Article')) - id_ = item.connect('activate', self._visit_uri, uri) - self._handlers[id_] = item - submenu.append(item) - - item = Gtk.MenuItem.new_with_mnemonic( - _('Look it up in _Dictionary')) - dict_link = app.settings.get('dictionary_url') - if dict_link == 'WIKTIONARY': - # Default is wikitionary.org - if app.settings.get('always_english_wiktionary'): - uri = (f'https://en.wiktionary.org/wiki/' - f'Special:Search?search={uri_text}') - else: - uri = (f'https://%7Bi18n.get_short_lang_code()%7D.wiktionary.org/' - f'wiki/Special:Search?search={uri_text}') - id_ = item.connect('activate', self._visit_uri, uri) - self._handlers[id_] = item - else: - if dict_link.find('%s') == -1: - # There has to be a '%s' in the url if it’s not WIKTIONARY - item = Gtk.MenuItem.new_with_label( - _('Dictionary URL is missing a "%s"')) - item.set_sensitive(False) - else: - uri = dict_link % uri_text - id_ = item.connect('activate', self._visit_uri, uri) - self._handlers[id_] = item - submenu.append(item) - - search_link = app.settings.get('search_engine') - if search_link.find('%s') == -1: - # There has to be a '%s' in the url - item = Gtk.MenuItem.new_with_label( - _('Web Search URL is missing a "%s"')) - item.set_sensitive(False) - else: - item = Gtk.MenuItem.new_with_mnemonic(_('Web _Search for it')) - uri = search_link % uri_text - id_ = item.connect('activate', self._visit_uri, uri) - self._handlers[id_] = item - submenu.append(item) - - item = Gtk.MenuItem.new_with_mnemonic(_('Open as _Link')) - id_ = item.connect('activate', self._visit_uri, uri) - self._handlers[id_] = item - submenu.append(item) - + action_menu_item = get_conv_action_context_menu( + self._account, self._selected_text) + menu.prepend(action_menu_item) menu.show_all()
def _on_uri_clicked(self, texttag, _widget, event, iter_, _kind): @@ -346,7 +298,7 @@ def _on_uri_clicked(self, texttag, _widget, event, iter_, _kind):
uri = parse_uri(word) if event.button.button == 3: # right click - self._show_context_menu(uri) + self._show_uri_context_menu(uri) return Gdk.EVENT_STOP
# TODO: @@ -359,8 +311,8 @@ def _on_uri_clicked(self, texttag, _widget, event, iter_, _kind): open_uri(uri, account=self._account) return Gdk.EVENT_STOP
- def _show_context_menu(self, uri): - menu = get_conv_context_menu(self._account, uri) + def _show_uri_context_menu(self, uri): + menu = get_conv_uri_context_menu(self._account, uri) if menu is None: log.warning('No handler for URI type: %s', uri) return @@ -373,7 +325,3 @@ def destroy(menu, _pspec): menu.attach_to_widget(self, None) menu.connect('notify::visible', destroy) menu.popup_at_pointer() - - @staticmethod - def _visit_uri(_widget, uri): - open_uri(uri)
===================================== gajim/gui_menu_builder.py ===================================== @@ -14,6 +14,9 @@ # You should have received a copy of the GNU General Public License # along with Gajim. If not, see http://www.gnu.org/licenses/.
+from typing import Optional +from urllib.parse import quote + from gi.repository import Gtk from gi.repository import Gio from gi.repository import GLib @@ -22,9 +25,14 @@ from gajim.common.helpers import is_affiliation_change_allowed from gajim.common.helpers import is_role_change_allowed from gajim.common.helpers import jid_is_blocked +from gajim.common.helpers import reduce_chars_newlines from gajim.common.i18n import _ +from gajim.common.i18n import get_short_lang_code from gajim.common.const import URIType from gajim.common.const import URIAction +from gajim.common.structs import URI + +from gajim.gui.const import ControlType
def get_singlechat_menu(control_id, account, jid, type_): @@ -65,7 +73,7 @@ def build_chatstate_menu():
for entry in entries: label, setting = entry - action = 'win.send-chatstate-%s::%s' % (control_id, setting) + action = f'win.send-chatstate-{control_id}::{setting}' menu.append(label, action) return menu
@@ -162,7 +170,7 @@ def build_menu(preset): return build_menu(groupchat_menu)
-def get_account_menu(account): +def get_account_menu(account: str) -> Gio.Menu: ''' [(action, label/sub_menu)] action: string @@ -196,7 +204,7 @@ def build_menu(preset): for item in preset: if isinstance(item[1], str): action, label = item - action = 'app.{}{}'.format(account, action) + action = f'app.{account}{action}' menuitem = Gio.MenuItem.new(label, action) variant = GLib.Variant('s', account) menuitem.set_action_and_target_value(action, variant) @@ -250,11 +258,11 @@ def build_accounts_menu(): menubar.insert_submenu(menu_position, _('Accounts'), acc_menu)
-def get_encryption_menu(control_id, control_type, zeroconf=False): +def get_encryption_menu(control_id: str, control_type: ControlType, + zeroconf: Optional[bool] = False) -> Gio.Menu: menu = Gio.Menu() menu.append( - _('Disabled'), - 'win.set-encryption-{}::{}'.format(control_id, 'disabled')) + _('Disabled'), f'win.set-encryption-{control_id}::disabled') for name, plugin in app.plugin_manager.encryption_plugins.items(): if control_type.is_groupchat: if not hasattr(plugin, 'allow_groupchat'): @@ -265,15 +273,82 @@ def get_encryption_menu(control_id, control_type, zeroconf=False): if zeroconf: if not hasattr(plugin, 'allow_zeroconf'): continue - menu_action = 'win.set-encryption-{}::{}'.format( - control_id, name) + menu_action = f'win.set-encryption-{control_id}::{name}' menu.append(name, menu_action) if menu.get_n_items() == 1: return None return menu
-def get_conv_context_menu(account, uri): +def get_conv_action_context_menu(account: str, selected_text: str) -> Gtk.Menu: + selected_text_short = reduce_chars_newlines(selected_text, 10, 1) + + action_menu_item = Gtk.MenuItem.new_with_mnemonic( + _('_Actions for "%s"') % selected_text_short) + submenu = Gtk.Menu() + action_menu_item.set_submenu(submenu) + + uri_text = quote(selected_text.encode('utf-8')) + + if app.settings.get('always_english_wikipedia'): + uri = (f'https://en.wikipedia.org/wiki/' + f'Special:Search?search={uri_text}') + else: + uri = (f'https://%7Bget_short_lang_code()%7D.wikipedia.org/' + f'wiki/Special:Search?search={uri_text}') + item = Gtk.MenuItem.new_with_mnemonic(_('Read _Wikipedia Article')) + value = GLib.Variant.new_strv([account, uri]) + item.set_action_target_value(value) + submenu.append(item) + + item = Gtk.MenuItem.new_with_mnemonic( + _('Look it up in _Dictionary')) + dict_link = app.settings.get('dictionary_url') + if dict_link == 'WIKTIONARY': + # Default is wikitionary.org + if app.settings.get('always_english_wiktionary'): + uri = (f'https://en.wiktionary.org/wiki/' + f'Special:Search?search={uri_text}') + else: + uri = (f'https://%7Bget_short_lang_code()%7D.wiktionary.org/' + f'wiki/Special:Search?search={uri_text}') + else: + if dict_link.find('%s') == -1: + # There has to be a '%s' in the url if it’s not WIKTIONARY + item = Gtk.MenuItem.new_with_label( + _('Dictionary URL is missing a "%s"')) + item.set_sensitive(False) + else: + uri = dict_link % uri_text + value = GLib.Variant.new_strv([account, uri]) + item.set_action_target_value(value) + submenu.append(item) + + search_link = app.settings.get('search_engine') + if search_link.find('%s') == -1: + # There has to be a '%s' in the url + item = Gtk.MenuItem.new_with_label( + _('Web Search URL is missing a "%s"')) + item.set_sensitive(False) + else: + item = Gtk.MenuItem.new_with_mnemonic(_('Web _Search for it')) + uri = search_link % uri_text + value = GLib.Variant.new_strv([account, uri]) + item.set_action_target_value(value) + submenu.append(item) + + item = Gtk.MenuItem.new_with_mnemonic(_('Open as _Link')) + value = GLib.Variant.new_strv([account, uri_text]) + item.set_action_target_value(value) + submenu.append(item) + + for item in submenu: + item.set_action_name('app.open-link') + + return action_menu_item + + +def get_conv_uri_context_menu(account: str, uri: URI) -> Gtk.Menu: if uri.type == URIType.XMPP: if uri.action == URIAction.JOIN: context_menu = [ @@ -323,9 +398,9 @@ def get_conv_context_menu(account, uri): menuitem.set_label(label)
if action.startswith('-'): - action = 'app.%s%s' % (account, action) + action = f'app.{account}{action}' else: - action = 'app.%s' % action + action = f'app.{action}' menuitem.set_action_name(action)
data = uri.data @@ -456,15 +531,15 @@ def build_workspaces_submenu(current_workspace_id, account, jid):
def get_groupchat_roster_menu(account, control_id, self_contact, contact): menu = Gtk.Menu() + real_jid = contact.real_jid or ''
item = Gtk.MenuItem(label=_('Information')) - action = 'win.contact-information-%s::%s' % (control_id, contact.name) + action = f'win.contact-information-{control_id}::{contact.name}' item.set_detailed_action_name(action) menu.append(item)
item = Gtk.MenuItem(label=_('Add to Contact List…')) - action = 'app.{account}-add-contact(["{account}", "{jid}"])'.format( - account=account, jid=contact.real_jid or '') + action = f'app.{account}-add-contact(["{account}", "{real_jid}"])' if contact.real_jid is None: item.set_sensitive(False) else: @@ -472,14 +547,14 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact): menu.append(item)
item = Gtk.MenuItem(label=_('Execute Command…')) - action = 'win.execute-command-%s::%s' % (control_id, contact.name) + action = f'win.execute-command-{control_id}::{contact.name}' item.set_detailed_action_name(action) menu.append(item)
menu.append(Gtk.SeparatorMenuItem())
item = Gtk.MenuItem(label=_('Kick')) - action = 'win.kick-%s::%s' % (control_id, contact.name) + action = f'win.kick-{control_id}::{contact.name}' if is_role_change_allowed(self_contact, contact): item.set_detailed_action_name(action) else: @@ -487,7 +562,7 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact): menu.append(item)
item = Gtk.MenuItem(label=_('Ban')) - action = 'win.ban-%s::%s' % (control_id, contact.real_jid or '') + action = f'win.ban-{control_id}::{real_jid}' if is_affiliation_change_allowed(self_contact, contact, 'outcast'): item.set_detailed_action_name(action) else: @@ -497,8 +572,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact): menu.append(Gtk.SeparatorMenuItem())
item = Gtk.MenuItem(label=_('Make Owner')) - action = 'win.change-affiliation-%s(["%s", "owner"])' % (control_id, - contact.real_jid) + action = (f'win.change-affiliation-{control_id}' + f'(["{contact.real_jid}", "owner"])') if is_affiliation_change_allowed(self_contact, contact, 'owner'): item.set_detailed_action_name(action) else: @@ -506,8 +581,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact): menu.append(item)
item = Gtk.MenuItem(label=_('Make Admin')) - action = 'win.change-affiliation-%s(["%s", "admin"])' % (control_id, - contact.real_jid) + action = (f'win.change-affiliation-{control_id}' + f'(["{contact.real_jid}", "admin"])') if is_affiliation_change_allowed(self_contact, contact, 'admin'): item.set_detailed_action_name(action) else: @@ -515,8 +590,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact): menu.append(item)
item = Gtk.MenuItem(label=_('Make Member')) - action = 'win.change-affiliation-%s(["%s", "member"])' % (control_id, - contact.real_jid) + action = (f'win.change-affiliation-{control_id}' + f'(["{contact.real_jid}", "member"])') if is_affiliation_change_allowed(self_contact, contact, 'member'): item.set_detailed_action_name(action) else: @@ -524,8 +599,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact): menu.append(item)
item = Gtk.MenuItem(label=_('Revoke Member')) - action = 'win.change-affiliation-%s(["%s", "none"])' % (control_id, - contact.real_jid) + action = (f'win.change-affiliation-{control_id}' + f'(["{contact.real_jid}", "none"])') if is_affiliation_change_allowed(self_contact, contact, 'none'): item.set_detailed_action_name(action) else: @@ -540,9 +615,8 @@ def get_groupchat_roster_menu(account, control_id, self_contact, contact): role = 'visitor'
item = Gtk.MenuItem(label=label) - action = 'win.change-role-%s(["%s", "%s"])' % (control_id, - contact.name, - role) + action = (f'win.change-role-{control_id}' + f'(["{contact.name}", "{role}"])') if is_role_change_allowed(self_contact, contact): item.set_detailed_action_name(action) else:
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/c21880ddfd988751f47bead35e739917...