/* * This program 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. * * This program 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 General Public License * for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, see . * * * Authors: * Chris Lahey * * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) * */ #ifdef HAVE_CONFIG_H #include #endif #include "eab-editor.h" #include "e-contact-editor.h" #include #include #include #include #include #include #include #include "shell/e-shell.h" #include "e-util/e-util.h" #include "addressbook/printing/e-contact-print.h" #include "addressbook/gui/widgets/eab-gui-util.h" #include "eab-contact-merging.h" #include "e-contact-editor-fullname.h" #include "e-contact-editor-dyntable.h" #define SLOTS_PER_LINE 2 #define SLOTS_IN_COLLAPSED_STATE SLOTS_PER_LINE #define EMAIL_SLOTS 50 #define PHONE_SLOTS 50 #define SIP_SLOTS 4 #define IM_SLOTS 50 #define ADDRESS_SLOTS 3 #define EVOLUTION_UI_SLOT_PARAM "X-EVOLUTION-UI-SLOT" /* IM columns */ enum { COLUMN_IM_ICON, COLUMN_IM_SERVICE, COLUMN_IM_SCREENNAME, COLUMN_IM_LOCATION, COLUMN_IM_LOCATION_TYPE, COLUMN_IM_SERVICE_FIELD, NUM_IM_COLUMNS }; typedef struct { EContactEditor *editor; ESource *source; } ConnectClosure; static void e_contact_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec); static void e_contact_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec); static void e_contact_editor_constructed (GObject *object); static void e_contact_editor_dispose (GObject *object); static void e_contact_editor_raise (EABEditor *editor); static void e_contact_editor_show (EABEditor *editor); static void e_contact_editor_save_contact (EABEditor *editor, gboolean should_close); static void e_contact_editor_close (EABEditor *editor); static gboolean e_contact_editor_is_valid (EABEditor *editor); static gboolean e_contact_editor_is_changed (EABEditor *editor); static GtkWindow * e_contact_editor_get_window (EABEditor *editor); static void save_contact (EContactEditor *ce, gboolean should_close); static void entry_activated (EContactEditor *editor); static void set_entry_text (EContactEditor *editor, GtkEntry *entry, const gchar *string); static void sensitize_ok (EContactEditor *ce); static EABEditorClass *parent_class = NULL; enum { PROP_0, PROP_SOURCE_CLIENT, PROP_TARGET_CLIENT, PROP_CONTACT, PROP_IS_NEW_CONTACT, PROP_EDITABLE, PROP_CHANGED, PROP_WRITABLE_FIELDS, PROP_REQUIRED_FIELDS }; enum { DYNAMIC_LIST_EMAIL, DYNAMIC_LIST_PHONE, DYNAMIC_LIST_ADDRESS }; static struct { EContactField field_id; const gchar *type_1; const gchar *type_2; } phones[] = { { E_CONTACT_PHONE_ASSISTANT, EVC_X_ASSISTANT, NULL }, { E_CONTACT_PHONE_BUSINESS, "WORK", "VOICE" }, { E_CONTACT_PHONE_BUSINESS_FAX, "WORK", "FAX" }, { E_CONTACT_PHONE_CALLBACK, EVC_X_CALLBACK, NULL }, { E_CONTACT_PHONE_CAR, "CAR", NULL }, { E_CONTACT_PHONE_COMPANY, "X-EVOLUTION-COMPANY", NULL }, { E_CONTACT_PHONE_HOME, "HOME", "VOICE" }, { E_CONTACT_PHONE_HOME_FAX, "HOME", "FAX" }, { E_CONTACT_PHONE_ISDN, "ISDN", NULL }, { E_CONTACT_PHONE_MOBILE, "CELL", NULL }, { E_CONTACT_PHONE_OTHER, "VOICE", NULL }, { E_CONTACT_PHONE_OTHER_FAX, "FAX", NULL }, { E_CONTACT_PHONE_PAGER, "PAGER", NULL }, { E_CONTACT_PHONE_PRIMARY, "PREF", NULL }, { E_CONTACT_PHONE_RADIO, EVC_X_RADIO, NULL }, { E_CONTACT_PHONE_TELEX, EVC_X_TELEX, NULL }, { E_CONTACT_PHONE_TTYTDD, EVC_X_TTYTDD, NULL } }; /* Defaults from the table above */ static const gint phones_default[] = { 1, 6, 9, 2, 7, 12, 10, 10 }; static EContactField addresses[] = { E_CONTACT_ADDRESS_WORK, E_CONTACT_ADDRESS_HOME, E_CONTACT_ADDRESS_OTHER }; static EContactField address_labels[] = { E_CONTACT_ADDRESS_LABEL_WORK, E_CONTACT_ADDRESS_LABEL_HOME, E_CONTACT_ADDRESS_LABEL_OTHER }; static const gchar *address_name[] = { "work", "home", "other" }; static struct { EContactField field; const gchar *pretty_name; } im_service[] = { { E_CONTACT_IM_AIM, N_ ("AIM") }, { E_CONTACT_IM_JABBER, N_ ("Jabber") }, { E_CONTACT_IM_YAHOO, N_ ("Yahoo") }, { E_CONTACT_IM_GADUGADU, N_ ("Gadu-Gadu") }, { E_CONTACT_IM_MSN, N_ ("MSN") }, { E_CONTACT_IM_ICQ, N_ ("ICQ") }, { E_CONTACT_IM_GROUPWISE, N_ ("GroupWise") }, { E_CONTACT_IM_SKYPE, N_ ("Skype") }, { E_CONTACT_IM_TWITTER, N_ ("Twitter") }, { E_CONTACT_IM_GOOGLE_TALK, N_ ("Google Talk")} }; static EContactField im_service_fetch_set[] = { E_CONTACT_IM_AIM, E_CONTACT_IM_JABBER, E_CONTACT_IM_YAHOO, E_CONTACT_IM_GADUGADU, E_CONTACT_IM_MSN, E_CONTACT_IM_ICQ, E_CONTACT_IM_GROUPWISE, E_CONTACT_IM_SKYPE, E_CONTACT_IM_TWITTER, E_CONTACT_IM_GOOGLE_TALK }; /* Defaults from the table above */ static const gint im_service_default[] = { 0, 2, 4, 5 }; static struct { const gchar *name; const gchar *pretty_name; } common_location[] = { { "WORK", N_ ("Work") }, { "HOME", N_ ("Home") }, { "OTHER", N_ ("Other") } }; /* Default from the table above */ static const gint email_default[] = { 0, 1, 2, 2 }; static const gint sips_default[] = { 0, 1, 2, 2 }; #define STRING_IS_EMPTY(x) (!(x) || !(*(x))) #define STRING_MAKE_NON_NULL(x) ((x) ? (x) : "") struct _EContactEditorPrivate { /* item specific fields */ EBookClient *source_client; EBookClient *target_client; EContact *contact; GtkBuilder *builder; GtkWidget *app; GtkWidget *file_selector; EContactName *name; /* Whether we are editing a new contact or an existing one */ guint is_new_contact : 1; /* Whether an image is associated with a contact. */ guint image_set : 1; /* Whether the contact has been changed since bringing up the contact editor */ guint changed : 1; /* Wheter should check for contact to merge. Only when name or email are changed */ guint check_merge : 1; /* Whether the contact editor will accept modifications, save */ guint target_editable : 1; /* Whether an async wombat call is in progress */ guint in_async_call : 1; /* Whether an image is changed */ guint image_changed : 1; /* Whether to try to reduce space used */ guint compress_ui : 1; GSList *writable_fields; GSList *required_fields; GCancellable *cancellable; /* signal ids for "writable_status" */ gint target_editable_id; GtkWidget *fullname_dialog; GtkWidget *categories_dialog; GtkUIManager *ui_manager; EFocusTracker *focus_tracker; }; G_DEFINE_TYPE (EContactEditor, e_contact_editor, EAB_TYPE_EDITOR) static GtkActionEntry undo_entries[] = { { "undo-menu", "Undo menu", NULL, NULL, NULL, NULL }, /* just a fake undo menu, to get shortcuts working */ { "undo", "edit-undo", N_("_Undo"), "z", N_("Undo"), NULL }, /* Handled by EFocusTracker */ { "redo", "edit-redo", N_("_Redo"), "y", N_("Redo"), NULL } /* Handled by EFocusTracker */ }; static void connect_closure_free (ConnectClosure *connect_closure) { if (connect_closure->editor != NULL) g_object_unref (connect_closure->editor); if (connect_closure->source != NULL) g_object_unref (connect_closure->source); g_slice_free (ConnectClosure, connect_closure); } static void e_contact_editor_contact_added (EABEditor *editor, const GError *error, EContact *contact) { GtkWindow *window; const gchar *message; if (error == NULL) return; if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; window = eab_editor_get_window (editor); message = _("Error adding contact"); eab_error_dialog (NULL, window, message, error); } static void e_contact_editor_contact_modified (EABEditor *editor, const GError *error, EContact *contact) { GtkWindow *window; const gchar *message; if (error == NULL) return; if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; window = eab_editor_get_window (editor); message = _("Error modifying contact"); eab_error_dialog (NULL, window, message, error); } static void e_contact_editor_contact_deleted (EABEditor *editor, const GError *error, EContact *contact) { GtkWindow *window; const gchar *message; if (error == NULL) return; if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; window = eab_editor_get_window (editor); message = _("Error removing contact"); eab_error_dialog (NULL, window, message, error); } static void e_contact_editor_closed (EABEditor *editor) { g_object_unref (editor); } static void e_contact_editor_class_init (EContactEditorClass *class) { GObjectClass *object_class = G_OBJECT_CLASS (class); EABEditorClass *editor_class = EAB_EDITOR_CLASS (class); g_type_class_add_private (class, sizeof (EContactEditorPrivate)); parent_class = g_type_class_ref (EAB_TYPE_EDITOR); object_class->set_property = e_contact_editor_set_property; object_class->get_property = e_contact_editor_get_property; object_class->constructed = e_contact_editor_constructed; object_class->dispose = e_contact_editor_dispose; editor_class->raise = e_contact_editor_raise; editor_class->show = e_contact_editor_show; editor_class->close = e_contact_editor_close; editor_class->is_valid = e_contact_editor_is_valid; editor_class->save_contact = e_contact_editor_save_contact; editor_class->is_changed = e_contact_editor_is_changed; editor_class->get_window = e_contact_editor_get_window; editor_class->contact_added = e_contact_editor_contact_added; editor_class->contact_modified = e_contact_editor_contact_modified; editor_class->contact_deleted = e_contact_editor_contact_deleted; editor_class->editor_closed = e_contact_editor_closed; g_object_class_install_property ( object_class, PROP_SOURCE_CLIENT, g_param_spec_object ( "source_client", "Source EBookClient", NULL, E_TYPE_BOOK_CLIENT, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_TARGET_CLIENT, g_param_spec_object ( "target_client", "Target EBookClient", NULL, E_TYPE_BOOK_CLIENT, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_CONTACT, g_param_spec_object ( "contact", "Contact", NULL, E_TYPE_CONTACT, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_IS_NEW_CONTACT, g_param_spec_boolean ( "is_new_contact", "Is New Contact", NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_WRITABLE_FIELDS, g_param_spec_pointer ( "writable_fields", "Writable Fields", NULL, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_REQUIRED_FIELDS, g_param_spec_pointer ( "required_fields", "Required Fields", NULL, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_EDITABLE, g_param_spec_boolean ( "editable", "Editable", NULL, FALSE, G_PARAM_READWRITE)); g_object_class_install_property ( object_class, PROP_CHANGED, g_param_spec_boolean ( "changed", "Changed", NULL, FALSE, G_PARAM_READWRITE)); } static void entry_activated (EContactEditor *editor) { save_contact (editor, TRUE); } /* FIXME: Linear time... */ static gboolean is_field_supported (EContactEditor *editor, EContactField field_id) { GSList *fields, *iter; const gchar *field; fields = editor->priv->writable_fields; if (!fields) return FALSE; field = e_contact_field_name (field_id); if (!field) return FALSE; for (iter = fields; iter; iter = iter->next) { const gchar *this_field = iter->data; if (!this_field) continue; if (!strcmp (field, this_field)) return TRUE; } return FALSE; } /* This function tells you whether name_to_style will make sense. */ static gboolean style_makes_sense (const EContactName *name, const gchar *company, gint style) { switch (style) { case 0: /* Fall Through */ case 1: return TRUE; case 2: if (name) { if (name->additional && *name->additional) return TRUE; else return FALSE; } return FALSE; case 3: if (company && *company) return TRUE; else return FALSE; case 4: /* Fall Through */ case 5: if (company && *company && name && ((name->given && *name->given) || (name->family && *name->family))) return TRUE; else return FALSE; default: return FALSE; } } static gchar * name_to_style (const EContactName *name, const gchar *company, gint style) { gchar *string; gchar *strings[4], **stringptr; gchar *midstring[4], **midstrptr; gchar *substring; switch (style) { case 0: stringptr = strings; if (name) { if (name->family && *name->family) *(stringptr++) = name->family; if (name->given && *name->given) *(stringptr++) = name->given; } *stringptr = NULL; string = g_strjoinv (", ", strings); break; case 1: stringptr = strings; if (name) { if (name->given && *name->given) *(stringptr++) = name->given; if (name->family && *name->family) *(stringptr++) = name->family; } *stringptr = NULL; string = g_strjoinv (" ", strings); break; case 2: midstrptr = midstring; if (name) { if (name->family && *name->family) *(midstrptr++) = name->family; if (name->given && *name->given) *(midstrptr++) = name->given; } *midstrptr = NULL; stringptr = strings; *(stringptr++) = g_strjoinv(", ", midstring); if (name) { if (name->additional && *name->additional) *(stringptr++) = name->additional; } *stringptr = NULL; string = g_strjoinv (" ", strings); break; case 3: string = g_strdup (company); break; case 4: /* Fall Through */ case 5: stringptr = strings; if (name) { if (name->family && *name->family) *(stringptr++) = name->family; if (name->given && *name->given) *(stringptr++) = name->given; } *stringptr = NULL; substring = g_strjoinv (", ", strings); if (!(company && *company)) company = ""; if (style == 4) string = g_strdup_printf ("%s (%s)", substring, company); else string = g_strdup_printf ("%s (%s)", company, substring); g_free (substring); break; default: string = g_strdup (""); } return string; } static gint file_as_get_style (EContactEditor *editor) { GtkEntry *file_as = GTK_ENTRY ( gtk_bin_get_child (GTK_BIN ( e_builder_get_widget (editor->priv->builder, "combo-file-as")))); GtkEntry *company_w = GTK_ENTRY ( e_builder_get_widget (editor->priv->builder, "entry-company")); gchar *filestring; gchar *trystring; EContactName *name = editor->priv->name; const gchar *company; gint i; if (!(file_as && GTK_IS_ENTRY (file_as))) return -1; company = gtk_entry_get_text (GTK_ENTRY (company_w)); filestring = g_strdup (gtk_entry_get_text (file_as)); for (i = 0; i < 6; i++) { trystring = name_to_style (name, company, i); if (!strcmp (trystring, filestring)) { g_free (trystring); g_free (filestring); return i; } g_free (trystring); } g_free (filestring); return -1; } static void file_as_set_style (EContactEditor *editor, gint style) { gchar *string; gint i; GList *strings = NULL; GtkComboBox *combo_file_as = GTK_COMBO_BOX ( e_builder_get_widget (editor->priv->builder, "combo-file-as")); GtkEntry *company_w = GTK_ENTRY ( e_builder_get_widget (editor->priv->builder, "entry-company")); const gchar *company; if (!(combo_file_as && GTK_IS_COMBO_BOX (combo_file_as))) return; company = gtk_entry_get_text (GTK_ENTRY (company_w)); if (style == -1) { GtkWidget *entry; entry = gtk_bin_get_child (GTK_BIN (combo_file_as)); if (entry) { string = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); strings = g_list_append (strings, string); } } for (i = 0; i < 6; i++) { if (style_makes_sense (editor->priv->name, company, i)) { gchar *u; u = name_to_style (editor->priv->name, company, i); if (!STRING_IS_EMPTY (u)) strings = g_list_append (strings, u); else g_free (u); } } if (combo_file_as) { GList *l; GtkListStore *list_store; GtkTreeIter iter; list_store = GTK_LIST_STORE ( gtk_combo_box_get_model (combo_file_as)); gtk_list_store_clear (list_store); for (l = strings; l; l = l->next) { gtk_list_store_append (list_store, &iter); gtk_list_store_set (list_store, &iter, 0, l->data, -1); } } g_list_foreach (strings, (GFunc) g_free, NULL); g_list_free (strings); if (style != -1) { string = name_to_style (editor->priv->name, company, style); set_entry_text ( editor, GTK_ENTRY (gtk_bin_get_child ( GTK_BIN (combo_file_as))), string); g_free (string); } } static void name_entry_changed (GtkWidget *widget, EContactEditor *editor) { gint style = 0; const gchar *string; style = file_as_get_style (editor); e_contact_name_free (editor->priv->name); string = gtk_entry_get_text (GTK_ENTRY (widget)); editor->priv->name = e_contact_name_from_string (string); file_as_set_style (editor, style); editor->priv->check_merge = TRUE; sensitize_ok (editor); if (string && !*string) gtk_window_set_title ( GTK_WINDOW (editor->priv->app), _("Contact Editor")); } static void file_as_combo_changed (GtkWidget *widget, EContactEditor *editor) { GtkWidget *entry; gchar *string = NULL; entry = gtk_bin_get_child (GTK_BIN (widget)); if (entry) string = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); if (string && *string) { gchar *title; title = g_strdup_printf (_("Contact Editor - %s"), string); gtk_window_set_title (GTK_WINDOW (editor->priv->app), title); g_free (title); } else { gtk_window_set_title ( GTK_WINDOW (editor->priv->app), _("Contact Editor")); } sensitize_ok (editor); g_free (string); } static void company_entry_changed (GtkWidget *widget, EContactEditor *editor) { gint style = 0; style = file_as_get_style (editor); file_as_set_style (editor, style); } static void update_file_as_combo (EContactEditor *editor) { file_as_set_style (editor, file_as_get_style (editor)); } static void fill_in_source_field (EContactEditor *editor) { GtkWidget *source_menu; if (!editor->priv->target_client) return; source_menu = e_builder_get_widget ( editor->priv->builder, "client-combo-box"); e_source_combo_box_set_active ( E_SOURCE_COMBO_BOX (source_menu), e_client_get_source (E_CLIENT (editor->priv->target_client))); } static void sensitize_ok (EContactEditor *ce) { GtkWidget *widget; gboolean allow_save; GtkWidget *entry_fullname = e_builder_get_widget (ce->priv->builder, "entry-fullname"); GtkWidget *entry_file_as = gtk_bin_get_child (GTK_BIN ( e_builder_get_widget (ce->priv->builder, "combo-file-as"))); GtkWidget *company_name = e_builder_get_widget (ce->priv->builder, "entry-company"); const gchar *name_entry_string = gtk_entry_get_text (GTK_ENTRY (entry_fullname)); const gchar *file_as_entry_string = gtk_entry_get_text (GTK_ENTRY (entry_file_as)); const gchar *company_name_string = gtk_entry_get_text (GTK_ENTRY (company_name)); allow_save = ce->priv->target_editable && ce->priv->changed; if (!strcmp (name_entry_string, "") || !strcmp (file_as_entry_string, "")) { if (strcmp (company_name_string , "")) { allow_save = TRUE; } else allow_save = FALSE; } widget = e_builder_get_widget (ce->priv->builder, "button-ok"); gtk_widget_set_sensitive (widget, allow_save); } static void object_changed (GObject *object, EContactEditor *editor) { if (!editor->priv->target_editable) { g_warning ("non-editable contact editor has an editable field in it."); return; } if (!editor->priv->check_merge && GTK_IS_WIDGET (object)) { const gchar *widget_name; widget_name = gtk_widget_get_name (GTK_WIDGET (object)); if (widget_name && ((g_str_equal (widget_name, "fullname")) || (g_str_equal (widget_name, "nickname")) || (g_str_equal (widget_name, "file-as")) || (g_str_has_prefix (widget_name, "email-")))) editor->priv->check_merge = TRUE; } if (!editor->priv->changed) { editor->priv->changed = TRUE; sensitize_ok (editor); } } static void image_chooser_changed (GtkWidget *widget, EContactEditor *editor) { editor->priv->image_set = TRUE; editor->priv->image_changed = TRUE; } static void set_entry_text (EContactEditor *editor, GtkEntry *entry, const gchar *string) { const gchar *oldstring = gtk_entry_get_text (entry); if (!string) string = ""; if (strcmp (string, oldstring)) { g_signal_handlers_block_matched ( entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor); gtk_entry_set_text (entry, string); g_signal_handlers_unblock_matched ( entry, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor); } } static void init_email_record_location (EContactEditor *editor) { GtkWidget *w; GtkListStore *store; gint i; EContactEditorDynTable *dyntable; w = e_builder_get_widget (editor->priv->builder, "mail-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); store = e_contact_editor_dyntable_get_combo_store (dyntable); for (i = 0; i < G_N_ELEMENTS (common_location); i++) { GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, DYNTABLE_COMBO_COLUMN_TEXT, _(common_location[i].pretty_name), DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE, -1); } e_contact_editor_dyntable_set_combo_defaults (dyntable, email_default, G_N_ELEMENTS (email_default)); } static const gchar * email_index_to_location (gint index) { return common_location[index].name; } static void phone_index_to_type (gint index, const gchar **type_1, const gchar **type_2) { *type_1 = phones [index].type_1; *type_2 = phones [index].type_2; } static void sip_index_to_type (gint index, const gchar **type_1) { *type_1 = common_location[index].name; } static gint get_email_location (EVCardAttribute *attr) { gint i; for (i = 0; i < G_N_ELEMENTS (common_location); i++) { if (e_vcard_attribute_has_type (attr, common_location[i].name)) return i; } return -1; } static gint get_phone_type (EVCardAttribute *attr) { gint i; for (i = 0; i < G_N_ELEMENTS (phones); i++) { if (e_vcard_attribute_has_type (attr, phones[i].type_1) && (phones[i].type_2 == NULL || e_vcard_attribute_has_type (attr, phones[i].type_2))) return i; } return -1; } static gint get_sip_type (EVCardAttribute *attr) { gint ii; for (ii = 0; ii < G_N_ELEMENTS (common_location); ii++) { if (e_vcard_attribute_has_type (attr, common_location[ii].name)) return ii; } return -1; } static EVCardAttributeParam * get_ui_slot_param (EVCardAttribute *attr) { EVCardAttributeParam *param = NULL; GList *param_list; GList *l; param_list = e_vcard_attribute_get_params (attr); for (l = param_list; l; l = g_list_next (l)) { const gchar *str; param = l->data; str = e_vcard_attribute_param_get_name (param); if (!g_ascii_strcasecmp (str, EVOLUTION_UI_SLOT_PARAM)) break; param = NULL; } return param; } static gint get_ui_slot (EVCardAttribute *attr) { EVCardAttributeParam *param; gint slot = -1; param = get_ui_slot_param (attr); if (param) { GList *value_list; value_list = e_vcard_attribute_param_get_values (param); slot = atoi (value_list->data); } return slot; } static void set_ui_slot (EVCardAttribute *attr, gint slot) { EVCardAttributeParam *param; gchar *slot_str; param = get_ui_slot_param (attr); if (!param) { param = e_vcard_attribute_param_new (EVOLUTION_UI_SLOT_PARAM); e_vcard_attribute_add_param (attr, param); } e_vcard_attribute_param_remove_values (param); slot_str = g_strdup_printf ("%d", slot); e_vcard_attribute_param_add_value (param, slot_str); g_free (slot_str); } static void fill_in_email (EContactEditor *editor) { GList *email_attr_list; GList *l; GtkWidget *w; EContactEditorDynTable *dyntable; GtkListStore *data_store; GtkTreeIter iter; w = e_builder_get_widget (editor->priv->builder, "mail-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); /* Clear */ e_contact_editor_dyntable_clear_data (dyntable); /* Fill in */ data_store = e_contact_editor_dyntable_extract_data (dyntable); email_attr_list = e_contact_get_attributes ( editor->priv->contact, E_CONTACT_EMAIL); for (l = email_attr_list; l; l = g_list_next (l)) { EVCardAttribute *attr = l->data; gchar *email_address; gint email_location; gint slot; email_address = e_vcard_attribute_get_value (attr); email_location = get_email_location (attr); slot = get_ui_slot (attr); if (slot < 1) slot = EMAIL_SLOTS + 1; //add at the end gtk_list_store_append (data_store, &iter); gtk_list_store_set (data_store, &iter, DYNTABLE_STORE_COLUMN_SORTORDER, slot, DYNTABLE_STORE_COLUMN_SELECTED_ITEM, email_location, DYNTABLE_STORE_COLUMN_ENTRY_STRING, email_address, -1); g_free (email_address); } g_list_free_full (email_attr_list, (GDestroyNotify) e_vcard_attribute_free); e_contact_editor_dyntable_fill_in_data (dyntable); } static void extract_email (EContactEditor *editor) { GList *attr_list = NULL; GList *old_attr_list; GList *ll; gint i; GtkWidget *w; EContactEditorDynTable *dyntable; GtkListStore *data_store; GtkTreeModel *tree_model; GtkTreeIter iter; gboolean valid; w = e_builder_get_widget (editor->priv->builder, "mail-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); data_store = e_contact_editor_dyntable_extract_data (dyntable); tree_model = GTK_TREE_MODEL (data_store); valid = gtk_tree_model_get_iter_first (tree_model, &iter); while (valid) { gchar *address; gint location; EVCardAttribute *attr; attr = e_vcard_attribute_new ( "", e_contact_vcard_attribute (E_CONTACT_EMAIL)); gtk_tree_model_get (tree_model,&iter, DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &location, DYNTABLE_STORE_COLUMN_ENTRY_STRING, &address, -1); if (location >= 0) e_vcard_attribute_add_param_with_value ( attr, e_vcard_attribute_param_new (EVC_TYPE), email_index_to_location (location)); e_vcard_attribute_add_value (attr, address); attr_list = g_list_prepend (attr_list, attr); valid = gtk_tree_model_iter_next (tree_model, &iter); } attr_list = g_list_reverse (attr_list); /* Splice in the old attributes, minus the EMAIL_SLOTS first */ old_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_EMAIL); for (ll = old_attr_list, i = 1; ll && i <= EMAIL_SLOTS; i++) { e_vcard_attribute_free (ll->data); ll = g_list_delete_link (ll, ll); } old_attr_list = ll; attr_list = g_list_concat (attr_list, old_attr_list); e_contact_set_attributes (editor->priv->contact, E_CONTACT_EMAIL, attr_list); g_list_free_full (attr_list, (GDestroyNotify) e_vcard_attribute_free); } static void sensitize_email (EContactEditor *editor) { gboolean enabled = FALSE; GtkWidget *w; EContactEditorDynTable *dyntable; guint max_entries = SLOTS_IN_COLLAPSED_STATE; w = e_builder_get_widget (editor->priv->builder, "mail-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); if (editor->priv->target_editable) { if (is_field_supported (editor, E_CONTACT_EMAIL)) { enabled = TRUE; max_entries = EMAIL_SLOTS; } else if (is_field_supported (editor, E_CONTACT_EMAIL_4)) { enabled = TRUE; max_entries = 4; } else if (is_field_supported (editor, E_CONTACT_EMAIL_3)) { enabled = TRUE; max_entries = 3; } else if (is_field_supported (editor, E_CONTACT_EMAIL_2)) { enabled = TRUE; max_entries = 2; } else if (is_field_supported (editor, E_CONTACT_EMAIL_1)) { enabled = TRUE; max_entries = 1; } } gtk_widget_set_sensitive (w, enabled); e_contact_editor_dyntable_set_max_entries (dyntable, max_entries); } static void set_arrow_image (EContactEditor *editor, const gchar *arrow_widget, gboolean expanded) { GtkWidget *arrow; arrow = e_builder_get_widget (editor->priv->builder, arrow_widget); if (expanded) gtk_arrow_set ( GTK_ARROW (arrow), GTK_ARROW_DOWN, GTK_SHADOW_NONE); else gtk_arrow_set ( GTK_ARROW (arrow), GTK_ARROW_RIGHT, GTK_SHADOW_NONE); } static gboolean is_arrow_image_arrow_down (EContactEditor *editor, const gchar *arrow_widget) { GtkWidget *arrow; gint value; arrow = e_builder_get_widget (editor->priv->builder, arrow_widget); g_object_get (arrow, "arrow-type", &value, NULL); if (value == GTK_ARROW_DOWN) return TRUE; return FALSE; } static void expand_widget_list (EContactEditor *editor, const gchar **widget_names, gboolean expanded) { gint i; for (i = 0; widget_names[i]; i++) gtk_widget_set_visible ( e_builder_get_widget (editor->priv->builder, widget_names[i]), expanded); } static void expand_web (EContactEditor *editor, gboolean expanded) { const gchar *names[] = { "label-videourl", "label-fburl", "entry-videourl", "entry-fburl", NULL }; set_arrow_image (editor, "arrow-web-expand", expanded); expand_widget_list (editor, names, expanded); } static void expand_phone (EContactEditor *editor, gboolean expanded) { GtkWidget *w; EContactEditorDynTable *dyntable; set_arrow_image (editor, "arrow-phone-expand", expanded); w = e_builder_get_widget (editor->priv->builder, "phone-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); if (expanded) e_contact_editor_dyntable_set_show_max (dyntable, PHONE_SLOTS); else e_contact_editor_dyntable_set_show_max (dyntable, SLOTS_IN_COLLAPSED_STATE); } static void expand_sip (EContactEditor *editor, gboolean expanded) { GtkWidget *w; EContactEditorDynTable *dyntable; set_arrow_image (editor, "arrow-sip-expand", expanded); w = e_builder_get_widget (editor->priv->builder, "sip-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); if (expanded) e_contact_editor_dyntable_set_show_max (dyntable, SIP_SLOTS); else e_contact_editor_dyntable_set_show_max (dyntable, SLOTS_IN_COLLAPSED_STATE); } static void expand_im (EContactEditor *editor, gboolean expanded) { GtkWidget *w; EContactEditorDynTable *dyntable; set_arrow_image (editor, "arrow-im-expand", expanded); w = e_builder_get_widget (editor->priv->builder, "im-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); if (expanded) e_contact_editor_dyntable_set_show_max (dyntable, IM_SLOTS); else e_contact_editor_dyntable_set_show_max (dyntable, SLOTS_IN_COLLAPSED_STATE); } static void expand_mail (EContactEditor *editor, gboolean expanded) { GtkWidget *w; EContactEditorDynTable *dyntable; set_arrow_image (editor, "arrow-mail-expand", expanded); w = e_builder_get_widget (editor->priv->builder, "mail-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); if (expanded) e_contact_editor_dyntable_set_show_max (dyntable, EMAIL_SLOTS); else e_contact_editor_dyntable_set_show_max (dyntable, SLOTS_IN_COLLAPSED_STATE); } static void row_added_mail (EContactEditorDynTable *dyntable, EContactEditor *editor) { expand_mail (editor, TRUE); } static void init_email (EContactEditor *editor) { GtkWidget *w; EContactEditorDynTable *dyntable; w = e_builder_get_widget (editor->priv->builder, "mail-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); e_contact_editor_dyntable_set_max_entries (dyntable, EMAIL_SLOTS); e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE); e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_IN_COLLAPSED_STATE); g_signal_connect ( w, "changed", G_CALLBACK (object_changed), editor); g_signal_connect_swapped ( w, "activate", G_CALLBACK (entry_activated), editor); g_signal_connect ( w, "row-added", G_CALLBACK (row_added_mail), editor); init_email_record_location (editor); expand_mail (editor, TRUE); } static void fill_in_phone (EContactEditor *editor) { GList *tel_attr_list; GList *l; GtkWidget *w; EContactEditorDynTable *dyntable; GtkListStore *data_store; GtkTreeIter iter; w = e_builder_get_widget (editor->priv->builder, "phone-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); /* Clear */ e_contact_editor_dyntable_clear_data (dyntable); /* Fill in */ tel_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_TEL); data_store = e_contact_editor_dyntable_extract_data (dyntable); for (l = tel_attr_list; l; l = g_list_next (l)) { EVCardAttribute *attr = l->data; gchar *phone; gint slot; gint phone_type; slot = get_ui_slot (attr); if (slot < 0) slot = PHONE_SLOTS + 1; /* append at the end */ phone_type = get_phone_type (attr); phone = e_vcard_attribute_get_value (attr); gtk_list_store_append (data_store, &iter); gtk_list_store_set (data_store,&iter, DYNTABLE_STORE_COLUMN_SORTORDER, slot, DYNTABLE_STORE_COLUMN_SELECTED_ITEM, phone_type, DYNTABLE_STORE_COLUMN_ENTRY_STRING, phone, -1); g_free (phone); } e_contact_editor_dyntable_fill_in_data (dyntable); g_list_free_full (tel_attr_list, (GDestroyNotify) e_vcard_attribute_free); } static void extract_phone (EContactEditor *editor) { GList *tel_attr_list = NULL; GList *old_attr_list; GList *ll; gint i; GtkWidget *w; EContactEditorDynTable *dyntable; GtkListStore *data_store; GtkTreeModel *tree_model; GtkTreeIter iter; gboolean valid; w = e_builder_get_widget (editor->priv->builder, "phone-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); data_store = e_contact_editor_dyntable_extract_data (dyntable); tree_model = GTK_TREE_MODEL (data_store); valid = gtk_tree_model_get_iter_first (tree_model, &iter); while (valid) { gint phone_type; gchar *phone; EVCardAttribute *attr; gtk_tree_model_get (tree_model,&iter, DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &phone_type, DYNTABLE_STORE_COLUMN_ENTRY_STRING, &phone, -1); attr = e_vcard_attribute_new ("", EVC_TEL); if (phone_type >= 0) { const gchar *type_1; const gchar *type_2; phone_index_to_type (phone_type, &type_1, &type_2); e_vcard_attribute_add_param_with_value ( attr, e_vcard_attribute_param_new (EVC_TYPE), type_1); if (type_2) e_vcard_attribute_add_param_with_value ( attr, e_vcard_attribute_param_new (EVC_TYPE), type_2); } e_vcard_attribute_add_value (attr, phone); tel_attr_list = g_list_prepend (tel_attr_list, attr); valid = gtk_tree_model_iter_next (tree_model, &iter); } /* Splice in the old attributes, minus the PHONE_SLOTS first */ tel_attr_list = g_list_reverse (tel_attr_list); old_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_TEL); for (ll = old_attr_list, i = 1; ll && i <= PHONE_SLOTS; i++) { e_vcard_attribute_free (ll->data); ll = g_list_delete_link (ll, ll); } old_attr_list = ll; tel_attr_list = g_list_concat (tel_attr_list, old_attr_list); e_contact_set_attributes (editor->priv->contact, E_CONTACT_TEL, tel_attr_list); g_list_free_full (tel_attr_list, (GDestroyNotify) e_vcard_attribute_free); } static void init_phone_record_type (EContactEditor *editor) { GtkWidget *w; GtkListStore *store; gint i; EContactEditorDynTable *dyntable; w = e_builder_get_widget (editor->priv->builder, "phone-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); store = e_contact_editor_dyntable_get_combo_store (dyntable); for (i = 0; i < G_N_ELEMENTS (phones); i++) { GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, DYNTABLE_COMBO_COLUMN_TEXT, e_contact_pretty_name (phones[i].field_id), DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE, -1); } e_contact_editor_dyntable_set_combo_defaults (dyntable, phones_default, G_N_ELEMENTS (phones_default)); } static void row_added_phone (EContactEditorDynTable *dyntable, EContactEditor *editor) { expand_phone (editor, TRUE); } static void init_phone (EContactEditor *editor) { GtkWidget *w; EContactEditorDynTable *dyntable; w = e_builder_get_widget (editor->priv->builder, "phone-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); e_contact_editor_dyntable_set_max_entries (dyntable, PHONE_SLOTS); e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE); e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_IN_COLLAPSED_STATE); g_signal_connect ( w, "changed", G_CALLBACK (object_changed), editor); g_signal_connect_swapped ( w, "activate", G_CALLBACK (entry_activated), editor); g_signal_connect ( w, "row-added", G_CALLBACK (row_added_phone), editor); init_phone_record_type (editor); expand_phone (editor, TRUE); } static void sensitize_phone_types (EContactEditor *editor) { GtkWidget *w; GtkListStore *listStore; GtkTreeModel *model; GtkTreeIter iter; gint i; gboolean valid; w = e_builder_get_widget (editor->priv->builder, "phone-dyntable"); listStore = e_contact_editor_dyntable_get_combo_store (E_CONTACT_EDITOR_DYNTABLE (w)); model = GTK_TREE_MODEL (listStore); valid = gtk_tree_model_get_iter_first (model, &iter); for (i = 0; i < G_N_ELEMENTS (phones); i++) { if (!valid) { g_warning (G_STRLOC ": Unexpected end of phone items in combo box"); return; } gtk_list_store_set (GTK_LIST_STORE (model), &iter, DYNTABLE_COMBO_COLUMN_SENSITIVE, is_field_supported (editor, phones[i].field_id), -1); valid = gtk_tree_model_iter_next (model, &iter); } } static void sensitize_phone (EContactEditor *editor) { GtkWidget *w; gboolean enabled = FALSE; int i; w = e_builder_get_widget (editor->priv->builder, "phone-dyntable"); if (editor->priv->target_editable) { enabled = is_field_supported (editor, E_CONTACT_TEL); for (i = 0; i < G_N_ELEMENTS (phones) && !enabled; i++) { enabled = is_field_supported (editor, phones[i].field_id); } } gtk_widget_set_sensitive (w, enabled); sensitize_phone_types (editor); } static void fill_in_sip (EContactEditor *editor) { GList *sip_attr_list; GList *l; GtkWidget *w; EContactEditorDynTable *dyntable; GtkListStore *data_store; GtkTreeIter iter; w = e_builder_get_widget (editor->priv->builder, "sip-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); /* Clear */ e_contact_editor_dyntable_clear_data (dyntable); /* Fill in */ sip_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_SIP); data_store = e_contact_editor_dyntable_extract_data (dyntable); for (l = sip_attr_list; l; l = g_list_next (l)) { EVCardAttribute *attr = l->data; gchar *sip; gint sip_type; sip_type = get_sip_type (attr); sip = e_vcard_attribute_get_value (attr); if (sip_type < 0) sip_type = 2; gtk_list_store_append (data_store, &iter); gtk_list_store_set (data_store,&iter, DYNTABLE_STORE_COLUMN_SORTORDER, -1, DYNTABLE_STORE_COLUMN_SELECTED_ITEM, sip_type, DYNTABLE_STORE_COLUMN_ENTRY_STRING, sip, -1); g_free (sip); } e_contact_editor_dyntable_fill_in_data (dyntable); g_list_free_full (sip_attr_list, (GDestroyNotify) e_vcard_attribute_free); } static void extract_sip (EContactEditor *editor) { GList *sip_attr_list = NULL; GList *old_attr_list; GList *ll; gint i; GtkWidget *w; EContactEditorDynTable *dyntable; GtkListStore *data_store; GtkTreeModel *tree_model; GtkTreeIter iter; gboolean valid; w = e_builder_get_widget (editor->priv->builder, "sip-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); data_store = e_contact_editor_dyntable_extract_data (dyntable); tree_model = GTK_TREE_MODEL (data_store); valid = gtk_tree_model_get_iter_first (tree_model, &iter); while (valid) { gint sip_type; gchar *sip; EVCardAttribute *attr; gtk_tree_model_get (tree_model,&iter, DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &sip_type, DYNTABLE_STORE_COLUMN_ENTRY_STRING, &sip, -1); attr = e_vcard_attribute_new ("", EVC_X_SIP); if (sip_type >= 0) { const gchar *type_1; sip_index_to_type (sip_type, &type_1); e_vcard_attribute_add_param_with_value ( attr, e_vcard_attribute_param_new (EVC_TYPE), type_1); } e_vcard_attribute_add_value (attr, sip); sip_attr_list = g_list_prepend (sip_attr_list, attr); valid = gtk_tree_model_iter_next (tree_model, &iter); } /* Splice in the old attributes, minus the SIP_SLOTS first */ sip_attr_list = g_list_reverse (sip_attr_list); old_attr_list = e_contact_get_attributes (editor->priv->contact, E_CONTACT_SIP); for (ll = old_attr_list, i = 1; ll && i <= SIP_SLOTS; i++) { e_vcard_attribute_free (ll->data); ll = g_list_delete_link (ll, ll); } old_attr_list = ll; sip_attr_list = g_list_concat (sip_attr_list, old_attr_list); e_contact_set_attributes (editor->priv->contact, E_CONTACT_SIP, sip_attr_list); g_list_free_full (sip_attr_list, (GDestroyNotify) e_vcard_attribute_free); } static void init_sip_record_type (EContactEditor *editor) { GtkWidget *w; GtkListStore *store; gint i; EContactEditorDynTable *dyntable; w = e_builder_get_widget (editor->priv->builder, "sip-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); store = e_contact_editor_dyntable_get_combo_store (dyntable); for (i = 0; i < G_N_ELEMENTS (common_location); i++) { GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, DYNTABLE_COMBO_COLUMN_TEXT, _(common_location[i].pretty_name), DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE, -1); } e_contact_editor_dyntable_set_combo_defaults (dyntable, sips_default, G_N_ELEMENTS (sips_default)); } static void row_added_sip (EContactEditorDynTable *dyntable, EContactEditor *editor) { expand_sip (editor, TRUE); } static void init_sip (EContactEditor *editor) { GtkWidget *w; EContactEditorDynTable *dyntable; w = e_builder_get_widget (editor->priv->builder, "sip-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); e_contact_editor_dyntable_set_max_entries (dyntable, SIP_SLOTS); e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE); e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_IN_COLLAPSED_STATE); g_signal_connect ( w, "changed", G_CALLBACK (object_changed), editor); g_signal_connect_swapped ( w, "activate", G_CALLBACK (entry_activated), editor); g_signal_connect ( w, "row-added", G_CALLBACK (row_added_sip), editor); init_sip_record_type (editor); expand_sip (editor, TRUE); } static void sensitize_sip_types (EContactEditor *editor) { GtkWidget *w; GtkListStore *listStore; GtkTreeModel *model; GtkTreeIter iter; gint i; gboolean valid; w = e_builder_get_widget (editor->priv->builder, "sip-dyntable"); listStore = e_contact_editor_dyntable_get_combo_store (E_CONTACT_EDITOR_DYNTABLE (w)); model = GTK_TREE_MODEL (listStore); valid = gtk_tree_model_get_iter_first (model, &iter); for (i = 0; i < G_N_ELEMENTS (common_location); i++) { if (!valid) { g_warning (G_STRLOC ": Unexpected end of sip items in combo box"); return; } gtk_list_store_set (GTK_LIST_STORE (model), &iter, DYNTABLE_COMBO_COLUMN_SENSITIVE, is_field_supported (editor, E_CONTACT_SIP), -1); valid = gtk_tree_model_iter_next (model, &iter); } } static void sensitize_sip (EContactEditor *editor) { GtkWidget *w; gboolean enabled = TRUE; w = e_builder_get_widget (editor->priv->builder, "sip-dyntable"); if (!editor->priv->target_editable || !is_field_supported (editor, E_CONTACT_SIP)) enabled = FALSE; gtk_widget_set_sensitive (w, enabled); sensitize_sip_types (editor); } static void row_added_im (EContactEditorDynTable *dyntable, EContactEditor *editor) { expand_im (editor, TRUE); } static void init_im_record_type (EContactEditor *editor) { GtkWidget *w; GtkListStore *store; gint i; EContactEditorDynTable *dyntable; w = e_builder_get_widget (editor->priv->builder, "im-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); store = e_contact_editor_dyntable_get_combo_store (dyntable); for (i = 0; i < G_N_ELEMENTS (im_service); i++) { GtkTreeIter iter; gtk_list_store_append (store, &iter); gtk_list_store_set (store, &iter, DYNTABLE_COMBO_COLUMN_TEXT, _(im_service[i].pretty_name), DYNTABLE_COMBO_COLUMN_SENSITIVE, TRUE, -1); } e_contact_editor_dyntable_set_combo_defaults (dyntable, im_service_default, G_N_ELEMENTS (im_service_default)); } static void init_im (EContactEditor *editor) { GtkWidget *w; EContactEditorDynTable *dyntable; w = e_builder_get_widget (editor->priv->builder, "im-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); e_contact_editor_dyntable_set_max_entries (dyntable, IM_SLOTS); e_contact_editor_dyntable_set_num_columns (dyntable, SLOTS_PER_LINE, TRUE); e_contact_editor_dyntable_set_show_min (dyntable, SLOTS_IN_COLLAPSED_STATE); g_signal_connect ( w, "changed", G_CALLBACK (object_changed), editor); g_signal_connect_swapped ( w, "activate", G_CALLBACK (entry_activated), editor); g_signal_connect ( w, "row-added", G_CALLBACK (row_added_im), editor); init_im_record_type (editor); expand_im (editor, TRUE); } static gint get_service_type (EVCardAttribute *attr) { gint ii; const gchar *name; EContactField field; for (ii = 0; ii < G_N_ELEMENTS (im_service); ii++) { name = e_vcard_attribute_get_name (attr); field = e_contact_field_id_from_vcard (name); if (field == im_service[ii].field) return ii; } return -1; } static void fill_in_im (EContactEditor *editor) { GList *im_attr_list; GList *l; GtkWidget *w; EContactEditorDynTable *dyntable; GtkListStore *data_store; GtkTreeIter iter; w = e_builder_get_widget (editor->priv->builder, "im-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); /* Clear */ e_contact_editor_dyntable_clear_data (dyntable); /* Fill in */ data_store = e_contact_editor_dyntable_extract_data (dyntable); im_attr_list = e_contact_get_attributes_set ( editor->priv->contact, im_service_fetch_set, G_N_ELEMENTS (im_service_fetch_set) ); for (l = im_attr_list; l; l = g_list_next(l)) { EVCardAttribute *attr = l->data; gchar *im_name; gint service_type; gint slot; im_name = e_vcard_attribute_get_value (attr); service_type = get_service_type (attr); slot = get_ui_slot (attr); if (slot < 0) slot = IM_SLOTS + 1; //attach at the end gtk_list_store_append (data_store, &iter); gtk_list_store_set (data_store, &iter, DYNTABLE_STORE_COLUMN_SORTORDER, slot, DYNTABLE_STORE_COLUMN_SELECTED_ITEM, service_type, DYNTABLE_STORE_COLUMN_ENTRY_STRING, im_name, -1); g_free (im_name); } g_list_free_full (im_attr_list, (GDestroyNotify) e_vcard_attribute_free); e_contact_editor_dyntable_fill_in_data (dyntable); } static void extract_im (EContactEditor *editor) { GList *attr_list = NULL; GList *old_attr_list = NULL; GList *ll; gint ii; GtkWidget *w; EContactEditorDynTable *dyntable; GtkListStore *data_store; GtkTreeModel *tree_model; GtkTreeIter iter; gboolean valid; w = e_builder_get_widget (editor->priv->builder, "im-dyntable"); dyntable = E_CONTACT_EDITOR_DYNTABLE (w); data_store = e_contact_editor_dyntable_extract_data (dyntable); tree_model = GTK_TREE_MODEL (data_store); valid = gtk_tree_model_get_iter_first (tree_model, &iter); while (valid) { gint service_type; gint slot; gchar *im_name; EVCardAttribute *attr; gtk_tree_model_get (tree_model,&iter, DYNTABLE_STORE_COLUMN_SORTORDER, &slot, DYNTABLE_STORE_COLUMN_SELECTED_ITEM, &service_type, DYNTABLE_STORE_COLUMN_ENTRY_STRING, &im_name, -1); attr = e_vcard_attribute_new ("", e_contact_vcard_attribute ( im_service[service_type].field)); /* older evolution versions (<=3.12) will crash if SLOT>4 is stored, * but if we don't store the slot we loose sortorder. * this works only for <=4 IM slots. for more, old evolution * will go through types (AIM, Jabber, ...) and stop after 4 * no matter what x-evo-slot says. */ if (slot < 4) set_ui_slot (attr, slot + 1); e_vcard_attribute_add_value (attr, im_name); attr_list = g_list_prepend (attr_list, attr); valid = gtk_tree_model_iter_next (tree_model, &iter); } attr_list = g_list_reverse (attr_list); /* Splice in the old attributes, minus the IM_SLOTS first */ old_attr_list = e_contact_get_attributes_set ( editor->priv->contact, im_service_fetch_set, G_N_ELEMENTS (im_service_fetch_set) ); for (ll = old_attr_list, ii = 0; ll && ii < IM_SLOTS; ii++) { e_vcard_attribute_free (ll->data); ll = g_list_delete_link (ll, ll); } old_attr_list = ll; attr_list = g_list_concat (attr_list, old_attr_list); for (ii = 0; ii < G_N_ELEMENTS (im_service_fetch_set); ii++) { e_contact_set_attributes (editor->priv->contact, im_service_fetch_set[ii], NULL); } for (ll = attr_list; ll; ll = ll->next) { EVCard *vcard; vcard = E_VCARD (editor->priv->contact); e_vcard_append_attribute (vcard, e_vcard_attribute_copy ((EVCardAttribute *) ll->data)); } g_list_free_full (attr_list, (GDestroyNotify) e_vcard_attribute_free); } static void sensitize_im_types (EContactEditor *editor) { GtkWidget *w; GtkListStore *list_store; GtkTreeModel *model; GtkTreeIter iter; gint i; gboolean valid; w = e_builder_get_widget (editor->priv->builder, "im-dyntable"); list_store = e_contact_editor_dyntable_get_combo_store (E_CONTACT_EDITOR_DYNTABLE (w)); model = GTK_TREE_MODEL (list_store); valid = gtk_tree_model_get_iter_first (model, &iter); for (i = 0; i < G_N_ELEMENTS (im_service); i++) { if (!valid) { g_warning (G_STRLOC ": Unexpected end of im items in combo box"); return; } gtk_list_store_set ( GTK_LIST_STORE (model), &iter, DYNTABLE_COMBO_COLUMN_SENSITIVE, is_field_supported (editor, im_service[i].field), -1); valid = gtk_tree_model_iter_next (model, &iter); } } static void sensitize_im (EContactEditor *editor) { gint i; gboolean enabled; gboolean no_ims_supported; GtkWidget *w; enabled = editor->priv->target_editable; no_ims_supported = TRUE; for (i = 0; i < G_N_ELEMENTS (im_service); i++) if (is_field_supported (editor, im_service[i].field)) { no_ims_supported = FALSE; break; } if (no_ims_supported) enabled = FALSE; w = e_builder_get_widget (editor->priv->builder, "im-dyntable"); gtk_widget_set_sensitive (w, enabled); sensitize_im_types (editor); } static void init_personal (EContactEditor *editor) { gtk_expander_set_expanded ( GTK_EXPANDER (e_builder_get_widget ( editor->priv->builder, "expander-personal-misc")), !editor->priv->compress_ui); expand_web (editor, !editor->priv->compress_ui); } static void init_address_textview (EContactEditor *editor, gint record) { gchar *textview_name; GtkWidget *textview; GtkTextBuffer *text_buffer; textview_name = g_strdup_printf ( "textview-%s-address", address_name[record]); textview = e_builder_get_widget (editor->priv->builder, textview_name); g_free (textview_name); text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); g_signal_connect ( text_buffer, "changed", G_CALLBACK (object_changed), editor); } static void init_address_field (EContactEditor *editor, gint record, const gchar *widget_field_name) { gchar *entry_name; GtkWidget *entry; entry_name = g_strdup_printf ( "entry-%s-%s", address_name[record], widget_field_name); entry = e_builder_get_widget (editor->priv->builder, entry_name); g_free (entry_name); g_signal_connect ( entry, "changed", G_CALLBACK (object_changed), editor); g_signal_connect_swapped ( entry, "activate", G_CALLBACK (entry_activated), editor); } static void init_address_record (EContactEditor *editor, gint record) { init_address_textview (editor, record); init_address_field (editor, record, "city"); init_address_field (editor, record, "state"); init_address_field (editor, record, "zip"); init_address_field (editor, record, "country"); init_address_field (editor, record, "pobox"); } static void init_address (EContactEditor *editor) { gint i; for (i = 0; i < ADDRESS_SLOTS; i++) init_address_record (editor, i); gtk_expander_set_expanded ( GTK_EXPANDER (e_builder_get_widget ( editor->priv->builder, "expander-address-other")), !editor->priv->compress_ui); } static void fill_in_address_textview (EContactEditor *editor, gint record, EContactAddress *address) { gchar *textview_name; GtkWidget *textview; GtkTextBuffer *text_buffer; GtkTextIter iter_end, iter_start; textview_name = g_strdup_printf ("textview-%s-address", address_name[record]); textview = e_builder_get_widget (editor->priv->builder, textview_name); g_free (textview_name); text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); gtk_text_buffer_set_text (text_buffer, address->street ? address->street : "", -1); gtk_text_buffer_get_end_iter (text_buffer, &iter_end); if (address->ext && *address->ext) { gtk_text_buffer_insert (text_buffer, &iter_end, "\n", -1); gtk_text_buffer_insert (text_buffer, &iter_end, address->ext, -1); } else { gtk_text_buffer_insert (text_buffer, &iter_end, "", -1); } gtk_text_buffer_get_iter_at_line (text_buffer, &iter_start, 0); gtk_text_buffer_place_cursor (text_buffer, &iter_start); } static void fill_in_address_label_textview (EContactEditor *editor, gint record, const gchar *label) { gchar *textview_name; GtkWidget *textview; GtkTextBuffer *text_buffer; textview_name = g_strdup_printf ( "textview-%s-address", address_name[record]); textview = e_builder_get_widget (editor->priv->builder, textview_name); g_free (textview_name); text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); gtk_text_buffer_set_text (text_buffer, label ? label : "", -1); } static void fill_in_address_field (EContactEditor *editor, gint record, const gchar *widget_field_name, const gchar *string) { gchar *entry_name; GtkWidget *entry; entry_name = g_strdup_printf ( "entry-%s-%s", address_name[record], widget_field_name); entry = e_builder_get_widget (editor->priv->builder, entry_name); g_free (entry_name); set_entry_text (editor, GTK_ENTRY (entry), string); } static void fill_in_address_record (EContactEditor *editor, gint record) { EContactAddress *address; gchar *address_label; address = e_contact_get (editor->priv->contact, addresses[record]); address_label = e_contact_get (editor->priv->contact, address_labels[record]); if (address && (!STRING_IS_EMPTY (address->street) || !STRING_IS_EMPTY (address->ext) || !STRING_IS_EMPTY (address->locality) || !STRING_IS_EMPTY (address->region) || !STRING_IS_EMPTY (address->code) || !STRING_IS_EMPTY (address->po) || !STRING_IS_EMPTY (address->country))) { fill_in_address_textview (editor, record, address); fill_in_address_field (editor, record, "city", address->locality); fill_in_address_field (editor, record, "state", address->region); fill_in_address_field (editor, record, "zip", address->code); fill_in_address_field (editor, record, "country", address->country); fill_in_address_field (editor, record, "pobox", address->po); } else if (!STRING_IS_EMPTY (address_label)) { fill_in_address_label_textview (editor, record, address_label); } g_free (address_label); if (address) g_boxed_free (e_contact_address_get_type (), address); } static void fill_in_address (EContactEditor *editor) { gint i; for (i = 0; i < ADDRESS_SLOTS; i++) fill_in_address_record (editor, i); } static void extract_address_textview (EContactEditor *editor, gint record, EContactAddress *address) { gchar *textview_name; GtkWidget *textview; GtkTextBuffer *text_buffer; GtkTextIter iter_1, iter_2; textview_name = g_strdup_printf ("textview-%s-address", address_name[record]); textview = e_builder_get_widget (editor->priv->builder, textview_name); g_free (textview_name); text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (textview)); gtk_text_buffer_get_start_iter (text_buffer, &iter_1); /* Skip blank lines */ while (gtk_text_iter_get_chars_in_line (&iter_1) < 1 && !gtk_text_iter_is_end (&iter_1)) gtk_text_iter_forward_line (&iter_1); if (gtk_text_iter_is_end (&iter_1)) return; iter_2 = iter_1; gtk_text_iter_forward_to_line_end (&iter_2); /* Extract street (first line of text) */ address->street = gtk_text_iter_get_text (&iter_1, &iter_2); iter_1 = iter_2; gtk_text_iter_forward_line (&iter_1); if (gtk_text_iter_is_end (&iter_1)) return; gtk_text_iter_forward_to_end (&iter_2); /* Extract extended address (remaining lines of text) */ address->ext = gtk_text_iter_get_text (&iter_1, &iter_2); } static gchar * extract_address_field (EContactEditor *editor, gint record, const gchar *widget_field_name) { gchar *entry_name; GtkWidget *entry; entry_name = g_strdup_printf ( "entry-%s-%s", address_name[record], widget_field_name); entry = e_builder_get_widget (editor->priv->builder, entry_name); g_free (entry_name); return g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); } static gchar * append_to_address_label (gchar *address_label, const gchar *part, gboolean newline) { gchar *new_address_label; if (STRING_IS_EMPTY (part)) return address_label; if (address_label) new_address_label = g_strjoin ( newline ? "\n" : ", ", address_label, part, NULL); else new_address_label = g_strdup (part); g_free (address_label); return new_address_label; } static void set_address_label (EContact *contact, EContactField field, EContactAddress *address) { gchar *address_label = NULL; gboolean format_address; GSettings *settings; if (!address) { e_contact_set (contact, field, NULL); return; } settings = g_settings_new ("org.gnome.evolution.addressbook"); format_address = g_settings_get_boolean (settings, "address-formatting"); g_object_unref (settings); if (format_address) { address_label = eab_format_address ( contact, (field == E_CONTACT_ADDRESS_LABEL_WORK) ? E_CONTACT_ADDRESS_WORK : E_CONTACT_ADDRESS_HOME); } if (!format_address || !address_label) { address_label = append_to_address_label ( address_label, address->street, TRUE); address_label = append_to_address_label ( address_label, address->ext, TRUE); address_label = append_to_address_label ( address_label, address->locality, TRUE); address_label = append_to_address_label ( address_label, address->region, FALSE); address_label = append_to_address_label ( address_label, address->code, TRUE); address_label = append_to_address_label ( address_label, address->po, TRUE); address_label = append_to_address_label ( address_label, address->country, TRUE); } e_contact_set (contact, field, address_label); g_free (address_label); } static void extract_address_record (EContactEditor *editor, gint record) { EContactAddress *address; address = g_new0 (EContactAddress, 1); extract_address_textview (editor, record, address); address->locality = extract_address_field (editor, record, "city"); address->region = extract_address_field (editor, record, "state"); address->code = extract_address_field (editor, record, "zip"); address->country = extract_address_field (editor, record, "country"); address->po = extract_address_field (editor, record, "pobox"); if (!STRING_IS_EMPTY (address->street) || !STRING_IS_EMPTY (address->ext) || !STRING_IS_EMPTY (address->locality) || !STRING_IS_EMPTY (address->region) || !STRING_IS_EMPTY (address->code) || !STRING_IS_EMPTY (address->po) || !STRING_IS_EMPTY (address->country)) { e_contact_set (editor->priv->contact, addresses[record], address); set_address_label (editor->priv->contact, address_labels[record], address); } else { e_contact_set (editor->priv->contact, addresses[record], NULL); set_address_label (editor->priv->contact, address_labels[record], NULL); } g_boxed_free (e_contact_address_get_type (), address); } static void extract_address (EContactEditor *editor) { gint i; for (i = 0; i < ADDRESS_SLOTS; i++) extract_address_record (editor, i); } static void sensitize_address_textview (EContactEditor *editor, gint record, gboolean enabled) { gchar *widget_name; GtkWidget *textview; GtkWidget *label; widget_name = g_strdup_printf ("textview-%s-address", address_name[record]); textview = e_builder_get_widget (editor->priv->builder, widget_name); g_free (widget_name); widget_name = g_strdup_printf ("label-%s-address", address_name[record]); label = e_builder_get_widget (editor->priv->builder, widget_name); g_free (widget_name); gtk_text_view_set_editable (GTK_TEXT_VIEW (textview), enabled); gtk_widget_set_sensitive (label, enabled); } static void sensitize_address_field (EContactEditor *editor, gint record, const gchar *widget_field_name, gboolean enabled) { gchar *widget_name; GtkWidget *entry; GtkWidget *label; widget_name = g_strdup_printf ( "entry-%s-%s", address_name[record], widget_field_name); entry = e_builder_get_widget (editor->priv->builder, widget_name); g_free (widget_name); widget_name = g_strdup_printf ( "label-%s-%s", address_name[record], widget_field_name); label = e_builder_get_widget (editor->priv->builder, widget_name); g_free (widget_name); gtk_editable_set_editable (GTK_EDITABLE (entry), enabled); gtk_widget_set_sensitive (label, enabled); } static void sensitize_address_record (EContactEditor *editor, gint record, gboolean enabled) { sensitize_address_textview (editor, record, enabled); sensitize_address_field (editor, record, "city", enabled); sensitize_address_field (editor, record, "state", enabled); sensitize_address_field (editor, record, "zip", enabled); sensitize_address_field (editor, record, "country", enabled); sensitize_address_field (editor, record, "pobox", enabled); } static void sensitize_address (EContactEditor *editor) { gint i; for (i = 0; i < ADDRESS_SLOTS; i++) { gboolean enabled = TRUE; if (!editor->priv->target_editable || !(is_field_supported (editor, addresses[i]) || is_field_supported (editor, address_labels[i]))) enabled = FALSE; sensitize_address_record (editor, i, enabled); } } typedef struct { const gchar *widget_name; gint field_id; /* EContactField or -1 */ gboolean process_data; /* If we should extract/fill in contents */ gboolean desensitize_for_read_only; } FieldMapping; /* Table of widgets that interact with simple fields. This table is used to: * * - Fill in data. * - Extract data. * - Set sensitivity based on backend capabilities. * - Set sensitivity based on book writeability. */ static FieldMapping simple_field_map[] = { { "entry-homepage", E_CONTACT_HOMEPAGE_URL, TRUE, TRUE }, { "accellabel-homepage", E_CONTACT_HOMEPAGE_URL, FALSE, TRUE }, { "entry-jobtitle", E_CONTACT_TITLE, TRUE, TRUE }, { "label-jobtitle", E_CONTACT_TITLE, FALSE, TRUE }, { "entry-company", E_CONTACT_ORG, TRUE, TRUE }, { "label-company", E_CONTACT_ORG, FALSE, TRUE }, { "entry-department", E_CONTACT_ORG_UNIT, TRUE, TRUE }, { "label-department", E_CONTACT_ORG_UNIT, FALSE, TRUE }, { "entry-profession", E_CONTACT_ROLE, TRUE, TRUE }, { "label-profession", E_CONTACT_ROLE, FALSE, TRUE }, { "entry-manager", E_CONTACT_MANAGER, TRUE, TRUE }, { "label-manager", E_CONTACT_MANAGER, FALSE, TRUE }, { "entry-assistant", E_CONTACT_ASSISTANT, TRUE, TRUE }, { "label-assistant", E_CONTACT_ASSISTANT, FALSE, TRUE }, { "entry-nickname", E_CONTACT_NICKNAME, TRUE, TRUE }, { "label-nickname", E_CONTACT_NICKNAME, FALSE, TRUE }, { "dateedit-birthday", E_CONTACT_BIRTH_DATE, TRUE, TRUE }, { "label-birthday", E_CONTACT_BIRTH_DATE, FALSE, TRUE }, { "dateedit-anniversary", E_CONTACT_ANNIVERSARY, TRUE, TRUE }, { "label-anniversary", E_CONTACT_ANNIVERSARY, FALSE, TRUE }, { "entry-spouse", E_CONTACT_SPOUSE, TRUE, TRUE }, { "label-spouse", E_CONTACT_SPOUSE, FALSE, TRUE }, { "entry-office", E_CONTACT_OFFICE, TRUE, TRUE }, { "label-office", E_CONTACT_OFFICE, FALSE, TRUE }, { "text-comments", E_CONTACT_NOTE, TRUE, TRUE }, { "entry-fullname", E_CONTACT_FULL_NAME, TRUE, TRUE }, { "button-fullname", E_CONTACT_FULL_NAME, FALSE, TRUE }, { "entry-categories", E_CONTACT_CATEGORIES, TRUE, TRUE }, { "button-categories", E_CONTACT_CATEGORIES, FALSE, TRUE }, { "entry-weblog", E_CONTACT_BLOG_URL, TRUE, TRUE }, { "label-weblog", E_CONTACT_BLOG_URL, FALSE, TRUE }, { "entry-caluri", E_CONTACT_CALENDAR_URI, TRUE, TRUE }, { "label-caluri", E_CONTACT_CALENDAR_URI, FALSE, TRUE }, { "entry-fburl", E_CONTACT_FREEBUSY_URL, TRUE, TRUE }, { "label-fburl", E_CONTACT_FREEBUSY_URL, FALSE, TRUE }, { "entry-videourl", E_CONTACT_VIDEO_URL, TRUE, TRUE }, { "label-videourl", E_CONTACT_VIDEO_URL, FALSE, TRUE }, { "checkbutton-htmlmail", E_CONTACT_WANTS_HTML, TRUE, TRUE }, { "image-chooser", E_CONTACT_PHOTO, TRUE, TRUE }, { "button-image", E_CONTACT_PHOTO, FALSE, TRUE }, { "combo-file-as", E_CONTACT_FILE_AS, TRUE, TRUE }, { "accellabel-fileas", E_CONTACT_FILE_AS, FALSE, TRUE }, }; static void init_simple_field (EContactEditor *editor, GtkWidget *widget) { GObject *changed_object = NULL; if (GTK_IS_ENTRY (widget)) { changed_object = G_OBJECT (widget); g_signal_connect_swapped ( widget, "activate", G_CALLBACK (entry_activated), editor); } else if (GTK_IS_COMBO_BOX (widget)) { changed_object = G_OBJECT (widget); g_signal_connect_swapped ( gtk_bin_get_child (GTK_BIN (widget)), "activate", G_CALLBACK (entry_activated), editor); } else if (GTK_IS_TEXT_VIEW (widget)) { changed_object = G_OBJECT ( gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget))); } else if (E_IS_URL_ENTRY (widget)) { changed_object = G_OBJECT (widget); g_signal_connect_swapped ( changed_object, "activate", G_CALLBACK (entry_activated), editor); } else if (E_IS_DATE_EDIT (widget)) { changed_object = G_OBJECT (widget); } else if (E_IS_IMAGE_CHOOSER (widget)) { changed_object = G_OBJECT (widget); g_signal_connect ( widget, "changed", G_CALLBACK (image_chooser_changed), editor); } else if (GTK_IS_TOGGLE_BUTTON (widget)) { g_signal_connect ( widget, "toggled", G_CALLBACK (object_changed), editor); } if (changed_object) g_signal_connect ( changed_object, "changed", G_CALLBACK (object_changed), editor); } static void fill_in_simple_field (EContactEditor *editor, GtkWidget *widget, gint field_id) { EContact *contact; contact = editor->priv->contact; g_signal_handlers_block_matched ( widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor); if (GTK_IS_ENTRY (widget)) { gchar *text = e_contact_get (contact, field_id); gtk_entry_set_text (GTK_ENTRY (widget), STRING_MAKE_NON_NULL (text)); g_free (text); } else if (GTK_IS_COMBO_BOX (widget)) { gchar *text = e_contact_get (contact, field_id); gtk_entry_set_text ( GTK_ENTRY (gtk_bin_get_child (GTK_BIN (widget))), STRING_MAKE_NON_NULL (text)); g_free (text); } else if (GTK_IS_TEXT_VIEW (widget)) { GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)); gchar *text = e_contact_get (contact, field_id); gtk_text_buffer_set_text (buffer, STRING_MAKE_NON_NULL (text), -1); g_free (text); } else if (E_IS_URL_ENTRY (widget)) { gchar *text = e_contact_get (contact, field_id); gtk_entry_set_text ( GTK_ENTRY (widget), STRING_MAKE_NON_NULL (text)); g_free (text); } else if (E_IS_DATE_EDIT (widget)) { EContactDate *date = e_contact_get (contact, field_id); if (date) e_date_edit_set_date ( E_DATE_EDIT (widget), date->year, date->month, date->day); else e_date_edit_set_time (E_DATE_EDIT (widget), -1); e_contact_date_free (date); } else if (E_IS_IMAGE_CHOOSER (widget)) { EContactPhoto *photo = e_contact_get (contact, field_id); editor->priv->image_set = FALSE; if (photo && photo->type == E_CONTACT_PHOTO_TYPE_INLINED) { e_image_chooser_set_image_data ( E_IMAGE_CHOOSER (widget), (gchar *) photo->data.inlined.data, photo->data.inlined.length); editor->priv->image_set = TRUE; } else if (photo && photo->type == E_CONTACT_PHOTO_TYPE_URI) { gchar *file_name = g_filename_from_uri (photo->data.uri, NULL, NULL); if (file_name) { e_image_chooser_set_from_file ( E_IMAGE_CHOOSER (widget), file_name); editor->priv->image_set = TRUE; g_free (file_name); } } if (!editor->priv->image_set) { gchar *file_name; file_name = e_icon_factory_get_icon_filename ( "avatar-default", GTK_ICON_SIZE_DIALOG); e_image_chooser_set_from_file ( E_IMAGE_CHOOSER (widget), file_name); editor->priv->image_set = FALSE; g_free (file_name); } editor->priv->image_changed = FALSE; e_contact_photo_free (photo); } else if (GTK_IS_TOGGLE_BUTTON (widget)) { gboolean val = e_contact_get (contact, field_id) != NULL; gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (widget), val); } else { g_warning (G_STRLOC ": Unhandled widget class in mappings!"); } g_signal_handlers_unblock_matched ( widget, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL, editor); } static void extract_simple_field (EContactEditor *editor, GtkWidget *widget, gint field_id) { EContact *contact; contact = editor->priv->contact; if (GTK_IS_ENTRY (widget)) { const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget)); e_contact_set (contact, field_id, (gchar *) text); } else if (GTK_IS_COMBO_BOX_TEXT (widget)) { gchar *text = NULL; if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (widget))) { GtkWidget *entry = gtk_bin_get_child (GTK_BIN (widget)); text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); } if (!text) text = gtk_combo_box_text_get_active_text (GTK_COMBO_BOX_TEXT (widget)); e_contact_set (contact, field_id, text); g_free (text); } else if (GTK_IS_COMBO_BOX (widget)) { GtkTreeIter iter; gchar *text = NULL; if (gtk_combo_box_get_has_entry (GTK_COMBO_BOX (widget))) { GtkWidget *entry = gtk_bin_get_child (GTK_BIN (widget)); text = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); } if (!text && gtk_combo_box_get_active_iter (GTK_COMBO_BOX (widget), &iter)) { GtkListStore *store; store = GTK_LIST_STORE ( gtk_combo_box_get_model ( GTK_COMBO_BOX (widget))); gtk_tree_model_get ( GTK_TREE_MODEL (store), &iter, 0, &text, -1); } e_contact_set (contact, field_id, text); g_free (text); } else if (GTK_IS_TEXT_VIEW (widget)) { GtkTextBuffer *buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (widget)); GtkTextIter start, end; gchar *text; gtk_text_buffer_get_start_iter (buffer, &start); gtk_text_buffer_get_end_iter (buffer, &end); text = gtk_text_buffer_get_text (buffer, &start, &end, FALSE); e_contact_set (contact, field_id, text); g_free (text); } else if (E_IS_URL_ENTRY (widget)) { const gchar *text = gtk_entry_get_text (GTK_ENTRY (widget)); e_contact_set (contact, field_id, (gchar *) text); } else if (E_IS_DATE_EDIT (widget)) { EContactDate date; if (e_date_edit_get_date ( E_DATE_EDIT (widget), (gint *) &date.year, (gint *) &date.month, (gint *) &date.day)) e_contact_set (contact, field_id, &date); else e_contact_set (contact, field_id, NULL); } else if (E_IS_IMAGE_CHOOSER (widget)) { EContactPhoto photo; photo.type = E_CONTACT_PHOTO_TYPE_INLINED; photo.data.inlined.mime_type = NULL; if (editor->priv->image_changed) { gchar *img_buff = NULL; if (editor->priv->image_set && e_image_chooser_get_image_data ( E_IMAGE_CHOOSER (widget), &img_buff, &photo.data.inlined.length)) { GdkPixbuf *pixbuf, *new; GdkPixbufLoader *loader = gdk_pixbuf_loader_new (); photo.data.inlined.data = (guchar *) img_buff; img_buff = NULL; gdk_pixbuf_loader_write ( loader, photo.data.inlined.data, photo.data.inlined.length, NULL); gdk_pixbuf_loader_close (loader, NULL); pixbuf = gdk_pixbuf_loader_get_pixbuf (loader); if (pixbuf) { gint width, height, prompt_response; g_object_ref (pixbuf); height = gdk_pixbuf_get_height (pixbuf); width = gdk_pixbuf_get_width (pixbuf); if ((height > 96 || width > 96)) { prompt_response = e_alert_run_dialog_for_args (GTK_WINDOW (editor->priv->app), "addressbook:prompt-resize", NULL); if (prompt_response == GTK_RESPONSE_YES) { if (width > height) { height = height * 96 / width; width = 96; } else { width = width *96 / height; height = 96; } new = e_icon_factory_pixbuf_scale (pixbuf, width, height); if (new) { GdkPixbufFormat *format = gdk_pixbuf_loader_get_format (loader); gchar *format_name = gdk_pixbuf_format_get_name (format); g_free (photo.data.inlined.data); gdk_pixbuf_save_to_buffer ( new, &img_buff, &photo.data.inlined.length, format_name, NULL, NULL); photo.data.inlined.data = (guchar *) img_buff; img_buff = NULL; g_free (format_name); g_object_unref (new); } } else if (prompt_response == GTK_RESPONSE_CANCEL) { g_object_unref (pixbuf); g_object_unref (loader); return; } } g_object_unref (pixbuf); } editor->priv->image_changed = FALSE; g_object_unref (loader); e_contact_set (contact, field_id, &photo); g_free (photo.data.inlined.data); } else { editor->priv->image_changed = FALSE; e_contact_set (contact, E_CONTACT_PHOTO, NULL); } } } else if (GTK_IS_TOGGLE_BUTTON (widget)) { gboolean val; val = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); e_contact_set (contact, field_id, val ? (gpointer) 1 : NULL); } else { g_warning (G_STRLOC ": Unhandled widget class in mappings!"); } } static void sensitize_simple_field (GtkWidget *widget, gboolean enabled) { if (GTK_IS_ENTRY (widget)) gtk_editable_set_editable (GTK_EDITABLE (widget), enabled); else if (GTK_IS_TEXT_VIEW (widget)) gtk_text_view_set_editable (GTK_TEXT_VIEW (widget), enabled); else if (E_IS_DATE_EDIT (widget)) e_date_edit_set_editable (E_DATE_EDIT (widget), enabled); else gtk_widget_set_sensitive (widget, enabled); } static void init_simple (EContactEditor *editor) { GtkWidget *widget; gint i; for (i = 0; i < G_N_ELEMENTS (simple_field_map); i++) { widget = e_builder_get_widget ( editor->priv->builder, simple_field_map[i].widget_name); if (!widget) continue; init_simple_field (editor, widget); } /* --- Special cases --- */ /* Update file_as */ widget = e_builder_get_widget (editor->priv->builder, "entry-fullname"); g_signal_connect ( widget, "changed", G_CALLBACK (name_entry_changed), editor); widget = e_builder_get_widget (editor->priv->builder, "combo-file-as"); gtk_combo_box_set_entry_text_column (GTK_COMBO_BOX (widget), 0); g_signal_connect ( widget, "changed", G_CALLBACK (file_as_combo_changed), editor); widget = e_builder_get_widget (editor->priv->builder, "entry-company"); g_signal_connect ( widget, "changed", G_CALLBACK (company_entry_changed), editor); } static void fill_in_simple (EContactEditor *editor) { EContactName *name; gchar *filename; gint i; for (i = 0; i < G_N_ELEMENTS (simple_field_map); i++) { GtkWidget *widget; if (simple_field_map[i].field_id < 0 || !simple_field_map[i].process_data) continue; widget = e_builder_get_widget ( editor->priv->builder, simple_field_map[i].widget_name); if (!widget) continue; fill_in_simple_field ( editor, widget, simple_field_map[i].field_id); } /* --- Special cases --- */ /* Update broken-up name */ g_object_get (editor->priv->contact, "name", &name, NULL); if (editor->priv->name) e_contact_name_free (editor->priv->name); editor->priv->name = name; /* Update the contact editor title */ filename = (gchar *) e_contact_get (editor->priv->contact, E_CONTACT_FILE_AS); if (filename) { gchar *title; title = g_strdup_printf (_("Contact Editor - %s"), filename); gtk_window_set_title (GTK_WINDOW (editor->priv->app), title); g_free (title); g_free (filename); } else gtk_window_set_title ( GTK_WINDOW (editor->priv->app), _("Contact Editor")); /* Update file_as combo options */ update_file_as_combo (editor); } static void extract_simple (EContactEditor *editor) { gint i; for (i = 0; i < G_N_ELEMENTS (simple_field_map); i++) { GtkWidget *widget; if (simple_field_map[i].field_id < 0 || !simple_field_map[i].process_data) continue; widget = e_builder_get_widget ( editor->priv->builder, simple_field_map[i].widget_name); if (!widget) continue; extract_simple_field ( editor, widget, simple_field_map[i].field_id); } /* Special cases */ e_contact_set (editor->priv->contact, E_CONTACT_NAME, editor->priv->name); } static void sensitize_simple (EContactEditor *editor) { gint i; for (i = 0; i < G_N_ELEMENTS (simple_field_map); i++) { GtkWidget *widget; gboolean enabled = TRUE; widget = e_builder_get_widget ( editor->priv->builder, simple_field_map[i].widget_name); if (!widget) continue; if (simple_field_map[i].field_id >= 0 && !is_field_supported (editor, simple_field_map[i].field_id)) enabled = FALSE; if (simple_field_map[i].desensitize_for_read_only && !editor->priv->target_editable) enabled = FALSE; sensitize_simple_field (widget, enabled); } } static void fill_in_all (EContactEditor *editor) { GtkWidget *focused_widget; gpointer weak_pointer; /* Widget changes can cause focus widget change, thus remember the current widget and restore it after the fill is done; some fill operations can delete widgets, like the dyntable, thus do the weak_pointer as well. */ focused_widget = gtk_window_get_focus (eab_editor_get_window (EAB_EDITOR (editor))); weak_pointer = focused_widget; if (focused_widget) g_object_add_weak_pointer (G_OBJECT (focused_widget), &weak_pointer); fill_in_source_field (editor); fill_in_simple (editor); fill_in_email (editor); fill_in_phone (editor); fill_in_sip (editor); fill_in_im (editor); fill_in_address (editor); if (weak_pointer) { g_object_remove_weak_pointer (G_OBJECT (focused_widget), &weak_pointer); gtk_widget_grab_focus (focused_widget); } } static void extract_all (EContactEditor *editor) { extract_simple (editor); extract_email (editor); extract_phone (editor); extract_sip (editor); extract_im (editor); extract_address (editor); } static void sensitize_all (EContactEditor *editor) { GtkWidget *focused_widget; gpointer weak_pointer; /* Widget changes can cause focus widget change, thus remember the current widget and restore it after the fill is done; some fill operations can delete widgets, like the dyntable, thus do the weak_pointer as well. */ focused_widget = gtk_window_get_focus (eab_editor_get_window (EAB_EDITOR (editor))); weak_pointer = focused_widget; if (focused_widget) g_object_add_weak_pointer (G_OBJECT (focused_widget), &weak_pointer); sensitize_ok (editor); sensitize_simple (editor); sensitize_email (editor); sensitize_phone (editor); sensitize_sip (editor); sensitize_im (editor); sensitize_address (editor); if (weak_pointer) { g_object_remove_weak_pointer (G_OBJECT (focused_widget), &weak_pointer); gtk_widget_grab_focus (focused_widget); } } static void init_all (EContactEditor *editor) { const gchar *contents[] = { "viewport1", "viewport2", "viewport3", "text-comments" }; gint ii; GtkRequisition tab_req, requisition; GtkWidget *widget; init_simple (editor); init_email (editor); init_phone (editor); init_sip (editor); init_im (editor); init_personal (editor); init_address (editor); /* with so many scrolled windows, we need to * do some manual sizing */ requisition.width = -1; requisition.height = -1; for (ii = 0; ii < G_N_ELEMENTS (contents); ii++) { widget = e_builder_get_widget (editor->priv->builder, contents[ii]); gtk_widget_get_preferred_size (widget, NULL, &tab_req); if (tab_req.width > requisition.width) requisition.width = tab_req.width; if (tab_req.height > requisition.height) requisition.height = tab_req.height; } if (requisition.width > 0 && requisition.height > 0) { GtkWidget *window; GdkScreen *screen; GdkRectangle monitor_area; gint x = 0, y = 0, monitor, width, height; window = e_builder_get_widget ( editor->priv->builder, "contact editor"); gtk_widget_get_preferred_size (window, &tab_req, NULL); width = tab_req.width - 320 + 24; height = tab_req.height - 240 + 24; screen = gtk_window_get_screen (GTK_WINDOW (window)); gtk_window_get_position (GTK_WINDOW (window), &x, &y); monitor = gdk_screen_get_monitor_at_point (screen, x, y); if (monitor < 0) monitor = 0; if (monitor >= gdk_screen_get_n_monitors (screen)) monitor = 0; gdk_screen_get_monitor_workarea (screen, monitor, &monitor_area); if (requisition.width > monitor_area.width - width) requisition.width = monitor_area.width - width; if (requisition.height > monitor_area.height - height) requisition.height = monitor_area.height - height; if (requisition.width > 0 && requisition.height > 0) gtk_window_set_default_size ( GTK_WINDOW (window), width + requisition.width, height + requisition.height); } widget = e_builder_get_widget (editor->priv->builder, "text-comments"); if (widget) e_spell_text_view_attach (GTK_TEXT_VIEW (widget)); } static void contact_editor_get_client_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { EClientComboBox *combo_box; ConnectClosure *closure = user_data; EClient *client; GError *error = NULL; combo_box = E_CLIENT_COMBO_BOX (source_object); client = e_client_combo_box_get_client_finish ( combo_box, result, &error); /* Sanity check. */ g_return_if_fail ( ((client != NULL) && (error == NULL)) || ((client == NULL) && (error != NULL))); if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warn_if_fail (client == NULL); g_error_free (error); goto exit; } else if (error != NULL) { GtkWindow *parent; parent = eab_editor_get_window (EAB_EDITOR (closure->editor)); eab_load_error_dialog ( GTK_WIDGET (parent), NULL, closure->source, error); e_source_combo_box_set_active ( E_SOURCE_COMBO_BOX (combo_box), e_client_get_source (E_CLIENT (closure->editor->priv->target_client))); g_error_free (error); goto exit; } /* FIXME Write a private contact_editor_set_target_client(). */ g_object_set (closure->editor, "target_client", client, NULL); g_object_unref (client); exit: connect_closure_free (closure); } static void source_changed (EClientComboBox *combo_box, EContactEditor *editor) { ConnectClosure *closure; ESource *target_source; ESource *source_source; ESource *source; source = e_source_combo_box_ref_active ( E_SOURCE_COMBO_BOX (combo_box)); g_return_if_fail (source != NULL); if (editor->priv->cancellable != NULL) { g_cancellable_cancel (editor->priv->cancellable); g_object_unref (editor->priv->cancellable); editor->priv->cancellable = NULL; } target_source = e_client_get_source (E_CLIENT (editor->priv->target_client)); source_source = e_client_get_source (E_CLIENT (editor->priv->source_client)); if (e_source_equal (target_source, source)) goto exit; if (e_source_equal (source_source, source)) { g_object_set ( editor, "target_client", editor->priv->source_client, NULL); goto exit; } editor->priv->cancellable = g_cancellable_new (); closure = g_slice_new0 (ConnectClosure); closure->editor = g_object_ref (editor); closure->source = g_object_ref (source); e_client_combo_box_get_client ( combo_box, source, editor->priv->cancellable, contact_editor_get_client_cb, closure); exit: g_object_unref (source); } static void full_name_response (GtkDialog *dialog, gint response, EContactEditor *editor) { EContactName *name; GtkWidget *fname_widget; gint style = 0; gboolean editable = FALSE; g_object_get (dialog, "editable", &editable, NULL); if (editable && response == GTK_RESPONSE_OK) { g_object_get (dialog, "name", &name, NULL); style = file_as_get_style (editor); fname_widget = e_builder_get_widget ( editor->priv->builder, "entry-fullname"); if (GTK_IS_ENTRY (fname_widget)) { GtkEntry *entry; gchar *full_name = e_contact_name_to_string (name); const gchar *old_full_name; entry = GTK_ENTRY (fname_widget); old_full_name = gtk_entry_get_text (entry); if (strcmp (full_name, old_full_name)) gtk_entry_set_text (entry, full_name); g_free (full_name); } e_contact_name_free (editor->priv->name); editor->priv->name = name; file_as_set_style (editor, style); } gtk_widget_destroy (GTK_WIDGET (dialog)); editor->priv->fullname_dialog = NULL; } static gint full_name_editor_delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data) { if (GTK_IS_WIDGET (widget)) gtk_widget_destroy (widget); return TRUE; } static void full_name_clicked (GtkWidget *button, EContactEditor *editor) { GtkDialog *dialog; gboolean fullname_supported; if (editor->priv->fullname_dialog) { gtk_window_present (GTK_WINDOW (editor->priv->fullname_dialog)); return; } dialog = GTK_DIALOG (e_contact_editor_fullname_new (editor->priv->name)); fullname_supported = is_field_supported (editor, E_CONTACT_FULL_NAME); g_object_set ( dialog, "editable", fullname_supported & editor->priv->target_editable, NULL); g_signal_connect ( dialog, "response", G_CALLBACK (full_name_response), editor); /* Close the fullname dialog if the editor is closed */ g_signal_connect_swapped ( editor, "editor_closed", G_CALLBACK (full_name_editor_delete_event_cb), dialog); gtk_widget_show (GTK_WIDGET (dialog)); editor->priv->fullname_dialog = GTK_WIDGET (dialog); } static void categories_response (GtkDialog *dialog, gint response, EContactEditor *editor) { gchar *categories; GtkWidget *entry; entry = e_builder_get_widget (editor->priv->builder, "entry-categories"); if (response == GTK_RESPONSE_OK) { categories = e_categories_dialog_get_categories ( E_CATEGORIES_DIALOG (dialog)); if (GTK_IS_ENTRY (entry)) gtk_entry_set_text ( GTK_ENTRY (entry), categories); else e_contact_set ( editor->priv->contact, E_CONTACT_CATEGORIES, categories); g_free (categories); } gtk_widget_destroy (GTK_WIDGET (dialog)); editor->priv->categories_dialog = NULL; } static void categories_clicked (GtkWidget *button, EContactEditor *editor) { gchar *categories = NULL; GtkDialog *dialog; GtkWindow *window; GtkWidget *entry = e_builder_get_widget (editor->priv->builder, "entry-categories"); if (entry && GTK_IS_ENTRY (entry)) categories = g_strdup (gtk_entry_get_text (GTK_ENTRY (entry))); else if (editor->priv->contact) categories = e_contact_get (editor->priv->contact, E_CONTACT_CATEGORIES); if (editor->priv->categories_dialog != NULL) { gtk_window_present (GTK_WINDOW (editor->priv->categories_dialog)); g_free (categories); return; }else if (!(dialog = GTK_DIALOG (e_categories_dialog_new (categories)))) { e_alert_run_dialog_for_args ( GTK_WINDOW (editor->priv->app), "addressbook:edit-categories", NULL); g_free (categories); return; } g_signal_connect ( dialog, "response", G_CALLBACK (categories_response), editor); window = GTK_WINDOW (dialog); /* Close the category dialog if the editor is closed */ gtk_window_set_destroy_with_parent (window, TRUE); gtk_window_set_modal (window, FALSE); gtk_window_set_transient_for (window, eab_editor_get_window (EAB_EDITOR (editor))); gtk_widget_show (GTK_WIDGET (dialog)); g_free (categories); editor->priv->categories_dialog = GTK_WIDGET (dialog); } static void image_selected (EContactEditor *editor) { gchar *file_name; GtkWidget *image_chooser; file_name = gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER (editor->priv->file_selector)); if (!file_name) return; image_chooser = e_builder_get_widget (editor->priv->builder, "image-chooser"); g_signal_handlers_block_by_func ( image_chooser, image_chooser_changed, editor); e_image_chooser_set_from_file ( E_IMAGE_CHOOSER (image_chooser), file_name); g_signal_handlers_unblock_by_func ( image_chooser, image_chooser_changed, editor); editor->priv->image_set = TRUE; editor->priv->image_changed = TRUE; object_changed (G_OBJECT (image_chooser), editor); } static void image_cleared (EContactEditor *editor) { GtkWidget *image_chooser; gchar *file_name; image_chooser = e_builder_get_widget ( editor->priv->builder, "image-chooser"); file_name = e_icon_factory_get_icon_filename ( "avatar-default", GTK_ICON_SIZE_DIALOG); g_signal_handlers_block_by_func ( image_chooser, image_chooser_changed, editor); e_image_chooser_set_from_file ( E_IMAGE_CHOOSER (image_chooser), file_name); g_signal_handlers_unblock_by_func ( image_chooser, image_chooser_changed, editor); g_free (file_name); editor->priv->image_set = FALSE; editor->priv->image_changed = TRUE; object_changed (G_OBJECT (image_chooser), editor); } static void file_chooser_response (GtkWidget *widget, gint response, EContactEditor *editor) { if (response == GTK_RESPONSE_ACCEPT) image_selected (editor); else if (response == GTK_RESPONSE_NO) image_cleared (editor); gtk_widget_hide (editor->priv->file_selector); } static gboolean file_selector_deleted (GtkWidget *widget) { gtk_widget_hide (widget); return TRUE; } static void update_preview_cb (GtkFileChooser *file_chooser, gpointer data) { GtkWidget *preview; gchar *filename = NULL; GdkPixbuf *pixbuf; gtk_file_chooser_set_preview_widget_active (file_chooser, TRUE); preview = GTK_WIDGET (data); filename = gtk_file_chooser_get_preview_filename (file_chooser); if (filename == NULL) return; pixbuf = gdk_pixbuf_new_from_file_at_size (filename, 128, 128, NULL); if (!pixbuf) { gchar *alternate_file; alternate_file = e_icon_factory_get_icon_filename ( "avatar-default", GTK_ICON_SIZE_DIALOG); if (alternate_file) { pixbuf = gdk_pixbuf_new_from_file_at_size ( alternate_file, 128, 128, NULL); g_free (alternate_file); } } g_free (filename); gtk_image_set_from_pixbuf (GTK_IMAGE (preview), pixbuf); if (pixbuf) g_object_unref (pixbuf); } static void image_clicked (GtkWidget *button, EContactEditor *editor) { if (!editor->priv->file_selector) { GtkImage *preview; GtkFileFilter *filter; editor->priv->file_selector = gtk_file_chooser_dialog_new ( _("Please select an image for this contact"), GTK_WINDOW (editor->priv->app), GTK_FILE_CHOOSER_ACTION_OPEN, _("_Cancel"), GTK_RESPONSE_CANCEL, _("_Open"), GTK_RESPONSE_ACCEPT, _("_No image"), GTK_RESPONSE_NO, NULL); filter = gtk_file_filter_new (); gtk_file_filter_add_mime_type (filter, "image/*"); gtk_file_chooser_set_filter ( GTK_FILE_CHOOSER (editor->priv->file_selector), filter); preview = GTK_IMAGE (gtk_image_new ()); gtk_file_chooser_set_preview_widget ( GTK_FILE_CHOOSER (editor->priv->file_selector), GTK_WIDGET (preview)); g_signal_connect ( editor->priv->file_selector, "update-preview", G_CALLBACK (update_preview_cb), preview); gtk_dialog_set_default_response ( GTK_DIALOG (editor->priv->file_selector), GTK_RESPONSE_ACCEPT); g_signal_connect ( editor->priv->file_selector, "response", G_CALLBACK (file_chooser_response), editor); g_signal_connect_after ( editor->priv->file_selector, "delete-event", G_CALLBACK (file_selector_deleted), editor->priv->file_selector); } /* Display the dialog */ gtk_window_set_modal (GTK_WINDOW (editor->priv->file_selector), TRUE); gtk_window_present (GTK_WINDOW (editor->priv->file_selector)); } typedef struct { EContactEditor *ce; gboolean should_close; gchar *new_id; } EditorCloseStruct; static void contact_removed_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { EBookClient *book_client = E_BOOK_CLIENT (source_object); EditorCloseStruct *ecs = user_data; EContactEditor *ce = ecs->ce; gboolean should_close = ecs->should_close; GError *error = NULL; e_book_client_remove_contact_finish (book_client, result, &error); gtk_widget_set_sensitive (ce->priv->app, TRUE); ce->priv->in_async_call = FALSE; e_contact_set (ce->priv->contact, E_CONTACT_UID, ecs->new_id); eab_editor_contact_deleted (EAB_EDITOR (ce), error, ce->priv->contact); ce->priv->is_new_contact = FALSE; if (should_close) { eab_editor_close (EAB_EDITOR (ce)); } else { ce->priv->changed = FALSE; g_object_ref (ce->priv->target_client); g_object_unref (ce->priv->source_client); ce->priv->source_client = ce->priv->target_client; sensitize_all (ce); } if (error != NULL) g_error_free (error); g_object_unref (ce); g_free (ecs->new_id); g_free (ecs); } static void contact_added_cb (EBookClient *book_client, const GError *error, const gchar *id, gpointer closure) { EditorCloseStruct *ecs = closure; EContactEditor *ce = ecs->ce; gboolean should_close = ecs->should_close; if (ce->priv->source_client != ce->priv->target_client && !e_client_is_readonly (E_CLIENT (ce->priv->source_client)) && !error && ce->priv->is_new_contact == FALSE) { ecs->new_id = g_strdup (id); e_book_client_remove_contact ( ce->priv->source_client, ce->priv->contact, NULL, contact_removed_cb, ecs); return; } gtk_widget_set_sensitive (ce->priv->app, TRUE); ce->priv->in_async_call = FALSE; e_contact_set (ce->priv->contact, E_CONTACT_UID, id); eab_editor_contact_added (EAB_EDITOR (ce), error, ce->priv->contact); if (!error) { ce->priv->is_new_contact = FALSE; if (should_close) { eab_editor_close (EAB_EDITOR (ce)); } else { ce->priv->changed = FALSE; sensitize_all (ce); } } g_object_unref (ce); g_free (ecs); } static void contact_modified_cb (EBookClient *book_client, const GError *error, gpointer closure) { EditorCloseStruct *ecs = closure; EContactEditor *ce = ecs->ce; gboolean should_close = ecs->should_close; gtk_widget_set_sensitive (ce->priv->app, TRUE); ce->priv->in_async_call = FALSE; eab_editor_contact_modified (EAB_EDITOR (ce), error, ce->priv->contact); if (!error) { if (should_close) { eab_editor_close (EAB_EDITOR (ce)); } else { ce->priv->changed = FALSE; sensitize_all (ce); } } g_object_unref (ce); g_free (ecs); } static void contact_modified_ready_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { EBookClient *book_client = E_BOOK_CLIENT (source_object); GError *error = NULL; e_book_client_modify_contact_finish (book_client, result, &error); contact_modified_cb (book_client, error, user_data); if (error != NULL) g_error_free (error); } /* Emits the signal to request saving a contact */ static void real_save_contact (EContactEditor *ce, gboolean should_close) { EShell *shell; EditorCloseStruct *ecs; ESourceRegistry *registry; shell = eab_editor_get_shell (EAB_EDITOR (ce)); registry = e_shell_get_registry (shell); ecs = g_new0 (EditorCloseStruct, 1); ecs->ce = ce; g_object_ref (ecs->ce); ecs->should_close = should_close; gtk_widget_set_sensitive (ce->priv->app, FALSE); ce->priv->in_async_call = TRUE; if (ce->priv->source_client != ce->priv->target_client) { /* Two-step move; add to target, then remove from source */ eab_merging_book_add_contact ( registry, ce->priv->target_client, ce->priv->contact, contact_added_cb, ecs); } else { if (ce->priv->is_new_contact) eab_merging_book_add_contact ( registry, ce->priv->target_client, ce->priv->contact, contact_added_cb, ecs); else if (ce->priv->check_merge) eab_merging_book_modify_contact ( registry, ce->priv->target_client, ce->priv->contact, contact_modified_cb, ecs); else e_book_client_modify_contact ( ce->priv->target_client, ce->priv->contact, NULL, contact_modified_ready_cb, ecs); } } static void save_contact (EContactEditor *ce, gboolean should_close) { gchar *uid; const gchar *name_entry_string; const gchar *file_as_entry_string; const gchar *company_name_string; GtkWidget *entry_fullname, *entry_file_as, *company_name, *client_combo_box; ESource *active_source; if (!ce->priv->target_client) return; client_combo_box = e_builder_get_widget (ce->priv->builder, "client-combo-box"); active_source = e_source_combo_box_ref_active (E_SOURCE_COMBO_BOX (client_combo_box)); g_return_if_fail (active_source != NULL); if (!e_source_equal (e_client_get_source (E_CLIENT (ce->priv->target_client)), active_source)) { e_alert_run_dialog_for_args ( GTK_WINDOW (ce->priv->app), "addressbook:error-still-opening", e_source_get_display_name (active_source), NULL); g_object_unref (active_source); return; } g_object_unref (active_source); if (ce->priv->target_editable && e_client_is_readonly (E_CLIENT (ce->priv->source_client))) { if (e_alert_run_dialog_for_args ( GTK_WINDOW (ce->priv->app), "addressbook:prompt-move", NULL) == GTK_RESPONSE_NO) return; } entry_fullname = e_builder_get_widget (ce->priv->builder, "entry-fullname"); entry_file_as = gtk_bin_get_child ( GTK_BIN (e_builder_get_widget (ce->priv->builder, "combo-file-as"))); company_name = e_builder_get_widget (ce->priv->builder, "entry-company"); name_entry_string = gtk_entry_get_text (GTK_ENTRY (entry_fullname)); file_as_entry_string = gtk_entry_get_text (GTK_ENTRY (entry_file_as)); company_name_string = gtk_entry_get_text (GTK_ENTRY (company_name)); if (strcmp (company_name_string , "")) { if (!strcmp (name_entry_string, "")) gtk_entry_set_text ( GTK_ENTRY (entry_fullname), company_name_string); if (!strcmp (file_as_entry_string, "")) gtk_entry_set_text ( GTK_ENTRY (entry_file_as), company_name_string); } extract_all (ce); if (!e_contact_editor_is_valid (EAB_EDITOR (ce))) { uid = e_contact_get (ce->priv->contact, E_CONTACT_UID); g_object_unref (ce->priv->contact); ce->priv->contact = e_contact_new (); if (uid) { e_contact_set (ce->priv->contact, E_CONTACT_UID, uid); g_free (uid); } return; } real_save_contact (ce, should_close); } static void e_contact_editor_save_contact (EABEditor *editor, gboolean should_close) { save_contact (E_CONTACT_EDITOR (editor), should_close); } /* Closes the dialog box and emits the appropriate signals */ static void e_contact_editor_close (EABEditor *editor) { EContactEditor *ce = E_CONTACT_EDITOR (editor); if (ce->priv->app != NULL) { gtk_widget_destroy (ce->priv->app); ce->priv->app = NULL; eab_editor_closed (editor); } } static const EContactField non_string_fields[] = { E_CONTACT_FULL_NAME, E_CONTACT_ADDRESS, E_CONTACT_ADDRESS_HOME, E_CONTACT_ADDRESS_WORK, E_CONTACT_ADDRESS_OTHER, E_CONTACT_EMAIL, E_CONTACT_IM_AIM, E_CONTACT_IM_GROUPWISE, E_CONTACT_IM_JABBER, E_CONTACT_IM_YAHOO, E_CONTACT_IM_GADUGADU, E_CONTACT_IM_MSN, E_CONTACT_IM_ICQ, E_CONTACT_IM_SKYPE, E_CONTACT_IM_TWITTER, E_CONTACT_PHOTO, E_CONTACT_LOGO, E_CONTACT_X509_CERT, E_CONTACT_CATEGORY_LIST, E_CONTACT_BIRTH_DATE, E_CONTACT_ANNIVERSARY }; static gboolean is_non_string_field (EContactField id) { gint count = sizeof (non_string_fields) / sizeof (EContactField); gint i; for (i = 0; i < count; i++) if (id == non_string_fields[i]) return TRUE; return FALSE; } /* insert checks here (date format, for instance, etc.) */ static gboolean e_contact_editor_is_valid (EABEditor *editor) { EContactEditor *ce = E_CONTACT_EDITOR (editor); GtkWidget *widget; gboolean validation_error = FALSE; GSList *iter; GString *errmsg = g_string_new (_("The contact data is invalid:\n\n")); time_t bday, now = time (NULL); widget = e_builder_get_widget (ce->priv->builder, "dateedit-birthday"); if (!(e_date_edit_date_is_valid (E_DATE_EDIT (widget)))) { g_string_append_printf ( errmsg, _("'%s' has an invalid format"), e_contact_pretty_name (E_CONTACT_BIRTH_DATE)); validation_error = TRUE; } /* If valid, see if the birthday is a future date */ bday = e_date_edit_get_time (E_DATE_EDIT (widget)); if (bday > now) { g_string_append_printf ( errmsg, _("'%s' cannot be a future date"), e_contact_pretty_name (E_CONTACT_BIRTH_DATE)); validation_error = TRUE; } widget = e_builder_get_widget (ce->priv->builder, "dateedit-anniversary"); if (!(e_date_edit_date_is_valid (E_DATE_EDIT (widget)))) { g_string_append_printf ( errmsg, _("%s'%s' has an invalid format"), validation_error ? ",\n" : "", e_contact_pretty_name (E_CONTACT_ANNIVERSARY)); validation_error = TRUE; } for (iter = ce->priv->required_fields; iter; iter = iter->next) { const gchar *field_name = iter->data; EContactField field_id = e_contact_field_id (field_name); if (is_non_string_field (field_id)) { if (e_contact_get_const (ce->priv->contact, field_id) == NULL) { g_string_append_printf ( errmsg, _("%s'%s' is empty"), validation_error ? ",\n" : "", e_contact_pretty_name (field_id)); validation_error = TRUE; break; } } else { const gchar *text; text = e_contact_get_const (ce->priv->contact, field_id); if (STRING_IS_EMPTY (text)) { g_string_append_printf ( errmsg, _("%s'%s' is empty"), validation_error ? ",\n" : "", e_contact_pretty_name (field_id)); validation_error = TRUE; break; } } } if (validation_error) { g_string_append (errmsg, "."); e_alert_run_dialog_for_args ( GTK_WINDOW (ce->priv->app), "addressbook:generic-error", _("Invalid contact."), errmsg->str, NULL); g_string_free (errmsg, TRUE); return FALSE; } else { g_string_free (errmsg, TRUE); return TRUE; } } static gboolean e_contact_editor_is_changed (EABEditor *editor) { return E_CONTACT_EDITOR (editor)->priv->changed; } static GtkWindow * e_contact_editor_get_window (EABEditor *editor) { return GTK_WINDOW (E_CONTACT_EDITOR (editor)->priv->app); } static void file_save_and_close_cb (GtkWidget *widget, EContactEditor *ce) { save_contact (ce, TRUE); } static void file_cancel_cb (GtkWidget *widget, EContactEditor *ce) { eab_editor_close (EAB_EDITOR (ce)); } /* Callback used when the dialog box is destroyed */ static gint app_delete_event_cb (GtkWidget *widget, GdkEvent *event, gpointer data) { EContactEditor *ce; ce = E_CONTACT_EDITOR (data); /* if we're saving, don't allow the dialog to close */ if (ce->priv->in_async_call) return TRUE; if (ce->priv->changed) { switch (eab_prompt_save_dialog (GTK_WINDOW (ce->priv->app))) { case GTK_RESPONSE_YES: eab_editor_save_contact (EAB_EDITOR (ce), TRUE); return TRUE; case GTK_RESPONSE_NO: break; case GTK_RESPONSE_CANCEL: default: return TRUE; } } eab_editor_close (EAB_EDITOR (ce)); return TRUE; } static void show_help_cb (GtkWidget *widget, gpointer data) { /* FIXME Pass a proper parent window. */ e_display_help (NULL, "contacts-usage-add-contact"); } static GList * add_to_tab_order (GList *list, GtkBuilder *builder, const gchar *name) { GtkWidget *widget = e_builder_get_widget (builder, name); return g_list_prepend (list, widget); } static void setup_tab_order (GtkBuilder *builder) { GtkWidget *container; GList *list = NULL; /* container = e_builder_get_widget (builder, "table-contact-editor-general"); * if (container) { list = add_to_tab_order (list, builder, "entry-fullname"); list = add_to_tab_order (list, builder, "entry-jobtitle"); list = add_to_tab_order (list, builder, "entry-company"); list = add_to_tab_order (list, builder, "combo-file-as"); list = add_to_tab_order (list, builder, "entry-phone-1"); list = add_to_tab_order (list, builder, "entry-phone-2"); list = add_to_tab_order (list, builder, "entry-phone-3"); list = add_to_tab_order (list, builder, "entry-phone-4"); * list = add_to_tab_order (list, builder, "entry-email1"); list = add_to_tab_order (list, builder, "alignment-htmlmail"); list = add_to_tab_order (list, builder, "entry-web"); list = add_to_tab_order (list, builder, "entry-homepage"); list = add_to_tab_order (list, builder, "button-fulladdr"); list = add_to_tab_order (list, builder, "text-address"); list = g_list_reverse (list); e_container_change_tab_order (GTK_CONTAINER (container), list); g_list_free (list); } */ container = e_builder_get_widget (builder, "table-home-address"); gtk_container_get_focus_chain (GTK_CONTAINER (container), &list); list = add_to_tab_order (list, builder, "scrolledwindow-home-address"); list = add_to_tab_order (list, builder, "entry-home-city"); list = add_to_tab_order (list, builder, "entry-home-zip"); list = add_to_tab_order (list, builder, "entry-home-state"); list = add_to_tab_order (list, builder, "entry-home-pobox"); list = add_to_tab_order (list, builder, "entry-home-country"); list = g_list_reverse (list); gtk_container_set_focus_chain (GTK_CONTAINER (container), list); g_list_free (list); container = e_builder_get_widget (builder, "table-work-address"); gtk_container_get_focus_chain (GTK_CONTAINER (container), &list); list = add_to_tab_order (list, builder, "scrolledwindow-work-address"); list = add_to_tab_order (list, builder, "entry-work-city"); list = add_to_tab_order (list, builder, "entry-work-zip"); list = add_to_tab_order (list, builder, "entry-work-state"); list = add_to_tab_order (list, builder, "entry-work-pobox"); list = add_to_tab_order (list, builder, "entry-work-country"); list = g_list_reverse (list); gtk_container_set_focus_chain (GTK_CONTAINER (container), list); g_list_free (list); container = e_builder_get_widget (builder, "table-other-address"); gtk_container_get_focus_chain (GTK_CONTAINER (container), &list); list = add_to_tab_order (list, builder, "scrolledwindow-other-address"); list = add_to_tab_order (list, builder, "entry-other-city"); list = add_to_tab_order (list, builder, "entry-other-zip"); list = add_to_tab_order (list, builder, "entry-other-state"); list = add_to_tab_order (list, builder, "entry-other-pobox"); list = add_to_tab_order (list, builder, "entry-other-country"); list = g_list_reverse (list); gtk_container_set_focus_chain (GTK_CONTAINER (container), list); g_list_free (list); } static void expand_web_toggle (EContactEditor *ce) { GtkWidget *widget; widget = e_builder_get_widget (ce->priv->builder, "label-videourl"); expand_web (ce, !gtk_widget_get_visible (widget)); } static void expand_phone_toggle (EContactEditor *ce) { expand_phone (ce, !is_arrow_image_arrow_down (ce, "arrow-phone-expand")); } static void expand_sip_toggle (EContactEditor *ce) { expand_sip (ce, !is_arrow_image_arrow_down (ce, "arrow-sip-expand")); } static void expand_im_toggle (EContactEditor *ce) { expand_im (ce, !is_arrow_image_arrow_down (ce, "arrow-im-expand")); } static void expand_mail_toggle (EContactEditor *ce) { expand_mail (ce, !is_arrow_image_arrow_down (ce, "arrow-mail-expand")); } static void contact_editor_focus_widget_changed_cb (EFocusTracker *focus_tracker, GParamSpec *param, EContactEditor *editor) { GtkWidget *widget; widget = e_focus_tracker_get_focus (focus_tracker); /* there is no problem to call the attach multiple times */ if (widget) e_widget_undo_attach (widget, focus_tracker); } static void e_contact_editor_init (EContactEditor *e_contact_editor) { GtkBuilder *builder; EShell *shell; EClientCache *client_cache; GtkWidget *container; GtkWidget *widget, *label; GtkEntryCompletion *completion; e_contact_editor->priv = G_TYPE_INSTANCE_GET_PRIVATE ( e_contact_editor, E_TYPE_CONTACT_EDITOR, EContactEditorPrivate); /* FIXME The shell should be obtained * through a constructor property. */ shell = e_shell_get_default (); client_cache = e_shell_get_client_cache (shell); e_contact_editor->priv->name = e_contact_name_new (); e_contact_editor->priv->contact = NULL; e_contact_editor->priv->changed = FALSE; e_contact_editor->priv->check_merge = FALSE; e_contact_editor->priv->image_set = FALSE; e_contact_editor->priv->image_changed = FALSE; e_contact_editor->priv->in_async_call = FALSE; e_contact_editor->priv->target_editable = TRUE; e_contact_editor->priv->fullname_dialog = NULL; e_contact_editor->priv->categories_dialog = NULL; e_contact_editor->priv->compress_ui = e_shell_get_express_mode (shell); /* Make sure custom widget types are available */ g_type_ensure (E_TYPE_IMAGE_CHOOSER); g_type_ensure (E_TYPE_CLIENT_COMBO_BOX); g_type_ensure (E_TYPE_CONTACT_EDITOR_DYNTABLE); g_type_ensure (E_TYPE_URL_ENTRY); g_type_ensure (E_TYPE_DATE_EDIT); builder = gtk_builder_new (); e_load_ui_builder_definition (builder, "contact-editor.ui"); e_contact_editor->priv->builder = builder; setup_tab_order (builder); e_contact_editor->priv->app = e_builder_get_widget (builder, "contact editor"); widget = e_contact_editor->priv->app; gtk_widget_ensure_style (widget); gtk_window_set_type_hint ( GTK_WINDOW (widget), GDK_WINDOW_TYPE_HINT_NORMAL); container = gtk_dialog_get_action_area (GTK_DIALOG (widget)); gtk_container_set_border_width (GTK_CONTAINER (container), 12); container = gtk_dialog_get_content_area (GTK_DIALOG (widget)); gtk_container_set_border_width (GTK_CONTAINER (container), 0); init_all (e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-image"); g_signal_connect ( widget, "clicked", G_CALLBACK (image_clicked), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-fullname"); g_signal_connect ( widget, "clicked", G_CALLBACK (full_name_clicked), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-categories"); g_signal_connect ( widget, "clicked", G_CALLBACK (categories_clicked), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "client-combo-box"); e_client_combo_box_set_client_cache ( E_CLIENT_COMBO_BOX (widget), client_cache); g_signal_connect ( widget, "changed", G_CALLBACK (source_changed), e_contact_editor); label = e_builder_get_widget ( e_contact_editor->priv->builder, "where-label"); gtk_label_set_mnemonic_widget (GTK_LABEL (label), widget); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-ok"); g_signal_connect ( widget, "clicked", G_CALLBACK (file_save_and_close_cb), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-cancel"); g_signal_connect ( widget, "clicked", G_CALLBACK (file_cancel_cb), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-help"); g_signal_connect ( widget, "clicked", G_CALLBACK (show_help_cb), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-web-expand"); g_signal_connect_swapped ( widget, "clicked", G_CALLBACK (expand_web_toggle), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-phone-expand"); g_signal_connect_swapped ( widget, "clicked", G_CALLBACK (expand_phone_toggle), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-sip-expand"); g_signal_connect_swapped ( widget, "clicked", G_CALLBACK (expand_sip_toggle), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-im-expand"); g_signal_connect_swapped ( widget, "clicked", G_CALLBACK (expand_im_toggle), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "button-mail-expand"); g_signal_connect_swapped ( widget, "clicked", G_CALLBACK (expand_mail_toggle), e_contact_editor); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "entry-fullname"); if (widget) gtk_widget_grab_focus (widget); widget = e_builder_get_widget ( e_contact_editor->priv->builder, "entry-categories"); completion = e_category_completion_new (); gtk_entry_set_completion (GTK_ENTRY (widget), completion); g_object_unref (completion); /* Connect to the deletion of the dialog */ g_signal_connect ( e_contact_editor->priv->app, "delete_event", G_CALLBACK (app_delete_event_cb), e_contact_editor); /* set the icon */ gtk_window_set_icon_name ( GTK_WINDOW (e_contact_editor->priv->app), "contact-editor"); /* show window */ gtk_widget_show (e_contact_editor->priv->app); gtk_application_add_window ( GTK_APPLICATION (shell), GTK_WINDOW (e_contact_editor->priv->app)); } static void e_contact_editor_constructed (GObject *object) { const gchar *ui = "" " " " " " " " " " " " " ""; EContactEditor *editor = E_CONTACT_EDITOR (object); GtkActionGroup *action_group; GtkAction *action; GError *error = NULL; editor->priv->focus_tracker = e_focus_tracker_new (GTK_WINDOW (editor->priv->app)); editor->priv->ui_manager = gtk_ui_manager_new (); gtk_window_add_accel_group ( GTK_WINDOW (editor->priv->app), gtk_ui_manager_get_accel_group (editor->priv->ui_manager)); e_signal_connect_notify ( editor->priv->focus_tracker, "notify::focus", G_CALLBACK (contact_editor_focus_widget_changed_cb), editor); action_group = gtk_action_group_new ("undo"); gtk_action_group_set_translation_domain ( action_group, GETTEXT_PACKAGE); gtk_action_group_add_actions ( action_group, undo_entries, G_N_ELEMENTS (undo_entries), editor); gtk_ui_manager_insert_action_group ( editor->priv->ui_manager, action_group, 0); action = gtk_action_group_get_action (action_group, "undo"); e_focus_tracker_set_undo_action (editor->priv->focus_tracker, action); action = gtk_action_group_get_action (action_group, "redo"); e_focus_tracker_set_redo_action (editor->priv->focus_tracker, action); g_object_unref (action_group); gtk_ui_manager_add_ui_from_string (editor->priv->ui_manager, ui, -1, &error); if (error != NULL) { g_warning ("%s: %s", G_STRFUNC, error->message); g_error_free (error); } } static void e_contact_editor_dispose (GObject *object) { EContactEditor *e_contact_editor = E_CONTACT_EDITOR (object); if (e_contact_editor->priv->file_selector != NULL) { gtk_widget_destroy (e_contact_editor->priv->file_selector); e_contact_editor->priv->file_selector = NULL; } g_slist_free_full ( e_contact_editor->priv->writable_fields, (GDestroyNotify) g_free); e_contact_editor->priv->writable_fields = NULL; g_slist_free_full ( e_contact_editor->priv->required_fields, (GDestroyNotify) g_free); e_contact_editor->priv->required_fields = NULL; if (e_contact_editor->priv->target_client) { g_signal_handler_disconnect ( e_contact_editor->priv->target_client, e_contact_editor->priv->target_editable_id); } if (e_contact_editor->priv->name) { e_contact_name_free (e_contact_editor->priv->name); e_contact_editor->priv->name = NULL; } if (e_contact_editor->priv->focus_tracker) { g_signal_handlers_disconnect_by_data ( e_contact_editor->priv->focus_tracker, e_contact_editor); } g_clear_object (&e_contact_editor->priv->contact); g_clear_object (&e_contact_editor->priv->source_client); g_clear_object (&e_contact_editor->priv->target_client); g_clear_object (&e_contact_editor->priv->builder); g_clear_object (&e_contact_editor->priv->ui_manager); g_clear_object (&e_contact_editor->priv->cancellable); g_clear_object (&e_contact_editor->priv->focus_tracker); /* Chain up to parent's dispose() method. */ G_OBJECT_CLASS (parent_class)->dispose (object); } static void supported_fields_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { EBookClient *book_client = E_BOOK_CLIENT (source_object); EContactEditor *ce = user_data; gchar *prop_value = NULL; GSList *fields; gboolean success; GError *error = NULL; success = e_client_get_backend_property_finish ( E_CLIENT (book_client), result, &prop_value, &error); if (!success) prop_value = NULL; if (error != NULL) { g_warning ( "%s: Failed to get supported fields: %s", G_STRFUNC, error->message); g_error_free (error); } if (!g_slist_find (eab_editor_get_all_editors (), ce)) { g_warning ( "supported_fields_cb called for book that's still " "around, but contact editor that's been destroyed."); g_free (prop_value); return; } fields = e_client_util_parse_comma_strings (prop_value); g_object_set (ce, "writable_fields", fields, NULL); g_slist_free_full (fields, (GDestroyNotify) g_free); g_free (prop_value); eab_editor_show (EAB_EDITOR (ce)); sensitize_all (ce); } static void required_fields_cb (GObject *source_object, GAsyncResult *result, gpointer user_data) { EBookClient *book_client = E_BOOK_CLIENT (source_object); EContactEditor *ce = user_data; gchar *prop_value = NULL; GSList *fields; gboolean success; GError *error = NULL; success = e_client_get_backend_property_finish ( E_CLIENT (book_client), result, &prop_value, &error); if (!success) prop_value = NULL; if (error != NULL) { g_warning ( "%s: Failed to get supported fields: %s", G_STRFUNC, error->message); g_error_free (error); } if (!g_slist_find (eab_editor_get_all_editors (), ce)) { g_warning ( "supported_fields_cb called for book that's still " "around, but contact editor that's been destroyed."); g_free (prop_value); return; } fields = e_client_util_parse_comma_strings (prop_value); g_object_set (ce, "required_fields", fields, NULL); g_slist_free_full (fields, (GDestroyNotify) g_free); g_free (prop_value); } EABEditor * e_contact_editor_new (EShell *shell, EBookClient *book_client, EContact *contact, gboolean is_new_contact, gboolean editable) { EABEditor *editor; g_return_val_if_fail (E_IS_SHELL (shell), NULL); g_return_val_if_fail (E_IS_BOOK_CLIENT (book_client), NULL); g_return_val_if_fail (E_IS_CONTACT (contact), NULL); editor = g_object_new (E_TYPE_CONTACT_EDITOR, "shell", shell, NULL); g_object_set ( editor, "source_client", book_client, "contact", contact, "is_new_contact", is_new_contact, "editable", editable, NULL); return editor; } static void notify_readonly_cb (EBookClient *book_client, GParamSpec *pspec, EContactEditor *ce) { EClient *client; gint new_target_editable; gboolean changed = FALSE; client = E_CLIENT (ce->priv->target_client); new_target_editable = !e_client_is_readonly (client); if (ce->priv->target_editable != new_target_editable) changed = TRUE; ce->priv->target_editable = new_target_editable; if (changed) sensitize_all (ce); } static void e_contact_editor_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { EContactEditor *editor; editor = E_CONTACT_EDITOR (object); switch (property_id) { case PROP_SOURCE_CLIENT: { gboolean writable; gboolean changed = FALSE; EBookClient *source_client; source_client = E_BOOK_CLIENT (g_value_get_object (value)); if (source_client == editor->priv->source_client) break; if (editor->priv->source_client) g_object_unref (editor->priv->source_client); editor->priv->source_client = source_client; g_object_ref (editor->priv->source_client); if (!editor->priv->target_client) { editor->priv->target_client = editor->priv->source_client; g_object_ref (editor->priv->target_client); editor->priv->target_editable_id = e_signal_connect_notify ( editor->priv->target_client, "notify::readonly", G_CALLBACK (notify_readonly_cb), editor); e_client_get_backend_property ( E_CLIENT (editor->priv->target_client), BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS, NULL, supported_fields_cb, editor); e_client_get_backend_property ( E_CLIENT (editor->priv->target_client), BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS, NULL, required_fields_cb, editor); } writable = !e_client_is_readonly (E_CLIENT (editor->priv->target_client)); if (writable != editor->priv->target_editable) { editor->priv->target_editable = writable; changed = TRUE; } if (changed) sensitize_all (editor); break; } case PROP_TARGET_CLIENT: { gboolean writable; gboolean changed = FALSE; EBookClient *target_client; target_client = E_BOOK_CLIENT (g_value_get_object (value)); if (target_client == editor->priv->target_client) break; if (editor->priv->target_client) { g_signal_handler_disconnect ( editor->priv->target_client, editor->priv->target_editable_id); g_object_unref (editor->priv->target_client); } editor->priv->target_client = target_client; g_object_ref (editor->priv->target_client); editor->priv->target_editable_id = e_signal_connect_notify ( editor->priv->target_client, "notify::readonly", G_CALLBACK (notify_readonly_cb), editor); e_client_get_backend_property ( E_CLIENT (editor->priv->target_client), BOOK_BACKEND_PROPERTY_SUPPORTED_FIELDS, NULL, supported_fields_cb, editor); e_client_get_backend_property ( E_CLIENT (editor->priv->target_client), BOOK_BACKEND_PROPERTY_REQUIRED_FIELDS, NULL, required_fields_cb, editor); if (!editor->priv->is_new_contact) editor->priv->changed = TRUE; writable = !e_client_is_readonly (E_CLIENT (editor->priv->target_client)); if (writable != editor->priv->target_editable) { editor->priv->target_editable = writable; changed = TRUE; } if (changed) sensitize_all (editor); break; } case PROP_CONTACT: if (editor->priv->contact) g_object_unref (editor->priv->contact); editor->priv->contact = e_contact_duplicate ( E_CONTACT (g_value_get_object (value))); fill_in_all (editor); editor->priv->changed = FALSE; break; case PROP_IS_NEW_CONTACT: editor->priv->is_new_contact = g_value_get_boolean (value); break; case PROP_EDITABLE: { gboolean new_value = g_value_get_boolean (value); gboolean changed = (editor->priv->target_editable != new_value); editor->priv->target_editable = new_value; if (changed) sensitize_all (editor); break; } case PROP_CHANGED: { gboolean new_value = g_value_get_boolean (value); gboolean changed = (editor->priv->changed != new_value); editor->priv->changed = new_value; if (changed) sensitize_ok (editor); break; } case PROP_WRITABLE_FIELDS: g_slist_free_full ( editor->priv->writable_fields, (GDestroyNotify) g_free); editor->priv->writable_fields = g_slist_copy_deep ( g_value_get_pointer (value), (GCopyFunc) g_strdup, NULL); sensitize_all (editor); break; case PROP_REQUIRED_FIELDS: g_slist_free_full ( editor->priv->required_fields, (GDestroyNotify) g_free); editor->priv->required_fields = g_slist_copy_deep ( g_value_get_pointer (value), (GCopyFunc) g_strdup, NULL); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void e_contact_editor_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { EContactEditor *e_contact_editor; e_contact_editor = E_CONTACT_EDITOR (object); switch (property_id) { case PROP_SOURCE_CLIENT: g_value_set_object (value, e_contact_editor->priv->source_client); break; case PROP_TARGET_CLIENT: g_value_set_object (value, e_contact_editor->priv->target_client); break; case PROP_CONTACT: extract_all (e_contact_editor); g_value_set_object (value, e_contact_editor->priv->contact); break; case PROP_IS_NEW_CONTACT: g_value_set_boolean ( value, e_contact_editor->priv->is_new_contact); break; case PROP_EDITABLE: g_value_set_boolean ( value, e_contact_editor->priv->target_editable); break; case PROP_CHANGED: g_value_set_boolean ( value, e_contact_editor->priv->changed); break; case PROP_WRITABLE_FIELDS: g_value_set_pointer (value, e_contact_editor->priv->writable_fields); break; case PROP_REQUIRED_FIELDS: g_value_set_pointer (value, e_contact_editor->priv->required_fields); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } /** * e_contact_editor_raise: * @config: The %EContactEditor object. * * Raises the dialog associated with this %EContactEditor object. */ static void e_contact_editor_raise (EABEditor *editor) { EContactEditor *ce = E_CONTACT_EDITOR (editor); GdkWindow *window; window = gtk_widget_get_window (ce->priv->app); if (window != NULL) gdk_window_raise (window); } /** * e_contact_editor_show: * @ce: The %EContactEditor object. * * Shows the dialog associated with this %EContactEditor object. */ static void e_contact_editor_show (EABEditor *editor) { EContactEditor *ce = E_CONTACT_EDITOR (editor); gtk_widget_show (ce->priv->app); }