]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-roster-view.c
roster-view: add 'show-groups' property
[empathy.git] / libempathy-gtk / empathy-roster-view.c
1
2 #include "config.h"
3
4 #include "empathy-roster-view.h"
5
6 #include <libempathy-gtk/empathy-roster-item.h>
7
8 G_DEFINE_TYPE (EmpathyRosterView, empathy_roster_view, EGG_TYPE_LIST_BOX)
9
10 enum
11 {
12   PROP_MANAGER = 1,
13   PROP_SHOW_OFFLINE,
14   PROP_SHOW_GROUPS,
15   N_PROPS
16 };
17
18 /*
19 enum
20 {
21   LAST_SIGNAL
22 };
23
24 static guint signals[LAST_SIGNAL];
25 */
26
27 struct _EmpathyRosterViewPriv
28 {
29   EmpathyIndividualManager *manager;
30
31   /* FolksIndividual (borrowed) -> EmpathyRosterItem (borrowed) */
32   GHashTable *items;
33
34   gboolean show_offline;
35   gboolean show_groups;
36 };
37
38 static void
39 empathy_roster_view_get_property (GObject *object,
40     guint property_id,
41     GValue *value,
42     GParamSpec *pspec)
43 {
44   EmpathyRosterView *self = EMPATHY_ROSTER_VIEW (object);
45
46   switch (property_id)
47     {
48       case PROP_MANAGER:
49         g_value_set_object (value, self->priv->manager);
50         break;
51       case PROP_SHOW_OFFLINE:
52         g_value_set_boolean (value, self->priv->show_offline);
53         break;
54       case PROP_SHOW_GROUPS:
55         g_value_set_boolean (value, self->priv->show_groups);
56         break;
57       default:
58         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
59         break;
60     }
61 }
62
63 static void
64 empathy_roster_view_set_property (GObject *object,
65     guint property_id,
66     const GValue *value,
67     GParamSpec *pspec)
68 {
69   EmpathyRosterView *self = EMPATHY_ROSTER_VIEW (object);
70
71   switch (property_id)
72     {
73       case PROP_MANAGER:
74         g_assert (self->priv->manager == NULL); /* construct only */
75         self->priv->manager = g_value_dup_object (value);
76         break;
77       case PROP_SHOW_OFFLINE:
78         empathy_roster_view_show_offline (self, g_value_get_boolean (value));
79         break;
80       case PROP_SHOW_GROUPS:
81         empathy_roster_view_show_groups (self, g_value_get_boolean (value));
82         break;
83       default:
84         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
85         break;
86     }
87 }
88
89 static void
90 item_changed_cb (GtkWidget *item,
91     GParamSpec *spec,
92     EmpathyRosterView *self)
93 {
94   egg_list_box_child_changed (EGG_LIST_BOX (self), item);
95 }
96
97 static void
98 individual_added (EmpathyRosterView *self,
99     FolksIndividual *individual)
100 {
101   GtkWidget *item;
102
103   item = g_hash_table_lookup (self->priv->items, individual);
104   if (item != NULL)
105     return;
106
107   item = empathy_roster_item_new (individual);
108
109   /* Need to refilter if online is changed */
110   g_signal_connect (item, "notify::online",
111       G_CALLBACK (item_changed_cb), self);
112
113   /* Need to resort if alias is changed */
114   g_signal_connect (item, "notify::alias",
115       G_CALLBACK (item_changed_cb), self);
116
117   gtk_widget_show (item);
118   gtk_container_add (GTK_CONTAINER (self), item);
119
120   g_hash_table_insert (self->priv->items, individual, item);
121 }
122
123 static void
124 individual_removed (EmpathyRosterView *self,
125     FolksIndividual *individual)
126 {
127   GtkWidget *item;
128
129   item = g_hash_table_lookup (self->priv->items, individual);
130   if (item == NULL)
131     return;
132
133   gtk_container_remove (GTK_CONTAINER (self), item);
134
135   g_hash_table_remove (self->priv->items, individual);
136 }
137
138 static void
139 members_changed_cb (EmpathyIndividualManager *manager,
140     const gchar *message,
141     GList *added,
142     GList *removed,
143     TpChannelGroupChangeReason reason,
144     EmpathyRosterView *self)
145 {
146   GList *l;
147
148   for (l = added; l != NULL; l = g_list_next (l))
149     {
150       FolksIndividual *individual = l->data;
151
152       individual_added (self, individual);
153     }
154
155   for (l = removed; l != NULL; l = g_list_next (l))
156     {
157       FolksIndividual *individual = l->data;
158
159       individual_removed (self, individual);
160     }
161 }
162
163 static gint
164 roster_view_sort (EmpathyRosterItem *a,
165     EmpathyRosterItem *b,
166     EmpathyRosterView *self)
167 {
168   FolksIndividual *ind_a, *ind_b;
169   const gchar *alias_a, *alias_b;
170
171   ind_a = empathy_roster_item_get_individual (a);
172   ind_b = empathy_roster_item_get_individual (b);
173
174   alias_a = folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (ind_a));
175   alias_b = folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (ind_b));
176
177   return g_ascii_strcasecmp (alias_a, alias_b);
178 }
179
180 static void
181 update_separator (GtkWidget **separator,
182     GtkWidget *child,
183     GtkWidget *before,
184     gpointer user_data)
185 {
186   if (before == NULL)
187     {
188       /* No separator before the first row */
189       g_clear_object (separator);
190       return;
191     }
192
193   if (*separator != NULL)
194     return;
195
196   *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
197   g_object_ref_sink (*separator);
198 }
199
200 static gboolean
201 filter_list (GtkWidget *child,
202     gpointer user_data)
203 {
204   EmpathyRosterView *self = user_data;
205   EmpathyRosterItem *item = EMPATHY_ROSTER_ITEM (child);
206
207   if (self->priv->show_offline)
208     return TRUE;
209
210   return empathy_roster_item_is_online (item);
211 }
212
213 static void
214 populate_view (EmpathyRosterView *self)
215 {
216   GList *individuals, *l;
217
218   individuals = empathy_individual_manager_get_members (self->priv->manager);
219   for (l = individuals; l != NULL; l = g_list_next (l))
220     {
221       FolksIndividual *individual = l->data;
222
223       individual_added (self, individual);
224     }
225
226   g_list_free (individuals);
227 }
228
229 static void
230 empathy_roster_view_constructed (GObject *object)
231 {
232   EmpathyRosterView *self = EMPATHY_ROSTER_VIEW (object);
233   void (*chain_up) (GObject *) =
234       ((GObjectClass *) empathy_roster_view_parent_class)->constructed;
235
236   if (chain_up != NULL)
237     chain_up (object);
238
239   g_assert (EMPATHY_IS_INDIVIDUAL_MANAGER (self->priv->manager));
240
241   populate_view (self);
242
243   tp_g_signal_connect_object (self->priv->manager, "members-changed",
244       G_CALLBACK (members_changed_cb), self, 0);
245
246   egg_list_box_set_sort_func (EGG_LIST_BOX (self),
247       (GCompareDataFunc) roster_view_sort, self, NULL);
248
249   egg_list_box_set_separator_funcs (EGG_LIST_BOX (self), update_separator,
250       self, NULL);
251
252   egg_list_box_set_filter_func (EGG_LIST_BOX (self), filter_list, self, NULL);
253 }
254
255 static void
256 empathy_roster_view_dispose (GObject *object)
257 {
258   EmpathyRosterView *self = EMPATHY_ROSTER_VIEW (object);
259   void (*chain_up) (GObject *) =
260       ((GObjectClass *) empathy_roster_view_parent_class)->dispose;
261
262   g_clear_object (&self->priv->manager);
263
264   if (chain_up != NULL)
265     chain_up (object);
266 }
267
268 static void
269 empathy_roster_view_finalize (GObject *object)
270 {
271   EmpathyRosterView *self = EMPATHY_ROSTER_VIEW (object);
272   void (*chain_up) (GObject *) =
273       ((GObjectClass *) empathy_roster_view_parent_class)->finalize;
274
275   g_hash_table_unref (self->priv->items);
276
277   if (chain_up != NULL)
278     chain_up (object);
279 }
280
281 static void
282 empathy_roster_view_class_init (
283     EmpathyRosterViewClass *klass)
284 {
285   GObjectClass *oclass = G_OBJECT_CLASS (klass);
286   GParamSpec *spec;
287
288   oclass->get_property = empathy_roster_view_get_property;
289   oclass->set_property = empathy_roster_view_set_property;
290   oclass->constructed = empathy_roster_view_constructed;
291   oclass->dispose = empathy_roster_view_dispose;
292   oclass->finalize = empathy_roster_view_finalize;
293
294   spec = g_param_spec_object ("manager", "Manager",
295       "EmpathyIndividualManager",
296       EMPATHY_TYPE_INDIVIDUAL_MANAGER,
297       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
298   g_object_class_install_property (oclass, PROP_MANAGER, spec);
299
300   spec = g_param_spec_boolean ("show-offline", "Show Offline",
301       "Show offline contacts",
302       FALSE,
303       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
304   g_object_class_install_property (oclass, PROP_SHOW_OFFLINE, spec);
305
306   spec = g_param_spec_boolean ("show-groups", "Show Groups",
307       "Show groups",
308       FALSE,
309       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
310   g_object_class_install_property (oclass, PROP_SHOW_GROUPS, spec);
311
312   g_type_class_add_private (klass, sizeof (EmpathyRosterViewPriv));
313 }
314
315 static void
316 empathy_roster_view_init (EmpathyRosterView *self)
317 {
318   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
319       EMPATHY_TYPE_ROSTER_VIEW, EmpathyRosterViewPriv);
320
321   self->priv->items = g_hash_table_new (NULL, NULL);
322 }
323
324 GtkWidget *
325 empathy_roster_view_new (EmpathyIndividualManager *manager)
326 {
327   g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (manager), NULL);
328
329   return g_object_new (EMPATHY_TYPE_ROSTER_VIEW,
330       "manager", manager,
331       NULL);
332 }
333
334 EmpathyIndividualManager *
335 empathy_roster_view_get_manager (EmpathyRosterView *self)
336 {
337   return self->priv->manager;
338 }
339
340 void
341 empathy_roster_view_show_offline (EmpathyRosterView *self,
342     gboolean show)
343 {
344   if (self->priv->show_offline == show)
345     return;
346
347   self->priv->show_offline = show;
348   egg_list_box_refilter (EGG_LIST_BOX (self));
349
350   g_object_notify (G_OBJECT (self), "show-offline");
351 }
352
353 static void
354 clear_view (EmpathyRosterView *self)
355 {
356   gtk_container_foreach (GTK_CONTAINER (self),
357       (GtkCallback) gtk_widget_destroy, NULL);
358
359   g_hash_table_remove_all (self->priv->items);
360 }
361
362 void
363 empathy_roster_view_show_groups (EmpathyRosterView *self,
364     gboolean show)
365 {
366   if (self->priv->show_groups == show)
367     return;
368
369   self->priv->show_groups = show;
370
371   clear_view (self);
372   populate_view (self);
373
374   g_object_notify (G_OBJECT (self), "show-groups");
375 }