diff --git a/services/presence/Buddy.py b/services/presence/Buddy.py index bf369f7e..618902e9 100644 --- a/services/presence/Buddy.py +++ b/services/presence/Buddy.py @@ -33,6 +33,11 @@ class BuddyDBusHelper(dbus.service.Object): def ServiceDisappeared(self, object_path): pass + @dbus.service.signal(BUDDY_DBUS_INTERFACE, + signature="") + def Disappeared(self): + pass + @dbus.service.signal(BUDDY_DBUS_INTERFACE, signature="") def IconChanged(self): @@ -268,6 +273,7 @@ class Buddy(object): if service.get_type() == PRESENCE_SERVICE_TYPE: self._valid = False + self._dbus_helper.Disappeared() def remove_activity(self, activity): actid = activity.get_id() diff --git a/shell/model/BuddyModel.py b/shell/model/BuddyModel.py index 9e6c6bd3..0606ca4f 100644 --- a/shell/model/BuddyModel.py +++ b/shell/model/BuddyModel.py @@ -1,26 +1,53 @@ from sugar.presence import PresenceService from sugar.canvas.IconColor import IconColor +import gobject + +_NOT_PRESENT_COLOR = "#888888,#BBBBBB" + +class BuddyModel(gobject.GObject): + __gsignals__ = { + 'appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'color-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])), + 'current-activity-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([gobject.TYPE_PYOBJECT])) + } + + def __init__(self, name=None, buddy=None): + if name and buddy: + raise RuntimeError("Must specify only _one_ of name or buddy.") + + gobject.GObject.__init__(self) + + self._ba_handler = None + self._pc_handler = None + self._dis_handler = None -class BuddyModel: - def __init__(self, buddy=None): self._cur_activity = None self._pservice = PresenceService.get_instance() - self._buddy = buddy - if self._buddy: - self.set_name(self._buddy.get_name()) - self.set_color(self._buddy.get_color()) - self._buddy.connect('property-changed', - self.__buddy_property_changed_cb) - else: - # if we don't have a buddy yet, connect to the PS - # and wait until the buddy pops up on the network - self._pservice.connect('buddy-appeared', self.__buddy_appeared_cb) + self._buddy = None - def set_name(self, name): - self._name = name + # If given just a name, try to get the buddy from the PS first + if not buddy: + self._name = name + # FIXME: use public key, not name + buddy = self._pservice.get_buddy_by_name(self._name) + + # If successful, copy properties from the PS buddy object + if buddy: + self.__update_buddy(buddy) + else: + # Otherwise, connect to the PS's buddy-appeared signal and + # wait for the buddy to appear + self._ba_handler = self._pservice.connect('buddy-appeared', + self.__buddy_appeared_cb) + self._name = name + # Set color to 'inactive'/'disconnected' + self.__set_color_from_string(_NOT_PRESENT_COLOR) - def set_color(self, color_string): + def __set_color_from_string(self, color_string): self._color = IconColor(color_string) def get_name(self): @@ -30,24 +57,53 @@ class BuddyModel: return self._color def get_buddy(self): - # If we have a buddy already, just return - if self._buddy: - return self._buddy - - # Otherwise try to get the buddy from the PS - self._buddy = self._pservice.get_buddy_by_name(self._name) - if self._buddy: - self._buddy.connect('property-changed', - self.__buddy_property_changed_cb) return self._buddy + def __update_buddy(self, buddy): + if not buddy: + raise ValueError("Buddy cannot be None.") + + self._buddy = buddy + self._name = self._buddy.get_name() + self.__set_color_from_string(self._buddy.get_color()) + + self._pc_handler = self._buddy.connect('property-changed', self.__buddy_property_changed_cb) + self._dis_handler = self._buddy.connect('disappeared', self.__buddy_disappeared_cb) + def __buddy_appeared_cb(self, pservice, buddy): # FIXME: use public key rather than buddy name - if not self._buddy and buddy.get_name() == self._name: - self.get_buddy() + if self._buddy or buddy.get_name() != self._name: + return + + if self._ba_handler: + # Once we have the buddy, we no longer need to + # monitor buddy-appeared events + self._pservice.disconnect(self._ba_handler) + self._ba_handler = None + + self.__update_buddy(buddy) + self.emit('appeared') def __buddy_property_changed_cb(self, buddy, keys): + if not self._buddy: + return + # all we care about right now is current activity - curact = self._buddy.get_current_activity() - self._cur_activity = self._pservice.get_activity(curact) + if 'curact' in keys: + curact = self._buddy.get_current_activity() + self._cur_activity = self._pservice.get_activity(curact) + self.emit('current-activity-changed', self._cur_activity) + if 'color' in keys: + self.__set_color_from_string(self._buddy.get_color()) + self.emit('color-changed', self.get_color()) + def __buddy_disappeared_cb(self, buddy): + if buddy != self._buddy: + return + self._buddy.disconnect(self._pc_handler) + self._buddy.disconnect(self._dis_handler) + self.__set_color_from_string(_NOT_PRESENT_COLOR) + self._cur_activity = None + self.emit('current-activity-changed', self._cur_activity) + self.emit('disappeared') + self._buddy = None diff --git a/shell/model/Friends.py b/shell/model/Friends.py index 4a2298c5..5048688b 100644 --- a/shell/model/Friends.py +++ b/shell/model/Friends.py @@ -50,9 +50,7 @@ class Friends(gobject.GObject): success = cp.read([self._path]) if success: for name in cp.sections(): - buddy = BuddyModel() - buddy.set_name(name) - buddy.set_color(cp.get(name, 'color')) + buddy = BuddyModel(name) self.add_friend(buddy) except Exception, exc: logging.error("Error parsing friends file: %s" % exc) diff --git a/shell/view/BuddyIcon.py b/shell/view/BuddyIcon.py index 9ae709a5..f6386e0d 100644 --- a/shell/view/BuddyIcon.py +++ b/shell/view/BuddyIcon.py @@ -2,18 +2,25 @@ from sugar.canvas.MenuIcon import MenuIcon from view.BuddyMenu import BuddyMenu class BuddyIcon(MenuIcon): - def __init__(self, shell, menu_shell, friend): + def __init__(self, shell, menu_shell, buddy): MenuIcon.__init__(self, menu_shell, icon_name='stock-buddy', - color=friend.get_color(), size=112) + color=buddy.get_color(), size=112) self._shell = shell - self._friend = friend + self._buddy = buddy + self._buddy.connect('appeared', self.__buddy_presence_change_cb) + self._buddy.connect('disappeared', self.__buddy_presence_change_cb) + self._buddy.connect('color-changed', self.__buddy_presence_change_cb) + + def __buddy_presence_change_cb(self, buddy, color=None): + # Update the icon's color when the buddy comes and goes + self.set_property('color', buddy.get_color()) def set_popup_distance(self, distance): self._popup_distance = distance def create_menu(self): - menu = BuddyMenu(self._shell, self._friend) + menu = BuddyMenu(self._shell, self._buddy) menu.connect('action', self._popup_action_cb) return menu @@ -22,14 +29,14 @@ class BuddyIcon(MenuIcon): friends = self._shell.get_model().get_friends() if action == BuddyMenu.ACTION_REMOVE_FRIEND: - friends.remove(self._friend) + friends.remove(self._buddy) - buddy = self._friend.get_buddy() - if buddy == None: + ps_buddy = self._buddy.get_buddy() + if ps_buddy == None: return if action == BuddyMenu.ACTION_INVITE: activity = self._shell.get_current_activity() - activity.invite(buddy) + activity.invite(ps_buddy) elif action == BuddyMenu.ACTION_MAKE_FRIEND: - friends.make_friend(buddy) + friends.make_friend(ps_buddy) diff --git a/sugar/presence/Buddy.py b/sugar/presence/Buddy.py index ee7bfdef..a0d3fd15 100644 --- a/sugar/presence/Buddy.py +++ b/sugar/presence/Buddy.py @@ -7,6 +7,8 @@ class Buddy(gobject.GObject): __gsignals__ = { 'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([])), + 'disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, + ([])), 'service-appeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, ([gobject.TYPE_PYOBJECT])), 'service-disappeared': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE, @@ -33,6 +35,7 @@ class Buddy(gobject.GObject): self._buddy.connect_to_signal('IconChanged', self._icon_changed_cb) self._buddy.connect_to_signal('ServiceAppeared', self._service_appeared_cb) self._buddy.connect_to_signal('ServiceDisappeared', self._service_disappeared_cb) + self._buddy.connect_to_signal('Disappeared', self._disappeared_cb) self._buddy.connect_to_signal('JoinedActivity', self._joined_activity_cb) self._buddy.connect_to_signal('LeftActivity', self._left_activity_cb) self._buddy.connect_to_signal('PropertyChanged', self._property_changed_cb) @@ -54,6 +57,12 @@ class Buddy(gobject.GObject): def _icon_changed_cb(self): gobject.idle_add(self._emit_icon_changed_signal) + def _emit_disappeared_signal(self): + self.emit('disappeared') + + def _disappeared_cb(self): + gobject.idle_add(self._emit_disappeared_signal) + def _emit_service_appeared_signal(self, object_path): self.emit('service-appeared', self._ps_new_object(object_path)) return False