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