# Copyright (C) 2007 Collabora Ltd. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA import logging from hashlib import sha1 import dbus from gtk import VBox, Label, TreeView, Expander, ListStore, CellRendererText,\ ScrolledWindow, CellRendererToggle, TextView, VPaned from gobject import timeout_add from gettext import gettext as _ logger = logging.getLogger('ps_watcher') #logging.basicConfig(filename='/tmp/ps_watcher.log') #logging.getLogger().setLevel(1) PS_NAME = 'org.laptop.Sugar.Presence' PS_PATH = '/org/laptop/Sugar/Presence' PS_IFACE = PS_NAME ACTIVITY_IFACE = PS_IFACE + '.Activity' BUDDY_IFACE = PS_IFACE + '.Buddy' # keep these in sync with the calls to ListStore() ACT_COL_PATH = 0 ACT_COL_WEIGHT = 1 ACT_COL_STRIKE = 2 ACT_COL_ID = 3 ACT_COL_COLOR = 4 ACT_COL_TYPE = 5 ACT_COL_NAME = 6 ACT_COL_CONN = 7 ACT_COL_CHANNELS = 8 ACT_COL_BUDDIES = 9 BUDDY_COL_PATH = 0 BUDDY_COL_WEIGHT = 1 BUDDY_COL_STRIKE = 2 BUDDY_COL_NICK = 3 BUDDY_COL_OWNER = 4 BUDDY_COL_COLOR = 5 BUDDY_COL_IP4 = 6 BUDDY_COL_CUR_ACT = 7 BUDDY_COL_KEY_ID = 8 BUDDY_COL_ACTIVITIES = 9 BUDDY_COL_HANDLES = 10 class ActivityWatcher(object): def __init__(self, ps_watcher, object_path): self.ps_watcher = ps_watcher self.bus = ps_watcher.bus self.proxy = self.bus.get_object(self.ps_watcher.unique_name, object_path) self.iface = dbus.Interface(self.proxy, ACTIVITY_IFACE) self.object_path = object_path self.appearing = True self.disappearing = False timeout_add(5000, self._finish_appearing) self.id = '?' self.color = '?' self.type = '?' self.name = '?' self.conn = '?' self.channels = None self.buddies = None self.iter = self.ps_watcher.add_activity(self) self.iface.GetId(reply_handler=self._on_get_id_success, error_handler=self._on_get_id_failure) self.iface.GetColor(reply_handler=self._on_get_color_success, error_handler=self._on_get_color_failure) self.iface.GetType(reply_handler=self._on_get_type_success, error_handler=self._on_get_type_failure) self.iface.GetName(reply_handler=self._on_get_name_success, error_handler=self._on_get_name_failure) self.iface.connect_to_signal('NewChannel', self._on_new_channel) self.iface.GetChannels(reply_handler=self._on_get_channels_success, error_handler=self._on_get_channels_failure) self.iface.connect_to_signal('BuddyJoined', self._on_buddy_joined) self.iface.connect_to_signal('BuddyLeft', self._on_buddy_left) self.iface.GetJoinedBuddies(reply_handler=self._on_get_buddies_success, error_handler=self._on_get_buddies_failure) def _on_buddy_joined(self, buddy): if self.buddies is None: return if buddy.startswith('/org/laptop/Sugar/Presence/Buddies/'): buddy = '.../' + buddy[35:] self.ps_watcher.log(_('INFO') + ': ' + _('Activity %(path)s emitted BuddyJoined("%(buddy)s") or mentioned the buddy in GetJoinedBuddies') % {'path':self.object_path, 'buddy':buddy}) self.buddies.append(buddy) self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_BUDDIES, ' '.join(self.buddies)) def _on_buddy_left(self, buddy): if self.buddies is None: return if buddy.startswith('/org/laptop/Sugar/Presence/Buddies/'): buddy = '.../' + buddy[35:] self.ps_watcher.log(_('INFO') + ': ' + _('Activity %(path)s emitted BuddyLeft("%(buddy)s")') % {'path':self.object_path, 'buddy':buddy}) try: self.buddies.remove(buddy) except ValueError: pass self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_BUDDIES, ' '.join(self.buddies)) def _on_get_buddies_success(self, buddies): self.buddies = [] for buddy in buddies: self._on_buddy_joined(buddy) def _on_get_buddies_failure(self, e): self.log(_('ERROR') + ': ' + _('.GetJoinedBuddies(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_BUDDIES, '!') def _on_new_channel(self, channel): if self.channels is None: return if channel.startswith(self.full_conn): channel = '...' + channel[len(self.full_conn):] self.ps_watcher.log(_('INFO') + ': ' + _('Activity %(path)s emitted NewChannel("%(channel)s") or mentioned the channel in GetChannels()') % {'path':self.object_path, 'channel':channel}) self.channels.append(channel) # FIXME: listen for Telepathy Closed signal! self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CHANNELS, ' '.join(self.channels)) def _on_get_channels_success(self, service, conn, channels): self.full_conn = conn if conn.startswith('/org/freedesktop/Telepathy/Connection/'): self.conn = '.../' + conn[38:] else: self.conn = conn self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CONN, self.conn) self.channels = [] for channel in channels: self._on_new_channel(channel) self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CHANNELS, ' '.join(self.channels)) def _on_get_channels_failure(self, e): self.ps_watcher.log(_('ERROR') + ': ' + _('.GetChannels(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CONN, '!') self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_CHANNELS, '!') def _on_get_id_success(self, ident): self.id = ident self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_ID, ident) def _on_get_id_failure(self, e): self.ps_watcher.log(_('ERROR') + ': ' + _('.GetId(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_ID, '!') def _on_get_color_success(self, color): self.color = color self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_COLOR, color) def _on_get_color_failure(self, e): self.ps_watcher.log(_('ERROR') + ': ' + _('.GetColor(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_COLOR, '!') def _on_get_type_success(self, type_): self.type = type_ self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_TYPE, type_) def _on_get_type_failure(self, e): self.ps_watcher.log(_('ERROR') + ': ' + _('.GetType(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_TYPE, '!') def _on_get_name_success(self, name): self.name = name self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_NAME, name) def _on_get_name_failure(self, e): self.ps_watcher.log(_('ERROR') + ': ' + _('.GetName(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_NAME, '!') def _finish_appearing(self): self.appearing = False self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_WEIGHT, 400) return False def disappear(self): self.disappearing = True self.ps_watcher.activities_list_store.set(self.iter, ACT_COL_STRIKE, True) timeout_add(5000, self._finish_disappearing) def _finish_disappearing(self): self.ps_watcher.remove_activity(self) return False class BuddyWatcher(object): def __init__(self, ps_watcher, object_path): self.ps_watcher = ps_watcher self.bus = ps_watcher.bus self.proxy = self.bus.get_object(self.ps_watcher.unique_name, object_path) self.iface = dbus.Interface(self.proxy, BUDDY_IFACE) self.object_path = object_path self.appearing = True self.disappearing = False timeout_add(5000, self._finish_appearing) self.nick = '?' self.owner = False self.color = '?' self.ipv4 = '?' self.cur_act = '?' self.keyid = '?' self.activities = None self.handles = None self.iter = self.ps_watcher.add_buddy(self) self.iface.connect_to_signal('PropertyChanged', self._on_props_changed, byte_arrays=True) self.ps_watcher.log(_('Calling .GetProperties()'), object_path) self.iface.GetProperties(reply_handler=self._on_get_props_success, error_handler=self._on_get_props_failure, byte_arrays=True) self.iface.connect_to_signal('JoinedActivity', self._on_joined) self.iface.connect_to_signal('LeftActivity', self._on_left) self.ps_watcher.log(_('Calling .GetJoinedActivities()'), object_path) self.iface.GetJoinedActivities(reply_handler=self._on_get_acts_success, error_handler=self._on_get_acts_failure) self.iface.connect_to_signal('TelepathyHandleAdded', self._on_handle_added) self.iface.connect_to_signal('TelepathyHandleRemoved', self._on_handle_removed) self.ps_watcher.log(_('Calling .GetTelepathyHandles()'), object_path) self.iface.GetTelepathyHandles( reply_handler=self._on_get_handles_success, error_handler=self._on_get_handles_failure) def _on_handle_added(self, service, conn, handle): if self.handles is None: return self.ps_watcher.log(_('INFO') + ': ' + _('Buddy %s emitted Telepathy HandleAdded(' + \ '"%s", "%s", %u) or mentioned the handle in ' + \ 'GetTelepathyHandles()'), self.object_path, service, conn, handle) if conn.startswith('/org/freedesktop/Telepathy/Connection/'): conn = '.../' + conn[38:] self.handles.append('%u@%s' % (handle, conn)) self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_HANDLES, ' '.join(self.handles)) def _on_handle_removed(self, service, conn, handle): if self.handles is None: return if conn.startswith('/org/freedesktop/Telepathy/Connection/'): conn = '.../' + conn[38:] self.ps_watcher.log(_('INFO') + ': ' + _('Buddy %(path)s emitted HandleRemoved("%(service)s", "%(conn)s", %(handle)u)') % {'path':self.object_path, 'service':service, 'conn':conn, 'handle':handle}) try: self.handles.remove('%u@%s' % (handle, conn)) except ValueError: pass self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_HANDLES, ' '.join(self.handles)) def _on_get_handles_success(self, handles): self.handles = [] for service, conn, handle in handles: self._on_handle_added(service, conn, handle) def _on_get_handles_failure(self, e): self.ps_watcher.log(_('ERROR') + ': ' +_('.GetTelepathyHandles(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_HANDLES, '!') def _on_joined(self, act): if self.activities is None: return if act.startswith('/org/laptop/Sugar/Presence/Activities/'): act = '.../' + act[38:] self.ps_watcher.log(_('INFO') + ': ' + _('Buddy %(path)s emitted ActivityJoined("%(act)s") or mentioned it in GetJoinedActivities()') % {'path':self.object_path, 'act':act}) self.activities.append(act) self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_ACTIVITIES, ' '.join(self.activities)) def _on_left(self, act): if self.activities is None: return if act.startswith('/org/laptop/Sugar/Presence/Activities/'): act = '.../' + act[38:] self.ps_watcher.log(_('INFO') + ': ' + _('Buddy %(path)s emitted ActivityLeft("%(act)s")') % {'path':self.object_path, 'act':act}) try: self.activities.remove(act) except ValueError: pass self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_ACTIVITIES, ' '.join(self.activities)) def _on_get_acts_success(self, activities): self.activities = [] for act in activities: self._on_joined(act) def _on_get_acts_failure(self, e): self.ps_watcher.log(_('ERROR') + ': ' + _('.GetJoinedActivities(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_ACTIVITIES, '!') def _on_props_changed(self, props): try: logger.debug('PropertyChanged(%s, %s)', self, props) self.ps_watcher.log(_('INFO') + ': ' + _(' emitted PropertyChanged(%(props)r)') % {'path':self.object_path, 'props':props}) self._props_changed(props) except Exception, e: self.ps_watcher.log(_('INTERNAL ERROR: %s'), e) def _on_get_props_success(self, props): try: logger.debug('GetProperties(%s, %s)', self, props) self.ps_watcher.log(_('INFO') + ': ' + _('.GetProperties() -> %(props)r') % {'path':self.object_path, 'props':props}) self._props_changed(props) except Exception, e: self.ps_watcher.log(_('INTERNAL ERROR: %s'), e) def _props_changed(self, props): logger.debug('Begin _props_changed') if 'nick' in props: self.nick = props.get('nick', '?') self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_NICK, self.nick) if 'owner' in props: self.owner = bool(props.get('owner', False)) self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_OWNER, self.owner) if 'color' in props: self.color = props.get('color', '?') self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_COLOR, self.color) if 'ip4-address' in props: self.ipv4 = props.get('ip4-address', '?') self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_IP4, self.ipv4) if 'current-activity' in props: self.cur_act = props.get('current-activity', '?') self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_CUR_ACT, self.cur_act) if 'key' in props: key = props.get('key', None) if key: self.keyid = '%d bytes, sha1 %s' % (len(key), sha1(key).hexdigest()) else: # could be '' (present, empty value) or None (absent). Either way: self.keyid = '?' self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_KEY_ID, self.keyid) logger.debug('End _props_changed') def _on_get_props_failure(self, e): self.ps_watcher.log(_('ERROR') + ': ' + _('.GetProperties(): %(e)s') % {'path':self.object_path, 'e':e}) self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_NICK, '!') self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_OWNER, False) self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_COLOR, '!') self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_IP4, '!') self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_CUR_ACT, '!') self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_KEY_ID, '!') def _finish_appearing(self): self.appearing = False self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_WEIGHT, 400) return False def disappear(self): self.disappearing = True self.ps_watcher.buddies_list_store.set(self.iter, BUDDY_COL_STRIKE, True) timeout_add(5000, self._finish_disappearing) def _finish_disappearing(self): self.ps_watcher.remove_buddy(self) return False class PresenceServiceWatcher(VBox): def __init__(self, bus, unique_name, log): VBox.__init__(self) self.bus = bus self.unique_name = unique_name self.proxy = bus.get_object(unique_name, PS_PATH) self.iface = dbus.Interface(self.proxy, PS_IFACE) self.log = log self.activities = None self.iface.connect_to_signal('ActivityAppeared', self._on_activity_appeared) self.iface.connect_to_signal('ActivityDisappeared', self._on_activity_disappeared) self.iface.GetActivities(reply_handler=self._on_get_activities_success, error_handler=self._on_get_activities_failure) self.buddies = None self.iface.connect_to_signal('BuddyAppeared', self._on_buddy_appeared) self.iface.connect_to_signal('BuddyDisappeared', self._on_buddy_disappeared) self.iface.GetBuddies(reply_handler=self._on_get_buddies_success, error_handler=self._on_get_buddies_failure) # keep this in sync with the ACT_COL_ constants self.activities_list_store = ListStore(str, # object path int, # weight (bold if new) bool, # strikethrough (dead) str, # ID str, # color str, # type str, # name str, # conn str, # channels str, # buddies ) self.pack_start(Label(_('Activities:')), False, False) self.activities_list = TreeView(self.activities_list_store) c = self.activities_list.insert_column_with_attributes(0, _('Object path'), CellRendererText(), text=ACT_COL_PATH, weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(ACT_COL_PATH) c = self.activities_list.insert_column_with_attributes(1, _('ID'), CellRendererText(), text=ACT_COL_ID, weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(ACT_COL_ID) c = self.activities_list.insert_column_with_attributes(2, _('Color'), CellRendererText(), text=ACT_COL_COLOR, weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(ACT_COL_COLOR) c = self.activities_list.insert_column_with_attributes(3, _('Type'), CellRendererText(), text=ACT_COL_TYPE, weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(ACT_COL_TYPE) c = self.activities_list.insert_column_with_attributes(4, _('Name'), CellRendererText(), text=ACT_COL_NAME, weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(ACT_COL_NAME) c = self.activities_list.insert_column_with_attributes(5, _('Connection'), CellRendererText(), text=ACT_COL_CONN, weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(ACT_COL_CONN) c = self.activities_list.insert_column_with_attributes(6, _('Channels'), CellRendererText(), text=ACT_COL_CHANNELS, weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE) c.set_resizable(True) c = self.activities_list.insert_column_with_attributes(7, _('Buddies'), CellRendererText(), text=ACT_COL_BUDDIES, weight=ACT_COL_WEIGHT, strikethrough=ACT_COL_STRIKE) c.set_resizable(True) scroller = ScrolledWindow() scroller.add(self.activities_list) self.pack_start(scroller) # keep this in sync with the BUDDY_COL_ constants self.buddies_list_store = ListStore(str, int, bool, str, bool, str, str, str, str, str, str) self.pack_start(Label(_('Buddies:')), False, False) self.buddies_list = TreeView(self.buddies_list_store) c = self.buddies_list.insert_column_with_attributes(0, _('Object path'), CellRendererText(), text=BUDDY_COL_PATH, weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(BUDDY_COL_PATH) c = self.buddies_list.insert_column_with_attributes(1, _('Pubkey'), CellRendererText(), text=BUDDY_COL_KEY_ID, weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(BUDDY_COL_KEY_ID) c = self.buddies_list.insert_column_with_attributes(2, _('Nick'), CellRendererText(), text=BUDDY_COL_NICK, weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(BUDDY_COL_NICK) c = self.buddies_list.insert_column_with_attributes(3, _('Owner'), CellRendererToggle(), active=BUDDY_COL_OWNER) c = self.buddies_list.insert_column_with_attributes(4, _('Color'), CellRendererText(), text=BUDDY_COL_COLOR, weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(BUDDY_COL_OWNER) c = self.buddies_list.insert_column_with_attributes(5, _('IPv4'), CellRendererText(), text=BUDDY_COL_IP4, weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(BUDDY_COL_IP4) c = self.buddies_list.insert_column_with_attributes(6, _('CurAct'), CellRendererText(), text=BUDDY_COL_CUR_ACT, weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(BUDDY_COL_CUR_ACT) c = self.buddies_list.insert_column_with_attributes(7, _('Activities'), CellRendererText(), text=BUDDY_COL_ACTIVITIES, weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(BUDDY_COL_ACTIVITIES) c = self.buddies_list.insert_column_with_attributes(8, _('Handles'), CellRendererText(), text=BUDDY_COL_HANDLES, weight=BUDDY_COL_WEIGHT, strikethrough=BUDDY_COL_STRIKE) c.set_resizable(True) c.set_sort_column_id(BUDDY_COL_HANDLES) scroller = ScrolledWindow() scroller.add(self.buddies_list) self.pack_start(scroller) self.iface.connect_to_signal('ActivityInvitation', self._on_activity_invitation) self.iface.connect_to_signal('PrivateInvitation', self._on_private_invitation) def _on_get_activities_success(self, paths): self.log(_('INFO') + ': ' + _('PS GetActivities() returned %r'), paths) self.activities = {} for path in paths: self.activities[path] = ActivityWatcher(self, path) def _on_get_activities_failure(self, e): self.log(_('ERROR') + ': ' + _('PS GetActivities() failed with %s'), e) def add_activity(self, act): path = act.object_path if path.startswith('/org/laptop/Sugar/Presence/Activities/'): path = '.../' + path[38:] return self.activities_list_store.append((path, 700, False, act.id, act.color, act.type, act.name, act.conn, '?', '?')) def remove_activity(self, act): self.activities.pop(act.object_path, None) self.activities_list_store.remove(act.iter) def _on_activity_appeared(self, path): if self.activities is None: return self.log(_('INFO') + ': ' + _('PS emitted ActivityAppeared("%s")'), path) self.activities[path] = ActivityWatcher(self, path) def _on_activity_disappeared(self, path): if self.activities is None: return self.log(_('INFO') + ': ' + _('PS emitted ActivityDisappeared("%s")'), path) act = self.activities.get(path) if act is None: self.log(_('WARNING') + ': ' + _('Trying to remove activity "%s" which is already absent'), path) else: # we don't remove the activity straight away, just cross it out act.disappear() def _on_activity_invitation(self, path): self.log(_('INFO') + ': ' + _('PS emitted ActivityInvitation("%s")'), path) def _on_private_invitation(self, bus_name, conn, channel): self.log(_('INFO') + ': ' + _('PS emitted PrivateInvitation("%(bus)s", "%(conn)s", "%(channel)s")') % {'bus':bus_name, 'conn':conn, 'channel':channel}) def _on_get_buddies_success(self, paths): self.log(_('INFO') + ': ' + _('PS GetBuddies() returned %r'), paths) self.buddies = {} for path in paths: self.buddies[path] = BuddyWatcher(self, path) def _on_get_buddies_failure(self, e): self.log(_('ERROR') + ': ' + _('PS GetBuddies() failed with %s'), e) def add_buddy(self, b): path = b.object_path if path.startswith('/org/laptop/Sugar/Presence/Buddies/'): path = '.../' + path[35:] return self.buddies_list_store.append((path, 700, False, b.nick, b.owner, b.color, b.ipv4, b.cur_act, b.keyid, '?', '?')) def remove_buddy(self, b): self.buddies.pop(b.object_path, None) self.buddies_list_store.remove(b.iter) def _on_buddy_appeared(self, path): if self.buddies is None: return self.log(_('INFO') + ': ' + _('PS emitted BuddyAppeared("%s")'), path) self.buddies[path] = BuddyWatcher(self, path) def _on_buddy_disappeared(self, path): if self.buddies is None: return self.log(_('INFO') + ': ' + _('PS emitted BuddyDisappeared("%s")'), path) b = self.buddies.get(path) if b is None: self.log(_('ERROR') + ': ' + _('Trying to remove buddy "%s" which is already absent'), path) else: # we don't remove the activity straight away, just cross it out b.disappear() class PresenceServiceNameWatcher(VBox): def __init__(self, bus): VBox.__init__(self) self.bus = bus logger.debug('Running...') self.label = Label(_('Looking for Presence Service...')) self.errors = ListStore(str) errors_tree = TreeView(model=self.errors) errors_tree.insert_column_with_attributes(0, _('Log'), CellRendererText(), text=0) scroller = ScrolledWindow() scroller.add(errors_tree) self.paned = VPaned() self.paned.pack1(scroller) self.pack_start(self.label, False, False) self.pack_end(self.paned) bus.watch_name_owner(PS_NAME, self.on_name_owner_change) self.ps_watcher = Label('-') self.paned.pack2(self.ps_watcher) self.show_all() def log(self, format, *args): self.errors.append((format % args,)) def on_name_owner_change(self, owner): try: if owner: self.label.set_text(_('Presence Service running: unique name %s') % owner) if self.ps_watcher is not None: self.paned.remove(self.ps_watcher) self.ps_watcher = PresenceServiceWatcher(self.bus, owner, self.log) self.paned.pack2(self.ps_watcher) self.show_all() else: self.label.set_text(_('Presence Service not running')) if self.ps_watcher is not None: self.paned.remove(self.ps_watcher) self.ps_watcher = Label('-') self.paned.pack2(self.ps_watcher) except Exception, e: self.log(_('ERROR') + ': %s', e)