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