Philipp Hörist pushed to branch master at gajim / gajim
Commits:
-
48dda6b6
by lovetox at 2022-01-12T17:02:47+01:00
-
863cd8f2
by lovetox at 2022-01-12T17:02:47+01:00
-
ffa79c74
by lovetox at 2022-01-12T17:02:47+01:00
4 changed files:
Changes:
| ... | ... | @@ -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
|
| ... | ... | @@ -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,
|
| ... | ... | @@ -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 |
|
| ... | ... | @@ -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)
|