[Patch] Conversion of old data files

Andreas Köhler andi5.py at gmx.net
Tue Apr 11 17:46:30 EDT 2006


Hi,

here comes my first try to tackle the problem of data files created
by GnuCash prior to version 1.9, which tend to miss encoding
declarations.

As there is a wide range of possible questions I will not start
explaining what it exactly does. From on overall perspective it
enriches the file opening procedure by a druid, that lets the user
select from different possible representations of ambiguous words
found in the file, which gets reparsed then with these
substitutions.

I splitted the patch in several subpatches which mainly differ in
the files that get touched by them. But I do not know whether
applying only a proper subset of them makes any sense.

So, this is not ready for release. But you might want to take a look
at it and will probably have a lot of suggestions :) These are the
things I care about most:

* Is it OK, generally?

* We need a string freeze in 1.9.5. Please correct every string, I
  am no native speaker. I cannot even guarantee correctness of the
  content ;-) Strings that are missing? At least the final druid
  page. There is a translatable list of encodings typically used in
  a locale, please cry now if you really do not like this
  (Christian, what do you think?)

* The code is not tested throroughly. Play with it :D

* more to come...

-- andi5

PS: I will have less spare time the next weeks, but it should be
    possible to get this in 2.0, right?


>>> diffstat qof.patch
 qofbackend.h |    1 +
 qofsession.c |    1 +
 2 files changed, 2 insertions(+)

 * add ERR_FILEIO_NO_ENCODING and do not assume something broken if
   it occurs

>>> diffstat backend.patch
 gnc-backend-file.c       |   20 +-
 gnc-backend-file.h       |    1
 io-example-account.c     |    2
 io-example-account.h     |    2
 io-gncxml-v1.c           |    2
 io-gncxml-v2.c           |  423 ++++++++++++++++++++++++++++++++++++++++++++++-
 io-gncxml-v2.h           |   48 +++++
 sixtp.c                  |   65 +++++++
 sixtp.h                  |    9 -
 test/test-xml2-is-file.c |    2
 10 files changed, 552 insertions(+), 22 deletions(-)

 * extend gnc_is_xml_data_file_v2 by an encoding location parameter
 * enable parsing files in push mode
 * find ambiguous words
 * parse a file with substitutions

>>> diffstat gnc-file.patch
 gnc-file.c |   17 +++++++++++++++++
 1 file changed, 17 insertions(+)

 * catch missing encoding and spawn druid, open file afterwards

>>> diffstat druid-glade.patch
 Makefile.am                |    1
 druid-gnc-xml-import.glade | 1161 +++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 1162 insertions(+)

 * glade file

>>> diffstat druid.patch
 Makefile.am            |    2
 druid-gnc-xml-import.c | 1518 +++++++++++++++++++++++++++++++++++++++++++++++++
 druid-gnc-xml-import.h |   33 +
 3 files changed, 1553 insertions(+)

 * black box ;-)
-------------- next part --------------
Index: lib/libqof/qof/qofbackend.h
===================================================================
--- lib/libqof/qof/qofbackend.h	(revision 13769)
+++ lib/libqof/qof/qofbackend.h	(working copy)
@@ -116,6 +116,7 @@
   ERR_FILEIO_BACKUP_ERROR,   /**< couldn't make a backup of the file */
   ERR_FILEIO_WRITE_ERROR,    /**< couldn't write to the file */
   ERR_FILEIO_READ_ERROR,     /**< Could not open the file for reading. */
+  ERR_FILEIO_NO_ENCODING,    /**< file does not specify encoding */
 
   /* network errors */
   ERR_NETIO_SHORT_READ = 2000,  /**< not enough bytes received */
Index: lib/libqof/qof/qofsession.c
===================================================================
--- lib/libqof/qof/qofsession.c	(revision 13769)
+++ lib/libqof/qof/qofsession.c	(working copy)
@@ -1113,6 +1113,7 @@
 	err = qof_session_get_error(session);
 	if ((err != ERR_BACKEND_NO_ERR) &&
 		(err != ERR_FILEIO_FILE_TOO_OLD) &&
+		(err != ERR_FILEIO_NO_ENCODING) &&
 		(err != ERR_SQL_DB_TOO_OLD))
 	{
 		/* Something broke, put back the old stuff */
-------------- next part --------------
Index: src/backend/file/gnc-backend-file.c
===================================================================
--- src/backend/file/gnc-backend-file.c	(revision 13769)
+++ src/backend/file/gnc-backend-file.c	(working copy)
@@ -408,8 +408,13 @@
 static QofBookFileType
 gnc_file_be_determine_file_type(const char *path)
 {
-  if (gnc_is_xml_data_file_v2(path)) {
-    return GNC_BOOK_XML2_FILE;
+  gboolean with_encoding;
+  if (gnc_is_xml_data_file_v2(path, &with_encoding)) {
+    if (with_encoding) {
+      return GNC_BOOK_XML2_FILE;
+    } else {
+      return GNC_BOOK_XML2_FILE_NO_ENCODING;
+    }
   } else if (gnc_is_xml_data_file(path)) {
     return GNC_BOOK_XML1_FILE;
   } else if (is_gzipped_file(path)) {
@@ -435,10 +440,10 @@
 	rc = stat(path, &sbuf);
 	if(rc < 0) { return FALSE; }
 	if (sbuf.st_size == 0)    { PINFO (" empty file"); return TRUE; }
-	if(gnc_is_xml_data_file_v2(path))   { return TRUE; } 
-	else if(gnc_is_xml_data_file(path)) { return TRUE; } 
-	else if(is_gzipped_file(path))      { return TRUE; }
-	else if(gnc_is_bin_file(path))      { return TRUE; }
+	if(gnc_is_xml_data_file_v2(path, NULL)) { return TRUE; } 
+	else if(gnc_is_xml_data_file(path))     { return TRUE; } 
+	else if(is_gzipped_file(path))          { return TRUE; }
+	else if(gnc_is_bin_file(path))          { return TRUE; }
 	PINFO (" %s is not a gnc file", path);
 	return FALSE;
 }	
@@ -873,6 +878,9 @@
         if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
         break;
 
+    case GNC_BOOK_XML2_FILE_NO_ENCODING:
+        error = ERR_FILEIO_NO_ENCODING;
+        break;
     case GNC_BOOK_XML1_FILE:
         rc = qof_session_load_from_xml_file (book, be->fullpath);
         if (FALSE == rc) error = ERR_FILEIO_PARSE_ERROR;
Index: src/backend/file/gnc-backend-file.h
===================================================================
--- src/backend/file/gnc-backend-file.h	(revision 13769)
+++ src/backend/file/gnc-backend-file.h	(working copy)
@@ -57,6 +57,7 @@
     GNC_BOOK_BIN_FILE,
     GNC_BOOK_XML1_FILE,
     GNC_BOOK_XML2_FILE,
+    GNC_BOOK_XML2_FILE_NO_ENCODING,
     QSF_GNC_OBJECT,
     QSF_OBJECT,
     QSF_MAP,
Index: src/backend/file/io-example-account.c
===================================================================
--- src/backend/file/io-example-account.c	(revision 13769)
+++ src/backend/file/io-example-account.c	(working copy)
@@ -481,5 +481,5 @@
 gboolean
 gnc_is_example_account_xml(const gchar *name)
 {
-    return gnc_is_our_xml_file(name, GNC_ACCOUNT_STRING);
+    return gnc_is_our_xml_file(name, GNC_ACCOUNT_STRING, NULL);
 }
Index: src/backend/file/test/test-xml2-is-file.c
===================================================================
--- src/backend/file/test/test-xml2-is-file.c	(revision 13769)
+++ src/backend/file/test/test-xml2-is-file.c	(working copy)
@@ -22,7 +22,7 @@
 
     filename = malloc(strlen(directory) + 1 + strlen(FILENAME) + 1);
     sprintf(filename, "%s/%s", directory, FILENAME);
-    do_test(gnc_is_xml_data_file_v2(filename), "gnc_is_xml_data_file_v2");
+    do_test(gnc_is_xml_data_file_v2(filename, NULL), "gnc_is_xml_data_file_v2");
 
     print_test_results();
     exit(get_rv());
Index: src/backend/file/io-example-account.h
===================================================================
--- src/backend/file/io-example-account.h	(revision 13769)
+++ src/backend/file/io-example-account.h	(working copy)
@@ -51,8 +51,6 @@
                                             const gchar *filename);
 
 
-gboolean gnc_is_xml_data_file_v2(const gchar *filename);
-
 void gnc_free_example_account_list(GSList *list);
 GSList* gnc_load_example_account_list(QofBook *book,
                                       const char *dirname);
Index: src/backend/file/sixtp.c
===================================================================
--- src/backend/file/sixtp.c	(revision 13769)
+++ src/backend/file/sixtp.c	(working copy)
@@ -748,6 +748,52 @@
     return ret;
 }
 
+gboolean
+sixtp_parse_push (sixtp *sixtp,
+                  sixtp_push_handler push_handler,
+                  gpointer push_user_data,
+                  gpointer data_for_top_level,
+                  gpointer global_data,
+                  gpointer *parse_result)
+{
+    sixtp_parser_context *ctxt;
+    xmlParserCtxtPtr xml_context;
+
+    if (!push_handler) {
+        PERR("No push handler specified");
+        return FALSE;
+    }
+
+    if (!(ctxt = sixtp_context_new(sixtp, global_data, data_for_top_level))) {
+        PERR("sixtp_context_new returned null");
+        return FALSE;
+    }
+
+    xml_context = xmlCreatePushParserCtxt(&ctxt->handler, &ctxt->data,
+                                          NULL, 0, NULL);
+    ctxt->data.saxParserCtxt = xml_context;
+    ctxt->data.bad_xml_parser = sixtp_dom_parser_new(gnc_bad_xml_end_handler,
+                                                     NULL, NULL);
+
+    (*push_handler)(xml_context, push_user_data);
+
+    sixtp_context_run_end_handler(ctxt);
+
+    if (ctxt->data.parsing_ok) {
+        if (parse_result)
+            *parse_result = ctxt->top_frame->frame_data;
+        sixtp_context_destroy(ctxt);
+        return TRUE;
+    } else {
+        if (parse_result)
+            *parse_result = NULL;
+        if (g_slist_length(ctxt->data.stack) > 1)
+            sixtp_handle_catastrophe(&ctxt->data);
+        sixtp_context_destroy(ctxt);
+        return FALSE;
+    }
+}
+
 /***********************************************************************/
 static gboolean
 eat_whitespace(unsigned char **cursor)
@@ -787,7 +833,8 @@
 }
 
 gboolean
-gnc_is_our_xml_file(const char *filename, const char *first_tag)
+gnc_is_our_xml_file(const char *filename, const char *first_tag,
+                    gboolean *with_encoding)
 {
   FILE *f = NULL;
   char first_chunk[256];
@@ -796,6 +843,10 @@
   
   g_return_val_if_fail(filename, FALSE);
   g_return_val_if_fail(first_tag, FALSE);
+
+  if (with_encoding) {
+    *with_encoding = FALSE;
+  }
   
   f = fopen(filename, "r");
   if (f == NULL) {
@@ -838,6 +889,18 @@
 
       result = (strncmp(cursor, tag_compare, strlen(tag_compare)) == 0);
       g_free (tag_compare);
+
+      if (result && with_encoding) {
+        *cursor = '\0';
+        cursor = first_chunk;
+        while (search_for('e', &cursor)) {
+          if (strncmp(cursor, "ncoding=", 8) == 0) {
+            *with_encoding = TRUE;
+            break;
+          }
+        }
+      }
+
       return result;
   }
   else
Index: src/backend/file/sixtp.h
===================================================================
--- src/backend/file/sixtp.h	(revision 13769)
+++ src/backend/file/sixtp.h	(working copy)
@@ -86,6 +86,9 @@
                                    gpointer *result,
                                    const gchar *tag);
 
+typedef void (*sixtp_push_handler)(xmlParserCtxtPtr xml_context,
+                                   gpointer user_data);
+
 typedef struct sixtp 
 {
   /* If you change this, don't forget to modify all the copy/etc. functions */
@@ -180,6 +183,9 @@
 gboolean sixtp_parse_buffer(sixtp *sixtp, char *bufp, int bufsz,
                             gpointer data_for_top_level, gpointer global_data,
                             gpointer *parse_result);
+gboolean sixtp_parse_push(sixtp *sixtp, sixtp_push_handler push_handler,
+                          gpointer push_user_data, gpointer data_for_top_level,
+                          gpointer global_data, gpointer *parse_result);
 
 void sixtp_set_start(sixtp *parser, sixtp_start_handler start_handler);
 void sixtp_set_before_child(sixtp *parser, sixtp_before_child_handler handler);
@@ -198,7 +204,8 @@
 gboolean sixtp_add_sub_parser(sixtp *parser, const gchar* tag,
                               sixtp *sub_parser);
 
-gboolean gnc_is_our_xml_file(const char *filename, const char *first_tag);
+gboolean gnc_is_our_xml_file(const char *filename, const char *first_tag,
+                             gboolean *with_encoding);
 
 
 #endif /* _SIXTP_H_ */
Index: src/backend/file/io-gncxml-v1.c
===================================================================
--- src/backend/file/io-gncxml-v1.c	(revision 13769)
+++ src/backend/file/io-gncxml-v1.c	(working copy)
@@ -403,7 +403,7 @@
 gboolean
 gnc_is_xml_data_file(const gchar *filename) 
 {
-    return gnc_is_our_xml_file(filename, "gnc");
+    return gnc_is_our_xml_file(filename, "gnc", NULL);
 }
 
 /* ================================================================== */
Index: src/backend/file/io-gncxml-v2.c
===================================================================
--- src/backend/file/io-gncxml-v2.c	(revision 13769)
+++ src/backend/file/io-gncxml-v2.c	(working copy)
@@ -26,6 +26,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <zlib.h>
+#include <errno.h>
 
 #include "gnc-engine.h"
 #include "gnc-pricedb-p.h"
@@ -646,16 +647,19 @@
     return gd;
 }
 
-gboolean
-qof_session_load_from_xml_file_v2(FileBackend *fbe, QofBook *book)
+static gboolean
+qof_session_load_from_xml_file_v2_full(
+    FileBackend *fbe, QofBook *book,
+    sixtp_push_handler push_handler, gpointer push_user_data)
 {
-         AccountGroup *grp;
+    AccountGroup *grp;
     QofBackend *be = &fbe->be;
     sixtp_gdv2 *gd;
     sixtp *top_parser;
     sixtp *main_parser;
     sixtp *book_parser;
     struct file_backend be_data;
+    gboolean retval;
 
     gd = gnc_sixtp_gdv2_new(book, FALSE, file_rw_feedback, be->percentage);
 
@@ -717,9 +721,22 @@
     xaccLogDisable ();
     xaccDisableDataScrubbing();
 
-    if(!gnc_xml_parse_file(top_parser, fbe->fullpath,
-                           generic_callback, gd, book))
-    {
+    if (push_handler) {
+        gpointer parse_result = NULL;
+        gxpf_data gpdata;
+
+        gpdata.cb = generic_callback;
+        gpdata.parsedata = gd;
+        gpdata.bookdata = book;
+
+        retval = sixtp_parse_push(top_parser, push_handler, push_user_data,
+                                  NULL, &gpdata, &parse_result);
+    } else {
+        retval = gnc_xml_parse_file(top_parser, fbe->fullpath,
+                                    generic_callback, gd, book);
+    }
+
+    if (!retval) {
         sixtp_destroy(top_parser);
         xaccLogEnable ();
         xaccEnableDataScrubbing();
@@ -766,6 +783,12 @@
     return FALSE;
 }
 
+gboolean
+qof_session_load_from_xml_file_v2(FileBackend *fbe, QofBook *book)
+{
+    return qof_session_load_from_xml_file_v2_full(fbe, book, NULL, NULL);
+}
+
 /***********************************************************************/
 
 static void
@@ -1359,7 +1382,391 @@
 
 /***********************************************************************/
 gboolean
-gnc_is_xml_data_file_v2(const gchar *name)
+gnc_is_xml_data_file_v2(const gchar *name, gboolean *with_encoding)
 {
-    return gnc_is_our_xml_file(name, GNC_V2_STRING);
+    return gnc_is_our_xml_file(name, GNC_V2_STRING, with_encoding);
 }
+
+
+static void
+replace_character_references(gchar *string)
+{
+    gchar *cursor, *semicolon, *tail;
+    glong number;
+
+    for (cursor = strstr(string, "&#");
+         cursor && *cursor;
+         cursor = strstr(cursor, "&#")) {
+        semicolon = strchr(cursor, ';');
+        if (semicolon && *semicolon) {
+
+            /* parse number */
+            errno = 0;
+            if (*(cursor+2) == 'x') {
+                number = strtol(cursor+3, &tail, 16);
+            } else {
+                number = strtol(cursor+2, &tail, 10);
+            }
+            if (errno || tail != semicolon || number < 0 || number > 255) {
+                PWARN("Illegal character reference");
+                return;
+            }
+
+            /* overwrite '&' with the specified character */
+            *cursor = (gchar) number;
+            cursor++;
+            if (*(semicolon+1)) {
+                /* move text after semicolon the the left */
+                tail = g_strdup(semicolon+1);
+                strcpy(cursor, tail);
+                g_free(tail);
+            } else {
+                /* cut here */
+                *cursor = '\0';
+            }
+
+        } else {
+            PWARN("Unclosed character reference");
+            return;
+        }
+    }
+}
+
+static void
+conv_free(conv_type *conv) {
+    if (conv) {
+        g_free(conv->utf8_string);
+        g_free(conv);
+    }
+}
+
+static void
+conv_list_free(GList *conv_list) {
+    g_list_foreach(conv_list, (GFunc) conv_free, NULL);
+    g_list_free(conv_list);
+}
+
+typedef struct  {
+    GQuark encoding;
+    GIConv iconv;
+} iconv_item_type;
+
+gint
+gnc_xml2_find_ambiguous(const gchar *filename, GList *encodings,
+                        GHashTable **unique, GHashTable **ambiguous,
+                        GList **impossible, GError **error)
+{
+    GIOChannel *channel=NULL;
+    GIOStatus status;
+    GList *iconv_list=NULL, *conv_list=NULL, *iter;
+    iconv_item_type *iconv_item=NULL, *ascii=NULL;
+    const gchar *enc;
+    GHashTable *processed=NULL;
+    gint n_impossible = 0;
+    gboolean clean_return = FALSE;
+
+    channel = g_io_channel_new_file(filename, "r", error);
+    if (*error) {
+        PWARN("Unable to open file %s", filename);
+        goto cleanup_find_ambs;
+    }
+
+    status = g_io_channel_set_encoding(channel, NULL, error);
+    if (status != G_IO_STATUS_NORMAL) {
+        PWARN("Error on unsetting encoding on IOChannel");
+        goto cleanup_find_ambs;
+    }
+
+    /* we need ascii */
+    ascii = g_new(iconv_item_type, 1);
+    ascii->encoding = g_quark_from_string("ASCII");
+    ascii->iconv = g_iconv_open("UTF-8", "ASCII");
+    if (ascii->iconv == (GIConv) -1) {
+        PWARN("Unable to open ASCII ICONV conversion descriptor");
+        goto cleanup_find_ambs;
+    }
+
+    /* call iconv_open on encodings */
+    for (iter = encodings; iter; iter = iter->next) {
+        iconv_item = g_new(iconv_item_type, 1);
+        iconv_item->encoding = GPOINTER_TO_UINT (iter->data);
+        if (iconv_item->encoding == ascii->encoding) {
+            continue;
+        }
+
+        enc = g_quark_to_string(iconv_item->encoding);
+        iconv_item->iconv = g_iconv_open("UTF-8", enc);
+        if (iconv_item->iconv == (GIConv) -1) {
+            PWARN("Unable to open IConv conversion descriptor for '%s'", enc);
+            goto cleanup_find_ambs;
+        } else {
+            iconv_list = g_list_prepend(iconv_list, iconv_item);
+        }
+    }
+
+    /* prepare data containers */
+    if (unique)
+      *unique = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+                                      (GDestroyNotify) conv_free);
+    if (ambiguous)
+      *ambiguous = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+                                         (GDestroyNotify) conv_list_free);
+    if (impossible)
+      *impossible = NULL;
+    processed = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL);
+
+    /* loop through lines */
+    while (1) {
+        gchar *line, *word, *utf8;
+        gchar **word_array, **word_cursor;
+        conv_type *conv;
+
+        status = g_io_channel_read_line(channel, &line, NULL, NULL, error);
+        if (status == G_IO_STATUS_EOF) {
+            break;
+        }
+        if (status == G_IO_STATUS_AGAIN) {
+            continue;
+        }
+        if (status != G_IO_STATUS_NORMAL) {
+            goto cleanup_find_ambs;
+        }
+
+        g_strchomp(line);
+        replace_character_references(line);
+        word_array = g_strsplit_set(line, "> <", 0);
+        g_free(line);
+
+        /* loop through words */
+        for (word_cursor = word_array; *word_cursor; word_cursor++) {
+            word = *word_cursor;
+            if (!word)
+              continue;
+
+            utf8 = g_convert_with_iconv(word, -1, ascii->iconv,
+                                        NULL, NULL, error);
+            if (utf8) {
+                /* pure ascii */
+                g_free(utf8);
+                continue;
+            }
+            g_error_free(*error);
+            *error = NULL;
+
+            if (g_hash_table_lookup_extended(processed, word, NULL, NULL)) {
+                /* already processed */
+                continue;
+            }
+
+            /* loop through encodings */
+            conv_list = NULL;
+            for (iter = iconv_list; iter; iter = iter->next) {
+                iconv_item = iter->data;
+                utf8 = g_convert_with_iconv(word, -1, iconv_item->iconv,
+                                            NULL, NULL, error);
+                if (utf8) {
+                    conv = g_new(conv_type, 1);
+                    conv->encoding = iconv_item->encoding;
+                    conv->utf8_string = utf8;
+                    conv_list = g_list_prepend(conv_list, conv);
+                } else {
+                    g_error_free(*error);
+                    *error = NULL;
+                }
+            }
+
+            /* no successful conversion */
+            if (!conv_list) {
+                if (impossible)
+                    *impossible = g_list_append(*impossible, g_strdup(word));
+                n_impossible++;
+            }
+
+            /* more than one successful conversion */
+            else if (conv_list->next) {
+                if (ambiguous) {
+                    g_hash_table_insert(*ambiguous, g_strdup(word), conv_list);
+                } else {
+                    conv_list_free(conv_list);
+                }
+            }
+
+            /* only one successful conversion */
+            else {
+                if (unique) {
+                    g_hash_table_insert(*unique, g_strdup(word), conv);
+                } else {
+                    conv_free(conv);
+                }
+                g_list_free(conv_list);
+            }
+
+            g_hash_table_insert(processed, g_strdup(word), NULL);
+        }
+        g_strfreev(word_array);
+    }
+
+    clean_return = TRUE;
+
+ cleanup_find_ambs:
+
+    if (iconv_list) {
+        for (iter = iconv_list; iter; iter = iter->next) {
+            if (iter->data) {
+                g_iconv_close(((iconv_item_type*) iter->data)->iconv);
+                g_free(iter->data);
+            }
+        }
+        g_list_free(iconv_list);
+    }
+    if (processed)
+        g_hash_table_destroy(processed);
+    if (ascii)
+        g_free(ascii);
+    if (channel)
+        g_io_channel_unref(channel);
+
+    return (clean_return) ? n_impossible : -1;
+}
+
+typedef struct {
+    gchar *filename;
+    GHashTable *subst;
+} push_data_type;
+
+static void
+parse_with_subst_push_handler (xmlParserCtxtPtr xml_context,
+                               push_data_type *push_data)
+{
+    GIOChannel *channel=NULL;
+    GIOStatus status;
+    GIConv ascii=(GIConv)-1;
+    GString *output=NULL;
+    GError *error=NULL;
+
+    channel = g_io_channel_new_file(push_data->filename, "r", &error);
+    if (error) {
+        PWARN("Unable to open file %s", push_data->filename);
+        goto cleanup_push_handler;
+    }
+
+    status = g_io_channel_set_encoding(channel, NULL, &error);
+    if (status != G_IO_STATUS_NORMAL) {
+        PWARN("Error on unsetting encoding on IOChannel");
+        goto cleanup_push_handler;
+    }
+
+    ascii = g_iconv_open("UTF-8", "ASCII");
+    if (ascii == (GIConv) -1) {
+        PWARN("Unable to open ASCII ICONV conversion descriptor");
+        goto cleanup_push_handler;
+    }
+
+    /* loop through lines */
+    while (1) {
+        gchar *line, *word, *repl, *utf8;
+        gint pos, len;
+        gchar *start, *cursor;
+
+        status = g_io_channel_read_line(channel, &line, NULL, NULL, &error);
+        if (status == G_IO_STATUS_EOF) {
+            break;
+        }
+        if (status == G_IO_STATUS_AGAIN) {
+            continue;
+        }
+        if (status != G_IO_STATUS_NORMAL) {
+            goto cleanup_push_handler;
+        }
+
+        replace_character_references(line);
+        output = g_string_new(line);
+        g_free(line);
+
+        /* loop through words */
+        cursor = output->str;
+        pos = 0;
+        while (1) {
+            /* ignore delimiters */
+            while (*cursor=='>' || *cursor==' ' || *cursor=='<' ||
+                   *cursor=='\n') {
+                cursor++;
+                pos += 1;
+            }
+
+            if (!*cursor)
+                /* welcome to EOL */
+                break;
+
+            /* search for a delimiter */
+            start = cursor;
+            len = 0;
+            while (*cursor && *cursor!='>' && *cursor!=' ' && *cursor!='<' &&
+                   *cursor!='\n') {
+                cursor++;
+                len++;
+            }
+
+            utf8 = g_convert_with_iconv(start, len, ascii, NULL, NULL, &error);
+
+            if (utf8) {
+                /* pure ascii */
+                g_free(utf8);
+                pos += len;
+            } else {
+                g_error_free(error);
+                error = NULL;
+
+                word = g_strndup(start, len);
+                repl = g_hash_table_lookup(push_data->subst, word);
+                g_free(word);
+                if (repl) {
+                    /* there is a replacement */
+                    output = g_string_insert(g_string_erase(output, pos, len),
+                                             pos, repl);
+                    pos += strlen(repl);
+                    cursor = output->str + pos;
+                } else {
+                    /* there is no replacement, return immediately */
+                    goto cleanup_push_handler;
+                }
+            }
+        }
+
+        if (xmlParseChunk(xml_context, output->str, output->len, 0) != 0) {
+            goto cleanup_push_handler;
+        }
+    }
+
+    /* last chunk */
+    xmlParseChunk(xml_context, "", 0, 1);
+
+ cleanup_push_handler:
+
+    if (output)
+        g_string_free(output, TRUE);
+    if (ascii != (GIConv) -1)
+        g_iconv_close(ascii);
+    if (channel)
+        g_io_channel_unref(channel);
+}
+
+gboolean
+gnc_xml2_parse_with_subst (FileBackend *fbe, QofBook *book, GHashTable *subst)
+{
+    push_data_type *push_data;
+    gboolean success;
+
+    push_data = g_new(push_data_type, 1);
+    push_data->filename = fbe->fullpath;
+    push_data->subst = subst;
+
+    success = qof_session_load_from_xml_file_v2_full(
+        fbe, book, (sixtp_push_handler) parse_with_subst_push_handler,
+        push_data);
+
+    if (success)
+        qof_book_kvp_changed(book);
+
+    return success;
+}
Index: src/backend/file/io-gncxml-v2.h
===================================================================
--- src/backend/file/io-gncxml-v2.h	(revision 13769)
+++ src/backend/file/io-gncxml-v2.h	(working copy)
@@ -142,11 +142,57 @@
 /** The is_gncxml_file() routine checks to see if the first few 
  * chars of the file look like gnc-xml data.
  */
-gboolean gnc_is_xml_data_file_v2(const gchar *name);
+gboolean gnc_is_xml_data_file_v2(const gchar *name, gboolean *with_encoding);
 
 /** Write a name-space declaration for the provided namespace data type
  * within the GNC XML namespace at http://www.gnucash.org/XML.
  */
 void gnc_xml2_write_namespace_decl (FILE *out, const char *namespace);
 
+
+typedef struct {
+  GQuark encoding;
+  gchar *utf8_string;
+} conv_type;
+
+/** Read a file as plain byte stream to find words that are not completely ASCII.
+ * On error, @unique, @ambiguous and @impossible will be filled up to that point,
+ * @error may contain an io channel error, -1 will be returned.
+ *
+ * @param filename Name of the file to read.
+ *
+ * @param encodings List of encodings to check words for, each begin one a GQuark
+ * in a pointer.
+ *
+ * @param unique Location used for a hash table for unique solutions, if not
+ * NULL. The byte sequence is the key, successful_conversion the value.
+ *
+ * @param ambiguous Location used for a hash table for ambiguous byte sequences,
+ * if not NULL. The byte sequences is the key, a list of successful_conversions
+ * the value.
+ *
+ * @param impossible Location used for a list for undecodable byte sequences,
+ * if not NULL.
+ *
+ * @param error Location to return an io channel error.
+ *
+ * @return Size of impossible, -1 on error.
+ */
+gint gnc_xml2_find_ambiguous(
+    const gchar *filename, GList *encodings, GHashTable **unique,
+    GHashTable **ambiguous, GList **impossible, GError **error);
+typedef gint (*find_ambiguous_handler)(
+    const gchar *filename, GList *encodings, GHashTable **unique,
+    GHashTable **ambiguous, GList **impossible, GError **error);
+
+/** Parse a file in push mode, but replace byte sequences in the file given a
+ * hash table of substitutions
+ *
+ * @param subst hash table with keys and values of type gchar*
+ */
+gboolean gnc_xml2_parse_with_subst (
+    FileBackend *fbe, QofBook *book, GHashTable *subst);
+typedef gboolean (*parse_with_subst_handler)(
+    FileBackend *fbe, QofBook *book, GHashTable *subst);
+
 #endif /* __IO_GNCXML_V2_H__ */
-------------- next part --------------
Index: src/gnome-utils/gnc-file.c
===================================================================
--- src/gnome-utils/gnc-file.c	(revision 13769)
+++ src/gnome-utils/gnc-file.c	(working copy)
@@ -29,6 +29,7 @@
 #include <g-wrap-wct.h>
 
 #include "dialog-utils.h"
+#include "druid-gnc-xml-import.h"
 #include "gnc-commodity.h"
 #include "gnc-component-manager.h"
 #include "gnc-engine.h"
@@ -740,6 +741,22 @@
 
     /* check for i/o error, put up appropriate error dialog */
     io_err = qof_session_get_error (new_session);
+
+    if (io_err == ERR_FILEIO_NO_ENCODING) {
+      qof_session_pop_error (new_session);
+      if (gnc_xml_convert_single_file (newfile)) {
+        /* try to load once again */
+        gnc_window_show_progress(_("Reading file..."), 0.0);
+        qof_session_load (new_session, gnc_window_show_progress);
+        gnc_window_show_progress(NULL, -1.0);
+        xaccLogEnable();
+        io_err = qof_session_get_error (new_session);
+      }
+      else {
+        io_err = ERR_FILEIO_PARSE_ERROR;
+      }
+    }
+
     uh_oh = show_session_error (io_err, newfile, GNC_FILE_DIALOG_OPEN);
 
     new_group = gnc_book_get_group (qof_session_get_book (new_session));
-------------- next part --------------
Index: src/gnome-utils/glade/Makefile.am
===================================================================
--- src/gnome-utils/glade/Makefile.am	(revision 13769)
+++ src/gnome-utils/glade/Makefile.am	(working copy)
@@ -6,6 +6,7 @@
   druid-provider-multifile.glade \
   exchange-dialog.glade \
   druid-gconf-setup.glade \
+  druid-gnc-xml-import.glade \
   gnc-date-format.glade \
   gnc-gui-query.glade \
   preferences.glade \
Index: src/gnome-utils/glade/druid-gnc-xml-import.glade
===================================================================
--- src/gnome-utils/glade/druid-gnc-xml-import.glade	(revision 0)
+++ src/gnome-utils/glade/druid-gnc-xml-import.glade	(revision 0)
@@ -0,0 +1,1161 @@
+<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
+<!DOCTYPE glade-interface SYSTEM "http://glade.gnome.org/glade-2.0.dtd">
+
+<glade-interface>
+<requires lib="gnome"/>
+
+<widget class="GtkDialog" id="GnuCash XML Import Dialog">
+  <property name="title" translatable="yes">GnuCash XML Import Druid</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+  <property name="modal">False</property>
+  <property name="default_width">500</property>
+  <property name="default_height">500</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="has_separator">False</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox1">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">0</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="hidden action area">
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GnomeDruid" id="gnc_xml_import_druid">
+	  <property name="border_width">4</property>
+	  <property name="visible">True</property>
+	  <property name="show_help">False</property>
+	  <signal name="cancel" handler="gxi_cancel_cb" last_modification_time="Thu, 30 Mar 2006 05:47:59 GMT"/>
+
+	  <child>
+	    <widget class="GnomeDruidPageEdge" id="start_page">
+	      <property name="visible">True</property>
+	      <property name="position">GNOME_EDGE_START</property>
+	      <property name="title" translatable="yes">Title placeholder</property>
+	      <property name="text">Introduction placeholder</property>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GnomeDruidPageStandard" id="load_file_page">
+	      <property name="visible">True</property>
+	      <property name="title" translatable="yes">Choose a file to import</property>
+	      <signal name="next" handler="gxi_load_file_next_cb" last_modification_time="Thu, 30 Mar 2006 05:52:54 GMT"/>
+
+	      <child internal-child="vbox">
+		<widget class="GtkVBox" id="file_chooser_box">
+		  <property name="border_width">16</property>
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <placeholder/>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GnomeDruidPageStandard" id="encodings_doc_page">
+	      <property name="visible">True</property>
+	      <property name="title">enc doc page placeholder</property>
+
+	      <child internal-child="vbox">
+		<widget class="GtkVBox" id="druid-vbox6">
+		  <property name="border_width">16</property>
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="encodings_doc_label">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">placeholder</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">True</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GnomeDruidPageStandard" id="conversion_page">
+	      <property name="visible">True</property>
+	      <property name="title" translatable="yes">Convert the file</property>
+	      <signal name="prepare" handler="gxi_conversion_prepare_cb" after="yes" last_modification_time="Fri, 07 Apr 2006 18:12:18 GMT"/>
+	      <signal name="next" handler="gxi_conversion_next_cb" last_modification_time="Sun, 09 Apr 2006 12:52:00 GMT"/>
+
+	      <child internal-child="vbox">
+		<widget class="GtkVBox" id="druid-vbox3">
+		  <property name="border_width">16</property>
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <widget class="GtkVBox" id="vbox10">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">12</property>
+
+		      <child>
+			<widget class="GtkHBox" id="hbox10">
+			  <property name="visible">True</property>
+			  <property name="homogeneous">False</property>
+			  <property name="spacing">0</property>
+
+			  <child>
+			    <widget class="GtkButton" id="edit_encs_button">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			      <signal name="clicked" handler="gxi_edit_encodings_clicked_cb" last_modification_time="Fri, 07 Apr 2006 23:16:12 GMT"/>
+
+			      <child>
+				<widget class="GtkAlignment" id="alignment12">
+				  <property name="visible">True</property>
+				  <property name="xalign">0.5</property>
+				  <property name="yalign">0.5</property>
+				  <property name="xscale">0</property>
+				  <property name="yscale">0</property>
+				  <property name="top_padding">0</property>
+				  <property name="bottom_padding">0</property>
+				  <property name="left_padding">0</property>
+				  <property name="right_padding">0</property>
+
+				  <child>
+				    <widget class="GtkHBox" id="hbox12">
+				      <property name="visible">True</property>
+				      <property name="homogeneous">False</property>
+				      <property name="spacing">2</property>
+
+				      <child>
+					<widget class="GtkImage" id="image15">
+					  <property name="visible">True</property>
+					  <property name="stock">gtk-select-font</property>
+					  <property name="icon_size">4</property>
+					  <property name="xalign">0.5</property>
+					  <property name="yalign">0.5</property>
+					  <property name="xpad">0</property>
+					  <property name="ypad">0</property>
+					</widget>
+					<packing>
+					  <property name="padding">0</property>
+					  <property name="expand">False</property>
+					  <property name="fill">False</property>
+					</packing>
+				      </child>
+
+				      <child>
+					<widget class="GtkLabel" id="label13">
+					  <property name="visible">True</property>
+					  <property name="label" translatable="yes">_Edit list of encodings</property>
+					  <property name="use_underline">True</property>
+					  <property name="use_markup">False</property>
+					  <property name="justify">GTK_JUSTIFY_LEFT</property>
+					  <property name="wrap">False</property>
+					  <property name="selectable">False</property>
+					  <property name="xalign">0.5</property>
+					  <property name="yalign">0.5</property>
+					  <property name="xpad">0</property>
+					  <property name="ypad">0</property>
+					</widget>
+					<packing>
+					  <property name="padding">0</property>
+					  <property name="expand">False</property>
+					  <property name="fill">False</property>
+					</packing>
+				      </child>
+				    </widget>
+				  </child>
+				</widget>
+			      </child>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			      <property name="pack_type">GTK_PACK_END</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkHBox" id="default_enc_box">
+			      <property name="visible">True</property>
+			      <property name="homogeneous">False</property>
+			      <property name="spacing">12</property>
+
+			      <child>
+				<widget class="GtkLabel" id="label12">
+				  <property name="visible">True</property>
+				  <property name="label" translatable="yes">Default encoding:</property>
+				  <property name="use_underline">False</property>
+				  <property name="use_markup">False</property>
+				  <property name="justify">GTK_JUSTIFY_LEFT</property>
+				  <property name="wrap">False</property>
+				  <property name="selectable">False</property>
+				  <property name="xalign">0.5</property>
+				  <property name="yalign">0.5</property>
+				  <property name="xpad">0</property>
+				  <property name="ypad">0</property>
+				</widget>
+				<packing>
+				  <property name="padding">0</property>
+				  <property name="expand">False</property>
+				  <property name="fill">False</property>
+				</packing>
+			      </child>
+
+			      <child>
+				<placeholder/>
+			      </child>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkLabel" id="impossible_label">
+			  <property name="visible">True</property>
+			  <property name="label">placeholder: #unassigned, #impossible</property>
+			  <property name="use_underline">False</property>
+			  <property name="use_markup">False</property>
+			  <property name="justify">GTK_JUSTIFY_LEFT</property>
+			  <property name="wrap">False</property>
+			  <property name="selectable">False</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xpad">0</property>
+			  <property name="ypad">0</property>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">False</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkAlignment" id="alignment13">
+			  <property name="visible">True</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xscale">0.75</property>
+			  <property name="yscale">1</property>
+			  <property name="top_padding">0</property>
+			  <property name="bottom_padding">0</property>
+			  <property name="left_padding">0</property>
+			  <property name="right_padding">0</property>
+
+			  <child>
+			    <widget class="GtkScrolledWindow" id="scrolledwindow4">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+			      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+			      <property name="shadow_type">GTK_SHADOW_NONE</property>
+			      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+			      <child>
+				<widget class="GtkViewport" id="viewport1">
+				  <property name="visible">True</property>
+				  <property name="shadow_type">GTK_SHADOW_IN</property>
+
+				  <child>
+				    <widget class="GtkAlignment" id="string_box_container">
+				      <property name="border_width">6</property>
+				      <property name="visible">True</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xscale">1</property>
+				      <property name="yscale">1</property>
+				      <property name="top_padding">0</property>
+				      <property name="bottom_padding">0</property>
+				      <property name="left_padding">0</property>
+				      <property name="right_padding">12</property>
+
+				      <child>
+					<placeholder/>
+				      </child>
+				    </widget>
+				  </child>
+				</widget>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">True</property>
+			  <property name="fill">True</property>
+			</packing>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GnomeDruidPageStandard" id="loaded_files_page">
+	      <property name="visible">True</property>
+	      <property name="title" translatable="yes">GnuCash XML files you have loaded</property>
+	      <signal name="prepare" handler="gxi_loaded_files_prepare_cb" last_modification_time="Thu, 30 Mar 2006 22:20:01 GMT"/>
+	      <signal name="next" handler="gxi_loaded_files_next_cb" last_modification_time="Thu, 30 Mar 2006 23:20:21 GMT"/>
+
+	      <child internal-child="vbox">
+		<widget class="GtkVBox" id="druid-vbox5">
+		  <property name="border_width">16</property>
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">12</property>
+
+		  <child>
+		    <widget class="GtkScrolledWindow" id="scrolledwindow1">
+		      <property name="visible">True</property>
+		      <property name="can_focus">True</property>
+		      <property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+		      <property name="shadow_type">GTK_SHADOW_IN</property>
+		      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+		      <child>
+			<widget class="GtkTreeView" id="selected_file_list">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="headers_visible">False</property>
+			  <property name="rules_hint">False</property>
+			  <property name="reorderable">False</property>
+			  <property name="enable_search">True</property>
+			  <property name="fixed_height_mode">False</property>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkLabel" id="label1">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">Click &quot;Load another file&quot; if you have more data to import at this time. Do this if you have saved your accounts to separate GnuCash files.
+
+Click &quot;Forward&quot; to finish loading files and move to the next step of the GnuCash XML import process.</property>
+		      <property name="use_underline">False</property>
+		      <property name="use_markup">False</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">True</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkHButtonBox" id="hbuttonbox1">
+		      <property name="visible">True</property>
+		      <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+		      <property name="spacing">0</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkVButtonBox" id="vbuttonbox1">
+		      <property name="visible">True</property>
+		      <property name="layout_style">GTK_BUTTONBOX_SPREAD</property>
+		      <property name="spacing">0</property>
+
+		      <child>
+			<widget class="GtkButton" id="unload_file_button">
+			  <property name="visible">True</property>
+			  <property name="can_default">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <property name="focus_on_click">True</property>
+			  <signal name="clicked" handler="gxi_unload_file_clicked_cb" last_modification_time="Thu, 30 Mar 2006 08:57:18 GMT"/>
+
+			  <child>
+			    <widget class="GtkAlignment" id="alignment2">
+			      <property name="visible">True</property>
+			      <property name="xalign">0.5</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xscale">0</property>
+			      <property name="yscale">0</property>
+			      <property name="top_padding">0</property>
+			      <property name="bottom_padding">0</property>
+			      <property name="left_padding">0</property>
+			      <property name="right_padding">0</property>
+
+			      <child>
+				<widget class="GtkHBox" id="hbox2">
+				  <property name="visible">True</property>
+				  <property name="homogeneous">False</property>
+				  <property name="spacing">2</property>
+
+				  <child>
+				    <widget class="GtkImage" id="image2">
+				      <property name="visible">True</property>
+				      <property name="stock">gtk-remove</property>
+				      <property name="icon_size">4</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xpad">0</property>
+				      <property name="ypad">0</property>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+
+				  <child>
+				    <widget class="GtkLabel" id="label3">
+				      <property name="visible">True</property>
+				      <property name="label" translatable="yes">Unload selected file</property>
+				      <property name="use_underline">True</property>
+				      <property name="use_markup">False</property>
+				      <property name="justify">GTK_JUSTIFY_LEFT</property>
+				      <property name="wrap">False</property>
+				      <property name="selectable">False</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xpad">0</property>
+				      <property name="ypad">0</property>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+				</widget>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+		      </child>
+
+		      <child>
+			<widget class="GtkButton" id="load_file_button">
+			  <property name="visible">True</property>
+			  <property name="can_default">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <property name="focus_on_click">True</property>
+			  <signal name="clicked" handler="gxi_load_file_clicked_cb" last_modification_time="Thu, 30 Mar 2006 09:01:19 GMT"/>
+
+			  <child>
+			    <widget class="GtkAlignment" id="alignment1">
+			      <property name="visible">True</property>
+			      <property name="xalign">0.5</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xscale">0</property>
+			      <property name="yscale">0</property>
+			      <property name="top_padding">0</property>
+			      <property name="bottom_padding">0</property>
+			      <property name="left_padding">0</property>
+			      <property name="right_padding">0</property>
+
+			      <child>
+				<widget class="GtkHBox" id="hbox1">
+				  <property name="visible">True</property>
+				  <property name="homogeneous">False</property>
+				  <property name="spacing">2</property>
+
+				  <child>
+				    <widget class="GtkImage" id="image1">
+				      <property name="visible">True</property>
+				      <property name="stock">gtk-add</property>
+				      <property name="icon_size">4</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xpad">0</property>
+				      <property name="ypad">0</property>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+
+				  <child>
+				    <widget class="GtkLabel" id="label2">
+				      <property name="visible">True</property>
+				      <property name="label" translatable="yes">Load another file</property>
+				      <property name="use_underline">True</property>
+				      <property name="use_markup">False</property>
+				      <property name="justify">GTK_JUSTIFY_LEFT</property>
+				      <property name="wrap">False</property>
+				      <property name="selectable">False</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xpad">0</property>
+				      <property name="ypad">0</property>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+				</widget>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+		      </child>
+
+		      <child>
+			<widget class="GtkButton" id="no_merge_button">
+			  <property name="visible">True</property>
+			  <property name="can_default">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="relief">GTK_RELIEF_NORMAL</property>
+			  <property name="focus_on_click">True</property>
+			  <signal name="clicked" handler="gxi_cancel_cb" last_modification_time="Thu, 30 Mar 2006 22:07:55 GMT"/>
+
+			  <child>
+			    <widget class="GtkAlignment" id="alignment4">
+			      <property name="visible">True</property>
+			      <property name="xalign">0.5</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xscale">0</property>
+			      <property name="yscale">0</property>
+			      <property name="top_padding">0</property>
+			      <property name="bottom_padding">0</property>
+			      <property name="left_padding">0</property>
+			      <property name="right_padding">0</property>
+
+			      <child>
+				<widget class="GtkHBox" id="hbox3">
+				  <property name="visible">True</property>
+				  <property name="homogeneous">False</property>
+				  <property name="spacing">2</property>
+
+				  <child>
+				    <widget class="GtkImage" id="image3">
+				      <property name="visible">True</property>
+				      <property name="stock">gtk-cancel</property>
+				      <property name="icon_size">4</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xpad">0</property>
+				      <property name="ypad">0</property>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+
+				  <child>
+				    <widget class="GtkLabel" id="label5">
+				      <property name="visible">True</property>
+				      <property name="label" translatable="yes">Do not merge</property>
+				      <property name="use_underline">True</property>
+				      <property name="use_markup">False</property>
+				      <property name="justify">GTK_JUSTIFY_LEFT</property>
+				      <property name="wrap">False</property>
+				      <property name="selectable">False</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xpad">0</property>
+				      <property name="ypad">0</property>
+				    </widget>
+				    <packing>
+				      <property name="padding">0</property>
+				      <property name="expand">False</property>
+				      <property name="fill">False</property>
+				    </packing>
+				  </child>
+				</widget>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GnomeDruidPageStandard" id="merge_page">
+	      <property name="visible">True</property>
+	      <property name="title">This one is a placeholder right now</property>
+
+	      <child internal-child="vbox">
+		<widget class="GtkVBox" id="druid-vbox6">
+		  <property name="border_width">16</property>
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <placeholder/>
+		  </child>
+		</widget>
+	      </child>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GnomeDruidPageEdge" id="end_page">
+	      <property name="visible">True</property>
+	      <property name="position">GNOME_EDGE_FINISH</property>
+	      <property name="title" translatable="yes">Finish GnuCash XML Import</property>
+	      <property name="text">FIXME: explain what we do or what we have done (+translatable)</property>
+	      <signal name="finish" handler="gxi_end_finish_cb" last_modification_time="Thu, 30 Mar 2006 05:00:17 GMT"/>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+<widget class="GtkDialog" id="Encodings Dialog">
+  <property name="border_width">6</property>
+  <property name="title" translatable="yes">Edit the list of encodings</property>
+  <property name="type">GTK_WINDOW_TOPLEVEL</property>
+  <property name="window_position">GTK_WIN_POS_CENTER_ON_PARENT</property>
+  <property name="modal">True</property>
+  <property name="default_height">300</property>
+  <property name="resizable">True</property>
+  <property name="destroy_with_parent">False</property>
+  <property name="decorated">True</property>
+  <property name="skip_taskbar_hint">False</property>
+  <property name="skip_pager_hint">False</property>
+  <property name="type_hint">GDK_WINDOW_TYPE_HINT_DIALOG</property>
+  <property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
+  <property name="has_separator">True</property>
+
+  <child internal-child="vbox">
+    <widget class="GtkVBox" id="dialog-vbox2">
+      <property name="visible">True</property>
+      <property name="homogeneous">False</property>
+      <property name="spacing">6</property>
+
+      <child internal-child="action_area">
+	<widget class="GtkHButtonBox" id="dialog-action_area1">
+	  <property name="visible">True</property>
+	  <property name="layout_style">GTK_BUTTONBOX_END</property>
+
+	  <child>
+	    <widget class="GtkButton" id="cancelbutton1">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-cancel</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-6</property>
+	    </widget>
+	  </child>
+
+	  <child>
+	    <widget class="GtkButton" id="okbutton1">
+	      <property name="visible">True</property>
+	      <property name="can_default">True</property>
+	      <property name="can_focus">True</property>
+	      <property name="label">gtk-ok</property>
+	      <property name="use_stock">True</property>
+	      <property name="relief">GTK_RELIEF_NORMAL</property>
+	      <property name="focus_on_click">True</property>
+	      <property name="response_id">-5</property>
+	    </widget>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">False</property>
+	  <property name="fill">True</property>
+	  <property name="pack_type">GTK_PACK_END</property>
+	</packing>
+      </child>
+
+      <child>
+	<widget class="GtkHBox" id="hbox6">
+	  <property name="visible">True</property>
+	  <property name="homogeneous">False</property>
+	  <property name="spacing">18</property>
+
+	  <child>
+	    <widget class="GtkVBox" id="vbox4">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">12</property>
+
+	      <child>
+		<widget class="GtkVBox" id="vbox5">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="label6">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">&lt;b&gt;S_ystem input encodings&lt;/b&gt;</property>
+		      <property name="use_underline">True</property>
+		      <property name="use_markup">True</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="mnemonic_widget">available_encs_view</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkAlignment" id="alignment7">
+		      <property name="visible">True</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xscale">1</property>
+		      <property name="yscale">1</property>
+		      <property name="top_padding">0</property>
+		      <property name="bottom_padding">0</property>
+		      <property name="left_padding">12</property>
+		      <property name="right_padding">0</property>
+
+		      <child>
+			<widget class="GtkHBox" id="hbox8">
+			  <property name="visible">True</property>
+			  <property name="homogeneous">False</property>
+			  <property name="spacing">6</property>
+
+			  <child>
+			    <widget class="GtkScrolledWindow" id="scrolledwindow2">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+			      <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+			      <property name="shadow_type">GTK_SHADOW_IN</property>
+			      <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+			      <child>
+				<widget class="GtkTreeView" id="available_encs_view">
+				  <property name="visible">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="headers_visible">False</property>
+				  <property name="rules_hint">False</property>
+				  <property name="reorderable">False</property>
+				  <property name="enable_search">True</property>
+				  <property name="fixed_height_mode">False</property>
+				  <signal name="row_activated" handler="gxi_available_enc_activated_cb" last_modification_time="Sat, 08 Apr 2006 01:13:30 GMT"/>
+				</widget>
+			      </child>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">True</property>
+			      <property name="fill">True</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkAlignment" id="alignment8">
+			      <property name="visible">True</property>
+			      <property name="xalign">0.5</property>
+			      <property name="yalign">0.5</property>
+			      <property name="xscale">1</property>
+			      <property name="yscale">0</property>
+			      <property name="top_padding">0</property>
+			      <property name="bottom_padding">0</property>
+			      <property name="left_padding">0</property>
+			      <property name="right_padding">0</property>
+
+			      <child>
+				<widget class="GtkButton" id="add_enc_button">
+				  <property name="visible">True</property>
+				  <property name="can_focus">True</property>
+				  <property name="relief">GTK_RELIEF_NORMAL</property>
+				  <property name="focus_on_click">True</property>
+				  <signal name="clicked" handler="gxi_add_enc_clicked_cb" last_modification_time="Sat, 08 Apr 2006 00:11:22 GMT"/>
+
+				  <child>
+				    <widget class="GtkImage" id="image11">
+				      <property name="visible">True</property>
+				      <property name="stock">gtk-go-forward</property>
+				      <property name="icon_size">4</property>
+				      <property name="xalign">0.5</property>
+				      <property name="yalign">0.5</property>
+				      <property name="xpad">0</property>
+				      <property name="ypad">0</property>
+				    </widget>
+				  </child>
+				</widget>
+			      </child>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkVBox" id="vbox6">
+		  <property name="visible">True</property>
+		  <property name="homogeneous">False</property>
+		  <property name="spacing">6</property>
+
+		  <child>
+		    <widget class="GtkLabel" id="label10">
+		      <property name="visible">True</property>
+		      <property name="label" translatable="yes">&lt;b&gt;_Custom encoding&lt;/b&gt;</property>
+		      <property name="use_underline">True</property>
+		      <property name="use_markup">True</property>
+		      <property name="justify">GTK_JUSTIFY_LEFT</property>
+		      <property name="wrap">False</property>
+		      <property name="selectable">False</property>
+		      <property name="xalign">0</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xpad">0</property>
+		      <property name="ypad">0</property>
+		      <property name="mnemonic_widget">custom_enc_entry</property>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">False</property>
+		      <property name="fill">False</property>
+		    </packing>
+		  </child>
+
+		  <child>
+		    <widget class="GtkAlignment" id="alignment6">
+		      <property name="visible">True</property>
+		      <property name="xalign">0.5</property>
+		      <property name="yalign">0.5</property>
+		      <property name="xscale">1</property>
+		      <property name="yscale">1</property>
+		      <property name="top_padding">0</property>
+		      <property name="bottom_padding">0</property>
+		      <property name="left_padding">12</property>
+		      <property name="right_padding">0</property>
+
+		      <child>
+			<widget class="GtkHBox" id="hbox7">
+			  <property name="visible">True</property>
+			  <property name="homogeneous">False</property>
+			  <property name="spacing">6</property>
+
+			  <child>
+			    <widget class="GtkEntry" id="custom_enc_entry">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="editable">True</property>
+			      <property name="visibility">True</property>
+			      <property name="max_length">0</property>
+			      <property name="text" translatable="yes"></property>
+			      <property name="has_frame">True</property>
+			      <property name="invisible_char">*</property>
+			      <property name="activates_default">False</property>
+			      <signal name="activate" handler="gxi_custom_enc_activate_cb" last_modification_time="Sat, 08 Apr 2006 10:09:02 GMT"/>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">True</property>
+			      <property name="fill">True</property>
+			    </packing>
+			  </child>
+
+			  <child>
+			    <widget class="GtkButton" id="add_custom_enc_button">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			      <signal name="clicked" handler="gxi_add_custom_enc_clicked_cb" last_modification_time="Sat, 08 Apr 2006 00:11:40 GMT"/>
+
+			      <child>
+				<widget class="GtkImage" id="image10">
+				  <property name="visible">True</property>
+				  <property name="stock">gtk-go-forward</property>
+				  <property name="icon_size">4</property>
+				  <property name="xalign">0.5</property>
+				  <property name="yalign">0.5</property>
+				  <property name="xpad">0</property>
+				  <property name="ypad">0</property>
+				</widget>
+			      </child>
+			    </widget>
+			    <packing>
+			      <property name="padding">0</property>
+			      <property name="expand">False</property>
+			      <property name="fill">False</property>
+			    </packing>
+			  </child>
+			</widget>
+		      </child>
+		    </widget>
+		    <packing>
+		      <property name="padding">0</property>
+		      <property name="expand">True</property>
+		      <property name="fill">True</property>
+		    </packing>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">True</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+
+	  <child>
+	    <widget class="GtkVBox" id="vbox7">
+	      <property name="visible">True</property>
+	      <property name="homogeneous">False</property>
+	      <property name="spacing">6</property>
+
+	      <child>
+		<widget class="GtkLabel" id="label11">
+		  <property name="visible">True</property>
+		  <property name="label" translatable="yes">&lt;b&gt;_Selected encodings&lt;/b&gt;</property>
+		  <property name="use_underline">True</property>
+		  <property name="use_markup">True</property>
+		  <property name="justify">GTK_JUSTIFY_LEFT</property>
+		  <property name="wrap">False</property>
+		  <property name="selectable">False</property>
+		  <property name="xalign">0</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xpad">0</property>
+		  <property name="ypad">0</property>
+		  <property name="mnemonic_widget">selected_encs_view</property>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">False</property>
+		  <property name="fill">False</property>
+		</packing>
+	      </child>
+
+	      <child>
+		<widget class="GtkAlignment" id="alignment9">
+		  <property name="visible">True</property>
+		  <property name="xalign">0.5</property>
+		  <property name="yalign">0.5</property>
+		  <property name="xscale">1</property>
+		  <property name="yscale">1</property>
+		  <property name="top_padding">0</property>
+		  <property name="bottom_padding">0</property>
+		  <property name="left_padding">12</property>
+		  <property name="right_padding">0</property>
+
+		  <child>
+		    <widget class="GtkHBox" id="hbox9">
+		      <property name="visible">True</property>
+		      <property name="homogeneous">False</property>
+		      <property name="spacing">6</property>
+
+		      <child>
+			<widget class="GtkAlignment" id="alignment14">
+			  <property name="visible">True</property>
+			  <property name="xalign">0.5</property>
+			  <property name="yalign">0.5</property>
+			  <property name="xscale">1</property>
+			  <property name="yscale">0</property>
+			  <property name="top_padding">0</property>
+			  <property name="bottom_padding">0</property>
+			  <property name="left_padding">0</property>
+			  <property name="right_padding">0</property>
+
+			  <child>
+			    <widget class="GtkButton" id="remove_enc_button">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="relief">GTK_RELIEF_NORMAL</property>
+			      <property name="focus_on_click">True</property>
+			      <signal name="clicked" handler="gxi_remove_enc_clicked_cb" last_modification_time="Sat, 08 Apr 2006 00:12:54 GMT"/>
+
+			      <child>
+				<widget class="GtkImage" id="image12">
+				  <property name="visible">True</property>
+				  <property name="stock">gtk-go-back</property>
+				  <property name="icon_size">4</property>
+				  <property name="xalign">0.5</property>
+				  <property name="yalign">0.5</property>
+				  <property name="xpad">0</property>
+				  <property name="ypad">0</property>
+				</widget>
+			      </child>
+			    </widget>
+			  </child>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">True</property>
+			</packing>
+		      </child>
+
+		      <child>
+			<widget class="GtkScrolledWindow" id="scrolledwindow3">
+			  <property name="visible">True</property>
+			  <property name="can_focus">True</property>
+			  <property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
+			  <property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
+			  <property name="shadow_type">GTK_SHADOW_IN</property>
+			  <property name="window_placement">GTK_CORNER_TOP_LEFT</property>
+
+			  <child>
+			    <widget class="GtkTreeView" id="selected_encs_view">
+			      <property name="visible">True</property>
+			      <property name="can_focus">True</property>
+			      <property name="headers_visible">False</property>
+			      <property name="rules_hint">False</property>
+			      <property name="reorderable">False</property>
+			      <property name="enable_search">True</property>
+			      <property name="fixed_height_mode">False</property>
+			      <signal name="row_activated" handler="gxi_selected_enc_activated_cb" last_modification_time="Sat, 08 Apr 2006 01:12:38 GMT"/>
+			    </widget>
+			  </child>
+			</widget>
+			<packing>
+			  <property name="padding">0</property>
+			  <property name="expand">False</property>
+			  <property name="fill">True</property>
+			</packing>
+		      </child>
+		    </widget>
+		  </child>
+		</widget>
+		<packing>
+		  <property name="padding">0</property>
+		  <property name="expand">True</property>
+		  <property name="fill">True</property>
+		</packing>
+	      </child>
+	    </widget>
+	    <packing>
+	      <property name="padding">0</property>
+	      <property name="expand">False</property>
+	      <property name="fill">True</property>
+	    </packing>
+	  </child>
+	</widget>
+	<packing>
+	  <property name="padding">0</property>
+	  <property name="expand">True</property>
+	  <property name="fill">True</property>
+	</packing>
+      </child>
+    </widget>
+  </child>
+</widget>
+
+</glade-interface>
-------------- next part --------------
Index: src/gnome-utils/Makefile.am
===================================================================
--- src/gnome-utils/Makefile.am	(revision 13769)
+++ src/gnome-utils/Makefile.am	(working copy)
@@ -48,6 +48,7 @@
   dialog-utils.c \
   druid-utils.c \
   druid-gconf-setup.c \
+  druid-gnc-xml-import.c \
   gnc-account-sel.c \
   gnc-amount-edit.c \
   gnc-commodity-edit.c \
@@ -116,6 +117,7 @@
   dialog-utils.h \
   druid-utils.h \
   druid-gconf-setup.h \
+  druid-gnc-xml-import.h \
   gnc-account-sel.h \
   gnc-amount-edit.h \
   gnc-commodity-edit.h \
Index: src/gnome-utils/druid-gnc-xml-import.c
===================================================================
--- src/gnome-utils/druid-gnc-xml-import.c	(revision 0)
+++ src/gnome-utils/druid-gnc-xml-import.c	(revision 0)
@@ -0,0 +1,1518 @@
+/*
+ * druid-gnc-xml-import.c --
+ * Copyright (C) 2006 Andreas Koehler <andi5.py at gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#include "config.h"
+
+#include <gnome.h>
+#include <glib/gi18n.h>
+
+#include "TransLog.h"
+#include "druid-gnc-xml-import.h"
+#include "dialog-utils.h"
+#include "druid-utils.h"
+#include "gnc-backend-file.h"
+#include "gnc-filepath-utils.h"
+#include "gnc-module.h"
+#include "gnc-ui.h"
+#include "io-gncxml-v2.h"
+
+#define XML_GLADE_FILE "druid-gnc-xml-import.glade"
+
+/* NOTE: Merge stuff is not implemented, only the result of a file parse is
+ * merged into a given session.
+ */
+
+/* NOTE: This file uses the term "encoding" even in places where it is not
+ * accurate. Please ignore that. Encodings occur in different forms:
+ * - as descriptive string, as in the list of system encodings
+ * - as string used for g_iconv_open
+ * - as GQuark, representing above string
+ * - as pointer, containing above gquark, used in lists
+ */
+
+typedef enum {
+  XML_CONVERT_SINGLE_FILE,
+  XML_MERGE_FILES
+} GncXmlImportType;
+
+typedef struct {
+  GncXmlImportType import_type;
+
+  GtkWidget *dialog;                  /* global window */
+  GtkWidget *druid;                   /* druid */
+  GtkWidget *file_chooser;            /* file chooser widget on load file page */
+  GtkWidget *default_encoding_combo;  /* top combo on conversion page */
+  GtkWidget *summary_label;           /* label on conversion page */
+  GtkWidget *string_box;              /* vbox of combos on conversion page */
+  GtkWidget *encodings_dialog;        /* dialog for selection of encodings */
+  GtkTreeView *available_encs_view;   /* list view of standard encodings */
+  GtkTreeView *selected_encs_view;    /* list view of selected encodings */
+  GtkListStore *file_list_store;      /* list store for loaded files */
+  GtkTreeView *file_list_view;        /* list view for loaded files */
+
+  GList *files;                       /* list of loaded files */
+
+  GList *encodings;                   /* list of GQuarks for encodings */
+  GQuark default_encoding;            /* default GQuark, may be zero */
+
+  /* hash table that maps byte sequences to conversions, i.e. in the current
+     encodings setting, there is only one possible conversion */
+  GHashTable *unique;
+
+  /* hash table that maps byte sequences to a list of conversions, i.e. in the
+     current encodings setting, there exactly these conversions are possible */
+  GHashTable *ambiguous_ht;
+
+  /* sorted list of ambiguous words, used for the construction of the combos */
+  GList *ambiguous_list;
+
+  /* hash table that maps byte sequences to conversions. these reflect the
+     choices the user made, accumulated and updated in the whole conversion.
+     Note: this may contain conversions that are not available in the current
+     encodings setting, just imagine, user accidentally removed an important
+     encoding from the list */
+  GHashTable *choices;
+
+  /* number of byte sequences that have multiple possible conversions, but not in
+     the default encoding. and the user has not decided yet, of course. */
+  gint n_unassigned;
+
+  /* number of byte sequences without any reasonable interpretation */
+  gint n_impossible;
+
+  /* hash table that maps byte sequences to other byte sequences to be replaced
+     by them. */
+  GHashTable *subst;
+
+  gchar *filename;
+  QofSession *session;
+} GncXmlImportData;
+
+typedef struct {
+  gchar *filename;
+  GtkTreeIter *file_list_iter;
+} GncXmlImportFile;
+
+/* used for the string combos, see ambiguous_free */
+typedef struct {
+  gchar *byte_sequence;
+  GList *conv_list;
+} ambiguous_type;
+
+enum {
+  FILE_COL_NAME = 0,
+  FILE_COL_INFO,
+  FILE_NUM_COLS
+};
+
+enum {
+  WORD_COL_STRING = 0,
+  WORD_COL_ENCODING,
+  WORD_NUM_COLS
+};
+
+enum {
+  ENC_COL_STRING = 0,
+  ENC_COL_QUARK,
+  ENC_NUM_COLS
+};
+
+static void gxi_data_destroy (GncXmlImportData *data);
+static void gxi_ambiguous_info_destroy (GncXmlImportData *data);
+static void gxi_session_destroy (GncXmlImportData *data);
+static void gxi_check_file (GncXmlImportData *data);
+static gboolean gxi_parse_file (GncXmlImportData *data);
+static gboolean gxi_save_file (GncXmlImportData *data);
+static void gxi_update_progress_bar (const gchar *message, double percentage);
+static void gxi_update_default_enc_combo (GncXmlImportData *data);
+static void gxi_update_summary_label (GncXmlImportData *data);
+static void gxi_update_string_box (GncXmlImportData *data);
+static void gxi_update_conversion_forward (GncXmlImportData *data);
+static GnomeDruidPage *gxi_get_named_page (GncXmlImportData *data,
+                                           const gchar *name);
+void gxi_dialog_destroy_cb (GtkObject *object, GncXmlImportData *data);
+void gxi_cancel_cb (GnomeDruid *druid, GncXmlImportData *data);
+void gxi_chooser_file_activated_cb (GtkFileChooser *chooser,
+                                    GncXmlImportData *data);
+gboolean gxi_load_file_next_cb (GnomeDruidPage *page, GtkWidget *widget,
+                                GncXmlImportData *data);
+void gxi_conversion_prepare_cb (GnomeDruidPage *page, GtkWidget *widget,
+                                GncXmlImportData *data);
+static void gxi_default_enc_combo_changed_cb (GtkComboBox *combo,
+                                              GncXmlImportData *data);
+static void gxi_string_combo_changed_cb (GtkComboBox *combo,
+                                         GncXmlImportData *data);
+void gxi_edit_encodings_clicked_cb (GtkButton *button, GncXmlImportData *data);
+void gxi_available_enc_activated_cb (GtkTreeView *view, GtkTreePath *path,
+                                     GtkTreeViewColumn *column,
+                                     GncXmlImportData *data);
+void gxi_add_enc_clicked_cb (GtkButton *button, GncXmlImportData *data);
+void gxi_custom_enc_activate_cb (GtkEntry *entry, GncXmlImportData *data);
+void gxi_add_custom_enc_clicked_cb (GtkButton *button, GncXmlImportData *data);
+void gxi_selected_enc_activated_cb (GtkTreeView *view, GtkTreePath *path,
+                                    GtkTreeViewColumn *column,
+                                    GncXmlImportData *data);
+void gxi_remove_enc_clicked_cb (GtkButton *button, GncXmlImportData *data);
+gboolean gxi_conversion_next_cb  (GnomeDruidPage *page, GtkWidget *widget,
+                                  GncXmlImportData *data);
+void gxi_loaded_files_prepare_cb (GnomeDruidPage *page, GtkWidget *widget,
+                                  GncXmlImportData *data);
+gboolean gxi_loaded_files_next_cb (GnomeDruidPage *page, GtkWidget *widget,
+                                   GncXmlImportData *data);
+void gxi_unload_file_clicked_cb (GtkButton *button, GncXmlImportData *data);
+void gxi_load_file_clicked_cb (GtkButton *button, GncXmlImportData *data);
+void gxi_end_finish_cb (GnomeDruidPage *page, GtkWidget *widget,
+                        GncXmlImportData *data);
+
+static const gchar *encodings_doc_string = N_(
+  "The file you tried to load does not specify an encoding, so GnuCash is "
+  "unable to unambiguously interpret it. This is typical for files created with "
+  "GnuCash prior to 2.0.\n\n"
+  "On the next page you will be asked to select the best looking decoded string "
+  "for every ambiguous word GnuCash found while trying to open your file. "
+  "Therefore GnuCash has guessed what encodings you might have used. So "
+  "probably everything will look just fine and you can simply click "
+  "'Forward'.\n\n"
+  "If it does not work for you, try to change the default encoding at the top. "
+  "Maybe you even have to edit the list of encodings by clicking on the button "
+  "in the top right corner.\n\n"
+  "The page is not overly complicated, just take the time until you feel "
+  "comfortable with it. You can always come back and read this message again.");
+
+static const gchar *encodings_doc_page_title = N_("Missing file encoding");
+
+/* The debugging module that this .o belongs to. */
+static QofLogModule log_module = GNC_MOD_GUI;
+
+/* window containing a progress bar */
+static GtkWidget *progress_window = NULL;
+static GtkProgressBar *progress_bar = NULL;
+
+/* this is used for a static tree of system encodings. encoding may be NULL.
+   parent declares how often to go up in the path of the previous element and use
+   that as parent, e.g. 0 -> child of previous, 1 -> same level as previous */
+typedef struct {
+  gchar *text;
+  gchar *encoding;
+  gint parent;
+} system_encoding_type;
+static system_encoding_type system_encodings [] =
+{
+  { N_("Unicode"),                                NULL,          2 },
+  {    "UTF-8",                                   "UTF-8",       0 },
+  { N_("European"),                               NULL,          2 },
+  { N_("ISO-8859-1 (West European)"),             "ISO-8859-1",  0 },
+  { N_("ISO-8859-2 (East European)"),             "ISO-8859-2",  1 },
+  { N_("ISO-8859-3 (South European)"),            "ISO-8859-3",  1 },
+  { N_("ISO-8859-4 (North European)"),            "ISO-8859-4",  1 },
+  { N_("ISO-8859-5 (Cyrillic)"),                  "ISO-8859-5",  1 },
+  { N_("ISO-8859-6 (Arabic)"),                    "ISO-8859-6",  1 },
+  { N_("ISO-8859-7 (Greek)"),                     "ISO-8859-7",  1 },
+  { N_("ISO-8859-8 (Hebrew)"),                    "ISO-8859-8",  1 },
+  { N_("ISO-8859-9 (Turkish)"),                   "ISO-8859-9",  1 },
+  { N_("ISO-8859-10 (Nordic)"),                   "ISO-8859-10", 1 },
+  { N_("ISO-8859-11 (Thai)"),                     "ISO-8859-11", 1 },
+  { N_("ISO-8859-13 (Baltic)"),                   "ISO-8859-13", 1 },
+  { N_("ISO-8859-14 (Celtic)"),                   "ISO-8859-14", 1 },
+  { N_("ISO-8859-15 (West European, Euro sign)"), "ISO-8859-15", 1 },
+  { N_("ISO-8859-16 (South-East European)"),      "ISO-8859-16", 1 },
+  { N_("Cyrillic"),                               NULL,          2 },
+  { N_("KOI8-R (Russian)"),                       "KOI8-R",      0 },
+  { N_("KOI8-U (Ukrainian)"),                     "KOI8-U",      1 },
+};
+static guint n_system_encodings = G_N_ELEMENTS (system_encodings);
+
+static find_ambiguous_handler find_ambiguous = NULL;
+static parse_with_subst_handler parse_with_subst = NULL;
+static GModule *allsymbols = NULL;
+
+static gboolean
+gxi_find_backend_symbols ()
+{
+  if (!allsymbols)
+    allsymbols = g_module_open (NULL, 0);
+
+  if (!g_module_symbol (allsymbols, "gnc_xml2_find_ambiguous",
+                        (gpointer*) &find_ambiguous) ||
+      !g_module_symbol (allsymbols, "gnc_xml2_parse_with_subst",
+                        (gpointer*) &parse_with_subst))
+    return FALSE;
+
+  return TRUE;
+}
+
+gboolean
+gnc_xml_convert_single_file (const gchar *filename)
+{
+  GncXmlImportData *data;
+  GtkWidget *dialog, *widget;
+  GladeXML *xml;
+  gboolean success;
+
+  g_return_val_if_fail (gxi_find_backend_symbols (), FALSE);
+
+  data = g_new0 (GncXmlImportData, 1);
+  data->import_type = XML_CONVERT_SINGLE_FILE;
+  data->filename = g_strdup (filename);
+
+  /* gather ambiguous info */
+  gxi_check_file (data);
+  if (data->n_impossible == -1)
+    return FALSE;
+
+  if (!g_hash_table_size (data->ambiguous_ht)) {
+
+    /* no ambiguous strings */
+    success =
+      gxi_parse_file (data) &&
+      gxi_save_file (data);
+
+    gxi_data_destroy (data);
+
+  } else {
+
+    /* common druid initialization */
+    xml = gnc_glade_xml_new (XML_GLADE_FILE, "GnuCash XML Import Dialog");
+
+    dialog = glade_xml_get_widget (xml, "GnuCash XML Import Dialog");
+    gtk_widget_hide ((GTK_DIALOG (dialog))->action_area);
+    data->dialog = dialog;
+    g_object_set_data_full (G_OBJECT (dialog), "xml", xml, g_object_unref);
+    glade_xml_signal_autoconnect_full (xml, gnc_glade_autoconnect_full_func,
+                                       data);
+
+    data->druid = glade_xml_get_widget (xml, "gnc_xml_import_druid");
+    gnc_druid_set_colors (GNOME_DRUID (data->druid));
+
+    /* start page, explanations */
+    widget = glade_xml_get_widget (xml, "start_page");
+    gnome_druid_page_edge_set_text (GNOME_DRUID_PAGE_EDGE (widget),
+                                    gettext (encodings_doc_string));
+    gnome_druid_page_edge_set_title (GNOME_DRUID_PAGE_EDGE (widget),
+                                     gettext (encodings_doc_page_title));
+    gtk_widget_show (widget);
+
+    gtk_widget_hide (glade_xml_get_widget (xml, "encodings_doc_page"));
+    gtk_widget_hide (glade_xml_get_widget (xml, "load_file_page"));
+    gtk_widget_hide (glade_xml_get_widget (xml, "loaded_files_page"));
+    gtk_widget_hide (glade_xml_get_widget (xml, "merge_page"));
+    gtk_widget_show (glade_xml_get_widget (xml, "end_page"));
+
+    gxi_update_default_enc_combo (data);
+    gxi_update_string_box (data);
+
+    success =
+      gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_APPLY &&
+      gxi_save_file (data);
+
+    gtk_widget_destroy (data->dialog);
+  }
+
+  return success;
+}
+
+/* this is NOT fully implemented */
+void
+gnc_xml_merge_files (void)
+{
+  GncXmlImportData *data;
+  GtkWidget *dialog, *widget, *box;
+  GladeXML *xml;
+
+  data = g_new0 (GncXmlImportData, 1);
+  data->import_type = XML_MERGE_FILES;
+
+  /* common druid initialization */
+  xml = gnc_glade_xml_new (XML_GLADE_FILE, "GnuCash XML Import Dialog");
+
+  dialog = glade_xml_get_widget (xml, "GnuCash XML Import Dialog");
+  gtk_widget_hide ((GTK_DIALOG (dialog))->action_area);
+  data->dialog = dialog;
+  g_object_set_data_full (G_OBJECT (dialog), "xml", xml, g_object_unref);
+  glade_xml_signal_autoconnect_full (xml, gnc_glade_autoconnect_full_func,
+                                     data);
+
+  data->druid = glade_xml_get_widget (xml, "gnc_xml_import_druid");
+  gnc_druid_set_colors (GNOME_DRUID (data->druid));
+
+  /* encodings explanations */
+  widget = glade_xml_get_widget (xml, "encodings_doc_label");
+  gtk_label_set_text (GTK_LABEL (widget), gettext (encodings_doc_string));
+  widget = glade_xml_get_widget (xml, "encodings_doc_page");
+  gnome_druid_page_standard_set_title (GNOME_DRUID_PAGE_STANDARD (widget),
+                                       gettext (encodings_doc_page_title));
+
+  gtk_widget_show (glade_xml_get_widget (xml, "start_page"));
+  gtk_widget_show (glade_xml_get_widget (xml, "end_page"));
+
+  /* file chooser */
+  data->file_chooser = gtk_file_chooser_widget_new (
+    GTK_FILE_CHOOSER_ACTION_OPEN);
+  box = glade_xml_get_widget (xml, "file_chooser_box");
+  gtk_box_pack_start (GTK_BOX (box), data->file_chooser, TRUE, TRUE, 0);
+  g_signal_connect (G_OBJECT (data->file_chooser), "file-activated",
+                    G_CALLBACK (gxi_chooser_file_activated_cb), data);
+  gtk_widget_show (data->file_chooser);
+
+  /* selected file list */
+  data->file_list_store = gtk_list_store_new (FILE_NUM_COLS,
+                                              G_TYPE_STRING, G_TYPE_POINTER);
+  data->file_list_view = GTK_TREE_VIEW (glade_xml_get_widget (
+                                          xml, "selected_file_list"));
+  gtk_tree_view_insert_column_with_attributes (
+    data->file_list_view, -1, NULL,
+    gtk_cell_renderer_text_new (), "text", FILE_COL_NAME, NULL);
+  gtk_tree_view_set_model (data->file_list_view,
+                           GTK_TREE_MODEL (data->file_list_store));
+  g_object_unref (data->file_list_store);
+
+  gtk_widget_show (dialog);
+}
+
+static void
+gxi_data_destroy (GncXmlImportData *data)
+{
+  if (!data)
+    return;
+
+  if (data->dialog)
+    gtk_widget_hide (data->dialog);
+
+  if (data->file_chooser) {
+    gtk_widget_destroy (data->file_chooser);
+    data->file_chooser = NULL;
+  }
+
+  if (data->filename) {
+    g_free (data->filename);
+    data->filename = NULL;
+  }
+
+  gxi_session_destroy (data);
+  gxi_ambiguous_info_destroy (data);
+
+  if (data->choices) {
+    g_hash_table_destroy (data->choices);
+    data->choices = NULL;
+  }
+
+  if (data->string_box) {
+    gtk_widget_destroy (data->string_box);
+    data->string_box = NULL;
+  }
+
+  if (data->dialog) {
+    gtk_widget_destroy (data->dialog);
+    data->dialog = NULL;
+  }
+
+  g_free (data);
+}
+
+static void
+conv_free (conv_type *conv) {
+  if (conv) {
+    g_free(conv->utf8_string);
+    g_free(conv);
+  }
+}
+
+static conv_type *
+conv_copy (const conv_type *conv) {
+  conv_type *new = NULL;
+  if (conv) {
+    new = g_new(conv_type, 1);
+    new->encoding = conv->encoding;
+    new->utf8_string = g_strdup (conv->utf8_string);
+  }
+  return new;
+}
+
+static gint
+conv_enc_cmp (const conv_type *conv, const GQuark *enc)
+{
+  return conv->encoding - *enc;
+}
+
+static const gchar *
+get_decoded_string (const ambiguous_type *amb, const GQuark enc)
+{
+  GList *found = g_list_find_custom (amb->conv_list, &enc,
+                                     (GCompareFunc) conv_enc_cmp);
+
+  if (found) {
+    return ((conv_type*) found->data)->utf8_string;
+  } else {
+    return NULL;
+  }
+}
+
+static gint
+ambiguous_cmp (const ambiguous_type *a, const ambiguous_type *b,
+               GncXmlImportData *data)
+{
+  const gchar *string_a = get_decoded_string (a, data->default_encoding);
+  const gchar *string_b = get_decoded_string (b, data->default_encoding);
+
+  if (string_a) {
+    if (string_b) {
+      /* both look good, usual compare */
+      return strcmp (string_a, string_b);
+    } else {
+      /* a look good, b not. put b to the top */
+      return 1;
+    }
+  } else {
+    if (string_b) {
+      /* b looks good, a not. pub a to the top */
+      return -1;
+    } else {
+      /* both look suboptimal, see whether one has a decision attached to it */
+      conv_type *conv_a = g_hash_table_lookup (data->choices, a->byte_sequence);
+      conv_type *conv_b = g_hash_table_lookup (data->choices, b->byte_sequence);
+      if (conv_a && !conv_b) return 1;
+      if (conv_b && !conv_a) return -1;
+      return strcmp (a->byte_sequence, b->byte_sequence);
+    }
+  }
+}
+
+static void
+ambiguous_list_insert (gchar *byte_sequence, GList *conv_list,
+                       GncXmlImportData *data)
+{
+  GList *iter;
+
+  ambiguous_type *amb = g_new (ambiguous_type, 1);
+  amb->byte_sequence = g_strdup (byte_sequence);
+  amb->conv_list = NULL;
+  for (iter = g_list_last (conv_list); iter; iter = iter->prev)
+    amb->conv_list = g_list_prepend (amb->conv_list, conv_copy (iter->data));
+
+  data->ambiguous_list = g_list_insert_sorted_with_data (
+    data->ambiguous_list, amb, (GCompareDataFunc) ambiguous_cmp, data);
+}
+
+static void
+ambiguous_free (ambiguous_type *amb) {
+  if (amb) {
+    g_free (amb->byte_sequence);
+    g_list_foreach (amb->conv_list, (GFunc) conv_free, NULL);
+    g_list_free (amb->conv_list);
+    g_free (amb);
+  }
+}
+
+static void
+gxi_ambiguous_info_destroy (GncXmlImportData *data)
+{
+  if (data->unique) {
+    g_hash_table_destroy (data->unique);
+    data->unique = NULL;
+  }
+  if (data->ambiguous_ht) {
+    g_hash_table_destroy (data->ambiguous_ht);
+    data->unique = NULL;
+  }
+  if (data->ambiguous_list) {
+    g_list_foreach (data->ambiguous_list, (GFunc) ambiguous_free, NULL);
+    g_list_free (data->ambiguous_list);
+    data->ambiguous_list = NULL;
+  }
+}
+
+static void
+gxi_session_destroy (GncXmlImportData *data)
+{
+  if (data->session) {
+    xaccLogDisable ();
+    qof_session_destroy (data->session);
+    xaccLogEnable ();
+    data->session = NULL;
+  }
+}
+
+static void
+gxi_check_file (GncXmlImportData *data)
+{
+  GError *error=NULL;
+
+  if (!data->encodings) {
+    gboolean is_utf8;
+    const gchar *locale_enc;
+    gchar *enc_string, **enc_array, **enc_cursor;
+    gpointer enc_ptr;
+    GIConv iconv;
+
+    /* first locale encoding */
+    is_utf8 = g_get_charset (&locale_enc);
+    enc_string = g_ascii_strup (locale_enc, -1);
+    enc_ptr = GUINT_TO_POINTER (g_quark_from_string (enc_string));
+    g_free (enc_string);
+    data->encodings = g_list_append (NULL, enc_ptr);
+
+    /* add utf-8 */
+    if (!is_utf8) {
+      enc_ptr = GUINT_TO_POINTER (g_quark_from_string ("UTF-8"));
+      data->encodings = g_list_append (data->encodings, enc_ptr);
+    }
+
+    /* Translators: Please insert encodings here that are typically used in your
+     * locale, separated by spaces. No need for ASCII or UTF-8, check `locale -m`
+     * for assistance with spelling. */
+    enc_array = g_strsplit (_("ISO-8859-1 KOI8-U"), " ", 0);
+
+    /* loop through typical encodings */
+    for (enc_cursor = enc_array; *enc_cursor; enc_cursor++) {
+      if (!**enc_cursor) continue;
+      enc_string = g_ascii_strup (*enc_cursor, -1);
+      enc_ptr = GUINT_TO_POINTER (g_quark_from_string (enc_string));
+
+      if (!g_list_find (data->encodings, enc_ptr)) {
+        /* test whether we like this encoding */
+        iconv = g_iconv_open ("UTF-8", enc_string);
+        if (iconv != (GIConv) -1)
+          /* we like it */
+          data->encodings = g_list_append (data->encodings, enc_ptr);
+        g_iconv_close (iconv);
+      }
+      g_free (enc_string);
+    }
+    g_strfreev (enc_array);
+  }
+
+  if (!data->default_encoding) {
+    /* choose top one */
+    data->default_encoding = GPOINTER_TO_UINT (data->encodings->data);
+  }
+
+  if (!data->choices) {
+    data->choices = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                           g_free, (GDestroyNotify) conv_free);
+  }
+
+  gxi_ambiguous_info_destroy (data);
+
+  /* analyze file */
+  data->n_impossible = (*find_ambiguous) (
+    data->filename, data->encodings, &data->unique, &data->ambiguous_ht,
+    NULL, &error);
+
+  if (data->n_impossible != -1) {
+    /* sort ambiguous words */
+    g_hash_table_foreach (data->ambiguous_ht, (GHFunc)ambiguous_list_insert,
+                          data);
+  }
+}
+
+static void
+subst_insert_amb (gchar *byte_sequence, GList *conv_list, GncXmlImportData *data)
+{
+  conv_type *choice;
+  GList *default_conv;
+  gchar *default_utf8;
+
+  if (!data->subst)
+    return;
+  choice = g_hash_table_lookup (data->choices, byte_sequence);
+  if (choice) {
+    /* user choice */
+    g_hash_table_insert (data->subst, g_strdup (byte_sequence),
+                         g_strdup (choice->utf8_string));
+  } else {
+    default_conv = g_list_find_custom (conv_list, &data->default_encoding,
+                                       (GCompareFunc) conv_enc_cmp);
+    if (default_conv) {
+      /* default conversion */
+      default_utf8 = ((conv_type*) default_conv->data)->utf8_string;
+      g_hash_table_insert (data->subst, g_strdup (byte_sequence),
+                           g_strdup (default_utf8));
+    } else {
+      /* no conversion avaiable, stop filling of subst */
+      g_hash_table_destroy (data->subst);
+      data->subst = NULL;
+    }
+  }
+}
+
+static void
+subst_insert_unique (gchar *byte_sequence, conv_type *conv,
+                     GncXmlImportData *data)
+{
+  if (!data->subst)
+    return;
+  g_hash_table_insert (data->subst, g_strdup (byte_sequence),
+                       g_strdup (conv->utf8_string));
+}
+
+static gboolean
+gxi_parse_file (GncXmlImportData *data)
+{
+  QofSession *session=NULL;
+  QofBook *book;
+  FileBackend *backend;
+  QofBackendError io_err = ERR_BACKEND_NO_ERR;
+  gchar *logpath, *message=NULL;
+  gboolean success=FALSE;
+
+  if (data->n_unassigned || data->n_impossible)
+    goto cleanup_parse_file;
+
+  /* fill subst hash table with byte sequence substitutions */
+  data->subst = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+  g_hash_table_foreach (data->ambiguous_ht, (GHFunc) subst_insert_amb, data);
+  g_hash_table_foreach (data->unique, (GHFunc) subst_insert_unique, data);
+
+  if (!data->subst)
+    goto cleanup_parse_file;
+
+  /* create a temporary QofSession */
+  gxi_session_destroy (data);
+  session = qof_session_new ();
+  data->session = session;
+  qof_session_begin (session, data->filename, TRUE, FALSE);
+  io_err = qof_session_get_error (session);
+  if (io_err != ERR_BACKEND_NO_ERR) {
+    message = _("The file could not be reopen.");
+    goto cleanup_parse_file;
+  }
+
+  logpath = xaccResolveFilePath (data->filename);
+  xaccLogSetBaseName (logpath);
+  xaccLogDisable ();
+  gxi_update_progress_bar (_("Reading file..."), 0.0);
+  qof_session_load (session, gxi_update_progress_bar);
+  gxi_update_progress_bar (NULL, -1.0);
+  xaccLogEnable ();
+
+  io_err = qof_session_get_error (session);
+  if (io_err == ERR_BACKEND_NO_ERR) {
+    /* loaded sucessfully now. strange, but ok */
+    success = TRUE;
+    goto cleanup_parse_file;
+  } else if (io_err != ERR_FILEIO_NO_ENCODING) {
+    /* another error, cannot handle this here */
+    message = _("The file could not be reopen.");
+    goto cleanup_parse_file;
+  }
+
+  qof_session_pop_error (session);
+  book = qof_session_get_book (session);
+  backend = (FileBackend*) qof_book_get_backend (book);
+
+  gxi_update_progress_bar (_("Parsing file..."), 0.0);
+  success = (*parse_with_subst) (backend, book, data->subst);
+  gxi_update_progress_bar (NULL, -1.0);
+
+  if (success)
+    data->session = session;
+  else
+    message = _("There was an error parsing the file.");
+
+ cleanup_parse_file:
+
+  if (data->subst) {
+    g_hash_table_destroy (data->subst);
+    data->subst = NULL;
+  }
+  if (message) {
+    gnc_error_dialog (data->dialog, message);
+  }
+  if (!success)
+    gxi_session_destroy (data);
+
+  return success;
+}
+
+static gboolean
+gxi_save_file (GncXmlImportData *data)
+{
+  QofBackendError io_err;
+  g_return_val_if_fail (data && data->session, FALSE);
+
+  gxi_update_progress_bar (_("Writing file..."), 0.0);
+  qof_session_save (data->session, gxi_update_progress_bar);
+  gxi_update_progress_bar (NULL, -1.0);
+
+  io_err = qof_session_get_error (data->session);
+
+  if (io_err == ERR_BACKEND_NO_ERR) {
+    return TRUE;
+  } else {
+    gxi_session_destroy (data);
+    return FALSE;
+  }
+}
+
+static void
+gxi_update_progress_bar (const gchar *message, double percentage)
+{
+  if (!progress_window) {
+    progress_window = gtk_window_new (GTK_WINDOW_POPUP);
+    progress_bar = GTK_PROGRESS_BAR (gtk_progress_bar_new ());
+    gtk_container_set_border_width (GTK_CONTAINER (progress_window), 12);
+    gtk_container_add (GTK_CONTAINER (progress_window),
+                       GTK_WIDGET (progress_bar));
+    gtk_widget_show (GTK_WIDGET (progress_bar));
+  }
+
+  if (percentage < 0) {
+    gtk_progress_bar_set_text (progress_bar, NULL);
+    gtk_progress_bar_set_fraction (progress_bar, 0.0);
+    gtk_widget_hide (progress_window);
+  } else {
+    gtk_progress_bar_set_text (progress_bar, message);
+    if (percentage <= 100)
+      gtk_progress_bar_set_fraction (progress_bar, percentage/100);
+    else
+      gtk_progress_bar_pulse (progress_bar);
+    gtk_widget_show (progress_window);
+  }
+}
+
+static void
+gxi_update_default_enc_combo (GncXmlImportData *data)
+{
+  GtkComboBox *combo;
+  GList *enc_iter;
+
+  /* add encodings list */
+  if (data->default_encoding_combo)
+    gtk_widget_destroy (data->default_encoding_combo);
+  data->default_encoding_combo = gtk_combo_box_new_text ();
+  combo = GTK_COMBO_BOX (data->default_encoding_combo);
+
+  for (enc_iter = data->encodings; enc_iter; enc_iter = enc_iter->next) {
+    gtk_combo_box_append_text (
+      combo, g_quark_to_string (GPOINTER_TO_UINT (enc_iter->data)));
+  }
+  gtk_combo_box_set_active (
+    combo,
+    g_list_index (data->encodings, GUINT_TO_POINTER (data->default_encoding)));
+
+  /* show encodings */
+  g_signal_connect (G_OBJECT (combo), "changed",
+                    G_CALLBACK (gxi_default_enc_combo_changed_cb), data);
+  gtk_container_add (GTK_CONTAINER (gnc_glade_lookup_widget (
+                                      data->druid, "default_enc_box")),
+                     GTK_WIDGET (combo));
+  gtk_widget_show (GTK_WIDGET (combo));
+}
+
+static void
+gxi_update_summary_label (GncXmlImportData *data)
+{
+  gchar *string;
+  gboolean show;
+
+  if (data->n_unassigned) {
+    if (data->n_impossible) {
+      string = g_strdup_printf (
+        _("There are %d unassigned and %d undecodable words. "
+          "Please add encodings."),
+        data->n_unassigned, data->n_impossible);
+      show = TRUE;
+    } else {
+      string = g_strdup_printf (
+        _("There are %d unassigned words. "
+          "Please decide on them or add encodings."),
+        data->n_unassigned);
+      show = TRUE;
+    }
+  } else {
+    if (data->n_impossible) {
+      string = g_strdup_printf (
+        _("There are %d undecodable words. "
+          "Please add encodings."),
+        data->n_impossible);
+      show = TRUE;
+    } else {
+      show = FALSE;
+    }
+  }
+
+  if (show) {
+    gtk_label_set_text (GTK_LABEL (data->summary_label), string);
+    g_free (string);
+    gtk_widget_show (data->summary_label);
+  } else {
+    gtk_widget_hide (data->summary_label);
+  }
+}
+
+static void
+gxi_update_string_box (GncXmlImportData *data)
+{
+  gchar *string;
+  const gchar *utf8;
+  GtkBox *vbox;
+  GtkComboBox *combo;
+  GtkListStore *store;
+  GList *word_iter, *conv_iter;
+  GtkCellRenderer *renderer;
+  GtkTreeIter iter;
+  GQuark chosen_encoding;
+  GtkTreeIter *chosen_iter, *default_iter;
+  ambiguous_type *amb;
+  conv_type *conv;
+
+  if (data->string_box)
+    gtk_widget_destroy (data->string_box);
+
+  data->string_box = gtk_vbox_new (FALSE, 6);
+  vbox = GTK_BOX (data->string_box);
+
+  data->n_unassigned = 0;
+
+  /* loop through words */
+  for (word_iter=data->ambiguous_list; word_iter; word_iter=word_iter->next) {
+
+    store = gtk_list_store_new (WORD_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER);
+    combo = GTK_COMBO_BOX (gtk_combo_box_new_with_model (
+                             GTK_TREE_MODEL (store)));
+    g_object_unref (store);
+    renderer = gtk_cell_renderer_text_new ();
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
+    gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
+                                    "text", WORD_COL_STRING, NULL);
+
+    /* add default string, if possible */
+    amb = (ambiguous_type*) word_iter->data;
+    utf8 = get_decoded_string (amb, data->default_encoding);
+    default_iter = NULL;
+    if (utf8) {
+      string = g_strdup_printf ("%s (default)", utf8);
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter, WORD_COL_STRING, string,
+                          WORD_COL_ENCODING,
+                          GUINT_TO_POINTER (data->default_encoding), -1);
+      g_free (string);
+      default_iter = gtk_tree_iter_copy (&iter);
+    }
+
+    /* user has selected this previously */
+    conv = (conv_type*) g_hash_table_lookup (data->choices, amb->byte_sequence);
+    chosen_encoding = (conv) ? conv->encoding : 0;
+    chosen_iter = NULL;
+
+    /* loop through conversions */
+    for (conv_iter = amb->conv_list; conv_iter; conv_iter = conv_iter->next) {
+      conv = (conv_type*) conv_iter->data;
+      string = g_strdup_printf ("%s (%s)", conv->utf8_string,
+                                g_quark_to_string (conv->encoding));
+      gtk_list_store_append (store, &iter);
+      gtk_list_store_set (store, &iter, WORD_COL_STRING, string,
+                          WORD_COL_ENCODING,
+                          GUINT_TO_POINTER (conv->encoding), -1);
+      g_free (string);
+
+      if (chosen_encoding && conv->encoding == chosen_encoding) {
+        chosen_iter = gtk_tree_iter_copy (&iter);
+      }
+    } /* next conversion */
+
+    if (chosen_iter) {
+      /* select previous selection again, are not we cute */
+      gtk_combo_box_set_active_iter (combo, chosen_iter);
+      gtk_tree_iter_free (chosen_iter);
+    } else {
+      if (default_iter) {
+        /* select default entry */
+        gtk_combo_box_set_active_iter (combo, default_iter);
+      } else {
+        /* count it */
+        data->n_unassigned++;
+      }
+    }
+
+    /* wire up combo */
+    g_object_set_data (G_OBJECT (combo), "ambiguous", amb);
+    g_signal_connect (G_OBJECT (combo), "changed",
+                      G_CALLBACK (gxi_string_combo_changed_cb), data);
+    gtk_box_pack_start (vbox, GTK_WIDGET (combo), FALSE, FALSE, 0);
+    gtk_widget_show (GTK_WIDGET (combo));
+
+  } /* next word */
+
+  /* wire up whole string vbox */
+  gtk_container_add (GTK_CONTAINER (gnc_glade_lookup_widget (
+                                      data->druid, "string_box_container")),
+                     GTK_WIDGET (vbox));
+  gtk_widget_show (GTK_WIDGET (vbox));
+
+  /* update label now, n_unassigned is calculated */
+  if (!data->summary_label)
+    data->summary_label = gnc_glade_lookup_widget (data->druid,
+                                                   "impossible_label");
+  gxi_update_summary_label (data);
+}
+
+static void
+gxi_update_conversion_forward (GncXmlImportData *data)
+{
+  if (data->n_unassigned || data->n_impossible)
+    gnome_druid_set_buttons_sensitive (GNOME_DRUID (data->druid),
+                                       TRUE, FALSE, TRUE, TRUE);
+  else
+    gnome_druid_set_buttons_sensitive (GNOME_DRUID (data->druid),
+                                       TRUE, TRUE, TRUE, TRUE);
+}
+
+static GnomeDruidPage *
+gxi_get_named_page (GncXmlImportData *data, const gchar *name)
+{
+  return GNOME_DRUID_PAGE (gnc_glade_lookup_widget (data->dialog, name));
+}
+
+void
+gxi_dialog_destroy_cb (GtkObject *object, GncXmlImportData *data)
+{
+  data->dialog = NULL;
+  gxi_data_destroy (data);
+}
+
+void
+gxi_cancel_cb (GnomeDruid *druid, GncXmlImportData *data)
+{
+  if (data->import_type == XML_CONVERT_SINGLE_FILE) {
+    gtk_dialog_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_CANCEL);
+  } else {
+    gtk_widget_destroy (data->dialog);
+  }
+}
+
+static gint
+file_filename_cmp (const GncXmlImportFile *file, const gchar *filename)
+{
+  return strcmp (file->filename, filename);
+}
+
+static void
+gxi_load_file (GncXmlImportData *data)
+{
+  GncXmlImportFile *file;
+  gchar *filename;
+  GtkTreeIter iter;
+
+  g_return_if_fail (data != NULL);
+
+  filename  = gtk_file_chooser_get_filename (
+    GTK_FILE_CHOOSER (data->file_chooser));
+
+  if (filename == NULL) {
+    return;
+  }
+  if (!g_file_test (filename, G_FILE_TEST_IS_REGULAR)) {
+    g_free (filename);
+    return;
+  }
+
+  if (g_list_find_custom (data->files, filename,
+                          (GCompareFunc) file_filename_cmp)) {
+    const gchar *message = _(
+      "That GnuCash XML file is already loaded. Please select another file.");
+    gnc_error_dialog (data->dialog, message);
+    g_free (filename);
+    return;
+  }
+
+  file = g_new0 (GncXmlImportFile, 1);
+  file->filename = filename;
+
+  data->files = g_list_append (data->files, file);
+
+  gtk_list_store_append (data->file_list_store, &iter);
+  gtk_list_store_set (data->file_list_store, &iter,
+                      FILE_COL_NAME, filename,
+                      FILE_COL_INFO, file,
+                      -1);
+  file->file_list_iter = gtk_tree_iter_copy (&iter);
+
+  gnome_druid_set_page (
+    GNOME_DRUID (data->druid),
+/*     gxi_get_named_page (data, "loaded_files_page")); */
+    gxi_get_named_page (data, "encodings_doc_page"));
+}
+
+static void
+gxi_unload_file (GncXmlImportData *data, GncXmlImportFile *file)
+{
+  g_return_if_fail (data != NULL && file != NULL);
+
+  data->files = g_list_remove (data->files, file);
+  gtk_list_store_remove (data->file_list_store, file->file_list_iter);
+  gtk_tree_iter_free (file->file_list_iter);
+
+  g_free (file->filename);
+}
+
+void
+gxi_chooser_file_activated_cb (GtkFileChooser *chooser, GncXmlImportData *data)
+{
+  gxi_load_file (data);
+}
+
+gboolean
+gxi_load_file_next_cb (GnomeDruidPage *page, GtkWidget *widget,
+                       GncXmlImportData *data)
+{
+  GtkFileChooser *chooser = GTK_FILE_CHOOSER (data->file_chooser);
+  gchar *filename = gtk_file_chooser_get_filename (chooser);
+
+  if (filename != NULL) {
+    if (g_file_test (filename, G_FILE_TEST_IS_DIR)) {
+      gtk_file_chooser_set_current_folder (chooser, filename);
+    } else {
+      gxi_load_file (data);
+    }
+    g_free (filename);
+  }
+
+  return TRUE;
+}
+
+void
+gxi_conversion_prepare_cb (GnomeDruidPage *page, GtkWidget *widget,
+                           GncXmlImportData *data)
+{
+  gxi_update_conversion_forward (data);
+}
+
+static void
+gxi_default_enc_combo_changed_cb (GtkComboBox *combo, GncXmlImportData *data)
+{
+  GtkTreeIter iter;
+  gchar *enc_string;
+  GQuark curr_enc;
+
+  if (!gtk_combo_box_get_active_iter (combo, &iter))
+    return;
+
+  gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter,
+                      0, &enc_string, -1);
+  curr_enc = g_quark_from_string (enc_string);
+
+  if (data->default_encoding == curr_enc)
+    return;
+  if (!g_list_find (data->encodings, GUINT_TO_POINTER (curr_enc))) {
+    /* should not happen */
+    PERR("invalid encoding selection");
+    return;
+  }
+
+  data->default_encoding = curr_enc;
+  gxi_check_file (data);
+  gxi_update_string_box (data);
+  gxi_update_conversion_forward (data);
+}
+
+static void
+gxi_string_combo_changed_cb (GtkComboBox *combo, GncXmlImportData *data)
+{
+  GtkTreeIter iter;
+  GList *found, *default_conv;
+  gboolean is_active;
+  ambiguous_type *amb;
+  conv_type *prev_conv, *curr_conv;
+  gpointer ptr;
+  GQuark prev_enc, curr_enc;
+
+  amb = (ambiguous_type*) g_object_get_data (G_OBJECT (combo), "ambiguous");
+  prev_conv = (conv_type*) g_hash_table_lookup (data->choices,
+                                                amb->byte_sequence);
+  if (prev_conv)
+    prev_enc = prev_conv->encoding;
+
+  default_conv = g_list_find_custom (amb->conv_list, &data->default_encoding,
+                                     (GCompareFunc) conv_enc_cmp);
+
+  is_active = gtk_combo_box_get_active_iter (combo, &iter);
+  if (is_active) {
+    gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter,
+                        WORD_COL_ENCODING, &ptr, -1);
+    curr_enc = GPOINTER_TO_UINT (ptr);
+    found = g_list_find_custom (amb->conv_list, &curr_enc,
+                                (GCompareFunc) conv_enc_cmp);
+    if (found) {
+      curr_conv = (conv_type*) found->data;
+    } else {
+      /* should not happen */
+      PERR("invalid string selection");
+      is_active = FALSE;
+    }
+  }
+
+  if (is_active) {
+    if (prev_conv) {
+      if (curr_enc == prev_enc)
+        return;
+
+      /* remember new choice */
+      g_hash_table_replace (data->choices, g_strdup (amb->byte_sequence),
+                            conv_copy (curr_conv));
+
+      found = g_list_find_custom (amb->conv_list, &prev_enc,
+                                  (GCompareFunc) conv_enc_cmp);
+      if (!found && !default_conv) {
+        /* user selected encoding for a byte sequence undecodable in the default
+           encoding, for the first time. previous selection is invalid now */
+        data->n_unassigned--;
+        gxi_update_summary_label (data);
+      }
+    }
+    else {
+      /* first choice ever */
+      g_hash_table_insert (data->choices, g_strdup (amb->byte_sequence),
+                           conv_copy (curr_conv));
+
+      if (!default_conv) {
+        /* user selected encoding for a byte sequence undecodable in the default
+           encoding, for the first time. no previous selection */
+        data->n_unassigned--;
+        gxi_update_summary_label (data);
+      }
+    }
+  }
+  else {
+    if (prev_conv) {
+      /* user decided not to decide... however he did that */
+      g_hash_table_remove (data->choices, amb->byte_sequence);
+
+      if (!default_conv) {
+        /* user deselected encoding for a byte sequence undecodable in the
+           default encoding */
+        data->n_unassigned++;
+        gxi_update_summary_label (data);
+      }
+    }
+    /* the missing else clause means pure ignorance of this dialog ;-) */
+  }
+}
+
+void
+gxi_edit_encodings_clicked_cb (GtkButton *button, GncXmlImportData *data)
+{
+  GladeXML *xml;
+  GtkWidget *dialog;
+  GtkListStore *list_store;
+  GtkTreeStore *tree_store;
+  GtkTreeIter iter, parent, *parent_ptr;
+  GList *encodings_bak, *enc_iter;
+  const gchar *encoding;
+  gchar *string;
+  system_encoding_type *system_enc;
+  gpointer enc_ptr;
+  gint i, j;
+
+  xml = gnc_glade_xml_new (XML_GLADE_FILE, "Encodings Dialog");
+  dialog = glade_xml_get_widget (xml, "Encodings Dialog");
+  data->encodings_dialog = dialog;
+  g_object_set_data_full (G_OBJECT (dialog), "xml", xml, g_object_unref);
+  glade_xml_signal_autoconnect_full (xml, gnc_glade_autoconnect_full_func, data);
+  gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->dialog));
+
+  data->available_encs_view = GTK_TREE_VIEW (glade_xml_get_widget (
+                                               xml, "available_encs_view"));
+
+  /* set up selected encodings list */
+  data->selected_encs_view = GTK_TREE_VIEW (glade_xml_get_widget (
+                                              xml, "selected_encs_view"));
+  list_store = gtk_list_store_new (ENC_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER);
+  for (enc_iter = data->encodings; enc_iter; enc_iter = enc_iter->next) {
+    encoding = g_quark_to_string (GPOINTER_TO_UINT (enc_iter->data));
+    gtk_list_store_append (list_store, &iter);
+    gtk_list_store_set (list_store, &iter, ENC_COL_STRING, encoding,
+                        ENC_COL_QUARK, enc_iter->data, -1);
+  }
+  gtk_tree_view_insert_column_with_attributes (
+    data->selected_encs_view, -1, NULL,
+    gtk_cell_renderer_text_new (), "text", ENC_COL_STRING, NULL);
+  gtk_tree_view_set_model (data->selected_encs_view,
+                           GTK_TREE_MODEL (list_store));
+  g_object_unref (list_store);
+
+  /* set up system encodings list */
+  data->available_encs_view = GTK_TREE_VIEW (glade_xml_get_widget (
+                                               xml, "available_encs_view"));
+  tree_store = gtk_tree_store_new (ENC_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER);
+  for (i = 0, system_enc = system_encodings;
+       i < n_system_encodings;
+       i++, system_enc++) {
+    if (i == 0) {
+      /* first system encoding */
+      parent_ptr = NULL;
+    } else {
+      parent_ptr = &iter;
+      for (j = 0; j < system_enc->parent; j++)
+        if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (tree_store),
+                                        &parent, &iter)) {
+          /* go up one level */
+          iter = parent;
+        } else {
+          /* no parent to toplevel element */
+          parent_ptr = NULL;
+        }
+    }
+    if (system_enc->encoding)
+      enc_ptr = GUINT_TO_POINTER (g_quark_from_string (system_enc->encoding));
+    else
+      enc_ptr = NULL;
+    string = gettext (system_enc->text);
+    gtk_tree_store_append (tree_store, &iter, parent_ptr);
+    gtk_tree_store_set (tree_store, &iter, ENC_COL_STRING,
+                        gettext (system_enc->text), ENC_COL_QUARK, enc_ptr, -1);
+  }
+  gtk_tree_view_insert_column_with_attributes (
+    data->available_encs_view, -1, NULL,
+    gtk_cell_renderer_text_new (), "text", ENC_COL_STRING, NULL);
+  gtk_tree_view_set_model (data->available_encs_view,
+                           GTK_TREE_MODEL (tree_store));
+  g_object_unref (tree_store);
+
+  /* run the dialog */
+  encodings_bak = g_list_copy (data->encodings);
+  if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK) {
+    g_list_free (encodings_bak);
+    if (!g_list_find (data->encodings,
+                      GUINT_TO_POINTER (data->default_encoding))) {
+      /* choose top level encoding then */
+      data->default_encoding = GPOINTER_TO_UINT (data->encodings->data);
+    }
+
+    /* update whole page */
+    gxi_check_file (data);
+    gxi_update_default_enc_combo (data);
+    gxi_update_string_box (data);
+    gxi_update_conversion_forward (data);
+  }
+  else {
+    g_list_free (data->encodings);
+    data->encodings = encodings_bak;
+  }
+
+  gtk_widget_destroy (dialog);
+  data->encodings_dialog = NULL;
+}
+
+static void
+gxi_add_encoding (GncXmlImportData *data, gpointer encoding_ptr)
+{
+  GIConv iconv;
+  const gchar *message;
+  gchar *enc_string;
+  GtkListStore *store;
+  GtkTreeIter iter;
+
+  enc_string = g_ascii_strup (
+    g_quark_to_string (GPOINTER_TO_UINT (encoding_ptr)), -1);
+  encoding_ptr = GUINT_TO_POINTER (g_quark_from_string (enc_string));
+
+  if (g_list_find (data->encodings, encoding_ptr)) {
+    message = _("This encoding has been added to the list already.");
+    gnc_error_dialog (data->encodings_dialog, message);
+    return;
+  }
+
+  /* test whether we like this encoding */
+  iconv = g_iconv_open ("UTF-8", enc_string);
+  if (iconv == (GIConv) -1) {
+    g_iconv_close (iconv);
+    g_free (enc_string);
+    message = _("This is an invalid encoding.");
+    gnc_error_dialog (data->encodings_dialog, message);
+    return;
+  }
+  g_iconv_close (iconv);
+
+  /* add to the list */
+  data->encodings = g_list_append (data->encodings, encoding_ptr);
+  store = GTK_LIST_STORE (gtk_tree_view_get_model (data->selected_encs_view));
+  gtk_list_store_append (store, &iter);
+  gtk_list_store_set (store, &iter, ENC_COL_STRING, enc_string,
+                      ENC_COL_QUARK, encoding_ptr, -1);
+
+  g_free (enc_string);
+
+  if (!data->encodings->next)
+    gtk_dialog_set_response_sensitive (GTK_DIALOG (data->encodings_dialog),
+                                       GTK_RESPONSE_OK, TRUE);
+}
+
+void
+gxi_available_enc_activated_cb (GtkTreeView *view, GtkTreePath *path,
+                                GtkTreeViewColumn *column,
+                                GncXmlImportData *data)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  gpointer enc_ptr;
+
+  model = gtk_tree_view_get_model (data->available_encs_view);
+  if (!gtk_tree_model_get_iter (model, &iter, path))
+    return;
+  gtk_tree_model_get (model, &iter, ENC_COL_QUARK, &enc_ptr, -1);
+  if (!enc_ptr)
+    return;
+  gxi_add_encoding (data, enc_ptr);
+}
+
+void
+gxi_add_enc_clicked_cb (GtkButton *button, GncXmlImportData *data)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  gpointer enc_ptr;
+
+  selection = gtk_tree_view_get_selection (data->available_encs_view);
+  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+    return;
+  gtk_tree_model_get (model, &iter, ENC_COL_QUARK, &enc_ptr, -1);
+  if (!enc_ptr)
+    return;
+  gxi_add_encoding (data, enc_ptr);
+}
+
+void
+gxi_custom_enc_activate_cb (GtkEntry *entry, GncXmlImportData *data)
+{
+  const gchar *enc_string;
+
+  enc_string = gtk_entry_get_text (entry);
+  if (!enc_string)
+    return;
+  gxi_add_encoding (data, GUINT_TO_POINTER (g_quark_from_string (enc_string)));
+}
+
+void
+gxi_add_custom_enc_clicked_cb (GtkButton *button, GncXmlImportData *data)
+{
+  GtkWidget *entry = gnc_glade_lookup_widget (data->encodings_dialog,
+                                              "custom_enc_entry");
+  gxi_custom_enc_activate_cb (GTK_ENTRY (entry), data);
+}
+
+static void
+gxi_remove_encoding (GncXmlImportData *data, GtkTreeModel *model,
+                         GtkTreeIter *iter)
+{
+  gpointer enc_ptr;
+
+  gtk_tree_model_get (model, iter, ENC_COL_QUARK, &enc_ptr, -1);
+  data->encodings = g_list_remove (data->encodings, enc_ptr);
+  gtk_list_store_remove (GTK_LIST_STORE (model), iter);
+  if (!data->encodings)
+    gtk_dialog_set_response_sensitive (GTK_DIALOG (data->encodings_dialog),
+                                       GTK_RESPONSE_OK, FALSE);
+}
+
+void
+gxi_selected_enc_activated_cb (GtkTreeView *view, GtkTreePath *path,
+                               GtkTreeViewColumn *column, GncXmlImportData *data)
+{
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  model = gtk_tree_view_get_model (data->selected_encs_view);
+  if (!gtk_tree_model_get_iter (model, &iter, path))
+    return;
+  gxi_remove_encoding (data, model, &iter);
+}
+
+void
+gxi_remove_enc_clicked_cb (GtkButton *button, GncXmlImportData *data)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+
+  selection = gtk_tree_view_get_selection (data->selected_encs_view);
+  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+    return;
+  gxi_remove_encoding (data, model, &iter);
+}
+
+gboolean
+gxi_conversion_next_cb  (GnomeDruidPage *page, GtkWidget *widget,
+                         GncXmlImportData *data)
+{
+  return !gxi_parse_file (data);
+}
+
+void
+gxi_loaded_files_prepare_cb (GnomeDruidPage *page, GtkWidget *widget,
+                             GncXmlImportData *data)
+{
+  gnome_druid_set_buttons_sensitive (GNOME_DRUID (data->druid),
+                                     FALSE, TRUE, TRUE, TRUE);
+}
+
+gboolean
+gxi_loaded_files_next_cb (GnomeDruidPage *page, GtkWidget *widget,
+                          GncXmlImportData *data)
+{
+  if (!g_list_first (data->files)) {
+    const gchar *message = _(
+      "No files to merge. Please add ones by clicking on 'Load another file'.");
+    gnc_error_dialog (data->dialog, message);
+    return TRUE;
+  }
+
+  return FALSE;
+}
+
+void
+gxi_unload_file_clicked_cb (GtkButton *button, GncXmlImportData *data)
+{
+  GtkTreeSelection *selection;
+  GtkTreeModel *model;
+  GtkTreeIter iter;
+  GncXmlImportFile *file;
+
+  selection = gtk_tree_view_get_selection (GTK_TREE_VIEW (data->file_list_view));
+  if (!gtk_tree_selection_get_selected (selection, &model, &iter))
+    return;
+
+  gtk_tree_model_get (model, &iter, FILE_COL_INFO, &file, -1);
+
+
+
+  gxi_unload_file (data, file);
+}
+
+void
+gxi_load_file_clicked_cb (GtkButton *button, GncXmlImportData *data)
+{
+  gnome_druid_set_page (GNOME_DRUID (data->druid),
+                        gxi_get_named_page (data, "load_file_page"));
+}
+
+void
+gxi_end_finish_cb (GnomeDruidPage *page, GtkWidget *widget,
+                   GncXmlImportData *data)
+{
+  if (data->import_type == XML_CONVERT_SINGLE_FILE) {
+    gtk_dialog_response (GTK_DIALOG (data->dialog), GTK_RESPONSE_APPLY);
+  } else {
+    gtk_widget_destroy (data->dialog);
+  }
+}
Index: src/gnome-utils/druid-gnc-xml-import.h
===================================================================
--- src/gnome-utils/druid-gnc-xml-import.h	(revision 0)
+++ src/gnome-utils/druid-gnc-xml-import.h	(revision 0)
@@ -0,0 +1,33 @@
+/*
+ * druid-gnc-xml-import.h --
+ * Copyright (C) 2006 Andreas Koehler <andi5.py at gmx.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, contact:
+ *
+ * Free Software Foundation           Voice:  +1-617-542-5942
+ * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
+ * Boston, MA  02110-1301,  USA       gnu at gnu.org
+ */
+
+#ifndef DRUID_GNC_XML_IMPORT_H
+#define DRUID_GNC_XML_IMPORT_H
+
+#include "qof.h"
+
+gboolean gnc_xml_convert_single_file (const gchar *filename);
+
+/* this is NOT fully implemented */
+void gnc_xml_merge_files (void);
+
+#endif /* DRUID_GNC_XML_IMPORT_H */


More information about the gnucash-devel mailing list