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