aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMilan Crha <mcrha@redhat.com>2010-05-19 04:47:29 +0800
committerMilan Crha <mcrha@redhat.com>2010-05-19 04:47:29 +0800
commitef7690c3845e3c1cebcf3caba7f7667a10e7123d (patch)
treeab4a5bfa25bc5e25e0fed851c25226ce1930ec52
parent0f92771be57383e5d6e7d0f3e05743d192066eb3 (diff)
downloadgsoc2013-evolution-ef7690c3845e3c1cebcf3caba7f7667a10e7123d.tar
gsoc2013-evolution-ef7690c3845e3c1cebcf3caba7f7667a10e7123d.tar.gz
gsoc2013-evolution-ef7690c3845e3c1cebcf3caba7f7667a10e7123d.tar.bz2
gsoc2013-evolution-ef7690c3845e3c1cebcf3caba7f7667a10e7123d.tar.lz
gsoc2013-evolution-ef7690c3845e3c1cebcf3caba7f7667a10e7123d.tar.xz
gsoc2013-evolution-ef7690c3845e3c1cebcf3caba7f7667a10e7123d.tar.zst
gsoc2013-evolution-ef7690c3845e3c1cebcf3caba7f7667a10e7123d.zip
Bug #499320 - Preview before import from command line
-rw-r--r--addressbook/importers/Makefile.am2
-rw-r--r--addressbook/importers/evolution-addressbook-importers.h3
-rw-r--r--addressbook/importers/evolution-csv-importer.c81
-rw-r--r--addressbook/importers/evolution-ldif-importer.c63
-rw-r--r--addressbook/importers/evolution-vcard-importer.c345
-rw-r--r--calendar/importers/Makefile.am3
-rw-r--r--calendar/importers/icalendar-importer.c515
-rw-r--r--e-util/e-import.c26
-rw-r--r--e-util/e-import.h6
-rw-r--r--mail/importers/Makefile.am1
-rw-r--r--mail/importers/elm-importer.c1
-rw-r--r--mail/importers/evolution-mbox-importer.c150
-rw-r--r--mail/importers/mail-importer.h7
-rw-r--r--mail/importers/pine-importer.c1
-rw-r--r--modules/mail/e-mail-shell-backend.c33
-rw-r--r--widgets/misc/Makefile.am2
-rw-r--r--widgets/misc/e-import-assistant.c105
-rw-r--r--widgets/misc/e-web-view-preview.c481
-rw-r--r--widgets/misc/e-web-view-preview.h101
19 files changed, 1901 insertions, 25 deletions
diff --git a/addressbook/importers/Makefile.am b/addressbook/importers/Makefile.am
index 7ccc4c7545..742f46a334 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 747fe4281d..adaa62931a 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 6a12d7075a..9ad939dea9 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 41dda05a40..52ce90e212 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 080ef940f2..49e7bef70b 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 a5274b60e1..b54e619249 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 1f7da5e421..f719c9ce41 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 4475344018..14485633c1 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 affc364a4b..57bb79450d 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 65e099b461..4592de8eb5 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 c9ddcf75d5..937bfa50de 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 4fa4bf96f0..dc419dc89e 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 5f2f8c8b26..15de575f8b 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 07b7f22fbb..ed3b62e0b1 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 105b55285a..5c16a34b21 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 9a33f48b32..6373ceb58b 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 30d41385f4..3ca06dcda8 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 0000000000..e268779827
--- /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 0000000000..319f67ec82
--- /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 */