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