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