1 /**********************************************************************
2  * assistant-xml-encoding.c -- Conversion of old XML file
3  * Copyright (C) 2006 Andreas Koehler <andi5.py@gmx.net>
4  * Copyright (C) 2011 Robert Fewell
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of
9  * the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, contact:
18  *
19  * Free Software Foundation           Voice:  +1-617-542-5942
20  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652
21  * Boston, MA  02110-1301,  USA       gnu@gnu.org
22  *
23  **********************************************************************/
24 
25 #include <config.h>
26 
27 #include <glib/gi18n.h>
28 #include <gmodule.h>
29 
30 #include "TransLog.h"
31 #include "assistant-xml-encoding.h"
32 #include "dialog-utils.h"
33 #include "gnc-backend-xml.h"
34 #include "gnc-component-manager.h"
35 #include "gnc-uri-utils.h"
36 #include "gnc-ui.h"
37 
38 /* The following are copied from src/backend/xml/io-gncxml2-v2.h as a temporary
39  * measure to enable this to compile in the face of making changing struct
40  * FileBackend into C++ class XmlBackend, which can't be exposed to this C
41  * file. A future commit will separate the session code from the UI code in this
42  * file.
43  */
44 typedef struct
45 {
46     GQuark encoding;
47     gchar* utf8_string;
48 } conv_type;
49 
50 extern gint gnc_xml2_find_ambiguous (const gchar* filename,
51                                      GList* encodings,
52                                      GHashTable** unique,
53                                      GHashTable** ambiguous,
54                                      GList** impossible);
55 
56 extern gboolean gnc_xml2_parse_with_subst (QofBackend* xml_be, QofBook* book,
57                                            GHashTable* subst);
58 /* NOTE: This file uses the term "encoding" even in places where it is not
59  * accurate. Please ignore that. Encodings occur in different forms:
60  * - as descriptive string, as in the list of system encodings
61  * - as string used for g_iconv_open
62  * - as GQuark, representing above string
63  * - as pointer, containing above gquark, used in lists
64  */
65 
66 typedef struct
67 {
68     GtkWidget *assistant;               /* assistant */
69     gboolean  canceled;                 /* we are canceled */
70     GtkWidget *default_encoding_combo;  /* top combo on conversion page */
71     GtkWidget *default_encoding_hbox;   /* Encoding Hbox */
72     GtkWidget *summary_label;           /* label on conversion page */
73     GtkWidget *impossible_label;        /* impossible label on conversion page */
74     GtkWidget *string_box;              /* vbox of combos on conversion page */
75     GtkWidget *string_box_container;    /* container on conversion page */
76     GtkWidget *encodings_dialog;        /* dialog for selection of encodings */
77     GtkWidget *custom_enc_entry;        /* custom entry */
78     GtkTreeView *available_encs_view;   /* list view of standard encodings */
79     GtkTreeView *selected_encs_view;    /* list view of selected encodings */
80 
81     GList *encodings;                   /* list of GQuarks for encodings */
82     GQuark default_encoding;            /* default GQuark, may be zero */
83 
84     /* hash table that maps byte sequences to conversions, i.e. in the current
85        encodings setting, there is only one possible conversion */
86     GHashTable *unique;
87 
88     /* hash table that maps byte sequences to a list of conversions, i.e. in the
89        current encodings setting, there exactly these conversions are possible */
90     GHashTable *ambiguous_ht;
91 
92     /* sorted list of ambiguous words, used for the construction of the combos */
93     GList *ambiguous_list;
94 
95     /* hash table that maps byte sequences to conversions. these reflect the
96        choices the user made, accumulated and updated in the whole conversion.
97        Note: this may contain conversions that are not available in the current
98        encodings setting, just imagine, user accidentally removed an important
99        encoding from the list */
100     GHashTable *choices;
101 
102     /* number of byte sequences that have multiple possible conversions, but not in
103        the default encoding. and the user has not decided yet, of course. */
104     gint n_unassigned;
105 
106     /* number of byte sequences without any reasonable interpretation */
107     gint n_impossible;
108 
109     /* hash table that maps byte sequences to other byte sequences to be replaced
110        by them. */
111     GHashTable *subst;
112 
113     gchar *filename;
114     QofSession *session;
115 } GncXmlImportData;
116 
117 /* used for the string combos, see ambiguous_free */
118 typedef struct
119 {
120     gchar *byte_sequence;
121     GList *conv_list;
122 } ambiguous_type;
123 
124 enum
125 {
126     FILE_COL_NAME = 0,
127     FILE_COL_INFO,
128     FILE_NUM_COLS
129 };
130 
131 enum
132 {
133     WORD_COL_STRING = 0,
134     WORD_COL_ENCODING,
135     WORD_NUM_COLS
136 };
137 
138 enum
139 {
140     ENC_COL_STRING = 0,
141     ENC_COL_QUARK,
142     ENC_NUM_COLS
143 };
144 
145 
146 void gxi_prepare_cb (GtkAssistant  *assistant, GtkWidget *page, GncXmlImportData  *data);
147 void gxi_cancel_cb (GtkAssistant  *gtkassistant, GncXmlImportData *data);
148 void gxi_finish_cb (GtkAssistant  *gtkassistant, GncXmlImportData *data);
149 
150 void gxi_conversion_prepare (GtkAssistant *assistant, gpointer data );
151 void gxi_conversion_next (GtkAssistant *assistant,  gpointer data);
152 
153 static void gxi_data_destroy (GncXmlImportData *data);
154 static void gxi_ambiguous_info_destroy (GncXmlImportData *data);
155 static void gxi_session_destroy (GncXmlImportData *data);
156 static void gxi_check_file (GncXmlImportData *data);
157 static void gxi_sort_ambiguous_list (GncXmlImportData *data);
158 static gboolean gxi_parse_file (GncXmlImportData *data);
159 static gboolean gxi_save_file (GncXmlImportData *data);
160 static void gxi_update_progress_bar (const gchar *message, double percentage);
161 static void gxi_update_default_enc_combo (GncXmlImportData *data);
162 static void gxi_update_summary_label (GncXmlImportData *data);
163 static void gxi_update_string_box (GncXmlImportData *data);
164 static void gxi_update_conversion_forward (GncXmlImportData *data);
165 
166 static void gxi_default_enc_combo_changed_cb (GtkComboBox *combo, GncXmlImportData *data);
167 static void gxi_string_combo_changed_cb (GtkComboBox *combo, GncXmlImportData *data);
168 void gxi_edit_encodings_clicked_cb (GtkButton *button, GncXmlImportData *data);
169 void gxi_available_enc_activated_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, GncXmlImportData *data);
170 void gxi_add_enc_clicked_cb (GtkButton *button, GncXmlImportData *data);
171 void gxi_custom_enc_activate_cb (GtkEntry *entry, GncXmlImportData *data);
172 void gxi_add_custom_enc_clicked_cb (GtkButton *button, GncXmlImportData *data);
173 void gxi_selected_enc_activated_cb (GtkTreeView *view, GtkTreePath *path, GtkTreeViewColumn *column, GncXmlImportData *data);
174 void gxi_remove_enc_clicked_cb (GtkButton *button, GncXmlImportData *data);
175 
176 /* Translators: Run the assistant in your language to see GTK's translation of the button labels. */
177 static const gchar *encodings_doc_string = N_(
178             "\nThe file you are trying to load is from an older version of "
179             "GnuCash. The file format in the older versions was missing the "
180             "detailed specification of the character encoding being used. This "
181             "means the text in your data file could be read in multiple ambiguous "
182             "ways. This ambiguity cannot be resolved automatically, but the new "
183             "GnuCash 2.0.0 file format will include all necessary specifications so "
184             "that you do not have to go through this step again."
185             "\n\n"
186             "GnuCash will try to guess the correct character encoding for your data "
187             "file. On the next page GnuCash will show the resulting texts when "
188             "using this guess. You have to check whether the words look as "
189             "expected. Either everything looks fine and you can simply press "
190             "\"Next\". Or the words contain unexpected characters, in which "
191             "case you should select different character encodings to see "
192             "different results. You may have to edit the list of character "
193             "encodings by clicking on the respective button."
194             "\n\n"
195             "Press \"Next\" now to select the correct character encoding for "
196             "your data file.\n");
197 
198 static const gchar *encodings_doc_page_title = N_("Ambiguous character encoding");
199 
200 static const gchar *finish_convert_string = N_(
201             "The file has been loaded successfully. If you click \"Apply\" it will be saved "
202             "and reloaded into the main application. That way you will have a working "
203             "file as backup in the same directory.\n\n"
204             "You can also go back and verify your selections by clicking on \"Back\".");
205 
206 /* The debugging module that this .o belongs to. */
207 static QofLogModule log_module = GNC_MOD_ASSISTANT;
208 
209 /* window containing a progress bar */
210 static GtkWidget *progress_window = NULL;
211 static GtkProgressBar *progress_bar = NULL;
212 
213 /* this is used for a static tree of system encodings. encoding may be NULL.
214    parent declares how often to go up in the path of the previous element and use
215    that as parent, e.g. 0 -> child of previous, 1 -> same level as previous */
216 typedef struct
217 {
218     gchar *text;
219     gchar *encoding;
220     gint parent;
221 } system_encoding_type;
222 static system_encoding_type system_encodings [] =
223 {
224     { N_("Unicode"),                                NULL,          2 },
225     {    "UTF-8",                                   "UTF-8",       0 },
226     { N_("European"),                               NULL,          2 },
227     { N_("ISO-8859-1 (West European)"),             "ISO-8859-1",  0 },
228     { N_("ISO-8859-2 (East European)"),             "ISO-8859-2",  1 },
229     { N_("ISO-8859-3 (South European)"),            "ISO-8859-3",  1 },
230     { N_("ISO-8859-4 (North European)"),            "ISO-8859-4",  1 },
231     { N_("ISO-8859-5 (Cyrillic)"),                  "ISO-8859-5",  1 },
232     { N_("ISO-8859-6 (Arabic)"),                    "ISO-8859-6",  1 },
233     { N_("ISO-8859-7 (Greek)"),                     "ISO-8859-7",  1 },
234     { N_("ISO-8859-8 (Hebrew)"),                    "ISO-8859-8",  1 },
235     { N_("ISO-8859-9 (Turkish)"),                   "ISO-8859-9",  1 },
236     { N_("ISO-8859-10 (Nordic)"),                   "ISO-8859-10", 1 },
237     { N_("ISO-8859-11 (Thai)"),                     "ISO-8859-11", 1 },
238     { N_("ISO-8859-13 (Baltic)"),                   "ISO-8859-13", 1 },
239     { N_("ISO-8859-14 (Celtic)"),                   "ISO-8859-14", 1 },
240     { N_("ISO-8859-15 (West European, Euro sign)"), "ISO-8859-15", 1 },
241     { N_("ISO-8859-16 (South-East European)"),      "ISO-8859-16", 1 },
242     { N_("Cyrillic"),                               NULL,          2 },
243     { N_("KOI8-R (Russian)"),                       "KOI8-R",      0 },
244     { N_("KOI8-U (Ukrainian)"),                     "KOI8-U",      1 },
245 };
246 static guint n_system_encodings = G_N_ELEMENTS (system_encodings);
247 
248 
gxi_prepare_cb(GtkAssistant * assistant,GtkWidget * page,GncXmlImportData * data)249 void gxi_prepare_cb (GtkAssistant  *assistant, GtkWidget *page,
250                      GncXmlImportData  *data)
251 {
252     switch (gtk_assistant_get_current_page(assistant))
253     {
254     case 1:
255         /* Current page is the Conversion page */
256         gxi_conversion_prepare (assistant, data);
257         break;
258     case 2:
259         /* Current page is final page */
260         gxi_conversion_next (assistant, data);
261         break;
262     }
263 }
264 
265 void
gxi_finish_cb(GtkAssistant * assistant,GncXmlImportData * data)266 gxi_finish_cb (GtkAssistant *assistant, GncXmlImportData *data)
267 {
268     gtk_main_quit();
269 }
270 
271 static void
gxi_update_conversion_forward(GncXmlImportData * data)272 gxi_update_conversion_forward (GncXmlImportData *data)
273 {
274     GtkAssistant *assistant = GTK_ASSISTANT(data->assistant);
275     gint num = gtk_assistant_get_current_page (assistant);
276     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
277 
278     if (data->n_unassigned || data->n_impossible)
279         gtk_assistant_set_page_complete (assistant, page, FALSE);
280     else
281         gtk_assistant_set_page_complete (assistant, page, TRUE);
282 }
283 
284 void
gxi_cancel_cb(GtkAssistant * gtkassistant,GncXmlImportData * data)285 gxi_cancel_cb (GtkAssistant *gtkassistant, GncXmlImportData *data)
286 {
287     gnc_suspend_gui_refresh ();
288     data->canceled = TRUE;
289     gnc_resume_gui_refresh ();
290     gtk_main_quit();
291 }
292 
293 /***************************************************/
294 
295 gboolean
gnc_xml_convert_single_file(const gchar * filename)296 gnc_xml_convert_single_file (const gchar *filename)
297 {
298     GncXmlImportData *data;
299     GtkWidget *widget;
300     GtkBuilder *builder;
301     gboolean success;
302 
303     data = g_new0 (GncXmlImportData, 1);
304     data->filename = gnc_uri_get_path (filename);
305     data->canceled = FALSE;
306 
307     /* gather ambiguous info */
308     gxi_check_file (data);
309     if (data->n_impossible == -1)
310         return FALSE;
311 
312     if (!g_hash_table_size (data->ambiguous_ht))
313     {
314         /* no ambiguous strings */
315         success = gxi_parse_file (data) &&
316                   gxi_save_file (data);
317 
318         gxi_data_destroy (data);
319     }
320     else
321     {
322         /* common assistant initialization */
323         builder = gtk_builder_new();
324         gnc_builder_add_from_file  (builder , "assistant-xml-encoding.glade", "assistant_xml_encoding");
325         data->assistant = GTK_WIDGET(gtk_builder_get_object (builder, "assistant_xml_encoding"));
326 
327         /* Enable buttons on all pages. */
328         gtk_assistant_set_page_complete (GTK_ASSISTANT (data->assistant),
329                                          GTK_WIDGET(gtk_builder_get_object(builder, "start_page")),
330                                          TRUE);
331         gtk_assistant_set_page_complete (GTK_ASSISTANT (data->assistant),
332                                          GTK_WIDGET(gtk_builder_get_object(builder, "conversion_page")),
333                                          TRUE);
334         gtk_assistant_set_page_complete (GTK_ASSISTANT (data->assistant),
335                                          GTK_WIDGET(gtk_builder_get_object(builder, "end_page")),
336                                          TRUE);
337 
338         /* start page, explanations */
339         gtk_assistant_set_page_title (GTK_ASSISTANT(data->assistant),
340                                       gtk_assistant_get_nth_page (GTK_ASSISTANT(data->assistant), 0),
341                                       gettext(encodings_doc_page_title));
342 
343         widget = GTK_WIDGET(gtk_builder_get_object (builder, "start_page"));
344         gtk_label_set_text (GTK_LABEL(widget), gettext (encodings_doc_string));
345 
346         /* conversion page */
347         data->default_encoding_hbox = GTK_WIDGET(gtk_builder_get_object (builder, "default_enc_box"));
348         data->string_box_container = GTK_WIDGET(gtk_builder_get_object (builder, "string_box_container"));
349         data->impossible_label = GTK_WIDGET(gtk_builder_get_object (builder, "impossible_label"));
350 
351         /* finish page */
352         widget = GTK_WIDGET(gtk_builder_get_object(builder, "end_page"));
353         gtk_label_set_text (GTK_LABEL(widget), gettext (finish_convert_string));
354 
355         gtk_builder_connect_signals(builder, data);
356 
357         gtk_widget_show_all (data->assistant);
358 
359         gxi_update_default_enc_combo (data);
360         gxi_update_string_box (data);
361 
362         g_object_unref(G_OBJECT(builder));
363 
364         /* This won't return until the assistant is finished */
365         gtk_main();
366 
367         if (data->canceled)
368             success = FALSE;
369         else
370             success = gxi_save_file (data);
371     }
372 
373     /* destroy all the data variables */
374     gxi_data_destroy (data);
375     g_free (data);
376 
377     return success;
378 }
379 
380 static void
gxi_data_destroy(GncXmlImportData * data)381 gxi_data_destroy (GncXmlImportData *data)
382 {
383     if (!data)
384         return;
385 
386     if (data->filename)
387     {
388         g_free (data->filename);
389         data->filename = NULL;
390     }
391 
392     gxi_session_destroy (data);
393     gxi_ambiguous_info_destroy (data);
394 
395     if (data->choices)
396     {
397         g_hash_table_destroy (data->choices);
398         data->choices = NULL;
399     }
400 
401     if (data->string_box)
402     {
403         gtk_widget_destroy (data->string_box);
404         data->string_box = NULL;
405     }
406 
407     if (data->assistant)
408     {
409         gtk_widget_destroy (data->assistant);
410         data->assistant = NULL;
411     }
412 }
413 
414 static void
conv_free(conv_type * conv)415 conv_free (conv_type *conv)
416 {
417     if (conv)
418     {
419         g_free(conv->utf8_string);
420         g_free(conv);
421     }
422 }
423 
424 static conv_type *
conv_copy(const conv_type * conv)425 conv_copy (const conv_type *conv)
426 {
427     conv_type *new_type = NULL;
428     if (conv)
429     {
430         new_type = g_new(conv_type, 1);
431         new_type->encoding = conv->encoding;
432         new_type->utf8_string = g_strdup (conv->utf8_string);
433     }
434     return new_type;
435 }
436 
437 static gint
conv_enc_cmp(const conv_type * conv,const GQuark * enc)438 conv_enc_cmp (const conv_type *conv, const GQuark *enc)
439 {
440     return conv->encoding - *enc;
441 }
442 
443 static const gchar *
get_decoded_string(const ambiguous_type * amb,const GQuark enc)444 get_decoded_string (const ambiguous_type *amb, const GQuark enc)
445 {
446     GList *found = g_list_find_custom (amb->conv_list, &enc,
447                                        (GCompareFunc) conv_enc_cmp);
448 
449     if (found)
450     {
451         return ((conv_type*) found->data)->utf8_string;
452     }
453     else
454     {
455         return NULL;
456     }
457 }
458 
459 static gint
ambiguous_cmp(const ambiguous_type * a,const ambiguous_type * b,GncXmlImportData * data)460 ambiguous_cmp (const ambiguous_type *a, const ambiguous_type *b,
461                GncXmlImportData *data)
462 {
463     const gchar *string_a = get_decoded_string (a, data->default_encoding);
464     const gchar *string_b = get_decoded_string (b, data->default_encoding);
465 
466     if (string_a)
467     {
468         if (string_b)
469         {
470             /* both look good, usual compare */
471             return strcmp (string_a, string_b);
472         }
473         else
474         {
475             /* a look good, b not. put b to the top */
476             return 1;
477         }
478     }
479     else
480     {
481         if (string_b)
482         {
483             /* b looks good, a not. put a to the top */
484             return -1;
485         }
486         else
487         {
488             /* both look suboptimal, see whether one has a decision attached to it */
489             conv_type *conv_a = g_hash_table_lookup (data->choices, a->byte_sequence);
490             conv_type *conv_b = g_hash_table_lookup (data->choices, b->byte_sequence);
491             if (conv_a && !conv_b) return 1;
492             if (conv_b && !conv_a) return -1;
493             return strcmp (a->byte_sequence, b->byte_sequence);
494         }
495     }
496 }
497 
498 static void
ambiguous_list_insert(gchar * byte_sequence,GList * conv_list,GncXmlImportData * data)499 ambiguous_list_insert (gchar *byte_sequence, GList *conv_list,
500                        GncXmlImportData *data)
501 {
502     GList *iter;
503 
504     ambiguous_type *amb = g_new (ambiguous_type, 1);
505     amb->byte_sequence = g_strdup (byte_sequence);
506     amb->conv_list = NULL;
507     for (iter = g_list_last (conv_list); iter; iter = iter->prev)
508         amb->conv_list = g_list_prepend (amb->conv_list, conv_copy (iter->data));
509 
510     data->ambiguous_list = g_list_prepend (data->ambiguous_list, amb);
511 }
512 
513 static void
ambiguous_free(ambiguous_type * amb)514 ambiguous_free (ambiguous_type *amb)
515 {
516     if (amb)
517     {
518         g_free (amb->byte_sequence);
519         g_list_foreach (amb->conv_list, (GFunc) conv_free, NULL);
520         g_list_free (amb->conv_list);
521         g_free (amb);
522     }
523 }
524 
525 static void
gxi_ambiguous_info_destroy(GncXmlImportData * data)526 gxi_ambiguous_info_destroy (GncXmlImportData *data)
527 {
528     if (data->unique)
529     {
530         g_hash_table_destroy (data->unique);
531         data->unique = NULL;
532     }
533     if (data->ambiguous_ht)
534     {
535         g_hash_table_destroy (data->ambiguous_ht);
536         data->ambiguous_ht = NULL;
537     }
538     if (data->ambiguous_list)
539     {
540         g_list_foreach (data->ambiguous_list, (GFunc) ambiguous_free, NULL);
541         g_list_free (data->ambiguous_list);
542         data->ambiguous_list = NULL;
543     }
544 }
545 
546 static void
gxi_session_destroy(GncXmlImportData * data)547 gxi_session_destroy (GncXmlImportData *data)
548 {
549     if (data->session)
550     {
551         xaccLogDisable ();
552         qof_session_destroy (data->session);
553         xaccLogEnable ();
554         data->session = NULL;
555     }
556 }
557 
558 static void
gxi_sort_ambiguous_list(GncXmlImportData * data)559 gxi_sort_ambiguous_list (GncXmlImportData *data)
560 {
561     data->ambiguous_list = g_list_sort_with_data (
562                                data->ambiguous_list, (GCompareDataFunc) ambiguous_cmp, data);
563 
564 }
565 
566 static void
subst_insert_amb(gchar * byte_sequence,GList * conv_list,GncXmlImportData * data)567 subst_insert_amb (gchar *byte_sequence, GList *conv_list, GncXmlImportData *data)
568 {
569     conv_type *choice;
570     GList *default_conv;
571     gchar *default_utf8;
572 
573     if (!data->subst)
574         return;
575     choice = g_hash_table_lookup (data->choices, byte_sequence);
576     if (choice)
577     {
578         /* user choice */
579         g_hash_table_insert (data->subst, g_strdup (byte_sequence),
580                              g_strdup (choice->utf8_string));
581     }
582     else
583     {
584         default_conv = g_list_find_custom (conv_list, &data->default_encoding,
585                                            (GCompareFunc) conv_enc_cmp);
586         if (default_conv)
587         {
588             /* default conversion */
589             default_utf8 = ((conv_type*) default_conv->data)->utf8_string;
590             g_hash_table_insert (data->subst, g_strdup (byte_sequence),
591                                  g_strdup (default_utf8));
592         }
593         else
594         {
595             /* no conversion available, stop filling of subst */
596             g_hash_table_destroy (data->subst);
597             data->subst = NULL;
598         }
599     }
600 }
601 
602 static void
subst_insert_unique(gchar * byte_sequence,conv_type * conv,GncXmlImportData * data)603 subst_insert_unique (gchar *byte_sequence, conv_type *conv,
604                      GncXmlImportData *data)
605 {
606     if (!data->subst)
607         return;
608     g_hash_table_insert (data->subst, g_strdup (byte_sequence),
609                          g_strdup (conv->utf8_string));
610 }
611 
612 static void
gxi_update_progress_bar(const gchar * message,double percentage)613 gxi_update_progress_bar (const gchar *message, double percentage)
614 {
615     if (!progress_window)
616     {
617         progress_window = gtk_window_new (GTK_WINDOW_POPUP);
618         progress_bar = GTK_PROGRESS_BAR (gtk_progress_bar_new ());
619         gtk_container_set_border_width (GTK_CONTAINER (progress_window), 12);
620         gtk_container_add (GTK_CONTAINER (progress_window),
621                            GTK_WIDGET (progress_bar));
622         gtk_widget_show (GTK_WIDGET (progress_bar));
623     }
624 
625     if (percentage < 0)
626     {
627         gtk_progress_bar_set_text (progress_bar, NULL);
628         gtk_progress_bar_set_fraction (progress_bar, 0.0);
629         gtk_widget_hide (progress_window);
630     }
631     else
632     {
633         gtk_progress_bar_set_text (progress_bar, message);
634         if (percentage <= 100)
635             gtk_progress_bar_set_fraction (progress_bar, percentage / 100);
636         else
637             gtk_progress_bar_pulse (progress_bar);
638         gtk_widget_show (progress_window);
639     }
640 }
641 
642 static void
gxi_update_default_enc_combo(GncXmlImportData * data)643 gxi_update_default_enc_combo (GncXmlImportData *data)
644 {
645     GtkComboBoxText *combo;
646     GList *enc_iter;
647 
648     /* add encodings list */
649     if (data->default_encoding_combo)
650         gtk_widget_destroy (data->default_encoding_combo);
651     data->default_encoding_combo = gtk_combo_box_text_new();
652     combo = GTK_COMBO_BOX_TEXT (data->default_encoding_combo);
653 
654     for (enc_iter = data->encodings; enc_iter; enc_iter = enc_iter->next)
655     {
656         gtk_combo_box_text_append_text (
657             combo, g_quark_to_string (GPOINTER_TO_UINT (enc_iter->data)));
658     }
659     gtk_combo_box_set_active (GTK_COMBO_BOX(combo),
660         g_list_index (data->encodings, GUINT_TO_POINTER (data->default_encoding)));
661 
662     /* show encodings */
663     g_signal_connect (G_OBJECT (combo), "changed",
664                       G_CALLBACK (gxi_default_enc_combo_changed_cb), data);
665     gtk_container_add (GTK_CONTAINER (data->default_encoding_hbox), GTK_WIDGET (combo));
666     gtk_widget_show (GTK_WIDGET (combo));
667 }
668 
669 static void
gxi_update_summary_label(GncXmlImportData * data)670 gxi_update_summary_label (GncXmlImportData *data)
671 {
672     gchar *string = NULL;
673     gboolean show = FALSE;
674 
675     if (data->n_unassigned)
676     {
677         if (data->n_impossible)
678         {
679             string = g_strdup_printf (
680                          _("There are %d unassigned and %d undecodable words. "
681                            "Please add encodings."),
682                          data->n_unassigned, data->n_impossible);
683             show = TRUE;
684         }
685         else
686         {
687             string = g_strdup_printf (
688                          _("There are %d unassigned words. "
689                            "Please decide on them or add encodings."),
690                          data->n_unassigned);
691             show = TRUE;
692         }
693     }
694     else
695     {
696         if (data->n_impossible)
697         {
698             string = g_strdup_printf (
699                          _("There are %d undecodable words. "
700                            "Please add encodings."),
701                          data->n_impossible);
702             show = TRUE;
703         }
704         else
705         {
706             show = FALSE;
707         }
708     }
709 
710     if (show)
711     {
712         gtk_label_set_text (GTK_LABEL (data->summary_label), string);
713         g_free (string);
714         gtk_widget_show (data->summary_label);
715     }
716     else
717     {
718         gtk_widget_hide (data->summary_label);
719     }
720 }
721 
722 static void
gxi_update_string_box(GncXmlImportData * data)723 gxi_update_string_box (GncXmlImportData *data)
724 {
725     gchar *string;
726     const gchar *utf8;
727     GtkBox *vbox;
728     GtkComboBox *combo;
729     GtkListStore *store;
730     GList *word_iter, *conv_iter;
731     GtkCellRenderer *renderer;
732     GtkTreeIter iter;
733     GQuark chosen_encoding;
734     GtkTreeIter *chosen_iter, *default_iter;
735     ambiguous_type *amb;
736     conv_type *conv;
737 
738     if (data->string_box)
739         gtk_widget_destroy (data->string_box);
740 
741     data->string_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
742     gtk_box_set_homogeneous (GTK_BOX (data->string_box), FALSE);
743 
744     vbox = GTK_BOX (data->string_box);
745 
746     data->n_unassigned = 0;
747 
748     /* loop through words */
749     for (word_iter = data->ambiguous_list; word_iter; word_iter = word_iter->next)
750     {
751 
752         store = gtk_list_store_new (WORD_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER);
753         combo = GTK_COMBO_BOX (gtk_combo_box_new_with_model (
754                                    GTK_TREE_MODEL (store)));
755         g_object_unref (store);
756         renderer = gtk_cell_renderer_text_new ();
757         gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (combo), renderer, TRUE);
758         gtk_cell_layout_set_attributes (GTK_CELL_LAYOUT (combo), renderer,
759                                         "text", WORD_COL_STRING, NULL);
760 
761         /* add default string, if possible */
762         amb = (ambiguous_type*) word_iter->data;
763         utf8 = get_decoded_string (amb, data->default_encoding);
764         default_iter = NULL;
765         if (utf8)
766         {
767             string = g_strdup_printf ("%s (default)", utf8);
768             gtk_list_store_append (store, &iter);
769             gtk_list_store_set (store, &iter, WORD_COL_STRING, string,
770                                 WORD_COL_ENCODING,
771                                 GUINT_TO_POINTER (data->default_encoding), -1);
772             g_free (string);
773             default_iter = gtk_tree_iter_copy (&iter);
774         }
775 
776         /* user has selected this previously */
777         conv = (conv_type*) g_hash_table_lookup (data->choices, amb->byte_sequence);
778         chosen_encoding = (conv) ? conv->encoding : 0;
779         chosen_iter = NULL;
780 
781         /* loop through conversions */
782         for (conv_iter = amb->conv_list; conv_iter; conv_iter = conv_iter->next)
783         {
784             conv = (conv_type*) conv_iter->data;
785             string = g_strdup_printf ("%s (%s)", conv->utf8_string,
786                                       g_quark_to_string (conv->encoding));
787             gtk_list_store_append (store, &iter);
788             gtk_list_store_set (store, &iter, WORD_COL_STRING, string,
789                                 WORD_COL_ENCODING,
790                                 GUINT_TO_POINTER (conv->encoding), -1);
791             g_free (string);
792 
793             if (chosen_encoding && conv->encoding == chosen_encoding)
794             {
795                 chosen_iter = gtk_tree_iter_copy (&iter);
796             }
797         } /* next conversion */
798 
799         if (chosen_iter)
800         {
801             /* select previous selection again, are not we cute */
802             gtk_combo_box_set_active_iter (combo, chosen_iter);
803             gtk_tree_iter_free (chosen_iter);
804         }
805         else
806         {
807             if (default_iter)
808             {
809                 /* select default entry */
810                 gtk_combo_box_set_active_iter (combo, default_iter);
811             }
812             else
813             {
814                 /* count it */
815                 data->n_unassigned++;
816             }
817         }
818 
819         /* wire up combo */
820         g_object_set_data (G_OBJECT (combo), "ambiguous", amb);
821         g_signal_connect (G_OBJECT (combo), "changed",
822                           G_CALLBACK (gxi_string_combo_changed_cb), data);
823         gtk_box_pack_start (vbox, GTK_WIDGET (combo), FALSE, FALSE, 0);
824         gtk_widget_show (GTK_WIDGET (combo));
825 
826     } /* next word */
827 
828     /* wire up whole string vbox */
829     gtk_container_add (GTK_CONTAINER (data->string_box_container), GTK_WIDGET (vbox));
830     gtk_widget_show (GTK_WIDGET (vbox));
831 
832     /* update label now, n_unassigned is calculated */
833     if (!data->summary_label)
834         data->summary_label = data->impossible_label;
835     gxi_update_summary_label (data);
836 }
837 
838 void
gxi_conversion_prepare(GtkAssistant * assistant,gpointer user_data)839 gxi_conversion_prepare (GtkAssistant *assistant, gpointer user_data )
840 {
841     GncXmlImportData *data = user_data;
842 
843     gxi_update_string_box (data);
844     gxi_update_conversion_forward (data);
845 }
846 
847 static void
gxi_default_enc_combo_changed_cb(GtkComboBox * combo,GncXmlImportData * data)848 gxi_default_enc_combo_changed_cb (GtkComboBox *combo, GncXmlImportData *data)
849 {
850     GtkTreeIter iter;
851     gchar *enc_string;
852     GQuark curr_enc;
853 
854     if (!gtk_combo_box_get_active_iter (combo, &iter))
855         return;
856 
857     gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter,
858                         0, &enc_string, -1);
859     curr_enc = g_quark_from_string (enc_string);
860     g_free (enc_string);
861 
862     if (data->default_encoding == curr_enc)
863         return;
864     if (!g_list_find (data->encodings, GUINT_TO_POINTER (curr_enc)))
865     {
866         /* should not happen */
867         PERR("invalid encoding selection");
868         return;
869     }
870 
871     data->default_encoding = curr_enc;
872     gxi_sort_ambiguous_list (data);
873     gxi_update_string_box (data);
874     gxi_update_conversion_forward (data);
875 }
876 
877 static void
gxi_string_combo_changed_cb(GtkComboBox * combo,GncXmlImportData * data)878 gxi_string_combo_changed_cb (GtkComboBox *combo, GncXmlImportData *data)
879 {
880     GtkTreeIter iter;
881     GList *found, *default_conv;
882     gboolean is_active;
883     ambiguous_type *amb;
884     conv_type *prev_conv, *curr_conv = NULL;
885     gpointer ptr;
886     GQuark prev_enc, curr_enc;
887 
888     amb = (ambiguous_type*) g_object_get_data (G_OBJECT (combo), "ambiguous");
889     prev_conv = (conv_type*) g_hash_table_lookup (data->choices,
890                 amb->byte_sequence);
891     if (prev_conv)
892         prev_enc = prev_conv->encoding;
893 
894     default_conv = g_list_find_custom (amb->conv_list, &data->default_encoding,
895                                        (GCompareFunc) conv_enc_cmp);
896 
897     is_active = gtk_combo_box_get_active_iter (combo, &iter);
898     if (is_active)
899     {
900         gtk_tree_model_get (gtk_combo_box_get_model (combo), &iter,
901                             WORD_COL_ENCODING, &ptr, -1);
902         curr_enc = GPOINTER_TO_UINT (ptr);
903         found = g_list_find_custom (amb->conv_list, &curr_enc,
904                                     (GCompareFunc) conv_enc_cmp);
905         if (found)
906         {
907             curr_conv = (conv_type*) found->data;
908         }
909         else
910         {
911             /* should not happen */
912             PERR("invalid string selection");
913             is_active = FALSE;
914         }
915     }
916 
917     if (is_active)
918     {
919         if (prev_conv)
920         {
921             if (curr_enc == prev_enc)
922                 return;
923 
924             /* remember new choice */
925             g_hash_table_replace (data->choices, g_strdup (amb->byte_sequence),
926                                   conv_copy (curr_conv));
927 
928             found = g_list_find_custom (amb->conv_list, &prev_enc,
929                                         (GCompareFunc) conv_enc_cmp);
930             if (!found && !default_conv)
931             {
932                 /* user selected encoding for a byte sequence undecodable in the default
933                    encoding, for the first time. previous selection is invalid now */
934                 data->n_unassigned--;
935                 gxi_update_summary_label (data);
936                 gxi_update_conversion_forward (data);
937             }
938         }
939         else
940         {
941             /* first choice ever */
942             g_hash_table_insert (data->choices, g_strdup (amb->byte_sequence),
943                                  conv_copy (curr_conv));
944 
945             if (!default_conv)
946             {
947                 /* user selected encoding for a byte sequence undecodable in the default
948                    encoding, for the first time. no previous selection */
949                 data->n_unassigned--;
950                 gxi_update_summary_label (data);
951                 gxi_update_conversion_forward (data);
952             }
953         }
954     }
955     else
956     {
957         if (prev_conv)
958         {
959             /* user decided not to decide... however he did that */
960             g_hash_table_remove (data->choices, amb->byte_sequence);
961 
962             if (!default_conv)
963             {
964                 /* user deselected encoding for a byte sequence undecodable in the
965                    default encoding */
966                 data->n_unassigned++;
967                 gxi_update_summary_label (data);
968                 gxi_update_conversion_forward (data);
969             }
970         }
971         /* the missing else clause means pure ignorance of this dialog ;-) */
972     }
973 }
974 
975 void
gxi_conversion_next(GtkAssistant * assistant,gpointer user_data)976 gxi_conversion_next (GtkAssistant *assistant, gpointer user_data)
977 {
978     GncXmlImportData *data = user_data;
979     gxi_parse_file (data);
980 }
981 
982 static void
gxi_check_file(GncXmlImportData * data)983 gxi_check_file (GncXmlImportData *data)
984 {
985     if (!data->encodings)
986     {
987         gboolean is_utf8;
988         const gchar *locale_enc;
989         gchar *enc_string, **enc_array, **enc_cursor;
990         gpointer enc_ptr;
991         GIConv iconv;
992 
993         /* first locale encoding */
994         is_utf8 = g_get_charset (&locale_enc);
995         enc_string = g_ascii_strup (locale_enc, -1);
996         enc_ptr = GUINT_TO_POINTER (g_quark_from_string (enc_string));
997         g_free (enc_string);
998         data->encodings = g_list_append (NULL, enc_ptr);
999 
1000         /* add utf-8 */
1001         if (!is_utf8)
1002         {
1003             enc_ptr = GUINT_TO_POINTER (g_quark_from_string ("UTF-8"));
1004             data->encodings = g_list_append (data->encodings, enc_ptr);
1005         }
1006 
1007         /* Translators: Please insert encodings here that are typically used in your
1008            locale, separated by spaces. No need for ASCII or UTF-8, check 'locale -m'
1009            for assistance with spelling. */
1010         enc_array = g_strsplit (_("ISO-8859-1 KOI8-U"), " ", 0);
1011 
1012         /* loop through typical encodings */
1013         for (enc_cursor = enc_array; *enc_cursor; enc_cursor++)
1014         {
1015             if (!**enc_cursor) continue;
1016             enc_string = g_ascii_strup (*enc_cursor, -1);
1017             enc_ptr = GUINT_TO_POINTER (g_quark_from_string (enc_string));
1018 
1019             if (!g_list_find (data->encodings, enc_ptr))
1020             {
1021                 /* test whether we like this encoding */
1022                 iconv = g_iconv_open ("UTF-8", enc_string);
1023                 if (iconv != (GIConv) - 1)
1024                     /* we like it */
1025                     data->encodings = g_list_append (data->encodings, enc_ptr);
1026                 g_iconv_close (iconv);
1027             }
1028             g_free (enc_string);
1029         }
1030         g_strfreev (enc_array);
1031     }
1032 
1033     if (!data->default_encoding)
1034     {
1035         /* choose top one */
1036         data->default_encoding = GPOINTER_TO_UINT (data->encodings->data);
1037     }
1038 
1039     if (!data->choices)
1040     {
1041         data->choices = g_hash_table_new_full (g_str_hash, g_str_equal,
1042                                                g_free, (GDestroyNotify) conv_free);
1043     }
1044 
1045     gxi_ambiguous_info_destroy (data);
1046 
1047     /* analyze file */
1048     data->n_impossible = gnc_xml2_find_ambiguous (
1049                              data->filename, data->encodings, &data->unique, &data->ambiguous_ht, NULL);
1050 
1051     if (data->n_impossible != -1)
1052     {
1053         /* sort ambiguous words */
1054         g_hash_table_foreach (data->ambiguous_ht, (GHFunc)ambiguous_list_insert,
1055                               data);
1056         gxi_sort_ambiguous_list (data);
1057     }
1058 }
1059 
1060 static gboolean
gxi_parse_file(GncXmlImportData * data)1061 gxi_parse_file (GncXmlImportData *data)
1062 {
1063     QofSession *session = NULL;
1064     QofBook *book;
1065     QofBackend *backend;
1066     QofBackendError io_err = ERR_BACKEND_NO_ERR;
1067     gchar *message = NULL;
1068     gboolean success = FALSE;
1069 
1070     if (data->n_unassigned || data->n_impossible)
1071         goto cleanup_parse_file;
1072 
1073     /* fill subst hash table with byte sequence substitutions */
1074     data->subst = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
1075     g_hash_table_foreach (data->ambiguous_ht, (GHFunc) subst_insert_amb, data);
1076     g_hash_table_foreach (data->unique, (GHFunc) subst_insert_unique, data);
1077 
1078     if (!data->subst)
1079         goto cleanup_parse_file;
1080 
1081     /* create a temporary QofSession */
1082     gxi_session_destroy (data);
1083     session = qof_session_new (NULL);
1084     data->session = session;
1085     qof_session_begin (session, data->filename, SESSION_READ_ONLY);
1086     io_err = qof_session_get_error (session);
1087     if (io_err != ERR_BACKEND_NO_ERR)
1088     {
1089         message = _("The file could not be reopened.");
1090         goto cleanup_parse_file;
1091     }
1092 
1093     xaccLogDisable ();
1094     gxi_update_progress_bar (_("Reading file..."), 0.0);
1095     qof_session_load (session, gxi_update_progress_bar);
1096     gxi_update_progress_bar (NULL, -1.0);
1097     xaccLogEnable ();
1098 
1099     io_err = qof_session_get_error (session);
1100     if (io_err == ERR_BACKEND_NO_ERR)
1101     {
1102         /* loaded successfully now. strange, but ok */
1103         success = TRUE;
1104         goto cleanup_parse_file;
1105     }
1106     else if (io_err != ERR_FILEIO_NO_ENCODING)
1107     {
1108         /* another error, cannot handle this here */
1109         message = _("The file could not be reopened.");
1110         goto cleanup_parse_file;
1111     }
1112 
1113     qof_session_pop_error (session);
1114     book = qof_session_get_book (session);
1115     backend = qof_book_get_backend (book);
1116 
1117     gxi_update_progress_bar (_("Parsing file..."), 0.0);
1118     success = gnc_xml2_parse_with_subst (backend, book, data->subst);
1119     gxi_update_progress_bar (NULL, -1.0);
1120 
1121     if (success)
1122         data->session = session;
1123     else
1124         message = _("There was an error parsing the file.");
1125 
1126 cleanup_parse_file:
1127 
1128     if (data->subst)
1129     {
1130         g_hash_table_destroy (data->subst);
1131         data->subst = NULL;
1132     }
1133     if (message)
1134     {
1135         gnc_error_dialog (GTK_WINDOW (data->assistant), "%s", message);
1136     }
1137     if (!success)
1138         gxi_session_destroy (data);
1139 
1140     return success;
1141 }
1142 
1143 static gboolean
gxi_save_file(GncXmlImportData * data)1144 gxi_save_file (GncXmlImportData *data)
1145 {
1146     QofBackendError io_err;
1147     g_return_val_if_fail (data && data->session, FALSE);
1148 
1149     gxi_update_progress_bar (_("Writing file..."), 0.0);
1150     qof_session_save (data->session, gxi_update_progress_bar);
1151     gxi_update_progress_bar (NULL, -1.0);
1152 
1153     io_err = qof_session_get_error (data->session);
1154 
1155     if (io_err == ERR_BACKEND_NO_ERR)
1156     {
1157         return TRUE;
1158     }
1159     else
1160     {
1161         gxi_session_destroy (data);
1162         return FALSE;
1163     }
1164 }
1165 
1166 
1167 /***************************
1168  *                         *
1169  * Encodings dialog window *
1170  *                         *
1171  **************************/
1172 void
gxi_edit_encodings_clicked_cb(GtkButton * button,GncXmlImportData * data)1173 gxi_edit_encodings_clicked_cb (GtkButton *button, GncXmlImportData *data)
1174 {
1175     GtkBuilder *builder;
1176     GtkWidget *dialog;
1177     GtkListStore *list_store;
1178     GtkTreeStore *tree_store;
1179     GtkTreeIter iter, parent, *parent_ptr;
1180     GList *encodings_bak, *enc_iter;
1181     const gchar *encoding;
1182     system_encoding_type *system_enc;
1183     gpointer enc_ptr;
1184     gint i, j;
1185 
1186     builder = gtk_builder_new();
1187     gnc_builder_add_from_file (builder, "assistant-xml-encoding.glade", "encodings_dialog");
1188     dialog = GTK_WIDGET(gtk_builder_get_object (builder, "encodings_dialog"));
1189     data->encodings_dialog = dialog;
1190 
1191     // Set the name for this assistant so it can be easily manipulated with css
1192     gtk_widget_set_name (GTK_WIDGET(dialog), "gnc-id-assistant-xml-encoding");
1193 
1194     gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, data);
1195 
1196     gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (data->assistant));
1197 
1198     data->available_encs_view = GTK_TREE_VIEW (gtk_builder_get_object (builder, "available_encs_view"));
1199 
1200     data->custom_enc_entry = GTK_WIDGET(gtk_builder_get_object (builder, "custom_enc_entry"));
1201 
1202     /* set up selected encodings list */
1203     data->selected_encs_view = GTK_TREE_VIEW (gtk_builder_get_object (builder, "selected_encs_view"));
1204     list_store = gtk_list_store_new (ENC_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER);
1205     for (enc_iter = data->encodings; enc_iter; enc_iter = enc_iter->next)
1206     {
1207         encoding = g_quark_to_string (GPOINTER_TO_UINT (enc_iter->data));
1208         gtk_list_store_append (list_store, &iter);
1209         gtk_list_store_set (list_store, &iter, ENC_COL_STRING, encoding,
1210                             ENC_COL_QUARK, enc_iter->data, -1);
1211     }
1212     gtk_tree_view_insert_column_with_attributes (
1213         data->selected_encs_view, -1, NULL,
1214         gtk_cell_renderer_text_new (), "text", ENC_COL_STRING, NULL);
1215     gtk_tree_view_set_model (data->selected_encs_view,
1216                              GTK_TREE_MODEL (list_store));
1217     g_object_unref (list_store);
1218 
1219     /* set up system encodings list */
1220     data->available_encs_view = GTK_TREE_VIEW (gtk_builder_get_object (builder, "available_encs_view"));
1221     tree_store = gtk_tree_store_new (ENC_NUM_COLS, G_TYPE_STRING, G_TYPE_POINTER);
1222     for (i = 0, system_enc = system_encodings;
1223             i < n_system_encodings;
1224             i++, system_enc++)
1225     {
1226         if (i == 0)
1227         {
1228             /* first system encoding */
1229             parent_ptr = NULL;
1230         }
1231         else
1232         {
1233             parent_ptr = &iter;
1234             for (j = 0; j < system_enc->parent; j++)
1235                 if (gtk_tree_model_iter_parent (GTK_TREE_MODEL (tree_store),
1236                                                 &parent, &iter))
1237                 {
1238                     /* go up one level */
1239                     iter = parent;
1240                 }
1241                 else
1242                 {
1243                     /* no parent to toplevel element */
1244                     parent_ptr = NULL;
1245                 }
1246         }
1247         if (system_enc->encoding)
1248             enc_ptr = GUINT_TO_POINTER (g_quark_from_string (system_enc->encoding));
1249         else
1250             enc_ptr = NULL;
1251 
1252         gtk_tree_store_append (tree_store, &iter, parent_ptr);
1253         gtk_tree_store_set (tree_store, &iter, ENC_COL_STRING,
1254                             gettext (system_enc->text), ENC_COL_QUARK, enc_ptr, -1);
1255     }
1256     gtk_tree_view_insert_column_with_attributes (
1257         data->available_encs_view, -1, NULL,
1258         gtk_cell_renderer_text_new (), "text", ENC_COL_STRING, NULL);
1259     gtk_tree_view_set_model (data->available_encs_view,
1260                              GTK_TREE_MODEL (tree_store));
1261     g_object_unref (tree_store);
1262 
1263     /* run the dialog */
1264     encodings_bak = g_list_copy (data->encodings);
1265     if (gtk_dialog_run (GTK_DIALOG (dialog)) == GTK_RESPONSE_OK)
1266     {
1267         g_list_free (encodings_bak);
1268         if (data->encodings && !g_list_find (data->encodings,
1269                           GUINT_TO_POINTER (data->default_encoding)))
1270         {
1271             /* choose top level encoding then */
1272             data->default_encoding = GPOINTER_TO_UINT (data->encodings->data);
1273         }
1274 
1275         /* update whole page */
1276         gxi_check_file (data);
1277         gxi_update_default_enc_combo (data);
1278         gxi_update_string_box (data);
1279         gxi_update_conversion_forward (data);
1280     }
1281     else
1282     {
1283         g_list_free (data->encodings);
1284         data->encodings = encodings_bak;
1285     }
1286     g_object_unref(G_OBJECT(builder));
1287 
1288     gtk_widget_destroy (dialog);
1289     data->encodings_dialog = NULL;
1290 }
1291 
1292 static void
gxi_add_encoding(GncXmlImportData * data,gpointer encoding_ptr)1293 gxi_add_encoding (GncXmlImportData *data, gpointer encoding_ptr)
1294 {
1295     GIConv iconv;
1296     const gchar *message;
1297     gchar *enc_string;
1298     GtkListStore *store;
1299     GtkTreeIter iter;
1300 
1301     enc_string = g_ascii_strup (
1302                      g_quark_to_string (GPOINTER_TO_UINT (encoding_ptr)), -1);
1303     encoding_ptr = GUINT_TO_POINTER (g_quark_from_string (enc_string));
1304 
1305     if (g_list_find (data->encodings, encoding_ptr))
1306     {
1307         message = _("This encoding has been added to the list already.");
1308         gnc_error_dialog (GTK_WINDOW (data->encodings_dialog), "%s", message);
1309         return;
1310     }
1311 
1312     /* test whether we like this encoding */
1313     iconv = g_iconv_open ("UTF-8", enc_string);
1314     if (iconv == (GIConv) - 1)
1315     {
1316         g_iconv_close (iconv);
1317         g_free (enc_string);
1318         message = _("This is an invalid encoding.");
1319         gnc_error_dialog (GTK_WINDOW (data->encodings_dialog), "%s", message);
1320         return;
1321     }
1322     g_iconv_close (iconv);
1323 
1324     /* add to the list */
1325     data->encodings = g_list_append (data->encodings, encoding_ptr);
1326     store = GTK_LIST_STORE (gtk_tree_view_get_model (data->selected_encs_view));
1327     gtk_list_store_append (store, &iter);
1328     gtk_list_store_set (store, &iter, ENC_COL_STRING, enc_string,
1329                         ENC_COL_QUARK, encoding_ptr, -1);
1330 
1331     g_free (enc_string);
1332 
1333     if (!data->encodings->next)
1334         gtk_dialog_set_response_sensitive (GTK_DIALOG (data->encodings_dialog),
1335                                            GTK_RESPONSE_OK, TRUE);
1336 }
1337 
1338 void
gxi_add_enc_clicked_cb(GtkButton * button,GncXmlImportData * data)1339 gxi_add_enc_clicked_cb (GtkButton *button, GncXmlImportData *data)
1340 {
1341     GtkTreeSelection *selection;
1342     GtkTreeModel *model;
1343     GtkTreeIter iter;
1344     gpointer enc_ptr;
1345 
1346     selection = gtk_tree_view_get_selection (data->available_encs_view);
1347     if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1348         return;
1349     gtk_tree_model_get (model, &iter, ENC_COL_QUARK, &enc_ptr, -1);
1350     if (!enc_ptr)
1351         return;
1352     gxi_add_encoding (data, enc_ptr);
1353 }
1354 
1355 static void
gxi_remove_encoding(GncXmlImportData * data,GtkTreeModel * model,GtkTreeIter * iter)1356 gxi_remove_encoding (GncXmlImportData *data, GtkTreeModel *model,
1357                      GtkTreeIter *iter)
1358 {
1359     gpointer enc_ptr;
1360 
1361     gtk_tree_model_get (model, iter, ENC_COL_QUARK, &enc_ptr, -1);
1362     data->encodings = g_list_remove (data->encodings, enc_ptr);
1363     gtk_list_store_remove (GTK_LIST_STORE (model), iter);
1364     if (!data->encodings)
1365         gtk_dialog_set_response_sensitive (GTK_DIALOG (data->encodings_dialog),
1366                                            GTK_RESPONSE_OK, FALSE);
1367 }
1368 
1369 void
gxi_remove_enc_clicked_cb(GtkButton * button,GncXmlImportData * data)1370 gxi_remove_enc_clicked_cb (GtkButton *button, GncXmlImportData *data)
1371 {
1372     GtkTreeSelection *selection;
1373     GtkTreeModel *model;
1374     GtkTreeIter iter;
1375 
1376     selection = gtk_tree_view_get_selection (data->selected_encs_view);
1377     if (!gtk_tree_selection_get_selected (selection, &model, &iter))
1378         return;
1379     gxi_remove_encoding (data, model, &iter);
1380 }
1381 
1382 void
gxi_available_enc_activated_cb(GtkTreeView * view,GtkTreePath * path,GtkTreeViewColumn * column,GncXmlImportData * data)1383 gxi_available_enc_activated_cb (GtkTreeView *view, GtkTreePath *path,
1384                                 GtkTreeViewColumn *column,
1385                                 GncXmlImportData *data)
1386 {
1387     GtkTreeModel *model;
1388     GtkTreeIter iter;
1389     gpointer enc_ptr;
1390 
1391     model = gtk_tree_view_get_model (data->available_encs_view);
1392     if (!gtk_tree_model_get_iter (model, &iter, path))
1393         return;
1394     gtk_tree_model_get (model, &iter, ENC_COL_QUARK, &enc_ptr, -1);
1395     if (!enc_ptr)
1396         return;
1397     gxi_add_encoding (data, enc_ptr);
1398 }
1399 
1400 void
gxi_custom_enc_activate_cb(GtkEntry * entry,GncXmlImportData * data)1401 gxi_custom_enc_activate_cb (GtkEntry *entry, GncXmlImportData *data)
1402 {
1403     const gchar *enc_string;
1404 
1405     enc_string = gtk_entry_get_text (entry);
1406     if (!enc_string)
1407         return;
1408     gxi_add_encoding (data, GUINT_TO_POINTER (g_quark_from_string (enc_string)));
1409 }
1410 
1411 void
gxi_add_custom_enc_clicked_cb(GtkButton * button,GncXmlImportData * data)1412 gxi_add_custom_enc_clicked_cb (GtkButton *button, GncXmlImportData *data)
1413 {
1414     GtkWidget *entry = data->custom_enc_entry;
1415     gxi_custom_enc_activate_cb (GTK_ENTRY (entry), data);
1416 }
1417 
1418 void
gxi_selected_enc_activated_cb(GtkTreeView * view,GtkTreePath * path,GtkTreeViewColumn * column,GncXmlImportData * data)1419 gxi_selected_enc_activated_cb (GtkTreeView *view, GtkTreePath *path,
1420                                GtkTreeViewColumn *column, GncXmlImportData *data)
1421 {
1422     GtkTreeModel *model;
1423     GtkTreeIter iter;
1424 
1425     model = gtk_tree_view_get_model (data->selected_encs_view);
1426     if (!gtk_tree_model_get_iter (model, &iter, path))
1427         return;
1428     gxi_remove_encoding (data, model, &iter);
1429 }
1430