]> git.0d.be Git - empathy.git/blob - libempathy/empathy-connection-aggregator.c
UOA: Do not segfault when "Done" or "Cancel" button clicked but widget is not ready yet
[empathy.git] / libempathy / empathy-connection-aggregator.c
1 /*
2  * empathy-connection-aggregator.c - Source for EmpathyConnectionAggregator
3  * Copyright (C) 2010 Collabora Ltd.
4  * @author Cosimo Cecchi <cosimo.cecchi@collabora.co.uk>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20
21 #include "config.h"
22
23 #include "empathy-connection-aggregator.h"
24
25 #define DEBUG_FLAG EMPATHY_DEBUG_OTHER
26 #include "empathy-debug.h"
27
28 G_DEFINE_TYPE (EmpathyConnectionAggregator, empathy_connection_aggregator,
29     G_TYPE_OBJECT);
30
31 enum {
32   EVENT_CONTACT_LIST_CHANGED,
33   LAST_SIGNAL
34 };
35
36 static guint signals[LAST_SIGNAL];
37
38 struct _EmpathyConnectionAggregatorPriv {
39   TpAccountManager *mgr;
40
41   /* List of owned TpConnection */
42   GList *conns;
43 };
44
45 static void
46 empathy_connection_aggregator_dispose (GObject *object)
47 {
48   EmpathyConnectionAggregator *self = (EmpathyConnectionAggregator *) object;
49
50   g_clear_object (&self->priv->mgr);
51
52   g_list_free_full (self->priv->conns, g_object_unref);
53   self->priv->conns = NULL;
54
55   G_OBJECT_CLASS (empathy_connection_aggregator_parent_class)->dispose (object);
56 }
57
58 static void
59 empathy_connection_aggregator_class_init (
60     EmpathyConnectionAggregatorClass *klass)
61 {
62   GObjectClass *oclass = G_OBJECT_CLASS (klass);
63
64   oclass->dispose = empathy_connection_aggregator_dispose;
65
66   signals[EVENT_CONTACT_LIST_CHANGED] =
67     g_signal_new ("contact-list-changed",
68       G_TYPE_FROM_CLASS (klass),
69       G_SIGNAL_RUN_LAST,
70       0,
71       NULL, NULL,
72       g_cclosure_marshal_generic,
73       G_TYPE_NONE,
74       2, G_TYPE_PTR_ARRAY, G_TYPE_PTR_ARRAY);
75
76   g_type_class_add_private (klass, sizeof (EmpathyConnectionAggregatorPriv));
77 }
78
79 static void
80 contact_list_changed_cb (TpConnection *conn,
81     GPtrArray *added,
82     GPtrArray *removed,
83     EmpathyConnectionAggregator *self)
84 {
85   g_signal_emit (self, signals[EVENT_CONTACT_LIST_CHANGED], 0, added, removed);
86 }
87
88 static void
89 conn_invalidated_cb (TpConnection *conn,
90     guint domain,
91     gint code,
92     gchar *message,
93     EmpathyConnectionAggregator *self)
94 {
95   self->priv->conns = g_list_remove (self->priv->conns, conn);
96
97   g_object_unref (conn);
98 }
99
100 static void
101 check_connection (EmpathyConnectionAggregator *self,
102     TpConnection *conn)
103 {
104   GPtrArray *contacts;
105
106   if (g_list_find (self->priv->conns, conn) != NULL)
107     return;
108
109   self->priv->conns = g_list_prepend (self->priv->conns,
110       g_object_ref (conn));
111
112   tp_g_signal_connect_object (conn, "contact-list-changed",
113       G_CALLBACK (contact_list_changed_cb), self, 0);
114
115   contacts = tp_connection_dup_contact_list (conn);
116   if (contacts != NULL)
117     {
118       GPtrArray *empty;
119
120       empty = g_ptr_array_new ();
121
122       contact_list_changed_cb (conn, contacts, empty, self);
123       g_ptr_array_unref (empty);
124     }
125   g_ptr_array_unref (contacts);
126
127   tp_g_signal_connect_object (conn, "invalidated",
128       G_CALLBACK (conn_invalidated_cb), self, 0);
129 }
130
131 static void
132 check_account (EmpathyConnectionAggregator *self,
133     TpAccount *account)
134 {
135   TpConnection *conn;
136
137   conn = tp_account_get_connection (account);
138   if (conn != NULL)
139     check_connection (self, conn);
140 }
141
142 static void
143 account_conn_changed_cb (TpAccount *account,
144     GParamSpec *spec,
145     EmpathyConnectionAggregator *self)
146 {
147   check_account (self, account);
148 }
149
150 static void
151 add_account (EmpathyConnectionAggregator *self,
152     TpAccount *account)
153 {
154   check_account (self, account);
155
156   tp_g_signal_connect_object (account, "notify::connection",
157       G_CALLBACK (account_conn_changed_cb), self, 0);
158 }
159
160 static void
161 account_validity_changed_cb (TpAccountManager *manager,
162     TpAccount *account,
163     gboolean valid,
164     EmpathyConnectionAggregator *self)
165 {
166   if (valid)
167     add_account (self, account);
168 }
169
170 static void
171 am_prepare_cb (GObject *source,
172     GAsyncResult *result,
173     gpointer user_data)
174 {
175   EmpathyConnectionAggregator *self = EMPATHY_CONNECTION_AGGREGATOR (user_data);
176   GError *error = NULL;
177   GList *accounts, *l;
178
179   if (!tp_proxy_prepare_finish (source, result, &error))
180     {
181       DEBUG ("Failed to prepare account manager: %s", error->message);
182       g_error_free (error);
183       goto out;
184     }
185
186   accounts = tp_account_manager_dup_valid_accounts (self->priv->mgr);
187   for (l = accounts; l != NULL; l = g_list_next (l))
188     {
189       TpAccount *account = l->data;
190
191       add_account (self, account);
192     }
193
194   tp_g_signal_connect_object (self->priv->mgr, "account-validity-changed",
195       G_CALLBACK (account_validity_changed_cb), self, 0);
196
197   g_list_free_full (accounts, g_object_unref);
198
199 out:
200   g_object_unref (self);
201 }
202
203 static void
204 empathy_connection_aggregator_init (EmpathyConnectionAggregator *self)
205 {
206   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
207       EMPATHY_TYPE_CONNECTION_AGGREGATOR, EmpathyConnectionAggregatorPriv);
208
209   self->priv->mgr = tp_account_manager_dup ();
210
211   tp_proxy_prepare_async (self->priv->mgr, NULL, am_prepare_cb,
212       g_object_ref (self));
213 }
214
215 EmpathyConnectionAggregator *
216 empathy_connection_aggregator_dup_singleton (void)
217 {
218   static EmpathyConnectionAggregator *aggregator = NULL;
219
220   if (G_LIKELY (aggregator != NULL))
221       return g_object_ref (aggregator);
222
223   aggregator = g_object_new (EMPATHY_TYPE_CONNECTION_AGGREGATOR, NULL);
224
225   g_object_add_weak_pointer (G_OBJECT (aggregator), (gpointer *) &aggregator);
226   return aggregator;
227 }
228
229 /* (transfer container) */
230 GList *
231 empathy_connection_aggregator_get_all_groups (EmpathyConnectionAggregator *self)
232 {
233   GList *keys, *l;
234   GHashTable *set;
235
236   set = g_hash_table_new (g_str_hash, g_str_equal);
237
238   for (l = self->priv->conns; l != NULL; l = g_list_next (l))
239     {
240       TpConnection *conn = l->data;
241       const gchar * const *groups;
242       guint i;
243
244       groups = tp_connection_get_contact_groups (conn);
245       if (groups == NULL)
246         continue;
247
248       for (i = 0; groups[i] != NULL; i++)
249         g_hash_table_insert (set, (gchar *) groups[i], GUINT_TO_POINTER (TRUE));
250     }
251
252   keys = g_hash_table_get_keys (set);
253   g_hash_table_unref (set);
254
255   return keys;
256 }
257
258 GPtrArray *
259 empathy_connection_aggregator_dup_all_contacts (
260     EmpathyConnectionAggregator *self)
261 {
262   GPtrArray *result;
263   GList *l;
264
265   result = g_ptr_array_new_with_free_func (g_object_unref);
266
267   for (l = self->priv->conns; l != NULL; l = g_list_next (l))
268     {
269       TpConnection *conn = l->data;
270       GPtrArray *contacts;
271
272       contacts = tp_connection_dup_contact_list (conn);
273       if (contacts == NULL)
274         continue;
275
276       tp_g_ptr_array_extend (result, contacts);
277
278       /* tp_g_ptr_array_extend() doesn't give us an extra ref */
279       g_ptr_array_foreach (contacts, (GFunc) g_object_ref, NULL);
280
281       g_ptr_array_unref (contacts);
282     }
283
284   return result;
285 }
286
287 static void
288 rename_group_cb (GObject *source,
289     GAsyncResult *result,
290     gpointer user_data)
291 {
292   GError *error = NULL;
293
294   if (!tp_connection_rename_group_finish (TP_CONNECTION (source), result,
295         &error))
296     {
297       DEBUG ("Failed to rename group on %s: %s",
298           tp_proxy_get_object_path (source), error->message);
299       g_error_free (error);
300     }
301 }
302
303 void
304 empathy_connection_aggregator_rename_group (EmpathyConnectionAggregator *self,
305     const gchar *old_name,
306     const gchar *new_name)
307 {
308   GList *l;
309
310   for (l = self->priv->conns; l != NULL; l = g_list_next (l))
311     {
312       TpConnection *conn = l->data;
313       const gchar * const *groups;
314
315       groups = tp_connection_get_contact_groups (conn);
316
317       if (!tp_strv_contains (groups, old_name))
318         continue;
319
320       DEBUG ("Rename group '%s' to '%s' on %s", old_name, new_name,
321           tp_proxy_get_object_path (conn));
322
323       tp_connection_rename_group_async (conn, old_name, new_name,
324           rename_group_cb, NULL);
325     }
326 }