]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-individual-store-channel.c
Merge remote-tracking branch 'jonny/ft'
[empathy.git] / libempathy-gtk / empathy-individual-store-channel.c
1 /*
2  * Copyright (C) 2005-2007 Imendio AB
3  * Copyright (C) 2007-2011 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., 51 Franklin St, Fifth Floor,
18  * Boston, MA  02110-1301  USA
19  *
20  * Authors: Mikael Hallendal <micke@imendio.com>
21  *          Martyn Russell <martyn@imendio.com>
22  *          Xavier Claessens <xclaesse@gmail.com>
23  *          Travis Reitter <travis.reitter@collabora.co.uk>
24  */
25
26 #include "config.h"
27
28 #include <string.h>
29
30 #include <glib.h>
31 #include <glib/gi18n-lib.h>
32 #include <gtk/gtk.h>
33
34 #include <folks/folks.h>
35 #include <folks/folks-telepathy.h>
36 #include <telepathy-glib/util.h>
37
38 #include <libempathy/empathy-utils.h>
39 #include <libempathy/empathy-enum-types.h>
40
41 #include "empathy-individual-store-channel.h"
42
43 #include "empathy-ui-utils.h"
44 #include "empathy-gtk-enum-types.h"
45
46 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
47 #include <libempathy/empathy-debug.h>
48
49 struct _EmpathyIndividualStoreChannelPriv
50 {
51   TpChannel *channel;
52
53   /* TpContact => FolksIndividual
54    * We keep the individuals we have added to the store so can easily remove
55    * them when their TpContact leaves the channel. */
56   GHashTable *individuals;
57 };
58
59 enum
60 {
61   PROP_0,
62   PROP_CHANNEL,
63 };
64
65
66 G_DEFINE_TYPE (EmpathyIndividualStoreChannel, empathy_individual_store_channel,
67     EMPATHY_TYPE_INDIVIDUAL_STORE);
68
69 static void
70 add_members (EmpathyIndividualStoreChannel *self,
71     GPtrArray *members)
72 {
73   EmpathyIndividualStore *store = (EmpathyIndividualStore *) self;
74   guint i;
75
76   for (i = 0; i < members->len; i++)
77     {
78       TpContact *contact = g_ptr_array_index (members, i);
79       FolksIndividual *individual;
80
81       if (g_hash_table_lookup (self->priv->individuals, contact) != NULL)
82         continue;
83
84       individual = empathy_create_individual_from_tp_contact (contact);
85
86       DEBUG ("%s joined channel %s", tp_contact_get_identifier (contact),
87           tp_proxy_get_object_path (self->priv->channel));
88
89       individual_store_add_individual_and_connect (store, individual);
90
91       /* Pass the individual reference to the hash table */
92       g_hash_table_insert (self->priv->individuals, g_object_ref (contact),
93           individual);
94     }
95 }
96
97 static void
98 remove_members (EmpathyIndividualStoreChannel *self,
99     GPtrArray *members)
100 {
101   EmpathyIndividualStore *store = (EmpathyIndividualStore *) self;
102   guint i;
103
104   for (i = 0; i < members->len; i++)
105     {
106       TpContact *contact = g_ptr_array_index (members, i);
107       FolksIndividual *individual;
108
109       individual = g_hash_table_lookup (self->priv->individuals, contact);
110       if (individual == NULL)
111         continue;
112
113       DEBUG ("%s left channel %s", tp_contact_get_identifier (contact),
114           tp_proxy_get_object_path (self->priv->channel));
115
116       individual_store_remove_individual_and_disconnect (store, individual);
117
118       g_hash_table_remove (self->priv->individuals, contact);
119     }
120 }
121
122 static void
123 group_contacts_changed_cb (TpChannel *channel,
124     GPtrArray *added,
125     GPtrArray *removed,
126     GPtrArray *local_pending,
127     GPtrArray *remote_pending,
128     TpContact *actor,
129     GHashTable *details,
130     gpointer user_data)
131 {
132   EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
133       user_data);
134
135   remove_members (self, removed);
136   add_members (self, added);
137 }
138
139 static void
140 channel_prepare_cb (GObject *source,
141     GAsyncResult *result,
142     gpointer user_data)
143 {
144   EmpathyIndividualStoreChannel *self = user_data;
145   TpChannel *channel = (TpChannel *) source;
146   GError *error = NULL;
147   GPtrArray *members;
148
149   if (!tp_proxy_prepare_finish (source, result, &error))
150     {
151       DEBUG ("Failed to prepare %s: %s", tp_proxy_get_object_path (source),
152           error->message);
153
154       g_error_free (error);
155     }
156
157   /* Add initial members */
158   members = tp_channel_group_dup_members_contacts (channel);
159   if (members != NULL)
160     {
161       add_members (self, members);
162       g_ptr_array_unref (members);
163     }
164
165   tp_g_signal_connect_object (channel, "group-contacts-changed",
166       G_CALLBACK (group_contacts_changed_cb), self, 0);
167 }
168
169 static void
170 individual_store_channel_set_individual_channel (
171     EmpathyIndividualStoreChannel *self,
172     TpChannel *channel)
173 {
174   GQuark features[] = { TP_CHANNEL_FEATURE_CONTACTS, 0 };
175
176   g_assert (self->priv->channel == NULL); /* construct only */
177   self->priv->channel = g_object_ref (channel);
178
179   tp_proxy_prepare_async (channel, features, channel_prepare_cb, self);
180 }
181
182 static void
183 individual_store_channel_dispose (GObject *object)
184 {
185   EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
186       object);
187   EmpathyIndividualStore *store = EMPATHY_INDIVIDUAL_STORE (object);
188   GHashTableIter iter;
189   gpointer v;
190
191   g_hash_table_iter_init (&iter, self->priv->individuals);
192   while (g_hash_table_iter_next (&iter, NULL, &v))
193     {
194       FolksIndividual *individual = v;
195
196       empathy_individual_store_disconnect_individual (store, individual);
197     }
198
199   tp_clear_pointer (&self->priv->individuals, g_hash_table_unref);
200   g_clear_object (&self->priv->channel);
201
202   G_OBJECT_CLASS (empathy_individual_store_channel_parent_class)->dispose (
203       object);
204 }
205
206 static void
207 individual_store_channel_get_property (GObject *object,
208     guint param_id,
209     GValue *value,
210     GParamSpec *pspec)
211 {
212   EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
213       object);
214
215   switch (param_id)
216     {
217     case PROP_CHANNEL:
218       g_value_set_object (value, self->priv->channel);
219       break;
220     default:
221       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
222       break;
223     };
224 }
225
226 static void
227 individual_store_channel_set_property (GObject *object,
228     guint param_id,
229     const GValue *value,
230     GParamSpec *pspec)
231 {
232   switch (param_id)
233     {
234     case PROP_CHANNEL:
235       individual_store_channel_set_individual_channel (
236           EMPATHY_INDIVIDUAL_STORE_CHANNEL (object),
237           g_value_get_object (value));
238       break;
239     default:
240       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
241       break;
242     };
243 }
244
245 static void
246 individual_store_channel_reload_individuals (EmpathyIndividualStore *store)
247 {
248   EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
249       store);
250   GPtrArray *members;
251   GList *list, *l;
252
253   /* remove all. The list returned by g_hash_table_get_keys() is valid until
254    * the hash table is modified so we can't remove the contact directly in the
255    * iteration. */
256   members = g_ptr_array_new_with_free_func (g_object_unref);
257
258   list = g_hash_table_get_keys (self->priv->individuals);
259   for (l = list; l != NULL; l = g_list_next (l))
260     {
261       g_ptr_array_add (members, g_object_ref (l->data));
262     }
263
264   remove_members (self, members);
265
266   g_list_free (list);
267   g_ptr_array_unref (members);
268
269   /* re-add members */
270   members = tp_channel_group_dup_members_contacts (self->priv->channel);
271   if (members == NULL)
272     return;
273
274   add_members (self, members);
275   g_ptr_array_unref (members);
276 }
277
278 static gboolean
279 individual_store_channel_initial_loading (EmpathyIndividualStore *store)
280 {
281   EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
282       store);
283
284   return !tp_proxy_is_prepared (self->priv->channel,
285       TP_CHANNEL_FEATURE_CONTACTS);
286 }
287
288 static void
289 empathy_individual_store_channel_class_init (
290     EmpathyIndividualStoreChannelClass *klass)
291 {
292   GObjectClass *object_class = G_OBJECT_CLASS (klass);
293   EmpathyIndividualStoreClass *store_class = EMPATHY_INDIVIDUAL_STORE_CLASS (
294       klass);
295
296   object_class->dispose = individual_store_channel_dispose;
297   object_class->get_property = individual_store_channel_get_property;
298   object_class->set_property = individual_store_channel_set_property;
299
300   store_class->reload_individuals = individual_store_channel_reload_individuals;
301   store_class->initial_loading = individual_store_channel_initial_loading;
302
303   g_object_class_install_property (object_class,
304       PROP_CHANNEL,
305       g_param_spec_object ("individual-channel",
306           "Individual channel",
307           "Individual channel",
308           TP_TYPE_CHANNEL,
309           G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
310
311   g_type_class_add_private (object_class,
312       sizeof (EmpathyIndividualStoreChannelPriv));
313 }
314
315 static void
316 empathy_individual_store_channel_init (EmpathyIndividualStoreChannel *self)
317 {
318   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
319       EMPATHY_TYPE_INDIVIDUAL_STORE_CHANNEL, EmpathyIndividualStoreChannelPriv);
320
321   self->priv->individuals = g_hash_table_new_full (NULL, NULL, g_object_unref,
322       g_object_unref);
323 }
324
325 EmpathyIndividualStoreChannel *
326 empathy_individual_store_channel_new (TpChannel *channel)
327 {
328   g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
329
330   return g_object_new (EMPATHY_TYPE_INDIVIDUAL_STORE_CHANNEL,
331       "individual-channel", channel, NULL);
332 }
333
334 TpChannel *
335 empathy_individual_store_channel_get_channel (
336     EmpathyIndividualStoreChannel *self)
337 {
338   g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE_CHANNEL (self), FALSE);
339
340   return self->priv->channel;
341 }