diff options
Diffstat (limited to 'clients_icons/clients_icons.py')
-rw-r--r-- | clients_icons/clients_icons.py | 418 |
1 files changed, 154 insertions, 264 deletions
diff --git a/clients_icons/clients_icons.py b/clients_icons/clients_icons.py index 38dc4b6..80f9124 100644 --- a/clients_icons/clients_icons.py +++ b/clients_icons/clients_icons.py @@ -1,41 +1,36 @@ -import os import logging +from pathlib import Path from gi.repository import Gtk -from gi.repository import GdkPixbuf +from nbxmpp import JID -from clients_icons.clients import CLIENTS, LIBPURPLE_CLIENTS +from clients_icons import clients + +from gajim.roster_window import Column -from gajim.plugins.gui import GajimPluginConfigDialog from gajim.plugins import GajimPlugin -from gajim.plugins.helpers import log_calls +from gajim.plugins.gui import GajimPluginConfigDialog +from gajim.plugins.plugins_i18n import _ + from gajim.common import ged from gajim.common import app from gajim.common import caps_cache -# Since Gajim 1.1.0 _() has to be imported -try: - from gajim.common.i18n import _ -except ImportError: - pass -log = logging.getLogger('gajim.plugin_system.clients_icons') +from gajim.gtk.util import load_icon -class ClientsIconsPlugin(GajimPlugin): +log = logging.getLogger('gajim.plugin_system.client_icons') - @log_calls('ClientsIconsPlugin') + +class ClientsIconsPlugin(GajimPlugin): def init(self): self.description = _('Shows client icons in roster' ' and in groupchats.') self.pos_list = [_('after statusicon'), _('before avatar')] self.events_handlers = { - 'caps-presence-received': (ged.POSTGUI, - self.caps_presence_received), - 'gc-presence-received': (ged.POSTGUI, - self.gc_presence_received), - 'caps-disco-received': (ged.POSTGUI, - self.caps_disco_received), } + 'caps-update': (ged.POSTGUI, self._on_caps_update), + } self.gui_extension_points = { 'groupchat_control': (self.connect_with_groupchat_control, self.disconnect_from_groupchat_control), @@ -44,42 +39,28 @@ class ClientsIconsPlugin(GajimPlugin): 'gc_tooltip_populate': (self.connect_with_gc_tooltip_populate, None), } self.config_default_values = { - 'show_in_roster': (True, ''), - 'show_in_groupchats': (True, ''), - 'show_in_tooltip': (True, ''), - 'show_unknown_icon': (True, ''), - 'pos_in_list': (0, ''), - 'show_facebook': (True, ''),} + 'show_in_roster': (True, ''), + 'show_in_groupchats': (True, ''), + 'show_in_tooltip': (True, ''), + 'show_unknown_icon': (True, ''), + 'pos_in_list': (0, ''), + 'show_facebook': (True, ''), + } + + _icon_theme = Gtk.IconTheme.get_default() + if _icon_theme is not None: + _icon_theme.append_search_path(str(Path(__file__).parent)) self.config_dialog = ClientsIconsPluginConfigDialog(self) - icon_path = os.path.join(self.local_file_path('icons'), 'unknown.png') - self.default_pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size( - icon_path, 16, 16) - self.icon_cache = {} - @log_calls('ClientsIconsPlugin') - def get_client_name(self, contact, client_name): + @staticmethod + def get_client_identity_name(contact): identities = contact.client_caps.get_cache_lookup_strategy()( caps_cache.capscache).identities if identities: - log.debug('get_client_name, identities: %s', str(identities)) for entry in identities: if entry['category'] == 'client': - client_name = entry.get('name', client_name) - break - return client_name - - def get_client_icon_by_name(self, client_name): - client_icon = None - name_splits = client_name.split() - name_splits = reversed([" ".join(name_splits[:(i+1)]) for i in range(len(name_splits))]) - for name in name_splits: - if not client_icon: - log.debug("get_client_icon_by_name, searching for name fragment '%s'..." % name) - client_icon = CLIENTS.get(name, (None,))[0] - if client_icon: - break - return client_icon + return entry.get('name') @staticmethod def is_groupchat(contact): @@ -87,14 +68,10 @@ class ClientsIconsPlugin(GajimPlugin): return contact.is_groupchat() return False - @log_calls('ClientsIconsPlugin') def add_tooltip_row(self, tooltip, contact, tooltip_grid): caps = contact.client_caps._node - log.debug('connect_with_gc_tooltip_populate, caps: %s', caps) - caps_image, client_name = self.get_icon(caps, contact) - client_name = self.get_client_name(contact, client_name) + caps_image, client_name = self.get_icon(caps, contact, tooltip_grid) caps_image.set_halign(Gtk.PositionType.RIGHT) - log.debug('connect_with_gc_tooltip_populate, client_name: %s', client_name) # fill clients table self.table = Gtk.Grid() @@ -122,27 +99,25 @@ class ClientsIconsPlugin(GajimPlugin): tooltip_grid.attach_next_to(self.table, label, Gtk.PositionType.RIGHT, 1, 1) - @log_calls('ClientsIconsPlugin') def connect_with_gc_tooltip_populate(self, tooltip, contact, tooltip_grid): if not self.config['show_in_tooltip']: return # Check if clients info already attached to tooltip - has_attached = False - for child in tooltip_grid.get_children(): - if child.get_name() == 'client_icons_grid': - caps = contact.client_caps._node - caps_image, client_name = self.get_icon(caps, contact) - child.remove(child.get_child_at(1, 1)) - child.attach(caps_image, 1, 1, 1, 1) - child.get_child_at(2, 1).set_markup(client_name) - child.show_all() - if child.get_name() == 'client_icons_label': - child.show() - has_attached = True - if not has_attached: - self.add_tooltip_row(tooltip, contact, tooltip_grid) - - @log_calls('ClientsIconsPlugin') + + node = contact.client_caps._node + image, client_name = self.get_icon(node, contact, tooltip_grid) + label = Gtk.Label(label=client_name) + + box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL) + box.add(image) + box.add(label) + box.show_all() + + tooltip_grid.insert_next_to(tooltip._ui.affiliation, + Gtk.PositionType.BOTTOM) + tooltip_grid.attach_next_to(box, tooltip._ui.affiliation, + Gtk.PositionType.BOTTOM, 1, 1) + def connect_with_roster_tooltip_populate(self, tooltip, contacts, tooltip_grid): if not self.config['show_in_tooltip']: return @@ -155,6 +130,8 @@ class ClientsIconsPlugin(GajimPlugin): num_resources = 0 contacts_dict = {} for contact in contacts: + if contact.show == 'offline': + return if contact.resource: num_resources += 1 if int(contact.priority) in contacts_dict: @@ -165,97 +142,50 @@ class ClientsIconsPlugin(GajimPlugin): contact_keys.reverse() # fill clients table - self.table = Gtk.Grid() - self.table.insert_row(0) - self.table.insert_row(0) - self.table.insert_column(0) - self.table.set_property('column-spacing', 2) + table = Gtk.Grid() + table.insert_row(0) + table.insert_row(0) + table.insert_column(0) + table.set_property('column-spacing', 2) vcard_current_row = 0 for priority in contact_keys: for acontact in contacts_dict[priority]: caps = acontact.client_caps._node - caps_image, client_name = self.get_icon(caps, acontact) - client_name = self.get_client_name(acontact, client_name) - caps_image.set_alignment(0, 0) - self.table.attach(caps_image, 1, vcard_current_row, 1, 1) - label = Gtk.Label() - label.set_alignment(0, 0) - label.set_markup(client_name) - self.table.attach(label, 2, vcard_current_row, 1, 1) + caps_image, client_name = self.get_icon(caps, acontact, tooltip_grid) + caps_image.set_valign(Gtk.Align.START) + table.attach(caps_image, 1, vcard_current_row, 1, 1) + label = Gtk.Label(label=client_name) + label.set_valign(Gtk.Align.START) + label.set_xalign(0) + table.attach(label, 2, vcard_current_row, 1, 1) vcard_current_row += 1 - self.table.show_all() + table.show_all() + table.set_valign(Gtk.Align.START) # set label label = Gtk.Label() - label.set_alignment(0, 0) + label.set_halign(Gtk.Align.END) + label.set_valign(Gtk.Align.START) if num_resources > 1: - label.set_markup(_('Clients:')) + label.set_text(_('Clients:')) else: - if contact.show == 'offline': - return - label.set_markup(_('Client:')) + label.set_text(_('Client:')) label.show() # set clients table to tooltip - tooltip_grid.insert_next_to(tooltip.resource_label, + tooltip_grid.insert_next_to(tooltip._ui.resource_label, Gtk.PositionType.BOTTOM) - tooltip_grid.attach_next_to(label, tooltip.resource_label, + tooltip_grid.attach_next_to(label, tooltip._ui.resource_label, Gtk.PositionType.BOTTOM, 1, 1) - tooltip_grid.attach_next_to(self.table, label, + tooltip_grid.attach_next_to(table, label, Gtk.PositionType.RIGHT, 1, 1) - def get_icon(self, caps, contact): - client_name = _('Unknown') - caps_ = None - if caps: - # libpurple returns pidgin.im/ only, we have to look for resource name - if 'pidgin.im/' in caps: - caps = 'libpurple' - for client in LIBPURPLE_CLIENTS: - if client in contact.resource.lower(): - caps = LIBPURPLE_CLIENTS[client] - - if 'sleekxmpp.com'in caps: - caps = 'httр://sleekxmpp.com/ver/1.1.11' - caps_from_jid = self.check_jid(contact.jid) - if caps_from_jid: - caps = caps_from_jid - caps_ = caps.split('#')[0].split() - - client_name = self.get_client_name(contact, client_name) - client_icon = self.get_client_icon_by_name(client_name) - - if caps_ and not client_icon: - client_icon = CLIENTS.get(caps_[0].split()[0], (None,))[0] - client_name = CLIENTS.get(caps_[0].split()[0], ('', _('Unknown')))[1] - - if not client_icon: - return Gtk.Image.new_from_pixbuf(self.default_pixbuf), _('Unknown') - else: - icon_path = os.path.join(self.local_file_path('icons'), client_icon) - if icon_path in self.icon_cache: - return Gtk.Image.new_from_pixbuf(self.icon_cache[icon_path]), \ - client_name - else: - pb = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, 16, 16) - self.icon_cache[icon_path] = pb - return Gtk.Image.new_from_pixbuf(pb), client_name - - def check_jid(self, jid): - caps = None - if 'facebook.com' in jid and self.config['show_facebook']: - caps = 'facebook.com' - elif '@vk.com' in jid and self.config['show_facebook']: - caps = 'vk.com' - elif jid == 'juick@juick.com': - caps = 'http://juick.com/caps' - elif jid == 'psto@psto.net': - caps = 'psto@psto.net' - elif jid == 'rss@isida-bot.com': - caps = 'rss@isida-bot.com' - return caps - - @log_calls('ClientsIconsPlugin') + def get_icon(self, node, contact, widget): + identity_name = self.get_client_identity_name(contact) + client_name, icon_name = clients.get_data(identity_name, node) + surface = load_icon(icon_name, widget=widget) + return Gtk.Image.new_from_surface(surface), client_name + def connect_with_roster_draw_contact(self, roster, jid, account, contact): if not self.active: return @@ -268,22 +198,19 @@ class ClientsIconsPlugin(GajimPlugin): return for iter_ in child_iters: if roster.model[iter_][self.renderer_num] is None: - caps = contact.client_caps._node - if not caps: - caps = self.check_jid(jid) - self.set_icon(roster.model, iter_, self.renderer_num, caps, contact) + node = contact.client_caps._node + self.set_icon(roster.model, iter_, self.renderer_num, node, contact) - @log_calls('ClientsIconsPlugin') def connect_with_groupchat_control(self, chat_control): chat_control.nb_ext_renderers += 1 - chat_control.columns += [GdkPixbuf.Pixbuf] + chat_control.columns += [str] self.groupchats_tree_is_transformed = True self.chat_control = chat_control col = Gtk.TreeViewColumn() self.muc_renderer_num = 4 + chat_control.nb_ext_renderers client_icon_rend = ( 'client_icon', Gtk.CellRendererPixbuf(), False, - 'pixbuf', self.muc_renderer_num, + 'icon_name', self.muc_renderer_num, self.tree_cell_data_func, chat_control) # remove old column chat_control.list_treeview.remove_column( @@ -310,14 +237,10 @@ class ClientsIconsPlugin(GajimPlugin): chat_control.account, chat_control.room_jid): gc_contact = app.contacts.get_gc_contact( chat_control.account, chat_control.room_jid, nick) - iter_ = chat_control.add_contact_to_roster( - nick, gc_contact.show, gc_contact.role, - gc_contact.affiliation, gc_contact.status, gc_contact.jid) + iter_ = chat_control.add_contact_to_roster(nick) if not self.config['show_in_groupchats']: - log.debug("not showing in groupchats...") continue caps = gc_contact.client_caps._node - log.debug("caps: %s" % str(caps)) self.set_icon( chat_control.model, iter_, self.muc_renderer_num, caps, gc_contact) @@ -325,7 +248,6 @@ class ClientsIconsPlugin(GajimPlugin): # Recalculate column width for ellipsizin chat_control.list_treeview.columns_autosize() - @log_calls('ClientsIconsPlugin') def disconnect_from_groupchat_control(self, gc_control): gc_control.nb_ext_renderers -= 1 col = gc_control.list_treeview.get_column(0) @@ -345,7 +267,6 @@ class ClientsIconsPlugin(GajimPlugin): gc_control.list_treeview.set_model(gc_control.model) gc_control.draw_roster() - @log_calls('ClientsIconsPlugin') def activate(self): self.active = None roster = app.interface.roster @@ -355,8 +276,8 @@ class ClientsIconsPlugin(GajimPlugin): self.renderer = Gtk.CellRendererPixbuf() client_icon_rend = ( 'client_icon', self.renderer, False, - 'pixbuf', self.renderer_num, - roster._fill_pep_pixbuf_renderer, self.renderer_num) + 'icon_name', self.renderer_num, + self._roster_icon_renderer, self.renderer_num) # remove old column roster.tree.remove_column(roster.tree.get_column(0)) # add new renderer in renderers list @@ -371,11 +292,33 @@ class ClientsIconsPlugin(GajimPlugin): roster.fill_column(col) roster.tree.insert_column(col, 0) # redraw roster - roster.columns += [GdkPixbuf.Pixbuf] + roster.columns += [str] self.active = True roster.setup_and_draw_roster() - @log_calls('ClientsIconsPlugin') + def _roster_icon_renderer(self, column, renderer, model, titer, data=None): + try: + type_ = model[titer][Column.TYPE] + except TypeError: + return + + # allocate space for the icon only if needed + if model[titer][data] is None: + renderer.set_property('visible', False) + else: + renderer.set_property('visible', True) + + if type_ == 'account': + app.interface.roster._set_account_row_background_color(renderer) + renderer.set_property('xalign', 1) + elif type_: + if not model[titer][Column.JID] or not model[titer][Column.ACCOUNT]: + # This can append at the moment we add the row + return + jid = model[titer][Column.JID] + account = model[titer][Column.ACCOUNT] + app.interface.roster._set_contact_row_background_color(renderer, jid, account) + def deactivate(self): self.active = None roster = app.interface.roster @@ -393,139 +336,86 @@ class ClientsIconsPlugin(GajimPlugin): self.renderer_num + 1:] roster.setup_and_draw_roster() - def caps_disco_received(self, iq_obj): - log.debug("caps disco received...") - if not self.config['show_in_roster']: - return - roster = app.interface.roster - contact = app.contacts.get_contact_from_full_jid( - iq_obj.conn.name, iq_obj.jid) - if self.is_groupchat(contact): + def _on_caps_update(self, event): + if event.conn.name == 'Local': + # zeroconf return + + contact = self._get_contact_or_gc_contact_for_jid(event.conn.name, event.fjid) if contact is None: - room_jid, nick = app.get_room_and_nick_from_fjid(iq_obj.fjid) - contact = app.contacts.get_gc_contact(iq_obj.conn.name, room_jid, nick) - if contact: - gc_control = app.interface.msg_win_mgr.get_gc_control( - iq_obj.jid, iq_obj.conn.name) - iter_ = gc_control.get_contact_iter(nick) - self.set_icon(gc_control.model, iter_, - self.muc_renderer_num, None, contact) - return - if not contact: - return - child_iters = roster._get_contact_iter( - iq_obj.jid, iq_obj.conn.name, contact, roster.model) - if not child_iters: return - for iter_ in child_iters: - caps = contact.client_caps._node - caps_ = self.check_jid(iq_obj.jid) - if caps_: - caps = caps_ - self.set_icon(roster.model, iter_, self.renderer_num, caps, contact) - def caps_presence_received(self, iq_obj): - log.debug("caps presence received...") + if contact.is_gc_contact: + self._draw_gc_contact(event, contact) + else: + self._draw_roster_contact(event, contact) + + def _draw_roster_contact(self, event, contact): if not self.config['show_in_roster']: return - roster = app.interface.roster - contact = app.contacts.get_contact_with_highest_priority( - iq_obj.conn.name, iq_obj.jid) - if not contact or self.is_groupchat(contact): - return - if iq_obj.resource == 'local': - # zeroconf + if contact.is_groupchat(): return - - iters = roster._get_contact_iter(iq_obj.jid, iq_obj.conn.name, contact, + roster = app.interface.roster + iters = roster._get_contact_iter(event.jid, event.conn.name, contact, roster.model) iter_ = iters[0] - if contact.show == 'error': - self.set_icon(roster.model, iter_, self.renderer_num, None, contact) + # highest contact changed + caps = contact.client_caps._node + if not caps: return - # highest contact changed if roster.model[iter_][self.renderer_num] is not None: - caps = contact.client_caps._node - if caps: - log.debug('caps_presence_received, caps: %s', caps) - self.set_icon(roster.model, iter_, self.renderer_num, caps, contact) - return - - caps = None - tag = iq_obj.stanza.getTags('c') - if tag: - caps = tag[0].getAttr('node') - if caps: - if 'pidgin.im/' in caps: - caps = 'libpurple' - for client in LIBPURPLE_CLIENTS: - if client in contact.resource.lower(): - caps = LIBPURPLE_CLIENTS[client] - if 'sleekxmpp.com'in caps: - caps = 'httр://sleekxmpp.com/ver/1.1.11' - - caps_from_jid = self.check_jid(iq_obj.jid) - if caps_from_jid: - caps = caps_from_jid + self.set_icon(roster.model, iter_, self.renderer_num, caps, contact) + return for iter_ in iters: self.set_icon(roster.model, iter_, self.renderer_num, caps, contact) - def gc_presence_received(self, iq_obj): - log.debug("gc presence received...") + def _draw_gc_contact(self, event, contact): if not self.config['show_in_groupchats']: return - contact = app.contacts.get_gc_contact( - iq_obj.conn.name, iq_obj.presence_obj.jid, iq_obj.nick) - if not contact: + + control = app.interface.msg_win_mgr.get_gc_control(contact.room_jid, + event.conn.name) + if control is None: return - caps = None - tag = iq_obj.stanza.getTags('c') - if tag: - caps = tag[0].getAttr('node') - if caps: - log.debug('gc_presence_received, caps: %s', caps) - if 'pidgin.im/' in caps: - caps = 'libpurple' - if 'sleekxmpp.com' in caps: - caps = 'httр://sleekxmpp.com/ver/1.1.11' - iter_ = iq_obj.gc_control.get_contact_iter(iq_obj.nick) - model = iq_obj.gc_control.model - if model[iter_][self.muc_renderer_num] is not None: + iter_ = control.get_contact_iter(contact.name) + if control.model[iter_][self.muc_renderer_num] is not None: + return + caps = contact.client_caps._node + if not caps: return - self.set_icon(model, iter_, self.muc_renderer_num, caps, contact) + self.set_icon(control.model, iter_, self.muc_renderer_num, caps, contact) - def set_icon(self, model, iter_, pos, caps, contact): - client_icon = self.get_client_icon_by_name( - self.get_client_name(contact, _('Unknown'))) + def _get_contact_or_gc_contact_for_jid(self, account, fjid): + contact = app.contacts.get_contact_from_full_jid(account, fjid) - if caps and not client_icon: - caps_ = caps.split('#')[0].split() - if caps_: - log.debug('set_icon, caps_: %s', caps_) - client_icon = CLIENTS.get(caps_[0].split()[0], (None,))[0] + if contact is None: + fjid = JID(fjid) + room_jid, resource = fjid.getStripped(), fjid.getResource() + contact = app.contacts.get_gc_contact(account, room_jid, resource) + return contact + + def set_icon(self, model, iter_, pos, node, contact): + identity_name = self.get_client_identity_name(contact) + _client_name, icon_name = clients.get_data(identity_name, node) + if 'unknown' in icon_name: + if node is not None: + log.warning('Unknown client: %s %s', identity_name, node) + if not self.config['show_unknown_icon']: + model[iter_][pos] = None + return - if not client_icon: - if self.config['show_unknown_icon']: - model[iter_][pos] = self.default_pixbuf - else: - icon_path = os.path.join(self.local_file_path('icons'), client_icon) - if icon_path in self.icon_cache: - model[iter_][pos] = self.icon_cache[icon_path] - else: - pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(icon_path, 16, 16) - model[iter_][pos] = pixbuf - self.icon_cache[icon_path] = pixbuf + model[iter_][pos] = icon_name def tree_cell_data_func(self, column, renderer, model, iter_, control): if not model.iter_parent(iter_): renderer.set_property('visible', False) return - elif model[iter_][self.muc_renderer_num]: + + if model[iter_][self.muc_renderer_num]: renderer.set_property('visible', True) contact = app.contacts.get_gc_contact( |