]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-roster-model-aggregator.c
UOA: Do not segfault when "Done" or "Cancel" button clicked but widget is not ready yet
[empathy.git] / libempathy-gtk / empathy-roster-model-aggregator.c
1 /*
2  * empathy-roster-model-aggregator.c
3  *
4  * Implementation of EmpathyRosterModel using FolksIndividualAggregator as
5  * source.
6  *
7  * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23
24 #include "config.h"
25
26 #include <folks/folks-telepathy.h>
27
28 #include "empathy-roster-model-aggregator.h"
29
30 /**
31  * SECTION: empathy-roster-model-aggregator
32  * @title: EmpathyRosterModelAggregator
33  * @short_description: TODO
34  *
35  * TODO
36  */
37
38 /**
39  * EmpathyRosterModelAggregator:
40  *
41  * Data structure representing a #EmpathyRosterModelAggregator.
42  *
43  * Since: UNRELEASED
44  */
45
46 /**
47  * EmpathyRosterModelAggregatorClass:
48  *
49  * The class of a #EmpathyRosterModelAggregator.
50  *
51  * Since: UNRELEASED
52  */
53
54 static void roster_model_iface_init (EmpathyRosterModelInterface *iface);
55
56 G_DEFINE_TYPE_WITH_CODE (EmpathyRosterModelAggregator,
57     empathy_roster_model_aggregator,
58     G_TYPE_OBJECT,
59     G_IMPLEMENT_INTERFACE (EMPATHY_TYPE_ROSTER_MODEL, roster_model_iface_init))
60
61 enum
62 {
63   PROP_AGGREGATOR = 1,
64   PROP_FILTER_FUNC,
65   PROP_FILTER_DATA,
66   N_PROPS
67 };
68
69 /*
70 enum
71 {
72   LAST_SIGNAL
73 };
74
75 static guint signals[LAST_SIGNAL];
76 */
77
78 struct _EmpathyRosterModelAggregatorPriv
79 {
80   FolksIndividualAggregator *aggregator;
81   GHashTable *filtered_individuals; /* Individual -> Individual */
82
83   EmpathyRosterModelAggregatorFilterFunc filter_func;
84   gpointer filter_data;
85 };
86
87 static void
88 individual_group_changed_cb (FolksIndividual *individual,
89     gchar *group,
90     gboolean is_member,
91     EmpathyRosterModelAggregator *self)
92 {
93   empathy_roster_model_fire_groups_changed (EMPATHY_ROSTER_MODEL (self),
94       individual, group, is_member);
95 }
96
97 static void
98 add_to_filtered_individuals (EmpathyRosterModelAggregator *self,
99     FolksIndividual *individual)
100 {
101   g_hash_table_add (self->priv->filtered_individuals,
102       g_object_ref (individual));
103
104   tp_g_signal_connect_object (individual, "group-changed",
105       G_CALLBACK (individual_group_changed_cb), self, 0);
106
107   empathy_roster_model_fire_individual_added (EMPATHY_ROSTER_MODEL (self),
108       individual);
109 }
110
111 static void
112 remove_from_filtered_individuals (EmpathyRosterModelAggregator *self,
113     FolksIndividual *individual)
114 {
115   g_signal_handlers_disconnect_by_func (individual,
116       individual_group_changed_cb, self);
117
118   g_hash_table_remove (self->priv->filtered_individuals, individual);
119
120   empathy_roster_model_fire_individual_removed (EMPATHY_ROSTER_MODEL (self),
121       individual);
122 }
123
124 static void
125 individual_notify_cb (FolksIndividual *individual,
126     GParamSpec *param,
127     EmpathyRosterModelAggregator *self)
128 {
129   if (!self->priv->filter_func (EMPATHY_ROSTER_MODEL (self), individual, self)
130       && g_hash_table_contains (self->priv->filtered_individuals, individual))
131     remove_from_filtered_individuals (self, individual);
132
133   if (self->priv->filter_func (EMPATHY_ROSTER_MODEL (self), individual, self)
134       && !g_hash_table_contains (self->priv->filtered_individuals, individual))
135     add_to_filtered_individuals (self, individual);
136 }
137
138 static void
139 add_individual (EmpathyRosterModelAggregator *self,
140     FolksIndividual *individual)
141 {
142   if (self->priv->filter_func != NULL)
143     {
144       tp_g_signal_connect_object (individual, "notify",
145           G_CALLBACK (individual_notify_cb), self, 0);
146
147       if (!self->priv->filter_func (EMPATHY_ROSTER_MODEL (self), individual,
148               self))
149         return;
150     }
151
152   add_to_filtered_individuals (self, individual);
153 }
154
155 static void
156 remove_individual (EmpathyRosterModelAggregator *self,
157     FolksIndividual *individual)
158 {
159   if (self->priv->filter_func != NULL)
160     g_signal_handlers_disconnect_by_func (individual,
161         individual_notify_cb, self);
162
163   if (g_hash_table_contains (self->priv->filtered_individuals,
164           individual))
165     remove_from_filtered_individuals (self, individual);
166 }
167
168 static void
169 populate_individuals (EmpathyRosterModelAggregator *self)
170 {
171   GeeMap *individuals;
172   GeeMapIterator *iter;
173
174   individuals = folks_individual_aggregator_get_individuals (
175       self->priv->aggregator);
176   iter = gee_map_map_iterator (individuals);
177   while (gee_map_iterator_next (iter))
178     {
179       add_individual (self, gee_map_iterator_get_value (iter));
180     }
181   g_clear_object (&iter);
182 }
183
184 static void
185 aggregator_individuals_changed_cb (FolksIndividualAggregator *aggregator,
186     GeeSet *added,
187     GeeSet *removed,
188     gchar *message,
189     FolksPersona *actor,
190     FolksGroupDetailsChangeReason reason,
191     EmpathyRosterModelAggregator *self)
192 {
193   if (gee_collection_get_size (GEE_COLLECTION (added)) > 0)
194     {
195       GeeIterator *iter = gee_iterable_iterator (GEE_ITERABLE (added));
196
197       while (iter != NULL && gee_iterator_next (iter))
198         {
199           add_individual (self, gee_iterator_get (iter));
200         }
201       g_clear_object (&iter);
202     }
203
204   if (gee_collection_get_size (GEE_COLLECTION (removed)) > 0)
205     {
206       GeeIterator *iter = gee_iterable_iterator (GEE_ITERABLE (removed));
207
208       while (iter != NULL && gee_iterator_next (iter))
209         {
210           remove_individual (self, gee_iterator_get (iter));
211         }
212       g_clear_object (&iter);
213     }
214 }
215
216 static void
217 empathy_roster_model_aggregator_get_property (GObject *object,
218     guint property_id,
219     GValue *value,
220     GParamSpec *pspec)
221 {
222   EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object);
223
224   switch (property_id)
225     {
226       case PROP_AGGREGATOR:
227         g_value_set_object (value, self->priv->aggregator);
228         break;
229       case PROP_FILTER_FUNC:
230         g_value_set_pointer (value, self->priv->filter_func);
231         break;
232       case PROP_FILTER_DATA:
233         g_value_set_pointer (value, self->priv->filter_data);
234         break;
235       default:
236         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
237         break;
238     }
239 }
240
241 static void
242 empathy_roster_model_aggregator_set_property (GObject *object,
243     guint property_id,
244     const GValue *value,
245     GParamSpec *pspec)
246 {
247   EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object);
248
249   switch (property_id)
250     {
251       case PROP_AGGREGATOR:
252         g_assert (self->priv->aggregator == NULL); /* construct only */
253         self->priv->aggregator = g_value_dup_object (value);
254         break;
255       case PROP_FILTER_FUNC:
256         g_assert (self->priv->filter_func == NULL); /* construct only */
257         self->priv->filter_func = g_value_get_pointer (value);
258         break;
259       case PROP_FILTER_DATA:
260         g_assert (self->priv->filter_data == NULL); /* construct only */
261         self->priv->filter_data = g_value_get_pointer (value);
262         break;
263       default:
264         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
265         break;
266     }
267 }
268
269 static void
270 empathy_roster_model_aggregator_constructed (GObject *object)
271 {
272   EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object);
273   void (*chain_up) (GObject *) =
274       ((GObjectClass *) empathy_roster_model_aggregator_parent_class)->constructed;
275
276   if (chain_up != NULL)
277     chain_up (object);
278
279   if (self->priv->aggregator == NULL)
280     self->priv->aggregator = folks_individual_aggregator_new ();
281
282   g_assert (FOLKS_IS_INDIVIDUAL_AGGREGATOR (self->priv->aggregator));
283
284   tp_g_signal_connect_object (self->priv->aggregator, "individuals-changed",
285       G_CALLBACK (aggregator_individuals_changed_cb), self, 0);
286
287   folks_individual_aggregator_prepare (self->priv->aggregator, NULL, NULL);
288
289   populate_individuals (self);
290 }
291
292 static void
293 empathy_roster_model_aggregator_dispose (GObject *object)
294 {
295   EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object);
296   void (*chain_up) (GObject *) =
297       ((GObjectClass *) empathy_roster_model_aggregator_parent_class)->dispose;
298
299   g_clear_object (&self->priv->aggregator);
300   g_clear_pointer (&self->priv->filtered_individuals, g_hash_table_unref);
301
302   if (chain_up != NULL)
303     chain_up (object);
304 }
305
306 static void
307 empathy_roster_model_aggregator_finalize (GObject *object)
308 {
309   //EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (object);
310   void (*chain_up) (GObject *) =
311       ((GObjectClass *) empathy_roster_model_aggregator_parent_class)->finalize;
312
313   if (chain_up != NULL)
314     chain_up (object);
315 }
316
317 static void
318 empathy_roster_model_aggregator_class_init (
319     EmpathyRosterModelAggregatorClass *klass)
320 {
321   GObjectClass *oclass = G_OBJECT_CLASS (klass);
322   GParamSpec *spec;
323
324   oclass->get_property = empathy_roster_model_aggregator_get_property;
325   oclass->set_property = empathy_roster_model_aggregator_set_property;
326   oclass->constructed = empathy_roster_model_aggregator_constructed;
327   oclass->dispose = empathy_roster_model_aggregator_dispose;
328   oclass->finalize = empathy_roster_model_aggregator_finalize;
329
330   spec = g_param_spec_object ("aggregator", "Aggregator",
331       "FolksIndividualAggregator",
332       FOLKS_TYPE_INDIVIDUAL_AGGREGATOR,
333       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
334   g_object_class_install_property (oclass, PROP_AGGREGATOR, spec);
335
336   spec = g_param_spec_pointer ("filter-func", "Filter-Func",
337       "EmpathyRosterModelAggregatorFilterFunc",
338       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
339   g_object_class_install_property (oclass, PROP_FILTER_FUNC, spec);
340
341   spec = g_param_spec_pointer ("filter-data", "Filter-Data",
342       "GPointer",
343       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
344   g_object_class_install_property (oclass, PROP_FILTER_DATA, spec);
345
346   g_type_class_add_private (klass, sizeof (EmpathyRosterModelAggregatorPriv));
347 }
348
349 static void
350 empathy_roster_model_aggregator_init (EmpathyRosterModelAggregator *self)
351 {
352   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
353       EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR, EmpathyRosterModelAggregatorPriv);
354
355   self->priv->filtered_individuals = g_hash_table_new_full (NULL, NULL, NULL,
356       g_object_unref);
357 }
358
359 EmpathyRosterModelAggregator *
360 empathy_roster_model_aggregator_new (
361     EmpathyRosterModelAggregatorFilterFunc filter_func,
362     gpointer user_data)
363 {
364   return g_object_new (EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR,
365       "filter-func", filter_func,
366       "filter-data", user_data,
367       NULL);
368 }
369
370 EmpathyRosterModelAggregator *
371 empathy_roster_model_aggregator_new_with_aggregator (
372     FolksIndividualAggregator *aggregator,
373     EmpathyRosterModelAggregatorFilterFunc filter_func,
374     gpointer user_data)
375 {
376   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL_AGGREGATOR (aggregator), NULL);
377
378   return g_object_new (EMPATHY_TYPE_ROSTER_MODEL_AGGREGATOR,
379       "aggregator", aggregator,
380       "filter-func", filter_func,
381       "filter-data", user_data,
382       NULL);
383 }
384
385 static GList *
386 empathy_roster_model_aggregator_get_individuals (EmpathyRosterModel *model)
387 {
388   EmpathyRosterModelAggregator *self = EMPATHY_ROSTER_MODEL_AGGREGATOR (model);
389
390   return g_hash_table_get_values (self->priv->filtered_individuals);
391 }
392
393 static GList *
394 empathy_roster_model_aggregator_dup_groups_for_individual (
395     EmpathyRosterModel *model,
396     FolksIndividual *individual)
397 {
398   GList *groups_list = NULL;
399   GeeSet *groups_set;
400
401   groups_set = folks_group_details_get_groups (
402       FOLKS_GROUP_DETAILS (individual));
403   if (gee_collection_get_size (GEE_COLLECTION (groups_set)) > 0)
404     {
405       GeeIterator *iter = gee_iterable_iterator (GEE_ITERABLE (groups_set));
406
407       while (iter != NULL && gee_iterator_next (iter))
408         {
409           groups_list = g_list_prepend (groups_list, gee_iterator_get (iter));
410         }
411       g_clear_object (&iter);
412     }
413
414   return groups_list;
415 }
416
417 static void
418 roster_model_iface_init (EmpathyRosterModelInterface *iface)
419 {
420   iface->get_individuals = empathy_roster_model_aggregator_get_individuals;
421   iface->dup_groups_for_individual =
422     empathy_roster_model_aggregator_dup_groups_for_individual;
423 }