aboutsummaryrefslogtreecommitdiffstats
path: root/addressbook/importers/evolution-vcard-importer.c
diff options
context:
space:
mode:
Diffstat (limited to 'addressbook/importers/evolution-vcard-importer.c')
-rw-r--r--addressbook/importers/evolution-vcard-importer.c1016
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;
+}