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

Commits:

4 changed files:

Changes:

  • gajim/common/app.py
    ... ... @@ -44,6 +44,7 @@
    44 44
     from nbxmpp.idlequeue import IdleQueue
    
    45 45
     from gi.repository import Gdk
    
    46 46
     from gi.repository import GLib
    
    47
    +from gi.repository import GObject
    
    47 48
     
    
    48 49
     import gajim
    
    49 50
     from gajim.common import types
    
    ... ... @@ -669,6 +670,15 @@ def check_finalize(obj: Any) -> None:
    669 670
         logger = logging.getLogger('gajim.leak')
    
    670 671
         finalizer = weakref.finalize(obj, logger.info, f'{name} has been finalized')
    
    671 672
     
    
    673
    +    g_objects: list[str] = []
    
    674
    +    if isinstance(obj, GObject.Object):
    
    675
    +        g_objects.append(name)
    
    676
    +
    
    677
    +        def g_object_finalized():
    
    678
    +            g_objects.remove(name)
    
    679
    +
    
    680
    +        obj.weak_ref(g_object_finalized)
    
    681
    +
    
    672 682
         def is_finalizer_ref(ref):
    
    673 683
             try:
    
    674 684
                 return isinstance(ref[2][0], str)
    
    ... ... @@ -677,6 +687,11 @@ def is_finalizer_ref(ref):
    677 687
     
    
    678 688
         def check_finalized():
    
    679 689
             gc.collect()
    
    690
    +        gc.collect()
    
    691
    +
    
    692
    +        if g_objects:
    
    693
    +            logger.warning('GObject not finalized: %s', name)
    
    694
    +
    
    680 695
             tup = finalizer.peek()
    
    681 696
             if tup is None:
    
    682 697
                 return
    

  • gajim/gtk/groupchat_roster.py
    ... ... @@ -365,27 +365,31 @@ def _on_roster_button_press_event(self,
    365 365
                 return
    
    366 366
     
    
    367 367
             if event.button == 3:  # right click
    
    368
    -            self._show_contact_menu(nick)
    
    368
    +            rec = Gdk.Rectangle()
    
    369
    +            rec.x, rec.y = int(event.x), int(event.y)
    
    370
    +            rec.height, rec.width = 1, 1
    
    371
    +            self._show_contact_menu(nick, rec)
    
    369 372
     
    
    370 373
             if event.button == 2:  # middle click
    
    371 374
                 self.emit('row-activated', nick)
    
    372 375
     
    
    373
    -    def _show_contact_menu(self, nick: str) -> None:
    
    376
    +    def _show_contact_menu(self, nick: str, rect: Gdk.Rectangle) -> None:
    
    374 377
             self_contact = self._group_chat_contact.get_self()
    
    378
    +        assert self_contact is not None
    
    375 379
             contact = self._group_chat_contact.get_resource(nick)
    
    376 380
             menu = get_groupchat_roster_menu(self._account,
    
    377 381
                                              self._control_id,
    
    378 382
                                              self_contact,
    
    379 383
                                              contact)
    
    380 384
     
    
    381
    -        def destroy(menu: Gtk.Menu, _pspec: GObject.ParamSpec) -> None:
    
    382
    -            visible = menu.get_property('visible')
    
    383
    -            if not visible:
    
    384
    -                GLib.idle_add(menu.destroy)
    
    385
    +        def destroy(popover: Gtk.Popover) -> None:
    
    386
    +            app.check_finalize(popover)
    
    387
    +            GLib.idle_add(popover.set_relative_to, None)
    
    385 388
     
    
    386
    -        menu.attach_to_widget(self, None)
    
    387
    -        menu.connect('notify::visible', destroy)
    
    388
    -        menu.popup_at_pointer()
    
    389
    +        popover = Gtk.Popover.new_from_model(self, menu)
    
    390
    +        popover.set_pointing_to(rect)
    
    391
    +        popover.connect('closed', destroy)
    
    392
    +        popover.popup()
    
    389 393
     
    
    390 394
         def _tree_compare_iters(self,
    
    391 395
                                 model: Gtk.TreeModel,
    

  • gajim/gtk/menus.py
    ... ... @@ -525,107 +525,92 @@ def build_workspaces_submenu(current_workspace_id: str,
    525 525
         return submenu
    
    526 526
     
    
    527 527
     
    
    528
    -def get_groupchat_roster_menu(account: str,
    
    529
    -                              control_id: str,
    
    530
    -                              self_contact: types.GroupchatParticipant,
    
    531
    -                              contact: types.GroupchatParticipant
    
    532
    -                              ) -> Gtk.Menu:
    
    533
    -    menu = Gtk.Menu()
    
    534
    -    real_jid = ''
    
    535
    -    if contact.real_jid is not None:
    
    536
    -        real_jid = contact.real_jid.bare
    
    537
    -
    
    538
    -    item = Gtk.MenuItem(label=_('Information'))
    
    539
    -    action = f'win.contact-information-{control_id}::{contact.name}'
    
    540
    -    item.set_detailed_action_name(action)
    
    541
    -    menu.append(item)
    
    542
    -
    
    543
    -    item = Gtk.MenuItem(label=_('Add to Contact List…'))
    
    544
    -    action = f'app.{account}-add-contact(["{account}", "{real_jid}"])'
    
    545
    -    if contact.real_jid is None:
    
    546
    -        item.set_sensitive(False)
    
    547
    -    else:
    
    548
    -        item.set_detailed_action_name(action)
    
    549
    -    menu.append(item)
    
    550
    -
    
    551
    -    item = Gtk.MenuItem(label=_('Execute Command…'))
    
    552
    -    action = f'win.execute-command-{control_id}::{contact.name}'
    
    553
    -    item.set_detailed_action_name(action)
    
    554
    -    menu.append(item)
    
    555
    -
    
    556
    -    menu.append(Gtk.SeparatorMenuItem())
    
    557
    -
    
    558
    -    item = Gtk.MenuItem(label=_('Kick'))
    
    559
    -    action = f'win.kick-{control_id}::{contact.name}'
    
    560
    -    if is_role_change_allowed(self_contact, contact):
    
    561
    -        item.set_detailed_action_name(action)
    
    562
    -    else:
    
    563
    -        item.set_sensitive(False)
    
    564
    -    menu.append(item)
    
    528
    +def get_groupchat_admin_menu(control_id: str,
    
    529
    +                             self_contact: types.GroupchatParticipant,
    
    530
    +                             contact: types.GroupchatParticipant) -> Gio.Menu:
    
    565 531
     
    
    566
    -    item = Gtk.MenuItem(label=_('Ban'))
    
    567
    -    action = f'win.ban-{control_id}::{real_jid}'
    
    568
    -    if is_affiliation_change_allowed(self_contact, contact, 'outcast'):
    
    569
    -        item.set_detailed_action_name(action)
    
    570
    -    else:
    
    571
    -        item.set_sensitive(False)
    
    572
    -    menu.append(item)
    
    532
    +    adminitems: MenuItemListT = []
    
    573 533
     
    
    574
    -    menu.append(Gtk.SeparatorMenuItem())
    
    534
    +    action = f'win.change-affiliation-{control_id}'
    
    575 535
     
    
    576
    -    item = Gtk.MenuItem(label=_('Make Owner'))
    
    577
    -    action = (f'win.change-affiliation-{control_id}'
    
    578
    -              f'(["{contact.real_jid}", "owner"])')
    
    579 536
         if is_affiliation_change_allowed(self_contact, contact, 'owner'):
    
    580
    -        item.set_detailed_action_name(action)
    
    581
    -    else:
    
    582
    -        item.set_sensitive(False)
    
    583
    -    menu.append(item)
    
    537
    +        value = f'["{contact.real_jid}", "owner"]'
    
    538
    +        adminitems.append((_('Make Owner'), action, value))
    
    584 539
     
    
    585
    -    item = Gtk.MenuItem(label=_('Make Admin'))
    
    586
    -    action = (f'win.change-affiliation-{control_id}'
    
    587
    -              f'(["{contact.real_jid}", "admin"])')
    
    588 540
         if is_affiliation_change_allowed(self_contact, contact, 'admin'):
    
    589
    -        item.set_detailed_action_name(action)
    
    590
    -    else:
    
    591
    -        item.set_sensitive(False)
    
    592
    -    menu.append(item)
    
    541
    +        value = f'["{contact.real_jid}", "admin"]'
    
    542
    +        adminitems.append((_('Make Admin'), action, value))
    
    593 543
     
    
    594
    -    item = Gtk.MenuItem(label=_('Make Member'))
    
    595
    -    action = (f'win.change-affiliation-{control_id}'
    
    596
    -              f'(["{contact.real_jid}", "member"])')
    
    597 544
         if is_affiliation_change_allowed(self_contact, contact, 'member'):
    
    598
    -        item.set_detailed_action_name(action)
    
    599
    -    else:
    
    600
    -        item.set_sensitive(False)
    
    601
    -    menu.append(item)
    
    545
    +        value = f'["{contact.real_jid}", "member"]'
    
    546
    +        adminitems.append((_('Make Member'), action, value))
    
    602 547
     
    
    603
    -    item = Gtk.MenuItem(label=_('Revoke Member'))
    
    604
    -    action = (f'win.change-affiliation-{control_id}'
    
    605
    -              f'(["{contact.real_jid}", "none"])')
    
    606 548
         if is_affiliation_change_allowed(self_contact, contact, 'none'):
    
    607
    -        item.set_detailed_action_name(action)
    
    608
    -    else:
    
    609
    -        item.set_sensitive(False)
    
    610
    -    menu.append(item)
    
    549
    +        value = f'["{contact.real_jid}", "none"]'
    
    550
    +        adminitems.append((_('Revoke Member'), action, value))
    
    611 551
     
    
    612
    -    if contact.role.is_visitor:
    
    613
    -        label = _('Grant Voice')
    
    614
    -        role = 'participant'
    
    615
    -    else:
    
    616
    -        label = _('Revoke Voice')
    
    617
    -        role = 'visitor'
    
    552
    +    if is_affiliation_change_allowed(self_contact, contact, 'outcast'):
    
    553
    +        value = f'"{contact.real_jid}"'
    
    554
    +        adminitems.append((_('Ban…'), f'win.ban-{control_id}', value))
    
    555
    +
    
    556
    +    if not adminitems:
    
    557
    +        adminitems.append((_('Not Available'), 'dummy', None))
    
    558
    +
    
    559
    +    return make_menu(adminitems)
    
    560
    +
    
    561
    +
    
    562
    +def get_groupchat_mod_menu(control_id: str,
    
    563
    +                           self_contact: types.GroupchatParticipant,
    
    564
    +                           contact: types.GroupchatParticipant
    
    565
    +                           ) -> Gio.Menu:
    
    566
    +
    
    567
    +    moditems: MenuItemListT = []
    
    618 568
     
    
    619
    -    item = Gtk.MenuItem(label=label)
    
    620
    -    action = (f'win.change-role-{control_id}'
    
    621
    -              f'(["{contact.name}", "{role}"])')
    
    622 569
         if is_role_change_allowed(self_contact, contact):
    
    623
    -        item.set_detailed_action_name(action)
    
    624
    -    else:
    
    625
    -        item.set_sensitive(False)
    
    626
    -    menu.append(item)
    
    570
    +        value = f'"{contact.name}"'
    
    571
    +        moditems.append((_('Kick…'), f'win.kick-{control_id}', value))
    
    572
    +
    
    573
    +    action = f'win.change-role-{control_id}'
    
    574
    +
    
    575
    +    if is_role_change_allowed(self_contact, contact):
    
    576
    +        if contact.role.is_visitor:
    
    577
    +            value = f'["{contact.name}", "participant"]'
    
    578
    +            moditems.append((_('Grant Voice'), action, value))
    
    579
    +        else:
    
    580
    +            value = f'["{contact.name}", "visitor"]'
    
    581
    +            moditems.append((_('Revoke Voice'), action, value))
    
    582
    +
    
    583
    +    if not moditems:
    
    584
    +        moditems.append((_('Not Available'), 'dummy', None))
    
    585
    +
    
    586
    +    return make_menu(moditems)
    
    587
    +
    
    588
    +
    
    589
    +def get_groupchat_roster_menu(account: str,
    
    590
    +                              control_id: str,
    
    591
    +                              self_contact: types.GroupchatParticipant,
    
    592
    +                              contact: types.GroupchatParticipant
    
    593
    +                              ) -> Gio.Menu:
    
    594
    +
    
    595
    +    value = f'"{contact.name}"'
    
    596
    +
    
    597
    +    general_items: MenuItemListT = [
    
    598
    +        (_('Information'), f'win.contact-information-{control_id}', value),
    
    599
    +        (_('Execute Command…'), f'win.execute-command-{control_id}', value),
    
    600
    +    ]
    
    601
    +
    
    602
    +    real_contact = contact.get_real_contact()
    
    603
    +    if real_contact is not None and not real_contact.is_in_roster:
    
    604
    +        value = f'["{account}", "{real_contact.jid}"]'
    
    605
    +        action = f'app.{account}-add-contact'
    
    606
    +        general_items.insert(1, (_('Add to Contact List…'), action, value))
    
    607
    +
    
    608
    +    mod_menu = get_groupchat_mod_menu(control_id, self_contact, contact)
    
    609
    +    admin_menu = get_groupchat_admin_menu(control_id, self_contact, contact)
    
    627 610
     
    
    628
    -    menu.show_all()
    
    611
    +    menu = make_menu(general_items)
    
    612
    +    menu.append_section(_('Moderation'), mod_menu)
    
    613
    +    menu.append_section(_('Administration'), admin_menu)
    
    629 614
         return menu
    
    630 615
     
    
    631 616
     
    

  • gajim/gtk/util.py
    ... ... @@ -67,6 +67,7 @@
    67 67
     
    
    68 68
     
    
    69 69
     MenuValueT = Union[None, str, GLib.Variant, VariantMixin]
    
    70
    +MenuItemListT = list[tuple[str, str, MenuValueT]]
    
    70 71
     
    
    71 72
     
    
    72 73
     class NickCompletionGenerator:
    
    ... ... @@ -911,7 +912,7 @@ def get_key_theme() -> Optional[str]:
    911 912
     
    
    912 913
     
    
    913 914
     def make_menu_item(label: str,
    
    914
    -                   action: str,
    
    915
    +                   action: Optional[str] = None,
    
    915 916
                        value: MenuValueT = None) -> Gio.MenuItem:
    
    916 917
     
    
    917 918
         if value is None:
    
    ... ... @@ -927,7 +928,7 @@ def make_menu_item(label: str,
    927 928
         return item
    
    928 929
     
    
    929 930
     
    
    930
    -def make_menu(menuitems: list[tuple[str, str, MenuValueT]]) -> Gio.Menu:
    
    931
    +def make_menu(menuitems: MenuItemListT) -> Gio.Menu:
    
    931 932
         menu = Gio.Menu()
    
    932 933
         for item in menuitems:
    
    933 934
             menuitem = make_menu_item(*item)