Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/src/sugar/presence/buddy.py
blob: fab23d2cb92620007f18408d35a97ced054eb257 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
# Copyright (C) 2007, Red Hat, Inc.
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library 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
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.

"""UI interface to a buddy in the presence service

STABLE.
"""

import gobject
import gtk
import dbus

class Buddy(gobject.GObject):
    """UI interface for a Buddy in the presence service
    
    Each buddy interface tracks a set of activities and properties
    that can be queried to provide UI controls for manipulating 
    the presence interface.
    
    Properties Dictionary:
        'key': public key, 
        'nick': nickname , 
        'color': color (XXX what format), 
        'current-activity': (XXX dbus path?), 
        'owner': (XXX dbus path?), 
        'icon': (XXX pixel data for an icon?)
    See __gproperties__
    """
    __gsignals__ = {
        'icon-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                         ([])),
        'joined-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                         ([gobject.TYPE_PYOBJECT])),
        'left-activity': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                         ([gobject.TYPE_PYOBJECT])),
        'property-changed': (gobject.SIGNAL_RUN_FIRST, gobject.TYPE_NONE,
                         ([gobject.TYPE_PYOBJECT])),
    }

    __gproperties__ = {
        'key'              : (str, None, None, None, gobject.PARAM_READABLE),
        'icon'             : (str, None, None, None, gobject.PARAM_READABLE),
        'nick'             : (str, None, None, None, gobject.PARAM_READABLE),
        'color'            : (str, None, None, None, gobject.PARAM_READABLE),
        'current-activity' : (object, None, None, gobject.PARAM_READABLE),
        'owner'            : (bool, None, None, False, gobject.PARAM_READABLE),
        'ip4-address'      : (str, None, None, None, gobject.PARAM_READABLE)
    }

    _PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
    _BUDDY_DBUS_INTERFACE = "org.laptop.Sugar.Presence.Buddy"

    def __init__(self, bus, new_obj_cb, del_obj_cb, object_path):
        """Initialise the reference to the buddy
        
        bus -- dbus bus object 
        new_obj_cb -- callback to call when this buddy joins an activity 
        del_obj_cb -- callback to call when this buddy leaves an activity 
        object_path -- path to the buddy object 
        """
        gobject.GObject.__init__(self)
        self._object_path = object_path
        self._ps_new_object = new_obj_cb
        self._ps_del_object = del_obj_cb
        self._properties = {}
        self._activities = {}

        bobj = bus.get_object(self._PRESENCE_SERVICE, object_path)
        self._buddy = dbus.Interface(bobj, self._BUDDY_DBUS_INTERFACE)

        self._icon_changed_signal = self._buddy.connect_to_signal(
                'IconChanged', self._icon_changed_cb, byte_arrays=True)
        self._joined_activity_signal = self._buddy.connect_to_signal(
                'JoinedActivity', self._joined_activity_cb)
        self._left_activity_signal = self._buddy.connect_to_signal(
                'LeftActivity', self._left_activity_cb)
        self._property_changed_signal = self._buddy.connect_to_signal(
                'PropertyChanged', self._property_changed_cb)

        self._properties = self._get_properties_helper()

        activities = self._buddy.GetJoinedActivities()
        for op in activities:
            self._activities[op] = self._ps_new_object(op)
        self._icon = None

    def destroy(self):
        self._icon_changed_signal.remove()
        self._joined_activity_signal.remove()
        self._left_activity_signal.remove()
        self._property_changed_signal.remove()
        
    def _get_properties_helper(self):
        """Retrieve the Buddy's property dictionary from the service object
        """
        props = self._buddy.GetProperties(byte_arrays=True)
        if not props:
            return {}
        return props

    def do_get_property(self, pspec):
        """Retrieve a particular property from our property dictionary 
        
        pspec -- XXX some sort of GTK specifier object with attributes
            including 'name', 'active' and 'icon-name'
        """
        if pspec.name == "key":
            return self._properties["key"]
        elif pspec.name == "nick":
            return self._properties["nick"]
        elif pspec.name == "color":
            return self._properties["color"]
        elif pspec.name == "current-activity":
            if not self._properties.has_key("current-activity"):
                return None
            curact = self._properties["current-activity"]
            if not len(curact):
                return None
            for activity in self._activities.values():
                if activity.props.id == curact:
                    return activity
            return None
        elif pspec.name == "owner":
            return self._properties["owner"]
        elif pspec.name == "icon":
            if not self._icon:
                self._icon = str(self._buddy.GetIcon(byte_arrays=True))
            return self._icon
        elif pspec.name == "ip4-address":
            # IPv4 address will go away quite soon
            if not self._properties.has_key("ip4-address"):
                return None
            return self._properties["ip4-address"]

    def object_path(self):
        """Retrieve our dbus object path"""
        return self._object_path

    def _emit_icon_changed_signal(self, bytes):
        """Emit GObject signal when icon has changed"""
        self._icon = str(bytes)
        self.emit('icon-changed')
        return False

    def _icon_changed_cb(self, icon_data):
        """Handle dbus signal by emitting a GObject signal"""
        gobject.idle_add(self._emit_icon_changed_signal, icon_data)

    def _emit_joined_activity_signal(self, object_path):
        """Emit activity joined signal with Activity object"""
        self.emit('joined-activity', self._ps_new_object(object_path))
        return False

    def _joined_activity_cb(self, object_path):
        """Handle dbus signal by emitting a GObject signal
        
        Stores the activity in activities dictionary as well
        """
        if not self._activities.has_key(object_path):
            self._activities[object_path] = self._ps_new_object(object_path)
        gobject.idle_add(self._emit_joined_activity_signal, object_path)

    def _emit_left_activity_signal(self, object_path):
        """Emit activity left signal with Activity object
        
        XXX this calls self._ps_new_object instead of self._ps_del_object,
            which would seem to be the incorrect callback?
        """
        self.emit('left-activity', self._ps_new_object(object_path))
        return False

    def _left_activity_cb(self, object_path):
        """Handle dbus signal by emitting a GObject signal
        
        Also removes from the activities dictionary
        """
        if self._activities.has_key(object_path):
            del self._activities[object_path]
        gobject.idle_add(self._emit_left_activity_signal, object_path)

    def _handle_property_changed_signal(self, prop_list):
        """Emit property-changed signal with property dictionary 
        
        Generates a property-changed signal with the results of 
        _get_properties_helper()
        """
        self._properties = self._get_properties_helper()
        # FIXME: don't leak unexposed property names
        self.emit('property-changed', prop_list)
        return False

    def _property_changed_cb(self, prop_list):
        """Handle dbus signal by emitting a GObject signal"""
        gobject.idle_add(self._handle_property_changed_signal, prop_list)

    def get_icon_pixbuf(self):
        """Retrieve Buddy's icon as a GTK pixel buffer
        
        XXX Why aren't the icons coming in as SVG?
        """
        if self.props.icon and len(self.props.icon):
            pbl = gtk.gdk.PixbufLoader()
            pbl.write(self.props.icon)
            pbl.close()
            return pbl.get_pixbuf()
        else:
            return None

    def get_joined_activities(self):
        """Retrieve the set of all activities which this buddy has joined 
        
        Uses the GetJoinedActivities method on the service 
        object to produce object paths, wraps each in an 
        Activity object.  
        
        returns list of presence Activity objects
        """
        try:
            resp = self._buddy.GetJoinedActivities()
        except dbus.exceptions.DBusException:
            return []
        acts = []
        for item in resp:
            acts.append(self._ps_new_object(item))
        return acts