1 /*******************************************************************\
2  * assistant-csv-account-import.c -- An assistant for importing     *
3  *                                         Accounts from a file.    *
4  *                                                                  *
5  * Copyright (C) 2012 Robert Fewell                                 *
6  *                                                                  *
7  * This program is free software; you can redistribute it and/or    *
8  * modify it under the terms of the GNU General Public License as   *
9  * published by the Free Software Foundation; either version 2 of   *
10  * the License, or (at your option) any later version.              *
11  *                                                                  *
12  * This program is distributed in the hope that it will be useful,  *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
15  * GNU General Public License for more details.                     *
16  *                                                                  *
17  * You should have received a copy of the GNU General Public License*
18  * along with this program; if not, contact:                        *
19  *                                                                  *
20  * Free Software Foundation           Voice:  +1-617-542-5942       *
21  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
22  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
23 \********************************************************************/
24 /** @file assistant-csv-account-import.c
25     @brief CSV Import Assistant
26     @author Copyright (c) 2012 Robert Fewell
27 */
28 #include <config.h>
29 
30 #include <gtk/gtk.h>
31 #include <glib/gi18n.h>
32 
33 #include "dialog-utils.h"
34 #include "gnc-ui.h"
35 #include "gnc-uri-utils.h"
36 #include "gnc-ui-util.h"
37 
38 #include "gnc-component-manager.h"
39 
40 #include "assistant-csv-account-import.h"
41 #include "csv-account-import.h"
42 
43 #define GNC_PREFS_GROUP "dialogs.import.csv"
44 #define ASSISTANT_CSV_IMPORT_CM_CLASS "assistant-csv-account-import"
45 
46 /* This static indicates the debugging module that this .o belongs to.  */
47 static QofLogModule log_module = GNC_MOD_ASSISTANT;
48 
49 /*************************************************************************/
50 
51 void csv_import_assistant_prepare (GtkAssistant  *assistant, GtkWidget *page, gpointer user_data);
52 void csv_import_assistant_finish (GtkAssistant *gtkassistant, gpointer user_data);
53 void csv_import_assistant_cancel (GtkAssistant *gtkassistant, gpointer user_data);
54 void csv_import_assistant_close (GtkAssistant *gtkassistant, gpointer user_data);
55 
56 void csv_import_assistant_start_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
57 void csv_import_assistant_account_page_prepare (GtkAssistant *gtkassistant, gpointer user_data);
58 void csv_import_assistant_file_page_prepare (GtkAssistant *assistant, gpointer user_data);
59 void csv_import_assistant_finish_page_prepare (GtkAssistant *assistant, gpointer user_data);
60 void csv_import_assistant_summary_page_prepare (GtkAssistant *assistant, gpointer user_data);
61 
62 void csv_import_sep_cb (GtkWidget *radio, gpointer user_data );
63 void csv_import_hrows_cb (GtkWidget *spin, gpointer user_data );
64 
65 void csv_import_file_chooser_file_activated_cb (GtkFileChooser *chooser, CsvImportInfo *info);
66 void csv_import_file_chooser_selection_changed_cb (GtkFileChooser *chooser, CsvImportInfo *info);
67 
68 static gchar *gnc_input_dialog (GtkWidget *parent, const gchar *title, const gchar *msg, const gchar *default_input);
69 
70 static const gchar *finish_tree_string = N_(
71             "The accounts will be imported from the file '%s' when you click 'Apply'.\n\n"
72             "You can verify your selections by clicking on 'Back' or 'Cancel' to Abort Import.\n");
73 
74 static const gchar *new_book_finish_tree_string = N_(
75             "The accounts will be imported from the file '%s' when you click 'Apply'.\n\n"
76             "You can verify your selections by clicking on 'Back' or 'Cancel' to Abort Import.\n\n"
77             "If this is your initial import into a new file, you will first see "
78             "a dialog for setting book options, since these can affect how "
79             "imported data is converted to GnuCash transactions.\n"
80             "Note: After import, you may need to use 'View / Filter By / Other' menu option "
81             "and select to show unused Accounts.\n");
82 
83 /* Escape '_' in string */
84 static gchar *mnemonic_escape (const gchar *source);
mnemonic_escape(const gchar * source)85 static gchar *mnemonic_escape (const gchar *source)
86 {
87     const guchar *p;
88     gchar *dest;
89     gchar *q;
90 
91     g_return_val_if_fail (source != NULL, NULL);
92 
93     p = (guchar *) source;
94     q = dest = g_malloc (strlen (source) * 2 + 1);
95 
96     while (*p)
97     {
98         switch (*p)
99         {
100         case '_':
101             *q++ = '_';
102             *q++ = '_';
103             break;
104         default:
105             *q++ = *p;
106             break;
107         }
108         p++;
109     }
110     *q = 0;
111     return dest;
112 }
113 
114 static
create_regex(GString * regex_str,const gchar * sep)115 void create_regex (GString *regex_str, const gchar *sep)
116 {
117     if (!sep) return;
118 
119     g_string_printf (regex_str,
120             "\\G(?<type>[^%s]*)%s"
121             "(?<full_name>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
122             "(?<name>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
123             "(?<code>\"(?:[^\"]|\"\")*\"|[^%s]*)%s?"
124             "(?<description>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
125             "(?<color>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
126             "(?<notes>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
127             "(?<symbol>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
128             "(?<namespace>\"(?:[^\"]|\"\")*\"|[^%s]*)%s"
129             "(?<hidden>[^%s]*)%s"
130             "(?<tax>[^%s]*)%s"
131             "(?<placeholder>[^%s[:cntrl:]]*)(?:\\R*)",
132             sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep,
133             sep, sep, sep, sep, sep, sep, sep, sep, sep, sep, sep);
134 
135 }
136 
137 /*************************************************************************/
138 
139 /**************************************************
140  * csv_import_assistant_check_filename
141  *
142  * check for a valid filename for GtkFileChooser callbacks
143  **************************************************/
144 static gboolean
csv_import_assistant_check_filename(GtkFileChooser * chooser,CsvImportInfo * info)145 csv_import_assistant_check_filename (GtkFileChooser *chooser,
146                                      CsvImportInfo *info)
147 {
148     gchar *file_name = gtk_file_chooser_get_filename (chooser);
149 
150     /* Test for a valid filename and not a directory */
151     if (file_name && !g_file_test (file_name, G_FILE_TEST_IS_DIR))
152     {
153         gchar *filepath = gnc_uri_get_path (file_name);
154         gchar *filedir = g_path_get_dirname (filepath);
155 
156         g_free (info->file_name);
157         info->file_name = g_strdup (file_name);
158 
159         g_free (info->starting_dir);
160         info->starting_dir = g_strdup (filedir);
161 
162         g_free (filedir);
163         g_free (filepath);
164         g_free (file_name);
165 
166         DEBUG("file_name selected is %s", info->file_name);
167         DEBUG("starting directory is %s", info->starting_dir);
168         return TRUE;
169     }
170     g_free (file_name);
171     return FALSE;
172 }
173 
174 
175 /**************************************************
176  * csv_import_file_chooser_file_activated_cb
177  *
178  * call back for GtkFileChooser file-activated signal
179  **************************************************/
180 void
csv_import_file_chooser_file_activated_cb(GtkFileChooser * chooser,CsvImportInfo * info)181 csv_import_file_chooser_file_activated_cb (GtkFileChooser *chooser,
182                                            CsvImportInfo *info)
183 {
184     GtkAssistant *assistant = GTK_ASSISTANT(info->assistant);
185     gtk_assistant_set_page_complete (assistant, info->file_page, FALSE);
186 
187     /* Test for a valid filename and not a directory */
188     if (csv_import_assistant_check_filename (chooser, info))
189     {
190         gtk_assistant_set_page_complete (assistant, info->file_page, TRUE);
191         gtk_assistant_next_page (assistant);
192     }
193 }
194 
195 
196 /**************************************************
197  * csv_import_file_chooser_selection_changed_cb
198  *
199  * call back for file chooser widget
200  **************************************************/
201 void
csv_import_file_chooser_selection_changed_cb(GtkFileChooser * chooser,CsvImportInfo * info)202 csv_import_file_chooser_selection_changed_cb (GtkFileChooser *chooser,
203                                               CsvImportInfo *info)
204 {
205     GtkAssistant *assistant = GTK_ASSISTANT(info->assistant);
206     gtk_assistant_set_page_complete (assistant, info->account_page, FALSE);
207 
208     /* Enable the "Next" button based on a valid filename */
209     gtk_assistant_set_page_complete (assistant, info->file_page,
210         csv_import_assistant_check_filename (chooser, info));
211 }
212 
213 
214 /*******************************************************
215  * csv_import_hrows_cb
216  *
217  * call back for the start row / number of header rows
218  *******************************************************/
csv_import_hrows_cb(GtkWidget * spin,gpointer user_data)219 void csv_import_hrows_cb (GtkWidget *spin, gpointer user_data)
220 {
221     CsvImportInfo *info = user_data;
222 
223     GtkTreeIter iter;
224     gboolean valid;
225     int num_rows;
226 
227     /* Get number of rows for header */
228     info->header_rows = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON(spin));
229 
230     /* Get number of rows displayed */
231     num_rows = gtk_tree_model_iter_n_children (GTK_TREE_MODEL(info->store), NULL);
232 
233     /* Modify background color for header rows */
234     if (info->header_rows == 0)
235     {
236         valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(info->store), &iter, NULL, 0 );
237         if (valid)
238             gtk_list_store_set (info->store, &iter, ROW_COLOR, NULL, -1);
239     }
240     else
241     {
242         if (info->header_rows - 1 < num_rows)
243         {
244             valid = gtk_tree_model_iter_nth_child (GTK_TREE_MODEL(info->store), &iter, NULL, info->header_rows - 1 );
245             if (valid)
246                 gtk_list_store_set (info->store, &iter, ROW_COLOR, "pink", -1);
247             valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(info->store), &iter);
248             if (valid)
249                 gtk_list_store_set (info->store, &iter, ROW_COLOR, NULL, -1);
250         }
251     }
252 }
253 
254 
255 /*******************************************************
256  * csv_import_assistant_enable_account_forward
257  *
258  * enable "Next" button on account_page if store has rows
259  *******************************************************/
csv_import_assistant_enable_account_forward(CsvImportInfo * info)260 static void csv_import_assistant_enable_account_forward (CsvImportInfo *info)
261 {
262     GtkAssistant *assistant = GTK_ASSISTANT(info->assistant);
263     gboolean store_has_rows = TRUE;
264 
265     /* if the store is empty, disable "Next" button */
266     if (gtk_tree_model_iter_n_children (GTK_TREE_MODEL(info->store), NULL) == 0)
267         store_has_rows = FALSE;
268 
269     gtk_assistant_set_page_complete (assistant, info->account_page, store_has_rows);
270 }
271 
272 
273 /*******************************************************
274  * csv_import_sep_cb
275  *
276  * call back for type of separator required
277  *******************************************************/
csv_import_sep_cb(GtkWidget * radio,gpointer user_data)278 void csv_import_sep_cb (GtkWidget *radio, gpointer user_data)
279 {
280     CsvImportInfo *info = user_data;
281     const gchar *name;
282     gchar *temp;
283     gchar *sep = NULL;
284 
285     if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON(radio)))
286     {
287         LEAVE("1st callback of pair. Defer to 2nd callback.");
288         return;
289     }
290 
291     name = gtk_buildable_get_name (GTK_BUILDABLE(radio));
292     if (g_strcmp0 (name, "radio_semi") == 0)
293         sep = ";";
294     else if (g_strcmp0 (name, "radio_colon") == 0)
295         sep = ":";
296     else
297         sep = ","; /* Use as default as well */
298 
299     create_regex (info->regexp, sep);
300 
301     if (g_strcmp0 (name, "radio_custom") == 0)
302     {
303         temp = gnc_input_dialog (GTK_WIDGET (info->assistant),
304                                  _("Adjust regular expression used for import"),
305                                  _("This regular expression is used to parse the import file. Modify according to your needs.\n"),
306                                   info->regexp->str);
307         if (temp)
308         {
309             g_string_assign (info->regexp, temp);
310             g_free (temp);
311         }
312     }
313 
314     /* Generate preview */
315     gtk_list_store_clear (info->store);
316     gtk_widget_set_sensitive (info->header_row_spin, TRUE);
317 
318     if (csv_import_read_file (GTK_WINDOW (info->assistant), info->file_name, info->regexp->str, info->store, 11) == MATCH_FOUND)
319         gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->header_row_spin), 1); // set header spin to 1
320     else
321         gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->header_row_spin), 0); //reset header spin to 0
322 
323     /* if the store has rows, enable "Next" button */
324     csv_import_assistant_enable_account_forward (info);
325 }
326 
327 
328 /*******************************************************
329  * load_settings
330  *
331  * load the default settings for the assistant
332  *******************************************************/
333 static
load_settings(CsvImportInfo * info)334 void load_settings (CsvImportInfo *info)
335 {
336     info->header_rows = 0;
337     info->error = "";
338     info->starting_dir = NULL;
339     info->file_name = NULL;
340     info->error = "";
341 
342     /* The default directory for the user to select files. */
343     info->starting_dir = gnc_get_default_directory (GNC_PREFS_GROUP);
344 }
345 
346 
347 /* =============================================================== */
348 
349 
350 /********************************************************************\
351  * gnc_input_dialog                                                 *
352  *   simple convenience dialog to get a single value from the user  *
353  *   user may choose between "Ok" and "Cancel"                      *
354  *                                                                  *
355  * NOTE: This function does not return until the dialog is closed   *
356  *                                                                  *
357  * Args:   parent  - the parent window or NULL                      *
358  *         title   - the title of the dialog                        *
359  *         msg     - the message to display                         *
360  *         default_input - will be displayed as default input       *
361  * Return: the input (text) the user entered, if pressed "Ok"       *
362  *         NULL, if pressed "Cancel"                                *
363 \********************************************************************/
364 static gchar *
gnc_input_dialog(GtkWidget * parent,const gchar * title,const gchar * msg,const gchar * default_input)365 gnc_input_dialog (GtkWidget *parent, const gchar *title, const gchar *msg, const gchar *default_input)
366 {
367     GtkWidget *dialog, *label, *content_area;
368     gint result;
369     GtkWidget *view;
370     GtkTextBuffer *buffer;
371     gchar *user_input;
372     GtkTextIter start, end;
373 
374     /* Create the widgets */
375     dialog = gtk_dialog_new_with_buttons (title, GTK_WINDOW(parent),
376                                           GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
377                                           _("_OK"), GTK_RESPONSE_ACCEPT,
378                                           _("_Cancel"), GTK_RESPONSE_REJECT,
379                                           NULL);
380 
381     content_area = gtk_dialog_get_content_area (GTK_DIALOG(dialog));
382 
383     // add a label
384     label = gtk_label_new (msg);
385     gtk_container_add (GTK_CONTAINER(content_area), label);
386 
387     // add a textview
388     view = gtk_text_view_new ();
389     gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW(view), GTK_WRAP_WORD_CHAR);
390     buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(view));
391     gtk_text_buffer_set_text (buffer, default_input, -1);
392     gtk_container_add (GTK_CONTAINER(content_area), view);
393 
394     // run the dialog
395     gtk_widget_show_all (dialog);
396     result = gtk_dialog_run (GTK_DIALOG(dialog));
397 
398     if (result == GTK_RESPONSE_REJECT)
399         user_input = 0;
400     else
401     {
402         gtk_text_buffer_get_start_iter (buffer, &start);
403         gtk_text_buffer_get_end_iter (buffer, &end);
404         user_input = gtk_text_buffer_get_text (buffer,
405                                                &start, &end, FALSE);
406     }
407 
408     gtk_widget_destroy (dialog);
409 
410     return user_input;
411 }
412 
413 
414 /* =============================================================== */
415 
416 
417 /*******************************************************
418  * Assistant page prepare functions
419  *******************************************************/
420 void
csv_import_assistant_start_page_prepare(GtkAssistant * assistant,gpointer user_data)421 csv_import_assistant_start_page_prepare (GtkAssistant *assistant,
422         gpointer user_data)
423 {
424     gint num = gtk_assistant_get_current_page (assistant);
425     GtkWidget *page = gtk_assistant_get_nth_page (assistant, num);
426 
427     /* Enable the Assistant Buttons */
428     gtk_assistant_set_page_complete (assistant, page, TRUE);
429 }
430 
431 
432 void
csv_import_assistant_file_page_prepare(GtkAssistant * assistant,gpointer user_data)433 csv_import_assistant_file_page_prepare (GtkAssistant *assistant,
434                                         gpointer user_data)
435 {
436     CsvImportInfo *info = user_data;
437 
438     /* Set the default directory */
439     if (info->starting_dir)
440         gtk_file_chooser_set_current_folder (GTK_FILE_CHOOSER(info->file_chooser), info->starting_dir);
441 
442     /* Disable the "Next" Assistant Button */
443     gtk_assistant_set_page_complete (assistant, info->file_page, FALSE);
444 }
445 
446 
447 void
csv_import_assistant_account_page_prepare(GtkAssistant * assistant,gpointer user_data)448 csv_import_assistant_account_page_prepare (GtkAssistant *assistant,
449         gpointer user_data)
450 {
451     CsvImportInfo *info = user_data;
452     csv_import_result res;
453 
454     /* Disable the "Next" Assistant Button */
455     gtk_assistant_set_page_complete (assistant, info->account_page, FALSE);
456 
457     /* test read one line */
458     gtk_list_store_clear (info->store);
459     res = csv_import_read_file (GTK_WINDOW (info->assistant), info->file_name, info->regexp->str, info->store, 1 );
460     if (res == RESULT_OPEN_FAILED)
461     {
462         gnc_error_dialog (GTK_WINDOW (info->assistant), _("The input file can not be opened."));
463         gtk_assistant_previous_page (assistant);
464     }
465     else if (res == RESULT_OK)
466         gtk_assistant_set_page_complete (assistant, info->account_page, TRUE);
467     else if (res == MATCH_FOUND)
468         gtk_assistant_set_page_complete (assistant, info->account_page, TRUE);
469 
470     // generate preview
471     gtk_list_store_clear (info->store);
472 
473     gtk_widget_set_sensitive (info->header_row_spin, TRUE);
474 
475     if (csv_import_read_file (GTK_WINDOW (info->assistant), info->file_name, info->regexp->str, info->store, 11 ) == MATCH_FOUND)
476         gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->header_row_spin), 1); // set header spin to 1
477     else
478         gtk_spin_button_set_value (GTK_SPIN_BUTTON(info->header_row_spin), 0); //reset header spin to 0
479 
480     /* if the store has rows, enable "Next" button */
481     csv_import_assistant_enable_account_forward (info);
482 }
483 
484 
485 void
csv_import_assistant_finish_page_prepare(GtkAssistant * assistant,gpointer user_data)486 csv_import_assistant_finish_page_prepare (GtkAssistant *assistant,
487         gpointer user_data)
488 {
489     CsvImportInfo *info = user_data;
490     gchar *text;
491 
492     /* Set Finish page text */
493     /* Before creating accounts, if this is a new book, tell user they can
494      * specify book options, since they affect how transactions are created */
495     if (info->new_book)
496         text = g_strdup_printf (gettext (new_book_finish_tree_string), info->file_name);
497     else
498         text = g_strdup_printf (gettext (finish_tree_string), info->file_name);
499 
500     gtk_label_set_text (GTK_LABEL(info->finish_label), text);
501     g_free (text);
502 
503     /* Save the Window size and directory */
504     gnc_set_default_directory (GNC_PREFS_GROUP, info->starting_dir);
505 
506     /* Enable the Assistant Buttons */
507     gtk_assistant_set_page_complete (assistant, info->finish_label, TRUE);
508 }
509 
510 
511 void
csv_import_assistant_summary_page_prepare(GtkAssistant * assistant,gpointer user_data)512 csv_import_assistant_summary_page_prepare (GtkAssistant *assistant,
513         gpointer user_data)
514 {
515     CsvImportInfo *info = user_data;
516     gchar *text, *errtext, *mtext;
517 
518     /* Before creating accounts, if this is a new book, let user specify
519      * book options, since they affect how transactions are created */
520     if (info->new_book)
521         info->new_book = gnc_new_book_option_display (info->assistant);
522 
523     if (g_strcmp0 (info->error, "") != 0)
524     {
525         GtkTextBuffer *buffer;
526 
527         buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW(info->summary_error_view));
528         text = g_strdup_printf (gettext ("Import completed but with errors!\n\nThe number of Accounts added was %u and "
529                                         "%u were updated.\n\nSee below for errors..."), info->num_new, info->num_updates);
530         errtext = g_strdup_printf ("%s", info->error);
531         gtk_text_buffer_set_text (buffer, errtext, -1);
532         g_free (errtext);
533         g_free (info->error);
534     }
535     else
536         text = g_strdup_printf (gettext ("Import completed successfully!\n\nThe number of Accounts added was %u and "
537                                         "%u were updated.\n"), info->num_new, info->num_updates);
538 
539     mtext = g_strdup_printf ("<span size=\"medium\"><b>%s</b></span>", text);
540     gtk_label_set_markup (GTK_LABEL(info->summary_label), mtext);
541 
542     g_free (text);
543     g_free (mtext);
544 }
545 
546 
547 void
csv_import_assistant_prepare(GtkAssistant * assistant,GtkWidget * page,gpointer user_data)548 csv_import_assistant_prepare (GtkAssistant *assistant, GtkWidget *page,
549                               gpointer user_data)
550 {
551     gint currentpage = gtk_assistant_get_current_page (assistant);
552 
553     switch (currentpage)
554     {
555     case 0:
556         /* Current page is Import Start page */
557         csv_import_assistant_start_page_prepare (assistant, user_data);
558         break;
559     case 1:
560         /* Current page is File select page */
561         csv_import_assistant_file_page_prepare (assistant, user_data);
562         break;
563     case 2:
564         /* Current page is Account page */
565         csv_import_assistant_account_page_prepare (assistant, user_data);
566         break;
567     case 3:
568         /* Current page is Finish page */
569         csv_import_assistant_finish_page_prepare (assistant, user_data);
570         break;
571     case 4:
572         /* Current page is Summary page */
573         csv_import_assistant_summary_page_prepare (assistant, user_data);
574         break;
575     }
576 }
577 
578 
579 /*******************************************************
580  * Assistant call back functions
581  *******************************************************/
582 static void
csv_import_assistant_destroy_cb(GtkWidget * object,gpointer user_data)583 csv_import_assistant_destroy_cb (GtkWidget *object, gpointer user_data)
584 {
585     CsvImportInfo *info = user_data;
586     gnc_unregister_gui_component_by_data (ASSISTANT_CSV_IMPORT_CM_CLASS, info);
587     g_free (info);
588 }
589 
590 void
csv_import_assistant_cancel(GtkAssistant * assistant,gpointer user_data)591 csv_import_assistant_cancel (GtkAssistant *assistant, gpointer user_data)
592 {
593     CsvImportInfo *info = user_data;
594     gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_CM_CLASS, info);
595 }
596 
597 void
csv_import_assistant_close(GtkAssistant * assistant,gpointer user_data)598 csv_import_assistant_close (GtkAssistant *assistant, gpointer user_data)
599 {
600     CsvImportInfo *info = user_data;
601     gnc_close_gui_component_by_data (ASSISTANT_CSV_IMPORT_CM_CLASS, info);
602 }
603 
604 void
csv_import_assistant_finish(GtkAssistant * assistant,gpointer user_data)605 csv_import_assistant_finish (GtkAssistant *assistant, gpointer user_data)
606 {
607     CsvImportInfo *info = user_data;
608 
609     gtk_list_store_clear (info->store);
610     csv_import_read_file (GTK_WINDOW (info->assistant), info->file_name, info->regexp->str, info->store, 0 );
611     csv_account_import (info);
612 }
613 
614 static void
csv_import_close_handler(gpointer user_data)615 csv_import_close_handler (gpointer user_data)
616 {
617     CsvImportInfo *info = user_data;
618 
619     g_free (info->starting_dir);
620     g_free (info->file_name);
621     g_string_free (info->regexp, TRUE);
622     g_object_unref (info->store);
623 
624     gnc_save_window_size (GNC_PREFS_GROUP, GTK_WINDOW(info->assistant));
625     gtk_widget_destroy (info->assistant);
626 }
627 
628 /*******************************************************
629  * Create the Assistant
630  *******************************************************/
631 static GtkWidget *
csv_import_assistant_create(CsvImportInfo * info)632 csv_import_assistant_create (CsvImportInfo *info)
633 {
634     GtkBuilder *builder;
635     GtkWidget *button;
636     GtkCellRenderer *renderer;
637     GtkTreeViewColumn *column;
638     gchar *mnemonic_desc = NULL;
639 
640     builder = gtk_builder_new();
641     gnc_builder_add_from_file  (builder, "assistant-csv-account-import.glade", "num_hrows_adj");
642     gnc_builder_add_from_file  (builder, "assistant-csv-account-import.glade", "csv_account_import_assistant");
643     info->assistant = GTK_WIDGET(gtk_builder_get_object (builder, "csv_account_import_assistant"));
644 
645     // Set the name for this assistant so it can be easily manipulated with css
646     gtk_widget_set_name (GTK_WIDGET(info->assistant), "gnc-id-assistant-csv-account-import");
647     gnc_widget_style_context_add_class (GTK_WIDGET(info->assistant), "gnc-class-imports");
648 
649     /* Load default settings */
650     load_settings (info);
651 
652     /* Enable buttons on all page. */
653     gtk_assistant_set_page_complete (GTK_ASSISTANT(info->assistant),
654                                      GTK_WIDGET(gtk_builder_get_object(builder, "start_page")),
655                                      TRUE);
656     gtk_assistant_set_page_complete (GTK_ASSISTANT(info->assistant),
657                                      GTK_WIDGET(gtk_builder_get_object(builder, "file_page")),
658                                      FALSE);
659     gtk_assistant_set_page_complete (GTK_ASSISTANT(info->assistant),
660                                      GTK_WIDGET(gtk_builder_get_object(builder, "import_tree_page")),
661                                      TRUE);
662     gtk_assistant_set_page_complete (GTK_ASSISTANT(info->assistant),
663                                      GTK_WIDGET(gtk_builder_get_object(builder, "end_page")),
664                                      FALSE);
665     gtk_assistant_set_page_complete (GTK_ASSISTANT(info->assistant),
666                                      GTK_WIDGET(gtk_builder_get_object(builder, "summary_page")),
667                                      TRUE);
668 
669     /* Start Page */
670 
671     /* File chooser Page */
672     info->file_page = GTK_WIDGET(gtk_builder_get_object(builder, "file_page"));
673     info->file_chooser = gtk_file_chooser_widget_new (GTK_FILE_CHOOSER_ACTION_OPEN);
674     g_signal_connect (G_OBJECT(info->file_chooser), "selection-changed",
675                       G_CALLBACK(csv_import_file_chooser_selection_changed_cb), info);
676     g_signal_connect (G_OBJECT(info->file_chooser), "file-activated",
677                       G_CALLBACK(csv_import_file_chooser_file_activated_cb), info);
678 
679     gtk_box_pack_start (GTK_BOX(info->file_page), info->file_chooser, TRUE, TRUE, 6);
680     gtk_widget_show (info->file_chooser);
681 
682     /* Account Tree Page */
683     info->account_page = GTK_WIDGET(gtk_builder_get_object(builder, "import_tree_page"));
684     info->header_row_spin = GTK_WIDGET(gtk_builder_get_object (builder, "num_hrows"));
685     info->tree_view = GTK_WIDGET(gtk_builder_get_object (builder, "treeview"));
686 
687     /* Comma Separated file default */
688     info->regexp = g_string_new ("");
689     create_regex (info->regexp, ",");
690 
691     /* create model and bind to view */
692     info->store = gtk_list_store_new (N_COLUMNS,
693                                       G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
694                                       G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING);
695     gtk_tree_view_set_model (GTK_TREE_VIEW(info->tree_view), GTK_TREE_MODEL(info->store));
696 #define CREATE_COLUMN(description,column_id) \
697   renderer = gtk_cell_renderer_text_new (); \
698   mnemonic_desc = mnemonic_escape (_(description)); \
699   column = gtk_tree_view_column_new_with_attributes (mnemonic_desc, renderer, "text", column_id, NULL); \
700   gtk_tree_view_column_add_attribute (column, renderer, "background", ROW_COLOR); \
701   gtk_tree_view_column_set_resizable (column, TRUE); \
702   gtk_tree_view_append_column (GTK_TREE_VIEW(info->tree_view), column); \
703   g_free (mnemonic_desc);
704     CREATE_COLUMN ("Type", TYPE);
705     CREATE_COLUMN ("Account Full Name", FULL_NAME);
706     CREATE_COLUMN ("Account Name", NAME);
707     CREATE_COLUMN ("Account Code", CODE);
708     CREATE_COLUMN ("Description", DESCRIPTION);
709     CREATE_COLUMN ("Account Color", COLOR);
710     CREATE_COLUMN ("Notes", NOTES);
711     CREATE_COLUMN ("Symbol", SYMBOL);
712     CREATE_COLUMN ("Namespace", NAMESPACE);
713     CREATE_COLUMN ("Hidden", HIDDEN);
714     CREATE_COLUMN ("Tax Info", TAX);
715     CREATE_COLUMN ("Placeholder", PLACE_HOLDER);
716 
717     /* Finish Page */
718     info->finish_label = GTK_WIDGET(gtk_builder_get_object (builder, "end_page"));
719     /* Summary Page */
720     info->summary_label = GTK_WIDGET(gtk_builder_get_object (builder, "summary_label"));
721     info->summary_error_view = GTK_WIDGET(gtk_builder_get_object (builder, "summary_error_view"));
722 
723     g_signal_connect (G_OBJECT(info->assistant), "destroy",
724                       G_CALLBACK(csv_import_assistant_destroy_cb), info);
725 
726     gnc_restore_window_size (GNC_PREFS_GROUP,
727                              GTK_WINDOW(info->assistant), gnc_ui_get_main_window(NULL));
728 
729     gtk_builder_connect_signals (builder, info);
730     g_object_unref (G_OBJECT(builder));
731     return info->assistant;
732 }
733 
734 
735 /********************************************************************\
736  * gnc_file_csv_account_import                                      *
737  * opens up a assistant to import accounts.                         *
738  *                                                                  *
739  * Args:   import_type                                              *
740  * Return: nothing                                                  *
741 \********************************************************************/
742 void
gnc_file_csv_account_import(void)743 gnc_file_csv_account_import(void)
744 {
745     CsvImportInfo *info;
746 
747     info = g_new0 (CsvImportInfo, 1);
748 
749     /* In order to trigger a book options display on the creation of a new book,
750      * we need to detect when we are dealing with a new book. */
751     info->new_book = gnc_is_new_book();
752 
753     csv_import_assistant_create (info);
754 
755     gnc_register_gui_component (ASSISTANT_CSV_IMPORT_CM_CLASS,
756                                 NULL, csv_import_close_handler,
757                                 info);
758 
759     gtk_widget_show_all (info->assistant);
760 
761     gnc_window_adjust_for_screen (GTK_WINDOW(info->assistant));
762 }
763