[evolution] Bug #499320 - Preview before import from command line



commit ef7690c3845e3c1cebcf3caba7f7667a10e7123d
Author: Milan Crha <mcrha redhat com>
Date:   Tue May 18 22:47:29 2010 +0200

    Bug #499320 - Preview before import from command line

 addressbook/importers/Makefile.am                  |    2 +
 .../importers/evolution-addressbook-importers.h    |    3 +
 addressbook/importers/evolution-csv-importer.c     |   81 +++-
 addressbook/importers/evolution-ldif-importer.c    |   63 +++-
 addressbook/importers/evolution-vcard-importer.c   |  345 +++++++++++++
 calendar/importers/Makefile.am                     |    3 +
 calendar/importers/icalendar-importer.c            |  515 +++++++++++++++++++-
 e-util/e-import.c                                  |   26 +
 e-util/e-import.h                                  |    6 +
 mail/importers/Makefile.am                         |    1 +
 mail/importers/elm-importer.c                      |    1 +
 mail/importers/evolution-mbox-importer.c           |  150 ++++++
 mail/importers/mail-importer.h                     |    7 +
 mail/importers/pine-importer.c                     |    1 +
 modules/mail/e-mail-shell-backend.c                |   33 ++
 widgets/misc/Makefile.am                           |    2 +
 widgets/misc/e-import-assistant.c                  |  105 ++++-
 widgets/misc/e-web-view-preview.c                  |  481 ++++++++++++++++++
 widgets/misc/e-web-view-preview.h                  |  101 ++++
 19 files changed, 1901 insertions(+), 25 deletions(-)
---
diff --git a/addressbook/importers/Makefile.am b/addressbook/importers/Makefile.am
index 7ccc4c7..742f46a 100644
--- a/addressbook/importers/Makefile.am
+++ b/addressbook/importers/Makefile.am
@@ -7,6 +7,7 @@ libevolution_addressbook_importers_la_CPPFLAGS =	\
 	-DG_LOG_DOMAIN=\"Evolution-Importer\"		\
 	-I$(top_srcdir)					\
 	-I$(top_srcdir)/addressbook			\
+	-I$(top_srcdir)/widgets				\
 	-I$(top_builddir)/addressbook			\
 	$(EVOLUTION_ADDRESSBOOK_CFLAGS)
 
@@ -21,6 +22,7 @@ libevolution_addressbook_importers_la_LDFLAGS = $(NO_UNDEFINED)
 libevolution_addressbook_importers_la_LIBADD = \
 	$(top_builddir)/e-util/libeutil.la 				\
 	$(top_builddir)/addressbook/util/libeabutil.la			\
+	$(top_builddir)/widgets/misc/libemiscwidgets.la			\
 	$(IMPORTERS_LIBS)
 
 -include $(top_srcdir)/git.mk
diff --git a/addressbook/importers/evolution-addressbook-importers.h b/addressbook/importers/evolution-addressbook-importers.h
index 747fe42..adaa629 100644
--- a/addressbook/importers/evolution-addressbook-importers.h
+++ b/addressbook/importers/evolution-addressbook-importers.h
@@ -23,3 +23,6 @@ struct _EImportImporter *evolution_vcard_importer_peek(void);
 struct _EImportImporter *evolution_csv_outlook_importer_peek(void);
 struct _EImportImporter *evolution_csv_mozilla_importer_peek(void);
 struct _EImportImporter *evolution_csv_evolution_importer_peek(void);
+
+/* private utility function for importers only */
+struct _GtkWidget *evolution_contact_importer_get_preview_widget (const GList *contacts);
diff --git a/addressbook/importers/evolution-csv-importer.c b/addressbook/importers/evolution-csv-importer.c
index 6a12d70..9ad939d 100644
--- a/addressbook/importers/evolution-csv-importer.c
+++ b/addressbook/importers/evolution-csv-importer.c
@@ -762,17 +762,26 @@ csv_import (EImport *ei, EImportTarget *target, EImportImporter *im)
 {
 	CSVImporter *gci;
 	EBook *book;
+	gchar *filename;
 	FILE *file;
 	EImportTargetURI *s = (EImportTargetURI *) target;
 
+	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;
+	}
+
 	book = e_book_new(g_datalist_get_data(&target->data, "csv-source"), NULL);
 	if (book == NULL) {
 		g_message("Couldn't Create EBook");
 		e_import_complete(ei, target);
+		g_free (filename);
 		return;
 	}
 
-	file = g_fopen (g_filename_from_uri(s->uri_src, NULL, NULL), "r");
+	file = g_fopen (filename, "r");
+	g_free (filename);
 	if (file == NULL) {
 		g_message("Can't open .csv file");
 		e_import_complete(ei, target);
@@ -825,6 +834,73 @@ csv_cancel(EImport *ei, EImportTarget *target, EImportImporter *im) {
 		gci->state = 1;
 }
 
+static GtkWidget *
+csv_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	GtkWidget *preview;
+	GList *contacts = NULL;
+	EContact *contact;
+	EImportTargetURI *s = (EImportTargetURI *)target;
+	gchar *filename;
+	FILE *file;
+	CSVImporter *gci;
+
+	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;
+	}
+
+	file = g_fopen (filename, "r");
+	g_free (filename);
+	if (file == NULL) {
+		g_message (G_STRLOC ": Can't open .csv file");
+		return NULL;
+	}
+
+	gci = g_malloc0 (sizeof (*gci));
+	gci->file = file;
+	gci->count = 0;
+	fseek(file, 0, SEEK_END);
+	gci->size = ftell (file);
+	fseek (file, 0, SEEK_SET);
+
+	while (contact = getNextCSVEntry (gci, gci->file), contact != NULL) {
+		contacts = g_list_prepend (contacts, contact);
+	}
+
+	contacts = g_list_reverse (contacts);
+	preview = evolution_contact_importer_get_preview_widget (contacts);
+
+	g_list_foreach (contacts, (GFunc) g_object_unref, NULL);
+	g_list_free (contacts);
+	fclose (file);
+	g_free (gci);
+
+	return preview;
+}
+
+static GtkWidget *
+outlook_csv_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	importer = OUTLOOK_IMPORTER;
+	return csv_get_preview (ei, target, im);
+}
+
+static GtkWidget *
+mozilla_csv_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	importer = MOZILLA_IMPORTER;
+	return csv_get_preview (ei, target, im);
+}
+
+static GtkWidget *
+evolution_csv_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	importer = EVOLUTION_IMPORTER;
+	return csv_get_preview (ei, target, im);
+}
+
 static EImportImporter csv_outlook_importer = {
 	E_IMPORT_TARGET_URI,
 	0,
@@ -832,6 +908,7 @@ static EImportImporter csv_outlook_importer = {
 	csv_getwidget,
 	outlook_csv_import,
 	csv_cancel,
+	outlook_csv_get_preview,
 };
 
 static EImportImporter csv_mozilla_importer = {
@@ -841,6 +918,7 @@ static EImportImporter csv_mozilla_importer = {
 	csv_getwidget,
 	mozilla_csv_import,
 	csv_cancel,
+	mozilla_csv_get_preview,
 };
 
 static EImportImporter csv_evolution_importer = {
@@ -850,6 +928,7 @@ static EImportImporter csv_evolution_importer = {
 	csv_getwidget,
 	evolution_csv_import,
 	csv_cancel,
+	evolution_csv_get_preview,
 };
 
 EImportImporter *
diff --git a/addressbook/importers/evolution-ldif-importer.c b/addressbook/importers/evolution-ldif-importer.c
index 41dda05..52ce90e 100644
--- a/addressbook/importers/evolution-ldif-importer.c
+++ b/addressbook/importers/evolution-ldif-importer.c
@@ -221,7 +221,7 @@ populate_contact_address (EContactAddress *address, gchar *attr, gchar *value)
 }
 
 static gboolean
-parseLine (LDIFImporter *gci, EContact *contact,
+parseLine (GHashTable *dn_contact_hash, EContact *contact,
 	   EContactAddress *work_address, EContactAddress *home_address,
 	   gchar **buf)
 {
@@ -307,7 +307,7 @@ parseLine (LDIFImporter *gci, EContact *contact,
 		if (!field_handled) {
 			if (!g_ascii_strcasecmp (ptr, "dn"))
 				g_hash_table_insert (
-					gci->dn_contact_hash,
+					dn_contact_hash,
 					g_strdup (ldif_value->str), contact);
 			else if (!g_ascii_strcasecmp (ptr, "objectclass") &&
 				!g_ascii_strcasecmp (ldif_value->str, "groupofnames")) {
@@ -342,7 +342,7 @@ parseLine (LDIFImporter *gci, EContact *contact,
 }
 
 static EContact *
-getNextLDIFEntry(LDIFImporter *gci, FILE *f )
+getNextLDIFEntry(GHashTable *dn_contact_hash, FILE *f )
 {
 	EContact *contact;
 	EContactAddress *work_address, *home_address;
@@ -372,7 +372,7 @@ getNextLDIFEntry(LDIFImporter *gci, FILE *f )
 
 	buf = str->str;
 	while (buf) {
-		if (!parseLine (gci, contact, work_address, home_address, &buf)) {
+		if (!parseLine (dn_contact_hash, contact, work_address, home_address, &buf)) {
 			/* parsing error */
 			g_string_free (str, TRUE);
 			e_contact_address_free (work_address);
@@ -480,7 +480,7 @@ ldif_import_contacts(gpointer d)
 	   ones till the end */
 
 	if (gci->state == 0) {
-		while (count < 50 && (contact = getNextLDIFEntry(gci, gci->file))) {
+		while (count < 50 && (contact = getNextLDIFEntry(gci->dn_contact_hash, gci->file))) {
 			if (e_contact_get (contact, E_CONTACT_IS_LIST)) {
 				gci->list_contacts = g_slist_prepend(gci->list_contacts, contact);
 			} else {
@@ -674,6 +674,58 @@ ldif_cancel(EImport *ei, EImportTarget *target, EImportImporter *im)
 		gci->state = 2;
 }
 
+static GtkWidget *
+ldif_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	GtkWidget *preview;
+	GList *contacts = NULL;
+	EContact *contact;
+	EImportTargetURI *s = (EImportTargetURI *)target;
+	gchar *filename;
+	GHashTable *dn_contact_hash;
+	FILE *file;
+
+	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;
+	}
+
+	file = g_fopen(filename, "r");
+	g_free (filename);
+
+	if (file == NULL) {
+		g_message (G_STRLOC ": Can't open .ldif file");
+		return NULL;
+	}
+
+	dn_contact_hash = g_hash_table_new_full (
+		g_str_hash, g_str_equal,
+		(GDestroyNotify) g_free,
+		(GDestroyNotify) NULL);
+
+	while (contact = getNextLDIFEntry (dn_contact_hash, file), contact != NULL) {
+		if (!e_contact_get (contact, E_CONTACT_IS_LIST)) {
+			add_to_notes(contact, E_CONTACT_OFFICE);
+			add_to_notes(contact, E_CONTACT_SPOUSE);
+			add_to_notes(contact, E_CONTACT_BLOG_URL);
+		}
+
+		contacts = g_list_prepend (contacts, contact);
+	}
+
+	g_hash_table_destroy (dn_contact_hash);
+
+	contacts = g_list_reverse (contacts);
+	preview = evolution_contact_importer_get_preview_widget (contacts);
+
+	g_list_foreach (contacts, (GFunc) g_object_unref, NULL);
+	g_list_free (contacts);
+	fclose (file);
+
+	return preview;
+}
+
 static EImportImporter ldif_importer = {
 	E_IMPORT_TARGET_URI,
 	0,
@@ -681,6 +733,7 @@ static EImportImporter ldif_importer = {
 	ldif_getwidget,
 	ldif_import,
 	ldif_cancel,
+	ldif_get_preview,
 };
 
 EImportImporter *
diff --git a/addressbook/importers/evolution-vcard-importer.c b/addressbook/importers/evolution-vcard-importer.c
index 080ef94..49e7bef 100644
--- a/addressbook/importers/evolution-vcard-importer.c
+++ b/addressbook/importers/evolution-vcard-importer.c
@@ -42,6 +42,8 @@
 #include <libebook/e-destination.h>
 
 #include "e-util/e-import.h"
+#include "e-util/e-datetime-format.h"
+#include "misc/e-web-view-preview.h"
 
 #include "evolution-addressbook-importers.h"
 
@@ -550,6 +552,61 @@ vcard_cancel(EImport *ei, EImportTarget *target, EImportImporter *im)
 		gci->state = 1;
 }
 
+static GtkWidget *
+vcard_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	GtkWidget *preview;
+	GList *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_list_foreach (contacts, (GFunc) g_object_unref, NULL);
+	g_list_free (contacts);
+
+	return preview;
+}
+
 static EImportImporter vcard_importer = {
 	E_IMPORT_TARGET_URI,
 	0,
@@ -557,6 +614,7 @@ static EImportImporter vcard_importer = {
 	vcard_getwidget,
 	vcard_import,
 	vcard_cancel,
+	vcard_get_preview,
 };
 
 EImportImporter *
@@ -567,3 +625,290 @@ evolution_vcard_importer_peek(void)
 
 	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,
+
+		-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_EMAIL) {
+			GList *attrs, *a;
+			gboolean have = FALSE;
+
+			attrs = e_contact_get_attributes (contact, field);
+			for (a = attrs; a; a = a->next) {
+				EVCardAttribute *attr = a->data;
+				GList *value;
+
+				if (!attr)
+					continue;
+
+				for (value = e_vcard_attribute_get_values (attr); value; value = value->next) {
+					const gchar *str = value->data;
+
+					if (str && *str) {
+						e_web_view_preview_add_section (preview, have ? NULL : e_contact_pretty_name (field), str);
+						have = TRUE;
+						had_value = TRUE;
+					}
+				}
+				e_vcard_attribute_free (attr);
+			}
+
+			g_list_free (attrs);
+		} else if (field == E_CONTACT_CATEGORIES) {
+			gchar *value = e_contact_get (contact, field);
+
+			if (value && *value) {
+				e_web_view_preview_add_section (preview, e_contact_pretty_name (field), value);
+				had_value = TRUE;
+			}
+
+			g_free (value);
+		} else {
+			const gchar *value = e_contact_get_const (contact, field);
+
+			if (value && *value) {
+				e_web_view_preview_add_section (preview, e_contact_pretty_name (field), 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 GList *contacts)
+{
+	GtkWidget *preview;
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+	GtkListStore *store;
+	GtkTreeIter iter;
+	const GList *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;
+		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);
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter,
+			0, description ? description : "",
+			1, contact,
+			-1 );
+	}
+
+	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;
+}
diff --git a/calendar/importers/Makefile.am b/calendar/importers/Makefile.am
index a5274b6..b54e619 100644
--- a/calendar/importers/Makefile.am
+++ b/calendar/importers/Makefile.am
@@ -6,6 +6,7 @@ libevolution_calendar_importers_la_CPPFLAGS = 		\
 	-DG_LOG_DOMAIN=\"Evolution-Importer\"		\
 	-I$(top_srcdir)					\
 	-I$(top_srcdir)/calendar			\
+	-I$(top_srcdir)/widgets				\
 	-I$(top_builddir)/calendar			\
 	$(GNOME_PLATFORM_CFLAGS)			\
 	$(EVOLUTION_CALENDAR_CFLAGS)
@@ -19,6 +20,8 @@ libevolution_calendar_importers_la_LDFLAGS = $(NO_UNDEFINED)
 libevolution_calendar_importers_la_LIBADD = \
 	$(top_builddir)/e-util/libeutil.la 				\
 	$(top_builddir)/calendar/common/libevolution-calendarprivate.la	\
+	$(top_builddir)/shell/libeshell.la				\
+	$(top_builddir)/widgets/misc/libemiscwidgets.la			\
 	$(EVOLUTION_CALENDAR_LIBS)					\
 	$(GNOME_PLATFORM_LIBS)
 
diff --git a/calendar/importers/icalendar-importer.c b/calendar/importers/icalendar-importer.c
index 1f7da5e..f719c9c 100644
--- a/calendar/importers/icalendar-importer.c
+++ b/calendar/importers/icalendar-importer.c
@@ -36,13 +36,18 @@
 #include <gtk/gtk.h>
 
 #include <libecal/e-cal.h>
+#include <libecal/e-cal-time-util.h>
 #include <libedataserverui/e-source-selector.h>
 #include <libical/icalvcal.h>
 #include "evolution-calendar-importer.h"
+#include "shell/e-shell.h"
 #include "common/authentication.h"
+#include "gui/calendar-config-keys.h"
 
 #include "e-util/e-import.h"
 #include "e-util/e-util-private.h"
+#include "e-util/e-datetime-format.h"
+#include "misc/e-web-view-preview.h"
 
 /* We timeout after 2 minutes, when opening the folders. */
 #define IMPORTER_TIMEOUT_SECONDS 120
@@ -81,6 +86,16 @@ static const gchar *import_type_strings[] = {
  * Functions shared by iCalendar & vCalendar importer.
  */
 
+static GtkWidget *ical_get_preview (icalcomponent *icalcomp);
+
+static gboolean
+is_icalcomp_usable (icalcomponent *icalcomp)
+{
+	return icalcomp && icalcomponent_is_valid (icalcomp) && (
+		icalcomponent_get_first_component (icalcomp, ICAL_VEVENT_COMPONENT) != NULL ||
+		icalcomponent_get_first_component (icalcomp, ICAL_VTODO_COMPONENT) != NULL);
+}
+
 static void
 ivcal_import_done(ICalImporter *ici)
 {
@@ -379,13 +394,14 @@ ical_supported(EImport *ei, EImportTarget *target, EImportImporter *im)
 		return FALSE;
 
 	if (g_file_get_contents (filename, &contents, NULL, NULL)) {
-		icalcomponent *icalcomp;
+		icalcomponent *icalcomp = NULL;
 
-		icalcomp = e_cal_util_parse_ics_string (contents);
+		if (g_ascii_strncasecmp (contents, "BEGIN:", 6) == 0)
+			icalcomp = e_cal_util_parse_ics_string (contents);
 		g_free (contents);
 
 		if (icalcomp) {
-			if (icalcomponent_is_valid (icalcomp))
+			if (is_icalcomp_usable (icalcomp))
 				ret = TRUE;
 			else
 				ret = FALSE;
@@ -427,6 +443,40 @@ ical_import(EImport *ei, EImportTarget *target, EImportImporter *im)
 		e_import_complete(ei, target);
 }
 
+static GtkWidget *
+ivcal_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	GtkWidget *preview;
+	EImportTargetURI *s = (EImportTargetURI *)target;
+	gchar *filename;
+	icalcomponent *icalcomp;
+	gchar *contents;
+
+	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;
+	}
+
+	if (!g_file_get_contents (filename, &contents, NULL, NULL)) {
+		g_free (filename);
+		return NULL;
+	}
+	g_free (filename);
+
+	icalcomp = e_cal_util_parse_ics_string (contents);
+	g_free (contents);
+
+	if (!icalcomp)
+		return NULL;
+
+	preview = ical_get_preview (icalcomp);
+
+	icalcomponent_free (icalcomp);
+
+	return preview;
+}
+
 static EImportImporter ical_importer = {
 	E_IMPORT_TARGET_URI,
 	0,
@@ -434,6 +484,7 @@ static EImportImporter ical_importer = {
 	ivcal_getwidget,
 	ical_import,
 	ivcal_cancel,
+	ivcal_get_preview,
 };
 
 EImportImporter *
@@ -480,7 +531,7 @@ vcal_supported(EImport *ei, EImportTarget *target, EImportImporter *im)
 
 		icalcomp = e_cal_util_parse_ics_string (contents);
 
-		if (icalcomp && icalcomponent_is_valid (icalcomp)) {
+		if (icalcomp && is_icalcomp_usable (icalcomp)) {
 			/* If we can create proper iCalendar from the file, then
 			   rather use ics importer, because it knows to read more
 			   information than older version, the vCalendar. */
@@ -568,6 +619,33 @@ vcal_import(EImport *ei, EImportTarget *target, EImportImporter *im)
 		e_import_complete(ei, target);
 }
 
+static GtkWidget *
+vcal_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	GtkWidget *preview;
+	EImportTargetURI *s = (EImportTargetURI *)target;
+	gchar *filename;
+	icalcomponent *icalcomp;
+
+	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;
+	}
+
+	icalcomp = load_vcalendar_file (filename);
+	g_free (filename);
+
+	if (!icalcomp)
+		return NULL;
+
+	preview = ical_get_preview (icalcomp);
+
+	icalcomponent_free (icalcomp);
+
+	return preview;
+}
+
 static EImportImporter vcal_importer = {
 	E_IMPORT_TARGET_URI,
 	0,
@@ -575,6 +653,7 @@ static EImportImporter vcal_importer = {
 	ivcal_getwidget,
 	vcal_import,
 	ivcal_cancel,
+	vcal_get_preview,
 };
 
 EImportImporter *
@@ -772,6 +851,7 @@ static EImportImporter gnome_calendar_importer = {
 	gnome_calendar_getwidget,
 	gnome_calendar_import,
 	gnome_calendar_cancel,
+	NULL, /* get_preview */
 };
 
 EImportImporter *
@@ -782,3 +862,430 @@ gnome_calendar_importer_peek(void)
 
 	return &gnome_calendar_importer;
 }
+
+/* ********************************************************************** */
+
+static gchar *
+format_dt (const ECalComponentDateTime *dt, GHashTable *timezones, icaltimezone *users_zone)
+{
+	struct tm tm;
+
+	g_return_val_if_fail (dt != NULL, NULL);
+	g_return_val_if_fail (timezones != NULL, NULL);
+
+	if (!dt->value)
+		return NULL;
+
+	dt->value->zone = NULL;
+	if (dt->tzid) {
+		dt->value->zone = g_hash_table_lookup (timezones, dt->tzid);
+		if (!dt->value->zone)
+			dt->value->zone = icaltimezone_get_builtin_timezone_from_tzid (dt->tzid);
+	}
+
+	if (dt->value->zone)
+		tm = icaltimetype_to_tm_with_zone (dt->value, (icaltimezone *) dt->value->zone, users_zone);
+	else
+		tm = icaltimetype_to_tm (dt->value);
+
+	return e_datetime_format_format_tm ("calendar", "table", dt->value->is_date ? DTFormatKindDate : DTFormatKindDateTime, &tm);
+}
+
+static const gchar *
+strip_mailto (const gchar *str)
+{
+	if (!str || g_ascii_strncasecmp (str, "mailto:";, 7) != 0)
+		return str;
+
+	return str + 7;
+}
+
+static void
+preview_comp (EWebViewPreview *preview, ECalComponent *comp)
+{
+	ECalComponentText text = { 0 };
+	ECalComponentDateTime dt;
+	ECalComponentClassification classif;
+	const gchar *str;
+	gchar *tmp;
+	gint percent;
+	gboolean have;
+	GHashTable *timezones;
+	icaltimezone *users_zone;
+	GSList *slist, *l;
+
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (comp != NULL);
+
+	timezones = g_object_get_data (G_OBJECT (preview), "iCalImp-timezones");
+	users_zone = g_object_get_data (G_OBJECT (preview), "iCalImp-userszone");
+
+	str = NULL;
+	switch (e_cal_component_get_vtype (comp)) {
+	case E_CAL_COMPONENT_EVENT:
+		str = e_cal_component_has_attendees (comp) ? C_("iCalImp", "Meeting") : C_("iCalImp", "Event");
+		break;
+	case E_CAL_COMPONENT_TODO:
+		str = C_("iCalImp", "Task");
+		break;
+	case E_CAL_COMPONENT_JOURNAL:
+		str = C_("iCalImp", "Memo");
+		break;
+	default:
+		str = "??? Other ???";
+		break;
+	}
+
+	have = FALSE;
+	if (e_cal_component_has_recurrences (comp)) {
+		e_web_view_preview_add_section (preview, have ? NULL : str, C_("iCalImp", "has recurrences"));
+		have = TRUE;
+	}
+
+	if (e_cal_component_is_instance (comp)) {
+		e_web_view_preview_add_section (preview, have ? NULL : str, C_("iCalImp", "is an instance"));
+		have = TRUE;
+	}
+
+	if (e_cal_component_has_alarms (comp)) {
+		e_web_view_preview_add_section (preview, have ? NULL : str, C_("iCalImp", "has alarms"));
+		have = TRUE;
+	}
+
+	if (e_cal_component_has_attachments (comp)) {
+		e_web_view_preview_add_section (preview, have ? NULL : str, C_("iCalImp", "has attachments"));
+		have = TRUE;
+	}
+
+	if (!have) {
+		e_web_view_preview_add_section (preview, str, "");
+	}
+
+	str = NULL;
+	classif = E_CAL_COMPONENT_CLASS_NONE;
+	e_cal_component_get_classification (comp, &classif);
+	if (classif == E_CAL_COMPONENT_CLASS_PUBLIC) {
+		/* Translators: Appointment's classification */
+		str = C_("iCalImp", "Public");
+	} else if (classif == E_CAL_COMPONENT_CLASS_PRIVATE) {
+		/* Translators: Appointment's classification */
+		str = C_("iCalImp", "Private");
+	} else if (classif == E_CAL_COMPONENT_CLASS_CONFIDENTIAL) {
+		/* Translators: Appointment's classification */
+		str = C_("iCalImp", "Confidential");
+	}
+	if (str)
+		/* Translators: Appointment's classification section name */
+		e_web_view_preview_add_section (preview, C_("iCalImp", "Classification"), str);
+
+	e_cal_component_get_summary (comp, &text);
+	if ((text.value && *text.value) || (text.altrep && *text.altrep))
+		/* Translators: Appointment's summary */
+		e_web_view_preview_add_section (preview, C_("iCalImp", "Summary"), (text.value && *text.value) ? text.value : text.altrep);
+
+	str = NULL;
+	e_cal_component_get_location (comp, &str);
+	if (str && *str)
+		/* Translators: Appointment's location */
+		e_web_view_preview_add_section (preview, C_("iCalImp", "Location"), str);
+
+	dt.value = NULL;
+	e_cal_component_get_dtstart (comp, &dt);
+	if (dt.value) {
+		tmp = format_dt (&dt, timezones, users_zone);
+		if (tmp)
+			/* Translators: Appointment's start time */
+			e_web_view_preview_add_section (preview, C_("iCalImp", "Start"), tmp);
+		g_free (tmp);
+	}
+	e_cal_component_free_datetime (&dt);
+
+	dt.value = NULL;
+	e_cal_component_get_due (comp, &dt);
+	if (dt.value) {
+		tmp = format_dt (&dt, timezones, users_zone);
+		if (tmp)
+			/* Translators: 'Due' like the time due a task should be finished */
+			e_web_view_preview_add_section (preview, C_("iCalImp", "Due"), tmp);
+		g_free (tmp);
+	} else {
+		e_cal_component_free_datetime (&dt);
+
+		dt.value = NULL;
+		e_cal_component_get_dtend (comp, &dt);
+		if (dt.value) {
+			tmp = format_dt (&dt, timezones, users_zone);
+
+			if (tmp)
+				/* Translators: Appointment's end time */
+				e_web_view_preview_add_section (preview, C_("iCalImp", "End"), tmp);
+			g_free (tmp);
+		}
+	}
+	e_cal_component_free_datetime (&dt);
+
+	str = NULL;
+	e_cal_component_get_categories (comp, &str);
+	if (str && *str)
+		/* Translators: Appointment's categories */
+		e_web_view_preview_add_section (preview, C_("iCalImp", "Categories"), str);
+
+	percent = e_cal_component_get_percent_as_int (comp);
+	if (percent >= 0) {
+		tmp = NULL;
+		if (percent == 100) {
+			icaltimetype *completed = NULL;
+
+			e_cal_component_get_completed (comp, &completed);
+
+			if (completed) {
+				dt.tzid = "UTC";
+				dt.value = completed;
+
+				tmp = format_dt (&dt, timezones, users_zone);
+
+				e_cal_component_free_icaltimetype (completed);
+			}
+		}
+
+		if (!tmp)
+			tmp = g_strdup_printf ("%d%%", percent);
+
+		/* Translators: Appointment's complete value (either percentage, or a date/time of a completion) */
+		e_web_view_preview_add_section (preview, C_("iCalImp", "Completed"), tmp);
+		g_free (tmp);
+	}
+
+	str = NULL;
+	e_cal_component_get_url (comp, &str);
+	if (str && *str)
+		/* Translators: Appointment's URL */
+		e_web_view_preview_add_section (preview, C_("iCalImp", "URL"), str);
+
+	if (e_cal_component_has_organizer (comp)) {
+		ECalComponentOrganizer organizer = { 0 };
+
+		e_cal_component_get_organizer (comp, &organizer);
+
+		if (organizer.value && *organizer.value) {
+			if (organizer.cn && *organizer.cn) {
+				tmp = g_strconcat (organizer.cn, " <", strip_mailto (organizer.value), ">", NULL);
+				/* Translators: Appointment's organizer */
+				e_web_view_preview_add_section (preview, C_("iCalImp", "Organizer"), tmp);
+				g_free (tmp);
+			} else {
+				e_web_view_preview_add_section (preview, C_("iCalImp", "Organizer"), strip_mailto (organizer.value));
+			}
+		}
+	}
+
+	if (e_cal_component_has_attendees (comp)) {
+		GSList *attendees = NULL, *a;
+		have = FALSE;
+
+		e_cal_component_get_attendee_list (comp, &attendees);
+
+		for (a = attendees; a; a = a->next) {
+			ECalComponentAttendee *attnd = a->data;
+
+			if (!attnd || !attnd->value || !*attnd->value)
+				continue;
+
+			if (attnd->cn && *attnd->cn) {
+				tmp = g_strconcat (attnd->cn, " <", strip_mailto (attnd->value), ">", NULL);
+				/* Translators: Appointment's attendees */
+				e_web_view_preview_add_section (preview, have ? NULL : C_("iCalImp", "Attendees"), tmp);
+				g_free (tmp);
+			} else {
+				e_web_view_preview_add_section (preview, have ? NULL : C_("iCalImp", "Attendees"), strip_mailto (attnd->value));
+			}
+
+			have = TRUE;
+		}
+
+		e_cal_component_free_attendee_list (attendees);
+	}
+
+	slist = NULL;
+	e_cal_component_get_description_list (comp, &slist);
+	for (l = slist; l; l = l->next) {
+		ECalComponentText *txt = l->data;
+
+		e_web_view_preview_add_section (preview, l != slist ? NULL : C_("iCalImp", "Description"), (txt && txt->value) ? txt->value : "");
+	}
+
+	e_cal_component_free_text_list (slist);
+}
+
+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) {
+		ECalComponent *comp = NULL;
+
+		gtk_tree_model_get (model, &iter, 3, &comp, -1);
+
+		if (comp) {
+			preview_comp (preview, comp);
+			g_object_unref (comp);
+		}
+	}
+
+	e_web_view_preview_end_update (preview);
+}
+
+static icaltimezone *
+get_users_timezone (void)
+{
+	/* more or less copy&paste of calendar_config_get_icaltimezone */
+	icaltimezone *zone = NULL;
+	gchar *location;
+
+	if (e_shell_settings_get_boolean (e_shell_get_shell_settings (e_shell_get_default ()), "cal-use-system-timezone")) {
+		location = e_cal_util_get_system_timezone_location ();
+	} else {
+		GConfClient *client = gconf_client_get_default ();
+
+		location = gconf_client_get_string (client, CALENDAR_CONFIG_TIMEZONE, NULL);
+
+		g_object_unref (client);
+	}
+
+	if (location) {
+		zone = icaltimezone_get_builtin_timezone (location);
+
+		g_free (location);
+	}
+
+	return zone;
+}
+
+static void
+free_zone_cb (gpointer ptr)
+{
+	icaltimezone *zone = ptr;
+
+	if (zone)
+		icaltimezone_free (zone, 1);
+}
+
+static GtkWidget *
+ical_get_preview (icalcomponent *icalcomp)
+{
+	GtkWidget *preview;
+	GtkTreeView *tree_view;
+	GtkTreeSelection *selection;
+	GtkListStore *store;
+	GtkTreeIter iter;
+	GHashTable *timezones;
+	icalcomponent *subcomp;
+	icaltimezone *users_zone;
+
+	if (!icalcomp || !is_icalcomp_usable (icalcomp))
+		return NULL;
+
+	store = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, E_TYPE_CAL_COMPONENT);
+
+	timezones = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, free_zone_cb);
+	users_zone = get_users_timezone ();
+
+	/* get timezones first */
+	for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_VTIMEZONE_COMPONENT);
+	     subcomp;
+	     subcomp = icalcomponent_get_next_component (icalcomp,  ICAL_VTIMEZONE_COMPONENT)) {
+		icaltimezone *zone = icaltimezone_new ();
+		if (!icaltimezone_set_component (zone, icalcomponent_new_clone (subcomp)) || !icaltimezone_get_tzid (zone)) {
+			icaltimezone_free (zone, 1);
+		} else {
+			g_hash_table_insert (timezones, (gchar *) icaltimezone_get_tzid (zone), zone);
+		}
+	}
+
+	/* then each component */
+	for (subcomp = icalcomponent_get_first_component (icalcomp, ICAL_ANY_COMPONENT);
+	     subcomp;
+	     subcomp = icalcomponent_get_next_component (icalcomp,  ICAL_ANY_COMPONENT)) {
+		icalcomponent_kind kind = icalcomponent_isa (subcomp);
+
+		if (kind == ICAL_VEVENT_COMPONENT ||
+		    kind == ICAL_VTODO_COMPONENT ||
+		    kind == ICAL_VJOURNAL_COMPONENT) {
+			ECalComponent *comp = e_cal_component_new ();
+			ECalComponentText summary = { 0 };
+			ECalComponentDateTime dt = { 0 };
+			gchar *formatted_dt;
+
+			if (!e_cal_component_set_icalcomponent (comp, icalcomponent_new_clone (subcomp))) {
+				g_object_unref (comp);
+				continue;
+			}
+
+			e_cal_component_get_summary (comp, &summary);
+			e_cal_component_get_dtstart (comp, &dt);
+			formatted_dt = format_dt (&dt, timezones, users_zone);
+
+			gtk_list_store_append (store, &iter);
+			gtk_list_store_set (store, &iter,
+				0, kind == ICAL_VEVENT_COMPONENT ? (e_cal_component_has_attendees (comp) ? C_("iCalImp", "Meeting") : C_("iCalImp", "Event")) :
+				   kind == ICAL_VTODO_COMPONENT ? C_("iCalImp", "Task") :
+				   kind == ICAL_VJOURNAL_COMPONENT ? C_("iCalImp", "Memo") : "??? Other ???",
+				1, formatted_dt ? formatted_dt : "",
+				2, summary.value && *summary.value ? summary.value : summary.altrep && *summary.altrep ? summary.altrep : "",
+				3, comp,
+				-1);
+
+			g_free (formatted_dt);
+			e_cal_component_free_datetime (&dt);
+			g_object_unref (comp);
+		}
+	}
+
+	if (!gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter)) {
+		g_object_unref (store);
+		g_hash_table_destroy (timezones);
+		return NULL;
+	}
+
+	preview = e_web_view_preview_new ();
+	gtk_widget_show (preview);
+
+	g_object_set_data_full (G_OBJECT (preview), "iCalImp-timezones", timezones, (GDestroyNotify) g_hash_table_destroy);
+	g_object_set_data (G_OBJECT (preview), "iCalImp-userszone", users_zone);
+
+	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);
+
+	/* Translators: Column header for a component type; it can be Event, Task or Memo */
+	gtk_tree_view_insert_column_with_attributes (tree_view, -1, C_("iCalImp", "Type"),
+		gtk_cell_renderer_text_new (), "text", 0, NULL);
+
+	/* Translators: Column header for a component start date/time */
+	gtk_tree_view_insert_column_with_attributes (tree_view, -1, C_("iCalImp", "Start"),
+		gtk_cell_renderer_text_new (), "text", 1, NULL);
+
+	/* Translators: Column header for a component summary */
+	gtk_tree_view_insert_column_with_attributes (tree_view, -1, C_("iCalImp", "Summary"),
+		gtk_cell_renderer_text_new (), "text", 2, 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;
+}
diff --git a/e-util/e-import.c b/e-util/e-import.c
index 4475344..1448563 100644
--- a/e-util/e-import.c
+++ b/e-util/e-import.c
@@ -216,6 +216,32 @@ e_import_get_widget (EImport *import,
 }
 
 /**
+ * e_import_get_preview_widget:
+ * @import: an #EImport
+ * @target: Target of interest
+ * @im: Importer to get a preview widget of
+ *
+ * Gets a widget that the importer uses to preview data to be
+ * imported.  This widget should be packed into a container
+ * widget.  It should not be shown_all.
+ *
+ * Return value: NULL if the importer doesn't support preview.
+ **/
+GtkWidget *
+e_import_get_preview_widget (EImport *import,
+                     EImportTarget *target,
+                     EImportImporter *im)
+{
+	g_return_val_if_fail (im != NULL, NULL);
+	g_return_val_if_fail (target != NULL, NULL);
+
+	if (!im->get_preview)
+		return NULL;
+
+	return im->get_preview (import, target, im);
+}
+
+/**
  * e_import_complete:
  * @import: an #EImport
  * @target: Target just completed (unused currently)
diff --git a/e-util/e-import.h b/e-util/e-import.h
index affc364..57bb794 100644
--- a/e-util/e-import.h
+++ b/e-util/e-import.h
@@ -78,6 +78,8 @@ enum _e_import_target_t {
  * @supported: Callback to see if this target is supported by the importer.
  * @get_widget: A widget factory for this importer, if it needs any extra information in the assistant.  It will update the target.
  * @import: Run the import.
+ * @cancel: Cancel the import.
+ * @get_preview: Callback to create a preview widget for just importing data.
  * @user_data: User data for the callbacks;
  *
  * Base importer description.
@@ -91,6 +93,7 @@ struct _EImportImporter {
 	EImportWidgetFunc get_widget;
 	EImportImportFunc import;
 	EImportImportFunc cancel;
+	EImportWidgetFunc get_preview;
 
 	gpointer user_data;
 
@@ -199,6 +202,9 @@ void		e_import_cancel			(EImport *import,
 GtkWidget *	e_import_get_widget		(EImport *import,
 						 EImportTarget *target,
 						 EImportImporter *importer);
+GtkWidget *	e_import_get_preview_widget	(EImport *import,
+						 EImportTarget *target,
+						 EImportImporter *im);
 void		e_import_status			(EImport *import,
 						 EImportTarget *target,
 						 const gchar *what,
diff --git a/mail/importers/Makefile.am b/mail/importers/Makefile.am
index 65e099b..4592de8 100644
--- a/mail/importers/Makefile.am
+++ b/mail/importers/Makefile.am
@@ -27,6 +27,7 @@ libevolution_mail_importers_la_LIBADD =				\
 	$(top_builddir)/filter/libfilter.la			\
 	$(top_builddir)/mail/libevolution-mail.la		\
 	$(top_builddir)/shell/libeshell.la			\
+	$(top_builddir)/widgets/misc/libemiscwidgets.la		\
 	$(GNOME_PLATFORM_LIBS)					\
 	$(IMPORTERS_LIBS)
 
diff --git a/mail/importers/elm-importer.c b/mail/importers/elm-importer.c
index c9ddcf7..937bfa5 100644
--- a/mail/importers/elm-importer.c
+++ b/mail/importers/elm-importer.c
@@ -357,6 +357,7 @@ static EImportImporter elm_importer = {
 	elm_getwidget,
 	elm_import,
 	elm_cancel,
+	NULL, /* get_preview */
 };
 
 EImportImporter *
diff --git a/mail/importers/evolution-mbox-importer.c b/mail/importers/evolution-mbox-importer.c
index 4fa4bf9..dc419dc 100644
--- a/mail/importers/evolution-mbox-importer.c
+++ b/mail/importers/evolution-mbox-importer.c
@@ -33,6 +33,7 @@
 #include <stdio.h>
 #include <ctype.h>
 #include <string.h>
+#include <errno.h>
 
 #include <gtk/gtk.h>
 #include <glib/gi18n.h>
@@ -53,6 +54,7 @@
 #include "mail-importer.h"
 
 #include "e-util/e-import.h"
+#include "misc/e-web-view-preview.h"
 
 typedef struct {
 	EImport *import;
@@ -247,6 +249,153 @@ mbox_cancel(EImport *ei, EImportTarget *target, EImportImporter *im)
 		camel_operation_cancel(importer->cancel);
 }
 
+static MboxImporterCreatePreviewFunc create_preview_func = NULL;
+static MboxImporterFillPreviewFunc fill_preview_func = NULL;
+
+void
+mbox_importer_set_preview_funcs (MboxImporterCreatePreviewFunc create_func, MboxImporterFillPreviewFunc fill_func)
+{
+	create_preview_func = create_func;
+	fill_preview_func = fill_func;
+}
+
+static void
+preview_selection_changed_cb (GtkTreeSelection *selection, EWebViewPreview *preview)
+{
+	GtkTreeIter iter;
+	GtkTreeModel *model = NULL;
+	gboolean found = FALSE;
+
+	g_return_if_fail (selection != NULL);
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (fill_preview_func != NULL);
+
+	if (gtk_tree_selection_get_selected (selection, &model, &iter) && model) {
+		CamelMimeMessage *msg = NULL;
+
+		gtk_tree_model_get (model, &iter, 2, &msg, -1);
+
+		if (msg) {
+			found = TRUE;
+			fill_preview_func (G_OBJECT (preview), msg);
+			g_object_unref (msg);
+		}
+	}
+
+	if (!found) {
+		e_web_view_preview_begin_update (preview);
+		e_web_view_preview_end_update (preview);
+	}
+}
+
+static GtkWidget *
+mbox_get_preview (EImport *ei, EImportTarget *target, EImportImporter *im)
+{
+	GtkWidget *preview = NULL;
+	EImportTargetURI *s = (EImportTargetURI *)target;
+	gchar *filename;
+	gint fd;
+	CamelMimeParser *mp;
+	GtkListStore *store = NULL;
+	GtkTreeIter iter;
+	GtkWidget *preview_widget = NULL;
+
+	if (!create_preview_func || !fill_preview_func)
+		return NULL;
+
+	filename = g_filename_from_uri (s->uri_src, NULL, NULL);
+	if (!filename) {
+		g_message (G_STRLOC ": Couldn't get filename from URI '%s'", s->uri_src);
+		return NULL;
+	}
+
+	fd = g_open (filename, O_RDONLY|O_BINARY, 0);
+	if (fd == -1) {
+		g_warning ("Cannot find source file to import '%s': %s", filename, g_strerror (errno));
+		g_free (filename);
+		return NULL;
+	}
+
+	g_free (filename);
+
+	mp = camel_mime_parser_new();
+	camel_mime_parser_scan_from (mp, TRUE);
+	if (camel_mime_parser_init_with_fd (mp, fd) == -1) {
+		g_object_unref (mp);
+		return NULL;
+	}
+
+	while (camel_mime_parser_step (mp, NULL, NULL) == CAMEL_MIME_PARSER_STATE_FROM) {
+		CamelMimeMessage *msg;
+		gchar *from;
+
+		msg = camel_mime_message_new();
+		if (camel_mime_part_construct_from_parser ((CamelMimePart *)msg, mp) == -1) {
+			g_object_unref (msg);
+			break;
+		}
+
+		if (!store)
+			store = gtk_list_store_new (3, G_TYPE_STRING, G_TYPE_STRING, CAMEL_TYPE_MIME_MESSAGE);
+
+		from = NULL;
+		if (camel_mime_message_get_from (msg))
+			from = camel_address_encode (CAMEL_ADDRESS (camel_mime_message_get_from (msg)));
+
+		gtk_list_store_append (store, &iter);
+		gtk_list_store_set (store, &iter,
+			0, camel_mime_message_get_subject (msg) ? camel_mime_message_get_subject (msg) : "",
+			1, from ? from : "",
+			2, msg,
+			-1);
+
+		g_object_unref (msg);
+		g_free (from);
+
+		camel_mime_parser_step (mp, NULL, NULL);
+	}
+
+	if (store) {
+		GtkTreeView *tree_view;
+		GtkTreeSelection *selection;
+
+		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);
+
+		/* Translators: Column header for a message subject */
+		gtk_tree_view_insert_column_with_attributes (tree_view, -1, C_("mboxImp", "Subject"),
+			gtk_cell_renderer_text_new (), "text", 0, NULL);
+
+		/* Translators: Column header for a message From address */
+		gtk_tree_view_insert_column_with_attributes (tree_view, -1, C_("mboxImp", "From"),
+			gtk_cell_renderer_text_new (), "text", 1, 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));
+
+		create_preview_func (G_OBJECT (preview), &preview_widget);
+		g_return_val_if_fail (preview_widget != NULL, NULL);
+
+		e_web_view_preview_set_preview (E_WEB_VIEW_PREVIEW (preview), preview_widget);
+		gtk_widget_show (preview_widget);
+
+		selection = gtk_tree_view_get_selection (tree_view);
+		g_return_val_if_fail (gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter), NULL);
+		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;
+}
+
 static EImportImporter mbox_importer = {
 	E_IMPORT_TARGET_URI,
 	0,
@@ -254,6 +403,7 @@ static EImportImporter mbox_importer = {
 	mbox_getwidget,
 	mbox_import,
 	mbox_cancel,
+	mbox_get_preview,
 };
 
 EImportImporter *
diff --git a/mail/importers/mail-importer.h b/mail/importers/mail-importer.h
index 5f2f8c8..15de575 100644
--- a/mail/importers/mail-importer.h
+++ b/mail/importers/mail-importer.h
@@ -29,6 +29,13 @@
 
 EImportImporter *mbox_importer_peek(void);
 
+typedef void (*MboxImporterCreatePreviewFunc)(GObject *preview, GtkWidget **preview_widget);
+typedef void (*MboxImporterFillPreviewFunc)(GObject *preview, CamelMimeMessage *msg);
+
+/* 'create_func' is a function to create a view. 'fill_func' is to fill view with a preview of a message 'msg'
+   (mail importer cannot link to em-format-html-display directly) */
+void mbox_importer_set_preview_funcs (MboxImporterCreatePreviewFunc create_func, MboxImporterFillPreviewFunc fill_func);
+
 EImportImporter *elm_importer_peek(void);
 EImportImporter *pine_importer_peek(void);
 
diff --git a/mail/importers/pine-importer.c b/mail/importers/pine-importer.c
index 07b7f22..ed3b62e 100644
--- a/mail/importers/pine-importer.c
+++ b/mail/importers/pine-importer.c
@@ -410,6 +410,7 @@ static EImportImporter pine_importer = {
 	pine_getwidget,
 	pine_import,
 	pine_cancel,
+	NULL, /* get_preview */
 };
 
 EImportImporter *
diff --git a/modules/mail/e-mail-shell-backend.c b/modules/mail/e-mail-shell-backend.c
index 105b552..5c16a34 100644
--- a/modules/mail/e-mail-shell-backend.c
+++ b/modules/mail/e-mail-shell-backend.c
@@ -31,6 +31,7 @@
 #include "shell/e-shell-window.h"
 #include "composer/e-msg-composer.h"
 #include "widgets/misc/e-preferences-window.h"
+#include "widgets/misc/e-web-view.h"
 
 #include "e-mail-shell-settings.h"
 #include "e-mail-shell-sidebar.h"
@@ -70,6 +71,9 @@ struct _EMailShellBackendPrivate {
 static gpointer parent_class;
 static GType mail_shell_backend_type;
 
+static void mbox_create_preview_cb (GObject *preview, GtkWidget **preview_widget);
+static void mbox_fill_preview_cb (GObject *preview, CamelMimeMessage *msg);
+
 static void
 mail_shell_backend_init_importers (void)
 {
@@ -80,6 +84,7 @@ mail_shell_backend_init_importers (void)
 
 	importer = mbox_importer_peek ();
 	e_import_class_add_importer (import_class, importer, NULL, NULL);
+	mbox_importer_set_preview_funcs (mbox_create_preview_cb, mbox_fill_preview_cb);
 
 	importer = elm_importer_peek ();
 	e_import_class_add_importer (import_class, importer, NULL, NULL);
@@ -770,3 +775,31 @@ e_mail_labels_get_filter_options (void)
 
 	return g_slist_reverse (list);
 }
+
+/* utility functions for mbox importer */
+static void
+mbox_create_preview_cb (GObject *preview, GtkWidget **preview_widget)
+{
+	EMFormatHTMLDisplay *format;
+
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (preview_widget != NULL);
+
+	format = em_format_html_display_new ();
+	g_object_set_data_full (preview, "mbox-imp-formatter", format, g_object_unref);
+	*preview_widget = GTK_WIDGET (EM_FORMAT_HTML (format)->html);
+}
+
+static void
+mbox_fill_preview_cb (GObject *preview, CamelMimeMessage *msg)
+{
+	EMFormatHTMLDisplay *format;
+
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (msg != NULL);
+
+	format = g_object_get_data (preview, "mbox-imp-formatter");
+	g_return_if_fail (format != NULL);
+
+	em_format_format (EM_FORMAT (format), NULL, NULL, msg);
+}
diff --git a/widgets/misc/Makefile.am b/widgets/misc/Makefile.am
index 9a33f48..6373ceb 100644
--- a/widgets/misc/Makefile.am
+++ b/widgets/misc/Makefile.am
@@ -71,6 +71,7 @@ widgetsinclude_HEADERS =			\
 	e-signature-tree-view.h			\
 	e-url-entry.h				\
 	e-web-view.h				\
+	e-web-view-preview.h			\
 	ea-calendar-cell.h			\
 	ea-calendar-item.h			\
 	ea-cell-table.h				\
@@ -147,6 +148,7 @@ libemiscwidgets_la_SOURCES =			\
 	e-signature-tree-view.c			\
 	e-url-entry.c				\
 	e-web-view.c				\
+	e-web-view-preview.c			\
 	ea-calendar-cell.c			\
 	ea-calendar-item.c			\
 	ea-cell-table.c				\
diff --git a/widgets/misc/e-import-assistant.c b/widgets/misc/e-import-assistant.c
index 30d4138..3ca06dc 100644
--- a/widgets/misc/e-import-assistant.c
+++ b/widgets/misc/e-import-assistant.c
@@ -71,8 +71,12 @@ struct _ImportProgressPage {
 };
 
 struct _ImportSimplePage {
+	GtkWidget *actionlabel;
+	GtkWidget *filetypetable;
 	GtkWidget *filetype;
-	GtkWidget *control; /* importer's destination widget in an alignment */
+	GtkWidget *control; /* importer's destination or preview widget in an alignment */
+	gboolean has_preview; /* TRUE when 'control' holds a preview widget,
+				   otherwise holds destination widget */
 
 	EImportTargetURI *target;
 	EImportImporter *importer;
@@ -427,7 +431,6 @@ import_assistant_simple_page_init (EImportAssistant *import_assistant)
 	GtkWidget *widget;
 	GtkCellRenderer *cell;
 	GtkListStore *store;
-	const gchar *text;
 	gint row = 0;
 
 	page = gtk_vbox_new (FALSE, 6);
@@ -436,12 +439,11 @@ import_assistant_simple_page_init (EImportAssistant *import_assistant)
 
 	container = page;
 
-	text = _("Select what type of file you want to import from the list.");
-
-	widget = gtk_label_new (text);
+	widget = gtk_label_new ("");
 	gtk_label_set_line_wrap (GTK_LABEL (widget), TRUE);
 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
 	gtk_widget_show (widget);
+	import_assistant->priv->simple_page.actionlabel = widget;
 
 	widget = gtk_table_new (2, 1, FALSE);
 	gtk_table_set_row_spacings (GTK_TABLE (widget), 2);
@@ -449,6 +451,7 @@ import_assistant_simple_page_init (EImportAssistant *import_assistant)
 	gtk_container_set_border_width (GTK_CONTAINER (widget), 8);
 	gtk_box_pack_start (GTK_BOX (container), widget, FALSE, TRUE, 0);
 	gtk_widget_show (widget);
+	import_assistant->priv->simple_page.filetypetable = widget;
 
 	container = widget;
 
@@ -743,7 +746,7 @@ prepare_progress_page (GtkAssistant *assistant,
 
 	g_object_get (G_OBJECT (assistant), "is-simple", &is_simple, NULL);
 
-	intelligent_import = gtk_toggle_button_get_active (
+	intelligent_import = is_simple ? FALSE : gtk_toggle_button_get_active (
 		GTK_TOGGLE_BUTTON (priv->type_page.intelligent));
 
 	if (is_simple) {
@@ -797,9 +800,15 @@ simple_filetype_changed_cb (GtkComboBox *combo_box, GtkAssistant *assistant)
 
 	if (page->control)
 		gtk_widget_destroy (page->control);
+	page->has_preview = FALSE;
+
+	control = e_import_get_preview_widget (priv->import, (EImportTarget *) page->target, page->importer);
+	if (control) {
+		page->has_preview = TRUE;
+		gtk_widget_set_size_request (control, 320, 240);
+	} else
+		control = create_importer_control (priv->import, (EImportTarget *)page->target, page->importer);
 
-	control = create_importer_control (
-		priv->import, (EImportTarget *)page->target, page->importer);
 	page->control = gtk_alignment_new (0.0, 0.0, 1.0, 1.0);
 	gtk_widget_show (page->control);
 	gtk_container_add (GTK_CONTAINER (page->control), control);
@@ -847,8 +856,6 @@ prepare_simple_page (GtkAssistant *assistant, GtkWidget *vbox)
 			-1);
 	}
 
-	g_slist_free (importers);
-
 	gtk_combo_box_set_active (GTK_COMBO_BOX (page->filetype), 0);
 	g_object_set_data (G_OBJECT (page->filetype), "page-vbox", vbox);
 
@@ -857,6 +864,56 @@ prepare_simple_page (GtkAssistant *assistant, GtkWidget *vbox)
 	g_signal_connect (
 		page->filetype, "changed",
 		G_CALLBACK (simple_filetype_changed_cb), assistant);
+
+	if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL (store), NULL) == 1) {
+		gchar *title;
+
+		/* only one importer found, make it even simpler */
+		gtk_label_set_text (GTK_LABEL (page->actionlabel),
+			page->has_preview ?
+				_("Preview data to be imported") :
+				_("Choose the destination for this import"));
+
+		gtk_widget_hide (page->filetypetable);
+
+		title = g_strconcat (_("Import Data"), " - ", ((EImportImporter *)importers->data)->name, NULL);
+		gtk_assistant_set_page_title (assistant, vbox, title);
+		g_free (title);
+	} else {
+		/* multiple importers found, be able to choose from them */
+		gtk_label_set_text (GTK_LABEL (page->actionlabel), _("Select what type of file you want to import from the list."));
+
+		gtk_widget_show (page->filetypetable);
+
+		gtk_assistant_set_page_title (assistant, vbox, _("Import Data"));
+	}
+
+	g_slist_free (importers);
+}
+
+static gboolean
+prepare_simple_destination_page (GtkAssistant *assistant,
+                          GtkWidget *vbox)
+{
+	EImportAssistantPrivate *priv;
+	ImportDestinationPage *page;
+	ImportSimplePage *simple_page;
+
+	priv = E_IMPORT_ASSISTANT_GET_PRIVATE (assistant);
+	page = &priv->destination_page;
+	simple_page = &priv->simple_page;
+
+	if (page->control)
+		gtk_container_remove (GTK_CONTAINER (vbox), page->control);
+
+	page->control = create_importer_control (
+		priv->import, (EImportTarget *)
+		simple_page->target, simple_page->importer);
+
+	gtk_box_pack_start (GTK_BOX (vbox), page->control, TRUE, TRUE, 0);
+	gtk_assistant_set_page_complete (assistant, vbox, TRUE);
+
+	return FALSE;
 }
 
 static gint
@@ -864,6 +921,16 @@ forward_cb (gint current_page,
             EImportAssistant *import_assistant)
 {
 	GtkToggleButton *toggle_button;
+	gboolean is_simple = FALSE;
+
+	g_object_get (G_OBJECT (import_assistant), "is-simple", &is_simple, NULL);
+
+	if (is_simple) {
+		if (!import_assistant->priv->simple_page.has_preview)
+			current_page++;
+
+		return current_page + 1;
+	}
 
 	toggle_button = GTK_TOGGLE_BUTTON (
 		import_assistant->priv->type_page.intelligent);
@@ -1076,6 +1143,8 @@ import_assistant_prepare (GtkAssistant *assistant,
 		if (page_no == 0) {
 			prepare_simple_page (assistant, page);
 		} else if (page_no == 1) {
+			prepare_simple_destination_page (assistant, page);
+		} else if (page_no == 2) {
 			prepare_progress_page (assistant, page);
 		}
 
@@ -1193,6 +1262,14 @@ import_assistant_construct (EImportAssistant *import_assistant)
 
 		gtk_assistant_append_page (assistant, page);
 		gtk_assistant_set_page_header_image (assistant, page, pixbuf);
+		gtk_assistant_set_page_title (assistant, page, _("Import Data"));
+		gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_CONTENT);
+
+		/* File destination page - when with preview*/
+		page = import_assistant_destination_page_init (import_assistant);
+
+		gtk_assistant_append_page (assistant, page);
+		gtk_assistant_set_page_header_image (assistant, page, pixbuf);
 		gtk_assistant_set_page_title (assistant, page, _("Import Location"));
 		gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_CONTENT);
 	} else {
@@ -1272,11 +1349,9 @@ import_assistant_construct (EImportAssistant *import_assistant)
 	gtk_assistant_set_page_type (assistant, page, GTK_ASSISTANT_PAGE_PROGRESS);
 	gtk_assistant_set_page_complete (assistant, page, TRUE);
 
-	if (!import_assistant->priv->is_simple) {
-		gtk_assistant_set_forward_page_func (
-			assistant, (GtkAssistantPageFunc)
-			forward_cb, import_assistant, NULL);
-	}
+	gtk_assistant_set_forward_page_func (
+		assistant, (GtkAssistantPageFunc)
+		forward_cb, import_assistant, NULL);
 
 	g_object_unref (pixbuf);
 
diff --git a/widgets/misc/e-web-view-preview.c b/widgets/misc/e-web-view-preview.c
new file mode 100644
index 0000000..e268779
--- /dev/null
+++ b/widgets/misc/e-web-view-preview.c
@@ -0,0 +1,481 @@
+/*
+ * e-web-view-preview.c
+ *
+ * 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/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+#include "e-web-view-preview.h"
+
+#include <config.h>
+#include <string.h>
+#include <glib/gi18n-lib.h>
+
+#define E_WEB_VIEW_PREVIEW_GET_PRIVATE(obj) \
+	(G_TYPE_INSTANCE_GET_PRIVATE \
+	((obj), E_TYPE_WEB_VIEW_PREVIEW, EWebViewPreviewPrivate))
+
+struct _EWebViewPreviewPrivate {
+	gboolean escape_values;
+	GString *updating_content; /* is NULL when not between begin_update/end_update */
+};
+
+enum {
+	PROP_0,
+	PROP_TREE_VIEW,
+	PROP_PREVIEW_WIDGET,
+	PROP_ESCAPE_VALUES
+};
+
+G_DEFINE_TYPE (EWebViewPreview, e_web_view_preview, GTK_TYPE_VPANED);
+
+static void
+web_view_preview_set_property (GObject *object,
+                       guint property_id,
+                       const GValue *value,
+                       GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_ESCAPE_VALUES:
+			e_web_view_preview_set_escape_values (
+				E_WEB_VIEW_PREVIEW (object),
+				g_value_get_boolean (value));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+web_view_preview_get_property (GObject *object,
+                       guint property_id,
+                       GValue *value,
+                       GParamSpec *pspec)
+{
+	switch (property_id) {
+		case PROP_TREE_VIEW:
+			g_value_set_object (
+				value, e_web_view_preview_get_tree_view (
+				E_WEB_VIEW_PREVIEW (object)));
+			return;
+
+		case PROP_PREVIEW_WIDGET:
+			g_value_set_object (
+				value, e_web_view_preview_get_preview (
+				E_WEB_VIEW_PREVIEW (object)));
+			return;
+
+		case PROP_ESCAPE_VALUES:
+			g_value_set_boolean (
+				value, e_web_view_preview_get_escape_values (
+				E_WEB_VIEW_PREVIEW (object)));
+			return;
+	}
+
+	G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
+}
+
+static void
+web_view_preview_dispose (GObject *object)
+{
+	EWebViewPreviewPrivate *priv;
+
+	priv = E_WEB_VIEW_PREVIEW_GET_PRIVATE (object);
+
+	if (priv->updating_content != NULL) {
+		g_string_free (priv->updating_content, TRUE);
+		priv->updating_content = NULL;
+	}
+
+	/* Chain up to parent's dispose() method. */
+	G_OBJECT_CLASS (e_web_view_preview_parent_class)->dispose (object);
+}
+
+static void
+e_web_view_preview_class_init (EWebViewPreviewClass *klass)
+{
+	GObjectClass *object_class;
+
+	g_type_class_add_private (klass, sizeof (EWebViewPreviewPrivate));
+
+	object_class = G_OBJECT_CLASS (klass);
+	object_class->set_property = web_view_preview_set_property;
+	object_class->get_property = web_view_preview_get_property;
+	object_class->dispose = web_view_preview_dispose;
+
+	g_object_class_install_property (
+		object_class,
+		PROP_TREE_VIEW,
+		g_param_spec_object (
+			"tree-view",
+			"Tree View",
+			NULL,
+			GTK_TYPE_TREE_VIEW,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_PREVIEW_WIDGET,
+		g_param_spec_object (
+			"preview-widget",
+			"Preview Widget",
+			NULL,
+			GTK_TYPE_WIDGET,
+			G_PARAM_READABLE));
+
+	g_object_class_install_property (
+		object_class,
+		PROP_ESCAPE_VALUES,
+		g_param_spec_boolean (
+			"escape-values",
+			"Whether escaping values automatically, when inserting",
+			NULL,
+			TRUE,
+			G_PARAM_READWRITE));
+}
+
+static GtkWidget *
+in_scrolled_window (GtkWidget *widget)
+{
+	GtkWidget *sw;
+
+	g_return_val_if_fail (widget != NULL, NULL);
+
+	sw = gtk_scrolled_window_new (NULL, NULL);
+	gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+	gtk_container_add (GTK_CONTAINER (sw), widget);
+
+	gtk_widget_show (widget);
+	gtk_widget_show (sw);
+
+	return sw;
+}
+
+static void
+e_web_view_preview_init (EWebViewPreview *preview)
+{
+	GtkWidget *tree_view_sw, *web_view_sw;
+	EWebViewPreviewPrivate *priv;
+
+	priv = E_WEB_VIEW_PREVIEW_GET_PRIVATE (preview);
+	preview->priv = priv;
+	priv->escape_values = TRUE;
+
+	tree_view_sw = in_scrolled_window (gtk_tree_view_new ());
+	web_view_sw = in_scrolled_window (e_web_view_new ());
+
+	gtk_widget_hide (tree_view_sw);
+	gtk_widget_show (web_view_sw);
+
+	gtk_paned_pack1 (GTK_PANED (preview), tree_view_sw, FALSE, TRUE);
+	gtk_paned_pack2 (GTK_PANED (preview), web_view_sw, TRUE, TRUE);
+
+	/* rawly 3 lines of a text plus a little bit more */
+	if (gtk_paned_get_position (GTK_PANED (preview)) < 85)
+		gtk_paned_set_position (GTK_PANED (preview), 85);
+}
+
+GtkWidget *
+e_web_view_preview_new (void)
+{
+	return g_object_new (E_TYPE_WEB_VIEW_PREVIEW, NULL);
+}
+
+GtkTreeView *
+e_web_view_preview_get_tree_view (EWebViewPreview *preview)
+{
+	g_return_val_if_fail (preview != NULL, NULL);
+	g_return_val_if_fail (E_IS_WEB_VIEW_PREVIEW (preview), NULL);
+
+	return GTK_TREE_VIEW (gtk_bin_get_child (GTK_BIN (gtk_paned_get_child1 (GTK_PANED (preview)))));
+}
+
+GtkWidget *
+e_web_view_preview_get_preview	(EWebViewPreview *preview)
+{
+	g_return_val_if_fail (preview != NULL, NULL);
+	g_return_val_if_fail (E_IS_WEB_VIEW_PREVIEW (preview), NULL);
+
+	return gtk_bin_get_child (GTK_BIN (gtk_paned_get_child2 (GTK_PANED (preview))));
+}
+
+void
+e_web_view_preview_set_preview	(EWebViewPreview *preview, GtkWidget *preview_widget)
+{
+	GtkWidget *old_child;
+
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview_widget != NULL);
+	g_return_if_fail (GTK_IS_WIDGET (preview_widget));
+
+	old_child = gtk_bin_get_child (GTK_BIN (gtk_paned_get_child2 (GTK_PANED (preview))));
+	if (old_child) {
+		g_return_if_fail (old_child != preview_widget);
+		gtk_widget_destroy (old_child);
+	}
+
+	gtk_container_add (GTK_CONTAINER (gtk_paned_get_child2 (GTK_PANED (preview))), preview_widget);
+}
+
+void
+e_web_view_preview_show_tree_view (EWebViewPreview *preview)
+{
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+
+	gtk_widget_show (gtk_paned_get_child1 (GTK_PANED (preview)));
+}
+
+void
+e_web_view_preview_hide_tree_view(EWebViewPreview *preview)
+{
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+
+	gtk_widget_hide (gtk_paned_get_child1 (GTK_PANED (preview)));
+}
+
+void
+e_web_view_preview_set_escape_values (EWebViewPreview *preview, gboolean escape)
+{
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+
+	preview->priv->escape_values = escape;
+}
+
+gboolean
+e_web_view_preview_get_escape_values (EWebViewPreview *preview)
+{
+	g_return_val_if_fail (preview != NULL, FALSE);
+	g_return_val_if_fail (E_IS_WEB_VIEW_PREVIEW (preview), FALSE);
+	g_return_val_if_fail (preview->priv != NULL, FALSE);
+
+	return preview->priv->escape_values;
+}
+
+void
+e_web_view_preview_begin_update (EWebViewPreview *preview)
+{
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+
+	if (preview->priv->updating_content) {
+		g_warning ("%s: Previous content update isn't finished with e_web_view_preview_end_update()", G_STRFUNC);
+		g_string_free (preview->priv->updating_content, TRUE);
+	}
+
+	preview->priv->updating_content = g_string_new ("<TABLE width=\"100%\" border=\"0\" cols=\"2\">");
+}
+
+void
+e_web_view_preview_end_update (EWebViewPreview *preview)
+{
+	GtkWidget *web_view;
+
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+	g_return_if_fail (preview->priv->updating_content != NULL);
+
+	g_string_append (preview->priv->updating_content, "</TABLE>");
+
+	web_view = e_web_view_preview_get_preview (preview);
+	if (E_IS_WEB_VIEW (web_view))
+		e_web_view_load_string (E_WEB_VIEW (web_view), preview->priv->updating_content->str);
+
+	g_string_free (preview->priv->updating_content, TRUE);
+	preview->priv->updating_content = NULL;
+}
+
+static gchar *
+replace_string (const gchar *text, const gchar *find, const gchar *replace)
+{
+	const gchar *p, *next;
+	GString *str;
+	gint find_len;
+
+	g_return_val_if_fail (text != NULL, NULL);
+	g_return_val_if_fail (find != NULL, NULL);
+	g_return_val_if_fail (*find, NULL);
+
+	find_len = strlen (find);
+	str = g_string_new ("");
+
+	p = text;
+	while (next = strstr (p, find), next) {
+		if (p + 1 < next)
+			g_string_append_len (str, p, next - p);
+
+		if (replace && *replace)
+			g_string_append (str, replace);
+
+		p = next + find_len;
+	}
+
+	g_string_append (str, p);
+
+	return g_string_free (str, FALSE);
+}
+
+static gchar *
+web_view_preview_escape_text (EWebViewPreview *preview, const gchar *text)
+{
+	gchar *utf8_valid, *res, *end;
+
+	if (!e_web_view_preview_get_escape_values (preview))
+		return NULL;
+
+	g_return_val_if_fail (text != NULL, NULL);
+
+	if (g_utf8_validate (text, -1, NULL)) {
+		res = g_markup_escape_text (text, -1);
+	} else {
+		utf8_valid = g_strdup (text);
+		while (end = NULL, !g_utf8_validate (utf8_valid, -1, (const gchar **) &end) && end && *end)
+			*end = '?';
+
+		res = g_markup_escape_text (utf8_valid, -1);
+
+		g_free (utf8_valid);
+	}
+
+	if (res && strchr (res, '\n')) {
+		/* replace line breaks with <BR> */
+		if (strchr (res, '\r')) {
+			end = replace_string (res, "\r", "");
+			g_free (res);
+			res = end;
+		}
+
+		end = replace_string (res, "\n", "<BR>");
+		g_free (res);
+		res = end;
+	}
+
+	return res;
+}
+
+void
+e_web_view_preview_add_header (EWebViewPreview *preview, gint index, const gchar *header)
+{
+	gchar *escaped;
+
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+	g_return_if_fail (preview->priv->updating_content != NULL);
+	g_return_if_fail (header != NULL);
+
+	if (index < 1)
+		index = 1;
+	else if (index > 6)
+		index = 6;
+
+	escaped = web_view_preview_escape_text (preview, header);
+	if (escaped)
+		header = escaped;
+
+	g_string_append_printf (preview->priv->updating_content, "<TR><TD colspan=2><H%d>%s</H%d></TD></TR>", index, header, index);
+
+	g_free (escaped);
+}
+
+void
+e_web_view_preview_add_text (EWebViewPreview *preview, const gchar *text)
+{
+	gchar *escaped;
+
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+	g_return_if_fail (preview->priv->updating_content != NULL);
+	g_return_if_fail (text != NULL);
+
+	escaped = web_view_preview_escape_text (preview, text);
+	if (escaped)
+		text = escaped;
+
+	g_string_append_printf (preview->priv->updating_content, "<TR><TD colspan=2><FONT size=\"3\">%s</FONT></TD></TR>", text);
+
+	g_free (escaped);
+}
+
+void
+e_web_view_preview_add_raw_html (EWebViewPreview *preview, const gchar *raw_html)
+{
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+	g_return_if_fail (preview->priv->updating_content != NULL);
+	g_return_if_fail (raw_html != NULL);
+
+	g_string_append_printf (preview->priv->updating_content, "<TR><TD colspan=2>%s</TD></TR>", raw_html);
+}
+
+void
+e_web_view_preview_add_separator (EWebViewPreview *preview)
+{
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+	g_return_if_fail (preview->priv->updating_content != NULL);
+
+	g_string_append (preview->priv->updating_content, "<TR><TD colspan=2><HR></TD></TR>");
+}
+
+void
+e_web_view_preview_add_empty_line (EWebViewPreview *preview)
+{
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+	g_return_if_fail (preview->priv->updating_content != NULL);
+
+	g_string_append (preview->priv->updating_content, "<TR><TD colspan=2>&nbsp;</TD></TR>");
+}
+
+/* section can be NULL, but value cannot */
+void
+e_web_view_preview_add_section (EWebViewPreview *preview, const gchar *section, const gchar *value)
+{
+	gchar *escaped_section = NULL, *escaped_value;
+
+	g_return_if_fail (preview != NULL);
+	g_return_if_fail (E_IS_WEB_VIEW_PREVIEW (preview));
+	g_return_if_fail (preview->priv != NULL);
+	g_return_if_fail (preview->priv->updating_content != NULL);
+	g_return_if_fail (value != NULL);
+
+	if (section) {
+		escaped_section = web_view_preview_escape_text (preview, section);
+		if (escaped_section)
+			section = escaped_section;
+	}
+
+	escaped_value = web_view_preview_escape_text (preview, value);
+	if (escaped_value)
+		value = escaped_value;
+
+	g_string_append_printf (preview->priv->updating_content, "<TR><TD width=\"10%%\" valign=\"top\" nowrap><FONT size=\"3\"><B>%s</B></FONT></TD><TD width=\"90%%\"><FONT size=\"3\">%s</FONT></TD></TR>", section ? section : "", value);
+
+	g_free (escaped_section);
+	g_free (escaped_value);
+}
diff --git a/widgets/misc/e-web-view-preview.h b/widgets/misc/e-web-view-preview.h
new file mode 100644
index 0000000..319f67e
--- /dev/null
+++ b/widgets/misc/e-web-view-preview.h
@@ -0,0 +1,101 @@
+/*
+ * e-web-view-preview.h
+ *
+ * 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/>
+ *
+ *
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
+ *
+ */
+
+/* This is intended to serve as a common widget for previews before import.
+ * It contains a GtkTreeView at the top and an EWebView at the bottom.
+ * The tree view is not shown initially, it should be forced with
+ * e_web_view_preview_show_tree_view().
+ *
+ * The internal default EWebView can be accessed by e_web_view_preview_get_preview()
+ * and it should be updated for each change of the selected item in the tree
+ * view, when it's shown.
+ *
+ * Updating an EWebView content through helper functions of an EWebViewPreview
+ * begins with call of e_web_view_preview_begin_update(), which starts an empty
+ * page construction, which is finished by e_web_view_preview_end_update(),
+ * and the content of the EWebView is updated.
+ */
+
+#ifndef E_WEB_VIEW_PREVIEW_H
+#define E_WEB_VIEW_PREVIEW_H
+
+#include <misc/e-web-view.h>
+
+/* Standard GObject macros */
+#define E_TYPE_WEB_VIEW_PREVIEW \
+	(e_web_view_preview_get_type ())
+#define E_WEB_VIEW_PREVIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST \
+	((obj), E_TYPE_WEB_VIEW_PREVIEW, EWebViewPreview))
+#define E_WEB_VIEW_PREVIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_CAST \
+	((cls), E_TYPE_WEB_VIEW_PREVIEW, EWebViewPreviewClass))
+#define E_IS_WEB_VIEW_PREVIEW(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE \
+	((obj), E_TYPE_WEB_VIEW_PREVIEW))
+#define E_IS_WEB_VIEW_PREVIEW_CLASS(cls) \
+	(G_TYPE_CHECK_CLASS_TYPE \
+	((cls), E_TYPE_WEB_VIEW_PREVIEW))
+#define E_WEB_VIEW_PREVIEW_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS \
+	((obj), E_TYPE_WEB_VIEW_PREVIEW, EWebViewPreviewClass))
+
+G_BEGIN_DECLS
+
+typedef struct _EWebViewPreview EWebViewPreview;
+typedef struct _EWebViewPreviewClass EWebViewPreviewClass;
+typedef struct _EWebViewPreviewPrivate EWebViewPreviewPrivate;
+
+struct _EWebViewPreview {
+	GtkVPaned parent;
+	EWebViewPreviewPrivate *priv;
+};
+
+struct _EWebViewPreviewClass {
+	GtkVPanedClass parent_class;
+};
+
+GType		e_web_view_preview_get_type	(void);
+GtkWidget *	e_web_view_preview_new		(void);
+
+GtkTreeView *	e_web_view_preview_get_tree_view(EWebViewPreview *preview);
+GtkWidget *	e_web_view_preview_get_preview	(EWebViewPreview *preview);
+void		e_web_view_preview_set_preview  (EWebViewPreview *preview, GtkWidget *preview_widget);
+
+void 		e_web_view_preview_show_tree_view(EWebViewPreview *preview);
+void 		e_web_view_preview_hide_tree_view(EWebViewPreview *preview);
+
+void		e_web_view_preview_set_escape_values(EWebViewPreview *preview, gboolean escape);
+gboolean	e_web_view_preview_get_escape_values(EWebViewPreview *preview);
+
+void		e_web_view_preview_begin_update	(EWebViewPreview *preview);
+void		e_web_view_preview_end_update	(EWebViewPreview *preview);
+
+void		e_web_view_preview_add_header	(EWebViewPreview *preview, gint index, const gchar *header);
+void		e_web_view_preview_add_text	(EWebViewPreview *preview, const gchar *text);
+void		e_web_view_preview_add_raw_html	(EWebViewPreview *preview, const gchar *raw_html);
+void		e_web_view_preview_add_separator(EWebViewPreview *preview);
+void		e_web_view_preview_add_empty_line(EWebViewPreview *preview);
+void		e_web_view_preview_add_section	(EWebViewPreview *preview, const gchar *section, const gchar *value);
+
+G_END_DECLS
+
+#endif /* E_WEB_VIEW_PREVIEW_H */



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]