Help: Use stable 'if' namespace instead of experimental
[empathy-mirror.git] / libempathy-gtk / empathy-roster-contact.c
blobd98ca13fca064cc8c6810687e1d3d6d9cd5bd08a
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 PROP_MOST_RECENT_EVENT,
22 N_PROPS
26 enum
28 LAST_SIGNAL
31 static guint signals[LAST_SIGNAL];
34 struct _EmpathyRosterContactPriv
36 FolksIndividual *individual;
37 EmpathyContact *contact;
38 gchar *group;
40 TplLogManager *log_manager;
41 TplEvent *most_recent_event;
43 GtkWidget *avatar;
44 GtkWidget *first_line_alig;
45 GtkWidget *alias;
46 GtkWidget *presence_msg;
47 GtkWidget *most_recent_msg;
48 GtkWidget *presence_icon;
49 GtkWidget *phone_icon;
51 /* If not NULL, used instead of the individual's presence icon */
52 gchar *event_icon;
54 gboolean online;
57 static const gchar *
58 get_alias (EmpathyRosterContact *self)
60 return folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (
61 self->priv->individual));
64 static void
65 empathy_roster_contact_get_property (GObject *object,
66 guint property_id,
67 GValue *value,
68 GParamSpec *pspec)
70 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
72 switch (property_id)
74 case PROP_INDIVIDIUAL:
75 g_value_set_object (value, self->priv->individual);
76 break;
77 case PROP_GROUP:
78 g_value_set_string (value, self->priv->group);
79 break;
80 case PROP_ONLINE:
81 g_value_set_boolean (value, self->priv->online);
82 break;
83 case PROP_ALIAS:
84 g_value_set_string (value, get_alias (self));
85 break;
86 case PROP_MOST_RECENT_EVENT:
87 g_value_set_object (value, self->priv->most_recent_event);
88 break;
89 default:
90 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
91 break;
95 static void
96 empathy_roster_contact_set_property (GObject *object,
97 guint property_id,
98 const GValue *value,
99 GParamSpec *pspec)
101 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
103 switch (property_id)
105 case PROP_INDIVIDIUAL:
106 g_assert (self->priv->individual == NULL); /* construct only */
107 self->priv->individual = g_value_dup_object (value);
108 break;
109 case PROP_GROUP:
110 g_assert (self->priv->group == NULL); /* construct only */
111 self->priv->group = g_value_dup_string (value);
112 break;
113 default:
114 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
115 break;
119 gint64
120 empathy_roster_contact_get_most_recent_timestamp (EmpathyRosterContact *contact)
122 if (contact->priv->most_recent_event) {
123 return tpl_event_get_timestamp (contact->priv->most_recent_event);
125 return 0;
128 static const gchar*
129 get_most_recent_message (EmpathyRosterContact *contact)
131 if (contact->priv->most_recent_event) {
132 return tpl_text_event_get_message (TPL_TEXT_EVENT(contact->priv->most_recent_event));
134 return NULL;
137 static void
138 avatar_loaded_cb (GObject *source,
139 GAsyncResult *result,
140 gpointer user_data)
142 TpWeakRef *wr = user_data;
143 EmpathyRosterContact *self;
144 GdkPixbuf *pixbuf;
146 self = tp_weak_ref_dup_object (wr);
147 if (self == NULL)
148 goto out;
150 pixbuf = empathy_pixbuf_avatar_from_individual_scaled_finish (
151 FOLKS_INDIVIDUAL (source), result, NULL);
153 if (pixbuf == NULL)
155 pixbuf = tpaw_pixbuf_from_icon_name_sized (
156 TPAW_IMAGE_AVATAR_DEFAULT, AVATAR_SIZE);
159 gtk_image_set_from_pixbuf (GTK_IMAGE (self->priv->avatar), pixbuf);
160 g_object_unref (pixbuf);
162 g_object_unref (self);
164 out:
165 tp_weak_ref_destroy (wr);
168 static void
169 update_most_recent_msg (EmpathyRosterContact *self)
171 const gchar* msg = get_most_recent_message (self);
173 if (tp_str_empty (msg))
175 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
176 0, 0.5, 1, 1);
177 gtk_widget_hide (self->priv->most_recent_msg);
179 else
181 gchar *tmp = g_strdup (msg);
182 if (strchr(tmp, '\n')) strchr(tmp, '\n')[0] = 0;
183 gtk_label_set_text (GTK_LABEL (self->priv->most_recent_msg), tmp);
184 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
185 0, 0.75, 1, 1);
186 gtk_misc_set_alignment (GTK_MISC (self->priv->most_recent_msg), 0, 0.25);
187 g_free (tmp);
191 static void
192 update_avatar (EmpathyRosterContact *self)
194 empathy_pixbuf_avatar_from_individual_scaled_async (self->priv->individual,
195 AVATAR_SIZE, AVATAR_SIZE, NULL, avatar_loaded_cb,
196 tp_weak_ref_new (self, NULL, NULL));
199 static void
200 avatar_changed_cb (FolksIndividual *individual,
201 GParamSpec *spec,
202 EmpathyRosterContact *self)
204 update_avatar (self);
207 static void
208 update_alias (EmpathyRosterContact *self)
210 gtk_label_set_text (GTK_LABEL (self->priv->alias), get_alias (self));
212 g_object_notify (G_OBJECT (self), "alias");
215 static void
216 alias_changed_cb (FolksIndividual *individual,
217 GParamSpec *spec,
218 EmpathyRosterContact *self)
220 update_alias (self);
223 static void
224 update_presence_msg (EmpathyRosterContact *self)
226 const gchar *msg;
227 GStrv types;
229 msg = folks_presence_details_get_presence_message (
230 FOLKS_PRESENCE_DETAILS (self->priv->individual));
232 if (tp_str_empty (msg))
234 /* Just display the alias in the center of the row */
235 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
236 0, 0.5, 1, 1);
238 gtk_widget_hide (self->priv->presence_msg);
240 else
242 FolksPresenceType type;
244 type = folks_presence_details_get_presence_type (
245 FOLKS_PRESENCE_DETAILS (self->priv->individual));
246 if (type == FOLKS_PRESENCE_TYPE_ERROR)
248 gchar *tmp;
250 /* Add a prefix explaining that something goes wrong when trying to
251 * fetch contact's presence. */
252 tmp = g_strdup_printf (_("Server cannot find contact: %s"), msg);
253 gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), tmp);
255 g_free (tmp);
257 else
259 gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), msg);
262 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
263 0, 0.75, 1, 1);
264 gtk_misc_set_alignment (GTK_MISC (self->priv->presence_msg), 0, 0.25);
266 gtk_widget_show (self->priv->presence_msg);
269 types = (GStrv) empathy_individual_get_client_types (self->priv->individual);
271 gtk_widget_set_visible (self->priv->phone_icon,
272 empathy_client_types_contains_mobile_device (types));
275 static void
276 presence_message_changed_cb (FolksIndividual *individual,
277 GParamSpec *spec,
278 EmpathyRosterContact *self)
280 update_presence_msg (self);
283 static void
284 update_presence_icon (EmpathyRosterContact *self)
286 const gchar *icon;
288 if (self->priv->event_icon == NULL)
289 icon = empathy_icon_name_for_individual (self->priv->individual);
290 else
291 icon = self->priv->event_icon;
293 gtk_image_set_from_icon_name (GTK_IMAGE (self->priv->presence_icon), icon,
294 GTK_ICON_SIZE_MENU);
297 static void
298 update_online (EmpathyRosterContact *self)
300 FolksPresenceType presence;
301 gboolean online;
303 presence = folks_presence_details_get_presence_type (
304 FOLKS_PRESENCE_DETAILS (self->priv->individual));
306 switch (presence)
308 case FOLKS_PRESENCE_TYPE_UNSET:
309 case FOLKS_PRESENCE_TYPE_OFFLINE:
310 case FOLKS_PRESENCE_TYPE_UNKNOWN:
311 case FOLKS_PRESENCE_TYPE_ERROR:
312 online = FALSE;
313 break;
315 case FOLKS_PRESENCE_TYPE_AVAILABLE:
316 case FOLKS_PRESENCE_TYPE_AWAY:
317 case FOLKS_PRESENCE_TYPE_EXTENDED_AWAY:
318 case FOLKS_PRESENCE_TYPE_HIDDEN:
319 case FOLKS_PRESENCE_TYPE_BUSY:
320 online = TRUE;
321 break;
323 default:
324 g_warning ("Unknown FolksPresenceType: %d", presence);
325 online = FALSE;
328 if (self->priv->online == online)
329 return;
331 self->priv->online = online;
332 g_object_notify (G_OBJECT (self), "online");
335 static void
336 presence_status_changed_cb (FolksIndividual *individual,
337 GParamSpec *spec,
338 EmpathyRosterContact *self)
340 update_presence_icon (self);
341 update_online (self);
344 static void
345 get_filtered_events (GObject *source_object, GAsyncResult *res, gpointer user_data)
347 EmpathyRosterContact *contact = EMPATHY_ROSTER_CONTACT (user_data);
348 GError *error;
349 GList *events;
351 error = NULL;
352 if (!tpl_log_manager_get_filtered_events_finish (contact->priv->log_manager, res, &events, &error))
354 g_warning ("Unable to get events: %s", error->message);
355 g_error_free (error);
356 goto out;
359 if (events) {
360 contact->priv->most_recent_event = TPL_EVENT (events->data);
361 g_object_notify (G_OBJECT (contact), "most-recent-event");
362 update_most_recent_msg (contact);
365 out:
366 return;
369 static void
370 empathy_roster_contact_constructed (GObject *object)
372 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
373 TplEntity *tpl_entity;
374 void (*chain_up) (GObject *) =
375 ((GObjectClass *) empathy_roster_contact_parent_class)->constructed;
377 if (chain_up != NULL)
378 chain_up (object);
380 g_assert (FOLKS_IS_INDIVIDUAL (self->priv->individual));
382 self->priv->contact = empathy_contact_dup_best_for_action (
383 self->priv->individual,
384 EMPATHY_ACTION_CHAT);
386 self->priv->log_manager = tpl_log_manager_dup_singleton ();
388 tpl_entity = tpl_entity_new_from_tp_contact (
389 empathy_contact_get_tp_contact (self->priv->contact),
390 TPL_ENTITY_CONTACT);
391 tpl_log_manager_get_filtered_events_async(
392 self->priv->log_manager,
393 empathy_contact_get_account (self->priv->contact),
394 tpl_entity,
395 TPL_EVENT_MASK_TEXT,
397 NULL,
398 NULL,
399 get_filtered_events,
400 object);
402 tp_g_signal_connect_object (self->priv->individual, "notify::avatar",
403 G_CALLBACK (avatar_changed_cb), self, 0);
404 tp_g_signal_connect_object (self->priv->individual, "notify::alias",
405 G_CALLBACK (alias_changed_cb), self, 0);
406 tp_g_signal_connect_object (self->priv->individual,
407 "notify::presence-message",
408 G_CALLBACK (presence_message_changed_cb), self, 0);
409 tp_g_signal_connect_object (self->priv->individual, "notify::presence-status",
410 G_CALLBACK (presence_status_changed_cb), self, 0);
412 update_avatar (self);
413 update_alias (self);
414 update_presence_msg (self);
415 update_presence_icon (self);
417 update_online (self);
420 static void
421 empathy_roster_contact_dispose (GObject *object)
423 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
424 void (*chain_up) (GObject *) =
425 ((GObjectClass *) empathy_roster_contact_parent_class)->dispose;
427 g_clear_object (&self->priv->individual);
429 if (chain_up != NULL)
430 chain_up (object);
433 static void
434 empathy_roster_contact_finalize (GObject *object)
436 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
437 void (*chain_up) (GObject *) =
438 ((GObjectClass *) empathy_roster_contact_parent_class)->finalize;
440 g_free (self->priv->group);
441 g_free (self->priv->event_icon);
442 g_object_unref (self->priv->log_manager);
444 if (chain_up != NULL)
445 chain_up (object);
448 static void
449 empathy_roster_contact_class_init (
450 EmpathyRosterContactClass *klass)
452 GObjectClass *oclass = G_OBJECT_CLASS (klass);
453 GParamSpec *spec;
455 oclass->get_property = empathy_roster_contact_get_property;
456 oclass->set_property = empathy_roster_contact_set_property;
457 oclass->constructed = empathy_roster_contact_constructed;
458 oclass->dispose = empathy_roster_contact_dispose;
459 oclass->finalize = empathy_roster_contact_finalize;
461 spec = g_param_spec_object ("individual", "Individual",
462 "FolksIndividual",
463 FOLKS_TYPE_INDIVIDUAL,
464 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
465 g_object_class_install_property (oclass, PROP_INDIVIDIUAL, spec);
467 spec = g_param_spec_string ("group", "Group",
468 "Group of this widget, or NULL",
469 NULL,
470 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
471 g_object_class_install_property (oclass, PROP_GROUP, spec);
473 spec = g_param_spec_boolean ("online", "Online",
474 "TRUE if Individual is online",
475 FALSE,
476 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
477 g_object_class_install_property (oclass, PROP_ONLINE, spec);
479 spec = g_param_spec_string ("alias", "Alias",
480 "The Alias of the individual displayed in the widget",
481 NULL,
482 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
483 g_object_class_install_property (oclass, PROP_ALIAS, spec);
485 spec = g_param_spec_object ("most-recent-event", "Most recent event",
486 "Most recent event",
487 TPL_TYPE_EVENT,
488 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
489 g_object_class_install_property (oclass, PROP_MOST_RECENT_EVENT, spec);
491 g_type_class_add_private (klass, sizeof (EmpathyRosterContactPriv));
494 static void
495 empathy_roster_contact_init (EmpathyRosterContact *self)
497 GtkWidget *alig, *main_box, *box, *first_line_box;
498 GtkStyleContext *context;
500 self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
501 EMPATHY_TYPE_ROSTER_CONTACT, EmpathyRosterContactPriv);
503 alig = gtk_alignment_new (0.5, 0.5, 1, 1);
504 gtk_widget_show (alig);
505 gtk_alignment_set_padding (GTK_ALIGNMENT (alig), 4, 4, 4, 12);
507 main_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 8);
509 /* Avatar */
510 self->priv->avatar = gtk_image_new ();
512 gtk_widget_set_size_request (self->priv->avatar, AVATAR_SIZE, AVATAR_SIZE);
514 gtk_box_pack_start (GTK_BOX (main_box), self->priv->avatar, FALSE, FALSE, 0);
515 gtk_widget_show (self->priv->avatar);
517 box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
519 /* Alias and phone icon */
520 self->priv->first_line_alig = gtk_alignment_new (0, 0.5, 1, 1);
521 first_line_box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 0);
523 self->priv->alias = gtk_label_new (NULL);
524 gtk_label_set_ellipsize (GTK_LABEL (self->priv->alias), PANGO_ELLIPSIZE_END);
525 gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->alias,
526 FALSE, FALSE, 0);
527 gtk_misc_set_alignment (GTK_MISC (self->priv->alias), 0, 0.5);
528 gtk_widget_show (self->priv->alias);
530 self->priv->phone_icon = gtk_image_new_from_icon_name ("phone-symbolic",
531 GTK_ICON_SIZE_MENU);
532 gtk_misc_set_alignment (GTK_MISC (self->priv->phone_icon), 0, 0.5);
533 gtk_box_pack_start (GTK_BOX (first_line_box), self->priv->phone_icon,
534 TRUE, TRUE, 0);
536 gtk_container_add (GTK_CONTAINER (self->priv->first_line_alig),
537 first_line_box);
538 gtk_widget_show (self->priv->first_line_alig);
540 gtk_box_pack_start (GTK_BOX (box), self->priv->first_line_alig,
541 TRUE, TRUE, 0);
542 gtk_widget_show (first_line_box);
544 gtk_box_pack_start (GTK_BOX (main_box), box, TRUE, TRUE, 0);
545 gtk_widget_show (box);
547 /* Presence */
548 self->priv->presence_msg = gtk_label_new (NULL);
549 gtk_label_set_ellipsize (GTK_LABEL (self->priv->presence_msg),
550 PANGO_ELLIPSIZE_END);
552 gtk_box_pack_start (GTK_BOX (box), self->priv->presence_msg, TRUE, TRUE, 0);
553 gtk_widget_show (self->priv->presence_msg);
556 context = gtk_widget_get_style_context (self->priv->presence_msg);
557 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
559 /* Most recent message */
560 self->priv->most_recent_msg = gtk_label_new (NULL);
561 gtk_label_set_ellipsize (GTK_LABEL (self->priv->most_recent_msg),
562 PANGO_ELLIPSIZE_END);
563 gtk_box_pack_start (GTK_BOX (box), self->priv->most_recent_msg, TRUE, TRUE, 0);
564 gtk_widget_show (self->priv->most_recent_msg);
566 context = gtk_widget_get_style_context (self->priv->most_recent_msg);
567 gtk_style_context_add_class (context, GTK_STYLE_CLASS_DIM_LABEL);
569 /* Presence icon */
570 self->priv->presence_icon = gtk_image_new ();
572 gtk_box_pack_start (GTK_BOX (main_box), self->priv->presence_icon,
573 FALSE, FALSE, 0);
574 gtk_widget_show (self->priv->presence_icon);
576 gtk_container_add (GTK_CONTAINER (self), alig);
577 gtk_container_add (GTK_CONTAINER (alig), main_box);
578 gtk_widget_show (main_box);
581 GtkWidget *
582 empathy_roster_contact_new (FolksIndividual *individual,
583 const gchar *group)
585 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
587 return g_object_new (EMPATHY_TYPE_ROSTER_CONTACT,
588 "individual", individual,
589 "group", group,
590 NULL);
593 FolksIndividual *
594 empathy_roster_contact_get_individual (EmpathyRosterContact *self)
596 return self->priv->individual;
599 EmpathyContact *
600 empathy_roster_contact_get_contact (EmpathyRosterContact *self)
602 return self->priv->contact;
605 gboolean
606 empathy_roster_contact_is_online (EmpathyRosterContact *self)
608 return self->priv->online;
611 const gchar *
612 empathy_roster_contact_get_group (EmpathyRosterContact *self)
614 return self->priv->group;
617 void
618 empathy_roster_contact_set_event_icon (EmpathyRosterContact *self,
619 const gchar *icon)
621 if (!tp_strdiff (self->priv->event_icon, icon))
622 return;
624 g_free (self->priv->event_icon);
625 self->priv->event_icon = g_strdup (icon);
627 update_presence_icon (self);
630 GdkPixbuf *
631 empathy_roster_contact_get_avatar_pixbuf (EmpathyRosterContact *self)
633 return gtk_image_get_pixbuf (GTK_IMAGE (self->priv->avatar));