Get the priv struct of the right object.
[empathy.git] / libempathy / empathy-contact-manager.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * Copyright (C) 2007-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: Xavier Claessens <xclaesse@gmail.com>
20  */
21
22 #include <config.h>
23
24 #include <string.h>
25
26 #include <telepathy-glib/enums.h>
27
28 #include "empathy-contact-manager.h"
29 #include "empathy-account-manager.h"
30 #include "empathy-contact-monitor.h"
31 #include "empathy-contact-list.h"
32 #include "empathy-utils.h"
33
34 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
35 #include "empathy-debug.h"
36
37 #define GET_PRIV(obj) EMPATHY_GET_PRIV (obj, EmpathyContactManager)
38 typedef struct {
39         GHashTable     *lists;
40         EmpathyAccountManager *account_manager;
41         EmpathyContactMonitor *contact_monitor;
42 } EmpathyContactManagerPriv;
43
44 static void contact_manager_iface_init         (EmpathyContactListIface    *iface);
45
46 G_DEFINE_TYPE_WITH_CODE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT,
47                          G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
48                                                 contact_manager_iface_init));
49
50 static EmpathyContactManager *manager_singleton = NULL;
51
52 static void
53 contact_manager_members_changed_cb (EmpathyTpContactList  *list,
54                                     EmpathyContact        *contact,
55                                     EmpathyContact        *actor,
56                                     guint                  reason,
57                                     gchar                 *message,
58                                     gboolean               is_member,
59                                     EmpathyContactManager *manager)
60 {
61         g_signal_emit_by_name (manager, "members-changed",
62                                contact, actor, reason, message, is_member);
63 }
64
65 static void
66 contact_manager_pendings_changed_cb (EmpathyTpContactList  *list,
67                                      EmpathyContact        *contact,
68                                      EmpathyContact        *actor,
69                                      guint                  reason,
70                                      gchar                 *message,
71                                      gboolean               is_pending,
72                                      EmpathyContactManager *manager)
73 {
74         g_signal_emit_by_name (manager, "pendings-changed",
75                                contact, actor, reason, message, is_pending);
76 }
77
78 static void
79 contact_manager_groups_changed_cb (EmpathyTpContactList  *list,
80                                    EmpathyContact        *contact,
81                                    gchar                 *group,
82                                    gboolean               is_member,
83                                    EmpathyContactManager *manager)
84 {
85         g_signal_emit_by_name (manager, "groups-changed",
86                                contact, group, is_member);
87 }
88
89 static void
90 contact_manager_disconnect_foreach (gpointer key,
91                                     gpointer value,
92                                     gpointer user_data)
93 {
94         EmpathyTpContactList  *list = value;
95         EmpathyContactManager *manager = user_data;
96
97         /* Disconnect signals from the list */
98         g_signal_handlers_disconnect_by_func (list,
99                                               contact_manager_members_changed_cb,
100                                               manager);
101         g_signal_handlers_disconnect_by_func (list,
102                                               contact_manager_pendings_changed_cb,
103                                               manager);
104         g_signal_handlers_disconnect_by_func (list,
105                                               contact_manager_groups_changed_cb,
106                                               manager);
107 }
108
109 static void
110 contact_manager_invalidated_cb (TpProxy *connection,
111                                 guint    domain,
112                                 gint     code,
113                                 gchar   *message,
114                                 EmpathyContactManager *manager)
115 {
116         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
117         EmpathyTpContactList *list;
118
119         DEBUG ("Removing connection: %s (%s)",
120                 tp_proxy_get_object_path (TP_PROXY (connection)),
121                 message);
122
123         list = g_hash_table_lookup (priv->lists, connection);
124         if (list) {
125                 empathy_tp_contact_list_remove_all (list);
126                 g_hash_table_remove (priv->lists, connection);          
127         }
128 }
129
130 static void
131 contact_manager_new_connection_cb (EmpathyAccountManager *account_manager,
132                                    TpConnection *connection,
133                                    EmpathyContactManager *manager)
134 {
135         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
136         EmpathyTpContactList      *list;
137
138         if (g_hash_table_lookup (priv->lists, connection)) {
139                 return;
140         }
141
142         DEBUG ("Adding new connection: %s",
143                 tp_proxy_get_object_path (TP_PROXY (connection)));
144
145         list = empathy_tp_contact_list_new (connection);
146         g_hash_table_insert (priv->lists, g_object_ref (connection), list);
147         g_signal_connect (connection, "invalidated",
148                           G_CALLBACK (contact_manager_invalidated_cb),
149                           manager);
150
151         /* Connect signals */
152         g_signal_connect (list, "members-changed",
153                           G_CALLBACK (contact_manager_members_changed_cb),
154                           manager);
155         g_signal_connect (list, "pendings-changed",
156                           G_CALLBACK (contact_manager_pendings_changed_cb),
157                           manager);
158         g_signal_connect (list, "groups-changed",
159                           G_CALLBACK (contact_manager_groups_changed_cb),
160                           manager);
161 }
162
163 static void
164 contact_manager_finalize (GObject *object)
165 {
166         EmpathyContactManagerPriv *priv = GET_PRIV (object);
167
168         g_hash_table_foreach (priv->lists,
169                               contact_manager_disconnect_foreach,
170                               object);
171         g_hash_table_destroy (priv->lists);
172
173         g_signal_handlers_disconnect_by_func (priv->account_manager,
174                                               contact_manager_new_connection_cb,
175                                               object);
176         g_object_unref (priv->account_manager);
177
178         if (priv->contact_monitor) {
179                 g_object_unref (priv->contact_monitor);
180         }
181 }
182
183 static GObject *
184 contact_manager_constructor (GType type,
185                              guint n_props,
186                              GObjectConstructParam *props)
187 {
188         GObject *retval;
189
190         if (manager_singleton) {
191                 retval = g_object_ref (manager_singleton);
192         } else {
193                 retval = G_OBJECT_CLASS (empathy_contact_manager_parent_class)->constructor
194                         (type, n_props, props);
195
196                 manager_singleton = EMPATHY_CONTACT_MANAGER (retval);
197                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
198         }
199
200         return retval;
201 }
202
203 static void
204 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
205 {
206         GObjectClass *object_class = G_OBJECT_CLASS (klass);
207
208         object_class->finalize = contact_manager_finalize;
209         object_class->constructor = contact_manager_constructor;
210
211         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
212 }
213
214 static void
215 empathy_contact_manager_init (EmpathyContactManager *manager)
216 {
217         GList *connections, *l;
218         EmpathyContactManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
219                 EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv);
220
221         manager->priv = priv;
222         priv->lists = g_hash_table_new_full (empathy_proxy_hash,
223                                              empathy_proxy_equal,
224                                              (GDestroyNotify) g_object_unref,
225                                              (GDestroyNotify) g_object_unref);
226         priv->account_manager = empathy_account_manager_dup_singleton ();
227         priv->contact_monitor = NULL;
228
229         g_signal_connect (priv->account_manager, "new-connection",
230                           G_CALLBACK (contact_manager_new_connection_cb),
231                           manager);
232
233         /* Get ContactList for existing connections */
234         connections = empathy_account_manager_dup_connections (priv->account_manager);
235         for (l = connections; l; l = l->next) {
236                 contact_manager_new_connection_cb (priv->account_manager,
237                                                    l->data, manager);
238                 g_object_unref (l->data);
239         }
240         g_list_free (connections);
241 }
242
243 EmpathyContactManager *
244 empathy_contact_manager_dup_singleton (void)
245 {
246         return g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
247 }
248
249 EmpathyTpContactList *
250 empathy_contact_manager_get_list (EmpathyContactManager *manager,
251                                   TpConnection          *connection)
252 {
253         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
254
255         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
256         g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
257
258         return g_hash_table_lookup (priv->lists, connection);
259 }
260
261 static void
262 contact_manager_add (EmpathyContactList *manager,
263                      EmpathyContact     *contact,
264                      const gchar        *message)
265 {
266         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
267         EmpathyContactList        *list;
268         TpConnection              *connection;
269
270         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
271
272         connection = empathy_contact_get_connection (contact);
273         list = g_hash_table_lookup (priv->lists, connection);
274
275         if (list) {
276                 empathy_contact_list_add (list, contact, message);
277         }
278 }
279
280 static void
281 contact_manager_remove (EmpathyContactList *manager,
282                         EmpathyContact     *contact,
283                         const gchar        *message)
284 {
285         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
286         EmpathyContactList        *list;
287         TpConnection              *connection;
288
289         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
290
291         connection = empathy_contact_get_connection (contact);
292         list = g_hash_table_lookup (priv->lists, connection);
293
294         if (list) {
295                 empathy_contact_list_remove (list, contact, message);
296         }
297 }
298
299 static void
300 contact_manager_get_members_foreach (TpConnection          *connection,
301                                      EmpathyTpContactList  *list,
302                                      GList                **contacts)
303 {
304         GList *l;
305
306         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
307         *contacts = g_list_concat (*contacts, l);
308 }
309
310 static GList *
311 contact_manager_get_members (EmpathyContactList *manager)
312 {
313         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
314         GList                     *contacts = NULL;
315
316         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
317
318         g_hash_table_foreach (priv->lists,
319                               (GHFunc) contact_manager_get_members_foreach,
320                               &contacts);
321
322         return contacts;
323 }
324
325 static EmpathyContactMonitor *
326 contact_manager_get_monitor (EmpathyContactList *manager)
327 {
328         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
329
330         if (priv->contact_monitor == NULL) {
331                 priv->contact_monitor = empathy_contact_monitor_new_for_iface (manager);
332         }
333
334         return priv->contact_monitor;
335 }
336
337 static void
338 contact_manager_get_pendings_foreach (TpConnection          *connection,
339                                       EmpathyTpContactList  *list,
340                                       GList                **contacts)
341 {
342         GList *l;
343
344         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
345         *contacts = g_list_concat (*contacts, l);
346 }
347
348 static GList *
349 contact_manager_get_pendings (EmpathyContactList *manager)
350 {
351         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
352         GList                     *contacts = NULL;
353
354         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
355
356         g_hash_table_foreach (priv->lists,
357                               (GHFunc) contact_manager_get_pendings_foreach,
358                               &contacts);
359
360         return contacts;
361 }
362
363 static void
364 contact_manager_get_all_groups_foreach (TpConnection          *connection,
365                                         EmpathyTpContactList  *list,
366                                         GList                **all_groups)
367 {
368         GList *groups, *l;
369
370         groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list));
371         for (l = groups; l; l = l->next) {
372                 if (!g_list_find_custom (*all_groups,
373                                          l->data,
374                                          (GCompareFunc) strcmp)) {
375                         *all_groups = g_list_prepend (*all_groups, l->data);
376                 } else {
377                         g_free (l->data);
378                 }
379         }
380
381         g_list_free (groups);
382 }
383
384 static GList *
385 contact_manager_get_all_groups (EmpathyContactList *manager)
386 {
387         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
388         GList                     *groups = NULL;
389
390         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
391
392         g_hash_table_foreach (priv->lists,
393                               (GHFunc) contact_manager_get_all_groups_foreach,
394                               &groups);
395
396         return groups;
397 }
398
399 static GList *
400 contact_manager_get_groups (EmpathyContactList *manager,
401                             EmpathyContact     *contact)
402 {
403         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
404         EmpathyContactList        *list;
405         TpConnection              *connection;
406
407         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
408
409         connection = empathy_contact_get_connection (contact);
410         list = g_hash_table_lookup (priv->lists, connection);
411
412         if (list) {
413                 return empathy_contact_list_get_groups (list, contact);
414         }
415
416         return NULL;
417 }
418
419 static void
420 contact_manager_add_to_group (EmpathyContactList *manager,
421                               EmpathyContact     *contact,
422                               const gchar        *group)
423 {
424         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
425         EmpathyContactList        *list;
426         TpConnection              *connection;
427
428         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
429
430         connection = empathy_contact_get_connection (contact);
431         list = g_hash_table_lookup (priv->lists, connection);
432
433         if (list) {
434                 empathy_contact_list_add_to_group (list, contact, group);
435         }
436 }
437
438 static void
439 contact_manager_remove_from_group (EmpathyContactList *manager,
440                                    EmpathyContact     *contact,
441                                    const gchar        *group)
442 {
443         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
444         EmpathyContactList        *list;
445         TpConnection              *connection;
446
447         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
448
449         connection = empathy_contact_get_connection (contact);
450         list = g_hash_table_lookup (priv->lists, connection);
451
452         if (list) {
453                 empathy_contact_list_remove_from_group (list, contact, group);
454         }
455 }
456
457 typedef struct {
458         const gchar *old_group;
459         const gchar *new_group;
460 } RenameGroupData;
461
462 static void
463 contact_manager_rename_group_foreach (TpConnection         *connection,
464                                       EmpathyTpContactList *list,
465                                       RenameGroupData      *data)
466 {
467         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
468                                            data->old_group,
469                                            data->new_group);
470 }
471
472 static void
473 contact_manager_rename_group (EmpathyContactList *manager,
474                               const gchar        *old_group,
475                               const gchar        *new_group)
476 {
477         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
478         RenameGroupData            data;
479
480         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
481
482         data.old_group = old_group;
483         data.new_group = new_group;
484         g_hash_table_foreach (priv->lists,
485                               (GHFunc) contact_manager_rename_group_foreach,
486                               &data);
487 }
488
489 static void contact_manager_remove_group_foreach (TpConnection         *connection,
490                                                   EmpathyTpContactList *list,
491                                                   const gchar *group)
492 {
493         empathy_contact_list_remove_group (EMPATHY_CONTACT_LIST (list),
494                                            group);
495 }
496
497 static void
498 contact_manager_remove_group (EmpathyContactList *manager,
499                               const gchar *group)
500 {
501         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
502         
503         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
504
505         g_hash_table_foreach (priv->lists,
506                               (GHFunc) contact_manager_remove_group_foreach,
507                               (gpointer) group);
508 }
509
510 static void
511 contact_manager_iface_init (EmpathyContactListIface *iface)
512 {
513         iface->add               = contact_manager_add;
514         iface->remove            = contact_manager_remove;
515         iface->get_members       = contact_manager_get_members;
516         iface->get_monitor       = contact_manager_get_monitor;
517         iface->get_pendings      = contact_manager_get_pendings;
518         iface->get_all_groups    = contact_manager_get_all_groups;
519         iface->get_groups        = contact_manager_get_groups;
520         iface->add_to_group      = contact_manager_add_to_group;
521         iface->remove_from_group = contact_manager_remove_from_group;
522         iface->rename_group      = contact_manager_rename_group;
523         iface->remove_group      = contact_manager_remove_group;
524 }
525
526 gboolean
527 empathy_contact_manager_can_add (EmpathyContactManager *manager,
528                                  TpConnection          *connection)
529 {
530         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
531         EmpathyTpContactList      *list;
532         
533         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), FALSE);
534
535         list = g_hash_table_lookup (priv->lists, connection);
536         if (list == NULL)
537                 return FALSE;
538
539         return empathy_tp_contact_list_can_add (list);
540 }
541