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