From fad2ecd3f071c671425954638081c641ff603503 Mon Sep 17 00:00:00 2001 From: Marco Barisione Date: Tue, 30 Jul 2013 14:09:13 +0100 Subject: user-info: move from Empathy to tp-account-widgets https://bugzilla.gnome.org/show_bug.cgi?id=699492 --- libempathy-gtk/Makefile.am | 2 - libempathy-gtk/empathy-user-info.c | 776 ------------------------------------ libempathy-gtk/empathy-user-info.h | 75 ---- src/empathy-accounts-dialog.c | 8 +- tp-account-widgets/Makefile.am | 2 + tp-account-widgets/tpaw-user-info.c | 776 ++++++++++++++++++++++++++++++++++++ tp-account-widgets/tpaw-user-info.h | 74 ++++ 7 files changed, 856 insertions(+), 857 deletions(-) delete mode 100644 libempathy-gtk/empathy-user-info.c delete mode 100644 libempathy-gtk/empathy-user-info.h create mode 100644 tp-account-widgets/tpaw-user-info.c create mode 100644 tp-account-widgets/tpaw-user-info.h diff --git a/libempathy-gtk/Makefile.am b/libempathy-gtk/Makefile.am index 9c849a75b..19e07d505 100644 --- a/libempathy-gtk/Makefile.am +++ b/libempathy-gtk/Makefile.am @@ -81,7 +81,6 @@ libempathy_gtk_handwritten_source = \ empathy-theme-manager.c \ empathy-tls-dialog.c \ empathy-ui-utils.c \ - empathy-user-info.c \ empathy-plist.c \ empathy-theme-adium.c \ empathy-webkit-utils.c \ @@ -143,7 +142,6 @@ libempathy_gtk_headers = \ empathy-theme-manager.h \ empathy-tls-dialog.h \ empathy-ui-utils.h \ - empathy-user-info.h \ empathy-plist.h \ empathy-theme-adium.h \ empathy-webkit-utils.h \ diff --git a/libempathy-gtk/empathy-user-info.c b/libempathy-gtk/empathy-user-info.c deleted file mode 100644 index 1177adca3..000000000 --- a/libempathy-gtk/empathy-user-info.c +++ /dev/null @@ -1,776 +0,0 @@ -/* - * empathy-user-info.c - Source for EmpathyUserInfo - * - * Copyright (C) 2012 - Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with This library. If not, see . - */ - -#include "config.h" -#include "empathy-user-info.h" - -#include -#include -#include -#include -#include - -#include "empathy-utils.h" - -#define DEBUG_FLAG EMPATHY_DEBUG_CONTACT -#include "empathy-debug.h" - -G_DEFINE_TYPE (EmpathyUserInfo, empathy_user_info, GTK_TYPE_GRID) - -struct _EmpathyUserInfoPrivate -{ - TpAccount *account; - - GtkWidget *avatar_chooser; - GtkWidget *identifier_label; - GtkWidget *nickname_entry; - GtkWidget *details_label; - GtkWidget *details_spinner; - - GList *details_to_set; - gboolean details_changed; - GCancellable *details_cancellable; -}; - -enum -{ - PROP_0, - PROP_ACCOUNT, -}; - -#define DATA_FIELD "contact-info-field" -#define DATA_IS_CONTACT_INFO "is-contact-info" - -static void -contact_info_changed_cb (GtkEntry *entry, - EmpathyUserInfo *self) -{ - const gchar *strv[] = { NULL, NULL }; - TpContactInfoField *field; - - self->priv->details_changed = TRUE; - - field = g_object_get_data ((GObject *) entry, DATA_FIELD); - g_assert (field != 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 -bday_changed_cb (TpawCalendarButton *button, - GDate *date, - EmpathyUserInfo *self) -{ - const gchar *strv[] = { NULL, NULL }; - TpContactInfoField *field; - - self->priv->details_changed = TRUE; - - field = g_object_get_data ((GObject *) button, DATA_FIELD); - g_assert (field != NULL); - - if (date != NULL) - { - gchar tmp[255]; - - g_date_strftime (tmp, sizeof (tmp), TPAW_DATE_FORMAT_DISPLAY_SHORT, - date); - strv[0] = tmp; - } - - if (field->field_value != NULL) - g_strfreev (field->field_value); - field->field_value = g_strdupv ((GStrv) strv); -} - -static gboolean -field_name_in_field_list (GList *list, - const gchar *name) -{ - GList *l; - - for (l = list; l != NULL; l = g_list_next (l)) - { - TpContactInfoField *field = l->data; - - if (!tp_strdiff (field->field_name, name)) - return TRUE; - } - - return FALSE; -} - -static TpContactInfoFieldSpec * -get_spec_from_list (GList *list, - const gchar *name) -{ - GList *l; - - for (l = list; l != NULL; l = g_list_next (l)) - { - TpContactInfoFieldSpec *spec = l->data; - - if (!tp_strdiff (spec->name, name)) - return spec; - } - - return NULL; -} - -static void -add_row (GtkGrid *grid, - GtkWidget *title, - GtkWidget *value, - gboolean contact_info) -{ - /* Title */ - gtk_grid_attach_next_to (grid, title, NULL, GTK_POS_BOTTOM, 1, 1); - gtk_misc_set_alignment (GTK_MISC (title), 1, 0.5); - gtk_style_context_add_class (gtk_widget_get_style_context (title), - GTK_STYLE_CLASS_DIM_LABEL); - gtk_widget_show (title); - - /* Value */ - gtk_grid_attach_next_to (grid, value, title, GTK_POS_RIGHT, - contact_info ? 2 : 1, 1); - gtk_widget_set_hexpand (value, TRUE); - if (GTK_IS_LABEL (value)) - { - gtk_misc_set_alignment (GTK_MISC (value), 0, 0.5); - gtk_label_set_selectable (GTK_LABEL (value), TRUE); - } - gtk_widget_show (value); - - if (contact_info) - { - g_object_set_data (G_OBJECT (title), - DATA_IS_CONTACT_INFO, (gpointer) TRUE); - g_object_set_data (G_OBJECT (value), - DATA_IS_CONTACT_INFO, (gpointer) TRUE); - } -} - -static guint -fill_contact_info_grid (EmpathyUserInfo *self) -{ - TpConnection *connection; - TpContact *contact; - GList *specs, *l; - guint n_rows = 0; - GList *info; - const char **field_names = tpaw_contact_info_get_field_names (NULL); - guint i; - - g_assert (self->priv->details_to_set == NULL); - - connection = tp_account_get_connection (self->priv->account); - contact = tp_connection_get_self_contact (connection); - specs = tp_connection_dup_contact_info_supported_fields (connection); - info = tp_contact_dup_contact_info (contact); - - /* Look at the fields set in our vCard */ - for (l = info; l != NULL; l = l->next) - { - TpContactInfoField *field = l->data; - - /* For some reason it can happen that the vCard contains fields the CM - * claims to be not supported. This is a workaround for gabble bug - * https://bugs.freedesktop.org/show_bug.cgi?id=64319. But we shouldn't - * crash on buggy CM anyway. */ - if (get_spec_from_list (specs, field->field_name) == NULL) - { - DEBUG ("Buggy CM: self's vCard contains %s field but it is not in " - "Connection' supported fields", field->field_name); - continue; - } - - /* make a copy for the details_to_set list */ - field = tp_contact_info_field_copy (field); - DEBUG ("Field %s is in our vCard", field->field_name); - - self->priv->details_to_set = g_list_prepend (self->priv->details_to_set, - field); - } - - /* Add fields which are supported but not in the vCard */ - for (i = 0; field_names[i] != NULL; i++) - { - TpContactInfoFieldSpec *spec; - TpContactInfoField *field; - - /* Check if the field was in the vCard */ - if (field_name_in_field_list (self->priv->details_to_set, - field_names[i])) - continue; - - /* Check if the CM supports the field */ - spec = get_spec_from_list (specs, field_names[i]); - if (spec == NULL) - continue; - - /* add an empty field so user can set a value */ - field = tp_contact_info_field_new (spec->name, spec->parameters, NULL); - - self->priv->details_to_set = g_list_prepend (self->priv->details_to_set, - field); - } - - /* Add widgets for supported fields */ - self->priv->details_to_set = g_list_sort (self->priv->details_to_set, - (GCompareFunc) tpaw_contact_info_field_spec_cmp); - - for (l = self->priv->details_to_set; l != NULL; l= g_list_next (l)) - { - TpContactInfoField *field = l->data; - GtkWidget *label, *w; - TpContactInfoFieldSpec *spec; - gboolean has_field; - char *title; - - has_field = tpaw_contact_info_lookup_field (field->field_name, - NULL, NULL); - if (!has_field) - { - /* Empathy doesn't display this field so we can't change it. - * But we put it in the details_to_set list so it won't be erased - * when calling SetContactInfo (bgo #630427) */ - DEBUG ("Unhandled ContactInfo field spec: %s", field->field_name); - continue; - } - - spec = get_spec_from_list (specs, field->field_name); - /* We shouldn't have added the field to details_to_set if it's not - * supported by the CM */ - g_assert (spec != NULL); - - if (spec->flags & TP_CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME) - { - DEBUG ("Ignoring field '%s' due it to having the " - "Overwritten_By_Nickname flag", field->field_name); - continue; - } - - /* Add Title */ - title = tpaw_contact_info_field_label (field->field_name, - field->parameters, - (spec->flags & TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT)); - label = gtk_label_new (title); - g_free (title); - - /* TODO: if TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT is not set we - * should allow user to tag the vCard fields (bgo#672034) */ - - /* Add Value */ - if (!tp_strdiff (field->field_name, "bday")) - { - w = tpaw_calendar_button_new (); - - if (field->field_value[0]) - { - GDate date; - - g_date_set_parse (&date, field->field_value[0]); - if (g_date_valid (&date)) - { - tpaw_calendar_button_set_date (TPAW_CALENDAR_BUTTON (w), - &date); - } - } - - g_signal_connect (w, "date-changed", - G_CALLBACK (bday_changed_cb), self); - } - else - { - w = gtk_entry_new (); - gtk_entry_set_text (GTK_ENTRY (w), - field->field_value[0] ? field->field_value[0] : ""); - g_signal_connect (w, "changed", - G_CALLBACK (contact_info_changed_cb), self); - } - - add_row (GTK_GRID (self), label, w, TRUE); - - g_object_set_data ((GObject *) w, DATA_FIELD, field); - - n_rows++; - } - - tp_contact_info_spec_list_free (specs); - tp_contact_info_list_free (info); - - return n_rows; -} - -static void -grid_foreach_cb (GtkWidget *widget, - gpointer data) -{ - if (g_object_get_data (G_OBJECT (widget), DATA_IS_CONTACT_INFO) != NULL) - gtk_widget_destroy (widget); -} - -static void -request_contact_info_cb (GObject *object, - GAsyncResult *res, - gpointer user_data) -{ - EmpathyUserInfo *self = user_data; - TpContact *contact = TP_CONTACT (object); - guint n_rows; - 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 self */ - if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) - { - g_clear_error (&error); - return; - } - g_clear_error (&error); - } - - n_rows = fill_contact_info_grid (self); - - gtk_widget_set_visible (self->priv->details_label, n_rows > 0); - gtk_spinner_stop (GTK_SPINNER (self->priv->details_spinner)); - gtk_widget_hide (self->priv->details_spinner); -} - -static void -reload_contact_info (EmpathyUserInfo *self) -{ - TpConnection *connection; - TpContact *contact = NULL; - TpContactInfoFlags flags; - - /* Cancel previous RequestContactInfo, if any */ - if (self->priv->details_cancellable != NULL) - g_cancellable_cancel (self->priv->details_cancellable); - g_clear_object (&self->priv->details_cancellable); - - /* Remove current contact info widgets, if any */ - gtk_container_foreach (GTK_CONTAINER (self), grid_foreach_cb, NULL); - gtk_widget_hide (self->priv->details_label); - gtk_widget_hide (self->priv->details_spinner); - - tp_clear_pointer (&self->priv->details_to_set, tp_contact_info_list_free); - self->priv->details_changed = FALSE; - - connection = tp_account_get_connection (self->priv->account); - if (connection != NULL) - contact = tp_connection_get_self_contact (connection); - - /* Display infobar if we don't have a self contact (probably offline) */ - if (contact == NULL) - { - GtkWidget *infobar; - GtkWidget *content; - GtkWidget *label; - - infobar = gtk_info_bar_new (); - gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_INFO); - content = gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar)); - label = gtk_label_new (_("Go online to edit your personal information.")); - gtk_container_add (GTK_CONTAINER (content), label); - gtk_widget_show (label); - - gtk_grid_attach_next_to ((GtkGrid *) self, infobar, - NULL, GTK_POS_BOTTOM, 3, 1); - gtk_widget_show (infobar); - - g_object_set_data (G_OBJECT (infobar), - DATA_IS_CONTACT_INFO, (gpointer) TRUE); - return; - } - - if (!tp_proxy_has_interface_by_id (connection, - TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO)) - return; - - flags = tp_connection_get_contact_info_flags (connection); - if ((flags & TP_CONTACT_INFO_FLAG_CAN_SET) == 0) - return; - - /* Request the contact's info */ - gtk_widget_show (self->priv->details_spinner); - gtk_spinner_start (GTK_SPINNER (self->priv->details_spinner)); - - g_assert (self->priv->details_cancellable == NULL); - self->priv->details_cancellable = g_cancellable_new (); - tp_contact_request_contact_info_async (contact, - self->priv->details_cancellable, request_contact_info_cb, - self); -} - -static void -connection_notify_cb (EmpathyUserInfo *self) -{ - TpConnection *connection = tp_account_get_connection (self->priv->account); - - if (connection != NULL) - { - tp_g_signal_connect_object (connection, "notify::self-contact", - G_CALLBACK (reload_contact_info), self, G_CONNECT_SWAPPED); - } - - reload_contact_info (self); -} - -static void -identifier_notify_cb (TpAccount *account, - GParamSpec *param_spec, - EmpathyUserInfo *self) -{ - gtk_label_set_label (GTK_LABEL (self->priv->identifier_label), - tp_account_get_normalized_name (self->priv->account)); -} - -static void -nickname_notify_cb (TpAccount *account, - GParamSpec *param_spec, - EmpathyUserInfo *self) -{ - gtk_entry_set_text (GTK_ENTRY (self->priv->nickname_entry), - tp_account_get_nickname (self->priv->account)); -} - -static void -empathy_user_info_constructed (GObject *object) -{ - EmpathyUserInfo *self = (EmpathyUserInfo *) object; - GtkGrid *grid = (GtkGrid *) self; - GtkWidget *title; - - G_OBJECT_CLASS (empathy_user_info_parent_class)->constructed (object); - - gtk_grid_set_column_spacing (grid, 6); - gtk_grid_set_row_spacing (grid, 6); - - /* Setup id label */ - title = gtk_label_new (_("Identifier")); - self->priv->identifier_label = gtk_label_new ( - tp_account_get_normalized_name (self->priv->account)); - add_row (grid, title, self->priv->identifier_label, FALSE); - g_signal_connect_object (self->priv->account, "notify::normalized-name", - G_CALLBACK (identifier_notify_cb), self, 0); - - /* Setup nickname entry */ - title = gtk_label_new (_("Alias")); - self->priv->nickname_entry = gtk_entry_new (); - gtk_entry_set_text (GTK_ENTRY (self->priv->nickname_entry), - tp_account_get_nickname (self->priv->account)); - add_row (grid, title, self->priv->nickname_entry, FALSE); - g_signal_connect_object (self->priv->account, "notify::nickname", - G_CALLBACK (nickname_notify_cb), self, 0); - - /* Set up avatar chooser */ - self->priv->avatar_chooser = tpaw_avatar_chooser_new (self->priv->account); - gtk_grid_attach (grid, self->priv->avatar_chooser, - 2, 0, 1, 3); - gtk_widget_show (self->priv->avatar_chooser); - - /* Details label */ - self->priv->details_label = gtk_label_new (NULL); - gtk_label_set_markup (GTK_LABEL (self->priv->details_label), - _("Personal Details")); - gtk_misc_set_alignment (GTK_MISC (self->priv->details_label), 0, 0.5); - gtk_grid_attach_next_to (grid, self->priv->details_label, NULL, - GTK_POS_BOTTOM, 3, 1); - - /* Details spinner */ - self->priv->details_spinner = gtk_spinner_new (); - gtk_widget_set_hexpand (self->priv->details_spinner, TRUE); - gtk_widget_set_vexpand (self->priv->details_spinner, TRUE); - gtk_grid_attach_next_to (grid, self->priv->details_spinner, NULL, - GTK_POS_BOTTOM, 3, 1); - - g_signal_connect_swapped (self->priv->account, "notify::connection", - G_CALLBACK (connection_notify_cb), self); - connection_notify_cb (self); -} - -static void -empathy_user_info_dispose (GObject *object) -{ - EmpathyUserInfo *self = (EmpathyUserInfo *) object; - - if (self->priv->account != NULL) - { - /* Disconnect the signal manually, because TpAccount::dispose will emit - * "notify::connection" signal before tp_g_signal_connect_object() had - * a chance to disconnect. */ - g_signal_handlers_disconnect_by_func (self->priv->account, - connection_notify_cb, self); - g_clear_object (&self->priv->account); - } - - if (self->priv->details_cancellable != NULL) - g_cancellable_cancel (self->priv->details_cancellable); - g_clear_object (&self->priv->details_cancellable); - - G_OBJECT_CLASS (empathy_user_info_parent_class)->dispose (object); -} - -static void -empathy_user_info_get_property (GObject *object, - guint property_id, - GValue *value, - GParamSpec *pspec) -{ - EmpathyUserInfo *self = (EmpathyUserInfo *) object; - - switch (property_id) - { - case PROP_ACCOUNT: - g_value_set_object (value, self->priv->account); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -empathy_user_info_set_property (GObject *object, - guint property_id, - const GValue *value, - GParamSpec *pspec) -{ - EmpathyUserInfo *self = (EmpathyUserInfo *) object; - - switch (property_id) - { - case PROP_ACCOUNT: - g_assert (self->priv->account == NULL); /* construct-only */ - self->priv->account = g_value_dup_object (value); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); - break; - } -} - -static void -empathy_user_info_init (EmpathyUserInfo *self) -{ - self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, - EMPATHY_TYPE_USER_INFO, EmpathyUserInfoPrivate); -} - -static void -empathy_user_info_class_init (EmpathyUserInfoClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - GParamSpec *param_spec; - - object_class->constructed = empathy_user_info_constructed; - object_class->dispose = empathy_user_info_dispose; - object_class->get_property = empathy_user_info_get_property; - object_class->set_property = empathy_user_info_set_property; - - g_type_class_add_private (object_class, sizeof (EmpathyUserInfoPrivate)); - - param_spec = g_param_spec_object ("account", - "account", - "The #TpAccount on which user info should be edited", - TP_TYPE_ACCOUNT, - G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); - g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec); -} - -GtkWidget * -empathy_user_info_new (TpAccount *account) -{ - g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); - - return g_object_new (EMPATHY_TYPE_USER_INFO, - "account", account, - NULL); -} - -void -empathy_user_info_discard (EmpathyUserInfo *self) -{ - g_return_if_fail (EMPATHY_IS_USER_INFO (self)); - - reload_contact_info (self); - gtk_entry_set_text ((GtkEntry *) self->priv->nickname_entry, - tp_account_get_nickname (self->priv->account)); -} - -static void -apply_complete_one (GSimpleAsyncResult *result) -{ - guint count; - - count = g_simple_async_result_get_op_res_gssize (result); - count--; - g_simple_async_result_set_op_res_gssize (result, count); - - if (count == 0) - g_simple_async_result_complete (result); -} - -static void -avatar_chooser_apply_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - TpawAvatarChooser *avatar_chooser = (TpawAvatarChooser *) source; - GSimpleAsyncResult *my_result = user_data; - GError *error = NULL; - - if (!tpaw_avatar_chooser_apply_finish (avatar_chooser, result, &error)) - g_simple_async_result_take_error (my_result, error); - - apply_complete_one (my_result); - g_object_unref (my_result); -} - -static void -set_nickname_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - TpAccount *account = (TpAccount *) source; - GSimpleAsyncResult *my_result = user_data; - GError *error = NULL; - - if (!tp_account_set_nickname_finish (account, result, &error)) - g_simple_async_result_take_error (my_result, error); - - apply_complete_one (my_result); - g_object_unref (my_result); -} - -static void -set_contact_info_cb (GObject *source, - GAsyncResult *result, - gpointer user_data) -{ - TpConnection *connection = (TpConnection *) source; - GSimpleAsyncResult *my_result = user_data; - GError *error = NULL; - - if (!tp_connection_set_contact_info_finish (connection, result, &error)) - g_simple_async_result_take_error (my_result, error); - - apply_complete_one (my_result); - g_object_unref (my_result); -} - -static gboolean -field_value_is_empty (TpContactInfoField *field) -{ - guint i; - - if (field->field_value == NULL) - return TRUE; - - /* Field is empty if all its values are empty */ - for (i = 0; field->field_value[i] != NULL; i++) - { - if (!tp_str_empty (field->field_value[i])) - return FALSE; - } - - return TRUE; -} - -void -empathy_user_info_apply_async (EmpathyUserInfo *self, - GAsyncReadyCallback callback, - gpointer user_data) -{ - GSimpleAsyncResult *result; - const gchar *new_nickname; - guint count = 0; - GList *l, *next; - - g_return_if_fail (EMPATHY_IS_USER_INFO (self)); - - result = g_simple_async_result_new ((GObject *) self, callback, user_data, - empathy_user_info_apply_async); - - /* Apply avatar */ - tpaw_avatar_chooser_apply_async ( - (TpawAvatarChooser *) self->priv->avatar_chooser, - avatar_chooser_apply_cb, g_object_ref (result)); - count++; - - /* Apply nickname */ - new_nickname = gtk_entry_get_text (GTK_ENTRY (self->priv->nickname_entry)); - if (tp_strdiff (new_nickname, tp_account_get_nickname (self->priv->account))) - { - tp_account_set_nickname_async (self->priv->account, new_nickname, - set_nickname_cb, g_object_ref (result)); - count++; - } - - /* Remove empty fields */ - for (l = self->priv->details_to_set; l != NULL; l = next) - { - TpContactInfoField *field = l->data; - - next = l->next; - if (field_value_is_empty (field)) - { - DEBUG ("Drop empty field: %s", field->field_name); - tp_contact_info_field_free (field); - self->priv->details_to_set = - g_list_delete_link (self->priv->details_to_set, l); - } - } - - if (self->priv->details_to_set != NULL) - { - if (self->priv->details_changed) - { - tp_connection_set_contact_info_async ( - tp_account_get_connection (self->priv->account), - self->priv->details_to_set, set_contact_info_cb, - g_object_ref (result)); - count++; - } - - tp_contact_info_list_free (self->priv->details_to_set); - self->priv->details_to_set = NULL; - } - - self->priv->details_changed = FALSE; - - g_simple_async_result_set_op_res_gssize (result, count); - - g_object_unref (result); -} - -gboolean -empathy_user_info_apply_finish (EmpathyUserInfo *self, - GAsyncResult *result, - GError **error) -{ - empathy_implement_finish_void (self, empathy_user_info_apply_async); -} diff --git a/libempathy-gtk/empathy-user-info.h b/libempathy-gtk/empathy-user-info.h deleted file mode 100644 index a06c72911..000000000 --- a/libempathy-gtk/empathy-user-info.h +++ /dev/null @@ -1,75 +0,0 @@ -/* - * empathy-user-info.h - Header for EmpathyUserInfo - * - * Copyright (C) 2012 - Collabora Ltd. - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with This library. If not, see . - */ - -#ifndef __EMPATHY_USER_INFO_H__ -#define __EMPATHY_USER_INFO_H__ - -#include -#include - -G_BEGIN_DECLS - -#define EMPATHY_TYPE_USER_INFO \ - (empathy_user_info_get_type ()) -#define EMPATHY_USER_INFO(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST ((obj), EMPATHY_TYPE_USER_INFO, \ - EmpathyUserInfo)) -#define EMPATHY_USER_INFO_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST ((klass), EMPATHY_TYPE_USER_INFO, \ - EmpathyUserInfoClass)) -#define EMPATHY_IS_USER_INFO(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EMPATHY_TYPE_USER_INFO)) -#define EMPATHY_IS_USER_INFO_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_TYPE ((klass), EMPATHY_TYPE_USER_INFO)) -#define EMPATHY_USER_INFO_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), EMPATHY_TYPE_USER_INFO, \ - EmpathyUserInfoClass)) - -typedef struct _EmpathyUserInfo EmpathyUserInfo; -typedef struct _EmpathyUserInfoClass EmpathyUserInfoClass; -typedef struct _EmpathyUserInfoPrivate EmpathyUserInfoPrivate; - -struct _EmpathyUserInfo { - GtkGrid parent; - - EmpathyUserInfoPrivate *priv; -}; - -struct _EmpathyUserInfoClass { - GtkGridClass parent_class; -}; - -GType empathy_user_info_get_type (void) G_GNUC_CONST; - -GtkWidget *empathy_user_info_new (TpAccount *account); - -void empathy_user_info_discard (EmpathyUserInfo *self); - -void empathy_user_info_apply_async (EmpathyUserInfo *self, - GAsyncReadyCallback callback, - gpointer user_data); -gboolean empathy_user_info_apply_finish (EmpathyUserInfo *self, - GAsyncResult *result, - GError **error); - - -G_END_DECLS - -#endif /* __EMPATHY_USER_INFO_H__ */ - diff --git a/src/empathy-accounts-dialog.c b/src/empathy-accounts-dialog.c index 53f6997d8..ca5d57f5c 100644 --- a/src/empathy-accounts-dialog.c +++ b/src/empathy-accounts-dialog.c @@ -30,6 +30,7 @@ #include #include #include +#include #include #include "empathy-accounts-common.h" @@ -39,7 +40,6 @@ #include "empathy-new-account-dialog.h" #include "empathy-pkg-kit.h" #include "empathy-ui-utils.h" -#include "empathy-user-info.h" #include "empathy-utils.h" #define DEBUG_FLAG EMPATHY_DEBUG_ACCOUNT @@ -701,7 +701,7 @@ account_dialog_create_dialog_content (EmpathyAccountsDialog *dialog, gtk_widget_show (priv->dialog_content); alig = gtk_alignment_new (0.5, 0, 1, 1); - priv->user_info = empathy_user_info_new (account); + priv->user_info = tpaw_user_info_new (account); gtk_container_add (GTK_CONTAINER (alig), priv->user_info); gtk_box_pack_start (GTK_BOX (priv->dialog_content), alig, TRUE, TRUE, 0); gtk_widget_show (alig); @@ -925,7 +925,7 @@ accounts_dialog_update_settings (EmpathyAccountsDialog *dialog, if (priv->user_info != NULL) { - empathy_user_info_apply_async ((EmpathyUserInfo *) priv->user_info, + tpaw_user_info_apply_async ((TpawUserInfo *) priv->user_info, NULL, NULL); priv->user_info = NULL; } @@ -2431,7 +2431,7 @@ do_dispose (GObject *obj) if (priv->user_info != NULL) { - empathy_user_info_apply_async ((EmpathyUserInfo *) priv->user_info, + tpaw_user_info_apply_async ((TpawUserInfo *) priv->user_info, NULL, NULL); priv->user_info = NULL; } diff --git a/tp-account-widgets/Makefile.am b/tp-account-widgets/Makefile.am index 21a10feb0..fa79032f6 100644 --- a/tp-account-widgets/Makefile.am +++ b/tp-account-widgets/Makefile.am @@ -44,6 +44,7 @@ libtp_account_widgets_sources = \ tpaw-live-search.c \ tpaw-string-parser.c \ tpaw-time.c \ + tpaw-user-info.c \ tpaw-utils.c \ totem-subtitle-encoding.c \ $(NULL) @@ -71,6 +72,7 @@ libtp_account_widgets_headers = \ tpaw-live-search.h \ tpaw-string-parser.h \ tpaw-time.h \ + tpaw-user-info.h \ tpaw-utils.h \ totem-subtitle-encoding.h \ $(NULL) diff --git a/tp-account-widgets/tpaw-user-info.c b/tp-account-widgets/tpaw-user-info.c new file mode 100644 index 000000000..9a4843fe9 --- /dev/null +++ b/tp-account-widgets/tpaw-user-info.c @@ -0,0 +1,776 @@ +/* + * tpaw-user-info.c - Source for TpawUserInfo + * + * Copyright (C) 2012 - Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with This library. If not, see . + */ + +#include "config.h" +#include "tpaw-user-info.h" + +#include +#include +#include +#include +#include + +#include "empathy-utils.h" + +#define DEBUG_FLAG EMPATHY_DEBUG_CONTACT +#include "empathy-debug.h" + +G_DEFINE_TYPE (TpawUserInfo, tpaw_user_info, GTK_TYPE_GRID) + +struct _TpawUserInfoPrivate +{ + TpAccount *account; + + GtkWidget *avatar_chooser; + GtkWidget *identifier_label; + GtkWidget *nickname_entry; + GtkWidget *details_label; + GtkWidget *details_spinner; + + GList *details_to_set; + gboolean details_changed; + GCancellable *details_cancellable; +}; + +enum +{ + PROP_0, + PROP_ACCOUNT, +}; + +#define DATA_FIELD "contact-info-field" +#define DATA_IS_CONTACT_INFO "is-contact-info" + +static void +contact_info_changed_cb (GtkEntry *entry, + TpawUserInfo *self) +{ + const gchar *strv[] = { NULL, NULL }; + TpContactInfoField *field; + + self->priv->details_changed = TRUE; + + field = g_object_get_data ((GObject *) entry, DATA_FIELD); + g_assert (field != 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 +bday_changed_cb (TpawCalendarButton *button, + GDate *date, + TpawUserInfo *self) +{ + const gchar *strv[] = { NULL, NULL }; + TpContactInfoField *field; + + self->priv->details_changed = TRUE; + + field = g_object_get_data ((GObject *) button, DATA_FIELD); + g_assert (field != NULL); + + if (date != NULL) + { + gchar tmp[255]; + + g_date_strftime (tmp, sizeof (tmp), TPAW_DATE_FORMAT_DISPLAY_SHORT, + date); + strv[0] = tmp; + } + + if (field->field_value != NULL) + g_strfreev (field->field_value); + field->field_value = g_strdupv ((GStrv) strv); +} + +static gboolean +field_name_in_field_list (GList *list, + const gchar *name) +{ + GList *l; + + for (l = list; l != NULL; l = g_list_next (l)) + { + TpContactInfoField *field = l->data; + + if (!tp_strdiff (field->field_name, name)) + return TRUE; + } + + return FALSE; +} + +static TpContactInfoFieldSpec * +get_spec_from_list (GList *list, + const gchar *name) +{ + GList *l; + + for (l = list; l != NULL; l = g_list_next (l)) + { + TpContactInfoFieldSpec *spec = l->data; + + if (!tp_strdiff (spec->name, name)) + return spec; + } + + return NULL; +} + +static void +add_row (GtkGrid *grid, + GtkWidget *title, + GtkWidget *value, + gboolean contact_info) +{ + /* Title */ + gtk_grid_attach_next_to (grid, title, NULL, GTK_POS_BOTTOM, 1, 1); + gtk_misc_set_alignment (GTK_MISC (title), 1, 0.5); + gtk_style_context_add_class (gtk_widget_get_style_context (title), + GTK_STYLE_CLASS_DIM_LABEL); + gtk_widget_show (title); + + /* Value */ + gtk_grid_attach_next_to (grid, value, title, GTK_POS_RIGHT, + contact_info ? 2 : 1, 1); + gtk_widget_set_hexpand (value, TRUE); + if (GTK_IS_LABEL (value)) + { + gtk_misc_set_alignment (GTK_MISC (value), 0, 0.5); + gtk_label_set_selectable (GTK_LABEL (value), TRUE); + } + gtk_widget_show (value); + + if (contact_info) + { + g_object_set_data (G_OBJECT (title), + DATA_IS_CONTACT_INFO, (gpointer) TRUE); + g_object_set_data (G_OBJECT (value), + DATA_IS_CONTACT_INFO, (gpointer) TRUE); + } +} + +static guint +fill_contact_info_grid (TpawUserInfo *self) +{ + TpConnection *connection; + TpContact *contact; + GList *specs, *l; + guint n_rows = 0; + GList *info; + const char **field_names = tpaw_contact_info_get_field_names (NULL); + guint i; + + g_assert (self->priv->details_to_set == NULL); + + connection = tp_account_get_connection (self->priv->account); + contact = tp_connection_get_self_contact (connection); + specs = tp_connection_dup_contact_info_supported_fields (connection); + info = tp_contact_dup_contact_info (contact); + + /* Look at the fields set in our vCard */ + for (l = info; l != NULL; l = l->next) + { + TpContactInfoField *field = l->data; + + /* For some reason it can happen that the vCard contains fields the CM + * claims to be not supported. This is a workaround for gabble bug + * https://bugs.freedesktop.org/show_bug.cgi?id=64319. But we shouldn't + * crash on buggy CM anyway. */ + if (get_spec_from_list (specs, field->field_name) == NULL) + { + DEBUG ("Buggy CM: self's vCard contains %s field but it is not in " + "Connection' supported fields", field->field_name); + continue; + } + + /* make a copy for the details_to_set list */ + field = tp_contact_info_field_copy (field); + DEBUG ("Field %s is in our vCard", field->field_name); + + self->priv->details_to_set = g_list_prepend (self->priv->details_to_set, + field); + } + + /* Add fields which are supported but not in the vCard */ + for (i = 0; field_names[i] != NULL; i++) + { + TpContactInfoFieldSpec *spec; + TpContactInfoField *field; + + /* Check if the field was in the vCard */ + if (field_name_in_field_list (self->priv->details_to_set, + field_names[i])) + continue; + + /* Check if the CM supports the field */ + spec = get_spec_from_list (specs, field_names[i]); + if (spec == NULL) + continue; + + /* add an empty field so user can set a value */ + field = tp_contact_info_field_new (spec->name, spec->parameters, NULL); + + self->priv->details_to_set = g_list_prepend (self->priv->details_to_set, + field); + } + + /* Add widgets for supported fields */ + self->priv->details_to_set = g_list_sort (self->priv->details_to_set, + (GCompareFunc) tpaw_contact_info_field_spec_cmp); + + for (l = self->priv->details_to_set; l != NULL; l= g_list_next (l)) + { + TpContactInfoField *field = l->data; + GtkWidget *label, *w; + TpContactInfoFieldSpec *spec; + gboolean has_field; + char *title; + + has_field = tpaw_contact_info_lookup_field (field->field_name, + NULL, NULL); + if (!has_field) + { + /* We don't display this field so we can't change it. + * But we put it in the details_to_set list so it won't be erased + * when calling SetContactInfo (bgo #630427) */ + DEBUG ("Unhandled ContactInfo field spec: %s", field->field_name); + continue; + } + + spec = get_spec_from_list (specs, field->field_name); + /* We shouldn't have added the field to details_to_set if it's not + * supported by the CM */ + g_assert (spec != NULL); + + if (spec->flags & TP_CONTACT_INFO_FIELD_FLAG_OVERWRITTEN_BY_NICKNAME) + { + DEBUG ("Ignoring field '%s' due it to having the " + "Overwritten_By_Nickname flag", field->field_name); + continue; + } + + /* Add Title */ + title = tpaw_contact_info_field_label (field->field_name, + field->parameters, + (spec->flags & TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT)); + label = gtk_label_new (title); + g_free (title); + + /* TODO: if TP_CONTACT_INFO_FIELD_FLAG_PARAMETERS_EXACT is not set we + * should allow user to tag the vCard fields (bgo#672034) */ + + /* Add Value */ + if (!tp_strdiff (field->field_name, "bday")) + { + w = tpaw_calendar_button_new (); + + if (field->field_value[0]) + { + GDate date; + + g_date_set_parse (&date, field->field_value[0]); + if (g_date_valid (&date)) + { + tpaw_calendar_button_set_date (TPAW_CALENDAR_BUTTON (w), + &date); + } + } + + g_signal_connect (w, "date-changed", + G_CALLBACK (bday_changed_cb), self); + } + else + { + w = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (w), + field->field_value[0] ? field->field_value[0] : ""); + g_signal_connect (w, "changed", + G_CALLBACK (contact_info_changed_cb), self); + } + + add_row (GTK_GRID (self), label, w, TRUE); + + g_object_set_data ((GObject *) w, DATA_FIELD, field); + + n_rows++; + } + + tp_contact_info_spec_list_free (specs); + tp_contact_info_list_free (info); + + return n_rows; +} + +static void +grid_foreach_cb (GtkWidget *widget, + gpointer data) +{ + if (g_object_get_data (G_OBJECT (widget), DATA_IS_CONTACT_INFO) != NULL) + gtk_widget_destroy (widget); +} + +static void +request_contact_info_cb (GObject *object, + GAsyncResult *res, + gpointer user_data) +{ + TpawUserInfo *self = user_data; + TpContact *contact = TP_CONTACT (object); + guint n_rows; + 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 self */ + if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + { + g_clear_error (&error); + return; + } + g_clear_error (&error); + } + + n_rows = fill_contact_info_grid (self); + + gtk_widget_set_visible (self->priv->details_label, n_rows > 0); + gtk_spinner_stop (GTK_SPINNER (self->priv->details_spinner)); + gtk_widget_hide (self->priv->details_spinner); +} + +static void +reload_contact_info (TpawUserInfo *self) +{ + TpConnection *connection; + TpContact *contact = NULL; + TpContactInfoFlags flags; + + /* Cancel previous RequestContactInfo, if any */ + if (self->priv->details_cancellable != NULL) + g_cancellable_cancel (self->priv->details_cancellable); + g_clear_object (&self->priv->details_cancellable); + + /* Remove current contact info widgets, if any */ + gtk_container_foreach (GTK_CONTAINER (self), grid_foreach_cb, NULL); + gtk_widget_hide (self->priv->details_label); + gtk_widget_hide (self->priv->details_spinner); + + tp_clear_pointer (&self->priv->details_to_set, tp_contact_info_list_free); + self->priv->details_changed = FALSE; + + connection = tp_account_get_connection (self->priv->account); + if (connection != NULL) + contact = tp_connection_get_self_contact (connection); + + /* Display infobar if we don't have a self contact (probably offline) */ + if (contact == NULL) + { + GtkWidget *infobar; + GtkWidget *content; + GtkWidget *label; + + infobar = gtk_info_bar_new (); + gtk_info_bar_set_message_type (GTK_INFO_BAR (infobar), GTK_MESSAGE_INFO); + content = gtk_info_bar_get_content_area (GTK_INFO_BAR (infobar)); + label = gtk_label_new (_("Go online to edit your personal information.")); + gtk_container_add (GTK_CONTAINER (content), label); + gtk_widget_show (label); + + gtk_grid_attach_next_to ((GtkGrid *) self, infobar, + NULL, GTK_POS_BOTTOM, 3, 1); + gtk_widget_show (infobar); + + g_object_set_data (G_OBJECT (infobar), + DATA_IS_CONTACT_INFO, (gpointer) TRUE); + return; + } + + if (!tp_proxy_has_interface_by_id (connection, + TP_IFACE_QUARK_CONNECTION_INTERFACE_CONTACT_INFO)) + return; + + flags = tp_connection_get_contact_info_flags (connection); + if ((flags & TP_CONTACT_INFO_FLAG_CAN_SET) == 0) + return; + + /* Request the contact's info */ + gtk_widget_show (self->priv->details_spinner); + gtk_spinner_start (GTK_SPINNER (self->priv->details_spinner)); + + g_assert (self->priv->details_cancellable == NULL); + self->priv->details_cancellable = g_cancellable_new (); + tp_contact_request_contact_info_async (contact, + self->priv->details_cancellable, request_contact_info_cb, + self); +} + +static void +connection_notify_cb (TpawUserInfo *self) +{ + TpConnection *connection = tp_account_get_connection (self->priv->account); + + if (connection != NULL) + { + tp_g_signal_connect_object (connection, "notify::self-contact", + G_CALLBACK (reload_contact_info), self, G_CONNECT_SWAPPED); + } + + reload_contact_info (self); +} + +static void +identifier_notify_cb (TpAccount *account, + GParamSpec *param_spec, + TpawUserInfo *self) +{ + gtk_label_set_label (GTK_LABEL (self->priv->identifier_label), + tp_account_get_normalized_name (self->priv->account)); +} + +static void +nickname_notify_cb (TpAccount *account, + GParamSpec *param_spec, + TpawUserInfo *self) +{ + gtk_entry_set_text (GTK_ENTRY (self->priv->nickname_entry), + tp_account_get_nickname (self->priv->account)); +} + +static void +tpaw_user_info_constructed (GObject *object) +{ + TpawUserInfo *self = (TpawUserInfo *) object; + GtkGrid *grid = (GtkGrid *) self; + GtkWidget *title; + + G_OBJECT_CLASS (tpaw_user_info_parent_class)->constructed (object); + + gtk_grid_set_column_spacing (grid, 6); + gtk_grid_set_row_spacing (grid, 6); + + /* Setup id label */ + title = gtk_label_new (_("Identifier")); + self->priv->identifier_label = gtk_label_new ( + tp_account_get_normalized_name (self->priv->account)); + add_row (grid, title, self->priv->identifier_label, FALSE); + g_signal_connect_object (self->priv->account, "notify::normalized-name", + G_CALLBACK (identifier_notify_cb), self, 0); + + /* Setup nickname entry */ + title = gtk_label_new (_("Alias")); + self->priv->nickname_entry = gtk_entry_new (); + gtk_entry_set_text (GTK_ENTRY (self->priv->nickname_entry), + tp_account_get_nickname (self->priv->account)); + add_row (grid, title, self->priv->nickname_entry, FALSE); + g_signal_connect_object (self->priv->account, "notify::nickname", + G_CALLBACK (nickname_notify_cb), self, 0); + + /* Set up avatar chooser */ + self->priv->avatar_chooser = tpaw_avatar_chooser_new (self->priv->account); + gtk_grid_attach (grid, self->priv->avatar_chooser, + 2, 0, 1, 3); + gtk_widget_show (self->priv->avatar_chooser); + + /* Details label */ + self->priv->details_label = gtk_label_new (NULL); + gtk_label_set_markup (GTK_LABEL (self->priv->details_label), + _("Personal Details")); + gtk_misc_set_alignment (GTK_MISC (self->priv->details_label), 0, 0.5); + gtk_grid_attach_next_to (grid, self->priv->details_label, NULL, + GTK_POS_BOTTOM, 3, 1); + + /* Details spinner */ + self->priv->details_spinner = gtk_spinner_new (); + gtk_widget_set_hexpand (self->priv->details_spinner, TRUE); + gtk_widget_set_vexpand (self->priv->details_spinner, TRUE); + gtk_grid_attach_next_to (grid, self->priv->details_spinner, NULL, + GTK_POS_BOTTOM, 3, 1); + + g_signal_connect_swapped (self->priv->account, "notify::connection", + G_CALLBACK (connection_notify_cb), self); + connection_notify_cb (self); +} + +static void +tpaw_user_info_dispose (GObject *object) +{ + TpawUserInfo *self = (TpawUserInfo *) object; + + if (self->priv->account != NULL) + { + /* Disconnect the signal manually, because TpAccount::dispose will emit + * "notify::connection" signal before tp_g_signal_connect_object() had + * a chance to disconnect. */ + g_signal_handlers_disconnect_by_func (self->priv->account, + connection_notify_cb, self); + g_clear_object (&self->priv->account); + } + + if (self->priv->details_cancellable != NULL) + g_cancellable_cancel (self->priv->details_cancellable); + g_clear_object (&self->priv->details_cancellable); + + G_OBJECT_CLASS (tpaw_user_info_parent_class)->dispose (object); +} + +static void +tpaw_user_info_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + TpawUserInfo *self = (TpawUserInfo *) object; + + switch (property_id) + { + case PROP_ACCOUNT: + g_value_set_object (value, self->priv->account); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tpaw_user_info_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + TpawUserInfo *self = (TpawUserInfo *) object; + + switch (property_id) + { + case PROP_ACCOUNT: + g_assert (self->priv->account == NULL); /* construct-only */ + self->priv->account = g_value_dup_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +tpaw_user_info_init (TpawUserInfo *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, + TPAW_TYPE_USER_INFO, TpawUserInfoPrivate); +} + +static void +tpaw_user_info_class_init (TpawUserInfoClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + GParamSpec *param_spec; + + object_class->constructed = tpaw_user_info_constructed; + object_class->dispose = tpaw_user_info_dispose; + object_class->get_property = tpaw_user_info_get_property; + object_class->set_property = tpaw_user_info_set_property; + + g_type_class_add_private (object_class, sizeof (TpawUserInfoPrivate)); + + param_spec = g_param_spec_object ("account", + "account", + "The #TpAccount on which user info should be edited", + TP_TYPE_ACCOUNT, + G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_ACCOUNT, param_spec); +} + +GtkWidget * +tpaw_user_info_new (TpAccount *account) +{ + g_return_val_if_fail (TP_IS_ACCOUNT (account), NULL); + + return g_object_new (TPAW_TYPE_USER_INFO, + "account", account, + NULL); +} + +void +tpaw_user_info_discard (TpawUserInfo *self) +{ + g_return_if_fail (TPAW_IS_USER_INFO (self)); + + reload_contact_info (self); + gtk_entry_set_text ((GtkEntry *) self->priv->nickname_entry, + tp_account_get_nickname (self->priv->account)); +} + +static void +apply_complete_one (GSimpleAsyncResult *result) +{ + guint count; + + count = g_simple_async_result_get_op_res_gssize (result); + count--; + g_simple_async_result_set_op_res_gssize (result, count); + + if (count == 0) + g_simple_async_result_complete (result); +} + +static void +avatar_chooser_apply_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpawAvatarChooser *avatar_chooser = (TpawAvatarChooser *) source; + GSimpleAsyncResult *my_result = user_data; + GError *error = NULL; + + if (!tpaw_avatar_chooser_apply_finish (avatar_chooser, result, &error)) + g_simple_async_result_take_error (my_result, error); + + apply_complete_one (my_result); + g_object_unref (my_result); +} + +static void +set_nickname_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpAccount *account = (TpAccount *) source; + GSimpleAsyncResult *my_result = user_data; + GError *error = NULL; + + if (!tp_account_set_nickname_finish (account, result, &error)) + g_simple_async_result_take_error (my_result, error); + + apply_complete_one (my_result); + g_object_unref (my_result); +} + +static void +set_contact_info_cb (GObject *source, + GAsyncResult *result, + gpointer user_data) +{ + TpConnection *connection = (TpConnection *) source; + GSimpleAsyncResult *my_result = user_data; + GError *error = NULL; + + if (!tp_connection_set_contact_info_finish (connection, result, &error)) + g_simple_async_result_take_error (my_result, error); + + apply_complete_one (my_result); + g_object_unref (my_result); +} + +static gboolean +field_value_is_empty (TpContactInfoField *field) +{ + guint i; + + if (field->field_value == NULL) + return TRUE; + + /* Field is empty if all its values are empty */ + for (i = 0; field->field_value[i] != NULL; i++) + { + if (!tp_str_empty (field->field_value[i])) + return FALSE; + } + + return TRUE; +} + +void +tpaw_user_info_apply_async (TpawUserInfo *self, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GSimpleAsyncResult *result; + const gchar *new_nickname; + guint count = 0; + GList *l, *next; + + g_return_if_fail (TPAW_IS_USER_INFO (self)); + + result = g_simple_async_result_new ((GObject *) self, callback, user_data, + tpaw_user_info_apply_async); + + /* Apply avatar */ + tpaw_avatar_chooser_apply_async ( + (TpawAvatarChooser *) self->priv->avatar_chooser, + avatar_chooser_apply_cb, g_object_ref (result)); + count++; + + /* Apply nickname */ + new_nickname = gtk_entry_get_text (GTK_ENTRY (self->priv->nickname_entry)); + if (tp_strdiff (new_nickname, tp_account_get_nickname (self->priv->account))) + { + tp_account_set_nickname_async (self->priv->account, new_nickname, + set_nickname_cb, g_object_ref (result)); + count++; + } + + /* Remove empty fields */ + for (l = self->priv->details_to_set; l != NULL; l = next) + { + TpContactInfoField *field = l->data; + + next = l->next; + if (field_value_is_empty (field)) + { + DEBUG ("Drop empty field: %s", field->field_name); + tp_contact_info_field_free (field); + self->priv->details_to_set = + g_list_delete_link (self->priv->details_to_set, l); + } + } + + if (self->priv->details_to_set != NULL) + { + if (self->priv->details_changed) + { + tp_connection_set_contact_info_async ( + tp_account_get_connection (self->priv->account), + self->priv->details_to_set, set_contact_info_cb, + g_object_ref (result)); + count++; + } + + tp_contact_info_list_free (self->priv->details_to_set); + self->priv->details_to_set = NULL; + } + + self->priv->details_changed = FALSE; + + g_simple_async_result_set_op_res_gssize (result, count); + + g_object_unref (result); +} + +gboolean +tpaw_user_info_apply_finish (TpawUserInfo *self, + GAsyncResult *result, + GError **error) +{ + empathy_implement_finish_void (self, tpaw_user_info_apply_async); +} diff --git a/tp-account-widgets/tpaw-user-info.h b/tp-account-widgets/tpaw-user-info.h new file mode 100644 index 000000000..454884966 --- /dev/null +++ b/tp-account-widgets/tpaw-user-info.h @@ -0,0 +1,74 @@ +/* + * tpaw-user-info.h - Header for TpawUserInfo + * + * Copyright (C) 2012 - Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with This library. If not, see . + */ + +#ifndef __TPAW_USER_INFO_H__ +#define __TPAW_USER_INFO_H__ + +#include +#include + +G_BEGIN_DECLS + +#define TPAW_TYPE_USER_INFO \ + (tpaw_user_info_get_type ()) +#define TPAW_USER_INFO(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), TPAW_TYPE_USER_INFO, \ + TpawUserInfo)) +#define TPAW_USER_INFO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), TPAW_TYPE_USER_INFO, \ + TpawUserInfoClass)) +#define TPAW_IS_USER_INFO(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TPAW_TYPE_USER_INFO)) +#define TPAW_IS_USER_INFO_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), TPAW_TYPE_USER_INFO)) +#define TPAW_USER_INFO_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), TPAW_TYPE_USER_INFO, \ + TpawUserInfoClass)) + +typedef struct _TpawUserInfo TpawUserInfo; +typedef struct _TpawUserInfoClass TpawUserInfoClass; +typedef struct _TpawUserInfoPrivate TpawUserInfoPrivate; + +struct _TpawUserInfo { + GtkGrid parent; + + TpawUserInfoPrivate *priv; +}; + +struct _TpawUserInfoClass { + GtkGridClass parent_class; +}; + +GType tpaw_user_info_get_type (void) G_GNUC_CONST; + +GtkWidget *tpaw_user_info_new (TpAccount *account); + +void tpaw_user_info_discard (TpawUserInfo *self); + +void tpaw_user_info_apply_async (TpawUserInfo *self, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean tpaw_user_info_apply_finish (TpawUserInfo *self, + GAsyncResult *result, + GError **error); + + +G_END_DECLS + +#endif /* __TPAW_USER_INFO_H__ */ -- cgit v1.2.3