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>
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
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;
55 G_DEFINE_TYPE (EmpathyIndividualStoreChannel, empathy_individual_store_channel,
56 EMPATHY_TYPE_INDIVIDUAL_STORE);
59 add_members (EmpathyIndividualStoreChannel *self,
62 EmpathyIndividualStore *store = (EmpathyIndividualStore *) self;
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)
73 individual = empathy_ensure_individual_from_tp_contact (contact);
74 if (individual == NULL)
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),
89 remove_members (EmpathyIndividualStoreChannel *self,
92 EmpathyIndividualStore *store = (EmpathyIndividualStore *) self;
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)
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);
114 group_contacts_changed_cb (TpChannel *channel,
117 GPtrArray *local_pending,
118 GPtrArray *remote_pending,
123 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
126 remove_members (self, removed);
127 add_members (self, added);
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;
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 */
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");
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,
167 pixbuf = gdk_pixbuf_new_from_file (icon_filename, NULL);
168 g_free (icon_filename);
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,
185 /* Store takes it's own ref */
186 g_object_unref (pixbuf);
188 empathy_individual_store_free_iters (iters);
191 g_object_unref (contact);
195 individual_store_channel_set_individual_channel (
196 EmpathyIndividualStoreChannel *self,
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);
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),
221 individual_store_channel_dispose (GObject *object)
223 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
225 EmpathyIndividualStore *store = EMPATHY_INDIVIDUAL_STORE (object);
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 (
245 individual_store_channel_get_property (GObject *object,
250 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
256 g_value_set_object (value, self->priv->channel);
259 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
265 individual_store_channel_set_property (GObject *object,
273 individual_store_channel_set_individual_channel (
274 EMPATHY_INDIVIDUAL_STORE_CHANNEL (object),
275 g_value_get_object (value));
278 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
284 individual_store_channel_reload_individuals (EmpathyIndividualStore *store)
286 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
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
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);
305 g_ptr_array_unref (members);
308 members = tp_channel_group_dup_members_contacts (self->priv->channel);
312 add_members (self, members);
313 g_ptr_array_unref (members);
317 individual_store_channel_initial_loading (EmpathyIndividualStore *store)
319 EmpathyIndividualStoreChannel *self = EMPATHY_INDIVIDUAL_STORE_CHANNEL (
322 return !tp_proxy_is_prepared (self->priv->channel,
323 TP_CHANNEL_FEATURE_CONTACTS);
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 (
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,
343 g_param_spec_object ("individual-channel",
344 "Individual channel",
345 "Individual channel",
347 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
349 g_type_class_add_private (object_class,
350 sizeof (EmpathyIndividualStoreChannelPriv));
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,
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);
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;