Merge branch 'gnome-3-6'
[empathy-mirror.git] / libempathy-gtk / empathy-roster-contact.c
blob7ab8087465d3827e607ee376bba15d057c11d4cd
1 #include "config.h"
3 #include <glib/gi18n-lib.h>
5 #include "empathy-roster-contact.h"
7 #include <telepathy-glib/telepathy-glib.h>
9 #include <libempathy/empathy-utils.h>
11 #include <libempathy-gtk/empathy-images.h>
12 #include <libempathy-gtk/empathy-ui-utils.h>
14 G_DEFINE_TYPE (EmpathyRosterContact, empathy_roster_contact, GTK_TYPE_ALIGNMENT)
16 #define AVATAR_SIZE 48
18 enum
20 PROP_INDIVIDIUAL = 1,
21 PROP_GROUP,
22 PROP_ONLINE,
23 PROP_ALIAS,
24 N_PROPS
28 enum
30 LAST_SIGNAL
33 static guint signals[LAST_SIGNAL];
36 struct _EmpathyRosterContactPriv
38 FolksIndividual *individual;
39 gchar *group;
41 GtkWidget *avatar;
42 GtkWidget *first_line_alig;
43 GtkWidget *alias;
44 GtkWidget *presence_msg;
45 GtkWidget *presence_icon;
46 GtkWidget *phone_icon;
48 /* If not NULL, used instead of the individual's presence icon */
49 gchar *event_icon;
51 gboolean online;
54 static const gchar *
55 get_alias (EmpathyRosterContact *self)
57 return folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (
58 self->priv->individual));
61 static void
62 empathy_roster_contact_get_property (GObject *object,
63 guint property_id,
64 GValue *value,
65 GParamSpec *pspec)
67 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
69 switch (property_id)
71 case PROP_INDIVIDIUAL:
72 g_value_set_object (value, self->priv->individual);
73 break;
74 case PROP_GROUP:
75 g_value_set_string (value, self->priv->group);
76 break;
77 case PROP_ONLINE:
78 g_value_set_boolean (value, self->priv->online);
79 break;
80 case PROP_ALIAS:
81 g_value_set_string (value, get_alias (self));
82 break;
83 default:
84 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
85 break;
89 static void
90 empathy_roster_contact_set_property (GObject *object,
91 guint property_id,
92 const GValue *value,
93 GParamSpec *pspec)
95 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
97 switch (property_id)
99 case PROP_INDIVIDIUAL:
100 g_assert (self->priv->individual == NULL); /* construct only */
101 self->priv->individual = g_value_dup_object (value);
102 break;
103 case PROP_GROUP:
104 g_assert (self->priv->group == NULL); /* construct only */
105 self->priv->group = g_value_dup_string (value);
106 break;
107 default:
108 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
109 break;
113 static void
114 avatar_loaded_cb (GObject *source,
115 GAsyncResult *result,
116 gpointer user_data)
118 TpWeakRef *wr = user_data;
119 EmpathyRosterContact *self;
120 GdkPixbuf *pixbuf;
122 self = tp_weak_ref_dup_object (wr);
123 if (self == NULL)
124 goto out;
126 pixbuf = empathy_pixbuf_avatar_from_individual_scaled_finish (
127 FOLKS_INDIVIDUAL (source), result, NULL);
129 if (pixbuf == NULL)
131 pixbuf = empathy_pixbuf_from_icon_name_sized (
132 EMPATHY_IMAGE_AVATAR_DEFAULT, AVATAR_SIZE);
135 gtk_image_set_from_pixbuf (GTK_IMAGE (self->priv->avatar), pixbuf);
136 g_object_unref (pixbuf);
138 g_object_unref (self);
140 out:
141 tp_weak_ref_destroy (wr);
144 static void
145 update_avatar (EmpathyRosterContact *self)
147 empathy_pixbuf_avatar_from_individual_scaled_async (self->priv->individual,
148 AVATAR_SIZE, AVATAR_SIZE, NULL, avatar_loaded_cb,
149 tp_weak_ref_new (self, NULL, NULL));
152 static void
153 avatar_changed_cb (FolksIndividual *individual,
154 GParamSpec *spec,
155 EmpathyRosterContact *self)
157 update_avatar (self);
160 static void
161 update_alias (EmpathyRosterContact *self)
163 gtk_label_set_text (GTK_LABEL (self->priv->alias), get_alias (self));
165 g_object_notify (G_OBJECT (self), "alias");
168 static void
169 alias_changed_cb (FolksIndividual *individual,
170 GParamSpec *spec,
171 EmpathyRosterContact *self)
173 update_alias (self);
176 static void
177 update_presence_msg (EmpathyRosterContact *self)
179 const gchar *msg;
180 GStrv types;
182 msg = folks_presence_details_get_presence_message (
183 FOLKS_PRESENCE_DETAILS (self->priv->individual));
185 if (tp_str_empty (msg))
187 /* Just display the alias in the center of the row */
188 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
189 0, 0.5, 1, 1);
191 gtk_widget_hide (self->priv->presence_msg);
193 else
195 FolksPresenceType type;
197 type = folks_presence_details_get_presence_type (
198 FOLKS_PRESENCE_DETAILS (self->priv->individual));
199 if (type == FOLKS_PRESENCE_TYPE_ERROR)
201 gchar *tmp;
203 /* Add a prefix explaining that something goes wrong when trying to
204 * fetch contact's presence. */
205 tmp = g_strdup_printf (_("Server cannot find contact: %s"), msg);
206 gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), tmp);
208 g_free (tmp);
210 else
212 gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), msg);
215 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
216 0, 0.75, 1, 1);
217 gtk_misc_set_alignment (GTK_MISC (self->priv->presence_msg), 0, 0.25);
219 gtk_widget_show (self->priv->presence_msg);
222 types = (GStrv) empathy_individual_get_client_types (self->priv->individual);
224 gtk_widget_set_visible (self->priv->phone_icon,
225 empathy_client_types_contains_mobile_device (types));
228 static void
229 presence_message_changed_cb (FolksIndividual *individual,
230 GParamSpec *spec,
231 EmpathyRosterContact *self)
233 update_presence_msg (self);
236 static void
237 update_presence_icon (EmpathyRosterContact *self)
239 const gchar *icon;
241 if (self->priv->event_icon == NULL)
242 icon = empathy_icon_name_for_individual (self->priv->individual);
243 else
244 icon = self->priv->event_icon;
246 gtk_image_set_from_icon_name (GTK_IMAGE (self->priv->presence_icon), icon,
247 GTK_ICON_SIZE_MENU);
250 static void
251 update_online (EmpathyRosterContact *self)
253 FolksPresenceType presence;
254 gboolean online;
256 presence = folks_presence_details_get_presence_type (
257 FOLKS_PRESENCE_DETAILS (self->priv->individual));
259 switch (presence)
261 case FOLKS_PRESENCE_TYPE_UNSET:
262 case FOLKS_PRESENCE_TYPE_OFFLINE:
263 case FOLKS_PRESENCE_TYPE_UNKNOWN:
264 case FOLKS_PRESENCE_TYPE_ERROR:
265 online = FALSE;
266 break;
268 case FOLKS_PRESENCE_TYPE_AVAILABLE:
269 case FOLKS_PRESENCE_TYPE_AWAY:
270 case FOLKS_PRESENCE_TYPE_EXTENDED_AWAY:
271 case FOLKS_PRESENCE_TYPE_HIDDEN:
272 case FOLKS_PRESENCE_TYPE_BUSY:
273 online = TRUE;
274 break;
276 default:
277 g_warning ("Unknown FolksPresenceType: %d", presence);
278 online = FALSE;
281 if (self->priv->online == online)
282 return;
284 self->priv->online = online;
285 g_object_notify (G_OBJECT (self), "online");
288 static void
289 presence_status_changed_cb (FolksIndividual *individual,
290 GParamSpec *spec,
291 EmpathyRosterContact *self)
293 update_presence_icon (self);
294 update_online (self);
297 static void
298 empathy_roster_contact_constructed (GObject *object)
300 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
301 void (*chain_up) (GObject *) =
302 ((GObjectClass *) empathy_roster_contact_parent_class)->constructed;
304 if (chain_up != NULL)
305 chain_up (object);
307 g_assert (FOLKS_IS_INDIVIDUAL (self->priv->individual));
309 tp_g_signal_connect_object (self->priv->individual, "notify::avatar",
310 G_CALLBACK (avatar_changed_cb), self, 0);
311 tp_g_signal_connect_object (self->priv->individual, "notify::alias",
312 G_CALLBACK (alias_changed_cb), self, 0);
313 tp_g_signal_connect_object (self->priv->individual,
314 "notify::presence-message",
315 G_CALLBACK (presence_message_changed_cb), self, 0);
316 tp_g_signal_connect_object (self->priv->individual, "notify::presence-status",
317 G_CALLBACK (presence_status_changed_cb), self, 0);
319 update_avatar (self);
320 update_alias (self);
321 update_presence_msg (self);
322 update_presence_icon (self);
324 update_online (self);
327 static void
328 empathy_roster_contact_dispose (GObject *object)
330 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
331 void (*chain_up) (GObject *) =
332 ((GObjectClass *) empathy_roster_contact_parent_class)->dispose;
334 g_clear_object (&self->priv->individual);
336 if (chain_up != NULL)
337 chain_up (object);
340 static void
341 empathy_roster_contact_finalize (GObject *object)
343 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
344 void (*chain_up) (GObject *) =
345 ((GObjectClass *) empathy_roster_contact_parent_class)->finalize;
347 g_free (self->priv->group);
348 g_free (self->priv->event_icon);
350 if (chain_up != NULL)
351 chain_up (object);
354 static void
355 empathy_roster_contact_class_init (
356 EmpathyRosterContactClass *klass)
358 GObjectClass *oclass = G_OBJECT_CLASS (klass);
359 GParamSpec *spec;
361 oclass->get_property = empathy_roster_contact_get_property;
362 oclass->set_property = empathy_roster_contact_set_property;
363 oclass->constructed = empathy_roster_contact_constructed;
364 oclass->dispose = empathy_roster_contact_dispose;
365 oclass->finalize = empathy_roster_contact_finalize;
367 spec = g_param_spec_object ("individual", "Individual",
368 "FolksIndividual",
369 FOLKS_TYPE_INDIVIDUAL,
370 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
371 g_object_class_install_property (oclass, PROP_INDIVIDIUAL, spec);
373 spec = g_param_spec_string ("group", "Group",
374 "Group of this widget, or NULL",
375 NULL,
376 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
377 g_object_class_install_property (oclass, PROP_GROUP, spec);
379 spec = g_param_spec_boolean ("online", "Online",
380 "TRUE if Individual is online",
381 FALSE,
382 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
383 g_object_class_install_property (oclass, PROP_ONLINE, spec);
385 spec = g_param_spec_string ("alias", "Alias",
386 "The Alias of the individual displayed in the widget",
387 NULL,
388 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
389 g_object_class_install_property (oclass, PROP_ALIAS, spec);
391 g_type_class_add_private (klass, sizeof (EmpathyRosterContactPriv));
394 static void
395 empathy_roster_contact_init (EmpathyRosterContact *self)
397 GtkWidget *main_box, *box, *first_line_box;
398 GtkStyleContext *context;
400 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
401 EMPATHY_TYPE_ROSTER_CONTACT, EmpathyRosterContactPriv);
403 main_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
405 /* Avatar */
406 self->priv->avatar = gtk_image_new ();
408 gtk_widget_set_size_request (self->priv->avatar, AVATAR_SIZE, AVATAR_SIZE);
410 gtk_box_pack_start (GTK_BOX (main_box), self->priv->avatar, FALSE, FALSE, 0);
411 gtk_widget_show (self->priv->avatar);
413 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
415 /* Alias and phone icon */
416 self->priv->first_line_alig = gtk_alignment_new (0, 0.5, 1, 1);
417 first_line_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
419 self->priv->alias = gtk_label_new (NULL);
420 gtk_label_set_ellipsize (GTK_LABEL (self->priv->alias), PANGO_ELLIPSIZE_END);
421 gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->alias,
422 FALSE, FALSE, 0);
423 gtk_misc_set_alignment (GTK_MISC (self->priv->alias), 0, 0.5);
424 gtk_widget_show (self->priv->alias);
426 self->priv->phone_icon = gtk_image_new_from_icon_name ("phone-symbolic",
427 GTK_ICON_SIZE_MENU);
428 gtk_misc_set_alignment (GTK_MISC (self->priv->phone_icon), 0, 0.5);
429 gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->phone_icon,
430 TRUE, TRUE, 0);
432 gtk_container_add (GTK_CONTAINER (self->priv->first_line_alig),
433 first_line_box);
434 gtk_widget_show (self->priv->first_line_alig);
436 gtk_box_pack_start (GTK_BOX (box), self->priv->first_line_alig,
437 TRUE, TRUE, 0);
438 gtk_widget_show (first_line_box);
440 gtk_box_pack_start (GTK_BOX (main_box), box, TRUE, TRUE, 0);
441 gtk_widget_show (box);
443 /* Presence */
444 self->priv->presence_msg = gtk_label_new (NULL);
445 gtk_label_set_ellipsize (GTK_LABEL (self->priv->presence_msg),
446 PANGO_ELLIPSIZE_END);
447 gtk_box_pack_start (GTK_BOX (box), self->priv->presence_msg, TRUE, TRUE, 0);
448 gtk_widget_show (self->priv->presence_msg);
450 context = gtk_widget_get_style_context (self->priv->presence_msg);
451 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
453 /* Presence icon */
454 self->priv->presence_icon = gtk_image_new ();
456 gtk_box_pack_start (GTK_BOX (main_box), self->priv->presence_icon,
457 FALSE, FALSE, 0);
458 gtk_widget_show (self->priv->presence_icon);
460 gtk_container_add (GTK_CONTAINER (self), main_box);
461 gtk_widget_show (main_box);
464 GtkWidget *
465 empathy_roster_contact_new (FolksIndividual *individual,
466 const gchar *group)
468 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
470 return g_object_new (EMPATHY_TYPE_ROSTER_CONTACT,
471 "individual", individual,
472 "group", group,
473 "bottom-padding", 4,
474 "top-padding", 4,
475 "left-padding", 4,
476 "right-padding", 12,
477 NULL);
480 FolksIndividual *
481 empathy_roster_contact_get_individual (EmpathyRosterContact *self)
483 return self->priv->individual;
486 gboolean
487 empathy_roster_contact_is_online (EmpathyRosterContact *self)
489 return self->priv->online;
492 const gchar *
493 empathy_roster_contact_get_group (EmpathyRosterContact *self)
495 return self->priv->group;
498 void
499 empathy_roster_contact_set_event_icon (EmpathyRosterContact *self,
500 const gchar *icon)
502 if (!tp_strdiff (self->priv->event_icon, icon))
503 return;
505 g_free (self->priv->event_icon);
506 self->priv->event_icon = g_strdup (icon);
508 update_presence_icon (self);
511 GdkPixbuf *
512 empathy_roster_contact_get_avatar_pixbuf (EmpathyRosterContact *self)
514 return gtk_image_get_pixbuf (GTK_IMAGE (self->priv->avatar));