]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
[darcs-to-svn @ Remove EmpathySession and move all programs into src/]
[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-helpers.h>
28 #include <libtelepathy/tp-constants.h>
29
30 #include "empathy-contact-manager.h"
31 #include "gossip-utils.h"
32 #include "gossip-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         gboolean        setup;
43 };
44
45 typedef struct {
46         const gchar *old_group;
47         const gchar *new_group;
48 } ContactManagerRenameGroupData;
49
50 typedef struct {
51         GossipContact *contact;
52         const gchar   *id;
53 } ContactManagerFindData;
54
55 static void     empathy_contact_manager_class_init   (EmpathyContactManagerClass      *klass);
56 static void     empathy_contact_manager_init         (EmpathyContactManager           *manager);
57 static void     contact_manager_finalize             (GObject                         *object);
58 static void     contact_manager_setup_foreach        (McAccount                       *account,
59                                                       EmpathyContactList              *list,
60                                                       EmpathyContactManager           *manager);
61 static gboolean contact_manager_find_foreach         (McAccount                       *account,
62                                                       EmpathyContactList              *list,
63                                                       ContactManagerFindData          *data);
64 static void     contact_manager_add_account          (EmpathyContactManager           *manager,
65                                                       McAccount                       *account);
66 static void     contact_manager_added_cb             (EmpathyContactList              *list,
67                                                       GossipContact                   *contact,
68                                                       EmpathyContactManager           *manager);
69 static void     contact_manager_removed_cb           (EmpathyContactList              *list,
70                                                       GossipContact                   *contact,
71                                                       EmpathyContactManager           *manager);
72 static void     contact_manager_destroy_cb           (EmpathyContactList              *list,
73                                                       EmpathyContactManager           *manager);
74 static void     contact_manager_rename_group_foreach (McAccount                       *account,
75                                                       EmpathyContactList              *list,
76                                                       ContactManagerRenameGroupData   *data);
77 static void     contact_manager_get_groups_foreach   (McAccount                       *account,
78                                                       EmpathyContactList              *list,
79                                                       GList                          **all_groups);
80 static void     contact_manager_get_contacts_foreach (McAccount                       *account,
81                                                       EmpathyContactList              *list,
82                                                       GList                          **contacts);
83 static void     contact_manager_status_changed_cb    (MissionControl                  *mc,
84                                                       TelepathyConnectionStatus        status,
85                                                       McPresence                       presence,
86                                                       TelepathyConnectionStatusReason  reason,
87                                                       const gchar                     *unique_name,
88                                                       EmpathyContactManager           *manager);
89
90 enum {
91         CONTACT_ADDED,
92         CONTACT_REMOVED,
93         LAST_SIGNAL
94 };
95
96 static guint signals[LAST_SIGNAL];
97
98 G_DEFINE_TYPE (EmpathyContactManager, empathy_contact_manager, G_TYPE_OBJECT);
99
100 static void
101 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
102 {
103         GObjectClass *object_class = G_OBJECT_CLASS (klass);
104
105         object_class->finalize = contact_manager_finalize;
106
107         signals[CONTACT_ADDED] =
108                 g_signal_new ("contact-added",
109                               G_TYPE_FROM_CLASS (klass),
110                               G_SIGNAL_RUN_LAST,
111                               0,
112                               NULL, NULL,
113                               g_cclosure_marshal_VOID__OBJECT,
114                               G_TYPE_NONE,
115                               1, GOSSIP_TYPE_CONTACT);
116
117         signals[CONTACT_REMOVED] =
118                 g_signal_new ("contact-removed",
119                               G_TYPE_FROM_CLASS (klass),
120                               G_SIGNAL_RUN_LAST,
121                               0,
122                               NULL, NULL,
123                               g_cclosure_marshal_VOID__OBJECT,
124                               G_TYPE_NONE,
125                               1, GOSSIP_TYPE_CONTACT);
126
127         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
128 }
129
130 static void
131 empathy_contact_manager_init (EmpathyContactManager *manager)
132 {
133         EmpathyContactManagerPriv *priv;
134         GSList                    *accounts, *l;
135
136         priv = GET_PRIV (manager);
137
138         priv->lists = g_hash_table_new_full (gossip_account_hash,
139                                              gossip_account_equal,
140                                              (GDestroyNotify) g_object_unref,
141                                              (GDestroyNotify) g_object_unref);
142
143         priv->mc = mission_control_new (tp_get_bus ());
144
145         dbus_g_proxy_connect_signal (DBUS_G_PROXY (priv->mc),
146                                      "AccountStatusChanged",
147                                      G_CALLBACK (contact_manager_status_changed_cb),
148                                      manager, NULL);
149
150         /* Get ContactList for existing connections */
151         accounts = mission_control_get_online_connections (priv->mc, NULL);
152         for (l = accounts; l; l = l->next) {
153                 McAccount *account;
154
155                 account = l->data;
156                 contact_manager_add_account (manager, account);
157                 
158                 g_object_unref (account);
159         }
160         g_slist_free (accounts);
161 }
162
163 static void
164 contact_manager_finalize (GObject *object)
165 {
166         EmpathyContactManagerPriv *priv;
167
168         priv = GET_PRIV (object);
169
170         g_hash_table_destroy (priv->lists);
171         g_object_unref (priv->mc);
172 }
173
174 EmpathyContactManager *
175 empathy_contact_manager_new (void)
176 {
177         static EmpathyContactManager *manager = NULL;
178
179         if (!manager) {
180                 manager = g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
181                 g_object_add_weak_pointer (G_OBJECT (manager), (gpointer) &manager);
182         } else {
183                 g_object_ref (manager);
184         }
185
186         return manager;
187 }
188
189 void
190 empathy_contact_manager_setup (EmpathyContactManager *manager)
191 {
192         EmpathyContactManagerPriv *priv;
193
194         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
195
196         priv = GET_PRIV (manager);
197
198         if (priv->setup) {
199                 /* Already done */
200                 return;
201         }
202
203         g_hash_table_foreach (priv->lists,
204                               (GHFunc) contact_manager_setup_foreach,
205                               manager);
206
207         priv->setup = TRUE;
208 }
209
210 EmpathyContactList *
211 empathy_contact_manager_get_list (EmpathyContactManager *manager,
212                                   McAccount             *account)
213 {
214         EmpathyContactManagerPriv *priv;
215
216         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
217         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
218
219         priv = GET_PRIV (manager);
220
221         return g_hash_table_lookup (priv->lists, account);
222 }
223
224 GossipContact *
225 empathy_contact_manager_get_own (EmpathyContactManager *manager,
226                                  McAccount             *account)
227 {
228         EmpathyContactManagerPriv *priv;
229         EmpathyContactList        *list;
230
231         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
232         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
233
234         priv = GET_PRIV (manager);
235
236         list = g_hash_table_lookup (priv->lists, account);
237         
238         if (!list) {
239                 return NULL;
240         }
241
242         return empathy_contact_list_get_own (list);
243 }
244
245 GossipContact *
246 empathy_contact_manager_create (EmpathyContactManager *manager,
247                                 McAccount             *account,
248                                 const gchar           *id)
249 {
250         EmpathyContactManagerPriv *priv;
251         EmpathyContactList        *list;
252
253         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
254         g_return_val_if_fail (MC_IS_ACCOUNT (account), NULL);
255         g_return_val_if_fail (id != NULL, NULL);
256
257         priv = GET_PRIV (manager);
258
259         list = g_hash_table_lookup (priv->lists, account);
260         
261         if (!list) {
262                 return NULL;
263         }
264
265         return empathy_contact_list_get_from_id (list, id);
266 }
267
268 GossipContact *
269 empathy_contact_manager_find (EmpathyContactManager *manager,
270                               const gchar           *id)
271 {
272         EmpathyContactManagerPriv *priv;
273         ContactManagerFindData     data;
274
275         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
276         g_return_val_if_fail (id != NULL, NULL);
277
278         priv = GET_PRIV (manager);
279
280         data.contact = NULL;
281         data.id = id;
282
283         g_hash_table_find (priv->lists,
284                            (GHRFunc) contact_manager_find_foreach,
285                            &data);
286
287         return data.contact;
288 }
289
290 void
291 empathy_contact_manager_add (EmpathyContactManager *manager,
292                              GossipContact         *contact,
293                              const gchar           *message)
294 {
295         EmpathyContactManagerPriv *priv;
296         EmpathyContactList        *list;
297         McAccount                 *account;
298         guint                      handle;
299
300         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
301         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
302
303         priv = GET_PRIV (manager);
304
305         account = gossip_contact_get_account (contact);
306         handle = gossip_contact_get_handle (contact);
307         list = g_hash_table_lookup (priv->lists, account);
308
309         if (list) {
310                 empathy_contact_list_add (list, handle, message);
311         }
312 }
313
314 void
315 empathy_contact_manager_remove (EmpathyContactManager *manager,
316                                 GossipContact         *contact)
317 {
318         EmpathyContactManagerPriv *priv;
319         EmpathyContactList        *list;
320         McAccount                 *account;
321         guint                      handle;
322
323         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
324         g_return_if_fail (GOSSIP_IS_CONTACT (contact));
325
326         priv = GET_PRIV (manager);
327
328         account = gossip_contact_get_account (contact);
329         handle = gossip_contact_get_handle (contact);
330         list = g_hash_table_lookup (priv->lists, account);
331
332         if (list) {
333                 empathy_contact_list_remove (list, handle);
334         }
335 }
336
337 void
338 empathy_contact_manager_rename_group (EmpathyContactManager *manager,
339                                       const gchar           *old_group,
340                                       const gchar           *new_group)
341 {
342         EmpathyContactManagerPriv   *priv;
343         ContactManagerRenameGroupData  data;
344
345         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
346         g_return_if_fail (old_group != NULL);
347         g_return_if_fail (new_group != NULL);
348
349         priv = GET_PRIV (manager);
350
351         data.old_group = old_group;
352         data.new_group = new_group;
353
354         g_hash_table_foreach (priv->lists,
355                               (GHFunc) contact_manager_rename_group_foreach,
356                               &data);
357 }
358
359 GList *
360 empathy_contact_manager_get_groups (EmpathyContactManager *manager)
361 {
362         EmpathyContactManagerPriv *priv;
363         GList                     *groups = NULL;
364
365         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
366
367         priv = GET_PRIV (manager);
368
369         g_hash_table_foreach (priv->lists,
370                               (GHFunc) contact_manager_get_groups_foreach,
371                               &groups);
372
373         return groups;
374 }
375
376 GList *
377 empathy_contact_manager_get_contacts (EmpathyContactManager *manager)
378 {
379         EmpathyContactManagerPriv *priv;
380         GList                     *contacts = NULL;
381
382         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
383
384         priv = GET_PRIV (manager);
385
386         g_hash_table_foreach (priv->lists,
387                               (GHFunc) contact_manager_get_contacts_foreach,
388                               &contacts);
389
390         return contacts;
391 }
392
393 static void
394 contact_manager_setup_foreach (McAccount             *account,
395                                EmpathyContactList    *list,
396                                EmpathyContactManager *manager)
397 {
398         empathy_contact_list_setup (list);
399 }
400
401 static gboolean
402 contact_manager_find_foreach (McAccount              *account,
403                               EmpathyContactList     *list,
404                               ContactManagerFindData *data)
405 {
406         data->contact = empathy_contact_list_find (list, data->id);
407         
408         if (data->contact) {
409                 return TRUE;
410         }
411
412         return FALSE;
413 }
414
415 static void
416 contact_manager_add_account (EmpathyContactManager *manager,
417                              McAccount             *account)
418 {
419         EmpathyContactManagerPriv *priv;
420         EmpathyContactList        *list;
421
422         priv = GET_PRIV (manager);
423
424         if (g_hash_table_lookup (priv->lists, account)) {
425                 return;
426         }
427
428         gossip_debug (DEBUG_DOMAIN, "Adding new account: %s",
429                       mc_account_get_display_name (account));
430
431         list = empathy_contact_list_new (account);
432         if (!list) {
433                 return;
434         }
435
436         g_hash_table_insert (priv->lists, g_object_ref (account), list);
437
438         /* Connect signals */
439         g_signal_connect (list, "contact-added",
440                           G_CALLBACK (contact_manager_added_cb),
441                           manager);
442         g_signal_connect (list, "contact-removed",
443                           G_CALLBACK (contact_manager_removed_cb),
444                           manager);
445         g_signal_connect (list, "destroy",
446                           G_CALLBACK (contact_manager_destroy_cb),
447                           manager);
448
449         if (priv->setup) {
450                 empathy_contact_list_setup (list);
451         }
452 }
453
454 static void
455 contact_manager_added_cb (EmpathyContactList    *list,
456                           GossipContact         *contact,
457                           EmpathyContactManager *manager)
458 {
459         g_signal_emit (manager, signals[CONTACT_ADDED], 0, contact);
460 }
461
462 static void
463 contact_manager_removed_cb (EmpathyContactList    *list,
464                             GossipContact         *contact,
465                             EmpathyContactManager *manager)
466 {
467         g_signal_emit (manager, signals[CONTACT_REMOVED], 0, contact);
468 }
469
470 static void
471 contact_manager_destroy_cb (EmpathyContactList    *list,
472                             EmpathyContactManager *manager)
473 {
474         EmpathyContactManagerPriv *priv;
475         McAccount                 *account;
476
477         priv = GET_PRIV (manager);
478
479         account = empathy_contact_list_get_account (list);
480
481         gossip_debug (DEBUG_DOMAIN, "Removing account: %s",
482                       mc_account_get_display_name (account));
483
484         /* Disconnect signals from the list */
485         g_signal_handlers_disconnect_by_func (list,
486                                               contact_manager_added_cb,
487                                               manager);
488         g_signal_handlers_disconnect_by_func (list,
489                                               contact_manager_removed_cb,
490                                               manager);
491         g_signal_handlers_disconnect_by_func (list,
492                                               contact_manager_destroy_cb,
493                                               manager);
494
495         g_hash_table_remove (priv->lists, account);
496 }
497
498 static void
499 contact_manager_rename_group_foreach (McAccount                     *account,
500                                       EmpathyContactList            *list,
501                                       ContactManagerRenameGroupData *data)
502 {
503         empathy_contact_list_rename_group (list,
504                                            data->old_group,
505                                            data->new_group);
506 }
507
508 static void
509 contact_manager_get_groups_foreach (McAccount           *account,
510                                     EmpathyContactList  *list,
511                                     GList              **all_groups)
512 {
513         GList *groups, *l;
514
515         groups = empathy_contact_list_get_groups (list);
516         for (l = groups; l; l = l->next) {
517                 if (!g_list_find_custom (*all_groups,
518                                          l->data,
519                                          (GCompareFunc) strcmp)) {
520                         *all_groups = g_list_append (*all_groups,
521                                                      g_strdup (l->data));
522                 }
523                 g_free (l->data);
524         }
525
526         g_list_free (groups);
527 }
528
529 static void
530 contact_manager_get_contacts_foreach (McAccount           *account,
531                                       EmpathyContactList  *list,
532                                       GList              **contacts)
533 {
534         GList *l;
535
536         l = empathy_contact_list_get_contacts (list);
537         *contacts = g_list_concat (*contacts, l);
538 }
539
540 static void
541 contact_manager_status_changed_cb (MissionControl                  *mc,
542                                    TelepathyConnectionStatus        status,
543                                    McPresence                       presence,
544                                    TelepathyConnectionStatusReason  reason,
545                                    const gchar                     *unique_name,
546                                    EmpathyContactManager           *manager)
547 {
548         EmpathyContactManagerPriv *priv;
549         McAccount                 *account;
550
551         priv = GET_PRIV (manager);
552
553         if (status != TP_CONN_STATUS_CONNECTED) {
554                 /* We only care about newly connected accounts */
555                 return;
556         }
557
558         account = mc_account_lookup (unique_name);
559         contact_manager_add_account (manager, account);
560
561         g_object_unref (account);
562 }
563