]> git.0d.be Git - empathy.git/blob - libempathy-gtk/empathy-roster-contact.c
roster-contact: add group property
[empathy.git] / libempathy-gtk / empathy-roster-contact.c
1 #include "config.h"
2
3 #include "empathy-roster-contact.h"
4
5 #include <telepathy-glib/util.h>
6
7 #include <libempathy/empathy-utils.h>
8
9 #include <libempathy-gtk/empathy-images.h>
10 #include <libempathy-gtk/empathy-ui-utils.h>
11
12 G_DEFINE_TYPE (EmpathyRosterContact, empathy_roster_contact, GTK_TYPE_ALIGNMENT)
13
14 #define AVATAR_SIZE 48
15
16 enum
17 {
18   PROP_INDIVIDIUAL = 1,
19   PROP_GROUP,
20   PROP_ONLINE,
21   PROP_ALIAS,
22   N_PROPS
23 };
24
25 /*
26 enum
27 {
28   LAST_SIGNAL
29 };
30
31 static guint signals[LAST_SIGNAL];
32 */
33
34 struct _EmpathyRosterContactPriv
35 {
36   FolksIndividual *individual;
37   gchar *group;
38
39   GtkWidget *avatar;
40   GtkWidget *first_line_alig;
41   GtkWidget *alias;
42   GtkWidget *presence_msg;
43   GtkWidget *presence_icon;
44   GtkWidget *phone_icon;
45
46   gboolean online;
47 };
48
49 static const gchar *
50 get_alias (EmpathyRosterContact *self)
51 {
52   return folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (
53         self->priv->individual));
54 }
55
56 static void
57 empathy_roster_contact_get_property (GObject *object,
58     guint property_id,
59     GValue *value,
60     GParamSpec *pspec)
61 {
62   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
63
64   switch (property_id)
65     {
66       case PROP_INDIVIDIUAL:
67         g_value_set_object (value, self->priv->individual);
68         break;
69       case PROP_GROUP:
70         g_value_set_string (value, self->priv->group);
71         break;
72       case PROP_ONLINE:
73         g_value_set_boolean (value, self->priv->online);
74         break;
75       case PROP_ALIAS:
76         g_value_set_string (value, get_alias (self));
77         break;
78       default:
79         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
80         break;
81     }
82 }
83
84 static void
85 empathy_roster_contact_set_property (GObject *object,
86     guint property_id,
87     const GValue *value,
88     GParamSpec *pspec)
89 {
90   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
91
92   switch (property_id)
93     {
94       case PROP_INDIVIDIUAL:
95         g_assert (self->priv->individual == NULL); /* construct only */
96         self->priv->individual = g_value_dup_object (value);
97         break;
98       case PROP_GROUP:
99         g_assert (self->priv->group == NULL); /* construct only */
100         self->priv->group = g_value_dup_string (value);
101         break;
102       default:
103         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
104         break;
105     }
106 }
107
108 static void
109 avatar_loaded_cb (GObject *source,
110     GAsyncResult *result,
111     gpointer user_data)
112 {
113   TpWeakRef *wr = user_data;
114   EmpathyRosterContact *self;
115   GdkPixbuf *pixbuf;
116
117   self = tp_weak_ref_dup_object (wr);
118   if (self == NULL)
119     goto out;
120
121   pixbuf = empathy_pixbuf_avatar_from_individual_scaled_finish (
122       FOLKS_INDIVIDUAL (source), result, NULL);
123
124   if (pixbuf == NULL)
125     {
126       pixbuf = empathy_pixbuf_from_icon_name_sized (
127           EMPATHY_IMAGE_AVATAR_DEFAULT, AVATAR_SIZE);
128     }
129
130   gtk_image_set_from_pixbuf (GTK_IMAGE (self->priv->avatar), pixbuf);
131   g_object_unref (pixbuf);
132
133   g_object_unref (self);
134
135 out:
136   tp_weak_ref_destroy (wr);
137 }
138
139 static void
140 update_avatar (EmpathyRosterContact *self)
141 {
142   empathy_pixbuf_avatar_from_individual_scaled_async (self->priv->individual,
143       AVATAR_SIZE, AVATAR_SIZE, NULL, avatar_loaded_cb,
144       tp_weak_ref_new (self, NULL, NULL));
145 }
146
147 static void
148 avatar_changed_cb (FolksIndividual *individual,
149     GParamSpec *spec,
150     EmpathyRosterContact *self)
151 {
152   update_avatar (self);
153 }
154
155 static void
156 update_alias (EmpathyRosterContact *self)
157 {
158   gtk_label_set_text (GTK_LABEL (self->priv->alias), get_alias (self));
159
160   g_object_notify (G_OBJECT (self), "alias");
161 }
162
163 static void
164 alias_changed_cb (FolksIndividual *individual,
165     GParamSpec *spec,
166     EmpathyRosterContact *self)
167 {
168   update_alias (self);
169 }
170
171 static gboolean
172 is_phone (FolksIndividual *individual)
173 {
174   const gchar * const *types;
175
176   types = empathy_individual_get_client_types (individual);
177   if (types == NULL)
178     return FALSE;
179
180   if (g_strv_length ((GStrv) types) <= 0)
181     return FALSE;
182
183   return !tp_strdiff (types[0], "phone");
184 }
185
186 static void
187 update_presence_msg (EmpathyRosterContact *self)
188 {
189   const gchar *msg;
190
191   msg = folks_presence_details_get_presence_message (
192       FOLKS_PRESENCE_DETAILS (self->priv->individual));
193
194   if (tp_str_empty (msg))
195     {
196       /* Just display the alias in the center of the row */
197       gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
198           0, 0.5, 1, 1);
199
200       gtk_widget_hide (self->priv->presence_msg);
201     }
202   else
203     {
204       gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), msg);
205
206       gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
207           0, 0.75, 1, 1);
208       gtk_misc_set_alignment (GTK_MISC (self->priv->presence_msg), 0, 0.25);
209
210       gtk_widget_show (self->priv->presence_msg);
211     }
212
213   gtk_widget_set_visible (self->priv->phone_icon,
214       is_phone (self->priv->individual));
215 }
216
217 static void
218 presence_message_changed_cb (FolksIndividual *individual,
219     GParamSpec *spec,
220     EmpathyRosterContact *self)
221 {
222   update_presence_msg (self);
223 }
224
225 static void
226 update_presence_icon (EmpathyRosterContact *self)
227 {
228   const gchar *icon;
229
230   icon = empathy_icon_name_for_individual (self->priv->individual);
231
232   gtk_image_set_from_icon_name (GTK_IMAGE (self->priv->presence_icon), icon,
233       GTK_ICON_SIZE_MENU);
234 }
235
236 static void
237 update_online (EmpathyRosterContact *self)
238 {
239   FolksPresenceType presence;
240   gboolean online;
241
242   presence = folks_presence_details_get_presence_type (
243       FOLKS_PRESENCE_DETAILS (self->priv->individual));
244
245   switch (presence)
246     {
247       case FOLKS_PRESENCE_TYPE_UNSET:
248       case FOLKS_PRESENCE_TYPE_OFFLINE:
249       case FOLKS_PRESENCE_TYPE_UNKNOWN:
250       case FOLKS_PRESENCE_TYPE_ERROR:
251         online = FALSE;
252         break;
253
254       case FOLKS_PRESENCE_TYPE_AVAILABLE:
255       case FOLKS_PRESENCE_TYPE_AWAY:
256       case FOLKS_PRESENCE_TYPE_EXTENDED_AWAY:
257       case FOLKS_PRESENCE_TYPE_HIDDEN:
258       case FOLKS_PRESENCE_TYPE_BUSY:
259         online = TRUE;
260         break;
261
262       default:
263         g_warning ("Unknown FolksPresenceType: %d", presence);
264         online = FALSE;
265     }
266
267   if (self->priv->online == online)
268     return;
269
270   self->priv->online = online;
271   g_object_notify (G_OBJECT (self), "online");
272 }
273
274 static void
275 presence_status_changed_cb (FolksIndividual *individual,
276     GParamSpec *spec,
277     EmpathyRosterContact *self)
278 {
279   update_presence_icon (self);
280   update_online (self);
281 }
282
283 static void
284 empathy_roster_contact_constructed (GObject *object)
285 {
286   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
287   void (*chain_up) (GObject *) =
288       ((GObjectClass *) empathy_roster_contact_parent_class)->constructed;
289
290   if (chain_up != NULL)
291     chain_up (object);
292
293   g_assert (FOLKS_IS_INDIVIDUAL (self->priv->individual));
294
295   tp_g_signal_connect_object (self->priv->individual, "notify::avatar",
296       G_CALLBACK (avatar_changed_cb), self, 0);
297   tp_g_signal_connect_object (self->priv->individual, "notify::alias",
298       G_CALLBACK (alias_changed_cb), self, 0);
299   tp_g_signal_connect_object (self->priv->individual,
300       "notify::presence-message",
301       G_CALLBACK (presence_message_changed_cb), self, 0);
302   tp_g_signal_connect_object (self->priv->individual, "notify::presence-status",
303       G_CALLBACK (presence_status_changed_cb), self, 0);
304
305   update_avatar (self);
306   update_alias (self);
307   update_presence_msg (self);
308   update_presence_icon (self);
309
310   update_online (self);
311 }
312
313 static void
314 empathy_roster_contact_dispose (GObject *object)
315 {
316   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
317   void (*chain_up) (GObject *) =
318       ((GObjectClass *) empathy_roster_contact_parent_class)->dispose;
319
320   g_clear_object (&self->priv->individual);
321
322   if (chain_up != NULL)
323     chain_up (object);
324 }
325
326 static void
327 empathy_roster_contact_finalize (GObject *object)
328 {
329   EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
330   void (*chain_up) (GObject *) =
331       ((GObjectClass *) empathy_roster_contact_parent_class)->finalize;
332
333   g_free (self->priv->group);
334
335   if (chain_up != NULL)
336     chain_up (object);
337 }
338
339 static void
340 empathy_roster_contact_class_init (
341     EmpathyRosterContactClass *klass)
342 {
343   GObjectClass *oclass = G_OBJECT_CLASS (klass);
344   GParamSpec *spec;
345
346   oclass->get_property = empathy_roster_contact_get_property;
347   oclass->set_property = empathy_roster_contact_set_property;
348   oclass->constructed = empathy_roster_contact_constructed;
349   oclass->dispose = empathy_roster_contact_dispose;
350   oclass->finalize = empathy_roster_contact_finalize;
351
352   spec = g_param_spec_object ("individual", "Individual",
353       "FolksIndividual",
354       FOLKS_TYPE_INDIVIDUAL,
355       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
356   g_object_class_install_property (oclass, PROP_INDIVIDIUAL, spec);
357
358   spec = g_param_spec_string ("group", "Group",
359       "Group of this widget, or NULL",
360       NULL,
361       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
362   g_object_class_install_property (oclass, PROP_GROUP, spec);
363
364   spec = g_param_spec_boolean ("online", "Online",
365       "TRUE if Individual is online",
366       FALSE,
367       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
368   g_object_class_install_property (oclass, PROP_ONLINE, spec);
369
370   spec = g_param_spec_string ("alias", "Alias",
371       "The Alias of the individual displayed in the widget",
372       NULL,
373       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
374   g_object_class_install_property (oclass, PROP_ALIAS, spec);
375
376   g_type_class_add_private (klass, sizeof (EmpathyRosterContactPriv));
377 }
378
379 static void
380 empathy_roster_contact_init (EmpathyRosterContact *self)
381 {
382   GtkWidget *main_box, *box, *first_line_box;
383   GtkStyleContext *context;
384
385   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
386       EMPATHY_TYPE_ROSTER_CONTACT, EmpathyRosterContactPriv);
387
388   gtk_widget_set_size_request (GTK_WIDGET (self), 300, 64);
389
390   main_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
391
392   /* Avatar */
393   self->priv->avatar = gtk_image_new ();
394
395   gtk_box_pack_start (GTK_BOX (main_box), self->priv->avatar, FALSE, FALSE, 0);
396   gtk_widget_show (self->priv->avatar);
397
398   box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
399
400   /* Alias and phone icon */
401   self->priv->first_line_alig = gtk_alignment_new (0, 0.5, 1, 1);
402   first_line_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
403
404   self->priv->alias = gtk_label_new (NULL);
405   gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->alias,
406       FALSE, FALSE, 0);
407   gtk_widget_show (self->priv->alias);
408
409   self->priv->phone_icon = gtk_image_new_from_icon_name ("phone-symbolic",
410       GTK_ICON_SIZE_MENU);
411   gtk_misc_set_alignment (GTK_MISC (self->priv->phone_icon), 0, 0.5);
412   gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->phone_icon,
413       TRUE, TRUE, 0);
414
415   gtk_container_add (GTK_CONTAINER (self->priv->first_line_alig),
416       first_line_box);
417   gtk_widget_show (self->priv->first_line_alig);
418
419   gtk_box_pack_start (GTK_BOX (box), self->priv->first_line_alig,
420       TRUE, TRUE, 0);
421   gtk_widget_show (first_line_box);
422
423   gtk_box_pack_start (GTK_BOX (main_box), box, TRUE, TRUE, 0);
424   gtk_widget_show (box);
425
426   /* Presence */
427   self->priv->presence_msg = gtk_label_new (NULL);
428   gtk_box_pack_start (GTK_BOX (box), self->priv->presence_msg, TRUE, TRUE, 0);
429   gtk_widget_show (self->priv->presence_msg);
430
431   context = gtk_widget_get_style_context (self->priv->presence_msg);
432   gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
433
434   /* Presence icon */
435   self->priv->presence_icon = gtk_image_new ();
436
437   gtk_box_pack_start (GTK_BOX (main_box), self->priv->presence_icon,
438       FALSE, FALSE, 0);
439   gtk_widget_show (self->priv->presence_icon);
440
441   gtk_container_add (GTK_CONTAINER (self), main_box);
442   gtk_widget_show (main_box);
443 }
444
445 GtkWidget *
446 empathy_roster_contact_new (FolksIndividual *individual,
447     const gchar *group)
448 {
449   g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
450
451   return g_object_new (EMPATHY_TYPE_ROSTER_CONTACT,
452       "individual", individual,
453       "group", group,
454       "bottom-padding", 8,
455       "top-padding", 8,
456       "left-padding", 8,
457       "right-padding", 8,
458       NULL);
459 }
460
461 FolksIndividual *
462 empathy_roster_contact_get_individual (EmpathyRosterContact *self)
463 {
464   return self->priv->individual;
465 }
466
467 gboolean
468 empathy_roster_contact_is_online (EmpathyRosterContact *self)
469 {
470   return self->priv->online;
471 }
472
473 const gchar *
474 empathy_roster_contact_get_group (EmpathyRosterContact *self)
475 {
476   return self->priv->group;
477 }