X-Git-Url: https://git.0d.be/?p=empathy.git;a=blobdiff_plain;f=libempathy-gtk%2Fempathy-individual-widget.c;h=215d3218e19ea524af29dfea458fab7f9439782a;hp=7e4be2235ecdaf244b6e05136726368af1ef6a18;hb=006f4b813d176e487e0f720424a6274ad7d726ee;hpb=7373f750b996baae4d229952a4e2a0140820487c diff --git a/libempathy-gtk/empathy-individual-widget.c b/libempathy-gtk/empathy-individual-widget.c index 7e4be223..215d3218 100644 --- a/libempathy-gtk/empathy-individual-widget.c +++ b/libempathy-gtk/empathy-individual-widget.c @@ -42,11 +42,10 @@ #include #include "empathy-avatar-image.h" +#include "empathy-contactinfo-utils.h" #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" #define DEBUG_FLAG EMPATHY_DEBUG_CONTACT @@ -79,12 +78,12 @@ typedef struct { 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; + /* unowned Persona (borrowed from priv->individual) -> GtkGrid child */ + GHashTable *persona_grids; /* Table containing the information for the individual as whole, or NULL */ - GtkTable *individual_table; + GtkGrid *individual_grid; /* Individual */ GtkWidget *hbox_presence; @@ -96,7 +95,7 @@ typedef struct { /* Location */ GtkWidget *vbox_location; GtkWidget *subvbox_location; - GtkWidget *table_location; + GtkWidget *grid_location; GtkWidget *label_location; #ifdef HAVE_LIBCHAMPLAIN GtkWidget *viewport_map; @@ -107,9 +106,12 @@ typedef struct { /* Groups */ GtkWidget *groups_widget; + /* Client types */ + GtkWidget *hbox_client_types; + /* Details */ GtkWidget *vbox_details; - GtkWidget *table_details; + GtkWidget *grid_details; GtkWidget *hbox_details_requested; GtkWidget *details_spinner; GCancellable *details_cancellable; /* owned */ @@ -123,6 +125,9 @@ enum { PROP_FLAGS }; +static void client_types_update (EmpathyIndividualWidget *self); +static void remove_weak_contact (EmpathyIndividualWidget *self); + static void details_set_up (EmpathyIndividualWidget *self) { @@ -136,79 +141,76 @@ details_set_up (EmpathyIndividualWidget *self) gtk_widget_show (priv->details_spinner); } -typedef struct +static void +client_types_notify_cb (TpContact *contact, + GParamSpec *pspec, + EmpathyIndividualWidget *self) { - const gchar *field_name; - const gchar *title; - gboolean linkify; -} InfoFieldData; + client_types_update (self); +} -static InfoFieldData info_field_data[] = -{ - { "fn", N_("Full name:"), FALSE }, - { "tel", N_("Phone number:"), FALSE }, - { "email", N_("E-mail address:"), TRUE }, - { "url", N_("Website:"), TRUE }, - { "bday", N_("Birthday:"), FALSE }, - { NULL, NULL } -}; +typedef struct { + EmpathyIndividualWidget *widget; /* weak */ + TpContact *contact; /* owned */ +} DetailsData; -static InfoFieldData * -find_info_field_data (const gchar *field_name) +static void +update_weak_contact (EmpathyIndividualWidget *self) { - guint i; + EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); + TpContact *tp_contact = NULL; - for (i = 0; info_field_data[i].field_name != NULL; i++) - { - if (tp_strdiff (info_field_data[i].field_name, field_name) == FALSE) - return info_field_data + i; - } - return NULL; -} + remove_weak_contact (self); -static gint -contact_info_field_name_cmp (const gchar *name1, - const gchar *name2) -{ - guint i; + 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) */ + GeeSet *personas; + GeeIterator *iter; + FolksPresenceType presence_type = FOLKS_PRESENCE_TYPE_UNSET; - if (tp_strdiff (name1, name2) == FALSE) - return 0; + personas = folks_individual_get_personas (priv->individual); + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); + while (gee_iterator_next (iter)) + { + FolksPersona *persona = gee_iterator_get (iter); - /* We use the order of info_field_data */ - for (i = 0; info_field_data[i].field_name != NULL; i++) - { - if (tp_strdiff (info_field_data[i].field_name, name1) == FALSE) - return -1; - if (tp_strdiff (info_field_data[i].field_name, name2) == FALSE) - return +1; - } + /* We only want personas which have presence and a TpContact */ + if (empathy_folks_persona_is_interesting (FOLKS_PERSONA (persona))) + { + FolksPresenceDetails *presence; + FolksPresenceType presence_type_cur; - return g_strcmp0 (name1, name2); -} + presence = FOLKS_PRESENCE_DETAILS (persona); + presence_type_cur = folks_presence_details_get_presence_type ( + presence); -static gint -contact_info_field_cmp (TpContactInfoField *field1, - TpContactInfoField *field2) -{ - return contact_info_field_name_cmp (field1->field_name, field2->field_name); -} + if (folks_presence_details_typecmp ( + presence_type_cur, presence_type) > 0) + { + presence_type = presence_type_cur; + tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona)); + } + } -typedef struct { - EmpathyIndividualWidget *widget; /* weak */ - TpContact *contact; /* owned */ -} DetailsData; + g_clear_object (&persona); + } + g_clear_object (&iter); + } -static void -details_data_free (DetailsData *data) -{ - if (data->widget != NULL) + if (tp_contact != NULL) { - g_object_remove_weak_pointer (G_OBJECT (data->widget), - (gpointer *) &data->widget); + priv->contact = tp_contact; + g_object_add_weak_pointer (G_OBJECT (tp_contact), + (gpointer *) &priv->contact); + + g_signal_connect (priv->contact, "notify::client-types", + (GCallback) client_types_notify_cb, self); } - g_object_unref (data->contact); - g_slice_free (DetailsData, data); } static guint @@ -220,12 +222,13 @@ details_update_show (EmpathyIndividualWidget *self, guint n_rows = 0; info = tp_contact_get_contact_info (contact); - info = g_list_sort (info, (GCompareFunc) contact_info_field_cmp); + info = g_list_sort (info, (GCompareFunc) empathy_contact_info_field_cmp); for (l = info; l != NULL; l = l->next) { TpContactInfoField *field = l->data; - InfoFieldData *field_data; + gchar *title; const gchar *value; + EmpathyContactInfoFormatFunc format; GtkWidget *w; if (field->field_value == NULL || field->field_value[0] == NULL) @@ -233,27 +236,31 @@ details_update_show (EmpathyIndividualWidget *self, value = field->field_value[0]; - field_data = find_info_field_data (field->field_name); - if (field_data == NULL) + if (!empathy_contact_info_lookup_field (field->field_name, + NULL, &format)) { DEBUG ("Unhandled ContactInfo field: %s", field->field_name); continue; } /* Add Title */ - w = gtk_label_new (_(field_data->title)); - gtk_table_attach (GTK_TABLE (priv->table_details), - w, 0, 1, n_rows, n_rows + 1, GTK_FILL, 0, 0, 0); + title = empathy_contact_info_field_label (field->field_name, + field->parameters); + w = gtk_label_new (title); + g_free (title); + gtk_grid_attach (GTK_GRID (priv->grid_details), + w, 0, n_rows, 1, 1); gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); gtk_widget_show (w); /* Add Value */ w = gtk_label_new (value); - if (field_data->linkify == TRUE) + + if (format != NULL) { gchar *markup; - markup = empathy_add_link_markup (value); + markup = format (field->field_value); gtk_label_set_markup (GTK_LABEL (w), markup); g_free (markup); } @@ -261,8 +268,8 @@ details_update_show (EmpathyIndividualWidget *self, 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); + gtk_grid_attach (GTK_GRID (priv->grid_details), + w, 1, n_rows, 1, 1); gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); gtk_widget_show (w); @@ -281,7 +288,7 @@ details_notify_cb (TpContact *contact, EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); guint n_rows; - gtk_container_foreach (GTK_CONTAINER (priv->table_details), + gtk_container_foreach (GTK_CONTAINER (priv->grid_details), (GtkCallback) gtk_widget_destroy, NULL); n_rows = details_update_show (self, contact); @@ -289,7 +296,7 @@ details_notify_cb (TpContact *contact, if (n_rows > 0) { gtk_widget_show (priv->vbox_details); - gtk_widget_show (priv->table_details); + gtk_widget_show (priv->grid_details); } else { @@ -301,76 +308,70 @@ details_notify_cb (TpContact *contact, } static void -details_request_cb (TpContact *contact, +details_request_cb (GObject *source, GAsyncResult *res, - DetailsData *data) + gpointer user_data) { - EmpathyIndividualWidget *self = data->widget; + EmpathyIndividualWidget *self = user_data; + EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); + TpContact *contact = (TpContact *) source; gboolean hide_widget = FALSE; GError *error = NULL; if (tp_contact_request_contact_info_finish (contact, res, &error) == TRUE) - details_notify_cb (contact, NULL, self); + { + details_notify_cb (contact, NULL, self); + } else - hide_widget = TRUE; - - g_clear_error (&error); - - if (self != NULL) { - EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - - if (hide_widget == TRUE) - gtk_widget_hide (GET_PRIV (self)->vbox_details); - - 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) + /* If the request got cancelled it could mean the contact widget is + * destroyed, so we should not dereference information */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { - g_object_remove_weak_pointer (G_OBJECT (priv->contact_info_contact), - (gpointer *) &priv->contact_info_contact); + g_error_free (error); + return; } - 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); + hide_widget = TRUE; + g_error_free (error); } - details_data_free (data); + if (hide_widget == TRUE) + gtk_widget_hide (GET_PRIV (self)->vbox_details); + + tp_clear_object (&priv->details_cancellable); + + tp_g_signal_connect_object (contact, "notify::contact-info", + (GCallback) details_notify_cb, self, 0); } static void -details_feature_prepared_cb (TpConnection *connection, - GAsyncResult *res, - DetailsData *data) +fetch_contact_information (EmpathyIndividualWidget *self) { - EmpathyIndividualWidget *self = data->widget; EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); + TpConnection *connection; - if (tp_proxy_prepare_finish (connection, res, NULL) == FALSE) + connection = tp_contact_get_connection (priv->contact); + + if (!tp_proxy_has_interface_by_id (connection, + TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO)) { - gtk_widget_hide (priv->vbox_details); - details_data_free (data); + gtk_widget_hide (GET_PRIV (self)->vbox_details); return; } /* Request the Individual's info */ gtk_widget_show (priv->vbox_details); gtk_widget_show (priv->hbox_details_requested); - gtk_widget_hide (priv->table_details); + gtk_widget_hide (priv->grid_details); gtk_spinner_start (GTK_SPINNER (priv->details_spinner)); if (priv->details_cancellable == NULL) { priv->details_cancellable = g_cancellable_new (); - tp_contact_request_contact_info_async (data->contact, - priv->details_cancellable, (GAsyncReadyCallback) details_request_cb, - data); + + tp_contact_request_contact_info_async (priv->contact, + priv->details_cancellable, details_request_cb, self); } } @@ -378,49 +379,18 @@ static void details_update (EmpathyIndividualWidget *self) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - TpContact *tp_contact = NULL; if (!(priv->flags & EMPATHY_INDIVIDUAL_WIDGET_SHOW_DETAILS)) return; 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; + if (priv->contact == NULL) + update_weak_contact (self); - 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 (tp_contact != NULL) + if (priv->contact != NULL) { - GQuark features[] = { TP_CONNECTION_FEATURE_CONTACT_INFO, 0 }; - TpConnection *connection; - DetailsData *data; - - 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); - - /* First, make sure the CONTACT_INFO feature is ready on the connection */ - connection = tp_contact_get_connection (tp_contact); - tp_proxy_prepare_async (connection, features, - (GAsyncReadyCallback) details_feature_prepared_cb, data); + fetch_contact_information (self); } } @@ -432,9 +402,9 @@ groups_update (EmpathyIndividualWidget *self) if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_EDIT_GROUPS && priv->individual != NULL) { - empathy_groups_widget_set_groupable ( + empathy_groups_widget_set_group_details ( EMPATHY_GROUPS_WIDGET (priv->groups_widget), - FOLKS_GROUPS (priv->individual)); + FOLKS_GROUP_DETAILS (priv->individual)); gtk_widget_show (priv->groups_widget); } else @@ -528,9 +498,11 @@ location_update (EmpathyIndividualWidget *self) int i; const gchar *skey; gboolean display_map = FALSE; - GList *personas, *l; + GeeSet *personas; + GeeIterator *iter; - 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; @@ -539,12 +511,14 @@ location_update (EmpathyIndividualWidget *self) /* FIXME: For the moment, we just display the first location data we can * find amongst the Individual's Personas. Once libfolks grows a location * interface, we can use that. (bgo#627400) */ + personas = folks_individual_get_personas (priv->individual); - for (l = personas; l != NULL; l = l->next) + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); + while (location == NULL && gee_iterator_next (iter)) { - FolksPersona *persona = FOLKS_PERSONA (l->data); + FolksPersona *persona = gee_iterator_get (iter); - if (TPF_IS_PERSONA (persona)) + if (empathy_folks_persona_is_interesting (persona)) { TpContact *tp_contact; @@ -552,18 +526,25 @@ location_update (EmpathyIndividualWidget *self) * have to keep it alive for the duration of the function, since we're * accessing its private data. */ tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona)); - contact = empathy_contact_dup_from_tp_contact (tp_contact); - empathy_contact_set_persona (contact, persona); - - /* Try and get a location */ - location = empathy_contact_get_location (contact); - if (location != NULL && g_hash_table_size (location) > 0) - break; + if (tp_contact != NULL) + { + contact = empathy_contact_dup_from_tp_contact (tp_contact); + empathy_contact_set_persona (contact, persona); - location = NULL; - tp_clear_object (&contact); + /* Try and get a location */ + location = empathy_contact_get_location (contact); + /* if location isn't fully valid, treat the contact as + * insufficient */ + if (location != NULL && g_hash_table_size (location) <= 0) + { + location = NULL; + g_clear_object (&contact); + } + } } + g_clear_object (&persona); } + g_clear_object (&iter); if (contact == NULL || location == NULL) { @@ -584,13 +565,11 @@ location_update (EmpathyIndividualWidget *self) gchar *user_date; 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_); + user_date = empathy_time_to_string_relative (stamp); tmp = g_strdup_printf ("%s", _("Location")); /* translators: format is "Location, $date" */ @@ -602,12 +581,12 @@ location_update (EmpathyIndividualWidget *self) } /* Prepare the location information table */ - if (priv->table_location != NULL) - gtk_widget_destroy (priv->table_location); + if (priv->grid_location != NULL) + gtk_widget_destroy (priv->grid_location); - priv->table_location = gtk_table_new (1, 2, FALSE); + priv->grid_location = gtk_grid_new (); gtk_box_pack_start (GTK_BOX (priv->subvbox_location), - priv->table_location, FALSE, FALSE, 5); + priv->grid_location, FALSE, FALSE, 5); for (i = 0; (skey = ordered_geolocation_keys[i]); i++) @@ -624,8 +603,8 @@ location_update (EmpathyIndividualWidget *self) label = gtk_label_new (user_label); gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5); - gtk_table_attach (GTK_TABLE (priv->table_location), - label, 0, 1, row, row + 1, GTK_FILL, GTK_FILL, 10, 0); + gtk_grid_attach (GTK_GRID (priv->grid_location), + label, 0, row, 1, 1); gtk_widget_show (label); if (G_VALUE_TYPE (gvalue) == G_TYPE_DOUBLE) @@ -640,7 +619,7 @@ location_update (EmpathyIndividualWidget *self) } else if (G_VALUE_TYPE (gvalue) == G_TYPE_INT64) { - time_t time_; + gint64 time_; time_ = g_value_get_int64 (value); svalue = empathy_time_to_string_utc (time_, _("%B %e, %Y at %R UTC")); @@ -649,8 +628,8 @@ location_update (EmpathyIndividualWidget *self) if (svalue != NULL) { label = gtk_label_new (svalue); - gtk_table_attach_defaults (GTK_TABLE (priv->table_location), - label, 1, 2, row, row + 1); + gtk_grid_attach (GTK_GRID (priv->grid_location), + label, 1, row, 1, 1); gtk_misc_set_alignment (GTK_MISC (label), 0, 0); gtk_widget_show (label); @@ -679,7 +658,7 @@ location_update (EmpathyIndividualWidget *self) if (row > 0) { /* We can display some fields */ - gtk_widget_show (priv->table_location); + gtk_widget_show (priv->grid_location); } else if (display_map == FALSE) { @@ -691,8 +670,7 @@ location_update (EmpathyIndividualWidget *self) #ifdef HAVE_LIBCHAMPLAIN if (display_map == TRUE) { - GPtrArray *markers; - ChamplainLayer *layer; + ChamplainMarkerLayer *layer; priv->map_view_embed = gtk_champlain_embed_new (); priv->map_view = gtk_champlain_embed_get_view ( @@ -701,23 +679,23 @@ location_update (EmpathyIndividualWidget *self) gtk_container_add (GTK_CONTAINER (priv->viewport_map), priv->map_view_embed); g_object_set (G_OBJECT (priv->map_view), - "show-license", TRUE, - "scroll-mode", CHAMPLAIN_SCROLL_MODE_KINETIC, + "kinetic-mode", TRUE, "zoom-level", 10, NULL); - layer = champlain_layer_new (); - champlain_view_add_layer (priv->map_view, layer); - markers = g_ptr_array_new (); + layer = champlain_marker_layer_new (); + champlain_view_add_layer (priv->map_view, CHAMPLAIN_LAYER (layer)); /* FIXME: For now, we have to do this manually. Once libfolks grows a * location interface, we can use that. (bgo#627400) */ + personas = folks_individual_get_personas (priv->individual); - for (l = personas; l != NULL; l = l->next) + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); + while (gee_iterator_next (iter)) { - FolksPersona *persona = FOLKS_PERSONA (l->data); + FolksPersona *persona = gee_iterator_get (iter); - if (TPF_IS_PERSONA (persona)) + if (empathy_folks_persona_is_interesting (persona)) { gdouble lat = 0.0, lon = 0.0; ClutterActor *marker; @@ -725,55 +703,48 @@ location_update (EmpathyIndividualWidget *self) /* Get the contact */ tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona)); + if (tp_contact == NULL) + goto while_finish; + contact = empathy_contact_dup_from_tp_contact (tp_contact); empathy_contact_set_persona (contact, persona); /* Try and get a location */ location = empathy_contact_get_location (contact); if (location == NULL || g_hash_table_size (location) == 0) - { - g_object_unref (contact); - continue; - } + goto while_finish; /* Get this persona's latitude and longitude */ value = g_hash_table_lookup (location, EMPATHY_LOCATION_LAT); if (value == NULL) - { - g_object_unref (contact); - continue; - } + goto while_finish; lat = g_value_get_double (value); value = g_hash_table_lookup (location, EMPATHY_LOCATION_LON); if (value == NULL) - { - g_object_unref (contact); - continue; - } + goto while_finish; lon = g_value_get_double (value); /* Add a marker to the map */ - marker = champlain_marker_new_with_text ( - folks_alias_get_alias (FOLKS_ALIAS (persona)), NULL, NULL, - NULL); - champlain_base_marker_set_position ( - CHAMPLAIN_BASE_MARKER (marker), lat, lon); - clutter_container_add (CLUTTER_CONTAINER (layer), marker, NULL); - - g_ptr_array_add (markers, marker); - - g_object_unref (contact); + marker = champlain_label_new_with_text ( + folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (persona)), + NULL, NULL, NULL); + champlain_location_set_location (CHAMPLAIN_LOCATION (marker), + lat, lon); + champlain_marker_layer_add_marker (layer, + CHAMPLAIN_MARKER (marker)); } + +while_finish: + g_clear_object (&persona); + g_clear_object (&contact); } + g_clear_object (&iter); /* Zoom to show all of the markers */ - g_ptr_array_add (markers, NULL); /* NULL-terminate the array */ - champlain_view_ensure_markers_visible (priv->map_view, - (ChamplainBaseMarker **) markers->pdata, FALSE); - g_ptr_array_free (markers, TRUE); + champlain_view_ensure_layers_visible (priv->map_view, FALSE); gtk_widget_show_all (priv->viewport_map); } @@ -782,6 +753,57 @@ location_update (EmpathyIndividualWidget *self) gtk_widget_show (priv->vbox_location); } +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); + } + +} + +static void +remove_weak_contact (EmpathyIndividualWidget *self) +{ + EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); + + if (priv->contact == NULL) + return; + + g_signal_handlers_disconnect_by_func (priv->contact, client_types_notify_cb, + self); + + g_object_remove_weak_pointer (G_OBJECT (priv->contact), + (gpointer *) &priv->contact); + priv->contact = NULL; +} + static EmpathyAvatar * persona_dup_avatar (FolksPersona *persona) { @@ -789,10 +811,13 @@ persona_dup_avatar (FolksPersona *persona) EmpathyContact *contact; EmpathyAvatar *avatar; - if (!TPF_IS_PERSONA (persona)) + if (!empathy_folks_persona_is_interesting (persona)) return NULL; tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona)); + if (tp_contact == NULL) + return NULL; + contact = empathy_contact_dup_from_tp_contact (tp_contact); empathy_contact_set_persona (contact, persona); @@ -807,20 +832,25 @@ persona_dup_avatar (FolksPersona *persona) static EmpathyAvatar * individual_dup_avatar (FolksIndividual *individual) { - GList *personas, *l; + GeeSet *personas; + GeeIterator *iter; EmpathyAvatar *avatar = NULL; /* FIXME: We just choose the first Persona which has an avatar, and save that. * The avatar handling in EmpathyContact needs to be moved into libfolks as - * much as possible, and this code rewritten to use FolksAvatar. + * much as possible, and this code rewritten to use FolksHasAvatar. * (bgo#627401) */ + personas = folks_individual_get_personas (individual); - for (l = personas; l != NULL; l = l->next) + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); + while (avatar == NULL && gee_iterator_next (iter)) { - avatar = persona_dup_avatar (FOLKS_PERSONA (l->data)); - if (avatar != NULL) - break; + FolksPersona *persona = gee_iterator_get (iter); + avatar = persona_dup_avatar (persona); + + g_clear_object (&persona); } + g_clear_object (&iter); return avatar; } @@ -930,7 +960,7 @@ popup_avatar_menu (EmpathyIndividualWidget *self, return FALSE; empathy_avatar_unref (avatar); - menu = gtk_menu_new (); + menu = empathy_context_menu_new (parent); /* Add "Save as..." entry */ item = gtk_image_menu_item_new_from_stock (GTK_STOCK_SAVE_AS, NULL); @@ -951,10 +981,7 @@ popup_avatar_menu (EmpathyIndividualWidget *self, event_time = gtk_get_current_event_time (); } - gtk_menu_attach_to_widget (GTK_MENU (menu), parent, NULL); gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, button, event_time); - g_object_ref_sink (menu); - g_object_unref (menu); return TRUE; } @@ -983,37 +1010,42 @@ avatar_widget_button_press_event_cb (GtkWidget *widget, static TpAccount * individual_is_user (FolksIndividual *individual) { - GList *personas, *l; + GeeSet *personas; + GeeIterator *iter; + TpAccount *retval = NULL; /* FIXME: This should move into libfolks when libfolks grows a way of * determining "self". (bgo#627402) */ personas = folks_individual_get_personas (individual); - for (l = personas; l != NULL; l = l->next) + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); + while (gee_iterator_next (iter)) { - FolksPersona *persona = FOLKS_PERSONA (l->data); + FolksPersona *persona = gee_iterator_get (iter); if (TPF_IS_PERSONA (persona)) { TpContact *tp_contact; - EmpathyContact *contact; + EmpathyContact *contact = NULL; /* Get the contact */ tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona)); - contact = empathy_contact_dup_from_tp_contact (tp_contact); - empathy_contact_set_persona (contact, persona); - - /* Determine if the contact is the user */ - if (empathy_contact_is_user (contact)) + if (tp_contact != NULL) { - g_object_unref (contact); - return g_object_ref (empathy_contact_get_account (contact)); + contact = empathy_contact_dup_from_tp_contact (tp_contact); + empathy_contact_set_persona (contact, persona); + + /* Determine if the contact is the user */ + if (empathy_contact_is_user (contact)) + retval = g_object_ref (empathy_contact_get_account (contact)); } g_object_unref (contact); } + g_clear_object (&persona); } + g_clear_object (&iter); - return NULL; + return retval; } static void @@ -1054,7 +1086,8 @@ entry_alias_focus_event_cb (GtkEditable *editable, } else { - folks_alias_set_alias (FOLKS_ALIAS (priv->individual), alias); + folks_alias_details_set_alias (FOLKS_ALIAS_DETAILS (priv->individual), + alias); } } @@ -1066,8 +1099,8 @@ favourite_toggled_cb (GtkToggleButton *button, EmpathyIndividualWidget *self) { gboolean active = gtk_toggle_button_get_active (button); - folks_favourite_set_is_favourite ( - FOLKS_FAVOURITE (GET_PRIV (self)->individual), active); + folks_favourite_details_set_is_favourite ( + FOLKS_FAVOURITE_DETAILS (GET_PRIV (self)->individual), active); } static void @@ -1077,28 +1110,28 @@ notify_avatar_cb (gpointer folks_object, { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); EmpathyAvatar *avatar = NULL; - GObject *table; + GObject *grid; GtkWidget *avatar_widget; if (FOLKS_IS_INDIVIDUAL (folks_object)) { avatar = individual_dup_avatar (FOLKS_INDIVIDUAL (folks_object)); - table = G_OBJECT (priv->individual_table); + grid = G_OBJECT (priv->individual_grid); } else if (FOLKS_IS_PERSONA (folks_object)) { avatar = persona_dup_avatar (FOLKS_PERSONA (folks_object)); - table = g_hash_table_lookup (priv->persona_tables, folks_object); + grid = g_hash_table_lookup (priv->persona_grids, folks_object); } else { g_assert_not_reached (); } - if (table == NULL) + if (grid == NULL) return; - avatar_widget = g_object_get_data (table, "avatar-widget"); + avatar_widget = g_object_get_data (grid, "avatar-widget"); empathy_avatar_image_set (EMPATHY_AVATAR_IMAGE (avatar_widget), avatar); if (avatar != NULL) @@ -1111,30 +1144,30 @@ notify_alias_cb (gpointer folks_object, EmpathyIndividualWidget *self) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - GObject *table; + GObject *grid; GtkWidget *alias_widget; if (FOLKS_IS_INDIVIDUAL (folks_object)) - table = G_OBJECT (priv->individual_table); + grid = G_OBJECT (priv->individual_grid); else if (FOLKS_IS_PERSONA (folks_object)) - table = g_hash_table_lookup (priv->persona_tables, folks_object); + grid = g_hash_table_lookup (priv->persona_grids, folks_object); else g_assert_not_reached (); - if (table == NULL) + if (grid == NULL) return; - alias_widget = g_object_get_data (table, "alias-widget"); + alias_widget = g_object_get_data (grid, "alias-widget"); if (GTK_IS_ENTRY (alias_widget)) { gtk_entry_set_text (GTK_ENTRY (alias_widget), - folks_alias_get_alias (FOLKS_ALIAS (folks_object))); + folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (folks_object))); } else { gtk_label_set_label (GTK_LABEL (alias_widget), - folks_alias_get_alias (FOLKS_ALIAS (folks_object))); + folks_alias_details_get_alias (FOLKS_ALIAS_DETAILS (folks_object))); } } @@ -1144,30 +1177,32 @@ notify_presence_cb (gpointer folks_object, EmpathyIndividualWidget *self) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - GObject *table; + GObject *grid; GtkWidget *status_label, *state_image; const gchar *message; gchar *markup_text = NULL; if (FOLKS_IS_INDIVIDUAL (folks_object)) - table = G_OBJECT (priv->individual_table); + grid = G_OBJECT (priv->individual_grid); else if (FOLKS_IS_PERSONA (folks_object)) - table = g_hash_table_lookup (priv->persona_tables, folks_object); + grid = g_hash_table_lookup (priv->persona_grids, folks_object); else g_assert_not_reached (); - if (table == NULL) + if (grid == NULL) return; - status_label = g_object_get_data (table, "status-label"); - state_image = g_object_get_data (table, "state-image"); + status_label = g_object_get_data (grid, "status-label"); + state_image = g_object_get_data (grid, "state-image"); /* FIXME: Default messages should be moved into libfolks (bgo#627403) */ - message = folks_presence_get_presence_message (FOLKS_PRESENCE (folks_object)); + message = folks_presence_details_get_presence_message ( + FOLKS_PRESENCE_DETAILS (folks_object)); if (EMP_STR_EMPTY (message)) { message = empathy_presence_get_default_message ( - folks_presence_get_presence_type (FOLKS_PRESENCE (folks_object))); + folks_presence_details_get_presence_type ( + FOLKS_PRESENCE_DETAILS (folks_object))); } if (message != NULL) @@ -1177,7 +1212,8 @@ notify_presence_cb (gpointer folks_object, gtk_image_set_from_icon_name (GTK_IMAGE (state_image), empathy_icon_name_for_presence ( - folks_presence_get_presence_type (FOLKS_PRESENCE (folks_object))), + folks_presence_details_get_presence_type ( + FOLKS_PRESENCE_DETAILS (folks_object))), GTK_ICON_SIZE_BUTTON); gtk_widget_show (state_image); } @@ -1188,42 +1224,42 @@ notify_is_favourite_cb (gpointer folks_object, EmpathyIndividualWidget *self) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - GObject *table; + GObject *grid; GtkWidget *favourite_widget; if (FOLKS_IS_INDIVIDUAL (folks_object)) - table = G_OBJECT (priv->individual_table); + grid = G_OBJECT (priv->individual_grid); else if (FOLKS_IS_PERSONA (folks_object)) - table = g_hash_table_lookup (priv->persona_tables, folks_object); + grid = g_hash_table_lookup (priv->persona_grids, folks_object); else g_assert_not_reached (); - if (table == NULL) + if (grid == NULL) return; - favourite_widget = g_object_get_data (table, "favourite-widget"); + favourite_widget = g_object_get_data (grid, "favourite-widget"); if (GTK_IS_TOGGLE_BUTTON (favourite_widget)) { gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (favourite_widget), - folks_favourite_get_is_favourite (FOLKS_FAVOURITE (folks_object))); + folks_favourite_details_get_is_favourite ( + FOLKS_FAVOURITE_DETAILS (folks_object))); } } static void alias_presence_avatar_favourite_set_up (EmpathyIndividualWidget *self, - GtkTable *table, + GtkGrid *grid, guint starting_row) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - GtkWidget *label, *alias, *image, *avatar, *alignment; + GtkWidget *label, *alias, *image, *avatar; guint current_row = starting_row; /* Alias */ label = gtk_label_new (_("Alias:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_table_attach (table, label, 0, 1, current_row, current_row + 1, GTK_FILL, - GTK_FILL, 0, 0); + gtk_grid_attach (grid, label, 0, current_row, 1, 1); gtk_widget_show (label); /* Set up alias label/entry */ @@ -1245,9 +1281,9 @@ alias_presence_avatar_favourite_set_up (EmpathyIndividualWidget *self, gtk_misc_set_alignment (GTK_MISC (alias), 0.0, 0.5); } - g_object_set_data (G_OBJECT (table), "alias-widget", alias); - gtk_table_attach (table, alias, 1, 2, current_row, current_row + 1, - GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + g_object_set_data (G_OBJECT (grid), "alias-widget", alias); + gtk_grid_attach_next_to (grid, alias, label, + GTK_POS_RIGHT, 1, 1); gtk_widget_show (alias); current_row++; @@ -1258,26 +1294,26 @@ alias_presence_avatar_favourite_set_up (EmpathyIndividualWidget *self, /* Presence image */ image = gtk_image_new_from_stock (GTK_STOCK_MISSING_IMAGE, GTK_ICON_SIZE_BUTTON); - g_object_set_data (G_OBJECT (table), "state-image", image); + g_object_set_data (G_OBJECT (grid), "state-image", image); gtk_box_pack_start (GTK_BOX (priv->hbox_presence), image, FALSE, 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); - g_object_set_data (G_OBJECT (table), "status-label", label); - gtk_box_pack_start (GTK_BOX (priv->hbox_presence), label, TRUE, - TRUE, 0); + g_object_set_data (G_OBJECT (grid), "status-label", label); + gtk_box_pack_start (GTK_BOX (priv->hbox_presence), label, FALSE, + FALSE, 0); gtk_widget_show (label); - gtk_table_attach (table, priv->hbox_presence, 0, 2, current_row, - current_row + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_grid_attach (grid, priv->hbox_presence, + 0, current_row, 2, 1); gtk_widget_show (priv->hbox_presence); current_row++; @@ -1290,9 +1326,9 @@ alias_presence_avatar_favourite_set_up (EmpathyIndividualWidget *self, g_signal_connect (favourite, "toggled", (GCallback) favourite_toggled_cb, self); - g_object_set_data (G_OBJECT (table), "favourite-widget", favourite); - gtk_table_attach (table, favourite, 0, 2, current_row, current_row + 1, - GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + g_object_set_data (G_OBJECT (grid), "favourite-widget", favourite); + gtk_grid_attach (grid, favourite, + 0, current_row, 2, 1); gtk_widget_show (favourite); current_row++; @@ -1309,15 +1345,18 @@ alias_presence_avatar_favourite_set_up (EmpathyIndividualWidget *self, (GCallback) avatar_widget_button_press_event_cb, self); } - g_object_set_data (G_OBJECT (table), "avatar-widget", avatar); - - alignment = gtk_alignment_new (1.0, 0.0, 0.0, 0.0); - gtk_container_add (GTK_CONTAINER (alignment), avatar); + g_object_set_data (G_OBJECT (grid), "avatar-widget", avatar); + g_object_set (avatar, + "valign", GTK_ALIGN_START, + "margin-left", 6, + "margin-right", 6, + "margin-top", 6, + "margin-bottom", 6, + NULL); + + gtk_grid_attach (grid, avatar, + 2, 0, 1, current_row); gtk_widget_show (avatar); - - gtk_table_attach (table, alignment, 2, 3, 0, current_row, - GTK_FILL | GTK_EXPAND, GTK_FILL | GTK_EXPAND, 6, 6); - gtk_widget_show (alignment); } static void @@ -1327,16 +1366,19 @@ update_persona (EmpathyIndividualWidget *self, FolksPersona *persona) TpContact *tp_contact; EmpathyContact *contact; TpAccount *account; - GtkTable *table; + GtkGrid *grid; GtkLabel *label; GtkImage *image; const gchar *id; - table = g_hash_table_lookup (priv->persona_tables, persona); + grid = g_hash_table_lookup (priv->persona_grids, persona); - g_assert (table != NULL); + g_assert (grid != NULL); tp_contact = tpf_persona_get_contact (TPF_PERSONA (persona)); + if (tp_contact == NULL) + return; + contact = empathy_contact_dup_from_tp_contact (tp_contact); empathy_contact_set_persona (contact, persona); @@ -1347,8 +1389,8 @@ update_persona (EmpathyIndividualWidget *self, FolksPersona *persona) { const gchar *name; - label = g_object_get_data (G_OBJECT (table), "account-label"); - image = g_object_get_data (G_OBJECT (table), "account-image"); + label = g_object_get_data (G_OBJECT (grid), "account-label"); + image = g_object_get_data (G_OBJECT (grid), "account-image"); name = tp_account_get_display_name (account); gtk_label_set_label (label, name); @@ -1358,7 +1400,7 @@ update_persona (EmpathyIndividualWidget *self, FolksPersona *persona) } /* Update id widget */ - label = g_object_get_data (G_OBJECT (table), "id-widget"); + label = g_object_get_data (G_OBJECT (grid), "id-widget"); id = folks_persona_get_display_id (persona); gtk_label_set_label (label, (id != NULL) ? id : ""); @@ -1379,25 +1421,26 @@ add_persona (EmpathyIndividualWidget *self, { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); GtkBox *hbox; - GtkTable *table; + GtkGrid *grid; GtkWidget *label, *account_label, *account_image, *separator; guint current_row = 0; - if (!TPF_IS_PERSONA (persona)) + if (!empathy_folks_persona_is_interesting (persona)) return; if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_EDIT_FAVOURITE) - table = GTK_TABLE (gtk_table_new (5, 3, FALSE)); + grid = GTK_GRID (gtk_grid_new ()); else - table = GTK_TABLE (gtk_table_new (4, 3, FALSE)); - gtk_table_set_row_spacings (table, 6); - gtk_table_set_col_spacings (table, 6); + grid = GTK_GRID (gtk_grid_new ()); + + gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL); + gtk_grid_set_row_spacing (grid, 6); + gtk_grid_set_column_spacing (grid, 6); /* Account and Identifier */ label = gtk_label_new (_("Account:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_table_attach (table, label, 0, 1, current_row, current_row + 1, - GTK_FILL, GTK_FILL, 0, 0); + gtk_grid_attach (grid, label, 0, current_row, 1, 1); gtk_widget_show (label); /* Pack the protocol icon with the account name in an hbox */ @@ -1415,10 +1458,9 @@ add_persona (EmpathyIndividualWidget *self, gtk_box_pack_start (hbox, account_image, FALSE, FALSE, 0); gtk_box_pack_start (hbox, account_label, FALSE, TRUE, 0); - g_object_set_data (G_OBJECT (table), "account-image", account_image); - g_object_set_data (G_OBJECT (table), "account-label", account_label); - gtk_table_attach (table, GTK_WIDGET (hbox), 1, 2, current_row, - current_row + 1, GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + g_object_set_data (G_OBJECT (grid), "account-image", account_image); + g_object_set_data (G_OBJECT (grid), "account-label", account_label); + gtk_grid_attach_next_to (grid, GTK_WIDGET (hbox), label, GTK_POS_RIGHT, 1, 1); gtk_widget_show (GTK_WIDGET (hbox)); current_row++; @@ -1426,8 +1468,7 @@ add_persona (EmpathyIndividualWidget *self, /* Translators: Identifier to connect to Instant Messaging network */ label = gtk_label_new (_("Identifier:")); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); - gtk_table_attach (table, label, 0, 1, current_row, current_row + 1, - GTK_FILL, GTK_FILL, 0, 0); + gtk_grid_attach (grid, label, 0, current_row, 1, 1); gtk_widget_show (label); /* Set up ID label */ @@ -1436,16 +1477,15 @@ add_persona (EmpathyIndividualWidget *self, (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); - gtk_table_attach (table, label, 1, 2, current_row, current_row + 1, - GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + g_object_set_data (G_OBJECT (grid), "id-widget", label); + gtk_grid_attach (grid, label, 1, current_row, 1, 1); gtk_widget_show (label); current_row++; - alias_presence_avatar_favourite_set_up (self, table, current_row); + alias_presence_avatar_favourite_set_up (self, grid, current_row); - /* Connect to signals and display the table */ + /* Connect to signals and display the grid */ g_signal_connect (persona, "notify::alias", (GCallback) notify_alias_cb, self); g_signal_connect (persona, "notify::avatar", @@ -1462,17 +1502,17 @@ add_persona (EmpathyIndividualWidget *self, } gtk_box_pack_start (GTK_BOX (priv->vbox_individual), - GTK_WIDGET (table), FALSE, TRUE, 0); - gtk_widget_show (GTK_WIDGET (table)); + GTK_WIDGET (grid), FALSE, TRUE, 0); + gtk_widget_show (GTK_WIDGET (grid)); - /* Pack a separator after the table */ + /* Pack a separator after the grid */ separator = gtk_hseparator_new (); - g_object_set_data (G_OBJECT (table), "separator", separator); + g_object_set_data (G_OBJECT (grid), "separator", separator); gtk_box_pack_start (GTK_BOX (priv->vbox_individual), separator, FALSE, FALSE, 0); gtk_widget_show (separator); - g_hash_table_replace (priv->persona_tables, persona, table); + g_hash_table_replace (priv->persona_grids, persona, grid); /* Update the new widgets */ update_persona (self, persona); @@ -1484,13 +1524,13 @@ remove_persona (EmpathyIndividualWidget *self, { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); GtkWidget *separator; - GtkTable *table; + GtkGrid *grid; - if (!TPF_IS_PERSONA (persona)) + if (!empathy_folks_persona_is_interesting (persona)) return; - table = g_hash_table_lookup (priv->persona_tables, persona); - if (table == NULL) + grid = g_hash_table_lookup (priv->persona_grids, persona); + if (grid == NULL) return; g_signal_handlers_disconnect_by_func (persona, notify_alias_cb, self); @@ -1504,19 +1544,19 @@ remove_persona (EmpathyIndividualWidget *self, } /* Remove the separator */ - separator = g_object_get_data (G_OBJECT (table), "separator"); + separator = g_object_get_data (G_OBJECT (grid), "separator"); if (separator != NULL) gtk_container_remove (GTK_CONTAINER (priv->vbox_individual), separator); /* Remove the widget */ gtk_container_remove (GTK_CONTAINER (priv->vbox_individual), - GTK_WIDGET (table)); + GTK_WIDGET (grid)); - g_hash_table_remove (priv->persona_tables, persona); + g_hash_table_remove (priv->persona_grids, persona); } static void -update_individual_table (EmpathyIndividualWidget *self) +update_individual_grid (EmpathyIndividualWidget *self) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); @@ -1529,110 +1569,120 @@ update_individual_table (EmpathyIndividualWidget *self) } static void -individual_table_set_up (EmpathyIndividualWidget *self) +individual_grid_set_up (EmpathyIndividualWidget *self) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - GtkTable *table; guint current_row = 0; - guint nb_rows = 2; - - if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_EDIT_FAVOURITE) - nb_rows++; - - if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP) - nb_rows++; + GtkGrid *grid; - table = GTK_TABLE (gtk_table_new (nb_rows, 3, FALSE)); - gtk_table_set_row_spacings (table, 6); - gtk_table_set_col_spacings (table, 6); + grid = GTK_GRID (gtk_grid_new ()); + gtk_orientable_set_orientation (GTK_ORIENTABLE (grid), GTK_ORIENTATION_VERTICAL); + gtk_grid_set_row_spacing (grid, 6); + gtk_grid_set_column_spacing (grid, 6); /* We only display the number of personas in tooltips */ if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_FOR_TOOLTIP) { gchar *message; GtkWidget *label; - GList *personas, *l; + GeeSet *personas; + GeeIterator *iter; guint num_personas = 0; /* Meta-contacts message displaying how many Telepathy personas we have */ personas = folks_individual_get_personas (priv->individual); - for (l = personas; l != NULL; l = l->next) + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); + while (gee_iterator_next (iter)) { - if (TPF_IS_PERSONA (l->data)) + FolksPersona *persona = gee_iterator_get (iter); + if (empathy_folks_persona_is_interesting (persona)) num_personas++; - } - message = g_strdup_printf (ngettext ("Meta-contact containing %u contact", - "Meta-contact containing %u contacts", num_personas), num_personas); + g_clear_object (&persona); + } + g_clear_object (&iter); + + /* Translators: the plurality applies to both instances of the word + * "contact" */ + message = g_strdup_printf ( + ngettext ("Linked contact containing %u contact", + "Linked contacts containing %u contacts", num_personas), + num_personas); label = gtk_label_new (message); gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5); g_free (message); - gtk_table_attach (table, label, 0, 2, current_row, current_row + 1, - GTK_FILL | GTK_EXPAND, GTK_FILL, 0, 0); + gtk_grid_attach (grid, label, 0, current_row, 2, 1); gtk_widget_show (label); current_row++; } - alias_presence_avatar_favourite_set_up (self, table, current_row); + alias_presence_avatar_favourite_set_up (self, grid, current_row); - /* Display the table */ - gtk_box_pack_start (GTK_BOX (priv->vbox_individual), GTK_WIDGET (table), + /* Display the grid */ + gtk_box_pack_start (GTK_BOX (priv->vbox_individual), GTK_WIDGET (grid), FALSE, TRUE, 0); - gtk_widget_show (GTK_WIDGET (table)); + gtk_widget_show (GTK_WIDGET (grid)); - priv->individual_table = table; + priv->individual_grid = grid; - /* Update the table */ - update_individual_table (self); + /* Update the grid */ + update_individual_grid (self); } static void -individual_table_destroy (EmpathyIndividualWidget *self) +individual_grid_destroy (EmpathyIndividualWidget *self) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - if (priv->individual_table == NULL) + if (priv->individual_grid == NULL) return; gtk_container_remove (GTK_CONTAINER (priv->vbox_individual), - GTK_WIDGET (priv->individual_table)); - priv->individual_table = NULL; + GTK_WIDGET (priv->individual_grid)); + priv->individual_grid = NULL; } static void personas_changed_cb (FolksIndividual *individual, - GList *added, - GList *removed, + GeeSet *added, + GeeSet *removed, EmpathyIndividualWidget *self) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); - GList *personas, *l, *children; + GList *l, *children; + GeeSet *personas; + GeeIterator *iter; 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); + /* we'll re-use this iterator throughout */ + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); - /* Note that old_num_personas is the number of persona tables we were + /* Note that old_num_personas is the number of persona gridss we were * displaying, not the number of Personas which were in the Individual * before. */ - old_num_personas = g_hash_table_size (priv->persona_tables); + old_num_personas = g_hash_table_size (priv->persona_grids); - for (l = personas; l != NULL; l = l->next) + while (gee_iterator_next (iter)) { - if (TPF_IS_PERSONA (l->data)) + FolksPersona *persona = gee_iterator_get (iter); + if (empathy_folks_persona_is_interesting (persona)) new_num_personas++; + + g_clear_object (&persona); } /* * 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 + * for each of the Individual's Personas. (i.e. One grid 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.) + * (i.e. One grid in total.) * * | SHOW_PERSONAS | !SHOW_PERSONAS * -------------+---------------+--------------- @@ -1645,7 +1695,7 @@ personas_changed_cb (FolksIndividual *individual, 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 + * need to set up the gridss 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. */ @@ -1654,32 +1704,73 @@ personas_changed_cb (FolksIndividual *individual, if (was_showing_personas && will_show_personas) { + GeeIterator *iter_changed; + /* Remove outdated Personas */ - for (l = removed; l != NULL; l = l->next) - remove_persona (self, FOLKS_PERSONA (l->data)); + iter_changed = gee_iterable_iterator (GEE_ITERABLE (removed)); + while (gee_iterator_next (iter_changed)) + { + FolksPersona *persona = gee_iterator_get (iter_changed); + remove_persona (self, persona); + g_clear_object (&persona); + } + g_clear_object (&iter_changed); /* Add new Personas */ - for (l = added; l != NULL; l = l->next) - add_persona (self, FOLKS_PERSONA (l->data)); + iter_changed = gee_iterable_iterator (GEE_ITERABLE (added)); + while (gee_iterator_next (iter_changed)) + { + FolksPersona *persona = gee_iterator_get (iter_changed); + add_persona (self, persona); + g_clear_object (&persona); + } + g_clear_object (&iter_changed); } else if (!was_showing_personas && will_show_personas) { - /* Remove the old Individual table */ - individual_table_destroy (self); + gboolean c; - /* Set up all the Persona tables instead */ - for (l = personas; l != NULL; l = l->next) - add_persona (self, FOLKS_PERSONA (l->data)); + /* Remove the old Individual grid */ + individual_grid_destroy (self); + + /* Set up all the Persona grids instead */ + for (c = gee_iterator_first (iter); c; c = gee_iterator_next (iter)) + { + FolksPersona *persona = gee_iterator_get (iter); + add_persona (self, persona); + g_clear_object (&persona); + } } else if (was_showing_personas && !will_show_personas) { + gboolean c; + /* Remove all Personas */ - for (l = personas; l != NULL; l = l->next) - remove_persona (self, FOLKS_PERSONA (l->data)); + for (c = gee_iterator_first (iter); c; c = gee_iterator_next (iter)) + { + FolksPersona *persona = gee_iterator_get (iter); + remove_persona (self, persona); + g_clear_object (&persona); + } - /* Set up the Individual table instead */ - individual_table_set_up (self); + if (removed != NULL) + { + GeeIterator *iter_changed; + + iter_changed = gee_iterable_iterator (GEE_ITERABLE (removed)); + while (gee_iterator_next (iter_changed)) + { + FolksPersona *persona = gee_iterator_get (iter_changed); + remove_persona (self, persona); + g_clear_object (&persona); + } + g_clear_object (&iter_changed); + } + + /* Set up the Individual grid instead */ + individual_grid_set_up (self); } + g_clear_object (&iter); /* Hide the last separator and show the others */ children = gtk_container_get_children (GTK_CONTAINER (priv->vbox_individual)); @@ -1698,13 +1789,22 @@ personas_changed_cb (FolksIndividual *individual, 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) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (self); if (priv->individual != NULL) { - GList *personas, *l; + GeeSet *personas; + GeeIterator *iter; g_signal_handlers_disconnect_by_func (priv->individual, notify_alias_cb, self); @@ -1714,6 +1814,8 @@ remove_individual (EmpathyIndividualWidget *self) notify_avatar_cb, self); g_signal_handlers_disconnect_by_func (priv->individual, personas_changed_cb, self); + g_signal_handlers_disconnect_by_func (priv->individual, + individual_removed_cb, self); if (priv->flags & EMPATHY_INDIVIDUAL_WIDGET_EDIT_FAVOURITE) { @@ -1722,18 +1824,18 @@ remove_individual (EmpathyIndividualWidget *self) } 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) + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); + while (gee_iterator_next (iter)) { - 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; + FolksPersona *persona = gee_iterator_get (iter); + remove_persona (self, persona); + g_clear_object (&persona); } + g_clear_object (&iter); + individual_grid_destroy (self); + + if (priv->contact != NULL) + remove_weak_contact (self); tp_clear_object (&priv->individual); } @@ -1760,6 +1862,8 @@ individual_update (EmpathyIndividualWidget *self) (GCallback) notify_avatar_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) { @@ -1767,7 +1871,7 @@ individual_update (EmpathyIndividualWidget *self) (GCallback) notify_is_favourite_cb, self); } - /* Update individual table */ + /* Update individual grid */ personas_changed_cb (priv->individual, NULL, NULL, self); } @@ -1775,25 +1879,30 @@ individual_update (EmpathyIndividualWidget *self) { gtk_widget_hide (priv->vbox_individual); } - else if (priv->individual_table != NULL) + else if (priv->individual_grid != NULL) { /* We only need to update the details for the Individual as a whole */ - update_individual_table (self); + update_individual_grid (self); gtk_widget_show (priv->vbox_individual); } else { /* We need to update the details for every Persona in the Individual */ - GList *personas, *l; + GeeSet *personas; + GeeIterator *iter; personas = folks_individual_get_personas (priv->individual); - for (l = personas; l != NULL; l = l->next) + iter = gee_iterable_iterator (GEE_ITERABLE (personas)); + while (gee_iterator_next (iter)) { - if (!TPF_IS_PERSONA (l->data)) - continue; + FolksPersona *persona = gee_iterator_get (iter); + + if (empathy_folks_persona_is_interesting (persona)) + update_persona (self, persona); - update_persona (self, FOLKS_PERSONA (l->data)); + g_clear_object (&persona); } + g_clear_object (&iter); gtk_widget_show (priv->vbox_individual); } @@ -1828,19 +1937,20 @@ empathy_individual_widget_init (EmpathyIndividualWidget *self) #endif "groups_widget", &priv->groups_widget, "vbox_details", &priv->vbox_details, - "table_details", &priv->table_details, + "grid_details", &priv->grid_details, "hbox_details_requested", &priv->hbox_details_requested, + "hbox_client_types", &priv->hbox_client_types, NULL); g_free (filename); - priv->table_location = NULL; + priv->grid_location = NULL; gtk_box_pack_start (GTK_BOX (self), priv->vbox_individual_widget, TRUE, TRUE, 0); gtk_widget_show (priv->vbox_individual_widget); - priv->persona_tables = g_hash_table_new (NULL, NULL); - priv->individual_table = NULL; + priv->persona_grids = g_hash_table_new (NULL, NULL); + priv->individual_grid = NULL; /* Create widgets */ details_set_up (self); @@ -1944,7 +2054,7 @@ finalize (GObject *object) { EmpathyIndividualWidgetPriv *priv = GET_PRIV (object); - g_hash_table_destroy (priv->persona_tables); + g_hash_table_destroy (priv->persona_grids); G_OBJECT_CLASS (empathy_individual_widget_parent_class)->finalize (object); } @@ -2063,4 +2173,5 @@ empathy_individual_widget_set_individual (EmpathyIndividualWidget *self, groups_update (self); details_update (self); location_update (self); + client_types_update (self); }