3.12.12
[nijm-empathy.git] / libempathy-gtk / empathy-roster-contact.c
blobdf57a87fb1a8d0d11cf73a0e6bf0c045493efc17
1 #include "config.h"
2 #include "empathy-roster-contact.h"
4 #include <glib/gi18n-lib.h>
5 #include <tp-account-widgets/tpaw-images.h>
6 #include <tp-account-widgets/tpaw-pixbuf-utils.h>
8 #include "empathy-ui-utils.h"
9 #include "empathy-utils.h"
11 G_DEFINE_TYPE (EmpathyRosterContact, empathy_roster_contact, GTK_TYPE_LIST_BOX_ROW)
13 #define AVATAR_SIZE 48
15 enum
17 PROP_INDIVIDIUAL = 1,
18 PROP_GROUP,
19 PROP_ONLINE,
20 PROP_ALIAS,
21 N_PROPS
25 enum
27 LAST_SIGNAL
30 static guint signals[LAST_SIGNAL];
33 struct _EmpathyRosterContactPriv
35 FolksIndividual *individual;
36 gchar *group;
38 GtkWidget *avatar;
39 GtkWidget *first_line_alig;
40 GtkWidget *alias;
41 GtkWidget *presence_msg;
42 GtkWidget *presence_icon;
43 GtkWidget *phone_icon;
45 /* If not NULL, used instead of the individual's presence icon */
46 gchar *event_icon;
48 gboolean online;
51 static const gchar *
52 get_alias (EmpathyRosterContact *self)
54 return folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (
55 self->priv->individual));
58 static void
59 empathy_roster_contact_get_property (GObject *object,
60 guint property_id,
61 GValue *value,
62 GParamSpec *pspec)
64 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
66 switch (property_id)
68 case PROP_INDIVIDIUAL:
69 g_value_set_object (value, self->priv->individual);
70 break;
71 case PROP_GROUP:
72 g_value_set_string (value, self->priv->group);
73 break;
74 case PROP_ONLINE:
75 g_value_set_boolean (value, self->priv->online);
76 break;
77 case PROP_ALIAS:
78 g_value_set_string (value, get_alias (self));
79 break;
80 default:
81 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
82 break;
86 static void
87 empathy_roster_contact_set_property (GObject *object,
88 guint property_id,
89 const GValue *value,
90 GParamSpec *pspec)
92 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
94 switch (property_id)
96 case PROP_INDIVIDIUAL:
97 g_assert (self->priv->individual == NULL); /* construct only */
98 self->priv->individual = g_value_dup_object (value);
99 break;
100 case PROP_GROUP:
101 g_assert (self->priv->group == NULL); /* construct only */
102 self->priv->group = g_value_dup_string (value);
103 break;
104 default:
105 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
106 break;
110 static void
111 avatar_loaded_cb (GObject *source,
112 GAsyncResult *result,
113 gpointer user_data)
115 TpWeakRef *wr = user_data;
116 EmpathyRosterContact *self;
117 GdkPixbuf *pixbuf;
119 self = tp_weak_ref_dup_object (wr);
120 if (self == NULL)
121 goto out;
123 pixbuf = empathy_pixbuf_avatar_from_individual_scaled_finish (
124 FOLKS_INDIVIDUAL (source), result, NULL);
126 if (pixbuf == NULL)
128 pixbuf = tpaw_pixbuf_from_icon_name_sized (
129 TPAW_IMAGE_AVATAR_DEFAULT, AVATAR_SIZE);
132 gtk_image_set_from_pixbuf (GTK_IMAGE (self->priv->avatar), pixbuf);
133 g_object_unref (pixbuf);
135 g_object_unref (self);
137 out:
138 tp_weak_ref_destroy (wr);
141 static void
142 update_avatar (EmpathyRosterContact *self)
144 empathy_pixbuf_avatar_from_individual_scaled_async (self->priv->individual,
145 AVATAR_SIZE, AVATAR_SIZE, NULL, avatar_loaded_cb,
146 tp_weak_ref_new (self, NULL, NULL));
149 static void
150 avatar_changed_cb (FolksIndividual *individual,
151 GParamSpec *spec,
152 EmpathyRosterContact *self)
154 update_avatar (self);
157 static void
158 update_alias (EmpathyRosterContact *self)
160 gtk_label_set_text (GTK_LABEL (self->priv->alias), get_alias (self));
162 g_object_notify (G_OBJECT (self), "alias");
165 static void
166 alias_changed_cb (FolksIndividual *individual,
167 GParamSpec *spec,
168 EmpathyRosterContact *self)
170 update_alias (self);
173 static void
174 update_presence_msg (EmpathyRosterContact *self)
176 const gchar *msg;
177 GStrv types;
179 msg = folks_presence_details_get_presence_message (
180 FOLKS_PRESENCE_DETAILS (self->priv->individual));
182 if (tp_str_empty (msg))
184 /* Just display the alias in the center of the row */
185 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
186 0, 0.5, 1, 1);
188 gtk_widget_hide (self->priv->presence_msg);
190 else
192 FolksPresenceType type;
194 type = folks_presence_details_get_presence_type (
195 FOLKS_PRESENCE_DETAILS (self->priv->individual));
196 if (type == FOLKS_PRESENCE_TYPE_ERROR)
198 gchar *tmp;
200 /* Add a prefix explaining that something goes wrong when trying to
201 * fetch contact's presence. */
202 tmp = g_strdup_printf (_("Server cannot find contact: %s"), msg);
203 gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), tmp);
205 g_free (tmp);
207 else
209 gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), msg);
212 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
213 0, 0.75, 1, 1);
214 gtk_misc_set_alignment (GTK_MISC (self->priv->presence_msg), 0, 0.25);
216 gtk_widget_show (self->priv->presence_msg);
219 types = (GStrv) empathy_individual_get_client_types (self->priv->individual);
221 gtk_widget_set_visible (self->priv->phone_icon,
222 empathy_client_types_contains_mobile_device (types));
225 static void
226 presence_message_changed_cb (FolksIndividual *individual,
227 GParamSpec *spec,
228 EmpathyRosterContact *self)
230 update_presence_msg (self);
233 static void
234 update_presence_icon (EmpathyRosterContact *self)
236 const gchar *icon;
238 if (self->priv->event_icon == NULL)
239 icon = empathy_icon_name_for_individual (self->priv->individual);
240 else
241 icon = self->priv->event_icon;
243 gtk_image_set_from_icon_name (GTK_IMAGE (self->priv->presence_icon), icon,
244 GTK_ICON_SIZE_MENU);
247 static void
248 update_online (EmpathyRosterContact *self)
250 FolksPresenceType presence;
251 gboolean online;
253 presence = folks_presence_details_get_presence_type (
254 FOLKS_PRESENCE_DETAILS (self->priv->individual));
256 switch (presence)
258 case FOLKS_PRESENCE_TYPE_UNSET:
259 case FOLKS_PRESENCE_TYPE_OFFLINE:
260 case FOLKS_PRESENCE_TYPE_UNKNOWN:
261 case FOLKS_PRESENCE_TYPE_ERROR:
262 online = FALSE;
263 break;
265 case FOLKS_PRESENCE_TYPE_AVAILABLE:
266 case FOLKS_PRESENCE_TYPE_AWAY:
267 case FOLKS_PRESENCE_TYPE_EXTENDED_AWAY:
268 case FOLKS_PRESENCE_TYPE_HIDDEN:
269 case FOLKS_PRESENCE_TYPE_BUSY:
270 online = TRUE;
271 break;
273 default:
274 g_warning ("Unknown FolksPresenceType: %d", presence);
275 online = FALSE;
278 if (self->priv->online == online)
279 return;
281 self->priv->online = online;
282 g_object_notify (G_OBJECT (self), "online");
285 static void
286 presence_status_changed_cb (FolksIndividual *individual,
287 GParamSpec *spec,
288 EmpathyRosterContact *self)
290 update_presence_icon (self);
291 update_online (self);
294 static void
295 empathy_roster_contact_constructed (GObject *object)
297 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
298 void (*chain_up) (GObject *) =
299 ((GObjectClass *) empathy_roster_contact_parent_class)->constructed;
301 if (chain_up != NULL)
302 chain_up (object);
304 g_assert (FOLKS_IS_INDIVIDUAL (self->priv->individual));
306 tp_g_signal_connect_object (self->priv->individual, "notify::avatar",
307 G_CALLBACK (avatar_changed_cb), self, 0);
308 tp_g_signal_connect_object (self->priv->individual, "notify::alias",
309 G_CALLBACK (alias_changed_cb), self, 0);
310 tp_g_signal_connect_object (self->priv->individual,
311 "notify::presence-message",
312 G_CALLBACK (presence_message_changed_cb), self, 0);
313 tp_g_signal_connect_object (self->priv->individual, "notify::presence-status",
314 G_CALLBACK (presence_status_changed_cb), self, 0);
316 update_avatar (self);
317 update_alias (self);
318 update_presence_msg (self);
319 update_presence_icon (self);
321 update_online (self);
324 static void
325 empathy_roster_contact_dispose (GObject *object)
327 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
328 void (*chain_up) (GObject *) =
329 ((GObjectClass *) empathy_roster_contact_parent_class)->dispose;
331 g_clear_object (&self->priv->individual);
333 if (chain_up != NULL)
334 chain_up (object);
337 static void
338 empathy_roster_contact_finalize (GObject *object)
340 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
341 void (*chain_up) (GObject *) =
342 ((GObjectClass *) empathy_roster_contact_parent_class)->finalize;
344 g_free (self->priv->group);
345 g_free (self->priv->event_icon);
347 if (chain_up != NULL)
348 chain_up (object);
351 static void
352 empathy_roster_contact_class_init (
353 EmpathyRosterContactClass *klass)
355 GObjectClass *oclass = G_OBJECT_CLASS (klass);
356 GParamSpec *spec;
358 oclass->get_property = empathy_roster_contact_get_property;
359 oclass->set_property = empathy_roster_contact_set_property;
360 oclass->constructed = empathy_roster_contact_constructed;
361 oclass->dispose = empathy_roster_contact_dispose;
362 oclass->finalize = empathy_roster_contact_finalize;
364 spec = g_param_spec_object ("individual", "Individual",
365 "FolksIndividual",
366 FOLKS_TYPE_INDIVIDUAL,
367 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
368 g_object_class_install_property (oclass, PROP_INDIVIDIUAL, spec);
370 spec = g_param_spec_string ("group", "Group",
371 "Group of this widget, or NULL",
372 NULL,
373 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
374 g_object_class_install_property (oclass, PROP_GROUP, spec);
376 spec = g_param_spec_boolean ("online", "Online",
377 "TRUE if Individual is online",
378 FALSE,
379 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
380 g_object_class_install_property (oclass, PROP_ONLINE, spec);
382 spec = g_param_spec_string ("alias", "Alias",
383 "The Alias of the individual displayed in the widget",
384 NULL,
385 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
386 g_object_class_install_property (oclass, PROP_ALIAS, spec);
388 g_type_class_add_private (klass, sizeof (EmpathyRosterContactPriv));
391 static void
392 empathy_roster_contact_init (EmpathyRosterContact *self)
394 GtkWidget *alig, *main_box, *box, *first_line_box;
395 GtkStyleContext *context;
397 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
398 EMPATHY_TYPE_ROSTER_CONTACT, EmpathyRosterContactPriv);
400 alig = gtk_alignment_new (0.5, 0.5, 1, 1);
401 gtk_widget_show (alig);
402 gtk_alignment_set_padding (GTK_ALIGNMENT (alig), 4, 4, 4, 12);
404 main_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
406 /* Avatar */
407 self->priv->avatar = gtk_image_new ();
409 gtk_widget_set_size_request (self->priv->avatar, AVATAR_SIZE, AVATAR_SIZE);
411 gtk_box_pack_start (GTK_BOX (main_box), self->priv->avatar, FALSE, FALSE, 0);
412 gtk_widget_show (self->priv->avatar);
414 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
416 /* Alias and phone icon */
417 self->priv->first_line_alig = gtk_alignment_new (0, 0.5, 1, 1);
418 first_line_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
420 self->priv->alias = gtk_label_new (NULL);
421 gtk_label_set_ellipsize (GTK_LABEL (self->priv->alias), PANGO_ELLIPSIZE_END);
422 gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->alias,
423 FALSE, FALSE, 0);
424 gtk_misc_set_alignment (GTK_MISC (self->priv->alias), 0, 0.5);
425 gtk_widget_show (self->priv->alias);
427 self->priv->phone_icon = gtk_image_new_from_icon_name ("phone-symbolic",
428 GTK_ICON_SIZE_MENU);
429 gtk_misc_set_alignment (GTK_MISC (self->priv->phone_icon), 0, 0.5);
430 gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->phone_icon,
431 TRUE, TRUE, 0);
433 gtk_container_add (GTK_CONTAINER (self->priv->first_line_alig),
434 first_line_box);
435 gtk_widget_show (self->priv->first_line_alig);
437 gtk_box_pack_start (GTK_BOX (box), self->priv->first_line_alig,
438 TRUE, TRUE, 0);
439 gtk_widget_show (first_line_box);
441 gtk_box_pack_start (GTK_BOX (main_box), box, TRUE, TRUE, 0);
442 gtk_widget_show (box);
444 /* Presence */
445 self->priv->presence_msg = gtk_label_new (NULL);
446 gtk_label_set_ellipsize (GTK_LABEL (self->priv->presence_msg),
447 PANGO_ELLIPSIZE_END);
448 gtk_box_pack_start (GTK_BOX (box), self->priv->presence_msg, TRUE, TRUE, 0);
449 gtk_widget_show (self->priv->presence_msg);
451 context = gtk_widget_get_style_context (self->priv->presence_msg);
452 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
454 /* Presence icon */
455 self->priv->presence_icon = gtk_image_new ();
457 gtk_box_pack_start (GTK_BOX (main_box), self->priv->presence_icon,
458 FALSE, FALSE, 0);
459 gtk_widget_show (self->priv->presence_icon);
461 gtk_container_add (GTK_CONTAINER (self), alig);
462 gtk_container_add (GTK_CONTAINER (alig), main_box);
463 gtk_widget_show (main_box);
466 GtkWidget *
467 empathy_roster_contact_new (FolksIndividual *individual,
468 const gchar *group)
470 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
472 return g_object_new (EMPATHY_TYPE_ROSTER_CONTACT,
473 "individual", individual,
474 "group", group,
475 NULL);
478 FolksIndividual *
479 empathy_roster_contact_get_individual (EmpathyRosterContact *self)
481 return self->priv->individual;
484 gboolean
485 empathy_roster_contact_is_online (EmpathyRosterContact *self)
487 return self->priv->online;
490 const gchar *
491 empathy_roster_contact_get_group (EmpathyRosterContact *self)
493 return self->priv->group;
496 void
497 empathy_roster_contact_set_event_icon (EmpathyRosterContact *self,
498 const gchar *icon)
500 if (!tp_strdiff (self->priv->event_icon, icon))
501 return;
503 g_free (self->priv->event_icon);
504 self->priv->event_icon = g_strdup (icon);
506 update_presence_icon (self);
509 GdkPixbuf *
510 empathy_roster_contact_get_avatar_pixbuf (EmpathyRosterContact *self)
512 return gtk_image_get_pixbuf (GTK_IMAGE (self->priv->avatar));