From 84db1f04162ce84f915f739d68e831d0d3baf449 Mon Sep 17 00:00:00 2001 From: Xavier Claessens Date: Fri, 14 May 2010 14:16:36 +0200 Subject: Display contact vCard in information dialog, add basic vCard editor for self contact Fixes bug #588922 --- libempathy-gtk/empathy-contact-dialogs.c | 5 +- libempathy-gtk/empathy-contact-widget.c | 393 ++++++++++++++++++++++++++++++- libempathy-gtk/empathy-contact-widget.h | 2 + libempathy-gtk/empathy-contact-widget.ui | 74 +----- 4 files changed, 403 insertions(+), 71 deletions(-) (limited to 'libempathy-gtk') diff --git a/libempathy-gtk/empathy-contact-dialogs.c b/libempathy-gtk/empathy-contact-dialogs.c index f83ac7a81..b359b86cd 100644 --- a/libempathy-gtk/empathy-contact-dialogs.c +++ b/libempathy-gtk/empathy-contact-dialogs.c @@ -196,7 +196,7 @@ empathy_contact_information_dialog_show (EmpathyContact *contact, /* Contact info widget */ contact_widget = empathy_contact_widget_new (contact, EMPATHY_CONTACT_WIDGET_SHOW_LOCATION | - EMPATHY_CONTACT_WIDGET_EDIT_NONE); + EMPATHY_CONTACT_WIDGET_SHOW_DETAILS); gtk_container_set_border_width (GTK_CONTAINER (contact_widget), 8); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (dialog))), contact_widget, @@ -308,7 +308,8 @@ empathy_contact_personal_dialog_show (GtkWindow *parent) contact_widget = empathy_contact_widget_new (NULL, EMPATHY_CONTACT_WIDGET_EDIT_ACCOUNT | EMPATHY_CONTACT_WIDGET_EDIT_ALIAS | - EMPATHY_CONTACT_WIDGET_EDIT_AVATAR); + EMPATHY_CONTACT_WIDGET_EDIT_AVATAR | + EMPATHY_CONTACT_WIDGET_EDIT_DETAILS); gtk_container_set_border_width (GTK_CONTAINER (contact_widget), 8); gtk_box_pack_start (GTK_BOX (gtk_dialog_get_content_area (GTK_DIALOG (personal_dialog))), contact_widget, diff --git a/libempathy-gtk/empathy-contact-widget.c b/libempathy-gtk/empathy-contact-widget.c index 05dfb7a50..5b03c597e 100644 --- a/libempathy-gtk/empathy-contact-widget.c +++ b/libempathy-gtk/empathy-contact-widget.c @@ -34,6 +34,7 @@ #include #include +#include #include #include @@ -94,7 +95,6 @@ typedef struct GtkWidget *widget_id; GtkWidget *widget_alias; GtkWidget *label_alias; - GtkWidget *entry_alias; GtkWidget *hbox_presence; GtkWidget *image_state; GtkWidget *label_status; @@ -123,6 +123,8 @@ typedef struct GtkWidget *vbox_details; GtkWidget *table_details; GtkWidget *hbox_details_requested; + GList *details_to_set; + GCancellable *details_cancellable; /* Client */ GtkWidget *vbox_client; @@ -146,17 +148,384 @@ enum COL_COUNT }; +static void +contact_widget_save (EmpathyContactWidget *information) +{ + TpConnection *connection; + GList *l, *next; + + connection = empathy_contact_get_connection (information->contact); + + /* Remove empty fields */ + for (l = information->details_to_set; l != NULL; l = next) + { + TpContactInfoField *field = l->data; + + next = l->next; + if (field->field_value == NULL || EMP_STR_EMPTY (field->field_value[0])) + { + DEBUG ("Drop empty field: %s", field->field_name); + tp_contact_info_field_free (field); + information->details_to_set = + g_list_delete_link (information->details_to_set, l); + } + } + + if (information->details_to_set != NULL) + { + tp_connection_set_contact_info_async (connection, + information->details_to_set, NULL, NULL); + tp_contact_info_list_free (information->details_to_set); + information->details_to_set = NULL; + } +} + static void contact_widget_details_setup (EmpathyContactWidget *information) { - /* FIXME: Needs new telepathy spec */ gtk_widget_hide (information->vbox_details); } +static void +contact_widget_details_changed_cb (GtkEntry *entry, + TpContactInfoField *field) +{ + const gchar *strv[] = { NULL, NULL }; + + strv[0] = gtk_entry_get_text (entry); + + if (field->field_value != NULL) + g_strfreev (field->field_value); + field->field_value = g_strdupv ((GStrv) strv); +} + +static void contact_widget_details_notify_cb (EmpathyContactWidget *information); + +typedef struct +{ + const gchar *field_name; + const gchar *title; + gboolean linkify; +} InfoFieldData; + +static InfoFieldData info_field_datas[] = +{ + { "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 } +}; + +static InfoFieldData * +find_info_field_data (const gchar *field_name) +{ + guint i; + + for (i = 0; info_field_datas[i].field_name != NULL; i++) + { + if (!tp_strdiff (info_field_datas[i].field_name, field_name)) + return info_field_datas + i; + } + return NULL; +} + +static gint +contact_info_field_name_cmp (const gchar *name1, + const gchar *name2) +{ + guint i; + + if (!tp_strdiff (name1, name2)) + return 0; + + /* We use the order of info_field_datas */ + for (i = 0; info_field_datas[i].field_name != NULL; i++) + { + if (!tp_strdiff (info_field_datas[i].field_name, name1)) + return -1; + if (!tp_strdiff (info_field_datas[i].field_name, name2)) + return +1; + } + + return g_strcmp0 (name1, name2); +} + +static gint +contact_info_field_cmp (TpContactInfoField *field1, + TpContactInfoField *field2) +{ + return contact_info_field_name_cmp (field1->field_name, field2->field_name); +} + +static gint +contact_info_field_spec_cmp (TpContactInfoFieldSpec *spec1, + TpContactInfoFieldSpec *spec2) +{ + return contact_info_field_name_cmp (spec1->name, spec2->name); +} + +static guint +contact_widget_details_update_edit (EmpathyContactWidget *information) +{ + TpContact *contact; + TpConnection *connection; + GList *specs, *l; + guint n_rows = 0; + + g_assert (information->details_to_set == NULL); + + contact = empathy_contact_get_tp_contact (information->contact); + connection = tp_contact_get_connection (contact); + + specs = tp_connection_get_contact_info_supported_fields (connection); + specs = g_list_sort (specs, (GCompareFunc) contact_info_field_spec_cmp); + for (l = specs; l != NULL; l = l->next) + { + TpContactInfoFieldSpec *spec = l->data; + TpContactInfoField *field; + InfoFieldData *field_data; + GList *info, *ll; + GStrv value = NULL; + GtkWidget *w; + + field_data = find_info_field_data (spec->name); + if (field_data == NULL) + { + DEBUG ("Unhandled ContactInfo field spec: %s", spec->name); + continue; + } + + /* Search initial value */ + info = tp_contact_get_contact_info (contact); + for (ll = info; ll != NULL; ll = ll->next) + { + field = ll->data; + if (!tp_strdiff (field->field_name, spec->name)) + { + value = field->field_value; + break; + } + } + + field = tp_contact_info_field_new (spec->name, spec->parameters, value); + information->details_to_set = g_list_prepend (information->details_to_set, + field); + + /* Add Title */ + w = gtk_label_new (_(field_data->title)); + gtk_table_attach (GTK_TABLE (information->table_details), + w, 0, 1, n_rows, n_rows + 1, GTK_FILL, 0, 0, 0); + gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); + gtk_widget_show (w); + + /* Add Value */ + w = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (w), + field->field_value[0] ? field->field_value[0] : ""); + gtk_table_attach_defaults (GTK_TABLE (information->table_details), + w, 1, 2, n_rows, n_rows + 1); + gtk_widget_show (w); + + g_signal_connect (w, "changed", + G_CALLBACK (contact_widget_details_changed_cb), field); + + n_rows++; + } + g_list_free (specs); + + return n_rows; +} + +static guint +contact_widget_details_update_show (EmpathyContactWidget *information) +{ + TpContact *contact; + GList *info, *l; + guint n_rows = 0; + + contact = empathy_contact_get_tp_contact (information->contact); + info = tp_contact_get_contact_info (contact); + info = g_list_sort (info, (GCompareFunc) contact_info_field_cmp); + for (l = info; l != NULL; l = l->next) + { + TpContactInfoField *field = l->data; + InfoFieldData *field_data; + const gchar *value; + GtkWidget *w; + + if (field->field_value == NULL || field->field_value[0] == NULL) + continue; + + value = field->field_value[0]; + + field_data = find_info_field_data (field->field_name); + if (field_data == NULL) + { + DEBUG ("Unhandled ContactInfo field: %s", field->field_name); + continue; + } + + /* Add Title */ + w = gtk_label_new (_(field_data->title)); + gtk_table_attach (GTK_TABLE (information->table_details), + w, 0, 1, n_rows, n_rows + 1, GTK_FILL, 0, 0, 0); + 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) + { + gchar *markup; + + markup = empathy_add_link_markup (value); + gtk_label_set_markup (GTK_LABEL (w), markup); + g_free (markup); + } + + if ((information->flags & EMPATHY_CONTACT_WIDGET_FOR_TOOLTIP) == 0) + gtk_label_set_selectable (GTK_LABEL (w), TRUE); + + gtk_table_attach_defaults (GTK_TABLE (information->table_details), + w, 1, 2, n_rows, n_rows + 1); + gtk_misc_set_alignment (GTK_MISC (w), 0, 0.5); + gtk_widget_show (w); + + n_rows++; + } + g_list_free (info); + + return n_rows; +} + +static void +contact_widget_details_notify_cb (EmpathyContactWidget *information) +{ + guint n_rows; + + gtk_container_foreach (GTK_CONTAINER (information->table_details), + (GtkCallback) gtk_widget_destroy, NULL); + + if ((information->flags & EMPATHY_CONTACT_WIDGET_EDIT_DETAILS) != 0) + n_rows = contact_widget_details_update_edit (information); + else + n_rows = contact_widget_details_update_show (information); + + if (n_rows > 0) + { + gtk_widget_show (information->vbox_details); + gtk_widget_show (information->table_details); + } + else + { + gtk_widget_hide (information->vbox_details); + } + + gtk_widget_hide (information->hbox_details_requested); +} + +static void +contact_widget_details_request_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + TpContact *contact = TP_CONTACT (object); + EmpathyContactWidget *information = user_data; + GError *error = NULL; + + if (!tp_contact_request_contact_info_finish (contact, res, &error)) + { + /* 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_clear_error (&error); + return; + } + + gtk_widget_hide (information->vbox_details); + g_clear_error (&error); + } + else + { + contact_widget_details_notify_cb (information); + } + + /* If we are going to edit ContactInfo, we don't want live updates */ + if ((information->flags & EMPATHY_CONTACT_WIDGET_EDIT_DETAILS) == 0) + { + g_signal_connect_swapped (contact, "notify::contact-info", + G_CALLBACK (contact_widget_details_notify_cb), information); + } + + g_object_unref (information->details_cancellable); + information->details_cancellable = NULL; +} + +static void +contact_widget_details_feature_prepared_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + TpConnection *connection = TP_CONNECTION (object); + EmpathyContactWidget *information = user_data; + TpContact *contact; + TpContactInfoFlags flags; + + if (!tp_proxy_prepare_finish (connection, res, NULL)) + { + gtk_widget_hide (information->vbox_details); + return; + } + + /* If we want to edit info, but connection does not support that, stop */ + flags = tp_connection_get_contact_info_flags (connection); + if ((flags & TP_CONTACT_INFO_FLAG_CAN_SET) == 0 && + (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_DETAILS) != 0) + { + gtk_widget_hide (information->vbox_details); + return; + } + + /* Request the contact's info */ + gtk_widget_show (information->vbox_details); + gtk_widget_show (information->hbox_details_requested); + gtk_widget_hide (information->table_details); + + contact = empathy_contact_get_tp_contact (information->contact); + g_assert (information->details_cancellable == NULL); + information->details_cancellable = g_cancellable_new (); + tp_contact_request_contact_info_async (contact, + information->details_cancellable, contact_widget_details_request_cb, + information); +} + static void contact_widget_details_update (EmpathyContactWidget *information) { - /* FIXME: Needs new telepathy spec */ + TpContact *tp_contact = NULL; + + if ((information->flags & EMPATHY_CONTACT_WIDGET_SHOW_DETAILS) == 0 && + (information->flags & EMPATHY_CONTACT_WIDGET_EDIT_DETAILS) == 0) + return; + + gtk_widget_hide (information->vbox_details); + + if (information->contact != NULL) + tp_contact = empathy_contact_get_tp_contact (information->contact); + + if (tp_contact != NULL) + { + GQuark features[] = { TP_CONNECTION_FEATURE_CONTACT_INFO, 0 }; + TpConnection *connection; + + /* 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, + contact_widget_details_feature_prepared_cb, information); + } } static void @@ -1048,6 +1417,10 @@ contact_widget_remove_contact (EmpathyContactWidget *information) { if (information->contact) { + TpContact *tp_contact; + + contact_widget_save (information); + g_signal_handlers_disconnect_by_func (information->contact, contact_widget_name_notify_cb, information); g_signal_handlers_disconnect_by_func (information->contact, @@ -1057,9 +1430,23 @@ contact_widget_remove_contact (EmpathyContactWidget *information) g_signal_handlers_disconnect_by_func (information->contact, contact_widget_groups_notify_cb, information); + tp_contact = empathy_contact_get_tp_contact (information->contact); + if (tp_contact != NULL) + { + g_signal_handlers_disconnect_by_func (tp_contact, + contact_widget_details_notify_cb, information); + } + g_object_unref (information->contact); information->contact = NULL; } + + if (information->details_cancellable != NULL) + { + g_cancellable_cancel (information->details_cancellable); + g_object_unref (information->details_cancellable); + information->details_cancellable = NULL; + } } static void contact_widget_change_contact (EmpathyContactWidget *information); diff --git a/libempathy-gtk/empathy-contact-widget.h b/libempathy-gtk/empathy-contact-widget.h index af669477e..fb684a41b 100644 --- a/libempathy-gtk/empathy-contact-widget.h +++ b/libempathy-gtk/empathy-contact-widget.h @@ -62,6 +62,8 @@ typedef enum EMPATHY_CONTACT_WIDGET_SHOW_LOCATION = 1 << 6, EMPATHY_CONTACT_WIDGET_NO_SET_ALIAS = 1 << 7, EMPATHY_CONTACT_WIDGET_EDIT_FAVOURITE = 1 << 8, + EMPATHY_CONTACT_WIDGET_SHOW_DETAILS = 1 << 9, + EMPATHY_CONTACT_WIDGET_EDIT_DETAILS = 1 << 10, } EmpathyContactWidgetFlags; GtkWidget * empathy_contact_widget_new (EmpathyContact *contact, diff --git a/libempathy-gtk/empathy-contact-widget.ui b/libempathy-gtk/empathy-contact-widget.ui index 438abf237..0792dcff9 100644 --- a/libempathy-gtk/empathy-contact-widget.ui +++ b/libempathy-gtk/empathy-contact-widget.ui @@ -3,7 +3,6 @@ - vertical 6 @@ -12,7 +11,6 @@ True - vertical 6 @@ -102,7 +100,6 @@ True - vertical @@ -120,7 +117,6 @@ - vertical 6 @@ -142,7 +138,6 @@ True - vertical 5 @@ -178,7 +173,6 @@ - vertical 6 @@ -202,7 +196,6 @@ True - vertical 6 @@ -288,7 +281,6 @@ - vertical 6 @@ -312,66 +304,13 @@ True - vertical 6 - 4 + True 2 12 6 - - - 0 - Full name: - - - GTK_FILL - - - - - - 0 - E-mail address: - - - 1 - 2 - GTK_FILL - - - - - - 0 - Website: - - - 2 - 3 - GTK_FILL - - - - - - 0 - Birthday: - - - 3 - 4 - GTK_FILL - - - - - - - - - @@ -385,7 +324,6 @@ - True 6 @@ -434,7 +372,6 @@ - vertical 6 @@ -458,16 +395,17 @@ True - vertical 6 + True 3 2 12 6 + True 0 0 OS: @@ -480,6 +418,7 @@ + True 0 0 Version: @@ -492,6 +431,7 @@ + True 0 0 Client: @@ -502,6 +442,7 @@ + True True 0 2 @@ -516,6 +457,7 @@ + True True 0 2 @@ -532,6 +474,7 @@ + True True 0 2 @@ -554,7 +497,6 @@ - True 6 -- cgit v1.2.3