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