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