]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
335131a2f70803f67b28405c2a48401c86358b0e
[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 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         DEBUG ("Removing account: %s", 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         DEBUG ("Adding new account: %s", mc_account_get_display_name (account));
142
143         list = empathy_tp_contact_list_new (account);
144         if (!list) {
145                 return;
146         }
147
148         g_hash_table_insert (priv->lists, g_object_ref (account), list);
149
150         /* Connect signals */
151         g_signal_connect (list, "members-changed",
152                           G_CALLBACK (contact_manager_members_changed_cb),
153                           manager);
154         g_signal_connect (list, "pendings-changed",
155                           G_CALLBACK (contact_manager_pendings_changed_cb),
156                           manager);
157         g_signal_connect (list, "groups-changed",
158                           G_CALLBACK (contact_manager_groups_changed_cb),
159                           manager);
160         g_signal_connect (list, "destroy",
161                           G_CALLBACK (contact_manager_destroy_cb),
162                           manager);
163 }
164
165 static void
166 contact_manager_connection_changed_cb (EmpathyAccountManager *account_manager,
167                                        McAccount *account,
168                                        TpConnectionStatusReason  reason,
169                                        TpConnectionStatus current,
170                                        TpConnectionStatus previous,
171                                        EmpathyContactManager *manager)
172 {
173         if (current != TP_CONNECTION_STATUS_CONNECTED) {
174                 /* We only care about newly connected accounts */
175                 return;
176         }
177
178         contact_manager_add_account (manager, account);
179 }
180
181 static void
182 contact_manager_finalize (GObject *object)
183 {
184         EmpathyContactManagerPriv *priv = GET_PRIV (object);
185
186         g_hash_table_foreach (priv->lists,
187                               contact_manager_disconnect_foreach,
188                               object);
189         g_hash_table_destroy (priv->lists);
190
191         g_signal_handlers_disconnect_by_func (priv->account_manager,
192                                               contact_manager_connection_changed_cb,
193                                               object);
194         g_object_unref (priv->account_manager);
195
196         if (priv->contact_monitor) {
197                 g_object_unref (priv->contact_monitor);
198         }
199 }
200
201 static GObject *
202 contact_manager_constructor (GType type,
203                              guint n_props,
204                              GObjectConstructParam *props)
205 {
206         GObject *retval;
207
208         if (manager_singleton) {
209                 retval = g_object_ref (manager_singleton);
210         } else {
211                 retval = G_OBJECT_CLASS (empathy_contact_manager_parent_class)->constructor
212                         (type, n_props, props);
213
214                 manager_singleton = EMPATHY_CONTACT_MANAGER (retval);
215                 g_object_add_weak_pointer (retval, (gpointer *) &manager_singleton);
216         }
217
218         return retval;
219 }
220
221 static void
222 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
223 {
224         GObjectClass *object_class = G_OBJECT_CLASS (klass);
225
226         object_class->finalize = contact_manager_finalize;
227         object_class->constructor = contact_manager_constructor;
228
229         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
230 }
231
232 static void
233 empathy_contact_manager_init (EmpathyContactManager *manager)
234 {
235         GSList                    *accounts, *l;
236         MissionControl            *mc;
237         EmpathyContactManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
238                 EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv);
239
240         manager->priv = priv;
241         priv->lists = g_hash_table_new_full (empathy_account_hash,
242                                              empathy_account_equal,
243                                              (GDestroyNotify) g_object_unref,
244                                              (GDestroyNotify) g_object_unref);
245         priv->account_manager = empathy_account_manager_dup_singleton ();
246         priv->contact_monitor = NULL;
247
248         g_signal_connect (priv->account_manager,
249                           "account-connection-changed",
250                           G_CALLBACK (contact_manager_connection_changed_cb), manager);
251
252         mc = empathy_mission_control_dup_singleton ();
253
254         /* Get ContactList for existing connections */
255         accounts = mission_control_get_online_connections (mc, NULL);
256         for (l = accounts; l; l = l->next) {
257                 contact_manager_add_account (manager, l->data);
258                 g_object_unref (l->data);
259         }
260
261         g_slist_free (accounts);
262         g_object_unref (mc);
263 }
264
265 EmpathyContactManager *
266 empathy_contact_manager_dup_singleton (void)
267 {
268         return g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
269 }
270
271 EmpathyTpContactList *
272 empathy_contact_manager_get_list (EmpathyContactManager *manager,
273                                   McAccount             *account)
274 {
275         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
276
277         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
278         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
279
280         return g_hash_table_lookup (priv->lists, account);
281 }
282
283 static void
284 contact_manager_add (EmpathyContactList *manager,
285                      EmpathyContact     *contact,
286                      const gchar        *message)
287 {
288         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
289         EmpathyContactList        *list;
290         McAccount                 *account;
291
292         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
293
294         account = empathy_contact_get_account (contact);
295         list = g_hash_table_lookup (priv->lists, account);
296
297         if (list) {
298                 empathy_contact_list_add (list, contact, message);
299         }
300 }
301
302 static void
303 contact_manager_remove (EmpathyContactList *manager,
304                         EmpathyContact      *contact,
305                         const gchar        *message)
306 {
307         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
308         EmpathyContactList        *list;
309         McAccount                 *account;
310
311         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
312
313         account = empathy_contact_get_account (contact);
314         list = g_hash_table_lookup (priv->lists, account);
315
316         if (list) {
317                 empathy_contact_list_remove (list, contact, message);
318         }
319 }
320
321 static void
322 contact_manager_get_members_foreach (McAccount             *account,
323                                      EmpathyTpContactList  *list,
324                                      GList                **contacts)
325 {
326         GList *l;
327
328         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
329         *contacts = g_list_concat (*contacts, l);
330 }
331
332 static GList *
333 contact_manager_get_members (EmpathyContactList *manager)
334 {
335         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
336         GList                     *contacts = NULL;
337
338         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
339
340         g_hash_table_foreach (priv->lists,
341                               (GHFunc) contact_manager_get_members_foreach,
342                               &contacts);
343
344         return contacts;
345 }
346
347 static EmpathyContactMonitor *
348 contact_manager_get_monitor (EmpathyContactList *manager)
349 {
350         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
351
352         if (priv->contact_monitor == NULL) {
353                 priv->contact_monitor = empathy_contact_monitor_new_for_iface (manager);
354         }
355
356         return priv->contact_monitor;
357 }
358
359 static void
360 contact_manager_get_pendings_foreach (McAccount             *account,
361                                       EmpathyTpContactList  *list,
362                                       GList                **contacts)
363 {
364         GList *l;
365
366         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
367         *contacts = g_list_concat (*contacts, l);
368 }
369
370 static GList *
371 contact_manager_get_pendings (EmpathyContactList *manager)
372 {
373         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
374         GList                     *contacts = NULL;
375
376         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
377
378         g_hash_table_foreach (priv->lists,
379                               (GHFunc) contact_manager_get_pendings_foreach,
380                               &contacts);
381
382         return contacts;
383 }
384
385 static void
386 contact_manager_get_all_groups_foreach (McAccount             *account,
387                                         EmpathyTpContactList  *list,
388                                         GList                **all_groups)
389 {
390         GList *groups, *l;
391
392         groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list));
393         for (l = groups; l; l = l->next) {
394                 if (!g_list_find_custom (*all_groups,
395                                          l->data,
396                                          (GCompareFunc) strcmp)) {
397                         *all_groups = g_list_prepend (*all_groups, l->data);
398                 } else {
399                         g_free (l->data);
400                 }
401         }
402
403         g_list_free (groups);
404 }
405
406 static GList *
407 contact_manager_get_all_groups (EmpathyContactList *manager)
408 {
409         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
410         GList                     *groups = NULL;
411
412         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
413
414         g_hash_table_foreach (priv->lists,
415                               (GHFunc) contact_manager_get_all_groups_foreach,
416                               &groups);
417
418         return groups;
419 }
420
421 static GList *
422 contact_manager_get_groups (EmpathyContactList *manager,
423                             EmpathyContact     *contact)
424 {
425         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
426         EmpathyContactList        *list;
427         McAccount                 *account;
428
429         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
430
431         account = empathy_contact_get_account (contact);
432         list = g_hash_table_lookup (priv->lists, account);
433
434         if (list) {
435                 return empathy_contact_list_get_groups (list, contact);
436         }
437
438         return NULL;
439 }
440
441 static void
442 contact_manager_add_to_group (EmpathyContactList *manager,
443                               EmpathyContact     *contact,
444                               const gchar        *group)
445 {
446         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
447         EmpathyContactList        *list;
448         McAccount                 *account;
449
450         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
451
452         account = empathy_contact_get_account (contact);
453         list = g_hash_table_lookup (priv->lists, account);
454
455         if (list) {
456                 empathy_contact_list_add_to_group (list, contact, group);
457         }
458 }
459
460 static void
461 contact_manager_remove_from_group (EmpathyContactList *manager,
462                                    EmpathyContact     *contact,
463                                    const gchar        *group)
464 {
465         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
466         EmpathyContactList        *list;
467         McAccount                 *account;
468
469         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
470
471         account = empathy_contact_get_account (contact);
472         list = g_hash_table_lookup (priv->lists, account);
473
474         if (list) {
475                 empathy_contact_list_remove_from_group (list, contact, group);
476         }
477 }
478
479 typedef struct {
480         const gchar *old_group;
481         const gchar *new_group;
482 } RenameGroupData;
483
484 static void
485 contact_manager_rename_group_foreach (McAccount            *account,
486                                       EmpathyTpContactList *list,
487                                       RenameGroupData      *data)
488 {
489         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
490                                            data->old_group,
491                                            data->new_group);
492 }
493
494 static void
495 contact_manager_rename_group (EmpathyContactList *manager,
496                               const gchar        *old_group,
497                               const gchar        *new_group)
498 {
499         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
500         RenameGroupData            data;
501
502         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
503
504         data.old_group = old_group;
505         data.new_group = new_group;
506         g_hash_table_foreach (priv->lists,
507                               (GHFunc) contact_manager_rename_group_foreach,
508                               &data);
509 }
510
511 static void contact_manager_remove_group_foreach (McAccount     *account,
512                                                   EmpathyTpContactList *list,
513                                                   const gchar *group)
514 {
515         empathy_contact_list_remove_group (EMPATHY_CONTACT_LIST (list),
516                                            group);
517 }
518
519 static void
520 contact_manager_remove_group (EmpathyContactList *manager,
521                               const gchar *group)
522 {
523         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
524         
525         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
526
527         g_hash_table_foreach (priv->lists,
528                               (GHFunc) contact_manager_remove_group_foreach,
529                               (gpointer) group);
530 }
531
532 static void
533 contact_manager_iface_init (EmpathyContactListIface *iface)
534 {
535         iface->add               = contact_manager_add;
536         iface->remove            = contact_manager_remove;
537         iface->get_members       = contact_manager_get_members;
538         iface->get_monitor       = contact_manager_get_monitor;
539         iface->get_pendings      = contact_manager_get_pendings;
540         iface->get_all_groups    = contact_manager_get_all_groups;
541         iface->get_groups        = contact_manager_get_groups;
542         iface->add_to_group      = contact_manager_add_to_group;
543         iface->remove_from_group = contact_manager_remove_from_group;
544         iface->rename_group      = contact_manager_rename_group;
545         iface->remove_group      = contact_manager_remove_group;
546 }
547
548 gboolean
549 empathy_contact_manager_can_add (EmpathyContactManager *manager,
550                                  McAccount             *account)
551 {
552         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
553         EmpathyTpContactList      *list;
554         
555         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), FALSE);
556
557         list = g_hash_table_lookup (priv->lists, account);
558         if (list == NULL)
559                 return FALSE;
560
561         return empathy_tp_contact_list_can_add (list);
562 }
563