]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
575814fab8218dc4c8df4f00a8fa6385ead136b8
[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 Collabora Ltd.
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU General Public License as
7  * published by the Free Software Foundation; either version 2 of the
8  * License, or (at your option) any later version.
9  *
10  * This program 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  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public
16  * License along with this program; if not, write to the
17  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18  * Boston, MA 02111-1307, USA.
19  * 
20  * Authors: Xavier Claessens <xclaesse@gmail.com>
21  */
22
23 #include <config.h>
24
25 #include <string.h>
26
27 #include <libtelepathy/tp-constants.h>
28
29 #include "empathy-contact-manager.h"
30 #include "empathy-contact-list.h"
31 #include "empathy-utils.h"
32 #include "empathy-debug.h"
33
34 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
35                        EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv))
36
37 #define DEBUG_DOMAIN "ContactManager"
38
39 struct _EmpathyContactManagerPriv {
40         GHashTable     *lists;
41         MissionControl *mc;
42 };
43
44 static void empathy_contact_manager_class_init (EmpathyContactManagerClass *klass);
45 static void empathy_contact_manager_init       (EmpathyContactManager      *manager);
46 static void contact_manager_iface_init         (EmpathyContactListIface    *iface);
47
48 G_DEFINE_TYPE_WITH_CODE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT,
49                          G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
50                                                 contact_manager_iface_init));
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 contact_manager_destroy_cb (EmpathyTpContactList  *list,
90                                         EmpathyContactManager *manager);
91
92 static void
93 contact_manager_disconnect_foreach (gpointer key,
94                                     gpointer value,
95                                     gpointer user_data)
96 {
97         EmpathyTpContactList  *list = value;
98         EmpathyContactManager *manager = user_data;
99
100         /* Disconnect signals from the list */
101         g_signal_handlers_disconnect_by_func (list,
102                                               contact_manager_members_changed_cb,
103                                               manager);
104         g_signal_handlers_disconnect_by_func (list,
105                                               contact_manager_pendings_changed_cb,
106                                               manager);
107         g_signal_handlers_disconnect_by_func (list,
108                                               contact_manager_groups_changed_cb,
109                                               manager);
110         g_signal_handlers_disconnect_by_func (list,
111                                               contact_manager_destroy_cb,
112                                               manager);
113 }
114
115 static void
116 contact_manager_destroy_cb (EmpathyTpContactList  *list,
117                             EmpathyContactManager *manager)
118 {
119         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
120         McAccount                 *account;
121
122         account = empathy_tp_contact_list_get_account (list);
123
124         empathy_debug (DEBUG_DOMAIN, "Removing account: %s",
125                        mc_account_get_display_name (account));
126
127         contact_manager_disconnect_foreach (account, list, manager);
128         g_hash_table_remove (priv->lists, account);
129 }
130
131 static void
132 contact_manager_add_account (EmpathyContactManager *manager,
133                              McAccount             *account)
134 {
135         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
136         EmpathyTpContactList      *list;
137
138         if (g_hash_table_lookup (priv->lists, account)) {
139                 return;
140         }
141
142         empathy_debug (DEBUG_DOMAIN, "Adding new account: %s",
143                        mc_account_get_display_name (account));
144
145         list = empathy_tp_contact_list_new (account);
146         if (!list) {
147                 return;
148         }
149
150         g_hash_table_insert (priv->lists, g_object_ref (account), list);
151
152         /* Connect signals */
153         g_signal_connect (list, "members-changed",
154                           G_CALLBACK (contact_manager_members_changed_cb),
155                           manager);
156         g_signal_connect (list, "pendings-changed",
157                           G_CALLBACK (contact_manager_pendings_changed_cb),
158                           manager);
159         g_signal_connect (list, "groups-changed",
160                           G_CALLBACK (contact_manager_groups_changed_cb),
161                           manager);
162         g_signal_connect (list, "destroy",
163                           G_CALLBACK (contact_manager_destroy_cb),
164                           manager);
165 }
166
167 static void
168 contact_manager_status_changed_cb (MissionControl                  *mc,
169                                    TelepathyConnectionStatus        status,
170                                    McPresence                       presence,
171                                    TelepathyConnectionStatusReason  reason,
172                                    const gchar                     *unique_name,
173                                    EmpathyContactManager           *manager)
174 {
175         McAccount *account;
176
177         if (status != TP_CONN_STATUS_CONNECTED) {
178                 /* We only care about newly connected accounts */
179                 return;
180         }
181
182         account = mc_account_lookup (unique_name);
183         contact_manager_add_account (manager, account);
184         g_object_unref (account);
185 }
186
187 static void
188 contact_manager_finalize (GObject *object)
189 {
190         EmpathyContactManagerPriv *priv = GET_PRIV (object);
191
192         g_hash_table_foreach (priv->lists,
193                               contact_manager_disconnect_foreach,
194                               object);
195         g_hash_table_destroy (priv->lists);
196         g_object_unref (priv->mc);
197 }
198
199 static void
200 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
201 {
202         GObjectClass *object_class = G_OBJECT_CLASS (klass);
203
204         object_class->finalize = contact_manager_finalize;
205
206         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
207 }
208
209 static void
210 empathy_contact_manager_init (EmpathyContactManager *manager)
211 {
212         EmpathyContactManagerPriv *priv;
213         GSList                    *accounts, *l;
214
215         priv = GET_PRIV (manager);
216
217         priv->lists = g_hash_table_new_full (empathy_account_hash,
218                                              empathy_account_equal,
219                                              (GDestroyNotify) g_object_unref,
220                                              (GDestroyNotify) g_object_unref);
221
222         priv->mc = empathy_mission_control_new ();
223
224         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
225                                      "AccountStatusChanged",
226                                      G_CALLBACK (contact_manager_status_changed_cb),
227                                      manager, NULL);
228
229         /* Get ContactList for existing connections */
230         accounts = mission_control_get_online_connections (priv->mc, NULL);
231         for (l = accounts; l; l = l->next) {
232                 contact_manager_add_account (manager, l->data);
233                 g_object_unref (l->data);
234         }
235         g_slist_free (accounts);
236 }
237
238 EmpathyContactManager *
239 empathy_contact_manager_new (void)
240 {
241         static EmpathyContactManager *manager = NULL;
242
243         if (!manager) {
244                 manager = g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
245                 g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager);
246         } else {
247                 g_object_ref (manager);
248         }
249
250         return manager;
251 }
252
253 EmpathyTpContactList *
254 empathy_contact_manager_get_list (EmpathyContactManager *manager,
255                                   McAccount             *account)
256 {
257         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
258
259         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
260         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
261
262         return g_hash_table_lookup (priv->lists, account);
263 }
264
265 static void
266 contact_manager_add (EmpathyContactList *manager,
267                      EmpathyContact     *contact,
268                      const gchar        *message)
269 {
270         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
271         EmpathyContactList        *list;
272         McAccount                 *account;
273
274         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
275
276         account = empathy_contact_get_account (contact);
277         list = g_hash_table_lookup (priv->lists, account);
278
279         if (list) {
280                 empathy_contact_list_add (list, contact, message);
281         }
282 }
283
284 static void
285 contact_manager_remove (EmpathyContactList *manager,
286                         EmpathyContact      *contact,
287                         const gchar        *message)
288 {
289         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
290         EmpathyContactList        *list;
291         McAccount                 *account;
292
293         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
294
295         account = empathy_contact_get_account (contact);
296         list = g_hash_table_lookup (priv->lists, account);
297
298         if (list) {
299                 empathy_contact_list_remove (list, contact, message);
300         }
301 }
302
303 static void
304 contact_manager_get_members_foreach (McAccount             *account,
305                                      EmpathyTpContactList  *list,
306                                      GList                **contacts)
307 {
308         GList *l;
309
310         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
311         *contacts = g_list_concat (*contacts, l);
312 }
313
314 static GList *
315 contact_manager_get_members (EmpathyContactList *manager)
316 {
317         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
318         GList                     *contacts = NULL;
319
320         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
321
322         g_hash_table_foreach (priv->lists,
323                               (GHFunc) contact_manager_get_members_foreach,
324                               &contacts);
325
326         return contacts;
327 }
328
329 static void
330 contact_manager_get_pendings_foreach (McAccount             *account,
331                                       EmpathyTpContactList  *list,
332                                       GList                **contacts)
333 {
334         GList *l;
335
336         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
337         *contacts = g_list_concat (*contacts, l);
338 }
339
340 static GList *
341 contact_manager_get_pendings (EmpathyContactList *manager)
342 {
343         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
344         GList                     *contacts = NULL;
345
346         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
347
348         g_hash_table_foreach (priv->lists,
349                               (GHFunc) contact_manager_get_pendings_foreach,
350                               &contacts);
351
352         return contacts;
353 }
354
355 static void
356 contact_manager_get_all_groups_foreach (McAccount             *account,
357                                         EmpathyTpContactList  *list,
358                                         GList                **all_groups)
359 {
360         GList *groups, *l;
361
362         groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list));
363         for (l = groups; l; l = l->next) {
364                 if (!g_list_find_custom (*all_groups,
365                                          l->data,
366                                          (GCompareFunc) strcmp)) {
367                         *all_groups = g_list_prepend (*all_groups, l->data);
368                 } else {
369                         g_free (l->data);
370                 }
371         }
372
373         g_list_free (groups);
374 }
375
376 static GList *
377 contact_manager_get_all_groups (EmpathyContactList *manager)
378 {
379         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
380         GList                     *groups = NULL;
381
382         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
383
384         g_hash_table_foreach (priv->lists,
385                               (GHFunc) contact_manager_get_all_groups_foreach,
386                               &groups);
387
388         return groups;
389 }
390
391 static GList *
392 contact_manager_get_groups (EmpathyContactList *manager,
393                             EmpathyContact     *contact)
394 {
395         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
396         EmpathyContactList        *list;
397         McAccount                 *account;
398
399         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
400
401         account = empathy_contact_get_account (contact);
402         list = g_hash_table_lookup (priv->lists, account);
403
404         if (list) {
405                 return empathy_contact_list_get_groups (list, contact);
406         }
407
408         return NULL;
409 }
410
411 static void
412 contact_manager_add_to_group (EmpathyContactList *manager,
413                               EmpathyContact     *contact,
414                               const gchar        *group)
415 {
416         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
417         EmpathyContactList        *list;
418         McAccount                 *account;
419
420         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
421
422         account = empathy_contact_get_account (contact);
423         list = g_hash_table_lookup (priv->lists, account);
424
425         if (list) {
426                 empathy_contact_list_add_to_group (list, contact, group);
427         }
428 }
429
430 static void
431 contact_manager_remove_from_group (EmpathyContactList *manager,
432                                    EmpathyContact     *contact,
433                                    const gchar        *group)
434 {
435         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
436         EmpathyContactList        *list;
437         McAccount                 *account;
438
439         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
440
441         account = empathy_contact_get_account (contact);
442         list = g_hash_table_lookup (priv->lists, account);
443
444         if (list) {
445                 empathy_contact_list_remove_from_group (list, contact, group);
446         }
447 }
448
449 typedef struct {
450         const gchar *old_group;
451         const gchar *new_group;
452 } RenameGroupData;
453
454 static void
455 contact_manager_rename_group_foreach (McAccount            *account,
456                                       EmpathyTpContactList *list,
457                                       RenameGroupData      *data)
458 {
459         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
460                                            data->old_group,
461                                            data->new_group);
462 }
463
464 static void
465 contact_manager_rename_group (EmpathyContactList *manager,
466                               const gchar        *old_group,
467                               const gchar        *new_group)
468 {
469         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
470         RenameGroupData            data;
471
472         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
473
474         data.old_group = old_group;
475         data.new_group = new_group;
476         g_hash_table_foreach (priv->lists,
477                               (GHFunc) contact_manager_rename_group_foreach,
478                               &data);
479 }
480
481 static void
482 contact_manager_iface_init (EmpathyContactListIface *iface)
483 {
484         iface->add               = contact_manager_add;
485         iface->remove            = contact_manager_remove;
486         iface->get_members       = contact_manager_get_members;
487         iface->get_pendings      = contact_manager_get_pendings;
488         iface->get_all_groups    = contact_manager_get_all_groups;
489         iface->get_groups        = contact_manager_get_groups;
490         iface->add_to_group      = contact_manager_add_to_group;
491         iface->remove_from_group = contact_manager_remove_from_group;
492         iface->rename_group      = contact_manager_rename_group;
493 }
494