]> git.0d.be Git - empathy.git/blob - libempathy/empathy-contact-manager.c
Updated Polish translation
[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/account-manager.h>
27 #include <telepathy-glib/enums.h>
28
29 #include "empathy-contact-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         TpAccountManager *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
90 contact_manager_invalidated_cb (TpProxy *connection,
91                                 guint    domain,
92                                 gint     code,
93                                 gchar   *message,
94                                 EmpathyContactManager *manager)
95 {
96         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
97         EmpathyTpContactList *list;
98
99         DEBUG ("Removing connection: %s (%s)",
100                 tp_proxy_get_object_path (TP_PROXY (connection)),
101                 message);
102
103         list = g_hash_table_lookup (priv->lists, connection);
104         if (list) {
105                 empathy_tp_contact_list_remove_all (list);
106                 g_hash_table_remove (priv->lists, connection);
107         }
108 }
109
110 static void
111 contact_manager_disconnect_foreach (gpointer key,
112                                     gpointer value,
113                                     gpointer user_data)
114 {
115         TpConnection *connection = key;
116         EmpathyTpContactList  *list = value;
117         EmpathyContactManager *manager = user_data;
118
119         /* Disconnect signals from the list */
120         g_signal_handlers_disconnect_by_func (list,
121                                               contact_manager_members_changed_cb,
122                                               manager);
123         g_signal_handlers_disconnect_by_func (list,
124                                               contact_manager_pendings_changed_cb,
125                                               manager);
126         g_signal_handlers_disconnect_by_func (list,
127                                               contact_manager_groups_changed_cb,
128                                               manager);
129         g_signal_handlers_disconnect_by_func (connection,
130                                               contact_manager_invalidated_cb,
131                                               manager);
132 }
133
134 static void
135 contact_manager_status_changed_cb (TpAccount *account,
136                                    guint old_status,
137                                    guint new_status,
138                                    guint reason,
139                                    gchar *dbus_error_name,
140                                    GHashTable *details,
141                                    EmpathyContactManager *self)
142 {
143         EmpathyContactManagerPriv *priv = GET_PRIV (self);
144         EmpathyTpContactList      *list;
145         TpConnection              *connection;
146
147         if (new_status == TP_CONNECTION_STATUS_DISCONNECTED)
148                 /* No point to start tracking a connection which is about to die */
149                 return;
150
151         connection = tp_account_get_connection (account);
152
153         if (connection == NULL || g_hash_table_lookup (priv->lists, connection)) {
154                 return;
155         }
156
157         DEBUG ("Adding new connection: %s",
158                 tp_proxy_get_object_path (TP_PROXY (connection)));
159
160         list = empathy_tp_contact_list_new (connection);
161         g_hash_table_insert (priv->lists, g_object_ref (connection), list);
162         g_signal_connect (connection, "invalidated",
163                           G_CALLBACK (contact_manager_invalidated_cb),
164                           self);
165
166         /* Connect signals */
167         g_signal_connect (list, "members-changed",
168                           G_CALLBACK (contact_manager_members_changed_cb),
169                           self);
170         g_signal_connect (list, "pendings-changed",
171                           G_CALLBACK (contact_manager_pendings_changed_cb),
172                           self);
173         g_signal_connect (list, "groups-changed",
174                           G_CALLBACK (contact_manager_groups_changed_cb),
175                           self);
176 }
177
178 static void
179 contact_manager_validity_changed_cb (TpAccountManager *account_manager,
180                                      TpAccount *account,
181                                      gboolean valid,
182                                      EmpathyContactManager *manager)
183 {
184         if (valid) {
185                 empathy_signal_connect_weak (account, "status-changed",
186                             G_CALLBACK (contact_manager_status_changed_cb),
187                             G_OBJECT (manager));
188         }
189 }
190
191 static void
192 contact_manager_finalize (GObject *object)
193 {
194         EmpathyContactManagerPriv *priv = GET_PRIV (object);
195
196         g_hash_table_foreach (priv->lists,
197                               contact_manager_disconnect_foreach,
198                               object);
199         g_hash_table_destroy (priv->lists);
200
201         g_object_unref (priv->account_manager);
202
203         if (priv->contact_monitor) {
204                 g_object_unref (priv->contact_monitor);
205         }
206 }
207
208 static GObject *
209 contact_manager_constructor (GType type,
210                              guint n_props,
211                              GObjectConstructParam *props)
212 {
213         GObject *retval;
214
215         if (manager_singleton) {
216                 retval = g_object_ref (manager_singleton);
217         } else {
218                 retval = G_OBJECT_CLASS (empathy_contact_manager_parent_class)->constructor
219                         (type, n_props, props);
220
221                 manager_singleton = EMPATHY_CONTACT_MANAGER (retval);
222                 g_object_add_weak_pointer (retval, (gpointer) &manager_singleton);
223         }
224
225         return retval;
226 }
227
228 /**
229  * empathy_contact_manager_initialized:
230  *
231  * Reports whether or not the singleton has already been created.
232  *
233  * There can be instances where you want to access the #EmpathyContactManager
234  * only if it has been set up for this process.
235  *
236  * Returns: %TRUE if the #EmpathyContactManager singleton has previously
237  * been initialized.
238  */
239 gboolean
240 empathy_contact_manager_initialized (void)
241 {
242         return (manager_singleton != NULL);
243 }
244
245 static void
246 empathy_contact_manager_class_init (EmpathyContactManagerClass *klass)
247 {
248         GObjectClass *object_class = G_OBJECT_CLASS (klass);
249
250         object_class->finalize = contact_manager_finalize;
251         object_class->constructor = contact_manager_constructor;
252
253         g_type_class_add_private (object_class, sizeof (EmpathyContactManagerPriv));
254 }
255
256 static void
257 account_manager_prepared_cb (GObject *source_object,
258                              GAsyncResult *result,
259                              gpointer user_data)
260 {
261         GList *accounts, *l;
262         EmpathyContactManager *manager = user_data;
263         TpAccountManager *account_manager = TP_ACCOUNT_MANAGER (source_object);
264         GError *error = NULL;
265
266         if (!tp_account_manager_prepare_finish (account_manager, result, &error)) {
267                 DEBUG ("Failed to prepare account manager: %s", error->message);
268                 g_error_free (error);
269                 return;
270         }
271
272         accounts = tp_account_manager_get_valid_accounts (account_manager);
273
274         for (l = accounts; l != NULL; l = l->next) {
275                 TpAccount *account = l->data;
276                 TpConnection *conn = tp_account_get_connection (account);
277
278                 if (conn != NULL) {
279                         contact_manager_status_changed_cb (account, 0, 0, 0,
280                                                            NULL, NULL, manager);
281                 }
282
283                 empathy_signal_connect_weak (account, "status-changed",
284                     G_CALLBACK (contact_manager_status_changed_cb),
285                     G_OBJECT (manager));
286         }
287         g_list_free (accounts);
288
289         empathy_signal_connect_weak (account_manager, "account-validity-changed",
290                              G_CALLBACK (contact_manager_validity_changed_cb),
291                              G_OBJECT (manager));
292 }
293
294 static void
295 empathy_contact_manager_init (EmpathyContactManager *manager)
296 {
297         EmpathyContactManagerPriv *priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
298                 EMPATHY_TYPE_CONTACT_MANAGER, EmpathyContactManagerPriv);
299
300         manager->priv = priv;
301         priv->lists = g_hash_table_new_full (empathy_proxy_hash,
302                                              empathy_proxy_equal,
303                                              (GDestroyNotify) g_object_unref,
304                                              (GDestroyNotify) g_object_unref);
305         priv->account_manager = tp_account_manager_dup ();
306         priv->contact_monitor = NULL;
307
308         tp_account_manager_prepare_async (priv->account_manager, NULL,
309             account_manager_prepared_cb, manager);
310 }
311
312 EmpathyContactManager *
313 empathy_contact_manager_dup_singleton (void)
314 {
315         return g_object_new (EMPATHY_TYPE_CONTACT_MANAGER, NULL);
316 }
317
318 EmpathyTpContactList *
319 empathy_contact_manager_get_list (EmpathyContactManager *manager,
320                                   TpConnection          *connection)
321 {
322         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
323
324         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
325         g_return_val_if_fail (TP_IS_CONNECTION (connection), NULL);
326
327         return g_hash_table_lookup (priv->lists, connection);
328 }
329
330 static void
331 contact_manager_add (EmpathyContactList *manager,
332                      EmpathyContact     *contact,
333                      const gchar        *message)
334 {
335         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
336         EmpathyContactList        *list;
337         TpConnection              *connection;
338
339         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
340
341         connection = empathy_contact_get_connection (contact);
342         list = g_hash_table_lookup (priv->lists, connection);
343
344         if (list) {
345                 empathy_contact_list_add (list, contact, message);
346         }
347 }
348
349 static void
350 contact_manager_remove (EmpathyContactList *manager,
351                         EmpathyContact     *contact,
352                         const gchar        *message)
353 {
354         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
355         EmpathyContactList        *list;
356         TpConnection              *connection;
357
358         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
359
360         connection = empathy_contact_get_connection (contact);
361         list = g_hash_table_lookup (priv->lists, connection);
362
363         if (list) {
364                 empathy_contact_list_remove (list, contact, message);
365         }
366 }
367
368 static void
369 contact_manager_get_members_foreach (TpConnection          *connection,
370                                      EmpathyTpContactList  *list,
371                                      GList                **contacts)
372 {
373         GList *l;
374
375         l = empathy_contact_list_get_members (EMPATHY_CONTACT_LIST (list));
376         *contacts = g_list_concat (*contacts, l);
377 }
378
379 static GList *
380 contact_manager_get_members (EmpathyContactList *manager)
381 {
382         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
383         GList                     *contacts = NULL;
384
385         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
386
387         g_hash_table_foreach (priv->lists,
388                               (GHFunc) contact_manager_get_members_foreach,
389                               &contacts);
390
391         return contacts;
392 }
393
394 static EmpathyContactMonitor *
395 contact_manager_get_monitor (EmpathyContactList *manager)
396 {
397         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
398
399         if (priv->contact_monitor == NULL) {
400                 priv->contact_monitor = empathy_contact_monitor_new_for_iface (manager);
401         }
402
403         return priv->contact_monitor;
404 }
405
406 static void
407 contact_manager_get_pendings_foreach (TpConnection          *connection,
408                                       EmpathyTpContactList  *list,
409                                       GList                **contacts)
410 {
411         GList *l;
412
413         l = empathy_contact_list_get_pendings (EMPATHY_CONTACT_LIST (list));
414         *contacts = g_list_concat (*contacts, l);
415 }
416
417 static GList *
418 contact_manager_get_pendings (EmpathyContactList *manager)
419 {
420         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
421         GList                     *contacts = NULL;
422
423         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
424
425         g_hash_table_foreach (priv->lists,
426                               (GHFunc) contact_manager_get_pendings_foreach,
427                               &contacts);
428
429         return contacts;
430 }
431
432 static void
433 contact_manager_get_all_groups_foreach (TpConnection          *connection,
434                                         EmpathyTpContactList  *list,
435                                         GList                **all_groups)
436 {
437         GList *groups, *l;
438
439         groups = empathy_contact_list_get_all_groups (EMPATHY_CONTACT_LIST (list));
440         for (l = groups; l; l = l->next) {
441                 if (!g_list_find_custom (*all_groups,
442                                          l->data,
443                                          (GCompareFunc) strcmp)) {
444                         *all_groups = g_list_prepend (*all_groups, l->data);
445                 } else {
446                         g_free (l->data);
447                 }
448         }
449
450         g_list_free (groups);
451 }
452
453 static GList *
454 contact_manager_get_all_groups (EmpathyContactList *manager)
455 {
456         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
457         GList                     *groups = NULL;
458
459         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
460
461         g_hash_table_foreach (priv->lists,
462                               (GHFunc) contact_manager_get_all_groups_foreach,
463                               &groups);
464
465         return groups;
466 }
467
468 static GList *
469 contact_manager_get_groups (EmpathyContactList *manager,
470                             EmpathyContact     *contact)
471 {
472         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
473         EmpathyContactList        *list;
474         TpConnection              *connection;
475
476         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), NULL);
477
478         connection = empathy_contact_get_connection (contact);
479         list = g_hash_table_lookup (priv->lists, connection);
480
481         if (list) {
482                 return empathy_contact_list_get_groups (list, contact);
483         }
484
485         return NULL;
486 }
487
488 static void
489 contact_manager_add_to_group (EmpathyContactList *manager,
490                               EmpathyContact     *contact,
491                               const gchar        *group)
492 {
493         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
494         EmpathyContactList        *list;
495         TpConnection              *connection;
496
497         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
498
499         connection = empathy_contact_get_connection (contact);
500         list = g_hash_table_lookup (priv->lists, connection);
501
502         if (list) {
503                 empathy_contact_list_add_to_group (list, contact, group);
504         }
505 }
506
507 static void
508 contact_manager_remove_from_group (EmpathyContactList *manager,
509                                    EmpathyContact     *contact,
510                                    const gchar        *group)
511 {
512         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
513         EmpathyContactList        *list;
514         TpConnection              *connection;
515
516         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
517
518         connection = empathy_contact_get_connection (contact);
519         list = g_hash_table_lookup (priv->lists, connection);
520
521         if (list) {
522                 empathy_contact_list_remove_from_group (list, contact, group);
523         }
524 }
525
526 typedef struct {
527         const gchar *old_group;
528         const gchar *new_group;
529 } RenameGroupData;
530
531 static void
532 contact_manager_rename_group_foreach (TpConnection         *connection,
533                                       EmpathyTpContactList *list,
534                                       RenameGroupData      *data)
535 {
536         empathy_contact_list_rename_group (EMPATHY_CONTACT_LIST (list),
537                                            data->old_group,
538                                            data->new_group);
539 }
540
541 static void
542 contact_manager_rename_group (EmpathyContactList *manager,
543                               const gchar        *old_group,
544                               const gchar        *new_group)
545 {
546         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
547         RenameGroupData            data;
548
549         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
550
551         data.old_group = old_group;
552         data.new_group = new_group;
553         g_hash_table_foreach (priv->lists,
554                               (GHFunc) contact_manager_rename_group_foreach,
555                               &data);
556 }
557
558 static void contact_manager_remove_group_foreach (TpConnection         *connection,
559                                                   EmpathyTpContactList *list,
560                                                   const gchar *group)
561 {
562         empathy_contact_list_remove_group (EMPATHY_CONTACT_LIST (list),
563                                            group);
564 }
565
566 static void
567 contact_manager_remove_group (EmpathyContactList *manager,
568                               const gchar *group)
569 {
570         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
571
572         g_return_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager));
573
574         g_hash_table_foreach (priv->lists,
575                               (GHFunc) contact_manager_remove_group_foreach,
576                               (gpointer) group);
577 }
578
579 static void
580 contact_manager_iface_init (EmpathyContactListIface *iface)
581 {
582         iface->add               = contact_manager_add;
583         iface->remove            = contact_manager_remove;
584         iface->get_members       = contact_manager_get_members;
585         iface->get_monitor       = contact_manager_get_monitor;
586         iface->get_pendings      = contact_manager_get_pendings;
587         iface->get_all_groups    = contact_manager_get_all_groups;
588         iface->get_groups        = contact_manager_get_groups;
589         iface->add_to_group      = contact_manager_add_to_group;
590         iface->remove_from_group = contact_manager_remove_from_group;
591         iface->rename_group      = contact_manager_rename_group;
592         iface->remove_group      = contact_manager_remove_group;
593 }
594
595 EmpathyContactListFlags
596 empathy_contact_manager_get_flags_for_connection (
597                                 EmpathyContactManager *manager,
598                                 TpConnection          *connection)
599 {
600         EmpathyContactManagerPriv *priv = GET_PRIV (manager);
601         EmpathyContactList        *list;
602         EmpathyContactListFlags    flags;
603
604         g_return_val_if_fail (EMPATHY_IS_CONTACT_MANAGER (manager), FALSE);
605         g_return_val_if_fail (connection != NULL, FALSE);
606
607         list = g_hash_table_lookup (priv->lists, connection);
608         if (list == NULL) {
609                 return FALSE;
610         }
611         flags = empathy_contact_list_get_flags (list);
612
613         return flags;
614 }
615