GnuCash  5.6-150-g038405b370+
assistant-xml-encoding.c
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;
116 
117 /* used for the string combos, see ambiguous_free */
118 typedef struct
119 {
120  gchar *byte_sequence;
121  GList *conv_list;
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;
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 
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
266 gxi_finish_cb (GtkAssistant *assistant, GncXmlImportData *data)
267 {
268  gtk_main_quit();
269 }
270 
271 static void
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
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
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
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
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 *
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
438 conv_enc_cmp (const conv_type *conv, const GQuark *enc)
439 {
440  return conv->encoding - *enc;
441 }
442 
443 static const gchar *
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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 }
void qof_session_save(QofSession *session, QofPercentageFunc percentage_func)
The qof_session_save() method will commit all changes that have been made to the session.
Definition: qofsession.cpp:625
QofBackendError
The errors that can be reported to the GUI & other front-end users.
Definition: qofbackend.h:57
void qof_session_begin(QofSession *session, const char *uri, SessionOpenMode mode)
Begins a new session.
Definition: qofsession.cpp:610
void xaccLogDisable(void)
document me
Definition: TransLog.cpp:95
gchar * gnc_uri_get_path(const gchar *uri)
Extracts the path part from a uri.
#define PERR(format, args...)
Log a serious error.
Definition: qoflog.h:244
Create a new store at the URI even if a store already exists there.
Definition: qofsession.h:128
file does not specify encoding
Definition: qofbackend.h:99
QofBook * qof_session_get_book(const QofSession *session)
Returns the QofBook of this session.
Definition: qofsession.cpp:574
gboolean gnc_xml2_parse_with_subst(GncXmlBackend *xml_be, QofBook *book, GHashTable *subst)
Parse a file in push mode, but replace byte sequences in the file given a hash table of substitutions...
QofBackendError qof_session_pop_error(QofSession *session)
The qof_session_pop_error() routine can be used to obtain the reason for any failure.
Definition: qofsession.cpp:567
QofBackendError qof_session_get_error(QofSession *session)
The qof_session_get_error() routine can be used to obtain the reason for any failure.
Definition: qofsession.cpp:728
API for the transaction logger.
gint gnc_xml2_find_ambiguous(const gchar *filename, GList *encodings, GHashTable **unique, GHashTable **ambiguous, GList **impossible)
Read a file as plain byte stream to find words that are not completely ASCII.
Utility functions for convert uri in separate components and back.
load and save data to files
QofBackend * qof_book_get_backend(const QofBook *book)
Retrieve the backend used by this book.
Definition: qofbook.cpp:440
void xaccLogEnable(void)
document me
Definition: TransLog.cpp:99