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
21 PROP_MOST_RECENT_EVENT,
31 static guint signals[LAST_SIGNAL];
34 struct _EmpathyRosterContactPriv
36 FolksIndividual *individual;
37 EmpathyContact *contact;
40 TplLogManager *log_manager;
41 TplEvent *most_recent_event;
44 GtkWidget *first_line_alig;
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 */
58 get_alias (EmpathyRosterContact *self)
60 return folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (
61 self->priv->individual));
65 empathy_roster_contact_get_property (GObject *object,
70 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
74 case PROP_INDIVIDIUAL:
75 g_value_set_object (value, self->priv->individual);
78 g_value_set_string (value, self->priv->group);
81 g_value_set_boolean (value, self->priv->online);
84 g_value_set_string (value, get_alias (self));
86 case PROP_MOST_RECENT_EVENT:
87 g_value_set_object (value, self->priv->most_recent_event);
90 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
96 empathy_roster_contact_set_property (GObject *object,
101 EmpathyRosterContact *self = EMPATHY_ROSTER_CONTACT (object);
105 case PROP_INDIVIDIUAL:
106 g_assert (self->priv->individual == NULL); /* construct only */
107 self->priv->individual = g_value_dup_object (value);
110 g_assert (self->priv->group == NULL); /* construct only */
111 self->priv->group = g_value_dup_string (value);
114 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
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);
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));
138 avatar_loaded_cb (GObject *source,
139 GAsyncResult *result,
142 TpWeakRef *wr = user_data;
143 EmpathyRosterContact *self;
146 self = tp_weak_ref_dup_object (wr);
150 pixbuf = empathy_pixbuf_avatar_from_individual_scaled_finish (
151 FOLKS_INDIVIDUAL (source), result, 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);
165 tp_weak_ref_destroy (wr);
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),
177 gtk_widget_hide (self->priv->most_recent_msg);
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),
186 gtk_misc_set_alignment (GTK_MISC (self->priv->most_recent_msg), 0, 0.25);
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));
200 avatar_changed_cb (FolksIndividual *individual,
202 EmpathyRosterContact *self)
204 update_avatar (self);
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");
216 alias_changed_cb (FolksIndividual *individual,
218 EmpathyRosterContact *self)
224 update_presence_msg (EmpathyRosterContact *self)
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),
238 gtk_widget_hide (self->priv->presence_msg);
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)
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);
259 gtk_label_set_text (GTK_LABEL (self->priv->presence_msg), msg);
262 gtk_alignment_set (GTK_ALIGNMENT (self->priv->first_line_alig),
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));
276 presence_message_changed_cb (FolksIndividual *individual,
278 EmpathyRosterContact *self)
280 update_presence_msg (self);
284 update_presence_icon (EmpathyRosterContact *self)
288 if (self->priv->event_icon == NULL)
289 icon = empathy_icon_name_for_individual (self->priv->individual);
291 icon = self->priv->event_icon;
293 gtk_image_set_from_icon_name (GTK_IMAGE (self->priv->presence_icon), icon,
298 update_online (EmpathyRosterContact *self)
300 FolksPresenceType presence;
303 presence = folks_presence_details_get_presence_type (
304 FOLKS_PRESENCE_DETAILS (self->priv->individual));
308 case FOLKS_PRESENCE_TYPE_UNSET:
309 case FOLKS_PRESENCE_TYPE_OFFLINE:
310 case FOLKS_PRESENCE_TYPE_UNKNOWN:
311 case FOLKS_PRESENCE_TYPE_ERROR:
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:
324 g_warning ("Unknown FolksPresenceType: %d", presence);
328 if (self->priv->online == online)
331 self->priv->online = online;
332 g_object_notify (G_OBJECT (self), "online");
336 presence_status_changed_cb (FolksIndividual *individual,
338 EmpathyRosterContact *self)
340 update_presence_icon (self);
341 update_online (self);
345 get_filtered_events (GObject *source_object, GAsyncResult *res, gpointer user_data)
347 EmpathyRosterContact *contact = EMPATHY_ROSTER_CONTACT (user_data);
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);
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);
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)
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),
391 tpl_log_manager_get_filtered_events_async(
392 self->priv->log_manager,
393 empathy_contact_get_account (self->priv->contact),
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);
414 update_presence_msg (self);
415 update_presence_icon (self);
417 update_online (self);
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)
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)
449 empathy_roster_contact_class_init (
450 EmpathyRosterContactClass *klass)
452 GObjectClass *oclass = G_OBJECT_CLASS (klass);
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",
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",
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",
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",
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",
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));
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);
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,
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",
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,
536 gtk_container_add (GTK_CONTAINER (self->priv->first_line_alig),
538 gtk_widget_show (self->priv->first_line_alig);
540 gtk_box_pack_start (GTK_BOX (box), self->priv->first_line_alig,
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);
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);
570 self->priv->presence_icon = gtk_image_new ();
572 gtk_box_pack_start (GTK_BOX (main_box), self->priv->presence_icon,
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);
582 empathy_roster_contact_new (FolksIndividual *individual,
585 g_return_val_if_fail (FOLKS_IS_INDIVIDUAL (individual), NULL);
587 return g_object_new (EMPATHY_TYPE_ROSTER_CONTACT,
588 "individual", individual,
594 empathy_roster_contact_get_individual (EmpathyRosterContact *self)
596 return self->priv->individual;
600 empathy_roster_contact_get_contact (EmpathyRosterContact *self)
602 return self->priv->contact;
606 empathy_roster_contact_is_online (EmpathyRosterContact *self)
608 return self->priv->online;
612 empathy_roster_contact_get_group (EmpathyRosterContact *self)
614 return self->priv->group;
618 empathy_roster_contact_set_event_icon (EmpathyRosterContact *self,
621 if (!tp_strdiff (self->priv->event_icon, icon))
624 g_free (self->priv->event_icon);
625 self->priv->event_icon = g_strdup (icon);
627 update_presence_icon (self);
631 empathy_roster_contact_get_avatar_pixbuf (EmpathyRosterContact *self)
633 return gtk_image_get_pixbuf (GTK_IMAGE (self->priv->avatar));