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