]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-monitor.c
Don't own a reference to the EmpathyTpChat, otherwise it won't be
[empathy.git] / libempathy / empathy-contact-monitor.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2008 Collabora Ltd.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  * 
19  * Authors: Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
20  */
21
22 #include <glib-object.h>
23
24 #include <libmissioncontrol/mc-enum-types.h>
25
26 #include "empathy-contact-monitor.h"
27 #include "empathy-contact-list.h"
28
29 #include "empathy-contact.h"
30 #include "empathy-utils.h"
31 #include "empathy-marshal.h"
32
33 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContactMonitor)
34
35 typedef struct {
36         EmpathyContactList *proxy;
37         GPtrArray *contacts;
38 } EmpathyContactMonitorPriv;
39
40 enum {
41         CONTACT_ADDED,
42         CONTACT_AVATAR_CHANGED,
43         CONTACT_CAPABILITIES_CHANGED,
44         CONTACT_NAME_CHANGED,
45         CONTACT_PRESENCE_CHANGED,
46         CONTACT_PRESENCE_MESSAGE_CHANGED,
47         CONTACT_REMOVED,
48         LAST_SIGNAL
49 };
50
51 enum {
52         PROP_0,
53         PROP_PROXY
54 };
55
56 static void  contact_remove_foreach (EmpathyContact *contact,
57                                      EmpathyContactMonitor *monitor);
58 static void  cl_members_changed_cb  (EmpathyContactList    *cl,
59                                      EmpathyContact        *contact,
60                                      EmpathyContact        *actor,
61                                      guint                  reason,
62                                      gchar                 *message,
63                                      gboolean               is_member,
64                                      EmpathyContactMonitor *monitor);
65
66 static guint signals[LAST_SIGNAL];
67
68 G_DEFINE_TYPE (EmpathyContactMonitor, empathy_contact_monitor, G_TYPE_OBJECT);
69
70 static void
71 do_set_property (GObject      *object,
72                  guint         param_id,
73                  const GValue *value,
74                  GParamSpec   *pspec)
75 {
76         switch (param_id) {
77         case PROP_PROXY:
78                 empathy_contact_monitor_set_proxy
79                                 (EMPATHY_CONTACT_MONITOR (object),
80                                  g_value_get_object (value));
81                 break;
82         default:
83                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
84                 break;
85         };
86 }
87
88 static void
89 do_get_property (GObject    *object,
90                  guint       param_id,
91                  GValue     *value,
92                  GParamSpec *pspec)
93 {
94         EmpathyContactMonitorPriv *priv = GET_PRIV (object);
95
96         switch (param_id) {
97         case PROP_PROXY:
98                 g_value_set_object (value, priv->proxy);
99                 break;
100         default:
101                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
102                 break;
103         };
104 }
105
106 static void
107 do_finalize (GObject *obj)
108 {
109         EmpathyContactMonitorPriv *priv;
110
111         priv = GET_PRIV (obj);
112         
113         if (priv->contacts) {
114                 g_ptr_array_foreach (priv->contacts,
115                                      (GFunc) contact_remove_foreach, obj);
116                 g_ptr_array_free (priv->contacts, TRUE);
117                 priv->contacts = NULL;
118         }
119
120         if (priv->proxy) {
121                 g_signal_handlers_disconnect_by_func (priv->proxy,
122                                                       cl_members_changed_cb, obj);
123         }
124
125         G_OBJECT_CLASS (empathy_contact_monitor_parent_class)->finalize (obj);
126 }
127
128 static void
129 empathy_contact_monitor_class_init (EmpathyContactMonitorClass *klass)
130 {
131         GObjectClass *oclass = G_OBJECT_CLASS (klass);
132
133         oclass->finalize = do_finalize;
134         oclass->get_property = do_get_property;
135         oclass->set_property = do_set_property;
136
137         g_object_class_install_property (oclass,
138                                          PROP_PROXY,
139                                          g_param_spec_object ("proxy",
140                                                               "Monitor's proxy",
141                                                               "The contact list associated we're monitoring",
142                                                               EMPATHY_TYPE_CONTACT_LIST,
143                                                               G_PARAM_READWRITE |
144                                                               G_PARAM_CONSTRUCT_ONLY |
145                                                               G_PARAM_STATIC_STRINGS));
146
147         signals[CONTACT_ADDED] =
148                 g_signal_new ("contact-added",
149                               G_TYPE_FROM_CLASS (klass),
150                               G_SIGNAL_RUN_LAST,
151                               0,
152                               NULL, NULL,
153                               g_cclosure_marshal_VOID__OBJECT,
154                               G_TYPE_NONE,
155                               1, EMPATHY_TYPE_CONTACT);
156         signals[CONTACT_AVATAR_CHANGED] =
157                 g_signal_new ("contact-avatar-changed",
158                               G_TYPE_FROM_CLASS (klass),
159                               G_SIGNAL_RUN_LAST,
160                               0,
161                               NULL, NULL,
162                               g_cclosure_marshal_VOID__OBJECT,
163                               G_TYPE_NONE,
164                               1, EMPATHY_TYPE_CONTACT);
165         signals[CONTACT_CAPABILITIES_CHANGED] =
166                 g_signal_new ("contact-capabilities-changed",
167                               G_TYPE_FROM_CLASS (klass),
168                               G_SIGNAL_RUN_LAST,
169                               0,
170                               NULL, NULL,
171                               g_cclosure_marshal_VOID__OBJECT,
172                               G_TYPE_NONE,
173                               1, EMPATHY_TYPE_CONTACT);
174         signals[CONTACT_NAME_CHANGED] =
175                 g_signal_new ("contact-name-changed",
176                               G_TYPE_FROM_CLASS (klass),
177                               G_SIGNAL_RUN_LAST,
178                               0,
179                               NULL, NULL,
180                               _empathy_marshal_VOID__OBJECT_STRING,
181                               G_TYPE_NONE,
182                               2, EMPATHY_TYPE_CONTACT,
183                               G_TYPE_STRING);
184         signals[CONTACT_PRESENCE_CHANGED] =
185                 g_signal_new ("contact-presence-changed",
186                               G_TYPE_FROM_CLASS (klass),
187                               G_SIGNAL_RUN_LAST,
188                               0,
189                               NULL, NULL,
190                               _empathy_marshal_VOID__OBJECT_ENUM_ENUM,
191                               G_TYPE_NONE,
192                               3, EMPATHY_TYPE_CONTACT,
193                               MC_TYPE_PRESENCE,
194                               MC_TYPE_PRESENCE);
195         signals[CONTACT_PRESENCE_MESSAGE_CHANGED] =
196                 g_signal_new ("contact-presence-message-changed",
197                               G_TYPE_FROM_CLASS (klass),
198                               G_SIGNAL_RUN_LAST,
199                               0,
200                               NULL, NULL,
201                               _empathy_marshal_VOID__OBJECT_STRING,
202                               G_TYPE_NONE,
203                               2, EMPATHY_TYPE_CONTACT,
204                               G_TYPE_STRING);
205         signals[CONTACT_REMOVED] =
206                 g_signal_new ("contact-removed",
207                               G_TYPE_FROM_CLASS (klass),
208                               G_SIGNAL_RUN_LAST,
209                               0,
210                               NULL, NULL,
211                               g_cclosure_marshal_VOID__OBJECT,
212                               G_TYPE_NONE,
213                               1, EMPATHY_TYPE_CONTACT);
214
215         g_type_class_add_private (klass, sizeof (EmpathyContactMonitorPriv));
216 }
217
218 static void
219 empathy_contact_monitor_init (EmpathyContactMonitor *self)
220 {
221         EmpathyContactMonitorPriv *priv =
222                 G_TYPE_INSTANCE_GET_PRIVATE (self,
223                                              EMPATHY_TYPE_CONTACT_MONITOR, EmpathyContactMonitorPriv);
224
225         self->priv = priv;
226         priv->contacts = NULL;
227         priv->proxy = NULL;
228 }
229
230 static void
231 contact_monitor_presence_changed_cb (EmpathyContact *contact,
232                                      McPresence current_presence,
233                                      McPresence previous_presence,
234                                      EmpathyContactMonitor *self)
235 {
236         g_signal_emit (self, signals[CONTACT_PRESENCE_CHANGED], 0, contact,
237                        current_presence, previous_presence);
238 }
239
240 static void
241 contact_monitor_presence_message_changed_cb (EmpathyContact *contact,
242                                              GParamSpec *pspec,
243                                              EmpathyContactMonitor *self)
244 {
245         const char *status;
246
247         /* use the status so that we always have a presence message */
248         status = empathy_contact_get_status (contact);
249
250         g_signal_emit (self, signals[CONTACT_PRESENCE_MESSAGE_CHANGED], 0,
251                        contact, status);
252 }
253
254 static void
255 contact_monitor_name_changed_cb (EmpathyContact *contact,
256                                  GParamSpec *pspec,
257                                  EmpathyContactMonitor *self)
258 {
259         const char *name;
260
261         name = empathy_contact_get_name (contact);
262
263         g_signal_emit (self, signals[CONTACT_NAME_CHANGED], 0, contact, name);
264 }
265
266 static void
267 contact_monitor_avatar_changed_cb (EmpathyContact *contact,
268                                    GParamSpec *pspec,
269                                    EmpathyContactMonitor *self)
270 {
271         /* don't emit a pixbuf in the signal, as we don't know how large
272          * a client would like it to be.
273          */
274
275         g_signal_emit (self, signals[CONTACT_AVATAR_CHANGED], 0, contact);
276 }
277
278 static void
279 contact_monitor_capabilities_changed_cb (EmpathyContact *contact,
280                                          GParamSpec *pspec,
281                                          EmpathyContactMonitor *self)
282 {
283         g_signal_emit (self, signals[CONTACT_CAPABILITIES_CHANGED], 0, contact);
284 }
285
286 static void
287 contact_add (EmpathyContactMonitor *monitor,
288              EmpathyContact *contact)
289 {
290         EmpathyContactMonitorPriv *priv = GET_PRIV (monitor);
291
292         g_signal_connect (contact, "presence-changed",
293                           G_CALLBACK (contact_monitor_presence_changed_cb),
294                           monitor);
295         g_signal_connect (contact, "notify::presence-message",
296                           G_CALLBACK (contact_monitor_presence_message_changed_cb),
297                           monitor);
298         g_signal_connect (contact, "notify::name",
299                           G_CALLBACK (contact_monitor_name_changed_cb),
300                           monitor);
301         g_signal_connect (contact, "notify::avatar",
302                           G_CALLBACK (contact_monitor_avatar_changed_cb),
303                           monitor);
304         g_signal_connect (contact, "notify::capabilities",
305                           G_CALLBACK (contact_monitor_capabilities_changed_cb),
306                           monitor);
307
308         g_ptr_array_add (priv->contacts, g_object_ref (contact));
309
310         g_signal_emit (monitor, signals[CONTACT_ADDED], 0, contact);
311 }
312
313 static void
314 contact_remove (EmpathyContactMonitor *monitor,
315                 EmpathyContact *contact)
316 {
317         EmpathyContactMonitorPriv *priv = GET_PRIV (monitor);
318
319         g_signal_handlers_disconnect_by_func (contact,
320                                               G_CALLBACK (contact_monitor_presence_changed_cb),
321                                               monitor);
322         g_signal_handlers_disconnect_by_func (contact,
323                                               G_CALLBACK (contact_monitor_presence_message_changed_cb),
324                                               monitor);
325         g_signal_handlers_disconnect_by_func (contact,
326                                               G_CALLBACK (contact_monitor_name_changed_cb),
327                                               monitor);
328         g_signal_handlers_disconnect_by_func (contact,
329                                               G_CALLBACK (contact_monitor_avatar_changed_cb),
330                                               monitor);
331         g_signal_handlers_disconnect_by_func (contact,
332                                               G_CALLBACK (contact_monitor_capabilities_changed_cb),
333                                               monitor);
334
335         g_ptr_array_remove (priv->contacts, contact);
336
337         g_signal_emit (monitor, signals[CONTACT_REMOVED], 0, contact);
338
339         g_object_unref (contact);
340 }
341
342 static void
343 contact_remove_foreach (EmpathyContact *contact,
344                         EmpathyContactMonitor *monitor)
345 {
346         contact_remove (monitor, contact);
347 }
348
349 static void
350 cl_members_changed_cb (EmpathyContactList    *cl,
351                        EmpathyContact        *contact,
352                        EmpathyContact        *actor,
353                        guint                  reason,
354                        gchar                 *message,
355                        gboolean               is_member,
356                        EmpathyContactMonitor *monitor)
357 {
358         if (is_member) {
359                 contact_add (monitor, contact);
360         } else {
361                 contact_remove (monitor, contact);
362         }
363 }
364
365 /* public methods */
366
367 void
368 empathy_contact_monitor_set_proxy (EmpathyContactMonitor *self,
369                                    EmpathyContactList *proxy)
370 {
371         EmpathyContactMonitorPriv *priv;
372
373         g_assert (EMPATHY_IS_CONTACT_MONITOR (self));
374         g_assert (EMPATHY_IS_CONTACT_LIST (proxy));
375
376         priv = GET_PRIV (self);
377
378         if (priv->contacts != NULL) {
379                 g_ptr_array_foreach (priv->contacts,
380                                      (GFunc) contact_remove_foreach, self);
381                 g_ptr_array_free (priv->contacts, TRUE);
382                 priv->contacts = NULL;
383         }
384
385         priv->proxy = proxy;
386         priv->contacts = g_ptr_array_new ();
387
388         g_signal_connect (proxy, "members-changed",
389                           G_CALLBACK (cl_members_changed_cb), self);
390 }
391
392 EmpathyContactMonitor *
393 empathy_contact_monitor_new_for_proxy (EmpathyContactList *proxy)
394 {
395         EmpathyContactMonitor *retval;
396
397         g_assert (EMPATHY_IS_CONTACT_LIST (proxy));
398
399         retval = g_object_new (EMPATHY_TYPE_CONTACT_MONITOR,
400                                "proxy", proxy, NULL);
401
402         return retval;
403 }