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