Philipp Hörist pushed to branch master at gajim / gajim
Commits: 48dda6b6 by lovetox at 2022-01-12T17:02:47+01:00 Util: Add type annotations
- - - - - 863cd8f2 by lovetox at 2022-01-12T17:02:47+01:00 App: Improve check_finalize()
It now checks also if the GObject.Object was finalized. Before it did only for the Python wrapper
- - - - - ffa79c74 by lovetox at 2022-01-12T17:02:47+01:00 Menu: Port groupchat roster menu to Gio.Menu
- - - - -
4 changed files:
- gajim/common/app.py - gajim/gtk/groupchat_roster.py - gajim/gtk/menus.py - gajim/gtk/util.py
Changes:
===================================== gajim/common/app.py ===================================== @@ -44,6 +44,7 @@ from nbxmpp.idlequeue import IdleQueue from gi.repository import Gdk from gi.repository import GLib +from gi.repository import GObject
import gajim from gajim.common import types @@ -669,6 +670,15 @@ def check_finalize(obj: Any) -> None: logger = logging.getLogger('gajim.leak') finalizer = weakref.finalize(obj, logger.info, f'{name} has been finalized')
+ g_objects: list[str] = [] + if isinstance(obj, GObject.Object): + g_objects.append(name) + + def g_object_finalized(): + g_objects.remove(name) + + obj.weak_ref(g_object_finalized) + def is_finalizer_ref(ref): try: return isinstance(ref[2][0], str) @@ -677,6 +687,11 @@ def is_finalizer_ref(ref):
def check_finalized(): gc.collect() + gc.collect() + + if g_objects: + logger.warning('GObject not finalized: %s', name) + tup = finalizer.peek() if tup is None: return
===================================== gajim/gtk/groupchat_roster.py ===================================== @@ -365,27 +365,31 @@ def _on_roster_button_press_event(self, return
if event.button == 3: # right click - self._show_contact_menu(nick) + rec = Gdk.Rectangle() + rec.x, rec.y = int(event.x), int(event.y) + rec.height, rec.width = 1, 1 + self._show_contact_menu(nick, rec)
if event.button == 2: # middle click self.emit('row-activated', nick)
- def _show_contact_menu(self, nick: str) -> None: + def _show_contact_menu(self, nick: str, rect: Gdk.Rectangle) -> None: self_contact = self._group_chat_contact.get_self() + assert self_contact is not None contact = self._group_chat_contact.get_resource(nick) menu = get_groupchat_roster_menu(self._account, self._control_id, self_contact, contact)
- def destroy(menu: Gtk.Menu, _pspec: GObject.ParamSpec) -> None: - visible = menu.get_property('visible') - if not visible: - GLib.idle_add(menu.destroy) + def destroy(popover: Gtk.Popover) -> None: + app.check_finalize(popover) + GLib.idle_add(popover.set_relative_to, None)
- menu.attach_to_widget(self, None) - menu.connect('notify::visible', destroy) - menu.popup_at_pointer() + popover = Gtk.Popover.new_from_model(self, menu) + popover.set_pointing_to(rect) + popover.connect('closed', destroy) + popover.popup()
def _tree_compare_iters(self, model: Gtk.TreeModel,
===================================== gajim/gtk/menus.py ===================================== @@ -525,107 +525,92 @@ def build_workspaces_submenu(current_workspace_id: str, return submenu
-def get_groupchat_roster_menu(account: str, - control_id: str, - self_contact: types.GroupchatParticipant, - contact: types.GroupchatParticipant - ) -> Gtk.Menu: - menu = Gtk.Menu() - real_jid = '' - if contact.real_jid is not None: - real_jid = contact.real_jid.bare - - item = Gtk.MenuItem(label=_('Information')) - 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 = f'app.{account}-add-contact(["{account}", "{real_jid}"])' - if contact.real_jid is None: - item.set_sensitive(False) - else: - item.set_detailed_action_name(action) - menu.append(item) - - item = Gtk.MenuItem(label=_('Execute Command…')) - 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 = f'win.kick-{control_id}::{contact.name}' - if is_role_change_allowed(self_contact, contact): - item.set_detailed_action_name(action) - else: - item.set_sensitive(False) - menu.append(item) +def get_groupchat_admin_menu(control_id: str, + self_contact: types.GroupchatParticipant, + contact: types.GroupchatParticipant) -> Gio.Menu:
- item = Gtk.MenuItem(label=_('Ban')) - action = f'win.ban-{control_id}::{real_jid}' - if is_affiliation_change_allowed(self_contact, contact, 'outcast'): - item.set_detailed_action_name(action) - else: - item.set_sensitive(False) - menu.append(item) + adminitems: MenuItemListT = []
- menu.append(Gtk.SeparatorMenuItem()) + action = f'win.change-affiliation-{control_id}'
- item = Gtk.MenuItem(label=_('Make Owner')) - 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: - item.set_sensitive(False) - menu.append(item) + value = f'["{contact.real_jid}", "owner"]' + adminitems.append((_('Make Owner'), action, value))
- item = Gtk.MenuItem(label=_('Make Admin')) - 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: - item.set_sensitive(False) - menu.append(item) + value = f'["{contact.real_jid}", "admin"]' + adminitems.append((_('Make Admin'), action, value))
- item = Gtk.MenuItem(label=_('Make Member')) - 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: - item.set_sensitive(False) - menu.append(item) + value = f'["{contact.real_jid}", "member"]' + adminitems.append((_('Make Member'), action, value))
- item = Gtk.MenuItem(label=_('Revoke Member')) - 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: - item.set_sensitive(False) - menu.append(item) + value = f'["{contact.real_jid}", "none"]' + adminitems.append((_('Revoke Member'), action, value))
- if contact.role.is_visitor: - label = _('Grant Voice') - role = 'participant' - else: - label = _('Revoke Voice') - role = 'visitor' + if is_affiliation_change_allowed(self_contact, contact, 'outcast'): + value = f'"{contact.real_jid}"' + adminitems.append((_('Ban…'), f'win.ban-{control_id}', value)) + + if not adminitems: + adminitems.append((_('Not Available'), 'dummy', None)) + + return make_menu(adminitems) + + +def get_groupchat_mod_menu(control_id: str, + self_contact: types.GroupchatParticipant, + contact: types.GroupchatParticipant + ) -> Gio.Menu: + + moditems: MenuItemListT = []
- item = Gtk.MenuItem(label=label) - 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: - item.set_sensitive(False) - menu.append(item) + value = f'"{contact.name}"' + moditems.append((_('Kick…'), f'win.kick-{control_id}', value)) + + action = f'win.change-role-{control_id}' + + if is_role_change_allowed(self_contact, contact): + if contact.role.is_visitor: + value = f'["{contact.name}", "participant"]' + moditems.append((_('Grant Voice'), action, value)) + else: + value = f'["{contact.name}", "visitor"]' + moditems.append((_('Revoke Voice'), action, value)) + + if not moditems: + moditems.append((_('Not Available'), 'dummy', None)) + + return make_menu(moditems) + + +def get_groupchat_roster_menu(account: str, + control_id: str, + self_contact: types.GroupchatParticipant, + contact: types.GroupchatParticipant + ) -> Gio.Menu: + + value = f'"{contact.name}"' + + general_items: MenuItemListT = [ + (_('Information'), f'win.contact-information-{control_id}', value), + (_('Execute Command…'), f'win.execute-command-{control_id}', value), + ] + + real_contact = contact.get_real_contact() + if real_contact is not None and not real_contact.is_in_roster: + value = f'["{account}", "{real_contact.jid}"]' + action = f'app.{account}-add-contact' + general_items.insert(1, (_('Add to Contact List…'), action, value)) + + mod_menu = get_groupchat_mod_menu(control_id, self_contact, contact) + admin_menu = get_groupchat_admin_menu(control_id, self_contact, contact)
- menu.show_all() + menu = make_menu(general_items) + menu.append_section(_('Moderation'), mod_menu) + menu.append_section(_('Administration'), admin_menu) return menu
===================================== gajim/gtk/util.py ===================================== @@ -67,6 +67,7 @@
MenuValueT = Union[None, str, GLib.Variant, VariantMixin] +MenuItemListT = list[tuple[str, str, MenuValueT]]
class NickCompletionGenerator: @@ -911,7 +912,7 @@ def get_key_theme() -> Optional[str]:
def make_menu_item(label: str, - action: str, + action: Optional[str] = None, value: MenuValueT = None) -> Gio.MenuItem:
if value is None: @@ -927,7 +928,7 @@ def make_menu_item(label: str, return item
-def make_menu(menuitems: list[tuple[str, str, MenuValueT]]) -> Gio.Menu: +def make_menu(menuitems: MenuItemListT) -> Gio.Menu: menu = Gio.Menu() for item in menuitems: menuitem = make_menu_item(*item)
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/e21d2e9aa12e28426d86c86a8ff5ab27...