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