]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
We can't use *_run_* API from dbus signal cb or method implementation. To avoid probl...
[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-contact-list.h"
30 #include "empathy-utils.h"
31 #include "empathy-debug.h"
32
33 #define GET_PRIV(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
34                        EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv))
35
36 #define DEBUG_DOMAIN "ContactManager"
37
38 struct _EmpathyContactManagerPriv {
39         GHashTable     *lists;
40         MissionControl *mc;
41 };
42
43 static void empathy_contact_manager_class_init (EmpathyContactManagerClass *klass);
44 static void empathy_contact_manager_init       (EmpathyContactManager      *manager);
45 static void contact_manager_iface_init         (EmpathyContactListIface    *iface);
46
47 G_DEFINE_TYPE_WITH_CODE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT,
48                          G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_CONTACT_LIST,
49                                                 contact_manager_iface_init));
50
51 static void
52 contact_manager_members_changed_cb (EmpathyTpContactList  *list,
53                                     EmpathyContact        *contact,
54                                     EmpathyContact        *actor,
55                                     guint                  reason,
56                                     gchar                 *message,
57                                     gboolean               is_member,
58                                     EmpathyContactManager *manager)
59 {
60         g_signal_emit_by_name (manager, "members-changed",
61                                contact, actor, reason, message, is_member);
62 }
63
64 static void
65 contact_manager_pendings_changed_cb (EmpathyTpContactList  *list,
66                                      EmpathyContact        *contact,
67                                      EmpathyContact        *actor,
68                                      guint                  reason,
69                                      gchar                 *message,
70                                      gboolean               is_pending,
71                                      EmpathyContactManager *manager)
72 {
73         g_signal_emit_by_name (manager, "pendings-changed",
74                                contact, actor, reason, message, is_pending);
75 }
76
77 static void
78 contact_manager_groups_changed_cb (EmpathyTpContactList  *list,
79                                    EmpathyContact        *contact,
80                                    gchar                 *group,
81                                    gboolean               is_member,
82                                    EmpathyContactManager *manager)
83 {
84         g_signal_emit_by_name (manager, "groups-changed",
85                                contact, group, is_member);
86 }
87
88 static void contact_manager_destroy_cb (EmpathyTpContactList  *list,
89                                         EmpathyContactManager *manager);
90
91 static void
92 contact_manager_disconnect_foreach (gpointer key,
93                                     gpointer value,
94                                     gpointer user_data)
95 {
96         EmpathyTpContactList  *list = value;
97         EmpathyContactManager *manager = user_data;
98
99         /* Disconnect signals from the list */
100         g_signal_handlers_disconnect_by_func (list,
101                                               contact_manager_members_changed_cb,
102                                               manager);
103         g_signal_handlers_disconnect_by_func (list,
104                                               contact_manager_pendings_changed_cb,
105                                               manager);
106         g_signal_handlers_disconnect_by_func (list,
107                                               contact_manager_groups_changed_cb,
108                                               manager);
109         g_signal_handlers_disconnect_by_func (list,
110                                               contact_manager_destroy_cb,
111                                               manager);
112 }
113
114 static void
115 contact_manager_destroy_cb (EmpathyTpContactList  *list,
116                             EmpathyContactManager *manager)
117 {
118         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
119         McAccount                 *account;
120
121         account = empathy_tp_contact_list_get_account (list);
122
123         empathy_debug (DEBUG_DOMAIN, "Removing account: %s",
124                        mc_account_get_display_name (account));
125
126         contact_manager_disconnect_foreach (account, list, manager);
127         g_hash_table_remove (priv->lists, account);
128 }
129
130 static void
131 contact_manager_add_account (EmpathyContactManager *manager,
132                              McAccount             *account)
133 {
134         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
135         EmpathyTpContactList      *list;
136
137         if (g_hash_table_lookup (priv->lists, account)) {
138                 return;
139         }
140
141         empathy_debug (DEBUG_DOMAIN, "Adding new account: %s",
142                        mc_account_get_display_name (account));
143
144         list = empathy_tp_contact_list_new (account);
145         if (!list) {
146                 return;
147         }
148
149         g_hash_table_insert (priv->lists, g_object_ref (account), list);
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         g_signal_connect (list, "destroy",
162                           G_CALLBACK (contact_manager_destroy_cb),
163                           manager);
164 }
165
166 static void
167 contact_manager_status_changed_cb (MissionControl           *mc,
168                                    TpConnectionStatus        status,
169                                    McPresence                presence,
170                                    TpConnectionStatusReason  reason,
171                                    const gchar              *unique_name,
172                                    EmpathyContactManager    *manager)
173 {
174         McAccount *account;
175
176         if (status != TP_CONNECTION_STATUS_CONNECTED) {
177                 /* We only care about newly connected accounts */
178                 return;
179         }
180
181         account = mc_account_lookup (unique_name);
182         contact_manager_add_account (manager, account);
183         g_object_unref (account);
184 }
185
186 static void
187 contact_manager_finalize (GObject *object)
188 {
189         EmpathyContactManagerPriv *priv = GET_PRIV (object);
190
191         dbus_g_proxy_disconnect_signal (DBUS_G_PROXY (priv->mc),
192                                         "AccountStatusChanged",
193                                         G_CALLBACK (contact_manager_status_changed_cb),
194                                         object);
195
196         g_hash_table_foreach (priv->lists,
197                               contact_manager_disconnect_foreach,
198                               object);
199         g_hash_table_destroy (priv->lists);
200         g_object_unref (priv->mc);
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
210         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
211 }
212
213 static void
214 empathy_contact_manager_init (EmpathyContactManager *manager)
215 {
216         EmpathyContactManagerPriv *priv;
217         GSList                    *accounts, *l;
218
219         priv = GET_PRIV (manager);
220
221         priv->lists = g_hash_table_new_full (empathy_account_hash,
222                                              empathy_account_equal,
223                                              (GDestroyNotify) g_object_unref,
224                                              (GDestroyNotify) g_object_unref);
225
226         priv->mc = empathy_mission_control_new ();
227
228         empathy_connect_to_account_status_changed (priv->mc,
229                                                    G_CALLBACK (contact_manager_status_changed_cb),
230                                                    manager, NULL);
231
232         /* Get ContactList for existing connections */
233         accounts = mission_control_get_online_connections (priv->mc, NULL);
234         for (l = accounts; l; l = l->next) {
235                 contact_manager_add_account (manager, l->data);
236                 g_object_unref (l->data);
237         }
238         g_slist_free (accounts);
239 }
240
241 EmpathyContactManager *
242 empathy_contact_manager_new (void)
243 {
244         static EmpathyContactManager *manager = NULL;
245
246         if (!manager) {
247                 manager = g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
248                 g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager);
249         } else {
250                 g_object_ref (manager);
251         }
252
253         return manager;
254 }
255
256 EmpathyTpContactList *
257 empathy_contact_manager_get_list (EmpathyContactManager *manager,
258                                   McAccount             *account)
259 {
260         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
261
262         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
263         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
264
265         return g_hash_table_lookup (priv->lists, account);
266 }
267
268 static void
269 contact_manager_add (EmpathyContactList *manager,
270                      EmpathyContact     *contact,
271                      const gchar        *message)
272 {
273         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
274         EmpathyContactList        *list;
275         McAccount                 *account;
276
277         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
278
279         account = empathy_contact_get_account (contact);
280         list = g_hash_table_lookup (priv->lists, account);
281
282         if (list) {
283                 empathy_contact_list_add (list, contact, message);
284         }
285 }
286
287 static void
288 contact_manager_remove (EmpathyContactList *manager,
289                         EmpathyContact      *contact,
290                         const gchar        *message)
291 {
292         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
293         EmpathyContactList        *list;
294         McAccount                 *account;
295
296         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
297
298         account = empathy_contact_get_account (contact);
299         list = g_hash_table_lookup (priv->lists, account);
300
301         if (list) {
302                 empathy_contact_list_remove (list, contact, message);
303         }
304 }
305
306 static void
307 contact_manager_get_members_foreach (McAccount             *account,
308                                      EmpathyTpContactList  *list,
309                                      GList                **contacts)
310 {
311         GList *l;
312
313         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
314         *contacts = g_list_concat (*contacts, l);
315 }
316
317 static GList *
318 contact_manager_get_members (EmpathyContactList *manager)
319 {
320         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
321         GList                     *contacts = NULL;
322
323         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
324
325         g_hash_table_foreach (priv->lists,
326                               (GHFunc) contact_manager_get_members_foreach,
327                               &contacts);
328
329         return contacts;
330 }
331
332 static void
333 contact_manager_get_pendings_foreach (McAccount             *account,
334                                       EmpathyTpContactList  *list,
335                                       GList                **contacts)
336 {
337         GList *l;
338
339         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
340         *contacts = g_list_concat (*contacts, l);
341 }
342
343 static GList *
344 contact_manager_get_pendings (EmpathyContactList *manager)
345 {
346         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
347         GList                     *contacts = NULL;
348
349         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
350
351         g_hash_table_foreach (priv->lists,
352                               (GHFunc) contact_manager_get_pendings_foreach,
353                               &contacts);
354
355         return contacts;
356 }
357
358 static void
359 contact_manager_get_all_groups_foreach (McAccount             *account,
360                                         EmpathyTpContactList  *list,
361                                         GList                **all_groups)
362 {
363         GList *groups, *l;
364
365         groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list));
366         for (l = groups; l; l = l->next) {
367                 if (!g_list_find_custom (*all_groups,
368                                          l->data,
369                                          (GCompareFunc) strcmp)) {
370                         *all_groups = g_list_prepend (*all_groups, l->data);
371                 } else {
372                         g_free (l->data);
373                 }
374         }
375
376         g_list_free (groups);
377 }
378
379 static GList *
380 contact_manager_get_all_groups (EmpathyContactList *manager)
381 {
382         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
383         GList                     *groups = NULL;
384
385         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
386
387         g_hash_table_foreach (priv->lists,
388                               (GHFunc) contact_manager_get_all_groups_foreach,
389                               &groups);
390
391         return groups;
392 }
393
394 static GList *
395 contact_manager_get_groups (EmpathyContactList *manager,
396                             EmpathyContact     *contact)
397 {
398         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
399         EmpathyContactList        *list;
400         McAccount                 *account;
401
402         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
403
404         account = empathy_contact_get_account (contact);
405         list = g_hash_table_lookup (priv->lists, account);
406
407         if (list) {
408                 return empathy_contact_list_get_groups (list, contact);
409         }
410
411         return NULL;
412 }
413
414 static void
415 contact_manager_add_to_group (EmpathyContactList *manager,
416                               EmpathyContact     *contact,
417                               const gchar        *group)
418 {
419         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
420         EmpathyContactList        *list;
421         McAccount                 *account;
422
423         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
424
425         account = empathy_contact_get_account (contact);
426         list = g_hash_table_lookup (priv->lists, account);
427
428         if (list) {
429                 empathy_contact_list_add_to_group (list, contact, group);
430         }
431 }
432
433 static void
434 contact_manager_remove_from_group (EmpathyContactList *manager,
435                                    EmpathyContact     *contact,
436                                    const gchar        *group)
437 {
438         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
439         EmpathyContactList        *list;
440         McAccount                 *account;
441
442         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
443
444         account = empathy_contact_get_account (contact);
445         list = g_hash_table_lookup (priv->lists, account);
446
447         if (list) {
448                 empathy_contact_list_remove_from_group (list, contact, group);
449         }
450 }
451
452 typedef struct {
453         const gchar *old_group;
454         const gchar *new_group;
455 } RenameGroupData;
456
457 static void
458 contact_manager_rename_group_foreach (McAccount            *account,
459                                       EmpathyTpContactList *list,
460                                       RenameGroupData      *data)
461 {
462         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
463                                            data->old_group,
464                                            data->new_group);
465 }
466
467 static void
468 contact_manager_rename_group (EmpathyContactList *manager,
469                               const gchar        *old_group,
470                               const gchar        *new_group)
471 {
472         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
473         RenameGroupData            data;
474
475         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
476
477         data.old_group = old_group;
478         data.new_group = new_group;
479         g_hash_table_foreach (priv->lists,
480                               (GHFunc) contact_manager_rename_group_foreach,
481                               &data);
482 }
483
484 static void contact_manager_remove_group_foreach (McAccount     *account,
485                                                   EmpathyTpContactList *list,
486                                                   const gchar *group)
487 {
488         empathy_contact_list_remove_group (EMPATHY_CONTACT_LIST (list),
489                                            group);
490 }
491
492 static void
493 contact_manager_remove_group (EmpathyContactList *manager,
494                               const gchar *group)
495 {
496         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
497         
498         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
499
500         g_hash_table_foreach (priv->lists,
501                               (GHFunc) contact_manager_remove_group_foreach,
502                               (gpointer) group);
503 }
504
505 static void
506 contact_manager_iface_init (EmpathyContactListIface *iface)
507 {
508         iface->add               = contact_manager_add;
509         iface->remove            = contact_manager_remove;
510         iface->get_members       = contact_manager_get_members;
511         iface->get_pendings      = contact_manager_get_pendings;
512         iface->get_all_groups    = contact_manager_get_all_groups;
513         iface->get_groups        = contact_manager_get_groups;
514         iface->add_to_group      = contact_manager_add_to_group;
515         iface->remove_from_group = contact_manager_remove_from_group;
516         iface->rename_group      = contact_manager_rename_group;
517         iface->remove_group      = contact_manager_remove_group;
518 }
519