]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-roster-view.c
factor out add_roster_contact()
[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-contact.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) -> EmpathyRosterContact (borrowed) */
32   GHashTable *roster_contacts;
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 roster_contact_changed_cb (GtkWidget *child,
91     GParamSpec *spec,
92     EmpathyRosterView *self)
93 {
94   egg_list_box_child_changed (EGG_LIST_BOX (self), child);
95 }
96
97 static GtkWidget *
98 add_roster_contact (EmpathyRosterView *self,
99     FolksIndividual *individual)
100 {
101   GtkWidget *contact;
102
103   contact = empathy_roster_contact_new (individual);
104
105   /* Need to refilter if online is changed */
106   g_signal_connect (contact, "notify::online",
107       G_CALLBACK (roster_contact_changed_cb), self);
108
109   /* Need to resort if alias is changed */
110   g_signal_connect (contact, "notify::alias",
111       G_CALLBACK (roster_contact_changed_cb), self);
112
113   gtk_widget_show (contact);
114   gtk_container_add (GTK_CONTAINER (self), contact);
115
116   return contact;
117 }
118
119 static void
120 individual_added (EmpathyRosterView *self,
121     FolksIndividual *individual)
122 {
123   GtkWidget *contact;
124
125   contact = g_hash_table_lookup (self->priv->roster_contacts, individual);
126   if (contact != NULL)
127     return;
128
129   contact = add_roster_contact (self, individual);
130
131   g_hash_table_insert (self->priv->roster_contacts, individual, contact);
132 }
133
134 static void
135 individual_removed (EmpathyRosterView *self,
136     FolksIndividual *individual)
137 {
138   GtkWidget *contact;
139
140   contact = g_hash_table_lookup (self->priv->roster_contacts, individual);
141   if (contact == NULL)
142     return;
143
144   gtk_container_remove (GTK_CONTAINER (self), contact);
145
146   g_hash_table_remove (self->priv->roster_contacts, individual);
147 }
148
149 static void
150 members_changed_cb (EmpathyIndividualManager *manager,
151     const gchar *message,
152     GList *added,
153     GList *removed,
154     TpChannelGroupChangeReason reason,
155     EmpathyRosterView *self)
156 {
157   GList *l;
158
159   for (l = added; l != NULL; l = g_list_next (l))
160     {
161       FolksIndividual *individual = l->data;
162
163       individual_added (self, individual);
164     }
165
166   for (l = removed; l != NULL; l = g_list_next (l))
167     {
168       FolksIndividual *individual = l->data;
169
170       individual_removed (self, individual);
171     }
172 }
173
174 static gint
175 roster_view_sort (EmpathyRosterContact *a,
176     EmpathyRosterContact *b,
177     EmpathyRosterView *self)
178 {
179   FolksIndividual *ind_a, *ind_b;
180   const gchar *alias_a, *alias_b;
181
182   ind_a = empathy_roster_contact_get_individual (a);
183   ind_b = empathy_roster_contact_get_individual (b);
184
185   alias_a = folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (ind_a));
186   alias_b = folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (ind_b));
187
188   return g_ascii_strcasecmp (alias_a, alias_b);
189 }
190
191 static void
192 update_separator (GtkWidget **separator,
193     GtkWidget *child,
194     GtkWidget *before,
195     gpointer user_data)
196 {
197   if (before == NULL)
198     {
199       /* No separator before the first row */
200       g_clear_object (separator);
201       return;
202     }
203
204   if (*separator != NULL)
205     return;
206
207   *separator = gtk_separator_new (GTK_ORIENTATION_HORIZONTAL);
208   g_object_ref_sink (*separator);
209 }
210
211 static gboolean
212 filter_list (GtkWidget *child,
213     gpointer user_data)
214 {
215   EmpathyRosterView *self = user_data;
216   EmpathyRosterContact *contact = EMPATHY_ROSTER_CONTACT (child);
217
218   if (self->priv->show_offline)
219     return TRUE;
220
221   return empathy_roster_contact_is_online (contact);
222 }
223
224 static void
225 populate_view (EmpathyRosterView *self)
226 {
227   GList *individuals, *l;
228
229   individuals = empathy_individual_manager_get_members (self->priv->manager);
230   for (l = individuals; l != NULL; l = g_list_next (l))
231     {
232       FolksIndividual *individual = l->data;
233
234       individual_added (self, individual);
235     }
236
237   g_list_free (individuals);
238 }
239
240 static void
241 empathy_roster_view_constructed (GObject *object)
242 {
243   EmpathyRosterView *self = EMPATHY_ROSTER_VIEW (object);
244   void (*chain_up) (GObject *) =
245       ((GObjectClass *) empathy_roster_view_parent_class)->constructed;
246
247   if (chain_up != NULL)
248     chain_up (object);
249
250   g_assert (EMPATHY_IS_INDIVIDUAL_MANAGER (self->priv->manager));
251
252   populate_view (self);
253
254   tp_g_signal_connect_object (self->priv->manager, "members-changed",
255       G_CALLBACK (members_changed_cb), self, 0);
256
257   egg_list_box_set_sort_func (EGG_LIST_BOX (self),
258       (GCompareDataFunc) roster_view_sort, self, NULL);
259
260   egg_list_box_set_separator_funcs (EGG_LIST_BOX (self), update_separator,
261       self, NULL);
262
263   egg_list_box_set_filter_func (EGG_LIST_BOX (self), filter_list, self, NULL);
264 }
265
266 static void
267 empathy_roster_view_dispose (GObject *object)
268 {
269   EmpathyRosterView *self = EMPATHY_ROSTER_VIEW (object);
270   void (*chain_up) (GObject *) =
271       ((GObjectClass *) empathy_roster_view_parent_class)->dispose;
272
273   g_clear_object (&self->priv->manager);
274
275   if (chain_up != NULL)
276     chain_up (object);
277 }
278
279 static void
280 empathy_roster_view_finalize (GObject *object)
281 {
282   EmpathyRosterView *self = EMPATHY_ROSTER_VIEW (object);
283   void (*chain_up) (GObject *) =
284       ((GObjectClass *) empathy_roster_view_parent_class)->finalize;
285
286   g_hash_table_unref (self->priv->roster_contacts);
287
288   if (chain_up != NULL)
289     chain_up (object);
290 }
291
292 static void
293 empathy_roster_view_class_init (
294     EmpathyRosterViewClass *klass)
295 {
296   GObjectClass *oclass = G_OBJECT_CLASS (klass);
297   GParamSpec *spec;
298
299   oclass->get_property = empathy_roster_view_get_property;
300   oclass->set_property = empathy_roster_view_set_property;
301   oclass->constructed = empathy_roster_view_constructed;
302   oclass->dispose = empathy_roster_view_dispose;
303   oclass->finalize = empathy_roster_view_finalize;
304
305   spec = g_param_spec_object ("manager", "Manager",
306       "EmpathyIndividualManager",
307       EMPATHY_TYPE_INDIVIDUAL_MANAGER,
308       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
309   g_object_class_install_property (oclass, PROP_MANAGER, spec);
310
311   spec = g_param_spec_boolean ("show-offline", "Show Offline",
312       "Show offline contacts",
313       FALSE,
314       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
315   g_object_class_install_property (oclass, PROP_SHOW_OFFLINE, spec);
316
317   spec = g_param_spec_boolean ("show-groups", "Show Groups",
318       "Show groups",
319       FALSE,
320       G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
321   g_object_class_install_property (oclass, PROP_SHOW_GROUPS, spec);
322
323   g_type_class_add_private (klass, sizeof (EmpathyRosterViewPriv));
324 }
325
326 static void
327 empathy_roster_view_init (EmpathyRosterView *self)
328 {
329   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
330       EMPATHY_TYPE_ROSTER_VIEW, EmpathyRosterViewPriv);
331
332   self->priv->roster_contacts = g_hash_table_new (NULL, NULL);
333 }
334
335 GtkWidget *
336 empathy_roster_view_new (EmpathyIndividualManager *manager)
337 {
338   g_return_val_if_fail (EMPATHY_IS_INDIVIDUAL_MANAGER (manager), NULL);
339
340   return g_object_new (EMPATHY_TYPE_ROSTER_VIEW,
341       "manager", manager,
342       NULL);
343 }
344
345 EmpathyIndividualManager *
346 empathy_roster_view_get_manager (EmpathyRosterView *self)
347 {
348   return self->priv->manager;
349 }
350
351 void
352 empathy_roster_view_show_offline (EmpathyRosterView *self,
353     gboolean show)
354 {
355   if (self->priv->show_offline == show)
356     return;
357
358   self->priv->show_offline = show;
359   egg_list_box_refilter (EGG_LIST_BOX (self));
360
361   g_object_notify (G_OBJECT (self), "show-offline");
362 }
363
364 static void
365 clear_view (EmpathyRosterView *self)
366 {
367   gtk_container_foreach (GTK_CONTAINER (self),
368       (GtkCallback) gtk_widget_destroy, NULL);
369
370   g_hash_table_remove_all (self->priv->roster_contacts);
371 }
372
373 void
374 empathy_roster_view_show_groups (EmpathyRosterView *self,
375     gboolean show)
376 {
377   if (self->priv->show_groups == show)
378     return;
379
380   self->priv->show_groups = show;
381
382   clear_view (self);
383   populate_view (self);
384
385   g_object_notify (G_OBJECT (self), "show-groups");
386 }