#include "empathy-groups-widget.h"
#include "empathy-gtk-enum-types.h"
#include "empathy-individual-widget.h"
-#include "empathy-kludge-label.h"
#include "empathy-string-parser.h"
#include "empathy-ui-utils.h"
EmpathyIndividualWidgetFlags flags;
/* weak pointer to the contact whose contact details we're displaying */
- TpContact *contact_info_contact;
+ TpContact *contact;
/* unowned Persona (borrowed from priv->individual) -> GtkTable child */
GHashTable *persona_tables;
/* Groups */
GtkWidget *groups_widget;
+ /* Client types */
+ GtkWidget *hbox_client_types;
+
/* Details */
GtkWidget *vbox_details;
GtkWidget *table_details;
PROP_FLAGS
};
+static void client_types_update (EmpathyIndividualWidget *self);
+static void remove_weak_contact (EmpathyIndividualWidget *self);
+
static void
details_set_up (EmpathyIndividualWidget *self)
{
return contact_info_field_name_cmp (field1->field_name, field2->field_name);
}
+static void
+update_weak_contact (EmpathyIndividualWidget *self)
+{
+ EmpathyIndividualWidgetPriv *priv = GET_PRIV (self);
+ TpContact *tp_contact = NULL;
+
+ remove_weak_contact (self);
+
+ if (priv->individual != NULL)
+ {
+ /* FIXME: We take the most available TpContact we find and only
+ * use its details. It would be a lot better if we would get the
+ * details for every TpContact in the Individual and merge them
+ * all, but that requires vCard support in libfolks for it to
+ * not be hideously complex. (bgo#627399) */
+ GList *personas, *l;
+ FolksPresenceType presence_type = FOLKS_PRESENCE_TYPE_UNSET;
+
+ personas = folks_individual_get_personas (priv->individual);
+ for (l = personas; l != NULL; l = l->next)
+ {
+ FolksPresence *presence;
+
+ /* We only want personas which implement FolksPresence */
+ if (!FOLKS_IS_PRESENCE (l->data))
+ continue;
+
+ presence = FOLKS_PRESENCE (l->data);
+
+ if (folks_presence_typecmp (folks_presence_get_presence_type (presence),
+ presence_type) > 0
+ && TPF_IS_PERSONA (presence))
+ {
+ presence_type = folks_presence_get_presence_type (presence);
+ tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
+ }
+ }
+ }
+
+ if (tp_contact != NULL)
+ {
+ priv->contact = tp_contact;
+ g_object_add_weak_pointer (G_OBJECT (tp_contact),
+ (gpointer *) &priv->contact);
+ }
+}
+
typedef struct {
EmpathyIndividualWidget *widget; /* weak */
TpContact *contact; /* owned */
g_free (markup);
}
- if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP))
- gtk_label_set_selectable (GTK_LABEL (w), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (w),
+ (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP) ? FALSE : TRUE);
gtk_table_attach_defaults (GTK_TABLE (priv->table_details),
w, 1, 2, n_rows, n_rows + 1);
tp_clear_object (&priv->details_cancellable);
- /* We need a (weak) pointer to the contact so that we can disconnect the
- * signal handler on deconstruction. */
- if (priv->contact_info_contact != NULL)
- {
- g_object_remove_weak_pointer (G_OBJECT (priv->contact_info_contact),
- (gpointer *) &priv->contact_info_contact);
- }
-
- priv->contact_info_contact = contact;
- g_object_add_weak_pointer (G_OBJECT (contact),
- (gpointer *) &priv->contact_info_contact);
-
g_signal_connect (contact, "notify::contact-info",
(GCallback) details_notify_cb, self);
}
DetailsData *data)
{
EmpathyIndividualWidget *self = data->widget;
- EmpathyIndividualWidgetPriv *priv = GET_PRIV (self);
+ EmpathyIndividualWidgetPriv *priv = NULL;
- if (tp_proxy_prepare_finish (connection, res, NULL) == FALSE)
+ if (tp_proxy_prepare_finish (connection, res, NULL) == FALSE || self == NULL)
{
- gtk_widget_hide (priv->vbox_details);
+ if (self != NULL)
+ gtk_widget_hide (GET_PRIV (self)->vbox_details);
details_data_free (data);
return;
}
+ priv = GET_PRIV (self);
+
/* Request the Individual's info */
gtk_widget_show (priv->vbox_details);
gtk_widget_show (priv->hbox_details_requested);
gtk_widget_hide (priv->vbox_details);
- if (priv->individual != NULL)
- {
- /* FIXME: We take the first TpContact we find and only use its details.
- * It would be a lot better if we would get the details for every
- * TpContact in the Individual and merge them all, but that requires
- * vCard support in libfolks for it to not be hideously complex.
- * (bgo#627399) */
- GList *personas, *l;
-
- personas = folks_individual_get_personas (priv->individual);
- for (l = personas; l != NULL; l = l->next)
- {
- if (TPF_IS_PERSONA (l->data))
- {
- tp_contact = tpf_persona_get_contact (TPF_PERSONA (l->data));
- if (tp_contact != NULL)
- break;
- }
- }
- }
+ if (priv->contact == NULL)
+ update_weak_contact (self);
- if (tp_contact != NULL)
+ if (priv->contact != NULL)
{
GQuark features[] = { TP_CONNECTION_FEATURE_CONTACT_INFO, 0 };
TpConnection *connection;
data = g_slice_new (DetailsData);
data->widget = self;
g_object_add_weak_pointer (G_OBJECT (self), (gpointer *) &data->widget);
- data->contact = g_object_ref (tp_contact);
+ data->contact = g_object_ref (priv->contact);
/* First, make sure the CONTACT_INFO feature is ready on the connection */
connection = tp_contact_get_connection (tp_contact);
{
empathy_groups_widget_set_groupable (
EMPATHY_GROUPS_WIDGET (priv->groups_widget),
- FOLKS_GROUPS (priv->individual));
+ FOLKS_GROUPABLE (priv->individual));
gtk_widget_show (priv->groups_widget);
}
else
gboolean display_map = FALSE;
GList *personas, *l;
- if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION))
+ if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_LOCATION) ||
+ priv->individual == NULL)
{
gtk_widget_hide (priv->vbox_location);
return;
gchar *text;
gint64 stamp;
time_t time_;
+ gchar *tmp;
stamp = g_value_get_int64 (value);
time_ = stamp;
user_date = empathy_time_to_string_relative (time_);
- text = g_strconcat ( _("<b>Location</b>, "), user_date, NULL);
+ tmp = g_strdup_printf ("<b>%s</b>", _("Location"));
+ /* translators: format is "Location, $date" */
+ text = g_strdup_printf (_("%s, %s"), tmp, user_date);
+ g_free (tmp);
gtk_label_set_markup (GTK_LABEL (priv->label_location), text);
g_free (user_date);
g_free (text);
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
gtk_widget_show (label);
- if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP))
- gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (label),
+ (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP) ? FALSE :
+ TRUE);
}
g_free (svalue);
/* Add a marker to the map */
marker = champlain_marker_new_with_text (
- folks_alias_get_alias (FOLKS_ALIAS (persona)), NULL, NULL,
- NULL);
+ folks_aliasable_get_alias (FOLKS_ALIASABLE (persona)), NULL,
+ NULL, NULL);
champlain_base_marker_set_position (
CHAMPLAIN_BASE_MARKER (marker), lat, lon);
clutter_container_add (CLUTTER_CONTAINER (layer), marker, NULL);
gtk_widget_show (priv->vbox_location);
}
+static void
+client_types_notify_cb (TpContact *contact,
+ GParamSpec *pspec,
+ EmpathyIndividualWidget *self)
+{
+ client_types_update (self);
+}
+
+static void
+client_types_update (EmpathyIndividualWidget *self)
+{
+ EmpathyIndividualWidgetPriv *priv = GET_PRIV (self);
+ const gchar * const *types;
+
+ if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_CLIENT_TYPES) ||
+ priv->individual == NULL)
+ {
+ gtk_widget_hide (priv->hbox_client_types);
+ return;
+ }
+
+ if (priv->contact == NULL)
+ update_weak_contact (self);
+
+ /* let's try that again... */
+ if (priv->contact == NULL)
+ return;
+
+ types = tp_contact_get_client_types (priv->contact);
+
+ if (types != NULL
+ && g_strv_length ((gchar **) types) > 0
+ && !tp_strdiff (types[0], "phone"))
+ {
+ gtk_widget_show (priv->hbox_client_types);
+ }
+ else
+ {
+ gtk_widget_hide (priv->hbox_client_types);
+ }
+
+ g_signal_connect (priv->contact, "notify::client-types",
+ (GCallback) client_types_notify_cb, self);
+}
+
+static void
+remove_weak_contact (EmpathyIndividualWidget *self)
+{
+ EmpathyIndividualWidgetPriv *priv = GET_PRIV (self);
+
+ if (priv->contact == NULL)
+ return;
+
+ g_object_remove_weak_pointer (G_OBJECT (priv->contact),
+ (gpointer *) &priv->contact);
+ priv->contact = NULL;
+}
+
static EmpathyAvatar *
persona_dup_avatar (FolksPersona *persona)
{
}
else
{
- folks_alias_set_alias (FOLKS_ALIAS (priv->individual), alias);
+ folks_aliasable_set_alias (FOLKS_ALIASABLE (priv->individual), alias);
}
}
if (GTK_IS_ENTRY (alias_widget))
{
gtk_entry_set_text (GTK_ENTRY (alias_widget),
- folks_alias_get_alias (FOLKS_ALIAS (folks_object)));
+ folks_aliasable_get_alias (FOLKS_ALIASABLE (folks_object)));
}
else
{
gtk_label_set_label (GTK_LABEL (alias_widget),
- folks_alias_get_alias (FOLKS_ALIAS (folks_object)));
+ folks_aliasable_get_alias (FOLKS_ALIASABLE (folks_object)));
}
}
else
{
alias = gtk_label_new (NULL);
- if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP))
- gtk_label_set_selectable (GTK_LABEL (alias), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (alias),
+ (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP) ? FALSE : TRUE);
gtk_misc_set_alignment (GTK_MISC (alias), 0.0, 0.5);
}
FALSE, 0);
gtk_widget_show (image);
- /* Set up status_label as a KludgeLabel */
- label = empathy_kludge_label_new ("");
+ label = gtk_label_new ("");
gtk_label_set_line_wrap_mode (GTK_LABEL (label), PANGO_WRAP_WORD_CHAR);
gtk_label_set_line_wrap (GTK_LABEL (label), TRUE);
+ gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
gtk_label_set_selectable (GTK_LABEL (label),
(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP) ? FALSE : TRUE);
if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_EDIT_FAVOURITE)
notify_is_favourite_cb (persona, NULL, self);
+
+ g_object_unref (contact);
}
static void
hbox = GTK_BOX (gtk_hbox_new (FALSE, 6));
account_label = gtk_label_new (NULL);
- gtk_label_set_selectable (GTK_LABEL (account_label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (account_label),
+ (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP) ? FALSE : TRUE);
gtk_misc_set_alignment (GTK_MISC (account_label), 0.0, 0.5);
gtk_widget_show (account_label);
/* Set up ID label */
label = gtk_label_new (NULL);
- gtk_label_set_selectable (GTK_LABEL (label), TRUE);
+ gtk_label_set_selectable (GTK_LABEL (label),
+ (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP) ? FALSE : TRUE);
gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
g_object_set_data (G_OBJECT (table), "id-widget", label);
}
static void
-notify_personas_cb (FolksIndividual *individual,
- GParamSpec *pspec,
+personas_changed_cb (FolksIndividual *individual,
+ GList *added,
+ GList *removed,
EmpathyIndividualWidget *self)
{
EmpathyIndividualWidgetPriv *priv = GET_PRIV (self);
- GList *personas, *l, *children, *remove_personas = NULL;
- GHashTableIter iter;
- FolksPersona *persona;
- GtkTable *table;
- gboolean is_last;
- guint old_num_personas, new_num_personas;
+ GList *personas, *l, *children;
+ gboolean show_personas, was_showing_personas, will_show_personas, is_last;
+ guint old_num_personas, new_num_personas = 0;
personas = folks_individual_get_personas (individual);
+
+ /* Note that old_num_personas is the number of persona tables we were
+ * displaying, not the number of Personas which were in the Individual
+ * before. */
old_num_personas = g_hash_table_size (priv->persona_tables);
- new_num_personas = g_list_length (personas);
-
- /* Remove Personas */
- g_hash_table_iter_init (&iter, priv->persona_tables);
- while (g_hash_table_iter_next (&iter, (gpointer *) &persona,
- (gpointer *) &table))
- {
- /* FIXME: This is slow. bgo#626725 */
- /* Old persona or we were displaying all personas and now just want to
- * display the individual table.
- * We can't remove the persona inside this loop, as that would invalidate
- * the hash table iter. */
- if (g_list_find (personas, persona) == NULL ||
- (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_PERSONAS) &&
- new_num_personas > 1))
- {
- remove_personas = g_list_prepend (remove_personas, persona);
- }
+
+ for (l = personas; l != NULL; l = l->next)
+ {
+ if (TPF_IS_PERSONA (l->data))
+ new_num_personas++;
}
- for (l = remove_personas; l != NULL; l = l->next)
- remove_persona (self, FOLKS_PERSONA (l->data));
- g_list_free (remove_personas);
+ /*
+ * What we display for various conditions:
+ * - "Personas": display the alias, avatar, presence account and identifier
+ * for each of the Individual's Personas. (i.e. One table per
+ * Persona.)
+ * - "Individual": display the alias, avatar and presence for the Individual,
+ * and a label saying "Meta-contact containing x contacts".
+ * (i.e. One table in total.)
+ *
+ * | SHOW_PERSONAS | !SHOW_PERSONAS
+ * -------------+---------------+---------------
+ * > 1 Persona | Personas | Individual
+ * -------------+---------------+---------------
+ * == 1 Persona | Personas | Personas
+ */
+ show_personas = (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_PERSONAS) != 0;
+ was_showing_personas = show_personas || old_num_personas == 1;
+ will_show_personas = show_personas || new_num_personas == 1;
+
+ /* If both @added and @removed are NULL, we're being called manually, and we
+ * need to set up the tables for the first time. We do this simply by
+ * ensuring was_showing_personas and will_show_personas are different so that
+ * the code resets the UI.
+ */
+ if (added == NULL && removed == NULL)
+ was_showing_personas = !will_show_personas;
- individual_table_destroy (self);
+ if (was_showing_personas && will_show_personas)
+ {
+ /* Remove outdated Personas */
+ for (l = removed; l != NULL; l = l->next)
+ remove_persona (self, FOLKS_PERSONA (l->data));
- /* If we're !SHOW_PERSONAS and have more than one Persona, we only display
- * the Individual's alias, avatar and presence, and a label saying
- * "Meta-contact containing x contacts". (i.e. One table.)
- * If we're SHOW_PERSONAS or have only one Persona, we display the
- * alias, avatar, presence, account and identifier for each of the
- * Individual's Personas. (i.e. One table per Persona.) */
- if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_PERSONAS) &&
- new_num_personas > 1)
+ /* Add new Personas */
+ for (l = added; l != NULL; l = l->next)
+ add_persona (self, FOLKS_PERSONA (l->data));
+ }
+ else if (!was_showing_personas && will_show_personas)
{
- individual_table_set_up (self);
+ /* Remove the old Individual table */
+ individual_table_destroy (self);
+
+ /* Set up all the Persona tables instead */
+ for (l = personas; l != NULL; l = l->next)
+ add_persona (self, FOLKS_PERSONA (l->data));
}
- else
+ else if (was_showing_personas && !will_show_personas)
{
- /* Add Personas */
+ /* Remove all Personas */
for (l = personas; l != NULL; l = l->next)
- {
- persona = FOLKS_PERSONA (l->data);
-
- if (!TPF_IS_PERSONA (persona))
- continue;
+ remove_persona (self, FOLKS_PERSONA (l->data));
+ for (l = removed; l != NULL; l = l->next)
+ remove_persona (self, FOLKS_PERSONA (l->data));
- /* New persona or we were displaying the individual table and we
- * now want to display all personas */
- if ((!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_PERSONAS) &&
- new_num_personas <= 1) ||
- g_hash_table_lookup (priv->persona_tables, persona) == NULL)
- {
- add_persona (self, persona);
- }
- }
+ /* Set up the Individual table instead */
+ individual_table_set_up (self);
}
/* Hide the last separator and show the others */
g_list_free (children);
}
+static void
+individual_removed_cb (FolksIndividual *individual,
+ FolksIndividual *replacement_individual,
+ EmpathyIndividualWidget *self)
+{
+ empathy_individual_widget_set_individual (self, replacement_individual);
+}
+
static void
remove_individual (EmpathyIndividualWidget *self)
{
g_signal_handlers_disconnect_by_func (priv->individual,
notify_avatar_cb, self);
g_signal_handlers_disconnect_by_func (priv->individual,
- notify_personas_cb, self);
+ personas_changed_cb, self);
+ g_signal_handlers_disconnect_by_func (priv->individual,
+ individual_removed_cb, self);
if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_EDIT_FAVOURITE)
{
personas = folks_individual_get_personas (priv->individual);
for (l = personas; l != NULL; l = l->next)
remove_persona (self, FOLKS_PERSONA (l->data));
+ individual_table_destroy (self);
- if (priv->contact_info_contact != NULL)
- {
- g_signal_handlers_disconnect_by_func (priv->contact_info_contact,
- details_notify_cb, self);
- g_object_remove_weak_pointer (G_OBJECT (priv->contact_info_contact),
- (gpointer *) &priv->contact_info_contact);
- priv->contact_info_contact = NULL;
- }
+ if (priv->contact != NULL)
+ remove_weak_contact (self);
tp_clear_object (&priv->individual);
}
(GCallback) notify_presence_cb, self);
g_signal_connect (priv->individual, "notify::avatar",
(GCallback) notify_avatar_cb, self);
- g_signal_connect (priv->individual, "notify::personas",
- (GCallback) notify_personas_cb, self);
+ g_signal_connect (priv->individual, "personas-changed",
+ (GCallback) personas_changed_cb, self);
+ g_signal_connect (priv->individual, "removed",
+ (GCallback) individual_removed_cb, self);
if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_EDIT_FAVOURITE)
{
}
/* Update individual table */
- notify_personas_cb (priv->individual, NULL, self);
+ personas_changed_cb (priv->individual, NULL, NULL, self);
}
if (priv->individual == NULL)
"vbox_details", &priv->vbox_details,
"table_details", &priv->table_details,
"hbox_details_requested", &priv->hbox_details_requested,
+ "hbox_client_types", &priv->hbox_client_types,
NULL);
g_free (filename);
groups_update (self);
details_update (self);
location_update (self);
+ client_types_update (self);
}