From ef7690c3845e3c1cebcf3caba7f7667a10e7123d Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Tue, 18 May 2010 22:47:29 +0200 Subject: 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(-) create mode 100644 widgets/misc/e-web-view-preview.c create mode 100644 widgets/misc/e-web-view-preview.h 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 #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 #include +#include #include #include #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 @@ -215,6 +215,32 @@ e_import_get_widget (EImport *import, return im->get_widget (import, target, im); } +/** + * 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 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 #include #include +#include #include #include @@ -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); } @@ -1191,6 +1260,14 @@ import_assistant_construct (EImportAssistant *import_assistant) /* simple import assistant page, URIs of files will be known later */ page = import_assistant_simple_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 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")); @@ -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 + * + * + * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com) + * + */ + +#include "e-web-view-preview.h" + +#include +#include +#include + +#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 (""); +} + +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, "
"); + + 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
*/ + if (strchr (res, '\r')) { + end = replace_string (res, "\r", ""); + g_free (res); + res = end; + } + + end = replace_string (res, "\n", "
"); + 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, "%s", 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, "%s", 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, "%s", 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, "
"); +} + +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, " "); +} + +/* 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, "%s%s", 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 + * + * + * 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 + +/* 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 */ -- cgit v1.2.3