diff options
Diffstat (limited to 'addressbook/importers/evolution-vcard-importer.c')
-rw-r--r-- | addressbook/importers/evolution-vcard-importer.c | 1016 |
1 files changed, 1016 insertions, 0 deletions
diff --git a/addressbook/importers/evolution-vcard-importer.c b/addressbook/importers/evolution-vcard-importer.c new file mode 100644 index 0000000000..09778b5f21 --- /dev/null +++ b/addressbook/importers/evolution-vcard-importer.c @@ -0,0 +1,1016 @@ +/* + * Evolution calendar importer component + * + * 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; either + * version 2 of the License, or (at your option) version 3. + * + * 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 + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with the program; if not, see <http://www.gnu.org/licenses/> + * + * + * Authors: + * Chris Toshok <toshok@ximian.com> + * JP Rosevear <jpr@ximian.com> + * Michael Zucchi <notzed@ximian.com> + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <stdio.h> +#include <string.h> + +#include <gtk/gtk.h> +#include <glib/gi18n.h> +#include <glib/gstdio.h> + +#include <libebook/libebook.h> + +#include <util/eab-book-util.h> + +#include <shell/e-shell.h> + +#include "evolution-addressbook-importers.h" + +enum _VCardEncoding { + VCARD_ENCODING_NONE, + VCARD_ENCODING_UTF8, + VCARD_ENCODING_UTF16, + VCARD_ENCODING_LOCALE +}; + +typedef enum _VCardEncoding VCardEncoding; + +typedef struct { + EImport *import; + EImportTarget *target; + + guint idle_id; + + gint state; /* 0 - importing, 1 - cancelled/complete */ + gint total; + gint count; + + ESource *primary; + + GSList *contactlist; + GSList *iterator; + EBookClient *book_client; + + /* when opening book */ + gchar *contents; + VCardEncoding encoding; +} VCardImporter; + +static void vcard_import_done (VCardImporter *gci); + +static void +add_to_notes (EContact *contact, + EContactField field) +{ + const gchar *old_text; + const gchar *field_text; + gchar *new_text; + + old_text = e_contact_get_const (contact, E_CONTACT_NOTE); + if (old_text && strstr (old_text, e_contact_pretty_name (field))) + return; + + field_text = e_contact_get_const (contact, field); + if (!field_text || !*field_text) + return; + + new_text = g_strdup_printf ( + "%s%s%s: %s", + old_text ? old_text : "", + old_text && *old_text && + *(old_text + strlen (old_text) - 1) != '\n' ? "\n" : "", + e_contact_pretty_name (field), field_text); + e_contact_set (contact, E_CONTACT_NOTE, new_text); + g_free (new_text); +} + +static void +vcard_import_contact (VCardImporter *gci, + EContact *contact) +{ + EContactPhoto *photo; + GList *attrs, *attr; + gchar *uid = NULL; + + /* Apple's addressbook.app exports PHOTO's without a TYPE + * param, so let's figure out the format here if there's a + * PHOTO attribute missing a TYPE param. + * + * this is sort of a hack, as EContact sets the type for us if + * we use the setter. so let's e_contact_get + e_contact_set + * on E_CONTACT_PHOTO. + */ + photo = e_contact_get (contact, E_CONTACT_PHOTO); + if (photo) { + e_contact_set (contact, E_CONTACT_PHOTO, photo); + e_contact_photo_free (photo); + } + + /* Deal with our XML EDestination stuff in EMAIL attributes, if there is any. */ + attrs = e_contact_get_attributes (contact, E_CONTACT_EMAIL); + for (attr = attrs; attr; attr = attr->next) { + EVCardAttribute *a = attr->data; + GList *v = e_vcard_attribute_get_values (a); + + if (v && v->data) { + if (!strncmp ((gchar *) v->data, "<?xml", 5)) { + EDestination *dest = e_destination_import ((gchar *) v->data); + + e_destination_export_to_vcard_attribute (dest, a); + + g_object_unref (dest); + + } + } + } + e_contact_set_attributes (contact, E_CONTACT_EMAIL, attrs); + + /* Deal with TEL attributes that don't conform to what we need. + * + * 1. if there's no location (HOME/WORK/OTHER), default to OTHER. + * 2. if there's *only* a location specified, default to VOICE. + */ + attrs = e_vcard_get_attributes (E_VCARD (contact)); + for (attr = attrs; attr; attr = attr->next) { + EVCardAttribute *a = attr->data; + gboolean location_only = TRUE; + gboolean no_location = TRUE; + gboolean is_work_home = FALSE; + GList *params, *param; + + if (g_ascii_strcasecmp (e_vcard_attribute_get_name (a), + EVC_TEL)) + continue; + + params = e_vcard_attribute_get_params (a); + for (param = params; param; param = param->next) { + EVCardAttributeParam *p = param->data; + GList *vs, *v; + + if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (p), + EVC_TYPE)) + continue; + + vs = e_vcard_attribute_param_get_values (p); + for (v = vs; v; v = v->next) { + is_work_home = is_work_home || + !g_ascii_strcasecmp ((gchar *) v->data, "WORK") || + !g_ascii_strcasecmp ((gchar *) v->data, "HOME"); + + if (!g_ascii_strcasecmp ((gchar *) v->data, "WORK") || + !g_ascii_strcasecmp ((gchar *) v->data, "HOME") || + !g_ascii_strcasecmp ((gchar *) v->data, "OTHER")) + no_location = FALSE; + else + location_only = FALSE; + } + } + + if (is_work_home) { + /* only WORK and HOME phone numbers require locations, + * the rest should be kept as is */ + if (location_only) { + /* add VOICE */ + e_vcard_attribute_add_param_with_value ( + a, + e_vcard_attribute_param_new (EVC_TYPE), + "VOICE"); + } + if (no_location) { + /* add OTHER */ + e_vcard_attribute_add_param_with_value ( + a, + e_vcard_attribute_param_new (EVC_TYPE), + "OTHER"); + } + } + } + + /* Deal with ADR and EMAIL attributes that don't conform to what + * we need. If HOME or WORK isn't specified, add TYPE=OTHER. */ + attrs = e_vcard_get_attributes (E_VCARD (contact)); + for (attr = attrs; attr; attr = attr->next) { + EVCardAttribute *a = attr->data; + gboolean no_location = TRUE; + GList *params, *param; + + if (g_ascii_strcasecmp (e_vcard_attribute_get_name (a), EVC_ADR) && + g_ascii_strcasecmp (e_vcard_attribute_get_name (a), EVC_EMAIL)) + continue; + + params = e_vcard_attribute_get_params (a); + for (param = params; param; param = param->next) { + EVCardAttributeParam *p = param->data; + GList *vs, *v; + + if (g_ascii_strcasecmp (e_vcard_attribute_param_get_name (p), + EVC_TYPE)) + continue; + + vs = e_vcard_attribute_param_get_values (p); + for (v = vs; v; v = v->next) { + if (!g_ascii_strcasecmp ((gchar *) v->data, "WORK") || + !g_ascii_strcasecmp ((gchar *) v->data, "HOME")) + no_location = FALSE; + } + } + + if (no_location) { + /* add OTHER */ + e_vcard_attribute_add_param_with_value ( + a, + e_vcard_attribute_param_new (EVC_TYPE), + "OTHER"); + } + } + + /* Work around the fact that these fields no longer show up in the UI */ + add_to_notes (contact, E_CONTACT_OFFICE); + add_to_notes (contact, E_CONTACT_SPOUSE); + add_to_notes (contact, E_CONTACT_BLOG_URL); + + /* FIXME Error checking */ + if (e_book_client_add_contact_sync (gci->book_client, contact, &uid, NULL, NULL) && uid) { + e_contact_set (contact, E_CONTACT_UID, uid); + g_free (uid); + } +} + +static gboolean +vcard_import_contacts (gpointer data) +{ + VCardImporter *gci = data; + gint count = 0; + GSList *iterator = gci->iterator; + + if (gci->state == 0) { + while (count < 50 && iterator) { + vcard_import_contact (gci, iterator->data); + count++; + iterator = iterator->next; + } + gci->count += count; + gci->iterator = iterator; + if (iterator == NULL) + gci->state = 1; + } + if (gci->state == 1) { + vcard_import_done (gci); + return FALSE; + } else { + e_import_status ( + gci->import, gci->target, _("Importing..."), + gci->count * 100 / gci->total); + return TRUE; + } +} + +#define BOM (gunichar2)0xFEFF +#define ANTIBOM (gunichar2)0xFFFE + +static gboolean +has_bom (const gunichar2 *utf16) +{ + + if ((utf16 == NULL) || (*utf16 == '\0')) { + return FALSE; + } + + return ((*utf16 == BOM) || (*utf16 == ANTIBOM)); +} + +static void +fix_utf16_endianness (gunichar2 *utf16) +{ + gunichar2 *it; + + if ((utf16 == NULL) || (*utf16 == '\0')) { + return; + } + + if (*utf16 != ANTIBOM) { + return; + } + + for (it = utf16; *it != '\0'; it++) { + *it = GUINT16_SWAP_LE_BE (*it); + } +} + +/* Converts an UTF-16 string to an UTF-8 string removing the BOM character + * WARNING: this may modify the utf16 argument if the function detects the + * string isn't using the local endianness + */ +static gchar * +utf16_to_utf8 (gunichar2 *utf16) +{ + + if (utf16 == NULL) { + return NULL; + } + + fix_utf16_endianness (utf16); + + if (*utf16 == BOM) { + utf16++; + } + + return g_utf16_to_utf8 (utf16, -1, NULL, NULL, NULL); +} + +/* Actually check the contents of this file */ +static VCardEncoding +guess_vcard_encoding (const gchar *filename) +{ + FILE *handle; + gchar line[4096]; + gchar *line_utf8; + VCardEncoding encoding = VCARD_ENCODING_NONE; + + handle = g_fopen (filename, "r"); + if (handle == NULL) { + g_print ("\n"); + return VCARD_ENCODING_NONE; + } + + if (fgets (line, 4096, handle) == NULL) { + fclose (handle); + g_print ("\n"); + return VCARD_ENCODING_NONE; + } + fclose (handle); + + if (has_bom ((gunichar2 *) line)) { + gunichar2 *utf16 = (gunichar2 *) line; + /* Check for a BOM to try to detect UTF-16 encoded vcards + * (MacOSX address book creates such vcards for example) + */ + line_utf8 = utf16_to_utf8 (utf16); + if (line_utf8 == NULL) { + return VCARD_ENCODING_NONE; + } + encoding = VCARD_ENCODING_UTF16; + } else if (g_utf8_validate (line, -1, NULL)) { + line_utf8 = g_strdup (line); + encoding = VCARD_ENCODING_UTF8; + } else { + line_utf8 = g_locale_to_utf8 (line, -1, NULL, NULL, NULL); + if (line_utf8 == NULL) { + return VCARD_ENCODING_NONE; + } + encoding = VCARD_ENCODING_LOCALE; + } + + if (g_ascii_strncasecmp (line_utf8, "BEGIN:VCARD", 11) != 0) { + encoding = VCARD_ENCODING_NONE; + } + + g_free (line_utf8); + return encoding; +} + +static void +primary_selection_changed_cb (ESourceSelector *selector, + EImportTarget *target) +{ + ESource *source; + + source = e_source_selector_ref_primary_selection (selector); + g_return_if_fail (source != NULL); + + g_datalist_set_data_full ( + &target->data, "vcard-source", + source, (GDestroyNotify) g_object_unref); +} + +static GtkWidget * +vcard_getwidget (EImport *ei, + EImportTarget *target, + EImportImporter *im) +{ + EShell *shell; + GtkWidget *vbox, *selector; + ESourceRegistry *registry; + ESource *primary; + const gchar *extension_name; + + vbox = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0); + + shell = e_shell_get_default (); + registry = e_shell_get_registry (shell); + extension_name = E_SOURCE_EXTENSION_ADDRESS_BOOK; + selector = e_source_selector_new (registry, extension_name); + e_source_selector_set_show_toggles ( + E_SOURCE_SELECTOR (selector), FALSE); + gtk_box_pack_start (GTK_BOX (vbox), selector, FALSE, TRUE, 6); + + primary = g_datalist_get_data (&target->data, "vcard-source"); + if (primary == NULL) { + GList *list; + + list = e_source_registry_list_sources (registry, extension_name); + if (list != NULL) { + primary = g_object_ref (list->data); + g_datalist_set_data_full ( + &target->data, "vcard-source", primary, + (GDestroyNotify) g_object_unref); + } + + g_list_free_full (list, (GDestroyNotify) g_object_unref); + } + e_source_selector_set_primary_selection ( + E_SOURCE_SELECTOR (selector), primary); + + g_signal_connect ( + selector, "primary_selection_changed", + G_CALLBACK (primary_selection_changed_cb), target); + + gtk_widget_show_all (vbox); + + return vbox; +} + +static gboolean +vcard_supported (EImport *ei, + EImportTarget *target, + EImportImporter *im) +{ + EImportTargetURI *s; + gchar *filename; + gboolean retval; + + if (target->type != E_IMPORT_TARGET_URI) + return FALSE; + + s = (EImportTargetURI *) target; + if (s->uri_src == NULL) + return TRUE; + + if (strncmp (s->uri_src, "file:///", 8) != 0) + return FALSE; + + filename = g_filename_from_uri (s->uri_src, NULL, NULL); + if (filename == NULL) + return FALSE; + retval = (guess_vcard_encoding (filename) != VCARD_ENCODING_NONE); + g_free (filename); + + return retval; +} + +static void +vcard_import_done (VCardImporter *gci) +{ + if (gci->idle_id) + g_source_remove (gci->idle_id); + + g_free (gci->contents); + g_object_unref (gci->book_client); + g_slist_free_full (gci->contactlist, (GDestroyNotify) g_object_unref); + + e_import_complete (gci->import, gci->target); + g_object_unref (gci->import); + g_free (gci); +} + +static void +book_client_connect_cb (GObject *source_object, + GAsyncResult *result, + gpointer user_data) +{ + VCardImporter *gci = user_data; + EClient *client; + + client = e_book_client_connect_finish (result, NULL); + + if (client == NULL) { + vcard_import_done (gci); + return; + } + + gci->book_client = E_BOOK_CLIENT (client); + + if (gci->encoding == VCARD_ENCODING_UTF16) { + gchar *tmp; + + gunichar2 *contents_utf16 = (gunichar2 *) gci->contents; + tmp = utf16_to_utf8 (contents_utf16); + g_free (gci->contents); + gci->contents = tmp; + + } else if (gci->encoding == VCARD_ENCODING_LOCALE) { + gchar *tmp; + tmp = g_locale_to_utf8 (gci->contents, -1, NULL, NULL, NULL); + g_free (gci->contents); + gci->contents = tmp; + } + + gci->contactlist = eab_contact_list_from_string (gci->contents); + g_free (gci->contents); + gci->contents = NULL; + gci->iterator = gci->contactlist; + gci->total = g_slist_length (gci->contactlist); + + if (gci->iterator) + gci->idle_id = g_idle_add (vcard_import_contacts, gci); + else + vcard_import_done (gci); +} + +static void +vcard_import (EImport *ei, + EImportTarget *target, + EImportImporter *im) +{ + VCardImporter *gci; + ESource *source; + EImportTargetURI *s = (EImportTargetURI *) target; + gchar *filename; + gchar *contents; + VCardEncoding encoding; + + filename = g_filename_from_uri (s->uri_src, NULL, NULL); + if (filename == NULL) { + g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src); + e_import_complete (ei, target); + return; + } + encoding = guess_vcard_encoding (filename); + if (encoding == VCARD_ENCODING_NONE) { + g_free (filename); + /* This check is superfluous, we've already + * checked otherwise we can't get here ... */ + e_import_complete (ei, target); + return; + } + + if (!g_file_get_contents (filename, &contents, NULL, NULL)) { + g_message (G_STRLOC ":Couldn't read file."); + g_free (filename); + e_import_complete (ei, target); + return; + } + + g_free (filename); + gci = g_malloc0 (sizeof (*gci)); + g_datalist_set_data (&target->data, "vcard-data", gci); + gci->import = g_object_ref (ei); + gci->target = target; + gci->encoding = encoding; + gci->contents = contents; + + source = g_datalist_get_data (&target->data, "vcard-source"); + + e_book_client_connect (source, NULL, book_client_connect_cb, gci); +} + +static void +vcard_cancel (EImport *ei, + EImportTarget *target, + EImportImporter *im) +{ + VCardImporter *gci = g_datalist_get_data (&target->data, "vcard-data"); + + if (gci) + gci->state = 1; +} + +static GtkWidget * +vcard_get_preview (EImport *ei, + EImportTarget *target, + EImportImporter *im) +{ + GtkWidget *preview; + GSList *contacts; + gchar *contents; + VCardEncoding encoding; + EImportTargetURI *s = (EImportTargetURI *) target; + gchar *filename; + + filename = g_filename_from_uri (s->uri_src, NULL, NULL); + if (filename == NULL) { + g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src); + return NULL; + } + + encoding = guess_vcard_encoding (filename); + if (encoding == VCARD_ENCODING_NONE) { + g_free (filename); + return NULL; + } + + if (!g_file_get_contents (filename, &contents, NULL, NULL)) { + g_message (G_STRLOC ": Couldn't read file."); + g_free (filename); + return NULL; + } + + g_free (filename); + + if (encoding == VCARD_ENCODING_UTF16) { + gchar *tmp; + + gunichar2 *contents_utf16 = (gunichar2 *) contents; + tmp = utf16_to_utf8 (contents_utf16); + g_free (contents); + contents = tmp; + } else if (encoding == VCARD_ENCODING_LOCALE) { + gchar *tmp; + tmp = g_locale_to_utf8 (contents, -1, NULL, NULL, NULL); + g_free (contents); + contents = tmp; + } + + contacts = eab_contact_list_from_string (contents); + g_free (contents); + + preview = evolution_contact_importer_get_preview_widget (contacts); + + g_slist_free_full (contacts, (GDestroyNotify) g_object_unref); + + return preview; +} + +static EImportImporter vcard_importer = { + E_IMPORT_TARGET_URI, + 0, + vcard_supported, + vcard_getwidget, + vcard_import, + vcard_cancel, + vcard_get_preview, +}; + +EImportImporter * +evolution_vcard_importer_peek (void) +{ + vcard_importer.name = _("vCard (.vcf, .gcrd)"); + vcard_importer.description = _("Evolution vCard Importer"); + + return &vcard_importer; +} + +/* utility functions shared between all contact importers */ +static void +preview_contact (EWebViewPreview *preview, + EContact *contact) +{ + gint idx; + gboolean had_value = FALSE; + + const gint fields[] = { + E_CONTACT_FILE_AS, + E_CONTACT_CATEGORIES, + + E_CONTACT_IS_LIST, + E_CONTACT_LIST_SHOW_ADDRESSES, + E_CONTACT_WANTS_HTML, + + E_CONTACT_FULL_NAME, + E_CONTACT_GIVEN_NAME, + E_CONTACT_FAMILY_NAME, + E_CONTACT_NICKNAME, + E_CONTACT_SPOUSE, + E_CONTACT_BIRTH_DATE, + E_CONTACT_ANNIVERSARY, + E_CONTACT_MAILER, + E_CONTACT_EMAIL, + + -1, + + E_CONTACT_ORG, + E_CONTACT_ORG_UNIT, + E_CONTACT_OFFICE, + E_CONTACT_TITLE, + E_CONTACT_ROLE, + E_CONTACT_MANAGER, + E_CONTACT_ASSISTANT, + + -1, + + E_CONTACT_PHONE_ASSISTANT, + E_CONTACT_PHONE_BUSINESS, + E_CONTACT_PHONE_BUSINESS_2, + E_CONTACT_PHONE_BUSINESS_FAX, + E_CONTACT_PHONE_CALLBACK, + E_CONTACT_PHONE_CAR, + E_CONTACT_PHONE_COMPANY, + E_CONTACT_PHONE_HOME, + E_CONTACT_PHONE_HOME_2, + E_CONTACT_PHONE_HOME_FAX, + E_CONTACT_PHONE_ISDN, + E_CONTACT_PHONE_MOBILE, + E_CONTACT_PHONE_OTHER, + E_CONTACT_PHONE_OTHER_FAX, + E_CONTACT_PHONE_PAGER, + E_CONTACT_PHONE_PRIMARY, + E_CONTACT_PHONE_RADIO, + E_CONTACT_PHONE_TELEX, + E_CONTACT_PHONE_TTYTDD, + + -1, + + E_CONTACT_ADDRESS_HOME, + E_CONTACT_ADDRESS_WORK, + E_CONTACT_ADDRESS_OTHER, + + -1, + + E_CONTACT_HOMEPAGE_URL, + E_CONTACT_BLOG_URL, + E_CONTACT_CALENDAR_URI, + E_CONTACT_FREEBUSY_URL, + E_CONTACT_ICS_CALENDAR, + E_CONTACT_VIDEO_URL, + + -1, + + E_CONTACT_IM_AIM, + E_CONTACT_IM_GROUPWISE, + E_CONTACT_IM_JABBER, + E_CONTACT_IM_YAHOO, + E_CONTACT_IM_MSN, + E_CONTACT_IM_ICQ, + E_CONTACT_IM_GADUGADU, + E_CONTACT_IM_SKYPE, + E_CONTACT_IM_TWITTER, + + -1, + + E_CONTACT_NOTE + }; + + g_return_if_fail (preview != NULL); + g_return_if_fail (contact != NULL); + + for (idx = 0; idx < G_N_ELEMENTS (fields); idx++) { + EContactField field; + + if (fields[idx] == -1) { + if (had_value) + e_web_view_preview_add_empty_line (preview); + had_value = FALSE; + continue; + } + + field = fields[idx]; + + if (field == E_CONTACT_BIRTH_DATE || field == E_CONTACT_ANNIVERSARY) { + EContactDate *dt = e_contact_get (contact, field); + if (dt) { + GDate gd = { 0 }; + struct tm tm; + gchar *value; + + g_date_set_dmy (&gd, dt->day, dt->month, dt->year); + g_date_to_struct_tm (&gd, &tm); + + value = e_datetime_format_format_tm ( + "addressbook", "table", + DTFormatKindDate, &tm); + if (value) { + e_web_view_preview_add_section ( + preview, + e_contact_pretty_name (field), + value); + had_value = TRUE; + } + + g_free (value); + e_contact_date_free (dt); + } + } else if (field == E_CONTACT_IS_LIST || + field == E_CONTACT_WANTS_HTML || + field == E_CONTACT_LIST_SHOW_ADDRESSES) { + if (e_contact_get (contact, field)) { + e_web_view_preview_add_text ( + preview, e_contact_pretty_name (field)); + had_value = TRUE; + } + } else if (field == E_CONTACT_ADDRESS_HOME || + field == E_CONTACT_ADDRESS_WORK || + field == E_CONTACT_ADDRESS_OTHER) { + EContactAddress *addr = e_contact_get (contact, field); + if (addr) { + gboolean have = FALSE; + + #define add_it(_what) \ + if (addr->_what && *addr->_what) { \ + e_web_view_preview_add_section ( \ + preview, have ? NULL : \ + e_contact_pretty_name (field), addr->_what); \ + have = TRUE; \ + had_value = TRUE; \ + } + + add_it (po); + add_it (ext); + add_it (street); + add_it (locality); + add_it (region); + add_it (code); + add_it (country); + + #undef add_it + + e_contact_address_free (addr); + } + } else if (field == E_CONTACT_IM_AIM || + field == E_CONTACT_IM_GROUPWISE || + field == E_CONTACT_IM_JABBER || + field == E_CONTACT_IM_YAHOO || + field == E_CONTACT_IM_MSN || + field == E_CONTACT_IM_ICQ || + field == E_CONTACT_IM_GADUGADU || + field == E_CONTACT_IM_SKYPE || + field == E_CONTACT_IM_TWITTER || + field == E_CONTACT_EMAIL) { + GList *attrs, *a; + gboolean have = FALSE; + const gchar *pretty_name; + + pretty_name = e_contact_pretty_name (field); + + attrs = e_contact_get_attributes (contact, field); + for (a = attrs; a; a = a->next) { + EVCardAttribute *attr = a->data; + GList *value; + + if (!attr) + continue; + + value = e_vcard_attribute_get_values (attr); + + while (value != NULL) { + const gchar *str = value->data; + + if (str && *str) { + e_web_view_preview_add_section ( + preview, have ? NULL : + pretty_name, str); + have = TRUE; + had_value = TRUE; + } + + value = value->next; + } + + e_vcard_attribute_free (attr); + } + + g_list_free (attrs); + + } else if (field == E_CONTACT_CATEGORIES) { + const gchar *pretty_name; + const gchar *value; + + pretty_name = e_contact_pretty_name (field); + value = e_contact_get_const (contact, field); + + if (value != NULL && *value != '\0') { + e_web_view_preview_add_section ( + preview, pretty_name, value); + had_value = TRUE; + } + + } else { + const gchar *pretty_name; + const gchar *value; + + pretty_name = e_contact_pretty_name (field); + value = e_contact_get_const (contact, field); + + if (value != NULL && *value != '\0') { + e_web_view_preview_add_section ( + preview, pretty_name, value); + had_value = TRUE; + } + } + } +} + +static void +preview_selection_changed_cb (GtkTreeSelection *selection, + EWebViewPreview *preview) +{ + GtkTreeIter iter; + GtkTreeModel *model = NULL; + + g_return_if_fail (selection != NULL); + g_return_if_fail (preview != NULL); + + e_web_view_preview_begin_update (preview); + + if (gtk_tree_selection_get_selected (selection, &model, &iter) && model) { + EContact *contact = NULL; + + gtk_tree_model_get (model, &iter, 1, &contact, -1); + + if (contact) { + preview_contact (preview, contact); + g_object_unref (contact); + } + } + + e_web_view_preview_end_update (preview); +} + +GtkWidget * +evolution_contact_importer_get_preview_widget (const GSList *contacts) +{ + GtkWidget *preview; + GtkTreeView *tree_view; + GtkTreeSelection *selection; + GtkListStore *store; + GtkTreeIter iter; + const GSList *c; + + if (!contacts) + return NULL; + + store = gtk_list_store_new (2, G_TYPE_STRING, E_TYPE_CONTACT); + + for (c = contacts; c; c = c->next) { + const gchar *description; + gchar *free_description = NULL; + EContact *contact = (EContact *) c->data; + + if (!contact || !E_IS_CONTACT (contact)) + continue; + + description = e_contact_get_const (contact, E_CONTACT_FILE_AS); + if (!description) + description = e_contact_get_const (contact, E_CONTACT_UID); + if (!description) + description = e_contact_get_const (contact, E_CONTACT_FULL_NAME); + if (!description) { + description = e_contact_get_const (contact, E_CONTACT_EMAIL_1); + if (description) { + const gchar *at = strchr (description, '@'); + if (at) { + free_description = g_strndup ( + description, + (gsize) (at - description)); + description = free_description; + } + } + } + + gtk_list_store_append (store, &iter); + gtk_list_store_set ( + store, &iter, + 0, description ? description : "", + 1, contact, + -1); + + g_free (free_description); + } + + if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) { + g_object_unref (store); + return NULL; + } + + preview = e_web_view_preview_new (); + gtk_widget_show (preview); + + tree_view = e_web_view_preview_get_tree_view (E_WEB_VIEW_PREVIEW (preview)); + g_return_val_if_fail (tree_view != NULL, NULL); + + gtk_tree_view_set_model (tree_view, GTK_TREE_MODEL (store)); + g_object_unref (store); + + gtk_tree_view_insert_column_with_attributes ( + tree_view, -1, _("Contact"), + gtk_cell_renderer_text_new (), "text", 0, NULL); + + if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) > 1) + e_web_view_preview_show_tree_view (E_WEB_VIEW_PREVIEW (preview)); + + selection = gtk_tree_view_get_selection (tree_view); + gtk_tree_selection_select_iter (selection, &iter); + g_signal_connect ( + selection, "changed", + G_CALLBACK (preview_selection_changed_cb), preview); + + preview_selection_changed_cb (selection, E_WEB_VIEW_PREVIEW (preview)); + + return preview; +} |