mock-pkcs11: use g_hash_table_unref()
[nijm-empathy.git] / libempathy-gtk / empathy-individual-store-channel.c
bloba18bc33de3e72b0da26d3683af87f753f2c1bad6
1 /*
2 * Copyright (C) 2005-2007 Imendio AB
3 * Copyright (C) 2007-2011 Collabora Ltd.
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.
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.
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
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>
26 #include "config.h"
27 #include "empathy-individual-store-channel.h"
29 #include <tp-account-widgets/tpaw-pixbuf-utils.h>
31 #include "empathy-utils.h"
32 #include "empathy-ui-utils.h"
33 #include "empathy-images.h"
35 #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT
36 #include "empathy-debug.h"
38 struct _EmpathyIndividualStoreChannelPriv
40 TpChannel *channel;
42 /* TpContact => FolksIndividual
43 * We keep the individuals we have added to the store so can easily remove
44 * them when their TpContact leaves the channel. */
45 GHashTable *individuals;
48 enum
50 PROP_0,
51 PROP_CHANNEL,
55 G_DEFINE_TYPE (EmpathyIndividualStoreChannel, empathy_individual_store_channel,
56 EMPATHY_TYPE_INDIVIDUAL_STORE);
58 static void
59 add_members (EmpathyIndividualStoreChannel *self,
60 GPtrArray *members)
62 EmpathyIndividualStore *store = (EmpathyIndividualStore *) self;
63 guint i;
65 for (i = 0; i < members->len; i++)
67 TpContact *contact = g_ptr_array_index (members, i);
68 FolksIndividual *individual;
70 if (g_hash_table_lookup (self->priv->individuals, contact) != NULL)
71 continue;
73 individual = empathy_ensure_individual_from_tp_contact (contact);
74 if (individual == NULL)
75 return;
77 DEBUG ("%s joined channel %s", tp_contact_get_identifier (contact),
78 tp_proxy_get_object_path (self->priv->channel));
80 individual_store_add_individual_and_connect (store, individual);
82 /* Pass the individual reference to the hash table */
83 g_hash_table_insert (self->priv->individuals, g_object_ref (contact),
84 individual);
88 static void
89 remove_members (EmpathyIndividualStoreChannel *self,
90 GPtrArray *members)
92 EmpathyIndividualStore *store = (EmpathyIndividualStore *) self;
93 guint i;
95 for (i = 0; i < members->len; i++)
97 TpContact *contact = g_ptr_array_index (members, i);
98 FolksIndividual *individual;
100 individual = g_hash_table_lookup (self->priv->individuals, contact);
101 if (individual == NULL)
102 continue;
104 DEBUG ("%s left channel %s", tp_contact_get_identifier (contact),
105 tp_proxy_get_object_path (self->priv->channel));
107 individual_store_remove_individual_and_disconnect (store, individual);
109 g_hash_table_remove (self->priv->individuals, contact);
113 static void
114 group_contacts_changed_cb (TpChannel *channel,
115 GPtrArray *added,
116 GPtrArray *removed,
117 GPtrArray *local_pending,
118 GPtrArray *remote_pending,
119 TpContact *actor,
120 GHashTable *details,
121 gpointer user_data)
123 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
124 user_data);
126 remove_members (self, removed);
127 add_members (self, added);
130 static void
131 individual_store_channel_contact_chat_state_changed (TpTextChannel *channel,
132 TpContact *tp_contact,
133 TpChannelChatState state,
134 EmpathyIndividualStoreChannel *self)
136 FolksIndividual *individual;
137 EmpathyContact *contact = NULL;
138 GList *iters, *l;
139 GdkPixbuf *pixbuf;
141 contact = empathy_contact_dup_from_tp_contact (tp_contact);
142 if (empathy_contact_is_user (contact))
144 /* We don't care about our own chat composing states */
145 goto finally;
148 DEBUG ("Contact %s entered chat state %d",
149 tp_contact_get_identifier (tp_contact), state);
151 individual = g_hash_table_lookup (self->priv->individuals, tp_contact);
152 if (individual == NULL)
154 g_warning ("individual is NULL");
155 goto finally;
158 iters = empathy_individual_store_find_contact (
159 EMPATHY_INDIVIDUAL_STORE (self), individual);
161 if (state == TP_CHANNEL_CHAT_STATE_COMPOSING)
163 gchar *icon_filename =
164 tpaw_filename_from_icon_name (EMPATHY_IMAGE_TYPING,
165 GTK_ICON_SIZE_MENU);
167 pixbuf = gdk_pixbuf_new_from_file (icon_filename, NULL);
168 g_free (icon_filename);
170 else
172 pixbuf = empathy_individual_store_get_individual_status_icon (
173 EMPATHY_INDIVIDUAL_STORE (self), individual);
175 /* Take a ref as the 'if' blocks creates a new pixbuf */
176 g_object_ref (pixbuf);
179 for (l = iters; l != NULL; l = l->next)
181 gtk_tree_store_set (GTK_TREE_STORE (self), l->data,
182 EMPATHY_INDIVIDUAL_STORE_COL_ICON_STATUS, pixbuf,
183 -1);
185 /* Store takes it's own ref */
186 g_object_unref (pixbuf);
188 empathy_individual_store_free_iters (iters);
190 finally:
191 g_object_unref (contact);
194 static void
195 individual_store_channel_set_individual_channel (
196 EmpathyIndividualStoreChannel *self,
197 TpChannel *channel)
199 GPtrArray *members;
201 g_assert (self->priv->channel == NULL); /* construct only */
202 self->priv->channel = g_object_ref (channel);
204 /* Add initial members */
205 members = tp_channel_group_dup_members_contacts (channel);
206 if (members != NULL)
208 add_members (self, members);
209 g_ptr_array_unref (members);
212 tp_g_signal_connect_object (channel, "group-contacts-changed",
213 G_CALLBACK (group_contacts_changed_cb), self, 0);
215 tp_g_signal_connect_object (channel, "contact-chat-state-changed",
216 G_CALLBACK (individual_store_channel_contact_chat_state_changed),
217 self, 0);
220 static void
221 individual_store_channel_dispose (GObject *object)
223 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
224 object);
225 EmpathyIndividualStore *store = EMPATHY_INDIVIDUAL_STORE (object);
226 GHashTableIter iter;
227 gpointer v;
229 g_hash_table_iter_init (&iter, self->priv->individuals);
230 while (g_hash_table_iter_next (&iter, NULL, &v))
232 FolksIndividual *individual = v;
234 empathy_individual_store_disconnect_individual (store, individual);
237 tp_clear_pointer (&self->priv->individuals, g_hash_table_unref);
238 g_clear_object (&self->priv->channel);
240 G_OBJECT_CLASS (empathy_individual_store_channel_parent_class)->dispose (
241 object);
244 static void
245 individual_store_channel_get_property (GObject *object,
246 guint param_id,
247 GValue *value,
248 GParamSpec *pspec)
250 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
251 object);
253 switch (param_id)
255 case PROP_CHANNEL:
256 g_value_set_object (value, self->priv->channel);
257 break;
258 default:
259 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
260 break;
264 static void
265 individual_store_channel_set_property (GObject *object,
266 guint param_id,
267 const GValue *value,
268 GParamSpec *pspec)
270 switch (param_id)
272 case PROP_CHANNEL:
273 individual_store_channel_set_individual_channel (
274 EMPATHY_INDIVIDUAL_STORE_CHANNEL (object),
275 g_value_get_object (value));
276 break;
277 default:
278 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
279 break;
283 static void
284 individual_store_channel_reload_individuals (EmpathyIndividualStore *store)
286 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
287 store);
288 GPtrArray *members;
289 GList *list, *l;
291 /* remove all. The list returned by g_hash_table_get_keys() is valid until
292 * the hash table is modified so we can't remove the contact directly in the
293 * iteration. */
294 members = g_ptr_array_new_with_free_func (g_object_unref);
296 list = g_hash_table_get_keys (self->priv->individuals);
297 for (l = list; l != NULL; l = g_list_next (l))
299 g_ptr_array_add (members, g_object_ref (l->data));
302 remove_members (self, members);
304 g_list_free (list);
305 g_ptr_array_unref (members);
307 /* re-add members */
308 members = tp_channel_group_dup_members_contacts (self->priv->channel);
309 if (members == NULL)
310 return;
312 add_members (self, members);
313 g_ptr_array_unref (members);
316 static gboolean
317 individual_store_channel_initial_loading (EmpathyIndividualStore *store)
319 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
320 store);
322 return !tp_proxy_is_prepared (self->priv->channel,
323 TP_CHANNEL_FEATURE_CONTACTS);
326 static void
327 empathy_individual_store_channel_class_init (
328 EmpathyIndividualStoreChannelClass *klass)
330 GObjectClass *object_class = G_OBJECT_CLASS (klass);
331 EmpathyIndividualStoreClass *store_class = EMPATHY_INDIVIDUAL_STORE_CLASS (
332 klass);
334 object_class->dispose = individual_store_channel_dispose;
335 object_class->get_property = individual_store_channel_get_property;
336 object_class->set_property = individual_store_channel_set_property;
338 store_class->reload_individuals = individual_store_channel_reload_individuals;
339 store_class->initial_loading = individual_store_channel_initial_loading;
341 g_object_class_install_property (object_class,
342 PROP_CHANNEL,
343 g_param_spec_object ("individual-channel",
344 "Individual channel",
345 "Individual channel",
346 TP_TYPE_CHANNEL,
347 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
349 g_type_class_add_private (object_class,
350 sizeof (EmpathyIndividualStoreChannelPriv));
353 static void
354 empathy_individual_store_channel_init (EmpathyIndividualStoreChannel *self)
356 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
357 EMPATHY_TYPE_INDIVIDUAL_STORE_CHANNEL, EmpathyIndividualStoreChannelPriv);
359 self->priv->individuals = g_hash_table_new_full (NULL, NULL, g_object_unref,
360 g_object_unref);
363 EmpathyIndividualStoreChannel *
364 empathy_individual_store_channel_new (TpChannel *channel)
366 g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL);
368 return g_object_new (EMPATHY_TYPE_INDIVIDUAL_STORE_CHANNEL,
369 "individual-channel", channel, NULL);
372 TpChannel *
373 empathy_individual_store_channel_get_channel (
374 EmpathyIndividualStoreChannel *self)
376 g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_STORE_CHANNEL (self), FALSE);
378 return self->priv->channel;