Philipp Hörist pushed to branch mainwindow at gajim / gajim
Commits: 809931d9 by lovetox at 2021-03-05T23:10:41+01:00 Work on Roster
- - - - - c35662a7 by lovetox at 2021-03-05T23:10:41+01:00 Roster: Add events
- - - - - bd78ea96 by lovetox at 2021-03-05T23:10:41+01:00 Roster: Refactor
- - - - -
2 changed files:
- gajim/common/modules/roster.py - gajim/gtk/roster.py
Changes:
===================================== gajim/common/modules/roster.py ===================================== @@ -26,7 +26,10 @@ class Roster(BaseModule):
_nbxmpp_extends = 'Roster' - _nbxmpp_methods = [] + _nbxmpp_methods = [ + 'delete_item', + 'set_item', + ]
def __init__(self, con): BaseModule.__init__(self, con) @@ -57,7 +60,7 @@ def load_roster(self): self._roster = roster
def _store_roster(self): - app.storage.cache.store_roster(self._account, self._data) + app.storage.cache.store_roster(self._account, self._roster)
def request_roster(self): version = app.settings.get_account_setting(self._account, @@ -92,6 +95,9 @@ def _on_request_roster(self, task): 'roster_version', roster.version)
+ app.nec.push_incoming_event(NetworkEvent('roster-received', + account=self._account)) + self._con.connect_machine()
def _set_roster_from_data(self, items): @@ -101,41 +107,24 @@ def _set_roster_from_data(self, items): self._con.get_module('Contacts').add_contact(item.jid) self._roster[item.jid] = item
- app.storage.cache.store_roster(self._account, self._roster) + self._store_roster()
- def _process_roster_push(self, _con, stanza, _properties): - return - # TODO + def _process_roster_push(self, _con, _stanza, properties): self._log.info('Push received')
- sender = stanza.getFrom() - if sender is not None: - if not self._con.get_own_jid().bare_match(sender): - self._log.warning('Wrong JID %s', stanza.getFrom()) - return - - push_items, version = self._parse_push(stanza) + item = properties.roster.item + self._roster[item.jid] = item
- self._ack_roster_push(stanza) + self._store_roster()
- for item in push_items: - attrs = item.data - app.nec.push_incoming_event(NetworkEvent( - 'roster-info', - conn=self._con, - jid=item.jid, - nickname=attrs['name'], - sub=attrs['subscription'], - ask=attrs['ask'], - groups=attrs['groups'], - avatar_sha=None)) - - self._store_roster() - - self._log.info('New version: %s', version) + self._log.info('New version: %s', properties.roster.version) app.settings.set_account_setting(self._account, 'roster_version', - version) + properties.roster.version) + + app.nec.push_incoming_event(NetworkEvent('roster-push', + account=self._account, + item=item))
raise nbxmpp.NodeProcessed
===================================== gajim/gtk/roster.py ===================================== @@ -2,6 +2,7 @@ import logging from typing import Optional from enum import IntEnum +from collections import defaultdict
from gi.repository import Gdk from gi.repository import Gio @@ -14,6 +15,7 @@ from gajim.common.const import AvatarSize from gajim.common.const import StyleAttr from gajim.common.helpers import jid_is_blocked +from gajim.common.helpers import event_filter from gajim.common.i18n import _
from gajim.gui_menu_builder import get_roster_menu @@ -29,6 +31,9 @@ ]
+DEFAULT_GROUP = _('Contacts') + + class Column(IntEnum): AVATAR = 0 TEXT = 1 @@ -60,7 +65,7 @@ def __init__(self, account): self.add(self._ui.roster_treeview)
# Holds the Gtk.TreeRowReference for each contact - self._contact_refs = {} + self._contact_refs = defaultdict(list) # Holds the Gtk.TreeRowReference for each group self._group_refs = {}
@@ -78,6 +83,7 @@ def __init__(self, account): ('account-connected', ged.CORE, self._on_account_state), ('account-disconnected', ged.CORE, self._on_account_state), ('theme-update', ged.GUI2, self._on_theme_update), + ('roster-push', ged.GUI2, self._on_roster_push), ])
self._add_actions() @@ -129,6 +135,9 @@ def _remove_actions(self): for action in actions: app.window.remove_action(f'{action}-{self._account}')
+ def _get_contact(self, jid): + return self._client.get_module('Contacts').get_contact(jid) + def _on_account_state(self, _event): self.update_actions()
@@ -202,20 +211,6 @@ def _show_contact_menu(self, jid, treeview, event): popover.set_pointing_to(rectangle) popover.popup()
- def _on_destroy(self, _roster): - self._remove_actions() - self._contact_refs = {} - self._group_refs = {} - self._roster.set_model(None) - self._roster = None - # Store keeps a ref on the object if we don’t unset the sort func - self._store.set_sort_func(Column.TEXT, print) - self._store.clear() - self._store = None - # self._tooltip.destroy() - # self._tooltip = None - app.check_finalize(self) - def set_model(self): self._roster.set_model(self._store)
@@ -224,6 +219,11 @@ def redraw(self): self._roster.set_model(self._store) self._roster.expand_all()
+ def _reset(self): + self._roster.set_model(None) + self.enable_sort(False) + self._initial_draw() + def enable_sort(self, enable): column = Gtk.TREE_SORTABLE_UNSORTED_SORT_COLUMN_ID if enable: @@ -239,114 +239,137 @@ def _initial_draw(self): for contact in self._client.get_module('Roster').iter_contacts(): contact.connect('presence-update', self._on_presence_update) contact.connect('avatar-update', self._on_avatar_update) - self._add_contact(contact) + self._add_or_update_contact(contact)
self.enable_sort(True) self.set_model() self._roster.expand_all()
def _on_presence_update(self, contact, _signal_name): - self.draw_avatar(contact) + self._draw_contact(contact)
def _on_avatar_update(self, contact, _signal_name): - self.draw_avatar(contact) - - def _add_contact(self, contact): - main_group = _('Contacts') - - if contact.groups: - for group_name in contact.groups: - group_iter = self._get_group_iter(group_name) - if not group_iter: - group_iter = self._store.append( - None, (None, group_name, False, group_name)) - group_path = self._store.get_path(group_iter) - group_ref = Gtk.TreeRowReference(self._store, group_path) - self._group_refs[group_name] = group_ref - else: - group_iter = self._get_group_iter(main_group) - if not group_iter: - group_iter = self._store.append( - None, (None, main_group, False, main_group)) - group_path = self._store.get_path(group_iter) - group_ref = Gtk.TreeRowReference(self._store, group_path) - self._group_refs[main_group] = group_ref - - # Avatar - surface = contact.get_avatar(AvatarSize.CHAT, self.get_scale_factor()) - - jid = str(contact.jid) - # TODO: Multiple groups per contact ? + self._draw_contact(contact) + + @event_filter(['account']) + def _on_roster_received(self, event): + self._reset() + + @event_filter(['account']) + def _on_roster_push(self, event): + contact = self._get_contact(event.item.jid) + self._add_or_update_contact(contact) + + def _get_current_groups(self, jid): + groups = set() + for ref in self._contact_refs[jid]: + iter_ = self._get_iter_from_ref(ref) + group_iter = self._store.iter_parent(iter_) + groups.add(self._store[group_iter][Column.JID_OR_GROUP]) + return groups + + def _add_group(self, group_name): + group_iter = self._get_group_iter(group_name) + if group_iter is not None: + return group_iter + + group_iter = self._store.append( + None, (None, group_name, False, group_name)) + group_path = self._store.get_path(group_iter) + group_ref = Gtk.TreeRowReference(self._store, group_path) + self._group_refs[group_name] = group_ref + return group_iter + + def _add_contact_to_group(self, contact, group): + group_iter = self._add_group(group) iter_ = self._store.append( - group_iter, (surface, contact.name, True, jid)) - self._contact_refs[jid] = Gtk.TreeRowReference( - self._store, self._store.get_path(iter_)) + group_iter, + (None, contact.name, True, str(contact.jid)))
- self.draw_groups() - self.draw_contact(jid) + ref = Gtk.TreeRowReference(self._store, self._store.get_path(iter_)) + self._contact_refs[contact.jid].append(ref)
- if (group_path is not None and - self._roster.get_model() is not None): - self._roster.expand_row(group_path, False) - - def remove_contact(self, jid): - # TODO: How to delete a roster item? - #self._client.get_module('Roster').delete_item(jid) + def _add_contact_to_groups(self, contact, groups): + for group in groups: + self._add_contact_to_group(contact, group)
- iter_ = self._get_contact_iter(jid) - if not iter_: + def _remove_contact_from_groups(self, contact, groups): + if not groups: return
- group_iter = self._store.iter_parent(iter_) - if group_iter is None: - raise ValueError('Trying to remove non-chil') - - self._store.remove(iter_) - del self._contact_refs[jid] - if not self._store.iter_has_child(group_iter): + refs = self._contact_refs[contact.jid] + for ref in refs: + iter_ = self._get_iter_from_ref(ref) + group_iter = self._store.iter_parent(iter_) + group = self._store[group_iter][Column.JID_OR_GROUP] + if group in groups: + iter_ = self._store.get_iter(ref.get_path()) + self._store.remove(iter_) + self._contact_refs[contact.jid].remove(ref) + + self._check_for_empty_groups() + + def _remove_contact(self, contact): + refs = self._contact_refs.pop(contact.jid) + for ref in refs: + iter_ = self._get_iter_from_ref(ref) + self._store.remove(iter_) + + self._check_for_empty_groups() + + def _check_for_empty_groups(self): + for ref in list(self._group_refs.values()): + group_iter = self._get_iter_from_ref(ref) + if self._store.iter_has_child(group_iter): + continue group = self._store[group_iter][Column.JID_OR_GROUP] - del self._group_refs[group] self._store.remove(group_iter) + del self._group_refs[group]
- self.redraw() + def _add_or_update_contact(self, contact): + new_groups = set(contact.groups or [DEFAULT_GROUP]) + groups = self._get_current_groups(contact.jid) + + add_to_groups = new_groups - groups + remove_from_groups = groups - new_groups
- def draw_groups(self): + self._add_contact_to_groups(contact, add_to_groups) + self._remove_contact_from_groups(contact, remove_from_groups) + + self._draw_groups() + self._draw_contact(contact) + + def _draw_groups(self): for group in self._group_refs: - self.draw_group(group) + self._draw_group(group)
- def draw_group(self, group_name): + def _draw_group(self, group_name): group_iter = self._get_group_iter(group_name) if not group_iter: return
+ if self._roster.get_model() is not None: + group_path = self._store.get_path(group_iter) + self._roster.expand_row(group_path, False) + total_users = self._get_total_user_count() group_users = self._store.iter_n_children(group_iter) group_name += f' ({group_users}/{total_users})'
self._store[group_iter][Column.TEXT] = group_name
- def draw_contacts(self): - for jid in self._contact_refs: - self.draw_contact(jid) + def _draw_contact(self, contact): + for ref in self._contact_refs[contact.jid]: + self._draw_contact_row(ref, contact)
- def draw_contact(self, jid): - iter_ = self._get_contact_iter(jid) - if not iter_: - return + def _draw_contact_row(self, ref, contact): + iter_ = self._get_iter_from_ref(ref)
- contact = self._client.get_module('Contacts').get_contact(jid) name = GLib.markup_escape_text(contact.name) - if jid_is_blocked(self._account, jid): + if jid_is_blocked(self._account, contact.jid): name = f'<span strikethrough="true">{name}</span>' - - self.draw_avatar(contact) self._store[iter_][Column.TEXT] = name
- def draw_avatar(self, contact): - iter_ = self._get_contact_iter(str(contact.jid)) - if iter_ is None: - return - surface = contact.get_avatar( AvatarSize.ROSTER, self.get_scale_factor()) self._store[iter_][Column.AVATAR] = surface @@ -357,6 +380,9 @@ def _get_total_user_count(self): count += self._store.iter_n_children(group_row.iter) return count
+ def _get_iter_from_ref(self, ref): + return self._store.get_iter(ref.get_path()) + def _get_group_iter(self, group_name: str) -> Optional[Gtk.TreeIter]: try: ref = self._group_refs[group_name] @@ -368,18 +394,6 @@ def _get_group_iter(self, group_name: str) -> Optional[Gtk.TreeIter]: return None return self._store.get_iter(path)
- def _get_contact_iter(self, jid: str) -> Optional[Gtk.TreeIter]: - try: - ref = self._contact_refs[jid] - except KeyError: - return None - - path = ref.get_path() - if path is None: - return None - - return self._store.get_iter(path) - @staticmethod def _text_cell_data_func(_column, renderer, model, iter_, _user_data): has_parent = bool(model.iter_parent(iter_)) @@ -413,10 +427,8 @@ def _tree_compare_iters(self, model, iter1, iter2, _user_data): if not app.settings.get('sort_by_show_in_roster'): return locale.strcoll(name1.lower(), name2.lower())
- contact1 = self._client.get_module('Contacts').get_contact( - model[iter1][Column.JID_OR_GROUP]) - contact2 = self._client.get_module('Contacts').get_contact( - model[iter2][Column.JID_OR_GROUP]) + contact1 = self._get_contact(model[iter1][Column.JID_OR_GROUP]) + contact2 = self._get_contact(model[iter2][Column.JID_OR_GROUP])
if contact1.show != contact2.show: return -1 if contact1.show > contact2.show else 1 @@ -444,3 +456,16 @@ def process_event(self, event): self._on_presence_received(event) else: log.warning('Unhandled Event: %s', event.name) + + def _on_destroy(self, _roster): + self._remove_actions() + self._contact_refs = {} + self._group_refs = {} + self._roster.set_model(None) + self._roster = None + self._store.clear() + self._store.reset_default_sort_func() + self._store = None + # self._tooltip.destroy() + # self._tooltip = None + app.check_finalize(self)
View it on GitLab: https://dev.gajim.org/gajim/gajim/-/compare/9ed4c82e2701a759ca3224be0d8e5c87...