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
;